Skip to content

Commit 0e1e6b6

Browse files
author
raineer
committed
Fix: Missing members in checklist modal (Arishavelle, Reagan)Fix Hire Codevs Showcase Qualification Filter
fix(in-house): enhance table spacing per team lead feedbackfix(in-house): improve table spacing and alignment
1 parent 3a1c4c4 commit 0e1e6b6

4 files changed

Lines changed: 1468 additions & 147 deletions

File tree

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

Lines changed: 87 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,29 @@
22

33
import { useState, useEffect } from "react";
44
import { ChevronDown, ChevronUp, AlertCircle, CheckCircle, User } from "lucide-react";
5-
import { SimpleMemberData } from "@/app/home/projects/actions";
5+
import { SimpleMemberData, getMembers, getTeamLead } from "@/app/home/projects/actions";
66
import { createClientClientComponent } from "@/utils/supabase/client";
77

8+
/**
9+
* ChecklistStatusBanner - ENHANCED SELF-FETCHING VERSION
10+
*
11+
* FIXES APPLIED:
12+
* 1. ✅ Fetches fresh member data internally (no stale props)
13+
* 2. ✅ Always shows current database state
14+
* 3. ✅ Automatic refresh when component mounts
15+
* 4. ✅ No dependency on parent component state
16+
* 5. ✅ Props now optional for backward compatibility
17+
*
18+
* KEY CHANGE:
19+
* - Does NOT rely on teamMembers/teamLead props from parent
20+
* - Fetches fresh data from getMembers() and getTeamLead()
21+
* - Guarantees accurate member count display
22+
*/
23+
824
interface ChecklistStatusBannerProps {
925
projectId: string;
10-
teamMembers: SimpleMemberData[];
11-
teamLead: SimpleMemberData | null;
26+
teamMembers?: SimpleMemberData[]; // Optional - will fetch if not provided
27+
teamLead?: SimpleMemberData | null; // Optional - will fetch if not provided
1228
}
1329

