85 lines
2.8 KiB
TypeScript
85 lines
2.8 KiB
TypeScript
import { useState } from 'react';
|
|
import { Link } from 'react-router-dom';
|
|
|
|
import type { GallerySummary } from '../../types/gallery';
|
|
import { formatBytes, formatDate } from '../../lib/format';
|
|
|
|
interface GalleryCardProps {
|
|
gallery: GallerySummary;
|
|
onDelete: (gallery: GallerySummary) => void;
|
|
}
|
|
|
|
export function GalleryCard({ gallery, onDelete }: GalleryCardProps) {
|
|
const [copied, setCopied] = useState(false);
|
|
const clientURL = `${window.location.origin}/g/${gallery.slug}`;
|
|
|
|
async function copyLink() {
|
|
try {
|
|
await navigator.clipboard.writeText(clientURL);
|
|
setCopied(true);
|
|
window.setTimeout(() => setCopied(false), 1800);
|
|
} catch {
|
|
setCopied(false);
|
|
}
|
|
}
|
|
|
|
return (
|
|
<article className="gallery-card">
|
|
<Link className="gallery-card__cover" to={`/dashboard/galleries/${gallery.id}/edit`}>
|
|
{gallery.coverUrl ? (
|
|
<img src={gallery.coverUrl} alt="" loading="lazy" />
|
|
) : (
|
|
<div className="gallery-card__cover-placeholder" aria-hidden="true">
|
|
<span>{gallery.clientName.slice(0, 1).toUpperCase()}</span>
|
|
</div>
|
|
)}
|
|
<span className={`status-pill status-pill--${gallery.status}`}>
|
|
<i aria-hidden="true" /> {gallery.status}
|
|
</span>
|
|
<span className="gallery-card__cover-arrow" aria-hidden="true">
|
|
↗
|
|
</span>
|
|
</Link>
|
|
<div className="gallery-card__body">
|
|
<div className="gallery-card__heading">
|
|
<div>
|
|
<p>{gallery.clientName}</p>
|
|
<h3>{gallery.title}</h3>
|
|
</div>
|
|
<span className="gallery-card__date">{formatDate(gallery.createdAt)}</span>
|
|
</div>
|
|
<div className="gallery-card__meta">
|
|
<span>{gallery.photoCount} photos</span>
|
|
<span>{gallery.videoCount} videos</span>
|
|
<span>{formatBytes(gallery.totalBytes)}</span>
|
|
</div>
|
|
<div className="gallery-card__actions">
|
|
<Link className="text-action" to={`/dashboard/galleries/${gallery.id}/edit`}>
|
|
Edit <span aria-hidden="true">↗</span>
|
|
</Link>
|
|
<Link className="text-action text-action--muted" to={`/preview/${gallery.id}`}>
|
|
Preview
|
|
</Link>
|
|
{gallery.status === 'published' && (
|
|
<button
|
|
className="text-action text-action--muted"
|
|
type="button"
|
|
onClick={() => void copyLink()}
|
|
>
|
|
{copied ? 'Copied' : 'Copy link'}
|
|
</button>
|
|
)}
|
|
<button
|
|
className="gallery-card__delete"
|
|
type="button"
|
|
onClick={() => onDelete(gallery)}
|
|
aria-label={`Delete ${gallery.title}`}
|
|
>
|
|
×
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</article>
|
|
);
|
|
}
|