@stratum-hq/react 0.1.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 (113) hide show
  1. package/dist/components/ConfigEditor.d.ts +5 -0
  2. package/dist/components/ConfigEditor.d.ts.map +1 -0
  3. package/dist/components/ConfigEditor.js +71 -0
  4. package/dist/components/ConfigEditor.js.map +1 -0
  5. package/dist/components/ConfigEditor.stories.d.ts +35 -0
  6. package/dist/components/ConfigEditor.stories.d.ts.map +1 -0
  7. package/dist/components/ConfigEditor.stories.js +73 -0
  8. package/dist/components/ConfigEditor.stories.js.map +1 -0
  9. package/dist/components/PermissionEditor.d.ts +5 -0
  10. package/dist/components/PermissionEditor.d.ts.map +1 -0
  11. package/dist/components/PermissionEditor.js +46 -0
  12. package/dist/components/PermissionEditor.js.map +1 -0
  13. package/dist/components/PermissionEditor.stories.d.ts +34 -0
  14. package/dist/components/PermissionEditor.stories.d.ts.map +1 -0
  15. package/dist/components/PermissionEditor.stories.js +90 -0
  16. package/dist/components/PermissionEditor.stories.js.map +1 -0
  17. package/dist/components/Skeleton.d.ts +14 -0
  18. package/dist/components/Skeleton.d.ts.map +1 -0
  19. package/dist/components/Skeleton.js +25 -0
  20. package/dist/components/Skeleton.js.map +1 -0
  21. package/dist/components/Skeleton.stories.d.ts +46 -0
  22. package/dist/components/Skeleton.stories.d.ts.map +1 -0
  23. package/dist/components/Skeleton.stories.js +125 -0
  24. package/dist/components/Skeleton.stories.js.map +1 -0
  25. package/dist/components/TableSkeleton.d.ts +10 -0
  26. package/dist/components/TableSkeleton.d.ts.map +1 -0
  27. package/dist/components/TableSkeleton.js +6 -0
  28. package/dist/components/TableSkeleton.js.map +1 -0
  29. package/dist/components/TenantSwitcher.d.ts +7 -0
  30. package/dist/components/TenantSwitcher.d.ts.map +1 -0
  31. package/dist/components/TenantSwitcher.js +40 -0
  32. package/dist/components/TenantSwitcher.js.map +1 -0
  33. package/dist/components/TenantSwitcher.stories.d.ts +30 -0
  34. package/dist/components/TenantSwitcher.stories.d.ts.map +1 -0
  35. package/dist/components/TenantSwitcher.stories.js +76 -0
  36. package/dist/components/TenantSwitcher.stories.js.map +1 -0
  37. package/dist/components/TenantTree.d.ts +7 -0
  38. package/dist/components/TenantTree.d.ts.map +1 -0
  39. package/dist/components/TenantTree.js +19 -0
  40. package/dist/components/TenantTree.js.map +1 -0
  41. package/dist/components/TenantTree.stories.d.ts +28 -0
  42. package/dist/components/TenantTree.stories.d.ts.map +1 -0
  43. package/dist/components/TenantTree.stories.js +71 -0
  44. package/dist/components/TenantTree.stories.js.map +1 -0
  45. package/dist/components/Toast.d.ts +18 -0
  46. package/dist/components/Toast.d.ts.map +1 -0
  47. package/dist/components/Toast.js +22 -0
  48. package/dist/components/Toast.js.map +1 -0
  49. package/dist/components/Toast.stories.d.ts +36 -0
  50. package/dist/components/Toast.stories.d.ts.map +1 -0
  51. package/dist/components/Toast.stories.js +105 -0
  52. package/dist/components/Toast.stories.js.map +1 -0
  53. package/dist/components/ToastContainer.d.ts +11 -0
  54. package/dist/components/ToastContainer.d.ts.map +1 -0
  55. package/dist/components/ToastContainer.js +9 -0
  56. package/dist/components/ToastContainer.js.map +1 -0
  57. package/dist/components/headless/ConfigEditor.d.ts +15 -0
  58. package/dist/components/headless/ConfigEditor.d.ts.map +1 -0
  59. package/dist/components/headless/ConfigEditor.js +7 -0
  60. package/dist/components/headless/ConfigEditor.js.map +1 -0
  61. package/dist/components/headless/PermissionEditor.d.ts +15 -0
  62. package/dist/components/headless/PermissionEditor.d.ts.map +1 -0
  63. package/dist/components/headless/PermissionEditor.js +7 -0
  64. package/dist/components/headless/PermissionEditor.js.map +1 -0
  65. package/dist/components/headless/TenantSwitcher.d.ts +39 -0
  66. package/dist/components/headless/TenantSwitcher.d.ts.map +1 -0
  67. package/dist/components/headless/TenantSwitcher.js +61 -0
  68. package/dist/components/headless/TenantSwitcher.js.map +1 -0
  69. package/dist/components/headless/TenantTree.d.ts +21 -0
  70. package/dist/components/headless/TenantTree.d.ts.map +1 -0
  71. package/dist/components/headless/TenantTree.js +22 -0
  72. package/dist/components/headless/TenantTree.js.map +1 -0
  73. package/dist/components/storybook-helpers.d.ts +96 -0
  74. package/dist/components/storybook-helpers.d.ts.map +1 -0
  75. package/dist/components/storybook-helpers.js +473 -0
  76. package/dist/components/storybook-helpers.js.map +1 -0
  77. package/dist/hooks/use-config.d.ts +16 -0
  78. package/dist/hooks/use-config.d.ts.map +1 -0
  79. package/dist/hooks/use-config.js +54 -0
  80. package/dist/hooks/use-config.js.map +1 -0
  81. package/dist/hooks/use-messages.d.ts +11 -0
  82. package/dist/hooks/use-messages.d.ts.map +1 -0
  83. package/dist/hooks/use-messages.js +23 -0
  84. package/dist/hooks/use-messages.js.map +1 -0
  85. package/dist/hooks/use-permissions.d.ts +10 -0
  86. package/dist/hooks/use-permissions.d.ts.map +1 -0
  87. package/dist/hooks/use-permissions.js +48 -0
  88. package/dist/hooks/use-permissions.js.map +1 -0
  89. package/dist/hooks/use-tenant-tree.d.ts +13 -0
  90. package/dist/hooks/use-tenant-tree.d.ts.map +1 -0
  91. package/dist/hooks/use-tenant-tree.js +65 -0
  92. package/dist/hooks/use-tenant-tree.js.map +1 -0
  93. package/dist/hooks/use-tenant.d.ts +23 -0
  94. package/dist/hooks/use-tenant.d.ts.map +1 -0
  95. package/dist/hooks/use-tenant.js +6 -0
  96. package/dist/hooks/use-tenant.js.map +1 -0
  97. package/dist/hooks/use-toast.d.ts +14 -0
  98. package/dist/hooks/use-toast.d.ts.map +1 -0
  99. package/dist/hooks/use-toast.js +46 -0
  100. package/dist/hooks/use-toast.js.map +1 -0
  101. package/dist/i18n.d.ts +57 -0
  102. package/dist/i18n.d.ts.map +1 -0
  103. package/dist/i18n.js +59 -0
  104. package/dist/i18n.js.map +1 -0
  105. package/dist/index.d.ts +38 -0
  106. package/dist/index.d.ts.map +1 -0
  107. package/dist/index.js +26 -0
  108. package/dist/index.js.map +1 -0
  109. package/dist/provider.d.ts +30 -0
  110. package/dist/provider.d.ts.map +1 -0
  111. package/dist/provider.js +82 -0
  112. package/dist/provider.js.map +1 -0
  113. package/package.json +53 -0
