@stratum-hq/react 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/dist/components/AuditLogViewer.d.ts +6 -0
  2. package/dist/components/AuditLogViewer.d.ts.map +1 -0
  3. package/dist/components/AuditLogViewer.js +42 -0
  4. package/dist/components/AuditLogViewer.js.map +1 -0
  5. package/dist/components/ConfigInheritanceVisualizer.d.ts +17 -0
  6. package/dist/components/ConfigInheritanceVisualizer.d.ts.map +1 -0
  7. package/dist/components/ConfigInheritanceVisualizer.js +353 -0
  8. package/dist/components/ConfigInheritanceVisualizer.js.map +1 -0
  9. package/dist/components/DraggableTenantTree.d.ts +24 -0
  10. package/dist/components/DraggableTenantTree.d.ts.map +1 -0
  11. package/dist/components/DraggableTenantTree.js +152 -0
  12. package/dist/components/DraggableTenantTree.js.map +1 -0
  13. package/dist/components/TenantTree.d.ts +4 -1
  14. package/dist/components/TenantTree.d.ts.map +1 -1
  15. package/dist/components/TenantTree.js +4 -4
  16. package/dist/components/TenantTree.js.map +1 -1
  17. package/dist/components/WebhookEditor.d.ts +5 -0
  18. package/dist/components/WebhookEditor.d.ts.map +1 -0
  19. package/dist/components/WebhookEditor.js +60 -0
  20. package/dist/components/WebhookEditor.js.map +1 -0
  21. package/dist/components/headless/ConfigEditor.d.ts +0 -1
  22. package/dist/components/headless/ConfigEditor.d.ts.map +1 -1
  23. package/dist/components/headless/ConfigInheritanceVisualizer.d.ts +17 -0
  24. package/dist/components/headless/ConfigInheritanceVisualizer.d.ts.map +1 -0
  25. package/dist/components/headless/ConfigInheritanceVisualizer.js +7 -0
  26. package/dist/components/headless/ConfigInheritanceVisualizer.js.map +1 -0
  27. package/dist/components/headless/PermissionEditor.d.ts +0 -1
  28. package/dist/components/headless/PermissionEditor.d.ts.map +1 -1
  29. package/dist/components/headless/TenantSwitcher.d.ts +0 -1
  30. package/dist/components/headless/TenantSwitcher.d.ts.map +1 -1
  31. package/dist/components/headless/TenantTree.d.ts +0 -1
  32. package/dist/components/headless/TenantTree.d.ts.map +1 -1
  33. package/dist/hooks/use-audit-logs.d.ts +20 -0
  34. package/dist/hooks/use-audit-logs.d.ts.map +1 -0
  35. package/dist/hooks/use-audit-logs.js +29 -0
  36. package/dist/hooks/use-audit-logs.js.map +1 -0
  37. package/dist/hooks/use-config-cascade.d.ts +39 -0
  38. package/dist/hooks/use-config-cascade.d.ts.map +1 -0
  39. package/dist/hooks/use-config-cascade.js +82 -0
  40. package/dist/hooks/use-config-cascade.js.map +1 -0
  41. package/dist/hooks/use-messages.d.ts.map +1 -1
  42. package/dist/hooks/use-tenant-tree.d.ts.map +1 -1
  43. package/dist/hooks/use-tenant-tree.js +34 -4
  44. package/dist/hooks/use-tenant-tree.js.map +1 -1
  45. package/dist/hooks/use-tenant.d.ts +1 -0
  46. package/dist/hooks/use-tenant.d.ts.map +1 -1
  47. package/dist/hooks/use-webhooks.d.ts +24 -0
  48. package/dist/hooks/use-webhooks.d.ts.map +1 -0
  49. package/dist/hooks/use-webhooks.js +46 -0
  50. package/dist/hooks/use-webhooks.js.map +1 -0
  51. package/dist/index.d.ts +16 -0
  52. package/dist/index.d.ts.map +1 -1
  53. package/dist/index.js +8 -0
  54. package/dist/index.js.map +1 -1
  55. package/package.json +4 -2
