diff --git a/.env.example b/.env.example index e4f9a39..84c4bc2 100644 --- a/.env.example +++ b/.env.example @@ -1,6 +1,11 @@ # Database -DATABASE_URL="postgresql://user:password@host:port/database" -DIRECT_URL="postgresql://user:password@host:port/database" +# For local development with Docker (see README.md "Local Development Database" section) +DATABASE_URL="postgresql://postgres:postgres@localhost:5432/senate_path_dev?schema=public" +DIRECT_URL="postgresql://postgres:postgres@localhost:5432/senate_path_dev?schema=public" + +# For production (Supabase or other hosted PostgreSQL) +# DATABASE_URL="postgresql://user:password@host:port/database" +# DIRECT_URL="postgresql://user:password@host:port/database" # Supabase NEXT_PUBLIC_SUPABASE_URL="your-supabase-url" diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000..3c540b8 --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,124 @@ +# GitHub Copilot Instructions for Next.js Project + +## Commit & Branch Naming Conventions + +### Commits +- **Format**: `#XXX commit message in lowercase imperative mood` +- **Examples**: + - `#123 add user authentication flow` + - `#456 fix navigation menu overflow` + - `#789 refactor payment processing logic` +- **Rules**: + - Always prefix with issue number + - Use lowercase only + - Use imperative mood (add, fix, refactor, not added, fixed, refactored) + - No colon after issue number + +### Branches +- **Format**: `XXX-ticket-name-in-kebab-case` +- **Examples**: + - `123-add-user-authentication` + - `456-fix-navigation-overflow` + - `789-refactor-payment-processing` + +### Pull Requests +- **Format**: `#XXX Ticket Name In Title Case` +- **Examples**: + - `#123 Add User Authentication` + - `#456 Fix Navigation Overflow` + - `#789 Refactor Payment Processing` + +## Tech Stack + +- **Framework**: Next.js with App Router +- **Database**: Prisma ORM +- **Styling**: Tailwind CSS +- **UI Components**: shadcn/ui +- **Language**: TypeScript (strict mode) + +## Architecture & Patterns + +### API Layer +- **NEVER use API routes** (`app/api/` directory) +- **ALWAYS use Server Actions** for data mutations +- **Use server components** for data fetching with Prisma calls +- Import Prisma queries from separate service/repository files + +### Component Structure +- Components live in centralized `components/` directory +- Organize into subdirectories as needed: +``` + components/ + ├── ui/ # shadcn components + ├── forms/ # form-related components + ├── layouts/ # layout components + └── features/ # feature-specific shared components +``` +- Keep components focused and composable + +### TypeScript +- Use TypeScript strict mode +- Define proper types for all props and function parameters +- Avoid `any` type +- Prefer interfaces for component props +- Use Prisma-generated types where applicable + +### Server Actions +- Define server actions in separate files (e.g., `actions/user-actions.ts`) +- Always use `'use server'` directive +- Include proper error handling and validation +- Return typed responses + +### Data Access +- All database operations go through Prisma +- Create service/repository files for complex queries +- Keep Prisma calls in server components or server actions only +- Never expose database calls to client components + +### File Organization +``` +app/ + ├── (routes)/ # route groups +components/ # shared components +lib/ + ├── actions/ # server actions + ├── data/ # data queries and services + ├── db.ts # Prisma client + └── utils.ts # utility functions +``` + +## Code Style Preferences + +- Use named exports over default exports in all cases +- Prefer functional components with hooks +- Use async/await over promises +- Implement proper loading and error states +- Use Tailwind classes, avoid custom CSS unless absolutely necessary +- Follow shadcn/ui patterns for component composition + +## Best Practices + +1. **Server vs Client Components**: + - Default to server components + - Only use `'use client'` when needed (interactivity, hooks, browser APIs) + +2. **Data Fetching**: + - Fetch data in server components + - Use Prisma for all database queries + - Implement proper error boundaries + +3. **Forms**: + - Use Server Actions for form submissions + - Implement progressive enhancement + - Add proper validation (client + server) + +4. **Performance**: + - Leverage Next.js caching strategies + - Use `revalidatePath` or `revalidateTag` after mutations + - Optimize images with next/image + +5. **Security**: + - Validate all inputs + - Never expose sensitive data to client + - Use environment variables for secrets + \ No newline at end of file diff --git a/README.md b/README.md index 3157a25..eb97a55 100644 --- a/README.md +++ b/README.md @@ -62,8 +62,9 @@ The application manages three main entities: - To check if it's installed: `node -v` - To install: [mac/linux](https://github.com/nvm-sh/nvm) or [windows](https://github.com/coreybutler/nvm-windows) -2. [PostgreSQL](https://www.postgresql.org/download/) database - - Local installation or cloud service (Supabase, Neon, etc.) +2. [Docker Desktop](https://www.docker.com/products/docker-desktop/) (for local database) + - Required for running the local PostgreSQL database + - To check if it's installed: `docker --version` ### Installation @@ -83,7 +84,9 @@ npm install cp .env.example .env.local ``` -Edit `.env.local` and add your database connection strings and Supabase credentials: +The `.env.example` file comes pre-configured with local database settings. For local development, you can use these defaults as-is. + +For production or if you want to use a different database: ``` DATABASE_URL="postgresql://user:password@host:port/database" DIRECT_URL="postgresql://user:password@host:port/database" @@ -98,11 +101,114 @@ To get your Supabase credentials: - Copy the Project URL and anon/public key - Copy the service_role key (this is needed for user management - keep it secret!) -4. Generate Prisma client and run migrations: +## Local Development Database + +This project uses Docker to run a local PostgreSQL database for development. This allows you to test database schema changes locally before deploying to production. + +### Prerequisites + +- [Docker Desktop](https://www.docker.com/products/docker-desktop/) must be installed and running +- Ensure port 5432 is available on your machine + +### Initial Setup + +1. Make sure you have copied `.env.example` to `.env.local`: +```bash +cp .env.example .env.local +``` + +The default local database connection is already configured in `.env.example`: +``` +DATABASE_URL="postgresql://postgres:postgres@localhost:5432/senate_path_dev?schema=public" +DIRECT_URL="postgresql://postgres:postgres@localhost:5432/senate_path_dev?schema=public" +``` + +2. Start the local PostgreSQL database: +```bash +npm run db:start +``` + +3. Run Prisma migrations to create the database schema: +```bash +npm run prisma:migrate +``` + +You're now ready to develop! The local database will persist data between restarts. + +### Common Commands + +| Command | Description | +|---------|-------------| +| `npm run db:start` | Start the Docker PostgreSQL container in the background | +| `npm run db:stop` | Stop the Docker container (data is preserved) | +| `npm run db:reset` | Stop container, remove all data, start fresh, and run migrations | +| `npm run prisma:migrate` | Create and apply a new Prisma migration (will prompt for migration name) | +| `npm run prisma:studio` | Open Prisma Studio to visually browse and edit your local database | +| `npm run prisma:generate` | Regenerate Prisma client after schema changes | + +### Development Workflow + +1. Create a new branch for your work: +```bash +git checkout -b 123-add-new-feature +``` + +2. Start the local database (if not already running): +```bash +npm run db:start +``` + +3. Make changes to your Prisma schema in `prisma/schema.prisma` + +4. Create and apply the migration: ```bash -npm run prisma:generate npm run prisma:migrate ``` +Prisma will prompt you for a migration name (e.g., "add_user_role_field") + +5. Test your changes locally using Prisma Studio or your application: +```bash +npm run prisma:studio # Visual database browser +npm run dev # Start Next.js dev server +``` + +6. Commit both your schema changes AND the migration files: +```bash +git add prisma/schema.prisma prisma/migrations/ +git commit -m "#123 add new feature" +git push +``` + +### Troubleshooting + +**Port 5432 already in use** +- Mac/Linux: Check if PostgreSQL is running: `sudo lsof -i :5432` +- Windows: Check port usage: `netstat -ano | findstr :5432` +- Stop the existing PostgreSQL service or change the port in `docker-compose.yml` + +**Container won't start** +- Make sure Docker Desktop is running +- Check Docker logs: `docker logs senate-path-db` +- Try removing the container: `docker compose down -v` then `npm run db:start` + +**Database connection errors** +- Verify the container is running: `docker ps` +- Check your `.env.local` file has the correct `DATABASE_URL` +- Try restarting the container: `npm run db:stop && npm run db:start` + +**Need to start fresh** +- Use `npm run db:reset` to completely reset your local database +- This will delete all data and re-run migrations + +**Migration conflicts** +- If you have uncommitted migrations, you may need to reset: `npm run db:reset` +- Delete problematic migration folders from `prisma/migrations/` and re-create them + +### Database Deployment Notes + +- Local database is completely separate from production/staging databases +- Migration files in `prisma/migrations/` are version controlled +- Migrations run automatically on deployment when merged to `dev` or `main` branches ## Running the app @@ -121,12 +227,21 @@ npm start ## Available Scripts +### Development - `npm run dev` - Start development server - `npm run build` - Build for production - `npm start` - Start production server - `npm run lint` - Run ESLint + +### Database Management +- `npm run db:start` - Start local Docker PostgreSQL container +- `npm run db:stop` - Stop local Docker container +- `npm run db:reset` - Reset local database (removes all data and re-runs migrations) + +### Prisma - `npm run prisma:generate` - Generate Prisma client -- `npm run prisma:migrate` - Run database migrations +- `npm run prisma:migrate` - Create and apply database migrations +- `npm run prisma:studio` - Open Prisma Studio for database inspection ## Key Features diff --git a/app/admin/settings/settings-form.tsx b/app/admin/settings/settings-form.tsx index 3f7318b..54b2cbc 100644 --- a/app/admin/settings/settings-form.tsx +++ b/app/admin/settings/settings-form.tsx @@ -4,6 +4,7 @@ import { useState } from 'react'; import { useForm } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; import { z } from 'zod'; +import { useRouter } from 'next/navigation'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; @@ -49,6 +50,7 @@ interface SettingsFormProps { } export default function SettingsForm({ settings }: SettingsFormProps) { + const router = useRouter(); const [isSubmitting, setIsSubmitting] = useState(false); const [error, setError] = useState(null); @@ -101,6 +103,7 @@ export default function SettingsForm({ settings }: SettingsFormProps) { if (result.success) { toast.success('Settings updated successfully!'); + router.refresh(); } else { setError(result.error || 'Failed to update settings'); } diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..80f82d5 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,20 @@ +services: + postgres: + image: postgres:15-alpine + container_name: senate-path-db + environment: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + POSTGRES_DB: senate_path_dev + ports: + - "5432:5432" + volumes: + - postgres_data:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U postgres"] + interval: 5s + timeout: 5s + retries: 5 + +volumes: + postgres_data: diff --git a/lib/actions/settings.ts b/lib/actions/settings.ts index 35f69f8..860cfee 100644 --- a/lib/actions/settings.ts +++ b/lib/actions/settings.ts @@ -16,6 +16,9 @@ export interface UpdateSettingsData { customMessage: string | null; } +// Fixed ID for the singleton settings record +const SETTINGS_ID = 'default'; + export async function updateSettings(data: UpdateSettingsData) { try { // Check if user is authenticated @@ -28,39 +31,27 @@ export async function updateSettings(data: UpdateSettingsData) { return { success: false, error: 'Unauthorized' }; } - // Get or create settings - let settings = await db.settings.findFirst(); - - if (!settings) { - // Create new settings - settings = await db.settings.create({ - data: { - requiredNominations: data.requiredNominations, - maxCommunityNominations: data.maxCommunityNominations, - endorsementRequired: data.endorsementRequired, - endorsementsOpen: data.endorsementsOpen, - applicationDeadline: data.applicationDeadline, - applicationsOpen: data.applicationsOpen, - nominationsOpen: data.nominationsOpen, - customMessage: data.customMessage, - }, - }); - } else { - // Update existing settings - settings = await db.settings.update({ - where: { id: settings.id }, - data: { - requiredNominations: data.requiredNominations, - maxCommunityNominations: data.maxCommunityNominations, - endorsementRequired: data.endorsementRequired, - endorsementsOpen: data.endorsementsOpen, - applicationDeadline: data.applicationDeadline, - applicationsOpen: data.applicationsOpen, - nominationsOpen: data.nominationsOpen, - customMessage: data.customMessage, - }, - }); - } + // Prepare settings data + const settingsData = { + requiredNominations: data.requiredNominations, + maxCommunityNominations: data.maxCommunityNominations, + endorsementRequired: data.endorsementRequired, + endorsementsOpen: data.endorsementsOpen, + applicationDeadline: data.applicationDeadline, + applicationsOpen: data.applicationsOpen, + nominationsOpen: data.nominationsOpen, + customMessage: data.customMessage, + }; + + // Use upsert to atomically create or update settings + const settings = await db.settings.upsert({ + where: { id: SETTINGS_ID }, + update: settingsData, + create: { + id: SETTINGS_ID, + ...settingsData, + }, + }); // Revalidate all pages that might use settings revalidatePath('/'); diff --git a/lib/data/settings.ts b/lib/data/settings.ts index a18bb1c..f656d39 100644 --- a/lib/data/settings.ts +++ b/lib/data/settings.ts @@ -1,7 +1,6 @@ 'use server'; import { db } from '@/lib/db'; -import { cache } from 'react'; export interface Settings { id: string; @@ -17,40 +16,16 @@ export interface Settings { updatedAt: Date; } -/** - * Get the application settings. If no settings exist, create default settings. - * This is cached to reduce database queries. - * - * Note: This implementation uses findFirst() and assumes a single settings record. - * While the database schema allows multiple records, the application logic ensures - * only one record is created and used. If needed, a unique constraint could be added - * to enforce this at the database level, or we could use a fixed ID. - */ -export const getSettings = cache(async (): Promise => { - try { - // Try to get the first settings record - let settings = await db.settings.findFirst(); - - // If no settings exist, create default settings - if (!settings) { - settings = await db.settings.create({ - data: { - requiredNominations: 15, - maxCommunityNominations: 7, - endorsementRequired: false, - endorsementsOpen: true, - applicationsOpen: true, - nominationsOpen: true, - }, - }); - } - - return settings; - } catch (error) { - console.error('Error fetching settings:', error); - // Return default settings if database query fails - return { - id: 'default', +// Fixed ID for the singleton settings record +const SETTINGS_ID = 'default'; + +export async function getSettings(): Promise { + // Use upsert to atomically get or create settings + const settings = await db.settings.upsert({ + where: { id: SETTINGS_ID }, + update: {}, + create: { + id: SETTINGS_ID, requiredNominations: 15, maxCommunityNominations: 7, endorsementRequired: false, @@ -59,8 +34,8 @@ export const getSettings = cache(async (): Promise => { applicationsOpen: true, nominationsOpen: true, customMessage: null, - createdAt: new Date(), - updatedAt: new Date(), - }; - } -}); + }, + }); + + return settings; +} diff --git a/lib/db.ts b/lib/db.ts index 588fe5b..b2d6681 100644 --- a/lib/db.ts +++ b/lib/db.ts @@ -1,5 +1,16 @@ import { PrismaClient } from '@prisma/client'; +// Validate that required environment variables are set +if (!process.env.DATABASE_URL) { + throw new Error( + 'DATABASE_URL is not set. Please ensure you have a .env or .env.local file with DATABASE_URL defined.\n' + + 'For local development, run:\n' + + ' cp .env.example .env.local\n' + + ' npm run db:start\n' + + 'See README.md "Local Development Database" section for details.' + ); +} + const globalForPrisma = globalThis as unknown as { prisma: PrismaClient | undefined; }; diff --git a/package-lock.json b/package-lock.json index d7dc0cd..01c9e6f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@nomination-system/source", - "version": "1.3.1", + "version": "1.4.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@nomination-system/source", - "version": "1.3.1", + "version": "1.4.0", "hasInstallScript": true, "license": "MIT", "dependencies": { @@ -22,8 +22,8 @@ "@tailwindcss/postcss": "^4.1.18", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", - "dotenv": "^17.2.4", - "lucide-react": "^0.563.0", + "dotenv": "^17.3.1", + "lucide-react": "^0.564.0", "next": "^16.1.6", "prisma": "^6.18.0", "react": "^19.2.4", @@ -35,8 +35,8 @@ "zod": "^4.3.6" }, "devDependencies": { - "@types/node": "^25.2.0", - "@types/react": "^19.2.13", + "@types/node": "^25.2.3", + "@types/react": "^19.2.14", "@types/react-dom": "^19.2.3", "@types/styled-components": "^5.1.36", "eslint": "^9.39.2", @@ -90,7 +90,6 @@ "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", @@ -2158,7 +2157,6 @@ "resolved": "https://registry.npmjs.org/@supabase/supabase-js/-/supabase-js-2.95.3.tgz", "integrity": "sha512-Fukw1cUTQ6xdLiHDJhKKPu6svEPaCEDvThqCne3OaQyZvuq2qjhJAd91kJu3PXLG18aooCgYBaB6qQz35hhABg==", "license": "MIT", - "peer": true, "dependencies": { "@supabase/auth-js": "2.95.3", "@supabase/functions-js": "2.95.3", @@ -2532,9 +2530,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "25.2.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.2.0.tgz", - "integrity": "sha512-DZ8VwRFUNzuqJ5khrvwMXHmvPe+zGayJhr2CDNiKB1WBE1ST8Djl00D0IC4vvNmHMdj6DlbYRIaFE7WHjlDl5w==", + "version": "25.2.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.2.3.tgz", + "integrity": "sha512-m0jEgYlYz+mDJZ2+F4v8D1AyQb+QzsNqRuI7xg1VQX/KlKS0qT9r1Mo16yo5F/MtifXFgaofIFsdFMox2SxIbQ==", "license": "MIT", "dependencies": { "undici-types": "~7.16.0" @@ -2547,12 +2545,11 @@ "license": "MIT" }, "node_modules/@types/react": { - "version": "19.2.13", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.13.tgz", - "integrity": "sha512-KkiJeU6VbYbUOp5ITMIc7kBfqlYkKA5KhEHVrGMmUUMt7NeaZg65ojdPk+FtNrBAOXNVM5QM72jnADjM+XVRAQ==", + "version": "19.2.14", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", + "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", "devOptional": true, "license": "MIT", - "peer": true, "dependencies": { "csstype": "^3.2.2" } @@ -2563,7 +2560,6 @@ "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", "devOptional": true, "license": "MIT", - "peer": true, "peerDependencies": { "@types/react": "^19.2.0" } @@ -2635,7 +2631,6 @@ "integrity": "sha512-BnOroVl1SgrPLywqxyqdJ4l3S2MsKVLDVxZvjI1Eoe8ev2r3kGDo+PcMihNmDE+6/KjkTubSJnmqGZZjQSBq/g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.46.2", "@typescript-eslint/types": "8.46.2", @@ -3153,7 +3148,6 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -3506,7 +3500,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.8.19", "caniuse-lite": "^1.0.30001751", @@ -3983,9 +3976,9 @@ } }, "node_modules/dotenv": { - "version": "17.2.4", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.4.tgz", - "integrity": "sha512-mudtfb4zRB4bVvdj0xRo+e6duH1csJRM8IukBqfTRvHotn9+LBXB8ynAidP9zHqoRC/fsllXgk4kCKlR21fIhw==", + "version": "17.3.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.3.1.tgz", + "integrity": "sha512-IO8C/dzEb6O3F9/twg6ZLXz164a2fhTnEWb95H23Dm4OuN+92NmEAlTrupP9VW6Jm3sO26tQlqyvyi4CsnY9GA==", "license": "BSD-2-Clause", "engines": { "node": ">=12" @@ -4260,7 +4253,6 @@ "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -4446,7 +4438,6 @@ "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.9", @@ -6185,9 +6176,9 @@ } }, "node_modules/lucide-react": { - "version": "0.563.0", - "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.563.0.tgz", - "integrity": "sha512-8dXPB2GI4dI8jV4MgUDGBeLdGk8ekfqVZ0BdLcrRzocGgG75ltNEmWS+gE7uokKF/0oSUuczNDT+g9hFJ23FkA==", + "version": "0.564.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.564.0.tgz", + "integrity": "sha512-JJ8GVTQqFwuliifD48U6+h7DXEHdkhJ/E87kksGByII3qHxtPciVb8T8woQONHBQgHVOl7rSMrrip3SeVNy7Fg==", "license": "ISC", "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" @@ -6710,7 +6701,6 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -6788,7 +6778,6 @@ "integrity": "sha512-bXWy3vTk8mnRmT+SLyZBQoC2vtV9Z8u7OHvEu+aULYxwiop/CPiFZ+F56KsNRNf35jw+8wcu8pmLsjxpBxAO9g==", "hasInstallScript": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "@prisma/config": "6.18.0", "@prisma/engines": "6.18.0" @@ -6887,7 +6876,6 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz", "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -6897,7 +6885,6 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz", "integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==", "license": "MIT", - "peer": true, "dependencies": { "scheduler": "^0.27.0" }, @@ -6910,7 +6897,6 @@ "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.71.1.tgz", "integrity": "sha512-9SUJKCGKo8HUSsCO+y0CtqkqI5nNuaDqTxyqPsZPqIwudpj4rCrAz/jZV+jn57bx5gtZKOh3neQu94DXMc+w5w==", "license": "MIT", - "peer": true, "engines": { "node": ">=18.0.0" }, @@ -7600,8 +7586,7 @@ "version": "4.1.18", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.18.tgz", "integrity": "sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/tailwindcss-animate": { "version": "1.0.7", @@ -7803,7 +7788,6 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "devOptional": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -8141,7 +8125,6 @@ "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz", "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", "license": "MIT", - "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } diff --git a/package.json b/package.json index f458ba0..8fa1d3f 100644 --- a/package.json +++ b/package.json @@ -1,14 +1,18 @@ { "name": "@nomination-system/source", - "version": "1.4.0", + "version": "1.4.1", "license": "MIT", "scripts": { "dev": "next dev", "build": "next build", "start": "next start", "lint": "next lint", + "db:start": "docker compose up -d", + "db:stop": "docker compose down", + "db:reset": "docker compose down -v && docker compose up -d --wait && npm run prisma:migrate", "prisma:generate": "prisma generate", "prisma:migrate": "prisma migrate dev", + "prisma:studio": "prisma studio", "postinstall": "prisma generate" }, "private": true, @@ -25,8 +29,8 @@ "@tailwindcss/postcss": "^4.1.18", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", - "dotenv": "^17.2.4", - "lucide-react": "^0.563.0", + "dotenv": "^17.3.1", + "lucide-react": "^0.564.0", "next": "^16.1.6", "prisma": "^6.18.0", "react": "^19.2.4", @@ -38,8 +42,8 @@ "zod": "^4.3.6" }, "devDependencies": { - "@types/node": "^25.2.0", - "@types/react": "^19.2.13", + "@types/node": "^25.2.3", + "@types/react": "^19.2.14", "@types/react-dom": "^19.2.3", "@types/styled-components": "^5.1.36", "eslint": "^9.39.2", diff --git a/prisma/migrations/20260214181540_fix_endorsements_open_default/migration.sql b/prisma/migrations/20260214181540_fix_endorsements_open_default/migration.sql new file mode 100644 index 0000000..6106687 --- /dev/null +++ b/prisma/migrations/20260214181540_fix_endorsements_open_default/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "settings" ALTER COLUMN "endorsementsOpen" SET DEFAULT true;