import React, { useState, useRef } from "react"; import { generateId } from "../utils.js"; const TYPE_META = { beitrag: { label: "Beitrag", color: "#1a4e8a", bg: "#e8f0fa" }, ankuendigung: { label: "Ankündigung", color: "#b5621e", bg: "#fdf0e8" }, event: { label: "Event", color: "#2d6a4f", bg: "#e8f5ee" }, }; function canWriteCheck(currentUser, data) { const myUser = (data.users || []).find(u => u.id === currentUser?.id); const myRole = (data.appRoles|| []).find(r => r.id === (currentUser?.appRoleId || myUser?.appRoleId)); return currentUser?.role === "admin" || myRole?.permissions === null || (myRole?.permissions || []).includes("pinnwand-schreiben"); } // ─── Poll bar ───────────────────────────────────────────────────────────────── function PollBlock({ poll, postId, currentUserId, onVote }) { if (!poll) return null; const total = Object.keys(poll.votes || {}).length; const myVote = poll.votes?.[currentUserId]; const hasVoted = !!myVote; return (
{poll.question}
{poll.options.map(opt => { const count = Object.values(poll.votes || {}).filter(v => v === opt.id).length; const pct = total > 0 ? Math.round((count / total) * 100) : 0; const isMyVote = myVote === opt.id; return (
{!hasVoted ? ( ) : (
{isMyVote ? "✓ " : ""}{opt.label} {count} ({pct}%)
)}
); })} {hasVoted &&
{total} Stimme{total !== 1 ? "n" : ""} abgegeben
}
); } // ─── Post card ──────────────────────────────────────────────────────────────── function PostCard({ post, currentUser, canWrite, onEdit, onDelete, onPin, onVote }) { const meta = TYPE_META[post.type] || TYPE_META.beitrag; const isOwn = post.authorId === currentUser?.id; const isAdmin = currentUser?.role === "admin"; const [expanded, setExpanded] = useState(false); const [confirmDel, setConfirmDel] = useState(false); const bodyLines = (post.body || "").split("\n"); const isLong = bodyLines.length > 6 || post.body?.length > 400; const isDraft = post.status === "entwurf"; return (
{post.image && ( )}
{meta.label.toUpperCase()} {isDraft && ENTWURF} {post.pinned && push_pin ANGEPINNT} {post.eventDate && ( 📅 {new Date(post.eventDate).toLocaleDateString("de-CH", { weekday: "short", day: "numeric", month: "long" })} )}
{isAdmin && !isDraft && ( )} {(isOwn || isAdmin) && canWrite && ( <> {confirmDel ? ( <> Löschen? ) : ( )} )}
{post.title &&
{post.title}
}
{post.body}
{isLong && ( )}
{post.authorName} {new Date(post.createdAt).toLocaleDateString("de-CH", { day: "numeric", month: "long", year: "numeric", hour: "2-digit", minute: "2-digit" })}
); } // ─── Post modal ─────────────────────────────────────────────────────────────── function PostModal({ initial, onSave, onClose }) { const [form, setForm] = useState(initial || { title: "", body: "", type: "beitrag", eventDate: "", pinned: false, poll: null, image: "" }); const [pollEnabled, setPollEnabled] = useState(!!initial?.poll); const [poll, setPoll] = useState(initial?.poll || { question: "", options: [{ id: generateId(), label: "" }, { id: generateId(), label: "" }], votes: {} }); const fileInputRef = useRef(null); const setF = patch => setForm(f => ({ ...f, ...patch })); const setOpt = (id, label) => setPoll(p => ({ ...p, options: p.options.map(o => o.id === id ? { ...o, label } : o) })); const addOpt = () => { if (poll.options.length >= 5) return; setPoll(p => ({ ...p, options: [...p.options, { id: generateId(), label: "" }] })); }; const delOpt = id => { if (poll.options.length <= 2) return; setPoll(p => ({ ...p, options: p.options.filter(o => o.id !== id) })); }; const handleImage = (e) => { const file = e.target.files?.[0]; if (!file) return; const reader = new FileReader(); reader.onload = (ev) => { const img = new Image(); img.onload = () => { const maxW = 1200; const scale = img.width > maxW ? maxW / img.width : 1; const canvas = document.createElement("canvas"); canvas.width = Math.round(img.width * scale); canvas.height = Math.round(img.height * scale); canvas.getContext("2d").drawImage(img, 0, 0, canvas.width, canvas.height); setF({ image: canvas.toDataURL("image/jpeg", 0.8) }); }; img.src = ev.target.result; }; reader.readAsDataURL(file); e.target.value = ""; }; const handleSave = (draft = false) => { if (!form.body.trim()) return; const finalPoll = pollEnabled && poll.question.trim() && poll.options.filter(o => o.label.trim()).length >= 2 ? { ...poll, options: poll.options.filter(o => o.label.trim()), votes: initial?.poll?.votes || {} } : null; onSave({ ...form, poll: finalPoll }, draft); }; const isDraft = initial?.status === "entwurf"; const isNew = !initial; const primaryLabel = (isNew || isDraft) ? "Veröffentlichen" : "Speichern"; return (
e.stopPropagation()}>
{initial ? "Beitrag bearbeiten" : "Neuer Beitrag"}
{form.type === "event" && (
setF({ eventDate: e.target.value })} />
)}
setF({ title: e.target.value })} placeholder="Titel des Beitrags…" />