Done works

This commit is contained in:
Daniel Flanagan 2024-01-11 18:57:29 -06:00
parent 2ba678c4a3
commit 78bd64f100
Signed by: lytedev
GPG key ID: 5B2020A0F9921EF4
6 changed files with 69 additions and 21 deletions

View file

@ -7,6 +7,7 @@ import * as $_app from './routes/_app.tsx'
import * as $admin from './routes/admin.tsx'
import * as $api_joke from './routes/api/joke.ts'
import * as $api_todo from './routes/api/todo.ts'
import * as $api_todo_done from './routes/api/todo/done.ts'
import * as $api_user from './routes/api/user.ts'
import * as $greet_name_ from './routes/greet/[name].tsx'
import * as $index from './routes/index.tsx'
@ -24,6 +25,7 @@ const manifest = {
'./routes/admin.tsx': $admin,
'./routes/api/joke.ts': $api_joke,
'./routes/api/todo.ts': $api_todo,
'./routes/api/todo/done.ts': $api_todo_done,
'./routes/api/user.ts': $api_user,
'./routes/greet/[name].tsx': $greet_name_,
'./routes/index.tsx': $index,

View file

@ -21,12 +21,13 @@ const unassignedUserPlaceholder: User = {
color: '888888',
}
interface UserSelectButtonProps extends JSX.HTMLAttributes<HTMLLabelElement> {
interface UserSelectButtonProps extends JSX.HTMLAttributes<HTMLInputElement> {
user: User
}
function UserSelectButton(
{ user: { id, avatarUrl, color }, ...props }: UserSelectButtonProps,
{ user: { id, name, avatarUrl, color }, tabindex, ...props }:
UserSelectButtonProps,
) {
const eid = `assigneeUserId_${id}`
return (
@ -34,17 +35,18 @@ function UserSelectButton(
<input
aria-hidden='true'
type='radio'
class='hidden left-[99999px]'
class='peer sr-only'
id={eid}
name='assigneeUserId'
value={id}
{...props}
/>
<Label
{...props}
for={eid}
className='cursor-pointer hover:bg-gray-500/20 rounded p-2'
style={`border-color: #${color};`}
tabindex={tabindex}
className='cursor-pointer peer-checked:border-t-2 peer-checked:bg-gray-500/20 hover:bg-gray-500/25 rounded p-2'
role='button'
aria-pressed='false'
>
<Avatar
className='mb-2'
@ -54,7 +56,7 @@ function UserSelectButton(
style={`color: #${color};`}
class='font-semibold text-center'
>
Shared
{name}
</span>
</Label>
</>
@ -91,13 +93,20 @@ export default function Dashboard({ users, unassignedTodos }: Props) {
Assignee
<ul class='flex gap-2'>
<li>
<UserSelectButton tabindex={0} user={unassignedUser} />
<UserSelectButton
checked={todoAssignUserId.value == null}
tabindex={0}
user={unassignedUser}
/>
</li>
{Object.values(users).map(
(user, i) => {
(user) => {
return (
<li>
<UserSelectButton tabindex={i} user={user} />
<UserSelectButton
checked={todoAssignUserId.value == user.id}
user={user}
/>
</li>
)
},

View file

@ -13,7 +13,9 @@ export function TodoList(
Props,
) {
const todoItem = (
{ className, description, hideDone }: Pick<Todo, 'description'> & {
{ id, doneAt, className, description, hideDone }:
& Pick<Todo, 'description' | 'id' | 'doneAt'>
& {
className?: string
hideDone?: boolean
},
@ -22,10 +24,24 @@ export function TodoList(
style={`border-color: #${color}`}
class={`${className || ''} ${
hideDone ? '' : 'border-l-4'
} p-4 rounded drop-shadow-lg bg-white dark:bg-stone-900 flex flex-col`}
} p-4 rounded drop-shadow-lg bg-white dark:bg-stone-900 flex flex-col gap-2`}
>
{JSON.stringify(doneAt)}
<span class='text-xl'>{description}</span>
{hideDone ? '' : <Button class='mt-2'>Done</Button>}
{(hideDone || doneAt != null) ? '' : (
<Button
onClick={async () => {
await fetch('/api/todo/done', {
method: 'POST',
body: JSON.stringify({ id }),
})
// TODO: remove the todoitem?
// TODO: confetti
}}
>
Done
</Button>
)}
</li>
)
return (

View file

@ -8,7 +8,11 @@ type TodoPayload = z.infer<typeof TodoPayload>
async function createOrUpdate(todo: TodoPayload) {
if (!todo.id) {
const newTodo: Todo = { ...todo, id: ulid(), createdAt: new Date() }
const newTodo: Todo = {
...todo,
id: ulid(),
createdAt: new Date(),
}
return await db.todos.create({ data: newTodo })
} else {
return await db.todos.update({ where: { id: todo.id }, data: todo })
@ -18,7 +22,9 @@ async function createOrUpdate(todo: TodoPayload) {
export const handler: Handlers<Todo | null> = {
async POST(req, _ctx) {
if (req.headers.get('content-type')?.includes('json')) {
const result = await createOrUpdate(TodoPayload.parse(await req.json()))
const result = await createOrUpdate(
TodoPayload.parse(await req.json()),
)
return new Response(JSON.stringify(result))
} else {
const form = await req.formData()
@ -29,7 +35,8 @@ export const handler: Handlers<Todo | null> = {
const todo = TodoPayload.parse({
id: id,
emoji: form.get('emoji')?.toString(),
emoji: form.get('emoji')?.toString() || null,
doneAt: form.get('doneAt')?.toString() || null,
description: form.get('description')?.toString(),
assigneeUserId: form.get('assigneeUserId')?.toString(),
})
@ -41,7 +48,7 @@ export const handler: Handlers<Todo | null> = {
await createOrUpdate(todo)
const url = new URL(req.url)
url.pathname = '/admin'
url.pathname = '/'
return Response.redirect(url, 303)
}
},

14
routes/api/todo/done.ts Normal file
View file

@ -0,0 +1,14 @@
import { Handlers } from '$fresh/server.ts'
import { db, TodoModel } from '@homeman/models.ts'
const Model = TodoModel.pick({ id: true })
export const handler: Handlers = {
async POST(req, _ctx) {
const { id } = Model.parse(await req.json())
const todo = await db.todos.findFirst({ where: { id } })
todo.doneAt = new Date()
const newTodo = await db.todos.update({ where: { id }, data: todo })
return new Response(JSON.stringify(newTodo))
},
}

View file

@ -1,5 +1,5 @@
import { Handlers, PageProps } from '$fresh/server.ts'
import { db, Todo, User, UserWithTodos } from '@homeman/models.ts'
import { db, Todo, UserWithTodos } from '@homeman/models.ts'
import Dashboard from '@homeman/islands/Dashboard.tsx'
interface Data {