hazo_config 2.1.7 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/CHANGE_LOG.md +16 -0
  2. package/README.md +9 -4
  3. package/dist/components/app_config.d.ts.map +1 -1
  4. package/dist/components/app_config.js +1 -0
  5. package/dist/components/app_config_list_editor/app_config_list_editor.d.ts +1 -1
  6. package/dist/components/app_config_list_editor/app_config_list_editor.d.ts.map +1 -1
  7. package/dist/components/app_config_list_editor/app_config_list_editor.js +136 -15
  8. package/dist/components/app_config_list_editor/components/bulk_edit_modal.d.ts +11 -0
  9. package/dist/components/app_config_list_editor/components/bulk_edit_modal.d.ts.map +1 -0
  10. package/dist/components/app_config_list_editor/components/bulk_edit_modal.js +83 -0
  11. package/dist/components/app_config_list_editor/components/color_swatch_picker.d.ts +1 -1
  12. package/dist/components/app_config_list_editor/components/color_swatch_picker.d.ts.map +1 -1
  13. package/dist/components/app_config_list_editor/components/delete_dialog.d.ts +1 -1
  14. package/dist/components/app_config_list_editor/components/delete_dialog.d.ts.map +1 -1
  15. package/dist/components/app_config_list_editor/components/edit_modal.d.ts +3 -1
  16. package/dist/components/app_config_list_editor/components/edit_modal.d.ts.map +1 -1
  17. package/dist/components/app_config_list_editor/components/edit_modal.js +16 -2
  18. package/dist/components/app_config_list_editor/components/empty_state.d.ts +1 -1
  19. package/dist/components/app_config_list_editor/components/empty_state.d.ts.map +1 -1
  20. package/dist/components/app_config_list_editor/components/import_export_buttons.d.ts +1 -1
  21. package/dist/components/app_config_list_editor/components/import_export_buttons.d.ts.map +1 -1
  22. package/dist/components/app_config_list_editor/components/import_export_buttons.js +1 -0
  23. package/dist/components/app_config_list_editor/components/list_item_row.d.ts +4 -1
  24. package/dist/components/app_config_list_editor/components/list_item_row.d.ts.map +1 -1
  25. package/dist/components/app_config_list_editor/components/list_item_row.js +3 -3
  26. package/dist/components/app_config_list_editor/components/save_status_indicator.d.ts +1 -1
  27. package/dist/components/app_config_list_editor/components/save_status_indicator.d.ts.map +1 -1
  28. package/dist/components/app_config_list_editor/components/save_status_indicator.js +1 -0
  29. package/dist/components/app_config_list_editor/components/search_bar.d.ts +1 -1
  30. package/dist/components/app_config_list_editor/components/search_bar.d.ts.map +1 -1
  31. package/dist/components/app_config_list_editor/index.d.ts +2 -0
  32. package/dist/components/app_config_list_editor/index.d.ts.map +1 -1
  33. package/dist/components/app_config_list_editor/index.js +1 -0
  34. package/dist/components/app_config_list_editor/types.d.ts +25 -1
  35. package/dist/components/app_config_list_editor/types.d.ts.map +1 -1
  36. package/dist/components/config_editor.d.ts.map +1 -1
  37. package/dist/components/config_editor.js +1 -0
  38. package/dist/components/config_viewer.d.ts.map +1 -1
  39. package/dist/components/config_viewer.js +1 -0
  40. package/dist/components/index.d.ts +2 -0
  41. package/dist/components/index.d.ts.map +1 -1
  42. package/dist/components/index.js +1 -0
  43. package/dist/components/ui/alert-dialog.d.ts +2 -2
  44. package/dist/components/ui/dialog.d.ts +2 -2
  45. package/dist/components/use_app_config.d.ts.map +1 -1
  46. package/dist/components/use_app_config.js +1 -0
  47. package/dist/components/use_config_sections.d.ts.map +1 -1
  48. package/dist/components/use_config_sections.js +1 -0
  49. package/dist/lib/config_loader.d.ts +1 -0
  50. package/dist/lib/config_loader.d.ts.map +1 -1
  51. package/dist/lib/config_loader.js +35 -13
  52. package/package.json +6 -6
package/CHANGE_LOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # Changelog
2
2
 
3
+ ## 2.1.11 — 2026-06-14
4
+
5
+ ### Fixed (RSC bundling defect surfaced by hazo_admin)
6
+ - Added the `'use client'` directive to the 9 client component/hook modules re-exported from the package barrel (`config_viewer`, `config_editor`, `app_config`, `use_config_sections`, `use_app_config`, and the four `app_config_list_editor/*` files). All import React hooks (`useState`/`useEffect`/`useCallback`) but were missing the directive, so any server module that pulled in the `hazo_config` barrel — e.g. `hazo_admin`'s `createAdminPresetRoutes` install-probe (`import('hazo_config')`) inside an API route — failed `next build` with *"You're importing a module that depends on `useEffect` into a React Server Component module."* Turbopack stops at the first offending file (`edit_modal`), so the directive had to be added to every hook-using module to fully clear the error. Stories (`*.stories.tsx`) are build-excluded and left untouched.
7
+
8
+ ## 2.1.10 — 2026-06-09
9
+
10
+ ### Fixed
11
+ - 2.1.10 — expose dotted [a.b] sections via getSection('a.b'); composite-key sections stored additively alongside flat sections.
12
+
13
+ ## 2.1.9 — 2026-06-04
14
+
15
+ ### Fixed
16
+ - `hazo_core/errors` probe: replaced the synchronous `createRequire('hazo_core/errors')` optional probe with a lazy `import(/* turbopackIgnore: true */ /* webpackIgnore: true */ 'hazo_core/errors')` fired at module init. Eliminates the `⚠ Module not found: Can't resolve 'hazo_core/errors'` Turbopack static-analysis warning in every Next 16 app that includes hazo_config in its server bundle. Also fixes a latent bug: the old `_require` call was dead code because `hazo_core/errors` is an ESM-only subpath export that CJS `require()` never resolved — `HazoConfigError` was always silently falling back to `LocalConfigError`. The new `import()` form actually resolves at runtime.
17
+ - `refresh()` null-prototype crash: `ini` v4 uses `Object.create(null)` for all parsed objects and creates nested null-prototype objects for dotted section names (e.g. `[parent.child]` → `parsed.parent = { child: {...} }`). Calling `String()` on a null-prototype object throws `"Cannot convert object to primitive value"` because there is no inherited `toString()`. Fixed by skipping nested object values in the value loop — hazo config uses flat sections; the dotted section name itself is the correct access key.
18
+
3
19
  ## 2.1.7 — 2026-05-30
4
20
 
5
21
  ### Fixed (bundling defect surfaced by seo_monitor)
package/README.md CHANGED
@@ -13,9 +13,9 @@ A React component library for managing configuration with database-backed storag
13
13
 
14
14
  ## Quick Start
15
15
 
16
- 1. Install the package:
16
+ 1. Install the package (with its hazo peers):
17
17
  ```bash
18
- npm install hazo_config
18
+ npm install hazo_config hazo_core hazo_ui
19
19
  ```
20
20
 
21
21
  2. Add CSS custom properties to your `globals.css`:
@@ -59,7 +59,7 @@ See detailed setup instructions below.
59
59
  ### Installation
60
60
 
61
61
  ```bash
62
- npm install hazo_config
62
+ npm install hazo_config hazo_core hazo_ui
63
63
  ```
64
64
 
65
65
  ### Styling Setup (Required)
@@ -150,7 +150,7 @@ import type { AppConfigItem, AppConfigContext, ConfigType } from 'hazo_config'
150
150
  import { HazoConfig } from 'hazo_config/server'
151
151
  ```
152
152
 
153
- **Why?** The `HazoConfig` class uses Node.js `fs` and `path` modules. Importing it in client code causes "Module not found: Can't resolve 'fs'" errors. The `/server` entry point uses the `server-only` package to prevent accidental client imports.
153
+ **Why?** The `HazoConfig` class uses Node.js `fs` and `path` modules. Importing it in client code causes "Module not found: Can't resolve 'fs'" errors. The `/server` entry point is intentionally kept without the `server-only` marker — Next.js's build phase evaluates route modules without the `react-server` condition, causing `server-only` to throw during `next build`. A real client-side import of `HazoConfig` still fails at runtime due to the `fs`/`path` usage.
154
154
 
155
155
  ### Major Version 2.0 Changes
156
156
 
@@ -171,6 +171,11 @@ import { HazoConfig } from 'hazo_config/server'
171
171
 
172
172
  const config = new HazoConfig({ filePath: './config.ini' })
173
173
  const dbHost = config.get('database', 'host')
174
+
175
+ // Dotted section names (e.g. [parent.child] in INI) are accessed as-is:
176
+ // [parent.child]
177
+ // key = value
178
+ const section = config.getSection('parent.child') // { key: 'value' }
174
179
  ```
175
180
 
176
181
  ### Example: Client-side Config Display (INI-based)
