@augmenting-integrations/ui 4.0.2 → 4.1.3
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.
- package/dist/components/admin/AppRegistryForm.cjs +198 -0
- package/dist/components/admin/AppRegistryForm.cjs.map +1 -0
- package/dist/components/admin/AppRegistryForm.d.ts +24 -0
- package/dist/components/admin/AppRegistryForm.d.ts.map +1 -0
- package/dist/components/admin/AppRegistryForm.js +164 -0
- package/dist/components/admin/AppRegistryForm.js.map +1 -0
- package/dist/components/shells/AppShell.cjs +45 -1
- package/dist/components/shells/AppShell.cjs.map +1 -1
- package/dist/components/shells/AppShell.d.ts +14 -1
- package/dist/components/shells/AppShell.d.ts.map +1 -1
- package/dist/components/shells/AppShell.js +46 -2
- package/dist/components/shells/AppShell.js.map +1 -1
- package/dist/index.cjs +3 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/package.json +13 -13
- package/LICENSE +0 -21
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
"use client";
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __export = (target, all) => {
|
|
10
|
+
for (var name in all)
|
|
11
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
12
|
+
};
|
|
13
|
+
var __copyProps = (to, from, except, desc) => {
|
|
14
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
15
|
+
for (let key of __getOwnPropNames(from))
|
|
16
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
17
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
18
|
+
}
|
|
19
|
+
return to;
|
|
20
|
+
};
|
|
21
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
22
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
23
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
24
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
25
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
26
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
27
|
+
mod
|
|
28
|
+
));
|
|
29
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
30
|
+
var AppRegistryForm_exports = {};
|
|
31
|
+
__export(AppRegistryForm_exports, {
|
|
32
|
+
AppRegistryForm: () => AppRegistryForm
|
|
33
|
+
});
|
|
34
|
+
module.exports = __toCommonJS(AppRegistryForm_exports);
|
|
35
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
36
|
+
var React = __toESM(require("react"));
|
|
37
|
+
var import_button = require("../ui/button.js");
|
|
38
|
+
var import_input = require("../ui/input.js");
|
|
39
|
+
var import_label = require("../ui/label.js");
|
|
40
|
+
var import_textarea = require("../ui/textarea.js");
|
|
41
|
+
function kebab(s) {
|
|
42
|
+
return s.toLowerCase().normalize("NFKD").replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40);
|
|
43
|
+
}
|
|
44
|
+
function AppRegistryForm({
|
|
45
|
+
availableGroups,
|
|
46
|
+
initial,
|
|
47
|
+
defaultNavOrder = 10,
|
|
48
|
+
onSubmit,
|
|
49
|
+
busy = false,
|
|
50
|
+
submitLabel = "Save"
|
|
51
|
+
}) {
|
|
52
|
+
const [displayName, setDisplayName] = React.useState(initial?.displayName ?? "");
|
|
53
|
+
const [slug, setSlug] = React.useState(initial?.slug ?? "");
|
|
54
|
+
const [slugTouched, setSlugTouched] = React.useState(!!initial?.slug);
|
|
55
|
+
const [subdomain, setSubdomain] = React.useState(initial?.subdomain ?? "");
|
|
56
|
+
const [subdomainTouched, setSubdomainTouched] = React.useState(
|
|
57
|
+
initial?.subdomain !== void 0 && initial?.subdomain !== ""
|
|
58
|
+
);
|
|
59
|
+
const [navOrder, setNavOrder] = React.useState(
|
|
60
|
+
initial?.navOrder ?? defaultNavOrder
|
|
61
|
+
);
|
|
62
|
+
const [requiredGroups, setRequiredGroups] = React.useState(
|
|
63
|
+
initial?.requiredGroups ?? []
|
|
64
|
+
);
|
|
65
|
+
const [description, setDescription] = React.useState(initial?.description ?? "");
|
|
66
|
+
React.useEffect(() => {
|
|
67
|
+
if (!slugTouched) setSlug(kebab(displayName));
|
|
68
|
+
}, [displayName, slugTouched]);
|
|
69
|
+
React.useEffect(() => {
|
|
70
|
+
if (!subdomainTouched) setSubdomain(kebab(slug));
|
|
71
|
+
}, [slug, subdomainTouched]);
|
|
72
|
+
const toggleGroup = (g) => {
|
|
73
|
+
setRequiredGroups(
|
|
74
|
+
(prev) => prev.includes(g) ? prev.filter((x) => x !== g) : [...prev, g]
|
|
75
|
+
);
|
|
76
|
+
};
|
|
77
|
+
const handleSubmit = async (e) => {
|
|
78
|
+
e.preventDefault();
|
|
79
|
+
await onSubmit({
|
|
80
|
+
displayName: displayName.trim(),
|
|
81
|
+
slug: slug.trim(),
|
|
82
|
+
subdomain: subdomain.trim(),
|
|
83
|
+
navOrder,
|
|
84
|
+
requiredGroups,
|
|
85
|
+
description: description.trim()
|
|
86
|
+
});
|
|
87
|
+
};
|
|
88
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { onSubmit: handleSubmit, className: "space-y-5", children: [
|
|
89
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "space-y-2", children: [
|
|
90
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_label.Label, { htmlFor: "display-name", children: "Display name" }),
|
|
91
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
92
|
+
import_input.Input,
|
|
93
|
+
{
|
|
94
|
+
id: "display-name",
|
|
95
|
+
value: displayName,
|
|
96
|
+
onChange: (e) => setDisplayName(e.target.value),
|
|
97
|
+
placeholder: "Claims Processing",
|
|
98
|
+
required: true
|
|
99
|
+
}
|
|
100
|
+
),
|
|
101
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-xs text-muted-foreground", children: "Shown in the nav and admin list. Slug and subdomain are auto-derived." })
|
|
102
|
+
] }),
|
|
103
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "space-y-2", children: [
|
|
104
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_label.Label, { htmlFor: "slug", children: "Slug" }),
|
|
105
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
106
|
+
import_input.Input,
|
|
107
|
+
{
|
|
108
|
+
id: "slug",
|
|
109
|
+
value: slug,
|
|
110
|
+
onChange: (e) => {
|
|
111
|
+
setSlugTouched(true);
|
|
112
|
+
setSlug(e.target.value);
|
|
113
|
+
},
|
|
114
|
+
placeholder: "claims-processing",
|
|
115
|
+
required: true,
|
|
116
|
+
pattern: "^[a-z0-9][a-z0-9-]*$"
|
|
117
|
+
}
|
|
118
|
+
),
|
|
119
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: "text-xs text-muted-foreground", children: [
|
|
120
|
+
"Stable identifier (kebab-case). Must match",
|
|
121
|
+
" ",
|
|
122
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("code", { className: "font-mono", children: "APP_SLUG" }),
|
|
123
|
+
" in the app's .env."
|
|
124
|
+
] })
|
|
125
|
+
] }),
|
|
126
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "space-y-2", children: [
|
|
127
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_label.Label, { htmlFor: "subdomain", children: "Subdomain" }),
|
|
128
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
129
|
+
import_input.Input,
|
|
130
|
+
{
|
|
131
|
+
id: "subdomain",
|
|
132
|
+
value: subdomain,
|
|
133
|
+
onChange: (e) => {
|
|
134
|
+
setSubdomainTouched(true);
|
|
135
|
+
setSubdomain(e.target.value);
|
|
136
|
+
},
|
|
137
|
+
placeholder: "claims-processing",
|
|
138
|
+
pattern: "^[a-z0-9]([a-z0-9-]*[a-z0-9])?$"
|
|
139
|
+
}
|
|
140
|
+
),
|
|
141
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: "text-xs text-muted-foreground", children: [
|
|
142
|
+
"Leave empty for the apex app. Otherwise this becomes",
|
|
143
|
+
" ",
|
|
144
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("code", { className: "font-mono", children: "<subdomain>.<apex>" }),
|
|
145
|
+
"."
|
|
146
|
+
] })
|
|
147
|
+
] }),
|
|
148
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "space-y-2", children: [
|
|
149
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_label.Label, { htmlFor: "nav-order", children: "Nav order" }),
|
|
150
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
151
|
+
import_input.Input,
|
|
152
|
+
{
|
|
153
|
+
id: "nav-order",
|
|
154
|
+
type: "number",
|
|
155
|
+
value: navOrder,
|
|
156
|
+
onChange: (e) => setNavOrder(Number(e.target.value))
|
|
157
|
+
}
|
|
158
|
+
),
|
|
159
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-xs text-muted-foreground", children: "Lower numbers come first." })
|
|
160
|
+
] }),
|
|
161
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "space-y-2", children: [
|
|
162
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_label.Label, { children: "Required groups" }),
|
|
163
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "flex flex-wrap gap-2", children: availableGroups.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-xs text-muted-foreground", children: "No groups available." }) : availableGroups.map((g) => {
|
|
164
|
+
const checked = requiredGroups.includes(g);
|
|
165
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
166
|
+
"button",
|
|
167
|
+
{
|
|
168
|
+
type: "button",
|
|
169
|
+
onClick: () => toggleGroup(g),
|
|
170
|
+
className: checked ? "rounded-full border border-primary bg-primary px-3 py-1 text-xs text-primary-foreground" : "rounded-full border border-border px-3 py-1 text-xs",
|
|
171
|
+
children: g
|
|
172
|
+
},
|
|
173
|
+
g
|
|
174
|
+
);
|
|
175
|
+
}) }),
|
|
176
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-xs text-muted-foreground", children: "Empty = any authenticated user can see this app." })
|
|
177
|
+
] }),
|
|
178
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "space-y-2", children: [
|
|
179
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_label.Label, { htmlFor: "description", children: "Description" }),
|
|
180
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
181
|
+
import_textarea.Textarea,
|
|
182
|
+
{
|
|
183
|
+
id: "description",
|
|
184
|
+
value: description,
|
|
185
|
+
onChange: (e) => setDescription(e.target.value),
|
|
186
|
+
rows: 3,
|
|
187
|
+
placeholder: "Optional notes shown in the admin UI."
|
|
188
|
+
}
|
|
189
|
+
)
|
|
190
|
+
] }),
|
|
191
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "flex justify-end", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_button.Button, { type: "submit", disabled: busy, children: busy ? "Saving..." : submitLabel }) })
|
|
192
|
+
] });
|
|
193
|
+
}
|
|
194
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
195
|
+
0 && (module.exports = {
|
|
196
|
+
AppRegistryForm
|
|
197
|
+
});
|
|
198
|
+
//# sourceMappingURL=AppRegistryForm.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/components/admin/AppRegistryForm.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { Button } from \"../ui/button.js\";\nimport { Input } from \"../ui/input.js\";\nimport { Label } from \"../ui/label.js\";\nimport { Textarea } from \"../ui/textarea.js\";\n\n// Auto-derivation: displayName -> slug -> subdomain.\n// \"Claims Processing\" -> \"claims-processing\" -> \"claims-processing\"\n// User can override slug and subdomain independently.\nfunction kebab(s: string): string {\n return s\n .toLowerCase()\n .normalize(\"NFKD\")\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\")\n .slice(0, 40);\n}\n\nexport interface AppRegistryFormValue {\n displayName: string;\n slug: string;\n subdomain: string;\n navOrder: number;\n requiredGroups: string[];\n description: string;\n}\n\nexport interface AppRegistryFormProps {\n /** Available Cognito groups to choose from for `requiredGroups`. */\n availableGroups: string[];\n /** Initial values (for edit forms). Empty defaults make this an add form. */\n initial?: Partial<AppRegistryFormValue>;\n /** Default navOrder for new rows (e.g. max+10 derived in the page). */\n defaultNavOrder?: number;\n /** Submit handler; the page wires this to its admin API. */\n onSubmit: (value: AppRegistryFormValue) => Promise<void> | void;\n /** Disable while submitting. */\n busy?: boolean;\n submitLabel?: string;\n}\n\nexport function AppRegistryForm({\n availableGroups,\n initial,\n defaultNavOrder = 10,\n onSubmit,\n busy = false,\n submitLabel = \"Save\",\n}: AppRegistryFormProps) {\n const [displayName, setDisplayName] = React.useState(initial?.displayName ?? \"\");\n const [slug, setSlug] = React.useState(initial?.slug ?? \"\");\n const [slugTouched, setSlugTouched] = React.useState(!!initial?.slug);\n const [subdomain, setSubdomain] = React.useState(initial?.subdomain ?? \"\");\n const [subdomainTouched, setSubdomainTouched] = React.useState(\n initial?.subdomain !== undefined && initial?.subdomain !== \"\",\n );\n const [navOrder, setNavOrder] = React.useState<number>(\n initial?.navOrder ?? defaultNavOrder,\n );\n const [requiredGroups, setRequiredGroups] = React.useState<string[]>(\n initial?.requiredGroups ?? [],\n );\n const [description, setDescription] = React.useState(initial?.description ?? \"\");\n\n // Auto-derive slug and subdomain from displayName until the user edits them.\n React.useEffect(() => {\n if (!slugTouched) setSlug(kebab(displayName));\n }, [displayName, slugTouched]);\n React.useEffect(() => {\n if (!subdomainTouched) setSubdomain(kebab(slug));\n }, [slug, subdomainTouched]);\n\n const toggleGroup = (g: string) => {\n setRequiredGroups((prev) =>\n prev.includes(g) ? prev.filter((x) => x !== g) : [...prev, g],\n );\n };\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n await onSubmit({\n displayName: displayName.trim(),\n slug: slug.trim(),\n subdomain: subdomain.trim(),\n navOrder,\n requiredGroups,\n description: description.trim(),\n });\n };\n\n return (\n <form onSubmit={handleSubmit} className=\"space-y-5\">\n <div className=\"space-y-2\">\n <Label htmlFor=\"display-name\">Display name</Label>\n <Input\n id=\"display-name\"\n value={displayName}\n onChange={(e) => setDisplayName(e.target.value)}\n placeholder=\"Claims Processing\"\n required\n />\n <p className=\"text-xs text-muted-foreground\">\n Shown in the nav and admin list. Slug and subdomain are auto-derived.\n </p>\n </div>\n\n <div className=\"space-y-2\">\n <Label htmlFor=\"slug\">Slug</Label>\n <Input\n id=\"slug\"\n value={slug}\n onChange={(e) => {\n setSlugTouched(true);\n setSlug(e.target.value);\n }}\n placeholder=\"claims-processing\"\n required\n pattern=\"^[a-z0-9][a-z0-9-]*$\"\n />\n <p className=\"text-xs text-muted-foreground\">\n Stable identifier (kebab-case). Must match{\" \"}\n <code className=\"font-mono\">APP_SLUG</code> in the app's .env.\n </p>\n </div>\n\n <div className=\"space-y-2\">\n <Label htmlFor=\"subdomain\">Subdomain</Label>\n <Input\n id=\"subdomain\"\n value={subdomain}\n onChange={(e) => {\n setSubdomainTouched(true);\n setSubdomain(e.target.value);\n }}\n placeholder=\"claims-processing\"\n pattern=\"^[a-z0-9]([a-z0-9-]*[a-z0-9])?$\"\n />\n <p className=\"text-xs text-muted-foreground\">\n Leave empty for the apex app. Otherwise this becomes{\" \"}\n <code className=\"font-mono\"><subdomain>.<apex></code>.\n </p>\n </div>\n\n <div className=\"space-y-2\">\n <Label htmlFor=\"nav-order\">Nav order</Label>\n <Input\n id=\"nav-order\"\n type=\"number\"\n value={navOrder}\n onChange={(e) => setNavOrder(Number(e.target.value))}\n />\n <p className=\"text-xs text-muted-foreground\">Lower numbers come first.</p>\n </div>\n\n <div className=\"space-y-2\">\n <Label>Required groups</Label>\n <div className=\"flex flex-wrap gap-2\">\n {availableGroups.length === 0 ? (\n <p className=\"text-xs text-muted-foreground\">No groups available.</p>\n ) : (\n availableGroups.map((g) => {\n const checked = requiredGroups.includes(g);\n return (\n <button\n type=\"button\"\n key={g}\n onClick={() => toggleGroup(g)}\n className={\n checked\n ? \"rounded-full border border-primary bg-primary px-3 py-1 text-xs text-primary-foreground\"\n : \"rounded-full border border-border px-3 py-1 text-xs\"\n }\n >\n {g}\n </button>\n );\n })\n )}\n </div>\n <p className=\"text-xs text-muted-foreground\">\n Empty = any authenticated user can see this app.\n </p>\n </div>\n\n <div className=\"space-y-2\">\n <Label htmlFor=\"description\">Description</Label>\n <Textarea\n id=\"description\"\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n rows={3}\n placeholder=\"Optional notes shown in the admin UI.\"\n />\n </div>\n\n <div className=\"flex justify-end\">\n <Button type=\"submit\" disabled={busy}>\n {busy ? \"Saving...\" : submitLabel}\n </Button>\n </div>\n </form>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
export interface AppRegistryFormValue {
|
|
3
|
+
displayName: string;
|
|
4
|
+
slug: string;
|
|
5
|
+
subdomain: string;
|
|
6
|
+
navOrder: number;
|
|
7
|
+
requiredGroups: string[];
|
|
8
|
+
description: string;
|
|
9
|
+
}
|
|
10
|
+
export interface AppRegistryFormProps {
|
|
11
|
+
/** Available Cognito groups to choose from for `requiredGroups`. */
|
|
12
|
+
availableGroups: string[];
|
|
13
|
+
/** Initial values (for edit forms). Empty defaults make this an add form. */
|
|
14
|
+
initial?: Partial<AppRegistryFormValue>;
|
|
15
|
+
/** Default navOrder for new rows (e.g. max+10 derived in the page). */
|
|
16
|
+
defaultNavOrder?: number;
|
|
17
|
+
/** Submit handler; the page wires this to its admin API. */
|
|
18
|
+
onSubmit: (value: AppRegistryFormValue) => Promise<void> | void;
|
|
19
|
+
/** Disable while submitting. */
|
|
20
|
+
busy?: boolean;
|
|
21
|
+
submitLabel?: string;
|
|
22
|
+
}
|
|
23
|
+
export declare function AppRegistryForm({ availableGroups, initial, defaultNavOrder, onSubmit, busy, submitLabel, }: AppRegistryFormProps): React.JSX.Element;
|
|
24
|
+
//# sourceMappingURL=AppRegistryForm.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AppRegistryForm.d.ts","sourceRoot":"","sources":["../../../src/components/admin/AppRegistryForm.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAkB/B,MAAM,WAAW,oBAAoB;IACnC,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,oBAAoB;IACnC,oEAAoE;IACpE,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,6EAA6E;IAC7E,OAAO,CAAC,EAAE,OAAO,CAAC,oBAAoB,CAAC,CAAC;IACxC,uEAAuE;IACvE,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,4DAA4D;IAC5D,QAAQ,EAAE,CAAC,KAAK,EAAE,oBAAoB,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAChE,gCAAgC;IAChC,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,wBAAgB,eAAe,CAAC,EAC9B,eAAe,EACf,OAAO,EACP,eAAoB,EACpB,QAAQ,EACR,IAAY,EACZ,WAAoB,GACrB,EAAE,oBAAoB,qBA0JtB"}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { Button } from "../ui/button.js";
|
|
5
|
+
import { Input } from "../ui/input.js";
|
|
6
|
+
import { Label } from "../ui/label.js";
|
|
7
|
+
import { Textarea } from "../ui/textarea.js";
|
|
8
|
+
function kebab(s) {
|
|
9
|
+
return s.toLowerCase().normalize("NFKD").replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40);
|
|
10
|
+
}
|
|
11
|
+
function AppRegistryForm({
|
|
12
|
+
availableGroups,
|
|
13
|
+
initial,
|
|
14
|
+
defaultNavOrder = 10,
|
|
15
|
+
onSubmit,
|
|
16
|
+
busy = false,
|
|
17
|
+
submitLabel = "Save"
|
|
18
|
+
}) {
|
|
19
|
+
const [displayName, setDisplayName] = React.useState(initial?.displayName ?? "");
|
|
20
|
+
const [slug, setSlug] = React.useState(initial?.slug ?? "");
|
|
21
|
+
const [slugTouched, setSlugTouched] = React.useState(!!initial?.slug);
|
|
22
|
+
const [subdomain, setSubdomain] = React.useState(initial?.subdomain ?? "");
|
|
23
|
+
const [subdomainTouched, setSubdomainTouched] = React.useState(
|
|
24
|
+
initial?.subdomain !== void 0 && initial?.subdomain !== ""
|
|
25
|
+
);
|
|
26
|
+
const [navOrder, setNavOrder] = React.useState(
|
|
27
|
+
initial?.navOrder ?? defaultNavOrder
|
|
28
|
+
);
|
|
29
|
+
const [requiredGroups, setRequiredGroups] = React.useState(
|
|
30
|
+
initial?.requiredGroups ?? []
|
|
31
|
+
);
|
|
32
|
+
const [description, setDescription] = React.useState(initial?.description ?? "");
|
|
33
|
+
React.useEffect(() => {
|
|
34
|
+
if (!slugTouched) setSlug(kebab(displayName));
|
|
35
|
+
}, [displayName, slugTouched]);
|
|
36
|
+
React.useEffect(() => {
|
|
37
|
+
if (!subdomainTouched) setSubdomain(kebab(slug));
|
|
38
|
+
}, [slug, subdomainTouched]);
|
|
39
|
+
const toggleGroup = (g) => {
|
|
40
|
+
setRequiredGroups(
|
|
41
|
+
(prev) => prev.includes(g) ? prev.filter((x) => x !== g) : [...prev, g]
|
|
42
|
+
);
|
|
43
|
+
};
|
|
44
|
+
const handleSubmit = async (e) => {
|
|
45
|
+
e.preventDefault();
|
|
46
|
+
await onSubmit({
|
|
47
|
+
displayName: displayName.trim(),
|
|
48
|
+
slug: slug.trim(),
|
|
49
|
+
subdomain: subdomain.trim(),
|
|
50
|
+
navOrder,
|
|
51
|
+
requiredGroups,
|
|
52
|
+
description: description.trim()
|
|
53
|
+
});
|
|
54
|
+
};
|
|
55
|
+
return /* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, className: "space-y-5", children: [
|
|
56
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
57
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "display-name", children: "Display name" }),
|
|
58
|
+
/* @__PURE__ */ jsx(
|
|
59
|
+
Input,
|
|
60
|
+
{
|
|
61
|
+
id: "display-name",
|
|
62
|
+
value: displayName,
|
|
63
|
+
onChange: (e) => setDisplayName(e.target.value),
|
|
64
|
+
placeholder: "Claims Processing",
|
|
65
|
+
required: true
|
|
66
|
+
}
|
|
67
|
+
),
|
|
68
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Shown in the nav and admin list. Slug and subdomain are auto-derived." })
|
|
69
|
+
] }),
|
|
70
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
71
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "slug", children: "Slug" }),
|
|
72
|
+
/* @__PURE__ */ jsx(
|
|
73
|
+
Input,
|
|
74
|
+
{
|
|
75
|
+
id: "slug",
|
|
76
|
+
value: slug,
|
|
77
|
+
onChange: (e) => {
|
|
78
|
+
setSlugTouched(true);
|
|
79
|
+
setSlug(e.target.value);
|
|
80
|
+
},
|
|
81
|
+
placeholder: "claims-processing",
|
|
82
|
+
required: true,
|
|
83
|
+
pattern: "^[a-z0-9][a-z0-9-]*$"
|
|
84
|
+
}
|
|
85
|
+
),
|
|
86
|
+
/* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground", children: [
|
|
87
|
+
"Stable identifier (kebab-case). Must match",
|
|
88
|
+
" ",
|
|
89
|
+
/* @__PURE__ */ jsx("code", { className: "font-mono", children: "APP_SLUG" }),
|
|
90
|
+
" in the app's .env."
|
|
91
|
+
] })
|
|
92
|
+
] }),
|
|
93
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
94
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "subdomain", children: "Subdomain" }),
|
|
95
|
+
/* @__PURE__ */ jsx(
|
|
96
|
+
Input,
|
|
97
|
+
{
|
|
98
|
+
id: "subdomain",
|
|
99
|
+
value: subdomain,
|
|
100
|
+
onChange: (e) => {
|
|
101
|
+
setSubdomainTouched(true);
|
|
102
|
+
setSubdomain(e.target.value);
|
|
103
|
+
},
|
|
104
|
+
placeholder: "claims-processing",
|
|
105
|
+
pattern: "^[a-z0-9]([a-z0-9-]*[a-z0-9])?$"
|
|
106
|
+
}
|
|
107
|
+
),
|
|
108
|
+
/* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground", children: [
|
|
109
|
+
"Leave empty for the apex app. Otherwise this becomes",
|
|
110
|
+
" ",
|
|
111
|
+
/* @__PURE__ */ jsx("code", { className: "font-mono", children: "<subdomain>.<apex>" }),
|
|
112
|
+
"."
|
|
113
|
+
] })
|
|
114
|
+
] }),
|
|
115
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
116
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "nav-order", children: "Nav order" }),
|
|
117
|
+
/* @__PURE__ */ jsx(
|
|
118
|
+
Input,
|
|
119
|
+
{
|
|
120
|
+
id: "nav-order",
|
|
121
|
+
type: "number",
|
|
122
|
+
value: navOrder,
|
|
123
|
+
onChange: (e) => setNavOrder(Number(e.target.value))
|
|
124
|
+
}
|
|
125
|
+
),
|
|
126
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Lower numbers come first." })
|
|
127
|
+
] }),
|
|
128
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
129
|
+
/* @__PURE__ */ jsx(Label, { children: "Required groups" }),
|
|
130
|
+
/* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-2", children: availableGroups.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "No groups available." }) : availableGroups.map((g) => {
|
|
131
|
+
const checked = requiredGroups.includes(g);
|
|
132
|
+
return /* @__PURE__ */ jsx(
|
|
133
|
+
"button",
|
|
134
|
+
{
|
|
135
|
+
type: "button",
|
|
136
|
+
onClick: () => toggleGroup(g),
|
|
137
|
+
className: checked ? "rounded-full border border-primary bg-primary px-3 py-1 text-xs text-primary-foreground" : "rounded-full border border-border px-3 py-1 text-xs",
|
|
138
|
+
children: g
|
|
139
|
+
},
|
|
140
|
+
g
|
|
141
|
+
);
|
|
142
|
+
}) }),
|
|
143
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Empty = any authenticated user can see this app." })
|
|
144
|
+
] }),
|
|
145
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
146
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "description", children: "Description" }),
|
|
147
|
+
/* @__PURE__ */ jsx(
|
|
148
|
+
Textarea,
|
|
149
|
+
{
|
|
150
|
+
id: "description",
|
|
151
|
+
value: description,
|
|
152
|
+
onChange: (e) => setDescription(e.target.value),
|
|
153
|
+
rows: 3,
|
|
154
|
+
placeholder: "Optional notes shown in the admin UI."
|
|
155
|
+
}
|
|
156
|
+
)
|
|
157
|
+
] }),
|
|
158
|
+
/* @__PURE__ */ jsx("div", { className: "flex justify-end", children: /* @__PURE__ */ jsx(Button, { type: "submit", disabled: busy, children: busy ? "Saving..." : submitLabel }) })
|
|
159
|
+
] });
|
|
160
|
+
}
|
|
161
|
+
export {
|
|
162
|
+
AppRegistryForm
|
|
163
|
+
};
|
|
164
|
+
//# sourceMappingURL=AppRegistryForm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/components/admin/AppRegistryForm.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { Button } from \"../ui/button.js\";\nimport { Input } from \"../ui/input.js\";\nimport { Label } from \"../ui/label.js\";\nimport { Textarea } from \"../ui/textarea.js\";\n\n// Auto-derivation: displayName -> slug -> subdomain.\n// \"Claims Processing\" -> \"claims-processing\" -> \"claims-processing\"\n// User can override slug and subdomain independently.\nfunction kebab(s: string): string {\n return s\n .toLowerCase()\n .normalize(\"NFKD\")\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\")\n .slice(0, 40);\n}\n\nexport interface AppRegistryFormValue {\n displayName: string;\n slug: string;\n subdomain: string;\n navOrder: number;\n requiredGroups: string[];\n description: string;\n}\n\nexport interface AppRegistryFormProps {\n /** Available Cognito groups to choose from for `requiredGroups`. */\n availableGroups: string[];\n /** Initial values (for edit forms). Empty defaults make this an add form. */\n initial?: Partial<AppRegistryFormValue>;\n /** Default navOrder for new rows (e.g. max+10 derived in the page). */\n defaultNavOrder?: number;\n /** Submit handler; the page wires this to its admin API. */\n onSubmit: (value: AppRegistryFormValue) => Promise<void> | void;\n /** Disable while submitting. */\n busy?: boolean;\n submitLabel?: string;\n}\n\nexport function AppRegistryForm({\n availableGroups,\n initial,\n defaultNavOrder = 10,\n onSubmit,\n busy = false,\n submitLabel = \"Save\",\n}: AppRegistryFormProps) {\n const [displayName, setDisplayName] = React.useState(initial?.displayName ?? \"\");\n const [slug, setSlug] = React.useState(initial?.slug ?? \"\");\n const [slugTouched, setSlugTouched] = React.useState(!!initial?.slug);\n const [subdomain, setSubdomain] = React.useState(initial?.subdomain ?? \"\");\n const [subdomainTouched, setSubdomainTouched] = React.useState(\n initial?.subdomain !== undefined && initial?.subdomain !== \"\",\n );\n const [navOrder, setNavOrder] = React.useState<number>(\n initial?.navOrder ?? defaultNavOrder,\n );\n const [requiredGroups, setRequiredGroups] = React.useState<string[]>(\n initial?.requiredGroups ?? [],\n );\n const [description, setDescription] = React.useState(initial?.description ?? \"\");\n\n // Auto-derive slug and subdomain from displayName until the user edits them.\n React.useEffect(() => {\n if (!slugTouched) setSlug(kebab(displayName));\n }, [displayName, slugTouched]);\n React.useEffect(() => {\n if (!subdomainTouched) setSubdomain(kebab(slug));\n }, [slug, subdomainTouched]);\n\n const toggleGroup = (g: string) => {\n setRequiredGroups((prev) =>\n prev.includes(g) ? prev.filter((x) => x !== g) : [...prev, g],\n );\n };\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n await onSubmit({\n displayName: displayName.trim(),\n slug: slug.trim(),\n subdomain: subdomain.trim(),\n navOrder,\n requiredGroups,\n description: description.trim(),\n });\n };\n\n return (\n <form onSubmit={handleSubmit} className=\"space-y-5\">\n <div className=\"space-y-2\">\n <Label htmlFor=\"display-name\">Display name</Label>\n <Input\n id=\"display-name\"\n value={displayName}\n onChange={(e) => setDisplayName(e.target.value)}\n placeholder=\"Claims Processing\"\n required\n />\n <p className=\"text-xs text-muted-foreground\">\n Shown in the nav and admin list. Slug and subdomain are auto-derived.\n </p>\n </div>\n\n <div className=\"space-y-2\">\n <Label htmlFor=\"slug\">Slug</Label>\n <Input\n id=\"slug\"\n value={slug}\n onChange={(e) => {\n setSlugTouched(true);\n setSlug(e.target.value);\n }}\n placeholder=\"claims-processing\"\n required\n pattern=\"^[a-z0-9][a-z0-9-]*$\"\n />\n <p className=\"text-xs text-muted-foreground\">\n Stable identifier (kebab-case). Must match{\" \"}\n <code className=\"font-mono\">APP_SLUG</code> in the app's .env.\n </p>\n </div>\n\n <div className=\"space-y-2\">\n <Label htmlFor=\"subdomain\">Subdomain</Label>\n <Input\n id=\"subdomain\"\n value={subdomain}\n onChange={(e) => {\n setSubdomainTouched(true);\n setSubdomain(e.target.value);\n }}\n placeholder=\"claims-processing\"\n pattern=\"^[a-z0-9]([a-z0-9-]*[a-z0-9])?$\"\n />\n <p className=\"text-xs text-muted-foreground\">\n Leave empty for the apex app. Otherwise this becomes{\" \"}\n <code className=\"font-mono\"><subdomain>.<apex></code>.\n </p>\n </div>\n\n <div className=\"space-y-2\">\n <Label htmlFor=\"nav-order\">Nav order</Label>\n <Input\n id=\"nav-order\"\n type=\"number\"\n value={navOrder}\n onChange={(e) => setNavOrder(Number(e.target.value))}\n />\n <p className=\"text-xs text-muted-foreground\">Lower numbers come first.</p>\n </div>\n\n <div className=\"space-y-2\">\n <Label>Required groups</Label>\n <div className=\"flex flex-wrap gap-2\">\n {availableGroups.length === 0 ? (\n <p className=\"text-xs text-muted-foreground\">No groups available.</p>\n ) : (\n availableGroups.map((g) => {\n const checked = requiredGroups.includes(g);\n return (\n <button\n type=\"button\"\n key={g}\n onClick={() => toggleGroup(g)}\n className={\n checked\n ? \"rounded-full border border-primary bg-primary px-3 py-1 text-xs text-primary-foreground\"\n : \"rounded-full border border-border px-3 py-1 text-xs\"\n }\n >\n {g}\n </button>\n );\n })\n )}\n </div>\n <p className=\"text-xs text-muted-foreground\">\n Empty = any authenticated user can see this app.\n </p>\n </div>\n\n <div className=\"space-y-2\">\n <Label htmlFor=\"description\">Description</Label>\n <Textarea\n id=\"description\"\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n rows={3}\n placeholder=\"Optional notes shown in the admin UI.\"\n />\n </div>\n\n <div className=\"flex justify-end\">\n <Button type=\"submit\" disabled={busy}>\n {busy ? \"Saving...\" : submitLabel}\n </Button>\n </div>\n </form>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -34,6 +34,7 @@ __export(AppShell_exports, {
|
|
|
34
34
|
module.exports = __toCommonJS(AppShell_exports);
|
|
35
35
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
36
36
|
var React = __toESM(require("react"));
|
|
37
|
+
var import_react_query = require("@tanstack/react-query");
|
|
37
38
|
var import_lucide_react = require("lucide-react");
|
|
38
39
|
var import_Logo = require("../chrome/Logo.js");
|
|
39
40
|
var import_ThemeSwitcher = require("../chrome/ThemeSwitcher.js");
|
|
@@ -62,16 +63,59 @@ function NavGroup({ group, pathname }) {
|
|
|
62
63
|
}) })
|
|
63
64
|
] });
|
|
64
65
|
}
|
|
66
|
+
function EcosystemSection() {
|
|
67
|
+
const currentHostname = typeof window !== "undefined" ? window.location.hostname : "";
|
|
68
|
+
const { data: apps } = (0, import_react_query.useQuery)({
|
|
69
|
+
queryKey: ["augmenting-integrations/registry", "apps"],
|
|
70
|
+
queryFn: async () => {
|
|
71
|
+
const r = await fetch("/api/apps", { credentials: "same-origin" });
|
|
72
|
+
if (!r.ok) return [];
|
|
73
|
+
const json = await r.json();
|
|
74
|
+
return Array.isArray(json) ? json : [];
|
|
75
|
+
},
|
|
76
|
+
staleTime: 5 * 6e4,
|
|
77
|
+
refetchOnWindowFocus: false
|
|
78
|
+
});
|
|
79
|
+
if (!apps || apps.length === 0) return null;
|
|
80
|
+
const sorted = [...apps].sort((a, b) => (a.navOrder ?? 0) - (b.navOrder ?? 0));
|
|
81
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "space-y-1", children: [
|
|
82
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "px-3 text-xs font-semibold uppercase tracking-wide text-muted-foreground", children: "Apps" }),
|
|
83
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", { className: "space-y-0.5", children: sorted.map((app) => {
|
|
84
|
+
let active;
|
|
85
|
+
try {
|
|
86
|
+
active = new URL(app.appUrl).hostname === currentHostname;
|
|
87
|
+
} catch {
|
|
88
|
+
active = false;
|
|
89
|
+
}
|
|
90
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
91
|
+
"a",
|
|
92
|
+
{
|
|
93
|
+
href: app.appUrl,
|
|
94
|
+
className: (0, import_utils.cn)(
|
|
95
|
+
"flex items-center gap-3 rounded-md px-3 py-2 text-sm transition",
|
|
96
|
+
active ? "bg-sidebar-accent text-sidebar-accent-foreground" : "text-sidebar-foreground hover:bg-sidebar-accent/60"
|
|
97
|
+
),
|
|
98
|
+
children: [
|
|
99
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.AppWindow, { className: "h-4 w-4 shrink-0" }),
|
|
100
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: app.displayName })
|
|
101
|
+
]
|
|
102
|
+
}
|
|
103
|
+
) }, app.slug);
|
|
104
|
+
}) })
|
|
105
|
+
] });
|
|
106
|
+
}
|
|
65
107
|
function AppShell({
|
|
66
108
|
children,
|
|
67
109
|
pathname,
|
|
68
110
|
navGroups,
|
|
69
111
|
homeHref = "/",
|
|
70
|
-
showRoleSwitcher = true
|
|
112
|
+
showRoleSwitcher = true,
|
|
113
|
+
showEcosystemNav = true
|
|
71
114
|
}) {
|
|
72
115
|
const [mobileOpen, setMobileOpen] = React.useState(false);
|
|
73
116
|
const sidebarContent = /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex h-full flex-col gap-6 px-3 py-4", children: [
|
|
74
117
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("a", { href: homeHref, className: "px-2", "aria-label": "Home", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_Logo.Logo, {}) }),
|
|
118
|
+
showEcosystemNav ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(EcosystemSection, {}) : null,
|
|
75
119
|
navGroups.map((g) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(NavGroup, { group: g, pathname }, g.label))
|
|
76
120
|
] });
|
|
77
121
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex min-h-svh bg-background text-foreground", children: [
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/shells/AppShell.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { Menu, X } from \"lucide-react\";\nimport { Logo } from \"../chrome/Logo.js\";\nimport { ThemeSwitcher } from \"../chrome/ThemeSwitcher.js\";\nimport { RoleSwitcher } from \"../chrome/RoleSwitcher.js\";\nimport { cn } from \"../../lib/utils.js\";\n\n// Nav uses raw `<a>` not `next/link`.
|
|
1
|
+
{"version":3,"sources":["../../../src/components/shells/AppShell.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { useQuery } from \"@tanstack/react-query\";\nimport { Menu, X, AppWindow } from \"lucide-react\";\nimport { Logo } from \"../chrome/Logo.js\";\nimport { ThemeSwitcher } from \"../chrome/ThemeSwitcher.js\";\nimport { RoleSwitcher } from \"../chrome/RoleSwitcher.js\";\nimport { cn } from \"../../lib/utils.js\";\n\n// Nav uses raw `<a>` not `next/link`. Cross-app links target different\n// subdomains (different Lambda origins), and within a single app\n// navigating via raw <a> avoids subtle next/link basePath issues. We\n// lose prefetch but it doesn't help across subdomains anyway.\n\nexport type AppNavItem = {\n href: string;\n label: string;\n icon: React.ComponentType<{ className?: string }>;\n /** When provided, item only renders if the consumer's `getFlag(flag)` returns true. */\n flag?: string;\n};\n\nexport type AppNavGroup = { label: string; items: AppNavItem[] };\n\n/** Subset of @augmenting-integrations/registry's AppRegistryItem that AppShell needs. */\nexport type EcosystemApp = {\n slug: string;\n displayName: string;\n appUrl: string;\n navOrder: number;\n};\n\nfunction NavGroup({ group, pathname }: { group: AppNavGroup; pathname: string }) {\n if (group.items.length === 0) return null;\n return (\n <div className=\"space-y-1\">\n <p className=\"px-3 text-xs font-semibold uppercase tracking-wide text-muted-foreground\">\n {group.label}\n </p>\n <ul className=\"space-y-0.5\">\n {group.items.map((item) => {\n const active = pathname === item.href || pathname.startsWith(`${item.href}/`);\n return (\n <li key={item.href}>\n <a\n href={item.href}\n className={cn(\n \"flex items-center gap-3 rounded-md px-3 py-2 text-sm transition\",\n active\n ? \"bg-sidebar-accent text-sidebar-accent-foreground\"\n : \"text-sidebar-foreground hover:bg-sidebar-accent/60\",\n )}\n >\n <item.icon className=\"h-4 w-4 shrink-0\" />\n <span>{item.label}</span>\n </a>\n </li>\n );\n })}\n </ul>\n </div>\n );\n}\n\nfunction EcosystemSection() {\n const currentHostname = typeof window !== \"undefined\" ? window.location.hostname : \"\";\n\n const { data: apps } = useQuery<EcosystemApp[]>({\n queryKey: [\"augmenting-integrations/registry\", \"apps\"],\n queryFn: async () => {\n const r = await fetch(\"/api/apps\", { credentials: \"same-origin\" });\n if (!r.ok) return [];\n const json = (await r.json()) as unknown;\n return Array.isArray(json) ? (json as EcosystemApp[]) : [];\n },\n staleTime: 5 * 60_000,\n refetchOnWindowFocus: false,\n });\n\n if (!apps || apps.length === 0) return null;\n\n const sorted = [...apps].sort((a, b) => (a.navOrder ?? 0) - (b.navOrder ?? 0));\n\n return (\n <div className=\"space-y-1\">\n <p className=\"px-3 text-xs font-semibold uppercase tracking-wide text-muted-foreground\">\n Apps\n </p>\n <ul className=\"space-y-0.5\">\n {sorted.map((app) => {\n let active: boolean;\n try {\n active = new URL(app.appUrl).hostname === currentHostname;\n } catch {\n active = false;\n }\n return (\n <li key={app.slug}>\n <a\n href={app.appUrl}\n className={cn(\n \"flex items-center gap-3 rounded-md px-3 py-2 text-sm transition\",\n active\n ? \"bg-sidebar-accent text-sidebar-accent-foreground\"\n : \"text-sidebar-foreground hover:bg-sidebar-accent/60\",\n )}\n >\n <AppWindow className=\"h-4 w-4 shrink-0\" />\n <span>{app.displayName}</span>\n </a>\n </li>\n );\n })}\n </ul>\n </div>\n );\n}\n\nexport function AppShell({\n children,\n pathname,\n navGroups,\n homeHref = \"/\",\n showRoleSwitcher = true,\n showEcosystemNav = true,\n}: {\n children: React.ReactNode;\n pathname: string;\n navGroups: AppNavGroup[];\n homeHref?: string;\n showRoleSwitcher?: boolean;\n /**\n * Show the auto-discovered Apps section that fetches from `/api/apps`.\n * Default true. Set false if the consuming app handles ecosystem nav\n * separately or is deployed standalone.\n */\n showEcosystemNav?: boolean;\n}) {\n const [mobileOpen, setMobileOpen] = React.useState(false);\n\n const sidebarContent = (\n <div className=\"flex h-full flex-col gap-6 px-3 py-4\">\n <a href={homeHref} className=\"px-2\" aria-label=\"Home\">\n <Logo />\n </a>\n {showEcosystemNav ? <EcosystemSection /> : null}\n {navGroups.map((g) => (\n <NavGroup key={g.label} group={g} pathname={pathname} />\n ))}\n </div>\n );\n\n return (\n <div className=\"flex min-h-svh bg-background text-foreground\">\n <aside className=\"hidden w-64 shrink-0 border-r border-sidebar-border bg-sidebar text-sidebar-foreground md:block\">\n {sidebarContent}\n </aside>\n\n {mobileOpen ? (\n <div className=\"fixed inset-0 z-50 flex md:hidden\">\n <div\n className=\"flex-1 bg-foreground/40 backdrop-blur-sm\"\n onClick={() => setMobileOpen(false)}\n aria-hidden\n />\n <aside className=\"relative w-72 border-l border-sidebar-border bg-sidebar text-sidebar-foreground\">\n <button\n type=\"button\"\n onClick={() => setMobileOpen(false)}\n aria-label=\"Close menu\"\n className=\"absolute right-2 top-2 inline-flex h-9 w-9 items-center justify-center rounded-md hover:bg-sidebar-accent\"\n >\n <X className=\"h-4 w-4\" />\n </button>\n {sidebarContent}\n </aside>\n </div>\n ) : null}\n\n <div className=\"flex min-w-0 flex-1 flex-col\">\n <header className=\"flex h-14 items-center justify-between gap-3 border-b border-border bg-background/80 px-4 backdrop-blur md:px-6\">\n <button\n type=\"button\"\n onClick={() => setMobileOpen(true)}\n aria-label=\"Open menu\"\n className=\"inline-flex h-9 w-9 items-center justify-center rounded-md border border-border md:hidden\"\n >\n <Menu className=\"h-4 w-4\" />\n </button>\n <div className=\"flex items-center gap-3 md:ml-auto\">\n {showRoleSwitcher ? <RoleSwitcher /> : null}\n <ThemeSwitcher />\n </div>\n </header>\n <main className=\"min-w-0 flex-1 px-4 py-6 md:px-8 md:py-8\">{children}</main>\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -12,11 +12,24 @@ export type AppNavGroup = {
|
|
|
12
12
|
label: string;
|
|
13
13
|
items: AppNavItem[];
|
|
14
14
|
};
|
|
15
|
-
|
|
15
|
+
/** Subset of @augmenting-integrations/registry's AppRegistryItem that AppShell needs. */
|
|
16
|
+
export type EcosystemApp = {
|
|
17
|
+
slug: string;
|
|
18
|
+
displayName: string;
|
|
19
|
+
appUrl: string;
|
|
20
|
+
navOrder: number;
|
|
21
|
+
};
|
|
22
|
+
export declare function AppShell({ children, pathname, navGroups, homeHref, showRoleSwitcher, showEcosystemNav, }: {
|
|
16
23
|
children: React.ReactNode;
|
|
17
24
|
pathname: string;
|
|
18
25
|
navGroups: AppNavGroup[];
|
|
19
26
|
homeHref?: string;
|
|
20
27
|
showRoleSwitcher?: boolean;
|
|
28
|
+
/**
|
|
29
|
+
* Show the auto-discovered Apps section that fetches from `/api/apps`.
|
|
30
|
+
* Default true. Set false if the consuming app handles ecosystem nav
|
|
31
|
+
* separately or is deployed standalone.
|
|
32
|
+
*/
|
|
33
|
+
showEcosystemNav?: boolean;
|
|
21
34
|
}): React.JSX.Element;
|
|
22
35
|
//# sourceMappingURL=AppShell.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AppShell.d.ts","sourceRoot":"","sources":["../../../src/components/shells/AppShell.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"AppShell.d.ts","sourceRoot":"","sources":["../../../src/components/shells/AppShell.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAa/B,MAAM,MAAM,UAAU,GAAG;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,KAAK,CAAC,aAAa,CAAC;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAClD,uFAAuF;IACvF,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,UAAU,EAAE,CAAA;CAAE,CAAC;AAEjE,yFAAyF;AACzF,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAwFF,wBAAgB,QAAQ,CAAC,EACvB,QAAQ,EACR,QAAQ,EACR,SAAS,EACT,QAAc,EACd,gBAAuB,EACvB,gBAAuB,GACxB,EAAE;IACD,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,WAAW,EAAE,CAAC;IACzB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B,qBA6DA"}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import * as React from "react";
|
|
4
|
-
import {
|
|
4
|
+
import { useQuery } from "@tanstack/react-query";
|
|
5
|
+
import { Menu, X, AppWindow } from "lucide-react";
|
|
5
6
|
import { Logo } from "../chrome/Logo.js";
|
|
6
7
|
import { ThemeSwitcher } from "../chrome/ThemeSwitcher.js";
|
|
7
8
|
import { RoleSwitcher } from "../chrome/RoleSwitcher.js";
|
|
@@ -29,16 +30,59 @@ function NavGroup({ group, pathname }) {
|
|
|
29
30
|
}) })
|
|
30
31
|
] });
|
|
31
32
|
}
|
|
33
|
+
function EcosystemSection() {
|
|
34
|
+
const currentHostname = typeof window !== "undefined" ? window.location.hostname : "";
|
|
35
|
+
const { data: apps } = useQuery({
|
|
36
|
+
queryKey: ["augmenting-integrations/registry", "apps"],
|
|
37
|
+
queryFn: async () => {
|
|
38
|
+
const r = await fetch("/api/apps", { credentials: "same-origin" });
|
|
39
|
+
if (!r.ok) return [];
|
|
40
|
+
const json = await r.json();
|
|
41
|
+
return Array.isArray(json) ? json : [];
|
|
42
|
+
},
|
|
43
|
+
staleTime: 5 * 6e4,
|
|
44
|
+
refetchOnWindowFocus: false
|
|
45
|
+
});
|
|
46
|
+
if (!apps || apps.length === 0) return null;
|
|
47
|
+
const sorted = [...apps].sort((a, b) => (a.navOrder ?? 0) - (b.navOrder ?? 0));
|
|
48
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
49
|
+
/* @__PURE__ */ jsx("p", { className: "px-3 text-xs font-semibold uppercase tracking-wide text-muted-foreground", children: "Apps" }),
|
|
50
|
+
/* @__PURE__ */ jsx("ul", { className: "space-y-0.5", children: sorted.map((app) => {
|
|
51
|
+
let active;
|
|
52
|
+
try {
|
|
53
|
+
active = new URL(app.appUrl).hostname === currentHostname;
|
|
54
|
+
} catch {
|
|
55
|
+
active = false;
|
|
56
|
+
}
|
|
57
|
+
return /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(
|
|
58
|
+
"a",
|
|
59
|
+
{
|
|
60
|
+
href: app.appUrl,
|
|
61
|
+
className: cn(
|
|
62
|
+
"flex items-center gap-3 rounded-md px-3 py-2 text-sm transition",
|
|
63
|
+
active ? "bg-sidebar-accent text-sidebar-accent-foreground" : "text-sidebar-foreground hover:bg-sidebar-accent/60"
|
|
64
|
+
),
|
|
65
|
+
children: [
|
|
66
|
+
/* @__PURE__ */ jsx(AppWindow, { className: "h-4 w-4 shrink-0" }),
|
|
67
|
+
/* @__PURE__ */ jsx("span", { children: app.displayName })
|
|
68
|
+
]
|
|
69
|
+
}
|
|
70
|
+
) }, app.slug);
|
|
71
|
+
}) })
|
|
72
|
+
] });
|
|
73
|
+
}
|
|
32
74
|
function AppShell({
|
|
33
75
|
children,
|
|
34
76
|
pathname,
|
|
35
77
|
navGroups,
|
|
36
78
|
homeHref = "/",
|
|
37
|
-
showRoleSwitcher = true
|
|
79
|
+
showRoleSwitcher = true,
|
|
80
|
+
showEcosystemNav = true
|
|
38
81
|
}) {
|
|
39
82
|
const [mobileOpen, setMobileOpen] = React.useState(false);
|
|
40
83
|
const sidebarContent = /* @__PURE__ */ jsxs("div", { className: "flex h-full flex-col gap-6 px-3 py-4", children: [
|
|
41
84
|
/* @__PURE__ */ jsx("a", { href: homeHref, className: "px-2", "aria-label": "Home", children: /* @__PURE__ */ jsx(Logo, {}) }),
|
|
85
|
+
showEcosystemNav ? /* @__PURE__ */ jsx(EcosystemSection, {}) : null,
|
|
42
86
|
navGroups.map((g) => /* @__PURE__ */ jsx(NavGroup, { group: g, pathname }, g.label))
|
|
43
87
|
] });
|
|
44
88
|
return /* @__PURE__ */ jsxs("div", { className: "flex min-h-svh bg-background text-foreground", children: [
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/shells/AppShell.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { Menu, X } from \"lucide-react\";\nimport { Logo } from \"../chrome/Logo.js\";\nimport { ThemeSwitcher } from \"../chrome/ThemeSwitcher.js\";\nimport { RoleSwitcher } from \"../chrome/RoleSwitcher.js\";\nimport { cn } from \"../../lib/utils.js\";\n\n// Nav uses raw `<a>` not `next/link`.
|
|
1
|
+
{"version":3,"sources":["../../../src/components/shells/AppShell.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { useQuery } from \"@tanstack/react-query\";\nimport { Menu, X, AppWindow } from \"lucide-react\";\nimport { Logo } from \"../chrome/Logo.js\";\nimport { ThemeSwitcher } from \"../chrome/ThemeSwitcher.js\";\nimport { RoleSwitcher } from \"../chrome/RoleSwitcher.js\";\nimport { cn } from \"../../lib/utils.js\";\n\n// Nav uses raw `<a>` not `next/link`. Cross-app links target different\n// subdomains (different Lambda origins), and within a single app\n// navigating via raw <a> avoids subtle next/link basePath issues. We\n// lose prefetch but it doesn't help across subdomains anyway.\n\nexport type AppNavItem = {\n href: string;\n label: string;\n icon: React.ComponentType<{ className?: string }>;\n /** When provided, item only renders if the consumer's `getFlag(flag)` returns true. */\n flag?: string;\n};\n\nexport type AppNavGroup = { label: string; items: AppNavItem[] };\n\n/** Subset of @augmenting-integrations/registry's AppRegistryItem that AppShell needs. */\nexport type EcosystemApp = {\n slug: string;\n displayName: string;\n appUrl: string;\n navOrder: number;\n};\n\nfunction NavGroup({ group, pathname }: { group: AppNavGroup; pathname: string }) {\n if (group.items.length === 0) return null;\n return (\n <div className=\"space-y-1\">\n <p className=\"px-3 text-xs font-semibold uppercase tracking-wide text-muted-foreground\">\n {group.label}\n </p>\n <ul className=\"space-y-0.5\">\n {group.items.map((item) => {\n const active = pathname === item.href || pathname.startsWith(`${item.href}/`);\n return (\n <li key={item.href}>\n <a\n href={item.href}\n className={cn(\n \"flex items-center gap-3 rounded-md px-3 py-2 text-sm transition\",\n active\n ? \"bg-sidebar-accent text-sidebar-accent-foreground\"\n : \"text-sidebar-foreground hover:bg-sidebar-accent/60\",\n )}\n >\n <item.icon className=\"h-4 w-4 shrink-0\" />\n <span>{item.label}</span>\n </a>\n </li>\n );\n })}\n </ul>\n </div>\n );\n}\n\nfunction EcosystemSection() {\n const currentHostname = typeof window !== \"undefined\" ? window.location.hostname : \"\";\n\n const { data: apps } = useQuery<EcosystemApp[]>({\n queryKey: [\"augmenting-integrations/registry\", \"apps\"],\n queryFn: async () => {\n const r = await fetch(\"/api/apps\", { credentials: \"same-origin\" });\n if (!r.ok) return [];\n const json = (await r.json()) as unknown;\n return Array.isArray(json) ? (json as EcosystemApp[]) : [];\n },\n staleTime: 5 * 60_000,\n refetchOnWindowFocus: false,\n });\n\n if (!apps || apps.length === 0) return null;\n\n const sorted = [...apps].sort((a, b) => (a.navOrder ?? 0) - (b.navOrder ?? 0));\n\n return (\n <div className=\"space-y-1\">\n <p className=\"px-3 text-xs font-semibold uppercase tracking-wide text-muted-foreground\">\n Apps\n </p>\n <ul className=\"space-y-0.5\">\n {sorted.map((app) => {\n let active: boolean;\n try {\n active = new URL(app.appUrl).hostname === currentHostname;\n } catch {\n active = false;\n }\n return (\n <li key={app.slug}>\n <a\n href={app.appUrl}\n className={cn(\n \"flex items-center gap-3 rounded-md px-3 py-2 text-sm transition\",\n active\n ? \"bg-sidebar-accent text-sidebar-accent-foreground\"\n : \"text-sidebar-foreground hover:bg-sidebar-accent/60\",\n )}\n >\n <AppWindow className=\"h-4 w-4 shrink-0\" />\n <span>{app.displayName}</span>\n </a>\n </li>\n );\n })}\n </ul>\n </div>\n );\n}\n\nexport function AppShell({\n children,\n pathname,\n navGroups,\n homeHref = \"/\",\n showRoleSwitcher = true,\n showEcosystemNav = true,\n}: {\n children: React.ReactNode;\n pathname: string;\n navGroups: AppNavGroup[];\n homeHref?: string;\n showRoleSwitcher?: boolean;\n /**\n * Show the auto-discovered Apps section that fetches from `/api/apps`.\n * Default true. Set false if the consuming app handles ecosystem nav\n * separately or is deployed standalone.\n */\n showEcosystemNav?: boolean;\n}) {\n const [mobileOpen, setMobileOpen] = React.useState(false);\n\n const sidebarContent = (\n <div className=\"flex h-full flex-col gap-6 px-3 py-4\">\n <a href={homeHref} className=\"px-2\" aria-label=\"Home\">\n <Logo />\n </a>\n {showEcosystemNav ? <EcosystemSection /> : null}\n {navGroups.map((g) => (\n <NavGroup key={g.label} group={g} pathname={pathname} />\n ))}\n </div>\n );\n\n return (\n <div className=\"flex min-h-svh bg-background text-foreground\">\n <aside className=\"hidden w-64 shrink-0 border-r border-sidebar-border bg-sidebar text-sidebar-foreground md:block\">\n {sidebarContent}\n </aside>\n\n {mobileOpen ? (\n <div className=\"fixed inset-0 z-50 flex md:hidden\">\n <div\n className=\"flex-1 bg-foreground/40 backdrop-blur-sm\"\n onClick={() => setMobileOpen(false)}\n aria-hidden\n />\n <aside className=\"relative w-72 border-l border-sidebar-border bg-sidebar text-sidebar-foreground\">\n <button\n type=\"button\"\n onClick={() => setMobileOpen(false)}\n aria-label=\"Close menu\"\n className=\"absolute right-2 top-2 inline-flex h-9 w-9 items-center justify-center rounded-md hover:bg-sidebar-accent\"\n >\n <X className=\"h-4 w-4\" />\n </button>\n {sidebarContent}\n </aside>\n </div>\n ) : null}\n\n <div className=\"flex min-w-0 flex-1 flex-col\">\n <header className=\"flex h-14 items-center justify-between gap-3 border-b border-border bg-background/80 px-4 backdrop-blur md:px-6\">\n <button\n type=\"button\"\n onClick={() => setMobileOpen(true)}\n aria-label=\"Open menu\"\n className=\"inline-flex h-9 w-9 items-center justify-center rounded-md border border-border md:hidden\"\n >\n <Menu className=\"h-4 w-4\" />\n </button>\n <div className=\"flex items-center gap-3 md:ml-auto\">\n {showRoleSwitcher ? <RoleSwitcher /> : null}\n <ThemeSwitcher />\n </div>\n </header>\n <main className=\"min-w-0 flex-1 px-4 py-6 md:px-8 md:py-8\">{children}</main>\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.cjs
CHANGED
|
@@ -19,6 +19,7 @@ var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "defau
|
|
|
19
19
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
20
20
|
var index_exports = {};
|
|
21
21
|
__export(index_exports, {
|
|
22
|
+
AppRegistryForm: () => import_AppRegistryForm.AppRegistryForm,
|
|
22
23
|
AppShell: () => import_AppShell.AppShell,
|
|
23
24
|
Hero: () => import_Hero.Hero,
|
|
24
25
|
Logo: () => import_Logo.Logo,
|
|
@@ -58,6 +59,7 @@ var import_MdxContent = require("./components/marketing/MdxContent.js");
|
|
|
58
59
|
var import_PublicShell = require("./components/shells/PublicShell.js");
|
|
59
60
|
var import_AppShell = require("./components/shells/AppShell.js");
|
|
60
61
|
var import_StudioShell = require("./components/shells/StudioShell.js");
|
|
62
|
+
var import_AppRegistryForm = require("./components/admin/AppRegistryForm.js");
|
|
61
63
|
var import_SessionProvider = require("./providers/SessionProvider.js");
|
|
62
64
|
var import_QueryProvider = require("./providers/QueryProvider.js");
|
|
63
65
|
var import_ThemeProvider = require("./providers/ThemeProvider.js");
|
|
@@ -65,6 +67,7 @@ var import_MockProvider = require("./providers/MockProvider.js");
|
|
|
65
67
|
var import_utils = require("./lib/utils.js");
|
|
66
68
|
// Annotate the CommonJS export names for ESM import in node:
|
|
67
69
|
0 && (module.exports = {
|
|
70
|
+
AppRegistryForm,
|
|
68
71
|
AppShell,
|
|
69
72
|
Hero,
|
|
70
73
|
Logo,
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["// shadcn/ui components\nexport * from \"./components/ui/avatar.js\";\nexport * from \"./components/ui/badge.js\";\nexport * from \"./components/ui/button.js\";\nexport * from \"./components/ui/card.js\";\nexport * from \"./components/ui/checkbox.js\";\nexport * from \"./components/ui/dialog.js\";\nexport * from \"./components/ui/dropdown-menu.js\";\nexport * from \"./components/ui/input.js\";\nexport * from \"./components/ui/label.js\";\nexport * from \"./components/ui/select.js\";\nexport * from \"./components/ui/separator.js\";\nexport * from \"./components/ui/sheet.js\";\nexport * from \"./components/ui/table.js\";\nexport * from \"./components/ui/tabs.js\";\nexport * from \"./components/ui/textarea.js\";\n\n// Chrome\nexport { Logo } from \"./components/chrome/Logo.js\";\nexport { ThemeSwitcher } from \"./components/chrome/ThemeSwitcher.js\";\nexport { RoleSwitcher } from \"./components/chrome/RoleSwitcher.js\";\n\n// Marketing\nexport { Hero } from \"./components/marketing/Hero.js\";\nexport { MdxContent } from \"./components/marketing/MdxContent.js\";\n\n// Shells\nexport { PublicShell } from \"./components/shells/PublicShell.js\";\nexport type { NavLink } from \"./components/shells/PublicShell.js\";\nexport { AppShell } from \"./components/shells/AppShell.js\";\nexport type {
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["// shadcn/ui components\nexport * from \"./components/ui/avatar.js\";\nexport * from \"./components/ui/badge.js\";\nexport * from \"./components/ui/button.js\";\nexport * from \"./components/ui/card.js\";\nexport * from \"./components/ui/checkbox.js\";\nexport * from \"./components/ui/dialog.js\";\nexport * from \"./components/ui/dropdown-menu.js\";\nexport * from \"./components/ui/input.js\";\nexport * from \"./components/ui/label.js\";\nexport * from \"./components/ui/select.js\";\nexport * from \"./components/ui/separator.js\";\nexport * from \"./components/ui/sheet.js\";\nexport * from \"./components/ui/table.js\";\nexport * from \"./components/ui/tabs.js\";\nexport * from \"./components/ui/textarea.js\";\n\n// Chrome\nexport { Logo } from \"./components/chrome/Logo.js\";\nexport { ThemeSwitcher } from \"./components/chrome/ThemeSwitcher.js\";\nexport { RoleSwitcher } from \"./components/chrome/RoleSwitcher.js\";\n\n// Marketing\nexport { Hero } from \"./components/marketing/Hero.js\";\nexport { MdxContent } from \"./components/marketing/MdxContent.js\";\n\n// Shells\nexport { PublicShell } from \"./components/shells/PublicShell.js\";\nexport type { NavLink } from \"./components/shells/PublicShell.js\";\nexport { AppShell } from \"./components/shells/AppShell.js\";\nexport type {\n AppNavItem,\n AppNavGroup,\n EcosystemApp,\n} from \"./components/shells/AppShell.js\";\nexport { StudioShell } from \"./components/shells/StudioShell.js\";\n\n// Admin\nexport { AppRegistryForm } from \"./components/admin/AppRegistryForm.js\";\nexport type {\n AppRegistryFormValue,\n AppRegistryFormProps,\n} from \"./components/admin/AppRegistryForm.js\";\n\n// Providers\nexport { SessionProvider } from \"./providers/SessionProvider.js\";\nexport { QueryProvider } from \"./providers/QueryProvider.js\";\nexport { ThemeProvider, useThemeName } from \"./providers/ThemeProvider.js\";\nexport { MockProvider } from \"./providers/MockProvider.js\";\n\n// Helpers\nexport { cn } from \"./lib/utils.js\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA,0BAAc,sCADd;AAEA,0BAAc,qCAFd;AAGA,0BAAc,sCAHd;AAIA,0BAAc,oCAJd;AAKA,0BAAc,wCALd;AAMA,0BAAc,sCANd;AAOA,0BAAc,6CAPd;AAQA,0BAAc,qCARd;AASA,0BAAc,qCATd;AAUA,0BAAc,sCAVd;AAWA,0BAAc,yCAXd;AAYA,0BAAc,qCAZd;AAaA,0BAAc,qCAbd;AAcA,0BAAc,oCAdd;AAeA,0BAAc,wCAfd;AAkBA,kBAAqB;AACrB,2BAA8B;AAC9B,0BAA6B;AAG7B,kBAAqB;AACrB,wBAA2B;AAG3B,yBAA4B;AAE5B,sBAAyB;AAMzB,yBAA4B;AAG5B,6BAAgC;AAOhC,6BAAgC;AAChC,2BAA8B;AAC9B,2BAA4C;AAC5C,0BAA6B;AAG7B,mBAAmB;","names":[]}
|
package/dist/index.d.ts
CHANGED
|
@@ -21,8 +21,10 @@ export { MdxContent } from "./components/marketing/MdxContent.js";
|
|
|
21
21
|
export { PublicShell } from "./components/shells/PublicShell.js";
|
|
22
22
|
export type { NavLink } from "./components/shells/PublicShell.js";
|
|
23
23
|
export { AppShell } from "./components/shells/AppShell.js";
|
|
24
|
-
export type { AppNavItem, AppNavGroup } from "./components/shells/AppShell.js";
|
|
24
|
+
export type { AppNavItem, AppNavGroup, EcosystemApp, } from "./components/shells/AppShell.js";
|
|
25
25
|
export { StudioShell } from "./components/shells/StudioShell.js";
|
|
26
|
+
export { AppRegistryForm } from "./components/admin/AppRegistryForm.js";
|
|
27
|
+
export type { AppRegistryFormValue, AppRegistryFormProps, } from "./components/admin/AppRegistryForm.js";
|
|
26
28
|
export { SessionProvider } from "./providers/SessionProvider.js";
|
|
27
29
|
export { QueryProvider } from "./providers/QueryProvider.js";
|
|
28
30
|
export { ThemeProvider, useThemeName } from "./providers/ThemeProvider.js";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,2BAA2B,CAAC;AAC1C,cAAc,0BAA0B,CAAC;AACzC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,yBAAyB,CAAC;AACxC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,kCAAkC,CAAC;AACjD,cAAc,0BAA0B,CAAC;AACzC,cAAc,0BAA0B,CAAC;AACzC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,0BAA0B,CAAC;AACzC,cAAc,0BAA0B,CAAC;AACzC,cAAc,yBAAyB,CAAC;AACxC,cAAc,6BAA6B,CAAC;AAG5C,OAAO,EAAE,IAAI,EAAE,MAAM,6BAA6B,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,sCAAsC,CAAC;AACrE,OAAO,EAAE,YAAY,EAAE,MAAM,qCAAqC,CAAC;AAGnE,OAAO,EAAE,IAAI,EAAE,MAAM,gCAAgC,CAAC;AACtD,OAAO,EAAE,UAAU,EAAE,MAAM,sCAAsC,CAAC;AAGlE,OAAO,EAAE,WAAW,EAAE,MAAM,oCAAoC,CAAC;AACjE,YAAY,EAAE,OAAO,EAAE,MAAM,oCAAoC,CAAC;AAClE,OAAO,EAAE,QAAQ,EAAE,MAAM,iCAAiC,CAAC;AAC3D,YAAY,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,2BAA2B,CAAC;AAC1C,cAAc,0BAA0B,CAAC;AACzC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,yBAAyB,CAAC;AACxC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,kCAAkC,CAAC;AACjD,cAAc,0BAA0B,CAAC;AACzC,cAAc,0BAA0B,CAAC;AACzC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,0BAA0B,CAAC;AACzC,cAAc,0BAA0B,CAAC;AACzC,cAAc,yBAAyB,CAAC;AACxC,cAAc,6BAA6B,CAAC;AAG5C,OAAO,EAAE,IAAI,EAAE,MAAM,6BAA6B,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,sCAAsC,CAAC;AACrE,OAAO,EAAE,YAAY,EAAE,MAAM,qCAAqC,CAAC;AAGnE,OAAO,EAAE,IAAI,EAAE,MAAM,gCAAgC,CAAC;AACtD,OAAO,EAAE,UAAU,EAAE,MAAM,sCAAsC,CAAC;AAGlE,OAAO,EAAE,WAAW,EAAE,MAAM,oCAAoC,CAAC;AACjE,YAAY,EAAE,OAAO,EAAE,MAAM,oCAAoC,CAAC;AAClE,OAAO,EAAE,QAAQ,EAAE,MAAM,iCAAiC,CAAC;AAC3D,YAAY,EACV,UAAU,EACV,WAAW,EACX,YAAY,GACb,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,MAAM,oCAAoC,CAAC;AAGjE,OAAO,EAAE,eAAe,EAAE,MAAM,uCAAuC,CAAC;AACxE,YAAY,EACV,oBAAoB,EACpB,oBAAoB,GACrB,MAAM,uCAAuC,CAAC;AAG/C,OAAO,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AACjE,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAC3E,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAG3D,OAAO,EAAE,EAAE,EAAE,MAAM,gBAAgB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -21,12 +21,14 @@ import { MdxContent } from "./components/marketing/MdxContent.js";
|
|
|
21
21
|
import { PublicShell } from "./components/shells/PublicShell.js";
|
|
22
22
|
import { AppShell } from "./components/shells/AppShell.js";
|
|
23
23
|
import { StudioShell } from "./components/shells/StudioShell.js";
|
|
24
|
+
import { AppRegistryForm } from "./components/admin/AppRegistryForm.js";
|
|
24
25
|
import { SessionProvider } from "./providers/SessionProvider.js";
|
|
25
26
|
import { QueryProvider } from "./providers/QueryProvider.js";
|
|
26
27
|
import { ThemeProvider, useThemeName } from "./providers/ThemeProvider.js";
|
|
27
28
|
import { MockProvider } from "./providers/MockProvider.js";
|
|
28
29
|
import { cn } from "./lib/utils.js";
|
|
29
30
|
export {
|
|
31
|
+
AppRegistryForm,
|
|
30
32
|
AppShell,
|
|
31
33
|
Hero,
|
|
32
34
|
Logo,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["// shadcn/ui components\nexport * from \"./components/ui/avatar.js\";\nexport * from \"./components/ui/badge.js\";\nexport * from \"./components/ui/button.js\";\nexport * from \"./components/ui/card.js\";\nexport * from \"./components/ui/checkbox.js\";\nexport * from \"./components/ui/dialog.js\";\nexport * from \"./components/ui/dropdown-menu.js\";\nexport * from \"./components/ui/input.js\";\nexport * from \"./components/ui/label.js\";\nexport * from \"./components/ui/select.js\";\nexport * from \"./components/ui/separator.js\";\nexport * from \"./components/ui/sheet.js\";\nexport * from \"./components/ui/table.js\";\nexport * from \"./components/ui/tabs.js\";\nexport * from \"./components/ui/textarea.js\";\n\n// Chrome\nexport { Logo } from \"./components/chrome/Logo.js\";\nexport { ThemeSwitcher } from \"./components/chrome/ThemeSwitcher.js\";\nexport { RoleSwitcher } from \"./components/chrome/RoleSwitcher.js\";\n\n// Marketing\nexport { Hero } from \"./components/marketing/Hero.js\";\nexport { MdxContent } from \"./components/marketing/MdxContent.js\";\n\n// Shells\nexport { PublicShell } from \"./components/shells/PublicShell.js\";\nexport type { NavLink } from \"./components/shells/PublicShell.js\";\nexport { AppShell } from \"./components/shells/AppShell.js\";\nexport type {
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["// shadcn/ui components\nexport * from \"./components/ui/avatar.js\";\nexport * from \"./components/ui/badge.js\";\nexport * from \"./components/ui/button.js\";\nexport * from \"./components/ui/card.js\";\nexport * from \"./components/ui/checkbox.js\";\nexport * from \"./components/ui/dialog.js\";\nexport * from \"./components/ui/dropdown-menu.js\";\nexport * from \"./components/ui/input.js\";\nexport * from \"./components/ui/label.js\";\nexport * from \"./components/ui/select.js\";\nexport * from \"./components/ui/separator.js\";\nexport * from \"./components/ui/sheet.js\";\nexport * from \"./components/ui/table.js\";\nexport * from \"./components/ui/tabs.js\";\nexport * from \"./components/ui/textarea.js\";\n\n// Chrome\nexport { Logo } from \"./components/chrome/Logo.js\";\nexport { ThemeSwitcher } from \"./components/chrome/ThemeSwitcher.js\";\nexport { RoleSwitcher } from \"./components/chrome/RoleSwitcher.js\";\n\n// Marketing\nexport { Hero } from \"./components/marketing/Hero.js\";\nexport { MdxContent } from \"./components/marketing/MdxContent.js\";\n\n// Shells\nexport { PublicShell } from \"./components/shells/PublicShell.js\";\nexport type { NavLink } from \"./components/shells/PublicShell.js\";\nexport { AppShell } from \"./components/shells/AppShell.js\";\nexport type {\n AppNavItem,\n AppNavGroup,\n EcosystemApp,\n} from \"./components/shells/AppShell.js\";\nexport { StudioShell } from \"./components/shells/StudioShell.js\";\n\n// Admin\nexport { AppRegistryForm } from \"./components/admin/AppRegistryForm.js\";\nexport type {\n AppRegistryFormValue,\n AppRegistryFormProps,\n} from \"./components/admin/AppRegistryForm.js\";\n\n// Providers\nexport { SessionProvider } from \"./providers/SessionProvider.js\";\nexport { QueryProvider } from \"./providers/QueryProvider.js\";\nexport { ThemeProvider, useThemeName } from \"./providers/ThemeProvider.js\";\nexport { MockProvider } from \"./providers/MockProvider.js\";\n\n// Helpers\nexport { cn } from \"./lib/utils.js\";\n"],"mappings":"AACA,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AAGd,SAAS,YAAY;AACrB,SAAS,qBAAqB;AAC9B,SAAS,oBAAoB;AAG7B,SAAS,YAAY;AACrB,SAAS,kBAAkB;AAG3B,SAAS,mBAAmB;AAE5B,SAAS,gBAAgB;AAMzB,SAAS,mBAAmB;AAG5B,SAAS,uBAAuB;AAOhC,SAAS,uBAAuB;AAChC,SAAS,qBAAqB;AAC9B,SAAS,eAAe,oBAAoB;AAC5C,SAAS,oBAAoB;AAG7B,SAAS,UAAU;","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@augmenting-integrations/ui",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.1.3",
|
|
4
4
|
"description": "Shared UI: shadcn components, brand chrome (Logo, ThemeSwitcher, RoleSwitcher), shells (PublicShell, AppShell, StudioShell), and providers (Session, Query, Theme, Mock).",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"publishConfig": {
|
|
@@ -21,6 +21,11 @@
|
|
|
21
21
|
"dist",
|
|
22
22
|
"README.md"
|
|
23
23
|
],
|
|
24
|
+
"scripts": {
|
|
25
|
+
"build": "tsup",
|
|
26
|
+
"clean": "rm -rf dist",
|
|
27
|
+
"test": "vitest run --passWithNoTests"
|
|
28
|
+
},
|
|
24
29
|
"dependencies": {
|
|
25
30
|
"class-variance-authority": "^0.7.1",
|
|
26
31
|
"clsx": "^2.1.1",
|
|
@@ -31,17 +36,19 @@
|
|
|
31
36
|
"tailwind-merge": "^3.5.0"
|
|
32
37
|
},
|
|
33
38
|
"peerDependencies": {
|
|
39
|
+
"@augmenting-integrations/brand": "workspace:*",
|
|
40
|
+
"@augmenting-integrations/themes": "workspace:*",
|
|
34
41
|
"@tanstack/react-query": "^5.0.0",
|
|
35
42
|
"msw": "^2.0.0",
|
|
36
43
|
"next": "^16.0.0",
|
|
37
44
|
"next-auth": "^5.0.0-beta.31",
|
|
38
45
|
"next-themes": "^0.4.0",
|
|
39
46
|
"react": "^19.0.0",
|
|
40
|
-
"react-dom": "^19.0.0"
|
|
41
|
-
"@augmenting-integrations/themes": "4.0.2",
|
|
42
|
-
"@augmenting-integrations/brand": "4.0.2"
|
|
47
|
+
"react-dom": "^19.0.0"
|
|
43
48
|
},
|
|
44
49
|
"devDependencies": {
|
|
50
|
+
"@augmenting-integrations/brand": "workspace:*",
|
|
51
|
+
"@augmenting-integrations/themes": "workspace:*",
|
|
45
52
|
"@tanstack/react-query": "^5.100.9",
|
|
46
53
|
"@types/react": "^19.0.0",
|
|
47
54
|
"@types/react-dom": "^19.0.0",
|
|
@@ -53,13 +60,6 @@
|
|
|
53
60
|
"react-dom": "^19.0.0",
|
|
54
61
|
"tsup": "^8.3.5",
|
|
55
62
|
"typescript": "^5.7.2",
|
|
56
|
-
"vitest": "^4.1.5"
|
|
57
|
-
"@augmenting-integrations/brand": "4.0.2",
|
|
58
|
-
"@augmenting-integrations/themes": "4.0.2"
|
|
59
|
-
},
|
|
60
|
-
"scripts": {
|
|
61
|
-
"build": "tsup",
|
|
62
|
-
"clean": "rm -rf dist",
|
|
63
|
-
"test": "vitest run --passWithNoTests"
|
|
63
|
+
"vitest": "^4.1.5"
|
|
64
64
|
}
|
|
65
|
-
}
|
|
65
|
+
}
|
package/LICENSE
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2026 Augmenting Integrations LLC
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|