'use client'; import { useState } from 'react'; import { useParams, useRouter } from 'next/navigation'; import { useForm, FormProvider } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; import { useMutation, useQuery } from '@tanstack/react-query'; import { ChevronLeft, ChevronRight, Check, Loader2 } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Textarea } from '@/components/ui/textarea'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Badge } from '@/components/ui/badge'; import { InvoiceLineItems } from '@/components/invoices/invoice-line-items'; import { apiFetch } from '@/lib/api/client'; import { createInvoiceSchema, type CreateInvoiceInput } from '@/lib/validators/invoices'; const PAYMENT_TERMS = [ { label: 'Immediate', value: 'immediate' }, { label: 'Net 10', value: 'net10' }, { label: 'Net 15', value: 'net15' }, { label: 'Net 30', value: 'net30' }, { label: 'Net 45', value: 'net45' }, { label: 'Net 60', value: 'net60' }, ]; const STEPS = [ { id: 1, label: 'Client Info' }, { id: 2, label: 'Line Items' }, { id: 3, label: 'Review' }, ]; export default function NewInvoicePage() { const params = useParams<{ portSlug: string }>(); const portSlug = params?.portSlug ?? ''; const router = useRouter(); const [step, setStep] = useState(1); const methods = useForm({ resolver: zodResolver(createInvoiceSchema), defaultValues: { paymentTerms: 'net30', currency: 'USD', lineItems: [], expenseIds: [], }, }); const { register, handleSubmit, watch, setValue, formState: { errors } } = methods; const watchedValues = watch(); const lineItems = watchedValues.lineItems ?? []; const subtotal = lineItems.reduce( (sum, li) => sum + (Number(li.quantity) || 0) * (Number(li.unitPrice) || 0), 0, ); const isNet10 = watchedValues.paymentTerms === 'net10'; const discountPct = isNet10 ? 2 : 0; const discountAmount = (subtotal * discountPct) / 100; const total = subtotal - discountAmount; const createMutation = useMutation({ mutationFn: (data: CreateInvoiceInput) => apiFetch('/api/v1/invoices', { method: 'POST', body: data, }), onSuccess: (res: any) => { const id = res?.data?.id; if (id) { router.push(`/${portSlug}/invoices/${id}`); } else { router.push(`/${portSlug}/invoices`); } }, }); async function goNext() { if (step === 1) { const valid = await methods.trigger([ 'clientName', 'billingEmail', 'billingAddress', 'dueDate', 'paymentTerms', 'currency', ]); if (valid) setStep(2); } else if (step === 2) { setStep(3); } } function goBack() { setStep((s) => Math.max(1, s - 1)); } function onSubmit(data: CreateInvoiceInput) { createMutation.mutate(data); } return (
{/* Header */}

New Invoice

{/* Step indicator */}
{STEPS.map((s, idx) => (
s.id ? 'bg-primary text-primary-foreground' : step === s.id ? 'bg-primary text-primary-foreground' : 'bg-muted text-muted-foreground' }`} > {step > s.id ? : s.id}
{s.label} {idx < STEPS.length - 1 && (
)}
))}
{/* Step 1: Client Info */} {step === 1 && ( Client Information
{errors.clientName && (

{errors.clientName.message}

)}
{errors.billingEmail && (

{errors.billingEmail.message}

)}