aligned manual persona editor tabs and fields with view-only mode - removed tabs and fields from manual edit mode that don't show up in view-only detail mode
This commit is contained in:
parent
f1cc094318
commit
db3a418d3c
2 changed files with 279 additions and 614 deletions
2
dist/index.html
vendored
2
dist/index.html
vendored
|
|
@ -7,7 +7,7 @@
|
|||
<meta name="description" content="Lovable Generated Project" />
|
||||
<meta name="author" content="Lovable" />
|
||||
<meta property="og:image" content="/og-image.png" />
|
||||
<script type="module" crossorigin src="/semblance/assets/index-iDGrOQEx.js"></script>
|
||||
<script type="module" crossorigin src="/semblance/assets/index-CIkWTPLM.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/semblance/assets/index-sPfhNyTb.css">
|
||||
</head>
|
||||
|
||||
|
|
|
|||
|
|
@ -97,14 +97,14 @@ export default function PersonaEditor({ persona, onSave, onCancel }: PersonaEdit
|
|||
}));
|
||||
};
|
||||
|
||||
const handleArrayAdd = (field: 'goals' | 'frustrations' | 'motivations' | 'scenarios' | 'fears') => {
|
||||
const handleArrayAdd = (field: 'goals' | 'frustrations' | 'motivations' | 'scenarios') => {
|
||||
setEditedPersona(prev => ({
|
||||
...prev,
|
||||
[field]: [...(prev[field] || []), '']
|
||||
}));
|
||||
};
|
||||
|
||||
const handleArrayUpdate = (field: 'goals' | 'frustrations' | 'motivations' | 'scenarios' | 'fears', index: number, value: string) => {
|
||||
const handleArrayUpdate = (field: 'goals' | 'frustrations' | 'motivations' | 'scenarios', index: number, value: string) => {
|
||||
setEditedPersona(prev => {
|
||||
const newArray = [...(prev[field] || [])];
|
||||
newArray[index] = value;
|
||||
|
|
@ -115,7 +115,7 @@ export default function PersonaEditor({ persona, onSave, onCancel }: PersonaEdit
|
|||
});
|
||||
};
|
||||
|
||||
const handleArrayRemove = (field: 'goals' | 'frustrations' | 'motivations' | 'scenarios' | 'fears', index: number) => {
|
||||
const handleArrayRemove = (field: 'goals' | 'frustrations' | 'motivations' | 'scenarios', index: number) => {
|
||||
setEditedPersona(prev => {
|
||||
const newArray = [...(prev[field] || [])];
|
||||
newArray.splice(index, 1);
|
||||
|
|
@ -366,141 +366,147 @@ export default function PersonaEditor({ persona, onSave, onCancel }: PersonaEdit
|
|||
</Button>
|
||||
</div>
|
||||
|
||||
<Tabs defaultValue="basic">
|
||||
<TabsList className="grid w-full grid-cols-6">
|
||||
<TabsTrigger value="basic">Basic</TabsTrigger>
|
||||
<TabsTrigger value="cooper">Cooper</TabsTrigger>
|
||||
<Tabs defaultValue="general">
|
||||
<TabsList className="grid w-full grid-cols-4">
|
||||
<TabsTrigger value="general">General</TabsTrigger>
|
||||
<TabsTrigger value="cooper-profile">Cooper Profile</TabsTrigger>
|
||||
<TabsTrigger value="personality">Personality</TabsTrigger>
|
||||
<TabsTrigger value="demographics">Demographics</TabsTrigger>
|
||||
<TabsTrigger value="lifestyle">Lifestyle</TabsTrigger>
|
||||
<TabsTrigger value="extended">Extended</TabsTrigger>
|
||||
<TabsTrigger value="scenarios">Scenarios</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
<TabsContent value="basic" className="mt-6">
|
||||
<TabsContent value="general" className="mt-6 space-y-6">
|
||||
<Card>
|
||||
<CardContent className="p-6">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center space-x-4 mb-6">
|
||||
<div className="h-16 w-16 rounded-full bg-muted flex items-center justify-center">
|
||||
<div className="h-16 w-16 rounded-full bg-gray-200 flex items-center justify-center">
|
||||
<span className="font-medium text-gray-600">{editedPersona.name?.charAt(0) || 'P'}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<div>
|
||||
<label className="text-sm font-medium block mb-1">Name</label>
|
||||
<Input
|
||||
value={editedPersona.name || ''}
|
||||
onChange={e => handleChange('name', e.target.value)}
|
||||
className="w-64"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label className="text-sm font-medium block mb-1">Age Range</label>
|
||||
<Select
|
||||
value={editedPersona.age || ''}
|
||||
onValueChange={value => handleChange('age', value)}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Select age range" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="18-24">18-24</SelectItem>
|
||||
<SelectItem value="25-34">25-34</SelectItem>
|
||||
<SelectItem value="35-44">35-44</SelectItem>
|
||||
<SelectItem value="45-54">45-54</SelectItem>
|
||||
<SelectItem value="55-64">55-64</SelectItem>
|
||||
<SelectItem value="65+">65+</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="text-sm font-medium block mb-1">Gender</label>
|
||||
<Select
|
||||
value={editedPersona.gender || ''}
|
||||
onValueChange={value => handleChange('gender', value)}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Select gender" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="Male">Male</SelectItem>
|
||||
<SelectItem value="Female">Female</SelectItem>
|
||||
<SelectItem value="Non-binary">Non-binary</SelectItem>
|
||||
<SelectItem value="Other">Other</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="text-sm font-medium block mb-1">Occupation</label>
|
||||
<Input
|
||||
value={editedPersona.occupation || ''}
|
||||
onChange={e => handleChange('occupation', e.target.value)}
|
||||
className="w-64"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="text-sm font-medium block mb-1">Education</label>
|
||||
<Select
|
||||
value={editedPersona.education || ''}
|
||||
onValueChange={value => handleChange('education', value)}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Select education level" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="High School">High School</SelectItem>
|
||||
<SelectItem value="Some College">Some College</SelectItem>
|
||||
<SelectItem value="Associate's Degree">Associate's Degree</SelectItem>
|
||||
<SelectItem value="Bachelor's Degree">Bachelor's Degree</SelectItem>
|
||||
<SelectItem value="Master's Degree">Master's Degree</SelectItem>
|
||||
<SelectItem value="PhD">PhD</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="text-sm font-medium block mb-1">Location</label>
|
||||
<Input
|
||||
value={editedPersona.location || ''}
|
||||
onChange={e => handleChange('location', e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="text-sm font-medium block mb-1">Ethnicity (Optional)</label>
|
||||
<Select
|
||||
value={editedPersona.ethnicity || ''}
|
||||
onValueChange={value => handleChange('ethnicity', value)}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Select ethnicity" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="white">White</SelectItem>
|
||||
<SelectItem value="black">Black</SelectItem>
|
||||
<SelectItem value="asian">Asian</SelectItem>
|
||||
<SelectItem value="hispanic">Hispanic/Latino</SelectItem>
|
||||
<SelectItem value="native-american">Native American</SelectItem>
|
||||
<SelectItem value="middle-eastern">Middle Eastern</SelectItem>
|
||||
<SelectItem value="mixed">Mixed</SelectItem>
|
||||
<SelectItem value="other">Other</SelectItem>
|
||||
<SelectItem value="prefer-not-to-say">Prefer not to say</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<label className="text-sm font-medium block mb-1">Personality</label>
|
||||
<Textarea
|
||||
value={editedPersona.personality || ''}
|
||||
onChange={e => handleChange('personality', e.target.value)}
|
||||
rows={3}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="space-y-6">
|
||||
<div>
|
||||
<h3 className="font-medium text-sm mb-3 flex items-center">
|
||||
<span className="mr-2">👥</span>Demographics
|
||||
</h3>
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
<div>
|
||||
<label className="text-sm font-medium block mb-1">Age</label>
|
||||
<Input
|
||||
value={editedPersona.age || ''}
|
||||
onChange={e => handleChange('age', e.target.value)}
|
||||
placeholder="e.g. 29, 25-34"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="text-sm font-medium block mb-1">Gender</label>
|
||||
<Input
|
||||
value={editedPersona.gender || ''}
|
||||
onChange={e => handleChange('gender', e.target.value)}
|
||||
placeholder="e.g. Female, Male, Non-binary"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="text-sm font-medium block mb-1">Ethnicity</label>
|
||||
<Input
|
||||
value={editedPersona.ethnicity || ''}
|
||||
onChange={e => handleChange('ethnicity', e.target.value)}
|
||||
placeholder="e.g. White, Asian, Mixed"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 mt-4">
|
||||
<div>
|
||||
<label className="text-sm font-medium block mb-1">Education</label>
|
||||
<Input
|
||||
value={editedPersona.education || ''}
|
||||
onChange={e => handleChange('education', e.target.value)}
|
||||
placeholder="e.g. Master's Degree (Comp Sci)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="text-sm font-medium block mb-1">Social Grade</label>
|
||||
<Input
|
||||
value={editedPersona.socialGrade || ''}
|
||||
onChange={e => handleChange('socialGrade', e.target.value)}
|
||||
placeholder="e.g. A, B, C1, C2"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="text-sm font-medium block mb-1">Household Income</label>
|
||||
<Input
|
||||
value={editedPersona.householdIncome || ''}
|
||||
onChange={e => handleChange('householdIncome', e.target.value)}
|
||||
placeholder="e.g. £85,000 - £90,000"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 mt-4">
|
||||
<div>
|
||||
<label className="text-sm font-medium block mb-1">Household Composition</label>
|
||||
<Input
|
||||
value={editedPersona.householdComposition || ''}
|
||||
onChange={e => handleChange('householdComposition', e.target.value)}
|
||||
placeholder="e.g. Single-person household in a modern Islington flat"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 className="font-medium text-sm mb-3 flex items-center">
|
||||
<span className="mr-2">📍</span>Location
|
||||
</h3>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label className="text-sm font-medium block mb-1">Location</label>
|
||||
<Input
|
||||
value={editedPersona.location || ''}
|
||||
onChange={e => handleChange('location', e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="text-sm font-medium block mb-1">Living Situation</label>
|
||||
<Input
|
||||
value={editedPersona.livingSituation || ''}
|
||||
onChange={e => handleChange('livingSituation', e.target.value)}
|
||||
placeholder="e.g. Own home, Rent apartment"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 className="font-medium text-sm mb-3 flex items-center">
|
||||
<span className="mr-2">❤️</span>Interests
|
||||
</h3>
|
||||
<div>
|
||||
<label className="text-sm font-medium block mb-1">Interests</label>
|
||||
<Textarea
|
||||
value={editedPersona.interests || ''}
|
||||
onChange={e => handleChange('interests', e.target.value)}
|
||||
|
|
@ -508,75 +514,133 @@ export default function PersonaEditor({ persona, onSave, onCancel }: PersonaEdit
|
|||
placeholder="Tech, travel, cooking, etc."
|
||||
/>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 className="font-medium text-sm mb-3 flex items-center">
|
||||
<span className="mr-2">📚</span>Media
|
||||
</h3>
|
||||
<div>
|
||||
<div className="flex justify-between mb-1">
|
||||
<label className="text-sm font-medium">Tech Savviness</label>
|
||||
<span className="text-sm">{editedPersona.techSavviness}%</span>
|
||||
</div>
|
||||
<Slider
|
||||
value={[editedPersona.techSavviness]}
|
||||
onValueChange={values => handleChange('techSavviness', values[0])}
|
||||
max={100}
|
||||
step={1}
|
||||
<Textarea
|
||||
value={editedPersona.mediaConsumption || ''}
|
||||
onChange={e => handleChange('mediaConsumption', e.target.value)}
|
||||
rows={3}
|
||||
placeholder="TV shows, podcasts, news sources, social media platforms"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div className="flex justify-between mb-1">
|
||||
<label className="text-sm font-medium">Brand Loyalty</label>
|
||||
<span className="text-sm">{editedPersona.brandLoyalty || 0}%</span>
|
||||
</div>
|
||||
<Slider
|
||||
value={[editedPersona.brandLoyalty || 0]}
|
||||
onValueChange={values => handleChange('brandLoyalty', values[0])}
|
||||
max={100}
|
||||
step={1}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div className="flex justify-between mb-1">
|
||||
<label className="text-sm font-medium">Price Consciousness</label>
|
||||
<span className="text-sm">{editedPersona.priceConsciousness || 0}%</span>
|
||||
</div>
|
||||
<Slider
|
||||
value={[editedPersona.priceConsciousness || 0]}
|
||||
onValueChange={values => handleChange('priceConsciousness', values[0])}
|
||||
max={100}
|
||||
step={1}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div className="flex justify-between mb-1">
|
||||
<label className="text-sm font-medium">Environmental Concern</label>
|
||||
<span className="text-sm">{editedPersona.environmentalConcern || 0}%</span>
|
||||
</div>
|
||||
<Slider
|
||||
value={[editedPersona.environmentalConcern || 0]}
|
||||
onValueChange={values => handleChange('environmentalConcern', values[0])}
|
||||
max={100}
|
||||
step={1}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 gap-4 pt-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<label className="text-sm font-medium">Purchasing Power</label>
|
||||
<Switch
|
||||
checked={editedPersona.hasPurchasingPower || false}
|
||||
onCheckedChange={value => handleChange('hasPurchasingPower', value)}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 className="font-medium text-sm mb-3">Digital Behavior</h3>
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<div className="flex justify-between mb-1">
|
||||
<label className="text-sm font-medium">Tech Savviness</label>
|
||||
<span className="text-sm">{editedPersona.techSavviness}%</span>
|
||||
</div>
|
||||
<Slider
|
||||
value={[editedPersona.techSavviness]}
|
||||
onValueChange={values => handleChange('techSavviness', values[0])}
|
||||
max={100}
|
||||
step={1}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<label className="text-sm font-medium">Has Children</label>
|
||||
<Switch
|
||||
checked={editedPersona.hasChildren || false}
|
||||
onCheckedChange={value => handleChange('hasChildren', value)}
|
||||
<div>
|
||||
<div className="flex justify-between mb-1">
|
||||
<label className="text-sm font-medium">Brand Loyalty</label>
|
||||
<span className="text-sm">{editedPersona.brandLoyalty || 0}%</span>
|
||||
</div>
|
||||
<Slider
|
||||
value={[editedPersona.brandLoyalty || 0]}
|
||||
onValueChange={values => handleChange('brandLoyalty', values[0])}
|
||||
max={100}
|
||||
step={1}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div className="flex justify-between mb-1">
|
||||
<label className="text-sm font-medium">Price Sensitivity</label>
|
||||
<span className="text-sm">{editedPersona.priceConsciousness || 0}%</span>
|
||||
</div>
|
||||
<Slider
|
||||
value={[editedPersona.priceConsciousness || 0]}
|
||||
onValueChange={values => handleChange('priceConsciousness', values[0])}
|
||||
max={100}
|
||||
step={1}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div className="flex justify-between mb-1">
|
||||
<label className="text-sm font-medium">Environmental Concern</label>
|
||||
<span className="text-sm">{editedPersona.environmentalConcern || 0}%</span>
|
||||
</div>
|
||||
<Slider
|
||||
value={[editedPersona.environmentalConcern || 0]}
|
||||
onValueChange={values => handleChange('environmentalConcern', values[0])}
|
||||
max={100}
|
||||
step={1}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 mt-4">
|
||||
<div>
|
||||
<label className="text-sm font-medium block mb-1">Device Usage</label>
|
||||
<Textarea
|
||||
value={editedPersona.deviceUsage || ''}
|
||||
onChange={e => handleChange('deviceUsage', e.target.value)}
|
||||
rows={3}
|
||||
placeholder="Smartphone, laptop, tablet, smart TV, gaming console"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="text-sm font-medium block mb-1">Shopping Habits</label>
|
||||
<Textarea
|
||||
value={editedPersona.shoppingHabits || ''}
|
||||
onChange={e => handleChange('shoppingHabits', e.target.value)}
|
||||
rows={3}
|
||||
placeholder="Online vs in-store, frequency, preferred retailers"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 className="font-medium text-sm mb-3">Additional Information</h3>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label className="text-sm font-medium block mb-1">Brand Preferences</label>
|
||||
<Textarea
|
||||
value={editedPersona.brandPreferences || ''}
|
||||
onChange={e => handleChange('brandPreferences', e.target.value)}
|
||||
rows={3}
|
||||
placeholder="Favorite brands, brand values alignment"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="text-sm font-medium block mb-1">Communication Preferences</label>
|
||||
<Textarea
|
||||
value={editedPersona.communicationPreferences || ''}
|
||||
onChange={e => handleChange('communicationPreferences', e.target.value)}
|
||||
rows={3}
|
||||
placeholder="Email, phone, text, video calls, in-person"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-4">
|
||||
<label className="text-sm font-medium block mb-1">Additional Information</label>
|
||||
<Textarea
|
||||
value={editedPersona.additionalInformation || ''}
|
||||
onChange={e => handleChange('additionalInformation', e.target.value)}
|
||||
rows={4}
|
||||
placeholder="Any other relevant details or context"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -584,7 +648,7 @@ export default function PersonaEditor({ persona, onSave, onCancel }: PersonaEdit
|
|||
</Card>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="cooper" className="mt-6 space-y-6">
|
||||
<TabsContent value="cooper-profile" className="mt-6 space-y-6">
|
||||
<Card>
|
||||
<CardContent className="p-6">
|
||||
<div className="mb-4">
|
||||
|
|
@ -764,51 +828,6 @@ export default function PersonaEditor({ persona, onSave, onCancel }: PersonaEdit
|
|||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardContent className="p-6">
|
||||
<div className="space-y-4 mb-6">
|
||||
<div>
|
||||
<label className="text-sm font-medium block mb-1">Scenario Section Title</label>
|
||||
<Input
|
||||
value={editedPersona.scenarioType || ''}
|
||||
onChange={e => handleChange('scenarioType', e.target.value)}
|
||||
placeholder="Life Scenarios"
|
||||
/>
|
||||
<p className="text-xs text-muted-foreground mt-1">
|
||||
Custom title for the scenarios section (e.g., "Customer Journey", "Use Cases")
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3 className="font-medium text-lg mb-3">Usage Scenarios</h3>
|
||||
{(editedPersona.scenarios || []).map((item, index) => (
|
||||
<div key={index} className="flex items-start gap-2 mb-2">
|
||||
<Textarea
|
||||
value={item || ''}
|
||||
onChange={e => handleArrayUpdate('scenarios', index, e.target.value)}
|
||||
rows={2}
|
||||
/>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={() => handleArrayRemove('scenarios', index)}
|
||||
>
|
||||
<Trash2 className="h-4 w-4 text-muted-foreground" />
|
||||
</Button>
|
||||
</div>
|
||||
))}
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => handleArrayAdd('scenarios')}
|
||||
className="mt-2"
|
||||
>
|
||||
<Plus className="h-4 w-4 mr-2" />
|
||||
Add Scenario
|
||||
</Button>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="personality" className="mt-6">
|
||||
|
|
@ -901,403 +920,49 @@ export default function PersonaEditor({ persona, onSave, onCancel }: PersonaEdit
|
|||
</Card>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="demographics" className="mt-6">
|
||||
<TabsContent value="scenarios" className="mt-6">
|
||||
<Card>
|
||||
<CardContent className="p-6">
|
||||
<h3 className="font-medium text-lg mb-4">Demographic Information</h3>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<label className="text-sm font-medium block mb-1">Social Grade</label>
|
||||
<Select
|
||||
value={editedPersona.socialGrade || ''}
|
||||
onValueChange={value => handleChange('socialGrade', value)}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Select social grade" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="A">A - Higher managerial</SelectItem>
|
||||
<SelectItem value="B">B - Intermediate managerial</SelectItem>
|
||||
<SelectItem value="C1">C1 - Supervisory or clerical</SelectItem>
|
||||
<SelectItem value="C2">C2 - Skilled manual workers</SelectItem>
|
||||
<SelectItem value="D">D - Semi and unskilled manual workers</SelectItem>
|
||||
<SelectItem value="E">E - State pensioners, unemployed</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="text-sm font-medium block mb-1">Household Income</label>
|
||||
<Select
|
||||
value={editedPersona.householdIncome || ''}
|
||||
onValueChange={value => handleChange('householdIncome', value)}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Select income range" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="Under $25k">Under $25,000</SelectItem>
|
||||
<SelectItem value="$25k-$50k">$25,000 - $50,000</SelectItem>
|
||||
<SelectItem value="$50k-$75k">$50,000 - $75,000</SelectItem>
|
||||
<SelectItem value="$75k-$100k">$75,000 - $100,000</SelectItem>
|
||||
<SelectItem value="$100k-$150k">$100,000 - $150,000</SelectItem>
|
||||
<SelectItem value="$150k-$250k">$150,000 - $250,000</SelectItem>
|
||||
<SelectItem value="Over $250k">Over $250,000</SelectItem>
|
||||
<SelectItem value="Prefer not to say">Prefer not to say</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<label className="text-sm font-medium block mb-1">Household Composition</label>
|
||||
<Select
|
||||
value={editedPersona.householdComposition || ''}
|
||||
onValueChange={value => handleChange('householdComposition', value)}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Select household type" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="Single person">Single person</SelectItem>
|
||||
<SelectItem value="Couple without children">Couple without children</SelectItem>
|
||||
<SelectItem value="Couple with children">Couple with children</SelectItem>
|
||||
<SelectItem value="Single parent">Single parent</SelectItem>
|
||||
<SelectItem value="Multi-generational">Multi-generational</SelectItem>
|
||||
<SelectItem value="Shared housing">Shared housing</SelectItem>
|
||||
<SelectItem value="Other">Other</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="text-sm font-medium block mb-1">Living Situation</label>
|
||||
<Select
|
||||
value={editedPersona.livingSituation || ''}
|
||||
onValueChange={value => handleChange('livingSituation', value)}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Select living situation" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="Own home">Own home</SelectItem>
|
||||
<SelectItem value="Rent apartment">Rent apartment</SelectItem>
|
||||
<SelectItem value="Rent house">Rent house</SelectItem>
|
||||
<SelectItem value="Live with family">Live with family</SelectItem>
|
||||
<SelectItem value="Student housing">Student housing</SelectItem>
|
||||
<SelectItem value="Assisted living">Assisted living</SelectItem>
|
||||
<SelectItem value="Other">Other</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="lifestyle" className="mt-6">
|
||||
<Card>
|
||||
<CardContent className="p-6">
|
||||
<h3 className="font-medium text-lg mb-4">Lifestyle & Behavior</h3>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<label className="text-sm font-medium block mb-1">Media Consumption</label>
|
||||
<Textarea
|
||||
value={editedPersona.mediaConsumption || ''}
|
||||
onChange={e => handleChange('mediaConsumption', e.target.value)}
|
||||
rows={3}
|
||||
placeholder="TV shows, podcasts, news sources, social media platforms"
|
||||
/>
|
||||
<p className="text-xs text-muted-foreground mt-1">
|
||||
Describe media consumption habits and preferences
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="text-sm font-medium block mb-1">Device Usage</label>
|
||||
<Textarea
|
||||
value={editedPersona.deviceUsage || ''}
|
||||
onChange={e => handleChange('deviceUsage', e.target.value)}
|
||||
rows={3}
|
||||
placeholder="Smartphone, laptop, tablet, smart TV, gaming console"
|
||||
/>
|
||||
<p className="text-xs text-muted-foreground mt-1">
|
||||
Primary devices and usage patterns
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="text-sm font-medium block mb-1">Shopping Habits</label>
|
||||
<Textarea
|
||||
value={editedPersona.shoppingHabits || ''}
|
||||
onChange={e => handleChange('shoppingHabits', e.target.value)}
|
||||
rows={3}
|
||||
placeholder="Online vs in-store, frequency, preferred retailers"
|
||||
/>
|
||||
<p className="text-xs text-muted-foreground mt-1">
|
||||
Shopping behavior and preferences
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="text-sm font-medium block mb-1">Brand Preferences</label>
|
||||
<Textarea
|
||||
value={editedPersona.brandPreferences || ''}
|
||||
onChange={e => handleChange('brandPreferences', e.target.value)}
|
||||
rows={3}
|
||||
placeholder="Favorite brands, brand values alignment"
|
||||
/>
|
||||
<p className="text-xs text-muted-foreground mt-1">
|
||||
Preferred brands and reasoning
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<label className="text-sm font-medium block mb-1">Communication Preferences</label>
|
||||
<Textarea
|
||||
value={editedPersona.communicationPreferences || ''}
|
||||
onChange={e => handleChange('communicationPreferences', e.target.value)}
|
||||
rows={3}
|
||||
placeholder="Email, phone, text, video calls, in-person"
|
||||
/>
|
||||
<p className="text-xs text-muted-foreground mt-1">
|
||||
Preferred communication methods and channels
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="text-sm font-medium block mb-1">Payment Methods</label>
|
||||
<Textarea
|
||||
value={editedPersona.paymentMethods || ''}
|
||||
onChange={e => handleChange('paymentMethods', e.target.value)}
|
||||
rows={3}
|
||||
placeholder="Credit cards, digital wallets, cash, BNPL"
|
||||
/>
|
||||
<p className="text-xs text-muted-foreground mt-1">
|
||||
Preferred payment methods and financial tools
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="text-sm font-medium block mb-1">Purchase Behavior</label>
|
||||
<Textarea
|
||||
value={editedPersona.purchaseBehaviour || ''}
|
||||
onChange={e => handleChange('purchaseBehaviour', e.target.value)}
|
||||
rows={3}
|
||||
placeholder="Research habits, decision factors, impulse vs planned buying"
|
||||
/>
|
||||
<p className="text-xs text-muted-foreground mt-1">
|
||||
How they approach making purchase decisions
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="extended" className="mt-6 space-y-6">
|
||||
<Card>
|
||||
<CardContent className="p-6">
|
||||
<h3 className="font-medium text-lg mb-4">Extended Profile</h3>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<label className="text-sm font-medium block mb-1">Core Values</label>
|
||||
<Textarea
|
||||
value={editedPersona.coreValues || ''}
|
||||
onChange={e => handleChange('coreValues', e.target.value)}
|
||||
rows={3}
|
||||
placeholder="Key principles and values that guide decisions"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="text-sm font-medium block mb-1">Lifestyle Choices</label>
|
||||
<Textarea
|
||||
value={editedPersona.lifestyleChoices || ''}
|
||||
onChange={e => handleChange('lifestyleChoices', e.target.value)}
|
||||
rows={3}
|
||||
placeholder="Health, fitness, diet, work-life balance preferences"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="text-sm font-medium block mb-1">Social Activities</label>
|
||||
<Textarea
|
||||
value={editedPersona.socialActivities || ''}
|
||||
onChange={e => handleChange('socialActivities', e.target.value)}
|
||||
rows={3}
|
||||
placeholder="Social hobbies, community involvement, networking"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="text-sm font-medium block mb-1">Category Knowledge</label>
|
||||
<Textarea
|
||||
value={editedPersona.categoryKnowledge || ''}
|
||||
onChange={e => handleChange('categoryKnowledge', e.target.value)}
|
||||
rows={3}
|
||||
placeholder="Expertise in specific product/service categories"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="text-sm font-medium block mb-1">Decision Influences</label>
|
||||
<Textarea
|
||||
value={editedPersona.decisionInfluences || ''}
|
||||
onChange={e => handleChange('decisionInfluences', e.target.value)}
|
||||
rows={3}
|
||||
placeholder="What factors most influence their decisions"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="text-sm font-medium block mb-1">Pain Points</label>
|
||||
<Textarea
|
||||
value={editedPersona.painPoints || ''}
|
||||
onChange={e => handleChange('painPoints', e.target.value)}
|
||||
rows={3}
|
||||
placeholder="Common challenges and friction points"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<label className="text-sm font-medium block mb-1">Journey Context</label>
|
||||
<Textarea
|
||||
value={editedPersona.journeyContext || ''}
|
||||
onChange={e => handleChange('journeyContext', e.target.value)}
|
||||
rows={3}
|
||||
placeholder="Current life stage and contextual factors"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="text-sm font-medium block mb-1">Key Touchpoints</label>
|
||||
<Textarea
|
||||
value={editedPersona.keyTouchpoints || ''}
|
||||
onChange={e => handleChange('keyTouchpoints', e.target.value)}
|
||||
rows={3}
|
||||
placeholder="Important interaction points and channels"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="space-y-4">
|
||||
<h4 className="font-medium text-sm">Self-Determination Needs</h4>
|
||||
|
||||
<div>
|
||||
<label className="text-sm font-medium block mb-1">Autonomy</label>
|
||||
<Textarea
|
||||
value={editedPersona.selfDeterminationNeeds?.autonomy || ''}
|
||||
onChange={e => handleChange('selfDeterminationNeeds', {
|
||||
...editedPersona.selfDeterminationNeeds,
|
||||
autonomy: e.target.value
|
||||
})}
|
||||
rows={2}
|
||||
placeholder="Need for independence and self-direction"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="text-sm font-medium block mb-1">Competence</label>
|
||||
<Textarea
|
||||
value={editedPersona.selfDeterminationNeeds?.competence || ''}
|
||||
onChange={e => handleChange('selfDeterminationNeeds', {
|
||||
...editedPersona.selfDeterminationNeeds,
|
||||
competence: e.target.value
|
||||
})}
|
||||
rows={2}
|
||||
placeholder="Need to feel capable and effective"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="text-sm font-medium block mb-1">Relatedness</label>
|
||||
<Textarea
|
||||
value={editedPersona.selfDeterminationNeeds?.relatedness || ''}
|
||||
onChange={e => handleChange('selfDeterminationNeeds', {
|
||||
...editedPersona.selfDeterminationNeeds,
|
||||
relatedness: e.target.value
|
||||
})}
|
||||
rows={2}
|
||||
placeholder="Need for connection and belonging"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardContent className="p-6">
|
||||
<div className="space-y-4">
|
||||
<div className="space-y-4 mb-6">
|
||||
<div>
|
||||
<h3 className="font-medium text-lg mb-3">Fears & Concerns</h3>
|
||||
{(editedPersona.fears || []).map((item, index) => (
|
||||
<div key={index} className="flex items-center gap-2 mb-2">
|
||||
<Input
|
||||
value={item}
|
||||
onChange={e => handleArrayUpdate('fears', index, e.target.value)}
|
||||
placeholder="Enter a fear or concern"
|
||||
/>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={() => handleArrayRemove('fears', index)}
|
||||
>
|
||||
<Trash2 className="h-4 w-4 text-muted-foreground" />
|
||||
</Button>
|
||||
</div>
|
||||
))}
|
||||
<label className="text-sm font-medium block mb-1">Scenario Section Title</label>
|
||||
<Input
|
||||
value={editedPersona.scenarioType || ''}
|
||||
onChange={e => handleChange('scenarioType', e.target.value)}
|
||||
placeholder="Life Scenarios"
|
||||
/>
|
||||
<p className="text-xs text-muted-foreground mt-1">
|
||||
Custom title for the scenarios section (e.g., "Customer Journey", "Use Cases")
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3 className="font-medium text-lg mb-3">Usage Scenarios</h3>
|
||||
{(editedPersona.scenarios || []).map((item, index) => (
|
||||
<div key={index} className="flex items-start gap-2 mb-2">
|
||||
<Textarea
|
||||
value={item || ''}
|
||||
onChange={e => handleArrayUpdate('scenarios', index, e.target.value)}
|
||||
rows={2}
|
||||
/>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => handleArrayAdd('fears')}
|
||||
className="mt-2"
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={() => handleArrayRemove('scenarios', index)}
|
||||
>
|
||||
<Plus className="h-4 w-4 mr-2" />
|
||||
Add Fear/Concern
|
||||
<Trash2 className="h-4 w-4 text-muted-foreground" />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="text-sm font-medium block mb-1">Personal Narrative</label>
|
||||
<Textarea
|
||||
value={editedPersona.narrative || ''}
|
||||
onChange={e => handleChange('narrative', e.target.value)}
|
||||
rows={4}
|
||||
placeholder="Personal story, background, key life experiences"
|
||||
/>
|
||||
<p className="text-xs text-muted-foreground mt-1">
|
||||
A brief narrative that captures their personal story
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="text-sm font-medium block mb-1">Additional Information</label>
|
||||
<Textarea
|
||||
value={editedPersona.additionalInformation || ''}
|
||||
onChange={e => handleChange('additionalInformation', e.target.value)}
|
||||
rows={4}
|
||||
placeholder="Any other relevant details or context"
|
||||
/>
|
||||
<p className="text-xs text-muted-foreground mt-1">
|
||||
Additional context or details not covered elsewhere
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => handleArrayAdd('scenarios')}
|
||||
className="mt-2"
|
||||
>
|
||||
<Plus className="h-4 w-4 mr-2" />
|
||||
Add Scenario
|
||||
</Button>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</TabsContent>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue