Ouvre Le Mur de la Honte aux Archontes, plafond quotidien relevé à 12
Build and deploy / deploy (push) Successful in 36s
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:
+80
-78
@@ -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'aujourd'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'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'aujourd'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'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">
|
||||
|
||||
+9
-10
@@ -50,15 +50,14 @@ export default async function MurPage() {
|
||||
supabase.from("wall_note_reactions").select("note_id, emoji").eq("user_id", user.id),
|
||||
]);
|
||||
|
||||
let myNoteToday: WallNoteRow | null = null;
|
||||
if (!isJudge) {
|
||||
const { data: myNotes } = await supabase
|
||||
.from("wall_notes")
|
||||
.select("id, text, image_url, created_at")
|
||||
.eq("user_id", user.id)
|
||||
.order("created_at", { ascending: false });
|
||||
myNoteToday = (myNotes ?? []).find((n) => isToday(n.created_at)) ?? null;
|
||||
}
|
||||
// Les Archontes peuvent désormais publier aussi (comme les Citoyens) :
|
||||
// besoin de savoir s'ils ont déjà posté aujourd'hui, peu importe le rôle.
|
||||
const { data: myNotes } = await supabase
|
||||
.from("wall_notes")
|
||||
.select("id, text, image_url, created_at")
|
||||
.eq("user_id", user.id)
|
||||
.order("created_at", { ascending: false });
|
||||
const myNoteToday: WallNoteRow | null = (myNotes ?? []).find((n) => isToday(n.created_at)) ?? null;
|
||||
|
||||
let history: WallHistoryRow[] = [];
|
||||
if (isJudge) {
|
||||
@@ -75,7 +74,7 @@ export default async function MurPage() {
|
||||
<div className="mb-6 text-center">
|
||||
<h1 className="font-heading text-2xl tracking-[0.1em] text-gold-bright uppercase">Le Mur de la Honte</h1>
|
||||
<p className="font-serif text-sm text-marble/60 italic">
|
||||
Une note anonyme par jour, dix places sur le mur — la journée passée, tout s'efface.
|
||||
Une note anonyme par jour, douze places sur le mur — la journée passée, tout s'efface.
|
||||
</p>
|
||||
</div>
|
||||
<MurView
|
||||
|
||||
Reference in New Issue
Block a user