1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
|
import { useAuth } from "../contexts/AuthContext";
import { useSupervisorQuestions } from "../contexts/SupervisorQuestionsContext";
import { useUserSettings } from "../hooks/useUserSettings";
import { RewriteLink } from "./RewriteLink";
interface NavLink {
label: string;
href: string;
requiresAuth?: boolean;
external?: boolean;
/**
* When true the link is hidden once the user has flipped on the
* document-mode UI — those areas (Exec, Contracts) are subsumed by the
* directive-document interface and surfacing them just creates noise.
*/
hideInDocumentMode?: boolean;
}
const NAV_LINKS: NavLink[] = [
{ label: "Listen", href: "/listen" },
{ label: "Directives", href: "/directives", requiresAuth: true },
{ label: "Orders", href: "/orders", requiresAuth: true },
{
label: "Contracts",
href: "/contracts",
requiresAuth: true,
hideInDocumentMode: true,
},
{
label: "Exec",
href: "/exec",
requiresAuth: true,
hideInDocumentMode: true,
},
{ label: "Daemons", href: "/daemons", requiresAuth: true },
{ label: "History", href: "/history", requiresAuth: true },
];
export function NavStrip() {
const { isAuthenticated, isAuthConfigured, signOut, user } = useAuth();
const { pendingQuestions } = useSupervisorQuestions();
const { settings } = useUserSettings();
const documentMode = settings?.documentModeEnabled ?? false;
const directiveQuestionCount = pendingQuestions.filter(q => q.directiveId).length;
const handleSignOut = async () => {
await signOut();
window.location.href = "/login";
};
// Check if user has access (authenticated or auth not configured)
const hasAccess = isAuthenticated || !isAuthConfigured;
return (
<nav
className="flex items-center gap-2.5 px-3 py-2.5 border-t border-b border-dashed border-[rgba(117,170,252,0.35)] bg-[#0c1729] font-mono uppercase tracking-wide text-[11px]"
aria-label="Main navigation"
>
<span className="text-[#9bc3ff] pr-2.5 border-r border-[rgba(117,170,252,0.35)]">
NAV//
</span>
<div className="flex flex-wrap gap-2 items-center flex-1">
{NAV_LINKS.filter(
(link) => !(documentMode && link.hideInDocumentMode),
).map((link) => {
// In document mode, the directive concept is surfaced to the user
// as "Contracts" — keep the same href (/directives) so existing
// links and bookmarks continue to work.
const displayLabel =
documentMode && link.label === "Directives" ? "Contracts" : link.label;
return (
<span key={link.label} className="relative inline-flex items-center">
<RewriteLink
to={link.href}
disabled={link.requiresAuth && !hasAccess}
external={link.external}
>
{displayLabel}
</RewriteLink>
{link.label === "Directives" && directiveQuestionCount > 0 && (
<span className="ml-0.5 inline-block w-2 h-2 rounded-full bg-amber-400 animate-pulse" title={`${directiveQuestionCount} pending question(s)`} />
)}
</span>
);
})}
</div>
<div className="flex items-center gap-2 pl-2.5 border-l border-[rgba(117,170,252,0.35)]">
{isAuthenticated && isAuthConfigured ? (
<>
{user?.email && (
<span className="text-[#9bc3ff] opacity-60">{user.email}</span>
)}
<RewriteLink to="/settings">Settings</RewriteLink>
<button
type="button"
onClick={handleSignOut}
className="bg-transparent border-none text-[#9bc3ff] hover:text-white transition-colors cursor-pointer uppercase text-[11px] font-mono tracking-wide p-0"
>
Logout
</button>
</>
) : (
<RewriteLink to="/login">Login</RewriteLink>
)}
</div>
</nav>
);
}
|