feat: add AI data validation, test data generator, index advisor, and snapshots
Four new killer features leveraging AI (Ollama) and PostgreSQL internals: - Data Validation: describe quality rules in natural language, AI generates SQL to find violations, run with pass/fail results and sample violations - Test Data Generator: right-click table to generate realistic FK-aware test data with AI, preview before inserting in a transaction - Index Advisor: analyze pg_stat tables + AI recommendations for CREATE/DROP INDEX with one-click apply - Data Snapshots: export selected tables to JSON (FK-ordered), restore from file with optional truncate in a transaction Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
131
src/hooks/use-snapshots.ts
Normal file
131
src/hooks/use-snapshots.ts
Normal file
@@ -0,0 +1,131 @@
|
||||
import { useState, useEffect, useCallback, useRef } from "react";
|
||||
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import {
|
||||
createSnapshot,
|
||||
restoreSnapshot,
|
||||
listSnapshots,
|
||||
readSnapshotMetadata,
|
||||
onSnapshotProgress,
|
||||
} from "@/lib/tauri";
|
||||
import type {
|
||||
CreateSnapshotParams,
|
||||
RestoreSnapshotParams,
|
||||
SnapshotProgress,
|
||||
SnapshotMetadata,
|
||||
} from "@/types";
|
||||
|
||||
export function useListSnapshots() {
|
||||
return useQuery({
|
||||
queryKey: ["snapshots"],
|
||||
queryFn: listSnapshots,
|
||||
staleTime: 30_000,
|
||||
});
|
||||
}
|
||||
|
||||
export function useReadSnapshotMetadata() {
|
||||
return useMutation({
|
||||
mutationFn: (filePath: string) => readSnapshotMetadata(filePath),
|
||||
});
|
||||
}
|
||||
|
||||
export function useCreateSnapshot() {
|
||||
const [progress, setProgress] = useState<SnapshotProgress | null>(null);
|
||||
const snapshotIdRef = useRef<string>("");
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const mutation = useMutation({
|
||||
mutationFn: ({
|
||||
params,
|
||||
snapshotId,
|
||||
filePath,
|
||||
}: {
|
||||
params: CreateSnapshotParams;
|
||||
snapshotId: string;
|
||||
filePath: string;
|
||||
}) => {
|
||||
snapshotIdRef.current = snapshotId;
|
||||
setProgress(null);
|
||||
return createSnapshot(params, snapshotId, filePath);
|
||||
},
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ["snapshots"] });
|
||||
},
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const unlistenPromise = onSnapshotProgress((p) => {
|
||||
if (p.snapshot_id === snapshotIdRef.current) {
|
||||
setProgress(p);
|
||||
}
|
||||
});
|
||||
return () => {
|
||||
unlistenPromise.then((unlisten) => unlisten());
|
||||
};
|
||||
}, []);
|
||||
|
||||
const mutationRef = useRef(mutation);
|
||||
mutationRef.current = mutation;
|
||||
|
||||
const reset = useCallback(() => {
|
||||
mutationRef.current.reset();
|
||||
setProgress(null);
|
||||
snapshotIdRef.current = "";
|
||||
}, []);
|
||||
|
||||
return {
|
||||
create: mutation.mutate,
|
||||
result: mutation.data as SnapshotMetadata | undefined,
|
||||
error: mutation.error ? String(mutation.error) : null,
|
||||
isCreating: mutation.isPending,
|
||||
progress,
|
||||
reset,
|
||||
};
|
||||
}
|
||||
|
||||
export function useRestoreSnapshot() {
|
||||
const [progress, setProgress] = useState<SnapshotProgress | null>(null);
|
||||
const snapshotIdRef = useRef<string>("");
|
||||
|
||||
const mutation = useMutation({
|
||||
mutationFn: ({
|
||||
params,
|
||||
snapshotId,
|
||||
}: {
|
||||
params: RestoreSnapshotParams;
|
||||
snapshotId: string;
|
||||
}) => {
|
||||
snapshotIdRef.current = snapshotId;
|
||||
setProgress(null);
|
||||
return restoreSnapshot(params, snapshotId);
|
||||
},
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const unlistenPromise = onSnapshotProgress((p) => {
|
||||
if (p.snapshot_id === snapshotIdRef.current) {
|
||||
setProgress(p);
|
||||
}
|
||||
});
|
||||
return () => {
|
||||
unlistenPromise.then((unlisten) => unlisten());
|
||||
};
|
||||
}, []);
|
||||
|
||||
const mutationRef = useRef(mutation);
|
||||
mutationRef.current = mutation;
|
||||
|
||||
const reset = useCallback(() => {
|
||||
mutationRef.current.reset();
|
||||
setProgress(null);
|
||||
snapshotIdRef.current = "";
|
||||
}, []);
|
||||
|
||||
return {
|
||||
restore: mutation.mutate,
|
||||
rowsRestored: mutation.data as number | undefined,
|
||||
error: mutation.error ? String(mutation.error) : null,
|
||||
isRestoring: mutation.isPending,
|
||||
progress,
|
||||
reset,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user