#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:
@@ -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} />
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user