Ouvre Le Mur de la Honte aux Archontes, plafond quotidien relevé à 12
Build and deploy / deploy (push) Successful in 36s

post_wall_note() n'a plus de vérification de rôle : un Archonte peut désormais publier une note anonyme comme n'importe quel Citoyen. La policy Storage d'upload sur wall-images perd aussi son filtre par rôle (sinon possible de poster une note mais pas d'y joindre une image). Limite quotidienne relevée de 10 à 12 notes pour couvrir tout le groupe plutôt que les seuls Citoyens.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Valentin ROBIN
2026-08-25 17:30:46 +02:00
parent 40a10170bb
commit 5e75939b94
4 changed files with 113 additions and 107 deletions
+80 -78
View File
@@ -9,7 +9,11 @@ import { createClient, waitForRealtimeAuth } from "@/lib/supabase/client";
import type { MyReactionRow, ReactionCountRow, WallHistoryRow, WallNoteRow } from "./page";
const MAX_LENGTH = 200;
const MAX_NOTES_PER_DAY = 10;
// Les Archontes peuvent désormais publier aussi (plus réservé aux
// Citoyens) : 12 places pour couvrir tout le groupe, pas seulement les
// Citoyens — doit rester aligné avec la borne côté serveur dans
// post_wall_note (supabase/schema.sql).
const MAX_NOTES_PER_DAY = 12;
const MAX_IMAGE_SOURCE_BYTES = 20 * 1024 * 1024;
const REACTION_EMOJIS = ["👍", "😂", "😱", "❤️", "🔥"];
@@ -362,85 +366,83 @@ export function MurView({
)}
</div>
{!isJudge && (
<div className="marble-surface rounded-2xl border border-gold/40 p-4 shadow-sm">
{myNoteToday ? (
<div>
<p className="text-sm text-olive">Ta note d&apos;aujourd&apos;hui est publiée.</p>
{myNoteToday.image_url && (
<Image
src={myNoteToday.image_url}
alt=""
width={300}
height={200}
className="mt-2 max-h-40 w-full rounded-md object-cover"
/>
)}
<p className="mt-2 rounded-md border border-gold/20 bg-white/40 px-3 py-2 text-sm text-text-marble">
{myNoteToday.text}
</p>
<p className="mt-2 text-xs text-text-mut">Reviens demain pour en publier une nouvelle.</p>
</div>
) : wallFull ? (
<p className="text-sm text-text-mut">Le mur est complet pour aujourd&apos;hui, reviens demain !</p>
) : (
<form onSubmit={handleSubmit} className="flex flex-col gap-2">
<textarea
value={text}
onChange={(event) => setText(event.target.value)}
maxLength={MAX_LENGTH}
rows={2}
placeholder="Écrire une note anonyme…"
className="rounded-md border border-gold/30 bg-white/50 px-3 py-2 text-sm text-text-marble outline-none focus:border-gold"
<div className="marble-surface rounded-2xl border border-gold/40 p-4 shadow-sm">
{myNoteToday ? (
<div>
<p className="text-sm text-olive">Ta note d&apos;aujourd&apos;hui est publiée.</p>
{myNoteToday.image_url && (
<Image
src={myNoteToday.image_url}
alt=""
width={300}
height={200}
className="mt-2 max-h-40 w-full rounded-md object-cover"
/>
<p className="text-right text-[0.65rem] text-text-mut">
{text.length}/{MAX_LENGTH}
)}
<p className="mt-2 rounded-md border border-gold/20 bg-white/40 px-3 py-2 text-sm text-text-marble">
{myNoteToday.text}
</p>
<p className="mt-2 text-xs text-text-mut">Reviens demain pour en publier une nouvelle.</p>
</div>
) : wallFull ? (
<p className="text-sm text-text-mut">Le mur est complet pour aujourd&apos;hui, reviens demain !</p>
) : (
<form onSubmit={handleSubmit} className="flex flex-col gap-2">
<textarea
value={text}
onChange={(event) => setText(event.target.value)}
maxLength={MAX_LENGTH}
rows={2}
placeholder="Écrire une note anonyme…"
className="rounded-md border border-gold/30 bg-white/50 px-3 py-2 text-sm text-text-marble outline-none focus:border-gold"
/>
<p className="text-right text-[0.65rem] text-text-mut">
{text.length}/{MAX_LENGTH}
</p>
{imagePreview ? (
<div className="relative w-32">
<Image
src={imagePreview}
alt=""
width={128}
height={96}
unoptimized
className="h-24 w-32 rounded-md object-cover"
/>
<button
type="button"
onClick={clearImage}
className="absolute -top-2 -right-2 rounded-full bg-ink-2 p-1 text-marble"
aria-label="Retirer l'image"
>
<IconClose className="h-3 w-3" />
</button>
</div>
) : (
<label className="inline-flex w-fit cursor-pointer items-center gap-1.5 rounded-md border border-gold/30 px-3 py-1.5 text-xs font-medium text-text-marble hover:bg-gold/10">
<IconCamera className="h-4 w-4" />
Ajouter une image (optionnel)
<input type="file" accept="image/*" onChange={handleImageChange} className="hidden" />
</label>
)}
{imageError && <p className="text-xs text-oxblood">{imageError}</p>}
{error && (
<p role="alert" className="text-sm text-oxblood">
{error}
</p>
{imagePreview ? (
<div className="relative w-32">
<Image
src={imagePreview}
alt=""
width={128}
height={96}
unoptimized
className="h-24 w-32 rounded-md object-cover"
/>
<button
type="button"
onClick={clearImage}
className="absolute -top-2 -right-2 rounded-full bg-ink-2 p-1 text-marble"
aria-label="Retirer l'image"
>
<IconClose className="h-3 w-3" />
</button>
</div>
) : (
<label className="inline-flex w-fit cursor-pointer items-center gap-1.5 rounded-md border border-gold/30 px-3 py-1.5 text-xs font-medium text-text-marble hover:bg-gold/10">
<IconCamera className="h-4 w-4" />
Ajouter une image (optionnel)
<input type="file" accept="image/*" onChange={handleImageChange} className="hidden" />
</label>
)}
{imageError && <p className="text-xs text-oxblood">{imageError}</p>}
{error && (
<p role="alert" className="text-sm text-oxblood">
{error}
</p>
)}
<button
type="submit"
disabled={posting}
className="rounded-md bg-ink-2 px-4 py-2 font-heading text-sm tracking-wide text-gold-bright uppercase transition hover:bg-ink disabled:opacity-50"
>
{posting ? "Publication…" : "Publier anonymement"}
</button>
</form>
)}
</div>
)}
)}
<button
type="submit"
disabled={posting}
className="rounded-md bg-ink-2 px-4 py-2 font-heading text-sm tracking-wide text-gold-bright uppercase transition hover:bg-ink disabled:opacity-50"
>
{posting ? "Publication…" : "Publier anonymement"}
</button>
</form>
)}
</div>
{isJudge && (
<div className="marble-surface rounded-2xl border border-gold/40 p-4 shadow-sm">