Skip to content

Commit a2076d4

Browse files
committed
refactor(components): lift SettingsTab init into useSettingsInit hook
Move the cross-section initial data load (Promise.all of 12 hook fetches), the audit loader, and the MemoryKernel preflight refresh into useSettingsInit. Shell still calls the 10 data hooks and orchestrates handlers; the new hook owns the init-driven state plus the effects that populate it on mount.
1 parent 570f565 commit a2076d4

2 files changed

Lines changed: 343 additions & 136 deletions

File tree

src/components/Settings/SettingsTab.tsx

Lines changed: 58 additions & 136 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import appPackage from "../../../package.json";
44
import { useTheme } from "../../contexts/ThemeContext";
55
import { useToastContext } from "../../contexts/ToastContext";
66
import {
7-
getResponseQualityThresholds,
87
type ResponseQualityThresholds,
98
resetResponseQualityThresholds,
109
saveResponseQualityThresholds,
@@ -18,13 +17,6 @@ import { useKb } from "../../hooks/useKb";
1817
import { useLlm } from "../../hooks/useLlm";
1918
import { useSearchApiEmbedding } from "../../hooks/useSearchApiEmbedding";
2019
import { useSettingsOps } from "../../hooks/useSettingsOps";
21-
import type { ModelInfo } from "../../types/llm";
22-
import type {
23-
AuditEntry,
24-
DeploymentHealthSummary,
25-
IntegrationConfigRecord,
26-
MemoryKernelPreflightStatus,
27-
} from "../../types/settings";
2820
import {
2921
formatAuditEvent,
3022
formatBytes,
@@ -33,6 +25,7 @@ import {
3325
getSearchApiEmbeddingBadge,
3426
validateQualityThresholds,
3527
} from "./SettingsTab.helpers";
28+
import { useSettingsInit } from "./useSettingsInit";
3629
import { AdvancedSearchSection } from "./sections/AdvancedSearchSection";
3730
import { ContextWindowSection } from "./sections/ContextWindowSection";
3831
import { JiraSection } from "./sections/JiraSection";
@@ -131,87 +124,81 @@ export function SettingsTab() {
131124
deleteVariable,
132125
} = useCustomVariables();
133126

134-
const [loadedModel, setLoadedModel] = useState<string | null>(null);
135-
const [loadedModelInfo, setLoadedModelInfo] = useState<ModelInfo | null>(
136-
null,
137-
);
138-
const [downloadedModels, setDownloadedModels] = useState<string[]>([]);
139-
const [kbFolder, setKbFolderState] = useState<string | null>(null);
140-
const [indexStats, setIndexStats] = useState<{
141-
total_chunks: number;
142-
total_files: number;
143-
} | null>(null);
144-
const [vectorEnabled, setVectorEnabled] = useState(false);
145-
const [jiraConfigured, setJiraConfigured] = useState(false);
146-
const [contextWindowSize, setContextWindowSize] = useState<number | null>(
147-
null,
148-
);
149-
const [embeddingDownloaded, setEmbeddingDownloaded] = useState(false);
150-
const [allowUnverifiedLocalModels, setAllowUnverifiedLocalModels] =
151-
useState(false);
127+
const init = useSettingsInit({
128+
getLoadedModel,
129+
getModelInfo,
130+
listModels,
131+
getKbFolder,
132+
getIndexStats,
133+
getVectorConsent,
134+
getContextWindow,
135+
checkJiraConfig,
136+
isEmbeddingDownloaded,
137+
getDeploymentHealthSummary,
138+
listIntegrations,
139+
checkEmbeddingStatus,
140+
refreshSearchApiEmbeddingStatus,
141+
onShowError: showError,
142+
});
143+
144+
const {
145+
loadedModel,
146+
setLoadedModel,
147+
loadedModelInfo,
148+
setLoadedModelInfo,
149+
downloadedModels,
150+
setDownloadedModels,
151+
kbFolder,
152+
setKbFolder: setKbFolderState,
153+
indexStats,
154+
setIndexStats,
155+
vectorEnabled,
156+
setVectorEnabled,
157+
jiraConfigured,
158+
setJiraConfigured,
159+
contextWindowSize,
160+
setContextWindowSize,
161+
embeddingDownloaded,
162+
setEmbeddingDownloaded,
163+
allowUnverifiedLocalModels,
164+
setAllowUnverifiedLocalModels,
165+
deploymentHealth,
166+
setDeploymentHealth,
167+
integrations,
168+
setIntegrations,
169+
qualityThresholds,
170+
setQualityThresholds,
171+
memoryKernelPreflight,
172+
memoryKernelLoading,
173+
auditEntries,
174+
auditLoading,
175+
auditPage,
176+
setAuditPage,
177+
loadInitialState,
178+
loadAuditEntries,
179+
refreshMemoryKernelStatus,
180+
} = init;
181+
152182
const [generatingEmbeddings, setGeneratingEmbeddings] = useState(false);
153183
const [loading, setLoading] = useState<string | null>(null);
154184
const [error, setError] = useState<string | null>(null);
155185
const [backupLoading, setBackupLoading] = useState<
156186
"export" | "import" | null
157187
>(null);
158-
const [auditEntries, setAuditEntries] = useState<AuditEntry[]>([]);
159-
const [auditLoading, setAuditLoading] = useState(false);
160188
const [auditExporting, setAuditExporting] = useState(false);
161189
const [auditSeverityFilter, setAuditSeverityFilter] = useState<
162190
"all" | "info" | "warning" | "error" | "critical"
163191
>("all");
164192
const [auditSearchQuery, setAuditSearchQuery] = useState("");
165-
const [auditPage, setAuditPage] = useState(1);
166-
167-
const [deploymentHealth, setDeploymentHealth] =
168-
useState<DeploymentHealthSummary | null>(null);
169193
const [deployPreflightChecks, setDeployPreflightChecks] = useState<string[]>(
170194
[],
171195
);
172196
const [deployPreflightRunning, setDeployPreflightRunning] = useState(false);
173-
const [integrations, setIntegrations] = useState<IntegrationConfigRecord[]>(
174-
[],
175-
);
176-
const [qualityThresholds, setQualityThresholds] =
177-
useState<ResponseQualityThresholds>(() => getResponseQualityThresholds());
178197
const [qualityThresholdError, setQualityThresholdError] = useState<
179198
string | null
180199
>(null);
181-
const [memoryKernelPreflight, setMemoryKernelPreflight] =
182-
useState<MemoryKernelPreflightStatus | null>(null);
183-
const [memoryKernelLoading, setMemoryKernelLoading] = useState(false);
184200
const revampFlags = useMemo(() => resolveRevampFlags(), []);
185201

186-
const loadAuditEntries = useCallback(async () => {
187-
setAuditLoading(true);
188-
try {
189-
const entries = await invoke<AuditEntry[]>("get_audit_entries", {
190-
limit: 200,
191-
});
192-
setAuditEntries(entries ?? []);
193-
setAuditPage(1);
194-
} catch (err) {
195-
showError(`Failed to load audit logs: ${err}`);
196-
} finally {
197-
setAuditLoading(false);
198-
}
199-
}, [showError]);
200-
201-
const refreshMemoryKernelStatus = useCallback(async () => {
202-
setMemoryKernelLoading(true);
203-
try {
204-
const status = await invoke<MemoryKernelPreflightStatus>(
205-
"get_memory_kernel_preflight_status",
206-
);
207-
setMemoryKernelPreflight(status);
208-
} catch {
209-
setMemoryKernelPreflight(null);
210-
} finally {
211-
setMemoryKernelLoading(false);
212-
}
213-
}, []);
214-
215202
const filteredAuditEntries = useMemo(() => {
216203
const normalized = auditEntries.slice().reverse();
217204
const query = auditSearchQuery.trim().toLowerCase();
@@ -239,83 +226,18 @@ export function SettingsTab() {
239226

240227
useEffect(() => {
241228
setAuditPage((prev) => Math.min(prev, auditTotalPages));
242-
}, [auditTotalPages]);
243-
244-
useEffect(() => {
245-
refreshMemoryKernelStatus();
246-
}, [refreshMemoryKernelStatus]);
229+
}, [auditTotalPages, setAuditPage]);
247230

248231
const pagedAuditEntries = useMemo(() => {
249232
const start = (auditPage - 1) * AUDIT_PAGE_SIZE;
250233
return filteredAuditEntries.slice(start, start + AUDIT_PAGE_SIZE);
251234
}, [filteredAuditEntries, auditPage]);
252235

253236
useEffect(() => {
254-
Promise.resolve(loadInitialState()).catch((err) =>
255-
console.error("Settings init failed:", err),
256-
);
257237
Promise.resolve(loadVariables()).catch((err) =>
258238
console.error("Variables load failed:", err),
259239
);
260-
Promise.resolve(loadAuditEntries()).catch((err) =>
261-
console.error("Audit load failed:", err),
262-
);
263-
// eslint-disable-next-line react-hooks/exhaustive-deps
264-
}, [loadVariables, loadAuditEntries]);
265-
266-
async function loadInitialState() {
267-
try {
268-
const [
269-
loaded,
270-
modelInfo,
271-
downloaded,
272-
folder,
273-
stats,
274-
consent,
275-
jiraConfigResult,
276-
ctxWindow,
277-
embDownloaded,
278-
allowUnverifiedModels,
279-
deployHealth,
280-
integrationsList,
281-
] = await Promise.all([
282-
getLoadedModel(),
283-
getModelInfo().catch(() => null),
284-
listModels(),
285-
getKbFolder(),
286-
getIndexStats().catch(() => null),
287-
getVectorConsent().catch(() => null),
288-
checkJiraConfig().catch(() => false),
289-
getContextWindow().catch(() => null),
290-
isEmbeddingDownloaded().catch(() => false),
291-
invoke<boolean>("get_allow_unverified_local_models").catch(() => false),
292-
getDeploymentHealthSummary().catch(() => null),
293-
listIntegrations().catch(() => []),
294-
]);
295-
setLoadedModel(loaded);
296-
setLoadedModelInfo(modelInfo);
297-
setDownloadedModels(downloaded);
298-
setKbFolderState(folder);
299-
setIndexStats(stats);
300-
if (consent) {
301-
setVectorEnabled(consent.enabled);
302-
}
303-
setJiraConfigured(jiraConfigResult);
304-
setContextWindowSize(ctxWindow);
305-
setEmbeddingDownloaded(embDownloaded);
306-
setAllowUnverifiedLocalModels(allowUnverifiedModels);
307-
setDeploymentHealth(deployHealth);
308-
setIntegrations(integrationsList ?? []);
309-
setQualityThresholds(getResponseQualityThresholds());
310-
311-
await Promise.all([
312-
checkEmbeddingStatus(),
313-
refreshSearchApiEmbeddingStatus(),
314-
]);
315-
} catch (err) {
316-
console.error("Failed to load settings state:", err);
317-
}
318-
}
240+
}, [loadVariables]);
319241

320242
async function handleVectorToggle() {
321243
const newValue = !vectorEnabled;

0 commit comments

Comments
 (0)