@@ -1 +1 @@
1
- {"version":3,"file":"app_config.d.ts","sourceRoot":"","sources":["../../src/components/app_config.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAyC,MAAM,OAAO,CAAA;AAK7D,OAAO,KAAK,EAAE,cAAc,EAA6B,MAAM,4BAA4B,CAAA;AA0P3F;;;;;;GAMG;AACH,eAAO,MAAM,SAAS,EAAE,KAAK,CAAC,EAAE,CAAC,cAAc,CA6a9C,CAAA"}
1
+ {"version":3,"file":"app_config.d.ts","sourceRoot":"","sources":["../../src/components/app_config.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAyC,MAAM,OAAO,CAAA;AAK7D,OAAO,KAAK,EAAE,cAAc,EAA6B,MAAM,4BAA4B,CAAA;AA0P3F;;;;;;GAMG;AACH,eAAO,MAAM,SAAS,EAAE,KAAK,CAAC,EAAE,CAAC,cAAc,CA6a9C,CAAA"}
@@ -1,3 +1,4 @@
1
+ 'use client';
1
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
3
  // AppConfig component for managing database-backed configuration
3
4
  // Provides UI for viewing and editing configuration stored in hazo_app_config table
@@ -3,5 +3,5 @@ import type { AppConfigListEditorProps } from './types.js';
3
3
  * AppConfigListEditor - A polished CRUD list editor for config item arrays.
4
4
  * Purely callback-based: no API calls, no database access.
5
5
  */
6
- export declare function AppConfigListEditor<T extends Record<string, unknown>>({ items, on_items_change, columns, id_field, auto_id_from, title, description, enable_search, search_threshold, render_item, render_item_indicator, render_preview, delete_confirmation, max_items, id_editable_after_create, className, save_status, edit_modal_max_width, }: AppConfigListEditorProps<T>): import("react/jsx-runtime").JSX.Element;
6
+ export declare function AppConfigListEditor<T extends Record<string, unknown>>({ items, on_items_change, columns, id_field, auto_id_from, title, description, enable_search, search_threshold, render_item, render_item_indicator, render_preview, delete_confirmation, max_items, id_editable_after_create, className, save_status, edit_modal_max_width, enable_selection, bulk_edit_fields, on_bulk_update, on_bulk_delete, on_item_create, on_item_update, on_item_delete, on_reload, }: AppConfigListEditorProps<T>): import("react").JSX.Element;
7
7
  //# sourceMappingURL=app_config_list_editor.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"app_config_list_editor.d.ts","sourceRoot":"","sources":["../../../src/components/app_config_list_editor/app_config_list_editor.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,wBAAwB,EAAqC,MAAM,YAAY,CAAA;AAS7F;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EACrE,KAAK,EACL,eAAe,EACf,OAAO,EACP,QAAQ,EACR,YAAY,EACZ,KAAK,EACL,WAAW,EACX,aAAqB,EACrB,gBAAoB,EACpB,WAAW,EACX,qBAAqB,EACrB,cAAc,EACd,mBAAmB,EACnB,SAAS,EACT,wBAAgC,EAChC,SAAS,EACT,WAAoB,EACpB,oBAAoB,GACrB,EAAE,wBAAwB,CAAC,CAAC,CAAC,2CAoQ7B"}
1
+ {"version":3,"file":"app_config_list_editor.d.ts","sourceRoot":"","sources":["../../../src/components/app_config_list_editor/app_config_list_editor.tsx"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,wBAAwB,EAAqC,MAAM,YAAY,CAAA;AAU7F;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EACrE,KAAK,EACL,eAAe,EACf,OAAO,EACP,QAAQ,EACR,YAAY,EACZ,KAAK,EACL,WAAW,EACX,aAAqB,EACrB,gBAAoB,EACpB,WAAW,EACX,qBAAqB,EACrB,cAAc,EACd,mBAAmB,EACnB,SAAS,EACT,wBAAgC,EAChC,SAAS,EACT,WAAoB,EACpB,oBAAoB,EAEpB,gBAAgB,EAChB,gBAAgB,EAChB,cAAc,EACd,cAAc,EAEd,cAAc,EACd,cAAc,EACd,cAAc,EACd,SAAS,GACV,EAAE,wBAAwB,CAAC,CAAC,CAAC,+BAmb7B"}
@@ -1,8 +1,9 @@
1
+ 'use client';
1
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
3
  // AppConfigListEditor - Main component
3
4
  // Generic CRUD list editor for arrays of structured objects stored as JSON config
4
5
  import { useState, useCallback, useMemo } from 'react';
5
- import { Plus } from 'lucide-react';
6
+ import { Plus, Trash2 } from 'lucide-react';
6
7
  import { cn } from 'hazo_ui';
7
8
  import { ListItemRow } from './components/list_item_row.js';
8
9
  import { EditModal } from './components/edit_modal.js';
@@ -11,14 +12,22 @@ import { EmptyState } from './components/empty_state.js';
11
12
  import { DeleteDialog } from './components/delete_dialog.js';
12
13
  import { SaveStatusIndicator } from './components/save_status_indicator.js';
13
14
  import { ImportExportButtons } from './components/import_export_buttons.js';
15
+ import { BulkEditModal } from './components/bulk_edit_modal.js';
14
16
  /**
15
17
  * AppConfigListEditor - A polished CRUD list editor for config item arrays.
16
18
  * Purely callback-based: no API calls, no database access.
17
19
  */
18
- export function AppConfigListEditor({ items, on_items_change, columns, id_field, auto_id_from, title, description, enable_search = false, search_threshold = 8, render_item, render_item_indicator, render_preview, delete_confirmation, max_items, id_editable_after_create = false, className, save_status = 'idle', edit_modal_max_width, }) {
20
+ export function AppConfigListEditor({ items, on_items_change, columns, id_field, auto_id_from, title, description, enable_search = false, search_threshold = 8, render_item, render_item_indicator, render_preview, delete_confirmation, max_items, id_editable_after_create = false, className, save_status = 'idle', edit_modal_max_width,
21
+ // Selection + bulk
22
+ enable_selection, bulk_edit_fields, on_bulk_update, on_bulk_delete,
23
+ // Async adapter
24
+ on_item_create, on_item_update, on_item_delete, on_reload, }) {
19
25
  const [search_term, set_search_term] = useState('');
20
26
  const [edit_state, set_edit_state] = useState(null);
21
27
  const [delete_state, set_delete_state] = useState(null);
28
+ const [selected_ids, set_selected_ids] = useState(new Set());
29
+ const [bulk_edit_open, set_bulk_edit_open] = useState(false);
30
+ const [is_saving, set_is_saving] = useState(false);
22
31
  // Derive a friendly item label from the title (e.g., "Classification Tags" -> "tags")
23
32
  const item_label = useMemo(() => {
24
33
  if (!title)
@@ -49,6 +58,48 @@ export function AppConfigListEditor({ items, on_items_change, columns, id_field,
49
58
  }, [items, search_term, columns]);
50
59
  const show_search = enable_search && items.length > search_threshold;
51
60
  const is_max_reached = max_items !== undefined && max_items !== null && items.length >= max_items;
61
+ // Selection helpers
62
+ const toggle_row = useCallback((id) => {
63
+ set_selected_ids(prev => {
64
+ const next = new Set(prev);
65
+ if (next.has(id)) {
66
+ next.delete(id);
67
+ }
68
+ else {
69
+ next.add(id);
70
+ }
71
+ return next;
72
+ });
73
+ }, []);
74
+ const toggle_select_all = useCallback(() => {
75
+ const filtered_ids = filtered_items.map(item => String(item[id_field]));
76
+ const all_selected = filtered_ids.length > 0 && filtered_ids.every(id => selected_ids.has(id));
77
+ if (all_selected) {
78
+ // Deselect all filtered
79
+ set_selected_ids(prev => {
80
+ const next = new Set(prev);
81
+ for (const id of filtered_ids)
82
+ next.delete(id);
83
+ return next;
84
+ });
85
+ }
86
+ else {
87
+ // Select all filtered
88
+ set_selected_ids(prev => {
89
+ const next = new Set(prev);
90
+ for (const id of filtered_ids)
91
+ next.add(id);
92
+ return next;
93
+ });
94
+ }
95
+ }, [filtered_items, id_field, selected_ids]);
96
+ const clear_selection = useCallback(() => {
97
+ set_selected_ids(new Set());
98
+ }, []);
99
+ // Bulk selection state
100
+ const filtered_ids = useMemo(() => filtered_items.map(item => String(item[id_field])), [filtered_items, id_field]);
101
+ const all_filtered_selected = filtered_ids.length > 0 && filtered_ids.every(id => selected_ids.has(id));
102
+ const some_filtered_selected = filtered_ids.some(id => selected_ids.has(id));
52
103
  // CRUD handlers
53
104
  const handle_add = useCallback(() => {
54
105
  const empty_item = {};
@@ -79,34 +130,100 @@ export function AppConfigListEditor({ items, on_items_change, columns, id_field,
79
130
  }, []);
80
131
  const handle_delete_request = useCallback((item) => {
81
132
  if (!delete_confirmation) {
82
- // No confirmation needed, delete immediately
83
- on_items_change(items.filter((i) => i[id_field] !== item[id_field]));
133
+ // No confirmation needed use async adapter or on_items_change
134
+ if (on_item_delete) {
135
+ const id = String(item[id_field]);
136
+ set_is_saving(true);
137
+ on_item_delete(id)
138
+ .then(() => on_reload?.())
139
+ .finally(() => set_is_saving(false));
140
+ }
141
+ else {
142
+ on_items_change(items.filter((i) => i[id_field] !== item[id_field]));
143
+ }
84
144
  return;
85
145
  }
86
146
  set_delete_state({ item });
87
- }, [delete_confirmation, items, id_field, on_items_change]);
88
- const handle_delete_confirm = useCallback(() => {
147
+ }, [delete_confirmation, items, id_field, on_items_change, on_item_delete, on_reload]);
148
+ const handle_delete_confirm = useCallback(async () => {
89
149
  if (!delete_state)
90
150
  return;
91
- on_items_change(items.filter((i) => i[id_field] !== delete_state.item[id_field]));
151
+ if (on_item_delete) {
152
+ const id = String(delete_state.item[id_field]);
153
+ set_is_saving(true);
154
+ try {
155
+ await on_item_delete(id);
156
+ await on_reload?.();
157
+ }
158
+ finally {
159
+ set_is_saving(false);
160
+ }
161
+ }
162
+ else {
163
+ on_items_change(items.filter((i) => i[id_field] !== delete_state.item[id_field]));
164
+ }
92
165
  set_delete_state(null);
93
- }, [delete_state, items, id_field, on_items_change]);
94
- const handle_save = useCallback((saved_item) => {
166
+ }, [delete_state, items, id_field, on_items_change, on_item_delete, on_reload]);
167
+ const handle_save = useCallback(async (saved_item) => {
95
168
  if (edit_state?.mode === 'create') {
96
- on_items_change([...items, saved_item]);
169
+ if (on_item_create) {
170
+ set_is_saving(true);
171
+ try {
172
+ await on_item_create(saved_item);
173
+ await on_reload?.();
174
+ }
175
+ finally {
176
+ set_is_saving(false);
177
+ }
178
+ }
179
+ else {
180
+ on_items_change([...items, saved_item]);
181
+ }
97
182
  }
98
183
  else if (edit_state?.mode === 'edit') {
99
- const original_id = edit_state.original_item?.[id_field];
100
- on_items_change(items.map((i) => (i[id_field] === original_id ? saved_item : i)));
184
+ const original_id = String(edit_state.original_item?.[id_field] ?? '');
185
+ if (on_item_update) {
186
+ set_is_saving(true);
187
+ try {
188
+ await on_item_update(original_id, saved_item);
189
+ await on_reload?.();
190
+ }
191
+ finally {
192
+ set_is_saving(false);
193
+ }
194
+ }
195
+ else {
196
+ on_items_change(items.map((i) => (i[id_field] === edit_state.original_item?.[id_field] ? saved_item : i)));
197
+ }
101
198
  }
102
199
  set_edit_state(null);
103
- }, [edit_state, items, id_field, on_items_change]);
200
+ }, [edit_state, items, id_field, on_items_change, on_item_create, on_item_update, on_reload]);
104
201
  const handle_cancel_edit = useCallback(() => {
105
202
  set_edit_state(null);
106
203
  }, []);
107
204
  const handle_cancel_delete = useCallback(() => {
108
205
  set_delete_state(null);
109
206
  }, []);
207
+ // Bulk action handlers
208
+ const handle_bulk_edit_confirm = useCallback(async (patch) => {
209
+ if (on_bulk_update) {
210
+ await on_bulk_update(Array.from(selected_ids), patch);
211
+ }
212
+ clear_selection();
213
+ set_bulk_edit_open(false);
214
+ }, [on_bulk_update, selected_ids, clear_selection]);
215
+ const handle_bulk_delete = useCallback(async () => {
216
+ const ids = Array.from(selected_ids);
217
+ if (ids.length > 1) {
218
+ const confirmed = window.confirm(`Delete ${ids.length} selected items? This cannot be undone.`);
219
+ if (!confirmed)
220
+ return;
221
+ }
222
+ if (on_bulk_delete) {
223
+ await on_bulk_delete(ids);
224
+ }
225
+ clear_selection();
226
+ }, [on_bulk_delete, selected_ids, clear_selection]);
110
227
  // Get delete confirmation message
111
228
  const delete_message = useMemo(() => {
112
229
  if (!delete_state || !delete_confirmation)
@@ -126,7 +243,11 @@ export function AppConfigListEditor({ items, on_items_change, columns, id_field,
126
243
  }
127
244
  return String(delete_state.item[id_field] ?? '');
128
245
  }, [delete_state, columns, id_field]);
129
- return (_jsxs("div", { className: cn('cls_list_editor', className), children: [(title || description) && (_jsxs("div", { className: "cls_list_editor_header mb-6", children: [title && (_jsxs("div", { className: "flex items-center gap-3", children: [_jsx("h2", { className: "text-2xl font-bold text-gray-900", children: title }), _jsx(SaveStatusIndicator, { status: save_status })] })), description && (_jsx("p", { className: "text-sm text-gray-500 mt-1", children: description }))] })), _jsxs("div", { className: "cls_list_editor_section_bar flex items-center justify-between mb-3", children: [_jsx("div", { className: "flex items-center gap-2", children: _jsx("span", { className: "text-xs font-semibold uppercase tracking-wider text-gray-500", children: section_label }) }), _jsxs("div", { className: "flex items-center gap-1.5", children: [_jsx(ImportExportButtons, { items: items, on_items_change: on_items_change, columns: columns, id_field: id_field, title: title, max_items: max_items }), _jsxs("button", { type: "button", onClick: handle_add, disabled: is_max_reached, className: cn('inline-flex items-center gap-1.5 px-3 py-1.5 text-sm font-medium rounded-full transition-colors', is_max_reached
246
+ const selection_count = selected_ids.size;
247
+ return (_jsxs("div", { className: cn('cls_list_editor', className), children: [(title || description) && (_jsxs("div", { className: "cls_list_editor_header mb-6", children: [title && (_jsxs("div", { className: "flex items-center gap-3", children: [_jsx("h2", { className: "text-2xl font-bold text-gray-900", children: title }), _jsx(SaveStatusIndicator, { status: save_status })] })), description && (_jsx("p", { className: "text-sm text-gray-500 mt-1", children: description }))] })), _jsxs("div", { className: "cls_list_editor_section_bar flex items-center justify-between mb-3", children: [_jsxs("div", { className: "flex items-center gap-2", children: [enable_selection && filtered_items.length > 0 && (_jsx("input", { type: "checkbox", checked: all_filtered_selected, ref: (el) => {
248
+ if (el)
249
+ el.indeterminate = !all_filtered_selected && some_filtered_selected;
250
+ }, onChange: toggle_select_all, className: "w-4 h-4 rounded border-gray-300 text-violet-600 focus:ring-violet-500", "aria-label": "Select all" })), _jsx("span", { className: "text-xs font-semibold uppercase tracking-wider text-gray-500", children: section_label })] }), _jsxs("div", { className: "flex items-center gap-1.5", children: [_jsx(ImportExportButtons, { items: items, on_items_change: on_items_change, columns: columns, id_field: id_field, title: title, max_items: max_items }), _jsxs("button", { type: "button", onClick: handle_add, disabled: is_max_reached, className: cn('inline-flex items-center gap-1.5 px-3 py-1.5 text-sm font-medium rounded-full transition-colors', is_max_reached
130
251
  ? 'bg-gray-100 text-gray-400 cursor-not-allowed'
131
- : 'bg-violet-600 hover:bg-violet-700 text-white'), title: is_max_reached ? `Maximum of ${max_items} items reached` : undefined, children: [_jsx(Plus, { className: "w-4 h-4" }), "Add"] })] })] }), show_search && (_jsx("div", { className: "mb-3", children: _jsx(SearchBar, { value: search_term, on_change: set_search_term, item_count: filtered_items.length, item_label: item_label }) })), _jsx("div", { className: "cls_list_editor_card rounded-xl border border-gray-200 bg-white overflow-hidden", children: items.length === 0 ? (_jsx(EmptyState, { item_label: item_label, on_add: handle_add })) : filtered_items.length === 0 ? (_jsxs("div", { className: "py-8 text-center text-sm text-gray-500", children: ["No ", item_label, " matching \u201C", search_term, "\u201D"] })) : (_jsx("div", { className: "divide-y divide-gray-100", children: filtered_items.map((item, index) => (_jsx(ListItemRow, { item: item, index: index, columns: columns, render_item: render_item, render_item_indicator: render_item_indicator, on_edit: () => handle_edit(item), on_delete: () => handle_delete_request(item) }, String(item[id_field])))) })) }), is_max_reached && (_jsxs("p", { className: "text-xs text-gray-400 mt-2", children: ["Maximum of ", max_items, " ", item_label, " reached."] })), edit_state && (_jsx(EditModal, { open: true, mode: edit_state.mode, item: edit_state.item, columns: columns, id_field: id_field, auto_id_from: auto_id_from, id_editable_after_create: id_editable_after_create, existing_ids: existing_ids, item_type_label: item_type_label, render_preview: render_preview, on_save: handle_save, on_cancel: handle_cancel_edit, max_width_class: edit_modal_max_width })), delete_state && (_jsx(DeleteDialog, { open: true, item_name: delete_item_name, message: delete_message, on_confirm: handle_delete_confirm, on_cancel: handle_cancel_delete }))] }));
252
+ : 'bg-violet-600 hover:bg-violet-700 text-white'), title: is_max_reached ? `Maximum of ${max_items} items reached` : undefined, children: [_jsx(Plus, { className: "w-4 h-4" }), "Add"] })] })] }), enable_selection && selection_count > 0 && (_jsxs("div", { className: "cls_bulk_action_bar flex items-center gap-3 mb-3 px-4 py-2.5 bg-violet-50 border border-violet-200 rounded-xl", children: [_jsxs("span", { className: "text-sm font-medium text-violet-800", children: [selection_count, " selected"] }), _jsx("button", { type: "button", onClick: clear_selection, className: "text-xs text-violet-600 hover:text-violet-800 underline", children: "Clear" }), _jsx("div", { className: "flex-1" }), bulk_edit_fields && bulk_edit_fields.length > 0 && on_bulk_update && (_jsx("button", { type: "button", onClick: () => set_bulk_edit_open(true), className: "inline-flex items-center gap-1.5 px-3 py-1.5 text-sm font-medium rounded-full bg-violet-600 hover:bg-violet-700 text-white transition-colors", children: "Edit selected" })), on_bulk_delete && (_jsxs("button", { type: "button", onClick: handle_bulk_delete, className: "inline-flex items-center gap-1.5 px-3 py-1.5 text-sm font-medium rounded-full bg-red-600 hover:bg-red-700 text-white transition-colors", children: [_jsx(Trash2, { className: "w-3.5 h-3.5" }), "Delete selected"] }))] })), show_search && (_jsx("div", { className: "mb-3", children: _jsx(SearchBar, { value: search_term, on_change: set_search_term, item_count: filtered_items.length, item_label: item_label }) })), _jsx("div", { className: "cls_list_editor_card rounded-xl border border-gray-200 bg-white overflow-hidden", children: items.length === 0 ? (_jsx(EmptyState, { item_label: item_label, on_add: handle_add })) : filtered_items.length === 0 ? (_jsxs("div", { className: "py-8 text-center text-sm text-gray-500", children: ["No ", item_label, " matching \u201C", search_term, "\u201D"] })) : (_jsx("div", { className: "divide-y divide-gray-100", children: filtered_items.map((item, index) => (_jsx(ListItemRow, { item: item, index: index, columns: columns, render_item: render_item, render_item_indicator: render_item_indicator, on_edit: () => handle_edit(item), on_delete: () => handle_delete_request(item), show_checkbox: enable_selection, is_selected: selected_ids.has(String(item[id_field])), on_toggle_select: () => toggle_row(String(item[id_field])) }, String(item[id_field])))) })) }), is_max_reached && (_jsxs("p", { className: "text-xs text-gray-400 mt-2", children: ["Maximum of ", max_items, " ", item_label, " reached."] })), edit_state && (_jsx(EditModal, { open: true, mode: edit_state.mode, item: edit_state.item, columns: columns, id_field: id_field, auto_id_from: auto_id_from, id_editable_after_create: id_editable_after_create, existing_ids: existing_ids, item_type_label: item_type_label, render_preview: render_preview, on_save: handle_save, on_cancel: handle_cancel_edit, max_width_class: edit_modal_max_width, is_saving: is_saving })), delete_state && (_jsx(DeleteDialog, { open: true, item_name: delete_item_name, message: delete_message, on_confirm: handle_delete_confirm, on_cancel: handle_cancel_delete })), bulk_edit_fields && bulk_edit_fields.length > 0 && (_jsx(BulkEditModal, { open: bulk_edit_open, selected_count: selection_count, columns: columns, bulk_edit_fields: bulk_edit_fields, on_confirm: handle_bulk_edit_confirm, on_cancel: () => set_bulk_edit_open(false) }))] }));
132
253
  }
@@ -0,0 +1,11 @@
1
+ import type { ColumnDef } from '../types.js';
2
+ export interface BulkEditModalProps<T extends Record<string, unknown>> {
3
+ open: boolean;
4
+ selected_count: number;
5
+ columns: ColumnDef<T>[];
6
+ bulk_edit_fields: (keyof T & string)[];
7
+ on_confirm: (patch: Partial<T>) => void;
8
+ on_cancel: () => void;
9
+ }
10
+ export declare function BulkEditModal<T extends Record<string, unknown>>({ open, selected_count, columns, bulk_edit_fields, on_confirm, on_cancel, }: BulkEditModalProps<T>): import("react").JSX.Element;
11
+ //# sourceMappingURL=bulk_edit_modal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bulk_edit_modal.d.ts","sourceRoot":"","sources":["../../../../src/components/app_config_list_editor/components/bulk_edit_modal.tsx"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAE5C,MAAM,WAAW,kBAAkB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IACnE,IAAI,EAAE,OAAO,CAAA;IACb,cAAc,EAAE,MAAM,CAAA;IACtB,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC,EAAE,CAAA;IACvB,gBAAgB,EAAE,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC,EAAE,CAAA;IACtC,UAAU,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,KAAK,IAAI,CAAA;IACvC,SAAS,EAAE,MAAM,IAAI,CAAA;CACtB;AAED,wBAAgB,aAAa,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EAC/D,IAAI,EACJ,cAAc,EACd,OAAO,EACP,gBAAgB,EAChB,UAAU,EACV,SAAS,GACV,EAAE,kBAAkB,CAAC,CAAC,CAAC,+BAuOvB"}
@@ -0,0 +1,83 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ // Bulk edit modal component
4
+ // Allows editing shared fields across multiple selected items
5
+ import { useState, useCallback, useEffect } from 'react';
6
+ import { cn } from 'hazo_ui';
7
+ import { Input } from '../../ui/input.js';
8
+ import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, } from '../../ui/dialog.js';
9
+ import { Button } from '../../ui/button.js';
10
+ export function BulkEditModal({ open, selected_count, columns, bulk_edit_fields, on_confirm, on_cancel, }) {
11
+ // Only the columns that are listed in bulk_edit_fields
12
+ const editable_cols = columns.filter(c => bulk_edit_fields.includes(c.field));
13
+ // form_data holds the current value for each field
14
+ const [form_data, set_form_data] = useState({});
15
+ // enabled_fields tracks which fields the user has opted into
16
+ const [enabled_fields, set_enabled_fields] = useState(new Set());
17
+ // Reset on open
18
+ useEffect(() => {
19
+ if (open) {
20
+ const initial = {};
21
+ for (const col of editable_cols) {
22
+ if (col.type === 'toggle') {
23
+ initial[col.field] = false;
24
+ }
25
+ else if (col.type === 'tag_picker') {
26
+ initial[col.field] = [];
27
+ }
28
+ else {
29
+ initial[col.field] = '';
30
+ }
31
+ }
32
+ set_form_data(initial);
33
+ set_enabled_fields(new Set());
34
+ }
35
+ }, [open]); // eslint-disable-line react-hooks/exhaustive-deps
36
+ const update_field = useCallback((field, value) => {
37
+ set_form_data(prev => ({ ...prev, [field]: value }));
38
+ }, []);
39
+ const toggle_field_enabled = useCallback((field) => {
40
+ set_enabled_fields(prev => {
41
+ const next = new Set(prev);
42
+ if (next.has(field)) {
43
+ next.delete(field);
44
+ }
45
+ else {
46
+ next.add(field);
47
+ }
48
+ return next;
49
+ });
50
+ }, []);
51
+ const handle_confirm = useCallback(() => {
52
+ const patch = {};
53
+ for (const col of editable_cols) {
54
+ if (enabled_fields.has(col.field)) {
55
+ patch[col.field] = form_data[col.field];
56
+ }
57
+ }
58
+ on_confirm(patch);
59
+ }, [editable_cols, enabled_fields, form_data, on_confirm]);
60
+ const render_field_control = (col) => {
61
+ const value = form_data[col.field];
62
+ const is_enabled = enabled_fields.has(col.field);
63
+ if (col.type === 'toggle') {
64
+ return (_jsx("button", { type: "button", role: "switch", "aria-checked": Boolean(value), disabled: !is_enabled, onClick: () => update_field(col.field, !value), className: cn('relative inline-flex h-5 w-9 shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors', value ? 'bg-primary' : 'bg-gray-200', !is_enabled && 'opacity-40 cursor-not-allowed'), children: _jsx("span", { className: cn('pointer-events-none inline-block h-4 w-4 transform rounded-full bg-white shadow ring-0 transition-transform', value ? 'translate-x-4' : 'translate-x-0') }) }));
65
+ }
66
+ if (col.type === 'select' && col.options) {
67
+ return (_jsxs("select", { value: String(value ?? ''), disabled: !is_enabled, onChange: (e) => update_field(col.field, e.target.value), className: cn('flex h-10 w-full rounded-lg border border-input bg-background px-3 py-2 text-sm ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-violet-500/20 focus-visible:border-violet-300', !is_enabled && 'opacity-40 cursor-not-allowed bg-gray-50'), children: [_jsx("option", { value: "", children: col.placeholder || 'Select...' }), col.options.map((opt) => (_jsx("option", { value: opt.value, children: opt.label }, opt.value)))] }));
68
+ }
69
+ if (col.type === 'textarea') {
70
+ return (_jsx("textarea", { value: String(value ?? ''), disabled: !is_enabled, onChange: (e) => update_field(col.field, e.target.value), placeholder: col.placeholder, rows: 3, className: cn('flex w-full rounded-lg border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-violet-500/20 focus-visible:border-violet-300 disabled:cursor-not-allowed disabled:opacity-50', !is_enabled && 'opacity-40 bg-gray-50') }));
71
+ }
72
+ if (col.type === 'number') {
73
+ return (_jsx(Input, { type: "number", value: value !== undefined && value !== null ? String(value) : '', disabled: !is_enabled, onChange: (e) => update_field(col.field, e.target.value === '' ? '' : Number(e.target.value)), placeholder: col.placeholder, className: cn('rounded-lg focus-visible:ring-violet-500/20 focus-visible:ring-offset-0', !is_enabled && 'opacity-40 bg-gray-50') }));
74
+ }
75
+ // Default: text
76
+ return (_jsx(Input, { type: "text", value: String(value ?? ''), disabled: !is_enabled, onChange: (e) => update_field(col.field, e.target.value), placeholder: col.placeholder, className: cn('rounded-lg focus-visible:ring-violet-500/20 focus-visible:ring-offset-0', !is_enabled && 'opacity-40 bg-gray-50') }));
77
+ };
78
+ return (_jsx(Dialog, { open: open, onOpenChange: (is_open) => { if (!is_open)
79
+ on_cancel(); }, children: _jsxs(DialogContent, { className: "cls_bulk_edit_modal w-[95vw] rounded-2xl p-0 gap-0 overflow-hidden max-h-[90vh] flex flex-col sm:max-w-lg", children: [_jsxs(DialogHeader, { className: "px-6 pt-6 pb-4 shrink-0", children: [_jsxs(DialogTitle, { children: ["Edit ", selected_count, " selected items"] }), _jsx("p", { className: "text-sm text-gray-500 mt-1", children: "Enable a field below to apply a new value to all selected items. Disabled fields are left unchanged." })] }), _jsxs("div", { className: "px-6 pb-4 space-y-3 overflow-y-auto flex-1 min-h-0", children: [editable_cols.map((col) => {
80
+ const is_enabled = enabled_fields.has(col.field);
81
+ return (_jsxs("div", { className: "cls_bulk_edit_field", children: [_jsxs("div", { className: "flex items-center gap-2 mb-1.5", children: [_jsx("input", { type: "checkbox", id: `bulk_enable_${col.field}`, checked: is_enabled, onChange: () => toggle_field_enabled(col.field), className: "w-4 h-4 rounded border-gray-300 text-violet-600 focus:ring-violet-500" }), _jsxs("label", { htmlFor: `bulk_enable_${col.field}`, className: cn('text-sm font-medium cursor-pointer select-none', is_enabled ? 'text-gray-700' : 'text-gray-400'), children: [col.label, !is_enabled && (_jsx("span", { className: "ml-2 text-xs font-normal text-gray-400", children: "Leave unchanged" }))] })] }), _jsx("div", { className: cn('transition-opacity', !is_enabled && 'opacity-50'), children: col.type === 'toggle' ? (_jsxs("div", { className: "flex items-center justify-between py-1 pl-6", children: [_jsx("span", { className: "text-sm text-gray-500", children: col.label }), render_field_control(col)] })) : (_jsx("div", { className: "pl-6", children: render_field_control(col) })) })] }, col.field));
82
+ }), editable_cols.length === 0 && (_jsx("p", { className: "text-sm text-gray-400 py-4 text-center", children: "No editable fields configured." }))] }), _jsxs(DialogFooter, { className: "bg-gray-50 px-6 py-4 border-t border-gray-100 sm:justify-between shrink-0", children: [_jsx(Button, { type: "button", variant: "ghost", onClick: on_cancel, className: "rounded-full", children: "Cancel" }), _jsxs(Button, { type: "button", onClick: handle_confirm, disabled: enabled_fields.size === 0, className: "rounded-full bg-violet-600 hover:bg-violet-700 text-white disabled:opacity-50 disabled:cursor-not-allowed", children: ["Apply to ", selected_count, " items"] })] })] }) }));
83
+ }
@@ -4,6 +4,6 @@ interface ColorSwatchPickerProps {
4
4
  on_change: (color: string) => void;
5
5
  className?: string;
6
6
  }
7
- export declare function ColorSwatchPicker({ value, options, on_change, className, }: ColorSwatchPickerProps): import("react/jsx-runtime").JSX.Element;
7
+ export declare function ColorSwatchPicker({ value, options, on_change, className, }: ColorSwatchPickerProps): import("react").JSX.Element;
8
8
  export {};
9
9
  //# sourceMappingURL=color_swatch_picker.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"color_swatch_picker.d.ts","sourceRoot":"","sources":["../../../../src/components/app_config_list_editor/components/color_swatch_picker.tsx"],"names":[],"mappings":"AAMA,UAAU,sBAAsB;IAC9B,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB,SAAS,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IAClC,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,wBAAgB,iBAAiB,CAAC,EAChC,KAAK,EACL,OAAO,EACP,SAAS,EACT,SAAS,GACV,EAAE,sBAAsB,2CA+BxB"}
1
+ {"version":3,"file":"color_swatch_picker.d.ts","sourceRoot":"","sources":["../../../../src/components/app_config_list_editor/components/color_swatch_picker.tsx"],"names":[],"mappings":"AAMA,UAAU,sBAAsB;IAC9B,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB,SAAS,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IAClC,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,wBAAgB,iBAAiB,CAAC,EAChC,KAAK,EACL,OAAO,EACP,SAAS,EACT,SAAS,GACV,EAAE,sBAAsB,+BA+BxB"}
@@ -5,6 +5,6 @@ interface DeleteDialogProps {
5
5
  on_confirm: () => void;
6
6
  on_cancel: () => void;
7
7
  }
8
- export declare function DeleteDialog({ open, item_name, message, on_confirm, on_cancel, }: DeleteDialogProps): import("react/jsx-runtime").JSX.Element;
8
+ export declare function DeleteDialog({ open, item_name, message, on_confirm, on_cancel, }: DeleteDialogProps): import("react").JSX.Element;
9
9
  export {};
10
10
  //# sourceMappingURL=delete_dialog.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"delete_dialog.d.ts","sourceRoot":"","sources":["../../../../src/components/app_config_list_editor/components/delete_dialog.tsx"],"names":[],"mappings":"AAeA,UAAU,iBAAiB;IACzB,IAAI,EAAE,OAAO,CAAA;IACb,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,MAAM,CAAA;IACf,UAAU,EAAE,MAAM,IAAI,CAAA;IACtB,SAAS,EAAE,MAAM,IAAI,CAAA;CACtB;AAED,wBAAgB,YAAY,CAAC,EAC3B,IAAI,EACJ,SAAS,EACT,OAAO,EACP,UAAU,EACV,SAAS,GACV,EAAE,iBAAiB,2CAgCnB"}
1
+ {"version":3,"file":"delete_dialog.d.ts","sourceRoot":"","sources":["../../../../src/components/app_config_list_editor/components/delete_dialog.tsx"],"names":[],"mappings":"AAeA,UAAU,iBAAiB;IACzB,IAAI,EAAE,OAAO,CAAA;IACb,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,MAAM,CAAA;IACf,UAAU,EAAE,MAAM,IAAI,CAAA;IACtB,SAAS,EAAE,MAAM,IAAI,CAAA;CACtB;AAED,wBAAgB,YAAY,CAAC,EAC3B,IAAI,EACJ,SAAS,EACT,OAAO,EACP,UAAU,EACV,SAAS,GACV,EAAE,iBAAiB,+BAgCnB"}
@@ -15,7 +15,9 @@ interface EditModalProps<T extends Record<string, unknown>> {
15
15
  on_cancel: () => void;
16
16
  /** Tailwind max-width class(es). Default: `sm:max-w-2xl`. */
17
17
  max_width_class?: string;
18
+ /** When true, disables the Save button (async adapter in progress) */
19
+ is_saving?: boolean;
18
20
  }
19
- export declare function EditModal<T extends Record<string, unknown>>({ open, mode, item: initial_item, columns, id_field, auto_id_from, id_editable_after_create, existing_ids, item_type_label, render_preview, on_save, on_cancel, max_width_class, }: EditModalProps<T>): import("react/jsx-runtime").JSX.Element;
21
+ export declare function EditModal<T extends Record<string, unknown>>({ open, mode, item: initial_item, columns, id_field, auto_id_from, id_editable_after_create, existing_ids, item_type_label, render_preview, on_save, on_cancel, max_width_class, is_saving, }: EditModalProps<T>): React.JSX.Element;
20
22
  export {};
21
23
  //# sourceMappingURL=edit_modal.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"edit_modal.d.ts","sourceRoot":"","sources":["../../../../src/components/app_config_list_editor/components/edit_modal.tsx"],"names":[],"mappings":"AAGA,OAAO,KAA2C,MAAM,OAAO,CAAA;AAY/D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAG5C,UAAU,cAAc,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IACxD,IAAI,EAAE,OAAO,CAAA;IACb,IAAI,EAAE,QAAQ,GAAG,MAAM,CAAA;IACvB,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,CAAA;IAChB,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC,EAAE,CAAA;IACvB,QAAQ,EAAE,MAAM,CAAC,GAAG,MAAM,CAAA;IAC1B,YAAY,CAAC,EAAE,MAAM,CAAC,GAAG,MAAM,CAAA;IAC/B,wBAAwB,CAAC,EAAE,OAAO,CAAA;IAClC,YAAY,EAAE,MAAM,EAAE,CAAA;IACtB,eAAe,EAAE,MAAM,CAAA;IACvB,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,KAAK,CAAC,SAAS,CAAA;IAC7C,OAAO,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,IAAI,CAAA;IAC1B,SAAS,EAAE,MAAM,IAAI,CAAA;IACrB,6DAA6D;IAC7D,eAAe,CAAC,EAAE,MAAM,CAAA;CACzB;AAED,wBAAgB,SAAS,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EAC3D,IAAI,EACJ,IAAI,EACJ,IAAI,EAAE,YAAY,EAClB,OAAO,EACP,QAAQ,EACR,YAAY,EACZ,wBAAwB,EACxB,YAAY,EACZ,eAAe,EACf,cAAc,EACd,OAAO,EACP,SAAS,EACT,eAAgC,GACjC,EAAE,cAAc,CAAC,CAAC,CAAC,2CAqRnB"}
1
+ {"version":3,"file":"edit_modal.d.ts","sourceRoot":"","sources":["../../../../src/components/app_config_list_editor/components/edit_modal.tsx"],"names":[],"mappings":"AAKA,OAAO,KAA2C,MAAM,OAAO,CAAA;AAY/D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAG5C,UAAU,cAAc,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IACxD,IAAI,EAAE,OAAO,CAAA;IACb,IAAI,EAAE,QAAQ,GAAG,MAAM,CAAA;IACvB,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,CAAA;IAChB,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC,EAAE,CAAA;IACvB,QAAQ,EAAE,MAAM,CAAC,GAAG,MAAM,CAAA;IAC1B,YAAY,CAAC,EAAE,MAAM,CAAC,GAAG,MAAM,CAAA;IAC/B,wBAAwB,CAAC,EAAE,OAAO,CAAA;IAClC,YAAY,EAAE,MAAM,EAAE,CAAA;IACtB,eAAe,EAAE,MAAM,CAAA;IACvB,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,KAAK,CAAC,SAAS,CAAA;IAC7C,OAAO,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,IAAI,CAAA;IAC1B,SAAS,EAAE,MAAM,IAAI,CAAA;IACrB,6DAA6D;IAC7D,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,sEAAsE;IACtE,SAAS,CAAC,EAAE,OAAO,CAAA;CACpB;AAED,wBAAgB,SAAS,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EAC3D,IAAI,EACJ,IAAI,EACJ,IAAI,EAAE,YAAY,EAClB,OAAO,EACP,QAAQ,EACR,YAAY,EACZ,wBAAwB,EACxB,YAAY,EACZ,eAAe,EACf,cAAc,EACd,OAAO,EACP,SAAS,EACT,eAAgC,EAChC,SAAiB,GAClB,EAAE,cAAc,CAAC,CAAC,CAAC,qBAoTnB"}
@@ -1,3 +1,4 @@
1
+ 'use client';
1
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
3
  // Edit modal component
3
4
  // Centered dialog for creating/editing items with dynamic form fields
@@ -8,7 +9,7 @@ import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, } from
8
9
  import { Button } from '../../ui/button.js';
9
10
  import { ColorSwatchPicker } from './color_swatch_picker.js';
10
11
  import { slugify } from '../types.js';
11
- export function EditModal({ open, mode, item: initial_item, columns, id_field, auto_id_from, id_editable_after_create, existing_ids, item_type_label, render_preview, on_save, on_cancel, max_width_class = 'sm:max-w-2xl', }) {
12
+ export function EditModal({ open, mode, item: initial_item, columns, id_field, auto_id_from, id_editable_after_create, existing_ids, item_type_label, render_preview, on_save, on_cancel, max_width_class = 'sm:max-w-2xl', is_saving = false, }) {
12
13
  const [form_data, set_form_data] = useState({});
13
14
  const [errors, set_errors] = useState(new Map());
14
15
  const [user_touched_id, set_user_touched_id] = useState(false);
@@ -88,6 +89,19 @@ export function EditModal({ open, mode, item: initial_item, columns, id_field, a
88
89
  const error = errors.get(col.field);
89
90
  const is_id_field = col.field === id_field;
90
91
  const is_disabled = is_id_field && mode === 'edit' && !id_editable_after_create;
92
+ // Readonly fields — show a non-editable display
93
+ if (col.readonly) {
94
+ const display_value = col.format
95
+ ? col.format(value, form_data)
96
+ : String(value ?? '');
97
+ return (_jsxs("div", { className: "cls_edit_field space-y-1.5", children: [_jsx("label", { className: "text-sm font-medium text-gray-700", children: col.label }), _jsx("div", { className: "flex h-10 w-full items-center rounded-lg border border-input bg-gray-50 px-3 py-2 text-sm text-gray-500", children: display_value })] }, col.field));
98
+ }
99
+ // Custom render_edit or type === 'custom'
100
+ if (col.render_edit || col.type === 'custom') {
101
+ if (!col.render_edit)
102
+ return null;
103
+ return (_jsxs("div", { className: "cls_edit_field space-y-1.5", children: [_jsxs("label", { className: "text-sm font-medium text-gray-700", children: [col.label, col.required && _jsx("span", { className: "text-red-500 ml-0.5", children: "*" })] }), col.render_edit(value, form_data, (v) => update_field(col.field, v), { mode }), error && _jsx("p", { className: "text-xs text-red-600", children: error })] }, col.field));
104
+ }
91
105
  // Tag picker fields — render with dedicated component
92
106
  if (col.type === 'tag_picker') {
93
107
  return (_jsxs("div", { className: "cls_edit_field space-y-1.5", children: [_jsxs("label", { className: "text-sm font-medium text-gray-700", children: [col.label, col.required && _jsx("span", { className: "text-red-500 ml-0.5", children: "*" })] }), _jsx(TagPickerField, { value: Array.isArray(value) ? value : (typeof value === 'string' && value ? value.split(',').map(s => s.trim()).filter(Boolean) : []), options: col.tag_options ?? [], on_change: (tags) => update_field(col.field, tags) }), error && _jsx("p", { className: "text-xs text-red-600", children: error })] }, col.field));
@@ -101,7 +115,7 @@ export function EditModal({ open, mode, item: initial_item, columns, id_field, a
101
115
  : update_field(col.field, e.target.value), placeholder: col.placeholder, disabled: is_disabled, className: cn('rounded-lg focus-visible:ring-violet-500/20 focus-visible:ring-offset-0', is_disabled && 'bg-gray-50 text-gray-500', error && 'border-red-400 focus-visible:ring-red-500/20') })), col.type === 'number' && (_jsx(Input, { type: "number", value: value !== undefined && value !== null ? String(value) : '', onChange: (e) => update_field(col.field, e.target.value === '' ? '' : Number(e.target.value)), placeholder: col.placeholder, className: cn('rounded-lg focus-visible:ring-violet-500/20 focus-visible:ring-offset-0', error && 'border-red-400 focus-visible:ring-red-500/20') })), col.type === 'textarea' && (_jsx("textarea", { value: String(value ?? ''), onChange: (e) => update_field(col.field, e.target.value), placeholder: col.placeholder, rows: 3, className: cn('flex w-full rounded-lg border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-violet-500/20 focus-visible:border-violet-300 disabled:cursor-not-allowed disabled:opacity-50', error && 'border-red-400 focus-visible:ring-red-500/20') })), col.type === 'select' && col.options && (_jsxs("select", { value: String(value ?? ''), onChange: (e) => update_field(col.field, e.target.value), className: cn('flex h-10 w-full rounded-lg border border-input bg-background px-3 py-2 text-sm ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-violet-500/20 focus-visible:border-violet-300', error && 'border-red-400 focus-visible:ring-red-500/20'), children: [_jsx("option", { value: "", children: col.placeholder || 'Select...' }), col.options.map((opt) => (_jsx("option", { value: opt.value, children: opt.label }, opt.value)))] })), col.type === 'color_swatch' && col.color_options && (_jsx(ColorSwatchPicker, { value: String(value ?? ''), options: col.color_options, on_change: (color) => update_field(col.field, color) })), is_id_field && auto_id_from && mode === 'create' && !user_touched_id && (_jsxs("p", { className: "text-xs text-gray-400", children: ["Auto-generated from ", columns.find(c => c.field === auto_id_from)?.label?.toLowerCase() || auto_id_from] })), error && (_jsx("p", { className: "text-xs text-red-600", children: error }))] }, col.field));
102
116
  };
103
117
  return (_jsx(Dialog, { open: open, onOpenChange: (is_open) => { if (!is_open)
104
- on_cancel(); }, children: _jsxs(DialogContent, { className: cn('cls_edit_modal w-[95vw] rounded-2xl p-0 gap-0 overflow-hidden max-h-[90vh] flex flex-col', max_width_class), children: [_jsx(DialogHeader, { className: "px-6 pt-6 pb-4 shrink-0", children: _jsx(DialogTitle, { children: mode === 'create' ? `New ${item_type_label}` : `Edit ${item_type_label}` }) }), _jsxs("div", { className: "px-6 pb-4 space-y-4 overflow-y-auto flex-1 min-h-0", children: [render_preview && Object.keys(form_data).length > 0 && (_jsx("div", { className: "p-3 bg-gray-50 rounded-lg border border-gray-100", children: render_preview(form_data) })), columns.map((col) => render_field(col))] }), _jsxs(DialogFooter, { className: "bg-gray-50 px-6 py-4 border-t border-gray-100 sm:justify-between shrink-0", children: [_jsx(Button, { type: "button", variant: "ghost", onClick: on_cancel, className: "rounded-full", children: "Cancel" }), _jsx(Button, { type: "button", onClick: handle_save, className: "rounded-full bg-violet-600 hover:bg-violet-700 text-white", children: mode === 'create' ? `Add ${item_type_label}` : 'Save Changes' })] })] }) }));
118
+ on_cancel(); }, children: _jsxs(DialogContent, { className: cn('cls_edit_modal w-[95vw] rounded-2xl p-0 gap-0 overflow-hidden max-h-[90vh] flex flex-col', max_width_class), children: [_jsx(DialogHeader, { className: "px-6 pt-6 pb-4 shrink-0", children: _jsx(DialogTitle, { children: mode === 'create' ? `New ${item_type_label}` : `Edit ${item_type_label}` }) }), _jsxs("div", { className: "px-6 pb-4 space-y-4 overflow-y-auto flex-1 min-h-0", children: [render_preview && Object.keys(form_data).length > 0 && (_jsx("div", { className: "p-3 bg-gray-50 rounded-lg border border-gray-100", children: render_preview(form_data) })), columns.map((col) => render_field(col))] }), _jsxs(DialogFooter, { className: "bg-gray-50 px-6 py-4 border-t border-gray-100 sm:justify-between shrink-0", children: [_jsx(Button, { type: "button", variant: "ghost", onClick: on_cancel, className: "rounded-full", children: "Cancel" }), _jsx(Button, { type: "button", onClick: handle_save, disabled: is_saving, className: "rounded-full bg-violet-600 hover:bg-violet-700 text-white disabled:opacity-50 disabled:cursor-not-allowed", children: is_saving ? 'Saving…' : mode === 'create' ? `Add ${item_type_label}` : 'Save Changes' })] })] }) }));
105
119
  }
