// screen-idea.jsx const { useState: useIdeaState, useEffect: useIdeaEffect } = React; function IdeaDetail({ idea, vote, onVote, goBack, openUser, currentUser }) { const Icon = window.Icon; const [bookmarked, setBookmarked] = useIdeaState(true); const [draft, setDraft] = useIdeaState(''); // idea.comments from the home list is a count number, not an array — always start empty and load from API const [comments, setComments] = useIdeaState(Array.isArray(idea?.comments) ? idea.comments : []); const [expanded, setExpanded] = useIdeaState(null); const [posting, setPosting] = useIdeaState(false); const [replyTo, setReplyTo] = useIdeaState(null); // comment id being replied to const [replyDraft, setReplyDraft] = useIdeaState(''); const [replyPosting, setReplyPosting] = useIdeaState(false); const [replies, setReplies] = useIdeaState({}); // { [commentId]: [...] } const [loadingReplies, setLoadingReplies] = useIdeaState({}); // Reload comments when idea changes useIdeaEffect(() => { if (!idea?.id) return; window.API.getIdea(idea.id) .then(d => setComments(d.comments || [])) .catch(() => setComments(window.COMMENTS)); }, [idea?.id]); const ideaUp = Number(idea?.up_votes ?? idea?.up ?? 0); const ideaDown = Number(idea?.down_votes ?? idea?.down ?? 0); const up = ideaUp + (vote === 'up' ? 1 : 0); const down = ideaDown + (vote === 'down' ? 1 : 0); const post = async () => { if (!draft.trim() || posting) return; setPosting(true); try { await window.API.addComment(idea.id, draft.trim()); const d = await window.API.getIdea(idea.id); setComments(d.comments || []); setDraft(''); } catch { // optimistic fallback const me = currentUser || {}; setComments(cs => [{ id: Date.now(), author: me.name || 'You', avatar: me.avatar, ago: 'just now', up_votes: 0, down_votes: 0, replies: 0, body: draft.trim() }, ...cs]); setDraft(''); } finally { setPosting(false); } }; const toggleReplies = async (commentId, currentCount) => { if (expanded === commentId) { setExpanded(null); return; } setExpanded(commentId); if (replies[commentId]) return; // already loaded setLoadingReplies(l => ({ ...l, [commentId]: true })); try { const data = await window.API.getReplies(idea.id, commentId); setReplies(r => ({ ...r, [commentId]: data })); } catch { setReplies(r => ({ ...r, [commentId]: [] })); } finally { setLoadingReplies(l => ({ ...l, [commentId]: false })); } }; const postReply = async (parentId) => { if (!replyDraft.trim() || replyPosting) return; setReplyPosting(true); try { await window.API.addComment(idea.id, replyDraft.trim(), parentId); // Reload replies for this comment const data = await window.API.getReplies(idea.id, parentId); setReplies(r => ({ ...r, [parentId]: data })); // Update reply count on parent comment setComments(cs => cs.map(c => c.id === parentId ? { ...c, replies: (Number(c.replies) || 0) + 1 } : c)); setReplyDraft(''); setReplyTo(null); } catch { // optimistic fallback const me = currentUser || {}; const newReply = { id: Date.now(), author: me.name || 'You', avatar: me.avatar, ago: 'just now', up_votes: 0, down_votes: 0, body: replyDraft.trim() }; setReplies(r => ({ ...r, [parentId]: [...(r[parentId] || []), newReply] })); setComments(cs => cs.map(c => c.id === parentId ? { ...c, replies: (Number(c.replies) || 0) + 1 } : c)); setReplyDraft(''); setReplyTo(null); } finally { setReplyPosting(false); } }; const [cVotes, setCVotes] = useIdeaState({}); const voteComment = async (id, dir) => { const prev = cVotes[id]; setCVotes(v => ({ ...v, [id]: v[id] === dir ? undefined : dir })); try { const res = await window.API.voteComment(idea.id, id, dir); setCVotes(v => ({ ...v, [id]: res.vote || undefined })); } catch { setCVotes(v => ({ ...v, [id]: prev })); } }; return (
{idea.desc} The proposal outlines a phased rollout, success metrics tied to engagement and turnaround time, and a clear owner for each milestone so progress stays visible to leadership.
{cText}
{r.body || r.text}
{s.text}