- Middleware now redirects unauthenticated users to /welcome instead of /login
- Added /features and /pricing to public paths
- Created 8 dedicated feature pages: AI OCR, Scanner Integration, Integrations,
Team Collaboration, Collection Days, Multi-Site, Analytics, Security
- Created features index page at /features with links to all detail pages
- Created standalone pricing page at /pricing with comparison table
- Extracted shared MarketingNav (with features dropdown) and MarketingFooter
- Created FeaturePageShell for consistent feature page layout
- Updated landing page to use shared components and link to feature pages
Made-with: Cursor
- Open registration to all users (remove invite-only gate)
- Add domain auto-join: new users matching org allowedDomains get auto-added
- Track active workspace via activeOrgId on User model
- Refactor JWT callback to resolve active org from all memberships
- Add org switch, list, and create-personal API endpoints
- Add workspace-setup page for users without an org
- Build OrgSwitcher dropdown in sidebar header
- Add allowed email domains management to org settings
Made-with: Cursor
- Swap nodemailer SMTP transport for @getbrevo/brevo SDK, which uses
HTTP with built-in retries (better for Vercel serverless)
- Add sendInvitationEmail function and wire it into the org invitations
POST route so new invites trigger an email automatically
- Update .env.example with BREVO_API_KEY, EMAIL_FROM_NAME,
EMAIL_FROM_ADDRESS replacing the old SMTP_* vars
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
- Create /api/org (GET details, PUT update)
- Create /api/org/members (GET list)
- Create /api/org/invitations (GET list, POST create)
- Create /api/org/locations (GET list with collection days, POST create)
- Fix registration: check org count instead of user count so
a fresh domain setup works even if orphaned users exist
from a prior AUTH_SECRET rotation
Made-with: Cursor
- Middleware now redirects to /onboarding when user has no org
(previously required orgRole=owner which was undefined for new users)
- Refresh JWT cookie after onboarding completes so middleware sees
updated orgId/onboardingComplete on next navigation
- Integrations page shows meaningful error instead of empty grid
when API returns 401 due to missing org
Made-with: Cursor
Next.js requires useSearchParams() to be inside a Suspense boundary
for static page generation. Wraps the new integration page component.
Made-with: Cursor
- Auto-sign-in after registration instead of redirect to login
- Email verification system with token generation, send/confirm API routes, and persistent banner
- 7-step onboarding wizard (org, location, services, upload source, AI, integrations, complete)
- Middleware redirects owners with incomplete onboarding to /onboarding
- Integration provider plugin architecture with registry and 6 providers (Planning Center, Monday.com, Airtable, Google Sheets, Webhook, CSV Export)
- Full integration CRUD API with test, sync, fields, and OAuth authorize/callback routes
- Refactored fireIntegrationEvent to use Integration model with legacy AppSettings fallback
- Migration script for existing Monday.com/webhook config to Integration rows
- Settings page restructured from monolithic 1290-line file into focused sub-routes with section navigation
- Integration hub UI with provider tiles, connect flow, and individual config pages
- Post-onboarding contextual guidance cards on dashboard with dismissible hints
- Schema: Integration model, onboardingComplete/onboardingStep on Organization, dismissedHints on OrgMember
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 scanInbox function that opens a separate IMAP connection and
processes all unseen messages in the configured folder
- Wire to email-watch API as action "scan"
- Add Scan Inbox button to settings page next to Start/Stop Monitoring
- Useful for processing emails that arrived before monitoring started,
since IMAP IDLE only notifies about new messages
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
- Sync-all endpoint now supports mode param: "push" (new only), "update"
(existing only), or "all" (both); also accepts cardIds for batch ops
- Response includes separate created/updated/failed counts
- Settings button renamed to "Sync All to Monday.com" and shows breakdown
- Add "Monday.com" button to the floating selection toolbar for syncing
selected cards in batch
Made-with: Cursor
- Change cell copy from click-to-copy to a small hover button so clicking
a table row navigates to the card detail page again
- Add fullscreen lightbox popup on card detail page images with Escape to
close and click-outside dismiss
- Further strengthen nextStep OCR prompts to emphasize inspecting each
checkbox square independently and note most respondents check only one
Made-with: Cursor
- Replace read-only badge chips with toggleable multi-select chips for
messageTopics, nextStep, campusPreference, and howHeard on card detail page
- Add editable Switch toggles for all boolean fields (prayerForTeam,
prayerConfidential, iSaidYesBookSent, ftGuestLetterSent)
- Change edits state to Record<string, unknown> to support array and
boolean values alongside strings
- Update OCR prompts to normalize visitType variations (e.g., "I am a
first or second time guest at Echo Life" -> "First/Second Time Guest")
- Update message topics list in OCR schema to match actual survey form
options (Stress/Anxiety, Hearing God's Voice, Dealing With Doubt, etc.)
Made-with: Cursor
Card-level reprocessing only re-OCRs existing images. When the old
page count bug caused survey pages to never be extracted, those images
don't exist to reprocess.
New /api/cards/recover-survey endpoint:
- Finds all cards missing frontImagePath (survey side)
- Locates their source PDFs in MinIO via the ProcessingJob
- Re-extracts the correct survey page for each card
- Stores the image and runs OCR to fill in survey data
- Processes sequentially with rate-limit delays
Settings page shows amber banner when missing backs are detected
with a one-click "Recover Missing Backs" button.
Made-with: Cursor
Rate limiting:
- Retry with exponential backoff (5s, 10s, 20s, 40s) on rate limit errors
- Reduce concurrent OCR jobs from 2 to 1
- Add 2s delay between AI calls within a card (response vs survey side)
- Add 3s delay between cards in a multi-page PDF
- Add 3s delay between jobs in the processing queue
Batch reprocess:
- New /api/cards/reprocess-batch endpoint processes cards sequentially
with delays to respect rate limits
- Reprocess button added to selection toolbar on dashboard
Made-with: Cursor
The root cause was sending raw text to checkbox, status, dropdown, and
date columns. Monday.com requires specific JSON structures per type.
- Store column types in _columnTypes when Fetch Columns is clicked
- Format boolean/checkbox: {"checked": "true"/"false"}
- Format status/color: {"label": "value"}
- Format dropdown: {"labels": ["val1", "val2"]}
- Format date: parse MM/DD/YYYY to {"date": "YYYY-MM-DD"}
- Format email/phone/link with proper nested structures
- Text columns pass through as plain strings
Made-with: Cursor
- Fix toast showing success when all cards fail (now shows error/warning)
- Fix column value formatting: booleans use checkbox format, JSON arrays
join as comma-separated strings, objects serialize properly
- Filter out empty/null/undefined string values before sending to API
- Add verbose logging to Monday.com GraphQL client for debugging
Made-with: Cursor
- Processing queue limits OCR to 2 concurrent jobs across all sources (upload, email, folder watcher)
- 50 MB file size cap enforced on uploads, email attachments, and folder watcher
- Monday.com bulk sync now processes in batches of 5 with 1s delay between batches
Made-with: Cursor
- Extract reusable pushCardToMonday() that creates or updates Monday items
- New /api/integrations/monday/push/[id] endpoint for single-card push
- New /api/integrations/monday/sync-all endpoint for bulk push of all
unsynced cards (ocrStatus=complete, no mondayItemId)
- Add "Push to Monday.com" button on card detail page header (shows
"Update Monday" when card already has a Monday item)
- Add "Push All to Monday.com" button in Monday.com settings card
Made-with: Cursor
Duplex scanners flip the sheet between sides, so odd pages (back) are
180° rotated relative to even pages (front). Landscape back pages now
rotate CCW instead of CW, and portrait back pages get a 180° flip.
Also swapped "Front"/"Back" labels to match the physical card sides.
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
Watches an email inbox via IMAP IDLE for incoming scanned cards,
extracts PDF/image attachments, and feeds them through the existing
OCR pipeline. Configurable via the Settings page with test connection
support.
Made-with: Cursor
The Reprocess button and dropdown item were only visible for cards
with ocrStatus "error". Now they appear for any card that isn't
currently processing, so users can re-run OCR on completed cards too
(e.g. after changing AI settings).
Made-with: Cursor
- Profile dropdown in header shows user name/email/avatar with links
to profile, settings, support, theme submenu, and sign out
- New /profile page with editable user info (job title, company, bio)
and read-only SSO fields when behind Authentik forward-auth
- /api/auth/me endpoint reads X-authentik-* headers and enriches
with avatar from Authentik API
- Settings page gains Preferences tab with appearance theme picker
and notification toggle switches
- User profile context backed by localStorage, merges with Authentik
data when available
Made-with: Cursor
- Switch font from Geist to Google Quicksand
- Replace indigo/violet primary with charcoal gray (rgb 31,32,29)
- Update gradient-mesh to warm neutral tones (amber/sage)
- Remove sidebar, add fixed top header bar with profile dropdown
- Merge dashboard and upload pages into single table-centric view
- Stat cards now act as clickable table filters with active highlight
- Simplified filter toolbar: search + dropdowns left, export + upload right
- Upload modal with drag-drop, per-file XHR progress, background uploads
- Global drag-and-drop listener opens upload modal from anywhere
- Table rows are clickable (navigate to detail page)
- Right-click column headers for visibility context menu
- Mobile-responsive card layout below md breakpoint
- Uploading row placeholders with animated progress bars
- Floating selection toolbar slides in at bottom on multi-select
- Delete sidebar, dashboard-hero, quick-actions, upload page
Made-with: Cursor
Replace individual provider SDKs (@ai-sdk/openai, @ai-sdk/google,
@ai-sdk/anthropic) with the Vercel AI Gateway. Cloud models are now
accessed via a single AI_GATEWAY_API_KEY with provider/model strings
(e.g. openai/gpt-4o-mini, google/gemini-2.5-flash). Ollama remains
available as a local fallback via @ai-sdk/openai-compatible.
Made-with: Cursor
Replace the raw Ollama fetch-based OCR with the Vercel AI SDK,
adding support for OpenAI, Google Gemini, Anthropic, and Ollama
as selectable providers from the Settings page. Uses generateText
with Output.object() and Zod schemas for type-safe structured
data extraction.
Made-with: Cursor
Tests S3 bucket access and falls back to a raw HTTP fetch to show
what the endpoint actually returns (helps identify port misconfiguration).
Made-with: Cursor
- Add start.sh that runs prisma db push before starting the server,
creating tables automatically on first deploy
- Copy prisma CLI + engines into production image for runtime migrations
- Add configurable storage cleanup: source PDF retention (default 30d)
and image retention (default 180d) with manual trigger in Settings
- Add /api/cleanup endpoint (GET for status, POST to run)
- Add retention settings to AppSettings schema
- Add ListObjectsV2 helper to minio.ts for bulk cleanup
Tested: local Docker build passes
Made-with: Cursor