Document Prisma ERD generation

This commit is contained in:
Janpeter Visser 2026-04-25 17:08:55 +02:00
parent b5e967d8d3
commit 4ec0683f88
9 changed files with 3997 additions and 25 deletions

View file

@ -116,17 +116,40 @@ Vul daarna `DATABASE_URL` en `SESSION_SECRET` in. `DIRECT_URL` is optioneel loka
npx prisma db push npx prisma db push
``` ```
4. Seed testdata indien nodig: 4. Genereer Prisma Client en de ERD:
```bash
npm run db:erd
```
Deze command voert `prisma generate` uit. Daardoor worden zowel de Prisma Client als `docs/erd.svg` opnieuw opgebouwd.
5. Seed testdata indien nodig:
```bash ```bash
npx prisma db seed npx prisma db seed
``` ```
5. Start de app: 6. Start de app:
```bash ```bash
npm run dev npm run dev
``` ```
## Database
![ERD](./docs/erd.svg)
De databasevisualisatie wordt gegenereerd uit `prisma/schema.prisma` via `prisma-erd-generator`.
Handmatige generatie:
```bash
npm run db:erd
```
Tijdens lokale development draait `npm run dev` naast Next.js ook `npm run db:erd:watch`. Bij wijzigingen in `prisma/schema.prisma` wordt `docs/erd.svg` automatisch opnieuw gegenereerd.
Gebruik `npx prisma db push` alleen om het schema naar de database te synchroniseren. Gebruik `npm run db:erd` om Prisma Client en de ERD te genereren.
De app draait standaard op `http://localhost:3000`. De app draait standaard op `http://localhost:3000`.
@ -137,6 +160,7 @@ npm run dev # lokale development server
npm run lint # ESLint npm run lint # ESLint
npm test # Vitest test suite npm test # Vitest test suite
npm run build # productiebuild zoals Vercel die verwacht npm run build # productiebuild zoals Vercel die verwacht
npm run db:erd # Prisma Client + docs/erd.svg genereren
``` ```
### Environment variables ### Environment variables

View file

