feat(onboarding): show recurring monthly fee in the wizard cost summary
All checks were successful
Build and Push / build (push) Successful in 1m42s
All checks were successful
Build and Push / build (push) Successful in 1m42s
This commit is contained in:
@@ -81,6 +81,7 @@ export default async function NewInstancePage() {
|
|||||||
hasOrgBilling={hasOrgBilling}
|
hasOrgBilling={hasOrgBilling}
|
||||||
existingOrgBilling={orgBilling}
|
existingOrgBilling={orgBilling}
|
||||||
setupFeeChf={pricing.tenantSetupFeeChf}
|
setupFeeChf={pricing.tenantSetupFeeChf}
|
||||||
|
monthlyFeeChf={pricing.tenantMonthlyFeeChf}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -326,6 +326,7 @@ export default async function DashboardPage() {
|
|||||||
hasOrgBilling={hasOrgBilling}
|
hasOrgBilling={hasOrgBilling}
|
||||||
existingOrgBilling={orgBilling}
|
existingOrgBilling={orgBilling}
|
||||||
setupFeeChf={platformPricing.tenantSetupFeeChf}
|
setupFeeChf={platformPricing.tenantSetupFeeChf}
|
||||||
|
monthlyFeeChf={platformPricing.tenantMonthlyFeeChf}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -31,6 +31,12 @@ interface OnboardingFlowProps {
|
|||||||
* step. Forwarded straight to the wizard.
|
* step. Forwarded straight to the wizard.
|
||||||
*/
|
*/
|
||||||
setupFeeChf?: number | null;
|
setupFeeChf?: number | null;
|
||||||
|
/**
|
||||||
|
* Recurring per-tenant monthly fee (net CHF). Forwarded to the
|
||||||
|
* wizard's review-step cost summary so the customer sees the ongoing
|
||||||
|
* commitment, not just the one-time setup fee.
|
||||||
|
*/
|
||||||
|
monthlyFeeChf?: number | null;
|
||||||
/**
|
/**
|
||||||
* Bug 6: when present, the wizard is rendered in edit mode against
|
* Bug 6: when present, the wizard is rendered in edit mode against
|
||||||
* the given pending request. See `OnboardingWizard` for the full
|
* the given pending request. See `OnboardingWizard` for the full
|
||||||
@@ -59,6 +65,7 @@ export function OnboardingFlow({
|
|||||||
hasOrgBilling,
|
hasOrgBilling,
|
||||||
existingOrgBilling,
|
existingOrgBilling,
|
||||||
setupFeeChf,
|
setupFeeChf,
|
||||||
|
monthlyFeeChf,
|
||||||
editingRequest,
|
editingRequest,
|
||||||
}: OnboardingFlowProps) {
|
}: OnboardingFlowProps) {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
@@ -71,6 +78,7 @@ export function OnboardingFlow({
|
|||||||
hasOrgBilling={hasOrgBilling}
|
hasOrgBilling={hasOrgBilling}
|
||||||
existingOrgBilling={existingOrgBilling}
|
existingOrgBilling={existingOrgBilling}
|
||||||
setupFeeChf={setupFeeChf}
|
setupFeeChf={setupFeeChf}
|
||||||
|
monthlyFeeChf={monthlyFeeChf}
|
||||||
editingRequest={editingRequest}
|
editingRequest={editingRequest}
|
||||||
onComplete={() => {
|
onComplete={() => {
|
||||||
// Navigate back to /dashboard and re-fetch on the server. The
|
// Navigate back to /dashboard and re-fetch on the server. The
|
||||||
|
|||||||
@@ -117,6 +117,13 @@ interface WizardProps {
|
|||||||
* the order skips the Checkout redirect (handled server-side).
|
* the order skips the Checkout redirect (handled server-side).
|
||||||
*/
|
*/
|
||||||
setupFeeChf?: number | null;
|
setupFeeChf?: number | null;
|
||||||
|
/**
|
||||||
|
* The platform's recurring per-tenant monthly fee (net CHF, before
|
||||||
|
* VAT). Shown on the review step alongside the setup fee so the
|
||||||
|
* customer sees the ongoing commitment — not just the one-time
|
||||||
|
* charge — before submitting. Null/0 hides the monthly line.
|
||||||
|
*/
|
||||||
|
monthlyFeeChf?: number | null;
|
||||||
/**
|
/**
|
||||||
* Bug 6: when present, the wizard renders in "edit" mode — fields
|
* Bug 6: when present, the wizard renders in "edit" mode — fields
|
||||||
* are pre-populated from the request, the SOUL.md auto-fetch is
|
* are pre-populated from the request, the SOUL.md auto-fetch is
|
||||||
@@ -157,6 +164,7 @@ export function OnboardingWizard({
|
|||||||
hasOrgBilling,
|
hasOrgBilling,
|
||||||
existingOrgBilling,
|
existingOrgBilling,
|
||||||
setupFeeChf,
|
setupFeeChf,
|
||||||
|
monthlyFeeChf,
|
||||||
editingRequest,
|
editingRequest,
|
||||||
onComplete,
|
onComplete,
|
||||||
}: WizardProps) {
|
}: WizardProps) {
|
||||||
@@ -1382,28 +1390,46 @@ export function OnboardingWizard({
|
|||||||
|
|
||||||
<p className="text-xs text-text-muted">{t("confirmNote")}</p>
|
<p className="text-xs text-text-muted">{t("confirmNote")}</p>
|
||||||
|
|
||||||
{/* Phase 9b: order-time setup-fee notice + amount. The
|
{/* Cost summary. Surfaces the full commitment before
|
||||||
figure shown is the net platform fee (before VAT);
|
submitting — not just the one-time setup fee but the
|
||||||
VAT is added server-side based on the billing
|
recurring monthly per-assistant fee and the fact that
|
||||||
country. We show "+ VAT" rather than a computed
|
AI usage is billed by consumption (with the budget-cap
|
||||||
gross to avoid mis-displaying a country-dependent
|
control as the reassurance). All figures are net (before
|
||||||
total. If setupFeeChf is null/0, no charge happens
|
VAT); VAT is added server-side per billing country, so
|
||||||
and the whole block is suppressed. */}
|
we show "+ VAT" rather than a country-dependent gross.
|
||||||
{typeof setupFeeChf === "number" && setupFeeChf > 0 && (
|
The block is suppressed only when there are no fixed
|
||||||
|
fees at all. */}
|
||||||
|
{((typeof setupFeeChf === "number" && setupFeeChf > 0) ||
|
||||||
|
(typeof monthlyFeeChf === "number" && monthlyFeeChf > 0)) && (
|
||||||
<div className="text-xs rounded-md border border-accent/30 bg-accent/10 text-text-secondary px-3 py-3 mt-4">
|
<div className="text-xs rounded-md border border-accent/30 bg-accent/10 text-text-secondary px-3 py-3 mt-4">
|
||||||
<strong className="block text-text-primary mb-1">
|
<strong className="block text-text-primary mb-2">
|
||||||
{t("setupFeeNoticeHeading")}
|
{t("costSummaryHeading")}
|
||||||
</strong>
|
</strong>
|
||||||
<div className="flex items-baseline justify-between mb-2 pb-2 border-b border-accent/20">
|
{typeof setupFeeChf === "number" && setupFeeChf > 0 && (
|
||||||
<span>{t("setupFeeAmountLabel")}</span>
|
<div className="flex items-baseline justify-between mb-1.5">
|
||||||
<span className="text-sm font-semibold text-text-primary">
|
<span>{t("costSetupLabel")}</span>
|
||||||
CHF {setupFeeChf.toFixed(2)}{" "}
|
<span className="text-sm font-semibold text-text-primary">
|
||||||
<span className="text-[10px] font-normal text-text-muted">
|
CHF {setupFeeChf.toFixed(2)}{" "}
|
||||||
{t("setupFeePlusVat")}
|
<span className="text-[10px] font-normal text-text-muted">
|
||||||
|
{t("setupFeePlusVat")}
|
||||||
|
</span>
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</div>
|
||||||
|
)}
|
||||||
|
{typeof monthlyFeeChf === "number" && monthlyFeeChf > 0 && (
|
||||||
|
<div className="flex items-baseline justify-between mb-1.5">
|
||||||
|
<span>{t("costMonthlyLabel")}</span>
|
||||||
|
<span className="text-sm font-semibold text-text-primary">
|
||||||
|
CHF {monthlyFeeChf.toFixed(2)}{" "}
|
||||||
|
<span className="text-[10px] font-normal text-text-muted">
|
||||||
|
{t("setupFeePlusVat")}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className="mt-2 pt-2 border-t border-accent/20 leading-relaxed">
|
||||||
|
{t("costUsageNote")}
|
||||||
</div>
|
</div>
|
||||||
{t("setupFeeNoticeBody")}
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user