My personal site hosted @ https://indexx.dev
1import { useEffect, useState } from "react";
2
3export default function NowPlaying() {
4 const [data, setData] = useState(null);
5 const [error, setError] = useState(null);
6
7 useEffect(() => {
8 const fetchProjectData = async () => {
9 try {
10 const res = await fetch(
11 "https://pds.indexx.dev/xrpc/com.atproto.repo.listRecords?repo=did%3Aplc%3Asfjxpxxyvewb2zlxwoz2vduw&collection=dev.indexx.www.project&limit=100&reverse=false",
12 );
13 if (!res.ok) throw new Error(`HTTP ${res.status}`);
14
15 const data = await res.json();
16 const projects = data.records.map((record) => record.value);
17
18 setData(projects.map((project) => ({
19 title: project.name,
20 description: project.description,
21 date: project.dateLabel,
22 href: project.url,
23 note: project.note ?? "",
24 })));
25 } catch (err) {
26 console.error("Fetch failed:", err);
27 setError(err.message);
28 }
29 };
30
31 fetchProjectData();
32 }, []);
33
34 if (error) return <span>Error: {error}</span>;
35 if (!data) return null;
36
37 return (
38 <section id="projects-pane" data-bs-theme="dark">
39 <h6
40 className="text-muted text-center"
41 style={{ textTransform: "uppercase", letterSpacing: "5px" }}
42 >
43 My Recent Projects
44 </h6>
45
46 <ul className="list-unstyled">
47 {data.map((project) => (
48 <li>
49 <a
50 href={project.href}
51 target="_blank"
52 className="text-reset"
53 >
54 <div
55 className="project-card"
56 style={{ color: "#fff" }}
57 >
58 <small className="text-muted">
59 {project.date}
60 </small>
61 <h4>{project.title}</h4>
62 {project.description}
63 {project.note && (
64 <>
65 <br />
66 <small className="text-muted">
67 {project.note}
68 </small>
69 </>
70 )}
71 </div>
72 </a>
73 </li>
74 ))}
75 </ul>
76 </section>
77 );
78}