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

270 lines
8.4 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,
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>
);
}