From c8d45911171b7d42f7aca22e080c90815f84a9b8 Mon Sep 17 00:00:00 2001 From: Vadym Samoilenko Date: Sun, 24 May 2026 14:18:32 +0100 Subject: [PATCH] =?UTF-8?q?feat:=20form=20redesign=20=E2=80=94=20Synthetic?= =?UTF-8?q?=20Users=20(Epic=203)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AI Recruiter form: - Word-count indicator floats inside textarea corner (absolute overlay) - Model dropdown: compact label "Model", removed description, shorter option text - personaCount: removed description, label → "Number of personas" Manual Creation form: - Behavioral Attributes (4): Slider → Input[type=number] inline with label+% suffix - OCEAN traits (5): Slider → Input[type=number] with description below - userCount header: +/- buttons → single Input[type=number] (w-16, centered) - TabsList: sticky top-0 z-10 so tabs stay visible while scrolling - Removed unused Slider and Sliders imports Co-Authored-By: Claude Sonnet 4.6 --- src/components/UserCreator.tsx | 269 ++++++------------ .../ai-recruiter/AIRecruiterForm.tsx | 48 ++-- 2 files changed, 109 insertions(+), 208 deletions(-) diff --git a/src/components/UserCreator.tsx b/src/components/UserCreator.tsx index 418ffdf5..ad56fedf 100755 --- a/src/components/UserCreator.tsx +++ b/src/components/UserCreator.tsx @@ -2,7 +2,7 @@ import { useState, useEffect } from 'react'; import { zodResolver } from "@hookform/resolvers/zod"; import { useForm } from "react-hook-form"; import { z } from "zod"; -import { Sliders, Plus, Users, Save, Loader, Trash2 } from 'lucide-react'; +import { Plus, Users, Save, Loader, Trash2 } from 'lucide-react'; import { toastService } from '@/lib/toast'; import { personasApi, authApi } from '@/lib/api'; import { useNavigate } from 'react-router-dom'; @@ -31,7 +31,6 @@ import { SelectTrigger, SelectValue, } from "@/components/ui/select"; -import { Slider } from "@/components/ui/slider"; import { Textarea } from "@/components/ui/textarea"; import { Switch } from "@/components/ui/switch"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; @@ -559,20 +558,24 @@ export default function UserCreator({ targetFolderId, targetFolderName }: UserCr

Create Synthetic Users

-
- -
- - {userCount} -
- +
+ + setUserCount(Math.min(10, Math.max(1, parseInt(e.target.value) || 1)))} + className="w-16 h-8 text-center text-sm" + /> + users
- + Basic Attitudinal Personality @@ -771,100 +774,44 @@ export default function UserCreator({ targetFolderId, targetFolderName }: UserCr )} /> -
-

Behavioral Attributes

- - ( - -
- Tech Savviness - {field.value}% -
- - field.onChange(value[0])} - /> - - -
- )} - /> - - ( - -
- Brand Loyalty - {field.value}% -
- - field.onChange(value[0])} - /> - - -
- )} - /> - - ( - -
- Price Consciousness - {field.value}% -
- - field.onChange(value[0])} - /> - - -
- )} - /> - - ( - -
- Environmental Concern - {field.value}% -
- - field.onChange(value[0])} - /> - - -
- )} - /> +
+

Behavioral Attributes

+ + {( + [ + { name: 'techSavviness', label: 'Tech Savviness' }, + { name: 'brandLoyalty', label: 'Brand Loyalty' }, + { name: 'priceConsciousness', label: 'Price Consciousness' }, + { name: 'environmentalConcern', label: 'Environmental Concern' }, + ] as const + ).map(({ name, label }) => ( + ( + +
+ {label} +
+ + field.onChange(Math.min(100, Math.max(0, parseInt(e.target.value) || 0)))} + className="w-16 h-8 text-center text-sm" + /> + + % +
+
+ +
+ )} + /> + ))}

OCEAN Personality Traits

- -
-
-
- - {(form.watch('oceanTraits') || { openness: 50 }).openness || 50}% -
- handleOceanChange('openness', values[0])} - max={100} - step={1} - /> -

- Creativity, curiosity, and openness to new ideas -

-
- -
-
- - {(form.watch('oceanTraits') || { conscientiousness: 50 }).conscientiousness || 50}% -
- handleOceanChange('conscientiousness', values[0])} - max={100} - step={1} - /> -

- Organization, responsibility, and self-discipline -

-
- -
-
- - {(form.watch('oceanTraits') || { extraversion: 50 }).extraversion || 50}% -
- handleOceanChange('extraversion', values[0])} - max={100} - step={1} - /> -

- Sociability, assertiveness, and talkativeness -

-
- -
-
- - {(form.watch('oceanTraits') || { agreeableness: 50 }).agreeableness || 50}% -
- handleOceanChange('agreeableness', values[0])} - max={100} - step={1} - /> -

- Compassion, cooperation, and concern for others -

-
- -
-
- - {(form.watch('oceanTraits') || { neuroticism: 50 }).neuroticism || 50}% -
- handleOceanChange('neuroticism', values[0])} - max={100} - step={1} - /> -

- Emotional reactivity, anxiety, and sensitivity to stress -

-
+ +
+ {( + [ + { key: 'openness', label: 'Openness to Experience', desc: 'Creativity, curiosity, and openness to new ideas' }, + { key: 'conscientiousness', label: 'Conscientiousness', desc: 'Organization, responsibility, and self-discipline' }, + { key: 'extraversion', label: 'Extraversion', desc: 'Sociability, assertiveness, and talkativeness' }, + { key: 'agreeableness', label: 'Agreeableness', desc: 'Compassion, cooperation, and concern for others' }, + { key: 'neuroticism', label: 'Neuroticism', desc: 'Emotional reactivity, anxiety, and sensitivity to stress' }, + ] as const + ).map(({ key, label, desc }) => { + const ocean = form.watch('oceanTraits') || { openness: 50, conscientiousness: 50, extraversion: 50, agreeableness: 50, neuroticism: 50 }; + const val = ocean[key] ?? 50; + return ( +
+
+ +
+ handleOceanChange(key, Math.min(100, Math.max(0, parseInt(e.target.value) || 0)))} + className="w-16 h-8 text-center text-sm" + /> + % +
+
+

{desc}

+
+ ); + })}
diff --git a/src/components/ai-recruiter/AIRecruiterForm.tsx b/src/components/ai-recruiter/AIRecruiterForm.tsx index 59e0c863..c68518bc 100755 --- a/src/components/ai-recruiter/AIRecruiterForm.tsx +++ b/src/components/ai-recruiter/AIRecruiterForm.tsx @@ -179,13 +179,17 @@ export default function AIRecruiterForm({ onSubmit, isGenerating }: AIRecruiterF /> -