mirror of
https://github.com/myronblair/kino-app
synced 2026-07-28 13:33:49 -05:00
auto-commit for df4b0748-985b-4592-8c48-1e56102f3613
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import api from "../lib/api";
|
||||
import { toast } from "sonner";
|
||||
import { Trash2, Star } from "lucide-react";
|
||||
|
||||
export default function Admin() {
|
||||
const [movies, setMovies] = useState([]);
|
||||
|
||||
const load = async () => {
|
||||
const { data } = await api.get("/movies");
|
||||
setMovies(data);
|
||||
};
|
||||
useEffect(() => { load(); }, []);
|
||||
|
||||
const remove = async (id) => {
|
||||
if (!window.confirm("Remove this movie permanently?")) return;
|
||||
await api.delete(`/movies/${id}`);
|
||||
toast.success("Removed");
|
||||
load();
|
||||
};
|
||||
|
||||
const toggleFeatured = async (m) => {
|
||||
await api.patch(`/movies/${m.id}`, { featured: !m.featured });
|
||||
if (!m.featured) {
|
||||
// unset others client-side? Keep simple: server allows multiple but featured endpoint picks first
|
||||
toast.success("Featured updated");
|
||||
}
|
||||
load();
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-[#050505] pt-32 pb-24" data-testid="admin-page">
|
||||
<div className="px-6 md:px-12 max-w-[1500px] mx-auto">
|
||||
<span className="text-xs uppercase tracking-[0.3em] text-[#D9381E]">Library management</span>
|
||||
<h1 className="font-display text-5xl md:text-6xl font-black tracking-tighter text-white mt-3">
|
||||
Admin
|
||||
</h1>
|
||||
<p className="text-[#8A8A8A] mt-4">
|
||||
Manage your library and review pending requests.
|
||||
</p>
|
||||
|
||||
<div className="mt-10 flex gap-3">
|
||||
<a href="/admin/upload" className="bg-[#D9381E] hover:bg-[#ED4B32] text-white px-6 py-3 text-sm uppercase tracking-[0.2em]" data-testid="admin-upload-link">
|
||||
Upload New
|
||||
</a>
|
||||
<a href="/requests" className="bg-white/10 hover:bg-white/20 text-white px-6 py-3 text-sm uppercase tracking-[0.2em] border border-white/10">
|
||||
Review Requests
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div className="mt-12 border border-[#222]">
|
||||
<div className="grid grid-cols-12 px-5 py-3 border-b border-[#222] text-[10px] uppercase tracking-[0.3em] text-[#8A8A8A]">
|
||||
<span className="col-span-5">Title</span>
|
||||
<span className="col-span-2">Year</span>
|
||||
<span className="col-span-2">Rating</span>
|
||||
<span className="col-span-2">Source</span>
|
||||
<span className="col-span-1 text-right">Actions</span>
|
||||
</div>
|
||||
{movies.map((m) => (
|
||||
<div key={m.id} className="grid grid-cols-12 items-center px-5 py-4 border-b border-[#222] last:border-b-0 hover:bg-[#0F0F0F] transition-colors"
|
||||
data-testid={`admin-movie-row-${m.id}`}>
|
||||
<div className="col-span-5 flex items-center gap-3">
|
||||
<img src={m.poster_url} alt="" className="w-10 h-14 object-cover" />
|
||||
<span className="text-white truncate">{m.title}</span>
|
||||
</div>
|
||||
<span className="col-span-2 text-[#8A8A8A] text-sm">{m.year}</span>
|
||||
<span className="col-span-2 text-[#8A8A8A] text-sm">{m.rating}</span>
|
||||
<span className="col-span-2 text-[10px] uppercase tracking-[0.3em] text-[#8A8A8A]">{m.storage_type}</span>
|
||||
<div className="col-span-1 flex justify-end gap-2">
|
||||
<button onClick={() => toggleFeatured(m)} className={`${m.featured ? "text-[#D9381E]" : "text-[#8A8A8A]"} hover:text-[#ED4B32] transition-colors`} aria-label="Feature" data-testid={`feature-${m.id}`}>
|
||||
<Star size={16} strokeWidth={1.5} fill={m.featured ? "#D9381E" : "none"} />
|
||||
</button>
|
||||
<button onClick={() => remove(m.id)} className="text-[#8A8A8A] hover:text-[#fca5a5] transition-colors" aria-label="Delete" data-testid={`delete-${m.id}`}>
|
||||
<Trash2 size={16} strokeWidth={1.5} />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user