diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 00000000..416c47b1 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,4 @@ +ereuse_devicehub/static/vendor +ereuse_devicehub/static/js/print.pdf.js +ereuse_devicehub/static/js/qrcode.js +*.min.js \ No newline at end of file diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 00000000..0d9213bb --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,36 @@ +{ + "env": { + "browser": true, + "es2021": true, + "jquery": true + }, + "extends": [ + "airbnb", + "prettier" + ], + "plugins": [ + "prettier" + ], + "parserOptions": { + "ecmaVersion": "latest" + }, + "rules": { + "quotes": ["error","double"], + "no-use-before-define": "off", + "no-unused-vars": "warn", + "no-undef": "warn", + "camelcase": "off", + "no-console": "off", + "no-plusplus": "off", + "no-param-reassign": "off", + "no-new": "warn", + "strict": "off", + "class-methods-use-this": "off", + "eqeqeq": "warn", + "radix": "warn" + }, + "globals": { + "API_URLS": true, + "Api": true + } +} \ No newline at end of file diff --git a/.github/workflows/eslint.yml b/.github/workflows/eslint.yml new file mode 100644 index 00000000..7e42feaf --- /dev/null +++ b/.github/workflows/eslint.yml @@ -0,0 +1,55 @@ +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. +# ESLint is a tool for identifying and reporting on patterns +# found in ECMAScript/JavaScript code. +# More details at https://github.com/eslint/eslint +# and https://eslint.org + +name: ESLint + +on: + push: + branches: [master, testing] + pull_request_target: + branches: [master, testing] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-node@v1 + with: + node-version: '16' + - name: Install dependencies + run: npm install + - name: Run linters + uses: wearerequired/lint-action@v1 + with: + eslint: true + prettier: false + commit_message: "Fix code style issues with ${linter}" + auto_fix: true + commit: true + github_token: "${{ secrets.GITHUB_TOKEN }}" + git_name: "Lint Action" + - name: Save Code Linting Report JSON + # npm script for ESLint + # eslint --output-file eslint_report.json --format json src + # See https://eslint.org/docs/user-guide/command-line-interface#options + run: npm run lint:report + # Continue to the next step even if this fails + continue-on-error: true + - name: Annotate Code Linting Results + uses: ataylorme/eslint-annotate-action@1.2.0 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + report-json: "eslint_report.json" + only-pr-files: true + - name: Upload ESLint report + uses: actions/upload-artifact@v2 + with: + name: eslint_report.json + path: eslint_report.json diff --git a/.gitignore b/.gitignore index 6a130a6e..0d6f75c3 100644 --- a/.gitignore +++ b/.gitignore @@ -119,3 +119,11 @@ ENV/ # Temporal dir tmp/ + +# NPM modules +node_modules/ +yarn.lock + +# ESLint Report +eslint_report.json + diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 00000000..f92e93ef --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,3 @@ +{ + "printWidth": 250 +} \ No newline at end of file diff --git a/ereuse_devicehub/static/css/devicehub.css b/ereuse_devicehub/static/css/devicehub.css new file mode 100644 index 00000000..e6ae1893 --- /dev/null +++ b/ereuse_devicehub/static/css/devicehub.css @@ -0,0 +1,25 @@ +/** +* eReuse CSS +*/ + +/*-------------------------------------------------------------- +# LotsSelector +--------------------------------------------------------------*/ + +#dropDownLotsSelector { + max-height: 500px; +} + +#dropDownLotsSelector>ul#LotsSelector { + list-style-type: none; + margin: 0; + padding: 0; + min-width: max-content; + max-height: 380px; + overflow-y: auto; +} + +#dropDownLotsSelector #ApplyDeviceLots { + padding-top: 0px; + padding-bottom: 5px; +} diff --git a/ereuse_devicehub/static/css/style.css b/ereuse_devicehub/static/css/style.css index 8e263853..1fd44f19 100644 --- a/ereuse_devicehub/static/css/style.css +++ b/ereuse_devicehub/static/css/style.css @@ -1,10 +1,10 @@ -/** -* Template Name: NiceAdmin - v2.2.0 -* Template URL: https://bootstrapmade.com/nice-admin-bootstrap-admin-html-template/ -* Author: BootstrapMade.com -* License: https://bootstrapmade.com/license/ -*/ - +/** +* Template Name: NiceAdmin - v2.2.0 +* Template URL: https://bootstrapmade.com/nice-admin-bootstrap-admin-html-template/ +* Author: BootstrapMade.com +* License: https://bootstrapmade.com/license/ +*/ + /*-------------------------------------------------------------- # General --------------------------------------------------------------*/ @@ -1081,4 +1081,4 @@ h1, h2, h3, h4, h5, h6 { text-align: center; font-size: 13px; color: #012970; -} \ No newline at end of file +} diff --git a/ereuse_devicehub/static/js/api.js b/ereuse_devicehub/static/js/api.js index ee98a08f..190e5d42 100644 --- a/ereuse_devicehub/static/js/api.js +++ b/ereuse_devicehub/static/js/api.js @@ -4,7 +4,7 @@ const Api = { * @returns get lots */ async get_lots() { - var request = await this.doRequest(API_URLS.lots, "GET", null); + const request = await this.doRequest(API_URLS.lots, "GET", null); if (request != undefined) return request.items; throw request; }, @@ -15,7 +15,7 @@ const Api = { * @returns full detailed device list */ async get_devices(ids) { - var request = await this.doRequest(API_URLS.devices + '?filter={"id": [' + ids.toString() + ']}', "GET", null); + const request = await this.doRequest(`${API_URLS.devices }?filter={"id": [${ ids.toString() }]}`, "GET", null); if (request != undefined) return request.items; throw request; }, @@ -26,7 +26,7 @@ const Api = { * @returns full detailed device list */ async search_device(id) { - var request = await this.doRequest(API_URLS.devices + '?filter={"devicehub_id": ["' + id + '"]}', "GET", null) + const request = await this.doRequest(`${API_URLS.devices }?filter={"devicehub_id": ["${ id }"]}`, "GET", null) if (request != undefined) return request.items throw request }, @@ -37,8 +37,8 @@ const Api = { * @param {number[]} listDevices list devices id */ async devices_add(lotID, listDevices) { - var queryURL = API_URLS.devices_modify.replace("UUID", lotID) + "?" + listDevices.map(deviceID => "id=" + deviceID).join("&"); - return await Api.doRequest(queryURL, "POST", null); + const queryURL = `${API_URLS.devices_modify.replace("UUID", lotID) }?${ listDevices.map(deviceID => `id=${ deviceID}`).join("&")}`; + return Api.doRequest(queryURL, "POST", null); }, /** @@ -47,8 +47,8 @@ const Api = { * @param {number[]} listDevices list devices id */ async devices_remove(lotID, listDevices) { - var queryURL = API_URLS.devices_modify.replace("UUID", lotID) + "?" + listDevices.map(deviceID => "id=" + deviceID).join("&"); - return await Api.doRequest(queryURL, "DELETE", null); + const queryURL = `${API_URLS.devices_modify.replace("UUID", lotID) }?${ listDevices.map(deviceID => `id=${ deviceID}`).join("&")}`; + return Api.doRequest(queryURL, "DELETE", null); }, /** @@ -59,13 +59,13 @@ const Api = { * @returns */ async doRequest(url, type, body) { - var result; + let result; try { result = await $.ajax({ - url: url, - type: type, + url, + type, headers: { "Authorization": API_URLS.Auth_Token }, - body: body + body }); return result; } catch (error) { diff --git a/ereuse_devicehub/static/js/create_device.js b/ereuse_devicehub/static/js/create_device.js index 1c9e0655..a1b609b0 100644 --- a/ereuse_devicehub/static/js/create_device.js +++ b/ereuse_devicehub/static/js/create_device.js @@ -1,15 +1,15 @@ -$(document).ready(function() { +$(document).ready(() => { $("#type").on("change", deviceInputs); deviceInputs(); }) function deviceInputs() { - if ($("#type").val() == 'Monitor') { + if ($("#type").val() == "Monitor") { $("#screen").show(); $("#resolution").show(); $("#imei").hide(); $("#meid").hide(); - } else if (['Smartphone', 'Cellphone', 'Tablet'].includes($("#type").val())) { + } else if (["Smartphone", "Cellphone", "Tablet"].includes($("#type").val())) { $("#screen").hide(); $("#resolution").hide(); $("#imei").show(); diff --git a/ereuse_devicehub/static/js/main.js b/ereuse_devicehub/static/js/main.js index 5eaec3ea..55a4951c 100644 --- a/ereuse_devicehub/static/js/main.js +++ b/ereuse_devicehub/static/js/main.js @@ -14,9 +14,9 @@ el = el.trim() if (all) { return [...document.querySelectorAll(el)] - } else { + } return document.querySelector(el) - } + } /** @@ -34,103 +34,101 @@ * Easy on scroll event listener */ const onscroll = (el, listener) => { - el.addEventListener('scroll', listener) + el.addEventListener("scroll", listener) } /** * Sidebar toggle */ - if (select('.toggle-sidebar-btn')) { - on('click', '.toggle-sidebar-btn', function (e) { - select('body').classList.toggle('toggle-sidebar') + if (select(".toggle-sidebar-btn")) { + on("click", ".toggle-sidebar-btn", (e) => { + select("body").classList.toggle("toggle-sidebar") }) } /** * Search bar toggle */ - if (select('.search-bar-toggle')) { - on('click', '.search-bar-toggle', function (e) { - select('.search-bar').classList.toggle('search-bar-show') + if (select(".search-bar-toggle")) { + on("click", ".search-bar-toggle", (e) => { + select(".search-bar").classList.toggle("search-bar-show") }) } /** * Navbar links active state on scroll */ - let navbarlinks = select('#navbar .scrollto', true) + const navbarlinks = select("#navbar .scrollto", true) const navbarlinksActive = () => { - let position = window.scrollY + 200 + const position = window.scrollY + 200 navbarlinks.forEach(navbarlink => { if (!navbarlink.hash) return - let section = select(navbarlink.hash) + const section = select(navbarlink.hash) if (!section) return if (position >= section.offsetTop && position <= (section.offsetTop + section.offsetHeight)) { - navbarlink.classList.add('active') + navbarlink.classList.add("active") } else { - navbarlink.classList.remove('active') + navbarlink.classList.remove("active") } }) } - window.addEventListener('load', navbarlinksActive) + window.addEventListener("load", navbarlinksActive) onscroll(document, navbarlinksActive) /** * Toggle .header-scrolled class to #header when page is scrolled */ - let selectHeader = select('#header') + const selectHeader = select("#header") if (selectHeader) { const headerScrolled = () => { if (window.scrollY > 100) { - selectHeader.classList.add('header-scrolled') + selectHeader.classList.add("header-scrolled") } else { - selectHeader.classList.remove('header-scrolled') + selectHeader.classList.remove("header-scrolled") } } - window.addEventListener('load', headerScrolled) + window.addEventListener("load", headerScrolled) onscroll(document, headerScrolled) } /** * Back to top button */ - let backtotop = select('.back-to-top') + const backtotop = select(".back-to-top") if (backtotop) { const toggleBacktotop = () => { if (window.scrollY > 100) { - backtotop.classList.add('active') + backtotop.classList.add("active") } else { - backtotop.classList.remove('active') + backtotop.classList.remove("active") } } - window.addEventListener('load', toggleBacktotop) + window.addEventListener("load", toggleBacktotop) onscroll(document, toggleBacktotop) } /** * Initiate tooltips */ - var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]')) - var tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) { - return new bootstrap.Tooltip(tooltipTriggerEl) - }) + const tooltipTriggerList = [].slice.call(document.querySelectorAll("[data-bs-toggle=\"tooltip\"]")) + const tooltipList = tooltipTriggerList.map((tooltipTriggerEl) => new bootstrap.Tooltip(tooltipTriggerEl)) /** * Initiate quill editors */ - if (select('.quill-editor-default')) { - new Quill('.quill-editor-default', { - theme: 'snow' + if (select(".quill-editor-default")) { + new Quill(".quill-editor-default", { + theme: "snow" }); } - if (select('.quill-editor-bubble')) { - new Quill('.quill-editor-bubble', { - theme: 'bubble' + if (select(".quill-editor-bubble")) { + new Quill(".quill-editor-bubble", { + theme: "bubble" }); } - if (select('.quill-editor-full')) { + if (select(".quill-editor-full")) { new Quill(".quill-editor-full", { modules: { toolbar: [ @@ -181,24 +179,24 @@ /** * Initiate Bootstrap validation check */ - var needsValidation = document.querySelectorAll('.needs-validation') + const needsValidation = document.querySelectorAll(".needs-validation") Array.prototype.slice.call(needsValidation) - .forEach(function (form) { - form.addEventListener('submit', function (event) { + .forEach((form) => { + form.addEventListener("submit", (event) => { if (!form.checkValidity()) { event.preventDefault() event.stopPropagation() } - form.classList.add('was-validated') + form.classList.add("was-validated") }, false) }) /** * Initiate Datatables */ - const datatables = select('.datatable', true) + const datatables = select(".datatable", true) datatables.forEach(datatable => { new simpleDatatables.DataTable(datatable); }) @@ -206,11 +204,11 @@ /** * Autoresize echart charts */ - const mainContainer = select('#main'); + const mainContainer = select("#main"); if (mainContainer) { setTimeout(() => { - new ResizeObserver(function () { - select('.echart', true).forEach(getEchart => { + new ResizeObserver(() => { + select(".echart", true).forEach(getEchart => { echarts.getInstanceByDom(getEchart).resize(); }) }).observe(mainContainer); @@ -220,11 +218,11 @@ /** * Select all functionality */ - var btnSelectAll = document.getElementById("SelectAllBTN"); - var tableListCheckboxes = document.querySelectorAll(".deviceSelect"); + const btnSelectAll = document.getElementById("SelectAllBTN"); + const tableListCheckboxes = document.querySelectorAll(".deviceSelect"); function itemListCheckChanged(event) { - let isAllChecked = Array.from(tableListCheckboxes).map(itm => itm.checked); + const isAllChecked = Array.from(tableListCheckboxes).map(itm => itm.checked); if (isAllChecked.every(bool => bool == true)) { btnSelectAll.checked = true; btnSelectAll.indeterminate = false; @@ -241,8 +239,8 @@ }) btnSelectAll.addEventListener("click", event => { - let checkedState = event.target.checked; - tableListCheckboxes.forEach(ckeckbox => ckeckbox.checked = checkedState); + const checkedState = event.target.checked; + tableListCheckboxes.forEach(ckeckbox => {ckeckbox.checked = checkedState}); }) /** @@ -256,23 +254,23 @@ * Search form functionality */ window.addEventListener("DOMContentLoaded", () => { - var searchForm = document.getElementById("SearchForm") - var inputSearch = document.querySelector("#SearchForm > input") - var doSearch = true + const searchForm = document.getElementById("SearchForm") + const inputSearch = document.querySelector("#SearchForm > input") + const doSearch = true searchForm.addEventListener("submit", (event) => { event.preventDefault(); }) let timeoutHandler = setTimeout(() => { }, 1) - let dropdownList = document.getElementById("dropdown-search-list") - let defaultEmptySearch = document.getElementById("dropdown-search-list").innerHTML + const dropdownList = document.getElementById("dropdown-search-list") + const defaultEmptySearch = document.getElementById("dropdown-search-list").innerHTML inputSearch.addEventListener("input", (e) => { clearTimeout(timeoutHandler) - let searchText = e.target.value - if (searchText == '') { + const searchText = e.target.value + if (searchText == "") { document.getElementById("dropdown-search-list").innerHTML = defaultEmptySearch; return } @@ -315,7 +313,7 @@ const device = devices[i]; // See: ereuse_devicehub/resources/device/models.py - var verboseName = `${device.type} ${device.manufacturer} ${device.model}` + const verboseName = `${device.type} ${device.manufacturer} ${device.model}` const templateString = `