download
raw
5.78 kB
import React, { useState, useEffect } from 'react';
import {
Box,
Typography,
Card,
CardContent,
CardActions,
Button,
TextField,
Grid,
Alert,
CircularProgress,
IconButton,
Dialog,
DialogTitle,
DialogContent,
DialogActions,
} from '@mui/material';
import { Add as AddIcon, Delete as DeleteIcon, FolderOpen as OpenIcon } from '@mui/icons-material';
import { api } from '../services/api';
import { Project } from '../types';
interface ProjectListProps {
onSelectProject: (project: Project) => void;
}
const ProjectList: React.FC<ProjectListProps> = ({ onSelectProject }) => {
const [projects, setProjects] = useState<Project[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const [dialogOpen, setDialogOpen] = useState(false);
const [newProjectName, setNewProjectName] = useState('');
const [newProjectDescription, setNewProjectDescription] = useState('');
const [creating, setCreating] = useState(false);
useEffect(() => {
loadProjects();
}, []);
const loadProjects = async () => {
setLoading(true);
setError(null);
try {
const data = await api.getProjects();
setProjects(data);
} catch (err: any) {
setError(err.message || 'Erreur lors du chargement des projets');
} finally {
setLoading(false);
}
};
const handleCreateProject = async () => {
if (!newProjectName.trim()) return;
setCreating(true);
try {
const project = await api.createProject(newProjectName, newProjectDescription);
setProjects([...projects, project]);
setDialogOpen(false);
setNewProjectName('');
setNewProjectDescription('');
} catch (err: any) {
setError(err.message || 'Erreur lors de la création');
} finally {
setCreating(false);
}
};
const handleDeleteProject = async (projectId: string) => {
try {
await api.deleteProject(projectId);
setProjects(projects.filter((p) => p.id !== projectId));
} catch (err: any) {
setError(err.message || 'Erreur lors de la suppression');
}
};
if (loading) {
return (
<Box sx={{ display: 'flex', justifyContent: 'center', alignItems: 'center', flexGrow: 1 }}>
<CircularProgress />
</Box>
);
}
return (
<Box sx={{ flexGrow: 1, p: 3, overflow: 'auto' }}>
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 3 }}>
<Typography variant="h5">Mes Projets</Typography>
<Button
variant="contained"
startIcon={<AddIcon />}
onClick={() => setDialogOpen(true)}
>
Nouveau Projet
</Button>
</Box>
{error && (
<Alert severity="error" sx={{ mb: 2 }}>
{error}
</Alert>
)}
{projects.length === 0 ? (
<Box sx={{ textAlign: 'center', py: 8 }}>
<Typography color="textSecondary" sx={{ mb: 2 }}>
Aucun projet. Créez votre premier projet !
</Typography>
<Button
variant="outlined"
startIcon={<AddIcon />}
onClick={() => setDialogOpen(true)}
>
Créer un projet
</Button>
</Box>
) : (
<Grid container spacing={2}>
{projects.map((project) => (
<Grid item xs={12} sm={6} md={4} key={project.id}>
<Card>
<CardContent>
<Typography variant="h6" noWrap>
{project.name}
</Typography>
<Typography variant="body2" color="textSecondary">
{project.description || 'Aucune description'}
</Typography>
<Typography variant="caption" color="textSecondary" sx={{ display: 'block', mt: 1 }}>
Créé le {new Date(project.created_at).toLocaleDateString('fr-FR')}
</Typography>
</CardContent>
<CardActions>
<Button
size="small"
startIcon={<OpenIcon />}
onClick={() => onSelectProject(project)}
>
Ouvrir
</Button>
<IconButton
size="small"
color="error"
onClick={() => handleDeleteProject(project.id)}
>
<DeleteIcon />
</IconButton>
</CardActions>
</Card>
</Grid>
))}
</Grid>
)}
<Dialog open={dialogOpen} onClose={() => setDialogOpen(false)}>
<DialogTitle>Nouveau Projet</DialogTitle>
<DialogContent>
<TextField
autoFocus
fullWidth
label="Nom du projet"
value={newProjectName}
onChange={(e) => setNewProjectName(e.target.value)}
sx={{ mt: 1 }}
/>
<TextField
fullWidth
multiline
rows={3}
label="Description (optionnelle)"
value={newProjectDescription}
onChange={(e) => setNewProjectDescription(e.target.value)}
sx={{ mt: 2 }}
/>
</DialogContent>
<DialogActions>
<Button onClick={() => setDialogOpen(false)}>Annuler</Button>
<Button
variant="contained"
onClick={handleCreateProject}
disabled={creating || !newProjectName.trim()}
>
{creating ? <CircularProgress size={20} /> : 'Créer'}
</Button>
</DialogActions>
</Dialog>
</Box>
);
};
export default ProjectList;

Xet Storage Details

Size:
5.78 kB
·
Xet hash:
81795fdf51328f2ccabb68a9a7ab811c92be49eb7b2755f21dc7b17fa9988ca7

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.