157 lines
3.2 KiB
TypeScript
157 lines
3.2 KiB
TypeScript
import type { FC } from "react";
|
|
|
|
import {
|
|
ActionIcon,
|
|
Button,
|
|
Group,
|
|
Paper,
|
|
SimpleGrid,
|
|
Skeleton,
|
|
Stack,
|
|
Switch,
|
|
Text,
|
|
Title,
|
|
} from "@mantine/core";
|
|
import { AddIcon, CategoriesIcon } from "@wealthledger/icons";
|
|
import { LoadingSkeleton, StateView } from "@wealthledger/ui";
|
|
import { useTranslation } from "react-i18next";
|
|
import { Link } from "react-router";
|
|
|
|
import {
|
|
BankCardFace,
|
|
BankFeedback,
|
|
BankPeriodPicker,
|
|
BankSummaryView,
|
|
} from "@/entities/bank-card";
|
|
|
|
import type { CardsPageVm } from "./CardsPage.vm";
|
|
|
|
import classes from "./CardsPage.module.css";
|
|
|
|
type CardsPageViewProps = { vm: CardsPageVm };
|
|
|
|
const CardsPageViewCards: FC<CardsPageViewProps> = (props) => {
|
|
const { t } = useTranslation();
|
|
const { vm } = props;
|
|
|
|
if (vm.items) {
|
|
if (vm.items.length > 0) {
|
|
return (
|
|
<SimpleGrid cols={{ base: 1, sm: 2 }}>
|
|
{vm.items.map(card => (
|
|
<Paper
|
|
className={classes.card}
|
|
component={Link}
|
|
key={card.id}
|
|
p={0}
|
|
to={card.to}
|
|
>
|
|
<BankCardFace card={card} />
|
|
</Paper>
|
|
))}
|
|
</SimpleGrid>
|
|
);
|
|
}
|
|
|
|
const text = t(vm.archive ? "bank.emptyHint" : "bank.noActive");
|
|
|
|
return (
|
|
<StateView
|
|
action={<Button onClick={vm.onAddCard}>{t("bank.addCard")}</Button>}
|
|
text={text}
|
|
title={t("bank.empty")}
|
|
/>
|
|
);
|
|
}
|
|
|
|
if (vm.cards.isLoading) {
|
|
return (
|
|
<LoadingSkeleton label={t("bank.loading")}>
|
|
<SimpleGrid cols={{ base: 1, sm: 2 }}>
|
|
<Skeleton h={250} />
|
|
|
|
<Skeleton h={250} />
|
|
</SimpleGrid>
|
|
</LoadingSkeleton>
|
|
);
|
|
}
|
|
};
|
|
|
|
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
|
|
aria-label={t("bank.addCard")}
|
|
onClick={vm.onAddCard}
|
|
size="lg"
|
|
title={t("bank.addCard")}
|
|
variant="light"
|
|
>
|
|
<AddIcon size={20} strokeWidth={2} />
|
|
</ActionIcon>
|
|
</Group>
|
|
|
|
<Switch
|
|
checked={vm.archive}
|
|
label={t("bank.showArchive")}
|
|
onChange={vm.onArchiveChange}
|
|
/>
|
|
|
|
<CardsPageViewCards {...props} />
|
|
|
|
{vm.cards.isError && (
|
|
<Group role="alert">
|
|
<Text>{t(vm.items ? "bank.refreshError" : "bank.error")}</Text>
|
|
|
|
<Button onClick={vm.retryCards} variant="default">
|
|
{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 onChange={vm.setPeriod} period={vm.period} />
|
|
|
|
{Boolean(vm.categories.data?.length || vm.cards.data?.length) && (
|
|
<Button
|
|
color="gray"
|
|
leftSection={<CategoriesIcon size={18} strokeWidth="1.7" />}
|
|
onClick={vm.onCategories}
|
|
variant="subtle"
|
|
>
|
|
{t("bank.categories")}
|
|
</Button>
|
|
)}
|
|
</Group>
|
|
|
|
<BankSummaryView
|
|
all
|
|
data={vm.summary.data}
|
|
error={vm.summary.isError}
|
|
fetching={vm.summary.isFetching}
|
|
loading={vm.summary.isLoading}
|
|
retry={vm.retrySummary}
|
|
/>
|
|
|
|
{vm.categories.isError && (
|
|
<BankFeedback
|
|
cached={Boolean(vm.categories.data)}
|
|
error={vm.categories.error}
|
|
loadingError
|
|
retry={vm.retryCategories}
|
|
/>
|
|
)}
|
|
</Stack>
|
|
);
|
|
};
|
|
|
|
export { CardsPageView };
|