20 Commits
Author SHA1 Message Date
UbuntuandClaude Opus 4.8 0fd7f7a7a5 Fix "Conflict. Timeslot is taken" raw-JSON error on booking
Customers intermittently hit a full-screen raw JSON error when booking:
{"error":"Conflict. Timeslot is taken or does not fit the service."}

booking_process() re-validates the chosen slot at submit time and returned
409/403 raw JSON. Because the public booking form is a full-page POST, that
JSON filled the whole screen.

The trigger is a double submit. After inserting the booking, booking_process()
synchronously runs two Google Calendar createEvent calls, a lunch sync, an ntfy
push and an SMTP confirmation e-mail before redirecting - several seconds - and
the submit button was never disabled. On mobile the guest taps "Send" again; the
second request arrives after the first has committed, so the slot reads as taken.

Evidence: 168 duplicate booking pairs exist in prod (same guest, slot and worker,
consecutive booking ids, including runs of four). All are from 2025, none from
2026 - the 409 guard added around May 2025 converted those silent duplicates
into today's visible error.

Prevent the double submit:
- disable the submit button and relabel it on first submit, ignore later ones
- add a hidden sendBooking field, since disabling a submit button can drop its
  name/value from the POST and booking_process() bails to the homepage without it

Handle it gracefully when it still happens:
- new _booking_error() renders a localised page in the right skin instead of raw
  JSON, replacing all six JSON responses in booking_process()
- new booking-error views for barber/beauty in no/en/hu, each with a message per
  error case and a link back to booking
- new Service_model::getBookingBySlotAndGuest(); if the guest's own booking for
  that exact slot already exists the submit is a duplicate rather than a real
  conflict, so finish normally instead of erroring. Guarded on a non-empty
  e-mail, as admin block bookings are stored with an empty guest_email.

No schema change. Verified on test, dev and prod: friendly page in all three
languages and both skins, double submit redirects to booking-finished without
creating a duplicate row, and no raw JSON in any response.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TJso3iGT7TkW5tm4RSBohs
2026-07-21 16:21:53 +00:00
UbuntuandClaude Fable 5 06966a898a Add job postings section with vacancies listing and footer links
- New /ledigestilling/ index listing all open positions (split
  barber/beauty card styling)
- Three ads: negletekniker + massør (beauty design), barber (gold
  barber design with recolored CSS + assets)
- Rename ledigestilling.html -> negletekniker.html with 301 redirect
- "Ledige stillinger" footer link on barber/beauty pages (NO/EN/HU)
- Back-to-list link on each ad page

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 18:32:17 +00:00
UbuntuandClaude Opus 4.7 f86952dc95 Filter manage-booking workers by service category
Guests modifying a booking saw every worker for the subpage, so an
eyelash booking exposed nail-only workers as switchable. Now the worker
list is scoped to the booking's service category, other-category
services are disabled in step 1, and the process handler rejects any
worker/service category mismatch to defend against crafted POSTs.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-16 07:43:15 +00:00
Ubuntu 4a93b5efa0 Fix undefined array key in header on short URLs
The language switcher in barber/beauty headers reads
$currentPageUrlArray[2] unguarded after exploding REQUEST_URI on '/'.
On routes with no language prefix (e.g. POST /manage-booking-cancel
rendering the cancelled page inline), the array has only 2 elements,
triggering "Undefined array key 2" warnings and producing malformed
language links like https://studiobeve.no/en//.

Normalize the array with += [2 => '', 3 => ''] right after the explode
so indices 2 and 3 always exist.
2026-05-11 19:28:33 +00:00
UbuntuandClaude Opus 4.7 92a84aadb1 Remove dead strftime() call in worker calendar form
The strftime() result was immediately overwritten by a hardcoded
Hungarian month-name array. Drop the dead line so PHP 8.1+ stops
emitting an E_DEPRECATED warning when prod loads the worker calendar.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-11 11:41:47 +00:00
Ubuntu e24e68f603 Fix manage-booking time slots; align modify email with original
- getAvailableTimes() takes optional exclude_booking_id so a guest's
  own booking isn't counted as a conflict when editing — original time
  now reappears when extending services
- Manage-booking AJAX passes manage_token; server resolves to booking_id
- manage_booking_process uses the new param instead of the date-swap
  workaround (removes a small race-condition risk)
- Modify-booking emails (no/en/hu) now include Name/Email/Phone rows
  and the 24h cancellation policy, matching the original booking email
2026-05-10 13:18:07 +00:00
UbuntuandClaude Opus 4.7 d7bdcac978 Add search filter to services admin list; refresh load_services.json
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09 12:41:04 +00:00
UbuntuandClaude Opus 4.7 52e00216f7 Fix admin update forms — restore proper value="" quoting
The security hardening commit accidentally rendered every input value as
value=\"...\" (literal backslash-quote in HTML), which mangled all
submitted fields including the hidden worker_id/service_id, causing
UPDATE to match zero rows and silently no-op.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09 12:40:50 +00:00
UbuntuandClaude Opus 4.6 6abb90329d Add visual booking calendar (weekly Teams-style view)
- New weekly calendar at /bookings/calendar with time blocks per booking
- Color-coded by worker, overlapping bookings shown side-by-side
- Click booking to see details, edit or delete
- Worker filter dropdown, week navigation (prev/next/today)
- AJAX week loading for smooth navigation
- Link between list view and calendar view

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-07 14:03:41 +00:00
UbuntuandClaude Opus 4.6 0e172ceea8 Sync outstanding changes: beauty forms, config updates, gitignore
- Update beauty booking forms (en, hu, no)
- Update manage-booking view, worker calendar view
- Update config.php, google_calendar.php, GoogleCalendar library
- Add CLAUDE.md project instructions
- Add service-account-key.json to .gitignore
- Update ledigestilling page

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-07 13:02:18 +00:00
UbuntuandClaude Opus 4.6 420bcb37fd Security hardening: fix SQLi, XSS, file upload, and migrate DB to RDS
- Fix all SQL injection vulnerabilities across Service_model, User_model,
  Module_model, Log_model, and Admin controller using parameterized queries