@ -1,7 +1,26 @@
import Link from 'next/link' import Link from 'next/link'
import { cookies } from 'next/headers'
import { getIronSession } from 'iron-session'
import { AppIcon } from '@/components/shared/app-icon' import { AppIcon } from '@/components/shared/app-icon'
import { SessionData, sessionOptions } from '@/lib/session'
import packageJson from '@/package.json'
const buildDate = process.env.NEXT_PUBLIC_BUILD_DATE
? new Date(process.env.NEXT_PUBLIC_BUILD_DATE).toLocaleDateString('nl-NL', {
day: 'numeric',
month: 'short',
year: 'numeric',
})
: '—'
const version = process.env.NEXT_PUBLIC_APP_VERSION ?? packageJson.version
export default async function LandingPage() {
const session = process.env.SESSION_SECRET
? await getIronSession<SessionData>(await cookies(), sessionOptions)
: null
const isLoggedIn = Boolean(session?.userId)
export default function LandingPage() {
return ( return (
<div className="h-screen bg-background text-foreground flex flex-col overflow-hidden"> <div className="h-screen bg-background text-foreground flex flex-col overflow-hidden">
@ -12,6 +31,29 @@ export default function LandingPage() {
Scrum4Me Scrum4Me
</Link> </Link>
<nav className="ml-auto flex items-center gap-2"> <nav className="ml-auto flex items-center gap-2">
{isLoggedIn ? (
<>
<Link
href="/dashboard"
className="px-3 py-1.5 rounded-md text-sm text-muted-foreground hover:text-foreground hover:bg-surface-container transition-colors"
>
Producten
</Link>
<Link
href="/todos"
className="px-3 py-1.5 rounded-md text-sm text-muted-foreground hover:text-foreground hover:bg-surface-container transition-colors"
>
Todo&apos;s
</Link>
<Link
href="/settings"
className="px-3 py-1.5 rounded-md text-sm text-muted-foreground hover:text-foreground hover:bg-surface-container transition-colors"
>
Instellingen
</Link>
</>
) : (
<>
<Link <Link
href="/login" href="/login"
className="px-3 py-1.5 rounded-md text-sm text-muted-foreground hover:text-foreground hover:bg-surface-container transition-colors" className="px-3 py-1.5 rounded-md text-sm text-muted-foreground hover:text-foreground hover:bg-surface-container transition-colors"
@ -24,6 +66,8 @@ export default function LandingPage() {
> >
Registreren Registreren
</Link> </Link>
</>
)}
</nav> </nav>
</header> </header>
@ -279,10 +323,7 @@ export default function LandingPage() {
{/* ── Footer ─────────────────────────────────────────────────── */} {/* ── Footer ─────────────────────────────────────────────────── */}
<footer className="shrink-0 border-t border-border bg-surface-container-low px-6 py-4 flex items-center justify-between text-xs text-muted-foreground"> <footer className="shrink-0 border-t border-border bg-surface-container-low px-6 py-4 flex items-center justify-between text-xs text-muted-foreground">
<span>© {new Date().getFullYear()} Scrum4Me</span> <span>© {new Date().getFullYear()} Scrum4Me</span>
<div className="flex gap-4"> <span>v{version} · gebouwd op {buildDate}</span>
<Link href="/login" className="hover:text-foreground transition-colors">Inloggen</Link>
<Link href="/register" className="hover:text-foreground transition-colors">Registreren</Link>
</div>
</footer> </footer>
</div> </div>

View file

@ -23,7 +23,7 @@ export function NavBar({ isDemo }: NavBarProps) {
return ( return (
<header className="bg-surface-container-low border-b border-border h-14 flex items-center px-4 gap-4 shrink-0"> <header className="bg-surface-container-low border-b border-border h-14 flex items-center px-4 gap-4 shrink-0">
{/* Logo */} {/* Logo */}
<Link href="/dashboard" className="flex items-center gap-2 font-medium text-foreground"> <Link href="/" className="flex items-center gap-2 font-medium text-foreground">
<AppIcon size={24} /> <AppIcon size={24} />
<span className="text-primary font-semibold">Scrum4Me</span> <span className="text-primary font-semibold">Scrum4Me</span>
{isDemo && ( {isDemo && (

1
docs/erd.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 317 KiB

View file

@ -39,3 +39,35 @@ export default defineConfig({
migrations: { path: 'prisma/migrations' }, migrations: { path: 'prisma/migrations' },
}) })
``` ```
## Prisma generators
`prisma/schema.prisma` bevat twee generators:
```prisma
generator client {
provider = "prisma-client-js"
}
generator erd {
provider = "prisma-erd-generator"
output = "../docs/erd.svg"
}
```
`prisma generate` bouwt dus twee artifacts:
- Prisma Client in `node_modules/@prisma/client`
- het ERD-diagram in `docs/erd.svg`
## Commands
| Command | Gebruik |
|---|---|
| `npx prisma db push` | Schema synchroniseren naar de database |
| `npx prisma db seed` | Seeddata laden |
| `npm run db:erd` | `prisma generate`: Prisma Client en `docs/erd.svg` genereren |
| `npm run db:erd:watch` | `prisma/schema.prisma` watchen en ERD opnieuw genereren |
| `npm run dev` | Next.js dev server plus ERD watcher starten |
Belangrijk: `db push` schrijft naar de database, maar genereert geen ERD. Gebruik na schemawijzigingen `npm run db:erd` of laat `npm run dev` de watcher draaien.

View file

@ -770,6 +770,7 @@ NODE_ENV="development"
**CI/CD:** GitHub Actions → lint + typecheck + `prisma validate` op elke PR; Vercel deploy automatisch bij merge naar `main` **CI/CD:** GitHub Actions → lint + typecheck + `prisma validate` op elke PR; Vercel deploy automatisch bij merge naar `main`
**Database (cloud):** Neon — migraties via `prisma migrate deploy` in de Vercel build-stap **Database (cloud):** Neon — migraties via `prisma migrate deploy` in de Vercel build-stap
**Database (lokaal):** Neon (gratis tier) — `npx prisma db push` synchroniseert schema **Database (lokaal):** Neon (gratis tier) — `npx prisma db push` synchroniseert schema
**Prisma generatie:** `npm run db:erd` voert `prisma generate` uit en bouwt zowel Prisma Client als `docs/erd.svg`
**Seeding:** `npx prisma db seed` laadt de testdata uit het Product Backlog document **Seeding:** `npx prisma db seed` laadt de testdata uit het Product Backlog document
### Deployment checklist (pre-launch) ### Deployment checklist (pre-launch)

3866
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,16 +1,18 @@
{ {
"name": "scrum4me", "name": "scrum4me",
"version": "0.2.0", "version": "0.3.0",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "next dev", "dev": "concurrently \"next dev\" \"npm run db:erd:watch\"",
"build": "next build", "build": "next build",
"start": "next start", "start": "next start",
"lint": "eslint", "lint": "eslint",
"test": "vitest run", "test": "vitest run",
"test:watch": "vitest", "test:watch": "vitest",
"prepare": "husky", "prepare": "husky",
"postinstall": "prisma generate" "postinstall": "prisma generate",
"db:erd": "prisma generate",
"db:erd:watch": "chokidar \"prisma/schema.prisma\" -c \"npm run db:erd\""
}, },
"dependencies": { "dependencies": {
"@base-ui/react": "^1.4.1", "@base-ui/react": "^1.4.1",
@ -47,6 +49,7 @@
"*.{ts,tsx,mjs}": "eslint --fix" "*.{ts,tsx,mjs}": "eslint --fix"
}, },
"devDependencies": { "devDependencies": {
"@mermaid-js/mermaid-cli": "^11.12.0",
"@tailwindcss/postcss": "^4", "@tailwindcss/postcss": "^4",
"@types/bcryptjs": "^2.4.6", "@types/bcryptjs": "^2.4.6",
"@types/node": "^20", "@types/node": "^20",
@ -54,10 +57,13 @@
"@types/react": "^19", "@types/react": "^19",
"@types/react-dom": "^19", "@types/react-dom": "^19",
"@vitest/coverage-v8": "^4.1.5", "@vitest/coverage-v8": "^4.1.5",
"chokidar-cli": "^3.0.0",
"concurrently": "^9.2.1",
"eslint": "^9", "eslint": "^9",
"eslint-config-next": "16.2.4", "eslint-config-next": "16.2.4",
"husky": "^9.1.7", "husky": "^9.1.7",
"lint-staged": "^16.4.0", "lint-staged": "^16.4.0",
"prisma-erd-generator": "^2.4.2",
"tailwindcss": "^4", "tailwindcss": "^4",
"tsx": "^4.21.0", "tsx": "^4.21.0",
"typescript": "^5", "typescript": "^5",

View file

@ -2,6 +2,11 @@ generator client {
provider = "prisma-client-js" provider = "prisma-client-js"
} }
generator erd {
provider = "prisma-erd-generator"
output = "../docs/erd.svg"
}
datasource db { datasource db {
provider = "postgresql" provider = "postgresql"
} }