hazo_admin 0.1.1 → 0.3.1

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 (47) hide show
  1. package/CHANGE_LOG.md +17 -0
  2. package/README.md +72 -4
  3. package/SETUP_CHECKLIST.md +9 -1
  4. package/dist/api/index.d.ts.map +1 -1
  5. package/dist/api/index.js +109 -5
  6. package/dist/components/admin_app.d.ts.map +1 -1
  7. package/dist/components/admin_app.js +26 -0
  8. package/dist/components/admin_kinds.d.ts +15 -0
  9. package/dist/components/admin_kinds.d.ts.map +1 -0
  10. package/dist/components/admin_kinds.js +120 -0
  11. package/dist/components/admin_layout.d.ts.map +1 -1
  12. package/dist/components/admin_layout.js +18 -20
  13. package/dist/components/admin_nav.d.ts +17 -2
  14. package/dist/components/admin_nav.d.ts.map +1 -1
  15. package/dist/components/admin_nav.js +111 -1
  16. package/dist/components/api_keys_panel.d.ts +5 -0
  17. package/dist/components/api_keys_panel.d.ts.map +1 -0
  18. package/dist/components/api_keys_panel.js +21 -0
  19. package/dist/components/audit_panel.d.ts +5 -0
  20. package/dist/components/audit_panel.d.ts.map +1 -0
  21. package/dist/components/audit_panel.js +19 -0
  22. package/dist/components/blog_panel.d.ts +5 -0
  23. package/dist/components/blog_panel.d.ts.map +1 -0
  24. package/dist/components/blog_panel.js +23 -0
  25. package/dist/components/env_migration_panel.d.ts.map +1 -1
  26. package/dist/components/env_migration_panel.js +6 -8
  27. package/dist/components/feedback_panel.d.ts +5 -0
  28. package/dist/components/feedback_panel.d.ts.map +1 -0
  29. package/dist/components/feedback_panel.js +24 -0
  30. package/dist/components/files_panel.d.ts +5 -0
  31. package/dist/components/files_panel.d.ts.map +1 -0
  32. package/dist/components/files_panel.js +22 -0
  33. package/dist/components/masking_panel.d.ts +5 -0
  34. package/dist/components/masking_panel.d.ts.map +1 -0
  35. package/dist/components/masking_panel.js +65 -0
  36. package/dist/components/settings_panel.d.ts +5 -0
  37. package/dist/components/settings_panel.d.ts.map +1 -0
  38. package/dist/components/settings_panel.js +19 -0
  39. package/dist/index.client.d.ts +8 -0
  40. package/dist/index.client.d.ts.map +1 -1
  41. package/dist/index.client.js +8 -0
  42. package/dist/index.ui.d.ts +2 -0
  43. package/dist/index.ui.d.ts.map +1 -1
  44. package/dist/index.ui.js +1 -0
  45. package/dist/preset/page_factory.d.ts.map +1 -1
  46. package/dist/preset/page_factory.js +9 -13
  47. package/package.json +18 -8
@@ -1,5 +1,6 @@
1
+ import { getDefaultPermission } from './admin_kinds.js';
1
2
  export function defaultPermissionForKind(kind) {
2
- return kind === 'env_migration' || kind === 'health' ? 'env.migrate' : 'admin_system';
3
+ return getDefaultPermission(kind) ?? 'admin_system';
3
4
  }
4
5
  export function resolveNav(manifest, permissions) {
5
6
  const items = [];
@@ -15,6 +16,8 @@ export function resolveNav(manifest, permissions) {
15
16
  basePath: section.basePath ?? '/api/hazo_auth',
16
17
  icon: section.icon,
17
18
  permission,
19
+ group: section.group,
20
+ order: section.order,
18
21
  });
19
22
  }
20
23
  else if (section.kind === 'jobs') {
@@ -25,6 +28,8 @@ export function resolveNav(manifest, permissions) {
25
28
  basePath: section.basePath ?? '/api/admin/jobs',
26
29
  icon: section.icon,
27
30
  permission,
31
+ group: section.group,
32
+ order: section.order,
28
33
  });
29
34
  }
30
35
  else if (section.kind === 'logs') {
@@ -35,6 +40,8 @@ export function resolveNav(manifest, permissions) {
35
40
  basePath: section.basePath ?? '/api/admin/logs',
36
41
  icon: section.icon,
37
42
  permission,
43
+ group: section.group,
44
+ order: section.order,
38
45
  });
39
46
  }
40
47
  else if (section.kind === 'env_migration') {
@@ -45,6 +52,8 @@ export function resolveNav(manifest, permissions) {
45
52
  basePath: section.basePath ?? '/api/admin',
46
53
  icon: section.icon,
47
54
  permission,
55
+ group: section.group,
56
+ order: section.order,
48
57
  });
49
58
  }
