156 lines
3.6 KiB
TypeScript
156 lines
3.6 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<HTMLInputElement> {
|
|
user: User
|
|
}
|
|
|
|
function UserSelectButton(
|
|
{ user: { id, name, avatarUrl, color }, tabindex, ...props }:
|
|
UserSelectButtonProps,
|
|
) {
|
|
const eid = `assigneeUserId_${id}`
|
|
return (
|
|
<>
|
|
<input
|
|
aria-hidden='true'
|
|
type='radio'
|
|
class='peer sr-only'
|
|
id={eid}
|
|
name='assigneeUserId'
|
|
value={id}
|
|
{...props}
|
|
/>
|
|
<Label
|
|
for={eid}
|
|
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'
|
|
>
|
|
<Avatar
|
|
className='mb-2'
|
|
src={avatarUrl}
|
|
/>
|
|
<span
|
|
style={`color: #${color};`}
|
|
class='font-semibold text-center'
|
|
>
|
|
{name}
|
|
</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
|
|
checked={todoAssignUserId.value == null}
|
|
tabindex={0}
|
|
user={unassignedUser}
|
|
/>
|
|
</li>
|
|
{Object.values(users).map(
|
|
(user) => {
|
|
return (
|
|
<li>
|
|
<UserSelectButton
|
|
checked={todoAssignUserId.value == user.id}
|
|
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>
|
|
)
|
|
}
|