diff --git a/backend/controllers/adminController.js b/backend/controllers/adminController.js index 71da7bf..7b8c063 100644 --- a/backend/controllers/adminController.js +++ b/backend/controllers/adminController.js @@ -15,6 +15,8 @@ class AdminController { this.changeFalukantUser = this.changeFalukantUser.bind(this); this.getFalukantUserBranches = this.getFalukantUserBranches.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.getGenderRestrictions = this.getGenderRestrictions.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) { try { const userId = req.headers.userid; diff --git a/backend/routers/adminRouter.js b/backend/routers/adminRouter.js index 302d24e..ca15a61 100644 --- a/backend/routers/adminRouter.js +++ b/backend/routers/adminRouter.js @@ -26,6 +26,8 @@ router.get('/falukant/getuser/:id', authenticate, adminController.getFalukantUse router.post('/falukant/edituser', authenticate, adminController.changeFalukantUser); router.get('/falukant/branches/:falukantUserId', authenticate, adminController.getFalukantUserBranches); 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 --- router.get('/minigames/match3/campaigns', authenticate, adminController.getMatch3Campaigns); diff --git a/backend/services/adminService.js b/backend/services/adminService.js index b040495..8514f21 100644 --- a/backend/services/adminService.js +++ b/backend/services/adminService.js @@ -313,6 +313,62 @@ class AdminService { 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) { if (!(await this.hasUserAccess(userId, 'falukantusers'))) { throw new Error('noaccess'); diff --git a/deploy-frontend.sh b/deploy-frontend.sh new file mode 100644 index 0000000..2e59df5 --- /dev/null +++ b/deploy-frontend.sh @@ -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" diff --git a/frontend/src/i18n/locales/de/admin.json b/frontend/src/i18n/locales/de/admin.json index bd50a6b..d020f17 100644 --- a/frontend/src/i18n/locales/de/admin.json +++ b/frontend/src/i18n/locales/de/admin.json @@ -62,8 +62,16 @@ "branches": { "title": "Niederlassungen & Lager", "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": { diff --git a/frontend/src/i18n/locales/de/general.json b/frontend/src/i18n/locales/de/general.json index 0271f4d..07d7632 100644 --- a/frontend/src/i18n/locales/de/general.json +++ b/frontend/src/i18n/locales/de/general.json @@ -48,6 +48,8 @@ "create": "Erstellen", "update": "Aktualisieren", "save": "Speichern", + "add": "Hinzufügen", + "cancel": "Abbrechen", "yes": "Ja", "no": "Nein" } diff --git a/frontend/src/i18n/locales/en/admin.json b/frontend/src/i18n/locales/en/admin.json index 5ac56e9..ee1cfcf 100644 --- a/frontend/src/i18n/locales/en/admin.json +++ b/frontend/src/i18n/locales/en/admin.json @@ -74,8 +74,16 @@ "branches": { "title": "Branches & Warehouse", "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." } } } diff --git a/frontend/src/i18n/locales/en/general.json b/frontend/src/i18n/locales/en/general.json index a357264..781699e 100644 --- a/frontend/src/i18n/locales/en/general.json +++ b/frontend/src/i18n/locales/en/general.json @@ -15,6 +15,8 @@ "create": "Create", "update": "Update", "save": "Save", + "add": "Add", + "cancel": "Cancel", "yes": "Yes", "no": "No" } diff --git a/frontend/src/views/admin/falukant/EditUserView.vue b/frontend/src/views/admin/falukant/EditUserView.vue index 2747572..8bf1691 100644 --- a/frontend/src/views/admin/falukant/EditUserView.vue +++ b/frontend/src/views/admin/falukant/EditUserView.vue @@ -67,6 +67,11 @@
{{ $t('admin.falukant.edituser.branches.noStocks') }}
+
+ +
@@ -76,6 +81,36 @@
+ + +
+
+

{{ $t('admin.falukant.edituser.branches.addStock') }}

+
+
+ + +
+
+ + +
+
+
+ + +
+
+
@@ -109,8 +144,16 @@ export default { { value: 'branches', label: 'admin.falukant.edituser.tabs.branches' } ], userBranches: [], + stockTypes: [], + newStock: { + branchId: null, + stockTypeId: null, + quantity: 0 + }, + showAddStockDialog: false, loading: { - branches: false + branches: false, + stockTypes: false } } }, @@ -120,6 +163,9 @@ export default { async mounted() { const titlesResult = await apiClient.get('/api/falukant/nobility/titels'); this.titles = titlesResult.data; + + // Lade Stock-Types + await this.loadStockTypes(); }, methods: { async searchUser() { @@ -192,6 +238,55 @@ export default { console.error('Error updating stock:', error); 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; 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; +} \ No newline at end of file