Files
zod-backend/client/src/components/tasks/TasksList.tsx
2025-01-13 14:15:29 +03:00

201 lines
5.7 KiB
TypeScript

import {
Box,
Button,
Card,
CardContent,
Chip,
CircularProgress,
FormControl,
Grid,
InputLabel,
MenuItem,
Pagination,
Select,
SelectChangeEvent,
Typography
} from '@mui/material';
import React, { useEffect, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { juniorsApi, tasksApi } from '../../api/client';
import { Junior, PaginatedResponse } from '../../types/junior';
import { Task, TaskStatus } from '../../types/task';
const statusColors = {
PENDING: 'warning',
IN_PROGRESS: 'info',
COMPLETED: 'success'
} as const;
export const TasksList = () => {
const [loading, setLoading] = useState(true);
const [error, setError] = useState('');
const [tasks, setTasks] = useState<Task[]>([]);
const [juniors, setJuniors] = useState<Junior[]>([]);
const [page, setPage] = useState(1);
const [totalPages, setTotalPages] = useState(1);
const [status, setStatus] = useState<TaskStatus>('PENDING');
const [selectedJuniorId, setSelectedJuniorId] = useState<string>('');
const navigate = useNavigate();
const fetchJuniors = async () => {
try {
const response = await juniorsApi.getJuniors(1, 50);
const data = response.data as PaginatedResponse<Junior>;
setJuniors(data.data);
} catch (err) {
console.error('Failed to load juniors:', err);
}
};
const fetchTasks = async (pageNum: number) => {
try {
setLoading(true);
const response = await tasksApi.getTasks(status, pageNum, 10, selectedJuniorId || undefined);
const data = response.data as PaginatedResponse<Task>;
setTasks(data.data);
setTotalPages(data.meta.pageCount);
} catch (err) {
setError(err instanceof Error ? err.message : 'Failed to load tasks');
} finally {
setLoading(false);
}
};
useEffect(() => {
fetchJuniors();
}, []);
useEffect(() => {
fetchTasks(page);
}, [page, status, selectedJuniorId]);
const handlePageChange = (event: React.ChangeEvent<unknown>, value: number) => {
setPage(value);
};
const handleStatusChange = (event: SelectChangeEvent) => {
setStatus(event.target.value as TaskStatus);
setPage(1);
};
const handleJuniorChange = (event: SelectChangeEvent) => {
setSelectedJuniorId(event.target.value);
setPage(1);
};
if (loading && page === 1) {
return (
<Box display="flex" justifyContent="center" alignItems="center" minHeight="200px">
<CircularProgress />
</Box>
);
}
if (error) {
return (
<Box p={3}>
<Typography color="error">{error}</Typography>
</Box>
);
}
return (
<Box p={3}>
<Box display="flex" justifyContent="space-between" alignItems="center" mb={3}>
<Typography variant="h4">Tasks</Typography>
<Button
variant="contained"
color="primary"
onClick={() => navigate('/tasks/new')}
>
Create Task
</Button>
</Box>
<Box display="flex" gap={2} mb={3}>
<FormControl sx={{ minWidth: 200 }}>
<InputLabel>Status</InputLabel>
<Select
value={status}
label="Status"
onChange={handleStatusChange}
>
<MenuItem value="PENDING">Pending</MenuItem>
<MenuItem value="IN_PROGRESS">In Progress</MenuItem>
<MenuItem value="COMPLETED">Completed</MenuItem>
</Select>
</FormControl>
<FormControl sx={{ minWidth: 200 }}>
<InputLabel>Junior</InputLabel>
<Select
value={selectedJuniorId}
label="Junior"
onChange={handleJuniorChange}
>
<MenuItem value="">All Juniors</MenuItem>
{juniors.map(junior => (
<MenuItem key={junior.id} value={junior.id}>
{junior.fullName}
</MenuItem>
))}
</Select>
</FormControl>
</Box>
<Grid container spacing={3}>
{tasks.map((task) => (
<Grid item xs={12} sm={6} md={4} key={task.id}>
<Card>
<CardContent>
<Box display="flex" justifyContent="space-between" alignItems="flex-start">
<Typography variant="h6" gutterBottom>
{task.title}
</Typography>
<Chip
label={task.status}
color={statusColors[task.status]}
size="small"
/>
</Box>
<Typography color="textSecondary" gutterBottom>
Due: {new Date(task.dueDate).toLocaleDateString()}
</Typography>
<Typography variant="body2" gutterBottom>
{task.description}
</Typography>
<Typography color="primary" gutterBottom>
Reward: ${task.rewardAmount}
</Typography>
<Typography variant="body2" color="textSecondary">
Assigned to: {task.junior.fullName}
</Typography>
<Box mt={2}>
<Button
variant="outlined"
fullWidth
onClick={() => navigate(`/tasks/${task.id}`)}
>
View Details
</Button>
</Box>
</CardContent>
</Card>
</Grid>
))}
</Grid>
{totalPages > 1 && (
<Box display="flex" justifyContent="center" mt={4}>
<Pagination
count={totalPages}
page={page}
onChange={handlePageChange}
color="primary"
/>
</Box>
)}
</Box>
);
};