UX improvements

Fix calendar to better display in small windows
Bump the number of results for listing items
Quick workaround to successfully listing items after being created

Closes bug: 1556939
Change-Id: Ibaf0a999a729197900b18c1f22fd819f5e11e797
This commit is contained in:
Memo Garcia 2016-03-11 15:01:51 +00:00
parent b31f3c3665
commit a1541e6a59
19 changed files with 510 additions and 161 deletions

View File

@ -4,11 +4,8 @@
<h4>{% blocktrans %}Action{% endblocktrans %}</h4> <h4>{% blocktrans %}Action{% endblocktrans %}</h4>
<p>{% blocktrans %}Specify the details for this action.{% endblocktrans %}</p>
<p>{% blocktrans %}An action is the simplest execution within a job, it could be a backup or a restore <p>{% blocktrans %}An action is the simplest execution within a job, it could be a backup or a restore
and it contains rules that the action must follow (max retries, interval between intervals, etc) and it contains rules that the action must follow (max retries, interval between executions, etc).{% endblocktrans %}</p>
.{% endblocktrans %}</p>
{% endblock %} {% endblock %}

View File

@ -4,7 +4,7 @@
<h4>{% blocktrans %}Advanced configuration{% endblocktrans %}</h4> <h4>{% blocktrans %}Advanced configuration{% endblocktrans %}</h4>
<p>{% blocktrans %}Define extra information for the action to execute in the client.{% endblocktrans %}</p> <p>{% blocktrans %}Define extra information for the action executed by the client.{% endblocktrans %}</p>
{% endblock %} {% endblock %}

View File

@ -0,0 +1,16 @@
{% load i18n horizon humanize %}
{% block help_message %}
<h4>{% blocktrans %}Rules{% endblocktrans %}</h4>
<p>{% blocktrans %}Define the following action rules:
<ul>
<li>Number of retries</li>
<li>Retries interval</li>
<li>Action is mandatory</li>
</ul>
{% endblocktrans %}</p>
{% endblock %}

View File

@ -0,0 +1,13 @@
{% load i18n horizon humanize %}
{% block help_message %}
<h4>{% blocktrans %}Snapshot{% endblocktrans %}</h4>
<p>{% blocktrans %}The snapshot technologies currently supported are LVM for Linux and Volume Shadow Copy for Windows.{% endblocktrans %}</p>
<p>{% blocktrans %}For Linux, in order to use snapshot features, the data has to reside on an LVM volume.{% endblocktrans %}</p>
{% endblock %}
<script type='text/javascript' src='{{ STATIC_URL }}freezer/js/freezer.actions.snapshot.js'></script>

View File

