+ {/* Hero */}
+
+
+ {STRAW_POLL_TYPE_LABELS[poll.type]}
+
+ {STRAW_POLL_STATUS_LABELS[poll.status]}
+
+
+
{poll.title}
+ {poll.description &&
{poll.description}}
+ {poll.createdBy &&
by {poll.createdBy.name}}
+ {poll.closesAt && poll.status === 'ACTIVE' && (
+
+ Closes {dayjs(poll.closesAt).format('MMM D, YYYY h:mm A')}
+
+ )}
+
+
+ {/* Vote Form */}
+ {showVoteForm && (
+
+ Cast Your Vote
+
+ {poll.type === 'YES_NO_ABSTAIN' ? (
+
+ {poll.options?.map(opt => (
+
+ ))}
+
+ ) : (
+ setSelectedOption(e.target.value)}
+ style={{ width: '100%', marginBottom: 16 }}
+ >
+
+ {poll.options?.map(opt => (
+
+ {opt.label}
+
+ ))}
+
+
+ )}
+
+ {(poll.identityMode === 'ANONYMOUS' || poll.identityMode === 'MIXED') && !user && (
+ setVoterName(e.target.value)}
+ style={{ marginBottom: 16, maxWidth: 300 }}
+ />
+ )}
+
+
+
+ )}
+
+ {/* Already Voted */}
+ {hasVoted && poll.status === 'ACTIVE' && (
+
+
+ You've voted!
+ Your vote has been recorded.
+
+ )}
+
+ {/* Results */}
+ {showResults && poll.options && (
+
+ Results
+
+
+ )}
+
+ {!showResults && poll.resultVisibility !== 'LIVE' && poll.resultVisibility !== 'PUBLIC_ALWAYS' && (
+
+
+ Results will be visible {poll.resultVisibility === 'AFTER_VOTE' ? 'after you vote' : poll.resultVisibility === 'AFTER_CLOSE' ? 'when the poll closes' : ''}
+
+
+ )}
+
+ {/* Share */}
+
+ }
+ onClick={() => {
+ navigator.clipboard.writeText(window.location.href);
+ msg.success('Link copied!');
+ }}
+ >
+ Share This Poll
+
+
+
+ {/* Comments */}
+ {poll.allowComments && (
+ <>
+
Comments
+
+
+
+
+
+
+
+
+
+ {poll.comments && poll.comments.length > 0 ? (
+
(
+
+
+ {dayjs(comment.createdAt).format('MMM D, h:mm A')}
+
+ )}
+ />
+ ) : (
+ No comments yet.
+ )}
+ >
+ )}
+
+ );
+}
diff --git a/admin/src/pages/public/StrawPollsListPage.tsx b/admin/src/pages/public/StrawPollsListPage.tsx
new file mode 100644
index 00000000..8647b855
--- /dev/null
+++ b/admin/src/pages/public/StrawPollsListPage.tsx
@@ -0,0 +1,63 @@
+import { useState, useEffect } from 'react';
+import { Card, Row, Col, Tag, Typography, Spin, Empty, Grid } from 'antd';
+import { useNavigate } from 'react-router-dom';
+import axios from 'axios';
+import dayjs from 'dayjs';
+import relativeTime from 'dayjs/plugin/relativeTime';
+import type { StrawPoll } from '@/types/api';
+import { STRAW_POLL_TYPE_LABELS } from '@/types/api';
+
+dayjs.extend(relativeTime);
+
+const { Title, Text, Paragraph } = Typography;
+
+const apiBase = '/api';
+
+export default function StrawPollsListPage() {
+ const navigate = useNavigate();
+ const screens = Grid.useBreakpoint();
+ const isMobile = !screens.md;
+ const [polls, setPolls] = useState