import { useMemo, useRef, useState } from 'react' import { downloadDiagramFile, readDiagramFile, DiagramImportError } from '../../data/exportImport' import { supabase } from '../../data/supabaseClient' import { useAdminReviewStore } from '../../state/adminReviewStore' import { useAuthStore } from '../../state/authStore' import { useDiagramStore } from '../../state/diagramStore' import { useSubmissionStore } from '../../state/submissionStore' import { useCanEdit } from '../../hooks/useCanEdit' import AdminUsersModal from '../admin/AdminUsersModal' import ProfileModal from '../account/ProfileModal' import AnnouncementComposerModal from '../announcements/AnnouncementComposerModal' import DropdownMenu, { DropdownMenuItem } from '../common/DropdownMenu' import DiagramSharingModal from '../sharing/DiagramSharingModal' import VersionHistoryModal from '../sharing/VersionHistoryModal' import AdminReviewModal from '../submissions/AdminReviewModal' import MySubmissionsModal from '../submissions/MySubmissionsModal' import DiagramManagerModal from './DiagramManagerModal' // When "My Submissions" was last opened — a per-user UI preference (like // hiddenPublicDeviceTemplateIds in catalogStore), used to badge outcomes // (approved/rejected) you haven't seen yet without a server-side // notifications table. const LAST_SEEN_SUBMISSIONS_KEY = 'av-planner:lastSeenSubmissionsAt' function getLastSeenSubmissionsAt(): string { try { return window.localStorage.getItem(LAST_SEEN_SUBMISSIONS_KEY) ?? '' } catch { return '' } } function setLastSeenSubmissionsAt(iso: string): void { try { window.localStorage.setItem(LAST_SEEN_SUBMISSIONS_KEY, iso) } catch { // Not essential — worst case an already-seen outcome stays badged. } } export default function TopBar() { const diagram = useDiagramStore((s) => s.diagram) const canEdit = useCanEdit() const renameDiagram = useDiagramStore((s) => s.renameDiagram) const importDiagram = useDiagramStore((s) => s.importDiagram) const mySubmissions = useSubmissionStore((s) => s.mySubmissions) const username = useAuthStore((s) => s.username) const role = useAuthStore((s) => s.role) const isAdmin = role === 'admin' || role === 'super_admin' const isSuperAdmin = role === 'super_admin' const canPostAnnouncements = useAuthStore((s) => s.canPostAnnouncements) const canManageAnnouncements = isSuperAdmin || (role === 'admin' && canPostAnnouncements) const adminPendingCount = useAdminReviewStore((s) => s.allSubmissions.filter((sub) => sub.status === 'pending').length) const fileInputRef = useRef(null) const [diagramManagerOpen, setDiagramManagerOpen] = useState(false) const [versionHistoryOpen, setVersionHistoryOpen] = useState(false) const [sharingOpen, setSharingOpen] = useState(false) const [submissionsOpen, setSubmissionsOpen] = useState(false) const [adminReviewOpen, setAdminReviewOpen] = useState(false) const [adminUsersOpen, setAdminUsersOpen] = useState(false) const [announcementComposerOpen, setAnnouncementComposerOpen] = useState(false) const [profileOpen, setProfileOpen] = useState(false) const pendingSubmissionCount = useMemo( () => mySubmissions.filter((s) => s.status === 'pending').length, [mySubmissions], ) const unseenOutcomeCount = useMemo(() => { const lastSeen = getLastSeenSubmissionsAt() return mySubmissions.filter((s) => s.status !== 'pending' && s.updatedAt > lastSeen).length }, [mySubmissions]) const openMySubmissions = () => { setSubmissionsOpen(true) setLastSeenSubmissionsAt(new Date().toISOString()) } const handleExport = () => downloadDiagramFile(diagram) const handleImportClick = () => fileInputRef.current?.click() const handleFileChange = async (event: React.ChangeEvent) => { const file = event.target.files?.[0] event.target.value = '' if (!file) return try { const imported = await readDiagramFile(file) await importDiagram(imported) } catch (err) { const message = err instanceof DiagramImportError ? err.message : 'Could not read that file.' window.alert(`Import failed: ${message}`) } } return (
Diagrav renameDiagram(e.target.value)} disabled={!canEdit} className="w-28 min-w-0 rounded border border-transparent px-2 py-1 text-sm text-slate-700 hover:border-slate-200 focus:border-slate-300 focus:outline-none disabled:cursor-not-allowed disabled:opacity-60 sm:w-48 md:w-64" />
{/* Editing/reviewing actions below aren't useful on a mobile-width viewport (view-only there via useCanEdit) — hidden rather than removed so they come straight back if the window's just narrow, not actually a phone (see useIsMobile). "Diagrams" above and "Account" below stay: picking which diagram to look at, and signing out, are both still things a mobile visitor might want. */}
setVersionHistoryOpen(true)}>History setSharingOpen(true)}>Share Import Export {isAdmin && ( Admin {adminPendingCount > 0 && ( {adminPendingCount} )} } > setAdminReviewOpen(true)} badge={ adminPendingCount > 0 && ( {adminPendingCount} ) } > Review Queue {isSuperAdmin && setAdminUsersOpen(true)}>Manage Users} {canManageAnnouncements && ( setAnnouncementComposerOpen(true)}>Announcement )} )}
setProfileOpen(true)}>Profile supabase.auth.signOut()}>Sign out
{diagramManagerOpen && setDiagramManagerOpen(false)} />} {versionHistoryOpen && setVersionHistoryOpen(false)} />} {sharingOpen && setSharingOpen(false)} />} {submissionsOpen && setSubmissionsOpen(false)} />} {adminReviewOpen && setAdminReviewOpen(false)} />} {adminUsersOpen && setAdminUsersOpen(false)} />} {announcementComposerOpen && setAnnouncementComposerOpen(false)} />} {profileOpen && setProfileOpen(false)} />}
) }