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 }