diff --git a/frontend/src/components/BountySubmitModal.demo.tsx b/frontend/src/components/BountySubmitModal.demo.tsx
new file mode 100644
index 0000000..58e16ca
--- /dev/null
+++ b/frontend/src/components/BountySubmitModal.demo.tsx
@@ -0,0 +1,45 @@
+'use client';
+
+import React, { useState } from 'react';
+import { BountySubmitModal } from './BountySubmitModal';
+
+/**
+ * Demo page for Bounty Submission UI Flow.
+ * Shows a button that opens the submission modal.
+ */
+export default function BountySubmitDemo(): JSX.Element {
+ const [isOpen, setIsOpen] = useState(false);
+
+ return (
+
+
+
+ Interactive Bounty Submission
+
+
+ Multi-step form with dynamic link management and confirmation step.
+
+
+
+ {/* Trigger button */}
+
+
+
+ Click to open the bounty submission modal
+
+
+
+ {/* The modal */}
+
setIsOpen(false)}
+ bountyTitle="Implement User Authentication"
+ />
+
+ );
+}
diff --git a/frontend/src/components/BountySubmitModal.tsx b/frontend/src/components/BountySubmitModal.tsx
new file mode 100644
index 0000000..e207eed
--- /dev/null
+++ b/frontend/src/components/BountySubmitModal.tsx
@@ -0,0 +1,245 @@
+'use client';
+
+import React, { useState } from 'react';
+import { useForm, useFieldArray, watch } from 'react-hook-form';
+import { Modal } from '@/components/ui/Modal';
+import { Plus, Trash2, Send, ArrowLeft, CheckCircle2 } from 'lucide-react';
+import { cn } from '@/lib/utils';
+
+interface BountySubmissionForm {
+ prUrl: string;
+ notes: string;
+ externalLinks: { url: string }[];
+}
+
+type Step = 'form' | 'confirm' | 'success';
+
+interface BountySubmitModalProps {
+ isOpen: boolean;
+ onClose: () => void;
+ bountyTitle?: string;
+}
+
+/**
+ * Interactive Bounty Submission UI Flow
+ *
+ * Multi-step modal form for submitting bounty completion proof.
+ * Uses react-hook-form with useFieldArray for dynamic link management.
+ * Steps: Form → Confirmation → Success
+ */
+export function BountySubmitModal({
+ isOpen,
+ onClose,
+ bountyTitle = 'Open Bounty',
+}: BountySubmitModalProps): JSX.Element {
+ const [step, setStep] = useState('form');
+
+ const {
+ register,
+ control,
+ handleSubmit,
+ formState: { errors },
+ watch: watchFn,
+ reset,
+ } = useForm({
+ defaultValues: {
+ prUrl: '',
+ notes: '',
+ externalLinks: [{ url: '' }],
+ },
+ });
+
+ const { fields, append, remove } = useFieldArray({
+ control,
+ name: 'externalLinks',
+ });
+
+ // Watch values for confirmation step
+ const watchedPrUrl = watchFn('prUrl');
+ const watchedNotes = watchFn('notes');
+ const watchedLinks = watchFn('externalLinks');
+
+ const onSubmit = (data: BountySubmissionForm) => {
+ console.log('[BountySubmit] Submission data:', data);
+ setStep('confirm');
+ };
+
+ const confirmSubmission = () => {
+ console.log('[BountySubmit] Confirmed and submitted!');
+ setStep('success');
+ };
+
+ const handleClose = () => {
+ setStep('form');
+ reset();
+ onClose();
+ };
+
+ // Success state
+ if (step === 'success') {
+ return (
+
+
+
+
+ Thank you!
+
+
+ Your bounty submission has been received. Guild admins will review
+ your PR and get back to you soon.
+
+
+
+
+ );
+ }
+
+ return (
+
+ {step === 'form' ? (
+
+ ) : (
+ /* Confirmation step */
+
+
+
+ ⚠️ Are you sure? Once submitted, this cannot be edited.
+
+
+
+
+
+ PR URL:{' '}
+
+ {watchedPrUrl || '—'}
+
+
+
+ Notes: {watchedNotes || '—'}
+
+
+ External Links:{' '}
+ {watchedLinks?.filter((l) => l.url).length || 0} link(s)
+
+
+
+
+
+
+
+
+ )}
+
+ );
+}