feat(door): member /doors page (self-entry + my cards + my activity)

New listMyDoorActivity (service client after requireMember; rows where
the caller is actor or target; detail already redacted at write). /doors
composes the existing self-entry control, the masked own cards
(getMyCards, last4 only), and the member's own recent door activity;
empty-state when none. Sidebar 'Doors' link added under Learn.
This commit is contained in:
Moheeb Zara
2026-05-16 21:45:17 -07:00
parent c93ee9bdb7
commit 2fdc27597e
3 changed files with 134 additions and 0 deletions
+22
View File
@@ -801,3 +801,25 @@ export async function selfEntry(input: unknown) {
revalidatePath('/dashboard')
return result.ok ? { data: { ok: true } } : { error: result.reason }
}
// The signed-in member's OWN door activity (rows where they were the actor or
// the target). door_access_log has no member-facing RLS SELECT, so this uses
// the service client after requireMember and filters to this member only. The
// detail column is already secret-redacted at write time.
export async function listMyDoorActivity() {
const supabase = await createClient()
const auth = await requireMember(supabase)
if (!auth.ok) return { error: auth.error }
const { member } = auth
const admin = createAdminClient()
const { data, error } = await admin
.from('door_access_log')
.select('id, action, success, detail, occurred_at')
.eq('space_id', member.space_id)
.or(`actor_member_id.eq.${member.id},target_member_id.eq.${member.id}`)
.order('occurred_at', { ascending: false })
.limit(50)
if (error) return { error: error.message }
return { data: data ?? [] }
}