fe / serverSentEvents.js
3v324v23's picture
Add new translation keys and refactor service initialization for improved localization
889ef45
import { completedIcon, pendingIcon, processingIcon } from './constants.js';
import { apiBaseUrl, clientId, estTimeStep1, estTimeStep2 } from './main.js';
import { t, onLanguageChange } from './i18n.js';
const statusFaceCheckElement = document.getElementById('status-face-check');
const baseServices = [
{
id: 1,
name: 'Geolocation',
displayKey: 'serviceGeolocation',
},
{
id: 2,
name: 'Timestamp',
displayKey: 'serviceTimestamp',
},
{
id: 3,
name: 'AIGVDetection',
displayKey: 'serviceAIGVDetection',
},
{
id: 4,
name: 'Report',
displayKey: 'serviceReport',
},
];
const createInitData = () =>
baseServices.map((service) => ({
...service,
status: 'pending',
percent: 0,
}));
export let statusStep1 = 'pending';
export let statusStep2 = 'pending';
let _estTimeStep1 = 0;
let _estTimeStep2 = 0;
function formatText(key, params = {}) {
let value = t(key) || '';
Object.entries(params).forEach(([k, v]) => {
value = value.replace(`{${k}}`, v);
});
return value;
}
function getDisplayName(item) {
if (item.displayKey) {
const translated = t(item.displayKey);
if (translated) return translated;
}
return item.displayName || item.name || '';
}
function renderStatus(newValue, countItem, countItemCompleted) {
const percent = Math.round((countItemCompleted / countItem) * 100);
const step1Message =
statusStep1 === 'pending'
? `${t('waitingLabel')} - ${
_estTimeStep1 === 0
? t('calculatingLabel')
: formatText('minutesRemaining', { minutes: _estTimeStep1 })
}`
: statusStep1 === 'processing'
? formatText('processingEstimate', { minutes: _estTimeStep1 })
: t('completedLabel');
const step2Message =
statusStep2 === 'pending'
? `${t('waitingLabel')} - ${
_estTimeStep2 === 0
? t('calculatingLabel')
: formatText('beginsAfterStep1', { minutes: _estTimeStep2 })
}`
: statusStep2 === 'processing'
? formatText('processingEstimate', { minutes: _estTimeStep2 })
: t('completedLabel');
return `<div class="status-face-check">
<div class="header-status-face-check">
<div class="title-status-face-check">
<p>${formatText('statusHeaderCompleted', {
completed: countItemCompleted,
total: countItem,
})}</p>
<p>${formatText('statusPercent', { percent })}</p>
</div>
<div class="wrapper-processing">
<div class="processing" style="width: ${percent}%;"></div>
</div>
</div>
<div class="body-status-face-check">
${newValue
.map((item, index) => {
return `
${
index === 0
? `<div class="step">${formatText('stepTitle', {
step: 1,
})} ${step1Message}</div>`
: ''
}
${
index === 3
? `<div class="step">${formatText('stepTitle', {
step: 2,
})} ${step2Message} </div>`
: ''
}
<div class="item-status-face-check">
<span
>${
item.status === 'completed'
? completedIcon
: item.status === 'processing'
? processingIcon
: pendingIcon
}
</span>
<div class="content-status-face-check">
<p class="name-service">${formatText('serviceLabel', {
index: index + 1,
total: countItem,
name: getDisplayName(item),
})}</p>
<p class="status-service ${
item.status === 'pending'
? 'display-none'
: item.status === 'processing'
? 'processing-dots'
: ''
}">${
item.status === 'processing'
? t('statusProcessing')
: item.status === 'completed'
? t('statusCompleted')
: t('statusPending')
}<span class="dots "></span></p>
</div>
</div>`;
})
.join('')}
</div>
</div>`;
}
let data = {};
Object.defineProperty(data, 'value', {
set(newValue) {
const countItem = newValue.length;
const countItemCompleted = newValue.filter(
(item) => item.status === 'completed'
).length;
statusStep1 = 'pending';
statusStep2 = newValue.find((i) => i.name === 'Report').status;
if (
['Geolocation', 'Timestamp', 'AIGVDetection'].every((item) => {
return newValue.find((i) => i.name === item).status === 'completed';
})
) {
statusStep1 = 'completed';
} else if (
['Geolocation', 'Timestamp', 'AIGVDetection'].some((item) => {
return newValue.find((i) => i.name === item).status === 'processing';
})
) {
statusStep1 = 'processing';
}
const content = renderStatus(newValue, countItem, countItemCompleted);
statusFaceCheckElement.innerHTML = content;
this._value = newValue;
},
get() {
return this._value;
},
});
data.value = createInitData();
onLanguageChange(() => {
if (data.value) {
data.value = data.value;
}
});
export function initValueData() {
console.log('test...');
}
let isLoading = {};
Object.defineProperty(isLoading, 'value', {
set(newValue) {
if (newValue) {
// statusFaceCheckElement.classList.remove('display-none');
data.value = createInitData();
} else {
statusFaceCheckElement.classList.add('display-none');
data.value = createInitData();
}
this._value = newValue;
},
get() {
return this._value;
},
});
const source = new EventSource(apiBaseUrl + `v1/sse?client_id=${clientId}`);
source.onopen = () => console.log('✅ SSE connected');
let _interval;
source.onmessage = (event) => {
try {
const _data = JSON.parse(event.data);
console.log(_data);
if (_data?.status) {
if (_data.status === 'start') {
isLoading.value = true;
_estTimeStep1 = estTimeStep1;
_estTimeStep2 = estTimeStep2;
_interval = setInterval(() => {
if (statusStep1 === 'processing' && _estTimeStep1 > 1) {
_estTimeStep1 -= 1;
}
if (statusStep2 === 'processing' && _estTimeStep2 > 1) {
_estTimeStep2 -= 1;
}
data.value = data.value;
}, 60000);
} else {
setTimeout(() => {
isLoading.value = false;
_estTimeStep1 = 0;
_estTimeStep2 = 0;
data.value = createInitData();
clearInterval(_interval);
}, 2500);
}
}
if (_data?.status_service) {
const updatedData = data.value?.map((item) => {
if (_data.service === item.name) {
if (_data.status_service === 'completed') {
return { ...item, status: _data.status_service, percent: 100 };
}
return { ...item, status: _data.status_service };
}
return item;
});
data.value = updatedData;
}
} catch (err) {
console.log('err', err);
}
};
source.onerror = (err) => {
console.error('❌ SSE error:', err);
};