155 lines
4.8 KiB
TypeScript
155 lines
4.8 KiB
TypeScript
import { useState, useEffect } from 'react';
|
||
import {
|
||
Box,
|
||
Grid,
|
||
Typography,
|
||
CircularProgress,
|
||
Button,
|
||
Snackbar,
|
||
} from '@mui/material';
|
||
import Layout from '../components/Layout';
|
||
import MediaCard from '../components/MediaCard';
|
||
import type { Asset } from '../types';
|
||
import api from '../services/api';
|
||
|
||
export default function TrashPage() {
|
||
const [assets, setAssets] = useState<Asset[]>([]);
|
||
const [loading, setLoading] = useState(true);
|
||
const [selectedAssets, setSelectedAssets] = useState<Set<string>>(new Set());
|
||
const [snackbarOpen, setSnackbarOpen] = useState(false);
|
||
const [snackbarMessage, setSnackbarMessage] = useState('');
|
||
|
||
useEffect(() => {
|
||
loadDeletedAssets();
|
||
}, []);
|
||
|
||
const loadDeletedAssets = async () => {
|
||
try {
|
||
setLoading(true);
|
||
// We need to get all assets and filter deleted ones
|
||
// TODO: Add deleted filter to API
|
||
const response = await api.listAssets({ limit: 200 });
|
||
const deletedAssets = response.items.filter((asset) => asset.deleted_at);
|
||
setAssets(deletedAssets);
|
||
} catch (error) {
|
||
console.error('Failed to load deleted assets:', error);
|
||
} finally {
|
||
setLoading(false);
|
||
}
|
||
};
|
||
|
||
const handleSelect = (assetId: string, selected: boolean) => {
|
||
const newSelected = new Set(selectedAssets);
|
||
if (selected) {
|
||
newSelected.add(assetId);
|
||
} else {
|
||
newSelected.delete(assetId);
|
||
}
|
||
setSelectedAssets(newSelected);
|
||
};
|
||
|
||
const handleRestore = async () => {
|
||
if (selectedAssets.size === 0) return;
|
||
|
||
try {
|
||
await Promise.all(
|
||
Array.from(selectedAssets).map((assetId) => api.restoreAsset(assetId))
|
||
);
|
||
setAssets(assets.filter((a) => !selectedAssets.has(a.id)));
|
||
setSelectedAssets(new Set());
|
||
showSnackbar('Файлы восстановлены');
|
||
} catch (error) {
|
||
console.error('Failed to restore assets:', error);
|
||
showSnackbar('Ошибка при восстановлении файлов');
|
||
}
|
||
};
|
||
|
||
const handlePurge = async () => {
|
||
if (selectedAssets.size === 0) return;
|
||
|
||
if (!confirm('Вы уверены? Файлы будут удалены навсегда.')) {
|
||
return;
|
||
}
|
||
|
||
try {
|
||
await Promise.all(
|
||
Array.from(selectedAssets).map((assetId) => api.purgeAsset(assetId))
|
||
);
|
||
setAssets(assets.filter((a) => !selectedAssets.has(a.id)));
|
||
setSelectedAssets(new Set());
|
||
showSnackbar('Файлы удалены навсегда');
|
||
} catch (error) {
|
||
console.error('Failed to purge assets:', error);
|
||
showSnackbar('Ошибка при удалении файлов');
|
||
}
|
||
};
|
||
|
||
const showSnackbar = (message: string) => {
|
||
setSnackbarMessage(message);
|
||
setSnackbarOpen(true);
|
||
};
|
||
|
||
return (
|
||
<Layout>
|
||
<Box sx={{ height: '100%', display: 'flex', flexDirection: 'column' }}>
|
||
{/* Actions */}
|
||
{selectedAssets.size > 0 && (
|
||
<Box sx={{ p: 2, borderBottom: 1, borderColor: 'divider', display: 'flex', gap: 2 }}>
|
||
<Typography variant="body1" sx={{ flexGrow: 1, alignSelf: 'center' }}>
|
||
Выбрано: {selectedAssets.size}
|
||
</Typography>
|
||
<Button variant="outlined" onClick={handleRestore}>
|
||
Восстановить
|
||
</Button>
|
||
<Button variant="outlined" color="error" onClick={handlePurge}>
|
||
Удалить навсегда
|
||
</Button>
|
||
</Box>
|
||
)}
|
||
|
||
{/* Content */}
|
||
<Box sx={{ flexGrow: 1, overflow: 'auto', p: 2 }}>
|
||
{loading && (
|
||
<Box sx={{ display: 'flex', justifyContent: 'center', p: 4 }}>
|
||
<CircularProgress />
|
||
</Box>
|
||
)}
|
||
|
||
{!loading && assets.length === 0 && (
|
||
<Box sx={{ textAlign: 'center', p: 4 }}>
|
||
<Typography variant="h6" color="text.secondary" gutterBottom>
|
||
Корзина пуста
|
||
</Typography>
|
||
<Typography variant="body2" color="text.secondary">
|
||
Удаленные файлы будут отображаться здесь
|
||
</Typography>
|
||
</Box>
|
||
)}
|
||
|
||
{assets.length > 0 && (
|
||
<Grid container spacing={2}>
|
||
{assets.map((asset) => (
|
||
<Grid item xs={6} sm={4} md={3} lg={2} key={asset.id}>
|
||
<MediaCard
|
||
asset={asset}
|
||
selected={selectedAssets.has(asset.id)}
|
||
onSelect={handleSelect}
|
||
/>
|
||
</Grid>
|
||
))}
|
||
</Grid>
|
||
)}
|
||
</Box>
|
||
|
||
{/* Snackbar */}
|
||
<Snackbar
|
||
open={snackbarOpen}
|
||
autoHideDuration={3000}
|
||
onClose={() => setSnackbarOpen(false)}
|
||
message={snackbarMessage}
|
||
/>
|
||
</Box>
|
||
</Layout>
|
||
);
|
||
}
|