Done works
This commit is contained in:
parent
2ba678c4a3
commit
78bd64f100
|
@ -7,6 +7,7 @@ import * as $_app from './routes/_app.tsx'
|
||||||
import * as $admin from './routes/admin.tsx'
|
import * as $admin from './routes/admin.tsx'
|
||||||
import * as $api_joke from './routes/api/joke.ts'
|
import * as $api_joke from './routes/api/joke.ts'
|
||||||
import * as $api_todo from './routes/api/todo.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 $api_user from './routes/api/user.ts'
|
||||||
import * as $greet_name_ from './routes/greet/[name].tsx'
|
import * as $greet_name_ from './routes/greet/[name].tsx'
|
||||||
import * as $index from './routes/index.tsx'
|
import * as $index from './routes/index.tsx'
|
||||||
|
@ -24,6 +25,7 @@ const manifest = {
|
||||||
'./routes/admin.tsx': $admin,
|
'./routes/admin.tsx': $admin,
|
||||||
'./routes/api/joke.ts': $api_joke,
|
'./routes/api/joke.ts': $api_joke,
|
||||||
'./routes/api/todo.ts': $api_todo,
|
'./routes/api/todo.ts': $api_todo,
|
||||||
|
'./routes/api/todo/done.ts': $api_todo_done,
|
||||||
'./routes/api/user.ts': $api_user,
|
'./routes/api/user.ts': $api_user,
|
||||||
'./routes/greet/[name].tsx': $greet_name_,
|
'./routes/greet/[name].tsx': $greet_name_,
|
||||||
'./routes/index.tsx': $index,
|
'./routes/index.tsx': $index,
|
||||||
|
|
|
@ -21,12 +21,13 @@ const unassignedUserPlaceholder: User = {
|
||||||
color: '888888',
|
color: '888888',
|
||||||
}
|
}
|
||||||
|
|
||||||
interface UserSelectButtonProps extends JSX.HTMLAttributes<HTMLLabelElement> {
|
interface UserSelectButtonProps extends JSX.HTMLAttributes<HTMLInputElement> {
|
||||||
user: User
|
user: User
|
||||||
}
|
}
|
||||||
|
|
||||||
function UserSelectButton(
|
function UserSelectButton(
|
||||||
{ user: { id, avatarUrl, color }, ...props }: UserSelectButtonProps,
|
{ user: { id, name, avatarUrl, color }, tabindex, ...props }:
|
||||||
|
UserSelectButtonProps,
|
||||||
) {
|
) {
|
||||||
const eid = `assigneeUserId_${id}`
|
const eid = `assigneeUserId_${id}`
|
||||||
return (
|
return (
|
||||||
|
@ -34,17 +35,18 @@ function UserSelectButton(
|
||||||
<input
|
<input
|
||||||
aria-hidden='true'
|
aria-hidden='true'
|
||||||
type='radio'
|
type='radio'
|
||||||
class='hidden left-[99999px]'
|
class='peer sr-only'
|
||||||
id={eid}
|
id={eid}
|
||||||
name='assigneeUserId'
|
name='assigneeUserId'
|
||||||
value={id}
|
value={id}
|
||||||
|
{...props}
|
||||||
/>
|
/>
|
||||||
<Label
|
<Label
|
||||||
{...props}
|
|
||||||
for={eid}
|
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'
|
role='button'
|
||||||
aria-pressed='false'
|
|
||||||
>
|
>
|
||||||
<Avatar
|
<Avatar
|
||||||
className='mb-2'
|
className='mb-2'
|
||||||
|
@ -54,7 +56,7 @@ function UserSelectButton(
|
||||||
style={`color: #${color};`}
|
style={`color: #${color};`}
|
||||||
class='font-semibold text-center'
|
class='font-semibold text-center'
|
||||||
>
|
>
|
||||||
Shared
|
{name}
|
||||||
</span>
|
</span>
|
||||||
</Label>
|
</Label>
|
||||||
</>
|
</>
|
||||||
|
@ -91,13 +93,20 @@ export default function Dashboard({ users, unassignedTodos }: Props) {
|
||||||
Assignee
|
Assignee
|
||||||
<ul class='flex gap-2'>
|
<ul class='flex gap-2'>
|
||||||
<li>
|
<li>
|
||||||
<UserSelectButton tabindex={0} user={unassignedUser} />
|
<UserSelectButton
|
||||||
|
checked={todoAssignUserId.value == null}
|
||||||
|
tabindex={0}
|
||||||
|
user={unassignedUser}
|
||||||
|
/>
|
||||||
</li>
|
</li>
|
||||||
{Object.values(users).map(
|
{Object.values(users).map(
|
||||||
(user, i) => {
|
(user) => {
|
||||||
return (
|
return (
|
||||||
<li>
|
<li>
|
||||||
<UserSelectButton tabindex={i} user={user} />
|
<UserSelectButton
|
||||||
|
checked={todoAssignUserId.value == user.id}
|
||||||
|
user={user}
|
||||||
|
/>
|
||||||
</li>
|
</li>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|
|
@ -13,7 +13,9 @@ export function TodoList(
|
||||||
Props,
|
Props,
|
||||||
) {
|
) {
|
||||||
const todoItem = (
|
const todoItem = (
|
||||||
{ className, description, hideDone }: Pick<Todo, 'description'> & {
|
{ id, doneAt, className, description, hideDone }:
|
||||||
|
& Pick<Todo, 'description' | 'id' | 'doneAt'>
|
||||||
|
& {
|
||||||
className?: string
|
className?: string
|
||||||
hideDone?: boolean
|
hideDone?: boolean
|
||||||
},
|
},
|
||||||
|
@ -22,10 +24,24 @@ export function TodoList(
|
||||||
style={`border-color: #${color}`}
|
style={`border-color: #${color}`}
|
||||||
class={`${className || ''} ${
|
class={`${className || ''} ${
|
||||||
hideDone ? '' : 'border-l-4'
|
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>
|
<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>
|
</li>
|
||||||
)
|
)
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -8,7 +8,11 @@ type TodoPayload = z.infer<typeof TodoPayload>
|
||||||
|
|
||||||
async function createOrUpdate(todo: TodoPayload) {
|
async function createOrUpdate(todo: TodoPayload) {
|
||||||
if (!todo.id) {
|
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 })
|
return await db.todos.create({ data: newTodo })
|
||||||
} else {
|
} else {
|
||||||
return await db.todos.update({ where: { id: todo.id }, data: todo })
|
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> = {
|
export const handler: Handlers<Todo | null> = {
|
||||||
async POST(req, _ctx) {
|
async POST(req, _ctx) {
|
||||||
if (req.headers.get('content-type')?.includes('json')) {
|
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))
|
return new Response(JSON.stringify(result))
|
||||||
} else {
|
} else {
|
||||||
const form = await req.formData()
|
const form = await req.formData()
|
||||||
|
@ -29,7 +35,8 @@ export const handler: Handlers<Todo | null> = {
|
||||||
|
|
||||||
const todo = TodoPayload.parse({
|
const todo = TodoPayload.parse({
|
||||||
id: id,
|
id: id,
|
||||||
emoji: form.get('emoji')?.toString(),
|
emoji: form.get('emoji')?.toString() || null,
|
||||||
|
doneAt: form.get('doneAt')?.toString() || null,
|
||||||
description: form.get('description')?.toString(),
|
description: form.get('description')?.toString(),
|
||||||
assigneeUserId: form.get('assigneeUserId')?.toString(),
|
assigneeUserId: form.get('assigneeUserId')?.toString(),
|
||||||
})
|
})
|
||||||
|
@ -41,7 +48,7 @@ export const handler: Handlers<Todo | null> = {
|
||||||
await createOrUpdate(todo)
|
await createOrUpdate(todo)
|
||||||
|
|
||||||
const url = new URL(req.url)
|
const url = new URL(req.url)
|
||||||
url.pathname = '/admin'
|
url.pathname = '/'
|
||||||
return Response.redirect(url, 303)
|
return Response.redirect(url, 303)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
14
routes/api/todo/done.ts
Normal file
14
routes/api/todo/done.ts
Normal 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))
|
||||||
|
},
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
import { Handlers, PageProps } from '$fresh/server.ts'
|
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'
|
import Dashboard from '@homeman/islands/Dashboard.tsx'
|
||||||
|
|
||||||
interface Data {
|
interface Data {
|
||||||
|
|
Loading…
Reference in a new issue