feat(backend, frontend): Hinzufügen von Funktionen zur Verwaltung von Lagerbeständen im Falukant-System

- Implementierung von Methoden zur Hinzufügung und Abfrage von Lagerbeständen im AdminController und AdminService.
- Erweiterung der Routen im AdminRouter zur Unterstützung der neuen Lagerverwaltungsfunktionen.
- Anpassung der Benutzeroberfläche zur Integration eines Dialogs für die Lagerhinzufügung und zur Anzeige von Lagertypen.
- Aktualisierung der Übersetzungen in den Sprachdateien für die neuen Funktionen und Fehlermeldungen.
This commit is contained in:
Torsten Schulz (local)
2025-08-31 20:51:15 +02:00
parent 8a03c04668
commit 224503b660
9 changed files with 373 additions and 5 deletions

View File

@@ -15,6 +15,8 @@ class AdminController {
this.changeFalukantUser = this.changeFalukantUser.bind(this); this.changeFalukantUser = this.changeFalukantUser.bind(this);
this.getFalukantUserBranches = this.getFalukantUserBranches.bind(this); this.getFalukantUserBranches = this.getFalukantUserBranches.bind(this);
this.updateFalukantStock = this.updateFalukantStock.bind(this); this.updateFalukantStock = this.updateFalukantStock.bind(this);
this.addFalukantStock = this.addFalukantStock.bind(this);
this.getFalukantStockTypes = this.getFalukantStockTypes.bind(this);
this.getRoomTypes = this.getRoomTypes.bind(this); this.getRoomTypes = this.getRoomTypes.bind(this);
this.getGenderRestrictions = this.getGenderRestrictions.bind(this); this.getGenderRestrictions = this.getGenderRestrictions.bind(this);
this.getUserRights = this.getUserRights.bind(this); this.getUserRights = this.getUserRights.bind(this);
@@ -164,6 +166,29 @@ class AdminController {
} }
} }
async addFalukantStock(req, res) {
try {
const { userid: userId } = req.headers;
const { branchId, stockTypeId, quantity } = req.body;
const response = await AdminService.addFalukantStock(userId, branchId, stockTypeId, quantity);
res.status(200).json(response);
} catch (error) {
console.log(error);
res.status(403).json({ error: error.message });
}
}
async getFalukantStockTypes(req, res) {
try {
const { userid: userId } = req.headers;
const response = await AdminService.getFalukantStockTypes(userId);
res.status(200).json(response);
} catch (error) {
console.log(error);
res.status(403).json({ error: error.message });
}
}
async getRoomTypes(req, res) { async getRoomTypes(req, res) {
try { try {
const userId = req.headers.userid; const userId = req.headers.userid;

View File

@@ -26,6 +26,8 @@ router.get('/falukant/getuser/:id', authenticate, adminController.getFalukantUse
router.post('/falukant/edituser', authenticate, adminController.changeFalukantUser); router.post('/falukant/edituser', authenticate, adminController.changeFalukantUser);
router.get('/falukant/branches/:falukantUserId', authenticate, adminController.getFalukantUserBranches); router.get('/falukant/branches/:falukantUserId', authenticate, adminController.getFalukantUserBranches);
router.put('/falukant/stock/:stockId', authenticate, adminController.updateFalukantStock); router.put('/falukant/stock/:stockId', authenticate, adminController.updateFalukantStock);
router.post('/falukant/stock', authenticate, adminController.addFalukantStock);
router.get('/falukant/stock-types', authenticate, adminController.getFalukantStockTypes);
// --- Minigames Admin --- // --- Minigames Admin ---
router.get('/minigames/match3/campaigns', authenticate, adminController.getMatch3Campaigns); router.get('/minigames/match3/campaigns', authenticate, adminController.getMatch3Campaigns);

View File

@@ -313,6 +313,62 @@ class AdminService {
return stock; return stock;
} }
async addFalukantStock(userId, branchId, stockTypeId, quantity) {
if (!(await this.hasUserAccess(userId, 'falukantusers'))) {
throw new Error('noaccess');
}
// Prüfe ob Branch existiert
const branch = await Branch.findByPk(branchId);
if (!branch) {
throw new Error('Branch not found');
}
// Prüfe ob StockType existiert
const stockType = await FalukantStockType.findByPk(stockTypeId);
if (!stockType) {
throw new Error('Stock type not found');
}
// Prüfe ob bereits ein Stock dieses Typs für diesen Branch existiert
const existingStock = await FalukantStock.findOne({
where: {
branchId: branchId,
stockTypeId: stockTypeId
}
});
if (existingStock) {
throw new Error('Stock of this type already exists for this branch');
}
// Erstelle neuen Stock
const newStock = await FalukantStock.create({
branchId: branchId,
stockTypeId: stockTypeId,
quantity: quantity
});
// Lade den neuen Stock mit allen Beziehungen
const stockWithData = await FalukantStock.findByPk(newStock.id, {
include: [{ model: FalukantStockType, as: 'stockType', attributes: ['labelTr'] }]
});
return stockWithData;
}
async getFalukantStockTypes(userId) {
if (!(await this.hasUserAccess(userId, 'falukantusers'))) {
throw new Error('noaccess');
}
const stockTypes = await FalukantStockType.findAll({
attributes: ['id', 'labelTr']
});
return stockTypes;
}
async changeFalukantUser(userId, falukantUserId, falukantData) { async changeFalukantUser(userId, falukantUserId, falukantData) {
if (!(await this.hasUserAccess(userId, 'falukantusers'))) { if (!(await this.hasUserAccess(userId, 'falukantusers'))) {
throw new Error('noaccess'); throw new Error('noaccess');

57
deploy-frontend.sh Normal file
View File

@@ -0,0 +1,57 @@
#!/bin/bash
echo "=== YourPart Frontend Deployment ==="
# 1. Zum Frontend-Verzeichnis wechseln
cd ~/yourpart3/frontend
# 2. Berechtigungen für dist-Verzeichnis korrigieren (falls vorhanden)
if [ -d "dist" ]; then
echo "Korrigiere Berechtigungen für dist-Verzeichnis..."
sudo chown -R $USER:$USER dist/ 2>/dev/null || true
fi
# 3. Altes dist-Verzeichnis löschen
echo "Lösche altes dist-Verzeichnis..."
rm -rf dist/
# 4. Frontend neu bauen
echo "Baue Frontend neu..."
npm run build
if [ $? -ne 0 ]; then
echo "❌ Build fehlgeschlagen!"
exit 1
fi
echo "✅ Build erfolgreich!"
# 5. Zielverzeichnis erstellen (falls nicht vorhanden)
echo "Erstelle Zielverzeichnis..."
sudo mkdir -p /opt/yourpart/frontend/dist
# 6. Altes Frontend löschen
echo "Lösche altes Frontend..."
sudo rm -rf /opt/yourpart/frontend/dist/*
# 7. Neues Frontend kopieren
echo "Kopiere neues Frontend..."
sudo cp -r dist/* /opt/yourpart/frontend/dist/
# 8. Berechtigungen setzen
echo "Setze Berechtigungen..."
sudo chown -R www-data:www-data /opt/yourpart/frontend/dist
sudo chmod -R 755 /opt/yourpart/frontend/dist
# 9. Apache neu laden
echo "Lade Apache neu..."
sudo systemctl reload apache2
echo ""
echo "=== Frontend Deployment abgeschlossen! ==="
echo "✅ Frontend neu gebaut"
echo "✅ Frontend deployt"
echo "✅ Apache neu geladen"
echo ""
echo "Testen Sie jetzt die Anwendung!"
echo "API-Requests sollten jetzt an /api/... gehen statt localhost:3001"

View File

@@ -62,8 +62,16 @@
"branches": { "branches": {
"title": "Niederlassungen & Lager", "title": "Niederlassungen & Lager",
"noStocks": "Kein Lager vorhanden", "noStocks": "Kein Lager vorhanden",
"noBranches": "Keine Niederlassungen gefunden" "noBranches": "Keine Niederlassungen gefunden",
} "addStock": "Lager hinzufügen",
"stockType": "Lagertyp",
"selectStockType": "Lagertyp auswählen",
"quantity": "Menge"
},
"errorLoadingStockTypes": "Fehler beim Laden der Lagertypen.",
"errorAddingStock": "Fehler beim Hinzufügen des Lagers.",
"stockAdded": "Lager erfolgreich hinzugefügt.",
"invalidStockData": "Bitte gültige Lagertyp- und Mengenangaben eingeben."
} }
}, },
"chatrooms": { "chatrooms": {

View File

@@ -48,6 +48,8 @@
"create": "Erstellen", "create": "Erstellen",
"update": "Aktualisieren", "update": "Aktualisieren",
"save": "Speichern", "save": "Speichern",
"add": "Hinzufügen",
"cancel": "Abbrechen",
"yes": "Ja", "yes": "Ja",
"no": "Nein" "no": "Nein"
} }

View File

@@ -74,8 +74,16 @@
"branches": { "branches": {
"title": "Branches & Warehouse", "title": "Branches & Warehouse",
"noStocks": "No warehouse available", "noStocks": "No warehouse available",
"noBranches": "No branches found" "noBranches": "No branches found",
} "addStock": "Add Warehouse",
"stockType": "Warehouse Type",
"selectStockType": "Select warehouse type",
"quantity": "Quantity"
},
"errorLoadingStockTypes": "Error loading warehouse types.",
"errorAddingStock": "Error adding warehouse.",
"stockAdded": "Warehouse successfully added.",
"invalidStockData": "Please enter valid warehouse type and quantity."
} }
} }
} }

View File

@@ -15,6 +15,8 @@
"create": "Create", "create": "Create",
"update": "Update", "update": "Update",
"save": "Save", "save": "Save",
"add": "Add",
"cancel": "Cancel",
"yes": "Yes", "yes": "Yes",
"no": "No" "no": "No"
} }

View File

@@ -67,6 +67,11 @@
<div v-else class="no-stocks"> <div v-else class="no-stocks">
{{ $t('admin.falukant.edituser.branches.noStocks') }} {{ $t('admin.falukant.edituser.branches.noStocks') }}
</div> </div>
<div class="add-stock-section">
<button @click="openAddStockDialog(branch.id)" class="add-stock-btn">
{{ $t('admin.falukant.edituser.branches.addStock') }}
</button>
</div>
</div> </div>
</div> </div>
<div v-if="!userBranches.length" class="no-branches"> <div v-if="!userBranches.length" class="no-branches">
@@ -76,6 +81,36 @@
</div> </div>
</div> </div>
</div> </div>
<!-- Add Stock Dialog -->
<div v-if="showAddStockDialog" class="dialog-overlay" @click="closeAddStockDialog">
<div class="dialog" @click.stop>
<h3>{{ $t('admin.falukant.edituser.branches.addStock') }}</h3>
<div class="dialog-content">
<div class="form-group">
<label>{{ $t('admin.falukant.edituser.branches.stockType') }}:</label>
<select v-model="newStock.stockTypeId" class="form-select">
<option value="">{{ $t('admin.falukant.edituser.branches.selectStockType') }}</option>
<option v-for="stockType in stockTypes" :key="stockType.id" :value="stockType.id">
{{ $t(`falukant.branch.stocktype.${stockType.labelTr}`) }}
</option>
</select>
</div>
<div class="form-group">
<label>{{ $t('admin.falukant.edituser.branches.quantity') }}:</label>
<input type="number" v-model="newStock.quantity" min="1" class="form-input" />
</div>
</div>
<div class="dialog-actions">
<button @click="closeAddStockDialog" class="btn btn-secondary">
{{ $t('common.cancel') }}
</button>
<button @click="addStock" class="btn btn-primary">
{{ $t('common.add') }}
</button>
</div>
</div>
</div>
</div> </div>
</template> </template>
@@ -109,8 +144,16 @@ export default {
{ value: 'branches', label: 'admin.falukant.edituser.tabs.branches' } { value: 'branches', label: 'admin.falukant.edituser.tabs.branches' }
], ],
userBranches: [], userBranches: [],
stockTypes: [],
newStock: {
branchId: null,
stockTypeId: null,
quantity: 0
},
showAddStockDialog: false,
loading: { loading: {
branches: false branches: false,
stockTypes: false
} }
} }
}, },
@@ -120,6 +163,9 @@ export default {
async mounted() { async mounted() {
const titlesResult = await apiClient.get('/api/falukant/nobility/titels'); const titlesResult = await apiClient.get('/api/falukant/nobility/titels');
this.titles = titlesResult.data; this.titles = titlesResult.data;
// Lade Stock-Types
await this.loadStockTypes();
}, },
methods: { methods: {
async searchUser() { async searchUser() {
@@ -192,6 +238,55 @@ export default {
console.error('Error updating stock:', error); console.error('Error updating stock:', error);
this.$root.$refs.errorDialog.open('tr:admin.falukant.edituser.errorUpdatingStock'); this.$root.$refs.errorDialog.open('tr:admin.falukant.edituser.errorUpdatingStock');
} }
},
async loadStockTypes() {
this.loading.stockTypes = true;
try {
const stockTypesResult = await apiClient.get('/api/admin/falukant/stock-types');
this.stockTypes = stockTypesResult.data;
} catch (error) {
console.error('Error loading stock types:', error);
this.$root.$refs.errorDialog.open('tr:admin.falukant.edituser.errorLoadingStockTypes');
} finally {
this.loading.stockTypes = false;
}
},
openAddStockDialog(branchId) {
this.newStock.branchId = branchId;
this.newStock.stockTypeId = null;
this.newStock.quantity = 0;
this.showAddStockDialog = true;
},
closeAddStockDialog() {
this.showAddStockDialog = false;
this.newStock = {
branchId: null,
stockTypeId: null,
quantity: 0
};
},
async addStock() {
if (!this.newStock.stockTypeId || this.newStock.quantity <= 0) {
this.$root.$refs.errorDialog.open('tr:admin.falukant.edituser.invalidStockData');
return;
}
try {
await apiClient.post('/api/admin/falukant/stock', {
branchId: this.newStock.branchId,
stockTypeId: this.newStock.stockTypeId,
quantity: this.newStock.quantity
});
this.$root.$refs.messageDialog.open('tr:admin.falukant.edituser.stockAdded');
this.closeAddStockDialog();
// Lade Branches neu um den neuen Stock anzuzeigen
await this.loadUserBranches();
} catch (error) {
console.error('Error adding stock:', error);
this.$root.$refs.errorDialog.open('tr:admin.falukant.edituser.errorAddingStock');
}
} }
} }
} }
@@ -404,4 +499,117 @@ export default {
font-style: italic; font-style: italic;
padding: 20px; padding: 20px;
} }
/* Add Stock Dialog Styles */
.dialog-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
}
.dialog {
background: white;
border-radius: 8px;
padding: 20px;
min-width: 400px;
max-width: 500px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
}
.dialog h3 {
margin: 0 0 20px 0;
color: #333;
border-bottom: 1px solid #eee;
padding-bottom: 10px;
}
.dialog-content {
margin-bottom: 20px;
}
.form-group {
margin-bottom: 15px;
}
.form-group label {
display: block;
margin-bottom: 5px;
font-weight: bold;
color: #555;
}
.form-select, .form-input {
width: 100%;
padding: 8px 12px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 14px;
}
.form-select:focus, .form-input:focus {
outline: none;
border-color: #1976d2;
box-shadow: 0 0 0 2px rgba(25, 118, 210, 0.2);
}
.dialog-actions {
display: flex;
justify-content: flex-end;
gap: 10px;
}
.btn {
padding: 8px 16px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
font-weight: 500;
}
.btn-primary {
background: #1976d2;
color: white;
}
.btn-primary:hover {
background: #1565c0;
}
.btn-secondary {
background: #6c757d;
color: white;
}
.btn-secondary:hover {
background: #5a6268;
}
/* Add Stock Button */
.add-stock-section {
margin-top: 15px;
padding-top: 15px;
border-top: 1px solid #eee;
}
.add-stock-btn {
background: #28a745;
color: white;
border: none;
padding: 8px 16px;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
}
.add-stock-btn:hover {
background: #218838;
}
</style> </style>