168 lines
5.3 KiB
TypeScript
168 lines
5.3 KiB
TypeScript
"use client";
|
|
|
|
import { useState } from "react";
|
|
import { useSession } from "next-auth/react";
|
|
import Card from "@/components/ui/Card";
|
|
import Button from "@/components/ui/Button";
|
|
import Avatar from "@/components/ui/Avatar";
|
|
import { requestToJoin } from "@/actions/houses/requests";
|
|
import { useTransition } from "react";
|
|
import Alert from "@/components/ui/Alert";
|
|
|
|
interface House {
|
|
id: string;
|
|
name: string;
|
|
description: string | null;
|
|
creator: {
|
|
id: string;
|
|
username: string;
|
|
avatar: string | null;
|
|
};
|
|
memberships?: Array<{
|
|
id: string;
|
|
role: string;
|
|
user: {
|
|
id: string;
|
|
username: string;
|
|
avatar: string | null;
|
|
score?: number;
|
|
level?: number;
|
|
};
|
|
}>;
|
|
_count?: {
|
|
memberships: number;
|
|
};
|
|
}
|
|
|
|
interface HouseCardProps {
|
|
house: House;
|
|
onRequestSent?: () => void;
|
|
}
|
|
|
|
export default function HouseCard({ house, onRequestSent }: HouseCardProps) {
|
|
const { data: session } = useSession();
|
|
const [isPending, startTransition] = useTransition();
|
|
const [error, setError] = useState<string | null>(null);
|
|
const [success, setSuccess] = useState<string | null>(null);
|
|
|
|
const isMember = house.memberships?.some(
|
|
(m) => m.user.id === session?.user?.id
|
|
);
|
|
const memberCount = house._count?.memberships || house.memberships?.length || 0;
|
|
|
|
const handleRequestToJoin = () => {
|
|
if (!session?.user?.id) return;
|
|
|
|
setError(null);
|
|
setSuccess(null);
|
|
|
|
startTransition(async () => {
|
|
const result = await requestToJoin(house.id);
|
|
|
|
if (result.success) {
|
|
setSuccess("Demande envoyée avec succès");
|
|
onRequestSent?.();
|
|
} else {
|
|
setError(result.error || "Erreur lors de l'envoi de la demande");
|
|
}
|
|
});
|
|
};
|
|
|
|
return (
|
|
<Card className="p-4 sm:p-6">
|
|
<div className="flex flex-col sm:flex-row sm:justify-between sm:items-start gap-4 mb-4">
|
|
<div className="flex-1 min-w-0">
|
|
<h3 className="text-xl font-bold mb-2 break-words" style={{ color: "var(--foreground)" }}>
|
|
{house.name}
|
|
</h3>
|
|
{house.description && (
|
|
<p className="text-sm mb-2 break-words" style={{ color: "var(--muted-foreground)" }}>
|
|
{house.description}
|
|
</p>
|
|
)}
|
|
<div className="flex flex-wrap items-center gap-2 sm:gap-4 text-xs" style={{ color: "var(--muted-foreground)" }}>
|
|
<span>Créée par {house.creator.username}</span>
|
|
<span className="hidden sm:inline">•</span>
|
|
<span>{memberCount} membre{memberCount > 1 ? "s" : ""}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{error && (
|
|
<Alert variant="error" className="mb-4">
|
|
{error}
|
|
</Alert>
|
|
)}
|
|
|
|
{success && (
|
|
<Alert variant="success" className="mb-4">
|
|
{success}
|
|
</Alert>
|
|
)}
|
|
|
|
{session?.user?.id && !isMember && (
|
|
<Button
|
|
onClick={handleRequestToJoin}
|
|
disabled={isPending}
|
|
variant="primary"
|
|
size="sm"
|
|
className="w-full sm:w-auto"
|
|
>
|
|
{isPending ? "Envoi..." : "Demander à rejoindre"}
|
|
</Button>
|
|
)}
|
|
|
|
{isMember && (
|
|
<div className="text-xs mb-4" style={{ color: "var(--success)" }}>
|
|
✓ Vous êtes membre
|
|
</div>
|
|
)}
|
|
|
|
{/* Members List */}
|
|
{house.memberships && house.memberships.length > 0 && (
|
|
<div className="mt-4 pt-4 border-t" style={{ borderColor: "var(--border)" }}>
|
|
<h4 className="text-xs font-bold uppercase tracking-wider mb-3" style={{ color: "var(--muted-foreground)" }}>
|
|
Membres ({house.memberships.length})
|
|
</h4>
|
|
<div className="flex flex-wrap gap-2">
|
|
{house.memberships.map((membership) => (
|
|
<div
|
|
key={membership.id}
|
|
className="flex items-center gap-2 p-2 rounded"
|
|
style={{ backgroundColor: "var(--card-hover)" }}
|
|
title={`${membership.user.username} (${membership.role})${membership.user.score !== undefined ? ` - ${membership.user.score} pts` : ""}`}
|
|
>
|
|
<Avatar
|
|
src={membership.user.avatar}
|
|
username={membership.user.username}
|
|
size="sm"
|
|
className="flex-shrink-0"
|
|
borderClassName="border-pixel-gold/30"
|
|
/>
|
|
<div className="min-w-0">
|
|
<div className="flex items-center gap-1">
|
|
<span className="text-xs font-semibold truncate" style={{ color: "var(--foreground)" }}>
|
|
{membership.user.username}
|
|
</span>
|
|
{membership.role === "OWNER" && (
|
|
<span className="text-[10px] uppercase" style={{ color: "var(--accent)" }}>
|
|
👑
|
|
</span>
|
|
)}
|
|
</div>
|
|
{membership.user.score !== undefined && membership.user.level !== undefined && (
|
|
<div className="text-[10px]" style={{ color: "var(--muted-foreground)" }}>
|
|
{membership.user.score} pts • Lv.{membership.user.level}
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
)}
|
|
</Card>
|
|
);
|
|
}
|
|
|