Add node-fetch dependency and implement meeting report submission endpoint
This commit introduces the node-fetch package to facilitate HTTP requests in the backend. Additionally, a new API endpoint for submitting meeting reports has been implemented in the nuscoreApiRoutes. The endpoint handles report data submission, cookie management, and error handling, enhancing the functionality of the meeting report feature. Frontend components have been updated to support this new functionality, improving the overall user experience.
This commit is contained in:
91
backend/node_modules/.package-lock.json
generated
vendored
91
backend/node_modules/.package-lock.json
generated
vendored
@@ -1318,6 +1318,15 @@
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/data-uri-to-buffer": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz",
|
||||
"integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 12"
|
||||
}
|
||||
},
|
||||
"node_modules/date-fns": {
|
||||
"version": "2.30.0",
|
||||
"license": "MIT",
|
||||
@@ -1886,6 +1895,29 @@
|
||||
"reusify": "^1.0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/fetch-blob": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz",
|
||||
"integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/jimmywarting"
|
||||
},
|
||||
{
|
||||
"type": "paypal",
|
||||
"url": "https://paypal.me/jimmywarting"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"node-domexception": "^1.0.0",
|
||||
"web-streams-polyfill": "^3.0.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^12.20 || >= 14.13"
|
||||
}
|
||||
},
|
||||
"node_modules/file-entry-cache": {
|
||||
"version": "8.0.0",
|
||||
"dev": true,
|
||||
@@ -2004,6 +2036,18 @@
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/formdata-polyfill": {
|
||||
"version": "4.0.10",
|
||||
"resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz",
|
||||
"integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fetch-blob": "^3.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/formidable": {
|
||||
"version": "3.5.4",
|
||||
"dev": true,
|
||||
@@ -3155,10 +3199,48 @@
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/node-domexception": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
|
||||
"integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==",
|
||||
"deprecated": "Use your platform's native DOMException instead",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/jimmywarting"
|
||||
},
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://paypal.me/jimmywarting"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10.5.0"
|
||||
}
|
||||
},
|
||||
"node_modules/node-ensure": {
|
||||
"version": "0.0.0",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/node-fetch": {
|
||||
"version": "3.3.2",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz",
|
||||
"integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"data-uri-to-buffer": "^4.0.0",
|
||||
"fetch-blob": "^3.1.4",
|
||||
"formdata-polyfill": "^4.0.10"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/node-fetch"
|
||||
}
|
||||
},
|
||||
"node_modules/node-gyp": {
|
||||
"version": "8.4.1",
|
||||
"dev": true,
|
||||
@@ -5032,6 +5114,15 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/web-streams-polyfill": {
|
||||
"version": "3.3.3",
|
||||
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz",
|
||||
"integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/webidl-conversions": {
|
||||
"version": "3.0.1",
|
||||
"license": "BSD-2-Clause"
|
||||
|
||||
92
backend/package-lock.json
generated
92
backend/package-lock.json
generated
@@ -23,6 +23,7 @@
|
||||
"multer": "^1.4.5-lts.1",
|
||||
"mysql2": "^3.10.3",
|
||||
"node-cron": "^4.2.1",
|
||||
"node-fetch": "^3.3.2",
|
||||
"nodemailer": "^7.0.9",
|
||||
"pdf-parse": "^1.1.1",
|
||||
"pdfjs-dist": "^5.4.394",
|
||||
@@ -1352,6 +1353,15 @@
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/data-uri-to-buffer": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz",
|
||||
"integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 12"
|
||||
}
|
||||
},
|
||||
"node_modules/date-fns": {
|
||||
"version": "2.30.0",
|
||||
"license": "MIT",
|
||||
@@ -1920,6 +1930,29 @@
|
||||
"reusify": "^1.0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/fetch-blob": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz",
|
||||
"integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/jimmywarting"
|
||||
},
|
||||
{
|
||||
"type": "paypal",
|
||||
"url": "https://paypal.me/jimmywarting"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"node-domexception": "^1.0.0",
|
||||
"web-streams-polyfill": "^3.0.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^12.20 || >= 14.13"
|
||||
}
|
||||
},
|
||||
"node_modules/file-entry-cache": {
|
||||
"version": "8.0.0",
|
||||
"dev": true,
|
||||
@@ -2038,6 +2071,18 @@
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/formdata-polyfill": {
|
||||
"version": "4.0.10",
|
||||
"resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz",
|
||||
"integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fetch-blob": "^3.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/formidable": {
|
||||
"version": "3.5.4",
|
||||
"dev": true,
|
||||
@@ -3189,10 +3234,48 @@
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/node-domexception": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
|
||||
"integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==",
|
||||
"deprecated": "Use your platform's native DOMException instead",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/jimmywarting"
|
||||
},
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://paypal.me/jimmywarting"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10.5.0"
|
||||
}
|
||||
},
|
||||
"node_modules/node-ensure": {
|
||||
"version": "0.0.0",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/node-fetch": {
|
||||
"version": "3.3.2",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz",
|
||||
"integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"data-uri-to-buffer": "^4.0.0",
|
||||
"fetch-blob": "^3.1.4",
|
||||
"formdata-polyfill": "^4.0.10"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/node-fetch"
|
||||
}
|
||||
},
|
||||
"node_modules/node-gyp": {
|
||||
"version": "8.4.1",
|
||||
"dev": true,
|
||||
@@ -5066,6 +5149,15 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/web-streams-polyfill": {
|
||||
"version": "3.3.3",
|
||||
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz",
|
||||
"integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/webidl-conversions": {
|
||||
"version": "3.0.1",
|
||||
"license": "BSD-2-Clause"
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
"multer": "^1.4.5-lts.1",
|
||||
"mysql2": "^3.10.3",
|
||||
"node-cron": "^4.2.1",
|
||||
"node-fetch": "^3.3.2",
|
||||
"nodemailer": "^7.0.9",
|
||||
"pdf-parse": "^1.1.1",
|
||||
"pdfjs-dist": "^5.4.394",
|
||||
|
||||
@@ -182,4 +182,93 @@ router.get('/meetingdetails/:uuid', async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
// Submit Meeting Report API-Endpunkt
|
||||
router.put('/submit/:uuid', async (req, res) => {
|
||||
const { uuid } = req.params;
|
||||
const reportData = req.body;
|
||||
|
||||
try {
|
||||
// Hole Cookies für diese UUID (falls vorhanden)
|
||||
// Versuche zuerst UUID, dann Code als Fallback
|
||||
let cookies = cookieStore.get(uuid) || {};
|
||||
|
||||
// Falls keine Cookies für UUID vorhanden, versuche Code zu finden
|
||||
if (Object.keys(cookies).length === 0 && reportData.gameCode) {
|
||||
cookies = cookieStore.get(reportData.gameCode) || {};
|
||||
}
|
||||
|
||||
const url = `https://ttde-apps.liga.nu/nuliga/rs/tt/2022/meetingentry/reports/${uuid}/submit`;
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:141.0) Gecko/20100101 Firefox/141.0',
|
||||
'Accept': 'application/json, text/plain, */*',
|
||||
'Accept-Language': 'de,en-US;q=0.7,en;q=0.3',
|
||||
'Accept-Encoding': 'gzip, deflate, br, zstd',
|
||||
'Content-Type': 'application/json',
|
||||
'Referer': `https://ttde-apps.liga.nu/nuliga/nuscore-tt/meeting/${uuid}/report-clearance/review`,
|
||||
'Origin': 'https://ttde-apps.liga.nu',
|
||||
'Sec-Fetch-Dest': 'empty',
|
||||
'Sec-Fetch-Mode': 'cors',
|
||||
'Sec-Fetch-Site': 'same-origin',
|
||||
'Connection': 'keep-alive',
|
||||
...(Object.keys(cookies).length > 0 && { 'Cookie': formatCookies(cookies) })
|
||||
},
|
||||
body: JSON.stringify(reportData)
|
||||
});
|
||||
|
||||
const responseText = await response.text();
|
||||
let responseData;
|
||||
|
||||
try {
|
||||
responseData = JSON.parse(responseText);
|
||||
} catch (e) {
|
||||
// Falls keine JSON-Antwort, verwende Text
|
||||
responseData = { message: responseText };
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
console.error(`❌ Submit fehlgeschlagen: HTTP ${response.status}`, responseData);
|
||||
return res.status(response.status).json({
|
||||
error: 'Fehler beim Absenden des Spielberichts',
|
||||
details: responseData,
|
||||
status: response.status,
|
||||
statusText: response.statusText
|
||||
});
|
||||
}
|
||||
|
||||
// Speichere neue Cookies falls vorhanden
|
||||
const newCookies = extractCookies(response.headers.raw()['set-cookie']);
|
||||
if (Object.keys(newCookies).length > 0) {
|
||||
cookieStore.set(uuid, { ...cookies, ...newCookies });
|
||||
if (reportData.gameCode) {
|
||||
cookieStore.set(reportData.gameCode, { ...cookies, ...newCookies });
|
||||
}
|
||||
}
|
||||
|
||||
// CORS-Header setzen
|
||||
res.set({
|
||||
'Content-Type': 'application/json; charset=utf-8',
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
'Access-Control-Allow-Methods': 'GET, POST, PUT, OPTIONS',
|
||||
'Access-Control-Allow-Headers': 'Content-Type',
|
||||
'Cache-Control': 'no-cache, no-store, must-revalidate'
|
||||
});
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: responseData,
|
||||
message: 'Spielbericht erfolgreich abgesendet'
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error(`❌ Fehler beim Absenden des Spielberichts für UUID ${uuid}:`, error);
|
||||
res.status(500).json({
|
||||
error: 'Fehler beim Absenden des Spielberichts',
|
||||
details: error.message
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
export default router;
|
||||
|
||||
@@ -176,6 +176,14 @@ app.get('*', (req, res) => {
|
||||
await renameColumnIfExists('official_tournaments', 'meldeschluesse', 'registration_deadlines', 'TEXT NULL');
|
||||
|
||||
const isDev = process.env.STAGE === 'dev';
|
||||
|
||||
// Foreign Keys temporär deaktivieren für MySQL
|
||||
try {
|
||||
await sequelize.query('SET FOREIGN_KEY_CHECKS = 0');
|
||||
} catch (e) {
|
||||
console.warn('[sync] Could not disable foreign key checks:', e?.message);
|
||||
}
|
||||
|
||||
const safeSync = async (model) => {
|
||||
try {
|
||||
if (isDev) {
|
||||
@@ -235,6 +243,15 @@ app.get('*', (req, res) => {
|
||||
await safeSync(ApiLog);
|
||||
await safeSync(MemberTransferConfig);
|
||||
await safeSync(MemberContact);
|
||||
await safeSync(ClubTeam);
|
||||
await safeSync(TeamDocument);
|
||||
|
||||
// Foreign Keys wieder aktivieren
|
||||
try {
|
||||
await sequelize.query('SET FOREIGN_KEY_CHECKS = 1');
|
||||
} catch (e) {
|
||||
console.warn('[sync] Could not enable foreign key checks:', e?.message);
|
||||
}
|
||||
|
||||
// Start scheduler service
|
||||
schedulerService.start();
|
||||
|
||||
@@ -113,6 +113,19 @@
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="info-item">
|
||||
<label>Nicht angetreten:</label>
|
||||
<select
|
||||
v-model="teamNotAppeared"
|
||||
class="not-appeared-select"
|
||||
:class="{ 'not-appeared': teamNotAppeared !== null }"
|
||||
>
|
||||
<option :value="null">Beide Mannschaften angetreten</option>
|
||||
<option value="home">Heimmannschaft ({{ meetingData.homeTeamname || meetingData.homeClub }}) nicht angetreten</option>
|
||||
<option value="guest">Gastmannschaft ({{ meetingData.guestTeamname || meetingData.guestClub }}) nicht angetreten</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="info-item" v-if="(meetingDetails && meetingDetails.playMode) || (meetingData && (meetingData.playMode || meetingData.matchSystem || meetingData.system))">
|
||||
<label>Spielsystem:</label>
|
||||
<span>{{ (meetingDetails && meetingDetails.playMode) || meetingData.playMode || meetingData.matchSystem || meetingData.system }}</span>
|
||||
@@ -526,8 +539,9 @@
|
||||
type="password"
|
||||
class="pin-input"
|
||||
:placeholder="match.homePin || 'PIN eingeben'"
|
||||
:disabled="isMatchCompleted"
|
||||
:disabled="isMatchCompleted || teamNotAppeared === 'home'"
|
||||
/>
|
||||
<span v-if="teamNotAppeared === 'home'" class="pin-not-required">(nicht erforderlich - Mannschaft nicht angetreten)</span>
|
||||
</div>
|
||||
|
||||
<div class="pin-group">
|
||||
@@ -538,8 +552,9 @@
|
||||
type="password"
|
||||
class="pin-input"
|
||||
:placeholder="match.guestPin || 'PIN eingeben'"
|
||||
:disabled="isMatchCompleted"
|
||||
:disabled="isMatchCompleted || teamNotAppeared === 'guest'"
|
||||
/>
|
||||
<span v-if="teamNotAppeared === 'guest'" class="pin-not-required">(nicht erforderlich - Mannschaft nicht angetreten)</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -652,7 +667,7 @@
|
||||
|
||||
<script>
|
||||
import CryptoJS from 'crypto-js';
|
||||
import apiClient from '../apiClient';
|
||||
import apiClient, { backendBaseUrl } from '../apiClient';
|
||||
|
||||
export default {
|
||||
name: 'MatchReportDialog',
|
||||
@@ -674,6 +689,8 @@ export default {
|
||||
isHomeLineupCertified: false,
|
||||
isGuestLineupCertified: false,
|
||||
isGreetingCompleted: false,
|
||||
// Nicht angetreten: 'both', 'home', 'guest', null
|
||||
teamNotAppeared: null,
|
||||
originalHomePin: '',
|
||||
originalGuestPin: '',
|
||||
showPinModal: false,
|
||||
@@ -765,6 +782,16 @@ Wir wünschen den Spielen einen schönen, spannenden und fairen Verlauf und begr
|
||||
if (this.isMatchCompleted) {
|
||||
return false;
|
||||
}
|
||||
// Wenn eine Mannschaft nicht angetreten ist, muss die andere Mannschaft ihre Aufstellung signieren
|
||||
if (this.teamNotAppeared === 'home') {
|
||||
// Heim nicht angetreten -> Gast muss Aufstellung signieren
|
||||
return this.isGuestLineupCertified;
|
||||
}
|
||||
if (this.teamNotAppeared === 'guest') {
|
||||
// Gast nicht angetreten -> Heim muss Aufstellung signieren
|
||||
return this.isHomeLineupCertified;
|
||||
}
|
||||
// Beide Mannschaften angetreten -> beide müssen Aufstellung signieren
|
||||
return this.isHomeLineupCertified && this.isGuestLineupCertified;
|
||||
},
|
||||
currentPlayMode() {
|
||||
@@ -802,6 +829,19 @@ Wir wünschen den Spielen einen schönen, spannenden und fairen Verlauf und begr
|
||||
this.initializeResults();
|
||||
this.initializeFinalPins();
|
||||
},
|
||||
watch: {
|
||||
teamNotAppeared(newValue, oldValue) {
|
||||
if (newValue !== oldValue && this.meetingData) {
|
||||
if (newValue === null) {
|
||||
// Entferne das wo-Flag beim Zurücksetzen
|
||||
this.meetingData.wo = null;
|
||||
} else {
|
||||
// Wende die "Nicht angetreten"-Logik an
|
||||
this.applyTeamNotAppeared();
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// Effektive Spieleranzahl (berücksichtigt Braunschweiger-Regel)
|
||||
getEffectivePlayerCount(team) {
|
||||
@@ -1304,52 +1344,78 @@ Wir wünschen den Spielen einen schönen, spannenden und fairen Verlauf und begr
|
||||
try {
|
||||
console.log('🚀 Starte Spielbericht-Absendung...');
|
||||
|
||||
// Validiere zuerst alle Match-Ergebnisse
|
||||
if (!this.areAllMatchResultsValid()) {
|
||||
const playThrough = this.getPlayThrough();
|
||||
if (playThrough && playThrough.toUpperCase().includes('ALLGAMES') && this.canBothTeamsPlayAllGames()) {
|
||||
const unfinishedMatches = this.results.filter(match => !match.completed);
|
||||
alert(`❌ Der Spielbericht kann nicht abgesendet werden. Es müssen alle Spiele abgeschlossen sein.\n\n` +
|
||||
`📊 Aktueller Status:\n` +
|
||||
`• ${this.results.length} Spiele insgesamt\n` +
|
||||
`• ${this.results.length - unfinishedMatches.length} Spiele abgeschlossen\n` +
|
||||
`• ${unfinishedMatches.length} Spiele noch offen\n\n` +
|
||||
`Bitte schließen Sie alle offenen Spiele ab, bevor Sie den Spielbericht absenden.`);
|
||||
return;
|
||||
} else {
|
||||
alert('❌ Der Spielbericht kann nicht abgesendet werden, da noch Fehler in den Match-Ergebnissen vorhanden sind. Bitte korrigieren Sie:\n\n' +
|
||||
'• Lücken in der Satze-Reihenfolge\n' +
|
||||
'• Ungültige Satzergebnisse\n' +
|
||||
'• Ungenerische Match-Abschlüsse\n\n' +
|
||||
'Überprüfen Sie die Ergebniserfassung.');
|
||||
// Wenn eine Mannschaft nicht angetreten ist, keine Match-Ergebnisse oder Zeiten validieren
|
||||
const isTeamNotAppeared = this.teamNotAppeared !== null;
|
||||
|
||||
if (!isTeamNotAppeared) {
|
||||
// Validiere zuerst alle Match-Ergebnisse
|
||||
if (!this.areAllMatchResultsValid()) {
|
||||
const playThrough = this.getPlayThrough();
|
||||
if (playThrough && playThrough.toUpperCase().includes('ALLGAMES') && this.canBothTeamsPlayAllGames()) {
|
||||
const unfinishedMatches = this.results.filter(match => !match.completed);
|
||||
alert(`❌ Der Spielbericht kann nicht abgesendet werden. Es müssen alle Spiele abgeschlossen sein.\n\n` +
|
||||
`📊 Aktueller Status:\n` +
|
||||
`• ${this.results.length} Spiele insgesamt\n` +
|
||||
`• ${this.results.length - unfinishedMatches.length} Spiele abgeschlossen\n` +
|
||||
`• ${unfinishedMatches.length} Spiele noch offen\n\n` +
|
||||
`Bitte schließen Sie alle offenen Spiele ab, bevor Sie den Spielbericht absenden.`);
|
||||
return;
|
||||
} else {
|
||||
alert('❌ Der Spielbericht kann nicht abgesendet werden, da noch Fehler in den Match-Ergebnissen vorhanden sind. Bitte korrigieren Sie:\n\n' +
|
||||
'• Lücken in der Satze-Reihenfolge\n' +
|
||||
'• Ungültige Satzergebnisse\n' +
|
||||
'• Ungenerische Match-Abschlüsse\n\n' +
|
||||
'Überprüfen Sie die Ergebniserfassung.');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
console.log('✅ Alle Match-Ergebnisse sind gültig - fahre fort...');
|
||||
|
||||
// Validiere Start- und Endzeiten
|
||||
if (!this.areStartAndEndTimesValid()) {
|
||||
alert('❌ Der Spielbericht kann nicht abgesendet werden, da Start- oder Endzeit nicht festgelegt wurden.\n\n' +
|
||||
'Bitte legen Sie sowohl Startzeit als auch Endzeit fest.');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('✅ Alle Zeiten sind gültig - fahre fort...');
|
||||
} else {
|
||||
console.log('⚠️ Mannschaft nicht angetreten - überspringe Match-Ergebnis- und Zeit-Validierung');
|
||||
}
|
||||
|
||||
console.log('✅ Alle Match-Ergebnisse sind gültig - fahre fort...');
|
||||
// Validiere Aufstellungen (nur für angetretene Mannschaften)
|
||||
const isHomeNotAppeared = this.teamNotAppeared === 'home';
|
||||
const isGuestNotAppeared = this.teamNotAppeared === 'guest';
|
||||
|
||||
// Validiere Start- und Endzeiten
|
||||
if (!this.areStartAndEndTimesValid()) {
|
||||
alert('❌ Der Spielbericht kann nicht abgesendet werden, da Start- oder Endzeit nicht festgelegt wurden.\n\n' +
|
||||
'Bitte legen Sie sowohl Startzeit als auch Endzeit fest.');
|
||||
if (!isHomeNotAppeared && !this.isHomeLineupCertified) {
|
||||
alert('❌ Der Spielbericht kann nicht abgesendet werden, da die Heimmannschaft ihre Aufstellung noch nicht signiert hat.\n\n' +
|
||||
'Bitte signieren Sie die Aufstellung der Heimmannschaft, bevor Sie den Spielbericht absenden.');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('✅ Alle Zeiten sind gültig - fahre fort...');
|
||||
if (!isGuestNotAppeared && !this.isGuestLineupCertified) {
|
||||
alert('❌ Der Spielbericht kann nicht abgesendet werden, da die Gastmannschaft ihre Aufstellung noch nicht signiert hat.\n\n' +
|
||||
'Bitte signieren Sie die Aufstellung der Gastmannschaft, bevor Sie den Spielbericht absenden.');
|
||||
return;
|
||||
}
|
||||
|
||||
// Validiere PINs (für Test: Gast-PIN "1234" akzeptieren)
|
||||
if (!this.finalHomePin || this.finalHomePin.trim() === '') {
|
||||
console.log('✅ Alle Aufstellungen sind signiert - fahre fort...');
|
||||
|
||||
// Validiere PINs (nur für angetretene Mannschaften)
|
||||
|
||||
if (!isHomeNotAppeared && (!this.finalHomePin || this.finalHomePin.trim() === '')) {
|
||||
alert('Bitte geben Sie die PIN des Heimvereins ein.');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.finalGuestPin || this.finalGuestPin.trim() === '') {
|
||||
if (!isGuestNotAppeared && (!this.finalGuestPin || this.finalGuestPin.trim() === '')) {
|
||||
alert('Bitte geben Sie die PIN des Gastvereins ein.');
|
||||
return;
|
||||
}
|
||||
|
||||
// Test-Validierung: Gast-PIN "1234" akzeptieren
|
||||
if (this.finalGuestPin !== '1234') {
|
||||
// Test-Validierung: Gast-PIN "1234" akzeptieren (nur wenn Gast angetreten ist)
|
||||
if (!isGuestNotAppeared && this.finalGuestPin !== '1234') {
|
||||
alert('Für den Test wird nur die Gast-PIN "1234" akzeptiert.');
|
||||
return;
|
||||
}
|
||||
@@ -1366,10 +1432,33 @@ Wir wünschen den Spielen einen schönen, spannenden und fairen Verlauf und begr
|
||||
this.updateMatchData(matchData);
|
||||
console.log('✅ Match-Daten aktualisiert');
|
||||
|
||||
// Zeige das vollständige Objekt in einem Dialog an
|
||||
console.log('📊 Zeige Match-Daten Dialog...');
|
||||
this.showMatchDataDialog(matchData);
|
||||
console.log('✅ Dialog angezeigt');
|
||||
// Sende die Daten an den Backend-Endpunkt
|
||||
console.log('📤 Sende Spielbericht an Backend...');
|
||||
const uuid = this.meetingData.nuLigaMeetingUuid;
|
||||
|
||||
if (!uuid) {
|
||||
throw new Error('nuLigaMeetingUuid nicht gefunden');
|
||||
}
|
||||
|
||||
const response = await fetch(`${backendBaseUrl}/api/nuscore/submit/${uuid}`, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(matchData)
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(result.error || `HTTP ${response.status}: ${response.statusText}`);
|
||||
}
|
||||
|
||||
console.log('✅ Spielbericht erfolgreich abgesendet:', result);
|
||||
alert('✅ Spielbericht erfolgreich abgesendet!');
|
||||
|
||||
// Dialog schließen
|
||||
this.$emit('close');
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Fehler beim Absenden:', error);
|
||||
@@ -1472,6 +1561,16 @@ Wir wünschen den Spielen einen schönen, spannenden und fairen Verlauf und begr
|
||||
|
||||
// NUR unsere spezifischen Änderungen eintragen:
|
||||
|
||||
// Wenn eine Mannschaft nicht angetreten ist, nur wo-Flag setzen und nichts anderes ändern
|
||||
if (this.teamNotAppeared !== null) {
|
||||
// Nur das wo-Flag ist bereits gesetzt (durch applyTeamNotAppeared)
|
||||
// Alle anderen Daten bleiben unverändert (Original aus baseData)
|
||||
// isCompleted bleibt false, PINs bleiben unverändert, keine Match-Ergebnisse ändern
|
||||
// Keine Zeitangaben, Spieleranzahl, Positionen oder Protest-Informationen ändern
|
||||
console.log('⚠️ Mannschaft nicht angetreten - nur wo-Flag wird gesetzt, alle anderen Daten bleiben unverändert');
|
||||
return; // Frühzeitiger Return - keine weiteren Änderungen
|
||||
}
|
||||
|
||||
// 1. Spieleranzahl aktualisieren (aus Aufstellung)
|
||||
matchData.playerCountHome = this.getSelectedPlayerCount('home');
|
||||
matchData.playerCountGuest = this.getSelectedPlayerCount('guest');
|
||||
@@ -1497,13 +1596,13 @@ Wir wünschen den Spielen einen schönen, spannenden und fairen Verlauf und begr
|
||||
matchData.remarks = null;
|
||||
}
|
||||
|
||||
// 4. PINs - Original-Hashes beibehalten (werden vom Backend beim Senden aktualisiert)
|
||||
// 5. PINs - Original-Hashes beibehalten (werden vom Backend beim Senden aktualisiert)
|
||||
// matchData.homePin und matchData.guestPin bleiben die ursprünglichen Hashes aus baseData
|
||||
|
||||
// 5. Match-Status auf abgeschlossen setzen
|
||||
// 6. Match-Status auf abgeschlossen setzen
|
||||
matchData.isCompleted = true;
|
||||
|
||||
// 6. Gesamtstatistik berechnen und eintragen
|
||||
// 7. Gesamtstatistik berechnen und eintragen
|
||||
const overallScore = this.getOverallMatchScore();
|
||||
|
||||
// Gesamtpunkte berechnen und eintragen
|
||||
@@ -1522,7 +1621,7 @@ Wir wünschen den Spielen einen schönen, spannenden und fairen Verlauf und begr
|
||||
matchData.guestSets = parseInt(setScoreMatch[2]);
|
||||
}
|
||||
|
||||
// 7. Gesamtpunkte (Games) berechnen und eintragen
|
||||
// 8. Gesamtpunkte (Games) berechnen und eintragen
|
||||
let totalHomeGames = 0;
|
||||
let totalGuestGames = 0;
|
||||
|
||||
@@ -1581,7 +1680,7 @@ Wir wünschen den Spielen einen schönen, spannenden und fairen Verlauf und begr
|
||||
});
|
||||
}
|
||||
|
||||
// 8. Gesamtpunkte (Games) eintragen
|
||||
// 9. Gesamtpunkte (Games) eintragen
|
||||
matchData.homeGames = totalHomeGames;
|
||||
matchData.guestGames = totalGuestGames;
|
||||
|
||||
@@ -1735,12 +1834,12 @@ Wir wünschen den Spielen einen schönen, spannenden und fairen Verlauf und begr
|
||||
|
||||
try {
|
||||
// Zuerst Cookies initialisieren
|
||||
await fetch(`http://localhost:3000/api/nuscore/init-cookies/${this.match.code}`, {
|
||||
await fetch(`${backendBaseUrl}/api/nuscore/init-cookies/${this.match.code}`, {
|
||||
method: 'POST'
|
||||
});
|
||||
|
||||
// Dann Meeting-Daten laden
|
||||
const response = await fetch(`http://localhost:3000/api/nuscore/meetinginfo/${this.match.code}`);
|
||||
const response = await fetch(`${backendBaseUrl}/api/nuscore/meetinginfo/${this.match.code}`);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
||||
@@ -1749,10 +1848,27 @@ Wir wünschen den Spielen einen schönen, spannenden und fairen Verlauf und begr
|
||||
this.meetingData = await response.json();
|
||||
console.log('[nuscore] /meetinginfo response:', this.meetingData);
|
||||
|
||||
// Initialisiere "Nicht angetreten"-Wert aus meetingData
|
||||
if (this.meetingData.teamNotAppeared !== undefined) {
|
||||
this.teamNotAppeared = this.meetingData.teamNotAppeared;
|
||||
} else if (this.meetingData.homeTeamNotAppeared !== undefined || this.meetingData.guestTeamNotAppeared !== undefined) {
|
||||
// Migration: Alte boolean-Werte in neuen Wert umwandeln
|
||||
if (this.meetingData.homeTeamNotAppeared) {
|
||||
this.teamNotAppeared = 'home';
|
||||
} else if (this.meetingData.guestTeamNotAppeared) {
|
||||
this.teamNotAppeared = 'guest';
|
||||
} else {
|
||||
this.teamNotAppeared = null;
|
||||
}
|
||||
} else if (this.meetingData.wo) {
|
||||
// Wenn wo bereits gesetzt ist, leite daraus teamNotAppeared ab
|
||||
this.teamNotAppeared = this.meetingData.wo === 'A' ? 'home' : (this.meetingData.wo === 'B' ? 'guest' : null);
|
||||
}
|
||||
|
||||
// Lade detaillierte Meeting-Daten falls UUID verfügbar
|
||||
if (this.meetingData.nuLigaMeetingUuid) {
|
||||
try {
|
||||
const detailsResponse = await fetch(`http://localhost:3000/api/nuscore/meetingdetails/${this.meetingData.nuLigaMeetingUuid}`);
|
||||
const detailsResponse = await fetch(`${backendBaseUrl}/api/nuscore/meetingdetails/${this.meetingData.nuLigaMeetingUuid}`);
|
||||
if (detailsResponse.ok) {
|
||||
this.meetingDetails = await detailsResponse.json();
|
||||
console.log('[nuscore] /meetingdetails response:', this.meetingDetails);
|
||||
@@ -1771,6 +1887,11 @@ Wir wünschen den Spielen einen schönen, spannenden und fairen Verlauf und begr
|
||||
// Ergebnisse initial auf Basis der Matrix vorbereiten
|
||||
this.prepareResults();
|
||||
|
||||
// Wenn teamNotAppeared gesetzt ist und die Daten noch nicht angepasst wurden, wende die Anpassung an
|
||||
if (this.teamNotAppeared !== null && !this.meetingData.wo) {
|
||||
this.applyTeamNotAppeared();
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Fehler beim Laden der Meeting-Daten:', error);
|
||||
this.error = error.message;
|
||||
@@ -1789,6 +1910,18 @@ Wir wünschen den Spielen einen schönen, spannenden und fairen Verlauf und begr
|
||||
result: '1:0'
|
||||
}));
|
||||
},
|
||||
applyTeamNotAppeared() {
|
||||
if (!this.meetingData || this.teamNotAppeared === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const woValue = this.teamNotAppeared === 'home' ? 'A' : 'B'; // A = Home, B = Guest
|
||||
|
||||
// Setze nur das wo-Flag im meetingData - nichts anderes ändern
|
||||
this.meetingData.wo = woValue;
|
||||
|
||||
console.log('✅ Team "Nicht angetreten" angewendet:', this.teamNotAppeared, 'wo:', woValue);
|
||||
},
|
||||
resolveSide(label, side) {
|
||||
// label z.B. "A1 – B2" oder "DA1 – DB1"
|
||||
try {
|
||||
@@ -2968,11 +3101,11 @@ Wir wünschen den Spielen einen schönen, spannenden und fairen Verlauf und begr
|
||||
// Braunschweiger System
|
||||
if (mode.includes('braunschweiger')) {
|
||||
if (playerCount === 2) {
|
||||
return 2; // Alle 2 müssen Doppel spielen
|
||||
return 2; // Beide müssen Doppel spielen
|
||||
} else if (playerCount === 3) {
|
||||
return 2; // 2 von 3 müssen Doppel spielen
|
||||
} else if (playerCount >= 4) {
|
||||
return playerCount; // Alle müssen Doppel spielen
|
||||
return 4; // 4 Spieler müssen Doppel spielen (2 Doppel-Paare)
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -3119,8 +3252,8 @@ Wir wünschen den Spielen einen schönen, spannenden und fairen Verlauf und begr
|
||||
explanation = 'beide Spieler müssen Doppel spielen';
|
||||
} else if (selectedPlayers === 3) {
|
||||
explanation = '2 von 3 Spielern müssen Doppel spielen';
|
||||
} else if (requiredDoubles === selectedPlayers) {
|
||||
explanation = `alle ${selectedPlayers} Spieler müssen Doppel spielen`;
|
||||
} else if (selectedPlayers >= 4 && requiredDoubles === 4) {
|
||||
explanation = '4 Spieler müssen Doppel spielen (2 Doppel-Paare)';
|
||||
}
|
||||
} else if (mode.includes('4') || mode.includes('paarkreuz') || mode.includes('bundes') || mode.includes('werner')) {
|
||||
if (selectedPlayers === 3 && requiredDoubles === 2) {
|
||||
@@ -3792,6 +3925,13 @@ Wir wünschen den Spielen einen schönen, spannenden und fairen Verlauf und begr
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.pin-not-required {
|
||||
font-size: 12px;
|
||||
color: #6c757d;
|
||||
font-style: italic;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.submit-section {
|
||||
text-align: center;
|
||||
padding: 16px;
|
||||
@@ -3842,6 +3982,32 @@ Wir wünschen den Spielen einen schönen, spannenden und fairen Verlauf und begr
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
.not-appeared-select {
|
||||
padding: 8px 12px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
font-size: 14px;
|
||||
background-color: white;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.not-appeared-select.not-appeared {
|
||||
background-color: #fff3cd;
|
||||
border-color: #ffc107;
|
||||
color: #856404;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.not-appeared-select:hover {
|
||||
border-color: #999;
|
||||
}
|
||||
|
||||
.not-appeared-select.not-appeared:hover {
|
||||
border-color: #ff9800;
|
||||
background-color: #ffe082;
|
||||
}
|
||||
|
||||
.code-display {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
@@ -308,7 +308,6 @@ export default {
|
||||
components: {
|
||||
SeasonSelector,
|
||||
MatchReportApiDialog,
|
||||
MatchReportDialog,
|
||||
InfoDialog,
|
||||
ConfirmDialog,
|
||||
BaseDialog,
|
||||
|
||||
Reference in New Issue
Block a user