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
|
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 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 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):
|
||||||
|
@ -418,7 +425,6 @@ class TagDeviceForm(FlaskForm):
|
||||||
self.delete = kwargs.pop('delete', None)
|
self.delete = kwargs.pop('delete', None)
|
||||||
self.device_id = kwargs.pop('device', None)
|
self.device_id = kwargs.pop('device', None)
|
||||||
|
|
||||||
# import pdb; pdb.set_trace()
|
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
if self.delete:
|
if self.delete:
|
||||||
|
@ -466,7 +472,68 @@ class TagDeviceForm(FlaskForm):
|
||||||
|
|
||||||
|
|
||||||
class NewActionForm(FlaskForm):
|
class NewActionForm(FlaskForm):
|
||||||
name = StringField(u'Name')
|
name = StringField(u'Name', [validators.length(max=50)])
|
||||||
date = StringField(u'Date')
|
devices = HiddenField()
|
||||||
severity = StringField(u'Severity')
|
date = DateField(u'Date', validators=(validators.Optional(),))
|
||||||
description = StringField(u'Description')
|
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
|
import flask
|
||||||
|
from flask import Blueprint, request, url_for
|
||||||
from flask.views import View
|
from flask.views import View
|
||||||
from flask import Blueprint, url_for, request
|
from flask_login import current_user, login_required
|
||||||
from flask_login import login_required, current_user
|
|
||||||
|
|
||||||
|
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.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
|
|
||||||
|
|
||||||
# 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')
|
||||||
|
|
||||||
|
|
||||||
class DeviceListView(View):
|
class DeviceListMix(View):
|
||||||
decorators = [login_required]
|
decorators = [login_required]
|
||||||
template_name = 'inventory/device_list.html'
|
template_name = 'inventory/device_list.html'
|
||||||
|
|
||||||
def dispatch_request(self, lot_id=None):
|
def get_context(self, lot_id):
|
||||||
# TODO @cayop adding filter
|
# TODO @cayop adding filter
|
||||||
# https://github.com/eReuse/devicehub-teal/blob/testing/ereuse_devicehub/resources/device/views.py#L56
|
# https://github.com/eReuse/devicehub-teal/blob/testing/ereuse_devicehub/resources/device/views.py#L56
|
||||||
filter_types = ['Desktop', 'Laptop', 'Server']
|
filter_types = ['Desktop', 'Laptop', 'Server']
|
||||||
|
@ -30,19 +34,41 @@ class DeviceListView(View):
|
||||||
lot = lots.filter(Lot.id == lot_id).one()
|
lot = lots.filter(Lot.id == lot_id).one()
|
||||||
devices = [dev for dev in lot.devices if dev.type 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)
|
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:
|
else:
|
||||||
devices = Device.query.filter(
|
devices = Device.query.filter(
|
||||||
Device.owner_id == current_user.id).filter(
|
Device.owner_id == current_user.id).filter(
|
||||||
Device.type.in_(filter_types)).filter(Device.lots == None).order_by(
|
Device.type.in_(filter_types)).filter(Device.lots == None).order_by(
|
||||||
Device.updated.desc())
|
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,
|
'lots': lots,
|
||||||
'form_lot_device': LotDeviceForm(),
|
'form_lot_device': LotDeviceForm(),
|
||||||
'form_tag_device': TagDeviceForm(),
|
'form_tag_device': TagDeviceForm(),
|
||||||
|
'form_new_action': form_new_action,
|
||||||
|
'form_new_allocate': form_new_allocate,
|
||||||
'lot': lot,
|
'lot': lot,
|
||||||
'tags': tags}
|
'tags': tags,
|
||||||
return flask.render_template(self.template_name, **context)
|
'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):
|
class DeviceDetailView(View):
|
||||||
|
@ -261,6 +287,52 @@ class TagUnlinkDeviceView(View):
|
||||||
return flask.render_template(self.template_name, form=form, referrer=request.referrer)
|
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/', 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'))
|
||||||
|
|
|
@ -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
|
args[INHERIT_COND] = cls.id == Action.id
|
||||||
return args
|
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
|
@property
|
||||||
def date_str(self):
|
def date_str(self):
|
||||||
return '{:%c}'.format(self.end_time)
|
return '{:%c}'.format(self.end_time)
|
||||||
|
|
|
@ -55,6 +55,10 @@ class Action(Thing):
|
||||||
if 'start_time' in data and data['start_time'] < unix_time:
|
if 'start_time' in data and data['start_time'] < unix_time:
|
||||||
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):
|
class ActionWithOneDevice(Action):
|
||||||
__doc__ = m.ActionWithOneDevice.__doc__
|
__doc__ = m.ActionWithOneDevice.__doc__
|
||||||
|
|
|
@ -164,6 +164,10 @@ class Device(Thing):
|
||||||
super().__init__(**kw)
|
super().__init__(**kw)
|
||||||
self.set_hid()
|
self.set_hid()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def reverse_actions(self) -> list:
|
||||||
|
return reversed(self.actions)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def actions(self) -> list:
|
def actions(self) -> list:
|
||||||
"""All the actions where the device participated, including:
|
"""All the actions where the device participated, including:
|
||||||
|
|
|
@ -1,31 +1,46 @@
|
||||||
$(document).ready(function() {
|
$(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);
|
$(".deviceSelect").on("change", deviceSelect);
|
||||||
|
}
|
||||||
// $('#selectLot').selectpicker();
|
// $('#selectLot').selectpicker();
|
||||||
})
|
})
|
||||||
|
|
||||||
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").removeClass('d-none');
|
|
||||||
$("#addingLotModal .btn-primary").show();
|
$("#addingLotModal .btn-primary").show();
|
||||||
$("#removeLotModal .text-danger").hide();
|
|
||||||
$("#removeLotModal .btn-primary").removeClass('d-none');
|
$("#removeLotModal .pol").hide();
|
||||||
$("#removeLotModal .btn-primary").show();
|
$("#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() {
|
function removeTag() {
|
||||||
|
@ -39,5 +54,50 @@ function removeTag() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function newAction(action) {
|
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-->
|
</aside><!-- End Sidebar-->
|
||||||
|
|
||||||
<main id="main" class="main">
|
<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 %}
|
{% block main %}
|
||||||
|
|
||||||
{% endblock main %}
|
{% endblock main %}
|
||||||
|
|
|
@ -1,25 +1,51 @@
|
||||||
<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.lot_devices_add') }}" method="post">
|
<form action="{{ url_for('inventory.devices.action_add') }}" method="post">
|
||||||
{{ form_lot_device.csrf_token }}
|
{{ form_new_action.csrf_token }}
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
Please write a name of a lot
|
{% for field in form_new_action %}
|
||||||
<input class="devicesList" type="hidden" name="devices" />
|
{% if field != form_new_action.csrf_token %}
|
||||||
<p class="text-danger">
|
{% 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
|
You need select first some device before to do one action
|
||||||
</p>
|
</p>
|
||||||
|
<p class="enumeration-devices"></p>
|
||||||
</div>
|
</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">
|
<div class="modal-footer">
|
||||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
<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>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
|
|
@ -17,14 +17,14 @@
|
||||||
{% 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>
|
||||||
|
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
<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>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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">
|
<div class="tab-pane fade profile-overview" id="traceability">
|
||||||
<h5 class="card-title">Traceability log Details</h5>
|
<h5 class="card-title">Traceability log Details</h5>
|
||||||
<div class="list-group col-6">
|
<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">
|
<div class="list-group-item d-flex justify-content-between align-items-center">
|
||||||
{{ action.type }} {{ action.severity }}
|
{{ action.type }} {{ action.severity }}
|
||||||
<small class="text-muted">{{ action.created.strftime('%H:%M %d-%m-%Y') }}</small>
|
<small class="text-muted">{{ action.created.strftime('%H:%M %d-%m-%Y') }}</small>
|
||||||
|
|
|
@ -40,7 +40,7 @@
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-body pt-3">
|
<div class="card-body pt-3" style="min-height: 650px;">
|
||||||
<!-- Bordered Tabs -->
|
<!-- Bordered Tabs -->
|
||||||
|
|
||||||
<div class="btn-group dropdown ml-1">
|
<div class="btn-group dropdown ml-1">
|
||||||
|
@ -57,7 +57,7 @@
|
||||||
<span class="caret"></span>
|
<span class="caret"></span>
|
||||||
</button>
|
</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<ul class="dropdown-menu" aria-labelledby="btnLots"">
|
<ul class="dropdown-menu" aria-labelledby="btnLots">
|
||||||
<li>
|
<li>
|
||||||
<a href="javascript:void()" class="dropdown-item" data-bs-toggle="modal" data-bs-target="#addingLotModal">
|
<a href="javascript:void()" class="dropdown-item" data-bs-toggle="modal" data-bs-target="#addingLotModal">
|
||||||
<i class="bi bi-plus"></i>
|
<i class="bi bi-plus"></i>
|
||||||
|
@ -78,12 +78,14 @@
|
||||||
<i class="bi bi-plus"></i>
|
<i class="bi bi-plus"></i>
|
||||||
New Actions
|
New Actions
|
||||||
</button>
|
</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>
|
<li>
|
||||||
Status actions
|
Status actions
|
||||||
</li>
|
</li>
|
||||||
<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>
|
<i class="bi bi-recycle"></i>
|
||||||
Recycling
|
Recycling
|
||||||
</a>
|
</a>
|
||||||
|
@ -110,13 +112,13 @@
|
||||||
Allocation
|
Allocation
|
||||||
</li>
|
</li>
|
||||||
<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>
|
<i class="bi bi-house-fill"></i>
|
||||||
Allocate
|
Allocate
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="javascript:newAction('Deallocate')" class="dropdown-item">
|
<a href="javascript:newAllocate('Deallocate')" class="dropdown-item">
|
||||||
<i class="bi bi-house"></i>
|
<i class="bi bi-house"></i>
|
||||||
Deallocate
|
Deallocate
|
||||||
</a>
|
</a>
|
||||||
|
@ -137,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>
|
||||||
|
@ -232,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 }}
|
||||||
|
@ -269,6 +279,7 @@
|
||||||
{% include "inventory/removeDeviceslot.html" %}
|
{% include "inventory/removeDeviceslot.html" %}
|
||||||
{% include "inventory/removelot.html" %}
|
{% include "inventory/removelot.html" %}
|
||||||
{% include "inventory/actions.html" %}
|
{% include "inventory/actions.html" %}
|
||||||
|
{% include "inventory/allocate.html" %}
|
||||||
|
|
||||||
<!-- CDN -->
|
<!-- CDN -->
|
||||||
<script src="https://cdn.jsdelivr.net/npm/simple-datatables@latest"></script>
|
<script src="https://cdn.jsdelivr.net/npm/simple-datatables@latest"></script>
|
||||||
|
|
|
@ -16,14 +16,14 @@
|
||||||
{% 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>
|
||||||
|
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
<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>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
|
Reference in New Issue