110 lines
3.9 KiB
TypeScript
110 lines
3.9 KiB
TypeScript
import { useEffect, useState } from 'react';
|
|
import { Link } from 'react-router-dom';
|
|
|
|
import { DashboardLayout } from '../../components/dashboard/DashboardLayout';
|
|
import { GalleryCard } from '../../components/dashboard/GalleryCard';
|
|
import { StudioStat } from '../../components/dashboard/StudioStat';
|
|
import { useAuth } from '../../features/auth/useAuth';
|
|
import { deleteGallery, getGalleries } from '../../lib/api';
|
|
import { formatBytes } from '../../lib/format';
|
|
import type { GallerySummary } from '../../types/gallery';
|
|
|
|
export default function DashboardPage() {
|
|
const { user } = useAuth();
|
|
const [galleries, setGalleries] = useState<GallerySummary[]>([]);
|
|
const [loading, setLoading] = useState(true);
|
|
const [error, setError] = useState('');
|
|
|
|
useEffect(() => {
|
|
getGalleries()
|
|
.then(setGalleries)
|
|
.catch((reason: unknown) =>
|
|
setError(reason instanceof Error ? reason.message : 'Could not load galleries.'),
|
|
)
|
|
.finally(() => setLoading(false));
|
|
}, []);
|
|
|
|
async function removeGallery(gallery: GallerySummary) {
|
|
if (!window.confirm(`Delete "${gallery.title}"? This cannot be undone.`)) return;
|
|
try {
|
|
await deleteGallery(gallery.id);
|
|
setGalleries((current) => current.filter((item) => item.id !== gallery.id));
|
|
} catch (reason) {
|
|
setError(reason instanceof Error ? reason.message : 'Could not delete gallery.');
|
|
}
|
|
}
|
|
|
|
const photos = galleries.reduce((total, gallery) => total + gallery.photoCount, 0);
|
|
const storage = galleries.reduce((total, gallery) => total + gallery.totalBytes, 0);
|
|
const published = galleries.filter((gallery) => gallery.status === 'published').length;
|
|
|
|
return (
|
|
<DashboardLayout>
|
|
<div className="studio-page">
|
|
<header className="studio-page__header">
|
|
<div>
|
|
<p className="studio-kicker">{user?.name || 'Studio'} / Overview</p>
|
|
<h1 className="studio-display">Dashboard</h1>
|
|
</div>
|
|
<Link className="platform-button platform-button--accent" to="/dashboard/galleries/new">
|
|
<span>New Gallery</span>
|
|
<span aria-hidden="true">+</span>
|
|
</Link>
|
|
</header>
|
|
|
|
<section className="studio-stats" aria-label="Studio overview">
|
|
<StudioStat
|
|
label="Total Galleries"
|
|
value={String(galleries.length).padStart(2, '0')}
|
|
accent="coral"
|
|
/>
|
|
<StudioStat
|
|
label="Published"
|
|
value={String(published).padStart(2, '0')}
|
|
accent="violet"
|
|
/>
|
|
<StudioStat
|
|
label="Photographs"
|
|
value={String(photos).padStart(2, '0')}
|
|
accent="gold"
|
|
/>
|
|
<StudioStat
|
|
label="Storage Used"
|
|
value={formatBytes(storage)}
|
|
accent="ink"
|
|
/>
|
|
</section>
|
|
|
|
<section className="studio-section studio-section--recent">
|
|
<span className="studio-section__heading"></span>
|
|
{error && <p className="studio-alert studio-alert--error">{error}</p>}
|
|
{loading ? (
|
|
<div className="studio-list-loading">
|
|
<span />
|
|
<span />
|
|
<span />
|
|
</div>
|
|
) : galleries.length === 0 ? (
|
|
<div className="studio-empty">
|
|
<span className="studio-empty__mark">+</span>
|
|
<div>
|
|
<h3>No galleries yet</h3>
|
|
<p>No galleries. Create one to start.</p>
|
|
</div>
|
|
<Link className="inline-link" to="/dashboard/galleries/new">
|
|
Create Gallery <span aria-hidden="true">↗</span>
|
|
</Link>
|
|
</div>
|
|
) : (
|
|
<div className="gallery-grid gallery-grid--dashboard">
|
|
{galleries.slice(0, 3).map((gallery) => (
|
|
<GalleryCard key={gallery.id} gallery={gallery} onDelete={removeGallery} />
|
|
))}
|
|
</div>
|
|
)}
|
|
</section>
|
|
</div>
|
|
</DashboardLayout>
|
|
);
|
|
}
|