feat: unify income payment cards and remove comparisons (WealthLedger/main#33) (#74)
Frontend verify / commits (push) Skipped
Frontend verify / security (push) Successful in 10s
Frontend verify / verify (push) Successful in 7m18s

feat: unify income payment cards and remove comparisons (WealthLedger/main#33)
This commit was merged in pull request #74.
This commit is contained in:
2026-10-13 18:17:35 +03:00
parent b357743c10
commit 43b0f5574a
27 changed files with 474 additions and 1092 deletions
@@ -1,17 +1,20 @@
import type { FC } from "react";
import { Accordion, Alert, Badge, Group, Paper, Stack, Text } from "@mantine/core";
import { Accordion, Alert, Badge, Group, Stack, Text } from "@mantine/core";
import { useTranslation } from "react-i18next";
import type { BondCoupons as Coupons } from "@/entities/bond";
import { InstrumentMetric, InstrumentSection, type SectionState } from "@/features/instrument-details";
import { formatDate, formatMoney, formatSnapshotTime } from "@/shared/lib";
import { InstrumentSection, type SectionState } from "@/features/instrument-details";
import { formatDate, formatSnapshotTime, instrumentLogoUrl } from "@/shared/lib";
import { PayoutCard } from "@/shared/ui";
type BondCouponsProps = {
coupons?: Coupons;
logoName?: null | string;
pastCoupons: Coupons["coupons"];
state: SectionState;
ticker?: null | string;
upcoming: Coupons["coupons"];
};
@@ -39,6 +42,8 @@ const BondCoupons: FC<BondCouponsProps> = (props) => {
<Coupon
coupon={coupon}
key={`${coupon.number}:${coupon.paymentDate}`}
logoName={props.logoName}
ticker={props.ticker}
/>
))
)
@@ -75,6 +80,8 @@ const BondCoupons: FC<BondCouponsProps> = (props) => {
<Coupon
coupon={coupon}
key={`${coupon.number}:${coupon.paymentDate}`}
logoName={props.logoName}
ticker={props.ticker}
/>
))
)
@@ -91,59 +98,48 @@ const BondCoupons: FC<BondCouponsProps> = (props) => {
type CouponProps = {
coupon: Coupons["coupons"][number];
logoName?: null | string;
ticker?: null | string;
};
const Coupon: FC<CouponProps> = (props) => {
const { coupon } = props;
const { t } = useTranslation();
const title = `${t("events.type.coupon")} · ${props.ticker || t("bond.instrument")}`;
const metadata = [
coupon.paymentDate ? formatDate(coupon.paymentDate) : t("stock.dateUnknown"),
t("bond.couponNumber", { number: coupon.number ?? t("bond.unknown") }),
].join(" · ");
const conditions = (
<Group gap="md" mt="sm">
<Badge color="gray" variant="light">
{t(`bond.couponTypes.${coupon.couponType}`, { defaultValue: t("bond.unknown") })}
</Badge>
{coupon.periodDays != undefined && (
<Text c="dimmed" size="xs">{t("bond.couponDays", { count: coupon.periodDays })}</Text>
)}
{coupon.recordDate && (
<Text c="dimmed" size="xs">{t("stock.record")}: {formatDate(coupon.recordDate)}</Text>
)}
</Group>
);
return (
<Paper p="md" radius="md" withBorder>
<Group align="start" justify="space-between">
<div>
<Text fw={600}>
{coupon.paymentDate
? formatDate(coupon.paymentDate)
: t("stock.dateUnknown")}
</Text>
<Text c="dimmed" size="xs">
{t("bond.couponNumber", {
number: coupon.number ?? t("bond.unknown"),
})}
</Text>
</div>
<InstrumentMetric label={t("bond.perBond")} value={formatMoney(coupon.perBond)} />
{!!coupon.estimatedAmount && (
<InstrumentMetric
label={t("stock.yourEstimate")}
value={formatMoney(coupon.estimatedAmount)}
/>
)}
</Group>
<Group gap="md" mt="sm">
<Badge color="gray" variant="light">
{t(`bond.couponTypes.${coupon.couponType}`, {
defaultValue: t("bond.unknown"),
})}
</Badge>
{coupon.periodDays != undefined && (
<Text c="dimmed" size="xs">
{t("bond.couponDays", { count: coupon.periodDays })}
</Text>
)}
{!!coupon.recordDate && (
<Text c="dimmed" size="xs">
{t("stock.record")}: {formatDate(coupon.recordDate)}
</Text>
)}
</Group>
</Paper>
<PayoutCard
conditions={conditions}
estimate={coupon.estimatedAmount}
imageSrc={instrumentLogoUrl(props.logoName)}
initial={(props.ticker?.trim()[0] ?? "?").toUpperCase()}
kind="schedule"
metadata={metadata}
perSecurity={coupon.perBond}
perSecurityLabel={t("bond.perBond")}
title={title}
/>
);
};