diff --git a/.env.example b/.env.example
new file mode 100644
index 0000000..4dc673e
--- /dev/null
+++ b/.env.example
@@ -0,0 +1,3 @@
+SERVICE_USERNAME=
+SERVICE_PASSWORD=
+PORT=3000
diff --git a/.gitignore b/.gitignore
index b24d71e..713d500 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,50 +1,2 @@
-# These are some examples of commonly ignored file patterns.
-# You should customize this list as applicable to your project.
-# Learn more about .gitignore:
-# https://www.atlassian.com/git/tutorials/saving-changes/gitignore
-
-# Node artifact files
node_modules/
-dist/
-
-# Compiled Java class files
-*.class
-
-# Compiled Python bytecode
-*.py[cod]
-
-# Log files
-*.log
-
-# Package files
-*.jar
-
-# Maven
-target/
-dist/
-
-# JetBrains IDE
-.idea/
-
-# Unit test reports
-TEST*.xml
-
-# Generated by MacOS
-.DS_Store
-
-# Generated by Windows
-Thumbs.db
-
-# Applications
-*.app
-*.exe
-*.war
-
-# Large media files
-*.mp4
-*.tiff
-*.avi
-*.flv
-*.mov
-*.wmv
-
+.env
diff --git a/auth.js b/auth.js
index a55e0ce..1912755 100644
--- a/auth.js
+++ b/auth.js
@@ -1,163 +1,95 @@
-// Authentication Configurations
const OLIVER_AUTH_CONFIG = {
- apiBaseUrl: '/api', // Use local proxy to avoid CORS issues
- authDomain: 'local',
- authUsername: 'portal@oliver.agency',
- authPassword: 'Sp1d3r26!',
+ apiBaseUrl: '/api',
clientId: '7',
- userId: '9'
};
const TMM_AUTH_CONFIG = {
- apiBaseUrl: '/api', // Use local proxy to avoid CORS issues
- authDomain: 'local',
- authUsername: 'portal@oliver.agency',
- authPassword: 'Sp1d3r26!',
+ apiBaseUrl: '/api',
clientId: '7',
- userId: '9'
};
-// Handle login with external session
async function handleLogin(authConfig, loginType) {
- const username = document.getElementById('username').value;
- const password = document.getElementById('password').value;
+ const username = document.getElementById('username').value.trim();
const errorMessage = document.getElementById('errorMessage');
-
- // Get the correct button elements based on login type
const isOliver = loginType === 'oliver';
const loginButton = document.getElementById(isOliver ? 'oliverLoginButton' : 'tmmLoginButton');
- const buttonText = document.getElementById(isOliver ? 'oliverButtonText' : 'tmmButtonText');
- const spinner = document.getElementById(isOliver ? 'oliverSpinner' : 'tmmSpinner');
+ const buttonText = document.getElementById(isOliver ? 'oliverButtonText' : 'tmmButtonText');
+ const spinner = document.getElementById(isOliver ? 'oliverSpinner' : 'tmmSpinner');
- // Hide any previous error messages
errorMessage.style.display = 'none';
- // Disable button and show spinner
+ if (!username) {
+ errorMessage.textContent = 'Please enter your username.';
+ errorMessage.style.display = 'block';
+ return;
+ }
+
loginButton.disabled = true;
buttonText.textContent = 'Signing in...';
spinner.style.display = 'inline-block';
try {
- // First, call user.info to get the real userId for the entered username
+ // Step 1: Resolve username ā userId
const userInfoParams = new URLSearchParams({
- command: 'user.info',
+ command: 'user.info',
authDomain: 'local',
- authUsername: 'portal@oliver.agency',
- authPassword: 'Sp1d3r26!',
- clientId: '6',
- username: username,
- domain: 'local'
+ clientId: '6',
+ username,
+ domain: 'local',
});
- const userInfoUrl = `${authConfig.apiBaseUrl}?${userInfoParams.toString()}`;
-
- const userInfoResponse = await fetch(userInfoUrl, {
- method: 'GET',
- headers: {
- 'Accept': 'text/xml'
- }
+ const userInfoRes = await fetch(`${authConfig.apiBaseUrl}?${userInfoParams}`, {
+ headers: { Accept: 'text/xml' },
});
+ const userInfoDoc = new DOMParser().parseFromString(await userInfoRes.text(), 'text/xml');
- const userInfoXml = await userInfoResponse.text();
- console.log('User Info API Response:', userInfoXml);
-
- const userInfoParser = new DOMParser();
- const userInfoDoc = userInfoParser.parseFromString(userInfoXml, 'text/xml');
-
- // Check for errors
const userInfoError = userInfoDoc.querySelector('error');
if (userInfoError) {
- const errorMsg = userInfoError.querySelector('message')?.textContent || 'User not found';
- throw new Error(errorMsg);
+ throw new Error(userInfoError.querySelector('message')?.textContent || 'User not found');
}
- // Extract userId from user.info response
const userIdNode = userInfoDoc.querySelector('user > id') || userInfoDoc.querySelector('id');
- if (!userIdNode) {
- throw new Error('User not found. Please check your username.');
- }
+ if (!userIdNode) throw new Error('User not found. Please check your username.');
const userId = userIdNode.textContent;
- console.log('User ID from user.info:', userId);
- // Build the API URL for user.session.extern.add
- const params = new URLSearchParams({
- command: 'user.session.extern.add',
- authDomain: authConfig.authDomain,
- authUsername: authConfig.authUsername,
- authPassword: authConfig.authPassword,
- clientId: authConfig.clientId,
- username: username,
- userId: userId
+ // Step 2: Create external session for the user
+ const sessionParams = new URLSearchParams({
+ command: 'user.session.extern.add',
+ authDomain: 'local',
+ clientId: authConfig.clientId,
+ username,
+ userId,
});
- const apiUrl = `${authConfig.apiBaseUrl}?${params.toString()}`;
-
- // Make the API call
- const response = await fetch(apiUrl, {
- method: 'GET',
- headers: {
- 'Accept': 'text/xml'
- }
+ const sessionRes = await fetch(`${authConfig.apiBaseUrl}?${sessionParams}`, {
+ headers: { Accept: 'text/xml' },
});
+ const sessionDoc = new DOMParser().parseFromString(await sessionRes.text(), 'text/xml');
- const xmlText = await response.text();
- console.log('API Response:', xmlText);
-
- // Parse XML response
- const parser = new DOMParser();
- const xmlDoc = parser.parseFromString(xmlText, 'text/xml');
-
- // Check for errors in XML
- const errorNode = xmlDoc.querySelector('error');
- if (errorNode) {
- const errorMsg = errorNode.querySelector('message')?.textContent || 'Login failed';
- throw new Error(errorMsg);
+ const sessionError = sessionDoc.querySelector('error');
+ if (sessionError) {
+ throw new Error(sessionError.querySelector('message')?.textContent || 'Login failed');
}
- // Extract externSessionId from the response
- const externSessionIdNode = xmlDoc.querySelector('externSessionId');
- const externSessionId = externSessionIdNode?.textContent;
+ const externSessionId = sessionDoc.querySelector('externSessionId')?.textContent;
+ if (!externSessionId) throw new Error('No session received from server');
- if (!externSessionId) {
- throw new Error('No externSessionId received from server');
- }
+ sessionStorage.setItem('isAuthenticated', 'true');
+ sessionStorage.setItem('username', username);
+ sessionStorage.setItem('userId', userId);
+ sessionStorage.setItem('externSessionId', externSessionId);
+ sessionStorage.setItem('authConfig', JSON.stringify(authConfig));
- console.log('Extern Session ID:', externSessionId);
-
- // Check if login was successful
- if (response.ok && !errorNode) {
- // Store session data in sessionStorage
- sessionStorage.setItem('isAuthenticated', 'true');
- sessionStorage.setItem('username', username);
- sessionStorage.setItem('userId', userId);
- sessionStorage.setItem('externSessionId', externSessionId);
- sessionStorage.setItem('authConfig', JSON.stringify(authConfig));
-
- // Redirect to dashboard
- window.location.href = 'dashboard.html';
- } else {
- throw new Error('Login failed. Please check your credentials.');
- }
+ window.location.href = 'dashboard.html';
} catch (error) {
console.error('Login error:', error);
-
- // Show error message
errorMessage.textContent = error.message || 'An error occurred. Please try again.';
errorMessage.style.display = 'block';
-
- // Re-enable button
loginButton.disabled = false;
buttonText.textContent = isOliver ? 'Oliver Login' : '3M Login';
spinner.style.display = 'none';
}
}
-// Handle Oliver login button click
-document.getElementById('oliverLoginButton').addEventListener('click', function() {
- handleLogin(OLIVER_AUTH_CONFIG, 'oliver');
-});
-
-// Handle 3M login button click
-document.getElementById('tmmLoginButton').addEventListener('click', function() {
- handleLogin(TMM_AUTH_CONFIG, '3m');
-});
+document.getElementById('oliverLoginButton').addEventListener('click', () => handleLogin(OLIVER_AUTH_CONFIG, 'oliver'));
+document.getElementById('tmmLoginButton').addEventListener('click', () => handleLogin(TMM_AUTH_CONFIG, '3m'));
diff --git a/dashboard.html b/dashboard.html
index ee38351..e3b8c45 100644
--- a/dashboard.html
+++ b/dashboard.html
@@ -53,18 +53,6 @@
-
-
Loading jobs...
diff --git a/dashboard.js b/dashboard.js
index 72f252c..e52d01b 100644
--- a/dashboard.js
+++ b/dashboard.js
@@ -1,34 +1,31 @@
-// Global functions for button handlers
-function handleEdit(jobId) {
- console.log('Edit job:', jobId);
+function escapeHtml(str) {
+ return String(str)
+ .replace(/&/g, '&')
+ .replace(//g, '>')
+ .replace(/"/g, '"');
+}
- // Navigate to editor page in the same window
- window.location.href = `editor.html?jobId=${jobId}`;
+function handleEdit(jobId) {
+ window.location.href = `editor.html?jobId=${encodeURIComponent(jobId)}`;
}
async function handlePDF(jobId) {
- console.log('Generate PDF for job:', jobId);
-
- // Find the PDF button for this job and disable it
const jobCard = document.querySelector(`[data-job-id="${jobId}"]`);
- const pdfBtn = jobCard ? jobCard.querySelector('.btn-pdf') : null;
+ const pdfBtn = jobCard ? jobCard.querySelector('.btn-pdf') : null;
+
if (pdfBtn) {
pdfBtn.disabled = true;
pdfBtn.textContent = 'Downloading...';
}
- // Show popup immediately so user knows the button was clicked
showPdfPopup();
try {
- // Find the job
const job = allJobs.find(j => j.id === jobId);
- if (!job) {
- throw new Error('Job not found');
- }
+ if (!job) throw new Error('Job not found');
- // Get auth config from session
- const authConfigStr = sessionStorage.getItem('authConfig');
+ const authConfigStr = sessionStorage.getItem('authConfig');
const externSessionId = sessionStorage.getItem('externSessionId');
if (!authConfigStr || !externSessionId) {
@@ -39,45 +36,31 @@ async function handlePDF(jobId) {
const authConfig = JSON.parse(authConfigStr);
- // Build params for job.export.pdf using session-based auth
const params = new URLSearchParams({
- command: 'job.export.pdf',
+ command: 'job.export.pdf',
authDomain: 'local',
- sessionId: externSessionId,
- clientId: '7',
- id: jobId,
- result: 'file'
+ sessionId: externSessionId,
+ clientId: '7',
+ id: jobId,
+ result: 'file',
});
- // Fetch the PDF using GET
- const apiUrl = `${authConfig.apiBaseUrl}?${params.toString()}`;
- const response = await fetch(apiUrl, {
- method: 'GET'
- });
+ const response = await fetch(`${authConfig.apiBaseUrl}?${params}`);
+ if (!response.ok) throw new Error(`Failed to generate PDF (${response.status})`);
- if (!response.ok) {
- const errorText = await response.text();
- console.error('PDF export API error:', response.status, errorText);
- throw new Error(`Failed to generate PDF (${response.status})`);
- }
-
- const blob = await response.blob();
-
- // Create a temporary URL for the blob and trigger download
+ const blob = await response.blob();
const blobUrl = URL.createObjectURL(blob);
- const link = document.createElement('a');
- link.href = blobUrl;
+ const link = document.createElement('a');
+ link.href = blobUrl;
link.download = `${job.title || job.documentName || 'document'}_${jobId}.pdf`;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(blobUrl);
-
} catch (error) {
console.error('PDF export error:', error);
alert('Failed to export PDF. Please try again.');
} finally {
- // Re-enable the PDF button
if (pdfBtn) {
pdfBtn.disabled = false;
pdfBtn.textContent = 'PDF';
@@ -85,13 +68,10 @@ async function handlePDF(jobId) {
}
}
-// Show PDF download confirmation popup
function showPdfPopup() {
- // Create overlay
const overlay = document.createElement('div');
overlay.className = 'pdf-popup-overlay';
- // Create popup
const popup = document.createElement('div');
popup.className = 'pdf-popup';
popup.innerHTML = `
@@ -101,339 +81,188 @@ function showPdfPopup() {
overlay.appendChild(popup);
document.body.appendChild(overlay);
-
- // Show with active class for animation
requestAnimationFrame(() => overlay.classList.add('active'));
- // Close on OK button click
- popup.querySelector('.pdf-popup-ok-btn').addEventListener('click', () => {
+ const close = () => {
overlay.classList.remove('active');
setTimeout(() => overlay.remove(), 200);
- });
-
- // Close on overlay click
- overlay.addEventListener('click', (e) => {
- if (e.target === overlay) {
- overlay.classList.remove('active');
- setTimeout(() => overlay.remove(), 200);
- }
- });
+ };
+ popup.querySelector('.pdf-popup-ok-btn').addEventListener('click', close);
+ overlay.addEventListener('click', e => { if (e.target === overlay) close(); });
}
-// Check authentication on page load
-document.addEventListener('DOMContentLoaded', function() {
+document.addEventListener('DOMContentLoaded', () => {
checkAuthentication();
loadJobsFromAPI();
setupEventListeners();
});
-// Check if user is authenticated
function checkAuthentication() {
- const isAuthenticated = sessionStorage.getItem('isAuthenticated');
-
- if (!isAuthenticated || isAuthenticated !== 'true') {
- // Redirect to login if not authenticated
+ if (sessionStorage.getItem('isAuthenticated') !== 'true') {
window.location.href = 'login.html';
}
}
-// Setup event listeners
function setupEventListeners() {
- // Refresh button
document.getElementById('refreshBtn').addEventListener('click', loadJobsFromAPI);
-
- // Sign out button
document.getElementById('signOutBtn').addEventListener('click', handleSignOut);
-
- // Category filter
- document.getElementById('categorySelect').addEventListener('change', filterJobs);
}
-// Handle sign out
async function handleSignOut() {
const signOutBtn = document.getElementById('signOutBtn');
signOutBtn.disabled = true;
signOutBtn.textContent = 'Signing out...';
try {
- // Get session data
const externSessionId = sessionStorage.getItem('externSessionId');
- const authConfigStr = sessionStorage.getItem('authConfig');
+ const authConfigStr = sessionStorage.getItem('authConfig');
- if (!externSessionId || !authConfigStr) {
- throw new Error('Session data not found');
+ if (externSessionId && authConfigStr) {
+ const authConfig = JSON.parse(authConfigStr);
+ const params = new URLSearchParams({
+ command: 'user.session.extern.remove',
+ authDomain: 'local',
+ clientId: '7',
+ externSessionId,
+ });
+ await fetch(`${authConfig.apiBaseUrl}?${params}`, { headers: { Accept: 'text/xml' } });
}
-
- const authConfig = JSON.parse(authConfigStr);
-
- // Build the API URL for sign out using credential-based auth
- const params = new URLSearchParams({
- command: 'user.session.extern.remove',
- authDomain: 'local',
- authUsername: 'portal@oliver.agency',
- authPassword: 'Sp1d3r26!',
- clientId: '7',
- externSessionId: externSessionId
- });
-
- const apiUrl = `${authConfig.apiBaseUrl}?${params.toString()}`;
-
- // Make the API call
- const response = await fetch(apiUrl, {
- method: 'GET',
- headers: {
- 'Accept': 'text/xml'
- }
- });
-
- // Clear session data regardless of API response
- sessionStorage.clear();
-
- // Redirect to login
- window.location.href = 'login.html';
} catch (error) {
console.error('Sign out error:', error);
-
- // Clear session and redirect anyway
+ } finally {
sessionStorage.clear();
window.location.href = 'login.html';
}
}
-// Store for filtered jobs
-let allJobs = [];
+let allJobs = [];
let filteredJobs = [];
-// Load jobs from API
async function loadJobsFromAPI() {
const loadingMessage = document.getElementById('loadingMessage');
- const errorMessage = document.getElementById('errorMessage');
- const jobsGrid = document.getElementById('jobsGrid');
- const emptyState = document.getElementById('emptyState');
+ const errorMessage = document.getElementById('errorMessage');
+ const jobsGrid = document.getElementById('jobsGrid');
+ const emptyState = document.getElementById('emptyState');
- // Show loading
loadingMessage.style.display = 'block';
- errorMessage.style.display = 'none';
- emptyState.style.display = 'none';
- jobsGrid.innerHTML = '';
+ errorMessage.style.display = 'none';
+ emptyState.style.display = 'none';
+ jobsGrid.innerHTML = '';
try {
- // Get auth config and username from session
- const authConfigStr = sessionStorage.getItem('authConfig');
- const username = sessionStorage.getItem('username');
+ const authConfigStr = sessionStorage.getItem('authConfig');
+ const userId = sessionStorage.getItem('userId');
+ const externSessionId = sessionStorage.getItem('externSessionId');
- if (!authConfigStr || !username) {
- throw new Error('Session data not found');
+ if (!authConfigStr || !userId || !externSessionId) {
+ sessionStorage.clear();
+ window.location.href = 'login.html';
+ return;
}
const authConfig = JSON.parse(authConfigStr);
- const externSessionId = sessionStorage.getItem('externSessionId');
- if (!externSessionId) {
- throw new Error('Session expired');
- }
-
- // First, call user.info to get the userId for the logged-in user
- const userInfoParams = new URLSearchParams({
- command: 'user.info',
- authDomain: 'local',
- authUsername: 'portal@oliver.agency',
- authPassword: 'Sp1d3r26!',
- clientId: '6',
- username: username,
- domain: 'local'
- });
-
- const userInfoUrl = `${authConfig.apiBaseUrl}?${userInfoParams.toString()}`;
-
- const userInfoResponse = await fetch(userInfoUrl, {
- method: 'GET',
- headers: {
- 'Accept': 'text/xml'
- }
- });
-
- const userInfoXml = await userInfoResponse.text();
- console.log('User Info API Response:', userInfoXml);
-
- const userInfoParser = new DOMParser();
- const userInfoDoc = userInfoParser.parseFromString(userInfoXml, 'text/xml');
-
- // Check for errors
- const userInfoError = userInfoDoc.querySelector('error');
- if (userInfoError) {
- const errorMsg = userInfoError.querySelector('message')?.textContent || 'Failed to get user info';
- throw new Error(errorMsg);
- }
-
- // Extract userId from user.info response
- const userIdNode = userInfoDoc.querySelector('user > id') || userInfoDoc.querySelector('id');
- if (!userIdNode) {
- throw new Error('No userId returned from user.info');
- }
- const userId = userIdNode.textContent;
- console.log('User ID from user.info:', userId);
-
- // Build API URL for job.list using credential-based auth
const params = new URLSearchParams({
- command: 'job.list',
- authDomain: 'local',
- authUsername: 'portal@oliver.agency',
- authPassword: 'Sp1d3r26!',
- clientId: '7',
- userId: userId,
- includeDocumentInfos: '1',
- includeDocumentPreviews: '1',
+ command: 'job.list',
+ authDomain: 'local',
+ clientId: '7',
+ userId,
+ includeDocumentInfos: '1',
+ includeDocumentPreviews: '1',
includeDocumentWorkflows: '1',
- includeDocumentMetadata: '1'
+ includeDocumentMetadata: '1',
});
-
- // Add status filters
params.append('status[0]', 'STARTED');
params.append('status[1]', 'RUNNING');
- const apiUrl = `${authConfig.apiBaseUrl}?${params.toString()}`;
-
- // Fetch jobs from API
- const response = await fetch(apiUrl, {
- method: 'GET',
- headers: {
- 'Accept': 'text/xml'
- }
+ const response = await fetch(`${authConfig.apiBaseUrl}?${params}`, {
+ headers: { Accept: 'text/xml' },
});
+ const xmlDoc = new DOMParser().parseFromString(await response.text(), 'text/xml');
- const xmlText = await response.text();
- console.log('Jobs API Response:', xmlText);
-
- // Parse XML response
- const parser = new DOMParser();
- const xmlDoc = parser.parseFromString(xmlText, 'text/xml');
-
- // Check for errors
const errorNode = xmlDoc.querySelector('error');
if (errorNode) {
- const errorMsg = errorNode.querySelector('message')?.textContent || 'Failed to load jobs';
- throw new Error(errorMsg);
+ throw new Error(errorNode.querySelector('message')?.textContent || 'Failed to load jobs');
}
- // Parse jobs from XML
- const jobNodes = xmlDoc.querySelectorAll('job');
- allJobs = Array.from(jobNodes).map(jobNode => parseJobFromXML(jobNode));
+ allJobs = Array.from(xmlDoc.querySelectorAll('job')).map(parseJobFromXML);
filteredJobs = allJobs;
- // Hide loading
loadingMessage.style.display = 'none';
- // Display jobs
if (allJobs.length === 0) {
emptyState.style.display = 'block';
} else {
- loadJobsData();
+ renderJobs();
}
-
} catch (error) {
console.error('Failed to load jobs:', error);
loadingMessage.style.display = 'none';
- errorMessage.textContent = error.message || 'Failed to load jobs. Please try again.';
- errorMessage.style.display = 'block';
+ errorMessage.textContent = error.message || 'Failed to load jobs. Please try again.';
+ errorMessage.style.display = 'block';
}
}
-// Parse job data from XML node
function parseJobFromXML(jobNode) {
- const getNodeText = (selector) => {
- const node = jobNode.querySelector(selector);
- return node ? node.textContent : '';
- };
+ const get = sel => jobNode.querySelector(sel)?.textContent || '';
- const id = getNodeText('id');
- const jobName = getNodeText('name');
- const status = getNodeText('status');
-
- // Get document info
- const documentId = getNodeText('document > id');
- const documentVersion = getNodeText('document > version');
- const documentName = getNodeText('document > name');
- const pages = getNodeText('document > pages') || 'N/A';
-
- // Get translation language name
- const translationLanguageName = getNodeText('translationLanguage > name') || 'N/A';
-
- // Check if job is opened (locked)
- const isOpened = getNodeText('isOpened') === 'true';
-
- // Get progress data
- const totalItems = parseInt(getNodeText('totalItems')) || 0;
- const allItems = parseInt(getNodeText('allItems')) || 0;
- const doneItems = parseInt(getNodeText('doneItems')) || 0;
- const finishItems = parseInt(getNodeText('finishItems')) || 0;
-
- // Get last user and last edit info
- const lastUserName = getNodeText('lastuser > name') || 'N/A';
- const lastEdit = getNodeText('lastedit') || 'N/A';
-
- // Get preview from document > preview (base64 image)
const previewNode = jobNode.querySelector('document > preview');
- let thumbnail = null;
- if (previewNode && previewNode.textContent) {
- thumbnail = `data:image/jpeg;base64,${previewNode.textContent}`;
- }
+ const thumbnail = previewNode?.textContent
+ ? `data:image/jpeg;base64,${previewNode.textContent}`
+ : null;
+
+ const documentVersion = get('document > version');
+ const documentName = get('document > name');
+ const jobName = get('name');
return {
- id: id,
- documentId: documentId,
- title: documentVersion || documentName || jobName || 'Untitled',
+ id: get('id'),
+ documentId: get('document > id'),
+ title: documentVersion || documentName || jobName || 'Untitled',
documentName: documentVersion || documentName || 'N/A',
- language: translationLanguageName,
- pages: pages,
- status: status,
- isOpened: isOpened,
- totalItems: totalItems,
- doneItems: doneItems,
- finishItems: finishItems,
- lastUserName: lastUserName,
- lastEdit: lastEdit,
- thumbnail: thumbnail || 'https://via.placeholder.com/300x400/cccccc/666666?text=No+Preview'
+ language: get('translationLanguage > name') || 'N/A',
+ pages: get('document > pages') || 'N/A',
+ status: get('status'),
+ isOpened: get('isOpened') === 'true',
+ totalItems: parseInt(get('totalItems')) || 0,
+ doneItems: parseInt(get('doneItems')) || 0,
+ finishItems: parseInt(get('finishItems')) || 0,
+ lastUserName: get('lastuser > name') || 'N/A',
+ lastEdit: get('lastedit') || 'N/A',
+ thumbnail: thumbnail || 'https://via.placeholder.com/300x400/cccccc/666666?text=No+Preview',
};
}
-// Update overall progress bar
function updateOverallProgress() {
- // Calculate totals from all jobs (not filtered)
let totalFinishItems = 0;
- let totalDoneItems = 0;
- let totalAllItems = 0;
+ let totalDoneItems = 0;
+ let totalAllItems = 0;
allJobs.forEach(job => {
totalFinishItems += job.finishItems;
- totalDoneItems += job.doneItems;
- totalAllItems += job.totalItems;
+ totalDoneItems += job.doneItems;
+ totalAllItems += job.totalItems;
});
- // Calculate percentages
- const finishPercent = totalAllItems > 0 ? (totalFinishItems / totalAllItems * 100) : 0;
- const donePercent = totalAllItems > 0 ? (totalDoneItems / totalAllItems * 100) : 0;
- const remainingPercent = 100 - finishPercent - donePercent;
+ const finishPercent = totalAllItems > 0 ? (totalFinishItems / totalAllItems * 100) : 0;
+ const donePercent = totalAllItems > 0 ? (totalDoneItems / totalAllItems * 100) : 0;
+ const completedItems = totalFinishItems + totalDoneItems;
+ const overallPercent = totalAllItems > 0 ? Math.round((completedItems / totalAllItems) * 100) : 0;
- const completedItems = totalFinishItems + totalDoneItems;
- const overallPercent = totalAllItems > 0 ? Math.round((completedItems / totalAllItems) * 100) : 0;
-
- // Update DOM elements
- document.getElementById('overallProgressText').innerHTML = `
${completedItems} of ${totalAllItems} elements done (${overallPercent}%)`;
- document.getElementById('overallDone').style.width = `${donePercent}%`;
- document.getElementById('overallFinish').style.width = `${finishPercent}%`;
- document.getElementById('overallRemaining').style.width = `${remainingPercent}%`;
- document.getElementById('overallDoneText').textContent = `Done: ${totalDoneItems} (${Math.round(donePercent)}%)`;
+ document.getElementById('overallProgressText').innerHTML =
+ `
${completedItems} of ${totalAllItems} elements done (${overallPercent}%)`;
+ document.getElementById('overallDone').style.width = `${donePercent}%`;
+ document.getElementById('overallFinish').style.width = `${finishPercent}%`;
+ document.getElementById('overallRemaining').style.width = `${100 - finishPercent - donePercent}%`;
+ document.getElementById('overallDoneText').textContent = `Done: ${totalDoneItems} (${Math.round(donePercent)}%)`;
document.getElementById('overallFinishText').textContent = `Finished: ${totalFinishItems} (${Math.round(finishPercent)}%)`;
- document.getElementById('overallTotalText').textContent = `Total: ${totalAllItems}`;
+ document.getElementById('overallTotalText').textContent = `Total: ${totalAllItems}`;
}
-// Load jobs data
-function loadJobsData() {
- const jobsGrid = document.getElementById('jobsGrid');
+function renderJobs() {
+ const jobsGrid = document.getElementById('jobsGrid');
const emptyState = document.getElementById('emptyState');
- // Clear grid
jobsGrid.innerHTML = '';
if (filteredJobs.length === 0) {
@@ -442,69 +271,59 @@ function loadJobsData() {
}
emptyState.style.display = 'none';
-
- // Create job cards
- filteredJobs.forEach(job => {
- const jobCard = createJobCard(job);
- jobsGrid.appendChild(jobCard);
- });
-
- // Update overall progress bar
+ filteredJobs.forEach(job => jobsGrid.appendChild(createJobCard(job)));
updateOverallProgress();
}
-// Create a job card element
function createJobCard(job) {
const card = document.createElement('div');
card.className = 'job-card';
card.setAttribute('data-job-id', job.id);
- // Determine status badge class based on API status
let statusClass = 'status-ready';
const statusLower = job.status.toLowerCase();
- if (statusLower === 'running') {
- statusClass = 'status-progress';
- } else if (statusLower === 'started') {
- statusClass = 'status-ready';
- } else if (statusLower === 'stopped') {
- statusClass = 'status-progress';
- } else if (statusLower === 'finish') {
- statusClass = 'status-ready';
- }
+ if (statusLower === 'running') statusClass = 'status-progress';
+ else if (statusLower === 'finish') statusClass = 'status-done';
const lockedClass = job.isOpened ? ' job-locked' : '';
const disabledAttr = job.isOpened ? ' disabled' : '';
- const lockIcon = job.isOpened ? '
š
' : '';
+ const lockIcon = job.isOpened ? '
š
' : '';
- // Calculate progress percentages
- const finishPercent = job.totalItems > 0 ? (job.finishItems / job.totalItems * 100) : 0;
- const donePercent = job.totalItems > 0 ? (job.doneItems / job.totalItems * 100) : 0;
- const remainingPercent = 100 - finishPercent - donePercent;
-
- // Calculate overall completion
+ const finishPercent = job.totalItems > 0 ? (job.finishItems / job.totalItems * 100) : 0;
+ const donePercent = job.totalItems > 0 ? (job.doneItems / job.totalItems * 100) : 0;
const completedItems = job.finishItems + job.doneItems;
const overallPercent = job.totalItems > 0 ? Math.round((completedItems / job.totalItems) * 100) : 0;
+ const title = escapeHtml(job.title);
+ const documentName = escapeHtml(job.documentName);
+ const documentId = escapeHtml(job.documentId);
+ const language = escapeHtml(job.language);
+ const pages = escapeHtml(job.pages);
+ const lastUserName = escapeHtml(job.lastUserName);
+ const lastEdit = escapeHtml(job.lastEdit);
+ const jobId = escapeHtml(job.id);
+ const thumbnail = escapeHtml(job.thumbnail);
+
card.innerHTML = `
-
+
-

+
-
${job.documentName}
-
Document ID: ${job.documentId}
-
Language: ${job.language}
-
Pages: ${job.pages}
-
Last User: ${job.lastUserName}
-
Last Edit: ${job.lastEdit}
+
${documentName}
+
Document ID: ${documentId}
+
Language: ${language}
+
Pages: ${pages}
+
Last User: ${lastUserName}
+
Last Edit: ${lastEdit}
${completedItems} of ${job.totalItems} elements done (${overallPercent}%)
@@ -523,31 +342,11 @@ function createJobCard(job) {
-
-
+
+
`;
return card;
}
-
-// Filter jobs by category
-function filterJobs() {
- const categorySelect = document.getElementById('categorySelect');
- const selectedCategory = categorySelect.value;
-
- if (selectedCategory === 'all') {
- filteredJobs = allJobs;
- } else {
- filteredJobs = allJobs.filter(job =>
- job.category.toLowerCase() === selectedCategory.toLowerCase()
- );
- }
-
- loadJobsData();
-}
diff --git a/login.html b/login.html
index ea9f122..9f876a5 100644
--- a/login.html
+++ b/login.html
@@ -28,17 +28,6 @@
>
-
-
-
-