@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.
@@ -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 { EmptyState as o } from "../components/shared/EmptyState.js";
7
- import { SearchInput as s } from "../components/shared/SearchInput.js";
8
- import { FilterDropdown as c } from "../components/shared/FilterDropdown.js";
9
- import { GetHelpButton as l } from "../components/shared/GetHelpButton.js";
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 u } from "../constants/permissions.js";
12
- import { ComponentCard as d } from "../components/idp-components/ComponentCard.js";
13
- import { ComponentForm as f } from "../components/idp-components/ComponentForm.js";
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 p, useMemo as m, useState as h } from "react";
16
- import { useI18n as g } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
17
- import { Box as _, Plus as v, RefreshCw as y } from "lucide-react";
18
- import { usePermissions as b } from "@burdenoff/fe-libs/shared/providers/shell";
19
- import { AnimatePresence as x } from "framer-motion";
20
- import { jsx as S, jsxs as C } from "react/jsx-runtime";
21
- import { MotionItem as w, MotionList as T, MotionPage as E } from "@burdenoff/fe-libs/shared/motion";
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 D() {
24
- let D = n(), { t: O } = g(), { hasPermission: k, isLoading: A } = b(), j = p((e, t, n) => {
25
- let r = O(e, n), i = r === e ? t : 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
- }, [O]), M = [
29
+ }, [k]), N = [
28
30
  {
29
31
  value: "all",
30
- label: j("component.allTypes", "All Types")
32
+ label: M("component.allTypes", "All Types")
31
33
  },
32
34
  {
33
35
  value: "SERVICE",
34
- label: j("component.typeService", "Service")
36
+ label: M("component.typeService", "Service")
35
37
  },
36
38
  {
37
39
  value: "FRONTEND",
38
- label: j("component.typeFrontend", "Frontend")
40
+ label: M("component.typeFrontend", "Frontend")
39
41
  },
40
42
  {
41
43
  value: "LIBRARY",
42
- label: j("component.typeLibrary", "Library")
44
+ label: M("component.typeLibrary", "Library")
43
45
  },
44
46
  {
45
47
  value: "API",
46
- label: j("component.typeApi", "API")
48
+ label: M("component.typeApi", "API")
47
49
  },
48
50
  {
49
51
  value: "MOBILE",
50
- label: j("component.typeMobile", "Mobile")
52
+ label: M("component.typeMobile", "Mobile")
51
53
  },
52
54
  {
53
55
  value: "DATABASE",
54
- label: j("component.typeDatabase", "Database")
56
+ label: M("component.typeDatabase", "Database")
55
57
  },
56
58
  {
57
59
  value: "INFRASTRUCTURE",
58
- label: j("component.typeInfrastructure", "Infrastructure")
60
+ label: M("component.typeInfrastructure", "Infrastructure")
59
61
  },
60
62
  {
61
63
  value: "CUSTOM",
62
- label: j("component.typeCustom", "Custom")
64
+ label: M("component.typeCustom", "Custom")
63
65
  }
64
- ], N = [
66
+ ], P = [
65
67
  {
66
68
  value: "all",
67
- label: j("component.allLifecycles", "All Lifecycles")
69
+ label: M("component.allLifecycles", "All Lifecycles")
68
70
  },
69
71
  {
70
72
  value: "EXPERIMENTAL",
71
- label: j("component.lifecycleExperimental", "Experimental")
73
+ label: M("component.lifecycleExperimental", "Experimental")
72
74
  },
73
75
  {
74
76
  value: "PRODUCTION",
75
- label: j("component.lifecycleProduction", "Production")
77
+ label: M("component.lifecycleProduction", "Production")
76
78
  },
77
79
  {
78
80
  value: "DEPRECATED",
79
- label: j("component.lifecycleDeprecated", "Deprecated")
81
+ label: M("component.lifecycleDeprecated", "Deprecated")
80
82
  }
81
- ], [P, F] = h(""), [I, L] = h("all"), [R, z] = h("all"), [B, V] = h(!1), [H, U] = h(null), { data: W, loading: G, error: K, refetch: q } = t({
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: I === "all" ? void 0 : I,
85
- lifecycle: R === "all" ? void 0 : R
86
+ type: L === "all" ? void 0 : L,
87
+ lifecycle: z === "all" ? void 0 : z
86
88
  },
87
- ...P ? { pagination: { limit: 1e3 } } : {}
89
+ ...F ? { pagination: { limit: 1e3 } } : {}
88
90
  },
89
91
  fetchPolicy: "cache-and-network"
