Demo DB: feature-rich seed (12 objects, 500-row audit log) + regenerate action in Settings
- Expand demo schema to exercise every SQLite-available feature: composite PK (order_items), self-referencing FK (categories), JSON columns (lowercase 'json' so the cell popover triggers), BLOBs (files), no-PK table (page_views, rowid editing), TEXT PK (app_settings), empty table (marketing_campaigns), read-only view (order_summary), CHECK/UNIQUE/defaults/indexes, smart-sort tiers, and a 500-row audit_log for pagination/virtualization/filter demos - Stamp PRAGMA user_version = 2; stale demo files auto-recreate on launch (schema is idempotent, guarded inserts) - Add regenerate_demo_db command: drops live pool handle, wipes + re-seeds the demo file; new Settings > General > Demo 'Regenerate demo' button with confirm dialog, loading state, toasts - Fix Re-add demo to use the app data dir (was temp dir) so both paths match startup; reload connections after re-add - Add 9 unit tests (demo.test.rs) covering objects, seed counts, JSON validity, FK consistency, view queryability, idempotency, constraint shapes, and stale/regenerate file flows
This commit is contained in:
@@ -1,11 +1,14 @@
|
||||
import { useSettingsStore } from "../../stores/settingsStore";
|
||||
import { useConnectionStore } from "../../stores/connectionStore";
|
||||
import { useNotificationStore } from "../../stores/notificationStore";
|
||||
import { Select } from "../ui/Select";
|
||||
import { ThemePicker } from "../ui/ThemePicker";
|
||||
import { AccentPicker } from "../ui/AccentPicker";
|
||||
import { SettingsRow } from "../ui/SettingsRow";
|
||||
import { ConfirmDialog } from "../ui/ConfirmDialog";
|
||||
import * as cmd from "../../lib/commands";
|
||||
import type { FontSize } from "../../lib/types";
|
||||
import { useState } from "react";
|
||||
|
||||
const FONT_SIZE_OPTIONS: { value: FontSize; label: string }[] = [
|
||||
{ value: "small", label: "Small" },
|
||||
@@ -32,6 +35,10 @@ const PAGE_SIZE_OPTIONS = [
|
||||
export function GeneralSettingsTab() {
|
||||
const { settings, updateSetting, load } = useSettingsStore();
|
||||
const folders = useConnectionStore((s) => s.folders);
|
||||
const loadAll = useConnectionStore((s) => s.loadAll);
|
||||
const notify = useNotificationStore((s) => s.notify);
|
||||
const [confirmRegenerate, setConfirmRegenerate] = useState(false);
|
||||
const [regenerating, setRegenerating] = useState(false);
|
||||
|
||||
if (!settings) return null;
|
||||
|
||||
@@ -42,10 +49,26 @@ export function GeneralSettingsTab() {
|
||||
|
||||
const handleReAddDemo = async () => {
|
||||
try {
|
||||
await cmd.recreateDemoDb();
|
||||
const msg = await cmd.recreateDemoDb();
|
||||
await load();
|
||||
await loadAll();
|
||||
notify(msg, "success");
|
||||
} catch (e) {
|
||||
// ignore
|
||||
notify(e instanceof Error ? e.message : String(e), "error");
|
||||
}
|
||||
};
|
||||
|
||||
const handleRegenerateDemo = async () => {
|
||||
setConfirmRegenerate(false);
|
||||
setRegenerating(true);
|
||||
try {
|
||||
const msg = await cmd.regenerateDemoDb();
|
||||
await loadAll();
|
||||
notify(msg, "success");
|
||||
} catch (e) {
|
||||
notify(e instanceof Error ? e.message : String(e), "error");
|
||||
} finally {
|
||||
setRegenerating(false);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -140,7 +163,28 @@ export function GeneralSettingsTab() {
|
||||
Re-add demo
|
||||
</button>
|
||||
</SettingsRow>
|
||||
<SettingsRow
|
||||
title="Regenerate demo database"
|
||||
description="Reset the demo to its original state. Any edits or changes you made against the demo are lost."
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setConfirmRegenerate(true)}
|
||||
disabled={regenerating}
|
||||
className="rounded-lg border border-red-500/30 px-3 py-1.5 text-sm text-red-300 hover:bg-red-500/10 hover:text-red-200 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
|
||||
>
|
||||
{regenerating ? "Regenerating…" : "Regenerate demo"}
|
||||
</button>
|
||||
</SettingsRow>
|
||||
</div>
|
||||
<ConfirmDialog
|
||||
open={confirmRegenerate}
|
||||
title="Regenerate demo database?"
|
||||
message="This deletes the current demo file and re-seeds it with fresh data. Any edits or changes you made against the demo will be lost."
|
||||
confirmLabel="Regenerate"
|
||||
onConfirm={handleRegenerateDemo}
|
||||
onCancel={() => setConfirmRegenerate(false)}
|
||||
/>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -76,6 +76,10 @@ export async function recreateDemoDb(): Promise<string> {
|
||||
return invoke<string>("recreate_demo_db");
|
||||
}
|
||||
|
||||
export async function regenerateDemoDb(): Promise<string> {
|
||||
return invoke<string>("regenerate_demo_db");
|
||||
}
|
||||
|
||||
// ─── DB Viewer Lifecycle ────────────────────────────────────────
|
||||
|
||||
export async function dbConnect(connectionId: string, input: ConnectionInput): Promise<void> {
|
||||
|
||||
Reference in New Issue
Block a user