fix(appointments): detail page back/breadcrumb return to the appointment day
AppointmentDetailPage now derives the appointment's local day from slot_start and points both the «بازگشت» button and the «نوبتها» breadcrumb to /admin/appointments?date=<that day>, so viewing/acting on an appointment from any day returns to that day instead of today. Test: the «نوبتها» breadcrumb links back to the appointment's date. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,43 @@
|
|||||||
|
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
||||||
|
import { screen, waitFor } from '@testing-library/react';
|
||||||
|
import { Routes, Route } from 'react-router-dom';
|
||||||
|
import { renderWithProviders } from '../test/utils';
|
||||||
|
|
||||||
|
vi.mock('../lib/api', () => ({
|
||||||
|
api: { get: vi.fn(), post: vi.fn(), patch: vi.fn(), put: vi.fn(), delete: vi.fn() },
|
||||||
|
ApiError: class extends Error {},
|
||||||
|
}));
|
||||||
|
|
||||||
|
import { api } from '../lib/api';
|
||||||
|
import AppointmentDetailPage from './AppointmentDetailPage';
|
||||||
|
|
||||||
|
const get = api.get as ReturnType<typeof vi.fn>;
|
||||||
|
// 2024-06-01 12:00 محلی
|
||||||
|
const slotStart = Math.floor(new Date('2024-06-01T12:00:00').getTime() / 1000);
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
get.mockReset();
|
||||||
|
get.mockResolvedValue({
|
||||||
|
success: true,
|
||||||
|
data: {
|
||||||
|
uuid: 'ap1', patient_name: 'ساغر', patient_mobile: '09120000000',
|
||||||
|
slot_start: slotStart, slot_end: slotStart + 1800, status: 'confirmed', version: 1, created_at: slotStart,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('AppointmentDetailPage — بازگشت به همان روز', () => {
|
||||||
|
it('the «نوبتها» breadcrumb points back to the appointment day', async () => {
|
||||||
|
renderWithProviders(
|
||||||
|
<Routes>
|
||||||
|
<Route path="/admin/appointments/:uuid" element={<AppointmentDetailPage />} />
|
||||||
|
</Routes>,
|
||||||
|
{ route: '/admin/appointments/ap1' },
|
||||||
|
);
|
||||||
|
// پس از بارگذاری نوبت، لینک «نوبتها» باید به روزِ نوبت اشاره کند
|
||||||
|
await waitFor(() => {
|
||||||
|
const link = screen.getByText('نوبتها').closest('a');
|
||||||
|
expect(link?.getAttribute('href')).toContain('date=2024-06-01');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -22,6 +22,13 @@ const ALL_STATUSES: { value: AppointmentStatus; label: string }[] = [
|
|||||||
{ value: 'expired', label: 'منقضی' },
|
{ value: 'expired', label: 'منقضی' },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// روزِ محلیِ نوبت (YYYY-MM-DD) برای بازگشت به همان تاریخ در لیست.
|
||||||
|
const isoDay = (ts?: number | null) => {
|
||||||
|
if (!ts) return '';
|
||||||
|
const d = new Date(ts * 1000);
|
||||||
|
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`;
|
||||||
|
};
|
||||||
|
|
||||||
const timeOf = (ts?: number | null) => {
|
const timeOf = (ts?: number | null) => {
|
||||||
const d = toDate(ts ?? null);
|
const d = toDate(ts ?? null);
|
||||||
return d
|
return d
|
||||||
@@ -73,6 +80,9 @@ export default function AppointmentDetailPage() {
|
|||||||
|
|
||||||
// پاسخ single تودرتو است: { data: { data: {...} } }
|
// پاسخ single تودرتو است: { data: { data: {...} } }
|
||||||
const appt: any = (data?.data as any)?.data ?? data?.data;
|
const appt: any = (data?.data as any)?.data ?? data?.data;
|
||||||
|
// بازگشت به همان روزِ نوبت (نه امروز).
|
||||||
|
const day = isoDay(appt?.slot_start);
|
||||||
|
const backTo = day ? `/admin/appointments?date=${day}` : '/admin/appointments';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
@@ -80,11 +90,11 @@ export default function AppointmentDetailPage() {
|
|||||||
title="جزئیات نوبت"
|
title="جزئیات نوبت"
|
||||||
breadcrumbs={[
|
breadcrumbs={[
|
||||||
{ label: 'داشبورد', to: '/admin/dashboard' },
|
{ label: 'داشبورد', to: '/admin/dashboard' },
|
||||||
{ label: 'نوبتها', to: '/admin/appointments' },
|
{ label: 'نوبتها', to: backTo },
|
||||||
{ label: 'جزئیات' },
|
{ label: 'جزئیات' },
|
||||||
]}
|
]}
|
||||||
action={
|
action={
|
||||||
<button onClick={() => navigate('/admin/appointments')}
|
<button onClick={() => navigate(backTo)}
|
||||||
className="flex items-center gap-2 text-sm text-slate-500 dark:text-slate-400 hover:text-slate-800 dark:hover:text-slate-100 transition-colors">
|
className="flex items-center gap-2 text-sm text-slate-500 dark:text-slate-400 hover:text-slate-800 dark:hover:text-slate-100 transition-colors">
|
||||||
<ArrowRightIcon className="w-4 h-4" />
|
<ArrowRightIcon className="w-4 h-4" />
|
||||||
بازگشت
|
بازگشت
|
||||||
|
|||||||
Reference in New Issue
Block a user