@burdenoff/microfe-vibecontrols 2026.520.1 → 2026.520.2
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/agents/AgentForm.js +152 -148
- package/dist/components/agents/AgentForm.js.map +1 -1
- package/dist/components/catalog/CatalogForm.js +46 -42
- package/dist/components/catalog/CatalogForm.js.map +1 -1
- package/dist/components/idp-components/ComponentForm.js +69 -65
- package/dist/components/idp-components/ComponentForm.js.map +1 -1
- package/dist/components/shared/EntityTagPicker.js +5 -1
- package/dist/components/shared/EntityTagPicker.js.map +1 -1
- package/dist/components/tunnels/TunnelCreateWizard.js +45 -54
- package/dist/components/tunnels/TunnelCreateWizard.js.map +1 -1
- package/dist/hooks/useDraftEntityTags.js +29 -0
- package/dist/hooks/useDraftEntityTags.js.map +1 -0
- package/dist/pages/ActionsPage.js +109 -96
- package/dist/pages/ActionsPage.js.map +1 -1
- package/dist/pages/AgentsPage.js +345 -336
- package/dist/pages/AgentsPage.js.map +1 -1
- package/dist/pages/CatalogPage.js +107 -97
- package/dist/pages/CatalogPage.js.map +1 -1
- package/dist/pages/ComponentsPage.js +126 -116
- package/dist/pages/ComponentsPage.js.map +1 -1
- package/dist/pages/SessionsPage.js +598 -586
- package/dist/pages/SessionsPage.js.map +1 -1
- package/dist/pages/TargetsPage.js +499 -485
- package/dist/pages/TargetsPage.js.map +1 -1
- package/dist/pages/VibeDeckPage.js +310 -300
- package/dist/pages/VibeDeckPage.js.map +1 -1
- package/dist/pages/VibesPage.js +309 -296
- package/dist/pages/VibesPage.js.map +1 -1
- package/package.json +1 -1
|
@@ -3,94 +3,96 @@ import { useVibeNavigate as n } from "../providers/VibeControlsProvider.js";
|
|
|
3
3
|
import { PageHeader as r } from "../components/shared/PageHeader.js";
|
|
4
4
|
import { LoadingState as i } from "../components/shared/LoadingState.js";
|
|
5
5
|
import { ErrorState as a } from "../components/shared/ErrorState.js";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
6
|
+
import { EntityTagPicker as o } from "../components/shared/EntityTagPicker.js";
|
|
7
|
+
import { useDraftEntityTags as s } from "../hooks/useDraftEntityTags.js";
|
|
8
|
+
import { EmptyState as c } from "../components/shared/EmptyState.js";
|
|
9
|
+
import { SearchInput as l } from "../components/shared/SearchInput.js";
|
|
10
|
+
import { FilterDropdown as u } from "../components/shared/FilterDropdown.js";
|
|
11
|
+
import { GetHelpButton as d } from "../components/shared/GetHelpButton.js";
|
|
10
12
|
import "../components/shared/index.js";
|
|
11
|
-
import { VIBE_PERMISSIONS as
|
|
12
|
-
import { ComponentCard as
|
|
13
|
-
import { ComponentForm as
|
|
13
|
+
import { VIBE_PERMISSIONS as f } from "../constants/permissions.js";
|
|
14
|
+
import { ComponentCard as p } from "../components/idp-components/ComponentCard.js";
|
|
15
|
+
import { ComponentForm as m } from "../components/idp-components/ComponentForm.js";
|
|
14
16
|
import "../components/idp-components/index.js";
|
|
15
|
-
import { useCallback as
|
|
16
|
-
import { useI18n as
|
|
17
|
-
import { Box as
|
|
18
|
-
import { usePermissions as
|
|
19
|
-
import { AnimatePresence as
|
|
20
|
-
import { jsx as
|
|
21
|
-
import { MotionItem as
|
|
17
|
+
import { useCallback as h, useMemo as g, useState as _ } from "react";
|
|
18
|
+
import { useI18n as ee } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
19
|
+
import { Box as v, Plus as y, RefreshCw as b } from "lucide-react";
|
|
20
|
+
import { usePermissions as x } from "@burdenoff/fe-libs/shared/providers/shell";
|
|
21
|
+
import { AnimatePresence as S } from "framer-motion";
|
|
22
|
+
import { jsx as C, jsxs as w } from "react/jsx-runtime";
|
|
23
|
+
import { MotionItem as T, MotionList as E, MotionPage as D } from "@burdenoff/fe-libs/shared/motion";
|
|
22
24
|
//#region src/pages/ComponentsPage.tsx
|
|
23
|
-
function
|
|
24
|
-
let
|
|
25
|
-
let r =
|
|
25
|
+
function O() {
|
|
26
|
+
let O = n(), { t: k } = ee(), { hasPermission: A, isLoading: j } = x(), M = h((e, t, n) => {
|
|
27
|
+
let r = k(e, n), i = r === e ? t : r;
|
|
26
28
|
return n ? Object.entries(n).reduce((e, [t, n]) => e.replace(`{{${t}}}`, String(n)), i) : i;
|
|
27
|
-
}, [
|
|
29
|
+
}, [k]), N = [
|
|
28
30
|
{
|
|
29
31
|
value: "all",
|
|
30
|
-
label:
|
|
32
|
+
label: M("component.allTypes", "All Types")
|
|
31
33
|
},
|
|
32
34
|
{
|
|
33
35
|
value: "SERVICE",
|
|
34
|
-
label:
|
|
36
|
+
label: M("component.typeService", "Service")
|
|
35
37
|
},
|
|
36
38
|
{
|
|
37
39
|
value: "FRONTEND",
|
|
38
|
-
label:
|
|
40
|
+
label: M("component.typeFrontend", "Frontend")
|
|
39
41
|
},
|
|
40
42
|
{
|
|
41
43
|
value: "LIBRARY",
|
|
42
|
-
label:
|
|
44
|
+
label: M("component.typeLibrary", "Library")
|
|
43
45
|
},
|
|
44
46
|
{
|
|
45
47
|
value: "API",
|
|
46
|
-
label:
|
|
48
|
+
label: M("component.typeApi", "API")
|
|
47
49
|
},
|
|
48
50
|
{
|
|
49
51
|
value: "MOBILE",
|
|
50
|
-
label:
|
|
52
|
+
label: M("component.typeMobile", "Mobile")
|
|
51
53
|
},
|
|
52
54
|
{
|
|
53
55
|
value: "DATABASE",
|
|
54
|
-
label:
|
|
56
|
+
label: M("component.typeDatabase", "Database")
|
|
55
57
|
},
|
|
56
58
|
{
|
|
57
59
|
value: "INFRASTRUCTURE",
|
|
58
|
-
label:
|
|
60
|
+
label: M("component.typeInfrastructure", "Infrastructure")
|
|
59
61
|
},
|
|
60
62
|
{
|
|
61
63
|
value: "CUSTOM",
|
|
62
|
-
label:
|
|
64
|
+
label: M("component.typeCustom", "Custom")
|
|
63
65
|
}
|
|
64
|
-
],
|
|
66
|
+
], P = [
|
|
65
67
|
{
|
|
66
68
|
value: "all",
|
|
67
|
-
label:
|
|
69
|
+
label: M("component.allLifecycles", "All Lifecycles")
|
|
68
70
|
},
|
|
69
71
|
{
|
|
70
72
|
value: "EXPERIMENTAL",
|
|
71
|
-
label:
|
|
73
|
+
label: M("component.lifecycleExperimental", "Experimental")
|
|
72
74
|
},
|
|
73
75
|
{
|
|
74
76
|
value: "PRODUCTION",
|
|
75
|
-
label:
|
|
77
|
+
label: M("component.lifecycleProduction", "Production")
|
|
76
78
|
},
|
|
77
79
|
{
|
|
78
80
|
value: "DEPRECATED",
|
|
79
|
-
label:
|
|
81
|
+
label: M("component.lifecycleDeprecated", "Deprecated")
|
|
80
82
|
}
|
|
81
|
-
], [
|
|
83
|
+
], [F, I] = _(""), [L, R] = _("all"), [z, B] = _("all"), [V, H] = _(!1), [U, W] = _(null), { data: G, loading: K, error: q, refetch: J } = t({
|
|
82
84
|
variables: {
|
|
83
85
|
filter: {
|
|
84
|
-
type:
|
|
85
|
-
lifecycle:
|
|
86
|
+
type: L === "all" ? void 0 : L,
|
|
87
|
+
lifecycle: z === "all" ? void 0 : z
|
|
86
88
|
},
|
|
87
|
-
...
|
|
89
|
+
...F ? { pagination: { limit: 1e3 } } : {}
|
|
88
90
|
},
|
|
89
91
|
fetchPolicy: "cache-and-network"
|
|
90
|
-
}), [
|
|
92
|
+
}), [Y] = e({
|
|
91
93
|
refetchQueries: ["ListComponents"],
|
|
92
94
|
awaitRefetchQueries: !0
|
|
93
|
-
}),
|
|
95
|
+
}), { setDraftTagIds: X, flushTags: Z } = s("VIBECONTROLS_COMPONENT"), Q = g(() => (G?.vibecontrolsComponents?.items ?? []).map((e) => ({
|
|
94
96
|
id: e.id,
|
|
95
97
|
name: e.name,
|
|
96
98
|
description: e.description || null,
|
|
@@ -102,132 +104,132 @@ function D() {
|
|
|
102
104
|
tags: e.tags || [],
|
|
103
105
|
createdAt: e.createdAt,
|
|
104
106
|
updatedAt: e.updatedAt
|
|
105
|
-
})), [
|
|
106
|
-
if (!
|
|
107
|
-
let e =
|
|
108
|
-
return
|
|
109
|
-
}, [
|
|
110
|
-
if (
|
|
107
|
+
})), [G?.vibecontrolsComponents?.items]), $ = g(() => {
|
|
108
|
+
if (!F) return Q;
|
|
109
|
+
let e = F.toLowerCase();
|
|
110
|
+
return Q.filter((t) => t.name.toLowerCase().includes(e) || t.description?.toLowerCase().includes(e) || t.language?.toLowerCase().includes(e) || t.framework?.toLowerCase().includes(e) || t.tags?.some((t) => t.toLowerCase().includes(e)));
|
|
111
|
+
}, [Q, F]);
|
|
112
|
+
if (j) return /* @__PURE__ */ C("div", {
|
|
111
113
|
className: "flex items-center justify-center min-h-[400px]",
|
|
112
|
-
children: /* @__PURE__ */
|
|
114
|
+
children: /* @__PURE__ */ C("p", {
|
|
113
115
|
className: "text-text-secondary",
|
|
114
116
|
children: "Loading permissions..."
|
|
115
117
|
})
|
|
116
118
|
});
|
|
117
|
-
if (!
|
|
119
|
+
if (!A(f.VIBE_LIST)) return /* @__PURE__ */ C("div", {
|
|
118
120
|
className: "flex items-center justify-center min-h-[400px]",
|
|
119
|
-
children: /* @__PURE__ */
|
|
121
|
+
children: /* @__PURE__ */ w("div", {
|
|
120
122
|
className: "text-center space-y-2",
|
|
121
|
-
children: [/* @__PURE__ */
|
|
123
|
+
children: [/* @__PURE__ */ C("h2", {
|
|
122
124
|
className: "text-lg font-semibold text-foreground",
|
|
123
125
|
children: "Access Denied"
|
|
124
|
-
}), /* @__PURE__ */
|
|
126
|
+
}), /* @__PURE__ */ C("p", {
|
|
125
127
|
className: "text-sm text-text-secondary",
|
|
126
128
|
children: "You don't have permission to access this page."
|
|
127
129
|
})]
|
|
128
130
|
})
|
|
129
131
|
});
|
|
130
|
-
let
|
|
131
|
-
|
|
132
|
+
let te = (e) => {
|
|
133
|
+
O(`/components/${e.id}`);
|
|
132
134
|
};
|
|
133
|
-
return
|
|
135
|
+
return K && Q.length === 0 ? /* @__PURE__ */ C(D, {
|
|
134
136
|
className: "space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen",
|
|
135
|
-
children: /* @__PURE__ */
|
|
136
|
-
}) :
|
|
137
|
+
children: /* @__PURE__ */ C(i, { message: M("component.loading", "Loading components...") })
|
|
138
|
+
}) : q && Q.length === 0 ? /* @__PURE__ */ C(D, {
|
|
137
139
|
className: "space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen",
|
|
138
|
-
children: /* @__PURE__ */
|
|
139
|
-
title:
|
|
140
|
-
message:
|
|
141
|
-
onRetry: () =>
|
|
140
|
+
children: /* @__PURE__ */ C(a, {
|
|
141
|
+
title: M("component.failedLoad", "Failed to load components"),
|
|
142
|
+
message: q.message,
|
|
143
|
+
onRetry: () => J()
|
|
142
144
|
})
|
|
143
|
-
}) : /* @__PURE__ */
|
|
145
|
+
}) : /* @__PURE__ */ w(D, {
|
|
144
146
|
className: "space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen",
|
|
145
147
|
children: [
|
|
146
|
-
/* @__PURE__ */
|
|
147
|
-
title:
|
|
148
|
-
description:
|
|
149
|
-
actions: /* @__PURE__ */
|
|
148
|
+
/* @__PURE__ */ C(r, {
|
|
149
|
+
title: M("component.title", "Components"),
|
|
150
|
+
description: M("component.description", "{{total}} components", { total: Q.length }),
|
|
151
|
+
actions: /* @__PURE__ */ w("div", {
|
|
150
152
|
className: "flex items-center gap-2",
|
|
151
|
-
children: [/* @__PURE__ */
|
|
152
|
-
onClick: () =>
|
|
153
|
-
disabled:
|
|
153
|
+
children: [/* @__PURE__ */ C("button", {
|
|
154
|
+
onClick: () => J(),
|
|
155
|
+
disabled: K,
|
|
154
156
|
className: "flex items-center gap-2 px-3 py-2 text-text-secondary hover:bg-bg-sunken rounded-md transition-colors disabled:opacity-50",
|
|
155
|
-
title:
|
|
156
|
-
children: /* @__PURE__ */
|
|
157
|
-
}), /* @__PURE__ */
|
|
158
|
-
onClick: () =>
|
|
157
|
+
title: M("common.refresh", "Refresh"),
|
|
158
|
+
children: /* @__PURE__ */ C(b, { className: `w-4 h-4 ${K ? "animate-spin" : ""}` })
|
|
159
|
+
}), /* @__PURE__ */ w("button", {
|
|
160
|
+
onClick: () => H(!0),
|
|
159
161
|
className: "flex items-center gap-2 px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors",
|
|
160
|
-
children: [/* @__PURE__ */
|
|
162
|
+
children: [/* @__PURE__ */ C(y, { className: "w-4 h-4" }), M("component.createComponent", "Create Component")]
|
|
161
163
|
})]
|
|
162
164
|
})
|
|
163
165
|
}),
|
|
164
|
-
/* @__PURE__ */
|
|
166
|
+
/* @__PURE__ */ w("div", {
|
|
165
167
|
className: "space-y-3",
|
|
166
|
-
children: [/* @__PURE__ */
|
|
168
|
+
children: [/* @__PURE__ */ C("div", {
|
|
167
169
|
className: "flex items-center gap-2",
|
|
168
|
-
children: /* @__PURE__ */
|
|
169
|
-
value:
|
|
170
|
-
onChange:
|
|
171
|
-
placeholder:
|
|
170
|
+
children: /* @__PURE__ */ C(l, {
|
|
171
|
+
value: F,
|
|
172
|
+
onChange: I,
|
|
173
|
+
placeholder: M("component.searchPlaceholder", "Search components..."),
|
|
172
174
|
className: "flex-1 sm:w-64 sm:flex-none"
|
|
173
175
|
})
|
|
174
|
-
}), /* @__PURE__ */
|
|
176
|
+
}), /* @__PURE__ */ w("div", {
|
|
175
177
|
className: "flex items-center gap-2 sm:gap-3 flex-wrap",
|
|
176
|
-
children: [/* @__PURE__ */
|
|
177
|
-
label:
|
|
178
|
-
value:
|
|
179
|
-
options: M,
|
|
180
|
-
onChange: L
|
|
181
|
-
}), /* @__PURE__ */ S(c, {
|
|
182
|
-
label: j("component.filterLifecycle", "Lifecycle"),
|
|
183
|
-
value: R,
|
|
178
|
+
children: [/* @__PURE__ */ C(u, {
|
|
179
|
+
label: M("component.filterType", "Type"),
|
|
180
|
+
value: L,
|
|
184
181
|
options: N,
|
|
185
|
-
onChange:
|
|
182
|
+
onChange: R
|
|
183
|
+
}), /* @__PURE__ */ C(u, {
|
|
184
|
+
label: M("component.filterLifecycle", "Lifecycle"),
|
|
185
|
+
value: z,
|
|
186
|
+
options: P,
|
|
187
|
+
onChange: B
|
|
186
188
|
})]
|
|
187
189
|
})]
|
|
188
190
|
}),
|
|
189
|
-
(
|
|
191
|
+
(F || L !== "all" || z !== "all") && /* @__PURE__ */ w("p", {
|
|
190
192
|
className: "text-sm text-text-secondary",
|
|
191
|
-
children: [
|
|
192
|
-
filtered:
|
|
193
|
-
total:
|
|
194
|
-
}),
|
|
193
|
+
children: [M("component.showingResults", "Showing {{filtered}} of {{total}} components", {
|
|
194
|
+
filtered: $.length,
|
|
195
|
+
total: Q.length
|
|
196
|
+
}), K && /* @__PURE__ */ C("span", {
|
|
195
197
|
className: "ml-2 text-text-muted",
|
|
196
|
-
children:
|
|
198
|
+
children: M("common.refreshing", "(refreshing...)")
|
|
197
199
|
})]
|
|
198
200
|
}),
|
|
199
|
-
|
|
201
|
+
$.length > 0 ? /* @__PURE__ */ C(S, {
|
|
200
202
|
mode: "wait",
|
|
201
|
-
children: /* @__PURE__ */
|
|
203
|
+
children: /* @__PURE__ */ C(E, {
|
|
202
204
|
stagger: .04,
|
|
203
205
|
className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4",
|
|
204
|
-
children:
|
|
206
|
+
children: $.map((e) => /* @__PURE__ */ C(T, { children: /* @__PURE__ */ C(p, {
|
|
205
207
|
component: e,
|
|
206
|
-
onOpen:
|
|
208
|
+
onOpen: te
|
|
207
209
|
}) }, e.id))
|
|
208
210
|
}, "component-grid")
|
|
209
|
-
}) : /* @__PURE__ */
|
|
210
|
-
icon: /* @__PURE__ */
|
|
211
|
-
title:
|
|
212
|
-
description:
|
|
213
|
-
action: !
|
|
211
|
+
}) : /* @__PURE__ */ C(c, {
|
|
212
|
+
icon: /* @__PURE__ */ C(v, { className: "w-8 h-8 text-text-secondary" }),
|
|
213
|
+
title: F || L !== "all" || z !== "all" ? M("component.emptyNoResults", "No components found") : M("component.emptyNoComponents", "No components yet"),
|
|
214
|
+
description: F || L !== "all" || z !== "all" ? M("component.emptyAdjustSearch", "Try adjusting your search or filters") : M("component.emptyCreate", "Register your first component to start tracking your software catalog"),
|
|
215
|
+
action: !F && L === "all" && z === "all" ? /* @__PURE__ */ w("div", {
|
|
214
216
|
className: "flex items-center gap-2",
|
|
215
|
-
children: [/* @__PURE__ */
|
|
216
|
-
onClick: () =>
|
|
217
|
+
children: [/* @__PURE__ */ w("button", {
|
|
218
|
+
onClick: () => H(!0),
|
|
217
219
|
className: "flex items-center gap-2 px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors",
|
|
218
|
-
children: [/* @__PURE__ */
|
|
219
|
-
}), /* @__PURE__ */
|
|
220
|
+
children: [/* @__PURE__ */ C(y, { className: "w-4 h-4" }), M("component.createComponent", "Create Component")]
|
|
221
|
+
}), /* @__PURE__ */ C(d, { prompt: "What is a Component in VibeControls and how do I add one?" })]
|
|
220
222
|
}) : void 0
|
|
221
223
|
}),
|
|
222
|
-
|
|
224
|
+
U && /* @__PURE__ */ C("div", {
|
|
223
225
|
className: "fixed bottom-4 right-4 z-50 px-4 py-3 bg-status-success-bg text-status-success-text rounded-md shadow-lg text-sm",
|
|
224
|
-
children:
|
|
226
|
+
children: U
|
|
225
227
|
}),
|
|
226
|
-
/* @__PURE__ */
|
|
227
|
-
isOpen:
|
|
228
|
-
onClose: () =>
|
|
228
|
+
/* @__PURE__ */ C(m, {
|
|
229
|
+
isOpen: V,
|
|
230
|
+
onClose: () => H(!1),
|
|
229
231
|
onSubmit: async (e) => {
|
|
230
|
-
|
|
232
|
+
let t = await Y({ variables: { input: {
|
|
231
233
|
name: e.name,
|
|
232
234
|
description: e.description || void 0,
|
|
233
235
|
type: e.type,
|
|
@@ -237,14 +239,22 @@ function D() {
|
|
|
237
239
|
version: e.version || void 0,
|
|
238
240
|
owner: e.owner || void 0,
|
|
239
241
|
tags: e.tags.length > 0 ? e.tags : void 0
|
|
240
|
-
} } })
|
|
241
|
-
|
|
242
|
-
|
|
242
|
+
} } });
|
|
243
|
+
if (!t.data?.createVibecontrolsComponent?.id) throw Error(M("component.failedCreate", "Failed to create component"));
|
|
244
|
+
await Z(t.data.createVibecontrolsComponent.id), await J(), H(!1), W(M("component.createdSuccess", "Component created successfully")), setTimeout(() => W(null), 3e3);
|
|
245
|
+
},
|
|
246
|
+
extraSection: /* @__PURE__ */ w("div", { children: [/* @__PURE__ */ C("h3", {
|
|
247
|
+
className: "text-sm font-medium text-text-secondary mb-2",
|
|
248
|
+
children: M("component.tags", "Tags")
|
|
249
|
+
}), /* @__PURE__ */ C(o, {
|
|
250
|
+
entityType: "VIBECONTROLS_COMPONENT",
|
|
251
|
+
onDraftChange: X
|
|
252
|
+
})] })
|
|
243
253
|
})
|
|
244
254
|
]
|
|
245
255
|
});
|
|
246
256
|
}
|
|
247
257
|
//#endregion
|
|
248
|
-
export {
|
|
258
|
+
export { O as ComponentsPage };
|
|
249
259
|
|
|
250
260
|
//# sourceMappingURL=ComponentsPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ComponentsPage.js","names":[],"sources":["../../src/pages/ComponentsPage.tsx"],"sourcesContent":["import { useState, useMemo, useCallback } from 'react';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { VIBE_PERMISSIONS } from '../constants/permissions';\nimport { Plus, Box, RefreshCw } from 'lucide-react';\nimport { ComponentCard } from '@/components/idp-components';\nimport type { ComponentCardData } from '@/components/idp-components';\nimport { ComponentForm } from '@/components/idp-components';\nimport type { ComponentFormData } from '@/components/idp-components';\nimport {\n PageHeader,\n SearchInput,\n FilterDropdown,\n EmptyState,\n LoadingState,\n ErrorState,\n GetHelpButton,\n} from '@/components/shared';\nimport { MotionPage, MotionList, MotionItem } from '@burdenoff/fe-libs/shared/motion';\nimport { AnimatePresence } from 'framer-motion';\nimport { useVibeNavigate } from '@/providers/VibeControlsProvider';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { useListComponentsQuery, useCreateComponentMutation } from '@/generated/wspace-operations';\n\nexport function ComponentsPage() {\n const navigate = useVibeNavigate();\n const { t } = useI18n();\n const { hasPermission, isLoading: permissionsLoading } = usePermissions();\n\n const tr = useCallback(\n (key: string, fallback: string, params?: Record<string, string | number>): string => {\n const translated = t(key, params);\n const template = translated === key ? fallback : translated;\n if (!params) return template;\n return Object.entries(params).reduce(\n (acc, [paramKey, paramValue]) => acc.replace(`{{${paramKey}}}`, String(paramValue)),\n template\n );\n },\n [t]\n );\n\n const typeOptions = [\n { value: 'all', label: tr('component.allTypes', 'All Types') },\n { value: 'SERVICE', label: tr('component.typeService', 'Service') },\n { value: 'FRONTEND', label: tr('component.typeFrontend', 'Frontend') },\n { value: 'LIBRARY', label: tr('component.typeLibrary', 'Library') },\n { value: 'API', label: tr('component.typeApi', 'API') },\n { value: 'MOBILE', label: tr('component.typeMobile', 'Mobile') },\n { value: 'DATABASE', label: tr('component.typeDatabase', 'Database') },\n { value: 'INFRASTRUCTURE', label: tr('component.typeInfrastructure', 'Infrastructure') },\n { value: 'CUSTOM', label: tr('component.typeCustom', 'Custom') },\n ];\n\n const lifecycleOptions = [\n { value: 'all', label: tr('component.allLifecycles', 'All Lifecycles') },\n { value: 'EXPERIMENTAL', label: tr('component.lifecycleExperimental', 'Experimental') },\n { value: 'PRODUCTION', label: tr('component.lifecycleProduction', 'Production') },\n { value: 'DEPRECATED', label: tr('component.lifecycleDeprecated', 'Deprecated') },\n ];\n\n const [searchQuery, setSearchQuery] = useState('');\n const [typeFilter, setTypeFilter] = useState('all');\n const [lifecycleFilter, setLifecycleFilter] = useState('all');\n const [showCreateForm, setShowCreateForm] = useState(false);\n const [successMessage, setSuccessMessage] = useState<string | null>(null);\n\n const { data, loading, error, refetch } = useListComponentsQuery({\n variables: {\n filter: {\n type: typeFilter !== 'all' ? typeFilter : undefined,\n lifecycle: lifecycleFilter !== 'all' ? lifecycleFilter : undefined,\n },\n ...(searchQuery ? { pagination: { limit: 1000 } } : {}),\n },\n fetchPolicy: 'cache-and-network',\n });\n\n const [createComponent] = useCreateComponentMutation({\n refetchQueries: ['ListComponents'],\n awaitRefetchQueries: true,\n });\n\n const components: ComponentCardData[] = useMemo(() => {\n const items = data?.vibecontrolsComponents?.items ?? [];\n return items.map((item: Record<string, unknown>) => ({\n id: item.id as string,\n name: item.name as string,\n description: (item.description as string) || null,\n type: item.type as ComponentCardData['type'],\n lifecycle: item.lifecycle as ComponentCardData['lifecycle'],\n version: (item.version as string) || null,\n language: (item.language as string) || null,\n framework: (item.framework as string) || null,\n tags: (item.tags as string[]) || [],\n createdAt: item.createdAt as string,\n updatedAt: item.updatedAt as string,\n }));\n }, [data?.vibecontrolsComponents?.items]);\n\n const filteredComponents = useMemo(() => {\n if (!searchQuery) return components;\n const query = searchQuery.toLowerCase();\n return components.filter(\n (c) =>\n c.name.toLowerCase().includes(query) ||\n c.description?.toLowerCase().includes(query) ||\n c.language?.toLowerCase().includes(query) ||\n c.framework?.toLowerCase().includes(query) ||\n c.tags?.some((tag) => tag.toLowerCase().includes(query))\n );\n }, [components, searchQuery]);\n\n if (permissionsLoading)\n return (\n <div className=\"flex items-center justify-center min-h-[400px]\">\n <p className=\"text-text-secondary\">Loading permissions...</p>\n </div>\n );\n if (!hasPermission(VIBE_PERMISSIONS.VIBE_LIST))\n return (\n <div className=\"flex items-center justify-center min-h-[400px]\">\n <div className=\"text-center space-y-2\">\n <h2 className=\"text-lg font-semibold text-foreground\">Access Denied</h2>\n <p className=\"text-sm text-text-secondary\">\n You don't have permission to access this page.\n </p>\n </div>\n </div>\n );\n\n const handleComponentSelect = (component: ComponentCardData) => {\n navigate(`/components/${component.id}`);\n };\n\n const handleCreateComponent = async (formData: ComponentFormData) => {\n const result = await createComponent({\n variables: {\n input: {\n name: formData.name,\n description: formData.description || undefined,\n type: formData.type,\n lifecycle: formData.lifecycle,\n language: formData.language || undefined,\n framework: formData.framework || undefined,\n version: formData.version || undefined,\n owner: formData.owner || undefined,\n tags: formData.tags.length > 0 ? formData.tags : undefined,\n },\n },\n });\n if (!result.data?.createVibecontrolsComponent?.id) {\n throw new Error(tr('component.failedCreate', 'Failed to create component'));\n }\n await refetch();\n setShowCreateForm(false);\n setSuccessMessage(tr('component.createdSuccess', 'Component created successfully'));\n setTimeout(() => setSuccessMessage(null), 3000);\n };\n\n if (loading && components.length === 0) {\n return (\n <MotionPage className=\"space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen\">\n <LoadingState message={tr('component.loading', 'Loading components...')} />\n </MotionPage>\n );\n }\n\n if (error && components.length === 0) {\n return (\n <MotionPage className=\"space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen\">\n <ErrorState\n title={tr('component.failedLoad', 'Failed to load components')}\n message={error.message}\n onRetry={() => refetch()}\n />\n </MotionPage>\n );\n }\n\n return (\n <MotionPage className=\"space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen\">\n <PageHeader\n title={tr('component.title', 'Components')}\n description={tr('component.description', '{{total}} components', {\n total: components.length,\n })}\n actions={\n <div className=\"flex items-center gap-2\">\n <button\n onClick={() => refetch()}\n disabled={loading}\n className=\"flex items-center gap-2 px-3 py-2 text-text-secondary hover:bg-bg-sunken rounded-md transition-colors disabled:opacity-50\"\n title={tr('common.refresh', 'Refresh')}\n >\n <RefreshCw className={`w-4 h-4 ${loading ? 'animate-spin' : ''}`} />\n </button>\n <button\n onClick={() => setShowCreateForm(true)}\n className=\"flex items-center gap-2 px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors\"\n >\n <Plus className=\"w-4 h-4\" />\n {tr('component.createComponent', 'Create Component')}\n </button>\n </div>\n }\n />\n\n {/* Filters */}\n <div className=\"space-y-3\">\n <div className=\"flex items-center gap-2\">\n <SearchInput\n value={searchQuery}\n onChange={setSearchQuery}\n placeholder={tr('component.searchPlaceholder', 'Search components...')}\n className=\"flex-1 sm:w-64 sm:flex-none\"\n />\n </div>\n <div className=\"flex items-center gap-2 sm:gap-3 flex-wrap\">\n <FilterDropdown\n label={tr('component.filterType', 'Type')}\n value={typeFilter}\n options={typeOptions}\n onChange={setTypeFilter}\n />\n <FilterDropdown\n label={tr('component.filterLifecycle', 'Lifecycle')}\n value={lifecycleFilter}\n options={lifecycleOptions}\n onChange={setLifecycleFilter}\n />\n </div>\n </div>\n\n {/* Results count */}\n {(searchQuery || typeFilter !== 'all' || lifecycleFilter !== 'all') && (\n <p className=\"text-sm text-text-secondary\">\n {tr('component.showingResults', 'Showing {{filtered}} of {{total}} components', {\n filtered: filteredComponents.length,\n total: components.length,\n })}\n {loading && (\n <span className=\"ml-2 text-text-muted\">\n {tr('common.refreshing', '(refreshing...)')}\n </span>\n )}\n </p>\n )}\n\n {/* Grid */}\n {filteredComponents.length > 0 ? (\n <AnimatePresence mode=\"wait\">\n <MotionList\n key=\"component-grid\"\n stagger={0.04}\n className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4\"\n >\n {filteredComponents.map((component) => (\n <MotionItem key={component.id}>\n <ComponentCard component={component} onOpen={handleComponentSelect} />\n </MotionItem>\n ))}\n </MotionList>\n </AnimatePresence>\n ) : (\n <EmptyState\n icon={<Box className=\"w-8 h-8 text-text-secondary\" />}\n title={\n searchQuery || typeFilter !== 'all' || lifecycleFilter !== 'all'\n ? tr('component.emptyNoResults', 'No components found')\n : tr('component.emptyNoComponents', 'No components yet')\n }\n description={\n searchQuery || typeFilter !== 'all' || lifecycleFilter !== 'all'\n ? tr('component.emptyAdjustSearch', 'Try adjusting your search or filters')\n : tr(\n 'component.emptyCreate',\n 'Register your first component to start tracking your software catalog'\n )\n }\n action={\n !searchQuery && typeFilter === 'all' && lifecycleFilter === 'all' ? (\n <div className=\"flex items-center gap-2\">\n <button\n onClick={() => setShowCreateForm(true)}\n className=\"flex items-center gap-2 px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors\"\n >\n <Plus className=\"w-4 h-4\" />\n {tr('component.createComponent', 'Create Component')}\n </button>\n <GetHelpButton prompt=\"What is a Component in VibeControls and how do I add one?\" />\n </div>\n ) : undefined\n }\n />\n )}\n\n {/* Success Toast */}\n {successMessage && (\n <div className=\"fixed bottom-4 right-4 z-50 px-4 py-3 bg-status-success-bg text-status-success-text rounded-md shadow-lg text-sm\">\n {successMessage}\n </div>\n )}\n\n {/* Create Form */}\n <ComponentForm\n isOpen={showCreateForm}\n onClose={() => setShowCreateForm(false)}\n onSubmit={handleCreateComponent}\n />\n </MotionPage>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ComponentsPage.js","names":[],"sources":["../../src/pages/ComponentsPage.tsx"],"sourcesContent":["import { useState, useMemo, useCallback } from 'react';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { VIBE_PERMISSIONS } from '../constants/permissions';\nimport { Plus, Box, RefreshCw } from 'lucide-react';\nimport { ComponentCard } from '@/components/idp-components';\nimport type { ComponentCardData } from '@/components/idp-components';\nimport { ComponentForm } from '@/components/idp-components';\nimport type { ComponentFormData } from '@/components/idp-components';\nimport { EntityTagPicker } from '@/components/shared/EntityTagPicker';\nimport { useDraftEntityTags } from '@/hooks/useDraftEntityTags';\nimport {\n PageHeader,\n SearchInput,\n FilterDropdown,\n EmptyState,\n LoadingState,\n ErrorState,\n GetHelpButton,\n} from '@/components/shared';\nimport { MotionPage, MotionList, MotionItem } from '@burdenoff/fe-libs/shared/motion';\nimport { AnimatePresence } from 'framer-motion';\nimport { useVibeNavigate } from '@/providers/VibeControlsProvider';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { useListComponentsQuery, useCreateComponentMutation } from '@/generated/wspace-operations';\n\nexport function ComponentsPage() {\n const navigate = useVibeNavigate();\n const { t } = useI18n();\n const { hasPermission, isLoading: permissionsLoading } = usePermissions();\n\n const tr = useCallback(\n (key: string, fallback: string, params?: Record<string, string | number>): string => {\n const translated = t(key, params);\n const template = translated === key ? fallback : translated;\n if (!params) return template;\n return Object.entries(params).reduce(\n (acc, [paramKey, paramValue]) => acc.replace(`{{${paramKey}}}`, String(paramValue)),\n template\n );\n },\n [t]\n );\n\n const typeOptions = [\n { value: 'all', label: tr('component.allTypes', 'All Types') },\n { value: 'SERVICE', label: tr('component.typeService', 'Service') },\n { value: 'FRONTEND', label: tr('component.typeFrontend', 'Frontend') },\n { value: 'LIBRARY', label: tr('component.typeLibrary', 'Library') },\n { value: 'API', label: tr('component.typeApi', 'API') },\n { value: 'MOBILE', label: tr('component.typeMobile', 'Mobile') },\n { value: 'DATABASE', label: tr('component.typeDatabase', 'Database') },\n { value: 'INFRASTRUCTURE', label: tr('component.typeInfrastructure', 'Infrastructure') },\n { value: 'CUSTOM', label: tr('component.typeCustom', 'Custom') },\n ];\n\n const lifecycleOptions = [\n { value: 'all', label: tr('component.allLifecycles', 'All Lifecycles') },\n { value: 'EXPERIMENTAL', label: tr('component.lifecycleExperimental', 'Experimental') },\n { value: 'PRODUCTION', label: tr('component.lifecycleProduction', 'Production') },\n { value: 'DEPRECATED', label: tr('component.lifecycleDeprecated', 'Deprecated') },\n ];\n\n const [searchQuery, setSearchQuery] = useState('');\n const [typeFilter, setTypeFilter] = useState('all');\n const [lifecycleFilter, setLifecycleFilter] = useState('all');\n const [showCreateForm, setShowCreateForm] = useState(false);\n const [successMessage, setSuccessMessage] = useState<string | null>(null);\n\n const { data, loading, error, refetch } = useListComponentsQuery({\n variables: {\n filter: {\n type: typeFilter !== 'all' ? typeFilter : undefined,\n lifecycle: lifecycleFilter !== 'all' ? lifecycleFilter : undefined,\n },\n ...(searchQuery ? { pagination: { limit: 1000 } } : {}),\n },\n fetchPolicy: 'cache-and-network',\n });\n\n const [createComponent] = useCreateComponentMutation({\n refetchQueries: ['ListComponents'],\n awaitRefetchQueries: true,\n });\n const { setDraftTagIds, flushTags } = useDraftEntityTags('VIBECONTROLS_COMPONENT');\n\n const components: ComponentCardData[] = useMemo(() => {\n const items = data?.vibecontrolsComponents?.items ?? [];\n return items.map((item: Record<string, unknown>) => ({\n id: item.id as string,\n name: item.name as string,\n description: (item.description as string) || null,\n type: item.type as ComponentCardData['type'],\n lifecycle: item.lifecycle as ComponentCardData['lifecycle'],\n version: (item.version as string) || null,\n language: (item.language as string) || null,\n framework: (item.framework as string) || null,\n tags: (item.tags as string[]) || [],\n createdAt: item.createdAt as string,\n updatedAt: item.updatedAt as string,\n }));\n }, [data?.vibecontrolsComponents?.items]);\n\n const filteredComponents = useMemo(() => {\n if (!searchQuery) return components;\n const query = searchQuery.toLowerCase();\n return components.filter(\n (c) =>\n c.name.toLowerCase().includes(query) ||\n c.description?.toLowerCase().includes(query) ||\n c.language?.toLowerCase().includes(query) ||\n c.framework?.toLowerCase().includes(query) ||\n c.tags?.some((tag) => tag.toLowerCase().includes(query))\n );\n }, [components, searchQuery]);\n\n if (permissionsLoading)\n return (\n <div className=\"flex items-center justify-center min-h-[400px]\">\n <p className=\"text-text-secondary\">Loading permissions...</p>\n </div>\n );\n if (!hasPermission(VIBE_PERMISSIONS.VIBE_LIST))\n return (\n <div className=\"flex items-center justify-center min-h-[400px]\">\n <div className=\"text-center space-y-2\">\n <h2 className=\"text-lg font-semibold text-foreground\">Access Denied</h2>\n <p className=\"text-sm text-text-secondary\">\n You don't have permission to access this page.\n </p>\n </div>\n </div>\n );\n\n const handleComponentSelect = (component: ComponentCardData) => {\n navigate(`/components/${component.id}`);\n };\n\n const handleCreateComponent = async (formData: ComponentFormData) => {\n const result = await createComponent({\n variables: {\n input: {\n name: formData.name,\n description: formData.description || undefined,\n type: formData.type,\n lifecycle: formData.lifecycle,\n language: formData.language || undefined,\n framework: formData.framework || undefined,\n version: formData.version || undefined,\n owner: formData.owner || undefined,\n tags: formData.tags.length > 0 ? formData.tags : undefined,\n },\n },\n });\n if (!result.data?.createVibecontrolsComponent?.id) {\n throw new Error(tr('component.failedCreate', 'Failed to create component'));\n }\n await flushTags(result.data.createVibecontrolsComponent.id);\n await refetch();\n setShowCreateForm(false);\n setSuccessMessage(tr('component.createdSuccess', 'Component created successfully'));\n setTimeout(() => setSuccessMessage(null), 3000);\n };\n\n if (loading && components.length === 0) {\n return (\n <MotionPage className=\"space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen\">\n <LoadingState message={tr('component.loading', 'Loading components...')} />\n </MotionPage>\n );\n }\n\n if (error && components.length === 0) {\n return (\n <MotionPage className=\"space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen\">\n <ErrorState\n title={tr('component.failedLoad', 'Failed to load components')}\n message={error.message}\n onRetry={() => refetch()}\n />\n </MotionPage>\n );\n }\n\n return (\n <MotionPage className=\"space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen\">\n <PageHeader\n title={tr('component.title', 'Components')}\n description={tr('component.description', '{{total}} components', {\n total: components.length,\n })}\n actions={\n <div className=\"flex items-center gap-2\">\n <button\n onClick={() => refetch()}\n disabled={loading}\n className=\"flex items-center gap-2 px-3 py-2 text-text-secondary hover:bg-bg-sunken rounded-md transition-colors disabled:opacity-50\"\n title={tr('common.refresh', 'Refresh')}\n >\n <RefreshCw className={`w-4 h-4 ${loading ? 'animate-spin' : ''}`} />\n </button>\n <button\n onClick={() => setShowCreateForm(true)}\n className=\"flex items-center gap-2 px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors\"\n >\n <Plus className=\"w-4 h-4\" />\n {tr('component.createComponent', 'Create Component')}\n </button>\n </div>\n }\n />\n\n {/* Filters */}\n <div className=\"space-y-3\">\n <div className=\"flex items-center gap-2\">\n <SearchInput\n value={searchQuery}\n onChange={setSearchQuery}\n placeholder={tr('component.searchPlaceholder', 'Search components...')}\n className=\"flex-1 sm:w-64 sm:flex-none\"\n />\n </div>\n <div className=\"flex items-center gap-2 sm:gap-3 flex-wrap\">\n <FilterDropdown\n label={tr('component.filterType', 'Type')}\n value={typeFilter}\n options={typeOptions}\n onChange={setTypeFilter}\n />\n <FilterDropdown\n label={tr('component.filterLifecycle', 'Lifecycle')}\n value={lifecycleFilter}\n options={lifecycleOptions}\n onChange={setLifecycleFilter}\n />\n </div>\n </div>\n\n {/* Results count */}\n {(searchQuery || typeFilter !== 'all' || lifecycleFilter !== 'all') && (\n <p className=\"text-sm text-text-secondary\">\n {tr('component.showingResults', 'Showing {{filtered}} of {{total}} components', {\n filtered: filteredComponents.length,\n total: components.length,\n })}\n {loading && (\n <span className=\"ml-2 text-text-muted\">\n {tr('common.refreshing', '(refreshing...)')}\n </span>\n )}\n </p>\n )}\n\n {/* Grid */}\n {filteredComponents.length > 0 ? (\n <AnimatePresence mode=\"wait\">\n <MotionList\n key=\"component-grid\"\n stagger={0.04}\n className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4\"\n >\n {filteredComponents.map((component) => (\n <MotionItem key={component.id}>\n <ComponentCard component={component} onOpen={handleComponentSelect} />\n </MotionItem>\n ))}\n </MotionList>\n </AnimatePresence>\n ) : (\n <EmptyState\n icon={<Box className=\"w-8 h-8 text-text-secondary\" />}\n title={\n searchQuery || typeFilter !== 'all' || lifecycleFilter !== 'all'\n ? tr('component.emptyNoResults', 'No components found')\n : tr('component.emptyNoComponents', 'No components yet')\n }\n description={\n searchQuery || typeFilter !== 'all' || lifecycleFilter !== 'all'\n ? tr('component.emptyAdjustSearch', 'Try adjusting your search or filters')\n : tr(\n 'component.emptyCreate',\n 'Register your first component to start tracking your software catalog'\n )\n }\n action={\n !searchQuery && typeFilter === 'all' && lifecycleFilter === 'all' ? (\n <div className=\"flex items-center gap-2\">\n <button\n onClick={() => setShowCreateForm(true)}\n className=\"flex items-center gap-2 px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors\"\n >\n <Plus className=\"w-4 h-4\" />\n {tr('component.createComponent', 'Create Component')}\n </button>\n <GetHelpButton prompt=\"What is a Component in VibeControls and how do I add one?\" />\n </div>\n ) : undefined\n }\n />\n )}\n\n {/* Success Toast */}\n {successMessage && (\n <div className=\"fixed bottom-4 right-4 z-50 px-4 py-3 bg-status-success-bg text-status-success-text rounded-md shadow-lg text-sm\">\n {successMessage}\n </div>\n )}\n\n {/* Create Form */}\n <ComponentForm\n isOpen={showCreateForm}\n onClose={() => setShowCreateForm(false)}\n onSubmit={handleCreateComponent}\n extraSection={\n <div>\n <h3 className=\"text-sm font-medium text-text-secondary mb-2\">\n {tr('component.tags', 'Tags')}\n </h3>\n <EntityTagPicker entityType=\"VIBECONTROLS_COMPONENT\" onDraftChange={setDraftTagIds} />\n </div>\n }\n />\n </MotionPage>\n );\n}\n"],"mappings":"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"}
|