Просмотр исходного кода

added option to set demo license (#197)

Bernt Christian Egeland 2 месяцев назад
Родитель
Сommit
f2b3529276

+ 3 - 1
messages/de/settings.json

@@ -1132,7 +1132,9 @@
     "upgradeCalculating": "Anteiligen Betrag berechnen…",
     "upgradeAmountDue": "Jetzt fälliger Betrag",
     "upgradeSuccess": "Erfolgreich auf Enterprise upgegradet!",
-    "upgradeError": "Abonnement konnte nicht upgegradet werden"
+    "upgradeError": "Abonnement konnte nicht upgegradet werden",
+    "demoBadge": "Demo",
+    "demoExpiresIn": "Läuft in {days, plural, =0 {weniger als einem Tag} one {# Tag} other {# Tagen}} ab ({date})"
   },
   "ai": {
     "title": "KI-Assistent",

+ 3 - 1
messages/en/settings.json

@@ -1132,7 +1132,9 @@
     "upgradeCalculating": "Calculating prorated amount…",
     "upgradeAmountDue": "Amount due now",
     "upgradeSuccess": "Successfully upgraded to Enterprise!",
-    "upgradeError": "Failed to upgrade subscription"
+    "upgradeError": "Failed to upgrade subscription",
+    "demoBadge": "Demo",
+    "demoExpiresIn": "Expires in {days, plural, =0 {less than a day} one {# day} other {# days}} ({date})"
   },
   "ai": {
     "title": "AI Assistant",

+ 3 - 1
messages/es/settings.json

@@ -1132,7 +1132,9 @@
     "upgradeCalculating": "Calculando el monto prorrateado…",
     "upgradeAmountDue": "Monto a pagar ahora",
     "upgradeSuccess": "¡Mejorado a Enterprise con éxito!",
-    "upgradeError": "Error al mejorar la suscripción"
+    "upgradeError": "Error al mejorar la suscripción",
+    "demoBadge": "Demo",
+    "demoExpiresIn": "Caduca en {days, plural, =0 {menos de un día} one {# día} other {# días}} ({date})"
   },
   "ai": {
     "title": "Asistente de IA",

+ 3 - 1
messages/fr/settings.json

@@ -1132,7 +1132,9 @@
     "upgradeCalculating": "Calcul du montant au prorata…",
     "upgradeAmountDue": "Montant dû maintenant",
     "upgradeSuccess": "Mis à niveau vers Enterprise avec succès !",
-    "upgradeError": "Échec de la mise à niveau de l'abonnement"
+    "upgradeError": "Échec de la mise à niveau de l'abonnement",
+    "demoBadge": "Démo",
+    "demoExpiresIn": "Expire dans {days, plural, =0 {moins d'un jour} one {# jour} other {# jours}} ({date})"
   },
   "ai": {
     "title": "Assistant IA",

+ 3 - 1
messages/it/settings.json

@@ -1132,7 +1132,9 @@
     "upgradeCalculating": "Calcolo dell'importo proporzionale…",
     "upgradeAmountDue": "Importo dovuto ora",
     "upgradeSuccess": "Aggiornato a Enterprise con successo!",
-    "upgradeError": "Aggiornamento dell'abbonamento non riuscito"
+    "upgradeError": "Aggiornamento dell'abbonamento non riuscito",
+    "demoBadge": "Demo",
+    "demoExpiresIn": "Scade tra {days, plural, =0 {meno di un giorno} one {# giorno} other {# giorni}} ({date})"
   },
   "ai": {
     "title": "Assistente IA",

+ 3 - 1
messages/lt/settings.json

@@ -1132,7 +1132,9 @@
     "upgradeCalculating": "Skaičiuojama proporcinga suma…",
     "upgradeAmountDue": "Mokėtina suma dabar",
     "upgradeSuccess": "Sėkmingai atnaujinta į Enterprise!",
-    "upgradeError": "Nepavyko atnaujinti prenumeratos"
+    "upgradeError": "Nepavyko atnaujinti prenumeratos",
+    "demoBadge": "Demo",
+    "demoExpiresIn": "Baigsis po {days, plural, =0 {mažiau nei dienos} one {# dienos} other {# dienų}} ({date})"
   },
   "ai": {
     "title": "DI asistentas",

+ 3 - 1
messages/nb/settings.json

@@ -1132,7 +1132,9 @@
     "upgradeCalculating": "Beregner forholdsmessig beløp…",
     "upgradeAmountDue": "Beløp å betale nå",
     "upgradeSuccess": "Vellykket oppgradert til Enterprise!",
-    "upgradeError": "Kunne ikke oppgradere abonnementet"
+    "upgradeError": "Kunne ikke oppgradere abonnementet",
+    "demoBadge": "Demo",
+    "demoExpiresIn": "Utløper om {days, plural, =0 {mindre enn én dag} one {# dag} other {# dager}} ({date})"
   },
   "ai": {
     "title": "AI-assistent",

+ 3 - 1
messages/nl/settings.json

@@ -1132,7 +1132,9 @@
     "upgradeCalculating": "Evenredig bedrag berekenen…",
     "upgradeAmountDue": "Nu te betalen bedrag",
     "upgradeSuccess": "Succesvol geüpgraded naar Enterprise!",
-    "upgradeError": "Upgrade van abonnement mislukt"
+    "upgradeError": "Upgrade van abonnement mislukt",
+    "demoBadge": "Demo",
+    "demoExpiresIn": "Verloopt over {days, plural, =0 {minder dan een dag} one {# dag} other {# dagen}} ({date})"
   },
   "ai": {
     "title": "AI-assistent",

+ 3 - 1
messages/pl/settings.json

@@ -1132,7 +1132,9 @@
     "upgradeCalculating": "Obliczanie proporcjonalnej kwoty…",
     "upgradeAmountDue": "Kwota do zapłaty teraz",
     "upgradeSuccess": "Pomyślnie ulepszono do Enterprise!",
-    "upgradeError": "Nie udało się ulepszyć subskrypcji"
+    "upgradeError": "Nie udało się ulepszyć subskrypcji",
+    "demoBadge": "Demo",
+    "demoExpiresIn": "Wygasa za {days, plural, =0 {mniej niż dzień} one {# dzień} other {# dni}} ({date})"
   },
   "ai": {
     "title": "Asystent AI",

+ 3 - 1
messages/pt-BR/settings.json

@@ -1132,7 +1132,9 @@
     "upgradeCalculating": "Calculando o valor proporcional…",
     "upgradeAmountDue": "Valor a pagar agora",
     "upgradeSuccess": "Atualizado para Enterprise com sucesso!",
-    "upgradeError": "Falha ao atualizar a assinatura"
+    "upgradeError": "Falha ao atualizar a assinatura",
+    "demoBadge": "Demo",
+    "demoExpiresIn": "Expira em {days, plural, =0 {menos de um dia} one {# dia} other {# dias}} ({date})"
   },
   "ai": {
     "title": "Assistente de IA",

+ 3 - 1
messages/ru/settings.json

@@ -1132,7 +1132,9 @@
     "upgradeCalculating": "Расчёт пропорциональной суммы...",
     "upgradeAmountDue": "К оплате сейчас",
     "upgradeSuccess": "Успешно обновлено до корпоративного тарифа!",
-    "upgradeError": "Не удалось обновить подписку"
+    "upgradeError": "Не удалось обновить подписку",
+    "demoBadge": "Демо",
+    "demoExpiresIn": "Истекает через {days, plural, =0 {меньше дня} one {# день} other {# дней}} ({date})"
   },
   "ai": {
     "title": "ИИ-ассистент",

+ 3 - 1
messages/tr/settings.json

@@ -1132,7 +1132,9 @@
     "upgradeCalculating": "Orantılı tutar hesaplanıyor…",
     "upgradeAmountDue": "Şimdi ödenecek tutar",
     "upgradeSuccess": "Enterprise'a başarıyla yükseltildi!",
-    "upgradeError": "Abonelik yükseltilemedi"
+    "upgradeError": "Abonelik yükseltilemedi",
+    "demoBadge": "Demo",
+    "demoExpiresIn": "{days, plural, =0 {bir günden az} one {# gün} other {# gün}} içinde sona erer ({date})"
   },
   "ai": {
     "title": "Yapay Zeka Asistani",

+ 6 - 0
src/app/(authenticated)/settings/subscription/page.tsx

@@ -21,6 +21,11 @@ export default async function SubscriptionPage() {
     ? (subscription.plan.name.toLowerCase() === "enterprise" ? "enterprise" : "pro")
     : "free";
 
+  // A demo is a trialing subscription not backed by Stripe (granted from the
+  // admin panel). It carries full plan features but expires at currentPeriodEnd.
+  const isDemo =
+    subscription?.status === "trialing" && !subscription?.stripeSubscriptionId;
+
   const features = PLAN_FEATURES[plan];
 
   const [customerCount, memberCount] = await Promise.all([
@@ -35,6 +40,7 @@ export default async function SubscriptionPage() {
   return (
     <SubscriptionSettings
       plan={plan}
+      isDemo={isDemo}
       status={subscription?.status ?? null}
       cancelAtPeriodEnd={subscription?.cancelAtPeriodEnd ?? false}
       currentPeriodEnd={subscription?.currentPeriodEnd?.toISOString() ?? null}

+ 36 - 8
src/features/subscription/Components/subscription-settings.tsx

@@ -40,6 +40,7 @@ import type { PlanFeatures } from "@/lib/features";
 
 type Props = {
   plan: string;
+  isDemo: boolean;
   status: string | null;
   cancelAtPeriodEnd: boolean;
   currentPeriodEnd: string | null;
@@ -53,6 +54,7 @@ type Props = {
 
 export function SubscriptionSettings({
   plan,
+  isDemo,
   status,
   cancelAtPeriodEnd,
   currentPeriodEnd,
@@ -82,6 +84,17 @@ export function SubscriptionSettings({
   const isCanceling = cancelAtPeriodEnd && status === "active";
   const isPastDue = status === "past_due";
 
+  // Whole days left until a demo expires (0 once past the expiry date).
+  const demoDaysRemaining =
+    isDemo && currentPeriodEnd
+      ? Math.max(
+          0,
+          Math.ceil(
+            (new Date(currentPeriodEnd).getTime() - Date.now()) / 86_400_000,
+          ),
+        )
+      : null;
+
   const handleCheckout = async (selectedPlan: "pro" | "enterprise") => {
     setCheckoutLoading(selectedPlan);
     try {
@@ -271,16 +284,31 @@ export function SubscriptionSettings({
           <div className="flex items-center gap-3">
             <Label>{t("subscription.currentPlan")}:</Label>
             {planBadge}
-            {statusBadge}
+            {isDemo ? (
+              <Badge variant="outline" className="border-amber-500 text-amber-600">
+                {t("subscription.demoBadge")}
+              </Badge>
+            ) : (
+              statusBadge
+            )}
           </div>
 
-          {isPaid && planPrice > 0 && (
+          {isDemo && currentPeriodEnd && (
+            <div className="text-sm text-amber-600">
+              {t("subscription.demoExpiresIn", {
+                days: demoDaysRemaining ?? 0,
+                date: formatDate(currentPeriodEnd),
+              })}
+            </div>
+          )}
+
+          {isPaid && !isDemo && planPrice > 0 && (
             <div className="text-sm text-muted-foreground">
               ${planPrice}/{intervalLabel}
             </div>
           )}
 
-          {isPaid && currentPeriodStart && currentPeriodEnd && (
+          {isPaid && !isDemo && currentPeriodStart && currentPeriodEnd && (
             <div className="flex flex-col gap-1 text-sm">
               <div className="flex items-center gap-2">
                 <span className="text-muted-foreground">{t("subscription.billingPeriod")}:</span>
@@ -353,8 +381,8 @@ export function SubscriptionSettings({
         </CardContent>
       </Card>
 
-      {/* Card 3: Manage Subscription */}
-      {isPaid && (
+      {/* Card 3: Manage Subscription — demos have no Stripe billing to manage */}
+      {isPaid && !isDemo && (
         <Card>
           <CardHeader>
             <CardTitle>{t("subscription.manageTitle")}</CardTitle>
@@ -420,8 +448,8 @@ export function SubscriptionSettings({
         </Card>
       )}
 
-      {/* Upgrade Section */}
-      {plan === "free" && (
+      {/* Upgrade Section — free users and demo users subscribe via Stripe Checkout */}
+      {(plan === "free" || isDemo) && (
         <Card>
           <CardHeader>
             <CardTitle>{t("subscription.upgradeTitle")}</CardTitle>
@@ -460,7 +488,7 @@ export function SubscriptionSettings({
         </Card>
       )}
 
-      {plan === "pro" && (
+      {plan === "pro" && !isDemo && (
         <Card>
           <CardHeader>
             <CardTitle>{t("subscription.upgradeTitle")}</CardTitle>