Refactor SaleSection component: Simplify sell item and sell all logic, remove unnecessary state management, and improve UI feedback. Update translations and clean up unused code in i18n files. Optimize price loading in BranchView and remove legacy product loading in MoneyHistoryView. Streamline PoliticsView by removing own character ID handling and related logic.

This commit is contained in:
Torsten Schulz (local)
2026-01-26 16:03:48 +01:00
parent 80b639b511
commit 71748f6aa0
15 changed files with 822 additions and 3815 deletions

View File

@@ -572,49 +572,27 @@ export default {
return;
}
if (!this.products || this.products.length === 0) {
this.productPricesCache = {};
return;
}
// OPTIMIERUNG: Lade alle Preise in einem Batch-Request
try {
const productIds = this.products.map(p => p.id).join(',');
const { data } = await apiClient.get('/api/falukant/products/prices-in-region-batch', {
params: {
productIds: productIds,
regionId: this.selectedBranch.regionId
}
});
this.productPricesCache = data || {};
} catch (error) {
console.error('Error loading prices in batch:', error);
// Fallback: Lade Preise einzeln (aber parallel)
const pricePromises = this.products.map(async (product) => {
try {
const { data } = await apiClient.get('/api/falukant/products/price-in-region', {
params: {
productId: product.id,
regionId: this.selectedBranch.regionId
}
});
return { productId: product.id, price: data.price };
} catch (err) {
console.error(`Error loading price for product ${product.id}:`, err);
// Fallback auf Standard-Berechnung
const knowledgeFactor = product.knowledges?.[0]?.knowledge || 0;
const maxPrice = product.sellCost;
const minPrice = maxPrice * 0.6;
return { productId: product.id, price: minPrice + (maxPrice - minPrice) * (knowledgeFactor / 100) };
}
});
const results = await Promise.all(pricePromises);
this.productPricesCache = {};
results.forEach(({ productId, price }) => {
this.productPricesCache[productId] = price;
});
// Lade Preise für alle Produkte in der aktuellen Region
const prices = {};
for (const product of this.products) {
try {
const { data } = await apiClient.get('/api/falukant/products/price-in-region', {
params: {
productId: product.id,
regionId: this.selectedBranch.regionId
}
});
prices[product.id] = data.price;
} catch (error) {
console.error(`Error loading price for product ${product.id}:`, error);
// Fallback auf Standard-Berechnung
const knowledgeFactor = product.knowledges?.[0]?.knowledge || 0;
const maxPrice = product.sellCost;
const minPrice = maxPrice * 0.6;
prices[product.id] = minPrice + (maxPrice - minPrice) * (knowledgeFactor / 100);
}
}
this.productPricesCache = prices;
},
formatPercent(value) {
@@ -714,10 +692,7 @@ export default {
},
conditionLabel(value) {
// 0 ist ein gültiger Zustand (z.B. komplett kaputt) und darf nicht als "Unbekannt" enden.
if (value === null || value === undefined) return 'Unbekannt';
const v = Number(value);
if (!Number.isFinite(v)) return 'Unbekannt';
const v = Number(value) || 0;
if (v >= 95) return 'Ausgezeichnet'; // 95100
if (v >= 72) return 'Sehr gut'; // 7294
if (v >= 54) return 'Gut'; // 5471
@@ -725,7 +700,7 @@ export default {
if (v >= 22) return 'Schlecht'; // 2238
if (v >= 6) return 'Sehr schlecht'; // 621
if (v >= 1) return 'Katastrophal'; // 15
return 'Katastrophal'; // 0 oder kleiner
return 'Unbekannt';
},
speedLabel(value) {
@@ -1039,15 +1014,12 @@ export default {
});
await this.loadVehicles();
this.closeRepairAllVehiclesDialog();
// Statt JS-alert: Dialog schließen und MessageDialog anzeigen
this.$root.$refs.messageDialog?.open('tr:falukant.branch.transport.repairAllSuccess');
alert(this.$t('falukant.branch.transport.repairAllSuccess'));
this.$refs.statusBar?.fetchStatus();
} catch (error) {
console.error('Error repairing all vehicles:', error);
const errorMessage = error.response?.data?.message || this.$t('falukant.branch.transport.repairAllError');
// Bestätigungsdialog ebenfalls schließen und Fehler im MessageDialog anzeigen
this.closeRepairAllVehiclesDialog();
this.$root.$refs.messageDialog?.open(String(errorMessage), this.$t('error.title'));
alert(errorMessage);
}
},
@@ -1104,15 +1076,12 @@ export default {
await apiClient.post(`/api/falukant/vehicles/${this.repairVehicleDialog.vehicle.id}/repair`);
await this.loadVehicles();
this.closeRepairVehicleDialog();
// Statt JS-alert: Dialog schließen und MessageDialog anzeigen
this.$root.$refs.messageDialog?.open('tr:falukant.branch.transport.repairSuccess');
alert(this.$t('falukant.branch.transport.repairSuccess'));
this.$refs.statusBar?.fetchStatus();
} catch (error) {
console.error('Error repairing vehicle:', error);
const errorMessage = error.response?.data?.message || this.$t('falukant.branch.transport.repairError');
// Bestätigungsdialog ebenfalls schließen und Fehler im MessageDialog anzeigen
this.closeRepairVehicleDialog();
this.$root.$refs.messageDialog?.open(String(errorMessage), this.$t('error.title'));
alert(errorMessage);
}
},
},