@ -32,6 +32,10 @@ class ActionConfigurationAction(workflows.Action):
widget=forms.HiddenInput(), widget=forms.HiddenInput(),
required=False) required=False)
backup_name = forms.CharField(
label=_("Action Name *"),
required=False)
action = forms.ChoiceField( action = forms.ChoiceField(
help_text=_("Set the action to be taken"), help_text=_("Set the action to be taken"),
required=True) required=True)
@ -44,10 +48,6 @@ class ActionConfigurationAction(workflows.Action):
help_text=_("Set storage backend for a backup"), help_text=_("Set storage backend for a backup"),
required=True) required=True)
backup_name = forms.CharField(
label=_("Action Name *"),
required=False)
mysql_conf = forms.CharField( mysql_conf = forms.CharField(
label=_("MySQL Configuration File *"), label=_("MySQL Configuration File *"),
help_text=_("Set the path where the MySQL configuration file " help_text=_("Set the path where the MySQL configuration file "
@ -228,7 +228,7 @@ class ActionConfigurationAction(workflows.Action):
class Meta(object): class Meta(object):
name = _("Action") name = _("Action")
help_text_template = "disaster_recovery/jobs" \ help_text_template = "disaster_recovery/actions" \
"/_action.html" "/_action.html"
@ -268,7 +268,7 @@ class SnapshotConfigurationAction(workflows.Action):
class Meta(object): class Meta(object):
name = _("Snapshot") name = _("Snapshot")
help_text_template = "disaster_recovery/jobs" \ help_text_template = "disaster_recovery/actions" \
"/_snapshot.html" "/_snapshot.html"
@ -453,7 +453,7 @@ class AdvancedConfigurationAction(workflows.Action):
class Meta(object): class Meta(object):
name = _("Advanced") name = _("Advanced")
help_text_template = "disaster_recovery/jobs" \ help_text_template = "disaster_recovery/actions" \
"/_advanced.html" "/_advanced.html"
@ -493,19 +493,19 @@ class RulesConfigurationAction(workflows.Action):
label=_("Max Retries Interval"), label=_("Max Retries Interval"),
initial=0, initial=0,
min_value=0, min_value=0,
help_text=_("Set the interval between intervals " help_text=_("Set the interval between executions "
"for retries in seconds"), "for retries in seconds"),
required=False) required=False)
mandatory = forms.BooleanField( mandatory = forms.BooleanField(
label=_("Mandatory"), label=_("Mandatory"),
help_text=_("Set this job as mandatory"), help_text=_("Set this action as mandatory"),
widget=forms.CheckboxInput(), widget=forms.CheckboxInput(),
required=False) required=False)
class Meta(object): class Meta(object):
name = _("Rules") name = _("Rules")
help_text_template = "disaster_recovery/jobs" \ help_text_template = "disaster_recovery/actions" \
"/_rules.html" "/_rules.html"

View File

@ -14,6 +14,7 @@
# from horizon. # from horizon.
import logging import logging
import time
from django.conf import settings from django.conf import settings
@ -88,7 +89,7 @@ class Job(object):
self.request = request self.request = request
self.client = client(request) self.client = client(request)
def list(self, json=False, limit=100, offset=0, search=None): def list(self, json=False, limit=500, offset=0, search=None):
if search: if search:
search = {"match": [{"_all": search}, ], } search = {"match": [{"_all": search}, ], }
@ -120,7 +121,13 @@ class Job(object):
job.get('client_id')) job.get('client_id'))
def create(self, job): def create(self, job):
return self._build(job) job = self._build(job)
# due to elasticsearch replication time, sometimes the newly created
# file is not reflected in the ui immediately, so a quick and dirty
# workaround is to sleep for 3 seconds to make sure that the changes
# are reflected
time.sleep(3)
return job
def update(self, job_id, job): def update(self, job_id, job):
scheduling = {} scheduling = {}
@ -248,7 +255,7 @@ class Session(object):
self.request = request self.request = request
self.client = client(request) self.client = client(request)
def list(self, json=False, limit=30, offset=0, search=None): def list(self, json=False, limit=500, offset=0, search=None):
if search: if search:
search = {"match": [{"_all": search}, ], } search = {"match": [{"_all": search}, ], }
@ -285,7 +292,13 @@ class Session(object):
session.get('schedule', {}).get('schedule_end_date')) session.get('schedule', {}).get('schedule_end_date'))
def create(self, session): def create(self, session):
return self._build(session) session = self._build(session)
# due to elasticsearch replication time, sometimes the newly created
# file is not reflected in the ui immediately, so a quick and dirty
# workaround is to sleep for 3 seconds to make sure that the changes
# are reflected
time.sleep(3)
return session
def update(self, session, session_id): def update(self, session, session_id):
return self.client.sessions.update(session_id, session) return self.client.sessions.update(session_id, session)
@ -338,7 +351,7 @@ class Action(object):
self.request = request self.request = request
self.client = client(request) self.client = client(request)
def list(self, json=False, limit=100, offset=0, search=None): def list(self, json=False, limit=500, offset=0, search=None):
if search: if search:
search = {"match": [{"_all": search}, ], } search = {"match": [{"_all": search}, ], }
@ -374,7 +387,13 @@ class Action(object):
action['freezer_action'].get('storage')) action['freezer_action'].get('storage'))
def create(self, action): def create(self, action):
return self._build(action) action = self._build(action)
# due to elasticsearch replication time, sometimes the newly created
# file is not reflected in the ui immediately, so a quick and dirty
# workaround is to sleep for 3 seconds to make sure that the changes
# are reflected
time.sleep(3)
return action
def update(self, action, action_id): def update(self, action, action_id):
updated_action = {} updated_action = {}
@ -427,7 +446,7 @@ class Client(object):
self.request = request self.request = request
self.client = client(request) self.client = client(request)
def list(self, json=False, limit=100, offset=0, search=None): def list(self, json=False, limit=500, offset=0, search=None):
if search: if search:
search = {"match": [{"_all": search}, ], } search = {"match": [{"_all": search}, ], }
@ -465,7 +484,7 @@ class Backup(object):
self.request = request self.request = request
self.client = client(request) self.client = client(request)
def list(self, json=False, limit=30, offset=0, search=None): def list(self, json=False, limit=500, offset=0, search=None):
if search: if search:
search = {"match": [{"_all": search}, ], } search = {"match": [{"_all": search}, ], }

