#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.
- `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:
@@ -0,0 +1,70 @@
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
type AnalogClockProps = {
|
||||
size?: number;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
export function AnalogClock({ size = 44, className = "" }: AnalogClockProps) {
|
||||
const [now, setNow] = useState<Date>(() => new Date());
|
||||
useEffect(() => {
|
||||
const id = window.setInterval(() => setNow(new Date()), 1000);
|
||||
return () => window.clearInterval(id);
|
||||
}, []);
|
||||
|
||||
const hours = now.getHours() % 12;
|
||||
const minutes = now.getMinutes();
|
||||
const seconds = now.getSeconds();
|
||||
const hourAngle = (hours + minutes / 60) * 30;
|
||||
const minuteAngle = (minutes + seconds / 60) * 6;
|
||||
const secondAngle = seconds * 6;
|
||||
|
||||
const cx = 50;
|
||||
const cy = 50;
|
||||
const navy = "#0B1E3F";
|
||||
const gold = "#C9A04C";
|
||||
|
||||
return (
|
||||
<svg
|
||||
width={size}
|
||||
height={size}
|
||||
viewBox="0 0 100 100"
|
||||
className={className}
|
||||
role="img"
|
||||
aria-label="analog clock"
|
||||
data-testid="em-analog-clock"
|
||||
>
|
||||
<circle cx={cx} cy={cy} r="46" fill="#ffffff" stroke={navy} strokeWidth="3" />
|
||||
{Array.from({ length: 12 }).map((_, i) => {
|
||||
const angle = (i * 30 * Math.PI) / 180;
|
||||
const x1 = cx + Math.sin(angle) * 40;
|
||||
const y1 = cy - Math.cos(angle) * 40;
|
||||
const x2 = cx + Math.sin(angle) * 44;
|
||||
const y2 = cy - Math.cos(angle) * 44;
|
||||
return (
|
||||
<line
|
||||
key={i}
|
||||
x1={x1}
|
||||
y1={y1}
|
||||
x2={x2}
|
||||
y2={y2}
|
||||
stroke={navy}
|
||||
strokeWidth={i % 3 === 0 ? 2.5 : 1.5}
|
||||
strokeLinecap="round"
|
||||
/>
|
||||
);
|
||||
})}
|
||||
<g transform={`rotate(${hourAngle} ${cx} ${cy})`}>
|
||||
<line x1={cx} y1={cy + 6} x2={cx} y2={cy - 24} stroke={navy} strokeWidth="4" strokeLinecap="round" />
|
||||
</g>
|
||||
<g transform={`rotate(${minuteAngle} ${cx} ${cy})`}>
|
||||
<line x1={cx} y1={cy + 8} x2={cx} y2={cy - 34} stroke={navy} strokeWidth="2.75" strokeLinecap="round" />
|
||||
</g>
|
||||
<g transform={`rotate(${secondAngle} ${cx} ${cy})`}>
|
||||
<line x1={cx} y1={cy + 10} x2={cx} y2={cy - 38} stroke={gold} strokeWidth="1.5" strokeLinecap="round" />
|
||||
</g>
|
||||
<circle cx={cx} cy={cy} r="3" fill={navy} />
|
||||
<circle cx={cx} cy={cy} r="1.25" fill={gold} />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user