|
1
|
import React, { useState } from "react"; |
|
2
|
import { useQuery } from "@tanstack/react-query"; |
|
3
|
import { useLocation } from "wouter"; |
|
4
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; |
|
5
|
import { Button } from "@/components/ui/button"; |
|
6
|
import { Input } from "@/components/ui/input"; |
|
7
|
import { Badge } from "@/components/ui/badge"; |
|
8
|
import { Search, Laptop, Monitor, Star, Wrench, Filter, ExternalLink, Eye, Download } from "lucide-react"; |
|
9
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; |
|
10
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; |
|
11
|
import { Separator } from "@/components/ui/separator"; |
|
12
|
import Header from "@/components/layout/header"; |
|
13
|
import Footer from "@/components/layout/footer"; |
|
14
|
import type { DeviceBrand, DeviceModel, RepairGuide } from "@shared/schema"; |
|
15
|
|
|
16
|
export default function DeviceFinder() { |
|
17
|
const [, setLocation] = useLocation(); |
|
18
|
const [selectedDeviceType, setSelectedDeviceType] = useState<"laptop" | "desktop">("laptop"); |
|
19
|
const [selectedBrand, setSelectedBrand] = useState<string>(""); |
|
20
|
const [selectedModel, setSelectedModel] = useState<string>(""); |
|
21
|
const [searchQuery, setSearchQuery] = useState(""); |
|
22
|
const [guideFilter, setGuideFilter] = useState<string>("all"); |
|
23
|
const [categoryFilter, setCategoryFilter] = useState<string>("all"); |
|
24
|
|
|
25
|
// Fetch brands for selected device type |
|
26
|
const { data: brands = [] } = useQuery<DeviceBrand[]>({ |
|
27
|
queryKey: ["/api/device-brands", selectedDeviceType], |
|
28
|
queryFn: () => fetch(`/api/device-brands?deviceType=${selectedDeviceType}`).then(res => res.json()), |
|
29
|
}); |
|
30
|
|
|
31
|
// Fetch models for selected brand |
|
32
|
const { data: models = [] } = useQuery<DeviceModel[]>({ |
|
33
|
queryKey: ["/api/device-models", selectedBrand], |
|
34
|
queryFn: () => selectedBrand ? fetch(`/api/brands/${selectedBrand}/models`).then(res => res.json()) : Promise.resolve([]), |
|
35
|
enabled: !!selectedBrand, |
|
36
|
}); |
|
37
|
|
|
38
|
// Fetch compatible guides for selected model |
|
39
|
const { data: compatibleGuides = [] } = useQuery<RepairGuide[]>({ |
|
40
|
queryKey: ["/api/models", selectedModel, "guides"], |
|
41
|
queryFn: () => selectedModel ? fetch(`/api/models/${selectedModel}/guides`).then(res => res.json()) : Promise.resolve([]), |
|
42
|
enabled: !!selectedModel, |
|
43
|
}); |
|
44
|
|
|
45
|
// Search models when typing |
|
46
|
const { data: searchResults = [] } = useQuery<DeviceModel[]>({ |
|
47
|
queryKey: ["/api/device-models/search", searchQuery, selectedDeviceType], |
|
48
|
queryFn: () => searchQuery ? fetch(`/api/device-models?search=${encodeURIComponent(searchQuery)}&deviceType=${selectedDeviceType}`).then(res => res.json()) : Promise.resolve([]), |
|
49
|
enabled: searchQuery.length > 2, |
|
50
|
}); |
|
51
|
|
|
52
|
const selectedModelData = models.find(m => m.id === selectedModel); |
|
53
|
|
|
54
|
const getSupportLevelColor = (level: string) => { |
|
55
|
switch (level) { |
|
56
|
case "official": return "bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-300"; |
|
57
|
case "community": return "bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-300"; |
|
58
|
case "basic": return "bg-gray-100 text-gray-800 dark:bg-gray-900 dark:text-gray-300"; |
|
59
|
default: return "bg-gray-100 text-gray-800 dark:bg-gray-900 dark:text-gray-300"; |
|
60
|
} |
|
61
|
}; |
|
62
|
|
|
63
|
// Filter compatible guides based on selected filters |
|
64
|
const filteredGuides = compatibleGuides.filter(guide => { |
|
65
|
const difficultyMatch = guideFilter === "all" || guide.difficulty === guideFilter; |
|
66
|
const categoryMatch = categoryFilter === "all" || guide.category === categoryFilter; |
|
67
|
return difficultyMatch && categoryMatch; |
|
68
|
}); |
|
69
|
|
|
70
|
const handleGuideClick = (guideId: string) => { |
|
71
|
setLocation(`/guide/${guideId}`); |
|
72
|
}; |
|
73
|
|
|
74
|
return ( |
|
75
|
<div className="min-h-screen bg-gray-50 dark:bg-gray-900 amoled:bg-black"> |
|
76
|
<Header /> |
|
77
|
|
|
78
|
<div className="container mx-auto py-8 px-4"> |
|
79
|
<div className="max-w-6xl mx-auto"> |
|
80
|
<div className="text-center mb-8"> |
|
81
|
<h1 className="text-4xl font-bold mb-4 text-gray-900 dark:text-white amoled:text-white"> |
|
82
|
Device Finder |
|
83
|
</h1> |
|
84
|
<p className="text-xl text-gray-600 dark:text-gray-300 amoled:text-gray-300"> |
|
85
|
Find specific repair guides for your laptop or desktop computer model |
|
86
|
</p> |
|
87
|
<div className="mt-6 flex flex-wrap items-center justify-center gap-4 text-sm text-gray-500 dark:text-gray-400 amoled:text-gray-400"> |
|
88
|
<div className="flex items-center gap-2"> |
|
89
|
<Laptop className="h-4 w-4 text-primary-600" /> |
|
90
|
<span>25+ Device Brands</span> |
|
91
|
</div> |
|
92
|
<Separator orientation="vertical" className="h-4" /> |
|
93
|
<div className="flex items-center gap-2"> |
|
94
|
<Monitor className="h-4 w-4 text-primary-600" /> |
|
95
|
<span>150+ Device Models</span> |
|
96
|
</div> |
|
97
|
<Separator orientation="vertical" className="h-4" /> |
|
98
|
<div className="flex items-center gap-2"> |
|
99
|
<Wrench className="h-4 w-4 text-primary-600" /> |
|
100
|
<span>Compatibility Verified</span> |
|
101
|
</div> |
|
102
|
</div> |
|
103
|
</div> |
|
104
|
|
|
105
|
<Tabs value={selectedDeviceType} onValueChange={(value) => { |
|
106
|
setSelectedDeviceType(value as "laptop" | "desktop"); |
|
107
|
setSelectedBrand(""); |
|
108
|
setSelectedModel(""); |
|
109
|
setSearchQuery(""); |
|
110
|
}} className="mb-8"> |
|
111
|
<TabsList className="grid w-full grid-cols-2"> |
|
112
|
<TabsTrigger value="laptop" className="flex items-center gap-2"> |
|
113
|
<Laptop className="h-4 w-4" /> |
|
114
|
Laptops |
|
115
|
</TabsTrigger> |
|
116
|
<TabsTrigger value="desktop" className="flex items-center gap-2"> |
|
117
|
<Monitor className="h-4 w-4" /> |
|
118
|
Desktops |
|
119
|
</TabsTrigger> |
|
120
|
</TabsList> |
|
121
|
|
|
122
|
<TabsContent value={selectedDeviceType} className="space-y-6"> |
|
123
|
{/* Search Section */} |
|
124
|
<Card> |
|
125
|
<CardHeader> |
|
126
|
<CardTitle className="flex items-center gap-2"> |
|
127
|
<Search className="h-5 w-5" /> |
|
128
|
Quick Search |
|
129
|
</CardTitle> |
|
130
|
<CardDescription> |
|
131
|
Search for your specific device model |
|
132
|
</CardDescription> |
|
133
|
</CardHeader> |
|
134
|
<CardContent> |
|
135
|
<div className="relative"> |
|
136
|
<Input |
|
137
|
placeholder={`Search ${selectedDeviceType} models...`} |
|
138
|
value={searchQuery} |
|
139
|
onChange={(e) => setSearchQuery(e.target.value)} |
|
140
|
className="pr-10" |
|
141
|
/> |
|
142
|
<Search className="absolute right-3 top-3 h-4 w-4 text-muted-foreground" /> |
|
143
|
</div> |
|
144
|
|
|
145
|
{searchResults.length > 0 && ( |
|
146
|
<div className="mt-4 grid gap-3"> |
|
147
|
{searchResults.slice(0, 5).map((model) => ( |
|
148
|
<Card key={model.id} className="cursor-pointer hover:bg-accent" onClick={() => { |
|
149
|
setSelectedModel(model.id); |
|
150
|
setSelectedBrand(model.brandId); |
|
151
|
setSearchQuery(""); |
|
152
|
}}> |
|
153
|
<CardContent className="p-4"> |
|
154
|
<div className="flex justify-between items-center"> |
|
155
|
<div> |
|
156
|
<h4 className="font-semibold">{model.name}</h4> |
|
157
|
<p className="text-sm text-muted-foreground">{model.series} • {model.year}</p> |
|
158
|
</div> |
|
159
|
<Badge variant="secondary">{model.guideCount} guides</Badge> |
|
160
|
</div> |
|
161
|
</CardContent> |
|
162
|
</Card> |
|
163
|
))} |
|
164
|
</div> |
|
165
|
)} |
|
166
|
</CardContent> |
|
167
|
</Card> |
|
168
|
|
|
169
|
{/* Brand Selection */} |
|
170
|
<Card> |
|
171
|
<CardHeader> |
|
172
|
<CardTitle>Browse by Brand</CardTitle> |
|
173
|
<CardDescription> |
|
174
|
Select your device brand to see compatible models |
|
175
|
</CardDescription> |
|
176
|
</CardHeader> |
|
177
|
<CardContent> |
|
178
|
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3"> |
|
179
|
{brands.map((brand) => ( |
|
180
|
<Card |
|
181
|
key={brand.id} |
|
182
|
className={`cursor-pointer transition-colors ${ |
|
183
|
selectedBrand === brand.id ? "ring-2 ring-primary" : "hover:bg-accent" |
|
184
|
}`} |
|
185
|
onClick={() => { |
|
186
|
setSelectedBrand(brand.id); |
|
187
|
setSelectedModel(""); |
|
188
|
}} |
|
189
|
> |
|
190
|
<CardContent className="p-4"> |
|
191
|
<div className="flex justify-between items-center mb-2"> |
|
192
|
<h3 className="font-semibold">{brand.name}</h3> |
|
193
|
<Badge className={getSupportLevelColor(brand.supportLevel)}> |
|
194
|
{brand.supportLevel} |
|
195
|
</Badge> |
|
196
|
</div> |
|
197
|
<div className="flex items-center gap-2 text-sm text-muted-foreground"> |
|
198
|
<Star className="h-4 w-4" /> |
|
199
|
{brand.popularity} popularity |
|
200
|
</div> |
|
201
|
</CardContent> |
|
202
|
</Card> |
|
203
|
))} |
|
204
|
</div> |
|
205
|
</CardContent> |
|
206
|
</Card> |
|
207
|
|
|
208
|
{/* Model Selection */} |
|
209
|
{selectedBrand && models.length > 0 && ( |
|
210
|
<Card> |
|
211
|
<CardHeader> |
|
212
|
<CardTitle>Select Your Model</CardTitle> |
|
213
|
<CardDescription> |
|
214
|
Choose your specific device model |
|
215
|
</CardDescription> |
|
216
|
</CardHeader> |
|
217
|
<CardContent> |
|
218
|
<div className="grid gap-3 md:grid-cols-2"> |
|
219
|
{models.map((model) => ( |
|
220
|
<Card |
|
221
|
key={model.id} |
|
222
|
className={`cursor-pointer transition-colors ${ |
|
223
|
selectedModel === model.id ? "ring-2 ring-primary" : "hover:bg-accent" |
|
224
|
}`} |
|
225
|
onClick={() => setSelectedModel(model.id)} |
|
226
|
> |
|
227
|
<CardContent className="p-4"> |
|
228
|
<div className="flex justify-between items-center mb-2"> |
|
229
|
<h4 className="font-semibold">{model.name}</h4> |
|
230
|
<Badge variant="outline">{model.year}</Badge> |
|
231
|
</div> |
|
232
|
<p className="text-sm text-muted-foreground mb-2">{model.series}</p> |
|
233
|
<div className="flex items-center gap-2 text-sm"> |
|
234
|
<Wrench className="h-4 w-4" /> |
|
235
|
{model.guideCount} repair guides |
|
236
|
</div> |
|
237
|
</CardContent> |
|
238
|
</Card> |
|
239
|
))} |
|
240
|
</div> |
|
241
|
</CardContent> |
|
242
|
</Card> |
|
243
|
)} |
|
244
|
|
|
245
|
{/* Model Details and Compatible Guides */} |
|
246
|
{selectedModelData && ( |
|
247
|
<div className="grid gap-6 lg:grid-cols-3"> |
|
248
|
{/* Model Details */} |
|
249
|
<Card className="lg:col-span-1"> |
|
250
|
<CardHeader> |
|
251
|
<div className="flex items-center justify-between"> |
|
252
|
<div> |
|
253
|
<CardTitle className="text-lg">{selectedModelData.name}</CardTitle> |
|
254
|
<CardDescription> |
|
255
|
{selectedModelData.series} • {selectedModelData.year} |
|
256
|
</CardDescription> |
|
257
|
</div> |
|
258
|
<Badge variant="outline" className="bg-primary-50 dark:bg-primary-900 text-primary-700 dark:text-primary-300"> |
|
259
|
{selectedModelData.guideCount} guides |
|
260
|
</Badge> |
|
261
|
</div> |
|
262
|
</CardHeader> |
|
263
|
<CardContent className="space-y-4"> |
|
264
|
{selectedModelData.specifications && ( |
|
265
|
<div className="space-y-3"> |
|
266
|
<h4 className="font-semibold text-sm text-gray-900 dark:text-white amoled:text-white flex items-center gap-2"> |
|
267
|
<i className="material-icons text-sm">computer</i> |
|
268
|
Specifications |
|
269
|
</h4> |
|
270
|
<div className="text-sm space-y-2 bg-gray-50 dark:bg-gray-800 amoled:bg-gray-900 p-3 rounded-lg"> |
|
271
|
{Object.entries(selectedModelData.specifications as Record<string, any>).map(([key, value]) => ( |
|
272
|
<div key={key} className="flex justify-between items-center"> |
|
273
|
<span className="capitalize text-gray-600 dark:text-gray-400 amoled:text-gray-400 font-medium"> |
|
274
|
{key}: |
|
275
|
</span> |
|
276
|
<span className="text-gray-900 dark:text-white amoled:text-white">{String(value)}</span> |
|
277
|
</div> |
|
278
|
))} |
|
279
|
</div> |
|
280
|
</div> |
|
281
|
)} |
|
282
|
|
|
283
|
{selectedModelData.commonIssues && (selectedModelData.commonIssues as any[]).length > 0 && ( |
|
284
|
<div className="space-y-3"> |
|
285
|
<h4 className="font-semibold text-sm text-gray-900 dark:text-white amoled:text-white flex items-center gap-2"> |
|
286
|
<i className="material-icons text-sm text-red-600">warning</i> |
|
287
|
Common Issues |
|
288
|
</h4> |
|
289
|
<div className="flex flex-wrap gap-2"> |
|
290
|
{(selectedModelData.commonIssues as any[]).map((issue, index) => ( |
|
291
|
<Badge key={index} variant="destructive" className="text-xs"> |
|
292
|
{String(issue)} |
|
293
|
</Badge> |
|
294
|
))} |
|
295
|
</div> |
|
296
|
</div> |
|
297
|
)} |
|
298
|
</CardContent> |
|
299
|
</Card> |
|
300
|
|
|
301
|
{/* Compatible Guides */} |
|
302
|
<Card className="lg:col-span-2"> |
|
303
|
<CardHeader> |
|
304
|
<div className="flex items-center justify-between"> |
|
305
|
<div> |
|
306
|
<CardTitle className="flex items-center gap-2"> |
|
307
|
<Wrench className="h-5 w-5 text-primary-600" /> |
|
308
|
Compatible Repair Guides |
|
309
|
</CardTitle> |
|
310
|
<CardDescription> |
|
311
|
{filteredGuides.length} of {compatibleGuides.length} guides match your filters |
|
312
|
</CardDescription> |
|
313
|
</div> |
|
314
|
|
|
315
|
{/* Guide Filters */} |
|
316
|
<div className="flex items-center gap-2"> |
|
317
|
<Select value={categoryFilter} onValueChange={setCategoryFilter}> |
|
318
|
<SelectTrigger className="w-36"> |
|
319
|
<Filter className="h-4 w-4 mr-1" /> |
|
320
|
<SelectValue placeholder="Category" /> |
|
321
|
</SelectTrigger> |
|
322
|
<SelectContent> |
|
323
|
<SelectItem value="all">All Categories</SelectItem> |
|
324
|
<SelectItem value="hardware">Hardware</SelectItem> |
|
325
|
<SelectItem value="software">Software</SelectItem> |
|
326
|
<SelectItem value="cleaning">Cleaning</SelectItem> |
|
327
|
<SelectItem value="upgrades">Upgrades</SelectItem> |
|
328
|
</SelectContent> |
|
329
|
</Select> |
|
330
|
|
|
331
|
<Select value={guideFilter} onValueChange={setGuideFilter}> |
|
332
|
<SelectTrigger className="w-32"> |
|
333
|
<SelectValue placeholder="Difficulty" /> |
|
334
|
</SelectTrigger> |
|
335
|
<SelectContent> |
|
336
|
<SelectItem value="all">All Levels</SelectItem> |
|
337
|
<SelectItem value="easy">Easy</SelectItem> |
|
338
|
<SelectItem value="medium">Medium</SelectItem> |
|
339
|
<SelectItem value="hard">Hard</SelectItem> |
|
340
|
</SelectContent> |
|
341
|
</Select> |
|
342
|
</div> |
|
343
|
</div> |
|
344
|
</CardHeader> |
|
345
|
<CardContent> |
|
346
|
<div className="space-y-3 max-h-96 overflow-y-auto"> |
|
347
|
{filteredGuides.map((guide) => ( |
|
348
|
<Card |
|
349
|
key={guide.id} |
|
350
|
className="hover:bg-gray-50 dark:hover:bg-gray-800 amoled:hover:bg-gray-900 cursor-pointer border-l-4 border-l-transparent hover:border-l-primary-500 transition-all duration-200" |
|
351
|
onClick={() => handleGuideClick(guide.id)} |
|
352
|
> |
|
353
|
<CardContent className="p-4"> |
|
354
|
<div className="flex justify-between items-start mb-2"> |
|
355
|
<h5 className="font-semibold text-gray-900 dark:text-white amoled:text-white text-sm"> |
|
356
|
{guide.title} |
|
357
|
</h5> |
|
358
|
<div className="flex items-center gap-2"> |
|
359
|
<Badge |
|
360
|
variant={ |
|
361
|
guide.difficulty === "easy" ? "default" : |
|
362
|
guide.difficulty === "medium" ? "secondary" : "destructive" |
|
363
|
} |
|
364
|
className="text-xs" |
|
365
|
> |
|
366
|
{guide.difficulty} |
|
367
|
</Badge> |
|
368
|
<Badge variant="outline" className="text-xs"> |
|
369
|
{guide.category} |
|
370
|
</Badge> |
|
371
|
</div> |
|
372
|
</div> |
|
373
|
<p className="text-sm text-gray-600 dark:text-gray-300 amoled:text-gray-300 mb-3 line-clamp-2"> |
|
374
|
{guide.description} |
|
375
|
</p> |
|
376
|
<div className="flex items-center justify-between"> |
|
377
|
<div className="flex items-center gap-4 text-xs text-gray-500 dark:text-gray-400 amoled:text-gray-400"> |
|
378
|
<span className="flex items-center gap-1"> |
|
379
|
<i className="material-icons text-xs">schedule</i> |
|
380
|
{guide.estimatedTime} |
|
381
|
</span> |
|
382
|
<span className="flex items-center gap-1"> |
|
383
|
<Eye className="h-3 w-3" /> |
|
384
|
{guide.viewCount} |
|
385
|
</span> |
|
386
|
<span className="flex items-center gap-1"> |
|
387
|
<Download className="h-3 w-3" /> |
|
388
|
{guide.downloadCount} |
|
389
|
</span> |
|
390
|
</div> |
|
391
|
<Button |
|
392
|
size="sm" |
|
393
|
variant="ghost" |
|
394
|
className="h-7 px-2 text-primary-600 hover:text-primary-700 hover:bg-primary-50 dark:hover:bg-primary-900" |
|
395
|
onClick={(e) => { |
|
396
|
e.stopPropagation(); |
|
397
|
handleGuideClick(guide.id); |
|
398
|
}} |
|
399
|
> |
|
400
|
<ExternalLink className="h-3 w-3 mr-1" /> |
|
401
|
View |
|
402
|
</Button> |
|
403
|
</div> |
|
404
|
</CardContent> |
|
405
|
</Card> |
|
406
|
))} |
|
407
|
|
|
408
|
{filteredGuides.length === 0 && compatibleGuides.length > 0 && ( |
|
409
|
<div className="text-center py-8 text-gray-500 dark:text-gray-400 amoled:text-gray-400"> |
|
410
|
<Filter className="h-12 w-12 mx-auto mb-4 opacity-50" /> |
|
411
|
<p className="font-medium">No guides match your current filters</p> |
|
412
|
<p className="text-sm">Try adjusting the difficulty or category filters above</p> |
|
413
|
<Button |
|
414
|
variant="outline" |
|
415
|
size="sm" |
|
416
|
className="mt-3" |
|
417
|
onClick={() => { |
|
418
|
setGuideFilter("all"); |
|
419
|
setCategoryFilter("all"); |
|
420
|
}} |
|
421
|
> |
|
422
|
Clear Filters |
|
423
|
</Button> |
|
424
|
</div> |
|
425
|
)} |
|
426
|
|
|
427
|
{compatibleGuides.length === 0 && ( |
|
428
|
<div className="text-center py-8 text-gray-500 dark:text-gray-400 amoled:text-gray-400"> |
|
429
|
<Wrench className="h-12 w-12 mx-auto mb-4 opacity-50" /> |
|
430
|
<p className="font-medium">No guides available yet for this model</p> |
|
431
|
<p className="text-sm">Check back later or browse our general repair guides</p> |
|
432
|
<Button |
|
433
|
variant="outline" |
|
434
|
size="sm" |
|
435
|
className="mt-3" |
|
436
|
onClick={() => setLocation("/")} |
|
437
|
> |
|
438
|
Browse All Guides |
|
439
|
</Button> |
|
440
|
</div> |
|
441
|
)} |
|
442
|
</div> |
|
443
|
</CardContent> |
|
444
|
</Card> |
|
445
|
</div> |
|
446
|
)} |
|
447
|
</TabsContent> |
|
448
|
</Tabs> |
|
449
|
|
|
450
|
{/* Popular Devices Section */} |
|
451
|
<div className="mt-12 text-center"> |
|
452
|
<h2 className="text-2xl font-bold mb-4 text-gray-900 dark:text-white amoled:text-white"> |
|
453
|
Popular Device Categories |
|
454
|
</h2> |
|
455
|
<div className="grid md:grid-cols-2 lg:grid-cols-4 gap-4"> |
|
456
|
<Card className="hover:bg-gray-50 dark:hover:bg-gray-800 amoled:hover:bg-gray-900 cursor-pointer transition-colors"> |
|
457
|
<CardContent className="p-6 text-center"> |
|
458
|
<div className="w-12 h-12 bg-blue-100 dark:bg-blue-900 rounded-lg flex items-center justify-center mx-auto mb-3"> |
|
459
|
<i className="material-icons text-blue-600 dark:text-blue-300">laptop_mac</i> |
|
460
|
</div> |
|
461
|
<h3 className="font-semibold text-gray-900 dark:text-white amoled:text-white mb-1">MacBook Series</h3> |
|
462
|
<p className="text-sm text-gray-600 dark:text-gray-300 amoled:text-gray-300">15+ models supported</p> |
|
463
|
</CardContent> |
|
464
|
</Card> |
|
465
|
|
|
466
|
<Card className="hover:bg-gray-50 dark:hover:bg-gray-800 amoled:hover:bg-gray-900 cursor-pointer transition-colors"> |
|
467
|
<CardContent className="p-6 text-center"> |
|
468
|
<div className="w-12 h-12 bg-red-100 dark:bg-red-900 rounded-lg flex items-center justify-center mx-auto mb-3"> |
|
469
|
<i className="material-icons text-red-600 dark:text-red-300">laptop_windows</i> |
|
470
|
</div> |
|
471
|
<h3 className="font-semibold text-gray-900 dark:text-white amoled:text-white mb-1">ThinkPad Series</h3> |
|
472
|
<p className="text-sm text-gray-600 dark:text-gray-300 amoled:text-gray-300">20+ models supported</p> |
|
473
|
</CardContent> |
|
474
|
</Card> |
|
475
|
|
|
476
|
<Card className="hover:bg-gray-50 dark:hover:bg-gray-800 amoled:hover:bg-gray-900 cursor-pointer transition-colors"> |
|
477
|
<CardContent className="p-6 text-center"> |
|
478
|
<div className="w-12 h-12 bg-green-100 dark:bg-green-900 rounded-lg flex items-center justify-center mx-auto mb-3"> |
|
479
|
<i className="material-icons text-green-600 dark:text-green-300">desktop_windows</i> |
|
480
|
</div> |
|
481
|
<h3 className="font-semibold text-gray-900 dark:text-white amoled:text-white mb-1">Gaming PCs</h3> |
|
482
|
<p className="text-sm text-gray-600 dark:text-gray-300 amoled:text-gray-300">Custom builds supported</p> |
|
483
|
</CardContent> |
|
484
|
</Card> |
|
485
|
|
|
486
|
<Card className="hover:bg-gray-50 dark:hover:bg-gray-800 amoled:hover:bg-gray-900 cursor-pointer transition-colors"> |
|
487
|
<CardContent className="p-6 text-center"> |
|
488
|
<div className="w-12 h-12 bg-purple-100 dark:bg-purple-900 rounded-lg flex items-center justify-center mx-auto mb-3"> |
|
489
|
<i className="material-icons text-purple-600 dark:text-purple-300">business</i> |
|
490
|
</div> |
|
491
|
<h3 className="font-semibold text-gray-900 dark:text-white amoled:text-white mb-1">Business Laptops</h3> |
|
492
|
<p className="text-sm text-gray-600 dark:text-gray-300 amoled:text-gray-300">Enterprise solutions</p> |
|
493
|
</CardContent> |
|
494
|
</Card> |
|
495
|
</div> |
|
496
|
</div> |
|
497
|
</div> |
|
498
|
</div> |
|
499
|
|
|
500
|
<Footer /> |
|
501
|
</div> |
|
502
|
); |
|
503
|
} |