- RMM Overshell (migration 077): admin page, dispatch UI, executor/worker, target resolver, script registry (AD/DHCP/DNS/event-log/services/software/network/loglift) - LogLift evidence pipeline (migration 078): upload webhook, B2 storage client, receiver/matcher, EventLogCollector PowerShell script - IT Glue audit + write-back (migrations 075, 076): asset-audit runner, ticket xrefs, applications/configurations browse pages + apply/revert/audit endpoints - Link-aware analyzer bundles (migration 073) + provider toggle (migration 074): link-discovery service, OpenRouter LLM provider, related-tickets/itglue-suggestion panels, analyze-bundle endpoint - Endpoint data model + device-link reconciliation (migrations 079, 080): conflicts admin page, reconciler service, resolve endpoints - Dashboard overhaul: integration-health service + alerts, overview/health endpoints - Permissions: add itglue + rmm scopes; middleware: public /api/rmm/loglift route Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
62 lines
2 KiB
TypeScript
62 lines
2 KiB
TypeScript
'use client';
|
|
|
|
import { useEffect, useState, use } from 'react';
|
|
import { Skeleton } from '@/components/ui/skeleton';
|
|
import { AnalysisView } from '@/components/analyzer/analysis-view';
|
|
import { ItglueSuggestionsPanel } from '@/components/analyzer/itglue-suggestions-panel';
|
|
import type { PersistedAnalysis } from '@/lib/types/analyzer';
|
|
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert';
|
|
|
|
export default function AnalysisDetailPage({
|
|
params,
|
|
}: {
|
|
params: Promise<{ id: string }>;
|
|
}) {
|
|
const { id } = use(params);
|
|
const [analysis, setAnalysis] = useState<PersistedAnalysis | null>(null);
|
|
const [error, setError] = useState<string | null>(null);
|
|
|
|
useEffect(() => {
|
|
let cancelled = false;
|
|
(async () => {
|
|
try {
|
|
const res = await fetch(`/api/analyzer/analyses/${id}`);
|
|
if (!res.ok) {
|
|
const data = (await res.json().catch(() => ({}))) as { error?: string };
|
|
throw new Error(data.error ?? `Request failed: ${res.status}`);
|
|
}
|
|
const { analysis } = (await res.json()) as { analysis: PersistedAnalysis };
|
|
if (!cancelled) setAnalysis(analysis);
|
|
} catch (err) {
|
|
if (!cancelled) setError(err instanceof Error ? err.message : 'Unknown error');
|
|
}
|
|
})();
|
|
return () => {
|
|
cancelled = true;
|
|
};
|
|
}, [id]);
|
|
|
|
return (
|
|
<div className="container mx-auto px-6 py-6 max-w-5xl">
|
|
{error && (
|
|
<Alert variant="destructive">
|
|
<AlertTitle>Couldn’t load this analysis</AlertTitle>
|
|
<AlertDescription>{error}</AlertDescription>
|
|
</Alert>
|
|
)}
|
|
{!error && !analysis && (
|
|
<div className="space-y-4">
|
|
<Skeleton className="h-32 w-full" />
|
|
<Skeleton className="h-24 w-full" />
|
|
<Skeleton className="h-24 w-full" />
|
|
</div>
|
|
)}
|
|
{analysis && (
|
|
<div className="space-y-6">
|
|
<AnalysisView analysis={analysis} />
|
|
<ItglueSuggestionsPanel analysisId={analysis.id} />
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|