refactor(clickTtPlayerRegistrationService): enhance application search and link handling

- Introduced a new function to normalize comparable text for improved matching during application searches.
- Updated the _openApplicationAfterSearch method to accept member data, enhancing the accuracy of application link retrieval based on expected name and birth date.
- Improved error handling to provide clearer feedback when no matching application entry is found, ensuring better user guidance during the registration process.
This commit is contained in:
Torsten Schulz (local)
2026-03-11 17:21:29 +01:00
parent bfd6068c5c
commit 312a1d9d8a

View File

@@ -37,6 +37,14 @@ function escapeRegExp(value) {
return String(value).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
function normalizeComparableText(value) {
return String(value || '')
.normalize('NFKC')
.replace(/\s+/g, ' ')
.trim()
.toLowerCase();
}
function buildDebugHtmlPath() {
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
return path.join(process.cwd(), 'tmp', `clicktt-debug-${timestamp}.html`);
@@ -113,7 +121,7 @@ class ClickTtPlayerRegistrationService {
await this._clickByText(page, 'Personen suchen', trace);
await page.waitForLoadState('domcontentloaded');
await this._openApplicationAfterSearch(page, trace);
await this._openApplicationAfterSearch(page, memberJson, trace);
await this._fillApplicationForm(page, memberJson, primaryEmail, trace);
await this._clickByText(page, 'Weiter >>', trace);
await page.waitForLoadState('domcontentloaded');
@@ -472,9 +480,41 @@ class ClickTtPlayerRegistrationService {
}
}
async _openApplicationAfterSearch(page, trace) {
const explicitApplicationLink = page.getByText(/Spielberechtigung beantragen/i).first();
if (await explicitApplicationLink.count()) {
async _openApplicationAfterSearch(page, member, trace) {
const expectedName = normalizeComparableText(`${member.lastName}, ${member.firstName}`);
const expectedBirthDate = formatGermanDate(member.birthDate);
const explicitApplicationHref = await page.locator('table.result-set tr').evaluateAll((rows, criteria) => {
const normalize = (value) => String(value || '')
.normalize('NFKC')
.replace(/\s+/g, ' ')
.trim()
.toLowerCase();
for (const row of rows) {
const link = row.querySelector('a');
const strong = row.querySelector('strong');
const cells = Array.from(row.querySelectorAll('td'));
const linkText = normalize(link?.textContent || '');
const nameText = normalize(strong?.textContent || '');
const birthDateText = normalize(cells[1]?.textContent || '');
if (!link || !strong) continue;
if (!linkText.includes('spielberechtigung beantragen')) continue;
if (nameText !== criteria.expectedName) continue;
if (birthDateText !== normalize(criteria.expectedBirthDate)) continue;
return link.getAttribute('href');
}
return null;
}, {
expectedName,
expectedBirthDate
});
if (explicitApplicationHref) {
const explicitApplicationLink = page.locator(`a[href="${explicitApplicationHref}"]`).first();
let dialogSeen = false;
page.once('dialog', async (dialog) => {
dialogSeen = true;
@@ -488,7 +528,9 @@ class ClickTtPlayerRegistrationService {
this._trace(trace, 'step', {
name: 'click',
label: 'Spielberechtigung beantragen',
selector: 'text=/Spielberechtigung beantragen/i'
selector: `a[href="${explicitApplicationHref}"]`,
expectedName: `${member.lastName}, ${member.firstName}`,
expectedBirthDate
});
await explicitApplicationLink.click();
await page.waitForLoadState('domcontentloaded');
@@ -501,12 +543,12 @@ class ClickTtPlayerRegistrationService {
return;
}
const confirmLocator = page.locator('[onclick*="confirm("]').first();
if (await confirmLocator.count()) {
const text = sanitizePageText(
await confirmLocator.innerText().catch(() => '') || await confirmLocator.getAttribute('value').catch(() => '') || ''
const searchResultsText = sanitizePageText(await page.locator('table.result-set').innerText().catch(() => ''));
if (searchResultsText) {
throw new HttpError(
`Kein exakt passender Click-TT-Sucheintrag für "${member.lastName}, ${member.firstName}" mit Geburtsdatum ${expectedBirthDate} gefunden`,
409
);
throw new HttpError(`Click-TT-Suchergebnis mit Confirm gefunden, aber kein klickbares Antragselement per Label erkannt${text ? `: ${text}` : ''}`, 409);
}
}