Merge branch 'testing' into feature/3096-testing_selenium

This commit is contained in:
Cayo Puigdefabregas 2022-05-24 16:53:12 +02:00
commit f6e115909f
8 changed files with 761 additions and 452 deletions

View File

@ -28,7 +28,7 @@
"class-methods-use-this": "off", "class-methods-use-this": "off",
"eqeqeq": "warn", "eqeqeq": "warn",
"radix": "warn", "radix": "warn",
"max-classes-per-file": ["error", 2] "max-classes-per-file": "warn"
}, },
"globals": { "globals": {
"API_URLS": true, "API_URLS": true,

View File

@ -8,6 +8,7 @@ ml).
## master ## master
## testing ## testing
- [added] #273 Allow search/filter lots on lots management component.
## [2.1.1] - 2022-05-11 ## [2.1.1] - 2022-05-11
Hot fix release. Hot fix release.

View File

@ -5,14 +5,11 @@ Revises: eca457d8b2a4
Create Date: 2021-03-15 17:40:34.410408 Create Date: 2021-03-15 17:40:34.410408
""" """
import sqlalchemy as sa
import citext import citext
import teal import sqlalchemy as sa
from alembic import op from alembic import context, op
from alembic import context
from sqlalchemy.dialects import postgresql from sqlalchemy.dialects import postgresql
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.
revision = '51439cf24be8' revision = '51439cf24be8'
down_revision = '21afd375a654' down_revision = '21afd375a654'
@ -36,59 +33,197 @@ def upgrade_data():
def upgrade(): def upgrade():
## Trade # Trade
currency = sa.Enum('AFN', 'ARS', 'AWG', 'AUD', 'AZN', 'BSD', 'BBD', 'BDT', 'BYR', 'BZD', 'BMD', currency = sa.Enum(
'BOB', 'BAM', 'BWP', 'BGN', 'BRL', 'BND', 'KHR', 'CAD', 'KYD', 'CLP', 'CNY', 'AFN',
'COP', 'CRC', 'HRK', 'CUP', 'CZK', 'DKK', 'DOP', 'XCD', 'EGP', 'SVC', 'EEK', 'ARS',
'EUR', 'FKP', 'FJD', 'GHC', 'GIP', 'GTQ', 'GGP', 'GYD', 'HNL', 'HKD', 'HUF', 'AWG',
'ISK', 'INR', 'IDR', 'IRR', 'IMP', 'ILS', 'JMD', 'JPY', 'JEP', 'KZT', 'KPW', 'AUD',
'KRW', 'KGS', 'LAK', 'LVL', 'LBP', 'LRD', 'LTL', 'MKD', 'MYR', 'MUR', 'MXN', 'AZN',
'MNT', 'MZN', 'NAD', 'NPR', 'ANG', 'NZD', 'NIO', 'NGN', 'NOK', 'OMR', 'PKR', 'BSD',
'PAB', 'PYG', 'PEN', 'PHP', 'PLN', 'QAR', 'RON', 'RUB', 'SHP', 'SAR', 'RSD', 'BBD',
'SCR', 'SGD', 'SBD', 'SOS', 'ZAR', 'LKR', 'SEK', 'CHF', 'SRD', 'SYP', 'TWD', 'BDT',
'THB', 'TTD', 'TRY', 'TRL', 'TVD', 'UAH', 'GBP', 'USD', 'UYU', 'UZS', 'VEF', name='currency', create_type=False, checkfirst=True, schema=f'{get_inv()}') 'BYR',
'BZD',
'BMD',
'BOB',
'BAM',
'BWP',
'BGN',
'BRL',
'BND',
'KHR',
'CAD',
'KYD',
'CLP',
'CNY',
'COP',
'CRC',
'HRK',
'CUP',
'CZK',
'DKK',
'DOP',
'XCD',
'EGP',
'SVC',
'EEK',
'EUR',
'FKP',
'FJD',
'GHC',
'GIP',
'GTQ',
'GGP',
'GYD',
'HNL',
'HKD',
'HUF',
'ISK',
'INR',
'IDR',
'IRR',
'IMP',
'ILS',
'JMD',
'JPY',
'JEP',
'KZT',
'KPW',
'KRW',
'KGS',
'LAK',
'LVL',
'LBP',
'LRD',
'LTL',
'MKD',
'MYR',
'MUR',
'MXN',
'MNT',
'MZN',
'NAD',
'NPR',
'ANG',
'NZD',
'NIO',
'NGN',
'NOK',
'OMR',
'PKR',
'PAB',
'PYG',
'PEN',
'PHP',
'PLN',
'QAR',
'RON',
'RUB',
'SHP',
'SAR',
'RSD',
'SCR',
'SGD',
'SBD',
'SOS',
'ZAR',
'LKR',
'SEK',
'CHF',
'SRD',
'SYP',
'TWD',
'THB',
'TTD',
'TRY',
'TRL',
'TVD',
'UAH',
'GBP',
'USD',
'UYU',
'UZS',
'VEF',
name='currency',
create_type=False,
checkfirst=True,
)
op.drop_table('trade', schema=f'{get_inv()}') op.drop_table('trade', schema=f'{get_inv()}')
op.create_table('trade', op.create_table(
sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), 'trade',
sa.Column('price', sa.Float(decimal_return_scale=4), nullable=True), sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False),
sa.Column('lot_id', postgresql.UUID(as_uuid=True), nullable=True), sa.Column('price', sa.Float(decimal_return_scale=4), nullable=True),
sa.Column('date', sa.TIMESTAMP(timezone=True), nullable=True), sa.Column('lot_id', postgresql.UUID(as_uuid=True), nullable=True),
sa.Column('user_from_id', postgresql.UUID(as_uuid=True), nullable=False), sa.Column('date', sa.TIMESTAMP(timezone=True), nullable=True),
sa.Column('user_to_id', postgresql.UUID(as_uuid=True), nullable=False), sa.Column('user_from_id', postgresql.UUID(as_uuid=True), nullable=False),
sa.Column('document_id', citext.CIText(), nullable=True), sa.Column('user_to_id', postgresql.UUID(as_uuid=True), nullable=False),
sa.Column('confirm', sa.Boolean(), nullable=True), sa.Column('document_id', citext.CIText(), nullable=True),
sa.Column('code', citext.CIText(), default='', nullable=True, sa.Column('confirm', sa.Boolean(), nullable=True),
comment = "This code is used for traceability"), sa.Column(
sa.ForeignKeyConstraint(['id'], [f'{get_inv()}.action.id'], ), 'code',
sa.ForeignKeyConstraint(['user_from_id'], ['common.user.id'], ), citext.CIText(),
sa.ForeignKeyConstraint(['user_to_id'], ['common.user.id'], ), default='',
sa.ForeignKeyConstraint(['lot_id'], [f'{get_inv()}.lot.id'], ), nullable=True,
sa.PrimaryKeyConstraint('id'), comment="This code is used for traceability",
schema=f'{get_inv()}' ),
) sa.ForeignKeyConstraint(
['id'],
[f'{get_inv()}.action.id'],
),
sa.ForeignKeyConstraint(
['user_from_id'],
['common.user.id'],
),
sa.ForeignKeyConstraint(
['user_to_id'],
['common.user.id'],
),
sa.ForeignKeyConstraint(
['lot_id'],
[f'{get_inv()}.lot.id'],
),
sa.PrimaryKeyConstraint('id'),
schema=f'{get_inv()}',
)
op.add_column("trade", sa.Column("currency", currency, nullable=False), schema=f'{get_inv()}') op.add_column(
"trade", sa.Column("currency", currency, nullable=False), schema=f'{get_inv()}'
)
op.create_table(
'confirm',
sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False),
sa.Column('user_id', postgresql.UUID(as_uuid=True), nullable=False),
sa.Column('action_id', postgresql.UUID(as_uuid=True), nullable=False),
sa.ForeignKeyConstraint(
['id'],
[f'{get_inv()}.action.id'],
),
sa.ForeignKeyConstraint(
['action_id'],
[f'{get_inv()}.action.id'],
),
sa.ForeignKeyConstraint(
['user_id'],
['common.user.id'],
),
sa.PrimaryKeyConstraint('id'),
schema=f'{get_inv()}',
)
op.create_table('confirm', # User
sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), op.add_column(
sa.Column('user_id', postgresql.UUID(as_uuid=True), nullable=False), 'user',
sa.Column('action_id', postgresql.UUID(as_uuid=True), nullable=False), sa.Column('active', sa.Boolean(), default=True, nullable=True),
schema='common',
sa.ForeignKeyConstraint(['id'], [f'{get_inv()}.action.id'], ), )
sa.ForeignKeyConstraint(['action_id'], [f'{get_inv()}.action.id'], ), op.add_column(
sa.ForeignKeyConstraint(['user_id'], ['common.user.id'], ), 'user',
sa.PrimaryKeyConstraint('id'), sa.Column('phantom', sa.Boolean(), default=False, nullable=True),
schema=f'{get_inv()}' schema='common',
) )
## User
op.add_column('user', sa.Column('active', sa.Boolean(), default=True, nullable=True),
schema='common')
op.add_column('user', sa.Column('phantom', sa.Boolean(), default=False, nullable=True),
schema='common')
upgrade_data() upgrade_data()
@ -99,28 +234,57 @@ def upgrade():
def downgrade(): def downgrade():
op.drop_table('confirm', schema=f'{get_inv()}') op.drop_table('confirm', schema=f'{get_inv()}')
op.drop_table('trade', schema=f'{get_inv()}') op.drop_table('trade', schema=f'{get_inv()}')
op.create_table('trade', op.create_table(
sa.Column('shipping_date', sa.TIMESTAMP(timezone=True), nullable=True, 'trade',
comment='When are the devices going to be ready \n \ sa.Column(
for shipping?\n '), 'shipping_date',
sa.Column('invoice_number', citext.CIText(), nullable=True, sa.TIMESTAMP(timezone=True),
comment='The id of the invoice so they can be linked.'), nullable=True,
sa.Column('price_id', postgresql.UUID(as_uuid=True), nullable=True, comment='When are the devices going to be ready \n \
comment='The price set for this trade. \n \ for shipping?\n ',
),
sa.Column(
'invoice_number',
citext.CIText(),
nullable=True,
comment='The id of the invoice so they can be linked.',
),
sa.Column(
'price_id',
postgresql.UUID(as_uuid=True),
nullable=True,
comment='The price set for this trade. \n \
If no price is set it is supposed that the trade was\n \ If no price is set it is supposed that the trade was\n \
not payed, usual in donations.\n '), not payed, usual in donations.\n ',
sa.Column('to_id', postgresql.UUID(as_uuid=True), nullable=False), ),
sa.Column('confirms_id', postgresql.UUID(as_uuid=True), nullable=True, sa.Column('to_id', postgresql.UUID(as_uuid=True), nullable=False),
comment='An organize action that this association confirms. \ sa.Column(
'confirms_id',
postgresql.UUID(as_uuid=True),
nullable=True,
comment='An organize action that this association confirms. \
\n \n For example, a ``Sell`` or ``Rent``\n \ \n \n For example, a ``Sell`` or ``Rent``\n \
can confirm a ``Reserve`` action.\n '), can confirm a ``Reserve`` action.\n ',
sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), ),
sa.ForeignKeyConstraint(['confirms_id'], [f'{get_inv()}.organize.id'], ), sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False),
sa.ForeignKeyConstraint(['id'], [f'{get_inv()}.action.id'], ), sa.ForeignKeyConstraint(
sa.ForeignKeyConstraint(['price_id'], [f'{get_inv()}.price.id'], ), ['confirms_id'],
sa.ForeignKeyConstraint(['to_id'], [f'{get_inv()}.agent.id'], ), [f'{get_inv()}.organize.id'],
sa.PrimaryKeyConstraint('id'), ),
schema=f'{get_inv()}' sa.ForeignKeyConstraint(
) ['id'],
[f'{get_inv()}.action.id'],
),
sa.ForeignKeyConstraint(
['price_id'],
[f'{get_inv()}.price.id'],
),
sa.ForeignKeyConstraint(
['to_id'],
[f'{get_inv()}.agent.id'],
),
sa.PrimaryKeyConstraint('id'),
schema=f'{get_inv()}',
)
op.drop_column('user', 'active', schema='common') op.drop_column('user', 'active', schema='common')
op.drop_column('user', 'phantom', schema='common') op.drop_column('user', 'phantom', schema='common')

