feat(door): P1 member access cards + door permissions
Migration 034 adds member_cards (RFID/NFC UID -> member). The card UID is treated as a credential: door.manage-only RLS with no member SELECT policy; the member's own "My access cards" view goes through a service -client action that returns only count + last4 (never the raw UID). New door.manage / door.operate permissions (group Access), seeded to board and backfilled, introduced now for the whole Door epic. Pure door-logic masking is unit-tested. A per-member "Cards" panel on /members is gated to door.manage (independent of admin). No controller calls and no anonymous path in this phase. Mirrored in schema.sql; types and schema docs updated.
This commit is contained in:
@@ -95,12 +95,18 @@ export default async function MembersPage() {
|
||||
sid: self.space_id,
|
||||
perm: 'certifications.grant',
|
||||
})
|
||||
const { data: canManageCards } = await supabase.rpc('user_has_permission', {
|
||||
uid: user.id,
|
||||
sid: self.space_id,
|
||||
perm: 'door.manage',
|
||||
})
|
||||
|
||||
return (
|
||||
<MembersClient
|
||||
members={members ?? []}
|
||||
currentRole={self.role}
|
||||
canGrantCerts={!!canGrantCerts}
|
||||
canManageCards={!!canManageCards}
|
||||
areaLeadRoles={(areaLeadRoles ?? []) as Array<{ id: string; area_name: string; lead_id: string | null }>}
|
||||
inviteSlot={
|
||||
canInvite ? (
|
||||
|
||||
Reference in New Issue
Block a user