Merge branch 'testing' into fix-indeterminated-checkbox
This commit is contained in:
commit
60de8fde63
23
CHANGELOG.md
23
CHANGELOG.md
|
@ -8,10 +8,33 @@ ml).
|
||||||
## master
|
## master
|
||||||
|
|
||||||
## testing
|
## testing
|
||||||
|
|
||||||
|
## [2.1.0] - 2022-05-11
|
||||||
- [added] #219 Add functionality to searchbar (Lots and devices).
|
- [added] #219 Add functionality to searchbar (Lots and devices).
|
||||||
|
- [added] #222 Allow user to update its password.
|
||||||
|
- [added] #233 Filter in out trades from lots selector.
|
||||||
|
- [added] #236 Allow select multiple devices in multiple pages.
|
||||||
|
- [added] #237 Confirmation dialog on apply lots changes.
|
||||||
|
- [added] #238 Customize labels.
|
||||||
|
- [added] #242 Add icons in list of devices.
|
||||||
|
- [added] #244 Select full devices.
|
||||||
|
- [added] #257 Add functionality to search generic categories like all components.
|
||||||
|
- [added] #252 new tabs lots and public link in details of one device.
|
||||||
- [changed] #211 Print DHID-QR label for selected devices.
|
- [changed] #211 Print DHID-QR label for selected devices.
|
||||||
- [changed] #218 Add reactivity to device lots.
|
- [changed] #218 Add reactivity to device lots.
|
||||||
|
- [changed] #220 Add reactive lots list.
|
||||||
|
- [changed] #232 Set max lots list to 20.
|
||||||
|
- [changed] #235 Hide trade buttons.
|
||||||
|
- [changed] #239 Change Tags for Unique Identifier.
|
||||||
|
- [changed] #247 Change colors.
|
||||||
|
- [changed] #253 Drop download public links.
|
||||||
- [fixed] #214 Login workflow
|
- [fixed] #214 Login workflow
|
||||||
|
- [fixed] #221 Fix responsive issues on frontend.
|
||||||
|
- [fixed] #223 fix trade lots modal.
|
||||||
|
- [fixed] #224 fix clickable lots selector not working when click in text.
|
||||||
|
- [fixed] #254 Fix minor types in frontend.
|
||||||
|
- [fixed] #255 Fix status column on device list.
|
||||||
|
|
||||||
|
|
||||||
## [2.0.0] - 2022-03-15
|
## [2.0.0] - 2022-03-15
|
||||||
First server render HTML version. Completely rewrites views of angular JS client on flask.
|
First server render HTML version. Completely rewrites views of angular JS client on flask.
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
__version__ = "2.1.0.dev"
|
__version__ = "2.2.0.alpha0"
|
||||||
|
|
|
@ -52,16 +52,30 @@ from ereuse_devicehub.resources.user.exceptions import InsufficientPermission
|
||||||
from ereuse_devicehub.resources.user.models import User
|
from ereuse_devicehub.resources.user.models import User
|
||||||
|
|
||||||
DEVICES = {
|
DEVICES = {
|
||||||
"All": ["All"],
|
"All": ["All Devices", "All Components"],
|
||||||
"Computer": [
|
"Computer": [
|
||||||
|
"All Computers",
|
||||||
"Desktop",
|
"Desktop",
|
||||||
"Laptop",
|
"Laptop",
|
||||||
"Server",
|
"Server",
|
||||||
],
|
],
|
||||||
"Monitor": ["ComputerMonitor", "Monitor", "TelevisionSet", "Projector"],
|
"Monitor": [
|
||||||
"Mobile, tablet & smartphone": ["Mobile", "Tablet", "Smartphone", "Cellphone"],
|
"All Monitors",
|
||||||
"DataStorage": ["HardDrive", "SolidStateDrive"],
|
"ComputerMonitor",
|
||||||
|
"Monitor",
|
||||||
|
"TelevisionSet",
|
||||||
|
"Projector",
|
||||||
|
],
|
||||||
|
"Mobile, tablet & smartphone": [
|
||||||
|
"All Mobile",
|
||||||
|
"Mobile",
|
||||||
|
"Tablet",
|
||||||
|
"Smartphone",
|
||||||
|
"Cellphone",
|
||||||
|
],
|
||||||
|
"DataStorage": ["All DataStorage", "HardDrive", "SolidStateDrive"],
|
||||||
"Accessories & Peripherals": [
|
"Accessories & Peripherals": [
|
||||||
|
"All Peripherals",
|
||||||
"GraphicCard",
|
"GraphicCard",
|
||||||
"Motherboard",
|
"Motherboard",
|
||||||
"NetworkAdapter",
|
"NetworkAdapter",
|
||||||
|
@ -75,26 +89,104 @@ DEVICES = {
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
COMPUTERS = ['Desktop', 'Laptop', 'Server']
|
||||||
|
|
||||||
|
COMPONENTS = [
|
||||||
|
'GraphicCard',
|
||||||
|
'DataStorage',
|
||||||
|
'HardDrive',
|
||||||
|
'DataStorage',
|
||||||
|
'SolidStateDrive',
|
||||||
|
'Motherboard',
|
||||||
|
'NetworkAdapter',
|
||||||
|
'Processor',
|
||||||
|
'RamModule',
|
||||||
|
'SoundCard',
|
||||||
|
'Display',
|
||||||
|
'Battery',
|
||||||
|
'Camera',
|
||||||
|
]
|
||||||
|
|
||||||
|
MONITORS = ["ComputerMonitor", "Monitor", "TelevisionSet", "Projector"]
|
||||||
|
MOBILE = ["Mobile", "Tablet", "Smartphone", "Cellphone"]
|
||||||
|
DATASTORAGE = ["HardDrive", "SolidStateDrive"]
|
||||||
|
PERIPHERALS = [
|
||||||
|
"GraphicCard",
|
||||||
|
"Motherboard",
|
||||||
|
"NetworkAdapter",
|
||||||
|
"Processor",
|
||||||
|
"RamModule",
|
||||||
|
"SoundCard",
|
||||||
|
"Battery",
|
||||||
|
"Keyboard",
|
||||||
|
"Mouse",
|
||||||
|
"MemoryCardReader",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class FilterForm(FlaskForm):
|
class FilterForm(FlaskForm):
|
||||||
filter = SelectField(
|
filter = SelectField(
|
||||||
'', choices=DEVICES, default="Computer", render_kw={'class': "form-select"}
|
'', choices=DEVICES, default="All Computers", render_kw={'class': "form-select"}
|
||||||
)
|
)
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, lots, lot_id, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
self.lots = lots
|
||||||
|
self.lot_id = lot_id
|
||||||
|
self._get_types()
|
||||||
|
|
||||||
|
def _get_types(self):
|
||||||
types_of_devices = [item for sublist in DEVICES.values() for item in sublist]
|
types_of_devices = [item for sublist in DEVICES.values() for item in sublist]
|
||||||
dev = request.args.get('filter')
|
dev = request.args.get('filter')
|
||||||
self.device = dev if dev in types_of_devices else None
|
self.device_type = dev if dev in types_of_devices else None
|
||||||
if self.device:
|
if self.device_type:
|
||||||
self.filter.data = self.device
|
self.filter.data = self.device_type
|
||||||
|
|
||||||
|
def filter_from_lots(self):
|
||||||
|
if self.lot_id:
|
||||||
|
self.lot = self.lots.filter(Lot.id == self.lot_id).one()
|
||||||
|
device_ids = (d.id for d in self.lot.devices)
|
||||||
|
self.devices = Device.query.filter(Device.id.in_(device_ids))
|
||||||
|
else:
|
||||||
|
self.devices = Device.query.filter(Device.owner_id == g.user.id).filter_by(
|
||||||
|
lots=None
|
||||||
|
)
|
||||||
|
|
||||||
def search(self):
|
def search(self):
|
||||||
|
self.filter_from_lots()
|
||||||
|
filter_type = None
|
||||||
|
if self.device_type:
|
||||||
|
filter_type = [self.device_type]
|
||||||
|
else:
|
||||||
|
# Case without Filter
|
||||||
|
filter_type = COMPUTERS
|
||||||
|
|
||||||
if self.device:
|
# Generic Filters
|
||||||
return [self.device]
|
if "All Devices" == self.device_type:
|
||||||
|
filter_type = COMPUTERS + ["Monitor"] + MOBILE
|
||||||
|
|
||||||
return ['Desktop', 'Laptop', 'Server']
|
elif "All Components" == self.device_type:
|
||||||
|
filter_type = COMPONENTS
|
||||||
|
|
||||||
|
elif "All Computers" == self.device_type:
|
||||||
|
filter_type = COMPUTERS
|
||||||
|
|
||||||
|
elif "All Monitors" == self.device_type:
|
||||||
|
filter_type = MONITORS
|
||||||
|
|
||||||
|
elif "All Mobile" == self.device_type:
|
||||||
|
filter_type = MOBILE
|
||||||
|
|
||||||
|
elif "All DataStorage" == self.device_type:
|
||||||
|
filter_type = DATASTORAGE
|
||||||
|
|
||||||
|
elif "All Peripherals" == self.device_type:
|
||||||
|
filter_type = PERIPHERALS
|
||||||
|
|
||||||
|
if filter_type:
|
||||||
|
self.devices = self.devices.filter(Device.type.in_(filter_type))
|
||||||
|
|
||||||
|
return self.devices.order_by(Device.updated.desc())
|
||||||
|
|
||||||
|
|
||||||
class LotForm(FlaskForm):
|
class LotForm(FlaskForm):
|
||||||
|
|
|
@ -43,8 +43,8 @@ class DeviceListMix(GenericMixView):
|
||||||
def get_context(self, lot_id):
|
def get_context(self, lot_id):
|
||||||
super().get_context()
|
super().get_context()
|
||||||
lots = self.context['lots']
|
lots = self.context['lots']
|
||||||
form_filter = FilterForm()
|
form_filter = FilterForm(lots, lot_id)
|
||||||
filter_types = form_filter.search()
|
devices = form_filter.search()
|
||||||
lot = None
|
lot = None
|
||||||
tags = (
|
tags = (
|
||||||
Tag.query.filter(Tag.owner_id == current_user.id)
|
Tag.query.filter(Tag.owner_id == current_user.id)
|
||||||
|
@ -54,10 +54,6 @@ class DeviceListMix(GenericMixView):
|
||||||
|
|
||||||
if lot_id:
|
if lot_id:
|
||||||
lot = lots.filter(Lot.id == lot_id).one()
|
lot = lots.filter(Lot.id == lot_id).one()
|
||||||
devices = lot.devices
|
|
||||||
if "All" not in filter_types:
|
|
||||||
devices = [dev for dev in lot.devices if dev.type in filter_types]
|
|
||||||
devices = sorted(devices, key=lambda x: x.updated, reverse=True)
|
|
||||||
form_new_action = NewActionForm(lot=lot.id)
|
form_new_action = NewActionForm(lot=lot.id)
|
||||||
form_new_allocate = AllocateForm(lot=lot.id)
|
form_new_allocate = AllocateForm(lot=lot.id)
|
||||||
form_new_datawipe = DataWipeForm(lot=lot.id)
|
form_new_datawipe = DataWipeForm(lot=lot.id)
|
||||||
|
@ -67,20 +63,6 @@ class DeviceListMix(GenericMixView):
|
||||||
user_from=g.user.email,
|
user_from=g.user.email,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
if "All" in filter_types:
|
|
||||||
devices = (
|
|
||||||
Device.query.filter(Device.owner_id == current_user.id)
|
|
||||||
.filter_by(lots=None)
|
|
||||||
.order_by(Device.updated.desc())
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
devices = (
|
|
||||||
Device.query.filter(Device.owner_id == current_user.id)
|
|
||||||
.filter_by(lots=None)
|
|
||||||
.filter(Device.type.in_(filter_types))
|
|
||||||
.order_by(Device.updated.desc())
|
|
||||||
)
|
|
||||||
|
|
||||||
form_new_action = NewActionForm()
|
form_new_action = NewActionForm()
|
||||||
form_new_allocate = AllocateForm()
|
form_new_allocate = AllocateForm()
|
||||||
form_new_datawipe = DataWipeForm()
|
form_new_datawipe = DataWipeForm()
|
||||||
|
|
|
@ -83,25 +83,33 @@ window.addEventListener("DOMContentLoaded", () => {
|
||||||
const alertInfoDevices = document.getElementById("select-devices-info");
|
const alertInfoDevices = document.getElementById("select-devices-info");
|
||||||
|
|
||||||
function itemListCheckChanged() {
|
function itemListCheckChanged() {
|
||||||
const listDevices = TableController.getAllDevicesInCurrentPage()
|
alertInfoDevices.innerHTML = `Selected devices: ${TableController.getSelectedDevices().length}
|
||||||
const isAllChecked = listDevices.map(itm => itm.checked);
|
|
||||||
|
|
||||||
if (isAllChecked.every(bool => bool == true)) {
|
|
||||||
btnSelectAll.checked = true;
|
|
||||||
btnSelectAll.indeterminate = false;
|
|
||||||
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>"
|
||||||
}`;
|
}`;
|
||||||
alertInfoDevices.classList.remove("d-none");
|
|
||||||
} else if (isAllChecked.every(bool => bool == false)) {
|
if (TableController.getSelectedDevices().length <= 0) {
|
||||||
btnSelectAll.checked = false;
|
|
||||||
btnSelectAll.indeterminate = false;
|
|
||||||
alertInfoDevices.classList.add("d-none")
|
alertInfoDevices.classList.add("d-none")
|
||||||
} else {
|
} else {
|
||||||
|
alertInfoDevices.classList.remove("d-none");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TableController.getAllDevices().length == TableController.getSelectedDevices().length) {
|
||||||
|
btnSelectAll.checked = true;
|
||||||
|
btnSelectAll.indeterminate = false;
|
||||||
|
} else if(TableController.getAllSelectedDevicesInCurrentPage().length > 0) {
|
||||||
btnSelectAll.indeterminate = true;
|
btnSelectAll.indeterminate = true;
|
||||||
alertInfoDevices.classList.add("d-none")
|
} else {
|
||||||
|
btnSelectAll.checked = false;
|
||||||
|
btnSelectAll.indeterminate = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TableController.getAllDevices().length == 0) {
|
||||||
|
btnSelectAll.checked = false;
|
||||||
|
btnSelectAll.disabled = true;
|
||||||
|
} else {
|
||||||
|
btnSelectAll.disabled = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -125,6 +133,8 @@ window.addEventListener("DOMContentLoaded", () => {
|
||||||
table.on("datatable.page", () => itemListCheckChanged());
|
table.on("datatable.page", () => itemListCheckChanged());
|
||||||
table.on("datatable.perpage", () => itemListCheckChanged());
|
table.on("datatable.perpage", () => itemListCheckChanged());
|
||||||
table.on("datatable.update", () => itemListCheckChanged());
|
table.on("datatable.update", () => itemListCheckChanged());
|
||||||
|
|
||||||
|
itemListCheckChanged();
|
||||||
})
|
})
|
||||||
|
|
||||||
function deviceSelect() {
|
function deviceSelect() {
|
||||||
|
|
|
@ -60,7 +60,7 @@
|
||||||
<button class="btn btn-primary w-100" type="submit">Login</button>
|
<button class="btn btn-primary w-100" type="submit">Login</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<p class="small mb-0">Don't have account? <a href="#TODO">Create an account</a></p>
|
<p class="small mb-0">Don't have account? <a href="#TODO" onclick="$('#exampleModal').modal('show')" data-toggle="modal" data-target="#exampleModal">Create an account</a></p>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
@ -83,4 +83,18 @@
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</main><!-- End #main -->
|
</main><!-- End #main -->
|
||||||
|
|
||||||
|
<!-- Modal -->
|
||||||
|
<div class="modal fade" id="exampleModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog modal-dialog-centered" role="document">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="exampleModalLabel">Do you want to try USOdy tools?</h5>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
Just write an email to <a href="mali:hello@usody.com">hello@usody.com</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div> <!-- End register modal -->
|
||||||
{% endblock body %}
|
{% endblock body %}
|
||||||
|
|
|
@ -84,7 +84,7 @@
|
||||||
</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">
|
||||||
<h6 class="dropdown-header">Select some devices to manage lots</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>
|
||||||
<li>
|
<li>
|
||||||
|
|
Reference in New Issue