90
- }), [J] = e({
92
+ }), [Y] = e({
91
93
  refetchQueries: ["ListComponents"],
92
94
  awaitRefetchQueries: !0
93
- }), Y = m(() => (W?.vibecontrolsComponents?.items ?? []).map((e) => ({
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
- })), [W?.vibecontrolsComponents?.items]), X = m(() => {
106
- if (!P) return Y;
107
- let e = P.toLowerCase();
108
- return Y.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)));
109
- }, [Y, P]);
110
- if (A) return /* @__PURE__ */ S("div", {
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__ */ S("p", {
114
+ children: /* @__PURE__ */ C("p", {
113
115
  className: "text-text-secondary",
114
116
  children: "Loading permissions..."
115
117
  })
116
118
  });
117
- if (!k(u.VIBE_LIST)) return /* @__PURE__ */ S("div", {
119
+ if (!A(f.VIBE_LIST)) return /* @__PURE__ */ C("div", {
118
120
  className: "flex items-center justify-center min-h-[400px]",
119
- children: /* @__PURE__ */ C("div", {
121
+ children: /* @__PURE__ */ w("div", {
120
122
  className: "text-center space-y-2",
121
- children: [/* @__PURE__ */ S("h2", {
123
+ children: [/* @__PURE__ */ C("h2", {
122
124
  className: "text-lg font-semibold text-foreground",
123
125
  children: "Access Denied"
124
- }), /* @__PURE__ */ S("p", {
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 Z = (e) => {
131
- D(`/components/${e.id}`);
132
+ let te = (e) => {
133
+ O(`/components/${e.id}`);
132
134
  };
133
- return G && Y.length === 0 ? /* @__PURE__ */ S(E, {
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__ */ S(i, { message: j("component.loading", "Loading components...") })
136
- }) : K && Y.length === 0 ? /* @__PURE__ */ S(E, {
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__ */ S(a, {
139
- title: j("component.failedLoad", "Failed to load components"),
140
- message: K.message,
141
- onRetry: () => q()
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__ */ C(E, {
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__ */ S(r, {
147
- title: j("component.title", "Components"),
148
- description: j("component.description", "{{total}} components", { total: Y.length }),
149
- actions: /* @__PURE__ */ C("div", {
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__ */ S("button", {
152
- onClick: () => q(),
153
- disabled: G,
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: j("common.refresh", "Refresh"),
156
- children: /* @__PURE__ */ S(y, { className: `w-4 h-4 ${G ? "animate-spin" : ""}` })
157
- }), /* @__PURE__ */ C("button", {
158
- onClick: () => V(!0),
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__ */ S(v, { className: "w-4 h-4" }), j("component.createComponent", "Create Component")]
162
+ children: [/* @__PURE__ */ C(y, { className: "w-4 h-4" }), M("component.createComponent", "Create Component")]
161
163
  })]
162
164
  })
163
165
  }),
164
- /* @__PURE__ */ C("div", {
166
+ /* @__PURE__ */ w("div", {
165
167
  className: "space-y-3",
166
- children: [/* @__PURE__ */ S("div", {
168
+ children: [/* @__PURE__ */ C("div", {
167
169
  className: "flex items-center gap-2",
168
- children: /* @__PURE__ */ S(s, {
169
- value: P,
170
- onChange: F,
171
- placeholder: j("component.searchPlaceholder", "Search components..."),
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__ */ C("div", {
176
+ }), /* @__PURE__ */ w("div", {
175
177
  className: "flex items-center gap-2 sm:gap-3 flex-wrap",
176
- children: [/* @__PURE__ */ S(c, {
177
- label: j("component.filterType", "Type"),
178
- value: I,
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: z
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
- (P || I !== "all" || R !== "all") && /* @__PURE__ */ C("p", {
191
+ (F || L !== "all" || z !== "all") && /* @__PURE__ */ w("p", {
190
192
  className: "text-sm text-text-secondary",
191
- children: [j("component.showingResults", "Showing {{filtered}} of {{total}} components", {
192
- filtered: X.length,
193
- total: Y.length
194
- }), G && /* @__PURE__ */ S("span", {
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: j("common.refreshing", "(refreshing...)")
198
+ children: M("common.refreshing", "(refreshing...)")
197
199
  })]
198
200
  }),
199
- X.length > 0 ? /* @__PURE__ */ S(x, {
201
+ $.length > 0 ? /* @__PURE__ */ C(S, {
200
202
  mode: "wait",
201
- children: /* @__PURE__ */ S(T, {
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: X.map((e) => /* @__PURE__ */ S(w, { children: /* @__PURE__ */ S(d, {
206
+ children: $.map((e) => /* @__PURE__ */ C(T, { children: /* @__PURE__ */ C(p, {
205
207
  component: e,
206
- onOpen: Z
208
+ onOpen: te
207
209
  }) }, e.id))
208
210
  }, "component-grid")
209
- }) : /* @__PURE__ */ S(o, {
210
- icon: /* @__PURE__ */ S(_, { className: "w-8 h-8 text-text-secondary" }),
211
- title: P || I !== "all" || R !== "all" ? j("component.emptyNoResults", "No components found") : j("component.emptyNoComponents", "No components yet"),
212
- description: P || I !== "all" || R !== "all" ? j("component.emptyAdjustSearch", "Try adjusting your search or filters") : j("component.emptyCreate", "Register your first component to start tracking your software catalog"),
213
- action: !P && I === "all" && R === "all" ? /* @__PURE__ */ C("div", {
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__ */ C("button", {
216
- onClick: () => V(!0),
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__ */ S(v, { className: "w-4 h-4" }), j("component.createComponent", "Create Component")]
219
- }), /* @__PURE__ */ S(l, { prompt: "What is a Component in VibeControls and how do I add one?" })]
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
- H && /* @__PURE__ */ S("div", {
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: H
226
+ children: U
225
227
  }),
226
- /* @__PURE__ */ S(f, {
227
- isOpen: B,
228
- onClose: () => V(!1),
228
+ /* @__PURE__ */ C(m, {
229
+ isOpen: V,
230
+ onClose: () => H(!1),
229
231
  onSubmit: async (e) => {
230
- if (!(await J({ variables: { input: {
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
- } } })).data?.createVibecontrolsComponent?.id) throw Error(j("component.failedCreate", "Failed to create component"));
241
- await q(), V(!1), U(j("component.createdSuccess", "Component created successfully")), setTimeout(() => U(null), 3e3);
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 { D as ComponentsPage };
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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"}