Task list route
This commit is contained in:
parent
d80aa2eb5f
commit
45c7fdc28a
|
|
@ -9,7 +9,7 @@
|
|||
</head>
|
||||
|
||||
<body class="m-0 min-h-screen">
|
||||
<div id="root" class="my-0 mx-auto"></div>
|
||||
<div id="root" class="my-0 mx-auto max-w-screen-sm"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
import { } from 'react'
|
||||
|
||||
export function AccordionItem({ title, children, isOpen, onClick }: { title: string; children: JSX.Element; isOpen: boolean, onClick: () => void; }) {
|
||||
return (
|
||||
<>
|
||||
<button type="button" className="accordion-title" onClick={onClick}>
|
||||
<span>{title}</span>
|
||||
<svg className={"w-3 h-3 shrink-0" + (isOpen ? "" : " rotate-180")} xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 10 6">
|
||||
<path stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M9 5 5 1 1 5" />
|
||||
</svg>
|
||||
</button>
|
||||
{isOpen && <div className="accordion-body">{children}</div>}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
export const taskTypes = {
|
||||
task: 'Užduotis',
|
||||
bug: "Bug'as",
|
||||
};
|
||||
|
||||
export const taskStatuses = {
|
||||
paused: 'Laukia',
|
||||
'in progress': 'Daroma',
|
||||
testing: 'Testuojama',
|
||||
released: 'Padaryta',
|
||||
};
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
import { } from 'react'
|
||||
|
||||
export function EnumSelect({ name, entries }: { name: string; entries: Record<string, string> }): JSX.Element {
|
||||
return (
|
||||
<select id={name} name={name}>
|
||||
{Object.entries(entries).map(([value, name]) => <option key={value} value={value}>{name}</option>)}
|
||||
</select>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
import { } from 'react'
|
||||
import { taskTypes, taskStatuses } from './consts';
|
||||
import { EnumSelect } from './enum-select';
|
||||
|
||||
export function TaskForm({ tasks, setTasks, setExpanded }: { tasks: TodoTasks, setTasks: (t: TodoTasks) => void, setExpanded: (p: number) => void }): JSX.Element {
|
||||
return (
|
||||
<form className="grid grid-cols-1 gap-y-8" onSubmit={event => {
|
||||
event.preventDefault();
|
||||
|
||||
const data = Object.fromEntries(new FormData(event.currentTarget));
|
||||
const newTask = {
|
||||
id: tasks.reduce((max, row) => row.id > max ? row.id : max, 1) + 1,
|
||||
type: data.type as TaskType,
|
||||
title: data.title as string,
|
||||
status: data.status as TaskStatus,
|
||||
};
|
||||
|
||||
|
||||
if (data.insert) {
|
||||
setTasks([newTask, ...tasks]);
|
||||
} else {
|
||||
setTasks([...tasks, newTask]);
|
||||
}
|
||||
|
||||
setExpanded(0);
|
||||
}}>
|
||||
<div>
|
||||
<label htmlFor="type">Tipas</label>
|
||||
<div className="mt-2">
|
||||
<EnumSelect name="type" entries={taskTypes} />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="title">Pavadinimas</label>
|
||||
<div className="mt-2">
|
||||
<input type="text" id="title" name="title" className="" />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="type">Statusas</label>
|
||||
<div className="mt-2">
|
||||
<EnumSelect name="status" entries={taskStatuses} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center mb-4">
|
||||
<input id="insert" name="insert" type="checkbox" className="w-5 h-5 accent-indigo-500" />
|
||||
<label htmlFor="insert" className="ms-2">Įterpti pradžioje</label>
|
||||
</div>
|
||||
<div className="mt-2 flex items-center justify-end gap-x-6">
|
||||
<button type="button" className="font-semibold" onClick={() => setExpanded(0)}>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
type="submit"
|
||||
className="bg-indigo-500 rounded-lg px-3 py-2 font-semibold text-white hover:bg-indigo-600"
|
||||
>
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
import { } from 'react'
|
||||
import { taskTypes, taskStatuses } from './consts';
|
||||
|
||||
export function TaskList({ tasks }: { tasks: TodoTasks }): JSX.Element {
|
||||
return (
|
||||
<table className="w-full">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Tipas</th>
|
||||
<th>Pavadinimas</th>
|
||||
<th>Statusas</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{tasks.map(task => <tr key={task.id}>
|
||||
<td>{taskTypes[task.type]}</td>
|
||||
<td>{task.title}</td>
|
||||
<td>{taskStatuses[task.status]}</td>
|
||||
</tr>)}
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -2,18 +2,42 @@
|
|||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@layer base {
|
||||
h1 {
|
||||
@apply text-2xl my-5;
|
||||
}
|
||||
|
||||
@layer utilities {
|
||||
p,
|
||||
li {
|
||||
@apply my-4;
|
||||
}
|
||||
|
||||
a.nav-button {
|
||||
@apply bg-neutral-100 dark:bg-neutral-900 cursor-pointer px-5 py-2 rounded-lg border border-transparent hover:border-indigo-500 transition-colors duration-1000;
|
||||
@apply bg-neutral-100 dark:bg-neutral-900 cursor-pointer px-5 py-2 rounded-lg border border-gray-300 hover:border-indigo-500 transition-colors duration-700;
|
||||
}
|
||||
|
||||
th {
|
||||
@apply border-b dark:border-slate-600 pl-8 pt-0 pb-3 text-slate-400 dark:text-slate-200 text-left;
|
||||
}
|
||||
|
||||
td {
|
||||
@apply border-b border-slate-100 dark:border-slate-700 p-4 pl-8 text-slate-500 dark:text-slate-400;
|
||||
}
|
||||
|
||||
select, input {
|
||||
@apply w-full rounded-lg border-0 px-3 py-2 ring-1 ring-inset ring-gray-300 focus:ring-2 focus:ring-indigo-500 focus:outline-none hover:ring-indigo-500 transition-shadow duration-700;
|
||||
}
|
||||
|
||||
.accordion-title {
|
||||
@apply text-2xl flex items-center justify-between w-full p-5 font-medium border border-b-0 border-slate-200 dark:focus:ring-slate-800 dark:border-slate-700 hover:bg-slate-100 dark:hover:bg-slate-800 gap-3;
|
||||
}
|
||||
|
||||
.accordion-title:first-child {
|
||||
@apply rounded-t-xl;
|
||||
}
|
||||
|
||||
.accordion-title:last-child, .accordion-body:last-child {
|
||||
@apply border-b rounded-b-xl;
|
||||
}
|
||||
|
||||
.accordion-body {
|
||||
@apply p-5 border border-b-0 border-slate-200 dark:border-slate-700;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,16 +1,42 @@
|
|||
import { useState } from 'react'
|
||||
import { AccordionItem } from '../components/accordion-item';
|
||||
import { TaskList } from '../components/task-list';
|
||||
import { TaskForm } from '../components/task-form';
|
||||
|
||||
function App() {
|
||||
const [count, setCount] = useState(0)
|
||||
const [tasks, setTasks] = useState<TodoTasks>([
|
||||
{
|
||||
id: 1,
|
||||
type: 'task',
|
||||
title: 'Lipk iš lovos',
|
||||
status: 'in progress',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
type: 'task',
|
||||
title: 'Susirask kavos',
|
||||
status: 'paused',
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
type: 'bug',
|
||||
title: 'Sudaryk TODO sąrašą',
|
||||
status: 'testing',
|
||||
},
|
||||
]);
|
||||
|
||||
const [expanded, setExpanded] = useState(0);
|
||||
|
||||
const handleAccordion = (panel: number) => setExpanded(expanded == panel ? -1 : panel);
|
||||
|
||||
return (
|
||||
<>
|
||||
<h1>Vite + React</h1>
|
||||
<div>
|
||||
<button onClick={() => setCount((count) => count + 1)} className="bg-neutral-100 dark:bg-neutral-900 cursor-pointer px-5 py-2 rounded-lg border border-transparent hover:border-indigo-500 transition-colors duration-1000">
|
||||
count is {count}
|
||||
</button>
|
||||
</div>
|
||||
<AccordionItem title='Užduočių sąrašas' isOpen={expanded == 0} onClick={() => handleAccordion(0)}>
|
||||
<TaskList tasks={tasks} />
|
||||
</AccordionItem>
|
||||
<AccordionItem title='Nauja užduotis' isOpen={expanded == 1} onClick={() => handleAccordion(1)}>
|
||||
<TaskForm tasks={tasks} setTasks={setTasks} setExpanded={setExpanded} />
|
||||
</AccordionItem>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,10 +7,8 @@ export default function Root() {
|
|||
<Link to={`/list`} className="nav-button">Sąrašas</Link>
|
||||
<Link to={`/fetch`} className="nav-button">Duomenys</Link>
|
||||
</nav>
|
||||
<main className="flex justify-center">
|
||||
<article>
|
||||
<main className="my-10">
|
||||
<Outlet />
|
||||
</article>
|
||||
</main>
|
||||
</>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1 +1,13 @@
|
|||
/// <reference types="vite/client" />
|
||||
|
||||
type TaskType = 'task' | 'bug';
|
||||
type TaskStatus = 'paused' | 'in progress' | 'testing' | 'released';
|
||||
class TodoTask {
|
||||
id: number;
|
||||
type: TaskType;
|
||||
title: string;
|
||||
status: TaskStatus;
|
||||
}
|
||||
|
||||
type TodoTasks = TodoTask[];
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue