first commit
Some checks failed
Build / run (push) Has been cancelled

This commit is contained in:
maher
2025-10-29 11:42:25 +01:00
commit 703f50a09d
4595 changed files with 385164 additions and 0 deletions

View File

@@ -0,0 +1,99 @@
import {useBillingUser} from '../use-billing-user';
import {BillingPlanPanel} from '../billing-plan-panel';
import {Trans} from '../../../i18n/trans';
import {useInvoices} from '../requests/use-invoices';
import {FormattedDate} from '../../../i18n/formatted-date';
import {FormattedCurrency} from '../../../i18n/formatted-currency';
import {Chip} from '../../../ui/forms/input-field/chip-field/chip';
import {OpenInNewIcon} from '../../../icons/material/OpenInNew';
import {Skeleton} from '../../../ui/skeleton/skeleton';
import {AnimatePresence, m} from 'framer-motion';
import {Invoice} from '../../invoice';
import {opacityAnimation} from '../../../ui/animation/opacity-animation';
import {useSettings} from '../../../core/settings/use-settings';
export function InvoiceHistoryPanel() {
const {user} = useBillingUser();
const query = useInvoices(user?.id!);
if (!user) return null;
const invoices = query.data?.invoices;
return (
<BillingPlanPanel title={<Trans message="Payment history" />}>
<div className="max-w-[464px]">
<AnimatePresence initial={false} mode="wait">
{query.isLoading ? (
<LoadingSkeleton key="loading-skeleton" />
) : (
<InvoiceList key="invoices" invoices={invoices} />
)}
</AnimatePresence>
</div>
</BillingPlanPanel>
);
}
interface InvoiceListProps {
invoices?: Invoice[];
}
function InvoiceList({invoices}: InvoiceListProps) {
const {base_url} = useSettings();
return (
<m.div {...opacityAnimation}>
{!invoices?.length ? (
<div className="text-muted italic">
<Trans message="No invoices yet" />
</div>
) : undefined}
{invoices?.map(invoice => (
<div
className="whitespace-nowrap text-base flex items-center justify-between gap-10 mb-14"
key={invoice.id}
>
<a
href={`${base_url}/billing/invoices/${invoice.uuid}`}
target="_blank"
className="flex items-center gap-8 hover:underline"
rel="noreferrer"
>
<FormattedDate date={invoice.created_at} />
<OpenInNewIcon size="xs" />
</a>
{invoice.subscription.price && (
<div>
<FormattedCurrency
value={invoice.subscription.price.amount}
currency={invoice.subscription.price.currency}
/>
</div>
)}
<Chip
size="xs"
color={invoice.paid ? 'positive' : 'danger'}
radius="rounded"
>
{invoice.paid ? (
<Trans message="Paid" />
) : (
<Trans message="Unpaid" />
)}
</Chip>
<div>{invoice.subscription.product?.name}</div>
</div>
))}
</m.div>
);
}
function LoadingSkeleton() {
return (
<m.div {...opacityAnimation}>
<Skeleton className="mb-14" />
<Skeleton className="mb-14" />
<Skeleton className="mb-14" />
<Skeleton className="mb-14" />
<Skeleton />
</m.div>
);
}