Repository navigation
Feat/srs implementation - #114
Conversation
Introduces compare-committee-exports and generate-committee-diff-report with xlsx support and a pnpm compare-committees entry point. Co-authored-by: Cursor <cursoragent@cursor.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
- Create /admin layout.tsx with AuthCheck gate for all admin routes
- Create AdminSidebar with desktop sidebar and mobile Sheet hamburger
- Add adminNav.ts config with enabled/disabled ("Coming soon") items
- Create /admin page.tsx as Data landing page
- Redirect /admin/data and /admin/dashboard to /admin
- Remove redundant AuthCheck from orphaned admin pages
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Extract useFileUpload hook for shared R2 upload logic - Split SpecialReports.tsx into VoterImport.tsx and AbsenteeReport.tsx - Create ElectionConfigTab.tsx combining Election Dates + Office Names - Update AdminDataClient with new 5-tab layout: Invites | Election Config | Voter Import | Discrepancies | Absentee Report - Delete SpecialReports.tsx - Mark all deliverables complete in action items doc Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…m (1.1c) Replace hardcoded capacity check (>= 4) in handleRequest route with config-driven maxSeatsPerLted from singleton governance config row. Adds getGovernanceConfig() helper, seed data, and migration. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
- Add CommitteeTerm model and migration with termId FK on CommitteeList - Add getActiveTerm/getActiveTermId helpers in committeeValidation - Filter committee queries by active term across committees, bulk load, reports - Add /admin/terms page with TermsManagement (create, set active) - Add GET/POST /api/admin/terms, PATCH /api/admin/terms/[id] - Fix ESLint type-safety in terms page, TermsManagement, committeeValidation - Fix test type issues (fetchLoaded, reportJobs, reports, DateRangePicker, dateRangeValueEquality) - Refactor error display in AbsenteeReport and VoterImport Co-authored-by: Cursor <cursoragent@cursor.com>
…rt script - Add LtedDistrictCrosswalk Prisma model (SRS §4.2) - Migration for crosswalk table with unique constraint and index - seedLtedCrosswalk.ts: import script for 2024 LTED Matrix.xlsx - Monroe County town code → cityTown mapping - db:seed-lted-crosswalk npm script Co-authored-by: Cursor <cursoragent@cursor.com>
…kets - Add tickets 1.2-1.5: committee membership model, membership type, seat model, audit trail - Add tickets 2.1, 2.1a, 2.3: eligibility validation, email/phone submission, resignation workflow - Add test specs T1.2 (report generation API) and T1.3 (discrepancy handling) - Update 1.1c committee governance config and README Co-authored-by: Cursor <cursoragent@cursor.com>
- Add 1.4 to Depends On in 2.3-resignation-workflow.md - Update README dependency diagram: 1.4 → 2.3 - Update Tier 2 table Depends on column for 2.3 Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
- Add CommitteeMembership model with full lifecycle (SUBMITTED, ACTIVE, REMOVED, etc.) - Add MembershipStatus, RemovalReason, ResignationMethod, MembershipType enums - Migrate VoterRecord.committeeId and CommitteeRequest to CommitteeMembership - Update committee/add, remove, requestAdd, handleRequest APIs - CommitteeSelector and requests page use CommitteeMembership - Capacity and duplicate-committee checks via committeeValidation - Mark ticket 1.2 Done in docs/SRS Co-authored-by: Cursor <cursoragent@cursor.com>
- Migration: set membershipType=APPOINTED on backfilled ACTIVE records - API committee/add: accept optional membershipType, default APPOINTED - API handleRequest: set membershipType=APPOINTED on accept (leader vacancy fill) - AddCommitteeForm: optional membershipType select (admin only) - CommitteeSelector + VoterCard: display membershipType badge - Tests: expectMembershipCreate/Update for add and handleRequest - testUtils: use Prisma CommitteeMembershipUpdateInput/CreateInput - .cursorrules: use typed matchers instead of expect.objectContaining Co-authored-by: Cursor <cursoragent@cursor.com>
Break 1.5 into three implementation sub-tickets: schema & seed, utility & immutability guard, and route wiring with integration tests. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
SRS 1.4 — Seat Model: explicit seat slots per committee+term with weight tracking. Schema & migration: - Seat model: committeeListId, termId, seatNumber, isPetitioned, weight - CommitteeList.ltedWeight (Decimal?) - Migration with backfill from CommitteeGovernanceConfig.maxSeatsPerLted Seat assignment: - assignNextAvailableSeat() in seatUtils - ensureSeatsExist() for new committees (add, handleRequest, bulkLoad) - seatNumber assigned on membership activation Weight computation: - recomputeSeatWeights() when ltedWeight updated - PATCH /api/committee/updateLtedWeight - POST /api/admin/weightedTable/import (MCDC Excel bulk import) Admin UI: - CommitteeSelector: LTED weight field, seat roster with occupants - Admin Data: Weighted Table import tab Docs: 1.4 ticket updated to Done, README index updated Co-authored-by: Cursor <cursoragent@cursor.com>
- Gap 1: session.user.id falsy → 401 from withPrivilege, no report, no PDF call - Gap 2: verify x-webhook-signature header and gzip body in PDF API request Mark T1.2 ticket as Done. Co-authored-by: Cursor <cursoragent@cursor.com>
- bulkLoadCommittees: auth, VERCEL block, empty/no-discrepancy load, import with discrepancies, VRCNUM not found, error handling - handleCommitteeDiscrepancy: auth, 400/404, accept/reject flows, takeAddress, already-resolved idempotency, voter-not-found 500 - Mark T1.3 ticket Done in roadmap Co-authored-by: Cursor <cursoragent@cursor.com>
Add AuditAction enum (12 actions), AuditLog model with indexes, and User.auditLogs relation. Seed SYSTEM user for FK integrity on system-initiated audit events. Mark ticket 1.5a as Done. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implements the audit log utility function and runtime immutability enforcement for the AuditLog model per SRS §1.5b. - logAuditEvent: non-throwing utility wrapping prisma.auditLog.create with console.error fallback; exports SYSTEM_USER_ID constant - auditLogImmutabilityGuard: Prisma middleware blocking update/delete on AuditLog, registered in prisma.ts client setup - 13 unit tests covering utility fields, error suppression, and guard enforcement for all blocked operations Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add audit logging to add, remove, handleRequest, and requestAdd routes with before/after snapshots and capacity-rejection metadata. Includes integration tests for add and handleRequest covering happy path, no-op skipping, and audit failure resilience. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Mark 1.1, 1.1b, 1.3, 1.4, 1.5 as Done - Mark T1.2 and T1.3 testing tickets as Done - Update Implementation Sequence and Combined Timeline - Point 'Next' to 2.1 Eligibility Validation - Update 2.1 'What exists today' for backend-enforced already-in-another-committee Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
…R.5/1.R.7 Committee API fixes: - requestAdd now resubmits existing non-active CommitteeMembership rows by updating back to SUBMITTED, clearing terminal fields, and preserving/updating submissionMetadata (1.R.2). - handleRequest accept now supports replacement flow via submissionMetadata.removeMemberId, removes the replacement target with MEMBER_REMOVED audit logging, and returns 422 when target is missing/non-active (1.R.3). - add and handleRequest accept now execute capacity check + seat assignment + membership writes in a single transaction, with CommitteeList row locking (FOR UPDATE) and transactional seat utils support (1.R.7). - handleCommitteeDiscrepancy accept now writes CommitteeMembership/seat state instead of committeeMemberList.connect, including capacity handling (1.R.5). Bulk import and source-of-truth alignment: - Refactored bulkLoadCommittees to remove destructive committeeList.deleteMany and deprecated voterRecord.committeeId writes. - Bulk import now upserts committees, ensures seats, reconciles ACTIVE->REMOVED for absent imports, and creates/reactivates ACTIVE memberships with seat assignment (advances 1.R.4, supports 1.R.5). - report-server committee mapping now reads active CommitteeMembership + voterRecord instead of committeeMemberList; added dedicated tests. Test and tooling fixes: - Added AuditAction enum stubs and transaction mocking in frontend jest.setup to fix audit enum failures (1.R.6). - Updated/added route tests for requestAdd resubmission, replacement accept path, discrepancy membership activation, and report-server committee mapping helper coverage. Documentation updates: - Updated SRS remediation tickets 1.R.2-1.R.7 statuses and acceptance checklists to reflect implemented vs remaining work. Validation: - Full workspace test pass succeeded via pnpm test (shared-validators, voter-import-processor, frontend, report-server all green).
… shared helper Pull the SUBMITTED → ACTIVE / REJECTED transaction logic out of handleRequest and the meeting bulk-decisions route into a single membershipConfirmation module shared by both. Behavior changes folded in for consistency across the two confirm paths: - membershipType now resolves to membership.membershipType ?? "APPOINTED" in handleRequest (was hardcoded "APPOINTED"). - Confirm-path audit events (MEMBER_CONFIRMED/ACTIVATED and capacity reject) use logAuditEventOrThrow, so an audit-log failure rolls back the transaction instead of being swallowed. - Bulk meeting decisions now run the full confirm path: active-in-another- committee rejection, replacement-target handling, and maxSeats-bounded capacity check. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ge scaffolding Eliminate the six hand-maintained jurisdiction-scoped report types (committeeRoster, signInSheet, designationWeightSummary, vacancyReport, changesReport, petitionOutcomesReport) duplicated across schemas, pages, forms, and the report grid. - Add narrow cross-package scopeReportRegistry.ts (label/prisma type/ filename/format/extraFields); derive SCOPE_REPORT_TYPES, jurisdiction labels, and the scope slice of REPORT_TYPE_MAPPINGS from it. - Collapse generateReportSchema/enrichedReportDataSchema onto a shared variant tuple; enforce cityTown at API validation for jurisdiction scope. - Add frontend SCOPE_REPORT_UI registry + scopeReportFormSpecs, ScopedReportForm, ScopedReportPageShell, and loadScopedReportPageData (DB-side Leader filtering, fail-closed on missing user id); thin the six pages and delete six *Form.tsx. - Derive GenerateReportGrid scope cards from the UI registry (minPrivilege display-only; auth stays on actual session privilege). - Preserve auth/data-scope invariants; parity coverage for all six specs. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Extract shared trim-only comparison rules so admission validation and BOE flagging stay aligned; add sharp-edge and whitespace regression tests. Co-authored-by: Cursor <cursoragent@cursor.com>
Centralize active-member count and cross-committee checks in committeeValidation so transactional admission paths and preflight eligibility share one implementation. Co-authored-by: Cursor <cursoragent@cursor.com>
Extract runEligibilityPreflight with prefetched validation inputs, share findActiveTerm and ACTIVE_MEMBERSHIP_STATUS across committee routes, and assert single config/count fetches in tests. Co-authored-by: Cursor <cursoragent@cursor.com>
Add the test-type-safety skill and review doc, restore EnrichedReportData discrimination for report-server consumers, and move buildAuditWhere out of the audit route so Next.js typecheck stays valid. Co-authored-by: Cursor <cursoragent@cursor.com>
…kill Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…isma Consolidate the duplicated designation-weight rule (frontend and report-server each carried a hand-maintained copy) into a single pure, DB-agnostic engine in shared-prisma. Both consumers now delegate via thin adapters that preserve their existing call shapes and re-export the result types, so downstream callers are unchanged. - Add computeDesignationWeight + indexActiveMembershipsBySeat with a shared duplicate-seat integrity check; buildSeatRosterRows now indexes occupants once and shares the map with the weight engine. - Add jest + ts-jest and unit tests covering the engine's branches; add tsconfig.build.json so tests are excluded from the published build. - Export Prisma as a value so consumers can use Prisma.Decimal at runtime. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Enable add-candidate selection when a removal is already selected so the Remove or Replace flow works for full committees, with replacement-specific UI copy. Co-authored-by: Cursor <cursoragent@cursor.com>
Compliance-critical membership, petition, and bulk-import mutations no longer commit when their audit row fails to write, per SRS 11.1's immutable-audit requirement. - Swap logAuditEvent -> logAuditEventOrThrow at the 12 compliance-critical sites: requestAdd (2), add (2), petition-outcomes (5), bulkLoadUtils (3). - Wrap requestAdd's create/resubmit membership write + audit in a single prisma.$transaction so an audit failure rolls back the state change. - Record before/after isPetitioned on the seat PETITION_RECORDED audit. - Leave reference/config audit sites (crosswalk, jurisdictions, meetings) fail-open, each with an inline justification. - Document intended usage on logAuditEvent / logAuditEventOrThrow. - Tests: audit-failure paths now assert 500/rollback; add a two-committee bulk-import test proving rollback scope is per-committee (earlier committees stay committed, batch aborts on the failing one). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Parse YYYY-MM-DD ranges as UTC calendar days with a half-open upper bound so events later on the selected end date are included. Co-authored-by: Cursor <cursoragent@cursor.com>
Deduplicate the two LTED xlsx importers (crosswalk, weightedTable) and their upload tabs. Shared server lib at lib/lted/ (monroeTownCodes, digitParsing, xlsxUpload) with the stricter digit parser unified across both routes and seedLtedCrosswalk.ts; shared client <XlsxUploadCard /> used by both tabs. Per-row business logic stays in each route. Add a TODO on the weightedTable route to split the normal import from a bulk import that accepts the currently-unused ltedMatrix param. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Extract duplicated invite UI formatting and unused-invite predicates into lib/invites so admin, accept, and apply flows stay in sync. Co-authored-by: Cursor <cursoragent@cursor.com>
Extract shared PresignedUploadReportForm for voter import and absentee report, and drive admin data tabs from a co-located registry with render functions to prevent config/switch drift. Co-authored-by: Cursor <cursoragent@cursor.com>
Replace client-side AuthCheck wrappers on admin and authenticated server pages with server-side checks so Prisma queries run only after authorization. Co-authored-by: Cursor <cursoragent@cursor.com>
…nges When admins change maxSeatsPerLted, create missing seats or safely trim excess vacant seats in the active term, recompute weights, and audit the reconciliation. Block decreases that would strand active or petitioned seats; require an active term before allowing max-seat changes. Co-authored-by: Cursor <cursoragent@cursor.com>
Introduce repeatable whole-app review workflows with vector-specific methodologies, agent skills, scan/freeze/gate scripts, and pnpm review:* commands. Co-authored-by: Cursor <cursoragent@cursor.com>
Introduce the data-lifecycle retention review vector and tighten methodology for isolated run directories, explicit scan vector args, and scope-gate rules. Co-authored-by: Cursor <cursoragent@cursor.com>
…ectors Promotes three candidate vectors from FUTURE_CONSIDERATIONS.md into the review methodology, skill overlays, and scan tooling: frontend state & interaction correctness, accessibility & mobile operability, and dev experience & local reproducibility. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The Designated Petition reports-hub card and the /petitions page were gated only at "authenticated", but generation requires RequestAccess (ReadAccess sits below that bar). A ReadAccess user could see the card, open the form, submit, and always receive a 403. - GenerateReportGrid: add minPrivilege=RequestAccess to the Designated Petition card so the visibleReportTypes filter hides it from ReadAccess - petitions/page.tsx: gate to RequestAccess, rendering AdminPageAccessDenied for authenticated-but-insufficient while keeping PageSignInRequired for logged-out users - add GenerateReportGrid test covering card visibility by privilege Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replace next/font/google's Inter (which fetches from Google at build time) with the self-hosted @fontsource/inter weights, wired through the --font-sans CSS variable. Keeps builds fully offline/reproducible while still actually loading Inter instead of silently falling back to system fonts. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Mark the DB/auth-backed pages as force-dynamic so `next build` does not attempt to statically prerender them (which hits Prisma/auth at build time and breaks offline builds). Gate Sentry source-map upload and release creation behind an explicit CI check so local builds stay offline. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…mock types Add error-path cases for the invite GET/apply/pending routes (401/403/404/409/410 and blank-token short-circuit) and for applyPendingInvite (no_invite, missing jurisdiction scope, unconsumable invite). Replace loose casts in the select mock with a typed __isSelectTrigger guard, and drop a stale non-null assertion. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add POST /api/admin/invites cases for auth (401/403), Leader/non-Leader jurisdiction validation, existing-user and pending-invite rejection, and P2002 unique-constraint mapping to a duplicate-invite error. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
ca9af8f to
f4dfeb2
Compare
No description provided.