130 lines
3.9 KiB
TypeScript
130 lines
3.9 KiB
TypeScript
"use client";
|
|
|
|
import { useState, useTransition } from "react";
|
|
import Card from "@/components/ui/Card";
|
|
import Button from "@/components/ui/Button";
|
|
import {
|
|
acceptInvitation,
|
|
rejectInvitation,
|
|
} from "@/actions/houses/invitations";
|
|
import Alert from "@/components/ui/Alert";
|
|
|
|
interface Invitation {
|
|
id: string;
|
|
house: {
|
|
id: string;
|
|
name: string;
|
|
};
|
|
inviter: {
|
|
id: string;
|
|
username: string;
|
|
avatar: string | null;
|
|
};
|
|
status: string;
|
|
createdAt: string;
|
|
}
|
|
|
|
interface InvitationListProps {
|
|
invitations: Invitation[];
|
|
onUpdate?: () => void;
|
|
}
|
|
|
|
export default function InvitationList({
|
|
invitations,
|
|
onUpdate,
|
|
}: InvitationListProps) {
|
|
const [isPending, startTransition] = useTransition();
|
|
const [error, setError] = useState<string | null>(null);
|
|
|
|
const handleAccept = (invitationId: string) => {
|
|
setError(null);
|
|
startTransition(async () => {
|
|
const result = await acceptInvitation(invitationId);
|
|
if (result.success) {
|
|
// Rafraîchir le score dans le header (l'utilisateur reçoit des points)
|
|
window.dispatchEvent(new Event("refreshUserScore"));
|
|
// Rafraîchir le badge d'invitations dans le header
|
|
window.dispatchEvent(new Event("refreshInvitations"));
|
|
onUpdate?.();
|
|
} else {
|
|
setError(result.error || "Erreur lors de l'acceptation");
|
|
}
|
|
});
|
|
};
|
|
|
|
const handleReject = (invitationId: string) => {
|
|
setError(null);
|
|
startTransition(async () => {
|
|
const result = await rejectInvitation(invitationId);
|
|
if (result.success) {
|
|
// Rafraîchir le badge d'invitations dans le header
|
|
window.dispatchEvent(new Event("refreshInvitations"));
|
|
onUpdate?.();
|
|
} else {
|
|
setError(result.error || "Erreur lors du refus");
|
|
}
|
|
});
|
|
};
|
|
|
|
if (invitations.length === 0) {
|
|
return (
|
|
<p className="text-sm" style={{ color: "var(--muted-foreground)" }}>
|
|
Aucune invitation en attente
|
|
</p>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="space-y-4">
|
|
{error && <Alert variant="error">{error}</Alert>}
|
|
{invitations.map((invitation) => (
|
|
<Card key={invitation.id} className="p-4">
|
|
<div className="flex flex-col sm:flex-row sm:justify-between sm:items-start gap-3">
|
|
<div className="flex-1 min-w-0">
|
|
<h4 className="font-bold mb-1 break-words" style={{ color: "var(--foreground)" }}>
|
|
Invitation de {invitation.inviter.username}
|
|
</h4>
|
|
<p className="text-sm mb-2 break-words" style={{ color: "var(--muted-foreground)" }}>
|
|
Pour rejoindre la maison <strong>{invitation.house.name}</strong>
|
|
</p>
|
|
</div>
|
|
{invitation.status === "PENDING" && (
|
|
<div className="flex gap-2 sm:flex-nowrap">
|
|
<Button
|
|
onClick={() => handleAccept(invitation.id)}
|
|
disabled={isPending}
|
|
variant="success"
|
|
size="sm"
|
|
className="flex-1 sm:flex-none"
|
|
>
|
|
Accepter
|
|
</Button>
|
|
<Button
|
|
onClick={() => handleReject(invitation.id)}
|
|
disabled={isPending}
|
|
variant="danger"
|
|
size="sm"
|
|
className="flex-1 sm:flex-none"
|
|
>
|
|
Refuser
|
|
</Button>
|
|
</div>
|
|
)}
|
|
{invitation.status === "ACCEPTED" && (
|
|
<span className="text-xs flex-shrink-0" style={{ color: "var(--success)" }}>
|
|
✓ Acceptée
|
|
</span>
|
|
)}
|
|
{invitation.status === "REJECTED" && (
|
|
<span className="text-xs flex-shrink-0" style={{ color: "var(--destructive)" }}>
|
|
✗ Refusée
|
|
</span>
|
|
)}
|
|
</div>
|
|
</Card>
|
|
))}
|
|
</div>
|
|
);
|
|
}
|
|
|