147 lines
3.3 KiB
TypeScript
147 lines
3.3 KiB
TypeScript
|
import { Todo, User, UserWithTodos } from '@homeman/models.ts'
|
||
|
import { Signal, useSignal } from '@preact/signals'
|
||
|
import { TodoList } from '@homeman/islands/TodoList.tsx'
|
||
|
import { Dialog } from '@homeman/components/Dialog.tsx'
|
||
|
import { Label } from '@homeman/components/Label.tsx'
|
||
|
import { Input } from '@homeman/components/Input.tsx'
|
||
|
import { Avatar } from '@homeman/components/Avatar.tsx'
|
||
|
import { Button } from '@homeman/components/Button.tsx'
|
||
|
import { JSX } from 'preact'
|
||
|
|
||
|
interface Props {
|
||
|
users: Record<string, UserWithTodos>
|
||
|
unassignedTodos: Todo[]
|
||
|
}
|
||
|
|
||
|
const unassignedUserPlaceholder: User = {
|
||
|
id: '',
|
||
|
createdAt: new Date(),
|
||
|
name: 'Shared',
|
||
|
avatarUrl: 'http://placekitten.com/512/512',
|
||
|
color: '888888',
|
||
|
}
|
||
|
|
||
|
interface UserSelectButtonProps extends JSX.HTMLAttributes<HTMLLabelElement> {
|
||
|
user: User
|
||
|
}
|
||
|
|
||
|
function UserSelectButton(
|
||
|
{ user: { id, avatarUrl, color }, ...props }: UserSelectButtonProps,
|
||
|
) {
|
||
|
const eid = `assigneeUserId_${id}`
|
||
|
return (
|
||
|
<>
|
||
|
<input
|
||
|
aria-hidden='true'
|
||
|
type='radio'
|
||
|
class='hidden left-[99999px]'
|
||
|
id={eid}
|
||
|
name='assigneeUserId'
|
||
|
value={id}
|
||
|
/>
|
||
|
<Label
|
||
|
{...props}
|
||
|
for={eid}
|
||
|
className='cursor-pointer hover:bg-gray-500/20 rounded p-2'
|
||
|
role='button'
|
||
|
aria-pressed='false'
|
||
|
>
|
||
|
<Avatar
|
||
|
className='mb-2'
|
||
|
src={avatarUrl}
|
||
|
/>
|
||
|
<span
|
||
|
style={`color: #${color};`}
|
||
|
class='font-semibold text-center'
|
||
|
>
|
||
|
Shared
|
||
|
</span>
|
||
|
</Label>
|
||
|
</>
|
||
|
)
|
||
|
}
|
||
|
|
||
|
export default function Dashboard({ users, unassignedTodos }: Props) {
|
||
|
const todoAssignUserId: Signal<string | null> = useSignal(null)
|
||
|
const showAddTodoDialog = useSignal(false)
|
||
|
|
||
|
const unassignedUser: UserWithTodos = {
|
||
|
...unassignedUserPlaceholder,
|
||
|
assignedTodos: unassignedTodos,
|
||
|
}
|
||
|
|
||
|
return (
|
||
|
<main class='flex flex-col'>
|
||
|
<Dialog
|
||
|
headerTitle='Add todo'
|
||
|
show={showAddTodoDialog.value}
|
||
|
onClose={() => showAddTodoDialog.value = false}
|
||
|
>
|
||
|
<form
|
||
|
class='p-4 gap-4 flex flex-col'
|
||
|
action='/api/todo'
|
||
|
method='post'
|
||
|
encType='multipart/form-data'
|
||
|
>
|
||
|
<Label for='description'>
|
||
|
Description
|
||
|
<Input autofocus name='description' />
|
||
|
</Label>
|
||
|
<span>
|
||
|
Assignee
|
||
|
<ul class='flex gap-2'>
|
||
|
<li>
|
||
|
<UserSelectButton tabindex={0} user={unassignedUser} />
|
||
|
</li>
|
||
|
{Object.values(users).map(
|
||
|
(user, i) => {
|
||
|
return (
|
||
|
<li>
|
||
|
<UserSelectButton tabindex={i} user={user} />
|
||
|
</li>
|
||
|
)
|
||
|
},
|
||
|
)}
|
||
|
</ul>
|
||
|
</span>
|
||
|
<footer class='flex justify-end gap-2'>
|
||
|
<Button
|
||
|
type='button'
|
||
|
onClick={() => showAddTodoDialog.value = false}
|
||
|
>
|
||
|
Cancel
|
||
|
</Button>
|
||
|
<Input type='submit' value='Save' />
|
||
|
</footer>
|
||
|
</form>
|
||
|
</Dialog>
|
||
|
<h1 class='p-5 border-b-2 border-stone-500/20 text-2xl'>
|
||
|
Todos
|
||
|
</h1>
|
||
|
<ul class='p-4 relative flex overflow-x-scroll max-w-screen'>
|
||
|
<li>
|
||
|
<TodoList
|
||
|
onNewButtonClicked={() => {
|
||
|
console.log('shared new')
|
||
|
showAddTodoDialog.value = true
|
||
|
todoAssignUserId.value = null
|
||
|
}}
|
||
|
user={unassignedUser}
|
||
|
/>
|
||
|
</li>
|
||
|
{Object.values(users).map((u) => (
|
||
|
<li>
|
||
|
<TodoList
|
||
|
onNewButtonClicked={() => {
|
||
|
showAddTodoDialog.value = true
|
||
|
todoAssignUserId.value = u.id
|
||
|
}}
|
||
|
user={u}
|
||
|
/>
|
||
|
</li>
|
||
|
))}
|
||
|
</ul>
|
||
|
</main>
|
||
|
)
|
||
|
}
|