Merge pull request #53 from artslidd/develop

Improve the onboarding flow (UI)
This commit is contained in:
Arthur Belleville 2025-12-02 22:19:26 +01:00 committed by GitHub
commit d7d42d2805
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 188 additions and 162 deletions

View file

@ -154,12 +154,12 @@ export function OnboardingModal({ open, onComplete }: OnboardingModalProps) {
if (stepKey === "welcome") {
return (
<div className="space-y-6 py-8">
<div className="text-center space-y-4">
<div className="mx-auto w-20 h-20 bg-linear-to-br from-primary/20 to-primary/5 rounded-2xl flex items-center justify-center">
<Sparkles className="h-10 w-10 text-primary" />
<div className="space-y-4 sm:space-y-6 py-4 sm:py-6 lg:py-8">
<div className="text-center space-y-3 sm:space-y-4">
<div className="mx-auto w-16 h-16 sm:w-20 sm:h-20 bg-linear-to-br from-primary/20 to-primary/5 rounded-2xl flex items-center justify-center">
<Sparkles className="h-8 w-8 sm:h-10 sm:w-10 text-primary" />
</div>
<p className="text-muted-foreground text-lg max-w-md mx-auto leading-relaxed">
<p className="text-muted-foreground text-base sm:text-lg max-w-md mx-auto leading-relaxed px-2">
{t("modal.steps.welcome.content.intro")}
</p>
</div>
@ -169,44 +169,50 @@ export function OnboardingModal({ open, onComplete }: OnboardingModalProps) {
if (stepKey === "profile") {
return (
<div className="space-y-6 py-4">
<div className="space-y-5">
<Label className="text-base font-semibold">{t("modal.steps.profile.question")}</Label>
<div className="space-y-4 sm:space-y-6 py-2 sm:py-4">
<div className="space-y-4 sm:space-y-5">
<Label className="text-sm sm:text-base font-semibold block mb-1 sm:mb-2">
{t("modal.steps.profile.question")}
</Label>
<RadioGroup
value={profileType || ""}
onValueChange={(value: string) => setProfileType(value as ProfileType)}
className="space-y-3"
className="space-y-2.5 sm:space-y-3"
>
<label
htmlFor="freelance"
className={`flex items-center space-x-4 rounded-xl border-2 p-5 cursor-pointer transition-all ${
className={`flex items-center space-x-3 sm:space-x-4 rounded-xl border-2 p-4 sm:p-5 cursor-pointer transition-all touch-manipulation ${
profileType === "freelance"
? "border-primary bg-primary/5 shadow-sm"
: "border-border hover:border-primary/50 hover:bg-accent/50"
: "border-border hover:border-primary/50 hover:bg-accent/50 active:bg-accent"
}`}
>
<RadioGroupItem value="freelance" id="freelance" />
<div className="flex items-center gap-3 flex-1">
<div className="p-2 bg-primary/10 rounded-lg">
<Briefcase className="h-5 w-5 text-primary" />
<RadioGroupItem value="freelance" id="freelance" className="shrink-0" />
<div className="flex items-center gap-2 sm:gap-3 flex-1 min-w-0">
<div className="p-1.5 sm:p-2 bg-primary/10 rounded-lg shrink-0">
<Briefcase className="h-4 w-4 sm:h-5 sm:w-5 text-primary" />
</div>
<span className="font-medium">{t("modal.steps.profile.options.freelance")}</span>
<span className="font-medium text-sm sm:text-base">
{t("modal.steps.profile.options.freelance")}
</span>
</div>
</label>
<label
htmlFor="agency"
className={`flex items-center space-x-4 rounded-xl border-2 p-5 cursor-pointer transition-all ${
className={`flex items-center space-x-3 sm:space-x-4 rounded-xl border-2 p-4 sm:p-5 cursor-pointer transition-all touch-manipulation ${
profileType === "agency"
? "border-primary bg-primary/5 shadow-sm"
: "border-border hover:border-primary/50 hover:bg-accent/50"
: "border-border hover:border-primary/50 hover:bg-accent/50 active:bg-accent"
}`}
>
<RadioGroupItem value="agency" id="agency" />
<div className="flex items-center gap-3 flex-1">
<div className="p-2 bg-primary/10 rounded-lg">
<Building2 className="h-5 w-5 text-primary" />
<RadioGroupItem value="agency" id="agency" className="shrink-0" />
<div className="flex items-center gap-2 sm:gap-3 flex-1 min-w-0">
<div className="p-1.5 sm:p-2 bg-primary/10 rounded-lg shrink-0">
<Building2 className="h-4 w-4 sm:h-5 sm:w-5 text-primary" />
</div>
<span className="font-medium">{t("modal.steps.profile.options.agency")}</span>
<span className="font-medium text-sm sm:text-base">
{t("modal.steps.profile.options.agency")}
</span>
</div>
</label>
</RadioGroup>
@ -217,9 +223,12 @@ export function OnboardingModal({ open, onComplete }: OnboardingModalProps) {
if (stepKey === "service") {
return (
<div className="space-y-6 py-4">
<div className="space-y-4">
<Label htmlFor="service" className="text-base font-semibold">
<div className="space-y-4 sm:space-y-6 py-2 sm:py-4 px-1">
<div>
<Label
htmlFor="service"
className="text-sm sm:text-base font-semibold block mb-1 sm:mb-2"
>
{t("modal.steps.service.question")}
</Label>
<Input
@ -227,7 +236,7 @@ export function OnboardingModal({ open, onComplete }: OnboardingModalProps) {
value={serviceName}
onChange={(e) => setServiceName(e.target.value)}
placeholder={t("modal.steps.service.placeholder")}
className="text-base h-12 px-4"
className="text-sm sm:text-base sm:h-12 px-4"
autoFocus
/>
</div>
@ -237,27 +246,27 @@ export function OnboardingModal({ open, onComplete }: OnboardingModalProps) {
if (stepKey === "structure") {
return (
<div className="space-y-6 py-4">
<div className="space-y-5">
<div className="space-y-4 sm:space-y-6 py-2 sm:py-4">
<div className="space-y-4 sm:space-y-5">
<div>
<Label className="text-base font-semibold">
<Label className="text-sm sm:text-base font-semibold">
{t("modal.steps.structure.question")}
</Label>
<p className="text-sm text-muted-foreground mt-1.5">
<p className="text-xs sm:text-sm text-muted-foreground mt-1 sm:mt-1.5">
{t("modal.steps.structure.steps_label")}
</p>
</div>
<div className="space-y-2.5 max-h-[280px] overflow-y-auto pr-1">
<div className="space-y-2 sm:space-y-2.5 max-h-[200px] sm:max-h-[280px] overflow-y-auto px-1 py-1">
{stepNames.map((step, index) => (
<div key={index} className="flex gap-2 items-center group">
<div className="flex items-center justify-center w-8 h-8 rounded-full bg-primary/10 text-primary text-sm font-semibold shrink-0">
<div className="flex items-center justify-center w-7 h-7 sm:w-8 sm:h-8 rounded-full bg-primary/10 text-primary text-xs sm:text-sm font-semibold shrink-0">
{index + 1}
</div>
<Input
value={step}
onChange={(e) => updateStepName(index, e.target.value)}
placeholder={t("modal.steps.structure.step_placeholder", { index: index + 1 })}
className="flex-1 h-11"
className="flex-1 h-10 sm:h-11 text-sm sm:text-base min-w-0"
/>
{stepNames.length > 2 && (
<Button
@ -265,9 +274,9 @@ export function OnboardingModal({ open, onComplete }: OnboardingModalProps) {
variant="ghost"
size="icon"
onClick={() => removeStep(index)}
className="shrink-0 opacity-0 group-hover:opacity-100 transition-opacity"
className="shrink-0 opacity-100 sm:opacity-0 sm:group-hover:opacity-100 transition-opacity h-10 w-10 sm:h-11 sm:w-11"
>
<Trash2 className="h-4 w-4 text-destructive" />
<Trash2 className="h-3.5 w-3.5 sm:h-4 sm:w-4 text-destructive" />
</Button>
)}
</div>
@ -277,10 +286,10 @@ export function OnboardingModal({ open, onComplete }: OnboardingModalProps) {
type="button"
variant="outline"
onClick={addStep}
className="w-full h-11 border-dashed"
className="w-full h-10 sm:h-11 border-dashed text-sm sm:text-base"
disabled={stepNames.length >= 10}
>
<Plus className="h-4 w-4 mr-2" />
<Plus className="h-3.5 w-3.5 sm:h-4 sm:w-4 mr-2" />
Ajouter une étape
</Button>
</div>
@ -290,9 +299,9 @@ export function OnboardingModal({ open, onComplete }: OnboardingModalProps) {
if (stepKey === "project") {
return (
<div className="space-y-6 py-4">
<div className="space-y-4">
<Label htmlFor="project" className="text-base font-semibold">
<div className="space-y-4 sm:space-y-6 py-2 sm:py-4">
<div className="space-y-3 sm:space-y-4">
<Label htmlFor="project" className="text-sm sm:text-base font-semibold">
{t("modal.steps.project.question")}
</Label>
<Input
@ -300,13 +309,13 @@ export function OnboardingModal({ open, onComplete }: OnboardingModalProps) {
value={projectName}
onChange={(e) => setProjectName(e.target.value)}
placeholder={t("modal.steps.project.placeholder")}
className="text-base h-12 px-4"
className="text-sm sm:text-base h-11 sm:h-12 px-4"
autoFocus
/>
{projectName && (
<div className="bg-accent/50 rounded-lg p-4 space-y-2">
<p className="text-sm font-medium">Récapitulatif :</p>
<div className="text-sm text-muted-foreground space-y-1">
<div className="bg-accent/50 rounded-lg p-3 sm:p-4 space-y-2">
<p className="text-xs sm:text-sm font-medium">Récapitulatif :</p>
<div className="text-xs sm:text-sm text-muted-foreground space-y-1">
<p>
<span className="font-medium">Service :</span> {serviceName}
</p>
@ -330,14 +339,16 @@ export function OnboardingModal({ open, onComplete }: OnboardingModalProps) {
if (stepKey === "welcome") {
return (
<div className="flex flex-col items-center justify-center h-full p-8 animate-fade-in">
<div className="flex flex-col items-center justify-center h-full p-6 lg:p-8 animate-fade-in">
<div className="relative">
<div className="absolute inset-0 bg-primary/20 blur-3xl rounded-full animate-pulse" />
<Sparkles className="h-32 w-32 text-primary relative animate-float" />
<Sparkles className="h-24 w-24 lg:h-32 lg:w-32 text-primary relative animate-float" />
</div>
<div className="mt-8 text-center space-y-2">
<h3 className="text-2xl font-bold">XTablo</h3>
<p className="text-muted-foreground">Votre espace de gestion projet</p>
<div className="mt-6 lg:mt-8 text-center space-y-2">
<h3 className="text-xl lg:text-2xl font-bold">XTablo</h3>
<p className="text-sm lg:text-base text-muted-foreground">
Votre espace de gestion projet
</p>
</div>
</div>
);
@ -345,92 +356,95 @@ export function OnboardingModal({ open, onComplete }: OnboardingModalProps) {
if (stepKey === "profile") {
return (
<div className="flex flex-col items-center justify-center h-full p-8 animate-fade-in">
<div className="flex flex-col items-center justify-center h-full p-4 lg:p-8 animate-fade-in">
<div className="relative w-full max-w-md">
{/* Modern Illustration */}
{profileType === "freelance" ? (
// Freelance illustration
<div className="relative bg-card border-2 border-primary/20 rounded-3xl p-8 shadow-2xl overflow-hidden mb-6">
<div className="relative bg-card border-2 border-primary/20 rounded-2xl lg:rounded-3xl p-6 lg:p-8 shadow-2xl overflow-hidden mb-4 lg:mb-6">
<div className="absolute inset-0 bg-linear-to-br from-primary/10 via-transparent to-primary/5" />
<div className="relative z-10 flex flex-col items-center space-y-6">
<div className="relative z-10 flex flex-col items-center space-y-4 lg:space-y-6">
<div className="relative">
<div className="absolute inset-0 bg-primary/20 blur-2xl rounded-full animate-pulse" />
<div className="relative bg-primary/10 rounded-full p-8 animate-float">
<Briefcase className="h-20 w-20 text-primary" strokeWidth={1.5} />
<div className="relative bg-primary/10 rounded-full p-6 lg:p-8 animate-float">
<Briefcase
className="h-16 w-16 lg:h-20 lg:w-20 text-primary"
strokeWidth={1.5}
/>
</div>
</div>
<div className="text-center space-y-2">
<h3 className="text-2xl font-bold">Freelance</h3>
<p className="text-muted-foreground">
<h3 className="text-xl lg:text-2xl font-bold">Freelance</h3>
<p className="text-sm lg:text-base text-muted-foreground">
Travail en solo
<br />
Prestations personnalisées
</p>
</div>
<div className="flex gap-2 pt-4">
<div className="h-2 w-12 bg-primary/30 rounded-full" />
<div className="h-2 w-8 bg-primary/20 rounded-full" />
<div className="h-2 w-10 bg-primary/25 rounded-full" />
<div className="flex gap-2 pt-2 lg:pt-4">
<div className="h-1.5 lg:h-2 w-10 lg:w-12 bg-primary/30 rounded-full" />
<div className="h-1.5 lg:h-2 w-6 lg:w-8 bg-primary/20 rounded-full" />
<div className="h-1.5 lg:h-2 w-8 lg:w-10 bg-primary/25 rounded-full" />
</div>
</div>
<div className="absolute top-4 right-4 h-16 w-16 bg-primary/5 rounded-full blur-xl" />
<div className="absolute bottom-4 left-4 h-20 w-20 bg-primary/5 rounded-full blur-xl" />
<div className="absolute top-4 right-4 h-12 w-12 lg:h-16 lg:w-16 bg-primary/5 rounded-full blur-xl" />
<div className="absolute bottom-4 left-4 h-16 w-16 lg:h-20 lg:w-20 bg-primary/5 rounded-full blur-xl" />
</div>
) : profileType === "agency" ? (
// Agency illustration
<div className="relative bg-card border-2 border-primary/20 rounded-3xl p-8 shadow-2xl overflow-hidden mb-6">
<div className="relative bg-card border-2 border-primary/20 rounded-2xl lg:rounded-3xl p-6 lg:p-8 shadow-2xl overflow-hidden mb-4 lg:mb-6">
<div className="absolute inset-0 bg-linear-to-br from-primary/10 via-transparent to-primary/5" />
<div className="relative z-10 flex flex-col items-center space-y-6">
<div className="relative z-10 flex flex-col items-center space-y-4 lg:space-y-6">
<div className="relative">
<div className="absolute inset-0 bg-primary/20 blur-2xl rounded-full animate-pulse" />
<div className="relative flex items-end justify-center gap-2 animate-float">
<div className="bg-primary/20 rounded-full p-4">
<div className="h-12 w-12 rounded-full bg-primary/40 flex items-center justify-center">
<div className="h-6 w-6 rounded-full bg-primary" />
<div className="relative flex items-end justify-center gap-1.5 lg:gap-2 animate-float">
<div className="bg-primary/20 rounded-full p-3 lg:p-4">
<div className="h-10 w-10 lg:h-12 lg:w-12 rounded-full bg-primary/40 flex items-center justify-center">
<div className="h-5 w-5 lg:h-6 lg:w-6 rounded-full bg-primary" />
</div>
</div>
<div className="bg-primary/30 rounded-full p-5 -mb-2">
<div className="h-14 w-14 rounded-full bg-primary/50 flex items-center justify-center">
<div className="h-7 w-7 rounded-full bg-primary" />
<div className="bg-primary/30 rounded-full p-4 lg:p-5 -mb-1.5 lg:-mb-2">
<div className="h-12 w-12 lg:h-14 lg:w-14 rounded-full bg-primary/50 flex items-center justify-center">
<div className="h-6 w-6 lg:h-7 lg:w-7 rounded-full bg-primary" />
</div>
</div>
<div className="bg-primary/20 rounded-full p-4">
<div className="h-12 w-12 rounded-full bg-primary/40 flex items-center justify-center">
<div className="h-6 w-6 rounded-full bg-primary" />
<div className="bg-primary/20 rounded-full p-3 lg:p-4">
<div className="h-10 w-10 lg:h-12 lg:w-12 rounded-full bg-primary/40 flex items-center justify-center">
<div className="h-5 w-5 lg:h-6 lg:w-6 rounded-full bg-primary" />
</div>
</div>
</div>
</div>
<div className="text-center space-y-2">
<h3 className="text-2xl font-bold">Agence</h3>
<p className="text-muted-foreground">
<h3 className="text-xl lg:text-2xl font-bold">Agence</h3>
<p className="text-sm lg:text-base text-muted-foreground">
Équipe collaborative
<br />
Plusieurs projets simultanés
</p>
</div>
<div className="flex gap-2 pt-4">
<div className="h-2 w-10 bg-primary/30 rounded-full" />
<div className="h-2 w-12 bg-primary/25 rounded-full" />
<div className="h-2 w-8 bg-primary/20 rounded-full" />
<div className="h-2 w-10 bg-primary/25 rounded-full" />
<div className="flex gap-1.5 lg:gap-2 pt-2 lg:pt-4">
<div className="h-1.5 lg:h-2 w-8 lg:w-10 bg-primary/30 rounded-full" />
<div className="h-1.5 lg:h-2 w-10 lg:w-12 bg-primary/25 rounded-full" />
<div className="h-1.5 lg:h-2 w-6 lg:w-8 bg-primary/20 rounded-full" />
<div className="h-1.5 lg:h-2 w-8 lg:w-10 bg-primary/25 rounded-full" />
</div>
</div>
<div className="absolute top-4 right-4 h-16 w-16 bg-primary/5 rounded-full blur-xl" />
<div className="absolute bottom-4 left-4 h-20 w-20 bg-primary/5 rounded-full blur-xl" />
<div className="absolute top-4 right-4 h-12 w-12 lg:h-16 lg:w-16 bg-primary/5 rounded-full blur-xl" />
<div className="absolute bottom-4 left-4 h-16 w-16 lg:h-20 lg:w-20 bg-primary/5 rounded-full blur-xl" />
</div>
) : (
// Placeholder
<div className="relative bg-card border-2 border-muted/20 rounded-3xl p-8 shadow-xl overflow-hidden mb-6">
<div className="relative z-10 flex flex-col items-center space-y-6">
<div className="bg-muted/30 rounded-full p-12 animate-pulse">
<div className="h-16 w-16 rounded-full bg-muted/50" />
<div className="relative bg-card border-2 border-muted/20 rounded-2xl lg:rounded-3xl p-6 lg:p-8 shadow-xl overflow-hidden mb-4 lg:mb-6">
<div className="relative z-10 flex flex-col items-center space-y-4 lg:space-y-6">
<div className="bg-muted/30 rounded-full p-10 lg:p-12 animate-pulse">
<div className="h-12 w-12 lg:h-16 lg:w-16 rounded-full bg-muted/50" />
</div>
<div className="text-center space-y-2">
<h3 className="text-xl font-semibold text-muted-foreground">
<h3 className="text-lg lg:text-xl font-semibold text-muted-foreground">
Choisissez votre profil
</h3>
<p className="text-sm text-muted-foreground">
<p className="text-xs lg:text-sm text-muted-foreground">
Sélectionnez votre type d'activité
<br />
pour continuer
@ -446,28 +460,30 @@ export function OnboardingModal({ open, onComplete }: OnboardingModalProps) {
if (stepKey === "service") {
return (
<div className="flex flex-col items-center justify-center h-full p-8 animate-fade-in">
<div className="flex flex-col items-center justify-center h-full p-4 lg:p-8 animate-fade-in">
<div className="relative w-full max-w-xs">
<div className="bg-card border-2 border-primary/20 rounded-2xl p-6 shadow-xl">
<div className="flex items-center gap-3 mb-4">
<FolderKanban className="h-8 w-8 text-primary" />
<div className="flex-1">
<div className="font-semibold text-lg">{serviceName || "Votre service"}</div>
<div className="text-sm text-muted-foreground">Service principal</div>
<div className="bg-card border-2 border-primary/20 rounded-2xl p-4 lg:p-6 shadow-xl">
<div className="flex items-center gap-2 lg:gap-3 mb-3 lg:mb-4">
<FolderKanban className="h-6 w-6 lg:h-8 lg:w-8 text-primary shrink-0" />
<div className="flex-1 min-w-0">
<div className="font-semibold text-base lg:text-lg truncate">
{serviceName || "Votre service"}
</div>
<div className="text-xs lg:text-sm text-muted-foreground">Service principal</div>
</div>
</div>
<div className="space-y-3">
<div className="space-y-2 lg:space-y-3">
<div className="flex items-center gap-2">
<CheckCircle2 className="h-4 w-4 text-primary" />
<div className="h-2 bg-muted rounded flex-1" />
<CheckCircle2 className="h-3.5 w-3.5 lg:h-4 lg:w-4 text-primary shrink-0" />
<div className="h-1.5 lg:h-2 bg-muted rounded flex-1" />
</div>
<div className="flex items-center gap-2">
<CheckCircle2 className="h-4 w-4 text-primary" />
<div className="h-2 bg-muted rounded flex-1" />
<CheckCircle2 className="h-3.5 w-3.5 lg:h-4 lg:w-4 text-primary shrink-0" />
<div className="h-1.5 lg:h-2 bg-muted rounded flex-1" />
</div>
<div className="flex items-center gap-2">
<CheckCircle2 className="h-4 w-4 text-primary" />
<div className="h-2 bg-muted rounded flex-1" />
<CheckCircle2 className="h-3.5 w-3.5 lg:h-4 lg:w-4 text-primary shrink-0" />
<div className="h-1.5 lg:h-2 bg-muted rounded flex-1" />
</div>
</div>
</div>
@ -479,38 +495,38 @@ export function OnboardingModal({ open, onComplete }: OnboardingModalProps) {
if (stepKey === "structure") {
const validSteps = stepNames.filter((s) => s.trim());
return (
<div className="flex flex-col items-center justify-center h-full p-8 animate-fade-in">
<div className="flex flex-col items-center justify-center h-full p-4 lg:p-8 animate-fade-in">
<div className="relative w-full max-w-xs">
<div className="bg-card border-2 border-primary/20 rounded-2xl p-6 shadow-xl">
<div className="flex items-center gap-2 mb-4">
<Building2 className="h-6 w-6 text-primary" />
<div className="font-semibold">Étapes</div>
<div className="bg-card border-2 border-primary/20 rounded-2xl p-4 lg:p-6 shadow-xl">
<div className="flex items-center gap-2 mb-3 lg:mb-4">
<Building2 className="h-5 w-5 lg:h-6 lg:w-6 text-primary shrink-0" />
<div className="font-semibold text-sm lg:text-base">Étapes</div>
</div>
<div className="space-y-3">
<div className="space-y-2 lg:space-y-3">
{validSteps.length > 0 ? (
validSteps.map((step, index) => (
<div
key={index}
className="flex items-center gap-3 animate-slide-in"
className="flex items-center gap-2 lg:gap-3 animate-slide-in"
style={{ animationDelay: `${index * 100}ms` }}
>
<div className="flex items-center justify-center w-8 h-8 rounded-full bg-primary/10 text-primary text-sm font-semibold shrink-0">
<div className="flex items-center justify-center w-7 h-7 lg:w-8 lg:h-8 rounded-full bg-primary/10 text-primary text-xs lg:text-sm font-semibold shrink-0">
{index + 1}
</div>
<div className="flex-1 bg-accent/50 rounded-lg px-3 py-2 text-sm font-medium truncate">
<div className="flex-1 bg-accent/50 rounded-lg px-2.5 lg:px-3 py-1.5 lg:py-2 text-xs lg:text-sm font-medium truncate">
{step}
</div>
</div>
))
) : (
<>
<div className="flex items-center gap-3">
<div className="w-8 h-8 rounded-full bg-muted animate-pulse" />
<div className="h-8 bg-muted rounded flex-1 animate-pulse" />
<div className="flex items-center gap-2 lg:gap-3">
<div className="w-7 h-7 lg:w-8 lg:h-8 rounded-full bg-muted animate-pulse" />
<div className="h-7 lg:h-8 bg-muted rounded flex-1 animate-pulse" />
</div>
<div className="flex items-center gap-3">
<div className="w-8 h-8 rounded-full bg-muted animate-pulse" />
<div className="h-8 bg-muted rounded flex-1 animate-pulse" />
<div className="flex items-center gap-2 lg:gap-3">
<div className="w-7 h-7 lg:w-8 lg:h-8 rounded-full bg-muted animate-pulse" />
<div className="h-7 lg:h-8 bg-muted rounded flex-1 animate-pulse" />
</div>
</>
)}
@ -523,17 +539,21 @@ export function OnboardingModal({ open, onComplete }: OnboardingModalProps) {
if (stepKey === "project") {
return (
<div className="flex flex-col items-center justify-center h-full p-8 animate-fade-in">
<div className="flex flex-col items-center justify-center h-full p-4 lg:p-8 animate-fade-in">
<div className="relative w-full max-w-xs">
<div className="bg-linear-to-br from-primary/20 to-primary/5 border-2 border-primary/30 rounded-2xl p-6 shadow-2xl">
<div className="flex items-center gap-3 mb-4">
<CheckCircle2 className="h-10 w-10 text-primary animate-bounce-gentle" />
<div className="flex-1">
<div className="font-bold text-xl truncate">{projectName || "Nom du projet"}</div>
<div className="text-sm text-muted-foreground">{serviceName || "Service"}</div>
<div className="bg-linear-to-br from-primary/20 to-primary/5 border-2 border-primary/30 rounded-2xl p-4 lg:p-6 shadow-2xl">
<div className="flex items-center gap-2 lg:gap-3 mb-3 lg:mb-4">
<CheckCircle2 className="h-8 w-8 lg:h-10 lg:w-10 text-primary animate-bounce-gentle shrink-0" />
<div className="flex-1 min-w-0">
<div className="font-bold text-lg lg:text-xl truncate">
{projectName || "Nom du projet"}
</div>
<div className="text-xs lg:text-sm text-muted-foreground truncate">
{serviceName || "Service"}
</div>
</div>
</div>
<div className="space-y-2 text-sm">
<div className="space-y-1.5 lg:space-y-2 text-xs lg:text-sm">
<div className="flex items-center justify-between">
<span className="text-muted-foreground">Étapes</span>
<span className="font-semibold">{stepNames.filter((s) => s.trim()).length}</span>
@ -561,62 +581,68 @@ export function OnboardingModal({ open, onComplete }: OnboardingModalProps) {
onDismiss={() => setShowPointer(false)}
/>
<Dialog open={open && !showCelebration} onOpenChange={(open) => !open && handleSkip()}>
<DialogContent className="max-w-6xl p-0 gap-0 overflow-hidden">
<div className="flex flex-col md:flex-row min-h-[600px]">
<DialogContent className="max-w-[95vw] sm:max-w-2xl lg:max-w-4xl xl:max-w-6xl p-0 gap-0 overflow-hidden h-[95vh] sm:h-auto max-h-[95vh] flex flex-col">
<div className="flex flex-col lg:flex-row min-h-0 flex-1 overflow-hidden">
{/* Left side - Form */}
<div className="flex-1 flex flex-col p-8">
<DialogHeader className="mb-6">
<div className="flex items-center gap-4">
<div className="p-3 bg-linear-to-br from-primary/20 to-primary/5 rounded-xl shadow-sm">
<Icon className="h-7 w-7 text-primary" />
<div className="flex-1 flex flex-col p-4 sm:p-6 lg:p-8 min-h-0 overflow-y-auto">
<DialogHeader className="mb-4 sm:mb-6">
<div className="flex items-start sm:items-center gap-3 sm:gap-4">
<div className="p-2 sm:p-3 bg-linear-to-br from-primary/20 to-primary/5 rounded-xl shadow-sm shrink-0">
<Icon className="h-5 w-5 sm:h-6 sm:w-6 lg:h-7 lg:w-7 text-primary" />
</div>
<div className="flex-1">
<DialogTitle className="text-2xl font-bold">
<div className="flex-1 min-w-0">
<DialogTitle className="text-xl sm:text-2xl font-bold leading-tight">
{t(`modal.steps.${STEP_KEYS[currentStep]}.title`)}
</DialogTitle>
<DialogDescription className="text-base mt-1">
<DialogDescription className="text-sm sm:text-base mt-1">
{t(`modal.steps.${STEP_KEYS[currentStep]}.description`)}
</DialogDescription>
</div>
</div>
</DialogHeader>
<div className="flex-1 min-h-[280px]">{renderStepContent()}</div>
<div className="flex-1 min-h-[200px] sm:min-h-[280px] overflow-y-auto">
{renderStepContent()}
</div>
{/* Progress indicators */}
<div className="flex gap-2 justify-center my-6">
<div className="flex gap-1.5 sm:gap-2 justify-center my-4 sm:my-6 shrink-0">
{STEP_KEYS.map((_, index) => (
<div
key={index}
className={`h-2 rounded-full transition-all duration-300 ${
className={`h-1.5 sm:h-2 rounded-full transition-all duration-300 ${
index === currentStep
? "w-10 bg-primary shadow-sm"
? "w-8 sm:w-10 bg-primary shadow-sm"
: index < currentStep
? "w-2 bg-primary/60"
: "w-2 bg-muted"
? "w-1.5 sm:w-2 bg-primary/60"
: "w-1.5 sm:w-2 bg-muted"
}`}
/>
))}
</div>
<DialogFooter className="flex-row justify-between sm:justify-between gap-3">
<DialogFooter className="flex-col sm:flex-row justify-between gap-2 sm:gap-3 shrink-0 pt-2 sm:pt-0">
<Button
variant="ghost"
onClick={handleSkip}
className="text-muted-foreground hover:text-foreground"
className="text-muted-foreground hover:text-foreground w-full sm:w-auto order-2 sm:order-1"
>
{t("modal.skipTutorial")}
</Button>
<div className="flex gap-2">
<div className="flex gap-2 w-full sm:w-auto order-1 sm:order-2">
{!isFirstStep && (
<Button variant="outline" onClick={handleBack} className="min-w-[100px]">
<Button
variant="outline"
onClick={handleBack}
className="flex-1 sm:flex-none min-w-[100px]"
>
{t("modal.back")}
</Button>
)}
<Button
onClick={handleNext}
disabled={!canProceed() || createTablo.isPending || createEtape.isPending}
className="min-w-[120px]"
className="flex-1 sm:flex-none min-w-[120px]"
>
{createTablo.isPending || createEtape.isPending
? "Création..."
@ -629,13 +655,13 @@ export function OnboardingModal({ open, onComplete }: OnboardingModalProps) {
</div>
{/* Right side - Animated Preview */}
<div className="hidden md:flex flex-1 bg-linear-to-br from-accent/30 to-accent/10 border-l border-border relative overflow-hidden">
<div className="hidden lg:flex flex-1 bg-linear-to-br from-accent/30 to-accent/10 border-l border-border relative overflow-hidden min-h-[300px]">
{/* Decorative background elements */}
<div className="absolute top-10 right-10 w-32 h-32 bg-primary/10 rounded-full blur-3xl" />
<div className="absolute bottom-10 left-10 w-40 h-40 bg-primary/5 rounded-full blur-3xl" />
{/* Preview content */}
<div className="relative w-full">{renderPreview()}</div>
<div className="relative w-full overflow-y-auto">{renderPreview()}</div>
</div>
</div>
</DialogContent>

View file

@ -14,7 +14,7 @@
}
},
"profile": {
"title": "STEP 1: Freelance or Agency?",
"title": "What is your profile?",
"description": "Tell us about your business model",
"question": "You are",
"options": {
@ -23,22 +23,22 @@
}
},
"service": {
"title": "STEP 2: What is your main service?",
"title": "What is your main service?",
"description": "Define the service you want to track in this Tablo",
"question": "What is your service?",
"placeholder": "Ex: Web Development, Consulting..."
},
"structure": {
"title": "STEP 3: How many steps structure your service?",
"title": "How many steps structure your service?",
"description": "Break down your service into workflow steps",
"question": "How many steps make up your service?",
"steps_label": "Step name (ex: Brief -> Design -> Delivery)",
"step_placeholder": "Step {{index}}"
},
"project": {
"title": "Step 4: Project Name (Tablo)",
"title": "Project Name (Tablo)",
"description": "Give your first Tablo a name",
"question": "What would you like to call this project?",
"question": "What would you like to call this tablo?",
"placeholder": "ex: Landing Page - Client X"
}
}

View file

@ -7,14 +7,14 @@
"steps": {
"welcome": {
"title": "Bienvenue sur XTablo",
"description": "Ici, vous allez créer votre premier Tablo - un espace de suivi projet, clair, élégant et partageable avec vos clients ou votre équipe.",
"description": "Ici, vous allez créer votre premier Tablo - un espace de suivi projet, clair, élégant et partageable avec vos clients.",
"content": {
"intro": "En moins de 2 minutes, vous aurez un Tablo prêt à l'emploi, structuré autour de vos services.",
"cta": "Créer mon premier Tablo"
}
},
"profile": {
"title": "ÉTAPE 1 : Freelance ou Agence ?",
"title": "Quel est votre profil ?",
"description": "Parlez-nous de votre modèle d'activité",
"question": "Vous êtes",
"options": {
@ -23,22 +23,22 @@
}
},
"service": {
"title": "ÉTAPE 2 : Quel est votre service principal ?",
"title": "Quel est votre service principal ?",
"description": "Définissez le service que vous souhaitez suivre dans ce Tablo",
"question": "Quel est votre service ?",
"placeholder": "Ex: Création de site web, Consulting..."
},
"structure": {
"title": "ÉTAPE 3 : En combien d'étapes structurez-vous votre service ?",
"title": "En combien d'étapes structurez-vous votre service ?",
"description": "Décomposez votre service en étapes de workflow",
"question": "Combien d'étapes composent votre service ?",
"steps_label": "Nom de l'étape (ex : Brief -> Design -> Livraison)",
"step_placeholder": "Étape {{index}}"
},
"project": {
"title": "Étape 4 : Nom du projet (tablo)",
"title": "Nom du projet (tablo)",
"description": "Donnez un nom à votre premier Tablo",
"question": "Comment souhaitez-vous appeler ce projet ?",
"question": "Comment souhaitez-vous appeler ce tablo ?",
"placeholder": "ex : Landing Page - Client X"
}
}

View file

@ -7,7 +7,7 @@ const Input = React.forwardRef<HTMLInputElement, React.ComponentProps<"input">>(
<input
type={type}
className={cn(
"flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-base ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
"flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-base ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring focus-visible:ring-offset-0 disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
className
)}
ref={ref}