From 06f2ac541b2bd887202e4161c0f07492f86672af Mon Sep 17 00:00:00 2001 From: Arthur Belleville Date: Mon, 1 Dec 2025 19:34:30 +0100 Subject: [PATCH] Onboarding + freemium --- apps/main/src/components/Layout.tsx | 21 +- apps/main/src/components/OnboardingModal.tsx | 227 ++++++++++++++++++ apps/main/src/components/SubscriptionCard.tsx | 30 ++- apps/main/src/hooks/stripe.ts | 4 +- apps/main/src/i18n.ts | 4 + apps/main/src/locales/en/onboarding.json | 67 ++++++ apps/main/src/locales/fr/onboarding.json | 67 ++++++ apps/main/stats.html | 2 +- packages/shared-types/src/database.types.ts | 4 +- ...251201163807_add_is_freemium_available.sql | 96 ++++++++ supabase/tests/database/05_triggers.test.sql | 98 +++++++- xtablo-expo/lib/database.types.ts | 4 +- 12 files changed, 610 insertions(+), 14 deletions(-) create mode 100644 apps/main/src/components/OnboardingModal.tsx create mode 100644 apps/main/src/locales/en/onboarding.json create mode 100644 apps/main/src/locales/fr/onboarding.json create mode 100644 supabase/migrations/20251201163807_add_is_freemium_available.sql diff --git a/apps/main/src/components/Layout.tsx b/apps/main/src/components/Layout.tsx index 0f4c68b..c7cd8bf 100644 --- a/apps/main/src/components/Layout.tsx +++ b/apps/main/src/components/Layout.tsx @@ -1,15 +1,34 @@ import { Button } from "@xtablo/ui/components/button"; import { MenuIcon } from "lucide-react"; -import { useState } from "react"; +import { useEffect, useState } from "react"; import { Outlet } from "react-router-dom"; import { twMerge } from "tailwind-merge"; import { SideNavigation } from "./NavigationBar"; +import { OnboardingModal } from "./OnboardingModal"; + +const ONBOARDING_STORAGE_KEY = "xtablo-onboarding-completed"; export function Layout() { const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false); + const [showOnboarding, setShowOnboarding] = useState(false); + + useEffect(() => { + // Check if user has completed onboarding + const hasCompletedOnboarding = localStorage.getItem(ONBOARDING_STORAGE_KEY); + if (!hasCompletedOnboarding) { + setShowOnboarding(false); + } + }, []); + + const handleOnboardingComplete = () => { + localStorage.setItem(ONBOARDING_STORAGE_KEY, "true"); + setShowOnboarding(false); + }; return (
+ + +
+ {!isFirstStep && ( + + )} + +
+ + + + ); +} diff --git a/apps/main/src/components/SubscriptionCard.tsx b/apps/main/src/components/SubscriptionCard.tsx index a4fdb69..3be6dab 100644 --- a/apps/main/src/components/SubscriptionCard.tsx +++ b/apps/main/src/components/SubscriptionCard.tsx @@ -15,6 +15,7 @@ import { useCreatePortalSession, useReactivateSubscription, useSubscription, + useTrialExpiration, } from "../hooks/stripe"; import { useUser } from "../providers/UserStoreProvider"; @@ -31,8 +32,13 @@ export function SubscriptionCard() { const { mutate: reactivateSubscription, isPending: reactivatePending } = useReactivateSubscription(); - const isPaying = user.plan !== "none" && user.plan !== "beta"; + const { daysRemaining } = useTrialExpiration(); + + const isPaying = user.plan === "trial" || user.plan === "standard"; const isBeta = user.plan === "beta"; + const isFreemium = user.plan === "free"; + + const showTrialBanner = user.plan === "none"; // Replace with your actual price ID from Stripe Dashboard const STANDARD_MONTHLY_PRICE_ID = import.meta.env.VITE_STRIPE_STANDARD_MONTHLY_PRICE_ID || ""; @@ -138,16 +144,15 @@ export function SubscriptionCard() {
)} - {/* Free Tier */} - {!isPaying && !isBeta && ( + {showTrialBanner && (

- Plan Gratuit + Accès gratuit pendant 7 jours

- Fonctionnalités limitées + Il vous reste {daysRemaining} jours pour passer à Standard.

@@ -182,6 +187,21 @@ export function SubscriptionCard() {
)} + {isFreemium && ( +
+
+
+

+ Plan Freemium +

+

+ Vous profitez d'un accès gratuit, un seul tablo ne peut être créé. +

+
+
+
+ )} + {/* Standard Tier - Active */} {isPaying && subscription && !subscription.cancel_at_period_end && (
diff --git a/apps/main/src/hooks/stripe.ts b/apps/main/src/hooks/stripe.ts index 53cbf4b..ec6d4f9 100644 --- a/apps/main/src/hooks/stripe.ts +++ b/apps/main/src/hooks/stripe.ts @@ -54,9 +54,11 @@ export function useSubscription() { export function useIsPayingUser() { const user = useUser(); + const isPaying = user.plan === "trial" || user.plan === "standard"; + // Direct access from user profile (fastest) return { - data: user.plan !== "none", + data: isPaying, isLoading: false, }; } diff --git a/apps/main/src/i18n.ts b/apps/main/src/i18n.ts index a9007bc..7441806 100644 --- a/apps/main/src/i18n.ts +++ b/apps/main/src/i18n.ts @@ -8,6 +8,7 @@ import componentsEn from "./locales/en/components.json"; import modalsEn from "./locales/en/modals.json"; import navigationEn from "./locales/en/navigation.json"; import notesEn from "./locales/en/notes.json"; +import onboardingEn from "./locales/en/onboarding.json"; import pagesEn from "./locales/en/pages.json"; import planningEn from "./locales/en/planning.json"; import settingsEn from "./locales/en/settings.json"; @@ -20,6 +21,7 @@ import componentsFr from "./locales/fr/components.json"; import modalsFr from "./locales/fr/modals.json"; import navigationFr from "./locales/fr/navigation.json"; import notesFr from "./locales/fr/notes.json"; +import onboardingFr from "./locales/fr/onboarding.json"; import pagesFr from "./locales/fr/pages.json"; import planningFr from "./locales/fr/planning.json"; import settingsFr from "./locales/fr/settings.json"; @@ -42,6 +44,7 @@ i18n components: componentsFr, notes: notesFr, tablo: tabloFr, + onboarding: onboardingFr, }, en: { common: commonEn, @@ -55,6 +58,7 @@ i18n components: componentsEn, notes: notesEn, tablo: tabloEn, + onboarding: onboardingEn, }, }, lng: "fr", diff --git a/apps/main/src/locales/en/onboarding.json b/apps/main/src/locales/en/onboarding.json new file mode 100644 index 0000000..71a6547 --- /dev/null +++ b/apps/main/src/locales/en/onboarding.json @@ -0,0 +1,67 @@ +{ + "modal": { + "skipTutorial": "Skip tutorial", + "back": "Back", + "next": "Next", + "getStarted": "Get Started", + "steps": { + "welcome": { + "title": "Welcome to Xtablo", + "description": "Your all-in-one platform for seamless client onboarding", + "content": { + "intro": "Xtablo helps you streamline your client relationships from the very first interaction to ongoing collaboration.", + "features": "Whether you're managing projects, scheduling meetings, or staying in touch with clients, Xtablo brings everything together in one beautiful interface." + } + }, + "tablos": { + "title": "Organize with Tablos", + "description": "Create dedicated spaces for each client or project", + "content": { + "intro": "A Tablo is your workspace for managing a client relationship.", + "feature1": "Keep all client files, documents, and information in one place", + "feature2": "Invite team members and clients to collaborate", + "feature3": "Track progress and manage tasks effortlessly" + } + }, + "planning": { + "title": "Schedule & Plan", + "description": "Manage your calendar and client availability", + "content": { + "intro": "Never miss a meeting or deadline with integrated planning tools.", + "feature1": "Create events and sync them across your team", + "feature2": "Set your availability and let clients book time with you", + "feature3": "Get notifications for upcoming events" + } + }, + "chat": { + "title": "Communicate & Collaborate", + "description": "Built-in chat and notes for seamless teamwork", + "content": { + "intro": "Keep conversations organized and accessible to everyone who needs them.", + "feature1": "Real-time chat with your team and clients", + "feature2": "Share notes and important updates", + "feature3": "Keep a history of all project communications" + } + }, + "ready": { + "title": "Ready to Get Started?", + "description": "Let's create your first Tablo and onboard your first client", + "content": { + "intro": "You're all set! Here are some quick actions to get you started:", + "action1": { + "title": "1. Create your first Tablo", + "description": "Click \"New Tablo\" to create a workspace for your first client" + }, + "action2": { + "title": "2. Invite your team", + "description": "Add team members to collaborate on client projects" + }, + "action3": { + "title": "3. Explore the features", + "description": "Check out Planning, Chat, and Notes to see what Xtablo can do" + } + } + } + } + } +} diff --git a/apps/main/src/locales/fr/onboarding.json b/apps/main/src/locales/fr/onboarding.json new file mode 100644 index 0000000..07dcebf --- /dev/null +++ b/apps/main/src/locales/fr/onboarding.json @@ -0,0 +1,67 @@ +{ + "modal": { + "skipTutorial": "Passer le tutoriel", + "back": "Retour", + "next": "Suivant", + "getStarted": "Commencer", + "steps": { + "welcome": { + "title": "Bienvenue sur Xtablo", + "description": "Votre plateforme tout-en-un pour l'intégration fluide de vos clients", + "content": { + "intro": "Xtablo vous aide à rationaliser vos relations clients de la première interaction à la collaboration continue.", + "features": "Que vous gériez des projets, planifiez des réunions ou restiez en contact avec vos clients, Xtablo rassemble tout dans une interface élégante." + } + }, + "tablos": { + "title": "Organisez avec les Tablos", + "description": "Créez des espaces dédiés pour chaque client ou projet", + "content": { + "intro": "Un Tablo est votre espace de travail pour gérer une relation client.", + "feature1": "Conservez tous les fichiers, documents et informations clients au même endroit", + "feature2": "Invitez des membres de l'équipe et des clients à collaborer", + "feature3": "Suivez l'avancement et gérez les tâches sans effort" + } + }, + "planning": { + "title": "Planifiez & Organisez", + "description": "Gérez votre calendrier et les disponibilités de vos clients", + "content": { + "intro": "Ne manquez jamais une réunion ou une échéance grâce aux outils de planification intégrés.", + "feature1": "Créez des événements et synchronisez-les avec votre équipe", + "feature2": "Définissez vos disponibilités et laissez les clients réserver du temps avec vous", + "feature3": "Recevez des notifications pour les événements à venir" + } + }, + "chat": { + "title": "Communiquez & Collaborez", + "description": "Chat et notes intégrés pour un travail d'équipe fluide", + "content": { + "intro": "Gardez les conversations organisées et accessibles à tous ceux qui en ont besoin.", + "feature1": "Chat en temps réel avec votre équipe et vos clients", + "feature2": "Partagez des notes et des mises à jour importantes", + "feature3": "Conservez un historique de toutes les communications du projet" + } + }, + "ready": { + "title": "Prêt à commencer ?", + "description": "Créons votre premier Tablo et intégrons votre premier client", + "content": { + "intro": "Vous êtes prêt ! Voici quelques actions rapides pour commencer :", + "action1": { + "title": "1. Créez votre premier Tablo", + "description": "Cliquez sur \"Nouveau Tablo\" pour créer un espace de travail pour votre premier client" + }, + "action2": { + "title": "2. Invitez votre équipe", + "description": "Ajoutez des membres de l'équipe pour collaborer sur les projets clients" + }, + "action3": { + "title": "3. Explorez les fonctionnalités", + "description": "Découvrez la Planification, le Chat et les Notes pour voir ce que Xtablo peut faire" + } + } + } + } + } +} diff --git a/apps/main/stats.html b/apps/main/stats.html index baa29c9..8ebb3a4 100644 --- a/apps/main/stats.html +++ b/apps/main/stats.html @@ -4929,7 +4929,7 @@ var drawChart = (function (exports) {