feat(card): refine header and inline actions

This commit is contained in:
2026-09-12 22:07:29 +03:00
parent 9bb385b9f0
commit cf21a5bd17
2 changed files with 129 additions and 31 deletions
+41
View File
@@ -1,3 +1,30 @@
.card {
position: relative;
width: 100%;
max-width: 28rem;
}
.cardActions {
position: absolute;
top: 4.5rem;
right: clamp(1.1rem, 3vw, 1.6rem);
padding: 4px;
border: 1px solid var(--mantine-color-gray-3);
border-radius: var(--mantine-radius-lg);
background: var(--mantine-color-white);
box-shadow: 0 3px 12px #0000001a;
}
@media (hover: hover) and (pointer: fine) {
.cardActions {
opacity: 0;
pointer-events: none;
}
.card:hover .cardActions,
.card:focus-within .cardActions {
opacity: 1;
pointer-events: auto;
}
}
.wrap {
overflow-wrap: anywhere;
min-width: 0;
@@ -21,7 +48,21 @@
.operations[data-pending] {
opacity: 0.55;
}
@media (pointer: coarse) {
.cardActions button {
min-width: 36px;
min-height: 36px;
}
}
@media (prefers-reduced-motion: no-preference) {
.cardActions {
transition: opacity 180ms ease;
}
.page button {
transition:
background-color 180ms ease,
color 180ms ease;
}
.operations {
animation: operations-appear 200ms ease-out;
transition: opacity 150ms ease;
+88 -31
View File
@@ -4,7 +4,6 @@ import {
Button,
Group,
Pagination,
Paper,
Select,
Skeleton,
Stack,
@@ -124,41 +123,76 @@ const CardPageView: FC<CardPageViewProps> = (props) => {
);
}
return (
<Stack gap="lg">
<Paper p="md" withBorder>
<Stack>
<Title order={1} className={classes.wrap}>
{card.name}
</Title>
<Stack gap="lg" className={classes.page}>
<Stack>
<Title order={1} className={classes.wrap}>
{card.name}
</Title>
<div className={classes.card}>
<BankCardFace card={card} />
<Text c="dimmed">{t("bank.manualBalance")}</Text>
<Group>
<Button
disabled={Boolean(card.archivedAt) || !vm.cards.data}
onClick={() => vm.setOperation("new")}
<Group gap={4} wrap="nowrap" className={classes.cardActions}>
<ActionIcon
variant="subtle"
size="md"
radius="md"
aria-label={t("bank.editCard")}
title={t("bank.editCard")}
onClick={() => vm.setEditingCard(true)}
>
{t("bank.addOperation")}
</Button>
<Button variant="default" onClick={() => vm.setEditingCard(true)}>
{t("bank.editCard")}
</Button>
<Button
variant="default"
<svg
width="18"
height="18"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
aria-hidden="true"
>
<path d="m16 3 5 5-12 12-6 1 1-6L16 3Z M13 6l5 5" />
</svg>
</ActionIcon>
<ActionIcon
variant="subtle"
size="md"
radius="md"
color="red"
aria-label={t(card.archivedAt ? "bank.restore" : "bank.archive")}
title={t(card.archivedAt ? "bank.restore" : "bank.archive")}
loading={vm.archivePending}
onClick={() => void vm.toggleArchive()}
>
{t(card.archivedAt ? "bank.restore" : "bank.archive")}
</Button>
<svg
width="18"
height="18"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
aria-hidden="true"
>
<path
d={
card.archivedAt
? "M3 3h18v4H3z M5 7v14h14V7 M12 17V10m-3 3 3-3 3 3"
: "M3 6h18 M9 6V3h6v3 M5 6l1 15h12l1-15 M10 10v7 M14 10v7"
}
/>
</svg>
</ActionIcon>
</Group>
{vm.archiveError ? (
<BankFeedback
error={vm.archiveError}
retry={() => void vm.card.refetch()}
/>
) : null}
{card.archivedAt && <Text>{t("bank.archiveHint")}</Text>}
</Stack>
</Paper>
</div>
{vm.archiveError ? (
<BankFeedback
error={vm.archiveError}
retry={() => void vm.card.refetch()}
/>
) : null}
{card.archivedAt && <Text>{t("bank.archiveHint")}</Text>}
</Stack>
{vm.card.isError && (
<Group role="alert">
<Text>{t("bank.refreshError")}</Text>
@@ -185,7 +219,30 @@ const CardPageView: FC<CardPageViewProps> = (props) => {
retry={() => void vm.summary.refetch()}
/>
<Group justify="space-between" align="end">
<Title order={2}>{t("bank.operations")}</Title>
<Group gap="xs">
<Title order={2}>{t("bank.operations")}</Title>
<ActionIcon
variant="subtle"
size="lg"
aria-label={t("bank.addOperation")}
title={t("bank.addOperation")}
disabled={Boolean(card.archivedAt) || !vm.cards.data}
onClick={() => vm.setOperation("new")}
>
<svg
width="20"
height="20"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
aria-hidden="true"
>
<path d="M12 5v14 M5 12h14" />
</svg>
</ActionIcon>
</Group>
<Select
label={t("bank.pageSize")}
value={vm.pageSize}