Merge branch 'feature/server-side-render-actions' into feature/server-side-render-actions-documents
This commit is contained in:
commit
579e0e35f0
|
@ -1,24 +1,31 @@
|
||||||
import json
|
import json
|
||||||
from flask_wtf import FlaskForm
|
|
||||||
from wtforms import StringField, validators, MultipleFileField, FloatField, IntegerField, \
|
|
||||||
HiddenField, DateField, TextAreaField, SelectField
|
|
||||||
from flask import g, request
|
|
||||||
from sqlalchemy.util import OrderedSet
|
|
||||||
from json.decoder import JSONDecodeError
|
from json.decoder import JSONDecodeError
|
||||||
|
|
||||||
|
from flask import g, request
|
||||||
|
from flask_wtf import FlaskForm
|
||||||
|
from sqlalchemy.util import OrderedSet
|
||||||
|
from wtforms import (DateField, FloatField, HiddenField, IntegerField,
|
||||||
|
MultipleFileField, SelectField, StringField,
|
||||||
|
TextAreaField, validators)
|
||||||
|
|
||||||
from ereuse_devicehub.db import db
|
from ereuse_devicehub.db import db
|
||||||
from ereuse_devicehub.resources.device.models import Device, Computer, Smartphone, Cellphone, \
|
from ereuse_devicehub.resources.action.models import (Action, RateComputer,
|
||||||
Tablet, Monitor, Mouse, Keyboard, \
|
Snapshot, VisualTest)
|
||||||
MemoryCardReader, SAI
|
from ereuse_devicehub.resources.action.rate.v1_0 import CannotRate
|
||||||
from ereuse_devicehub.resources.action.models import Action, RateComputer, Snapshot, VisualTest
|
from ereuse_devicehub.resources.action.schemas import \
|
||||||
from ereuse_devicehub.resources.action.schemas import Snapshot as SnapshotSchema
|
Snapshot as SnapshotSchema
|
||||||
|
from ereuse_devicehub.resources.action.views.snapshot import (move_json,
|
||||||
|
save_json)
|
||||||
|
from ereuse_devicehub.resources.device.models import (SAI, Cellphone, Computer,
|
||||||
|
Device, Keyboard,
|
||||||
|
MemoryCardReader,
|
||||||
|
Monitor, Mouse,
|
||||||
|
Smartphone, Tablet)
|
||||||
|
from ereuse_devicehub.resources.device.sync import Sync
|
||||||
|
from ereuse_devicehub.resources.enums import Severity, SnapshotSoftware
|
||||||
from ereuse_devicehub.resources.lot.models import Lot
|
from ereuse_devicehub.resources.lot.models import Lot
|
||||||
from ereuse_devicehub.resources.tag.model import Tag
|
from ereuse_devicehub.resources.tag.model import Tag
|
||||||
from ereuse_devicehub.resources.enums import SnapshotSoftware, Severity
|
|
||||||
from ereuse_devicehub.resources.user.exceptions import InsufficientPermission
|
from ereuse_devicehub.resources.user.exceptions import InsufficientPermission
|
||||||
from ereuse_devicehub.resources.action.rate.v1_0 import CannotRate
|
|
||||||
from ereuse_devicehub.resources.device.sync import Sync
|
|
||||||
from ereuse_devicehub.resources.action.views.snapshot import save_json, move_json
|
|
||||||
|
|
||||||
|
|
||||||
class LotDeviceForm(FlaskForm):
|
class LotDeviceForm(FlaskForm):
|
||||||
|
@ -470,6 +477,7 @@ class NewActionForm(FlaskForm):
|
||||||
date = DateField(u'Date', validators=(validators.Optional(),))
|
date = DateField(u'Date', validators=(validators.Optional(),))
|
||||||
severity = SelectField(u'Severity', choices=[(v.name, v.name) for v in Severity])
|
severity = SelectField(u'Severity', choices=[(v.name, v.name) for v in Severity])
|
||||||
description = TextAreaField(u'Description')
|
description = TextAreaField(u'Description')
|
||||||
|
lot = HiddenField()
|
||||||
type = HiddenField()
|
type = HiddenField()
|
||||||
|
|
||||||
def validate(self, extra_validators=None):
|
def validate(self, extra_validators=None):
|
||||||
|
|
|
@ -1,14 +1,18 @@
|
||||||
import flask
|
|
||||||
import datetime
|
import datetime
|
||||||
from flask.views import View
|
|
||||||
from flask import Blueprint, url_for, request
|
|
||||||
from flask_login import login_required, current_user
|
|
||||||
|
|
||||||
|
import flask
|
||||||
|
from flask import Blueprint, request, url_for
|
||||||
|
from flask.views import View
|
||||||
|
from flask_login import current_user, login_required
|
||||||
|
|
||||||
|
from ereuse_devicehub.inventory.forms import (AllocateForm, LotDeviceForm,
|
||||||
|
LotForm, NewActionForm,
|
||||||
|
NewDeviceForm, TagDeviceForm,
|
||||||
|
TagForm, TagUnnamedForm,
|
||||||
|
UploadSnapshotForm)
|
||||||
|
from ereuse_devicehub.resources.device.models import Device
|
||||||
from ereuse_devicehub.resources.lot.models import Lot
|
from ereuse_devicehub.resources.lot.models import Lot
|
||||||
from ereuse_devicehub.resources.tag.model import Tag
|
from ereuse_devicehub.resources.tag.model import Tag
|
||||||
from ereuse_devicehub.resources.device.models import Device
|
|
||||||
from ereuse_devicehub.inventory.forms import LotDeviceForm, LotForm, UploadSnapshotForm, \
|
|
||||||
NewDeviceForm, TagForm, TagUnnamedForm, TagDeviceForm, NewActionForm, AllocateForm
|
|
||||||
|
|
||||||
# TODO(@slamora): rename base 'inventory.devices' --> 'inventory'
|
# TODO(@slamora): rename base 'inventory.devices' --> 'inventory'
|
||||||
devices = Blueprint('inventory.devices', __name__, url_prefix='/inventory')
|
devices = Blueprint('inventory.devices', __name__, url_prefix='/inventory')
|
||||||
|
@ -41,6 +45,11 @@ class DeviceListMix(View):
|
||||||
form_new_action = NewActionForm()
|
form_new_action = NewActionForm()
|
||||||
form_new_allocate = AllocateForm()
|
form_new_allocate = AllocateForm()
|
||||||
|
|
||||||
|
action_devices = form_new_action.devices.data
|
||||||
|
list_devices = []
|
||||||
|
if action_devices:
|
||||||
|
list_devices.extend([int(x) for x in action_devices.split(",")])
|
||||||
|
|
||||||
self.context = {
|
self.context = {
|
||||||
'devices': devices,
|
'devices': devices,
|
||||||
'lots': lots,
|
'lots': lots,
|
||||||
|
@ -49,7 +58,8 @@ class DeviceListMix(View):
|
||||||
'form_new_action': form_new_action,
|
'form_new_action': form_new_action,
|
||||||
'form_new_allocate': form_new_allocate,
|
'form_new_allocate': form_new_allocate,
|
||||||
'lot': lot,
|
'lot': lot,
|
||||||
'tags': tags
|
'tags': tags,
|
||||||
|
'list_devices': list_devices
|
||||||
}
|
}
|
||||||
|
|
||||||
return self.context
|
return self.context
|
||||||
|
@ -285,9 +295,14 @@ class NewActionView(View):
|
||||||
|
|
||||||
def dispatch_request(self):
|
def dispatch_request(self):
|
||||||
self.form = self._form()
|
self.form = self._form()
|
||||||
|
|
||||||
|
next_url = url_for('inventory.devices.devicelist')
|
||||||
|
lot_id = self.form.lot.data
|
||||||
|
if lot_id:
|
||||||
|
next_url = url_for('inventory.devices.lotdevicelist', lot_id=lot_id)
|
||||||
|
|
||||||
if self.form.validate_on_submit():
|
if self.form.validate_on_submit():
|
||||||
self.form.save()
|
self.form.save()
|
||||||
next_url = request.referrer or url_for('inventory.devices.devicelist')
|
|
||||||
return flask.redirect(next_url)
|
return flask.redirect(next_url)
|
||||||
|
|
||||||
|
|
||||||
|
@ -296,11 +311,13 @@ class NewAllocateView(NewActionView, DeviceListMix):
|
||||||
_form = AllocateForm
|
_form = AllocateForm
|
||||||
|
|
||||||
def dispatch_request(self, lot_id=None):
|
def dispatch_request(self, lot_id=None):
|
||||||
|
self.form = self._form()
|
||||||
|
|
||||||
next_url = url_for('inventory.devices.devicelist')
|
next_url = url_for('inventory.devices.devicelist')
|
||||||
|
lot_id = self.form.lot.data
|
||||||
if lot_id:
|
if lot_id:
|
||||||
next_url = url_for('inventory.devices.lotdevicelist', lot_id=lot_id)
|
next_url = url_for('inventory.devices.lotdevicelist', lot_id=lot_id)
|
||||||
|
|
||||||
self.form = self._form()
|
|
||||||
if self.form.validate_on_submit():
|
if self.form.validate_on_submit():
|
||||||
self.form.save()
|
self.form.save()
|
||||||
return flask.redirect(next_url)
|
return flask.redirect(next_url)
|
||||||
|
@ -312,8 +329,6 @@ class NewAllocateView(NewActionView, DeviceListMix):
|
||||||
|
|
||||||
devices.add_url_rule('/action/add/', view_func=NewActionView.as_view('action_add'))
|
devices.add_url_rule('/action/add/', view_func=NewActionView.as_view('action_add'))
|
||||||
devices.add_url_rule('/action/allocate/add/', view_func=NewAllocateView.as_view('allocate_add'))
|
devices.add_url_rule('/action/allocate/add/', view_func=NewAllocateView.as_view('allocate_add'))
|
||||||
devices.add_url_rule('/lot/<string:lot_id>/action/allocate/add/',
|
|
||||||
view_func=NewAllocateView.as_view('lot_allocate_add'))
|
|
||||||
devices.add_url_rule('/device/', view_func=DeviceListView.as_view('devicelist'))
|
devices.add_url_rule('/device/', view_func=DeviceListView.as_view('devicelist'))
|
||||||
devices.add_url_rule('/device/<string:id>/', view_func=DeviceDetailView.as_view('device_details'))
|
devices.add_url_rule('/device/<string:id>/', view_func=DeviceDetailView.as_view('device_details'))
|
||||||
devices.add_url_rule('/lot/<string:lot_id>/device/', view_func=DeviceListView.as_view('lotdevicelist'))
|
devices.add_url_rule('/lot/<string:lot_id>/device/', view_func=DeviceListView.as_view('lotdevicelist'))
|
||||||
|
|
|
@ -10,34 +10,37 @@ $(document).ready(function() {
|
||||||
})
|
})
|
||||||
|
|
||||||
function deviceSelect() {
|
function deviceSelect() {
|
||||||
var devices = $(".deviceSelect").filter(':checked');
|
var devices_count = $(".deviceSelect").filter(':checked').length;
|
||||||
var devices_id = $.map(devices, function(x) { return $(x).attr('data')}).join(",");
|
if (devices_count == 0) {
|
||||||
if (devices_id == "") {
|
$("#addingLotModal .pol").show();
|
||||||
$("#addingLotModal .text-danger").show();
|
|
||||||
$("#addingLotModal .btn-primary").hide();
|
$("#addingLotModal .btn-primary").hide();
|
||||||
|
|
||||||
$("#removeLotModal .text-danger").show();
|
$("#removeLotModal .pol").show();
|
||||||
$("#removeLotModal .btn-primary").hide();
|
$("#removeLotModal .btn-primary").hide();
|
||||||
$("#addingTagModal .text-danger").show();
|
|
||||||
|
$("#addingTagModal .pol").show();
|
||||||
$("#addingTagModal .btn-primary").hide();
|
$("#addingTagModal .btn-primary").hide();
|
||||||
|
|
||||||
|
$("#actionModal .pol").show();
|
||||||
|
$("#actionModal .btn-primary").hide();
|
||||||
|
|
||||||
|
$("#allocateModal .pol").show();
|
||||||
|
$("#allocateModal .btn-primary").hide();
|
||||||
} else {
|
} else {
|
||||||
$("#addingLotModal .text-danger").hide();
|
$("#addingLotModal .pol").hide();
|
||||||
$("#addingLotModal .btn-primary").show();
|
$("#addingLotModal .btn-primary").show();
|
||||||
|
|
||||||
$("#removeLotModal .text-danger").hide();
|
$("#removeLotModal .pol").hide();
|
||||||
$("#removeLotModal .btn-primary").show();
|
$("#removeLotModal .btn-primary").show();
|
||||||
|
|
||||||
$("#actionModal .text-danger").hide();
|
$("#actionModal .pol").hide();
|
||||||
$("#actionModal .btn-primary").show();
|
$("#actionModal .btn-primary").show();
|
||||||
|
|
||||||
$("#allocateModal .text-danger").hide();
|
$("#allocateModal .pol").hide();
|
||||||
$("#allocateModal .btn-primary").show();
|
$("#allocateModal .btn-primary").show();
|
||||||
|
|
||||||
$("#addingTagModal .text-danger").hide();
|
$("#addingTagModal .pol").hide();
|
||||||
}
|
}
|
||||||
$.map($(".devicesList"), function(x) {
|
|
||||||
$(x).val(devices_id);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeTag() {
|
function removeTag() {
|
||||||
|
@ -52,10 +55,49 @@ function removeTag() {
|
||||||
|
|
||||||
function newAction(action) {
|
function newAction(action) {
|
||||||
$("#actionModal #type").val(action);
|
$("#actionModal #type").val(action);
|
||||||
|
$("#actionModal #title-action").html(action);
|
||||||
|
get_device_list();
|
||||||
|
deviceSelect();
|
||||||
$("#activeActionModal").click();
|
$("#activeActionModal").click();
|
||||||
}
|
}
|
||||||
|
|
||||||
function newAllocate(action) {
|
function newAllocate(action) {
|
||||||
$("#allocateModal #type").val(action);
|
$("#allocateModal #type").val(action);
|
||||||
|
$("#allocateModal #title-action").html(action);
|
||||||
|
get_device_list();
|
||||||
|
deviceSelect();
|
||||||
$("#activeAllocateModal").click();
|
$("#activeAllocateModal").click();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function get_device_list() {
|
||||||
|
var devices = $(".deviceSelect").filter(':checked');
|
||||||
|
|
||||||
|
/* Insert the correct count of devices in actions form */
|
||||||
|
var devices_count = devices.length;
|
||||||
|
$("#allocateModal .devices-count").html(devices_count);
|
||||||
|
$("#actionModal .devices-count").html(devices_count);
|
||||||
|
|
||||||
|
/* Insert the correct value in the input devicesList */
|
||||||
|
var devices_id = $.map(devices, function(x) { return $(x).attr('data')}).join(",");
|
||||||
|
$.map($(".devicesList"), function(x) {
|
||||||
|
$(x).val(devices_id);
|
||||||
|
});
|
||||||
|
|
||||||
|
/* Create a list of devices for human representation */
|
||||||
|
var computer = {
|
||||||
|
"Desktop": "<i class='bi bi-building'></i>",
|
||||||
|
"Laptop": "<i class='bi bi-laptop'></i>",
|
||||||
|
};
|
||||||
|
list_devices = devices.map(function (x) {
|
||||||
|
var typ = $(devices[x]).data("device-type");
|
||||||
|
var manuf = $(devices[x]).data("device-manufacturer");
|
||||||
|
var dhid = $(devices[x]).data("device-dhid");
|
||||||
|
if (computer[typ]) {
|
||||||
|
typ = computer[typ];
|
||||||
|
};
|
||||||
|
return typ + " " + manuf + " " + dhid;
|
||||||
|
});
|
||||||
|
|
||||||
|
description = $.map(list_devices, function(x) { return x }).join(", ");
|
||||||
|
$(".enumeration-devices").html(description);
|
||||||
|
}
|
||||||
|
|
|
@ -1,24 +1,30 @@
|
||||||
<div class="modal fade" id="actionModal" tabindex="-1" style="display: none;" aria-hidden="true">
|
<div class="modal fade" id="actionModal" tabindex="-1" style="display: none;" aria-hidden="true">
|
||||||
<div class="modal-dialog modal-fullscreen">
|
<div class="modal-dialog modal-lg">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
|
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<h5 class="modal-title">New Action</h5>
|
<h5 class="modal-title">New Action <span id="title-action"></span></h5>
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form action="{{ url_for('inventory.devices.action_add') }}" method="post">
|
<form action="{{ url_for('inventory.devices.action_add') }}" method="post">
|
||||||
{{ form_new_action.csrf_token }}
|
{{ form_new_action.csrf_token }}
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<p class="text-danger">
|
|
||||||
You need select first some device before to do one action
|
|
||||||
</p>
|
|
||||||
{% for field in form_new_action %}
|
{% for field in form_new_action %}
|
||||||
{% if field != form_new_action.csrf_token %}
|
{% if field != form_new_action.csrf_token %}
|
||||||
{% if field == form_new_action.devices %}
|
{% if field == form_new_action.devices %}
|
||||||
{{ field(class_="devicesList") }}
|
<div class="col-12">
|
||||||
|
{{ field.label(class_="form-label") }}: <span class="devices-count"></span>
|
||||||
|
{{ field(class_="devicesList") }}
|
||||||
|
<p class="text-danger pol">
|
||||||
|
You need select first some device before to do one action
|
||||||
|
</p>
|
||||||
|
<p class="enumeration-devices"></p>
|
||||||
|
</div>
|
||||||
|
{% elif field == form_new_action.lot %}
|
||||||
|
{{ field }}
|
||||||
{% elif field == form_new_action.type %}
|
{% elif field == form_new_action.type %}
|
||||||
{{ field(class_="form-control") }}
|
{{ field }}
|
||||||
{% else %}
|
{% else %}
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
{{ field.label(class_="form-label") }}
|
{{ field.label(class_="form-label") }}
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
<input class="devicesList" type="hidden" name="devices" />
|
<input class="devicesList" type="hidden" name="devices" />
|
||||||
<p class="text-danger">
|
<p class="text-danger pol">
|
||||||
You need select first some device for adding this in a lot
|
You need select first some device for adding this in a lot
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
<input class="devicesList" type="hidden" name="device" />
|
<input class="devicesList" type="hidden" name="device" />
|
||||||
<p class="text-danger">
|
<p class="text-danger pol">
|
||||||
You need select first some device for adding this in a tag
|
You need select first some device for adding this in a tag
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,30 +1,31 @@
|
||||||
<div class="modal fade" id="allocateModal" tabindex="-1" style="display: none;" aria-hidden="true"
|
<div class="modal fade" id="allocateModal" tabindex="-1" style="display: none;" aria-hidden="true"
|
||||||
data-show-action-form="{{ form_new_allocate.type.data }}">
|
data-show-action-form="{{ form_new_allocate.type.data }}">
|
||||||
<div class="modal-dialog modal-fullscreen">
|
<div class="modal-dialog modal-lg">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
|
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<h5 class="modal-title">New Action Allocate/Deallocate</h5>
|
<h5 class="modal-title">New Action <span id="title-action"></span></h5>
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form action="
|
<form action="{{ url_for('inventory.devices.allocate_add') }}" method="post">
|
||||||
{% if lot %}
|
|
||||||
{{ url_for('inventory.devices.lot_allocate_add', lot_id=lot.id) }}
|
|
||||||
{% else %}
|
|
||||||
{{ url_for('inventory.devices.allocate_add') }}
|
|
||||||
{% endif %}" method="post">
|
|
||||||
{{ form_new_allocate.csrf_token }}
|
{{ form_new_allocate.csrf_token }}
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<p class="text-danger" id="pol" style="display: none;">
|
|
||||||
You need select first some device before to do one action
|
|
||||||
</p>
|
|
||||||
{% for field in form_new_allocate %}
|
{% for field in form_new_allocate %}
|
||||||
{% if field != form_new_allocate.csrf_token %}
|
{% if field != form_new_allocate.csrf_token %}
|
||||||
{% if field == form_new_allocate.devices %}
|
{% if field == form_new_allocate.devices %}
|
||||||
{{ field(class_="devicesList") }}
|
<div class="col-12">
|
||||||
|
{{ field.label(class_="form-label") }}: <span class="devices-count"></span>
|
||||||
|
{{ field(class_="devicesList") }}
|
||||||
|
<p class="text-danger pol" style="display: none;">
|
||||||
|
You need select first some device before to do one action
|
||||||
|
</p>
|
||||||
|
<p class="enumeration-devices"></p>
|
||||||
|
</div>
|
||||||
|
{% elif field == form_new_allocate.lot %}
|
||||||
|
{{ field }}
|
||||||
{% elif field == form_new_allocate.type %}
|
{% elif field == form_new_allocate.type %}
|
||||||
{{ field(class_="form-control") }}
|
{{ field }}
|
||||||
{% else %}
|
{% else %}
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
{{ field.label(class_="form-label") }}
|
{{ field.label(class_="form-label") }}
|
||||||
|
|
|
@ -139,7 +139,7 @@
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="javascript:newAction('DataWipe')" class="dropdown-item">
|
<a href="javascript:void()" class="dropdown-item">
|
||||||
<i class="bi bi-eraser-fill"></i>
|
<i class="bi bi-eraser-fill"></i>
|
||||||
DataWipe
|
DataWipe
|
||||||
</a>
|
</a>
|
||||||
|
@ -234,7 +234,15 @@
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for dev in devices %}
|
{% for dev in devices %}
|
||||||
<tr>
|
<tr>
|
||||||
<td><input type="checkbox" class="deviceSelect" data="{{ dev.id }}"/></td>
|
<td>
|
||||||
|
<input type="checkbox" class="deviceSelect" data="{{ dev.id }}"
|
||||||
|
data-device-type="{{ dev.type }}" data-device-manufacturer="{{ dev.manufacturer }}"
|
||||||
|
data-device-dhid="{{ dev.devicehub_id }}"
|
||||||
|
{% if form_new_allocate.type.data and dev.id in list_devices %}
|
||||||
|
checked="checked"
|
||||||
|
{% endif %}
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<a href="{{ url_for('inventory.devices.device_details', id=dev.devicehub_id)}}">
|
<a href="{{ url_for('inventory.devices.device_details', id=dev.devicehub_id)}}">
|
||||||
{{ dev.type }} {{ dev.manufacturer }} {{ dev.model }}
|
{{ dev.type }} {{ dev.manufacturer }} {{ dev.model }}
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
<input class="devicesList" type="hidden" name="devices" />
|
<input class="devicesList" type="hidden" name="devices" />
|
||||||
<p class="text-danger">
|
<p class="text-danger pol">
|
||||||
You need select first some device for remove this from a lot
|
You need select first some device for remove this from a lot
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
Reference in a new issue