fix(admin): remove enabled packs filter from outstanding questions query

The couple_packs table has no admin RLS policy, so the enabled packs
query returns empty for admin users. Query all partner-answered
questions instead — more useful for admin visibility anyway.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
jjbruntonandClaude Opus 4.5 committed 2026-01-31 22:58:21 +00:00
1 parent edb2cedda0
commit 8a44fef764
1 file changed
+70 -79
+70 -79
View File
@@ -493,96 +493,87 @@ export function UserDetailPage() {
// Use partner from state (set earlier in the first couple_id block) // Use partner from state (set earlier in the first couple_id block)
const partnerProfile = partner || (await supabase.from('profiles').select('id, name').eq('couple_id', profileData.couple_id).neq('id', userId).maybeSingle()).data; const partnerProfile = partner || (await supabase.from('profiles').select('id, name').eq('couple_id', profileData.couple_id).neq('id', userId).maybeSingle()).data;
if (partnerProfile) { if (partnerProfile) {
const enabledPackIds = (enabledPacksData || []).map((ep: any) => ep.pack_id); // Get question IDs user has already answered
const { data: userResponseIds } = await supabase
.from('responses')
.select('question_id')
.eq('user_id', userId);
const answeredIds = new Set((userResponseIds || []).map((r: any) => r.question_id));
if (enabledPackIds.length > 0) { // Get all partner responses in this couple
// Get question IDs user has already answered const { data: partnerResponses } = await supabase
const { data: userResponseIds } = await supabase .from('responses')
.from('responses') .select(`
.select('question_id') question_id,
.eq('user_id', userId); answer,
const answeredIds = new Set((userResponseIds || []).map((r: any) => r.question_id)); created_at
`)
.eq('user_id', partnerProfile.id)
.eq('couple_id', profileData.couple_id)
.order('created_at', { ascending: false });
// Get all partner responses in this couple // Filter to only questions user hasn't answered
const { data: partnerResponses } = await supabase const unansweredPartnerResponses = (partnerResponses || []).filter((r: any) => !answeredIds.has(r.question_id));
.from('responses') const unansweredQuestionIds = unansweredPartnerResponses.map((r: any) => r.question_id);
.select(`
question_id,
answer,
created_at
`)
.eq('user_id', partnerProfile.id)
.eq('couple_id', profileData.couple_id)
.order('created_at', { ascending: false });
// Filter to only questions user hasn't answered if (unansweredQuestionIds.length > 0) {
const unansweredPartnerResponses = (partnerResponses || []).filter((r: any) => !answeredIds.has(r.question_id)); // Fetch question details (no pack filter — admin sees all)
const unansweredQuestionIds = unansweredPartnerResponses.map((r: any) => r.question_id); const { data: questionDetails } = await supabase
.from('questions')
.select('id, text, intensity, pack:question_packs(id, name)')
.in('id', unansweredQuestionIds);
if (unansweredQuestionIds.length > 0) { const questionMap = new Map((questionDetails || []).map((q: any) => [q.id, q]));
// Fetch question details, filtered to enabled packs const partnerResponseMap = new Map(unansweredPartnerResponses.map((r: any) => [r.question_id, r]));
const { data: questionDetails } = await supabase
.from('questions')
.select('id, text, intensity, pack:question_packs(id, name)')
.in('id', unansweredQuestionIds)
.in('pack_id', enabledPackIds);
const questionMap = new Map((questionDetails || []).map((q: any) => [q.id, q])); const outstanding: OutstandingQuestion[] = [];
const partnerResponseMap = new Map(unansweredPartnerResponses.map((r: any) => [r.question_id, r])); for (const qId of unansweredQuestionIds) {
const q = questionMap.get(qId);
const outstanding: OutstandingQuestion[] = []; const pr = partnerResponseMap.get(qId);
for (const qId of unansweredQuestionIds) { if (q && pr) {
const q = questionMap.get(qId); outstanding.push({
const pr = partnerResponseMap.get(qId); id: qId,
if (q && pr) { text: q.text || '',
outstanding.push({ intensity: q.intensity || null,
id: qId, pack: { id: q.pack?.id || '', name: q.pack?.name || '' },
text: q.text || '', partner_response: { answer: pr.answer, created_at: pr.created_at },
intensity: q.intensity || null, });
pack: { id: q.pack?.id || '', name: q.pack?.name || '' },
partner_response: { answer: pr.answer, created_at: pr.created_at },
});
}
} }
setOutstandingTotal(outstanding.length);
const outFrom = (outstandingPage - 1) * outstandingPageSize;
setOutstandingQuestions(outstanding.slice(outFrom, outFrom + outstandingPageSize));
} else {
setOutstandingQuestions([]);
setOutstandingTotal(0);
} }
// Calculate gap status setOutstandingTotal(outstanding.length);
const { data: appConfig } = await supabase const outFrom = (outstandingPage - 1) * outstandingPageSize;
.from('app_config') setOutstandingQuestions(outstanding.slice(outFrom, outFrom + outstandingPageSize));
.select('answer_gap_threshold')
.limit(1)
.maybeSingle();
const threshold = appConfig?.answer_gap_threshold ?? 10;
setGapThreshold(threshold);
if (threshold > 0) {
// Count questions user answered that partner hasn't
const partnerAllAnswered = new Set((partnerResponses || []).map((r: any) => r.question_id));
const { data: userResponses } = await supabase
.from('responses')
.select('question_id')
.eq('user_id', userId)
.eq('couple_id', profileData.couple_id);
const userAhead = (userResponses || []).filter((r: any) => !partnerAllAnswered.has(r.question_id)).length;
const partnerAhead = (partnerResponses || []).filter((r: any) => !answeredIds.has(r.question_id)).length;
const gap = Math.max(0, userAhead - partnerAhead);
setNetGap(gap);
setGapDisabled(gap >= threshold);
} else {
setNetGap(0);
setGapDisabled(false);
}
} else { } else {
setOutstandingQuestions([]); setOutstandingQuestions([]);
setOutstandingTotal(0); setOutstandingTotal(0);
}
// Calculate gap status
const { data: appConfig } = await supabase
.from('app_config')
.select('answer_gap_threshold')
.limit(1)
.maybeSingle();
const threshold = appConfig?.answer_gap_threshold ?? 10;
setGapThreshold(threshold);
if (threshold > 0) {
// Count questions user answered that partner hasn't
const partnerAllAnswered = new Set((partnerResponses || []).map((r: any) => r.question_id));
const { data: userResponses } = await supabase
.from('responses')
.select('question_id')
.eq('user_id', userId)
.eq('couple_id', profileData.couple_id);
const userAhead = (userResponses || []).filter((r: any) => !partnerAllAnswered.has(r.question_id)).length;
const partnerAhead = unansweredPartnerResponses.length;
const gap = Math.max(0, userAhead - partnerAhead);
setNetGap(gap);
setGapDisabled(gap >= threshold);
} else {
setNetGap(0);
setGapDisabled(false); setGapDisabled(false);
} }
} else { } else {