Implement job hierarchy and region depth calculations in FalukantService; enhance PoliticsView with own position highlighting
- Added a job hierarchy mapping to determine positions based on their rank. - Introduced asynchronous region depth calculations to determine the hierarchy of regions. - Updated the mapping of office data to include job hierarchy levels and region depths. - Enhanced the PoliticsView to highlight the user's own positions with a distinct style. - Implemented a method to load the user's character ID for position comparison.
This commit is contained in:
@@ -4933,7 +4933,62 @@ class FalukantService extends BaseService {
|
|||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
return offices.map(office => {
|
// Job-Hierarchie-Ebene (höhere Zahl = höhere Position)
|
||||||
|
const jobHierarchy = {
|
||||||
|
'assessor': 1,
|
||||||
|
'councillor': 2,
|
||||||
|
'council': 3,
|
||||||
|
'beadle': 4,
|
||||||
|
'town-clerk': 4,
|
||||||
|
'mayor': 5,
|
||||||
|
'master-builder': 6,
|
||||||
|
'village-major': 7,
|
||||||
|
'judge': 8,
|
||||||
|
'bailif': 9,
|
||||||
|
'taxman': 10,
|
||||||
|
'sheriff': 11,
|
||||||
|
'consultant': 12,
|
||||||
|
'treasurer': 13,
|
||||||
|
'hangman': 12,
|
||||||
|
'territorial-council': 13,
|
||||||
|
'territorial-council-speaker': 14,
|
||||||
|
'ruler-consultant': 15,
|
||||||
|
'state-administrator': 16,
|
||||||
|
'super-state-administrator': 17,
|
||||||
|
'governor': 18,
|
||||||
|
'ministry-helper': 19,
|
||||||
|
'minister': 20,
|
||||||
|
'chancellor': 21
|
||||||
|
};
|
||||||
|
|
||||||
|
// Region-Hierarchie-Tiefe berechnen (0 = oberste Parent, höhere Zahl = tiefer)
|
||||||
|
const regionDepths = new Map();
|
||||||
|
const calculateRegionDepth = async (regionId) => {
|
||||||
|
if (regionDepths.has(regionId)) {
|
||||||
|
return regionDepths.get(regionId);
|
||||||
|
}
|
||||||
|
let depth = 0;
|
||||||
|
let currentId = regionId;
|
||||||
|
while (currentId !== null) {
|
||||||
|
const region = await RegionData.findByPk(currentId, {
|
||||||
|
attributes: ['parentId']
|
||||||
|
});
|
||||||
|
if (region && region.parentId) {
|
||||||
|
depth++;
|
||||||
|
currentId = region.parentId;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
regionDepths.set(regionId, depth);
|
||||||
|
return depth;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Alle Region-Tiefen parallel berechnen
|
||||||
|
const uniqueRegionIds = [...new Set(offices.map(o => o.regionId))];
|
||||||
|
await Promise.all(uniqueRegionIds.map(id => calculateRegionDepth(id)));
|
||||||
|
|
||||||
|
const mapped = offices.map(office => {
|
||||||
const o = office.get({ plain: true });
|
const o = office.get({ plain: true });
|
||||||
|
|
||||||
// Enddatum der Amtszeit berechnen: Start = createdAt, Dauer = termLength Jahre
|
// Enddatum der Amtszeit berechnen: Start = createdAt, Dauer = termLength Jahre
|
||||||
@@ -4953,20 +5008,64 @@ class FalukantService extends BaseService {
|
|||||||
name: o.type?.name
|
name: o.type?.name
|
||||||
},
|
},
|
||||||
region: {
|
region: {
|
||||||
|
id: o.region?.id,
|
||||||
name: o.region?.name,
|
name: o.region?.name,
|
||||||
regionType: o.region?.regionType
|
regionType: o.region?.regionType
|
||||||
? { labelTr: o.region.regionType.labelTr }
|
? { labelTr: o.region.regionType.labelTr }
|
||||||
: undefined
|
: undefined,
|
||||||
|
depth: regionDepths.get(o.region?.id) || 0
|
||||||
},
|
},
|
||||||
character: o.holder
|
character: o.holder
|
||||||
? {
|
? {
|
||||||
|
id: o.holder.id,
|
||||||
definedFirstName: o.holder.definedFirstName,
|
definedFirstName: o.holder.definedFirstName,
|
||||||
definedLastName: o.holder.definedLastName,
|
definedLastName: o.holder.definedLastName,
|
||||||
nobleTitle: o.holder.nobleTitle,
|
nobleTitle: o.holder.nobleTitle,
|
||||||
gender: o.holder.gender
|
gender: o.holder.gender
|
||||||
}
|
}
|
||||||
: null,
|
: null,
|
||||||
termEnds
|
termEnds,
|
||||||
|
jobHierarchyLevel: jobHierarchy[o.type?.name] || 0
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// Sortierung: 1. Region-Tiefe (aufsteigend, oberste Parent zuerst), 2. Job-Hierarchie (aufsteigend), 3. TermEnds (aufsteigend, frühere zuerst), 4. Vorname, 5. Nachname
|
||||||
|
mapped.sort((a, b) => {
|
||||||
|
// 1. Region-Tiefe (aufsteigend)
|
||||||
|
if (a.region.depth !== b.region.depth) {
|
||||||
|
return a.region.depth - b.region.depth;
|
||||||
|
}
|
||||||
|
// 2. Job-Hierarchie (aufsteigend)
|
||||||
|
if (a.jobHierarchyLevel !== b.jobHierarchyLevel) {
|
||||||
|
return a.jobHierarchyLevel - b.jobHierarchyLevel;
|
||||||
|
}
|
||||||
|
// 3. TermEnds (aufsteigend, frühere zuerst)
|
||||||
|
const termA = a.termEnds ? new Date(a.termEnds).getTime() : Infinity;
|
||||||
|
const termB = b.termEnds ? new Date(b.termEnds).getTime() : Infinity;
|
||||||
|
if (termA !== termB) {
|
||||||
|
return termA - termB;
|
||||||
|
}
|
||||||
|
// 4. Vorname
|
||||||
|
const firstNameA = a.character?.definedFirstName?.name || '';
|
||||||
|
const firstNameB = b.character?.definedFirstName?.name || '';
|
||||||
|
if (firstNameA !== firstNameB) {
|
||||||
|
return firstNameA.localeCompare(firstNameB);
|
||||||
|
}
|
||||||
|
// 5. Nachname
|
||||||
|
const lastNameA = a.character?.definedLastName?.name || '';
|
||||||
|
const lastNameB = b.character?.definedLastName?.name || '';
|
||||||
|
return lastNameA.localeCompare(lastNameB);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Entferne temporäre Felder vor der Rückgabe
|
||||||
|
return mapped.map(({ jobHierarchyLevel, ...rest }) => {
|
||||||
|
const { depth, id, ...regionRest } = rest.region;
|
||||||
|
return {
|
||||||
|
...rest,
|
||||||
|
region: {
|
||||||
|
name: regionRest.name,
|
||||||
|
regionType: regionRest.regionType
|
||||||
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr v-for="pos in currentPositions" :key="pos.id">
|
<tr v-for="pos in currentPositions" :key="pos.id" :class="{ 'own-position': isOwnPosition(pos) }">
|
||||||
<td>{{ $t(`falukant.politics.offices.${pos.officeType.name}`) }}</td>
|
<td>{{ $t(`falukant.politics.offices.${pos.officeType.name}`) }}</td>
|
||||||
<td>{{ pos.region.name }}</td>
|
<td>{{ pos.region.name }}</td>
|
||||||
<td>
|
<td>
|
||||||
@@ -193,6 +193,7 @@ export default {
|
|||||||
elections: [],
|
elections: [],
|
||||||
selectedCandidates: {},
|
selectedCandidates: {},
|
||||||
selectedApplications: [],
|
selectedApplications: [],
|
||||||
|
ownCharacterId: null,
|
||||||
loading: {
|
loading: {
|
||||||
current: false,
|
current: false,
|
||||||
openPolitics: false,
|
openPolitics: false,
|
||||||
@@ -209,7 +210,8 @@ export default {
|
|||||||
return this.elections.some(e => !e.voted);
|
return this.elections.some(e => !e.voted);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
async mounted() {
|
||||||
|
await this.loadOwnCharacterId();
|
||||||
this.loadCurrentPositions();
|
this.loadCurrentPositions();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
@@ -330,6 +332,24 @@ export default {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
async loadOwnCharacterId() {
|
||||||
|
try {
|
||||||
|
const { data } = await apiClient.get('/api/falukant/info');
|
||||||
|
if (data.character && data.character.id) {
|
||||||
|
this.ownCharacterId = data.character.id;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error loading own character ID', err);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
isOwnPosition(pos) {
|
||||||
|
if (!this.ownCharacterId || !pos.character) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return pos.character.id === this.ownCharacterId;
|
||||||
|
},
|
||||||
|
|
||||||
async submitApplications() {
|
async submitApplications() {
|
||||||
try {
|
try {
|
||||||
const response = await apiClient.post(
|
const response = await apiClient.post(
|
||||||
@@ -411,6 +431,11 @@ h2 {
|
|||||||
border: 1px solid #ddd;
|
border: 1px solid #ddd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.politics-table tbody tr.own-position {
|
||||||
|
background-color: #e0e0e0;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
.loading {
|
.loading {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
|
|||||||
Reference in New Issue
Block a user