Files

200 lines
5.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { NextPage } from "next";
import { useRouter } from "next/router";
import { TodoClient, Todos } from "../util/TodoClient";
import styles from "../styles/Todo.module.css";
import { SessionExpiredModal } from "../components/SessionExpiredModal";
import { Hanko } from "@teamhanko/hanko-elements";
const hankoAPI = process.env.NEXT_PUBLIC_HANKO_API!;
const todoAPI = process.env.NEXT_PUBLIC_TODO_API!;
const Todo: NextPage = () => {
const router = useRouter();
const todoClient = useMemo(() => new TodoClient(todoAPI), []);
const [hankoClient, setHankoClient] = useState<Hanko>();
const [todos, setTodos] = useState<Todos>([]);
const [description, setDescription] = useState<string>("");
const [error, setError] = useState<Error | null>(null);
const modalRef = useRef<HTMLDialogElement>(null);
useEffect(() => {
import("@teamhanko/hanko-elements").then(({ Hanko }) => setHankoClient(new Hanko(hankoAPI)));
}, []);
const redirectToProfile = () => {
router.push("/profile").catch(setError)
}
const redirectToLogin = useCallback(() => {
router.push("/").catch(setError)
}, [router]);
const addTodo = (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
const todo = {description, checked: false};
todoClient
.addTodo(todo)
.then((res) => {
if (res.status === 401) {
modalRef.current?.showModal();
return;
}
setDescription("");
listTodos();
return;
})
.catch((e) => {
setError(e);
});
};
const listTodos = useCallback(() => {
todoClient
.listTodos()
.then((res) => {
if (res.status === 401) {
modalRef.current?.showModal();
return;
}
return res.json();
})
.then((todo) => {
if (todo) {
setTodos(todo);
}
})
.catch((e) => {
setError(e);
});
}, [todoClient]);
const patchTodo = (id: string, checked: boolean) => {
todoClient
.patchTodo(id, checked)
.then((res) => {
if (res.status === 401) {
modalRef.current?.showModal();
return;
}
listTodos();
return;
})
.catch((e) => {
setError(e);
});
};
const deleteTodo = (id: string) => {
todoClient
.deleteTodo(id)
.then((res) => {
if (res.status === 401) {
modalRef.current?.showModal();
return;
}
listTodos();
return;
})
.catch((e) => {
setError(e);
});
};
const logout = () => {
hankoClient?.user
.logout()
.catch((e) => {
setError(e);
});
};
const changeDescription = (event: React.ChangeEvent<HTMLInputElement>) => {
setDescription(event.currentTarget.value);
};
const changeCheckbox = (event: React.ChangeEvent<HTMLInputElement>) => {
const {currentTarget} = event;
patchTodo(currentTarget.value, currentTarget.checked);
};
useEffect(() => listTodos(), [listTodos]);
useEffect(() => hankoClient?.onUserLoggedOut(() => {
redirectToLogin();
}), [hankoClient, redirectToLogin]);
useEffect(() => hankoClient?.onSessionNotPresent(() => {
redirectToLogin();
}), [hankoClient, redirectToLogin]);
useEffect(() => hankoClient?.onSessionExpired(() => {
modalRef.current?.showModal();
}), [hankoClient]);
return (
<>
<SessionExpiredModal ref={modalRef}/>
<nav className={styles.nav}>
<button onClick={logout} className={styles.button}>
Logout
</button>
<button onClick={redirectToProfile} className={styles.button}>
Profile
</button>
<button disabled className={styles.button}>
Todos
</button>
</nav>
<div className={styles.content}>
<h1 className={styles.headline}>Todos</h1>
<div className={styles.error}>{error?.message}</div>
<form onSubmit={addTodo} className={styles.form}>
<input
required
className={styles.input}
type={"text"}
value={description}
onChange={changeDescription}
/>
<button type={"submit"} className={styles.button}>
+
</button>
</form>
<div className={styles.list}>
{todos.map((todo, index) => (
<div className={styles.item} key={index}>
<input
className={styles.checkbox}
id={todo.todoID}
type={"checkbox"}
value={todo.todoID}
checked={todo.checked}
onChange={changeCheckbox}
/>
<label className={styles.description} htmlFor={todo.todoID}>
{todo.description}
</label>
<button className={styles.button} onClick={() => deleteTodo(todo.todoID!)}>
×
</button>
</div>
))}
</div>
</div>
</>
);
};
export default Todo;