Initial commit: Next Step health management app

A calm, reliable app to help manage appointments, medications, and notes
for chemo patients and their families.

Features:
- Today dashboard with next appointment and medications due
- Medication tracking with multiple schedule types (fixed times, interval, weekdays, PRN)
- One-tap dose logging with 5-minute undo window
- Questions for doctor tracking
- Family sharing with workspace model and invite links
- Offline-first with IndexedDB and sync
- Docker Compose deployment with Tailscale Funnel support

Tech stack:
- Next.js 14 (App Router) + TypeScript + Tailwind CSS
- PostgreSQL + Prisma
- Argon2 password hashing + session cookies
- Dexie.js for IndexedDB

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Gemini Agent
2026-01-18 23:16:45 +00:00
commit a32c609830
76 changed files with 9406 additions and 0 deletions

View File

@@ -0,0 +1,86 @@
'use client'
import { createContext, useContext, useEffect, useState, useCallback } from 'react'
import { startAutoSync, stopAutoSync, sync } from '@/lib/sync'
interface User {
id: string
email: string
name: string
}
interface Workspace {
id: string
name: string
role: string
clinicPhone: string | null
emergencyPhone: string | null
largeTextMode: boolean
}
interface AppContextType {
user: User
workspaces: Workspace[]
currentWorkspace: Workspace
setCurrentWorkspaceId: (id: string) => void
refreshData: () => Promise<void>
}
const AppContext = createContext<AppContextType | null>(null)
export function useApp() {
const context = useContext(AppContext)
if (!context) {
throw new Error('useApp must be used within AppProvider')
}
return context
}
interface AppProviderProps {
children: React.ReactNode
user: User
workspaces: Workspace[]
initialWorkspaceId: string
}
export function AppProvider({
children,
user,
workspaces,
initialWorkspaceId,
}: AppProviderProps) {
const [currentWorkspaceId, setCurrentWorkspaceId] = useState(initialWorkspaceId)
const currentWorkspace = workspaces.find((w) => w.id === currentWorkspaceId) || workspaces[0]
// Start auto-sync when workspace changes
useEffect(() => {
if (currentWorkspaceId) {
startAutoSync(currentWorkspaceId)
}
return () => {
stopAutoSync()
}
}, [currentWorkspaceId])
const refreshData = useCallback(async () => {
if (currentWorkspaceId) {
await sync(currentWorkspaceId)
}
}, [currentWorkspaceId])
return (
<AppContext.Provider
value={{
user,
workspaces,
currentWorkspace,
setCurrentWorkspaceId,
refreshData,
}}
>
{children}
</AppContext.Provider>
)
}