From e6757bb9d4b49bba92fad431a854f431b7c3ce6b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 14 Feb 2026 16:51:04 +0000 Subject: [PATCH 01/28] Bump the development-dependencies group with 2 updates Bumps the development-dependencies group with 2 updates: [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) and [@types/react](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react). Updates `@types/node` from 25.2.0 to 25.2.3 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) Updates `@types/react` from 19.2.13 to 19.2.14 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react) --- updated-dependencies: - dependency-name: "@types/node" dependency-version: 25.2.3 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: development-dependencies - dependency-name: "@types/react" dependency-version: 19.2.14 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: development-dependencies ... Signed-off-by: dependabot[bot] --- package-lock.json | 39 +++++++++++---------------------------- package.json | 4 ++-- 2 files changed, 13 insertions(+), 30 deletions(-) diff --git a/package-lock.json b/package-lock.json index d7dc0cd..da7d709 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": { @@ -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", @@ -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", @@ -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..a44e06f 100644 --- a/package.json +++ b/package.json @@ -38,8 +38,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", From c7eed33bc5af463d9d5fe6565da0d2efee3c5aea Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 14 Feb 2026 16:51:16 +0000 Subject: [PATCH 02/28] Bump the production-dependencies group with 2 updates Bumps the production-dependencies group with 2 updates: [dotenv](https://github.com/motdotla/dotenv) and [lucide-react](https://github.com/lucide-icons/lucide/tree/HEAD/packages/lucide-react). Updates `dotenv` from 17.2.4 to 17.3.1 - [Changelog](https://github.com/motdotla/dotenv/blob/master/CHANGELOG.md) - [Commits](https://github.com/motdotla/dotenv/compare/v17.2.4...v17.3.1) Updates `lucide-react` from 0.563.0 to 0.564.0 - [Release notes](https://github.com/lucide-icons/lucide/releases) - [Commits](https://github.com/lucide-icons/lucide/commits/0.564.0/packages/lucide-react) --- updated-dependencies: - dependency-name: dotenv dependency-version: 17.3.1 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: production-dependencies - dependency-name: lucide-react dependency-version: 0.564.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: production-dependencies ... Signed-off-by: dependabot[bot] --- package-lock.json | 39 +++++++++++---------------------------- package.json | 4 ++-- 2 files changed, 13 insertions(+), 30 deletions(-) diff --git a/package-lock.json b/package-lock.json index d7dc0cd..177435b 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", @@ -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", @@ -2552,7 +2550,6 @@ "integrity": "sha512-KkiJeU6VbYbUOp5ITMIc7kBfqlYkKA5KhEHVrGMmUUMt7NeaZg65ojdPk+FtNrBAOXNVM5QM72jnADjM+XVRAQ==", "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..dd60f32 100644 --- a/package.json +++ b/package.json @@ -25,8 +25,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", From c90ea7f57b404991b3e77201f9c72018cf639912 Mon Sep 17 00:00:00 2001 From: b-at-neu Date: Sat, 14 Feb 2026 12:01:51 -0500 Subject: [PATCH 03/28] #5 create copilot instructions file --- .github/copilot-instructions.md | 123 ++++++++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 .github/copilot-instructions.md diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000..3819d0d --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,123 @@ +# 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/ + ├── db.ts # Prisma client + ├── utils.ts # utility functions + └── validations/ # Zod schemas +``` + +## 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 From 81a5287ec5fc841ee9174655a2c89729cadc10cb Mon Sep 17 00:00:00 2001 From: b-at-neu Date: Sat, 14 Feb 2026 12:04:09 -0500 Subject: [PATCH 04/28] #5 update file organization structure --- .github/copilot-instructions.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 3819d0d..3c540b8 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -81,9 +81,10 @@ app/ ├── (routes)/ # route groups components/ # shared components lib/ + ├── actions/ # server actions + ├── data/ # data queries and services ├── db.ts # Prisma client - ├── utils.ts # utility functions - └── validations/ # Zod schemas + └── utils.ts # utility functions ``` ## Code Style Preferences From 23b537c60d755fea410af5ab4b7e0556437bcf83 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 14 Feb 2026 17:08:28 +0000 Subject: [PATCH 05/28] Initial plan From 49967e49079452b4a4cd0ea724c7fa6f082a46bf Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 14 Feb 2026 17:12:43 +0000 Subject: [PATCH 06/28] #0 improve settings initialization with better error handling Co-authored-by: b-at-neu <144247685+b-at-neu@users.noreply.github.com> --- lib/data/settings.ts | 69 ++++++++++++++++++++++++++++++++------------ package-lock.json | 4 +-- 2 files changed, 52 insertions(+), 21 deletions(-) diff --git a/lib/data/settings.ts b/lib/data/settings.ts index a18bb1c..3b998c7 100644 --- a/lib/data/settings.ts +++ b/lib/data/settings.ts @@ -2,6 +2,11 @@ import { db } from '@/lib/db'; import { cache } from 'react'; +import { + PrismaClientKnownRequestError, + PrismaClientInitializationError, + PrismaClientValidationError +} from '@prisma/client/runtime/library'; export interface Settings { id: string; @@ -17,6 +22,20 @@ export interface Settings { updatedAt: Date; } +/** + * Default settings to use when database is unavailable or settings don't exist yet + */ +const DEFAULT_SETTINGS: Omit = { + requiredNominations: 15, + maxCommunityNominations: 7, + endorsementRequired: false, + endorsementsOpen: true, + applicationDeadline: null, + applicationsOpen: true, + nominationsOpen: true, + customMessage: null, +}; + /** * Get the application settings. If no settings exist, create default settings. * This is cached to reduce database queries. @@ -33,32 +52,44 @@ export const getSettings = cache(async (): Promise => { // 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, - }, - }); + console.log('No settings found in database, creating default settings...'); + try { + settings = await db.settings.create({ + data: DEFAULT_SETTINGS, + }); + console.log('Default settings created successfully'); + } catch (createError) { + // If we can't create settings (e.g., database issue during creation), + // log the error but continue with in-memory defaults + console.error('Error creating default settings:', createError); + console.warn('Falling back to in-memory default settings'); + return { + id: 'default', + ...DEFAULT_SETTINGS, + createdAt: new Date(), + updatedAt: new Date(), + }; + } } return settings; } catch (error) { - console.error('Error fetching settings:', error); + // Handle different types of Prisma errors + if (error instanceof PrismaClientInitializationError) { + console.error('Database connection error:', error.message); + console.warn('Database is not available. Using in-memory default settings.'); + } else if (error instanceof PrismaClientKnownRequestError) { + console.error('Database request error:', error.message, error.code); + } else if (error instanceof PrismaClientValidationError) { + console.error('Database validation error:', error.message); + } else { + console.error('Unexpected error fetching settings:', error); + } + // Return default settings if database query fails return { id: 'default', - requiredNominations: 15, - maxCommunityNominations: 7, - endorsementRequired: false, - endorsementsOpen: true, - applicationDeadline: null, - applicationsOpen: true, - nominationsOpen: true, - customMessage: null, + ...DEFAULT_SETTINGS, createdAt: new Date(), updatedAt: new Date(), }; diff --git a/package-lock.json b/package-lock.json index d7dc0cd..ad3ea9f 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": { From 24edf38008ac5e04c588ff7c419e1673fb5fc2ea Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 14 Feb 2026 17:14:51 +0000 Subject: [PATCH 07/28] #0 improve error detection for database initialization errors Co-authored-by: b-at-neu <144247685+b-at-neu@users.noreply.github.com> --- lib/data/settings.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/data/settings.ts b/lib/data/settings.ts index 3b998c7..76e1c7e 100644 --- a/lib/data/settings.ts +++ b/lib/data/settings.ts @@ -82,6 +82,10 @@ export const getSettings = cache(async (): Promise => { console.error('Database request error:', error.message, error.code); } else if (error instanceof PrismaClientValidationError) { console.error('Database validation error:', error.message); + } else if (error && typeof error === 'object' && 'name' in error && error.name === 'PrismaClientInitializationError') { + // Sometimes during build, instanceof doesn't work, so check by name + console.error('Database connection error (detected by name):', (error as Error).message); + console.warn('Database is not available. Using in-memory default settings.'); } else { console.error('Unexpected error fetching settings:', error); } From 2a079250140357ea4b589c27fdde30dcf0b36734 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 14 Feb 2026 17:16:06 +0000 Subject: [PATCH 08/28] #0 consolidate duplicate error handling logic Co-authored-by: b-at-neu <144247685+b-at-neu@users.noreply.github.com> --- lib/data/settings.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/data/settings.ts b/lib/data/settings.ts index 76e1c7e..4505580 100644 --- a/lib/data/settings.ts +++ b/lib/data/settings.ts @@ -75,17 +75,17 @@ export const getSettings = cache(async (): Promise => { return settings; } catch (error) { // Handle different types of Prisma errors - if (error instanceof PrismaClientInitializationError) { - console.error('Database connection error:', error.message); + const isDatabaseConnectionError = + error instanceof PrismaClientInitializationError || + (error && typeof error === 'object' && 'name' in error && error.name === 'PrismaClientInitializationError'); + + if (isDatabaseConnectionError) { + console.error('Database connection error:', (error as Error).message); console.warn('Database is not available. Using in-memory default settings.'); } else if (error instanceof PrismaClientKnownRequestError) { console.error('Database request error:', error.message, error.code); } else if (error instanceof PrismaClientValidationError) { console.error('Database validation error:', error.message); - } else if (error && typeof error === 'object' && 'name' in error && error.name === 'PrismaClientInitializationError') { - // Sometimes during build, instanceof doesn't work, so check by name - console.error('Database connection error (detected by name):', (error as Error).message); - console.warn('Database is not available. Using in-memory default settings.'); } else { console.error('Unexpected error fetching settings:', error); } From 88748edbf46d28944ea408a1c25a77d1838b3572 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 14 Feb 2026 17:24:41 +0000 Subject: [PATCH 09/28] Initial plan From 1745431925ad0127b50432c0a8572da5b117b1e8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 14 Feb 2026 17:28:08 +0000 Subject: [PATCH 10/28] #9 add docker-based local database setup Co-authored-by: b-at-neu <144247685+b-at-neu@users.noreply.github.com> --- .env.example | 9 ++- README.md | 146 +++++++++++++++++++++++++++++++++++++++++++-- docker-compose.yml | 15 +++++ package.json | 4 ++ 4 files changed, 167 insertions(+), 7 deletions(-) create mode 100644 docker-compose.yml 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/README.md b/README.md index 3157a25..f4abcfa 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,7 +101,131 @@ 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: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** +- Check if PostgreSQL is already running locally: `sudo lsof -i :5432` (Mac/Linux) or check Task Manager (Windows) +- 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 +- For production conflicts, coordinate with your team before resetting + +### Database Deployment Notes + +- **Local database** is completely separate from production/staging databases +- **Migration files** in `prisma/migrations/` are version controlled and committed to git +- **Vercel deployment** automatically runs migrations when code is merged to `dev` or `main` branches +- **Production database** (Supabase) is updated through the Vercel deployment process +- You can safely reset your local database anytime without affecting production + +### Old Setup Instructions (PostgreSQL without Docker) + +If you prefer to use a cloud-hosted database or local PostgreSQL installation instead of Docker: + +1. [PostgreSQL](https://www.postgresql.org/download/) database + - Local installation or cloud service (Supabase, Neon, etc.) + +2. Update your `.env.local` with your database credentials: +``` +DATABASE_URL="postgresql://user:password@host:port/database" +DIRECT_URL="postgresql://user:password@host:port/database" +``` + +3. Generate Prisma client and run migrations: ```bash npm run prisma:generate npm run prisma:migrate @@ -121,12 +248,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/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..fde8177 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,15 @@ +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 + +volumes: + postgres_data: diff --git a/package.json b/package.json index f458ba0..8b9846f 100644 --- a/package.json +++ b/package.json @@ -7,8 +7,12 @@ "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 && npm run prisma:migrate", "prisma:generate": "prisma generate", "prisma:migrate": "prisma migrate dev", + "prisma:studio": "prisma studio", "postinstall": "prisma generate" }, "private": true, From 7614986ed3ef6af2768c8dda1ec2c6de3f2a73c2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 14 Feb 2026 17:30:12 +0000 Subject: [PATCH 11/28] #9 add sleep to db:reset for container startup Co-authored-by: b-at-neu <144247685+b-at-neu@users.noreply.github.com> --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index d7dc0cd..ad3ea9f 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": { diff --git a/package.json b/package.json index 8b9846f..e674aec 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "lint": "next lint", "db:start": "docker compose up -d", "db:stop": "docker compose down", - "db:reset": "docker compose down -v && docker compose up -d && npm run prisma:migrate", + "db:reset": "docker compose down -v && docker compose up -d && sleep 3 && npm run prisma:migrate", "prisma:generate": "prisma generate", "prisma:migrate": "prisma migrate dev", "prisma:studio": "prisma studio", From 4437ab5b47a078f0680686874edbde37ac5f57f3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 14 Feb 2026 17:31:21 +0000 Subject: [PATCH 12/28] Initial plan From f577a1ec0e55c2a2a297e16e76a4903ccf0e0fab Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 14 Feb 2026 17:31:25 +0000 Subject: [PATCH 13/28] #8 fail deployment when database connection is unavailable Co-authored-by: b-at-neu <144247685+b-at-neu@users.noreply.github.com> --- lib/data/settings.ts | 72 ++++++++++++++------------------------------ 1 file changed, 23 insertions(+), 49 deletions(-) diff --git a/lib/data/settings.ts b/lib/data/settings.ts index 4505580..c2667db 100644 --- a/lib/data/settings.ts +++ b/lib/data/settings.ts @@ -3,9 +3,7 @@ import { db } from '@/lib/db'; import { cache } from 'react'; import { - PrismaClientKnownRequestError, PrismaClientInitializationError, - PrismaClientValidationError } from '@prisma/client/runtime/library'; export interface Settings { @@ -22,24 +20,13 @@ export interface Settings { updatedAt: Date; } -/** - * Default settings to use when database is unavailable or settings don't exist yet - */ -const DEFAULT_SETTINGS: Omit = { - requiredNominations: 15, - maxCommunityNominations: 7, - endorsementRequired: false, - endorsementsOpen: true, - applicationDeadline: null, - applicationsOpen: true, - nominationsOpen: true, - customMessage: null, -}; - /** * Get the application settings. If no settings exist, create default settings. * This is cached to reduce database queries. * + * If the database connection fails, this function will throw an error to prevent + * deployment with an improperly configured database. + * * 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 @@ -53,49 +40,36 @@ export const getSettings = cache(async (): Promise => { // If no settings exist, create default settings if (!settings) { console.log('No settings found in database, creating default settings...'); - try { - settings = await db.settings.create({ - data: DEFAULT_SETTINGS, - }); - console.log('Default settings created successfully'); - } catch (createError) { - // If we can't create settings (e.g., database issue during creation), - // log the error but continue with in-memory defaults - console.error('Error creating default settings:', createError); - console.warn('Falling back to in-memory default settings'); - return { - id: 'default', - ...DEFAULT_SETTINGS, - createdAt: new Date(), - updatedAt: new Date(), - }; - } + settings = await db.settings.create({ + data: { + requiredNominations: 15, + maxCommunityNominations: 7, + endorsementRequired: false, + endorsementsOpen: true, + applicationsOpen: true, + nominationsOpen: true, + }, + }); + console.log('Default settings created successfully'); } return settings; } catch (error) { - // Handle different types of Prisma errors + // Check if this is a database connection error const isDatabaseConnectionError = error instanceof PrismaClientInitializationError || (error && typeof error === 'object' && 'name' in error && error.name === 'PrismaClientInitializationError'); if (isDatabaseConnectionError) { - console.error('Database connection error:', (error as Error).message); - console.warn('Database is not available. Using in-memory default settings.'); - } else if (error instanceof PrismaClientKnownRequestError) { - console.error('Database request error:', error.message, error.code); - } else if (error instanceof PrismaClientValidationError) { - console.error('Database validation error:', error.message); - } else { - console.error('Unexpected error fetching settings:', error); + console.error('FATAL: Database connection failed. Cannot proceed with deployment.'); + console.error('Database error:', (error as Error).message); + console.error('Please ensure the database is configured correctly and accessible.'); + // Re-throw the error to fail the deployment + throw error; } - // Return default settings if database query fails - return { - id: 'default', - ...DEFAULT_SETTINGS, - createdAt: new Date(), - updatedAt: new Date(), - }; + // For other errors, log and re-throw + console.error('Error fetching settings:', error); + throw error; } }); From 481e985edf680a82c0fad7f55b054bbaad2f496a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 14 Feb 2026 17:31:33 +0000 Subject: [PATCH 14/28] #9 use docker compose --wait for cross-platform compatibility Co-authored-by: b-at-neu <144247685+b-at-neu@users.noreply.github.com> --- docker-compose.yml | 5 +++++ package.json | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index fde8177..80f82d5 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -10,6 +10,11 @@ services: - "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/package.json b/package.json index e674aec..8165d90 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "lint": "next lint", "db:start": "docker compose up -d", "db:stop": "docker compose down", - "db:reset": "docker compose down -v && docker compose up -d && sleep 3 && npm run prisma:migrate", + "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", From 2e3d35532f26d7824a0cb61cd2c5b0bf35a69333 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 14 Feb 2026 17:32:27 +0000 Subject: [PATCH 15/28] #9 add security warning and improve windows troubleshooting Co-authored-by: b-at-neu <144247685+b-at-neu@users.noreply.github.com> --- README.md | 3 ++- docker-compose.yml | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index f4abcfa..7e35df8 100644 --- a/README.md +++ b/README.md @@ -182,7 +182,8 @@ git push ### Troubleshooting **Port 5432 already in use** -- Check if PostgreSQL is already running locally: `sudo lsof -i :5432` (Mac/Linux) or check Task Manager (Windows) +- 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** diff --git a/docker-compose.yml b/docker-compose.yml index 80f82d5..4021a3f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,6 +2,8 @@ services: postgres: image: postgres:15-alpine container_name: senate-path-db + # WARNING: These credentials are for LOCAL DEVELOPMENT ONLY + # DO NOT use this docker-compose.yml in production environment: POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres From b458d7ee58cd8ae4f286a3af8a1796e6df39b279 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 14 Feb 2026 17:32:49 +0000 Subject: [PATCH 16/28] #8 extract default settings to constant and include all fields Co-authored-by: b-at-neu <144247685+b-at-neu@users.noreply.github.com> --- lib/data/settings.ts | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/lib/data/settings.ts b/lib/data/settings.ts index c2667db..1b0609b 100644 --- a/lib/data/settings.ts +++ b/lib/data/settings.ts @@ -20,6 +20,20 @@ export interface Settings { updatedAt: Date; } +/** + * Default settings values used when creating initial settings + */ +const DEFAULT_SETTINGS_VALUES = { + requiredNominations: 15, + maxCommunityNominations: 7, + endorsementRequired: false, + endorsementsOpen: true, + applicationDeadline: null, + applicationsOpen: true, + nominationsOpen: true, + customMessage: null, +} as const; + /** * Get the application settings. If no settings exist, create default settings. * This is cached to reduce database queries. @@ -41,14 +55,7 @@ export const getSettings = cache(async (): Promise => { if (!settings) { console.log('No settings found in database, creating default settings...'); settings = await db.settings.create({ - data: { - requiredNominations: 15, - maxCommunityNominations: 7, - endorsementRequired: false, - endorsementsOpen: true, - applicationsOpen: true, - nominationsOpen: true, - }, + data: DEFAULT_SETTINGS_VALUES, }); console.log('Default settings created successfully'); } From 74e3b338686f572288acf0c501b887113c8f86fe Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 14 Feb 2026 17:34:03 +0000 Subject: [PATCH 17/28] #11 remove react cache from getSettings to fix persistence issue Co-authored-by: b-at-neu <144247685+b-at-neu@users.noreply.github.com> --- lib/data/settings.ts | 6 ++---- package-lock.json | 4 ++-- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/lib/data/settings.ts b/lib/data/settings.ts index a18bb1c..8100334 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; @@ -19,14 +18,13 @@ export interface Settings { /** * 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 => { +export async function getSettings(): Promise { try { // Try to get the first settings record let settings = await db.settings.findFirst(); @@ -63,4 +61,4 @@ export const getSettings = cache(async (): Promise => { updatedAt: new Date(), }; } -}); +} diff --git a/package-lock.json b/package-lock.json index d7dc0cd..ad3ea9f 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": { From ce05fce4261115c14db4b0754000f608333d7ba1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 14 Feb 2026 17:54:42 +0000 Subject: [PATCH 18/28] #8 add global database connection check at startup Co-authored-by: b-at-neu <144247685+b-at-neu@users.noreply.github.com> --- instrumentation.ts | 6 ++++ lib/data/settings.ts | 77 ++++++++++---------------------------------- lib/db.ts | 17 ++++++++++ 3 files changed, 40 insertions(+), 60 deletions(-) create mode 100644 instrumentation.ts diff --git a/instrumentation.ts b/instrumentation.ts new file mode 100644 index 0000000..c73fb05 --- /dev/null +++ b/instrumentation.ts @@ -0,0 +1,6 @@ +export async function register() { + if (process.env.NEXT_RUNTIME === 'nodejs') { + const { checkDatabaseConnection } = await import('@/lib/db'); + await checkDatabaseConnection(); + } +} diff --git a/lib/data/settings.ts b/lib/data/settings.ts index 1b0609b..b3afb9f 100644 --- a/lib/data/settings.ts +++ b/lib/data/settings.ts @@ -2,9 +2,6 @@ import { db } from '@/lib/db'; import { cache } from 'react'; -import { - PrismaClientInitializationError, -} from '@prisma/client/runtime/library'; export interface Settings { id: string; @@ -20,63 +17,23 @@ export interface Settings { updatedAt: Date; } -/** - * Default settings values used when creating initial settings - */ -const DEFAULT_SETTINGS_VALUES = { - requiredNominations: 15, - maxCommunityNominations: 7, - endorsementRequired: false, - endorsementsOpen: true, - applicationDeadline: null, - applicationsOpen: true, - nominationsOpen: true, - customMessage: null, -} as const; - -/** - * Get the application settings. If no settings exist, create default settings. - * This is cached to reduce database queries. - * - * If the database connection fails, this function will throw an error to prevent - * deployment with an improperly configured database. - * - * 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) { - console.log('No settings found in database, creating default settings...'); - settings = await db.settings.create({ - data: DEFAULT_SETTINGS_VALUES, - }); - console.log('Default settings created successfully'); - } - - return settings; - } catch (error) { - // Check if this is a database connection error - const isDatabaseConnectionError = - error instanceof PrismaClientInitializationError || - (error && typeof error === 'object' && 'name' in error && error.name === 'PrismaClientInitializationError'); - - if (isDatabaseConnectionError) { - console.error('FATAL: Database connection failed. Cannot proceed with deployment.'); - console.error('Database error:', (error as Error).message); - console.error('Please ensure the database is configured correctly and accessible.'); - // Re-throw the error to fail the deployment - throw error; - } - - // For other errors, log and re-throw - console.error('Error fetching settings:', error); - throw error; + let settings = await db.settings.findFirst(); + + if (!settings) { + settings = await db.settings.create({ + data: { + requiredNominations: 15, + maxCommunityNominations: 7, + endorsementRequired: false, + endorsementsOpen: true, + applicationDeadline: null, + applicationsOpen: true, + nominationsOpen: true, + customMessage: null, + }, + }); } + + return settings; }); diff --git a/lib/db.ts b/lib/db.ts index 588fe5b..c5c9006 100644 --- a/lib/db.ts +++ b/lib/db.ts @@ -7,3 +7,20 @@ const globalForPrisma = globalThis as unknown as { export const db = globalForPrisma.prisma ?? new PrismaClient(); if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = db; + +/** + * Check database connection health. Throws an error if connection fails. + * This should be called during application startup to fail fast if database is unavailable. + */ +export async function checkDatabaseConnection() { + try { + await db.$connect(); + await db.$queryRaw`SELECT 1`; + console.log('Database connection successful'); + } catch (error) { + console.error('FATAL: Database connection failed. Cannot proceed with deployment.'); + console.error('Database error:', error); + console.error('Please ensure the database is configured correctly and accessible.'); + throw error; + } +} From d988b0894a74c9b436501c406aca375a96e8f359 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 14 Feb 2026 17:55:36 +0000 Subject: [PATCH 19/28] #9 remove docker warning and simplify deployment notes Co-authored-by: b-at-neu <144247685+b-at-neu@users.noreply.github.com> --- README.md | 28 +++------------------------- docker-compose.yml | 2 -- 2 files changed, 3 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 7e35df8..eb97a55 100644 --- a/README.md +++ b/README.md @@ -203,34 +203,12 @@ git push **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 -- For production conflicts, coordinate with your team before resetting ### Database Deployment Notes -- **Local database** is completely separate from production/staging databases -- **Migration files** in `prisma/migrations/` are version controlled and committed to git -- **Vercel deployment** automatically runs migrations when code is merged to `dev` or `main` branches -- **Production database** (Supabase) is updated through the Vercel deployment process -- You can safely reset your local database anytime without affecting production - -### Old Setup Instructions (PostgreSQL without Docker) - -If you prefer to use a cloud-hosted database or local PostgreSQL installation instead of Docker: - -1. [PostgreSQL](https://www.postgresql.org/download/) database - - Local installation or cloud service (Supabase, Neon, etc.) - -2. Update your `.env.local` with your database credentials: -``` -DATABASE_URL="postgresql://user:password@host:port/database" -DIRECT_URL="postgresql://user:password@host:port/database" -``` - -3. Generate Prisma client and run migrations: -```bash -npm run prisma:generate -npm run prisma:migrate -``` +- 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 diff --git a/docker-compose.yml b/docker-compose.yml index 4021a3f..80f82d5 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,8 +2,6 @@ services: postgres: image: postgres:15-alpine container_name: senate-path-db - # WARNING: These credentials are for LOCAL DEVELOPMENT ONLY - # DO NOT use this docker-compose.yml in production environment: POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres From 2b5cec8335d720de6f2951426b565d0b9693c7f7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 14 Feb 2026 17:56:15 +0000 Subject: [PATCH 20/28] #8 remove redundant db connect call to prevent connection leaks Co-authored-by: b-at-neu <144247685+b-at-neu@users.noreply.github.com> --- lib/db.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/lib/db.ts b/lib/db.ts index c5c9006..4674049 100644 --- a/lib/db.ts +++ b/lib/db.ts @@ -8,13 +8,8 @@ export const db = globalForPrisma.prisma ?? new PrismaClient(); if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = db; -/** - * Check database connection health. Throws an error if connection fails. - * This should be called during application startup to fail fast if database is unavailable. - */ export async function checkDatabaseConnection() { try { - await db.$connect(); await db.$queryRaw`SELECT 1`; console.log('Database connection successful'); } catch (error) { From 5c65f580fce3f772569c1435aeb1128c4abfb705 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 14 Feb 2026 18:12:22 +0000 Subject: [PATCH 21/28] #9 add database url validation with clear error message Co-authored-by: b-at-neu <144247685+b-at-neu@users.noreply.github.com> --- lib/db.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) 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; }; From 907a6b0fae34c668dcbe869e5060338c60513e67 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 14 Feb 2026 18:13:37 +0000 Subject: [PATCH 22/28] Initial plan From 93dc22f7c7947c54b0df141c6161a1d08616c894 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 14 Feb 2026 18:16:14 +0000 Subject: [PATCH 23/28] #14 add migration to fix endorsementsOpen default value Co-authored-by: b-at-neu <144247685+b-at-neu@users.noreply.github.com> --- .../20260214181540_fix_endorsements_open_default/migration.sql | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 prisma/migrations/20260214181540_fix_endorsements_open_default/migration.sql 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; From 00eccf9fe4e9834d7b39fccc889eaeb0e40f241a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 14 Feb 2026 18:20:48 +0000 Subject: [PATCH 24/28] #8 remove instrumentation and let database errors propagate naturally Co-authored-by: b-at-neu <144247685+b-at-neu@users.noreply.github.com> --- instrumentation.ts | 6 ------ lib/db.ts | 12 ------------ 2 files changed, 18 deletions(-) delete mode 100644 instrumentation.ts diff --git a/instrumentation.ts b/instrumentation.ts deleted file mode 100644 index c73fb05..0000000 --- a/instrumentation.ts +++ /dev/null @@ -1,6 +0,0 @@ -export async function register() { - if (process.env.NEXT_RUNTIME === 'nodejs') { - const { checkDatabaseConnection } = await import('@/lib/db'); - await checkDatabaseConnection(); - } -} diff --git a/lib/db.ts b/lib/db.ts index 4674049..588fe5b 100644 --- a/lib/db.ts +++ b/lib/db.ts @@ -7,15 +7,3 @@ const globalForPrisma = globalThis as unknown as { export const db = globalForPrisma.prisma ?? new PrismaClient(); if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = db; - -export async function checkDatabaseConnection() { - try { - await db.$queryRaw`SELECT 1`; - console.log('Database connection successful'); - } catch (error) { - console.error('FATAL: Database connection failed. Cannot proceed with deployment.'); - console.error('Database error:', error); - console.error('Please ensure the database is configured correctly and accessible.'); - throw error; - } -} From b16d5230ffdab3751640f2bec77605ac2486f63a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 14 Feb 2026 20:02:10 +0000 Subject: [PATCH 25/28] #11 add router.refresh() to update ui after saving settings Co-authored-by: b-at-neu <144247685+b-at-neu@users.noreply.github.com> --- app/admin/settings/settings-form.tsx | 3 +++ 1 file changed, 3 insertions(+) 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'); } From 749992c2f570cc68fdabb7f39f1375831e1909a6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 16 Feb 2026 01:46:48 +0000 Subject: [PATCH 26/28] #11 use prisma upsert with fixed id to prevent duplicate settings rows Co-authored-by: b-at-neu <144247685+b-at-neu@users.noreply.github.com> --- lib/actions/settings.ts | 61 +++++++++++++++++++---------------------- lib/data/settings.ts | 35 ++++++++++++----------- 2 files changed, 47 insertions(+), 49 deletions(-) diff --git a/lib/actions/settings.ts b/lib/actions/settings.ts index 35f69f8..a3288be 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,31 @@ 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, - }, - }); - } + // Use upsert to atomically create or update settings + const settings = await db.settings.upsert({ + where: { id: SETTINGS_ID }, + update: { + requiredNominations: data.requiredNominations, + maxCommunityNominations: data.maxCommunityNominations, + endorsementRequired: data.endorsementRequired, + endorsementsOpen: data.endorsementsOpen, + applicationDeadline: data.applicationDeadline, + applicationsOpen: data.applicationsOpen, + nominationsOpen: data.nominationsOpen, + customMessage: data.customMessage, + }, + create: { + id: SETTINGS_ID, + requiredNominations: data.requiredNominations, + maxCommunityNominations: data.maxCommunityNominations, + endorsementRequired: data.endorsementRequired, + endorsementsOpen: data.endorsementsOpen, + applicationDeadline: data.applicationDeadline, + applicationsOpen: data.applicationsOpen, + nominationsOpen: data.nominationsOpen, + customMessage: data.customMessage, + }, + }); // Revalidate all pages that might use settings revalidatePath('/'); diff --git a/lib/data/settings.ts b/lib/data/settings.ts index 358f968..f656d39 100644 --- a/lib/data/settings.ts +++ b/lib/data/settings.ts @@ -16,23 +16,26 @@ export interface Settings { updatedAt: Date; } -export async function getSettings(): Promise { - let settings = await db.settings.findFirst(); +// Fixed ID for the singleton settings record +const SETTINGS_ID = 'default'; - if (!settings) { - settings = await db.settings.create({ - data: { - requiredNominations: 15, - maxCommunityNominations: 7, - endorsementRequired: false, - endorsementsOpen: true, - applicationDeadline: null, - applicationsOpen: true, - nominationsOpen: true, - customMessage: null, - }, - }); - } +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, + endorsementsOpen: true, + applicationDeadline: null, + applicationsOpen: true, + nominationsOpen: true, + customMessage: null, + }, + }); return settings; } From d8fde4d74a48b33ced677d8b8d245fa018273966 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 16 Feb 2026 01:48:08 +0000 Subject: [PATCH 27/28] #11 reduce duplication by extracting settings data object Co-authored-by: b-at-neu <144247685+b-at-neu@users.noreply.github.com> --- lib/actions/settings.ts | 32 ++++++++++++++------------------ 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/lib/actions/settings.ts b/lib/actions/settings.ts index a3288be..860cfee 100644 --- a/lib/actions/settings.ts +++ b/lib/actions/settings.ts @@ -31,29 +31,25 @@ export async function updateSettings(data: UpdateSettingsData) { return { success: false, error: 'Unauthorized' }; } + // 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: { - requiredNominations: data.requiredNominations, - maxCommunityNominations: data.maxCommunityNominations, - endorsementRequired: data.endorsementRequired, - endorsementsOpen: data.endorsementsOpen, - applicationDeadline: data.applicationDeadline, - applicationsOpen: data.applicationsOpen, - nominationsOpen: data.nominationsOpen, - customMessage: data.customMessage, - }, + update: settingsData, create: { id: SETTINGS_ID, - requiredNominations: data.requiredNominations, - maxCommunityNominations: data.maxCommunityNominations, - endorsementRequired: data.endorsementRequired, - endorsementsOpen: data.endorsementsOpen, - applicationDeadline: data.applicationDeadline, - applicationsOpen: data.applicationsOpen, - nominationsOpen: data.nominationsOpen, - customMessage: data.customMessage, + ...settingsData, }, }); From 65f5c2d942c467fed762d1bb220fd771cef8cb23 Mon Sep 17 00:00:00 2001 From: Benedikt Winkler <144247685+b-at-neu@users.noreply.github.com> Date: Sun, 15 Feb 2026 20:54:48 -0500 Subject: [PATCH 28/28] Bump version from 1.4.0 to 1.4.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a49b4ab..8fa1d3f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@nomination-system/source", - "version": "1.4.0", + "version": "1.4.1", "license": "MIT", "scripts": { "dev": "next dev",