Implement politics overview feature in FalukantService and update UI
- Added a new method `getPoliticsOverview` in FalukantService to retrieve currently held offices, including office holders and term end dates. - Enhanced the PoliticsView component to display the term end dates for current offices. - Updated localization files to include a new message for applying to selected positions. - Improved the handling of already applied positions in the open politics section, pre-selecting checkboxes accordingly.
This commit is contained in:
@@ -2472,7 +2472,112 @@ class FalukantService extends BaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async getPoliticsOverview(hashedUserId) {
|
async getPoliticsOverview(hashedUserId) {
|
||||||
|
// Liefert alle aktuell besetzten Ämter im eigenen Gebiet inklusive
|
||||||
|
// Inhaber und berechnetem Enddatum der Amtszeit.
|
||||||
|
const user = await getFalukantUserOrFail(hashedUserId);
|
||||||
|
|
||||||
|
// Charakter des Users bestimmen (Region ist dort hinterlegt)
|
||||||
|
const character = await FalukantCharacter.findOne({
|
||||||
|
where: { userId: user.id },
|
||||||
|
attributes: ['id', 'regionId']
|
||||||
|
});
|
||||||
|
if (!character) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Alle relevanten Regionen (Region + Eltern) laden
|
||||||
|
const relevantRegionIds = await this.getRegionAndParentIds(character.regionId);
|
||||||
|
|
||||||
|
// Aktuell besetzte Ämter in diesen Regionen laden
|
||||||
|
const offices = await PoliticalOffice.findAll({
|
||||||
|
where: {
|
||||||
|
regionId: {
|
||||||
|
[Op.in]: relevantRegionIds
|
||||||
|
}
|
||||||
|
},
|
||||||
|
include: [
|
||||||
|
{
|
||||||
|
model: PoliticalOfficeType,
|
||||||
|
as: 'type',
|
||||||
|
attributes: ['name', 'termLength']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: RegionData,
|
||||||
|
as: 'region',
|
||||||
|
attributes: ['name'],
|
||||||
|
include: [
|
||||||
|
{
|
||||||
|
model: RegionType,
|
||||||
|
as: 'regionType',
|
||||||
|
attributes: ['labelTr']
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: FalukantCharacter,
|
||||||
|
as: 'holder',
|
||||||
|
attributes: ['id', 'gender'],
|
||||||
|
include: [
|
||||||
|
{
|
||||||
|
model: FalukantPredefineFirstname,
|
||||||
|
as: 'definedFirstName',
|
||||||
|
attributes: ['name']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: FalukantPredefineLastname,
|
||||||
|
as: 'definedLastName',
|
||||||
|
attributes: ['name']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: TitleOfNobility,
|
||||||
|
as: 'nobleTitle',
|
||||||
|
attributes: ['labelTr']
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
order: [
|
||||||
|
[{ model: PoliticalOfficeType, as: 'type' }, 'name', 'ASC'],
|
||||||
|
[{ model: RegionData, as: 'region' }, 'name', 'ASC']
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
return offices.map(office => {
|
||||||
|
const o = office.get({ plain: true });
|
||||||
|
|
||||||
|
// Enddatum der Amtszeit berechnen: Start = createdAt, Dauer = termLength Jahre
|
||||||
|
let termEnds = null;
|
||||||
|
if (o.createdAt && o.type && typeof o.type.termLength === 'number') {
|
||||||
|
const start = new Date(o.createdAt);
|
||||||
|
if (!Number.isNaN(start.getTime())) {
|
||||||
|
const end = new Date(start);
|
||||||
|
end.setFullYear(end.getFullYear() + o.type.termLength);
|
||||||
|
termEnds = end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: o.id,
|
||||||
|
officeType: {
|
||||||
|
name: o.type?.name
|
||||||
|
},
|
||||||
|
region: {
|
||||||
|
name: o.region?.name,
|
||||||
|
regionType: o.region?.regionType
|
||||||
|
? { labelTr: o.region.regionType.labelTr }
|
||||||
|
: undefined
|
||||||
|
},
|
||||||
|
character: o.holder
|
||||||
|
? {
|
||||||
|
definedFirstName: o.holder.definedFirstName,
|
||||||
|
definedLastName: o.holder.definedLastName,
|
||||||
|
nobleTitle: o.holder.nobleTitle,
|
||||||
|
gender: o.holder.gender
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
termEnds
|
||||||
|
};
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async getOpenPolitics(hashedUserId) {
|
async getOpenPolitics(hashedUserId) {
|
||||||
@@ -2696,9 +2801,14 @@ class FalukantService extends BaseService {
|
|||||||
endDate: h.endDate
|
endDate: h.endDate
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
const alreadyApplied = (e.candidates || []).some(
|
||||||
|
c => c.characterId === characterId
|
||||||
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...e,
|
...e,
|
||||||
history: matchingHistory
|
history: matchingHistory,
|
||||||
|
alreadyApplied
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
@@ -640,7 +640,8 @@
|
|||||||
"region": "Region",
|
"region": "Region",
|
||||||
"date": "Datum",
|
"date": "Datum",
|
||||||
"candidacy": "Kandidatur",
|
"candidacy": "Kandidatur",
|
||||||
"none": "Keine offenen Positionen."
|
"none": "Keine offenen Positionen.",
|
||||||
|
"apply": "Für ausgewählte Positionen kandidieren"
|
||||||
},
|
},
|
||||||
"upcoming": {
|
"upcoming": {
|
||||||
"office": "Amt",
|
"office": "Amt",
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
<th>{{ $t('falukant.politics.current.office') }}</th>
|
<th>{{ $t('falukant.politics.current.office') }}</th>
|
||||||
<th>{{ $t('falukant.politics.current.region') }}</th>
|
<th>{{ $t('falukant.politics.current.region') }}</th>
|
||||||
<th>{{ $t('falukant.politics.current.holder') }}</th>
|
<th>{{ $t('falukant.politics.current.holder') }}</th>
|
||||||
|
<th>{{ $t('falukant.politics.current.termEnds') }}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@@ -31,9 +32,15 @@
|
|||||||
</span>
|
</span>
|
||||||
<span v-else>—</span>
|
<span v-else>—</span>
|
||||||
</td>
|
</td>
|
||||||
|
<td>
|
||||||
|
<span v-if="pos.termEnds">
|
||||||
|
{{ formatDate(pos.termEnds) }}
|
||||||
|
</span>
|
||||||
|
<span v-else>—</span>
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr v-if="!currentPositions.length">
|
<tr v-if="!currentPositions.length">
|
||||||
<td colspan="3">{{ $t('falukant.politics.current.none') }}</td>
|
<td colspan="4">{{ $t('falukant.politics.current.none') }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
@@ -60,8 +67,13 @@
|
|||||||
<td>{{ formatDate(e.date) }}</td>
|
<td>{{ formatDate(e.date) }}</td>
|
||||||
<!-- Checkbox ganz am Ende -->
|
<!-- Checkbox ganz am Ende -->
|
||||||
<td>
|
<td>
|
||||||
<input type="checkbox" :id="`apply-${e.id}`" v-model="selectedApplications"
|
<input
|
||||||
:value="e.id" />
|
type="checkbox"
|
||||||
|
:id="`apply-${e.id}`"
|
||||||
|
v-model="selectedApplications"
|
||||||
|
:value="e.id"
|
||||||
|
:disabled="e.alreadyApplied"
|
||||||
|
/>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr v-if="!openPolitics.length">
|
<tr v-if="!openPolitics.length">
|
||||||
@@ -222,7 +234,11 @@ export default {
|
|||||||
try {
|
try {
|
||||||
const { data } = await apiClient.get('/api/falukant/politics/open');
|
const { data } = await apiClient.get('/api/falukant/politics/open');
|
||||||
this.openPolitics = data;
|
this.openPolitics = data;
|
||||||
this.selectedApplications = [];
|
// Bereits beworbene Positionen vorselektieren, damit die Checkbox
|
||||||
|
// sichtbar markiert bleibt.
|
||||||
|
this.selectedApplications = data
|
||||||
|
.filter(e => e.alreadyApplied)
|
||||||
|
.map(e => e.id);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Error loading open politics', err);
|
console.error('Error loading open politics', err);
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
Reference in New Issue
Block a user