hazo_config 2.4.3 → 2.6.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.
- package/CHANGE_LOG.md +73 -0
- package/dist/components/app_config.d.ts.map +1 -1
- package/dist/components/app_config.js +14 -13
- package/dist/components/app_config_list_editor/app_config_list_editor.d.ts.map +1 -1
- package/dist/components/app_config_list_editor/app_config_list_editor.js +8 -4
- package/dist/components/app_config_list_editor/components/bulk_edit_modal.d.ts.map +1 -1
- package/dist/components/app_config_list_editor/components/bulk_edit_modal.js +5 -4
- package/dist/components/app_config_list_editor/components/color_swatch_picker.d.ts.map +1 -1
- package/dist/components/app_config_list_editor/components/color_swatch_picker.js +2 -1
- package/dist/components/app_config_list_editor/components/delete_dialog.d.ts.map +1 -1
- package/dist/components/app_config_list_editor/components/delete_dialog.js +2 -1
- package/dist/components/app_config_list_editor/components/edit_modal.d.ts.map +1 -1
- package/dist/components/app_config_list_editor/components/edit_modal.js +14 -7
- package/dist/components/app_config_list_editor/components/empty_state.d.ts.map +1 -1
- package/dist/components/app_config_list_editor/components/empty_state.js +2 -1
- package/dist/components/app_config_list_editor/components/import_export_buttons.d.ts.map +1 -1
- package/dist/components/app_config_list_editor/components/import_export_buttons.js +8 -7
- package/dist/components/app_config_list_editor/components/list_item_row.d.ts.map +1 -1
- package/dist/components/app_config_list_editor/components/list_item_row.js +4 -3
- package/dist/components/app_config_list_editor/components/save_status_indicator.d.ts.map +1 -1
- package/dist/components/app_config_list_editor/components/save_status_indicator.js +2 -1
- package/dist/components/app_config_list_editor/components/search_bar.d.ts.map +1 -1
- package/dist/components/app_config_list_editor/components/search_bar.js +2 -1
- package/dist/components/config_editor.d.ts.map +1 -1
- package/dist/components/config_editor.js +5 -4
- package/dist/components/config_viewer.d.ts.map +1 -1
- package/dist/components/config_viewer.js +3 -2
- package/dist/components/ui/button.d.ts +1 -1
- package/dist/components/ui/button.d.ts.map +1 -1
- package/dist/lib/clone_to_override.d.ts +26 -0
- package/dist/lib/clone_to_override.d.ts.map +1 -0
- package/dist/lib/clone_to_override.js +60 -0
- package/dist/lib/index.d.ts +3 -0
- package/dist/lib/index.d.ts.map +1 -1
- package/dist/lib/index.js +7 -0
- package/dist/lib/override_count.d.ts +14 -0
- package/dist/lib/override_count.d.ts.map +1 -0
- package/dist/lib/override_count.js +32 -0
- package/dist/lib/strings.d.ts +2 -0
- package/dist/lib/strings.d.ts.map +1 -0
- package/dist/lib/strings.js +18 -0
- package/dist/lib/two_tier.d.ts +60 -0
- package/dist/lib/two_tier.d.ts.map +1 -0
- package/dist/lib/two_tier.js +56 -0
- package/dist/locales/en.json +104 -0
- package/dist/server-data/index.d.ts +17 -0
- package/dist/server-data/index.d.ts.map +1 -0
- package/dist/server-data/index.js +16 -0
- package/package.json +20 -5
package/CHANGE_LOG.md
CHANGED
|
@@ -1,5 +1,78 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2.6.0 — 2026-09-21
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
Full adoption of the canonical `hazo_core/i18n` bridge (`createPackageT`) for
|
|
8
|
+
this package's own baked-in UI strings — hazo_config previously had no i18n
|
|
9
|
+
bridge at all.
|
|
10
|
+
|
|
11
|
+
- `src/locales/en.json` — a new flat, literal-dotted fallback catalog (102
|
|
12
|
+
keys) covering every `'use client'` component's user-facing text: the
|
|
13
|
+
`ConfigEditor`/`ConfigViewer` screens, `AppConfig` (+ its add-key/add-section
|
|
14
|
+
`KeyDialog`), and the whole `AppConfigListEditor` family (`SearchBar`,
|
|
15
|
+
`EmptyState`, `ImportExportButtons`, `ListItemRow`, `EditModal`,
|
|
16
|
+
`BulkEditModal`, `DeleteDialog`, `ColorSwatchPicker`,
|
|
17
|
+
`SaveStatusIndicator`). Namespaced by component (`config_editor.*`,
|
|
18
|
+
`config_viewer.*`, `app_config.*`, `list_editor.*`, `bulk_edit.*`,
|
|
19
|
+
`edit_modal.*`, `search.*`, `empty_state.*`, `import_export.*`,
|
|
20
|
+
`list_item.*`, `save_status.*`, `color_swatch.*`), with a small `common.*`
|
|
21
|
+
namespace (`cancel`, `delete`, `select_placeholder`, `hide_value`,
|
|
22
|
+
`show_value`) for strings that were byte-identical across components.
|
|
23
|
+
`{{var}}` mustache interpolation used for all dynamic copy (item counts,
|
|
24
|
+
labels, key names).
|
|
25
|
+
- `src/lib/strings.ts` — `export const t = createPackageT('hazo_config', en)`,
|
|
26
|
+
re-exported from `hazo_config/lib`. A plain JSON import (no
|
|
27
|
+
`with { type: 'json' }` attribute) is used since every consuming component
|
|
28
|
+
is client-bundled and this package's tsconfig already has
|
|
29
|
+
`moduleResolution: "bundler"` + `resolveJsonModule: true`.
|
|
30
|
+
- Every hardcoded UI string literal across the components above now routes
|
|
31
|
+
through `t()` — labels, buttons, aria-labels, placeholders, validation
|
|
32
|
+
messages, and toast/status text. Original estimate was ~30 strings; actual
|
|
33
|
+
extraction came to 102 catalog keys (the `AppConfigListEditor` family alone
|
|
34
|
+
accounts for over half).
|
|
35
|
+
- `hazo_ui/test-harness` autotest group `hazo_config_i18n` (new
|
|
36
|
+
`test-app/app/autotest/` route + sidebar entry) — catalog-loads,
|
|
37
|
+
all-sampled-keys-resolve, interpolation, and coverage-threshold cases,
|
|
38
|
+
matching the pattern used by `hazo_ui`'s own i18n autotest scenarios.
|
|
39
|
+
- `hazo_i18n` added as an **optional** peer dependency (`^0.5.0`); `hazo_core`
|
|
40
|
+
peer range bumped `^2.3.0` → `^2.4.0` (first version shipping
|
|
41
|
+
`hazo_core/i18n`).
|
|
42
|
+
|
|
43
|
+
### Fixed
|
|
44
|
+
|
|
45
|
+
`tsconfig.build.json`'s `moduleResolution` was hardcoded to the classic
|
|
46
|
+
`"node"` resolver, which cannot follow a `package.json` `exports` subpath
|
|
47
|
+
(e.g. `hazo_core/i18n`) — TS2307. Switched to `"bundler"`, matching the
|
|
48
|
+
package's own `tsconfig.json`.
|
|
49
|
+
|
|
50
|
+
## 2.5.0 — 2026-09-06
|
|
51
|
+
|
|
52
|
+
### Added
|
|
53
|
+
|
|
54
|
+
Ecosystem-first promotion of taxclientdocs' two-tier (global default +
|
|
55
|
+
scope-level override) config helpers, previously duplicated in
|
|
56
|
+
`lib/config/{two-tier,clone-to-override,override-count}.ts`.
|
|
57
|
+
|
|
58
|
+
- `mergeTwoTier<T>(opts): T[]` (`src/lib/two_tier.ts`, exported from the main
|
|
59
|
+
entry and `hazo_config/lib`) — pure, zero-dep merge of a `globals[]` +
|
|
60
|
+
`overrides[]` row pair (each shaped `{ id, scope_id, enabled, ... }`) into
|
|
61
|
+
the effective list. Override wins by key (via `keyFn`); an `additive` mode
|
|
62
|
+
returns the union of both tiers instead. `TwoTierRow`/`MergeOptions` types
|
|
63
|
+
exported alongside. No database dependency — callers fetch `globals`/
|
|
64
|
+
`overrides` however they like, matching the existing `AppConfig`
|
|
65
|
+
callback-persistence pattern.
|
|
66
|
+
- New `hazo_config/server-data` entry point: `cloneToOverride(table,
|
|
67
|
+
globalRowId, orgScopeId)` / `CloneToOverrideError` and
|
|
68
|
+
`getOverrideCount(table, keyColumn, keyValue)` (`src/lib/clone_to_override.ts`,
|
|
69
|
+
`src/lib/override_count.ts`) — hazo_connect-backed CRUD helpers for cloning
|
|
70
|
+
a global row into a scope-level override and counting existing overrides
|
|
71
|
+
for a key. Kept in a separate entry point (not `/server` or `/lib`) so a
|
|
72
|
+
consumer of the fs-based `HazoConfig` or the pure `mergeTwoTier` is never
|
|
73
|
+
required to install `hazo_connect`. `hazo_connect` added as an **optional**
|
|
74
|
+
peer dependency (`^4.1.0`) accordingly.
|
|
75
|
+
|
|
3
76
|
## 2.4.2 — 2026-08-04
|
|
4
77
|
|
|
5
78
|
### Changed
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app_config.d.ts","sourceRoot":"","sources":["../../src/components/app_config.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAyC,MAAM,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"app_config.d.ts","sourceRoot":"","sources":["../../src/components/app_config.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAyC,MAAM,OAAO,CAAA;AAM7D,OAAO,KAAK,EAAE,cAAc,EAA6B,MAAM,4BAA4B,CAAA;AA0P3F;;;;;;GAMG;AACH,eAAO,MAAM,SAAS,EAAE,KAAK,CAAC,EAAE,CAAC,cAAc,CA6a9C,CAAA"}
|
|
@@ -7,6 +7,7 @@ import { cn } from 'hazo_ui';
|
|
|
7
7
|
import { RefreshCw, Save, Trash2, Eye, EyeOff, Plus, X } from 'lucide-react';
|
|
8
8
|
import { useAppConfig } from './use_app_config.js';
|
|
9
9
|
import { is_sensitive_field, mask_value, DEFAULT_SENSITIVE_PATTERNS } from './use_config_sections.js';
|
|
10
|
+
import { t } from '../lib/strings.js';
|
|
10
11
|
const JsonEditor = ({ value, onChange, onValidationChange, placeholder, className }) => {
|
|
11
12
|
const [text_value, set_text_value] = useState(() => JSON.stringify(value, null, 2));
|
|
12
13
|
const [validation_error, set_validation_error] = useState();
|
|
@@ -20,7 +21,7 @@ const JsonEditor = ({ value, onChange, onValidationChange, placeholder, classNam
|
|
|
20
21
|
onValidationChange?.(true);
|
|
21
22
|
}
|
|
22
23
|
catch (err) {
|
|
23
|
-
const error_msg = err instanceof Error ? err.message : '
|
|
24
|
+
const error_msg = err instanceof Error ? err.message : t('app_config.invalid_json');
|
|
24
25
|
set_validation_error(error_msg);
|
|
25
26
|
onValidationChange?.(false, error_msg);
|
|
26
27
|
}
|
|
@@ -67,7 +68,7 @@ const KeyDialog = ({ mode, section_name, on_close, on_save }) => {
|
|
|
67
68
|
: new_key && new_value && is_json_valid;
|
|
68
69
|
if (!mode)
|
|
69
70
|
return null;
|
|
70
|
-
return (_jsx("div", { className: "fixed inset-0 bg-black/50 flex items-center justify-center z-50", children: _jsxs("div", { className: "bg-white rounded-lg shadow-xl max-w-md w-full mx-4", children: [_jsxs("div", { className: "flex items-center justify-between p-6 border-b", children: [_jsx("h3", { className: "text-lg font-semibold", children: mode === 'add-section' ? '
|
|
71
|
+
return (_jsx("div", { className: "fixed inset-0 bg-black/50 flex items-center justify-center z-50", children: _jsxs("div", { className: "bg-white rounded-lg shadow-xl max-w-md w-full mx-4", children: [_jsxs("div", { className: "flex items-center justify-between p-6 border-b", children: [_jsx("h3", { className: "text-lg font-semibold", children: mode === 'add-section' ? t('app_config.dialog_title_add_section') : t('app_config.dialog_title_add_key') }), _jsx("button", { onClick: on_close, className: "text-muted-foreground hover:text-foreground", type: "button", children: _jsx(X, { className: "h-5 w-5" }) })] }), _jsxs("div", { className: "p-6 space-y-4", children: [mode === 'add-section' && (_jsxs("div", { className: "space-y-2", children: [_jsx("label", { className: "text-sm font-medium", children: t('app_config.section_name_label') }), _jsx("input", { type: "text", placeholder: t('app_config.section_name_placeholder'), value: new_section, onChange: (e) => set_new_section(e.target.value), className: "w-full px-3 py-2 border border-input rounded-md focus:outline-none focus:ring-2 focus:ring-primary", autoFocus: true })] })), _jsxs("div", { className: "space-y-2", children: [_jsx("label", { className: "text-sm font-medium", children: t('app_config.key_name_label') }), _jsx("input", { type: "text", placeholder: t('app_config.key_name_placeholder'), value: new_key, onChange: (e) => set_new_key(e.target.value), className: "w-full px-3 py-2 border border-input rounded-md focus:outline-none focus:ring-2 focus:ring-primary", autoFocus: mode !== 'add-section' })] }), _jsxs("div", { className: "space-y-2", children: [_jsx("label", { className: "text-sm font-medium", children: t('app_config.type_label') }), _jsxs("div", { className: "flex gap-4", children: [_jsxs("label", { className: "flex items-center gap-2 cursor-pointer", children: [_jsx("input", { type: "radio", name: "type", value: "general", checked: new_type === 'general', onChange: (e) => set_new_type(e.target.value), className: "w-4 h-4 text-primary" }), _jsx("span", { className: "text-sm", children: t('app_config.type_general') })] }), _jsxs("label", { className: "flex items-center gap-2 cursor-pointer", children: [_jsx("input", { type: "radio", name: "type", value: "json", checked: new_type === 'json', onChange: (e) => set_new_type(e.target.value), className: "w-4 h-4 text-primary" }), _jsx("span", { className: "text-sm", children: t('app_config.type_json') })] })] })] }), _jsxs("div", { className: "space-y-2", children: [_jsx("label", { className: "text-sm font-medium", children: t('app_config.value_label') }), new_type === 'json' ? (_jsx(JsonEditor, { value: new_value ? (() => {
|
|
71
72
|
try {
|
|
72
73
|
return JSON.parse(new_value);
|
|
73
74
|
}
|
|
@@ -76,9 +77,9 @@ const KeyDialog = ({ mode, section_name, on_close, on_save }) => {
|
|
|
76
77
|
}
|
|
77
78
|
})() : {}, onChange: (val) => set_new_value(JSON.stringify(val, null, 2)), onValidationChange: (isValid) => {
|
|
78
79
|
set_is_json_valid(isValid);
|
|
79
|
-
}, placeholder: '{"key": "value"}' })) : (_jsx("input", { type: "text", placeholder:
|
|
80
|
+
}, placeholder: '{"key": "value"}' })) : (_jsx("input", { type: "text", placeholder: t('app_config.value_placeholder'), value: new_value, onChange: (e) => set_new_value(e.target.value), className: "w-full px-3 py-2 border border-input rounded-md focus:outline-none focus:ring-2 focus:ring-primary" }))] })] }), _jsxs("div", { className: "flex items-center justify-end gap-3 p-6 border-t bg-muted/30", children: [_jsx("button", { onClick: on_close, className: "px-4 py-2 text-sm font-medium text-muted-foreground hover:text-foreground", type: "button", children: t('common.cancel') }), _jsx("button", { onClick: handle_save, disabled: !can_save || is_saving, className: cn('px-4 py-2 text-sm font-medium rounded-md', can_save && !is_saving
|
|
80
81
|
? 'bg-primary text-primary-foreground hover:bg-primary/90'
|
|
81
|
-
: 'bg-muted text-muted-foreground cursor-not-allowed'), type: "button", children: is_saving ? '
|
|
82
|
+
: 'bg-muted text-muted-foreground cursor-not-allowed'), type: "button", children: is_saving ? t('app_config.saving') : mode === 'add-section' ? t('app_config.create_section') : t('app_config.add_key') })] })] }) }));
|
|
82
83
|
};
|
|
83
84
|
/**
|
|
84
85
|
* AppConfig component
|
|
@@ -87,7 +88,7 @@ const KeyDialog = ({ mode, section_name, on_close, on_save }) => {
|
|
|
87
88
|
* @param props - Component props
|
|
88
89
|
* @returns React component
|
|
89
90
|
*/
|
|
90
|
-
export const AppConfig = ({ context, fetch_config, save_config, delete_config, className, on_update, on_error, sensitive_fields, sensitive_patterns = DEFAULT_SENSITIVE_PATTERNS, disable_auto_mask = false, title = '
|
|
91
|
+
export const AppConfig = ({ context, fetch_config, save_config, delete_config, className, on_update, on_error, sensitive_fields, sensitive_patterns = DEFAULT_SENSITIVE_PATTERNS, disable_auto_mask = false, title = t('app_config.title_default'), }) => {
|
|
91
92
|
const { sections, is_loading, error, reload, set_value, delete_value, } = useAppConfig(context, fetch_config, save_config, delete_config);
|
|
92
93
|
const [selected_section, set_selected_section] = useState(null);
|
|
93
94
|
const [editing_fields, set_editing_fields] = useState(new Map());
|
|
@@ -217,7 +218,7 @@ export const AppConfig = ({ context, fetch_config, save_config, delete_config, c
|
|
|
217
218
|
* Handle delete key
|
|
218
219
|
*/
|
|
219
220
|
const handle_delete_key = useCallback(async (section, key) => {
|
|
220
|
-
if (!window.confirm(
|
|
221
|
+
if (!window.confirm(t('app_config.delete_key_confirm', { key })))
|
|
221
222
|
return;
|
|
222
223
|
try {
|
|
223
224
|
await delete_value(section, key);
|
|
@@ -270,13 +271,13 @@ export const AppConfig = ({ context, fetch_config, save_config, delete_config, c
|
|
|
270
271
|
return selected_section ? sections[selected_section] : null;
|
|
271
272
|
}, [selected_section, sections]);
|
|
272
273
|
if (is_loading) {
|
|
273
|
-
return (_jsx("div", { className: cn('cls_app_config flex items-center justify-center p-8', className), children: _jsx("div", { className: "text-muted-foreground", children:
|
|
274
|
+
return (_jsx("div", { className: cn('cls_app_config flex items-center justify-center p-8', className), children: _jsx("div", { className: "text-muted-foreground", children: t('app_config.loading') }) }));
|
|
274
275
|
}
|
|
275
|
-
return (_jsxs(_Fragment, { children: [_jsxs("div", { className: cn('cls_app_config space-y-6', className), children: [_jsxs("div", { className: "cls_app_config_header flex items-center justify-between", children: [_jsxs("div", { children: [_jsx("h2", { className: "text-2xl font-bold", children: title }), has_unsaved_changes && (_jsx("p", { className: "text-sm text-amber-600 mt-1", children:
|
|
276
|
+
return (_jsxs(_Fragment, { children: [_jsxs("div", { className: cn('cls_app_config space-y-6', className), children: [_jsxs("div", { className: "cls_app_config_header flex items-center justify-between", children: [_jsxs("div", { children: [_jsx("h2", { className: "text-2xl font-bold", children: title }), has_unsaved_changes && (_jsx("p", { className: "text-sm text-amber-600 mt-1", children: t('app_config.unsaved_changes') }))] }), _jsxs("div", { className: "flex gap-3", children: [_jsxs("button", { onClick: handle_refresh, className: "px-4 py-2 border border-input rounded-md hover:bg-accent hover:text-accent-foreground flex items-center gap-2 transition-colors", type: "button", disabled: is_saving, children: [_jsx(RefreshCw, { className: "h-4 w-4" }), t('app_config.refresh')] }), _jsxs("button", { onClick: handle_save, className: cn('px-4 py-2 rounded-md flex items-center gap-2 transition-colors', has_unsaved_changes
|
|
276
277
|
? 'bg-primary text-primary-foreground hover:bg-primary/90'
|
|
277
|
-
: 'bg-muted text-muted-foreground cursor-not-allowed'), type: "button", disabled: !has_unsaved_changes || is_saving, children: [_jsx(Save, { className: "h-4 w-4" }), is_saving ? '
|
|
278
|
+
: 'bg-muted text-muted-foreground cursor-not-allowed'), type: "button", disabled: !has_unsaved_changes || is_saving, children: [_jsx(Save, { className: "h-4 w-4" }), is_saving ? t('app_config.saving') : t('app_config.save')] })] })] }), _jsxs("div", { className: "grid grid-cols-1 lg:grid-cols-4 gap-6", children: [_jsx("div", { className: "lg:col-span-1", children: _jsxs("div", { className: "bg-card border border-border rounded-lg overflow-hidden", children: [_jsx("div", { className: "p-4 border-b bg-muted/30", children: _jsxs("div", { className: "flex items-center justify-between mb-3", children: [_jsx("h3", { className: "font-semibold", children: t('app_config.sections_heading') }), _jsx("button", { onClick: () => set_dialog_mode('add-section'), className: "text-primary hover:text-primary/80 transition-colors", type: "button", children: _jsx(Plus, { className: "h-5 w-5" }) })] }) }), _jsx("div", { className: "p-2", children: section_list.length === 0 ? (_jsx("div", { className: "text-sm text-muted-foreground text-center py-8 px-4", children: t('app_config.no_sections') })) : (_jsx("div", { className: "space-y-1", children: section_list.map((section_name) => (_jsx("button", { onClick: () => set_selected_section(section_name), className: cn('w-full text-left px-3 py-2 rounded-md text-sm font-medium transition-colors', selected_section === section_name
|
|
278
279
|
? 'bg-primary text-primary-foreground'
|
|
279
|
-
: 'hover:bg-muted'), type: "button", children: section_name }, section_name))) })) })] }) }), _jsx("div", { className: "lg:col-span-3", children: selected_section && current_section_data ? (_jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("h3", { className: "text-xl font-semibold", children: selected_section }), _jsxs("button", { onClick: () => set_dialog_mode('add'), className: "px-4 py-2 bg-primary text-primary-foreground rounded-md hover:bg-primary/90 flex items-center gap-2 transition-colors", type: "button", children: [_jsx(Plus, { className: "h-4 w-4" }),
|
|
280
|
+
: 'hover:bg-muted'), type: "button", children: section_name }, section_name))) })) })] }) }), _jsx("div", { className: "lg:col-span-3", children: selected_section && current_section_data ? (_jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("h3", { className: "text-xl font-semibold", children: selected_section }), _jsxs("button", { onClick: () => set_dialog_mode('add'), className: "px-4 py-2 bg-primary text-primary-foreground rounded-md hover:bg-primary/90 flex items-center gap-2 transition-colors", type: "button", children: [_jsx(Plus, { className: "h-4 w-4" }), t('app_config.add_key')] })] }), _jsxs("div", { className: "space-y-3", children: [Object.entries(current_section_data).map(([key, item]) => {
|
|
280
281
|
const is_sensitive = should_mask_field(key);
|
|
281
282
|
const show_value = !is_sensitive || is_revealed(selected_section, key);
|
|
282
283
|
const display_value = get_field_value(selected_section, key, item);
|
|
@@ -285,7 +286,7 @@ export const AppConfig = ({ context, fetch_config, save_config, delete_config, c
|
|
|
285
286
|
: 'bg-gray-100 text-gray-700'), children: item.config_type })] }) }), _jsxs("div", { className: "flex items-center gap-2", children: [is_sensitive && (_jsx("button", { onClick: () => toggle_reveal(selected_section, key), className: "text-muted-foreground hover:text-foreground transition-colors", type: "button", children: show_value ? _jsx(EyeOff, { className: "h-4 w-4" }) : _jsx(Eye, { className: "h-4 w-4" }) })), _jsx("button", { onClick: () => handle_delete_key(selected_section, key), className: "text-red-500 hover:text-red-700 transition-colors", type: "button", children: _jsx(Trash2, { className: "h-4 w-4" }) })] })] }), item.config_type === 'json' ? (_jsx(JsonEditor, { value: typeof display_value === 'object' ? display_value : {}, onChange: (val) => handle_field_change(selected_section, key, val), className: "bg-muted/30" })) : (_jsx("input", { type: is_sensitive && !show_value ? 'password' : 'text', value: is_sensitive && !show_value
|
|
286
287
|
? mask_value(String(display_value))
|
|
287
288
|
: String(display_value), onChange: (e) => handle_field_change(selected_section, key, e.target.value), className: "w-full px-3 py-2 border border-input rounded-md focus:outline-none focus:ring-2 focus:ring-primary", readOnly: is_sensitive && !show_value }))] }, key));
|
|
288
|
-
}), Object.keys(current_section_data).length === 0 && (_jsx("div", { className: "text-center py-12 text-muted-foreground", children:
|
|
289
|
-
? '
|
|
290
|
-
: '
|
|
289
|
+
}), Object.keys(current_section_data).length === 0 && (_jsx("div", { className: "text-center py-12 text-muted-foreground", children: t('app_config.no_keys') }))] })] })) : (_jsx("div", { className: "flex items-center justify-center h-64 bg-muted/30 border border-dashed border-border rounded-lg", children: _jsx("div", { className: "text-center text-muted-foreground", children: section_list.length > 0
|
|
290
|
+
? t('app_config.select_section_prompt')
|
|
291
|
+
: t('app_config.create_first_section_prompt') }) })) })] })] }), _jsx(KeyDialog, { mode: dialog_mode, section_name: selected_section || undefined, on_close: () => set_dialog_mode(null), on_save: handle_dialog_save })] }));
|
|
291
292
|
};
|
|
@@ -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":"
|
|
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":"AASA,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,EAEpB,gBAAgB,EAChB,gBAAgB,EAChB,cAAc,EACd,cAAc,EAEd,cAAc,EACd,cAAc,EACd,cAAc,EACd,SAAS,GACV,EAAE,wBAAwB,CAAC,CAAC,CAAC,+BA0c7B"}
|
|
@@ -5,6 +5,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
5
5
|
import { useState, useCallback, useMemo } from 'react';
|
|
6
6
|
import { Plus, Trash2 } from 'lucide-react';
|
|
7
7
|
import { cn, HazoUiConfirmDialog } from 'hazo_ui';
|
|
8
|
+
import { t } from '../../lib/strings.js';
|
|
8
9
|
import { ListItemRow } from './components/list_item_row.js';
|
|
9
10
|
import { EditModal } from './components/edit_modal.js';
|
|
10
11
|
import { SearchBar } from './components/search_bar.js';
|
|
@@ -248,9 +249,12 @@ on_item_create, on_item_update, on_item_delete, on_reload, }) {
|
|
|
248
249
|
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) => {
|
|
249
250
|
if (el)
|
|
250
251
|
el.indeterminate = !all_filtered_selected && some_filtered_selected;
|
|
251
|
-
}, onChange: toggle_select_all, className: "w-4 h-4 rounded border-gray-300 text-violet-600 focus:ring-violet-500", "aria-label":
|
|
252
|
+
}, onChange: toggle_select_all, className: "w-4 h-4 rounded border-gray-300 text-violet-600 focus:ring-violet-500", "aria-label": t('list_editor.select_all_aria') })), _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
|
|
252
253
|
? 'bg-gray-100 text-gray-400 cursor-not-allowed'
|
|
253
|
-
: 'bg-violet-600 hover:bg-violet-700 text-white'), title: is_max_reached ?
|
|
254
|
-
handle_cancel_delete(); }, title:
|
|
255
|
-
set_bulk_delete_open(false); }, title:
|
|
254
|
+
: 'bg-violet-600 hover:bg-violet-700 text-white'), title: is_max_reached ? t('list_editor.max_items_title', { max: max_items ?? 0 }) : undefined, children: [_jsx(Plus, { className: "w-4 h-4" }), t('list_editor.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: [_jsx("span", { className: "text-sm font-medium text-violet-800", children: t('list_editor.selected_count', { count: selection_count }) }), _jsx("button", { type: "button", onClick: clear_selection, className: "text-xs text-violet-600 hover:text-violet-800 underline", children: t('list_editor.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: t('list_editor.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" }), t('list_editor.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 ? (_jsx("div", { className: "py-8 text-center text-sm text-gray-500", children: t('list_editor.no_items_matching', { item_label, term: search_term }) })) : (_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 && (_jsx("p", { className: "text-xs text-gray-400 mt-2", children: t('list_editor.max_reached_note', { max: max_items ?? 0, item_label }) })), 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(HazoUiConfirmDialog, { open: true, onOpenChange: (is_open) => { if (!is_open)
|
|
255
|
+
handle_cancel_delete(); }, title: t('list_editor.delete_confirm_title', { name: delete_item_name }), description: delete_message, variant: "destructive", confirmLabel: t('common.delete'), onConfirm: handle_delete_confirm, onCancel: handle_cancel_delete })), on_bulk_delete && (_jsx(HazoUiConfirmDialog, { open: bulk_delete_open, onOpenChange: (is_open) => { if (!is_open)
|
|
256
|
+
set_bulk_delete_open(false); }, title: t('list_editor.bulk_delete_title', {
|
|
257
|
+
count: selection_count,
|
|
258
|
+
label: selection_count === 1 ? item_type_label.toLowerCase() : item_label,
|
|
259
|
+
}), description: t('list_editor.bulk_delete_description'), variant: "destructive", confirmLabel: t('list_editor.bulk_delete_confirm_label', { count: selection_count }), onConfirm: handle_bulk_delete_confirm, onCancel: () => set_bulk_delete_open(false) })), 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) }))] }));
|
|
256
260
|
}
|
|
@@ -1 +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":"
|
|
1
|
+
{"version":3,"file":"bulk_edit_modal.d.ts","sourceRoot":"","sources":["../../../../src/components/app_config_list_editor/components/bulk_edit_modal.tsx"],"names":[],"mappings":"AAiBA,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"}
|
|
@@ -4,6 +4,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
4
4
|
// Allows editing shared fields across multiple selected items
|
|
5
5
|
import { useState, useCallback, useEffect } from 'react';
|
|
6
6
|
import { cn } from 'hazo_ui';
|
|
7
|
+
import { t } from '../../../lib/strings.js';
|
|
7
8
|
import { Input } from '../../ui/input.js';
|
|
8
9
|
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, } from '../../ui/dialog.js';
|
|
9
10
|
import { Button } from '../../ui/button.js';
|
|
@@ -64,7 +65,7 @@ export function BulkEditModal({ open, selected_count, columns, bulk_edit_fields,
|
|
|
64
65
|
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
|
}
|
|
66
67
|
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 || '
|
|
68
|
+
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 || t('common.select_placeholder') }), col.options.map((opt) => (_jsx("option", { value: opt.value, children: opt.label }, opt.value)))] }));
|
|
68
69
|
}
|
|
69
70
|
if (col.type === 'textarea') {
|
|
70
71
|
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') }));
|
|
@@ -76,8 +77,8 @@ export function BulkEditModal({ open, selected_count, columns, bulk_edit_fields,
|
|
|
76
77
|
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
|
};
|
|
78
79
|
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: [
|
|
80
|
+
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: [_jsx(DialogTitle, { children: t('bulk_edit.title', { count: selected_count }) }), _jsx("p", { className: "text-sm text-gray-500 mt-1", children: t('bulk_edit.description') })] }), _jsxs("div", { className: "px-6 pb-4 space-y-3 overflow-y-auto flex-1 min-h-0", children: [editable_cols.map((col) => {
|
|
80
81
|
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:
|
|
82
|
-
}), editable_cols.length === 0 && (_jsx("p", { className: "text-sm text-gray-400 py-4 text-center", children:
|
|
82
|
+
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: t('bulk_edit.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));
|
|
83
|
+
}), editable_cols.length === 0 && (_jsx("p", { className: "text-sm text-gray-400 py-4 text-center", children: t('bulk_edit.no_fields') }))] }), _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: t('common.cancel') }), _jsx(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: t('bulk_edit.apply', { count: selected_count }) })] })] }) }));
|
|
83
84
|
}
|
|
@@ -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":"
|
|
1
|
+
{"version":3,"file":"color_swatch_picker.d.ts","sourceRoot":"","sources":["../../../../src/components/app_config_list_editor/components/color_swatch_picker.tsx"],"names":[],"mappings":"AAOA,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"}
|
|
@@ -3,6 +3,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
3
3
|
// Displays a row of circular color swatches for selection
|
|
4
4
|
import { Check } from 'lucide-react';
|
|
5
5
|
import { cn } from 'hazo_ui';
|
|
6
|
+
import { t } from '../../../lib/strings.js';
|
|
6
7
|
export function ColorSwatchPicker({ value, options, on_change, className, }) {
|
|
7
8
|
return (_jsx("div", { className: cn('cls_color_swatch_picker flex flex-wrap gap-2', className), children: options.map((color) => {
|
|
8
9
|
const is_selected = value === color;
|
|
@@ -10,6 +11,6 @@ export function ColorSwatchPicker({ value, options, on_change, className, }) {
|
|
|
10
11
|
const bg_class = color.split(' ').find(c => c.startsWith('bg-')) || 'bg-gray-200';
|
|
11
12
|
return (_jsx("button", { type: "button", onClick: () => on_change(color), className: cn('cls_color_swatch w-7 h-7 rounded-full flex items-center justify-center transition-transform hover:scale-110', bg_class, is_selected
|
|
12
13
|
? 'ring-2 ring-violet-600 ring-offset-2'
|
|
13
|
-
: 'ring-1 ring-gray-200'), "aria-label":
|
|
14
|
+
: 'ring-1 ring-gray-200'), "aria-label": t('color_swatch.select_aria', { color }), "aria-pressed": is_selected, children: is_selected && (_jsx(Check, { className: "w-3.5 h-3.5 text-current opacity-80" })) }, color));
|
|
14
15
|
}) }));
|
|
15
16
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"delete_dialog.d.ts","sourceRoot":"","sources":["../../../../src/components/app_config_list_editor/components/delete_dialog.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"delete_dialog.d.ts","sourceRoot":"","sources":["../../../../src/components/app_config_list_editor/components/delete_dialog.tsx"],"names":[],"mappings":"AAgBA,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"}
|
|
@@ -2,8 +2,9 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
// Delete confirmation dialog component
|
|
3
3
|
// Uses shadcn AlertDialog with red warning styling
|
|
4
4
|
import { AlertTriangle } from 'lucide-react';
|
|
5
|
+
import { t } from '../../../lib/strings.js';
|
|
5
6
|
import { AlertDialog, AlertDialogContent, AlertDialogHeader, AlertDialogTitle, AlertDialogDescription, AlertDialogFooter, AlertDialogCancel, AlertDialogAction, } from '../../ui/alert-dialog.js';
|
|
6
7
|
export function DeleteDialog({ open, item_name, message, on_confirm, on_cancel, }) {
|
|
7
8
|
return (_jsx(AlertDialog, { open: open, onOpenChange: (is_open) => { if (!is_open)
|
|
8
|
-
on_cancel(); }, children: _jsxs(AlertDialogContent, { className: "cls_delete_dialog max-w-sm rounded-2xl", children: [_jsxs(AlertDialogHeader, { className: "items-center sm:items-center", children: [_jsx("div", { className: "w-12 h-12 rounded-full bg-red-100 flex items-center justify-center mb-2", children: _jsx(AlertTriangle, { className: "w-6 h-6 text-red-600" }) }), _jsxs(AlertDialogTitle, { className: "text-center", children: ["Delete \u201C", item_name, "\u201D?"] }), _jsx(AlertDialogDescription, { className: "text-center", children: message })] }), _jsxs(AlertDialogFooter, { className: "sm:justify-center gap-2", children: [_jsx(AlertDialogCancel, { onClick: on_cancel, className: "rounded-full", children:
|
|
9
|
+
on_cancel(); }, children: _jsxs(AlertDialogContent, { className: "cls_delete_dialog max-w-sm rounded-2xl", children: [_jsxs(AlertDialogHeader, { className: "items-center sm:items-center", children: [_jsx("div", { className: "w-12 h-12 rounded-full bg-red-100 flex items-center justify-center mb-2", children: _jsx(AlertTriangle, { className: "w-6 h-6 text-red-600" }) }), _jsxs(AlertDialogTitle, { className: "text-center", children: ["Delete \u201C", item_name, "\u201D?"] }), _jsx(AlertDialogDescription, { className: "text-center", children: message })] }), _jsxs(AlertDialogFooter, { className: "sm:justify-center gap-2", children: [_jsx(AlertDialogCancel, { onClick: on_cancel, className: "rounded-full", children: t('common.cancel') }), _jsx(AlertDialogAction, { onClick: on_confirm, className: "rounded-full bg-red-600 text-white hover:bg-red-700", children: t('common.delete') })] })] }) }));
|
|
9
10
|
}
|
|
@@ -1 +1 @@
|
|
|
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;
|
|
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;AAe/D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAQ5C,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,qBA2UnB"}
|
|
@@ -4,6 +4,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
4
4
|
// Centered dialog for creating/editing items with dynamic form fields
|
|
5
5
|
import React, { useState, useCallback, useEffect } from 'react';
|
|
6
6
|
import { cn } from 'hazo_ui';
|
|
7
|
+
import { t } from '../../../lib/strings.js';
|
|
7
8
|
import { Input } from '../../ui/input.js';
|
|
8
9
|
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, } from '../../ui/dialog.js';
|
|
9
10
|
import { Button } from '../../ui/button.js';
|
|
@@ -56,7 +57,7 @@ export function EditModal({ open, mode, item: initial_item, columns, id_field, a
|
|
|
56
57
|
const value = form_data[col.field];
|
|
57
58
|
// Required check
|
|
58
59
|
if (col.required && (value === undefined || value === null || value === '')) {
|
|
59
|
-
new_errors.set(col.field,
|
|
60
|
+
new_errors.set(col.field, t('edit_modal.field_required', { label: col.label }));
|
|
60
61
|
continue;
|
|
61
62
|
}
|
|
62
63
|
// Custom validation
|
|
@@ -70,7 +71,7 @@ export function EditModal({ open, mode, item: initial_item, columns, id_field, a
|
|
|
70
71
|
// Check ID field is filled
|
|
71
72
|
const id_value = form_data[id_field];
|
|
72
73
|
if (!id_value || String(id_value).trim() === '') {
|
|
73
|
-
new_errors.set(id_field, '
|
|
74
|
+
new_errors.set(id_field, t('edit_modal.id_required'));
|
|
74
75
|
}
|
|
75
76
|
// Check for duplicate ID
|
|
76
77
|
if (id_value) {
|
|
@@ -79,7 +80,7 @@ export function EditModal({ open, mode, item: initial_item, columns, id_field, a
|
|
|
79
80
|
? existing_ids.includes(id_str)
|
|
80
81
|
: existing_ids.filter(id => id !== String(initial_item[id_field])).includes(id_str);
|
|
81
82
|
if (is_duplicate) {
|
|
82
|
-
new_errors.set(id_field, '
|
|
83
|
+
new_errors.set(id_field, t('edit_modal.id_duplicate'));
|
|
83
84
|
}
|
|
84
85
|
}
|
|
85
86
|
set_errors(new_errors);
|
|
@@ -119,11 +120,17 @@ export function EditModal({ open, mode, item: initial_item, columns, id_field, a
|
|
|
119
120
|
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.type === 'text' && (_jsx(Input, { type: "text", value: String(value ?? ''), onChange: (e) => is_id_field
|
|
120
121
|
? handle_id_change(e.target.value)
|
|
121
122
|
: 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 && ((col.searchable || col.options.length > 10)
|
|
122
|
-
? (_jsx(Combobox, { value: String(value ?? ''), onChange: (v) => update_field(col.field, v), options: [{ value: '', label: col.placeholder || '
|
|
123
|
-
: (_jsxs(Select, { value: String(value ?? ''), onValueChange: (v) => update_field(col.field, v === SELECT_CLEAR_VALUE ? '' : v), children: [_jsx(SelectTrigger, { className: cn(error && 'border-red-400'), children: _jsx(SelectValue, { placeholder: col.placeholder || '
|
|
123
|
+
? (_jsx(Combobox, { value: String(value ?? ''), onChange: (v) => update_field(col.field, v), options: [{ value: '', label: col.placeholder || t('common.select_placeholder') }, ...col.options], placeholder: col.placeholder || t('common.select_placeholder'), error: !!error }))
|
|
124
|
+
: (_jsxs(Select, { value: String(value ?? ''), onValueChange: (v) => update_field(col.field, v === SELECT_CLEAR_VALUE ? '' : v), children: [_jsx(SelectTrigger, { className: cn(error && 'border-red-400'), children: _jsx(SelectValue, { placeholder: col.placeholder || t('common.select_placeholder') }) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: SELECT_CLEAR_VALUE, children: col.placeholder || t('common.select_placeholder') }), col.options.map((opt) => (_jsx(SelectItem, { 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 && (_jsx("p", { className: "text-xs text-gray-400", children: t('edit_modal.auto_generated_hint', { source: 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));
|
|
124
125
|
};
|
|
125
126
|
return (_jsx(Dialog, { open: open, onOpenChange: (is_open) => { if (!is_open)
|
|
126
|
-
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'
|
|
127
|
+
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'
|
|
128
|
+
? t('edit_modal.title_new', { item_type: item_type_label })
|
|
129
|
+
: t('edit_modal.title_edit', { item_type: 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: t('common.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
|
|
130
|
+
? t('edit_modal.saving')
|
|
131
|
+
: mode === 'create'
|
|
132
|
+
? t('edit_modal.add_button', { item_type: item_type_label })
|
|
133
|
+
: t('edit_modal.save_changes') })] })] }) }));
|
|
127
134
|
}
|
|
128
135
|
/* ── Tag Picker Field ── */
|
|
129
136
|
function TagPickerField({ value, options, on_change }) {
|
|
@@ -151,5 +158,5 @@ function TagPickerField({ value, options, on_change }) {
|
|
|
151
158
|
on_change(value.filter(v => v !== tag_value));
|
|
152
159
|
};
|
|
153
160
|
const label_for = (v) => options.find(o => o.value === v)?.label ?? v;
|
|
154
|
-
return (_jsxs("div", { ref: ref, className: "relative", children: [_jsxs("div", { className: cn('flex flex-wrap gap-1.5 min-h-[38px] p-1.5 border rounded-lg bg-white cursor-text', open && 'ring-2 ring-violet-500/20 border-violet-400'), onClick: () => set_open(true), children: [value.map(v => (_jsxs("span", { className: "inline-flex items-center gap-1 px-2 py-0.5 rounded-md bg-violet-100 text-violet-800 text-xs font-medium", children: [label_for(v), _jsx("button", { type: "button", onClick: (e) => { e.stopPropagation(); remove_tag(v); }, className: "text-violet-500 hover:text-violet-800 ml-0.5", children: "\u00D7" })] }, v))), _jsx("input", { type: "text", value: search, onChange: e => { set_search(e.target.value); set_open(true); }, onFocus: () => set_open(true), placeholder: value.length === 0 ? '
|
|
161
|
+
return (_jsxs("div", { ref: ref, className: "relative", children: [_jsxs("div", { className: cn('flex flex-wrap gap-1.5 min-h-[38px] p-1.5 border rounded-lg bg-white cursor-text', open && 'ring-2 ring-violet-500/20 border-violet-400'), onClick: () => set_open(true), children: [value.map(v => (_jsxs("span", { className: "inline-flex items-center gap-1 px-2 py-0.5 rounded-md bg-violet-100 text-violet-800 text-xs font-medium", children: [label_for(v), _jsx("button", { type: "button", onClick: (e) => { e.stopPropagation(); remove_tag(v); }, className: "text-violet-500 hover:text-violet-800 ml-0.5", children: "\u00D7" })] }, v))), _jsx("input", { type: "text", value: search, onChange: e => { set_search(e.target.value); set_open(true); }, onFocus: () => set_open(true), placeholder: value.length === 0 ? t('edit_modal.tag_search_placeholder') : '', className: "flex-1 min-w-[80px] text-sm outline-none bg-transparent px-1 py-0.5" })] }), open && filtered.length > 0 && (_jsx("div", { className: "absolute z-50 mt-1 w-full max-h-48 overflow-y-auto rounded-lg border bg-white shadow-lg", children: filtered.map(opt => (_jsxs("button", { type: "button", onClick: () => add_tag(opt.value), className: "w-full text-left px-3 py-2 text-sm hover:bg-violet-50 transition-colors flex items-center justify-between", children: [_jsx("span", { children: opt.label }), _jsx("span", { className: "text-xs text-gray-400 font-mono", children: opt.value })] }, opt.value))) })), open && filtered.length === 0 && search && (_jsx("div", { className: "absolute z-50 mt-1 w-full rounded-lg border bg-white shadow-lg px-3 py-2 text-sm text-gray-400", children: t('edit_modal.no_matching_options') }))] }));
|
|
155
162
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"empty_state.d.ts","sourceRoot":"","sources":["../../../../src/components/app_config_list_editor/components/empty_state.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"empty_state.d.ts","sourceRoot":"","sources":["../../../../src/components/app_config_list_editor/components/empty_state.tsx"],"names":[],"mappings":"AAOA,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"}
|
|
@@ -3,6 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
// Displayed when the list has no items
|
|
4
4
|
import { Inbox, Plus } from 'lucide-react';
|
|
5
5
|
import { cn } from 'hazo_ui';
|
|
6
|
+
import { t } from '../../../lib/strings.js';
|
|
6
7
|
export function EmptyState({ item_label, on_add, className, }) {
|
|
7
|
-
return (_jsxs("div", { className: cn('cls_empty_state flex flex-col items-center justify-center py-12 px-4', className), children: [_jsx("div", { className: "w-12 h-12 rounded-full bg-gray-100 flex items-center justify-center mb-3", children: _jsx(Inbox, { className: "w-6 h-6 text-gray-400" }) }),
|
|
8
|
+
return (_jsxs("div", { className: cn('cls_empty_state flex flex-col items-center justify-center py-12 px-4', className), children: [_jsx("div", { className: "w-12 h-12 rounded-full bg-gray-100 flex items-center justify-center mb-3", children: _jsx(Inbox, { className: "w-6 h-6 text-gray-400" }) }), _jsx("p", { className: "text-sm font-medium text-gray-900 mb-1", children: t('empty_state.title', { item_label }) }), _jsx("p", { className: "text-sm text-gray-500 mb-4", children: t('empty_state.description', { item: item_label.replace(/s$/, '') }) }), _jsxs("button", { type: "button", onClick: on_add, className: "inline-flex items-center gap-1.5 px-4 py-2 text-sm font-medium text-white bg-violet-600 hover:bg-violet-700 rounded-full transition-colors", children: [_jsx(Plus, { className: "w-4 h-4" }), t('empty_state.cta', { item: item_label.replace(/s$/, '') })] })] }));
|
|
8
9
|
}
|
|
@@ -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":"
|
|
1
|
+
{"version":3,"file":"import_export_buttons.d.ts","sourceRoot":"","sources":["../../../../src/components/app_config_list_editor/components/import_export_buttons.tsx"],"names":[],"mappings":"AASA,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"}
|
|
@@ -5,6 +5,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
5
5
|
import { useRef, useState, useCallback, useEffect } from 'react';
|
|
6
6
|
import { Download, Upload } from 'lucide-react';
|
|
7
7
|
import { cn } from 'hazo_ui';
|
|
8
|
+
import { t } from '../../../lib/strings.js';
|
|
8
9
|
import { validate_import_data, merge_items, export_items_to_json, generate_export_filename, } from '../utils/json_import_export.js';
|
|
9
10
|
export function ImportExportButtons({ items, on_items_change, columns, id_field, title, max_items, }) {
|
|
10
11
|
const file_input_ref = useRef(null);
|
|
@@ -35,7 +36,7 @@ export function ImportExportButtons({ items, on_items_change, columns, id_field,
|
|
|
35
36
|
reader.onload = (e) => {
|
|
36
37
|
const text = e.target?.result;
|
|
37
38
|
if (typeof text !== 'string') {
|
|
38
|
-
set_status_message({ text: '
|
|
39
|
+
set_status_message({ text: t('import_export.error_read_file'), is_error: true });
|
|
39
40
|
return;
|
|
40
41
|
}
|
|
41
42
|
let data;
|
|
@@ -43,12 +44,12 @@ export function ImportExportButtons({ items, on_items_change, columns, id_field,
|
|
|
43
44
|
data = JSON.parse(text);
|
|
44
45
|
}
|
|
45
46
|
catch {
|
|
46
|
-
set_status_message({ text: '
|
|
47
|
+
set_status_message({ text: t('import_export.error_invalid_json'), is_error: true });
|
|
47
48
|
return;
|
|
48
49
|
}
|
|
49
50
|
const { valid_items, errors } = validate_import_data(data, columns, id_field);
|
|
50
51
|
if (valid_items.length === 0) {
|
|
51
|
-
const msg = errors.length > 0 ? errors[0] : '
|
|
52
|
+
const msg = errors.length > 0 ? errors[0] : t('import_export.error_no_valid_items');
|
|
52
53
|
set_status_message({ text: msg, is_error: true });
|
|
53
54
|
return;
|
|
54
55
|
}
|
|
@@ -56,13 +57,13 @@ export function ImportExportButtons({ items, on_items_change, columns, id_field,
|
|
|
56
57
|
on_items_change(result.merged_items);
|
|
57
58
|
const parts = [];
|
|
58
59
|
if (result.added_count > 0) {
|
|
59
|
-
parts.push(
|
|
60
|
+
parts.push(t('import_export.added', { count: result.added_count }));
|
|
60
61
|
}
|
|
61
62
|
if (result.skipped_count > 0) {
|
|
62
|
-
parts.push(
|
|
63
|
+
parts.push(t('import_export.skipped', { count: result.skipped_count }));
|
|
63
64
|
}
|
|
64
65
|
if (errors.length > 0) {
|
|
65
|
-
parts.push(
|
|
66
|
+
parts.push(t('import_export.invalid', { count: errors.length }));
|
|
66
67
|
}
|
|
67
68
|
set_status_message({
|
|
68
69
|
text: parts.join(', '),
|
|
@@ -71,5 +72,5 @@ export function ImportExportButtons({ items, on_items_change, columns, id_field,
|
|
|
71
72
|
};
|
|
72
73
|
reader.readAsText(file);
|
|
73
74
|
}, [items, columns, id_field, max_items, on_items_change]);
|
|
74
|
-
return (_jsxs("div", { className: "cls_import_export flex items-center gap-1.5", children: [status_message && (_jsx("span", { className: cn('cls_import_status text-xs transition-opacity duration-300', status_message.is_error ? 'text-red-600' : 'text-green-600'), children: status_message.text })), _jsxs("button", { type: "button", onClick: handle_export, className: "inline-flex items-center gap-1 px-2 py-1.5 text-xs font-medium text-gray-500 hover:text-gray-700 hover:bg-gray-100 rounded-full transition-colors", title:
|
|
75
|
+
return (_jsxs("div", { className: "cls_import_export flex items-center gap-1.5", children: [status_message && (_jsx("span", { className: cn('cls_import_status text-xs transition-opacity duration-300', status_message.is_error ? 'text-red-600' : 'text-green-600'), children: status_message.text })), _jsxs("button", { type: "button", onClick: handle_export, className: "inline-flex items-center gap-1 px-2 py-1.5 text-xs font-medium text-gray-500 hover:text-gray-700 hover:bg-gray-100 rounded-full transition-colors", title: t('import_export.export_title'), children: [_jsx(Download, { className: "w-3.5 h-3.5" }), t('import_export.export')] }), _jsxs("button", { type: "button", onClick: handle_import_click, className: "inline-flex items-center gap-1 px-2 py-1.5 text-xs font-medium text-gray-500 hover:text-gray-700 hover:bg-gray-100 rounded-full transition-colors", title: t('import_export.import_title'), children: [_jsx(Upload, { className: "w-3.5 h-3.5" }), t('import_export.import')] }), _jsx("input", { ref: file_input_ref, type: "file", accept: ".json", onChange: handle_file_selected, className: "hidden" })] }));
|
|
75
76
|
}
|
|
@@ -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;
|
|
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;AAIzB,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,qBA0HrB"}
|