Add house leaderboard feature: Integrate house leaderboard functionality in LeaderboardPage and LeaderboardSection components. Update userStatsService to fetch house leaderboard data, and enhance UI to display house rankings, scores, and member details. Update Prisma schema to include house-related models and relationships, and seed database with initial house data.
Some checks failed
Deploy with Docker Compose / deploy (push) Has been cancelled
Some checks failed
Deploy with Docker Compose / deploy (push) Has been cancelled
This commit is contained in:
119
components/houses/RequestList.tsx
Normal file
119
components/houses/RequestList.tsx
Normal file
@@ -0,0 +1,119 @@
|
||||
"use client";
|
||||
|
||||
import { useState, useTransition } from "react";
|
||||
import Card from "@/components/ui/Card";
|
||||
import Button from "@/components/ui/Button";
|
||||
import {
|
||||
acceptRequest,
|
||||
rejectRequest,
|
||||
} from "@/actions/houses/requests";
|
||||
import Alert from "@/components/ui/Alert";
|
||||
|
||||
interface Request {
|
||||
id: string;
|
||||
requester: {
|
||||
id: string;
|
||||
username: string;
|
||||
avatar: string | null;
|
||||
};
|
||||
status: string;
|
||||
createdAt: string;
|
||||
}
|
||||
|
||||
interface RequestListProps {
|
||||
requests: Request[];
|
||||
onUpdate?: () => void;
|
||||
}
|
||||
|
||||
export default function RequestList({
|
||||
requests,
|
||||
onUpdate,
|
||||
}: RequestListProps) {
|
||||
const [isPending, startTransition] = useTransition();
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const handleAccept = (requestId: string) => {
|
||||
setError(null);
|
||||
startTransition(async () => {
|
||||
const result = await acceptRequest(requestId);
|
||||
if (result.success) {
|
||||
onUpdate?.();
|
||||
} else {
|
||||
setError(result.error || "Erreur lors de l'acceptation");
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const handleReject = (requestId: string) => {
|
||||
setError(null);
|
||||
startTransition(async () => {
|
||||
const result = await rejectRequest(requestId);
|
||||
if (result.success) {
|
||||
onUpdate?.();
|
||||
} else {
|
||||
setError(result.error || "Erreur lors du refus");
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
if (requests.length === 0) {
|
||||
return (
|
||||
<p className="text-sm" style={{ color: "var(--muted-foreground)" }}>
|
||||
Aucune demande en attente
|
||||
</p>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
{error && <Alert variant="error">{error}</Alert>}
|
||||
{requests.map((request) => (
|
||||
<Card key={request.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)" }}>
|
||||
{request.requester.username}
|
||||
</h4>
|
||||
<p className="text-sm break-words" style={{ color: "var(--muted-foreground)" }}>
|
||||
souhaite rejoindre votre maison
|
||||
</p>
|
||||
</div>
|
||||
{request.status === "PENDING" && (
|
||||
<div className="flex gap-2 sm:flex-nowrap">
|
||||
<Button
|
||||
onClick={() => handleAccept(request.id)}
|
||||
disabled={isPending}
|
||||
variant="success"
|
||||
size="sm"
|
||||
className="flex-1 sm:flex-none"
|
||||
>
|
||||
Accepter
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => handleReject(request.id)}
|
||||
disabled={isPending}
|
||||
variant="danger"
|
||||
size="sm"
|
||||
className="flex-1 sm:flex-none"
|
||||
>
|
||||
Refuser
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
{request.status === "ACCEPTED" && (
|
||||
<span className="text-xs flex-shrink-0" style={{ color: "var(--success)" }}>
|
||||
✓ Acceptée
|
||||
</span>
|
||||
)}
|
||||
{request.status === "REJECTED" && (
|
||||
<span className="text-xs flex-shrink-0" style={{ color: "var(--destructive)" }}>
|
||||
✗ Refusée
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user