);
}
diff --git a/src/components/cards/data-table.tsx b/src/components/cards/data-table.tsx
index a7f9fe2..32e454d 100644
--- a/src/components/cards/data-table.tsx
+++ b/src/components/cards/data-table.tsx
@@ -239,28 +239,17 @@ export function DataTable({
target.closest("button") ||
target.closest("input") ||
target.closest('[data-slot="checkbox"]') ||
- target.closest('[data-slot="dropdown-menu"]') ||
- target.closest("[data-copy-cell]")
+ target.closest('[data-slot="dropdown-menu"]')
) {
return;
}
router.push(`/cards/${rowId}`);
};
- const handleCellClick = React.useCallback(
- async (e: React.MouseEvent, cellId: string, columnId: string) => {
- if (NON_COPYABLE_COLUMNS.has(columnId)) return;
-
- if (
- (e.target as HTMLElement).closest("button") ||
- (e.target as HTMLElement).closest("input") ||
- (e.target as HTMLElement).closest('[data-slot="checkbox"]')
- ) {
- return;
- }
-
+ const handleCopyClick = React.useCallback(
+ async (e: React.MouseEvent, cellElement: HTMLTableCellElement, cellId: string) => {
e.stopPropagation();
- const text = getCellText(e.currentTarget);
+ const text = getCellText(cellElement);
if (!text || text === "—") return;
try {
@@ -418,29 +407,30 @@ export function DataTable({
const isSticky = colId === "select" || !!colMeta?.sticky;
const cellKey = `${row.id}_${colId}`;
const isCopied = copiedCellId === cellKey;
+ const cellRef = React.createRef();
return (
handleCellClick(e, cellKey, colId)
- : undefined
- }
>
{flexRender(
cell.column.columnDef.cell,
cell.getContext()
)}
{isCopyable && (
-
+
+
)}
);
diff --git a/src/lib/ai-ocr.ts b/src/lib/ai-ocr.ts
index 972b0a0..9b434fd 100644
--- a/src/lib/ai-ocr.ts
+++ b/src/lib/ai-ocr.ts
@@ -63,7 +63,7 @@ const surveySchema = z.object({
),
messageTopicsOther: z.string().nullable().describe("Value if Other is filled in"),
nextStep: z.array(z.string()).describe(
- "This question has exactly 2 checkboxes. Include ONLY the ones with a visible mark (X, checkmark, filled). 'Baptism' = the checkbox for 'Expressing my faith in Jesus / baptized'. 'Next Steps' = the checkbox for 'Learning more about becoming a partner / attend Next Steps'. Return empty array if neither checkbox is marked."
+ "CRITICAL: This question has EXACTLY 2 separate checkboxes, each on its own line. Inspect each checkbox square individually. 'Baptism' = FIRST checkbox (next to 'Expressing my faith in Jesus...'). 'Next Steps' = SECOND checkbox (next to 'Learning more about becoming a partner...'). A checkbox is checked ONLY if its square contains a visible X, checkmark, or fill. An empty square = NOT checked. Most people check only one. Return empty array if neither is marked."
),
attendanceDuration: z.string().nullable().describe(
"The single checked radio option. One of: Less than 6 months, 6 Months - 1 Year, 1-3 Years, 4-6 Years, or 7+ Years. null if none is marked."
@@ -97,12 +97,14 @@ const SURVEY_SYSTEM_PROMPT = `You are analyzing a scanned church Easter survey f
Extract ALL of the following fields from the image.
CHECKBOX RULES - be very strict:
-- A checkbox is CHECKED only if it has a visible mark inside it: an X, a checkmark, a filled square, or pen/pencil marks inside the box.
-- An EMPTY box (no marks inside) means UNCHECKED, even if text appears next to it.
+- A checkbox is CHECKED only if its square box contains a visible mark: an X, a checkmark, a filled square, or pen/pencil marks INSIDE the box.
+- An EMPTY box (no marks inside the square) means UNCHECKED, even if text appears next to it.
- When in doubt, treat a checkbox as UNCHECKED.
-- For array fields (messageTopics, nextStep, campusPreference, howHeard): ONLY include items whose checkbox is physically marked. Return empty arrays when no checkboxes in that group are marked.
+- For array fields (messageTopics, nextStep, campusPreference, howHeard): ONLY include items whose checkbox square is physically marked. Return empty arrays when no checkboxes in that group are marked.
- Do NOT confuse section headings or question titles with checked answers.
+SPECIAL ATTENTION for "Next Step" question (question 2): It has exactly 2 checkboxes on separate lines. Look at each checkbox square independently. Most respondents check only one. Do not assume both are checked.
+
Be precise: return null for fields you cannot read.`;
export interface OcrResult {
diff --git a/src/lib/ollama.ts b/src/lib/ollama.ts
index 0c134b5..4bd0be9 100644
--- a/src/lib/ollama.ts
+++ b/src/lib/ollama.ts
@@ -64,10 +64,10 @@ CHECKBOX RULES - be very strict:
- For array fields: ONLY include items whose checkbox is physically marked. Return empty arrays when no checkboxes in that group are marked.
- Do NOT confuse section headings or question titles with checked answers.
-IMPORTANT for nextStep: There are exactly 2 checkboxes.
-- "Baptism" = the checkbox for "Expressing my faith in Jesus / I want to be baptized..."
-- "Next Steps" = the checkbox for "Learning more about becoming a partner / attend Next Steps..."
-Only include the one(s) whose box is physically marked.
+SPECIAL ATTENTION for nextStep: There are exactly 2 checkboxes on separate lines.
+- "Baptism" = FIRST checkbox (next to "Expressing my faith in Jesus / I want to be baptized...")
+- "Next Steps" = SECOND checkbox (next to "Learning more about becoming a partner / attend Next Steps...")
+Inspect each checkbox square independently. A checkbox is checked ONLY if its square contains a visible X, checkmark, or fill. An empty square = NOT checked. Most respondents check only one. Do NOT assume both are checked.
Return ONLY valid JSON with this exact structure (no markdown, no code fences):
{