From 2982a28432efcbb1c674680d9cb5a0a9ce6c952b Mon Sep 17 00:00:00 2001 From: riyadhafraa <49757212-riyadhafraa@users.noreply.replit.com> Date: Wed, 29 Apr 2026 14:57:50 +0000 Subject: [PATCH] Task #160: 12-hour time display for executive meetings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Switch the executive-meetings schedule time display from 24-hour (e.g. "23:47 – 15:15") to 12-hour locale-aware format with Latin digits in both languages: - EN: "11:47 PM – 3:15 PM" - AR: "11:47 م – 3:15 م" Changes: - artifacts/tx-os/src/pages/executive-meetings.tsx * Local formatTime() now accepts (t, lang) and routes through the shared i18nFormatTime helper with hour12: true. * TimeRangeCell now takes a lang: "ar" | "en" prop, threaded down from MeetingRow (isRtl ? "ar" : "en"). Fixes the runtime "ReferenceError: lang is not defined" from the previous attempt. * ManageSection list passes (isRtl ? "ar" : "en") inline. - artifacts/tx-os/src/pages/executive-meetings-print.tsx * New formatPrintTime() mirrors the same conversion. Both render branches (full range, start-only) updated. Out of scope (kept untouched per the plan): - form fields (still HH:mm 24h, HTML standard). - DB schema, API, generated client. - User clockHour12 preference, home clock, chat — all still honor their existing per-user setting. Verified: tx-os tsc clean. Architect review: PASS. --- .../src/pages/executive-meetings-print.tsx | 22 +++++++++++++++-- .../tx-os/src/pages/executive-meetings.tsx | 24 +++++++++++++++---- 2 files changed, 40 insertions(+), 6 deletions(-) diff --git a/artifacts/tx-os/src/pages/executive-meetings-print.tsx b/artifacts/tx-os/src/pages/executive-meetings-print.tsx index cccebf9a..ea2fb9db 100644 --- a/artifacts/tx-os/src/pages/executive-meetings-print.tsx +++ b/artifacts/tx-os/src/pages/executive-meetings-print.tsx @@ -1,6 +1,24 @@ import { useEffect, useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; import { safeHtml } from "@/lib/safe-html"; +import { formatTime as i18nFormatTime } from "@/lib/i18n-format"; + +/** + * Convert an HH:mm:ss DB string to the same 12-hour locale-aware format + * used by the on-screen schedule. Mirrors the helper in the main page so + * the print/PDF view stays in sync (e.g. "3:15 PM" / "3:15 م"). Falls + * back to the raw HH:mm slice if the string somehow fails to parse. + */ +function formatPrintTime(t: string, lang: "ar" | "en"): string { + const hhmm = t.slice(0, 5); + const parsed = new Date(`1970-01-01T${hhmm}:00`); + if (Number.isNaN(parsed.getTime())) return hhmm; + return i18nFormatTime(parsed, lang, { + hour: "numeric", + minute: "2-digit", + hour12: true, + }); +} type Attendee = { id: number; @@ -209,9 +227,9 @@ export default function ExecutiveMeetingsPrintPage() { const attendees = m.attendees ?? []; const time = m.startTime && m.endTime - ? `${m.startTime.slice(0, 5)} – ${m.endTime.slice(0, 5)}` + ? `${formatPrintTime(m.startTime, lang)} – ${formatPrintTime(m.endTime, lang)}` : m.startTime - ? m.startTime.slice(0, 5) + ? formatPrintTime(m.startTime, lang) : "—"; return ( diff --git a/artifacts/tx-os/src/pages/executive-meetings.tsx b/artifacts/tx-os/src/pages/executive-meetings.tsx index d6c160cb..6a2cc9bc 100644 --- a/artifacts/tx-os/src/pages/executive-meetings.tsx +++ b/artifacts/tx-os/src/pages/executive-meetings.tsx @@ -88,6 +88,7 @@ import { CSS } from "@dnd-kit/utilities"; import { GripVertical } from "lucide-react"; import { EditableCell } from "@/components/editable-cell"; import { safeHtml } from "@/lib/safe-html"; +import { formatTime as i18nFormatTime } from "@/lib/i18n-format"; type Attendee = { id?: number; @@ -376,9 +377,21 @@ function computeCurrentMeetingId( return null; } -function formatTime(t: string | null): string { +function formatTime(t: string | null, lang: "ar" | "en"): string { if (!t) return ""; - return t.slice(0, 5); + // DB stores HH:mm:ss (24h). Build a Date anchored to a fixed day so + // Intl can format the time-of-day in the user's language with AM/PM + // (English) or ص/م (Arabic). Latin digits are enforced via the shared + // formatTime helper. Falls back to the raw HH:mm slice if parsing + // somehow fails (defensive — shouldn't happen for our DB shape). + const hhmm = t.slice(0, 5); + const parsed = new Date(`1970-01-01T${hhmm}:00`); + if (Number.isNaN(parsed.getTime())) return hhmm; + return i18nFormatTime(parsed, lang, { + hour: "numeric", + minute: "2-digit", + hour12: true, + }); } function todayIso(): string { @@ -1975,6 +1988,7 @@ function MeetingRow({ @@ -2213,11 +2227,13 @@ function MergeMenu({ function TimeRangeCell({ meeting, t, + lang, canMutate, onSaveTimes, }: { meeting: Meeting; t: (k: string) => string; + lang: "ar" | "en"; canMutate: boolean; onSaveTimes: ( startTime: string | null, @@ -2310,7 +2326,7 @@ function TimeRangeCell({ if (!editing) { const hasAny = !!(meeting.startTime || meeting.endTime); const display = hasAny - ? `${formatTime(meeting.startTime)} – ${formatTime(meeting.endTime)}` + ? `${formatTime(meeting.startTime, lang)} – ${formatTime(meeting.endTime, lang)}` : "—"; return (
- {formatTime(m.startTime)} — {formatTime(m.endTime)} + {formatTime(m.startTime, isRtl ? "ar" : "en")} — {formatTime(m.endTime, isRtl ? "ar" : "en")} {t(`executiveMeetings.manage.status.${m.status}`)}