#572 president-only focused view for executive meetings

User asked that when the President (`executive_ceo` role) logs into
the Meetings page he sees a clean "presentation" layout: just the
schedule table, an analog clock plus the weekday/date of the
schedule, and nothing else. The Manage / Notifications / Audit /
PDF tabs and the PDF-export shortcut button are noisy for him and
must disappear — but his backend permissions stay intact.

Changes (frontend only — backend role gates untouched):
- New component `components/executive-meetings/analog-clock.tsx`:
  small SVG analog clock (hour/minute/second hands, navy + gold),
  ticks every second, ~44px default.
- New inner `PresidentClockBar` component in `executive-meetings.tsx`:
  renders the analog clock + weekday + **digital time** (ticking
  per second, localized) + selected schedule date in one block.
- `pages/executive-meetings.tsx`:
  - Derived `isPresidentView` from the `/me` roles: true iff
    `executive_ceo` is present AND the user is NOT also `admin`
    or `executive_office_manager` (admins keep full UI).
  - Effect forces `section = "schedule"` whenever
    `isPresidentView` becomes true, so the president can never
    land on a now-hidden tab.
  - Hides the header PDF-shortcut button and the entire SECTIONS
    tab list when `isPresidentView`. The `#em-toolbar-portal` is
    kept so the date picker still mounts.
  - `ScheduleSection` accepts two new optional props:
    `isPresidentView` and `lang`. When `isPresidentView` is on,
    the toolbar renders the AnalogClock next to a label showing
    `formatWeekday(date, lang, "long")` and `formatDate(date, lang)`
    (e.g. "Sunday · May 17, 2026" / "الأحد · 17 مايو 2026"). The
    label updates immediately when the date input changes.
  - Edit-mode toggle stays gated on `canMutate`, so the president
    (who has canMutate) still gets the inline-edit pencil if he
    wants it.

Out of scope: backend role changes, applying the focused view to
other roles, manual presentation-mode toggle. Verified via tsc
(only the pre-existing `use-push-subscription` error remains).
This commit is contained in:
riyadhafraa
2026-05-17 14:34:12 +00:00
parent 27384d019a
commit 21dda3d372
@@ -115,7 +115,7 @@ import { EditableCell } from "@/components/editable-cell";
// from the schedule row quick-actions menu shares one implementation.
import { PostponeDialog } from "@/components/executive-meetings/upcoming-meeting-alert";
import { AnalogClock } from "@/components/executive-meetings/analog-clock";
import { formatDate, formatWeekday } from "@/lib/i18n-format";
import { formatDate, formatTime as formatTimeI18n, formatWeekday } from "@/lib/i18n-format";
import { safeHtml, htmlToPlainText, wrapAsParagraph } from "@/lib/safe-html";
import {
ALERT_COLOR_PRESETS,
@@ -781,6 +781,34 @@ export default function ExecutiveMeetingsPage() {
);
}
function PresidentClockBar({ date, lang }: { date: string; lang: "ar" | "en" }) {
const [now, setNow] = useState<Date>(() => new Date());
useEffect(() => {
const id = window.setInterval(() => setNow(new Date()), 1000);
return () => window.clearInterval(id);
}, []);
return (
<div
className="flex items-center gap-2 sm:gap-3 ps-2 sm:ps-3 ms-1 border-s border-gray-200"
data-testid="em-president-clock-bar"
>
<AnalogClock size={40} />
<div className="leading-tight">
<div className="flex items-center gap-2 text-[#0B1E3F] font-semibold text-sm sm:text-base">
<span data-testid="em-president-weekday">{formatWeekday(date, lang, "long")}</span>
<span className="text-muted-foreground/60">·</span>
<span className="tabular-nums" data-testid="em-president-digital-time">
{formatTimeI18n(now, lang, { hour: "2-digit", minute: "2-digit", second: "2-digit", hour12: true })}
</span>
</div>
<div className="text-xs sm:text-sm text-muted-foreground tabular-nums" data-testid="em-president-date">
{formatDate(date, lang)}
</div>
</div>
</div>
);
}
function HeaderClock({ lang }: { lang: "ar" | "en" }) {
const [now, setNow] = useState<Date>(() => new Date());
useEffect(() => {
@@ -2677,20 +2705,7 @@ function ScheduleSection({
/>
</label>
{isPresidentView && (
<div
className="flex items-center gap-2 sm:gap-3 ps-2 sm:ps-3 ms-1 border-s border-gray-200"
data-testid="em-president-clock-bar"
>
<AnalogClock size={40} />
<div className="leading-tight">
<div className="text-[#0B1E3F] font-semibold text-sm sm:text-base">
{formatWeekday(date, lang, "long")}
</div>
<div className="text-xs sm:text-sm text-muted-foreground tabular-nums">
{formatDate(date, lang)}
</div>
</div>
</div>
<PresidentClockBar date={date} lang={lang} />
)}
</>
);