@@ -0,0 +1,6 @@
1
+ export interface AuditLogViewerProps {
2
+ limit?: number;
3
+ className?: string;
4
+ }
5
+ export declare function AuditLogViewer({ limit, className }: AuditLogViewerProps): import("react/jsx-runtime").JSX.Element;
6
+ //# sourceMappingURL=AuditLogViewer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AuditLogViewer.d.ts","sourceRoot":"","sources":["../../src/components/AuditLogViewer.tsx"],"names":[],"mappings":"AAIA,MAAM,WAAW,mBAAmB;IAClC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AA4BD,wBAAgB,cAAc,CAAC,EAAE,KAAU,EAAE,SAAS,EAAE,EAAE,mBAAmB,2CAyD5E"}
@@ -0,0 +1,42 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useAuditLogs } from "../hooks/use-audit-logs.js";
3
+ import { TableSkeleton } from "./TableSkeleton.js";
4
+ const ACTION_VARIANTS = {
5
+ "tenant.created": "stratum-badge--own",
6
+ "tenant.updated": "stratum-badge--inherited",
7
+ "tenant.deleted": "stratum-badge--locked",
8
+ "tenant.moved": "stratum-badge--inherited",
9
+ "tenant.reordered": "stratum-badge--inherited",
10
+ "config.updated": "stratum-badge--inherited",
11
+ "config.deleted": "stratum-badge--locked",
12
+ "permission.created": "stratum-badge--own",
13
+ "permission.deleted": "stratum-badge--locked",
14
+ };
15
+ function formatTime(iso) {
16
+ try {
17
+ const d = new Date(iso);
18
+ const now = Date.now();
19
+ const diff = now - d.getTime();
20
+ if (diff < 60_000)
21
+ return "just now";
22
+ if (diff < 3_600_000)
23
+ return `${Math.floor(diff / 60_000)}m ago`;
24
+ if (diff < 86_400_000)
25
+ return `${Math.floor(diff / 3_600_000)}h ago`;
26
+ return d.toLocaleDateString();
27
+ }
28
+ catch {
29
+ return iso;
30
+ }
31
+ }
32
+ export function AuditLogViewer({ limit = 20, className }) {
33
+ const { entries, loading, error } = useAuditLogs(limit);
34
+ if (loading) {
35
+ return (_jsx("div", { className: `stratum-audit-viewer ${className || ""}`, children: _jsx(TableSkeleton, { rows: 5, columns: 4 }) }));
36
+ }
37
+ if (error) {
38
+ return _jsxs("div", { className: className, children: ["Failed to load audit logs: ", error.message] });
39
+ }
40
+ return (_jsx("div", { className: `stratum-audit-viewer ${className || ""}`, children: _jsxs("table", { className: "stratum-audit-viewer__table", children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx("th", { children: "Action" }), _jsx("th", { children: "Resource" }), _jsx("th", { children: "Actor" }), _jsx("th", { children: "Time" })] }) }), _jsxs("tbody", { children: [entries.map((entry) => (_jsxs("tr", { children: [_jsx("td", { children: _jsx("span", { className: `stratum-badge ${ACTION_VARIANTS[entry.action] || ""}`, children: entry.action }) }), _jsx("td", { children: entry.resource_type && (_jsxs("span", { children: [entry.resource_type, entry.resource_id && _jsxs("code", { children: [" ", entry.resource_id.slice(0, 8), "..."] })] })) }), _jsx("td", { children: entry.actor || "system" }), _jsx("td", { children: formatTime(entry.created_at) })] }, entry.id))), entries.length === 0 && (_jsx("tr", { children: _jsx("td", { colSpan: 4, style: { textAlign: "center", padding: "16px" }, children: "No audit entries yet. Actions will appear here as you use the platform." }) }))] })] }) }));
41
+ }
42
+ //# sourceMappingURL=AuditLogViewer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AuditLogViewer.js","sourceRoot":"","sources":["../../src/components/AuditLogViewer.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * ConfigInheritanceVisualizer — shows how config values cascade from
3
+ * parent to children with real-time visual feedback.
4
+ *
5
+ * ┌─────────────────────────┐ ┌─────────────────────────┐
6
+ * │ Parent: AcmeSec │ │ Child: NorthStar MSP │
7
+ * │ ─────────────────────── │ ──▶ │ ─────────────────────── │
8
+ * │ max_users 1000 │ │ max_users 500 own │
9
+ * │ api_rate 10000 🔒 │ │ api_rate 10000 ↑ │
10
+ * │ brand_color #2563EB │ │ brand_color #1E40AF │
11
+ * └─────────────────────────┘ └─────────────────────────┘
12
+ */
13
+ export interface ConfigInheritanceVisualizerProps {
14
+ className?: string;
15
+ }
16
+ export declare function ConfigInheritanceVisualizer({ className }: ConfigInheritanceVisualizerProps): import("react/jsx-runtime").JSX.Element | null;
17
+ //# sourceMappingURL=ConfigInheritanceVisualizer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ConfigInheritanceVisualizer.d.ts","sourceRoot":"","sources":["../../src/components/ConfigInheritanceVisualizer.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAOH,MAAM,WAAW,gCAAgC;IAC/C,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AA2ED,wBAAgB,2BAA2B,CAAC,EAAE,SAAS,EAAE,EAAE,gCAAgC,kDA6H1F"}
@@ -0,0 +1,353 @@
1
+ import { jsxs as _jsxs, jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
2
+ /**
3
+ * ConfigInheritanceVisualizer — shows how config values cascade from
4
+ * parent to children with real-time visual feedback.
5
+ *
6
+ * ┌─────────────────────────┐ ┌─────────────────────────┐
7
+ * │ Parent: AcmeSec │ │ Child: NorthStar MSP │
8
+ * │ ─────────────────────── │ ──▶ │ ─────────────────────── │
9
+ * │ max_users 1000 │ │ max_users 500 own │
10
+ * │ api_rate 10000 🔒 │ │ api_rate 10000 ↑ │
11
+ * │ brand_color #2563EB │ │ brand_color #1E40AF │
12
+ * └─────────────────────────┘ └─────────────────────────┘
13
+ */
14
+ import { useState } from "react";
15
+ import { useConfigCascade } from "../hooks/use-config-cascade.js";
16
+ import { TableSkeleton } from "./TableSkeleton.js";
17
+ function Badge({ type }) {
18
+ const styles = {
19
+ inherited: { label: "Inherited", icon: "\u2191", className: "stratum-cascade-badge--inherited" },
20
+ locked: { label: "Locked", icon: "\u2193", className: "stratum-cascade-badge--locked" },
21
+ own: { label: "Own", icon: "\u2022", className: "stratum-cascade-badge--own" },
22
+ };
23
+ const s = styles[type];
24
+ return (_jsxs("span", { className: `stratum-cascade-badge ${s.className}`, children: [s.icon, " ", s.label] }));
25
+ }
26
+ function getBadgeType(entry) {
27
+ if (entry.locked)
28
+ return "locked";
29
+ if (entry.inherited)
30
+ return "inherited";
31
+ return "own";
32
+ }
33
+ function ConfigTable({ entries, title, subtitle, highlightKey, }) {
34
+ return (_jsxs("div", { className: "stratum-cascade-panel", children: [_jsxs("div", { className: "stratum-cascade-panel__header", children: [_jsx("span", { className: "stratum-cascade-panel__title", children: title }), subtitle && _jsx("span", { className: "stratum-cascade-panel__subtitle", children: subtitle })] }), _jsxs("table", { className: "stratum-cascade-table", children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx("th", { children: "Key" }), _jsx("th", { children: "Value" }), _jsx("th", { children: "Status" })] }) }), _jsxs("tbody", { children: [entries.map((entry) => (_jsxs("tr", { className: highlightKey === entry.key ? "stratum-cascade-row--highlight" : "", children: [_jsx("td", { className: "stratum-cascade-key", children: entry.key }), _jsx("td", { className: "stratum-cascade-value", children: _jsx("code", { children: JSON.stringify(entry.value) }) }), _jsx("td", { children: _jsx(Badge, { type: getBadgeType(entry) }) })] }, entry.key))), entries.length === 0 && (_jsx("tr", { children: _jsx("td", { colSpan: 3, className: "stratum-cascade-empty", children: "No config values" }) }))] })] })] }));
35
+ }
36
+ export function ConfigInheritanceVisualizer({ className }) {
37
+ const { data, loading, error, refresh } = useConfigCascade();
38
+ const [selectedChild, setSelectedChild] = useState(0);
39
+ const [highlightKey, setHighlightKey] = useState(null);
40
+ if (loading) {
41
+ return (_jsx("div", { className: `stratum-cascade ${className || ""}`, children: _jsx(TableSkeleton, { rows: 4, columns: 3 }) }));
42
+ }
43
+ if (error) {
44
+ return (_jsxs("div", { className: `stratum-cascade stratum-cascade--error ${className || ""}`, children: [_jsxs("p", { children: ["Failed to load inheritance data: ", error.message] }), _jsx("button", { type: "button", onClick: refresh, className: "stratum-cascade-retry", children: "Retry" })] }));
45
+ }
46
+ if (!data)
47
+ return null;
48
+ const activeChild = data.children[selectedChild];
49
+ return (_jsxs("div", { className: `stratum-cascade ${className || ""}`, children: [_jsx("style", { children: cascadeStyles }), data.children.length > 1 && (_jsx("div", { className: "stratum-cascade-tabs", role: "tablist", children: data.children.map((child, i) => (_jsx("button", { role: "tab", "aria-selected": i === selectedChild, className: `stratum-cascade-tab${i === selectedChild ? " active" : ""}`, onClick: () => { setSelectedChild(i); setHighlightKey(null); }, children: child.name }, child.id))) })), _jsxs("div", { className: "stratum-cascade-split", children: [_jsx(ConfigTable, { entries: data.parent.config, title: data.parent.name, subtitle: "Parent", highlightKey: highlightKey }), _jsxs("div", { className: "stratum-cascade-arrow", "aria-hidden": "true", children: [_jsx("div", { className: "stratum-cascade-arrow__line" }), _jsx("div", { className: "stratum-cascade-arrow__head", children: "\u25B6" })] }), activeChild ? (_jsx(ConfigTable, { entries: activeChild.config, title: activeChild.name, subtitle: "Child", highlightKey: highlightKey })) : (_jsxs("div", { className: "stratum-cascade-panel stratum-cascade-panel--empty", children: [_jsx("div", { className: "stratum-cascade-panel__header", children: _jsx("span", { className: "stratum-cascade-panel__title", children: "No children" }) }), _jsx("p", { className: "stratum-cascade-empty-message", children: "This tenant has no child tenants. Create a child tenant to see config inheritance in action." })] }))] }), activeChild && (_jsxs("div", { className: "stratum-cascade-diff", children: [_jsx("span", { className: "stratum-cascade-diff__label", children: "Inheritance summary:" }), (() => {
50
+ const inherited = activeChild.config.filter((e) => e.inherited && !e.locked).length;
51
+ const locked = activeChild.config.filter((e) => e.locked).length;
52
+ const own = activeChild.config.filter((e) => !e.inherited && !e.locked).length;
53
+ return (_jsxs(_Fragment, { children: [inherited > 0 && (_jsxs("span", { className: "stratum-cascade-diff__stat stratum-cascade-diff__stat--inherited", onMouseEnter: () => {
54
+ const firstInherited = activeChild.config.find((e) => e.inherited && !e.locked);
55
+ if (firstInherited)
56
+ setHighlightKey(firstInherited.key);
57
+ }, onMouseLeave: () => setHighlightKey(null), children: [inherited, " inherited"] })), locked > 0 && (_jsxs("span", { className: "stratum-cascade-diff__stat stratum-cascade-diff__stat--locked", children: [locked, " locked"] })), own > 0 && (_jsxs("span", { className: "stratum-cascade-diff__stat stratum-cascade-diff__stat--own", children: [own, " overridden"] }))] }));
58
+ })()] }))] }));
59
+ }
60
+ // ── Scoped styles ─────────────────────────────────────────────
61
+ const cascadeStyles = `
62
+ .stratum-cascade {
63
+ font-family: var(--font-body, 'DM Sans', system-ui, sans-serif);
64
+ }
65
+
66
+ .stratum-cascade--error {
67
+ padding: var(--space-xl, 24px);
68
+ color: var(--color-error, #DC2626);
69
+ font-size: 0.875rem;
70
+ }
71
+
72
+ .stratum-cascade-retry {
73
+ margin-top: var(--space-sm, 8px);
74
+ padding: var(--space-xs, 4px) var(--space-md, 12px);
75
+ border: 1px solid var(--border, #E2E8F0);
76
+ border-radius: var(--radius-sm, 4px);
77
+ background: var(--bg-card, white);
78
+ color: var(--text-secondary, #475569);
79
+ cursor: pointer;
80
+ font-size: 0.75rem;
81
+ }
82
+
83
+ /* Child selector tabs */
84
+ .stratum-cascade-tabs {
85
+ display: flex;
86
+ gap: 0;
87
+ border-bottom: 1px solid var(--border, #E2E8F0);
88
+ margin-bottom: var(--space-lg, 16px);
89
+ overflow-x: auto;
90
+ }
91
+
92
+ .stratum-cascade-tab {
93
+ padding: var(--space-sm, 8px) var(--space-lg, 16px);
94
+ font-size: 0.75rem;
95
+ font-weight: 500;
96
+ color: var(--text-tertiary, #64748B);
97
+ background: transparent;
98
+ border: none;
99
+ border-bottom: 2px solid transparent;
100
+ cursor: pointer;
101
+ font-family: var(--font-body, 'DM Sans', system-ui, sans-serif);
102
+ white-space: nowrap;
103
+ }
104
+
105
+ .stratum-cascade-tab:hover {
106
+ color: var(--text-primary, #0F172A);
107
+ }
108
+
109
+ .stratum-cascade-tab.active {
110
+ color: var(--color-accent, #0D9488);
111
+ border-bottom-color: var(--color-accent, #0D9488);
112
+ }
113
+
114
+ /* Split-screen layout */
115
+ .stratum-cascade-split {
116
+ display: flex;
117
+ gap: var(--space-md, 12px);
118
+ align-items: flex-start;
119
+ }
120
+
121
+ /* Cascade arrow */
122
+ .stratum-cascade-arrow {
123
+ display: flex;
124
+ flex-direction: column;
125
+ align-items: center;
126
+ justify-content: center;
127
+ padding: var(--space-3xl, 48px) 0;
128
+ flex-shrink: 0;
129
+ color: var(--color-accent, #0D9488);
130
+ opacity: 0.6;
131
+ }
132
+
133
+ .stratum-cascade-arrow__line {
134
+ width: 2px;
135
+ height: 24px;
136
+ background: var(--color-accent, #0D9488);
137
+ opacity: 0.4;
138
+ }
139
+
140
+ .stratum-cascade-arrow__head {
141
+ font-size: 0.75rem;
142
+ transform: rotate(0deg);
143
+ }
144
+
145
+ /* Panel (each side of the split) */
146
+ .stratum-cascade-panel {
147
+ flex: 1;
148
+ min-width: 0;
149
+ border: 1px solid var(--border, #E2E8F0);
150
+ border-radius: var(--radius-md, 6px);
151
+ overflow: hidden;
152
+ }
153
+
154
+ .stratum-cascade-panel--empty {
155
+ display: flex;
156
+ flex-direction: column;
157
+ align-items: center;
158
+ justify-content: center;
159
+ min-height: 200px;
160
+ }
161
+
162
+ .stratum-cascade-panel__header {
163
+ padding: var(--space-sm, 8px) var(--space-md, 12px);
164
+ border-bottom: 1px solid var(--border, #E2E8F0);
165
+ background: var(--bg-card, white);
166
+ display: flex;
167
+ align-items: baseline;
168
+ gap: var(--space-sm, 8px);
169
+ }
170
+
171
+ .stratum-cascade-panel__title {
172
+ font-size: 0.8125rem;
173
+ font-weight: 700;
174
+ color: var(--text-primary, #0F172A);
175
+ font-family: var(--font-display, 'Satoshi', sans-serif);
176
+ }
177
+
178
+ .stratum-cascade-panel__subtitle {
179
+ font-size: 0.6875rem;
180
+ color: var(--text-tertiary, #64748B);
181
+ text-transform: uppercase;
182
+ letter-spacing: 0.05em;
183
+ }
184
+
185
+ .stratum-cascade-empty-message {
186
+ padding: var(--space-xl, 24px);
187
+ text-align: center;
188
+ color: var(--text-tertiary, #64748B);
189
+ font-size: 0.8125rem;
190
+ }
191
+
192
+ /* Config table within panel */
193
+ .stratum-cascade-table {
194
+ width: 100%;
195
+ border-collapse: collapse;
196
+ font-size: 0.75rem;
197
+ }
198
+
199
+ .stratum-cascade-table th {
200
+ padding: var(--space-xs, 4px) var(--space-md, 12px);
201
+ text-align: left;
202
+ font-weight: 600;
203
+ font-size: 0.625rem;
204
+ text-transform: uppercase;
205
+ letter-spacing: 0.06em;
206
+ color: var(--text-tertiary, #64748B);
207
+ border-bottom: 1px solid var(--border, #E2E8F0);
208
+ font-family: var(--font-mono, 'JetBrains Mono', monospace);
209
+ }
210
+
211
+ .stratum-cascade-table td {
212
+ padding: var(--space-xs, 4px) var(--space-md, 12px);
213
+ border-bottom: 1px solid var(--border, #E2E8F0);
214
+ color: var(--text-primary, #0F172A);
215
+ }
216
+
217
+ .stratum-cascade-key {
218
+ font-family: var(--font-mono, 'JetBrains Mono', monospace);
219
+ font-weight: 500;
220
+ }
221
+
222
+ .stratum-cascade-value code {
223
+ font-family: var(--font-mono, 'JetBrains Mono', monospace);
224
+ font-size: 0.6875rem;
225
+ color: var(--text-secondary, #475569);
226
+ }
227
+
228
+ .stratum-cascade-empty {
229
+ text-align: center;
230
+ color: var(--text-tertiary, #64748B);
231
+ padding: var(--space-lg, 16px) !important;
232
+ font-style: italic;
233
+ }
234
+
235
+ /* Highlight row on hover from diff summary */
236
+ .stratum-cascade-row--highlight {
237
+ background: rgba(13, 148, 136, 0.08);
238
+ }
239
+
240
+ /* Badges */
241
+ .stratum-cascade-badge {
242
+ display: inline-flex;
243
+ align-items: center;
244
+ gap: 2px;
245
+ font-size: 0.625rem;
246
+ font-weight: 600;
247
+ padding: 1px 6px;
248
+ border-radius: 9999px;
249
+ white-space: nowrap;
250
+ }
251
+
252
+ .stratum-cascade-badge--inherited {
253
+ color: var(--color-accent, #0D9488);
254
+ background: var(--color-accent-light, #CCFBF1);
255
+ }
256
+
257
+ .stratum-cascade-badge--locked {
258
+ color: var(--color-warning, #D97706);
259
+ background: var(--color-warning-bg, #FEF3C7);
260
+ }
261
+
262
+ .stratum-cascade-badge--own {
263
+ color: var(--text-secondary, #475569);
264
+ background: var(--bg-input, var(--color-100, #F1F5F9));
265
+ border: 1px solid var(--border, #E2E8F0);
266
+ }
267
+
268
+ /* Dark mode overrides */
269
+ [data-theme="dark"] .stratum-cascade-badge--inherited {
270
+ background: rgba(13, 148, 136, 0.15);
271
+ }
272
+
273
+ [data-theme="dark"] .stratum-cascade-badge--locked {
274
+ background: rgba(217, 119, 6, 0.15);
275
+ }
276
+
277
+ /* Diff summary bar */
278
+ .stratum-cascade-diff {
279
+ margin-top: var(--space-md, 12px);
280
+ padding: var(--space-sm, 8px) var(--space-md, 12px);
281
+ border: 1px solid var(--border, #E2E8F0);
282
+ border-radius: var(--radius-sm, 4px);
283
+ display: flex;
284
+ align-items: center;
285
+ gap: var(--space-md, 12px);
286
+ font-size: 0.75rem;
287
+ background: var(--bg-card, white);
288
+ }
289
+
290
+ .stratum-cascade-diff__label {
291
+ color: var(--text-tertiary, #64748B);
292
+ font-weight: 500;
293
+ }
294
+
295
+ .stratum-cascade-diff__stat {
296
+ padding: 2px 8px;
297
+ border-radius: 9999px;
298
+ font-weight: 600;
299
+ font-size: 0.6875rem;
300
+ cursor: default;
301
+ }
302
+
303
+ .stratum-cascade-diff__stat--inherited {
304
+ color: var(--color-accent, #0D9488);
305
+ background: var(--color-accent-light, #CCFBF1);
306
+ }
307
+
308
+ .stratum-cascade-diff__stat--locked {
309
+ color: var(--color-warning, #D97706);
310
+ background: var(--color-warning-bg, #FEF3C7);
311
+ }
312
+
313
+ .stratum-cascade-diff__stat--own {
314
+ color: var(--text-secondary, #475569);
315
+ background: var(--bg-input, var(--color-100, #F1F5F9));
316
+ }
317
+
318
+ [data-theme="dark"] .stratum-cascade-diff__stat--inherited {
319
+ background: rgba(13, 148, 136, 0.15);
320
+ }
321
+
322
+ [data-theme="dark"] .stratum-cascade-diff__stat--locked {
323
+ background: rgba(217, 119, 6, 0.15);
324
+ }
325
+
326
+ /* Responsive: stack on narrow screens */
327
+ @media (max-width: 768px) {
328
+ .stratum-cascade-split {
329
+ flex-direction: column;
330
+ }
331
+
332
+ .stratum-cascade-arrow {
333
+ flex-direction: row;
334
+ padding: 0 var(--space-xl, 24px);
335
+ }
336
+
337
+ .stratum-cascade-arrow__line {
338
+ width: 24px;
339
+ height: 2px;
340
+ }
341
+
342
+ .stratum-cascade-arrow__head {
343
+ transform: rotate(90deg);
344
+ }
345
+ }
346
+
347
+ @media (prefers-reduced-motion: reduce) {
348
+ .stratum-cascade-row--highlight {
349
+ transition: none;
350
+ }
351
+ }
352
+ `;
353
+ //# sourceMappingURL=ConfigInheritanceVisualizer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ConfigInheritanceVisualizer.js","sourceRoot":"","sources":["../../src/components/ConfigInheritanceVisualizer.tsx"],"names":[],"mappings":";AAAA;;;;;;;;;;;GAWG;AAEH,OAAc,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACxC,OAAO,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AAElE,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAMnD,SAAS,KAAK,CAAC,EAAE,IAAI,EAA4C;IAC/D,MAAM,MAAM,GAAuE;QACjF,SAAS,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,kCAAkC,EAAE;QAChG,MAAM,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,+BAA+B,EAAE;QACvF,GAAG,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,4BAA4B,EAAE;KAC/E,CAAC;IACF,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;IACvB,OAAO,CACL,gBAAM,SAAS,EAAE,yBAAyB,CAAC,CAAC,SAAS,EAAE,aACpD,CAAC,CAAC,IAAI,OAAG,CAAC,CAAC,KAAK,IACZ,CACR,CAAC;AACJ,CAAC;AAED,SAAS,YAAY,CAAC,KAAyB;IAC7C,IAAI,KAAK,CAAC,MAAM;QAAE,OAAO,QAAQ,CAAC;IAClC,IAAI,KAAK,CAAC,SAAS;QAAE,OAAO,WAAW,CAAC;IACxC,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,WAAW,CAAC,EACnB,OAAO,EACP,KAAK,EACL,QAAQ,EACR,YAAY,GAMb;IACC,OAAO,CACL,eAAK,SAAS,EAAC,uBAAuB,aACpC,eAAK,SAAS,EAAC,+BAA+B,aAC5C,eAAM,SAAS,EAAC,8BAA8B,YAAE,KAAK,GAAQ,EAC5D,QAAQ,IAAI,eAAM,SAAS,EAAC,iCAAiC,YAAE,QAAQ,GAAQ,IAC5E,EACN,iBAAO,SAAS,EAAC,uBAAuB,aACtC,0BACE,yBACE,+BAAY,EACZ,iCAAc,EACd,kCAAe,IACZ,GACC,EACR,4BACG,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CACtB,cAEE,SAAS,EAAE,YAAY,KAAK,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,gCAAgC,CAAC,CAAC,CAAC,EAAE,aAE7E,aAAI,SAAS,EAAC,qBAAqB,YAAE,KAAK,CAAC,GAAG,GAAM,EACpD,aAAI,SAAS,EAAC,uBAAuB,YACnC,yBAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,KAAK,CAAC,GAAQ,GACvC,EACL,uBACE,KAAC,KAAK,IAAC,IAAI,EAAE,YAAY,CAAC,KAAK,CAAC,GAAI,GACjC,KATA,KAAK,CAAC,GAAG,CAUX,CACN,CAAC,EACD,OAAO,CAAC,MAAM,KAAK,CAAC,IAAI,CACvB,uBACE,aAAI,OAAO,EAAE,CAAC,EAAE,SAAS,EAAC,uBAAuB,iCAE5C,GACF,CACN,IACK,IACF,IACJ,CACP,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,2BAA2B,CAAC,EAAE,SAAS,EAAoC;IACzF,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,gBAAgB,EAAE,CAAC;IAC7D,MAAM,CAAC,aAAa,EAAE,gBAAgB,CAAC,GAAG,QAAQ,CAAS,CAAC,CAAC,CAAC;IAC9D,MAAM,CAAC,YAAY,EAAE,eAAe,CAAC,GAAG,QAAQ,CAAgB,IAAI,CAAC,CAAC;IAEtE,IAAI,OAAO,EAAE,CAAC;QACZ,OAAO,CACL,cAAK,SAAS,EAAE,mBAAmB,SAAS,IAAI,EAAE,EAAE,YAClD,KAAC,aAAa,IAAC,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,GAAI,GAClC,CACP,CAAC;IACJ,CAAC;IAED,IAAI,KAAK,EAAE,CAAC;QACV,OAAO,CACL,eAAK,SAAS,EAAE,0CAA0C,SAAS,IAAI,EAAE,EAAE,aACzE,6DAAqC,KAAK,CAAC,OAAO,IAAK,EACvD,iBAAQ,IAAI,EAAC,QAAQ,EAAC,OAAO,EAAE,OAAO,EAAE,SAAS,EAAC,uBAAuB,sBAEhE,IACL,CACP,CAAC;IACJ,CAAC;IAED,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC;IAEvB,MAAM,WAAW,GAA6B,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC;IAE3E,OAAO,CACL,eAAK,SAAS,EAAE,mBAAmB,SAAS,IAAI,EAAE,EAAE,aAClD,0BAAQ,aAAa,GAAS,EAG7B,IAAI,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,IAAI,CAC3B,cAAK,SAAS,EAAC,sBAAsB,EAAC,IAAI,EAAC,SAAS,YACjD,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC,CAC/B,iBAEE,IAAI,EAAC,KAAK,mBACK,CAAC,KAAK,aAAa,EAClC,SAAS,EAAE,sBAAsB,CAAC,KAAK,aAAa,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,EACvE,OAAO,EAAE,GAAG,EAAE,GAAG,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,YAE7D,KAAK,CAAC,IAAI,IANN,KAAK,CAAC,EAAE,CAON,CACV,CAAC,GACE,CACP,EAGD,eAAK,SAAS,EAAC,uBAAuB,aACpC,KAAC,WAAW,IACV,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,EAC3B,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI,EACvB,QAAQ,EAAC,QAAQ,EACjB,YAAY,EAAE,YAAY,GAC1B,EAGF,eAAK,SAAS,EAAC,uBAAuB,iBAAa,MAAM,aACvD,cAAK,SAAS,EAAC,6BAA6B,GAAG,EAC/C,cAAK,SAAS,EAAC,6BAA6B,YAAE,QAAQ,GAAO,IACzD,EAEL,WAAW,CAAC,CAAC,CAAC,CACb,KAAC,WAAW,IACV,OAAO,EAAE,WAAW,CAAC,MAAM,EAC3B,KAAK,EAAE,WAAW,CAAC,IAAI,EACvB,QAAQ,EAAC,OAAO,EAChB,YAAY,EAAE,YAAY,GAC1B,CACH,CAAC,CAAC,CAAC,CACF,eAAK,SAAS,EAAC,oDAAoD,aACjE,cAAK,SAAS,EAAC,+BAA+B,YAC5C,eAAM,SAAS,EAAC,8BAA8B,4BAAmB,GAC7D,EACN,YAAG,SAAS,EAAC,+BAA+B,6GAGxC,IACA,CACP,IACG,EAGL,WAAW,IAAI,CACd,eAAK,SAAS,EAAC,sBAAsB,aACnC,eAAM,SAAS,EAAC,6BAA6B,qCAA4B,EACxE,CAAC,GAAG,EAAE;wBACL,MAAM,SAAS,GAAG,WAAW,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC;wBACpF,MAAM,MAAM,GAAG,WAAW,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC;wBACjE,MAAM,GAAG,GAAG,WAAW,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC;wBAC/E,OAAO,CACL,8BACG,SAAS,GAAG,CAAC,IAAI,CAChB,gBACE,SAAS,EAAC,kEAAkE,EAC5E,YAAY,EAAE,GAAG,EAAE;wCACjB,MAAM,cAAc,GAAG,WAAW,CAAC,MAAM,CAAC,IAAI,CAC5C,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,IAAI,CAAC,CAAC,CAAC,MAAM,CAChC,CAAC;wCACF,IAAI,cAAc;4CAAE,eAAe,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC;oCAC1D,CAAC,EACD,YAAY,EAAE,GAAG,EAAE,CAAC,eAAe,CAAC,IAAI,CAAC,aAExC,SAAS,kBACL,CACR,EACA,MAAM,GAAG,CAAC,IAAI,CACb,gBAAM,SAAS,EAAC,+DAA+D,aAC5E,MAAM,eACF,CACR,EACA,GAAG,GAAG,CAAC,IAAI,CACV,gBAAM,SAAS,EAAC,4DAA4D,aACzE,GAAG,mBACC,CACR,IACA,CACJ,CAAC;oBACJ,CAAC,CAAC,EAAE,IACA,CACP,IACG,CACP,CAAC;AACJ,CAAC;AAED,iEAAiE;AAEjE,MAAM,aAAa,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmSrB,CAAC"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * DraggableTenantTree — TenantTree with drag-and-drop reparenting.
3
+ *
4
+ * Drag a tenant node onto another to move it as a child of the drop target.
5
+ * Uses @dnd-kit/core for drag interactions. Calls the Stratum moveTenant API
6
+ * on drop. Shows visual drop indicators during drag.
7
+ *
8
+ * ┌──────────────────────────────────┐
9
+ * │ AcmeSec │
10
+ * │ ├── NorthStar MSP │
11
+ * │ │ ├── Client Alpha ← drag │
12
+ * │ │ └── Client Beta │
13
+ * │ └── SouthShield MSP ← drop │ ← "Move Client Alpha under SouthShield?"
14
+ * │ └── Client Gamma │
15
+ * └──────────────────────────────────┘
16
+ */
17
+ export interface DraggableTenantTreeProps {
18
+ rootId?: string;
19
+ onSelect?: (tenantId: string) => void;
20
+ onMove?: (tenantId: string, newParentId: string) => void;
21
+ className?: string;
22
+ }
23
+ export declare function DraggableTenantTree({ rootId, onSelect, onMove, className, }: DraggableTenantTreeProps): import("react/jsx-runtime").JSX.Element;
24
+ //# sourceMappingURL=DraggableTenantTree.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DraggableTenantTree.d.ts","sourceRoot":"","sources":["../../src/components/DraggableTenantTree.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAkBH,MAAM,WAAW,wBAAwB;IACvC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IACtC,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;IACzD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAyGD,wBAAgB,mBAAmB,CAAC,EAClC,MAAM,EACN,QAAQ,EACR,MAAM,EACN,SAAS,GACV,EAAE,wBAAwB,2CAoG1B"}
@@ -0,0 +1,152 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * DraggableTenantTree — TenantTree with drag-and-drop reparenting.
4
+ *
5
+ * Drag a tenant node onto another to move it as a child of the drop target.
6
+ * Uses @dnd-kit/core for drag interactions. Calls the Stratum moveTenant API
7
+ * on drop. Shows visual drop indicators during drag.
8
+ *
9
+ * ┌──────────────────────────────────┐
10
+ * │ AcmeSec │
11
+ * │ ├── NorthStar MSP │
12
+ * │ │ ├── Client Alpha ← drag │
13
+ * │ │ └── Client Beta │
14
+ * │ └── SouthShield MSP ← drop │ ← "Move Client Alpha under SouthShield?"
15
+ * │ └── Client Gamma │
16
+ * └──────────────────────────────────┘
17
+ */
18
+ import { useState, useCallback } from "react";
19
+ import { DndContext, DragOverlay, useDraggable, useDroppable, closestCenter, } from "@dnd-kit/core";
20
+ import { useTenantTree } from "../hooks/use-tenant-tree.js";
21
+ import { useTenant } from "../hooks/use-tenant.js";
22
+ import { useStratum } from "../provider.js";
23
+ import { useMessages } from "../hooks/use-messages.js";
24
+ // ── Draggable + Droppable node ──────────────────────────────
25
+ function DraggableTreeNode({ node, selectedId, onSelect, onToggle, depth, t, }) {
26
+ const hasChildren = node.children.length > 0;
27
+ const { attributes, listeners, setNodeRef: setDragRef, isDragging } = useDraggable({
28
+ id: node.id,
29
+ data: { node },
30
+ });
31
+ const { setNodeRef: setDropRef, isOver } = useDroppable({
32
+ id: `drop-${node.id}`,
33
+ data: { node },
34
+ });
35
+ return (_jsxs("li", { role: "treeitem", "aria-expanded": hasChildren ? node.expanded : undefined, children: [_jsxs("div", { ref: (el) => { setDragRef(el); setDropRef(el); }, className: [
36
+ "stratum-tree__node",
37
+ selectedId === node.id ? "stratum-tree__node--selected" : "",
38
+ isDragging ? "stratum-tree__node--dragging" : "",
39
+ isOver ? "stratum-tree__node--drop-target" : "",
40
+ ].filter(Boolean).join(" "), style: {
41
+ paddingInlineStart: `calc(${depth} * var(--space-xl) + var(--space-sm))`,
42
+ opacity: isDragging ? 0.4 : 1,
43
+ }, ...attributes, ...listeners, children: [hasChildren ? (_jsx("button", { type: "button", className: "stratum-tree__toggle", onClick: (e) => { e.stopPropagation(); onToggle(node.id); }, "aria-label": node.expanded ? t("tenantTree.collapse") : t("tenantTree.expand"), children: node.expanded ? "\u25BC" : "\u25B6" })) : (_jsx("span", { className: "stratum-tree__spacer", children: " " })), _jsx("span", { className: "stratum-tree__label", onClick: () => onSelect?.(node.id), onKeyDown: (e) => e.key === "Enter" && onSelect?.(node.id), tabIndex: 0, role: "button", children: node.name }), _jsx("span", { className: "stratum-tree__badge", children: t("tenantTree.badgeRls") }), _jsx("span", { className: "stratum-tree__meta", children: node.status === "archived" ? t("tenantTree.archived") : "" })] }), hasChildren && node.expanded && (_jsx("ul", { role: "group", children: node.children.map((child) => (_jsx(DraggableTreeNode, { node: child, selectedId: selectedId, onSelect: onSelect, onToggle: onToggle, depth: depth + 1, t: t }, child.id))) }))] }));
44
+ }
45
+ // ── Drag overlay (ghost element shown while dragging) ────────
46
+ function DragOverlayContent({ node }) {
47
+ return (_jsx("div", { className: "stratum-tree__drag-overlay", children: node.name }));
48
+ }
49
+ // ── Main component ──────────────────────────────────────────
50
+ export function DraggableTenantTree({ rootId, onSelect, onMove, className, }) {
51
+ const { tree, loading, error, toggleExpand, refresh } = useTenantTree(rootId);
52
+ const { tenant } = useTenant();
53
+ const { apiCall, toast } = useStratum();
54
+ const { t } = useMessages();
55
+ const [activeNode, setActiveNode] = useState(null);
56
+ // Find a node by ID in the tree
57
+ const findNode = useCallback((id, nodes = tree) => {
58
+ for (const node of nodes) {
59
+ if (node.id === id)
60
+ return node;
61
+ const found = findNode(id, node.children);
62
+ if (found)
63
+ return found;
64
+ }
65
+ return null;
66
+ }, [tree]);
67
+ const handleDragStart = (event) => {
68
+ const node = event.active.data.current?.node;
69
+ setActiveNode(node ?? null);
70
+ };
71
+ const handleDragEnd = async (event) => {
72
+ setActiveNode(null);
73
+ const { active, over } = event;
74
+ if (!over)
75
+ return;
76
+ const draggedId = active.id;
77
+ // Drop target IDs are prefixed with "drop-"
78
+ const targetId = over.id.replace(/^drop-/, "");
79
+ // Don't drop on self or current parent
80
+ if (draggedId === targetId)
81
+ return;
82
+ const draggedNode = findNode(draggedId);
83
+ if (!draggedNode)
84
+ return;
85
+ // Check: don't drop a parent onto its own descendant (cycle)
86
+ const isDescendant = (parentNode, childId) => {
87
+ for (const child of parentNode.children) {
88
+ if (child.id === childId)
89
+ return true;
90
+ if (isDescendant(child, childId))
91
+ return true;
92
+ }
93
+ return false;
94
+ };
95
+ if (isDescendant(draggedNode, targetId)) {
96
+ toast.error("Cannot move a tenant under its own descendant");
97
+ return;
98
+ }
99
+ try {
100
+ await apiCall(`/api/v1/tenants/${draggedId}/move`, {
101
+ method: "POST",
102
+ headers: { "Content-Type": "application/json" },
103
+ body: JSON.stringify({ new_parent_id: targetId }),
104
+ });
105
+ toast.success(`Moved "${draggedNode.name}" successfully`);
106
+ onMove?.(draggedId, targetId);
107
+ await refresh();
108
+ }
109
+ catch (err) {
110
+ toast.error(`Failed to move tenant: ${err instanceof Error ? err.message : String(err)}`);
111
+ }
112
+ };
113
+ if (loading)
114
+ return _jsx("div", { className: className, children: t("tenantTree.loading") });
115
+ if (error)
116
+ return _jsx("div", { className: className, children: t("tenantTree.error", { message: error.message }) });
117
+ return (_jsxs("div", { className: `stratum-tree stratum-tree--draggable ${className || ""}`, children: [_jsx("style", { children: draggableStyles }), _jsxs(DndContext, { collisionDetection: closestCenter, onDragStart: handleDragStart, onDragEnd: handleDragEnd, children: [_jsx("ul", { role: "tree", className: "stratum-tree__root", children: tree.map((node) => (_jsx(DraggableTreeNode, { node: node, selectedId: tenant?.id, onSelect: onSelect, onToggle: toggleExpand, depth: 0, t: t }, node.id))) }), _jsx(DragOverlay, { children: activeNode ? _jsx(DragOverlayContent, { node: activeNode }) : null })] })] }));
118
+ }
119
+ // ── Scoped styles ───────────────────────────────────────────
120
+ const draggableStyles = `
121
+ .stratum-tree__node--dragging {
122
+ opacity: 0.4;
123
+ }
124
+
125
+ .stratum-tree__node--drop-target {
126
+ outline: 2px dashed var(--color-accent, #0D9488);
127
+ outline-offset: -2px;
128
+ border-radius: var(--radius-sm, 4px);
129
+ background: rgba(13, 148, 136, 0.08);
130
+ }
131
+
132
+ .stratum-tree__drag-overlay {
133
+ padding: var(--space-xs, 4px) var(--space-md, 12px);
134
+ background: var(--bg-card, white);
135
+ border: 1px solid var(--color-accent, #0D9488);
136
+ border-radius: var(--radius-sm, 4px);
137
+ box-shadow: var(--shadow-md);
138
+ font-size: 0.8125rem;
139
+ font-family: var(--font-body, 'DM Sans', system-ui, sans-serif);
140
+ color: var(--text-primary, #0F172A);
141
+ cursor: grabbing;
142
+ }
143
+
144
+ .stratum-tree--draggable .stratum-tree__node {
145
+ cursor: grab;
146
+ }
147
+
148
+ .stratum-tree--draggable .stratum-tree__node:active {
149
+ cursor: grabbing;
150
+ }
151
+ `;
152
+ //# sourceMappingURL=DraggableTenantTree.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DraggableTenantTree.js","sourceRoot":"","sources":["../../src/components/DraggableTenantTree.tsx"],"names":[],"mappings":"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"}
@@ -1,7 +1,10 @@
1
1
  export interface TenantTreeProps {
2
2
  rootId?: string;
3
3
  onSelect?: (tenantId: string) => void;
4
+ onEdit?: (tenantId: string, currentName: string) => void;
5
+ onArchive?: (tenantId: string, name: string) => void;
6
+ onAddChild?: (parentId: string) => void;
4
7
  className?: string;
5
8
  }
6
- export declare function TenantTree({ rootId, onSelect, className }: TenantTreeProps): import("react/jsx-runtime").JSX.Element;
9
+ export declare function TenantTree({ rootId, onSelect, onEdit, onArchive, onAddChild, className }: TenantTreeProps): import("react/jsx-runtime").JSX.Element;
7
10
  //# sourceMappingURL=TenantTree.d.ts.map