Skip to content

Commit 62ffb69

Browse files
updated profile completion system
1 parent 8555a09 commit 62ffb69

8 files changed

Lines changed: 265 additions & 18 deletions

File tree

‎apps/codebility/app/home/(dashboard)/_components/DashboardCompleteProfile.tsx‎

Lines changed: 47 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@ export default function DashboardCompleteProfile({
8080
workExperience: false,
8181
});
8282

83+
const [removedTasks, setRemovedTasks] = useState<Record<string, boolean>>({});
84+
8385
const [loading, setLoading] = useState(true);
8486
const [totalPoints, setTotalPoints] = useState(0);
8587

@@ -172,7 +174,7 @@ export default function DashboardCompleteProfile({
172174
return acc;
173175
}, {});
174176

175-
setCompletedTasks({
177+
const newCompletedTasks = {
176178
profilePhoto: !!pointsMap.image_url,
177179
skillsList: !!pointsMap.tech_stacks,
178180
aboutUser: !!pointsMap.about,
@@ -184,8 +186,16 @@ export default function DashboardCompleteProfile({
184186
!!pointsMap.discord,
185187
portfolioWorks: !!pointsMap.portfolio_website,
186188
workExperience: !!pointsMap.work_experience,
189+
};
190+
191+
// Check for newly completed tasks and remove them immediately
192+
Object.keys(newCompletedTasks).forEach((taskId) => {
193+
if (newCompletedTasks[taskId] && !completedTasks[taskId]) {
194+
setRemovedTasks((prev) => ({ ...prev, [taskId]: true }));
195+
}
187196
});
188197

198+
setCompletedTasks(newCompletedTasks);
189199
setTotalPoints(data.totalPoints);
190200
}
191201
} catch (error) {
@@ -202,17 +212,37 @@ export default function DashboardCompleteProfile({
202212
const totalTasks = tasks.length;
203213
const progressPercentage = (totalCompleted / totalTasks) * 100;
204214

215+
// Filter out removed tasks
216+
const visibleTasks = tasks.filter((task) => !removedTasks[task.id]);
217+
218+
const allTasksCompleted = totalCompleted === totalTasks;
219+
205220
/* ------------------- Render ------------------- */
206221
if (loading) {
207222
return (
208223
<Box className="flex w-full flex-1 flex-col gap-6 relative overflow-hidden">
209224
<div className="space-y-4">
210225
<div className="flex items-center justify-between">
211-
<h2 className="text-xl font-semibold text-white tracking-tight">
212-
Complete Your Profile
213-
</h2>
226+
<div className="h-7 w-48 bg-gray-700/50 rounded animate-pulse"></div>
227+
<div className="h-6 w-12 bg-gray-700/50 rounded-full animate-pulse"></div>
228+
</div>
229+
230+
<div className="w-full bg-gray-700/50 rounded-full h-2 animate-pulse"></div>
231+
232+
<div className="space-y-3">
233+
{[1, 2, 3, 4, 5, 6].map((i) => (
234+
<div
235+
key={i}
236+
className="rounded-lg border border-gray-700 bg-gray-800/50 p-4 animate-pulse"
237+
>
238+
<div className="flex items-center gap-3">
239+
<div className="w-4 h-4 rounded-full bg-gray-700"></div>
240+
<div className="h-5 flex-1 bg-gray-700/50 rounded"></div>
241+
<div className="h-5 w-16 bg-gray-700/50 rounded-full"></div>
242+
</div>
243+
</div>
244+
))}
214245
</div>
215-
<div className="text-center text-gray-400 py-8">Loading...</div>
216246
</div>
217247
</Box>
218248
);
@@ -223,7 +253,7 @@ export default function DashboardCompleteProfile({
223253
<div className="space-y-4">
224254
<div className="flex items-center justify-between">
225255
<h2 className="text-xl font-semibold text-white tracking-tight">
226-
Complete Your Profile
256+
{allTasksCompleted ? "You're All Set! 🎉" : "Complete Your Profile"}
227257
</h2>
228258
<Badge variant="secondary" className="text-sm font-semibold">
229259
{totalCompleted}/{totalTasks}
@@ -232,8 +262,16 @@ export default function DashboardCompleteProfile({
232262

233263
<Progress value={progressPercentage} className="h-2" />
234264

265+
{allTasksCompleted && visibleTasks.length === 0 && (
266+
<div className="text-center py-8 space-y-2">
267+
<p className="text-lg font-medium text-green-400">
268+
Congratulations! Your profile is all set.
269+
</p>
270+
</div>
271+
)}
272+
235273
<div className="space-y-3">
236-
{tasks.map((task) => (
274+
{visibleTasks.map((task) => (
237275
<div
238276
key={task.id}
239277
className="rounded-lg border border-gray-700 bg-gray-800/50 overflow-hidden"
@@ -244,7 +282,7 @@ export default function DashboardCompleteProfile({
244282
>
245283
<div className="flex items-center gap-3 flex-1">
246284
<div
247-
className={`w-4 h-4 rounded-full border ${
285+
className={`w-4 h-4 rounded-full border transition-all duration-300 ${
248286
completedTasks[task.id]
249287
? "bg-green-500 border-green-500"
250288
: "bg-gray-700 border-gray-600"
@@ -284,4 +322,4 @@ export default function DashboardCompleteProfile({
284322
</div>
285323
</Box>
286324
);
287-
}
325+
}

‎apps/codebility/app/home/settings/profile/_components/About.tsx‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ const About = ({ data }: AboutProps) => {
3838
},
3939
});
4040

41+
const currentAboutValue = watch("about");
42+
4143
// Check if user has earned points for the 'about' field
4244
useEffect(() => {
4345
async function checkAboutPoints() {
@@ -94,13 +96,16 @@ const About = ({ data }: AboutProps) => {
9496
setIsEditMode(false);
9597
};
9698

99+
// Show message only if: field is empty OR (field has content but no points earned yet)
100+
const shouldShowMessage = !currentAboutValue || (!hasAboutPoints && currentAboutValue);
101+
97102
return (
98103
<Box className="bg-light-900 dark:bg-dark-100 relative flex flex-col gap-2">
99104
<div className="flex items-center justify-between">
100105
<p className="text-lg">About</p>
101106

102107
<div className="flex items-center gap-2">
103-
{!hasAboutPoints && (
108+
{!hasAboutPoints && !currentAboutValue && (
104109
<span className="text-xs text-green-600 flex items-center gap-1">
105110
<svg
106111
className="h-3 w-3"

‎apps/codebility/app/home/settings/profile/_components/ContactInfo.tsx‎

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ const ContactInfo = ({ data }: ContactInfoProps) => {
4343
handleSubmit,
4444
reset,
4545
control,
46+
watch,
4647
formState: { isDirty },
4748
} = useForm<FormValues>({
4849
defaultValues: {
@@ -55,6 +56,9 @@ const ContactInfo = ({ data }: ContactInfoProps) => {
5556
},
5657
});
5758

59+
// Watch all contact fields
60+
const watchedFields = watch();
61+
5862
// Check if user has earned points for any contact field
5963
useEffect(() => {
6064
async function checkContactPoints() {
@@ -124,13 +128,24 @@ const ContactInfo = ({ data }: ContactInfoProps) => {
124128
setIsEditMode(false);
125129
};
126130

131+
// Check if any field is empty
132+
const hasEmptyFields = !watchedFields.phone_number ||
133+
!watchedFields.portfolio_website ||
134+
!watchedFields.github ||
135+
!watchedFields.linkedin ||
136+
!watchedFields.facebook ||
137+
!watchedFields.discord;
138+
139+
// Show message only if: has empty fields AND hasn't earned points yet
140+
const shouldShowMessage = hasEmptyFields && !hasContactPoints;
141+
127142
return (
128143
<Box className="bg-light-900 dark:bg-dark-100 relative flex flex-col gap-6">
129144
<div className="flex items-center justify-between">
130145
<p className="text-lg">Contact Info</p>
131146

132147
<div className="flex items-center gap-2">
133-
{!hasContactPoints && (
148+
{shouldShowMessage && (
134149
<span className="text-xs text-green-600 flex items-center gap-1">
135150
<svg
136151
className="h-3 w-3"

‎apps/codebility/app/home/settings/profile/_components/Experience.tsx‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,12 +123,18 @@ const Experience = ({ data, codevId }: ExperienceProps) => {
123123
return true;
124124
}, [experienceData]);
125125

126+
// Check if there are no experiences or all are empty
127+
const hasNoExperience = experienceData.length === 0;
128+
129+
// Show message only if: no experiences added AND hasn't earned points yet
130+
const shouldShowMessage = hasNoExperience && !hasWorkExperiencePoints;
131+
126132
return (
127133
<Box className="bg-light-900 dark:bg-dark-100 relative flex flex-col gap-2">
128134
<div className="flex items-center justify-between">
129135
<p className="text-lg">Experience</p>
130136

131-
{!hasWorkExperiencePoints && (
137+
{shouldShowMessage && (
132138
<span className="text-xs text-green-600 flex items-center gap-1">
133139
<svg
134140
className="h-3 w-3"

‎apps/codebility/app/home/settings/profile/_components/Photo.tsx‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -100,15 +100,15 @@ const Photo = ({ data }: PhotoProps) => {
100100
onOpen("deleteWarningModal", {}, {}, handleRemoveAvatar);
101101
};
102102

103-
// Check if user has an image (either from points API or directly from data)
104-
const hasImage = !!data.image_url || hasImagePoints;
103+
// Show message only if: no image_url AND hasn't earned points yet
104+
const shouldShowMessage = !data.image_url && !hasImagePoints;
105105

106106
return (
107107
<Box className="bg-light-900 dark:bg-dark-100 relative flex flex-col gap-2">
108108
<div className="flex items-center justify-between">
109109
<p className="text-lg">Your Photo</p>
110110

111-
{!hasImage && (
111+
{shouldShowMessage && (
112112
<span className="text-xs text-green-600 flex items-center gap-1">
113113
<svg
114114
className="h-3 w-3"

0 commit comments

Comments
 (0)