diff --git a/admin/src/pages/MeetingPlannerPage.tsx b/admin/src/pages/MeetingPlannerPage.tsx index 639305cc..83aa99ce 100644 --- a/admin/src/pages/MeetingPlannerPage.tsx +++ b/admin/src/pages/MeetingPlannerPage.tsx @@ -31,6 +31,7 @@ import { CheckCircleOutlined, ClockCircleOutlined, TeamOutlined, + EditOutlined, } from '@ant-design/icons'; import type { ColumnsType } from 'antd/es/table'; import dayjs from 'dayjs'; @@ -67,6 +68,7 @@ const TIMEZONE_OPTIONS = [ export default function MeetingPlannerPage() { const screens = Grid.useBreakpoint(); + const isMobile = !screens.md; const [polls, setPolls] = useState([]); const [pagination, setPagination] = useState({ page: 1, limit: 20, total: 0 }); const [loading, setLoading] = useState(false); @@ -93,6 +95,15 @@ export default function MeetingPlannerPage() { const [convertForm] = Form.useForm(); const [converting, setConverting] = useState(false); + // Edit drawer + const [editOpen, setEditOpen] = useState(false); + const [editForm] = Form.useForm(); + const [editing, setEditing] = useState(false); + const [editPoll, setEditPoll] = useState(null); + const [newOptionDate, setNewOptionDate] = useState(null); + const [newOptionStart, setNewOptionStart] = useState(null); + const [newOptionEnd, setNewOptionEnd] = useState(null); + const fetchPolls = useCallback(async () => { setLoading(true); try { @@ -222,6 +233,79 @@ export default function MeetingPlannerPage() { } }; + const openEditDrawer = async (id: string) => { + try { + const { data } = await api.get(`/meeting-planner/${id}`); + setEditPoll(data); + editForm.setFieldsValue({ + title: data.title, + description: data.description || '', + location: data.location || '', + timezone: data.timezone, + allowAnonymous: data.allowAnonymous, + notifyOnVote: data.notifyOnVote, + votingDeadline: data.votingDeadline ? dayjs(data.votingDeadline) : null, + }); + setEditOpen(true); + } catch { + message.error('Failed to load poll'); + } + }; + + const handleUpdate = async (values: any) => { + if (!editPoll) return; + setEditing(true); + try { + await api.put(`/meeting-planner/${editPoll.id}`, { + title: values.title, + description: values.description || null, + location: values.location || null, + timezone: values.timezone, + allowAnonymous: values.allowAnonymous, + notifyOnVote: values.notifyOnVote, + votingDeadline: values.votingDeadline?.toISOString() || null, + }); + message.success('Poll updated'); + setEditOpen(false); + setEditPoll(null); + editForm.resetFields(); + fetchPolls(); + // Refresh detail drawer if same poll is open + if (selectedPoll?.id === editPoll.id) { + fetchPollDetail(editPoll.id); + } + } catch { + message.error('Failed to update poll'); + } finally { + setEditing(false); + } + }; + + const handleAddOption = async (option: { date: string; startTime: string; endTime: string }) => { + if (!editPoll) return; + try { + await api.post(`/meeting-planner/${editPoll.id}/options`, { options: [option] }); + message.success('Option added'); + // Refresh edit poll data + const { data } = await api.get(`/meeting-planner/${editPoll.id}`); + setEditPoll(data); + } catch { + message.error('Failed to add option'); + } + }; + + const handleRemoveOption = async (optionId: string) => { + if (!editPoll) return; + try { + await api.delete(`/meeting-planner/${editPoll.id}/options/${optionId}`); + message.success('Option removed'); + const { data } = await api.get(`/meeting-planner/${editPoll.id}`); + setEditPoll(data); + } catch { + message.error('Failed to remove option'); + } + }; + const copyShareLink = (slug: string) => { const url = `${window.location.origin}/poll/${slug}`; navigator.clipboard.writeText(url); @@ -274,6 +358,9 @@ export default function MeetingPlannerPage() { width: 140, render: (_, record) => ( + +