Whiteboard: Titel nachträglich umbenennbar (Doppelklick oder ✎-Button, in Liste und Topbar)
This commit is contained in:
@@ -188,6 +188,8 @@ export default function Whiteboard({ toast, mobile }) {
|
||||
const [current, setCurrent] = useState(null); // { id, title, role }
|
||||
const [creating, setCreating] = useState(false);
|
||||
const [newTitle, setNewTitle] = useState('');
|
||||
const [editingId, setEditingId] = useState(null); // ID des gerade umbenannten Boards
|
||||
const [editTitle, setEditTitle] = useState('');
|
||||
|
||||
// ── Canvas-State ──────────────────────────────────────────────────────────
|
||||
const [elements, setElements] = useState([]);
|
||||
@@ -224,6 +226,16 @@ export default function Whiteboard({ toast, mobile }) {
|
||||
.finally(() => setLoading(false));
|
||||
}, []);
|
||||
|
||||
const renameBoard = async (id, title) => {
|
||||
if (!title?.trim()) return;
|
||||
try {
|
||||
await api(`/tools/whiteboard/${id}/title`, { method:'PATCH', body:{ title: title.trim() } });
|
||||
setBoards(prev => prev.map(b => b.id === id ? { ...b, title: title.trim() } : b));
|
||||
if (current?.id === id) setCurrent(prev => ({ ...prev, title: title.trim() }));
|
||||
} catch(e) { toast(e.message, 'error'); }
|
||||
finally { setEditingId(null); }
|
||||
};
|
||||
|
||||
useEffect(() => { loadBoards(); }, [loadBoards]);
|
||||
|
||||
// Canvas rendern wenn sich Elemente oder Viewport ändern
|
||||
@@ -560,16 +572,34 @@ export default function Whiteboard({ toast, mobile }) {
|
||||
|
||||
{boards.map(b => (
|
||||
<div key={b.id} style={{ ...S.card, display:'flex', alignItems:'center', gap:12, marginBottom:8, cursor:'pointer' }}
|
||||
onClick={() => openBoard(b)}>
|
||||
onClick={() => { if (editingId !== b.id) openBoard(b); }}>
|
||||
<div style={{ fontSize:24 }}>🖊</div>
|
||||
<div style={{ flex:1, minWidth:0 }}>
|
||||
<div style={{ color:'#fff', fontFamily:'monospace', fontSize:13, fontWeight:700 }}>{b.title}</div>
|
||||
{editingId === b.id ? (
|
||||
<input autoFocus value={editTitle}
|
||||
onChange={e => setEditTitle(e.target.value)}
|
||||
onKeyDown={e => { if (e.key==='Enter') renameBoard(b.id, editTitle); if (e.key==='Escape') setEditingId(null); }}
|
||||
onBlur={() => renameBoard(b.id, editTitle)}
|
||||
onClick={e => e.stopPropagation()}
|
||||
style={{ ...S.inp, fontSize:12, fontWeight:700, padding:'3px 8px', width:'100%' }}
|
||||
/>
|
||||
) : (
|
||||
<div style={{ color:'#fff', fontFamily:'monospace', fontSize:13, fontWeight:700 }}
|
||||
onDoubleClick={e => { e.stopPropagation(); if (b.role==='owner'||b.role==='edit'){ setEditingId(b.id); setEditTitle(b.title); } }}>
|
||||
{b.title}
|
||||
</div>
|
||||
)}
|
||||
<div style={{ color:'rgba(255,255,255,0.3)', fontFamily:'monospace', fontSize:10, marginTop:2 }}>
|
||||
{b.role === 'owner' ? '👑 Eigenes' : b.role === 'edit' ? '✏ Bearbeiten' : '👁 Lesen'}
|
||||
{b.owner_name && b.role !== 'owner' && ` · von ${b.owner_name}`}
|
||||
{' · ' + new Date(b.updated_at).toLocaleDateString('de-DE', { day:'2-digit', month:'2-digit', year:'numeric' })}
|
||||
</div>
|
||||
</div>
|
||||
{(b.role === 'owner' || b.role === 'edit') && (
|
||||
<button onClick={e => { e.stopPropagation(); setEditingId(b.id); setEditTitle(b.title); }}
|
||||
title="Umbenennen"
|
||||
style={{ background:'transparent', border:'none', color:'rgba(255,255,255,0.2)', cursor:'pointer', fontSize:14, padding:'4px 6px' }}>✎</button>
|
||||
)}
|
||||
{b.role === 'owner' && (
|
||||
<button onClick={e => { e.stopPropagation(); if (confirm(`"${b.title}" löschen?`))
|
||||
api(`/tools/whiteboard/${b.id}`, { method:'DELETE' }).then(loadBoards).catch(err => toast(err.message, 'error'));
|
||||
@@ -609,11 +639,24 @@ export default function Whiteboard({ toast, mobile }) {
|
||||
background:'rgba(0,0,0,0.6)', borderBottom:'1px solid rgba(255,255,255,0.08)', flexShrink:0, flexWrap:'wrap' }}>
|
||||
<button onClick={closeBoard} style={{ background:'transparent', border:'none', color:'rgba(255,255,255,0.5)',
|
||||
cursor:'pointer', fontSize:18, padding:'2px 6px', lineHeight:1 }}>←</button>
|
||||
<span style={{ color:'#fff', fontFamily:'monospace', fontSize:13, fontWeight:700, flex:1, minWidth:0,
|
||||
overflow:'hidden', textOverflow:'ellipsis', whiteSpace:'nowrap' }}>
|
||||
{editingId === current?.id ? (
|
||||
<input autoFocus value={editTitle}
|
||||
onChange={e => setEditTitle(e.target.value)}
|
||||
onKeyDown={e => { if (e.key==='Enter') renameBoard(current.id, editTitle); if (e.key==='Escape') setEditingId(null); }}
|
||||
onBlur={() => renameBoard(current.id, editTitle)}
|
||||
style={{ ...S.inp, fontSize:13, fontWeight:700, flex:1, padding:'3px 8px' }}
|
||||
/>
|
||||
) : (
|
||||
<span
|
||||
onDoubleClick={() => { if (current?.role !== 'view'){ setEditingId(current.id); setEditTitle(current.title); } }}
|
||||
title={current?.role !== 'view' ? 'Doppelklick zum Umbenennen' : ''}
|
||||
style={{ color:'#fff', fontFamily:'monospace', fontSize:13, fontWeight:700, flex:1, minWidth:0,
|
||||
overflow:'hidden', textOverflow:'ellipsis', whiteSpace:'nowrap',
|
||||
cursor: current?.role !== 'view' ? 'text' : 'default' }}>
|
||||
{current?.title}
|
||||
{current?.role === 'view' && <span style={{ color:'rgba(255,255,255,0.3)', fontSize:10, marginLeft:8 }}>👁 nur lesen</span>}
|
||||
</span>
|
||||
)}
|
||||
|
||||
{/* Aktive User */}
|
||||
{collab.length > 0 && (
|
||||
|
||||
Reference in New Issue
Block a user