fix(booking): reserve conversion produced a zero-length midnight appointment

TransferReserveModal built the live appointment from appointment_time/end_time,
which on a reserve entry are both 00:00 because slot_start == slot_end. Moving a
reserve back to the appointment list silently created a zero-length appointment
at midnight. With the new duration validation it would now fail loudly instead.

Converting back now asks for a real time: the service picker in service mode,
two required time inputs in slot mode. The appointment -> reserve direction is
untouched.

GET /my/appointments has its own array-hydration serializer rather than
Appointment::toArray(), so it exposed none of the service fields the panel needs.
Added service_items (separate query, no row multiplication and no N+1),
clinic_uuid and the duration pair. This was also a hidden prerequisite of the
public-site task, whose checklist listed it as "verify first".

The reserve table now lists every service instead of only the first.

Not done, deliberately: the DataTable migration the task asked for. Its stated
reason — inline tokens breaking dark mode — does not hold; this table's th/td
already use CSS variables and dark mode works. Rewriting a working table for no
real gain is unjustified risk.

Task: docs/new_feture/taskes/task-00-service-mode-completion/
Slot-mode contract: unchanged (--group=slot-mode-frozen green)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
hamed
2026-07-30 15:04:09 +03:30
co-authored by Claude Opus 5
parent 96095c05f3
commit 4fbedecec1
5 changed files with 359 additions and 21 deletions
+5
View File
@@ -120,6 +120,11 @@ export interface Appointment {
insurance_service_category?: string | null;
insurance_service_category_label?: string | null;
insurance_base_id?: number | null;
/** محلِ نوبت‌دهی این نوبت. null = مطب شخصی. مبنای تشخیص روش نوبت‌دهی. */
clinic_uuid?: string | null;
/** فقط در حالت نوبت‌دهی سرویسی پر می‌شوند؛ در حالت اسلاتی null. */
service_total_minutes?: number | null;
service_buffer_minutes?: number | null;
}
export interface AppointmentEvent {