View File

@ -49,12 +49,12 @@ def icons(backup):
placeholder = '<i class="fa fa-fw"></i>' placeholder = '<i class="fa fa-fw"></i>'
try: try:
level_txt = "Level: {} ({} backup) out of {}".format( level_txt = "Level: {} ({} backup)".format(
backup.level, "Full" if backup.level == 0 else "Incremental", backup.curr_backup_level, "Full"
backup.max_level) if backup.curr_backup_level == 0 else "Incremental")
result.append( result.append(
'<i class="fa fa-fw fa-custom-number" title="{}">{}</i>'.format( '<i class="fa fa-fw fa-custom-number" title="{}">{}</i>'.format(
level_txt, backup.level)) level_txt, backup.curr_backup_level))
except Exception: except Exception:
result.append("Level: {}".format("Full")) result.append("Level: {}".format("Full"))

View File

@ -2,10 +2,12 @@
{% block help_message %} {% block help_message %}
<h4>{% blocktrans %}Job{% endblocktrans %}</h4> <h4>{% blocktrans %}Job{% endblocktrans %}</h4>
<p>{% blocktrans %}Describes actions to be executed by a client.{% endblocktrans %}</p>
<p>{% blocktrans %}Job executions can be executed and scheduled, by providing the following settings.{% endblocktrans %}</p>
<p>{% blocktrans %}Specify the details for creating a job.{% endblocktrans %}</p> <link rel="stylesheet" href="{{ STATIC_URL }}freezer/css/bootstrap-datetimepicker.min.css">
<script type='text/javascript' src='{{ STATIC_URL }}freezer/js/vendor/moment.js'></script>
<p>{% blocktrans %}A job defines an ordered list of actions to be executed in a client with scheduling information.{% endblocktrans %}</p> <script type='text/javascript' src='{{ STATIC_URL }}freezer/js/vendor/bootstrap-datetimepicker.js'></script>
<script type='text/javascript' src='{{ STATIC_URL }}freezer/js/freezer.datetimepicker.js'></script>
{% endblock %} {% endblock %}

View File

@ -1,11 +0,0 @@
{% load i18n horizon humanize %}
{% block help_message %}
<h4>{% blocktrans %}Rules{% endblocktrans %}</h4>
<p>{% blocktrans %}Define rules for this action, from how many times it will retry the action, the interval
between interval and whether or not the action is mandatory. {% endblocktrans %}</p>
{% endblock %}

View File

@ -1,24 +0,0 @@
{% load i18n horizon humanize %}
{% block help_message %}
<h4>{% blocktrans %}Start and End Date Time{% endblocktrans %}</h4>
<p>{% blocktrans %}Set a start date and time to execute jobs{% endblocktrans %}</p>
<h4>{% blocktrans %}Interval{% endblocktrans %}</h4>
<p>{% blocktrans %}Set the interval in the following format:{% endblocktrans %}</p>
<ul>
<li>continuous</li>
<li>N weeks</li>
<li>N days</li>
<li>N hours</li>
<li>N minutes</li>
<li>N seconds</li>
</ul>
<p>{% blocktrans %}If no start date is provided the job will start immediately{% endblocktrans %}</p>
<script type='text/javascript' src='{{ STATIC_URL }}freezer/js/vendor/moment.js'></script>
<script type='text/javascript' src='{{ STATIC_URL }}freezer/js/vendor/bootstrap-datetimepicker.js'></script>
<script type='text/javascript' src='{{ STATIC_URL }}freezer/js/freezer.datetimepicker.js'></script>
{% endblock %}

View File

