Improve the onboarding flow (UI)
This commit is contained in:
parent
65eda86b34
commit
b253342ec9
4 changed files with 188 additions and 162 deletions
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
|
|
|
|||
Loading…
Reference in a new issue