-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathPrimitives.jsx
More file actions
157 lines (143 loc) · 7.86 KB
/
Copy pathPrimitives.jsx
File metadata and controls
157 lines (143 loc) · 7.86 KB
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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
// Opaque UI Kit — shared primitive components.
// NOTE: each component file exports onto `window` at the end so
// multiple <script type="text/babel"> blocks can share scope.
const OpaqueLogo = ({ width = 90, color = "currentColor" }) => (
<svg width={width} height={width * (14/80)} viewBox="0 0 80 14" style={{ color }} fill="currentColor">
<path d="M 70.21 0.244 L 80 0.244 L 80 2.395 L 72.669 2.395 L 72.669 5.736 L 79.799 5.736 L 79.799 8.02 L 72.669 8.02 L 72.669 11.612 L 80 11.612 L 80 13.794 L 70.21 13.794 L 70.21 0.244 Z M 65.651 0.244 L 65.651 8.591 C 65.651 10.197 64.843 11.764 62.165 11.778 C 59.487 11.764 58.68 10.197 58.68 8.591 L 58.68 0.244 L 56.229 0.244 L 56.229 8.546 C 56.229 11.813 58.006 14 62.165 14 C 66.324 14 68.101 11.813 68.101 8.546 L 68.101 0.244 L 65.651 0.244 Z M 52.112 12.03 L 55.114 12.03 C 55.425 12.684 55.834 13.265 56.336 13.762 C 56.419 13.844 56.504 13.923 56.591 14 L 46.827 14 C 42.343 14 39.605 10.866 39.605 7 C 39.605 3.134 42.343 0 46.827 0 C 51.31 0 54.048 3.134 54.048 7 C 54.048 9.463 52.802 11.379 50.936 12.384 C 51.34 12.188 51.598 12.03 52.112 12.03 Z M 46.827 11.846 C 49.748 11.846 51.49 9.676 51.49 7 C 51.49 4.324 49.748 2.155 46.827 2.155 C 43.905 2.155 42.163 4.324 42.163 7 C 42.163 9.676 43.905 11.846 46.827 11.846 Z M 29.852 10.721 L 28.591 13.793 L 25.805 13.793 C 25.836 13.702 31.545 0.832 31.805 0.244 L 33.942 0.244 C 34.203 0.832 39.912 13.702 39.943 13.793 L 37.157 13.793 L 35.896 10.721 L 29.852 10.721 Z M 32.906 3.189 L 32.841 3.189 L 30.706 8.627 L 35.04 8.627 L 32.906 3.189 Z M 27.65 4.41 C 27.65 6.554 26.011 8.638 23.409 8.638 L 18.573 8.638 L 18.573 13.794 L 16.078 13.794 L 16.078 0.244 L 22.888 0.244 C 26.548 0.244 27.65 2.265 27.65 4.41 Z M 25.156 4.454 C 25.156 3.381 24.556 2.314 23.193 2.314 L 18.573 2.314 L 18.573 6.565 L 22.651 6.565 C 24.414 6.565 25.156 5.526 25.156 4.454 Z M 7.222 0 C 2.738 0 0 3.134 0 7 C 0 10.866 2.738 14 7.222 14 C 11.705 14 14.444 10.866 14.444 7 C 14.444 3.134 11.705 0 7.222 0 Z M 11.885 7 C 11.885 9.676 10.143 11.845 7.222 11.845 C 4.301 11.845 2.558 9.676 2.558 7 C 2.558 4.324 4.3 2.154 7.222 2.154 C 10.144 2.154 11.885 4.324 11.885 7 Z"/>
</svg>
);
const Icon = ({ name, size = 20, filled = false, style }) => (
<span className="material-symbols-outlined" style={{
fontSize: size,
fontVariationSettings: filled ? "'FILL' 1" : undefined,
...style
}}>{name}</span>
);
const Button = ({ variant = "primary", size = "md", icon, children, onClick }) => (
<button className={`btn btn-${variant}${size === "sm" ? " btn-sm" : ""}`} onClick={onClick}>
{icon && <Icon name={icon} size={size === "sm" ? 16 : 18} />}
{children}
</button>
);
const IconButton = ({ icon, solid = false, onClick, title }) => (
<button className={`icon-btn${solid ? " solid" : ""}`} onClick={onClick} title={title}>
<Icon name={icon} size={18} />
</button>
);
const Chip = ({ variant = "success", children }) => (
<span className={`chip chip-${variant}`}>{children}</span>
);
const Status = ({ tone = "success", children }) => (
<span className="status"><span className={`dot dot-${tone}`} />{children}</span>
);
const Field = ({ icon = "search", placeholder, value, onChange, width }) => (
<label className="field-wrap" style={{ width }}>
{icon && <Icon name={icon} size={18} />}
<input value={value || ""} placeholder={placeholder} onChange={e => onChange && onChange(e.target.value)} />
</label>
);
const Select = ({ label, size, width }) => (
<button className={`select${size === "sm" ? " sm" : ""}`} style={{ width, minWidth: width }}>
<span>{label}</span>
<Icon name="expand_more" size={18} />
</button>
);
const MetricCard = ({ label, value, delta, deltaTone = "muted" }) => (
<div className="card metric">
<div className="lbl">{label}</div>
<div className="val">{value}</div>
<div className={`delta ${deltaTone}`}>{delta}</div>
</div>
);
// ---- Role model (3.0): Owner / Global Admin / Member -------------------
// Single shared source of truth so every page + the nav agree. Stored in
// localStorage under "opq-role" as: "owner" | "global-admin" | "member".
const OPQ_ROLE_LABEL = { "owner": "Owner", "global-admin": "Global Admin", "member": "Member" };
const OPQ_PERSONA = {
"owner": { name: "Annemarie Selaya", initials: "AS", role: "Owner" },
"global-admin": { name: "Evan McMillon", initials: "EM", role: "Global Admin" },
"member": { name: "Jordan Bellamy", initials: "JB", role: "Member" },
};
function opqGetRole() {
try {
const r = localStorage.getItem("opq-role");
if (!r) return "member";
if (r === "admin") return "global-admin"; // migrate legacy value
if (r === "builder") return "member"; // migrate legacy value
return r;
} catch (e) { return "member"; }
}
function opqIsAdmin(role) {
const r = role || opqGetRole();
return r === "owner" || r === "global-admin";
}
function opqIsOwner(role) { return (role || opqGetRole()) === "owner"; }
// ---- Workspace mode (3.0 default-workspace model) -------------------------
// Single-workspace mode: the default workspace is invisible. Multi-workspace
// mode: a second workspace exists, so the picker + workspace UI surface.
// Stored in localStorage under "opq-mode" as: "single" | "multi".
// Active workspace stored under "opq-workspace".
const OPQ_WORKSPACES = [
{ initials: "OS", name: "Opaque Systems", isDefault: true },
{ initials: "FI", name: "Finance Department" },
{ initials: "HR", name: "HR Internal" },
{ initials: "SM", name: "Sales & Marketing" },
];
function opqGetMode() {
try { return localStorage.getItem("opq-mode") === "multi" ? "multi" : "single"; }
catch (e) { return "single"; }
}
function opqIsMulti(mode) { return (mode || opqGetMode()) === "multi"; }
function opqGetWorkspace() {
try { return localStorage.getItem("opq-workspace") || "HR Internal"; }
catch (e) { return "HR Internal"; }
}
function opqSetWorkspace(name) {
try { localStorage.setItem("opq-workspace", name); } catch (e) {}
}
// Chip variant for a human-readable role label.
function opqRoleChip(role) {
if (role === "Owner") return "info";
if (role === "Global Admin") return "info-out";
return "neutral";
}
// Role-gated empty surface for pages a Member cannot reach (Registry, Org Settings).
const NoAccess = ({ title, area }) => (
<>
<div className="page-header">
<div className="title-row"><h1>{title}</h1></div>
</div>
<div className="scroll">
<div className="page-body">
<div className="wf-empty">
<div className="wf-empty-icon"><Icon name="lock" size={32} /></div>
<div className="wf-empty-title">You don’t have access to {area}</div>
<div className="wf-empty-body">
{area} is available to Owners and Global Admins. Switch your role from the
Tweaks panel to view it, or ask an administrator for access.
</div>
</div>
</div>
</div>
</>
);
const PageHeader = ({ title, chip, subtitle, actions, tabs, activeTab, onTab }) => (
<div className="page-header">
<div className="title-row">
<div style={{ display: "flex", alignItems: "center", gap: 16 }}>
<h1>{title}</h1>
{chip && (React.isValidElement(chip) ? chip : <Chip variant={chip.variant}>{chip.label}</Chip>)}
{subtitle && <span className="subtitle">{subtitle}</span>}
</div>
{actions && <div className="actions">{actions}</div>}
</div>
{tabs && (
<div className="tabs">
{tabs.map(t => (
<button key={t} className={`tab${activeTab === t ? " active" : ""}`} onClick={() => onTab && onTab(t)}>{t}</button>
))}
</div>
)}
</div>
);
Object.assign(window, { OpaqueLogo, Icon, Button, IconButton, Chip, Status, Field, Select, MetricCard, PageHeader, NoAccess, opqGetRole, opqIsAdmin, opqIsOwner, opqRoleChip, OPQ_ROLE_LABEL, OPQ_PERSONA, OPQ_WORKSPACES, opqGetMode, opqIsMulti, opqGetWorkspace, opqSetWorkspace });