initial commit

This commit is contained in:
2026-05-09 12:34:45 -04:00
commit 97fabc2c17
49 changed files with 4856 additions and 0 deletions

View File

@@ -0,0 +1,110 @@
import { useState } from "react";
import { useQuery } from "@tanstack/react-query";
import { api, MediaItem, TagsByCategory } from "../api/client";
export default function SearchPage() {
const [q, setQ] = useState("");
const [selectedTags, setSelectedTags] = useState<number[]>([]);
const [submitted, setSubmitted] = useState(false);
const { data: grouped = [] } = useQuery<TagsByCategory[]>({
queryKey: ["tags"],
queryFn: api.tags.list,
});
const { data: results = [], isFetching } = useQuery<MediaItem[]>({
queryKey: ["search", q, selectedTags],
queryFn: () => api.search({ q, tags: selectedTags }),
enabled: submitted,
});
function toggleTag(id: number) {
setSelectedTags((prev) =>
prev.includes(id) ? prev.filter((t) => t !== id) : [...prev, id]
);
}
function handleSubmit(e: React.FormEvent) {
e.preventDefault();
setSubmitted(true);
}
return (
<div style={{ padding: "2rem", maxWidth: 900 }}>
<h1>Search</h1>
<form onSubmit={handleSubmit} style={{ display: "flex", gap: 8, marginBottom: 16, flexWrap: "wrap" }}>
<input
placeholder="Search by filename…"
value={q}
onChange={(e) => setQ(e.target.value)}
style={{ flex: 1, minWidth: 200, padding: "6px 10px" }}
/>
<button type="submit">Search</button>
</form>
{/* Tag filter */}
{grouped.length > 0 && (
<div style={{ marginBottom: 24 }}>
<div style={{ fontSize: 13, color: "#666", marginBottom: 8 }}>Filter by tag:</div>
{grouped.map((group) => (
<div key={group.category} style={{ marginBottom: 8 }}>
<span style={{ fontSize: 11, fontWeight: 700, textTransform: "uppercase", color: "#888", marginRight: 8 }}>
{group.category}
</span>
{group.tags.map((tag) => (
<button
key={tag.id}
onClick={() => toggleTag(tag.id)}
style={{
margin: "2px",
padding: "2px 10px",
borderRadius: 12,
border: "1px solid",
cursor: "pointer",
background: selectedTags.includes(tag.id) ? "#3b82f6" : "transparent",
color: selectedTags.includes(tag.id) ? "#fff" : "inherit",
borderColor: selectedTags.includes(tag.id) ? "#3b82f6" : "#ccc",
fontSize: 13,
}}
>
{tag.name}
</button>
))}
</div>
))}
</div>
)}
{isFetching && <p>Searching</p>}
<div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fill, minmax(140px, 1fr))", gap: 12 }}>
{results.map((item) => (
<div key={item.id} style={{ border: "1px solid #ddd", borderRadius: 6, overflow: "hidden" }}>
<img
src={api.media.thumbnailUrl(item.id)}
alt={item.filename}
loading="lazy"
style={{ width: "100%", height: 110, objectFit: "cover" }}
/>
<div style={{ padding: "4px 6px", fontSize: 12, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>
{item.filename}
</div>
{item.tags.length > 0 && (
<div style={{ padding: "0 6px 4px", display: "flex", flexWrap: "wrap", gap: 4 }}>
{item.tags.map((t) => (
<span key={t.id} style={{ background: "#e0edff", color: "#3b82f6", borderRadius: 8, padding: "1px 6px", fontSize: 11 }}>
{t.name}
</span>
))}
</div>
)}
</div>
))}
</div>
{submitted && !isFetching && results.length === 0 && (
<p style={{ color: "#888" }}>No results found.</p>
)}
</div>
);
}