improvement: rtl direction, solar calendar and persian formatting added for persian users.

This commit is contained in:
2026-07-14 01:27:11 +03:30
parent 27eae25f61
commit ec2b23f4b1
50 changed files with 1410 additions and 528 deletions

View File

@@ -1,7 +1,7 @@
'use client';
import { useEffect, useRef } from 'react';
import { useTranslations } from 'next-intl';
import { useLocale, useTranslations } from 'next-intl';
import { DialogCloseButton } from '@/components/ui/shared/DialogCloseButton';
import {
purposeBannerStyle,
@@ -9,6 +9,7 @@ import {
} from '@/components/appointments/appointmentPurposeStyles';
import type { AppointmentRecord } from '@/types/appointment';
import type { TreatmentCatalogEntry } from '@/types/treatment-catalog';
import { formatAppTimeRange } from '@/lib/i18n/format';
type AppointmentOverlapPopoverProps = {
appointments: AppointmentRecord[];
@@ -18,11 +19,8 @@ type AppointmentOverlapPopoverProps = {
onClose: () => void;
};
function formatTimeRange(apt: AppointmentRecord): string {
const start = new Date(apt.startAt);
const end = new Date(apt.endAt);
const opts: Intl.DateTimeFormatOptions = { hour: 'numeric', minute: '2-digit' };
return `${start.toLocaleTimeString(undefined, opts)} ${end.toLocaleTimeString(undefined, opts)}`;
function formatTimeRange(apt: AppointmentRecord, locale: string): string {
return formatAppTimeRange(apt.startAt, apt.endAt, locale);
}
export function AppointmentOverlapPopover({
@@ -32,6 +30,7 @@ export function AppointmentOverlapPopover({
onSelect,
onClose,
}: AppointmentOverlapPopoverProps) {
const locale = useLocale();
const t = useTranslations('appointments');
const panelRef = useRef<HTMLDivElement>(null);
@@ -99,7 +98,7 @@ export function AppointmentOverlapPopover({
<p className="text-xs font-medium truncate">
{apt.patient.firstName} {apt.patient.lastName}
</p>
<p className="text-[11px] opacity-90 tabular-nums">{formatTimeRange(apt)}</p>
<p className="text-[11px] opacity-90 tabular-nums">{formatTimeRange(apt, locale)}</p>
<p className="text-[10px] opacity-80 truncate">
{purposeLabel(apt.purpose, treatmentCatalog)}
</p>

View File

@@ -1,7 +1,7 @@
'use client';
import { useMemo, useState } from 'react';
import { useTranslations } from 'next-intl';
import { useLocale, useTranslations } from 'next-intl';
import type { AppointmentColumnProvider, AppointmentRecord } from '@/types/appointment';
import {
SCHEDULE_SLOT_MINUTES,
@@ -96,6 +96,7 @@ export function AppointmentScheduleGrid({
onAppointmentClick,
onAppointmentOutsideHours,
}: AppointmentScheduleGridProps) {
const locale = useLocale();
const t = useTranslations('appointments');
const [overlapPopover, setOverlapPopover] = useState<OverlapPopoverState | null>(null);
@@ -218,7 +219,7 @@ export function AppointmentScheduleGrid({
className="absolute left-0 right-0 text-[11px] text-text-muted flex items-start justify-end pr-1.5 pt-0.5 border-b border-border/50"
style={{ top, height }}
>
{formatMinuteLabel(hour * 60)}
{formatMinuteLabel(hour * 60, locale)}
</div>
);
})}
@@ -263,7 +264,7 @@ export function AppointmentScheduleGrid({
: slotDisabled
? t('slotCannotCreate')
: t('slotBookAt', {
time: formatMinuteLabel(slotStartMinute),
time: formatMinuteLabel(slotStartMinute, locale),
})
}
className={`absolute left-0 right-0 border-b border-border/50 transition-colors ${