106
120
  /* ── Tag Picker Field ── */
107
121
  function TagPickerField({ value, options, on_change }) {
@@ -3,6 +3,6 @@ interface EmptyStateProps {
3
3
  on_add: () => void;
4
4
  className?: string;
5
5
  }
6
- export declare function EmptyState({ item_label, on_add, className, }: EmptyStateProps): import("react/jsx-runtime").JSX.Element;
6
+ export declare function EmptyState({ item_label, on_add, className, }: EmptyStateProps): import("react").JSX.Element;
7
7
  export {};
8
8
  //# sourceMappingURL=empty_state.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"empty_state.d.ts","sourceRoot":"","sources":["../../../../src/components/app_config_list_editor/components/empty_state.tsx"],"names":[],"mappings":"AAMA,UAAU,eAAe;IACvB,UAAU,EAAE,MAAM,CAAA;IAClB,MAAM,EAAE,MAAM,IAAI,CAAA;IAClB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,wBAAgB,UAAU,CAAC,EACzB,UAAU,EACV,MAAM,EACN,SAAS,GACV,EAAE,eAAe,2CAsBjB"}
1
+ {"version":3,"file":"empty_state.d.ts","sourceRoot":"","sources":["../../../../src/components/app_config_list_editor/components/empty_state.tsx"],"names":[],"mappings":"AAMA,UAAU,eAAe;IACvB,UAAU,EAAE,MAAM,CAAA;IAClB,MAAM,EAAE,MAAM,IAAI,CAAA;IAClB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,wBAAgB,UAAU,CAAC,EACzB,UAAU,EACV,MAAM,EACN,SAAS,GACV,EAAE,eAAe,+BAsBjB"}
@@ -7,6 +7,6 @@ interface ImportExportButtonsProps<T extends Record<string, unknown>> {
7
7
  title?: string;
8
8
  max_items?: number;
9
9
  }
10
- export declare function ImportExportButtons<T extends Record<string, unknown>>({ items, on_items_change, columns, id_field, title, max_items, }: ImportExportButtonsProps<T>): import("react/jsx-runtime").JSX.Element;
10
+ export declare function ImportExportButtons<T extends Record<string, unknown>>({ items, on_items_change, columns, id_field, title, max_items, }: ImportExportButtonsProps<T>): import("react").JSX.Element;
11
11
  export {};
12
12
  //# sourceMappingURL=import_export_buttons.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"import_export_buttons.d.ts","sourceRoot":"","sources":["../../../../src/components/app_config_list_editor/components/import_export_buttons.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAQ5C,UAAU,wBAAwB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAClE,KAAK,EAAE,CAAC,EAAE,CAAA;IACV,eAAe,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,KAAK,IAAI,CAAA;IACrC,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC,EAAE,CAAA;IACvB,QAAQ,EAAE,MAAM,CAAC,GAAG,MAAM,CAAA;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,wBAAgB,mBAAmB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EACrE,KAAK,EACL,eAAe,EACf,OAAO,EACP,QAAQ,EACR,KAAK,EACL,SAAS,GACV,EAAE,wBAAwB,CAAC,CAAC,CAAC,2CA0H7B"}
1
+ {"version":3,"file":"import_export_buttons.d.ts","sourceRoot":"","sources":["../../../../src/components/app_config_list_editor/components/import_export_buttons.tsx"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAQ5C,UAAU,wBAAwB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAClE,KAAK,EAAE,CAAC,EAAE,CAAA;IACV,eAAe,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,KAAK,IAAI,CAAA;IACrC,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC,EAAE,CAAA;IACvB,QAAQ,EAAE,MAAM,CAAC,GAAG,MAAM,CAAA;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,wBAAgB,mBAAmB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EACrE,KAAK,EACL,eAAe,EACf,OAAO,EACP,QAAQ,EACR,KAAK,EACL,SAAS,GACV,EAAE,wBAAwB,CAAC,CAAC,CAAC,+BA0H7B"}
@@ -1,3 +1,4 @@
1
+ 'use client';
1
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
3
  // Import/Export buttons for AppConfigListEditor
3
4
  // Provides JSON export (download) and import (file picker) with transient status messages
@@ -8,7 +8,10 @@ interface ListItemRowProps<T extends Record<string, unknown>> {
8
8
  render_item_indicator?: (item: T) => React.ReactNode;
9
9
  on_edit: () => void;
10
10
  on_delete: () => void;
11
+ show_checkbox?: boolean;
12
+ is_selected?: boolean;
13
+ on_toggle_select?: () => void;
11
14
  }
12
- export declare function ListItemRow<T extends Record<string, unknown>>({ item, index, columns, render_item, render_item_indicator, on_edit, on_delete, }: ListItemRowProps<T>): import("react/jsx-runtime").JSX.Element;
15
+ export declare function ListItemRow<T extends Record<string, unknown>>({ item, index, columns, render_item, render_item_indicator, on_edit, on_delete, show_checkbox, is_selected, on_toggle_select, }: ListItemRowProps<T>): React.JSX.Element;
13
16
  export {};
14
17
  //# sourceMappingURL=list_item_row.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"list_item_row.d.ts","sourceRoot":"","sources":["../../../../src/components/app_config_list_editor/components/list_item_row.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,MAAM,OAAO,CAAA;AAGzB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAE5C,UAAU,gBAAgB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAC1D,IAAI,EAAE,CAAC,CAAA;IACP,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC,EAAE,CAAA;IACvB,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,KAAK,CAAC,SAAS,CAAA;IACzD,qBAAqB,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,KAAK,CAAC,SAAS,CAAA;IACpD,OAAO,EAAE,MAAM,IAAI,CAAA;IACnB,SAAS,EAAE,MAAM,IAAI,CAAA;CACtB;AAED,wBAAgB,WAAW,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EAC7D,IAAI,EACJ,KAAK,EACL,OAAO,EACP,WAAW,EACX,qBAAqB,EACrB,OAAO,EACP,SAAS,GACV,EAAE,gBAAgB,CAAC,CAAC,CAAC,2CAgGrB"}
1
+ {"version":3,"file":"list_item_row.d.ts","sourceRoot":"","sources":["../../../../src/components/app_config_list_editor/components/list_item_row.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,MAAM,OAAO,CAAA;AAGzB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAE5C,UAAU,gBAAgB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAC1D,IAAI,EAAE,CAAC,CAAA;IACP,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC,EAAE,CAAA;IACvB,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,KAAK,CAAC,SAAS,CAAA;IACzD,qBAAqB,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,KAAK,CAAC,SAAS,CAAA;IACpD,OAAO,EAAE,MAAM,IAAI,CAAA;IACnB,SAAS,EAAE,MAAM,IAAI,CAAA;IACrB,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,gBAAgB,CAAC,EAAE,MAAM,IAAI,CAAA;CAC9B;AAED,wBAAgB,WAAW,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EAC7D,IAAI,EACJ,KAAK,EACL,OAAO,EACP,WAAW,EACX,qBAAqB,EACrB,OAAO,EACP,SAAS,EACT,aAAa,EACb,WAAW,EACX,gBAAgB,GACjB,EAAE,gBAAgB,CAAC,CAAC,CAAC,qBAoHrB"}
@@ -1,14 +1,14 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Pencil, Trash2 } from 'lucide-react';
3
3
  import { cn } from 'hazo_ui';
4
- export function ListItemRow({ item, index, columns, render_item, render_item_indicator, on_edit, on_delete, }) {
4
+ export function ListItemRow({ item, index, columns, render_item, render_item_indicator, on_edit, on_delete, show_checkbox, is_selected, on_toggle_select, }) {
5
5
  // Custom render overrides everything
6
6
  if (render_item) {
7
- return (_jsxs("div", { className: "cls_list_editor_row flex items-center gap-3 px-4 py-3 hover:bg-gray-50 transition-colors", children: [_jsx("div", { className: "flex-1 min-w-0", children: render_item(item, index) }), _jsxs("div", { className: "flex items-center gap-1 shrink-0", children: [_jsx("button", { type: "button", onClick: on_edit, className: "w-8 h-8 rounded-full flex items-center justify-center text-gray-400 hover:text-gray-700 hover:bg-gray-100 transition-colors", "aria-label": "Edit item", children: _jsx(Pencil, { className: "w-4 h-4" }) }), _jsx("button", { type: "button", onClick: on_delete, className: "w-8 h-8 rounded-full flex items-center justify-center text-gray-400 hover:text-red-600 hover:bg-red-50 transition-colors", "aria-label": "Delete item", children: _jsx(Trash2, { className: "w-4 h-4" }) })] })] }));
7
+ return (_jsxs("div", { className: "cls_list_editor_row flex items-center gap-3 px-4 py-3 hover:bg-gray-50 transition-colors", children: [show_checkbox && (_jsx("input", { type: "checkbox", checked: is_selected ?? false, onChange: on_toggle_select, className: "shrink-0 w-4 h-4 rounded border-gray-300 text-violet-600 focus:ring-violet-500", "aria-label": "Select item" })), _jsx("div", { className: "flex-1 min-w-0", children: render_item(item, index) }), _jsxs("div", { className: "flex items-center gap-1 shrink-0", children: [_jsx("button", { type: "button", onClick: on_edit, className: "w-8 h-8 rounded-full flex items-center justify-center text-gray-400 hover:text-gray-700 hover:bg-gray-100 transition-colors", "aria-label": "Edit item", children: _jsx(Pencil, { className: "w-4 h-4" }) }), _jsx("button", { type: "button", onClick: on_delete, className: "w-8 h-8 rounded-full flex items-center justify-center text-gray-400 hover:text-red-600 hover:bg-red-50 transition-colors", "aria-label": "Delete item", children: _jsx(Trash2, { className: "w-4 h-4" }) })] })] }));
8
8
  }
9
9
  // Default rendering based on column definitions
10
10
  const primary_cols = columns.filter(c => c.list_display === 'primary');
11
11
  const secondary_cols = columns.filter(c => c.list_display === 'secondary');
12
12
  const badge_cols = columns.filter(c => c.list_display === 'badge');
13
- return (_jsxs("div", { className: "cls_list_editor_row flex items-center gap-3 px-4 py-3 hover:bg-gray-50 transition-colors", children: [render_item_indicator && (_jsx("div", { className: "shrink-0", children: render_item_indicator(item) })), _jsxs("div", { className: "flex-1 min-w-0", children: [primary_cols.map((col) => (_jsx("div", { className: "text-sm font-medium text-gray-900 truncate", children: String(item[col.field] ?? '') }, col.field))), secondary_cols.map((col) => (_jsx("div", { className: "text-xs text-gray-500 truncate mt-0.5", children: String(item[col.field] ?? '') }, col.field)))] }), badge_cols.length > 0 && (_jsx("div", { className: "flex items-center gap-2 shrink-0", children: badge_cols.map((col) => (_jsx("span", { className: cn('inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-mono', 'bg-gray-100 text-gray-600'), children: String(item[col.field] ?? '') }, col.field))) })), _jsxs("div", { className: "flex items-center gap-1 shrink-0", children: [_jsx("button", { type: "button", onClick: on_edit, className: "w-8 h-8 rounded-full flex items-center justify-center text-gray-400 hover:text-gray-700 hover:bg-gray-100 transition-colors", "aria-label": "Edit item", children: _jsx(Pencil, { className: "w-4 h-4" }) }), _jsx("button", { type: "button", onClick: on_delete, className: "w-8 h-8 rounded-full flex items-center justify-center text-gray-400 hover:text-red-600 hover:bg-red-50 transition-colors", "aria-label": "Delete item", children: _jsx(Trash2, { className: "w-4 h-4" }) })] })] }));
13
+ return (_jsxs("div", { className: "cls_list_editor_row flex items-center gap-3 px-4 py-3 hover:bg-gray-50 transition-colors", children: [show_checkbox && (_jsx("input", { type: "checkbox", checked: is_selected ?? false, onChange: on_toggle_select, className: "shrink-0 w-4 h-4 rounded border-gray-300 text-violet-600 focus:ring-violet-500", "aria-label": "Select item" })), render_item_indicator && (_jsx("div", { className: "shrink-0", children: render_item_indicator(item) })), _jsxs("div", { className: "flex-1 min-w-0", children: [primary_cols.map((col) => (_jsx("div", { className: "text-sm font-medium text-gray-900 truncate", children: col.format?.(item[col.field], item) ?? String(item[col.field] ?? '') }, col.field))), secondary_cols.map((col) => (_jsx("div", { className: "text-xs text-gray-500 truncate mt-0.5", children: col.format?.(item[col.field], item) ?? String(item[col.field] ?? '') }, col.field)))] }), badge_cols.length > 0 && (_jsx("div", { className: "flex items-center gap-2 shrink-0", children: badge_cols.map((col) => (_jsx("span", { className: cn('inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-mono', 'bg-gray-100 text-gray-600'), children: col.format?.(item[col.field], item) ?? String(item[col.field] ?? '') }, col.field))) })), _jsxs("div", { className: "flex items-center gap-1 shrink-0", children: [_jsx("button", { type: "button", onClick: on_edit, className: "w-8 h-8 rounded-full flex items-center justify-center text-gray-400 hover:text-gray-700 hover:bg-gray-100 transition-colors", "aria-label": "Edit item", children: _jsx(Pencil, { className: "w-4 h-4" }) }), _jsx("button", { type: "button", onClick: on_delete, className: "w-8 h-8 rounded-full flex items-center justify-center text-gray-400 hover:text-red-600 hover:bg-red-50 transition-colors", "aria-label": "Delete item", children: _jsx(Trash2, { className: "w-4 h-4" }) })] })] }));
14
14
  }
@@ -2,6 +2,6 @@ interface SaveStatusIndicatorProps {
2
2
  status: 'idle' | 'saving' | 'saved' | 'error';
3
3
  className?: string;
4
4
  }
5
- export declare function SaveStatusIndicator({ status, className, }: SaveStatusIndicatorProps): import("react/jsx-runtime").JSX.Element | null;
5
+ export declare function SaveStatusIndicator({ status, className, }: SaveStatusIndicatorProps): import("react").JSX.Element | null;
6
6
  export {};
7
7
  //# sourceMappingURL=save_status_indicator.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"save_status_indicator.d.ts","sourceRoot":"","sources":["../../../../src/components/app_config_list_editor/components/save_status_indicator.tsx"],"names":[],"mappings":"AAOA,UAAU,wBAAwB;IAChC,MAAM,EAAE,MAAM,GAAG,QAAQ,GAAG,OAAO,GAAG,OAAO,CAAA;IAC7C,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,wBAAgB,mBAAmB,CAAC,EAClC,MAAM,EACN,SAAS,GACV,EAAE,wBAAwB,kDA+C1B"}
1
+ {"version":3,"file":"save_status_indicator.d.ts","sourceRoot":"","sources":["../../../../src/components/app_config_list_editor/components/save_status_indicator.tsx"],"names":[],"mappings":"AASA,UAAU,wBAAwB;IAChC,MAAM,EAAE,MAAM,GAAG,QAAQ,GAAG,OAAO,GAAG,OAAO,CAAA;IAC7C,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,wBAAgB,mBAAmB,CAAC,EAClC,MAAM,EACN,SAAS,GACV,EAAE,wBAAwB,sCA+C1B"}
@@ -1,3 +1,4 @@
1
+ 'use client';
1
2
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
3
  // Save status indicator component
3
4
  // Shows saving/saved/error states inline
@@ -5,6 +5,6 @@ interface SearchBarProps {
5
5
  item_label: string;
6
6
  className?: string;
7
7
  }
8
- export declare function SearchBar({ value, on_change, item_count, item_label, className, }: SearchBarProps): import("react/jsx-runtime").JSX.Element;
8
+ export declare function SearchBar({ value, on_change, item_count, item_label, className, }: SearchBarProps): import("react").JSX.Element;
9
9
  export {};
10
10
  //# sourceMappingURL=search_bar.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"search_bar.d.ts","sourceRoot":"","sources":["../../../../src/components/app_config_list_editor/components/search_bar.tsx"],"names":[],"mappings":"AAMA,UAAU,cAAc;IACtB,KAAK,EAAE,MAAM,CAAA;IACb,SAAS,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IAClC,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE,MAAM,CAAA;IAClB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,wBAAgB,SAAS,CAAC,EACxB,KAAK,EACL,SAAS,EACT,UAAU,EACV,UAAU,EACV,SAAS,GACV,EAAE,cAAc,2CA6BhB"}
1
+ {"version":3,"file":"search_bar.d.ts","sourceRoot":"","sources":["../../../../src/components/app_config_list_editor/components/search_bar.tsx"],"names":[],"mappings":"AAMA,UAAU,cAAc;IACtB,KAAK,EAAE,MAAM,CAAA;IACb,SAAS,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IAClC,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE,MAAM,CAAA;IAClB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,wBAAgB,SAAS,CAAC,EACxB,KAAK,EACL,SAAS,EACT,UAAU,EACV,UAAU,EACV,SAAS,GACV,EAAE,cAAc,+BA6BhB"}
@@ -1,4 +1,6 @@
1
1
  export { AppConfigListEditor } from './app_config_list_editor.js';
2
2
  export type { AppConfigListEditorProps, ColumnDef, ImportResult } from './types.js';
3
+ export { BulkEditModal } from './components/bulk_edit_modal.js';
4
+ export type { BulkEditModalProps } from './components/bulk_edit_modal.js';
3
5
  export { validate_import_data, merge_items, export_items_to_json, generate_export_filename, } from './utils/json_import_export.js';
4
6
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/app_config_list_editor/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAA;AACjE,YAAY,EAAE,wBAAwB,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AACnF,OAAO,EACL,oBAAoB,EACpB,WAAW,EACX,oBAAoB,EACpB,wBAAwB,GACzB,MAAM,+BAA+B,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/app_config_list_editor/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAA;AACjE,YAAY,EAAE,wBAAwB,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AACnF,OAAO,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAA;AAC/D,YAAY,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAA;AACzE,OAAO,EACL,oBAAoB,EACpB,WAAW,EACX,oBAAoB,EACpB,wBAAwB,GACzB,MAAM,+BAA+B,CAAA"}
@@ -1,3 +1,4 @@
1
1
  // Public exports for AppConfigListEditor
2
2
  export { AppConfigListEditor } from './app_config_list_editor.js';
3
+ export { BulkEditModal } from './components/bulk_edit_modal.js';
3
4
  export { validate_import_data, merge_items, export_items_to_json, generate_export_filename, } from './utils/json_import_export.js';
@@ -8,7 +8,7 @@ export interface ColumnDef<T> {
8
8
  /** Display label for the form */
9
9
  label: string;
10
10
  /** Column type determines the input control */
11
- type: 'text' | 'textarea' | 'color_swatch' | 'select' | 'number' | 'toggle' | 'tag_picker';
11
+ type: 'text' | 'textarea' | 'color_swatch' | 'select' | 'number' | 'toggle' | 'tag_picker' | 'custom';
12
12
  /** Placeholder text */
13
13
  placeholder?: string;
14
14
  /** Whether this field is required */
@@ -31,6 +31,14 @@ export interface ColumnDef<T> {
31
31
  color_options?: string[];
32
32
  /** Validation function - returns error message or null */
33
33
  validate?: (value: unknown, item: T) => string | null;
34
+ /** Custom edit control: rendered instead of built-in input when present */
35
+ render_edit?: (value: unknown, item: Partial<T>, set_value: (v: unknown) => void, ctx: {
36
+ mode: 'create' | 'edit';
37
+ }) => React.ReactNode;
38
+ /** Display formatter for list rendering (badge/secondary/primary cells) */
39
+ format?: (value: unknown, item: T) => string;
40
+ /** If true, field is shown but not editable in the edit form */
41
+ readonly?: boolean;
34
42
  }
35
43
  /**
36
44
  * Props for the AppConfigListEditor component
@@ -77,6 +85,22 @@ export interface AppConfigListEditorProps<T extends Record<string, unknown>> {
77
85
  * default `sm:max-w-2xl`. Use e.g. `sm:max-w-4xl` or `sm:max-w-[900px]`.
78
86
  */
79
87
  edit_modal_max_width?: string;
88
+ /** Enable row checkboxes and bulk action bar */
89
+ enable_selection?: boolean;
90
+ /** Column fields to expose in the bulk edit modal */
91
+ bulk_edit_fields?: (keyof T & string)[];
92
+ /** Called with selected IDs and patch when bulk edit is confirmed */
93
+ on_bulk_update?: (ids: string[], patch: Partial<T>) => void | Promise<void>;
94
+ /** Called with selected IDs when bulk delete is confirmed */
95
+ on_bulk_delete?: (ids: string[]) => void | Promise<void>;
96
+ /** Called instead of on_items_change when creating a new item */
97
+ on_item_create?: (item: T) => Promise<void>;
98
+ /** Called instead of on_items_change when updating an item */
99
+ on_item_update?: (id: string, item: T) => Promise<void>;
100
+ /** Called instead of on_items_change when deleting an item */
101
+ on_item_delete?: (id: string) => Promise<void>;
102
+ /** Called after async create/update/delete to trigger a data refresh */
103
+ on_reload?: () => void | Promise<void>;
80
104
  }
81
105
  /**
82
106
  * Internal state for the edit modal
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/components/app_config_list_editor/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/components/app_config_list_editor/types.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"config_editor.d.ts","sourceRoot":"","sources":["../../src/components/config_editor.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAyC,MAAM,OAAO,CAAA;AAE7D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAIrD;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC;;OAEG;IACH,eAAe,EAAE,cAAc,CAAA;IAC/B;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;IACtB;;OAEG;IACH,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAA;IACjC;;;OAGG;IACH,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAA;IAC3B;;;OAGG;IACH,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAA;IAC7B;;;OAGG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAA;CAC5B;AAWD;;;;;;GAMG;AACH,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,CAgYpD,CAAA"}
1
+ {"version":3,"file":"config_editor.d.ts","sourceRoot":"","sources":["../../src/components/config_editor.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAyC,MAAM,OAAO,CAAA;AAE7D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAIrD;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC;;OAEG;IACH,eAAe,EAAE,cAAc,CAAA;IAC/B;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;IACtB;;OAEG;IACH,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAA;IACjC;;;OAGG;IACH,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAA;IAC3B;;;OAGG;IACH,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAA;IAC7B;;;OAGG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAA;CAC5B;AAWD;;;;;;GAMG;AACH,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,CAgYpD,CAAA"}
@@ -1,3 +1,4 @@
1
+ 'use client';
1
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
3
  // Config editor component for managing configuration
3
4
  // Provides a more advanced interface for editing configuration values with sensitive field masking
@@ -1 +1 @@
1
- {"version":3,"file":"config_viewer.d.ts","sourceRoot":"","sources":["../../src/components/config_viewer.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAyC,MAAM,OAAO,CAAA;AAE7D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAIrD;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC;;OAEG;IACH,eAAe,EAAE,cAAc,CAAA;IAC/B;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;IACtB;;OAEG;IACH,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAA;IACjC;;;OAGG;IACH,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAA;IAC3B;;;OAGG;IACH,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAA;IAC7B;;;OAGG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAA;CAC5B;AAED;;;;;;GAMG;AACH,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,CA2MpD,CAAA"}
1
+ {"version":3,"file":"config_viewer.d.ts","sourceRoot":"","sources":["../../src/components/config_viewer.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAyC,MAAM,OAAO,CAAA;AAE7D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAIrD;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC;;OAEG;IACH,eAAe,EAAE,cAAc,CAAA;IAC/B;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;IACtB;;OAEG;IACH,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAA;IACjC;;;OAGG;IACH,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAA;IAC3B;;;OAGG;IACH,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAA;IAC7B;;;OAGG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAA;CAC5B;AAED;;;;;;GAMG;AACH,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,CA2MpD,CAAA"}
@@ -1,3 +1,4 @@
1
+ 'use client';
1
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
3
  // Config viewer component for displaying configuration data
3
4
  // Displays configuration sections and values in a readable format with sensitive field masking
@@ -10,5 +10,7 @@ export { useAppConfig } from './use_app_config.js';
10
10
  export type { UseAppConfigResult } from '../lib/app_config_types.js';
11
11
  export { AppConfigListEditor } from './app_config_list_editor/index.js';
12
12
  export type { AppConfigListEditorProps, ColumnDef, ImportResult } from './app_config_list_editor/index.js';
13
+ export { BulkEditModal } from './app_config_list_editor/index.js';
14
+ export type { BulkEditModalProps } from './app_config_list_editor/index.js';
13
15
  export { validate_import_data, merge_items, export_items_to_json, generate_export_filename, } from './app_config_list_editor/index.js';
14
16
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/components/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACjD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAC3C,YAAY,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AAC3D,YAAY,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AAC3D,YAAY,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAA;AAGhE,OAAO,EACL,iBAAiB,EACjB,kBAAkB,EAClB,UAAU,EACV,0BAA0B,EAC3B,MAAM,0BAA0B,CAAA;AACjC,YAAY,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAA;AAEvE,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAClD,YAAY,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAA;AAGpE,OAAO,EAAE,mBAAmB,EAAE,MAAM,mCAAmC,CAAA;AACvE,YAAY,EAAE,wBAAwB,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,mCAAmC,CAAA;AAC1G,OAAO,EACL,oBAAoB,EACpB,WAAW,EACX,oBAAoB,EACpB,wBAAwB,GACzB,MAAM,mCAAmC,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/components/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACjD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAC3C,YAAY,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AAC3D,YAAY,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AAC3D,YAAY,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAA;AAGhE,OAAO,EACL,iBAAiB,EACjB,kBAAkB,EAClB,UAAU,EACV,0BAA0B,EAC3B,MAAM,0BAA0B,CAAA;AACjC,YAAY,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAA;AAEvE,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAClD,YAAY,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAA;AAGpE,OAAO,EAAE,mBAAmB,EAAE,MAAM,mCAAmC,CAAA;AACvE,YAAY,EAAE,wBAAwB,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,mCAAmC,CAAA;AAC1G,OAAO,EAAE,aAAa,EAAE,MAAM,mCAAmC,CAAA;AACjE,YAAY,EAAE,kBAAkB,EAAE,MAAM,mCAAmC,CAAA;AAC3E,OAAO,EACL,oBAAoB,EACpB,WAAW,EACX,oBAAoB,EACpB,wBAAwB,GACzB,MAAM,mCAAmC,CAAA"}
@@ -8,4 +8,5 @@ export { useConfigSections, is_sensitive_field, mask_value, DEFAULT_SENSITIVE_PA
8
8
  export { useAppConfig } from './use_app_config.js';
9
9
  // AppConfigListEditor exports
10
10
  export { AppConfigListEditor } from './app_config_list_editor/index.js';
11
+ export { BulkEditModal } from './app_config_list_editor/index.js';
11
12
  export { validate_import_data, merge_items, export_items_to_json, generate_export_filename, } from './app_config_list_editor/index.js';
@@ -6,11 +6,11 @@ declare const AlertDialogPortal: React.FC<AlertDialogPrimitive.AlertDialogPortal
6
6
  declare const AlertDialogOverlay: React.ForwardRefExoticComponent<Omit<AlertDialogPrimitive.AlertDialogOverlayProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
7
7
  declare const AlertDialogContent: React.ForwardRefExoticComponent<Omit<AlertDialogPrimitive.AlertDialogContentProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
8
8
  declare const AlertDialogHeader: {
9
- ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>): import("react/jsx-runtime").JSX.Element;
9
+ ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>): React.JSX.Element;
10
10
  displayName: string;
11
11
  };
12
12
  declare const AlertDialogFooter: {
13
- ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>): import("react/jsx-runtime").JSX.Element;
13
+ ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>): React.JSX.Element;
14
14
  displayName: string;
15
15
  };
16
16
  declare const AlertDialogTitle: React.ForwardRefExoticComponent<Omit<AlertDialogPrimitive.AlertDialogTitleProps & React.RefAttributes<HTMLHeadingElement>, "ref"> & React.RefAttributes<HTMLHeadingElement>>;
@@ -7,11 +7,11 @@ declare const DialogClose: React.ForwardRefExoticComponent<DialogPrimitive.Dialo
7
7
  declare const DialogOverlay: React.ForwardRefExoticComponent<Omit<DialogPrimitive.DialogOverlayProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
8
8
  declare const DialogContent: React.ForwardRefExoticComponent<Omit<DialogPrimitive.DialogContentProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
9
9
  declare const DialogHeader: {
10
- ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>): import("react/jsx-runtime").JSX.Element;
10
+ ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>): React.JSX.Element;
11
11
  displayName: string;
12
12
  };
13
13
  declare const DialogFooter: {
14
- ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>): import("react/jsx-runtime").JSX.Element;
14
+ ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>): React.JSX.Element;
15
15
  displayName: string;
16
16
  };
17
17
  declare const DialogTitle: React.ForwardRefExoticComponent<Omit<DialogPrimitive.DialogTitleProps & React.RefAttributes<HTMLHeadingElement>, "ref"> & React.RefAttributes<HTMLHeadingElement>>;
@@ -1 +1 @@
1
- {"version":3,"file":"use_app_config.d.ts","sourceRoot":"","sources":["../../src/components/use_app_config.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAEV,gBAAgB,EAChB,aAAa,EACb,kBAAkB,EACnB,MAAM,4BAA4B,CAAA;AAkBnC;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAC1B,OAAO,EAAE,gBAAgB,EACzB,YAAY,EAAE,CAAC,OAAO,EAAE,gBAAgB,KAAK,OAAO,CAAC,aAAa,EAAE,CAAC,EACrE,WAAW,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,aAAa,EAAE,IAAI,GAAG,YAAY,GAAG,YAAY,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,EAC7F,aAAa,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,gBAAgB,KAAK,OAAO,CAAC,IAAI,CAAC,GACzF,kBAAkB,CA2JpB"}
1
+ {"version":3,"file":"use_app_config.d.ts","sourceRoot":"","sources":["../../src/components/use_app_config.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAEV,gBAAgB,EAChB,aAAa,EACb,kBAAkB,EACnB,MAAM,4BAA4B,CAAA;AAkBnC;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAC1B,OAAO,EAAE,gBAAgB,EACzB,YAAY,EAAE,CAAC,OAAO,EAAE,gBAAgB,KAAK,OAAO,CAAC,aAAa,EAAE,CAAC,EACrE,WAAW,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,aAAa,EAAE,IAAI,GAAG,YAAY,GAAG,YAAY,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,EAC7F,aAAa,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,gBAAgB,KAAK,OAAO,CAAC,IAAI,CAAC,GACzF,kBAAkB,CA2JpB"}
@@ -1,3 +1,4 @@
1
+ 'use client';
1
2
  // Custom hook for loading and managing database-backed application configuration
2
3
  // Provides async operations for reading/writing config to hazo_app_config table
3
4
  import { useState, useEffect, useCallback } from 'react';
@@ -1 +1 @@
1
- {"version":3,"file":"use_config_sections.d.ts","sourceRoot":"","sources":["../../src/components/use_config_sections.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAErD;;;GAGG;AACH,eAAO,MAAM,0BAA0B,UAUtC,CAAA;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,MAAM,EAClB,gBAAgB,CAAC,EAAE,MAAM,EAAE,EAC3B,kBAAkB,GAAE,MAAM,EAA+B,GACxD,OAAO,CAUT;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAGhD;AAED;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC;;OAEG;IACH,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;IAChD;;OAEG;IACH,UAAU,EAAE,OAAO,CAAA;IACnB;;OAEG;IACH,KAAK,EAAE,KAAK,GAAG,IAAI,CAAA;IACnB;;OAEG;IACH,MAAM,EAAE,MAAM,IAAI,CAAA;CACnB;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,eAAe,EAAE,cAAc,GAAG,uBAAuB,CAgC1F"}
1
+ {"version":3,"file":"use_config_sections.d.ts","sourceRoot":"","sources":["../../src/components/use_config_sections.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAErD;;;GAGG;AACH,eAAO,MAAM,0BAA0B,UAUtC,CAAA;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,MAAM,EAClB,gBAAgB,CAAC,EAAE,MAAM,EAAE,EAC3B,kBAAkB,GAAE,MAAM,EAA+B,GACxD,OAAO,CAUT;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAGhD;AAED;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC;;OAEG;IACH,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;IAChD;;OAEG;IACH,UAAU,EAAE,OAAO,CAAA;IACnB;;OAEG;IACH,KAAK,EAAE,KAAK,GAAG,IAAI,CAAA;IACnB;;OAEG;IACH,MAAM,EAAE,MAAM,IAAI,CAAA;CACnB;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,eAAe,EAAE,cAAc,GAAG,uBAAuB,CAgC1F"}
@@ -1,3 +1,4 @@
1
+ 'use client';
1
2
  // Custom hook for loading and managing configuration sections
2
3
  // Provides shared logic for ConfigViewer and ConfigEditor components
3
4
  import { useState, useEffect, useCallback } from 'react';
@@ -92,5 +92,6 @@ export declare class HazoConfig implements ConfigProvider {
92
92
  * @returns The resolved file path
93
93
  */
94
94
  getFilePath(): string;
95
+ private _registerNestedSection;
95
96
  }
96
97
  //# sourceMappingURL=config_loader.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"config_loader.d.ts","sourceRoot":"","sources":["../../src/lib/config_loader.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAMH,OAAO,KAAK,EAAE,cAAc,EAAE,iBAAiB,EAAU,MAAM,YAAY,CAAA;AAgF3E;;;;;GAKG;AACH,qBAAa,UAAW,YAAW,cAAc;IAC/C,OAAO,CAAC,QAAQ,CAAQ;IACxB,OAAO,CAAC,MAAM,CAAQ;IACtB,OAAO,CAAC,MAAM,CAA6C;IAC3D,OAAO,CAAC,YAAY,CAAQ;IAC5B,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,kBAAkB,CAAY;IAEtC;;;;OAIG;gBACS,OAAO,EAAE,iBAAiB;IAoCtC;;;OAGG;IACH,OAAO,CAAC,kBAAkB;IAsB1B;;;;;OAKG;IACH,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAKrD;;;;OAIG;IACH,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS;IAK/D;;;OAGG;IACH,cAAc,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAUxD;;;;;OAKG;IACH,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAQtD;;;;OAIG;IACH,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI;IAa1C;;;OAGG;IACH,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAOpC;;;OAGG;IACH,IAAI,IAAI,IAAI;IAmBZ;;;OAGG;IACH,OAAO,IAAI,IAAI;IA0Cf;;;OAGG;IACH,WAAW,IAAI,MAAM;CAGtB"}
1
+ {"version":3,"file":"config_loader.d.ts","sourceRoot":"","sources":["../../src/lib/config_loader.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAKH,OAAO,KAAK,EAAE,cAAc,EAAE,iBAAiB,EAAU,MAAM,YAAY,CAAA;AAwF3E;;;;;GAKG;AACH,qBAAa,UAAW,YAAW,cAAc;IAC/C,OAAO,CAAC,QAAQ,CAAQ;IACxB,OAAO,CAAC,MAAM,CAAQ;IACtB,OAAO,CAAC,MAAM,CAA6C;IAC3D,OAAO,CAAC,YAAY,CAAQ;IAC5B,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,kBAAkB,CAAY;IAEtC;;;;OAIG;gBACS,OAAO,EAAE,iBAAiB;IAoCtC;;;OAGG;IACH,OAAO,CAAC,kBAAkB;IAsB1B;;;;;OAKG;IACH,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAKrD;;;;OAIG;IACH,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS;IAK/D;;;OAGG;IACH,cAAc,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAUxD;;;;;OAKG;IACH,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAQtD;;;;OAIG;IACH,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI;IAa1C;;;OAGG;IACH,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAOpC;;;OAGG;IACH,IAAI,IAAI,IAAI;IAmBZ;;;OAGG;IACH,OAAO,IAAI,IAAI;IAgDf;;;OAGG;IACH,WAAW,IAAI,MAAM;IAIrB,OAAO,CAAC,sBAAsB;CAU/B"}
@@ -18,21 +18,25 @@
18
18
  */
19
19
  import fs from 'fs';
20
20
  import path from 'path';
21
- import { createRequire } from 'module';
22
21
  import ini from 'ini';
23
22
  import { ConfigErrorCode as EC } from './types.js';
24
- // ---------------------------------------------------------------------------
25
- // Optional hazo_core integration — HazoConfigError when available
26
- // ---------------------------------------------------------------------------
27
- const _require = createRequire(import.meta.url);
28
23
  let HazoCoreConfigError = null;
29
- try {
30
- const errorsModule = _require('hazo_core/errors');
31
- HazoCoreConfigError = errorsModule.HazoConfigError ?? null;
32
- }
33
- catch {
34
- // hazo_core not installed — use local fallback below
35
- }
24
+ // Fire-and-forget: populates HazoCoreConfigError as soon as the microtask queue runs.
25
+ // Using dynamic import() with bundler-ignore hints so Turbopack/webpack never try
26
+ // to statically resolve this optional peer dep and emit "Module not found" warnings.
27
+ // hazo_core/errors is an ESM-only subpath export, so import() is required (require() never works).
28
+ void (async () => {
29
+ try {
30
+ // @ts-ignore — hazo_core/errors is a valid subpath export; moduleResolution:"node" predates
31
+ // subpath exports so TS can't resolve it statically, but it loads correctly at runtime.
32
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
33
+ const errorsModule = (await import(/* turbopackIgnore: true */ /* webpackIgnore: true */ 'hazo_core/errors'));
34
+ HazoCoreConfigError = errorsModule.HazoConfigError ?? null;
35
+ }
36
+ catch {
37
+ // hazo_core not installed — use local fallback below
38
+ }
39
+ })();
36
40
  /**
37
41
  * Local fallback error for when hazo_core is not installed.
38
42
  * When hazo_core IS installed, throws HazoConfigError from hazo_core instead.
@@ -304,7 +308,14 @@ export class HazoConfig {
304
308
  if (typeof values === 'object' && values !== null) {
305
309
  this.config[section] = {};
306
310
  for (const [key, value] of Object.entries(values)) {
307
- this.config[section][key] = String(value);
311
+ if (typeof value === 'object' && value !== null) {
312
+ // Dotted section [a.b] → ini parses as { a: { b: {...} } }.
313
+ // Register the composite key 'a.b' so getSection('a.b') works.
314
+ this._registerNestedSection(section + '.' + key, value);
315
+ }
316
+ else {
317
+ this.config[section][key] = String(value);
318
+ }
308
319
  }
309
320
  }
310
321
  }
@@ -333,4 +344,15 @@ export class HazoConfig {
333
344
  getFilePath() {
334
345
  return this.filePath;
335
346
  }
347
+ _registerNestedSection(compositeName, obj) {
348
+ this.config[compositeName] = {};
349
+ for (const [k, v] of Object.entries(obj)) {
350
+ if (typeof v === 'object' && v !== null) {
351
+ this._registerNestedSection(compositeName + '.' + k, v);
352
+ }
353
+ else {
354
+ this.config[compositeName][k] = String(v);
355
+ }
356
+ }
357
+ }
336
358
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hazo_config",
3
- "version": "2.1.7",
3
+ "version": "2.2.0",
4
4
  "description": "Config wrapper with error handling",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -81,14 +81,14 @@
81
81
  "@storybook/react": "^8.0.0",
82
82
  "@storybook/react-vite": "^8.0.0",
83
83
  "@storybook/test": "^8.0.0",
84
+ "@tailwindcss/postcss": "^4.2.4",
84
85
  "@testing-library/react": "^14.3.1",
85
86
  "@types/ini": "^4.1.0",
86
87
  "@types/node": "^20.14.10",
87
88
  "@types/react": "^18.3.3",
88
89
  "@types/react-dom": "^18.3.0",
89
- "@tailwindcss/postcss": "^4.2.4",
90
90
  "@vitejs/plugin-react": "^4.2.0",
91
- "hazo_connect": "^3.0.0",
91
+ "hazo_connect": "^3.8.0",
92
92
  "jsdom": "^24.1.3",
93
93
  "postcss": "^8.4.49",
94
94
  "react": "^18.2.0",
@@ -97,11 +97,11 @@
97
97
  "tailwindcss": "^4.2.4",
98
98
  "typescript": "^5.7.2",
99
99
  "vite": "^6.0.0",
100
- "vitest": "^1.6.1"
100
+ "vitest": "^4.0.15"
101
101
  },
102
102
  "peerDependencies": {
103
- "hazo_core": "^1.0.0",
104
- "hazo_ui": "^3.1.3",
103
+ "hazo_core": "^1.2.0",
104
+ "hazo_ui": "^4.0.0",
105
105
  "react": "^18.0.0 || ^19.0.0",
106
106
  "react-dom": "^18.0.0 || ^19.0.0",
107
107
  "tailwindcss": "^4.0.0"