"use client"; import { useState, useEffect, useCallback } from "react"; import { useRouter } from "next/navigation"; import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import * as z from "zod"; import { CalendarIcon } from "lucide-react"; import { format } from "date-fns"; import { Button } from "@/components/ui/button"; import { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage, } from "@/components/ui/form"; import { Input } from "@/components/ui/input"; import { Card, CardContent } from "@/components/ui/card"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { Popover, PopoverContent, PopoverTrigger, } from "@/components/ui/popover"; import { Calendar } from "@/components/ui/calendar"; import { useToast } from "@/components/ui/use-toast"; import { api } from "@/lib/api"; // Validation schema const formSchema = z.object({ name: z .string() .min(3, { message: "Name must be at least 3 characters" }) .max(100, { message: "Name must be less than 100 characters" }), targetAmount: z .number() .min(1, { message: "Target amount must be greater than 0" }), currentAmount: z .number() .min(0, { message: "Current amount cannot be negative" }) .optional(), targetDate: z.date().optional(), status: z.enum(["Active", "Paused", "Achieved", "Cancelled"]), }); type FormValues = z.infer; interface GoalFormProps { goalId?: number; isEditing?: boolean; onSuccess?: () => void; } export function GoalForm({ goalId, isEditing = false, onSuccess }: GoalFormProps) { const [loading, setLoading] = useState(false); const [initialLoading, setInitialLoading] = useState(false); const router = useRouter(); const { toast } = useToast(); // Set up form with validation const form = useForm({ resolver: zodResolver(formSchema), defaultValues: { name: "", targetAmount: 0, currentAmount: 0, status: "Active", }, }); const fetchGoalData = useCallback(async () => { setInitialLoading(true); try { const response = await api.get(`/goals/${goalId}`); const goalData = response.data; // Set form values form.reset({ name: goalData.name, targetAmount: goalData.targetAmount, currentAmount: goalData.currentAmount, status: goalData.status as "Active" | "Paused" | "Achieved" | "Cancelled", ...(goalData.targetDate && { targetDate: new Date(goalData.targetDate) }), }); } catch (error) { toast({ title: "Error", description: "Failed to fetch goal data. Please try again.", variant: "destructive", }); console.error("Error fetching goal:", error); router.push("/goals"); } finally { setInitialLoading(false); } }, [goalId, form, toast, router]); // Fetch goal data if editing useEffect(() => { if (isEditing && goalId) { fetchGoalData(); } }, [isEditing, goalId, fetchGoalData]); const onSubmit = async (values: FormValues) => { try { setLoading(true); // Format data for API const formattedData = { ...values, targetDate: values.targetDate ? format(values.targetDate, "yyyy-MM-dd") : undefined, }; console.log("Submitting goal:", formattedData); if (isEditing) { // Update existing goal await api.put(`/goals/${goalId}`, formattedData); toast({ title: "Goal updated", description: "Your goal has been updated successfully.", }); } else { // Create new goal const response = await api.post("/goals", formattedData); console.log("Goal created response:", response.data); toast({ title: "Goal created", description: "Your new goal has been created successfully.", }); } // Call onSuccess callback if provided if (onSuccess) { onSuccess(); } else { // Force a full page reload directly to the goals page window.location.href = "/goals"; } } catch (error) { toast({ title: "Error", description: `Failed to ${isEditing ? "update" : "create"} goal. Please try again.`, variant: "destructive", }); console.error(`Error ${isEditing ? "updating" : "creating"} goal:`, error); } finally { setLoading(false); } }; if (initialLoading) { return
Loading goal data...
; } return (
( Goal Name A descriptive name for your financial goal )} />
( Target Amount field.onChange(Number(e.target.value))} /> The total amount you want to save )} /> ( Current Amount field.onChange(Number(e.target.value) || 0)} /> How much you've already saved towards this goal )} />
( Target Date (Optional) date < new Date()} initialFocus /> When you aim to achieve this goal )} /> ( Status The current status of your goal )} />

Don't see the amount you need?

Use the calculator to determine your target amount.

); }