139 lines
3.8 KiB
TypeScript
139 lines
3.8 KiB
TypeScript
import type { FC } from "react";
|
|
|
|
import { Accordion, Alert, Stack, Text } from "@mantine/core";
|
|
import { useTranslation } from "react-i18next";
|
|
|
|
import type { StockDividends as Dividends } from "@/entities/stock";
|
|
|
|
import { InstrumentSection, type SectionState } from "@/features/instrument-details";
|
|
import { formatDate, formatSnapshotTime, instrumentLogoUrl } from "@/shared/lib";
|
|
import { PayoutCard } from "@/shared/ui";
|
|
|
|
type Dividend = Dividends["dividends"][number];
|
|
|
|
type DividendCardProps = {
|
|
event: Dividend;
|
|
logoName?: null | string;
|
|
ticker?: null | string;
|
|
};
|
|
|
|
type StockDividendsProps = {
|
|
dividends?: Dividends;
|
|
logoName?: null | string;
|
|
pastDividends: Dividends["dividends"];
|
|
state: SectionState;
|
|
ticker?: null | string;
|
|
upcoming: Dividends["dividends"];
|
|
};
|
|
|
|
const DividendCard: FC<DividendCardProps> = (props) => {
|
|
const { event } = props;
|
|
const { t } = useTranslation();
|
|
const title = `${t("events.type.dividend")} · ${props.ticker || t("stock.share")}`;
|
|
|
|
const metadata = [
|
|
event.recordDate
|
|
? `${t("stock.record")}: ${formatDate(event.recordDate)}`
|
|
: t("stock.dateUnknown"),
|
|
event.paymentDate ? `${t("stock.payment")}: ${formatDate(event.paymentDate)}` : null,
|
|
].filter(Boolean).join(" · ");
|
|
|
|
const conditions = event.lastBuyDate
|
|
? <Text c="dimmed" size="xs">{t("stock.lastBuy")}: {formatDate(event.lastBuyDate)}</Text>
|
|
: undefined;
|
|
|
|
return (
|
|
<PayoutCard
|
|
conditions={conditions}
|
|
estimate={event.estimatedAmount}
|
|
imageSrc={instrumentLogoUrl(props.logoName)}
|
|
initial={(props.ticker?.trim()[0] ?? "?").toUpperCase()}
|
|
kind="schedule"
|
|
metadata={metadata}
|
|
perSecurity={event.perShare}
|
|
perSecurityLabel={t("stock.perShare")}
|
|
title={title}
|
|
/>
|
|
);
|
|
};
|
|
|
|
const StockDividends: FC<StockDividendsProps> = (props) => {
|
|
const { dividends, pastDividends, state, upcoming } = props;
|
|
const { t } = useTranslation();
|
|
|
|
return (
|
|
<InstrumentSection state={state} title={t("stock.upcoming")}>
|
|
{!!dividends && (
|
|
<Stack gap="md">
|
|
{(dividends.estimateUnavailable || dividends.positionIsStale) && (
|
|
<Alert aria-live="polite" color="orange" role="status">
|
|
{t("stock.estimateUnavailable")}
|
|
</Alert>
|
|
)}
|
|
|
|
{upcoming.length > 0
|
|
? upcoming.map(event => (
|
|
<DividendCard
|
|
event={event}
|
|
key={JSON.stringify([
|
|
event.recordDate,
|
|
event.paymentDate,
|
|
event.perShare,
|
|
])}
|
|
logoName={props.logoName}
|
|
ticker={props.ticker}
|
|
/>
|
|
))
|
|
: <Text c="dimmed">{t("stock.noUpcoming", { to: formatDate(dividends.to) })}</Text>}
|
|
|
|
<Text c="dimmed" size="xs">{t("stock.dividendNote")}</Text>
|
|
|
|
{!!dividends.positionAt && (
|
|
<Text c="dimmed" size="xs">
|
|
{t("stock.snapshotAt", { date: formatSnapshotTime(dividends.positionAt) })}
|
|
</Text>
|
|
)}
|
|
|
|
<Accordion radius="md" variant="contained">
|
|
<Accordion.Item value="history">
|
|
<Accordion.Control>
|
|
<Text fw={600}>{t("stock.dividendHistory")}</Text>
|
|
</Accordion.Control>
|
|
|
|
<Accordion.Panel>
|
|
<Text c="dimmed" my="sm" size="xs">
|
|
{t("stock.dividendWindow", {
|
|
from: formatDate(dividends.from),
|
|
to: formatDate(dividends.to),
|
|
})}
|
|
</Text>
|
|
|
|
{pastDividends.length > 0
|
|
? (
|
|
<Stack gap="sm">
|
|
{pastDividends.map(event => (
|
|
<DividendCard
|
|
event={event}
|
|
key={JSON.stringify([
|
|
event.recordDate,
|
|
event.paymentDate,
|
|
event.perShare,
|
|
])}
|
|
logoName={props.logoName}
|
|
ticker={props.ticker}
|
|
/>
|
|
))}
|
|
</Stack>
|
|
)
|
|
: <Text c="dimmed">{t("stock.noDividends")}</Text>}
|
|
</Accordion.Panel>
|
|
</Accordion.Item>
|
|
</Accordion>
|
|
</Stack>
|
|
)}
|
|
</InstrumentSection>
|
|
);
|
|
};
|
|
|
|
export { StockDividends };
|