mirror of
https://github.com/SamyRai/turash.git
synced 2025-12-26 23:01:33 +00:00
186 lines
6.2 KiB
TypeScript
186 lines
6.2 KiB
TypeScript
import { DataTable } from '@/components/admin/DataTable.tsx';
|
|
import { Badge, Button } from '@/components/ui';
|
|
import { useMediaAssets, useDeleteMediaAsset } from '@/hooks/api/useAdminAPI.ts';
|
|
import { useTranslation } from '@/hooks/useI18n.tsx';
|
|
import type { MediaAsset } from '@/services/admin-api.ts';
|
|
import { Edit, Plus, Trash2, Image as ImageIcon } from 'lucide-react';
|
|
import { useState } from 'react';
|
|
import { useNavigate } from 'react-router-dom';
|
|
|
|
const MediaLibraryPage = () => {
|
|
const { t } = useTranslation();
|
|
const navigate = useNavigate();
|
|
const [currentPage, setCurrentPage] = useState(1);
|
|
const pageSize = 25;
|
|
const [typeFilter, setTypeFilter] = useState<string | undefined>();
|
|
|
|
const { data, isLoading } = useMediaAssets({ type: typeFilter });
|
|
const { mutate: deleteAsset } = useDeleteMediaAsset();
|
|
|
|
const handleDelete = (asset: MediaAsset) => {
|
|
if (window.confirm(t('adminPage.content.media.confirmDelete') || 'Delete this media asset?')) {
|
|
deleteAsset(asset.id, {
|
|
onSuccess: () => {
|
|
// Query will refetch automatically
|
|
},
|
|
});
|
|
}
|
|
};
|
|
|
|
const formatFileSize = (bytes: number): string => {
|
|
if (bytes === 0) return '0 Bytes';
|
|
const k = 1024;
|
|
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
|
|
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
return Math.round(bytes / Math.pow(k, i) * 100) / 100 + ' ' + sizes[i];
|
|
};
|
|
|
|
const getTypeBadge = (type: string) => {
|
|
if (type.startsWith('image/')) {
|
|
return <Badge variant="success">{t('adminPage.content.media.type') || 'Image'}</Badge>;
|
|
}
|
|
if (type.startsWith('video/')) {
|
|
return <Badge variant="default">{t('adminPage.content.media.type') || 'Video'}</Badge>;
|
|
}
|
|
return <Badge variant="secondary">{type}</Badge>;
|
|
};
|
|
|
|
const columns = [
|
|
{
|
|
key: 'thumbnail',
|
|
header: t('adminPage.content.media.table.thumbnail') || 'Thumbnail',
|
|
render: (asset: MediaAsset) => (
|
|
<div className="flex items-center gap-2">
|
|
{asset.type?.startsWith('image/') ? (
|
|
<img
|
|
src={asset.url}
|
|
alt={asset.altText || asset.name}
|
|
className="h-12 w-12 rounded object-cover"
|
|
/>
|
|
) : (
|
|
<div className="h-12 w-12 rounded bg-muted flex items-center justify-center">
|
|
<ImageIcon className="h-6 w-6 text-muted-foreground" />
|
|
</div>
|
|
)}
|
|
</div>
|
|
),
|
|
},
|
|
{
|
|
key: 'name',
|
|
header: t('adminPage.content.media.table.name') || 'Name',
|
|
render: (asset: MediaAsset) => <span className="font-medium">{asset.name}</span>,
|
|
},
|
|
{
|
|
key: 'type',
|
|
header: t('adminPage.content.media.table.type') || 'Type',
|
|
render: (asset: MediaAsset) => getTypeBadge(asset.type || 'unknown'),
|
|
},
|
|
{
|
|
key: 'size',
|
|
header: t('adminPage.content.media.table.size') || 'Size',
|
|
render: (asset: MediaAsset) => (
|
|
<span className="text-sm text-muted-foreground">{formatFileSize(asset.size || 0)}</span>
|
|
),
|
|
},
|
|
{
|
|
key: 'uploadedAt',
|
|
header: t('adminPage.content.media.table.uploaded') || 'Uploaded',
|
|
render: (asset: MediaAsset) => {
|
|
try {
|
|
const date = new Date(asset.uploadedAt || asset.createdAt);
|
|
return <span className="text-sm text-muted-foreground">{date.toLocaleDateString()}</span>;
|
|
} catch {
|
|
return <span className="text-sm text-muted-foreground">—</span>;
|
|
}
|
|
},
|
|
},
|
|
];
|
|
|
|
const actions = [
|
|
{
|
|
label: t('adminPage.content.media.edit') || 'Edit',
|
|
icon: <Edit className="h-4 w-4" />,
|
|
onClick: (asset: MediaAsset) => navigate(`/admin/content/media/${asset.id}/edit`),
|
|
},
|
|
{
|
|
label: t('adminPage.content.media.delete') || 'Delete',
|
|
icon: <Trash2 className="h-4 w-4" />,
|
|
variant: 'destructive' as const,
|
|
onClick: (asset: MediaAsset) => handleDelete(asset),
|
|
},
|
|
];
|
|
|
|
const assets = data?.assets || [];
|
|
|
|
return (
|
|
<div className="space-y-6">
|
|
{/* Header */}
|
|
<div className="flex items-center justify-between">
|
|
<div>
|
|
<h1 className="text-2xl font-bold">{t('adminPage.content.media.title') || 'Media Library'}</h1>
|
|
<p className="text-muted-foreground">
|
|
{t('adminPage.content.media.description') || 'Manage uploaded media assets, images, and files'}
|
|
</p>
|
|
</div>
|
|
<Button onClick={() => navigate('/admin/content/media/new')}>
|
|
<Plus className="h-4 w-4 mr-2" />
|
|
{t('adminPage.content.media.newAsset') || 'Upload Media'}
|
|
</Button>
|
|
</div>
|
|
|
|
{/* Filters */}
|
|
<div className="flex gap-2">
|
|
<Button
|
|
variant={typeFilter === undefined ? 'default' : 'outline'}
|
|
onClick={() => setTypeFilter(undefined)}
|
|
>
|
|
{t('adminPage.content.announcements.all') || 'All'}
|
|
</Button>
|
|
<Button
|
|
variant={typeFilter === 'image' ? 'default' : 'outline'}
|
|
onClick={() => setTypeFilter('image')}
|
|
>
|
|
Images
|
|
</Button>
|
|
<Button
|
|
variant={typeFilter === 'video' ? 'default' : 'outline'}
|
|
onClick={() => setTypeFilter('video')}
|
|
>
|
|
Videos
|
|
</Button>
|
|
</div>
|
|
|
|
{/* Table */}
|
|
<DataTable
|
|
columns={columns}
|
|
data={assets}
|
|
getRowId={(asset) => asset.id}
|
|
isLoading={isLoading}
|
|
actions={actions}
|
|
pagination={
|
|
assets.length > 0
|
|
? {
|
|
currentPage,
|
|
totalPages: Math.ceil(assets.length / pageSize),
|
|
pageSize,
|
|
totalItems: assets.length,
|
|
onPageChange: setCurrentPage,
|
|
}
|
|
: undefined
|
|
}
|
|
emptyMessage={t('adminPage.content.media.noAssets') || 'No media assets found'}
|
|
emptyDescription={t('adminPage.content.media.createFirst') || 'Upload your first media asset to get started'}
|
|
emptyAction={
|
|
<Button onClick={() => navigate('/admin/content/media/new')}>
|
|
<Plus className="h-4 w-4 mr-2" />
|
|
{t('adminPage.content.media.newAsset') || 'Upload Media'}
|
|
</Button>
|
|
}
|
|
/>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default MediaLibraryPage;
|
|
|