|
1
|
import { useQuery } from "@tanstack/react-query"; |
|
2
|
import type { RepairGuide } from "@shared/schema"; |
|
3
|
import { Cpu, Code, Eraser, MoveUp, XCircle } from "lucide-react"; |
|
4
|
import { cn } from "@/lib/utils"; |
|
5
|
|
|
6
|
interface CategoryGridProps { |
|
7
|
selectedCategory: string; |
|
8
|
onCategoryChange: (category: string) => void; |
|
9
|
deviceType: string; |
|
10
|
} |
|
11
|
|
|
12
|
export default function CategoryGrid({ selectedCategory, onCategoryChange, deviceType }: CategoryGridProps) { |
|
13
|
const { data: allGuides = [] } = useQuery<RepairGuide[]>({ |
|
14
|
queryKey: ["api", "repair-guides"], |
|
15
|
}); |
|
16
|
|
|
17
|
const categories = [ |
|
18
|
{ |
|
19
|
id: "hardware", |
|
20
|
name: "Hardware", |
|
21
|
description: "Components & builds", |
|
22
|
icon: Cpu, |
|
23
|
color: "text-indigo-500", |
|
24
|
bgColor: "bg-indigo-500/10", |
|
25
|
}, |
|
26
|
{ |
|
27
|
id: "software", |
|
28
|
name: "Software", |
|
29
|
description: "OS & Driver fixes", |
|
30
|
icon: Code, |
|
31
|
color: "text-emerald-500", |
|
32
|
bgColor: "bg-emerald-500/10", |
|
33
|
}, |
|
34
|
{ |
|
35
|
id: "cleaning", |
|
36
|
name: "Cleaning", |
|
37
|
description: "Pro maintenance", |
|
38
|
icon: Eraser, |
|
39
|
color: "text-amber-500", |
|
40
|
bgColor: "bg-amber-500/10", |
|
41
|
}, |
|
42
|
{ |
|
43
|
id: "upgrades", |
|
44
|
name: "Upgrades", |
|
45
|
description: "Performance boosts", |
|
46
|
icon: MoveUp, |
|
47
|
color: "text-purple-500", |
|
48
|
bgColor: "bg-purple-500/10", |
|
49
|
}, |
|
50
|
]; |
|
51
|
|
|
52
|
const getCategoryCount = (categoryId: string) => { |
|
53
|
return allGuides.filter(guide => |
|
54
|
guide.category === categoryId && |
|
55
|
(!deviceType || guide.deviceType === deviceType) |
|
56
|
).length; |
|
57
|
}; |
|
58
|
|
|
59
|
return ( |
|
60
|
<div className="grid grid-cols-2 lg:grid-cols-4 gap-4 sm:gap-6"> |
|
61
|
{categories.map((category) => { |
|
62
|
const isActive = selectedCategory === category.id; |
|
63
|
const Icon = category.icon; |
|
64
|
|
|
65
|
return ( |
|
66
|
<button |
|
67
|
key={category.id} |
|
68
|
onClick={() => onCategoryChange(isActive ? "" : category.id)} |
|
69
|
className={cn( |
|
70
|
"group relative p-6 rounded-[2rem] border transition-all duration-500 text-left overflow-hidden", |
|
71
|
isActive |
|
72
|
? "bg-primary border-primary shadow-glass text-primary-foreground" |
|
73
|
: "bg-card/40 backdrop-blur-sm border-border/40 hover:border-primary/40 hover:shadow-elegant" |
|
74
|
)} |
|
75
|
> |
|
76
|
<div className={cn( |
|
77
|
"h-12 w-12 rounded-2xl flex items-center justify-center mb-6 transition-all duration-500", |
|
78
|
isActive ? "bg-white/20" : category.bgColor, |
|
79
|
!isActive && "group-hover:scale-110" |
|
80
|
)}> |
|
81
|
<Icon className={cn("h-6 w-6", isActive ? "text-white" : category.color)} /> |
|
82
|
</div> |
|
83
|
|
|
84
|
<h3 className="font-bold text-lg mb-1">{category.name}</h3> |
|
85
|
<p className={cn( |
|
86
|
"text-xs mb-4 line-clamp-1", |
|
87
|
isActive ? "text-white/80" : "text-muted-foreground" |
|
88
|
)}> |
|
89
|
{category.description} |
|
90
|
</p> |
|
91
|
|
|
92
|
<div className="flex items-center justify-between"> |
|
93
|
<span className={cn( |
|
94
|
"text-[10px] font-black uppercase tracking-widest px-2.5 py-1 rounded-full", |
|
95
|
isActive ? "bg-white/20 text-white" : "bg-muted text-muted-foreground" |
|
96
|
)}> |
|
97
|
{getCategoryCount(category.id)} guides |
|
98
|
</span> |
|
99
|
|
|
100
|
{isActive && <XCircle className="h-4 w-4 text-white/60 animate-in fade-in zoom-in" />} |
|
101
|
</div> |
|
102
|
|
|
103
|
{/* Decorative background glow for inactive items */} |
|
104
|
{!isActive && ( |
|
105
|
<div className={cn( |
|
106
|
"absolute -right-4 -bottom-4 h-16 w-16 opacity-0 group-hover:opacity-20 blur-2xl rounded-full transition-opacity duration-500", |
|
107
|
category.bgColor.replace("/10", "") |
|
108
|
)} /> |
|
109
|
)} |
|
110
|
</button> |
|
111
|
); |
|
112
|
})} |
|
113
|
</div> |
|
114
|
); |
|
115
|
} |