View File

@ -5,12 +5,10 @@ Revises: 3eb50297c365
Create Date: 2020-12-29 20:19:46.981207 Create Date: 2020-12-29 20:19:46.981207
""" """
from alembic import context
from alembic import op
import sqlalchemy as sa import sqlalchemy as sa
from sqlalchemy.dialects import postgresql
import teal import teal
from alembic import context, op
from sqlalchemy.dialects import postgresql
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.
revision = 'b4bd1538bad5' revision = 'b4bd1538bad5'
@ -25,33 +23,71 @@ def get_inv():
raise ValueError("Inventory value is not specified") raise ValueError("Inventory value is not specified")
return INV return INV
def upgrade(): def upgrade():
# op.execute("COMMIT")
op.execute("ALTER TYPE snapshotsoftware ADD VALUE 'WorkbenchDesktop'")
SOFTWARE = sa.Enum(
'Workbench',
'WorkbenchAndroid',
'AndroidApp',
'Web',
'DesktopApp',
'WorkbenchDesktop',
name='snapshotsoftware',
create_type=False,
checkfirst=True,
)
# Live action # Live action
op.drop_table('live', schema=f'{get_inv()}') op.drop_table('live', schema=f'{get_inv()}')
op.create_table('live', op.create_table(
'live',
sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False),
sa.Column('serial_number', sa.Unicode(), nullable=True, sa.Column(
comment='The serial number of the Hard Disk in lower case.'), 'serial_number',
sa.Unicode(),
nullable=True,
comment='The serial number of the Hard Disk in lower case.',
),
sa.Column('usage_time_hdd', sa.Interval(), nullable=True), sa.Column('usage_time_hdd', sa.Interval(), nullable=True),
sa.Column('snapshot_uuid', postgresql.UUID(as_uuid=True), nullable=False), sa.Column('snapshot_uuid', postgresql.UUID(as_uuid=True), nullable=False),
sa.Column('software_version', teal.db.StrictVersionType(length=32), nullable=False), sa.Column(
sa.Column('licence_version', teal.db.StrictVersionType(length=32), nullable=False), 'software_version', teal.db.StrictVersionType(length=32), nullable=False
sa.Column('software', sa.Enum('Workbench', 'WorkbenchAndroid', 'AndroidApp', 'Web', ),
'DesktopApp', 'WorkbenchDesktop', name='snapshotsoftware'), nullable=False), sa.Column(
sa.ForeignKeyConstraint(['id'], [f'{get_inv()}.action.id'], ), 'licence_version', teal.db.StrictVersionType(length=32), nullable=False
),
sa.Column('software', SOFTWARE, nullable=False),
sa.ForeignKeyConstraint(
['id'],
[f'{get_inv()}.action.id'],
),
sa.PrimaryKeyConstraint('id'), sa.PrimaryKeyConstraint('id'),
schema=f'{get_inv()}' schema=f'{get_inv()}',
) )
def downgrade(): def downgrade():
op.drop_table('live', schema=f'{get_inv()}') op.drop_table('live', schema=f'{get_inv()}')
op.create_table('live', op.create_table(
'live',
sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False),
sa.Column('serial_number', sa.Unicode(), nullable=True, sa.Column(
comment='The serial number of the Hard Disk in lower case.'), 'serial_number',
sa.Unicode(),
nullable=True,
comment='The serial number of the Hard Disk in lower case.',
),
sa.Column('usage_time_hdd', sa.Interval(), nullable=True), sa.Column('usage_time_hdd', sa.Interval(), nullable=True),
sa.Column('snapshot_uuid', postgresql.UUID(as_uuid=True), nullable=False), sa.Column('snapshot_uuid', postgresql.UUID(as_uuid=True), nullable=False),
sa.ForeignKeyConstraint(['id'], [f'{get_inv()}.action.id'], ), sa.ForeignKeyConstraint(
['id'],
[f'{get_inv()}.action.id'],
),
sa.PrimaryKeyConstraint('id'), sa.PrimaryKeyConstraint('id'),
schema=f'{get_inv()}' schema=f'{get_inv()}',
)
op.execute(
"select e.enumlabel FROM pg_enum e JOIN pg_type t ON e.enumtypid = t.oid WHERE t.typname = 'snapshotsoftware'"
) )

