Spaces:
Running
Running
Commit ·
aa22dac
0
Parent(s):
deloy prj on hf
Browse files- .gitignore +1 -0
- .prettierrc +10 -0
- Dockerfile +9 -0
- constants.js +81 -0
- default.conf +9 -0
- feedback.js +239 -0
- index.html +788 -0
- main.js +948 -0
- serverSentEvents.js +225 -0
- styles.css +954 -0
.gitignore
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
.env
|
.prettierrc
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"semi": true,
|
| 3 |
+
"singleQuote": true,
|
| 4 |
+
"tabWidth": 2,
|
| 5 |
+
"useTabs": false,
|
| 6 |
+
"trailingComma": "es5",
|
| 7 |
+
"printWidth": 80,
|
| 8 |
+
"bracketSpacing": true,
|
| 9 |
+
"arrowParens": "always"
|
| 10 |
+
}
|
Dockerfile
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
FROM nginx:alpine
|
| 2 |
+
|
| 3 |
+
RUN rm /etc/nginx/conf.d/default.conf
|
| 4 |
+
|
| 5 |
+
COPY default.conf /etc/nginx/conf.d/default.conf
|
| 6 |
+
|
| 7 |
+
COPY . /usr/share/nginx/html
|
| 8 |
+
|
| 9 |
+
EXPOSE 7860
|
constants.js
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
export const closeIcon = `<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
|
| 2 |
+
<path d="M14 26L26 14M14 14L26 26" stroke="#9BA5B7" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
| 3 |
+
</svg>`;
|
| 4 |
+
export const fileIcon = `<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
|
| 5 |
+
<rect width="40" height="40" rx="20" fill="#FFF6EE"/>
|
| 6 |
+
<path fill-rule="evenodd" clip-rule="evenodd" d="M22.5383 11.8375L26.9133 16.2125C27.0889 16.3883 27.1875 16.6266 27.1875 16.875V26.875C27.1875 27.2894 27.0229 27.6868 26.7299 27.9799C26.4368 28.2729 26.0394 28.4375 25.625 28.4375H14.375C13.9606 28.4375 13.5632 28.2729 13.2701 27.9799C12.9771 27.6868 12.8125 27.2894 12.8125 26.875V13.125C12.8125 12.7106 12.9771 12.3132 13.2701 12.0201C13.5632 11.7271 13.9606 11.5625 14.375 11.5625H21.875C21.9982 11.5625 22.1202 11.5868 22.234 11.634C22.3478 11.6812 22.4512 11.7503 22.5383 11.8375ZM22.5 16.25H24.2969L22.5 14.4531V16.25ZM14.6875 13.4375V26.5625H25.3125V18.125H21.5625C21.3139 18.125 21.0754 18.0262 20.8996 17.8504C20.7238 17.6746 20.625 17.4361 20.625 17.1875V13.4375H14.6875Z" fill="#FD7E14"/>
|
| 7 |
+
</svg>`;
|
| 8 |
+
export const eyeIcon = `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
| 9 |
+
<path d="M2.06202 12.348C1.97868 12.1235 1.97868 11.8765 2.06202 11.652C2.87372 9.68385 4.25153 8.00103 6.02079 6.81689C7.79004 5.63275 9.87106 5.00061 12 5.00061C14.129 5.00061 16.21 5.63275 17.9792 6.81689C19.7485 8.00103 21.1263 9.68385 21.938 11.652C22.0214 11.8765 22.0214 12.1235 21.938 12.348C21.1263 14.3161 19.7485 15.999 17.9792 17.1831C16.21 18.3672 14.129 18.9994 12 18.9994C9.87106 18.9994 7.79004 18.3672 6.02079 17.1831C4.25153 15.999 2.87372 14.3161 2.06202 12.348Z" stroke="#454545" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
| 10 |
+
<path d="M12 15C13.6569 15 15 13.6569 15 12C15 10.3431 13.6569 9 12 9C10.3431 9 9 10.3431 9 12C9 13.6569 10.3431 15 12 15Z" stroke="#454545" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
| 11 |
+
</svg>`;
|
| 12 |
+
export const trashIcon = `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
| 13 |
+
<path d="M20.25 4.5H3.75C3.45163 4.5 3.16548 4.61853 2.9545 4.8295C2.74353 5.04048 2.625 5.32663 2.625 5.625C2.625 5.92337 2.74353 6.20952 2.9545 6.4205C3.16548 6.63147 3.45163 6.75 3.75 6.75H4.125V19.5C4.125 19.9973 4.32254 20.4742 4.67417 20.8258C5.02581 21.1775 5.50272 21.375 6 21.375H18C18.4973 21.375 18.9742 21.1775 19.3258 20.8258C19.6775 20.4742 19.875 19.9973 19.875 19.5V6.75H20.25C20.5484 6.75 20.8345 6.63147 21.0455 6.4205C21.2565 6.20952 21.375 5.92337 21.375 5.625C21.375 5.32663 21.2565 5.04048 21.0455 4.8295C20.8345 4.61853 20.5484 4.5 20.25 4.5ZM17.625 19.125H6.375V6.75H17.625V19.125ZM7.125 1.875C7.125 1.57663 7.24353 1.29048 7.4545 1.0795C7.66548 0.868526 7.95163 0.75 8.25 0.75H15.75C16.0484 0.75 16.3345 0.868526 16.5455 1.0795C16.7565 1.29048 16.875 1.57663 16.875 1.875C16.875 2.17337 16.7565 2.45952 16.5455 2.6705C16.3345 2.88147 16.0484 3 15.75 3H8.25C7.95163 3 7.66548 2.88147 7.4545 2.6705C7.24353 2.45952 7.125 2.17337 7.125 1.875Z" fill="#FF5773"/>
|
| 14 |
+
</svg>`;
|
| 15 |
+
export const completedIcon = `<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
| 16 |
+
<g clip-path="url(#clip0_89_885)">
|
| 17 |
+
<path d="M10.0001 18.3333C14.6025 18.3333 18.3334 14.6024 18.3334 9.99999C18.3334 5.39762 14.6025 1.66666 10.0001 1.66666C5.39771 1.66666 1.66675 5.39762 1.66675 9.99999C1.66675 14.6024 5.39771 18.3333 10.0001 18.3333Z" stroke="#4BED73" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
| 18 |
+
<path d="M6.66675 10L9.16675 12.5L14.1667 7.5" stroke="#4BED73" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
| 19 |
+
</g>
|
| 20 |
+
<defs>
|
| 21 |
+
<clipPath id="clip0_89_885">
|
| 22 |
+
<rect width="20" height="20" fill="white"/>
|
| 23 |
+
</clipPath>
|
| 24 |
+
</defs>
|
| 25 |
+
</svg>`;
|
| 26 |
+
export const processingIcon = `<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
| 27 |
+
<g clip-path="url(#clip0_89_954)">
|
| 28 |
+
<path d="M10 5V10L11.3 10.65" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
| 29 |
+
<path d="M11.0224 18.2708C9.33144 18.48 7.61704 18.1657 6.11018 17.3705C4.60332 16.5753 3.37643 15.3373 2.59482 13.8233C1.81322 12.3093 1.51447 10.5922 1.73883 8.90317C1.96319 7.21419 2.69988 5.63456 3.84963 4.37715C4.99937 3.11973 6.50692 2.24496 8.16913 1.8707C9.83134 1.49645 11.5683 1.64069 13.146 2.28401C14.7237 2.92732 16.0663 4.03879 16.9929 5.46863C17.9195 6.89848 18.3855 8.57798 18.3282 10.2808" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
| 30 |
+
<path d="M11.6667 15L15.0001 11.6667L18.3334 15" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
| 31 |
+
<path d="M15 18.3334V11.6667" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
| 32 |
+
</g>
|
| 33 |
+
<defs>
|
| 34 |
+
<clipPath id="clip0_89_954">
|
| 35 |
+
<rect width="20" height="20" fill="white"/>
|
| 36 |
+
</clipPath>
|
| 37 |
+
</defs>
|
| 38 |
+
</svg>`;
|
| 39 |
+
export const pendingIcon = `<svg
|
| 40 |
+
width="20"
|
| 41 |
+
height="20"
|
| 42 |
+
viewBox="0 0 20 20"
|
| 43 |
+
fill="none"
|
| 44 |
+
xmlns="http://www.w3.org/2000/svg"
|
| 45 |
+
>
|
| 46 |
+
<g clip-path="url(#clip0_89_973)">
|
| 47 |
+
<path
|
| 48 |
+
d="M10.0001 18.3334C14.6025 18.3334 18.3334 14.6024 18.3334 10C18.3334 5.39765 14.6025 1.66669 10.0001 1.66669C5.39771 1.66669 1.66675 5.39765 1.66675 10C1.66675 14.6024 5.39771 18.3334 10.0001 18.3334Z"
|
| 49 |
+
stroke="white"
|
| 50 |
+
stroke-width="1.5"
|
| 51 |
+
stroke-linecap="round"
|
| 52 |
+
stroke-linejoin="round"
|
| 53 |
+
/>
|
| 54 |
+
</g>
|
| 55 |
+
<defs>
|
| 56 |
+
<clipPath id="clip0_89_973">
|
| 57 |
+
<rect width="20" height="20" fill="white" />
|
| 58 |
+
</clipPath>
|
| 59 |
+
</defs>
|
| 60 |
+
</svg>`;
|
| 61 |
+
export const confirmIcon = `<svg
|
| 62 |
+
width="48"
|
| 63 |
+
height="48"
|
| 64 |
+
viewBox="0 0 48 48"
|
| 65 |
+
fill="none"
|
| 66 |
+
xmlns="http://www.w3.org/2000/svg"
|
| 67 |
+
>
|
| 68 |
+
<rect
|
| 69 |
+
width="48"
|
| 70 |
+
height="48"
|
| 71 |
+
rx="24"
|
| 72 |
+
fill="#00BC7D"
|
| 73 |
+
fill-opacity="0.16"
|
| 74 |
+
/>
|
| 75 |
+
<path
|
| 76 |
+
fill-rule="evenodd"
|
| 77 |
+
clip-rule="evenodd"
|
| 78 |
+
d="M24 15C19.0294 15 15 19.0294 15 24C15 28.9706 19.0294 33 24 33C28.9706 33 33 28.9706 33 24C33 19.0294 28.9706 15 24 15ZM13 24C13 17.9249 17.9249 13 24 13C30.0751 13 35 17.9249 35 24C35 30.0751 30.0751 35 24 35C17.9249 35 13 30.0751 13 24ZM29.2071 20.2929C29.5976 20.6834 29.5976 21.3166 29.2071 21.7071L23.2071 27.7071C22.8166 28.0976 22.1834 28.0976 21.7929 27.7071L18.7929 24.7071C18.4024 24.3166 18.4024 23.6834 18.7929 23.2929C19.1834 22.9024 19.8166 22.9024 20.2071 23.2929L22.5 25.5858L27.7929 20.2929C28.1834 19.9024 28.8166 19.9024 29.2071 20.2929Z"
|
| 79 |
+
fill="#00BC7D"
|
| 80 |
+
/>
|
| 81 |
+
</svg>`;
|
default.conf
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
server {
|
| 2 |
+
listen 8004;
|
| 3 |
+
server_name localhost;
|
| 4 |
+
|
| 5 |
+
location / {
|
| 6 |
+
root /usr/share/nginx/html;
|
| 7 |
+
index index.html;
|
| 8 |
+
}
|
| 9 |
+
}
|
feedback.js
ADDED
|
@@ -0,0 +1,239 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { confirmIcon } from './constants.js';
|
| 2 |
+
import { apiBaseUrl } from './main.js';
|
| 3 |
+
const feedbackButton = document.getElementById('btn-feedback-face-check');
|
| 4 |
+
const closeFeedback = document.getElementById('close-feedback-1765253515534');
|
| 5 |
+
const liveChat = document.getElementById('live-chat-1765253515534');
|
| 6 |
+
const feedbackContainer = document.getElementById('feedback-1765253515534');
|
| 7 |
+
const blockNotification = document.getElementById(
|
| 8 |
+
'block-notification-1765336324468'
|
| 9 |
+
);
|
| 10 |
+
const blockQuestion = document.getElementById('block-question-1765336324468');
|
| 11 |
+
const footerFeedback = document.getElementById('footer-feedback-1765336324468');
|
| 12 |
+
const isSuccess = {};
|
| 13 |
+
const isLoading = {};
|
| 14 |
+
const isFailed = {};
|
| 15 |
+
Object.defineProperty(isSuccess, 'value', {
|
| 16 |
+
set(newValue) {
|
| 17 |
+
if (newValue) {
|
| 18 |
+
if (blockNotification) {
|
| 19 |
+
const contentSentSuccessfully = `<div class="notification-content-feedback">
|
| 20 |
+
${confirmIcon}
|
| 21 |
+
<p class="notification-message">Feedback sent successfully!</p>
|
| 22 |
+
</div>`;
|
| 23 |
+
blockNotification.innerHTML = contentSentSuccessfully;
|
| 24 |
+
blockNotification.classList.remove('display-none');
|
| 25 |
+
feedbackButton.disabled = true;
|
| 26 |
+
feedbackButton.classList.add('btn-disabled');
|
| 27 |
+
footerFeedback.classList.add('display-none');
|
| 28 |
+
}
|
| 29 |
+
|
| 30 |
+
if (blockQuestion) {
|
| 31 |
+
blockQuestion.classList.add('display-none');
|
| 32 |
+
}
|
| 33 |
+
} else {
|
| 34 |
+
blockNotification.classList.add('display-none');
|
| 35 |
+
blockQuestion.classList.remove('display-none');
|
| 36 |
+
blockNotification.innerHTML = '';
|
| 37 |
+
feedbackButton.disabled = false;
|
| 38 |
+
feedbackButton.classList.remove('btn-disabled');
|
| 39 |
+
footerFeedback.classList.remove('display-none');
|
| 40 |
+
}
|
| 41 |
+
this._value = newValue;
|
| 42 |
+
},
|
| 43 |
+
get() {
|
| 44 |
+
return this._value;
|
| 45 |
+
},
|
| 46 |
+
});
|
| 47 |
+
Object.defineProperty(isLoading, 'value', {
|
| 48 |
+
set(newValue) {
|
| 49 |
+
if (newValue) {
|
| 50 |
+
feedbackButton.disabled = true;
|
| 51 |
+
feedbackButton.classList.add('btn-disabled');
|
| 52 |
+
feedbackButton.innerText = 'Sending...';
|
| 53 |
+
} else {
|
| 54 |
+
feedbackButton.disabled = false;
|
| 55 |
+
feedbackButton.classList.remove('btn-disabled');
|
| 56 |
+
feedbackButton.innerText = 'Send';
|
| 57 |
+
}
|
| 58 |
+
this._value = newValue;
|
| 59 |
+
},
|
| 60 |
+
get() {
|
| 61 |
+
return this._value;
|
| 62 |
+
},
|
| 63 |
+
});
|
| 64 |
+
|
| 65 |
+
function resetFeedbackForm() {
|
| 66 |
+
const radios = document.querySelectorAll('.question input[type="radio"]');
|
| 67 |
+
const textareas = document.querySelectorAll('.feedback textarea');
|
| 68 |
+
const stars = document.querySelectorAll('.options-stars .star');
|
| 69 |
+
|
| 70 |
+
radios.forEach((radio) => {
|
| 71 |
+
radio.checked = false;
|
| 72 |
+
});
|
| 73 |
+
|
| 74 |
+
textareas.forEach((textarea) => {
|
| 75 |
+
textarea.value = '';
|
| 76 |
+
});
|
| 77 |
+
|
| 78 |
+
stars.forEach((star) => {
|
| 79 |
+
star.classList.remove('active');
|
| 80 |
+
});
|
| 81 |
+
}
|
| 82 |
+
|
| 83 |
+
function assignRadioValues() {
|
| 84 |
+
const questions = document.querySelectorAll('.question');
|
| 85 |
+
|
| 86 |
+
questions.forEach((question) => {
|
| 87 |
+
const questionId = question.id;
|
| 88 |
+
const options = question.querySelectorAll('.option');
|
| 89 |
+
|
| 90 |
+
options.forEach((option, index) => {
|
| 91 |
+
const radio = option.querySelector('input[type="radio"]');
|
| 92 |
+
if (!radio) return;
|
| 93 |
+
|
| 94 |
+
if (questionId === 'q0') {
|
| 95 |
+
radio.value = String(index + 1);
|
| 96 |
+
} else {
|
| 97 |
+
const optionText = option
|
| 98 |
+
.querySelector('.option-text')
|
| 99 |
+
?.textContent?.trim();
|
| 100 |
+
if (optionText) {
|
| 101 |
+
radio.value = optionText;
|
| 102 |
+
} else if (!radio.value) {
|
| 103 |
+
radio.value = String(index + 1);
|
| 104 |
+
}
|
| 105 |
+
}
|
| 106 |
+
});
|
| 107 |
+
});
|
| 108 |
+
}
|
| 109 |
+
|
| 110 |
+
function initializeStarRatings() {
|
| 111 |
+
const starGroups = document.querySelectorAll('.options-stars');
|
| 112 |
+
|
| 113 |
+
starGroups.forEach((group) => {
|
| 114 |
+
const options = Array.from(group.querySelectorAll('.option'));
|
| 115 |
+
const radios = options
|
| 116 |
+
.map((option) => option.querySelector('input[type="radio"]'))
|
| 117 |
+
.filter(Boolean);
|
| 118 |
+
const stars = options
|
| 119 |
+
.map((option) => option.querySelector('.star'))
|
| 120 |
+
.filter(Boolean);
|
| 121 |
+
|
| 122 |
+
const updateStars = (activeIndex) => {
|
| 123 |
+
stars.forEach((star, index) => {
|
| 124 |
+
star.classList.toggle('active', index <= activeIndex);
|
| 125 |
+
});
|
| 126 |
+
};
|
| 127 |
+
|
| 128 |
+
if (radios.length === 0 || stars.length === 0) {
|
| 129 |
+
return;
|
| 130 |
+
}
|
| 131 |
+
|
| 132 |
+
radios.forEach((radio, index) => {
|
| 133 |
+
radio.value = radio.value || String(index + 1);
|
| 134 |
+
radio.addEventListener('change', () => {
|
| 135 |
+
updateStars(index);
|
| 136 |
+
});
|
| 137 |
+
|
| 138 |
+
if (radio.checked) {
|
| 139 |
+
updateStars(index);
|
| 140 |
+
}
|
| 141 |
+
});
|
| 142 |
+
|
| 143 |
+
if (!radios.some((radio) => radio.checked)) {
|
| 144 |
+
updateStars(-1);
|
| 145 |
+
}
|
| 146 |
+
});
|
| 147 |
+
}
|
| 148 |
+
|
| 149 |
+
function collectFeedbackData() {
|
| 150 |
+
const responses = {};
|
| 151 |
+
const questions = document.querySelectorAll('.question');
|
| 152 |
+
|
| 153 |
+
questions.forEach((question) => {
|
| 154 |
+
if (!question.id) return;
|
| 155 |
+
const radios = question.querySelectorAll('input[type="radio"]');
|
| 156 |
+
const selected = question.querySelector('input[type="radio"]:checked');
|
| 157 |
+
const textarea = question.querySelector('textarea');
|
| 158 |
+
|
| 159 |
+
if (radios.length > 0) {
|
| 160 |
+
if (!selected) {
|
| 161 |
+
responses[question.id] = '-';
|
| 162 |
+
} else {
|
| 163 |
+
let value = selected.value;
|
| 164 |
+
if (!value) {
|
| 165 |
+
const optionText = selected
|
| 166 |
+
.closest('.opt')
|
| 167 |
+
?.querySelector('.option-text')
|
| 168 |
+
?.textContent?.trim();
|
| 169 |
+
value = optionText || '-';
|
| 170 |
+
}
|
| 171 |
+
responses[question.id] = value;
|
| 172 |
+
}
|
| 173 |
+
|
| 174 |
+
if (textarea) {
|
| 175 |
+
const textValue = textarea.value.trim();
|
| 176 |
+
responses[`${question.id}_comments`] = textValue || '-';
|
| 177 |
+
}
|
| 178 |
+
} else if (textarea) {
|
| 179 |
+
const textValue = textarea.value.trim();
|
| 180 |
+
responses[question.id] = textValue || '-';
|
| 181 |
+
} else {
|
| 182 |
+
responses[question.id] = '-';
|
| 183 |
+
}
|
| 184 |
+
});
|
| 185 |
+
|
| 186 |
+
return responses;
|
| 187 |
+
}
|
| 188 |
+
|
| 189 |
+
async function handleFeedbackSubmit(event) {
|
| 190 |
+
event.preventDefault();
|
| 191 |
+
isLoading.value = true;
|
| 192 |
+
// Simulate async feedback submission
|
| 193 |
+
const feedbackData = collectFeedbackData();
|
| 194 |
+
|
| 195 |
+
axios
|
| 196 |
+
.post(`${apiBaseUrl}/survey`, feedbackData, {
|
| 197 |
+
headers: {
|
| 198 |
+
'Content-Type': 'application/json',
|
| 199 |
+
},
|
| 200 |
+
})
|
| 201 |
+
.then((response) => {
|
| 202 |
+
console.log('Feedback submitted successfully:', response.data);
|
| 203 |
+
isLoading.value = false;
|
| 204 |
+
resetFeedbackForm();
|
| 205 |
+
isSuccess.value = true;
|
| 206 |
+
})
|
| 207 |
+
.catch((error) => {
|
| 208 |
+
console.error('Error submitting feedback:', error);
|
| 209 |
+
isLoading.value = false;
|
| 210 |
+
isFailed.value = true;
|
| 211 |
+
});
|
| 212 |
+
}
|
| 213 |
+
|
| 214 |
+
assignRadioValues();
|
| 215 |
+
initializeStarRatings();
|
| 216 |
+
|
| 217 |
+
if (feedbackButton) {
|
| 218 |
+
feedbackButton.addEventListener('click', handleFeedbackSubmit);
|
| 219 |
+
}
|
| 220 |
+
|
| 221 |
+
window.collectFeedbackData = collectFeedbackData;
|
| 222 |
+
|
| 223 |
+
closeFeedback.addEventListener('click', () => {
|
| 224 |
+
if (feedbackContainer) {
|
| 225 |
+
feedbackContainer.classList.add('display-none');
|
| 226 |
+
liveChat.classList.remove('display-none');
|
| 227 |
+
|
| 228 |
+
isLoading.value = false;
|
| 229 |
+
isFailed.value = false;
|
| 230 |
+
isSuccess.value = false;
|
| 231 |
+
}
|
| 232 |
+
});
|
| 233 |
+
|
| 234 |
+
liveChat.addEventListener('click', () => {
|
| 235 |
+
if (feedbackContainer) {
|
| 236 |
+
feedbackContainer.classList.remove('display-none');
|
| 237 |
+
liveChat.classList.add('display-none');
|
| 238 |
+
}
|
| 239 |
+
});
|
index.html
ADDED
|
@@ -0,0 +1,788 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8" />
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
| 6 |
+
<title>News Verification</title>
|
| 7 |
+
<link rel="stylesheet" href="styles.css" />
|
| 8 |
+
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
|
| 9 |
+
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
| 10 |
+
<script
|
| 11 |
+
type="text/javascript"
|
| 12 |
+
src="https://unpkg.com/mediainfo.js"
|
| 13 |
+
></script>
|
| 14 |
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js"></script>
|
| 15 |
+
</head>
|
| 16 |
+
<body>
|
| 17 |
+
<div class="container">
|
| 18 |
+
<header class="header-face-check">
|
| 19 |
+
<h1 class="title-header-face-check">News Verification</h1>
|
| 20 |
+
</header>
|
| 21 |
+
<main class="main-content">
|
| 22 |
+
<section class="upload-section">
|
| 23 |
+
<div class="wrapper-upload-box scroll-box">
|
| 24 |
+
<div class="upload-box">
|
| 25 |
+
<p>Upload a json file</p>
|
| 26 |
+
<label for="metadata-input" class="drop-zone">
|
| 27 |
+
<div id="wrapper-upload-face-check" class="wrapper-upload">
|
| 28 |
+
<svg
|
| 29 |
+
class="drop-zone-icon"
|
| 30 |
+
xmlns="http://www.w3.org/2000/svg"
|
| 31 |
+
fill="none"
|
| 32 |
+
viewBox="0 0 24 24"
|
| 33 |
+
stroke-width="1.5"
|
| 34 |
+
stroke="currentColor"
|
| 35 |
+
>
|
| 36 |
+
<path
|
| 37 |
+
stroke-linecap="round"
|
| 38 |
+
stroke-linejoin="round"
|
| 39 |
+
d="M3 16.5v2.25A2.25 2.25 0 005.25 21h13.5A2.25 2.25 0 0021 18.75V16.5m-13.5-9L12 3m0 0l4.5 4.5M12 3v13.5"
|
| 40 |
+
/>
|
| 41 |
+
</svg>
|
| 42 |
+
<p>
|
| 43 |
+
<span class="primary-1">Click here</span> to upload your
|
| 44 |
+
file or drag.
|
| 45 |
+
</p>
|
| 46 |
+
<p>Supported Format: JSON</p>
|
| 47 |
+
</div>
|
| 48 |
+
</label>
|
| 49 |
+
<input
|
| 50 |
+
type="file"
|
| 51 |
+
id="metadata-input"
|
| 52 |
+
class="display-none"
|
| 53 |
+
accept=".json"
|
| 54 |
+
/>
|
| 55 |
+
</div>
|
| 56 |
+
<div
|
| 57 |
+
id="wrapper-metadata-face-check"
|
| 58 |
+
class="display-none list-file"
|
| 59 |
+
></div>
|
| 60 |
+
<div class="line"></div>
|
| 61 |
+
<div class="upload-box">
|
| 62 |
+
<p>Upload media files</p>
|
| 63 |
+
<label for="file-input" class="drop-zone">
|
| 64 |
+
<div
|
| 65 |
+
id="wrapper-upload-face-check-media"
|
| 66 |
+
class="wrapper-upload"
|
| 67 |
+
>
|
| 68 |
+
<svg
|
| 69 |
+
class="drop-zone-icon"
|
| 70 |
+
xmlns="http://www.w3.org/2000/svg"
|
| 71 |
+
fill="none"
|
| 72 |
+
viewBox="0 0 24 24"
|
| 73 |
+
stroke-width="1.5"
|
| 74 |
+
stroke="currentColor"
|
| 75 |
+
>
|
| 76 |
+
<path
|
| 77 |
+
stroke-linecap="round"
|
| 78 |
+
stroke-linejoin="round"
|
| 79 |
+
d="M3 16.5v2.25A2.25 2.25 0 005.25 21h13.5A2.25 2.25 0 0021 18.75V16.5m-13.5-9L12 3m0 0l4.5 4.5M12 3v13.5"
|
| 80 |
+
/>
|
| 81 |
+
</svg>
|
| 82 |
+
<p>
|
| 83 |
+
<span class="primary-1">Click here</span> to upload your
|
| 84 |
+
files or drag.
|
| 85 |
+
</p>
|
| 86 |
+
<p>Supported Format: PNG, JPG, JPEG, GIF, MP4</p>
|
| 87 |
+
</div>
|
| 88 |
+
</label>
|
| 89 |
+
<input
|
| 90 |
+
type="file"
|
| 91 |
+
id="file-input"
|
| 92 |
+
class="display-none"
|
| 93 |
+
multiple
|
| 94 |
+
accept=".png,.jpg,.jpeg,.gif,.mp4"
|
| 95 |
+
/>
|
| 96 |
+
</div>
|
| 97 |
+
<div
|
| 98 |
+
id="wrapper-files-face-check"
|
| 99 |
+
class="display-none list-file"
|
| 100 |
+
></div>
|
| 101 |
+
</div>
|
| 102 |
+
<div class="button-group">
|
| 103 |
+
<button id="btn-clear-face-check" class="btn btn-clear">
|
| 104 |
+
Clear
|
| 105 |
+
</button>
|
| 106 |
+
<button id="btn-submit-face-check" class="btn btn-submit">
|
| 107 |
+
Submit
|
| 108 |
+
</button>
|
| 109 |
+
</div>
|
| 110 |
+
</section>
|
| 111 |
+
<section class="output-section">
|
| 112 |
+
<div id="menu-1762855257376" class="menu">
|
| 113 |
+
<div onclick="changeTab('case_summary', this)" class="item active">
|
| 114 |
+
Case Summary
|
| 115 |
+
</div>
|
| 116 |
+
<div
|
| 117 |
+
onclick="changeTab('content_classification', this)"
|
| 118 |
+
class="item"
|
| 119 |
+
>
|
| 120 |
+
Content Classification
|
| 121 |
+
</div>
|
| 122 |
+
<div onclick="changeTab('verified_evidence', this)" class="item">
|
| 123 |
+
Verified Evidence
|
| 124 |
+
</div>
|
| 125 |
+
<div onclick="changeTab('forensic_analysis', this)" class="item">
|
| 126 |
+
Forensic Analysis
|
| 127 |
+
</div>
|
| 128 |
+
<div
|
| 129 |
+
id="other_evidence-1763023210526"
|
| 130 |
+
onclick="changeTab('other_evidence', this)"
|
| 131 |
+
class="item"
|
| 132 |
+
>
|
| 133 |
+
Other Evidence & Findings
|
| 134 |
+
</div>
|
| 135 |
+
</div>
|
| 136 |
+
<div class="output-wrapper">
|
| 137 |
+
<div id="output-area" class="output-face-check scroll-box">
|
| 138 |
+
<p class="opacity-50">Verification result comes here...</p>
|
| 139 |
+
</div>
|
| 140 |
+
<div
|
| 141 |
+
id="status-face-check"
|
| 142 |
+
class="wrapper-status-face-check display-none"
|
| 143 |
+
></div>
|
| 144 |
+
</div>
|
| 145 |
+
<div class="wrapper-btn-save">
|
| 146 |
+
<button id="btn-save-face-check" class="btn btn-save">Save</button>
|
| 147 |
+
</div>
|
| 148 |
+
</section>
|
| 149 |
+
<section id="feedback-1765253515534" class="feedback display-none">
|
| 150 |
+
<div class="header-feedback">
|
| 151 |
+
<p class="content-header-feedback">Help us improve!</p>
|
| 152 |
+
<div id="close-feedback-1765253515534" class="close-feedback">
|
| 153 |
+
<svg
|
| 154 |
+
width="40"
|
| 155 |
+
height="40"
|
| 156 |
+
viewBox="0 0 40 40"
|
| 157 |
+
fill="none"
|
| 158 |
+
xmlns="http://www.w3.org/2000/svg"
|
| 159 |
+
>
|
| 160 |
+
<path
|
| 161 |
+
d="M14 26L26 14M14 14L26 26"
|
| 162 |
+
stroke="#38393E"
|
| 163 |
+
stroke-width="2"
|
| 164 |
+
stroke-linecap="round"
|
| 165 |
+
stroke-linejoin="round"
|
| 166 |
+
/>
|
| 167 |
+
</svg>
|
| 168 |
+
</div>
|
| 169 |
+
</div>
|
| 170 |
+
<div class="content-feedback">
|
| 171 |
+
<div id="block-question-1765336324468" class="flex flex-col gap-6">
|
| 172 |
+
<div class="question" id="q0">
|
| 173 |
+
<div class="q-title">How accurate was the conclusion?</div>
|
| 174 |
+
<div class="options-stars">
|
| 175 |
+
<div class="option">
|
| 176 |
+
<label class="opt">
|
| 177 |
+
<input type="radio" name="q0" />
|
| 178 |
+
<div class="star"></div>
|
| 179 |
+
</label>
|
| 180 |
+
</div>
|
| 181 |
+
<div class="option">
|
| 182 |
+
<label class="opt">
|
| 183 |
+
<input type="radio" name="q0" />
|
| 184 |
+
<div class="star"></div>
|
| 185 |
+
</label>
|
| 186 |
+
</div>
|
| 187 |
+
<div class="option">
|
| 188 |
+
<label class="opt">
|
| 189 |
+
<input type="radio" name="q0" />
|
| 190 |
+
<div class="star"></div>
|
| 191 |
+
</label>
|
| 192 |
+
</div>
|
| 193 |
+
<div class="option">
|
| 194 |
+
<label class="opt">
|
| 195 |
+
<input type="radio" name="q0" />
|
| 196 |
+
<div class="star"></div>
|
| 197 |
+
</label>
|
| 198 |
+
</div>
|
| 199 |
+
<div class="option">
|
| 200 |
+
<label class="opt">
|
| 201 |
+
<input type="radio" name="q0" />
|
| 202 |
+
<div class="star"></div>
|
| 203 |
+
</label>
|
| 204 |
+
</div>
|
| 205 |
+
</div>
|
| 206 |
+
</div>
|
| 207 |
+
<div class="question" id="q1">
|
| 208 |
+
<div class="q-title">
|
| 209 |
+
Did the algorithm correctly classify this content?
|
| 210 |
+
</div>
|
| 211 |
+
<div class="options">
|
| 212 |
+
<div class="option">
|
| 213 |
+
<label class="opt">
|
| 214 |
+
<input type="radio" name="q1" />
|
| 215 |
+
<div class="radio"></div>
|
| 216 |
+
<div class="option-text">Yes</div>
|
| 217 |
+
</label>
|
| 218 |
+
</div>
|
| 219 |
+
<div class="option">
|
| 220 |
+
<label class="opt">
|
| 221 |
+
<input type="radio" name="q1" />
|
| 222 |
+
<div class="radio"></div>
|
| 223 |
+
<div class="option-text">No - It was actually Real</div>
|
| 224 |
+
</label>
|
| 225 |
+
</div>
|
| 226 |
+
<div class="option">
|
| 227 |
+
<label class="opt">
|
| 228 |
+
<input type="radio" name="q1" />
|
| 229 |
+
<div class="radio"></div>
|
| 230 |
+
<div class="option-text">
|
| 231 |
+
No - It was actually AI-Generated
|
| 232 |
+
</div>
|
| 233 |
+
</label>
|
| 234 |
+
</div>
|
| 235 |
+
</div>
|
| 236 |
+
</div>
|
| 237 |
+
<div class="question" id="q2">
|
| 238 |
+
<div class="q-title">Were the provided links relevant?</div>
|
| 239 |
+
<div class="options links-list">
|
| 240 |
+
<div class="option">
|
| 241 |
+
<label class="opt">
|
| 242 |
+
<input type="radio" name="q2" />
|
| 243 |
+
<div class="radio"></div>
|
| 244 |
+
<div class="option-text">
|
| 245 |
+
✅ All links worked and were relevant
|
| 246 |
+
</div>
|
| 247 |
+
</label>
|
| 248 |
+
</div>
|
| 249 |
+
<div class="option">
|
| 250 |
+
<label class="opt">
|
| 251 |
+
<input type="radio" name="q2" />
|
| 252 |
+
<div class="radio"></div>
|
| 253 |
+
<div class="option-text">
|
| 254 |
+
⚠️ Some links were broken/dead
|
| 255 |
+
</div>
|
| 256 |
+
</label>
|
| 257 |
+
</div>
|
| 258 |
+
<div class="option">
|
| 259 |
+
<label class="opt">
|
| 260 |
+
<input class="radio" type="radio" name="q2" />
|
| 261 |
+
<div class="radio"></div>
|
| 262 |
+
<div class="option-text">
|
| 263 |
+
❌ Links were unrelated to the topic
|
| 264 |
+
</div>
|
| 265 |
+
</label>
|
| 266 |
+
</div>
|
| 267 |
+
</div>
|
| 268 |
+
</div>
|
| 269 |
+
<div class="question" id="q3">
|
| 270 |
+
<div class="q-title">
|
| 271 |
+
Was the technical analysis understandable?
|
| 272 |
+
</div>
|
| 273 |
+
<div class="options links-list">
|
| 274 |
+
<div class="option">
|
| 275 |
+
<label class="opt">
|
| 276 |
+
<input type="radio" name="q3" />
|
| 277 |
+
<div class="radio"></div>
|
| 278 |
+
<div class="option-text">Yes, clear</div></label
|
| 279 |
+
>
|
| 280 |
+
</div>
|
| 281 |
+
<div class="option">
|
| 282 |
+
<label class="opt">
|
| 283 |
+
<input type="radio" name="q3" />
|
| 284 |
+
<div class="radio"></div>
|
| 285 |
+
<div class="option-text">
|
| 286 |
+
Too technical / Hard to understand
|
| 287 |
+
</div>
|
| 288 |
+
</label>
|
| 289 |
+
</div>
|
| 290 |
+
<div class="option">
|
| 291 |
+
<label class="opt">
|
| 292 |
+
<input class="radio" type="radio" name="q3" />
|
| 293 |
+
<div class="radio"></div>
|
| 294 |
+
<div class="option-text">Not enough detail</div>
|
| 295 |
+
</label>
|
| 296 |
+
</div>
|
| 297 |
+
</div>
|
| 298 |
+
</div>
|
| 299 |
+
<div class="question" id="q4">
|
| 300 |
+
<div class="q-title">
|
| 301 |
+
Did the report contain any hallucinations (made-up facts)?
|
| 302 |
+
</div>
|
| 303 |
+
<div class="options links-list">
|
| 304 |
+
<div class="option">
|
| 305 |
+
<label class="opt">
|
| 306 |
+
<input type="radio" name="q4" />
|
| 307 |
+
<div class="radio"></div>
|
| 308 |
+
<div class="option-text">No issues</div></label
|
| 309 |
+
>
|
| 310 |
+
</div>
|
| 311 |
+
<div class="option">
|
| 312 |
+
<label class="opt">
|
| 313 |
+
<input type="radio" name="q4" />
|
| 314 |
+
<div class="radio"></div>
|
| 315 |
+
<div class="option-text">Invented dates/times</div>
|
| 316 |
+
</label>
|
| 317 |
+
</div>
|
| 318 |
+
<div class="option">
|
| 319 |
+
<label class="opt">
|
| 320 |
+
<input class="radio" type="radio" name="q4" />
|
| 321 |
+
<div class="radio"></div>
|
| 322 |
+
<div class="option-text">Invented people/names</div>
|
| 323 |
+
</label>
|
| 324 |
+
</div>
|
| 325 |
+
<div class="option">
|
| 326 |
+
<label class="opt">
|
| 327 |
+
<input class="radio" type="radio" name="q4" />
|
| 328 |
+
<div class="radio"></div>
|
| 329 |
+
<div class="option-text">Citations that don't exist</div>
|
| 330 |
+
</label>
|
| 331 |
+
</div>
|
| 332 |
+
</div>
|
| 333 |
+
</div>
|
| 334 |
+
<!-- 2.4 -->
|
| 335 |
+
<div class="question" id="q5">
|
| 336 |
+
<div class="q-title">
|
| 337 |
+
Did the app clearly identify the main people, organizations,
|
| 338 |
+
or sources involved in the news item?
|
| 339 |
+
</div>
|
| 340 |
+
<div class="options-stars">
|
| 341 |
+
<div class="option">
|
| 342 |
+
<label class="opt">
|
| 343 |
+
<input type="radio" name="q5" />
|
| 344 |
+
<div class="star"></div>
|
| 345 |
+
</label>
|
| 346 |
+
</div>
|
| 347 |
+
<div class="option">
|
| 348 |
+
<label class="opt">
|
| 349 |
+
<input type="radio" name="q5" />
|
| 350 |
+
<div class="star"></div>
|
| 351 |
+
</label>
|
| 352 |
+
</div>
|
| 353 |
+
<div class="option">
|
| 354 |
+
<label class="opt">
|
| 355 |
+
<input type="radio" name="q5" />
|
| 356 |
+
<div class="star"></div>
|
| 357 |
+
</label>
|
| 358 |
+
</div>
|
| 359 |
+
<div class="option">
|
| 360 |
+
<label class="opt">
|
| 361 |
+
<input type="radio" name="q5" />
|
| 362 |
+
<div class="star"></div>
|
| 363 |
+
</label>
|
| 364 |
+
</div>
|
| 365 |
+
<div class="option">
|
| 366 |
+
<label class="opt">
|
| 367 |
+
<input type="radio" name="q5" />
|
| 368 |
+
<div class="star"></div>
|
| 369 |
+
</label>
|
| 370 |
+
</div>
|
| 371 |
+
</div>
|
| 372 |
+
</div>
|
| 373 |
+
<div class="question" id="q6">
|
| 374 |
+
<div class="q-title">
|
| 375 |
+
How accurate did the app's assessment of the credibility of
|
| 376 |
+
the "Who" (e.g., source, expert, witness) seem to you?
|
| 377 |
+
</div>
|
| 378 |
+
<div class="options-stars">
|
| 379 |
+
<div class="option">
|
| 380 |
+
<label class="opt">
|
| 381 |
+
<input type="radio" name="q6" />
|
| 382 |
+
<div class="star"></div>
|
| 383 |
+
</label>
|
| 384 |
+
</div>
|
| 385 |
+
<div class="option">
|
| 386 |
+
<label class="opt">
|
| 387 |
+
<input type="radio" name="q6" />
|
| 388 |
+
<div class="star"></div>
|
| 389 |
+
</label>
|
| 390 |
+
</div>
|
| 391 |
+
<div class="option">
|
| 392 |
+
<label class="opt">
|
| 393 |
+
<input type="radio" name="q6" />
|
| 394 |
+
<div class="star"></div>
|
| 395 |
+
</label>
|
| 396 |
+
</div>
|
| 397 |
+
<div class="option">
|
| 398 |
+
<label class="opt">
|
| 399 |
+
<input type="radio" name="q6" />
|
| 400 |
+
<div class="star"></div>
|
| 401 |
+
</label>
|
| 402 |
+
</div>
|
| 403 |
+
<div class="option">
|
| 404 |
+
<label class="opt">
|
| 405 |
+
<input type="radio" name="q6" />
|
| 406 |
+
<div class="star"></div>
|
| 407 |
+
</label>
|
| 408 |
+
</div>
|
| 409 |
+
</div>
|
| 410 |
+
</div>
|
| 411 |
+
<!-- 2.5 -->
|
| 412 |
+
<div class="question" id="q7">
|
| 413 |
+
<div class="q-title">
|
| 414 |
+
Did the app correctly identify the central claim or action of
|
| 415 |
+
the news story?
|
| 416 |
+
</div>
|
| 417 |
+
<div class="options links-list">
|
| 418 |
+
<div class="option">
|
| 419 |
+
<label class="opt">
|
| 420 |
+
<input type="radio" name="q7" />
|
| 421 |
+
<div class="radio"></div>
|
| 422 |
+
<div class="option-text">Yes</div></label
|
| 423 |
+
>
|
| 424 |
+
</div>
|
| 425 |
+
<div class="option">
|
| 426 |
+
<label class="opt">
|
| 427 |
+
<input type="radio" name="q7" />
|
| 428 |
+
<div class="radio"></div>
|
| 429 |
+
<div class="option-text">No</div>
|
| 430 |
+
</label>
|
| 431 |
+
</div>
|
| 432 |
+
<div class="option">
|
| 433 |
+
<label class="opt">
|
| 434 |
+
<input class="radio" type="radio" name="q7" />
|
| 435 |
+
<div class="radio"></div>
|
| 436 |
+
<div class="option-text">Partially</div>
|
| 437 |
+
</label>
|
| 438 |
+
</div>
|
| 439 |
+
</div>
|
| 440 |
+
</div>
|
| 441 |
+
<div class="question" id="q8">
|
| 442 |
+
<div class="q-title">
|
| 443 |
+
How well did the app's verification evidence address the
|
| 444 |
+
specific details of the "What"?"
|
| 445 |
+
</div>
|
| 446 |
+
<div class="options-stars">
|
| 447 |
+
<div class="option">
|
| 448 |
+
<label class="opt">
|
| 449 |
+
<input type="radio" name="q8" />
|
| 450 |
+
<div class="star"></div>
|
| 451 |
+
</label>
|
| 452 |
+
</div>
|
| 453 |
+
<div class="option">
|
| 454 |
+
<label class="opt">
|
| 455 |
+
<input type="radio" name="q8" />
|
| 456 |
+
<div class="star"></div>
|
| 457 |
+
</label>
|
| 458 |
+
</div>
|
| 459 |
+
<div class="option">
|
| 460 |
+
<label class="opt">
|
| 461 |
+
<input type="radio" name="q8" />
|
| 462 |
+
<div class="star"></div>
|
| 463 |
+
</label>
|
| 464 |
+
</div>
|
| 465 |
+
<div class="option">
|
| 466 |
+
<label class="opt">
|
| 467 |
+
<input type="radio" name="q8" />
|
| 468 |
+
<div class="star"></div>
|
| 469 |
+
</label>
|
| 470 |
+
</div>
|
| 471 |
+
<div class="option">
|
| 472 |
+
<label class="opt">
|
| 473 |
+
<input type="radio" name="q8" />
|
| 474 |
+
<div class="star"></div>
|
| 475 |
+
</label>
|
| 476 |
+
</div>
|
| 477 |
+
</div>
|
| 478 |
+
</div>
|
| 479 |
+
<!-- 2.6 -->
|
| 480 |
+
<div class="question" id="q9">
|
| 481 |
+
<div class="q-title">
|
| 482 |
+
Did the app clearly show when the original event/information
|
| 483 |
+
was first published or occurred?
|
| 484 |
+
</div>
|
| 485 |
+
<div class="options-stars">
|
| 486 |
+
<div class="option">
|
| 487 |
+
<label class="opt">
|
| 488 |
+
<input type="radio" name="q9" />
|
| 489 |
+
<div class="star"></div>
|
| 490 |
+
</label>
|
| 491 |
+
</div>
|
| 492 |
+
<div class="option">
|
| 493 |
+
<label class="opt">
|
| 494 |
+
<input type="radio" name="q9" />
|
| 495 |
+
<div class="star"></div>
|
| 496 |
+
</label>
|
| 497 |
+
</div>
|
| 498 |
+
<div class="option">
|
| 499 |
+
<label class="opt">
|
| 500 |
+
<input type="radio" name="q9" />
|
| 501 |
+
<div class="star"></div>
|
| 502 |
+
</label>
|
| 503 |
+
</div>
|
| 504 |
+
<div class="option">
|
| 505 |
+
<label class="opt">
|
| 506 |
+
<input type="radio" name="q9" />
|
| 507 |
+
<div class="star"></div>
|
| 508 |
+
</label>
|
| 509 |
+
</div>
|
| 510 |
+
<div class="option">
|
| 511 |
+
<label class="opt">
|
| 512 |
+
<input type="radio" name="q9" />
|
| 513 |
+
<div class="star"></div>
|
| 514 |
+
</label>
|
| 515 |
+
</div>
|
| 516 |
+
</div>
|
| 517 |
+
</div>
|
| 518 |
+
<div class="question" id="q10">
|
| 519 |
+
<div class="q-title">
|
| 520 |
+
How useful was the time context (e.g., date, timeline of
|
| 521 |
+
events) in helping you determine the news' authenticity?
|
| 522 |
+
</div>
|
| 523 |
+
<div class="options-stars">
|
| 524 |
+
<div class="option">
|
| 525 |
+
<label class="opt">
|
| 526 |
+
<input type="radio" name="q10" />
|
| 527 |
+
<div class="star"></div>
|
| 528 |
+
</label>
|
| 529 |
+
</div>
|
| 530 |
+
<div class="option">
|
| 531 |
+
<label class="opt">
|
| 532 |
+
<input type="radio" name="q10" />
|
| 533 |
+
<div class="star"></div>
|
| 534 |
+
</label>
|
| 535 |
+
</div>
|
| 536 |
+
<div class="option">
|
| 537 |
+
<label class="opt">
|
| 538 |
+
<input type="radio" name="q10" />
|
| 539 |
+
<div class="star"></div>
|
| 540 |
+
</label>
|
| 541 |
+
</div>
|
| 542 |
+
<div class="option">
|
| 543 |
+
<label class="opt">
|
| 544 |
+
<input type="radio" name="q10" />
|
| 545 |
+
<div class="star"></div>
|
| 546 |
+
</label>
|
| 547 |
+
</div>
|
| 548 |
+
<div class="option">
|
| 549 |
+
<label class="opt">
|
| 550 |
+
<input type="radio" name="q10" />
|
| 551 |
+
<div class="star"></div>
|
| 552 |
+
</label>
|
| 553 |
+
</div>
|
| 554 |
+
</div>
|
| 555 |
+
</div>
|
| 556 |
+
<!-- 2.7 -->
|
| 557 |
+
<div class="question" id="q11">
|
| 558 |
+
<div class="q-title">
|
| 559 |
+
Was the location of the event ("Where") specified and verified
|
| 560 |
+
by the app's output?
|
| 561 |
+
</div>
|
| 562 |
+
<div class="options links-list">
|
| 563 |
+
<div class="option">
|
| 564 |
+
<label class="opt">
|
| 565 |
+
<input type="radio" name="q11" />
|
| 566 |
+
<div class="radio"></div>
|
| 567 |
+
<div class="option-text">Yes</div></label
|
| 568 |
+
>
|
| 569 |
+
</div>
|
| 570 |
+
<div class="option">
|
| 571 |
+
<label class="opt">
|
| 572 |
+
<input type="radio" name="q11" />
|
| 573 |
+
<div class="radio"></div>
|
| 574 |
+
<div class="option-text">No</div>
|
| 575 |
+
</label>
|
| 576 |
+
</div>
|
| 577 |
+
<div class="option">
|
| 578 |
+
<label class="opt">
|
| 579 |
+
<input class="radio" type="radio" name="q11" />
|
| 580 |
+
<div class="radio"></div>
|
| 581 |
+
<div class="option-text">Not Applicable</div>
|
| 582 |
+
</label>
|
| 583 |
+
</div>
|
| 584 |
+
</div>
|
| 585 |
+
</div>
|
| 586 |
+
<div class="question" id="q12">
|
| 587 |
+
<div class="q-title">
|
| 588 |
+
Did the app provide sufficient evidence (e.g., images, maps,
|
| 589 |
+
source addresses) to confirm the "Where" of the event?
|
| 590 |
+
</div>
|
| 591 |
+
<div class="options-stars">
|
| 592 |
+
<div class="option">
|
| 593 |
+
<label class="opt">
|
| 594 |
+
<input type="radio" name="q12" />
|
| 595 |
+
<div class="star"></div>
|
| 596 |
+
</label>
|
| 597 |
+
</div>
|
| 598 |
+
<div class="option">
|
| 599 |
+
<label class="opt">
|
| 600 |
+
<input type="radio" name="q12" />
|
| 601 |
+
<div class="star"></div>
|
| 602 |
+
</label>
|
| 603 |
+
</div>
|
| 604 |
+
<div class="option">
|
| 605 |
+
<label class="opt">
|
| 606 |
+
<input type="radio" name="q12" />
|
| 607 |
+
<div class="star"></div>
|
| 608 |
+
</label>
|
| 609 |
+
</div>
|
| 610 |
+
<div class="option">
|
| 611 |
+
<label class="opt">
|
| 612 |
+
<input type="radio" name="q12" />
|
| 613 |
+
<div class="star"></div>
|
| 614 |
+
</label>
|
| 615 |
+
</div>
|
| 616 |
+
<div class="option">
|
| 617 |
+
<label class="opt">
|
| 618 |
+
<input type="radio" name="q12" />
|
| 619 |
+
<div class="star"></div>
|
| 620 |
+
</label>
|
| 621 |
+
</div>
|
| 622 |
+
</div>
|
| 623 |
+
</div>
|
| 624 |
+
<!-- 2.8 -->
|
| 625 |
+
<div class="question" id="q13">
|
| 626 |
+
<div class="q-title">
|
| 627 |
+
How well did the app explain the motive or purpose behind the
|
| 628 |
+
original claim (e.g., persuasion, profit, error)?
|
| 629 |
+
</div>
|
| 630 |
+
<div class="options-stars">
|
| 631 |
+
<div class="option">
|
| 632 |
+
<label class="opt">
|
| 633 |
+
<input type="radio" name="q13" />
|
| 634 |
+
<div class="star"></div>
|
| 635 |
+
</label>
|
| 636 |
+
</div>
|
| 637 |
+
<div class="option">
|
| 638 |
+
<label class="opt">
|
| 639 |
+
<input type="radio" name="q13" />
|
| 640 |
+
<div class="star"></div>
|
| 641 |
+
</label>
|
| 642 |
+
</div>
|
| 643 |
+
<div class="option">
|
| 644 |
+
<label class="opt">
|
| 645 |
+
<input type="radio" name="q13" />
|
| 646 |
+
<div class="star"></div>
|
| 647 |
+
</label>
|
| 648 |
+
</div>
|
| 649 |
+
<div class="option">
|
| 650 |
+
<label class="opt">
|
| 651 |
+
<input type="radio" name="q13" />
|
| 652 |
+
<div class="star"></div>
|
| 653 |
+
</label>
|
| 654 |
+
</div>
|
| 655 |
+
<div class="option">
|
| 656 |
+
<label class="opt">
|
| 657 |
+
<input type="radio" name="q13" />
|
| 658 |
+
<div class="star"></div>
|
| 659 |
+
</label>
|
| 660 |
+
</div>
|
| 661 |
+
</div>
|
| 662 |
+
</div>
|
| 663 |
+
<div class="question" id="q14">
|
| 664 |
+
<div class="q-title">
|
| 665 |
+
Did the "Why" section help you understand if the news was
|
| 666 |
+
designed to be misleading or biased?
|
| 667 |
+
</div>
|
| 668 |
+
<div class="options links-list">
|
| 669 |
+
<div class="option">
|
| 670 |
+
<label class="opt">
|
| 671 |
+
<input type="radio" name="q14" />
|
| 672 |
+
<div class="radio"></div>
|
| 673 |
+
<div class="option-text">Yes</div></label
|
| 674 |
+
>
|
| 675 |
+
</div>
|
| 676 |
+
<div class="option">
|
| 677 |
+
<label class="opt">
|
| 678 |
+
<input type="radio" name="q14" />
|
| 679 |
+
<div class="radio"></div>
|
| 680 |
+
<div class="option-text">No</div>
|
| 681 |
+
</label>
|
| 682 |
+
</div>
|
| 683 |
+
<div class="option">
|
| 684 |
+
<label class="opt">
|
| 685 |
+
<input class="radio" type="radio" name="q14" />
|
| 686 |
+
<div class="radio"></div>
|
| 687 |
+
<div class="option-text">Already Clear</div>
|
| 688 |
+
</label>
|
| 689 |
+
</div>
|
| 690 |
+
</div>
|
| 691 |
+
</div>
|
| 692 |
+
<!-- 2.9 -->
|
| 693 |
+
<div class="question" id="q15">
|
| 694 |
+
<div class="q-title">What is your profession?</div>
|
| 695 |
+
<div class="options links-list">
|
| 696 |
+
<div class="option">
|
| 697 |
+
<label class="opt">
|
| 698 |
+
<input type="radio" name="q15" />
|
| 699 |
+
<div class="radio"></div>
|
| 700 |
+
<div class="option-text">
|
| 701 |
+
Journalist / Fact Checker
|
| 702 |
+
</div></label
|
| 703 |
+
>
|
| 704 |
+
</div>
|
| 705 |
+
<div class="option">
|
| 706 |
+
<label class="opt">
|
| 707 |
+
<input type="radio" name="q15" />
|
| 708 |
+
<div class="radio"></div>
|
| 709 |
+
<div class="option-text">Researcher / Student</div>
|
| 710 |
+
</label>
|
| 711 |
+
</div>
|
| 712 |
+
<div class="option">
|
| 713 |
+
<label class="opt">
|
| 714 |
+
<input class="radio" type="radio" name="q15" />
|
| 715 |
+
<div class="radio"></div>
|
| 716 |
+
<div class="option-text">Law Enforcement / OSINT</div>
|
| 717 |
+
</label>
|
| 718 |
+
</div>
|
| 719 |
+
<div class="option">
|
| 720 |
+
<label class="opt">
|
| 721 |
+
<input class="radio" type="radio" name="q15" />
|
| 722 |
+
<div class="radio"></div>
|
| 723 |
+
<div class="option-text">General User</div>
|
| 724 |
+
</label>
|
| 725 |
+
</div>
|
| 726 |
+
</div>
|
| 727 |
+
</div>
|
| 728 |
+
<div class="question" id="q16">
|
| 729 |
+
<label class="textarea" for="feedback-q16-comments">
|
| 730 |
+
<span class="textarea-label">
|
| 731 |
+
Help us improve. If the verification failed, what did we
|
| 732 |
+
miss?
|
| 733 |
+
</span>
|
| 734 |
+
</label>
|
| 735 |
+
<textarea
|
| 736 |
+
id="feedback-q16-comments"
|
| 737 |
+
class="feedback-textarea"
|
| 738 |
+
name="q16_comments"
|
| 739 |
+
rows="4"
|
| 740 |
+
placeholder="Text value"
|
| 741 |
+
></textarea>
|
| 742 |
+
</div>
|
| 743 |
+
</div>
|
| 744 |
+
<div
|
| 745 |
+
id="block-notification-1765336324468"
|
| 746 |
+
class="display-none"
|
| 747 |
+
></div>
|
| 748 |
+
</div>
|
| 749 |
+
<div id="footer-feedback-1765336324468" class="footer-feedback">
|
| 750 |
+
<button id="btn-feedback-face-check" class="btn btn-feedback">
|
| 751 |
+
Send
|
| 752 |
+
</button>
|
| 753 |
+
</div>
|
| 754 |
+
</section>
|
| 755 |
+
</main>
|
| 756 |
+
</div>
|
| 757 |
+
<div id="wrapper-modal-1760410479496" class="wrapper-modal">
|
| 758 |
+
<div id="model-1760410479496" class="modal"></div>
|
| 759 |
+
</div>
|
| 760 |
+
<div id="live-chat-1765253515534" class="live-chat">
|
| 761 |
+
<div>
|
| 762 |
+
<svg
|
| 763 |
+
width="32"
|
| 764 |
+
height="32"
|
| 765 |
+
viewBox="0 0 32 32"
|
| 766 |
+
fill="none"
|
| 767 |
+
xmlns="http://www.w3.org/2000/svg"
|
| 768 |
+
focusable="false"
|
| 769 |
+
aria-hidden="true"
|
| 770 |
+
>
|
| 771 |
+
<path
|
| 772 |
+
d="M18.96 28.84C17.3867 28.84 15.16 27.7333 13.4 22.44L12.44 19.56L9.56001 18.6C4.28001 16.84 3.17334 14.6133 3.17334 13.04C3.17334 11.48 4.28001 9.23997 9.56001 7.46664L20.88 3.69331C23.7067 2.74664 26.0667 3.02664 27.52 4.46664C28.9733 5.90664 29.2533 8.27997 28.3067 11.1066L24.5333 22.4266C22.76 27.7333 20.5333 28.84 18.96 28.84ZM10.1867 9.37331C6.48001 10.6133 5.16001 12.08 5.16001 13.04C5.16001 14 6.48001 15.4666 10.1867 16.6933L13.5467 17.8133C13.84 17.9066 14.08 18.1466 14.1733 18.44L15.2933 21.8C16.52 25.5066 18 26.8266 18.96 26.8266C19.92 26.8266 21.3867 25.5066 22.6267 21.8L26.4 10.48C27.08 8.42664 26.96 6.74664 26.0933 5.87997C25.2267 5.01331 23.5467 4.90664 21.5067 5.58664L10.1867 9.37331Z"
|
| 773 |
+
fill="#ffffff"
|
| 774 |
+
></path>
|
| 775 |
+
<path
|
| 776 |
+
d="M13.48 19.2C13.2267 19.2 12.9733 19.1066 12.7733 18.9066C12.3867 18.52 12.3867 17.88 12.7733 17.4933L17.5467 12.7066C17.9333 12.32 18.5733 12.32 18.96 12.7066C19.3467 13.0933 19.3467 13.7333 18.96 14.12L14.1867 18.9066C14 19.1066 13.7333 19.2 13.48 19.2Z"
|
| 777 |
+
fill="#ffffff"
|
| 778 |
+
></path>
|
| 779 |
+
</svg>
|
| 780 |
+
</div>
|
| 781 |
+
<div class="live-chat-text">Send your feedback</div>
|
| 782 |
+
</div>
|
| 783 |
+
<script type="module" src="constants.js"></script>
|
| 784 |
+
<script type="module" src="main.js"></script>
|
| 785 |
+
<script type="module" src="serverSentEvents.js"></script>
|
| 786 |
+
<script type="module" src="feedback.js"></script>
|
| 787 |
+
</body>
|
| 788 |
+
</html>
|
main.js
ADDED
|
@@ -0,0 +1,948 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { closeIcon, eyeIcon, fileIcon, trashIcon } from './constants.js';
|
| 2 |
+
|
| 3 |
+
const inputElement = document.getElementById('file-input');
|
| 4 |
+
const metadataElement = document.getElementById('metadata-input');
|
| 5 |
+
|
| 6 |
+
const outputElement = document.getElementById('output-area');
|
| 7 |
+
const btnClear = document.getElementById('btn-clear-face-check');
|
| 8 |
+
const btnSubmit = document.getElementById('btn-submit-face-check');
|
| 9 |
+
const btnSave = document.getElementById('btn-save-face-check');
|
| 10 |
+
const wrapperFilesElement = document.getElementById('wrapper-files-face-check');
|
| 11 |
+
const wrapperMetadataElement = document.getElementById(
|
| 12 |
+
'wrapper-metadata-face-check'
|
| 13 |
+
);
|
| 14 |
+
const wrapperUploadElement = document.getElementById(
|
| 15 |
+
'wrapper-upload-face-check'
|
| 16 |
+
);
|
| 17 |
+
const wrapperModalElement = document.getElementById(
|
| 18 |
+
'wrapper-modal-1760410479496'
|
| 19 |
+
);
|
| 20 |
+
const contentModalElement = document.getElementById('model-1760410479496');
|
| 21 |
+
const dropZones = document.getElementsByClassName('drop-zone');
|
| 22 |
+
const fileRemoves = document.getElementsByClassName('file-remove');
|
| 23 |
+
const statusFaceCheckElement = document.getElementById('status-face-check');
|
| 24 |
+
const tabMenuElement = document.getElementById('menu-1762855257376');
|
| 25 |
+
const tabItems = tabMenuElement.getElementsByClassName('item');
|
| 26 |
+
const livechat = document.getElementById('live-chat-1765253515534');
|
| 27 |
+
|
| 28 |
+
const REQUIRED_METADATA_FIELDS = [
|
| 29 |
+
'title',
|
| 30 |
+
'location',
|
| 31 |
+
'category',
|
| 32 |
+
'violence level',
|
| 33 |
+
'description',
|
| 34 |
+
'social media link',
|
| 35 |
+
];
|
| 36 |
+
export const clientId =
|
| 37 |
+
Date.now().toString(36) + Math.random().toString(36).substring(2, 8);
|
| 38 |
+
export const apiBaseUrl = 'http://localhost:3010/v1';
|
| 39 |
+
export let estTimeStep1 = 0;
|
| 40 |
+
export let estTimeStep2 = 0;
|
| 41 |
+
|
| 42 |
+
let currentPreviewURL = null;
|
| 43 |
+
const mediaPreviewUrls = new Map();
|
| 44 |
+
|
| 45 |
+
const renderer = new marked.Renderer();
|
| 46 |
+
renderer.link = function ({ href, text }) {
|
| 47 |
+
return `<a class="image-bg" href="${href}" target="_blank" rel="noopener noreferrer">${text}</a>`;
|
| 48 |
+
};
|
| 49 |
+
|
| 50 |
+
let isReplaced = true;
|
| 51 |
+
let isReplacedBlock = true;
|
| 52 |
+
renderer.text = function ({ text }) {
|
| 53 |
+
const hiddenTexts = [
|
| 54 |
+
'Verification Tools & Methods:',
|
| 55 |
+
'Synthetic Type (if applicable):',
|
| 56 |
+
'Other Artifacts:',
|
| 57 |
+
];
|
| 58 |
+
const noReplaces = [
|
| 59 |
+
'**Tags:**',
|
| 60 |
+
'**Authenticity Assessment:**',
|
| 61 |
+
'**Verification Tools & Methods:**',
|
| 62 |
+
'**Synthetic Type (if applicable):**',
|
| 63 |
+
'**Other Artifacts:**',
|
| 64 |
+
'**Supporting Sources:**',
|
| 65 |
+
'**Cross-Checking Information:**',
|
| 66 |
+
'**Other Info:**',
|
| 67 |
+
];
|
| 68 |
+
isReplaced = !noReplaces.some((item) => text.includes(item));
|
| 69 |
+
if (isReplacedBlock) {
|
| 70 |
+
isReplacedBlock = !text.includes('**Supporting Sources:**');
|
| 71 |
+
}
|
| 72 |
+
|
| 73 |
+
const normalizedText = text.trim().replace(/^[-*]\s*/, '');
|
| 74 |
+
if (hiddenTexts.some((item) => normalizedText === item)) {
|
| 75 |
+
return '';
|
| 76 |
+
}
|
| 77 |
+
|
| 78 |
+
if (!isReplaced) {
|
| 79 |
+
const inlineHtml =
|
| 80 |
+
typeof marked.parseInline === 'function'
|
| 81 |
+
? marked.parseInline(text)
|
| 82 |
+
: marked.Renderer.prototype.text.call(renderer, text);
|
| 83 |
+
return inlineHtml;
|
| 84 |
+
}
|
| 85 |
+
const replaced = text.replace(
|
| 86 |
+
/(?:\b([\w-]+)\s*)?(?:\[((?:Image\s+[^\]]+|Source\s+[^\]]+|URL\s+[^\]]+|[^,\]]+(?:\s*,\s*[^,\]]+)*))\]|\((Sources?\s+[^)]+)\))/gi,
|
| 87 |
+
(match, beforeWord, insideSquare, insideSource) => {
|
| 88 |
+
const beforeWordLower = (beforeWord || '').toLowerCase();
|
| 89 |
+
const insideLower = (insideSquare || '').toLowerCase();
|
| 90 |
+
|
| 91 |
+
const isImages = ['image', 'images', 'photo', 'photos'];
|
| 92 |
+
|
| 93 |
+
if (
|
| 94 |
+
isImages.includes(beforeWordLower) ||
|
| 95 |
+
isImages.some((item) => insideLower.includes(item))
|
| 96 |
+
) {
|
| 97 |
+
const listImage = insideSquare
|
| 98 |
+
.split(',')
|
| 99 |
+
.map(
|
| 100 |
+
(item) =>
|
| 101 |
+
`<span class="${
|
| 102 |
+
isReplacedBlock ? 'image-bg' : ''
|
| 103 |
+
}" onClick="previewImage('${item}')">${item}</span>`
|
| 104 |
+
);
|
| 105 |
+
return `${
|
| 106 |
+
beforeWord ? beforeWord + ' ' : ''
|
| 107 |
+
} <span>[${listImage.join(', ')}]</span>`;
|
| 108 |
+
}
|
| 109 |
+
const listUrl = (insideSquare || insideSource)
|
| 110 |
+
.split(',')
|
| 111 |
+
.map(
|
| 112 |
+
(item) =>
|
| 113 |
+
`<span class="${
|
| 114 |
+
isReplacedBlock ? 'image-bg' : ''
|
| 115 |
+
}" onClick=goToEvidence()>${item}</span>`
|
| 116 |
+
);
|
| 117 |
+
|
| 118 |
+
return `${beforeWord ? beforeWord + ' ' : ''} \n<span>[${listUrl.join(
|
| 119 |
+
', '
|
| 120 |
+
)}]</span>`;
|
| 121 |
+
}
|
| 122 |
+
);
|
| 123 |
+
|
| 124 |
+
return replaced;
|
| 125 |
+
};
|
| 126 |
+
|
| 127 |
+
marked.setOptions({
|
| 128 |
+
renderer,
|
| 129 |
+
breaks: true,
|
| 130 |
+
});
|
| 131 |
+
|
| 132 |
+
let files = {};
|
| 133 |
+
Object.defineProperty(files, 'value', {
|
| 134 |
+
set(newValue) {
|
| 135 |
+
const activeIds = new Set(
|
| 136 |
+
newValue.map((file) => `${file.name}|${file.size}|${file.lastModified}`)
|
| 137 |
+
);
|
| 138 |
+
mediaPreviewUrls.forEach((url, id) => {
|
| 139 |
+
if (!activeIds.has(id)) {
|
| 140 |
+
URL.revokeObjectURL(url);
|
| 141 |
+
mediaPreviewUrls.delete(id);
|
| 142 |
+
}
|
| 143 |
+
});
|
| 144 |
+
|
| 145 |
+
if (newValue.length > 0) {
|
| 146 |
+
const displayNames = newValue
|
| 147 |
+
.map((file) => {
|
| 148 |
+
const fileId = `${file.name}|${file.size}|${file.lastModified}`;
|
| 149 |
+
if (!mediaPreviewUrls.has(fileId)) {
|
| 150 |
+
mediaPreviewUrls.set(fileId, URL.createObjectURL(file));
|
| 151 |
+
}
|
| 152 |
+
const previewUrl = mediaPreviewUrls.get(fileId);
|
| 153 |
+
const previewMarkup = file.type.startsWith('video/')
|
| 154 |
+
? `<div class="file-thumbnail file-thumbnail-video">
|
| 155 |
+
<video src="${previewUrl}" preload="metadata" muted playsinline></video>
|
| 156 |
+
</div>`
|
| 157 |
+
: `<img class="file-thumbnail file-thumbnail-image" src="${previewUrl}" alt="${file.name}" />`;
|
| 158 |
+
return `<div onclick="previewFile(event, '${fileId}')" id="${fileId}" class="file-item pointer">
|
| 159 |
+
${previewMarkup}
|
| 160 |
+
<span class="file-name" data-full-name="${file.name}">${file.name}</span>
|
| 161 |
+
<div class="preview">${eyeIcon}</div>
|
| 162 |
+
<div onclick="removeFile(event, '${fileId}', 'media')" class="file-remove" data-file="${fileId}">${trashIcon}</div>
|
| 163 |
+
</div>`;
|
| 164 |
+
})
|
| 165 |
+
.join('');
|
| 166 |
+
wrapperFilesElement.innerHTML = displayNames;
|
| 167 |
+
wrapperFilesElement.classList.remove('display-none');
|
| 168 |
+
|
| 169 |
+
// Add tooltips only for truncated text
|
| 170 |
+
setTimeout(() => {
|
| 171 |
+
document.querySelectorAll('.file-name').forEach((span) => {
|
| 172 |
+
const fullName = span.getAttribute('data-full-name');
|
| 173 |
+
if (fullName && span.scrollWidth > span.clientWidth) {
|
| 174 |
+
span.setAttribute('title', fullName);
|
| 175 |
+
}
|
| 176 |
+
});
|
| 177 |
+
}, 0);
|
| 178 |
+
} else {
|
| 179 |
+
wrapperFilesElement.innerHTML = '';
|
| 180 |
+
wrapperFilesElement.classList.add('display-none');
|
| 181 |
+
}
|
| 182 |
+
this._value = newValue;
|
| 183 |
+
},
|
| 184 |
+
get() {
|
| 185 |
+
return this._value;
|
| 186 |
+
},
|
| 187 |
+
});
|
| 188 |
+
files.value = [];
|
| 189 |
+
|
| 190 |
+
function validateMetadataJson(file) {
|
| 191 |
+
return new Promise((resolve, reject) => {
|
| 192 |
+
if (!file) {
|
| 193 |
+
reject('Please provide a metadata file.');
|
| 194 |
+
return;
|
| 195 |
+
}
|
| 196 |
+
const reader = new FileReader();
|
| 197 |
+
reader.onload = (event) => {
|
| 198 |
+
const text = event.target?.result;
|
| 199 |
+
if (typeof text !== 'string' || text.trim().length === 0) {
|
| 200 |
+
reject('Metadata file must contain valid JSON.');
|
| 201 |
+
return;
|
| 202 |
+
}
|
| 203 |
+
try {
|
| 204 |
+
const parsed = JSON.parse(text);
|
| 205 |
+
if (
|
| 206 |
+
typeof parsed !== 'object' ||
|
| 207 |
+
parsed === null ||
|
| 208 |
+
Array.isArray(parsed)
|
| 209 |
+
) {
|
| 210 |
+
reject('Metadata file must be a JSON object.');
|
| 211 |
+
return;
|
| 212 |
+
}
|
| 213 |
+
const keys = Object.keys(parsed);
|
| 214 |
+
const missingFields = REQUIRED_METADATA_FIELDS.filter(
|
| 215 |
+
(field) => !keys.includes(field)
|
| 216 |
+
);
|
| 217 |
+
if (missingFields.length > 0) {
|
| 218 |
+
reject(
|
| 219 |
+
`Metadata file is missing required fields: ${missingFields.join(
|
| 220 |
+
', '
|
| 221 |
+
)}`
|
| 222 |
+
);
|
| 223 |
+
return;
|
| 224 |
+
}
|
| 225 |
+
const extraFields = keys.filter(
|
| 226 |
+
(key) => !REQUIRED_METADATA_FIELDS.includes(key)
|
| 227 |
+
);
|
| 228 |
+
if (extraFields.length > 0) {
|
| 229 |
+
reject(
|
| 230 |
+
`Metadata file contains unsupported fields: ${extraFields.join(
|
| 231 |
+
', '
|
| 232 |
+
)}`
|
| 233 |
+
);
|
| 234 |
+
return;
|
| 235 |
+
}
|
| 236 |
+
resolve();
|
| 237 |
+
} catch (error) {
|
| 238 |
+
reject('Metadata file must contain valid JSON.');
|
| 239 |
+
}
|
| 240 |
+
};
|
| 241 |
+
reader.onerror = () => {
|
| 242 |
+
reject('Unable to read the metadata file.');
|
| 243 |
+
};
|
| 244 |
+
reader.readAsText(file);
|
| 245 |
+
});
|
| 246 |
+
}
|
| 247 |
+
|
| 248 |
+
let metadata = {};
|
| 249 |
+
Object.defineProperty(metadata, 'value', {
|
| 250 |
+
set(newValue) {
|
| 251 |
+
if (newValue.length > 0) {
|
| 252 |
+
const displayNames = newValue
|
| 253 |
+
.map((meta) => {
|
| 254 |
+
const fileId = `${meta.name}|${meta.size}|${meta.lastModified}`;
|
| 255 |
+
return `<div onclick="previewMetadata(event, '${fileId}')" id="${fileId}" class="file-item pointer">
|
| 256 |
+
<div class="file-icon">${fileIcon}</div>
|
| 257 |
+
<span class="file-name" data-full-name="${meta.name}">${meta.name}</span>
|
| 258 |
+
<div class="preview">${eyeIcon}</div>
|
| 259 |
+
<div onclick="removeFile(event, '${fileId}', 'metadata')" class="file-remove" data-file="${fileId}">${trashIcon}</div>
|
| 260 |
+
</div>`;
|
| 261 |
+
})
|
| 262 |
+
.join('');
|
| 263 |
+
wrapperMetadataElement.innerHTML = displayNames;
|
| 264 |
+
wrapperMetadataElement.classList.remove('display-none');
|
| 265 |
+
|
| 266 |
+
// Add tooltips only for truncated text
|
| 267 |
+
setTimeout(() => {
|
| 268 |
+
document.querySelectorAll('.file-name').forEach((span) => {
|
| 269 |
+
const fullName = span.getAttribute('data-full-name');
|
| 270 |
+
if (fullName && span.scrollWidth > span.clientWidth) {
|
| 271 |
+
span.setAttribute('title', fullName);
|
| 272 |
+
}
|
| 273 |
+
});
|
| 274 |
+
}, 0);
|
| 275 |
+
} else {
|
| 276 |
+
wrapperMetadataElement.innerHTML = '';
|
| 277 |
+
wrapperMetadataElement.classList.add('display-none');
|
| 278 |
+
}
|
| 279 |
+
this._value = newValue;
|
| 280 |
+
},
|
| 281 |
+
get() {
|
| 282 |
+
return this._value;
|
| 283 |
+
},
|
| 284 |
+
});
|
| 285 |
+
metadata.value = [];
|
| 286 |
+
|
| 287 |
+
let data = {};
|
| 288 |
+
Object.defineProperty(data, 'value', {
|
| 289 |
+
set(newValue) {
|
| 290 |
+
if (newValue) {
|
| 291 |
+
btnSave.classList.add('active');
|
| 292 |
+
// livechat.classList.remove('display-none');
|
| 293 |
+
} else {
|
| 294 |
+
btnSave.classList.remove('active');
|
| 295 |
+
// livechat.classList.add('display-none');
|
| 296 |
+
}
|
| 297 |
+
this._value = newValue;
|
| 298 |
+
},
|
| 299 |
+
get() {
|
| 300 |
+
return this._value;
|
| 301 |
+
},
|
| 302 |
+
});
|
| 303 |
+
// data.value = {}
|
| 304 |
+
|
| 305 |
+
let selectedTab = {};
|
| 306 |
+
Object.defineProperty(selectedTab, 'value', {
|
| 307 |
+
set(newValue) {
|
| 308 |
+
if (data.value) {
|
| 309 |
+
const menuItems =
|
| 310 |
+
newValue === 'verified_evidence'
|
| 311 |
+
? `<div class="menu-children menu pt-0 mb-4">
|
| 312 |
+
<div onclick="changeTabChildren('source_details', this)" class="item active">Source Details</div>
|
| 313 |
+
<div onclick="changeTabChildren('where', this)" class="item">Where</div>
|
| 314 |
+
<div onclick="changeTabChildren('when', this)" class="item">When</div>
|
| 315 |
+
<div onclick="changeTabChildren('who', this)" class="item">Who</div>
|
| 316 |
+
<div onclick="changeTabChildren('why', this)" class="item">Why</div>
|
| 317 |
+
</div>`
|
| 318 |
+
: '';
|
| 319 |
+
|
| 320 |
+
outputElement.innerHTML =
|
| 321 |
+
menuItems +
|
| 322 |
+
marked.parse(
|
| 323 |
+
data.value.readme_content[newValue]?.content ||
|
| 324 |
+
data.value.readme_content[newValue]?.source_details?.content ||
|
| 325 |
+
''
|
| 326 |
+
);
|
| 327 |
+
}
|
| 328 |
+
isReplacedBlock = true;
|
| 329 |
+
this._value = newValue;
|
| 330 |
+
},
|
| 331 |
+
get() {
|
| 332 |
+
return this._value;
|
| 333 |
+
},
|
| 334 |
+
});
|
| 335 |
+
selectedTab.value = 'case_summary';
|
| 336 |
+
|
| 337 |
+
let selectedTabChildren = {};
|
| 338 |
+
Object.defineProperty(selectedTabChildren, 'value', {
|
| 339 |
+
set(newValue) {
|
| 340 |
+
if (data.value) {
|
| 341 |
+
const menuItems = `<div class="menu-children menu pt-0 mb-4">
|
| 342 |
+
<div onclick="changeTabChildren('source_details', this)" class="item ${
|
| 343 |
+
newValue === 'source_details' ? 'active' : ''
|
| 344 |
+
}">Source Details</div>
|
| 345 |
+
<div onclick="changeTabChildren('where', this)" class="item ${
|
| 346 |
+
newValue === 'where' ? 'active' : ''
|
| 347 |
+
}">Where</div>
|
| 348 |
+
<div onclick="changeTabChildren('when', this)" class="item ${
|
| 349 |
+
newValue === 'when' ? 'active' : ''
|
| 350 |
+
}">When</div>
|
| 351 |
+
<div onclick="changeTabChildren('who', this)" class="item ${
|
| 352 |
+
newValue === 'who' ? 'active' : ''
|
| 353 |
+
}">Who</div>
|
| 354 |
+
<div onclick="changeTabChildren('why', this)" class="item ${
|
| 355 |
+
newValue === 'why' ? 'active' : ''
|
| 356 |
+
}">Why</div>
|
| 357 |
+
</div>`;
|
| 358 |
+
outputElement.innerHTML =
|
| 359 |
+
menuItems +
|
| 360 |
+
marked.parse(
|
| 361 |
+
data.value.readme_content[selectedTab.value]?.[newValue]?.content ||
|
| 362 |
+
''
|
| 363 |
+
);
|
| 364 |
+
}
|
| 365 |
+
isReplacedBlock = true;
|
| 366 |
+
this._value = newValue;
|
| 367 |
+
},
|
| 368 |
+
get() {
|
| 369 |
+
return this._value;
|
| 370 |
+
},
|
| 371 |
+
});
|
| 372 |
+
selectedTab.value = 'case_summary';
|
| 373 |
+
|
| 374 |
+
let isLoading = {};
|
| 375 |
+
Object.defineProperty(isLoading, 'value', {
|
| 376 |
+
set(newValue) {
|
| 377 |
+
if (newValue) {
|
| 378 |
+
btnClear.disabled = true;
|
| 379 |
+
btnSave.disabled = true;
|
| 380 |
+
btnSubmit.disabled = true;
|
| 381 |
+
inputElement.disabled = true;
|
| 382 |
+
metadataElement.disabled = true;
|
| 383 |
+
Array.from(dropZones).forEach((dropZone) => {
|
| 384 |
+
dropZone.disabled = true;
|
| 385 |
+
dropZone.classList.add('btn-disabled');
|
| 386 |
+
});
|
| 387 |
+
Array.from(fileRemoves).forEach((fileRemove) => {
|
| 388 |
+
fileRemove.disabled = true;
|
| 389 |
+
fileRemove.classList.add('btn-disabled');
|
| 390 |
+
});
|
| 391 |
+
|
| 392 |
+
btnClear.classList.add('btn-disabled');
|
| 393 |
+
btnSave.classList.add('btn-disabled');
|
| 394 |
+
btnSubmit.classList.add('btn-disabled');
|
| 395 |
+
|
| 396 |
+
Array.from(tabItems).forEach((tabItem) => {
|
| 397 |
+
tabItem.disabled = true;
|
| 398 |
+
tabItem.classList.add('btn-disabled', 'no-hover');
|
| 399 |
+
});
|
| 400 |
+
} else {
|
| 401 |
+
btnClear.disabled = false;
|
| 402 |
+
btnSave.disabled = false;
|
| 403 |
+
btnSubmit.disabled = false;
|
| 404 |
+
inputElement.disabled = false;
|
| 405 |
+
metadataElement.disabled = false;
|
| 406 |
+
Array.from(dropZones).forEach((dropZone) => {
|
| 407 |
+
dropZone.disabled = false;
|
| 408 |
+
dropZone.classList.remove('btn-disabled');
|
| 409 |
+
});
|
| 410 |
+
Array.from(fileRemoves).forEach((fileRemove) => {
|
| 411 |
+
fileRemove.disabled = false;
|
| 412 |
+
fileRemove.classList.remove('btn-disabled');
|
| 413 |
+
});
|
| 414 |
+
btnClear.classList.remove('btn-disabled');
|
| 415 |
+
btnSave.classList.remove('btn-disabled');
|
| 416 |
+
btnSubmit.classList.remove('btn-disabled');
|
| 417 |
+
btnSubmit.innerText = 'Submit';
|
| 418 |
+
|
| 419 |
+
Array.from(tabItems).forEach((tabItem) => {
|
| 420 |
+
tabItem.disabled = false;
|
| 421 |
+
tabItem.classList.remove('btn-disabled', 'no-hover');
|
| 422 |
+
});
|
| 423 |
+
}
|
| 424 |
+
this._value = newValue;
|
| 425 |
+
},
|
| 426 |
+
get() {
|
| 427 |
+
return this._value;
|
| 428 |
+
},
|
| 429 |
+
});
|
| 430 |
+
|
| 431 |
+
inputElement.addEventListener('change', (event) => {
|
| 432 |
+
const allowedExtensions = ['.png', '.jpg', '.jpeg', '.gif', '.mp4'];
|
| 433 |
+
const selectedFiles = Array.from(event.target.files);
|
| 434 |
+
const invalidFiles = selectedFiles.filter((file) => {
|
| 435 |
+
return !allowedExtensions.some((ext) =>
|
| 436 |
+
file.name.toLowerCase().endsWith(ext)
|
| 437 |
+
);
|
| 438 |
+
});
|
| 439 |
+
if (invalidFiles.length > 0) {
|
| 440 |
+
createTemplateModal(
|
| 441 |
+
'Only the following formats are accepted: ' + allowedExtensions.join(', ')
|
| 442 |
+
);
|
| 443 |
+
inputElement.value = '';
|
| 444 |
+
return;
|
| 445 |
+
}
|
| 446 |
+
|
| 447 |
+
for (const file of selectedFiles) {
|
| 448 |
+
if (
|
| 449 |
+
!files.value.some((f) => f.name === file.name && f.size === file.size)
|
| 450 |
+
) {
|
| 451 |
+
files.value = [...files.value, file];
|
| 452 |
+
}
|
| 453 |
+
}
|
| 454 |
+
inputElement.value = '';
|
| 455 |
+
});
|
| 456 |
+
|
| 457 |
+
metadataElement.addEventListener('change', (event) => {
|
| 458 |
+
const allowedExtensions = ['.json'];
|
| 459 |
+
const selectedFiles = Array.from(event.target.files || []);
|
| 460 |
+
|
| 461 |
+
if (selectedFiles.length === 0) {
|
| 462 |
+
metadataElement.value = '';
|
| 463 |
+
return;
|
| 464 |
+
}
|
| 465 |
+
|
| 466 |
+
const invalidFiles = selectedFiles.filter((file) => {
|
| 467 |
+
return !allowedExtensions.some((ext) =>
|
| 468 |
+
file.name.toLowerCase().endsWith(ext)
|
| 469 |
+
);
|
| 470 |
+
});
|
| 471 |
+
|
| 472 |
+
if (invalidFiles.length > 0) {
|
| 473 |
+
createTemplateModal(
|
| 474 |
+
'Only the following formats are accepted: ' + allowedExtensions.join(', ')
|
| 475 |
+
);
|
| 476 |
+
metadataElement.value = '';
|
| 477 |
+
return;
|
| 478 |
+
}
|
| 479 |
+
|
| 480 |
+
const file = selectedFiles[0];
|
| 481 |
+
|
| 482 |
+
validateMetadataJson(file)
|
| 483 |
+
.then(() => {
|
| 484 |
+
metadata.value = [file];
|
| 485 |
+
})
|
| 486 |
+
.catch((message) => {
|
| 487 |
+
createTemplateModal(`<div>
|
| 488 |
+
<div class="note">
|
| 489 |
+
The content of the JSON file is invalid. Please follow the format below.
|
| 490 |
+
</div>
|
| 491 |
+
<div class="json-box">
|
| 492 |
+
{
|
| 493 |
+
"title": "",
|
| 494 |
+
"location": "",
|
| 495 |
+
"category": "",
|
| 496 |
+
"violence level": "",
|
| 497 |
+
"description": "",
|
| 498 |
+
"social media link": ""
|
| 499 |
+
}
|
| 500 |
+
</div>
|
| 501 |
+
</div>`);
|
| 502 |
+
})
|
| 503 |
+
.finally(() => {
|
| 504 |
+
metadataElement.value = '';
|
| 505 |
+
});
|
| 506 |
+
});
|
| 507 |
+
|
| 508 |
+
// Drag and drop for metadata (JSON files)
|
| 509 |
+
const metadataDropZone = document.querySelector('label[for="metadata-input"]');
|
| 510 |
+
metadataDropZone.addEventListener('dragover', (e) => {
|
| 511 |
+
e.preventDefault();
|
| 512 |
+
metadataDropZone.classList.add('drag-over');
|
| 513 |
+
});
|
| 514 |
+
metadataDropZone.addEventListener('dragleave', (e) => {
|
| 515 |
+
e.preventDefault();
|
| 516 |
+
metadataDropZone.classList.remove('drag-over');
|
| 517 |
+
});
|
| 518 |
+
metadataDropZone.addEventListener('drop', (e) => {
|
| 519 |
+
e.preventDefault();
|
| 520 |
+
metadataDropZone.classList.remove('drag-over');
|
| 521 |
+
const droppedFiles = Array.from(e.dataTransfer.files);
|
| 522 |
+
const allowedExtensions = ['.json'];
|
| 523 |
+
const invalidFiles = droppedFiles.filter((file) => {
|
| 524 |
+
return !allowedExtensions.some((ext) =>
|
| 525 |
+
file.name.toLowerCase().endsWith(ext)
|
| 526 |
+
);
|
| 527 |
+
});
|
| 528 |
+
if (invalidFiles.length > 0) {
|
| 529 |
+
createTemplateModal(
|
| 530 |
+
'Only the following formats are accepted: ' + allowedExtensions.join(', ')
|
| 531 |
+
);
|
| 532 |
+
return;
|
| 533 |
+
}
|
| 534 |
+
if (droppedFiles.length > 0) {
|
| 535 |
+
const file = droppedFiles[0]; // Only allow one JSON file
|
| 536 |
+
validateMetadataJson(file)
|
| 537 |
+
.then(() => {
|
| 538 |
+
metadata.value = [file];
|
| 539 |
+
})
|
| 540 |
+
.catch((message) => {
|
| 541 |
+
createTemplateModal(
|
| 542 |
+
`<div>
|
| 543 |
+
<div class="note">
|
| 544 |
+
The content of the JSON file is invalid. Please follow the format below.
|
| 545 |
+
</div>
|
| 546 |
+
<div class="json-box">
|
| 547 |
+
{
|
| 548 |
+
"title": "",
|
| 549 |
+
"location": "",
|
| 550 |
+
"category": "",
|
| 551 |
+
"violence level": "",
|
| 552 |
+
"description": "",
|
| 553 |
+
"social media link": ""
|
| 554 |
+
}
|
| 555 |
+
</div>
|
| 556 |
+
</div>`
|
| 557 |
+
);
|
| 558 |
+
});
|
| 559 |
+
}
|
| 560 |
+
});
|
| 561 |
+
|
| 562 |
+
// Drag and drop for media files
|
| 563 |
+
const mediaDropZone = document.querySelector('label[for="file-input"]');
|
| 564 |
+
mediaDropZone.addEventListener('dragover', (e) => {
|
| 565 |
+
e.preventDefault();
|
| 566 |
+
mediaDropZone.classList.add('drag-over');
|
| 567 |
+
});
|
| 568 |
+
mediaDropZone.addEventListener('dragleave', (e) => {
|
| 569 |
+
e.preventDefault();
|
| 570 |
+
mediaDropZone.classList.remove('drag-over');
|
| 571 |
+
});
|
| 572 |
+
mediaDropZone.addEventListener('drop', (e) => {
|
| 573 |
+
e.preventDefault();
|
| 574 |
+
mediaDropZone.classList.remove('drag-over');
|
| 575 |
+
const droppedFiles = Array.from(e.dataTransfer.files);
|
| 576 |
+
const allowedExtensions = ['.png', '.jpg', '.jpeg', '.gif', '.mp4'];
|
| 577 |
+
const invalidFiles = droppedFiles.filter((file) => {
|
| 578 |
+
return !allowedExtensions.some((ext) =>
|
| 579 |
+
file.name.toLowerCase().endsWith(ext)
|
| 580 |
+
);
|
| 581 |
+
});
|
| 582 |
+
if (invalidFiles.length > 0) {
|
| 583 |
+
createTemplateModal(
|
| 584 |
+
'Only the following formats are accepted: ' + allowedExtensions.join(', ')
|
| 585 |
+
);
|
| 586 |
+
return;
|
| 587 |
+
}
|
| 588 |
+
for (const file of droppedFiles) {
|
| 589 |
+
if (
|
| 590 |
+
!files.value.some((f) => f.name === file.name && f.size === file.size)
|
| 591 |
+
) {
|
| 592 |
+
files.value = [...files.value, file];
|
| 593 |
+
}
|
| 594 |
+
}
|
| 595 |
+
});
|
| 596 |
+
|
| 597 |
+
btnSubmit.addEventListener('click', () => handleSubmit());
|
| 598 |
+
btnClear.addEventListener('click', () => reset());
|
| 599 |
+
btnSave.addEventListener('click', () => handleDownload('report.md'));
|
| 600 |
+
|
| 601 |
+
function reset() {
|
| 602 |
+
inputElement.value = '';
|
| 603 |
+
metadataElement.value = '';
|
| 604 |
+
outputElement.innerHTML =
|
| 605 |
+
'<p class="opacity-50">Verification result comes here...</p>';
|
| 606 |
+
files.value = [];
|
| 607 |
+
metadata.value = [];
|
| 608 |
+
data.value = null;
|
| 609 |
+
}
|
| 610 |
+
|
| 611 |
+
async function handleSubmit() {
|
| 612 |
+
if (isLoading.value) return;
|
| 613 |
+
|
| 614 |
+
data.value = null;
|
| 615 |
+
outputElement.innerHTML =
|
| 616 |
+
'<p class="opacity-50">Verification result comes here...</p>';
|
| 617 |
+
|
| 618 |
+
if (files.value.length === 0 && metadata.value.length === 0) {
|
| 619 |
+
createTemplateModal('Please upload text-based file and media files');
|
| 620 |
+
return;
|
| 621 |
+
}
|
| 622 |
+
|
| 623 |
+
if (metadata.value.length === 0) {
|
| 624 |
+
createTemplateModal('Please upload text-based file');
|
| 625 |
+
return;
|
| 626 |
+
}
|
| 627 |
+
|
| 628 |
+
if (files.value.length === 0) {
|
| 629 |
+
createTemplateModal('Please upload media files');
|
| 630 |
+
return;
|
| 631 |
+
}
|
| 632 |
+
|
| 633 |
+
isLoading.value = true;
|
| 634 |
+
btnSubmit.innerText = 'Processing...';
|
| 635 |
+
statusFaceCheckElement.classList.remove('display-none');
|
| 636 |
+
const formData = new FormData();
|
| 637 |
+
formData.append('metadata_file', metadata.value[0]);
|
| 638 |
+
|
| 639 |
+
const mediainfo = await new Promise((resolve) => {
|
| 640 |
+
MediaInfo.mediaInfoFactory({ format: 'JSON' }, resolve);
|
| 641 |
+
});
|
| 642 |
+
let isGetTimed = false;
|
| 643 |
+
for (const [index, file] of files.value.entries()) {
|
| 644 |
+
const readChunk = async (chunkSize, offset) =>
|
| 645 |
+
new Uint8Array(
|
| 646 |
+
await file.slice(offset, offset + chunkSize).arrayBuffer()
|
| 647 |
+
);
|
| 648 |
+
|
| 649 |
+
try {
|
| 650 |
+
if (file.type.startsWith('video/') && !isGetTimed) {
|
| 651 |
+
const result = await mediainfo.analyzeData(file.size, readChunk);
|
| 652 |
+
const data = JSON.parse(result);
|
| 653 |
+
|
| 654 |
+
const video =
|
| 655 |
+
data.media.track.find((t) => t['@type'] === 'Video') || {};
|
| 656 |
+
const duration = +video.Duration || 0;
|
| 657 |
+
const frameRate = +video.FrameRate || 0;
|
| 658 |
+
const heightV = +video.Height || 0;
|
| 659 |
+
const widthV = +video.Width || 0;
|
| 660 |
+
|
| 661 |
+
const calcEstimatedTimeCode = Math.ceil(
|
| 662 |
+
(duration * frameRate * heightV * widthV * 3.17e-7) / 60
|
| 663 |
+
);
|
| 664 |
+
|
| 665 |
+
estTimeStep1 = calcEstimatedTimeCode;
|
| 666 |
+
isGetTimed = true;
|
| 667 |
+
}
|
| 668 |
+
|
| 669 |
+
formData.append('files', file);
|
| 670 |
+
} catch (error) {
|
| 671 |
+
console.error('Error:', error);
|
| 672 |
+
}
|
| 673 |
+
}
|
| 674 |
+
|
| 675 |
+
estTimeStep1 = estTimeStep1 + 5;
|
| 676 |
+
estTimeStep2 = 5;
|
| 677 |
+
|
| 678 |
+
axios
|
| 679 |
+
.post(`${apiBaseUrl}/face_check`, formData, {
|
| 680 |
+
headers: {
|
| 681 |
+
'Content-Type': 'multipart/form-data',
|
| 682 |
+
'X-Client-ID': clientId,
|
| 683 |
+
},
|
| 684 |
+
})
|
| 685 |
+
.then((res) => {
|
| 686 |
+
data.value = res.data;
|
| 687 |
+
const menuItems =
|
| 688 |
+
selectedTab.value === 'verified_evidence'
|
| 689 |
+
? `<div class="menu-children menu pt-0 mb-4">
|
| 690 |
+
<div onclick="changeTabChildren('source_details', this)" class="item active">Source Details</div>
|
| 691 |
+
<div onclick="changeTabChildren('where', this)" class="item">Where</div>
|
| 692 |
+
<div onclick="changeTabChildren('when', this)" class="item">When</div>
|
| 693 |
+
<div onclick="changeTabChildren('who', this)" class="item">Who</div>
|
| 694 |
+
<div onclick="changeTabChildren('why', this)" class="item">Why</div>
|
| 695 |
+
</div>`
|
| 696 |
+
: '';
|
| 697 |
+
outputElement.innerHTML =
|
| 698 |
+
menuItems +
|
| 699 |
+
marked.parse(
|
| 700 |
+
res.data.readme_content[selectedTab.value]?.content ||
|
| 701 |
+
res.data.readme_content[selectedTab.value]?.source_details
|
| 702 |
+
?.content ||
|
| 703 |
+
''
|
| 704 |
+
);
|
| 705 |
+
estTimeStep1 = 0;
|
| 706 |
+
estTimeStep2 = 0;
|
| 707 |
+
isLoading.value = false;
|
| 708 |
+
})
|
| 709 |
+
.catch((err) => {
|
| 710 |
+
outputElement.innerText =
|
| 711 |
+
'Error: An error occurred while processing your request. Please try again later.';
|
| 712 |
+
isLoading.value = false;
|
| 713 |
+
});
|
| 714 |
+
}
|
| 715 |
+
|
| 716 |
+
function handleHyperLinkImages(text) {
|
| 717 |
+
return text.replace(/[\[\(]Image\s+([\d,\s]+)[\]\)]/g, (match, numbers) => {
|
| 718 |
+
const list = numbers
|
| 719 |
+
.split(',')
|
| 720 |
+
.map((n) => n.trim())
|
| 721 |
+
.filter((n) => n !== '');
|
| 722 |
+
|
| 723 |
+
const result = list.map((n) => {
|
| 724 |
+
return `[![Image ${n}]](./media/image-${n}.png)`;
|
| 725 |
+
});
|
| 726 |
+
|
| 727 |
+
return result.join('');
|
| 728 |
+
});
|
| 729 |
+
}
|
| 730 |
+
|
| 731 |
+
async function handleDownload(filename) {
|
| 732 |
+
if (!data.value) {
|
| 733 |
+
createTemplateModal('No data to save.');
|
| 734 |
+
return;
|
| 735 |
+
}
|
| 736 |
+
const dataReport =
|
| 737 |
+
handleHyperLinkImages(`${data.value.readme_content.case_summary.title}${data.value.readme_content.case_summary.content}${data.value.readme_content.content_classification.title}${data.value.readme_content.content_classification.content}${data.value.readme_content.verified_evidence.title}
|
| 738 |
+
${data.value.readme_content.verified_evidence.source_details.title}${data.value.readme_content.verified_evidence.source_details.content}${data.value.readme_content.verified_evidence.where.title}${data.value.readme_content.verified_evidence.where.content}${data.value.readme_content.verified_evidence.when.title}${data.value.readme_content.verified_evidence.when.content}${data.value.readme_content.verified_evidence.who.title}${data.value.readme_content.verified_evidence.who.content}${data.value.readme_content.verified_evidence.why.title}${data.value.readme_content.verified_evidence.why.content}${data.value.readme_content.forensic_analysis.title}${data.value.readme_content.forensic_analysis.content}${data.value.readme_content.other_evidence.title}${data.value.readme_content.other_evidence.content}
|
| 739 |
+
`);
|
| 740 |
+
|
| 741 |
+
const mediaItems = Array.isArray(data.value.readme_content.media)
|
| 742 |
+
? data.value.readme_content.media
|
| 743 |
+
: [];
|
| 744 |
+
|
| 745 |
+
if (mediaItems.length === 0) {
|
| 746 |
+
const blob = new Blob([dataReport], {
|
| 747 |
+
type: 'text/markdown',
|
| 748 |
+
});
|
| 749 |
+
|
| 750 |
+
const url = URL.createObjectURL(blob);
|
| 751 |
+
|
| 752 |
+
const a = document.createElement('a');
|
| 753 |
+
a.href = url;
|
| 754 |
+
a.download = filename;
|
| 755 |
+
|
| 756 |
+
document.body.appendChild(a);
|
| 757 |
+
a.click();
|
| 758 |
+
|
| 759 |
+
document.body.removeChild(a);
|
| 760 |
+
URL.revokeObjectURL(url);
|
| 761 |
+
return;
|
| 762 |
+
}
|
| 763 |
+
|
| 764 |
+
if (typeof JSZip === 'undefined') {
|
| 765 |
+
createTemplateModal(
|
| 766 |
+
'Unable to prepare the download. Please check your connection and try again.'
|
| 767 |
+
);
|
| 768 |
+
return;
|
| 769 |
+
}
|
| 770 |
+
|
| 771 |
+
const zip = new JSZip();
|
| 772 |
+
zip.file(filename, dataReport);
|
| 773 |
+
|
| 774 |
+
// Package each available media asset alongside the markdown report
|
| 775 |
+
mediaItems.forEach((raw, index) => {
|
| 776 |
+
if (typeof raw !== 'string' || !raw.trim()) {
|
| 777 |
+
return;
|
| 778 |
+
}
|
| 779 |
+
const base64Data = raw.replace(/\s+/g, '');
|
| 780 |
+
zip.file(`media/image-${index + 1}.png`, base64Data, { base64: true });
|
| 781 |
+
});
|
| 782 |
+
|
| 783 |
+
try {
|
| 784 |
+
const archiveBlob = await zip.generateAsync({ type: 'blob' });
|
| 785 |
+
const baseName = filename ? filename.replace(/\.[^/.]+$/, '') : 'report';
|
| 786 |
+
const archiveName = `${baseName || 'report'}-assets.zip`;
|
| 787 |
+
const url = URL.createObjectURL(archiveBlob);
|
| 788 |
+
|
| 789 |
+
const a = document.createElement('a');
|
| 790 |
+
a.href = url;
|
| 791 |
+
a.download = archiveName;
|
| 792 |
+
|
| 793 |
+
document.body.appendChild(a);
|
| 794 |
+
a.click();
|
| 795 |
+
|
| 796 |
+
document.body.removeChild(a);
|
| 797 |
+
URL.revokeObjectURL(url);
|
| 798 |
+
} catch (error) {
|
| 799 |
+
console.error('Failed to generate download archive', error);
|
| 800 |
+
createTemplateModal(
|
| 801 |
+
'Unable to prepare the download. Please try again later.'
|
| 802 |
+
);
|
| 803 |
+
}
|
| 804 |
+
}
|
| 805 |
+
|
| 806 |
+
window.goToEvidence = function () {
|
| 807 |
+
if (selectedTab.value === 'other_evidence') return;
|
| 808 |
+
document.querySelectorAll('.menu .item').forEach((item) => {
|
| 809 |
+
item.classList.remove('active');
|
| 810 |
+
});
|
| 811 |
+
document
|
| 812 |
+
.getElementById('other_evidence-1763023210526')
|
| 813 |
+
.classList.add('active');
|
| 814 |
+
selectedTab.value = 'other_evidence';
|
| 815 |
+
};
|
| 816 |
+
|
| 817 |
+
window.previewImage = function (imageRaw) {
|
| 818 |
+
const match = (imageRaw.match(/\d+/) || 1) - 1;
|
| 819 |
+
const image = data.value.readme_content.media[+match];
|
| 820 |
+
createTemplateModal(
|
| 821 |
+
`<img src="data:image/jpeg;base64,${image}" alt="Image Preview" style="max-width: 100%; height: auto;" />`,
|
| 822 |
+
`Image ${+match + 1}`
|
| 823 |
+
);
|
| 824 |
+
};
|
| 825 |
+
|
| 826 |
+
window.changeTabChildren = function (tab, element) {
|
| 827 |
+
if (isLoading.value) return;
|
| 828 |
+
document.querySelectorAll('.menu-children .item').forEach((item) => {
|
| 829 |
+
item.classList.remove('active');
|
| 830 |
+
});
|
| 831 |
+
element.classList.add('active');
|
| 832 |
+
|
| 833 |
+
selectedTabChildren.value = tab;
|
| 834 |
+
};
|
| 835 |
+
|
| 836 |
+
window.changeTab = function (tab, element) {
|
| 837 |
+
if (isLoading.value) return;
|
| 838 |
+
document.querySelectorAll('.menu .item').forEach((item) => {
|
| 839 |
+
item.classList.remove('active');
|
| 840 |
+
});
|
| 841 |
+
element.classList.add('active');
|
| 842 |
+
|
| 843 |
+
selectedTab.value = tab;
|
| 844 |
+
};
|
| 845 |
+
|
| 846 |
+
window.removeFile = function (event, fileId, fileType) {
|
| 847 |
+
event.preventDefault();
|
| 848 |
+
event.stopPropagation();
|
| 849 |
+
if (isLoading.value) return;
|
| 850 |
+
const [fileName, fileSize, fileLastModified] = fileId.split('|');
|
| 851 |
+
if (fileType === 'metadata') {
|
| 852 |
+
metadata.value = metadata.value.filter(
|
| 853 |
+
(file) =>
|
| 854 |
+
!(
|
| 855 |
+
file.name === fileName &&
|
| 856 |
+
file.size.toString() === fileSize &&
|
| 857 |
+
file.lastModified.toString() === fileLastModified
|
| 858 |
+
)
|
| 859 |
+
);
|
| 860 |
+
return;
|
| 861 |
+
}
|
| 862 |
+
files.value = files.value.filter(
|
| 863 |
+
(file) =>
|
| 864 |
+
!(
|
| 865 |
+
file.name === fileName &&
|
| 866 |
+
file.size.toString() === fileSize &&
|
| 867 |
+
file.lastModified.toString() === fileLastModified
|
| 868 |
+
)
|
| 869 |
+
);
|
| 870 |
+
};
|
| 871 |
+
|
| 872 |
+
function createTemplateModal(content, title = 'Notification') {
|
| 873 |
+
const modal = `<div class="header-modal">
|
| 874 |
+
<span class="title">${title}</span>
|
| 875 |
+
<div onclick="closeModal()" class="close">${closeIcon}</div>
|
| 876 |
+
</div>
|
| 877 |
+
<div class="content-modal scroll-box">
|
| 878 |
+
${content}
|
| 879 |
+
</div>`;
|
| 880 |
+
openModal(modal);
|
| 881 |
+
}
|
| 882 |
+
|
| 883 |
+
window.previewMetadata = function (event, fileId) {
|
| 884 |
+
event.preventDefault();
|
| 885 |
+
event.stopPropagation();
|
| 886 |
+
const [fileName, fileSize, fileLastModified] = fileId.split('|');
|
| 887 |
+
const file = metadata.value.find(
|
| 888 |
+
(f) =>
|
| 889 |
+
f.name === fileName &&
|
| 890 |
+
f.size.toString() === fileSize &&
|
| 891 |
+
f.lastModified.toString() === fileLastModified
|
| 892 |
+
);
|
| 893 |
+
if (!file) return;
|
| 894 |
+
|
| 895 |
+
const reader = new FileReader();
|
| 896 |
+
reader.onload = function (e) {
|
| 897 |
+
const content = `<pre style="white-space: pre-wrap; word-wrap: break-word;">${e.target.result}</pre>`;
|
| 898 |
+
const modal = createTemplateModal(content, file.name);
|
| 899 |
+
};
|
| 900 |
+
reader.readAsText(file);
|
| 901 |
+
};
|
| 902 |
+
|
| 903 |
+
window.previewFile = function (event, fileId) {
|
| 904 |
+
event.preventDefault();
|
| 905 |
+
event.stopPropagation();
|
| 906 |
+
const [fileName, fileSize, fileLastModified] = fileId.split('|');
|
| 907 |
+
const file = files.value.find(
|
| 908 |
+
(f) =>
|
| 909 |
+
f.name === fileName &&
|
| 910 |
+
f.size.toString() === fileSize &&
|
| 911 |
+
f.lastModified.toString() === fileLastModified
|
| 912 |
+
);
|
| 913 |
+
if (!file) return;
|
| 914 |
+
|
| 915 |
+
const fileURL = URL.createObjectURL(file);
|
| 916 |
+
currentPreviewURL = fileURL;
|
| 917 |
+
let content = '';
|
| 918 |
+
if (file.type.startsWith('image/')) {
|
| 919 |
+
content = `<img src="${fileURL}" alt="${file.name}" style="max-width: 100%; height: auto;" />`;
|
| 920 |
+
} else if (file.type.startsWith('video/')) {
|
| 921 |
+
content = `<video controls style="max-width: 100%; height: auto;">
|
| 922 |
+
<source src="${fileURL}" type="${file.type}">
|
| 923 |
+
Your browser does not support the video tag.
|
| 924 |
+
</video>`;
|
| 925 |
+
} else {
|
| 926 |
+
content = 'Preview not available for this file type.';
|
| 927 |
+
}
|
| 928 |
+
|
| 929 |
+
createTemplateModal(content, file.name);
|
| 930 |
+
};
|
| 931 |
+
|
| 932 |
+
function openModal(content) {
|
| 933 |
+
contentModalElement.innerHTML = content;
|
| 934 |
+
wrapperModalElement.style.display = 'flex';
|
| 935 |
+
}
|
| 936 |
+
|
| 937 |
+
window.closeModal = function () {
|
| 938 |
+
wrapperModalElement.style.display = 'none';
|
| 939 |
+
const video = contentModalElement.querySelector('video');
|
| 940 |
+
if (video) {
|
| 941 |
+
video.pause();
|
| 942 |
+
video.currentTime = 0;
|
| 943 |
+
}
|
| 944 |
+
if (currentPreviewURL) {
|
| 945 |
+
URL.revokeObjectURL(currentPreviewURL);
|
| 946 |
+
currentPreviewURL = null;
|
| 947 |
+
}
|
| 948 |
+
};
|
serverSentEvents.js
ADDED
|
@@ -0,0 +1,225 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { completedIcon, pendingIcon, processingIcon } from './constants.js';
|
| 2 |
+
import { apiBaseUrl, clientId, estTimeStep1, estTimeStep2 } from './main.js';
|
| 3 |
+
|
| 4 |
+
const statusFaceCheckElement = document.getElementById('status-face-check');
|
| 5 |
+
const initData = [
|
| 6 |
+
{
|
| 7 |
+
id: 1,
|
| 8 |
+
name: 'Geolocation',
|
| 9 |
+
displayName: 'Location Analysis',
|
| 10 |
+
status: 'pending',
|
| 11 |
+
percent: 0,
|
| 12 |
+
},
|
| 13 |
+
{
|
| 14 |
+
id: 2,
|
| 15 |
+
name: 'Timestamp',
|
| 16 |
+
displayName: 'Time Analysis',
|
| 17 |
+
status: 'pending',
|
| 18 |
+
percent: 0,
|
| 19 |
+
},
|
| 20 |
+
{
|
| 21 |
+
id: 3,
|
| 22 |
+
name: 'AIGVDetection',
|
| 23 |
+
displayName: 'AI-Generated Content Detection',
|
| 24 |
+
status: 'pending',
|
| 25 |
+
percent: 0,
|
| 26 |
+
},
|
| 27 |
+
{
|
| 28 |
+
id: 4,
|
| 29 |
+
name: 'Report',
|
| 30 |
+
displayName: 'Comprehensive Report',
|
| 31 |
+
status: 'pending',
|
| 32 |
+
percent: 0,
|
| 33 |
+
},
|
| 34 |
+
];
|
| 35 |
+
export let statusStep1 = 'pending';
|
| 36 |
+
export let statusStep2 = 'pending';
|
| 37 |
+
let _estTimeStep1 = 0;
|
| 38 |
+
let _estTimeStep2 = 0;
|
| 39 |
+
|
| 40 |
+
let data = {};
|
| 41 |
+
Object.defineProperty(data, 'value', {
|
| 42 |
+
set(newValue) {
|
| 43 |
+
const countItem = newValue.length;
|
| 44 |
+
const countItemCompleted = newValue.filter(
|
| 45 |
+
(item) => item.status === 'completed'
|
| 46 |
+
).length;
|
| 47 |
+
statusStep1 = 'pending';
|
| 48 |
+
statusStep2 = newValue.find((i) => i.name === 'Report').status;
|
| 49 |
+
if (
|
| 50 |
+
['Geolocation', 'Timestamp', 'AIGVDetection'].every((item) => {
|
| 51 |
+
return newValue.find((i) => i.name === item).status === 'completed';
|
| 52 |
+
})
|
| 53 |
+
) {
|
| 54 |
+
statusStep1 = 'completed';
|
| 55 |
+
} else if (
|
| 56 |
+
['Geolocation', 'Timestamp', 'AIGVDetection'].some((item) => {
|
| 57 |
+
return newValue.find((i) => i.name === item).status === 'processing';
|
| 58 |
+
})
|
| 59 |
+
) {
|
| 60 |
+
statusStep1 = 'processing';
|
| 61 |
+
}
|
| 62 |
+
|
| 63 |
+
const content = `<div class="status-face-check">
|
| 64 |
+
<div class="header-status-face-check">
|
| 65 |
+
<div class="title-status-face-check">
|
| 66 |
+
<p>${countItemCompleted}/${countItem} completed</p>
|
| 67 |
+
<p>${Math.round(
|
| 68 |
+
(countItemCompleted / countItem) * 100
|
| 69 |
+
)}%</p>
|
| 70 |
+
</div>
|
| 71 |
+
<div class="wrapper-processing">
|
| 72 |
+
<div class="processing" style="width: ${Math.round(
|
| 73 |
+
(countItemCompleted / countItem) * 100
|
| 74 |
+
)}%;"></div>
|
| 75 |
+
</div>
|
| 76 |
+
</div>
|
| 77 |
+
<div class="body-status-face-check">
|
| 78 |
+
${newValue
|
| 79 |
+
.map((item, index) => {
|
| 80 |
+
return `
|
| 81 |
+
${
|
| 82 |
+
index === 0
|
| 83 |
+
? `<div class="step">Step 1: ${
|
| 84 |
+
statusStep1 === 'pending'
|
| 85 |
+
? `Waiting - ${
|
| 86 |
+
_estTimeStep1 === 0
|
| 87 |
+
? 'Calculating...'
|
| 88 |
+
: `${_estTimeStep1} minutes remaining.`
|
| 89 |
+
}`
|
| 90 |
+
: statusStep1 === 'processing'
|
| 91 |
+
? `Processing - Estimated time: ${_estTimeStep1} minutes.`
|
| 92 |
+
: `Completed`
|
| 93 |
+
}</div>`
|
| 94 |
+
: ''
|
| 95 |
+
}
|
| 96 |
+
${
|
| 97 |
+
index === 3
|
| 98 |
+
? `<div class="step">Step 2: ${
|
| 99 |
+
statusStep2 === 'pending'
|
| 100 |
+
? `Waiting - ${
|
| 101 |
+
_estTimeStep2 === 0
|
| 102 |
+
? 'Calculating...'
|
| 103 |
+
: `Begins after Step 1 is completed (Estimated time: ${_estTimeStep2} minutes).`
|
| 104 |
+
}`
|
| 105 |
+
: statusStep2 === 'processing'
|
| 106 |
+
? `Processing - Estimated time: ${_estTimeStep2} minutes.`
|
| 107 |
+
: `Completed`
|
| 108 |
+
} </div>`
|
| 109 |
+
: ''
|
| 110 |
+
}
|
| 111 |
+
<div class="item-status-face-check">
|
| 112 |
+
<span
|
| 113 |
+
>${
|
| 114 |
+
item.status === 'completed'
|
| 115 |
+
? completedIcon
|
| 116 |
+
: item.status === 'processing'
|
| 117 |
+
? processingIcon
|
| 118 |
+
: pendingIcon
|
| 119 |
+
}
|
| 120 |
+
</span>
|
| 121 |
+
<div class="content-status-face-check">
|
| 122 |
+
<p class="name-service">Service ${
|
| 123 |
+
index + 1
|
| 124 |
+
}/${countItem}: ${item.displayName}</p>
|
| 125 |
+
<p class="status-service ${
|
| 126 |
+
item.status === 'pending'
|
| 127 |
+
? 'display-none'
|
| 128 |
+
: item.status === 'processing'
|
| 129 |
+
? 'processing-dots'
|
| 130 |
+
: ''
|
| 131 |
+
}">${
|
| 132 |
+
item.status === 'processing'
|
| 133 |
+
? 'Processing'
|
| 134 |
+
: item.status === 'completed'
|
| 135 |
+
? 'Completed'
|
| 136 |
+
: 'Pending'
|
| 137 |
+
}<span class="dots "></span></p>
|
| 138 |
+
</div>
|
| 139 |
+
</div>`;
|
| 140 |
+
})
|
| 141 |
+
.join('')}
|
| 142 |
+
</div>
|
| 143 |
+
</div>`;
|
| 144 |
+
statusFaceCheckElement.innerHTML = content;
|
| 145 |
+
this._value = newValue;
|
| 146 |
+
},
|
| 147 |
+
get() {
|
| 148 |
+
return this._value;
|
| 149 |
+
},
|
| 150 |
+
});
|
| 151 |
+
data.value = initData;
|
| 152 |
+
|
| 153 |
+
export function initValueData() {
|
| 154 |
+
console.log('test...');
|
| 155 |
+
}
|
| 156 |
+
|
| 157 |
+
let isLoading = {};
|
| 158 |
+
Object.defineProperty(isLoading, 'value', {
|
| 159 |
+
set(newValue) {
|
| 160 |
+
if (newValue) {
|
| 161 |
+
// statusFaceCheckElement.classList.remove('display-none');
|
| 162 |
+
data.value = initData;
|
| 163 |
+
} else {
|
| 164 |
+
statusFaceCheckElement.classList.add('display-none');
|
| 165 |
+
data.value = initData;
|
| 166 |
+
}
|
| 167 |
+
this._value = newValue;
|
| 168 |
+
},
|
| 169 |
+
get() {
|
| 170 |
+
return this._value;
|
| 171 |
+
},
|
| 172 |
+
});
|
| 173 |
+
const source = new EventSource(`${apiBaseUrl}/sse?client_id=${clientId}`);
|
| 174 |
+
|
| 175 |
+
source.onopen = () => console.log('✅ SSE connected');
|
| 176 |
+
|
| 177 |
+
let _interval;
|
| 178 |
+
source.onmessage = (event) => {
|
| 179 |
+
try {
|
| 180 |
+
const _data = JSON.parse(event.data);
|
| 181 |
+
|
| 182 |
+
console.log(_data);
|
| 183 |
+
|
| 184 |
+
if (_data?.status) {
|
| 185 |
+
if (_data.status === 'start') {
|
| 186 |
+
isLoading.value = true;
|
| 187 |
+
_estTimeStep1 = estTimeStep1;
|
| 188 |
+
_estTimeStep2 = estTimeStep2;
|
| 189 |
+
_interval = setInterval(() => {
|
| 190 |
+
if (statusStep1 === 'processing' && _estTimeStep1 > 1) {
|
| 191 |
+
_estTimeStep1 -= 1;
|
| 192 |
+
}
|
| 193 |
+
if (statusStep2 === 'processing' && _estTimeStep2 > 1) {
|
| 194 |
+
_estTimeStep2 -= 1;
|
| 195 |
+
}
|
| 196 |
+
data.value = data.value;
|
| 197 |
+
}, 60000);
|
| 198 |
+
} else {
|
| 199 |
+
isLoading.value = false;
|
| 200 |
+
_estTimeStep1 = 0;
|
| 201 |
+
_estTimeStep2 = 0;
|
| 202 |
+
data.value = initData;
|
| 203 |
+
clearInterval(_interval);
|
| 204 |
+
}
|
| 205 |
+
}
|
| 206 |
+
if (_data?.status_service) {
|
| 207 |
+
const updatedData = data.value?.map((item) => {
|
| 208 |
+
if (_data.service === item.name) {
|
| 209 |
+
if (_data.status_service === 'completed') {
|
| 210 |
+
return { ...item, status: _data.status_service, percent: 100 };
|
| 211 |
+
}
|
| 212 |
+
return { ...item, status: _data.status_service };
|
| 213 |
+
}
|
| 214 |
+
return item;
|
| 215 |
+
});
|
| 216 |
+
data.value = updatedData;
|
| 217 |
+
}
|
| 218 |
+
} catch (err) {
|
| 219 |
+
console.log('err', err);
|
| 220 |
+
}
|
| 221 |
+
};
|
| 222 |
+
|
| 223 |
+
source.onerror = (err) => {
|
| 224 |
+
console.error('❌ SSE error:', err);
|
| 225 |
+
};
|
styles.css
ADDED
|
@@ -0,0 +1,954 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
* {
|
| 2 |
+
margin: 0;
|
| 3 |
+
padding: 0;
|
| 4 |
+
box-sizing: border-box;
|
| 5 |
+
user-select: none;
|
| 6 |
+
}
|
| 7 |
+
|
| 8 |
+
html,
|
| 9 |
+
body {
|
| 10 |
+
height: 100%;
|
| 11 |
+
font-family: Arial, Helvetica, sans-serif;
|
| 12 |
+
line-height: 1.5;
|
| 13 |
+
-webkit-font-smoothing: antialiased;
|
| 14 |
+
}
|
| 15 |
+
|
| 16 |
+
img,
|
| 17 |
+
picture,
|
| 18 |
+
video,
|
| 19 |
+
canvas,
|
| 20 |
+
svg {
|
| 21 |
+
display: block;
|
| 22 |
+
max-width: 100%;
|
| 23 |
+
}
|
| 24 |
+
|
| 25 |
+
button,
|
| 26 |
+
input,
|
| 27 |
+
select,
|
| 28 |
+
textarea {
|
| 29 |
+
font: inherit;
|
| 30 |
+
}
|
| 31 |
+
|
| 32 |
+
a {
|
| 33 |
+
color: inherit;
|
| 34 |
+
text-decoration: none;
|
| 35 |
+
}
|
| 36 |
+
|
| 37 |
+
ul,
|
| 38 |
+
ol {
|
| 39 |
+
list-style: none;
|
| 40 |
+
}
|
| 41 |
+
|
| 42 |
+
/* General Body Styling */
|
| 43 |
+
body {
|
| 44 |
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
|
| 45 |
+
'Helvetica Neue', Arial, sans-serif;
|
| 46 |
+
background-color: #f8f9fa;
|
| 47 |
+
color: #333;
|
| 48 |
+
display: flex;
|
| 49 |
+
justify-content: center;
|
| 50 |
+
align-items: center;
|
| 51 |
+
min-height: 100vh;
|
| 52 |
+
margin: 0;
|
| 53 |
+
box-sizing: border-box;
|
| 54 |
+
}
|
| 55 |
+
|
| 56 |
+
/* Main Container */
|
| 57 |
+
.container {
|
| 58 |
+
background-color: #ffffff;
|
| 59 |
+
width: 100%;
|
| 60 |
+
height: 100%;
|
| 61 |
+
position: relative;
|
| 62 |
+
}
|
| 63 |
+
|
| 64 |
+
/* Header Section */
|
| 65 |
+
.header-face-check {
|
| 66 |
+
text-align: center;
|
| 67 |
+
border-bottom: 1px solid #e2e8f0;
|
| 68 |
+
padding: 15px 0;
|
| 69 |
+
}
|
| 70 |
+
|
| 71 |
+
.title-header-face-check {
|
| 72 |
+
font-size: 28px;
|
| 73 |
+
font-weight: 600;
|
| 74 |
+
}
|
| 75 |
+
|
| 76 |
+
/* Main Content Layout */
|
| 77 |
+
.main-content {
|
| 78 |
+
display: flex;
|
| 79 |
+
}
|
| 80 |
+
|
| 81 |
+
.description-upload-section {
|
| 82 |
+
color: #6c757d;
|
| 83 |
+
padding-bottom: 4px;
|
| 84 |
+
}
|
| 85 |
+
|
| 86 |
+
/* Upload Section Styling */
|
| 87 |
+
.upload-section {
|
| 88 |
+
display: flex;
|
| 89 |
+
flex-direction: column;
|
| 90 |
+
width: 25%;
|
| 91 |
+
height: calc(100vh - 73px);
|
| 92 |
+
}
|
| 93 |
+
|
| 94 |
+
.wrapper-upload-box {
|
| 95 |
+
flex-grow: 1;
|
| 96 |
+
overflow: scroll;
|
| 97 |
+
}
|
| 98 |
+
|
| 99 |
+
.upload-box {
|
| 100 |
+
padding: 1rem;
|
| 101 |
+
display: flex;
|
| 102 |
+
flex-direction: column;
|
| 103 |
+
}
|
| 104 |
+
|
| 105 |
+
.upload-box > p {
|
| 106 |
+
font-size: 18px;
|
| 107 |
+
font-weight: 600;
|
| 108 |
+
color: #454545;
|
| 109 |
+
margin-bottom: 12px;
|
| 110 |
+
}
|
| 111 |
+
|
| 112 |
+
.drop-zone {
|
| 113 |
+
border: 2px dashed #ced4da;
|
| 114 |
+
border-radius: 8px;
|
| 115 |
+
padding: 24px 4px;
|
| 116 |
+
text-align: center;
|
| 117 |
+
cursor: pointer;
|
| 118 |
+
transition: background-color 0.2s ease, border-color 0.2s ease;
|
| 119 |
+
flex-grow: 1;
|
| 120 |
+
display: flex;
|
| 121 |
+
flex-direction: column;
|
| 122 |
+
justify-content: center;
|
| 123 |
+
align-items: center;
|
| 124 |
+
}
|
| 125 |
+
|
| 126 |
+
.drop-zone:hover {
|
| 127 |
+
background-color: #f8f9fa;
|
| 128 |
+
border-color: #adb5bd;
|
| 129 |
+
}
|
| 130 |
+
|
| 131 |
+
.drop-zone.drag-over {
|
| 132 |
+
background-color: #e3f2fd;
|
| 133 |
+
border-color: #2196f3;
|
| 134 |
+
}
|
| 135 |
+
|
| 136 |
+
.drop-zone-icon {
|
| 137 |
+
width: 48px;
|
| 138 |
+
height: 48px;
|
| 139 |
+
color: #6c757d;
|
| 140 |
+
margin-bottom: 1rem;
|
| 141 |
+
}
|
| 142 |
+
|
| 143 |
+
.drop-zone p {
|
| 144 |
+
margin: 0.25rem 0;
|
| 145 |
+
font-weight: 500;
|
| 146 |
+
}
|
| 147 |
+
|
| 148 |
+
.drop-zone span {
|
| 149 |
+
font-size: 16px;
|
| 150 |
+
font-weight: 700;
|
| 151 |
+
line-height: 22px;
|
| 152 |
+
color: #fd7e14;
|
| 153 |
+
}
|
| 154 |
+
|
| 155 |
+
.wrapper-upload {
|
| 156 |
+
display: flex;
|
| 157 |
+
flex-direction: column;
|
| 158 |
+
align-items: center;
|
| 159 |
+
justify-content: center;
|
| 160 |
+
}
|
| 161 |
+
|
| 162 |
+
.list-file {
|
| 163 |
+
display: flex;
|
| 164 |
+
flex-direction: column;
|
| 165 |
+
gap: 12px;
|
| 166 |
+
padding: 0 1rem 1rem 1rem;
|
| 167 |
+
}
|
| 168 |
+
|
| 169 |
+
.file-item {
|
| 170 |
+
display: flex;
|
| 171 |
+
align-items: center;
|
| 172 |
+
gap: 8px;
|
| 173 |
+
padding: 8px 16px;
|
| 174 |
+
border: 1px solid #ced4da;
|
| 175 |
+
border-radius: 16px;
|
| 176 |
+
cursor: default;
|
| 177 |
+
}
|
| 178 |
+
|
| 179 |
+
.file-item .file-icon {
|
| 180 |
+
display: inline-block;
|
| 181 |
+
width: 40px;
|
| 182 |
+
height: 40px;
|
| 183 |
+
flex-shrink: 0;
|
| 184 |
+
}
|
| 185 |
+
|
| 186 |
+
.file-item .file-name {
|
| 187 |
+
flex-grow: 1;
|
| 188 |
+
text-decoration: underline;
|
| 189 |
+
font-weight: 500;
|
| 190 |
+
font-size: 16px;
|
| 191 |
+
line-height: 22px;
|
| 192 |
+
display: inline-block;
|
| 193 |
+
overflow: hidden;
|
| 194 |
+
text-overflow: ellipsis;
|
| 195 |
+
white-space: nowrap;
|
| 196 |
+
vertical-align: middle;
|
| 197 |
+
cursor: pointer;
|
| 198 |
+
transition: opacity 0.2s ease;
|
| 199 |
+
}
|
| 200 |
+
|
| 201 |
+
.file-item .file-name:hover {
|
| 202 |
+
opacity: 0.8;
|
| 203 |
+
}
|
| 204 |
+
|
| 205 |
+
.file-item .file-thumbnail {
|
| 206 |
+
display: inline-block;
|
| 207 |
+
width: 40px;
|
| 208 |
+
height: 40px;
|
| 209 |
+
border-radius: 12px;
|
| 210 |
+
flex-shrink: 0;
|
| 211 |
+
background-color: #f1f3f5;
|
| 212 |
+
overflow: hidden;
|
| 213 |
+
}
|
| 214 |
+
|
| 215 |
+
.file-item .file-thumbnail-image {
|
| 216 |
+
object-fit: cover;
|
| 217 |
+
display: inline-block;
|
| 218 |
+
}
|
| 219 |
+
|
| 220 |
+
.file-item .file-thumbnail-video {
|
| 221 |
+
display: inline-flex;
|
| 222 |
+
align-items: center;
|
| 223 |
+
justify-content: center;
|
| 224 |
+
position: relative;
|
| 225 |
+
background-color: #10151a;
|
| 226 |
+
}
|
| 227 |
+
|
| 228 |
+
.file-item .file-thumbnail-video video {
|
| 229 |
+
width: 100%;
|
| 230 |
+
height: 100%;
|
| 231 |
+
object-fit: cover;
|
| 232 |
+
pointer-events: none;
|
| 233 |
+
}
|
| 234 |
+
|
| 235 |
+
.file-item .file-thumbnail-video::before {
|
| 236 |
+
content: '';
|
| 237 |
+
position: absolute;
|
| 238 |
+
width: 28px;
|
| 239 |
+
height: 28px;
|
| 240 |
+
border-radius: 50%;
|
| 241 |
+
background: rgba(0, 0, 0, 0.45);
|
| 242 |
+
backdrop-filter: blur(2px);
|
| 243 |
+
}
|
| 244 |
+
|
| 245 |
+
.file-item .file-thumbnail-video::after {
|
| 246 |
+
content: '';
|
| 247 |
+
position: absolute;
|
| 248 |
+
width: 0;
|
| 249 |
+
height: 0;
|
| 250 |
+
border-left: 10px solid #ffffff;
|
| 251 |
+
border-top: 7px solid transparent;
|
| 252 |
+
border-bottom: 7px solid transparent;
|
| 253 |
+
margin-left: 3px;
|
| 254 |
+
}
|
| 255 |
+
|
| 256 |
+
.file-item .preview {
|
| 257 |
+
width: 24px;
|
| 258 |
+
height: 24px;
|
| 259 |
+
flex-shrink: 0;
|
| 260 |
+
cursor: pointer;
|
| 261 |
+
}
|
| 262 |
+
|
| 263 |
+
.file-item .preview:hover {
|
| 264 |
+
opacity: 0.8;
|
| 265 |
+
}
|
| 266 |
+
|
| 267 |
+
.file-item .file-remove {
|
| 268 |
+
cursor: pointer;
|
| 269 |
+
display: inline-block;
|
| 270 |
+
width: 24px;
|
| 271 |
+
height: 24px;
|
| 272 |
+
flex-shrink: 0;
|
| 273 |
+
}
|
| 274 |
+
|
| 275 |
+
#file-input {
|
| 276 |
+
display: none;
|
| 277 |
+
}
|
| 278 |
+
|
| 279 |
+
/* Button Styling */
|
| 280 |
+
.button-group {
|
| 281 |
+
display: flex;
|
| 282 |
+
gap: 0.75rem;
|
| 283 |
+
padding: 20px 24px;
|
| 284 |
+
border-top: 1px solid #e2e8f0;
|
| 285 |
+
}
|
| 286 |
+
|
| 287 |
+
.btn {
|
| 288 |
+
padding: 0.75rem 1.5rem;
|
| 289 |
+
border: none;
|
| 290 |
+
border-radius: 6px;
|
| 291 |
+
font-size: 1rem;
|
| 292 |
+
font-weight: 600;
|
| 293 |
+
cursor: pointer;
|
| 294 |
+
transition: background-color 0.2s ease;
|
| 295 |
+
width: 50%;
|
| 296 |
+
}
|
| 297 |
+
|
| 298 |
+
.btn-clear {
|
| 299 |
+
background-color: #e9ecef;
|
| 300 |
+
color: #495057;
|
| 301 |
+
border: 1px solid #dee2e6;
|
| 302 |
+
}
|
| 303 |
+
|
| 304 |
+
.btn-clear:hover {
|
| 305 |
+
background-color: #dee2e6;
|
| 306 |
+
}
|
| 307 |
+
|
| 308 |
+
.wrapper-btn-save {
|
| 309 |
+
display: flex;
|
| 310 |
+
justify-content: center;
|
| 311 |
+
align-items: center;
|
| 312 |
+
width: 100%;
|
| 313 |
+
background-color: #ffffff;
|
| 314 |
+
border-top: 1px solid #e2e8f0;
|
| 315 |
+
padding: 20px 24px;
|
| 316 |
+
}
|
| 317 |
+
|
| 318 |
+
.btn-submit {
|
| 319 |
+
background-color: #fd7e14;
|
| 320 |
+
color: #ffffff;
|
| 321 |
+
}
|
| 322 |
+
|
| 323 |
+
.btn-submit:hover {
|
| 324 |
+
background-color: #f96d00;
|
| 325 |
+
}
|
| 326 |
+
|
| 327 |
+
/* Output Section Styling */
|
| 328 |
+
.output-section {
|
| 329 |
+
flex: 1;
|
| 330 |
+
display: flex;
|
| 331 |
+
flex-direction: column;
|
| 332 |
+
border-left: 1px solid #dee2e6;
|
| 333 |
+
border-right: 1px solid #dee2e6;
|
| 334 |
+
height: calc(100vh - 73px);
|
| 335 |
+
background-color: #f5f5f5;
|
| 336 |
+
}
|
| 337 |
+
|
| 338 |
+
.output-section label {
|
| 339 |
+
font-weight: 600;
|
| 340 |
+
display: block;
|
| 341 |
+
padding-left: 1rem;
|
| 342 |
+
}
|
| 343 |
+
|
| 344 |
+
.output-wrapper {
|
| 345 |
+
position: relative;
|
| 346 |
+
width: 100%;
|
| 347 |
+
flex: 1 1;
|
| 348 |
+
padding: 1rem;
|
| 349 |
+
overflow: auto;
|
| 350 |
+
}
|
| 351 |
+
|
| 352 |
+
.wrapper-loading {
|
| 353 |
+
position: absolute;
|
| 354 |
+
top: 50%;
|
| 355 |
+
left: 50%;
|
| 356 |
+
}
|
| 357 |
+
|
| 358 |
+
.output-face-check {
|
| 359 |
+
position: relative;
|
| 360 |
+
flex-grow: 1;
|
| 361 |
+
width: 100%;
|
| 362 |
+
height: 100%;
|
| 363 |
+
padding: 1rem;
|
| 364 |
+
border: 1px solid #ced4da;
|
| 365 |
+
border-radius: 8px;
|
| 366 |
+
resize: none;
|
| 367 |
+
font-family: inherit;
|
| 368 |
+
font-size: 1rem;
|
| 369 |
+
box-sizing: border-box;
|
| 370 |
+
background-color: #ffffff;
|
| 371 |
+
}
|
| 372 |
+
|
| 373 |
+
.btn-save {
|
| 374 |
+
background-color: #e9ecef;
|
| 375 |
+
color: #495057;
|
| 376 |
+
border: 1px solid #dee2e6;
|
| 377 |
+
align-self: flex-start; /* Align to the left */
|
| 378 |
+
width: 300px;
|
| 379 |
+
}
|
| 380 |
+
|
| 381 |
+
.btn-save:hover {
|
| 382 |
+
background-color: #dee2e6;
|
| 383 |
+
}
|
| 384 |
+
.btn-save.active {
|
| 385 |
+
background-color: #fd7e14;
|
| 386 |
+
color: #ffffff;
|
| 387 |
+
}
|
| 388 |
+
.btn-save.active:hover {
|
| 389 |
+
background-color: #f96d00;
|
| 390 |
+
}
|
| 391 |
+
|
| 392 |
+
.btn-disabled {
|
| 393 |
+
cursor: not-allowed !important;
|
| 394 |
+
}
|
| 395 |
+
|
| 396 |
+
.display-none {
|
| 397 |
+
display: none !important;
|
| 398 |
+
}
|
| 399 |
+
|
| 400 |
+
.wrapper-modal {
|
| 401 |
+
position: fixed;
|
| 402 |
+
top: 0;
|
| 403 |
+
left: 0;
|
| 404 |
+
width: 100%;
|
| 405 |
+
height: 100%;
|
| 406 |
+
background: rgba(0, 0, 0, 0.5);
|
| 407 |
+
display: none;
|
| 408 |
+
justify-content: center;
|
| 409 |
+
align-items: center;
|
| 410 |
+
z-index: 1000;
|
| 411 |
+
}
|
| 412 |
+
|
| 413 |
+
.modal {
|
| 414 |
+
display: flex;
|
| 415 |
+
gap: 24px;
|
| 416 |
+
flex-direction: column;
|
| 417 |
+
justify-content: center;
|
| 418 |
+
align-items: center;
|
| 419 |
+
padding: 12px 16px 24px 16px;
|
| 420 |
+
background: white;
|
| 421 |
+
border-radius: 16px;
|
| 422 |
+
width: 700px;
|
| 423 |
+
}
|
| 424 |
+
|
| 425 |
+
.header-modal {
|
| 426 |
+
/* height: 40px; */
|
| 427 |
+
width: 100%;
|
| 428 |
+
display: flex;
|
| 429 |
+
align-items: center;
|
| 430 |
+
justify-content: space-between;
|
| 431 |
+
border-bottom: 1px solid #eef1f4;
|
| 432 |
+
}
|
| 433 |
+
|
| 434 |
+
.header-modal .title {
|
| 435 |
+
flex: 1;
|
| 436 |
+
color: #454545;
|
| 437 |
+
font-weight: 700;
|
| 438 |
+
font-size: 16px;
|
| 439 |
+
line-height: 22px;
|
| 440 |
+
}
|
| 441 |
+
|
| 442 |
+
.header-modal .close {
|
| 443 |
+
height: 40px;
|
| 444 |
+
width: 40px;
|
| 445 |
+
display: block;
|
| 446 |
+
cursor: pointer;
|
| 447 |
+
}
|
| 448 |
+
|
| 449 |
+
.content-modal {
|
| 450 |
+
width: 100%;
|
| 451 |
+
max-height: 80vh;
|
| 452 |
+
overflow: auto;
|
| 453 |
+
text-align: center;
|
| 454 |
+
}
|
| 455 |
+
|
| 456 |
+
.content-modal img,
|
| 457 |
+
.content-modal video {
|
| 458 |
+
width: 100%;
|
| 459 |
+
height: auto;
|
| 460 |
+
object-fit: cover;
|
| 461 |
+
}
|
| 462 |
+
|
| 463 |
+
.content-modal > pre {
|
| 464 |
+
text-align: left;
|
| 465 |
+
}
|
| 466 |
+
|
| 467 |
+
.line {
|
| 468 |
+
width: 100%;
|
| 469 |
+
height: 1px;
|
| 470 |
+
background-color: #e9e9e9;
|
| 471 |
+
}
|
| 472 |
+
|
| 473 |
+
.menu {
|
| 474 |
+
display: flex;
|
| 475 |
+
padding-top: 12px;
|
| 476 |
+
border-bottom: 1px solid #e2e8f0;
|
| 477 |
+
background-color: #ffffff;
|
| 478 |
+
overflow: auto;
|
| 479 |
+
}
|
| 480 |
+
|
| 481 |
+
.menu .item {
|
| 482 |
+
flex-shrink: 0;
|
| 483 |
+
padding: 8px 16px;
|
| 484 |
+
cursor: pointer;
|
| 485 |
+
border-bottom: 2px solid #cbd5e1;
|
| 486 |
+
font-weight: 600;
|
| 487 |
+
font-size: 14px;
|
| 488 |
+
line-height: 20px;
|
| 489 |
+
color: #454545;
|
| 490 |
+
|
| 491 |
+
white-space: normal;
|
| 492 |
+
overflow: hidden;
|
| 493 |
+
}
|
| 494 |
+
|
| 495 |
+
.menu .item:hover {
|
| 496 |
+
border-bottom: 2px solid #fd7e14;
|
| 497 |
+
}
|
| 498 |
+
|
| 499 |
+
.menu .item.no-hover {
|
| 500 |
+
pointer-events: none;
|
| 501 |
+
}
|
| 502 |
+
|
| 503 |
+
.menu .item.active {
|
| 504 |
+
border-bottom: 2px solid #fd7e14;
|
| 505 |
+
}
|
| 506 |
+
|
| 507 |
+
/* Responsive Design for smaller screens */
|
| 508 |
+
@media (max-width: 768px) {
|
| 509 |
+
.main-content {
|
| 510 |
+
flex-direction: column;
|
| 511 |
+
}
|
| 512 |
+
body {
|
| 513 |
+
padding: 1rem;
|
| 514 |
+
}
|
| 515 |
+
.container {
|
| 516 |
+
padding: 1.5rem;
|
| 517 |
+
}
|
| 518 |
+
.upload-section {
|
| 519 |
+
width: 100%;
|
| 520 |
+
}
|
| 521 |
+
}
|
| 522 |
+
@keyframes l2 {
|
| 523 |
+
100% {
|
| 524 |
+
transform: rotate(calc(var(--s, 1) * 1turn));
|
| 525 |
+
}
|
| 526 |
+
}
|
| 527 |
+
|
| 528 |
+
.scroll-box {
|
| 529 |
+
overflow: auto;
|
| 530 |
+
/* scrollbar-width: none; */
|
| 531 |
+
-ms-overflow-style: none;
|
| 532 |
+
}
|
| 533 |
+
.scroll-box::-webkit-scrollbar {
|
| 534 |
+
width: 0;
|
| 535 |
+
height: 0;
|
| 536 |
+
}
|
| 537 |
+
|
| 538 |
+
.opacity-50 {
|
| 539 |
+
opacity: 0.5;
|
| 540 |
+
}
|
| 541 |
+
|
| 542 |
+
.wrapper-status-face-check {
|
| 543 |
+
position: absolute;
|
| 544 |
+
top: 0;
|
| 545 |
+
left: 0;
|
| 546 |
+
right: 0;
|
| 547 |
+
bottom: 0;
|
| 548 |
+
background-color: #454545a8;
|
| 549 |
+
display: flex;
|
| 550 |
+
justify-content: center;
|
| 551 |
+
align-items: center;
|
| 552 |
+
}
|
| 553 |
+
|
| 554 |
+
.status-face-check {
|
| 555 |
+
display: flex;
|
| 556 |
+
flex-direction: column;
|
| 557 |
+
gap: 16px;
|
| 558 |
+
width: 360px;
|
| 559 |
+
color: #ffffff;
|
| 560 |
+
}
|
| 561 |
+
|
| 562 |
+
.header-status-face-check {
|
| 563 |
+
display: flex;
|
| 564 |
+
flex-direction: column;
|
| 565 |
+
gap: 12px;
|
| 566 |
+
}
|
| 567 |
+
|
| 568 |
+
.title-status-face-check {
|
| 569 |
+
display: flex;
|
| 570 |
+
justify-content: space-between;
|
| 571 |
+
}
|
| 572 |
+
|
| 573 |
+
.step {
|
| 574 |
+
font-weight: 600;
|
| 575 |
+
font-size: 16px;
|
| 576 |
+
}
|
| 577 |
+
|
| 578 |
+
.wrapper-processing {
|
| 579 |
+
width: 100%;
|
| 580 |
+
height: 12px;
|
| 581 |
+
border-radius: 1234px;
|
| 582 |
+
background-color: #ffffff4d;
|
| 583 |
+
}
|
| 584 |
+
|
| 585 |
+
.processing {
|
| 586 |
+
height: 100%;
|
| 587 |
+
border-radius: 1234px;
|
| 588 |
+
background-color: #4bed73;
|
| 589 |
+
width: 30%;
|
| 590 |
+
transition: width 0.3s ease;
|
| 591 |
+
}
|
| 592 |
+
|
| 593 |
+
.body-status-face-check {
|
| 594 |
+
display: flex;
|
| 595 |
+
flex-direction: column;
|
| 596 |
+
gap: 12px;
|
| 597 |
+
}
|
| 598 |
+
|
| 599 |
+
.item-status-face-check {
|
| 600 |
+
display: flex;
|
| 601 |
+
gap: 8px;
|
| 602 |
+
padding-left: 12px;
|
| 603 |
+
}
|
| 604 |
+
|
| 605 |
+
.content-status-face-check {
|
| 606 |
+
flex: 1;
|
| 607 |
+
display: flex;
|
| 608 |
+
flex-direction: column;
|
| 609 |
+
gap: 4px;
|
| 610 |
+
}
|
| 611 |
+
|
| 612 |
+
.name-service {
|
| 613 |
+
font-size: 14px;
|
| 614 |
+
font-weight: 500;
|
| 615 |
+
line-height: 20px;
|
| 616 |
+
}
|
| 617 |
+
|
| 618 |
+
.status-service {
|
| 619 |
+
font-size: 14px;
|
| 620 |
+
font-weight: 400;
|
| 621 |
+
line-height: 100%;
|
| 622 |
+
}
|
| 623 |
+
|
| 624 |
+
.wrapper-processing-item {
|
| 625 |
+
width: 100%;
|
| 626 |
+
height: 8px;
|
| 627 |
+
border-radius: 1234px;
|
| 628 |
+
background-color: #ffffff4d;
|
| 629 |
+
overflow: hidden;
|
| 630 |
+
position: relative;
|
| 631 |
+
}
|
| 632 |
+
|
| 633 |
+
.processing-item {
|
| 634 |
+
position: absolute;
|
| 635 |
+
top: 0;
|
| 636 |
+
left: 0;
|
| 637 |
+
height: 100%;
|
| 638 |
+
border-radius: inherit;
|
| 639 |
+
background-color: #fd7e14;
|
| 640 |
+
}
|
| 641 |
+
|
| 642 |
+
.pt-0 {
|
| 643 |
+
padding-top: 0;
|
| 644 |
+
}
|
| 645 |
+
|
| 646 |
+
.mb-4 {
|
| 647 |
+
margin-bottom: 1rem;
|
| 648 |
+
}
|
| 649 |
+
|
| 650 |
+
.pointer {
|
| 651 |
+
cursor: pointer;
|
| 652 |
+
}
|
| 653 |
+
|
| 654 |
+
.loader {
|
| 655 |
+
position: relative;
|
| 656 |
+
width: 100%;
|
| 657 |
+
height: 8px;
|
| 658 |
+
border-radius: 1234px;
|
| 659 |
+
background: #d6cccc;
|
| 660 |
+
overflow: hidden;
|
| 661 |
+
}
|
| 662 |
+
|
| 663 |
+
.loader::before {
|
| 664 |
+
content: '';
|
| 665 |
+
position: absolute;
|
| 666 |
+
top: 0;
|
| 667 |
+
left: -50%;
|
| 668 |
+
width: 50%;
|
| 669 |
+
height: 100%;
|
| 670 |
+
background: #fd7e14;
|
| 671 |
+
border-radius: inherit;
|
| 672 |
+
animation: loading 2s infinite linear;
|
| 673 |
+
}
|
| 674 |
+
|
| 675 |
+
@keyframes loading {
|
| 676 |
+
0% {
|
| 677 |
+
left: -50%;
|
| 678 |
+
}
|
| 679 |
+
100% {
|
| 680 |
+
left: 100%;
|
| 681 |
+
}
|
| 682 |
+
}
|
| 683 |
+
|
| 684 |
+
.status-service.processing-dots .dots::after {
|
| 685 |
+
content: '';
|
| 686 |
+
animation: dots 1.5s steps(4, end) infinite;
|
| 687 |
+
}
|
| 688 |
+
|
| 689 |
+
@keyframes dots {
|
| 690 |
+
0% {
|
| 691 |
+
content: '';
|
| 692 |
+
}
|
| 693 |
+
25% {
|
| 694 |
+
content: '.';
|
| 695 |
+
}
|
| 696 |
+
50% {
|
| 697 |
+
content: '..';
|
| 698 |
+
}
|
| 699 |
+
75% {
|
| 700 |
+
content: '...';
|
| 701 |
+
}
|
| 702 |
+
100% {
|
| 703 |
+
content: '';
|
| 704 |
+
}
|
| 705 |
+
}
|
| 706 |
+
|
| 707 |
+
.json-box {
|
| 708 |
+
background-color: #e9ecef;
|
| 709 |
+
border-radius: 6px;
|
| 710 |
+
padding: 16px 20px;
|
| 711 |
+
font-family: Consolas, monospace;
|
| 712 |
+
font-size: 14px;
|
| 713 |
+
line-height: 1.6;
|
| 714 |
+
color: #333;
|
| 715 |
+
white-space: pre;
|
| 716 |
+
box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05);
|
| 717 |
+
text-align: left;
|
| 718 |
+
}
|
| 719 |
+
|
| 720 |
+
.note {
|
| 721 |
+
margin-bottom: 12px;
|
| 722 |
+
font-family: Arial, sans-serif;
|
| 723 |
+
font-size: 14px;
|
| 724 |
+
color: #444;
|
| 725 |
+
}
|
| 726 |
+
|
| 727 |
+
.image-bg {
|
| 728 |
+
color: #176ebf;
|
| 729 |
+
text-decoration: underline;
|
| 730 |
+
cursor: pointer;
|
| 731 |
+
}
|
| 732 |
+
|
| 733 |
+
.image-bg:hover {
|
| 734 |
+
opacity: 0.8;
|
| 735 |
+
}
|
| 736 |
+
|
| 737 |
+
.whitespace-pre-line {
|
| 738 |
+
white-space: pre-line;
|
| 739 |
+
}
|
| 740 |
+
|
| 741 |
+
p {
|
| 742 |
+
overflow-wrap: anywhere;
|
| 743 |
+
}
|
| 744 |
+
|
| 745 |
+
.live-chat {
|
| 746 |
+
display: flex;
|
| 747 |
+
gap: 10px;
|
| 748 |
+
align-items: center;
|
| 749 |
+
position: absolute;
|
| 750 |
+
bottom: 20px;
|
| 751 |
+
right: 50px;
|
| 752 |
+
border-radius: 1234px;
|
| 753 |
+
padding: 12px 20px;
|
| 754 |
+
background-color: #28a745;
|
| 755 |
+
color: #ffffff;
|
| 756 |
+
cursor: pointer;
|
| 757 |
+
box-shadow: 0 12px 24px rgba(0, 0, 0, 0.15);
|
| 758 |
+
z-index: 999;
|
| 759 |
+
}
|
| 760 |
+
|
| 761 |
+
.live-chat:hover {
|
| 762 |
+
opacity: 0.8;
|
| 763 |
+
}
|
| 764 |
+
|
| 765 |
+
.live-chat-text {
|
| 766 |
+
display: flex;
|
| 767 |
+
align-items: center;
|
| 768 |
+
white-space: nowrap;
|
| 769 |
+
font-weight: 600;
|
| 770 |
+
}
|
| 771 |
+
|
| 772 |
+
.feedback {
|
| 773 |
+
display: flex;
|
| 774 |
+
flex-direction: column;
|
| 775 |
+
width: 25%;
|
| 776 |
+
height: calc(100vh - 73px);
|
| 777 |
+
}
|
| 778 |
+
.feedback .header-feedback {
|
| 779 |
+
height: 51px;
|
| 780 |
+
display: flex;
|
| 781 |
+
justify-content: space-between;
|
| 782 |
+
gap: 10px;
|
| 783 |
+
align-items: center;
|
| 784 |
+
padding-left: 16px;
|
| 785 |
+
padding-right: 8px;
|
| 786 |
+
border-bottom: 1px solid #e2e8f0;
|
| 787 |
+
}
|
| 788 |
+
.feedback .header-feedback .close-feedback {
|
| 789 |
+
cursor: pointer;
|
| 790 |
+
}
|
| 791 |
+
|
| 792 |
+
.feedback .content-feedback {
|
| 793 |
+
flex: 1 1;
|
| 794 |
+
padding: 18px 12px;
|
| 795 |
+
display: flex;
|
| 796 |
+
flex-direction: column;
|
| 797 |
+
gap: 24px;
|
| 798 |
+
overflow: auto;
|
| 799 |
+
}
|
| 800 |
+
|
| 801 |
+
.content-feedback .question {
|
| 802 |
+
display: flex;
|
| 803 |
+
flex-direction: column;
|
| 804 |
+
gap: 12px;
|
| 805 |
+
}
|
| 806 |
+
|
| 807 |
+
.content-feedback .question .textarea .textarea-label,
|
| 808 |
+
.content-feedback .question .q-title {
|
| 809 |
+
font-size: 16px;
|
| 810 |
+
font-weight: 600;
|
| 811 |
+
color: #454545;
|
| 812 |
+
line-height: 18px;
|
| 813 |
+
}
|
| 814 |
+
|
| 815 |
+
.content-feedback .question .options {
|
| 816 |
+
display: flex;
|
| 817 |
+
flex-direction: column;
|
| 818 |
+
gap: 12px;
|
| 819 |
+
}
|
| 820 |
+
|
| 821 |
+
.content-feedback .question .options .option {
|
| 822 |
+
width: fit-content;
|
| 823 |
+
}
|
| 824 |
+
|
| 825 |
+
.content-feedback .question .options-stars {
|
| 826 |
+
display: flex;
|
| 827 |
+
gap: 12px;
|
| 828 |
+
}
|
| 829 |
+
|
| 830 |
+
.content-feedback .question .options .option .opt .option-text {
|
| 831 |
+
line-height: 20px;
|
| 832 |
+
}
|
| 833 |
+
|
| 834 |
+
.content-feedback .question .options .option .opt {
|
| 835 |
+
display: flex;
|
| 836 |
+
gap: 8px;
|
| 837 |
+
cursor: pointer;
|
| 838 |
+
}
|
| 839 |
+
|
| 840 |
+
.star {
|
| 841 |
+
width: 32px;
|
| 842 |
+
height: 32px;
|
| 843 |
+
background: #e9ecef;
|
| 844 |
+
cursor: pointer;
|
| 845 |
+
clip-path: polygon(
|
| 846 |
+
50% 0%,
|
| 847 |
+
61% 35%,
|
| 848 |
+
98% 35%,
|
| 849 |
+
68% 57%,
|
| 850 |
+
79% 91%,
|
| 851 |
+
50% 70%,
|
| 852 |
+
21% 91%,
|
| 853 |
+
32% 57%,
|
| 854 |
+
2% 35%,
|
| 855 |
+
39% 35%
|
| 856 |
+
);
|
| 857 |
+
}
|
| 858 |
+
.options-stars .star.active {
|
| 859 |
+
background: gold;
|
| 860 |
+
}
|
| 861 |
+
|
| 862 |
+
input[type='radio'] {
|
| 863 |
+
display: none;
|
| 864 |
+
}
|
| 865 |
+
.radio {
|
| 866 |
+
flex-shrink: 0;
|
| 867 |
+
width: 20px;
|
| 868 |
+
height: 20px;
|
| 869 |
+
border: 2px solid #ced4da;
|
| 870 |
+
border-radius: 50%;
|
| 871 |
+
position: relative;
|
| 872 |
+
cursor: cusror;
|
| 873 |
+
}
|
| 874 |
+
input[type='radio']:checked + .radio {
|
| 875 |
+
border-color: #fd7e14;
|
| 876 |
+
}
|
| 877 |
+
input[type='radio']:checked + .radio::after {
|
| 878 |
+
content: '';
|
| 879 |
+
position: absolute;
|
| 880 |
+
top: 50%;
|
| 881 |
+
left: 50%;
|
| 882 |
+
transform: translate(-50%, -50%);
|
| 883 |
+
width: 8px;
|
| 884 |
+
height: 8px;
|
| 885 |
+
background-color: #fd7e14;
|
| 886 |
+
border-radius: 50%;
|
| 887 |
+
}
|
| 888 |
+
|
| 889 |
+
textarea {
|
| 890 |
+
border-radius: 16px;
|
| 891 |
+
padding: 16px;
|
| 892 |
+
border: 1px solid #cbd5e1;
|
| 893 |
+
}
|
| 894 |
+
textarea:focus {
|
| 895 |
+
outline: none;
|
| 896 |
+
border-color: #fd7e14;
|
| 897 |
+
}
|
| 898 |
+
|
| 899 |
+
.feedback .footer-feedback {
|
| 900 |
+
display: flex;
|
| 901 |
+
justify-content: center;
|
| 902 |
+
align-items: center;
|
| 903 |
+
width: 100%;
|
| 904 |
+
background-color: #ffffff;
|
| 905 |
+
border-top: 1px solid #e2e8f0;
|
| 906 |
+
padding: 20px 24px;
|
| 907 |
+
}
|
| 908 |
+
|
| 909 |
+
.feedback .footer-feedback .btn-feedback {
|
| 910 |
+
background-color: #28a745;
|
| 911 |
+
color: #ffffff;
|
| 912 |
+
border: 1px solid transparent;
|
| 913 |
+
}
|
| 914 |
+
|
| 915 |
+
.feedback .footer-feedback .btn-feedback:hover {
|
| 916 |
+
opacity: 0.8;
|
| 917 |
+
}
|
| 918 |
+
|
| 919 |
+
.feedback .header-feedback .content-header-feedback {
|
| 920 |
+
font-size: 18px;
|
| 921 |
+
font-weight: 600;
|
| 922 |
+
color: #454545;
|
| 923 |
+
}
|
| 924 |
+
|
| 925 |
+
.shrink-0 {
|
| 926 |
+
flex-shrink: 0;
|
| 927 |
+
}
|
| 928 |
+
|
| 929 |
+
.notification-content-feedback {
|
| 930 |
+
display: flex;
|
| 931 |
+
flex-direction: column;
|
| 932 |
+
align-items: center;
|
| 933 |
+
gap: 16px;
|
| 934 |
+
padding: 24px 16px;
|
| 935 |
+
}
|
| 936 |
+
|
| 937 |
+
.notification-content-feedback .notification-message {
|
| 938 |
+
font-size: 20px;
|
| 939 |
+
line-height: 20px;
|
| 940 |
+
font-weight: 600;
|
| 941 |
+
color: #454545;
|
| 942 |
+
}
|
| 943 |
+
|
| 944 |
+
.flex {
|
| 945 |
+
display: flex;
|
| 946 |
+
}
|
| 947 |
+
|
| 948 |
+
.flex-col {
|
| 949 |
+
flex-direction: column;
|
| 950 |
+
}
|
| 951 |
+
|
| 952 |
+
.gap-6 {
|
| 953 |
+
gap: 1.5rem;
|
| 954 |
+
}
|