|
| 1 | +import { useState } from "react"; |
1 | 2 | import { personalInfo } from "@/data/personalInfo"; |
2 | 3 | import MatrixRain from "@/components/MatrixRain"; |
3 | 4 | import TerminalFooter from "@/components/TerminalFooter"; |
4 | 5 | import TerminalHeader from "@/components/TerminalHeader"; |
5 | 6 |
|
| 7 | +const ALL_TECHS = [ |
| 8 | + ...new Set(personalInfo.projects.flatMap((p) => p.technologies)), |
| 9 | +]; |
| 10 | + |
6 | 11 | const Projects = () => { |
| 12 | + const [activeTech, setActiveTech] = useState<string | null>(null); |
| 13 | + |
| 14 | + const filteredProjects = activeTech |
| 15 | + ? personalInfo.projects.filter((p) => p.technologies.includes(activeTech)) |
| 16 | + : personalInfo.projects; |
7 | 17 |
|
8 | 18 | return ( |
9 | 19 | <div |
@@ -36,22 +46,63 @@ const Projects = () => { |
36 | 46 |
|
37 | 47 | <div style={{ padding: "20px 24px" }}> |
38 | 48 | {/* Command header */} |
39 | | - <div style={{ marginBottom: "20px", fontSize: "13px" }}> |
| 49 | + <div style={{ marginBottom: "16px", fontSize: "13px" }}> |
40 | 50 | <div style={{ display: "flex", alignItems: "center", gap: "6px", marginBottom: "4px" }}> |
41 | 51 | <span style={{ color: "var(--term-green)" }}>spectual</span> |
42 | 52 | <span style={{ color: "var(--term-dim)" }}>@</span> |
43 | 53 | <span style={{ color: "var(--term-blue)" }}>github.io</span> |
44 | 54 | <span style={{ color: "var(--term-text)" }}>:~$</span> |
45 | | - <span style={{ color: "var(--term-text)", marginLeft: "4px" }}>ls -la ~/projects</span> |
| 55 | + <span style={{ color: "var(--term-text)", marginLeft: "4px" }}> |
| 56 | + {activeTech |
| 57 | + ? `grep -i "${activeTech}" projects/` |
| 58 | + : "ls -la ~/projects"} |
| 59 | + </span> |
46 | 60 | </div> |
47 | 61 | <div style={{ color: "var(--term-dim)", fontSize: "11px" }}> |
48 | | - total {personalInfo.projects.length} entries |
| 62 | + {filteredProjects.length === personalInfo.projects.length |
| 63 | + ? `total ${personalInfo.projects.length} entries` |
| 64 | + : `${filteredProjects.length} match${filteredProjects.length !== 1 ? "es" : ""} (${personalInfo.projects.length} total)`} |
| 65 | + </div> |
| 66 | + </div> |
| 67 | + |
| 68 | + {/* Tech stack filter */} |
| 69 | + <div |
| 70 | + style={{ |
| 71 | + marginBottom: "20px", |
| 72 | + padding: "10px 12px", |
| 73 | + border: "1px solid var(--term-border)", |
| 74 | + backgroundColor: "var(--term-bg2)", |
| 75 | + fontSize: "11px", |
| 76 | + }} |
| 77 | + > |
| 78 | + <div style={{ color: "var(--term-dim)", marginBottom: "8px" }}> |
| 79 | + # filter by tech — click to toggle |
| 80 | + </div> |
| 81 | + <div style={{ display: "flex", flexWrap: "wrap", gap: "4px" }}> |
| 82 | + {ALL_TECHS.map((tech) => ( |
| 83 | + <button |
| 84 | + key={tech} |
| 85 | + onClick={() => setActiveTech(activeTech === tech ? null : tech)} |
| 86 | + style={{ |
| 87 | + border: `1px solid ${activeTech === tech ? "var(--term-green)" : "var(--term-border)"}`, |
| 88 | + backgroundColor: activeTech === tech ? "rgba(57,255,20,0.08)" : "transparent", |
| 89 | + color: activeTech === tech ? "var(--term-green)" : "var(--term-dim)", |
| 90 | + fontSize: "10px", |
| 91 | + padding: "2px 8px", |
| 92 | + cursor: "pointer", |
| 93 | + fontFamily: "inherit", |
| 94 | + transition: "all 0.15s", |
| 95 | + }} |
| 96 | + > |
| 97 | + {tech} |
| 98 | + </button> |
| 99 | + ))} |
49 | 100 | </div> |
50 | 101 | </div> |
51 | 102 |
|
52 | 103 | {/* Projects grid */} |
53 | 104 | <div className="grid gap-4 lg:grid-cols-2"> |
54 | | - {personalInfo.projects.map((project, index) => { |
| 105 | + {filteredProjects.map((project, index) => { |
55 | 106 | const isPatent = project.name.includes("Patent"); |
56 | 107 | const hasGithub = "githubUrl" in project && project.githubUrl; |
57 | 108 | const hasLive = "liveUrl" in project && project.liveUrl; |
@@ -156,8 +207,8 @@ const Projects = () => { |
156 | 207 | <span |
157 | 208 | key={tech} |
158 | 209 | style={{ |
159 | | - border: "1px solid var(--term-border)", |
160 | | - color: "var(--term-dim)", |
| 210 | + border: `1px solid ${activeTech === tech ? "var(--term-green)" : "var(--term-border)"}`, |
| 211 | + color: activeTech === tech ? "var(--term-green)" : "var(--term-dim)", |
161 | 212 | fontSize: "10px", |
162 | 213 | padding: "1px 6px", |
163 | 214 | fontFamily: "inherit", |
|
0 commit comments