itcloud/frontend/src/pages/LibraryPage.old.tsx

270 lines
8.4 KiB
TypeScript
Raw Normal View History

2025-12-30 23:18:13 +01:00
import { useState, useEffect } from 'react';
import {
Box,
Grid,
Fab,
Typography,
CircularProgress,
Button,
Select,
MenuItem,
FormControl,
InputLabel,
Dialog,
DialogTitle,
DialogContent,
DialogActions,
TextField,
Alert,
Snackbar,
} from '@mui/material';
import { Add as AddIcon, FilterList as FilterIcon } from '@mui/icons-material';
import Layout from '../components/Layout';
import MediaCard from '../components/MediaCard';
import UploadDialog from '../components/UploadDialog';
import ViewerModal from '../components/ViewerModal';
import type { Asset, AssetType } from '../types';
import api from '../services/api';
export default function LibraryPage() {
const [assets, setAssets] = useState<Asset[]>([]);
const [loading, setLoading] = useState(true);
const [hasMore, setHasMore] = useState(false);
const [cursor, setCursor] = useState<string | undefined>();
const [filter, setFilter] = useState<AssetType | 'all'>('all');
const [uploadOpen, setUploadOpen] = useState(false);
const [viewerAsset, setViewerAsset] = useState<Asset | null>(null);
const [shareDialogOpen, setShareDialogOpen] = useState(false);
const [shareAssetId, setShareAssetId] = useState<string>('');
const [shareLink, setShareLink] = useState<string>('');
const [snackbarOpen, setSnackbarOpen] = useState(false);
const [snackbarMessage, setSnackbarMessage] = useState('');
useEffect(() => {
loadAssets(true);
}, [filter]);
const loadAssets = async (reset: boolean = false) => {
try {
setLoading(true);
const response = await api.listAssets({
cursor: reset ? undefined : cursor,
limit: 50,
type: filter === 'all' ? undefined : filter,
});
setAssets(reset ? response.items : [...assets, ...response.items]);
setHasMore(response.has_more);
setCursor(response.next_cursor);
} catch (error) {
console.error('Failed to load assets:', error);
} finally {
setLoading(false);
}
};
const handleUploadComplete = () => {
setUploadOpen(false);
loadAssets(true);
showSnackbar('Файлы успешно загружены');
};
const handleDelete = async (assetId: string) => {
try {
await api.deleteAsset(assetId);
setAssets(assets.filter((a) => a.id !== assetId));
showSnackbar('Файл перемещен в корзину');
} catch (error) {
console.error('Failed to delete asset:', error);
showSnackbar('Ошибка при удалении файла');
}
};
const handleShare = (assetId: string) => {
setShareAssetId(assetId);
setShareDialogOpen(true);
};
const handleCreateShare = async () => {
try {
const share = await api.createShare({
asset_id: shareAssetId,
expires_in_seconds: 86400 * 7, // 7 days
});
const link = `${window.location.origin}/share/${share.token}`;
setShareLink(link);
showSnackbar('Ссылка создана');
} catch (error) {
console.error('Failed to create share:', error);
showSnackbar('Ошибка создания ссылки');
}
};
const handleCopyShareLink = () => {
// Fallback for HTTP (clipboard API requires HTTPS)
if (navigator.clipboard && window.isSecureContext) {
navigator.clipboard.writeText(shareLink);
} else {
// Fallback method for HTTP
const textArea = document.createElement('textarea');
textArea.value = shareLink;
textArea.style.position = 'fixed';
textArea.style.left = '-999999px';
document.body.appendChild(textArea);
textArea.focus();
textArea.select();
try {
document.execCommand('copy');
} catch (err) {
console.error('Failed to copy:', err);
}
document.body.removeChild(textArea);
}
showSnackbar('Ссылка скопирована');
setShareDialogOpen(false);
setShareLink('');
};
const showSnackbar = (message: string) => {
setSnackbarMessage(message);
setSnackbarOpen(true);
};
return (
<Layout>
<Box sx={{ height: '100%', display: 'flex', flexDirection: 'column' }}>
{/* Filters */}
<Box sx={{ p: 2, borderBottom: 1, borderColor: 'divider' }}>
<FormControl size="small" sx={{ minWidth: 200 }}>
<InputLabel>Тип файлов</InputLabel>
<Select
value={filter}
label="Тип файлов"
onChange={(e) => setFilter(e.target.value as AssetType | 'all')}
>
<MenuItem value="all">Все файлы</MenuItem>
<MenuItem value="photo">Фото</MenuItem>
<MenuItem value="video">Видео</MenuItem>
</Select>
</FormControl>
</Box>
{/* Content */}
<Box sx={{ flexGrow: 1, overflow: 'auto', p: 2 }}>
{loading && assets.length === 0 && (
<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}
onClick={() => setViewerAsset(asset)}
/>
</Grid>
))}
</Grid>
{hasMore && (
<Box sx={{ display: 'flex', justifyContent: 'center', mt: 3 }}>
<Button
variant="outlined"
onClick={() => loadAssets(false)}
disabled={loading}
>
{loading ? 'Загрузка...' : 'Загрузить еще'}
</Button>
</Box>
)}
</>
)}
</Box>
{/* FAB */}
<Fab
color="primary"
sx={{ position: 'fixed', bottom: 24, right: 24 }}
onClick={() => setUploadOpen(true)}
>
<AddIcon />
</Fab>
{/* Upload Dialog */}
<UploadDialog
open={uploadOpen}
onClose={() => setUploadOpen(false)}
onComplete={handleUploadComplete}
/>
{/* Viewer Modal */}
<ViewerModal
asset={viewerAsset}
assets={assets}
onClose={() => setViewerAsset(null)}
onDelete={handleDelete}
onShare={handleShare}
/>
{/* Share Dialog */}
<Dialog open={shareDialogOpen} onClose={() => setShareDialogOpen(false)}>
<DialogTitle>Поделиться файлом</DialogTitle>
<DialogContent>
{!shareLink ? (
<Typography>
Создать публичную ссылку на файл? Ссылка будет действительна 7 дней.
</Typography>
) : (
<TextField
fullWidth
value={shareLink}
label="Ссылка для доступа"
margin="normal"
InputProps={{
readOnly: true,
}}
/>
)}
</DialogContent>
<DialogActions>
<Button onClick={() => setShareDialogOpen(false)}>Отмена</Button>
{!shareLink ? (
<Button onClick={handleCreateShare} variant="contained">
Создать ссылку
</Button>
) : (
<Button onClick={handleCopyShareLink} variant="contained">
Скопировать
</Button>
)}
</DialogActions>
</Dialog>
{/* Snackbar */}
<Snackbar
open={snackbarOpen}
autoHideDuration={3000}
onClose={() => setSnackbarOpen(false)}
message={snackbarMessage}
/>
</Box>
</Layout>
);
}