Add Monday.com board fields and expand column mapper
- 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
This commit is contained in:
parent
58f38d92ed
commit
d58285785e
5 changed files with 102 additions and 5 deletions
|
|
@ -42,6 +42,14 @@ model ResponseCard {
|
|||
howHeardOther String?
|
||||
serviceAttended String?
|
||||
|
||||
// Monday.com / Workflow fields
|
||||
followUp String?
|
||||
notes String?
|
||||
serviceTime String?
|
||||
planningCenter String?
|
||||
iSaidYesBookSent Boolean @default(false)
|
||||
ftGuestLetterSent Boolean @default(false)
|
||||
|
||||
// Meta
|
||||
sourceFile String?
|
||||
frontImagePath String?
|
||||
|
|
|
|||
|
|
@ -77,6 +77,10 @@ export async function PUT(
|
|||
"campusPreferenceOther",
|
||||
"howHeardOther",
|
||||
"serviceAttended",
|
||||
"followUp",
|
||||
"notes",
|
||||
"serviceTime",
|
||||
"planningCenter",
|
||||
"ocrStatus",
|
||||
"reviewStatus",
|
||||
"ocrError",
|
||||
|
|
@ -87,6 +91,8 @@ export async function PUT(
|
|||
|
||||
if (body.prayerForTeam != null) data.prayerForTeam = Boolean(body.prayerForTeam);
|
||||
if (body.prayerConfidential != null) data.prayerConfidential = Boolean(body.prayerConfidential);
|
||||
if (body.iSaidYesBookSent != null) data.iSaidYesBookSent = Boolean(body.iSaidYesBookSent);
|
||||
if (body.ftGuestLetterSent != null) data.ftGuestLetterSent = Boolean(body.ftGuestLetterSent);
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ export async function GET(
|
|||
|
||||
const buffer = await getBuffer(key);
|
||||
|
||||
return new NextResponse(buffer, {
|
||||
return new NextResponse(new Uint8Array(buffer), {
|
||||
headers: {
|
||||
"Content-Type": contentType,
|
||||
"Cache-Control": "private, max-age=3600",
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import {
|
|||
ScanLine,
|
||||
User,
|
||||
ClipboardList,
|
||||
ClipboardCheck,
|
||||
Code,
|
||||
RefreshCw,
|
||||
Loader2,
|
||||
|
|
@ -71,6 +72,12 @@ type CardData = {
|
|||
howHeard: string[] | null;
|
||||
howHeardOther: string | null;
|
||||
serviceAttended: string | null;
|
||||
followUp: string | null;
|
||||
notes: string | null;
|
||||
serviceTime: string | null;
|
||||
planningCenter: string | null;
|
||||
iSaidYesBookSent: boolean;
|
||||
ftGuestLetterSent: boolean;
|
||||
ocrStatus: string;
|
||||
reviewStatus: string;
|
||||
ocrConfidence: number | null;
|
||||
|
|
@ -434,6 +441,47 @@ export default function CardDetailPage() {
|
|||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card variant="glass" className="xl:col-span-2">
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center gap-2 text-base">
|
||||
<ClipboardCheck className="size-4 text-primary" />
|
||||
Workflow & Tracking
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="grid gap-4 grid-cols-1 sm:grid-cols-2 lg:grid-cols-3">
|
||||
<Field label="Follow-Up" value={getValue("followUp")} onChange={(v) => setField("followUp", v)} />
|
||||
<Field label="Service Time" value={getValue("serviceTime")} onChange={(v) => setField("serviceTime", v)} />
|
||||
<Field label="Planning Center" value={getValue("planningCenter")} onChange={(v) => setField("planningCenter", v)} />
|
||||
<div>
|
||||
<Label className="mb-1.5 block text-xs font-medium text-muted-foreground">I Said Yes Book Sent</Label>
|
||||
<Badge variant={card.iSaidYesBookSent ? "default" : "secondary"}>
|
||||
{card.iSaidYesBookSent ? "Yes" : "No"}
|
||||
</Badge>
|
||||
</div>
|
||||
<div>
|
||||
<Label className="mb-1.5 block text-xs font-medium text-muted-foreground">FT Guest Letter Sent</Label>
|
||||
<Badge variant={card.ftGuestLetterSent ? "default" : "secondary"}>
|
||||
{card.ftGuestLetterSent ? "Yes" : "No"}
|
||||
</Badge>
|
||||
</div>
|
||||
</div>
|
||||
{getValue("notes") && (
|
||||
<>
|
||||
<Separator className="my-4 opacity-50" />
|
||||
<div>
|
||||
<Label className="mb-1.5 block text-xs font-medium text-muted-foreground">Notes</Label>
|
||||
<Textarea
|
||||
value={getValue("notes")}
|
||||
onChange={(e) => setField("notes", e.target.value)}
|
||||
rows={3}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{card.rawOcrResponse && (
|
||||
<Card variant="glass" className="xl:col-span-2">
|
||||
<CardHeader>
|
||||
|
|
|
|||
|
|
@ -58,6 +58,41 @@ import { Checkbox } from "@/components/ui/checkbox";
|
|||
|
||||
type MondayColumn = { id: string; title: string; type: string };
|
||||
|
||||
const MONDAY_MAPPABLE_FIELDS = [
|
||||
{ field: "name", label: "Name" },
|
||||
{ field: "gender", label: "Gender" },
|
||||
{ field: "dateOfBirth", label: "Date of Birth" },
|
||||
{ field: "maritalStatus", label: "Marital Status" },
|
||||
{ field: "visitType", label: "Visit Type" },
|
||||
{ field: "followUp", label: "Follow-Up" },
|
||||
{ field: "cellPhone", label: "Cell Phone" },
|
||||
{ field: "homePhone", label: "Home Phone" },
|
||||
{ field: "email", label: "Email" },
|
||||
{ field: "address", label: "Address" },
|
||||
{ field: "aptNumber", label: "Apt #" },
|
||||
{ field: "city", label: "City" },
|
||||
{ field: "state", label: "State" },
|
||||
{ field: "zip", label: "Zip" },
|
||||
{ field: "prayerRequests", label: "Prayer Requests" },
|
||||
{ field: "prayerForTeam", label: "For Prayer Team" },
|
||||
{ field: "prayerConfidential", label: "Confidential" },
|
||||
{ field: "messageTopics", label: "Message Topics" },
|
||||
{ field: "messageTopicsOther", label: "Other - Topics" },
|
||||
{ field: "nextStep", label: "Next Step" },
|
||||
{ field: "attendanceDuration", label: "Attendance Duration" },
|
||||
{ field: "campusPreference", label: "Campus Preference" },
|
||||
{ field: "campusPreferenceOther", label: "Other Location" },
|
||||
{ field: "howHeard", label: "How Did You Hear" },
|
||||
{ field: "howHeardOther", label: "Other - How Heard" },
|
||||
{ field: "serviceAttended", label: "A B C D" },
|
||||
{ field: "serviceTime", label: "Service Time" },
|
||||
{ field: "notes", label: "Notes" },
|
||||
{ field: "planningCenter", label: "Planning Center" },
|
||||
{ field: "iSaidYesBookSent", label: "I Said Yes Book Sent" },
|
||||
{ field: "ftGuestLetterSent", label: "FT Guest Letter Sent" },
|
||||
{ field: "reviewStatus", label: "Review Status" },
|
||||
] as const;
|
||||
|
||||
type SettingsData = {
|
||||
ollamaUrl: string;
|
||||
model: string;
|
||||
|
|
@ -878,10 +913,10 @@ export default function SettingsPage() {
|
|||
{mondayColumns.length > 0 && (
|
||||
<div className="rounded-xl border border-border/50 bg-muted/20 p-3 space-y-2">
|
||||
<p className="text-xs font-medium text-muted-foreground mb-2">Column Mapping</p>
|
||||
<div className="grid gap-2 sm:grid-cols-2">
|
||||
{["name", "email", "cellPhone", "gender", "visitType", "city", "state", "reviewStatus"].map((field) => (
|
||||
<div className="grid gap-2 sm:grid-cols-2 max-h-[28rem] overflow-y-auto pr-1">
|
||||
{MONDAY_MAPPABLE_FIELDS.map(({ field, label }) => (
|
||||
<div key={field} className="flex items-center gap-2">
|
||||
<span className="text-xs text-muted-foreground w-24 shrink-0 capitalize">{field}</span>
|
||||
<span className="text-xs text-muted-foreground w-36 shrink-0">{label}</span>
|
||||
<Select
|
||||
value={(settings.mondayColumnMap || {})[field] || "__none__"}
|
||||
onValueChange={(v) => setColumnMapping(field, !v || v === "__none__" ? "" : v)}
|
||||
|
|
@ -899,7 +934,7 @@ export default function SettingsPage() {
|
|||
</div>
|
||||
))}
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-xs text-muted-foreground w-24 shrink-0">Files (images)</span>
|
||||
<span className="text-xs text-muted-foreground w-36 shrink-0">Files (images)</span>
|
||||
<Select
|
||||
value={(settings.mondayColumnMap || {})._files || "__none__"}
|
||||
onValueChange={(v) => setColumnMapping("_files", !v || v === "__none__" ? "" : v)}
|
||||
|
|
|
|||
Loading…
Reference in a new issue