Skip to content

Commit 9f33d81

Browse files
committed
Add name input and enhance avatar with philosophy colors and work style animations
- Add name input as first step in avatar creation flow - Update steps array to include name step - Update validation logic for all steps including name - Simplify avatar card to show only essential info: name, title, description, tags - Remove philosophy and work style from card display (now used for avatar generation) - Add philosophy-based color palettes for each design philosophy - Add work style-based animations (iterative, detail-oriented, big-picture, etc.) - Update Avatar component to accept philosophy and workingStyle props - Apply colors and animations to avatar thumbnail based on user selections - Update XML generation to include name field - Update summary card to show name in review step
1 parent 8737cf9 commit 9f33d81

File tree

2 files changed

+179
-58
lines changed

2 files changed

+179
-58
lines changed

src/app/my-avatar/page.tsx

Lines changed: 70 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ import { ArrowRight, ArrowLeft, Download, Palette, Zap, Sparkles } from 'lucide-
66
import Avatar from '@/components/Avatar';
77

88
interface AvatarData {
9+
// Personal information
10+
name: string;
11+
912
// Style questions
1013
primaryColor: string;
1114
secondaryColor: string;
@@ -21,6 +24,7 @@ interface AvatarData {
2124
}
2225

2326
const STEPS = [
27+
{ id: 'name', title: 'Personal Info', icon: Palette },
2428
{ id: 'style', title: 'Design Style', icon: Palette },
2529
{ id: 'role', title: 'Role & Organization', icon: Zap },
2630
{ id: 'keywords', title: 'Expertise', icon: Sparkles },
@@ -104,6 +108,7 @@ export default function MyAvatarPage() {
104108
const [currentStep, setCurrentStep] = useState(0);
105109
const [showPreview, setShowPreview] = useState(false);
106110
const [avatarData, setAvatarData] = useState<AvatarData>({
111+
name: '',
107112
primaryColor: '',
108113
secondaryColor: '',
109114
philosophy: '',
@@ -119,16 +124,19 @@ export default function MyAvatarPage() {
119124

120125
const isStepComplete = (step: number) => {
121126
switch (step) {
122-
case 0: // Philosophy selection
127+
case 0: // Name input
128+
return avatarData.name.trim();
129+
case 1: // Philosophy selection
123130
return avatarData.philosophy;
124-
case 1: // Working style selection
131+
case 2: // Working style selection
125132
return avatarData.workingStyle;
126-
case 2: // Role and organization
133+
case 3: // Role and organization
127134
return avatarData.role && avatarData.organizationDescription;
128-
case 3: // Keywords
135+
case 4: // Keywords
129136
return avatarData.keywords[0]?.trim();
130-
case 4: // Final step - check all required fields
137+
case 5: // Final step - check all required fields
131138
return (
139+
avatarData.name.trim() &&
132140
avatarData.philosophy &&
133141
avatarData.workingStyle &&
134142
avatarData.role &&
@@ -159,6 +167,9 @@ export default function MyAvatarPage() {
159167
const generateXML = () => {
160168
const xmlContent = `<?xml version="1.0" encoding="UTF-8"?>
161169
<avatar>
170+
<personal>
171+
<name>${avatarData.name}</name>
172+
</personal>
162173
<style>
163174
<primaryColor>${avatarData.primaryColor}</primaryColor>
164175
<secondaryColor>${avatarData.secondaryColor}</secondaryColor>
@@ -190,6 +201,31 @@ export default function MyAvatarPage() {
190201
const renderStep = () => {
191202
switch (currentStep) {
192203
case 0:
204+
return (
205+
<div className="space-y-8">
206+
<div className="text-center">
207+
<h2 className="text-3xl font-bold text-foreground mb-4">What&apos;s your name?</h2>
208+
<p className="text-muted-foreground text-lg">Let&apos;s start with the basics</p>
209+
</div>
210+
211+
<div className="max-w-2xl mx-auto">
212+
<div>
213+
<label className="block text-sm font-medium text-foreground mb-2">
214+
Your Name
215+
</label>
216+
<input
217+
type="text"
218+
value={avatarData.name}
219+
onChange={(e) => updateAvatarData('name', e.target.value)}
220+
className="w-full px-4 py-3 rounded-lg border border-border bg-background text-foreground focus:outline-none focus:ring-2 focus:ring-accent text-lg"
221+
placeholder="Enter your name..."
222+
/>
223+
</div>
224+
</div>
225+
</div>
226+
);
227+
228+
case 1:
193229
return (
194230
<div className="space-y-8">
195231
<div className="text-center">
@@ -222,7 +258,7 @@ export default function MyAvatarPage() {
222258
</div>
223259
);
224260

225-
case 1:
261+
case 2:
226262
return (
227263
<div className="space-y-8">
228264
<div className="text-center">
@@ -251,7 +287,7 @@ export default function MyAvatarPage() {
251287
</div>
252288
);
253289

254-
case 2:
290+
case 3:
255291
return (
256292
<div className="space-y-8">
257293
<div className="text-center">
@@ -289,7 +325,7 @@ export default function MyAvatarPage() {
289325
</div>
290326
);
291327

292-
case 3:
328+
case 4:
293329
return (
294330
<div className="space-y-8">
295331
<div className="text-center">
@@ -321,7 +357,7 @@ export default function MyAvatarPage() {
321357
</div>
322358
);
323359

324-
case 4:
360+
case 5:
325361
return (
326362
<div className="space-y-8">
327363
<div className="text-center">
@@ -333,6 +369,11 @@ export default function MyAvatarPage() {
333369
{/* Summary Card */}
334370
<div className="bg-gradient-to-br from-background to-muted/20 p-8 rounded-2xl border border-border shadow-lg">
335371
<div className="space-y-4">
372+
<div>
373+
<h4 className="font-medium text-foreground mb-2">Name</h4>
374+
<p className="text-foreground">{avatarData.name}</p>
375+
</div>
376+
336377
<div>
337378
<h4 className="font-medium text-foreground mb-2">Design Philosophy</h4>
338379
<p className="text-accent capitalize">{avatarData.philosophy}</p>
@@ -402,56 +443,31 @@ export default function MyAvatarPage() {
402443
<div className="bg-gradient-to-br from-background to-muted/20 p-8 rounded-2xl border border-border shadow-lg">
403444
<div className="flex items-center space-x-6 mb-6">
404445
<div className="relative">
405-
<Avatar name="Your Name" size={80} />
406-
<div className="absolute -bottom-1 -right-1 w-6 h-6 rounded-full border-2 border-background"
407-
style={{ backgroundColor: avatarData.primaryColor }} />
446+
<Avatar
447+
name={avatarData.name}
448+
size={80}
449+
philosophy={avatarData.philosophy}
450+
workingStyle={avatarData.workingStyle}
451+
/>
408452
</div>
409453
<div className="flex-1">
410-
<h3 className="text-xl font-semibold text-foreground mb-1">{avatarData.role}</h3>
411-
<p className="text-muted-foreground text-sm mb-2">{avatarData.organizationDescription}</p>
412-
<div className="flex items-center space-x-2">
413-
<span className="text-xs text-muted-foreground">Philosophy:</span>
414-
<span className="text-xs font-medium text-accent capitalize">{avatarData.philosophy}</span>
415-
</div>
454+
<h3 className="text-xl font-semibold text-foreground mb-1">{avatarData.name}</h3>
455+
<p className="text-muted-foreground text-sm mb-2">{avatarData.role}</p>
456+
<p className="text-muted-foreground text-sm">{avatarData.organizationDescription}</p>
416457
</div>
417458
</div>
418459

419-
<div className="space-y-4">
420-
<div>
421-
<h4 className="font-medium text-foreground mb-3">Expertise</h4>
422-
<div className="flex flex-wrap gap-2">
423-
{avatarData.keywords.filter(k => k.trim()).map((keyword, index) => (
424-
<span
425-
key={index}
426-
className="px-3 py-1 bg-accent/10 text-accent rounded-full text-sm font-medium"
427-
>
428-
{keyword}
429-
</span>
430-
))}
431-
</div>
432-
</div>
433-
434-
<div className="pt-4 border-t border-border/50">
435-
<div className="flex items-center justify-between">
436-
<div>
437-
<h4 className="font-medium text-foreground mb-2">Design Style</h4>
438-
<div className="flex items-center space-x-3">
439-
<div className="flex space-x-1">
440-
<div
441-
className="w-4 h-4 rounded-full"
442-
style={{ backgroundColor: avatarData.primaryColor }}
443-
/>
444-
<div
445-
className="w-4 h-4 rounded-full"
446-
style={{ backgroundColor: avatarData.secondaryColor }}
447-
/>
448-
</div>
449-
<span className="text-sm text-muted-foreground capitalize">
450-
{avatarData.workingStyle.replace('-', ' ')} approach
451-
</span>
452-
</div>
453-
</div>
454-
</div>
460+
<div>
461+
<h4 className="font-medium text-foreground mb-3">Expertise</h4>
462+
<div className="flex flex-wrap gap-2">
463+
{avatarData.keywords.filter(k => k.trim()).map((keyword, index) => (
464+
<span
465+
key={index}
466+
className="px-3 py-1 bg-accent/10 text-accent rounded-full text-sm font-medium"
467+
>
468+
{keyword}
469+
</span>
470+
))}
455471
</div>
456472
</div>
457473
</div>

src/components/Avatar.tsx

Lines changed: 109 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
'use client';
22

33
import * as React from "react";
4+
import { motion } from "framer-motion";
45
import { generateColors } from "@/utils/avatar";
56

67
const DEFAULT_SIZE = 40;
@@ -9,27 +10,124 @@ interface AvatarProps extends React.HTMLAttributes<HTMLDivElement> {
910
name: string;
1011
size?: number;
1112
className?: string;
13+
philosophy?: string;
14+
workingStyle?: string;
1215
}
1316

14-
const colors = ["#F6C750", "#E63525", "#050D4C", "#D4EBEE"];
17+
// Philosophy-based color palettes
18+
const PHILOSOPHY_COLORS = {
19+
'rewarded': ["#FF6B6B", "#FFE66D", "#4ECDC4"],
20+
'playful': ["#667EEA", "#764BA2", "#F093FB"],
21+
'scalable': ["#56AB2F", "#A8E6CF", "#FFD93D"],
22+
'one-team': ["#FF416C", "#FF4B2B", "#FF6B6B"],
23+
'clarity': ["#2C3E50", "#34495E", "#ECF0F1"],
24+
'grit': ["#FF9A9E", "#FECFEF", "#FECFEF"],
25+
};
26+
27+
// Work style-based animation variants
28+
const WORK_STYLE_ANIMATIONS = {
29+
'iterative': {
30+
animate: {
31+
rotate: [0, 360],
32+
scale: [1, 1.1, 1],
33+
},
34+
transition: {
35+
duration: 4,
36+
repeat: Infinity,
37+
ease: "linear"
38+
}
39+
},
40+
'detail-oriented': {
41+
animate: {
42+
scale: [1, 1.05, 1],
43+
},
44+
transition: {
45+
duration: 2,
46+
repeat: Infinity,
47+
ease: "easeInOut"
48+
}
49+
},
50+
'big-picture': {
51+
animate: {
52+
rotate: [0, 180, 360],
53+
},
54+
transition: {
55+
duration: 6,
56+
repeat: Infinity,
57+
ease: "easeInOut"
58+
}
59+
},
60+
'collaborative': {
61+
animate: {
62+
x: [0, 5, -5, 0],
63+
y: [0, -5, 5, 0],
64+
},
65+
transition: {
66+
duration: 3,
67+
repeat: Infinity,
68+
ease: "easeInOut"
69+
}
70+
},
71+
'experimental': {
72+
animate: {
73+
scale: [1, 1.2, 0.8, 1],
74+
rotate: [0, 90, 180, 270, 360],
75+
},
76+
transition: {
77+
duration: 5,
78+
repeat: Infinity,
79+
ease: "easeInOut"
80+
}
81+
},
82+
'systematic': {
83+
animate: {
84+
scale: [1, 1.02, 1],
85+
},
86+
transition: {
87+
duration: 1.5,
88+
repeat: Infinity,
89+
ease: "linear"
90+
}
91+
},
92+
};
93+
94+
const defaultColors = ["#F6C750", "#E63525", "#050D4C", "#D4EBEE"];
1595

1696
function AvatarFallback({
1797
name,
1898
size = DEFAULT_SIZE,
1999
className = "",
100+
philosophy,
101+
workingStyle,
20102
}: {
21103
name: string;
22104
size?: number;
23105
className?: string;
106+
philosophy?: string;
107+
workingStyle?: string;
24108
}) {
25109
const titleId = React.useId();
110+
111+
// Use philosophy-based colors if available, otherwise use default colors
112+
const colors = philosophy && PHILOSOPHY_COLORS[philosophy as keyof typeof PHILOSOPHY_COLORS]
113+
? PHILOSOPHY_COLORS[philosophy as keyof typeof PHILOSOPHY_COLORS]
114+
: defaultColors;
115+
26116
const properties = generateColors(name, colors);
27117

28118
const maskId = React.useId();
29119
const filterId = React.useId();
30120

121+
// Get animation props based on working style
122+
const animationProps = workingStyle && WORK_STYLE_ANIMATIONS[workingStyle as keyof typeof WORK_STYLE_ANIMATIONS]
123+
? WORK_STYLE_ANIMATIONS[workingStyle as keyof typeof WORK_STYLE_ANIMATIONS]
124+
: {};
125+
31126
return (
32-
<div className={`inline-block ${className}`}>
127+
<motion.div
128+
className={`inline-block ${className}`}
129+
{...animationProps}
130+
>
33131
<svg
34132
viewBox={`0 0 ${size} ${size}`}
35133
fill="none"
@@ -87,7 +185,7 @@ function AvatarFallback({
87185
</filter>
88186
</defs>
89187
</svg>
90-
</div>
188+
</motion.div>
91189
);
92190
}
93191

@@ -97,6 +195,8 @@ export const Avatar = React.forwardRef<HTMLDivElement, AvatarProps>(
97195
name,
98196
size = DEFAULT_SIZE,
99197
className = "",
198+
philosophy,
199+
workingStyle,
100200
...rest
101201
},
102202
ref
@@ -107,7 +207,12 @@ export const Avatar = React.forwardRef<HTMLDivElement, AvatarProps>(
107207
className={className}
108208
{...rest}
109209
>
110-
<AvatarFallback name={name} size={size} />
210+
<AvatarFallback
211+
name={name}
212+
size={size}
213+
philosophy={philosophy}
214+
workingStyle={workingStyle}
215+
/>
111216
</div>
112217
);
113218
}

0 commit comments

Comments
 (0)