feat(timezone): implement Tehran timezone handling across the application

This commit is contained in:
hamed
2026-07-16 00:25:21 +03:30
parent 8cd1253b81
commit 2f060bd5be
10 changed files with 90 additions and 17 deletions
+2 -2
View File
@@ -12,6 +12,7 @@ import SearchableSelect from '../components/ui/SearchableSelect';
import { WalletChargeLink } from '../components/AppointmentActions';
import { useDoctorBookingServices } from '../hooks/useDoctorBookingServices';
import ServiceSlotPicker from '../components/appointments/ServiceSlotPicker';
import { tehranWallClockToUnix } from '../lib/utils';
/**
* افزودن نوبت — صفحهٔ کامل (بازسازی `CreateTurn.jsx` طرح tauri). از همان endpointهای
@@ -22,8 +23,7 @@ import ServiceSlotPicker from '../components/appointments/ServiceSlotPicker';
interface Option { uuid: string; name?: string; full_name?: string }
interface PatientRow { uuid: string; user_name?: string; user_mobile?: string; user_national_code?: string }
const toEpoch = (isoDate: string, time: string) =>
Math.floor(new Date(`${isoDate}T${time || '00:00'}`).getTime() / 1000);
const toEpoch = (isoDate: string, time: string) => tehranWallClockToUnix(isoDate, time);
const addMinutes = (time: string, min: number) => {
const [h, m] = time.split(':').map(Number);
const t = h * 60 + m + min;
+4 -4
View File
@@ -9,7 +9,7 @@ import { toast } from 'sonner';
import { api } from '../lib/api';
import type { PaginatedResponse, ApiResponse } from '../lib/api';
import type { Appointment } from '../types';
import { formatDate, toGregorianDate } from '../lib/utils';
import { formatDate, toGregorianDate, formatTime } from '../lib/utils';
import { useAuthStore } from '../stores/authStore';
import Pagination from '../components/ui/Pagination';
import AppointmentFiltersModal, { applyAppointmentFilters, EMPTY_FILTERS } from '../components/AppointmentFiltersModal';
@@ -432,7 +432,7 @@ export default function AppointmentsPage() {
// حالت سرویسی: اسلات ثابت وجود ندارد — برای هر شیفتِ کاری، نوبت‌های رزروشده
// نمایش داده می‌شوند و باقیِ زمان به‌صورت بازه‌(های) خالیِ قابل‌رزرو بین آن‌ها.
if (serviceMode) {
const fmt = (ts: number) => new Date(ts * 1000).toLocaleTimeString('fa-IR', { hour: '2-digit', minute: '2-digit' });
const fmt = (ts: number) => formatTime(ts);
const parseHM = (t: string) => { const [h, m] = (t ?? '00:00').split(':').map(Number); return (h * 3600) + (m * 60); };
const rawSessions: any[] = (slotsQuery.data?.data as any)?.sessions ?? [];
const booked = [...activeByStart.values()].sort((a, b) => Number(a.slot_start) - Number(b.slot_start));
@@ -479,8 +479,8 @@ export default function AppointmentsPage() {
out.push({
start: slotStart,
end: slotEnd,
start_time: s.start_time ?? new Date(slotStart * 1000).toLocaleTimeString('fa-IR', { hour: '2-digit', minute: '2-digit' }),
end_time: s.end_time ?? new Date(slotEnd * 1000).toLocaleTimeString('fa-IR', { hour: '2-digit', minute: '2-digit' }),
start_time: s.start_time ?? formatTime(slotStart),
end_time: s.end_time ?? formatTime(slotEnd),
is_available: s.is_available as boolean,
appointment: activeByStart.get(slotStart) ?? null,
cancelled_appointment: cancelledByStart.get(slotStart) ?? null,