50
59
  else if (section.kind === 'health') {
@@ -55,6 +64,105 @@ export function resolveNav(manifest, permissions) {
55
64
  basePath: section.basePath ?? '/api/admin',
56
65
  icon: section.icon,
57
66
  permission,
67
+ group: section.group,
68
+ order: section.order,
69
+ });
70
+ }
71
+ else if (section.kind === 'masking') {
72
+ items.push({
73
+ kind: 'masking',
74
+ label: section.label ?? 'Masking Rules',
75
+ path: '/admin/masking',
76
+ basePath: section.basePath ?? '/api/admin',
77
+ icon: section.icon,
78
+ permission,
79
+ group: section.group,
80
+ order: section.order,
81
+ });
82
+ }
83
+ else if (section.kind === 'audit') {
84
+ items.push({
85
+ kind: 'audit',
86
+ label: section.label ?? 'Audit Log',
87
+ path: '/admin/audit',
88
+ basePath: section.basePath ?? '/api/admin',
89
+ icon: section.icon,
90
+ permission,
91
+ group: section.group,
92
+ order: section.order,
93
+ });
94
+ }
95
+ else if (section.kind === 'settings') {
96
+ items.push({
97
+ kind: 'settings',
98
+ label: section.label ?? 'Settings',
99
+ path: '/admin/settings',
100
+ basePath: section.basePath ?? '/api/admin',
101
+ icon: section.icon,
102
+ permission,
103
+ group: section.group,
104
+ order: section.order,
105
+ });
106
+ }
107
+ else if (section.kind === 'api_keys') {
108
+ items.push({
109
+ kind: 'api_keys',
110
+ label: section.label ?? 'API Keys',
111
+ path: '/admin/api-keys',
112
+ basePath: section.basePath ?? '/api/admin',
113
+ icon: section.icon,
114
+ permission,
115
+ group: section.group,
116
+ order: section.order,
117
+ });
118
+ }
119
+ else if (section.kind === 'blog') {
120
+ items.push({
121
+ kind: 'blog',
122
+ label: section.label ?? 'Blog',
123
+ path: '/admin/blog',
124
+ basePath: section.basePath ?? '/api/admin',
125
+ icon: section.icon,
126
+ permission,
127
+ group: section.group,
128
+ order: section.order,
129
+ });
130
+ }
131
+ else if (section.kind === 'files') {
132
+ items.push({
133
+ kind: 'files',
134
+ label: section.label ?? 'Files',
135
+ path: '/admin/files',
136
+ basePath: section.basePath ?? '/api/admin',
137
+ icon: section.icon,
138
+ permission,
139
+ group: section.group,
140
+ order: section.order,
141
+ });
142
+ }
143
+ else if (section.kind === 'feedback') {
144
+ items.push({
145
+ kind: 'feedback',
146
+ label: section.label ?? 'Feedback',
147
+ path: '/admin/feedback',
148
+ basePath: section.basePath ?? '/api/admin',
149
+ icon: section.icon,
150
+ permission,
151
+ group: section.group,
152
+ order: section.order,
153
+ });
154
+ }
155
+ else if (section.kind === 'metrics') {
156
+ items.push({
157
+ kind: 'metrics',
158
+ label: section.label ?? 'Metrics',
159
+ path: '/admin/analytics',
160
+ basePath: section.basePath ?? '/api/admin/metrics',
161
+ icon: section.icon,
162
+ permission,
163
+ component: section.component,
164
+ group: section.group,
165
+ order: section.order,
58
166
  });
59
167
  }
60
168
  else if (section.kind === 'custom') {
@@ -66,6 +174,8 @@ export function resolveNav(manifest, permissions) {
66
174
  icon: section.icon,
67
175
  permission,
68
176
  component: section.component,
177
+ group: section.group,
178
+ order: section.order,
69
179
  });
70
180
  }
71
181
  }
