All files / shared / components CrudModal.tsx

50% Statements 17/34
38.46% Branches 10/26
36.36% Functions 4/11
53.12% Lines 17/32

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          69x   69x   276x 276x                                 276x                                                                                                                              
import { useState, type ReactNode } from 'react'
import {
  Dialog,
  DialogContent,
  DialogHeader,
  DialogTitle,
  DialogFooter,
} from '@/components/ui/dialog'
import { Button } from '@/components/ui/button'
import { DiscardDialog } from './DiscardDialog'
import { Loader2 } from 'lucide-react'

export interface CrudModalProps {
  open: boolean
  title: string
  isDirty: boolean
  isPending?: boolean
  saveLabel?: string
  onClose: () => void
  onSave?: () => void
  children: ReactNode
}

export function CrudModal({
  open,
  title,
  isDirty,
  isPending = false,
  saveLabel = 'Save',
  onClose,
  onSave,
  children,
}: CrudModalProps) {
  const [showDiscard, setShowDiscard] = useState(false)

  function handleCancel() {
    if (isDirty && !isPending) {
      setShowDiscard(true)
    } else {
      onClose()
    }
  }
 
  function handleDiscard() {
    setShowDiscard(false)
    onClose()
  }
 
  function handleOpenChange(open: boolean) {
    if (!open) {
      handleCancel()
    }
  }
 
  return (
    <>
      <Dialog open={open} onOpenChange={handleOpenChange}>
        <DialogContent
          showCloseButton={false}
          onInteractOutside={(e) => e.preventDefault()}
          onEscapeKeyDown={(e) => {
            e.preventDefault()
            handleCancel()
          }}
        >
          <DialogHeader>
            <DialogTitle>{title}</DialogTitle>
          </DialogHeader>
          {children}
          <DialogFooter>
            <Button variant="outline" onClick={handleCancel} type="button" disabled={isPending}>
              Cancel
            </Button>
            {onSave && (
              <Button onClick={onSave} disabled={isPending} type="button">
                {isPending && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
                {isPending ? 'Saving...' : saveLabel}
              </Button>
            )}
          </DialogFooter>
        </DialogContent>
      </Dialog>
      <DiscardDialog
        open={showDiscard}
        onDiscard={handleDiscard}
        onKeepEditing={() => setShowDiscard(false)}
      />
    </>
  )
}