Add todo frontend

This commit is contained in:
Kasper Juul Hermansen 2021-11-10 21:23:02 +01:00
parent b7a57461a9
commit f2e9fc67b7
Signed by: kjuulh
GPG Key ID: 0F95C140730F2F23
15 changed files with 215 additions and 5 deletions

View File

@ -0,0 +1,9 @@
import { FC } from "react";
interface PageHeadingProps {
title: string;
}
export const PageHeading: FC<PageHeadingProps> = ({ title }) => (
<h3 className="font-bold text-xl tracking-wide">{title}</h3>
);

View File

@ -0,0 +1,3 @@
export const ProjectsHeading = (props: { title: string }) => (
<h5 className="text-sm font-bold">{props.title}</h5>
);

View File

@ -0,0 +1,25 @@
import { useState } from "react";
import { CollapsedAddTodo } from "@src/components/todos/collapsed/collapsedAddTodo";
import { AddTodoForm } from "@src/components/todos/collapsed/addTodoForm";
import { CollapsedState } from "@src/components/todos/collapsed/collapsedState";
export function AddTodo() {
const [collapsed, setCollapsed] = useState<CollapsedState>(
CollapsedState.collapsed
);
if (collapsed === CollapsedState.collapsed) {
return (
<CollapsedAddTodo
onClick={() => setCollapsed(CollapsedState.notCollapsed)}
/>
);
}
return (
<AddTodoForm
onAdd={(todoName) => {}}
onClose={() => setCollapsed(CollapsedState.collapsed)}
/>
);
}

View File

@ -0,0 +1,35 @@
import { FC, useState } from "react";
export const AddTodoForm: FC<{
onAdd: (todoName: string) => void;
onClose: () => void;
}> = ({ onAdd, onClose }) => {
const [todoName, setTodoName] = useState("");
return (
<div className="p-2 space-y-1">
<div className="todo-input-form py-2 px-4 bg-gray-900 border border-gray-600">
<input
type="text"
placeholder="Todo name"
className="text-sm"
value={todoName}
onChange={(e) => setTodoName(e.target.value)}
/>
</div>
<div className="space-x-2">
<button
disabled={!todoName}
onClick={() => {
onAdd(todoName);
setTodoName("");
}}
className="disabled:text-gray-800 transition"
>
Add todo
</button>
<button onClick={onClose}>Cancel</button>
</div>
</div>
);
};

View File

@ -0,0 +1,5 @@
import { FC } from "react";
export const CollapsedAddTodo: FC<{ onClick: () => void }> = ({ onClick }) => {
return <div onClick={onClick}>Add todo</div>;
};

View File

@ -0,0 +1,12 @@
type Collapsed = true;
type NotCollapsed = false;
export type CollapsedState = Collapsed | NotCollapsed;
export const CollapsedState: {
collapsed: Collapsed;
notCollapsed: NotCollapsed;
} = {
collapsed: true,
notCollapsed: false,
};

View File

@ -0,0 +1,2 @@
export * from "./todoItem";
export * from "./todoList";

View File

@ -0,0 +1,20 @@
import { StatusState, Todo } from "@src/core/entities/todo";
import { FC } from "react";
interface TodoCheckmarkProps {
updateTodo: (todo: Todo) => void;
todo: Todo;
}
export const TodoCheckmark: FC<TodoCheckmarkProps> = (props) => (
<div
onClick={() =>
props.updateTodo({ ...props.todo, status: !props.todo.status })
}
className={`h-5 w-5 rounded-full border dark:border-gray-700 ${
props.todo.status === StatusState.done
? "dark:bg-gray-700"
: "hover:dark:bg-gray-600"
}`}
/>
);

View File

@ -0,0 +1,17 @@
import { Todo } from "@src/core/entities/todo";
import { FC } from "react";
import { TodoCheckmark } from "@src/components/todos/todoCheckmark";
interface TodoItemProps {
todo: Todo;
updateTodo: (todo: Todo) => void;
}
export const TodoItem: FC<TodoItemProps> = (props) => (
<div className="py-3 border-b border-gray-300 dark:border-gray-700">
<div className="flex items-center space-x-4">
<TodoCheckmark {...props} />
<span className="pb-1">{props.todo.name}</span>
</div>
</div>
);

View File

@ -0,0 +1,21 @@
import { Todo } from "@src/core/entities/todo";
import { TodoItem } from "@src/components/todos/todoItem";
import { AddTodo } from "@src/components/todos/addTodo";
export const TodoList = (props: { todos: Todo[] }) => (
<>
<ul id="inbox">
{props.todos.map((t, i) => (
<li key={i}>
<TodoItem
todo={t}
updateTodo={(todo) => {
console.log(todo);
}}
/>
</li>
))}
</ul>
<AddTodo />
</>
);

View File

@ -0,0 +1,18 @@
import { StatusState, Todo } from "@src/core/entities/todo";
export const createTodo = (name: string): Todo => ({
name,
status: StatusState.notDone,
});
const createDoneTodo = (name: string) => ({
...createTodo(name),
status: true,
});
const todos: Todo[] = [
createTodo("go to dentist"),
createTodo("Check e-Boks"),
createDoneTodo("Check and clean mail"),
];
export const getInboxTodos = () => todos;

View File

@ -0,0 +1,12 @@
type NotDone = false;
type Done = true;
type StatusState = NotDone | Done;
export const StatusState: { done: Done; notDone: NotDone } = {
done: true,
notDone: false,
};
export interface Todo {
name: string;
status: StatusState;
}

View File

@ -1,5 +1,18 @@
export default function Home() { import { useMemo } from "react";
import { getInboxTodos } from "@src/core/actions/todos";
import { PageHeading } from "@src/components/common/headings/pageHeading";
import { TodoList } from "@src/components/todos";
const HomePage = () => {
const inboxTodos = useMemo(() => getInboxTodos(), []);
return ( return (
<div>Hello</div> <main className="py-8 px-14 space-y-6">
) <section className="space-y-2">
} <PageHeading title="Inbox" />
<TodoList todos={inboxTodos} />
</section>
</main>
);
};
export default HomePage;

View File

@ -1,3 +1,21 @@
@tailwind base; @tailwind base;
@tailwind components; @tailwind components;
input {
@apply shadow-none placeholder-gray-300 dark:placeholder-gray-700 border-none outline-none;
background: transparent;
}
*:focus {
outline: none;
}
@tailwind utilities; @tailwind utilities;
* {
@apply dark:text-gray-100
}
body {
@apply h-screen dark:bg-black
}

View File

@ -3,7 +3,7 @@ module.exports = {
purge: { purge: {
content: ["./src/**/*.{ts,tsx}"], content: ["./src/**/*.{ts,tsx}"],
}, },
darkMode: false, // or 'media' or 'class' darkMode: "media", // or 'media' or 'class'
theme: { theme: {
extend: {}, extend: {},
}, },