feat: Enhance FaceAI functionality and improve login process
All checks were successful
Publish FaceAI Container / publish (push) Successful in 4m43s
All checks were successful
Publish FaceAI Container / publish (push) Successful in 4m43s
- Added a retry mechanism for page navigation in `live-site-test-utils.js` to handle transient errors during login. - Introduced a new function `performLiveLoginRequest` to handle login requests via API, improving the login flow. - Updated the login process to utilize the new API request method, ensuring a more robust authentication. - Implemented new utility functions in `rus-ecom-240621.js` for managing FaceAI state and filtering. - Created `faceai_photo_lookup.jsp` to handle photo lookups, returning JSON responses for better integration with the frontend. - Updated `faceai_return.php` to redirect users with appropriate parameters after FaceAI processing. - Modified `fotoCR.jsp` and `fotoCR-en.jsp` to include FaceAI photo IDs in the image elements for better tracking. - Enhanced the UI to display the number of matched photos dynamically based on FaceAI results.
This commit is contained in:
parent
6f191de115
commit
bba8026b7c
17 changed files with 1077 additions and 95 deletions
|
|
@ -1,5 +1,7 @@
|
|||
<script setup>
|
||||
defineProps({
|
||||
const fileInputId = 'faceai-upload-input';
|
||||
|
||||
const props = defineProps({
|
||||
loading: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
|
|
@ -54,6 +56,10 @@ defineProps({
|
|||
}
|
||||
});
|
||||
|
||||
function assignFileInput(element) {
|
||||
props.fileInput.value = element;
|
||||
}
|
||||
|
||||
const emit = defineEmits([
|
||||
'open-file-picker',
|
||||
'file-change',
|
||||
|
|
@ -108,7 +114,8 @@ const emit = defineEmits([
|
|||
@drop="emit('drop', $event)"
|
||||
>
|
||||
<input
|
||||
ref="fileInput"
|
||||
:ref="assignFileInput"
|
||||
:id="fileInputId"
|
||||
class="d-none"
|
||||
type="file"
|
||||
accept="image/*"
|
||||
|
|
@ -141,9 +148,18 @@ const emit = defineEmits([
|
|||
</div>
|
||||
|
||||
<div class="faceai-dropzone-actions" @click.stop>
|
||||
<button class="btn btn-outline-warning" type="button" :disabled="!canPickFile" @click="emit('open-file-picker')">
|
||||
<label
|
||||
class="btn btn-outline-warning mb-0"
|
||||
:class="{ disabled: !canPickFile }"
|
||||
:for="canPickFile ? fileInputId : null"
|
||||
role="button"
|
||||
:aria-disabled="!canPickFile"
|
||||
tabindex="0"
|
||||
@keydown.enter.prevent="emit('open-file-picker')"
|
||||
@keydown.space.prevent="emit('open-file-picker')"
|
||||
>
|
||||
{{ selectedFile ? t('uploaderReplace') : t('uploaderBrowse') }}
|
||||
</button>
|
||||
</label>
|
||||
<button v-if="selectedFile" class="btn btn-link" type="button" @click="emit('clear-file')">
|
||||
{{ t('uploaderRemove') }}
|
||||
</button>
|
||||
|
|
|
|||
|
|
@ -50,6 +50,8 @@ const copy = {
|
|||
raceDataUnavailable: 'I dati FaceAI non sono disponibili per questa gara.',
|
||||
invalidRaceData: 'I dati della gara ricevuti non sono validi. Torna alla pagina gara e riapri Face ID dalla gara corretta.',
|
||||
searchCreateError: 'Impossibile avviare la ricerca.',
|
||||
activeSearchExists: 'C\'e gia una ricerca in corso per questo utente. Attendi il completamento oppure ricarica la pagina.',
|
||||
rateLimited: 'Hai avviato troppe ricerche in poco tempo. Attendi un momento e riprova.',
|
||||
faceAiAlt: 'FaceAI',
|
||||
dropzoneDisabled: 'Il caricamento non è disponibile per questa gara.'
|
||||
},
|
||||
|
|
@ -101,11 +103,23 @@ const copy = {
|
|||
raceDataUnavailable: 'FaceAI data is not available for this race.',
|
||||
invalidRaceData: 'The race data received for this session is invalid. Go back to the race page and reopen Face ID from the correct race.',
|
||||
searchCreateError: 'Unable to start the search.',
|
||||
activeSearchExists: 'There is already a search running for this user. Wait for it to finish or reload the page.',
|
||||
rateLimited: 'Too many searches were started in a short time. Please wait a moment and try again.',
|
||||
faceAiAlt: 'FaceAI',
|
||||
dropzoneDisabled: 'Upload is not available for this race.'
|
||||
}
|
||||
};
|
||||
|
||||
const knownServerCodes = {
|
||||
PROCESSOR_UNAVAILABLE: 'processorUnavailable',
|
||||
ACTIVE_SEARCH_EXISTS: 'activeSearchExists',
|
||||
RATE_LIMITED: 'rateLimited',
|
||||
MISSING_SELFIE: 'chooseSelfie',
|
||||
RACE_PKL_UNAVAILABLE: 'raceDataUnavailable',
|
||||
RACE_DIRECTORY_NOT_FOUND: 'invalidRaceData',
|
||||
MISSING_RACE_STORAGE: 'invalidRaceData'
|
||||
};
|
||||
|
||||
const knownServerMessages = {
|
||||
'No training dataset available for this race.': 'raceDataUnavailable',
|
||||
'FaceAI data is not available for this race.': 'raceDataUnavailable',
|
||||
|
|
@ -115,7 +129,9 @@ const knownServerMessages = {
|
|||
'FaceAI processor is temporarily unavailable. Please try again shortly.': 'processorUnavailable',
|
||||
'Unable to build return URL.': 'redirectError',
|
||||
'Unable to create the search.': 'searchCreateError',
|
||||
'Choose a selfie before starting the search.': 'chooseSelfie'
|
||||
'Choose a selfie before starting the search.': 'chooseSelfie',
|
||||
'There is already an operation being processed.': 'activeSearchExists',
|
||||
'Too many search attempts. Please try again later.': 'rateLimited'
|
||||
};
|
||||
|
||||
const simulatorUrl = legacyUrl('/faceai_simulator.php?raceId=101&lang=it');
|
||||
|
|
@ -178,6 +194,15 @@ export function useFaceAiHome() {
|
|||
return t(fallbackKey);
|
||||
}
|
||||
|
||||
function localizeServerError(payload, fallbackKey) {
|
||||
const mappedCodeKey = payload?.code ? knownServerCodes[payload.code] : null;
|
||||
if (mappedCodeKey) {
|
||||
return t(mappedCodeKey);
|
||||
}
|
||||
|
||||
return localizeServerMessage(payload?.error, fallbackKey);
|
||||
}
|
||||
|
||||
function getAvailabilityUserMessage(availability, fallbackKey = 'unavailableDefault') {
|
||||
if (isInvalidRaceAvailability(availability)) {
|
||||
return t('invalidRaceData');
|
||||
|
|
@ -424,7 +449,7 @@ export function useFaceAiHome() {
|
|||
const response = await fetch(`/api/searches/${searchId}`, { credentials: 'include' });
|
||||
if (!response.ok) {
|
||||
const payload = await response.json().catch(() => ({}));
|
||||
errorMessage.value = localizeServerMessage(payload.error, 'pollError');
|
||||
errorMessage.value = localizeServerError(payload, 'pollError');
|
||||
isSubmitting.value = false;
|
||||
logFaceAiDebug('Search polling failed', { searchId, status: response.status, payload });
|
||||
return;
|
||||
|
|
@ -451,7 +476,7 @@ export function useFaceAiHome() {
|
|||
const redirectResponse = await fetch(`/api/searches/${searchId}/redirect`, { credentials: 'include' });
|
||||
const payload = await redirectResponse.json();
|
||||
if (!redirectResponse.ok) {
|
||||
errorMessage.value = localizeServerMessage(payload.error, 'redirectError');
|
||||
errorMessage.value = localizeServerError(payload, 'redirectError');
|
||||
logFaceAiDebug('Redirect build failed', { searchId, payload });
|
||||
return;
|
||||
}
|
||||
|
|
@ -498,7 +523,7 @@ export function useFaceAiHome() {
|
|||
|
||||
const payload = await response.json();
|
||||
if (!response.ok) {
|
||||
errorMessage.value = localizeServerMessage(payload.error, 'searchCreateError');
|
||||
errorMessage.value = localizeServerError(payload, 'searchCreateError');
|
||||
isSubmitting.value = false;
|
||||
logFaceAiDebug('Search creation failed', { status: response.status, payload });
|
||||
return;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue