Files
maaz519 0ffdc362b5 feat: member portal Sprint 4 — Events + RSVP
- Event + EventRsvp models + migration (RsvpStatus enum: GOING/MAYBE/NOT_GOING)
- EventsModule: admin CRUD (create/update/delete/publish) + RSVP list
- GET /my/events + POST /my/events/:id/rsvp in MyController/MyService
- Admin BFF routes: GET/POST /api/admin/events, PATCH/DELETE /api/admin/events/[id]
- Member BFF routes: GET /api/my/events, POST /api/my/events/[id]/rsvp
- /my/events page: upcoming/past split, RSVP button (client component, optimistic)
- Register DigestModule, OrgModule, ThreadsModule, EventsModule in AppModule
- Add EVENT_CREATED/DELETED/UPDATED and DIGEST_SENT to AuditAction

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-17 16:20:11 +05:30

63 lines
1.6 KiB
TypeScript

'use client';
import { useState } from 'react';
type RsvpStatus = 'GOING' | 'NOT_GOING' | 'MAYBE';
const LABELS: Record<RsvpStatus, string> = {
GOING: 'Going',
MAYBE: 'Maybe',
NOT_GOING: 'Not going',
};
const STYLES: Record<RsvpStatus, string> = {
GOING: 'bg-green-600 text-white hover:bg-green-700',
MAYBE: 'bg-yellow-500 text-white hover:bg-yellow-600',
NOT_GOING: 'bg-gray-200 text-gray-700 hover:bg-gray-300',
};
interface Props {
eventId: string;
initial: RsvpStatus | null;
}
export function RsvpButton({ eventId, initial }: Props) {
const [current, setCurrent] = useState<RsvpStatus | null>(initial);
const [loading, setLoading] = useState(false);
const rsvp = async (status: RsvpStatus) => {
if (loading) return;
setLoading(true);
try {
const res = await fetch(`/api/my/events/${eventId}/rsvp`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ status }),
});
if (res.ok) setCurrent(status);
} finally {
setLoading(false);
}
};
return (
<div className="flex gap-2 mt-3">
{(['GOING', 'MAYBE', 'NOT_GOING'] as RsvpStatus[]).map((s) => (
<button
key={s}
type="button"
disabled={loading}
onClick={() => rsvp(s)}
className={`text-xs rounded-full px-3 py-1.5 font-medium transition-colors disabled:opacity-50 border ${
current === s
? STYLES[s]
: 'border-gray-200 text-gray-600 hover:bg-gray-50'
}`}
>
{LABELS[s]}
</button>
))}
</div>
);
}