From a3d9b69bac85fdd13eab9af87218d04b300d09b6 Mon Sep 17 00:00:00 2001 From: ghaddaditw <40211818-ghaddaditw@users.noreply.replit.com> Date: Sun, 8 Jun 2025 15:30:13 +0000 Subject: [PATCH] Enhance finance tracking with record creation and improved data handling Updates the FinancesPage component to allow creating financial records via API. Replit-Commit-Author: Agent Replit-Commit-Session-Id: d7e7c4e8-20cb-41c4-9d0e-79f48938fede Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/9777c70b-fc38-4831-8d6b-78dfffe041b0/0880b8cd-75f3-424e-a72e-7bb3a1a4d190.jpg --- client/src/pages/FinancesPage.tsx | 367 +++++++++++++----------------- 1 file changed, 160 insertions(+), 207 deletions(-) diff --git a/client/src/pages/FinancesPage.tsx b/client/src/pages/FinancesPage.tsx index 81999ec..dd274c1 100644 --- a/client/src/pages/FinancesPage.tsx +++ b/client/src/pages/FinancesPage.tsx @@ -1,94 +1,100 @@ -import { useState } from "react"; -import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; -import { Button } from "@/components/ui/button"; -import { Input } from "@/components/ui/input"; -import { Label } from "@/components/ui/label"; -import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; -import { Badge } from "@/components/ui/badge"; -import { PageHeader } from "@/components/ui/page-header"; -import { useToast } from "@/hooks/use-toast"; -import Sidebar from "@/components/layout/Sidebar"; -import { Plus, DollarSign, TrendingUp, TrendingDown } from "lucide-react"; -import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog"; -import { apiRequest } from "@/lib/queryClient"; +import { useState } from 'react'; +import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; +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 { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog'; +import { Badge } from '@/components/ui/badge'; +import { PageHeader } from '@/components/ui/page-header'; +import { useToast } from '@/hooks/use-toast'; +import { apiRequest } from '@/lib/queryClient'; +import Sidebar from '@/components/layout/Sidebar'; +import { Plus, TrendingUp, TrendingDown, DollarSign } from 'lucide-react'; export default function FinancesPage() { const { toast } = useToast(); const queryClient = useQueryClient(); const [open, setOpen] = useState(false); const [newRecord, setNewRecord] = useState({ - type: "expense", - amount: "", - description: "", - category: "", + type: 'expense' as 'income' | 'expense', + amount: '', + category: '', + description: '' }); - const { data: recordsResponse, isLoading: recordsLoading } = useQuery({ - queryKey: ["/api/financial/records"], + // Fetch financial records + const { data: records, isLoading } = useQuery({ + queryKey: ['/api/financial-records'], + queryFn: async () => { + const response = await fetch('/api/financial-records'); + if (!response.ok) throw new Error('Failed to fetch records'); + return response.json(); + } }); - const { data: summaryResponse, isLoading: summaryLoading } = useQuery({ - queryKey: ["/api/financial/summary"], + // Fetch financial summary + const { data: summary } = useQuery({ + queryKey: ['/api/financial-records/summary'], + queryFn: async () => { + const response = await fetch('/api/financial-records/summary'); + if (!response.ok) throw new Error('Failed to fetch summary'); + return response.json(); + } }); - const records = (recordsResponse as any)?.records || []; - const summary = (summaryResponse as any)?.summary || { income: 0, expenses: 0, net: 0 }; - - const createRecordMutation = useMutation({ - mutationFn: async (record: typeof newRecord) => { - const response = await fetch("/api/financial/records", { - method: "POST", - headers: { - "Content-Type": "application/json", - }, + // Create record mutation + const createMutation = useMutation({ + mutationFn: async (recordData: typeof newRecord) => { + const response = await fetch('/api/financial-records', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ - ...record, - amount: parseFloat(record.amount), - }), - credentials: "include", + ...recordData, + amount: parseFloat(recordData.amount) + }) }); - if (!response.ok) throw new Error("Failed to create record"); + if (!response.ok) throw new Error('Failed to create record'); return response.json(); }, onSuccess: () => { - queryClient.invalidateQueries({ queryKey: ["/api/financial/records"] }); - queryClient.invalidateQueries({ queryKey: ["/api/financial/summary"] }); + queryClient.invalidateQueries({ queryKey: ['/api/financial-records'] }); + queryClient.invalidateQueries({ queryKey: ['/api/financial-records/summary'] }); setOpen(false); - setNewRecord({ type: "expense", amount: "", description: "", category: "" }); - toast({ - title: "Record created", - description: "Your financial record has been successfully created.", - }); + setNewRecord({ type: 'expense', amount: '', category: '', description: '' }); + toast({ title: "Record created successfully!" }); }, onError: () => { - toast({ - title: "Error", - description: "Failed to create record. Please try again.", - variant: "destructive", - }); - }, + toast({ title: "Failed to create record", variant: "destructive" }); + } }); - const handleCreateRecord = (e: React.FormEvent) => { + const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); - if (!newRecord.amount || isNaN(parseFloat(newRecord.amount))) { - toast({ - title: "Invalid amount", - description: "Please enter a valid amount.", - variant: "destructive", - }); + if (!newRecord.amount || !newRecord.category) { + toast({ title: "Please fill in all required fields", variant: "destructive" }); return; } - createRecordMutation.mutate(newRecord); + createMutation.mutate(newRecord); }; - const categories = { - income: ["Salary", "Freelance", "Investment", "Business", "Other"], - expense: ["Food", "Transportation", "Housing", "Entertainment", "Healthcare", "Shopping", "Other"], - }; + // Income categories + const incomeCategories = [ + 'Salary', 'Freelancing', 'Investment', 'Business', 'Gift', 'Other' + ]; - if (recordsLoading || summaryLoading) { + // Expense categories + const expenseCategories = [ + 'Food', 'Transportation', 'Housing', 'Utilities', 'Healthcare', + 'Entertainment', 'Shopping', 'Education', 'Travel', 'Other' + ]; + + const categories = newRecord.type === 'income' ? incomeCategories : expenseCategories; + + // Loading state + if (isLoading) { return (
@@ -97,7 +103,7 @@ export default function FinancesPage() {
- {[1, 2, 3].map((i) => ( + {[...Array(3)].map((_, i) => (
))}
@@ -131,58 +137,9 @@ export default function FinancesPage() {
-
- - setFormData({ ...formData, amount: e.target.value })} - required - /> -
- -
-
- - setFormData({ ...formData, category: e.target.value })} - required - /> -
-
- -