@@ -0,0 +1,5 @@
1
+ export interface ApiKeysPanelProps {
2
+ basePath?: string;
3
+ }
4
+ export declare function ApiKeysPanel({ basePath: _basePath }: ApiKeysPanelProps): import("react").JSX.Element;
5
+ //# sourceMappingURL=api_keys_panel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api_keys_panel.d.ts","sourceRoot":"","sources":["../../src/components/api_keys_panel.tsx"],"names":[],"mappings":"AAGA,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,YAAY,CAAC,EAAE,QAAQ,EAAE,SAAwB,EAAE,EAAE,iBAAiB,+BAqCrF"}
@@ -0,0 +1,21 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useEffect, useState } from 'react';
4
+ export function ApiKeysPanel({ basePath: _basePath = '/api/admin' }) {
5
+ const [notInstalled, setNotInstalled] = useState(false);
6
+ const [loading, setLoading] = useState(true);
7
+ useEffect(() => {
8
+ // Probe the CLIENT entry — the default `hazo_api` entry pulls server-only
9
+ // code that breaks this 'use client' component's browser bundle.
10
+ import('hazo_api/client').catch(() => null).then((pkg) => {
11
+ setNotInstalled(pkg === null);
12
+ setLoading(false);
13
+ });
14
+ }, []);
15
+ if (loading)
16
+ return _jsx("div", { className: "p-6 text-sm text-gray-400", children: "Loading\u2026" });
17
+ if (notInstalled) {
18
+ return (_jsxs("div", { className: "p-6 max-w-xl", children: [_jsx("h2", { className: "text-base font-semibold text-gray-800 mb-2", children: "API Keys" }), _jsxs("p", { className: "text-sm text-gray-500", children: [_jsx("code", { className: "text-xs bg-gray-100 px-1 rounded", children: "hazo_api" }), " is not installed. To enable API key management, install it and configure it in your application."] })] }));
19
+ }
20
+ return (_jsxs("div", { className: "p-6 max-w-xl", children: [_jsx("h2", { className: "text-base font-semibold text-gray-800 mb-2", children: "API Keys" }), _jsxs("p", { className: "text-sm text-gray-500", children: ["API Keys \u2014 manage application API keys", ' ', _jsxs("span", { className: "text-xs text-gray-400", children: ["(powered by ", _jsx("code", { className: "bg-gray-100 px-1 rounded", children: "hazo_api" }), ")"] })] }), _jsx("div", { className: "mt-4 text-sm text-gray-400 border rounded p-4 bg-gray-50", children: "Panel coming soon." })] }));
21
+ }
@@ -0,0 +1,5 @@
1
+ export interface AuditPanelProps {
2
+ basePath?: string;
3
+ }
4
+ export declare function AuditPanel({ basePath: _basePath }: AuditPanelProps): import("react").JSX.Element;
5
+ //# sourceMappingURL=audit_panel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit_panel.d.ts","sourceRoot":"","sources":["../../src/components/audit_panel.tsx"],"names":[],"mappings":"AAGA,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,UAAU,CAAC,EAAE,QAAQ,EAAE,SAAwB,EAAE,EAAE,eAAe,+BAmCjF"}
@@ -0,0 +1,19 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useEffect, useState } from 'react';
4
+ export function AuditPanel({ basePath: _basePath = '/api/admin' }) {
5
+ const [notInstalled, setNotInstalled] = useState(false);
6
+ const [loading, setLoading] = useState(true);
7
+ useEffect(() => {
8
+ import('hazo_audit').catch(() => null).then((pkg) => {
9
+ setNotInstalled(pkg === null);
10
+ setLoading(false);
11
+ });
12
+ }, []);
13
+ if (loading)
14
+ return _jsx("div", { className: "p-6 text-sm text-gray-400", children: "Loading\u2026" });
15
+ if (notInstalled) {
16
+ return (_jsxs("div", { className: "p-6 max-w-xl", children: [_jsx("h2", { className: "text-base font-semibold text-gray-800 mb-2", children: "Audit Log" }), _jsxs("p", { className: "text-sm text-gray-500", children: [_jsx("code", { className: "text-xs bg-gray-100 px-1 rounded", children: "hazo_audit" }), " is not installed. To enable audit logging, install it and configure it in your application."] })] }));
17
+ }
18
+ return (_jsxs("div", { className: "p-6 max-w-xl", children: [_jsx("h2", { className: "text-base font-semibold text-gray-800 mb-2", children: "Audit Log" }), _jsxs("p", { className: "text-sm text-gray-500", children: ["Audit log \u2014 view field-level audit events", ' ', _jsxs("span", { className: "text-xs text-gray-400", children: ["(powered by ", _jsx("code", { className: "bg-gray-100 px-1 rounded", children: "hazo_audit" }), ")"] })] }), _jsx("div", { className: "mt-4 text-sm text-gray-400 border rounded p-4 bg-gray-50", children: "Panel coming soon." })] }));
19
+ }
@@ -0,0 +1,5 @@
1
+ export interface BlogPanelProps {
2
+ basePath?: string;
3
+ }
4
+ export declare function BlogPanel({ basePath: _basePath }: BlogPanelProps): import("react").JSX.Element;
5
+ //# sourceMappingURL=blog_panel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blog_panel.d.ts","sourceRoot":"","sources":["../../src/components/blog_panel.tsx"],"names":[],"mappings":"AAGA,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,SAAS,CAAC,EAAE,QAAQ,EAAE,SAAwB,EAAE,EAAE,cAAc,+BAuC/E"}
@@ -0,0 +1,23 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useEffect, useState } from 'react';
4
+ export function BlogPanel({ basePath: _basePath = '/api/admin' }) {
5
+ const [notInstalled, setNotInstalled] = useState(false);
6
+ const [loading, setLoading] = useState(true);
7
+ useEffect(() => {
8
+ // Probe the CLIENT entry, not the default `hazo_blog` entry. The default is
9
+ // server-only; importing it from this 'use client' component would drag
10
+ // server-only code into the browser bundle and crash the route with
11
+ // "react-dom/server is not supported in React Server Components".
12
+ import('hazo_blog/client').catch(() => null).then((pkg) => {
13
+ setNotInstalled(pkg === null);
14
+ setLoading(false);
15
+ });
16
+ }, []);
17
+ if (loading)
18
+ return _jsx("div", { className: "p-6 text-sm text-gray-400", children: "Loading\u2026" });
19
+ if (notInstalled) {
20
+ return (_jsxs("div", { className: "p-6 max-w-xl", children: [_jsx("h2", { className: "text-base font-semibold text-gray-800 mb-2", children: "Blog" }), _jsxs("p", { className: "text-sm text-gray-500", children: [_jsx("code", { className: "text-xs bg-gray-100 px-1 rounded", children: "hazo_blog" }), " is not installed. To enable blog management, install it and configure it in your application."] })] }));
21
+ }
22
+ return (_jsxs("div", { className: "p-6 max-w-xl", children: [_jsx("h2", { className: "text-base font-semibold text-gray-800 mb-2", children: "Blog" }), _jsxs("p", { className: "text-sm text-gray-500", children: ["Blog \u2014 manage posts, categories, and tags", ' ', _jsxs("span", { className: "text-xs text-gray-400", children: ["(powered by ", _jsx("code", { className: "bg-gray-100 px-1 rounded", children: "hazo_blog" }), ")"] })] }), _jsx("div", { className: "mt-4 text-sm text-gray-400 border rounded p-4 bg-gray-50", children: "Panel coming soon." })] }));
23
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"env_migration_panel.d.ts","sourceRoot":"","sources":["../../src/components/env_migration_panel.tsx"],"names":[],"mappings":"AACA,OAAO,KAAsC,MAAM,OAAO,CAAC;AAE3D,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AA+CD,wBAAgB,iBAAiB,CAAC,EAAE,QAAuB,EAAE,EAAE,sBAAsB,qBA4dpF"}
1
+ {"version":3,"file":"env_migration_panel.d.ts","sourceRoot":"","sources":["../../src/components/env_migration_panel.tsx"],"names":[],"mappings":"AACA,OAAO,KAAsC,MAAM,OAAO,CAAC;AAE3D,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAkDD,wBAAgB,iBAAiB,CAAC,EAAE,QAAuB,EAAE,EAAE,sBAAsB,qBA+dpF"}
@@ -15,7 +15,6 @@ function normalizeResult(result) {
15
15
  }
16
16
  return result;
17
17
  }
