feat(waitlist): make the day-part preference, the conversion and the expiry real
Three rows of task 13 were storing data nothing ever read. `preferred_day_parts` was saved and displayed but never applied when matching. It was deferred because "evening" has no fixed meaning — but branches already carry a timezone (DoctorAddress::getTimezone), so the boundaries can be pinned: morning [6,12), afternoon [12,17), evening [17,22), in the branch's local hour. The list is now closed and validated; an unknown part is a 422 rather than a preference that silently matches nothing. The filter runs *before* the cut to ten recipients — otherwise the first ten slots go to people who did not want that hour and the real eleventh person is never told. `markConverted()` was dead code: nothing called it. It now runs off the AppointmentBooked domain event rather than from inside BookingService, because converting is a side effect of booking — inside the booking transaction a waitlist error could roll back the patient's actual appointment. The match is deliberately narrow (same patient, same service, start inside the window); a loose match closes a row the patient is still waiting on. It is idempotent, so redelivery is harmless. Expiry now exists as a service, a daily scheduled message and `app:waitlist:expire`. Expired rows were already excluded from matching, so this is display hygiene, not a behaviour fix: without it the waitlist page fills with dead entries and the operator cannot tell which are still live. It sets a status rather than deleting — who waited and never got a slot is data. Also: a waitlist window is capped at 90 days, matching the booking horizon. An unbounded window is a row that never expires and shows up in every match. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -9,6 +9,16 @@ import { usePermissions } from '../hooks/usePermissions';
|
||||
import { useWaitlist } from '../hooks/useCancellation';
|
||||
import type { WaitlistEntry } from '../types';
|
||||
|
||||
/**
|
||||
* مرزهای ساعتش در بکاند است (`WaitlistEntry::DAY_PARTS`)؛ اینجا فقط ترجمهٔ نمایش.
|
||||
* تعریف مرز در دو جا یعنی روزی که یکی عوض شود، بیمار برای ساعتی خبر شود که رد کرده بود.
|
||||
*/
|
||||
const DAY_PART_LABELS: Record<string, string> = {
|
||||
morning: 'صبح',
|
||||
afternoon: 'بعدازظهر',
|
||||
evening: 'عصر',
|
||||
};
|
||||
|
||||
const STATUS: Record<WaitlistEntry['status'], { label: string; className: string }> = {
|
||||
waiting: { label: 'در انتظار', className: 'badge' },
|
||||
notified: { label: 'خبر داده شد', className: 'badge amber' },
|
||||
@@ -53,7 +63,9 @@ export default function WaitlistPage() {
|
||||
header: 'زمان ترجیحی',
|
||||
render: (e) => (
|
||||
<span style={{ fontSize: 12, color: 'var(--text-2)' }}>
|
||||
{e.preferred_day_parts.length === 0 ? 'بیتفاوت' : e.preferred_day_parts.join('، ')}
|
||||
{e.preferred_day_parts.length === 0
|
||||
? 'بیتفاوت'
|
||||
: e.preferred_day_parts.map((p) => DAY_PART_LABELS[p] ?? p).join('، ')}
|
||||
</span>
|
||||
),
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user