149 lines
5.6 KiB
TypeScript
149 lines
5.6 KiB
TypeScript
import { useSubmit } from "react-router";
|
|
import { useTranslation } from "react-i18next";
|
|
import { PlanFeatureUsageDto } from "~/application/dtos/subscriptions/PlanFeatureUsageDto";
|
|
import MyProducts from "~/components/core/settings/subscription/MyProducts";
|
|
import MyInvoices from "~/components/core/settings/subscription/MyInvoices";
|
|
import SettingSection from "~/components/ui/sections/SettingSection";
|
|
import { TenantSubscriptionProductWithDetails } from "~/utils/db/subscriptions/tenantSubscriptionProducts.db.server";
|
|
import { TenantSubscriptionWithDetails } from "~/utils/db/tenantSubscriptions.db.server";
|
|
import { useRootData } from "~/utils/data/useRootData";
|
|
import type { CustomerBillingInvoiceDto } from "~/application/dtos/billing/CustomerBillingInvoiceDto";
|
|
import type { SubscriptionProductDto } from "~/application/dtos/subscriptions/SubscriptionProductDto";
|
|
import type { SubscriptionBillingPeriod } from "~/application/enums/subscriptions/SubscriptionBillingPeriod";
|
|
import PlansGrouped from "~/components/core/settings/subscription/plans/PlansGrouped";
|
|
import SeatUsageSection from "~/components/core/settings/subscription/SeatUsageSection";
|
|
import { Alert } from "~/ui/components/Alert";
|
|
import type { SeatUsageVM, SubscriptionBannerTone, SubscriptionBannerVM } from "@hivegrid/view-models";
|
|
|
|
function bannerVariant(tone: SubscriptionBannerTone): "success" | "warning" | "error" {
|
|
if (tone === "success") return "success";
|
|
if (tone === "warning") return "warning";
|
|
return "error";
|
|
}
|
|
|
|
export default function SubscriptionSettings({
|
|
mySubscription,
|
|
myInvoices,
|
|
availablePlans,
|
|
currenciesAndPeriod,
|
|
invoiceErrorKey,
|
|
permissions,
|
|
billingBanner,
|
|
seatUsage,
|
|
}: {
|
|
mySubscription: TenantSubscriptionWithDetails | null;
|
|
myFeatures: PlanFeatureUsageDto[];
|
|
myInvoices: CustomerBillingInvoiceDto[];
|
|
availablePlans: SubscriptionProductDto[];
|
|
currenciesAndPeriod: {
|
|
currencies: { value: string; options: string[] };
|
|
billingPeriods: { value: SubscriptionBillingPeriod; options: SubscriptionBillingPeriod[] };
|
|
};
|
|
invoiceErrorKey?: string | null;
|
|
permissions: { viewInvoices: boolean; manageSubscription: boolean };
|
|
billingBanner?: SubscriptionBannerVM;
|
|
seatUsage?: SeatUsageVM;
|
|
storageLimitGb?: number | null;
|
|
}) {
|
|
const { t } = useTranslation();
|
|
const submit = useSubmit();
|
|
const { csrf } = useRootData();
|
|
|
|
function onCancel(item: TenantSubscriptionProductWithDetails) {
|
|
const form = new FormData();
|
|
form.set("action", "cancel");
|
|
form.set("csrf", csrf ?? "");
|
|
form.set("tenant-subscription-product-id", item.id);
|
|
submit(form, {
|
|
method: "post",
|
|
});
|
|
}
|
|
|
|
// function onAddPaymentMethod() {
|
|
// const form = new FormData();
|
|
// form.set("action", "add-payment-method");
|
|
// submit(form, {
|
|
// method: "post",
|
|
// });
|
|
// }
|
|
|
|
return (
|
|
<div className="space-y-4" data-ui-id="ui.subscription.page">
|
|
{billingBanner?.status === "ready" ? (
|
|
<Alert
|
|
variant={bannerVariant(billingBanner.data.tone)}
|
|
notDismissible
|
|
title={t(billingBanner.data.titleKey)}
|
|
description={t(billingBanner.data.descriptionKey)}
|
|
data-ui-id="ui.subscription.statusBanner"
|
|
/>
|
|
) : null}
|
|
<SettingSection
|
|
title={t("settings.subscription.title")}
|
|
description={
|
|
<div className="flex flex-col space-y-1">
|
|
<div>{t("settings.subscription.description")}</div>
|
|
</div>
|
|
}
|
|
className=""
|
|
>
|
|
<MyProducts items={mySubscription?.products ?? []} onCancel={onCancel} />
|
|
</SettingSection>
|
|
|
|
{seatUsage && seatUsage.status !== "idle" && (
|
|
<>
|
|
<div className="hidden sm:block" aria-hidden="true">
|
|
<div className="py-4">
|
|
<div className="border-border border-t"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<SettingSection title={t("settings.subscription.seats.title")} description={t("settings.subscription.seats.description")} className="">
|
|
<SeatUsageSection vm={seatUsage} />
|
|
</SettingSection>
|
|
</>
|
|
)}
|
|
|
|
{availablePlans.length > 0 && (
|
|
<>
|
|
<div className="hidden sm:block" aria-hidden="true">
|
|
<div className="py-4">
|
|
<div className="border-border border-t"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<SettingSection title={t("settings.subscription.checkout.plans.title")} description={t("settings.subscription.checkout.plans.description")} className="">
|
|
<PlansGrouped
|
|
items={availablePlans}
|
|
tenantSubscription={mySubscription}
|
|
canSubmit={permissions.manageSubscription}
|
|
billingProviderCoupon={null}
|
|
currenciesAndPeriod={currenciesAndPeriod}
|
|
/>
|
|
</SettingSection>
|
|
</>
|
|
)}
|
|
|
|
{permissions.viewInvoices && (
|
|
<>
|
|
<div className="hidden sm:block" aria-hidden="true">
|
|
<div className="py-4">
|
|
<div className="border-border border-t"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<SettingSection title={t("app.subscription.invoices.title")} description={t("app.subscription.invoices.description")} className="">
|
|
<MyInvoices items={myInvoices} error={invoiceErrorKey ? t(invoiceErrorKey) : null} />
|
|
</SettingSection>
|
|
</>
|
|
)}
|
|
|
|
{/* <SettingSection title={t("app.subscription.paymentMethods.title")} description={t("app.subscription.paymentMethods.description")}>
|
|
<div className="space-y-2">
|
|
<MyPaymentMethods items={myPaymentMethods} onAdd={onAddPaymentMethod} onDelete={onDeletePaymentMethod} />
|
|
</div>
|
|
</SettingSection> */}
|
|
</div>
|
|
);
|
|
}
|