18
- const PROD_ROLES = ['prod', 'production'];
19
18
  export function EnvMigrationPanel({ basePath = '/api/admin' }) {
20
19
  // Env list
21
20
  const [envs, setEnvs] = useState([]);
@@ -52,9 +51,9 @@ export function EnvMigrationPanel({ basePath = '/api/admin' }) {
52
51
  if (Array.isArray(data)) {
53
52
  setEnvs(data);
54
53
  if (data.length > 0)
55
- setFrom(data[0]);
54
+ setFrom(data[0].name);
56
55
  if (data.length > 1)
57
- setTo(data[1]);
56
+ setTo(data[1].name);
58
57
  }
59
58
  else {
60
59
  setEnvsError('Failed to parse env list');
@@ -122,8 +121,7 @@ export function EnvMigrationPanel({ basePath = '/api/admin' }) {
122
121
  setJob(null);
123
122
  setRestoreMsg(null);
124
123
  setDetailsOpen(true);
125
- const isProd = PROD_ROLES.includes(to);
126
- if (isProd && prodConfirm !== to)
124
+ if (isProdTarget && prodConfirm !== to)
127
125
  return;
128
126
  const payload = {
129
127
  from,
@@ -135,7 +133,7 @@ export function EnvMigrationPanel({ basePath = '/api/admin' }) {
135
133
  tables,
136
134
  dryRun,
137
135
  };
138
- if (isProd) {
136
+ if (isProdTarget) {
139
137
  payload.allowProdTarget = true;
140
138
  payload.confirmToken = prodConfirm;
141
139
  }
@@ -181,10 +179,10 @@ export function EnvMigrationPanel({ basePath = '/api/admin' }) {
181
179
  setRestoreMsg(`Restore failed: ${err instanceof Error ? err.message : 'Network error'}`);
182
180
  }
183
181
  }
184
- const isProdTarget = PROD_ROLES.includes(to);
182
+ const isProdTarget = envs.find(e => e.name === to)?.role === 'production';
185
183
  const prodConfirmValid = !isProdTarget || prodConfirm === to;
186
184
  const canRun = !running && prodConfirmValid && !!from && !!to && from !== to;
187
- return (_jsxs("div", { className: "p-6 max-w-2xl space-y-6", children: [_jsx("h2", { className: "text-base font-semibold text-gray-800", children: "Env Migration" }), envsError && (_jsx("div", { className: "text-sm text-red-500", children: envsError })), _jsxs("form", { onSubmit: handleSubmit, className: "space-y-4", children: [_jsxs("div", { className: "flex gap-4", children: [_jsxs("div", { className: "flex-1", children: [_jsx("label", { className: "block text-xs font-medium text-gray-600 mb-1", children: "From" }), _jsx("select", { value: from, onChange: e => setFrom(e.target.value), className: "w-full border border-gray-300 rounded px-2 py-1.5 text-sm", disabled: running, children: envs.map(env => (_jsx("option", { value: env, children: env }, env))) })] }), _jsxs("div", { className: "flex-1", children: [_jsx("label", { className: "block text-xs font-medium text-gray-600 mb-1", children: "To" }), _jsx("select", { value: to, onChange: e => { setTo(e.target.value); setProdConfirm(''); }, className: "w-full border border-gray-300 rounded px-2 py-1.5 text-sm", disabled: running, children: envs.map(env => (_jsx("option", { value: env, children: env }, env))) })] })] }), _jsxs("div", { className: "flex gap-6", children: [_jsxs("label", { className: "flex items-center gap-2 text-sm text-gray-700", children: [_jsx("input", { type: "checkbox", checked: includeDb, onChange: e => setIncludeDb(e.target.checked), disabled: running }), "Include DB"] }), _jsxs("label", { className: "flex items-center gap-2 text-sm text-gray-700", children: [_jsx("input", { type: "checkbox", checked: includeFiles, onChange: e => setIncludeFiles(e.target.checked), disabled: running }), "Include Files"] }), _jsxs("label", { className: "flex items-center gap-2 text-sm text-gray-700", children: [_jsx("input", { type: "checkbox", checked: dryRun, onChange: e => setDryRun(e.target.checked), disabled: running }), "Dry run"] })] }), _jsxs("div", { children: [_jsx("label", { className: "block text-xs font-medium text-gray-600 mb-1", children: "Transport" }), _jsxs("select", { value: transport, onChange: e => setTransport(e.target.value), className: "border border-gray-300 rounded px-2 py-1.5 text-sm", disabled: running, children: [_jsx("option", { value: "auto", children: "auto" }), _jsx("option", { value: "local", children: "local" }), _jsx("option", { value: "api", children: "api" }), _jsx("option", { value: "ssh", disabled: true, children: "ssh (coming soon)" })] })] }), _jsxs("div", { children: [_jsx("label", { className: "block text-xs font-medium text-gray-600 mb-1", children: "Scrub (PII masking)" }), _jsxs("select", { value: scrub, onChange: e => setScrub(e.target.value), className: "border border-gray-300 rounded px-2 py-1.5 text-sm", disabled: running, children: [_jsx("option", { value: "none", children: "none" }), _jsx("option", { value: "auto", children: "auto" })] })] }), _jsxs("div", { children: [_jsx("label", { className: "block text-xs font-medium text-gray-600 mb-1", children: "Tables (glob, * = all)" }), _jsx("input", { type: "text", value: tables, onChange: e => setTables(e.target.value), className: "w-full border border-gray-300 rounded px-2 py-1.5 text-sm", disabled: running })] }), isProdTarget && (_jsxs("div", { className: "border border-red-300 rounded p-3 space-y-2 bg-red-50", children: [_jsx("div", { className: "text-sm font-medium text-red-700", children: "Warning: you are targeting a production environment." }), _jsxs("label", { className: "block text-xs font-medium text-red-600 mb-1", children: ["Type ", _jsx("code", { className: "font-mono bg-red-100 px-1 rounded", children: to }), " to confirm"] }), _jsx("input", { type: "text", value: prodConfirm, onChange: e => setProdConfirm(e.target.value), placeholder: to, className: "w-full border border-red-300 rounded px-2 py-1.5 text-sm", disabled: running })] })), _jsx("div", { children: _jsx("button", { type: "submit", disabled: !canRun, className: "px-4 py-2 bg-blue-600 text-white text-sm font-medium rounded hover:bg-blue-700 disabled:opacity-50 disabled:cursor-not-allowed", children: running ? 'Running…' : dryRun ? 'Run dry run' : 'Run migration' }) })] }), running && progress && (_jsxs("div", { className: "space-y-2", children: [_jsx("div", { className: "text-xs text-gray-500 uppercase tracking-wide font-medium", children: "Progress" }), _jsxs("div", { className: "text-sm text-gray-700", children: [progress.phase, ": ", progress.message] }), _jsx("div", { className: "w-full bg-gray-200 rounded h-2", children: _jsx("div", { className: "bg-blue-500 h-2 rounded transition-all", style: { width: `${Math.max(0, Math.min(100, progress.percent))}%` } }) }), _jsxs("div", { className: "text-xs text-gray-500", children: [progress.percent, "%"] })] })), submitError && (_jsx("div", { className: "text-sm text-red-600 border border-red-200 rounded p-3 bg-red-50", children: submitError })), job?.status === 'completed' && (() => {
185
+ return (_jsxs("div", { className: "p-6 max-w-2xl space-y-6", children: [_jsx("h2", { className: "text-base font-semibold text-gray-800", children: "Env Migration" }), envsError && (_jsx("div", { className: "text-sm text-red-500", children: envsError })), _jsxs("form", { onSubmit: handleSubmit, className: "space-y-4", children: [_jsxs("div", { className: "flex gap-4", children: [_jsxs("div", { className: "flex-1", children: [_jsx("label", { className: "block text-xs font-medium text-gray-600 mb-1", children: "From" }), _jsx("select", { value: from, onChange: e => setFrom(e.target.value), className: "w-full border border-gray-300 rounded px-2 py-1.5 text-sm", disabled: running, children: envs.map(env => (_jsxs("option", { value: env.name, children: [env.name, " ", env.role !== 'production' ? `(${env.role})` : '(PRODUCTION)'] }, env.name))) })] }), _jsxs("div", { className: "flex-1", children: [_jsx("label", { className: "block text-xs font-medium text-gray-600 mb-1", children: "To" }), _jsx("select", { value: to, onChange: e => { setTo(e.target.value); setProdConfirm(''); }, className: "w-full border border-gray-300 rounded px-2 py-1.5 text-sm", disabled: running, children: envs.map(env => (_jsxs("option", { value: env.name, children: [env.name, " ", env.role !== 'production' ? `(${env.role})` : '(PRODUCTION)'] }, env.name))) })] })] }), _jsxs("div", { className: "flex gap-6", children: [_jsxs("label", { className: "flex items-center gap-2 text-sm text-gray-700", children: [_jsx("input", { type: "checkbox", checked: includeDb, onChange: e => setIncludeDb(e.target.checked), disabled: running }), "Include DB"] }), _jsxs("label", { className: "flex items-center gap-2 text-sm text-gray-700", children: [_jsx("input", { type: "checkbox", checked: includeFiles, onChange: e => setIncludeFiles(e.target.checked), disabled: running }), "Include Files"] }), _jsxs("label", { className: "flex items-center gap-2 text-sm text-gray-700", children: [_jsx("input", { type: "checkbox", checked: dryRun, onChange: e => setDryRun(e.target.checked), disabled: running }), "Dry run"] })] }), _jsxs("div", { children: [_jsx("label", { className: "block text-xs font-medium text-gray-600 mb-1", children: "Transport" }), _jsxs("select", { value: transport, onChange: e => setTransport(e.target.value), className: "border border-gray-300 rounded px-2 py-1.5 text-sm", disabled: running, children: [_jsx("option", { value: "auto", children: "auto" }), _jsx("option", { value: "local", children: "local" }), _jsx("option", { value: "api", children: "api" }), _jsx("option", { value: "ssh", disabled: true, children: "ssh (coming soon)" })] })] }), _jsxs("div", { children: [_jsx("label", { className: "block text-xs font-medium text-gray-600 mb-1", children: "Scrub (PII masking)" }), _jsxs("select", { value: scrub, onChange: e => setScrub(e.target.value), className: "border border-gray-300 rounded px-2 py-1.5 text-sm", disabled: running, children: [_jsx("option", { value: "none", children: "none" }), _jsx("option", { value: "auto", children: "auto" })] })] }), _jsxs("div", { children: [_jsx("label", { className: "block text-xs font-medium text-gray-600 mb-1", children: "Tables (glob, * = all)" }), _jsx("input", { type: "text", value: tables, onChange: e => setTables(e.target.value), className: "w-full border border-gray-300 rounded px-2 py-1.5 text-sm", disabled: running })] }), isProdTarget && (_jsxs("div", { className: "border border-red-300 rounded p-3 space-y-2 bg-red-50", children: [_jsx("div", { className: "text-sm font-medium text-red-700", children: "Warning: you are targeting a production environment." }), _jsxs("label", { className: "block text-xs font-medium text-red-600 mb-1", children: ["Type ", _jsx("code", { className: "font-mono bg-red-100 px-1 rounded", children: to }), " to confirm"] }), _jsx("input", { type: "text", value: prodConfirm, onChange: e => setProdConfirm(e.target.value), placeholder: to, className: "w-full border border-red-300 rounded px-2 py-1.5 text-sm", disabled: running })] })), _jsx("div", { children: _jsx("button", { type: "submit", disabled: !canRun, className: "px-4 py-2 bg-blue-600 text-white text-sm font-medium rounded hover:bg-blue-700 disabled:opacity-50 disabled:cursor-not-allowed", children: running ? 'Running…' : dryRun ? 'Run dry run' : 'Run migration' }) })] }), running && progress && (_jsxs("div", { className: "space-y-2", children: [_jsx("div", { className: "text-xs text-gray-500 uppercase tracking-wide font-medium", children: "Progress" }), _jsxs("div", { className: "text-sm text-gray-700", children: [progress.phase, ": ", progress.message] }), _jsx("div", { className: "w-full bg-gray-200 rounded h-2", children: _jsx("div", { className: "bg-blue-500 h-2 rounded transition-all", style: { width: `${Math.max(0, Math.min(100, progress.percent))}%` } }) }), _jsxs("div", { className: "text-xs text-gray-500", children: [progress.percent, "%"] })] })), submitError && (_jsx("div", { className: "text-sm text-red-600 border border-red-200 rounded p-3 bg-red-50", children: submitError })), job?.status === 'completed' && (() => {
188
186
  const result = normalizeResult(job.result);
189
187
  const db = result?.db;
190
188
  const files = result?.files;
@@ -0,0 +1,5 @@
1
+ export interface FeedbackPanelProps {
2
+ basePath?: string;
3
+ }
4
+ export declare function FeedbackPanel({ basePath: _basePath }: FeedbackPanelProps): import("react").JSX.Element;
5
+ //# sourceMappingURL=feedback_panel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"feedback_panel.d.ts","sourceRoot":"","sources":["../../src/components/feedback_panel.tsx"],"names":[],"mappings":"AAGA,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,aAAa,CAAC,EAAE,QAAQ,EAAE,SAAwB,EAAE,EAAE,kBAAkB,+BAwCvF"}
@@ -0,0 +1,24 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useEffect, useState } from 'react';
4
+ export function FeedbackPanel({ basePath: _basePath = '/api/admin' }) {
5
+ const [notInstalled, setNotInstalled] = useState(false);
6
+ const [loading, setLoading] = useState(true);
7
+ useEffect(() => {
8
+ // Probe the CLIENT entry, not the default `hazo_feedback` entry. The default
9
+ // is server-only (`import 'server-only'` + the server factory → hazo_auth's
10
+ // next/headers chain); importing it from this 'use client' component drags
11
+ // server-only code into the browser bundle and crashes the whole route with
12
+ // "react-dom/server is not supported in React Server Components".
13
+ import('hazo_feedback/client').catch(() => null).then((pkg) => {
14
+ setNotInstalled(pkg === null);
15
+ setLoading(false);
16
+ });
17
+ }, []);
18
+ if (loading)
19
+ return _jsx("div", { className: "p-6 text-sm text-gray-400", children: "Loading\u2026" });
20
+ if (notInstalled) {
21
+ return (_jsxs("div", { className: "p-6 max-w-xl", children: [_jsx("h2", { className: "text-base font-semibold text-gray-800 mb-2", children: "Feedback" }), _jsxs("p", { className: "text-sm text-gray-500", children: [_jsx("code", { className: "text-xs bg-gray-100 px-1 rounded", children: "hazo_feedback" }), " is not installed. To enable feedback review, install it and configure it in your application."] })] }));
22
+ }
23
+ return (_jsxs("div", { className: "p-6 max-w-xl", children: [_jsx("h2", { className: "text-base font-semibold text-gray-800 mb-2", children: "Feedback" }), _jsxs("p", { className: "text-sm text-gray-500", children: ["Feedback \u2014 review in-app feedback submissions", ' ', _jsxs("span", { className: "text-xs text-gray-400", children: ["(powered by ", _jsx("code", { className: "bg-gray-100 px-1 rounded", children: "hazo_feedback" }), ")"] })] }), _jsx("div", { className: "mt-4 text-sm text-gray-400 border rounded p-4 bg-gray-50", children: "Panel coming soon." })] }));
24
+ }
@@ -0,0 +1,5 @@
1
+ export interface FilesPanelProps {
2
+ basePath?: string;
3
+ }
4
+ export declare function FilesPanel({ basePath: _basePath }: FilesPanelProps): import("react").JSX.Element;
5
+ //# sourceMappingURL=files_panel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"files_panel.d.ts","sourceRoot":"","sources":["../../src/components/files_panel.tsx"],"names":[],"mappings":"AAGA,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,UAAU,CAAC,EAAE,QAAQ,EAAE,SAAwB,EAAE,EAAE,eAAe,+BAsCjF"}
@@ -0,0 +1,22 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useEffect, useState } from 'react';
4
+ export function FilesPanel({ basePath: _basePath = '/api/admin' }) {
5
+ const [notInstalled, setNotInstalled] = useState(false);
6
+ const [loading, setLoading] = useState(true);
7
+ useEffect(() => {
8
+ // Probe the client-safe UI entry — the default `hazo_files` entry pulls
9
+ // googleapis / google-auth-library (Node-only) into this 'use client'
10
+ // component's browser bundle, which crashes the route.
11
+ import('hazo_files/ui').catch(() => null).then((pkg) => {
12
+ setNotInstalled(pkg === null);
13
+ setLoading(false);
14
+ });
15
+ }, []);
16
+ if (loading)
17
+ return _jsx("div", { className: "p-6 text-sm text-gray-400", children: "Loading\u2026" });
18
+ if (notInstalled) {
19
+ return (_jsxs("div", { className: "p-6 max-w-xl", children: [_jsx("h2", { className: "text-base font-semibold text-gray-800 mb-2", children: "Files" }), _jsxs("p", { className: "text-sm text-gray-500", children: [_jsx("code", { className: "text-xs bg-gray-100 px-1 rounded", children: "hazo_files" }), " is not installed. To enable file management, install it and configure it in your application."] })] }));
20
+ }
21
+ return (_jsxs("div", { className: "p-6 max-w-xl", children: [_jsx("h2", { className: "text-base font-semibold text-gray-800 mb-2", children: "Files" }), _jsxs("p", { className: "text-sm text-gray-500", children: ["Files \u2014 browse and manage uploaded files", ' ', _jsxs("span", { className: "text-xs text-gray-400", children: ["(powered by ", _jsx("code", { className: "bg-gray-100 px-1 rounded", children: "hazo_files" }), ")"] })] }), _jsx("div", { className: "mt-4 text-sm text-gray-400 border rounded p-4 bg-gray-50", children: "Panel coming soon." })] }));
22
+ }
@@ -0,0 +1,5 @@
1
+ export interface MaskingRulesPanelProps {
2
+ basePath?: string;
3
+ }
4
+ export declare function MaskingRulesPanel({ basePath }: MaskingRulesPanelProps): import("react").JSX.Element | null;
5
+ //# sourceMappingURL=masking_panel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"masking_panel.d.ts","sourceRoot":"","sources":["../../src/components/masking_panel.tsx"],"names":[],"mappings":"AAGA,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AASD,wBAAgB,iBAAiB,CAAC,EAAE,QAAuB,EAAE,EAAE,sBAAsB,sCA+GpF"}
@@ -0,0 +1,65 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useEffect, useState } from 'react';
4
+ export function MaskingRulesPanel({ basePath = '/api/admin' }) {
5
+ const [rules, setRules] = useState(null);
6
+ const [error, setError] = useState(null);
7
+ const [loading, setLoading] = useState(true);
8
+ const [seeding, setSeeding] = useState(false);
9
+ const [seedMessage, setSeedMessage] = useState(null);
10
+ const [seedError, setSeedError] = useState(false);
11
+ function fetchRules() {
12
+ setLoading(true);
13
+ setError(null);
14
+ fetch(`${basePath}/mask/rules`, { credentials: 'include' })
15
+ .then(async (r) => {
16
+ if (r.status === 503) {
17
+ const body = await r.json().catch(() => ({}));
18
+ throw new Error(body?.error?.reason ?? 'hazo_env not installed');
19
+ }
20
+ if (!r.ok)
21
+ throw new Error(`Failed to load masking rules (${r.status})`);
22
+ return r.json();
23
+ })
24
+ .then(setRules)
25
+ .catch((e) => setError(e instanceof Error ? e.message : 'Failed to load masking rules'))
26
+ .finally(() => setLoading(false));
27
+ }
28
+ useEffect(() => {
29
+ fetchRules();
30
+ // eslint-disable-next-line react-hooks/exhaustive-deps
31
+ }, [basePath]);
32
+ async function handleSeedFromIni() {
33
+ setSeeding(true);
34
+ setSeedMessage(null);
35
+ try {
36
+ const r = await fetch(`${basePath}/mask/seed-from-ini`, {
37
+ method: 'POST',
38
+ credentials: 'include',
39
+ });
40
+ if (!r.ok) {
41
+ const body = await r.json().catch(() => ({}));
42
+ throw new Error(body?.error?.reason ?? `Seed failed (${r.status})`);
43
+ }
44
+ setSeedMessage('Rules seeded successfully.');
45
+ setSeedError(false);
46
+ fetchRules();
47
+ }
48
+ catch (e) {
49
+ setSeedMessage(e instanceof Error ? e.message : 'Seed failed');
50
+ setSeedError(true);
51
+ }
52
+ finally {
53
+ setSeeding(false);
54
+ }
55
+ }
56
+ if (loading)
57
+ return _jsx("div", { className: "p-6 text-sm text-gray-400", children: "Loading masking rules\u2026" });
58
+ if (error)
59
+ return _jsx("div", { className: "p-6 text-sm text-gray-500", children: error });
60
+ if (!rules)
61
+ return null;
62
+ return (_jsxs("div", { className: "p-6 max-w-3xl", children: [_jsxs("div", { className: "flex items-center justify-between mb-4", children: [_jsx("h2", { className: "text-base font-semibold text-gray-800", children: "Masking Rules" }), _jsx("button", { onClick: handleSeedFromIni, disabled: seeding, className: "px-3 py-1.5 text-sm font-medium rounded border border-gray-300 bg-white hover:bg-gray-50 disabled:opacity-50 disabled:cursor-not-allowed", children: seeding ? 'Seeding…' : 'Seed from INI' })] }), seedMessage && (_jsx("div", { className: `mb-4 text-sm border rounded p-2 ${seedError
63
+ ? 'bg-destructive/10 text-destructive'
64
+ : 'bg-green-50 text-green-800'}`, children: seedMessage })), rules.length === 0 ? (_jsxs("div", { className: "text-sm text-gray-500", children: ["No masking rules configured. Use \"Seed from INI\" to load rules from", ' ', _jsx("code", { className: "text-xs bg-gray-100 px-1 rounded", children: "hazo_env_masking.ini" }), "."] })) : (_jsx("div", { className: "border rounded overflow-hidden", children: _jsxs("table", { className: "w-full text-sm", children: [_jsx("thead", { children: _jsxs("tr", { className: "bg-gray-50 border-b", children: [_jsx("th", { className: "text-left px-4 py-2 font-medium text-gray-700", children: "Table" }), _jsx("th", { className: "text-left px-4 py-2 font-medium text-gray-700", children: "Column" }), _jsx("th", { className: "text-left px-4 py-2 font-medium text-gray-700", children: "Transform" })] }) }), _jsx("tbody", { children: rules.map((rule, i) => (_jsxs("tr", { className: "border-b last:border-0", children: [_jsx("td", { className: "px-4 py-2 font-mono text-xs text-gray-800", children: rule.table }), _jsx("td", { className: "px-4 py-2 font-mono text-xs text-gray-800", children: rule.column }), _jsx("td", { className: "px-4 py-2 text-gray-600", children: rule.transform })] }, i))) })] }) }))] }));
65
+ }
@@ -0,0 +1,5 @@
1
+ export interface SettingsPanelProps {
2
+ basePath?: string;
3
+ }
4
+ export declare function SettingsPanel({ basePath: _basePath }: SettingsPanelProps): import("react").JSX.Element;
5
+ //# sourceMappingURL=settings_panel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"settings_panel.d.ts","sourceRoot":"","sources":["../../src/components/settings_panel.tsx"],"names":[],"mappings":"AAGA,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,aAAa,CAAC,EAAE,QAAQ,EAAE,SAAwB,EAAE,EAAE,kBAAkB,+BAmCvF"}
@@ -0,0 +1,19 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useEffect, useState } from 'react';
4
+ export function SettingsPanel({ basePath: _basePath = '/api/admin' }) {
5
+ const [notInstalled, setNotInstalled] = useState(false);
6
+ const [loading, setLoading] = useState(true);
7
+ useEffect(() => {
8
+ import('hazo_config').catch(() => null).then((pkg) => {
9
+ setNotInstalled(pkg === null);
10
+ setLoading(false);
11
+ });
12
+ }, []);
13
+ if (loading)
14
+ return _jsx("div", { className: "p-6 text-sm text-gray-400", children: "Loading\u2026" });
15
+ if (notInstalled) {
16
+ return (_jsxs("div", { className: "p-6 max-w-xl", children: [_jsx("h2", { className: "text-base font-semibold text-gray-800 mb-2", children: "Settings" }), _jsxs("p", { className: "text-sm text-gray-500", children: [_jsx("code", { className: "text-xs bg-gray-100 px-1 rounded", children: "hazo_config" }), " is not installed. To enable settings management, install it and configure it in your application."] })] }));
17
+ }
18
+ return (_jsxs("div", { className: "p-6 max-w-xl", children: [_jsx("h2", { className: "text-base font-semibold text-gray-800 mb-2", children: "Settings" }), _jsxs("p", { className: "text-sm text-gray-500", children: ["Settings \u2014 manage configuration keys per environment", ' ', _jsxs("span", { className: "text-xs text-gray-400", children: ["(powered by ", _jsx("code", { className: "bg-gray-100 px-1 rounded", children: "hazo_config" }), ")"] })] }), _jsx("div", { className: "mt-4 text-sm text-gray-400 border rounded p-4 bg-gray-50", children: "Panel coming soon." })] }));
19
+ }
@@ -2,6 +2,14 @@ export declare const HAZO_ADMIN_PERMISSIONS: {
2
2
  readonly ADMIN_SYSTEM: "admin_system";
3
3
  readonly ENV_MIGRATE: "env.migrate";
4
4
  readonly ENV_MASK_MANAGE: "env.mask.manage";
5
+ readonly AUDIT_READ: "audit.read";
6
+ readonly SETTINGS_MANAGE: "admin.settings.manage";
7
+ readonly APIKEYS_MANAGE: "admin.apikeys.manage";
8
+ readonly BLOG_MANAGE: "admin.blog.manage";
9
+ readonly FILES_MANAGE: "admin.files.manage";
10
+ readonly FEEDBACK_REVIEW: "admin.feedback.review";
11
+ readonly METRICS_VIEW: "metrics.view";
12
+ readonly METRICS_MANAGE: "metrics.manage";
5
13
  };
6
14
  export type HazoAdminPermissionKey = keyof typeof HAZO_ADMIN_PERMISSIONS;
7
15
  export type HazoAdminPermission = (typeof HAZO_ADMIN_PERMISSIONS)[HazoAdminPermissionKey];
@@ -1 +1 @@
1
- {"version":3,"file":"index.client.d.ts","sourceRoot":"","sources":["../src/index.client.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,sBAAsB;;;;CAIzB,CAAC;AAEX,MAAM,MAAM,sBAAsB,GAAG,MAAM,OAAO,sBAAsB,CAAC;AACzE,MAAM,MAAM,mBAAmB,GAAG,CAAC,OAAO,sBAAsB,CAAC,CAAC,sBAAsB,CAAC,CAAC"}
1
+ {"version":3,"file":"index.client.d.ts","sourceRoot":"","sources":["../src/index.client.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,sBAAsB;;;;;;;;;;;;CAYzB,CAAC;AAEX,MAAM,MAAM,sBAAsB,GAAG,MAAM,OAAO,sBAAsB,CAAC;AACzE,MAAM,MAAM,mBAAmB,GAAG,CAAC,OAAO,sBAAsB,CAAC,CAAC,sBAAsB,CAAC,CAAC"}
@@ -2,4 +2,12 @@ export const HAZO_ADMIN_PERMISSIONS = {
2
2
  ADMIN_SYSTEM: 'admin_system',
3
3
  ENV_MIGRATE: 'env.migrate',
4
4
  ENV_MASK_MANAGE: 'env.mask.manage',
5
+ AUDIT_READ: 'audit.read',
6
+ SETTINGS_MANAGE: 'admin.settings.manage',
7
+ APIKEYS_MANAGE: 'admin.apikeys.manage',
8
+ BLOG_MANAGE: 'admin.blog.manage',
9
+ FILES_MANAGE: 'admin.files.manage',
10
+ FEEDBACK_REVIEW: 'admin.feedback.review',
11
+ METRICS_VIEW: 'metrics.view',
12
+ METRICS_MANAGE: 'metrics.manage',
5
13
  };
@@ -14,4 +14,6 @@ export { HealthPanel } from './components/health_panel.js';
14
14
  export type { HealthPanelProps } from './components/health_panel.js';
15
15
  export type { AdminManifest, AdminNavSection, AdminNavItem } from './components/admin_nav.js';
16
16
  export { resolveNav } from './components/admin_nav.js';
17
+ export type { AdminKindDef } from './components/admin_kinds.js';
18
+ export { ADMIN_KINDS, getKindDef, getDefaultPermission, getDefaultIcon } from './components/admin_kinds.js';
17
19
  //# sourceMappingURL=index.ui.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.ui.d.ts","sourceRoot":"","sources":["../src/index.ui.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAC3D,YAAY,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AACrE,OAAO,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AACrD,YAAY,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AACzD,YAAY,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AACnE,OAAO,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AACvD,YAAY,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AACjE,OAAO,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AACvD,YAAY,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AACjE,OAAO,EAAE,iBAAiB,EAAE,MAAM,qCAAqC,CAAC;AACxE,YAAY,EAAE,sBAAsB,EAAE,MAAM,qCAAqC,CAAC;AAClF,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAC3D,YAAY,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AACrE,YAAY,EAAE,aAAa,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAC9F,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC"}
1
+ {"version":3,"file":"index.ui.d.ts","sourceRoot":"","sources":["../src/index.ui.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAC3D,YAAY,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AACrE,OAAO,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AACrD,YAAY,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AACzD,YAAY,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AACnE,OAAO,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AACvD,YAAY,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AACjE,OAAO,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AACvD,YAAY,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AACjE,OAAO,EAAE,iBAAiB,EAAE,MAAM,qCAAqC,CAAC;AACxE,YAAY,EAAE,sBAAsB,EAAE,MAAM,qCAAqC,CAAC;AAClF,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAC3D,YAAY,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AACrE,YAAY,EAAE,aAAa,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAC9F,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AACvD,YAAY,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAChE,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,oBAAoB,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC"}
package/dist/index.ui.js CHANGED
@@ -7,3 +7,4 @@ export { LogsPanel } from './components/logs_panel.js';
7
7
  export { EnvMigrationPanel } from './components/env_migration_panel.js';
8
8
  export { HealthPanel } from './components/health_panel.js';
9
9
  export { resolveNav } from './components/admin_nav.js';
10
+ export { ADMIN_KINDS, getKindDef, getDefaultPermission, getDefaultIcon } from './components/admin_kinds.js';
@@ -1 +1 @@
1
- {"version":3,"file":"page_factory.d.ts","sourceRoot":"","sources":["../../src/preset/page_factory.tsx"],"names":[],"mappings":"AAAA,OAAO,aAAa,CAAC;AAGrB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAEhE,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAE/D,MAAM,MAAM,qBAAqB,GAAG,IAAI,CAAC,uBAAuB,EAAE,gBAAgB,CAAC,GAAG;IACpF,cAAc,CAAC,EAAE,MAAM,OAAO,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC;CAC3C,CAAC;AAoBF,wBAAgB,eAAe,CAAC,QAAQ,EAAE,aAAa,EAAE,IAAI,CAAC,EAAE,qBAAqB,IACxD,YAAY;IAAE,MAAM,EAAE,OAAO,CAAC;QAAE,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC,CAAA;CAAE,0CAkDhF"}
1
+ {"version":3,"file":"page_factory.d.ts","sourceRoot":"","sources":["../../src/preset/page_factory.tsx"],"names":[],"mappings":"AAAA,OAAO,aAAa,CAAC;AAGrB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAGhE,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAK/D,MAAM,MAAM,qBAAqB,GAAG,IAAI,CAAC,uBAAuB,EAAE,gBAAgB,CAAC,GAAG;IACpF,cAAc,CAAC,EAAE,MAAM,OAAO,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC;CAC3C,CAAC;AAoBF,wBAAgB,eAAe,CAAC,QAAQ,EAAE,aAAa,EAAE,IAAI,CAAC,EAAE,qBAAqB,IACxD,YAAY;IAAE,MAAM,EAAE,OAAO,CAAC;QAAE,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC,CAAA;CAAE,0CAgDhF"}