Refactor: TanStack-Query-Daten-Layer + useDialog (Phase 3b)
- @tanstack/react-query (v5) als zentraler Daten-Layer; QueryClientProvider in main.tsx (retry 1, kein refetchOnWindowFocus, staleTime 5s). - lib/queries.ts: Domänen-Hooks (useHealth/useSystemStatus/useServices/ useModels/useRouting/useJobs/useTokenStats/useAgentStatus/useUpdates/ useDiscover/useConnect/useMemory) + zentrale Query-Keys (qk) + invalidate. Gleicher Key = eine Anfrage über alle Views (Dedup), einheitliches Polling. - lib/useDialog.tsx: ein Hook für Alert/Confirm/Prompt statt 5x dupliziertem Dialog-State + showAlert/showConfirm. - api.ts: TokenStats + ModelsResp typisiert. - Migriert auf Hooks/useDialog: App, SystemView, AgentView, ConnectView, MemoryView (manuelles useEffect+setInterval entfernt; Mutationen invalidieren gezielt die Query-Keys). Verifiziert: tsc grün, Build grün, alle migrierten Views ohne Konsolenfehler, Live-Daten (CPU/RAM/Dienste) rendern korrekt. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1,8 +1,9 @@
|
||||
import { useEffect, useState } from "react"
|
||||
import { useState } from "react"
|
||||
import { Trash2, Sparkles, User, Scroll, Shield, Tag, Clock, Search, Plus, BookOpen } from "lucide-react"
|
||||
import { api, type DedupeResult, type Memory } from "@/lib/api"
|
||||
import { api, type DedupeResult } from "@/lib/api"
|
||||
import { useMemory, useQueryClient } from "@/lib/queries"
|
||||
import { useDialog } from "@/lib/useDialog"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { CustomDialog } from "@/components/CustomDialog"
|
||||
|
||||
|
||||
const CATEGORIES = ["user", "instruction", "stable", "versioned", "ephemeral"]
|
||||
@@ -26,73 +27,32 @@ const BORDER_CLASSES: Record<string, string> = {
|
||||
}
|
||||
|
||||
export function MemoryView() {
|
||||
const [items, setItems] = useState<Memory[]>([])
|
||||
const [filter, setFilter] = useState("")
|
||||
const [q, setQ] = useState("")
|
||||
const [content, setContent] = useState("")
|
||||
const [category, setCategory] = useState("stable")
|
||||
const [error, setError] = useState("")
|
||||
const [deduping, setDeduping] = useState(false)
|
||||
|
||||
// Custom Dialog State
|
||||
const [dialog, setDialog] = useState<{
|
||||
type: "alert" | "confirm"
|
||||
title: string
|
||||
message: string
|
||||
onConfirm: () => void
|
||||
onCancel?: () => void
|
||||
} | null>(null)
|
||||
const qc = useQueryClient()
|
||||
const { showAlert, showConfirm, dialogElement } = useDialog()
|
||||
const { data: items = [], error: itemsErr } = useMemory({ q, category: filter })
|
||||
const error = itemsErr ? String(itemsErr) : ""
|
||||
|
||||
function showAlert(title: string, message: string, onConfirm?: () => void) {
|
||||
setDialog({
|
||||
type: "alert",
|
||||
title,
|
||||
message,
|
||||
onConfirm: () => {
|
||||
setDialog(null)
|
||||
if (onConfirm) onConfirm()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function showConfirm(title: string, message: string, onConfirm: () => void) {
|
||||
setDialog({
|
||||
type: "confirm",
|
||||
title,
|
||||
message,
|
||||
onConfirm: () => {
|
||||
setDialog(null)
|
||||
onConfirm()
|
||||
},
|
||||
onCancel: () => setDialog(null)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
function load() {
|
||||
const params = new URLSearchParams()
|
||||
if (q) params.set("q", q)
|
||||
if (filter) params.set("category", filter)
|
||||
api<Memory[]>(`/api/memory?${params}`)
|
||||
.then(setItems)
|
||||
.catch((e) => setError(String(e)))
|
||||
}
|
||||
|
||||
useEffect(load, [q, filter])
|
||||
const reloadMemory = () => qc.invalidateQueries({ queryKey: ["memory"] })
|
||||
|
||||
async function add() {
|
||||
if (!content.trim()) return
|
||||
await api("/api/memory", {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ content, category, source: "ui" })
|
||||
await api("/api/memory", {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ content, category, source: "ui" })
|
||||
})
|
||||
setContent("")
|
||||
load()
|
||||
reloadMemory()
|
||||
}
|
||||
|
||||
async function del(id: string) {
|
||||
await api(`/api/memory/${id}`, { method: "DELETE" })
|
||||
load()
|
||||
reloadMemory()
|
||||
}
|
||||
|
||||
async function cleanup() {
|
||||
@@ -111,11 +71,11 @@ export function MemoryView() {
|
||||
`${dry.duplicate_count} Dublette(n) in ${dry.groups.length} Gruppe(n) gefunden. Entfernen?`,
|
||||
async () => {
|
||||
try {
|
||||
await api("/api/memory/dedupe", {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ apply: true })
|
||||
await api("/api/memory/dedupe", {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ apply: true })
|
||||
})
|
||||
load()
|
||||
reloadMemory()
|
||||
} catch (e: any) {
|
||||
showAlert("Fehler", `Fehler beim Löschen: ${e.message}`)
|
||||
}
|
||||
@@ -285,15 +245,7 @@ export function MemoryView() {
|
||||
)}
|
||||
</div>
|
||||
|
||||
{dialog && (
|
||||
<CustomDialog
|
||||
type={dialog.type}
|
||||
title={dialog.title}
|
||||
message={dialog.message}
|
||||
onConfirm={dialog.onConfirm}
|
||||
onCancel={dialog.onCancel}
|
||||
/>
|
||||
)}
|
||||
{dialogElement}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user