|
1
|
import { useState } from "react"; |
|
2
|
import { useLocation } from "wouter"; |
|
3
|
import { |
|
4
|
Sparkles, |
|
5
|
Cpu, |
|
6
|
Laptop, |
|
7
|
Monitor, |
|
8
|
AlertTriangle, |
|
9
|
CheckCircle2, |
|
10
|
RefreshCw, |
|
11
|
Send, |
|
12
|
Wrench, |
|
13
|
ShieldAlert, |
|
14
|
ArrowRight, |
|
15
|
CheckSquare, |
|
16
|
Square, |
|
17
|
Copy, |
|
18
|
Check, |
|
19
|
Layers, |
|
20
|
Zap, |
|
21
|
FileText, |
|
22
|
Sliders, |
|
23
|
Download, |
|
24
|
Info |
|
25
|
} from "lucide-react"; |
|
26
|
import { Button } from "@/components/ui/button"; |
|
27
|
import { Input } from "@/components/ui/input"; |
|
28
|
import { Textarea } from "@/components/ui/textarea"; |
|
29
|
import { Badge } from "@/components/ui/badge"; |
|
30
|
import { useToast } from "@/hooks/use-toast"; |
|
31
|
import { cn } from "@/lib/utils"; |
|
32
|
|
|
33
|
interface DiagnosticResult { |
|
34
|
summary: string; |
|
35
|
confidence: "High" | "Medium" | "Low"; |
|
36
|
recommendedCategory: "hardware" | "software" | "cleaning" | "upgrades"; |
|
37
|
probableCauses: { |
|
38
|
cause: string; |
|
39
|
likelihood: "High" | "Medium" | "Low"; |
|
40
|
explanation: string; |
|
41
|
}[]; |
|
42
|
stepByStepPlan: { |
|
43
|
stepNumber: number; |
|
44
|
title: string; |
|
45
|
action: string; |
|
46
|
expectedOutcome: string; |
|
47
|
}[]; |
|
48
|
toolsAndSoftware: string[]; |
|
49
|
safetyWarnings: string[]; |
|
50
|
preventativeTips: string[]; |
|
51
|
isOutOfScope?: boolean; |
|
52
|
} |
|
53
|
|
|
54
|
const COMMON_SYMPTOMS = [ |
|
55
|
{ label: "No Power / Black Screen", icon: Zap, prompt: "Laptop won't power on. No fan spin, charging LED is off when plugged into AC adapter." }, |
|
56
|
{ label: "BSOD Crash / Stop Code", icon: AlertTriangle, prompt: "System crashes to Blue Screen of Death with stop code CRITICAL_PROCESS_DIED or MEMORY_MANAGEMENT." }, |
|
57
|
{ label: "Thermal Throttling & Loud Fans", icon: Sliders, prompt: "CPU temperatures reach 95°C+ during light tasks with constant high-RPM fan noise." }, |
|
58
|
{ label: "100% Disk Usage / Severe Lag", icon: Layers, prompt: "Computer takes 5+ minutes to boot with 100% disk usage freezing desktop responsiveness." }, |
|
59
|
{ label: "Display Artifacts & Flickering", icon: Monitor, prompt: "Screen shows colorful horizontal lines, checkerboard artifacts, or flickers when moving hinge." }, |
|
60
|
{ label: "Liquid Spill Emergency", icon: ShieldAlert, prompt: "Coffee/water spilled across laptop keyboard. Machine was powered off immediately." }, |
|
61
|
{ label: "Battery Won't Hold Charge", icon: Laptop, prompt: "Battery drains from 100% to 0% in 15 minutes and reports 'Service Recommended'." }, |
|
62
|
{ label: "Boot Device Not Found", icon: FileText, prompt: "BIOS displays 'No Bootable Device Found' or 0xc000000e error during startup." } |
|
63
|
]; |
|
64
|
|
|
65
|
export default function AITroubleshooter() { |
|
66
|
const [, setLocation] = useLocation(); |
|
67
|
const { toast } = useToast(); |
|
68
|
|
|
69
|
const [deviceType, setDeviceType] = useState<"laptop" | "desktop">("laptop"); |
|
70
|
const [brand, setBrand] = useState("Dell"); |
|
71
|
const [model, setModel] = useState(""); |
|
72
|
const [os, setOs] = useState("Windows 11"); |
|
73
|
const [errorCode, setErrorCode] = useState(""); |
|
74
|
const [symptoms, setSymptoms] = useState(""); |
|
75
|
|
|
76
|
const [isLoading, setIsLoading] = useState(false); |
|
77
|
const [result, setResult] = useState<DiagnosticResult | null>(null); |
|
78
|
const [completedSteps, setCompletedSteps] = useState<number[]>([]); |
|
79
|
const [copiedReport, setCopiedReport] = useState(false); |
|
80
|
|
|
81
|
const handleSelectSymptom = (promptText: string) => { |
|
82
|
setSymptoms(promptText); |
|
83
|
}; |
|
84
|
|
|
85
|
const handleDiagnose = async (e?: React.FormEvent) => { |
|
86
|
if (e) e.preventDefault(); |
|
87
|
if (!symptoms.trim()) { |
|
88
|
toast({ |
|
89
|
title: "Please enter symptoms", |
|
90
|
description: "Describe what is happening or select one of the common issue tags below.", |
|
91
|
variant: "destructive" |
|
92
|
}); |
|
93
|
return; |
|
94
|
} |
|
95
|
|
|
96
|
setIsLoading(true); |
|
97
|
setResult(null); |
|
98
|
setCompletedSteps([]); |
|
99
|
|
|
100
|
try { |
|
101
|
const response = await fetch("/api/ai/troubleshoot", { |
|
102
|
method: "POST", |
|
103
|
headers: { "Content-Type": "application/json" }, |
|
104
|
body: JSON.stringify({ |
|
105
|
symptoms: symptoms.trim(), |
|
106
|
deviceType, |
|
107
|
brand, |
|
108
|
model: model.trim(), |
|
109
|
os, |
|
110
|
errorCode: errorCode.trim() |
|
111
|
}) |
|
112
|
}); |
|
113
|
|
|
114
|
if (!response.ok) { |
|
115
|
throw new Error("Failed to process diagnostic query"); |
|
116
|
} |
|
117
|
|
|
118
|
const data: DiagnosticResult = await response.json(); |
|
119
|
setResult(data); |
|
120
|
toast({ |
|
121
|
title: "Diagnostic Analysis Complete", |
|
122
|
description: `Identified ${data.probableCauses?.length || 0} probable root causes.`, |
|
123
|
}); |
|
124
|
} catch (err: any) { |
|
125
|
console.error("Diagnostic error:", err); |
|
126
|
toast({ |
|
127
|
title: "Diagnosis Failed", |
|
128
|
description: err.message || "Could not complete diagnosis. Please try again.", |
|
129
|
variant: "destructive" |
|
130
|
}); |
|
131
|
} finally { |
|
132
|
setIsLoading(false); |
|
133
|
} |
|
134
|
}; |
|
135
|
|
|
136
|
const toggleStep = (stepNumber: number) => { |
|
137
|
if (completedSteps.includes(stepNumber)) { |
|
138
|
setCompletedSteps(completedSteps.filter(s => s !== stepNumber)); |
|
139
|
} else { |
|
140
|
setCompletedSteps([...completedSteps, stepNumber]); |
|
141
|
} |
|
142
|
}; |
|
143
|
|
|
144
|
const handleCopyReport = () => { |
|
145
|
if (!result) return; |
|
146
|
const report = `JCR GUIDE PRO - AI DIAGNOSTIC REPORT |
|
147
|
Device: ${brand} ${model || "Generic"} (${deviceType}) - OS: ${os} |
|
148
|
Symptoms: ${symptoms} |
|
149
|
Summary: ${result.summary} |
|
150
|
Confidence: ${result.confidence} | Category: ${result.recommendedCategory} |
|
151
|
|
|
152
|
PROBABLE CAUSES: |
|
153
|
${result.probableCauses?.map(c => `- [${c.likelihood}] ${c.cause}: ${c.explanation}`).join("\n")} |
|
154
|
|
|
155
|
STEP-BY-STEP RESOLUTION: |
|
156
|
${result.stepByStepPlan?.map(s => `${s.stepNumber}. ${s.title}: ${s.action} -> Expected: ${s.expectedOutcome}`).join("\n")} |
|
157
|
|
|
158
|
REQUIRED TOOLS & SOFTWARE: |
|
159
|
${result.toolsAndSoftware?.map(t => `- ${t}`).join("\n")} |
|
160
|
|
|
161
|
SAFETY WARNINGS: |
|
162
|
${result.safetyWarnings?.map(w => `! ${w}`).join("\n")}`; |
|
163
|
|
|
164
|
navigator.clipboard.writeText(report); |
|
165
|
setCopiedReport(true); |
|
166
|
toast({ |
|
167
|
title: "Report Copied!", |
|
168
|
description: "Complete technical diagnosis copied to clipboard.", |
|
169
|
}); |
|
170
|
setTimeout(() => setCopiedReport(false), 2000); |
|
171
|
}; |
|
172
|
|
|
173
|
return ( |
|
174
|
<div className="space-y-8"> |
|
175
|
{/* Input Diagnostic Card */} |
|
176
|
<div className="rounded-3xl border border-border/60 bg-card/60 backdrop-blur-xl p-6 sm:p-8 shadow-sm space-y-6"> |
|
177
|
|
|
178
|
{/* Card Header */} |
|
179
|
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-4 border-b border-border/40 pb-5"> |
|
180
|
<div className="space-y-1"> |
|
181
|
<div className="flex items-center gap-2"> |
|
182
|
<span className="inline-flex items-center gap-1.5 px-2.5 py-0.5 rounded-full bg-primary/10 text-primary border border-primary/20 text-[11px] font-bold uppercase tracking-wider"> |
|
183
|
<Sparkles className="h-3 w-3" /> Gemini 3 AI Diagnostic Specialist |
|
184
|
</span> |
|
185
|
<span className="inline-flex items-center gap-1 px-2 py-0.5 rounded-full bg-emerald-500/10 text-emerald-600 dark:text-emerald-400 border border-emerald-500/20 text-[10px] font-semibold"> |
|
186
|
Bench-Grade Precision |
|
187
|
</span> |
|
188
|
</div> |
|
189
|
<h2 className="text-xl sm:text-2xl font-bold tracking-tight"> |
|
190
|
Interactive Device Diagnostic Engine |
|
191
|
</h2> |
|
192
|
<p className="text-xs text-muted-foreground"> |
|
193
|
Describe your device symptoms, crash codes, or select common hardware failure patterns to generate an automated root-cause repair plan. |
|
194
|
</p> |
|
195
|
</div> |
|
196
|
</div> |
|
197
|
|
|
198
|
{/* Diagnostic Form */} |
|
199
|
<form onSubmit={handleDiagnose} className="space-y-6"> |
|
200
|
|
|
201
|
{/* Quick Hardware Specification Selectors */} |
|
202
|
<div className="grid grid-cols-2 sm:grid-cols-4 gap-3"> |
|
203
|
{/* Device Form Factor */} |
|
204
|
<div className="space-y-1.5"> |
|
205
|
<label className="text-xs font-semibold text-foreground flex items-center gap-1.5"> |
|
206
|
{deviceType === "laptop" ? <Laptop className="h-3.5 w-3.5 text-primary" /> : <Monitor className="h-3.5 w-3.5 text-primary" />} |
|
207
|
Device Type |
|
208
|
</label> |
|
209
|
<div className="grid grid-cols-2 gap-1 bg-muted/40 p-1 rounded-xl border border-border/40"> |
|
210
|
<button |
|
211
|
type="button" |
|
212
|
onClick={() => setDeviceType("laptop")} |
|
213
|
className={cn( |
|
214
|
"py-1.5 text-xs font-medium rounded-lg transition-all text-center", |
|
215
|
deviceType === "laptop" ? "bg-background text-foreground shadow-xs font-semibold" : "text-muted-foreground hover:text-foreground" |
|
216
|
)} |
|
217
|
> |
|
218
|
Laptop |
|
219
|
</button> |
|
220
|
<button |
|
221
|
type="button" |
|
222
|
onClick={() => setDeviceType("desktop")} |
|
223
|
className={cn( |
|
224
|
"py-1.5 text-xs font-medium rounded-lg transition-all text-center", |
|
225
|
deviceType === "desktop" ? "bg-background text-foreground shadow-xs font-semibold" : "text-muted-foreground hover:text-foreground" |
|
226
|
)} |
|
227
|
> |
|
228
|
Desktop |
|
229
|
</button> |
|
230
|
</div> |
|
231
|
</div> |
|
232
|
|
|
233
|
{/* Brand Selection */} |
|
234
|
<div className="space-y-1.5"> |
|
235
|
<label className="text-xs font-semibold text-foreground">Manufacturer / Brand</label> |
|
236
|
<select |
|
237
|
value={brand} |
|
238
|
onChange={(e) => setBrand(e.target.value)} |
|
239
|
className="w-full h-9 rounded-xl border border-border/50 bg-background/80 px-3 text-xs focus:ring-1 focus:ring-primary focus:outline-hidden" |
|
240
|
> |
|
241
|
<option value="Dell">Dell / Alienware</option> |
|
242
|
<option value="Apple">Apple (MacBook / Mac)</option> |
|
243
|
<option value="Lenovo">Lenovo (ThinkPad / Legion)</option> |
|
244
|
<option value="HP">HP (Spectre / OMEN / EliteBook)</option> |
|
245
|
<option value="ASUS">ASUS (ROG / TUF / ZenBook)</option> |
|
246
|
<option value="Acer">Acer (Predator / Nitro / Swift)</option> |
|
247
|
<option value="MSI">MSI Gaming & Workstations</option> |
|
248
|
<option value="Razer">Razer Blade</option> |
|
249
|
<option value="Microsoft">Microsoft Surface</option> |
|
250
|
<option value="Samsung">Samsung Galaxy Book</option> |
|
251
|
<option value="Framework">Framework (Modular)</option> |
|
252
|
<option value="Custom Built">Custom Built Desktop PC</option> |
|
253
|
<option value="Other">Other / Generic</option> |
|
254
|
</select> |
|
255
|
</div> |
|
256
|
|
|
257
|
{/* Exact Model (Optional) */} |
|
258
|
<div className="space-y-1.5"> |
|
259
|
<label className="text-xs font-semibold text-foreground">Model Name (Optional)</label> |
|
260
|
<Input |
|
261
|
placeholder="e.g. XPS 15 9530, M3 Max" |
|
262
|
value={model} |
|
263
|
onChange={(e) => setModel(e.target.value)} |
|
264
|
className="h-9 rounded-xl text-xs bg-background/80" |
|
265
|
/> |
|
266
|
</div> |
|
267
|
|
|
268
|
{/* Operating System */} |
|
269
|
<div className="space-y-1.5"> |
|
270
|
<label className="text-xs font-semibold text-foreground">Operating System</label> |
|
271
|
<select |
|
272
|
value={os} |
|
273
|
onChange={(e) => setOs(e.target.value)} |
|
274
|
className="w-full h-9 rounded-xl border border-border/50 bg-background/80 px-3 text-xs focus:ring-1 focus:ring-primary focus:outline-hidden" |
|
275
|
> |
|
276
|
<option value="Windows 11">Windows 11</option> |
|
277
|
<option value="Windows 10">Windows 10</option> |
|
278
|
<option value="macOS Sonoma / Sequoia">macOS Sonoma / Sequoia</option> |
|
279
|
<option value="Linux (Ubuntu / Fedora / Arch)">Linux</option> |
|
280
|
<option value="No OS / Bootloader failure">No OS / BIOS Only</option> |
|
281
|
</select> |
|
282
|
</div> |
|
283
|
</div> |
|
284
|
|
|
285
|
{/* Quick Issue Symptom Presets */} |
|
286
|
<div className="space-y-2"> |
|
287
|
<label className="text-xs font-semibold text-muted-foreground uppercase tracking-wider block"> |
|
288
|
Quick Symptom Presets (Click to Auto-fill) |
|
289
|
</label> |
|
290
|
<div className="flex flex-wrap gap-2"> |
|
291
|
{COMMON_SYMPTOMS.map((sym, idx) => { |
|
292
|
const Icon = sym.icon; |
|
293
|
const isSelected = symptoms === sym.prompt; |
|
294
|
return ( |
|
295
|
<button |
|
296
|
key={idx} |
|
297
|
type="button" |
|
298
|
onClick={() => handleSelectSymptom(sym.prompt)} |
|
299
|
className={cn( |
|
300
|
"inline-flex items-center gap-1.5 px-3 py-1.5 rounded-xl text-xs font-medium border transition-all cursor-pointer", |
|
301
|
isSelected |
|
302
|
? "bg-primary text-primary-foreground border-primary shadow-xs" |
|
303
|
: "bg-muted/40 text-foreground border-border/40 hover:bg-muted/80 hover:border-border" |
|
304
|
)} |
|
305
|
> |
|
306
|
<Icon className="h-3 w-3" /> |
|
307
|
<span>{sym.label}</span> |
|
308
|
</button> |
|
309
|
); |
|
310
|
})} |
|
311
|
</div> |
|
312
|
</div> |
|
313
|
|
|
314
|
{/* Symptoms Description & Error Code */} |
|
315
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-4"> |
|
316
|
<div className="md:col-span-2 space-y-1.5"> |
|
317
|
<label className="text-xs font-semibold text-foreground flex items-center justify-between"> |
|
318
|
<span>Detailed Symptoms & Malfunction Behavior</span> |
|
319
|
<span className="text-[11px] text-muted-foreground font-normal">Be specific for highest accuracy</span> |
|
320
|
</label> |
|
321
|
<Textarea |
|
322
|
placeholder="Describe what happens when you press the power button, any beep codes, LED blink codes, weird noises, thermal spikes, or recent liquid contact..." |
|
323
|
value={symptoms} |
|
324
|
onChange={(e) => setSymptoms(e.target.value)} |
|
325
|
rows={3} |
|
326
|
className="rounded-2xl text-xs bg-background/80 resize-none p-3.5 focus-visible:ring-1 focus-visible:ring-primary" |
|
327
|
/> |
|
328
|
</div> |
|
329
|
|
|
330
|
<div className="space-y-1.5 flex flex-col justify-between"> |
|
331
|
<div className="space-y-1.5"> |
|
332
|
<label className="text-xs font-semibold text-foreground"> |
|
333
|
Error Code / Stop Code (Optional) |
|
334
|
</label> |
|
335
|
<Input |
|
336
|
placeholder="e.g. 0xc000000e, WHEA_ERROR, 3 Amber 2 White" |
|
337
|
value={errorCode} |
|
338
|
onChange={(e) => setErrorCode(e.target.value)} |
|
339
|
className="h-9 rounded-xl text-xs bg-background/80" |
|
340
|
/> |
|
341
|
</div> |
|
342
|
|
|
343
|
<Button |
|
344
|
type="submit" |
|
345
|
disabled={isLoading || !symptoms.trim()} |
|
346
|
className="w-full h-11 rounded-2xl font-bold text-xs gap-2 bg-primary hover:bg-primary/90 text-primary-foreground shadow-sm cursor-pointer mt-3" |
|
347
|
> |
|
348
|
{isLoading ? ( |
|
349
|
<> |
|
350
|
<RefreshCw className="h-4 w-4 animate-spin" /> |
|
351
|
<span>Analyzing Logic & Schematics...</span> |
|
352
|
</> |
|
353
|
) : ( |
|
354
|
<> |
|
355
|
<Sparkles className="h-4 w-4" /> |
|
356
|
<span>Run AI Hardware Diagnosis</span> |
|
357
|
</> |
|
358
|
)} |
|
359
|
</Button> |
|
360
|
</div> |
|
361
|
</div> |
|
362
|
</form> |
|
363
|
</div> |
|
364
|
|
|
365
|
{/* Diagnostic Results Presentation */} |
|
366
|
{result && ( |
|
367
|
<div className="space-y-6 animate-in fade-in slide-in-from-bottom-3 duration-300"> |
|
368
|
|
|
369
|
{/* Out of scope notice if non-repair question */} |
|
370
|
{result.isOutOfScope ? ( |
|
371
|
<div className="rounded-3xl border border-amber-500/30 bg-amber-500/10 p-6 flex items-start gap-4 text-amber-800 dark:text-amber-300"> |
|
372
|
<Info className="h-6 w-6 shrink-0 mt-0.5" /> |
|
373
|
<div className="space-y-1"> |
|
374
|
<h4 className="font-bold text-sm">Out of Scope Query</h4> |
|
375
|
<p className="text-xs leading-relaxed">{result.summary}</p> |
|
376
|
<p className="text-[11px] opacity-80 pt-1"> |
|
377
|
The JCRGuru Diagnostic Engine is dedicated strictly to computer, laptop, OS, and electronics troubleshooting. |
|
378
|
</p> |
|
379
|
</div> |
|
380
|
</div> |
|
381
|
) : ( |
|
382
|
<div className="rounded-3xl border border-border/60 bg-card/70 backdrop-blur-xl p-6 sm:p-8 space-y-7 shadow-sm"> |
|
383
|
|
|
384
|
{/* Report Header */} |
|
385
|
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-4 border-b border-border/40 pb-5"> |
|
386
|
<div> |
|
387
|
<div className="flex items-center gap-2 mb-1.5"> |
|
388
|
<Badge variant="outline" className={cn( |
|
389
|
"text-[10px] font-bold uppercase tracking-wider px-2.5 py-0.5", |
|
390
|
result.recommendedCategory === "hardware" && "bg-rose-500/10 text-rose-600 dark:text-rose-400 border-rose-500/20", |
|
391
|
result.recommendedCategory === "software" && "bg-blue-500/10 text-blue-600 dark:text-blue-400 border-blue-500/20", |
|
392
|
result.recommendedCategory === "cleaning" && "bg-amber-500/10 text-amber-600 dark:text-amber-400 border-amber-500/20", |
|
393
|
result.recommendedCategory === "upgrades" && "bg-emerald-500/10 text-emerald-600 dark:text-emerald-400 border-emerald-500/20", |
|
394
|
)}> |
|
395
|
Category: {result.recommendedCategory} |
|
396
|
</Badge> |
|
397
|
<Badge variant="secondary" className="text-[10px] font-bold px-2 py-0.5"> |
|
398
|
Confidence: {result.confidence} |
|
399
|
</Badge> |
|
400
|
</div> |
|
401
|
<h3 className="text-xl sm:text-2xl font-bold tracking-tight text-foreground"> |
|
402
|
Diagnostic Analysis & Action Plan |
|
403
|
</h3> |
|
404
|
</div> |
|
405
|
|
|
406
|
<div className="flex items-center gap-2"> |
|
407
|
<Button |
|
408
|
variant="outline" |
|
409
|
size="sm" |
|
410
|
onClick={handleCopyReport} |
|
411
|
className="rounded-xl text-xs gap-1.5 h-9 border-border/60" |
|
412
|
> |
|
413
|
{copiedReport ? ( |
|
414
|
<> |
|
415
|
<Check className="h-3.5 w-3.5 text-emerald-500" /> |
|
416
|
<span>Copied!</span> |
|
417
|
</> |
|
418
|
) : ( |
|
419
|
<> |
|
420
|
<Copy className="h-3.5 w-3.5" /> |
|
421
|
<span>Copy Full Report</span> |
|
422
|
</> |
|
423
|
)} |
|
424
|
</Button> |
|
425
|
|
|
426
|
<Button |
|
427
|
size="sm" |
|
428
|
onClick={() => setLocation(`/?category=${result.recommendedCategory}&deviceType=${deviceType}`)} |
|
429
|
className="rounded-xl text-xs gap-1.5 h-9 bg-primary text-primary-foreground font-bold" |
|
430
|
> |
|
431
|
<Wrench className="h-3.5 w-3.5" /> |
|
432
|
<span>View {result.recommendedCategory} Guides</span> |
|
433
|
<ArrowRight className="h-3 w-3" /> |
|
434
|
</Button> |
|
435
|
</div> |
|
436
|
</div> |
|
437
|
|
|
438
|
{/* Summary Banner */} |
|
439
|
<div className="rounded-2xl border border-primary/20 bg-primary/5 p-4 sm:p-5 flex items-start gap-3"> |
|
440
|
<Sparkles className="h-5 w-5 text-primary shrink-0 mt-0.5" /> |
|
441
|
<div className="space-y-1"> |
|
442
|
<span className="text-xs font-bold uppercase tracking-wider text-primary">Key Diagnostic Finding</span> |
|
443
|
<p className="text-sm font-medium text-foreground leading-relaxed"> |
|
444
|
{result.summary} |
|
445
|
</p> |
|
446
|
</div> |
|
447
|
</div> |
|
448
|
|
|
449
|
{/* Root Cause Hypotheses */} |
|
450
|
<div className="space-y-3"> |
|
451
|
<h4 className="text-sm font-bold tracking-tight text-foreground flex items-center gap-2"> |
|
452
|
<AlertTriangle className="h-4 w-4 text-amber-500" /> |
|
453
|
Probable Root Causes ({result.probableCauses?.length || 0}) |
|
454
|
</h4> |
|
455
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-3.5"> |
|
456
|
{result.probableCauses?.map((cause, idx) => ( |
|
457
|
<div |
|
458
|
key={idx} |
|
459
|
className="rounded-2xl border border-border/60 bg-background/60 p-4 space-y-2 hover:border-primary/40 transition-colors" |
|
460
|
> |
|
461
|
<div className="flex items-center justify-between gap-2"> |
|
462
|
<span className="text-xs font-bold text-foreground leading-snug">{cause.cause}</span> |
|
463
|
<Badge |
|
464
|
variant="outline" |
|
465
|
className={cn( |
|
466
|
"text-[10px] font-bold px-1.5 py-0 shrink-0", |
|
467
|
cause.likelihood === "High" && "bg-rose-500/10 text-rose-600 border-rose-500/20", |
|
468
|
cause.likelihood === "Medium" && "bg-amber-500/10 text-amber-600 border-amber-500/20", |
|
469
|
cause.likelihood === "Low" && "bg-muted text-muted-foreground border-border/40" |
|
470
|
)} |
|
471
|
> |
|
472
|
{cause.likelihood} Probability |
|
473
|
</Badge> |
|
474
|
</div> |
|
475
|
<p className="text-xs text-muted-foreground leading-relaxed"> |
|
476
|
{cause.explanation} |
|
477
|
</p> |
|
478
|
</div> |
|
479
|
))} |
|
480
|
</div> |
|
481
|
</div> |
|
482
|
|
|
483
|
{/* Step-by-Step Interactive Action Plan */} |
|
484
|
<div className="space-y-3"> |
|
485
|
<div className="flex items-center justify-between"> |
|
486
|
<h4 className="text-sm font-bold tracking-tight text-foreground flex items-center gap-2"> |
|
487
|
<CheckCircle2 className="h-4 w-4 text-emerald-500" /> |
|
488
|
Step-by-Step Diagnostic & Resolution Protocol |
|
489
|
</h4> |
|
490
|
<span className="text-xs font-semibold text-muted-foreground"> |
|
491
|
{completedSteps.length} of {result.stepByStepPlan?.length || 0} Completed |
|
492
|
</span> |
|
493
|
</div> |
|
494
|
|
|
495
|
<div className="space-y-2.5"> |
|
496
|
{result.stepByStepPlan?.map((step) => { |
|
497
|
const isDone = completedSteps.includes(step.stepNumber); |
|
498
|
return ( |
|
499
|
<div |
|
500
|
key={step.stepNumber} |
|
501
|
onClick={() => toggleStep(step.stepNumber)} |
|
502
|
className={cn( |
|
503
|
"rounded-2xl border p-4 sm:p-5 flex items-start gap-4 cursor-pointer transition-all", |
|
504
|
isDone |
|
505
|
? "border-emerald-500/30 bg-emerald-500/5 opacity-80" |
|
506
|
: "border-border/60 bg-background/60 hover:border-primary/40 hover:bg-background/90" |
|
507
|
)} |
|
508
|
> |
|
509
|
<button |
|
510
|
type="button" |
|
511
|
className="mt-0.5 shrink-0 text-muted-foreground hover:text-foreground" |
|
512
|
aria-label="Toggle step" |
|
513
|
> |
|
514
|
{isDone ? ( |
|
515
|
<CheckSquare className="h-5 w-5 text-emerald-500" /> |
|
516
|
) : ( |
|
517
|
<Square className="h-5 w-5 text-muted-foreground" /> |
|
518
|
)} |
|
519
|
</button> |
|
520
|
|
|
521
|
<div className="space-y-1.5 flex-1"> |
|
522
|
<div className="flex items-center gap-2"> |
|
523
|
<span className="text-xs font-bold px-2 py-0.5 rounded-md bg-muted text-foreground"> |
|
524
|
Step {step.stepNumber} |
|
525
|
</span> |
|
526
|
<span className={cn("text-sm font-bold text-foreground", isDone && "line-through opacity-70")}> |
|
527
|
{step.title} |
|
528
|
</span> |
|
529
|
</div> |
|
530
|
<p className="text-xs text-foreground/80 leading-relaxed"> |
|
531
|
{step.action} |
|
532
|
</p> |
|
533
|
<div className="text-[11px] text-emerald-600 dark:text-emerald-400 flex items-center gap-1.5 font-medium pt-0.5"> |
|
534
|
<ArrowRight className="h-3 w-3 shrink-0" /> |
|
535
|
<span>Expected Verification Outcome: {step.expectedOutcome}</span> |
|
536
|
</div> |
|
537
|
</div> |
|
538
|
</div> |
|
539
|
); |
|
540
|
})} |
|
541
|
</div> |
|
542
|
</div> |
|
543
|
|
|
544
|
{/* Tools & Safety Section */} |
|
545
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 pt-2"> |
|
546
|
{/* Tools & Software Recommended */} |
|
547
|
<div className="rounded-2xl border border-border/50 bg-background/40 p-5 space-y-3"> |
|
548
|
<h5 className="text-xs font-bold uppercase tracking-wider text-foreground flex items-center gap-1.5"> |
|
549
|
<Wrench className="h-3.5 w-3.5 text-primary" /> |
|
550
|
Recommended Diagnostics & Tools |
|
551
|
</h5> |
|
552
|
<ul className="space-y-1.5 text-xs text-muted-foreground"> |
|
553
|
{result.toolsAndSoftware?.map((tool, idx) => ( |
|
554
|
<li key={idx} className="flex items-center gap-2 text-foreground/80"> |
|
555
|
<CheckCircle2 className="h-3 w-3 text-primary shrink-0" /> |
|
556
|
<span>{tool}</span> |
|
557
|
</li> |
|
558
|
))} |
|
559
|
</ul> |
|
560
|
<div className="pt-2"> |
|
561
|
<Button |
|
562
|
variant="outline" |
|
563
|
size="sm" |
|
564
|
onClick={() => setLocation("/software-tools")} |
|
565
|
className="w-full rounded-xl text-xs gap-1.5 h-8 border-border/60" |
|
566
|
> |
|
567
|
<Download className="h-3 w-3" /> |
|
568
|
<span>Download Diagnostic Utilities</span> |
|
569
|
</Button> |
|
570
|
</div> |
|
571
|
</div> |
|
572
|
|
|
573
|
{/* Safety & Electrostatic Warnings */} |
|
574
|
<div className="rounded-2xl border border-rose-500/20 bg-rose-500/5 p-5 space-y-3"> |
|
575
|
<h5 className="text-xs font-bold uppercase tracking-wider text-rose-600 dark:text-rose-400 flex items-center gap-1.5"> |
|
576
|
<ShieldAlert className="h-3.5 w-3.5 text-rose-500" /> |
|
577
|
Technician Safety Directives |
|
578
|
</h5> |
|
579
|
<ul className="space-y-1.5 text-xs text-foreground/80"> |
|
580
|
{result.safetyWarnings?.map((warning, idx) => ( |
|
581
|
<li key={idx} className="flex items-start gap-2"> |
|
582
|
<AlertTriangle className="h-3 w-3 text-rose-500 shrink-0 mt-0.5" /> |
|
583
|
<span>{warning}</span> |
|
584
|
</li> |
|
585
|
))} |
|
586
|
</ul> |
|
587
|
</div> |
|
588
|
</div> |
|
589
|
|
|
590
|
</div> |
|
591
|
)} |
|
592
|
|
|
593
|
</div> |
|
594
|
)} |
|
595
|
</div> |
|
596
|
); |
|
597
|
} |