Merge pull request #196 from eReuse/feature/server-side-render-actions
Feature: server side render actions
This commit is contained in:
commit
0eec5b549f
|
@ -1,24 +1,31 @@
|
|||
import json
|
||||
from flask_wtf import FlaskForm
|
||||
from wtforms import StringField, validators, MultipleFileField, FloatField, IntegerField, \
|
||||
SelectField
|
||||
from flask import g, request
|
||||
from sqlalchemy.util import OrderedSet
|
||||
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.resources.device.models import Device, Computer, Smartphone, Cellphone, \
|
||||
Tablet, Monitor, Mouse, Keyboard, \
|
||||
MemoryCardReader, SAI
|
||||
from ereuse_devicehub.resources.action.models import RateComputer, Snapshot, VisualTest
|
||||
from ereuse_devicehub.resources.action.schemas import Snapshot as SnapshotSchema
|
||||
from ereuse_devicehub.resources.action.models import (Action, RateComputer,
|
||||
Snapshot, VisualTest)
|
||||
from ereuse_devicehub.resources.action.rate.v1_0 import CannotRate
|
||||
from ereuse_devicehub.resources.action.schemas import \
|
||||
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.tag.model import Tag
|
||||
from ereuse_devicehub.resources.enums import SnapshotSoftware, Severity
|
||||
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):
|
||||
|
@ -418,7 +425,6 @@ class TagDeviceForm(FlaskForm):
|
|||
self.delete = kwargs.pop('delete', None)
|
||||
self.device_id = kwargs.pop('device', None)
|
||||
|
||||
# import pdb; pdb.set_trace()
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
if self.delete:
|
||||
|
@ -466,7 +472,68 @@ class TagDeviceForm(FlaskForm):
|
|||
|
||||
|
||||
class NewActionForm(FlaskForm):
|
||||
name = StringField(u'Name')
|
||||
date = StringField(u'Date')
|
||||
severity = StringField(u'Severity')
|
||||
description = StringField(u'Description')
|
||||
name = StringField(u'Name', [validators.length(max=50)])
|
||||
devices = HiddenField()
|
||||
date = DateField(u'Date', validators=(validators.Optional(),))
|
||||
severity = SelectField(u'Severity', choices=[(v.name, v.name) for v in Severity])
|
||||
description = TextAreaField(u'Description')
|
||||
lot = HiddenField()
|
||||
type = HiddenField()
|
||||
|
||||
def validate(self, extra_validators=None):
|
||||
is_valid = super().validate(extra_validators)
|
||||
|
||||
if not is_valid:
|
||||
return False
|
||||
|
||||
if self.devices.data:
|
||||
devices = set(self.devices.data.split(","))
|
||||
self._devices = OrderedSet(Device.query.filter(Device.id.in_(devices)).filter(
|
||||
Device.owner_id == g.user.id).all())
|
||||
|
||||
if not self._devices:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def save(self):
|
||||
Model = db.Model._decl_class_registry.data[self.type.data]()
|
||||
self.instance = Model()
|
||||
devices = self.devices.data
|
||||
severity = self.severity.data
|
||||
self.devices.data = self._devices
|
||||
self.severity.data = Severity[self.severity.data]
|
||||
|
||||
self.populate_obj(self.instance)
|
||||
db.session.add(self.instance)
|
||||
db.session.commit()
|
||||
|
||||
self.devices.data = devices
|
||||
self.severity.data = severity
|
||||
|
||||
return self.instance
|
||||
|
||||
|
||||
class AllocateForm(NewActionForm):
|
||||
start_time = DateField(u'Start time')
|
||||
end_time = DateField(u'End time')
|
||||
final_user_code = StringField(u'Final user code', [validators.length(max=50)])
|
||||
transaction = StringField(u'Transaction', [validators.length(max=50)])
|
||||
end_users = IntegerField(u'End users')
|
||||
|
||||
def validate(self, extra_validators=None):
|
||||
is_valid = super().validate(extra_validators)
|
||||
|
||||
start_time = self.start_time.data
|
||||
end_time = self.end_time.data
|
||||
if start_time and end_time and end_time < start_time:
|
||||
error = ['The action cannot finish before it starts.']
|
||||
self.start_time.errors = error
|
||||
self.end_time.errors = error
|
||||
is_valid = False
|
||||
|
||||
if not self.end_users.data:
|
||||
self.end_users.errors = ["You need to specify a number of users"]
|
||||
is_valid = False
|
||||
|
||||
return is_valid
|
||||
|
|
|
@ -1,23 +1,27 @@
|
|||
import flask
|
||||
from flask import Blueprint, request, url_for
|
||||
from flask.views import View
|
||||
from flask import Blueprint, url_for, request
|
||||
from flask_login import login_required, current_user
|
||||
from flask_login import current_user, login_required
|
||||
|
||||
from ereuse_devicehub import messages
|
||||
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.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
|
||||
|
||||
# TODO(@slamora): rename base 'inventory.devices' --> 'inventory'
|
||||
devices = Blueprint('inventory.devices', __name__, url_prefix='/inventory')
|
||||
|
||||
|
||||
class DeviceListView(View):
|
||||
class DeviceListMix(View):
|
||||
decorators = [login_required]
|
||||
template_name = 'inventory/device_list.html'
|
||||
|
||||
def dispatch_request(self, lot_id=None):
|
||||
def get_context(self, lot_id):
|
||||
# TODO @cayop adding filter
|
||||
# https://github.com/eReuse/devicehub-teal/blob/testing/ereuse_devicehub/resources/device/views.py#L56
|
||||
filter_types = ['Desktop', 'Laptop', 'Server']
|
||||
|
@ -30,19 +34,41 @@ class DeviceListView(View):
|
|||
lot = lots.filter(Lot.id == lot_id).one()
|
||||
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_allocate = AllocateForm(lot=lot.id)
|
||||
else:
|
||||
devices = Device.query.filter(
|
||||
Device.owner_id == current_user.id).filter(
|
||||
Device.type.in_(filter_types)).filter(Device.lots == None).order_by(
|
||||
Device.updated.desc())
|
||||
form_new_action = NewActionForm()
|
||||
form_new_allocate = AllocateForm()
|
||||
|
||||
context = {'devices': devices,
|
||||
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 = {
|
||||
'devices': devices,
|
||||
'lots': lots,
|
||||
'form_lot_device': LotDeviceForm(),
|
||||
'form_tag_device': TagDeviceForm(),
|
||||
'form_new_action': form_new_action,
|
||||
'form_new_allocate': form_new_allocate,
|
||||
'lot': lot,
|
||||
'tags': tags}
|
||||
return flask.render_template(self.template_name, **context)
|
||||
'tags': tags,
|
||||
'list_devices': list_devices
|
||||
}
|
||||
|
||||
return self.context
|
||||
|
||||
|
||||
class DeviceListView(DeviceListMix):
|
||||
|
||||
def dispatch_request(self, lot_id=None):
|
||||
self.get_context(lot_id)
|
||||
return flask.render_template(self.template_name, **self.context)
|
||||
|
||||
|
||||
class DeviceDetailView(View):
|
||||
|
@ -261,6 +287,52 @@ class TagUnlinkDeviceView(View):
|
|||
return flask.render_template(self.template_name, form=form, referrer=request.referrer)
|
||||
|
||||
|
||||
class NewActionView(View):
|
||||
methods = ['POST']
|
||||
decorators = [login_required]
|
||||
form_class = NewActionForm
|
||||
|
||||
def dispatch_request(self):
|
||||
self.form = self.form_class()
|
||||
|
||||
if self.form.validate_on_submit():
|
||||
instance = self.form.save()
|
||||
messages.success('Action "{}" created successfully!'.format(instance.type))
|
||||
|
||||
next_url = self.get_next_url()
|
||||
return flask.redirect(next_url)
|
||||
|
||||
def get_next_url(self):
|
||||
lot_id = self.form.lot.data
|
||||
|
||||
if lot_id:
|
||||
return url_for('inventory.devices.lotdevicelist', lot_id=lot_id)
|
||||
|
||||
return url_for('inventory.devices.devicelist')
|
||||
|
||||
|
||||
class NewAllocateView(NewActionView, DeviceListMix):
|
||||
methods = ['POST']
|
||||
form_class = AllocateForm
|
||||
|
||||
def dispatch_request(self):
|
||||
self.form = self.form_class()
|
||||
|
||||
if self.form.validate_on_submit():
|
||||
instance = self.form.save()
|
||||
messages.success('Action "{}" created successfully!'.format(instance.type))
|
||||
|
||||
next_url = self.get_next_url()
|
||||
return flask.redirect(next_url)
|
||||
|
||||
lot_id = self.form.lot.data
|
||||
self.get_context(lot_id)
|
||||
self.context['form_new_allocate'] = self.form
|
||||
return flask.render_template(self.template_name, **self.context)
|
||||
|
||||
|
||||
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('/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('/lot/<string:lot_id>/device/', view_func=DeviceListView.as_view('lotdevicelist'))
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
from flask import flash, session
|
||||
|
||||
DEBUG = 10
|
||||
INFO = 20
|
||||
SUCCESS = 25
|
||||
WARNING = 30
|
||||
ERROR = 40
|
||||
|
||||
DEFAULT_LEVELS = {
|
||||
'DEBUG': DEBUG,
|
||||
'INFO': INFO,
|
||||
'SUCCESS': SUCCESS,
|
||||
'WARNING': WARNING,
|
||||
'ERROR': ERROR,
|
||||
}
|
||||
|
||||
DEFAULT_TAGS = {
|
||||
DEBUG: 'light',
|
||||
INFO: 'info',
|
||||
SUCCESS: 'success',
|
||||
WARNING: 'warning',
|
||||
ERROR: 'danger',
|
||||
}
|
||||
|
||||
DEFAULT_ICONS = {
|
||||
DEFAULT_TAGS[DEBUG]: 'tools',
|
||||
DEFAULT_TAGS[INFO]: 'info-circle',
|
||||
DEFAULT_TAGS[SUCCESS]: 'check-circle',
|
||||
DEFAULT_TAGS[WARNING]: 'exclamation-triangle',
|
||||
DEFAULT_TAGS[ERROR]: 'exclamation-octagon',
|
||||
}
|
||||
|
||||
|
||||
def add_message(level, message):
|
||||
level_tag = DEFAULT_TAGS[level]
|
||||
if '_message_icon' not in session:
|
||||
session['_message_icon'] = DEFAULT_ICONS
|
||||
|
||||
flash(message, level_tag)
|
||||
|
||||
|
||||
def debug(message):
|
||||
"""Add a message with the ``DEBUG`` level."""
|
||||
add_message(DEBUG, message)
|
||||
|
||||
|
||||
def info(message):
|
||||
"""Add a message with the ``INFO`` level."""
|
||||
add_message(INFO, message)
|
||||
|
||||
|
||||
def success(message):
|
||||
"""Add a message with the ``SUCCESS`` level."""
|
||||
add_message(SUCCESS, message)
|
||||
|
||||
|
||||
def warning(message):
|
||||
"""Add a message with the ``WARNING`` level."""
|
||||
add_message(WARNING, message)
|
||||
|
||||
|
||||
def error(message):
|
||||
"""Add a message with the ``ERROR`` level."""
|
||||
add_message(ERROR, message)
|
|
@ -213,18 +213,6 @@ class Action(Thing):
|
|||
args[INHERIT_COND] = cls.id == Action.id
|
||||
return args
|
||||
|
||||
@validates('end_time')
|
||||
def validate_end_time(self, _, end_time: datetime):
|
||||
if self.start_time and end_time <= self.start_time:
|
||||
raise ValidationError('The action cannot finish before it starts.')
|
||||
return end_time
|
||||
|
||||
@validates('start_time')
|
||||
def validate_start_time(self, _, start_time: datetime):
|
||||
if self.end_time and start_time >= self.end_time:
|
||||
raise ValidationError('The action cannot start after it finished.')
|
||||
return start_time
|
||||
|
||||
@property
|
||||
def date_str(self):
|
||||
return '{:%c}'.format(self.end_time)
|
||||
|
|
|
@ -55,6 +55,10 @@ class Action(Thing):
|
|||
if 'start_time' in data and data['start_time'] < unix_time:
|
||||
data['start_time'] = unix_time
|
||||
|
||||
if data.get('end_time') and data.get('start_time'):
|
||||
if data['start_time'] > data['end_time']:
|
||||
raise ValidationError('The action cannot finish before it starts.')
|
||||
|
||||
|
||||
class ActionWithOneDevice(Action):
|
||||
__doc__ = m.ActionWithOneDevice.__doc__
|
||||
|
|
|
@ -164,6 +164,10 @@ class Device(Thing):
|
|||
super().__init__(**kw)
|
||||
self.set_hid()
|
||||
|
||||
@property
|
||||
def reverse_actions(self) -> list:
|
||||
return reversed(self.actions)
|
||||
|
||||
@property
|
||||
def actions(self) -> list:
|
||||
"""All the actions where the device participated, including:
|
||||
|
|
|
@ -1,31 +1,46 @@
|
|||
$(document).ready(function() {
|
||||
var show_action_form = $("#allocateModal").data('show-action-form');
|
||||
if (show_action_form != "None") {
|
||||
$("#allocateModal .btn-primary").show();
|
||||
newAllocate(show_action_form);
|
||||
} else {
|
||||
$(".deviceSelect").on("change", deviceSelect);
|
||||
}
|
||||
// $('#selectLot').selectpicker();
|
||||
})
|
||||
|
||||
function deviceSelect() {
|
||||
var devices = $(".deviceSelect").filter(':checked');
|
||||
var devices_id = $.map(devices, function(x) { return $(x).attr('data')}).join(",");
|
||||
if (devices_id == "") {
|
||||
$("#addingLotModal .text-danger").show();
|
||||
var devices_count = $(".deviceSelect").filter(':checked').length;
|
||||
if (devices_count == 0) {
|
||||
$("#addingLotModal .pol").show();
|
||||
$("#addingLotModal .btn-primary").hide();
|
||||
$("#removeLotModal .text-danger").show();
|
||||
|
||||
$("#removeLotModal .pol").show();
|
||||
$("#removeLotModal .btn-primary").hide();
|
||||
$("#addingTagModal .text-danger").show();
|
||||
|
||||
$("#addingTagModal .pol").show();
|
||||
$("#addingTagModal .btn-primary").hide();
|
||||
|
||||
$("#actionModal .pol").show();
|
||||
$("#actionModal .btn-primary").hide();
|
||||
|
||||
$("#allocateModal .pol").show();
|
||||
$("#allocateModal .btn-primary").hide();
|
||||
} else {
|
||||
$("#addingLotModal .text-danger").hide();
|
||||
$("#addingLotModal .btn-primary").removeClass('d-none');
|
||||
$("#addingLotModal .pol").hide();
|
||||
$("#addingLotModal .btn-primary").show();
|
||||
$("#removeLotModal .text-danger").hide();
|
||||
$("#removeLotModal .btn-primary").removeClass('d-none');
|
||||
|
||||
$("#removeLotModal .pol").hide();
|
||||
$("#removeLotModal .btn-primary").show();
|
||||
$("#addingTagModal .text-danger").hide();
|
||||
$("#addingTagModal .btn-primary").removeClass('d-none');
|
||||
|
||||
$("#actionModal .pol").hide();
|
||||
$("#actionModal .btn-primary").show();
|
||||
|
||||
$("#allocateModal .pol").hide();
|
||||
$("#allocateModal .btn-primary").show();
|
||||
|
||||
$("#addingTagModal .pol").hide();
|
||||
}
|
||||
$.map($(".devicesList"), function(x) {
|
||||
$(x).val(devices_id);
|
||||
});
|
||||
}
|
||||
|
||||
function removeTag() {
|
||||
|
@ -39,5 +54,50 @@ function removeTag() {
|
|||
}
|
||||
|
||||
function newAction(action) {
|
||||
console.log(action);
|
||||
$("#actionModal #type").val(action);
|
||||
$("#actionModal #title-action").html(action);
|
||||
get_device_list();
|
||||
deviceSelect();
|
||||
$("#activeActionModal").click();
|
||||
}
|
||||
|
||||
function newAllocate(action) {
|
||||
$("#allocateModal #type").val(action);
|
||||
$("#allocateModal #title-action").html(action);
|
||||
get_device_list();
|
||||
deviceSelect();
|
||||
$("#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);
|
||||
}
|
||||
|
|
|
@ -175,6 +175,15 @@
|
|||
</aside><!-- End Sidebar-->
|
||||
|
||||
<main id="main" class="main">
|
||||
{% block messages %}
|
||||
{% for level, message in get_flashed_messages(with_categories=true) %}
|
||||
<div class="alert alert-{{ level}} alert-dismissible fade show" role="alert">
|
||||
<i class="bi bi-{{ session['_message_icon'][level]}} me-1"></i>
|
||||
{{ message }}
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endblock %}
|
||||
{% block main %}
|
||||
|
||||
{% endblock main %}
|
||||
|
|
|
@ -1,25 +1,51 @@
|
|||
<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-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>
|
||||
</div>
|
||||
|
||||
<form action="{{ url_for('inventory.devices.lot_devices_add') }}" method="post">
|
||||
{{ form_lot_device.csrf_token }}
|
||||
<form action="{{ url_for('inventory.devices.action_add') }}" method="post">
|
||||
{{ form_new_action.csrf_token }}
|
||||
<div class="modal-body">
|
||||
Please write a name of a lot
|
||||
<input class="devicesList" type="hidden" name="devices" />
|
||||
<p class="text-danger">
|
||||
{% for field in form_new_action %}
|
||||
{% if field != form_new_action.csrf_token %}
|
||||
{% if field == form_new_action.devices %}
|
||||
<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 %}
|
||||
{{ field }}
|
||||
{% else %}
|
||||
<div class="col-12">
|
||||
{{ field.label(class_="form-label") }}
|
||||
{{ field(class_="form-control") }}
|
||||
{% if field.errors %}
|
||||
<p class="text-danger">
|
||||
{% for error in field.errors %}
|
||||
{{ error }}<br/>
|
||||
{% endfor %}
|
||||
</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
||||
<input type="submit" class="btn btn-primary d-none" value="Create" />
|
||||
<input type="submit" class="btn btn-primary" style="display: none;" value="Create" />
|
||||
</div>
|
||||
</form>
|
||||
|
||||
|
|
|
@ -17,14 +17,14 @@
|
|||
{% endfor %}
|
||||
</select>
|
||||
<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
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
||||
<input type="submit" class="btn btn-primary d-none" value="Save changes" />
|
||||
<input type="submit" class="btn btn-primary" style="display: none;" value="Save changes" />
|
||||
</div>
|
||||
</form>
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
{% endfor %}
|
||||
</select>
|
||||
<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
|
||||
</p>
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
<div class="modal fade" id="allocateModal" tabindex="-1" style="display: none;" aria-hidden="true"
|
||||
data-show-action-form="{{ form_new_allocate.type.data }}">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content">
|
||||
|
||||
<div class="modal-header">
|
||||
<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>
|
||||
</div>
|
||||
|
||||
<form action="{{ url_for('inventory.devices.allocate_add') }}" method="post">
|
||||
{{ form_new_allocate.csrf_token }}
|
||||
<div class="modal-body">
|
||||
{% for field in form_new_allocate %}
|
||||
{% if field != form_new_allocate.csrf_token %}
|
||||
{% if field == form_new_allocate.devices %}
|
||||
<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 %}
|
||||
{{ field }}
|
||||
{% else %}
|
||||
<div class="col-12">
|
||||
{{ field.label(class_="form-label") }}
|
||||
{{ field(class_="form-control") }}
|
||||
{% if field.errors %}
|
||||
<p class="text-danger">
|
||||
{% for error in field.errors %}
|
||||
{{ error }}<br/>
|
||||
{% endfor %}
|
||||
</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
||||
<input type="submit" class="btn btn-primary" style="display: none;" value="Create" />
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -109,7 +109,7 @@
|
|||
<div class="tab-pane fade profile-overview" id="traceability">
|
||||
<h5 class="card-title">Traceability log Details</h5>
|
||||
<div class="list-group col-6">
|
||||
{% for action in device.actions %}
|
||||
{% for action in device.reverse_actions %}
|
||||
<div class="list-group-item d-flex justify-content-between align-items-center">
|
||||
{{ action.type }} {{ action.severity }}
|
||||
<small class="text-muted">{{ action.created.strftime('%H:%M %d-%m-%Y') }}</small>
|
||||
|
|
|
@ -40,7 +40,7 @@
|
|||
</div>
|
||||
{% endif %}
|
||||
<div class="card">
|
||||
<div class="card-body pt-3">
|
||||
<div class="card-body pt-3" style="min-height: 650px;">
|
||||
<!-- Bordered Tabs -->
|
||||
|
||||
<div class="btn-group dropdown ml-1">
|
||||
|
@ -57,7 +57,7 @@
|
|||
<span class="caret"></span>
|
||||
</button>
|
||||
{% endif %}
|
||||
<ul class="dropdown-menu" aria-labelledby="btnLots"">
|
||||
<ul class="dropdown-menu" aria-labelledby="btnLots">
|
||||
<li>
|
||||
<a href="javascript:void()" class="dropdown-item" data-bs-toggle="modal" data-bs-target="#addingLotModal">
|
||||
<i class="bi bi-plus"></i>
|
||||
|
@ -78,12 +78,14 @@
|
|||
<i class="bi bi-plus"></i>
|
||||
New Actions
|
||||
</button>
|
||||
<ul class="dropdown-menu" aria-labelledby="btnActions"">
|
||||
<span class="d-none" id="activeActionModal" data-bs-toggle="modal" data-bs-target="#actionModal"></span>
|
||||
<span class="d-none" id="activeAllocateModal" data-bs-toggle="modal" data-bs-target="#allocateModal"></span>
|
||||
<ul class="dropdown-menu" aria-labelledby="btnActions">
|
||||
<li>
|
||||
Status actions
|
||||
</li>
|
||||
<li>
|
||||
<a href="javascript:newAction('Recycling')" class="dropdown-item" data-bs-toggle="modal" data-bs-target="#actionModal">
|
||||
<a href="javascript:newAction('Recycling')" class="dropdown-item">
|
||||
<i class="bi bi-recycle"></i>
|
||||
Recycling
|
||||
</a>
|
||||
|
@ -110,13 +112,13 @@
|
|||
Allocation
|
||||
</li>
|
||||
<li>
|
||||
<a href="javascript:newAction('Allocate')" class="dropdown-item">
|
||||
<a href="javascript:newAllocate('Allocate')" class="dropdown-item">
|
||||
<i class="bi bi-house-fill"></i>
|
||||
Allocate
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="javascript:newAction('Deallocate')" class="dropdown-item">
|
||||
<a href="javascript:newAllocate('Deallocate')" class="dropdown-item">
|
||||
<i class="bi bi-house"></i>
|
||||
Deallocate
|
||||
</a>
|
||||
|
@ -137,7 +139,7 @@
|
|||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="javascript:newAction('DataWipe')" class="dropdown-item">
|
||||
<a href="javascript:void()" class="dropdown-item">
|
||||
<i class="bi bi-eraser-fill"></i>
|
||||
DataWipe
|
||||
</a>
|
||||
|
@ -232,7 +234,15 @@
|
|||
<tbody>
|
||||
{% for dev in devices %}
|
||||
<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>
|
||||
<a href="{{ url_for('inventory.devices.device_details', id=dev.devicehub_id)}}">
|
||||
{{ dev.type }} {{ dev.manufacturer }} {{ dev.model }}
|
||||
|
@ -269,6 +279,7 @@
|
|||
{% include "inventory/removeDeviceslot.html" %}
|
||||
{% include "inventory/removelot.html" %}
|
||||
{% include "inventory/actions.html" %}
|
||||
{% include "inventory/allocate.html" %}
|
||||
|
||||
<!-- CDN -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/simple-datatables@latest"></script>
|
||||
|
|
|
@ -16,14 +16,14 @@
|
|||
{% endfor %}
|
||||
</select>
|
||||
<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
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
||||
<input type="submit" class="btn btn-primary d-none" value="Save changes" />
|
||||
<input type="submit" class="btn btn-primary" style="display: none;" value="Save changes" />
|
||||
</div>
|
||||
</form>
|
||||
|
||||
|
|
Reference in New Issue