diff --git a/src/webapp/features/cards/components/reportCardModal/ReportCardModal.tsx b/src/webapp/features/cards/components/reportCardModal/ReportCardModal.tsx new file mode 100644 index 00000000..f121573e --- /dev/null +++ b/src/webapp/features/cards/components/reportCardModal/ReportCardModal.tsx @@ -0,0 +1,231 @@ +import { useState } from 'react'; +import { + Button, + Stack, + Modal, + Select, + Textarea, + Text, + Group, + ThemeIcon, +} from '@mantine/core'; +import { BsExclamation } from 'react-icons/bs'; +import { DANGER_OVERLAY_PROPS } from '@/styles/overlays'; +import { + MAX_REPORT_DETAILS_LENGTH, + REPORT_REASONS, + ReportReasonKey, +} from './reportReasons'; +import { IoMdCheckmark } from 'react-icons/io'; + +export type ReportCardModalState = + | { kind: 'form' } + | { kind: 'submitting' } + | { kind: 'success' } + | { kind: 'error'; message: string } + | { kind: 'already-reported' }; + +export interface ReportSubmission { + cardId: string; + reason: ReportReasonKey; + details: string; +} + +interface Props { + isOpen: boolean; + onClose: () => void; + cardId: string; + //if omitted, a mock submission resolves after ~800ms (used by Storybook) + onSubmit?: (input: ReportSubmission) => Promise; + //Forces the modal into a particular state (used by Storybook) + initialState?: ReportCardModalState; +} + +const mockSubmit = (): Promise => + new Promise((resolve) => setTimeout(resolve, 800)); + +export default function ReportCardModal(props: Props) { + const [state, setState] = useState( + props.initialState ?? { kind: 'form' }, + ); + const [reason, setReason] = useState(null); + const [details, setDetails] = useState(''); + + const resetForm = () => { + setState(props.initialState ?? { kind: 'form' }); + setReason(null); + setDetails(''); + }; + + const detailsRequired = reason === 'other'; + const detailsValid = !detailsRequired || details.trim().length > 0; + const canSubmit = state.kind === 'form' && reason !== null && detailsValid; + + const handleSubmit = async () => { + if (!canSubmit || reason === null) return; + setState({ kind: 'submitting' }); + try { + const submitter = props.onSubmit ?? mockSubmit; + await submitter({ + cardId: props.cardId, + reason, + details: details.trim(), + }); + setState({ kind: 'success' }); + } catch (err) { + setState({ + kind: 'error', + message: + err instanceof Error + ? err.message + : 'Could not submit your report. Please try again.', + }); + } + }; + + return ( + e.stopPropagation()} + > + {state.kind === 'already-reported' ? ( + + ) : state.kind === 'success' ? ( + + ) : ( + +