itcloud/frontend/src/pages/TrashPage.tsx

155 lines
4.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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>
);
}