diff --git a/apps/web/src/app/create/worksheets/addition/components/WorksheetPreview.tsx b/apps/web/src/app/create/worksheets/addition/components/WorksheetPreview.tsx new file mode 100644 index 00000000..6b59d02c --- /dev/null +++ b/apps/web/src/app/create/worksheets/addition/components/WorksheetPreview.tsx @@ -0,0 +1,305 @@ +'use client' + +import { Suspense, useState, useEffect } from 'react' +import { useSuspenseQuery } from '@tanstack/react-query' +import { useTranslations } from 'next-intl' +import { css } from '../../../../../../styled-system/css' +import { hstack, stack } from '../../../../../../styled-system/patterns' +import type { WorksheetFormState } from '../types' + +interface WorksheetPreviewProps { + formState: WorksheetFormState +} + +function getDefaultDate(): string { + const now = new Date() + return now.toLocaleDateString('en-US', { + month: 'long', + day: 'numeric', + year: 'numeric', + }) +} + +async function fetchWorksheetPreview(formState: WorksheetFormState): Promise { + // Set current date for preview + const configWithDate = { + ...formState, + date: getDefaultDate(), + } + + const response = await fetch('/api/create/worksheets/addition/preview', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(configWithDate), + }) + + if (!response.ok) { + const errorData = await response.json().catch(() => ({})) + const errorMsg = errorData.error || errorData.message || 'Failed to fetch preview' + const details = errorData.details ? `\n\n${errorData.details}` : '' + const errors = errorData.errors ? `\n\nErrors:\n${errorData.errors.join('\n')}` : '' + throw new Error(errorMsg + details + errors) + } + + const data = await response.json() + return data.pages +} + +function PreviewContent({ formState }: WorksheetPreviewProps) { + const t = useTranslations('create.worksheets.addition') + const [currentPage, setCurrentPage] = useState(0) + + // Use Suspense Query - will suspend during loading + const { data: pages } = useSuspenseQuery({ + queryKey: [ + 'worksheet-preview', + formState.total, + formState.cols, + formState.rows, + formState.name, + formState.pAnyStart, + formState.pAllStart, + formState.interpolate, + formState.showCarryBoxes, + formState.showCellBorder, + // Note: seed, fontSize, and date intentionally excluded + ], + queryFn: () => fetchWorksheetPreview(formState), + }) + + const totalPages = pages.length + + // Reset to first page when preview updates + useEffect(() => { + setCurrentPage(0) + }, [pages]) + + return ( +
+
+

+ {t('preview.title')} +

+

+ {totalPages > 1 ? `${totalPages} pages` : t('preview.subtitle')} +

+
+ + {/* Pagination Controls (top) */} + {totalPages > 1 && ( +
+ + + Page {currentPage + 1} of {totalPages} + + +
+ )} + + {/* SVG Preview */} +
+ + {/* Pagination Controls (bottom) */} + {totalPages > 1 && ( +
+ + + Page {currentPage + 1} of {totalPages} + + +
+ )} + + {/* Info about full worksheet */} +
+ Full worksheet: {formState.total} problems in a {formState.cols}× + {formState.rows} grid + {formState.interpolate && ' (progressive difficulty: easy → hard)'} +
+
+ ) +} + +function PreviewFallback() { + return ( +
+

+ Generating preview... +

+
+ ) +} + +export function WorksheetPreview({ formState }: WorksheetPreviewProps) { + return ( + }> + + + ) +}