1430
interface MemberChecklistStatus {
@@ -19,16 +35,62 @@ interface MemberChecklistStatus {
1935
pendingItems: number;
2036
}
2137

22-
const ChecklistStatusBanner = ({ projectId, teamMembers, teamLead }: ChecklistStatusBannerProps) => {
38+
const ChecklistStatusBanner = ({ projectId }: ChecklistStatusBannerProps) => {
2339
const [isExpanded, setIsExpanded] = useState(false);
2440
const [isLoading, setIsLoading] = useState(true);
2541
const [memberStatuses, setMemberStatuses] = useState<MemberChecklistStatus[]>([]);
42+
43+
// ✅ NEW: Fresh member data (fetched internally)
44+
const [freshTeamMembers, setFreshTeamMembers] = useState<SimpleMemberData[]>([]);
45+
const [freshTeamLead, setFreshTeamLead] = useState<SimpleMemberData | null>(null);
46+
const [isFetchingMembers, setIsFetchingMembers] = useState(false);
2647

27-
const allMembers = teamLead ? [teamLead, ...teamMembers] : teamMembers;
48+
// ✅ CRITICAL FIX: Fetch fresh member data on mount
49+
useEffect(() => {
50+
if (projectId) {
51+
fetchFreshMemberData();
52+
}
53+
}, [projectId]);
54+
55+
// Fetch fresh member data from database
56+
const fetchFreshMemberData = async () => {
57+
console.log("🔄 ChecklistStatusBanner: Fetching fresh member data...");
58+
setIsFetchingMembers(true);
59+
60+
try {
61+
// Fetch team lead
62+
const teamLeadResult = await getTeamLead(projectId);
63+
if (teamLeadResult.data) {
64+
setFreshTeamLead(teamLeadResult.data);
65+
console.log("👑 Banner - Team Lead:", teamLeadResult.data.first_name, teamLeadResult.data.last_name);
66+
}
2867

68+
// Fetch members
69+
const membersResult = await getMembers(projectId);
70+
if (membersResult.data) {
71+
setFreshTeamMembers(membersResult.data);
72+
console.log("👥 Banner - Fresh members fetched:", membersResult.data.length);
73+
}
74+
} catch (error) {
75+
console.error("❌ Banner - Error fetching fresh member data:", error);
76+
} finally {
77+
setIsFetchingMembers(false);
78+
}
79+
};
80+
81+
// Use fresh data instead of props
82+
const allMembers = freshTeamLead
83+
? [freshTeamLead, ...freshTeamMembers]
84+
: freshTeamMembers;
85+
86+
console.log("📊 Banner - Total members for display:", allMembers.length);
87+
88+
// Load checklist statuses when we have member data
2989
useEffect(() => {
30-
loadChecklistStatuses();
31-
}, [projectId, allMembers.length]);
90+
if (!isFetchingMembers && allMembers.length > 0) {
91+
loadChecklistStatuses();
92+
}
93+
}, [projectId, allMembers.length, isFetchingMembers]);
3294

3395
const loadChecklistStatuses = async () => {
3496
setIsLoading(true);
@@ -68,6 +130,7 @@ const ChecklistStatusBanner = ({ projectId, teamMembers, teamLead }: ChecklistSt
68130
.sort((a, b) => b.pendingItems - a.pendingItems);
69131

70132
setMemberStatuses(statusArray);
133+
console.log("✅ Banner - Loaded statuses for", statusArray.length, "members with checklist items");
71134
} catch (error) {
72135
console.error("Error loading checklist statuses:", error);
73136
} finally {
@@ -80,6 +143,18 @@ const ChecklistStatusBanner = ({ projectId, teamMembers, teamLead }: ChecklistSt
80143
const totalPendingItems = memberStatuses.reduce((sum, s) => sum + s.pendingItems, 0);
81144
const membersFullyCompleted = memberStatuses.filter(s => s.pendingItems === 0).length;
82145

146+
// Show loading state while fetching members
147+
if (isFetchingMembers) {
148+
return (
149+
<div className="rounded-lg border-2 border-gray-300 dark:border-gray-700 bg-gray-50 dark:bg-gray-900/20 p-3">
150+
<div className="flex items-center gap-2">
151+
<div className="animate-spin rounded-full h-4 w-4 border-b-2 border-gray-500"></div>
152+
<p className="text-xs text-gray-600 dark:text-gray-400">Loading checklist status...</p>
153+
</div>
154+
</div>
155+
);
156+
}
157+
83158
// Don't show if no checklist items exist
84159
if (!isLoading && memberStatuses.length === 0) {
85160
return null;
@@ -199,7 +274,10 @@ const ChecklistStatusBanner = ({ projectId, teamMembers, teamLead }: ChecklistSt
199274
: 'border-green-300 dark:border-green-800 bg-green-50/50 dark:bg-green-950/10'
200275
}`}>
201276
<button
202-
onClick={loadChecklistStatuses}
277+
onClick={() => {
278+
fetchFreshMemberData();
279+
loadChecklistStatuses();
280+
}}
203281
className={`text-xs font-medium ${
204282
membersWithPending > 0
205283
? 'text-red-700 dark:text-red-300 hover:text-red-800 dark:hover:text-red-200'
@@ -215,4 +293,4 @@ const ChecklistStatusBanner = ({ projectId, teamMembers, teamLead }: ChecklistSt
215293
);
216294
};
217295

218-
export default ChecklistStatusBanner;
296+
export default ChecklistStatusBanner;

‎apps/codebility/app/home/my-team/_components/ChecklistManageModal.tsx‎

Lines changed: 98 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,23 @@ import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, DialogD
55
import Input from "@/components/ui/forms/input";
66
import toast from "react-hot-toast";
77
import { createClientClientComponent } from "@/utils/supabase/client";
8-
import { SimpleMemberData } from "@/app/home/projects/actions";
8+
import { SimpleMemberData, getMembers, getTeamLead } from "@/app/home/projects/actions";
99

1010
/**
11-
* ChecklistManageModal - TRIPLE-FIXED VERSION
11+
* ChecklistManageModal - ENHANCED SELF-FETCHING VERSION
1212
*
1313
* FIXES APPLIED:
14-
* 1. ✅ Enhanced delete confirmation dialog (no double quotes around item name)
15-
* 2. ✅ Removed ALL emoji from toasts (library provides icons automatically)
16-
* 3. ✅ Removed ALL double quotes around item names in toasts
17-
* 4. ✅ Clean, professional toast messages
18-
* 5. ✅ Improved dialog styling and UX
14+
* 1. ✅ Fetches fresh member data internally (no stale props)
15+
* 2. ✅ Always shows current database state
16+
* 3. ✅ Automatic refresh when modal opens
17+
* 4. ✅ No dependency on parent component state
18+
* 5. ✅ Enhanced delete confirmation dialog
19+
* 6. ✅ Clean toast messages (no emoji, no double quotes)
20+
*
21+
* KEY CHANGE:
22+
* - Does NOT rely on teamMembers prop from parent
23+
* - Fetches fresh data from getMembers() when modal opens
24+
* - Guarantees accurate member count for checklists
1925
*
2026
* AUTHENTICATION:
2127
* - Self-contained (no currentUserId prop needed)
@@ -31,17 +37,15 @@ interface ChecklistManageModalProps {
3137
isOpen: boolean;
3238
projectId: string;
3339
projectName: string;
34-
teamMembers: SimpleMemberData[];
35-
teamLeadId: string;
40+
teamMembers?: SimpleMemberData[]; // Optional, for backward compatibility
41+
teamLeadId?: string; // Optional, will fetch if not provided
3642
onClose: () => void;
3743
}
3844

3945
const ChecklistManageModal = ({
4046
isOpen,
4147
projectId,
4248
projectName,
43-
teamMembers,
44-
teamLeadId,
4549
onClose
4650
}: ChecklistManageModalProps) => {
4751
// State
@@ -53,6 +57,11 @@ const ChecklistManageModal = ({
5357
const [isFetching, setIsFetching] = useState(false);
5458
const [supabase, setSupabase] = useState<any>(null);
5559

60+
// Fresh member data (fetched internally)
61+
const [freshTeamMembers, setFreshTeamMembers] = useState<SimpleMemberData[]>([]);
62+
const [freshTeamLeadId, setFreshTeamLeadId] = useState<string>("");
63+
const [isFetchingMembers, setIsFetchingMembers] = useState(false);
64+
5665
// Self-contained auth
5766
const [currentCodevId, setCurrentCodevId] = useState<string | null>(null);
5867
const [isTeamLead, setIsTeamLead] = useState(false);
@@ -62,52 +71,95 @@ const ChecklistManageModal = ({
6271
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
6372
const [itemToDelete, setItemToDelete] = useState<string | null>(null);
6473

65-
// Initialize Supabase and get current user
74+
// Initialize Supabase
6675
useEffect(() => {
6776
const client = createClientClientComponent();
6877
setSupabase(client);
78+
}, []);
79+
80+
// ✅ CRITICAL FIX: Fetch fresh member data when modal opens
81+
useEffect(() => {
82+
if (isOpen && projectId) {
83+
fetchFreshMemberData();
84+
}
85+
}, [isOpen, projectId]);
86+
87+
// Fetch fresh member data from database
88+
const fetchFreshMemberData = async () => {
89+
console.log("🔄 ChecklistManageModal: Fetching fresh member data...");
90+
setIsFetchingMembers(true);
6991

70-
if (client) {
71-
client.auth.getSession().then(async ({ data: { session } }) => {
72-
if (session?.user?.email) {
73-
// Map auth user email to codev_id
74-
const { data: codevData } = await client
75-
.from("codev")
76-
.select("id")
77-
.eq("email_address", session.user.email)
78-
.single();
79-
80-
if (codevData) {
81-
setCurrentCodevId(codevData.id);
82-
// Check if this user is the team lead
83-
setIsTeamLead(codevData.id === teamLeadId);
84-
}
85-
}
86-
setAuthChecked(true);
87-
});
92+
try {
93+
// Fetch team lead
94+
const teamLeadResult = await getTeamLead(projectId);
95+
if (teamLeadResult.data?.id) {
96+
setFreshTeamLeadId(teamLeadResult.data.id);
97+
console.log("👑 Team Lead ID:", teamLeadResult.data.id);
98+
}
99+
100+
// Fetch members
101+
const membersResult = await getMembers(projectId);
102+
if (membersResult.data) {
103+
setFreshTeamMembers(membersResult.data);
104+
console.log("👥 Fresh members fetched:", membersResult.data.length);
105+
console.log("📝 Member names:", membersResult.data.map(m => `${m.first_name} ${m.last_name}`));
106+
}
107+
} catch (error) {
108+
console.error("❌ Error fetching fresh member data:", error);
109+
toast.error("Failed to load team members");
110+
} finally {
111+
setIsFetchingMembers(false);
88112
}
89-
}, [teamLeadId]);
113+
};
114+
115+
// Get current user and check auth
116+
useEffect(() => {
117+
if (!supabase || !freshTeamLeadId) return;
118+
119+
supabase.auth.getSession().then(async ({ data: { session } }: any) => {
120+
if (session?.user?.email) {
121+
// Map auth user email to codev_id
122+
const { data: codevData } = await supabase
123+
.from("codev")
124+
.select("id")
125+
.eq("email_address", session.user.email)
126+
.single();
127+
128+
if (codevData) {
129+
setCurrentCodevId(codevData.id);
130+
// Check if this user is the team lead
131+
setIsTeamLead(codevData.id === freshTeamLeadId);
132+
console.log("🔐 Current user is team lead:", codevData.id === freshTeamLeadId);
133+
}
134+
}
135+
setAuthChecked(true);
136+
});
137+
}, [supabase, freshTeamLeadId]);
90138

91139
// Get all member IDs (team lead + members)
92140
const allMemberIds = useMemo(() => {
93-
if (!teamLeadId) {
141+
if (!freshTeamLeadId) {
94142
return [];
95143
}
96144

97-
if (!teamMembers || !Array.isArray(teamMembers)) {
98-
return [teamLeadId];
145+
if (!freshTeamMembers || !Array.isArray(freshTeamMembers)) {
146+
return [freshTeamLeadId];
99147
}
100148

101-
const memberIds = teamMembers.map(m => m.id).filter(Boolean);
102-
return [teamLeadId, ...memberIds];
103-
}, [teamLeadId, teamMembers]);
149+
const memberIds = freshTeamMembers.map(m => m.id).filter(Boolean);
150+
const allIds = [freshTeamLeadId, ...memberIds];
151+
152+
console.log(`📊 Total team members for checklist: ${allIds.length} (1 lead + ${memberIds.length} members)`);
153+
154+
return allIds;
155+
}, [freshTeamLeadId, freshTeamMembers]);
104156

105157
// Load checklist items when modal opens
106158
useEffect(() => {
107-
if (isOpen && supabase && projectId) {
159+
if (isOpen && supabase && projectId && allMemberIds.length > 0) {
108160
loadChecklistItems();
109161
}
110-
}, [isOpen, supabase, projectId]);
162+
}, [isOpen, supabase, projectId, allMemberIds.length]);
111163

112164
// Load checklist items from database
113165
const loadChecklistItems = async () => {
@@ -177,7 +229,7 @@ const ChecklistManageModal = ({
177229
description: null,
178230
priority: "medium",
179231
completed: false,
180-
created_by: teamLeadId,
232+
created_by: freshTeamLeadId,
181233
due_date: null,
182234
created_at: new Date().toISOString(),
183235
updated_at: new Date().toISOString()
@@ -191,7 +243,6 @@ const ChecklistManageModal = ({
191243
if (error) {
192244
toast.error(`Failed: ${error.message}`);
193245
} else {
194-
// FIXED: No emoji, no double quotes, clean message
195246
toast.success(`${newItemTitle.trim()} added to checklist`);
196247
setItems(prev => [...prev, newItemTitle.trim()]);
197248
setNewItemTitle("");
@@ -252,7 +303,6 @@ const ChecklistManageModal = ({
252303
if (error) {
253304
toast.error("Failed to update item");
254305
} else {
255-
// FIXED: No emoji, no double quotes
256306
toast.success(`${oldTitle} updated to ${editingNewTitle.trim()}`);
257307
setItems(prev => prev.map(item => item === oldTitle ? editingNewTitle.trim() : item));
258308
handleCancelEdit();
@@ -300,7 +350,6 @@ const ChecklistManageModal = ({
300350
if (error) {
301351
toast.error("Failed to delete item");
302352
} else {
303-
// FIXED: No emoji, no double quotes
304353
toast.success(`${itemToDelete} removed from checklist`);
305354
setItems(prev => prev.filter(item => item !== itemToDelete));
306355
handleCancelDelete();
@@ -322,15 +371,17 @@ const ChecklistManageModal = ({
322371
}
323372
};
324373

325-
// Show loading state while checking auth
326-
if (!authChecked) {
374+
// Show loading state while fetching members or checking auth
375+
if (isFetchingMembers || !authChecked) {
327376
return (
328377
<Dialog open={isOpen} onOpenChange={handleClose}>
329378
<DialogContent className="max-w-md bg-white dark:bg-gray-900">
330379
<div className="flex items-center justify-center py-8">
331380
<div className="text-center">
332381
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-500 mx-auto mb-4"></div>
333-
<p className="text-sm text-gray-600 dark:text-gray-400">Loading...</p>
382+
<p className="text-sm text-gray-600 dark:text-gray-400">
383+
{isFetchingMembers ? "Loading team members..." : "Loading..."}
384+
</p>
334385
</div>
335386
</div>
336387
</DialogContent>
@@ -514,7 +565,7 @@ const ChecklistManageModal = ({
514565
</DialogContent>
515566
</Dialog>
516567

517-
{/* ENHANCED Delete Confirmation Dialog - NO DOUBLE QUOTES */}
568+
{/* Delete Confirmation Dialog */}
518569
<Dialog open={deleteDialogOpen} onOpenChange={setDeleteDialogOpen}>
519570
<DialogContent className="max-w-md bg-white dark:bg-gray-900">
520571
<DialogHeader>

0 commit comments

Comments
 (0)