Merge commit '7177b0fbfda37e495387834c7f2342b78f6559c8' into feature/confirm-trade-changes
This commit is contained in:
commit
eb5c109220
|
@ -9,6 +9,12 @@ dags-with-materialized-paths-using-postgres-ltree/>`_ you have
|
||||||
a low-level technical implementation of how lots and their
|
a low-level technical implementation of how lots and their
|
||||||
relationships are mapped.
|
relationships are mapped.
|
||||||
|
|
||||||
|
Getting lots
|
||||||
|
************
|
||||||
|
|
||||||
|
You can get lots list by ``GET /lots/``
|
||||||
|
There are one optional filter ``type``, only works with this 3 values ``temporary``, ``incoming`` and ``outgoing``
|
||||||
|
|
||||||
Create lots
|
Create lots
|
||||||
***********
|
***********
|
||||||
You create a lot by ``POST /lots/`` a `JSON Lot object <https://
|
You create a lot by ``POST /lots/`` a `JSON Lot object <https://
|
||||||
|
@ -28,7 +34,6 @@ And for devices is all the same:
|
||||||
``POST /lots/<parent-lot-id>/devices/?id=<device-id-1>&id=<device-id-2>``;
|
``POST /lots/<parent-lot-id>/devices/?id=<device-id-1>&id=<device-id-2>``;
|
||||||
idem for removing devices.
|
idem for removing devices.
|
||||||
|
|
||||||
|
|
||||||
Sharing lots
|
Sharing lots
|
||||||
************
|
************
|
||||||
Sharing a lot means giving certain permissions to users, like reading
|
Sharing a lot means giving certain permissions to users, like reading
|
||||||
|
|
|
@ -593,30 +593,68 @@ class NewActionForm(ActionFormMix):
|
||||||
|
|
||||||
class AllocateForm(ActionFormMix):
|
class AllocateForm(ActionFormMix):
|
||||||
start_time = DateField('Start time')
|
start_time = DateField('Start time')
|
||||||
end_time = DateField('End time')
|
end_time = DateField('End time', [validators.Optional()])
|
||||||
final_user_code = StringField('Final user code', [validators.length(max=50)])
|
final_user_code = StringField(
|
||||||
transaction = StringField('Transaction', [validators.length(max=50)])
|
'Final user code', [validators.Optional(), validators.length(max=50)]
|
||||||
end_users = IntegerField('End users')
|
)
|
||||||
|
transaction = StringField(
|
||||||
|
'Transaction', [validators.Optional(), validators.length(max=50)]
|
||||||
|
)
|
||||||
|
end_users = IntegerField('End users', [validators.Optional()])
|
||||||
|
|
||||||
def validate(self, extra_validators=None):
|
def validate(self, extra_validators=None):
|
||||||
is_valid = super().validate(extra_validators)
|
if not super().validate(extra_validators):
|
||||||
|
return False
|
||||||
|
|
||||||
if self.type.data not in ['Allocate', 'Deallocate']:
|
if self.type.data not in ['Allocate', 'Deallocate']:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
if not self.validate_dates():
|
||||||
|
return False
|
||||||
|
|
||||||
|
if not self.check_devices():
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def validate_dates(self):
|
||||||
start_time = self.start_time.data
|
start_time = self.start_time.data
|
||||||
end_time = self.end_time.data
|
end_time = self.end_time.data
|
||||||
|
|
||||||
|
if not start_time:
|
||||||
|
self.start_time.errors = ['Not a valid date value.!']
|
||||||
|
return False
|
||||||
|
|
||||||
if start_time and end_time and end_time < start_time:
|
if start_time and end_time and end_time < start_time:
|
||||||
error = ['The action cannot finish before it starts.']
|
error = ['The action cannot finish before it starts.']
|
||||||
self.start_time.errors = error
|
|
||||||
self.end_time.errors = error
|
self.end_time.errors = error
|
||||||
is_valid = False
|
return False
|
||||||
|
|
||||||
if not self.end_users.data:
|
if not end_time:
|
||||||
self.end_users.errors = ["You need to specify a number of users"]
|
self.end_time.data = self.start_time.data
|
||||||
is_valid = False
|
|
||||||
|
|
||||||
return is_valid
|
return True
|
||||||
|
|
||||||
|
def check_devices(self):
|
||||||
|
if self.type.data == 'Allocate':
|
||||||
|
txt = "You need deallocate before allocate this device again"
|
||||||
|
for device in self._devices:
|
||||||
|
if device.allocated:
|
||||||
|
self.devices.errors = [txt]
|
||||||
|
return False
|
||||||
|
|
||||||
|
device.allocated = True
|
||||||
|
|
||||||
|
if self.type.data == 'Deallocate':
|
||||||
|
txt = "Sorry some of this devices are actually deallocate"
|
||||||
|
for device in self._devices:
|
||||||
|
if not device.allocated:
|
||||||
|
self.devices.errors = [txt]
|
||||||
|
return False
|
||||||
|
|
||||||
|
device.allocated = False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
class DataWipeDocumentForm(Form):
|
class DataWipeDocumentForm(Form):
|
||||||
|
|
|
@ -421,7 +421,6 @@ class ExportsView(View):
|
||||||
'metrics': self.metrics,
|
'metrics': self.metrics,
|
||||||
'devices': self.devices_list,
|
'devices': self.devices_list,
|
||||||
'certificates': self.erasure,
|
'certificates': self.erasure,
|
||||||
'links': self.public_links,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if export_id not in export_ids:
|
if export_id not in export_ids:
|
||||||
|
@ -497,19 +496,6 @@ class ExportsView(View):
|
||||||
|
|
||||||
return self.response_csv(data, "actions_export.csv")
|
return self.response_csv(data, "actions_export.csv")
|
||||||
|
|
||||||
def public_links(self):
|
|
||||||
# get a csv with the publink links of this devices
|
|
||||||
data = StringIO()
|
|
||||||
cw = csv.writer(data, delimiter=';', lineterminator="\n", quotechar='"')
|
|
||||||
cw.writerow(['links'])
|
|
||||||
host_url = request.host_url
|
|
||||||
for dev in self.find_devices():
|
|
||||||
code = dev.devicehub_id
|
|
||||||
link = [f"{host_url}devices/{code}"]
|
|
||||||
cw.writerow(link)
|
|
||||||
|
|
||||||
return self.response_csv(data, "links.csv")
|
|
||||||
|
|
||||||
def erasure(self):
|
def erasure(self):
|
||||||
template = self.build_erasure_certificate()
|
template = self.build_erasure_certificate()
|
||||||
res = flask_weasyprint.render_pdf(
|
res = flask_weasyprint.render_pdf(
|
||||||
|
|
|
@ -10,7 +10,7 @@ from typing import Dict, List, Set
|
||||||
from boltons import urlutils
|
from boltons import urlutils
|
||||||
from citext import CIText
|
from citext import CIText
|
||||||
from ereuse_utils.naming import HID_CONVERSION_DOC, Naming
|
from ereuse_utils.naming import HID_CONVERSION_DOC, Naming
|
||||||
from flask import g
|
from flask import g, request
|
||||||
from flask_sqlalchemy import event
|
from flask_sqlalchemy import event
|
||||||
from more_itertools import unique_everseen
|
from more_itertools import unique_everseen
|
||||||
from sqlalchemy import BigInteger, Boolean, Column
|
from sqlalchemy import BigInteger, Boolean, Column
|
||||||
|
@ -297,6 +297,11 @@ class Device(Thing):
|
||||||
actions.reverse()
|
actions.reverse()
|
||||||
return actions
|
return actions
|
||||||
|
|
||||||
|
@property
|
||||||
|
def public_link(self) -> str:
|
||||||
|
host_url = request.host_url.strip('/')
|
||||||
|
return "{}{}".format(host_url, self.url.to_text())
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def url(self) -> urlutils.URL:
|
def url(self) -> urlutils.URL:
|
||||||
"""The URL where to GET this device."""
|
"""The URL where to GET this device."""
|
||||||
|
|
|
@ -29,6 +29,7 @@ class LotView(View):
|
||||||
"""
|
"""
|
||||||
format = EnumField(LotFormat, missing=None)
|
format = EnumField(LotFormat, missing=None)
|
||||||
search = f.Str(missing=None)
|
search = f.Str(missing=None)
|
||||||
|
type = f.Str(missing=None)
|
||||||
|
|
||||||
def post(self):
|
def post(self):
|
||||||
l = request.get_json()
|
l = request.get_json()
|
||||||
|
@ -88,6 +89,7 @@ class LotView(View):
|
||||||
else:
|
else:
|
||||||
query = Lot.query
|
query = Lot.query
|
||||||
query = self.visibility_filter(query)
|
query = self.visibility_filter(query)
|
||||||
|
query = self.type_filter(query, args)
|
||||||
if args['search']:
|
if args['search']:
|
||||||
query = query.filter(Lot.name.ilike(args['search'] + '%'))
|
query = query.filter(Lot.name.ilike(args['search'] + '%'))
|
||||||
lots = query.paginate(per_page=6 if args['search'] else query.count())
|
lots = query.paginate(per_page=6 if args['search'] else query.count())
|
||||||
|
@ -104,6 +106,21 @@ class LotView(View):
|
||||||
Lot.owner_id == g.user.id))
|
Lot.owner_id == g.user.id))
|
||||||
return query
|
return query
|
||||||
|
|
||||||
|
def type_filter(self, query, args):
|
||||||
|
lot_type = args.get('type')
|
||||||
|
|
||||||
|
# temporary
|
||||||
|
if lot_type == "temporary":
|
||||||
|
return query.filter(Lot.trade == None)
|
||||||
|
|
||||||
|
if lot_type == "incoming":
|
||||||
|
return query.filter(Lot.trade and Trade.user_to == g.user)
|
||||||
|
|
||||||
|
if lot_type == "outgoing":
|
||||||
|
return query.filter(Lot.trade and Trade.user_from == g.user)
|
||||||
|
|
||||||
|
return query
|
||||||
|
|
||||||
def query(self, args):
|
def query(self, args):
|
||||||
query = Lot.query.distinct()
|
query = Lot.query.distinct()
|
||||||
return query
|
return query
|
||||||
|
|
|
@ -19,12 +19,12 @@ body {
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
color: #4154f1;
|
color: #6c757d ;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
a:hover {
|
a:hover {
|
||||||
color: #717ff5;
|
color: #cc0066;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,7 +56,7 @@ h1, h2, h3, h4, h5, h6 {
|
||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: #012970;
|
color: #993365;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*--------------------------------------------------------------
|
/*--------------------------------------------------------------
|
||||||
|
@ -176,6 +176,31 @@ h1, h2, h3, h4, h5, h6 {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.btn-primary {
|
||||||
|
background-color: #993365;
|
||||||
|
border-color: #993365;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary:hover, .btn-primary:focus {
|
||||||
|
background-color: #cc0066;
|
||||||
|
border-color: #cc0066;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-danger {
|
||||||
|
background-color: #b3b1b1;
|
||||||
|
border-color: #b3b1b1;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-danger:hover {
|
||||||
|
background-color: #645e5f;
|
||||||
|
border-color: #645e5f;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
/* Light Backgrounds */
|
/* Light Backgrounds */
|
||||||
.bg-primary-light {
|
.bg-primary-light {
|
||||||
background-color: #cfe2ff;
|
background-color: #cfe2ff;
|
||||||
|
@ -326,12 +351,12 @@ h1, h2, h3, h4, h5, h6 {
|
||||||
color: #2c384e;
|
color: #2c384e;
|
||||||
}
|
}
|
||||||
.nav-tabs-bordered .nav-link:hover, .nav-tabs-bordered .nav-link:focus {
|
.nav-tabs-bordered .nav-link:hover, .nav-tabs-bordered .nav-link:focus {
|
||||||
color: #4154f1;
|
color: #993365;
|
||||||
}
|
}
|
||||||
.nav-tabs-bordered .nav-link.active {
|
.nav-tabs-bordered .nav-link.active {
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
color: #4154f1;
|
color: #993365;
|
||||||
border-bottom: 2px solid #4154f1;
|
border-bottom: 2px solid #993365;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*--------------------------------------------------------------
|
/*--------------------------------------------------------------
|
||||||
|
@ -370,7 +395,7 @@ h1, h2, h3, h4, h5, h6 {
|
||||||
font-size: 32px;
|
font-size: 32px;
|
||||||
padding-left: 10px;
|
padding-left: 10px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
color: #012970;
|
color: #993365;
|
||||||
}
|
}
|
||||||
.header .search-bar {
|
.header .search-bar {
|
||||||
min-width: 360px;
|
min-width: 360px;
|
||||||
|
@ -439,7 +464,7 @@ h1, h2, h3, h4, h5, h6 {
|
||||||
color: #012970;
|
color: #012970;
|
||||||
}
|
}
|
||||||
.header-nav .nav-profile {
|
.header-nav .nav-profile {
|
||||||
color: #012970;
|
color: #993365;
|
||||||
}
|
}
|
||||||
.header-nav .nav-profile img {
|
.header-nav .nav-profile img {
|
||||||
max-height: 36px;
|
max-height: 36px;
|
||||||
|
@ -606,7 +631,7 @@ h1, h2, h3, h4, h5, h6 {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: #4154f1;
|
color: #993365;
|
||||||
transition: 0.3;
|
transition: 0.3;
|
||||||
background: #f6f9ff;
|
background: #f6f9ff;
|
||||||
padding: 10px 15px;
|
padding: 10px 15px;
|
||||||
|
@ -615,21 +640,21 @@ h1, h2, h3, h4, h5, h6 {
|
||||||
.sidebar-nav .nav-link i {
|
.sidebar-nav .nav-link i {
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
color: #4154f1;
|
color: #993365;
|
||||||
}
|
}
|
||||||
.sidebar-nav .nav-link.collapsed {
|
.sidebar-nav .nav-link.collapsed {
|
||||||
color: #012970;
|
color: #6c757d;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
}
|
}
|
||||||
.sidebar-nav .nav-link.collapsed i {
|
.sidebar-nav .nav-link.collapsed i {
|
||||||
color: #899bbd;
|
color: #899bbd;
|
||||||
}
|
}
|
||||||
.sidebar-nav .nav-link:hover {
|
.sidebar-nav .nav-link:hover {
|
||||||
color: #4154f1;
|
color: #993365;
|
||||||
background: #f6f9ff;
|
background: #f6f9ff;
|
||||||
}
|
}
|
||||||
.sidebar-nav .nav-link:hover i {
|
.sidebar-nav .nav-link:hover i {
|
||||||
color: #4154f1;
|
color: #993365;
|
||||||
}
|
}
|
||||||
.sidebar-nav .nav-link .bi-chevron-down {
|
.sidebar-nav .nav-link .bi-chevron-down {
|
||||||
margin-right: 0;
|
margin-right: 0;
|
||||||
|
@ -660,7 +685,7 @@ h1, h2, h3, h4, h5, h6 {
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
}
|
}
|
||||||
.sidebar-nav .nav-content a:hover, .sidebar-nav .nav-content a.active {
|
.sidebar-nav .nav-content a:hover, .sidebar-nav .nav-content a.active {
|
||||||
color: #4154f1;
|
color: #993365;
|
||||||
}
|
}
|
||||||
.sidebar-nav .nav-content a.active i {
|
.sidebar-nav .nav-content a.active i {
|
||||||
background-color: #4154f1;
|
background-color: #4154f1;
|
||||||
|
@ -1003,7 +1028,7 @@ h1, h2, h3, h4, h5, h6 {
|
||||||
padding: 12px 15px;
|
padding: 12px 15px;
|
||||||
}
|
}
|
||||||
.contact .php-email-form button[type=submit] {
|
.contact .php-email-form button[type=submit] {
|
||||||
background: #4154f1;
|
background: #993365;
|
||||||
border: 0;
|
border: 0;
|
||||||
padding: 10px 30px;
|
padding: 10px 30px;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
|
@ -1011,7 +1036,15 @@ h1, h2, h3, h4, h5, h6 {
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
.contact .php-email-form button[type=submit]:hover {
|
.contact .php-email-form button[type=submit]:hover {
|
||||||
background: #5969f3;
|
background: #993365;
|
||||||
|
}
|
||||||
|
button[type=submit] {
|
||||||
|
background-color: #993365;
|
||||||
|
border-color: #993365;
|
||||||
|
}
|
||||||
|
button[type=submit]:hover {
|
||||||
|
background-color: #993365;
|
||||||
|
border-color: #993365;
|
||||||
}
|
}
|
||||||
@-webkit-keyframes animate-loading {
|
@-webkit-keyframes animate-loading {
|
||||||
0% {
|
0% {
|
||||||
|
|
BIN
ereuse_devicehub/static/img/logo_usody_clock.png
Normal file
BIN
ereuse_devicehub/static/img/logo_usody_clock.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 22 KiB |
83
ereuse_devicehub/static/img/logo_usody_clock.svg
Normal file
83
ereuse_devicehub/static/img/logo_usody_clock.svg
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
width="188.02727mm"
|
||||||
|
height="48.976315mm"
|
||||||
|
viewBox="0 0 188.02727 48.976315"
|
||||||
|
version="1.1"
|
||||||
|
id="svg20276"
|
||||||
|
sodipodi:docname="logo_usody_clock.svg"
|
||||||
|
inkscape:version="1.1.1 (3bf5ae0, 2021-09-20)"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="namedview20278"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:document-units="mm"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="0.82671156"
|
||||||
|
inkscape:cx="332.03842"
|
||||||
|
inkscape:cy="-66.528645"
|
||||||
|
inkscape:window-width="1680"
|
||||||
|
inkscape:window-height="1013"
|
||||||
|
inkscape:window-x="1280"
|
||||||
|
inkscape:window-y="0"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="layer1" />
|
||||||
|
<defs
|
||||||
|
id="defs20273" />
|
||||||
|
<g
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1"
|
||||||
|
transform="translate(-82.275303,-65.555746)">
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:50.8px;line-height:1.25;font-family:sans-serif;stroke-width:0.264583"
|
||||||
|
x="126.75784"
|
||||||
|
y="103.84124"
|
||||||
|
id="text1904"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan1902"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:50.8px;font-family:Arial;-inkscape-font-specification:'Arial, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583"
|
||||||
|
x="126.75784"
|
||||||
|
y="103.84124">Usod<tspan
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:50.8px;font-family:Arial;-inkscape-font-specification:'Arial, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#993265;fill-opacity:1"
|
||||||
|
id="tspan8044">y</tspan></tspan></text>
|
||||||
|
<g
|
||||||
|
id="g14774-8"
|
||||||
|
style="fill:#993264;fill-opacity:1;stroke:#993265;stroke-opacity:1"
|
||||||
|
transform="matrix(0.083748,0,0,0.08393574,81.384743,64.67498)">
|
||||||
|
<g
|
||||||
|
id="g14772-6"
|
||||||
|
style="fill:#993264;fill-opacity:1;stroke:#993265;stroke-opacity:1">
|
||||||
|
<path
|
||||||
|
d="M 499.5,230.1 C 485.2,95.8 364.4,-2 230,12.4 157,20.2 92,59.8 51.5,121.1 c -6.2,9.4 -3.6,22.1 5.8,28.3 9.4,6.2 22.1,3.6 28.3,-5.8 33.8,-51.1 88,-84.1 148.8,-90.5 111.9,-11.9 212.6,69.4 224.5,181.3 5.8,54.2 -9.9,107.4 -44.2,149.9 -34.3,42.4 -83,69 -137.2,74.7 -74,8 -146.6,-25.6 -188.9,-86 l 37.7,8.1 c 11.1,2.3 21.9,-4.6 24.3,-15.7 2.4,-11 -4.6,-21.9 -15.7,-24.3 L 53.4,323.6 c -11,-2.4 -21.9,4.6 -24.3,15.7 l -17.5,81.5 c -2.4,11 4.7,21.7 15.7,24.3 12.4,2.9 22.2,-6.1 24.3,-15.7 L 57.9,400 c 46.1,63.3 120.2,101 198.3,101 8.5,0 17.1,-0.4 25.7,-1.4 65.1,-6.9 123.6,-38.8 164.7,-89.7 41,-50.8 59.8,-114.7 52.9,-179.8 z"
|
||||||
|
id="path14768-0"
|
||||||
|
style="fill:#993264;fill-opacity:1;stroke:#993265;stroke-opacity:1" />
|
||||||
|
<path
|
||||||
|
d="m 271.8,140.3 c -11.3,0 -20.4,9.1 -20.4,20.4 V 256 c 0,5.4 2.2,10.6 6,14.4 l 95.3,95.3 c 9.5,10.7 24.9,4 28.9,0 8,-8 8,-20.9 0,-28.9 l -89.3,-89.3 v -86.8 c -0.1,-11.2 -9.2,-20.4 -20.5,-20.4 z"
|
||||||
|
id="path14770-4"
|
||||||
|
style="fill:#993264;fill-opacity:1;stroke:#993265;stroke-opacity:1" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g14772"
|
||||||
|
transform="matrix(0.083748,0,0,0.08393574,81.384743,64.67498)">
|
||||||
|
<path
|
||||||
|
id="path14768"
|
||||||
|
d="M 255.01758,10.996094 C 246.74651,11.038013 238.4,11.500391 230,12.400391 c -73,7.8 -138,47.399218 -178.5,108.699219 -6.2,9.4 -3.599219,22.10078 5.800781,28.30078 9.4,6.2 22.098828,3.59922 28.298828,-5.80078 33.800001,-51.100001 88.000781,-84.100001 148.800781,-90.500001 111.9,-11.9 212.6,69.400781 224.5,181.300781 5.8,54.2 -9.90117,107.40039 -44.20117,149.90039 C 380.39922,426.70078 331.7,453.3 277.5,459 c -8.97135,0.96988 -17.92013,1.30083 -26.79883,1.07422 l 0.0449,40.76172 c 1.81975,0.041 3.62871,0.16406 5.45313,0.16406 8.5,0 17.10117,-0.40039 25.70117,-1.40039 65.1,-6.9 123.59922,-38.79922 164.69922,-89.69922 41,-50.8 59.80039,-114.70078 52.90039,-179.80078 C 486.09375,104.19336 379.08362,10.36731 255.01758,10.996094 Z" />
|
||||||
|
<path
|
||||||
|
d="m 271.8,140.3 c -11.3,0 -20.4,9.1 -20.4,20.4 V 256 c 0,5.4 2.2,10.6 6,14.4 l 95.3,95.3 c 9.5,10.7 24.9,4 28.9,0 8,-8 8,-20.9 0,-28.9 l -89.3,-89.3 v -86.8 c -0.1,-11.2 -9.2,-20.4 -20.5,-20.4 z"
|
||||||
|
id="path14770" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 4.7 KiB |
|
@ -4,7 +4,7 @@ const Api = {
|
||||||
* @returns get lots
|
* @returns get lots
|
||||||
*/
|
*/
|
||||||
async get_lots() {
|
async get_lots() {
|
||||||
const request = await this.doRequest(API_URLS.lots, "GET", null);
|
const request = await this.doRequest(`${API_URLS.lots}?type=temporary`, "GET", null);
|
||||||
if (request != undefined) return request.items;
|
if (request != undefined) return request.items;
|
||||||
throw request;
|
throw request;
|
||||||
},
|
},
|
||||||
|
|
|
@ -80,6 +80,7 @@ class TableController {
|
||||||
*/
|
*/
|
||||||
window.addEventListener("DOMContentLoaded", () => {
|
window.addEventListener("DOMContentLoaded", () => {
|
||||||
const btnSelectAll = document.getElementById("SelectAllBTN");
|
const btnSelectAll = document.getElementById("SelectAllBTN");
|
||||||
|
const alertInfoDevices = document.getElementById("select-devices-info");
|
||||||
|
|
||||||
function itemListCheckChanged() {
|
function itemListCheckChanged() {
|
||||||
const listDevices = TableController.getAllDevicesInCurrentPage()
|
const listDevices = TableController.getAllDevicesInCurrentPage()
|
||||||
|
@ -88,11 +89,20 @@ window.addEventListener("DOMContentLoaded", () => {
|
||||||
if (isAllChecked.every(bool => bool == true)) {
|
if (isAllChecked.every(bool => bool == true)) {
|
||||||
btnSelectAll.checked = true;
|
btnSelectAll.checked = true;
|
||||||
btnSelectAll.indeterminate = false;
|
btnSelectAll.indeterminate = false;
|
||||||
|
alertInfoDevices.innerHTML = `Selected devices: ${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\">Cancel selection</a>"
|
||||||
|
}`;
|
||||||
|
alertInfoDevices.classList.remove("d-none");
|
||||||
} else if (isAllChecked.every(bool => bool == false)) {
|
} else if (isAllChecked.every(bool => bool == false)) {
|
||||||
btnSelectAll.checked = false;
|
btnSelectAll.checked = false;
|
||||||
btnSelectAll.indeterminate = false;
|
btnSelectAll.indeterminate = false;
|
||||||
|
alertInfoDevices.classList.add("d-none")
|
||||||
} else {
|
} else {
|
||||||
btnSelectAll.indeterminate = true;
|
btnSelectAll.indeterminate = true;
|
||||||
|
alertInfoDevices.classList.add("d-none")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -103,6 +113,13 @@ window.addEventListener("DOMContentLoaded", () => {
|
||||||
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 });
|
||||||
|
itemListCheckChanged()
|
||||||
|
})
|
||||||
|
|
||||||
|
alertInfoDevices.addEventListener("click", () => {
|
||||||
|
const checkState = TableController.getAllDevices().length == TableController.getSelectedDevices().length
|
||||||
|
TableController.getAllDevices().forEach(ckeckbox => { ckeckbox.checked = !checkState });
|
||||||
|
itemListCheckChanged()
|
||||||
})
|
})
|
||||||
|
|
||||||
// https://github.com/fiduswriter/Simple-DataTables/wiki/Events
|
// https://github.com/fiduswriter/Simple-DataTables/wiki/Events
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
<div class="d-flex align-items-center justify-content-between">
|
<div class="d-flex align-items-center justify-content-between">
|
||||||
<a href="{{ url_for('inventory.devicelist')}}" class="logo d-flex align-items-center">
|
<a href="{{ url_for('inventory.devicelist')}}" class="logo d-flex align-items-center">
|
||||||
<img src="{{ url_for('static', filename='img/usody-logo-black.svg') }}" alt="">
|
<img src="{{ url_for('static', filename='img/logo_usody_clock.png') }}" alt="">
|
||||||
</a>
|
</a>
|
||||||
<i class="bi bi-list toggle-sidebar-btn"></i>
|
<i class="bi bi-list toggle-sidebar-btn"></i>
|
||||||
</div><!-- End Logo -->
|
</div><!-- End Logo -->
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
|
|
||||||
<div class="d-flex justify-content-center py-4">
|
<div class="d-flex justify-content-center py-4">
|
||||||
<a href="{{ url_for('core.login') }}" class="logo d-flex align-items-center w-auto">
|
<a href="{{ url_for('core.login') }}" class="logo d-flex align-items-center w-auto">
|
||||||
<img src="{{ url_for('static', filename='img/usody-logo-black.svg') }}" alt="">
|
<img src="{{ url_for('static', filename='img/logo_usody_clock.png') }}" alt="">
|
||||||
</a>
|
</a>
|
||||||
</div><!-- End Logo -->
|
</div><!-- End Logo -->
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,14 @@
|
||||||
<button class="nav-link active" data-bs-toggle="tab" data-bs-target="#type">Type</button>
|
<button class="nav-link active" data-bs-toggle="tab" data-bs-target="#type">Type</button>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="{{ device.public_link }}" target="_blank">Web</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="nav-item">
|
||||||
|
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#lots">Lots</button>
|
||||||
|
</li>
|
||||||
|
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#status">Status</button>
|
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#status">Status</button>
|
||||||
</li>
|
</li>
|
||||||
|
@ -69,6 +77,50 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="tab-pane fade profile-overview" id="lots">
|
||||||
|
<h5 class="card-title">Incoming Lots</h5>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
{% for lot in device.lots %}
|
||||||
|
{% if lot.is_incoming %}
|
||||||
|
<div class="col">
|
||||||
|
<a class="ms-3" href="{{ url_for('inventory.lotdevicelist', lot_id=lot.id) }}">
|
||||||
|
<span>{{ lot.name }}</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h5 class="card-title">Outgoing Lots</h5>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
{% for lot in device.lots %}
|
||||||
|
{% if lot.is_outgoing %}
|
||||||
|
<div class="col">
|
||||||
|
<a class="ms-3" href="{{ url_for('inventory.lotdevicelist', lot_id=lot.id) }}">
|
||||||
|
<span>{{ lot.name }}</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h5 class="card-title">Temporary Lots</h5>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
{% for lot in device.lots %}
|
||||||
|
{% if lot.is_temporary %}
|
||||||
|
<div class="col">
|
||||||
|
<a class="ms-3" href="{{ url_for('inventory.lotdevicelist', lot_id=lot.id) }}">
|
||||||
|
<span>{{ lot.name }}</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="tab-pane fade profile-overview" id="status">
|
<div class="tab-pane fade profile-overview" id="status">
|
||||||
<h5 class="card-title">Status Details</h5>
|
<h5 class="card-title">Status Details</h5>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
|
|
@ -201,12 +201,6 @@
|
||||||
Metrics Spreadsheet
|
Metrics Spreadsheet
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
|
||||||
<a href="javascript:export_file('links')" class="dropdown-item">
|
|
||||||
<i class="bi bi-link-45deg"></i>
|
|
||||||
Public Links
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
<li>
|
||||||
<a href="javascript:export_file('certificates')" class="dropdown-item">
|
<a href="javascript:export_file('certificates')" class="dropdown-item">
|
||||||
<i class="bi bi-eraser-fill"></i>
|
<i class="bi bi-eraser-fill"></i>
|
||||||
|
@ -314,6 +308,10 @@
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
<div id="select-devices-info" class="alert alert-info mb-0 mt-3 d-none" role="alert">
|
||||||
|
If this text is showing is because there are an error
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="tab-content pt-2">
|
<div class="tab-content pt-2">
|
||||||
<form method="get">
|
<form method="get">
|
||||||
<div class="d-flex mt-4 mb-4">
|
<div class="d-flex mt-4 mb-4">
|
||||||
|
|
|
@ -243,18 +243,6 @@ def test_export_metrics(user3: UserClientFlask):
|
||||||
assert body == ''
|
assert body == ''
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.mvp
|
|
||||||
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
|
||||||
def test_export_links(user3: UserClientFlask):
|
|
||||||
snap = create_device(user3, 'real-eee-1001pxd.snapshot.12.json')
|
|
||||||
uri = "/inventory/export/links/?ids={id}".format(id=snap.device.devicehub_id)
|
|
||||||
|
|
||||||
body, status = user3.get(uri)
|
|
||||||
assert status == '200 OK'
|
|
||||||
body = body.split("\n")
|
|
||||||
assert ['links', 'http://localhost/devices/O48N2', ''] == body
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.mvp
|
@pytest.mark.mvp
|
||||||
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
||||||
def test_export_certificates(user3: UserClientFlask):
|
def test_export_certificates(user3: UserClientFlask):
|
||||||
|
@ -668,7 +656,7 @@ def test_action_allocate_error_required(user3: UserClientFlask):
|
||||||
body, status = user3.post(uri, data=data)
|
body, status = user3.post(uri, data=data)
|
||||||
assert status == '200 OK'
|
assert status == '200 OK'
|
||||||
assert 'Action Allocate error' in body
|
assert 'Action Allocate error' in body
|
||||||
assert 'You need to specify a number of users!' in body
|
assert 'Not a valid date value.' in body
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.mvp
|
@pytest.mark.mvp
|
||||||
|
|
Reference in a new issue