View File

@@ -67,28 +67,12 @@ export default {
currentPage: 1,
totalPages: 1,
},
productsById: {},
};
},
async mounted() {
await Promise.all([this.loadProducts(), this.fetchMoneyHistory(1)]);
await this.fetchMoneyHistory(1);
},
methods: {
async loadProducts() {
try {
const { data } = await apiClient.get('/api/falukant/products');
const map = {};
for (const p of (data || [])) {
if (p && p.id != null && p.labelTr) {
map[String(p.id)] = p.labelTr;
}
}
this.productsById = map;
} catch (e) {
console.error('Error loading products for money history', e);
this.productsById = {};
}
},
async fetchMoneyHistory(page) {
try {
const response = await apiClient.post('/api/falukant/moneyhistory', {
@@ -101,25 +85,6 @@ export default {
}
},
translateActivity(activity) {
try {
const raw = String(activity ?? '');
// Handle legacy format: "tax from sale product 3"
const m = raw.match(/^tax\s+from\s+sale\s+product\s+(\d+)$/i);
if (m && m[1]) {
const id = m[1];
const labelTr = this.productsById[String(id)];
const productName = labelTr ? this.$t(`falukant.product.${labelTr}`) : `#${id}`;
return this.$t('falukant.moneyHistory.activities.taxFromSaleProduct', { product: productName });
}
// New/structured format: "taxFromSaleProduct.<labelTr>"
if (raw.startsWith('taxFromSaleProduct.')) {
const labelTr = raw.substring('taxFromSaleProduct.'.length);
const productName = labelTr ? this.$t(`falukant.product.${labelTr}`) : labelTr;
return this.$t('falukant.moneyHistory.activities.taxFromSaleProduct', { product: productName });
}
} catch (_) {
// ignore and fall back
}
// Handle nested keys like "health.pill" -> "health.pill"
const key = `falukant.moneyHistory.activities.${activity}`;
const translation = this.$t(key);

View File

@@ -23,7 +23,7 @@
</tr>
</thead>
<tbody>
<tr v-for="pos in currentPositions" :key="pos.id" :class="{ 'own-position': isOwnPosition(pos) }">
<tr v-for="pos in currentPositions" :key="pos.id">
<td>{{ $t(`falukant.politics.offices.${pos.officeType.name}`) }}</td>
<td>{{ pos.region.name }}</td>
<td>
@@ -193,7 +193,6 @@ export default {
elections: [],
selectedCandidates: {},
selectedApplications: [],
ownCharacterId: null,
loading: {
current: false,
openPolitics: false,
@@ -210,8 +209,7 @@ export default {
return this.elections.some(e => !e.voted);
}
},
async mounted() {
await this.loadOwnCharacterId();
mounted() {
this.loadCurrentPositions();
},
methods: {
@@ -332,24 +330,6 @@ 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() {
try {
const response = await apiClient.post(
@@ -431,11 +411,6 @@ h2 {
border: 1px solid #ddd;
}
.politics-table tbody tr.own-position {
background-color: #e0e0e0;
font-weight: bold;
}
.loading {
text-align: center;
font-style: italic;