@ -1,12 +0,0 @@
{% load i18n horizon humanize %}
{% block help_message %}
<h4>{% blocktrans %}Action{% endblocktrans %}</h4>
<p>{% blocktrans %}Specify whether this action should execute a snapshot on the client file system.
In Linux and it's distros it will use LVM and in Windows it will use Volume Shadow Copy{% endblocktrans %}</p>
{% endblock %}
<script type='text/javascript' src='{{ STATIC_URL }}freezer/js/freezer.actions.snapshot.js'></script>

View File

@ -1,61 +0,0 @@
{% load i18n %}
<noscript><h3>{{ step }}</h3></noscript>
<div class="membership {{ step.slug }}_membership dropdown_fix" data-show-roles="{{ step.show_roles }}">
<div class="header">
<div class="help_text">{{ step.help_text }}</div>
<div class="row">
<div class="col-xs-6">
<div class="fake_table fake_table_header fake_{{ step.slug }}_table clearfix">
<span class="members_title">{{ step.available_list_title }}</span>
<div class="form-group has-feedback">
<input type="text" name="available_{{ step.slug }}_filter" id="available_{{ step.slug }}" class="filter {{ step.slug }}_filter form-control input-sm" placeholder="{% trans "Filter" %}">
<span class="fa fa-search search-icon form-control-feedback"></span>
</div>
</div>
</div>
<div class="col-xs-6">
<div class="fake_table fake_table_header fake_{{ step.slug }}_table clearfix">
<span class="members_title">{{ step.members_list_title }}</span>
<div class="form-group has-feedback">
<input type="text" name="{{ step.slug }}_members_filter" id="{{ step.slug }}_members" class="filter {{ step.slug }}_filter form-control input-sm" placeholder="{% trans "Filter" %}">
<span class="fa fa-search search-icon form-control-feedback"></span>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-xs-6 filterable {{ step.slug }}_filterable">
<div class="fake_table fake_{{ step.slug }}_table" id="available_{{ step.slug }}">
<ul class="available_members available_{{ step.slug }}"></ul>
<ul class="no_results" id="no_available_{{ step.slug }}"><li>{{ step.no_available_text }}</li></ul>
</div>
</div>
<div class="col-xs-6 filterable {{ step.slug }}_filterable">
<div class="fake_table fake_{{ step.slug }}_table" id="{{ step.slug }}_members">
<ul class="members {{ step.slug }}_members"></ul>
<ul class="no_results" id="no_{{ step.slug }}_members"><li>{{ step.no_members_text }}</li></ul>
</div>
</div>
</div>
</div>
<div class="hide">
{% include "horizon/common/_form_fields.html" %}
</div>
<script>
if (typeof horizon.membership !== 'undefined') {
horizon.membership.workflow_init($(".workflow"), "{{ step.slug }}", "{{ step.get_id }}");
} else {
addHorizonLoadEvent(function() {
horizon.membership.workflow_init($(".workflow"), "{{ step.slug }}", "{{ step.get_id }}");
});
}
</script>

View File

