All files / web/src/components/practice DocumentAdjustmentModal.tsx

0% Statements 0/92
0% Branches 0/1
0% Functions 0/1
0% Lines 0/92

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93                                                                                                                                                                                         
'use client'

import * as Dialog from '@radix-ui/react-dialog'
import dynamic from 'next/dynamic'
import type { DocumentAdjustmentState, Corner, Rotation } from '@/types/attachments'
import type { DetectQuadsInImageResult } from '@/components/practice/useDocumentDetection'
import { Z_INDEX } from '@/constants/zIndex'
import { css } from '../../../styled-system/css'

// Dynamic import for heavy OpenCV-dependent component
const DocumentAdjuster = dynamic(
  () => import('@/components/practice/DocumentAdjuster').then((m) => m.DocumentAdjuster),
  { ssr: false }
)

export interface DocumentAdjustmentModalProps {
  /** Current adjustment state (null when closed) */
  state: DocumentAdjustmentState | null
  /** Number of files remaining in queue (for "1 of N" display) */
  queueLength: number
  /** OpenCV reference */
  opencvRef: unknown
  /** Function to detect quads in an image */
  detectQuadsInImage: (canvas: HTMLCanvasElement) => DetectQuadsInImageResult
  /** Callback when adjustment is confirmed */
  onConfirm: (croppedFile: File, corners: Corner[], rotation: Rotation) => Promise<void>
  /** Callback when adjustment is skipped (use original) */
  onSkip: () => Promise<void>
  /** Callback when adjustment is cancelled (clear queue) */
  onCancel: () => void
}

/**
 * Modal for adjusting document perspective crop before upload
 *
 * Shows the DocumentAdjuster component in a fullscreen dialog,
 * allowing users to adjust corners and rotation before confirming.
 */
export function DocumentAdjustmentModal({
  state,
  queueLength,
  opencvRef,
  detectQuadsInImage,
  onConfirm,
  onSkip,
  onCancel,
}: DocumentAdjustmentModalProps) {
  const isOpen = state !== null && opencvRef !== null

  return (
    <Dialog.Root open={isOpen} onOpenChange={(open) => !open && onCancel()}>
      <Dialog.Portal>
        <Dialog.Overlay
          className={css({
            position: 'fixed',
            inset: 0,
            bg: 'black',
            zIndex: Z_INDEX.MODAL,
          })}
        />
        <Dialog.Content
          className={css({
            position: 'fixed',
            inset: 0,
            zIndex: Z_INDEX.MODAL + 1,
            outline: 'none',
          })}
        >
          <Dialog.Title className={css({ srOnly: true })}>
            Adjust Photo {queueLength > 0 ? `(1 of ${queueLength + 1})` : ''}
          </Dialog.Title>
          <Dialog.Description className={css({ srOnly: true })}>
            Drag corners to crop the document. Tap Done to confirm or Skip to use original.
          </Dialog.Description>
          {state !== null && opencvRef !== null && (
            <DocumentAdjuster
              sourceCanvas={state.sourceCanvas}
              initialCorners={state.corners}
              onConfirm={onConfirm}
              onCancel={onCancel}
              onSkip={onSkip}
              cv={opencvRef}
              detectQuadsInImage={detectQuadsInImage}
            />
          )}
        </Dialog.Content>
      </Dialog.Portal>
    </Dialog.Root>
  )
}

export default DocumentAdjustmentModal