perf: fix N+1 query pattern across all modules + rack numbering
CRITICAL PERF BUG: Every hook did storage.list() (1 HTTP call fetching ALL items with values, discarding values, returning only keys) then storage.get() for EACH key (N individual HTTP calls re-fetching values one by one). With 6 entries + contacts + tags, Registratura page fired ~40 sequential HTTP requests on load, where 3 would suffice. Fix: Replace list()+N*get() with single exportAll() call in ALL hooks: - registratura/registry-service.ts (added exportAll to RegistryStorage interface) - address-book/use-contacts.ts - it-inventory/use-inventory.ts - password-vault/use-vault.ts - word-templates/use-templates.ts - prompt-generator/use-prompt-generator.ts - hot-desk/use-reservations.ts - email-signature/use-saved-signatures.ts - digital-signatures/use-signatures.ts - ai-chat/use-chat.ts - core/tagging/tag-service.ts (uses storage.export()) Additional fixes: - registratura/use-registry.ts: addEntry uses optimistic local state update instead of double-refresh; closeEntry batches saves with Promise.all + single refresh - server-rack.tsx: reversed slot rendering so U1 is at bottom (standard rack numbering, per user's physical rack) Performance impact: ~90% reduction in HTTP requests on page load for all modules
This commit is contained in:
@@ -1,14 +1,14 @@
|
||||
'use client';
|
||||
"use client";
|
||||
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { useStorage } from '@/core/storage';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
import type { ChatMessage, ChatSession } from '../types';
|
||||
import { useState, useEffect, useCallback } from "react";
|
||||
import { useStorage } from "@/core/storage";
|
||||
import { v4 as uuid } from "uuid";
|
||||
import type { ChatMessage, ChatSession } from "../types";
|
||||
|
||||
const SESSION_PREFIX = 'session:';
|
||||
const SESSION_PREFIX = "session:";
|
||||
|
||||
export function useChat() {
|
||||
const storage = useStorage('ai-chat');
|
||||
const storage = useStorage("ai-chat");
|
||||
const [sessions, setSessions] = useState<ChatSession[]>([]);
|
||||
const [activeSessionId, setActiveSessionId] = useState<string | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
@@ -17,12 +17,11 @@ export function useChat() {
|
||||
|
||||
const refresh = useCallback(async () => {
|
||||
setLoading(true);
|
||||
const keys = await storage.list();
|
||||
const all = await storage.exportAll();
|
||||
const results: ChatSession[] = [];
|
||||
for (const key of keys) {
|
||||
if (key.startsWith(SESSION_PREFIX)) {
|
||||
const session = await storage.get<ChatSession>(key);
|
||||
if (session) results.push(session);
|
||||
for (const [key, value] of Object.entries(all)) {
|
||||
if (key.startsWith(SESSION_PREFIX) && value) {
|
||||
results.push(value as ChatSession);
|
||||
}
|
||||
}
|
||||
results.sort((a, b) => b.createdAt.localeCompare(a.createdAt));
|
||||
@@ -31,49 +30,65 @@ export function useChat() {
|
||||
}, [storage]);
|
||||
|
||||
// eslint-disable-next-line react-hooks/set-state-in-effect
|
||||
useEffect(() => { refresh(); }, [refresh]);
|
||||
useEffect(() => {
|
||||
refresh();
|
||||
}, [refresh]);
|
||||
|
||||
const createSession = useCallback(async (title?: string) => {
|
||||
const session: ChatSession = {
|
||||
id: uuid(),
|
||||
title: title || `Conversație ${new Date().toLocaleDateString('ro-RO')}`,
|
||||
messages: [],
|
||||
createdAt: new Date().toISOString(),
|
||||
};
|
||||
await storage.set(`${SESSION_PREFIX}${session.id}`, session);
|
||||
setSessions((prev) => [session, ...prev]);
|
||||
setActiveSessionId(session.id);
|
||||
return session;
|
||||
}, [storage]);
|
||||
const createSession = useCallback(
|
||||
async (title?: string) => {
|
||||
const session: ChatSession = {
|
||||
id: uuid(),
|
||||
title: title || `Conversație ${new Date().toLocaleDateString("ro-RO")}`,
|
||||
messages: [],
|
||||
createdAt: new Date().toISOString(),
|
||||
};
|
||||
await storage.set(`${SESSION_PREFIX}${session.id}`, session);
|
||||
setSessions((prev) => [session, ...prev]);
|
||||
setActiveSessionId(session.id);
|
||||
return session;
|
||||
},
|
||||
[storage],
|
||||
);
|
||||
|
||||
const addMessage = useCallback(async (content: string, role: ChatMessage['role']) => {
|
||||
if (!activeSessionId) return;
|
||||
const message: ChatMessage = {
|
||||
id: uuid(),
|
||||
role,
|
||||
content,
|
||||
timestamp: new Date().toISOString(),
|
||||
};
|
||||
setSessions((prev) => prev.map((s) => {
|
||||
if (s.id !== activeSessionId) return s;
|
||||
return { ...s, messages: [...s.messages, message] };
|
||||
}));
|
||||
// Persist
|
||||
const current = sessions.find((s) => s.id === activeSessionId);
|
||||
if (current) {
|
||||
const updated = { ...current, messages: [...current.messages, message] };
|
||||
await storage.set(`${SESSION_PREFIX}${activeSessionId}`, updated);
|
||||
}
|
||||
return message;
|
||||
}, [storage, activeSessionId, sessions]);
|
||||
const addMessage = useCallback(
|
||||
async (content: string, role: ChatMessage["role"]) => {
|
||||
if (!activeSessionId) return;
|
||||
const message: ChatMessage = {
|
||||
id: uuid(),
|
||||
role,
|
||||
content,
|
||||
timestamp: new Date().toISOString(),
|
||||
};
|
||||
setSessions((prev) =>
|
||||
prev.map((s) => {
|
||||
if (s.id !== activeSessionId) return s;
|
||||
return { ...s, messages: [...s.messages, message] };
|
||||
}),
|
||||
);
|
||||
// Persist
|
||||
const current = sessions.find((s) => s.id === activeSessionId);
|
||||
if (current) {
|
||||
const updated = {
|
||||
...current,
|
||||
messages: [...current.messages, message],
|
||||
};
|
||||
await storage.set(`${SESSION_PREFIX}${activeSessionId}`, updated);
|
||||
}
|
||||
return message;
|
||||
},
|
||||
[storage, activeSessionId, sessions],
|
||||
);
|
||||
|
||||
const deleteSession = useCallback(async (id: string) => {
|
||||
await storage.delete(`${SESSION_PREFIX}${id}`);
|
||||
setSessions((prev) => prev.filter((s) => s.id !== id));
|
||||
if (activeSessionId === id) {
|
||||
setActiveSessionId(null);
|
||||
}
|
||||
}, [storage, activeSessionId]);
|
||||
const deleteSession = useCallback(
|
||||
async (id: string) => {
|
||||
await storage.delete(`${SESSION_PREFIX}${id}`);
|
||||
setSessions((prev) => prev.filter((s) => s.id !== id));
|
||||
if (activeSessionId === id) {
|
||||
setActiveSessionId(null);
|
||||
}
|
||||
},
|
||||
[storage, activeSessionId],
|
||||
);
|
||||
|
||||
const selectSession = useCallback((id: string) => {
|
||||
setActiveSessionId(id);
|
||||
|
||||
Reference in New Issue
Block a user