2026-03-10 08:17:45 -04:00
|
|
|
import { NextRequest, NextResponse } from "next/server";
|
|
|
|
|
import { prisma } from "@/lib/db";
|
2026-04-16 00:51:44 -04:00
|
|
|
import { deleteObject } from "@/lib/storage";
|
2026-04-07 13:23:29 -04:00
|
|
|
import { fireIntegrationEvent } from "@/lib/integrations";
|
|
|
|
|
import { logActivity, diffCardFields } from "@/lib/activity-log";
|
Add dynamic fields, people directory, analytics, security hardening, and UX polish
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
2026-04-17 00:29:26 -04:00
|
|
|
import { requireApiAuthWithOrg, handleApiError } from "@/lib/api-auth";
|
Normalize role-based permissions across API and UI
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
2026-04-23 13:17:35 -04:00
|
|
|
import { can } from "@/lib/permissions";
|
2026-03-10 08:17:45 -04:00
|
|
|
|
|
|
|
|
export async function GET(
|
|
|
|
|
_request: NextRequest,
|
|
|
|
|
{ params }: { params: Promise<{ id: string }> }
|
|
|
|
|
) {
|
|
|
|
|
try {
|
Add dynamic fields, people directory, analytics, security hardening, and UX polish
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
2026-04-17 00:29:26 -04:00
|
|
|
const session = await requireApiAuthWithOrg();
|
2026-03-10 08:17:45 -04:00
|
|
|
const { id } = await params;
|
|
|
|
|
const card = await prisma.responseCard.findUnique({
|
|
|
|
|
where: { id },
|
Add dynamic fields, people directory, analytics, security hardening, and UX polish
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
2026-04-17 00:29:26 -04:00
|
|
|
include: {
|
|
|
|
|
formTemplate: {
|
|
|
|
|
include: { fields: { orderBy: { sortOrder: "asc" } } },
|
|
|
|
|
},
|
|
|
|
|
},
|
2026-03-10 08:17:45 -04:00
|
|
|
});
|
|
|
|
|
|
Add dynamic fields, people directory, analytics, security hardening, and UX polish
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
2026-04-17 00:29:26 -04:00
|
|
|
if (!card || card.organizationId !== session.user.orgId) {
|
2026-03-10 08:17:45 -04:00
|
|
|
return NextResponse.json({ error: "Card not found" }, { status: 404 });
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-07 13:38:08 -04:00
|
|
|
const frontImageUrl = card.frontImagePath
|
|
|
|
|
? `/api/images/${card.frontImagePath}`
|
|
|
|
|
: null;
|
|
|
|
|
const backImageUrl = card.backImagePath
|
|
|
|
|
? `/api/images/${card.backImagePath}`
|
|
|
|
|
: null;
|
2026-03-10 08:17:45 -04:00
|
|
|
|
|
|
|
|
return NextResponse.json({
|
|
|
|
|
...card,
|
|
|
|
|
frontImageUrl,
|
|
|
|
|
backImageUrl,
|
|
|
|
|
});
|
|
|
|
|
} catch (error) {
|
Add dynamic fields, people directory, analytics, security hardening, and UX polish
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
2026-04-17 00:29:26 -04:00
|
|
|
return handleApiError(error);
|
2026-03-10 08:17:45 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function PUT(
|
|
|
|
|
request: NextRequest,
|
|
|
|
|
{ params }: { params: Promise<{ id: string }> }
|
|
|
|
|
) {
|
|
|
|
|
try {
|
Add dynamic fields, people directory, analytics, security hardening, and UX polish
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
2026-04-17 00:29:26 -04:00
|
|
|
const session = await requireApiAuthWithOrg();
|
|
|
|
|
const user = session.user;
|
2026-04-11 01:00:11 -04:00
|
|
|
|
2026-03-10 08:17:45 -04:00
|
|
|
const { id } = await params;
|
|
|
|
|
const card = await prisma.responseCard.findUnique({
|
|
|
|
|
where: { id },
|
|
|
|
|
});
|
|
|
|
|
|
Add dynamic fields, people directory, analytics, security hardening, and UX polish
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
2026-04-17 00:29:26 -04:00
|
|
|
if (!card || card.organizationId !== session.user.orgId) {
|
2026-03-10 08:17:45 -04:00
|
|
|
return NextResponse.json({ error: "Card not found" }, { status: 404 });
|
|
|
|
|
}
|
|
|
|
|
|
Normalize role-based permissions across API and UI
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
2026-04-23 13:17:35 -04:00
|
|
|
// Editors and above can edit any card; reviewers can edit cards assigned
|
|
|
|
|
// to them; viewers cannot edit at all.
|
|
|
|
|
const canEditAnyCard = can(user.role, "cards.edit");
|
|
|
|
|
const canReviewAssigned =
|
|
|
|
|
can(user.role, "cards.review") && card.assignedToId === user.id;
|
|
|
|
|
if (!canEditAnyCard && !canReviewAssigned) {
|
|
|
|
|
return NextResponse.json(
|
|
|
|
|
{
|
|
|
|
|
error: canEditAnyCard === false && user.role === "reviewer"
|
|
|
|
|
? "You can only edit cards assigned to you"
|
|
|
|
|
: "You don't have permission to edit cards",
|
|
|
|
|
},
|
|
|
|
|
{ status: 403 }
|
|
|
|
|
);
|
2026-04-11 01:00:11 -04:00
|
|
|
}
|
|
|
|
|
|
2026-03-10 08:17:45 -04:00
|
|
|
const body = await request.json().catch(() => ({}));
|
|
|
|
|
const data: Record<string, unknown> = {};
|
|
|
|
|
|
|
|
|
|
const stringFields = [
|
|
|
|
|
"name",
|
Add dynamic fields, people directory, analytics, security hardening, and UX polish
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
2026-04-17 00:29:26 -04:00
|
|
|
"firstName",
|
|
|
|
|
"lastName",
|
2026-03-10 08:17:45 -04:00
|
|
|
"gender",
|
|
|
|
|
"dateOfBirth",
|
|
|
|
|
"maritalStatus",
|
|
|
|
|
"maritalStatusOther",
|
|
|
|
|
"visitType",
|
|
|
|
|
"cellPhone",
|
|
|
|
|
"homePhone",
|
|
|
|
|
"email",
|
|
|
|
|
"address",
|
|
|
|
|
"aptNumber",
|
|
|
|
|
"city",
|
|
|
|
|
"state",
|
|
|
|
|
"zip",
|
|
|
|
|
"prayerRequests",
|
|
|
|
|
"messageTopicsOther",
|
|
|
|
|
"attendanceDuration",
|
|
|
|
|
"campusPreferenceOther",
|
|
|
|
|
"howHeardOther",
|
|
|
|
|
"serviceAttended",
|
2026-04-07 13:50:03 -04:00
|
|
|
"followUp",
|
|
|
|
|
"notes",
|
|
|
|
|
"serviceTime",
|
|
|
|
|
"planningCenter",
|
2026-03-10 08:17:45 -04:00
|
|
|
"ocrStatus",
|
|
|
|
|
"reviewStatus",
|
|
|
|
|
"ocrError",
|
2026-04-11 01:00:11 -04:00
|
|
|
"reviewNotes",
|
2026-03-10 08:17:45 -04:00
|
|
|
];
|
|
|
|
|
for (const field of stringFields) {
|
|
|
|
|
if (body[field] != null) data[field] = String(body[field]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (body.prayerForTeam != null) data.prayerForTeam = Boolean(body.prayerForTeam);
|
|
|
|
|
if (body.prayerConfidential != null) data.prayerConfidential = Boolean(body.prayerConfidential);
|
2026-04-07 13:50:03 -04:00
|
|
|
if (body.iSaidYesBookSent != null) data.iSaidYesBookSent = Boolean(body.iSaidYesBookSent);
|
|
|
|
|
if (body.ftGuestLetterSent != null) data.ftGuestLetterSent = Boolean(body.ftGuestLetterSent);
|
2026-04-09 18:20:24 -04:00
|
|
|
|
2026-04-11 01:00:11 -04:00
|
|
|
for (const dateField of ["firstTimeGuestDate", "salvationDate", "assignedAt", "reviewedAt"] as const) {
|
2026-04-09 18:20:24 -04:00
|
|
|
if (body[dateField] !== undefined) {
|
|
|
|
|
data[dateField] = body[dateField] ? new Date(body[dateField]) : null;
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-03-10 08:17:45 -04:00
|
|
|
if (body.ocrConfidence != null) data.ocrConfidence = Number(body.ocrConfidence);
|
|
|
|
|
if (body.messageTopics != null) data.messageTopics = body.messageTopics;
|
|
|
|
|
if (body.nextStep != null) data.nextStep = body.nextStep;
|
|
|
|
|
if (body.campusPreference != null) data.campusPreference = body.campusPreference;
|
|
|
|
|
if (body.howHeard != null) data.howHeard = body.howHeard;
|
|
|
|
|
if (body.rawOcrResponse != null) data.rawOcrResponse = body.rawOcrResponse;
|
Add dynamic fields, people directory, analytics, security hardening, and UX polish
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
2026-04-17 00:29:26 -04:00
|
|
|
if (body.fieldData !== undefined) data.fieldData = body.fieldData;
|
2026-03-10 08:17:45 -04:00
|
|
|
|
2026-05-21 09:30:27 -04:00
|
|
|
// Promote any fieldData keys that match canonical top-level ResponseCard
|
|
|
|
|
// columns up to those columns. Form templates may mark contact/address
|
|
|
|
|
// fields as non-core, which means user edits land in `fieldData` only —
|
|
|
|
|
// promotion keeps the top-level columns (used by the list view, search,
|
|
|
|
|
// integrations, CSV export) in sync. Explicit body fields always win.
|
|
|
|
|
if (body.fieldData && typeof body.fieldData === "object") {
|
|
|
|
|
const fd = body.fieldData as Record<string, unknown>;
|
|
|
|
|
const PROMOTABLE_STRING = new Set([
|
|
|
|
|
"firstName", "lastName", "name",
|
|
|
|
|
"gender", "dateOfBirth",
|
|
|
|
|
"maritalStatus", "maritalStatusOther", "visitType",
|
|
|
|
|
"cellPhone", "homePhone", "email",
|
|
|
|
|
"address", "aptNumber", "city", "state", "zip",
|
|
|
|
|
"prayerRequests", "messageTopicsOther", "attendanceDuration",
|
|
|
|
|
"campusPreferenceOther", "howHeardOther", "serviceAttended",
|
|
|
|
|
"followUp", "notes", "serviceTime", "planningCenter",
|
|
|
|
|
]);
|
|
|
|
|
const PROMOTABLE_BOOL = new Set([
|
|
|
|
|
"prayerForTeam", "prayerConfidential",
|
|
|
|
|
"iSaidYesBookSent", "ftGuestLetterSent",
|
|
|
|
|
]);
|
|
|
|
|
const PROMOTABLE_ARRAY = new Set([
|
|
|
|
|
"messageTopics", "nextStep", "campusPreference", "howHeard",
|
|
|
|
|
]);
|
|
|
|
|
const PROMOTABLE_DATE = new Set([
|
|
|
|
|
"firstTimeGuestDate", "salvationDate",
|
|
|
|
|
]);
|
|
|
|
|
for (const [k, v] of Object.entries(fd)) {
|
|
|
|
|
if (k in data) continue;
|
|
|
|
|
if (PROMOTABLE_STRING.has(k)) {
|
|
|
|
|
data[k] = v == null || v === "" ? null : String(v);
|
|
|
|
|
} else if (PROMOTABLE_BOOL.has(k)) {
|
|
|
|
|
data[k] = Boolean(v);
|
|
|
|
|
} else if (PROMOTABLE_ARRAY.has(k)) {
|
|
|
|
|
data[k] = Array.isArray(v) ? v : null;
|
|
|
|
|
} else if (PROMOTABLE_DATE.has(k)) {
|
|
|
|
|
data[k] = v ? new Date(String(v)) : null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Keep the denormalized `name` field in sync with firstName/lastName.
|
|
|
|
|
// `name` is read by the list view, search, sort, integrations, and CSV
|
|
|
|
|
// export, so whenever first/last changes (and the caller didn't already
|
|
|
|
|
// pass an explicit `name`), recompute it.
|
|
|
|
|
if (("firstName" in data || "lastName" in data) && !("name" in data)) {
|
|
|
|
|
const nextFirst =
|
|
|
|
|
"firstName" in data ? (data.firstName as string | null) : card.firstName;
|
|
|
|
|
const nextLast =
|
|
|
|
|
"lastName" in data ? (data.lastName as string | null) : card.lastName;
|
|
|
|
|
const combined = [nextFirst, nextLast].filter(Boolean).join(" ").trim();
|
|
|
|
|
data.name = combined || null;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-11 01:00:11 -04:00
|
|
|
for (const assignField of ["assignedToId", "assignedById", "reviewedById"] as const) {
|
|
|
|
|
if (body[assignField] !== undefined) {
|
|
|
|
|
data[assignField] = body[assignField] || null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (body.reviewStatus === "in_review" && card.reviewStatus === "assigned") {
|
|
|
|
|
data.reviewStatus = "in_review";
|
|
|
|
|
}
|
|
|
|
|
|
Add dynamic fields, people directory, analytics, security hardening, and UX polish
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
2026-04-17 00:29:26 -04:00
|
|
|
if (body.reviewStatus === "reviewed") {
|
2026-04-11 01:00:11 -04:00
|
|
|
data.reviewedById = user.id;
|
|
|
|
|
data.reviewedAt = new Date();
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-07 13:23:29 -04:00
|
|
|
const oldCard = card as unknown as Record<string, unknown>;
|
|
|
|
|
|
2026-03-10 08:17:45 -04:00
|
|
|
const updated = await prisma.responseCard.update({
|
|
|
|
|
where: { id },
|
|
|
|
|
data: data as Parameters<typeof prisma.responseCard.update>[0]["data"],
|
|
|
|
|
});
|
|
|
|
|
|
2026-04-07 13:23:29 -04:00
|
|
|
const newCard = updated as unknown as Record<string, unknown>;
|
|
|
|
|
const changes = diffCardFields(oldCard, newCard);
|
|
|
|
|
if (changes.length > 0) {
|
2026-04-11 01:00:11 -04:00
|
|
|
logActivity(
|
|
|
|
|
id,
|
|
|
|
|
"manual_edit",
|
|
|
|
|
"user",
|
|
|
|
|
`${changes.length} field(s) updated manually`,
|
|
|
|
|
changes,
|
Add dynamic fields, people directory, analytics, security hardening, and UX polish
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
2026-04-17 00:29:26 -04:00
|
|
|
user.id
|
2026-04-11 01:00:11 -04:00
|
|
|
).catch(() => {});
|
2026-04-07 13:23:29 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const oldStatus = card.reviewStatus;
|
|
|
|
|
const newStatus = updated.reviewStatus;
|
|
|
|
|
if (oldStatus !== newStatus) {
|
|
|
|
|
if (newStatus === "reviewed") {
|
|
|
|
|
fireIntegrationEvent("card_reviewed", id, { oldCard }).catch(() => {});
|
|
|
|
|
} else if (newStatus === "exported") {
|
|
|
|
|
fireIntegrationEvent("card_exported", id, { oldCard }).catch(() => {});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-10 08:17:45 -04:00
|
|
|
return NextResponse.json(updated);
|
|
|
|
|
} catch (error) {
|
Add dynamic fields, people directory, analytics, security hardening, and UX polish
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
2026-04-17 00:29:26 -04:00
|
|
|
return handleApiError(error);
|
2026-03-10 08:17:45 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function DELETE(
|
2026-04-11 01:00:11 -04:00
|
|
|
request: NextRequest,
|
2026-03-10 08:17:45 -04:00
|
|
|
{ params }: { params: Promise<{ id: string }> }
|
|
|
|
|
) {
|
|
|
|
|
try {
|
Normalize role-based permissions across API and UI
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
2026-04-23 13:17:35 -04:00
|
|
|
const session = await requireApiAuthWithOrg("cards.delete");
|
2026-04-11 01:00:11 -04:00
|
|
|
|
2026-03-10 08:17:45 -04:00
|
|
|
const { id } = await params;
|
|
|
|
|
const card = await prisma.responseCard.findUnique({
|
|
|
|
|
where: { id },
|
|
|
|
|
});
|
|
|
|
|
|
Add dynamic fields, people directory, analytics, security hardening, and UX polish
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
2026-04-17 00:29:26 -04:00
|
|
|
if (!card || card.organizationId !== session.user.orgId) {
|
2026-03-10 08:17:45 -04:00
|
|
|
return NextResponse.json({ error: "Card not found" }, { status: 404 });
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-07 13:23:29 -04:00
|
|
|
fireIntegrationEvent("card_deleted", id).catch(() => {});
|
|
|
|
|
|
2026-03-10 08:17:45 -04:00
|
|
|
const deletePromises: Promise<void>[] = [];
|
|
|
|
|
if (card.frontImagePath) deletePromises.push(deleteObject(card.frontImagePath));
|
|
|
|
|
if (card.backImagePath) deletePromises.push(deleteObject(card.backImagePath));
|
|
|
|
|
await Promise.allSettled(deletePromises);
|
|
|
|
|
|
|
|
|
|
await prisma.responseCard.delete({
|
|
|
|
|
where: { id },
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return NextResponse.json({ success: true });
|
|
|
|
|
} catch (error) {
|
Add dynamic fields, people directory, analytics, security hardening, and UX polish
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
2026-04-17 00:29:26 -04:00
|
|
|
return handleApiError(error);
|
2026-03-10 08:17:45 -04:00
|
|
|
}
|
|
|
|
|
}
|