Refactor backend CORS settings to include default origins and improve error handling in chat services: Introduce dynamic CORS origin handling, enhance RabbitMQ message sending with fallback mechanisms, and update WebSocket service to manage pending messages. Update UI components for better accessibility and responsiveness, including adjustments to dialog and navigation elements. Enhance styling for improved user experience across various components.
This commit is contained in:
@@ -10,6 +10,36 @@
|
||||
|
||||
<!-- Match3 Levels Tab -->
|
||||
<div v-if="activeTab === 'match3-levels'" class="match3-admin">
|
||||
<section class="workflow-hero surface-card">
|
||||
<div>
|
||||
<span class="workflow-hero__eyebrow">Arbeitsfluss</span>
|
||||
<h2>{{ $t('admin.match3.title') }}</h2>
|
||||
<p>Erst Level waehlen, dann Spielfeld und Ziele anpassen und erst am Ende speichern.</p>
|
||||
</div>
|
||||
<div class="workflow-hero__meta">
|
||||
<span class="workflow-pill">{{ currentModeLabel }}</span>
|
||||
<span class="workflow-pill">{{ levels.length }} Level</span>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="workflow-grid">
|
||||
<article class="workflow-card surface-card">
|
||||
<span class="workflow-card__step">1</span>
|
||||
<h3>Level waehlen</h3>
|
||||
<p>Bestehendes Level oeffnen oder sofort mit einer neuen Vorlage starten.</p>
|
||||
</article>
|
||||
<article class="workflow-card surface-card">
|
||||
<span class="workflow-card__step">2</span>
|
||||
<h3>Spielfeld bauen</h3>
|
||||
<p>Groesse, Zuege, Kacheln und Layout zuerst festziehen, bevor Ziele folgen.</p>
|
||||
</article>
|
||||
<article class="workflow-card surface-card">
|
||||
<span class="workflow-card__step">3</span>
|
||||
<h3>Ziele speichern</h3>
|
||||
<p>Objectives nur dann scharf stellen, wenn Grunddaten und Board bereits stimmen.</p>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
<div class="section-header">
|
||||
<h2>{{ $t('admin.match3.title') }}</h2>
|
||||
</div>
|
||||
@@ -31,13 +61,38 @@
|
||||
{{ $t('admin.match3.levelFormat', { number: level.order, name: level.name }) }}
|
||||
</option>
|
||||
</select>
|
||||
<button type="button" class="btn btn-secondary level-select-action" @click="createLevel">
|
||||
{{ $t('admin.match3.newLevel') }}
|
||||
</button>
|
||||
</div>
|
||||
<p class="level-selection__hint">
|
||||
{{ isCreatingLevel ? 'Du erstellst gerade ein neues Level.' : 'Du bearbeitest ein bestehendes Level mit allen verbundenen Objectives.' }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<section class="admin-summary-grid">
|
||||
<article class="admin-summary-card surface-card">
|
||||
<span class="admin-summary-card__label">Modus</span>
|
||||
<strong>{{ currentModeLabel }}</strong>
|
||||
<p>{{ selectedLevel ? selectedLevel.name : 'Neue Vorlage mit leerem Spielfeld' }}</p>
|
||||
</article>
|
||||
<article class="admin-summary-card surface-card">
|
||||
<span class="admin-summary-card__label">Spielfeld</span>
|
||||
<strong>{{ levelForm.boardWidth }} x {{ levelForm.boardHeight }}</strong>
|
||||
<p>{{ levelForm.moveLimit }} Zuege, {{ levelForm.tileTypes.length }} aktive Tile-Typen.</p>
|
||||
</article>
|
||||
<article class="admin-summary-card surface-card">
|
||||
<span class="admin-summary-card__label">Objectives</span>
|
||||
<strong>{{ objectiveCount }}</strong>
|
||||
<p>{{ objectiveCount ? 'Ziele vorhanden und bearbeitbar.' : 'Noch keine Zieldefinition hinterlegt.' }}</p>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
<!-- Level Details -->
|
||||
<div v-if="selectedLevelId !== 'new' && selectedLevel" class="level-details">
|
||||
<div class="details-header">
|
||||
<h3>{{ selectedLevel.name }}</h3>
|
||||
<p>Bestehendes Level anpassen, ohne den Kontext des aktuellen Spielflusses zu verlieren.</p>
|
||||
</div>
|
||||
<div class="details-content">
|
||||
<div class="form-group">
|
||||
@@ -185,7 +240,7 @@
|
||||
<button type="button" class="btn btn-danger" @click="deleteSelectedLevel">
|
||||
{{ $t('admin.match3.delete') }}
|
||||
</button>
|
||||
<button type="button" class="btn btn-primary" @click="saveLevel">
|
||||
<button type="button" class="btn btn-primary" :disabled="!isLevelFormValid" @click="saveLevel">
|
||||
{{ $t('admin.match3.update') }}
|
||||
</button>
|
||||
</div>
|
||||
@@ -539,7 +594,7 @@
|
||||
<button type="button" class="btn btn-secondary" @click="cancelEdit">
|
||||
{{ $t('admin.match3.cancel') }}
|
||||
</button>
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<button type="submit" class="btn btn-primary" :disabled="!isLevelFormValid">
|
||||
{{ $t('admin.match3.create') }}
|
||||
</button>
|
||||
</div>
|
||||
@@ -553,6 +608,7 @@
|
||||
<script>
|
||||
import SimpleTabs from '../../components/SimpleTabs.vue';
|
||||
import apiClient from '../../utils/axios.js';
|
||||
import { showError, showSuccess } from '@/utils/feedback.js';
|
||||
|
||||
export default {
|
||||
name: 'AdminMinigamesView',
|
||||
@@ -593,10 +649,29 @@ export default {
|
||||
gridTemplateRows: `repeat(${this.levelForm.boardHeight}, 1fr)`
|
||||
};
|
||||
},
|
||||
|
||||
isCreatingLevel() {
|
||||
return this.selectedLevelId === 'new';
|
||||
},
|
||||
selectedLevel() {
|
||||
if (this.selectedLevelId === 'new') return null;
|
||||
return this.levels.find(l => l.id === this.selectedLevelId);
|
||||
},
|
||||
objectiveCount() {
|
||||
return this.levelForm.objectives?.length || 0;
|
||||
},
|
||||
currentModeLabel() {
|
||||
return this.isCreatingLevel ? 'Neues Level' : 'Level bearbeiten';
|
||||
},
|
||||
isLevelFormValid() {
|
||||
return Boolean(
|
||||
this.levelForm.name?.trim() &&
|
||||
this.levelForm.description?.trim() &&
|
||||
this.levelForm.boardWidth >= 3 &&
|
||||
this.levelForm.boardHeight >= 3 &&
|
||||
this.levelForm.moveLimit >= 5 &&
|
||||
this.levelForm.order >= 1 &&
|
||||
this.levelForm.tileTypes?.length
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -730,20 +805,14 @@ export default {
|
||||
},
|
||||
|
||||
setTileType(index, tileType) {
|
||||
console.log('setTileType called with:', index, tileType);
|
||||
if (tileType === 'o') {
|
||||
// Leer
|
||||
this.boardMatrix[index] = { active: false, tileType: 'o', index: index };
|
||||
} else if (tileType === 'r') {
|
||||
// Zufällig
|
||||
this.boardMatrix[index] = { active: true, tileType: 'r', index: index };
|
||||
console.log('Set random tile at index:', index, this.boardMatrix[index]);
|
||||
} else {
|
||||
// Spezifischer Tile-Typ
|
||||
this.boardMatrix[index] = { active: true, tileType: tileType, index: index };
|
||||
}
|
||||
this.selectedCellIndex = null; // Auswahl aufheben
|
||||
console.log('Board matrix after update:', this.boardMatrix);
|
||||
this.selectedCellIndex = null;
|
||||
},
|
||||
|
||||
// Mapping für Tile-Typen zu Zeichen
|
||||
@@ -785,7 +854,6 @@ export default {
|
||||
objectives: []
|
||||
};
|
||||
this.updateBoardMatrix();
|
||||
console.log('Bearbeitung abgebrochen, Objectives zurückgesetzt:', this.levelForm.objectives);
|
||||
},
|
||||
|
||||
updateBoardMatrix() {
|
||||
@@ -905,6 +973,7 @@ export default {
|
||||
...this.levelForm,
|
||||
boardLayout: this.generateBoardLayout()
|
||||
};
|
||||
const wasCreating = this.selectedLevelId === 'new';
|
||||
|
||||
let savedLevel;
|
||||
if (this.selectedLevelId !== 'new') {
|
||||
@@ -939,9 +1008,10 @@ export default {
|
||||
this.selectedLevelId = 'new';
|
||||
this.selectedCellIndex = null;
|
||||
this.loadLevels();
|
||||
showSuccess(this, wasCreating ? 'Level wurde erstellt.' : 'Level wurde aktualisiert.');
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Speichern des Levels:', error);
|
||||
alert('Fehler beim Speichern des Levels');
|
||||
showError(this, 'Fehler beim Speichern des Levels');
|
||||
}
|
||||
},
|
||||
|
||||
@@ -950,8 +1020,10 @@ export default {
|
||||
try {
|
||||
await apiClient.delete(`/api/admin/minigames/match3/levels/${levelId}`);
|
||||
this.loadLevels();
|
||||
showSuccess(this, 'Level wurde geloescht.');
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Löschen des Levels:', error);
|
||||
showError(this, 'Fehler beim Loeschen des Levels');
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -1025,6 +1097,94 @@ export default {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.workflow-hero {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
justify-content: space-between;
|
||||
gap: 18px;
|
||||
padding: 22px 24px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.workflow-hero h2 {
|
||||
margin: 0 0 8px;
|
||||
}
|
||||
|
||||
.workflow-hero p {
|
||||
margin: 0;
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
.workflow-hero__eyebrow,
|
||||
.admin-summary-card__label {
|
||||
display: inline-flex;
|
||||
margin-bottom: 4px;
|
||||
color: var(--color-text-muted);
|
||||
font-size: 0.76rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.workflow-hero__meta {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.workflow-pill {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
min-height: 34px;
|
||||
padding: 0 12px;
|
||||
border-radius: 999px;
|
||||
background: rgba(248, 162, 43, 0.12);
|
||||
color: #8a5411;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.workflow-grid,
|
||||
.admin-summary-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: 14px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.workflow-card,
|
||||
.admin-summary-card {
|
||||
padding: 18px;
|
||||
}
|
||||
|
||||
.workflow-card__step {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
margin-bottom: 10px;
|
||||
border-radius: 999px;
|
||||
background: rgba(248, 162, 43, 0.12);
|
||||
color: #8a5411;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.workflow-card h3,
|
||||
.admin-summary-card strong {
|
||||
margin: 0 0 8px;
|
||||
}
|
||||
|
||||
.admin-summary-card strong {
|
||||
display: block;
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.workflow-card p,
|
||||
.admin-summary-card p {
|
||||
margin: 0;
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
.section-header {
|
||||
margin-bottom: 30px;
|
||||
text-align: center;
|
||||
@@ -1055,6 +1215,8 @@ export default {
|
||||
.level-dropdown {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 10px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.level-select {
|
||||
@@ -1072,6 +1234,15 @@ export default {
|
||||
border-color: #F9A22C;
|
||||
}
|
||||
|
||||
.level-select-action {
|
||||
min-width: 180px;
|
||||
}
|
||||
|
||||
.level-selection__hint {
|
||||
margin: 12px 0 0;
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
/* Level Details & Form */
|
||||
.level-details,
|
||||
.level-form {
|
||||
@@ -1096,6 +1267,11 @@ export default {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.details-header p {
|
||||
margin: 8px 0 0;
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
@@ -1479,6 +1655,16 @@ export default {
|
||||
.match3-admin {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.workflow-hero {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.workflow-grid,
|
||||
.admin-summary-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.form-row {
|
||||
grid-template-columns: 1fr;
|
||||
|
||||
Reference in New Issue
Block a user