From 8410ac967fe0cbc977c1681719463a885241ffcb Mon Sep 17 00:00:00 2001 From: Jonathan Ma Date: Thu, 25 Jan 2024 12:51:43 -0500 Subject: [PATCH 1/8] change folder structure --- {renderer => assets}/AppIcon.png | Bin {renderer => assets}/MaterialIcons.woff2 | Bin {renderer => assets}/moon.png | Bin {renderer => assets}/sun.png | Bin {renderer => assets}/test.jpg | Bin main.js | 20 +- package-lock.json | 15 +- package.json | 3 +- renderer/item_main.js | 1419 ---------------- .../asset_translation_excel.js | 0 .../asset_translation_main.js | 0 .../asset_translation_sqlite.js | 0 .../item-translation-sqlite.js | 0 .../item_translation/item-translation.js | 2 +- {assets => src/misc}/autoupdater.js | 0 {assets => src/misc}/better-sqlite.js | 0 {assets => src/misc}/common.js | 0 {assets => src/misc}/constants.js | 0 {assets => src/misc}/exceljs.js | 0 {assets => src/misc}/indexDB.js | 2 +- {assets => src/misc}/maximo.js | 10 +- {assets => src/misc}/sharedDB.js | 0 {assets => src/misc}/spreadsheet.js | 0 {assets => src/misc}/utils.js | 0 {assets => src/misc}/validators.js | 0 .../renderer}/asset_translation.html | 2 +- .../renderer}/asset_translation.js | 0 {renderer => src/renderer}/item_main.html | 8 +- src/renderer/item_main.js | 1421 +++++++++++++++++ .../renderer}/item_translation.html | 0 .../renderer}/item_translation.js | 0 .../renderer}/observation_template.html | 0 .../renderer}/observation_template.js | 2 +- {renderer => src/renderer}/setting.html | 0 {renderer => src/renderer}/setting.js | 0 {renderer => src/renderer}/start_page.html | 4 +- {renderer => src/renderer}/start_page.js | 0 {renderer => src/renderer}/style.css | 10 +- {renderer => src/renderer}/table_style.css | 0 {renderer => src/renderer}/worker.js | 24 +- tsconfig.json | 110 ++ 41 files changed, 1588 insertions(+), 1464 deletions(-) rename {renderer => assets}/AppIcon.png (100%) rename {renderer => assets}/MaterialIcons.woff2 (100%) rename {renderer => assets}/moon.png (100%) rename {renderer => assets}/sun.png (100%) rename {renderer => assets}/test.jpg (100%) delete mode 100644 renderer/item_main.js rename {assets => src}/asset_translation/asset_translation_excel.js (100%) rename {assets => src}/asset_translation/asset_translation_main.js (100%) rename {assets => src}/asset_translation/asset_translation_sqlite.js (100%) rename {assets => src}/item_translation/item-translation-sqlite.js (100%) rename {assets => src}/item_translation/item-translation.js (99%) rename {assets => src/misc}/autoupdater.js (100%) rename {assets => src/misc}/better-sqlite.js (100%) rename {assets => src/misc}/common.js (100%) rename {assets => src/misc}/constants.js (100%) rename {assets => src/misc}/exceljs.js (100%) rename {assets => src/misc}/indexDB.js (99%) rename {assets => src/misc}/maximo.js (99%) rename {assets => src/misc}/sharedDB.js (100%) rename {assets => src/misc}/spreadsheet.js (100%) rename {assets => src/misc}/utils.js (100%) rename {assets => src/misc}/validators.js (100%) rename {renderer => src/renderer}/asset_translation.html (98%) rename {renderer => src/renderer}/asset_translation.js (100%) rename {renderer => src/renderer}/item_main.html (99%) create mode 100644 src/renderer/item_main.js rename {renderer => src/renderer}/item_translation.html (100%) rename {renderer => src/renderer}/item_translation.js (100%) rename {renderer => src/renderer}/observation_template.html (100%) rename {renderer => src/renderer}/observation_template.js (98%) rename {renderer => src/renderer}/setting.html (100%) rename {renderer => src/renderer}/setting.js (100%) rename {renderer => src/renderer}/start_page.html (97%) rename {renderer => src/renderer}/start_page.js (100%) rename {renderer => src/renderer}/style.css (89%) rename {renderer => src/renderer}/table_style.css (100%) rename {renderer => src/renderer}/worker.js (96%) create mode 100644 tsconfig.json diff --git a/renderer/AppIcon.png b/assets/AppIcon.png similarity index 100% rename from renderer/AppIcon.png rename to assets/AppIcon.png diff --git a/renderer/MaterialIcons.woff2 b/assets/MaterialIcons.woff2 similarity index 100% rename from renderer/MaterialIcons.woff2 rename to assets/MaterialIcons.woff2 diff --git a/renderer/moon.png b/assets/moon.png similarity index 100% rename from renderer/moon.png rename to assets/moon.png diff --git a/renderer/sun.png b/assets/sun.png similarity index 100% rename from renderer/sun.png rename to assets/sun.png diff --git a/renderer/test.jpg b/assets/test.jpg similarity index 100% rename from renderer/test.jpg rename to assets/test.jpg diff --git a/main.js b/main.js index 40b129c..4c8decf 100644 --- a/main.js +++ b/main.js @@ -2,9 +2,9 @@ const {app, BrowserWindow, ipcMain, screen, dialog, shell} = require('electron'); const path = require('path'); const fs = require('fs'); -const {appUpdater} = require('./assets/autoupdater'); -const CONSTANTS = require('./assets/constants.js'); -require('electron-reload')(__dirname) +const {appUpdater} = require('./src/misc/autoupdater.js'); +const CONSTANTS = require('./src/misc/constants.js'); +require('electron-reload')(__dirname); let mainWindow; let settingWindow; @@ -52,7 +52,7 @@ ipcMain.on('openSettings', (event, arg) => { }, }); - settingWindow.loadFile(path.join('renderer', 'setting.html')); + settingWindow.loadFile(path.join('src', 'renderer', 'setting.html')); settingWindow.show(); settingWindow.on('closed', () => { mainWindow.show(); @@ -111,23 +111,23 @@ ipcMain.on('getPath', (event, arg) => { }); ipcMain.on('loading', (event, arg) => { - mainWindow.loadFile(path.join('renderer', 'item_main.html')); + mainWindow.loadFile(path.join('src', 'renderer', 'item_main.html')); }); ipcMain.on('start_item_module', (event, arg) => { - mainWindow.loadFile(path.join('renderer', 'item_loading.html')); + mainWindow.loadFile(path.join('src', 'renderer', 'item_loading.html')); }); ipcMain.on('start_observation_template', (event, arg) => { - mainWindow.loadFile(path.join('renderer', 'observation_template.html')); + mainWindow.loadFile(path.join('src', 'renderer', 'observation_template.html')); }); ipcMain.on('start_item_translate', (event, arg) => { - mainWindow.loadFile(path.join('renderer', 'item_translation.html')); + mainWindow.loadFile(path.join('src', 'renderer', 'item_translation.html')); }); ipcMain.on('start_asset_translate', (event, arg) => { - mainWindow.loadFile(path.join('renderer', 'asset_translation.html')); + mainWindow.loadFile(path.join('src', 'renderer', 'asset_translation.html')); }); function createWindow() { @@ -147,7 +147,7 @@ function createWindow() { }); // and load the index.html of the app. - mainWindow.loadFile(path.join('renderer', 'start_page.html')); + mainWindow.loadFile(path.join('src', 'renderer', 'start_page.html')); // Open the DevTools. if(CONSTANTS.OPEN_DEV_TOOLS) { diff --git a/package-lock.json b/package-lock.json index 78fde1f..5b583fb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,7 +15,8 @@ "electron-squirrel-startup": "^1.0.0", "exceljs": "^4.3.0", "lodash": "^4.17.0", - "luxon": "^3.3.0" + "luxon": "^3.3.0", + "typescript": "^5.3.3" }, "devDependencies": { "@electron-forge/cli": "^6.2.1", @@ -6016,6 +6017,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/typescript": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", + "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, "node_modules/undici-types": { "version": "5.26.5", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", diff --git a/package.json b/package.json index 242708b..d5d4462 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,8 @@ "electron-squirrel-startup": "^1.0.0", "exceljs": "^4.3.0", "lodash": "^4.17.0", - "luxon": "^3.3.0" + "luxon": "^3.3.0", + "typescript": "^5.3.3" }, "config": { "forge": { diff --git a/renderer/item_main.js b/renderer/item_main.js deleted file mode 100644 index 3878af6..0000000 --- a/renderer/item_main.js +++ /dev/null @@ -1,1419 +0,0 @@ -const { clipboard, ipcRenderer, shell } = require('electron'); -const fs = require('fs'); -const path = require('path'); -// const { dialog } = require('electron').remote; -const Database = require('../assets/indexDB'); -const SharedDatabase = require('../assets/sharedDB'); -const Validate = require('../assets/validators'); -const Maximo = require('../assets/maximo'); -const CONSTANTS = require('../assets/constants.js'); -//stores items that are to be uploaded through the "batch upload" accordion. -let itemsToUpload = []; - -//stores images that are to be uploaded through the "image upload" accordion. -let imgsToUpload = []; - -//an object that stores the location of each column in the batch upload table. -//allows for column locations to be interchanged. -1 means a column is not in -//the table. Maybe in the future, column locations should be predetermined so -//that a global variable is not used for this. -let colLoc = { - description: -1, - uom: -1, - commGroup: -1, - glClass: -1, - maximo: -1, - vendor: -1, - storeroom: -1, - catNum: -1, - siteID: -1, -} - -//an object that stores all search results and "bookmarks" how many items have -//been loaded in the related results table. Used for infinite scroll. -let relatedResults = { - idx: 0, // store the index of the current item being loaded - curKey: 0, // store which key is currently being loaded from the search results - results: [], // store all search results (dictonary) -} - -//a function that is called immediately after the window has been loaded -window.onload = function () { - //set the darkmode toggle to the correct position by retreiving information from the local storage - document.getElementById('dark-mode-switch').checked = (localStorage.getItem('theme') === 'dark' ? true : false); - - //change the UI based on whether the user is a "power user". show all upload elements if they are a power user, else hide it. - if (localStorage.getItem('powerUser') === 'true') { - document.getElementById("upload-btn").style.display = "block"; - document.getElementById("request-btn").style.display = "none"; - document.getElementById("batch-upld-btn").style.display = "block"; - document.getElementById("img-upld-toggle").style.display = "block"; - document.getElementById("batch-mode-toggle").style.display = "block"; - return; - } -} - -/* -Power User Toggle - -Allows user to toggle between 2 modes: Power user and Normal User. -Normal user mode hides all upload elements and only allows the user to request items. -Power user mode shows all upload elements and allows the user to upload items and images. - -Created for the purpose of hiding upload functionality from people who shouldn't be -uploading items (a.k.a. everyone except for reliability team). -*/ -//set the user to a power user if they have clicked the secret button 5 times -document.getElementById("secret-button").addEventListener('click', (e) => { - let isPowerUser = false; - let numClicks = parseInt(e.target.getAttribute('data-clicks')); - - numClicks++; - - if (numClicks === 5) { - isPowerUser = true; - localStorage.setItem('powerUser', 'true'); - e.target.setAttribute('data-clicks', '0'); - } - else { - localStorage.setItem('powerUser', 'false'); - e.target.setAttribute('data-clicks', `${numClicks}`); - isPowerUser = false; - } - - //toggle whether elements are hidden or not based off of power user status - if (isPowerUser == true) { - document.getElementById("upload-btn").style.display = "block"; - document.getElementById("request-btn").style.display = "none"; - document.getElementById("batch-upld-btn").style.display = "block"; - document.getElementById("img-upld-toggle").style.display = "block"; - document.getElementById("batch-mode-toggle").style.display = "block"; - } - else { - document.getElementById("upload-btn").style.display = "none"; - document.getElementById("request-btn").style.display = "block"; - document.getElementById("batch-upld-btn").style.display = "none"; - document.getElementById("img-upld-toggle").style.display = "none"; - document.getElementById("batch-mode-toggle").style.display = "none"; - } -}); - -//gets user site information -async function getSite(credentials = {}) { - const maximo = new Maximo(); - const currInfo = await maximo.checkLogin(credentials?.userid, credentials?.password); - return currInfo.siteID; -} - -//open a modal that allows you to make an item request -document.getElementById("request-btn").addEventListener('click', () => { - //show request item modal - let requestModal = new bootstrap.Modal(document.getElementById("requestModal")); - requestModal.toggle(); - - let currPass = new SharedDatabase().getPassword(), - userid = currPass.userid; - let siteID; - - const sites = { - 'AA': ['AAG: Brampton B2 Storeroom', 'AAL: Brampton B2/B4 Maintenance Storeroom', 'AAO: Brampton B4 Oxidizer Storeroom'], - 'ANT': ['AN1: Antwerp Mod Line Storeroom', 'AN2: Antwerp Coating Line Storeroom'], - 'BA': ['BAL: IKO Calgary Maintenance Storeroom'], - 'BL': ['BLC: Hagerstown TPO Storeroom', 'BLD: Hagerstown ISO Storeroom', 'BLL: Hagerstown Maintenance Storeroom(Shared)'], - 'CA': ['CAL">IKO Kankakee Maintenance Storeroom'], - 'CAM': ['C61">IKO Appley Bridge Maintenance Storeroom'], - 'COM': ['CB1">Combronde Maintenance Storeroom'], - 'GA': ['GAL: IKO Wilmington Maintenance Storeroom'], - 'GC': ['GCL: Sumas Maintenance Storeroom', 'GCA: Sumas Shipping Storeroom', 'GCD: Sumas Shingle Storeroom', 'GCG: Sumas Mod Line Storeroom', 'GCJ: Sumas Crusher Storeroom', 'GCK: Sumas Tank Farm Storeroom'], - 'GE': ['GEL: Ashcroft Maintenance Storeroom'], - 'GH': ['GHL: IKO Hawkesbury Maintenance Storeroom'], - 'GI': ['GIL: IKO Madoc Maintenance Storeroom'], - 'GJ': ['GJL: CRC Toronto Maintenance Storeroom'], - 'GK': ['GKA: IG Brampton B7 and B8 Storeroom', 'GKC: IG Brampton B6 and Laminator Storeroom', 'GKL: IG Brampton Maintenance Storeroom'], - 'GM': ['GML: IG High River Maintenance Storeroom'], - 'GP': ['GPL: CRC Brampton Maintenance Storeroom'], - 'GR': ['GRL: Bramcal Maintenance Storeroom'], - 'GS': ['GSL: Sylacauga Maintenance Storeroom'], - 'GV': ['GVL: IKO Hillsboro Maintenance Storeroom'], - 'GX': ['GXL: Maxi-Mix Maintenance Storeroom'], - 'KLU': ['KD1: IKO Klundert Maintenance Storeroom', 'KD2: IKO Klundert Lab Storeroom', 'KD3: IKO Klundert Logistics Storeroom'], - 'PBM': ['PB6: Slovakia Maintenance Storeroom'], - 'RAM': ['RA6: IKO Alconbury Maintenance Storeroom'] - // Add more sites and storerooms as needed... - }; - - const userSite = getSite({ userid: userid, password: currPass.password }); - userSite.then(response => { - siteID = response; - - const storeroomSelect = document.getElementById('storeroom'); - //poppulate correct user storerooms in modal - function updateStoreroomOptions() { - - storeroomSelect.options.length = 1; - - // Add new options - const neededStorerooms = sites[siteID]; - for (const storeroom of neededStorerooms) { - const option = document.createElement('option'); - option.value = storeroom; - option.text = storeroom; - storeroomSelect.add(option); - } - } - updateStoreroomOptions(); - }) - .catch(error => console.error(`Error: ${error}`)); - - poppulateModal(); -}); - -//Allow input of manufacturer name & part number if "Other" is selected -document.getElementById("manu-name").addEventListener('click', (e) => { - if (e.target.value == "Other") { - document.getElementById("pref-manu").style.display = "block"; - document.getElementById("part-form").style.display = "block"; - } - else { - document.getElementById("pref-manu").style.display = "none"; - document.getElementById("part-form").style.display = "none"; - } -}) - -//download email file when submit button is pressed -document.getElementById("submit-btn").addEventListener('click', submitMail, false); - -//opens email file in default mail client -function submitMail() { - //checking required fields are filled - if (document.getElementById("manu-name").value == "Other") { - - if (!(document.getElementById("part-num").reportValidity() && - document.getElementById("storeroom").reportValidity() && - document.getElementById("item-descr").reportValidity())) { - console.log("Required fields still empty"); - return; - } - } - else { - - if (!(document.getElementById("storeroom").reportValidity() && - document.getElementById("item-descr").reportValidity())) { - console.log("Required fields still empty"); - return; - } - } - //storing current date and time for email subject - let currentdate = new Date(); - var datetime = currentdate.getFullYear() + "/" + (currentdate.getMonth() + 1) + "/" + (currentdate.getDay() + 1) - + " @ " - + currentdate.getHours() + ":" - + currentdate.getMinutes() + ":" + currentdate.getSeconds(); - let mailText = - ``; - - //Send string to main process to write file - ipcRenderer.send('write-file', mailText); - //requestModal.toggle(); -} - -/* Infinite scroll - -Allows elements to load as the user scrolls down the page, -drastically decreasing loading times and making UI smoother. -*/ -//listen for a scroll event. if the bottom of the results table is less than 100px below the bottom of the viewport, load more items -document.getElementById("everything").addEventListener('scroll', () => { - //dont add items to the list if the accordion is collapsed - if (document.getElementById("related-items-accordion-btn").classList.contains("collapsed") || relatedResults.results.length == 0) { - return; - } - - let searchResultsTable = document.getElementById("related-items"); - - let domRect = searchResultsTable.getBoundingClientRect(); - let spaceBelow = document.getElementById("everything").offsetHeight - domRect.bottom; - - if (spaceBelow > -100) { - //load more items if the bottom of the table is less than 100px below the bottom of the viewport - loadRelated(); - } -}) - -//Generate UI when files are selected by user -document.getElementById("imgInput").addEventListener("change", async (e) => { - let progressBar = new ProgressBar(); - - //reset UI - document.getElementById("imgList").innerHTML = ``; - //get files from file picker - let files = document.getElementById("imgInput").files; - imgsToUpload = files; - //make a comma separated string of all the item numbers that are to be uploaded - let nums = ''; - - //if no files were selected, return - if (files.length == 0 || !files) { - return; - } - - let imgList = document.getElementById("imgList"); - - progressBar.update(0, 'Loading Images...'); - - //for each image, add list item to HTML list - for (let i = 0; i < files.length; i++) { - let file = files[i]; - let completion = (i + 1) / files.length * 100; - - nums += file.name.slice(0, 7) + ','; //get first 7 characters of file name - progressBar.updateProgressBar(completion); - - imgList.innerHTML += ` -
  • -
    - -

    ${file.name.slice(0, 7)}

    -
    - pending -
  • `; - } - - //generate a link to open items that are being uploaded to in maximo - - let url = `https://${CONSTANTS.ENV}.iko.max-it-eam.com/maximo/oslc/graphite/manage-shell/index.html?event=loadapp&value=item&additionalevent=useqbe&additionaleventvalue=itemnum=${nums}`; - document.getElementById("img-upload-status-text").innerHTML = `Selected Items:`; - document.getElementById("imgs-link").addEventListener('click', function (e) { - e.preventDefault(); - shell.openExternal(url); - }); - - progressBar.update(100, 'Ready to Upload!'); -}); - -//clear the file picker each time it is clicked -document.getElementById("imgInput").addEventListener("click", () => { - document.getElementById("img-clear-btn").dispatchEvent(new Event('click')); -}); - -document.getElementById("img-clear-btn").addEventListener("click", () => { - //reset all related components - let progressBar = new ProgressBar(); - document.getElementById("imgList").innerHTML = ``; - progressBar.update(100, 'Ready!'); - //empty list of images to upload - imgsToUpload = []; - //empty file picker - document.getElementById("imgInput").value = null; - - document.getElementById("img-upload-status-text").innerHTML = 'Select Images to Continue...' -}); -document.getElementById("img-upload-btn").addEventListener("click", () => { - let progressBar = new ProgressBar(); - - let clearBtn = document.getElementById('img-clear-btn'); - let uploadBtn = document.getElementById('img-upload-btn'); - - //return if user has picked no images - if (imgsToUpload.length == 0) { - new Toast('No Images Selected!'); - return; - } - - let finishedItems = 0; - - //disable the clear and upload buttons while upload is taking place so the - //user can't send duplicate requests or accidentally clear the image upload list - //while its uploading - clearBtn.disabled = true; - uploadBtn.disabled = true; - - const worker = new WorkerHandler(); - - progressBar.update(0, 'Uploading Images...'); - - //upload all images and update UI - worker.work(['uploadImages', imgsToUpload], (result) => { - if (result[0] == 'success') { - //if success, display checkmark - document.getElementById(`img-${result[1]}-status`).innerHTML = `done`; - } else if (result[0] == 'fail') { - //if fail, display 'x' (cross) - document.getElementById(`img-${result[1]}-status`).innerHTML = `close`; - } else if (result[0] == 'done') { - progressBar.update(100, 'Upload Complete!'); - clearBtn.disabled = false; - uploadBtn.disabled = false; - } else if (result[0] == 'warning') { - //if warning, display triangle with exclamation point in it. This only occurs if you try - //to upload an image to an item that already has an image - document.getElementById(`img-${result[1]}-status`).innerHTML = `warning`; - } else if (result[0] == 'total failure') { - finishedItems = imgsToUpload.length; - progressBar.update(100, 'Error occurred while attempting upload!'); - document.getElementById("img-upload-status-text").innerHTML = `Upload Failed: ${result[1]}}`; - clearBtn.disabled = false; - uploadBtn.disabled = false; - } - - if (result != 'done') { - finishedItems++; - } - - //update progressbar when each image is uploaded/fails upload - progressBar.updateProgressBar(finishedItems * 100 / imgsToUpload.length); - }); -}); - -//Other -document.getElementById("load-item").addEventListener("click", loadItem); -document.getElementById("valid-single").addEventListener("click", () => { validSingle() }); -document.getElementById("valid-single-ext").addEventListener("click", () => { validSingle(true) }); -document.getElementById("settings").addEventListener("click", openSettings); -document.getElementById("topButton").addEventListener("click", toTop); -document.getElementById("endButton").addEventListener("click", toEnd); -document.getElementById("interactive").addEventListener("click", openExcel); -document.getElementById("worksheet-path").addEventListener("click", openExcel); -document.getElementById("pauseAuto").addEventListener("click", pauseAuto); - -document.getElementById("save-desc").addEventListener("click", writeDescription); -document.getElementById("save-num").addEventListener("click", writeItemNum); -document.getElementById("skip-row").addEventListener("click", skipRow); -document.getElementById("continueAuto").addEventListener("click", continueAuto); -document.getElementById("confirm-btn").addEventListener("click", () => { uploadItem(); }); -document.getElementById("upload-btn").addEventListener("click", () => { - - let confirmModal = new bootstrap.Modal(document.getElementById("confirmModal")); - if (!( - document.getElementById("maximo-desc").reportValidity() && - document.getElementById("uom-field").reportValidity() && - document.getElementById("com-group").reportValidity() && - document.getElementById("gl-class").reportValidity() - )) { - return; - } - ItemAnalysis(); - confirmModal.toggle(); - getNextNumThenUpdate(document.getElementById("num-type").value); -}); - - -//batch upload: -document.getElementById("openBatchFile").addEventListener("click", () => { openFile("worksheet-path") }); - -document.getElementById("clear-batch-items-btn").addEventListener("click", () => { - document.getElementById("batch-items-table").innerHTML = ``; - document.getElementById("batch-copy-nums").disabled = true; - document.getElementById("batch-upload-status-text").innerHTML = 'Waiting for paste...'; -}) - -document.getElementById("batch-copy-nums").addEventListener("click", () => { - try { - let result = getItemsFromTable("batch-items-table"); - if (result == undefined || result == null || result == 0) { - throw ('Table missing columns'); - } - let rows = parseInt(document.getElementById("batch-items-table").getAttribute("data-rows")) - 1; - let nums = ""; - for (let i = 2; i <= rows + 1; i++) { - nums += document.getElementById(`${i}-${colLoc.maximo}`).innerHTML ? (document.getElementById(`${i}-${colLoc.maximo}`).innerHTML + "\n") : ""; - } - navigator.clipboard.writeText(nums); - new Toast('Item Numbers Copied to Clipboard!'); - } catch (error) { - //console.log(error); - new Toast('Unable to copy numbers, please check table formatting!'); - } - -}); - -document.getElementById("batch-items-textinput").addEventListener("paste", (e) => { - setTimeout(() => { - let paste = e.target.value; - let table = document.getElementById("batch-items-table-div"); - table.innerHTML = convertToTable(paste, "batch-items-table"); - - document.getElementById("batch-copy-nums").disabled = false; - - document.getElementById("batch-upload-status-text").innerHTML = 'Paste detected! Edit table if needed and click upload.'; - e.target.value = ""; - }, 0) -}) -document.getElementById("batch-upload-btn").addEventListener("click", () => { - try { - itemsToUpload = getItemsFromTable("batch-items-table") - } catch (error) { - itemsToUpload = []; - document.getElementById("batch-upload-status-text").innerHTML = `Error, check table format! (${error})`; - return; - } - - if (itemsToUpload.length > 0) { - itemsToUpload.forEach((value, idx) => { - if (value) { - updateItemStatus('loading', idx + 1); - } - }) - batchUploadItems(itemsToUpload); - return; - } else { - document.getElementById("batch-upload-status-text").innerHTML = 'No valid items to upload!'; - } - - return; -}) -document.getElementById("batch-paste-btn").addEventListener("click", async () => { - const text = await navigator.clipboard.readText(); - const pasteEvent = new Event("paste", { "bubbles": true, "cancelable": false }); - let textinput = document.getElementById("batch-items-textinput"); - - textinput.value = text; - textinput.dispatchEvent(pasteEvent); -}) -document.getElementById("batch-copy-headers-btn").addEventListener("click", () => { - let copyText = `Maximo\tDescription\tIssue Unit\tCommodity Group\tGL Class\tSite\tStoreroom\tVendor\tCatalogue Number\n\t`; - navigator.clipboard.writeText(copyText); - new Toast('Table copied to clipboard!'); -}) -//dark theme toggle -document.getElementById("dark-mode-switch").addEventListener("click", toggleTheme); -//Infinite scroll - -// listener for enter key on search field -document.getElementById("maximo-desc").addEventListener("keyup", function (event) { - // Number 13 is the "Enter" key on the keyboard - if (event.key === "Enter") { - // Cancel the default action, if needed - event.preventDefault(); - // Trigger the button element with a click - validSingle(); - } -}); - -document.getElementById("interact-num").addEventListener("keyup", function (event) { - // Number 13 is the "Enter" key on the keyboard - if (event.key === "Enter") { - // Cancel the default action, if needed - event.preventDefault(); - // Trigger the button element with a click - loadItem(); - } -}); - -function pauseAuto() { - document.getElementById("modeSelect").checked = true; -} - -function loadItem() { - var itemnum = document.getElementById("interact-num").value.trim(); - new Toast(`Loading Item: ${itemnum}`); - const worker = new WorkerHandler(); - worker.work(['loadItem', itemnum], showItem); -} - -function auto_grow(elementID) { - const element = document.getElementById(elementID); - element.style.height = "5px"; - element.style.height = (element.scrollHeight) + "px"; -} - -function showItem(data) { - document.getElementById("maximo-desc").value = data[0].description; - document.getElementById("uom-field").value = data[0].uom; - document.getElementById("com-group").value = data[0].commodity_group; - document.getElementById("gl-class").value = data[0].gl_class; -} - -function writeDescription() { - const valid = new Validate(); - let field = document.getElementById("maximo-desc"); - if (field.value.length > 0) { - let bar = new ProgressBar(); - bar.update(0, 'Writing asset description to file'); - let desc = field.value.split(','); - desc = valid.assembleDescription(desc); - let params = worksheetParams(); - params.outRow = document.getElementById("current-row").innerHTML; - const worker = new WorkerHandler(); - worker.work(['writeDesc', [params, desc]], writeComplete); - } else { - new Toast('Please enter a valid description'); - } -} - -function worksheetParams(path = false) { - let params = { - // input parameters - wsName: document.getElementById("ws-name").value || "Sheet2", // name of ws - inDesc: (document.getElementById("input-col").value || "F").toUpperCase().split(','), // description columns for input - startRow: document.getElementById("start-row").value || "2", // starting row of ws - // output parameters - outItemNum: document.getElementById("output-col").value.toUpperCase() || "E", - outItemDesc: (document.getElementById("output-col-desc").value || "F,G,H").toUpperCase().split(','), - outComm: document.getElementById("interact-num").value.toUpperCase() || "I", // commodity group out - outGL: document.getElementById("interact-num").value.toUpperCase() || "J", // gl class out - outUOM: document.getElementById("interact-num").value.toUpperCase() || "K", // uom out - outQuestion: document.getElementById("interact-num").value.toUpperCase() || "L", // questions out - outTranslate: document.getElementById("output-col-translation").value.toUpperCase() || "L", - outMissing: document.getElementById("output-col-missing").value.toUpperCase() || "K", - // output data - itemNum: document.getElementById("interact-num").value || '999TEST', - itemDesc: document.getElementById("maximo-desc").value || "TEST,ITEM,DESCRIPTION", - commGroup: document.getElementById("com-group").value || "401", // commodity group in - glClass: document.getElementById("gl-class").value || "6200000000000", //gl class in - uom: document.getElementById("uom-field").value || "EA", // uom in - }; - if (path) { - params.filePath = path; - } else { - params.filePath = document.getElementById("worksheet-path").value; - } - return params; -} - -function writeItemNum() { - let num = document.getElementById("interact-num").value; - if (num.length > 0) { - let bar = new ProgressBar(); - bar.update(0, 'Writing item number to file'); - let path = document.getElementById("worksheet-path").value; - let wsName = document.getElementById("ws-name").value; - let rowNum = document.getElementById("current-row").innerHTML; - let cols = document.getElementById("output-col").value; - const worker = new WorkerHandler(); - worker.work(['writeNum', [path, wsName, rowNum, cols, num]], writeComplete); - } else { - new Toast('Please enter a valid item number'); - } -} - -function writeComplete() { - let rowNum = parseInt(document.getElementById("current-row").innerHTML); - new Toast(`Row ${rowNum} saved!`); - document.getElementById("interact-num").value = ''; - interactiveGoNext(Number(rowNum) + 1); -} - -function openFile(pathElement) { - const validFile = document.getElementById(pathElement); - const filePath = validFile.value; - if (filePath !== 'No file chosen') { - new Toast('Opening File in Excel!'); - shell.openExternal(filePath); - } -} - -//Deprecated function, unused. -function openSettings() { - ipcRenderer.send('openSettings'); - //sendsync blocks parent window... - //https://github.com/electron/electron/issues/10426 -} - -function openExcel() { - document.getElementById("input-col").value = document.getElementById("input-col").value.toUpperCase(); - document.getElementById("output-col").value = document.getElementById("output-col").value.toUpperCase(); - - ipcRenderer.invoke('select-to-be-translated', 'finished').then((result) => { - if (!result.canceled) { - const worker = new WorkerHandler(); - const params = worksheetParams(result.filePaths[0]); - worker.work(['interactive', params], finishLoadingBatch); - document.getElementById("worksheet-path").value = result.filePaths[0]; - } else { - new Toast('File Picker Cancelled'); - } - }); -} - -//BATCH UPLOAD FUNCTIONS -/** - * Reads a table and generates items from it - * - * @param {string} tableId the HTML id of the table to read - * @returns {Array} an array of Items - */ -function getItemsFromTable(tableId) { - colLoc = { - description: -1, - uom: -1, - commGroup: -1, - glClass: -1, - maximo: -1, - vendor: -1, - storeroom: -1, - catNum: -1, - siteID: -1, - } - - let table = document.getElementById(`${tableId}`); - //find Description, UOM, Commodity Group, and GL Class - let rows = parseInt(table.getAttribute("data-rows")); - let cols = parseInt(table.getAttribute("data-cols")); - //iniitalize items array - let items = []; - //go through first row to find headings. - let validParams = 0; - for (let i = 1; i <= cols; i++) { - //get a cell in the table by its id - let cell = document.getElementById("1-" + i); - - //see if cell value matches any of the required parameters to create an item object - if (cell.innerHTML.toUpperCase() === 'DESCRIPTION') { - colLoc.description = i; - validParams++; - } else if (cell.innerHTML.toUpperCase() === 'UOM' || cell.innerHTML.toUpperCase() === 'ISSUE UNIT') { - colLoc.uom = i; - validParams++; - } else if (cell.innerHTML.toUpperCase() === 'COMMODITY GROUP' || cell.innerHTML.toUpperCase() === 'COMM GROUP') { - colLoc.commGroup = i; - validParams++; - } else if (cell.innerHTML.toUpperCase() === 'GL CLASS') { - colLoc.glClass = i; - validParams++; - - } else if (cell.innerHTML.toUpperCase() === 'SITEID' || cell.innerHTML.toUpperCase() === 'SITE') { - colLoc.siteID = i; - validParams++; - } else if (cell.innerHTML.toUpperCase() === 'STOREROOM' || cell.innerHTML.toUpperCase() === 'STOREROOM') { - colLoc.storeroom = i; - validParams++; - } else if (cell.innerHTML.toUpperCase() === 'VENDOR' || cell.innerHTML.toUpperCase() === 'VENDOR NUMBER') { - colLoc.vendor = i; - validParams++; - } else if (cell.innerHTML.toUpperCase() === 'CAT NUMBER' || cell.innerHTML.toUpperCase() === 'CATALOG NUMBER' || cell.innerHTML.toUpperCase() === 'CATALOGUE NUMBER') { - colLoc.catNum = i; - validParams++; - } else if (cell.innerHTML.toUpperCase() === 'MAXIMO' || cell.innerHTML.toUpperCase() === 'ITEM NUMBER') { - colLoc.maximo = i; - validParams++; - } - // console.log(validParams) - } - - //Checking if mandatory columns are filled - if (colLoc.siteID != -1 || colLoc.storeroom != -1 || colLoc.vendor != -1 || colLoc.catNum != -1) { - if (colLoc.siteID == -1 || colLoc.storeroom == -1) { - let numMissing = 0; - let missingCols = ""; - let missingColArr = []; - console.log("missing params"); - for (const property in colLoc) { - if (colLoc[property] == -1 && property != "vendor" && property != "catNum") { - console.log(property); - numMissing++; - missingColArr.push(property.toLowerCase()); - } - } - missingCols = missingColArr.join(', '); - document.getElementById("batch-upload-status-text").innerHTML = `Table is missing ${numMissing} column(s): (${missingCols}). Table will not be uploaded!`; - return; - } - } - else { - if (validParams < 5) { - let missingCols = ""; - let missingColArr = []; - console.log("missing params"); - for (const property in colLoc) { - if (colLoc[property] == -1 && property != "siteID" && property != "storeroom" && property != "vendor" && property != "catNum") { - console.log(property); - missingColArr.push(property.toLowerCase()); - } - } - missingCols = missingColArr.join(', '); - document.getElementById("batch-upload-status-text").innerHTML = `Table is missing ${5 - validParams} column(s): (${missingCols}). Table will not be uploaded!`; - return; - } - - } - let invalidItems = 0; - //Make item for request that includes inventory upload - if (validParams > 5) { - let site = undefined; - let storeroom = undefined; - let vendor = undefined; - let catNum = undefined; - for (let i = 2; i <= rows; i++) { - let desc = sanitizeString(document.getElementById(i + "-" + colLoc.description).innerHTML); - let uom = sanitizeString(document.getElementById(i + "-" + colLoc.uom).innerHTML).toUpperCase(); - let commGroup = sanitizeString(document.getElementById(i + "-" + colLoc.commGroup).innerHTML); - let glclass = sanitizeString(document.getElementById(i + "-" + colLoc.glClass).innerHTML).toUpperCase(); - if (colLoc.siteID != -1) { site = sanitizeString(document.getElementById(i + "-" + colLoc.siteID).innerHTML).toUpperCase(); } - if (colLoc.storeroom != -1) { storeroom = sanitizeString(document.getElementById(i + "-" + colLoc.storeroom).innerHTML).toUpperCase(); } - if (colLoc.vendor != -1) { vendor = sanitizeString(document.getElementById(i + "-" + colLoc.vendor).innerHTML); } - if (colLoc.catNum != -1) { catNum = sanitizeString(document.getElementById(i + "-" + colLoc.catNum).innerHTML); } - let maximo = sanitizeString(document.getElementById(i + "-" + colLoc.maximo).innerHTML); - //if all required parameters are not available, don't create the item and move to next row - if (desc == '' || uom == '' || commGroup == '' || glclass == '' || desc == 0 || uom == 0 || commGroup == 0 || glclass == 0 || site == '' || storeroom == '') { - updateItemStatus('error', (i - 1)); - items.push(''); - invalidItems++; - continue; - } - - let item = new Item(undefined, desc, uom, commGroup, glclass, site, storeroom, vendor, catNum); - if (colLoc.maximo != -1 && maximo != 0 && maximo.toString().length === 7) { - item.itemnumber = maximo; - } else if (desc.toUpperCase().includes("DWG")) { - item.series = 98; - } else if (commGroup == "490" && glclass == "PLS") { - //Change when when item num reachs 9920000 - item.series = 991; - } - // console.log(item); - //add the item to the array - items.push(item); - } - } - //Make item for request that doesn't need inventory upload - else { - for (let i = 2; i <= rows; i++) { - let desc = sanitizeString(document.getElementById(i + "-" + colLoc.description).innerHTML); - let uom = sanitizeString(document.getElementById(i + "-" + colLoc.uom).innerHTML).toUpperCase(); - let commGroup = sanitizeString(document.getElementById(i + "-" + colLoc.commGroup).innerHTML); - let glclass = sanitizeString(document.getElementById(i + "-" + colLoc.glClass).innerHTML).toUpperCase(); - let maximo = sanitizeString(document.getElementById(i + "-" + colLoc.maximo).innerHTML); - //if all required parameters are not available, don't create the item and move to next row - if (desc == '' || uom == '' || commGroup == '' || glclass == '' || desc == 0 || uom == 0 || commGroup == 0 || glclass == 0) { - updateItemStatus('error', (i - 1)); - items.push(''); - invalidItems++; - continue; - } - let item = new Item(undefined, desc, uom, commGroup, glclass); - if (colLoc.maximo != -1 && maximo != 0 && maximo.toString().length === 7) { - item.itemnumber = maximo; - } else if (desc.toUpperCase().includes("DWG")) { - item.series = 98; - } else if (commGroup == "490" && glclass == "PLS") { - //Change when when item num reachs 9920000 - item.series = 991; - } - // console.log(item); - //add the item to the array - items.push(item); - } - } - - if (invalidItems > 0) { - document.getElementById("batch-upload-status-text").innerHTML = `Warning! ${invalidItems} invalid items will not be uploaded`; - } - //return the item array - return items; -} - -/** - * Uploads an item from item information accordion dropdown (single item upload) - * - */ -async function uploadItem() { - document.getElementById("confirm-btn").innerHTML = ' Uploading...'; - document.getElementById("confirm-btn").disabled = true; - const worker = new WorkerHandler(); - let item = new Item( - sanitizeString(document.getElementById("interact-num").value), - sanitizeString(document.getElementById("maximo-desc").value), - sanitizeString(document.getElementById("uom-field").value), - sanitizeString(document.getElementById("com-group").value), - sanitizeString(document.getElementById("gl-class").value) - ); - - if (document.getElementById("long-desc").value.length > 0) { - item.longdescription = document.getElementById("long-desc").value; - } - - - - worker.work(['uploadItems', [item]], (e) => { - console.log(e); - if(e === undefined || typeof e != 'string' || e == 200) { - document.getElementById("error").innerHTML = "Upload Success"; - document.getElementById("confirm-btn").innerHTML = "Upload Item"; - document.getElementById("confirm-btn").disabled = false; - new Toast("Upload Complete!", 'bg-success'); - let itemUrl = `https://${CONSTANTS.ENV}.iko.max-it-eam.com/maximo/oslc/graphite/manage-shell/index.html?event=loadapp&value=item&additionalevent=useqbe&additionaleventvalue=itemnum=${item.itemnumber}`; - document.getElementById("error").innerHTML = `Item Upload Successful! (Click to view item) `; - document.getElementById("item-link").addEventListener('click', function (x) { - x.preventDefault(); - shell.openExternal(itemUrl); - }); - } else { - document.getElementById("error").innerHTML = "Upload Fail"; - document.getElementById("confirm-btn").innerHTML = "Upload Item"; - document.getElementById("confirm-btn").disabled = false; - //TODO: fail messages - document.getElementById("error").innerHTML = `Item Upload Failed! ${e}`; - } - }); -} -/** - * Uploads an array of items - * - * @param {Array} items - */ -async function batchUploadItems(items) { - const worker = new WorkerHandler(); - //disable clear and upload buttons while uploading items to prevent duplicate requests - let btn = document.getElementById("batch-upload-btn"); - let clearBtn = document.getElementById("clear-batch-items-btn"); - clearBtn.disabled = true; - btn.disabled = true; - - worker.work(['uploadItems', items, true], (e) => { - let finishText = `Upload Finished! ${e[2]} items uploaded, ${e[3]} items added to inventory. `; - if (e[0] == "failure") { - new Toast(`Invalid! ${e[1]}}!`); - } - clearBtn.disabled = false; - btn.disabled = false; - updateItemNums(e[0]); - let rows = parseInt(document.getElementById("batch-items-table").getAttribute("data-rows")) - 1; - let nums = ""; - for (let i = 2; i <= rows + 1; i++) { - nums += document.getElementById(`${i}-${colLoc.maximo}`).innerHTML ? (document.getElementById(`${i}-${colLoc.maximo}`).innerHTML + ",") : ""; - } - if (e[2] > 0) { - let itemUrl = `https://${CONSTANTS.ENV}.iko.max-it-eam.com/maximo/oslc/graphite/manage-shell/index.html?event=loadapp&value=item&additionalevent=useqbe&additionaleventvalue=itemnum=${nums}`; - finishText += `Click to view:` - document.getElementById("batch-upload-status-text").innerHTML = finishText; - document.getElementById("batch-link").addEventListener('click', function (e) { - e.preventDefault(); - shell.openExternal(itemUrl); - }); - } else { - document.getElementById("batch-upload-status-text").innerHTML = finishText; - } - console.log("upload finished"); - }); -} -/** - * Gets a list of newly generated item nums and updates the table with them. - * - * If an item has just been uploaded, populates item num cell with new number. - * - * @param {int[][]} arr array of pairs of item nums and table row indexes - */ -function updateItemNums(arr) { - for (const pair of arr) { - let itemNum = pair[0]; - let itemRowIndex = pair[1]; - - //update item number cell - let cell = document.getElementById(`${itemRowIndex + 1}-${colLoc.maximo}`); - cell.innerHTML = itemNum; - - //highlight the item number yellow to signify that it was newly uploaded - cell.classList.add("table-alert"); - } -} -//////////////////////// - -function skipRow() { - let row = document.getElementById("current-row").innerHTML; - interactiveGoNext(Number(row) + 1); -} - -function finishLoadingBatch(params) { - let bar = new ProgressBar(); - // this has a special work thread since initializing a worker thread takes ~700 ms which is too long - document.getElementById("valid-row").innerHTML = params[1]; - document.getElementById("total-row").innerHTML = params[2]; - const worker = new Worker('./worker.js'); - const db = new Database(); - let description = db.getDescription(params[0]); - if (description === undefined) { - bar.update(100, 'Done!'); - worker.terminate(); - new Toast('Finished Batch Processing'); - return false; - } - bar.update(0, 'Processing Descriptions'); - processBatch(worker, params[0], description); - worker.onmessage = (msg) => { - if (msg.data[0] === 'nextrow') { - description = db.getDescription(msg.data[1]); - if (description === undefined) { - params = worksheetParams(document.getElementById("worksheet-path").value); - worker.postMessage([ - 'saveProcessed', - [params, msg.data[1]] - ]); - new Toast('Finished Batch Processing'); - new Toast('Please wait for file to finish saving...'); - return false; - } - document.getElementById("current-row").innerHTML = description.row; - bar.update(msg.data[1] / params[2] * 100, `Processing Description. Row: ${msg.data[1]} of ${params[2]}`); - processBatch(worker, msg.data[1], description); - } else if (msg.data[0] === 'saveComplete') { - interactiveGoNext(msg.data[1]); - new Toast('File Saved'); - worker.terminate(); - } else { - console.log(`IDK: ${msg.data}`); - } - }; -} - -function processBatch(worker, row, description) { - const interactive = document.getElementById("modeSelect").checked; - const related = document.getElementById("relatedSelect").checked; - const translate = document.getElementById("translateSelect").checked; - const params = worksheetParams(document.getElementById("worksheet-path").value); - if (interactive) { - new Toast('Pausing / Switching to Interactive Mode'); - worker.postMessage([ - 'saveProcessed', - [params, row] - ]); - } else { - worker.postMessage([ - 'nonInteractive', - [ - related, - translate, - description.description, - document.getElementById('selected-language').value, - params, - row - ] - ]); - } - -} - -function continueAuto() { - document.getElementById("modeSelect").checked = false; - finishLoadingBatch([ - Number(document.getElementById("current-row").innerHTML), - document.getElementById("valid-row").innerHTML, - document.getElementById("total-row").innerHTML, - ]); -} - -function interactiveGoNext(row) { - let bar = new ProgressBar(); - const db = new Database(); - let description = db.getDescription(row); - if (description === undefined) { - bar.update(100, 'Done!'); - new Toast('End of File Reached'); - return false; - } - document.getElementById("current-row").innerHTML = description.row; - if (description) { - const worker = new WorkerHandler(); - document.getElementById("maximo-desc").value = description.description; - worker.work(['validSingle', description.description], showResult); - } else { - let field = document.getElementById("maximo-desc"); - field.placeholder = "Row is blank, press skip row to go next"; - field.value = ""; - let bar = new ProgressBar(); - bar.update(100, 'Done'); - } -} - -function validSingle(isExtended = false) { - let bar = new ProgressBar(); - bar.update(0, 'Starting Item Description Validation'); - let raw_desc = document.getElementById("maximo-desc").value; - const worker = new WorkerHandler(); - worker.work(['validSingle', raw_desc], (result) => { - showResult(result, isExtended) - }); -} - -function showResult(result, isExtended = false) { - let triDesc = document.getElementById('result-triple-main'); - triDesc.value = result[0][0]; - triDesc = document.getElementById('result-triple-ext1'); - triDesc.value = result[0][1]; - triDesc = document.getElementById('result-triple-ext2'); - triDesc.value = result[0][2]; - const related = document.getElementById("relatedSelect").checked; - const translate = document.getElementById("translateSelect").checked; - calcConfidence(result[0][3]); - document.getElementById("validate-badge").innerHTML = "New"; - if (translate) { - translationDescription(result[0][3]); - } - if (related) { - findRelated(result[0], isExtended); - } -} - -async function ItemAnalysis() { - const valid = new Validate(); - let raw_desc = document.getElementById("maximo-desc").value; - let result = await valid.validateSingle(raw_desc); - let triDesc = document.getElementById('result-triple-main'); - triDesc.value = result[0]; - triDesc = document.getElementById('result-triple-ext1'); - triDesc.value = result[1]; - triDesc = document.getElementById('result-triple-ext2'); - triDesc.value = result[2]; - calcConfidence(result[3]); -} - -function findRelated(result, isExtended = false) { - const worker = new WorkerHandler(); - worker.work(['findRelated', result[3], isExtended], (result) => { showRelated(result, isExtended) }); -} - -function translationDescription(description) { - // for now do not translate if english is selected - if (document.getElementById("selected-language").value != 'en') { - const worker = new WorkerHandler(); - if (document.getElementById('result-triple-ext1').value) { - description = `${document.getElementById('result-triple-main').value},${document.getElementById('result-triple-ext1').value}`; - } else { - description = document.getElementById('result-triple-main').value; - } - - worker.work([ - 'translateItem', - description, - document.getElementById('selected-language').value, - 'post' - ], displayTranslation); - } else { - new Toast('Currently translation into English is not supported'); - } - -} - -function displayTranslation(data) { - document.getElementById('trans-desc').value = data[0]; - document.getElementById('translation-description').value = `The following words do not have a translation:\n${data[1]}\nPlease check logs at bottom of page for details`; - auto_grow('translation-description'); -} - -function calcConfidence(data) { - let description; - let level = 0; - let tree = ''; - let parent = 0; - const regex = /\d+/g; - const db = new Database(); - let analysis; - let result = ''; - const option = { - style: 'percent', - minimumFractionDigits: 1, - maximumFractionDigits: 1 - }; - const formatter = new Intl.NumberFormat("en-US", option); - - if (data?.length > 0) { //test if description is blank - description = data.split(','); - for (let j = 0; j < description.length; j++) { - if (!(description[j].match(regex))) { - if (db.isManufacturer(description[j])) { - result = `${result}\n${description[j]} is confirmed as a manufacturer`; - } else { - level++; - if (level == 1) { - tree = description[j]; - } else { - tree = tree + ',' + description[j]; - } - analysis = db.getAnalysis(tree); - if (analysis) { - if (level == 1) { - if (analysis.count >= 100) { - result = `${description[j]}: is COMMONLY used as an Item Type.\n${analysis.count}: occurrences found`; - } else if (analysis.count >= 20) { - result = `${description[j]}: is SOMETIMES used as an Item Type.\n${analysis.count}: occurrences found`; - } else { - result = `WARNING: ${description[j]}: is an UNCOMMON Item Type.\nPlease double check.\n${analysis.count}: occurrences found`; - } - } else { - if (analysis.count / parent >= 0.25) { - result = `${result}\n${description[j]} is COMMONLY used as an item descriptor for ${tree}.\n${analysis.count} of ${parent} = ${formatter.format(analysis.count / parent)}`; - } else if (analysis.count / parent >= 0.05) { - result = `${result}\n${description[j]} is SOMETIMES used as an item descriptor for ${tree}.\n${analysis.count} of ${parent} = ${formatter.format(analysis.count / parent)}`; - } else { - result = `${result}\n${description[j]} is an UNCOMMON item descriptor for ${tree}.\nPlease double check.\n${analysis.count} of ${parent} = ${formatter.format(analysis.count / parent)}`; - } - } - parent = analysis.count; - } else { - result = `${result}\n${description[j]}: Does not exist in Maximo as part of: ${tree}.\nPlease Check with Corporate`; - } - } - } - } - document.getElementById('valid-description').value = result.trim(); - } else { - new Toast('Blank Description'); - } -} - -/** - * Initializes search results table and populates relatedResults object - * with search results. - * - * @param {Array< Map>,Map,String>} result array of [array of item nums of all search results, map of item nums to descriptions, and search query with words separated by commas] - * @param {bool} isExtended whether the user has clicked extended search - */ -async function showRelated(result, isExtended = false) { - let bar = new ProgressBar(); - if (!result[0]) { - bar.update(100, 'Done!'); - return false; - } - - //reverse results to get newest items first. - //technically this isn't the best way to do - //this because results aren't guaranteed - //to be in order of oldest to newest. - for (const [key, value] of Object.entries(result[0])) { - result[0][key] = result[0][key].reverse(); - } - //populate global variable with search results (bad practice, but it works) - relatedResults = { - idx: 0, - curKey: 0, - results: result, - } - - //reset table after called - const relatedTable = document.getElementById('related-table'); - const numResultsText = document.getElementById('num-results'); - - if (isExtended) { - relatedTable.classList.add(`isExt`); - } else { - if (relatedTable.classList.contains(`isExt`)) { - relatedTable.classList.remove(`isExt`); - } - } - //Add headings to the search results table - relatedTable.innerHTML = ` - - - - - - - ${(isExtended ? '' : '')} - - - - - - - -
    Percent MatchItem NumberItem DescriptionMore InfoUOMC_GroupGL_Class
    - `; - - numResultsText.innerHTML = `Found ${Object.entries(result[1]).length} results`; - - //expand the search results accordion - document.getElementById('related-items-accordion-btn').classList.remove('collapsed'); - //load a couple of items - loadRelated(); - html = new bootstrap.Collapse(document.getElementById('accordion-relatedItem'), { toggle: false }); - html.show(); - bar.update(100, 'Done!'); -} - -function loadRelated() { - //check if user clicked extended search - const isExtended = document.getElementById('related-table').classList.contains('isExt'); - - //a map with percent match as key (in decimal form) and array of items as value - //for example, if the key is 1, the list of items match with the search query 100%. If the key is 0.5, the list of items match with the search query 50%. - const scores = relatedResults.results[0]; - - //relatedResults.idx is like a bookmark. It keeps track of how many items have been loaded from the array of items associated with the current key in scores. - //relatedResults.curKey is the number of the current key that is being loaded if you were to iterate thru the keys in scores. - //For example, the first key's number would be 0, second key 1, etc. - if (relatedResults.curKey >= Object.entries(scores).length) { - //If curKey is equal or larger than the number of keys in scores, then there are no more items to load, so return - return; - } else if (Object.entries(scores)[relatedResults.curKey][1].length == 0) { - //If there are no items associated with the current key, then move to the next key and try loading items again. - relatedResults.curKey++; //increment curKey so that the next time the function runs, it will try to load items from the next key - relatedResults.idx = 0; //reset idx so that it starts from the beginning of the array - loadRelated(); - return; //return so we don't do make an infinite loop - } - - let step = 20; //number of items to load at once. - - //get arrs from results obj - const itemNames = relatedResults.results[1]; //a map with the 9-series number of the item as the key and the item info as value. Item info is an array with 4 items: [description, gl class, uom, commodity group] - const searchWords = relatedResults.results[2].split(','); //an array of the search query split by commas. For example, if the search query is "test, item, description", then searchWords would be ["test", "item", "description"] - - let html = '', color = ''; //html is the html that will be added to the search results table. color is the color of the row in the table. - - let itemDescription; - - //formatting options for percent match. Converts decimal to percent and rounds to nearest whole number. - const option = { - style: 'percent', - minimumFractionDigits: 0, - maximumFractionDigits: 0 - }; - - const formatter = new Intl.NumberFormat("en-US", option); - // technically this is bad practise since object order might not be guarenteed - // https://stackoverflow.com/questions/983267/how-to-access-the-first-property-of-a-javascript-object - - let percentMatch = Object.entries(scores)[relatedResults.curKey][0]; //get the percent match (name of the current key) - let itemNumList = Object.entries(scores)[relatedResults.curKey][1]; //get the array of items associated with key - let itemsToLoad; //array of items to load - - if (relatedResults.idx + step >= itemNumList.length) { - //if there are less than 20 items to load, load the remaining items in value and increment curKey and reset idx - //this way, the next time the function is called, the next key will be loaded instead - itemsToLoad = itemNumList.slice(relatedResults.idx, undefined); //get array of items from idx to end of array - relatedResults.curKey++; - relatedResults.idx = 0; - } else { - itemsToLoad = itemNumList.slice(relatedResults.idx, relatedResults.idx + step); - relatedResults.idx += step; - } - - // iterate thru each item in value array - for (let itemNum of itemsToLoad) { - itemDescription = itemNames[itemNum][0]; - if (itemDescription) { - //Bold all words in item description that match the search query - for (let word of searchWords) { - split = word.split(' '); - for (let smallWord of split) { - if (smallWord.length > 0) { - itemDescription = itemDescription.replace( - new RegExp(`${smallWord}`, 'i'), - `${itemDescription.match(new RegExp(`${smallWord}`, 'i'))?.[0]}` - ); - } - } - - } - //set row color based on percent match - if (percentMatch > 0.7) { - color = 'table-success'; //green - } else if (percentMatch > 0.4) { - color = 'table-warning'; //yellow - } else { - color = 'table-danger'; //red - } - - //create HTML row. - //In extended search, the vendor info is split from the item description by a | (pipe character). - //All info after the pipe character is put into another column. - //If the item description does not have a pipe character, then the second column is not loaded. - html = `${html}\n - ${formatter.format(percentMatch)} - ${itemNum} - ${(isExtended ? `${itemDescription.substring(0, itemDescription.indexOf("|"))}` : `${itemDescription}`)} - ${(isExtended ? `${itemDescription.slice(itemDescription.indexOf("|") + 1)}` : '')} - ${itemNames[itemNum][2]} - ${itemNames[itemNum][3]} - ${itemNames[itemNum][1]} - add_task`; - } else { - html = `0\nxxxxxxx\nNo Related Items Found`; - } - } - - //add html to table - const relatedTable = document.getElementById('related-items'); - relatedTable.innerHTML += html; - - //if less than 5 items loaded, load more - if (itemsToLoad.length < 5) { - document.getElementById("everything").dispatchEvent(new Event('scroll')); - } -} - -//unused function (was used to copy item validation): probably remove this -function copyResult(copy) { - if (copy === 'single') { - let content = document.getElementById('result-single').innerText; - clipboard.writeText(content); - new Toast('Single Description Copied to Clipboard!'); - } else { - let desc = []; - let content = ''; - content = document.getElementById('result-triple-main').innerText; - desc.push(content); - content = document.getElementById('result-triple-ext1').innerText; - desc.push(content); - content = document.getElementById('result-triple-ext2').innerText; - desc.push(content); - clipboard.write({ - text: document.getElementById('result-single').innerText, - html: `
    ${desc[0]}${desc[1]}${desc[2]}
    `, - }); - new Toast('Triple Description Copied to Clipboard!'); - } -} diff --git a/assets/asset_translation/asset_translation_excel.js b/src/asset_translation/asset_translation_excel.js similarity index 100% rename from assets/asset_translation/asset_translation_excel.js rename to src/asset_translation/asset_translation_excel.js diff --git a/assets/asset_translation/asset_translation_main.js b/src/asset_translation/asset_translation_main.js similarity index 100% rename from assets/asset_translation/asset_translation_main.js rename to src/asset_translation/asset_translation_main.js diff --git a/assets/asset_translation/asset_translation_sqlite.js b/src/asset_translation/asset_translation_sqlite.js similarity index 100% rename from assets/asset_translation/asset_translation_sqlite.js rename to src/asset_translation/asset_translation_sqlite.js diff --git a/assets/item_translation/item-translation-sqlite.js b/src/item_translation/item-translation-sqlite.js similarity index 100% rename from assets/item_translation/item-translation-sqlite.js rename to src/item_translation/item-translation-sqlite.js diff --git a/assets/item_translation/item-translation.js b/src/item_translation/item-translation.js similarity index 99% rename from assets/item_translation/item-translation.js rename to src/item_translation/item-translation.js index faef978..c585c92 100644 --- a/assets/item_translation/item-translation.js +++ b/src/item_translation/item-translation.js @@ -1,5 +1,5 @@ const TransDB = require('./item-translation-sqlite'); -const Database = require('../indexDB'); +const Database = require('../misc/indexDB'); class TranslateDescription { contextTranslate(description, lang_code, result) { diff --git a/assets/autoupdater.js b/src/misc/autoupdater.js similarity index 100% rename from assets/autoupdater.js rename to src/misc/autoupdater.js diff --git a/assets/better-sqlite.js b/src/misc/better-sqlite.js similarity index 100% rename from assets/better-sqlite.js rename to src/misc/better-sqlite.js diff --git a/assets/common.js b/src/misc/common.js similarity index 100% rename from assets/common.js rename to src/misc/common.js diff --git a/assets/constants.js b/src/misc/constants.js similarity index 100% rename from assets/constants.js rename to src/misc/constants.js diff --git a/assets/exceljs.js b/src/misc/exceljs.js similarity index 100% rename from assets/exceljs.js rename to src/misc/exceljs.js diff --git a/assets/indexDB.js b/src/misc/indexDB.js similarity index 99% rename from assets/indexDB.js rename to src/misc/indexDB.js index 4fb8251..b6ae8ce 100644 --- a/assets/indexDB.js +++ b/src/misc/indexDB.js @@ -1,5 +1,5 @@ const Sql = require('better-sqlite3'); -const utils = require('../assets/utils'); +const utils = require('./utils'); const intersection = require('lodash/intersection'); // https://lodash.com/docs/4.17.15#intersection // fast library for intersection of arrays diff --git a/assets/maximo.js b/src/misc/maximo.js similarity index 99% rename from assets/maximo.js rename to src/misc/maximo.js index 56d1234..21152cb 100644 --- a/assets/maximo.js +++ b/src/misc/maximo.js @@ -1,6 +1,6 @@ // various functions for fetching data from maximo rest api -const SharedDatabase = require('../assets/sharedDB'); -const CONSTANTS = require('../assets/constants.js'); +const SharedDatabase = require('./sharedDB'); +const CONSTANTS = require('./constants.js'); /** @@ -313,11 +313,9 @@ class Maximo { }); const content = await response.json(); const statuscode = response.status; - if(statuscode == 200) { - + if (statuscode == 200) { return parseInt(content.validdoc); - } - else { + } else { throw new Error(parseInt(statuscode)); } } diff --git a/assets/sharedDB.js b/src/misc/sharedDB.js similarity index 100% rename from assets/sharedDB.js rename to src/misc/sharedDB.js diff --git a/assets/spreadsheet.js b/src/misc/spreadsheet.js similarity index 100% rename from assets/spreadsheet.js rename to src/misc/spreadsheet.js diff --git a/assets/utils.js b/src/misc/utils.js similarity index 100% rename from assets/utils.js rename to src/misc/utils.js diff --git a/assets/validators.js b/src/misc/validators.js similarity index 100% rename from assets/validators.js rename to src/misc/validators.js diff --git a/renderer/asset_translation.html b/src/renderer/asset_translation.html similarity index 98% rename from renderer/asset_translation.html rename to src/renderer/asset_translation.html index eddab36..1ca4635 100644 --- a/renderer/asset_translation.html +++ b/src/renderer/asset_translation.html @@ -7,7 +7,7 @@ EAM Spare Parts - + diff --git a/renderer/asset_translation.js b/src/renderer/asset_translation.js similarity index 100% rename from renderer/asset_translation.js rename to src/renderer/asset_translation.js diff --git a/renderer/item_main.html b/src/renderer/item_main.html similarity index 99% rename from renderer/item_main.html rename to src/renderer/item_main.html index 8a9b061..1df23de 100644 --- a/renderer/item_main.html +++ b/src/renderer/item_main.html @@ -7,11 +7,11 @@ - + EAM Spare Parts - - + + @@ -352,7 +352,7 @@
    -
    +

    Item Standardizer

    diff --git a/src/renderer/item_main.js b/src/renderer/item_main.js new file mode 100644 index 0000000..321bb19 --- /dev/null +++ b/src/renderer/item_main.js @@ -0,0 +1,1421 @@ +const {clipboard, ipcRenderer, shell} = require('electron'); +const fs = require('fs'); +const path = require('path'); +// const { dialog } = require('electron').remote; +const Database = require('../misc/indexDB'); +const SharedDatabase = require('../misc/sharedDB'); +const Validate = require('../misc/validators'); +const Maximo = require('../misc/maximo'); +const CONSTANTS = require('../misc/constants.js'); +// stores items that are to be uploaded through the "batch upload" accordion. +let itemsToUpload = []; + +// stores images that are to be uploaded through the "image upload" accordion. +let imgsToUpload = []; + +// an object that stores the location of each column in the batch upload table. +// allows for column locations to be interchanged. -1 means a column is not in +// the table. Maybe in the future, column locations should be predetermined so +// that a global variable is not used for this. +let colLoc = { + description: -1, + uom: -1, + commGroup: -1, + glClass: -1, + maximo: -1, + vendor: -1, + storeroom: -1, + catNum: -1, + siteID: -1, +}; + +// an object that stores all search results and "bookmarks" how many items have +// been loaded in the related results table. Used for infinite scroll. +let relatedResults = { + idx: 0, // store the index of the current item being loaded + curKey: 0, // store which key is currently being loaded from the search results + results: [], // store all search results (dictonary) +}; + +// a function that is called immediately after the window has been loaded +window.onload = function() { + // set the darkmode toggle to the correct position by retreiving information from the local storage + document.getElementById('dark-mode-switch').checked = (localStorage.getItem('theme') === 'dark' ? true : false); + + // change the UI based on whether the user is a "power user". show all upload elements if they are a power user, else hide it. + if (localStorage.getItem('powerUser') === 'true') { + document.getElementById('upload-btn').style.display = 'block'; + document.getElementById('request-btn').style.display = 'none'; + document.getElementById('batch-upld-btn').style.display = 'block'; + document.getElementById('img-upld-toggle').style.display = 'block'; + document.getElementById('batch-mode-toggle').style.display = 'block'; + return; + } +}; + +/* +Power User Toggle + +Allows user to toggle between 2 modes: Power user and Normal User. +Normal user mode hides all upload elements and only allows the user to request items. +Power user mode shows all upload elements and allows the user to upload items and images. + +Created for the purpose of hiding upload functionality from people who shouldn't be +uploading items (a.k.a. everyone except for reliability team). +*/ +// set the user to a power user if they have clicked the secret button 5 times +document.getElementById('secret-button').addEventListener('click', (e) => { + let isPowerUser = false; + let numClicks = parseInt(e.target.getAttribute('data-clicks')); + + numClicks++; + + if (numClicks === 5) { + isPowerUser = true; + localStorage.setItem('powerUser', 'true'); + e.target.setAttribute('data-clicks', '0'); + } else { + localStorage.setItem('powerUser', 'false'); + e.target.setAttribute('data-clicks', `${numClicks}`); + isPowerUser = false; + } + + // toggle whether elements are hidden or not based off of power user status + if (isPowerUser == true) { + document.getElementById('upload-btn').style.display = 'block'; + document.getElementById('request-btn').style.display = 'none'; + document.getElementById('batch-upld-btn').style.display = 'block'; + document.getElementById('img-upld-toggle').style.display = 'block'; + document.getElementById('batch-mode-toggle').style.display = 'block'; + } else { + document.getElementById('upload-btn').style.display = 'none'; + document.getElementById('request-btn').style.display = 'block'; + document.getElementById('batch-upld-btn').style.display = 'none'; + document.getElementById('img-upld-toggle').style.display = 'none'; + document.getElementById('batch-mode-toggle').style.display = 'none'; + } +}); + +// gets user site information +async function getSite(credentials = {}) { + const maximo = new Maximo(); + const currInfo = await maximo.checkLogin(credentials?.userid, credentials?.password); + return currInfo.siteID; +} + +// open a modal that allows you to make an item request +document.getElementById('request-btn').addEventListener('click', () => { + // show request item modal + const requestModal = new bootstrap.Modal(document.getElementById('requestModal')); + requestModal.toggle(); + + const currPass = new SharedDatabase().getPassword(); + const userid = currPass.userid; + let siteID; + + const sites = { + 'AA': ['AAG: Brampton B2 Storeroom', 'AAL: Brampton B2/B4 Maintenance Storeroom', 'AAO: Brampton B4 Oxidizer Storeroom'], + 'ANT': ['AN1: Antwerp Mod Line Storeroom', 'AN2: Antwerp Coating Line Storeroom'], + 'BA': ['BAL: IKO Calgary Maintenance Storeroom'], + 'BL': ['BLC: Hagerstown TPO Storeroom', 'BLD: Hagerstown ISO Storeroom', 'BLL: Hagerstown Maintenance Storeroom(Shared)'], + 'CA': ['CAL">IKO Kankakee Maintenance Storeroom'], + 'CAM': ['C61">IKO Appley Bridge Maintenance Storeroom'], + 'COM': ['CB1">Combronde Maintenance Storeroom'], + 'GA': ['GAL: IKO Wilmington Maintenance Storeroom'], + 'GC': ['GCL: Sumas Maintenance Storeroom', 'GCA: Sumas Shipping Storeroom', 'GCD: Sumas Shingle Storeroom', 'GCG: Sumas Mod Line Storeroom', 'GCJ: Sumas Crusher Storeroom', 'GCK: Sumas Tank Farm Storeroom'], + 'GE': ['GEL: Ashcroft Maintenance Storeroom'], + 'GH': ['GHL: IKO Hawkesbury Maintenance Storeroom'], + 'GI': ['GIL: IKO Madoc Maintenance Storeroom'], + 'GJ': ['GJL: CRC Toronto Maintenance Storeroom'], + 'GK': ['GKA: IG Brampton B7 and B8 Storeroom', 'GKC: IG Brampton B6 and Laminator Storeroom', 'GKL: IG Brampton Maintenance Storeroom'], + 'GM': ['GML: IG High River Maintenance Storeroom'], + 'GP': ['GPL: CRC Brampton Maintenance Storeroom'], + 'GR': ['GRL: Bramcal Maintenance Storeroom'], + 'GS': ['GSL: Sylacauga Maintenance Storeroom'], + 'GV': ['GVL: IKO Hillsboro Maintenance Storeroom'], + 'GX': ['GXL: Maxi-Mix Maintenance Storeroom'], + 'KLU': ['KD1: IKO Klundert Maintenance Storeroom', 'KD2: IKO Klundert Lab Storeroom', 'KD3: IKO Klundert Logistics Storeroom'], + 'PBM': ['PB6: Slovakia Maintenance Storeroom'], + 'RAM': ['RA6: IKO Alconbury Maintenance Storeroom'], + // Add more sites and storerooms as needed... + }; + + const userSite = getSite({userid: userid, password: currPass.password}); + userSite.then((response) => { + siteID = response; + + const storeroomSelect = document.getElementById('storeroom'); + // poppulate correct user storerooms in modal + function updateStoreroomOptions() { + storeroomSelect.options.length = 1; + + // Add new options + const neededStorerooms = sites[siteID]; + for (const storeroom of neededStorerooms) { + const option = document.createElement('option'); + option.value = storeroom; + option.text = storeroom; + storeroomSelect.add(option); + } + } + updateStoreroomOptions(); + }) + .catch((error) => console.error(`Error: ${error}`)); + + poppulateModal(); +}); + +// Allow input of manufacturer name & part number if "Other" is selected +document.getElementById('manu-name').addEventListener('click', (e) => { + if (e.target.value == 'Other') { + document.getElementById('pref-manu').style.display = 'block'; + document.getElementById('part-form').style.display = 'block'; + } else { + document.getElementById('pref-manu').style.display = 'none'; + document.getElementById('part-form').style.display = 'none'; + } +}); + +// download email file when submit button is pressed +document.getElementById('submit-btn').addEventListener('click', submitMail, false); + +// opens email file in default mail client +function submitMail() { + // checking required fields are filled + if (document.getElementById('manu-name').value == 'Other') { + if (!(document.getElementById('part-num').reportValidity() && + document.getElementById('storeroom').reportValidity() && + document.getElementById('item-descr').reportValidity())) { + console.log('Required fields still empty'); + return; + } + } else { + if (!(document.getElementById('storeroom').reportValidity() && + document.getElementById('item-descr').reportValidity())) { + console.log('Required fields still empty'); + return; + } + } + // storing current date and time for email subject + const currentdate = new Date(); + const datetime = currentdate.getFullYear() + '/' + (currentdate.getMonth() + 1) + '/' + (currentdate.getDay() + 1) + + ' @ ' + + currentdate.getHours() + ':' + + currentdate.getMinutes() + ':' + currentdate.getSeconds(); + const mailText = + ``; + + // Send string to main process to write file + ipcRenderer.send('write-file', mailText); + // requestModal.toggle(); +} + +/* Infinite scroll + +Allows elements to load as the user scrolls down the page, +drastically decreasing loading times and making UI smoother. +*/ +// listen for a scroll event. if the bottom of the results table is less than 100px below the bottom of the viewport, load more items +document.getElementById('everything').addEventListener('scroll', () => { + // dont add items to the list if the accordion is collapsed + if (document.getElementById('related-items-accordion-btn').classList.contains('collapsed') || relatedResults.results.length == 0) { + return; + } + + const searchResultsTable = document.getElementById('related-items'); + + const domRect = searchResultsTable.getBoundingClientRect(); + const spaceBelow = document.getElementById('everything').offsetHeight - domRect.bottom; + + if (spaceBelow > -100) { + // load more items if the bottom of the table is less than 100px below the bottom of the viewport + loadRelated(); + } +}); + +// Generate UI when files are selected by user +document.getElementById('imgInput').addEventListener('change', async (e) => { + const progressBar = new ProgressBar(); + + // reset UI + document.getElementById('imgList').innerHTML = ``; + // get files from file picker + const files = document.getElementById('imgInput').files; + imgsToUpload = files; + // make a comma separated string of all the item numbers that are to be uploaded + let nums = ''; + + // if no files were selected, return + if (files.length == 0 || !files) { + return; + } + + const imgList = document.getElementById('imgList'); + + progressBar.update(0, 'Loading Images...'); + + // for each image, add list item to HTML list + for (let i = 0; i < files.length; i++) { + const file = files[i]; + const completion = (i + 1) / files.length * 100; + + nums += file.name.slice(0, 7) + ','; // get first 7 characters of file name + progressBar.updateProgressBar(completion); + + imgList.innerHTML += ` +
  • +
    + +

    ${file.name.slice(0, 7)}

    +
    + pending +
  • `; + } + + // generate a link to open items that are being uploaded to in maximo + + const url = `https://${CONSTANTS.ENV}.iko.max-it-eam.com/maximo/oslc/graphite/manage-shell/index.html?event=loadapp&value=item&additionalevent=useqbe&additionaleventvalue=itemnum=${nums}`; + document.getElementById('img-upload-status-text').innerHTML = `Selected Items:`; + document.getElementById('imgs-link').addEventListener('click', function(e) { + e.preventDefault(); + shell.openExternal(url); + }); + + progressBar.update(100, 'Ready to Upload!'); +}); + +// clear the file picker each time it is clicked +document.getElementById('imgInput').addEventListener('click', () => { + document.getElementById('img-clear-btn').dispatchEvent(new Event('click')); +}); + +document.getElementById('img-clear-btn').addEventListener('click', () => { + // reset all related components + const progressBar = new ProgressBar(); + document.getElementById('imgList').innerHTML = ``; + progressBar.update(100, 'Ready!'); + // empty list of images to upload + imgsToUpload = []; + // empty file picker + document.getElementById('imgInput').value = null; + + document.getElementById('img-upload-status-text').innerHTML = 'Select Images to Continue...'; +}); +document.getElementById('img-upload-btn').addEventListener('click', () => { + const progressBar = new ProgressBar(); + + const clearBtn = document.getElementById('img-clear-btn'); + const uploadBtn = document.getElementById('img-upload-btn'); + + // return if user has picked no images + if (imgsToUpload.length == 0) { + new Toast('No Images Selected!'); + return; + } + + let finishedItems = 0; + + // disable the clear and upload buttons while upload is taking place so the + // user can't send duplicate requests or accidentally clear the image upload list + // while its uploading + clearBtn.disabled = true; + uploadBtn.disabled = true; + + const worker = new WorkerHandler(); + + progressBar.update(0, 'Uploading Images...'); + + // upload all images and update UI + worker.work(['uploadImages', imgsToUpload], (result) => { + if (result[0] == 'success') { + // if success, display checkmark + document.getElementById(`img-${result[1]}-status`).innerHTML = `done`; + } else if (result[0] == 'fail') { + // if fail, display 'x' (cross) + document.getElementById(`img-${result[1]}-status`).innerHTML = `close`; + } else if (result[0] == 'done') { + progressBar.update(100, 'Upload Complete!'); + clearBtn.disabled = false; + uploadBtn.disabled = false; + } else if (result[0] == 'warning') { + // if warning, display triangle with exclamation point in it. This only occurs if you try + // to upload an image to an item that already has an image + document.getElementById(`img-${result[1]}-status`).innerHTML = `warning`; + } else if (result[0] == 'total failure') { + finishedItems = imgsToUpload.length; + progressBar.update(100, 'Error occurred while attempting upload!'); + document.getElementById('img-upload-status-text').innerHTML = `Upload Failed: ${result[1]}}`; + clearBtn.disabled = false; + uploadBtn.disabled = false; + } + + if (result != 'done') { + finishedItems++; + } + + // update progressbar when each image is uploaded/fails upload + progressBar.updateProgressBar(finishedItems * 100 / imgsToUpload.length); + }); +}); + +// Other +document.getElementById('load-item').addEventListener('click', loadItem); +document.getElementById('valid-single').addEventListener('click', () => { + validSingle(); +}); +document.getElementById('valid-single-ext').addEventListener('click', () => { + validSingle(true); +}); +document.getElementById('settings').addEventListener('click', openSettings); +document.getElementById('topButton').addEventListener('click', toTop); +document.getElementById('endButton').addEventListener('click', toEnd); +document.getElementById('interactive').addEventListener('click', openExcel); +document.getElementById('worksheet-path').addEventListener('click', openExcel); +document.getElementById('pauseAuto').addEventListener('click', pauseAuto); + +document.getElementById('save-desc').addEventListener('click', writeDescription); +document.getElementById('save-num').addEventListener('click', writeItemNum); +document.getElementById('skip-row').addEventListener('click', skipRow); +document.getElementById('continueAuto').addEventListener('click', continueAuto); +document.getElementById('confirm-btn').addEventListener('click', () => { + uploadItem(); +}); +document.getElementById('upload-btn').addEventListener('click', () => { + const confirmModal = new bootstrap.Modal(document.getElementById('confirmModal')); + if (!( + document.getElementById('maximo-desc').reportValidity() && + document.getElementById('uom-field').reportValidity() && + document.getElementById('com-group').reportValidity() && + document.getElementById('gl-class').reportValidity() + )) { + return; + } + ItemAnalysis(); + confirmModal.toggle(); + getNextNumThenUpdate(document.getElementById('num-type').value); +}); + + +// batch upload: +document.getElementById('openBatchFile').addEventListener('click', () => { + openFile('worksheet-path'); +}); + +document.getElementById('clear-batch-items-btn').addEventListener('click', () => { + document.getElementById('batch-items-table').innerHTML = ``; + document.getElementById('batch-copy-nums').disabled = true; + document.getElementById('batch-upload-status-text').innerHTML = 'Waiting for paste...'; +}); + +document.getElementById('batch-copy-nums').addEventListener('click', () => { + try { + const result = getItemsFromTable('batch-items-table'); + if (result == undefined || result == null || result == 0) { + throw ('Table missing columns'); + } + const rows = parseInt(document.getElementById('batch-items-table').getAttribute('data-rows')) - 1; + let nums = ''; + for (let i = 2; i <= rows + 1; i++) { + nums += document.getElementById(`${i}-${colLoc.maximo}`).innerHTML ? (document.getElementById(`${i}-${colLoc.maximo}`).innerHTML + '\n') : ''; + } + navigator.clipboard.writeText(nums); + new Toast('Item Numbers Copied to Clipboard!'); + } catch (error) { + // console.log(error); + new Toast('Unable to copy numbers, please check table formatting!'); + } +}); + +document.getElementById('batch-items-textinput').addEventListener('paste', (e) => { + setTimeout(() => { + const paste = e.target.value; + const table = document.getElementById('batch-items-table-div'); + table.innerHTML = convertToTable(paste, 'batch-items-table'); + + document.getElementById('batch-copy-nums').disabled = false; + + document.getElementById('batch-upload-status-text').innerHTML = 'Paste detected! Edit table if needed and click upload.'; + e.target.value = ''; + }, 0); +}); +document.getElementById('batch-upload-btn').addEventListener('click', () => { + try { + itemsToUpload = getItemsFromTable('batch-items-table'); + } catch (error) { + itemsToUpload = []; + document.getElementById('batch-upload-status-text').innerHTML = `Error, check table format! (${error})`; + return; + } + + if (itemsToUpload.length > 0) { + itemsToUpload.forEach((value, idx) => { + if (value) { + updateItemStatus('loading', idx + 1); + } + }); + batchUploadItems(itemsToUpload); + return; + } else { + document.getElementById('batch-upload-status-text').innerHTML = 'No valid items to upload!'; + } + + return; +}); +document.getElementById('batch-paste-btn').addEventListener('click', async () => { + const text = await navigator.clipboard.readText(); + const pasteEvent = new Event('paste', {'bubbles': true, 'cancelable': false}); + const textinput = document.getElementById('batch-items-textinput'); + + textinput.value = text; + textinput.dispatchEvent(pasteEvent); +}); +document.getElementById('batch-copy-headers-btn').addEventListener('click', () => { + const copyText = `Maximo\tDescription\tIssue Unit\tCommodity Group\tGL Class\tSite\tStoreroom\tVendor\tCatalogue Number\n\t`; + navigator.clipboard.writeText(copyText); + new Toast('Table copied to clipboard!'); +}); +// dark theme toggle +document.getElementById('dark-mode-switch').addEventListener('click', toggleTheme); +// Infinite scroll + +// listener for enter key on search field +document.getElementById('maximo-desc').addEventListener('keyup', function(event) { + // Number 13 is the "Enter" key on the keyboard + if (event.key === 'Enter') { + // Cancel the default action, if needed + event.preventDefault(); + // Trigger the button element with a click + validSingle(); + } +}); + +document.getElementById('interact-num').addEventListener('keyup', function(event) { + // Number 13 is the "Enter" key on the keyboard + if (event.key === 'Enter') { + // Cancel the default action, if needed + event.preventDefault(); + // Trigger the button element with a click + loadItem(); + } +}); + +function pauseAuto() { + document.getElementById('modeSelect').checked = true; +} + +function loadItem() { + const itemnum = document.getElementById('interact-num').value.trim(); + new Toast(`Loading Item: ${itemnum}`); + const worker = new WorkerHandler(); + worker.work(['loadItem', itemnum], showItem); +} + +function auto_grow(elementID) { + const element = document.getElementById(elementID); + element.style.height = '5px'; + element.style.height = (element.scrollHeight) + 'px'; +} + +function showItem(data) { + document.getElementById('maximo-desc').value = data[0].description; + document.getElementById('uom-field').value = data[0].uom; + document.getElementById('com-group').value = data[0].commodity_group; + document.getElementById('gl-class').value = data[0].gl_class; +} + +function writeDescription() { + const valid = new Validate(); + const field = document.getElementById('maximo-desc'); + if (field.value.length > 0) { + const bar = new ProgressBar(); + bar.update(0, 'Writing asset description to file'); + let desc = field.value.split(','); + desc = valid.assembleDescription(desc); + const params = worksheetParams(); + params.outRow = document.getElementById('current-row').innerHTML; + const worker = new WorkerHandler(); + worker.work(['writeDesc', [params, desc]], writeComplete); + } else { + new Toast('Please enter a valid description'); + } +} + +function worksheetParams(path = false) { + const params = { + // input parameters + wsName: document.getElementById('ws-name').value || 'Sheet2', // name of ws + inDesc: (document.getElementById('input-col').value || 'F').toUpperCase().split(','), // description columns for input + startRow: document.getElementById('start-row').value || '2', // starting row of ws + // output parameters + outItemNum: document.getElementById('output-col').value.toUpperCase() || 'E', + outItemDesc: (document.getElementById('output-col-desc').value || 'F,G,H').toUpperCase().split(','), + outComm: document.getElementById('interact-num').value.toUpperCase() || 'I', // commodity group out + outGL: document.getElementById('interact-num').value.toUpperCase() || 'J', // gl class out + outUOM: document.getElementById('interact-num').value.toUpperCase() || 'K', // uom out + outQuestion: document.getElementById('interact-num').value.toUpperCase() || 'L', // questions out + outTranslate: document.getElementById('output-col-translation').value.toUpperCase() || 'L', + outMissing: document.getElementById('output-col-missing').value.toUpperCase() || 'K', + // output data + itemNum: document.getElementById('interact-num').value || '999TEST', + itemDesc: document.getElementById('maximo-desc').value || 'TEST,ITEM,DESCRIPTION', + commGroup: document.getElementById('com-group').value || '401', // commodity group in + glClass: document.getElementById('gl-class').value || '6200000000000', // gl class in + uom: document.getElementById('uom-field').value || 'EA', // uom in + }; + if (path) { + params.filePath = path; + } else { + params.filePath = document.getElementById('worksheet-path').value; + } + return params; +} + +function writeItemNum() { + const num = document.getElementById('interact-num').value; + if (num.length > 0) { + const bar = new ProgressBar(); + bar.update(0, 'Writing item number to file'); + const path = document.getElementById('worksheet-path').value; + const wsName = document.getElementById('ws-name').value; + const rowNum = document.getElementById('current-row').innerHTML; + const cols = document.getElementById('output-col').value; + const worker = new WorkerHandler(); + worker.work(['writeNum', [path, wsName, rowNum, cols, num]], writeComplete); + } else { + new Toast('Please enter a valid item number'); + } +} + +function writeComplete() { + const rowNum = parseInt(document.getElementById('current-row').innerHTML); + new Toast(`Row ${rowNum} saved!`); + document.getElementById('interact-num').value = ''; + interactiveGoNext(Number(rowNum) + 1); +} + +function openFile(pathElement) { + const validFile = document.getElementById(pathElement); + const filePath = validFile.value; + if (filePath !== 'No file chosen') { + new Toast('Opening File in Excel!'); + shell.openExternal(filePath); + } +} + +// Deprecated function, unused. +function openSettings() { + ipcRenderer.send('openSettings'); + // sendsync blocks parent window... + // https://github.com/electron/electron/issues/10426 +} + +function openExcel() { + document.getElementById('input-col').value = document.getElementById('input-col').value.toUpperCase(); + document.getElementById('output-col').value = document.getElementById('output-col').value.toUpperCase(); + + ipcRenderer.invoke('select-to-be-translated', 'finished').then((result) => { + if (!result.canceled) { + const worker = new WorkerHandler(); + const params = worksheetParams(result.filePaths[0]); + worker.work(['interactive', params], finishLoadingBatch); + document.getElementById('worksheet-path').value = result.filePaths[0]; + } else { + new Toast('File Picker Cancelled'); + } + }); +} + +// BATCH UPLOAD FUNCTIONS +/** + * Reads a table and generates items from it + * + * @param {string} tableId the HTML id of the table to read + * @return {Array} an array of Items + */ +function getItemsFromTable(tableId) { + colLoc = { + description: -1, + uom: -1, + commGroup: -1, + glClass: -1, + maximo: -1, + vendor: -1, + storeroom: -1, + catNum: -1, + siteID: -1, + }; + + const table = document.getElementById(`${tableId}`); + // find Description, UOM, Commodity Group, and GL Class + const rows = parseInt(table.getAttribute('data-rows')); + const cols = parseInt(table.getAttribute('data-cols')); + // iniitalize items array + const items = []; + // go through first row to find headings. + let validParams = 0; + for (let i = 1; i <= cols; i++) { + // get a cell in the table by its id + const cell = document.getElementById('1-' + i); + + // see if cell value matches any of the required parameters to create an item object + if (cell.innerHTML.toUpperCase() === 'DESCRIPTION') { + colLoc.description = i; + validParams++; + } else if (cell.innerHTML.toUpperCase() === 'UOM' || cell.innerHTML.toUpperCase() === 'ISSUE UNIT') { + colLoc.uom = i; + validParams++; + } else if (cell.innerHTML.toUpperCase() === 'COMMODITY GROUP' || cell.innerHTML.toUpperCase() === 'COMM GROUP') { + colLoc.commGroup = i; + validParams++; + } else if (cell.innerHTML.toUpperCase() === 'GL CLASS') { + colLoc.glClass = i; + validParams++; + } else if (cell.innerHTML.toUpperCase() === 'SITEID' || cell.innerHTML.toUpperCase() === 'SITE') { + colLoc.siteID = i; + validParams++; + } else if (cell.innerHTML.toUpperCase() === 'STOREROOM' || cell.innerHTML.toUpperCase() === 'STOREROOM') { + colLoc.storeroom = i; + validParams++; + } else if (cell.innerHTML.toUpperCase() === 'VENDOR' || cell.innerHTML.toUpperCase() === 'VENDOR NUMBER') { + colLoc.vendor = i; + validParams++; + } else if (cell.innerHTML.toUpperCase() === 'CAT NUMBER' || cell.innerHTML.toUpperCase() === 'CATALOG NUMBER' || cell.innerHTML.toUpperCase() === 'CATALOGUE NUMBER') { + colLoc.catNum = i; + validParams++; + } else if (cell.innerHTML.toUpperCase() === 'MAXIMO' || cell.innerHTML.toUpperCase() === 'ITEM NUMBER') { + colLoc.maximo = i; + validParams++; + } + // console.log(validParams) + } + + // Checking if mandatory columns are filled + if (colLoc.siteID != -1 || colLoc.storeroom != -1 || colLoc.vendor != -1 || colLoc.catNum != -1) { + if (colLoc.siteID == -1 || colLoc.storeroom == -1) { + let numMissing = 0; + let missingCols = ''; + const missingColArr = []; + console.log('missing params'); + for (const property in colLoc) { + if (colLoc[property] == -1 && property != 'vendor' && property != 'catNum') { + console.log(property); + numMissing++; + missingColArr.push(property.toLowerCase()); + } + } + missingCols = missingColArr.join(', '); + document.getElementById('batch-upload-status-text').innerHTML = `Table is missing ${numMissing} column(s): (${missingCols}). Table will not be uploaded!`; + return; + } + } else { + if (validParams < 5) { + let missingCols = ''; + const missingColArr = []; + console.log('missing params'); + for (const property in colLoc) { + if (colLoc[property] == -1 && property != 'siteID' && property != 'storeroom' && property != 'vendor' && property != 'catNum') { + console.log(property); + missingColArr.push(property.toLowerCase()); + } + } + missingCols = missingColArr.join(', '); + document.getElementById('batch-upload-status-text').innerHTML = `Table is missing ${5 - validParams} column(s): (${missingCols}). Table will not be uploaded!`; + return; + } + } + let invalidItems = 0; + // Make item for request that includes inventory upload + if (validParams > 5) { + let site = undefined; + let storeroom = undefined; + let vendor = undefined; + let catNum = undefined; + for (let i = 2; i <= rows; i++) { + const desc = sanitizeString(document.getElementById(i + '-' + colLoc.description).innerHTML); + const uom = sanitizeString(document.getElementById(i + '-' + colLoc.uom).innerHTML).toUpperCase(); + const commGroup = sanitizeString(document.getElementById(i + '-' + colLoc.commGroup).innerHTML); + const glclass = sanitizeString(document.getElementById(i + '-' + colLoc.glClass).innerHTML).toUpperCase(); + if (colLoc.siteID != -1) { + site = sanitizeString(document.getElementById(i + '-' + colLoc.siteID).innerHTML).toUpperCase(); + } + if (colLoc.storeroom != -1) { + storeroom = sanitizeString(document.getElementById(i + '-' + colLoc.storeroom).innerHTML).toUpperCase(); + } + if (colLoc.vendor != -1) { + vendor = sanitizeString(document.getElementById(i + '-' + colLoc.vendor).innerHTML); + } + if (colLoc.catNum != -1) { + catNum = sanitizeString(document.getElementById(i + '-' + colLoc.catNum).innerHTML); + } + const maximo = sanitizeString(document.getElementById(i + '-' + colLoc.maximo).innerHTML); + // if all required parameters are not available, don't create the item and move to next row + if (desc == '' || uom == '' || commGroup == '' || glclass == '' || desc == 0 || uom == 0 || commGroup == 0 || glclass == 0 || site == '' || storeroom == '') { + updateItemStatus('error', (i - 1)); + items.push(''); + invalidItems++; + continue; + } + + const item = new Item(undefined, desc, uom, commGroup, glclass, site, storeroom, vendor, catNum); + if (colLoc.maximo != -1 && maximo != 0 && maximo.toString().length === 7) { + item.itemnumber = maximo; + } else if (desc.toUpperCase().includes('DWG')) { + item.series = 98; + } else if (commGroup == '490' && glclass == 'PLS') { + // Change when when item num reachs 9920000 + item.series = 991; + } + // console.log(item); + // add the item to the array + items.push(item); + } + } + // Make item for request that doesn't need inventory upload + else { + for (let i = 2; i <= rows; i++) { + const desc = sanitizeString(document.getElementById(i + '-' + colLoc.description).innerHTML); + const uom = sanitizeString(document.getElementById(i + '-' + colLoc.uom).innerHTML).toUpperCase(); + const commGroup = sanitizeString(document.getElementById(i + '-' + colLoc.commGroup).innerHTML); + const glclass = sanitizeString(document.getElementById(i + '-' + colLoc.glClass).innerHTML).toUpperCase(); + const maximo = sanitizeString(document.getElementById(i + '-' + colLoc.maximo).innerHTML); + // if all required parameters are not available, don't create the item and move to next row + if (desc == '' || uom == '' || commGroup == '' || glclass == '' || desc == 0 || uom == 0 || commGroup == 0 || glclass == 0) { + updateItemStatus('error', (i - 1)); + items.push(''); + invalidItems++; + continue; + } + const item = new Item(undefined, desc, uom, commGroup, glclass); + if (colLoc.maximo != -1 && maximo != 0 && maximo.toString().length === 7) { + item.itemnumber = maximo; + } else if (desc.toUpperCase().includes('DWG')) { + item.series = 98; + } else if (commGroup == '490' && glclass == 'PLS') { + // Change when when item num reachs 9920000 + item.series = 991; + } + // console.log(item); + // add the item to the array + items.push(item); + } + } + + if (invalidItems > 0) { + document.getElementById('batch-upload-status-text').innerHTML = `Warning! ${invalidItems} invalid items will not be uploaded`; + } + // return the item array + return items; +} + +/** + * Uploads an item from item information accordion dropdown (single item upload) + * + */ +async function uploadItem() { + document.getElementById('confirm-btn').innerHTML = ' Uploading...'; + document.getElementById('confirm-btn').disabled = true; + const worker = new WorkerHandler(); + const item = new Item( + sanitizeString(document.getElementById('interact-num').value), + sanitizeString(document.getElementById('maximo-desc').value), + sanitizeString(document.getElementById('uom-field').value), + sanitizeString(document.getElementById('com-group').value), + sanitizeString(document.getElementById('gl-class').value), + ); + + if (document.getElementById('long-desc').value.length > 0) { + item.longdescription = document.getElementById('long-desc').value; + } + + + worker.work(['uploadItems', [item]], (e) => { + console.log(e); + if (e === undefined || typeof e != 'string' || e == 200) { + document.getElementById('error').innerHTML = 'Upload Success'; + document.getElementById('confirm-btn').innerHTML = 'Upload Item'; + document.getElementById('confirm-btn').disabled = false; + new Toast('Upload Complete!', 'bg-success'); + const itemUrl = `https://${CONSTANTS.ENV}.iko.max-it-eam.com/maximo/oslc/graphite/manage-shell/index.html?event=loadapp&value=item&additionalevent=useqbe&additionaleventvalue=itemnum=${item.itemnumber}`; + document.getElementById('error').innerHTML = `Item Upload Successful! (Click to view item) `; + document.getElementById('item-link').addEventListener('click', function(x) { + x.preventDefault(); + shell.openExternal(itemUrl); + }); + } else { + document.getElementById('error').innerHTML = 'Upload Fail'; + document.getElementById('confirm-btn').innerHTML = 'Upload Item'; + document.getElementById('confirm-btn').disabled = false; + // TODO: fail messages + document.getElementById('error').innerHTML = `Item Upload Failed! ${e}`; + } + }); +} +/** + * Uploads an array of items + * + * @param {Array} items + */ +async function batchUploadItems(items) { + const worker = new WorkerHandler(); + // disable clear and upload buttons while uploading items to prevent duplicate requests + const btn = document.getElementById('batch-upload-btn'); + const clearBtn = document.getElementById('clear-batch-items-btn'); + clearBtn.disabled = true; + btn.disabled = true; + + worker.work(['uploadItems', items, true], (e) => { + let finishText = `Upload Finished! ${e[2]} items uploaded, ${e[3]} items added to inventory. `; + if (e[0] == 'failure') { + new Toast(`Invalid! ${e[1]}}!`); + } + clearBtn.disabled = false; + btn.disabled = false; + updateItemNums(e[0]); + const rows = parseInt(document.getElementById('batch-items-table').getAttribute('data-rows')) - 1; + let nums = ''; + for (let i = 2; i <= rows + 1; i++) { + nums += document.getElementById(`${i}-${colLoc.maximo}`).innerHTML ? (document.getElementById(`${i}-${colLoc.maximo}`).innerHTML + ',') : ''; + } + if (e[2] > 0) { + const itemUrl = `https://${CONSTANTS.ENV}.iko.max-it-eam.com/maximo/oslc/graphite/manage-shell/index.html?event=loadapp&value=item&additionalevent=useqbe&additionaleventvalue=itemnum=${nums}`; + finishText += `Click to view:`; + document.getElementById('batch-upload-status-text').innerHTML = finishText; + document.getElementById('batch-link').addEventListener('click', function(e) { + e.preventDefault(); + shell.openExternal(itemUrl); + }); + } else { + document.getElementById('batch-upload-status-text').innerHTML = finishText; + } + console.log('upload finished'); + }); +} +/** + * Gets a list of newly generated item nums and updates the table with them. + * + * If an item has just been uploaded, populates item num cell with new number. + * + * @param {int[][]} arr array of pairs of item nums and table row indexes + */ +function updateItemNums(arr) { + for (const pair of arr) { + const itemNum = pair[0]; + const itemRowIndex = pair[1]; + + // update item number cell + const cell = document.getElementById(`${itemRowIndex + 1}-${colLoc.maximo}`); + cell.innerHTML = itemNum; + + // highlight the item number yellow to signify that it was newly uploaded + cell.classList.add('table-alert'); + } +} +// ////////////////////// + +function skipRow() { + const row = document.getElementById('current-row').innerHTML; + interactiveGoNext(Number(row) + 1); +} + +function finishLoadingBatch(params) { + const bar = new ProgressBar(); + // this has a special work thread since initializing a worker thread takes ~700 ms which is too long + document.getElementById('valid-row').innerHTML = params[1]; + document.getElementById('total-row').innerHTML = params[2]; + const worker = new Worker('./worker.js'); + const db = new Database(); + let description = db.getDescription(params[0]); + if (description === undefined) { + bar.update(100, 'Done!'); + worker.terminate(); + new Toast('Finished Batch Processing'); + return false; + } + bar.update(0, 'Processing Descriptions'); + processBatch(worker, params[0], description); + worker.onmessage = (msg) => { + if (msg.data[0] === 'nextrow') { + description = db.getDescription(msg.data[1]); + if (description === undefined) { + params = worksheetParams(document.getElementById('worksheet-path').value); + worker.postMessage([ + 'saveProcessed', + [params, msg.data[1]], + ]); + new Toast('Finished Batch Processing'); + new Toast('Please wait for file to finish saving...'); + return false; + } + document.getElementById('current-row').innerHTML = description.row; + bar.update(msg.data[1] / params[2] * 100, `Processing Description. Row: ${msg.data[1]} of ${params[2]}`); + processBatch(worker, msg.data[1], description); + } else if (msg.data[0] === 'saveComplete') { + interactiveGoNext(msg.data[1]); + new Toast('File Saved'); + worker.terminate(); + } else { + console.log(`IDK: ${msg.data}`); + } + }; +} + +function processBatch(worker, row, description) { + const interactive = document.getElementById('modeSelect').checked; + const related = document.getElementById('relatedSelect').checked; + const translate = document.getElementById('translateSelect').checked; + const params = worksheetParams(document.getElementById('worksheet-path').value); + if (interactive) { + new Toast('Pausing / Switching to Interactive Mode'); + worker.postMessage([ + 'saveProcessed', + [params, row], + ]); + } else { + worker.postMessage([ + 'nonInteractive', + [ + related, + translate, + description.description, + document.getElementById('selected-language').value, + params, + row, + ], + ]); + } +} + +function continueAuto() { + document.getElementById('modeSelect').checked = false; + finishLoadingBatch([ + Number(document.getElementById('current-row').innerHTML), + document.getElementById('valid-row').innerHTML, + document.getElementById('total-row').innerHTML, + ]); +} + +function interactiveGoNext(row) { + const bar = new ProgressBar(); + const db = new Database(); + const description = db.getDescription(row); + if (description === undefined) { + bar.update(100, 'Done!'); + new Toast('End of File Reached'); + return false; + } + document.getElementById('current-row').innerHTML = description.row; + if (description) { + const worker = new WorkerHandler(); + document.getElementById('maximo-desc').value = description.description; + worker.work(['validSingle', description.description], showResult); + } else { + const field = document.getElementById('maximo-desc'); + field.placeholder = 'Row is blank, press skip row to go next'; + field.value = ''; + const bar = new ProgressBar(); + bar.update(100, 'Done'); + } +} + +function validSingle(isExtended = false) { + const bar = new ProgressBar(); + bar.update(0, 'Starting Item Description Validation'); + const raw_desc = document.getElementById('maximo-desc').value; + const worker = new WorkerHandler(); + worker.work(['validSingle', raw_desc], (result) => { + showResult(result, isExtended); + }); +} + +function showResult(result, isExtended = false) { + let triDesc = document.getElementById('result-triple-main'); + triDesc.value = result[0][0]; + triDesc = document.getElementById('result-triple-ext1'); + triDesc.value = result[0][1]; + triDesc = document.getElementById('result-triple-ext2'); + triDesc.value = result[0][2]; + const related = document.getElementById('relatedSelect').checked; + const translate = document.getElementById('translateSelect').checked; + calcConfidence(result[0][3]); + document.getElementById('validate-badge').innerHTML = 'New'; + if (translate) { + translationDescription(result[0][3]); + } + if (related) { + findRelated(result[0], isExtended); + } +} + +async function ItemAnalysis() { + const valid = new Validate(); + const raw_desc = document.getElementById('maximo-desc').value; + const result = await valid.validateSingle(raw_desc); + let triDesc = document.getElementById('result-triple-main'); + triDesc.value = result[0]; + triDesc = document.getElementById('result-triple-ext1'); + triDesc.value = result[1]; + triDesc = document.getElementById('result-triple-ext2'); + triDesc.value = result[2]; + calcConfidence(result[3]); +} + +function findRelated(result, isExtended = false) { + const worker = new WorkerHandler(); + worker.work(['findRelated', result[3], isExtended], (result) => { + showRelated(result, isExtended); + }); +} + +function translationDescription(description) { + // for now do not translate if english is selected + if (document.getElementById('selected-language').value != 'en') { + const worker = new WorkerHandler(); + if (document.getElementById('result-triple-ext1').value) { + description = `${document.getElementById('result-triple-main').value},${document.getElementById('result-triple-ext1').value}`; + } else { + description = document.getElementById('result-triple-main').value; + } + + worker.work([ + 'translateItem', + description, + document.getElementById('selected-language').value, + 'post', + ], displayTranslation); + } else { + new Toast('Currently translation into English is not supported'); + } +} + +function displayTranslation(data) { + document.getElementById('trans-desc').value = data[0]; + document.getElementById('translation-description').value = `The following words do not have a translation:\n${data[1]}\nPlease check logs at bottom of page for details`; + auto_grow('translation-description'); +} + +function calcConfidence(data) { + let description; + let level = 0; + let tree = ''; + let parent = 0; + const regex = /\d+/g; + const db = new Database(); + let analysis; + let result = ''; + const option = { + style: 'percent', + minimumFractionDigits: 1, + maximumFractionDigits: 1, + }; + const formatter = new Intl.NumberFormat('en-US', option); + + if (data?.length > 0) { // test if description is blank + description = data.split(','); + for (let j = 0; j < description.length; j++) { + if (!(description[j].match(regex))) { + if (db.isManufacturer(description[j])) { + result = `${result}\n${description[j]} is confirmed as a manufacturer`; + } else { + level++; + if (level == 1) { + tree = description[j]; + } else { + tree = tree + ',' + description[j]; + } + analysis = db.getAnalysis(tree); + if (analysis) { + if (level == 1) { + if (analysis.count >= 100) { + result = `${description[j]}: is COMMONLY used as an Item Type.\n${analysis.count}: occurrences found`; + } else if (analysis.count >= 20) { + result = `${description[j]}: is SOMETIMES used as an Item Type.\n${analysis.count}: occurrences found`; + } else { + result = `WARNING: ${description[j]}: is an UNCOMMON Item Type.\nPlease double check.\n${analysis.count}: occurrences found`; + } + } else { + if (analysis.count / parent >= 0.25) { + result = `${result}\n${description[j]} is COMMONLY used as an item descriptor for ${tree}.\n${analysis.count} of ${parent} = ${formatter.format(analysis.count / parent)}`; + } else if (analysis.count / parent >= 0.05) { + result = `${result}\n${description[j]} is SOMETIMES used as an item descriptor for ${tree}.\n${analysis.count} of ${parent} = ${formatter.format(analysis.count / parent)}`; + } else { + result = `${result}\n${description[j]} is an UNCOMMON item descriptor for ${tree}.\nPlease double check.\n${analysis.count} of ${parent} = ${formatter.format(analysis.count / parent)}`; + } + } + parent = analysis.count; + } else { + result = `${result}\n${description[j]}: Does not exist in Maximo as part of: ${tree}.\nPlease Check with Corporate`; + } + } + } + } + document.getElementById('valid-description').value = result.trim(); + } else { + new Toast('Blank Description'); + } +} + +/** + * Initializes search results table and populates relatedResults object + * with search results. + * + * @param {Array< Map>,Map,String>} result array of [array of item nums of all search results, map of item nums to descriptions, and search query with words separated by commas] + * @param {bool} isExtended whether the user has clicked extended search + */ +async function showRelated(result, isExtended = false) { + const bar = new ProgressBar(); + if (!result[0]) { + bar.update(100, 'Done!'); + return false; + } + + // reverse results to get newest items first. + // technically this isn't the best way to do + // this because results aren't guaranteed + // to be in order of oldest to newest. + for (const [key, value] of Object.entries(result[0])) { + result[0][key] = result[0][key].reverse(); + } + // populate global variable with search results (bad practice, but it works) + relatedResults = { + idx: 0, + curKey: 0, + results: result, + }; + + // reset table after called + const relatedTable = document.getElementById('related-table'); + const numResultsText = document.getElementById('num-results'); + + if (isExtended) { + relatedTable.classList.add(`isExt`); + } else { + if (relatedTable.classList.contains(`isExt`)) { + relatedTable.classList.remove(`isExt`); + } + } + // Add headings to the search results table + relatedTable.innerHTML = ` + + + + + + + ${(isExtended ? '' : '')} + + + + + + + +
    Percent MatchItem NumberItem DescriptionMore InfoUOMC_GroupGL_Class
    + `; + + numResultsText.innerHTML = `Found ${Object.entries(result[1]).length} results`; + + // expand the search results accordion + document.getElementById('related-items-accordion-btn').classList.remove('collapsed'); + // load a couple of items + loadRelated(); + html = new bootstrap.Collapse(document.getElementById('accordion-relatedItem'), {toggle: false}); + html.show(); + bar.update(100, 'Done!'); +} + +function loadRelated() { + // check if user clicked extended search + const isExtended = document.getElementById('related-table').classList.contains('isExt'); + + // a map with percent match as key (in decimal form) and array of items as value + // for example, if the key is 1, the list of items match with the search query 100%. If the key is 0.5, the list of items match with the search query 50%. + const scores = relatedResults.results[0]; + + // relatedResults.idx is like a bookmark. It keeps track of how many items have been loaded from the array of items associated with the current key in scores. + // relatedResults.curKey is the number of the current key that is being loaded if you were to iterate thru the keys in scores. + // For example, the first key's number would be 0, second key 1, etc. + if (relatedResults.curKey >= Object.entries(scores).length) { + // If curKey is equal or larger than the number of keys in scores, then there are no more items to load, so return + return; + } else if (Object.entries(scores)[relatedResults.curKey][1].length == 0) { + // If there are no items associated with the current key, then move to the next key and try loading items again. + relatedResults.curKey++; // increment curKey so that the next time the function runs, it will try to load items from the next key + relatedResults.idx = 0; // reset idx so that it starts from the beginning of the array + loadRelated(); + return; // return so we don't do make an infinite loop + } + + const step = 20; // number of items to load at once. + + // get arrs from results obj + const itemNames = relatedResults.results[1]; // a map with the 9-series number of the item as the key and the item info as value. Item info is an array with 4 items: [description, gl class, uom, commodity group] + const searchWords = relatedResults.results[2].split(','); // an array of the search query split by commas. For example, if the search query is "test, item, description", then searchWords would be ["test", "item", "description"] + + let html = ''; let color = ''; // html is the html that will be added to the search results table. color is the color of the row in the table. + + let itemDescription; + + // formatting options for percent match. Converts decimal to percent and rounds to nearest whole number. + const option = { + style: 'percent', + minimumFractionDigits: 0, + maximumFractionDigits: 0, + }; + + const formatter = new Intl.NumberFormat('en-US', option); + // technically this is bad practise since object order might not be guarenteed + // https://stackoverflow.com/questions/983267/how-to-access-the-first-property-of-a-javascript-object + + const percentMatch = Object.entries(scores)[relatedResults.curKey][0]; // get the percent match (name of the current key) + const itemNumList = Object.entries(scores)[relatedResults.curKey][1]; // get the array of items associated with key + let itemsToLoad; // array of items to load + + if (relatedResults.idx + step >= itemNumList.length) { + // if there are less than 20 items to load, load the remaining items in value and increment curKey and reset idx + // this way, the next time the function is called, the next key will be loaded instead + itemsToLoad = itemNumList.slice(relatedResults.idx, undefined); // get array of items from idx to end of array + relatedResults.curKey++; + relatedResults.idx = 0; + } else { + itemsToLoad = itemNumList.slice(relatedResults.idx, relatedResults.idx + step); + relatedResults.idx += step; + } + + // iterate thru each item in value array + for (const itemNum of itemsToLoad) { + itemDescription = itemNames[itemNum][0]; + if (itemDescription) { + // Bold all words in item description that match the search query + for (const word of searchWords) { + split = word.split(' '); + for (const smallWord of split) { + if (smallWord.length > 0) { + itemDescription = itemDescription.replace( + new RegExp(`${smallWord}`, 'i'), + `${itemDescription.match(new RegExp(`${smallWord}`, 'i'))?.[0]}`, + ); + } + } + } + // set row color based on percent match + if (percentMatch > 0.7) { + color = 'table-success'; // green + } else if (percentMatch > 0.4) { + color = 'table-warning'; // yellow + } else { + color = 'table-danger'; // red + } + + // create HTML row. + // In extended search, the vendor info is split from the item description by a | (pipe character). + // All info after the pipe character is put into another column. + // If the item description does not have a pipe character, then the second column is not loaded. + html = `${html}\n + ${formatter.format(percentMatch)} + ${itemNum} + ${(isExtended ? `${itemDescription.substring(0, itemDescription.indexOf('|'))}` : `${itemDescription}`)} + ${(isExtended ? `${itemDescription.slice(itemDescription.indexOf('|') + 1)}` : '')} + ${itemNames[itemNum][2]} + ${itemNames[itemNum][3]} + ${itemNames[itemNum][1]} + add_task`; + } else { + html = `0\nxxxxxxx\nNo Related Items Found`; + } + } + + // add html to table + const relatedTable = document.getElementById('related-items'); + relatedTable.innerHTML += html; + + // if less than 5 items loaded, load more + if (itemsToLoad.length < 5) { + document.getElementById('everything').dispatchEvent(new Event('scroll')); + } +} + +// unused function (was used to copy item validation): probably remove this +function copyResult(copy) { + if (copy === 'single') { + const content = document.getElementById('result-single').innerText; + clipboard.writeText(content); + new Toast('Single Description Copied to Clipboard!'); + } else { + const desc = []; + let content = ''; + content = document.getElementById('result-triple-main').innerText; + desc.push(content); + content = document.getElementById('result-triple-ext1').innerText; + desc.push(content); + content = document.getElementById('result-triple-ext2').innerText; + desc.push(content); + clipboard.write({ + text: document.getElementById('result-single').innerText, + html: `
    ${desc[0]}${desc[1]}${desc[2]}
    `, + }); + new Toast('Triple Description Copied to Clipboard!'); + } +} diff --git a/renderer/item_translation.html b/src/renderer/item_translation.html similarity index 100% rename from renderer/item_translation.html rename to src/renderer/item_translation.html diff --git a/renderer/item_translation.js b/src/renderer/item_translation.js similarity index 100% rename from renderer/item_translation.js rename to src/renderer/item_translation.js diff --git a/renderer/observation_template.html b/src/renderer/observation_template.html similarity index 100% rename from renderer/observation_template.html rename to src/renderer/observation_template.html diff --git a/renderer/observation_template.js b/src/renderer/observation_template.js similarity index 98% rename from renderer/observation_template.js rename to src/renderer/observation_template.js index a9d5515..620091d 100644 --- a/renderer/observation_template.js +++ b/src/renderer/observation_template.js @@ -1,5 +1,5 @@ const { dialog } = require('electron').remote -const ObservationDatabase = require('../assets/better-sqlite'); +const ObservationDatabase = require('../misc/better-sqlite'); // Debug stuff // document.getElementById("selected_output").innerHTML = 'C:\\Users\\majona\\Documents\\observationList\\results.xlsx' diff --git a/renderer/setting.html b/src/renderer/setting.html similarity index 100% rename from renderer/setting.html rename to src/renderer/setting.html diff --git a/renderer/setting.js b/src/renderer/setting.js similarity index 100% rename from renderer/setting.js rename to src/renderer/setting.js diff --git a/renderer/start_page.html b/src/renderer/start_page.html similarity index 97% rename from renderer/start_page.html rename to src/renderer/start_page.html index de1b278..794b0ac 100644 --- a/renderer/start_page.html +++ b/src/renderer/start_page.html @@ -8,8 +8,8 @@ EAM Spare Parts Tool - - + + diff --git a/renderer/start_page.js b/src/renderer/start_page.js similarity index 100% rename from renderer/start_page.js rename to src/renderer/start_page.js diff --git a/renderer/style.css b/src/renderer/style.css similarity index 89% rename from renderer/style.css rename to src/renderer/style.css index 4e47659..31ea42d 100644 --- a/renderer/style.css +++ b/src/renderer/style.css @@ -1,6 +1,6 @@ @charset "UTF-8"; @import "table_style.css"; -@import "../node_modules/bootstrap/dist/css/bootstrap.min.css"; +@import "../../node_modules/bootstrap/dist/css/bootstrap.min.css"; /* --> custom styles for elements @@ -23,7 +23,7 @@ text-align: left; box-shadow: none; transition:all 0.2s ease-in-out; transform: scale(2); - background-image: url("sun.png"); + background-image: url("../../assets/moon.png"); background-color: #cfe2ff; outline-offset: -1px; outline: 2px solid #cfe2ff; @@ -34,7 +34,7 @@ text-align: left; background-color: #212529; outline: 2px solid #212529; border-color: #212529; - background-image: url("moon.png"); + background-image: url("../../assets/moon.png"); } input { @@ -63,14 +63,14 @@ textarea { font-family: "Material Icons"; font-style: normal; font-weight: 100 700; - src: url(./MaterialIcons.woff2) format("woff2"); + src: url(../../assets/MaterialIcons.woff2) format("woff2"); } @font-face { font-family: 'Material Symbols Outlined'; font-style: normal; font-weight: 300; - src: url(./MaterialIcons.woff2) format("woff2"); + src: url(../../assets/MaterialIcons.woff2) format("woff2"); } .material-icons { diff --git a/renderer/table_style.css b/src/renderer/table_style.css similarity index 100% rename from renderer/table_style.css rename to src/renderer/table_style.css diff --git a/renderer/worker.js b/src/renderer/worker.js similarity index 96% rename from renderer/worker.js rename to src/renderer/worker.js index d0a7926..3613253 100644 --- a/renderer/worker.js +++ b/src/renderer/worker.js @@ -1,16 +1,16 @@ -const Validate = require('../assets/validators'); -const ExcelReader = require('../assets/spreadsheet'); -const Spreadsheet = require('../assets/exceljs'); -const Database = require('../assets/indexDB'); -const SharedDatabase = require('../assets/sharedDB'); -const Maximo = require('../assets/maximo'); -const AssetTranslate = require('../assets/asset_translation/asset_translation_main.js'); -const ObservationDatabase = require('../assets/better-sqlite'); -const TranslationDatabase = require('../assets/item_translation/item-translation-sqlite'); +const Validate = require('../misc/validators'); +const ExcelReader = require('../misc/spreadsheet'); +const Spreadsheet = require('../misc/exceljs'); +const Database = require('../misc/indexDB'); +const SharedDatabase = require('../misc/sharedDB'); +const Maximo = require('../misc/maximo'); +const AssetTranslate = require('../asset_translation/asset_translation_main.js'); +const ObservationDatabase = require('../misc/better-sqlite'); +const TranslationDatabase = require('../item_translation/item-translation-sqlite'); const path = require('path'); -const Translation = require('../assets/item_translation/item-translation'); +const Translation = require('../item_translation/item-translation'); const fs = require('fs'); -const CONSTANTS = require('../assets/constants.js'); +const CONSTANTS = require('../misc/constants.js'); /** * Handles messages from the WorkerHandler * @@ -330,7 +330,7 @@ async function checkItemCache(version) { if (!(await shareDB.checkVersion(version))) { db.createTables(); const filePath = path.join( - require('path').resolve(__dirname).replace('renderer', 'assets'), + require('path').resolve(__dirname).replace('src\\renderer', 'assets'), 'item_information.xlsx', ); const excel = new ExcelReader(filePath); diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..20ca694 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,110 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig to read more about this file */ + + /* Projects */ + // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ + // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ + // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ + // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ + // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ + // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ + + /* Language and Environment */ + "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ + // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ + // "jsx": "preserve", /* Specify what JSX code is generated. */ + // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */ + // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ + // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ + // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ + // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ + // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ + // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ + // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ + // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ + + /* Modules */ + "module": "commonjs", /* Specify what module code is generated. */ + // "rootDir": "./", /* Specify the root folder within your source files. */ + // "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */ + // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ + // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ + // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ + // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ + // "types": [], /* Specify type package names to be included without being referenced in a source file. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ + // "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */ + // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */ + // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */ + // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */ + // "resolveJsonModule": true, /* Enable importing .json files. */ + // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */ + // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ + + /* JavaScript Support */ + "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ + "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ + // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ + + /* Emit */ + // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ + // "declarationMap": true, /* Create sourcemaps for d.ts files. */ + // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ + // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ + // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ + // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ + "outDir": "./built", /* Specify an output folder for all emitted files. */ + // "removeComments": true, /* Disable emitting comments. */ + // "noEmit": true, /* Disable emitting files from a compilation. */ + // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ + // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ + // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ + // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ + // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ + // "newLine": "crlf", /* Set the newline character for emitting files. */ + // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ + // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ + // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ + // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ + // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ + // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ + + /* Interop Constraints */ + // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ + // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */ + // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ + "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ + // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ + "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ + + /* Type Checking */ + "strict": true, /* Enable all strict type-checking options. */ + // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ + // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ + // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ + // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ + // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ + // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ + // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ + // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ + // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ + // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ + // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ + // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ + // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ + // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ + // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ + // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ + // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ + + /* Completeness */ + // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ + "skipLibCheck": true /* Skip type checking all .d.ts files. */ + }, + "include": ["./src/**/*", "main.js"] +} From 2f3ae28ac36324f9ded4ff14d7eece1e86245845 Mon Sep 17 00:00:00 2001 From: Jonathan Ma Date: Thu, 25 Jan 2024 14:05:22 -0500 Subject: [PATCH 2/8] transpile --- .../asset_translation_excel.js | 146 ++ .../asset_translation_main.js | 303 ++++ .../asset_translation_sqlite.js | 10 + .../item-translation-sqlite.js | 71 + built/item_translation/item-translation.js | 138 ++ built/main.js | 176 +++ built/misc/autoupdater.js | 39 + built/misc/better-sqlite.js | 177 +++ built/misc/common.js | 380 +++++ built/misc/constants.js | 7 + built/misc/exceljs.js | 369 +++++ built/misc/indexDB.js | 508 ++++++ built/misc/maximo.js | 472 ++++++ built/misc/sharedDB.js | 55 + built/misc/spreadsheet.js | 284 ++++ built/misc/utils.js | 36 + built/misc/validators.js | 144 ++ built/renderer/asset_translation.js | 67 + built/renderer/item_main.js | 1360 +++++++++++++++++ built/renderer/item_translation.js | 25 + built/renderer/observation_template.js | 111 ++ built/renderer/setting.js | 71 + built/renderer/start_page.js | 71 + built/renderer/worker.js | 589 +++++++ package-lock.json | 766 +++++++++- package.json | 5 +- main.js => src/main.js | 4 +- tsconfig.json | 4 +- 28 files changed, 6348 insertions(+), 40 deletions(-) create mode 100644 built/asset_translation/asset_translation_excel.js create mode 100644 built/asset_translation/asset_translation_main.js create mode 100644 built/asset_translation/asset_translation_sqlite.js create mode 100644 built/item_translation/item-translation-sqlite.js create mode 100644 built/item_translation/item-translation.js create mode 100644 built/main.js create mode 100644 built/misc/autoupdater.js create mode 100644 built/misc/better-sqlite.js create mode 100644 built/misc/common.js create mode 100644 built/misc/constants.js create mode 100644 built/misc/exceljs.js create mode 100644 built/misc/indexDB.js create mode 100644 built/misc/maximo.js create mode 100644 built/misc/sharedDB.js create mode 100644 built/misc/spreadsheet.js create mode 100644 built/misc/utils.js create mode 100644 built/misc/validators.js create mode 100644 built/renderer/asset_translation.js create mode 100644 built/renderer/item_main.js create mode 100644 built/renderer/item_translation.js create mode 100644 built/renderer/observation_template.js create mode 100644 built/renderer/setting.js create mode 100644 built/renderer/start_page.js create mode 100644 built/renderer/worker.js rename main.js => src/main.js (97%) diff --git a/built/asset_translation/asset_translation_excel.js b/built/asset_translation/asset_translation_excel.js new file mode 100644 index 0000000..c04fb82 --- /dev/null +++ b/built/asset_translation/asset_translation_excel.js @@ -0,0 +1,146 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +const Exceljs = require('exceljs'); +class AssetExcel { + constructor(filePath) { + this.filePath = filePath; + } + getAssetDescription(lang_code) { + return __awaiter(this, void 0, void 0, function* () { + const wb = new Exceljs.Workbook(); + yield wb.xlsx.readFile(this.filePath); + const ws = wb.getWorksheet(lang_code.toUpperCase()); //alternatively (fetch by ID): getWorksheet(1); + const lastRow = ws.lastRow.number; //last cell row in range + const data = {}; + for (let i = 2; i <= lastRow; i++) { + try { + if (ws.getCell(`A${i}`).text) { + data[`${lang_code}${ws.getCell(`A${i}`).text.toLowerCase().replace(/\s/g, " ").trim()}`] = { + translated: ws.getCell(`B${i}`).text, + siteid: ws.getCell(`C${i}`).text, + assetid: ws.getCell(`D${i}`).text, + description: ws.getCell(`A${i}`).text + }; + } + } + catch (error) { + console.log(error); + console.log(`row number: ${i}`); + } + } + return data; + }); + } + getDescriptors() { + return __awaiter(this, void 0, void 0, function* () { + const wb = new Exceljs.Workbook(); + yield wb.xlsx.readFile(this.filePath); + const ws = wb.getWorksheet('Lookups'); //alternatively (fetch by ID): getWorksheet(1); + const lastRow = ws.lastRow.number; //last cell row in range + const data = { 'worktype': {}, 'labortype': {}, 'frequency': {}, 'sites': {} }; + for (let i = 1; i <= lastRow; i++) { + try { + if (ws.getCell(`A${i}`).text) { //lang code, english desc - translated desc + data['worktype'][`${ws.getCell(`C${i}`).text}${ws.getCell(`A${i}`).text}`.toLowerCase()] = ws.getCell(`B${i}`).text; + } + if (ws.getCell(`F${i}`).text) { + data['labortype'][`${ws.getCell(`H${i}`).text}${ws.getCell(`F${i}`).text}`.toLowerCase()] = ws.getCell(`G${i}`).text; + } + if (ws.getCell(`K${i}`).text) { + data['frequency'][`${ws.getCell(`M${i}`).text}${ws.getCell(`K${i}`).text}`.toLowerCase()] = ws.getCell(`L${i}`).text; + } + if (ws.getCell(`P${i}`).text) { //sites + data['sites'][ws.getCell(`P${i}`).text.toLowerCase().trim()] = { + siteid: ws.getCell(`P${i}`).text.trim(), + description: ws.getCell(`Q${i}`).text, + orgid: ws.getCell(`R${i}`).text, + langcode: ws.getCell(`S${i}`).text + }; + } + } + catch (error) { + console.log(error); + console.log(`row number: ${i}`); + } + } + return data; + }); + } + ReadColumns(columns) { + return __awaiter(this, void 0, void 0, function* () { + // returns specified columns as a array of objects + const wb = new Exceljs.Workbook(); + yield wb.xlsx.readFile(this.filePath); + let wsDetails = { name: '', columns: [] }; + let colnum = 0; + let data = []; + let wsColumns; + for (const ws in wb.worksheets) { + wsDetails.name = wb.worksheets[ws].name; + for (const column in columns) { + wsColumns = wb.worksheets[ws].getRow(1).values.map(function (x) { try { + return x.toLowerCase(); + } + catch (err) { + undefined; + } }); + // convert ws columns to lower case + colnum = wsColumns.indexOf(columns[column]); + if (colnum === -1) { + wsDetails.columns = {}; + // if missing one column then go to next worksheet + break; + } + else { + wsDetails.columns[columns[column]] = colnum; + } + } + if (Object.keys(wsDetails.columns).length === columns.length) { + // if all columns are found then we can leave this loop + break; + } + } + if (wsDetails.columns === {}) { + console.log('error worksheet with specified columns does not exist'); + } + else { + const ws = wb.getWorksheet(wsDetails.name); + const lastrow = ws.lastRow.number; + let row; + let blanks; + for (let i = 2; i <= lastrow; i++) { + try { + row = {}; + blanks = 0; + for (const column in wsDetails.columns) { + if (ws.getCell(i, wsDetails.columns[column]).value === null) { + blanks++; + } + row[column] = ws.getCell(i, wsDetails.columns[column]).value; + } + if (blanks === columns.length) { + console.log('blank row ignoring'); + } + else { + data.push(row); + } + } + catch (err) { + console.log(err); + console.log(`row number: ${i}`); + } + } + return data; + } + }); + } +} +module.exports = AssetExcel; diff --git a/built/asset_translation/asset_translation_main.js b/built/asset_translation/asset_translation_main.js new file mode 100644 index 0000000..83ff392 --- /dev/null +++ b/built/asset_translation/asset_translation_main.js @@ -0,0 +1,303 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +const TransDB = require('./asset_translation_excel.js'); +const Exceljs = require('exceljs'); +const path = require('path'); +const fs = require('fs'); +const { debug } = require('console'); +class AssetTranslateDescription { + translate(params) { + return __awaiter(this, void 0, void 0, function* () { + // filename, wsname, language, siteid + // outputs csv files to the same folder 4 sheets, Asset, location, pm, jobplan + let excel = new TransDB(params.wb_translation); + const lookups = yield excel.getDescriptors(); + const lang_code = lookups.sites[params["siteid"]].langcode.toLowerCase(); + const org_id = lookups.sites[params["siteid"]].orgid; + const translations = yield excel.getAssetDescription(lang_code); + let folder = path.dirname(params.wb_pms); + let date = new Date(); + let i = 0; + let new_folder = path.join(folder, `${date.toDateString()}(${i})`); + try { // cause access throws error if path does not exist, undefined??? if it does + while (fs.accessSync(new_folder) === undefined) { + i = i + 1; + new_folder = path.join(folder, `${date.toDateString()}(${i})`); + } + } + catch (err) { + fs.mkdirSync(new_folder); + } + // output location + const options = { + formatterOptions: { + delimiter: ',', + quote: '"', + writeBOM: true, + } + }; + let logbook = new Exceljs.Workbook(); + let logsheet = logbook.addWorksheet('log'); + // write locations sheet + let wb = new Exceljs.Workbook(); + let ws = wb.addWorksheet('locations'); + ws.addRow(['IKO_Import', 'IKO_LOCATION', 'AddChange', lang_code.toUpperCase()]); + ws.addRow(['SITEID', 'LOCATION', 'DESCRIPTION']); + for (const asset in translations) { + if (translations[asset].assetid) { + ws.addRow([translations[asset].siteid, `L-${translations[asset].assetid}`, translations[asset].translated]); + } + } + yield wb.csv.writeFile(path.join(new_folder, 'location.csv'), options); + // write assets sheet + wb = new Exceljs.Workbook(); + ws = wb.addWorksheet('assets'); + ws.addRow(['IKO_Import', 'IKO_ASSET', 'AddChange', lang_code.toUpperCase()]); + ws.addRow(['SITEID', 'ASSETNUM', 'DESCRIPTION']); + for (const asset in translations) { + if (translations[asset].assetid) { + ws.addRow([translations[asset].siteid, `${translations[asset].assetid}`, translations[asset].translated]); + } + } + yield wb.csv.writeFile(path.join(new_folder, 'asset.csv'), options); + //get jobplan information + excel = new TransDB(params.wb_pms); + const jps = yield excel.ReadColumns(['jpnum', 'description', 'siteid']); + wb = new Exceljs.Workbook(); + ws = wb.addWorksheet('jobplans'); + ws.addRow(['IKO_Import', 'IKO_JOBPLAN', 'AddChange', lang_code.toUpperCase()]); + ws.addRow(['JPNUM', 'SITEID', 'DESCRIPTION', 'PLUSCREVNUM', 'ORGID']); + let thing; + for (const jp in jps) { + thing = this.NumParser(jps[jp], {}, lookups); + jps[jp]['translated_description'] = this.translateDescription(jps[jp], lookups, translations, logsheet, thing, lang_code); + if (thing.type === 'sjp') { + ws.addRow([jps[jp]['jpnum'], '', jps[jp]['translated_description'], 0, '']); + } + else { + ws.addRow([jps[jp]['jpnum'], jps[jp]['siteid'], jps[jp]['translated_description'], 0, org_id]); + } + } + yield wb.csv.writeFile(path.join(new_folder, 'jobplans.csv'), options); + const pms = yield excel.ReadColumns(['pmnum', 'description', 'siteid']); + wb = new Exceljs.Workbook(); + ws = wb.addWorksheet('pms'); + ws.addRow(['IKO_Import', 'IKO_PM', 'AddChange', lang_code.toUpperCase()]); + ws.addRow(['PMNUM', 'SITEID', 'DESCRIPTION']); + for (const pm in pms) { + thing = this.NumParser(pms[pm], {}, lookups); + pms[pm]['translated_description'] = this.translateDescription(pms[pm], lookups, translations, logsheet, thing, lang_code); + ws.addRow([pms[pm]['pmnum'], pms[pm]['siteid'], pms[pm]['translated_description']]); + } + yield wb.csv.writeFile(path.join(new_folder, 'pms.csv'), options); + yield logbook.csv.writeFile(path.join(new_folder, 'logs.csv')); + postMessage(['result', 'done']); + }); + } + translateDescription(info, lookups, translations, logsheet, thing, lang_code) { + var _a; + // returns translated description + let translated = ''; + if (thing.type === 'sjp') { + let temp = translations[`${lang_code}${thing.route_name.toLowerCase().replace(/\s/g, " ")}`]; //Check for empty data + //https://stackoverflow.com/questions/22036576/why-does-the-javascript-string-whitespace-character-nbsp-not-match + if (temp) { + translated = `${temp.translated.trim()} - `; + } + else { + logsheet.addRow(['Require Translation', thing.route_name, lang_code]); + } + temp = lookups.worktype[`${lang_code}${thing.sjp}`]; + if (temp) { + translated = `${translated}${temp}`; + } + else { + logsheet.addRow(['Require Translation', thing.sjp, lang_code]); + } + } + else if (thing.type === 'pm' || thing.type === 'pmjp') { + let temp = translations[`${lang_code}${thing.route_name.toLowerCase().replace(/\s/g, " ")}`]; + if (temp) { + translated = `${temp.translated.trim()} - `; + } + else { + logsheet.addRow(['Require Translation', thing.route_name, lang_code, 'Route']); + } + temp = `${lang_code}${thing.freq.slice(0, 1)}`; + if (thing.freq.slice(1) > 1) { + temp = `${temp}s`; + } + translated = `${translated}${thing.freq.slice(1)} ${lookups.frequency[temp]} - `; + if (thing.worktype.indexOf('lc') != -1) { + temp = lookups.worktype[`${lang_code}${thing.worktype.slice(0, 2)}`]; + temp = `${temp}${String.fromCharCode(64 + parseInt(thing.worktype.slice(2)))}`; + translated = `${translated}${temp}`; + temp = translations[`${lang_code}${(_a = thing.suffix) === null || _a === void 0 ? void 0 : _a.replace(/\s/g, " ").toLowerCase()}`]; + if (temp) { + translated = `${translated} ${temp.translated}`; + } + else { + logsheet.addRow(['Require Translation', thing.suffix, lang_code, 'Part']); + } + } + else { + translated = `${translated}${lookups.worktype[`${lang_code}${thing.worktype}`]} - ${lookups.labortype[`${lang_code}${thing.labor}`]}`; + } + } + else { + logsheet.addRow(['Unknown Format', info.description, lang_code,]); + } + if (translated.length > 100) { + logsheet.addRow(['Description Length', info.description, lang_code, translated.length, translated]); + } + return translated; + } + NumParser(info, specs, lookups) { + var _a; + // always match from beginning + const route_regex = /^\w{1,3}\d{1,4}/g; //matches routes / asset numbers + const freq_regex = /^(d|w|m|y)\d+/g; //matches frequency + const work_regex = /^\w{3}/g; //matches work type + const lc_regex = /^lf\d{1}/g; // matches lifecycle + const labor_regex = /^o|m|e/g; //matches labor type + const dup_regex = /^\d*$/g; //matches duplicate pm number + const lc_item = /(?<=LC-\w+\s).*/g; + const freqs = { + d: 'day', + w: 'week', + m: 'month', + y: 'year' + }; + const sjp_type = { + bde: 'Breakdown - Electrical', + bdm: 'Breakdown - Mechanical', + bdo: 'Breakdown - Operational', + core: 'Corrective - Post-Repair - Electrical', + corm: 'Corrective - Post-Repair - Mechanical', + epie: 'Equipment/Process Improvement - Electrical', + epim: 'Equipment/Process Improvement - Mechanical', + hkgm: 'Housekeeping', + inae: 'Inspection - Ad Hoc - Electrical', + inam: 'Inspection - Ad Hoc - Mechanical', + peme: 'Pre-emptive Maintenance - Electrical', + pemm: 'Pre-emptive Maintenance - Mechanical', + rece: 'Recondition - Electrical', + recm: 'Recondition - Mechanical', + safe: 'Safety - Electrical', + safm: 'Safety - Mechanical', + tshe: 'Troubleshooting - Electrical', + tshm: 'Troubleshooting - Mechanical', + stge: 'Standing - Electrical', + stgo: 'Standing - Production', + stgm: 'Standing - Mechanical' + }; + let text = (info.pmnum || info.jpnum).toLowerCase(); + if (text.length < 10 && sjp_type[text.slice(5)]) { + specs['sjp'] = text.slice(5); + specs['route_name'] = info.description.slice(0, info.description.length - sjp_type[specs['sjp']].length - 2).trim(); + specs['route'] = text.slice(0, 5); + specs['type'] = 'sjp'; + } + else { + // check if site pm jp (has siteid in num) + let match = text.slice(0, 2); + if (lookups.sites[match]) { + text = text.slice(2); + specs['siteid'] = match; + specs['type'] = 'pmjp'; + } + else if (lookups.sites[text.slice(0, 3)]) { + text = text.slice(3); + specs['siteid'] = text.slice(0, 3); + specs['type'] = 'pmjp'; + } + else { + specs['type'] = 'pm'; + } + match = text.match(route_regex); + if (match != null) { + specs['route'] = match[0]; + text = text.slice(specs['route'].length); + } + else { + console.log('no asset number / route match'); + } + match = text.match(freq_regex); + if (match != null) { + specs['freq'] = match[0]; + text = text.slice(specs['freq'].length); + const route_desc_regex = new RegExp(`.*(?=-\\s*${specs['freq'].slice(1)} ${freqs[specs['freq'].slice(0, 1)]})`, 'ig'); + match = info.description.match(route_desc_regex); + try { + specs['route_name'] = match[0].trim(); + } + catch (error) { + specs['route_name'] = 'number & description mismatch'; + console.log('number & description mismatch'); + console.log(info.description); + } + } + else { + // possibly standard site job plan + console.log('no frequency found'); + } + match = text.match(work_regex); + if (match != null) { + specs['worktype'] = match[0]; + text = text.slice(specs['worktype'].length); + specs['suffix'] = (_a = info.description.match(lc_item)) === null || _a === void 0 ? void 0 : _a[0]; + } + match = text.match(labor_regex); + if (match != null) { + specs['labor'] = match[0]; + text = text.slice(specs['labor'].length); + } + else { + console.log('no labor found'); + } + try { + specs['dup'] = parseInt(text); + } + catch (error) { + console.log('bad dup number'); + console.log(specs['dup']); + } + } + return specs; + } + ReverseSplit(str) { + let chars = str.split(''); + let result = []; + let hyphens = 0; // only split into 4 sections + let previous = chars.length; + for (let i = chars.length; i >= 0; i--) { + if (chars[i] === '-') { + // make sure pre-emptive is not split + try { + if (chars.slice(i - 3, i) != 'pre' && chars.slice(i, i + 7) != 'emptive') { + result[3 - hyphens] = chars.slice(i + 1, previous).join('').trim(); + hyphens++; + previous = i; + } + if (result[1]) { + result[0] = chars.slice(0, i).join('').trim(); + break; + } + } + catch (e) { + console.log(e); + } + } + } + return result; + } +} +module.exports = AssetTranslateDescription; diff --git a/built/asset_translation/asset_translation_sqlite.js b/built/asset_translation/asset_translation_sqlite.js new file mode 100644 index 0000000..b3328fc --- /dev/null +++ b/built/asset_translation/asset_translation_sqlite.js @@ -0,0 +1,10 @@ +"use strict"; +const { SqliteError } = require('better-sqlite3'); +const sql = require('better-sqlite3'); +// not needed tbh +class TranslationDatabase { + constructor() { + // save location is in user %appdata% + this.db = new sql(`${process.env.APPDATA}/EAM Spare Parts/AssetTransList.db`); //, { verbose: console.log }); + } +} diff --git a/built/item_translation/item-translation-sqlite.js b/built/item_translation/item-translation-sqlite.js new file mode 100644 index 0000000..5308c8a --- /dev/null +++ b/built/item_translation/item-translation-sqlite.js @@ -0,0 +1,71 @@ +"use strict"; +const { SqliteError } = require('better-sqlite3'); +const sql = require('better-sqlite3'); +class TranslationDatabase { + constructor() { + // save location is in user %appdata% + this.db = new sql(`${process.env.APPDATA}/EAM Spare Parts/translist.db`); //, { verbose: console.log }); + } + // save updated translation information + refreshData(data) { + // {english:string, lang_code:string, translation:string} + // need to check for duplicate english since that is not allowed + const dropTables = this.db.prepare('DROP TABLE IF EXISTS translations'); + const runQuery2 = this.db.transaction(() => { + dropTables.run(); + }); + runQuery2(); + const createTranslationTable = this.db.prepare(`CREATE TABLE translations( + translate_id INTEGER PRIMARY KEY, + english TEXT NOT NULL COLLATE NOCASE, + lang_code TEXT NOT NULL, + translation TEXT NOT NULL, + UNIQUE(english, lang_code) + );`); + const runQuery = this.db.transaction(() => { + createTranslationTable.run(); + }); + runQuery(); + const insert = this.db.prepare(`INSERT INTO translations ( + english, lang_code, translation) + VALUES (@english, @lang_code, @translation)`); + const insertMany = this.db.transaction((data) => { + for (const translation of data) { + try { + insert.run(translation); + } + catch (SqliteError) { + postMessage(['debug', SqliteError.message]); + console.log(SqliteError.message); + if (SqliteError.code == "SQLITE_CONSTRAINT_UNIQUE") { + postMessage(['error', 'Duplicate Value in English']); + } + } + } + }); + insertMany(data); + postMessage(['result', 'complete']); + } + // get translation of a word in the requested language + getTranslation(lang_code, word) { + const sql = this.db.prepare('SELECT * FROM translations WHERE lang_code = @lang_code AND english = @word'); + const result = sql.all({ lang_code: lang_code.toUpperCase(), word: word }); + if (result.length === 1) { + return result[0].translation; + } + else { + return false; + } + } + //get list of languages currently in database + getLanguages() { + const sql = this.db.prepare('SELECT DISTINCT lang_code FROM translations'); + const result = sql.all(); + let langs = []; + for (const lang of result) { + langs.push(lang.lang_code); + } + return langs; + } +} +module.exports = TranslationDatabase; diff --git a/built/item_translation/item-translation.js b/built/item_translation/item-translation.js new file mode 100644 index 0000000..d0d3bbc --- /dev/null +++ b/built/item_translation/item-translation.js @@ -0,0 +1,138 @@ +"use strict"; +const TransDB = require('./item-translation-sqlite'); +const Database = require('../misc/indexDB'); +class TranslateDescription { + contextTranslate(description, lang_code, result) { + // translations item description into requested language + // accounts for context of parent words + let descriptions = description.split(','); + const db = new TransDB(); + const db2 = new Database(); + const hasNumber = /\d/; //regex to check for numbers [0-9] + let temp; + let tempNew; + let transDesc = []; + let replacement; + let missing = []; + for (let i = 0; i < descriptions.length; i++) { + // 1. loop through all phrases (a phrase in this case is the string between commas) + // if the phrase does not have any numbers try to translate the whole phrase + for (let j = descriptions.length; j > i; j--) { + replacement = db.getTranslation(lang_code, descriptions.slice(i, j).join(',')); + if (replacement) { + postMessage(['debug', `${descriptions.slice(i, j)} translated to ${replacement}`]); + transDesc.push(replacement); + i = j - 1; + break; + } + else if (descriptions.slice(i, j).length > 1) { + postMessage(['debug', `${descriptions.slice(i, j).join(',')} has no translation to ${lang_code}`]); + } + else { + if (!(db2.isManufacturer(descriptions.slice(i, j)))) { + postMessage(['debug', `${descriptions.slice(i, j)} has no translation to ${lang_code}`]); + temp = descriptions[i].split(" "); + if (hasNumber.test(descriptions[i])) { + transDesc.push(descriptions[i]); + } + else if (temp.length == 1) { + transDesc.push(descriptions[i]); + missing.push(descriptions[i]); + } + else { + // if the phrase has numbers then split it by spaces and check each word + tempNew = []; + for (let k = 0; k < temp.length; k++) { + replacement = db.getTranslation(lang_code, temp[k]); + if (replacement) { + tempNew.push(replacement); + } + else if (temp[k].length > 0) { + tempNew.push(temp[k]); + missing.push(temp[k]); + postMessage(['debug', `${temp[k]} has no translation to ${lang_code}`]); + } + } + // join the individual words back into a phrase + transDesc.push(tempNew.join(" ")); + } + } + else { // if it is manufacturer + transDesc.push(descriptions[i]); + } + } + } + } + // join the phrases back into a description + if (result == 'post') { + postMessage(['result', transDesc.join(","), missing]); + } + else if (result == 'return') { + return { description: transDesc.join(","), missing: missing }; + } + else { + console.log('no return specified'); + postMessage(['error', 'no return specified']); + } + } + translate(params) { + // deprecated + //{description: string, manu: string, lang: string} + // translations item description into requested language + let descriptions = params.description.split(","); + const db = new TransDB(); + const hasNumber = /\d/; //regex to check for numbers [0-9] + // if a string contains numbers than its probably an item number which doesnt not need to be translated + let temp; + let tempNew; + let transDesc = []; + let replacement; + let missing = []; + for (let i = 0; i < descriptions.length; i++) { + // 1. loop through all phrases (a phrase in this case is the string between commas) + if (!hasNumber.test(descriptions[i])) { + // if the phrase does not have any numbers try to translate the whole phrase + replacement = db.getTranslation(params.lang, descriptions[i]); + if (replacement) { + transDesc.push(replacement); + } + else if (descriptions[i].length > 0) { + transDesc.push(descriptions[i]); + missing.push(descriptions[i]); + } + } + else { + // if the phrase has numbers then split it by spaces and check each word + temp = descriptions[i].split(" "); + if (temp.length > 1) { + tempNew = []; + for (let j = 0; j < temp.length; j++) { + if (!hasNumber.test(temp[j])) { + // if the word has no numbers translate it + replacement = db.getTranslation(params.lang, temp[j]); + if (replacement) { + tempNew.push(replacement); + } + else if (temp[j].length > 0) { + tempNew.push(temp[j]); + missing.push(temp[j]); + } + } + else { + // if it has numbers just leave it as it + tempNew.push(temp[j]); + } + } + } + else { + tempNew = temp; + } + // join the individual words back into a phrase + transDesc.push(tempNew.join(" ")); + } + } + // join the phrases back into a description + return { description: transDesc.join(","), missing: missing }; + } +} +module.exports = TranslateDescription; diff --git a/built/main.js b/built/main.js new file mode 100644 index 0000000..20b5bbe --- /dev/null +++ b/built/main.js @@ -0,0 +1,176 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +// Modules to control application life and create native browser window +const { app, BrowserWindow, ipcMain, screen, dialog, shell } = require('electron'); +const path = require('path'); +const fs = require('fs'); +const { appUpdater } = require('./misc/autoupdater.js'); +const CONSTANTS = require('./misc/constants.js'); +require('electron-reload')(__dirname); +let mainWindow; +let settingWindow; +if (require('electron-squirrel-startup')) { + app.quit(); +} +// Write eml file +ipcMain.on('write-file', (event, emailData) => { + const pathToFile = path.resolve(__dirname, 'downloadedFile.eml'); + fs.writeFile(pathToFile, emailData, (err) => { + if (err) { + console.error(`Error writing file: ${err}`); + } + else { + shell.openPath(pathToFile) + .then(() => { + sleep(2000).then(() => { + // Delete the file after opening + fs.unlink(pathToFile, (err) => { + if (err) { + console.error(`Error deleting file: ${err}`); + } + else { + console.log('File deleted successfully'); + } + }); + }) + .catch((err) => { + console.error(`Error opening file: ${err}`); + }); + }); + } + }); +}); +ipcMain.on('openSettings', (event, arg) => { + settingWindow = new BrowserWindow({ + parent: mainWindow, + width: 800, + height: 600, + autoHideMenuBar: true, + webPreferences: { + nodeIntegration: true, + nodeIntegrationInWorker: true, + contextIsolation: false, + }, + }); + settingWindow.loadFile(path.join('src', 'renderer', 'setting.html')); + settingWindow.show(); + settingWindow.on('closed', () => { + mainWindow.show(); + settingWindow = null; + }); + if (CONSTANTS.OPEN_DEV_TOOLS) { + settingWindow.webContents.openDevTools(); + } +}); +ipcMain.on('getVersion', (event, arg) => { + event.returnValue = app.getVersion(); +}); +ipcMain.handle('select-to-be-translated', (event, arg) => __awaiter(void 0, void 0, void 0, function* () { + const result = yield dialog.showOpenDialog(mainWindow, { + title: 'Select Spreadsheet', + filters: [ + { name: 'Spreadsheet', extensions: ['xls', 'xlsx', 'xlsm', 'xlsb'] }, + ], + properties: [ + 'openFile', + ], + }); + return result; +})); +ipcMain.handle('select-excel-file', (event, arg) => __awaiter(void 0, void 0, void 0, function* () { + const result = yield dialog.showOpenDialog(mainWindow, { + title: 'Select Excel Spreadsheet', + filters: [ + { name: 'Spreadsheet', extensions: ['xls', 'xlsx', 'xlsm', 'xlsb'] }, + ], + properties: [ + 'openFile', + ], + }); + return result; +})); +ipcMain.handle('select-translations', (event, arg) => __awaiter(void 0, void 0, void 0, function* () { + const result = yield dialog.showOpenDialog(mainWindow, { + title: 'Select Translation Definition Spreadsheet', + filters: [ + { name: 'Spreadsheet', extensions: ['xls', 'xlsx', 'xlsm', 'xlsb'] }, + ], + properties: [ + 'openFile', + ], + }); + return result; +})); +ipcMain.on('getPath', (event, arg) => { + event.returnValue = app.getPath('userData'); +}); +ipcMain.on('loading', (event, arg) => { + mainWindow.loadFile(path.join('src', 'renderer', 'item_main.html')); +}); +ipcMain.on('start_item_module', (event, arg) => { + mainWindow.loadFile(path.join('src', 'renderer', 'item_loading.html')); +}); +ipcMain.on('start_observation_template', (event, arg) => { + mainWindow.loadFile(path.join('src', 'renderer', 'observation_template.html')); +}); +ipcMain.on('start_item_translate', (event, arg) => { + mainWindow.loadFile(path.join('src', 'renderer', 'item_translation.html')); +}); +ipcMain.on('start_asset_translate', (event, arg) => { + mainWindow.loadFile(path.join('src', 'renderer', 'asset_translation.html')); +}); +function createWindow() { + // Create the browser window. + const { width, height } = screen.getPrimaryDisplay().workAreaSize; + mainWindow = new BrowserWindow({ + width: width / 2, + height: height, + x: 0, + y: 0, + autoHideMenuBar: true, + webPreferences: { + nodeIntegration: true, + nodeIntegrationInWorker: true, + contextIsolation: false, + }, + }); + // and load the index.html of the app. + mainWindow.loadFile(path.join('src', 'renderer', 'start_page.html')); + // Open the DevTools. + if (CONSTANTS.OPEN_DEV_TOOLS) { + mainWindow.webContents.openDevTools(); + } + const page = mainWindow.webContents; + page.once('did-frame-finish-load', () => { + console.log('checking for updates'); + appUpdater(); + }); + mainWindow.show(); +} +// This method will be called when Electron has finished +// initialization and is ready to create browser windows. +// Some APIs can only be used after this event occurs. +app.whenReady().then(() => { + createWindow(); + app.on('activate', function () { + // On macOS it's common to re-create a window in the app when the + // dock icon is clicked and there are no other windows open. + if (BrowserWindow.getAllWindows().length === 0) + createWindow(); + }); +}); +// Quit when all windows are closed, except on macOS. There, it's common +// for applications and their menu bar to stay active until the user quits +// explicitly with Cmd + Q. +app.on('window-all-closed', function () { + if (process.platform !== 'darwin') + app.quit(); +}); diff --git a/built/misc/autoupdater.js b/built/misc/autoupdater.js new file mode 100644 index 0000000..6397163 --- /dev/null +++ b/built/misc/autoupdater.js @@ -0,0 +1,39 @@ +"use strict"; +const os = require('os'); +const { app, autoUpdater, dialog } = require('electron'); +const version = app.getVersion(); +const platform = os.platform() + '_' + os.arch(); // usually returns darwin_64 +const updaterFeedURL = `https://jonathanmajh-iko-mro-items.onrender.com/update/` + platform + '/' + version; +// replace updaterFeedURL with https://l3gxze.deta.dev +function appUpdater() { + autoUpdater.setFeedURL(updaterFeedURL); + /* Log whats happening + TODO send autoUpdater events to renderer so that we could console log it in developer tools + You could alsoe use nslog or other logging to see what's happening */ + autoUpdater.on('error', err => console.log(err)); + autoUpdater.on('checking-for-update', () => console.log('checking-for-update')); + autoUpdater.on('update-available', () => { + console.log('update-available'); + }); + autoUpdater.on('update-not-available', () => console.log('update-not-available')); + // Ask the user if update is available + autoUpdater.on('update-downloaded', (event, releaseNotes, releaseName) => { + console.log('update-downloaded'); + // Ask user to update the app + const selected = dialog.showMessageBoxSync({ + type: 'question', + buttons: ['Update and Relaunch', 'Later'], + defaultId: 0, + message: 'Update Available!', + detail: `A new version of ${app.getName()} has been downloaded\nDo you want to update now?\nUpdate will be automatically installed on next start up.`, + }); + if (selected === 0) { + autoUpdater.quitAndInstall(); + } + }); + // init for updates + autoUpdater.checkForUpdates(); +} +exports = module.exports = { + appUpdater +}; diff --git a/built/misc/better-sqlite.js b/built/misc/better-sqlite.js new file mode 100644 index 0000000..3c19013 --- /dev/null +++ b/built/misc/better-sqlite.js @@ -0,0 +1,177 @@ +"use strict"; +const sql = require('better-sqlite3'); +class ObservationDatabase { + constructor() { + this.db = new sql(`${process.env.APPDATA}/EAM Spare Parts/obserlist.db`); //, { verbose: console.log }); + } + createTables() { + const dropTables = this.db.prepare('DROP TABLE IF EXISTS meters'); + const dropTables2 = this.db.prepare('DROP TABLE IF EXISTS observations'); + const dropTables3 = this.db.prepare('DROP TABLE IF EXISTS jobtasks'); + const runQuery2 = this.db.transaction(() => { + dropTables.run(); + dropTables2.run(); + dropTables3.run(); + }); + runQuery2(); + const createMeterTable = this.db.prepare(`CREATE TABLE meters( + meter_id INTEGER PRIMARY KEY, + name TEXT NOT NULL UNIQUE, + list_id TEXT NOT NULL UNIQUE, + inspect TEXT NOT NULL, + desc TEXT NOT NULL, + ext_desc TEXT NOT NULL, + in_maximo INT DEFAULT 0, + search_str TEXT);`); + const createObservationTable = this.db.prepare(`CREATE TABLE observations( + observation_id INTEGER PRIMARY KEY, + meter TEXT NOT NULL, + id_value TEXT NOT NULL, + observation TEXT NOT NULL, + action TEXT, + in_maximo INT DEFAULT 0, + search_str TEXT + )`); + const createJobTaskTable = this.db.prepare(`CREATE TABLE jobtasks ( + row_id INTEGER PRIMARY KEY, + jpnum INTEGER NOT NULL, + metername TEXT NOT NULL, + orgid TEXT, + siteid TEXT, + jptask INTEGER, + desc TEXT, + ext_desc TEXT, + status INT DEFAULT 0 + )`); // 0 = no longer defined, 1 = no change, 2 = need to update + const runQuery = this.db.transaction(() => { + createMeterTable.run(); + createObservationTable.run(); + createJobTaskTable.run(); + }); + runQuery(); + } + close() { + this.db.close(); + } + saveJobTasks(data) { + const insert = this.db.prepare(`INSERT INTO jobtasks ( + jpnum, metername, orgid, siteid, jptask, desc, ext_desc) + VALUES (@jpnum, @metername, @orgid, @siteid, @jptask, @desc, @ext_desc)`); + const insertMany = this.db.transaction((data) => { + for (const jobtask of data) + insert.run(jobtask); + }); + insertMany(data); + } + insertMeter(data) { + const insert = this.db.prepare(`INSERT INTO meters ( + name, list_id, inspect, desc, ext_desc, search_str) + VALUES (@name, @list_id, @inspect, @desc, @ext_desc, @search_str)`); + const insertMany = this.db.transaction((data) => { + for (const meter of data) { + meter.ext_desc = `
    ${meter.ext_desc.replaceAll('\n', '
    \n
    ')}
    `; + insert.run(meter); + } + }); + insertMany(data); + } + insertObservation(data) { + const insert = this.db.prepare(`INSERT INTO observations ( + meter, id_value, observation, action, search_str) + VALUES (@meter, @id_value, @observation, @action, @search_str)`); + const insertMany = this.db.transaction((data) => { + for (const meter of data) + insert.run(meter); + }); + insertMany(data); + } + compareDomainDefinition(list_id, inspect, maximo_table) { + // true means change will be taken care of when querying for in_maximo=0 + // maximo_table: meters are kept in two places, meters + alndomain, since they are the same the same function can be used, just make sure to make it as such + let stmt = this.db.prepare('SELECT list_id, inspect FROM meters WHERE list_id = ?'); + const meter = stmt.all(list_id); + if (meter.length === 1) { + if (meter[0].inspect == inspect) { + stmt = this.db.prepare(`UPDATE meters SET in_maximo = ${maximo_table} WHERE list_id = ?`); + stmt.run(list_id); + return true; + } + else { + postMessage(['debug', `Update Meter: "${list_id}" changed New: "${meter[0].inspect}" Old: "${inspect}"`]); + return true; + } + } + else { + postMessage(['debug', `Old Meter: ${list_id}: ${inspect} can be removed`]); + return false; + } + } + compareJobTasks() { + let stmt = this.db.prepare('SELECT name, desc, ext_desc FROM meters'); + const newJobTasks = stmt.all(); + let updatestmt; + stmt = this.db.prepare('SELECT row_id, jpnum, metername, desc, ext_desc FROM jobtasks WHERE metername = ?'); + for (const newJobTask of newJobTasks) { + let oldJobTasks = stmt.all(`${newJobTask.name}01`); + if (oldJobTasks.length != 0) { + for (const oldJobTask of oldJobTasks) { + if (oldJobTask.desc == newJobTask.desc && oldJobTask.ext_desc == newJobTask.ext_desc) { + updatestmt = this.db.prepare(`UPDATE jobtasks SET status = 1 WHERE row_id = ?`); + updatestmt.run(oldJobTask.row_id); + } + else { + updatestmt = this.db.prepare(`UPDATE jobtasks SET status = 2 WHERE row_id = ?`); + updatestmt.run(oldJobTask.row_id); + } + } + } + else { + postMessage(['debug', `Meter: ${newJobTask.name} not used by any existing Job Tasks`]); + } + } + } + compareDomainValues(search, observation) { + let stmt = this.db.prepare('SELECT meter, id_value, observation FROM observations WHERE search_str = ?'); + const observ = stmt.all(search); + if (observ.length === 1) { + if (observ[0].observation == observation) { + stmt = this.db.prepare('UPDATE observations SET in_maximo = 1 WHERE search_str = ?'); + stmt.run(search); + return true; + } + else { + postMessage(['debug', `Update Observation: "${search}" changed New: "${observ[0].observation}" Old: "${observation}"`]); + return true; + } + } + else { + postMessage(['debug', `Old Observation: ${search}: ${observation} can be removed`]); + return false; + } + } + getNewDomainDefinitions() { + const stmt = this.db.prepare('SELECT list_id, inspect FROM meters WHERE in_maximo = 0'); + const meters = stmt.all(); + return meters; + } + getNewMaximoMeters() { + const stmt = this.db.prepare('SELECT list_id, inspect FROM meters WHERE in_maximo = 0 or in_maximo = 1'); + const meters = stmt.all(); + return meters; + } + getNewDomainValues() { + const stmt = this.db.prepare('SELECT meter, id_value, observation FROM observations WHERE in_maximo = 0'); + const observs = stmt.all(); + return observs; + } + getJobTasks(status) { + const stmt = this.db.prepare(`select t1.jpnum, t1.metername, orgid, siteid, jptask, t2.desc, t2.ext_desc, t1.desc as old_desc, t1.ext_desc as old_ext_desc from + (select jpnum, metername, orgid, siteid, jptask, desc, ext_desc from jobtasks where status = ?) as t1 + left JOIN + (select name, desc, ext_desc from meters) as t2 + on substr(t1.metername, 1, length(t1.metername) - 2)=t2.name`); + const things = stmt.all(status); + return things; + } +} +module.exports = ObservationDatabase; diff --git a/built/misc/common.js b/built/misc/common.js new file mode 100644 index 0000000..630f4e2 --- /dev/null +++ b/built/misc/common.js @@ -0,0 +1,380 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +(function () { + if (!(localStorage.getItem('theme'))) { + localStorage.setItem('theme', 'dark'); + } + document.documentElement.setAttribute('data-bs-theme', localStorage.getItem('theme')); +})(); +// classes +class WorkerHandler { + work(params, callback) { + return __awaiter(this, void 0, void 0, function* () { + const worker = new Worker('./worker.js'); + worker.postMessage(params); + worker.onmessage = (e) => { + const log = new Logging(); + if (e.data[0] === 'result') { + worker.terminate(); + callback(e.data.slice(1)); + } + else if (e.data[0] === 'error') { + new Toast(e.data[1], 'bg-danger'); + const bar = new ProgressBar(); + bar.update(100, e.data[1]); + log.error(e.data[1]); + worker.terminate(); + } + else if (e.data[0] === 'progress') { + const bar = new ProgressBar(); + log.info(e.data[2]); + bar.update(e.data[1], e.data[2]); + } + else if (e.data[0] === 'warning') { + new Toast(e.data[1], 'bg-warning'); + log.warning(e.data[1]); + } + else if (e.data[0] === 'info') { + new Toast(e.data[1], 'bg-info'); + log.info(e.data[1]); + } + else if (e.data[0] === 'debug') { + log.info(e.data[1]); + } + else if (e.data[0] === 'fail') { + log.error(e.data[1]); + } + else if (e.data[0] === 'update') { + updateItemStatus(e.data[1], e.data[2]); + } + else if (e.data[0] === 'updateColors') { + updateTableColors(e.data[1], e.data[2]); + } + else if (e.data[0] === 'runCallback') { + callback(e.data.slice(1)); + } + else if (e.data[0] === 'upload-error') { + new Toast(`${e.data[1]} error. Upload failed.`, 'bg-danger'); + worker.terminate(); + callback(e.data[2]); + } + else { + console.log(`Unimplemented worker message ${e.data}`); + } + }; + }); + } +} +class Logging { + constructor() { + this.logTable = document.getElementById('logs-table'); + } + warning(msg) { + const row = this.logTable.insertRow(0); + row.innerHTML = `WARNING${msg}`; + row.classList.add('table-warning'); + } + error(msg) { + const row = this.logTable.insertRow(0); + row.innerHTML = `ERROR${msg}`; + row.classList.add('table-danger'); + } + info(msg) { + const row = this.logTable.insertRow(0); + row.innerHTML = `INFO${msg}`; + row.classList.add('table-primary'); + } +} +class ProgressBar { + constructor(barId = 'progress-bar', textId = 'progress-text') { + this.progressBar = document.getElementById('progress-bar'); + this.progressText = document.getElementById('progress-text'); + this.currentProgress = this.progressBar.getAttribute('style'); + this.currentProgress = this.currentProgress.slice(7, this.currentProgress.length - 2); + } + updateProgressBar(percent) { + this.progressBar.setAttribute('style', `width: ${percent}%;`); + } + update(percent, message, color = '') { + this.updateProgressBar(percent); + if (message) { + this.progressText.innerText = message; + } + if (!color && percent == 0) { + this.updateColor('bg-success'); + } + else if (color) { + this.updateColor(color); + } + } + updateColor(color) { + color = color + ' '; + const regx = new RegExp('\\b' + 'bg-' + '[^ ]*[ ]?\\b', 'g'); + this.progressBar.className = this.progressBar.className.replace(regx, color); + } + addProgressBar(percent, message = null) { + this.update(percent + this.currentProgress, message); + } + getProgress() { + return { + 'percent': this.currentProgress, + 'message': this.progressText.innerText, + }; + } +} +class Toast { + // popup thingy in top right corner + constructor(newMessage, color = 'bg-primary') { + this.toastContainer = document.getElementById('toastPlacement'); + this.newToast(newMessage, color); + } + newToast(message, color) { + const toast = document.createElement('div'); + toast.setAttribute('class', `toast d-flex align-items-center border-0 text-white ${color}`); + toast.innerHTML = `
    ${message}
    `; + const bsToast = new bootstrap.Toast(toast); + this.toastContainer.appendChild(toast); + bsToast.show(); + toast.addEventListener('hidden.bs.toast', (e) => { + e.target.remove(); + }); + } +} +class Item { + // add more properties later (e.g manufacturer, part num, etc.) + constructor(itemnumber = 0, description, issueunit, commoditygroup, glclass, siteID = '', storeroomname = '', vendorname = '', cataloguenum = '', series = 91, longdescription = '', assetprefix = '', assetseed = '', jpnum = '', inspectionrequired = 0, isimport = 0, rotating = 0) { + this.itemnumber = itemnumber; + this.series = series; + this.description = description; + this.issueunit = issueunit; + this.commoditygroup = commoditygroup; + this.glclass = glclass; + this.longdescription = longdescription; + this.assetprefix = assetprefix; + this.assetseed = assetseed; + this.jpnum = jpnum; + this.inspectionrequired = inspectionrequired; + this.isimport = isimport; + this.rotating = rotating; + this.siteID = siteID; + this.storeroomname = storeroomname; + this.vendorname = vendorname; + this.cataloguenum = cataloguenum; + } +} +// functions +// general +function fixSwitch() { + document.getElementById('dark-mode-switch').checked = (localStorage.getItem('theme') === 'dark' ? true : false); +} +function toTop() { + const element = document.getElementsByTagName('main'); + element[0].scrollTop = 0; // For Chrome, Firefox, IE and Opera +} +function toEnd() { + const element = document.getElementsByTagName('main'); + element[0].scrollTop = element[0].scrollHeight; // For Chrome, Firefox, IE and Opera +} +// theme related +function toggleTheme() { + setTheme(localStorage.getItem('theme') === 'dark' ? 'light' : 'dark'); +} +function setTheme(newTheme) { + // safety + if (localStorage.getItem('theme') === newTheme) { + return; + } + localStorage.setItem('theme', `${newTheme}`); + document.documentElement.setAttribute('data-bs-theme', newTheme); +} +function loadTheme() { + if (!(localStorage.getItem('theme'))) { + localStorage.setItem('theme', 'dark'); + } + document.documentElement.setAttribute('data-bs-theme', localStorage.getItem('theme')); + // console.log('i have run'); +} +// upload item related +function getNextNumThenUpdate(series) { + document.getElementById('error').innerHTML = 'Waiting for confirm...'; + const worker = new WorkerHandler(); + document.getElementById('confirm-btn').innerHTML = ' Loading...'; + document.getElementById('confirm-btn').disabled = true; + document.getElementById('item-itemnum').innerHTML = ' Retreiving the latest item number...'; + worker.work(['getCurItemNumber', series], updateItemInfo); + console.log('Getting new number from server'); +} +function updateItemInfo(curItemNum) { + console.log(curItemNum); + if (curItemNum[0] === 0) { + throw new Error(curItemNum[1]); + } + const itemnum = document.getElementById('interact-num'); + itemnum.value = curItemNum[1] + 1; + const desc = document.getElementById('maximo-desc'); + const uom = document.getElementById('uom-field'); + const commGroup = document.getElementById('com-group'); + const glclass = document.getElementById('gl-class'); + document.getElementById('item-itemnum').value = itemnum.value; + document.getElementById('item-desc').value = desc.value; + document.getElementById('item-uom').value = uom.value; + document.getElementById('item-commgroup').value = commGroup.value; + document.getElementById('item-glclass').value = glclass.value; + document.getElementById('confirm-btn').innerHTML = 'Upload Item'; + document.getElementById('confirm-btn').disabled = false; +} +function poppulateModal() { + const desc = document.getElementById('maximo-desc'); + const uom = document.getElementById('uom-field'); + const commGroup = document.getElementById('com-group'); + const glclass = document.getElementById('gl-class'); + document.getElementById('item-descr').value = desc.value; + document.getElementById('issue-unit').value = uom.value; + document.getElementById('comm-grp').value = commGroup.value; + document.getElementById('gl-class-new').value = glclass.value; +} +function sanitizeString(str) { + const badChars = ['<', '>']; + for (const badChar of badChars) { + str = str.replaceAll(badChar, ''); + } + str = str.replaceAll(/ /g, ' ').replaceAll(/\u00A0/g, ' '); + return str; +} +function convertToTable(pastedInput, id = '') { + let rawRows = pastedInput.split('\n'); + let numRows = rawRows.length; + let numCols = 0; + const bodyRows = []; + let diff = 0; + if (!pastedInput.toUpperCase().includes('MAXIMO')) { + numRows++; + const firstRow = ('Maximo\tDescription\tIssue Unit\tCommodity Group\tGL Class'); + rawRows = [firstRow, ...rawRows]; + } + rawRows.forEach((rawRow, idx) => { + const rawRowArray = rawRow.split('\t'); + if (rawRow == 0) { + diff--; + numRows--; + } + else { + if (rawRowArray.length > numCols) { + numCols = rawRowArray.length; + } + bodyRows.push(`\n`); + rawRowArray.forEach(function (value, index) { + bodyRows.push(`\t${value}\n`); + }); + if (idx == 0) { + bodyRows.push(``); + } + else { + bodyRows.push(`pending`); + } + bodyRows.push(`\n`); + } + }); + const table = ` + +${bodyRows.join('')} +
    + `; + return table; +} +// Highlights cells red for any cell with invalid data +function updateTableColors(itemindex, category) { + const colLoc = { + description: -1, + uom: -1, + commGroup: -1, + glClass: -1, + maximo: -1, + vendor: -1, + storeroom: -1, + catNum: -1, + siteID: -1, + }; + const table = document.getElementById('batch-items-table'); + // Assign column locations + const cols = parseInt(table.getAttribute('data-cols')); + // go through first row to find headings. + for (let i = 1; i <= cols; i++) { + // get a cell in the table by its id + const cell = document.getElementById('1-' + i); + // see if cell value matches any of the required parameters to create an item object + if (cell.innerHTML.toUpperCase() === 'DESCRIPTION') { + colLoc.description = i; + } + else if (cell.innerHTML.toUpperCase() === 'UOM' || cell.innerHTML.toUpperCase() === 'ISSUE UNIT') { + colLoc.uom = i; + } + else if (cell.innerHTML.toUpperCase() === 'COMMODITY GROUP' || cell.innerHTML.toUpperCase() === 'COMM GROUP') { + colLoc.commGroup = i; + } + else if (cell.innerHTML.toUpperCase() === 'GL CLASS') { + colLoc.glClass = i; + } + else if (cell.innerHTML.toUpperCase() === 'SITEID' || cell.innerHTML.toUpperCase() === 'SITE') { + colLoc.siteID = i; + } + else if (cell.innerHTML.toUpperCase() === 'STOREROOM' || cell.innerHTML.toUpperCase() === 'STOREROOM') { + colLoc.storeroom = i; + } + else if (cell.innerHTML.toUpperCase() === 'VENDOR' || cell.innerHTML.toUpperCase() === 'VENDOR NUMBER') { + colLoc.vendor = i; + } + else if (cell.innerHTML.toUpperCase() === 'CAT NUMBER' || cell.innerHTML.toUpperCase() === 'CATALOG NUMBER' || cell.innerHTML.toUpperCase() === 'CATALOGUE NUMBER') { + colLoc.catNum = i; + } + else if (cell.innerHTML.toUpperCase() === 'MAXIMO' || cell.innerHTML.toUpperCase() === 'ITEM NUMBER') { + colLoc.maximo = i; + } + } + const colNum = colLoc[category]; + const cell = document.getElementById(`${itemindex}-${colNum}`); + // change color of cell + cell.classList.add('table-danger'); +} +function updateItemStatus(status, itemindex) { + // Changes item status column to reflect status + const statusimg = document.getElementById(`item-${itemindex}-status`); + if (status == 'fail') { + statusimg.innerHTML = `close`; + } + else if (status == 'success') { + statusimg.innerHTML = `done`; + } + else if (status == 'loading') { + statusimg.innerHTML = `
    `; + } + else if (status == 'error') { + statusimg.innerHTML = `error`; + } + else if (status == 'partial') { + statusimg.innerHTML = `warning`; + } + else { + statusimg.innerHTML = `pending`; + } +} +function fileBase64(file) { + return new Promise((resolve, reject) => { + const reader = new FileReader(); + reader.readAsDataURL(file); + reader.onload = function () { + resolve(reader.result); + }; + reader.onerror = function (error) { + reject(error); + }; + }); +} diff --git a/built/misc/constants.js b/built/misc/constants.js new file mode 100644 index 0000000..9b9a45f --- /dev/null +++ b/built/misc/constants.js @@ -0,0 +1,7 @@ +"use strict"; +//for global constants. used for debugging +const CONSTANTS = Object.freeze({ + ENV: true ? 'prod.manage.prod' : 'test.manage.test', //set true to use production environment and set false for test environment. + OPEN_DEV_TOOLS: true, //set true to open dev tools on application launch +}); +module.exports = CONSTANTS; diff --git a/built/misc/exceljs.js b/built/misc/exceljs.js new file mode 100644 index 0000000..978e81a --- /dev/null +++ b/built/misc/exceljs.js @@ -0,0 +1,369 @@ +"use strict"; +// don't use +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +const Exceljs = require('exceljs'); +class Spreadsheet { + constructor(filePath) { + this.filePath = filePath; + } + getDescriptions(params) { + return __awaiter(this, void 0, void 0, function* () { + // returns all the english descriptions on a workbook + // {wsname:string, maxNumCol:string, description:[string], manufacturerer: string, startingRow: int} + const wb = new Exceljs.Workbook(); + yield wb.xlsx.readFile(this.filePath); + const ws = wb.getWorksheet(params.wsname); + const lastRow = ws.lastRow.number; + let descriptions = []; + let description = ""; + for (let i = params.startingRow; i <= lastRow; i++) { + for (const col of params.descriptions) { + description = `${description},${ws.getCell(`${col}${i}`).text}`; + } + descriptions.push({ + maxNum: ws.getCell(`${params.maxNumCol}${i}`).text, + description: description, + manufacturer: ws.getCell(`${params.manufacturerer}${i}`).text + }); + description = ""; + } + return descriptions; + }); + } + getTranslations() { + return __awaiter(this, void 0, void 0, function* () { + // read all translation definitions from a workbook for import + const wb = new Exceljs.Workbook(); + yield wb.xlsx.readFile(this.filePath); + const ws = wb.worksheets[0]; //assume there is only 1 worksheet and its the one we want + const lastRow = ws.lastRow.number; + const languages = ws.getRow(1).cellCount; + let lang_codes = []; + for (let i = 2; i <= languages; i++) { + lang_codes.push(ws.getCell(1, i).text.toUpperCase()); + } + let translations = []; + for (let i = 2; i <= lastRow; i++) { + for (let j = 2; j <= languages; j++) { + translations.push({ + english: ws.getCell(i, 1).text.toUpperCase().trim(), + lang_code: lang_codes[j - 2], + translation: ws.getCell(i, j).text.toUpperCase().trim() + }); + } + } + return translations; + }); + } + saveTranslations(data) { + return __awaiter(this, void 0, void 0, function* () { + // save translated descriptions to excel file + // multiple worksheets one for each language, and one for missing words + const wb = new Exceljs.Workbook(); + let ws; + let row; + let rowCount; + for (const lang of data.langs) { + rowCount = 2; + ws = wb.addWorksheet(lang); + row = ws.getRow(1); + row.values = ['Maximo Item Number', 'English Description', `${lang} Description`, 'Manufacturer Name']; + for (const item of data.item) { + row = ws.getRow(rowCount); + row.values = [item.maxNum, item.description, item[lang], item.manufacturer || "-"]; + rowCount++; + } + } + if (data.missing) { + ws = wb.addWorksheet('Missing Translations'); + rowCount = 1; + for (const change of data.missing) { + row = ws.getRow(rowCount); + row.values = [change]; + rowCount++; + } + } + yield wb.xlsx.writeFile(this.filePath); + postMessage(['result', 'done']); + }); + } + saveObserListChanges(data) { + return __awaiter(this, void 0, void 0, function* () { + // expected attributes for data: domain.changes, domain.delete + // saves observation list changes data, expect data in object format + const wb = new Exceljs.Workbook(); + let ws; + let row; + let rowCount; + if (data.domainDef.changes) { + rowCount = 3; + ws = wb.addWorksheet('ChangeCondDomDef'); + row = ws.getRow(1); + row.values = ['IKO_Import', 'IKO_ALNDOMAIN', 'AddChange', 'EN']; + row = ws.getRow(2); + row.values = ['DOMAINID', 'DESCRIPTION', 'DOMAINTYPE', 'MAXTYPE', 'LENGTH']; + for (const change of data.domainDef.changes) { + row = ws.getRow(rowCount); + row.values = [change.list_id, change.inspect, 'ALN', 'UPPER', '3']; + rowCount++; + } + } + if (data.domainDef.delete) { + ws = wb.addWorksheet('RemoveCondDomDef'); + row = ws.getRow(1); + row.values = ['DOMAINID']; + rowCount = 2; + for (const change of data.domainDef.delete) { + row = ws.getRow(rowCount); + row.values = [change]; + rowCount++; + } + } + if (data.domainVal.changes) { + let rowCount = 3; + ws = wb.addWorksheet('ChangeCondDomVal'); + row = ws.getRow(1); + row.values = ['IKO_Import', 'IKO_ALNDOMAIN', 'AddChange', 'EN']; + row = ws.getRow(2); + row.values = ['DOMAINID', 'VALUE', 'AD_DESCRIPTION']; + for (const observ of data.domainVal.changes) { + row = ws.getRow(rowCount); + row.values = [observ.meter, observ.id_value, observ.observation]; + rowCount++; + } + } + if (data.domainVal.delete) { + ws = wb.addWorksheet('RemoveCondDomVal'); + row = ws.getRow(1); + row.values = ['DOMAINID:VALUE']; + rowCount = 2; + for (const change of data.domainVal.delete) { + row = ws.getRow(rowCount); + row.values = [change]; + rowCount++; + } + } + if (data.meter.changes) { + let rowCount = 3; + ws = wb.addWorksheet('ChangeCondMeter'); + row = ws.getRow(1); + row.values = ['IKO_Import', 'IKO_METER', 'AddChange', 'EN']; + row = ws.getRow(2); + row.values = ['METERNAME', 'DESCRIPTION', 'METERTYPE', 'DOMAINID']; + for (const observ of data.meter.changes) { + row = ws.getRow(rowCount); + row.values = [`${observ.list_id.slice(2)}01`, observ.inspect, 'CHARACTERISTIC', observ.list_id]; + rowCount++; + } + } + if (data.meter.delete) { + ws = wb.addWorksheet('RemoveCondMeter'); + row = ws.getRow(1); + row.values = ['DOMAINID']; + rowCount = 2; + for (const change of data.meter.delete) { + row = ws.getRow(rowCount); + row.values = [change]; + rowCount++; + } + } + if (data.jobTask.changes) { + let rowCount = 3; + ws = wb.addWorksheet('ChangeJobTask'); + row = ws.getRow(1); + row.values = ['IKO_Import', 'IKO_JOBTASK', 'AddChange', 'EN']; + row = ws.getRow(2); + row.values = [ + 'ORGID', 'SITEID', 'JPNUM', 'PLUSCREVNUM', 'JPTASK', + 'PLUSCJPREVNUM', 'METERNAME', 'DESCRIPTION', + 'DESCRIPTION_LONGDESCRIPTION', '', 'OLD DESCRIPTION', 'OLD EXTENDED DESCRIPTION' + ]; + for (const jobTask of data.jobTask.changes) { + row = ws.getRow(rowCount); + row.values = [ + jobTask.orgid, jobTask.siteid, jobTask.jpnum, + 0, jobTask.jptask, 0, jobTask.metername, jobTask.desc, + jobTask.ext_desc, '', jobTask.old_desc, jobTask.old_ext_desc + ]; + rowCount++; + } + } + if (data.jobTask.delete) { + ws = wb.addWorksheet('RemoveJobTask'); + row = ws.getRow(1); + row.values = [ + 'ORGID', 'SITEID', 'JPNUM', 'PLUSCREVNUM', 'JPTASK', + 'PLUSCJPREVNUM', 'METERNAME', 'DESCRIPTION', + 'DESCRIPTION_LONGDESCRIPTION' + ]; + rowCount = 2; + for (const jobTask of data.jobTask.delete) { + row = ws.getRow(rowCount); + row.values = [ + jobTask.orgid, jobTask.siteid, jobTask.jpnum, + 0, jobTask.jptask, 0, jobTask.metername, jobTask.desc, + jobTask.ext_desc + ]; + rowCount++; + } + } + yield wb.xlsx.writeFile(this.filePath); + postMessage(['result', 'done']); + }); + } + getJobTasks() { + return __awaiter(this, void 0, void 0, function* () { + // getting extended description from REST API takes way too long, so require a worksheet with information retrived from the DB + // select jpnum, jptask, description, orgid, siteid, metername, ldtext from + // (select jpnum, jptask, description, orgid, siteid, metername, jobtaskid from jobtask where metername is not null) as t1 + // left join + // (select ldtext, ldkey from longdescription where ldownertable = 'JOBTASK') as t2 + // on t1.jobtaskid = t2.ldkey + const wb = new Exceljs.Workbook(); + yield wb.xlsx.readFile(this.filePath); + const ws = wb.worksheets[0]; //assume there is only 1 worksheet and its the one we want + const lastRow = ws.lastRow.number; + let row = ws.getRow(1).values.slice(1); + let jobTasks = []; + if (row.equals(['jpnum', 'jptask', 'description', 'orgid', 'siteid', 'metername', 'ldtext'])) { + console.log('pass'); + } + else { + postMessage(['error', `Please Check Column Heading for JobTask Input expecting ${['jpnum', 'jptask', 'description', 'orgid', 'siteid', 'metername', 'ldtext']}`]); + } + for (let i = 2; i <= lastRow; i++) { + row = ws.getRow(i).values; + jobTasks.push({ + jpnum: row[1], + metername: row[6], + orgid: row[4], + siteid: row[3], + jptask: row[2], + desc: row[3], + ext_desc: row[7] + }); + } + return jobTasks; + }); + } + readObservList(wsname) { + return __awaiter(this, void 0, void 0, function* () { + // loop through the excel sheet to extract information + const wb = new Exceljs.Workbook(); + yield wb.xlsx.readFile(this.filePath); + const ws = wb.getWorksheet(wsname); + let meter = ''; + let meters = []; + let observation = {}; + let observations = []; + let inspect; + let desc; + let temp1; + let temp2; + let temp3; + ws.eachRow(function (row, rowNumber) { + if (row.values[1] !== undefined) { + // check if row has meter definitions + meter = removeRichText(row.values[1]); + if (meters[meter]) { + postMessage(['error', `Duplicate Meter: ${meter} on Row: ${rowNumber}`]); + } + else { + inspect = removeRichText(row.values[3]); + desc = removeRichText(row.values[5]); + meters.push({ + name: meter, + list_id: `M-${meter}`, + inspect: inspect, + desc: `Inspect ${inspect}`, + ext_desc: desc, + search_str: `M-${meter}~${inspect}` + }); + temp1 = removeRichText(row.values[6]); + temp2 = removeRichText(row.values[7]); + temp3 = removeRichText(row.values[8]); + observation = { + meter: meter, + id_value: temp1, + observation: temp2, + action: temp3, + search_str: `${meter}~${temp1}` + }; + observations.push(observation); + } + } + else if (row.values[6] !== undefined) { + // it is just a observation row + temp1 = removeRichText(row.values[6]); + temp2 = removeRichText(row.values[7]); + temp3 = removeRichText(row.values[8]); + observation = { + meter: meter, + id_value: temp1, + observation: temp2, + action: temp3, + search_str: `${meter}~${temp1}` + }; + observations.push(observation); + } + }); + postMessage(['result', [meters.slice(1), observations.slice(1)]]); + }); + } +} +function removeRichText(value) { + // when reading cells from a row object there is no option for removing richtext + if (typeof (value) === "string") { + return value; + } + else if (typeof (value) == "object") { + let temp = ""; + for (const part of value.richText) + temp = `${temp}${part.text}`; + return temp; + } + else if (typeof (value) == "undefined") { + return undefined; + } + else { + postMessage(['error', `Unknown cell type: ${typeof (value)}`]); + } +} +//https://stackoverflow.com/a/14853974 +// Warn if overriding existing method +// Check if two arrays are equal +if (Array.prototype.equals) + console.warn("Overriding existing Array.prototype.equals. Possible causes: New API defines the method, there's a framework conflict or you've got double inclusions in your code."); +// attach the .equals method to Array's prototype to call it on any array +Array.prototype.equals = function (array) { + // if the other array is a falsy value, return + if (!array) + return false; + // compare lengths - can save a lot of time + if (this.length != array.length) + return false; + for (var i = 0, l = this.length; i < l; i++) { + // Check if we have nested arrays + if (this[i] instanceof Array && array[i] instanceof Array) { + // recurse into the nested arrays + if (!this[i].equals(array[i])) + return false; + } + else if (this[i] != array[i]) { + // Warning - two different object instances will never be equal: {x:20} != {x:20} + return false; + } + } + return true; +}; +// Hide method from for-in loops +Object.defineProperty(Array.prototype, "equals", { enumerable: false }); +module.exports = Spreadsheet; diff --git a/built/misc/indexDB.js b/built/misc/indexDB.js new file mode 100644 index 0000000..da6999e --- /dev/null +++ b/built/misc/indexDB.js @@ -0,0 +1,508 @@ +"use strict"; +const Sql = require('better-sqlite3'); +const utils = require('./utils'); +const intersection = require('lodash/intersection'); +// https://lodash.com/docs/4.17.15#intersection +// fast library for intersection of arrays +/** + * Database Class containing more Database functions + */ +class Database { + /** + * Open default db file + */ + constructor() { + this.db = new Sql(`${process.env.APPDATA}/EAM Spare Parts/program.db`); // , { verbose: console.log }); + } + createTables() { + const dropTables = this.db.prepare('DROP TABLE IF EXISTS manufacturers'); + const dropTables2 = this.db.prepare('DROP TABLE IF EXISTS abbreviations'); + const dropTables3 = this.db.prepare('DROP TABLE IF EXISTS workingDescription'); + const dropTables4 = this.db.prepare('DROP TABLE IF EXISTS itemCache'); + const dropTables5 = this.db.prepare('DROP TABLE IF EXISTS itemDescAnalysis'); + const dropTables6 = this.db.prepare('DROP TABLE IF EXISTS inventoryCache'); + const runQuery2 = this.db.transaction(() => { + dropTables.run(); + dropTables2.run(); + dropTables3.run(); + dropTables4.run(); + dropTables5.run(); + dropTables6.run(); + }); + runQuery2(); + const createTable1 = this.db.prepare(`CREATE TABLE manufacturers( + id INTEGER PRIMARY KEY, + full_name TEXT NOT NULL COLLATE NOCASE, + short_name TEXT NOT NULL UNIQUE COLLATE NOCASE, + homepage TEXT, + changed_date TEXT COLLATE NOCASE + );`); + const createTable2 = this.db.prepare(`CREATE TABLE abbreviations( + id INTEGER PRIMARY KEY, + orig_text TEXT NOT NULL COLLATE NOCASE, + replace_text TEXT NOT NULL COLLATE NOCASE + )`); + const createTable3 = this.db.prepare(`CREATE TABLE workingDescription ( + row INTEGER NOT NULL, + description TEXT NOT NULL COLLATE NOCASE, + analysis TEXT, + related TEXT, + translate TEXT, + orgid TEXT COLLATE NOCASE + )`); + const createTable4 = this.db.prepare(`CREATE TABLE itemCache ( + itemnum TEXT PRIMARY KEY, + description TEXT NOT NULL COLLATE NOCASE, + details TEXT COLLATE NOCASE, + changed_date TEXT COLLATE NOCASE, + search_text TEXT COLLATE NOCASE, + gl_class TEXT COLLATE NOCASE, + uom TEXT COLLATE NOCASE, + commodity_group TEXT COLLATE NOCASE, + ext_search_text TEXT COLLATE NOCASE, + ext_description TEXT COLLATE NOCASE + )`); + const createTable5 = this.db.prepare(`CREATE TABLE itemDescAnalysis ( + tree TEXT PRIMARY KEY COLLATE NOCASE, + descriptor TEXT NOT NULL COLLATE NOCASE, + parent TEXT, + count INTEGER, + level INTEGER + )`); + const createTable6 = this.db.prepare(`CREATE TABLE inventoryCache ( + itemnum TEXT NOT NULL COLLATE NOCASE, + siteid TEXT NOT NULL COLLATE NOCASE, + catalogcode TEXT COLLATE NOCASE, + modelnum TEXT COLLATE NOCASE, + vendor TEXT COLLATE NOCASE, + manufacturer TEXT COLLATE NOCASE, + companyname TEXT COLLATE NOCASE, + rowstamp TEXT, + location TEXT NOT NULL COLLATE NOCASE, + PRIMARY KEY (itemnum, location) + )`); + const runQuery = this.db.transaction(() => { + createTable1.run(); + createTable2.run(); + createTable3.run(); + createTable4.run(); + createTable5.run(); + createTable6.run(); + }); + runQuery(); + console.log('refreshed tables'); + } + clearItemCache() { + const stmt = this.db.prepare(`DELETE FROM itemCache`); + stmt.run(); + } + saveInventoryCache(data) { + const dataDB = []; + for (let i = 0; i < data.length; i++) { + dataDB.push({ + itemnum: data[i][0], + siteid: data[i][1], + catalogcode: data[i][2], + modelnum: data[i][3], + vendor: data[i][4], + manufacturer: data[i][5], + companyname: data[i][6], + rowstamp: data[i][8], + location: data[i][7], + }); + } + const insert = this.db.prepare(`insert or replace into inventoryCache ( + itemnum, siteid, catalogcode, modelnum, vendor, manufacturer, companyname, rowstamp, location) + VALUES (@itemnum, @siteid, @catalogcode, @modelnum, @vendor, @manufacturer, @companyname, @rowstamp, @location)`); + const insertMany = this.db.transaction((dataDB) => { + for (const item of dataDB) + insert.run(item); + }); + insertMany(dataDB); + } + /** + * For Updated items obtained from Maximo. + * Update the extended search information + * @param {list} itemNums Data returned from Maximo + * @return {list} processed data + */ + processNewItems(itemNums) { + let stmt; + const updatedData = []; + const inventoryData = new Map(); + for (const [key, value] of itemNums) { + stmt = this.db.prepare(`SELECT * FROM itemCache WHERE itemnum = '${value}'`); + const itemDetails = stmt.get(); + stmt = this.db.prepare(`SELECT * FROM inventoryCache WHERE itemnum = '${key}'`); + const results = stmt.all(); + results.forEach((inventory) => { + const row = []; + row[0] = inventory.itemnum; + row[1] = inventory.siteid; + row[2] = inventory.catalogcode; + row[3] = inventory.modelnum; + row[4] = inventory.vendor; + row[5] = inventory.manufacturer; + row[6] = inventory.companyname; + row[7] = inventory.location; + if (inventoryData.has(inventory.itemnum)) { + for (let j = 1; j <= 7; j++) { + if (row[j] != '') { + inventoryData.get(inventory.itemnum)[j] = inventoryData.get(inventory.itemnum)[j] + '|' + row[j]; + } + } + } + else { + inventoryData.set(inventory.itemnum, row); + } + }); + updatedData.push([ + itemDetails.itemnum, + itemDetails.description, + itemDetails.changed_date, + itemDetails.gl_class, + itemDetails.uom, + itemDetails.commodity_group, + itemDetails.details, + inventoryData.get(key), + ]); + } + return updatedData; + } + /** + * save items into db + * @param {list} data from source + */ + saveItemCache(data) { + const dataDB = []; + let search = ''; + let extSearch = ''; + let extDesc = ''; + for (let i = 0; i < data.length; i++) { + if (data[i][1]) { // test if description is blank + search = data[i][1].toUpperCase(); + for (const char of utils.STRINGCLEANUP) { + search = search.replaceAll(char, ''); + } + extSearch = search; + extDesc = ''; + // add inventory data + if (data[i][7]) { + for (let j = 0; j < data[i][7].length; j++) { + if (data[i][7][j].length > 0) { + extSearch = `${extSearch}|${data[i][7][j]}`; + } + } + for (let j = 2; j < data[i][7].length; j++) { + if (data[i][7][j].length > 0) { + extDesc = `${extDesc}${data[i][7][j].replaceAll('|', ' ')} `; + } + } + } + // add item master details + if (data[i][6]) { + extSearch = `${extSearch}|${data[i][6]}`; + extDesc = `${extDesc}${data[i][6]}`; + for (const char of utils.STRINGCLEANUP) { + extSearch = extSearch.replaceAll(char, ''); + } + } + dataDB.push({ + itemnum: data[i][0], + description: data[i][1], + changed_date: data[i][2], + gl_class: data[i][3], + uom: data[i][4], + commodity_group: data[i][5], + details: data[i][6], + search_text: search, + ext_search_text: extSearch, + ext_desc: extDesc, + }); + } + } + const insert = this.db.prepare(`INSERT OR REPLACE INTO itemCache ( + itemnum, description, details, changed_date, search_text, gl_class, uom, commodity_group, ext_search_text, ext_description) + VALUES (@itemnum, @description, @details, @changed_date, @search_text, @gl_class, @uom, @commodity_group, @ext_search_text, @ext_desc)`); + const insertMany = this.db.transaction((dataDB) => { + for (const item of dataDB) + insert.run(item); + }); + insertMany(dataDB); + const manufs = this.getAllManufacturers(); + const analysis = itemOccurrence(data, manufs); + const stmt = this.db.prepare(`INSERT INTO itemDescAnalysis (tree, descriptor, parent, count, level) + VALUES (@tree, @descriptor, @parent, @count, @level) + ON CONFLICT(tree) + DO UPDATE SET count = count + @count`); + const insertMany2 = this.db.transaction((analysis) => { + for (const [key, item] of analysis) { + stmt.run({ tree: key, descriptor: item.phrase, parent: item.parent, count: item.count, level: item.level }); + } + }); + insertMany2(analysis); + console.log('finished adding analysis'); + } + getAnalysis(tree) { + const stmt = this.db.prepare('SELECT tree, descriptor, parent, count, level FROM itemDescAnalysis where tree = @tree'); + const result = stmt.get({ tree: tree }); + return result; + } + getAllWorkingDesc() { + const stmt = this.db.prepare('SELECT *, itemCache.description FROM workingDescription left join itemCache on itemCache.itemnum = workingDescription.related WHERE analysis IS NOT NULL'); + const result = stmt.all(); + return result; + } + // get the time stamp (version) of when the item cache was last updated + getVersion(type) { + let stmt; + if (type === 'maximo') { + stmt = this.db.prepare('SELECT changed_date FROM itemCache ORDER BY changed_date DESC LIMIT 1'); + } + else if (type === 'manufacturer') { + stmt = this.db.prepare('SELECT changed_date FROM manufacturers ORDER BY changed_date DESC LIMIT 1'); + } + else if (type === 'inventory') { + stmt = this.db.prepare('SELECT rowstamp FROM inventoryCache ORDER BY rowstamp DESC LIMIT 1'); + } + let version = stmt.all(); + if (version.length == 0) { + version = [{ changed_date: '2022-01-01 00:00:00' }]; + } + return version; + } + getAllManufacturers() { + const stmt = this.db.prepare('SELECT short_name FROM manufacturers'); + return stmt.all(); + } + // populate the database with abbrivations + populateAbbr(data) { + const dataDB = []; + for (let i = 0; i < data.length; i++) { + dataDB.push({ orig_text: data[i][0], replace_text: data[i][1] }); + } + const insert = this.db.prepare(`INSERT INTO abbreviations ( + orig_text, replace_text) + VALUES (@orig_text, @replace_text)`); + const insertMany = this.db.transaction((data) => { + for (const item of data) + insert.run(item); + }); + insertMany(dataDB); + } + // populate the database with manufacturers + saveManufacturers(data) { + const dataDB = []; + for (let i = 0; i < data.length; i++) { + dataDB.push({ full_name: data[i][2], short_name: data[i][0], homepage: data[i][3], changed_date: data[i][1] }); + } + const insert = this.db.prepare(`INSERT OR REPLACE INTO manufacturers ( + full_name, short_name, homepage, changed_date) + VALUES (@full_name, @short_name, @homepage, @changed_date)`); + const insertMany = this.db.transaction((data) => { + for (const item of data) + insert.run(item); + }); + insertMany(dataDB); + } + // checks if the name given is a manufacturer + isManufacturer(name) { + const stmt = this.db.prepare(`SELECT short_name FROM manufacturers where full_name = ? or short_name = ?`); + return stmt.get([name, name]); + } + // check if the phrase given has a known abbrivation + isAbbreviation(phase) { + const result = this.db.prepare(`SELECT replace_text from abbreviations where orig_text = ?`); + return result.get([phase]); + } + // saves the current set of descriptions being worked on into the database + saveDescription(data) { + const dataDB = []; + for (const [, desc] of Object.entries(data)) { + dataDB.push({ row: desc[0], description: desc[1] }); + } + console.log('starting to clear'); + let stmt = this.db.prepare(`DELETE FROM workingDescription`); + stmt.run(); + stmt = this.db.prepare(`INSERT INTO workingDescription ( + row, description) + VALUES (@row, @description)`); + let count = 0; + const insertMany = this.db.transaction((dataDB) => { + for (const item of dataDB) { + if (item.description.length > 0) { + stmt.run(item); + count++; + } + } + }); + insertMany(dataDB); + console.log('finished adding'); + return count; + } + saveDescriptionAnalysis(data, row) { + const stmt = this.db.prepare('UPDATE workingDescription SET analysis = ?, related = ? WHERE row = ?'); + stmt.run(JSON.stringify(data), data.related, row); + } + getDescription(row) { + const result = this.db.prepare(`SELECT * FROM workingDescription WHERE row >= '${row}' ORDER BY row ASC LIMIT 1`); + return result.get(); + } + loadItem(itemnum) { + let result = this.db.prepare(`SELECT itemnum, description, gl_class, uom, commodity_group from itemCache where itemnum = '${itemnum}'`); + result = result.get(); + return result; + } + findRelated(data, ext = false, postmessage) { + const itemDict = {}; + for (const char of utils.STRINGCLEANUP) { + data = data.replaceAll(char, ','); + } + const phrases = data.split(','); + let result = []; + postMessage(['progress', 25, 'Getting Item Descriptions From Maximo']); + for (let i = 0; i < phrases.length; i++) { + if (phrases[i].length > 1) { // ignore single characters searches since they add too much time + result.push(this.fetchAndObjectify(phrases[i], ext, itemDict)); + postMessage(['progress', 75, 'Processing Item Descriptions From Maximo']); // change this to per phrase + } + } + result = result.filter((item) => item !== false); + if (result.length) { + let arrayAsNum = [...Array(result.length).keys()]; // create an array with only integers to find combinations + arrayAsNum = getCombinations(arrayAsNum); + const intersections = []; + for (let i = arrayAsNum.length; i > 0; i--) { // convert combination of integers to combination of arrays + const holder = []; + arrayAsNum[i - 1].forEach((index) => { + holder.push(result[index]); + }); + intersections.push([holder.length, intersection(...holder)]); + } + if (postmessage) { + postMessage(['result', matchAndScore(intersections), itemDict, data]); + } + else { + return [matchAndScore(intersections), itemDict, data]; + } + } + else { + postMessage(['warning', 'No related items returned from Maximo']); + postMessage(['result', false]); + } + } + fetchAndObjectify(phrase, ext, itemDict) { + phrase = phrase.toUpperCase(); + postMessage(['debug', `Getting item from cache: "${phrase}"`]); + let stmt; + if (ext) { + stmt = this.db.prepare(`SELECT * from itemCache where ext_search_text like ?`); + } + else { + stmt = this.db.prepare(`SELECT * from itemCache where search_text like ?`); + } + const result = stmt.all(`%${phrase}%`); + const itemNums = []; + result.forEach((item) => { + itemNums.push(item.itemnum); + if (ext) { + let desc = item.ext_search_text; + const idx = desc.indexOf('|'); // find the pipe symbol + // if there is a pipe symbol, then there is additional info after it + if (idx !== -1) { + let itemInfo = desc.slice(idx + 12); // stuff after the pipe symbol + itemInfo = itemInfo.replaceAll('NULL', '').replaceAll('|', ' '); + desc = item.description + '|' + itemInfo; + } + else { // no pipe symbol, just use the description + desc = item.description + '|'; + } + itemDict[item.itemnum] = [desc, item.gl_class, item.uom, item.commodity_group]; + } + else { + itemDict[item.itemnum] = [item.description, item.gl_class, item.uom, item.commodity_group]; + } + }); + return itemNums; + } +} +module.exports = Database; +function matchAndScore(data) { + postMessage(['progress', 80, 'Processing Item Descriptions']); + const numPhases = data[0][0]; + const matchedScores = {}; + const saved = {}; + data.forEach((item) => { + const score = item[0] / numPhases; + if (!(score in matchedScores)) { + matchedScores[score] = []; + } + item[1].forEach((itemNum) => { + if (!(itemNum in saved)) { + matchedScores[score].push(itemNum); + saved[itemNum] = 1; + } + }); + }); + return matchedScores; +} +// https://stackoverflow.com/a/59942031 +// Generate all possible non duplicate combinations of the arrays +function getCombinations(valuesArray) { + const combi = []; + let temp = []; + const slent = Math.pow(2, valuesArray.length); + for (let i = 0; i < slent; i++) { + temp = []; + for (let j = 0; j < valuesArray.length; j++) { + if ((i & Math.pow(2, j))) { + temp.push(valuesArray[j]); + } + } + if (temp.length > 0) { + combi.push(temp); + } + } + combi.sort((a, b) => a.length - b.length); + return combi; +} +function itemOccurrence(data, manufs) { + const dataProcessed = new Map(); + let description; + let level = 0; + let tree = ''; + let parent = ''; + const regex = /\d+/g; + for (let i = 0; i < data.length; i++) { + level = 0; + tree = ''; + parent = ''; + if (data[i][1]) { // test if description is blank + description = data[i][1].split(','); + for (let j = 0; j < description.length; j++) { + if (!(description[j].match(regex)) && !(manufs.includes(description[j]))) { + level++; + if (tree.length > 0) { + tree = tree + ',' + description[j]; + } + else { + tree = description[j]; + } + if (dataProcessed.has(tree)) { + dataProcessed.get(tree).count++; + } + else { + dataProcessed.set(tree, { + phrase: description[j], + parent: parent, + count: 1, + level: level, + }); + } + parent = description[j]; + } + } + } + } + return dataProcessed; +} diff --git a/built/misc/maximo.js b/built/misc/maximo.js new file mode 100644 index 0000000..b4400a2 --- /dev/null +++ b/built/misc/maximo.js @@ -0,0 +1,472 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +// various functions for fetching data from maximo rest api +const SharedDatabase = require('./sharedDB'); +const CONSTANTS = require('./constants.js'); +/** + * Class for all calls to Maximo + */ +class Maximo { + constructor() { + this.shareDB = new SharedDatabase(); + this.login = this.shareDB.getPassword(); + } + getMeters() { + return __awaiter(this, void 0, void 0, function* () { + let response; + let nextpage = true; + let pageno = 1; + const meters = []; + while (nextpage) { + try { + response = yield fetch(`https://${CONSTANTS.ENV}.iko.max-it-eam.com/maximo/api/os/iko_meter?lean=1&pageno=${pageno}&oslc.pageSize=100&oslc.select=*&oslc.where=domainid%3D%22M-%25%22`, { + headers: { + 'apikey': this.login.userid, + }, + }); + } + catch (err) { + postMessage(['error', 'Failed to fetch Data from Maximo, Please Check Network', err]); + return false; + } + const content = yield response.json(); + if (content['responseInfo']['nextPage']) { + pageno = pageno + 1; + } + else { + nextpage = false; + } + content['member'].forEach((meter) => { + meters.push({ + list_id: meter['domainid'], + inspect: meter['description'].slice(0, meter['description'].length - 9), + metername: meter['metername'], + }); + }); + } + return meters; + }); + } + getObservations() { + return __awaiter(this, void 0, void 0, function* () { + // return meters and observations + let response; + let nextpage = true; + let pageno = 1; + const meters = []; + const observations = []; + while (nextpage) { + try { + response = yield fetch(`https://${CONSTANTS.ENV}.iko.max-it-eam.com/maximo/api/os/iko_alndomain?lean=1&pageno=${pageno}&oslc.where=domainid%3D%22M-%25%22&oslc.pageSize=100&oslc.select=alndomain%2Cdomainid%2Cdescription`, { + headers: { + 'apikey': this.login.userid, + }, + }); + } + catch (err) { + postMessage(['error', 'Failed to fetch Data from Maximo, Please Check Network', err]); + return false; + } + const content = yield response.json(); + if (content['responseInfo']['nextPage']) { + pageno = pageno + 1; + } + else { + nextpage = false; + } + content['member'].forEach((meter) => { + meters.push({ + list_id: meter['domainid'], + inspect: meter['description'], + search_str: `${meter['domainid']}~${meter['description']}`, + }); + if (meter['alndomain']) { + meter['alndomain'].forEach((observation) => { + observations.push({ + meter: meter['domainid'].slice(2), + id_value: observation['value'], + observation: observation['description'], + search_str: `${meter['domainid'].slice(2)}~${observation['value']}`, + }); + }); + } + else { + postMessage(['warning', `Meter: ${meter['domainid']} has no observation codes`]); + } + }); + } + postMessage(['result', [meters, observations]]); + }); + } + /** + * get updated inventory records from Maximo + * @param {int} rowstamp timestamp of latest cached inventory item + */ + getNewInventory(rowstamp) { + return __awaiter(this, void 0, void 0, function* () { + let response; + try { + response = yield fetch(`https://${CONSTANTS.ENV}.iko.max-it-eam.com/maximo/api/os/iko_inventory?lean=1&oslc.select=vendor,vendor.name,manufacturer,siteid,modelnum,itemnum,catalogcode,location&fetchmodedelta=1&lastfetchts=${rowstamp}`, { + headers: { + 'apikey': this.login.userid, + }, + }); + } + catch (err) { + postMessage(['warning', 'Failed to fetch Data from Maximo, Please Check Network (1)', err]); + return false; + } + const content = yield response.json(); + const inventory = []; + if (content['Error']) { // content["Error"]["message"] + postMessage(['warning', content['Error']]); + postMessage(['warning', 'Failed to fetch Data from Maximo, Please Check Network (2)']); + yield new Promise((resolve) => setTimeout(resolve, 5000)); + } + else { + const newRowStamp = response.headers.get('maxrowstamp'); + content['member'].forEach((item) => { + var _a, _b, _c, _d, _e, _f; + inventory.push([ + item['itemnum'], + item['siteid'], + (_a = item['catalogcode']) !== null && _a !== void 0 ? _a : '', + (_b = item['modelnum']) !== null && _b !== void 0 ? _b : '', + (_c = item['$alias_this_attr$vendor']) !== null && _c !== void 0 ? _c : '', + (_d = item['manufacturer']) !== null && _d !== void 0 ? _d : '', + (_e = item['vendor']['name']) !== null && _e !== void 0 ? _e : '', + (_f = item['location']) !== null && _f !== void 0 ? _f : '', + newRowStamp, + ]); + }); + return [inventory, newRowStamp]; + } + }); + } + /** + * get updated item records from Maximo + * @param {int} date timestamp of latest cached item + */ + getNewItems(date) { + return __awaiter(this, void 0, void 0, function* () { + date = date.replace(' ', 'T'); + let response; + try { + response = yield fetch(`https://${CONSTANTS.ENV}.iko.max-it-eam.com/maximo/api/os/mxitem?lean=1&oslc.where=in22>"${date}" and itemnum="9%25"&oslc.select=itemnum,in22,description,issueunit,commoditygroup,externalrefid,status,description_longdescription`, { + headers: { + 'apikey': this.login.userid, + }, + }); + } + catch (err) { + postMessage(['warning', 'Failed to fetch Data from Maximo, Please Check Network (1)', err]); + return false; + } + const content = yield response.json(); + const items = []; + let previousDate = [new Date('2000-01-01'), '']; + let newDate = ''; + if (content['Error']) { // content["Error"]["message"] + postMessage(['warning', content['Error']]); + postMessage(['warning', 'Failed to fetch Data from Maximo, Please Check Network (2)']); + yield new Promise((resolve) => setTimeout(resolve, 5000)); + } + else { + content['member'].forEach((item) => { + newDate = item['in22'].replace('T', ' ').slice(0, -6); + items.push([ + item['itemnum'], + item['description'], + newDate, + item['externalrefid'], + item['issueunit'], + item['commoditygroup'], + item['description_longdescription'], + ]); + if (previousDate[0] < new Date(newDate)) { + previousDate = [new Date(newDate), newDate]; + } + }); + return [items, previousDate[1]]; + } + }); + } + getNewManufacturers(date) { + var _a; + return __awaiter(this, void 0, void 0, function* () { + date = date.replace(' ', 'T'); + let response; + try { + response = yield fetch(`https://${CONSTANTS.ENV}.iko.max-it-eam.com/maximo/api/os/IKO_COMPMASTER?lean=1&oslc.where=type="M" and changedate>"${date}"&oslc.select=company,name,homepage,changedate`, { + headers: { + 'apikey': this.login.userid, + }, + }); + } + catch (err) { + postMessage(['warning', 'Failed to fetch Data from Maximo, Please Check Network (1)', err]); + return false; + } + const content = yield response.json(); + const items = []; + let previousDate = [new Date('2000-01-01'), '']; + let newDate = ''; + if (content['Error']) { // content["Error"]["message"] + postMessage(['warning', (_a = content['Error']['message']) !== null && _a !== void 0 ? _a : content['Error']]); + postMessage(['warning', 'Failed to fetch Data from Maximo, Please Check Network (2)']); + yield new Promise((resolve) => setTimeout(resolve, 5000)); + } + else { + content['member'].forEach((item) => { + newDate = item['changedate'].replace('T', ' ').slice(0, -6); + items.push([ + item['company'], + newDate, + item['name'], + item['homepage'], + ]); + if (previousDate[0] < new Date(newDate)) { + previousDate = [new Date(newDate), newDate]; + } + }); + return [items, previousDate[1]]; + } + }); + } + /** + * + * @param {string} numSeries item series (99, 98, 91) + * @return {number} latest item number + */ + getCurItemNumber(numSeries) { + return __awaiter(this, void 0, void 0, function* () { + let response; + try { + // %25 is % + response = yield fetch(`https://${CONSTANTS.ENV}.iko.max-it-eam.com/maximo/api/os/mxitem?lean=1&oslc.where=status="active" and itemnum="${numSeries}%25"&_lid=${this.login.userid}&_lpwd=${this.login.password}&oslc.select=itemnum&oslc.pageSize=1&oslc.orderBy=-itemnum`, { + headers: { + 'apikey': this.login.userid, + }, + }); + } + catch (err) { + postMessage(['debug', 'Failed to fetch data from Maximo, please check network (1)']); // this likely doesnt work, probably remove it + throw new Error('Failed to fetch data from Maximo, please check network (1)'); + } + const content = yield response.json(); + if (content['Error']) { // content["Error"]["message"] + postMessage(['debug', 'Failed to fetch Data from Maximo, Please Check Network (2)']); // this likely doesnt work, probably remove it + throw new Error('Failed to fetch data from Maximo, please check network (2)'); + } + else { + try { + let number = content['member'][0]['itemnum']; + number = parseInt(number); + return number; + } + catch (_a) { + throw new Error('Invalid number series'); + } + } + }); + } + checkLogin(userid = this.login.userid, password = this.login.password) { + return __awaiter(this, void 0, void 0, function* () { + let response; + try { + response = yield fetch(`https://${CONSTANTS.ENV}.iko.max-it-eam.com/maximo/api/whoami?lean=1`, { + headers: { + 'apikey': userid, + }, + }); + } + catch (err) { + postMessage(['result', 1, 'Failed to fetch Data from Maximo, Please Check Network (1)']); + return false; + } + const content = yield response.json(); + if (content['Error']) { + postMessage(['result', 1, 'Failed to login to Maximo, Please Check User Name & Password']); + postMessage(['result', 1, content['Error']['message']]); + return false; + } + else { + const userSite = content['insertSite']; + const siteID = userSite; + const status = true; + this.shareDB.savePassword(userid, password); + this.login.password = password; + this.login.userid = userid; + postMessage(['debug', `Successfully logged in to Maximo as: ${content.displayName}`]); + postMessage(['result', 0, 'Successfully logged in to Maximo']); + return { siteID, status }; + } + }); + } + uploadToMaximo(item) { + return __awaiter(this, void 0, void 0, function* () { + const xmldoc = ` + + + + ${item.commoditygroup} + ${item.description.replaceAll('&', '&')} + ${item.longdescription.replaceAll('&', '&')} + ${item.glclass} + ${item.assetprefix} + ${item.assetseed} + ${item.jpnum} + ${item.inspectionrequired} + ${item.isimport} + ${item.issueunit} + ${item.itemnumber} + ITEMSET1 + ${item.rotating} + ACTIVE + + + `; + const response = yield fetch(`https://${CONSTANTS.ENV}.iko.max-it-eam.com/maximo/api/os/IKO_ITEMMASTER?action=importfile`, { + method: 'POST', + headers: { + 'filetype': 'XML', + 'apikey': this.login.userid, + // "preview": "1" + }, + body: xmldoc, + }); + const content = yield response.json(); + const statuscode = response.status; + if (statuscode == 200) { + return parseInt(content.validdoc); + } + else { + throw new Error(parseInt(statuscode)); + } + }); + } + // Uploads item to inventory + uploadToInventory(item) { + return __awaiter(this, void 0, void 0, function* () { + const xmldoc = ` + + + + ${item.cataloguenum} + ${item.issueunit} + ${item.itemnumber} + ITEMSET1 + ${item.storeroomname} + ${item.siteID} + ${item.vendorname} + + + `; + const response = yield fetch(`https://${CONSTANTS.ENV}.iko.max-it-eam.com/maximo/api/os/IKO_INVENTORY?action=importfile`, { + method: 'POST', + headers: { + 'filetype': 'XML', + 'apikey': this.login.userid, + // "preview": "1" + }, + body: xmldoc, + }); + const content = yield response.json(); + // if upload to storeroom succeeded + if (parseInt(content.validdoc) == 1) { + return 1; + } // failure due to invalid vendor name + else if (content['oslc:Error']['oslc:message'].includes('Company is not valid')) { + console.log(content['oslc:Error']['oslc:message']); + return 2; + } // failure due to invalid site id + else if (content['oslc:Error']['oslc:message'].includes('is not a valid site')) { + console.log(content['oslc:Error']['oslc:message']); + return 3; + } // failure due to invalid storeroom + else if (content['oslc:Error']['oslc:message'].includes('is not a valid inventory location')) { + console.log(content['oslc:Error']['oslc:message']); + return 4; + } // failure due to other reason i.e. item already has inventory fields filled in on Maximo + else { + console.log(content['oslc:Error']['oslc:message']); + return 0; + } + }); + } + /** + * Uploads an image to maximo + * + * @param {File} image + * @returns {string[]} [status, (message if upload is unsuccessful)] + */ + uploadImageToMaximo(image) { + return __awaiter(this, void 0, void 0, function* () { + // check valid image type + if (image.type !== 'image/jpeg' && image.type !== 'image/png') { + return ['fail', 'Image type not jpeg or png']; + } + // check if item number exists in maximo + const itemnum = image.name.slice(0, 7); // itemnum is first 7 digits of image name + let response = yield fetch(`https://${CONSTANTS.ENV}.iko.max-it-eam.com/maximo/api/os/mxitem?oslc.where=itemnum=${itemnum}`, { + method: 'GET', + headers: { + 'apikey': this.login.userid, + }, + }); + let content = yield response.json(); + if (content['rdfs:member'] == 0 || content['oslc:Error']) { + return ['fail', 'Item number not found']; + } + // get item id - item id is a code that lets you access information about the item through the API + let itemId = content['rdfs:member'][0]['rdf:resource']; + itemId = itemId.slice(38); + // console.log("item id " + itemId); + // check for existing image + response = yield fetch(`https://${CONSTANTS.ENV}.iko.max-it-eam.com/maximo/api/os/mxitem/${itemId}`, { + method: 'GET', + headers: { + 'apikey': this.login.userid, + }, + }); + content = yield response.json(); + // if image exists + if (content['_imagelibref']) { + // console.log("image exists"); + // code to delete existing image + /* response = await fetch(`https://${CONSTANTS.ENV}.iko.max-it-eam.com/maximo/api/os/mxitem/${itemId}?action=system:deleteimage`, { + method: "POST", + headers: { + "x-method-override":"PATCH", + "apikey": this.login.userid, + } + });*/ + // dont upload image + return ['warning', 'Image already exists for item number']; + } + // upload new image + response = yield fetch(`https://${CONSTANTS.ENV}.iko.max-it-eam.com/maximo/api/os/mxitem/${itemId}?action=system:addimage`, { + method: 'POST', + headers: { + 'x-method-override': 'PATCH', + 'Slug': `${itemnum}.jpg`, + 'Content-type': 'image/jpeg', + 'custom-encoding': 'base', + 'apikey': this.login.userid, + }, + body: image, + }); + // console.log(response['statusText']); + return ['success']; + }); + } +} +module.exports = Maximo; diff --git a/built/misc/sharedDB.js b/built/misc/sharedDB.js new file mode 100644 index 0000000..3b0b105 --- /dev/null +++ b/built/misc/sharedDB.js @@ -0,0 +1,55 @@ +"use strict"; +const Sql = require('better-sqlite3'); +/** + * Database class for setting related queries + */ +class SharedDatabase { + constructor() { + this.db = new Sql(`${process.env.APPDATA}/EAM Spare Parts/program.db`); // , { verbose: console.log }); + const stmt = this.db.prepare('CREATE TABLE IF NOT EXISTS settings(id INTEGER PRIMARY KEY, key TEXT UNIQUE NOT NULL, value TEXT NOT NULL)'); + stmt.run(); + } + /** + * check if version db was last opened against matches curVersion, updates version in DB + * @param {String} curVersion running app version + * @return {boolean} if version is same as db version + */ + checkVersion(curVersion) { + let stmt; + let lastVersion = '0.0.0'; + try { + stmt = this.db.prepare(`SELECT value FROM settings WHERE key = 'version'`); + lastVersion = stmt.get().value; + stmt = this.db.prepare(`UPDATE settings SET value = '${curVersion}' WHERE key = 'version'`); + stmt.run(); + } + catch (SqliteError) { + stmt = this.db.prepare('CREATE TABLE IF NOT EXISTS settings(id INTEGER PRIMARY KEY, key TEXT UNIQUE NOT NULL, value TEXT NOT NULL)'); + stmt.run(); + stmt = this.db.prepare(`INSERT INTO settings(key, value) VALUES ('version', '${curVersion}')`); + stmt.run(); + } + return (lastVersion == curVersion); + } + savePassword(userid, password) { + let stmt; + stmt = this.db.prepare('CREATE UNIQUE INDEX if not EXISTS idx_key ON settings(key);'); // patch table to have unique keys + stmt.run(); + stmt = this.db.prepare(`replace into settings(key, value) VALUES ('userid', '${userid}'), ('password', '${password}')`); + stmt.run(); + } + getPassword() { + var _a, _b; + try { + let stmt = this.db.prepare(`SELECT value FROM settings WHERE key = 'userid'`); + const userid = (_a = stmt.get()) === null || _a === void 0 ? void 0 : _a.value; + stmt = this.db.prepare(`SELECT value FROM settings WHERE key = 'password'`); + const password = (_b = stmt.get()) === null || _b === void 0 ? void 0 : _b.value; + return { userid: userid, password: password }; + } + catch (SqliteError) { + return { userid: '', password: '' }; + } + } +} +module.exports = SharedDatabase; diff --git a/built/misc/spreadsheet.js b/built/misc/spreadsheet.js new file mode 100644 index 0000000..0a6427f --- /dev/null +++ b/built/misc/spreadsheet.js @@ -0,0 +1,284 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +const Exceljs = require('exceljs'); +const fs = require('fs'); +const dt = require('luxon'); +/** + * class for reading data from excel item cache + */ +class ExcelReader { + constructor(filePath) { + this.filePath = filePath; + } + // the version number of the workbook is saved in a cell for tracking purposes + getVersion() { + return __awaiter(this, void 0, void 0, function* () { + const wb = new Exceljs.Workbook(); + yield wb.xlsx.readFile(this.filePath); + const ws = wb.getWorksheet('Sheet2'); + const version = dt.DateTime.fromSeconds((parseFloat(ws.getCell('A2').text) - 25569) * 86400 + 14400).toFormat('yyyy-LL-dd HH:mm:ss'); + return version; + }); + } + // read information about the item database (an initial file is included for + // faster startup rather than fetching all 100k+ items from maximo directly) + /** + * read the item cache file + */ + getItemCache() { + return __awaiter(this, void 0, void 0, function* () { + const wb = new Exceljs.Workbook(); + yield wb.xlsx.readFile(this.filePath); + // read inventory data which will be appended to ext_search_text + const ws3 = wb.getWorksheet('Sheet3'); + let lastRow = ws3.lastRow.number; + const inventoryData = new Map(); + const allInventory = []; + for (let i = 2; i <= lastRow; i++) { + const row = []; + row[0] = ws3.getCell(`A${i}`).text; + row[1] = ws3.getCell(`B${i}`).text; + row[2] = ws3.getCell(`C${i}`).text; + row[3] = ws3.getCell(`D${i}`).text; + row[4] = ws3.getCell(`E${i}`).text; + row[5] = ws3.getCell(`F${i}`).text; + row[6] = ws3.getCell(`G${i}`).text; + row[7] = ws3.getCell(`I${i}`).text; + allInventory.push([...row, ws3.getCell(`H${i}`).text]); + if (row[2].length > 0 || row[3].length > 0 || row[4].length > 0 || row[5].length > 0 || row[6].length > 0) { + if (inventoryData.has(row[0])) { + for (let j = 1; j <= 7; j++) { + if (row[j].length > 0 && row[j] != 'NULL') { + inventoryData.get(row[0])[j] = inventoryData.get(row[0])[j] + '|' + row[j]; + } + } + } + else { + for (let j = 2; j <= 7; j++) { + if (row[j] == 'NULL') { + row[j] = ''; + } + } + inventoryData.set(row[0], row); + } + } + } + // read item master data + const ws = wb.getWorksheet('Sheet1'); // alternatively (fetch by ID): getWorksheet(1); + lastRow = ws.lastRow.number; // last cell row in range + const data = []; // empty list + for (let i = 2; i <= lastRow; i++) { + try { + data.push([ + ws.getCell(`A${i}`).text, + ws.getCell(`B${i}`).text, + dt.DateTime.fromSeconds((parseFloat(ws.getCell(`C${i}`).text) - 25569) * 86400 + 14400).toFormat('yyyy-LL-dd HH:mm:ss'), + ws.getCell(`D${i}`).text, + ws.getCell(`E${i}`).text, + ws.getCell(`F${i}`).text, + ws.getCell(`H${i}`).text, + inventoryData.get(ws.getCell(`A${i}`).text), + ]); + } + catch (error) { + console.log(error); + console.log(`row number: ${i}`); + } + } + const ws2 = wb.getWorksheet('Sheet2'); + return [ + data, + dt.DateTime.fromSeconds((parseFloat(ws2.getCell('A2').text) - 25569) * 86400 + 14400).toFormat('yyyy-LL-dd HH:mm:ss'), + allInventory, + ]; + // to convert excel datetime in number format to string + }); + } + // get inital list of manufacturers from the workbook + getManufactures() { + return __awaiter(this, void 0, void 0, function* () { + const workbook = new Exceljs.Workbook(); + yield workbook.xlsx.readFile(this.filePath); + const worksheet = workbook.getWorksheet('Manufacturers'); + const lastrow = worksheet.lastRow.number; + const data = []; + for (let i = 2; i <= lastrow; i++) { + if (worksheet.getCell(`A${i}`).text) { + data.push([ + worksheet.getCell(`A${i}`).text, + dt.DateTime.fromSeconds((parseFloat(worksheet.getCell(`B${i}`).text) - 25569) * 86400 + 14400).toFormat('yyyy-LL-dd HH:mm:ss'), + worksheet.getCell(`C${i}`).text, + worksheet.getCell(`D${i}`).text, + ]); + } + } + return data; + }); + } + // get initial list of abbreviations from the workbook + getAbbreviations() { + return __awaiter(this, void 0, void 0, function* () { + const workbook = new Exceljs.Workbook(); + yield workbook.xlsx.readFile(this.filePath); + const worksheet = workbook.getWorksheet('Replacements'); + const lastrow = worksheet.lastRow.number; + const data = []; + for (let i = 3; i <= lastrow; i++) { + if (worksheet.getCell(`D${i}`).text) { + data.push([worksheet.getCell(`D${i}`).text, worksheet.getCell(`B${i}`).text]); + } + } + return data; + }); + } + // read item information from workbook being processed + getDescriptions(params) { + return __awaiter(this, void 0, void 0, function* () { + const workbook = new Exceljs.Workbook(); + yield workbook.xlsx.readFile(this.filePath); + fs.copyFileSync(this.filePath, `${this.filePath}.backup`); + postMessage(['info', `Backing up file as: "${this.filePath}.backup"`]); + const wsNames = workbook.worksheets.map(function (ele) { + return ele.name; + }); + if (!wsNames.includes(params.wsName)) { + postMessage(['info', 'Workbook has the following worksheets:']); + postMessage(['info', `${wsNames}`]); + postMessage([ + 'error', + `"${params.wsName} does not exist, Please check spelling & captitalization"`, + ]); + return false; + } + const worksheet = workbook.getWorksheet(params.wsName); + const lastrow = worksheet.lastRow.number; + const data = []; + let row = []; + for (let i = params.startRow; i <= lastrow; i++) { + row = []; + for (let j = 0; j < params.inDesc.length; j++) { + if (worksheet.getCell(`${params.inDesc[j]}${i}`).text) { + row.push(worksheet.getCell(`${params.inDesc[j]}${i}`).text); + } + } + data.push([i, row.join()]); + } + return data; + }); + } + // write validated item information to the workbook + // not used + writeDescriptions(descriptions, savePath) { + return __awaiter(this, void 0, void 0, function* () { + const workbook = xlsx.readFile(this.filePath, { cellStyles: true, bookVBA: true }); + const worksheet = workbook.Sheets['Validate']; + descriptions.forEach((description) => { + worksheet[`E${description.row}`] = { t: `s`, v: description.result[3], w: undefined }; // maximo description + worksheet[`F${description.row}`] = { t: `s`, v: description.result[0], w: undefined }; // main description + worksheet[`G${description.row}`] = { t: `s`, v: description.result[1], w: undefined }; // ext1 + worksheet[`H${description.row}`] = { t: `s`, v: description.result[2], w: undefined }; // ext2 + worksheet[`I${description.row}`] = { t: `s`, v: description.messages, w: undefined }; // msg + }); + try { + xlsx.writeFile(workbook, savePath); + } + catch (error) { + console.log(error); + const suffix = Date.now(); + savePath = savePath.split(`.`); + savePath = `${savePath[0]}${suffix}.${savePath[1]}`; + xlsx.writeFile(workbook, savePath); + } + return savePath; + }); + } + saveDescription(parmas) { + return __awaiter(this, void 0, void 0, function* () { + const workbook = new Exceljs.Workbook(); + yield workbook.xlsx.readFile(this.filePath); + const worksheet = workbook.getWorksheet(parmas[0].wsName); + worksheet.getCell(`${parmas[0].outItemDesc[0]}${parmas[0].outRow}`).value = parmas[1][0]; // description1 + worksheet.getCell(`${parmas[0].outItemDesc[1]}${parmas[0].outRow}`).value = parmas[1][1]; // description2 + worksheet.getCell(`${parmas[0].outItemDesc[2]}${parmas[0].outRow}`).value = parmas[1][2]; // manufacturer + worksheet.getCell(`${parmas[0].outUOM}${parmas[0].outRow}`).value = parmas[0].uom; // uom + worksheet.getCell(`${parmas[0].outComm}${parmas[0].outRow}`).value = parmas[0].commGroup; // c-group + // worksheet.getCell(`${parmas[0].outItemDesc[2]}${parmas[0].outRow}`).value = parmas[1][2]; //c-code + worksheet.getCell(`${parmas[0].outGL}${parmas[0].outRow}`).value = parmas[0].glClass; // gl-class + worksheet.getCell(`${parmas[0].outTranslate}${parmas[0].outRow}`).value = + 'placeholder-translated'; // translated + worksheet.getCell(`${parmas[0].outMissing}${parmas[0].outRow}`).value = + 'placeholder-missing'; // missing + // worksheet.getCell(`${parmas[0].outItemDesc[2]}${parmas[0].outRow}`).value = parmas[1][2]; //question + yield this.saveWorkbook(workbook, this.filePath); + }); + } + saveNumber(parmas) { + return __awaiter(this, void 0, void 0, function* () { + const workbook = new Exceljs.Workbook(); + yield workbook.xlsx.readFile(this.filePath); + const worksheet = workbook.getWorksheet(parmas[1]); + worksheet.getCell(`${parmas[3]}${parmas[2]}`).value = parmas[4]; + return yield this.saveWorkbook(workbook, this.filePath); + }); + } + saveNonInteractive(parmas, data) { + return __awaiter(this, void 0, void 0, function* () { + // convert to batch mode, individually it is too slow + const workbook = new Exceljs.Workbook(); + yield workbook.xlsx.readFile(this.filePath); + const worksheet = workbook.getWorksheet(parmas[0].wsName); + for (const item of data) { + item.analysis = JSON.parse(item.analysis); + if (item.analysis.related) { + worksheet.getCell(`${parmas[0].outItemNum}${item.row}`).value = + item.analysis.related; // itemnum + worksheet.getCell(`${parmas[0].outItemDesc[0]}${item.row}`).value = + item.description; + } + if (item.analysis.translate) { + worksheet.getCell(`${parmas[0].outTranslate}${item.row}`).value = + item.analysis.translate.description; // translated description + worksheet.getCell(`${parmas[0].outMissing}${item.row}`).value = + item.analysis.translate.missing.join('|'); // missing translations windows wants \r\n instead of just \n + } + // worksheet.getCell(`${parmas[0].outItemDesc}${item.row}`).value = parmas[2]; // en description + } + return yield this.saveWorkbook(workbook, this.filePath); + }); + } + saveWorkbook(workbook, savePath) { + return __awaiter(this, void 0, void 0, function* () { + try { + yield workbook.xlsx.writeFile(savePath); + } + catch (error) { + console.log(error); + postMessage([`error`, `Cannot edit old file make sure it is closed`]); + } + return savePath; + }); + } + getColumnByName(name) { + return __awaiter(this, void 0, void 0, function* () { + const wb = new Exceljs.Workbook(); + yield wb.xlsx.readFile(this.filePath); + const ws = wb.getWorksheet('Sheet1'); + let match; + ws.eachRow((row) => row.eachCell((cell) => { + if (cell.names.find((n) => n === name)) { + match = cell; + } + })); + return match; + }); + } +} +module.exports = ExcelReader; diff --git a/built/misc/utils.js b/built/misc/utils.js new file mode 100644 index 0000000..2b88c5a --- /dev/null +++ b/built/misc/utils.js @@ -0,0 +1,36 @@ +"use strict"; +function getCombinations(valuesArray) { + let combi = []; + let temp = []; + let slent = Math.pow(2, valuesArray.length); + for (let i = 0; i < slent; i++) { + temp = []; + for (let j = 0; j < valuesArray.length; j++) { + if ((i & Math.pow(2, j))) { + temp.push(valuesArray[j]); + } + } + if (temp.length > 0) { + combi.push(temp); + } + } + combi.sort((a, b) => a.length - b.length); + return combi; +} +function inOrderCombinations(valuesArray) { + let combi = []; + for (let i = 0; i < valuesArray.length; i++) { + for (let j = 0; j < valuesArray.length - i; j++) { + combi.push(valuesArray.slice(j, j + i + 1)); + } + } + return combi; +} +function splitToTwo() { + // [string] for english +} +function splitToThree() { + //{description: [string], manu: string} +} +const STRINGCLEANUP = [',', ' ', '-']; +module.exports = { getCombinations, inOrderCombinations, STRINGCLEANUP }; diff --git a/built/misc/validators.js b/built/misc/validators.js new file mode 100644 index 0000000..f4c9a84 --- /dev/null +++ b/built/misc/validators.js @@ -0,0 +1,144 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +const Database = require('./indexDB'); +const ExcelReader = require('./spreadsheet'); +const utils = require('./utils'); +class ManufacturerValidator { + constructor() { + this.CHAR_LIMIT = 30; + this.db = new Database(); + } + validateSingle(split_desc) { + let manufacturer = false; + // look from end of string since manufacturer name is mostly likely in last description + for (let i = split_desc.length - 1; i >= 0; i--) { + postMessage(['debug', `Looking for manufacturer named: "${split_desc[i]}"`]); + manufacturer = this.db.isManufacturer(split_desc[i]); + if (manufacturer) { + postMessage(['debug', `Found manufacturer with short name: "${manufacturer.short_name}"`]); + split_desc.splice(i, 1); //remove the manufactuer and re-add the short version to the end + split_desc.push(manufacturer.short_name); + return split_desc; + } + } + } +} +class PhraseReplacer { + constructor() { + this.db = new Database(); + } + replaceAbbreviated(split_desc) { + let replacement = false; + for (let i = 0; i < split_desc.length; i++) { + // look for replacements for phrases + replacement = this.db.isAbbreviation(split_desc[i].replace('-', ' ')); + if (replacement) { + postMessage(['debug', `Replacing: "${split_desc[i]} with: ${replacement.replace_text}"`]); + split_desc[i] = replacement.replace_text; + } + // look for replacement for individual words + let word_split = utils.inOrderCombinations(split_desc[i].split(' ')); + replacement = false; + for (let j = word_split.length - 1; j > 0; j--) { + replacement = this.db.isAbbreviation(word_split[j].join(' ').replace('-', ' ')); + if (replacement) { + postMessage(['debug', `Replacing: "${word_split[j].join(' ')} with: ${replacement.replace_text}"`]); + split_desc[i] = split_desc[i].replace(word_split[j].join(' '), replacement.replace_text); + } + } + } + return split_desc; + } +} +class Validate { + validateSingle(raw_desc) { + return __awaiter(this, void 0, void 0, function* () { + raw_desc = raw_desc.split(','); + let split_desc = []; + raw_desc.forEach(desc => { + split_desc.push(desc.trim()); + }); + postMessage(['debug', `Validating: "${split_desc}"`]); + let manuValid = new ManufacturerValidator(); + let manu = yield manuValid.validateSingle(split_desc); + if (!manu) { + postMessage(['debug', `No manufacturer found in: "${raw_desc}"`]); + } + else { + split_desc = manu; + } + let replace = new PhraseReplacer(); + let replaced = yield replace.replaceAbbreviated(split_desc); + if (replaced) { + split_desc = replaced; + } + else { + postMessage(['debug', `No words need to be replaced`]); + } + let result = this.assembleDescription(split_desc); + return result; + }); + } + validateBatch(filePath) { + return __awaiter(this, void 0, void 0, function* () { + postMessage(['debug', `Selected file path: "${filePath}"`]); + filePath = filePath[0]; + let excel = new ExcelReader(filePath); + let result = yield excel.getDescriptions(); + for (let i = 0; i < result.length; i++) { + result[i].result = yield this.validateSingle(result[i].value); + } + filePath = filePath.split('.'); + filePath = `${filePath[0]}_Validated.${filePath[1]}`; + filePath = yield excel.writeDescriptions(result, filePath); + return (filePath); + }); + } + assembleDescription(split_desc) { + const db = new Database(); + let descriptions = ['', '', '', '']; // jde1, jde2, jde3, maximo descriptions + let position = 0; //tracks which part of jde description is being added to + const regex = /\d+/g; + for (let i = 0; i < split_desc.length; i++) { + if (!(split_desc[i].match(regex))) { // if the phrase has no numbers + split_desc[i] = split_desc[i].toUpperCase(); + } + // if we are at end of array & the phrase is a manufacturer + if (i + 1 == split_desc.length && db.isManufacturer(split_desc[i])) { + if (descriptions[2].length == 0) { + descriptions[2] = split_desc[i]; + } + else { + descriptions[2] = `${descriptions[2]},${split_desc[i]}`; + } + } + // two ifs to avoid trailing / leading commas + else if (descriptions[position].length == 0) { + descriptions[position] = split_desc[i]; + } + else if (`${descriptions[position]},${split_desc[i]}`.length <= 30) { + descriptions[position] = `${descriptions[position]},${split_desc[i]}`; + } + else { // if too long put it into next word + position++; + if (position == 3) { // if jde is over flowing into Maximo + postMessage(['warning', 'Description is too long']); + } + else { + descriptions[position] = split_desc[i]; + } + } + } + descriptions[3] = descriptions.slice(0, 3).filter(Boolean).join(','); + return descriptions; + } +} +module.exports = Validate; diff --git a/built/renderer/asset_translation.js b/built/renderer/asset_translation.js new file mode 100644 index 0000000..644c597 --- /dev/null +++ b/built/renderer/asset_translation.js @@ -0,0 +1,67 @@ +"use strict"; +// Debug stuff +document.getElementById("selected_file").innerHTML = 'C:\\Users\\majona\\Documents\\GitHub\\iko_mro_items\\assets\\Translation_PM_JP_Asset.xlsx'; +document.getElementById("selected_jobtasks").innerHTML = `C:\\Users\\majona\\Documents\\jp_pm_translation-test_ant.xlsx`; +const { ipcRenderer } = require('electron'); +document.getElementById("topButton").addEventListener("click", toTop); +document.getElementById("endButton").addEventListener("click", toEnd); +document.getElementById("select_file").addEventListener("click", selectFile); +document.getElementById("select_jobtasks").addEventListener("click", selectJobTasks); +document.getElementById("process").addEventListener("click", processFile); +document.getElementById("dark-mode-switch").addEventListener("click", toggleTheme); +function selectFile() { + ipcRenderer.invoke('select-translations', 'finished').then((result) => { + if (!result.canceled) { + document.getElementById("selected_file").innerHTML = result.filePaths[0]; + } + else { + new Toast('File Picker Cancelled'); + } + }); +} +function selectJobTasks() { + ipcRenderer.invoke('select-to-be-translated', 'finished').then((result) => { + if (!result.canceled) { + document.getElementById("selected_jobtasks").innerHTML = result.filePaths[0]; + } + else { + new Toast('File Picker Cancelled'); + } + }); +} +function processFile() { + let bar = new ProgressBar; + const site_id = document.getElementById('siteSelect').value; + if (site_id === '0') { + bar.update(100, 'Please select a site'); + new Toast('Please select a site'); + } + else { + const worker = new WorkerHandler; + const wb_translation = document.getElementById("selected_file").innerHTML; + const wb_pms = document.getElementById("selected_jobtasks").innerHTML; + bar.update(0, 'Processing Translations'); + worker.work([ + 'translatepms', + { + wb_translation: wb_translation, + wb_pms: wb_pms, + siteid: site_id + } + ], showResults); + } +} +function showResults(results) { + let bar = new ProgressBar; + bar.update(100, 'Done!'); +} +function toggleTheme() { + theme = document.getElementById("dark-mode-switch").checked ? "light" : "dark"; + let str = "[data-bs-theme=\"" + theme + "\"]"; + theme = document.getElementById("dark-mode-switch").checked ? "dark" : "light"; + //console.log(str); + let elms = document.querySelectorAll(str); + for (const elm of elms) { + elm.setAttribute("data-bs-theme", theme); + } +} diff --git a/built/renderer/item_main.js b/built/renderer/item_main.js new file mode 100644 index 0000000..731e8e2 --- /dev/null +++ b/built/renderer/item_main.js @@ -0,0 +1,1360 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +const { clipboard, ipcRenderer, shell } = require('electron'); +const fs = require('fs'); +const path = require('path'); +// const { dialog } = require('electron').remote; +const Database = require('../misc/indexDB'); +const SharedDatabase = require('../misc/sharedDB'); +const Validate = require('../misc/validators'); +const Maximo = require('../misc/maximo'); +const CONSTANTS = require('../misc/constants.js'); +// stores items that are to be uploaded through the "batch upload" accordion. +let itemsToUpload = []; +// stores images that are to be uploaded through the "image upload" accordion. +let imgsToUpload = []; +// an object that stores the location of each column in the batch upload table. +// allows for column locations to be interchanged. -1 means a column is not in +// the table. Maybe in the future, column locations should be predetermined so +// that a global variable is not used for this. +let colLoc = { + description: -1, + uom: -1, + commGroup: -1, + glClass: -1, + maximo: -1, + vendor: -1, + storeroom: -1, + catNum: -1, + siteID: -1, +}; +// an object that stores all search results and "bookmarks" how many items have +// been loaded in the related results table. Used for infinite scroll. +let relatedResults = { + idx: 0, // store the index of the current item being loaded + curKey: 0, // store which key is currently being loaded from the search results + results: [], // store all search results (dictonary) +}; +// a function that is called immediately after the window has been loaded +window.onload = function () { + // set the darkmode toggle to the correct position by retreiving information from the local storage + document.getElementById('dark-mode-switch').checked = (localStorage.getItem('theme') === 'dark' ? true : false); + // change the UI based on whether the user is a "power user". show all upload elements if they are a power user, else hide it. + if (localStorage.getItem('powerUser') === 'true') { + document.getElementById('upload-btn').style.display = 'block'; + document.getElementById('request-btn').style.display = 'none'; + document.getElementById('batch-upld-btn').style.display = 'block'; + document.getElementById('img-upld-toggle').style.display = 'block'; + document.getElementById('batch-mode-toggle').style.display = 'block'; + return; + } +}; +/* +Power User Toggle + +Allows user to toggle between 2 modes: Power user and Normal User. +Normal user mode hides all upload elements and only allows the user to request items. +Power user mode shows all upload elements and allows the user to upload items and images. + +Created for the purpose of hiding upload functionality from people who shouldn't be +uploading items (a.k.a. everyone except for reliability team). +*/ +// set the user to a power user if they have clicked the secret button 5 times +document.getElementById('secret-button').addEventListener('click', (e) => { + let isPowerUser = false; + let numClicks = parseInt(e.target.getAttribute('data-clicks')); + numClicks++; + if (numClicks === 5) { + isPowerUser = true; + localStorage.setItem('powerUser', 'true'); + e.target.setAttribute('data-clicks', '0'); + } + else { + localStorage.setItem('powerUser', 'false'); + e.target.setAttribute('data-clicks', `${numClicks}`); + isPowerUser = false; + } + // toggle whether elements are hidden or not based off of power user status + if (isPowerUser == true) { + document.getElementById('upload-btn').style.display = 'block'; + document.getElementById('request-btn').style.display = 'none'; + document.getElementById('batch-upld-btn').style.display = 'block'; + document.getElementById('img-upld-toggle').style.display = 'block'; + document.getElementById('batch-mode-toggle').style.display = 'block'; + } + else { + document.getElementById('upload-btn').style.display = 'none'; + document.getElementById('request-btn').style.display = 'block'; + document.getElementById('batch-upld-btn').style.display = 'none'; + document.getElementById('img-upld-toggle').style.display = 'none'; + document.getElementById('batch-mode-toggle').style.display = 'none'; + } +}); +// gets user site information +function getSite(credentials = {}) { + return __awaiter(this, void 0, void 0, function* () { + const maximo = new Maximo(); + const currInfo = yield maximo.checkLogin(credentials === null || credentials === void 0 ? void 0 : credentials.userid, credentials === null || credentials === void 0 ? void 0 : credentials.password); + return currInfo.siteID; + }); +} +// open a modal that allows you to make an item request +document.getElementById('request-btn').addEventListener('click', () => { + // show request item modal + const requestModal = new bootstrap.Modal(document.getElementById('requestModal')); + requestModal.toggle(); + const currPass = new SharedDatabase().getPassword(); + const userid = currPass.userid; + let siteID; + const sites = { + 'AA': ['AAG: Brampton B2 Storeroom', 'AAL: Brampton B2/B4 Maintenance Storeroom', 'AAO: Brampton B4 Oxidizer Storeroom'], + 'ANT': ['AN1: Antwerp Mod Line Storeroom', 'AN2: Antwerp Coating Line Storeroom'], + 'BA': ['BAL: IKO Calgary Maintenance Storeroom'], + 'BL': ['BLC: Hagerstown TPO Storeroom', 'BLD: Hagerstown ISO Storeroom', 'BLL: Hagerstown Maintenance Storeroom(Shared)'], + 'CA': ['CAL">IKO Kankakee Maintenance Storeroom'], + 'CAM': ['C61">IKO Appley Bridge Maintenance Storeroom'], + 'COM': ['CB1">Combronde Maintenance Storeroom'], + 'GA': ['GAL: IKO Wilmington Maintenance Storeroom'], + 'GC': ['GCL: Sumas Maintenance Storeroom', 'GCA: Sumas Shipping Storeroom', 'GCD: Sumas Shingle Storeroom', 'GCG: Sumas Mod Line Storeroom', 'GCJ: Sumas Crusher Storeroom', 'GCK: Sumas Tank Farm Storeroom'], + 'GE': ['GEL: Ashcroft Maintenance Storeroom'], + 'GH': ['GHL: IKO Hawkesbury Maintenance Storeroom'], + 'GI': ['GIL: IKO Madoc Maintenance Storeroom'], + 'GJ': ['GJL: CRC Toronto Maintenance Storeroom'], + 'GK': ['GKA: IG Brampton B7 and B8 Storeroom', 'GKC: IG Brampton B6 and Laminator Storeroom', 'GKL: IG Brampton Maintenance Storeroom'], + 'GM': ['GML: IG High River Maintenance Storeroom'], + 'GP': ['GPL: CRC Brampton Maintenance Storeroom'], + 'GR': ['GRL: Bramcal Maintenance Storeroom'], + 'GS': ['GSL: Sylacauga Maintenance Storeroom'], + 'GV': ['GVL: IKO Hillsboro Maintenance Storeroom'], + 'GX': ['GXL: Maxi-Mix Maintenance Storeroom'], + 'KLU': ['KD1: IKO Klundert Maintenance Storeroom', 'KD2: IKO Klundert Lab Storeroom', 'KD3: IKO Klundert Logistics Storeroom'], + 'PBM': ['PB6: Slovakia Maintenance Storeroom'], + 'RAM': ['RA6: IKO Alconbury Maintenance Storeroom'], + // Add more sites and storerooms as needed... + }; + const userSite = getSite({ userid: userid, password: currPass.password }); + userSite.then((response) => { + siteID = response; + const storeroomSelect = document.getElementById('storeroom'); + // poppulate correct user storerooms in modal + function updateStoreroomOptions() { + storeroomSelect.options.length = 1; + // Add new options + const neededStorerooms = sites[siteID]; + for (const storeroom of neededStorerooms) { + const option = document.createElement('option'); + option.value = storeroom; + option.text = storeroom; + storeroomSelect.add(option); + } + } + updateStoreroomOptions(); + }) + .catch((error) => console.error(`Error: ${error}`)); + poppulateModal(); +}); +// Allow input of manufacturer name & part number if "Other" is selected +document.getElementById('manu-name').addEventListener('click', (e) => { + if (e.target.value == 'Other') { + document.getElementById('pref-manu').style.display = 'block'; + document.getElementById('part-form').style.display = 'block'; + } + else { + document.getElementById('pref-manu').style.display = 'none'; + document.getElementById('part-form').style.display = 'none'; + } +}); +// download email file when submit button is pressed +document.getElementById('submit-btn').addEventListener('click', submitMail, false); +// opens email file in default mail client +function submitMail() { + // checking required fields are filled + if (document.getElementById('manu-name').value == 'Other') { + if (!(document.getElementById('part-num').reportValidity() && + document.getElementById('storeroom').reportValidity() && + document.getElementById('item-descr').reportValidity())) { + console.log('Required fields still empty'); + return; + } + } + else { + if (!(document.getElementById('storeroom').reportValidity() && + document.getElementById('item-descr').reportValidity())) { + console.log('Required fields still empty'); + return; + } + } + // storing current date and time for email subject + const currentdate = new Date(); + const datetime = currentdate.getFullYear() + '/' + (currentdate.getMonth() + 1) + '/' + (currentdate.getDay() + 1) + + ' @ ' + + currentdate.getHours() + ':' + + currentdate.getMinutes() + ':' + currentdate.getSeconds(); + const mailText = ``; + // Send string to main process to write file + ipcRenderer.send('write-file', mailText); + // requestModal.toggle(); +} +/* Infinite scroll + +Allows elements to load as the user scrolls down the page, +drastically decreasing loading times and making UI smoother. +*/ +// listen for a scroll event. if the bottom of the results table is less than 100px below the bottom of the viewport, load more items +document.getElementById('everything').addEventListener('scroll', () => { + // dont add items to the list if the accordion is collapsed + if (document.getElementById('related-items-accordion-btn').classList.contains('collapsed') || relatedResults.results.length == 0) { + return; + } + const searchResultsTable = document.getElementById('related-items'); + const domRect = searchResultsTable.getBoundingClientRect(); + const spaceBelow = document.getElementById('everything').offsetHeight - domRect.bottom; + if (spaceBelow > -100) { + // load more items if the bottom of the table is less than 100px below the bottom of the viewport + loadRelated(); + } +}); +// Generate UI when files are selected by user +document.getElementById('imgInput').addEventListener('change', (e) => __awaiter(void 0, void 0, void 0, function* () { + const progressBar = new ProgressBar(); + // reset UI + document.getElementById('imgList').innerHTML = ``; + // get files from file picker + const files = document.getElementById('imgInput').files; + imgsToUpload = files; + // make a comma separated string of all the item numbers that are to be uploaded + let nums = ''; + // if no files were selected, return + if (files.length == 0 || !files) { + return; + } + const imgList = document.getElementById('imgList'); + progressBar.update(0, 'Loading Images...'); + // for each image, add list item to HTML list + for (let i = 0; i < files.length; i++) { + const file = files[i]; + const completion = (i + 1) / files.length * 100; + nums += file.name.slice(0, 7) + ','; // get first 7 characters of file name + progressBar.updateProgressBar(completion); + imgList.innerHTML += ` +
  • +
    + +

    ${file.name.slice(0, 7)}

    +
    + pending +
  • `; + } + // generate a link to open items that are being uploaded to in maximo + const url = `https://${CONSTANTS.ENV}.iko.max-it-eam.com/maximo/oslc/graphite/manage-shell/index.html?event=loadapp&value=item&additionalevent=useqbe&additionaleventvalue=itemnum=${nums}`; + document.getElementById('img-upload-status-text').innerHTML = `Selected Items:`; + document.getElementById('imgs-link').addEventListener('click', function (e) { + e.preventDefault(); + shell.openExternal(url); + }); + progressBar.update(100, 'Ready to Upload!'); +})); +// clear the file picker each time it is clicked +document.getElementById('imgInput').addEventListener('click', () => { + document.getElementById('img-clear-btn').dispatchEvent(new Event('click')); +}); +document.getElementById('img-clear-btn').addEventListener('click', () => { + // reset all related components + const progressBar = new ProgressBar(); + document.getElementById('imgList').innerHTML = ``; + progressBar.update(100, 'Ready!'); + // empty list of images to upload + imgsToUpload = []; + // empty file picker + document.getElementById('imgInput').value = null; + document.getElementById('img-upload-status-text').innerHTML = 'Select Images to Continue...'; +}); +document.getElementById('img-upload-btn').addEventListener('click', () => { + const progressBar = new ProgressBar(); + const clearBtn = document.getElementById('img-clear-btn'); + const uploadBtn = document.getElementById('img-upload-btn'); + // return if user has picked no images + if (imgsToUpload.length == 0) { + new Toast('No Images Selected!'); + return; + } + let finishedItems = 0; + // disable the clear and upload buttons while upload is taking place so the + // user can't send duplicate requests or accidentally clear the image upload list + // while its uploading + clearBtn.disabled = true; + uploadBtn.disabled = true; + const worker = new WorkerHandler(); + progressBar.update(0, 'Uploading Images...'); + // upload all images and update UI + worker.work(['uploadImages', imgsToUpload], (result) => { + if (result[0] == 'success') { + // if success, display checkmark + document.getElementById(`img-${result[1]}-status`).innerHTML = `done`; + } + else if (result[0] == 'fail') { + // if fail, display 'x' (cross) + document.getElementById(`img-${result[1]}-status`).innerHTML = `close`; + } + else if (result[0] == 'done') { + progressBar.update(100, 'Upload Complete!'); + clearBtn.disabled = false; + uploadBtn.disabled = false; + } + else if (result[0] == 'warning') { + // if warning, display triangle with exclamation point in it. This only occurs if you try + // to upload an image to an item that already has an image + document.getElementById(`img-${result[1]}-status`).innerHTML = `warning`; + } + else if (result[0] == 'total failure') { + finishedItems = imgsToUpload.length; + progressBar.update(100, 'Error occurred while attempting upload!'); + document.getElementById('img-upload-status-text').innerHTML = `Upload Failed: ${result[1]}}`; + clearBtn.disabled = false; + uploadBtn.disabled = false; + } + if (result != 'done') { + finishedItems++; + } + // update progressbar when each image is uploaded/fails upload + progressBar.updateProgressBar(finishedItems * 100 / imgsToUpload.length); + }); +}); +// Other +document.getElementById('load-item').addEventListener('click', loadItem); +document.getElementById('valid-single').addEventListener('click', () => { + validSingle(); +}); +document.getElementById('valid-single-ext').addEventListener('click', () => { + validSingle(true); +}); +document.getElementById('settings').addEventListener('click', openSettings); +document.getElementById('topButton').addEventListener('click', toTop); +document.getElementById('endButton').addEventListener('click', toEnd); +document.getElementById('interactive').addEventListener('click', openExcel); +document.getElementById('worksheet-path').addEventListener('click', openExcel); +document.getElementById('pauseAuto').addEventListener('click', pauseAuto); +document.getElementById('save-desc').addEventListener('click', writeDescription); +document.getElementById('save-num').addEventListener('click', writeItemNum); +document.getElementById('skip-row').addEventListener('click', skipRow); +document.getElementById('continueAuto').addEventListener('click', continueAuto); +document.getElementById('confirm-btn').addEventListener('click', () => { + uploadItem(); +}); +document.getElementById('upload-btn').addEventListener('click', () => { + const confirmModal = new bootstrap.Modal(document.getElementById('confirmModal')); + if (!(document.getElementById('maximo-desc').reportValidity() && + document.getElementById('uom-field').reportValidity() && + document.getElementById('com-group').reportValidity() && + document.getElementById('gl-class').reportValidity())) { + return; + } + ItemAnalysis(); + confirmModal.toggle(); + getNextNumThenUpdate(document.getElementById('num-type').value); +}); +// batch upload: +document.getElementById('openBatchFile').addEventListener('click', () => { + openFile('worksheet-path'); +}); +document.getElementById('clear-batch-items-btn').addEventListener('click', () => { + document.getElementById('batch-items-table').innerHTML = ``; + document.getElementById('batch-copy-nums').disabled = true; + document.getElementById('batch-upload-status-text').innerHTML = 'Waiting for paste...'; +}); +document.getElementById('batch-copy-nums').addEventListener('click', () => { + try { + const result = getItemsFromTable('batch-items-table'); + if (result == undefined || result == null || result == 0) { + throw ('Table missing columns'); + } + const rows = parseInt(document.getElementById('batch-items-table').getAttribute('data-rows')) - 1; + let nums = ''; + for (let i = 2; i <= rows + 1; i++) { + nums += document.getElementById(`${i}-${colLoc.maximo}`).innerHTML ? (document.getElementById(`${i}-${colLoc.maximo}`).innerHTML + '\n') : ''; + } + navigator.clipboard.writeText(nums); + new Toast('Item Numbers Copied to Clipboard!'); + } + catch (error) { + // console.log(error); + new Toast('Unable to copy numbers, please check table formatting!'); + } +}); +document.getElementById('batch-items-textinput').addEventListener('paste', (e) => { + setTimeout(() => { + const paste = e.target.value; + const table = document.getElementById('batch-items-table-div'); + table.innerHTML = convertToTable(paste, 'batch-items-table'); + document.getElementById('batch-copy-nums').disabled = false; + document.getElementById('batch-upload-status-text').innerHTML = 'Paste detected! Edit table if needed and click upload.'; + e.target.value = ''; + }, 0); +}); +document.getElementById('batch-upload-btn').addEventListener('click', () => { + try { + itemsToUpload = getItemsFromTable('batch-items-table'); + } + catch (error) { + itemsToUpload = []; + document.getElementById('batch-upload-status-text').innerHTML = `Error, check table format! (${error})`; + return; + } + if (itemsToUpload.length > 0) { + itemsToUpload.forEach((value, idx) => { + if (value) { + updateItemStatus('loading', idx + 1); + } + }); + batchUploadItems(itemsToUpload); + return; + } + else { + document.getElementById('batch-upload-status-text').innerHTML = 'No valid items to upload!'; + } + return; +}); +document.getElementById('batch-paste-btn').addEventListener('click', () => __awaiter(void 0, void 0, void 0, function* () { + const text = yield navigator.clipboard.readText(); + const pasteEvent = new Event('paste', { 'bubbles': true, 'cancelable': false }); + const textinput = document.getElementById('batch-items-textinput'); + textinput.value = text; + textinput.dispatchEvent(pasteEvent); +})); +document.getElementById('batch-copy-headers-btn').addEventListener('click', () => { + const copyText = `Maximo\tDescription\tIssue Unit\tCommodity Group\tGL Class\tSite\tStoreroom\tVendor\tCatalogue Number\n\t`; + navigator.clipboard.writeText(copyText); + new Toast('Table copied to clipboard!'); +}); +// dark theme toggle +document.getElementById('dark-mode-switch').addEventListener('click', toggleTheme); +// Infinite scroll +// listener for enter key on search field +document.getElementById('maximo-desc').addEventListener('keyup', function (event) { + // Number 13 is the "Enter" key on the keyboard + if (event.key === 'Enter') { + // Cancel the default action, if needed + event.preventDefault(); + // Trigger the button element with a click + validSingle(); + } +}); +document.getElementById('interact-num').addEventListener('keyup', function (event) { + // Number 13 is the "Enter" key on the keyboard + if (event.key === 'Enter') { + // Cancel the default action, if needed + event.preventDefault(); + // Trigger the button element with a click + loadItem(); + } +}); +function pauseAuto() { + document.getElementById('modeSelect').checked = true; +} +function loadItem() { + const itemnum = document.getElementById('interact-num').value.trim(); + new Toast(`Loading Item: ${itemnum}`); + const worker = new WorkerHandler(); + worker.work(['loadItem', itemnum], showItem); +} +function auto_grow(elementID) { + const element = document.getElementById(elementID); + element.style.height = '5px'; + element.style.height = (element.scrollHeight) + 'px'; +} +function showItem(data) { + document.getElementById('maximo-desc').value = data[0].description; + document.getElementById('uom-field').value = data[0].uom; + document.getElementById('com-group').value = data[0].commodity_group; + document.getElementById('gl-class').value = data[0].gl_class; +} +function writeDescription() { + const valid = new Validate(); + const field = document.getElementById('maximo-desc'); + if (field.value.length > 0) { + const bar = new ProgressBar(); + bar.update(0, 'Writing asset description to file'); + let desc = field.value.split(','); + desc = valid.assembleDescription(desc); + const params = worksheetParams(); + params.outRow = document.getElementById('current-row').innerHTML; + const worker = new WorkerHandler(); + worker.work(['writeDesc', [params, desc]], writeComplete); + } + else { + new Toast('Please enter a valid description'); + } +} +function worksheetParams(path = false) { + const params = { + // input parameters + wsName: document.getElementById('ws-name').value || 'Sheet2', // name of ws + inDesc: (document.getElementById('input-col').value || 'F').toUpperCase().split(','), // description columns for input + startRow: document.getElementById('start-row').value || '2', // starting row of ws + // output parameters + outItemNum: document.getElementById('output-col').value.toUpperCase() || 'E', + outItemDesc: (document.getElementById('output-col-desc').value || 'F,G,H').toUpperCase().split(','), + outComm: document.getElementById('interact-num').value.toUpperCase() || 'I', // commodity group out + outGL: document.getElementById('interact-num').value.toUpperCase() || 'J', // gl class out + outUOM: document.getElementById('interact-num').value.toUpperCase() || 'K', // uom out + outQuestion: document.getElementById('interact-num').value.toUpperCase() || 'L', // questions out + outTranslate: document.getElementById('output-col-translation').value.toUpperCase() || 'L', + outMissing: document.getElementById('output-col-missing').value.toUpperCase() || 'K', + // output data + itemNum: document.getElementById('interact-num').value || '999TEST', + itemDesc: document.getElementById('maximo-desc').value || 'TEST,ITEM,DESCRIPTION', + commGroup: document.getElementById('com-group').value || '401', // commodity group in + glClass: document.getElementById('gl-class').value || '6200000000000', // gl class in + uom: document.getElementById('uom-field').value || 'EA', // uom in + }; + if (path) { + params.filePath = path; + } + else { + params.filePath = document.getElementById('worksheet-path').value; + } + return params; +} +function writeItemNum() { + const num = document.getElementById('interact-num').value; + if (num.length > 0) { + const bar = new ProgressBar(); + bar.update(0, 'Writing item number to file'); + const path = document.getElementById('worksheet-path').value; + const wsName = document.getElementById('ws-name').value; + const rowNum = document.getElementById('current-row').innerHTML; + const cols = document.getElementById('output-col').value; + const worker = new WorkerHandler(); + worker.work(['writeNum', [path, wsName, rowNum, cols, num]], writeComplete); + } + else { + new Toast('Please enter a valid item number'); + } +} +function writeComplete() { + const rowNum = parseInt(document.getElementById('current-row').innerHTML); + new Toast(`Row ${rowNum} saved!`); + document.getElementById('interact-num').value = ''; + interactiveGoNext(Number(rowNum) + 1); +} +function openFile(pathElement) { + const validFile = document.getElementById(pathElement); + const filePath = validFile.value; + if (filePath !== 'No file chosen') { + new Toast('Opening File in Excel!'); + shell.openExternal(filePath); + } +} +// Deprecated function, unused. +function openSettings() { + ipcRenderer.send('openSettings'); + // sendsync blocks parent window... + // https://github.com/electron/electron/issues/10426 +} +function openExcel() { + document.getElementById('input-col').value = document.getElementById('input-col').value.toUpperCase(); + document.getElementById('output-col').value = document.getElementById('output-col').value.toUpperCase(); + ipcRenderer.invoke('select-to-be-translated', 'finished').then((result) => { + if (!result.canceled) { + const worker = new WorkerHandler(); + const params = worksheetParams(result.filePaths[0]); + worker.work(['interactive', params], finishLoadingBatch); + document.getElementById('worksheet-path').value = result.filePaths[0]; + } + else { + new Toast('File Picker Cancelled'); + } + }); +} +// BATCH UPLOAD FUNCTIONS +/** + * Reads a table and generates items from it + * + * @param {string} tableId the HTML id of the table to read + * @return {Array} an array of Items + */ +function getItemsFromTable(tableId) { + colLoc = { + description: -1, + uom: -1, + commGroup: -1, + glClass: -1, + maximo: -1, + vendor: -1, + storeroom: -1, + catNum: -1, + siteID: -1, + }; + const table = document.getElementById(`${tableId}`); + // find Description, UOM, Commodity Group, and GL Class + const rows = parseInt(table.getAttribute('data-rows')); + const cols = parseInt(table.getAttribute('data-cols')); + // iniitalize items array + const items = []; + // go through first row to find headings. + let validParams = 0; + for (let i = 1; i <= cols; i++) { + // get a cell in the table by its id + const cell = document.getElementById('1-' + i); + // see if cell value matches any of the required parameters to create an item object + if (cell.innerHTML.toUpperCase() === 'DESCRIPTION') { + colLoc.description = i; + validParams++; + } + else if (cell.innerHTML.toUpperCase() === 'UOM' || cell.innerHTML.toUpperCase() === 'ISSUE UNIT') { + colLoc.uom = i; + validParams++; + } + else if (cell.innerHTML.toUpperCase() === 'COMMODITY GROUP' || cell.innerHTML.toUpperCase() === 'COMM GROUP') { + colLoc.commGroup = i; + validParams++; + } + else if (cell.innerHTML.toUpperCase() === 'GL CLASS') { + colLoc.glClass = i; + validParams++; + } + else if (cell.innerHTML.toUpperCase() === 'SITEID' || cell.innerHTML.toUpperCase() === 'SITE') { + colLoc.siteID = i; + validParams++; + } + else if (cell.innerHTML.toUpperCase() === 'STOREROOM' || cell.innerHTML.toUpperCase() === 'STOREROOM') { + colLoc.storeroom = i; + validParams++; + } + else if (cell.innerHTML.toUpperCase() === 'VENDOR' || cell.innerHTML.toUpperCase() === 'VENDOR NUMBER') { + colLoc.vendor = i; + validParams++; + } + else if (cell.innerHTML.toUpperCase() === 'CAT NUMBER' || cell.innerHTML.toUpperCase() === 'CATALOG NUMBER' || cell.innerHTML.toUpperCase() === 'CATALOGUE NUMBER') { + colLoc.catNum = i; + validParams++; + } + else if (cell.innerHTML.toUpperCase() === 'MAXIMO' || cell.innerHTML.toUpperCase() === 'ITEM NUMBER') { + colLoc.maximo = i; + validParams++; + } + // console.log(validParams) + } + // Checking if mandatory columns are filled + if (colLoc.siteID != -1 || colLoc.storeroom != -1 || colLoc.vendor != -1 || colLoc.catNum != -1) { + if (colLoc.siteID == -1 || colLoc.storeroom == -1) { + let numMissing = 0; + let missingCols = ''; + const missingColArr = []; + console.log('missing params'); + for (const property in colLoc) { + if (colLoc[property] == -1 && property != 'vendor' && property != 'catNum') { + console.log(property); + numMissing++; + missingColArr.push(property.toLowerCase()); + } + } + missingCols = missingColArr.join(', '); + document.getElementById('batch-upload-status-text').innerHTML = `Table is missing ${numMissing} column(s): (${missingCols}). Table will not be uploaded!`; + return; + } + } + else { + if (validParams < 5) { + let missingCols = ''; + const missingColArr = []; + console.log('missing params'); + for (const property in colLoc) { + if (colLoc[property] == -1 && property != 'siteID' && property != 'storeroom' && property != 'vendor' && property != 'catNum') { + console.log(property); + missingColArr.push(property.toLowerCase()); + } + } + missingCols = missingColArr.join(', '); + document.getElementById('batch-upload-status-text').innerHTML = `Table is missing ${5 - validParams} column(s): (${missingCols}). Table will not be uploaded!`; + return; + } + } + let invalidItems = 0; + // Make item for request that includes inventory upload + if (validParams > 5) { + let site = undefined; + let storeroom = undefined; + let vendor = undefined; + let catNum = undefined; + for (let i = 2; i <= rows; i++) { + const desc = sanitizeString(document.getElementById(i + '-' + colLoc.description).innerHTML); + const uom = sanitizeString(document.getElementById(i + '-' + colLoc.uom).innerHTML).toUpperCase(); + const commGroup = sanitizeString(document.getElementById(i + '-' + colLoc.commGroup).innerHTML); + const glclass = sanitizeString(document.getElementById(i + '-' + colLoc.glClass).innerHTML).toUpperCase(); + if (colLoc.siteID != -1) { + site = sanitizeString(document.getElementById(i + '-' + colLoc.siteID).innerHTML).toUpperCase(); + } + if (colLoc.storeroom != -1) { + storeroom = sanitizeString(document.getElementById(i + '-' + colLoc.storeroom).innerHTML).toUpperCase(); + } + if (colLoc.vendor != -1) { + vendor = sanitizeString(document.getElementById(i + '-' + colLoc.vendor).innerHTML); + } + if (colLoc.catNum != -1) { + catNum = sanitizeString(document.getElementById(i + '-' + colLoc.catNum).innerHTML); + } + const maximo = sanitizeString(document.getElementById(i + '-' + colLoc.maximo).innerHTML); + // if all required parameters are not available, don't create the item and move to next row + if (desc == '' || uom == '' || commGroup == '' || glclass == '' || desc == 0 || uom == 0 || commGroup == 0 || glclass == 0 || site == '' || storeroom == '') { + updateItemStatus('error', (i - 1)); + items.push(''); + invalidItems++; + continue; + } + const item = new Item(undefined, desc, uom, commGroup, glclass, site, storeroom, vendor, catNum); + if (colLoc.maximo != -1 && maximo != 0 && maximo.toString().length === 7) { + item.itemnumber = maximo; + } + else if (desc.toUpperCase().includes('DWG')) { + item.series = 98; + } + else if (commGroup == '490' && glclass == 'PLS') { + // Change when when item num reachs 9920000 + item.series = 991; + } + // console.log(item); + // add the item to the array + items.push(item); + } + } + // Make item for request that doesn't need inventory upload + else { + for (let i = 2; i <= rows; i++) { + const desc = sanitizeString(document.getElementById(i + '-' + colLoc.description).innerHTML); + const uom = sanitizeString(document.getElementById(i + '-' + colLoc.uom).innerHTML).toUpperCase(); + const commGroup = sanitizeString(document.getElementById(i + '-' + colLoc.commGroup).innerHTML); + const glclass = sanitizeString(document.getElementById(i + '-' + colLoc.glClass).innerHTML).toUpperCase(); + const maximo = sanitizeString(document.getElementById(i + '-' + colLoc.maximo).innerHTML); + // if all required parameters are not available, don't create the item and move to next row + if (desc == '' || uom == '' || commGroup == '' || glclass == '' || desc == 0 || uom == 0 || commGroup == 0 || glclass == 0) { + updateItemStatus('error', (i - 1)); + items.push(''); + invalidItems++; + continue; + } + const item = new Item(undefined, desc, uom, commGroup, glclass); + if (colLoc.maximo != -1 && maximo != 0 && maximo.toString().length === 7) { + item.itemnumber = maximo; + } + else if (desc.toUpperCase().includes('DWG')) { + item.series = 98; + } + else if (commGroup == '490' && glclass == 'PLS') { + // Change when when item num reachs 9920000 + item.series = 991; + } + // console.log(item); + // add the item to the array + items.push(item); + } + } + if (invalidItems > 0) { + document.getElementById('batch-upload-status-text').innerHTML = `Warning! ${invalidItems} invalid items will not be uploaded`; + } + // return the item array + return items; +} +/** + * Uploads an item from item information accordion dropdown (single item upload) + * + */ +function uploadItem() { + return __awaiter(this, void 0, void 0, function* () { + document.getElementById('confirm-btn').innerHTML = ' Uploading...'; + document.getElementById('confirm-btn').disabled = true; + const worker = new WorkerHandler(); + const item = new Item(sanitizeString(document.getElementById('interact-num').value), sanitizeString(document.getElementById('maximo-desc').value), sanitizeString(document.getElementById('uom-field').value), sanitizeString(document.getElementById('com-group').value), sanitizeString(document.getElementById('gl-class').value)); + if (document.getElementById('long-desc').value.length > 0) { + item.longdescription = document.getElementById('long-desc').value; + } + worker.work(['uploadItems', [item]], (e) => { + console.log(e); + if (e === undefined || typeof e != 'string' || e == 200) { + document.getElementById('error').innerHTML = 'Upload Success'; + document.getElementById('confirm-btn').innerHTML = 'Upload Item'; + document.getElementById('confirm-btn').disabled = false; + new Toast('Upload Complete!', 'bg-success'); + const itemUrl = `https://${CONSTANTS.ENV}.iko.max-it-eam.com/maximo/oslc/graphite/manage-shell/index.html?event=loadapp&value=item&additionalevent=useqbe&additionaleventvalue=itemnum=${item.itemnumber}`; + document.getElementById('error').innerHTML = `Item Upload Successful! (Click to view item) `; + document.getElementById('item-link').addEventListener('click', function (x) { + x.preventDefault(); + shell.openExternal(itemUrl); + }); + } + else { + document.getElementById('error').innerHTML = 'Upload Fail'; + document.getElementById('confirm-btn').innerHTML = 'Upload Item'; + document.getElementById('confirm-btn').disabled = false; + // TODO: fail messages + document.getElementById('error').innerHTML = `Item Upload Failed! ${e}`; + } + }); + }); +} +/** + * Uploads an array of items + * + * @param {Array} items + */ +function batchUploadItems(items) { + return __awaiter(this, void 0, void 0, function* () { + const worker = new WorkerHandler(); + // disable clear and upload buttons while uploading items to prevent duplicate requests + const btn = document.getElementById('batch-upload-btn'); + const clearBtn = document.getElementById('clear-batch-items-btn'); + clearBtn.disabled = true; + btn.disabled = true; + worker.work(['uploadItems', items, true], (e) => { + let finishText = `Upload Finished! ${e[2]} items uploaded, ${e[3]} items added to inventory. `; + if (e[0] == 'failure') { + new Toast(`Invalid! ${e[1]}}!`); + } + clearBtn.disabled = false; + btn.disabled = false; + updateItemNums(e[0]); + const rows = parseInt(document.getElementById('batch-items-table').getAttribute('data-rows')) - 1; + let nums = ''; + for (let i = 2; i <= rows + 1; i++) { + nums += document.getElementById(`${i}-${colLoc.maximo}`).innerHTML ? (document.getElementById(`${i}-${colLoc.maximo}`).innerHTML + ',') : ''; + } + if (e[2] > 0) { + const itemUrl = `https://${CONSTANTS.ENV}.iko.max-it-eam.com/maximo/oslc/graphite/manage-shell/index.html?event=loadapp&value=item&additionalevent=useqbe&additionaleventvalue=itemnum=${nums}`; + finishText += `Click to view:`; + document.getElementById('batch-upload-status-text').innerHTML = finishText; + document.getElementById('batch-link').addEventListener('click', function (e) { + e.preventDefault(); + shell.openExternal(itemUrl); + }); + } + else { + document.getElementById('batch-upload-status-text').innerHTML = finishText; + } + console.log('upload finished'); + }); + }); +} +/** + * Gets a list of newly generated item nums and updates the table with them. + * + * If an item has just been uploaded, populates item num cell with new number. + * + * @param {int[][]} arr array of pairs of item nums and table row indexes + */ +function updateItemNums(arr) { + for (const pair of arr) { + const itemNum = pair[0]; + const itemRowIndex = pair[1]; + // update item number cell + const cell = document.getElementById(`${itemRowIndex + 1}-${colLoc.maximo}`); + cell.innerHTML = itemNum; + // highlight the item number yellow to signify that it was newly uploaded + cell.classList.add('table-alert'); + } +} +// ////////////////////// +function skipRow() { + const row = document.getElementById('current-row').innerHTML; + interactiveGoNext(Number(row) + 1); +} +function finishLoadingBatch(params) { + const bar = new ProgressBar(); + // this has a special work thread since initializing a worker thread takes ~700 ms which is too long + document.getElementById('valid-row').innerHTML = params[1]; + document.getElementById('total-row').innerHTML = params[2]; + const worker = new Worker('./worker.js'); + const db = new Database(); + let description = db.getDescription(params[0]); + if (description === undefined) { + bar.update(100, 'Done!'); + worker.terminate(); + new Toast('Finished Batch Processing'); + return false; + } + bar.update(0, 'Processing Descriptions'); + processBatch(worker, params[0], description); + worker.onmessage = (msg) => { + if (msg.data[0] === 'nextrow') { + description = db.getDescription(msg.data[1]); + if (description === undefined) { + params = worksheetParams(document.getElementById('worksheet-path').value); + worker.postMessage([ + 'saveProcessed', + [params, msg.data[1]], + ]); + new Toast('Finished Batch Processing'); + new Toast('Please wait for file to finish saving...'); + return false; + } + document.getElementById('current-row').innerHTML = description.row; + bar.update(msg.data[1] / params[2] * 100, `Processing Description. Row: ${msg.data[1]} of ${params[2]}`); + processBatch(worker, msg.data[1], description); + } + else if (msg.data[0] === 'saveComplete') { + interactiveGoNext(msg.data[1]); + new Toast('File Saved'); + worker.terminate(); + } + else { + console.log(`IDK: ${msg.data}`); + } + }; +} +function processBatch(worker, row, description) { + const interactive = document.getElementById('modeSelect').checked; + const related = document.getElementById('relatedSelect').checked; + const translate = document.getElementById('translateSelect').checked; + const params = worksheetParams(document.getElementById('worksheet-path').value); + if (interactive) { + new Toast('Pausing / Switching to Interactive Mode'); + worker.postMessage([ + 'saveProcessed', + [params, row], + ]); + } + else { + worker.postMessage([ + 'nonInteractive', + [ + related, + translate, + description.description, + document.getElementById('selected-language').value, + params, + row, + ], + ]); + } +} +function continueAuto() { + document.getElementById('modeSelect').checked = false; + finishLoadingBatch([ + Number(document.getElementById('current-row').innerHTML), + document.getElementById('valid-row').innerHTML, + document.getElementById('total-row').innerHTML, + ]); +} +function interactiveGoNext(row) { + const bar = new ProgressBar(); + const db = new Database(); + const description = db.getDescription(row); + if (description === undefined) { + bar.update(100, 'Done!'); + new Toast('End of File Reached'); + return false; + } + document.getElementById('current-row').innerHTML = description.row; + if (description) { + const worker = new WorkerHandler(); + document.getElementById('maximo-desc').value = description.description; + worker.work(['validSingle', description.description], showResult); + } + else { + const field = document.getElementById('maximo-desc'); + field.placeholder = 'Row is blank, press skip row to go next'; + field.value = ''; + const bar = new ProgressBar(); + bar.update(100, 'Done'); + } +} +function validSingle(isExtended = false) { + const bar = new ProgressBar(); + bar.update(0, 'Starting Item Description Validation'); + const raw_desc = document.getElementById('maximo-desc').value; + const worker = new WorkerHandler(); + worker.work(['validSingle', raw_desc], (result) => { + showResult(result, isExtended); + }); +} +function showResult(result, isExtended = false) { + let triDesc = document.getElementById('result-triple-main'); + triDesc.value = result[0][0]; + triDesc = document.getElementById('result-triple-ext1'); + triDesc.value = result[0][1]; + triDesc = document.getElementById('result-triple-ext2'); + triDesc.value = result[0][2]; + const related = document.getElementById('relatedSelect').checked; + const translate = document.getElementById('translateSelect').checked; + calcConfidence(result[0][3]); + document.getElementById('validate-badge').innerHTML = 'New'; + if (translate) { + translationDescription(result[0][3]); + } + if (related) { + findRelated(result[0], isExtended); + } +} +function ItemAnalysis() { + return __awaiter(this, void 0, void 0, function* () { + const valid = new Validate(); + const raw_desc = document.getElementById('maximo-desc').value; + const result = yield valid.validateSingle(raw_desc); + let triDesc = document.getElementById('result-triple-main'); + triDesc.value = result[0]; + triDesc = document.getElementById('result-triple-ext1'); + triDesc.value = result[1]; + triDesc = document.getElementById('result-triple-ext2'); + triDesc.value = result[2]; + calcConfidence(result[3]); + }); +} +function findRelated(result, isExtended = false) { + const worker = new WorkerHandler(); + worker.work(['findRelated', result[3], isExtended], (result) => { + showRelated(result, isExtended); + }); +} +function translationDescription(description) { + // for now do not translate if english is selected + if (document.getElementById('selected-language').value != 'en') { + const worker = new WorkerHandler(); + if (document.getElementById('result-triple-ext1').value) { + description = `${document.getElementById('result-triple-main').value},${document.getElementById('result-triple-ext1').value}`; + } + else { + description = document.getElementById('result-triple-main').value; + } + worker.work([ + 'translateItem', + description, + document.getElementById('selected-language').value, + 'post', + ], displayTranslation); + } + else { + new Toast('Currently translation into English is not supported'); + } +} +function displayTranslation(data) { + document.getElementById('trans-desc').value = data[0]; + document.getElementById('translation-description').value = `The following words do not have a translation:\n${data[1]}\nPlease check logs at bottom of page for details`; + auto_grow('translation-description'); +} +function calcConfidence(data) { + let description; + let level = 0; + let tree = ''; + let parent = 0; + const regex = /\d+/g; + const db = new Database(); + let analysis; + let result = ''; + const option = { + style: 'percent', + minimumFractionDigits: 1, + maximumFractionDigits: 1, + }; + const formatter = new Intl.NumberFormat('en-US', option); + if ((data === null || data === void 0 ? void 0 : data.length) > 0) { // test if description is blank + description = data.split(','); + for (let j = 0; j < description.length; j++) { + if (!(description[j].match(regex))) { + if (db.isManufacturer(description[j])) { + result = `${result}\n${description[j]} is confirmed as a manufacturer`; + } + else { + level++; + if (level == 1) { + tree = description[j]; + } + else { + tree = tree + ',' + description[j]; + } + analysis = db.getAnalysis(tree); + if (analysis) { + if (level == 1) { + if (analysis.count >= 100) { + result = `${description[j]}: is COMMONLY used as an Item Type.\n${analysis.count}: occurrences found`; + } + else if (analysis.count >= 20) { + result = `${description[j]}: is SOMETIMES used as an Item Type.\n${analysis.count}: occurrences found`; + } + else { + result = `WARNING: ${description[j]}: is an UNCOMMON Item Type.\nPlease double check.\n${analysis.count}: occurrences found`; + } + } + else { + if (analysis.count / parent >= 0.25) { + result = `${result}\n${description[j]} is COMMONLY used as an item descriptor for ${tree}.\n${analysis.count} of ${parent} = ${formatter.format(analysis.count / parent)}`; + } + else if (analysis.count / parent >= 0.05) { + result = `${result}\n${description[j]} is SOMETIMES used as an item descriptor for ${tree}.\n${analysis.count} of ${parent} = ${formatter.format(analysis.count / parent)}`; + } + else { + result = `${result}\n${description[j]} is an UNCOMMON item descriptor for ${tree}.\nPlease double check.\n${analysis.count} of ${parent} = ${formatter.format(analysis.count / parent)}`; + } + } + parent = analysis.count; + } + else { + result = `${result}\n${description[j]}: Does not exist in Maximo as part of: ${tree}.\nPlease Check with Corporate`; + } + } + } + } + document.getElementById('valid-description').value = result.trim(); + } + else { + new Toast('Blank Description'); + } +} +/** + * Initializes search results table and populates relatedResults object + * with search results. + * + * @param {Array< Map>,Map,String>} result array of [array of item nums of all search results, map of item nums to descriptions, and search query with words separated by commas] + * @param {bool} isExtended whether the user has clicked extended search + */ +function showRelated(result, isExtended = false) { + return __awaiter(this, void 0, void 0, function* () { + const bar = new ProgressBar(); + if (!result[0]) { + bar.update(100, 'Done!'); + return false; + } + // reverse results to get newest items first. + // technically this isn't the best way to do + // this because results aren't guaranteed + // to be in order of oldest to newest. + for (const [key, value] of Object.entries(result[0])) { + result[0][key] = result[0][key].reverse(); + } + // populate global variable with search results (bad practice, but it works) + relatedResults = { + idx: 0, + curKey: 0, + results: result, + }; + // reset table after called + const relatedTable = document.getElementById('related-table'); + const numResultsText = document.getElementById('num-results'); + if (isExtended) { + relatedTable.classList.add(`isExt`); + } + else { + if (relatedTable.classList.contains(`isExt`)) { + relatedTable.classList.remove(`isExt`); + } + } + // Add headings to the search results table + relatedTable.innerHTML = ` + + + + + + + ${(isExtended ? '' : '')} + + + + + + + +
    Percent MatchItem NumberItem DescriptionMore InfoUOMC_GroupGL_Class
    + `; + numResultsText.innerHTML = `Found ${Object.entries(result[1]).length} results`; + // expand the search results accordion + document.getElementById('related-items-accordion-btn').classList.remove('collapsed'); + // load a couple of items + loadRelated(); + html = new bootstrap.Collapse(document.getElementById('accordion-relatedItem'), { toggle: false }); + html.show(); + bar.update(100, 'Done!'); + }); +} +function loadRelated() { + var _a; + // check if user clicked extended search + const isExtended = document.getElementById('related-table').classList.contains('isExt'); + // a map with percent match as key (in decimal form) and array of items as value + // for example, if the key is 1, the list of items match with the search query 100%. If the key is 0.5, the list of items match with the search query 50%. + const scores = relatedResults.results[0]; + // relatedResults.idx is like a bookmark. It keeps track of how many items have been loaded from the array of items associated with the current key in scores. + // relatedResults.curKey is the number of the current key that is being loaded if you were to iterate thru the keys in scores. + // For example, the first key's number would be 0, second key 1, etc. + if (relatedResults.curKey >= Object.entries(scores).length) { + // If curKey is equal or larger than the number of keys in scores, then there are no more items to load, so return + return; + } + else if (Object.entries(scores)[relatedResults.curKey][1].length == 0) { + // If there are no items associated with the current key, then move to the next key and try loading items again. + relatedResults.curKey++; // increment curKey so that the next time the function runs, it will try to load items from the next key + relatedResults.idx = 0; // reset idx so that it starts from the beginning of the array + loadRelated(); + return; // return so we don't do make an infinite loop + } + const step = 20; // number of items to load at once. + // get arrs from results obj + const itemNames = relatedResults.results[1]; // a map with the 9-series number of the item as the key and the item info as value. Item info is an array with 4 items: [description, gl class, uom, commodity group] + const searchWords = relatedResults.results[2].split(','); // an array of the search query split by commas. For example, if the search query is "test, item, description", then searchWords would be ["test", "item", "description"] + let html = ''; + let color = ''; // html is the html that will be added to the search results table. color is the color of the row in the table. + let itemDescription; + // formatting options for percent match. Converts decimal to percent and rounds to nearest whole number. + const option = { + style: 'percent', + minimumFractionDigits: 0, + maximumFractionDigits: 0, + }; + const formatter = new Intl.NumberFormat('en-US', option); + // technically this is bad practise since object order might not be guarenteed + // https://stackoverflow.com/questions/983267/how-to-access-the-first-property-of-a-javascript-object + const percentMatch = Object.entries(scores)[relatedResults.curKey][0]; // get the percent match (name of the current key) + const itemNumList = Object.entries(scores)[relatedResults.curKey][1]; // get the array of items associated with key + let itemsToLoad; // array of items to load + if (relatedResults.idx + step >= itemNumList.length) { + // if there are less than 20 items to load, load the remaining items in value and increment curKey and reset idx + // this way, the next time the function is called, the next key will be loaded instead + itemsToLoad = itemNumList.slice(relatedResults.idx, undefined); // get array of items from idx to end of array + relatedResults.curKey++; + relatedResults.idx = 0; + } + else { + itemsToLoad = itemNumList.slice(relatedResults.idx, relatedResults.idx + step); + relatedResults.idx += step; + } + // iterate thru each item in value array + for (const itemNum of itemsToLoad) { + itemDescription = itemNames[itemNum][0]; + if (itemDescription) { + // Bold all words in item description that match the search query + for (const word of searchWords) { + split = word.split(' '); + for (const smallWord of split) { + if (smallWord.length > 0) { + itemDescription = itemDescription.replace(new RegExp(`${smallWord}`, 'i'), `${(_a = itemDescription.match(new RegExp(`${smallWord}`, 'i'))) === null || _a === void 0 ? void 0 : _a[0]}`); + } + } + } + // set row color based on percent match + if (percentMatch > 0.7) { + color = 'table-success'; // green + } + else if (percentMatch > 0.4) { + color = 'table-warning'; // yellow + } + else { + color = 'table-danger'; // red + } + // create HTML row. + // In extended search, the vendor info is split from the item description by a | (pipe character). + // All info after the pipe character is put into another column. + // If the item description does not have a pipe character, then the second column is not loaded. + html = `${html}\n + ${formatter.format(percentMatch)} + ${itemNum} + ${(isExtended ? `${itemDescription.substring(0, itemDescription.indexOf('|'))}` : `${itemDescription}`)} + ${(isExtended ? `${itemDescription.slice(itemDescription.indexOf('|') + 1)}` : '')} + ${itemNames[itemNum][2]} + ${itemNames[itemNum][3]} + ${itemNames[itemNum][1]} + add_task`; + } + else { + html = `0\nxxxxxxx\nNo Related Items Found`; + } + } + // add html to table + const relatedTable = document.getElementById('related-items'); + relatedTable.innerHTML += html; + // if less than 5 items loaded, load more + if (itemsToLoad.length < 5) { + document.getElementById('everything').dispatchEvent(new Event('scroll')); + } +} +// unused function (was used to copy item validation): probably remove this +function copyResult(copy) { + if (copy === 'single') { + const content = document.getElementById('result-single').innerText; + clipboard.writeText(content); + new Toast('Single Description Copied to Clipboard!'); + } + else { + const desc = []; + let content = ''; + content = document.getElementById('result-triple-main').innerText; + desc.push(content); + content = document.getElementById('result-triple-ext1').innerText; + desc.push(content); + content = document.getElementById('result-triple-ext2').innerText; + desc.push(content); + clipboard.write({ + text: document.getElementById('result-single').innerText, + html: `
    ${desc[0]}${desc[1]}${desc[2]}
    `, + }); + new Toast('Triple Description Copied to Clipboard!'); + } +} diff --git a/built/renderer/item_translation.js b/built/renderer/item_translation.js new file mode 100644 index 0000000..cbad55a --- /dev/null +++ b/built/renderer/item_translation.js @@ -0,0 +1,25 @@ +"use strict"; +// TODO need to fix this... +document.getElementById("translate-single").addEventListener("click", testFunction); +document.getElementById("translate-batch").addEventListener("click", batchTranslate); +function testFunction() { + let filePath = 'C:\\Users\\majona\\Documents\\TranslationDefinitions.xlsx'; + const worker = new WorkerHandler; + worker.work(['refreshTranslations', filePath], finished); +} +function batchTranslate() { + let filePath = 'C:\\Users\\majona\\Documents\\TestFileTranslationDescription.xlsx'; + let params = { + filePath: filePath, + wsname: document.getElementById("ws-name").value || document.getElementById("ws-name").placeholder, + maxNumCol: (document.getElementById("max-num").value || document.getElementById("max-num").placeholder).toUpperCase(), + descriptions: (document.getElementById("input-col").value || document.getElementById("input-col").placeholder).replaceAll(" ", "").toUpperCase().split(","), + manufacturerer: (document.getElementById("input-manu-col").value || document.getElementById("input-manu-col").placeholder).toUpperCase(), + startingRow: parseInt(document.getElementById("start-row").value || document.getElementById("start-row").placeholder) + }; + const worker = new WorkerHandler; + worker.work(['batchTranslate', params], finished); +} +function finished(stuff) { + console.log('finished'); +} diff --git a/built/renderer/observation_template.js b/built/renderer/observation_template.js new file mode 100644 index 0000000..cbf4c5d --- /dev/null +++ b/built/renderer/observation_template.js @@ -0,0 +1,111 @@ +"use strict"; +const { dialog } = require('electron').remote; +const ObservationDatabase = require('../misc/better-sqlite'); +// Debug stuff +// document.getElementById("selected_output").innerHTML = 'C:\\Users\\majona\\Documents\\observationList\\results.xlsx' +// document.getElementById("selected_jobtasks").innerHTML = `C:\\Users\\majona\\Documents\\observationList\\Book1.xlsx` +// document.getElementById("selected_file").innerHTML = 'C:\\Users\\majona\\Documents\\observationList\\Failure Code Building.xlsm' +// document.getElementById("ws-name").value = 'ObservationList' +document.getElementById("topButton").addEventListener("click", toTop); +document.getElementById("endButton").addEventListener("click", toEnd); +document.getElementById("select_file").addEventListener("click", selectFile); +document.getElementById("select_jobtasks").addEventListener("click", selectJobTasks); +document.getElementById("select_output").addEventListener("click", selectFolder); +document.getElementById("process").addEventListener("click", processFile); +document.getElementById("dark-mode-switch").addEventListener("click", toggleTheme); +function toggleTheme() { + theme = document.getElementById("dark-mode-switch").checked ? "light" : "dark"; + let str = "[data-bs-theme=\"" + theme + "\"]"; + theme = document.getElementById("dark-mode-switch").checked ? "dark" : "light"; + //console.log(str); + let elms = document.querySelectorAll(str); + for (const elm of elms) { + elm.setAttribute("data-bs-theme", theme); + } +} +function selectFile() { + dialog.showOpenDialog([], { + title: "Select Observation List Spreadsheet", + filters: [ + { name: 'Spreadsheet', extensions: ['xls', 'xlsx', 'xlsm', 'xlsb'] }, + ], + properties: [ + 'openFile' + ] + }).then(result => { + if (!result.canceled) { + document.getElementById("selected_file").innerHTML = result.filePaths[0]; + } + else { + new Toast('File Picker Cancelled'); + } + }); +} +function selectJobTasks() { + dialog.showOpenDialog([], { + title: "Select JobTasks List Spreadsheet", + filters: [ + { name: 'Spreadsheet', extensions: ['xls', 'xlsx', 'xlsm', 'xlsb'] }, + ], + properties: [ + 'openFile' + ] + }).then(result => { + if (!result.canceled) { + document.getElementById("selected_jobtasks").innerHTML = result.filePaths[0]; + } + else { + new Toast('File Picker Cancelled'); + } + }); +} +function selectFolder() { + dialog.showSaveDialog([], { + title: "Save as", + filters: [ + { name: 'Spreadsheet', extensions: ['xls', 'xlsx', 'xlsm', 'xlsb'] }, + ] + }).then(result => { + if (!result.canceled) { + document.getElementById("selected_output").innerHTML = result.filePath; + } + else { + new Toast('File Picker Cancelled'); + } + }); +} +function processFile() { + //getMaximoData(); + let bar = new ProgressBar; + bar.update(0, 'Processing Spreadsheet'); + const worker = new WorkerHandler; + const ws_name = document.getElementById("ws-name").value; + const wb_path = document.getElementById("selected_file").innerHTML; + worker.work(['processObservationList', [wb_path, ws_name]], saveReadObserves); +} +function saveReadObserves(data) { + const sqlite = new ObservationDatabase(); + sqlite.createTables(); + sqlite.insertMeter(data[0][0]); + sqlite.insertObservation(data[0][1]); + getMaximoData(); +} +function getMaximoData() { + let bar = new ProgressBar; + bar.update(33, 'Getting Data From Maximo'); + const worker = new WorkerHandler; + worker.work(['getMaximoObservation'], compareObservLists); +} +function compareObservLists(data) { + let bar = new ProgressBar; + bar.update(66, 'Comparing Data'); + const worker = new WorkerHandler; + const save_path = document.getElementById("selected_output").innerHTML; + const jobTaskPath = document.getElementById("selected_jobtasks").innerHTML; + worker.work(['compareObservLists', data[0], save_path, jobTaskPath], compareDone); +} +function compareDone() { + let bar = new ProgressBar; + bar.update(100, 'Done'); + new Toast('Done'); +} diff --git a/built/renderer/setting.js b/built/renderer/setting.js new file mode 100644 index 0000000..fe0a5d5 --- /dev/null +++ b/built/renderer/setting.js @@ -0,0 +1,71 @@ +"use strict"; +const { ipcRenderer } = require('electron'); +// document.getElementById("update-manu").addEventListener("click", updateManuf); +// document.getElementById("update-abbr").addEventListener("click", updateAbbre); +// document.getElementById("export-all").addEventListener("click", exportTables); +// document.getElementById("create-tables").addEventListener("click", createTables); +document.getElementById("update-trans").addEventListener("click", updateTranslation); +// function updateManuf() { +// let result = update('manufacturer'); +// } +// function updateAbbre() { +// let result = update('abbreviations'); +// } +// function exportTables() { +// // TODO +// } +function updateTranslation() { + ipcRenderer.invoke('select-to-be-translated', 'finished').then((result) => { + if (!result.canceled) { + const worker = new Worker('./worker.js'); + worker.postMessage(['refreshTranslations', result.filePaths[0]], complete); + //document.getElementById("selected_jobtasks").innerHTML = result.filePaths[0]; + } + }); +} +function complete(data) { + console.log(data); + console.log('complete'); +} +function createTables() { + const worker = new Worker('./worker.js'); + worker.postMessage(['createDatabase']); + worker.onmessage = function (e) { + console.log('message from worker'); + if (e.data[0] === 'result') { + // showResult(e.data[1]); + } + else { + console.log('unimplemented worker message'); + } + console.log(e); + }; +} +// function update(updateType) { +// dialog.showOpenDialog([], { +// title: "Select Spreadsheet with ".concat(updateType), +// filters: [ +// { name: 'Spreadsheet', extensions: ['xls', 'xlsx', 'xlsm', 'xlsb', 'csv'] }, +// ], +// properties: [ +// 'openFile' +// ] +// }).then(result => { +// if (!result.canceled) { +// console.log(result.filePaths); +// const worker = new Worker('./worker.js'); +// worker.postMessage(['update', updateType, result.filePaths[0]]); +// worker.onmessage = function (e) { +// console.log('message from worker'); +// if (e.data[0] === 'result') { +// // showResult(e.data[1]); +// } else { +// console.log('unimplemented worker message'); +// } +// console.log(e); +// } +// } else { +// console.log('file picker cancelled'); +// } +// }) +// } diff --git a/built/renderer/start_page.js b/built/renderer/start_page.js new file mode 100644 index 0000000..87c51c4 --- /dev/null +++ b/built/renderer/start_page.js @@ -0,0 +1,71 @@ +"use strict"; +const { ipcRenderer } = require('electron'); +let selected = ''; +const popupAlert = new bootstrap.Modal(document.getElementById('popupAlert'), { toggle: false }); +window.onload = function () { + document.getElementById('dark-mode-switch').checked = (localStorage.getItem('theme') === 'dark' ? true : false); + tryLoginItem(); +}; +function openItem() { + const worker = new WorkerHandler(); + version = ipcRenderer.sendSync('getVersion'); + worker.work(['checkItemCache', version], openMain); + function openMain() { + ipcRenderer.send('loading', 'finished'); + } +} +function tryLoginItem() { + selected = 'openItem'; + const worker = new WorkerHandler(); + worker.work(['checkUser'], checkLogin); +} +function checkLogin(status) { + document.getElementById('failedLogin').classList.remove("d-flex"); + document.getElementById('failedLogin').classList.add("d-none"); + document.getElementById('spinner').classList.remove("d-flex"); + document.getElementById('spinner').classList.add("d-none"); + if (status[0] === 0) { + popupAlert.hide(); + postMessage(['debug', `Successfully logged in to Maximo`]); + switch (selected) { + case 'openItem': + openItem(); + break; + default: + console.log('no default action set'); + } + } + else { + popupAlert.show(); + document.getElementById('failedLogin').classList.remove("d-none"); + document.getElementById('failedLogin').classList.add("d-flex"); + } +} +function tryLoginAgain() { + document.getElementById('spinner').classList.remove("d-none"); + document.getElementById('spinner').classList.add("d-flex"); + document.getElementById('failedLogin').classList.remove("d-flex"); + document.getElementById('failedLogin').classList.add("d-none"); + const worker = new WorkerHandler(); + worker.work(['checkUser', { + userid: document.getElementById('userid').value, + password: '' + }], checkLogin); +} +function noMaximo() { + popupAlert.hide(); + switch (selected) { + case 'openItem': + openItem(); + break; + default: + console.log('no default action set'); + } +} +// document.getElementById("openObserveTemp").addEventListener("click", openObserveTemp); +document.getElementById("openItem").addEventListener("click", tryLoginItem); +// document.getElementById("openItemTranslation").addEventListener("click", openItemTranslation); +// document.getElementById("openAssetDescription").addEventListener("click", openAssetDescription); +document.getElementById("continue").addEventListener("click", noMaximo); +document.getElementById("tryLogin").addEventListener("click", tryLoginAgain); +document.getElementById("dark-mode-switch").addEventListener("click", toggleTheme); diff --git a/built/renderer/worker.js b/built/renderer/worker.js new file mode 100644 index 0000000..77e1ee8 --- /dev/null +++ b/built/renderer/worker.js @@ -0,0 +1,589 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +const Validate = require('../misc/validators'); +const ExcelReader = require('../misc/spreadsheet'); +const Spreadsheet = require('../misc/exceljs'); +const Database = require('../misc/indexDB'); +const SharedDatabase = require('../misc/sharedDB'); +const Maximo = require('../misc/maximo'); +const AssetTranslate = require('../asset_translation/asset_translation_main.js'); +const ObservationDatabase = require('../misc/better-sqlite'); +const TranslationDatabase = require('../item_translation/item-translation-sqlite'); +const path = require('path'); +const Translation = require('../item_translation/item-translation'); +const fs = require('fs'); +const CONSTANTS = require('../misc/constants.js'); +/** + * Handles messages from the WorkerHandler + * + * @param {Array} e + */ +onmessage = function (e) { + let valid; + let maximo; + let result; + // decide which function to run based on the first element in the array + switch (e.data[0]) { + case 'validSingle': + valid = new Validate(); + valid.validateSingle(e.data[1]).then((result) => { + postMessage(['result', result]); + }); + break; + case 'validBatch': + valid = new Validate(); + valid.validateBatch(e.data[1]).then((result) => { + postMessage(['result', result]); + }); + break; + case 'update': + update(e); + break; + case 'createDatabase': + const db = new Database(); + db.createAbbreviations(); + db.createManufacturers(); + postMessage(['result', 'done']); + break; + case 'findRelated': + maximo = new Database(); + maximo.findRelated(e.data[1], e.data[2], true); + break; + case 'interactive': + interactive(e); + break; + case 'writeDesc': + writeDesc(e); + break; + case 'writeNum': + writeItemNum(e.data[1]); + break; + case 'checkItemCache': + checkItemCache(e.data[1]); + break; + case 'processObservationList': + const excel = new Spreadsheet(e.data[1][0]); + excel.readObservList(e.data[1][1]); + break; + case 'getMaximoObservation': + maximo = new Maximo(); + maximo.getObservations(); + break; + case 'compareObservLists': + compareObservLists(e.data[1], e.data[2], e.data[3]); + break; + case 'refreshTranslations': + refreshTranslations(e.data[1]); + break; + case 'batchTranslate': + batchTranslate(e.data[1]); + break; + case 'nonInteractive': + nonInteractiveSave(e.data[1]); + break; + case 'loadItem': + maximo = new Database(); + result = maximo.loadItem(e.data[1]); + if (result) { + postMessage(['result', result]); + } + else { + postMessage(['error', `${e.data[1]} cannot be found in Maximo`]); + } + break; + case 'translatepms': + const translate = new AssetTranslate(); + translate.translate(e.data[1]); + break; + case 'getCurItemNumber': + getCurItemNum(e.data[1]); + break; + case 'uploadItems': + e.data[2] ? uploadAllItems(e.data[1], e.data[2]) : uploadAllItems(e.data[1]); + break; + case 'translateItem': + const trans = new Translation(); + result = trans.contextTranslate(e.data[1], e.data[2], e.data[3]); + break; + case 'saveProcessed': + saveProgress(e.data[1]); + break; + case 'checkUser': + checkUser(e.data[1]); + break; + case 'uploadImages': + uploadImages(e.data[1]); + break; + default: + console.log(`Unimplimented work ${e.data[0]}`); + } +}; +function saveProgress(params) { + return __awaiter(this, void 0, void 0, function* () { + const db = new Database(); + const data = db.getAllWorkingDesc(); + const excel = new ExcelReader(params[0].filePath); + const result = yield excel.saveNonInteractive(params, data); + console.log(result); + postMessage(['saveComplete', Number(params[1]) + 1]); + }); +} +/** + * Get the latest item number for the given series (91, 98, 99) + * + * @param {string} series + */ +function getCurItemNum(series) { + return __awaiter(this, void 0, void 0, function* () { + const maximo = new Maximo(); + let num; + try { + num = yield maximo.getCurItemNumber(series); + postMessage(['result', 1, num]); + } + catch (err) { + postMessage(['fail', err]); + postMessage(['result', 0, 'Unable to get number']); + } + }); +} +function nonInteractiveSave(params) { + try { + if (params[0]) { + // find related + const maximo = new Database(); + const related = maximo.findRelated(params[2], false); + for (const value of Object.entries(related[0])) { + if (value[1][0]) { + params[0] = value[1][0]; + break; + } + params[0] = null; + } + // gets first element in related object scores + // technically this is bad practise since object order might not be guarenteed + // https://stackoverflow.com/questions/983267/how-to-access-the-first-property-of-a-javascript-object + } + if (params[1]) { + // translate + const trans = new Translation(); + params[1] = trans.contextTranslate(params[2], params[3], 'return'); + } + const db = new Database(); + db.saveDescriptionAnalysis({ related: params[0], translate: params[1] }, params[5]); + // number of rows should be shown and that should be used to determine when to save / finsih + // also need stop / cancel button + } + finally { + postMessage(['nextrow', Number(params[5]) + 1]); + } +} +// depre +function batchTranslate(params) { + return __awaiter(this, void 0, void 0, function* () { + // translate description in file to all available languagues + const excel = new Spreadsheet(params.filePath); + const descs = yield excel.getDescriptions(params); + const trans = new Translation(); + const db = new TranslationDatabase(); + const langs = db.getLanguages(); + let translated; + let result; + let missing = []; + const allTranslated = []; + for (const desc of descs) { + translated = desc; + for (const lang of langs) { + result = trans.translate({ lang: lang, description: desc.description }); + translated[lang] = result.description; + missing = missing.concat(result.missing); + } + allTranslated.push(translated); + } + console.log(allTranslated); + console.log(missing); + fs.copyFileSync(params.filePath, `${params.filePath}.backup`); + const writeExcel = new Spreadsheet(params.filePath); + writeExcel.saveTranslations({ langs: langs, item: allTranslated, missing: missing }); + }); +} +function interactive(e) { + return __awaiter(this, void 0, void 0, function* () { + const excel = new ExcelReader(e.data[1].filePath); + const data = yield excel.getDescriptions(e.data[1]); + const db = new Database(); + const dataSaved = db.saveDescription(data); + postMessage(['result', parseInt(e.data[1].startRow), dataSaved, data.length]); + }); +} +function writeDesc(e) { + return __awaiter(this, void 0, void 0, function* () { + const excel = new ExcelReader(e.data[1][0].filePath); + const result = yield excel.saveDescription(e.data[1]); + if (result) { + postMessage(['result', result]); + } + else { + // fail message + } + }); +} +function update(e) { + return __awaiter(this, void 0, void 0, function* () { + const updateType = e.data[1]; + const excel = new ExcelReader(e.data[2]); + const db = new Database(); + if (updateType === 'manufacturer') { + const data = yield excel.getManufactures(); + db.populateManufacturers(data); + } + else if (updateType === 'abbreviations') { + const data = yield excel.getAbbreviations(); + db.populateAbbreviations(data); + } + }); +} +function refreshTranslations(filePath) { + return __awaiter(this, void 0, void 0, function* () { + // load updated translation list from excel file + const excel = new Spreadsheet(filePath); + const data = yield excel.getTranslations(); + const db = new TranslationDatabase(); + const result = db.refreshData(data); + postMessage(['result', result]); + }); +} +function compareObservLists(data, savePath, jobTaskPath) { + return __awaiter(this, void 0, void 0, function* () { + const sqlite = new ObservationDatabase(); + // compare condition domain definition + const removeOldMeters = []; + for (const meter of data[0]) { + result = sqlite.compareDomainDefinition(meter.list_id, meter.inspect, 1); + if (!result) { + removeOldMeters.push(meter.list_id); + } + } + const newMeters = sqlite.getNewDomainDefinitions(); + // compare condition domain values + const removeOldObservations = []; + for (const observation of data[1]) { + result = sqlite.compareDomainValues(observation.search_str, observation.observation); + if (!result) { + removeOldObservations.push(observation.search_str.replace('~', ':')); + } + } + const newObservations = sqlite.getNewDomainValues(); + // compare data in meter table + const maximo = new Maximo(); + data = yield maximo.getMeters(); + const removeOldMaximoMeters = []; + for (const meter of data) { + result = sqlite.compareDomainDefinition(meter.list_id, meter.inspect, 2); + if (!result) { + removeOldMaximoMeters.push(meter.list_id); + } + } + const newMaximoMeters = sqlite.getNewMaximoMeters(); + const excel = new Spreadsheet(jobTaskPath); + data = yield excel.getJobTasks(); + sqlite.saveJobTasks(data); + sqlite.compareJobTasks(); + const newJobTasks = sqlite.getJobTasks(2); + const removeJobTasks = sqlite.getJobTasks(0); + const excelW = new Spreadsheet(savePath); + yield excelW.saveObserListChanges({ + domainDef: { changes: newMeters, delete: removeOldMeters }, + domainVal: { changes: newObservations, delete: removeOldObservations }, + meter: { changes: newMaximoMeters, delete: removeOldMaximoMeters }, + jobTask: { changes: newJobTasks, delete: removeJobTasks }, + }); + }); +} +function writeItemNum(data) { + return __awaiter(this, void 0, void 0, function* () { + const maximo = new Database(); + const item = maximo.loadItem(data[4]); + if (item) { + const excel = new ExcelReader(data[0]); + const result = yield excel.saveNumber(data); + if (result) { + postMessage(['result', result]); + } + } + else { + postMessage(['warning', `${data[4]} cannot be found in Maximo`]); + } + }); +} +function checkUser(credentials = {}) { + return __awaiter(this, void 0, void 0, function* () { + postMessage(['debug', `Checking Maximo Login`]); + const maximo = new Maximo(); + console.log(`logging in to https://${CONSTANTS.ENV}.iko.max-it-eam.com/maximo/api/whoami?lean=1`); + const currStatus = yield maximo.checkLogin(credentials === null || credentials === void 0 ? void 0 : credentials.userid, credentials === null || credentials === void 0 ? void 0 : credentials.password); + const validUser = currStatus.status; + if (validUser) { + postMessage(['result', true]); + } + else { + postMessage(['result', false]); + } + }); +} +/** + * Check cache of item information + * Update with new items from Maximo + * @param {String} version current app version + */ +function checkItemCache(version) { + return __awaiter(this, void 0, void 0, function* () { + // check internal cache of item information and update with new items in maximo + postMessage(['debug', `Loading Item Module`]); + const maximo = new Maximo(); + postMessage(['debug', `0%: Checking Program Version`]); + const db = new Database(); + const shareDB = new SharedDatabase(); + if (!(yield shareDB.checkVersion(version))) { + db.createTables(); + const filePath = path.join(require('path').resolve(__dirname).replace('src\\renderer', 'assets'), 'item_information.xlsx'); + const excel = new ExcelReader(filePath); + postMessage(['debug', `10%: Loading cache data from file`]); + db.clearItemCache(); + const data = yield excel.getItemCache(); + postMessage(['debug', `20%: Saving data to cache`]); + db.saveItemCache(data[0]); + db.saveInventoryCache(data[2]); + postMessage(['debug', `30%: Loading Manufacturer cache data from file`]); + const manu = yield excel.getManufactures(); + const abbr = yield excel.getAbbreviations(); + postMessage(['debug', `40%: Saving data to Manufacturer cache`]); + db.populateAbbr(abbr); + db.saveManufacturers(manu); + } + let curVersion; + curVersion = db.getVersion('inventory'); + curVersion = curVersion[0].rowstamp; + postMessage(['debug', `50%: Getting inventory with changes after: ${curVersion} from Maximo`]); + const newInventory = yield maximo.getNewInventory(curVersion); + if (newInventory) { + postMessage(['debug', '55%: Saving maximo data to inventory cache']); + db.saveInventoryCache(newInventory[0]); + } + curVersion = db.getVersion('maximo'); + curVersion = curVersion[0].changed_date; + postMessage(['debug', `60%: Getting items with changes after: ${curVersion} from Maximo`]); + let newItems = yield maximo.getNewItems(curVersion); + if (newItems) { + postMessage(['debug', '70%: Saving maximo data to item cache']); + db.saveItemCache(newItems[0]); + } + const itemNums = new Map(); + newInventory[0].forEach((inventory) => { + if (!itemNums.has(inventory[0])) { + itemNums.set(inventory[0], inventory[0]); + } + }); + newItems[0].forEach((item) => { + if (!itemNums.has(item[0])) { + itemNums.set(item[0], item[0]); + } + }); + const processedItems = db.processNewItems(itemNums); + db.saveItemCache(processedItems); + curVersion = db.getVersion('manufacturer'); + curVersion = curVersion[0].changed_date; + postMessage(['debug', `80%: Getting Manufacturer with changes after: ${curVersion} from Maximo`]); + newItems = yield maximo.getNewManufacturers(curVersion); + if (newItems) { + postMessage(['debug', '90%: Saving maximo data to Manufacturer cache']); + db.saveManufacturers(newItems[0]); + } + postMessage(['result', 'done']); + }); +} +/** + * Uploads a list of items to Maximo + * + * @param {Item[] | string[]} items - Array of items to upload + * @param {boolean} doUpdate - Whether or not to update item status. Item status is not updated for single item upload, but is updated for batch upload + * @postmessage list of new item numbers and item upload statistics + */ +function uploadAllItems(items, doUpdate = false) { + return __awaiter(this, void 0, void 0, function* () { + const maximo = new Maximo(); + // row index of the current item in the table (the first row has an index of 1, initializing to 0 because it gets incremented in the loop) + let rowIndex = 0; + // newNums is a list of new item numbers and their corresponding row index + const newNums = []; + // num is the current item number, numFails is the number of items that failed to upload, numSuccesses is the number of items that successfully uploaded, numStoreroomSuccesses is the number of items that were successfully added to a storeroom + let num; + let numFails = 0; + let numSuccesses = 0; + let numStoreroomSuccesses = 0; + for (const item of items) { + let needsNewNum = false; // by default the item does not need a new item number + rowIndex++; // for each item increment rowindex + try { + // if item is an empty string, it is invalid, therefore skip it + if (item === '') { + continue; + } + if (item.itemnumber === 0 || item.itemnumber.length != 7) { + // if the item number is 0 or is not 7 characters long, assign a new item number + needsNewNum = true; + // get latest item number + num = yield maximo.getCurItemNumber(item.series); + // increment it by 1 to get an unused item number + num++; // since we are incrementing nums, we can't use a 9S series number because it will be a string, not a number + // push the new item number and the row index to newNums + newNums.push([num, rowIndex]); + // set itemnumber property of the item to the new item number + item.itemnumber = num; + } + } + catch (err) { + // if theres an error, remove the new item num from newNums as it wont be used for the failed item. + if (needsNewNum) + newNums.pop(); + numFails++; + console.log(err + ', Item Upload Failed'); + if (doUpdate) + postMessage(['update', 'fail', rowIndex]); + postMessage(['fail', err]); + continue; + } + try { + const result = yield maximo.uploadToMaximo(item); + if (!result) { + if (doUpdate) + postMessage(['update', 'fail', rowIndex]); + throw new Error('Upload Failed'); + } + else { + if (doUpdate) + postMessage(['update', 'success', rowIndex]); + postMessage(['debug', `Upload of ${item.description} succeeded`]); + console.log('Upload of ' + item.description + ' success'); + numSuccesses++; + } + } + catch ({ err, message }) { + if (needsNewNum) + newNums.pop(); + numFails++; + postMessage(['fail', `Failed upload of ${item.description}`]); + console.error(`Failed upload of \"${item.description}\", ${err}`); + if (items.length == 1) { //single item upload failed + switch (message) { + case "400": + //invalid item error + postMessage(['upload-error', message, "An error occured due to the item's format. Please review the item."]); + break; + case "401": + //not logged in error + postMessage(['upload-error', message, "Upload rejected as user is not logged in. Please login and try again."]); + break; + case "403": + //not authorized error + postMessage(['upload-error', message, "User is not authorized to upload items. Please contact Corporate Reliability."]); + break; + case "502": + //bad gateway error + postMessage(['upload-error', message, "Connection error. Please try agian later."]); + break; + case "503": + //service unavailable error + postMessage(['upload-error', message, "Something went wrong with the server. Please try agian later."]); + break; + default: + if (message.length == 3 && parseInt(message) >= 400 && parseInt(message) < 600) { + postMessage(['upload-error', message, `${message} error.`]); + } + else { + postMessage(['upload-error', "Unidentified", "An unidentified error occured"]); + } + } + continue; + } + } + // Does inventory upload of the item if any of the inventory fields are filled in + if (item.storeroomname != '' || item.siteID != '' || item.cataloguenum != '' || item.vendorname != '') { + try { + const result = yield maximo.uploadToInventory(item); + // Cases of result are listed in maximo.js + if (result == 0) { + throw new Error('Unable to upload'); + } + else if (result == 1) { + postMessage(['debug', `Inventory upload of ${item.description} succeeded`]); + console.log('Adding to ' + item.storeroomname + ' success'); + numStoreroomSuccesses++; + } + else if (result == 2) { + throw new Error(['Invalid Vendor', 'vendor']); + } + else if (result == 3) { + throw new Error(['Invalid Site', 'siteID']); + } + else { + throw new Error(['Invalid Storeroom', 'storeroom']); + } + } + catch (err) { + numFails++; + // highlight the cells that have invalid values to red + postMessage(['updateColors', rowIndex + 1, err[1]]); + // Creates toast for the error + postMessage(['runCallback', 'failure', `Failed Inventory upload of ${item.description}. ${err}`]); + // Adds error to log + postMessage(['fail', `Failed Inventory upload of ${item.description}. ${err}`]); + // updates item status to 'warning' + postMessage(['update', 'partial', rowIndex]); + console.error(`Failed inventory upload of \"${item.description}\", ${err}`); + } + } + // console.log(rowIndex); + } + postMessage(['result', newNums, numFails, numSuccesses, numStoreroomSuccesses]); + }); +} +/** + * Uploads images to Maximo at the item master level + * @param {File[]} images + */ +function uploadImages(images) { + return __awaiter(this, void 0, void 0, function* () { + try { + const maximo = new Maximo(); + for (let i = 0; i < images.length; i++) { + const img = images[i]; + // try to upload the image + const data = yield maximo.uploadImageToMaximo(img); + const result = data[0]; + // handle result of upload + postMessage(['runCallback', result, i]); + // log result + if (result == 'success') { + postMessage(['debug', `${img.name} upload success`]); + } + else if (result == 'fail' || result == 'warning') { + postMessage(['fail', `${img.name} upload fail; ${data[1]}`]); + } + } + postMessage(['result', 'done']); + } + catch (err) { + console.log(err); + postMessage(['result', 'total failure', err]); + } + }); +} diff --git a/package-lock.json b/package-lock.json index 5b583fb..e26fc71 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,6 +16,8 @@ "exceljs": "^4.3.0", "lodash": "^4.17.0", "luxon": "^3.3.0", + "source-map-loader": "^5.0.0", + "ts-loader": "^9.5.1", "typescript": "^5.3.3" }, "devDependencies": { @@ -23,6 +25,7 @@ "@electron-forge/maker-squirrel": "^6.2.1", "@electron-forge/maker-zip": "^6.2.1", "@electron-forge/publisher-github": "^6.2.1", + "@types/electron": "^1.6.10", "electron": "^27.0.4", "eslint": "^8.53.0", "eslint-config-google": "^0.14.0" @@ -688,6 +691,64 @@ "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==", "dev": true }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "peer": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", + "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", + "peer": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "peer": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.5.tgz", + "integrity": "sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==", + "peer": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "peer": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.22", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.22.tgz", + "integrity": "sha512-Wf963MzWtA2sjrNt+g18IAln9lKnlRp+K2eH4jjIoF1wYeq3aMREpG09xhlhdzS0EjwU7qmUJYangWa+151vZw==", + "peer": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, "node_modules/@malept/cross-spawn-promise": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@malept/cross-spawn-promise/-/cross-spawn-promise-2.0.0.tgz", @@ -969,12 +1030,54 @@ "@types/responselike": "^1.0.0" } }, + "node_modules/@types/electron": { + "version": "1.6.10", + "resolved": "https://registry.npmjs.org/@types/electron/-/electron-1.6.10.tgz", + "integrity": "sha512-MOCVyzIwkBEloreoCVrTV108vSf8fFIJPsGruLCoAoBZdxtnJUqKA4lNonf/2u1twSjAspPEfmEheC+TLm/cMw==", + "deprecated": "This is a stub types definition for electron (https://github.com/electron/electron). electron provides its own type definitions, so you don't need @types/electron installed!", + "dev": true, + "dependencies": { + "electron": "*" + } + }, + "node_modules/@types/eslint": { + "version": "8.56.2", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.2.tgz", + "integrity": "sha512-uQDwm1wFHmbBbCZCqAlq6Do9LYwByNZHWzXppSnay9SuwJ+VRbjkbLABer54kcPnMSlG6Fdiy2yaFXm/z9Z5gw==", + "peer": true, + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/eslint-scope": { + "version": "3.7.7", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", + "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", + "peer": true, + "dependencies": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", + "peer": true + }, "node_modules/@types/http-cache-semantics": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==", "dev": true }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "peer": true + }, "node_modules/@types/keyv": { "version": "3.1.4", "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", @@ -988,7 +1091,6 @@ "version": "18.18.9", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.18.9.tgz", "integrity": "sha512-0f5klcuImLnG4Qreu9hPj/rEfFq6YRc5n2mAjSsH+ec/mJL+3voBH0+8T7o8RpFjH7ovc+TRsL/c7OYIQsPTfQ==", - "dev": true, "dependencies": { "undici-types": "~5.26.4" } @@ -1018,6 +1120,152 @@ "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", "dev": true }, + "node_modules/@webassemblyjs/ast": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.6.tgz", + "integrity": "sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q==", + "peer": true, + "dependencies": { + "@webassemblyjs/helper-numbers": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6" + } + }, + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz", + "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==", + "peer": true + }, + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz", + "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==", + "peer": true + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz", + "integrity": "sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA==", + "peer": true + }, + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz", + "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==", + "peer": true, + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.11.6", + "@webassemblyjs/helper-api-error": "1.11.6", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz", + "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==", + "peer": true + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz", + "integrity": "sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g==", + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/wasm-gen": "1.11.6" + } + }, + "node_modules/@webassemblyjs/ieee754": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz", + "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==", + "peer": true, + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/@webassemblyjs/leb128": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz", + "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==", + "peer": true, + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/utf8": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz", + "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==", + "peer": true + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz", + "integrity": "sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw==", + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/helper-wasm-section": "1.11.6", + "@webassemblyjs/wasm-gen": "1.11.6", + "@webassemblyjs/wasm-opt": "1.11.6", + "@webassemblyjs/wasm-parser": "1.11.6", + "@webassemblyjs/wast-printer": "1.11.6" + } + }, + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz", + "integrity": "sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA==", + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" + } + }, + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz", + "integrity": "sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g==", + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/wasm-gen": "1.11.6", + "@webassemblyjs/wasm-parser": "1.11.6" + } + }, + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz", + "integrity": "sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ==", + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-api-error": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" + } + }, + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz", + "integrity": "sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A==", + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.6", + "@xtuc/long": "4.2.2" + } + }, "node_modules/@xmldom/xmldom": { "version": "0.8.10", "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz", @@ -1027,6 +1275,18 @@ "node": ">=10.0.0" } }, + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "peer": true + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "peer": true + }, "node_modules/abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", @@ -1037,7 +1297,6 @@ "version": "8.11.2", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz", "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==", - "dev": true, "bin": { "acorn": "bin/acorn" }, @@ -1045,6 +1304,15 @@ "node": ">=0.4.0" } }, + "node_modules/acorn-import-assertions": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz", + "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==", + "peer": true, + "peerDependencies": { + "acorn": "^8" + } + }, "node_modules/acorn-jsx": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", @@ -1095,7 +1363,6 @@ "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -1107,6 +1374,15 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "peer": true, + "peerDependencies": { + "ajv": "^6.9.1" + } + }, "node_modules/ansi-escapes": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", @@ -1135,7 +1411,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -1422,6 +1697,38 @@ "node": ">=8" } }, + "node_modules/browserslist": { + "version": "4.22.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.2.tgz", + "integrity": "sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "peer": true, + "dependencies": { + "caniuse-lite": "^1.0.30001565", + "electron-to-chromium": "^1.4.601", + "node-releases": "^2.0.14", + "update-browserslist-db": "^1.0.13" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, "node_modules/buffer": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", @@ -1468,8 +1775,7 @@ "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" }, "node_modules/buffer-indexof-polyfill": { "version": "1.0.2", @@ -1592,6 +1898,26 @@ "node": ">=6" } }, + "node_modules/caniuse-lite": { + "version": "1.0.30001580", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001580.tgz", + "integrity": "sha512-mtj5ur2FFPZcCEpXFy8ADXbDACuNFXg6mxVDqp7tqooX6l3zwm+d8EPoeOSIFRDvHs8qu7/SLFOGniULkcH2iA==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "peer": true + }, "node_modules/chainsaw": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz", @@ -1607,7 +1933,6 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -1654,6 +1979,15 @@ "node": ">=10" } }, + "node_modules/chrome-trace-event": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", + "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", + "peer": true, + "engines": { + "node": ">=6.0" + } + }, "node_modules/clean-stack": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", @@ -1742,7 +2076,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, "dependencies": { "color-name": "~1.1.4" }, @@ -1753,8 +2086,7 @@ "node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "node_modules/color-support": { "version": "1.1.3", @@ -2221,6 +2553,12 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, + "node_modules/electron-to-chromium": { + "version": "1.4.645", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.645.tgz", + "integrity": "sha512-EeS1oQDCmnYsRDRy2zTeC336a/4LZ6WKqvSaM1jLocEk5ZuyszkQtCpsqvuvaIXGOUjwtvF6LTcS8WueibXvSw==", + "peer": true + }, "node_modules/electron-winstaller": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/electron-winstaller/-/electron-winstaller-5.2.1.tgz", @@ -2298,6 +2636,18 @@ "once": "^1.4.0" } }, + "node_modules/enhanced-resolve": { + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz", + "integrity": "sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/env-paths": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", @@ -2322,6 +2672,12 @@ "is-arrayish": "^0.2.1" } }, + "node_modules/es-module-lexer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.4.1.tgz", + "integrity": "sha512-cXLGjP0c4T3flZJKQSuziYoq7MlT+rnvfZjfp7h+I7K9BNX54kP9nyWvdbwjQ4u1iWbOL4u96fgeZLToQlZC7w==", + "peer": true + }, "node_modules/es6-error": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", @@ -2333,7 +2689,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true, "engines": { "node": ">=6" } @@ -2490,7 +2845,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, "dependencies": { "estraverse": "^5.2.0" }, @@ -2502,7 +2856,6 @@ "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, "engines": { "node": ">=4.0" } @@ -2516,6 +2869,15 @@ "node": ">=0.10.0" } }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "peer": true, + "engines": { + "node": ">=0.8.x" + } + }, "node_modules/exceljs": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/exceljs/-/exceljs-4.4.0.tgz", @@ -2693,8 +3055,7 @@ "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "node_modules/fast-glob": { "version": "3.3.2", @@ -2715,8 +3076,7 @@ "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" }, "node_modules/fast-levenshtein": { "version": "2.0.6", @@ -3086,6 +3446,12 @@ "node": ">= 6" } }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "peer": true + }, "node_modules/global-agent": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-3.0.0.tgz", @@ -3242,7 +3608,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, "engines": { "node": ">=8" } @@ -3381,8 +3746,6 @@ "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "dev": true, - "optional": true, "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" }, @@ -3675,6 +4038,35 @@ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, + "node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "peer": true, + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, "node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -3693,11 +4085,16 @@ "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", "dev": true }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "peer": true + }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", @@ -3886,6 +4283,15 @@ "node": ">=4" } }, + "node_modules/loader-runner": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", + "peer": true, + "engines": { + "node": ">=6.11.5" + } + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -4168,6 +4574,12 @@ "node": ">=6" } }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "peer": true + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -4181,7 +4593,6 @@ "version": "4.0.5", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dev": true, "dependencies": { "braces": "^3.0.2", "picomatch": "^2.3.1" @@ -4194,7 +4605,6 @@ "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "dev": true, "engines": { "node": ">= 0.6" } @@ -4203,7 +4613,6 @@ "version": "2.1.35", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dev": true, "dependencies": { "mime-db": "1.52.0" }, @@ -4381,6 +4790,12 @@ "node": ">= 0.6" } }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "peer": true + }, "node_modules/nice-try": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", @@ -4452,6 +4867,12 @@ "node": "^12.13 || ^14.13 || >=16" } }, + "node_modules/node-releases": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", + "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", + "peer": true + }, "node_modules/nopt": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/nopt/-/nopt-6.0.0.tgz", @@ -4807,6 +5228,12 @@ "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", "dev": true }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "peer": true + }, "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", @@ -4985,7 +5412,6 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, "engines": { "node": ">=6" } @@ -5022,6 +5448,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "peer": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, "node_modules/rc": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", @@ -5408,9 +5843,7 @@ "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true, - "optional": true + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "node_modules/saxes": { "version": "5.0.1", @@ -5423,6 +5856,24 @@ "node": ">=10" } }, + "node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "peer": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, "node_modules/semver": { "version": "7.5.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", @@ -5484,6 +5935,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "peer": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, "node_modules/set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", @@ -5621,16 +6081,41 @@ "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, "engines": { "node": ">=0.10.0" } }, + "node_modules/source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-loader": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-5.0.0.tgz", + "integrity": "sha512-k2Dur7CbSLcAH73sBcIkV5xjPV4SzqO1NJ7+XaQl8if3VODDUj3FNchNGpqgJSKbvUfJuhVdv8K2Eu8/TNl2eA==", + "dependencies": { + "iconv-lite": "^0.6.3", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.72.1" + } + }, "node_modules/source-map-support": { "version": "0.5.21", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dev": true, "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" @@ -5790,7 +6275,6 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -5810,6 +6294,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "engines": { + "node": ">=6" + } + }, "node_modules/tar": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.0.tgz", @@ -5907,6 +6399,64 @@ "rimraf": "bin.js" } }, + "node_modules/terser": { + "version": "5.27.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.27.0.tgz", + "integrity": "sha512-bi1HRwVRskAjheeYl291n3JC4GgO/Ty4z1nVs5AAsmonJulGxpSektecnNedrwK9C7vpvVtcX3cw00VSLt7U2A==", + "peer": true, + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser-webpack-plugin": { + "version": "5.3.10", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz", + "integrity": "sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==", + "peer": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.20", + "jest-worker": "^27.4.5", + "schema-utils": "^3.1.1", + "serialize-javascript": "^6.0.1", + "terser": "^5.26.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "uglify-js": { + "optional": true + } + } + }, + "node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "peer": true + }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -5976,6 +6526,33 @@ "node": ">=0.8.0" } }, + "node_modules/ts-loader": { + "version": "9.5.1", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.5.1.tgz", + "integrity": "sha512-rNH3sK9kGZcH9dYzC7CewQm4NtxJTjSEVRJ2DyBZR7f8/wcta+iV44UPCXc5+nzDzivKtlzV6c9P4e+oFhDLYg==", + "dependencies": { + "chalk": "^4.1.0", + "enhanced-resolve": "^5.0.0", + "micromatch": "^4.0.0", + "semver": "^7.3.4", + "source-map": "^0.7.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "typescript": "*", + "webpack": "^5.0.0" + } + }, + "node_modules/ts-loader/node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "engines": { + "node": ">= 8" + } + }, "node_modules/tslib": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", @@ -6032,8 +6609,7 @@ "node_modules/undici-types": { "version": "5.26.5", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" }, "node_modules/unique-filename": { "version": "2.0.1", @@ -6123,11 +6699,40 @@ "safe-buffer": "~5.1.0" } }, + "node_modules/update-browserslist-db": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", + "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "peer": true, + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, "dependencies": { "punycode": "^2.1.0" } @@ -6168,6 +6773,19 @@ "spdx-expression-parse": "^3.0.0" } }, + "node_modules/watchpack": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", + "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", + "peer": true, + "dependencies": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/wcwidth": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", @@ -6183,6 +6801,84 @@ "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", "dev": true }, + "node_modules/webpack": { + "version": "5.90.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.90.0.tgz", + "integrity": "sha512-bdmyXRCXeeNIePv6R6tGPyy20aUobw4Zy8r0LUS2EWO+U+Ke/gYDgsCh7bl5rB6jPpr4r0SZa6dPxBxLooDT3w==", + "peer": true, + "dependencies": { + "@types/eslint-scope": "^3.7.3", + "@types/estree": "^1.0.5", + "@webassemblyjs/ast": "^1.11.5", + "@webassemblyjs/wasm-edit": "^1.11.5", + "@webassemblyjs/wasm-parser": "^1.11.5", + "acorn": "^8.7.1", + "acorn-import-assertions": "^1.9.0", + "browserslist": "^4.21.10", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.15.0", + "es-module-lexer": "^1.2.1", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.9", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.2.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.3.10", + "watchpack": "^2.4.0", + "webpack-sources": "^3.2.3" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-sources": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "peer": true, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack/node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "peer": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/webpack/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "peer": true, + "engines": { + "node": ">=4.0" + } + }, "node_modules/whatwg-url": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", diff --git a/package.json b/package.json index d5d4462..832c422 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "productName": "EAM Spare Parts", "version": "3.6.1", "description": "Tool to help automate various Reliability Processes", - "main": "main.js", + "main": "built/main.js", "scripts": { "start": "electron-forge start", "package": "electron-forge package", @@ -22,6 +22,7 @@ "@electron-forge/maker-squirrel": "^6.2.1", "@electron-forge/maker-zip": "^6.2.1", "@electron-forge/publisher-github": "^6.2.1", + "@types/electron": "^1.6.10", "electron": "^27.0.4", "eslint": "^8.53.0", "eslint-config-google": "^0.14.0" @@ -34,6 +35,8 @@ "exceljs": "^4.3.0", "lodash": "^4.17.0", "luxon": "^3.3.0", + "source-map-loader": "^5.0.0", + "ts-loader": "^9.5.1", "typescript": "^5.3.3" }, "config": { diff --git a/main.js b/src/main.js similarity index 97% rename from main.js rename to src/main.js index 4c8decf..3a65c70 100644 --- a/main.js +++ b/src/main.js @@ -2,8 +2,8 @@ const {app, BrowserWindow, ipcMain, screen, dialog, shell} = require('electron'); const path = require('path'); const fs = require('fs'); -const {appUpdater} = require('./src/misc/autoupdater.js'); -const CONSTANTS = require('./src/misc/constants.js'); +const {appUpdater} = require('./misc/autoupdater.js'); +const CONSTANTS = require('./misc/constants.js'); require('electron-reload')(__dirname); let mainWindow; let settingWindow; diff --git a/tsconfig.json b/tsconfig.json index 20ca694..824b677 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -27,7 +27,7 @@ /* Modules */ "module": "commonjs", /* Specify what module code is generated. */ // "rootDir": "./", /* Specify the root folder within your source files. */ - // "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */ + "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */ // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ @@ -106,5 +106,5 @@ // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ "skipLibCheck": true /* Skip type checking all .d.ts files. */ }, - "include": ["./src/**/*", "main.js"] + "include": ["./src/**/*"] } From edde0e17dc608b9ea26cfd62a8fc5c34f5bd6a1a Mon Sep 17 00:00:00 2001 From: Jonathan Ma Date: Thu, 25 Jan 2024 14:05:41 -0500 Subject: [PATCH 3/8] change main --- src/{main.js => main.ts} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/{main.js => main.ts} (100%) diff --git a/src/main.js b/src/main.ts similarity index 100% rename from src/main.js rename to src/main.ts From 8d5319860a3e8176616ce7e27452f11d678782c7 Mon Sep 17 00:00:00 2001 From: Jonathan Ma Date: Thu, 25 Jan 2024 14:37:46 -0500 Subject: [PATCH 4/8] convert main --- .eslintrc.json | 8 +- built/main.js | 124 ++-- package-lock.json | 1525 +++++++++++++++++++++++++++++++++++++++++++-- package.json | 15 +- src/main.ts | 46 +- tsconfig.json | 2 +- 6 files changed, 1581 insertions(+), 139 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index 18e7957..8719891 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,15 +1,13 @@ { "env": { "browser": true, - "commonjs": true, "es2021": true }, - "extends": "google", + "extends": "standard-with-typescript", "parserOptions": { - "ecmaVersion": "latest" + "ecmaVersion": "latest", + "sourceType": "module" }, "rules": { - "max-len": 0, - "linebreak-style":"off" } } diff --git a/built/main.js b/built/main.js index 20b5bbe..8951d16 100644 --- a/built/main.js +++ b/built/main.js @@ -8,48 +8,52 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); // Modules to control application life and create native browser window -const { app, BrowserWindow, ipcMain, screen, dialog, shell } = require('electron'); -const path = require('path'); -const fs = require('fs'); -const { appUpdater } = require('./misc/autoupdater.js'); -const CONSTANTS = require('./misc/constants.js'); +const electron_1 = require("electron"); +const path_1 = __importDefault(require("path")); +const fs_1 = __importDefault(require("fs")); +const autoupdater_js_1 = require("./misc/autoupdater.js"); +const constants_js_1 = __importDefault(require("./misc/constants.js")); require('electron-reload')(__dirname); let mainWindow; let settingWindow; if (require('electron-squirrel-startup')) { - app.quit(); + electron_1.app.quit(); } // Write eml file -ipcMain.on('write-file', (event, emailData) => { - const pathToFile = path.resolve(__dirname, 'downloadedFile.eml'); - fs.writeFile(pathToFile, emailData, (err) => { +electron_1.ipcMain.on('write-file', (event, emailData) => { + const pathToFile = path_1.default.resolve(__dirname, 'downloadedFile.eml'); + fs_1.default.writeFile(pathToFile, emailData, (err) => { if (err) { console.error(`Error writing file: ${err}`); } else { - shell.openPath(pathToFile) - .then(() => { - sleep(2000).then(() => { - // Delete the file after opening - fs.unlink(pathToFile, (err) => { - if (err) { - console.error(`Error deleting file: ${err}`); - } - else { - console.log('File deleted successfully'); - } - }); - }) - .catch((err) => { - console.error(`Error opening file: ${err}`); - }); - }); + electron_1.shell.openPath(pathToFile); + // .then(() => { + // sleep(2000).then(() => { + // // Delete the file after opening + // fs.unlink(pathToFile, (err: any) => { + // if (err) { + // console.error(`Error deleting file: ${err}`); + // } else { + // console.log('File deleted successfully'); + // } + // }); + // }, + // ) + // .catch((err: any) => { + // console.error(`Error opening file: ${err}`); + // }); + // }); } }); }); -ipcMain.on('openSettings', (event, arg) => { - settingWindow = new BrowserWindow({ +electron_1.ipcMain.on('openSettings', (event, arg) => { + settingWindow = new electron_1.BrowserWindow({ parent: mainWindow, width: 800, height: 600, @@ -60,21 +64,21 @@ ipcMain.on('openSettings', (event, arg) => { contextIsolation: false, }, }); - settingWindow.loadFile(path.join('src', 'renderer', 'setting.html')); + settingWindow.loadFile(path_1.default.join('src', 'renderer', 'setting.html')); settingWindow.show(); settingWindow.on('closed', () => { mainWindow.show(); settingWindow = null; }); - if (CONSTANTS.OPEN_DEV_TOOLS) { + if (constants_js_1.default.OPEN_DEV_TOOLS) { settingWindow.webContents.openDevTools(); } }); -ipcMain.on('getVersion', (event, arg) => { - event.returnValue = app.getVersion(); +electron_1.ipcMain.on('getVersion', (event, arg) => { + event.returnValue = electron_1.app.getVersion(); }); -ipcMain.handle('select-to-be-translated', (event, arg) => __awaiter(void 0, void 0, void 0, function* () { - const result = yield dialog.showOpenDialog(mainWindow, { +electron_1.ipcMain.handle('select-to-be-translated', (event, arg) => __awaiter(void 0, void 0, void 0, function* () { + const result = yield electron_1.dialog.showOpenDialog(mainWindow, { title: 'Select Spreadsheet', filters: [ { name: 'Spreadsheet', extensions: ['xls', 'xlsx', 'xlsm', 'xlsb'] }, @@ -85,8 +89,8 @@ ipcMain.handle('select-to-be-translated', (event, arg) => __awaiter(void 0, void }); return result; })); -ipcMain.handle('select-excel-file', (event, arg) => __awaiter(void 0, void 0, void 0, function* () { - const result = yield dialog.showOpenDialog(mainWindow, { +electron_1.ipcMain.handle('select-excel-file', (event, arg) => __awaiter(void 0, void 0, void 0, function* () { + const result = yield electron_1.dialog.showOpenDialog(mainWindow, { title: 'Select Excel Spreadsheet', filters: [ { name: 'Spreadsheet', extensions: ['xls', 'xlsx', 'xlsm', 'xlsb'] }, @@ -97,8 +101,8 @@ ipcMain.handle('select-excel-file', (event, arg) => __awaiter(void 0, void 0, vo }); return result; })); -ipcMain.handle('select-translations', (event, arg) => __awaiter(void 0, void 0, void 0, function* () { - const result = yield dialog.showOpenDialog(mainWindow, { +electron_1.ipcMain.handle('select-translations', (event, arg) => __awaiter(void 0, void 0, void 0, function* () { + const result = yield electron_1.dialog.showOpenDialog(mainWindow, { title: 'Select Translation Definition Spreadsheet', filters: [ { name: 'Spreadsheet', extensions: ['xls', 'xlsx', 'xlsm', 'xlsb'] }, @@ -109,28 +113,28 @@ ipcMain.handle('select-translations', (event, arg) => __awaiter(void 0, void 0, }); return result; })); -ipcMain.on('getPath', (event, arg) => { - event.returnValue = app.getPath('userData'); +electron_1.ipcMain.on('getPath', (event, arg) => { + event.returnValue = electron_1.app.getPath('userData'); }); -ipcMain.on('loading', (event, arg) => { - mainWindow.loadFile(path.join('src', 'renderer', 'item_main.html')); +electron_1.ipcMain.on('loading', (event, arg) => { + mainWindow.loadFile(path_1.default.join('src', 'renderer', 'item_main.html')); }); -ipcMain.on('start_item_module', (event, arg) => { - mainWindow.loadFile(path.join('src', 'renderer', 'item_loading.html')); +electron_1.ipcMain.on('start_item_module', (event, arg) => { + mainWindow.loadFile(path_1.default.join('src', 'renderer', 'item_loading.html')); }); -ipcMain.on('start_observation_template', (event, arg) => { - mainWindow.loadFile(path.join('src', 'renderer', 'observation_template.html')); +electron_1.ipcMain.on('start_observation_template', (event, arg) => { + mainWindow.loadFile(path_1.default.join('src', 'renderer', 'observation_template.html')); }); -ipcMain.on('start_item_translate', (event, arg) => { - mainWindow.loadFile(path.join('src', 'renderer', 'item_translation.html')); +electron_1.ipcMain.on('start_item_translate', (event, arg) => { + mainWindow.loadFile(path_1.default.join('src', 'renderer', 'item_translation.html')); }); -ipcMain.on('start_asset_translate', (event, arg) => { - mainWindow.loadFile(path.join('src', 'renderer', 'asset_translation.html')); +electron_1.ipcMain.on('start_asset_translate', (event, arg) => { + mainWindow.loadFile(path_1.default.join('src', 'renderer', 'asset_translation.html')); }); function createWindow() { // Create the browser window. - const { width, height } = screen.getPrimaryDisplay().workAreaSize; - mainWindow = new BrowserWindow({ + const { width, height } = electron_1.screen.getPrimaryDisplay().workAreaSize; + mainWindow = new electron_1.BrowserWindow({ width: width / 2, height: height, x: 0, @@ -143,34 +147,34 @@ function createWindow() { }, }); // and load the index.html of the app. - mainWindow.loadFile(path.join('src', 'renderer', 'start_page.html')); + mainWindow.loadFile(path_1.default.join('src', 'renderer', 'start_page.html')); // Open the DevTools. - if (CONSTANTS.OPEN_DEV_TOOLS) { + if (constants_js_1.default.OPEN_DEV_TOOLS) { mainWindow.webContents.openDevTools(); } const page = mainWindow.webContents; page.once('did-frame-finish-load', () => { console.log('checking for updates'); - appUpdater(); + (0, autoupdater_js_1.appUpdater)(); }); mainWindow.show(); } // This method will be called when Electron has finished // initialization and is ready to create browser windows. // Some APIs can only be used after this event occurs. -app.whenReady().then(() => { +electron_1.app.whenReady().then(() => { createWindow(); - app.on('activate', function () { + electron_1.app.on('activate', function () { // On macOS it's common to re-create a window in the app when the // dock icon is clicked and there are no other windows open. - if (BrowserWindow.getAllWindows().length === 0) + if (electron_1.BrowserWindow.getAllWindows().length === 0) createWindow(); }); }); // Quit when all windows are closed, except on macOS. There, it's common // for applications and their menu bar to stay active until the user quits // explicitly with Cmd + Q. -app.on('window-all-closed', function () { +electron_1.app.on('window-all-closed', function () { if (process.platform !== 'darwin') - app.quit(); + electron_1.app.quit(); }); diff --git a/package-lock.json b/package-lock.json index e26fc71..830217a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,8 +17,7 @@ "lodash": "^4.17.0", "luxon": "^3.3.0", "source-map-loader": "^5.0.0", - "ts-loader": "^9.5.1", - "typescript": "^5.3.3" + "ts-loader": "^9.5.1" }, "devDependencies": { "@electron-forge/cli": "^6.2.1", @@ -26,9 +25,16 @@ "@electron-forge/maker-zip": "^6.2.1", "@electron-forge/publisher-github": "^6.2.1", "@types/electron": "^1.6.10", + "@typescript-eslint/eslint-plugin": "^6.19.1", + "@typescript-eslint/parser": "^6.19.1", "electron": "^27.0.4", - "eslint": "^8.53.0", - "eslint-config-google": "^0.14.0" + "eslint": "^8.56.0", + "eslint-config-google": "^0.14.0", + "eslint-config-standard-with-typescript": "^43.0.1", + "eslint-plugin-import": "^2.29.1", + "eslint-plugin-n": "^16.6.2", + "eslint-plugin-promise": "^6.1.1", + "typescript": "^5.3.3" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -572,9 +578,9 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.3.tgz", - "integrity": "sha512-yZzuIG+jnVu6hNSzFEN07e8BxF3uAzYtQb6uDkaYZLo6oYZDCq454c5kB8zxnzfCYyP4MIuyBn10L0DqwujTmA==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", "dev": true, "dependencies": { "ajv": "^6.12.4", @@ -607,9 +613,9 @@ } }, "node_modules/@eslint/js": { - "version": "8.53.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.53.0.tgz", - "integrity": "sha512-Kn7K8dx/5U6+cT1yEhpX1w4PCSg0M+XyRILPgvwcEBjerFWCwQj5sbr3/VmxqV0JGHCBCzyd6LxypEuehypY1w==", + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.56.0.tgz", + "integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1075,8 +1081,13 @@ "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "peer": true + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==" + }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true }, "node_modules/@types/keyv": { "version": "3.1.4", @@ -1104,6 +1115,12 @@ "@types/node": "*" } }, + "node_modules/@types/semver": { + "version": "7.5.6", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.6.tgz", + "integrity": "sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==", + "dev": true + }, "node_modules/@types/yauzl": { "version": "2.10.3", "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", @@ -1114,6 +1131,220 @@ "@types/node": "*" } }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "6.19.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.19.1.tgz", + "integrity": "sha512-roQScUGFruWod9CEyoV5KlCYrubC/fvG8/1zXuT0WTcxX87GnMMmnksMwSg99lo1xiKrBzw2icsJPMAw1OtKxg==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "6.19.1", + "@typescript-eslint/type-utils": "6.19.1", + "@typescript-eslint/utils": "6.19.1", + "@typescript-eslint/visitor-keys": "6.19.1", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.4", + "natural-compare": "^1.4.0", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "6.19.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.19.1.tgz", + "integrity": "sha512-WEfX22ziAh6pRE9jnbkkLGp/4RhTpffr2ZK5bJ18M8mIfA8A+k97U9ZyaXCEJRlmMHh7R9MJZWXp/r73DzINVQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "6.19.1", + "@typescript-eslint/types": "6.19.1", + "@typescript-eslint/typescript-estree": "6.19.1", + "@typescript-eslint/visitor-keys": "6.19.1", + "debug": "^4.3.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "6.19.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.19.1.tgz", + "integrity": "sha512-4CdXYjKf6/6aKNMSly/BP4iCSOpvMmqtDzRtqFyyAae3z5kkqEjKndR5vDHL8rSuMIIWP8u4Mw4VxLyxZW6D5w==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.19.1", + "@typescript-eslint/visitor-keys": "6.19.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "6.19.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.19.1.tgz", + "integrity": "sha512-0vdyld3ecfxJuddDjACUvlAeYNrHP/pDeQk2pWBR2ESeEzQhg52DF53AbI9QCBkYE23lgkhLCZNkHn2hEXXYIg==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "6.19.1", + "@typescript-eslint/utils": "6.19.1", + "debug": "^4.3.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/types": { + "version": "6.19.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.19.1.tgz", + "integrity": "sha512-6+bk6FEtBhvfYvpHsDgAL3uo4BfvnTnoge5LrrCj2eJN8g3IJdLTD4B/jK3Q6vo4Ql/Hoip9I8aB6fF+6RfDqg==", + "dev": true, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "6.19.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.19.1.tgz", + "integrity": "sha512-aFdAxuhzBFRWhy+H20nYu19+Km+gFfwNO4TEqyszkMcgBDYQjmPJ61erHxuT2ESJXhlhrO7I5EFIlZ+qGR8oVA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.19.1", + "@typescript-eslint/visitor-keys": "6.19.1", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "9.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "6.19.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.19.1.tgz", + "integrity": "sha512-JvjfEZuP5WoMqwh9SPAPDSHSg9FBHHGhjPugSRxu5jMfjvBpq5/sGTD+9M9aQ5sh6iJ8AY/Kk/oUYVEMAPwi7w==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.19.1", + "@typescript-eslint/types": "6.19.1", + "@typescript-eslint/typescript-estree": "6.19.1", + "semver": "^7.5.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "6.19.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.19.1.tgz", + "integrity": "sha512-gkdtIO+xSO/SmI0W68DBg4u1KElmIUo3vXzgHyGPs6cxgB0sa3TlptRAAE0hUY1hM6FcDKEv7aIwiTGm76cXfQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.19.1", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, "node_modules/@ungap/structured-clone": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", @@ -1522,6 +1753,123 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", + "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "is-array-buffer": "^3.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-includes": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.7.tgz", + "integrity": "sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/array.prototype.findlastindex": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.3.tgz", + "integrity": "sha512-LzLoiOMAxvy+Gd3BAq3B7VeIgPdo+Q8hthvKtXybMvRV0jrXfJM/t8mw7nNlpEcVlVUnCnM2KSX4XU5HmpodOA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0", + "get-intrinsic": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flat": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz", + "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flatmap": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz", + "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.2.tgz", + "integrity": "sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.0", + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1", + "is-array-buffer": "^3.0.2", + "is-shared-array-buffer": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/astral-regex": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", @@ -1554,6 +1902,18 @@ "node": ">=0.8" } }, + "node_modules/available-typed-arrays": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -1793,6 +2153,27 @@ "node": ">=0.2.0" } }, + "node_modules/builtin-modules": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", + "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/builtins": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.0.1.tgz", + "integrity": "sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ==", + "dev": true, + "dependencies": { + "semver": "^7.0.0" + } + }, "node_modules/cacache": { "version": "16.1.3", "resolved": "https://registry.npmjs.org/cacache/-/cacache-16.1.3.tgz", @@ -1889,6 +2270,20 @@ "node": ">=8" } }, + "node_modules/call-bind": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", + "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.1", + "set-function-length": "^1.1.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -2344,7 +2739,6 @@ "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==", "dev": true, - "optional": true, "dependencies": { "get-intrinsic": "^1.2.1", "gopd": "^1.0.1", @@ -2359,7 +2753,6 @@ "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", "dev": true, - "optional": true, "dependencies": { "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", @@ -2409,6 +2802,27 @@ "minimatch": "^3.0.4" } }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dir-glob/node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -2672,19 +3086,112 @@ "is-arrayish": "^0.2.1" } }, - "node_modules/es-module-lexer": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.4.1.tgz", - "integrity": "sha512-cXLGjP0c4T3flZJKQSuziYoq7MlT+rnvfZjfp7h+I7K9BNX54kP9nyWvdbwjQ4u1iWbOL4u96fgeZLToQlZC7w==", - "peer": true - }, - "node_modules/es6-error": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", - "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", + "node_modules/es-abstract": { + "version": "1.22.3", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.3.tgz", + "integrity": "sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA==", "dev": true, - "optional": true - }, + "dependencies": { + "array-buffer-byte-length": "^1.0.0", + "arraybuffer.prototype.slice": "^1.0.2", + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.5", + "es-set-tostringtag": "^2.0.1", + "es-to-primitive": "^1.2.1", + "function.prototype.name": "^1.1.6", + "get-intrinsic": "^1.2.2", + "get-symbol-description": "^1.0.0", + "globalthis": "^1.0.3", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0", + "internal-slot": "^1.0.5", + "is-array-buffer": "^3.0.2", + "is-callable": "^1.2.7", + "is-negative-zero": "^2.0.2", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "is-string": "^1.0.7", + "is-typed-array": "^1.1.12", + "is-weakref": "^1.0.2", + "object-inspect": "^1.13.1", + "object-keys": "^1.1.1", + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.5.1", + "safe-array-concat": "^1.0.1", + "safe-regex-test": "^1.0.0", + "string.prototype.trim": "^1.2.8", + "string.prototype.trimend": "^1.0.7", + "string.prototype.trimstart": "^1.0.7", + "typed-array-buffer": "^1.0.0", + "typed-array-byte-length": "^1.0.0", + "typed-array-byte-offset": "^1.0.0", + "typed-array-length": "^1.0.4", + "unbox-primitive": "^1.0.2", + "which-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-module-lexer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.4.1.tgz", + "integrity": "sha512-cXLGjP0c4T3flZJKQSuziYoq7MlT+rnvfZjfp7h+I7K9BNX54kP9nyWvdbwjQ4u1iWbOL4u96fgeZLToQlZC7w==", + "peer": true + }, + "node_modules/es-set-tostringtag": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.2.tgz", + "integrity": "sha512-BuDyupZt65P9D2D2vA/zqcI3G5xRsklm5N3xCwuiy+/vKy8i0ifdsQP1sLgO4tZDSCaQUSnmC48khknGMV3D2Q==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.2", + "has-tostringtag": "^1.0.0", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-shim-unscopables": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", + "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", + "dev": true, + "dependencies": { + "hasown": "^2.0.0" + } + }, + "node_modules/es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", + "dev": true, + "optional": true + }, "node_modules/escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -2706,15 +3213,15 @@ } }, "node_modules/eslint": { - "version": "8.53.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.53.0.tgz", - "integrity": "sha512-N4VuiPjXDUa4xVeV/GC/RV3hQW9Nw+Y463lkWaKKXKYMvmRiRDAtfpuPFLN+E1/6ZhyR8J2ig+eVREnYgUsiag==", + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz", + "integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.3", - "@eslint/js": "8.53.0", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.56.0", "@humanwhocodes/config-array": "^0.11.13", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", @@ -2760,6 +3267,18 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/eslint-compat-utils": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/eslint-compat-utils/-/eslint-compat-utils-0.1.2.tgz", + "integrity": "sha512-Jia4JDldWnFNIru1Ehx1H5s9/yxiRHY/TimCuUc0jNexew3cF1gI6CYZil1ociakfWO3rRqFjl1mskBblB3RYg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "eslint": ">=6.0.0" + } + }, "node_modules/eslint-config-google": { "version": "0.14.0", "resolved": "https://registry.npmjs.org/eslint-config-google/-/eslint-config-google-0.14.0.tgz", @@ -2772,6 +3291,220 @@ "eslint": ">=5.16.0" } }, + "node_modules/eslint-config-standard": { + "version": "17.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-17.1.0.tgz", + "integrity": "sha512-IwHwmaBNtDK4zDHQukFDW5u/aTb8+meQWZvNFWkiGmbWjD6bqyuSSBxxXKkCftCUzc1zwCH2m/baCNDLGmuO5Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "eslint": "^8.0.1", + "eslint-plugin-import": "^2.25.2", + "eslint-plugin-n": "^15.0.0 || ^16.0.0 ", + "eslint-plugin-promise": "^6.0.0" + } + }, + "node_modules/eslint-config-standard-with-typescript": { + "version": "43.0.1", + "resolved": "https://registry.npmjs.org/eslint-config-standard-with-typescript/-/eslint-config-standard-with-typescript-43.0.1.tgz", + "integrity": "sha512-WfZ986+qzIzX6dcr4yGUyVb/l9N3Z8wPXCc5z/70fljs3UbWhhV+WxrfgsqMToRzuuyX9MqZ974pq2UPhDTOcA==", + "dev": true, + "dependencies": { + "@typescript-eslint/parser": "^6.4.0", + "eslint-config-standard": "17.1.0" + }, + "peerDependencies": { + "@typescript-eslint/eslint-plugin": "^6.4.0", + "eslint": "^8.0.1", + "eslint-plugin-import": "^2.25.2", + "eslint-plugin-n": "^15.0.0 || ^16.0.0 ", + "eslint-plugin-promise": "^6.0.0", + "typescript": "*" + } + }, + "node_modules/eslint-import-resolver-node": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", + "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", + "dev": true, + "dependencies": { + "debug": "^3.2.7", + "is-core-module": "^2.13.0", + "resolve": "^1.22.4" + } + }, + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-module-utils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz", + "integrity": "sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==", + "dev": true, + "dependencies": { + "debug": "^3.2.7" + }, + "engines": { + "node": ">=4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-es-x": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-es-x/-/eslint-plugin-es-x-7.5.0.tgz", + "integrity": "sha512-ODswlDSO0HJDzXU0XvgZ3lF3lS3XAZEossh15Q2UHjwrJggWeBoKqqEsLTZLXl+dh5eOAozG0zRcYtuE35oTuQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.1.2", + "@eslint-community/regexpp": "^4.6.0", + "eslint-compat-utils": "^0.1.2" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ota-meshi" + }, + "peerDependencies": { + "eslint": ">=8" + } + }, + "node_modules/eslint-plugin-import": { + "version": "2.29.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz", + "integrity": "sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==", + "dev": true, + "dependencies": { + "array-includes": "^3.1.7", + "array.prototype.findlastindex": "^1.2.3", + "array.prototype.flat": "^1.3.2", + "array.prototype.flatmap": "^1.3.2", + "debug": "^3.2.7", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.9", + "eslint-module-utils": "^2.8.0", + "hasown": "^2.0.0", + "is-core-module": "^2.13.1", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.7", + "object.groupby": "^1.0.1", + "object.values": "^1.1.7", + "semver": "^6.3.1", + "tsconfig-paths": "^3.15.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" + } + }, + "node_modules/eslint-plugin-import/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-plugin-import/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/eslint-plugin-n": { + "version": "16.6.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-16.6.2.tgz", + "integrity": "sha512-6TyDmZ1HXoFQXnhCTUjVFULReoBPOAjpuiKELMkeP40yffI/1ZRO+d9ug/VC6fqISo2WkuIBk3cvuRPALaWlOQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "builtins": "^5.0.1", + "eslint-plugin-es-x": "^7.5.0", + "get-tsconfig": "^4.7.0", + "globals": "^13.24.0", + "ignore": "^5.2.4", + "is-builtin-module": "^3.2.1", + "is-core-module": "^2.12.1", + "minimatch": "^3.1.2", + "resolve": "^1.22.2", + "semver": "^7.5.3" + }, + "engines": { + "node": ">=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-plugin-promise": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-6.1.1.tgz", + "integrity": "sha512-tjqWDwVZQo7UIPMeDReOpUgHCmCiH+ePnVT+5zVapL0uuHnegBUs2smM13CzOs2Xb5+MHMRFTs9v24yjba4Oig==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + } + }, "node_modules/eslint-scope": { "version": "7.2.2", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", @@ -3205,6 +3938,15 @@ "node": ">= 12" } }, + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.3" + } + }, "node_modules/fs-constants": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", @@ -3299,6 +4041,33 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/function.prototype.name": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", + "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "functions-have-names": "^1.2.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/galactus": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/galactus/-/galactus-1.0.0.tgz", @@ -3355,7 +4124,6 @@ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==", "dev": true, - "optional": true, "dependencies": { "function-bind": "^1.1.2", "has-proto": "^1.0.1", @@ -3411,6 +4179,34 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-tsconfig": { + "version": "4.7.2", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.7.2.tgz", + "integrity": "sha512-wuMsz4leaj5hbGgg4IvDU0bqJagpftG5l5cXIAvo8uZrqn0NJqwtfupTN00VnkQJPcIRrxYrm1Ue24btpCha2A==", + "dev": true, + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, "node_modules/github-from-package": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", @@ -3513,9 +4309,9 @@ } }, "node_modules/globals": { - "version": "13.23.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.23.0.tgz", - "integrity": "sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==", + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, "dependencies": { "type-fest": "^0.20.2" @@ -3544,7 +4340,6 @@ "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", "dev": true, - "optional": true, "dependencies": { "define-properties": "^1.1.3" }, @@ -3555,12 +4350,31 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/gopd": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", "dev": true, - "optional": true, "dependencies": { "get-intrinsic": "^1.1.3" }, @@ -3604,6 +4418,15 @@ "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", "dev": true }, + "node_modules/has-bigints": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -3617,7 +4440,6 @@ "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz", "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==", "dev": true, - "optional": true, "dependencies": { "get-intrinsic": "^1.2.2" }, @@ -3630,7 +4452,6 @@ "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", "dev": true, - "optional": true, "engines": { "node": ">= 0.4" }, @@ -3643,7 +4464,21 @@ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", "dev": true, - "optional": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.2" + }, "engines": { "node": ">= 0.4" }, @@ -3845,6 +4680,20 @@ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" }, + "node_modules/internal-slot": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.6.tgz", + "integrity": "sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.2", + "hasown": "^2.0.0", + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/interpret": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz", @@ -3860,12 +4709,38 @@ "integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==", "dev": true }, + "node_modules/is-array-buffer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", + "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.0", + "is-typed-array": "^1.1.10" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", "dev": true }, + "node_modules/is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dev": true, + "dependencies": { + "has-bigints": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -3877,6 +4752,49 @@ "node": ">=8" } }, + "node_modules/is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-builtin-module": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz", + "integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==", + "dev": true, + "dependencies": { + "builtin-modules": "^3.3.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-core-module": { "version": "2.13.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", @@ -3889,6 +4807,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-docker": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", @@ -3947,6 +4880,18 @@ "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", "dev": true }, + "node_modules/is-negative-zero": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -3955,6 +4900,21 @@ "node": ">=0.12.0" } }, + "node_modules/is-number-object": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-path-inside": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", @@ -3973,13 +4933,86 @@ "node": ">=0.10.0" } }, - "node_modules/is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", + "node_modules/is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", + "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.12.tgz", + "integrity": "sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==", "dev": true, + "dependencies": { + "which-typed-array": "^1.1.11" + }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-unicode-supported": { @@ -3994,6 +5027,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-windows": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", @@ -4109,6 +5154,18 @@ "dev": true, "optional": true }, + "node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, "node_modules/jsonfile": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", @@ -4965,16 +6022,88 @@ "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, + "node_modules/object-inspect": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", + "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/object-keys": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", "dev": true, - "optional": true, "engines": { "node": ">= 0.4" } }, + "node_modules/object.assign": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", + "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.fromentries": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.7.tgz", + "integrity": "sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.groupby": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.1.tgz", + "integrity": "sha512-HqaQtqLnp/8Bn4GL16cj+CUYbnpe1bh0TtEaWvybszDG4tgxCJuRpV8VGuvNaI1fAnI4lUJzDG55MXcOH4JZcQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1" + } + }, + "node_modules/object.values": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.7.tgz", + "integrity": "sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -5640,6 +6769,23 @@ "node": ">= 10.13.0" } }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz", + "integrity": "sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "set-function-name": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -5707,6 +6853,15 @@ "npm": ">=2" } }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, "node_modules/responselike": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", @@ -5821,6 +6976,30 @@ "tslib": "^2.1.0" } }, + "node_modules/safe-array-concat": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.0.tgz", + "integrity": "sha512-ZdQ0Jeb9Ofti4hbt5lX3T2JcAamT9hfzYU1MNB+z/jaEbB6wfFfPIR/zEORmZqobkCCJhSjodobH6WHNmJ97dg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.5", + "get-intrinsic": "^1.2.2", + "has-symbols": "^1.0.3", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-array-concat/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -5840,6 +7019,23 @@ } ] }, + "node_modules/safe-regex-test": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.2.tgz", + "integrity": "sha512-83S9w6eFq12BBIJYvjMux6/dkirb8+4zJRA9cxNBVb7Wq5fJBW+Xze48WqR8pxua7bDuAaaAxtVVd4Idjp1dBQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.5", + "get-intrinsic": "^1.2.2", + "is-regex": "^1.1.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", @@ -5950,6 +7146,36 @@ "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", "dev": true }, + "node_modules/set-function-length": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.0.tgz", + "integrity": "sha512-4DBHDoyHlM1IRPGYcoxexgh67y4ueR53FKV1yyxwFMY7aCqcN/38M1+SwZ/qJQ8iLv7+ck385ot4CcisOAPT9w==", + "dev": true, + "dependencies": { + "define-data-property": "^1.1.1", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.2", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.1.tgz", + "integrity": "sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==", + "dev": true, + "dependencies": { + "define-data-property": "^1.0.1", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/setimmediate": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", @@ -5976,6 +7202,20 @@ "node": ">=8" } }, + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", @@ -6025,6 +7265,15 @@ "simple-concat": "^1.0.0" } }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/slice-ansi": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", @@ -6194,6 +7443,51 @@ "node": ">=8" } }, + "node_modules/string.prototype.trim": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz", + "integrity": "sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz", + "integrity": "sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz", + "integrity": "sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -6526,6 +7820,18 @@ "node": ">=0.8.0" } }, + "node_modules/ts-api-utils": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz", + "integrity": "sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==", + "dev": true, + "engines": { + "node": ">=16.13.0" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, "node_modules/ts-loader": { "version": "9.5.1", "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.5.1.tgz", @@ -6553,6 +7859,18 @@ "node": ">= 8" } }, + "node_modules/tsconfig-paths": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", + "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", + "dev": true, + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, "node_modules/tslib": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", @@ -6594,6 +7912,71 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/typed-array-buffer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz", + "integrity": "sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.1", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz", + "integrity": "sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "has-proto": "^1.0.1", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz", + "integrity": "sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "has-proto": "^1.0.1", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", + "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "is-typed-array": "^1.1.9" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/typescript": { "version": "5.3.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", @@ -6606,6 +7989,21 @@ "node": ">=14.17" } }, + "node_modules/unbox-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", + "which-boxed-primitive": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/undici-types": { "version": "5.26.5", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", @@ -6904,6 +8302,41 @@ "node": ">= 8" } }, + "node_modules/which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, + "dependencies": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.13.tgz", + "integrity": "sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.4", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/wide-align": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", diff --git a/package.json b/package.json index 832c422..8dd4719 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,7 @@ "description": "Tool to help automate various Reliability Processes", "main": "built/main.js", "scripts": { + "build": "tsc", "start": "electron-forge start", "package": "electron-forge package", "make": "electron-forge make", @@ -23,9 +24,16 @@ "@electron-forge/maker-zip": "^6.2.1", "@electron-forge/publisher-github": "^6.2.1", "@types/electron": "^1.6.10", + "@typescript-eslint/eslint-plugin": "^6.19.1", + "@typescript-eslint/parser": "^6.19.1", "electron": "^27.0.4", - "eslint": "^8.53.0", - "eslint-config-google": "^0.14.0" + "eslint": "^8.56.0", + "eslint-config-google": "^0.14.0", + "eslint-config-standard-with-typescript": "^43.0.1", + "eslint-plugin-import": "^2.29.1", + "eslint-plugin-n": "^16.6.2", + "eslint-plugin-promise": "^6.1.1", + "typescript": "^5.3.3" }, "dependencies": { "better-sqlite3": "^9.1.1", @@ -36,8 +44,7 @@ "lodash": "^4.17.0", "luxon": "^3.3.0", "source-map-loader": "^5.0.0", - "ts-loader": "^9.5.1", - "typescript": "^5.3.3" + "ts-loader": "^9.5.1" }, "config": { "forge": { diff --git a/src/main.ts b/src/main.ts index 3a65c70..94ff3a3 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,11 +1,11 @@ // Modules to control application life and create native browser window -const {app, BrowserWindow, ipcMain, screen, dialog, shell} = require('electron'); -const path = require('path'); -const fs = require('fs'); -const {appUpdater} = require('./misc/autoupdater.js'); -const CONSTANTS = require('./misc/constants.js'); +import {app, BrowserWindow, ipcMain, screen, dialog, shell} from 'electron'; +import path from 'path'; +import fs from 'fs'; +import { appUpdater } from './misc/autoupdater.js'; +import CONSTANTS from './misc/constants.js'; require('electron-reload')(__dirname); -let mainWindow; +let mainWindow: Electron.BrowserWindow; let settingWindow; if (require('electron-squirrel-startup')) { @@ -14,27 +14,27 @@ if (require('electron-squirrel-startup')) { // Write eml file ipcMain.on('write-file', (event, emailData) => { const pathToFile = path.resolve(__dirname, 'downloadedFile.eml'); - fs.writeFile(pathToFile, emailData, (err) => { + fs.writeFile(pathToFile, emailData, (err: any) => { if (err) { console.error(`Error writing file: ${err}`); } else { shell.openPath(pathToFile) - .then(() => { - sleep(2000).then(() => { - // Delete the file after opening - fs.unlink(pathToFile, (err) => { - if (err) { - console.error(`Error deleting file: ${err}`); - } else { - console.log('File deleted successfully'); - } - }); - }, - ) - .catch((err) => { - console.error(`Error opening file: ${err}`); - }); - }); + // .then(() => { + // sleep(2000).then(() => { + // // Delete the file after opening + // fs.unlink(pathToFile, (err: any) => { + // if (err) { + // console.error(`Error deleting file: ${err}`); + // } else { + // console.log('File deleted successfully'); + // } + // }); + // }, + // ) + // .catch((err: any) => { + // console.error(`Error opening file: ${err}`); + // }); + // }); } }); }); diff --git a/tsconfig.json b/tsconfig.json index 824b677..6d1ecc2 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -100,7 +100,7 @@ // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ - // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ + "allowUnreachableCode": false, /* Disable error reporting for unreachable code. */ /* Completeness */ // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ From d3178d37e4a5122cfeb2ac186ec300f0b0ffc0e1 Mon Sep 17 00:00:00 2001 From: Jonathan Ma Date: Thu, 25 Jan 2024 14:39:55 -0500 Subject: [PATCH 5/8] autoupdater ts --- src/misc/{autoupdater.js => autoupdater.ts} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/misc/{autoupdater.js => autoupdater.ts} (100%) diff --git a/src/misc/autoupdater.js b/src/misc/autoupdater.ts similarity index 100% rename from src/misc/autoupdater.js rename to src/misc/autoupdater.ts From 534da9330dfc8c2e4513856598b69203de291ba7 Mon Sep 17 00:00:00 2001 From: Jonathan Ma Date: Thu, 25 Jan 2024 14:53:54 -0500 Subject: [PATCH 6/8] convert autoupdater --- built/misc/autoupdater.js | 41 ++++++++++++---------- src/misc/autoupdater.ts | 71 ++++++++++++++++++++------------------- 2 files changed, 60 insertions(+), 52 deletions(-) diff --git a/built/misc/autoupdater.js b/built/misc/autoupdater.js index 6397163..5351f20 100644 --- a/built/misc/autoupdater.js +++ b/built/misc/autoupdater.js @@ -1,39 +1,46 @@ "use strict"; -const os = require('os'); -const { app, autoUpdater, dialog } = require('electron'); -const version = app.getVersion(); -const platform = os.platform() + '_' + os.arch(); // usually returns darwin_64 -const updaterFeedURL = `https://jonathanmajh-iko-mro-items.onrender.com/update/` + platform + '/' + version; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.appUpdater = void 0; +const os_1 = __importDefault(require("os")); +const electron_1 = require("electron"); +const version = electron_1.app.getVersion(); +const platform = os_1.default.platform() + '_' + os_1.default.arch(); // usually returns darwin_64 +const updaterFeedURL = 'https://jonathanmajh-iko-mro-items.onrender.com/update/' + platform + '/' + version; // replace updaterFeedURL with https://l3gxze.deta.dev +const urlOptions = { url: updaterFeedURL }; function appUpdater() { - autoUpdater.setFeedURL(updaterFeedURL); + electron_1.autoUpdater.setFeedURL(urlOptions); /* Log whats happening - TODO send autoUpdater events to renderer so that we could console log it in developer tools - You could alsoe use nslog or other logging to see what's happening */ - autoUpdater.on('error', err => console.log(err)); - autoUpdater.on('checking-for-update', () => console.log('checking-for-update')); - autoUpdater.on('update-available', () => { + TODO send autoUpdater events to renderer so that we could console log it in developer tools + You could alsoe use nslog or other logging to see what's happening */ + electron_1.autoUpdater.on('error', err => { console.log(err); }); + electron_1.autoUpdater.on('checking-for-update', () => { console.log('checking-for-update'); }); + electron_1.autoUpdater.on('update-available', () => { console.log('update-available'); }); - autoUpdater.on('update-not-available', () => console.log('update-not-available')); + electron_1.autoUpdater.on('update-not-available', () => { console.log('update-not-available'); }); // Ask the user if update is available - autoUpdater.on('update-downloaded', (event, releaseNotes, releaseName) => { + electron_1.autoUpdater.on('update-downloaded', (event, releaseNotes, releaseName) => { console.log('update-downloaded'); // Ask user to update the app - const selected = dialog.showMessageBoxSync({ + const selected = electron_1.dialog.showMessageBoxSync({ type: 'question', buttons: ['Update and Relaunch', 'Later'], defaultId: 0, message: 'Update Available!', - detail: `A new version of ${app.getName()} has been downloaded\nDo you want to update now?\nUpdate will be automatically installed on next start up.`, + detail: `A new version of ${electron_1.app.getName()} has been downloaded\nDo you want to update now?\nUpdate will be automatically installed on next start up.` }); if (selected === 0) { - autoUpdater.quitAndInstall(); + electron_1.autoUpdater.quitAndInstall(); } }); // init for updates - autoUpdater.checkForUpdates(); + electron_1.autoUpdater.checkForUpdates(); } +exports.appUpdater = appUpdater; exports = module.exports = { appUpdater }; diff --git a/src/misc/autoupdater.ts b/src/misc/autoupdater.ts index 23e47fc..aebe937 100644 --- a/src/misc/autoupdater.ts +++ b/src/misc/autoupdater.ts @@ -1,42 +1,43 @@ -const os = require('os'); -const { app, autoUpdater, dialog } = require('electron'); -const version = app.getVersion(); -const platform = os.platform() + '_' + os.arch(); // usually returns darwin_64 +import os from 'os' +import { app, autoUpdater, dialog, type FeedURLOptions } from 'electron' +const version = app.getVersion() +const platform = os.platform() + '_' + os.arch() // usually returns darwin_64 -const updaterFeedURL = `https://jonathanmajh-iko-mro-items.onrender.com/update/` + platform + '/' + version; +const updaterFeedURL = 'https://jonathanmajh-iko-mro-items.onrender.com/update/' + platform + '/' + version // replace updaterFeedURL with https://l3gxze.deta.dev +const urlOptions: FeedURLOptions = { url: updaterFeedURL } -function appUpdater() { - autoUpdater.setFeedURL(updaterFeedURL); - /* Log whats happening - TODO send autoUpdater events to renderer so that we could console log it in developer tools - You could alsoe use nslog or other logging to see what's happening */ - autoUpdater.on('error', err => console.log(err)); - autoUpdater.on('checking-for-update', () => console.log('checking-for-update')); - autoUpdater.on('update-available', () => { - console.log('update-available'); - }); - autoUpdater.on('update-not-available', () => console.log('update-not-available')); +export function appUpdater (): void { + autoUpdater.setFeedURL(urlOptions) + /* Log whats happening +TODO send autoUpdater events to renderer so that we could console log it in developer tools +You could alsoe use nslog or other logging to see what's happening */ + autoUpdater.on('error', err => { console.log(err) }) + autoUpdater.on('checking-for-update', () => { console.log('checking-for-update') }) + autoUpdater.on('update-available', () => { + console.log('update-available') + }) + autoUpdater.on('update-not-available', () => { console.log('update-not-available') }) - // Ask the user if update is available - autoUpdater.on('update-downloaded', (event, releaseNotes, releaseName) => { - console.log('update-downloaded'); - // Ask user to update the app - const selected = dialog.showMessageBoxSync({ - type: 'question', - buttons: ['Update and Relaunch', 'Later'], - defaultId: 0, - message: 'Update Available!', - detail: `A new version of ${app.getName()} has been downloaded\nDo you want to update now?\nUpdate will be automatically installed on next start up.`, - }); - if (selected === 0) { - autoUpdater.quitAndInstall(); - } - }); - // init for updates - autoUpdater.checkForUpdates(); + // Ask the user if update is available + autoUpdater.on('update-downloaded', (event, releaseNotes, releaseName) => { + console.log('update-downloaded') + // Ask user to update the app + const selected = dialog.showMessageBoxSync({ + type: 'question', + buttons: ['Update and Relaunch', 'Later'], + defaultId: 0, + message: 'Update Available!', + detail: `A new version of ${app.getName()} has been downloaded\nDo you want to update now?\nUpdate will be automatically installed on next start up.` + }) + if (selected === 0) { + autoUpdater.quitAndInstall() + } + }) + // init for updates + autoUpdater.checkForUpdates() } exports = module.exports = { - appUpdater -}; \ No newline at end of file + appUpdater +} From 052e02fe51b7a684ae4eb7ae2d366ed74f013f67 Mon Sep 17 00:00:00 2001 From: Jonathan Ma Date: Thu, 25 Jan 2024 14:54:53 -0500 Subject: [PATCH 7/8] utils --- src/misc/{utils.js => utils.ts} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/misc/{utils.js => utils.ts} (100%) diff --git a/src/misc/utils.js b/src/misc/utils.ts similarity index 100% rename from src/misc/utils.js rename to src/misc/utils.ts From 8e49230b8c23733fed15320c75ac6238cd0c667e Mon Sep 17 00:00:00 2001 From: Jonathan Ma Date: Tue, 20 Feb 2024 11:10:21 -0500 Subject: [PATCH 8/8] misc --- src/misc/{utils.ts => utils.js} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/misc/{utils.ts => utils.js} (100%) diff --git a/src/misc/utils.ts b/src/misc/utils.js similarity index 100% rename from src/misc/utils.ts rename to src/misc/utils.js