Changeset 8449b9c


Ignore:
Timestamp:
04/30/26 11:27:00 (2 months ago)
Author:
Andrej <asumanovski@…>
Branches:
master
Children:
4f85b41
Parents:
708af9e
Message:

Finance feature

Files:
23 added
4 edited

Legend:

Unmodified
Added
Removed
  • frontend/package-lock.json

    r708af9e r8449b9c  
    1717        "react-dom": "^19.2.0",
    1818        "react-icons": "^5.5.0",
     19        "react-is": "^19.2.5",
    1920        "react-router-dom": "^7.13.0"
    2021      },
     
    95419542      }
    95429543    },
     9544    "node_modules/react-is": {
     9545      "version": "19.2.5",
     9546      "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.5.tgz",
     9547      "integrity": "sha512-Dn0t8IQhCmeIT3wu+Apm1/YVsJXsGWi6k4sPdnBIdqMVtHtv0IGi6dcpNpNkNac0zB2uUAqNX3MHzN8c+z2rwQ==",
     9548      "license": "MIT"
     9549    },
    95439550    "node_modules/react-refresh": {
    95449551      "version": "0.18.0",
  • frontend/package.json

    r708af9e r8449b9c  
    1919    "react-dom": "^19.2.0",
    2020    "react-icons": "^5.5.0",
     21    "react-is": "^19.2.5",
    2122    "react-router-dom": "^7.13.0"
    2223  },
  • frontend/src/App.jsx

    r708af9e r8449b9c  
    1313import NewWeightIntake from "./pages/Dashboard/pages/Weight/NewWeightIntake.jsx";
    1414import Finance from "./pages/Dashboard/pages/Finance/Finance.jsx";
     15import FinanceTracking from "./pages/Dashboard/pages/Finance/FinanceTracking.jsx";
     16import NewIncome from "./pages/Dashboard/pages/Finance/NewIncome.jsx";
    1517import Investing from "./pages/Dashboard/pages/Investing/Investing.jsx";
    1618import InvestingTracking from "./pages/Dashboard/pages/Investing/InvestingTracking.jsx";
     
    5153          <Route path="weight/intakes/new" element={<NewWeightIntake />} />
    5254          <Route path="finance" element={<Finance />} />
     55           <Route path="finance/tracking" element={<FinanceTracking />} />
     56           <Route path="finance/incomes/new" element={<NewIncome />} />
    5357          <Route path="investing" element={<Investing />} />
    5458          <Route path="investing/tracking" element={<InvestingTracking />} />
  • frontend/src/pages/Dashboard/pages/Finance/Finance.jsx

    r708af9e r8449b9c  
    1 import React from "react";
     1import React, { useEffect, useMemo, useState } from "react";
     2import { useNavigate } from "react-router-dom";
    23
    3 const Finance = () => {
     4import { getFinanceStatus, startFinanceTracking } from "../../../../api/finance";
     5
     6import FinanceCenteredCard from "./components/FinanceCenteredCard.jsx";
     7import FinanceStartCtaCard from "./components/FinanceStartCtaCard.jsx";
     8import FinanceStartForm from "./components/FinanceStartForm.jsx";
     9
     10export default function Finance() {
     11  const navigate = useNavigate();
     12
     13  const [isLoading, setIsLoading] = useState(true);
     14  const [isTracking, setIsTracking] = useState(false);
     15  const [step, setStep] = useState("cta"); // cta | form
     16  const [error, setError] = useState("");
     17  const [isSubmitting, setIsSubmitting] = useState(false);
     18
     19  const initialForm = useMemo(
     20    () => ({
     21      spendingBudget: "50",
     22      savingBudget: "20",
     23      investingBudget: "20",
     24      donationBudget: "5",
     25      credit: "5",
     26    }),
     27    [],
     28  );
     29
     30  const [form, setForm] = useState(initialForm);
     31
     32  useEffect(() => {
     33    let isMounted = true;
     34    const run = async () => {
     35      setIsLoading(true);
     36      setError("");
     37      try {
     38        const tracking = await getFinanceStatus();
     39        if (!isMounted) return;
     40        setIsTracking(tracking);
     41        if (tracking) {
     42          navigate("/dashboard/finance/tracking", { replace: true });
     43        }
     44      } catch (err) {
     45        if (!isMounted) return;
     46        const message =
     47          err?.response?.data?.message || "Failed to load finance status";
     48        setError(message);
     49      } finally {
     50        if (isMounted) setIsLoading(false);
     51      }
     52    };
     53    run();
     54    return () => {
     55      isMounted = false;
     56    };
     57  }, [navigate]);
     58
     59  const onStart = () => {
     60    setError("");
     61    setStep("form");
     62  };
     63
     64  const onCancel = () => {
     65    setStep("cta");
     66    setForm(initialForm);
     67    setError("");
     68  };
     69
     70  const onSubmit = async (e) => {
     71    e.preventDefault();
     72    setError("");
     73    setIsSubmitting(true);
     74    try {
     75      await startFinanceTracking({
     76        spendingBudget:
     77          form.spendingBudget === "" ? null : Number(form.spendingBudget),
     78        savingBudget: form.savingBudget === "" ? null : Number(form.savingBudget),
     79        investingBudget:
     80          form.investingBudget === "" ? null : Number(form.investingBudget),
     81        donationBudget:
     82          form.donationBudget === "" ? null : Number(form.donationBudget),
     83        credit: form.credit === "" ? null : Number(form.credit),
     84      });
     85
     86      setIsTracking(true);
     87      navigate("/dashboard/finance/tracking", { replace: true });
     88    } catch (err) {
     89      const message =
     90        err?.response?.data?.message ||
     91        Object.values(err?.response?.data?.errors ?? {})[0] ||
     92        "Failed to start tracking";
     93      setError(message);
     94    } finally {
     95      setIsSubmitting(false);
     96    }
     97  };
     98
     99  if (isLoading) {
     100    return <FinanceCenteredCard title="Finance" message="Loading…" />;
     101  }
     102
     103  if (isTracking) {
     104    return <FinanceCenteredCard title="Finance" message="Redirecting…" />;
     105  }
     106
    4107  return (
    5     <div className="space-y-4">
    6       <h1 className="text-2xl font-bold">Finance</h1>
    7       <div className="card bg-base-200 border border-base-300">
    8         <div className="card-body">
    9           <p className="opacity-80">Finance dashboard content goes here.</p>
    10         </div>
     108    <div className="min-h-[70vh] w-full flex items-center justify-center">
     109      <div className="w-full max-w-3xl">
     110        {step === "cta" ? (
     111          <FinanceStartCtaCard error={error} onStart={onStart} />
     112        ) : (
     113          <FinanceStartForm
     114            form={form}
     115            setForm={setForm}
     116            onSubmit={onSubmit}
     117            onCancel={onCancel}
     118            error={error}
     119            isSubmitting={isSubmitting}
     120          />
     121        )}
    11122      </div>
    12123    </div>
    13124  );
    14 };
    15 
    16 export default Finance;
     125}
Note: See TracChangeset for help on using the changeset viewer.