@@ -0,0 +1,5 @@
1
+ export interface ConfigEditorProps {
2
+ className?: string;
3
+ }
4
+ export declare function ConfigEditor({ className }: ConfigEditorProps): import("react/jsx-runtime").JSX.Element;
5
+ //# sourceMappingURL=ConfigEditor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ConfigEditor.d.ts","sourceRoot":"","sources":["../../src/components/ConfigEditor.tsx"],"names":[],"mappings":"AAMA,MAAM,WAAW,iBAAiB;IAChC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,YAAY,CAAC,EAAE,SAAS,EAAE,EAAE,iBAAiB,2CA6J5D"}
@@ -0,0 +1,71 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useState } from "react";
3
+ import { useConfig } from "../hooks/use-config.js";
4
+ import { useMessages } from "../hooks/use-messages.js";
5
+ import { useStratum } from "../provider.js";
6
+ import { TableSkeleton } from "./TableSkeleton.js";
7
+ export function ConfigEditor({ className }) {
8
+ const { config, loading, error, setConfigValue, deleteConfigValue } = useConfig();
9
+ const { toast } = useStratum();
10
+ const { t } = useMessages();
11
+ const [editingKey, setEditingKey] = useState(null);
12
+ const [editValue, setEditValue] = useState("");
13
+ const [newKey, setNewKey] = useState("");
14
+ const [newValue, setNewValue] = useState("");
15
+ if (loading) {
16
+ return (_jsx("div", { className: `stratum-config-editor ${className || ""}`, children: _jsx(TableSkeleton, { rows: 5, columns: 5 }) }));
17
+ }
18
+ if (error)
19
+ return _jsx("div", { className: className, children: t("configEditor.error", { message: error.message }) });
20
+ const handleSave = async (key) => {
21
+ try {
22
+ let value;
23
+ try {
24
+ value = JSON.parse(editValue);
25
+ }
26
+ catch {
27
+ value = editValue;
28
+ }
29
+ await setConfigValue(key, value);
30
+ setEditingKey(null);
31
+ toast.success(`Config "${key}" saved successfully`);
32
+ }
33
+ catch (err) {
34
+ toast.error(`Failed to save config "${key}": ${err instanceof Error ? err.message : String(err)}`);
35
+ }
36
+ };
37
+ const handleDelete = async (key) => {
38
+ try {
39
+ await deleteConfigValue(key);
40
+ toast.success(`Config "${key}" removed`);
41
+ }
42
+ catch (err) {
43
+ toast.error(`Failed to remove config "${key}": ${err instanceof Error ? err.message : String(err)}`);
44
+ }
45
+ };
46
+ const handleAdd = async () => {
47
+ if (!newKey)
48
+ return;
49
+ try {
50
+ let value;
51
+ try {
52
+ value = JSON.parse(newValue);
53
+ }
54
+ catch {
55
+ value = newValue;
56
+ }
57
+ await setConfigValue(newKey, value);
58
+ toast.success(`Config "${newKey}" added`);
59
+ setNewKey("");
60
+ setNewValue("");
61
+ }
62
+ catch (err) {
63
+ toast.error(`Failed to add config "${newKey}": ${err instanceof Error ? err.message : String(err)}`);
64
+ }
65
+ };
66
+ return (_jsxs("div", { className: `stratum-config-editor ${className || ""}`, children: [_jsxs("table", { className: "stratum-config-editor__table", children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx("th", { children: t("configEditor.columnKey") }), _jsx("th", { children: t("configEditor.columnValue") }), _jsx("th", { children: t("configEditor.columnSource") }), _jsx("th", { children: t("configEditor.columnStatus") }), _jsx("th", { children: t("configEditor.columnActions") })] }) }), _jsx("tbody", { children: config.map((entry) => (_jsxs("tr", { className: entry.locked ? "stratum-config-editor__row--locked" : "", children: [_jsx("td", { children: entry.key }), _jsx("td", { children: editingKey === entry.key ? (_jsx("input", { type: "text", value: editValue, onChange: (e) => setEditValue(e.target.value), onKeyDown: (e) => e.key === "Enter" && handleSave(entry.key), "aria-label": t("configEditor.editLabel", { key: entry.key }) })) : (_jsx("code", { children: JSON.stringify(entry.value) })) }), _jsxs("td", { className: "stratum-config-editor__source", children: [entry.source_tenant_id.slice(0, 8), "..."] }), _jsxs("td", { children: [entry.locked && _jsx("span", { className: "stratum-badge stratum-badge--locked", children: t("configEditor.locked") }), entry.inherited && !entry.locked && (_jsx("span", { className: "stratum-badge stratum-badge--inherited", children: t("configEditor.inherited") })), !entry.inherited && !entry.locked && (_jsx("span", { className: "stratum-badge stratum-badge--own", children: t("configEditor.own") }))] }), _jsx("td", { children: !entry.locked && (_jsx(_Fragment, { children: editingKey === entry.key ? (_jsxs(_Fragment, { children: [_jsx("button", { type: "button", onClick: () => handleSave(entry.key), children: t("configEditor.saveButton") }), _jsx("button", { type: "button", onClick: () => setEditingKey(null), children: t("configEditor.cancelButton") })] })) : (_jsxs(_Fragment, { children: [_jsx("button", { type: "button", onClick: () => {
67
+ setEditingKey(entry.key);
68
+ setEditValue(JSON.stringify(entry.value));
69
+ }, children: t("configEditor.editButton") }), !entry.inherited && (_jsx("button", { type: "button", onClick: () => handleDelete(entry.key), children: t("configEditor.removeButton") }))] })) })) })] }, entry.key))) })] }), _jsxs("div", { className: "stratum-config-editor__add", children: [_jsx("input", { type: "text", value: newKey, onChange: (e) => setNewKey(e.target.value), placeholder: t("configEditor.keyPlaceholder"), "aria-label": t("configEditor.keyLabel") }), _jsx("input", { type: "text", value: newValue, onChange: (e) => setNewValue(e.target.value), placeholder: t("configEditor.valuePlaceholder"), "aria-label": t("configEditor.valueLabel") }), _jsx("button", { type: "button", onClick: handleAdd, disabled: !newKey, children: t("configEditor.addButton") })] })] }));
70
+ }
71
+ //# sourceMappingURL=ConfigEditor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ConfigEditor.js","sourceRoot":"","sources":["../../src/components/ConfigEditor.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ import type { Meta, StoryObj } from "@storybook/react";
2
+ import { ConfigEditor } from "./ConfigEditor.js";
3
+ declare const meta: Meta<typeof ConfigEditor>;
4
+ export default meta;
5
+ type Story = StoryObj<typeof ConfigEditor>;
6
+ /**
7
+ * Default state with a mix of own, inherited, and locked configuration
8
+ * values. Represents a typical mid-level tenant (CyberShield MSP) that
9
+ * inherits SSO and MFA settings from Acme Corp but sets its own billing plan.
10
+ */
11
+ export declare const Default: Story;
12
+ /**
13
+ * Loading state shown while config entries are being resolved.
14
+ * In production this shows "Loading config..." while the API
15
+ * resolves the full inheritance chain.
16
+ */
17
+ export declare const Loading: Story;
18
+ /**
19
+ * Empty state when the tenant has no configuration values set
20
+ * and nothing is inherited from parent tenants.
21
+ */
22
+ export declare const Empty: Story;
23
+ /**
24
+ * All values locked by a parent tenant (e.g., Acme Corp enforcing
25
+ * organization-wide policy). No edit or delete actions are available.
26
+ * The rows are highlighted with a warning background.
27
+ */
28
+ export declare const AllLocked: Story;
29
+ /**
30
+ * Configuration that includes sensitive values like webhook URLs
31
+ * and API secret keys. These are typically locked and inherited
32
+ * from the root tenant.
33
+ */
34
+ export declare const WithSensitiveValues: Story;
35
+ //# sourceMappingURL=ConfigEditor.stories.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ConfigEditor.stories.d.ts","sourceRoot":"","sources":["../../src/components/ConfigEditor.stories.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAEvD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAQjD,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,OAAO,YAAY,CAuBnC,CAAC;AAEF,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,YAAY,CAAC,CAAC;AAE3C;;;;GAIG;AACH,eAAO,MAAM,OAAO,EAAE,KAErB,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,OAAO,EAAE,KAcrB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,KAAK,EAAE,KAcnB,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,SAAS,EAAE,KAcvB,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,EAAE,KAcjC,CAAC"}
@@ -0,0 +1,73 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { ConfigEditor } from "./ConfigEditor.js";
3
+ import { MockStratumProvider, mockLockedConfigEntries, mockSensitiveConfigEntries, } from "./storybook-helpers.js";
4
+ const meta = {
5
+ title: "Components/ConfigEditor",
6
+ component: ConfigEditor,
7
+ parameters: {
8
+ docs: {
9
+ description: {
10
+ component: "Table-based editor for tenant configuration values. " +
11
+ "Displays each key's value, source tenant, and inheritance status " +
12
+ "(Own, Inherited, Locked). Inherited values show the source tenant ID. " +
13
+ "Locked values cannot be edited or deleted.",
14
+ },
15
+ },
16
+ },
17
+ decorators: [
18
+ (Story) => (_jsx(MockStratumProvider, { initialTenantId: "00000000-0000-0000-0000-000000000003", children: _jsx("div", { style: { padding: "24px", maxWidth: 900 }, children: _jsx(Story, {}) }) })),
19
+ ],
20
+ };
21
+ export default meta;
22
+ /**
23
+ * Default state with a mix of own, inherited, and locked configuration
24
+ * values. Represents a typical mid-level tenant (CyberShield MSP) that
25
+ * inherits SSO and MFA settings from Acme Corp but sets its own billing plan.
26
+ */
27
+ export const Default = {
28
+ args: {},
29
+ };
30
+ /**
31
+ * Loading state shown while config entries are being resolved.
32
+ * In production this shows "Loading config..." while the API
33
+ * resolves the full inheritance chain.
34
+ */
35
+ export const Loading = {
36
+ args: {},
37
+ decorators: [
38
+ (Story) => (_jsx(MockStratumProvider, { initialTenantId: "00000000-0000-0000-0000-000000000003", loading: true, children: _jsx("div", { style: { padding: "24px", maxWidth: 900 }, children: _jsx(Story, {}) }) })),
39
+ ],
40
+ };
41
+ /**
42
+ * Empty state when the tenant has no configuration values set
43
+ * and nothing is inherited from parent tenants.
44
+ */
45
+ export const Empty = {
46
+ args: {},
47
+ decorators: [
48
+ (Story) => (_jsx(MockStratumProvider, { initialTenantId: "00000000-0000-0000-0000-000000000003", configEntries: [], children: _jsx("div", { style: { padding: "24px", maxWidth: 900 }, children: _jsx(Story, {}) }) })),
49
+ ],
50
+ };
51
+ /**
52
+ * All values locked by a parent tenant (e.g., Acme Corp enforcing
53
+ * organization-wide policy). No edit or delete actions are available.
54
+ * The rows are highlighted with a warning background.
55
+ */
56
+ export const AllLocked = {
57
+ args: {},
58
+ decorators: [
59
+ (Story) => (_jsx(MockStratumProvider, { initialTenantId: "00000000-0000-0000-0000-000000000003", configEntries: mockLockedConfigEntries, children: _jsx("div", { style: { padding: "24px", maxWidth: 900 }, children: _jsx(Story, {}) }) })),
60
+ ],
61
+ };
62
+ /**
63
+ * Configuration that includes sensitive values like webhook URLs
64
+ * and API secret keys. These are typically locked and inherited
65
+ * from the root tenant.
66
+ */
67
+ export const WithSensitiveValues = {
68
+ args: {},
69
+ decorators: [
70
+ (Story) => (_jsx(MockStratumProvider, { initialTenantId: "00000000-0000-0000-0000-000000000003", configEntries: mockSensitiveConfigEntries, children: _jsx("div", { style: { padding: "24px", maxWidth: 900 }, children: _jsx(Story, {}) }) })),
71
+ ],
72
+ };
73
+ //# sourceMappingURL=ConfigEditor.stories.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ConfigEditor.stories.js","sourceRoot":"","sources":["../../src/components/ConfigEditor.stories.tsx"],"names":[],"mappings":";AAEA,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EACL,mBAAmB,EAEnB,uBAAuB,EACvB,0BAA0B,GAC3B,MAAM,wBAAwB,CAAC;AAEhC,MAAM,IAAI,GAA8B;IACtC,KAAK,EAAE,yBAAyB;IAChC,SAAS,EAAE,YAAY;IACvB,UAAU,EAAE;QACV,IAAI,EAAE;YACJ,WAAW,EAAE;gBACX,SAAS,EACP,sDAAsD;oBACtD,mEAAmE;oBACnE,wEAAwE;oBACxE,4CAA4C;aAC/C;SACF;KACF;IACD,UAAU,EAAE;QACV,CAAC,KAAK,EAAE,EAAE,CAAC,CACT,KAAC,mBAAmB,IAAC,eAAe,EAAC,sCAAsC,YACzE,cAAK,KAAK,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,EAAE,YAC5C,KAAC,KAAK,KAAG,GACL,GACc,CACvB;KACF;CACF,CAAC;AAEF,eAAe,IAAI,CAAC;AAGpB;;;;GAIG;AACH,MAAM,CAAC,MAAM,OAAO,GAAU;IAC5B,IAAI,EAAE,EAAE;CACT,CAAC;AAEF;;;;GAIG;AACH,MAAM,CAAC,MAAM,OAAO,GAAU;IAC5B,IAAI,EAAE,EAAE;IACR,UAAU,EAAE;QACV,CAAC,KAAK,EAAE,EAAE,CAAC,CACT,KAAC,mBAAmB,IAClB,eAAe,EAAC,sCAAsC,EACtD,OAAO,EAAE,IAAI,YAEb,cAAK,KAAK,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,EAAE,YAC5C,KAAC,KAAK,KAAG,GACL,GACc,CACvB;KACF;CACF,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,MAAM,KAAK,GAAU;IAC1B,IAAI,EAAE,EAAE;IACR,UAAU,EAAE;QACV,CAAC,KAAK,EAAE,EAAE,CAAC,CACT,KAAC,mBAAmB,IAClB,eAAe,EAAC,sCAAsC,EACtD,aAAa,EAAE,EAAE,YAEjB,cAAK,KAAK,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,EAAE,YAC5C,KAAC,KAAK,KAAG,GACL,GACc,CACvB;KACF;CACF,CAAC;AAEF;;;;GAIG;AACH,MAAM,CAAC,MAAM,SAAS,GAAU;IAC9B,IAAI,EAAE,EAAE;IACR,UAAU,EAAE;QACV,CAAC,KAAK,EAAE,EAAE,CAAC,CACT,KAAC,mBAAmB,IAClB,eAAe,EAAC,sCAAsC,EACtD,aAAa,EAAE,uBAAuB,YAEtC,cAAK,KAAK,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,EAAE,YAC5C,KAAC,KAAK,KAAG,GACL,GACc,CACvB;KACF;CACF,CAAC;AAEF;;;;GAIG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAU;IACxC,IAAI,EAAE,EAAE;IACR,UAAU,EAAE;QACV,CAAC,KAAK,EAAE,EAAE,CAAC,CACT,KAAC,mBAAmB,IAClB,eAAe,EAAC,sCAAsC,EACtD,aAAa,EAAE,0BAA0B,YAEzC,cAAK,KAAK,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,EAAE,YAC5C,KAAC,KAAK,KAAG,GACL,GACc,CACvB;KACF;CACF,CAAC"}
@@ -0,0 +1,5 @@
1
+ export interface PermissionEditorProps {
2
+ className?: string;
3
+ }
4
+ export declare function PermissionEditor({ className }: PermissionEditorProps): import("react/jsx-runtime").JSX.Element;
5
+ //# sourceMappingURL=PermissionEditor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PermissionEditor.d.ts","sourceRoot":"","sources":["../../src/components/PermissionEditor.tsx"],"names":[],"mappings":"AAMA,MAAM,WAAW,qBAAqB;IACpC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAKD,wBAAgB,gBAAgB,CAAC,EAAE,SAAS,EAAE,EAAE,qBAAqB,2CAiHpE"}
@@ -0,0 +1,46 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState } from "react";
3
+ import { usePermissions } from "../hooks/use-permissions.js";
4
+ import { useMessages } from "../hooks/use-messages.js";
5
+ import { useStratum } from "../provider.js";
6
+ import { TableSkeleton } from "./TableSkeleton.js";
7
+ const MODES = ["LOCKED", "INHERITED", "DELEGATED"];
8
+ const REVOCATION_MODES = ["CASCADE", "SOFT", "PERMANENT"];
9
+ export function PermissionEditor({ className }) {
10
+ const { permissions, loading, error, createPermission, deletePermission } = usePermissions();
11
+ const { toast } = useStratum();
12
+ const { t } = useMessages();
13
+ const [newKey, setNewKey] = useState("");
14
+ const [newMode, setNewMode] = useState("INHERITED");
15
+ const [newRevocationMode, setNewRevocationMode] = useState("CASCADE");
16
+ if (loading) {
17
+ return (_jsx("div", { className: `stratum-permission-editor ${className || ""}`, children: _jsx(TableSkeleton, { rows: 5, columns: 6 }) }));
18
+ }
19
+ if (error)
20
+ return _jsx("div", { className: className, children: t("permissionEditor.error", { message: error.message }) });
21
+ const handleAdd = async () => {
22
+ if (!newKey)
23
+ return;
24
+ try {
25
+ await createPermission(newKey, true, newMode, newRevocationMode);
26
+ toast.success(`Permission "${newKey}" added`);
27
+ setNewKey("");
28
+ setNewMode("INHERITED");
29
+ setNewRevocationMode("CASCADE");
30
+ }
31
+ catch (err) {
32
+ toast.error(`Failed to add permission "${newKey}": ${err instanceof Error ? err.message : String(err)}`);
33
+ }
34
+ };
35
+ const handleDelete = async (key, sourceTenantId) => {
36
+ try {
37
+ await deletePermission(sourceTenantId);
38
+ toast.success(`Permission "${key}" removed`);
39
+ }
40
+ catch (err) {
41
+ toast.error(`Failed to remove permission "${key}": ${err instanceof Error ? err.message : String(err)}`);
42
+ }
43
+ };
44
+ return (_jsxs("div", { className: `stratum-permission-editor ${className || ""}`, children: [_jsxs("table", { className: "stratum-permission-editor__table", children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx("th", { children: t("permissionEditor.columnKey") }), _jsx("th", { children: t("permissionEditor.columnValue") }), _jsx("th", { children: t("permissionEditor.columnMode") }), _jsx("th", { children: t("permissionEditor.columnSource") }), _jsx("th", { children: t("permissionEditor.columnStatus") }), _jsx("th", { children: t("permissionEditor.columnActions") })] }) }), _jsx("tbody", { children: permissions.map((perm) => (_jsxs("tr", { children: [_jsx("td", { children: perm.key }), _jsx("td", { children: _jsx("code", { children: JSON.stringify(perm.value) }) }), _jsx("td", { children: _jsx("span", { className: `stratum-badge stratum-badge--${perm.mode.toLowerCase()}`, children: perm.mode }) }), _jsxs("td", { className: "stratum-permission-editor__source", children: [perm.source_tenant_id.slice(0, 8), "..."] }), _jsxs("td", { children: [perm.locked && _jsx("span", { className: "stratum-badge stratum-badge--locked", children: t("permissionEditor.locked") }), perm.delegated && _jsx("span", { className: "stratum-badge stratum-badge--delegated", children: t("permissionEditor.delegated") })] }), _jsx("td", { children: !perm.locked && (_jsx("button", { type: "button", onClick: () => handleDelete(perm.key, perm.source_tenant_id), children: t("permissionEditor.removeButton") })) })] }, perm.key))) })] }), _jsxs("div", { className: "stratum-permission-editor__add", children: [_jsx("input", { type: "text", value: newKey, onChange: (e) => setNewKey(e.target.value), placeholder: t("permissionEditor.keyPlaceholder"), "aria-label": t("permissionEditor.keyLabel") }), _jsx("select", { value: newMode, onChange: (e) => setNewMode(e.target.value), "aria-label": t("permissionEditor.modeLabel"), children: MODES.map((m) => (_jsx("option", { value: m, children: m }, m))) }), _jsx("select", { value: newRevocationMode, onChange: (e) => setNewRevocationMode(e.target.value), "aria-label": t("permissionEditor.revocationModeLabel"), children: REVOCATION_MODES.map((m) => (_jsx("option", { value: m, children: m }, m))) }), _jsx("button", { type: "button", onClick: handleAdd, disabled: !newKey, children: t("permissionEditor.addButton") })] })] }));
45
+ }
46
+ //# sourceMappingURL=PermissionEditor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PermissionEditor.js","sourceRoot":"","sources":["../../src/components/PermissionEditor.tsx"],"names":[],"mappings":";AAAA,OAAc,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACxC,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvD,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAMnD,MAAM,KAAK,GAAG,CAAC,QAAQ,EAAE,WAAW,EAAE,WAAW,CAAU,CAAC;AAC5D,MAAM,gBAAgB,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,WAAW,CAAU,CAAC;AAEnE,MAAM,UAAU,gBAAgB,CAAC,EAAE,SAAS,EAAyB;IACnE,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,KAAK,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,GAAG,cAAc,EAAE,CAAC;IAC7F,MAAM,EAAE,KAAK,EAAE,GAAG,UAAU,EAAE,CAAC;IAC/B,MAAM,EAAE,CAAC,EAAE,GAAG,WAAW,EAAE,CAAC;IAC5B,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,QAAQ,CAAC,EAAE,CAAC,CAAC;IACzC,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAS,WAAW,CAAC,CAAC;IAC5D,MAAM,CAAC,iBAAiB,EAAE,oBAAoB,CAAC,GAAG,QAAQ,CAAS,SAAS,CAAC,CAAC;IAE9E,IAAI,OAAO,EAAE,CAAC;QACZ,OAAO,CACL,cAAK,SAAS,EAAE,6BAA6B,SAAS,IAAI,EAAE,EAAE,YAC5D,KAAC,aAAa,IAAC,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,GAAI,GAClC,CACP,CAAC;IACJ,CAAC;IACD,IAAI,KAAK;QAAE,OAAO,cAAK,SAAS,EAAE,SAAS,YAAG,CAAC,CAAC,wBAAwB,EAAE,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC,GAAO,CAAC;IAE7G,MAAM,SAAS,GAAG,KAAK,IAAI,EAAE;QAC3B,IAAI,CAAC,MAAM;YAAE,OAAO;QACpB,IAAI,CAAC;YACH,MAAM,gBAAgB,CAAC,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,iBAAiB,CAAC,CAAC;YACjE,KAAK,CAAC,OAAO,CAAC,eAAe,MAAM,SAAS,CAAC,CAAC;YAC9C,SAAS,CAAC,EAAE,CAAC,CAAC;YACd,UAAU,CAAC,WAAW,CAAC,CAAC;YACxB,oBAAoB,CAAC,SAAS,CAAC,CAAC;QAClC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,KAAK,CAAC,KAAK,CAAC,6BAA6B,MAAM,MAAM,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAC3G,CAAC;IACH,CAAC,CAAC;IAEF,MAAM,YAAY,GAAG,KAAK,EAAE,GAAW,EAAE,cAAsB,EAAE,EAAE;QACjE,IAAI,CAAC;YACH,MAAM,gBAAgB,CAAC,cAAc,CAAC,CAAC;YACvC,KAAK,CAAC,OAAO,CAAC,eAAe,GAAG,WAAW,CAAC,CAAC;QAC/C,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,KAAK,CAAC,KAAK,CAAC,gCAAgC,GAAG,MAAM,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAC3G,CAAC;IACH,CAAC,CAAC;IAEF,OAAO,CACL,eAAK,SAAS,EAAE,6BAA6B,SAAS,IAAI,EAAE,EAAE,aAC5D,iBAAO,SAAS,EAAC,kCAAkC,aACjD,0BACE,yBACE,uBAAK,CAAC,CAAC,4BAA4B,CAAC,GAAM,EAC1C,uBAAK,CAAC,CAAC,8BAA8B,CAAC,GAAM,EAC5C,uBAAK,CAAC,CAAC,6BAA6B,CAAC,GAAM,EAC3C,uBAAK,CAAC,CAAC,+BAA+B,CAAC,GAAM,EAC7C,uBAAK,CAAC,CAAC,+BAA+B,CAAC,GAAM,EAC7C,uBAAK,CAAC,CAAC,gCAAgC,CAAC,GAAM,IAC3C,GACC,EACR,0BACG,WAAW,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CACzB,yBACE,uBAAK,IAAI,CAAC,GAAG,GAAM,EACnB,uBAAI,yBAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,GAAQ,GAAK,EAClD,uBACE,eAAM,SAAS,EAAE,gCAAgC,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,YACvE,IAAI,CAAC,IAAI,GACL,GACJ,EACL,cAAI,SAAS,EAAC,mCAAmC,aAC9C,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,WAC/B,EACL,yBACG,IAAI,CAAC,MAAM,IAAI,eAAM,SAAS,EAAC,qCAAqC,YAAE,CAAC,CAAC,yBAAyB,CAAC,GAAQ,EAC1G,IAAI,CAAC,SAAS,IAAI,eAAM,SAAS,EAAC,wCAAwC,YAAE,CAAC,CAAC,4BAA4B,CAAC,GAAQ,IACjH,EACL,uBACG,CAAC,IAAI,CAAC,MAAM,IAAI,CACf,iBAAQ,IAAI,EAAC,QAAQ,EAAC,OAAO,EAAE,GAAG,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,gBAAgB,CAAC,YAC/E,CAAC,CAAC,+BAA+B,CAAC,GAC5B,CACV,GACE,KArBE,IAAI,CAAC,GAAG,CAsBZ,CACN,CAAC,GACI,IACF,EAER,eAAK,SAAS,EAAC,gCAAgC,aAC7C,gBACE,IAAI,EAAC,MAAM,EACX,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,CAAC,CAAsC,EAAE,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAC/E,WAAW,EAAE,CAAC,CAAC,iCAAiC,CAAC,gBACrC,CAAC,CAAC,2BAA2B,CAAC,GAC1C,EACF,iBACE,KAAK,EAAE,OAAO,EACd,QAAQ,EAAE,CAAC,CAAuC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,gBACrE,CAAC,CAAC,4BAA4B,CAAC,YAE1C,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAChB,iBAAgB,KAAK,EAAE,CAAC,YAAG,CAAC,IAAf,CAAC,CAAwB,CACvC,CAAC,GACK,EACT,iBACE,KAAK,EAAE,iBAAiB,EACxB,QAAQ,EAAE,CAAC,CAAuC,EAAE,EAAE,CAAC,oBAAoB,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,gBAC/E,CAAC,CAAC,sCAAsC,CAAC,YAEpD,gBAAgB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAC3B,iBAAgB,KAAK,EAAE,CAAC,YAAG,CAAC,IAAf,CAAC,CAAwB,CACvC,CAAC,GACK,EACT,iBAAQ,IAAI,EAAC,QAAQ,EAAC,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC,MAAM,YACxD,CAAC,CAAC,4BAA4B,CAAC,GACzB,IACL,IACF,CACP,CAAC;AACJ,CAAC"}
@@ -0,0 +1,34 @@
1
+ import type { Meta, StoryObj } from "@storybook/react";
2
+ import { PermissionEditor } from "./PermissionEditor.js";
3
+ declare const meta: Meta<typeof PermissionEditor>;
4
+ export default meta;
5
+ type Story = StoryObj<typeof PermissionEditor>;
6
+ /**
7
+ * Default state with a realistic mix of permission modes.
8
+ * - LOCKED: tenant.manage and audit.read (set by root, cannot be changed)
9
+ * - INHERITED: config.write and users.invite (flow from ancestor)
10
+ * - DELEGATED: billing.view and webhooks.manage (delegated to this tenant)
11
+ */
12
+ export declare const Default: Story;
13
+ /**
14
+ * Loading state while permissions are being resolved through
15
+ * the inheritance chain.
16
+ */
17
+ export declare const Loading: Story;
18
+ /**
19
+ * Empty state when no permission policies have been defined.
20
+ * Only the table headers and the "Add" form are visible.
21
+ */
22
+ export declare const Empty: Story;
23
+ /**
24
+ * All permissions locked by the root tenant. No "Remove" actions
25
+ * are available. This represents a leaf tenant with fully restricted
26
+ * permission management.
27
+ */
28
+ export declare const AllLocked: Story;
29
+ /**
30
+ * All permissions delegated to this tenant, meaning the tenant
31
+ * has full control and can remove any of them.
32
+ */
33
+ export declare const AllDelegated: Story;
34
+ //# sourceMappingURL=PermissionEditor.stories.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PermissionEditor.stories.d.ts","sourceRoot":"","sources":["../../src/components/PermissionEditor.stories.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAEvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAGzD,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,OAAO,gBAAgB,CAuBvC,CAAC;AAEF,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAE/C;;;;;GAKG;AACH,eAAO,MAAM,OAAO,EAAE,KAErB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,OAAO,EAAE,KAcrB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,KAAK,EAAE,KAcnB,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,SAAS,EAAE,KAuBvB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,YAAY,EAAE,KAuB1B,CAAC"}
@@ -0,0 +1,90 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { PermissionEditor } from "./PermissionEditor.js";
3
+ import { MockStratumProvider, mockPermissions } from "./storybook-helpers.js";
4
+ const meta = {
5
+ title: "Components/PermissionEditor",
6
+ component: PermissionEditor,
7
+ parameters: {
8
+ docs: {
9
+ description: {
10
+ component: "Table-based editor for tenant permission policies. " +
11
+ "Shows each permission's key, value, mode (LOCKED / INHERITED / DELEGATED), " +
12
+ "source tenant, and status badges. Locked permissions cannot be removed. " +
13
+ "Supports adding new permissions with configurable mode and revocation policy.",
14
+ },
15
+ },
16
+ },
17
+ decorators: [
18
+ (Story) => (_jsx(MockStratumProvider, { initialTenantId: "00000000-0000-0000-0000-000000000003", children: _jsx("div", { style: { padding: "24px", maxWidth: 900 }, children: _jsx(Story, {}) }) })),
19
+ ],
20
+ };
21
+ export default meta;
22
+ /**
23
+ * Default state with a realistic mix of permission modes.
24
+ * - LOCKED: tenant.manage and audit.read (set by root, cannot be changed)
25
+ * - INHERITED: config.write and users.invite (flow from ancestor)
26
+ * - DELEGATED: billing.view and webhooks.manage (delegated to this tenant)
27
+ */
28
+ export const Default = {
29
+ args: {},
30
+ };
31
+ /**
32
+ * Loading state while permissions are being resolved through
33
+ * the inheritance chain.
34
+ */
35
+ export const Loading = {
36
+ args: {},
37
+ decorators: [
38
+ (Story) => (_jsx(MockStratumProvider, { initialTenantId: "00000000-0000-0000-0000-000000000003", loading: true, children: _jsx("div", { style: { padding: "24px", maxWidth: 900 }, children: _jsx(Story, {}) }) })),
39
+ ],
40
+ };
41
+ /**
42
+ * Empty state when no permission policies have been defined.
43
+ * Only the table headers and the "Add" form are visible.
44
+ */
45
+ export const Empty = {
46
+ args: {},
47
+ decorators: [
48
+ (Story) => (_jsx(MockStratumProvider, { initialTenantId: "00000000-0000-0000-0000-000000000003", permissions: [], children: _jsx("div", { style: { padding: "24px", maxWidth: 900 }, children: _jsx(Story, {}) }) })),
49
+ ],
50
+ };
51
+ /**
52
+ * All permissions locked by the root tenant. No "Remove" actions
53
+ * are available. This represents a leaf tenant with fully restricted
54
+ * permission management.
55
+ */
56
+ export const AllLocked = {
57
+ args: {},
58
+ decorators: [
59
+ (Story) => {
60
+ const allLocked = mockPermissions.map((p) => ({
61
+ ...p,
62
+ mode: "LOCKED",
63
+ locked: true,
64
+ delegated: false,
65
+ source_tenant_id: "00000000-0000-0000-0000-000000000001",
66
+ }));
67
+ return (_jsx(MockStratumProvider, { initialTenantId: "00000000-0000-0000-0000-000000000003", permissions: allLocked, children: _jsx("div", { style: { padding: "24px", maxWidth: 900 }, children: _jsx(Story, {}) }) }));
68
+ },
69
+ ],
70
+ };
71
+ /**
72
+ * All permissions delegated to this tenant, meaning the tenant
73
+ * has full control and can remove any of them.
74
+ */
75
+ export const AllDelegated = {
76
+ args: {},
77
+ decorators: [
78
+ (Story) => {
79
+ const allDelegated = mockPermissions.map((p) => ({
80
+ ...p,
81
+ mode: "DELEGATED",
82
+ locked: false,
83
+ delegated: true,
84
+ source_tenant_id: "00000000-0000-0000-0000-000000000003",
85
+ }));
86
+ return (_jsx(MockStratumProvider, { initialTenantId: "00000000-0000-0000-0000-000000000003", permissions: allDelegated, children: _jsx("div", { style: { padding: "24px", maxWidth: 900 }, children: _jsx(Story, {}) }) }));
87
+ },
88
+ ],
89
+ };
90
+ //# sourceMappingURL=PermissionEditor.stories.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PermissionEditor.stories.js","sourceRoot":"","sources":["../../src/components/PermissionEditor.stories.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ export interface SkeletonProps {
2
+ /** Width of the skeleton element (CSS value) */
3
+ width?: string;
4
+ /** Height of the skeleton element (CSS value) */
5
+ height?: string;
6
+ /** Shape variant */
7
+ variant?: "text" | "rect" | "circle";
8
+ /** Number of skeleton elements to render */
9
+ count?: number;
10
+ /** Additional CSS class name */
11
+ className?: string;
12
+ }
13
+ export declare function Skeleton({ width, height, variant, count, className, }: SkeletonProps): import("react/jsx-runtime").JSX.Element;
14
+ //# sourceMappingURL=Skeleton.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Skeleton.d.ts","sourceRoot":"","sources":["../../src/components/Skeleton.tsx"],"names":[],"mappings":"AAEA,MAAM,WAAW,aAAa;IAC5B,gDAAgD;IAChD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,iDAAiD;IACjD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,oBAAoB;IACpB,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,QAAQ,CAAC;IACrC,4CAA4C;IAC5C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gCAAgC;IAChC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,QAAQ,CAAC,EACvB,KAAK,EACL,MAAM,EACN,OAAgB,EAChB,KAAS,EACT,SAAS,GACV,EAAE,aAAa,2CAqCf"}
@@ -0,0 +1,25 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ export function Skeleton({ width, height, variant = "text", count = 1, className, }) {
3
+ const variantStyles = {
4
+ text: {
5
+ width: width || "100%",
6
+ height: height || "0.8125rem",
7
+ borderRadius: "var(--radius-sm, 4px)",
8
+ },
9
+ rect: {
10
+ width: width || "100%",
11
+ height: height || "48px",
12
+ borderRadius: "var(--radius-sm, 4px)",
13
+ },
14
+ circle: {
15
+ width: width || "40px",
16
+ height: height || "40px",
17
+ borderRadius: "var(--radius-full, 9999px)",
18
+ },
19
+ }[variant];
20
+ const elements = Array.from({ length: count }, (_, i) => (_jsx("div", { className: `stratum-skeleton stratum-skeleton--${variant} ${className || ""}`, style: variantStyles, role: "status", "aria-label": "Loading", "aria-busy": "true" }, i)));
21
+ if (count === 1)
22
+ return elements[0];
23
+ return (_jsx("div", { className: "stratum-skeleton__group", style: { display: "flex", flexDirection: "column", gap: "var(--space-sm, 8px)" }, children: elements }));
24
+ }
25
+ //# sourceMappingURL=Skeleton.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Skeleton.js","sourceRoot":"","sources":["../../src/components/Skeleton.tsx"],"names":[],"mappings":";AAeA,MAAM,UAAU,QAAQ,CAAC,EACvB,KAAK,EACL,MAAM,EACN,OAAO,GAAG,MAAM,EAChB,KAAK,GAAG,CAAC,EACT,SAAS,GACK;IACd,MAAM,aAAa,GAAwB;QACzC,IAAI,EAAE;YACJ,KAAK,EAAE,KAAK,IAAI,MAAM;YACtB,MAAM,EAAE,MAAM,IAAI,WAAW;YAC7B,YAAY,EAAE,uBAAuB;SACtC;QACD,IAAI,EAAE;YACJ,KAAK,EAAE,KAAK,IAAI,MAAM;YACtB,MAAM,EAAE,MAAM,IAAI,MAAM;YACxB,YAAY,EAAE,uBAAuB;SACtC;QACD,MAAM,EAAE;YACN,KAAK,EAAE,KAAK,IAAI,MAAM;YACtB,MAAM,EAAE,MAAM,IAAI,MAAM;YACxB,YAAY,EAAE,4BAA4B;SAC3C;KACF,CAAC,OAAO,CAAC,CAAC;IAEX,MAAM,QAAQ,GAAG,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CACvD,cAEE,SAAS,EAAE,sCAAsC,OAAO,IAAI,SAAS,IAAI,EAAE,EAAE,EAC7E,KAAK,EAAE,aAAa,EACpB,IAAI,EAAC,QAAQ,gBACF,SAAS,eACV,MAAM,IALX,CAAC,CAMN,CACH,CAAC,CAAC;IAEH,IAAI,KAAK,KAAK,CAAC;QAAE,OAAO,QAAQ,CAAC,CAAC,CAAE,CAAC;IAErC,OAAO,CACL,cAAK,SAAS,EAAC,yBAAyB,EAAC,KAAK,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,QAAQ,EAAE,GAAG,EAAE,sBAAsB,EAAE,YACtH,QAAQ,GACL,CACP,CAAC;AACJ,CAAC"}
@@ -0,0 +1,46 @@
1
+ import type { Meta, StoryObj } from "@storybook/react";
2
+ import { Skeleton } from "./Skeleton.js";
3
+ import { TableSkeleton } from "./TableSkeleton.js";
4
+ declare const meta: Meta<typeof Skeleton>;
5
+ export default meta;
6
+ type Story = StoryObj<typeof Skeleton>;
7
+ /**
8
+ * Text variant — default skeleton for loading text lines.
9
+ * Full width, 13px height matching the base font size.
10
+ */
11
+ export declare const Text: Story;
12
+ /**
13
+ * Multiple text lines simulating a paragraph or list loading state.
14
+ */
15
+ export declare const TextMultipleLines: Story;
16
+ /**
17
+ * Rectangle variant — for cards, images, or block-level placeholders.
18
+ */
19
+ export declare const Rect: Story;
20
+ /**
21
+ * Circle variant — for avatar or icon placeholders.
22
+ */
23
+ export declare const Circle: Story;
24
+ /**
25
+ * Composite layout: avatar circle next to text lines,
26
+ * simulating a tenant detail card loading state.
27
+ */
28
+ export declare const CompositeLayout: Story;
29
+ /**
30
+ * Custom sizing with small dimensions for inline placeholders.
31
+ */
32
+ export declare const CustomSizing: Story;
33
+ /**
34
+ * Default TableSkeleton with 5 rows and 4 columns, matching the
35
+ * ConfigEditor table layout.
36
+ */
37
+ export declare const TableDefault: StoryObj<typeof TableSkeleton>;
38
+ /**
39
+ * Config-style table skeleton with 5 columns (Key, Value, Source, Status, Actions).
40
+ */
41
+ export declare const TableConfigLayout: StoryObj<typeof TableSkeleton>;
42
+ /**
43
+ * Minimal skeleton — 2 rows, 2 columns.
44
+ */
45
+ export declare const TableMinimal: StoryObj<typeof TableSkeleton>;
46
+ //# sourceMappingURL=Skeleton.stories.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Skeleton.stories.d.ts","sourceRoot":"","sources":["../../src/components/Skeleton.stories.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAEvD,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAEnD,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,OAAO,QAAQ,CAa/B,CAAC;AAEF,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,QAAQ,CAAC,CAAC;AAEvC;;;GAGG;AACH,eAAO,MAAM,IAAI,EAAE,KAWlB,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,iBAAiB,EAAE,KAY/B,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,IAAI,EAAE,KAalB,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,MAAM,EAAE,KAapB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,eAAe,EAAE,KAgB7B,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,YAAY,EAAE,KAa1B,CAAC;AAwBF;;;GAGG;AACH,eAAO,MAAM,YAAY,EAAE,QAAQ,CAAC,OAAO,aAAa,CAMvD,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,iBAAiB,EAAE,QAAQ,CAAC,OAAO,aAAa,CAM5D,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,YAAY,EAAE,QAAQ,CAAC,OAAO,aAAa,CAMvD,CAAC"}