Files
hackerspace.sh/app/(app)/me/notification-prefs.tsx
T
Moheeb Zara ab8c22ae1d Add notification preferences and alternate dues payment options
Notification preferences (migration 048):
- Per-member opt-out of muteable categories (bookings, classes, forms,
  admin alerts); billing is always-on. Opt-out model, default on.
- Dispatcher reads prefs and marks muted outbox rows 'skipped' instead
  of sending; pure isMuted logic, fail-open on a prefs lookup error.
- notification_preferences table (default-deny RLS); /me toggles and
  self-view via validated service-client actions.

Alternate dues payment methods (migration 049):
- Admin sets external pay-here URLs per platform (PayPal/Zeffy/Venmo)
  in /settings; members see active ones on /me and pay off-platform;
  the treasurer reconciles manually (platform pre-tagged). https-only.
- dues_payment_methods table (SELECT space member, write admin/board).

/me visual pass:
- Restore the chip/preset profile editor (skills/interests/willing-to/
  affiliations); promote ChipInput and presets to shared modules.
- Gate the Stripe pay-dues button on whether the space has Stripe
  configured; show external links or a contact-admin note otherwise.
2026-05-19 20:26:32 -07:00

53 lines
1.8 KiB
TypeScript

'use client'
import { useState, useTransition } from 'react'
import { toast } from 'sonner'
import { Switch } from '@/components/ui/switch'
import {
CATEGORY_META,
type PrefMap,
type NotificationCategory,
} from '@/lib/notifications-prefs-logic'
import { setMyNotificationPreference } from '@/lib/actions'
// Per-category email opt-out toggles. Optimistic: the switch flips immediately
// and reverts if the server action fails. Billing is never shown (always on).
export function NotificationPrefs({ initial }: { initial: PrefMap }) {
const [prefs, setPrefs] = useState<PrefMap>(initial)
const [pending, startTransition] = useTransition()
function toggle(category: NotificationCategory, next: boolean) {
const prev = prefs[category] ?? true
setPrefs(p => ({ ...p, [category]: next }))
startTransition(async () => {
const res = await setMyNotificationPreference({ category, enabled: next })
if ('error' in res) {
setPrefs(p => ({ ...p, [category]: prev }))
toast.error(res.error)
}
})
}
return (
<ul className="divide-y rounded-lg border border-border">
{CATEGORY_META.map(meta => {
const enabled = prefs[meta.category] ?? true
return (
<li key={meta.category} className="p-4 flex items-center justify-between gap-4">
<div className="min-w-0">
<span className="font-sans text-sm text-foreground">{meta.label}</span>
<p className="font-sans text-xs text-muted-foreground mt-0.5">{meta.description}</p>
</div>
<Switch
checked={enabled}
disabled={pending}
onCheckedChange={next => toggle(meta.category, next)}
aria-label={`Email me about ${meta.label}`}
/>
</li>
)
})}
</ul>
)
}