- Add htmlspecialchars() to all user-controlled output in admin views
  (bookings, services, workers, service categories, login form)
- Fix XSS in AJAX worker response and manage-booking-cancelled view
- Add file extension whitelist (jpg, jpeg, png, gif, webp) to all uploads
- Remove webshell (pentest2.php) from assets/img/profiles/
- Stop logging plaintext passwords on failed login attempts
- Migrate database.php hostname from localhost to AWS RDS endpoint
- Fix dropdown styling (white-on-white) in worker calendar view

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-07 13:00:32 +00:00
Astral04andClaude Sonnet 4.6 307f17faa6 Fix timezone bug causing slots past closing time; remove lunch fallback outside window
- Add Europe/Oslo timezone to all DateTime constructors in getAvailableTimes() and
  computeLunchBreak() to prevent UTC vs local time mismatch that allowed booking
  slots 1 hour past the worker's end time on same-day bookings
- Remove fallback loop in computeLunchBreak() that pushed the lunch break outside
  the configured window; lunch break is now strictly enforced within the interval

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-07 11:52:30 +01:00
Astral04andClaude Sonnet 4.6 b8f4f67b23 Add visual worker calendar and floating lunch break system
- Monthly calendar grid per worker with colour-coded day status
- Override types: vacation, sick, custom hours, other, day-off
- Date-range override support via modal
- Floating 30-min lunch break: finds slot closest to preferred time
  within configurable window, adapts to existing bookings
- Lunch break only applies for shifts >= 6 hours
- Lunch slot shown in admin calendar; blocked in booking availability
- DB migrations: absence_type/note on worker_schedule_overrides,
  lunch_window_start/end/preferred_time on workers

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-04 13:34:58 +01:00
Astral04andClaude Sonnet 4.6 993e0cb8b1 per-worker ntfy topics for targeted push notifications
Evelin receives all booking notifications via the shared ntfy topic in config.
Each worker also gets notified on their own personal ntfy topic (set per-worker
in the admin panel) — so workers only see their own booking events.

- _ntfy() now accepts optional $workerTopic and sends to both topics if different
- All three call sites (new/modify/cancel) pass $worker->ntfy_topic
- Admin worker form + worker_process() wired for ntfy_topic field
- DB: ALTER TABLE workers ADD COLUMN ntfy_topic VARCHAR(100) NULL DEFAULT NULL

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-01 15:30:08 +01:00
Astral04andClaude Sonnet 4.6 bb3a65259b Google Calendar integration for booking notifications
- New GoogleCalendar library: createEvent/updateEvent/deleteEvent via service account, all wrapped in try/catch so failures never break booking flow
- booking_process: creates worker + owner calendar events on new booking
- manage_booking_process: deletes old events, creates new ones on modify
- manage_booking_cancel: deletes events before cancellation
- Service_model: updateBookingCalEvents() stores gcal event IDs
- Admin worker form: Google Calendar ID field added
- PHPMailer: enabled exceptions (was silently swallowing SMTP errors)
- Config: application/config/google_calendar.php for service account path + Evelin calendar ID

DB migration required:
  ALTER TABLE workers ADD COLUMN google_calendar_id VARCHAR(255) NULL DEFAULT NULL;
  ALTER TABLE bookings ADD COLUMN gcal_event_id_worker VARCHAR(255) NULL DEFAULT NULL;
  ALTER TABLE bookings ADD COLUMN gcal_event_id_owner VARCHAR(255) NULL DEFAULT NULL;

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-01 14:46:58 +01:00
Astral04andClaude Sonnet 4.6 2183c8aff2 guest booking self-management feature + layout fixes
- add manage_token column to bookings (DB migration done)
- generate unique token per booking, include manage link in confirmation emails (no/en/hu)
- new routes: manage-booking, manage-booking-process, manage-booking-cancel
- new views: manage-booking.php, manage-booking-cancelled.php
- 24h cutoff enforcement for cancel/modify; emails sent to guest + studio CC
- fix manage-booking step 3 float layout (overflow:auto BFC clearfix)
- fix booking page time slot overflow: bookingResultsWrapper 378px -> 360px

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-28 16:01:12 +01:00
Astral04 02169a89f6 booking form category show fix 2026-01-25 10:26:12 +01:00
Astral04 3a7dd84992 foglalás időpont +1 órás baszakodás 2025-12-16 11:10:33 +01:00
Astral04 fb7178a919 mobile redirect fix 2025-10-04 14:16:51 +02:00
Astral04 68c8245cef add all files 2025-10-04 11:38:07 +02:00