feat(me): my-payments self-view in the Membership tab (P3d)
New getMyPayments: payments SELECT is treasurer-scoped (RLS), so the self-view uses the validated service client strictly scoped to the caller's space_id + member_id (same convention as getMyBilling / getMyNotifications). Read-only list (amount, date, platform, status) in the Membership tab. Docs updated. 521 tests; build clean.
This commit is contained in:
@@ -52,6 +52,7 @@ export type Reservation = {
|
||||
export type MyCard = { id: string; card_type: string; label: string | null; is_active: boolean; last4: string }
|
||||
export type MyVisit = { id: string; checked_in_at: string; checked_out_at: string | null; is_host: boolean; check_in_note: string | null; check_out_note: string | null }
|
||||
export type MyNotif = { id: string; type: string; subject: string; status: string; createdAt: string; sentAt: string | null }
|
||||
export type MyPayment = { id: string; amount: number; currency: string; platform: string; description: string | null; status: string; date: string }
|
||||
export type HeldPermission = { code: string; label: string }
|
||||
export type Billing = { status: string | null; currentPeriodEnd: string | null; hasCustomer: boolean } | null
|
||||
|
||||
@@ -60,6 +61,7 @@ type Props = {
|
||||
grants: Grant[]
|
||||
held: HeldPermission[]
|
||||
billing: Billing
|
||||
payments: MyPayment[]
|
||||
notifs: MyNotif[]
|
||||
classSignups: ClassSignup[]
|
||||
reservations: Reservation[]
|
||||
@@ -74,6 +76,7 @@ export function MePortalClient({
|
||||
grants,
|
||||
held,
|
||||
billing,
|
||||
payments,
|
||||
notifs,
|
||||
classSignups,
|
||||
reservations,
|
||||
@@ -135,6 +138,32 @@ export function MePortalClient({
|
||||
<DuesCard billing={billing} />
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2 className={sectionH}>My payments</h2>
|
||||
{payments.length === 0 ? (
|
||||
<p className="font-sans text-sm text-muted-foreground">
|
||||
No payments recorded for you yet.
|
||||
</p>
|
||||
) : (
|
||||
<ul className="divide-y rounded-lg border border-border">
|
||||
{payments.map(p => (
|
||||
<li key={p.id} className="p-4 flex items-start justify-between gap-4 flex-wrap">
|
||||
<div className="min-w-0">
|
||||
<span className="font-sans text-sm font-semibold text-foreground">
|
||||
{p.currency} {p.amount.toFixed(2)}
|
||||
</span>
|
||||
<p className="font-mono text-[10px] text-muted-foreground mt-1">
|
||||
{new Date(p.date).toLocaleDateString()} · {p.platform}
|
||||
{p.description ? ` · ${p.description}` : ''}
|
||||
</p>
|
||||
</div>
|
||||
<Badge variant={p.status === 'linked' ? 'default' : 'outline'}>{p.status}</Badge>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2 className={sectionH}>Certifications</h2>
|
||||
{grants.length === 0 ? (
|
||||
|
||||
@@ -2,7 +2,7 @@ import { redirect } from 'next/navigation'
|
||||
import { createClient } from '@/lib/supabase/server'
|
||||
import { PageHeader, PageTitle } from '@/components/ui/page-title'
|
||||
import { PERMISSIONS, PERMISSION_CODES } from '@/lib/permissions-catalog'
|
||||
import { getMyClassSignups, getMyReservations, getMyCards, getMyVisits, getMyBilling, getMyNotifications } from '@/lib/actions'
|
||||
import { getMyClassSignups, getMyReservations, getMyCards, getMyVisits, getMyBilling, getMyNotifications, getMyPayments } from '@/lib/actions'
|
||||
import {
|
||||
MePortalClient,
|
||||
type Grant,
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
type MyCard,
|
||||
type MyVisit,
|
||||
type MyNotif,
|
||||
type MyPayment,
|
||||
} from './me-portal-client'
|
||||
|
||||
export const dynamic = 'force-dynamic'
|
||||
@@ -97,6 +98,9 @@ export default async function MePage() {
|
||||
const notifsRes = await getMyNotifications()
|
||||
const notifs: MyNotif[] = 'data' in notifsRes ? (notifsRes.data as MyNotif[]) : []
|
||||
|
||||
const paymentsRes = await getMyPayments()
|
||||
const payments: MyPayment[] = 'data' in paymentsRes ? (paymentsRes.data as MyPayment[]) : []
|
||||
|
||||
return (
|
||||
<>
|
||||
<PageHeader>
|
||||
@@ -108,6 +112,7 @@ export default async function MePage() {
|
||||
grants={grants}
|
||||
held={held}
|
||||
billing={billing}
|
||||
payments={payments}
|
||||
notifs={notifs}
|
||||
classSignups={classSignups}
|
||||
reservations={reservations}
|
||||
|
||||
Reference in New Issue
Block a user