- Rewrite parseToISO to handle many OCR date formats: "july 20, 02",
"20/May/1999", "08/JUN/26", "Oct / 02 / 2004", "Feb 19, 2016", etc.
- Return null instead of raw string when date parsing fails, preventing
Monday.com ColumnValueException for unparseable dates
- Normalize long nextStep text from pre-update OCR runs (e.g.
"Expressing my faith in Jesus..." -> "Baptism") before sending to
Monday.com dropdowns
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
Strengthened system prompts and Zod schema descriptions in both ai-ocr.ts
and ollama.ts with explicit checkbox rules: only mark as checked when a
visible X/checkmark/fill is inside the box, treat empty boxes as unchecked,
and err on the side of unchecked when uncertain. Clarified the ambiguous
nextStep field to distinguish the two checkbox options from the section
heading.
Made-with: Cursor
- Expand ResponseCard type to include all 30+ Prisma fields
- Add 18 new toggleable columns (Personal, Survey, Prayer, Workflow groups)
- ClickUp-style Copy popover in selection toolbar with field toggle switches
- Click-to-copy on individual cells with visual feedback
- Ctrl/Cmd+click column headers to copy column values
- Columns dropdown in filter bar for column visibility management
- Sticky select/name columns when scrolling horizontally
- Keyboard shortcuts: Ctrl+C to copy, Escape to clear selection
- Grouped context menu for column visibility (Core, Personal, Survey, Prayer, Workflow)
Made-with: Cursor
The 3GB Coolify server OOMs during Next.js build when node uses 1.5GB
because Docker overhead + OS leaves less than 2GB available. Also
clean npm cache after install to free ~100MB in the deps layer.
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
The old getPdfPageCount used a fragile regex on raw PDF binary that
could undercount pages, causing survey sides to be skipped entirely.
Replaced with pdf-lib's PDFDocument.load() for reliable counting,
with the old regex as a fallback.
Added logging for page counts and pairing to diagnose missing backs.
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
Monday.com API returns column types as "checkbox" (not "boolean"),
"status" (not "color"), and "numbers" (not "numeric"). Added these
as aliases in formatForColumnType so values are properly formatted.
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
Added instrumentation.ts that reads persisted watcher state from the
database and restarts active watchers when the Next.js server boots.
Also added imapflow/mailparser to serverExternalPackages.
Made-with: Cursor
Scanners store landscape content with a PDF /Rotate flag that pdf2pic
doesn't always apply. Now uses a square bounding box with
preserveAspectRatio, then detects landscape output and rotates to
portrait with sharp.
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
Replace DropdownMenuSub/DropdownMenuGroup with flat menu items
to avoid base-ui runtime errors. Theme now cycles with a single
click (Light → Dark → System).
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
- Add @ai-sdk/gateway as direct dependency (was only a transitive dep
of ai, fragile in Docker builds with --ignore-scripts)
- Avoid double prisma generate in Dockerfile by calling next build
directly instead of npm run build
- Set NODE_OPTIONS max-old-space-size=1536 for the builder stage to
prevent OOM on the 3GB Coolify build server
Made-with: Cursor
Cards with OCR error status now show a "Reprocess" option in the
row actions dropdown menu on the dashboard, triggering the existing
reprocess API endpoint.
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
Pass null instead of "__all__" as the Select value when no filter is
active, so the placeholder text (Visit Type, Attendance, Service) is
displayed by the base-ui SelectValue component.
Made-with: Cursor
Replace base-ui DropdownMenu (requires anchor trigger) with a custom
positioned panel for the header right-click context menu. The base-ui
Menu primitive cannot be rendered free-floating without a trigger element.
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
Use a separate Docker stage to install prisma CLI + all transitive
deps, then merge into the runner's node_modules with correct ownership.
Verified: prisma --version works inside the container.
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
The postinstall script runs prisma generate, but the prisma schema
files aren't available in the deps stage (only package.json is copied).
The builder stage already runs prisma generate after copying all files.
Made-with: Cursor
Alpine's apk package manager fails in Coolify's build environment due to
network permission issues. Debian slim's apt-get is more reliable in
containerized CI/CD pipelines.
Made-with: Cursor