@ -115,7 +115,15 @@ class InfoConfigurationAction(workflows.Action):
schedule_interval = forms.CharField( schedule_interval = forms.CharField(
label=_("Interval"), label=_("Interval"),
required=False, required=False,
help_text=_("Repeat this configuration in a minutes interval.")) help_text=_("""Set the interval in the following format:
continuous,
N weeks,
N days,
N hours,
N minutes,
N seconds,
If no start date is provided the job
will start immediately"""))
schedule_end_date = forms.CharField( schedule_end_date = forms.CharField(
label=_("End Date and Time"), label=_("End Date and Time"),
@ -170,7 +178,7 @@ class InfoConfigurationAction(workflows.Action):
name = _("Job Info") name = _("Job Info")
slug = "info" slug = "info"
help_text_template = "disaster_recovery/jobs" \ help_text_template = "disaster_recovery/jobs" \
"/_scheduling.html" "/_info.html"
class InfoConfiguration(workflows.Step): class InfoConfiguration(workflows.Step):

View File

@ -90,7 +90,7 @@ class InfoConfigurationAction(workflows.Action):
name = _("Job Info") name = _("Job Info")
slug = "info" slug = "info"
help_text_template = "disaster_recovery/jobs" \ help_text_template = "disaster_recovery/jobs" \
"/_scheduling.html" "/_info.html"
class InfoConfiguration(workflows.Step): class InfoConfiguration(workflows.Step):

View File

@ -3,10 +3,11 @@
{% block help_message %} {% block help_message %}
<h4>{% blocktrans %}Session{% endblocktrans %}</h4> <h4>{% blocktrans %}Session{% endblocktrans %}</h4>
<p>{% blocktrans %}Specify the name for this session.{% endblocktrans %}</p> <p>{% blocktrans %}A group of jobs which share the same scheduling time.{% endblocktrans %}</p>
<p>{% blocktrans %}A session is a collection of jobs which share scheduling information and will coordinate one
or more clients.{% endblocktrans %}</p>
<link rel="stylesheet" href="{{ STATIC_URL }}freezer/css/bootstrap-datetimepicker.min.css">
<script type='text/javascript' src='{{ STATIC_URL }}freezer/js/vendor/moment.js'></script>
<script type='text/javascript' src='{{ STATIC_URL }}freezer/js/vendor/bootstrap-datetimepicker.js'></script>
<script type='text/javascript' src='{{ STATIC_URL }}freezer/js/freezer.datetimepicker.js'></script>
{% endblock %} {% endblock %}

View File

@ -44,7 +44,16 @@ class SessionConfigurationAction(workflows.Action):
schedule_interval = forms.CharField( schedule_interval = forms.CharField(
label=_("Interval"), label=_("Interval"),
required=False) required=False,
help_text=_("""Set the interval in the following format:
continuous,
N weeks,
N days,
N hours,
N minutes,
N seconds,
If no start date is provided the job
will start immediately"""))
schedule_end_date = forms.CharField( schedule_end_date = forms.CharField(
label=_("End Date and Time"), label=_("End Date and Time"),
@ -80,8 +89,8 @@ class SessionConfigurationAction(workflows.Action):
class Meta: class Meta:
name = _("Session Information") name = _("Session Information")
slug = "sessions" slug = "sessions"
help_text_template = "disaster_recovery/jobs" \ help_text_template = "disaster_recovery/sessions" \
"/_scheduling.html" "/_info.html"
class SessionConfiguration(workflows.Step): class SessionConfiguration(workflows.Step):

View File

@ -0,0 +1,373 @@
/*!
* Datetimepicker for Bootstrap 3
* version : 4.17.37
* https://github.com/Eonasdan/bootstrap-datetimepicker/
*/
.bootstrap-datetimepicker-widget {
list-style: none;
}
.bootstrap-datetimepicker-widget.dropdown-menu {
margin: 2px 0;
padding: 4px;
width: 19em;
}
@media (min-width: 768px) {
.bootstrap-datetimepicker-widget.dropdown-menu.timepicker-sbs {
width: 38em;
}
}
@media (min-width: 992px) {
.bootstrap-datetimepicker-widget.dropdown-menu.timepicker-sbs {
width: 38em;
}
}
@media (min-width: 1200px) {
.bootstrap-datetimepicker-widget.dropdown-menu.timepicker-sbs {
width: 38em;
}
}
.bootstrap-datetimepicker-widget.dropdown-menu:before,
.bootstrap-datetimepicker-widget.dropdown-menu:after {
content: '';
display: inline-block;
position: absolute;
}
.bootstrap-datetimepicker-widget.dropdown-menu.bottom:before {
border-left: 7px solid transparent;
border-right: 7px solid transparent;
border-bottom: 7px solid #cccccc;
border-bottom-color: rgba(0, 0, 0, 0.2);
top: -7px;
left: 7px;
}
.bootstrap-datetimepicker-widget.dropdown-menu.bottom:after {
border-left: 6px solid transparent;
border-right: 6px solid transparent;
border-bottom: 6px solid white;
top: -6px;
left: 8px;
}
.bootstrap-datetimepicker-widget.dropdown-menu.top:before {
border-left: 7px solid transparent;
border-right: 7px solid transparent;
border-top: 7px solid #cccccc;
border-top-color: rgba(0, 0, 0, 0.2);
bottom: -7px;
left: 6px;
}
.bootstrap-datetimepicker-widget.dropdown-menu.top:after {
border-left: 6px solid transparent;
border-right: 6px solid transparent;
border-top: 6px solid white;
bottom: -6px;
left: 7px;
}
.bootstrap-datetimepicker-widget.dropdown-menu.pull-right:before {
left: auto;
right: 6px;
}
.bootstrap-datetimepicker-widget.dropdown-menu.pull-right:after {
left: auto;
right: 7px;
}
.bootstrap-datetimepicker-widget .list-unstyled {
margin: 0;
}
.bootstrap-datetimepicker-widget a[data-action] {
padding: 6px 0;
}
.bootstrap-datetimepicker-widget a[data-action]:active {
box-shadow: none;
}
.bootstrap-datetimepicker-widget .timepicker-hour,
.bootstrap-datetimepicker-widget .timepicker-minute,
.bootstrap-datetimepicker-widget .timepicker-second {
width: 54px;
font-weight: bold;
font-size: 1.2em;
margin: 0;
}
.bootstrap-datetimepicker-widget button[data-action] {
padding: 6px;
}
.bootstrap-datetimepicker-widget .btn[data-action="incrementHours"]::after {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
content: "Increment Hours";
}
.bootstrap-datetimepicker-widget .btn[data-action="incrementMinutes"]::after {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
content: "Increment Minutes";
}
.bootstrap-datetimepicker-widget .btn[data-action="decrementHours"]::after {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
content: "Decrement Hours";
}
.bootstrap-datetimepicker-widget .btn[data-action="decrementMinutes"]::after {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
content: "Decrement Minutes";
}
.bootstrap-datetimepicker-widget .btn[data-action="showHours"]::after {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
content: "Show Hours";
}
.bootstrap-datetimepicker-widget .btn[data-action="showMinutes"]::after {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
content: "Show Minutes";
}
.bootstrap-datetimepicker-widget .btn[data-action="togglePeriod"]::after {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
content: "Toggle AM/PM";
}
.bootstrap-datetimepicker-widget .btn[data-action="clear"]::after {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
content: "Clear the picker";
}
.bootstrap-datetimepicker-widget .btn[data-action="today"]::after {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
content: "Set the date to today";
}
.bootstrap-datetimepicker-widget .picker-switch {
text-align: center;
}
.bootstrap-datetimepicker-widget .picker-switch::after {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
content: "Toggle Date and Time Screens";
}
.bootstrap-datetimepicker-widget .picker-switch td {
padding: 0;
margin: 0;
height: auto;
width: auto;
line-height: inherit;
}
.bootstrap-datetimepicker-widget .picker-switch td span {
line-height: 2.5;
height: 2.5em;
width: 100%;
}
.bootstrap-datetimepicker-widget table {
width: 100%;
margin: 0;
}
.bootstrap-datetimepicker-widget table td,
.bootstrap-datetimepicker-widget table th {
text-align: center;
border-radius: 4px;
}
.bootstrap-datetimepicker-widget table th {
height: 20px;
line-height: 20px;
width: 20px;
}
.bootstrap-datetimepicker-widget table th.picker-switch {
width: 145px;
}
.bootstrap-datetimepicker-widget table th.disabled,
.bootstrap-datetimepicker-widget table th.disabled:hover {
background: none;
color: #777777;
cursor: not-allowed;
}
.bootstrap-datetimepicker-widget table th.prev::after {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
content: "Previous Month";
}
.bootstrap-datetimepicker-widget table th.next::after {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
content: "Next Month";
}
.bootstrap-datetimepicker-widget table thead tr:first-child th {
cursor: pointer;
}
.bootstrap-datetimepicker-widget table thead tr:first-child th:hover {
background: #eeeeee;
}
.bootstrap-datetimepicker-widget table td {
height: 54px;
line-height: 54px;
width: 54px;
}
.bootstrap-datetimepicker-widget table td.cw {
font-size: .8em;
height: 20px;
line-height: 20px;
color: #777777;
}
.bootstrap-datetimepicker-widget table td.day {
height: 20px;
line-height: 20px;
width: 20px;
}
.bootstrap-datetimepicker-widget table td.day:hover,
.bootstrap-datetimepicker-widget table td.hour:hover,
.bootstrap-datetimepicker-widget table td.minute:hover,
.bootstrap-datetimepicker-widget table td.second:hover {
background: #eeeeee;
cursor: pointer;
}
.bootstrap-datetimepicker-widget table td.old,
.bootstrap-datetimepicker-widget table td.new {
color: #777777;
}
.bootstrap-datetimepicker-widget table td.today {
position: absolute;
}
.bootstrap-datetimepicker-widget table td.today:before {
content: '';
display: inline-block;
border: solid transparent;
border-width: 0 0 7px 7px;
border-bottom-color: #337ab7;
border-top-color: rgba(0, 0, 0, 0.2);
position: absolute;
bottom: 4px;
right: 4px;
}
.bootstrap-datetimepicker-widget table td.active,
.bootstrap-datetimepicker-widget table td.active:hover {
background-color: #337ab7;
color: #ffffff;
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
}
.bootstrap-datetimepicker-widget table td.active.today:before {
border-bottom-color: #fff;
}
.bootstrap-datetimepicker-widget table td.disabled,
.bootstrap-datetimepicker-widget table td.disabled:hover {
background: none;
color: #777777;
cursor: not-allowed;
}
.bootstrap-datetimepicker-widget table td span {
display: inline-block;
width: 54px;
height: 54px;
line-height: 54px;
margin: 2px 1.5px;
cursor: pointer;
border-radius: 4px;
}
.bootstrap-datetimepicker-widget table td span:hover {
background: #eeeeee;
}
.bootstrap-datetimepicker-widget table td span.active {
background-color: #337ab7;
color: #ffffff;
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
}
.bootstrap-datetimepicker-widget table td span.old {
color: #777777;
}
.bootstrap-datetimepicker-widget table td span.disabled,
.bootstrap-datetimepicker-widget table td span.disabled:hover {
background: none;
color: #777777;
cursor: not-allowed;
}
.bootstrap-datetimepicker-widget.usetwentyfour td.hour {
height: 27px;
line-height: 27px;
}
.bootstrap-datetimepicker-widget.wider {
width: 21em;
}
.bootstrap-datetimepicker-widget .datepicker-decades .decade {
line-height: 1.8em !important;
}
.input-group.date .input-group-addon {
cursor: pointer;
}
.sr-only {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
}

View File

@ -18,12 +18,6 @@
"use strict"; "use strict";
function hideIncrementalOptions () {
$("#id_max_level").closest(".form-group").hide();
$("#id_always_level").closest(".form-group").hide();
$("#id_restart_always_level").closest(".form-group").hide();
}
$("#id_no_incremental").click(function () { $("#id_no_incremental").click(function () {
if ($("#id_no_incremental").is(":checked")) { if ($("#id_no_incremental").is(":checked")) {
$("#id_max_level").closest(".form-group").hide(); $("#id_max_level").closest(".form-group").hide();
@ -34,7 +28,4 @@ $("#id_no_incremental").click(function () {
$("#id_always_level").closest(".form-group").show(); $("#id_always_level").closest(".form-group").show();
$("#id_restart_always_level").closest(".form-group").show(); $("#id_restart_always_level").closest(".form-group").show();
} }
}); });
hideIncrementalOptions();

View File

@ -20,10 +20,38 @@
$(function () { $(function () {
$('#id_schedule_start_date').datetimepicker({ $('#id_schedule_start_date').datetimepicker({
format: 'YYYY-MM-DDTHH:mm:ss' format: 'YYYY-MM-DDTHH:mm:ss',
showClose: true,
tooltips: {
today: 'Go to today',
clear: 'Clear selection',
close: 'Close the picker'
},
widgetPositioning: {
horizontal: 'left',
vertical: 'bottom'
}
}); });
$('#id_schedule_end_date').datetimepicker({ $('#id_schedule_end_date').datetimepicker({
format: 'YYYY-MM-DDTHH:mm:ss' format: 'YYYY-MM-DDTHH:mm:ss',
showClose: true,
tooltips: {
today: 'Go to today',
clear: 'Clear selection',
close: 'Close the picker'
},
widgetPositioning: {
horizontal: 'left',
vertical: 'bottom'
},
useCurrent: false //Important! See issue #1075
});
$("#id_schedule_start_date").on("dp.change", function (e) {
$('#id_schedule_end_date').data("DateTimePicker").minDate(e.date);
});
$("#id_schedule_end_date").on("dp.change", function (e) {
$('#id_schedule_start_date').data("DateTimePicker").maxDate(e.date);
}); });
}); });