itcloud/frontend/src/pages/TrashPage.tsx

155 lines
4.8 KiB
TypeScript
Raw Normal View History

2025-12-30 13:35:19 +01:00
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>
);
}