Restructure citizen vote page + add vote deadline

Reorganize the citizen page (/commune/[slug]) for voters: full-width
interactive chart with "Population" zoom by default, separate auth and
vote sections, countdown timer for vote deadline.

Backend: add vote_deadline column to communes with Alembic migration.
Admin: add deadline configuration card with datetime-local input.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Yvv
2026-02-23 00:46:48 +01:00
parent 2af95ebcf1
commit 6caea1b809
7 changed files with 434 additions and 206 deletions

View File

@@ -29,6 +29,29 @@
</NuxtLink>
</div>
<!-- Vote deadline -->
<div class="card" style="margin-bottom: 2rem;">
<h3 style="margin-bottom: 1rem;">Parametres du vote</h3>
<div style="display: flex; gap: 1rem; align-items: flex-end; flex-wrap: wrap;">
<div>
<label style="display: block; font-size: 0.8rem; color: var(--color-text-muted); margin-bottom: 0.25rem;">
Date limite de vote
</label>
<input
v-model="voteDeadline"
type="datetime-local"
class="form-input"
style="width: 260px;"
/>
</div>
<button class="btn btn-primary" @click="saveDeadline" :disabled="savingDeadline">
<span v-if="savingDeadline" class="spinner" style="width: 1rem; height: 1rem;"></span>
Enregistrer
</button>
<span v-if="deadlineSaved" style="color: #059669; font-size: 0.85rem;">Enregistre !</span>
</div>
</div>
<!-- Stats -->
<div v-if="stats" class="card" style="margin-bottom: 2rem;">
<h3 style="margin-bottom: 1rem;">Statistiques foyers</h3>
@@ -145,6 +168,26 @@ const search = ref('')
const page = ref(1)
const perPage = 20
// Vote deadline
const voteDeadline = ref('')
const savingDeadline = ref(false)
const deadlineSaved = ref(false)
async function saveDeadline() {
savingDeadline.value = true
deadlineSaved.value = false
try {
await api.put(`/communes/${slug}`, {
vote_deadline: voteDeadline.value ? new Date(voteDeadline.value).toISOString() : null,
})
deadlineSaved.value = true
} catch (e: any) {
alert(e.message || 'Erreur')
} finally {
savingDeadline.value = false
}
}
const filteredHouseholds = computed(() => {
if (!search.value) return households.value
const q = search.value.toLowerCase()
@@ -173,6 +216,10 @@ function statusBadge(status: string) {
onMounted(async () => {
try {
commune.value = await api.get<any>(`/communes/${slug}`)
if (commune.value.vote_deadline) {
// Format for datetime-local input (YYYY-MM-DDTHH:mm)
voteDeadline.value = commune.value.vote_deadline.slice(0, 16)
}
} catch (e: any) {
return
}