Files
zshame/app/page.tsx
2026-06-05 03:02:41 +03:00

105 lines
2.9 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client";
import { useEffect, useMemo, useState } from "react";
type ShamePhoto = {
name: string;
path: string;
url: string;
htmlUrl: string;
};
type PhotoResponse = {
photos: ShamePhoto[];
repo: string | null;
path: string;
branch?: string | null;
message: string | null;
};
export default function Home() {
const [data, setData] = useState<PhotoResponse | null>(null);
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
const controller = new AbortController();
async function loadPhotos() {
setIsLoading(true);
setError(null);
try {
const response = await fetch("/api/github-photos", {
signal: controller.signal
});
const nextData = (await response.json()) as PhotoResponse;
setData(nextData);
if (!response.ok) {
setError(nextData.message || "Could not load photos from GitHub.");
}
} catch (nextError) {
if (nextError instanceof DOMException && nextError.name === "AbortError") {
return;
}
setError("Could not reach the photo source.");
} finally {
setIsLoading(false);
}
}
loadPhotos();
return () => controller.abort();
}, []);
const repoLabel = data?.repo ?? "GitHub source not set";
const boardStamp = useMemo(() => {
const count = data?.photos.length ?? 0;
if (count === 0) {
return "Empty";
}
return `${count} filed`;
}, [data?.photos.length]);
return (
<main className="shell">
<section className="hero" aria-labelledby="page-title">
<div className="heroText">
<h1 id="page-title">Доска позора</h1>
<p className="summary">
тут только самое позорное.
</p>
</div>
<div className="caseTag" aria-label="Board status">
<span>{boardStamp}</span>
<strong>{repoLabel}</strong>
</div>
</section>
<section className="board" aria-live="polite" aria-busy={isLoading}>
{isLoading ? <div className="notice">Загружаю фотографии с GitHub...</div> : null}
{!isLoading && error ? <div className="notice danger">{error}</div> : null}
{!isLoading && !error && data?.message ? <div className="notice">{data.message}</div> : null}
{data?.photos.map((photo, index) => (
<article className="photoCard" key={photo.path}>
<a href={photo.htmlUrl} target="_blank" rel="noreferrer" aria-label={`Open ${photo.name} on GitHub`}>
<img src={photo.url} alt={photo.name} loading={index < 3 ? "eager" : "lazy"} />
</a>
<div className="caption">
<span>#{String(index + 1).padStart(2, "0")}</span>
<strong>{photo.name}</strong>
</div>
</article>
))}
</section>
</main>
);
}