Two bugs caused the cards list and integrations to show stale data
after a user edited a card in the detail view:
1. ResponseCard.name is a denormalized display string set only at
OCR / survey-submit time. Editing firstName or lastName never
recomputed it, so the table header and Name column kept the old
value. PUT /api/cards/[id] now recomputes name from first + last
whenever either changes (unless the caller passed an explicit
name). The detail page header reads from in-flight edits so the
title updates live as the user types.
2. The default form template marked only firstName/lastName as
isCore. Every other field (email, cellPhone, address, etc.) was
non-core, so dynamic-field edits landed in ResponseCard.fieldData
JSON and never touched the top-level columns the list view,
search, CSV export, and integrations read from. The PUT route
now promotes any fieldData keys that match canonical columns up
to those columns; the default template marks all canonical
fields as isCore so new orgs avoid the problem in the first
place.
Adds scripts/backfill-core-fields.ts (dry-run by default; pass
--apply to commit) to flip existing FormField rows to isCore = true
where the key matches a canonical column and to promote any
existing fieldData values into empty top-level columns + recompute
stale name values.
Co-authored-by: Cursor <cursoragent@cursor.com>
Centralizes role/permission enforcement so each role (owner, admin, editor,
reviewer, viewer) behaves consistently in the API and UI.
- Extend src/lib/permissions.ts with an expanded action map (cards.reprocess,
cards.assign, uploads.create, integrations.manage, etc.) plus helper
predicates (isAdminRole, canEditContent).
- Add requireApiAuthWithPermission(action) to src/lib/api-auth.ts with a
narrowed OrgSession return type and PermissionError -> 403 handling.
- Replace hand-rolled role checks in card, org, integration, form-template,
settings, upload, and location routes with the shared helpers so 403s are
uniform and derived from one permission map.
- Close the editor UI gap: the dashboard upload button, row-level mark
reviewed/reprocess/delete, and card detail edit/reprocess/export/assign
now flow from can(role, action) instead of ad-hoc isAdmin checks.
- Gate /settings/* at the middleware layer for non-admins and hide the
Settings entry in the sidebar and top-bar menu when the role cannot
access it.
- Use isAdminRole() in the team members settings page for consistency.
Made-with: Cursor
Phase 1 - Security & Bug Fixes:
- Add requireApiAuth helper and protect all 25 unprotected API routes
- Add org-tenant scoping to all card, job, stats, and notification queries
- Fix SSRF in ai-test, mask secrets in settings API, fix middleware bypass
- Fix cards pagination routing, stat filter sync, drag-drop file passing
- Add PUT /api/auth/me for profile persistence, stuck job recovery
- Fix email watcher MIME type detection
Phase 2 - Dynamic Fields & Digital Survey:
- Add FormTemplate, FormField, Person, PasswordResetToken models to schema
- Add fieldData, formTemplateId, firstName, lastName, personId to ResponseCard
- Build FormTemplate CRUD API with field management and org scoping
- Build Form Builder UI with field ordering, type config, and section management
- Refactor card detail page to render fields dynamically from templates
- Add dynamic OCR prompt/schema generation from template fields
- Build public survey page at /s/[orgSlug]/[formSlug] with branding
- Add QR code generation API and share section component
Phase 3 - People & Analytics:
- Build People CRUD API with merge and batch auto-link endpoints
- Build People list and detail pages with search, merge dialog
- Add auto-link logic in OCR completion to match/create Person records
- Add /api/stats/trends endpoint with time series and team activity
- Build Reports page with Recharts (area charts, bar charts, pipeline)
- Upgrade dashboard with sparklines and People stat card
Phase 4 - UX Polish:
- Replace silent error handling with toast notifications across all pages
- Add loading skeletons, differentiated empty states
- Add ARIA labels, skip-to-content link, accessible column toggle
- Add forgot password flow, Cmd+K command palette, Collection Days pages
- Unify Echo branding and theme toggle consistency
Made-with: Cursor
The org membership role is "owner" but UI components only checked for
"admin", hiding action buttons (reprocess, push, export, etc.) for
org owners. Update isAdmin checks in card detail page and dashboard,
plus the delete API route, to include "owner".
Made-with: Cursor
- Replace pdf2pic/GraphicsMagick with pdfjs-dist + @napi-rs/canvas for
Vercel-compatible PDF rasterization
- Replace MinIO with Supabase Storage (S3-compatible); rename minio.ts
to storage.ts and update all imports
- Replace in-memory job queue with Upstash QStash; upload route now
persists files to storage before enqueuing, /api/jobs/process handles
the QStash callback
- Convert email watcher from persistent IMAP connection to stateless
scanInbox() polled by Vercel Cron every 2 minutes
- Add FTP watcher (basic-ftp) with cron polling for scanner integration
via Dreamhost FTP drop directory
- Add FTP config fields to AppSettings schema
- Remove folder watcher (chokidar), standalone output, Docker-only code
- Update next.config.ts, middleware, instrumentation for serverless
- Add vercel.json with cron schedules for email and FTP polling
- Add migration scripts for database (pg_dump/restore) and storage
(S3-to-S3 copy) with verification
Made-with: Cursor
- Add User model synced from Authentik headers with admin/reviewer/viewer roles
- Add assignment fields (assignedToId, assignedById, reviewedById, etc.) to ResponseCard
- Add userId tracking to ActivityLog and Notification models
- Create auth.ts with getOrCreateUser() and role mapping from Authentik groups
- Create /api/users endpoint and /api/cards/assign batch assignment endpoint
- Gate card mutations behind role checks (viewers read-only, reviewers edit assigned only)
- Gate Monday.com push behind reviewStatus=reviewed instead of ocr_complete
- Add "My Cards" stat card, Assigned To filter, and assignment columns to table
- Add Assign button with user picker to batch selection toolbar (admin only)
- Update card detail: assignment banner, Mark Complete button, prev/next nav, reassign
- Make all field components accept readOnly prop for role-based editing
- Gate settings page behind admin role
- Add userId to stats API for per-user card counts
- Expose dbUser and role through UserProfileProvider context
Made-with: Cursor
- Add firstTimeGuestDate and salvationDate (DateTime?) to Prisma schema
- Auto-compute dates during OCR: find previous Sunday from card createdAt
when visitType indicates first/second time guest or nextStep includes
Baptism
- Add editable date inputs on card detail page in Workflow section
- Add to Monday.com mappable fields in settings for column mapping
- Add table columns (hidden by default) for both date fields
- Handle full ISO datetime strings in Monday.com date parser
- Apply same logic during reprocessing
Made-with: Cursor
- Add followUp, notes, serviceTime, planningCenter, iSaidYesBookSent,
ftGuestLetterSent fields to ResponseCard to match Monday.com board
- Expand column mapper from 8 fields to all 32 ResponseCard fields with
friendly labels matching the Easter Survey spreadsheet columns
- Add Workflow & Tracking section on card detail page for the new fields
- Fix pre-existing Buffer type error in images API route
Made-with: Cursor
The presigned URLs pointed to a private IP unreachable from the browser.
The image route now streams bytes directly and the card API returns
proxy paths matching the dashboard's existing approach.
Made-with: Cursor
- Bidirectional Monday.com integration with column mapping and file uploads
- Generic webhook system with HMAC-SHA256 signing and configurable events
- Field-level activity log with diff tracking on card detail page
- Persistent notification center with unread badge in header
- Event dispatcher wired into OCR pipeline, card edits, exports, and deletes
- New Prisma models: ActivityLog, Notification; new fields on AppSettings and ResponseCard
- Settings UI with full Monday.com and webhook configuration panels
Made-with: Cursor