@burdenoff/microfe-vibecontrols 2026.519.6 → 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,11 +3,11 @@ import { useI18n as n } from "@burdenoff/fe-libs/shared/providers/shell/I18nProv
3
3
  import { X as r } from "lucide-react";
4
4
  import { jsx as i, jsxs as a } from "react/jsx-runtime";
5
5
  //#region src/components/idp-components/ComponentForm.tsx
6
- var o = ({ isOpen: o, onClose: s, onSubmit: c, initialData: l, title: u }) => {
7
- let { t: d } = n(), f = (e, t) => {
8
- let n = d(e);
6
+ var o = ({ isOpen: o, onClose: s, onSubmit: c, initialData: l, title: u, extraSection: d }) => {
7
+ let { t: f } = n(), p = (e, t) => {
8
+ let n = f(e);
9
9
  return n === e ? t : n;
10
- }, p = e(null), m = e(null), h = e(null), g = e(null), _ = e(null), v = e(null), y = e(null), b = e(null), x = e(null), [S, C] = t(!1), [w, T] = t(null), [E, D] = t({});
10
+ }, m = e(null), h = e(null), g = e(null), _ = e(null), v = e(null), y = e(null), b = e(null), x = e(null), S = e(null), [C, w] = t(!1), [T, E] = t(null), [D, O] = t({});
11
11
  return o ? /* @__PURE__ */ i("div", {
12
12
  className: "fixed inset-0 bg-bg-overlay flex items-center justify-center z-50 p-4",
13
13
  children: /* @__PURE__ */ a("div", {
@@ -17,7 +17,7 @@ var o = ({ isOpen: o, onClose: s, onSubmit: c, initialData: l, title: u }) => {
17
17
  className: "flex items-center justify-between mb-4",
18
18
  children: [/* @__PURE__ */ i("h2", {
19
19
  className: "text-lg font-semibold text-text-primary",
20
- children: u || f("component.createComponent", "Create Component")
20
+ children: u || p("component.createComponent", "Create Component")
21
21
  }), /* @__PURE__ */ i("button", {
22
22
  onClick: s,
23
23
  className: "p-1 rounded hover:bg-bg-sunken transition-colors text-text-secondary",
@@ -27,38 +27,38 @@ var o = ({ isOpen: o, onClose: s, onSubmit: c, initialData: l, title: u }) => {
27
27
  /* @__PURE__ */ a("div", {
28
28
  className: "space-y-4",
29
29
  children: [
30
- w && /* @__PURE__ */ i("div", {
30
+ T && /* @__PURE__ */ i("div", {
31
31
  className: "p-3 bg-status-error-bg text-status-error-text text-sm rounded-md",
32
- children: w
32
+ children: T
33
33
  }),
34
34
  /* @__PURE__ */ a("div", { children: [
35
35
  /* @__PURE__ */ i("label", {
36
36
  className: "block text-sm font-medium text-text-primary mb-1",
37
- children: f("component.nameLabel", "Name *")
37
+ children: p("component.nameLabel", "Name *")
38
38
  }),
39
39
  /* @__PURE__ */ i("input", {
40
- ref: p,
40
+ ref: m,
41
41
  type: "text",
42
42
  defaultValue: l?.name || "",
43
- placeholder: f("component.namePlaceholder", "my-service"),
44
- onChange: () => E.name && D((e) => ({
43
+ placeholder: p("component.namePlaceholder", "my-service"),
44
+ onChange: () => D.name && O((e) => ({
45
45
  ...e,
46
46
  name: void 0
47
47
  })),
48
- className: `w-full px-3 py-2 bg-bg-sunken border rounded-md text-text-primary placeholder:text-text-placeholder ${E.name ? "border-status-error-border" : "border-border-default"}`
48
+ className: `w-full px-3 py-2 bg-bg-sunken border rounded-md text-text-primary placeholder:text-text-placeholder ${D.name ? "border-status-error-border" : "border-border-default"}`
49
49
  }),
50
- E.name && /* @__PURE__ */ i("p", {
50
+ D.name && /* @__PURE__ */ i("p", {
51
51
  className: "mt-1 text-xs text-status-error-text",
52
- children: E.name
52
+ children: D.name
53
53
  })
54
54
  ] }),
55
55
  /* @__PURE__ */ a("div", { children: [/* @__PURE__ */ i("label", {
56
56
  className: "block text-sm font-medium text-text-primary mb-1",
57
- children: f("component.descriptionLabel", "Description")
57
+ children: p("component.descriptionLabel", "Description")
58
58
  }), /* @__PURE__ */ i("textarea", {
59
- ref: m,
59
+ ref: h,
60
60
  defaultValue: l?.description || "",
61
- placeholder: f("component.descriptionPlaceholder", "Describe this component..."),
61
+ placeholder: p("component.descriptionPlaceholder", "Describe this component..."),
62
62
  rows: 3,
63
63
  className: "w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder resize-none"
64
64
  })] }),
@@ -66,64 +66,64 @@ var o = ({ isOpen: o, onClose: s, onSubmit: c, initialData: l, title: u }) => {
66
66
  className: "grid grid-cols-1 sm:grid-cols-2 gap-4",
67
67
  children: [/* @__PURE__ */ a("div", { children: [/* @__PURE__ */ i("label", {
68
68
  className: "block text-sm font-medium text-text-primary mb-1",
69
- children: f("component.typeLabel", "Type")
69
+ children: p("component.typeLabel", "Type")
70
70
  }), /* @__PURE__ */ a("select", {
71
- ref: h,
71
+ ref: g,
72
72
  defaultValue: l?.type || "SERVICE",
73
73
  className: "w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary",
74
74
  children: [
75
75
  /* @__PURE__ */ i("option", {
76
76
  value: "SERVICE",
77
- children: f("component.typeService", "Service")
77
+ children: p("component.typeService", "Service")
78
78
  }),
79
79
  /* @__PURE__ */ i("option", {
80
80
  value: "FRONTEND",
81
- children: f("component.typeFrontend", "Frontend")
81
+ children: p("component.typeFrontend", "Frontend")
82
82
  }),
83
83
  /* @__PURE__ */ i("option", {
84
84
  value: "LIBRARY",
85
- children: f("component.typeLibrary", "Library")
85
+ children: p("component.typeLibrary", "Library")
86
86
  }),
87
87
  /* @__PURE__ */ i("option", {
88
88
  value: "API",
89
- children: f("component.typeApi", "API")
89
+ children: p("component.typeApi", "API")
90
90
  }),
91
91
  /* @__PURE__ */ i("option", {
92
92
  value: "MOBILE",
93
- children: f("component.typeMobile", "Mobile")
93
+ children: p("component.typeMobile", "Mobile")
94
94
  }),
95
95
  /* @__PURE__ */ i("option", {
96
96
  value: "DATABASE",
97
- children: f("component.typeDatabase", "Database")
97
+ children: p("component.typeDatabase", "Database")
98
98
  }),
99
99
  /* @__PURE__ */ i("option", {
100
100
  value: "INFRASTRUCTURE",
101
- children: f("component.typeInfrastructure", "Infrastructure")
101
+ children: p("component.typeInfrastructure", "Infrastructure")
102
102
  }),
103
103
  /* @__PURE__ */ i("option", {
104
104
  value: "CUSTOM",
105
- children: f("component.typeCustom", "Custom")
105
+ children: p("component.typeCustom", "Custom")
106
106
  })
107
107
  ]
108
108
  })] }), /* @__PURE__ */ a("div", { children: [/* @__PURE__ */ i("label", {
109
109
  className: "block text-sm font-medium text-text-primary mb-1",
110
- children: f("component.lifecycleLabel", "Lifecycle")
110
+ children: p("component.lifecycleLabel", "Lifecycle")
111
111
  }), /* @__PURE__ */ a("select", {
112
- ref: g,
112
+ ref: _,
113
113
  defaultValue: l?.lifecycle || "EXPERIMENTAL",
114
114
  className: "w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary",
115
115
  children: [
116
116
  /* @__PURE__ */ i("option", {
117
117
  value: "EXPERIMENTAL",
118
- children: f("component.lifecycleExperimental", "Experimental")
118
+ children: p("component.lifecycleExperimental", "Experimental")
119
119
  }),
120
120
  /* @__PURE__ */ i("option", {
121
121
  value: "PRODUCTION",
122
- children: f("component.lifecycleProduction", "Production")
122
+ children: p("component.lifecycleProduction", "Production")
123
123
  }),
124
124
  /* @__PURE__ */ i("option", {
125
125
  value: "DEPRECATED",
126
- children: f("component.lifecycleDeprecated", "Deprecated")
126
+ children: p("component.lifecycleDeprecated", "Deprecated")
127
127
  })
128
128
  ]
129
129
  })] })]
@@ -132,21 +132,21 @@ var o = ({ isOpen: o, onClose: s, onSubmit: c, initialData: l, title: u }) => {
132
132
  className: "grid grid-cols-1 sm:grid-cols-2 gap-4",
133
133
  children: [/* @__PURE__ */ a("div", { children: [/* @__PURE__ */ i("label", {
134
134
  className: "block text-sm font-medium text-text-primary mb-1",
135
- children: f("component.languageLabel", "Language")
135
+ children: p("component.languageLabel", "Language")
136
136
  }), /* @__PURE__ */ i("input", {
137
- ref: _,
137
+ ref: v,
138
138
  type: "text",
139
139
  defaultValue: l?.language || "",
140
- placeholder: f("component.languagePlaceholder", "TypeScript"),
140
+ placeholder: p("component.languagePlaceholder", "TypeScript"),
141
141
  className: "w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder"
142
142
  })] }), /* @__PURE__ */ a("div", { children: [/* @__PURE__ */ i("label", {
143
143
  className: "block text-sm font-medium text-text-primary mb-1",
144
- children: f("component.frameworkLabel", "Framework")
144
+ children: p("component.frameworkLabel", "Framework")
145
145
  }), /* @__PURE__ */ i("input", {
146
- ref: v,
146
+ ref: y,
147
147
  type: "text",
148
148
  defaultValue: l?.framework || "",
149
- placeholder: f("component.frameworkPlaceholder", "React"),
149
+ placeholder: p("component.frameworkPlaceholder", "React"),
150
150
  className: "w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder"
151
151
  })] })]
152
152
  }),
@@ -154,74 +154,78 @@ var o = ({ isOpen: o, onClose: s, onSubmit: c, initialData: l, title: u }) => {
154
154
  className: "grid grid-cols-1 sm:grid-cols-2 gap-4",
155
155
  children: [/* @__PURE__ */ a("div", { children: [/* @__PURE__ */ i("label", {
156
156
  className: "block text-sm font-medium text-text-primary mb-1",
157
- children: f("component.versionLabel", "Version")
157
+ children: p("component.versionLabel", "Version")
158
158
  }), /* @__PURE__ */ i("input", {
159
- ref: y,
159
+ ref: b,
160
160
  type: "text",
161
161
  defaultValue: l?.version || "",
162
- placeholder: f("component.versionPlaceholder", "1.0.0"),
162
+ placeholder: p("component.versionPlaceholder", "1.0.0"),
163
163
  className: "w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder"
164
164
  })] }), /* @__PURE__ */ a("div", { children: [/* @__PURE__ */ i("label", {
165
165
  className: "block text-sm font-medium text-text-primary mb-1",
166
- children: f("component.ownerLabel", "Owner")
166
+ children: p("component.ownerLabel", "Owner")
167
167
  }), /* @__PURE__ */ i("input", {
168
- ref: b,
168
+ ref: x,
169
169
  type: "text",
170
170
  defaultValue: l?.owner || "",
171
- placeholder: f("component.ownerPlaceholder", "team-name"),
171
+ placeholder: p("component.ownerPlaceholder", "team-name"),
172
172
  className: "w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder"
173
173
  })] })]
174
174
  }),
175
175
  /* @__PURE__ */ a("div", { children: [/* @__PURE__ */ i("label", {
176
176
  className: "block text-sm font-medium text-text-primary mb-1",
177
- children: f("component.tagsLabel", "Tags (comma-separated)")
177
+ children: p("component.tagsLabel", "Tags (comma-separated)")
178
178
  }), /* @__PURE__ */ i("input", {
179
- ref: x,
179
+ ref: S,
180
180
  type: "text",
181
181
  defaultValue: l?.tags?.join(", ") || "",
182
- placeholder: f("component.tagsPlaceholder", "backend, api, graphql"),
182
+ placeholder: p("component.tagsPlaceholder", "backend, api, graphql"),
183
183
  className: "w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder"
184
184
  })] })
185
185
  ]
186
186
  }),
187
+ d && /* @__PURE__ */ i("div", {
188
+ className: "mt-4 border-t border-border-default pt-4",
189
+ children: d
190
+ }),
187
191
  /* @__PURE__ */ a("div", {
188
192
  className: "flex flex-col sm:flex-row justify-end gap-3 mt-6",
189
193
  children: [/* @__PURE__ */ i("button", {
190
194
  onClick: s,
191
- disabled: S,
195
+ disabled: C,
192
196
  className: "px-4 py-2 text-text-primary hover:bg-bg-sunken rounded-md transition-colors disabled:opacity-50",
193
- children: f("common.cancel", "Cancel")
197
+ children: p("common.cancel", "Cancel")
194
198
  }), /* @__PURE__ */ i("button", {
195
199
  onClick: async () => {
196
- let e = p.current?.value?.trim();
200
+ let e = m.current?.value?.trim();
197
201
  if (!e) {
198
- D({ name: f("component.nameRequired", "Name is required") }), T(f("component.fillRequired", "Please fill in all required fields"));
202
+ O({ name: p("component.nameRequired", "Name is required") }), E(p("component.fillRequired", "Please fill in all required fields"));
199
203
  return;
200
204
  }
201
- D({});
205
+ O({});
202
206
  let t = {
203
207
  name: e,
204
- description: m.current?.value?.trim() || "",
205
- type: h.current?.value || "SERVICE",
206
- lifecycle: g.current?.value || "EXPERIMENTAL",
207
- language: _.current?.value?.trim() || "",
208
- framework: v.current?.value?.trim() || "",
209
- version: y.current?.value?.trim() || "",
210
- owner: b.current?.value?.trim() || "",
211
- tags: x.current?.value ? x.current.value.split(",").map((e) => e.trim()).filter(Boolean) : []
208
+ description: h.current?.value?.trim() || "",
209
+ type: g.current?.value || "SERVICE",
210
+ lifecycle: _.current?.value || "EXPERIMENTAL",
211
+ language: v.current?.value?.trim() || "",
212
+ framework: y.current?.value?.trim() || "",
213
+ version: b.current?.value?.trim() || "",
214
+ owner: x.current?.value?.trim() || "",
215
+ tags: S.current?.value ? S.current.value.split(",").map((e) => e.trim()).filter(Boolean) : []
212
216
  };
213
- C(!0), T(null);
217
+ w(!0), E(null);
214
218
  try {
215
219
  await c(t), s();
216
220
  } catch (e) {
217
- T(e instanceof Error ? e.message : f("component.failedSave", "Failed to save component"));
221
+ E(e instanceof Error ? e.message : p("component.failedSave", "Failed to save component"));
218
222
  } finally {
219
- C(!1);
223
+ w(!1);
220
224
  }
221
225
  },
222
- disabled: S,
226
+ disabled: C,
223
227
  className: "px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors disabled:opacity-50",
224
- children: S ? f("common.saving", "Saving...") : l?.name ? f("common.save", "Save") : f("component.createComponent", "Create Component")
228
+ children: C ? p("common.saving", "Saving...") : l?.name ? p("common.save", "Save") : p("component.createComponent", "Create Component")
225
229
  })]
226
230
  })
227
231
  ]
@@ -1 +1 @@
1
- {"version":3,"file":"ComponentForm.js","names":[],"sources":["../../../src/components/idp-components/ComponentForm.tsx"],"sourcesContent":["import { useRef, useState } from 'react';\nimport type { FC } from 'react';\nimport { X } from 'lucide-react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\nexport interface ComponentFormData {\n name: string;\n description: string;\n type:\n | 'SERVICE'\n | 'FRONTEND'\n | 'LIBRARY'\n | 'API'\n | 'MOBILE'\n | 'DATABASE'\n | 'INFRASTRUCTURE'\n | 'CUSTOM';\n lifecycle: 'EXPERIMENTAL' | 'PRODUCTION' | 'DEPRECATED';\n language: string;\n framework: string;\n version: string;\n owner: string;\n tags: string[];\n}\n\nexport interface ComponentFormProps {\n isOpen: boolean;\n onClose: () => void;\n onSubmit: (data: ComponentFormData) => Promise<void>;\n initialData?: Partial<ComponentFormData>;\n title?: string;\n}\n\nexport const ComponentForm: FC<ComponentFormProps> = ({\n isOpen,\n onClose,\n onSubmit,\n initialData,\n title,\n}) => {\n const { t } = useI18n();\n const tr = (key: string, fallback: string) => {\n const v = t(key);\n return v === key ? fallback : v;\n };\n\n const nameRef = useRef<HTMLInputElement>(null);\n const descriptionRef = useRef<HTMLTextAreaElement>(null);\n const typeRef = useRef<HTMLSelectElement>(null);\n const lifecycleRef = useRef<HTMLSelectElement>(null);\n const languageRef = useRef<HTMLInputElement>(null);\n const frameworkRef = useRef<HTMLInputElement>(null);\n const versionRef = useRef<HTMLInputElement>(null);\n const ownerRef = useRef<HTMLInputElement>(null);\n const tagsRef = useRef<HTMLInputElement>(null);\n\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [fieldErrors, setFieldErrors] = useState<{ name?: string }>({});\n\n if (!isOpen) return null;\n\n const handleSubmit = async () => {\n const name = nameRef.current?.value?.trim();\n if (!name) {\n setFieldErrors({ name: tr('component.nameRequired', 'Name is required') });\n setError(tr('component.fillRequired', 'Please fill in all required fields'));\n return;\n }\n setFieldErrors({});\n\n const data: ComponentFormData = {\n name,\n description: descriptionRef.current?.value?.trim() || '',\n type: (typeRef.current?.value as ComponentFormData['type']) || 'SERVICE',\n lifecycle: (lifecycleRef.current?.value as ComponentFormData['lifecycle']) || 'EXPERIMENTAL',\n language: languageRef.current?.value?.trim() || '',\n framework: frameworkRef.current?.value?.trim() || '',\n version: versionRef.current?.value?.trim() || '',\n owner: ownerRef.current?.value?.trim() || '',\n tags: tagsRef.current?.value\n ? tagsRef.current.value\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean)\n : [],\n };\n\n setLoading(true);\n setError(null);\n try {\n await onSubmit(data);\n onClose();\n } catch (err) {\n setError(\n err instanceof Error ? err.message : tr('component.failedSave', 'Failed to save component')\n );\n } finally {\n setLoading(false);\n }\n };\n\n return (\n <div className=\"fixed inset-0 bg-bg-overlay flex items-center justify-center z-50 p-4\">\n <div className=\"bg-bg-surface rounded-lg p-6 w-full max-w-lg max-h-[90vh] overflow-y-auto\">\n <div className=\"flex items-center justify-between mb-4\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {title || tr('component.createComponent', 'Create Component')}\n </h2>\n <button\n onClick={onClose}\n className=\"p-1 rounded hover:bg-bg-sunken transition-colors text-text-secondary\"\n >\n <X className=\"w-5 h-5\" />\n </button>\n </div>\n <div className=\"space-y-4\">\n {error && (\n <div className=\"p-3 bg-status-error-bg text-status-error-text text-sm rounded-md\">\n {error}\n </div>\n )}\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('component.nameLabel', 'Name *')}\n </label>\n <input\n ref={nameRef}\n type=\"text\"\n defaultValue={initialData?.name || ''}\n placeholder={tr('component.namePlaceholder', 'my-service')}\n onChange={() =>\n fieldErrors.name && setFieldErrors((e) => ({ ...e, name: undefined }))\n }\n className={`w-full px-3 py-2 bg-bg-sunken border rounded-md text-text-primary placeholder:text-text-placeholder ${fieldErrors.name ? 'border-status-error-border' : 'border-border-default'}`}\n />\n {fieldErrors.name && (\n <p className=\"mt-1 text-xs text-status-error-text\">{fieldErrors.name}</p>\n )}\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('component.descriptionLabel', 'Description')}\n </label>\n <textarea\n ref={descriptionRef}\n defaultValue={initialData?.description || ''}\n placeholder={tr('component.descriptionPlaceholder', 'Describe this component...')}\n rows={3}\n className=\"w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder resize-none\"\n />\n </div>\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-4\">\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('component.typeLabel', 'Type')}\n </label>\n <select\n ref={typeRef}\n defaultValue={initialData?.type || 'SERVICE'}\n className=\"w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary\"\n >\n <option value=\"SERVICE\">{tr('component.typeService', 'Service')}</option>\n <option value=\"FRONTEND\">{tr('component.typeFrontend', 'Frontend')}</option>\n <option value=\"LIBRARY\">{tr('component.typeLibrary', 'Library')}</option>\n <option value=\"API\">{tr('component.typeApi', 'API')}</option>\n <option value=\"MOBILE\">{tr('component.typeMobile', 'Mobile')}</option>\n <option value=\"DATABASE\">{tr('component.typeDatabase', 'Database')}</option>\n <option value=\"INFRASTRUCTURE\">\n {tr('component.typeInfrastructure', 'Infrastructure')}\n </option>\n <option value=\"CUSTOM\">{tr('component.typeCustom', 'Custom')}</option>\n </select>\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('component.lifecycleLabel', 'Lifecycle')}\n </label>\n <select\n ref={lifecycleRef}\n defaultValue={initialData?.lifecycle || 'EXPERIMENTAL'}\n className=\"w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary\"\n >\n <option value=\"EXPERIMENTAL\">\n {tr('component.lifecycleExperimental', 'Experimental')}\n </option>\n <option value=\"PRODUCTION\">\n {tr('component.lifecycleProduction', 'Production')}\n </option>\n <option value=\"DEPRECATED\">\n {tr('component.lifecycleDeprecated', 'Deprecated')}\n </option>\n </select>\n </div>\n </div>\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-4\">\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('component.languageLabel', 'Language')}\n </label>\n <input\n ref={languageRef}\n type=\"text\"\n defaultValue={initialData?.language || ''}\n placeholder={tr('component.languagePlaceholder', 'TypeScript')}\n className=\"w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder\"\n />\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('component.frameworkLabel', 'Framework')}\n </label>\n <input\n ref={frameworkRef}\n type=\"text\"\n defaultValue={initialData?.framework || ''}\n placeholder={tr('component.frameworkPlaceholder', 'React')}\n className=\"w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder\"\n />\n </div>\n </div>\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-4\">\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('component.versionLabel', 'Version')}\n </label>\n <input\n ref={versionRef}\n type=\"text\"\n defaultValue={initialData?.version || ''}\n placeholder={tr('component.versionPlaceholder', '1.0.0')}\n className=\"w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder\"\n />\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('component.ownerLabel', 'Owner')}\n </label>\n <input\n ref={ownerRef}\n type=\"text\"\n defaultValue={initialData?.owner || ''}\n placeholder={tr('component.ownerPlaceholder', 'team-name')}\n className=\"w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder\"\n />\n </div>\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('component.tagsLabel', 'Tags (comma-separated)')}\n </label>\n <input\n ref={tagsRef}\n type=\"text\"\n defaultValue={initialData?.tags?.join(', ') || ''}\n placeholder={tr('component.tagsPlaceholder', 'backend, api, graphql')}\n className=\"w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder\"\n />\n </div>\n </div>\n <div className=\"flex flex-col sm:flex-row justify-end gap-3 mt-6\">\n <button\n onClick={onClose}\n disabled={loading}\n className=\"px-4 py-2 text-text-primary hover:bg-bg-sunken rounded-md transition-colors disabled:opacity-50\"\n >\n {tr('common.cancel', 'Cancel')}\n </button>\n <button\n onClick={handleSubmit}\n disabled={loading}\n className=\"px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors disabled:opacity-50\"\n >\n {loading\n ? tr('common.saving', 'Saving...')\n : initialData?.name\n ? tr('common.save', 'Save')\n : tr('component.createComponent', 'Create Component')}\n </button>\n </div>\n </div>\n </div>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"ComponentForm.js","names":[],"sources":["../../../src/components/idp-components/ComponentForm.tsx"],"sourcesContent":["import { useRef, useState } from 'react';\nimport type { FC, ReactNode } from 'react';\nimport { X } from 'lucide-react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\nexport interface ComponentFormData {\n name: string;\n description: string;\n type:\n | 'SERVICE'\n | 'FRONTEND'\n | 'LIBRARY'\n | 'API'\n | 'MOBILE'\n | 'DATABASE'\n | 'INFRASTRUCTURE'\n | 'CUSTOM';\n lifecycle: 'EXPERIMENTAL' | 'PRODUCTION' | 'DEPRECATED';\n language: string;\n framework: string;\n version: string;\n owner: string;\n tags: string[];\n}\n\nexport interface ComponentFormProps {\n isOpen: boolean;\n onClose: () => void;\n onSubmit: (data: ComponentFormData) => Promise<void>;\n initialData?: Partial<ComponentFormData>;\n title?: string;\n /** Optional extra content (e.g. a draft EntityTagPicker) rendered above the footer. */\n extraSection?: ReactNode;\n}\n\nexport const ComponentForm: FC<ComponentFormProps> = ({\n isOpen,\n onClose,\n onSubmit,\n initialData,\n title,\n extraSection,\n}) => {\n const { t } = useI18n();\n const tr = (key: string, fallback: string) => {\n const v = t(key);\n return v === key ? fallback : v;\n };\n\n const nameRef = useRef<HTMLInputElement>(null);\n const descriptionRef = useRef<HTMLTextAreaElement>(null);\n const typeRef = useRef<HTMLSelectElement>(null);\n const lifecycleRef = useRef<HTMLSelectElement>(null);\n const languageRef = useRef<HTMLInputElement>(null);\n const frameworkRef = useRef<HTMLInputElement>(null);\n const versionRef = useRef<HTMLInputElement>(null);\n const ownerRef = useRef<HTMLInputElement>(null);\n const tagsRef = useRef<HTMLInputElement>(null);\n\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [fieldErrors, setFieldErrors] = useState<{ name?: string }>({});\n\n if (!isOpen) return null;\n\n const handleSubmit = async () => {\n const name = nameRef.current?.value?.trim();\n if (!name) {\n setFieldErrors({ name: tr('component.nameRequired', 'Name is required') });\n setError(tr('component.fillRequired', 'Please fill in all required fields'));\n return;\n }\n setFieldErrors({});\n\n const data: ComponentFormData = {\n name,\n description: descriptionRef.current?.value?.trim() || '',\n type: (typeRef.current?.value as ComponentFormData['type']) || 'SERVICE',\n lifecycle: (lifecycleRef.current?.value as ComponentFormData['lifecycle']) || 'EXPERIMENTAL',\n language: languageRef.current?.value?.trim() || '',\n framework: frameworkRef.current?.value?.trim() || '',\n version: versionRef.current?.value?.trim() || '',\n owner: ownerRef.current?.value?.trim() || '',\n tags: tagsRef.current?.value\n ? tagsRef.current.value\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean)\n : [],\n };\n\n setLoading(true);\n setError(null);\n try {\n await onSubmit(data);\n onClose();\n } catch (err) {\n setError(\n err instanceof Error ? err.message : tr('component.failedSave', 'Failed to save component')\n );\n } finally {\n setLoading(false);\n }\n };\n\n return (\n <div className=\"fixed inset-0 bg-bg-overlay flex items-center justify-center z-50 p-4\">\n <div className=\"bg-bg-surface rounded-lg p-6 w-full max-w-lg max-h-[90vh] overflow-y-auto\">\n <div className=\"flex items-center justify-between mb-4\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {title || tr('component.createComponent', 'Create Component')}\n </h2>\n <button\n onClick={onClose}\n className=\"p-1 rounded hover:bg-bg-sunken transition-colors text-text-secondary\"\n >\n <X className=\"w-5 h-5\" />\n </button>\n </div>\n <div className=\"space-y-4\">\n {error && (\n <div className=\"p-3 bg-status-error-bg text-status-error-text text-sm rounded-md\">\n {error}\n </div>\n )}\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('component.nameLabel', 'Name *')}\n </label>\n <input\n ref={nameRef}\n type=\"text\"\n defaultValue={initialData?.name || ''}\n placeholder={tr('component.namePlaceholder', 'my-service')}\n onChange={() =>\n fieldErrors.name && setFieldErrors((e) => ({ ...e, name: undefined }))\n }\n className={`w-full px-3 py-2 bg-bg-sunken border rounded-md text-text-primary placeholder:text-text-placeholder ${fieldErrors.name ? 'border-status-error-border' : 'border-border-default'}`}\n />\n {fieldErrors.name && (\n <p className=\"mt-1 text-xs text-status-error-text\">{fieldErrors.name}</p>\n )}\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('component.descriptionLabel', 'Description')}\n </label>\n <textarea\n ref={descriptionRef}\n defaultValue={initialData?.description || ''}\n placeholder={tr('component.descriptionPlaceholder', 'Describe this component...')}\n rows={3}\n className=\"w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder resize-none\"\n />\n </div>\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-4\">\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('component.typeLabel', 'Type')}\n </label>\n <select\n ref={typeRef}\n defaultValue={initialData?.type || 'SERVICE'}\n className=\"w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary\"\n >\n <option value=\"SERVICE\">{tr('component.typeService', 'Service')}</option>\n <option value=\"FRONTEND\">{tr('component.typeFrontend', 'Frontend')}</option>\n <option value=\"LIBRARY\">{tr('component.typeLibrary', 'Library')}</option>\n <option value=\"API\">{tr('component.typeApi', 'API')}</option>\n <option value=\"MOBILE\">{tr('component.typeMobile', 'Mobile')}</option>\n <option value=\"DATABASE\">{tr('component.typeDatabase', 'Database')}</option>\n <option value=\"INFRASTRUCTURE\">\n {tr('component.typeInfrastructure', 'Infrastructure')}\n </option>\n <option value=\"CUSTOM\">{tr('component.typeCustom', 'Custom')}</option>\n </select>\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('component.lifecycleLabel', 'Lifecycle')}\n </label>\n <select\n ref={lifecycleRef}\n defaultValue={initialData?.lifecycle || 'EXPERIMENTAL'}\n className=\"w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary\"\n >\n <option value=\"EXPERIMENTAL\">\n {tr('component.lifecycleExperimental', 'Experimental')}\n </option>\n <option value=\"PRODUCTION\">\n {tr('component.lifecycleProduction', 'Production')}\n </option>\n <option value=\"DEPRECATED\">\n {tr('component.lifecycleDeprecated', 'Deprecated')}\n </option>\n </select>\n </div>\n </div>\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-4\">\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('component.languageLabel', 'Language')}\n </label>\n <input\n ref={languageRef}\n type=\"text\"\n defaultValue={initialData?.language || ''}\n placeholder={tr('component.languagePlaceholder', 'TypeScript')}\n className=\"w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder\"\n />\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('component.frameworkLabel', 'Framework')}\n </label>\n <input\n ref={frameworkRef}\n type=\"text\"\n defaultValue={initialData?.framework || ''}\n placeholder={tr('component.frameworkPlaceholder', 'React')}\n className=\"w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder\"\n />\n </div>\n </div>\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-4\">\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('component.versionLabel', 'Version')}\n </label>\n <input\n ref={versionRef}\n type=\"text\"\n defaultValue={initialData?.version || ''}\n placeholder={tr('component.versionPlaceholder', '1.0.0')}\n className=\"w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder\"\n />\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('component.ownerLabel', 'Owner')}\n </label>\n <input\n ref={ownerRef}\n type=\"text\"\n defaultValue={initialData?.owner || ''}\n placeholder={tr('component.ownerPlaceholder', 'team-name')}\n className=\"w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder\"\n />\n </div>\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('component.tagsLabel', 'Tags (comma-separated)')}\n </label>\n <input\n ref={tagsRef}\n type=\"text\"\n defaultValue={initialData?.tags?.join(', ') || ''}\n placeholder={tr('component.tagsPlaceholder', 'backend, api, graphql')}\n className=\"w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder\"\n />\n </div>\n </div>\n {extraSection && (\n <div className=\"mt-4 border-t border-border-default pt-4\">{extraSection}</div>\n )}\n <div className=\"flex flex-col sm:flex-row justify-end gap-3 mt-6\">\n <button\n onClick={onClose}\n disabled={loading}\n className=\"px-4 py-2 text-text-primary hover:bg-bg-sunken rounded-md transition-colors disabled:opacity-50\"\n >\n {tr('common.cancel', 'Cancel')}\n </button>\n <button\n onClick={handleSubmit}\n disabled={loading}\n className=\"px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors disabled:opacity-50\"\n >\n {loading\n ? tr('common.saving', 'Saving...')\n : initialData?.name\n ? tr('common.save', 'Save')\n : tr('component.createComponent', 'Create Component')}\n </button>\n </div>\n </div>\n </div>\n );\n};\n"],"mappings":"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"}
@@ -6,7 +6,11 @@ import { jsx as g, jsxs as _ } from "react/jsx-runtime";
6
6
  //#region src/components/shared/EntityTagPicker.tsx
7
7
  var v = 4e3, y = 250;
8
8
  function b({ entityType: b, entityId: x, onDraftChange: S, readOnly: C = !1 }) {
9
- let w = e(), T = !x, [E, D] = u(!1), [O, k] = u(""), [A, j] = u(null), [M, N] = u([]), { data: P, loading: F, refetch: I } = n(b, x ?? ""), { data: L, loading: R } = t(50, { skip: !E }), [z, { data: B, loading: V }] = r(), [H, { loading: U }] = i(), [W, { loading: G }] = a(), K = c(() => P?.vibecontrolsEntityTags ?? [], [P]), q = c(() => T ? M.map((e) => ({
9
+ let w = e(), T = !x, [E, D] = u(!1), [O, k] = u(""), [A, j] = u(null), [M, N] = u([]);
10
+ s(() => {
11
+ T && S?.([]);
12
+ }, []);
13
+ let { data: P, loading: F, refetch: I } = n(b, x ?? ""), { data: L, loading: R } = t(50, { skip: !E }), [z, { data: B, loading: V }] = r(), [H, { loading: U }] = i(), [W, { loading: G }] = a(), K = c(() => P?.vibecontrolsEntityTags ?? [], [P]), q = c(() => T ? M.map((e) => ({
10
14
  key: e.id,
11
15
  tagId: e.id,
12
16
  label: e.label
@@ -1 +1 @@
1
- {"version":3,"file":"EntityTagPicker.js","names":[],"sources":["../../../src/components/shared/EntityTagPicker.tsx"],"sourcesContent":["/**\n * EntityTagPicker — Reusable tag picker for any VibeControls entity\n *\n * Two modes:\n * - Persisted (entityId provided): self-fetches assignments and writes\n * immediately via tag/untag mutations. Used on detail/edit surfaces.\n * - Draft (entityId omitted/empty): no queries or mutations — selections\n * are held locally and surfaced via `onDraftChange(tagIds)`. The parent\n * create form persists them after the entity is created (e.g. via\n * replaceVibecontrolsEntityTags with the new id). Used in create\n * forms/wizards where no entity id exists yet.\n *\n * Usage:\n * <EntityTagPicker entityType=\"VIBECONTROLS_VIBE\" entityId={vibe.id} />\n * <EntityTagPicker entityType=\"VIBECONTROLS_VIBE\" onDraftChange={setTagIds} />\n */\nimport { useState, useCallback, useMemo, useEffect, useRef } from 'react';\nimport { Tag, Plus, X, Search, Loader2 } from 'lucide-react';\nimport { useTr } from '../../shared/hooks/useTr';\nimport {\n useGetEntityTags,\n useAvailableTags,\n useSearchTags,\n useTagEntity,\n useUntagEntity,\n} from '../../hooks/useEntityTags';\nimport type { AvailableTag, EntityTagAssignment } from '../../hooks/useEntityTags';\n\ninterface EntityTagPickerProps {\n entityType: string;\n /**\n * Existing entity id. Omit (or pass empty) to run in draft mode for\n * create forms — selections are emitted via `onDraftChange` instead of\n * being persisted immediately.\n */\n entityId?: string;\n /**\n * Draft-mode callback. Receives the current selected tag ids whenever\n * the draft selection changes. Only invoked when `entityId` is absent.\n */\n onDraftChange?: (tagIds: string[]) => void;\n readOnly?: boolean;\n}\n\nconst ERROR_DISMISS_MS = 4000;\nconst SEARCH_DEBOUNCE_MS = 250;\n\nexport function EntityTagPicker({\n entityType,\n entityId,\n onDraftChange,\n readOnly = false,\n}: EntityTagPickerProps) {\n const tr = useTr();\n const draftMode = !entityId;\n const [isAdding, setIsAdding] = useState(false);\n const [searchQuery, setSearchQuery] = useState('');\n const [errorMessage, setErrorMessage] = useState<string | null>(null);\n // Draft-mode local selection (no persistence until the parent flushes it).\n const [draftTags, setDraftTags] = useState<AvailableTag[]>([]);\n\n const {\n data: tagsData,\n loading: tagsLoading,\n refetch: refetchTags,\n } = useGetEntityTags(entityType, entityId ?? '');\n const { data: availableData, loading: availableLoading } = useAvailableTags(50, {\n skip: !isAdding,\n });\n const [searchTags, { data: searchData, loading: searchLoading }] = useSearchTags();\n const [tagEntity, { loading: tagging }] = useTagEntity();\n const [untagEntity, { loading: untagging }] = useUntagEntity();\n\n // Apollo Client v4 returns DeepPartialObject from useQuery data, but our\n // hook types declare full shapes — we cast back to the declared types since\n // the resolver is guaranteed to return non-null fields per the schema.\n const assignedTags = useMemo(\n () => (tagsData?.vibecontrolsEntityTags ?? []) as EntityTagAssignment[],\n [tagsData]\n );\n\n // Unified chip list — draft selections in draft mode, persisted\n // assignments otherwise. `tagId` drives removal; `key` is render-stable.\n const chipItems = useMemo(\n () =>\n draftMode\n ? draftTags.map((t) => ({ key: t.id, tagId: t.id, label: t.label }))\n : assignedTags.map((a) => ({\n key: a.id,\n tagId: a.tagId,\n label:\n a.tag?.label ?? tr('vibecontrols.shared.entityTagPicker.untitledTag', 'Untitled tag'),\n })),\n [draftMode, draftTags, assignedTags, tr]\n );\n\n const assignedTagIds = useMemo(() => new Set(chipItems.map((c) => c.tagId)), [chipItems]);\n\n const suggestions: AvailableTag[] = useMemo(() => {\n const source = searchQuery\n ? (searchData?.searchVibecontrolsTags as AvailableTag[] | undefined)\n : (availableData?.availableVibecontrolsTags as AvailableTag[] | undefined);\n return (source ?? []).filter((t) => !assignedTagIds.has(t.id));\n }, [searchQuery, searchData, availableData, assignedTagIds]);\n\n // Auto-dismiss error toast after a few seconds\n useEffect(() => {\n if (!errorMessage) return;\n const timer = setTimeout(() => setErrorMessage(null), ERROR_DISMISS_MS);\n return () => clearTimeout(timer);\n }, [errorMessage]);\n\n // Debounce the search query so we don't fire a request on every keystroke.\n const searchTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n useEffect(() => {\n return () => {\n if (searchTimerRef.current) clearTimeout(searchTimerRef.current);\n };\n }, []);\n\n const handleSearch = useCallback(\n (query: string) => {\n setSearchQuery(query);\n if (searchTimerRef.current) clearTimeout(searchTimerRef.current);\n if (query.length < 2) return;\n searchTimerRef.current = setTimeout(() => {\n searchTags({ variables: { query, limit: 10 } });\n }, SEARCH_DEBOUNCE_MS);\n },\n [searchTags]\n );\n\n const handleAddTag = useCallback(\n async (tag: AvailableTag) => {\n if (draftMode) {\n setDraftTags((prev) => {\n if (prev.some((t) => t.id === tag.id)) return prev;\n const next = [...prev, tag];\n onDraftChange?.(next.map((t) => t.id));\n return next;\n });\n setSearchQuery('');\n setErrorMessage(null);\n return;\n }\n try {\n await tagEntity({\n variables: { input: { entityType, entityId: entityId ?? '', tagId: tag.id } },\n });\n await refetchTags();\n setSearchQuery('');\n setErrorMessage(null);\n } catch (err) {\n setErrorMessage(\n err instanceof Error\n ? err.message\n : tr('vibecontrols.shared.entityTagPicker.failedToAddTag', 'Failed to add tag')\n );\n }\n },\n [draftMode, onDraftChange, tagEntity, entityType, entityId, refetchTags, tr]\n );\n\n const handleRemoveTag = useCallback(\n async (tagId: string) => {\n if (draftMode) {\n setDraftTags((prev) => {\n const next = prev.filter((t) => t.id !== tagId);\n onDraftChange?.(next.map((t) => t.id));\n return next;\n });\n setErrorMessage(null);\n return;\n }\n try {\n await untagEntity({\n variables: { input: { entityType, entityId: entityId ?? '', tagId } },\n });\n await refetchTags();\n setErrorMessage(null);\n } catch (err) {\n setErrorMessage(\n err instanceof Error\n ? err.message\n : tr('vibecontrols.shared.entityTagPicker.failedToRemoveTag', 'Failed to remove tag')\n );\n }\n },\n [draftMode, onDraftChange, untagEntity, entityType, entityId, refetchTags, tr]\n );\n\n if (tagsLoading && !draftMode) {\n return (\n <div\n className=\"flex items-center gap-2 text-text-secondary text-sm\"\n role=\"status\"\n aria-live=\"polite\"\n >\n <Loader2 className=\"h-4 w-4 animate-spin\" aria-hidden=\"true\" />\n {tr('vibecontrols.shared.entityTagPicker.loadingTags', 'Loading tags...')}\n </div>\n );\n }\n\n return (\n <div className=\"space-y-3\">\n {errorMessage && (\n <div\n role=\"alert\"\n className=\"flex items-start gap-2 rounded-md border border-red-300 bg-red-50 px-3 py-2 text-sm text-red-700\"\n >\n <span className=\"flex-1\">{errorMessage}</span>\n <button\n type=\"button\"\n onClick={() => setErrorMessage(null)}\n aria-label={tr('vibecontrols.shared.entityTagPicker.dismissError', 'Dismiss error')}\n className=\"text-red-700 hover:text-red-900\"\n >\n <X className=\"h-3.5 w-3.5\" aria-hidden=\"true\" />\n </button>\n </div>\n )}\n\n <div\n className=\"flex flex-wrap gap-2\"\n aria-label={tr('vibecontrols.shared.entityTagPicker.assignedTags', 'Assigned tags')}\n >\n {chipItems.length === 0 && !isAdding && (\n <p className=\"text-text-secondary text-sm\">\n {tr('vibecontrols.shared.entityTagPicker.noTagsAssigned', 'No tags assigned')}\n </p>\n )}\n {chipItems.map((chip) => (\n <span\n key={chip.key}\n className=\"inline-flex items-center gap-1 px-3 py-1 bg-action-primary-bg/10 text-action-primary-bg text-sm rounded-full\"\n >\n <Tag className=\"h-3 w-3\" aria-hidden=\"true\" />\n {chip.label}\n {!readOnly && (\n <button\n type=\"button\"\n onClick={() => handleRemoveTag(chip.tagId)}\n disabled={untagging}\n aria-label={`Remove tag ${chip.label}`}\n className=\"ml-1 hover:text-red-500 transition-colors disabled:opacity-50\"\n >\n <X className=\"h-3 w-3\" aria-hidden=\"true\" />\n </button>\n )}\n </span>\n ))}\n {!readOnly && !isAdding && (\n <button\n type=\"button\"\n onClick={() => setIsAdding(true)}\n aria-label={tr('vibecontrols.shared.entityTagPicker.addTag', 'Add tag')}\n className=\"inline-flex items-center gap-1 px-3 py-1 border border-dashed border-border-default text-text-secondary text-sm rounded-full hover:border-action-primary-bg hover:text-action-primary-bg transition-colors\"\n >\n <Plus className=\"h-3 w-3\" aria-hidden=\"true\" />\n {tr('vibecontrols.shared.entityTagPicker.addTag', 'Add Tag')}\n </button>\n )}\n </div>\n\n {isAdding && (\n <div className=\"border border-border-default rounded-lg p-3 space-y-2 bg-bg-surface\">\n <div className=\"relative\">\n <Search\n className=\"absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-text-secondary\"\n aria-hidden=\"true\"\n />\n <input\n type=\"text\"\n value={searchQuery}\n onChange={(e) => handleSearch(e.target.value)}\n placeholder={tr(\n 'vibecontrols.shared.entityTagPicker.searchPlaceholder',\n 'Search tags...'\n )}\n autoFocus\n aria-label={tr(\n 'vibecontrols.shared.entityTagPicker.searchAvailableTags',\n 'Search available tags'\n )}\n className=\"w-full pl-9 pr-3 py-2 text-sm bg-bg-default border border-border-default rounded-md focus:outline-none focus:ring-2 focus:ring-action-primary-bg/50 text-text-primary placeholder:text-text-secondary\"\n />\n </div>\n <div\n className=\"flex flex-wrap gap-2 max-h-32 overflow-y-auto\"\n aria-label={tr('vibecontrols.shared.entityTagPicker.tagSuggestions', 'Tag suggestions')}\n >\n {(searchLoading || (availableLoading && !searchQuery)) && (\n <span className=\"text-text-secondary text-xs\">\n {tr('vibecontrols.shared.entityTagPicker.loading', 'Loading...')}\n </span>\n )}\n {!searchLoading && !availableLoading && suggestions.length === 0 && (\n <span className=\"text-text-secondary text-xs\">\n {searchQuery\n ? tr('vibecontrols.shared.entityTagPicker.noMatchingTags', 'No matching tags')\n : tr('vibecontrols.shared.entityTagPicker.noAvailableTags', 'No available tags')}\n </span>\n )}\n {suggestions.map((tag) => (\n <button\n key={tag.id}\n type=\"button\"\n onClick={() => handleAddTag(tag)}\n disabled={tagging}\n aria-label={`Add tag ${tag.label}`}\n className=\"inline-flex items-center gap-1 px-3 py-1 bg-bg-sunken text-text-primary text-sm rounded-full hover:bg-action-primary-bg/10 hover:text-action-primary-bg transition-colors cursor-pointer disabled:opacity-50\"\n >\n <Plus className=\"h-3 w-3\" aria-hidden=\"true\" />\n {tag.label}\n </button>\n ))}\n </div>\n <button\n type=\"button\"\n onClick={() => {\n setIsAdding(false);\n setSearchQuery('');\n }}\n className=\"text-xs text-text-secondary hover:text-text-primary\"\n >\n {tr('vibecontrols.shared.entityTagPicker.done', 'Done')}\n </button>\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"EntityTagPicker.js","names":[],"sources":["../../../src/components/shared/EntityTagPicker.tsx"],"sourcesContent":["/**\n * EntityTagPicker — Reusable tag picker for any VibeControls entity\n *\n * Two modes:\n * - Persisted (entityId provided): self-fetches assignments and writes\n * immediately via tag/untag mutations. Used on detail/edit surfaces.\n * - Draft (entityId omitted/empty): no queries or mutations — selections\n * are held locally and surfaced via `onDraftChange(tagIds)`. The parent\n * create form persists them after the entity is created (e.g. via\n * replaceVibecontrolsEntityTags with the new id). Used in create\n * forms/wizards where no entity id exists yet.\n *\n * Usage:\n * <EntityTagPicker entityType=\"VIBECONTROLS_VIBE\" entityId={vibe.id} />\n * <EntityTagPicker entityType=\"VIBECONTROLS_VIBE\" onDraftChange={setTagIds} />\n */\nimport { useState, useCallback, useMemo, useEffect, useRef } from 'react';\nimport { Tag, Plus, X, Search, Loader2 } from 'lucide-react';\nimport { useTr } from '../../shared/hooks/useTr';\nimport {\n useGetEntityTags,\n useAvailableTags,\n useSearchTags,\n useTagEntity,\n useUntagEntity,\n} from '../../hooks/useEntityTags';\nimport type { AvailableTag, EntityTagAssignment } from '../../hooks/useEntityTags';\n\ninterface EntityTagPickerProps {\n entityType: string;\n /**\n * Existing entity id. Omit (or pass empty) to run in draft mode for\n * create forms — selections are emitted via `onDraftChange` instead of\n * being persisted immediately.\n */\n entityId?: string;\n /**\n * Draft-mode callback. Receives the current selected tag ids whenever\n * the draft selection changes. Only invoked when `entityId` is absent.\n */\n onDraftChange?: (tagIds: string[]) => void;\n readOnly?: boolean;\n}\n\nconst ERROR_DISMISS_MS = 4000;\nconst SEARCH_DEBOUNCE_MS = 250;\n\nexport function EntityTagPicker({\n entityType,\n entityId,\n onDraftChange,\n readOnly = false,\n}: EntityTagPickerProps) {\n const tr = useTr();\n const draftMode = !entityId;\n const [isAdding, setIsAdding] = useState(false);\n const [searchQuery, setSearchQuery] = useState('');\n const [errorMessage, setErrorMessage] = useState<string | null>(null);\n // Draft-mode local selection (no persistence until the parent flushes it).\n const [draftTags, setDraftTags] = useState<AvailableTag[]>([]);\n\n // Reset parent draft-id state on mount so a fresh create form never inherits\n // tag ids left over from a previous create on the same page-level hook.\n useEffect(() => {\n if (draftMode) onDraftChange?.([]);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n const {\n data: tagsData,\n loading: tagsLoading,\n refetch: refetchTags,\n } = useGetEntityTags(entityType, entityId ?? '');\n const { data: availableData, loading: availableLoading } = useAvailableTags(50, {\n skip: !isAdding,\n });\n const [searchTags, { data: searchData, loading: searchLoading }] = useSearchTags();\n const [tagEntity, { loading: tagging }] = useTagEntity();\n const [untagEntity, { loading: untagging }] = useUntagEntity();\n\n // Apollo Client v4 returns DeepPartialObject from useQuery data, but our\n // hook types declare full shapes — we cast back to the declared types since\n // the resolver is guaranteed to return non-null fields per the schema.\n const assignedTags = useMemo(\n () => (tagsData?.vibecontrolsEntityTags ?? []) as EntityTagAssignment[],\n [tagsData]\n );\n\n // Unified chip list — draft selections in draft mode, persisted\n // assignments otherwise. `tagId` drives removal; `key` is render-stable.\n const chipItems = useMemo(\n () =>\n draftMode\n ? draftTags.map((t) => ({ key: t.id, tagId: t.id, label: t.label }))\n : assignedTags.map((a) => ({\n key: a.id,\n tagId: a.tagId,\n label:\n a.tag?.label ?? tr('vibecontrols.shared.entityTagPicker.untitledTag', 'Untitled tag'),\n })),\n [draftMode, draftTags, assignedTags, tr]\n );\n\n const assignedTagIds = useMemo(() => new Set(chipItems.map((c) => c.tagId)), [chipItems]);\n\n const suggestions: AvailableTag[] = useMemo(() => {\n const source = searchQuery\n ? (searchData?.searchVibecontrolsTags as AvailableTag[] | undefined)\n : (availableData?.availableVibecontrolsTags as AvailableTag[] | undefined);\n return (source ?? []).filter((t) => !assignedTagIds.has(t.id));\n }, [searchQuery, searchData, availableData, assignedTagIds]);\n\n // Auto-dismiss error toast after a few seconds\n useEffect(() => {\n if (!errorMessage) return;\n const timer = setTimeout(() => setErrorMessage(null), ERROR_DISMISS_MS);\n return () => clearTimeout(timer);\n }, [errorMessage]);\n\n // Debounce the search query so we don't fire a request on every keystroke.\n const searchTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n useEffect(() => {\n return () => {\n if (searchTimerRef.current) clearTimeout(searchTimerRef.current);\n };\n }, []);\n\n const handleSearch = useCallback(\n (query: string) => {\n setSearchQuery(query);\n if (searchTimerRef.current) clearTimeout(searchTimerRef.current);\n if (query.length < 2) return;\n searchTimerRef.current = setTimeout(() => {\n searchTags({ variables: { query, limit: 10 } });\n }, SEARCH_DEBOUNCE_MS);\n },\n [searchTags]\n );\n\n const handleAddTag = useCallback(\n async (tag: AvailableTag) => {\n if (draftMode) {\n setDraftTags((prev) => {\n if (prev.some((t) => t.id === tag.id)) return prev;\n const next = [...prev, tag];\n onDraftChange?.(next.map((t) => t.id));\n return next;\n });\n setSearchQuery('');\n setErrorMessage(null);\n return;\n }\n try {\n await tagEntity({\n variables: { input: { entityType, entityId: entityId ?? '', tagId: tag.id } },\n });\n await refetchTags();\n setSearchQuery('');\n setErrorMessage(null);\n } catch (err) {\n setErrorMessage(\n err instanceof Error\n ? err.message\n : tr('vibecontrols.shared.entityTagPicker.failedToAddTag', 'Failed to add tag')\n );\n }\n },\n [draftMode, onDraftChange, tagEntity, entityType, entityId, refetchTags, tr]\n );\n\n const handleRemoveTag = useCallback(\n async (tagId: string) => {\n if (draftMode) {\n setDraftTags((prev) => {\n const next = prev.filter((t) => t.id !== tagId);\n onDraftChange?.(next.map((t) => t.id));\n return next;\n });\n setErrorMessage(null);\n return;\n }\n try {\n await untagEntity({\n variables: { input: { entityType, entityId: entityId ?? '', tagId } },\n });\n await refetchTags();\n setErrorMessage(null);\n } catch (err) {\n setErrorMessage(\n err instanceof Error\n ? err.message\n : tr('vibecontrols.shared.entityTagPicker.failedToRemoveTag', 'Failed to remove tag')\n );\n }\n },\n [draftMode, onDraftChange, untagEntity, entityType, entityId, refetchTags, tr]\n );\n\n if (tagsLoading && !draftMode) {\n return (\n <div\n className=\"flex items-center gap-2 text-text-secondary text-sm\"\n role=\"status\"\n aria-live=\"polite\"\n >\n <Loader2 className=\"h-4 w-4 animate-spin\" aria-hidden=\"true\" />\n {tr('vibecontrols.shared.entityTagPicker.loadingTags', 'Loading tags...')}\n </div>\n );\n }\n\n return (\n <div className=\"space-y-3\">\n {errorMessage && (\n <div\n role=\"alert\"\n className=\"flex items-start gap-2 rounded-md border border-red-300 bg-red-50 px-3 py-2 text-sm text-red-700\"\n >\n <span className=\"flex-1\">{errorMessage}</span>\n <button\n type=\"button\"\n onClick={() => setErrorMessage(null)}\n aria-label={tr('vibecontrols.shared.entityTagPicker.dismissError', 'Dismiss error')}\n className=\"text-red-700 hover:text-red-900\"\n >\n <X className=\"h-3.5 w-3.5\" aria-hidden=\"true\" />\n </button>\n </div>\n )}\n\n <div\n className=\"flex flex-wrap gap-2\"\n aria-label={tr('vibecontrols.shared.entityTagPicker.assignedTags', 'Assigned tags')}\n >\n {chipItems.length === 0 && !isAdding && (\n <p className=\"text-text-secondary text-sm\">\n {tr('vibecontrols.shared.entityTagPicker.noTagsAssigned', 'No tags assigned')}\n </p>\n )}\n {chipItems.map((chip) => (\n <span\n key={chip.key}\n className=\"inline-flex items-center gap-1 px-3 py-1 bg-action-primary-bg/10 text-action-primary-bg text-sm rounded-full\"\n >\n <Tag className=\"h-3 w-3\" aria-hidden=\"true\" />\n {chip.label}\n {!readOnly && (\n <button\n type=\"button\"\n onClick={() => handleRemoveTag(chip.tagId)}\n disabled={untagging}\n aria-label={`Remove tag ${chip.label}`}\n className=\"ml-1 hover:text-red-500 transition-colors disabled:opacity-50\"\n >\n <X className=\"h-3 w-3\" aria-hidden=\"true\" />\n </button>\n )}\n </span>\n ))}\n {!readOnly && !isAdding && (\n <button\n type=\"button\"\n onClick={() => setIsAdding(true)}\n aria-label={tr('vibecontrols.shared.entityTagPicker.addTag', 'Add tag')}\n className=\"inline-flex items-center gap-1 px-3 py-1 border border-dashed border-border-default text-text-secondary text-sm rounded-full hover:border-action-primary-bg hover:text-action-primary-bg transition-colors\"\n >\n <Plus className=\"h-3 w-3\" aria-hidden=\"true\" />\n {tr('vibecontrols.shared.entityTagPicker.addTag', 'Add Tag')}\n </button>\n )}\n </div>\n\n {isAdding && (\n <div className=\"border border-border-default rounded-lg p-3 space-y-2 bg-bg-surface\">\n <div className=\"relative\">\n <Search\n className=\"absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-text-secondary\"\n aria-hidden=\"true\"\n />\n <input\n type=\"text\"\n value={searchQuery}\n onChange={(e) => handleSearch(e.target.value)}\n placeholder={tr(\n 'vibecontrols.shared.entityTagPicker.searchPlaceholder',\n 'Search tags...'\n )}\n autoFocus\n aria-label={tr(\n 'vibecontrols.shared.entityTagPicker.searchAvailableTags',\n 'Search available tags'\n )}\n className=\"w-full pl-9 pr-3 py-2 text-sm bg-bg-default border border-border-default rounded-md focus:outline-none focus:ring-2 focus:ring-action-primary-bg/50 text-text-primary placeholder:text-text-secondary\"\n />\n </div>\n <div\n className=\"flex flex-wrap gap-2 max-h-32 overflow-y-auto\"\n aria-label={tr('vibecontrols.shared.entityTagPicker.tagSuggestions', 'Tag suggestions')}\n >\n {(searchLoading || (availableLoading && !searchQuery)) && (\n <span className=\"text-text-secondary text-xs\">\n {tr('vibecontrols.shared.entityTagPicker.loading', 'Loading...')}\n </span>\n )}\n {!searchLoading && !availableLoading && suggestions.length === 0 && (\n <span className=\"text-text-secondary text-xs\">\n {searchQuery\n ? tr('vibecontrols.shared.entityTagPicker.noMatchingTags', 'No matching tags')\n : tr('vibecontrols.shared.entityTagPicker.noAvailableTags', 'No available tags')}\n </span>\n )}\n {suggestions.map((tag) => (\n <button\n key={tag.id}\n type=\"button\"\n onClick={() => handleAddTag(tag)}\n disabled={tagging}\n aria-label={`Add tag ${tag.label}`}\n className=\"inline-flex items-center gap-1 px-3 py-1 bg-bg-sunken text-text-primary text-sm rounded-full hover:bg-action-primary-bg/10 hover:text-action-primary-bg transition-colors cursor-pointer disabled:opacity-50\"\n >\n <Plus className=\"h-3 w-3\" aria-hidden=\"true\" />\n {tag.label}\n </button>\n ))}\n </div>\n <button\n type=\"button\"\n onClick={() => {\n setIsAdding(false);\n setSearchQuery('');\n }}\n className=\"text-xs text-text-secondary hover:text-text-primary\"\n >\n {tr('vibecontrols.shared.entityTagPicker.done', 'Done')}\n </button>\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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"}