Skip to content

Commit 160b5c9

Browse files
Zeff01claude
andcommitted
feat: Improve my-team page UX and add Admin position to signup
- Add ability to clear meeting schedule and return to "no meeting" state - Fix inconsistent button heights (all buttons now h-9 with consistent spacing) - Reorganize header into two rows to reduce crowding when Save Changes appears - Change proposal link button to navigate instead of copy to clipboard - Add Admin position option to signup form 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 87ab980 commit 160b5c9

5 files changed

Lines changed: 138 additions & 86 deletions

File tree

‎apps/codebility/app/auth/sign-up/page.tsx‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ const POSITIONS = [
3232
{ id: 4, name: "Mobile Developer" },
3333
{ id: 5, name: "UI/UX Designer" },
3434
{ id: 6, name: "QA Engineer" },
35+
{ id: 7, name: "Admin" },
3536
];
3637

3738
// Validation schema

‎apps/codebility/app/home/my-team/[projectId]/_components/ScheduleMeetingModal.tsx‎

Lines changed: 71 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,45 @@ const ScheduleMeetingModal = ({ isOpen, onClose, projectId, projectName, teamMem
6767
}));
6868
};
6969

70+
const handleClearSchedule = async () => {
71+
setIsSubmitting(true);
72+
try {
73+
// Clear the meeting schedule
74+
await saveMeetingSchedule(projectId, {
75+
selectedDays: [],
76+
time: "00:00"
77+
});
78+
79+
// Notify all team members about schedule removal
80+
const allMembers = teamLead ? [teamLead, ...teamMembers] : teamMembers;
81+
const notificationPromises = allMembers.map(member =>
82+
createNotificationAction({
83+
recipientId: member.id,
84+
title: "Meeting Schedule Removed",
85+
message: `The meeting schedule for ${projectName} has been removed.`,
86+
type: 'event',
87+
priority: 'medium',
88+
actionUrl: `/home/my-team/${projectId}`,
89+
metadata: {
90+
projectId,
91+
projectName,
92+
scheduleRemoved: true
93+
}
94+
})
95+
);
96+
97+
await Promise.all(notificationPromises);
98+
99+
toast.success("Meeting schedule removed!");
100+
onClose();
101+
} catch (error) {
102+
console.error("Error clearing meeting schedule:", error);
103+
toast.error("Failed to clear meeting schedule");
104+
} finally {
105+
setIsSubmitting(false);
106+
}
107+
};
108+
70109
const handleSubmit = async () => {
71110
if (!meetingData.time || meetingData.selectedDays.length === 0) {
72111
toast.error("Please select meeting days and time");
@@ -76,28 +115,28 @@ const ScheduleMeetingModal = ({ isOpen, onClose, projectId, projectName, teamMem
76115
setIsSubmitting(true);
77116
try {
78117
// Check if schedule has changed
79-
const hasExistingSchedule = currentSchedule &&
80-
currentSchedule.selectedDays.length > 0 &&
118+
const hasExistingSchedule = currentSchedule &&
119+
currentSchedule.selectedDays.length > 0 &&
81120
currentSchedule.time;
82-
121+
83122
const scheduleChanged = hasExistingSchedule && (
84123
JSON.stringify(currentSchedule.selectedDays.sort()) !== JSON.stringify(meetingData.selectedDays.sort()) ||
85124
currentSchedule.time !== meetingData.time
86125
);
87-
126+
88127
// Create a recurring meeting schedule
89128
const allMembers = teamLead ? [teamLead, ...teamMembers] : teamMembers;
90-
129+
91130
// Format selected days for display
92131
const selectedDaysDisplay = meetingData.selectedDays
93132
.map(day => weekDays.find(d => d.value === day)?.short)
94133
.join(", ");
95-
134+
96135
const notificationTitle = scheduleChanged ? "Meeting Schedule Changed" : "Meeting Schedule Set";
97136
const scheduleMessage = `Team meetings scheduled every ${selectedDaysDisplay} at ${meetingData.time} @ Discord`;
98-
137+
99138
// Send notifications to all team members
100-
const notificationPromises = allMembers.map(member =>
139+
const notificationPromises = allMembers.map(member =>
101140
createNotificationAction({
102141
recipientId: member.id,
103142
title: notificationTitle,
@@ -121,15 +160,15 @@ const ScheduleMeetingModal = ({ isOpen, onClose, projectId, projectName, teamMem
121160
);
122161

123162
await Promise.all(notificationPromises);
124-
163+
125164
// Save the meeting schedule
126165
await saveMeetingSchedule(projectId, {
127166
selectedDays: meetingData.selectedDays,
128167
time: meetingData.time
129168
});
130-
169+
131170
toast.success(scheduleChanged ? "Meeting schedule updated!" : "Meeting schedule set!");
132-
171+
133172
onClose();
134173
} catch (error) {
135174
console.error("Error setting meeting schedule:", error);
@@ -200,13 +239,27 @@ const ScheduleMeetingModal = ({ isOpen, onClose, projectId, projectName, teamMem
200239
</div>
201240
</div>
202241

203-
<DialogFooter>
204-
<Button variant="outline" onClick={onClose} disabled={isSubmitting}>
205-
Cancel
206-
</Button>
207-
<Button onClick={handleSubmit} disabled={isSubmitting}>
208-
{isSubmitting ? "Setting..." : "Set Schedule"}
209-
</Button>
242+
<DialogFooter className="flex-col sm:flex-row gap-2">
243+
<div className="flex gap-2 flex-1">
244+
{currentSchedule && currentSchedule.selectedDays.length > 0 && (
245+
<Button
246+
variant="outline"
247+
onClick={handleClearSchedule}
248+
disabled={isSubmitting}
249+
className="border-red-300 text-red-700 hover:bg-red-50 dark:border-red-600 dark:text-red-300 dark:hover:bg-red-900/20"
250+
>
251+
Clear Schedule
252+
</Button>
253+
)}
254+
</div>
255+
<div className="flex gap-2">
256+
<Button variant="outline" onClick={onClose} disabled={isSubmitting}>
257+
Cancel
258+
</Button>
259+
<Button onClick={handleSubmit} disabled={isSubmitting}>
260+
{isSubmitting ? "Setting..." : "Set Schedule"}
261+
</Button>
262+
</div>
210263
</DialogFooter>
211264
</DialogContent>
212265
</Dialog>

‎apps/codebility/app/home/my-team/[projectId]/_components/SyncAllAttendance.tsx‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,10 @@ export default function SyncAllAttendance({ projectId, isTeamLead }: SyncAllAtte
4343
disabled={isSyncing}
4444
size="sm"
4545
variant="outline"
46-
className="flex items-center gap-1.5 text-xs border-gray-300 text-gray-700 hover:bg-gray-50 dark:border-gray-600 dark:text-gray-300 dark:hover:bg-gray-800"
46+
className="flex items-center gap-2 h-9 border-gray-300 text-gray-700 hover:bg-gray-50 dark:border-gray-600 dark:text-gray-300 dark:hover:bg-gray-800"
4747
title="Sync all team members' attendance points"
4848
>
49-
<RefreshCw className={`h-3.5 w-3.5 ${isSyncing ? 'animate-spin' : ''}`} />
49+
<RefreshCw className={`h-4 w-4 ${isSyncing ? 'animate-spin' : ''}`} />
5050
{isSyncing ? 'Syncing Team...' : 'Sync All Points'}
5151
</Button>
5252
);

‎apps/codebility/app/home/my-team/[projectId]/_components/TeamDetailView.tsx‎

Lines changed: 61 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -272,15 +272,15 @@ const TeamDetailView = ({ projectData }: TeamDetailViewProps) => {
272272
)}
273273

274274
{/* Header with Tab Buttons */}
275-
<div className="flex items-center justify-between">
276-
{/* LEFT: View Mode Tabs + Checklist Button */}
275+
<div className="flex flex-col gap-3">
276+
{/* TOP ROW: View Mode Tabs + Checklist Button */}
277277
<div className="flex items-center gap-2">
278278
{/* Team View Tab - Always visible */}
279279
<Button
280280
variant={viewMode === "team" ? "default" : "outline"}
281281
size="sm"
282282
onClick={() => setViewMode("team")}
283-
className={`flex items-center gap-2 ${
283+
className={`flex items-center gap-2 h-9 ${
284284
viewMode !== "team" ? "border-gray-300 text-gray-700 hover:bg-gray-50 dark:border-gray-600 dark:text-gray-300 dark:hover:bg-gray-800" : ""
285285
}`}
286286
>
@@ -293,7 +293,7 @@ const TeamDetailView = ({ projectData }: TeamDetailViewProps) => {
293293
variant={viewMode === "attendance" ? "default" : "outline"}
294294
size="sm"
295295
onClick={() => setViewMode("attendance")}
296-
className={`flex items-center gap-2 ${
296+
className={`flex items-center gap-2 h-9 ${
297297
viewMode !== "attendance" ? "border-gray-300 text-gray-700 hover:bg-gray-50 dark:border-gray-600 dark:text-gray-300 dark:hover:bg-gray-800" : ""
298298
}`}
299299
>
@@ -308,77 +308,77 @@ const TeamDetailView = ({ projectData }: TeamDetailViewProps) => {
308308
variant="outline"
309309
size="sm"
310310
onClick={handleOpenChecklistModal}
311-
className="flex items-center gap-2 border-purple-300 text-purple-700 hover:bg-purple-50 dark:border-purple-600 dark:text-purple-300 dark:hover:bg-purple-900/20"
311+
className="flex items-center gap-2 h-9 border-purple-300 text-purple-700 hover:bg-purple-50 dark:border-purple-600 dark:text-purple-300 dark:hover:bg-purple-900/20"
312312
>
313313
<CheckSquare className="h-4 w-4" />
314314
Checklist
315315
</Button>
316316
)}
317317
</div>
318318

319-
{/* RIGHT: Action Buttons */}
320-
<div className="flex gap-2">
321-
{/* Save & Sync buttons - Only for team leads in attendance view */}
322-
{viewMode === "attendance" && isCurrentUserTeamLead && (
323-
<>
324-
{hasAttendanceChanges && (
325-
<Button
326-
onClick={handleSaveAttendance}
327-
variant="default"
328-
size="sm"
329-
className="flex items-center gap-1.5 text-xs"
330-
>
331-
<Save className="h-3.5 w-3.5" />
332-
Save Changes
333-
</Button>
334-
)}
335-
336-
<SyncAllAttendance
337-
projectId={projectInfo.id}
319+
{/* BOTTOM ROW: Action Buttons - Only shown when needed */}
320+
{((viewMode === "attendance" && isCurrentUserTeamLead) || (viewMode === "team" && isCurrentUserTeamLead) || true) && (
321+
<div className="flex items-center gap-2 flex-wrap">
322+
{/* Save & Sync buttons - Only for team leads in attendance view */}
323+
{viewMode === "attendance" && isCurrentUserTeamLead && hasAttendanceChanges && (
324+
<Button
325+
onClick={handleSaveAttendance}
326+
variant="default"
327+
size="sm"
328+
className="flex items-center gap-2 h-9"
329+
>
330+
<Save className="h-4 w-4" />
331+
Save Changes
332+
</Button>
333+
)}
334+
335+
{viewMode === "attendance" && isCurrentUserTeamLead && (
336+
<SyncAllAttendance
337+
projectId={projectInfo.id}
338338
isTeamLead={isCurrentUserTeamLead}
339339
/>
340-
</>
341-
)}
342-
343-
{/* Manage Members - ONLY VISIBLE TO TEAM LEADS in Team View */}
344-
{viewMode === "team" && isCurrentUserTeamLead && (
345-
<Button
346-
onClick={handleOpenAddModal}
347-
disabled={isLoadingMembers}
348-
size="sm"
349-
className="flex items-center gap-1.5 text-xs px-3 py-1.5 h-auto max-w-[200px]"
350-
>
351-
<UserPlus className="h-3.5 w-3.5" />
352-
{isLoadingMembers ? 'Loading...' : 'Manage Members'}
353-
</Button>
354-
)}
355-
356-
{/* Schedule Meeting - ONLY VISIBLE TO TEAM LEADS */}
357-
{isCurrentUserTeamLead && (
340+
)}
341+
342+
{/* Manage Members - ONLY VISIBLE TO TEAM LEADS in Team View */}
343+
{viewMode === "team" && isCurrentUserTeamLead && (
344+
<Button
345+
onClick={handleOpenAddModal}
346+
disabled={isLoadingMembers}
347+
size="sm"
348+
className="flex items-center gap-2 h-9"
349+
>
350+
<UserPlus className="h-4 w-4" />
351+
{isLoadingMembers ? 'Loading...' : 'Manage Members'}
352+
</Button>
353+
)}
354+
355+
{/* Schedule Meeting - ONLY VISIBLE TO TEAM LEADS */}
356+
{isCurrentUserTeamLead && (
357+
<Button
358+
variant="outline"
359+
size="sm"
360+
className="flex items-center gap-2 h-9 border-gray-300 text-gray-700 hover:bg-gray-50 dark:border-gray-600 dark:text-gray-300 dark:hover:bg-gray-800"
361+
title="Schedule Meetings"
362+
onClick={() => setShowScheduleMeetingModal(true)}
363+
>
364+
<CalendarDays className="h-4 w-4" />
365+
Schedule Meeting
366+
</Button>
367+
)}
368+
369+
{/* Kanban Board Button - VISIBLE TO ALL MEMBERS */}
358370
<Button
359371
variant="outline"
360372
size="sm"
361-
className="flex items-center gap-1.5 text-xs px-3 py-1.5 h-auto border-gray-300 text-gray-700 hover:bg-gray-50 dark:border-gray-600 dark:text-gray-300 dark:hover:bg-gray-800"
362-
title="Schedule Meetings"
363-
onClick={() => setShowScheduleMeetingModal(true)}
373+
className="flex items-center gap-2 h-9 border-purple-300 text-purple-700 hover:bg-purple-50 dark:border-purple-600 dark:text-purple-300 dark:hover:bg-purple-900/20"
374+
title="Go to Kanban Board"
375+
onClick={() => router.push(`/home/kanban/${projectInfo.id}`)}
364376
>
365-
<CalendarDays className="h-3.5 w-3.5" />
366-
Schedule Meeting
377+
<Kanban className="h-4 w-4" />
378+
Kanban Board
367379
</Button>
368-
)}
369-
370-
{/* Kanban Board Button - VISIBLE TO ALL MEMBERS */}
371-
<Button
372-
variant="outline"
373-
size="sm"
374-
className="flex items-center gap-1.5 text-xs px-3 py-1.5 h-auto border-purple-300 text-purple-700 hover:bg-purple-50 dark:border-purple-600 dark:text-purple-300 dark:hover:bg-purple-900/20"
375-
title="Go to Kanban Board"
376-
onClick={() => router.push(`/home/kanban/${projectInfo.id}`)}
377-
>
378-
<Kanban className="h-3.5 w-3.5" />
379-
Kanban Board
380-
</Button>
381-
</div>
380+
</div>
381+
)}
382382
</div>
383383

384384
{viewMode === "team" ? (

‎apps/codebility/app/home/settings/services/page.tsx‎

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1584,16 +1584,14 @@ export default function ServicesPage() {
15841584

15851585
<Button
15861586
onClick={() => {
1587-
const proposalUrl = `${window.location.origin}/proposal`;
1588-
navigator.clipboard.writeText(proposalUrl);
1589-
toast.success("Proposal link copied to clipboard!");
1587+
window.location.href = "/proposal";
15901588
}}
15911589
className="bg-gradient-to-r from-blue-500 to-purple-500 hover:from-blue-600 hover:to-purple-600 text-white"
15921590
>
15931591
<svg className="h-4 w-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
1594-
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z" />
1592+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" />
15951593
</svg>
1596-
Copy Proposal Link
1594+
View Proposal
15971595
</Button>
15981596
</div>
15991597
</div>

0 commit comments

Comments
 (0)