WIP
This commit is contained in:
parent
573692c6a7
commit
d0230b95b5
6 changed files with 145 additions and 135 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -17,3 +17,5 @@ node_modules/
|
|||
*.db-shm
|
||||
|
||||
/static/uploads
|
||||
|
||||
/data
|
||||
|
|
6
db.ts
6
db.ts
|
@ -2,7 +2,7 @@ import {
|
|||
createPentagon,
|
||||
TableDefinition,
|
||||
} from 'https://deno.land/x/pentagon@v0.1.5/mod.ts'
|
||||
import { DoneTaskModel, TodoModel, UserModel } from '@homeman/models.ts'
|
||||
import { TaskModel, TodoModel, UserModel } from '@homeman/models.ts'
|
||||
|
||||
export const kv = await Deno.openKv('homeman.db')
|
||||
|
||||
|
@ -19,8 +19,8 @@ export const schema: Record<string, TableDefinition> = {
|
|||
assignee: ['users', UserModel, 'assigneeUserId', 'id'],
|
||||
},
|
||||
},
|
||||
doneTasks: {
|
||||
schema: DoneTaskModel,
|
||||
tasks: {
|
||||
schema: TaskModel,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -58,16 +58,10 @@ export function toPhase(dt?: Date | null): z.infer<typeof DailyPhase> {
|
|||
|
||||
const Task = z.object({
|
||||
id: z.string(),
|
||||
description: z.string(),
|
||||
emoji: z.string().nullable(),
|
||||
doneAt: z.date().nullable(),
|
||||
phase: DailyPhase,
|
||||
})
|
||||
export const TaskModel = Task
|
||||
export type Task = z.infer<typeof Task>
|
||||
|
||||
const DoneTask = Task.pick({
|
||||
id: true,
|
||||
doneAt: true,
|
||||
})
|
||||
export const DoneTaskModel = DoneTask
|
||||
export type DoneTask = z.infer<typeof DoneTask>
|
||||
|
|
60
playground.ts
Normal file
60
playground.ts
Normal file
|
@ -0,0 +1,60 @@
|
|||
import { DailyPhase, TaskModel } from '@homeman/models.ts'
|
||||
import { db } from '@homeman/db.ts'
|
||||
import { ulid } from 'https://deno.land/x/ulid@v0.3.0/mod.ts'
|
||||
|
||||
const hardcoded: Record<DailyPhase, [string, string][]> = {
|
||||
'Morning': [
|
||||
['🥣', 'Breakfast'],
|
||||
['🪥', 'Brush teeth'],
|
||||
['👕', 'Get dressed'],
|
||||
['🙏', 'Ask and thank Jesus'],
|
||||
['✝️', 'Bible story or devotional'],
|
||||
['📚', 'School: with Mrs. Emily or Mama'],
|
||||
['🎨', 'Create time: 🧶craft ✏️ draw 🧑🏼🎨 paint'],
|
||||
['🏗️', ' Build time: 🧱legos 🚂train tracks 🏎magna tiles'],
|
||||
['👯', 'Friend time: playdate or neighbor time'],
|
||||
['🚗', 'Outing: 📚library 🌳park 🥑groceries ☕ coffee shop'],
|
||||
],
|
||||
'Midday': [
|
||||
['🥓', 'Lunch'],
|
||||
['🧹', 'Tidy time'],
|
||||
['🤫', 'Quiet time'],
|
||||
['🏃', 'BIG energy time: 🚲bike 🥁 drums 🤸 silly play'],
|
||||
['👯', 'Friend time: playdate or neighbor time'],
|
||||
['🚗', 'Outing: 📚library 🌳park 🥑groceries'],
|
||||
],
|
||||
'Evening': [
|
||||
['🍽️', 'Dinner'],
|
||||
['🚗', 'Outing'],
|
||||
['👪', 'Family time: 🃏games 🕺dance party 🤸silly play 🏋️workout'],
|
||||
],
|
||||
'Bedtime': [
|
||||
['🪥', 'Brush teeth'],
|
||||
['🛁', 'Take bath'],
|
||||
['👕', 'Put on pajamas'],
|
||||
['🙏', 'Ask and thank Jesus'],
|
||||
['✝️', 'Bible story or devotional'],
|
||||
['📕', 'Read a story'],
|
||||
['❤', 'Emotions check in: 😭 😡 😂 😟 😣 😀 ☹️ 😰 😁'],
|
||||
],
|
||||
'Night': [
|
||||
['👨⚖️', 'Sleep!'],
|
||||
],
|
||||
}
|
||||
Object.entries(hardcoded).forEach(async ([tphase, phaseTasks]) => {
|
||||
const phase = tphase as DailyPhase
|
||||
for (const [emoji, description] of phaseTasks) {
|
||||
const task = TaskModel.parse({
|
||||
phase,
|
||||
emoji,
|
||||
description,
|
||||
id: ulid(),
|
||||
doneAt: null,
|
||||
})
|
||||
console.log(task)
|
||||
const result = await db.tasks.create({
|
||||
data: task,
|
||||
})
|
||||
console.log('after create:', result)
|
||||
}
|
||||
})
|
|
@ -1,81 +1,81 @@
|
|||
import { Handlers } from '$fresh/server.ts'
|
||||
import { DoneTask, DoneTaskModel } from '@homeman/models.ts'
|
||||
import { Task, TaskModel } from '@homeman/models.ts'
|
||||
import { db, kv } from '@homeman/db.ts'
|
||||
import { ulid } from 'https://deno.land/x/ulid@v0.3.0/mod.ts'
|
||||
import { z } from 'https://deno.land/x/zod@v3.21.4/mod.ts'
|
||||
|
||||
const DoneTaskPayload = DoneTaskModel.partial({ doneAt: true })
|
||||
type DoneTaskPayload = z.infer<typeof DoneTaskPayload>
|
||||
const TaskPayload = TaskModel.partial({ id: true })
|
||||
type TaskPayload = z.infer<typeof TaskPayload>
|
||||
|
||||
export const handler: Handlers<DoneTask | null> = {
|
||||
async function createOrUpdate(task: TaskPayload) {
|
||||
if (!task.id || task.id === '') {
|
||||
const newTask: Task = {
|
||||
...task,
|
||||
id: ulid(),
|
||||
}
|
||||
const result = await db.tasks.create({ data: newTask })
|
||||
await kv.set(['last_task_updated'], newTask.id)
|
||||
return result
|
||||
} else {
|
||||
const result = await db.tasks.update({ where: { id: task.id }, data: task })
|
||||
await kv.set(['last_task_updated'], task.id)
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
export const handler: Handlers<Task | null> = {
|
||||
async POST(req, _ctx) {
|
||||
// a task is marked done
|
||||
let id: string | undefined
|
||||
if (req.headers.get('content-type')?.includes('json')) {
|
||||
id = DoneTaskPayload.parse(await req.json()).id
|
||||
} else {
|
||||
id = new URL(req.url).searchParams.get('id') || undefined
|
||||
}
|
||||
console.log('done task post:', id)
|
||||
if (!id) {
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
message: "can't complete task with id empty string",
|
||||
}),
|
||||
{ status: 400 },
|
||||
const result = await createOrUpdate(
|
||||
TaskPayload.parse(await req.json()),
|
||||
)
|
||||
return new Response(JSON.stringify(result))
|
||||
} else {
|
||||
const form = await req.formData()
|
||||
const id = form.get('id')?.toString() || undefined
|
||||
|
||||
const doneAt = form.get('doneAt')
|
||||
console.log('task POST doneAt:', doneAt)
|
||||
|
||||
const task = TaskPayload.parse({
|
||||
id: id,
|
||||
emoji: form.get('emoji')?.toString() || null,
|
||||
doneAt: form.get('doneAt')?.toString() || null,
|
||||
description: form.get('description')?.toString(),
|
||||
assigneeUserId: form.get('assigneeUserId')?.toString() || null,
|
||||
})
|
||||
|
||||
if (!id) {
|
||||
delete task.id
|
||||
}
|
||||
|
||||
if (!task.assigneeUserId) {
|
||||
delete task.id
|
||||
}
|
||||
|
||||
await createOrUpdate(task)
|
||||
|
||||
const url = new URL(req.url)
|
||||
url.pathname = '/'
|
||||
return Response.redirect(url, 303)
|
||||
}
|
||||
|
||||
const newDoneTask: DoneTask = {
|
||||
id,
|
||||
doneAt: new Date(),
|
||||
}
|
||||
const res = await db.doneTasks.create({
|
||||
data: newDoneTask,
|
||||
})
|
||||
console.log('done task create result:', res)
|
||||
console.log(await db.doneTasks.findMany({}))
|
||||
|
||||
await kv.set(['last_task_updated'], id)
|
||||
|
||||
return new Response(JSON.stringify({ id }))
|
||||
},
|
||||
async DELETE(req, _ctx) {
|
||||
let id: string | undefined
|
||||
// task: form or query params or json
|
||||
let data
|
||||
if (req.headers.get('content-type')?.includes('json')) {
|
||||
id = DoneTaskPayload.parse(await req.json()).id
|
||||
data = await req.json()
|
||||
} else {
|
||||
id = new URL(req.url).searchParams.get('id') || undefined
|
||||
data = { id: new URL(req.url).searchParams.get('id') }
|
||||
}
|
||||
console.log('done task delete:', id)
|
||||
if (!id) {
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
message: "can't uncomplete task with id empty string",
|
||||
}),
|
||||
{ status: 400 },
|
||||
)
|
||||
}
|
||||
if (!id) {
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
message: "can't complete task with id empty string",
|
||||
}),
|
||||
{ status: 400 },
|
||||
)
|
||||
}
|
||||
|
||||
await db.doneTasks.deleteMany({
|
||||
where: {
|
||||
id,
|
||||
},
|
||||
})
|
||||
|
||||
await kv.set(['last_task_updated'], id)
|
||||
|
||||
return new Response(JSON.stringify({ id }))
|
||||
console.log('delete task data:', data)
|
||||
const taskData = TaskModel.pick({ id: true }).parse(data)
|
||||
const result = await db.tasks.delete({ where: taskData })
|
||||
await kv.set(['last_task_updated'], taskData.id)
|
||||
return new Response(JSON.stringify(result))
|
||||
},
|
||||
async GET(req, ctx) {
|
||||
// TODO: json or query params
|
||||
// task: json or query params
|
||||
const accept = req.headers.get('accept')
|
||||
if (accept === 'text/event-stream') {
|
||||
console.log('Request for task event stream')
|
||||
|
@ -98,7 +98,7 @@ export const handler: Handlers<DoneTask | null> = {
|
|||
continue
|
||||
}
|
||||
|
||||
const task = await db.doneTask.findFirst({
|
||||
const task = await db.tasks.findFirst({
|
||||
where: { id: entry.value },
|
||||
})
|
||||
const chunk = `data: ${
|
||||
|
@ -108,21 +108,21 @@ export const handler: Handlers<DoneTask | null> = {
|
|||
value: task,
|
||||
})
|
||||
}\n\n`
|
||||
console.log('todo event chunk:', chunk)
|
||||
console.log('task event chunk:', chunk)
|
||||
controller.enqueue(new TextEncoder().encode(chunk))
|
||||
}
|
||||
if (entries.done) {
|
||||
return
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(`Error refreshing todo:`, e)
|
||||
console.error(`Error refreshing task:`, e)
|
||||
}
|
||||
}
|
||||
},
|
||||
cancel() {
|
||||
stream.cancel()
|
||||
console.log(
|
||||
`Closed todo updates stream to ${JSON.stringify(ctx.remoteAddr)}`,
|
||||
`Closed task updates stream to ${JSON.stringify(ctx.remoteAddr)}`,
|
||||
)
|
||||
},
|
||||
})
|
||||
|
@ -132,8 +132,14 @@ export const handler: Handlers<DoneTask | null> = {
|
|||
},
|
||||
})
|
||||
}
|
||||
return new Response(
|
||||
JSON.stringify({ done: await db.doneTasks.findMany({}) }),
|
||||
)
|
||||
const data = await req.json().catch(() => {})
|
||||
const taskData = TaskModel.pick({ id: true }).safeParse(data)
|
||||
if (taskData.success) {
|
||||
return new Response(
|
||||
JSON.stringify(await db.tasks.findFirst({ where: taskData.data })),
|
||||
)
|
||||
} else {
|
||||
return new Response(JSON.stringify(await db.tasks.findMany({})))
|
||||
}
|
||||
},
|
||||
}
|
||||
|
|
|
@ -1,72 +1,20 @@
|
|||
import { Handlers, PageProps } from '$fresh/server.ts'
|
||||
import { DailyPhase, DoneTask, Task } from '@homeman/models.ts'
|
||||
import { Task } from '@homeman/models.ts'
|
||||
import { Routine } from '@homeman/islands/Routine.tsx'
|
||||
import { db } from '@homeman/db.ts'
|
||||
// import { db, kv, Todo, UserWithTodos } from '@homeman/models.ts'
|
||||
|
||||
interface Data {
|
||||
tasks: Task[]
|
||||
done: DoneTask[]
|
||||
}
|
||||
|
||||
export const handler: Handlers = {
|
||||
async GET(_req, ctx) {
|
||||
const tasks: Task[] = Array.from([])
|
||||
console.log(tasks)
|
||||
|
||||
const hardcoded: Record<DailyPhase, [string, string][]> = {
|
||||
'Morning': [
|
||||
['🥣', 'Breakfast'],
|
||||
['🪥', 'Brush teeth'],
|
||||
['👕', 'Get dressed'],
|
||||
['🙏', 'Ask and thank Jesus'],
|
||||
['✝️', 'Bible story or devotional'],
|
||||
['📚', 'School: with Mrs. Emily or Mama'],
|
||||
['🎨', 'Create time: 🧶craft ✏️ draw 🧑🏼🎨 paint'],
|
||||
['🏗️', ' Build time: 🧱legos 🚂train tracks 🏎magna tiles'],
|
||||
['👯', 'Friend time: playdate or neighbor time'],
|
||||
['🚗', 'Outing: 📚library 🌳park 🥑groceries ☕ coffee shop'],
|
||||
],
|
||||
'Midday': [
|
||||
['🥓', 'Lunch'],
|
||||
['🧹', 'Tidy time'],
|
||||
['🤫', 'Quiet time'],
|
||||
['🏃', 'BIG energy time: 🚲bike 🥁 drums 🤸 silly play'],
|
||||
['👯', 'Friend time: playdate or neighbor time'],
|
||||
['🚗', 'Outing: 📚library 🌳park 🥑groceries'],
|
||||
],
|
||||
'Evening': [
|
||||
['🍽️', 'Dinner'],
|
||||
['🚗', 'Outing'],
|
||||
['👪', 'Family time: 🃏games 🕺dance party 🤸silly play 🏋️workout'],
|
||||
],
|
||||
'Bedtime': [
|
||||
['🪥', 'Brush teeth'],
|
||||
['🛁', 'Take bath'],
|
||||
['👕', 'Put on pajamas'],
|
||||
['🙏', 'Ask and thank Jesus'],
|
||||
['✝️', 'Bible story or devotional'],
|
||||
['📕', 'Read a story'],
|
||||
['❤', 'Emotions check in: 😭 😡 😂 😟 😣 😀 ☹️ 😰 😁'],
|
||||
],
|
||||
'Night': [
|
||||
['👨⚖️', 'Sleep!'],
|
||||
],
|
||||
}
|
||||
Object.entries(hardcoded).forEach(([tphase, phaseTasks]) => {
|
||||
const phase = tphase as DailyPhase
|
||||
for (const [emoji, id] of phaseTasks) {
|
||||
tasks.push({ phase, emoji, id, doneAt: null })
|
||||
}
|
||||
})
|
||||
|
||||
const done = await db.doneTasks.findMany({})
|
||||
console.log({ done })
|
||||
|
||||
return ctx.render({ tasks, done })
|
||||
const tasks = await db.tasks.findMany({})
|
||||
return ctx.render({ tasks })
|
||||
},
|
||||
}
|
||||
|
||||
export default function Page(props: PageProps<Data>) {
|
||||
return <Routine tasks={props.data.tasks} done={props.data.done} />
|
||||
return <Routine tasks={props.data.tasks} />
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue