feat(clickTtPlayerRegistrationService, MembersView): enhance Click-TT registration flow and UI feedback
- Replaced the abort confirmation flow with a new function to open the application after a search, improving user experience by directly navigating to the application. - Updated error handling to provide clearer messages when a confirm dialog is encountered, enhancing traceability. - Modified the MembersView to track application submission status and display detailed success/error messages, improving user feedback during the registration process. - Introduced a new method to format and display trace details, aiding in debugging and user communication.
This commit is contained in:
@@ -368,13 +368,40 @@ function injectProxyNavigationScript(html, proxyBaseUrl, pageBaseUrl, sid) {
|
||||
"console.log('[ClickTT Proxy] inline confirm elements json',JSON.stringify(elements));",
|
||||
'}catch(e){}',
|
||||
'}',
|
||||
'function bindInlineConfirmElements(){',
|
||||
'try{',
|
||||
"var elements=[].slice.call(document.querySelectorAll('[onclick*=\"confirm(\"]'));",
|
||||
'elements.forEach(function(el,index){',
|
||||
"if(el.__clickttConfirmBound)return;",
|
||||
'el.__clickttConfirmBound=true;',
|
||||
"el.addEventListener('click',function(event){",
|
||||
"try{console.log('[ClickTT Proxy] direct confirm element click',{index:index,tag:el.tagName,name:el.getAttribute('name'),value:el.getAttribute('value'),href:el.getAttribute('href'),onclick:el.getAttribute('onclick'),text:(el.textContent||'').trim().slice(0,120)});}catch(e){}",
|
||||
'var allowed=shouldAllowInlineConfirm(el);',
|
||||
'if(!allowed){',
|
||||
'event.preventDefault();',
|
||||
'event.stopPropagation();',
|
||||
'return false;',
|
||||
'}',
|
||||
"var href=el.getAttribute&&el.getAttribute('href');",
|
||||
'var targetUrl=normalizeUrl(href,PAGE_BASE_URL);',
|
||||
'if(targetUrl&&shouldProxyUrl(targetUrl)){',
|
||||
'event.preventDefault();',
|
||||
'event.stopPropagation();',
|
||||
'navigateViaProxy(targetUrl);',
|
||||
'return false;',
|
||||
'}',
|
||||
'return true;',
|
||||
'},true);',
|
||||
'});',
|
||||
'}catch(e){}',
|
||||
'}',
|
||||
'function getSubmitTarget(form, submitter){',
|
||||
"var action=submitter&&submitter.getAttribute?submitter.getAttribute('formaction'):null;",
|
||||
'if(action)return normalizeUrl(action,PAGE_BASE_URL);',
|
||||
"return normalizeUrl((form&&form.getAttribute?form.getAttribute('action'):null)||PAGE_BASE_URL,PAGE_BASE_URL);",
|
||||
'}',
|
||||
'var lastSubmitter=null;',
|
||||
'if(document.readyState==="loading"){document.addEventListener("DOMContentLoaded",logInlineConfirmElements,{once:true});}else{logInlineConfirmElements();}',
|
||||
'if(document.readyState==="loading"){document.addEventListener("DOMContentLoaded",function(){logInlineConfirmElements();bindInlineConfirmElements();},{once:true});}else{logInlineConfirmElements();bindInlineConfirmElements();}',
|
||||
"document.addEventListener('click',function(event){",
|
||||
"var anchor=event.target&&event.target.closest?event.target.closest('a[href]'):null;",
|
||||
"var submitControl=event.target&&event.target.closest?event.target.closest('button, input[type=\"submit\"], input[type=\"image\"]'):null;",
|
||||
|
||||
@@ -86,7 +86,7 @@ class ClickTtPlayerRegistrationService {
|
||||
await this._clickByText(page, 'Personen suchen', trace);
|
||||
await page.waitForLoadState('domcontentloaded');
|
||||
|
||||
await this._abortIfConfirmFlow(page);
|
||||
await this._openApplicationAfterSearch(page, trace);
|
||||
await this._fillApplicationForm(page, memberJson, primaryEmail);
|
||||
await this._clickByText(page, 'Weiter >>', trace);
|
||||
await page.waitForLoadState('domcontentloaded');
|
||||
@@ -288,11 +288,41 @@ class ClickTtPlayerRegistrationService {
|
||||
await fillIfEmpty(13, email);
|
||||
}
|
||||
|
||||
async _abortIfConfirmFlow(page) {
|
||||
async _openApplicationAfterSearch(page, trace) {
|
||||
const explicitApplicationLink = page.getByText(/Spielberechtigung beantragen/i).first();
|
||||
if (await explicitApplicationLink.count()) {
|
||||
let dialogSeen = false;
|
||||
page.once('dialog', async (dialog) => {
|
||||
dialogSeen = true;
|
||||
this._trace(trace, 'dialog', {
|
||||
kind: dialog.type(),
|
||||
message: sanitizePageText(dialog.message())
|
||||
});
|
||||
await dialog.accept();
|
||||
});
|
||||
|
||||
this._trace(trace, 'step', {
|
||||
name: 'click',
|
||||
label: 'Spielberechtigung beantragen',
|
||||
selector: 'text=/Spielberechtigung beantragen/i'
|
||||
});
|
||||
await explicitApplicationLink.click();
|
||||
await page.waitForLoadState('domcontentloaded');
|
||||
|
||||
this._trace(trace, 'step', {
|
||||
name: 'search-result-application-opened',
|
||||
dialogSeen,
|
||||
url: page.url()
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const confirmLocator = page.locator('[onclick*="confirm("]').first();
|
||||
if (await confirmLocator.count()) {
|
||||
const text = sanitizePageText(await confirmLocator.innerText().catch(() => '') || await confirmLocator.getAttribute('value').catch(() => '') || '');
|
||||
throw new HttpError(`Der Antrag befindet sich im noch nicht automatisierten click-TT-Confirm-/Neuanlage-Flow${text ? `: ${text}` : ''}`, 409);
|
||||
const text = sanitizePageText(
|
||||
await confirmLocator.innerText().catch(() => '') || await confirmLocator.getAttribute('value').catch(() => '') || ''
|
||||
);
|
||||
throw new HttpError(`Click-TT-Suchergebnis mit Confirm gefunden, aber kein klickbares Antragselement per Label erkannt${text ? `: ${text}` : ''}`, 409);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -256,7 +256,7 @@
|
||||
⛔
|
||||
</span>
|
||||
<span
|
||||
v-if="member.active && !member.ttr && !member.qttr"
|
||||
v-if="member.active && !member.ttr && !member.qttr && !member.clickTtApplicationSubmitted"
|
||||
@click.stop="requestClickTtRegistration(member)"
|
||||
class="action-icon"
|
||||
:class="{ 'action-icon-disabled': clickTtPendingMemberIds.includes(member.id) }"
|
||||
@@ -665,7 +665,7 @@ export default {
|
||||
const confirmed = await this.showConfirm(
|
||||
'Click-TT-Antrag starten',
|
||||
`Soll fuer ${member.firstName} ${member.lastName} der automatisierte Click-TT-Antrag gestartet werden?`,
|
||||
'Aktuell ist nur der Workflow fuer bereits im click-TT-System vorhandene Spieler automatisiert.',
|
||||
'Der Antrag wird im Backend automatisiert durch den Click-TT-Workflow gefuehrt.',
|
||||
'info'
|
||||
);
|
||||
if (!confirmed) {
|
||||
@@ -675,19 +675,58 @@ export default {
|
||||
this.clickTtPendingMemberIds = [...this.clickTtPendingMemberIds, member.id];
|
||||
try {
|
||||
const response = await apiClient.post(`/clubmembers/clicktt-registration/${this.currentClub}/${member.id}`);
|
||||
const traceDetails = this.formatClickTtTrace(response.data?.trace);
|
||||
if (response.data?.success) {
|
||||
await this.showInfo('Click-TT-Antrag', getSafeMessage(response.data.message, 'Der Click-TT-Antrag wurde erfolgreich eingereicht.'), response.data.finalUrl || '', 'success');
|
||||
member.clickTtApplicationSubmitted = true;
|
||||
const successDetails = [response.data.finalUrl || '', traceDetails].filter(Boolean).join('\n\n');
|
||||
await this.showInfo(
|
||||
'Click-TT-Antrag',
|
||||
getSafeMessage(response.data.message, 'Der Click-TT-Antrag wurde erfolgreich eingereicht.'),
|
||||
successDetails,
|
||||
'success'
|
||||
);
|
||||
} else {
|
||||
await this.showInfo('Click-TT-Antrag', getSafeMessage(response.data?.error, 'Der Click-TT-Antrag konnte nicht eingereicht werden.'), '', 'error');
|
||||
await this.showInfo(
|
||||
'Click-TT-Antrag',
|
||||
getSafeMessage(response.data?.error, 'Der Click-TT-Antrag konnte nicht eingereicht werden.'),
|
||||
traceDetails,
|
||||
'error'
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Click-TT-Antrag fehlgeschlagen', error);
|
||||
const errorMessage = getSafeErrorMessage(error, 'Der Click-TT-Antrag konnte nicht eingereicht werden.');
|
||||
await this.showInfo('Click-TT-Antrag', errorMessage, '', 'error');
|
||||
await this.showInfo(
|
||||
'Click-TT-Antrag',
|
||||
errorMessage,
|
||||
this.formatClickTtTrace(error?.response?.data?.trace),
|
||||
'error'
|
||||
);
|
||||
} finally {
|
||||
this.clickTtPendingMemberIds = this.clickTtPendingMemberIds.filter(id => id !== member.id);
|
||||
}
|
||||
},
|
||||
formatClickTtTrace(trace) {
|
||||
if (!Array.isArray(trace) || trace.length === 0) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return trace
|
||||
.slice(-12)
|
||||
.map((entry) => {
|
||||
const type = entry?.type || 'trace';
|
||||
const parts = [type];
|
||||
|
||||
if (entry?.name) parts.push(entry.name);
|
||||
if (entry?.label) parts.push(`label=${entry.label}`);
|
||||
if (entry?.status) parts.push(`status=${entry.status}`);
|
||||
if (entry?.url) parts.push(`url=${entry.url}`);
|
||||
if (entry?.message) parts.push(`message=${entry.message}`);
|
||||
|
||||
return parts.join(' | ');
|
||||
})
|
||||
.join('\n');
|
||||
},
|
||||
toggleNewMember() {
|
||||
this.memberFormIsOpen = !this.memberFormIsOpen;
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user