View File

@ -1,353 +1,357 @@
/** /**
* Template Name: NiceAdmin - v2.2.0 * Template Name: NiceAdmin - v2.2.0
* Template URL: https://bootstrapmade.com/nice-admin-bootstrap-admin-html-template/ * Template URL: https://bootstrapmade.com/nice-admin-bootstrap-admin-html-template/
* Author: BootstrapMade.com * Author: BootstrapMade.com
* License: https://bootstrapmade.com/license/ * License: https://bootstrapmade.com/license/
*/ */
(function () { (function () {
"use strict"; "use strict";
/** /**
* Easy selector helper function * Easy selector helper function
*/ */
const select = (el, all = false) => { const select = (el, all = false) => {
el = el.trim() el = el.trim()
if (all) { if (all) {
return [...document.querySelectorAll(el)] return [...document.querySelectorAll(el)]
} }
return document.querySelector(el) return document.querySelector(el)
} }
/** /**
* Easy event listener function * Easy event listener function
*/ */
const on = (type, el, listener, all = false) => { const on = (type, el, listener, all = false) => {
if (all) { if (all) {
select(el, all).forEach(e => e.addEventListener(type, listener)) select(el, all).forEach(e => e.addEventListener(type, listener))
} else { } else {
select(el, all).addEventListener(type, listener) select(el, all).addEventListener(type, listener)
} }
} }
/** /**
* Easy on scroll event listener * Easy on scroll event listener
*/ */
const onscroll = (el, listener) => { const onscroll = (el, listener) => {
el.addEventListener("scroll", listener) el.addEventListener("scroll", listener)
} }
/** /**
* Sidebar toggle * Sidebar toggle
*/ */
if (select(".toggle-sidebar-btn")) { if (select(".toggle-sidebar-btn")) {
on("click", ".toggle-sidebar-btn", (e) => { on("click", ".toggle-sidebar-btn", (e) => {
select("body").classList.toggle("toggle-sidebar") select("body").classList.toggle("toggle-sidebar")
}) })
} }
/** /**
* Search bar toggle * Search bar toggle
*/ */
if (select(".search-bar-toggle")) { if (select(".search-bar-toggle")) {
on("click", ".search-bar-toggle", (e) => { on("click", ".search-bar-toggle", (e) => {
select(".search-bar").classList.toggle("search-bar-show") select(".search-bar").classList.toggle("search-bar-show")
}) })
} }
/** /**
* Navbar links active state on scroll * Navbar links active state on scroll
*/ */
const navbarlinks = select("#navbar .scrollto", true) const navbarlinks = select("#navbar .scrollto", true)
const navbarlinksActive = () => { const navbarlinksActive = () => {
const position = window.scrollY + 200 const position = window.scrollY + 200
navbarlinks.forEach(navbarlink => { navbarlinks.forEach(navbarlink => {
if (!navbarlink.hash) return if (!navbarlink.hash) return
const section = select(navbarlink.hash) const section = select(navbarlink.hash)
if (!section) return if (!section) return
if (position >= section.offsetTop && position <= (section.offsetTop + section.offsetHeight)) { if (position >= section.offsetTop && position <= (section.offsetTop + section.offsetHeight)) {
navbarlink.classList.add("active") navbarlink.classList.add("active")
} else { } else {
navbarlink.classList.remove("active") navbarlink.classList.remove("active")
} }
}) })
} }
window.addEventListener("load", navbarlinksActive) window.addEventListener("load", navbarlinksActive)
onscroll(document, navbarlinksActive) onscroll(document, navbarlinksActive)
/** /**
* Toggle .header-scrolled class to #header when page is scrolled * Toggle .header-scrolled class to #header when page is scrolled
*/ */
const selectHeader = select("#header") const selectHeader = select("#header")
if (selectHeader) { if (selectHeader) {
const headerScrolled = () => { const headerScrolled = () => {
if (window.scrollY > 100) { if (window.scrollY > 100) {
selectHeader.classList.add("header-scrolled") selectHeader.classList.add("header-scrolled")
} else { } else {
selectHeader.classList.remove("header-scrolled") selectHeader.classList.remove("header-scrolled")
} }
} }
window.addEventListener("load", headerScrolled) window.addEventListener("load", headerScrolled)
onscroll(document, headerScrolled) onscroll(document, headerScrolled)
} }
/** /**
* Back to top button * Back to top button
*/ */
const backtotop = select(".back-to-top") const backtotop = select(".back-to-top")
if (backtotop) { if (backtotop) {
const toggleBacktotop = () => { const toggleBacktotop = () => {
if (window.scrollY > 100) { if (window.scrollY > 100) {
backtotop.classList.add("active") backtotop.classList.add("active")
} else { } else {
backtotop.classList.remove("active") backtotop.classList.remove("active")
} }
} }
window.addEventListener("load", toggleBacktotop) window.addEventListener("load", toggleBacktotop)
onscroll(document, toggleBacktotop) onscroll(document, toggleBacktotop)
} }
/** /**
* Initiate tooltips * Initiate tooltips
*/ */
const tooltipTriggerList = [].slice.call(document.querySelectorAll("[data-bs-toggle=\"tooltip\"]")) const tooltipTriggerList = [].slice.call(document.querySelectorAll("[data-bs-toggle=\"tooltip\"]"))
const tooltipList = tooltipTriggerList.map((tooltipTriggerEl) => new bootstrap.Tooltip(tooltipTriggerEl)) const tooltipList = tooltipTriggerList.map((tooltipTriggerEl) => new bootstrap.Tooltip(tooltipTriggerEl))
/** /**
* Initiate quill editors * Initiate quill editors
*/ */
if (select(".quill-editor-default")) { if (select(".quill-editor-default")) {
new Quill(".quill-editor-default", { new Quill(".quill-editor-default", {
theme: "snow" theme: "snow"
}); });
} }
if (select(".quill-editor-bubble")) { if (select(".quill-editor-bubble")) {
new Quill(".quill-editor-bubble", { new Quill(".quill-editor-bubble", {
theme: "bubble" theme: "bubble"
}); });
} }
if (select(".quill-editor-full")) { if (select(".quill-editor-full")) {
new Quill(".quill-editor-full", { new Quill(".quill-editor-full", {
modules: { modules: {
toolbar: [ toolbar: [
[{ [{
font: [] font: []
}, { }, {
size: [] size: []
}], }],
["bold", "italic", "underline", "strike"], ["bold", "italic", "underline", "strike"],
[{ [{
color: [] color: []
}, },
{ {
background: [] background: []
} }
], ],
[{ [{
script: "super" script: "super"
}, },
{ {
script: "sub" script: "sub"
} }
], ],
[{ [{
list: "ordered" list: "ordered"
}, },
{ {
list: "bullet" list: "bullet"
}, },
{ {
indent: "-1" indent: "-1"
}, },
{ {
indent: "+1" indent: "+1"
} }
], ],
["direction", { ["direction", {
align: [] align: []
}], }],
["link", "image", "video"], ["link", "image", "video"],
["clean"] ["clean"]
] ]
}, },
theme: "snow" theme: "snow"
}); });
} }
/** /**
* Initiate Bootstrap validation check * Initiate Bootstrap validation check
*/ */
const needsValidation = document.querySelectorAll(".needs-validation") const needsValidation = document.querySelectorAll(".needs-validation")
Array.prototype.slice.call(needsValidation) Array.prototype.slice.call(needsValidation)
.forEach((form) => { .forEach((form) => {
form.addEventListener("submit", (event) => { form.addEventListener("submit", (event) => {
if (!form.checkValidity()) { if (!form.checkValidity()) {
event.preventDefault() event.preventDefault()
event.stopPropagation() event.stopPropagation()
} }
form.classList.add("was-validated") form.classList.add("was-validated")
}, false) }, false)
}) })
/** /**
* Initiate Datatables * Initiate Datatables
*/ */
const datatables = select(".datatable", true) const datatables = select(".datatable", true)
datatables.forEach(datatable => { datatables.forEach(datatable => {
new simpleDatatables.DataTable(datatable); new simpleDatatables.DataTable(datatable);
}) })
/** /**
* Autoresize echart charts * Autoresize echart charts
*/ */
const mainContainer = select("#main"); const mainContainer = select("#main");
if (mainContainer) { if (mainContainer) {
setTimeout(() => { setTimeout(() => {
new ResizeObserver(() => { new ResizeObserver(() => {
select(".echart", true).forEach(getEchart => { select(".echart", true).forEach(getEchart => {
echarts.getInstanceByDom(getEchart).resize(); echarts.getInstanceByDom(getEchart).resize();
}) })
}).observe(mainContainer); }).observe(mainContainer);
}, 200); }, 200);
} }
/** /**
* Avoid hide dropdown when user clicked inside * Avoid hide dropdown when user clicked inside
*/ */
document.getElementById("dropDownLotsSelector").addEventListener("click", event => { const dropdownLotSelector = document.getElementById("dropDownLotsSelector")
event.stopPropagation(); if (dropdownLotSelector != null) { // If exists selector it will set click event
}) dropdownLotSelector.addEventListener("click", event => {
event.stopPropagation();
/** })
* Search form functionality }
*/
window.addEventListener("DOMContentLoaded", () => {
const searchForm = document.getElementById("SearchForm") /**
const inputSearch = document.querySelector("#SearchForm > input") * Search form functionality
const doSearch = true */
window.addEventListener("DOMContentLoaded", () => {
searchForm.addEventListener("submit", (event) => { const searchForm = document.getElementById("SearchForm")
event.preventDefault(); const inputSearch = document.querySelector("#SearchForm > input")
}) const doSearch = true
let timeoutHandler = setTimeout(() => { }, 1) searchForm.addEventListener("submit", (event) => {
const dropdownList = document.getElementById("dropdown-search-list") event.preventDefault();
const defaultEmptySearch = document.getElementById("dropdown-search-list").innerHTML })
let timeoutHandler = setTimeout(() => { }, 1)
inputSearch.addEventListener("input", (e) => { const dropdownList = document.getElementById("dropdown-search-list")
clearTimeout(timeoutHandler) const defaultEmptySearch = document.getElementById("dropdown-search-list").innerHTML
const searchText = e.target.value
if (searchText == "") {
document.getElementById("dropdown-search-list").innerHTML = defaultEmptySearch; inputSearch.addEventListener("input", (e) => {
return clearTimeout(timeoutHandler)
} const searchText = e.target.value
if (searchText == "") {
let resultCount = 0; document.getElementById("dropdown-search-list").innerHTML = defaultEmptySearch;
function searchCompleted() { return
resultCount++; }
setTimeout(() => {
if (resultCount == 2 && document.getElementById("dropdown-search-list").children.length == 2) { let resultCount = 0;
document.getElementById("dropdown-search-list").innerHTML = ` function searchCompleted() {
<li id="deviceSearchLoader" class="dropdown-item"> resultCount++;
<i class="bi bi-x-lg"></i> setTimeout(() => {
<span style="margin-right: 10px">Nothing found</span> if (resultCount == 2 && document.getElementById("dropdown-search-list").children.length == 2) {
</li>` document.getElementById("dropdown-search-list").innerHTML = `
} <li id="deviceSearchLoader" class="dropdown-item">
}, 100) <i class="bi bi-x-lg"></i>
} <span style="margin-right: 10px">Nothing found</span>
</li>`
timeoutHandler = setTimeout(async () => { }
dropdownList.innerHTML = ` }, 100)
<li id="deviceSearchLoader" class="dropdown-item"> }
<i class="bi bi-laptop"></i>
<div class="spinner-border spinner-border-sm" role="status"> timeoutHandler = setTimeout(async () => {
<span class="visually-hidden">Loading...</span> dropdownList.innerHTML = `
</div> <li id="deviceSearchLoader" class="dropdown-item">
</li> <i class="bi bi-laptop"></i>
<li id="lotSearchLoader" class="dropdown-item"> <div class="spinner-border spinner-border-sm" role="status">
<i class="bi bi-folder2"></i> <span class="visually-hidden">Loading...</span>
<div class="spinner-border spinner-border-sm" role="status"> </div>
<span class="visually-hidden">Loading...</span> </li>
</div> <li id="lotSearchLoader" class="dropdown-item">
</li>`; <i class="bi bi-folder2"></i>
<div class="spinner-border spinner-border-sm" role="status">
<span class="visually-hidden">Loading...</span>
try { </div>
Api.search_device(searchText.toUpperCase()).then(devices => { </li>`;
dropdownList.querySelector("#deviceSearchLoader").style = "display: none"
for (let i = 0; i < devices.length; i++) { try {
const device = devices[i]; Api.search_device(searchText.toUpperCase()).then(devices => {
dropdownList.querySelector("#deviceSearchLoader").style = "display: none"
// See: ereuse_devicehub/resources/device/models.py
const verboseName = `${device.type} ${device.manufacturer} ${device.model}` for (let i = 0; i < devices.length; i++) {
const device = devices[i];
const templateString = `
<li> // See: ereuse_devicehub/resources/device/models.py
<a class="dropdown-item" href="${API_URLS.devices_detail.replace("ReplaceTEXT", device.devicehubID)}" style="display: flex; align-items: center;" href="#"> const verboseName = `${device.type} ${device.manufacturer} ${device.model}`
<i class="bi bi-laptop"></i>
<span style="margin-right: 10px">${verboseName}</span> const templateString = `
<span class="badge bg-secondary" style="margin-left: auto;">${device.devicehubID}</span> <li>
</a> <a class="dropdown-item" href="${API_URLS.devices_detail.replace("ReplaceTEXT", device.devicehubID)}" style="display: flex; align-items: center;" href="#">
</li>`; <i class="bi bi-laptop"></i>
dropdownList.innerHTML += templateString <span style="margin-right: 10px">${verboseName}</span>
if (i == 4) { // Limit to 4 resullts <span class="badge bg-secondary" style="margin-left: auto;">${device.devicehubID}</span>
break; </a>
} </li>`;
} dropdownList.innerHTML += templateString
if (i == 4) { // Limit to 4 resullts
searchCompleted(); break;
}) }
} catch (error) { }
dropdownList.innerHTML += `
<li id="deviceSearchLoader" class="dropdown-item"> searchCompleted();
<i class="bi bi-x"></i> })
<div class="spinner-border spinner-border-sm" role="status"> } catch (error) {
<span class="visually-hidden">Error searching devices</span> dropdownList.innerHTML += `
</div> <li id="deviceSearchLoader" class="dropdown-item">
</li>`; <i class="bi bi-x"></i>
console.log(error); <div class="spinner-border spinner-border-sm" role="status">
} <span class="visually-hidden">Error searching devices</span>
</div>
try { </li>`;
Api.get_lots().then(lots => { console.log(error);
dropdownList.querySelector("#lotSearchLoader").style = "display: none" }
for (let i = 0; i < lots.length; i++) {
const lot = lots[i]; try {
if (lot.name.toUpperCase().includes(searchText.toUpperCase())) { Api.get_lots().then(lots => {
const templateString = ` dropdownList.querySelector("#lotSearchLoader").style = "display: none"
<li> for (let i = 0; i < lots.length; i++) {
<a class="dropdown-item" href="${API_URLS.lots_detail.replace("ReplaceTEXT", lot.id)}" style="display: flex; align-items: center;" href="#"> const lot = lots[i];
<i class="bi bi-folder2"></i> if (lot.name.toUpperCase().includes(searchText.toUpperCase())) {
<span style="margin-right: 10px">${lot.name}</span> const templateString = `
</a> <li>
</li>`; <a class="dropdown-item" href="${API_URLS.lots_detail.replace("ReplaceTEXT", lot.id)}" style="display: flex; align-items: center;" href="#">
dropdownList.innerHTML += templateString <i class="bi bi-folder2"></i>
if (i == 4) { // Limit to 4 resullts <span style="margin-right: 10px">${lot.name}</span>
break; </a>
} </li>`;
} dropdownList.innerHTML += templateString
} if (i == 4) { // Limit to 4 resullts
searchCompleted(); break;
}) }
}
} catch (error) { }
dropdownList.innerHTML += ` searchCompleted();
<li id="deviceSearchLoader" class="dropdown-item"> })
<i class="bi bi-x"></i>
<div class="spinner-border spinner-border-sm" role="status"> } catch (error) {
<span class="visually-hidden">Error searching lots</span> dropdownList.innerHTML += `
</div> <li id="deviceSearchLoader" class="dropdown-item">
</li>`; <i class="bi bi-x"></i>
console.log(error); <div class="spinner-border spinner-border-sm" role="status">
} <span class="visually-hidden">Error searching lots</span>
}, 1000) </div>
}) </li>`;
}) console.log(error);
}
})(); }, 1000)
})
})
})();

View File

@ -1,5 +1,7 @@
"use strict"; "use strict";
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
function _classStaticPrivateFieldSpecGet(receiver, classConstructor, descriptor) { _classCheckPrivateStaticAccess(receiver, classConstructor); _classCheckPrivateStaticFieldDescriptor(descriptor, "get"); return _classApplyDescriptorGet(receiver, descriptor); } function _classStaticPrivateFieldSpecGet(receiver, classConstructor, descriptor) { _classCheckPrivateStaticAccess(receiver, classConstructor); _classCheckPrivateStaticFieldDescriptor(descriptor, "get"); return _classApplyDescriptorGet(receiver, descriptor); }
function _classCheckPrivateStaticFieldDescriptor(descriptor, action) { if (descriptor === undefined) { throw new TypeError("attempted to " + action + " private static field before its declaration"); } } function _classCheckPrivateStaticFieldDescriptor(descriptor, action) { if (descriptor === undefined) { throw new TypeError("attempted to " + action + " private static field before its declaration"); } }
@ -328,11 +330,59 @@ function export_file(type_file) {
$("#exportAlertModal").click(); $("#exportAlertModal").click();
} }
} }
class lotsSearcher {
static enable() {
if (this.lotsSearchElement) this.lotsSearchElement.disabled = false;
}
static disable() {
if (this.lotsSearchElement) this.lotsSearchElement.disabled = true;
}
/**
* do search when lot change in the search input
*/
static doSearch(inputSearch) {
const lots = this.getListLots();
for (let i = 0; i < lots.length; i++) {
if (lot.innerText.toLowerCase().includes(inputSearch.toLowerCase())) {
lot.parentElement.style.display = "";
} else {
lot.parentElement.style.display = "none";
}
}
}
}
_defineProperty(lotsSearcher, "lots", []);
_defineProperty(lotsSearcher, "lotsSearchElement", null);
_defineProperty(lotsSearcher, "getListLots", () => {
let lotsList = document.getElementById("LotsSelector");
if (lotsList) {
// Apply filter to get only labels
return Array.from(lotsList.children).filter(item => item.querySelector("label"));
}
return [];
});
document.addEventListener("DOMContentLoaded", () => {
lotsSearcher.lotsSearchElement = document.getElementById("lots-search");
lotsSearcher.lotsSearchElement.addEventListener("input", e => {
lotsSearcher.doSearch(e.target.value);
});
});
/** /**
* Reactive lots button * Reactive lots button
*/ */
async function processSelectedDevices() { async function processSelectedDevices() {
class Actions { class Actions {
constructor() { constructor() {
@ -584,6 +634,7 @@ async function processSelectedDevices() {
document.getElementById("ApplyDeviceLots").classList.add("disabled"); document.getElementById("ApplyDeviceLots").classList.add("disabled");
try { try {
lotsSearcher.disable();
listHTML.html("<li style=\"text-align: center\"><div class=\"spinner-border text-info\" style=\"margin: auto\" role=\"status\"></div></li>"); listHTML.html("<li style=\"text-align: center\"><div class=\"spinner-border text-info\" style=\"margin: auto\" role=\"status\"></div></li>");
const selectedDevices = await Api.get_devices(selectedDevicesID); const selectedDevices = await Api.get_devices(selectedDevicesID);
let lots = await Api.get_lots(); let lots = await Api.get_lots();
@ -614,6 +665,7 @@ async function processSelectedDevices() {
listHTML.html(""); listHTML.html("");
lotsList.forEach(lot => templateLot(lot, selectedDevices, listHTML, actions)); lotsList.forEach(lot => templateLot(lot, selectedDevices, listHTML, actions));
lotsSearcher.enable();
} catch (error) { } catch (error) {
console.log(error); console.log(error);
listHTML.html("<li style=\"color: red; text-align: center\">Error feching devices and lots<br>(see console for more details)</li>"); listHTML.html("<li style=\"color: red; text-align: center\">Error feching devices and lots<br>(see console for more details)</li>");

View File

@ -93,7 +93,7 @@ const selectorController = (action) => {
table.on("datatable.perpage", () => itemListCheckChanged()); table.on("datatable.perpage", () => itemListCheckChanged());
table.on("datatable.update", () => itemListCheckChanged()); table.on("datatable.update", () => itemListCheckChanged());
} }
if (action == "softInit") { if (action == "softInit") {
softInit(); softInit();
itemListCheckChanged(); itemListCheckChanged();
@ -103,8 +103,8 @@ const selectorController = (action) => {
function itemListCheckChanged() { function itemListCheckChanged() {
alertInfoDevices.innerHTML = `Selected devices: ${TableController.getSelectedDevices().length} alertInfoDevices.innerHTML = `Selected devices: ${TableController.getSelectedDevices().length}
${TableController.getAllDevices().length != TableController.getSelectedDevices().length ${TableController.getAllDevices().length != TableController.getSelectedDevices().length
? `<a href="#" class="ml-3">Select all devices (${TableController.getAllDevices().length})</a>` ? `<a href="#" class="ml-3">Select all devices (${TableController.getAllDevices().length})</a>`
: "<a href=\"#\" class=\"ml-3\">Cancel selection</a>" : "<a href=\"#\" class=\"ml-3\">Cancel selection</a>"
}`; }`;
if (TableController.getSelectedDevices().length <= 0) { if (TableController.getSelectedDevices().length <= 0) {
@ -132,7 +132,7 @@ const selectorController = (action) => {
get_device_list(); get_device_list();
} }
btnSelectAll.addEventListener("click", event => { btnSelectAll.addEventListener("click", event => {
const checkedState = event.target.checked; const checkedState = event.target.checked;
TableController.getAllDevicesInCurrentPage().forEach(ckeckbox => { ckeckbox.checked = checkedState }); TableController.getAllDevicesInCurrentPage().forEach(ckeckbox => { ckeckbox.checked = checkedState });
@ -317,6 +317,47 @@ function export_file(type_file) {
} }
} }
class lotsSearcher {
static lots = [];
static lotsSearchElement = null;
static getListLots = () => {
const lotsList = document.getElementById("LotsSelector")
if (lotsList) {
// Apply filter to get only labels
return Array.from(lotsList.children).filter(item => item.querySelector("label"));
}
return [];
}
static enable() {
if (this.lotsSearchElement) this.lotsSearchElement.disabled = false;
}
static disable() {
if (this.lotsSearchElement) this.lotsSearchElement.disabled = true;
}
/**
* do search when lot change in the search input
*/
static doSearch(inputSearch) {
const lots = this.getListLots();
for (let i = 0; i < lots.length; i++) {
if (lot.innerText.toLowerCase().includes(inputSearch.toLowerCase())) {
lot.parentElement.style.display = "";
} else {
lot.parentElement.style.display = "none";
}
}
}
}
document.addEventListener("DOMContentLoaded", () => {
lotsSearcher.lotsSearchElement = document.getElementById("lots-search");
lotsSearcher.lotsSearchElement.addEventListener("input", (e) => { lotsSearcher.doSearch(e.target.value) })
})
/** /**
* Reactive lots button * Reactive lots button
@ -438,7 +479,7 @@ async function processSelectedDevices() {
const tmpDiv = document.createElement("div") const tmpDiv = document.createElement("div")
tmpDiv.innerHTML = newRequest tmpDiv.innerHTML = newRequest
const newTable = document.createElement("table") const newTable = document.createElement("table")
newTable.innerHTML = tmpDiv.querySelector("table").innerHTML newTable.innerHTML = tmpDiv.querySelector("table").innerHTML
newTable.classList = "table" newTable.classList = "table"
@ -557,6 +598,7 @@ async function processSelectedDevices() {
document.getElementById("ApplyDeviceLots").classList.add("disabled"); document.getElementById("ApplyDeviceLots").classList.add("disabled");
try { try {
lotsSearcher.disable()
listHTML.html("<li style=\"text-align: center\"><div class=\"spinner-border text-info\" style=\"margin: auto\" role=\"status\"></div></li>") listHTML.html("<li style=\"text-align: center\"><div class=\"spinner-border text-info\" style=\"margin: auto\" role=\"status\"></div></li>")
const selectedDevices = await Api.get_devices(selectedDevicesID); const selectedDevices = await Api.get_devices(selectedDevicesID);
let lots = await Api.get_lots(); let lots = await Api.get_lots();
@ -589,6 +631,7 @@ async function processSelectedDevices() {
listHTML.html(""); listHTML.html("");
lotsList.forEach(lot => templateLot(lot, selectedDevices, listHTML, actions)); lotsList.forEach(lot => templateLot(lot, selectedDevices, listHTML, actions));
lotsSearcher.enable();
} catch (error) { } catch (error) {
console.log(error); console.log(error);
listHTML.html("<li style=\"color: red; text-align: center\">Error feching devices and lots<br>(see console for more details)</li>"); listHTML.html("<li style=\"color: red; text-align: center\">Error feching devices and lots<br>(see console for more details)</li>");

View File

@ -87,7 +87,16 @@
<span class="caret"></span> <span class="caret"></span>
</button> </button>
<span class="d-none" id="activeTradeModal" data-bs-toggle="modal" data-bs-target="#tradeLotModal"></span> <span class="d-none" id="activeTradeModal" data-bs-toggle="modal" data-bs-target="#tradeLotModal"></span>
<ul class="dropdown-menu" aria-labelledby="btnLots" id="dropDownLotsSelector"> <ul class="dropdown-menu" aria-labelledby="btnLots" id="dropDownLotsSelector">
<div class="row w-100">
<div class="input-group mb-3 mx-2">
<div class="input-group-prepend">
<span class="input-group-text" id="basic-addon1"><i class="bi bi-search"></i></span>
</div>
<input type="text" class="form-control" id="lots-search" placeholder="search" aria-label="search" aria-describedby="basic-addon1">
</div>
</div>
<h6 class="dropdown-header">Select lots where to store the selected devices</h6> <h6 class="dropdown-header">Select lots where to store the selected devices</h6>
<ul class="mx-3" id="LotsSelector"></ul> <ul class="mx-3" id="LotsSelector"></ul>
<li><hr /></li> <li><hr /></li>