Ajoute l'historique des versions des questions soumises par les Citoyens
Build and deploy / deploy (push) Successful in 35s
Build and deploy / deploy (push) Successful in 35s
Nouvelle table chariot_submission_history (append-only, comme points_log) : chariot_submissions ne garde que la valeur actuelle (upsert), donc sans ce journal les Archontes ne voyaient jamais les versions précédentes d'une question modifiée plusieurs fois. submit_chariot_question() y écrit désormais à chaque changement réel. Sur /char/questions, chaque suggestion affiche un lien "Historique (N)" (visible seulement si la personne a déjà modifié sa proposition) qui déplie les versions précédentes avec leur date.
This commit is contained in:
@@ -17,6 +17,12 @@ export type SubmissionRow = {
|
||||
profiles: { pseudo: string; avatar_url: string | null } | null;
|
||||
};
|
||||
|
||||
export type SubmissionHistoryRow = {
|
||||
user_id: string;
|
||||
text: string;
|
||||
created_at: string;
|
||||
};
|
||||
|
||||
export default async function CharQuestionsPage() {
|
||||
const supabase = await createClient();
|
||||
const {
|
||||
@@ -58,6 +64,13 @@ export default async function CharQuestionsPage() {
|
||||
.order("updated_at", { ascending: true })
|
||||
.returns<SubmissionRow[]>();
|
||||
|
||||
// Versions précédentes des propositions (chariot_submissions ne garde que
|
||||
// la version actuelle) — pour la section "Historique" de la modération.
|
||||
const { data: submissionHistory } = await supabase
|
||||
.from("chariot_submission_history")
|
||||
.select("user_id, text, created_at")
|
||||
.order("created_at", { ascending: false });
|
||||
|
||||
return (
|
||||
<div className="mx-auto w-full max-w-2xl px-4 py-8">
|
||||
<div className="mb-6 text-center">
|
||||
@@ -76,6 +89,7 @@ export default async function CharQuestionsPage() {
|
||||
initialQuestions={questions ?? []}
|
||||
initialRevealedId={settings?.chariot_revealed_question_id ?? null}
|
||||
initialSubmissions={submissions ?? []}
|
||||
initialSubmissionHistory={submissionHistory ?? []}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -4,7 +4,15 @@ import { useCallback, useEffect, useState } from "react";
|
||||
import { Avatar } from "@/components/avatar";
|
||||
import { IconChevronDown, IconPencil, IconPlus, IconTrash } from "@/components/icons";
|
||||
import { createClient, waitForRealtimeAuth } from "@/lib/supabase/client";
|
||||
import type { QuestionRow, SubmissionRow } from "./page";
|
||||
import type { QuestionRow, SubmissionHistoryRow, SubmissionRow } from "./page";
|
||||
|
||||
function formatHistoryDate(iso: string): string {
|
||||
return new Date(iso).toLocaleString("fr-FR", {
|
||||
dateStyle: "short",
|
||||
timeStyle: "short",
|
||||
timeZone: "Europe/Paris",
|
||||
});
|
||||
}
|
||||
|
||||
function QuestionForm({
|
||||
initial,
|
||||
@@ -187,14 +195,17 @@ function QuestionRowItem({
|
||||
|
||||
function SubmissionRowItem({
|
||||
submission,
|
||||
pastVersions,
|
||||
onAddToBank,
|
||||
onReject,
|
||||
}: {
|
||||
submission: SubmissionRow;
|
||||
pastVersions: SubmissionHistoryRow[];
|
||||
onAddToBank: () => void;
|
||||
onReject: () => void;
|
||||
}) {
|
||||
const [confirmingReject, setConfirmingReject] = useState(false);
|
||||
const [showHistory, setShowHistory] = useState(false);
|
||||
const profile = submission.profiles;
|
||||
const pseudo = profile?.pseudo ?? "un Citoyen";
|
||||
|
||||
@@ -204,6 +215,28 @@ function SubmissionRowItem({
|
||||
<div className="min-w-0 flex-1">
|
||||
<p className="text-xs font-medium text-text-mut">{pseudo}</p>
|
||||
<p className="text-text-marble">{submission.text}</p>
|
||||
{pastVersions.length > 0 && (
|
||||
<>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowHistory((v) => !v)}
|
||||
className="mt-1 text-[0.7rem] font-medium text-text-mut underline hover:text-text-marble"
|
||||
>
|
||||
{showHistory ? "Masquer l'historique" : `Historique (${pastVersions.length})`}
|
||||
</button>
|
||||
{showHistory && (
|
||||
<ul className="mt-1 flex flex-col gap-1 border-l-2 border-gold/20 pl-2">
|
||||
{pastVersions.map((version) => (
|
||||
<li key={version.created_at} className="text-xs text-text-mut">
|
||||
<span className="text-text-marble">{version.text}</span>
|
||||
{" — "}
|
||||
{formatHistoryDate(version.created_at)}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex shrink-0 items-center gap-1">
|
||||
<button
|
||||
@@ -242,19 +275,22 @@ export function QuestionsView({
|
||||
initialQuestions,
|
||||
initialRevealedId,
|
||||
initialSubmissions,
|
||||
initialSubmissionHistory,
|
||||
}: {
|
||||
initialQuestions: QuestionRow[];
|
||||
initialRevealedId: string | null;
|
||||
initialSubmissions: SubmissionRow[];
|
||||
initialSubmissionHistory: SubmissionHistoryRow[];
|
||||
}) {
|
||||
const [questions, setQuestions] = useState(initialQuestions);
|
||||
const [revealedId, setRevealedId] = useState(initialRevealedId);
|
||||
const [submissions, setSubmissions] = useState(initialSubmissions);
|
||||
const [submissionHistory, setSubmissionHistory] = useState(initialSubmissionHistory);
|
||||
const [adding, setAdding] = useState(false);
|
||||
|
||||
const refetch = useCallback(async () => {
|
||||
const supabase = createClient();
|
||||
const [{ data: q }, { data: s }, { data: sub }] = await Promise.all([
|
||||
const [{ data: q }, { data: s }, { data: sub }, { data: hist }] = await Promise.all([
|
||||
supabase
|
||||
.from("chariot_questions")
|
||||
.select("id, text, position, created_at")
|
||||
@@ -266,10 +302,15 @@ export function QuestionsView({
|
||||
.select("user_id, text, updated_at, profiles(pseudo, avatar_url)")
|
||||
.order("updated_at", { ascending: true })
|
||||
.returns<SubmissionRow[]>(),
|
||||
supabase
|
||||
.from("chariot_submission_history")
|
||||
.select("user_id, text, created_at")
|
||||
.order("created_at", { ascending: false }),
|
||||
]);
|
||||
if (q) setQuestions(q);
|
||||
if (s) setRevealedId(s.chariot_revealed_question_id);
|
||||
if (sub) setSubmissions(sub);
|
||||
if (hist) setSubmissionHistory(hist);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -284,6 +325,7 @@ export function QuestionsView({
|
||||
.on("postgres_changes", { event: "*", schema: "public", table: "chariot_questions" }, refetch)
|
||||
.on("postgres_changes", { event: "*", schema: "public", table: "settings" }, refetch)
|
||||
.on("postgres_changes", { event: "*", schema: "public", table: "chariot_submissions" }, refetch)
|
||||
.on("postgres_changes", { event: "*", schema: "public", table: "chariot_submission_history" }, refetch)
|
||||
.subscribe();
|
||||
});
|
||||
|
||||
@@ -336,14 +378,22 @@ export function QuestionsView({
|
||||
Suggestions des Citoyens
|
||||
</p>
|
||||
<ul className="flex flex-col divide-y divide-gold/10">
|
||||
{submissions.map((submission) => (
|
||||
<SubmissionRowItem
|
||||
key={submission.user_id}
|
||||
submission={submission}
|
||||
onAddToBank={() => addSubmissionToBank(submission)}
|
||||
onReject={() => rejectSubmission(submission.user_id)}
|
||||
/>
|
||||
))}
|
||||
{submissions.map((submission) => {
|
||||
// Trié desc : la première entrée est la version actuelle
|
||||
// (déjà affichée au-dessus), le reste est l'historique.
|
||||
const pastVersions = submissionHistory
|
||||
.filter((h) => h.user_id === submission.user_id)
|
||||
.slice(1);
|
||||
return (
|
||||
<SubmissionRowItem
|
||||
key={submission.user_id}
|
||||
submission={submission}
|
||||
pastVersions={pastVersions}
|
||||
onAddToBank={() => addSubmissionToBank(submission)}
|
||||
onReject={() => rejectSubmission(submission.user_id)}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user