fix: harden identity, live access, drafts and setup (review only)

Source-reviewed hardening changes and regression tests. UNBUILT / UNTESTED: not a deployable release. Go formatting, compilation, automated regression tests, full frontend validation and staging acceptance remain required. See docs/hardening/VALIDATION.md. Main remains the original clean snapshot; no upstream Git history imported.
This commit is contained in:
2026-10-16 12:15:20 +00:00
parent e57610a27c
commit 63e0457886
86 changed files with 5174 additions and 1929 deletions

View File

@@ -1,3 +1,4 @@
// Modified in AgentDeskFixes: safe, reviewed setup repairs and honest routing diagnostics.
"use client"
import {
@@ -14,7 +15,7 @@ import {
XCircleIcon,
} from "lucide-react"
import Link from "next/link"
import { useCallback, useEffect, useMemo, useState } from "react"
import { useCallback, useEffect, useMemo, useRef, useState } from "react"
import { toast } from "sonner"
import { DashboardPage } from "@/components/dashboard-page"
@@ -25,6 +26,7 @@ import {
import { OptionCombobox } from "@/components/option-combobox"
import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog"
import {
Table,
TableBody,
@@ -39,6 +41,7 @@ import {
fetchSetupReadiness,
simulateSetupRoute,
type SetupQuickFixAction,
type SetupQuickFixResponse,
type SetupReadinessResponse,
type SetupSimulateRouteResponse,
type SetupStepStatus,
@@ -98,6 +101,15 @@ export function SetupWizardView() {
const [readiness, setReadiness] = useState<SetupReadinessResponse | null>(null)
const [loading, setLoading] = useState(true)
const [fixingAction, setFixingAction] = useState<string | null>(null)
const fixingRef = useRef(false)
const selectedChannelRef = useRef("")
const simulationRequestRef = useRef(0)
const readinessRequestRef = useRef(0)
const [pendingFix, setPendingFix] = useState<{
action: SetupQuickFixAction
entityId?: number
preview: SetupQuickFixResponse
} | null>(null)
const [selectedChannelId, setSelectedChannelId] = useState<string>("")
const [simulation, setSimulation] =
@@ -105,31 +117,38 @@ export function SetupWizardView() {
const [simulating, setSimulating] = useState(false)
const runSimulation = useCallback(async (channelIdNum?: number) => {
const requestId = ++simulationRequestRef.current
setSimulating(true)
setSimulation(null)
try {
const sim = await simulateSetupRoute({
channel_id: channelIdNum && channelIdNum > 0 ? channelIdNum : undefined,
bypass_ai: true,
bypass_ai: false,
})
setSimulation(sim)
if (requestId !== simulationRequestRef.current) return
setSimulation({ ...sim, candidate_operators: sim.candidate_operators ?? [] })
if (sim.channel_id > 0) {
selectedChannelRef.current = String(sim.channel_id)
setSelectedChannelId(String(sim.channel_id))
}
} catch (error) {
if (requestId !== simulationRequestRef.current) return
toast.error(
error instanceof Error ? error.message : t("common.operationFailed"),
)
} finally {
setSimulating(false)
if (requestId === simulationRequestRef.current) setSimulating(false)
}
}, [t])
const loadAll = useCallback(async () => {
const requestId = ++readinessRequestRef.current
setLoading(true)
try {
const data = await fetchSetupReadiness()
if (requestId !== readinessRequestRef.current) return
setReadiness(data)
const currentNum = Number(selectedChannelId)
const currentNum = Number(selectedChannelRef.current)
const targetChannelId =
currentNum > 0
? currentNum
@@ -141,13 +160,14 @@ export function SetupWizardView() {
}
await runSimulation(targetChannelId)
} catch (error) {
if (requestId !== readinessRequestRef.current) return
toast.error(
error instanceof Error ? error.message : t("common.loadFailed"),
)
} finally {
setLoading(false)
if (requestId === readinessRequestRef.current) setLoading(false)
}
}, [runSimulation, selectedChannelId, t])
}, [runSimulation, t])
useEffect(() => {
void loadAll()
@@ -157,30 +177,52 @@ export function SetupWizardView() {
window.addEventListener(SETUP_READINESS_UPDATED_EVENT, handleUpdated)
return () => {
window.removeEventListener(SETUP_READINESS_UPDATED_EVENT, handleUpdated)
simulationRequestRef.current++
readinessRequestRef.current++
}
}, [loadAll])
async function handleQuickFix(
action: SetupQuickFixAction,
entityId?: number,
) {
const key = entityId ? `${action}:${entityId}` : action
setFixingAction(key)
async function handleQuickFix(action: SetupQuickFixAction, entityId?: number) {
if (fixingRef.current) return
fixingRef.current = true
setFixingAction(entityId ? `${action}:${entityId}` : action)
try {
const res = await applySetupQuickFix({ action, entity_id: entityId })
setReadiness(res.readiness)
notifySetupReadinessUpdated()
toast.success(t("setupWizard.quickFixSuccess"))
const targetChannelId =
Number(selectedChannelId) ||
res.readiness.channels.find((ch) => ch.status === 0)?.id ||
0
await runSimulation(targetChannelId)
const preview = await applySetupQuickFix({ action, entity_id: entityId })
setPendingFix({ action, entityId, preview })
} catch (error) {
toast.error(
error instanceof Error ? error.message : t("common.operationFailed"),
)
toast.error(error instanceof Error ? error.message : t("common.operationFailed"))
} finally {
fixingRef.current = false
setFixingAction(null)
}
}
async function confirmQuickFix() {
if (!pendingFix || fixingRef.current) return
fixingRef.current = true
setFixingAction(pendingFix.action)
try {
const result = await applySetupQuickFix({
action: pendingFix.action,
entity_id: pendingFix.entityId,
apply: true,
preview_token: pendingFix.preview.preview_token,
})
if (!result.applied) throw new Error(t("setupWizard.preview.notApplied"))
if (result.readiness) setReadiness(result.readiness)
setPendingFix(null)
notifySetupReadinessUpdated()
if (result.warning_codes.length) {
toast.warning(result.warning_codes.map((code) => t(`setupWizard.repairWarnings.${code}`)).join(" "))
} else {
toast.success(t("setupWizard.quickFixSuccess"))
}
} catch (error) {
toast.error(error instanceof Error ? error.message : t("common.operationFailed"))
// A failed/stale preview must be reviewed again, not silently reapplied.
setPendingFix(null)
} finally {
fixingRef.current = false
setFixingAction(null)
}
}
@@ -418,7 +460,10 @@ export function SetupWizardView() {
</h2>
<div className="grid gap-3 md:grid-cols-2">
{readiness.steps.map((step) => {
const primaryQuickFix = step.quick_fix_actions[0]
const primaryQuickFix =
(step.key === "ai_agents" || step.key === "channels") && step.total_count > 0
? undefined
: step.quick_fix_actions[0]
return (
<div
key={step.key}
@@ -469,6 +514,11 @@ export function SetupWizardView() {
{t(`setupWizard.issues.${issue.code}`)}
</span>
</div>
{issue.quick_fix_action && issue.entity_id > 0 && issue.entity_type !== "operator" ? (
<Button size="sm" variant="outline" className="shrink-0 rounded-md" disabled={fixingAction !== null} onClick={() => void handleQuickFix(issue.quick_fix_action!, issue.entity_id)}>
{t("setupWizard.preview.review")}
</Button>
) : null}
</div>
))}
</div>
@@ -542,6 +592,7 @@ export function SetupWizardView() {
options={channelOptions}
value={selectedChannelId}
onChange={(val) => {
selectedChannelRef.current = val
setSelectedChannelId(val)
void runSimulation(Number(val))
}}
@@ -744,6 +795,39 @@ export function SetupWizardView() {
</div>
) : null}
</div>
<Dialog open={pendingFix !== null} onOpenChange={(open) => { if (!open && !fixingRef.current) setPendingFix(null) }}>
<DialogContent className="grid max-h-[85vh] grid-rows-[auto_minmax(0,1fr)_auto] rounded-md sm:max-w-2xl" showCloseButton={false}>
<DialogHeader>
<DialogTitle>{t("setupWizard.preview.title")}</DialogTitle>
<DialogDescription>{t("setupWizard.preview.description")}</DialogDescription>
</DialogHeader>
<div className="space-y-3 overflow-y-auto break-words">
{pendingFix?.preview.changes.length === 0 ? <p>{t("setupWizard.preview.noChanges")}</p> : null}
{pendingFix?.preview.changes.map((change, index) => (
<div key={`${change.entity_type}-${change.entity_id}-${index}`} className="space-y-2 rounded-md border p-3">
<p className="font-medium">{t(`setupWizard.preview.${change.operation}`)}: {change.entity_name}{change.entity_id > 0 ? ` (#${change.entity_id})` : ""}</p>
<dl className="space-y-1 text-xs">
{Object.entries(change.after).map(([field, value]) => (
<div key={field} className="grid gap-1 sm:grid-cols-[minmax(0,1fr)_minmax(0,2fr)]">
<dt className="text-muted-foreground">{t(`setupWizard.repairFields.${field}`)}</dt>
<dd>{change.before && field in change.before ? `${String(change.before[field])} → ` : ""}{String(value)}</dd>
</div>
))}
</dl>
</div>
))}
{pendingFix?.preview.warning_codes.map((code) => <p key={code} className="text-sm text-amber-700 dark:text-amber-300">{t(`setupWizard.repairWarnings.${code}`)}</p>)}
<Link className="text-sm underline" href="/dashboard/agent-team-schedules">{t("setupWizard.preview.manageSchedules")}</Link>
</div>
<DialogFooter>
<Button variant="outline" disabled={fixingAction !== null} onClick={() => setPendingFix(null)}>{t("common.cancel")}</Button>
<Button disabled={fixingAction !== null || !pendingFix?.preview.changes.length} onClick={() => void confirmQuickFix()}>
{fixingAction !== null ? <Loader2Icon className="size-4 animate-spin" /> : null}
{t("setupWizard.preview.apply")}
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
</DashboardPage>
)
}