Add mobile poll responses viewer and inline vote success feedback

Mobile users could only see aggregate vote counts (7Y/0M/1N) but not
who voted what. Adds a collapsible "View Responses" panel with per-voter
breakdown and score summary. Also adds a persistent inline success Alert
after vote submission to complement the easy-to-miss toast notification.

Bunker Admin
This commit is contained in:
bunker-admin 2026-03-03 14:22:19 -07:00
parent 31e46af493
commit d528bc7816

View File

@ -15,6 +15,7 @@ import {
Space,
Divider,
Alert,
Collapse,
} from 'antd';
import {
CalendarOutlined,
@ -22,6 +23,7 @@ import {
EnvironmentOutlined,
CheckCircleOutlined,
SendOutlined,
EyeOutlined,
} from '@ant-design/icons';
import axios from 'axios';
import dayjs from 'dayjs';
@ -58,6 +60,7 @@ export default function SchedulingPollPage() {
const [votes, setVotes] = useState<Record<string, PollVoteValue>>({});
const [submitting, setSubmitting] = useState(false);
const [hasVoted, setHasVoted] = useState(false);
const [voteSuccess, setVoteSuccess] = useState(false);
// Comment form state
const [commentName, setCommentName] = useState(user?.name || '');
@ -98,6 +101,7 @@ export default function SchedulingPollPage() {
useEffect(() => { fetchPoll(); }, [fetchPoll]);
const handleVoteChange = (optionId: string, value: PollVoteValue) => {
setVoteSuccess(false);
setVotes((prev) => ({ ...prev, [optionId]: value }));
};
@ -128,6 +132,7 @@ export default function SchedulingPollPage() {
message.success(hasVoted ? 'Votes updated' : 'Votes submitted');
setHasVoted(true);
setVoteSuccess(true);
fetchPoll();
} catch (err: any) {
message.error(err.response?.data?.error?.message || 'Failed to submit votes');
@ -287,6 +292,66 @@ export default function SchedulingPollPage() {
)}
</Card>
))}
{/* Mobile: collapsible voter responses */}
{(poll.voters?.length ?? 0) > 0 && (
<Collapse
ghost
style={{ marginBottom: 12 }}
items={[{
key: 'responses',
label: (
<Space>
<EyeOutlined />
<span>View Responses ({poll.voters?.length})</span>
</Space>
),
children: (
<div>
{poll.voters?.map((voter, i) => (
<div
key={i}
style={{
padding: '10px 0',
borderBottom: i < (poll.voters?.length ?? 0) - 1 ? '1px solid #252525' : undefined,
}}
>
<Text strong style={{ display: 'block', marginBottom: 6 }}>{voter.name}</Text>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 4 }}>
{poll.options?.map((opt) => {
const value = voter.votes[opt.id] as PollVoteValue | undefined;
return value ? (
<Tag
key={opt.id}
color={value === 'YES' ? 'green' : value === 'IF_NEED_BE' ? 'gold' : 'default'}
style={{ fontSize: 11 }}
>
{dayjs(opt.date).format('M/D')} {VOTE_VALUE_LABELS[value]}
</Tag>
) : null;
})}
</div>
</div>
))}
{/* Score summary */}
<Divider style={{ margin: '8px 0' }} />
<Text strong style={{ display: 'block', marginBottom: 6 }}>Scores</Text>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
{poll.options?.map((opt) => (
<Tag
key={opt.id}
color={opt.score === bestScore && bestScore > 0 ? 'green' : 'default'}
style={{ fontSize: 11 }}
>
{dayjs(opt.date).format('M/D')}: {opt.score ?? 0}
</Tag>
))}
</div>
</div>
),
}]}
/>
)}
</div>
) : (
/* Desktop: voting matrix */
@ -437,6 +502,18 @@ export default function SchedulingPollPage() {
>
{hasVoted ? 'Update Votes' : 'Submit Votes'}
</Button>
{voteSuccess && (
<Alert
type="success"
message="Your votes have been recorded!"
showIcon
icon={<CheckCircleOutlined />}
style={{ marginTop: 12 }}
closable
onClose={() => setVoteSuccess(false)}
/>
)}
</Card>
)}