accept-button.tsx43 lines · main
1'use client';
2
3import { useState } from 'react';
4
5interface Props {
6 invitationId: string;
7 action: (invitationId: string) => Promise<void>;
8}
9
10export function AcceptButton({ invitationId, action }: Props) {
11 const [pending, setPending] = useState(false);
12 const [error, setError] = useState<string | null>(null);
13
14 async function onClick() {
15 setPending(true);
16 setError(null);
17 try {
18 await action(invitationId);
19 // Server action redirects on success — control should not return
20 // here, but if it does (e.g. revalidatePath without redirect) the
21 // row will disappear on the next render.
22 } catch (err) {
23 setError(err instanceof Error ? err.message : 'accept failed');
24 setPending(false);
25 }
26 }
27
28 return (
29 <div className="flex flex-col items-end gap-1">
30 <button
31 type="button"
32 onClick={onClick}
33 disabled={pending}
34 className="rounded-md bg-[var(--color-primary)] px-4 py-2 font-mono text-sm font-medium text-[var(--color-text-inverse)] transition hover:bg-[var(--color-primary-hover)] disabled:cursor-not-allowed disabled:opacity-60"
35 >
36 {pending ? 'accepting…' : 'accept'}
37 </button>
38 {error ? (
39 <span className="font-mono text-xs text-[var(--color-text-error)]">{error}</span>
40 ) : null}
41 </div>
42 );
43}