No persistence, but we have routine stuff working
This commit is contained in:
parent
074d058825
commit
2ce48df6f6
59
common.ts
Normal file
59
common.ts
Normal file
|
@ -0,0 +1,59 @@
|
|||
import { confetti } from 'https://esm.sh/@tsparticles/confetti@3.0.3'
|
||||
import { IS_BROWSER } from '$fresh/runtime.ts'
|
||||
|
||||
let fireworks: HTMLAudioElement | null
|
||||
if (IS_BROWSER) {
|
||||
fireworks = new Audio('/fireworks.mp3')
|
||||
}
|
||||
|
||||
export function excitement() {
|
||||
if (IS_BROWSER) {
|
||||
const count = 200,
|
||||
defaults = {
|
||||
origin: { y: 0.7 },
|
||||
}
|
||||
|
||||
if (fireworks) fireworks.play()
|
||||
|
||||
// deno-lint-ignore no-inner-declarations
|
||||
function fire(particleRatio: number, opts: {
|
||||
spread?: number
|
||||
startVelocity?: number
|
||||
decay?: number
|
||||
scalar?: number
|
||||
}) {
|
||||
confetti(
|
||||
Object.assign({}, defaults, opts, {
|
||||
particleCount: Math.floor(count * particleRatio),
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
fire(0.25, {
|
||||
spread: 26,
|
||||
startVelocity: 55,
|
||||
})
|
||||
|
||||
fire(0.2, {
|
||||
spread: 60,
|
||||
})
|
||||
|
||||
fire(0.35, {
|
||||
spread: 100,
|
||||
decay: 0.91,
|
||||
scalar: 0.8,
|
||||
})
|
||||
|
||||
fire(0.1, {
|
||||
spread: 120,
|
||||
startVelocity: 25,
|
||||
decay: 0.92,
|
||||
scalar: 1.2,
|
||||
})
|
||||
|
||||
fire(0.1, {
|
||||
spread: 120,
|
||||
startVelocity: 45,
|
||||
})
|
||||
}
|
||||
}
|
27
db.ts
Normal file
27
db.ts
Normal file
|
@ -0,0 +1,27 @@
|
|||
import {
|
||||
createPentagon,
|
||||
TableDefinition,
|
||||
} from 'https://deno.land/x/pentagon@v0.1.5/mod.ts'
|
||||
import { TaskModel, TodoModel, UserModel } from '@homeman/models.ts'
|
||||
|
||||
export const kv = await Deno.openKv('homeman.db')
|
||||
|
||||
export const schema: Record<string, TableDefinition> = {
|
||||
users: {
|
||||
schema: UserModel,
|
||||
relations: {
|
||||
assignedTodos: ['todos', [TodoModel], 'id', 'assigneeUserId'],
|
||||
},
|
||||
},
|
||||
todos: {
|
||||
schema: TodoModel,
|
||||
relations: {
|
||||
assignee: ['users', UserModel, 'assigneeUserId', 'id'],
|
||||
},
|
||||
},
|
||||
tasks: {
|
||||
schema: TaskModel,
|
||||
},
|
||||
}
|
||||
|
||||
export const db = createPentagon(kv, schema)
|
|
@ -11,11 +11,13 @@ 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'
|
||||
import * as $routine from './routes/routine.tsx'
|
||||
import * as $Admin from './islands/Admin.tsx'
|
||||
import * as $Clock from './islands/Clock.tsx'
|
||||
import * as $Counter from './islands/Counter.tsx'
|
||||
import * as $Dashboard from './islands/Dashboard.tsx'
|
||||
import * as $Nav from './islands/Nav.tsx'
|
||||
import * as $Routine from './islands/Routine.tsx'
|
||||
import * as $TodoList from './islands/TodoList.tsx'
|
||||
import { type Manifest } from '$fresh/server.ts'
|
||||
|
||||
|
@ -30,6 +32,7 @@ const manifest = {
|
|||
'./routes/api/user.ts': $api_user,
|
||||
'./routes/greet/[name].tsx': $greet_name_,
|
||||
'./routes/index.tsx': $index,
|
||||
'./routes/routine.tsx': $routine,
|
||||
},
|
||||
islands: {
|
||||
'./islands/Admin.tsx': $Admin,
|
||||
|
@ -37,6 +40,7 @@ const manifest = {
|
|||
'./islands/Counter.tsx': $Counter,
|
||||
'./islands/Dashboard.tsx': $Dashboard,
|
||||
'./islands/Nav.tsx': $Nav,
|
||||
'./islands/Routine.tsx': $Routine,
|
||||
'./islands/TodoList.tsx': $TodoList,
|
||||
},
|
||||
baseUrl: import.meta.url,
|
||||
|
|
|
@ -7,9 +7,8 @@ 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'
|
||||
import { confetti } from 'https://esm.sh/@tsparticles/confetti@3.0.3'
|
||||
import { useEffect } from 'preact/hooks'
|
||||
import { IS_BROWSER } from '$fresh/runtime.ts'
|
||||
import { excitement } from '@homeman/common.ts'
|
||||
|
||||
interface Props {
|
||||
users: Record<string, UserWithTodos>
|
||||
|
@ -31,63 +30,6 @@ interface UserSelectButtonProps extends JSX.HTMLAttributes<HTMLInputElement> {
|
|||
user: User
|
||||
}
|
||||
|
||||
let fireworks: HTMLAudioElement | null
|
||||
if (IS_BROWSER) {
|
||||
fireworks = new Audio('/fireworks.mp3')
|
||||
}
|
||||
|
||||
function excitement() {
|
||||
if (IS_BROWSER) {
|
||||
const count = 200,
|
||||
defaults = {
|
||||
origin: { y: 0.7 },
|
||||
}
|
||||
|
||||
if (fireworks) fireworks.play()
|
||||
|
||||
// deno-lint-ignore no-inner-declarations
|
||||
function fire(particleRatio: number, opts: {
|
||||
spread?: number
|
||||
startVelocity?: number
|
||||
decay?: number
|
||||
scalar?: number
|
||||
}) {
|
||||
confetti(
|
||||
Object.assign({}, defaults, opts, {
|
||||
particleCount: Math.floor(count * particleRatio),
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
fire(0.25, {
|
||||
spread: 26,
|
||||
startVelocity: 55,
|
||||
})
|
||||
|
||||
fire(0.2, {
|
||||
spread: 60,
|
||||
})
|
||||
|
||||
fire(0.35, {
|
||||
spread: 100,
|
||||
decay: 0.91,
|
||||
scalar: 0.8,
|
||||
})
|
||||
|
||||
fire(0.1, {
|
||||
spread: 120,
|
||||
startVelocity: 25,
|
||||
decay: 0.92,
|
||||
scalar: 1.2,
|
||||
})
|
||||
|
||||
fire(0.1, {
|
||||
spread: 120,
|
||||
startVelocity: 45,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function UserSelectButton(
|
||||
{ user: { id, name, avatarUrl, color }, tabindex, ...props }:
|
||||
UserSelectButtonProps,
|
||||
|
|
81
islands/Routine.tsx
Normal file
81
islands/Routine.tsx
Normal file
|
@ -0,0 +1,81 @@
|
|||
import { DailyPhase, DailyPhaseModel, Task, toPhase } from '@homeman/models.ts'
|
||||
import { useSignal } from '@preact/signals'
|
||||
import { excitement } from '@homeman/common.ts'
|
||||
|
||||
export interface Props {
|
||||
tasks: Task[]
|
||||
}
|
||||
|
||||
interface TaskWithIndex extends Task {
|
||||
index: number
|
||||
}
|
||||
|
||||
const phaseEmoji: Record<DailyPhase, string> = {
|
||||
Morning: '🌄',
|
||||
Midday: '🌞',
|
||||
Evening: '🌆',
|
||||
Bedtime: '🛌',
|
||||
Night: '🌙',
|
||||
}
|
||||
|
||||
type TaskGroups = Record<DailyPhase, TaskWithIndex[]>
|
||||
|
||||
export function Routine(
|
||||
props: Props,
|
||||
) {
|
||||
const tasks = useSignal(props.tasks)
|
||||
const currentPhase = useSignal(toPhase())
|
||||
const taskGroups: TaskGroups = {
|
||||
Morning: [],
|
||||
Midday: [],
|
||||
Evening: [],
|
||||
Bedtime: [],
|
||||
Night: [],
|
||||
}
|
||||
tasks.value.forEach((t, i) => taskGroups[t.phase].push({ ...t, index: i }))
|
||||
return (
|
||||
<>
|
||||
<nav class='flex overflow-x-scroll'>
|
||||
{DailyPhaseModel.options.map((phase) => (
|
||||
<button
|
||||
onClick={() => {
|
||||
currentPhase.value = phase
|
||||
console.log(currentPhase, phase)
|
||||
}}
|
||||
class={`p-4 shrink-0 grow border-b-2 text-lg ${
|
||||
currentPhase.value == phase
|
||||
? 'border-purple-500'
|
||||
: 'border-gray-500/20 hover:border-gray-500'
|
||||
}`}
|
||||
>
|
||||
{phaseEmoji[phase]} {phase}
|
||||
</button>
|
||||
))}
|
||||
</nav>
|
||||
<main class='flex flex-col overflow-x-scroll'>
|
||||
<ul>
|
||||
{taskGroups[currentPhase.value].map((
|
||||
{ emoji, text, doneAt, index }: TaskWithIndex,
|
||||
) => (
|
||||
<li
|
||||
role='button'
|
||||
class={`text-lg cursor-pointer p-4 hover:bg-gray-500/20 ${
|
||||
doneAt ? 'line-through opacity-30 hover:bg-gray-500' : ''
|
||||
}`}
|
||||
onClick={() => {
|
||||
tasks.value[index].doneAt = tasks.value[index].doneAt
|
||||
? null
|
||||
: new Date()
|
||||
tasks.value = [...tasks.value]
|
||||
if (tasks.value[index].doneAt) excitement()
|
||||
}}
|
||||
>
|
||||
{emoji ? `${emoji.trim()} ` : ''}
|
||||
{text.trim()}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</main>
|
||||
</>
|
||||
)
|
||||
}
|
82
models.ts
82
models.ts
|
@ -1,21 +1,4 @@
|
|||
import { z } from 'https://deno.land/x/zod@v3.21.4/mod.ts'
|
||||
import {
|
||||
createPentagon,
|
||||
TableDefinition,
|
||||
} from 'https://deno.land/x/pentagon@v0.1.5/mod.ts'
|
||||
// import { ulid } from 'https://deno.land/x/ulid@v0.3.0/mod.ts'
|
||||
|
||||
export const kv = await Deno.openKv('homeman.db')
|
||||
|
||||
// const todos = kv.list({ prefix: ['todos'] })
|
||||
// const deleteme = []
|
||||
// for await (const u of todos) {
|
||||
// deleteme.push(u.key)
|
||||
// console.log(u)
|
||||
// }
|
||||
// for (const d of deleteme) {
|
||||
// await kv.delete(d)
|
||||
// }
|
||||
|
||||
const User = z.object({
|
||||
id: z.string().ulid().describe('primary'),
|
||||
|
@ -44,37 +27,40 @@ const Todo = z.object({
|
|||
export const TodoModel = Todo
|
||||
export type Todo = z.infer<typeof Todo>
|
||||
|
||||
export const schema: Record<string, TableDefinition> = {
|
||||
users: {
|
||||
schema: User,
|
||||
relations: {
|
||||
assignedTodos: ['todos', [Todo], 'id', 'assigneeUserId'],
|
||||
},
|
||||
},
|
||||
todos: {
|
||||
schema: Todo,
|
||||
relations: {
|
||||
assignee: ['users', User, 'assigneeUserId', 'id'],
|
||||
},
|
||||
},
|
||||
const DailyPhase = z.enum([
|
||||
'Morning',
|
||||
'Midday',
|
||||
'Evening',
|
||||
'Bedtime',
|
||||
'Night',
|
||||
])
|
||||
export const DailyPhaseModel = DailyPhase
|
||||
export type DailyPhase = z.infer<typeof DailyPhase>
|
||||
|
||||
export function toPhase(dt?: Date | null): z.infer<typeof DailyPhase> {
|
||||
const d = dt || new Date()
|
||||
const h = d.getHours()
|
||||
|
||||
if (h >= 6 && h < 12) {
|
||||
return 'Morning'
|
||||
} else if (h >= 12 && h < 17) {
|
||||
return 'Midday'
|
||||
} else if (h >= 17 && h < 19) {
|
||||
return 'Evening'
|
||||
} else if (h >= 19 && h < 23) {
|
||||
return 'Bedtime'
|
||||
}
|
||||
|
||||
export const db = createPentagon(kv, schema)
|
||||
// if (h >= 21 || h < 6) {
|
||||
return 'Night'
|
||||
// } else
|
||||
}
|
||||
|
||||
// const daddy: User = {
|
||||
// id: ulid(),
|
||||
// createdAt: new Date(),
|
||||
// name: 'Daddy',
|
||||
// avatarUrl: null,
|
||||
// }
|
||||
// const todo: Todo = {
|
||||
// emoji: null,
|
||||
// id: ulid(),
|
||||
// createdAt: new Date(),
|
||||
// description: 'Test Todo',
|
||||
// doneAt: null,
|
||||
// assigneeUserId: daddy.id,
|
||||
// }
|
||||
|
||||
// db.todos.create({ data: todo })
|
||||
// db.users.create({ data: daddy })
|
||||
const Task = z.object({
|
||||
emoji: z.string().nullable(),
|
||||
text: z.string(),
|
||||
doneAt: z.date().nullable(),
|
||||
phase: DailyPhase,
|
||||
})
|
||||
export const TaskModel = Task
|
||||
export type Task = z.infer<typeof Task>
|
||||
|
|
|
@ -6,7 +6,7 @@ export default function Error404() {
|
|||
<Head>
|
||||
<title>404 - Page not found</title>
|
||||
</Head>
|
||||
<div class='px-4 py-8 mx-auto bg-[#86efac]'>
|
||||
<div class='px-4 py-8 mx-auto bg-rose-500/20'>
|
||||
<div class='max-w-screen-md mx-auto flex flex-col items-center justify-center'>
|
||||
<img
|
||||
class='my-6'
|
||||
|
|
|
@ -10,7 +10,7 @@ export default function App({ Component }: PageProps) {
|
|||
<title>homeman-deno</title>
|
||||
<link rel='stylesheet' href='/styles.css' />
|
||||
</head>
|
||||
<body class='bg-stone-100 text-stone-950 dark:bg-stone-950 dark:text-stone-100 min-h-lvh'>
|
||||
<body class='bg-stone-100 text-stone-950 dark:bg-stone-950 dark:text-stone-100 min-h-lvh relative'>
|
||||
<Nav />
|
||||
<Component />
|
||||
</body>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { Handlers, PageProps } from '$fresh/server.ts'
|
||||
import { db } from '@homeman/models.ts'
|
||||
import { db } from '@homeman/db.ts'
|
||||
import { Admin, Props } from '@homeman/islands/Admin.tsx'
|
||||
|
||||
export const handler: Handlers = {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { Handlers } from '$fresh/server.ts'
|
||||
import { db, kv, Todo, TodoModel } from '@homeman/models.ts'
|
||||
import { Todo, TodoModel } 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'
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { Handlers } from '$fresh/server.ts'
|
||||
import { db, kv, TodoModel } from '@homeman/models.ts'
|
||||
import { TodoModel } from '@homeman/models.ts'
|
||||
import { db, kv } from '@homeman/db.ts'
|
||||
|
||||
const Model = TodoModel.pick({ id: true })
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { Handlers } from '$fresh/server.ts'
|
||||
import { db, kv, User, UserModel } from '@homeman/models.ts'
|
||||
import { User, UserModel } 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'
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { Handlers, PageProps } from '$fresh/server.ts'
|
||||
import { db, kv, Todo, UserWithTodos } from '@homeman/models.ts'
|
||||
import { Todo, UserWithTodos } from '@homeman/models.ts'
|
||||
import { db, kv } from '@homeman/db.ts'
|
||||
import Dashboard from '@homeman/islands/Dashboard.tsx'
|
||||
|
||||
interface Data {
|
||||
|
|
79
routes/routine.tsx
Normal file
79
routes/routine.tsx
Normal file
|
@ -0,0 +1,79 @@
|
|||
import { Handlers, PageProps } from '$fresh/server.ts'
|
||||
import { Task } from '@homeman/models.ts'
|
||||
import { Routine } from '@homeman/islands/Routine.tsx'
|
||||
// import { db, kv, Todo, UserWithTodos } from '@homeman/models.ts'
|
||||
|
||||
interface Data {
|
||||
tasks: Task[]
|
||||
}
|
||||
|
||||
export const handler: Handlers = {
|
||||
GET(_req, ctx) {
|
||||
const tasks: Task[] = Array.from([])
|
||||
console.log(tasks)
|
||||
|
||||
Array.prototype.forEach.apply([
|
||||
['🥣', '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'],
|
||||
], [([emoji, text]) => {
|
||||
tasks.push({
|
||||
emoji,
|
||||
text,
|
||||
doneAt: null,
|
||||
phase: 'Morning',
|
||||
})
|
||||
}])
|
||||
|
||||
return ctx.render({ tasks })
|
||||
},
|
||||
}
|
||||
|
||||
export default function Page(props: PageProps<Data>) {
|
||||
console.log(props)
|
||||
return <Routine tasks={props.data.tasks} />
|
||||
}
|
||||
|
||||
/*
|
||||
🌄 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
|
||||
|
||||
🌤️ AFTERNOON
|
||||
🥓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
|
||||
|
||||
*/
|
Loading…
Reference in a new issue