@@ -30,7 +30,6 @@ import {
3030 Clock ,
3131 Trash2 ,
3232 Save ,
33- X ,
3433 CheckCircle2 ,
3534 AlertCircle ,
3635 Zap ,
@@ -59,185 +58,7 @@ import {
5958 LANGUAGES ,
6059} from './ProfilePage.constants' ;
6160import type { QuickSetupData , EditableSection , TabId } from './ProfilePage.constants' ;
62-
63- // =============================================================================
64- // Types
65- // =============================================================================
66-
67- // =============================================================================
68- // Utility Components
69- // =============================================================================
70-
71- function ProgressRing ( {
72- progress,
73- size = 80 ,
74- strokeWidth = 8 ,
75- color = 'text-primary' ,
76- } : {
77- progress : number ;
78- size ?: number ;
79- strokeWidth ?: number ;
80- color ?: string ;
81- } ) {
82- const radius = ( size - strokeWidth ) / 2 ;
83- const circumference = radius * 2 * Math . PI ;
84- const offset = circumference - ( progress / 100 ) * circumference ;
85-
86- return (
87- < div className = "relative" style = { { width : size , height : size } } >
88- < svg className = "transform -rotate-90 w-full h-full" >
89- < circle
90- cx = { size / 2 }
91- cy = { size / 2 }
92- r = { radius }
93- stroke = "currentColor"
94- strokeWidth = { strokeWidth }
95- fill = "transparent"
96- className = "text-bg-tertiary dark:text-dark-bg-tertiary"
97- />
98- < circle
99- cx = { size / 2 }
100- cy = { size / 2 }
101- r = { radius }
102- stroke = "currentColor"
103- strokeWidth = { strokeWidth }
104- fill = "transparent"
105- strokeDasharray = { circumference }
106- strokeDashoffset = { offset }
107- strokeLinecap = "round"
108- className = { `${ color } transition-all duration-500 ease-out` }
109- />
110- </ svg >
111- < div className = "absolute inset-0 flex items-center justify-center" >
112- < span className = "text-lg font-bold text-text-primary dark:text-dark-text-primary" >
113- { progress } %
114- </ span >
115- </ div >
116- </ div >
117- ) ;
118- }
119-
120- function StatCard ( {
121- icon : Icon ,
122- label,
123- value,
124- color,
125- trend,
126- } : {
127- icon : typeof User ;
128- label : string ;
129- value : string | number ;
130- color : string ;
131- trend ?: { value : number ; positive : boolean } ;
132- } ) {
133- return (
134- < div className = "p-4 bg-bg-secondary dark:bg-dark-bg-secondary rounded-xl border border-border dark:border-dark-border hover:border-primary/30 transition-colors" >
135- < div className = "flex items-center gap-3 mb-2" >
136- < div className = { `p-2 rounded-lg ${ color } ` } >
137- < Icon className = "w-4 h-4" />
138- </ div >
139- < span className = "text-sm text-text-muted dark:text-dark-text-muted" > { label } </ span >
140- </ div >
141- < div className = "flex items-end gap-2" >
142- < span className = "text-2xl font-bold text-text-primary dark:text-dark-text-primary" >
143- { value }
144- </ span >
145- { trend && (
146- < span className = { `text-xs mb-1 ${ trend . positive ? 'text-success' : 'text-error' } ` } >
147- { trend . positive ? '+' : '' }
148- { trend . value } %
149- </ span >
150- ) }
151- </ div >
152- </ div >
153- ) ;
154- }
155-
156- function SectionCard ( {
157- title,
158- icon : Icon ,
159- children,
160- action,
161- } : {
162- title : string ;
163- icon : typeof User ;
164- children : React . ReactNode ;
165- action ?: { label : string ; onClick : ( ) => void } ;
166- } ) {
167- return (
168- < div className = "p-5 bg-bg-secondary dark:bg-dark-bg-secondary rounded-xl border border-border dark:border-dark-border" >
169- < div className = "flex items-center justify-between mb-4" >
170- < div className = "flex items-center gap-2" >
171- < Icon className = "w-5 h-5 text-primary" />
172- < h3 className = "font-semibold text-text-primary dark:text-dark-text-primary" > { title } </ h3 >
173- </ div >
174- { action && (
175- < button onClick = { action . onClick } className = "text-xs text-primary hover:underline" >
176- { action . label }
177- </ button >
178- ) }
179- </ div >
180- { children }
181- </ div >
182- ) ;
183- }
184-
185- function TagInput ( {
186- tags,
187- onAdd,
188- onRemove,
189- placeholder,
190- color = 'primary' ,
191- } : {
192- tags : string [ ] ;
193- onAdd : ( tag : string ) => void ;
194- onRemove : ( tag : string ) => void ;
195- placeholder : string ;
196- color ?: 'primary' | 'success' | 'warning' | 'error' ;
197- } ) {
198- const [ input , setInput ] = useState ( '' ) ;
199-
200- const colorClasses = {
201- primary : 'bg-primary/10 text-primary border-primary/20' ,
202- success : 'bg-success/10 text-success border-success/20' ,
203- warning : 'bg-warning/10 text-warning border-warning/20' ,
204- error : 'bg-error/10 text-error border-error/20' ,
205- } ;
206-
207- const handleKeyDown = ( e : React . KeyboardEvent ) => {
208- if ( e . key === 'Enter' && input . trim ( ) ) {
209- e . preventDefault ( ) ;
210- onAdd ( input . trim ( ) ) ;
211- setInput ( '' ) ;
212- }
213- } ;
214-
215- return (
216- < div className = "space-y-2" >
217- < div className = "flex flex-wrap gap-2" >
218- { tags . map ( ( tag ) => (
219- < span
220- key = { tag }
221- className = { `inline-flex items-center gap-1 px-2.5 py-1 rounded-full text-sm border ${ colorClasses [ color ] } ` }
222- >
223- { tag }
224- < button onClick = { ( ) => onRemove ( tag ) } className = "hover:opacity-70" >
225- < X className = "w-3 h-3" />
226- </ button >
227- </ span >
228- ) ) }
229- </ div >
230- < input
231- type = "text"
232- value = { input }
233- onChange = { ( e ) => setInput ( e . target . value ) }
234- onKeyDown = { handleKeyDown }
235- placeholder = { placeholder }
236- className = "w-full px-3 py-2 bg-bg-tertiary dark:bg-dark-bg-tertiary border border-border dark:border-dark-border rounded-lg text-sm text-text-primary dark:text-dark-text-primary placeholder:text-text-muted focus:outline-none focus:ring-2 focus:ring-primary/50"
237- />
238- </ div >
239- ) ;
240- }
61+ import { ProgressRing , StatCard , SectionCard , TagInput } from './ProfilePage.components' ;
24162
24263// =============================================================================
24364// Main Component
0 commit comments