makearmy-app/app/projects/[id]/projects.tsx

153 lines
4.4 KiB
TypeScript

"use client";
import { useEffect, useState } from "react";
import { useParams } from "next/navigation";
import Image from "next/image";
import Link from "next/link";
import ReactMarkdown from "react-markdown";
export default function ProjectDetailPage() {
const { id } = useParams();
const [project, setProject] = useState(null);
useEffect(() => {
if (!id) return;
const url = new URL(`${process.env.NEXT_PUBLIC_API_BASE_URL}/items/projects/${id}`);
url.searchParams.set(
"fields",
"submission_id,title,uploader,category,tags,p_image.filename_disk,p_image.title,p_files.directus_files_id.filename_disk"
);
url.searchParams.set("limit", "1");
fetch(url.toString())
.then((res) => res.json())
.then((data) => setProject(data.data))
.catch(() => setProject(null));
}, [id]);
if (!project) {
return (
<div className="p-6 max-w-5xl mx-auto">
<div className="mb-4">
<Link href="/projects" className="text-accent underline">
Back to Projects
</Link>
</div>
<p className="text-muted-foreground">Loading project</p>
</div>
);
}
const imageSrc = project.p_image?.filename_disk
? `${process.env.NEXT_PUBLIC_ASSET_URL || "https://forms.lasereverything.net"}/assets/${project.p_image.filename_disk}`
: null;
const fileList: string[] = Array.isArray(project.p_files)
? project.p_files
.map((f: any) => f?.directus_files_id?.filename_disk)
.filter(Boolean)
: [];
return (
<div className="p-6 max-w-5xl mx-auto">
<style jsx global>{`
.file-pill {
display: inline-block;
font-size: 0.8rem;
padding: 0.25rem 0.5rem;
background-color: var(--muted);
color: var(--foreground);
border-radius: 0.25rem;
margin-right: 0.25rem;
margin-bottom: 0.25rem;
transition: background-color 0.2s ease;
}
.file-pill:hover {
background-color: #ffde59;
color: #000;
}
`}</style>
<div className="mb-4">
<Link href="/projects" className="text-accent underline">
Back to Projects
</Link>
</div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
<div className="md:col-span-1">
<div className="border rounded overflow-hidden bg-card">
{imageSrc ? (
<Image
src={imageSrc}
alt={project.p_image?.title || "Project image"}
width={800}
height={800}
className="w-full h-auto object-cover"
/>
) : (
<div className="p-6 text-sm text-muted-foreground">No preview image</div>
)}
</div>
<div className="mt-4">
<h3 className="text-sm font-semibold mb-2">Files</h3>
{fileList.length > 0 ? (
<div className="flex flex-wrap">
{fileList.map((fname, i) => (
<a
key={i}
className="file-pill"
href={`${process.env.NEXT_PUBLIC_ASSET_URL || "https://forms.lasereverything.net"}/assets/${fname}`}
target="_blank"
rel="noopener noreferrer"
title={fname}
>
{fname}
</a>
))}
</div>
) : (
<p className="text-xs text-muted-foreground">No files attached.</p>
)}
</div>
</div>
<div className="md:col-span-2">
<h1 className="text-2xl font-bold mb-2">{project.title}</h1>
<p className="text-sm text-muted-foreground mb-1">
Uploaded by: {project.uploader || "—"}
</p>
<p className="text-sm text-muted-foreground mb-2">
Category: {project.category || "—"}
</p>
<div className="flex flex-wrap gap-1">
{Array.isArray(project.tags) && project.tags.length > 0 ? (
project.tags.map((tag, i) => (
<a
key={i}
href={`/projects?query=${encodeURIComponent(tag)}`}
target="_blank"
rel="noopener noreferrer"
className="text-xs bg-muted text-foreground rounded px-2 py-0.5 hover:bg-accent hover:text-background transition-colors"
title={`Search for ${tag}`}
>
{tag}
</a>
))
) : (
<span className="text-xs text-muted-foreground">No tags</span>
)}
</div>
{/* Optional long description if you add one later */}
{project.body ? (
<div className="prose dark:prose-invert mt-6">
<ReactMarkdown>{project.body}</ReactMarkdown>
</div>
) : null}
</div>
</div>
</div>
);
}