Initial commit — Grow baby tracker

Next.js 16 App Router, Prisma + PostgreSQL, NextAuth v5 JWT.

Features: dashboard quick-log, timeline, growth charts (WHO percentiles),
stats, journal/notes, doctor notes, milestones, vaccinations, settings,
superadmin panel. Mobile-first with sidebar nav + bottom nav + quick-add FAB.
Dark mode, PWA push notifications, multi-family invite system.

Docker: multi-stage Dockerfile + docker-compose with postgres service.
This commit is contained in:
2026-06-13 01:52:46 +02:00
commit cd16c354c0
94 changed files with 14354 additions and 0 deletions
@@ -0,0 +1,141 @@
-- CreateEnum
CREATE TYPE "EventType" AS ENUM ('BREASTFEED', 'BOTTLE', 'DIAPER_WET', 'DIAPER_STOOL', 'NAP', 'NIGHT_SLEEP', 'MEDICATION', 'TEMPERATURE');
-- CreateTable
CREATE TABLE "User" (
"id" TEXT NOT NULL,
"email" TEXT NOT NULL,
"name" TEXT NOT NULL,
"password" TEXT,
"image" TEXT,
"familyId" TEXT,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "User_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "Account" (
"id" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"type" TEXT NOT NULL,
"provider" TEXT NOT NULL,
"providerAccountId" TEXT NOT NULL,
"refresh_token" TEXT,
"access_token" TEXT,
"expires_at" INTEGER,
"token_type" TEXT,
"scope" TEXT,
"id_token" TEXT,
"session_state" TEXT,
CONSTRAINT "Account_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "Session" (
"id" TEXT NOT NULL,
"sessionToken" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"expires" TIMESTAMP(3) NOT NULL,
CONSTRAINT "Session_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "VerificationToken" (
"identifier" TEXT NOT NULL,
"token" TEXT NOT NULL,
"expires" TIMESTAMP(3) NOT NULL
);
-- CreateTable
CREATE TABLE "Family" (
"id" TEXT NOT NULL,
"name" TEXT NOT NULL,
"inviteCode" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "Family_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "Baby" (
"id" TEXT NOT NULL,
"name" TEXT NOT NULL,
"birthDate" TIMESTAMP(3) NOT NULL,
"gender" TEXT,
"familyId" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "Baby_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "Event" (
"id" TEXT NOT NULL,
"babyId" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"type" "EventType" NOT NULL,
"startedAt" TIMESTAMP(3) NOT NULL,
"endedAt" TIMESTAMP(3),
"notes" TEXT,
"metadata" JSONB,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "Event_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "GrowthLog" (
"id" TEXT NOT NULL,
"babyId" TEXT NOT NULL,
"date" TIMESTAMP(3) NOT NULL,
"weight" DOUBLE PRECISION,
"height" DOUBLE PRECISION,
"headCirc" DOUBLE PRECISION,
"notes" TEXT,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "GrowthLog_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE UNIQUE INDEX "User_email_key" ON "User"("email");
-- CreateIndex
CREATE UNIQUE INDEX "Account_provider_providerAccountId_key" ON "Account"("provider", "providerAccountId");
-- CreateIndex
CREATE UNIQUE INDEX "Session_sessionToken_key" ON "Session"("sessionToken");
-- CreateIndex
CREATE UNIQUE INDEX "VerificationToken_token_key" ON "VerificationToken"("token");
-- CreateIndex
CREATE UNIQUE INDEX "VerificationToken_identifier_token_key" ON "VerificationToken"("identifier", "token");
-- CreateIndex
CREATE UNIQUE INDEX "Family_inviteCode_key" ON "Family"("inviteCode");
-- AddForeignKey
ALTER TABLE "User" ADD CONSTRAINT "User_familyId_fkey" FOREIGN KEY ("familyId") REFERENCES "Family"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Account" ADD CONSTRAINT "Account_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Session" ADD CONSTRAINT "Session_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Baby" ADD CONSTRAINT "Baby_familyId_fkey" FOREIGN KEY ("familyId") REFERENCES "Family"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Event" ADD CONSTRAINT "Event_babyId_fkey" FOREIGN KEY ("babyId") REFERENCES "Baby"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Event" ADD CONSTRAINT "Event_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "GrowthLog" ADD CONSTRAINT "GrowthLog_babyId_fkey" FOREIGN KEY ("babyId") REFERENCES "Baby"("id") ON DELETE CASCADE ON UPDATE CASCADE;
@@ -0,0 +1,52 @@
-- CreateEnum
CREATE TYPE "UserRole" AS ENUM ('PARENT', 'READONLY', 'DOCTOR');
-- AlterTable
ALTER TABLE "User" ADD COLUMN "isSuperAdmin" BOOLEAN NOT NULL DEFAULT false,
ADD COLUMN "pushoverUserKey" TEXT,
ADD COLUMN "role" "UserRole" NOT NULL DEFAULT 'PARENT';
-- CreateTable
CREATE TABLE "PasswordResetToken" (
"id" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"token" TEXT NOT NULL,
"expiresAt" TIMESTAMP(3) NOT NULL,
"used" BOOLEAN NOT NULL DEFAULT false,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "PasswordResetToken_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "SystemConfig" (
"key" TEXT NOT NULL,
"value" TEXT NOT NULL,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "SystemConfig_pkey" PRIMARY KEY ("key")
);
-- CreateTable
CREATE TABLE "DoctorNote" (
"id" TEXT NOT NULL,
"babyId" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"content" TEXT NOT NULL,
"date" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "DoctorNote_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE UNIQUE INDEX "PasswordResetToken_token_key" ON "PasswordResetToken"("token");
-- AddForeignKey
ALTER TABLE "PasswordResetToken" ADD CONSTRAINT "PasswordResetToken_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "DoctorNote" ADD CONSTRAINT "DoctorNote_babyId_fkey" FOREIGN KEY ("babyId") REFERENCES "Baby"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "DoctorNote" ADD CONSTRAINT "DoctorNote_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
+3
View File
@@ -0,0 +1,3 @@
# Please do not edit this file manually
# It should be added in your version-control system (e.g., Git)
provider = "postgresql"
+194
View File
@@ -0,0 +1,194 @@
generator client {
provider = "prisma-client"
output = "../src/generated/prisma"
}
datasource db {
provider = "postgresql"
}
enum UserRole {
PARENT
READONLY
DOCTOR
}
model User {
id String @id @default(cuid())
email String @unique
name String
password String?
image String?
role UserRole @default(PARENT)
isSuperAdmin Boolean @default(false)
pushoverUserKey String?
familyId String?
family Family? @relation(fields: [familyId], references: [id])
events Event[]
sessions Session[]
accounts Account[]
doctorNotes DoctorNote[]
journalNotes JournalNote[]
passwordResetTokens PasswordResetToken[]
forcedLogoutAt DateTime?
createdAt DateTime @default(now())
}
model PasswordResetToken {
id String @id @default(cuid())
userId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
token String @unique @default(cuid())
expiresAt DateTime
used Boolean @default(false)
createdAt DateTime @default(now())
}
model SystemConfig {
key String @id
value String
updatedAt DateTime @updatedAt
}
model Account {
id String @id @default(cuid())
userId String
type String
provider String
providerAccountId String
refresh_token String?
access_token String?
expires_at Int?
token_type String?
scope String?
id_token String?
session_state String?
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@unique([provider, providerAccountId])
}
model Session {
id String @id @default(cuid())
sessionToken String @unique
userId String
expires DateTime
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
}
model VerificationToken {
identifier String
token String @unique
expires DateTime
@@unique([identifier, token])
}
model Family {
id String @id @default(cuid())
name String
inviteCode String @unique @default(cuid())
users User[]
babies Baby[]
createdAt DateTime @default(now())
}
model Baby {
id String @id @default(cuid())
name String
birthDate DateTime
gender String?
familyId String
family Family @relation(fields: [familyId], references: [id])
events Event[]
growthLogs GrowthLog[]
doctorNotes DoctorNote[]
journalNotes JournalNote[]
milestones Milestone[]
vaccinations Vaccination[]
createdAt DateTime @default(now())
}
model Event {
id String @id @default(cuid())
babyId String
baby Baby @relation(fields: [babyId], references: [id], onDelete: Cascade)
userId String
user User @relation(fields: [userId], references: [id])
type EventType
startedAt DateTime
endedAt DateTime?
notes String?
metadata Json?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
enum EventType {
BREASTFEED
BOTTLE
DIAPER_WET
DIAPER_STOOL
NAP
NIGHT_SLEEP
MEDICATION
TEMPERATURE
}
model GrowthLog {
id String @id @default(cuid())
babyId String
baby Baby @relation(fields: [babyId], references: [id], onDelete: Cascade)
date DateTime
weight Float?
height Float?
headCirc Float?
notes String?
createdAt DateTime @default(now())
}
model DoctorNote {
id String @id @default(cuid())
babyId String
baby Baby @relation(fields: [babyId], references: [id], onDelete: Cascade)
userId String
user User @relation(fields: [userId], references: [id])
content String
date DateTime @default(now())
createdAt DateTime @default(now())
}
model JournalNote {
id String @id @default(cuid())
babyId String
baby Baby @relation(fields: [babyId], references: [id], onDelete: Cascade)
userId String
user User @relation(fields: [userId], references: [id])
date DateTime
content String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model Milestone {
id String @id @default(cuid())
babyId String
baby Baby @relation(fields: [babyId], references: [id], onDelete: Cascade)
title String
date DateTime
notes String?
icon String?
createdAt DateTime @default(now())
}
model Vaccination {
id String @id @default(cuid())
babyId String
baby Baby @relation(fields: [babyId], references: [id], onDelete: Cascade)
name String
date DateTime?
dueDate DateTime?
notes String?
done Boolean @default(false)
createdAt DateTime @default(now())
}