feat(politics): implement reconcilePoliticalVacancies endpoint and UI integration for managing political vacancies

This commit is contained in:
Torsten Schulz (local)
2026-08-24 10:40:23 +02:00
parent d0f4e33fc0
commit 684880a32e
10 changed files with 136 additions and 11 deletions

View File

@@ -22,8 +22,15 @@
<button type="button" @click="requestSchedules">
{{ $t('admin.falukant.workerSchedules.refresh') }}
</button>
<button type="button" :disabled="reconcilingPolitics" @click="reconcilePoliticalVacancies">
{{ $t(reconcilingPolitics
? 'admin.falukant.workerSchedules.politicsReconcileLoading'
: 'admin.falukant.workerSchedules.politicsReconcile') }}
</button>
</div>
<p v-if="politicsResult" class="politics-result">{{ politicsResult }}</p>
<p v-if="generatedAt" class="generated-at">
{{ $t('admin.falukant.workerSchedules.generatedAt') }}: {{ formatTs(generatedAt) }}
</p>
@@ -79,6 +86,7 @@
<script>
import { mapState } from 'vuex';
import apiClient from '@/utils/axios.js';
export default {
name: 'AdminFalukantWorkerSchedulesView',
@@ -91,6 +99,8 @@ export default {
loading: false,
error: null,
timer: null,
reconcilingPolitics: false,
politicsResult: null,
};
},
computed: {
@@ -177,6 +187,26 @@ export default {
this.error = this.$t('admin.falukant.workerSchedules.sendError');
}
},
async reconcilePoliticalVacancies() {
if (!window.confirm(this.$t('admin.falukant.workerSchedules.politicsReconcileConfirm'))) return;
this.reconcilingPolitics = true;
this.politicsResult = null;
try {
const { data } = await apiClient.post('/api/admin/falukant/politics/reconcile-vacancies');
const count = Number(data?.createdCount || 0);
this.politicsResult = count
? this.$t('admin.falukant.workerSchedules.politicsReconcileSuccess', {
count,
date: new Date(data.electionDate).toLocaleString()
})
: this.$t('admin.falukant.workerSchedules.politicsReconcileNone');
} catch (err) {
this.politicsResult = err?.response?.data?.error
|| this.$t('admin.falukant.workerSchedules.politicsReconcileError');
} finally {
this.reconcilingPolitics = false;
}
},
handleDaemonMessage(evt) {
try {
const payload = JSON.parse(evt.data);
@@ -214,6 +244,7 @@ export default {
.toolbar { display: flex; gap: 12px; align-items: center; margin: 12px 0; flex-wrap: wrap; }
.toggle { display: inline-flex; gap: 6px; align-items: center; }
.generated-at { color: #666; margin-bottom: 8px; }
.politics-result { margin: 8px 0; color: #216b2f; }
.workers { display: grid; gap: 12px; }
.worker-card { border: 1px solid #ddd; border-radius: 8px; padding: 12px; background: #fff; }
.worker-card__header { display: flex; justify-content: space-between; gap: 12px; align-items: center; flex-wrap: wrap; }