Todo.tsx
import { useState } from 'react';
import { Container, Button, Grid, Typography } from '@mui/material';
import ModalComponent from './Modal';
import ListComponent from './List';
import { TodoItem } from './types';
import AddIcon from '@mui/icons-material/Add';
const TodoApp = () => {
const [todos, setTodos] = useState<TodoItem[]>([]);
const [modalOpen, setModalOpen] = useState(false);
const [modalData, setModalData] = useState<Partial<TodoItem>>({});
const handleOpenModal = (item?: TodoItem) => {
setModalData(item || {});
setModalOpen(true);
};
const handleCloseModal = () => {
setModalOpen(false);
};
const addTodo = (newTodo: TodoItem) => {
setTodos([...todos, { ...newTodo, id: Date.now(), type: 'Todo' }]);
handleCloseModal();
};
const updateTodo = (updatedTodo: TodoItem) => {
setTodos(todos.map(todo => todo.id === updatedTodo.id ? updatedTodo : todo));
handleCloseModal();
};
const moveTodo = (id: number, toType: 'Todo'|'Start' | 'End') => {
setTodos(todos.map(todo => todo.id === id ? { ...todo, type: toType } : todo));
};
const getTodosByType = (type: 'Todo' | 'Start' | 'End') => {
return todos.filter(todo => todo.type === type);
};
return (
<Container>
<Typography variant="h4" align="center">Todo App</Typography>
<Button startIcon={<AddIcon />} onClick={() => handleOpenModal()}>Add Todo</Button>
<Grid container spacing={2}>
<Grid item xs={4}>
<ListComponent title="Todo" items={getTodosByType('Todo')} onMove={moveTodo} onEdit={handleOpenModal} />
</Grid>
<Grid item xs={4}>
<ListComponent title="Start" items={getTodosByType('Start')} onMove={moveTodo} onEdit={handleOpenModal} />
</Grid>
<Grid item xs={4}>
<ListComponent title="End" items={getTodosByType('End')} onMove={moveTodo} onEdit={handleOpenModal} />
</Grid>
</Grid>
<ModalComponent
open={modalOpen}
handleClose={handleCloseModal}
onAdd={addTodo}
onUpdate={updateTodo}
modalData={modalData as TodoItem}
/>
</Container>
);
};
export default TodoApp;
Modal.tsx
import React, { useState, useEffect } from 'react';
import { Dialog, DialogActions, DialogContent, DialogTitle, TextField, Button } from '@mui/material';
import { TodoItem } from './types';
interface ModalComponentProps {
open: boolean;
handleClose: () => void;
onAdd: (newTodo: TodoItem) => void;
onUpdate: (updatedTodo: TodoItem) => void;
modalData: TodoItem;
}
const ModalComponent: React.FC<ModalComponentProps> = ({ open, handleClose, onAdd, onUpdate, modalData }) => {
const [title, setTitle] = useState<string>(modalData.title || '');
const [details, setDetails] = useState<string>(modalData.details || '');
useEffect(() => {
setTitle(modalData.title || '');
setDetails(modalData.details || '');
}, [modalData]);
const handleSave = () => {
const todo = { ...modalData, title, details };
if (modalData.id) {
onUpdate(todo as TodoItem);
} else {
onAdd(todo as TodoItem);
}
};
return (
<Dialog open={open} onClose={handleClose}>
<DialogTitle>{modalData.id ? 'Edit Todo' : 'Add Todo'}</DialogTitle>
<DialogContent>
<TextField
autoFocus
margin="dense"
label="Title"
type="text"
fullWidth
variant="standard"
value={title}
onChange={(e) => setTitle(e.target.value)}
/>
<TextField
margin="dense"
label="Details"
type="text"
fullWidth
variant="standard"
value={details}
onChange={(e) => setDetails(e.target.value)}
/>
</DialogContent>
<DialogActions>
<Button onClick={handleClose}>Cancel</Button>
<Button onClick={handleSave}>{modalData.id ? 'Update' : 'Add'}</Button>
</DialogActions>
</Dialog>
);
};
export default ModalComponent;
List.tsx
import React from 'react';
import { List, ListItem, ListItemText, IconButton, Typography } from '@mui/material';
import ArrowForwardIcon from '@mui/icons-material/ArrowForward';
import ArrowBackIcon from '@mui/icons-material/ArrowBack';
import EditIcon from '@mui/icons-material/Edit';
import { TodoItem } from './types';
interface ListComponentProps {
title: string;
items: TodoItem[];
onMove: (id: number, toType:'Todo' |'Start' | 'End') => void;
onEdit: (item: TodoItem) => void;
}
const ListComponent: React.FC<ListComponentProps> = ({ title, items, onMove, onEdit }) => {
return (
<div>
<Typography variant="h6">{title}</Typography>
<List>
{items.map(item => (
<ListItem key={item.id} secondaryAction={
<>
{title === 'Todo' && <IconButton edge="end" onClick={() => onMove(item.id, 'Start')}><ArrowForwardIcon /></IconButton>}
{title === 'Start' && (
<>
<IconButton edge="end" onClick={() => onMove(item.id, 'End')}><ArrowForwardIcon /></IconButton>
<IconButton edge="end" onClick={() => onMove(item.id, 'Todo')}><ArrowBackIcon /></IconButton>
</>
)}
{title === 'End' && <IconButton edge="end" onClick={() => onMove(item.id, 'Start')}><ArrowBackIcon /></IconButton>}
<IconButton edge="end" onClick={() => onEdit(item)}><EditIcon /></IconButton>
</>
}>
<ListItemText primary={item.title} secondary={item.details} />
</ListItem>
))}
</List>
</div>
);
};
export default ListComponent;
types.ts
export interface TodoItem {
id: number;
title: string;
details: string;
type: 'Todo' | 'Start' | 'End';
}