fix: resolve member status check bugs

Fix '.eq('status', 'current')' in all actions and page-level files.

Co-authored-by: Moheeb Zara <[email protected]>
This commit is contained in:
v0
2026-03-11 07:49:53 +00:00
co-authored by Moheeb Zara
parent c33434e552
commit 8cdb9dbb87
14 changed files with 82 additions and 57 deletions
+1 -1
View File
@@ -7,7 +7,7 @@ export default async function CommsPage() {
if (!user) return null
const { data: member } = await supabase
.from('space_members').select('*, spaces(id, name, slug)').eq('user_id', user.id).eq('status', 'current').single()
.from('space_members').select('*, spaces(id, name, slug)').eq('user_id', user.id).in('status', ['current', 'unverified', 'late']).single()
if (!member) return null
const { data: channels } = await supabase
+1 -1
View File
@@ -7,7 +7,7 @@ export default async function ContactsPage() {
if (!user) return null
const { data: member } = await supabase
.from('space_members').select('space_id').eq('user_id', user.id).eq('status', 'current').single()
.from('space_members').select('space_id').eq('user_id', user.id).in('status', ['current', 'unverified', 'late']).single()
if (!member) return null
const { data: contacts } = await supabase
+1 -1
View File
@@ -7,7 +7,7 @@ export default async function ImportPage() {
if (!user) return null
const { data: member } = await supabase
.from('space_members').select('space_id, role').eq('user_id', user.id).eq('status', 'current').single()
.from('space_members').select('space_id, role').eq('user_id', user.id).in('status', ['current', 'unverified', 'late']).single()
const isAdmin = member?.role === 'admin' || member?.role === 'board' || member?.role === 'treasurer'
+1 -1
View File
@@ -10,7 +10,7 @@ export default async function MembersPage() {
.from('space_members')
.select('space_id, role')
.eq('user_id', user.id)
.eq('status', 'current')
.in('status', ['current', 'unverified', 'late'])
.single()
if (!self) return null
+1 -1
View File
@@ -11,7 +11,7 @@ export default async function OpsEntryPage({ params }: { params: Promise<{ id: s
if (!user) return null
const { data: member } = await supabase
.from('space_members').select('space_id, role, display_name').eq('user_id', user.id).eq('status', 'current').single()
.from('space_members').select('space_id, role, display_name').eq('user_id', user.id).in('status', ['current', 'unverified', 'late']).single()
if (!member) return null
const { data: entry } = await supabase
+1 -1
View File
@@ -7,7 +7,7 @@ export default async function NewOpsEntryPage() {
if (!user) return null
const { data: member } = await supabase
.from('space_members').select('space_id, role, display_name').eq('user_id', user.id).eq('status', 'current').single()
.from('space_members').select('space_id, role, display_name').eq('user_id', user.id).in('status', ['current', 'unverified', 'late']).single()
if (!member) return null
return <KbEntryEditor member={member} />
+1 -1
View File
@@ -7,7 +7,7 @@ export default async function OpsPage() {
if (!user) return null
const { data: member } = await supabase
.from('space_members').select('space_id, role, display_name, user_id').eq('user_id', user.id).eq('status', 'current').single()
.from('space_members').select('space_id, role, display_name, user_id').eq('user_id', user.id).in('status', ['current', 'unverified', 'late']).single()
if (!member) return null
const canSeeSecrets = member.role === 'admin' || member.role === 'board'
+2 -2
View File
@@ -7,7 +7,7 @@ export default async function PaymentsPage() {
if (!user) return null
const { data: member } = await supabase
.from('space_members').select('space_id, role').eq('user_id', user.id).eq('status', 'current').single()
.from('space_members').select('space_id, role').eq('user_id', user.id).in('status', ['current', 'unverified', 'late']).single()
if (!member) return null
const [{ data: payments }, { data: members }, { data: integrations }] = await Promise.all([
@@ -19,7 +19,7 @@ export default async function PaymentsPage() {
supabase.from('space_members')
.select('id, display_name, email')
.eq('space_id', member.space_id)
.eq('status', 'current'),
.in('status', ['current', 'late']),
supabase.from('integrations')
.select('platform, is_connected, config')
.eq('space_id', member.space_id),
+1 -1
View File
@@ -7,7 +7,7 @@ export default async function ProjectsPage() {
if (!user) return null
const { data: member } = await supabase
.from('space_members').select('space_id').eq('user_id', user.id).eq('status', 'current').single()
.from('space_members').select('space_id').eq('user_id', user.id).in('status', ['current', 'unverified', 'late']).single()
if (!member) return null
const { data: projects } = await supabase
+1 -1
View File
@@ -8,7 +8,7 @@ export default async function SettingsPage() {
if (!user) redirect('/login')
const { data: member } = await supabase
.from('space_members').select('space_id, role').eq('user_id', user.id).eq('status', 'current').single()
.from('space_members').select('space_id, role').eq('user_id', user.id).in('status', ['current', 'unverified', 'late']).single()
if (!member) redirect('/login')
+7 -4
View File
@@ -53,10 +53,13 @@ export function TasksClient({ tasks: initialTasks, members, currentUserId, space
const [formError, setFormError] = useState('')
const [formLoading, setFormLoading] = useState(false)
// Ongoing = tasks with a recurrence (not 'none')
// Ongoing = recurring tasks (recurrence != 'none')
// Chores = chore type OR regular tasks without recurrence (the general open pool)
// Mine = tasks claimed/assigned to me
// Done = completed
const isDone = (t: any) => t.status === 'done' || t.status === 'completed'
const isOpen = (t: any) => !isDone(t)
const chores = tasks.filter(t => (t.type === 'chore' || t.task_type === 'chore') && isOpen(t) && (!t.recurrence || t.recurrence === 'none'))
const chores = tasks.filter(t => isOpen(t) && (!t.recurrence || t.recurrence === 'none'))
const ongoing = tasks.filter(t => t.recurrence && t.recurrence !== 'none' && isOpen(t))
const mine = tasks.filter(t => (t.claimed_by === currentUserId || t.assigned_to === currentUserId) && isOpen(t))
const done = tasks.filter(isDone)
@@ -154,7 +157,7 @@ export function TasksClient({ tasks: initialTasks, members, currentUserId, space
activeTab === tab ? 'border-primary text-primary' : 'border-transparent text-muted-foreground hover:text-foreground'
}`}
>
{tab === 'mine' ? 'My Tasks' : tab.charAt(0).toUpperCase() + tab.slice(1)}{' '}
{tab === 'mine' ? 'My Tasks' : tab === 'chores' ? 'Open Tasks' : tab.charAt(0).toUpperCase() + tab.slice(1)}{' '}
{tabCounts[tab] > 0 && (
<span className={`ml-1 text-[10px] font-mono px-1.5 py-0.5 rounded ${
activeTab === tab ? 'bg-primary/10 text-primary' : 'bg-muted text-muted-foreground'
@@ -235,7 +238,7 @@ export function TasksClient({ tasks: initialTasks, members, currentUserId, space
</div>
)) : (
<div className="px-4 py-10 text-center">
<p className="font-sans text-sm text-muted-foreground">No {activeTab === 'done' ? 'completed' : 'open'} tasks in this view</p>
<p className="font-sans text-sm text-muted-foreground">No {activeTab === 'done' ? 'completed' : activeTab === 'chores' ? 'open' : activeTab} tasks in this view</p>
<button
onClick={() => setShowCreate(true)}
className="font-mono text-xs text-primary mt-2 hover:underline"
+1 -1
View File
@@ -55,7 +55,7 @@ export async function POST(req: NextRequest) {
if (!user) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
const { data: member } = await supabase
.from('space_members').select('space_id, role').eq('user_id', user.id).eq('status', 'current').single()
.from('space_members').select('space_id, role').eq('user_id', user.id).in('status', ['current', 'unverified', 'late']).single()
if (!member || !['admin', 'board', 'treasurer'].includes(member.role)) {
return NextResponse.json({ error: 'Insufficient permissions' }, { status: 403 })
}
+40 -41
View File
@@ -25,18 +25,27 @@ export async function getUser() {
return user
}
// ─── Helper: get current member (any non-inactive status) ────────────────────
async function getMember(supabase: any, userId: string) {
const { data } = await supabase
.from('space_members')
.select('space_id, role, display_name')
.eq('user_id', userId)
.in('status', ['current', 'unverified', 'late'])
.single()
return data
}
export async function getCurrentMember() {
const supabase = await createClient()
const { data: { user } } = await supabase.auth.getUser()
if (!user) return null
const { data } = await supabase
.from('space_members')
.select('*, spaces(*)')
.eq('user_id', user.id)
.eq('status', 'current')
.in('status', ['current', 'unverified', 'late'])
.single()
return data
}
@@ -54,8 +63,7 @@ export async function createTask(formData: {
const { data: { user } } = await supabase.auth.getUser()
if (!user) return { error: 'Not authenticated' }
const { data: member } = await supabase
.from('space_members').select('space_id, display_name').eq('user_id', user.id).eq('status', 'current').single()
const member = await getMember(supabase, user.id)
if (!member) return { error: 'No active membership' }
const { data, error } = await supabase.from('tasks').insert({
@@ -94,8 +102,7 @@ export async function claimTask(taskId: string) {
const { data: { user } } = await supabase.auth.getUser()
if (!user) return { error: 'Not authenticated' }
const { data: member } = await supabase
.from('space_members').select('space_id, display_name').eq('user_id', user.id).eq('status', 'current').single()
const member = await getMember(supabase, user.id)
if (!member) return { error: 'No active membership' }
const { error } = await supabase.from('tasks').update({
@@ -125,8 +132,7 @@ export async function completeTask(taskId: string) {
const { data: { user } } = await supabase.auth.getUser()
if (!user) return { error: 'Not authenticated' }
const { data: member } = await supabase
.from('space_members').select('space_id, display_name').eq('user_id', user.id).eq('status', 'current').single()
const member = await getMember(supabase, user.id)
if (!member) return { error: 'No active membership' }
const { error } = await supabase.from('tasks').update({
@@ -156,8 +162,7 @@ export async function deleteTask(taskId: string) {
const { data: { user } } = await supabase.auth.getUser()
if (!user) return { error: 'Not authenticated' }
const { data: member } = await supabase
.from('space_members').select('space_id').eq('user_id', user.id).eq('status', 'current').single()
const member = await getMember(supabase, user.id)
if (!member) return { error: 'No active membership' }
const { error } = await supabase.from('tasks').delete()
@@ -181,8 +186,7 @@ export async function createProject(formData: {
const { data: { user } } = await supabase.auth.getUser()
if (!user) return { error: 'Not authenticated' }
const { data: member } = await supabase
.from('space_members').select('space_id, display_name').eq('user_id', user.id).eq('status', 'current').single()
const member = await getMember(supabase, user.id)
if (!member) return { error: 'No active membership' }
const { data, error } = await supabase.from('projects').insert({
@@ -216,8 +220,7 @@ export async function updateProjectStatus(projectId: string, status: string) {
const { data: { user } } = await supabase.auth.getUser()
if (!user) return { error: 'Not authenticated' }
const { data: member } = await supabase
.from('space_members').select('space_id').eq('user_id', user.id).eq('status', 'current').single()
const member = await getMember(supabase, user.id)
if (!member) return { error: 'No active membership' }
const { error } = await supabase.from('projects').update({ status, updated_at: new Date().toISOString() })
@@ -234,8 +237,7 @@ export async function deleteProject(projectId: string) {
const { data: { user } } = await supabase.auth.getUser()
if (!user) return { error: 'Not authenticated' }
const { data: member } = await supabase
.from('space_members').select('space_id').eq('user_id', user.id).eq('status', 'current').single()
const member = await getMember(supabase, user.id)
if (!member) return { error: 'No active membership' }
const { error } = await supabase.from('projects').delete()
@@ -263,7 +265,7 @@ export async function addMember(formData: {
if (!user) return { error: 'Not authenticated' }
const { data: self } = await supabase
.from('space_members').select('space_id, role').eq('user_id', user.id).eq('status', 'current').single()
.from('space_members').select('space_id, role').eq('user_id', user.id).in('status', ['current', 'unverified', 'late']).single()
if (!self || (self.role !== 'admin' && self.role !== 'board')) return { error: 'Admin access required' }
const { data, error } = await supabase.from('space_members').insert({
@@ -302,7 +304,7 @@ export async function updateMember(memberId: string, updates: {
if (!user) return { error: 'Not authenticated' }
const { data: self } = await supabase
.from('space_members').select('space_id, role').eq('user_id', user.id).eq('status', 'current').single()
.from('space_members').select('space_id, role').eq('user_id', user.id).in('status', ['current', 'unverified', 'late']).single()
if (!self || (self.role !== 'admin' && self.role !== 'board')) return { error: 'Admin access required' }
const { error } = await supabase.from('space_members').update(updates)
@@ -319,7 +321,7 @@ export async function approveMember(memberId: string) {
if (!user) return { error: 'Not authenticated' }
const { data: self } = await supabase
.from('space_members').select('space_id, role, display_name').eq('user_id', user.id).eq('status', 'current').single()
.from('space_members').select('space_id, role, display_name').eq('user_id', user.id).in('status', ['current', 'unverified', 'late']).single()
if (!self || (self.role !== 'admin' && self.role !== 'board')) return { error: 'Admin access required' }
const { error } = await supabase.from('space_members').update({ status: 'current', approved: true })
@@ -346,7 +348,7 @@ export async function removeMember(memberId: string) {
if (!user) return { error: 'Not authenticated' }
const { data: self } = await supabase
.from('space_members').select('space_id, role').eq('user_id', user.id).eq('status', 'current').single()
.from('space_members').select('space_id, role').eq('user_id', user.id).in('status', ['current', 'unverified', 'late']).single()
if (!self || self.role !== 'admin') return { error: 'Admin access required' }
const { error } = await supabase.from('space_members').delete()
@@ -373,8 +375,7 @@ export async function createContact(formData: {
const { data: { user } } = await supabase.auth.getUser()
if (!user) return { error: 'Not authenticated' }
const { data: member } = await supabase
.from('space_members').select('space_id').eq('user_id', user.id).eq('status', 'current').single()
const member = await getMember(supabase, user.id)
if (!member) return { error: 'No active membership' }
const code = formData.name.slice(0, 3).toUpperCase() + Math.floor(Math.random() * 900 + 100)
@@ -411,8 +412,7 @@ export async function updateContact(contactId: string, updates: {
const { data: { user } } = await supabase.auth.getUser()
if (!user) return { error: 'Not authenticated' }
const { data: member } = await supabase
.from('space_members').select('space_id').eq('user_id', user.id).eq('status', 'current').single()
const member = await getMember(supabase, user.id)
if (!member) return { error: 'No active membership' }
const { error } = await supabase.from('contacts').update({ ...updates, updated_at: new Date().toISOString() })
@@ -428,8 +428,7 @@ export async function deleteContact(contactId: string) {
const { data: { user } } = await supabase.auth.getUser()
if (!user) return { error: 'Not authenticated' }
const { data: member } = await supabase
.from('space_members').select('space_id').eq('user_id', user.id).eq('status', 'current').single()
const member = await getMember(supabase, user.id)
if (!member) return { error: 'No active membership' }
const { error } = await supabase.from('contacts').delete()
@@ -453,7 +452,7 @@ export async function logCashPayment(formData: {
if (!user) return { error: 'Not authenticated' }
const { data: member } = await supabase
.from('space_members').select('space_id, role, display_name').eq('user_id', user.id).eq('status', 'current').single()
.from('space_members').select('space_id, role, display_name').eq('user_id', user.id).in('status', ['current', 'unverified', 'late']).single()
if (!member || (member.role !== 'admin' && member.role !== 'board' && member.role !== 'treasurer')) {
return { error: 'Treasurer access required' }
}
@@ -500,7 +499,7 @@ export async function linkPaymentToMember(paymentId: string, memberId: string) {
if (!user) return { error: 'Not authenticated' }
const { data: self } = await supabase
.from('space_members').select('space_id, role').eq('user_id', user.id).eq('status', 'current').single()
.from('space_members').select('space_id, role').eq('user_id', user.id).in('status', ['current', 'unverified', 'late']).single()
if (!self || (self.role !== 'admin' && self.role !== 'board' && self.role !== 'treasurer')) {
return { error: 'Treasurer access required' }
}
@@ -542,7 +541,7 @@ export async function importPaymentsCsv(rows: Array<{
if (!user) return { error: 'Not authenticated' }
const { data: member } = await supabase
.from('space_members').select('space_id, role').eq('user_id', user.id).eq('status', 'current').single()
.from('space_members').select('space_id, role').eq('user_id', user.id).in('status', ['current', 'unverified', 'late']).single()
if (!member || (member.role !== 'admin' && member.role !== 'board' && member.role !== 'treasurer')) {
return { error: 'Treasurer access required' }
}
@@ -582,7 +581,7 @@ export async function createKbEntry(formData: {
if (!user) return { error: 'Not authenticated' }
const { data: member } = await supabase
.from('space_members').select('space_id, display_name, handle').eq('user_id', user.id).eq('status', 'current').single()
.from('space_members').select('space_id, display_name, handle').eq('user_id', user.id).in('status', ['current', 'unverified', 'late']).single()
if (!member) return { error: 'No active membership' }
const { data, error } = await supabase.from('knowledge_base').insert({
@@ -616,7 +615,7 @@ export async function updateKbEntry(entryId: string, updates: {
if (!user) return { error: 'Not authenticated' }
const { data: member } = await supabase
.from('space_members').select('space_id, display_name').eq('user_id', user.id).eq('status', 'current').single()
.from('space_members').select('space_id, display_name').eq('user_id', user.id).in('status', ['current', 'unverified', 'late']).single()
if (!member) return { error: 'No active membership' }
// Only pass columns that actually exist in the DB
@@ -640,7 +639,7 @@ export async function deleteKbEntry(entryId: string) {
if (!user) return { error: 'Not authenticated' }
const { data: member } = await supabase
.from('space_members').select('space_id').eq('user_id', user.id).eq('status', 'current').single()
.from('space_members').select('space_id').eq('user_id', user.id).in('status', ['current', 'unverified', 'late']).single()
if (!member) return { error: 'No active membership' }
const { error } = await supabase.from('knowledge_base').delete()
@@ -665,7 +664,7 @@ export async function createSecret(formData: {
if (!user) return { error: 'Not authenticated' }
const { data: member } = await supabase
.from('space_members').select('space_id, role').eq('user_id', user.id).eq('status', 'current').single()
.from('space_members').select('space_id, role').eq('user_id', user.id).in('status', ['current', 'unverified', 'late']).single()
if (!member || (member.role !== 'admin' && member.role !== 'board')) return { error: 'Admin access required' }
const { data, error } = await supabase.from('secrets').insert({
@@ -688,7 +687,7 @@ export async function deleteSecret(secretId: string) {
if (!user) return { error: 'Not authenticated' }
const { data: member } = await supabase
.from('space_members').select('space_id, role').eq('user_id', user.id).eq('status', 'current').single()
.from('space_members').select('space_id, role').eq('user_id', user.id).in('status', ['current', 'unverified', 'late']).single()
if (!member || member.role !== 'admin') return { error: 'Admin access required' }
const { error } = await supabase.from('secrets').delete()
@@ -713,7 +712,7 @@ export async function upsertAreaLead(formData: {
if (!user) return { error: 'Not authenticated' }
const { data: member } = await supabase
.from('space_members').select('space_id, role').eq('user_id', user.id).eq('status', 'current').single()
.from('space_members').select('space_id, role').eq('user_id', user.id).in('status', ['current', 'unverified', 'late']).single()
if (!member || (member.role !== 'admin' && member.role !== 'board')) return { error: 'Admin access required' }
const { data, error } = await supabase.from('area_leads').upsert({
@@ -745,7 +744,7 @@ export async function updateSpaceSettings(updates: {
if (!user) return { error: 'Not authenticated' }
const { data: member } = await supabase
.from('space_members').select('space_id, role').eq('user_id', user.id).eq('status', 'current').single()
.from('space_members').select('space_id, role').eq('user_id', user.id).in('status', ['current', 'unverified', 'late']).single()
if (!member || member.role !== 'admin') return { error: 'Admin access required' }
const { error } = await supabase.from('spaces').update({
@@ -764,7 +763,7 @@ export async function rotateWebhookSecret() {
if (!user) return { error: 'Not authenticated' }
const { data: member } = await supabase
.from('space_members').select('space_id, role').eq('user_id', user.id).eq('status', 'current').single()
.from('space_members').select('space_id, role').eq('user_id', user.id).in('status', ['current', 'unverified', 'late']).single()
if (!member || member.role !== 'admin') return { error: 'Admin access required' }
// Generate a new webhook secret
@@ -785,7 +784,7 @@ export async function saveIntegration(platform: string, config: Record<string, s
if (!user) return { error: 'Not authenticated' }
const { data: member } = await supabase
.from('space_members').select('space_id, role').eq('user_id', user.id).eq('status', 'current').single()
.from('space_members').select('space_id, role').eq('user_id', user.id).in('status', ['current', 'unverified', 'late']).single()
if (!member || member.role !== 'admin') return { error: 'Admin access required' }
// Mask sensitive values — store but indicate they're set
@@ -815,7 +814,7 @@ export async function disconnectIntegration(platform: string) {
if (!user) return { error: 'Not authenticated' }
const { data: member } = await supabase
.from('space_members').select('space_id, role').eq('user_id', user.id).eq('status', 'current').single()
.from('space_members').select('space_id, role').eq('user_id', user.id).in('status', ['current', 'unverified', 'late']).single()
if (!member || member.role !== 'admin') return { error: 'Admin access required' }
const { error } = await supabase.from('integrations').update({
@@ -845,7 +844,7 @@ export async function importMembers(rows: Array<{
if (!user) return { error: 'Not authenticated' }
const { data: member } = await supabase
.from('space_members').select('space_id, role').eq('user_id', user.id).eq('status', 'current').single()
.from('space_members').select('space_id, role').eq('user_id', user.id).in('status', ['current', 'unverified', 'late']).single()
if (!member || (member.role !== 'admin' && member.role !== 'board')) return { error: 'Admin access required' }
const inserts = rows.map(r => ({
+23
View File
@@ -0,0 +1,23 @@
const fs = require('fs')
const path = require('path')
const filePath = '/vercel/share/v0-project/lib/actions.ts'
console.log('Patching:', filePath)
let content = fs.readFileSync(filePath, 'utf8')
// Replace ALL .eq('user_id', user.id).eq('status', 'current').single() patterns
content = content.replaceAll(
`.eq('user_id', user.id).eq('status', 'current').single()`,
`.eq('user_id', user.id).in('status', ['current', 'unverified', 'late']).single()`
)
// Also the multi-line variant
content = content.replaceAll(
`.eq('user_id', user.id)\n .eq('status', 'current')\n .single()`,
`.eq('user_id', user.id)\n .in('status', ['current', 'unverified', 'late'])\n .single()`
)
fs.writeFileSync(filePath, content, 'utf8')
console.log('Done. File length:', content.length)
const remaining = (content.match(/eq\('status', 'current'\)/g) || []).length
console.log('Remaining eq(status,current) occurrences:', remaining)