// Shared screens: Meldungen Kanban, Chat, News, Calendar, Wasch, Docs, Settings const MeldungenKanban = ({onOpen, ctx}) => { const [view, setView] = React.useState('kanban'); const [tickets, setTickets] = React.useState([]); React.useEffect(() => { Store.getTickets().then(ts => setTickets(ts.length ? ts : TICKETS)); }, [ctx?.dataTick]); const byStatus = Object.keys(STATUS_LABELS).reduce((acc,s)=>{acc[s]=tickets.filter(t=>t.status===s); return acc;},{}); return ( <>
} />
{view==='kanban' ? (
{Object.entries(STATUS_LABELS).map(([k,v])=>(
{v.label} {byStatus[k].length}
{byStatus[k].map(t=>(
onOpen&&onOpen(t.id)}>
{t.id}
{t.title}
{t.category||t.cat||'—'}
{t.unit||t.mieter||'—'} {t.created_at ? new Date(t.created_at).toLocaleDateString('de-CH') : (t.date||'')}
))}
))}
) : ( {tickets.map(t=>( onOpen&&onOpen(t.id)} style={{cursor:'pointer'}}> ))}
IDTitelKategorieDringlichkeitMieterLiegenschaftStatusGemeldet
{t.id} {t.title} {t.category||t.cat||'—'} {t.unit||t.mieter||'—'} {t.created_at ? new Date(t.created_at).toLocaleDateString('de-CH') : (t.date||'')}
)}
); }; const LiegenschaftsChat = ({inline, onNav}) => { const [active, setActive] = React.useState('L-001'); const [msg, setMsg] = React.useState(''); const chats = [ {id:'L-001', name:'Rämistrasse 42', members:11, last:'Hauswart-Schlüssel reicht völlig.', when:'11:50', unread:0}, {id:'L-002', name:'Länggass. 78', members:8, last:'@Peter wegen Waschmaschine 3.OG', when:'gestern', unread:2}, {id:'L-003', name:'Spalenring 134', members:14, last:'Danke für die schnelle Rückmeldung!', when:'gestern', unread:0}, {id:'L-005', name:'Zürcherstr. 205', members:13, last:'Termin Schimmel 3.OG bestätigt', when:'2 T.', unread:0}, {id:'DM-AM', name:'Andrea Meier', members:'1:1', last:'Können Sie mir den Mietvertrag zusenden?', when:'3 T.', unread:1, dm:true}, ]; const content = (
ALLE CHATS
{chats.map(c=>(
setActive(c.id)}>
{c.dm?:}
{c.name}
{c.dm?'Direktnachricht':`${c.members} Teilnehmer`}
{c.when}
{c.unread>0 &&
{c.unread}
}
{c.last}
))}
Rämistrasse 42 · Liegenschafts-Chat
11 Teilnehmer · 9 online · Moderiert durch Verwaltung
Angepinnt · Peter (Hauswart) · Notfall­nummer ausserhalb Bürozeiten: 044 999 88 77. Bei Wasser­schäden immer zuerst Hauptventil UG zudrehen.
{CHAT_MESSAGES.map(m=>(
{!m.me &&
{m.from} · {m.role}
}
{m.text}
{m.time} {m.me && '· gelesen von 8'}
))}
setMsg(e.target.value)}/>
TEILNEHMER · 11
Laura Keller
Verwaltung
Peter Aeschlimann
Hauswart
{['AM','RH','SF','MG','JS','FW','BL'].map((ini,i)=>(
{['Andrea Meier','Reto Huber','Sandra Frei','Marco Gasser','Johannes Stucki','Flavio Widmer','Beatrice Lehmann'][i]}
Mieter
))}
DATEIEN & FOTOS
{[1,2,3,4].map(i=>
IMG
)}
); if (inline) return content; return ( <>
{content}
); }; const NEWS_TONE = {urgent:'crit', event:'warn', info:'neutral'}; const NEWS_LABEL = {urgent:'DRINGEND', event:'TERMIN', info:'INFO'}; const SchwarzesBrett = ({standalone, propId, ctx}) => { const [posts, setPosts] = React.useState([]); const [showForm, setShowForm] = React.useState(false); const [form, setForm] = React.useState({title:'', body:'', type:'info', pinned:false}); const [saving, setSaving] = React.useState(false); const load = () => Store.getNews(propId||null).then(ps => setPosts(ps.length ? ps : NEWS)); React.useEffect(() => { load(); }, [propId, ctx?.dataTick]); const submit = async () => { if (!form.title || saving) return; setSaving(true); try { const n = await Store.createNews({...form, pinned: form.pinned?1:0, propId: propId||null}); setPosts(prev => [n, ...prev.filter(p => p.id)]); setShowForm(false); setForm({title:'', body:'', type:'info', pinned:false}); } catch(e) { alert(e.message); } setSaving(false); }; const del = async (id) => { if (!id || typeof id === 'number') return; // don't delete mock if (!confirm('Post löschen?')) return; await Store.deleteNews(id); setPosts(prev => prev.filter(p => p.id !== id)); }; const content = (
{showForm && (

Neuer Post

{['info','event','urgent'].map(t=>( ))}
setForm(f=>({...f,title:e.target.value}))} placeholder="Betreff des Posts" autoFocus/>