Ignore:
Timestamp:
05/13/26 00:35:13 (8 weeks ago)
Author:
Andrej <asumanovski@…>
Branches:
master
Children:
447c39f
Parents:
a8381b1
Message:

Fixed auth issue

Location:
frontend/src/pages/Dashboard
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • frontend/src/pages/Dashboard/DashboardLayout.jsx

    ra8381b1 r42da64d  
    66import DashboardSidebar from "./components/DashboardSidebar.jsx";
    77import DashboardTopbar from "./components/DashboardTopbar.jsx";
     8import { clearAuthSession } from "../../utils/authSession";
    89
    910const DashboardLayout = () => {
     
    2223
    2324  const onLogout = () => {
    24     localStorage.removeItem("authToken");
    25     localStorage.removeItem("authUser");
     25    clearAuthSession();
    2626    navigate("/", { replace: true });
    2727  };
  • frontend/src/pages/Dashboard/pages/Investing/InvestingTracking.jsx

    ra8381b1 r42da64d  
    214214        onAddAsset={() => navigate("/dashboard/investing/assets/new")}
    215215      />
    216       <InvestingProgressCard assets={graphAssets} />
     216       <InvestingProgressCard assets={graphAssets} quotesBySymbol={quotesBySymbol} />
    217217
    218218      {quotesError ? (
  • frontend/src/pages/Dashboard/pages/Investing/components/InvestingProgressCard.jsx

    ra8381b1 r42da64d  
    33import TimeRangeToggle from "../../../../../components/graphs/TimeRangeToggle.jsx";
    44import PercentChangeAreaChart from "../../../../../components/graphs/PercentChangeAreaChart.jsx";
    5 import { percentChangeSeries, sumByTimeBucket } from "../../../../../utils/timeSeries.js";
    6 
    7 export default function InvestingProgressCard({ assets }) {
     5import { formatBucketLabel } from "../../../../../utils/timeSeries.js";
     6
     7function bucketKey(date, granularity) {
     8  const d = new Date(date);
     9  if (Number.isNaN(d.getTime())) return null;
     10
     11  function startOfDay(d) {
     12    return new Date(d.getFullYear(), d.getMonth(), d.getDate());
     13  }
     14
     15  function startOfISOWeek(d) {
     16    const date = startOfDay(d);
     17    const day = date.getDay();
     18    const diff = (day === 0 ? -6 : 1) - day;
     19    date.setDate(date.getDate() + diff);
     20    return startOfDay(date);
     21  }
     22
     23  function startOfMonth(d) {
     24    return new Date(d.getFullYear(), d.getMonth(), 1);
     25  }
     26
     27  function startOfYear(d) {
     28    return new Date(d.getFullYear(), 0, 1);
     29  }
     30
     31  let s;
     32  switch (granularity) {
     33    case "daily":
     34      s = startOfDay(d);
     35      break;
     36    case "weekly":
     37      s = startOfISOWeek(d);
     38      break;
     39    case "monthly":
     40      s = startOfMonth(d);
     41      break;
     42    case "yearly":
     43      s = startOfYear(d);
     44      break;
     45    default:
     46      s = startOfDay(d);
     47  }
     48  return s.getTime();
     49}
     50
     51export default function InvestingProgressCard({ assets, quotesBySymbol = {} }) {
    852  const [range, setRange] = useState("weekly");
    953
    1054  const points = useMemo(() => {
    11     // Option B: "net invested momentum".
    12     // Since we only have assets (with buy date/price/quantity) and no transaction history,
    13     // we treat each asset as a one-time investment at buyDate.
    14     const buckets = sumByTimeBucket(
    15       assets,
    16       range,
    17       (a) => a.buyDate,
    18       (a) => (Number(a?.quantity) || 0) * (Number(a?.buyPrice) || 0),
    19     );
    20     return percentChangeSeries(buckets);
    21   }, [assets, range]);
     55    // Calculate portfolio percentage gain/loss over time using current prices
     56    // Display cumulative portfolio value and percentage change between time buckets
     57
     58    if (!assets || assets.length === 0) {
     59      return [];
     60    }
     61
     62    // Group assets by buy date bucket
     63    const buckets = new Map();
     64    for (const asset of assets) {
     65      if (asset?.buyDate) {
     66        const ts = bucketKey(asset.buyDate, range);
     67        if (ts !== null) {
     68          if (!buckets.has(ts)) {
     69            buckets.set(ts, []);
     70          }
     71          buckets.get(ts).push(asset);
     72        }
     73      }
     74    }
     75
     76    // Sort buckets chronologically
     77    const sortedBuckets = Array.from(buckets.entries()).sort((a, b) => a[0] - b[0]);
     78
     79    // Calculate cumulative portfolio value at each bucket
     80    const points = [];
     81    let cumulativeAssets = [];
     82
     83    for (const [ts, assetsInBucket] of sortedBuckets) {
     84      cumulativeAssets = [...cumulativeAssets, ...assetsInBucket];
     85
     86      let totalInvested = 0;
     87      let totalCurrentValue = 0;
     88
     89      for (const asset of cumulativeAssets) {
     90        const qty = Number(asset?.quantity) || 0;
     91        const buyPrice = Number(asset?.buyPrice) || 0;
     92        const ticker = String(asset?.tickerSymbol || "").toUpperCase();
     93        const currentPrice = quotesBySymbol[ticker] || buyPrice;
     94
     95        totalInvested += qty * buyPrice;
     96        totalCurrentValue += qty * currentPrice;
     97      }
     98
     99      if (totalInvested > 0) {
     100        points.push({
     101          ts,
     102          invested: totalInvested,
     103          current: totalCurrentValue,
     104        });
     105      }
     106    }
     107
     108    // Calculate percentage gain from invested to current value
     109    const result = [];
     110    for (const p of points) {
     111      const percentageGain = ((p.current - p.invested) / p.invested) * 100;
     112      result.push({
     113        ts: p.ts,
     114        value: percentageGain,
     115        base: p.current,
     116        invested: p.invested,
     117      });
     118    }
     119
     120    return result;
     121  }, [assets, range, quotesBySymbol]);
    22122
    23123  const latest = points?.length ? points[points.length - 1] : null;
    24124  const latestPct = latest ? Number(latest.value ?? 0) : 0;
    25   const latestBase = latest ? Number(latest.base ?? 0) : 0;
     125  const latestCurrentValue = latest ? Number(latest.base ?? 0) : 0;
     126  const latestInvested = latest ? Number(latest.invested ?? 0) : 0;
     127
     128  const totalCurrentPortfolioValue = assets.reduce((sum, asset) => {
     129    const qty = Number(asset?.quantity) || 0;
     130    const ticker = String(asset?.tickerSymbol || "").toUpperCase();
     131    const currentPrice = quotesBySymbol[ticker] || Number(asset?.buyPrice) || 0;
     132    return sum + qty * currentPrice;
     133  }, 0);
     134
     135  const totalInvestedAmount = assets.reduce((sum, asset) => {
     136    const qty = Number(asset?.quantity) || 0;
     137    const buyPrice = Number(asset?.buyPrice) || 0;
     138    return sum + qty * buyPrice;
     139  }, 0);
    26140
    27141  return (
     
    29143      <div className="card-body">
    30144        <div className="flex items-center justify-between gap-4">
    31           <h2 className="card-title">Progress</h2>
     145          <h2 className="card-title">Portfolio Value</h2>
    32146          <div className="flex items-center gap-3">
    33             <span className="badge badge-ghost">
    34               {latestPct >= 0 ? "+" : ""}
    35               {latestPct.toFixed(1)}% (last)
    36             </span>
     147            <div className="flex flex-col items-end gap-1">
     148              <span className="text-sm font-semibold">
     149                ${totalCurrentPortfolioValue.toFixed(2)}
     150              </span>
     151              <span
     152                className={`text-xs font-bold ${
     153                  latestPct >= 0 ? "text-success" : "text-error"
     154                }`}
     155              >
     156                {latestPct >= 0 ? "+" : ""}
     157                {latestPct.toFixed(2)}%
     158              </span>
     159            </div>
    37160            <TimeRangeToggle value={range} onChange={setRange} />
    38161          </div>
     
    40163
    41164        <div className="mt-4 w-full overflow-hidden rounded-xl border border-base-300 bg-base-100 p-2">
    42           {points.length < 2 ? (
     165          {points.length < 1 ? (
    43166            <div className="flex h-65 items-center justify-center text-sm opacity-70">
    44               Add at least 2 investments to see % change.
     167              Add investments to see portfolio growth.
    45168            </div>
    46169          ) : (
     
    49172              granularity={range}
    50173              height={260}
    51               positiveColor="#14b8a6"
     174              positiveColor="#10b981"
    52175            />
    53176          )}
    54177        </div>
    55178
    56         <div className="mt-2 text-xs opacity-70">
    57           Showing percentage change in invested amount per {range} bucket. Latest bucket invested: {Math.round(latestBase)}.
     179        <div className="mt-3 grid grid-cols-3 gap-2 text-xs">
     180          <div className="rounded bg-base-300 p-2">
     181            <div className="opacity-70">Invested</div>
     182            <div className="font-semibold">${totalInvestedAmount.toFixed(2)}</div>
     183          </div>
     184          <div className="rounded bg-base-300 p-2">
     185            <div className="opacity-70">Current Value</div>
     186            <div className="font-semibold">${totalCurrentPortfolioValue.toFixed(2)}</div>
     187          </div>
     188          <div className="rounded bg-base-300 p-2">
     189            <div className="opacity-70">Gain/Loss</div>
     190            <div
     191              className={`font-semibold ${
     192                totalCurrentPortfolioValue >= totalInvestedAmount
     193                  ? "text-success"
     194                  : "text-error"
     195              }`}
     196            >
     197              ${(totalCurrentPortfolioValue - totalInvestedAmount).toFixed(2)}
     198            </div>
     199          </div>
    58200        </div>
    59201      </div>
Note: See TracChangeset for help on using the changeset viewer.