diff --git a/apps/web/src/app/create/worksheets/components/ActionsSidebar.tsx b/apps/web/src/app/create/worksheets/components/ActionsSidebar.tsx new file mode 100644 index 00000000..63771348 --- /dev/null +++ b/apps/web/src/app/create/worksheets/components/ActionsSidebar.tsx @@ -0,0 +1,224 @@ +'use client' + +import { css } from '@styled/css' +import { stack } from '@styled/patterns' +import { useRouter } from 'next/navigation' +import { useState } from 'react' +import { UploadWorksheetModal } from '@/components/worksheets/UploadWorksheetModal' +import { useTheme } from '@/contexts/ThemeContext' +import { GenerateButton } from './GenerateButton' +import { ShareModal } from './ShareModal' +import { useWorksheetConfig } from './WorksheetConfigContext' + +interface ActionsSidebarProps { + onGenerate: () => Promise + status: 'idle' | 'generating' | 'success' | 'error' +} + +export function ActionsSidebar({ onGenerate, status }: ActionsSidebarProps) { + const { resolvedTheme } = useTheme() + const isDark = resolvedTheme === 'dark' + const router = useRouter() + const [isUploadModalOpen, setIsUploadModalOpen] = useState(false) + const [isShareModalOpen, setIsShareModalOpen] = useState(false) + const [isGeneratingShare, setIsGeneratingShare] = useState(false) + const [justCopied, setJustCopied] = useState(false) + const { formState } = useWorksheetConfig() + + // Upload complete handler + const handleUploadComplete = (attemptId: string) => { + router.push(`/worksheets/attempts/${attemptId}`) + } + + // Quick share - copy link to clipboard without showing modal + const handleQuickShare = async () => { + setIsGeneratingShare(true) + setJustCopied(false) + + try { + const response = await fetch('/api/worksheets/share', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + worksheetType: 'addition', + config: formState, + }), + }) + + if (!response.ok) { + throw new Error('Failed to create share link') + } + + const data = await response.json() + await navigator.clipboard.writeText(data.url) + + setJustCopied(true) + setTimeout(() => setJustCopied(false), 2000) + } catch (err) { + console.error('Failed to create share link:', err) + // TODO: Show error toast + } finally { + setIsGeneratingShare(false) + } + } + + return ( + <> +
+
+ {/* Generate Button */} + + + {/* Share Button with Copy Shortcut */} +
+ {/* Main share button - opens QR modal */} + + + {/* Copy shortcut */} + +
+ + {/* Upload Worksheet Button */} + +
+
+ + {/* Share Modal */} + setIsShareModalOpen(false)} + worksheetType="addition" + config={formState} + isDark={isDark} + /> + + {/* Upload Worksheet Modal */} + setIsUploadModalOpen(false)} + onUploadComplete={handleUploadComplete} + /> + + ) +} diff --git a/apps/web/src/app/create/worksheets/components/PreviewCenter.tsx b/apps/web/src/app/create/worksheets/components/PreviewCenter.tsx new file mode 100644 index 00000000..c07d15a1 --- /dev/null +++ b/apps/web/src/app/create/worksheets/components/PreviewCenter.tsx @@ -0,0 +1,367 @@ +'use client' + +import * as DropdownMenu from '@radix-ui/react-dropdown-menu' +import { css } from '@styled/css' +import { useRouter } from 'next/navigation' +import { useEffect, useRef, useState } from 'react' +import type { WorksheetFormState } from '@/app/create/worksheets/types' +import { UploadWorksheetModal } from '@/components/worksheets/UploadWorksheetModal' +import { useTheme } from '@/contexts/ThemeContext' +import { ShareModal } from './ShareModal' +import { WorksheetPreview } from './WorksheetPreview' + +interface PreviewCenterProps { + formState: WorksheetFormState + initialPreview?: string[] + onGenerate: () => Promise + status: 'idle' | 'generating' | 'success' | 'error' +} + +export function PreviewCenter({ + formState, + initialPreview, + onGenerate, + status, +}: PreviewCenterProps) { + const router = useRouter() + const { resolvedTheme } = useTheme() + const isDark = resolvedTheme === 'dark' + const scrollContainerRef = useRef(null) + const [isScrolling, setIsScrolling] = useState(false) + const scrollTimeoutRef = useRef() + const [isUploadModalOpen, setIsUploadModalOpen] = useState(false) + const [isShareModalOpen, setIsShareModalOpen] = useState(false) + const [isGeneratingShare, setIsGeneratingShare] = useState(false) + const [justCopied, setJustCopied] = useState(false) + const isGenerating = status === 'generating' + + // Detect scrolling in the scroll container + useEffect(() => { + const container = scrollContainerRef.current + if (!container) return + + const handleScroll = () => { + setIsScrolling(true) + + // Clear existing timeout + if (scrollTimeoutRef.current) { + clearTimeout(scrollTimeoutRef.current) + } + + // Set new timeout to hide after 1 second of no scrolling + scrollTimeoutRef.current = setTimeout(() => { + setIsScrolling(false) + }, 1000) + } + + container.addEventListener('scroll', handleScroll, { passive: true }) + + return () => { + container.removeEventListener('scroll', handleScroll) + if (scrollTimeoutRef.current) { + clearTimeout(scrollTimeoutRef.current) + } + } + }, []) + + // Upload complete handler + const handleUploadComplete = (attemptId: string) => { + router.push(`/worksheets/attempts/${attemptId}`) + } + + // Quick share - copy link to clipboard without showing modal + const handleQuickShare = async () => { + setIsGeneratingShare(true) + setJustCopied(false) + + try { + const response = await fetch('/api/worksheets/share', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + worksheetType: 'addition', + config: formState, + }), + }) + + if (!response.ok) { + throw new Error('Failed to create share link') + } + + const data = await response.json() + await navigator.clipboard.writeText(data.url) + + setJustCopied(true) + setTimeout(() => setJustCopied(false), 2000) + } catch (err) { + console.error('Failed to create share link:', err) + // TODO: Show error toast + } finally { + setIsGeneratingShare(false) + } + } + + return ( +
+ {/* Floating Action Button - Top Right */} +
+ {/* Main Download Button */} + + + {/* Dropdown Trigger */} + + + + + + + + +
+ {/* Main share button - opens QR modal */} + + + {/* Copy shortcut */} + +
+
+ + setIsUploadModalOpen(true)} + className={css({ + px: '4', + py: '2.5', + fontSize: 'sm', + fontWeight: 'medium', + color: 'gray.700', + cursor: 'pointer', + display: 'flex', + alignItems: 'center', + gap: '2', + outline: 'none', + _hover: { + bg: 'purple.50', + color: 'purple.700', + }, + _focus: { + bg: 'purple.50', + color: 'purple.700', + }, + })} + > + ⬆️ + Upload + +
+
+
+
+ + {/* Share Modal */} + setIsShareModalOpen(false)} + worksheetType="addition" + config={formState} + isDark={isDark} + /> + + {/* Upload Worksheet Modal */} + setIsUploadModalOpen(false)} + onUploadComplete={handleUploadComplete} + /> + +
+ +
+
+ ) +}