diff --git a/assets/scripts/application.js b/assets/scripts/application.js index febf92e..ceac647 100644 --- a/assets/scripts/application.js +++ b/assets/scripts/application.js @@ -44,7 +44,6 @@ class Application { #chromeRuntimeOnMessage(){ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { - console.log(message) if (message.type !== "changeScreen") return; this.setScreen(message.data.screen); @@ -56,6 +55,7 @@ class Application { 'loading', // Information are being loaded 'started', // User is signed in and working 'not-started', // User is signed in but not working + 'not-logged', // User is not signed in 'disabled' // Extension is disabled ]; diff --git a/assets/scripts/background.js b/assets/scripts/background.js index ecf425f..d3be348 100644 --- a/assets/scripts/background.js +++ b/assets/scripts/background.js @@ -1,13 +1,17 @@ class BackgroundHelper { static passTimeInStringToMinutes(time) { - let [hour, minute] = time.split(':').map(v => parseInt(v)); - - if (isNaN(hour)) hour = (new Date).getHours(); - if (isNaN(minute)) minute = (new Date).getMinutes(); - - if (!minute) minute = 0; + try{ + let [hour, minute] = time.split(':').map(v => parseInt(v)); - return (minute + (hour * 60)); + if (isNaN(hour)) hour = (new Date).getHours(); + if (isNaN(minute)) minute = (new Date).getMinutes(); + + if (!minute) minute = 0; + + return (minute + (hour * 60)); + }catch(e){ + return 0; + } } } class Events { diff --git a/assets/scripts/config.js b/assets/scripts/config.js index 46f515a..75cb9e0 100644 --- a/assets/scripts/config.js +++ b/assets/scripts/config.js @@ -1,32 +1,4 @@ class DashboardHelper { - static decideScreen(){ - const loginScreen = document.querySelector('#login-screen'); - const configScreen = document.querySelector('#dashboard-screen'); - - const user = JSON.parse(localStorage.getItem('tradingWorksUser')); - - if(user){ - loginScreen.classList.add('hidden'); - configScreen.classList.remove('hidden'); - - DashboardLoader.loadTWInfo(); - new DashboardCharts(); - }else{ - loginScreen.classList.remove('hidden'); - configScreen.classList.add('hidden'); - } - } - - static parseJwt(token) { - const base64Url = token.split('.')[1]; - const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/'); - const jsonPayload = decodeURIComponent(window.atob(base64).split('').map(function(c) { - return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2); - }).join('')); - - return JSON.parse(jsonPayload); - } - static allowSendMessageToggle(){ const toggle = document.querySelector('#allow-send-messages-whatsapp'); toggle.addEventListener('change', e => { @@ -72,46 +44,6 @@ class DashboardHelper { } } class DashboardForms { - - static submitLogin(){ - const loginForm = document.querySelector('#login-form'); - const inputLogin = document.querySelector('#login'); - const inputPassword = document.querySelector('#password'); - - loginForm.addEventListener('submit', async event => { - event.preventDefault(); - - try{ - const request = await fetch("https://api-infra.tworh.com.br/api/auth/login", { - "method": "POST", - "headers": { - "accept": "application/json", - "content-type": "application/json;charset=UTF-8" - }, - "referrerPolicy": "no-referrer", - "body": `{\"username\":\"${inputLogin.value}\",\"password\":\"${inputPassword.value}\",\"rememberMe\":true}` - }); - - const response = await request.json(); - const token = response.listResponse[0].userToken; - const user = DashboardHelper.parseJwt(token); - - localStorage.setItem('tradingWorksUser', JSON.stringify({...user, userToken: token})); - - DashboardHelper.decideScreen(); - }catch(err){ - // console.log(err); - - Array.from(loginForm.querySelectorAll('.invalid-input')).forEach(alert => { - alert.classList.remove('hidden'); - setTimeout(() => { - alert.classList.add('hidden'); - }, 3000); - }); - } - }); - } - static submitSettings(){ const form = document.querySelector('#settings-form'); form.addEventListener('submit', (event) => { @@ -121,8 +53,10 @@ class DashboardForms { const formData = new FormData(event.target); const formattedFormData = Object.fromEntries(formData.entries()); - localStorage.setItem('tradingWorksSettings', JSON.stringify(formattedFormData)); - chrome.runtime.sendMessage({type: 'updateSettings', data: formattedFormData}); + const currentSettings = JSON.parse(localStorage.getItem('tradingWorksSettings')) || {}; + const settings = {...currentSettings, ...formattedFormData}; + localStorage.setItem('tradingWorksSettings', JSON.stringify(settings)); + chrome.runtime.sendMessage({type: 'updateSettings', data: settings}); const button = document.querySelector('button[type="submit"]'); button.innerHTML = 'Sucesso! 🎉'; @@ -135,6 +69,7 @@ class DashboardForms { setTimeout(() => { button.innerHTML = '💾 Salvar'; + window.location.reload(); }, 2000); }catch(e){ console.log(e); @@ -152,6 +87,8 @@ class DashboardForms { whats: '🤖 *TW+:* Olá!👋 Esse é um teste de notificação do Tradingworks+ no Whatsapp. Por aqui está tudo certo! 🚀' }); }); + + } static handleTimeInputs(){ @@ -196,23 +133,28 @@ class DashboardForms { class DashboardLoader { static loadTWInfo(){ const companyNameInput = document.getElementById("tw-info-companyname"); - const issInput = document.getElementById("tw-info-iss"); - const emailInput = document.getElementById("tw-info-email"); - const payRollRuleTitleInput = document.getElementById("tw-info-payrollruletitle"); - const companyIdInput = document.getElementById("tw-info-companyid"); - const employeeIdInput = document.getElementById("tw-info-employeeid"); - const userName = document.getElementById("tw-info-username"); - - const user = JSON.parse(localStorage.getItem('tradingWorksUser')); - if(!user) return; - - companyNameInput.value = user.companyname; - issInput.value = user.iss; - emailInput.value = user.email; - payRollRuleTitleInput.value = user.payrollruletitle; - companyIdInput.value = user.companyid; - employeeIdInput.value = user.employeeid; - userName.innerText = user.nickname; + const shortName = document.getElementById("tw-info-shortName"); + const userNameInput = document.getElementById("tw-info-username"); + const userAvatar = document.getElementById("tw-info-avatar"); + const lastUpdate = document.getElementById("last-update"); + + const settings = JSON.parse(localStorage.getItem('tradingWorksSettings')); + if(!settings) return; + + if(settings.userName){ + shortName.innerText = settings.userName.split(' ')[0]; + userNameInput.value = settings.userName; + } + + if(settings.employ) companyNameInput.value = settings.employ; + + if(settings.userImage) userAvatar.src = settings.userImage; + + if(settings.lastUpdate) { + lastUpdate.innerText = `Última sincronização em ${new Date(settings.lastUpdate).toLocaleString('pt-BR')}`; + }else{ + lastUpdate.innerText = 'Não sincronizado. Verifique sua sessão no TradingWorks.'; + } } static loadSettings(){ @@ -237,14 +179,44 @@ class DashboardLoader { } } } -class DashboardCharts { +class DashboardLoadData { constructor(){ - this.timeCardData = null; - this.bankHoursData = null; + this.settings = JSON.parse(localStorage.getItem('tradingWorksSettings')) || {}; this.currentGraphShowing = 0; + this.loadTable(); this.loadCharts(); - this.loadChartsIteration(); + } + + loadTable(){ + if(!this.settings) return; + if(!this.settings.currentMonthAppointments) return; + + const tableContainer = document.getElementById('table-appointments'); + tableContainer.classList.remove('hidden'); + + const currentMonthAppointments = this.settings.currentMonthAppointments; + if(!currentMonthAppointments) return; + + const table = document.querySelector('#table-appointments'); + const tbody = table.querySelector('tbody'); + + currentMonthAppointments.reverse().forEach(appointment => { + const tr = document.createElement('tr'); + + const jornada = appointment.jornada != '' ? appointment.jornada.split('  ').map(h => `${h.trim()}`).join('\n') : '-'; + + tr.innerHTML = `${appointment.data || '-'}`; // Data + tr.innerHTML += `${jornada}`; // Pontos + tr.innerHTML += `${appointment.horas_em_pausas || '-'}`; // Em Pausa + tr.innerHTML += `${appointment["horas_trab."] || '-'}`; // Trabalhando + tr.innerHTML += `${appointment.horas_totais || '-'}`; // Total + + tbody.appendChild(tr); + }); + + const currentMonth = document.querySelectorAll('span[data-content="current-month"]'); + currentMonth.forEach(span => span.innerText = '- ' + new Date().toLocaleString('pt-BR', { month: 'long', year: 'numeric' })); } loadChartsIteration(){ @@ -274,260 +246,289 @@ class DashboardCharts { }); } - async captureInformation(){ - const user = JSON.parse(localStorage.getItem('tradingWorksUser')); - - if(!user) return; - - const [rawBankTimeData, rawTimeCardData] = await Promise.all([fetch(`https://api-main.tworh.com.br/api/CompTimeEvent/list?EmployeeId=${user.employeeid}&CompanyId=${user.companyid}&ViewHistory=false`, { - "method": "GET", - "headers": { - "accept": "application/json, text/plain, */*", - "authorization": `Bearer ${user.token}`, - "content-type": "application/json", - } - }), fetch(`https://api-main.tworh.com.br/api/Timecard/list?EmployeeId=${user.employeeid}&CompanyId=${user.companyid}`, { - "method": "GET", - "headers": { - "accept": "application/json, text/plain, */*", - "authorization": `Bearer ${user.token}`, - "content-type": "application/json", - } - })]); - - const [bankTimeData, timeCardData] = await Promise.all([rawBankTimeData.json(), rawTimeCardData.json()]); - - this.timeCardData = timeCardData.listResponse; - this.bankHoursData = bankTimeData.listResponse; - } - async loadCharts(){ - await this.captureInformation(); + if(!this.settings) return; + if(!this.settings.currentMonthAppointments) return; - this.#chartBankOfHoursPerRelease(); - this.#chartDaysAbsentPerMonth(); - this.#chartHoursWorkedPerMonth(); - this.#chartOvertimePerMonth(); + const chartContainers = document.getElementById('charts-container'); + chartContainers.classList.remove('hidden'); - this.#removeLoading(); - } + this.#chartNumberOfPointsPerDay(); + this.#chartHoursOfBreakPerDay(); + this.#chartHoursWorkedPerDay(); + this.#chartTotalHoursWorked(); - #removeLoading(){ - const loading = document.querySelector('#dash-loading-screen'); - loading.classList.add('hidden'); + this.loadChartsIteration(); } #showChart(){ - const chartBankOfHoursPerRelease = document.querySelector("[data-chart='chart-bank-of-hours-per-release']"); - const chartDaysAbsentPerMonth = document.querySelector("[data-chart='chart-days-absent-per-month']"); - const chartHoursWorkedPerMonth = document.querySelector("[data-chart='chart-hours-worked-per-month']"); - const chartOvertimePerMonth = document.querySelector("[data-chart='chart-overtime-per-month']"); + const numberOfPointsPerDay = document.querySelector("[data-chart='number-of-points-per-day']"); + const hoursOfBreakPerDay = document.querySelector("[data-chart='hours-of-break-per-day']"); + const hoursWorkedPerDay = document.querySelector("[data-chart='hours-worked-per-day']"); + const totalHoursWorked = document.querySelector("[data-chart='total-hours-worked']"); - const charts = [chartBankOfHoursPerRelease, chartDaysAbsentPerMonth, chartHoursWorkedPerMonth, chartOvertimePerMonth]; + const charts = [numberOfPointsPerDay, hoursOfBreakPerDay, hoursWorkedPerDay, totalHoursWorked]; charts.forEach(chart => chart.classList.add('hidden')); charts[this.currentGraphShowing % charts.length].classList.remove('hidden'); } - #chartBankOfHoursPerRelease(){ - const chart = document.getElementById('chart-bank-of-hours-per-release'); - + #chartNumberOfPointsPerDay(){ + const containerHeight = document.querySelector('.chart-container').offsetHeight; + const chart = document.getElementById('number-of-points-per-day'); if(!chart) return; - const bankHoursData = this.bankHoursData.sort((a, b) => { - const dateA = new Date(a.baseDate); - const dateB = new Date(b.baseDate); - - return dateA - dateB; - }); - - const groupedData = {}; - - bankHoursData.forEach((entry, i) => { - const createdDate = new Date(entry.baseDate); - const monthYear = createdDate.toLocaleString('pt-BR', { month: 'numeric', year: 'numeric' }); - - if (!groupedData[monthYear]) groupedData[monthYear] = 0; - - groupedData[monthYear] += entry.compTime; - }); - - const labels = Object.keys(groupedData); - const data = Object.values(groupedData); + const labels = this.settings.currentMonthAppointments.map(item => item.data) + const data = this.settings.currentMonthAppointments.map(item => item.jornada.split('  ').filter(Boolean).length) new Chart(chart, { type: 'bar', data: { labels: labels, datasets: [{ - label: 'Saldo do Banco de Horas por mês/lançamento', + label: 'Pontos registrados por Dia', data: data, borderWidth: 1, backgroundColor: '#A7BF31' }] }, options: { - responsive:true, + responsive: true, maintainAspectRatio: false, scales: { y: { - beginAtZero: true + beginAtZero: true, + ticks: { + callback: function(value) { + return `${value} ponto${value != 1 ? 's' : ''}`; + } + } + } + }, + plugins: { + tooltip: { + callbacks: { + label: function(context) { + const value = context.raw; + + return `${value} ponto${value != 1 ? 's' : ''} ${value % 2 === 0 ? '✅' : '❌'}`; + } + } } } } }); - } - #chartDaysAbsentPerMonth(){ - const chart = document.getElementById('chart-days-absent-per-month'); + chart.style.height = `${containerHeight - 150}px` + chart.style.width = '100%'; + } + #chartHoursOfBreakPerDay(){ + const containerHeight = document.querySelector('.chart-container').offsetHeight; + const chart = document.getElementById('hours-of-break-per-day'); if(!chart) return; - const timeCardData = this.timeCardData.sort((a, b) => { - const dateA = new Date(a.toDate); - const dateB = new Date(b.toDate); - - return dateA - dateB; - }); - - const groupedData = {}; - - timeCardData.forEach((entry, i) => { - const createdDate = new Date(entry.toDate); - const monthYear = createdDate.toLocaleString('pt-BR', { month: 'numeric', year: 'numeric' }); - - if (!groupedData[monthYear]) groupedData[monthYear] = 0; - - groupedData[monthYear] += entry.absentDays; + const labels = this.settings.currentMonthAppointments.map(item => item.data) + const data = this.settings.currentMonthAppointments.map(item => { + const [hours, minutes] = item.horas_em_pausas.split(':'); + return parseFloat(hours) * 60 + parseFloat(minutes); }); - const labels = Object.keys(groupedData); - const data = Object.values(groupedData); - new Chart(chart, { type: 'bar', data: { labels: labels, datasets: [{ - label: 'Dias Ausentes por Mês', + label: 'Tempo de intervalo por Dia', data: data, borderWidth: 1, backgroundColor: '#A7BF31' }] }, options: { - responsive:true, + responsive: true, maintainAspectRatio: false, scales: { y: { - beginAtZero: true + beginAtZero: true, + ticks: { + callback: function(value) { + const hours = Math.floor(value / 60); + const minutes = value % 60; + + let text = ''; + if(hours > 0) text += `${hours}h `; + if(minutes > 0) text += `${minutes}min`; + + return text; + } + } + } + }, + plugins: { + tooltip: { + callbacks: { + label: function(context) { + const value = context.raw; + const hours = Math.floor(value / 60); + const minutes = value % 60; + + let text = ''; + if(hours > 0) text += `${hours}h `; + if(minutes > 0) text += `${minutes}min`; + + return text; + } + } } } } }); - } - #chartHoursWorkedPerMonth(){ - const chart = document.getElementById('chart-hours-worked-per-month'); - - const timeCardData = this.timeCardData.sort((a, b) => { - const dateA = new Date(a.toDate); - const dateB = new Date(b.toDate); - - return dateA - dateB; - }); + chart.style.height = `${containerHeight - 150}px` + chart.style.width = '100%'; + } - const groupedData = {}; + #chartHoursWorkedPerDay(){ + const containerHeight = document.querySelector('.chart-container').offsetHeight; + const chart = document.getElementById('hours-worked-per-day'); + if(!chart) return; - timeCardData.forEach((entry, i) => { - const createdDate = new Date(entry.toDate); - const monthYear = createdDate.toLocaleString('pt-BR', { month: 'numeric', year: 'numeric' }); - - if (!groupedData[monthYear]) groupedData[monthYear] = 0; - - groupedData[monthYear] += entry.workedHours; + const labels = this.settings.currentMonthAppointments.map(item => item.data) + const data = this.settings.currentMonthAppointments.map(item => { + const [hours, minutes] = item['horas_trab.'].split(':'); + return parseFloat(hours) * 60 + parseFloat(minutes); }); - const labels = Object.keys(groupedData); - const data = Object.values(groupedData); - new Chart(chart, { type: 'bar', data: { labels: labels, datasets: [{ - label: 'Horas trabalhadas por Mês', + label: 'Horas trabalhadas por Dia', data: data, borderWidth: 1, backgroundColor: '#A7BF31' }] }, options: { - responsive:true, + responsive: true, maintainAspectRatio: false, scales: { y: { - beginAtZero: true + beginAtZero: true, + ticks: { + callback: function(value) { + const hours = Math.floor(value / 60); + const minutes = value % 60; + + let text = ''; + if(hours > 0) text += `${hours}h `; + if(minutes > 0) text += `${minutes}min`; + + return text; + } + } + } + }, + plugins: { + tooltip: { + callbacks: { + label: function(context) { + const value = context.raw; + const hours = Math.floor(value / 60); + const minutes = value % 60; + + let text = ''; + if(hours > 0) text += `${hours}h `; + if(minutes > 0) text += `${minutes}min`; + + return text; + } + } } } } }); - } - - #chartOvertimePerMonth(){ - const chart = document.getElementById('chart-overtime-per-month'); - - const timeCardData = this.timeCardData.sort((a, b) => { - const dateA = new Date(a.toDate); - const dateB = new Date(b.toDate); - return dateA - dateB; - }); + chart.style.height = `${containerHeight - 150}px` + chart.style.width = '100%'; + } - const groupedData = {}; + #chartTotalHoursWorked(){ + const containerHeight = document.querySelector('.chart-container').offsetHeight; + const chart = document.getElementById('total-hours-worked'); + if(!chart) return; - timeCardData.forEach((entry, i) => { - const createdDate = new Date(entry.toDate); - const monthYear = createdDate.toLocaleString('pt-BR', { month: 'numeric', year: 'numeric' }); - - if (!groupedData[monthYear]) groupedData[monthYear] = 0; - - groupedData[monthYear] += entry.overtimeHours; + const labels = this.settings.currentMonthAppointments.map(item => item.data) + const data = this.settings.currentMonthAppointments.map(item => { + const [hours, minutes] = item.horas_totais.split(':'); + return parseFloat(hours) * 60 + parseFloat(minutes); }); - const labels = Object.keys(groupedData); - const data = Object.values(groupedData); - new Chart(chart, { type: 'bar', data: { labels: labels, datasets: [{ - label: 'Horas extras acumuladas por Mês', + label: 'Tempo de Jornada por Dia', data: data, borderWidth: 1, backgroundColor: '#A7BF31' }] }, options: { - responsive:true, + responsive: true, maintainAspectRatio: false, scales: { y: { - beginAtZero: true + beginAtZero: true, + ticks: { + callback: function(value) { + const hours = Math.floor(value / 60); + const minutes = value % 60; + + let text = ''; + if(hours > 0) text += `${hours}h `; + if(minutes > 0) text += `${minutes}min`; + + return text; + } + } + } + }, + plugins: { + tooltip: { + callbacks: { + label: function(context) { + const value = context.raw; + const hours = Math.floor(value / 60); + const minutes = value % 60; + + let text = ''; + if(hours > 0) text += `${hours}h `; + if(minutes > 0) text += `${minutes}min`; + + return text; + } + } } } } }); + + chart.style.height = `${containerHeight - 150}px` + chart.style.width = '100%'; } } window.addEventListener('DOMContentLoaded', () => { - DashboardHelper.decideScreen(); + DashboardLoader.loadTWInfo(); + new DashboardLoadData(); + DashboardHelper.allowSendMessageToggle(); - DashboardForms.submitLogin(); DashboardForms.submitSettings(); DashboardForms.submitSendMessage(); DashboardForms.handleTimeInputs(); diff --git a/assets/scripts/popup.js b/assets/scripts/popup.js index 72b537f..27560ea 100644 --- a/assets/scripts/popup.js +++ b/assets/scripts/popup.js @@ -1,6 +1,7 @@ class Popup { constructor(){ this.information = undefined; + this.settings = undefined; } updateContent(){ @@ -10,12 +11,14 @@ class Popup { if(!active) return this.#setScreen('disabled'); if(active) this.#setScreen('started'); - const data = JSON.parse(localStorage.getItem('tradingWorksPlusCalculatedData')); - if(!data) return this.#setScreen('not-started'); + this.settings = JSON.parse(localStorage.getItem('tradingWorksSettings')); + if(!this.settings) return this.#setScreen('not-started'); - this.#setScreen('started'); + this.information = JSON.parse(localStorage.getItem('tradingWorksPlusCalculatedData')); + if(!this.information) return this.#setScreen('not-started'); + if(!this.information?.points) return this.#setScreen('not-logged'); - this.information = data; + this.#setScreen('started'); this.#updateTableTimes(); this.#updateTableTotals(); @@ -27,6 +30,7 @@ class Popup { #updateTableTimes() { if (!this.information) return; + if(!this.information.points) return; const tableBodyTimes = document.getElementById('table-body-times'); tableBodyTimes.innerHTML = this.information.points.map((point, index) => { @@ -36,8 +40,8 @@ class Popup { const interval = point.interval ? PopupHelper.formatBalance(point.interval) : undefined; return (`
-
${start}
-
${end}
+
${start}
+
${end}
${duration}
` + (interval ? `
@@ -114,6 +118,9 @@ class Popup { const date = new Date(); const dateElement = document.getElementById('current-date'); dateElement.innerHTML = PopupHelper.formatDate(date, 'dd de MM, hh:min:ss'); + + const lastUpdate = document.getElementById('last-update'); + lastUpdate.innerHTML = PopupHelper.formatDate(this.settings.lastUpdate, 'Última sincronização em dd de MM às hh:min:ss'); } #setScreen(screen){ diff --git a/assets/styles/config.css b/assets/styles/config.css index e8ac29e..6daeeaf 100644 --- a/assets/styles/config.css +++ b/assets/styles/config.css @@ -35,6 +35,7 @@ main{ } .container{ + position: relative; background-color: var(--atlantis-50); padding: 1rem 2rem; border-radius: 1rem; @@ -104,6 +105,7 @@ main{ width: 100%; height: 100%; display: flex; + justify-content:center; flex-wrap: wrap; gap: .5rem; } @@ -176,10 +178,11 @@ main{ padding-bottom: .25rem; } -.disabled{ +input[disabled], .disabled{ opacity: .7; + cursor: not-allowed; } -.disabled *{ +input[disabled], .disabled *{ pointer-events: none; } @@ -221,6 +224,16 @@ main{ .hidden{ display: none !important; } .hidden-opacity{ opacity: 0; } +.tw-info-avatar{ + position: absolute; + border-radius: 50%; + top: 12px; + right: 28px; + width: 30px; + height: 30px; + border: 2px solid var(--atlantis-200); +} + footer{ display: flex; align-items: center; @@ -229,7 +242,7 @@ footer{ width: 100%; font-size: .85rem; color: var(--atlantis-300); - margin-bottom: 1rem; + margin: .75rem 0 .5rem 0; } footer img { @@ -243,13 +256,10 @@ footer a{ margin-left: .25rem; } -.chart-container{ - max-width: 100%; - min-height: 275px; -} - .charts-container{ + width: 100%; position: relative; + height: calc(100vh / 3); } .charts-container .chart-arrow-left{ @@ -265,7 +275,7 @@ footer a{ justify-content: center; cursor: pointer; font-size: 1.5rem; - transition: all ease-in-out .2s; + transition: all ease-in-out .1s; scale: 1; } @@ -286,7 +296,7 @@ footer a{ justify-content: center; cursor: pointer; font-size: 1.5rem; - transition: all ease-in-out .2s; + transition: all ease .1s; scale: 1; } @@ -299,6 +309,8 @@ footer a{ align-items: center; justify-content: center; width: 100%; + margin-top: .35rem; + opacity: .65; } #dash-loading-screen { @@ -317,6 +329,13 @@ footer a{ z-index: 1; } +span#last-update{ + font-size: .65rem; + color: var(--atlantis-300); + font-style: italic; + margin: .0rem; +} + .lds-ripple { display: inline-block; position: relative; @@ -400,4 +419,100 @@ canvas{ form.invalid input:required{ border: 2px solid #ff6a6a !important; -} \ No newline at end of file +} + +.table-container { + width: 100%; + padding: 0 .25rem 0 0; + height: calc((100vh - 100px - 70px - 30px) / 2); + overflow-x: auto; + border-radius: 0.25rem; +} + +table { + width: 100%; + border-collapse: collapse; + font-size: .75rem; + background-color: var(--atlantis-50); + border-radius: 0.5rem; +} + +thead { + background-color: var(--atlantis-500); + color: var(--atlantis-100); +} + +thead th { + text-align: center; + font-weight: bold; + padding: .35rem 0; +} + +tbody tr:nth-child(even) { + background-color: var(--atlantis-200); +} + +tbody tr:nth-child(odd) { + background-color: var(--atlantis-50); +} + +tbody td { + width: calc(100% / 5); + text-align: center; + border-bottom: 1px solid var(--atlantis-300); + padding: .25rem 0; +} + +tbody td span { + background-color: var(--atlantis-400); + color: var(--atlantis-900); + padding: 0 .25rem; + border-radius: .25rem; + font-size: .65rem; + width: 35px; + display: inline-block; +} + +tfoot { + background-color: var(--atlantis-600); + color: var(--atlantis-50); +} + +tfoot td { + padding: 0.75rem; + font-weight: bold; + text-align: right; +} + +@media (max-width: 768px) { + thead { + display: none; + } + + tbody, tfoot { + display: block; + width: 100%; + } + + tbody tr, tfoot tr { + display: flex; + flex-direction: column; + width: 100%; + border-bottom: 1px solid var(--atlantis-300); + } + + tbody td, tfoot td { + display: flex; + justify-content: space-between; + align-items: center; + padding: 0.5rem; + text-align: left; + } + + tbody td::before, tfoot td::before { + content: attr(data-label); + font-weight: bold; + width: 50%; + flex-basis: 50%; + } +} diff --git a/assets/styles/popup.css b/assets/styles/popup.css index 6089746..afdcba1 100644 --- a/assets/styles/popup.css +++ b/assets/styles/popup.css @@ -45,7 +45,7 @@ header{ } main{ - padding: .5rem; + padding: .2rem .5rem; width: 100%; flex: 1; gap: .5rem; @@ -53,7 +53,6 @@ main{ align-items: center; justify-content: space-between; flex-direction: column; - padding-bottom: 1rem; } .table{ @@ -95,6 +94,7 @@ main{ .table--item{ padding: .30rem; + font-weight: bold; } .table--item.manual{ @@ -165,6 +165,17 @@ span#current-date, span#estimated-output-hour, span#hours-balance-container{ font-style: italic; } +span#estimated-output-hour{ + margin-bottom: .5rem; +} + +span#last-update{ + font-size: .65rem; + color: var(--atlantis-600); + font-style: italic; + margin: .0rem; +} + .message{ text-align: center; font-size: .65rem; @@ -214,6 +225,7 @@ footer a{ flex-direction: column; min-height: 100px; font-size: 1rem; + margin-bottom: 1rem; } .lds-ripple { @@ -303,7 +315,7 @@ footer a{ } } -#not-signed-in-screen, #disabled-screen, #not-started-screen{ +#not-signed-in-screen, #disabled-screen, #not-started-screen, #not-logged-screen{ display: flex; align-items: center; justify-content: center; @@ -311,18 +323,18 @@ footer a{ min-height: 100px; } -#not-signed-in-screen p, #not-started-screen p, #disabled-screen p { +#not-signed-in-screen p, #not-started-screen p, #disabled-screen p, #not-logged-screen p { text-align: center; color: var(--atlantis-700); font-size: .85rem; } -#not-signed-in-screen strong, #not-started-screen strong, #disabled-screen strong { +#not-signed-in-screen strong, #not-started-screen strong, #disabled-screen strong, #not-logged-screen strong { color: var(--atlantis-600); font-weight: bold; } -#not-signed-in-screen a, #not-started-screen a, #disabled-screen a { +#not-signed-in-screen a, #not-started-screen a, #disabled-screen a, #not-logged-screen a { color: var(--atlantis-600); text-decoration: none; font-weight: bold; diff --git a/config/index.html b/config/index.html index 3f0c6e3..b277caa 100644 --- a/config/index.html +++ b/config/index.html @@ -16,47 +16,24 @@

+

+ Última atualização em 00 de junho às 00:00:00
-
-

Olá, user! 👋🏼

+
+

Olá, Colaborador! 👋🏼

+ user avatar -
-
-
- - -
-
- - -
-
- - -
-
+
- - -
- -
- - + +
- - -
+ + +
- -
- -
-

⚙️ Configurações

-
@@ -106,35 +83,46 @@

⚙️ Configurações

-
-
-
-
-
-
- Carregando.. -
+ + +
- -
-

🔒 Entrar

-
-
-
- - - -
-
-
-
- - - -
-
-
-
- -
-
-
- -
diff --git a/manifest.json b/manifest.json index 6f94da4..0e9ce23 100644 --- a/manifest.json +++ b/manifest.json @@ -1,6 +1,6 @@ { "name": "Tradingworks+", - "version": "2.0.1", + "version": "2.0.5", "description": "Extension to facilitate the usability of the TradingWorks platform for managing points between work shifts.", "icons": { "16": "assets/favicon16.png", @@ -10,7 +10,7 @@ "manifest_version": 3, "author": "Ernane Ferreira", "permissions": ["storage", "notifications", "offscreen"], - "host_permissions": ["https://app.tradingworks.net/*", "https://buddy.ernane.dev/*", "https://api-main.tworh.com.br/*", "https://api-infra.tworh.com.br/", "https://cdn.jsdelivr.net/*"], + "host_permissions": ["https://app.tradingworks.net/*", "https://buddy.ernane.dev/*"], "action": { "default_popup": "./popup.html", "default_title": "Tradingworks+" diff --git a/offscreen/offscreen.js b/offscreen/offscreen.js index 9f0391e..1773593 100644 --- a/offscreen/offscreen.js +++ b/offscreen/offscreen.js @@ -39,19 +39,29 @@ class OffscreenHelper { static calculatePoints(points) { return points.map((point, index, array) => { - if (index % 2 !== 0) return; - - const start = point; - const end = array[index + 1]; - const nextPoint = array[index + 2]; + const start = new Date(); + start.setHours(Number(point.start.split(':')[0])); + start.setMinutes(Number(point.start.split(':')[1])); + + let end = null; + if(point.end){ + end = new Date(); + end.setHours(Number(point.end.split(':')[0])); + end.setMinutes(Number(point.end.split(':')[1])); + }else{ + point.compTime = OffscreenHelper.calculateInterval(start, new Date(), points.length) * 60; + } + + const nextPointStart = new Date(); + nextPointStart.setHours(Number(array[index + 1]?.start.split(':')[0])); + nextPointStart.setMinutes(Number(array[index + 1]?.start.split(':')[1])); return { - startDate: start?.eventDateTime, - endDate: end?.eventDateTime, - manualStartDate: !!(start?.workerReason), - manualEndDate: !!(end?.workerReason), - duration: OffscreenHelper.calculateDiffDates(start?.eventDateTime, end?.eventDateTime), - interval: OffscreenHelper.calculateInterval(end?.eventDateTime, nextPoint?.eventDateTime, points.length) + startDate: start, + endDate: end, + duration: point.compTime/60 || 0, + interval: array[index + 1] ? OffscreenHelper.calculateInterval(end, nextPointStart, points.length) : 0, + ...point }; }).filter(Boolean); } @@ -181,59 +191,139 @@ class TWOffscreenNotifications { } } class TWOffscreen { - TRADING_WORKS_POINTS_API = 'https://api-main.tworh.com.br/api/attendanceregister/list'; - TRADING_WORKS_TIME_BANK_API = 'https://api-main.tworh.com.br/api/CompTimeEvent/list'; - constructor() { this.#updateTradingWorksData(); } - get configurations() { + async #fetchUserPoints() { try { - return JSON.parse(localStorage.getItem('tradingWorksSettings')); + const request = fetch("https://app.tradingworks.net/Attendances/ClockInOut.aspx"); + const rawHtml = await request.then(res => res.text()); + + const parser = new DOMParser(); + const document = parser.parseFromString(rawHtml, 'text/html'); + + const table = document.querySelector('.container table'); + if(!table) return null; + + let cells = table.getElementsByTagName('td'); + let textContent = ''; + let colors = []; + for (let cell of cells) { + let icon = cell.querySelector('i'); + if(icon) colors.push(icon?.style.color || ''); + textContent += cell.textContent + ' '; + } + + const rawPoints = textContent.match(/\b\d{2}:\d{2}\b/g); + const points = []; + + // rawPoints.pop(); + // rawPoints.pop(); + // rawPoints.pop(); + // rawPoints.pop(); + + for (let i = 0; i < rawPoints.length; i += 3) { + let compTime = 0; + if(rawPoints[i + 2]){ + const [compTimeHours, compTimeMinutes] = rawPoints[i + 2].split(':').map(Number); + compTime = compTimeHours * 60 + compTimeMinutes; + } + + points.push({ + start: rawPoints[i], + startColor: null, + end: rawPoints[i + 1], + endColor: null, + compTime: compTime, + compTimeString: rawPoints[i + 2] + }); + } + + points.forEach((point, index) => { + if(point.start) point.startColor = colors[index * 2]; + if(point.end) point.endColor = colors[(index * 2) + 1]; + }); + + return points; } catch (e) { + // console.log(e); return null; } } - get user(){ - try{ - return JSON.parse(localStorage.getItem('tradingWorksUser')); - }catch(e){ - return null; - } - } + async #fetchTimeBank() { + try { + const request = fetch("https://app.tradingworks.net/Payroll/MyTimeCards.aspx"); + const rawHtml = await request.then(res => res.text()); - async #fetchUserPoints(employeeid, companyid) { - if (!employeeid || !companyid) return null; + const parser = new DOMParser(); + const document = parser.parseFromString(rawHtml, 'text/html'); - try { - // ?CompanyId=<0000>&EmployeeId=<00000>&BaseDate= - const currentDate = new Date(); - const baseDate = `${currentDate.getFullYear()}-${currentDate.getMonth() + 1}-${currentDate.getDate()}`; - const URL = `${this.TRADING_WORKS_POINTS_API}?CompanyId=${companyid}&EmployeeId=${employeeid}&BaseDate=${baseDate}`; - const rawData = await fetch(URL, {method: 'GET'}); + const bodyBalance = document.getElementById('Body_Body_lblBalance'); + if(!bodyBalance) return null; + + const bh = bodyBalance.innerText.match(/\b\d{2}:\d{2}\b/g); + if(!bh[0]) return null; - return await rawData.json(); + const [hours, minutes] = bh[0].split(':').map(Number); + + return (hours * 60 + minutes) * (bodyBalance.innerText.includes('-') ? -1 : 1); } catch (e) { // console.log(e); return null; } } - async #fetchTimeBank(employeeid, companyid) { - if (!employeeid || !companyid) return null; + async #fetchExtraSettings(){ + const extraSettings = {}; + + const getCurrentMonthAppointments = async () => { + const request = fetch("https://app.tradingworks.net/Attendances/ListAttendances.aspx"); + const rawHtml = await request.then(res => res.text()); + + + const parser = new DOMParser(); + const document = parser.parseFromString(rawHtml, 'text/html'); - // ?EmployeeId=<00000>&CompanyId=<0000>&ViewHistory=false + const table = document.querySelector('table[data-filename="TWO - Apontamentos"]'); + const headers = []; + const data = []; + + const headerElements = table.querySelectorAll('thead th'); + headerElements.forEach(header => { + headers.push(header.textContent.trim().replace(/\s/g, '_').toLowerCase()); + }); + + const rows = table.querySelectorAll('tbody tr'); + rows.forEach(row => { + const cells = row.querySelectorAll('td'); + const rowData = {}; + cells.forEach((cell, index) => { + rowData[headers[index]] = cell.textContent.trim(); + }); + data.push(rowData); + }); + + return data; + } try { - const URL = `${this.TRADING_WORKS_TIME_BANK_API}?EmployeeId=${employeeid}&CompanyId=${companyid}&ViewHistory=false`; - const rawData = await fetch(URL, {method: 'GET'}); + const request = fetch("https://app.tradingworks.net/Attendances/ClockInOut.aspx"); + const rawHtml = await request.then(res => res.text()); + + const parser = new DOMParser(); + const document = parser.parseFromString(rawHtml, 'text/html'); - return await rawData.json(); + extraSettings['employ'] = document.getElementById('Body_lnkCompanyAlias').innerText; + extraSettings['userName'] = document.getElementById('Body_Body_lblFullName').innerText; + extraSettings['userImage'] = document.getElementById('Body_Body_imgAvatar').src; + extraSettings['currentMonthAppointments'] = await getCurrentMonthAppointments(); } catch (e) { // console.log(e); - return null; + return extraSettings; } + + return extraSettings; } async #updateTradingWorksData() { @@ -243,40 +333,30 @@ class TWOffscreen { this.#setScreen('disabled'); return setTimeout(async () => await this.#updateTradingWorksData(), 60000); } - - this.#setScreen('loading'); - - const user = this.user; - - if (!user){ - this.#setScreen('not-started'); - return null; - }; - const userPoints = await this.#fetchUserPoints(user.employeeid, user.companyid); - const userTimeBank = await this.#fetchTimeBank(user.employeeid, user.companyid); + const userPoints = await this.#fetchUserPoints(); + const userTimeBank = await this.#fetchTimeBank(); - if (!userPoints || !userTimeBank){ - this.#setScreen('not-started'); - return null; - }; + if(!userPoints){ + localStorage.setItem('tradingWorksPlusCalculatedData', JSON.stringify({})); + return setTimeout(async () => await this.#updateTradingWorksData(), 1000); + } - this.#setScreen('started'); + const extraSettings = await this.#fetchExtraSettings(); + const currentSettings = JSON.parse(localStorage.getItem('tradingWorksSettings')) || {}; + const settings = {...currentSettings, ...extraSettings, lastUpdate: new Date()}; - const timeBank = userTimeBank.listResponse.map(el => el.compTime).reduce((acc, cur) => acc + cur, 0); - const points = userPoints.listResponse; + localStorage.setItem('tradingWorksSettings', JSON.stringify(settings)); - // points.pop(); - // points.pop(); - // points.pop(); - // points.pop(); + const timeBank = userTimeBank/60; + const points = userPoints; const data = OffscreenHelper.calculateInformation({ points, timeBank }); TWOffscreenNotifications.handleSentMessages(data); localStorage.setItem('tradingWorksPlusCalculatedData', JSON.stringify(data)); - setTimeout(async () => await this.#updateTradingWorksData(), 60000); + setTimeout(async () => await this.#updateTradingWorksData(), 30000); } #setScreen(screen) { diff --git a/popup.html b/popup.html index eb4b270..457a6fc 100644 --- a/popup.html +++ b/popup.html @@ -18,7 +18,7 @@
@@ -47,6 +47,11 @@

TradingWorks+

+ + @@ -64,6 +69,7 @@

TradingWorks+

+ Última atualização em 00 de junho às 00:00:00