"use client";
import { useCallback, useEffect, useState } from "react";
import { Avatar } from "@/components/avatar";
import { createClient, waitForRealtimeAuth } from "@/lib/supabase/client";
import type { CitizenRow, MyVoteRow } from "./page";
function CitizenVoteRow({ citizen, onVote }: { citizen: CitizenRow; onVote: () => void }) {
const [confirming, setConfirming] = useState(false);
return (
{citizen.pseudo}
{confirming ? (
) : (
)}
);
}
export function UrneView({
isJudge,
currentUserId,
citizens,
initialVoteCounts,
initialMyVoteToday,
}: {
isJudge: boolean;
currentUserId: string;
citizens: CitizenRow[];
initialVoteCounts: Record;
initialMyVoteToday: MyVoteRow | null;
}) {
const [voteCounts, setVoteCounts] = useState(initialVoteCounts);
const [myVoteToday, setMyVoteToday] = useState(initialMyVoteToday);
const [error, setError] = useState(null);
const refetch = useCallback(async () => {
const supabase = createClient();
const [{ data: counts }, { data: myVotes }] = await Promise.all([
supabase.rpc("urn_vote_counts"),
isJudge
? Promise.resolve({ data: null })
: supabase
.from("urn_votes")
.select("target_id, created_at")
.eq("voter_id", currentUserId)
.order("created_at", { ascending: false }),
]);
if (counts) {
const next: Record = {};
for (const row of counts) next[row.target_id] = row.votes;
setVoteCounts(next);
}
if (myVotes) {
const today = new Date().toLocaleDateString("sv-SE", { timeZone: "Europe/Paris" });
setMyVoteToday(
myVotes.find(
(v) => new Date(v.created_at).toLocaleDateString("sv-SE", { timeZone: "Europe/Paris" }) === today,
) ?? null,
);
}
}, [isJudge, currentUserId]);
useEffect(() => {
const supabase = createClient();
let channel: ReturnType | null = null;
let cancelled = false;
waitForRealtimeAuth(supabase).then(() => {
if (cancelled) return;
channel = supabase
.channel("urn-votes-changes")
.on("postgres_changes", { event: "*", schema: "public", table: "urn_votes" }, refetch)
.subscribe();
});
return () => {
cancelled = true;
if (channel) supabase.removeChannel(channel);
};
}, [refetch]);
async function handleVote(targetId: string) {
setError(null);
const supabase = createClient();
const { error: rpcError } = await supabase.rpc("cast_urn_vote", { p_target_id: targetId });
if (rpcError) {
setError(
rpcError.message.includes("already voted")
? "Tu as déjà voté aujourd'hui."
: "Une erreur est survenue, réessaie.",
);
refetch();
return;
}
refetch();
}
const sortedCitizens = [...citizens].sort((a, b) => {
const diff = (voteCounts[b.id] ?? 0) - (voteCounts[a.id] ?? 0);
return diff !== 0 ? diff : a.pseudo.localeCompare(b.pseudo);
});
const myVoteTarget = myVoteToday ? citizens.find((c) => c.id === myVoteToday.target_id) : null;
const otherCitizens = citizens.filter((c) => c.id !== currentUserId);
return (
Classement
{sortedCitizens.map((citizen) => {
const count = voteCounts[citizen.id] ?? 0;
return (
-
{citizen.pseudo}
{count > 0 ? `×${count}` : "—"}
);
})}
{!isJudge && (
{myVoteToday ? (
Tu as voté pour {myVoteTarget?.pseudo ?? "quelqu'un"} aujourd'hui.
Reviens demain pour déposer un nouveau jeton.
) : (
<>
Déposer un jeton
{error && (
{error}
)}
{otherCitizens.map((citizen) => (
handleVote(citizen.id)} />
))}
>
)}
)}
);
}