From 752504e1732774cfda3389a1d3d6ec2698be067b Mon Sep 17 00:00:00 2001 From: Scott Idem Date: Wed, 17 Jun 2026 14:11:19 -0400 Subject: [PATCH] feat(data-stores): bulk rename, wildcard hint, theme fixes - Add Bulk Rename panel: code filter (% wildcard), find/replace text, preview table before apply, sequential PATCH with IDB cache update - Fix Code filter label/placeholder to show % wildcard syntax - Add note that API results are scoped to the active account (backend behavior) - Replace bg-black/5 with bg-surface-500/10 for light/dark compatibility Co-Authored-By: Claude Sonnet 4.6 --- src/routes/core/data_stores/+page.svelte | 208 +++++++++++++++++++++-- 1 file changed, 196 insertions(+), 12 deletions(-) diff --git a/src/routes/core/data_stores/+page.svelte b/src/routes/core/data_stores/+page.svelte index 5cffd983..f6b797d8 100644 --- a/src/routes/core/data_stores/+page.svelte +++ b/src/routes/core/data_stores/+page.svelte @@ -4,6 +4,7 @@ import { untrack } from 'svelte'; import { goto } from '$app/navigation'; import { Modal } from 'flowbite-svelte'; import { + ArrowRight, Check, ChevronLeft, ChevronRight, @@ -12,6 +13,7 @@ import { Eye, Filter, LoaderCircle, + Pencil, Plus, RefreshCw, Save, @@ -69,6 +71,16 @@ let draft_notes = $state(''); let html_edit_mode = $state<'source' | 'visual'>('source'); let submit_status = $state<'idle' | 'processing' | 'saved' | 'error'>('idle'); +// ── Bulk rename state ───────────────────────────────────────────────────────── +let show_bulk_rename = $state(false); +let rename_filter = $state(''); // code LIKE filter — use % as wildcard +let rename_find_text = $state(''); // literal substring to find within matching codes +let rename_replace_text = $state(''); // literal replacement for find_text +let rename_preview: { id: string; old_code: string; new_code: string }[] = $state([]); +let rename_loading = $state(false); +let rename_apply_status = $state<'idle' | 'applying' | 'done' | 'error'>('idle'); +let rename_applied = $state(0); + // ── Search ──────────────────────────────────────────────────────────────────── async function do_search(reset = true) { if (reset) page_offset = 0; @@ -258,6 +270,67 @@ async function handle_delete() { } } +// ── Bulk rename ─────────────────────────────────────────────────────────────── +async function do_rename_preview() { + if (!rename_filter.trim()) return; + rename_loading = true; + rename_preview = []; + rename_apply_status = 'idle'; + rename_applied = 0; + + const result_li = await api.search_ae_obj({ + api_cfg: $ae_api, + obj_type: 'data_store', + search_query: { and: [{ field: 'code', op: 'like', value: rename_filter.trim() }] }, + order_by_li: [{ code: 'ASC' }], + limit: 200, + offset: 0, + log_lvl: 0 + }); + + if (result_li) { + const find = rename_find_text.trim(); + const repl = rename_replace_text.trim(); + rename_preview = result_li + .map((ds: ae_DataStore) => { + const old_code = ds.code ?? ''; + const new_code = find ? old_code.split(find).join(repl) : old_code; + return { id: (ds.id ?? (ds as any).data_store_id) as string, old_code, new_code }; + }) + .filter((r: { id: string; old_code: string; new_code: string }) => r.old_code !== r.new_code); + } + + rename_loading = false; +} + +async function do_rename_apply() { + if (!rename_preview.length) return; + if (!confirm(`Rename ${rename_preview.length} data store code(s)?\n\nThis cannot be undone.`)) return; + + rename_apply_status = 'applying'; + rename_applied = 0; + const api_cfg = untrack(() => $ae_api); + + for (const item of rename_preview) { + const result = await api.update_ae_obj({ + api_cfg, + obj_type: 'data_store', + obj_id: item.id, + fields: { code: item.new_code } + }); + if (result) { + rename_applied++; + await db_core.data_store.update(item.id, { code: item.new_code }); + } + } + + rename_apply_status = rename_applied === rename_preview.length ? 'done' : 'error'; + if (rename_apply_status === 'done') { + rename_preview = []; + do_search(false); + } +} + // ── Helpers ─────────────────────────────────────────────────────────────────── function type_badge(type: string | null | undefined) { switch (type) { @@ -293,13 +366,22 @@ function content_preview(ds: ae_DataStore): string {

Content & Configuration Storage

- +
+ + +
@@ -310,18 +392,21 @@ function content_preview(ds: ae_DataStore): string {
- + e.key === 'Enter' && do_search()} />
e.key === 'Enter' && do_search()} /> +

Results are scoped to the active account by the API.

@@ -392,6 +478,104 @@ function content_preview(ds: ae_DataStore): string {
+ + {#if show_bulk_rename} +
+
+ Bulk Rename Codes +
+ +
+
+ + e.key === 'Enter' && do_rename_preview()} /> +
+
+ + +
+
+ + +
+
+ +
+ + {#if rename_preview.length > 0} + + {/if} + {#if rename_apply_status === 'error'} + Applied {rename_applied}/{rename_preview.length} — check console for errors. + {/if} +
+ + {#if rename_preview.length > 0} +
+ + + + + + + + + + {#each rename_preview as item (item.id)} + + + + + + {/each} + +
Current CodeNew Code
{item.old_code}{item.new_code}
+
+ {:else if !rename_loading && rename_filter.trim()} +

No changes to preview — either no records matched or find/replace text produced no differences.

+ {/if} +
+ {/if} + {#if results.length > 0}
@@ -611,7 +795,7 @@ function content_preview(ds: ae_DataStore): string {
-
+