Add Falukant region and transport management features

- Implemented new endpoints in AdminController for managing Falukant regions, including fetching, updating, and deleting region distances.
- Enhanced the FalukantService with methods for retrieving region distances and handling upsert operations.
- Updated the router to expose new routes for region management and transport creation.
- Introduced a transport management interface in the frontend, allowing users to create and manage transports between branches.
- Added localization for new transport-related terms and improved the vehicle management interface to include transport options.
- Enhanced the database initialization logic to support new region and transport models.
This commit is contained in:
Torsten Schulz (local)
2025-11-26 16:44:27 +01:00
parent 29dd7ec80c
commit 06ea259dc9
27 changed files with 2100 additions and 57 deletions

View File

@@ -11,15 +11,18 @@
<div class="create-branch-form">
<div class="map-wrapper">
<!-- linke Spalte: Karte + Regionen + Dev-Draw -->
<div class="map-container">
<div
class="map-container"
@mousedown="onMouseDown"
@mousemove="onMouseMove"
@mouseup="onMouseUp"
@mouseleave="onMouseUp"
>
<img
ref="mapImage"
src="/images/falukant/map.png"
class="map"
@mousedown="onMouseDown"
@mousemove="onMouseMove"
@mouseup="onMouseUp"
@mouseleave="onMouseUp"
@load="onMapLoaded"
@dragstart.prevent
/>
<div
@@ -27,12 +30,7 @@
:key="city.name"
class="city-region"
:class="city.branches.length > 0 ? 'has-branch' : 'clickable'"
:style="{
top: city.map.y + 'px',
left: city.map.x + 'px',
width: city.map.w + 'px',
height: city.map.h + 'px'
}"
:style="cityRegionStyle(city.map)"
@click="city.branches.length === 0 && onCityClick(city)"
:title="city.name"
></div>
@@ -101,7 +99,9 @@
startX: null,
startY: null,
currentX: 0,
currentY: 0,
currentY: 0,
mapWidth: 0,
mapHeight: 0,
};
},
@@ -130,6 +130,11 @@
},
methods: {
onMapLoaded() {
const bounds = this.$refs.mapImage.getBoundingClientRect();
this.mapWidth = bounds.width;
this.mapHeight = bounds.height;
},
open() {
this.$refs.dialog.open();
},
@@ -149,6 +154,12 @@
this.$emit('create-branch');
this.close();
} catch (e) {
if (e?.response?.status === 412 && e?.response?.data?.error === 'insufficientFunds') {
alert(this.$t('falukant.branch.actions.insufficientFunds'));
} else {
console.error('Error creating branch', e);
alert(this.$t('falukant.error.generic') || 'Fehler beim Erstellen der Niederlassung.');
}
}
},
@@ -208,6 +219,21 @@
height: Math.round(height),
};
},
cityRegionStyle(map) {
if (!map || !this.mapWidth || !this.mapHeight) return {};
const toPxX = (v) => (v >= 0 && v <= 1 ? v * this.mapWidth : v);
const toPxY = (v) => (v >= 0 && v <= 1 ? v * this.mapHeight : v);
const x = toPxX(map.x);
const y = toPxY(map.y);
const w = toPxX(map.w);
const h = toPxY(map.h);
return {
top: `${y}px`,
left: `${x}px`,
width: `${w}px`,
height: `${h}px`,
};
},
async loadCities() {
const { data } = await apiClient.get('/api/falukant/cities');