Merge branch 'testing' into fix-indeterminated-checkbox

This commit is contained in:
RubenPX 2022-05-11 17:44:50 +02:00 committed by GitHub
commit 60de8fde63
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 168 additions and 47 deletions

View File

@ -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.

View File

@ -1 +1 @@
__version__ = "2.1.0.dev" __version__ = "2.2.0.alpha0"

View File

@ -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):

View File

@ -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()

View File

@ -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() {

View File

@ -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 %}

View File

@ -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>