feat(notifications): P2e member self-view on /me + docs

getMyNotifications (service-client self-view, mirrors getMyBilling) +
a read-only Notifications section on /me. Docs updated in the same
change: ARCHITECTURE (Phase 2 subsystem + limitation #5), API_REFERENCE
(notifications section, fixed the stale no-auth-middleware claim),
DATABASE_SCHEMA + DB_SCHEMA_MAP (migration 041 + table), DEPLOYMENT
(env vars + dispatcher crontab), HANDOFF pass-45.
This commit is contained in:
Moheeb Zara
2026-05-17 04:58:46 -07:00
parent 75097a601c
commit 7eb83676e7
9 changed files with 138 additions and 4 deletions
+3
View File
@@ -68,3 +68,6 @@ export * from './presence'
// Stripe recurring dues (product spine Phase 1)
export * from './stripe'
// Transactional notifications outbox (product spine Phase 2)
export * from './notifications'
+34
View File
@@ -0,0 +1,34 @@
'use server'
import { createClient } from '@/lib/supabase/server'
import { createAdminClient } from '@/lib/supabase/admin'
import { requireMember } from '@/lib/auth-helpers'
// The caller's own notification history. The notifications RLS SELECT is
// admin/board/treasurer only (mirrors member_billing), so the member
// self-view goes through the validated service client, same as getMyBilling.
export async function getMyNotifications() {
const supabase = await createClient()
const auth = await requireMember(supabase)
if (!auth.ok) return { error: auth.error }
const { member } = auth
const { data } = await createAdminClient()
.from('notifications')
.select('id, type, subject, status, created_at, sent_at')
.eq('space_id', member.space_id)
.eq('member_id', member.id)
.order('created_at', { ascending: false })
.limit(15)
return {
data: (data ?? []).map(r => ({
id: r.id as string,
type: r.type as string,
subject: r.subject as string,
status: r.status as string,
createdAt: r.created_at as string,
sentAt: (r.sent_at as string | null) ?? null,
})),
}
}