Frontend security / security (push) Successful in 9s
Frontend verify / verify (push) Successful in 5m10s
Conventional commits / commits (pull_request) Successful in 25s
Frontend security / security (pull_request) Successful in 9s
Frontend verify / verify (pull_request) Successful in 5m10s
142 lines
3.5 KiB
TypeScript
142 lines
3.5 KiB
TypeScript
import {
|
|
ActionIcon,
|
|
Button,
|
|
Group,
|
|
Paper,
|
|
SimpleGrid,
|
|
Skeleton,
|
|
Stack,
|
|
Switch,
|
|
Text,
|
|
Title,
|
|
} from "@mantine/core";
|
|
import { AddIcon } from "@wealthledger/icons";
|
|
import { LoadingSkeleton, StateView } from "@wealthledger/ui";
|
|
import type { FC } from "react";
|
|
import { useTranslation } from "react-i18next";
|
|
import { Link } from "react-router";
|
|
import {
|
|
BankCardFace,
|
|
BankFeedback,
|
|
BankPeriodPicker,
|
|
BankSummaryView,
|
|
} from "@/entities/bank-card";
|
|
import classes from "./CardsPage.module.css";
|
|
import type { CardsPageVm } from "./CardsPage.vm";
|
|
|
|
type CardsPageViewProps = { vm: CardsPageVm };
|
|
const CardsPageView: FC<CardsPageViewProps> = (props) => {
|
|
const { vm } = props;
|
|
const { t } = useTranslation();
|
|
return (
|
|
<Stack gap="lg">
|
|
<Group gap="sm">
|
|
<Title order={1}>{t("bank.title")}</Title>
|
|
<ActionIcon
|
|
size="lg"
|
|
variant="light"
|
|
aria-label={t("bank.addCard")}
|
|
title={t("bank.addCard")}
|
|
onClick={() => vm.setAdding(true)}
|
|
>
|
|
<AddIcon size={20} strokeWidth={2} />
|
|
</ActionIcon>
|
|
</Group>
|
|
<Switch
|
|
label={t("bank.showArchive")}
|
|
checked={vm.archive}
|
|
onChange={(event) => vm.setArchive(event.currentTarget.checked)}
|
|
/>
|
|
{vm.items ? (
|
|
vm.items.length ? (
|
|
<SimpleGrid cols={{ base: 1, sm: 2 }}>
|
|
{vm.items.map((card) => (
|
|
<Paper
|
|
key={card.id}
|
|
component={Link}
|
|
to={card.to}
|
|
p={0}
|
|
className={classes.card}
|
|
>
|
|
<BankCardFace card={card} />
|
|
</Paper>
|
|
))}
|
|
</SimpleGrid>
|
|
) : (
|
|
<StateView
|
|
title={t("bank.empty")}
|
|
text={t(vm.archive ? "bank.emptyHint" : "bank.noActive")}
|
|
action={
|
|
<Button onClick={() => vm.setAdding(true)}>
|
|
{t("bank.addCard")}
|
|
</Button>
|
|
}
|
|
/>
|
|
)
|
|
) : vm.cards.isLoading ? (
|
|
<LoadingSkeleton label={t("bank.loading")}>
|
|
<SimpleGrid cols={{ base: 1, sm: 2 }}>
|
|
<Skeleton h={250} />
|
|
<Skeleton h={250} />
|
|
</SimpleGrid>
|
|
</LoadingSkeleton>
|
|
) : null}
|
|
{vm.cards.isError && (
|
|
<Group role="alert">
|
|
<Text>{t(vm.items ? "bank.refreshError" : "bank.error")}</Text>
|
|
<Button variant="default" onClick={() => void vm.cards.refetch()}>
|
|
{t("bank.retry")}
|
|
</Button>
|
|
</Group>
|
|
)}
|
|
{vm.cards.data && vm.cards.isFetching && (
|
|
<Text role="status">{t("bank.refreshing")}</Text>
|
|
)}
|
|
<Group align="flex-end" justify="space-between">
|
|
<BankPeriodPicker period={vm.period} onChange={vm.setPeriod} />
|
|
{Boolean(vm.categories.data?.length || vm.cards.data?.length) && (
|
|
<Button
|
|
variant="subtle"
|
|
color="gray"
|
|
onClick={() => vm.setManagingCategories(true)}
|
|
leftSection={
|
|
<svg
|
|
width="18"
|
|
height="18"
|
|
viewBox="0 0 24 24"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
strokeWidth="1.7"
|
|
strokeLinejoin="round"
|
|
aria-hidden="true"
|
|
>
|
|
<path d="M3 3h7v7H3zM14 3h7v7h-7zM3 14h7v7H3zM14 14h7v7h-7z" />
|
|
</svg>
|
|
}
|
|
>
|
|
{t("bank.categories")}
|
|
</Button>
|
|
)}
|
|
</Group>
|
|
<BankSummaryView
|
|
all
|
|
data={vm.summary.data}
|
|
loading={vm.summary.isLoading}
|
|
error={vm.summary.isError}
|
|
fetching={vm.summary.isFetching}
|
|
retry={() => void vm.summary.refetch()}
|
|
/>
|
|
{vm.categories.isError && (
|
|
<BankFeedback
|
|
loadingError
|
|
error={vm.categories.error}
|
|
cached={Boolean(vm.categories.data)}
|
|
retry={() => void vm.categories.refetch()}
|
|
/>
|
|
)}
|
|
</Stack>
|
|
);
|
|
};
|
|
|
|
export { CardsPageView };
|