From 11df79f988f030ff7daa77c17b73e03e06da750c Mon Sep 17 00:00:00 2001 From: b-at-neu Date: Tue, 7 Apr 2026 18:44:56 -0400 Subject: [PATCH 1/9] AUTH-21 add GitHub Actions CI pipeline Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/ci.yml | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..510fe6b --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,35 @@ +name: CI + +on: + push: + branches: + - main + - dev + pull_request: + branches: + - main + - dev + +jobs: + ci: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + + - name: Install dependencies + run: npm ci + + - name: Type check + run: npx tsc --noEmit + + - name: Lint + run: npm run lint + + - name: Format check + run: npm run format:check From 7845bc4fdb6f61955f589393057d186ca34dbdfa Mon Sep 17 00:00:00 2001 From: b-at-neu Date: Tue, 7 Apr 2026 18:47:19 -0400 Subject: [PATCH 2/9] AUTH-21 split CI into individual typecheck, lint, and format jobs Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/ci.yml | 36 +++++++++++++++++++++++------------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 510fe6b..ea9dd06 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,25 +11,35 @@ on: - dev jobs: - ci: + typecheck: runs-on: ubuntu-latest - steps: - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 with: node-version: 20 cache: npm + - run: npm ci + - run: npx tsc --noEmit - - name: Install dependencies - run: npm ci - - - name: Type check - run: npx tsc --noEmit - - - name: Lint - run: npm run lint + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + - run: npm ci + - run: npm run lint - - name: Format check - run: npm run format:check + format: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + - run: npm ci + - run: npm run format:check From 818f1a5f6089ed747c65b8f584980096edfff000 Mon Sep 17 00:00:00 2001 From: b-at-neu Date: Tue, 7 Apr 2026 18:49:08 -0400 Subject: [PATCH 3/9] AUTH-21 bump node to 22, sync package-lock Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/ci.yml | 6 +++--- package-lock.json | 41 ++++++++++++++++++++++++++-------------- 2 files changed, 30 insertions(+), 17 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ea9dd06..99d7423 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,7 +17,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: - node-version: 20 + node-version: 22 cache: npm - run: npm ci - run: npx tsc --noEmit @@ -28,7 +28,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: - node-version: 20 + node-version: 22 cache: npm - run: npm ci - run: npm run lint @@ -39,7 +39,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: - node-version: 20 + node-version: 22 cache: npm - run: npm ci - run: npm run format:check diff --git a/package-lock.json b/package-lock.json index 6211688..a0ac58d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2300,7 +2300,7 @@ "version": "19.2.3", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", - "dev": true, + "devOptional": true, "license": "MIT", "peerDependencies": { "@types/react": "^19.2.0" @@ -2902,6 +2902,19 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/acorn-walk": { + "version": "8.3.5", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.5.tgz", + "integrity": "sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/agent-base": { "version": "7.1.4", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", @@ -3211,6 +3224,19 @@ "node": ">=6.0.0" } }, + "node_modules/better-result": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/better-result/-/better-result-2.7.0.tgz", + "integrity": "sha512-7zrmXjAK8u8Z6SOe4R65XObOR5X+Y2I/VVku3t5cPOGQ8/WsBcfFmfnIPiEl5EBMDOzPHRwbiPbMtQBKYdw7RA==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@clack/prompts": "^0.11.0" + }, + "bin": { + "better-result": "bin/cli.mjs" + } + }, "node_modules/bin-links": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/bin-links/-/bin-links-6.0.0.tgz", @@ -3228,19 +3254,6 @@ "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/better-result": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/better-result/-/better-result-2.7.0.tgz", - "integrity": "sha512-7zrmXjAK8u8Z6SOe4R65XObOR5X+Y2I/VVku3t5cPOGQ8/WsBcfFmfnIPiEl5EBMDOzPHRwbiPbMtQBKYdw7RA==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "@clack/prompts": "^0.11.0" - }, - "bin": { - "better-result": "bin/cli.mjs" - } - }, "node_modules/brace-expansion": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", From 3b4984d0e2e30611ca00701d8937b35ad070353a Mon Sep 17 00:00:00 2001 From: b-at-neu Date: Tue, 7 Apr 2026 18:49:46 -0400 Subject: [PATCH 4/9] AUTH-21 skip postinstall scripts in CI to avoid prisma generate Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 99d7423..7bfeb58 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,7 +19,7 @@ jobs: with: node-version: 22 cache: npm - - run: npm ci + - run: npm ci --ignore-scripts - run: npx tsc --noEmit lint: @@ -30,7 +30,7 @@ jobs: with: node-version: 22 cache: npm - - run: npm ci + - run: npm ci --ignore-scripts - run: npm run lint format: @@ -41,5 +41,5 @@ jobs: with: node-version: 22 cache: npm - - run: npm ci + - run: npm ci --ignore-scripts - run: npm run format:check From 6c97a7dc45d98139e6e49ecefb50043aec5d702c Mon Sep 17 00:00:00 2001 From: b-at-neu Date: Tue, 7 Apr 2026 18:51:10 -0400 Subject: [PATCH 5/9] AUTH-21 run prisma generate before typecheck with dummy env Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/ci.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7bfeb58..0db520d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,6 +20,9 @@ jobs: node-version: 22 cache: npm - run: npm ci --ignore-scripts + - run: npx prisma generate + env: + DIRECT_URL: "postgresql://placeholder" - run: npx tsc --noEmit lint: From df0bfaa251f565a93176c96c812c387d14c82e31 Mon Sep 17 00:00:00 2001 From: b-at-neu Date: Tue, 7 Apr 2026 18:51:26 -0400 Subject: [PATCH 6/9] AUTH-21 remove unnecessary any cast in seed.ts Co-Authored-By: Claude Sonnet 4.6 --- prisma/seed.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prisma/seed.ts b/prisma/seed.ts index 39a4fa0..13e91fc 100644 --- a/prisma/seed.ts +++ b/prisma/seed.ts @@ -6,7 +6,7 @@ import { PrismaClient } from '../src/generated/prisma/index.js' // Connect to whatever database is specified by DATABASE_URL const pool = new pg.Pool({ connectionString: process.env.DATABASE_URL }) -const adapter = new PrismaPg(pool as any) +const adapter = new PrismaPg(pool) const prisma = new PrismaClient({ adapter }) From 9477d98aea1b8026c3622ae0c66de5eda973d7fc Mon Sep 17 00:00:00 2001 From: b-at-neu Date: Tue, 7 Apr 2026 18:52:51 -0400 Subject: [PATCH 7/9] AUTH-21 fix type error and prettier violations in existing files Co-Authored-By: Claude Sonnet 4.6 --- README.md | 28 ++++++++++++++-------------- prisma.config.ts | 2 +- prisma/seed.ts | 43 ++++++++++++++++++++++--------------------- src/lib/prisma.ts | 8 ++++---- 4 files changed, 41 insertions(+), 40 deletions(-) diff --git a/README.md b/README.md index 692609b..ee5c16d 100644 --- a/README.md +++ b/README.md @@ -39,13 +39,13 @@ Internal authentication service cp .env.example .env ``` - | Variable | Purpose | - | --- | --- | - | `DATABASE_URL` | Pooled Postgres connection used by the application (via PgBouncer in prod) | - | `DIRECT_URL` | Direct Postgres connection used by Prisma for migrations | - | `NEXT_PUBLIC_SUPABASE_URL` | Supabase project URL (public) | - | `NEXT_PUBLIC_SUPABASE_ANON_KEY` | Supabase anon key — "Publishable" in CLI output (public) | - | `SUPABASE_SERVICE_ROLE_KEY` | Supabase service role key — "Secret" in CLI output (**never expose to the client**) | + | Variable | Purpose | + | ------------------------------- | ----------------------------------------------------------------------------------- | + | `DATABASE_URL` | Pooled Postgres connection used by the application (via PgBouncer in prod) | + | `DIRECT_URL` | Direct Postgres connection used by Prisma for migrations | + | `NEXT_PUBLIC_SUPABASE_URL` | Supabase project URL (public) | + | `NEXT_PUBLIC_SUPABASE_ANON_KEY` | Supabase anon key — "Publishable" in CLI output (public) | + | `SUPABASE_SERVICE_ROLE_KEY` | Supabase service role key — "Secret" in CLI output (**never expose to the client**) | > Locally there is no connection pooling, so `DATABASE_URL` and `DIRECT_URL` will be the same. @@ -63,11 +63,11 @@ Internal authentication service ## Scripts -| Command | Description | -| --- | --- | -| `npm run dev` | Start Next.js dev server | -| `npm run build` | Production build | -| `npm run start` | Start production server | -| `npm run lint` | Run ESLint | -| `npm run format` | Format code with Prettier | +| Command | Description | +| ---------------------- | -------------------------------- | +| `npm run dev` | Start Next.js dev server | +| `npm run build` | Production build | +| `npm run start` | Start production server | +| `npm run lint` | Run ESLint | +| `npm run format` | Format code with Prettier | | `npm run format:check` | Check formatting without writing | diff --git a/prisma.config.ts b/prisma.config.ts index 56a9206..b7e2c04 100644 --- a/prisma.config.ts +++ b/prisma.config.ts @@ -7,7 +7,7 @@ export default defineConfig({ schema: "prisma/schema.prisma", migrations: { path: "prisma/migrations", - seed: 'npx ts-node prisma/seed.ts', + seed: "npx ts-node prisma/seed.ts", }, datasource: { url: env("DIRECT_URL"), diff --git a/prisma/seed.ts b/prisma/seed.ts index 13e91fc..661dcad 100644 --- a/prisma/seed.ts +++ b/prisma/seed.ts @@ -1,39 +1,40 @@ -import 'dotenv/config' -import pg from 'pg' -import { PrismaPg } from '@prisma/adapter-pg' -import { PrismaClient } from '../src/generated/prisma/index.js' +import "dotenv/config"; +import pg from "pg"; +import { PrismaPg } from "@prisma/adapter-pg"; +import { PrismaClient } from "../src/generated/prisma/index.js"; // Connect to whatever database is specified by DATABASE_URL -const pool = new pg.Pool({ connectionString: process.env.DATABASE_URL }) +const pool = new pg.Pool({ connectionString: process.env.DATABASE_URL }); -const adapter = new PrismaPg(pool) +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const adapter = new PrismaPg(pool as any); -const prisma = new PrismaClient({ adapter }) +const prisma = new PrismaClient({ adapter }); async function main() { // Fake Supabase user IDs - const adminSupabaseId = '00000000-0000-0000-0000-000000000001' - const userSupabaseId = '00000000-0000-0000-0000-000000000002' + const adminSupabaseId = "00000000-0000-0000-0000-000000000001"; + const userSupabaseId = "00000000-0000-0000-0000-000000000002"; const admin = await prisma.user.upsert({ where: { supabaseUserId: adminSupabaseId }, update: {}, create: { supabaseUserId: adminSupabaseId, isAdmin: true }, - }) + }); const user = await prisma.user.upsert({ where: { supabaseUserId: userSupabaseId }, update: {}, create: { supabaseUserId: userSupabaseId, isAdmin: false }, - }) + }); const project1 = await prisma.project.create({ - data: { name: 'Attendance Manager', description: 'Cool project' }, - }) + data: { name: "Attendance Manager", description: "Cool project" }, + }); const project2 = await prisma.project.create({ - data: { name: 'Website Creation', description: 'Less project' }, - }) + data: { name: "Website Creation", description: "Less project" }, + }); await prisma.userProject.createMany({ data: [ @@ -42,22 +43,22 @@ async function main() { { userId: user.id, projectId: project1.id }, ], skipDuplicates: true, - }) + }); await prisma.session.create({ data: { userId: admin.id, projectId: project1.id, - token: 'admin-session-token', + token: "admin-session-token", expiresAt: new Date(Date.now() + 1000 * 60 * 60 * 24), // 24h }, - }) + }); - console.log('data seeded!') + console.log("data seeded!"); } main() .catch(console.error) .finally(async () => { - await prisma.$disconnect() - }) + await prisma.$disconnect(); + }); diff --git a/src/lib/prisma.ts b/src/lib/prisma.ts index 7c4dbc3..a1ac13e 100644 --- a/src/lib/prisma.ts +++ b/src/lib/prisma.ts @@ -3,16 +3,16 @@ import { PrismaClient } from "../generated/prisma/client"; import { PrismaPg } from "@prisma/adapter-pg"; const globalForPrisma = globalThis as unknown as { - prisma?: PrismaClient; + prisma?: PrismaClient; }; function createPrismaClient() { - const adapter = new PrismaPg({ connectionString: process.env.DATABASE_URL }); - return new PrismaClient({ adapter, errorFormat: "minimal" }); + const adapter = new PrismaPg({ connectionString: process.env.DATABASE_URL }); + return new PrismaClient({ adapter, errorFormat: "minimal" }); } export const prisma = globalForPrisma.prisma ?? createPrismaClient(); if (process.env.NODE_ENV !== "production") { - globalForPrisma.prisma = prisma; + globalForPrisma.prisma = prisma; } From af9bc407505d4a071eeba8580d475e62056aa466 Mon Sep 17 00:00:00 2001 From: b-at-neu Date: Tue, 7 Apr 2026 18:54:06 -0400 Subject: [PATCH 8/9] AUTH-21 align @types/pg version and ignore generated prisma files in eslint Co-Authored-By: Claude Sonnet 4.6 --- eslint.config.mjs | 2 + package-lock.json | 127 ++++++++++++++++++++++------------------------ package.json | 2 +- prisma/seed.ts | 3 +- 4 files changed, 66 insertions(+), 68 deletions(-) diff --git a/eslint.config.mjs b/eslint.config.mjs index 863deca..8c31ce7 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -14,6 +14,8 @@ const eslintConfig = defineConfig([ "out/**", "build/**", "next-env.d.ts", + // Generated files: + "src/generated/**", ]), ]); diff --git a/package-lock.json b/package-lock.json index a0ac58d..8c12efb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,7 +23,7 @@ "devDependencies": { "@tailwindcss/postcss": "^4", "@types/node": "^20", - "@types/pg": "^8.20.0", + "@types/pg": "^8.11.11", "@types/react": "^19", "@types/react-dom": "^19", "eslint": "^9", @@ -1362,35 +1362,6 @@ "postgres-array": "3.0.4" } }, - "node_modules/@prisma/adapter-pg/node_modules/@types/pg": { - "version": "8.11.11", - "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.11.11.tgz", - "integrity": "sha512-kGT1qKM8wJQ5qlawUrEkXgvMSXoV213KfMGXcwfDwUIfUHXqXYXOfS1nE1LINRJVVVx5wCm70XnFlMHaIcQAfw==", - "license": "MIT", - "dependencies": { - "@types/node": "*", - "pg-protocol": "*", - "pg-types": "^4.0.1" - } - }, - "node_modules/@prisma/adapter-pg/node_modules/pg-types": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-4.1.0.tgz", - "integrity": "sha512-o2XFanIMy/3+mThw69O8d4n1E5zsLhdO+OPqswezu7Z5ekP4hYDqlDjlmOpYMbzY2Br0ufCwJLdDIXeNVwcWFg==", - "license": "MIT", - "dependencies": { - "pg-int8": "1.0.1", - "pg-numeric": "1.0.2", - "postgres-array": "~3.0.1", - "postgres-bytea": "~3.0.0", - "postgres-date": "~2.1.0", - "postgres-interval": "^3.0.0", - "postgres-range": "^1.1.1" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/@prisma/adapter-pg/node_modules/postgres-array": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-3.0.4.tgz", @@ -1400,36 +1371,6 @@ "node": ">=12" } }, - "node_modules/@prisma/adapter-pg/node_modules/postgres-bytea": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-3.0.0.tgz", - "integrity": "sha512-CNd4jim9RFPkObHSjVHlVrxoVQXz7quwNFpz7RY1okNNme49+sVyiTvTRobiLV548Hx/hb1BG+iE7h9493WzFw==", - "license": "MIT", - "dependencies": { - "obuf": "~1.1.2" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/@prisma/adapter-pg/node_modules/postgres-date": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-2.1.0.tgz", - "integrity": "sha512-K7Juri8gtgXVcDfZttFKVmhglp7epKb1K4pgrkLxehjqkrgPhfG6OO8LHLkfaqkbpjNRnra018XwAr1yQFWGcA==", - "license": "MIT", - "engines": { - "node": ">=12" - } - }, - "node_modules/@prisma/adapter-pg/node_modules/postgres-interval": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-3.0.0.tgz", - "integrity": "sha512-BSNDnbyZCXSxgA+1f5UU2GmwhoI0aU5yMxRGO8CdFEcY2BQF9xm/7MqKnYoM1nJDk8nONNWDk9WeSmePFhQdlw==", - "license": "MIT", - "engines": { - "node": ">=12" - } - }, "node_modules/@prisma/client": { "version": "7.6.0", "resolved": "https://registry.npmjs.org/@prisma/client/-/client-7.6.0.tgz", @@ -2275,15 +2216,71 @@ } }, "node_modules/@types/pg": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.20.0.tgz", - "integrity": "sha512-bEPFOaMAHTEP1EzpvHTbmwR8UsFyHSKsRisLIHVMXnpNefSbGA1bD6CVy+qKjGSqmZqNqBDV2azOBo8TgkcVow==", - "dev": true, + "version": "8.11.11", + "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.11.11.tgz", + "integrity": "sha512-kGT1qKM8wJQ5qlawUrEkXgvMSXoV213KfMGXcwfDwUIfUHXqXYXOfS1nE1LINRJVVVx5wCm70XnFlMHaIcQAfw==", "license": "MIT", "dependencies": { "@types/node": "*", "pg-protocol": "*", - "pg-types": "^2.2.0" + "pg-types": "^4.0.1" + } + }, + "node_modules/@types/pg/node_modules/pg-types": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-4.1.0.tgz", + "integrity": "sha512-o2XFanIMy/3+mThw69O8d4n1E5zsLhdO+OPqswezu7Z5ekP4hYDqlDjlmOpYMbzY2Br0ufCwJLdDIXeNVwcWFg==", + "license": "MIT", + "dependencies": { + "pg-int8": "1.0.1", + "pg-numeric": "1.0.2", + "postgres-array": "~3.0.1", + "postgres-bytea": "~3.0.0", + "postgres-date": "~2.1.0", + "postgres-interval": "^3.0.0", + "postgres-range": "^1.1.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@types/pg/node_modules/postgres-array": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-3.0.4.tgz", + "integrity": "sha512-nAUSGfSDGOaOAEGwqsRY27GPOea7CNipJPOA7lPbdEpx5Kg3qzdP0AaWC5MlhTWV9s4hFX39nomVZ+C4tnGOJQ==", + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/@types/pg/node_modules/postgres-bytea": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-3.0.0.tgz", + "integrity": "sha512-CNd4jim9RFPkObHSjVHlVrxoVQXz7quwNFpz7RY1okNNme49+sVyiTvTRobiLV548Hx/hb1BG+iE7h9493WzFw==", + "license": "MIT", + "dependencies": { + "obuf": "~1.1.2" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@types/pg/node_modules/postgres-date": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-2.1.0.tgz", + "integrity": "sha512-K7Juri8gtgXVcDfZttFKVmhglp7epKb1K4pgrkLxehjqkrgPhfG6OO8LHLkfaqkbpjNRnra018XwAr1yQFWGcA==", + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/@types/pg/node_modules/postgres-interval": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-3.0.0.tgz", + "integrity": "sha512-BSNDnbyZCXSxgA+1f5UU2GmwhoI0aU5yMxRGO8CdFEcY2BQF9xm/7MqKnYoM1nJDk8nONNWDk9WeSmePFhQdlw==", + "license": "MIT", + "engines": { + "node": ">=12" } }, "node_modules/@types/react": { diff --git a/package.json b/package.json index 1a3ee93..d313223 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "devDependencies": { "@tailwindcss/postcss": "^4", "@types/node": "^20", - "@types/pg": "^8.20.0", + "@types/pg": "^8.11.11", "@types/react": "^19", "@types/react-dom": "^19", "eslint": "^9", diff --git a/prisma/seed.ts b/prisma/seed.ts index 661dcad..21a6561 100644 --- a/prisma/seed.ts +++ b/prisma/seed.ts @@ -6,8 +6,7 @@ import { PrismaClient } from "../src/generated/prisma/index.js"; // Connect to whatever database is specified by DATABASE_URL const pool = new pg.Pool({ connectionString: process.env.DATABASE_URL }); -// eslint-disable-next-line @typescript-eslint/no-explicit-any -const adapter = new PrismaPg(pool as any); +const adapter = new PrismaPg(pool); const prisma = new PrismaClient({ adapter }); From 7286f022e193135666023b48a3e7c289798a5e7d Mon Sep 17 00:00:00 2001 From: b-at-neu Date: Tue, 7 Apr 2026 19:01:30 -0400 Subject: [PATCH 9/9] AUTH-21 fix prettier violations in shadcn components Co-Authored-By: Claude Sonnet 4.6 --- src/components/ui/badge.tsx | 18 +++---- src/components/ui/button.tsx | 14 ++--- src/components/ui/card.tsx | 30 +++++------ src/components/ui/dialog.tsx | 51 +++++++++--------- src/components/ui/dropdown-menu.tsx | 82 +++++++++++++++-------------- src/components/ui/form.tsx | 79 +++++++++++++-------------- src/components/ui/input.tsx | 12 ++--- src/components/ui/label.tsx | 12 ++--- src/components/ui/sonner.tsx | 42 +++++++-------- src/lib/utils.ts | 6 +-- 10 files changed, 173 insertions(+), 173 deletions(-) diff --git a/src/components/ui/badge.tsx b/src/components/ui/badge.tsx index b20959d..a8cbbac 100644 --- a/src/components/ui/badge.tsx +++ b/src/components/ui/badge.tsx @@ -1,8 +1,8 @@ -import { mergeProps } from "@base-ui/react/merge-props" -import { useRender } from "@base-ui/react/use-render" -import { cva, type VariantProps } from "class-variance-authority" +import { mergeProps } from "@base-ui/react/merge-props"; +import { useRender } from "@base-ui/react/use-render"; +import { cva, type VariantProps } from "class-variance-authority"; -import { cn } from "@/lib/utils" +import { cn } from "@/lib/utils"; const badgeVariants = cva( "group/badge inline-flex h-5 w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-4xl border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-all focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3!", @@ -24,8 +24,8 @@ const badgeVariants = cva( defaultVariants: { variant: "default", }, - } -) + }, +); function Badge({ className, @@ -39,14 +39,14 @@ function Badge({ { className: cn(badgeVariants({ variant }), className), }, - props + props, ), render, state: { slot: "badge", variant, }, - }) + }); } -export { Badge, badgeVariants } +export { Badge, badgeVariants }; diff --git a/src/components/ui/button.tsx b/src/components/ui/button.tsx index 09df753..cc44df6 100644 --- a/src/components/ui/button.tsx +++ b/src/components/ui/button.tsx @@ -1,7 +1,7 @@ -import { Button as ButtonPrimitive } from "@base-ui/react/button" -import { cva, type VariantProps } from "class-variance-authority" +import { Button as ButtonPrimitive } from "@base-ui/react/button"; +import { cva, type VariantProps } from "class-variance-authority"; -import { cn } from "@/lib/utils" +import { cn } from "@/lib/utils"; const buttonVariants = cva( "group/button inline-flex shrink-0 items-center justify-center rounded-lg border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", @@ -37,8 +37,8 @@ const buttonVariants = cva( variant: "default", size: "default", }, - } -) + }, +); function Button({ className, @@ -52,7 +52,7 @@ function Button({ className={cn(buttonVariants({ variant, size, className }))} {...props} /> - ) + ); } -export { Button, buttonVariants } +export { Button, buttonVariants }; diff --git a/src/components/ui/card.tsx b/src/components/ui/card.tsx index 40cac5f..325aa00 100644 --- a/src/components/ui/card.tsx +++ b/src/components/ui/card.tsx @@ -1,6 +1,6 @@ -import * as React from "react" +import * as React from "react"; -import { cn } from "@/lib/utils" +import { cn } from "@/lib/utils"; function Card({ className, @@ -13,11 +13,11 @@ function Card({ data-size={size} className={cn( "group/card flex flex-col gap-4 overflow-hidden rounded-xl bg-card py-4 text-sm text-card-foreground ring-1 ring-foreground/10 has-data-[slot=card-footer]:pb-0 has-[>img:first-child]:pt-0 data-[size=sm]:gap-3 data-[size=sm]:py-3 data-[size=sm]:has-data-[slot=card-footer]:pb-0 *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl", - className + className, )} {...props} /> - ) + ); } function CardHeader({ className, ...props }: React.ComponentProps<"div">) { @@ -26,11 +26,11 @@ function CardHeader({ className, ...props }: React.ComponentProps<"div">) { data-slot="card-header" className={cn( "group/card-header @container/card-header grid auto-rows-min items-start gap-1 rounded-t-xl px-4 group-data-[size=sm]/card:px-3 has-data-[slot=card-action]:grid-cols-[1fr_auto] has-data-[slot=card-description]:grid-rows-[auto_auto] [.border-b]:pb-4 group-data-[size=sm]/card:[.border-b]:pb-3", - className + className, )} {...props} /> - ) + ); } function CardTitle({ className, ...props }: React.ComponentProps<"div">) { @@ -39,11 +39,11 @@ function CardTitle({ className, ...props }: React.ComponentProps<"div">) { data-slot="card-title" className={cn( "font-heading text-base leading-snug font-medium group-data-[size=sm]/card:text-sm", - className + className, )} {...props} /> - ) + ); } function CardDescription({ className, ...props }: React.ComponentProps<"div">) { @@ -53,7 +53,7 @@ function CardDescription({ className, ...props }: React.ComponentProps<"div">) { className={cn("text-sm text-muted-foreground", className)} {...props} /> - ) + ); } function CardAction({ className, ...props }: React.ComponentProps<"div">) { @@ -62,11 +62,11 @@ function CardAction({ className, ...props }: React.ComponentProps<"div">) { data-slot="card-action" className={cn( "col-start-2 row-span-2 row-start-1 self-start justify-self-end", - className + className, )} {...props} /> - ) + ); } function CardContent({ className, ...props }: React.ComponentProps<"div">) { @@ -76,7 +76,7 @@ function CardContent({ className, ...props }: React.ComponentProps<"div">) { className={cn("px-4 group-data-[size=sm]/card:px-3", className)} {...props} /> - ) + ); } function CardFooter({ className, ...props }: React.ComponentProps<"div">) { @@ -85,11 +85,11 @@ function CardFooter({ className, ...props }: React.ComponentProps<"div">) { data-slot="card-footer" className={cn( "flex items-center rounded-b-xl border-t bg-muted/50 p-4 group-data-[size=sm]/card:p-3", - className + className, )} {...props} /> - ) + ); } export { @@ -100,4 +100,4 @@ export { CardAction, CardDescription, CardContent, -} +}; diff --git a/src/components/ui/dialog.tsx b/src/components/ui/dialog.tsx index 014f5aa..308e17f 100644 --- a/src/components/ui/dialog.tsx +++ b/src/components/ui/dialog.tsx @@ -1,26 +1,26 @@ -"use client" +"use client"; -import * as React from "react" -import { Dialog as DialogPrimitive } from "@base-ui/react/dialog" +import * as React from "react"; +import { Dialog as DialogPrimitive } from "@base-ui/react/dialog"; -import { cn } from "@/lib/utils" -import { Button } from "@/components/ui/button" -import { XIcon } from "lucide-react" +import { cn } from "@/lib/utils"; +import { Button } from "@/components/ui/button"; +import { XIcon } from "lucide-react"; function Dialog({ ...props }: DialogPrimitive.Root.Props) { - return + return ; } function DialogTrigger({ ...props }: DialogPrimitive.Trigger.Props) { - return + return ; } function DialogPortal({ ...props }: DialogPrimitive.Portal.Props) { - return + return ; } function DialogClose({ ...props }: DialogPrimitive.Close.Props) { - return + return ; } function DialogOverlay({ @@ -32,11 +32,11 @@ function DialogOverlay({ data-slot="dialog-overlay" className={cn( "fixed inset-0 isolate z-50 bg-black/10 duration-100 supports-backdrop-filter:backdrop-blur-xs data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0", - className + className, )} {...props} /> - ) + ); } function DialogContent({ @@ -45,7 +45,7 @@ function DialogContent({ showCloseButton = true, ...props }: DialogPrimitive.Popup.Props & { - showCloseButton?: boolean + showCloseButton?: boolean; }) { return ( @@ -54,7 +54,7 @@ function DialogContent({ data-slot="dialog-content" className={cn( "fixed top-1/2 left-1/2 z-50 grid w-full max-w-[calc(100%-2rem)] -translate-x-1/2 -translate-y-1/2 gap-4 rounded-xl bg-popover p-4 text-sm text-popover-foreground ring-1 ring-foreground/10 duration-100 outline-none sm:max-w-sm data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", - className + className, )} {...props} > @@ -70,14 +70,13 @@ function DialogContent({ /> } > - + Close )} - ) + ); } function DialogHeader({ className, ...props }: React.ComponentProps<"div">) { @@ -87,7 +86,7 @@ function DialogHeader({ className, ...props }: React.ComponentProps<"div">) { className={cn("flex flex-col gap-2", className)} {...props} /> - ) + ); } function DialogFooter({ @@ -96,14 +95,14 @@ function DialogFooter({ children, ...props }: React.ComponentProps<"div"> & { - showCloseButton?: boolean + showCloseButton?: boolean; }) { return (
@@ -114,7 +113,7 @@ function DialogFooter({ )}
- ) + ); } function DialogTitle({ className, ...props }: DialogPrimitive.Title.Props) { @@ -123,11 +122,11 @@ function DialogTitle({ className, ...props }: DialogPrimitive.Title.Props) { data-slot="dialog-title" className={cn( "font-heading text-base leading-none font-medium", - className + className, )} {...props} /> - ) + ); } function DialogDescription({ @@ -139,11 +138,11 @@ function DialogDescription({ data-slot="dialog-description" className={cn( "text-sm text-muted-foreground *:[a]:underline *:[a]:underline-offset-3 *:[a]:hover:text-foreground", - className + className, )} {...props} /> - ) + ); } export { @@ -157,4 +156,4 @@ export { DialogPortal, DialogTitle, DialogTrigger, -} +}; diff --git a/src/components/ui/dropdown-menu.tsx b/src/components/ui/dropdown-menu.tsx index 9d5ebbd..0d67f0d 100644 --- a/src/components/ui/dropdown-menu.tsx +++ b/src/components/ui/dropdown-menu.tsx @@ -1,21 +1,21 @@ -"use client" +"use client"; -import * as React from "react" -import { Menu as MenuPrimitive } from "@base-ui/react/menu" +import * as React from "react"; +import { Menu as MenuPrimitive } from "@base-ui/react/menu"; -import { cn } from "@/lib/utils" -import { ChevronRightIcon, CheckIcon } from "lucide-react" +import { cn } from "@/lib/utils"; +import { ChevronRightIcon, CheckIcon } from "lucide-react"; function DropdownMenu({ ...props }: MenuPrimitive.Root.Props) { - return + return ; } function DropdownMenuPortal({ ...props }: MenuPrimitive.Portal.Props) { - return + return ; } function DropdownMenuTrigger({ ...props }: MenuPrimitive.Trigger.Props) { - return + return ; } function DropdownMenuContent({ @@ -41,16 +41,19 @@ function DropdownMenuContent({ > - ) + ); } function DropdownMenuGroup({ ...props }: MenuPrimitive.Group.Props) { - return + return ; } function DropdownMenuLabel({ @@ -58,7 +61,7 @@ function DropdownMenuLabel({ inset, ...props }: MenuPrimitive.GroupLabel.Props & { - inset?: boolean + inset?: boolean; }) { return ( - ) + ); } function DropdownMenuItem({ @@ -79,8 +82,8 @@ function DropdownMenuItem({ variant = "default", ...props }: MenuPrimitive.Item.Props & { - inset?: boolean - variant?: "default" | "destructive" + inset?: boolean; + variant?: "default" | "destructive"; }) { return ( - ) + ); } function DropdownMenuSub({ ...props }: MenuPrimitive.SubmenuRoot.Props) { - return + return ; } function DropdownMenuSubTrigger({ @@ -106,7 +109,7 @@ function DropdownMenuSubTrigger({ children, ...props }: MenuPrimitive.SubmenuTrigger.Props & { - inset?: boolean + inset?: boolean; }) { return ( {children} - ) + ); } function DropdownMenuSubContent({ @@ -135,14 +138,17 @@ function DropdownMenuSubContent({ return ( - ) + ); } function DropdownMenuCheckboxItem({ @@ -152,7 +158,7 @@ function DropdownMenuCheckboxItem({ inset, ...props }: MenuPrimitive.CheckboxItem.Props & { - inset?: boolean + inset?: boolean; }) { return ( - + {children} - ) + ); } function DropdownMenuRadioGroup({ ...props }: MenuPrimitive.RadioGroup.Props) { @@ -185,7 +190,7 @@ function DropdownMenuRadioGroup({ ...props }: MenuPrimitive.RadioGroup.Props) { data-slot="dropdown-menu-radio-group" {...props} /> - ) + ); } function DropdownMenuRadioItem({ @@ -194,7 +199,7 @@ function DropdownMenuRadioItem({ inset, ...props }: MenuPrimitive.RadioItem.Props & { - inset?: boolean + inset?: boolean; }) { return ( @@ -211,13 +216,12 @@ function DropdownMenuRadioItem({ data-slot="dropdown-menu-radio-item-indicator" > - + {children} - ) + ); } function DropdownMenuSeparator({ @@ -230,7 +234,7 @@ function DropdownMenuSeparator({ className={cn("-mx-1 my-1 h-px bg-border", className)} {...props} /> - ) + ); } function DropdownMenuShortcut({ @@ -242,11 +246,11 @@ function DropdownMenuShortcut({ data-slot="dropdown-menu-shortcut" className={cn( "ml-auto text-xs tracking-widest text-muted-foreground group-focus/dropdown-menu-item:text-accent-foreground", - className + className, )} {...props} /> - ) + ); } export { @@ -265,4 +269,4 @@ export { DropdownMenuSub, DropdownMenuSubTrigger, DropdownMenuSubContent, -} +}; diff --git a/src/components/ui/form.tsx b/src/components/ui/form.tsx index ea5329a..f3b8ee4 100644 --- a/src/components/ui/form.tsx +++ b/src/components/ui/form.tsx @@ -1,8 +1,8 @@ -"use client" +"use client"; -import * as React from "react" -import * as LabelPrimitive from "@radix-ui/react-label" -import { Slot } from "@radix-ui/react-slot" +import * as React from "react"; +import * as LabelPrimitive from "@radix-ui/react-label"; +import { Slot } from "@radix-ui/react-slot"; import { Controller, FormProvider, @@ -10,23 +10,23 @@ import { type ControllerProps, type FieldPath, type FieldValues, -} from "react-hook-form" +} from "react-hook-form"; -import { cn } from "@/lib/utils" -import { Label } from "@/components/ui/label" +import { cn } from "@/lib/utils"; +import { Label } from "@/components/ui/label"; -const Form = FormProvider +const Form = FormProvider; type FormFieldContextValue< TFieldValues extends FieldValues = FieldValues, TName extends FieldPath = FieldPath, > = { - name: TName -} + name: TName; +}; const FormFieldContext = React.createContext( - {} as FormFieldContextValue -) + {} as FormFieldContextValue, +); const FormField = < TFieldValues extends FieldValues = FieldValues, @@ -38,21 +38,21 @@ const FormField = < - ) -} + ); +}; const useFormField = () => { - const fieldContext = React.useContext(FormFieldContext) - const itemContext = React.useContext(FormItemContext) - const { getFieldState, formState } = useFormContext() + const fieldContext = React.useContext(FormFieldContext); + const itemContext = React.useContext(FormItemContext); + const { getFieldState, formState } = useFormContext(); - const fieldState = getFieldState(fieldContext.name, formState) + const fieldState = getFieldState(fieldContext.name, formState); if (!fieldContext) { - throw new Error("useFormField should be used within ") + throw new Error("useFormField should be used within "); } - const { id } = itemContext + const { id } = itemContext; return { id, @@ -61,32 +61,32 @@ const useFormField = () => { formDescriptionId: `${id}-form-item-description`, formMessageId: `${id}-form-item-message`, ...fieldState, - } -} + }; +}; type FormItemContextValue = { - id: string -} + id: string; +}; const FormItemContext = React.createContext( - {} as FormItemContextValue -) + {} as FormItemContextValue, +); function FormItem({ className, ...props }: React.ComponentProps<"div">) { - const id = React.useId() + const id = React.useId(); return (
- ) + ); } function FormLabel({ className, ...props }: React.ComponentProps) { - const { error, formItemId } = useFormField() + const { error, formItemId } = useFormField(); return (