← back to Dw Boardroom V2
frontend/src/components/AgendaManager.tsx
133 lines
import { useState, useEffect } from 'react';
import { api } from '../api';
import type { AgendaItem, Meeting } from '../types';
export default function AgendaManager() {
const [meeting, setMeeting] = useState<Meeting | null>(null);
const [items, setItems] = useState<AgendaItem[]>([]);
const [topic, setTopic] = useState('');
const [recommendation, setRecommendation] = useState('');
const [decisionType, setDecisionType] = useState('B');
const [owner, setOwner] = useState('');
useEffect(() => {
api.getActiveMeeting().then(data => {
setMeeting(data.meeting);
if (data.meeting) {
api.getAgenda(data.meeting.id).then(setItems);
}
}).catch(() => {});
}, []);
function refresh() {
if (meeting) {
api.getAgenda(meeting.id).then(setItems);
}
}
async function addItem() {
if (!meeting || !topic.trim()) return;
try {
await api.addAgendaItem(meeting.id, { topic, recommendation, decisionType, owner: owner || undefined });
setTopic(''); setRecommendation(''); setOwner('');
refresh();
} catch (err: any) {
alert(err.message);
}
}
async function deleteItem(id: number) {
if (!meeting) return;
await api.deleteAgendaItem(meeting.id, id);
refresh();
}
if (!meeting) {
return (
<div style={styles.card}>
<div style={styles.empty}>No active meeting. Start a meeting first to manage its agenda.</div>
</div>
);
}
return (
<div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
{/* Add Item Form */}
<div style={styles.card}>
<h3 style={styles.title}>Add Agenda Item</h3>
<div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
<input style={styles.input} placeholder="Topic" value={topic} onChange={e => setTopic(e.target.value)} />
<input style={styles.input} placeholder="Recommendation" value={recommendation} onChange={e => setRecommendation(e.target.value)} />
<select style={styles.select} value={decisionType} onChange={e => setDecisionType(e.target.value)}>
<option value="A">Type A (Auto)</option>
<option value="B">Type B (Advisory)</option>
<option value="C">Type C (Human)</option>
</select>
<input style={{ ...styles.input, maxWidth: 120 }} placeholder="Owner" value={owner} onChange={e => setOwner(e.target.value)} />
<button style={styles.addBtn} onClick={addItem} disabled={items.length >= 5}>
{items.length >= 5 ? 'Max 5' : 'Add'}
</button>
</div>
<div style={{ fontSize: 11, color: '#7a7f96', marginTop: 6 }}>
{items.length}/5 items | Meeting: {meeting.meeting_type} ({meeting.id.slice(0, 8)})
</div>
</div>
{/* Items List */}
<div style={styles.card}>
<h3 style={styles.title}>Current Agenda</h3>
<div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
{items.length === 0 && <div style={styles.empty}>No agenda items yet.</div>}
{items.map((item, idx) => (
<div key={item.id} style={styles.item}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
<span style={styles.position}>{idx + 1}</span>
<span style={{ fontWeight: 600, fontSize: 13 }}>{item.topic}</span>
</div>
<div style={{ display: 'flex', gap: 6, alignItems: 'center' }}>
<span style={{
...styles.typeBadge,
background: item.decision_type === 'A' ? '#22c55e22' : item.decision_type === 'C' ? '#ef444422' : '#3b82f622',
color: item.decision_type === 'A' ? '#22c55e' : item.decision_type === 'C' ? '#ef4444' : '#3b82f6',
}}>
Type {item.decision_type}
</span>
<span style={{
...styles.statusBadge,
background: item.status === 'discussed' ? '#f59e0b22' : item.status === 'resolved' ? '#22c55e22' : '#7a7f9622',
color: item.status === 'discussed' ? '#f59e0b' : item.status === 'resolved' ? '#22c55e' : '#7a7f96',
}}>
{item.status}
</span>
<button style={styles.deleteBtn} onClick={() => deleteItem(item.id)}>x</button>
</div>
</div>
{item.recommendation && (
<div style={{ fontSize: 11, color: '#7a7f96', marginTop: 4 }}>Rec: {item.recommendation}</div>
)}
{item.owner && (
<div style={{ fontSize: 11, color: '#7a7f96', marginTop: 2 }}>Owner: {item.owner}</div>
)}
</div>
))}
</div>
</div>
</div>
);
}
const styles: Record<string, React.CSSProperties> = {
card: { background: '#1a1a2e', borderRadius: 12, padding: 20, border: '1px solid #252540' },
title: { margin: '0 0 12px', fontSize: 15, fontWeight: 700 },
input: { padding: '8px 10px', borderRadius: 6, border: '1px solid #252540', background: '#0a0a12', color: '#e8eaf0', fontSize: 12, fontFamily: 'inherit', flex: 1, minWidth: 140 },
select: { padding: '8px', borderRadius: 6, border: '1px solid #252540', background: '#0a0a12', color: '#e8eaf0', fontSize: 12, fontFamily: 'inherit' },
addBtn: { padding: '8px 16px', borderRadius: 6, border: 'none', background: '#8b5cf6', color: '#fff', fontWeight: 600, fontSize: 12, cursor: 'pointer', fontFamily: 'inherit' },
empty: { color: '#7a7f96', fontSize: 13, textAlign: 'center' as const, padding: 30 },
item: { background: '#12121f', borderRadius: 8, padding: 12, border: '1px solid #252540' },
position: { width: 22, height: 22, borderRadius: '50%', background: '#8b5cf622', color: '#8b5cf6', display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 10, fontWeight: 700 },
typeBadge: { padding: '2px 8px', borderRadius: 4, fontSize: 10, fontWeight: 600 },
statusBadge: { padding: '2px 8px', borderRadius: 4, fontSize: 10, fontWeight: 600 },
deleteBtn: { width: 20, height: 20, borderRadius: 4, border: '1px solid #ef444466', background: 'transparent', color: '#ef4444', fontSize: 11, cursor: 'pointer', fontFamily: 'inherit' },
};