feat: show persisted investment data and sync state

Consume the provided resource-envelope API snapshot. Preserve financial snapshots during
refresh, observe synchronization without importing, and fence late responses across
account and connection changes.
This commit is contained in:
2026-09-26 21:22:49 +03:00
parent b987fd8e78
commit 4e5f564fa1
81 changed files with 4714 additions and 1420 deletions
@@ -3,6 +3,7 @@ import { LoadingSkeleton } from "@wealthledger/ui";
import type { FC } from "react";
import { useTranslation } from "react-i18next";
import { Link } from "react-router";
import { formatSnapshotTime } from "@/shared/lib";
import type { BrokerageEvent } from "../../api/useBrokerageEvents";
import { BrokerageEventCard } from "../BrokerageEventCard";
@@ -12,7 +13,14 @@ type BrokerageEventsPreviewProps = {
type BrokerageEventsPreviewState =
| { state: "loading" | "error" | "empty"; to: string }
| { state: "loaded"; to: string; items: BrokerageEvent[] };
| {
state: "loaded" | "partial";
to: string;
lastSuccessfulAt?: string | null;
stale?: boolean;
refreshError?: boolean;
items: BrokerageEvent[];
};
const BrokerageEventsPreview: FC<BrokerageEventsPreviewProps> = (props) => {
const { events } = props;
@@ -20,6 +28,21 @@ const BrokerageEventsPreview: FC<BrokerageEventsPreviewProps> = (props) => {
return (
<Stack gap="sm">
{events.state === "loaded" ? (
<>
{events.lastSuccessfulAt ? (
<Text size="xs" c="dimmed">
{t("investment.sourceTime", {
date: formatSnapshotTime(events.lastSuccessfulAt),
})}
</Text>
) : null}
{events.stale ? <Text size="xs">{t("investment.stale")}</Text> : null}
{events.refreshError ? (
<Text role="status">{t("accounts.refreshError")}</Text>
) : null}
</>
) : null}
{events.state === "loading" ? (
<LoadingSkeleton label={t("events.recentLoading")}>
{[0, 1, 2].map((index) => (
@@ -33,7 +56,10 @@ const BrokerageEventsPreview: FC<BrokerageEventsPreviewProps> = (props) => {
{events.state === "empty" ? (
<Text c="dimmed">{t("events.recentEmpty")}</Text>
) : null}
{events.state === "loaded"
{events.state === "partial" ? (
<Text role="status">{t("investment.partial")}</Text>
) : null}
{events.state === "loaded" || events.state === "partial"
? events.items.map((event) => (
<BrokerageEventCard
key={JSON.stringify([