98 lines
2.7 KiB
TypeScript
98 lines
2.7 KiB
TypeScript
import React from 'react';
|
||
import { useSearchParams } from 'react-router-dom';
|
||
import { Button } from '@/components/ui/button';
|
||
import { deletePhoto, featurePhoto, getEventPhotos, unfeaturePhoto } from '../api';
|
||
import { isAuthError } from '../auth/tokens';
|
||
|
||
export default function EventPhotosPage() {
|
||
const [sp] = useSearchParams();
|
||
const id = Number(sp.get('id'));
|
||
const [rows, setRows] = React.useState<any[]>([]);
|
||
const [loading, setLoading] = React.useState(true);
|
||
|
||
const load = React.useCallback(async () => {
|
||
setLoading(true);
|
||
try {
|
||
setRows(await getEventPhotos(id));
|
||
} catch (error) {
|
||
if (!isAuthError(error)) {
|
||
console.error(error);
|
||
}
|
||
} finally {
|
||
setLoading(false);
|
||
}
|
||
}, [id]);
|
||
|
||
React.useEffect(() => {
|
||
load();
|
||
}, [load]);
|
||
|
||
async function onFeature(photo: any) {
|
||
try {
|
||
await featurePhoto(photo.id);
|
||
await load();
|
||
} catch (error) {
|
||
if (!isAuthError(error)) {
|
||
console.error(error);
|
||
}
|
||
}
|
||
}
|
||
|
||
async function onUnfeature(photo: any) {
|
||
try {
|
||
await unfeaturePhoto(photo.id);
|
||
await load();
|
||
} catch (error) {
|
||
if (!isAuthError(error)) {
|
||
console.error(error);
|
||
}
|
||
}
|
||
}
|
||
|
||
async function onDelete(photo: any) {
|
||
try {
|
||
await deletePhoto(photo.id);
|
||
await load();
|
||
} catch (error) {
|
||
if (!isAuthError(error)) {
|
||
console.error(error);
|
||
}
|
||
}
|
||
}
|
||
|
||
return (
|
||
<div className="mx-auto max-w-5xl p-4">
|
||
<h1 className="mb-3 text-lg font-semibold">Fotos moderieren</h1>
|
||
{loading && <div>Lade ...</div>}
|
||
<div className="grid grid-cols-2 gap-3 sm:grid-cols-3 md:grid-cols-4">
|
||
{rows.map((p) => (
|
||
<div key={p.id} className="rounded border p-2">
|
||
<img
|
||
src={p.thumbnail_path || p.file_path}
|
||
className="mb-2 aspect-square w-full rounded object-cover"
|
||
alt={p.caption ?? 'Foto'}
|
||
/>
|
||
<div className="flex items-center justify-between text-sm">
|
||
<span>?? {p.likes_count}</span>
|
||
<div className="flex gap-1">
|
||
{p.is_featured ? (
|
||
<Button size="sm" variant="secondary" onClick={() => onUnfeature(p)}>
|
||
Unfeature
|
||
</Button>
|
||
) : (
|
||
<Button size="sm" variant="secondary" onClick={() => onFeature(p)}>
|
||
Feature
|
||
</Button>
|
||
)}
|
||
<Button size="sm" variant="destructive" onClick={() => onDelete(p)}>
|
||
L<EFBFBD>schen
|
||
</Button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|