Purge even more jquery stuff
and related static files etc. from old themes this might be the end of it..??
This commit is contained in:
parent
2ebae17839
commit
976a5836a9
|
@ -1,122 +1,14 @@
|
||||||
|
|
||||||
/******************************
|
|
||||||
* General
|
|
||||||
******************************/
|
|
||||||
|
|
||||||
* {
|
|
||||||
margin: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
font-family: Verdana, Arial, sans-serif;
|
|
||||||
font-size: 11pt;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: #0972a5;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
a:hover {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
margin-bottom: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
h2 {
|
|
||||||
font-size: 12pt;
|
|
||||||
margin: 20px auto 10px auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
li {
|
|
||||||
line-height: 2em;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
margin-bottom: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.left {
|
|
||||||
float: left;
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.right {
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
.wrapper {
|
|
||||||
overflow: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.buttons {
|
|
||||||
clear: both;
|
|
||||||
margin-top: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.dialog {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.flash-message {
|
|
||||||
background-color: #dddddd;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
padding: 3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.flash-messages div.ui-state-highlight {
|
|
||||||
padding: .3em;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.error-messages div.ui-state-error {
|
|
||||||
padding: .3em;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.flash-messages,
|
|
||||||
.error-messages {
|
|
||||||
margin: 0.5em 0 0 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul.error {
|
|
||||||
color: #dd6666;
|
|
||||||
font-weight: bold;
|
|
||||||
padding: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul.error li {
|
|
||||||
list-style-type: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
pre.is-family-sans-serif {
|
|
||||||
background-color: white;
|
|
||||||
font-family: Verdana, Arial, sans-serif;
|
|
||||||
font-size: 11pt;
|
|
||||||
padding: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
/******************************
|
|
||||||
* jQuery UI tweaks
|
|
||||||
******************************/
|
|
||||||
|
|
||||||
ul.ui-menu {
|
|
||||||
max-height: 30em;
|
|
||||||
}
|
|
||||||
|
|
||||||
/******************************
|
/******************************
|
||||||
* tweaks for root user
|
* tweaks for root user
|
||||||
******************************/
|
******************************/
|
||||||
|
|
||||||
.menubar .root-user .ui-button-text,
|
.navbar .navbar-end .navbar-link.root-user,
|
||||||
.menubar .root-user.ui-menu-item a {
|
.navbar .navbar-end .navbar-link.root-user:hover,
|
||||||
|
.navbar .navbar-end .navbar-link.root-user.is_active,
|
||||||
|
.navbar .navbar-end .navbar-item.root-user,
|
||||||
|
.navbar .navbar-end .navbar-item.root-user:hover,
|
||||||
|
.navbar .navbar-end .navbar-item.root-user.is_active {
|
||||||
background-color: red;
|
background-color: red;
|
||||||
color: black;
|
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
.menubar .root-user.ui-menu-item a {
|
|
||||||
padding-left: 1em;
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,28 +1,22 @@
|
||||||
|
|
||||||
/******************************
|
/******************************
|
||||||
* Filters
|
* Grid Filters
|
||||||
******************************/
|
******************************/
|
||||||
|
|
||||||
div.filters form {
|
.filters .filter {
|
||||||
margin-bottom: 10px;
|
margin-bottom: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.filters div.filter {
|
.filters .filter-fieldname .field,
|
||||||
margin-bottom: 10px;
|
.filters .filter-fieldname .field label {
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.filters div.filter label {
|
.filters .filter-fieldname .field label {
|
||||||
margin-right: 8px;
|
justify-content: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.filters div.filter select.filter-type {
|
.filters .filter-verb .select,
|
||||||
margin-right: 8px;
|
.filters .filter-verb .select select {
|
||||||
}
|
width: 100%;
|
||||||
|
|
||||||
div.filters div.filter div.value {
|
|
||||||
display: inline;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.filters div.buttons * {
|
|
||||||
margin-right: 8px;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,34 +1,37 @@
|
||||||
|
|
||||||
/******************************
|
/******************************
|
||||||
* Form Wrapper
|
* forms
|
||||||
******************************/
|
******************************/
|
||||||
|
|
||||||
div.form-wrapper {
|
/* note that this should only apply to "normal" primary forms */
|
||||||
overflow: auto;
|
/* TODO: replace this with bulma equivalent */
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/******************************
|
|
||||||
* Forms
|
|
||||||
******************************/
|
|
||||||
|
|
||||||
div.form,
|
|
||||||
div.fieldset-form,
|
|
||||||
div.fieldset {
|
|
||||||
clear: left;
|
|
||||||
float: left;
|
|
||||||
margin-top: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form {
|
.form {
|
||||||
padding-left: 5em;
|
padding-left: 5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* note that this should only apply to "normal" primary forms */
|
||||||
|
.form-wrapper .form .field.is-horizontal .field-label .label {
|
||||||
|
text-align: left;
|
||||||
|
white-space: nowrap;
|
||||||
|
width: 18em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* note that this should only apply to "normal" primary forms */
|
||||||
|
.form-wrapper .form .field.is-horizontal .field-body {
|
||||||
|
min-width: 30em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* note that this should only apply to "normal" primary forms */
|
||||||
|
.form-wrapper .form .field.is-horizontal .field-body .select,
|
||||||
|
.form-wrapper .form .field.is-horizontal .field-body .select select {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
/******************************
|
/******************************
|
||||||
* Fieldsets
|
* field-wrappers
|
||||||
******************************/
|
******************************/
|
||||||
|
|
||||||
|
/* TODO: replace this with bulma equivalent */
|
||||||
.field-wrapper {
|
.field-wrapper {
|
||||||
clear: both;
|
clear: both;
|
||||||
min-height: 30px;
|
min-height: 30px;
|
||||||
|
@ -36,16 +39,12 @@ div.fieldset {
|
||||||
margin: 15px;
|
margin: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.field-wrapper.with-error {
|
/* TODO: replace this with bulma equivalent */
|
||||||
background-color: #ddcccc;
|
|
||||||
border: 2px solid #dd6666;
|
|
||||||
padding-bottom: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.field-wrapper .field-row {
|
.field-wrapper .field-row {
|
||||||
display: table-row;
|
display: table-row;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* TODO: replace this with bulma equivalent */
|
||||||
.field-wrapper label {
|
.field-wrapper label {
|
||||||
display: table-cell;
|
display: table-cell;
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
|
@ -55,47 +54,8 @@ div.fieldset {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.field-wrapper.with-error label {
|
/* TODO: replace this with bulma equivalent */
|
||||||
padding-left: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.field-wrapper .field-error {
|
|
||||||
padding: 1em 0 0.5em 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.field-wrapper .field-error .error-msg {
|
|
||||||
color: #dd6666;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.field-wrapper .field {
|
.field-wrapper .field {
|
||||||
display: table-cell;
|
display: table-cell;
|
||||||
line-height: 25px;
|
line-height: 25px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.field-wrapper .field input[type=text],
|
|
||||||
.field-wrapper .field input[type=password],
|
|
||||||
.field-wrapper .field select,
|
|
||||||
.field-wrapper .field textarea {
|
|
||||||
width: 320px;
|
|
||||||
}
|
|
||||||
|
|
||||||
label input[type="checkbox"],
|
|
||||||
label input[type="radio"] {
|
|
||||||
margin-right: 0.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.field ul {
|
|
||||||
margin: 0px;
|
|
||||||
padding-left: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/******************************
|
|
||||||
* Buttons
|
|
||||||
******************************/
|
|
||||||
|
|
||||||
div.buttons {
|
|
||||||
clear: both;
|
|
||||||
margin: 10px 0px;
|
|
||||||
}
|
|
||||||
|
|
|
@ -261,6 +261,10 @@
|
||||||
* main actions
|
* main actions
|
||||||
******************************/
|
******************************/
|
||||||
|
|
||||||
|
a.grid-action {
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
.grid .actions {
|
.grid .actions {
|
||||||
width: 1px;
|
width: 1px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,40 +0,0 @@
|
||||||
.loadmask {
|
|
||||||
z-index: 100;
|
|
||||||
position: absolute;
|
|
||||||
top:0;
|
|
||||||
left:0;
|
|
||||||
-moz-opacity: 0.5;
|
|
||||||
opacity: .50;
|
|
||||||
filter: alpha(opacity=50);
|
|
||||||
background-color: #CCC;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
zoom: 1;
|
|
||||||
}
|
|
||||||
.loadmask-msg {
|
|
||||||
z-index: 20001;
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
border:1px solid #6593cf;
|
|
||||||
background: #c3daf9;
|
|
||||||
padding:2px;
|
|
||||||
}
|
|
||||||
.loadmask-msg div {
|
|
||||||
padding:5px 10px 5px 25px;
|
|
||||||
background: #fbfbfb url('../img/loading.gif') no-repeat 5px 5px;
|
|
||||||
line-height: 16px;
|
|
||||||
border:1px solid #a3bad9;
|
|
||||||
color:#222;
|
|
||||||
font:normal 11px tahoma, arial, helvetica, sans-serif;
|
|
||||||
cursor:wait;
|
|
||||||
}
|
|
||||||
.masked {
|
|
||||||
overflow: hidden !important;
|
|
||||||
}
|
|
||||||
.masked-relative {
|
|
||||||
position: relative !important;
|
|
||||||
}
|
|
||||||
.masked-hidden {
|
|
||||||
visibility: hidden !important;
|
|
||||||
}
|
|
15
tailbone/static/css/jquery.ui.menubar.css
vendored
15
tailbone/static/css/jquery.ui.menubar.css
vendored
|
@ -1,15 +0,0 @@
|
||||||
/*
|
|
||||||
* jQuery UI Menubar @VERSION
|
|
||||||
*
|
|
||||||
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
|
|
||||||
* Dual licensed under the MIT or GPL Version 2 licenses.
|
|
||||||
* http://jquery.org/license
|
|
||||||
*/
|
|
||||||
.ui-menubar { list-style: none; margin: 0; padding-left: 0; }
|
|
||||||
|
|
||||||
.ui-menubar-item { float: left; }
|
|
||||||
|
|
||||||
.ui-menubar .ui-button { float: left; font-weight: normal; border-top-width: 0 !important; border-bottom-width: 0 !important; margin: 0; outline: none; }
|
|
||||||
.ui-menubar .ui-menubar-link { border-right: 1px dashed transparent; border-left: 1px dashed transparent; }
|
|
||||||
|
|
||||||
.ui-menubar .ui-menu { width: 200px; position: absolute; z-index: 9999; font-weight: normal; }
|
|
14
tailbone/static/css/jquery.ui.tailbone.css
vendored
14
tailbone/static/css/jquery.ui.tailbone.css
vendored
|
@ -1,14 +0,0 @@
|
||||||
|
|
||||||
/**********************************************************************
|
|
||||||
* jquery.ui.tailbone.css
|
|
||||||
*
|
|
||||||
* jQuery UI tweaks for Tailbone
|
|
||||||
**********************************************************************/
|
|
||||||
|
|
||||||
.ui-widget {
|
|
||||||
font-size: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ui-menu-item a {
|
|
||||||
display: block;
|
|
||||||
}
|
|
57
tailbone/static/css/jquery.ui.timepicker.css
vendored
57
tailbone/static/css/jquery.ui.timepicker.css
vendored
|
@ -1,57 +0,0 @@
|
||||||
/*
|
|
||||||
* Timepicker stylesheet
|
|
||||||
* Highly inspired from datepicker
|
|
||||||
* FG - Nov 2010 - Web3R
|
|
||||||
*
|
|
||||||
* version 0.0.3 : Fixed some settings, more dynamic
|
|
||||||
* version 0.0.4 : Removed width:100% on tables
|
|
||||||
* version 0.1.1 : set width 0 on tables to fix an ie6 bug
|
|
||||||
*/
|
|
||||||
|
|
||||||
.ui-timepicker-inline { display: inline; }
|
|
||||||
|
|
||||||
#ui-timepicker-div { padding: 0.2em; }
|
|
||||||
.ui-timepicker-table { display: inline-table; width: 0; }
|
|
||||||
.ui-timepicker-table table { margin:0.15em 0 0 0; border-collapse: collapse; }
|
|
||||||
|
|
||||||
.ui-timepicker-hours, .ui-timepicker-minutes { padding: 0.2em; }
|
|
||||||
|
|
||||||
.ui-timepicker-table .ui-timepicker-title { line-height: 1.8em; text-align: center; }
|
|
||||||
.ui-timepicker-table td { padding: 0.1em; width: 2.2em; }
|
|
||||||
.ui-timepicker-table th.periods { padding: 0.1em; width: 2.2em; }
|
|
||||||
|
|
||||||
/* span for disabled cells */
|
|
||||||
.ui-timepicker-table td span {
|
|
||||||
display:block;
|
|
||||||
padding:0.2em 0.3em 0.2em 0.5em;
|
|
||||||
width: 1.2em;
|
|
||||||
|
|
||||||
text-align:right;
|
|
||||||
text-decoration:none;
|
|
||||||
}
|
|
||||||
/* anchors for clickable cells */
|
|
||||||
.ui-timepicker-table td a {
|
|
||||||
display:block;
|
|
||||||
padding:0.2em 0.3em 0.2em 0.5em;
|
|
||||||
width: 1.2em;
|
|
||||||
cursor: pointer;
|
|
||||||
text-align:right;
|
|
||||||
text-decoration:none;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* buttons and button pane styling */
|
|
||||||
.ui-timepicker .ui-timepicker-buttonpane {
|
|
||||||
background-image: none; margin: .7em 0 0 0; padding:0 .2em; border-left: 0; border-right: 0; border-bottom: 0;
|
|
||||||
}
|
|
||||||
.ui-timepicker .ui-timepicker-buttonpane button { margin: .5em .2em .4em; cursor: pointer; padding: .2em .6em .3em .6em; width:auto; overflow:visible; }
|
|
||||||
/* The close button */
|
|
||||||
.ui-timepicker .ui-timepicker-close { float: right }
|
|
||||||
|
|
||||||
/* the now button */
|
|
||||||
.ui-timepicker .ui-timepicker-now { float: left; }
|
|
||||||
|
|
||||||
/* the deselect button */
|
|
||||||
.ui-timepicker .ui-timepicker-deselect { float: left; }
|
|
||||||
|
|
||||||
|
|
|
@ -1,152 +1,90 @@
|
||||||
|
|
||||||
/******************************
|
/******************************
|
||||||
* Main Layout
|
* main layout
|
||||||
******************************/
|
******************************/
|
||||||
|
|
||||||
html, body, #body-wrapper {
|
body {
|
||||||
height: 100%;
|
display: flex;
|
||||||
}
|
flex-direction: column;
|
||||||
|
min-height: 100vh;
|
||||||
body > #body-wrapper {
|
|
||||||
height: auto;
|
|
||||||
min-height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
#body-wrapper {
|
|
||||||
margin: 0 1em;
|
|
||||||
width: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
#header {
|
|
||||||
height: 50px;
|
|
||||||
line-height: 50px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#body {
|
|
||||||
padding-top: 10px;
|
|
||||||
padding-bottom: 5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
#footer {
|
|
||||||
clear: both;
|
|
||||||
margin-top: -4em;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/******************************
|
|
||||||
* Header
|
|
||||||
******************************/
|
|
||||||
|
|
||||||
#header h1 {
|
|
||||||
float: left;
|
|
||||||
font-size: 25px;
|
|
||||||
margin: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#header div.login {
|
|
||||||
float: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* new stuff from 'better' theme begins here */
|
|
||||||
|
|
||||||
header .global {
|
|
||||||
background-color: #eaeaea;
|
|
||||||
height: 60px;
|
|
||||||
}
|
|
||||||
|
|
||||||
header .global a.home,
|
|
||||||
header .global a.global,
|
|
||||||
header .global span.global {
|
|
||||||
display: block;
|
|
||||||
float: left;
|
|
||||||
font-size: 2em;
|
|
||||||
font-weight: bold;
|
|
||||||
line-height: 60px;
|
|
||||||
margin-left: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
header .global a.home img {
|
|
||||||
display: block;
|
|
||||||
float: left;
|
|
||||||
padding: 5px 5px 5px 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
header .global .grid-nav {
|
|
||||||
display: inline-block;
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: bold;
|
|
||||||
line-height: 60px;
|
|
||||||
margin-left: 5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
header .global .grid-nav .ui-button,
|
|
||||||
header .global .grid-nav span.viewing {
|
|
||||||
margin-left: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
header .global .feedback {
|
|
||||||
float: right;
|
|
||||||
line-height: 60px;
|
|
||||||
margin-right: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
header .global .after-feedback {
|
|
||||||
float: right;
|
|
||||||
line-height: 60px;
|
|
||||||
margin-right: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
header .page {
|
|
||||||
border-bottom: 1px solid lightgrey;
|
|
||||||
padding: 0.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
header .page h1 {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0 0 0 0.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
/******************************
|
|
||||||
* Logo
|
|
||||||
******************************/
|
|
||||||
|
|
||||||
#logo {
|
|
||||||
display: block;
|
|
||||||
margin: 40px auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/****************************************
|
|
||||||
* content
|
|
||||||
****************************************/
|
|
||||||
|
|
||||||
body > #body-wrapper {
|
|
||||||
margin: 0px;
|
|
||||||
position: relative;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.content-wrapper {
|
.content-wrapper {
|
||||||
height: 100%;
|
display: flex;
|
||||||
padding-bottom: 30px;
|
flex: 1;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
|
|
||||||
#scrollpane {
|
|
||||||
height: 100%;
|
/******************************
|
||||||
|
* header
|
||||||
|
******************************/
|
||||||
|
|
||||||
|
/* this is the one in the very top left of screen, next to logo and linked to
|
||||||
|
the home page */
|
||||||
|
#global-header-title {
|
||||||
|
margin-left: 0.3rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
#scrollpane .inner-content {
|
header .level {
|
||||||
padding: 0 0.5em 0.5em 0.5em;
|
/* TODO: not sure what this 60px was supposed to do? but it broke the */
|
||||||
|
/* styles for the feedback dialog, so disabled it is.
|
||||||
|
/* height: 60px; */
|
||||||
|
/* line-height: 60px; */
|
||||||
|
padding-left: 0.5em;
|
||||||
|
padding-right: 0.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
header .level #header-logo {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
header .level .global-title,
|
||||||
|
header .level-left .global-title {
|
||||||
|
font-size: 2em;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* indent nested menu items a bit */
|
||||||
|
header .navbar-item.nested {
|
||||||
|
padding-left: 2.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
header span.header-text {
|
||||||
|
font-size: 2em;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
header .level .theme-picker {
|
||||||
|
display: inline-flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
#content-title {
|
||||||
|
padding: 0.3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
#content-title h1 {
|
||||||
|
font-size: 2rem;
|
||||||
|
margin-left: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************
|
||||||
|
* content
|
||||||
|
******************************/
|
||||||
|
|
||||||
|
#page-body {
|
||||||
|
padding: 0.4em;
|
||||||
|
}
|
||||||
|
|
||||||
/******************************
|
/******************************
|
||||||
* context menu
|
* context menu
|
||||||
******************************/
|
******************************/
|
||||||
|
|
||||||
#context-menu {
|
#context-menu {
|
||||||
list-style-type: none;
|
margin-bottom: 1em;
|
||||||
margin: 0.5em;
|
margin-left: 1em;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
@ -155,11 +93,19 @@ body > #body-wrapper {
|
||||||
* "object helper" panel
|
* "object helper" panel
|
||||||
******************************/
|
******************************/
|
||||||
|
|
||||||
|
.object-helpers .panel-heading {
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.object-helpers a {
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
.object-helper {
|
.object-helper {
|
||||||
border: 1px solid black;
|
border: 1px solid black;
|
||||||
margin: 1em;
|
margin: 1em;
|
||||||
padding: 1em;
|
padding: 1em;
|
||||||
min-width: 20em;
|
width: 20em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.object-helper-content {
|
.object-helper-content {
|
||||||
|
@ -167,87 +113,38 @@ body > #body-wrapper {
|
||||||
}
|
}
|
||||||
|
|
||||||
/******************************
|
/******************************
|
||||||
* Panels
|
* markdown
|
||||||
******************************/
|
******************************/
|
||||||
|
|
||||||
.panel,
|
.rendered-markdown p,
|
||||||
.panel-grid {
|
.rendered-markdown ul {
|
||||||
border-left: 1px solid Black;
|
margin-bottom: 1rem;
|
||||||
margin-bottom: 1em;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.panel {
|
.rendered-markdown .codehilite {
|
||||||
border-bottom: 1px solid Black;
|
margin-bottom: 2rem;
|
||||||
border-right: 1px solid Black;
|
|
||||||
padding: 0px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.panel h2,
|
/******************************
|
||||||
.panel-grid h2 {
|
* fix datepicker within modals
|
||||||
border-bottom: 1px solid Black;
|
* TODO: someday this may not be necessary? cf.
|
||||||
border-top: 1px solid Black;
|
* https://github.com/buefy/buefy/issues/292#issuecomment-347365637
|
||||||
padding: 5px;
|
******************************/
|
||||||
margin: 0px;
|
|
||||||
|
.modal .animation-content .modal-card {
|
||||||
|
overflow: visible !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.panel-grid h2 {
|
.modal-card-body {
|
||||||
border-right: 1px solid Black;
|
overflow: visible !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.panel-body {
|
|
||||||
overflow: auto;
|
|
||||||
padding: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/****************************************
|
|
||||||
* footer
|
|
||||||
****************************************/
|
|
||||||
|
|
||||||
#footer {
|
|
||||||
border-top: 1px solid lightgray;
|
|
||||||
bottom: 0;
|
|
||||||
font-size: 9pt;
|
|
||||||
height: 20px;
|
|
||||||
left: 0;
|
|
||||||
line-height: 20px;
|
|
||||||
margin: 0;
|
|
||||||
position: absolute;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
/******************************
|
/******************************
|
||||||
* feedback
|
* feedback
|
||||||
******************************/
|
******************************/
|
||||||
|
|
||||||
#feedback-dialog {
|
.feedback-dialog .red {
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
#feedback-dialog p {
|
|
||||||
margin-top: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
#feedback-dialog .red {
|
|
||||||
color: red;
|
color: red;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
#feedback-dialog .field-wrapper {
|
|
||||||
margin-top: 1em;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#feedback-dialog .field {
|
|
||||||
margin-bottom: 0;
|
|
||||||
margin-top: 0.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
#feedback-dialog .referrer .field {
|
|
||||||
clear: both;
|
|
||||||
float: none;
|
|
||||||
margin-top: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
#feedback-dialog textarea {
|
|
||||||
width: auto;
|
|
||||||
}
|
|
||||||
|
|
452
tailbone/static/js/jquery.ui.tailbone.js
vendored
452
tailbone/static/js/jquery.ui.tailbone.js
vendored
|
@ -1,452 +0,0 @@
|
||||||
|
|
||||||
/**********************************************************************
|
|
||||||
* jQuery UI plugins for Tailbone
|
|
||||||
**********************************************************************/
|
|
||||||
|
|
||||||
/**********************************************************************
|
|
||||||
* gridcore plugin
|
|
||||||
**********************************************************************/
|
|
||||||
|
|
||||||
(function($) {
|
|
||||||
|
|
||||||
$.widget('tailbone.gridcore', {
|
|
||||||
|
|
||||||
_create: function() {
|
|
||||||
|
|
||||||
var that = this;
|
|
||||||
|
|
||||||
// Add hover highlight effect to grid rows during mouse-over.
|
|
||||||
// this.element.on('mouseenter', 'tbody tr:not(.header)', function() {
|
|
||||||
this.element.on('mouseenter', 'tr:not(.header)', function() {
|
|
||||||
$(this).addClass('hovering');
|
|
||||||
});
|
|
||||||
// this.element.on('mouseleave', 'tbody tr:not(.header)', function() {
|
|
||||||
this.element.on('mouseleave', 'tr:not(.header)', function() {
|
|
||||||
$(this).removeClass('hovering');
|
|
||||||
});
|
|
||||||
|
|
||||||
// do some extra stuff for grids with checkboxes
|
|
||||||
|
|
||||||
// mark rows selected on page load, as needed
|
|
||||||
this.element.find('tr:not(.header) td.checkbox :checkbox:checked').each(function() {
|
|
||||||
$(this).parents('tr:first').addClass('selected');
|
|
||||||
});
|
|
||||||
|
|
||||||
// (un-)check all rows when clicking check-all box in header
|
|
||||||
if (this.element.find('tr.header td.checkbox :checkbox').length) {
|
|
||||||
this.element.on('click', 'tr.header td.checkbox :checkbox', function() {
|
|
||||||
var checked = $(this).prop('checked');
|
|
||||||
var rows = that.element.find('tr:not(.header)');
|
|
||||||
rows.find('td.checkbox :checkbox').prop('checked', checked);
|
|
||||||
if (checked) {
|
|
||||||
rows.addClass('selected');
|
|
||||||
} else {
|
|
||||||
rows.removeClass('selected');
|
|
||||||
}
|
|
||||||
that.element.trigger('gridchecked', that.count_selected());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// when row with checkbox is clicked, toggle selected status,
|
|
||||||
// unless clicking checkbox (since that already toggles it) or a
|
|
||||||
// link (since that does something completely different)
|
|
||||||
this.element.on('click', 'tr:not(.header)', function(event) {
|
|
||||||
var el = $(event.target);
|
|
||||||
if (!el.is('a') && !el.is(':checkbox')) {
|
|
||||||
$(this).find('td.checkbox :checkbox').click();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.element.on('change', 'tr:not(.header) td.checkbox :checkbox', function() {
|
|
||||||
if (this.checked) {
|
|
||||||
$(this).parents('tr:first').addClass('selected');
|
|
||||||
} else {
|
|
||||||
$(this).parents('tr:first').removeClass('selected');
|
|
||||||
}
|
|
||||||
that.element.trigger('gridchecked', that.count_selected());
|
|
||||||
});
|
|
||||||
|
|
||||||
// Show 'more' actions when user hovers over 'more' link.
|
|
||||||
this.element.on('mouseenter', '.actions a.more', function() {
|
|
||||||
that.element.find('.actions div.more').hide();
|
|
||||||
$(this).siblings('div.more')
|
|
||||||
.show()
|
|
||||||
.position({my: 'left-5 top-4', at: 'left top', of: $(this)});
|
|
||||||
});
|
|
||||||
this.element.on('mouseleave', '.actions div.more', function() {
|
|
||||||
$(this).hide();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Add speed bump for "Delete Row" action, if grid is so configured.
|
|
||||||
if (this.element.data('delete-speedbump')) {
|
|
||||||
this.element.on('click', 'tr:not(.header) .actions a.delete', function() {
|
|
||||||
return confirm("Are you sure you wish to delete this object?");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
count_selected: function() {
|
|
||||||
return this.element.find('tr:not(.header) td.checkbox :checkbox:checked').length;
|
|
||||||
},
|
|
||||||
|
|
||||||
// TODO: deprecate / remove this?
|
|
||||||
count_checked: function() {
|
|
||||||
return this.count_selected();
|
|
||||||
},
|
|
||||||
|
|
||||||
selected_rows: function() {
|
|
||||||
return this.element.find('tr:not(.header) td.checkbox :checkbox:checked').parents('tr:first');
|
|
||||||
},
|
|
||||||
|
|
||||||
all_uuids: function() {
|
|
||||||
var uuids = [];
|
|
||||||
this.element.find('tr:not(.header)').each(function() {
|
|
||||||
uuids.push($(this).data('uuid'));
|
|
||||||
});
|
|
||||||
return uuids;
|
|
||||||
},
|
|
||||||
|
|
||||||
selected_uuids: function() {
|
|
||||||
var uuids = [];
|
|
||||||
this.element.find('tr:not(.header) td.checkbox :checkbox:checked').each(function() {
|
|
||||||
uuids.push($(this).parents('tr:first').data('uuid'));
|
|
||||||
});
|
|
||||||
return uuids;
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
})( jQuery );
|
|
||||||
|
|
||||||
|
|
||||||
/**********************************************************************
|
|
||||||
* gridwrapper plugin
|
|
||||||
**********************************************************************/
|
|
||||||
|
|
||||||
(function($) {
|
|
||||||
|
|
||||||
$.widget('tailbone.gridwrapper', {
|
|
||||||
|
|
||||||
_create: function() {
|
|
||||||
|
|
||||||
var that = this;
|
|
||||||
|
|
||||||
// Snag some element references.
|
|
||||||
this.filters = this.element.find('.newfilters');
|
|
||||||
this.filters_form = this.filters.find('form');
|
|
||||||
this.add_filter = this.filters.find('#add-filter');
|
|
||||||
this.apply_filters = this.filters.find('#apply-filters');
|
|
||||||
this.default_filters = this.filters.find('#default-filters');
|
|
||||||
this.clear_filters = this.filters.find('#clear-filters');
|
|
||||||
this.save_defaults = this.filters.find('#save-defaults');
|
|
||||||
this.grid = this.element.find('.grid');
|
|
||||||
|
|
||||||
// add standard grid behavior
|
|
||||||
this.grid.gridcore();
|
|
||||||
|
|
||||||
// Enhance filters etc.
|
|
||||||
this.filters.find('.filter').gridfilter();
|
|
||||||
this.apply_filters.button('option', 'icons', {primary: 'ui-icon-search'});
|
|
||||||
this.default_filters.button('option', 'icons', {primary: 'ui-icon-home'});
|
|
||||||
this.clear_filters.button('option', 'icons', {primary: 'ui-icon-trash'});
|
|
||||||
this.save_defaults.button('option', 'icons', {primary: 'ui-icon-disk'});
|
|
||||||
if (! this.filters.find('.active:checked').length) {
|
|
||||||
this.apply_filters.button('disable');
|
|
||||||
}
|
|
||||||
this.add_filter.selectmenu({
|
|
||||||
width: '15em',
|
|
||||||
|
|
||||||
// Initially disabled if contains no enabled filter options.
|
|
||||||
disabled: this.add_filter.find('option:enabled').length == 1,
|
|
||||||
|
|
||||||
// When add-filter choice is made, show/focus new filter value input,
|
|
||||||
// and maybe hide the add-filter selection or show the apply button.
|
|
||||||
change: function (event, ui) {
|
|
||||||
var filter = that.filters.find('#filter-' + ui.item.value);
|
|
||||||
var select = $(this);
|
|
||||||
var option = ui.item.element;
|
|
||||||
filter.gridfilter('active', true);
|
|
||||||
filter.gridfilter('focus');
|
|
||||||
select.val('');
|
|
||||||
option.attr('disabled', 'disabled');
|
|
||||||
select.selectmenu('refresh');
|
|
||||||
if (select.find('option:enabled').length == 1) { // prompt is always enabled
|
|
||||||
select.selectmenu('disable');
|
|
||||||
}
|
|
||||||
that.apply_filters.button('enable');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.add_filter.on('selectmenuopen', function(event, ui) {
|
|
||||||
show_all_options($(this));
|
|
||||||
});
|
|
||||||
|
|
||||||
// Intercept filters form submittal, and submit via AJAX instead.
|
|
||||||
this.filters_form.on('submit', function() {
|
|
||||||
var settings = {filter: true, partial: true};
|
|
||||||
if (that.filters_form.find('input[name="save-current-filters-as-defaults"]').val() == 'true') {
|
|
||||||
settings['save-current-filters-as-defaults'] = true;
|
|
||||||
}
|
|
||||||
that.filters.find('.filter').each(function() {
|
|
||||||
|
|
||||||
// currently active filters will be included in form data
|
|
||||||
if ($(this).gridfilter('active')) {
|
|
||||||
settings[$(this).data('key')] = $(this).gridfilter('value');
|
|
||||||
settings[$(this).data('key') + '.verb'] = $(this).gridfilter('verb');
|
|
||||||
|
|
||||||
// others will be hidden from view
|
|
||||||
} else {
|
|
||||||
$(this).gridfilter('hide');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// if no filters are visible, disable submit button
|
|
||||||
if (! that.filters.find('.filter:visible').length) {
|
|
||||||
that.apply_filters.button('disable');
|
|
||||||
}
|
|
||||||
|
|
||||||
// okay, submit filters to server and refresh grid
|
|
||||||
that.refresh(settings);
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
|
|
||||||
// When user clicks Default Filters button, refresh page with
|
|
||||||
// instructions for the server to reset filters to default settings.
|
|
||||||
this.default_filters.click(function() {
|
|
||||||
that.filters_form.off('submit');
|
|
||||||
that.filters_form.find('input[name="reset-to-default-filters"]').val('true');
|
|
||||||
that.element.mask("Refreshing data...");
|
|
||||||
that.filters_form.get(0).submit();
|
|
||||||
});
|
|
||||||
|
|
||||||
// When user clicks Save Defaults button, refresh the grid as with
|
|
||||||
// Apply Filters, but add an instruction for the server to save
|
|
||||||
// current settings as defaults for the user.
|
|
||||||
this.save_defaults.click(function() {
|
|
||||||
that.filters_form.find('input[name="save-current-filters-as-defaults"]').val('true');
|
|
||||||
that.filters_form.submit();
|
|
||||||
that.filters_form.find('input[name="save-current-filters-as-defaults"]').val('false');
|
|
||||||
});
|
|
||||||
|
|
||||||
// When user clicks Clear Filters button, deactivate all filters
|
|
||||||
// and refresh the grid.
|
|
||||||
this.clear_filters.click(function() {
|
|
||||||
that.filters.find('.filter').each(function() {
|
|
||||||
if ($(this).gridfilter('active')) {
|
|
||||||
$(this).gridfilter('active', false);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
that.filters_form.submit();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Refresh data when user clicks a sortable column header.
|
|
||||||
this.element.on('click', 'tr.header a', function() {
|
|
||||||
var td = $(this).parent();
|
|
||||||
var data = {
|
|
||||||
sortkey: $(this).data('sortkey'),
|
|
||||||
sortdir: (td.hasClass('asc')) ? 'desc' : 'asc',
|
|
||||||
page: 1,
|
|
||||||
partial: true
|
|
||||||
};
|
|
||||||
that.refresh(data);
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Refresh data when user chooses a new page size setting.
|
|
||||||
this.element.on('change', '.pager #pagesize', function() {
|
|
||||||
var settings = {
|
|
||||||
partial: true,
|
|
||||||
pagesize: $(this).val()
|
|
||||||
};
|
|
||||||
that.refresh(settings);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Refresh data when user clicks a pager link.
|
|
||||||
this.element.on('click', '.pager a', function() {
|
|
||||||
that.refresh(this.search.substring(1)); // remove leading '?'
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
// Refreshes the visible data within the grid, according to the given settings.
|
|
||||||
refresh: function(settings) {
|
|
||||||
var that = this;
|
|
||||||
this.element.mask("Refreshing data...");
|
|
||||||
$.get(this.grid.data('url'), settings, function(data) {
|
|
||||||
that.grid.replaceWith(data);
|
|
||||||
that.grid = that.element.find('.grid');
|
|
||||||
that.grid.gridcore();
|
|
||||||
that.element.unmask();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
results_count: function(as_text) {
|
|
||||||
var count = null;
|
|
||||||
var match = /showing \d+ thru \d+ of (\S+)/.exec(this.element.find('.pager .showing').text());
|
|
||||||
if (match) {
|
|
||||||
count = match[1];
|
|
||||||
if (!as_text) {
|
|
||||||
count = parseInt(count, 10);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return count;
|
|
||||||
},
|
|
||||||
|
|
||||||
all_uuids: function() {
|
|
||||||
return this.grid.gridcore('all_uuids');
|
|
||||||
},
|
|
||||||
|
|
||||||
selected_uuids: function() {
|
|
||||||
return this.grid.gridcore('selected_uuids');
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
})( jQuery );
|
|
||||||
|
|
||||||
|
|
||||||
/**********************************************************************
|
|
||||||
* gridfilter plugin
|
|
||||||
**********************************************************************/
|
|
||||||
|
|
||||||
(function($) {
|
|
||||||
|
|
||||||
$.widget('tailbone.gridfilter', {
|
|
||||||
|
|
||||||
_create: function() {
|
|
||||||
|
|
||||||
var that = this;
|
|
||||||
|
|
||||||
// Track down some important elements.
|
|
||||||
this.checkbox = this.element.find('input[name$="-active"]');
|
|
||||||
this.label = this.element.find('label');
|
|
||||||
this.inputs = this.element.find('.inputs');
|
|
||||||
this.add_filter = this.element.parents('.grid-wrapper').find('#add-filter');
|
|
||||||
|
|
||||||
// Hide the checkbox and label, and add button for toggling active status.
|
|
||||||
this.checkbox.addClass('ui-helper-hidden-accessible');
|
|
||||||
this.label.hide();
|
|
||||||
this.activebutton = $('<button type="button" class="toggle" />')
|
|
||||||
.insertAfter(this.label)
|
|
||||||
.text(this.label.text())
|
|
||||||
.button({
|
|
||||||
icons: {primary: 'ui-icon-blank'}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Enhance verb dropdown as selectmenu.
|
|
||||||
this.verb_select = this.inputs.find('.verb');
|
|
||||||
this.valueless_verbs = {};
|
|
||||||
$.each(this.verb_select.data('hide-value-for').split(' '), function(index, value) {
|
|
||||||
that.valueless_verbs[value] = true;
|
|
||||||
});
|
|
||||||
this.verb_select.selectmenu({
|
|
||||||
width: '15em',
|
|
||||||
change: function(event, ui) {
|
|
||||||
if (ui.item.value in that.valueless_verbs) {
|
|
||||||
that.inputs.find('.value').hide();
|
|
||||||
} else {
|
|
||||||
that.inputs.find('.value').show();
|
|
||||||
that.focus();
|
|
||||||
that.select();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.verb_select.on('selectmenuopen', function(event, ui) {
|
|
||||||
show_all_options($(this));
|
|
||||||
});
|
|
||||||
|
|
||||||
// Enhance any date values with datepicker widget.
|
|
||||||
this.inputs.find('.value input[data-datepicker="true"]').datepicker({
|
|
||||||
dateFormat: 'yy-mm-dd',
|
|
||||||
changeYear: true,
|
|
||||||
changeMonth: true
|
|
||||||
});
|
|
||||||
|
|
||||||
// Enhance any choice/dropdown values with selectmenu.
|
|
||||||
this.inputs.find('.value select').selectmenu({
|
|
||||||
// provide sane width for value dropdown
|
|
||||||
width: '15em'
|
|
||||||
});
|
|
||||||
|
|
||||||
this.inputs.find('.value select').on('selectmenuopen', function(event, ui) {
|
|
||||||
show_all_options($(this));
|
|
||||||
});
|
|
||||||
|
|
||||||
// Listen for button click, to keep checkbox in sync.
|
|
||||||
this._on(this.activebutton, {
|
|
||||||
click: function(e) {
|
|
||||||
var checked = !this.checkbox.is(':checked');
|
|
||||||
this.checkbox.prop('checked', checked);
|
|
||||||
this.refresh();
|
|
||||||
if (checked) {
|
|
||||||
this.focus();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Update the initial state of the button according to checkbox.
|
|
||||||
this.refresh();
|
|
||||||
},
|
|
||||||
|
|
||||||
refresh: function() {
|
|
||||||
if (this.checkbox.is(':checked')) {
|
|
||||||
this.activebutton.button('option', 'icons', {primary: 'ui-icon-check'});
|
|
||||||
if (this.verb() in this.valueless_verbs) {
|
|
||||||
this.inputs.find('.value').hide();
|
|
||||||
} else {
|
|
||||||
this.inputs.find('.value').show();
|
|
||||||
}
|
|
||||||
this.inputs.show();
|
|
||||||
} else {
|
|
||||||
this.activebutton.button('option', 'icons', {primary: 'ui-icon-blank'});
|
|
||||||
this.inputs.hide();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
active: function(value) {
|
|
||||||
if (value === undefined) {
|
|
||||||
return this.checkbox.is(':checked');
|
|
||||||
}
|
|
||||||
if (value) {
|
|
||||||
if (!this.checkbox.is(':checked')) {
|
|
||||||
this.checkbox.prop('checked', true);
|
|
||||||
this.refresh();
|
|
||||||
this.element.show();
|
|
||||||
}
|
|
||||||
} else if (this.checkbox.is(':checked')) {
|
|
||||||
this.checkbox.prop('checked', false);
|
|
||||||
this.refresh();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
hide: function() {
|
|
||||||
this.active(false);
|
|
||||||
this.element.hide();
|
|
||||||
var option = this.add_filter.find('option[value="' + this.element.data('key') + '"]');
|
|
||||||
option.attr('disabled', false);
|
|
||||||
if (this.add_filter.selectmenu('option', 'disabled')) {
|
|
||||||
this.add_filter.selectmenu('enable');
|
|
||||||
}
|
|
||||||
this.add_filter.selectmenu('refresh');
|
|
||||||
},
|
|
||||||
|
|
||||||
focus: function() {
|
|
||||||
this.inputs.find('.value input').focus();
|
|
||||||
},
|
|
||||||
|
|
||||||
select: function() {
|
|
||||||
this.inputs.find('.value input').select();
|
|
||||||
},
|
|
||||||
|
|
||||||
value: function() {
|
|
||||||
return this.inputs.find('.value input, .value select').val();
|
|
||||||
},
|
|
||||||
|
|
||||||
verb: function() {
|
|
||||||
return this.inputs.find('.verb').val();
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
})( jQuery );
|
|
10
tailbone/static/js/lib/jquery.loadmask.min.js
vendored
10
tailbone/static/js/lib/jquery.loadmask.min.js
vendored
|
@ -1,10 +0,0 @@
|
||||||
/**
|
|
||||||
* Copyright (c) 2009 Sergiy Kovalchuk (serg472@gmail.com)
|
|
||||||
*
|
|
||||||
* Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)
|
|
||||||
* and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses.
|
|
||||||
*
|
|
||||||
* Following code is based on Element.mask() implementation from ExtJS framework (http://extjs.com/)
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
(function(a){a.fn.mask=function(c,b){a(this).each(function(){if(b!==undefined&&b>0){var d=a(this);d.data("_mask_timeout",setTimeout(function(){a.maskElement(d,c)},b))}else{a.maskElement(a(this),c)}})};a.fn.unmask=function(){a(this).each(function(){a.unmaskElement(a(this))})};a.fn.isMasked=function(){return this.hasClass("masked")};a.maskElement=function(d,c){if(d.data("_mask_timeout")!==undefined){clearTimeout(d.data("_mask_timeout"));d.removeData("_mask_timeout")}if(d.isMasked()){a.unmaskElement(d)}if(d.css("position")=="static"){d.addClass("masked-relative")}d.addClass("masked");var e=a('<div class="loadmask"></div>');if(navigator.userAgent.toLowerCase().indexOf("msie")>-1){e.height(d.height()+parseInt(d.css("padding-top"))+parseInt(d.css("padding-bottom")));e.width(d.width()+parseInt(d.css("padding-left"))+parseInt(d.css("padding-right")))}if(navigator.userAgent.toLowerCase().indexOf("msie 6")>-1){d.find("select").addClass("masked-hidden")}d.append(e);if(c!==undefined){var b=a('<div class="loadmask-msg" style="display:none;"></div>');b.append("<div>"+c+"</div>");d.append(b);b.css("top",Math.round(d.height()/2-(b.height()-parseInt(b.css("padding-top"))-parseInt(b.css("padding-bottom")))/2)+"px");b.css("left",Math.round(d.width()/2-(b.width()-parseInt(b.css("padding-left"))-parseInt(b.css("padding-right")))/2)+"px");b.show()}};a.unmaskElement=function(b){if(b.data("_mask_timeout")!==undefined){clearTimeout(b.data("_mask_timeout"));b.removeData("_mask_timeout")}b.find(".loadmask-msg,.loadmask").remove();b.removeClass("masked");b.removeClass("masked-relative");b.find("select").removeClass("masked-hidden")}})(jQuery);
|
|
331
tailbone/static/js/lib/jquery.ui.menubar.js
vendored
331
tailbone/static/js/lib/jquery.ui.menubar.js
vendored
|
@ -1,331 +0,0 @@
|
||||||
/*
|
|
||||||
* jQuery UI Menubar @VERSION
|
|
||||||
*
|
|
||||||
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
|
|
||||||
* Dual licensed under the MIT or GPL Version 2 licenses.
|
|
||||||
* http://jquery.org/license
|
|
||||||
*
|
|
||||||
* http://docs.jquery.com/UI/Menubar
|
|
||||||
*
|
|
||||||
* Depends:
|
|
||||||
* jquery.ui.core.js
|
|
||||||
* jquery.ui.widget.js
|
|
||||||
* jquery.ui.position.js
|
|
||||||
* jquery.ui.menu.js
|
|
||||||
*/
|
|
||||||
(function( $ ) {
|
|
||||||
|
|
||||||
// TODO when mixing clicking menus and keyboard navigation, focus handling is broken
|
|
||||||
// there has to be just one item that has tabindex
|
|
||||||
$.widget( "ui.menubar", {
|
|
||||||
version: "@VERSION",
|
|
||||||
options: {
|
|
||||||
autoExpand: false,
|
|
||||||
buttons: false,
|
|
||||||
items: "li",
|
|
||||||
menuElement: "ul",
|
|
||||||
menuIcon: false,
|
|
||||||
position: {
|
|
||||||
my: "left top",
|
|
||||||
at: "left bottom"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
_create: function() {
|
|
||||||
var that = this;
|
|
||||||
this.menuItems = this.element.children( this.options.items );
|
|
||||||
this.items = this.menuItems.children( "button, a" );
|
|
||||||
|
|
||||||
this.menuItems
|
|
||||||
.addClass( "ui-menubar-item" )
|
|
||||||
.attr( "role", "presentation" );
|
|
||||||
// let only the first item receive focus
|
|
||||||
this.items.slice(1).attr( "tabIndex", -1 );
|
|
||||||
|
|
||||||
this.element
|
|
||||||
.addClass( "ui-menubar ui-widget-header ui-helper-clearfix" )
|
|
||||||
.attr( "role", "menubar" );
|
|
||||||
this._focusable( this.items );
|
|
||||||
this._hoverable( this.items );
|
|
||||||
this.items.siblings( this.options.menuElement )
|
|
||||||
.menu({
|
|
||||||
position: {
|
|
||||||
within: this.options.position.within
|
|
||||||
},
|
|
||||||
select: function( event, ui ) {
|
|
||||||
ui.item.parents( "ul.ui-menu:last" ).hide();
|
|
||||||
that._close();
|
|
||||||
// TODO what is this targetting? there's probably a better way to access it
|
|
||||||
$(event.target).prev().focus();
|
|
||||||
that._trigger( "select", event, ui );
|
|
||||||
},
|
|
||||||
menus: that.options.menuElement
|
|
||||||
})
|
|
||||||
.hide()
|
|
||||||
.attr({
|
|
||||||
"aria-hidden": "true",
|
|
||||||
"aria-expanded": "false"
|
|
||||||
})
|
|
||||||
// TODO use _on
|
|
||||||
.bind( "keydown.menubar", function( event ) {
|
|
||||||
var menu = $( this );
|
|
||||||
if ( menu.is( ":hidden" ) ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
switch ( event.keyCode ) {
|
|
||||||
case $.ui.keyCode.LEFT:
|
|
||||||
that.previous( event );
|
|
||||||
event.preventDefault();
|
|
||||||
break;
|
|
||||||
case $.ui.keyCode.RIGHT:
|
|
||||||
that.next( event );
|
|
||||||
event.preventDefault();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
this.items.each(function() {
|
|
||||||
var input = $(this),
|
|
||||||
// TODO menu var is only used on two places, doesn't quite justify the .each
|
|
||||||
menu = input.next( that.options.menuElement );
|
|
||||||
|
|
||||||
// might be a non-menu button
|
|
||||||
if ( menu.length ) {
|
|
||||||
// TODO use _on
|
|
||||||
input.bind( "click.menubar focus.menubar mouseenter.menubar", function( event ) {
|
|
||||||
// ignore triggered focus event
|
|
||||||
if ( event.type === "focus" && !event.originalEvent ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
event.preventDefault();
|
|
||||||
// TODO can we simplify or extractthis check? especially the last two expressions
|
|
||||||
// there's a similar active[0] == menu[0] check in _open
|
|
||||||
if ( event.type === "click" && menu.is( ":visible" ) && that.active && that.active[0] === menu[0] ) {
|
|
||||||
that._close();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if ( ( that.open && event.type === "mouseenter" ) || event.type === "click" || that.options.autoExpand ) {
|
|
||||||
if( that.options.autoExpand ) {
|
|
||||||
clearTimeout( that.closeTimer );
|
|
||||||
}
|
|
||||||
|
|
||||||
that._open( event, menu );
|
|
||||||
}
|
|
||||||
})
|
|
||||||
// TODO use _on
|
|
||||||
.bind( "keydown", function( event ) {
|
|
||||||
switch ( event.keyCode ) {
|
|
||||||
case $.ui.keyCode.SPACE:
|
|
||||||
case $.ui.keyCode.UP:
|
|
||||||
case $.ui.keyCode.DOWN:
|
|
||||||
that._open( event, $( this ).next() );
|
|
||||||
event.preventDefault();
|
|
||||||
break;
|
|
||||||
case $.ui.keyCode.LEFT:
|
|
||||||
that.previous( event );
|
|
||||||
event.preventDefault();
|
|
||||||
break;
|
|
||||||
case $.ui.keyCode.RIGHT:
|
|
||||||
that.next( event );
|
|
||||||
event.preventDefault();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.attr( "aria-haspopup", "true" );
|
|
||||||
|
|
||||||
// TODO review if these options (menuIcon and buttons) are a good choice, maybe they can be merged
|
|
||||||
if ( that.options.menuIcon ) {
|
|
||||||
input.addClass( "ui-state-default" ).append( "<span class='ui-button-icon-secondary ui-icon ui-icon-triangle-1-s'></span>" );
|
|
||||||
input.removeClass( "ui-button-text-only" ).addClass( "ui-button-text-icon-secondary" );
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// TODO use _on
|
|
||||||
input.bind( "click.menubar mouseenter.menubar", function( event ) {
|
|
||||||
if ( ( that.open && event.type === "mouseenter" ) || event.type === "click" ) {
|
|
||||||
that._close();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
input
|
|
||||||
.addClass( "ui-button ui-widget ui-button-text-only ui-menubar-link" )
|
|
||||||
.attr( "role", "menuitem" )
|
|
||||||
.wrapInner( "<span class='ui-button-text'></span>" );
|
|
||||||
|
|
||||||
if ( that.options.buttons ) {
|
|
||||||
input.removeClass( "ui-menubar-link" ).addClass( "ui-state-default" );
|
|
||||||
}
|
|
||||||
});
|
|
||||||
that._on( {
|
|
||||||
keydown: function( event ) {
|
|
||||||
if ( event.keyCode === $.ui.keyCode.ESCAPE && that.active && that.active.menu( "collapse", event ) !== true ) {
|
|
||||||
var active = that.active;
|
|
||||||
that.active.blur();
|
|
||||||
that._close( event );
|
|
||||||
active.prev().focus();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
focusin: function( event ) {
|
|
||||||
clearTimeout( that.closeTimer );
|
|
||||||
},
|
|
||||||
focusout: function( event ) {
|
|
||||||
that.closeTimer = setTimeout( function() {
|
|
||||||
that._close( event );
|
|
||||||
}, 150);
|
|
||||||
},
|
|
||||||
"mouseleave .ui-menubar-item": function( event ) {
|
|
||||||
if ( that.options.autoExpand ) {
|
|
||||||
that.closeTimer = setTimeout( function() {
|
|
||||||
that._close( event );
|
|
||||||
}, 150);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"mouseenter .ui-menubar-item": function( event ) {
|
|
||||||
clearTimeout( that.closeTimer );
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Keep track of open submenus
|
|
||||||
this.openSubmenus = 0;
|
|
||||||
},
|
|
||||||
|
|
||||||
_destroy : function() {
|
|
||||||
this.menuItems
|
|
||||||
.removeClass( "ui-menubar-item" )
|
|
||||||
.removeAttr( "role" );
|
|
||||||
|
|
||||||
this.element
|
|
||||||
.removeClass( "ui-menubar ui-widget-header ui-helper-clearfix" )
|
|
||||||
.removeAttr( "role" )
|
|
||||||
.unbind( ".menubar" );
|
|
||||||
|
|
||||||
this.items
|
|
||||||
.unbind( ".menubar" )
|
|
||||||
.removeClass( "ui-button ui-widget ui-button-text-only ui-menubar-link ui-state-default" )
|
|
||||||
.removeAttr( "role" )
|
|
||||||
.removeAttr( "aria-haspopup" )
|
|
||||||
// TODO unwrap?
|
|
||||||
.children( "span.ui-button-text" ).each(function( i, e ) {
|
|
||||||
var item = $( this );
|
|
||||||
item.parent().html( item.html() );
|
|
||||||
})
|
|
||||||
.end()
|
|
||||||
.children( ".ui-icon" ).remove();
|
|
||||||
|
|
||||||
this.element.find( ":ui-menu" )
|
|
||||||
.menu( "destroy" )
|
|
||||||
.show()
|
|
||||||
.removeAttr( "aria-hidden" )
|
|
||||||
.removeAttr( "aria-expanded" )
|
|
||||||
.removeAttr( "tabindex" )
|
|
||||||
.unbind( ".menubar" );
|
|
||||||
},
|
|
||||||
|
|
||||||
_close: function() {
|
|
||||||
if ( !this.active || !this.active.length ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.active
|
|
||||||
.menu( "collapseAll" )
|
|
||||||
.hide()
|
|
||||||
.attr({
|
|
||||||
"aria-hidden": "true",
|
|
||||||
"aria-expanded": "false"
|
|
||||||
});
|
|
||||||
this.active
|
|
||||||
.prev()
|
|
||||||
.removeClass( "ui-state-active" )
|
|
||||||
.removeAttr( "tabIndex" );
|
|
||||||
this.active = null;
|
|
||||||
this.open = false;
|
|
||||||
this.openSubmenus = 0;
|
|
||||||
},
|
|
||||||
|
|
||||||
_open: function( event, menu ) {
|
|
||||||
// on a single-button menubar, ignore reopening the same menu
|
|
||||||
if ( this.active && this.active[0] === menu[0] ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// TODO refactor, almost the same as _close above, but don't remove tabIndex
|
|
||||||
if ( this.active ) {
|
|
||||||
this.active
|
|
||||||
.menu( "collapseAll" )
|
|
||||||
.hide()
|
|
||||||
.attr({
|
|
||||||
"aria-hidden": "true",
|
|
||||||
"aria-expanded": "false"
|
|
||||||
});
|
|
||||||
this.active
|
|
||||||
.prev()
|
|
||||||
.removeClass( "ui-state-active" );
|
|
||||||
}
|
|
||||||
// set tabIndex -1 to have the button skipped on shift-tab when menu is open (it gets focus)
|
|
||||||
var button = menu.prev().addClass( "ui-state-active" ).attr( "tabIndex", -1 );
|
|
||||||
this.active = menu
|
|
||||||
.show()
|
|
||||||
.position( $.extend({
|
|
||||||
of: button
|
|
||||||
}, this.options.position ) )
|
|
||||||
.removeAttr( "aria-hidden" )
|
|
||||||
.attr( "aria-expanded", "true" )
|
|
||||||
.menu("focus", event, menu.children( ".ui-menu-item" ).first() )
|
|
||||||
// TODO need a comment here why both events are triggered
|
|
||||||
// TODO: heh well given the above comment i'm not sure what the
|
|
||||||
// implications might be for disabling the focus() call..but it
|
|
||||||
// messes with text input focus in undesirable ways..so disable it
|
|
||||||
// we will..until we know why we shouldn't
|
|
||||||
// .focus()
|
|
||||||
.focusin();
|
|
||||||
this.open = true;
|
|
||||||
},
|
|
||||||
|
|
||||||
next: function( event ) {
|
|
||||||
if ( this.open && this.active.data( "menu" ).active.has( ".ui-menu" ).length ) {
|
|
||||||
// Track number of open submenus and prevent moving to next menubar item
|
|
||||||
this.openSubmenus++;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.openSubmenus = 0;
|
|
||||||
this._move( "next", "first", event );
|
|
||||||
},
|
|
||||||
|
|
||||||
previous: function( event ) {
|
|
||||||
if ( this.open && this.openSubmenus ) {
|
|
||||||
// Track number of open submenus and prevent moving to previous menubar item
|
|
||||||
this.openSubmenus--;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.openSubmenus = 0;
|
|
||||||
this._move( "prev", "last", event );
|
|
||||||
},
|
|
||||||
|
|
||||||
_move: function( direction, filter, event ) {
|
|
||||||
var next,
|
|
||||||
wrapItem;
|
|
||||||
if ( this.open ) {
|
|
||||||
next = this.active.closest( ".ui-menubar-item" )[ direction + "All" ]( this.options.items ).first().children( ".ui-menu" ).eq( 0 );
|
|
||||||
wrapItem = this.menuItems[ filter ]().children( ".ui-menu" ).eq( 0 );
|
|
||||||
} else {
|
|
||||||
if ( event ) {
|
|
||||||
next = $( event.target ).closest( ".ui-menubar-item" )[ direction + "All" ]( this.options.items ).children( ".ui-menubar-link" ).eq( 0 );
|
|
||||||
wrapItem = this.menuItems[ filter ]().children( ".ui-menubar-link" ).eq( 0 );
|
|
||||||
} else {
|
|
||||||
next = wrapItem = this.menuItems.children( "a" ).eq( 0 );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( next.length ) {
|
|
||||||
if ( this.open ) {
|
|
||||||
this._open( event, next );
|
|
||||||
} else {
|
|
||||||
next.removeAttr( "tabIndex")[0].focus();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if ( this.open ) {
|
|
||||||
this._open( event, wrapItem );
|
|
||||||
} else {
|
|
||||||
wrapItem.removeAttr( "tabIndex")[0].focus();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
}( jQuery ));
|
|
1496
tailbone/static/js/lib/jquery.ui.timepicker.js
vendored
1496
tailbone/static/js/lib/jquery.ui.timepicker.js
vendored
File diff suppressed because it is too large
Load diff
|
@ -1,193 +0,0 @@
|
||||||
|
|
||||||
/************************************************************
|
|
||||||
*
|
|
||||||
* tailbone.edit-shifts.js
|
|
||||||
*
|
|
||||||
* Common logic for editing time sheet / schedule data.
|
|
||||||
*
|
|
||||||
************************************************************/
|
|
||||||
|
|
||||||
|
|
||||||
var editing_day = null;
|
|
||||||
var new_shift_id = 1;
|
|
||||||
|
|
||||||
function add_shift(focus, uuid, start_time, end_time) {
|
|
||||||
var shift = $('#snippets .shift').clone();
|
|
||||||
if (! uuid) {
|
|
||||||
uuid = 'new-' + (new_shift_id++).toString();
|
|
||||||
}
|
|
||||||
shift.attr('data-uuid', uuid);
|
|
||||||
shift.children('input').each(function() {
|
|
||||||
var name = $(this).attr('name') + '-' + uuid;
|
|
||||||
$(this).attr('name', name);
|
|
||||||
$(this).attr('id', name);
|
|
||||||
});
|
|
||||||
shift.children('input[name|="edit_start_time"]').val(start_time || '');
|
|
||||||
shift.children('input[name|="edit_end_time"]').val(end_time || '');
|
|
||||||
$('#day-editor .shifts').append(shift);
|
|
||||||
shift.children('input').timepicker({showPeriod: true});
|
|
||||||
if (focus) {
|
|
||||||
shift.children('input:first').focus();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function calc_minutes(start_time, end_time) {
|
|
||||||
var start = parseTime(start_time);
|
|
||||||
start = new Date(2000, 0, 1, start.hh, start.mm);
|
|
||||||
var end = parseTime(end_time);
|
|
||||||
end = new Date(2000, 0, 1, end.hh, end.mm);
|
|
||||||
return Math.floor((end - start) / 1000 / 60);
|
|
||||||
}
|
|
||||||
|
|
||||||
function format_minutes(minutes) {
|
|
||||||
var hours = Math.floor(minutes / 60);
|
|
||||||
if (hours) {
|
|
||||||
minutes -= hours * 60;
|
|
||||||
}
|
|
||||||
return hours.toString() + ':' + (minutes < 10 ? '0' : '') + minutes.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
// stolen from http://stackoverflow.com/a/1788084
|
|
||||||
function parseTime(s) {
|
|
||||||
var part = s.match(/(\d+):(\d+)(?: )?(am|pm)?/i);
|
|
||||||
var hh = parseInt(part[1], 10);
|
|
||||||
var mm = parseInt(part[2], 10);
|
|
||||||
var ap = part[3] ? part[3].toUpperCase() : null;
|
|
||||||
if (ap == 'AM') {
|
|
||||||
if (hh == 12) {
|
|
||||||
hh = 0;
|
|
||||||
}
|
|
||||||
} else if (ap == 'PM') {
|
|
||||||
if (hh != 12) {
|
|
||||||
hh += 12;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return { hh: hh, mm: mm };
|
|
||||||
}
|
|
||||||
|
|
||||||
function time_input(shift, type) {
|
|
||||||
var input = shift.children('input[name|="' + type + '_time"]');
|
|
||||||
if (! input.length) {
|
|
||||||
input = $('<input type="hidden" name="' + type + '_time-' + shift.data('uuid') + '" />');
|
|
||||||
shift.append(input);
|
|
||||||
}
|
|
||||||
return input;
|
|
||||||
}
|
|
||||||
|
|
||||||
function update_row_hours(row) {
|
|
||||||
var minutes = 0;
|
|
||||||
row.find('.day .shift:not(.deleted)').each(function() {
|
|
||||||
var time_range = $.trim($(this).children('span').text()).split(' - ');
|
|
||||||
minutes += calc_minutes(time_range[0], time_range[1]);
|
|
||||||
});
|
|
||||||
row.children('.total').text(minutes ? format_minutes(minutes) : '0');
|
|
||||||
}
|
|
||||||
|
|
||||||
$(function() {
|
|
||||||
|
|
||||||
$('.timesheet').on('click', '.day', function() {
|
|
||||||
editing_day = $(this);
|
|
||||||
var editor = $('#day-editor');
|
|
||||||
var employee = editing_day.siblings('.employee').text();
|
|
||||||
var date = weekdays[editing_day.get(0).cellIndex - 1];
|
|
||||||
var shifts = editor.children('.shifts');
|
|
||||||
shifts.empty();
|
|
||||||
editing_day.children('.shift:not(.deleted)').each(function() {
|
|
||||||
var uuid = $(this).data('uuid');
|
|
||||||
var time_range = $.trim($(this).children('span').text()).split(' - ');
|
|
||||||
add_shift(false, uuid, time_range[0], time_range[1]);
|
|
||||||
});
|
|
||||||
if (! shifts.children('.shift').length) {
|
|
||||||
add_shift();
|
|
||||||
}
|
|
||||||
editor.dialog({
|
|
||||||
modal: true,
|
|
||||||
title: employee + ' - ' + date,
|
|
||||||
position: {my: 'center', at: 'center', of: editing_day},
|
|
||||||
width: 'auto',
|
|
||||||
autoResize: true,
|
|
||||||
buttons: [
|
|
||||||
{
|
|
||||||
text: "Update",
|
|
||||||
click: function() {
|
|
||||||
|
|
||||||
// TODO: is this hacky? invoking timepicker to format the time values
|
|
||||||
// in all cases, to avoid "invalid format" from user input
|
|
||||||
editor.find('.shifts .shift').each(function() {
|
|
||||||
var start_time = $(this).children('input[name|="edit_start_time"]');
|
|
||||||
var end_time = $(this).children('input[name|="edit_end_time"]');
|
|
||||||
$.timepicker._setTime(start_time.data('timepicker'), start_time.val());
|
|
||||||
$.timepicker._setTime(end_time.data('timepicker'), end_time.val());
|
|
||||||
});
|
|
||||||
|
|
||||||
// create / update shifts in time table, as needed
|
|
||||||
editor.find('.shifts .shift').each(function() {
|
|
||||||
var uuid = $(this).data('uuid');
|
|
||||||
var start_time = $(this).children('input[name|="edit_start_time"]').val();
|
|
||||||
var end_time = $(this).children('input[name|="edit_end_time"]').val();
|
|
||||||
var shift = editing_day.children('.shift[data-uuid="' + uuid + '"]');
|
|
||||||
if (! shift.length) {
|
|
||||||
shift = $('<p class="shift" data-uuid="' + uuid + '"><span></span></p>');
|
|
||||||
shift.append($('<input type="hidden" name="employee_uuid-' + uuid + '" value="'
|
|
||||||
+ editing_day.parents('tr:first').data('employee-uuid') + '" />'));
|
|
||||||
editing_day.append(shift);
|
|
||||||
}
|
|
||||||
shift.children('span').text(start_time + ' - ' + end_time);
|
|
||||||
time_input(shift, 'start').val(date + ' ' + start_time);
|
|
||||||
time_input(shift, 'end').val(date + ' ' + end_time);
|
|
||||||
});
|
|
||||||
|
|
||||||
// remove shifts from time table, as needed
|
|
||||||
editing_day.children('.shift').each(function() {
|
|
||||||
var uuid = $(this).data('uuid');
|
|
||||||
if (! editor.find('.shifts .shift[data-uuid="' + uuid + '"]').length) {
|
|
||||||
if (uuid.match(/^new-/)) {
|
|
||||||
$(this).remove();
|
|
||||||
} else {
|
|
||||||
$(this).addClass('deleted');
|
|
||||||
$(this).append($('<input type="hidden" name="delete-' + uuid + '" value="delete" />'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// mark day as modified, close dialog
|
|
||||||
editing_day.addClass('modified');
|
|
||||||
$('.save-changes').button('enable');
|
|
||||||
$('.undo-changes').button('enable');
|
|
||||||
update_row_hours(editing_day.parents('tr:first'));
|
|
||||||
editor.dialog('close');
|
|
||||||
data_modified = true;
|
|
||||||
okay_to_leave = false;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: "Cancel",
|
|
||||||
click: function() {
|
|
||||||
editor.dialog('close');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
$('#day-editor #add-shift').click(function() {
|
|
||||||
add_shift(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
$('#day-editor').on('click', '.shifts button', function() {
|
|
||||||
$(this).parents('.shift:first').remove();
|
|
||||||
});
|
|
||||||
|
|
||||||
$('.save-changes').click(function() {
|
|
||||||
$(this).button('disable').button('option', 'label', "Saving Changes...");
|
|
||||||
okay_to_leave = true;
|
|
||||||
$('#timetable-form').submit();
|
|
||||||
});
|
|
||||||
|
|
||||||
$('.undo-changes').click(function() {
|
|
||||||
$(this).button('disable').button('option', 'label', "Refreshing...");
|
|
||||||
okay_to_leave = true;
|
|
||||||
location.href = location.href;
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
|
@ -1,58 +1,54 @@
|
||||||
|
|
||||||
$(function() {
|
let FeedbackForm = {
|
||||||
|
props: ['action', 'message'],
|
||||||
|
template: '#feedback-template',
|
||||||
|
mixins: [FormPosterMixin],
|
||||||
|
methods: {
|
||||||
|
|
||||||
$('#feedback').click(function() {
|
pleaseReplyChanged(value) {
|
||||||
var dialog = $('#feedback-dialog');
|
this.$nextTick(() => {
|
||||||
var form = dialog.find('form');
|
this.$refs.userEmail.focus()
|
||||||
var textarea = form.find('textarea');
|
})
|
||||||
dialog.find('.referrer .field').html(location.href);
|
|
||||||
textarea.val('');
|
|
||||||
dialog.dialog({
|
|
||||||
title: "User Feedback",
|
|
||||||
width: 600,
|
|
||||||
modal: true,
|
|
||||||
buttons: [
|
|
||||||
{
|
|
||||||
text: "Send",
|
|
||||||
click: function(event) {
|
|
||||||
|
|
||||||
var msg = $.trim(textarea.val());
|
|
||||||
if (! msg) {
|
|
||||||
alert("Please enter a message.");
|
|
||||||
textarea.select();
|
|
||||||
textarea.focus();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
disable_button(dialog_button(event));
|
|
||||||
|
|
||||||
var data = {
|
|
||||||
_csrf: form.find('input[name="_csrf"]').val(),
|
|
||||||
referrer: location.href,
|
|
||||||
user: form.find('input[name="user"]').val(),
|
|
||||||
user_name: form.find('input[name="user_name"]').val(),
|
|
||||||
message: msg
|
|
||||||
};
|
|
||||||
|
|
||||||
$.ajax(form.attr('action'), {
|
|
||||||
method: 'POST',
|
|
||||||
data: data,
|
|
||||||
success: function(data) {
|
|
||||||
dialog.dialog('close');
|
|
||||||
alert("Message successfully sent.\n\nThank you for your feedback.");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
|
||||||
text: "Cancel",
|
|
||||||
click: function() {
|
|
||||||
dialog.dialog('close');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
showFeedback() {
|
||||||
|
this.showDialog = true
|
||||||
|
this.$nextTick(function() {
|
||||||
|
this.$refs.textarea.focus()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
sendFeedback() {
|
||||||
|
|
||||||
|
let params = {
|
||||||
|
referrer: this.referrer,
|
||||||
|
user: this.userUUID,
|
||||||
|
user_name: this.userName,
|
||||||
|
please_reply_to: this.pleaseReply ? this.userEmail : null,
|
||||||
|
message: this.message.trim(),
|
||||||
|
}
|
||||||
|
|
||||||
|
this.submitForm(this.action, params, response => {
|
||||||
|
|
||||||
|
this.$buefy.toast.open({
|
||||||
|
message: "Message sent! Thank you for your feedback.",
|
||||||
|
type: 'is-info',
|
||||||
|
duration: 4000, // 4 seconds
|
||||||
|
})
|
||||||
|
|
||||||
|
this.showDialog = false
|
||||||
|
// clear out message, in case they need to send another
|
||||||
|
this.message = ""
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let FeedbackFormData = {
|
||||||
|
referrer: null,
|
||||||
|
userUUID: null,
|
||||||
|
userName: null,
|
||||||
|
pleaseReply: false,
|
||||||
|
userEmail: null,
|
||||||
|
showDialog: false,
|
||||||
|
}
|
||||||
|
|
|
@ -1,386 +0,0 @@
|
||||||
|
|
||||||
/************************************************************
|
|
||||||
*
|
|
||||||
* tailbone.js
|
|
||||||
*
|
|
||||||
************************************************************/
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Initialize the disabled filters array. This is populated from within the
|
|
||||||
* /grids/search.mako template.
|
|
||||||
*/
|
|
||||||
var filters_to_disable = [];
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Disables options within the "add filter" dropdown which correspond to those
|
|
||||||
* filters already being displayed. Called from /grids/search.mako template.
|
|
||||||
*/
|
|
||||||
function disable_filter_options() {
|
|
||||||
while (filters_to_disable.length) {
|
|
||||||
var filter = filters_to_disable.shift();
|
|
||||||
var option = $('#add-filter option[value="' + filter + '"]');
|
|
||||||
option.attr('disabled', 'disabled');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Convenience function to disable a UI button.
|
|
||||||
*/
|
|
||||||
function disable_button(button, label) {
|
|
||||||
$(button).button('disable');
|
|
||||||
if (label === undefined) {
|
|
||||||
label = $(button).data('working-label') || "Working, please wait...";
|
|
||||||
}
|
|
||||||
if (label) {
|
|
||||||
if (label.slice(-3) != '...') {
|
|
||||||
label += '...';
|
|
||||||
}
|
|
||||||
$(button).button('option', 'label', label);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function disable_submit_button(form, label) {
|
|
||||||
// for some reason chrome requires us to do things this way...
|
|
||||||
// https://stackoverflow.com/questions/16867080/onclick-javascript-stops-form-submit-in-chrome
|
|
||||||
// https://stackoverflow.com/questions/5691054/disable-submit-button-on-form-submit
|
|
||||||
var submit = $(form).find('input[type="submit"]');
|
|
||||||
if (! submit.length) {
|
|
||||||
submit = $(form).find('button[type="submit"]');
|
|
||||||
}
|
|
||||||
if (submit.length) {
|
|
||||||
disable_button(submit, label);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Load next / previous page of results to grid. This function is called on
|
|
||||||
* the click event from the pager links, via inline script code.
|
|
||||||
*/
|
|
||||||
function grid_navigate_page(link, url) {
|
|
||||||
var wrapper = $(link).parents('div.grid-wrapper');
|
|
||||||
var grid = wrapper.find('div.grid');
|
|
||||||
wrapper.mask("Loading...");
|
|
||||||
$.get(url, function(data) {
|
|
||||||
wrapper.unmask();
|
|
||||||
grid.replaceWith(data);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Fetch the UUID value associated with a table row.
|
|
||||||
*/
|
|
||||||
function get_uuid(obj) {
|
|
||||||
obj = $(obj);
|
|
||||||
if (obj.attr('uuid')) {
|
|
||||||
return obj.attr('uuid');
|
|
||||||
}
|
|
||||||
var tr = obj.parents('tr:first');
|
|
||||||
if (tr.attr('uuid')) {
|
|
||||||
return tr.attr('uuid');
|
|
||||||
}
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Return a jQuery object containing a button from a dialog. This is a
|
|
||||||
* convenience function to help with browser differences. It is assumed
|
|
||||||
* that it is being called from within the relevant button click handler.
|
|
||||||
* @param {event} event - Click event object.
|
|
||||||
*/
|
|
||||||
function dialog_button(event) {
|
|
||||||
var button = $(event.target);
|
|
||||||
|
|
||||||
// TODO: not sure why this workaround is needed for Chrome..?
|
|
||||||
if (! button.hasClass('ui-button')) {
|
|
||||||
button = button.parents('.ui-button:first');
|
|
||||||
}
|
|
||||||
|
|
||||||
return button;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Scroll screen as needed to ensure all options are visible, for the given
|
|
||||||
* select menu widget.
|
|
||||||
*/
|
|
||||||
function show_all_options(select) {
|
|
||||||
if (! select.is(':visible')) {
|
|
||||||
/*
|
|
||||||
* Note that the following code was largely stolen from
|
|
||||||
* http://brianseekford.com/2013/06/03/how-to-scroll-a-container-or-element-into-view-using-jquery-javascript-in-your-html/
|
|
||||||
*/
|
|
||||||
|
|
||||||
var docViewTop = $(window).scrollTop();
|
|
||||||
var docViewBottom = docViewTop + $(window).height();
|
|
||||||
|
|
||||||
var widget = select.selectmenu('menuWidget');
|
|
||||||
var elemTop = widget.offset().top;
|
|
||||||
var elemBottom = elemTop + widget.height();
|
|
||||||
|
|
||||||
var isScrolled = ((elemBottom <= docViewBottom) && (elemTop >= docViewTop));
|
|
||||||
|
|
||||||
if (!isScrolled) {
|
|
||||||
if (widget.height() > $(window).height()) { //then just bring to top of the container
|
|
||||||
$(window).scrollTop(elemTop)
|
|
||||||
} else { //try and and bring bottom of container to bottom of screen
|
|
||||||
$(window).scrollTop(elemTop - ($(window).height() - widget.height()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* reference to existing timeout warning dialog, if any
|
|
||||||
*/
|
|
||||||
var session_timeout_warning = null;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Warn user of impending session timeout.
|
|
||||||
*/
|
|
||||||
function timeout_warning() {
|
|
||||||
if (! session_timeout_warning) {
|
|
||||||
session_timeout_warning = $('<div id="session-timeout-warning">' +
|
|
||||||
'You will be logged out in <span class="seconds"></span> ' +
|
|
||||||
'seconds...</div>');
|
|
||||||
}
|
|
||||||
session_timeout_warning.find('.seconds').text('60');
|
|
||||||
session_timeout_warning.dialog({
|
|
||||||
title: "Session Timeout Warning",
|
|
||||||
modal: true,
|
|
||||||
buttons: {
|
|
||||||
"Stay Logged In": function() {
|
|
||||||
session_timeout_warning.dialog('close');
|
|
||||||
$.get(noop_url, set_timeout_warning_timer);
|
|
||||||
},
|
|
||||||
"Logout Now": function() {
|
|
||||||
location.href = logout_url;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
window.setTimeout(timeout_warning_update, 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decrement the 'seconds' counter for the current timeout warning
|
|
||||||
*/
|
|
||||||
function timeout_warning_update() {
|
|
||||||
if (session_timeout_warning.is(':visible')) {
|
|
||||||
var span = session_timeout_warning.find('.seconds');
|
|
||||||
var seconds = parseInt(span.text()) - 1;
|
|
||||||
if (seconds) {
|
|
||||||
span.text(seconds.toString());
|
|
||||||
window.setTimeout(timeout_warning_update, 1000);
|
|
||||||
} else {
|
|
||||||
location.href = logout_url;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Warn user of impending session timeout.
|
|
||||||
*/
|
|
||||||
function set_timeout_warning_timer() {
|
|
||||||
// timout dialog says we're 60 seconds away, but we actually trigger when
|
|
||||||
// 70 seconds away from supposed timeout, in case of timer drift?
|
|
||||||
window.setTimeout(timeout_warning, session_timeout * 1000 - 70000);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* set initial timer for timeout warning, if applicable
|
|
||||||
*/
|
|
||||||
if (session_timeout) {
|
|
||||||
set_timeout_warning_timer();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
$(function() {
|
|
||||||
|
|
||||||
/*
|
|
||||||
* enhance buttons
|
|
||||||
*/
|
|
||||||
$('button, a.button').button();
|
|
||||||
$('input[type=submit]').button();
|
|
||||||
$('input[type=reset]').button();
|
|
||||||
$('a.button.autodisable').click(function() {
|
|
||||||
disable_button(this);
|
|
||||||
});
|
|
||||||
$('form.autodisable').submit(function() {
|
|
||||||
disable_submit_button(this);
|
|
||||||
});
|
|
||||||
|
|
||||||
// quickie button
|
|
||||||
$('#submit-quickie').button('option', 'icons', {primary: 'ui-icon-zoomin'});
|
|
||||||
|
|
||||||
/*
|
|
||||||
* enhance dropdowns
|
|
||||||
*/
|
|
||||||
$('select[auto-enhance="true"]').selectmenu();
|
|
||||||
$('select[auto-enhance="true"]').on('selectmenuopen', function(event, ui) {
|
|
||||||
show_all_options($(this));
|
|
||||||
});
|
|
||||||
|
|
||||||
/* Also automatically disable any buttons marked for that. */
|
|
||||||
$('a.button[disabled=disabled]').button('option', 'disabled', true);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Apply timepicker behavior to text inputs which are marked for it.
|
|
||||||
*/
|
|
||||||
$('input[type=text].timepicker').timepicker({
|
|
||||||
showPeriod: true
|
|
||||||
});
|
|
||||||
|
|
||||||
/*
|
|
||||||
* When filter labels are clicked, (un)check the associated checkbox.
|
|
||||||
*/
|
|
||||||
$('body').on('click', '.grid-wrapper .filter label', function() {
|
|
||||||
var checkbox = $(this).prev('input[type="checkbox"]');
|
|
||||||
if (checkbox.prop('checked')) {
|
|
||||||
checkbox.prop('checked', false);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
checkbox.prop('checked', true);
|
|
||||||
});
|
|
||||||
|
|
||||||
/*
|
|
||||||
* When a new filter is selected in the "add filter" dropdown, show it in
|
|
||||||
* the UI. This selects the filter's checkbox and puts focus to its input
|
|
||||||
* element. If all available filters have been displayed, the "add filter"
|
|
||||||
* dropdown will be hidden.
|
|
||||||
*/
|
|
||||||
$('body').on('change', '#add-filter', function() {
|
|
||||||
var select = $(this);
|
|
||||||
var filters = select.parents('div.filters:first');
|
|
||||||
var filter = filters.find('#filter-' + select.val());
|
|
||||||
var checkbox = filter.find('input[type="checkbox"]:first');
|
|
||||||
var input = filter.find(':last-child');
|
|
||||||
|
|
||||||
checkbox.prop('checked', true);
|
|
||||||
filter.show();
|
|
||||||
input.select();
|
|
||||||
input.focus();
|
|
||||||
|
|
||||||
filters.find('input[type="submit"]').show();
|
|
||||||
filters.find('button[type="reset"]').show();
|
|
||||||
|
|
||||||
select.find('option:selected').attr('disabled', true);
|
|
||||||
select.val('add a filter');
|
|
||||||
if (select.find('option:enabled').length == 1) {
|
|
||||||
select.hide();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
/*
|
|
||||||
* When user clicks the grid filters search button, perform the search in
|
|
||||||
* the background and reload the grid in-place.
|
|
||||||
*/
|
|
||||||
$('body').on('submit', '.filters form', function() {
|
|
||||||
var form = $(this);
|
|
||||||
var wrapper = form.parents('div.grid-wrapper');
|
|
||||||
var grid = wrapper.find('div.grid');
|
|
||||||
var data = form.serializeArray();
|
|
||||||
data.push({name: 'partial', value: true});
|
|
||||||
wrapper.mask("Loading...");
|
|
||||||
$.get(grid.attr('url'), data, function(data) {
|
|
||||||
wrapper.unmask();
|
|
||||||
grid.replaceWith(data);
|
|
||||||
});
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
|
|
||||||
/*
|
|
||||||
* When user clicks the grid filters reset button, manually clear all
|
|
||||||
* filter input elements, and submit a new search.
|
|
||||||
*/
|
|
||||||
$('body').on('click', '.filters form button[type="reset"]', function() {
|
|
||||||
var form = $(this).parents('form');
|
|
||||||
form.find('div.filter').each(function() {
|
|
||||||
$(this).find('div.value input').val('');
|
|
||||||
});
|
|
||||||
form.submit();
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
|
|
||||||
$('body').on('click', '.grid thead th.sortable a', function() {
|
|
||||||
var th = $(this).parent();
|
|
||||||
var wrapper = th.parents('div.grid-wrapper');
|
|
||||||
var grid = wrapper.find('div.grid');
|
|
||||||
var data = {
|
|
||||||
sort: th.attr('field'),
|
|
||||||
dir: (th.hasClass('sorted') && th.hasClass('asc')) ? 'desc' : 'asc',
|
|
||||||
page: 1,
|
|
||||||
partial: true
|
|
||||||
};
|
|
||||||
wrapper.mask("Loading...");
|
|
||||||
$.get(grid.attr('url'), data, function(data) {
|
|
||||||
wrapper.unmask();
|
|
||||||
grid.replaceWith(data);
|
|
||||||
});
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
|
|
||||||
$('body').on('mouseenter', '.grid.hoverable tbody tr', function() {
|
|
||||||
$(this).addClass('hovering');
|
|
||||||
});
|
|
||||||
|
|
||||||
$('body').on('mouseleave', '.grid.hoverable tbody tr', function() {
|
|
||||||
$(this).removeClass('hovering');
|
|
||||||
});
|
|
||||||
|
|
||||||
$('body').on('click', '.grid tbody td.view', function() {
|
|
||||||
var url = $(this).attr('url');
|
|
||||||
if (url) {
|
|
||||||
location.href = url;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$('body').on('click', '.grid tbody td.edit', function() {
|
|
||||||
var url = $(this).attr('url');
|
|
||||||
if (url) {
|
|
||||||
location.href = url;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$('body').on('click', '.grid tbody td.delete', function() {
|
|
||||||
var url = $(this).attr('url');
|
|
||||||
if (url) {
|
|
||||||
if (confirm("Do you really wish to delete this object?")) {
|
|
||||||
location.href = url;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// $('div.grid-wrapper').on('change', 'div.grid div.pager select#grid-page-count', function() {
|
|
||||||
$('body').on('change', '.grid .pager #grid-page-count', function() {
|
|
||||||
var select = $(this);
|
|
||||||
var wrapper = select.parents('div.grid-wrapper');
|
|
||||||
var grid = wrapper.find('div.grid');
|
|
||||||
var data = {
|
|
||||||
per_page: select.val(),
|
|
||||||
partial: true
|
|
||||||
};
|
|
||||||
wrapper.mask("Loading...");
|
|
||||||
$.get(grid.attr('url'), data, function(data) {
|
|
||||||
wrapper.unmask();
|
|
||||||
grid.replaceWith(data);
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
$('body').on('click', 'div.dialog button.close', function() {
|
|
||||||
var dialog = $(this).parents('div.dialog:first');
|
|
||||||
dialog.dialog('close');
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
|
@ -1,267 +0,0 @@
|
||||||
|
|
||||||
/************************************************************
|
|
||||||
*
|
|
||||||
* tailbone.timesheet.edit.js
|
|
||||||
*
|
|
||||||
* Common logic for editing time sheet / schedule data.
|
|
||||||
*
|
|
||||||
************************************************************/
|
|
||||||
|
|
||||||
|
|
||||||
var editing_day = null;
|
|
||||||
var new_shift_id = 1;
|
|
||||||
var show_timepicker = true;
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Add a new shift entry to the editor dialog.
|
|
||||||
* @param {boolean} focus - Whether to set focus to the start_time input
|
|
||||||
* element after adding the shift.
|
|
||||||
* @param {string} uuid - UUID value for the shift, if applicable.
|
|
||||||
* @param {string} start_time - Value for start_time input element.
|
|
||||||
* @param {string} end_time - Value for end_time input element.
|
|
||||||
*/
|
|
||||||
|
|
||||||
function add_shift(focus, uuid, start_time, end_time) {
|
|
||||||
var shift = $('#snippets .shift').clone();
|
|
||||||
if (! uuid) {
|
|
||||||
uuid = 'new-' + (new_shift_id++).toString();
|
|
||||||
}
|
|
||||||
shift.attr('data-uuid', uuid);
|
|
||||||
shift.children('input').each(function() {
|
|
||||||
var name = $(this).attr('name') + '-' + uuid;
|
|
||||||
$(this).attr('name', name);
|
|
||||||
$(this).attr('id', name);
|
|
||||||
});
|
|
||||||
shift.children('input[name|="edit_start_time"]').val(start_time);
|
|
||||||
shift.children('input[name|="edit_end_time"]').val(end_time);
|
|
||||||
$('#day-editor .shifts').append(shift);
|
|
||||||
|
|
||||||
// maybe trick timepicker into never showing itself
|
|
||||||
var args = {showPeriod: true};
|
|
||||||
if (! show_timepicker) {
|
|
||||||
args.showOn = 'button';
|
|
||||||
args.button = '#nevershow';
|
|
||||||
}
|
|
||||||
shift.children('input').timepicker(args);
|
|
||||||
|
|
||||||
if (focus) {
|
|
||||||
shift.children('input:first').focus();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculate the number of minutes between given the times.
|
|
||||||
* @param {string} start_time - Value from start_time input element.
|
|
||||||
* @param {string} end_time - Value from end_time input element.
|
|
||||||
*/
|
|
||||||
function calc_minutes(start_time, end_time) {
|
|
||||||
var start = parseTime(start_time);
|
|
||||||
var end = parseTime(end_time);
|
|
||||||
if (start && end) {
|
|
||||||
start = new Date(2000, 0, 1, start.hh, start.mm);
|
|
||||||
end = new Date(2000, 0, 1, end.hh, end.mm);
|
|
||||||
return Math.floor((end - start) / 1000 / 60);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts a number of minutes into string of HH:MM format.
|
|
||||||
* @param {number} minutes - Number of minutes to be converted.
|
|
||||||
*/
|
|
||||||
function format_minutes(minutes) {
|
|
||||||
var hours = Math.floor(minutes / 60);
|
|
||||||
if (hours) {
|
|
||||||
minutes -= hours * 60;
|
|
||||||
}
|
|
||||||
return hours.toString() + ':' + (minutes < 10 ? '0' : '') + minutes.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* NOTE: most of this logic was stolen from http://stackoverflow.com/a/1788084
|
|
||||||
*
|
|
||||||
* Parse a time string and convert to simple object with hh and mm keys.
|
|
||||||
* @param {string} time - Time value in 'HH:MM PP' format, or close enough.
|
|
||||||
*/
|
|
||||||
function parseTime(time) {
|
|
||||||
if (time) {
|
|
||||||
var part = time.match(/(\d+):(\d+)(?: )?(am|pm)?/i);
|
|
||||||
if (part) {
|
|
||||||
var hh = parseInt(part[1], 10);
|
|
||||||
var mm = parseInt(part[2], 10);
|
|
||||||
var ap = part[3] ? part[3].toUpperCase() : null;
|
|
||||||
if (ap == 'AM') {
|
|
||||||
if (hh == 12) {
|
|
||||||
hh = 0;
|
|
||||||
}
|
|
||||||
} else if (ap == 'PM') {
|
|
||||||
if (hh != 12) {
|
|
||||||
hh += 12;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return { hh: hh, mm: mm };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return a jQuery object containing the hidden start or end time input element
|
|
||||||
* for the shift (i.e. within the *main* timesheet form). This will create the
|
|
||||||
* input if necessary.
|
|
||||||
* @param {jQuery} shift - A jQuery object for the shift itself.
|
|
||||||
* @param {string} type - Should be 'start' or 'end' only.
|
|
||||||
*/
|
|
||||||
function time_input(shift, type) {
|
|
||||||
var input = shift.children('input[name|="' + type + '_time"]');
|
|
||||||
if (! input.length) {
|
|
||||||
input = $('<input type="hidden" name="' + type + '_time-' + shift.data('uuid') + '" />');
|
|
||||||
shift.append(input);
|
|
||||||
}
|
|
||||||
return input;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the weekly hour total for a given row (employee).
|
|
||||||
* @param {jQuery} row - A jQuery object for the row to be updated.
|
|
||||||
*/
|
|
||||||
function update_row_hours(row) {
|
|
||||||
var minutes = 0;
|
|
||||||
row.find('.day .shift:not(.deleted)').each(function() {
|
|
||||||
var time_range = $.trim($(this).children('span').text()).split(' - ');
|
|
||||||
minutes += calc_minutes(time_range[0], time_range[1]);
|
|
||||||
});
|
|
||||||
row.children('.total').text(minutes ? format_minutes(minutes) : '0');
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clean up user input within the editor dialog, e.g. '8:30am' => '08:30 AM'.
|
|
||||||
* This also should ensure invalid input will become empty string.
|
|
||||||
*/
|
|
||||||
function cleanup_editor_input() {
|
|
||||||
// TODO: is this hacky? invoking timepicker to format the time values
|
|
||||||
// in all cases, to avoid "invalid format" from user input
|
|
||||||
var backward = false;
|
|
||||||
$('#day-editor .shifts .shift').each(function() {
|
|
||||||
var start_time = $(this).children('input[name|="edit_start_time"]');
|
|
||||||
var end_time = $(this).children('input[name|="edit_end_time"]');
|
|
||||||
$.timepicker._setTime(start_time.data('timepicker'), start_time.val() || '??');
|
|
||||||
$.timepicker._setTime(end_time.data('timepicker'), end_time.val() || '??');
|
|
||||||
var t_start = parseTime(start_time.val());
|
|
||||||
var t_end = parseTime(end_time.val());
|
|
||||||
if (t_start && t_end) {
|
|
||||||
if ((t_start.hh > t_end.hh) || ((t_start.hh == t_end.hh) && (t_start.mm > t_end.mm))) {
|
|
||||||
alert("Start time falls *after* end time! Please fix...");
|
|
||||||
start_time.focus().select();
|
|
||||||
backward = true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return !backward;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the main timesheet table based on editor dialog input. This updates
|
|
||||||
* both the displayed timesheet, as well as any hidden input elements on the
|
|
||||||
* main form.
|
|
||||||
*/
|
|
||||||
function update_timetable() {
|
|
||||||
|
|
||||||
var date = weekdays[editing_day.get(0).cellIndex - 1];
|
|
||||||
|
|
||||||
// add or update
|
|
||||||
$('#day-editor .shifts .shift').each(function() {
|
|
||||||
var uuid = $(this).data('uuid');
|
|
||||||
var start_time = $(this).children('input[name|="edit_start_time"]').val();
|
|
||||||
var end_time = $(this).children('input[name|="edit_end_time"]').val();
|
|
||||||
var shift = editing_day.children('.shift[data-uuid="' + uuid + '"]');
|
|
||||||
if (! shift.length) {
|
|
||||||
if (! (start_time || end_time)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
shift = $('<p class="shift" data-uuid="' + uuid + '"><span></span></p>');
|
|
||||||
shift.append($('<input type="hidden" name="employee_uuid-' + uuid + '" value="'
|
|
||||||
+ editing_day.parents('tr:first').data('employee-uuid') + '" />'));
|
|
||||||
editing_day.append(shift);
|
|
||||||
}
|
|
||||||
shift.children('span').text((start_time || '??') + ' - ' + (end_time || '??'));
|
|
||||||
start_time = start_time ? (date + ' ' + start_time) : '';
|
|
||||||
end_time = end_time ? (date + ' ' + end_time) : '';
|
|
||||||
time_input(shift, 'start').val(start_time);
|
|
||||||
time_input(shift, 'end').val(end_time);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
// remove / mark for deletion
|
|
||||||
editing_day.children('.shift').each(function() {
|
|
||||||
var uuid = $(this).data('uuid');
|
|
||||||
if (! $('#day-editor .shifts .shift[data-uuid="' + uuid + '"]').length) {
|
|
||||||
if (uuid.match(/^new-/)) {
|
|
||||||
$(this).remove();
|
|
||||||
} else {
|
|
||||||
$(this).addClass('deleted');
|
|
||||||
$(this).append($('<input type="hidden" name="delete-' + uuid + '" value="delete" />'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Perform full "save" action for time sheet form, direct from day editor dialog.
|
|
||||||
*/
|
|
||||||
function save_dialog() {
|
|
||||||
if (! cleanup_editor_input()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
var save = $('#day-editor').parents('.ui-dialog').find('.ui-dialog-buttonpane button:first');
|
|
||||||
save.button('disable').button('option', 'label', "Saving...");
|
|
||||||
update_timetable();
|
|
||||||
$('#timetable-form').submit();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* on document load...
|
|
||||||
*/
|
|
||||||
$(function() {
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Within editor dialog, clicking Add Shift button will create a new/empty
|
|
||||||
* shift and set focus to its start_time input.
|
|
||||||
*/
|
|
||||||
$('#day-editor #add-shift').click(function() {
|
|
||||||
add_shift(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Within editor dialog, clicking a shift's "trash can" button will remove
|
|
||||||
* the shift.
|
|
||||||
*/
|
|
||||||
$('#day-editor').on('click', '.shifts button', function() {
|
|
||||||
$(this).parents('.shift:first').remove();
|
|
||||||
});
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Within editor dialog, Enter press within time field "might" trigger
|
|
||||||
* save. Note that this is only done for timesheet editing, not schedule.
|
|
||||||
*/
|
|
||||||
$('#day-editor').on('keydown', '.shifts input[type="text"]', function(event) {
|
|
||||||
if (!show_timepicker) { // TODO: this implies too much, should be cleaner
|
|
||||||
if (event.which == 13) {
|
|
||||||
save_dialog();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
|
@ -1,14 +0,0 @@
|
||||||
|
|
||||||
/******************************
|
|
||||||
* tweaks for root user
|
|
||||||
******************************/
|
|
||||||
|
|
||||||
.navbar .navbar-end .navbar-link.root-user,
|
|
||||||
.navbar .navbar-end .navbar-link.root-user:hover,
|
|
||||||
.navbar .navbar-end .navbar-link.root-user.is_active,
|
|
||||||
.navbar .navbar-end .navbar-item.root-user,
|
|
||||||
.navbar .navbar-end .navbar-item.root-user:hover,
|
|
||||||
.navbar .navbar-end .navbar-item.root-user.is_active {
|
|
||||||
background-color: red;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
|
@ -1,22 +0,0 @@
|
||||||
|
|
||||||
/******************************
|
|
||||||
* Grid Filters
|
|
||||||
******************************/
|
|
||||||
|
|
||||||
.filters .filter {
|
|
||||||
margin-bottom: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.filters .filter-fieldname .field,
|
|
||||||
.filters .filter-fieldname .field label {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.filters .filter-fieldname .field label {
|
|
||||||
justify-content: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.filters .filter-verb .select,
|
|
||||||
.filters .filter-verb .select select {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
|
@ -1,61 +0,0 @@
|
||||||
|
|
||||||
/******************************
|
|
||||||
* forms
|
|
||||||
******************************/
|
|
||||||
|
|
||||||
/* note that this should only apply to "normal" primary forms */
|
|
||||||
/* TODO: replace this with bulma equivalent */
|
|
||||||
.form {
|
|
||||||
padding-left: 5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* note that this should only apply to "normal" primary forms */
|
|
||||||
.form-wrapper .form .field.is-horizontal .field-label .label {
|
|
||||||
text-align: left;
|
|
||||||
white-space: nowrap;
|
|
||||||
width: 18em;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* note that this should only apply to "normal" primary forms */
|
|
||||||
.form-wrapper .form .field.is-horizontal .field-body {
|
|
||||||
min-width: 30em;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* note that this should only apply to "normal" primary forms */
|
|
||||||
.form-wrapper .form .field.is-horizontal .field-body .select,
|
|
||||||
.form-wrapper .form .field.is-horizontal .field-body .select select {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
/******************************
|
|
||||||
* field-wrappers
|
|
||||||
******************************/
|
|
||||||
|
|
||||||
/* TODO: replace this with bulma equivalent */
|
|
||||||
.field-wrapper {
|
|
||||||
clear: both;
|
|
||||||
min-height: 30px;
|
|
||||||
overflow: auto;
|
|
||||||
margin: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* TODO: replace this with bulma equivalent */
|
|
||||||
.field-wrapper .field-row {
|
|
||||||
display: table-row;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* TODO: replace this with bulma equivalent */
|
|
||||||
.field-wrapper label {
|
|
||||||
display: table-cell;
|
|
||||||
vertical-align: top;
|
|
||||||
width: 18em;
|
|
||||||
font-weight: bold;
|
|
||||||
padding-top: 2px;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* TODO: replace this with bulma equivalent */
|
|
||||||
.field-wrapper .field {
|
|
||||||
display: table-cell;
|
|
||||||
line-height: 25px;
|
|
||||||
}
|
|
|
@ -1,15 +0,0 @@
|
||||||
|
|
||||||
/********************************************************************************
|
|
||||||
* grids.css
|
|
||||||
*
|
|
||||||
* Style tweaks for the Buefy grids.
|
|
||||||
********************************************************************************/
|
|
||||||
|
|
||||||
|
|
||||||
/******************************
|
|
||||||
* actions column
|
|
||||||
******************************/
|
|
||||||
|
|
||||||
a.grid-action {
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
|
@ -1,150 +0,0 @@
|
||||||
|
|
||||||
/******************************
|
|
||||||
* main layout
|
|
||||||
******************************/
|
|
||||||
|
|
||||||
body {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
min-height: 100vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content-wrapper {
|
|
||||||
display: flex;
|
|
||||||
flex: 1;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/******************************
|
|
||||||
* header
|
|
||||||
******************************/
|
|
||||||
|
|
||||||
/* this is the one in the very top left of screen, next to logo and linked to
|
|
||||||
the home page */
|
|
||||||
#global-header-title {
|
|
||||||
margin-left: 0.3rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
header .level {
|
|
||||||
/* TODO: not sure what this 60px was supposed to do? but it broke the */
|
|
||||||
/* styles for the feedback dialog, so disabled it is.
|
|
||||||
/* height: 60px; */
|
|
||||||
/* line-height: 60px; */
|
|
||||||
padding-left: 0.5em;
|
|
||||||
padding-right: 0.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
header .level #header-logo {
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
|
|
||||||
header .level .global-title,
|
|
||||||
header .level-left .global-title {
|
|
||||||
font-size: 2em;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* indent nested menu items a bit */
|
|
||||||
header .navbar-item.nested {
|
|
||||||
padding-left: 2.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
header span.header-text {
|
|
||||||
font-size: 2em;
|
|
||||||
font-weight: bold;
|
|
||||||
margin-right: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
header .level .theme-picker {
|
|
||||||
display: inline-flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
#content-title {
|
|
||||||
padding: 0.3rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
#content-title h1 {
|
|
||||||
font-size: 2rem;
|
|
||||||
margin-left: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
/******************************
|
|
||||||
* content
|
|
||||||
******************************/
|
|
||||||
|
|
||||||
#page-body {
|
|
||||||
padding: 0.4em;
|
|
||||||
}
|
|
||||||
|
|
||||||
/******************************
|
|
||||||
* context menu
|
|
||||||
******************************/
|
|
||||||
|
|
||||||
#context-menu {
|
|
||||||
margin-bottom: 1em;
|
|
||||||
margin-left: 1em;
|
|
||||||
text-align: right;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
/******************************
|
|
||||||
* "object helper" panel
|
|
||||||
******************************/
|
|
||||||
|
|
||||||
.object-helpers .panel-heading {
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.object-helpers a {
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.object-helper {
|
|
||||||
border: 1px solid black;
|
|
||||||
margin: 1em;
|
|
||||||
padding: 1em;
|
|
||||||
width: 20em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.object-helper-content {
|
|
||||||
margin-top: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
/******************************
|
|
||||||
* markdown
|
|
||||||
******************************/
|
|
||||||
|
|
||||||
.rendered-markdown p,
|
|
||||||
.rendered-markdown ul {
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.rendered-markdown .codehilite {
|
|
||||||
margin-bottom: 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
/******************************
|
|
||||||
* fix datepicker within modals
|
|
||||||
* TODO: someday this may not be necessary? cf.
|
|
||||||
* https://github.com/buefy/buefy/issues/292#issuecomment-347365637
|
|
||||||
******************************/
|
|
||||||
|
|
||||||
.modal .animation-content .modal-card {
|
|
||||||
overflow: visible !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-card-body {
|
|
||||||
overflow: visible !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/******************************
|
|
||||||
* feedback
|
|
||||||
******************************/
|
|
||||||
|
|
||||||
.feedback-dialog .red {
|
|
||||||
color: red;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
|
@ -1,54 +0,0 @@
|
||||||
|
|
||||||
let FeedbackForm = {
|
|
||||||
props: ['action', 'message'],
|
|
||||||
template: '#feedback-template',
|
|
||||||
mixins: [FormPosterMixin],
|
|
||||||
methods: {
|
|
||||||
|
|
||||||
pleaseReplyChanged(value) {
|
|
||||||
this.$nextTick(() => {
|
|
||||||
this.$refs.userEmail.focus()
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
showFeedback() {
|
|
||||||
this.showDialog = true
|
|
||||||
this.$nextTick(function() {
|
|
||||||
this.$refs.textarea.focus()
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
sendFeedback() {
|
|
||||||
|
|
||||||
let params = {
|
|
||||||
referrer: this.referrer,
|
|
||||||
user: this.userUUID,
|
|
||||||
user_name: this.userName,
|
|
||||||
please_reply_to: this.pleaseReply ? this.userEmail : null,
|
|
||||||
message: this.message.trim(),
|
|
||||||
}
|
|
||||||
|
|
||||||
this.submitForm(this.action, params, response => {
|
|
||||||
|
|
||||||
this.$buefy.toast.open({
|
|
||||||
message: "Message sent! Thank you for your feedback.",
|
|
||||||
type: 'is-info',
|
|
||||||
duration: 4000, // 4 seconds
|
|
||||||
})
|
|
||||||
|
|
||||||
this.showDialog = false
|
|
||||||
// clear out message, in case they need to send another
|
|
||||||
this.message = ""
|
|
||||||
})
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let FeedbackFormData = {
|
|
||||||
referrer: null,
|
|
||||||
userUUID: null,
|
|
||||||
userName: null,
|
|
||||||
pleaseReply: false,
|
|
||||||
userEmail: null,
|
|
||||||
showDialog: false,
|
|
||||||
}
|
|
|
@ -1,63 +1,5 @@
|
||||||
## -*- coding: utf-8; -*-
|
## -*- coding: utf-8; -*-
|
||||||
|
|
||||||
## TODO: This function signature is getting out of hand...
|
|
||||||
<%def name="autocomplete(field_name, service_url, field_value=None, field_display=None, width='300px', select=None, selected=None, cleared=None, change_clicked=None, options={})">
|
|
||||||
<div id="${field_name}-container" class="autocomplete-container">
|
|
||||||
${h.hidden(field_name, id=field_name, value=field_value)}
|
|
||||||
${h.text(field_name+'-textbox', id=field_name+'-textbox', value=field_display,
|
|
||||||
class_='autocomplete-textbox', style='display: none;' if field_value else '')}
|
|
||||||
<div id="${field_name}-display" class="autocomplete-display"${'' if field_value else ' style="display: none;"'|n}>
|
|
||||||
<span>${field_display or ''}</span>
|
|
||||||
<button type="button" id="${field_name}-change" class="autocomplete-change">Change</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<script type="text/javascript">
|
|
||||||
$(function() {
|
|
||||||
$('#${field_name}-textbox').autocomplete({
|
|
||||||
source: '${service_url}',
|
|
||||||
autoFocus: true,
|
|
||||||
% for key, value in options.items():
|
|
||||||
${key}: ${value},
|
|
||||||
% endfor
|
|
||||||
focus: function(event, ui) {
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
% if select:
|
|
||||||
select: ${select}
|
|
||||||
% else:
|
|
||||||
select: function(event, ui) {
|
|
||||||
$('#${field_name}').val(ui.item.value);
|
|
||||||
$('#${field_name}-display span:first').text(ui.item.label);
|
|
||||||
$('#${field_name}-textbox').hide();
|
|
||||||
$('#${field_name}-display').show();
|
|
||||||
% if selected:
|
|
||||||
${selected}(ui.item.value, ui.item.label);
|
|
||||||
% endif
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
% endif
|
|
||||||
});
|
|
||||||
$('#${field_name}-change').click(function() {
|
|
||||||
% if change_clicked:
|
|
||||||
if (! ${change_clicked}()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
% endif
|
|
||||||
$('#${field_name}').val('');
|
|
||||||
$('#${field_name}-display').hide();
|
|
||||||
with ($('#${field_name}-textbox')) {
|
|
||||||
val('');
|
|
||||||
show();
|
|
||||||
focus();
|
|
||||||
}
|
|
||||||
% if cleared:
|
|
||||||
${cleared}();
|
|
||||||
% endif
|
|
||||||
});
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
</%def>
|
|
||||||
|
|
||||||
<%def name="tailbone_autocomplete_template()">
|
<%def name="tailbone_autocomplete_template()">
|
||||||
<script type="text/x-template" id="tailbone-autocomplete-template">
|
<script type="text/x-template" id="tailbone-autocomplete-template">
|
||||||
<div>
|
<div>
|
||||||
|
|
|
@ -73,7 +73,6 @@
|
||||||
</%def>
|
</%def>
|
||||||
|
|
||||||
<%def name="core_javascript()">
|
<%def name="core_javascript()">
|
||||||
${self.jquery()}
|
|
||||||
${self.vuejs()}
|
${self.vuejs()}
|
||||||
${self.buefy()}
|
${self.buefy()}
|
||||||
${self.fontawesome()}
|
${self.fontawesome()}
|
||||||
|
@ -92,22 +91,32 @@
|
||||||
${h.javascript_link(request.static_url('tailbone:static/js/tailbone.buefy.grid.js') + '?ver={}'.format(tailbone.__version__))}
|
${h.javascript_link(request.static_url('tailbone:static/js/tailbone.buefy.grid.js') + '?ver={}'.format(tailbone.__version__))}
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
var session_timeout = ${request.get_session_timeout() or 'null'};
|
|
||||||
var logout_url = '${request.route_url('logout')}';
|
|
||||||
var noop_url = '${request.route_url('noop')}';
|
|
||||||
$(function() {
|
|
||||||
## NOTE: this code was copied from
|
## NOTE: this code was copied from
|
||||||
## https://bulma.io/documentation/components/navbar/#navbar-menu
|
## https://bulma.io/documentation/components/navbar/#navbar-menu
|
||||||
$('.navbar-burger').click(function() {
|
|
||||||
$('.navbar-burger').toggleClass('is-active');
|
|
||||||
$('.navbar-menu').toggleClass('is-active');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
</%def>
|
|
||||||
|
|
||||||
<%def name="jquery()">
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
${h.javascript_link(h.get_liburl(request, 'jquery'))}
|
|
||||||
|
// Get all "navbar-burger" elements
|
||||||
|
const $navbarBurgers = Array.prototype.slice.call(document.querySelectorAll('.navbar-burger'), 0)
|
||||||
|
|
||||||
|
// Add a click event on each of them
|
||||||
|
$navbarBurgers.forEach( el => {
|
||||||
|
el.addEventListener('click', () => {
|
||||||
|
|
||||||
|
// Get the target from the "data-target" attribute
|
||||||
|
const target = el.dataset.target
|
||||||
|
const $target = document.getElementById(target)
|
||||||
|
|
||||||
|
// Toggle the "is-active" class on both the "navbar-burger" and the "navbar-menu"
|
||||||
|
el.classList.toggle('is-active')
|
||||||
|
$target.classList.toggle('is-active')
|
||||||
|
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
</script>
|
||||||
</%def>
|
</%def>
|
||||||
|
|
||||||
<%def name="vuejs()">
|
<%def name="vuejs()">
|
||||||
|
@ -129,14 +138,12 @@
|
||||||
|
|
||||||
${self.buefy_styles()}
|
${self.buefy_styles()}
|
||||||
|
|
||||||
${h.stylesheet_link(request.static_url('tailbone:static/themes/falafel/css/base.css') + '?ver={}'.format(tailbone.__version__))}
|
${h.stylesheet_link(request.static_url('tailbone:static/css/base.css') + '?ver={}'.format(tailbone.__version__))}
|
||||||
${h.stylesheet_link(request.static_url('tailbone:static/themes/falafel/css/layout.css') + '?ver={}'.format(tailbone.__version__))}
|
${h.stylesheet_link(request.static_url('tailbone:static/css/layout.css') + '?ver={}'.format(tailbone.__version__))}
|
||||||
${h.stylesheet_link(request.static_url('tailbone:static/css/grids.css') + '?ver={}'.format(tailbone.__version__))}
|
${h.stylesheet_link(request.static_url('tailbone:static/css/grids.css') + '?ver={}'.format(tailbone.__version__))}
|
||||||
${h.stylesheet_link(request.static_url('tailbone:static/themes/falafel/css/grids.css') + '?ver={}'.format(tailbone.__version__))}
|
${h.stylesheet_link(request.static_url('tailbone:static/css/grids.rowstatus.css') + '?ver={}'.format(tailbone.__version__))}
|
||||||
${h.stylesheet_link(request.static_url('tailbone:static/themes/falafel/css/grids.rowstatus.css') + '?ver={}'.format(tailbone.__version__))}
|
${h.stylesheet_link(request.static_url('tailbone:static/css/filters.css') + '?ver={}'.format(tailbone.__version__))}
|
||||||
## ${h.stylesheet_link(request.static_url('tailbone:static/css/filters.css') + '?ver={}'.format(tailbone.__version__))}
|
${h.stylesheet_link(request.static_url('tailbone:static/css/forms.css') + '?ver={}'.format(tailbone.__version__))}
|
||||||
${h.stylesheet_link(request.static_url('tailbone:static/themes/falafel/css/filters.css') + '?ver={}'.format(tailbone.__version__))}
|
|
||||||
${h.stylesheet_link(request.static_url('tailbone:static/themes/falafel/css/forms.css') + '?ver={}'.format(tailbone.__version__))}
|
|
||||||
${h.stylesheet_link(request.static_url('tailbone:static/css/diffs.css') + '?ver={}'.format(tailbone.__version__))}
|
${h.stylesheet_link(request.static_url('tailbone:static/css/diffs.css') + '?ver={}'.format(tailbone.__version__))}
|
||||||
|
|
||||||
${h.stylesheet_link(request.static_url('tailbone:static/css/codehilite.css') + '?ver={}'.format(tailbone.__version__))}
|
${h.stylesheet_link(request.static_url('tailbone:static/css/codehilite.css') + '?ver={}'.format(tailbone.__version__))}
|
||||||
|
@ -162,12 +169,6 @@
|
||||||
% endif
|
% endif
|
||||||
</%def>
|
</%def>
|
||||||
|
|
||||||
## TODO: this is only being referenced by the progress template i think?
|
|
||||||
## (so, should make a Buefy progress page at least)
|
|
||||||
<%def name="jquery_theme()">
|
|
||||||
${h.stylesheet_link(h.get_liburl(request, 'jquery_ui'))}
|
|
||||||
</%def>
|
|
||||||
|
|
||||||
<%def name="extra_styles()"></%def>
|
<%def name="extra_styles()"></%def>
|
||||||
|
|
||||||
<%def name="head_tags()"></%def>
|
<%def name="head_tags()"></%def>
|
||||||
|
@ -201,14 +202,14 @@
|
||||||
@select="globalSearchSelect">
|
@select="globalSearchSelect">
|
||||||
</b-autocomplete>
|
</b-autocomplete>
|
||||||
</div>
|
</div>
|
||||||
<a role="button" class="navbar-burger" aria-label="menu" aria-expanded="false">
|
<a role="button" class="navbar-burger" data-target="navbar-menu" aria-label="menu" aria-expanded="false">
|
||||||
<span aria-hidden="true"></span>
|
<span aria-hidden="true"></span>
|
||||||
<span aria-hidden="true"></span>
|
<span aria-hidden="true"></span>
|
||||||
<span aria-hidden="true"></span>
|
<span aria-hidden="true"></span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="navbar-menu">
|
<div class="navbar-menu" id="navbar-menu">
|
||||||
<div class="navbar-start">
|
<div class="navbar-start">
|
||||||
|
|
||||||
<div v-if="globalSearchData.length"
|
<div v-if="globalSearchData.length"
|
||||||
|
@ -742,7 +743,7 @@
|
||||||
<%def name="declare_whole_page_vars()">
|
<%def name="declare_whole_page_vars()">
|
||||||
${page_help.declare_vars()}
|
${page_help.declare_vars()}
|
||||||
${multi_file_upload.declare_vars()}
|
${multi_file_upload.declare_vars()}
|
||||||
${h.javascript_link(request.static_url('tailbone:static/themes/falafel/js/tailbone.feedback.js') + '?ver={}'.format(tailbone.__version__))}
|
${h.javascript_link(request.static_url('tailbone:static/js/tailbone.feedback.js') + '?ver={}'.format(tailbone.__version__))}
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
|
||||||
let WholePage = {
|
let WholePage = {
|
||||||
|
|
|
@ -2,23 +2,6 @@
|
||||||
<%inherit file="/master/view.mako" />
|
<%inherit file="/master/view.mako" />
|
||||||
<%namespace file="/util.mako" import="view_profiles_helper" />
|
<%namespace file="/util.mako" import="view_profiles_helper" />
|
||||||
|
|
||||||
<%def name="extra_javascript()">
|
|
||||||
${parent.extra_javascript()}
|
|
||||||
% if master.people_detachable and request.has_perm('{}.detach_person'.format(permission_prefix)):
|
|
||||||
<script type="text/javascript">
|
|
||||||
|
|
||||||
$(function() {
|
|
||||||
$('.people .grid .actions a.detach').click(function() {
|
|
||||||
if (! confirm("Are you sure you wish to detach this Person from the Customer?")) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
</script>
|
|
||||||
% endif
|
|
||||||
</%def>
|
|
||||||
|
|
||||||
<%def name="object_helpers()">
|
<%def name="object_helpers()">
|
||||||
${parent.object_helpers()}
|
${parent.object_helpers()}
|
||||||
% if show_profiles_helper and instance.people:
|
% if show_profiles_helper and instance.people:
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
## -*- coding: utf-8; -*-
|
## -*- coding: utf-8; -*-
|
||||||
<%inherit file="/master/view.mako" />
|
<%inherit file="/master/view.mako" />
|
||||||
|
|
||||||
## TODO: this page still uses jQuery but should use Vue.js
|
|
||||||
|
|
||||||
<%def name="extra_styles()">
|
<%def name="extra_styles()">
|
||||||
${parent.extra_styles()}
|
${parent.extra_styles()}
|
||||||
<style type="text/css">
|
<style type="text/css">
|
||||||
|
|
|
@ -1,99 +0,0 @@
|
||||||
## -*- coding: utf-8; -*-
|
|
||||||
|
|
||||||
% if not readonly:
|
|
||||||
<% _focus_rendered = False %>
|
|
||||||
${h.form(form.action_url, id=dform.formid, method='post', enctype='multipart/form-data', **form_kwargs)}
|
|
||||||
${h.csrf_token(request)}
|
|
||||||
% endif
|
|
||||||
|
|
||||||
% if dform.error:
|
|
||||||
<div class="error-messages">
|
|
||||||
<div class="ui-state-error ui-corner-all">
|
|
||||||
<span style="float: left; margin-right: .3em;" class="ui-icon ui-icon-alert"></span>
|
|
||||||
Please see errors below.
|
|
||||||
</div>
|
|
||||||
<div class="ui-state-error ui-corner-all">
|
|
||||||
<span style="float: left; margin-right: .3em;" class="ui-icon ui-icon-alert"></span>
|
|
||||||
${dform.error}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
% endif
|
|
||||||
|
|
||||||
% for field in form.fields:
|
|
||||||
|
|
||||||
## % if readonly or field.name in readonly_fields:
|
|
||||||
% if readonly:
|
|
||||||
${render_field_readonly(field)|n}
|
|
||||||
% elif field not in dform and field in form.readonly_fields:
|
|
||||||
${render_field_readonly(field)|n}
|
|
||||||
% elif field in dform:
|
|
||||||
<% field = dform[field] %>
|
|
||||||
|
|
||||||
% if form.field_visible(field.name):
|
|
||||||
<div class="field-wrapper ${field.name} ${'with-error' if field.error else ''}">
|
|
||||||
% if field.error:
|
|
||||||
<div class="field-error">
|
|
||||||
% for msg in field.error.messages():
|
|
||||||
<span class="error-msg">${msg}</span>
|
|
||||||
% endfor
|
|
||||||
</div>
|
|
||||||
% endif
|
|
||||||
<div class="field-row">
|
|
||||||
<label for="${field.oid}">${form.get_label(field.name)}</label>
|
|
||||||
<div class="field">
|
|
||||||
${field.serialize()|n}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
% if form.has_helptext(field.name):
|
|
||||||
<span class="instructions">${form.render_helptext(field.name)}</span>
|
|
||||||
% endif
|
|
||||||
</div>
|
|
||||||
|
|
||||||
## % if not _focus_rendered and (fieldset.focus is True or fieldset.focus is field):
|
|
||||||
% if not readonly and not _focus_rendered:
|
|
||||||
## % if not field.is_readonly() and getattr(field.renderer, 'needs_focus', True):
|
|
||||||
% if not field.widget.readonly:
|
|
||||||
<script type="text/javascript">
|
|
||||||
$(function() {
|
|
||||||
## % if hasattr(field.renderer, 'focus_name'):
|
|
||||||
## $('#${field.renderer.focus_name}').focus();
|
|
||||||
## % else:
|
|
||||||
## $('#${field.renderer.name}').focus();
|
|
||||||
## % endif
|
|
||||||
$('#${field.oid}').focus();
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
<% _focus_rendered = True %>
|
|
||||||
% endif
|
|
||||||
% endif
|
|
||||||
|
|
||||||
% else:
|
|
||||||
## hidden field
|
|
||||||
${field.serialize()|n}
|
|
||||||
% endif
|
|
||||||
|
|
||||||
% endif
|
|
||||||
|
|
||||||
% endfor
|
|
||||||
|
|
||||||
% if buttons:
|
|
||||||
${buttons|n}
|
|
||||||
% elif not readonly and (buttons is Undefined or (buttons is not None and buttons is not False)):
|
|
||||||
<div class="buttons">
|
|
||||||
## ${h.submit('create', form.create_label if form.creating else form.update_label)}
|
|
||||||
${h.submit('save', getattr(form, 'submit_label', getattr(form, 'save_label', "Submit")), class_='button is-primary')}
|
|
||||||
## % if form.creating and form.allow_successive_creates:
|
|
||||||
## ${h.submit('create_and_continue', form.successive_create_label)}
|
|
||||||
## % endif
|
|
||||||
% if getattr(form, 'show_reset', False):
|
|
||||||
<input type="reset" value="Reset" class="button" />
|
|
||||||
% endif
|
|
||||||
% if getattr(form, 'show_cancel', True):
|
|
||||||
${h.link_to("Cancel", form.cancel_url, class_='cancel button{}'.format(' autodisable' if form.auto_disable_cancel else ''))}
|
|
||||||
% endif
|
|
||||||
</div>
|
|
||||||
% endif
|
|
||||||
|
|
||||||
% if not readonly:
|
|
||||||
${h.end_form()}
|
|
||||||
% endif
|
|
|
@ -1,8 +0,0 @@
|
||||||
## -*- coding: utf-8; -*-
|
|
||||||
|
|
||||||
<div class="form">
|
|
||||||
${form.render_deform(readonly=True)|n}
|
|
||||||
% if buttons:
|
|
||||||
${buttons|n}
|
|
||||||
% endif
|
|
||||||
</div><!-- form -->
|
|
|
@ -1,37 +0,0 @@
|
||||||
## -*- coding: utf-8 -*-
|
|
||||||
<div class="filters" url="${search.request.current_route_url()}">
|
|
||||||
${search.begin()}
|
|
||||||
${search.hidden('filters', 'true')}
|
|
||||||
<% visible = [] %>
|
|
||||||
% for f in search.sorted_filters():
|
|
||||||
<div class="filter" id="filter-${f.name}"${' style="display: none;"' if not search.config.get('include_filter_'+f.name) else ''|n}>
|
|
||||||
${search.checkbox('include_filter_'+f.name)}
|
|
||||||
<label for="${f.name}">${f.label}</label>
|
|
||||||
${f.types_select()}
|
|
||||||
<div class="value">
|
|
||||||
${f.value_control()}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
% if search.config.get('include_filter_'+f.name):
|
|
||||||
<% visible.append(f.name) %>
|
|
||||||
% endif
|
|
||||||
% endfor
|
|
||||||
<div class="buttons">
|
|
||||||
${search.add_filter(visible)}
|
|
||||||
${search.submit('submit', "Search", style='display: none;' if not visible else None)}
|
|
||||||
<button type="reset"${' style="display: none;"' if not visible else ''|n}>Reset</button>
|
|
||||||
</div>
|
|
||||||
${search.end()}
|
|
||||||
% if visible:
|
|
||||||
<script language="javascript" type="text/javascript">
|
|
||||||
filters_to_disable = [
|
|
||||||
% for field in visible:
|
|
||||||
'${field}',
|
|
||||||
% endfor
|
|
||||||
];
|
|
||||||
$(function() {
|
|
||||||
disable_filter_options();
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
% endif
|
|
||||||
</div>
|
|
|
@ -6,19 +6,8 @@
|
||||||
## ##############################################################################
|
## ##############################################################################
|
||||||
<%inherit file="/page.mako" />
|
<%inherit file="/page.mako" />
|
||||||
|
|
||||||
## TODO: this page still uses old-style grid but should use Buefy grid
|
|
||||||
|
|
||||||
<%def name="title()">${model_title_plural} » ${instance_title} » history</%def>
|
<%def name="title()">${model_title_plural} » ${instance_title} » history</%def>
|
||||||
|
|
||||||
<%def name="extra_javascript()">
|
|
||||||
${parent.extra_javascript()}
|
|
||||||
<script type="text/javascript">
|
|
||||||
$(function() {
|
|
||||||
$('.grid-wrapper').gridwrapper();
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
</%def>
|
|
||||||
|
|
||||||
<%def name="content_title()">
|
<%def name="content_title()">
|
||||||
Version History
|
Version History
|
||||||
</%def>
|
</%def>
|
||||||
|
|
|
@ -1,402 +0,0 @@
|
||||||
## -*- coding: utf-8; -*-
|
|
||||||
<%inherit file="/master/view.mako" />
|
|
||||||
|
|
||||||
<%def name="extra_javascript()">
|
|
||||||
${parent.extra_javascript()}
|
|
||||||
<script type="text/javascript">
|
|
||||||
|
|
||||||
## NOTE: we must delay activation of accordions, otherwise they do not
|
|
||||||
## seem to "resize" correctly
|
|
||||||
var customer_accordion_activated = false;
|
|
||||||
var user_accordion_activated = false;
|
|
||||||
|
|
||||||
$(function() {
|
|
||||||
$('#profile-tabs').tabs({
|
|
||||||
activate: function(event, ui) {
|
|
||||||
## activate accordion, first time tab is activated
|
|
||||||
if (ui.newPanel.selector == '#customer-tab') {
|
|
||||||
if (! customer_accordion_activated) {
|
|
||||||
$('#customers-accordion').accordion();
|
|
||||||
customer_accordion_activated = true;
|
|
||||||
}
|
|
||||||
} else if (ui.newPanel.selector == '#user-tab') {
|
|
||||||
if (! user_accordion_activated) {
|
|
||||||
$('#users-accordion').accordion();
|
|
||||||
user_accordion_activated = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
</%def>
|
|
||||||
|
|
||||||
<div id="profile-tabs">
|
|
||||||
<ul>
|
|
||||||
<li><a href="#personal-tab">Personal</a></li>
|
|
||||||
<li><a href="#customer-tab">Customer</a></li>
|
|
||||||
<li><a href="#employee-tab">Employee</a></li>
|
|
||||||
<li><a href="#user-tab">User</a></li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<div id="personal-tab">
|
|
||||||
|
|
||||||
<div style="display: flex; justify-content: space-between;">
|
|
||||||
|
|
||||||
<div>
|
|
||||||
|
|
||||||
<div class="field-wrapper first_name">
|
|
||||||
<div class="field-row">
|
|
||||||
<label>First Name</label>
|
|
||||||
<div class="field">
|
|
||||||
${person.first_name}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="field-wrapper middle_name">
|
|
||||||
<div class="field-row">
|
|
||||||
<label>Middle Name</label>
|
|
||||||
<div class="field">
|
|
||||||
${person.middle_name}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="field-wrapper last_name">
|
|
||||||
<div class="field-row">
|
|
||||||
<label>Last Name</label>
|
|
||||||
<div class="field">
|
|
||||||
${person.last_name}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="field-wrapper street">
|
|
||||||
<div class="field-row">
|
|
||||||
<label>Street 1</label>
|
|
||||||
<div class="field">
|
|
||||||
${person.address.street if person.address else ''}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="field-wrapper street2">
|
|
||||||
<div class="field-row">
|
|
||||||
<label>Street 2</label>
|
|
||||||
<div class="field">
|
|
||||||
${person.address.street2 if person.address else ''}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="field-wrapper city">
|
|
||||||
<div class="field-row">
|
|
||||||
<label>City</label>
|
|
||||||
<div class="field">
|
|
||||||
${person.address.city if person.address else ''}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="field-wrapper state">
|
|
||||||
<div class="field-row">
|
|
||||||
<label>State</label>
|
|
||||||
<div class="field">
|
|
||||||
${person.address.state if person.address else ''}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="field-wrapper zipcode">
|
|
||||||
<div class="field-row">
|
|
||||||
<label>Zipcode</label>
|
|
||||||
<div class="field">
|
|
||||||
${person.address.zipcode if person.address else ''}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
% if person.phones:
|
|
||||||
% for phone in person.phones:
|
|
||||||
<div class="field-wrapper">
|
|
||||||
<div class="field-row">
|
|
||||||
<label>Phone Number</label>
|
|
||||||
<div class="field">
|
|
||||||
${phone.number} (type: ${phone.type})
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
% endfor
|
|
||||||
% else:
|
|
||||||
<div class="field-wrapper">
|
|
||||||
<div class="field-row">
|
|
||||||
<label>Phone Number</label>
|
|
||||||
<div class="field">
|
|
||||||
(none on file)
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
% endif
|
|
||||||
|
|
||||||
% if person.emails:
|
|
||||||
% for email in person.emails:
|
|
||||||
<div class="field-wrapper">
|
|
||||||
<div class="field-row">
|
|
||||||
<label>Email Address</label>
|
|
||||||
<div class="field">
|
|
||||||
${email.address} (type: ${email.type})
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
% endfor
|
|
||||||
% else:
|
|
||||||
<div class="field-wrapper">
|
|
||||||
<div class="field-row">
|
|
||||||
<label>Email Address</label>
|
|
||||||
<div class="field">
|
|
||||||
(none on file)
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
% endif
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
% if request.has_perm('people.view'):
|
|
||||||
${h.link_to("View Person", url('people.view', uuid=person.uuid), class_='button')}
|
|
||||||
% endif
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div><!-- personal-tab -->
|
|
||||||
|
|
||||||
<div id="customer-tab">
|
|
||||||
% if person.customers:
|
|
||||||
<p>${person} is associated with ${len(person.customers)} customer account(s)</p>
|
|
||||||
<br />
|
|
||||||
<div id="customers-accordion">
|
|
||||||
% for customer in person.customers:
|
|
||||||
<h3>${customer.id} - ${customer.name}</h3>
|
|
||||||
<div>
|
|
||||||
|
|
||||||
<div style="display: flex; justify-content: space-between;">
|
|
||||||
|
|
||||||
<div>
|
|
||||||
|
|
||||||
<div class="field-wrapper id">
|
|
||||||
<div class="field-row">
|
|
||||||
<label>ID</label>
|
|
||||||
<div class="field">
|
|
||||||
${customer.id or ''}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="field-wrapper name">
|
|
||||||
<div class="field-row">
|
|
||||||
<label>Name</label>
|
|
||||||
<div class="field">
|
|
||||||
${customer.name}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
% if customer.phones:
|
|
||||||
% for phone in customer.phones:
|
|
||||||
<div class="field-wrapper">
|
|
||||||
<div class="field-row">
|
|
||||||
<label>Phone Number</label>
|
|
||||||
<div class="field">
|
|
||||||
${phone.number} (type: ${phone.type})
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
% endfor
|
|
||||||
% else:
|
|
||||||
<div class="field-wrapper">
|
|
||||||
<div class="field-row">
|
|
||||||
<label>Phone Number</label>
|
|
||||||
<div class="field">
|
|
||||||
(none on file)
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
% endif
|
|
||||||
|
|
||||||
% if customer.emails:
|
|
||||||
% for email in customer.emails:
|
|
||||||
<div class="field-wrapper">
|
|
||||||
<div class="field-row">
|
|
||||||
<label>Email Address</label>
|
|
||||||
<div class="field">
|
|
||||||
${email.address} (type: ${email.type})
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
% endfor
|
|
||||||
% else:
|
|
||||||
<div class="field-wrapper">
|
|
||||||
<div class="field-row">
|
|
||||||
<label>Email Address</label>
|
|
||||||
<div class="field">
|
|
||||||
(none on file)
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
% endif
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
% if request.has_perm('customers.view'):
|
|
||||||
${h.link_to("View Customer", url('customers.view', uuid=customer.uuid), class_='button')}
|
|
||||||
% endif
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
% endfor
|
|
||||||
</div>
|
|
||||||
|
|
||||||
% else:
|
|
||||||
<p>${person} has never been a customer.</p>
|
|
||||||
% endif
|
|
||||||
</div><!-- customer-tab -->
|
|
||||||
|
|
||||||
<div id="employee-tab">
|
|
||||||
% if employee:
|
|
||||||
<div style="display: flex; justify-content: space-between;">
|
|
||||||
|
|
||||||
<div>
|
|
||||||
|
|
||||||
<div class="field-wrapper id">
|
|
||||||
<div class="field-row">
|
|
||||||
<label>ID</label>
|
|
||||||
<div class="field">
|
|
||||||
${employee.id or ''}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="field-wrapper display_name">
|
|
||||||
<div class="field-row">
|
|
||||||
<label>Display Name</label>
|
|
||||||
<div class="field">
|
|
||||||
${employee.display_name or ''}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="field-wrapper status">
|
|
||||||
<div class="field-row">
|
|
||||||
<label>Status</label>
|
|
||||||
<div class="field">
|
|
||||||
${enum.EMPLOYEE_STATUS.get(employee.status, '')}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
% if employee.phones:
|
|
||||||
% for phone in employee.phones:
|
|
||||||
<div class="field-wrapper">
|
|
||||||
<div class="field-row">
|
|
||||||
<label>Phone Number</label>
|
|
||||||
<div class="field">
|
|
||||||
${phone.number} (type: ${phone.type})
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
% endfor
|
|
||||||
% else:
|
|
||||||
<div class="field-wrapper">
|
|
||||||
<div class="field-row">
|
|
||||||
<label>Phone Number</label>
|
|
||||||
<div class="field">
|
|
||||||
(none on file)
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
% endif
|
|
||||||
|
|
||||||
% if employee.emails:
|
|
||||||
% for email in employee.emails:
|
|
||||||
<div class="field-wrapper">
|
|
||||||
<div class="field-row">
|
|
||||||
<label>Email Address</label>
|
|
||||||
<div class="field">
|
|
||||||
${email.address} (type: ${email.type})
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
% endfor
|
|
||||||
% else:
|
|
||||||
<div class="field-wrapper">
|
|
||||||
<div class="field-row">
|
|
||||||
<label>Email Address</label>
|
|
||||||
<div class="field">
|
|
||||||
(none on file)
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
% endif
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
% if request.has_perm('employees.view'):
|
|
||||||
${h.link_to("View Employee", url('employees.view', uuid=employee.uuid), class_='button')}
|
|
||||||
% endif
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
% else:
|
|
||||||
<p>${person} has never been an employee.</p>
|
|
||||||
% endif
|
|
||||||
</div><!-- employee-tab -->
|
|
||||||
|
|
||||||
<div id="user-tab">
|
|
||||||
% if person.users:
|
|
||||||
<p>${person} is associated with ${len(person.users)} user account(s)</p>
|
|
||||||
<br />
|
|
||||||
<div id="users-accordion">
|
|
||||||
% for user in person.users:
|
|
||||||
<h3>${user.username}</h3>
|
|
||||||
<div>
|
|
||||||
|
|
||||||
<div style="display: flex; justify-content: space-between;">
|
|
||||||
|
|
||||||
<div>
|
|
||||||
|
|
||||||
<div class="field-wrapper id">
|
|
||||||
<div class="field-row">
|
|
||||||
<label>Username</label>
|
|
||||||
<div class="field">
|
|
||||||
${user.username}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
% if request.has_perm('users.view'):
|
|
||||||
${h.link_to("View User", url('users.view', uuid=user.uuid), class_='button')}
|
|
||||||
% endif
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
% endfor
|
|
||||||
</div>
|
|
||||||
|
|
||||||
% else:
|
|
||||||
<p>${person} has never been a user.</p>
|
|
||||||
% endif
|
|
||||||
</div><!-- user-tab -->
|
|
||||||
|
|
||||||
</div><!-- profile-tabs -->
|
|
|
@ -1,99 +1,6 @@
|
||||||
## -*- coding: utf-8; -*-
|
## -*- coding: utf-8; -*-
|
||||||
<%inherit file="/batch/create.mako" />
|
<%inherit file="/batch/create.mako" />
|
||||||
|
|
||||||
<%def name="extra_javascript()">
|
## TODO: deprecate / remove this
|
||||||
${parent.extra_javascript()}
|
|
||||||
${self.func_show_mode()}
|
|
||||||
<script type="text/javascript">
|
|
||||||
|
|
||||||
var purchases_field = '${purchases_field}';
|
|
||||||
var purchases = null; // TODO: where is this used?
|
|
||||||
|
|
||||||
function vendor_selected(uuid, name) {
|
|
||||||
var mode = $('.mode select').val();
|
|
||||||
if (mode == ${enum.PURCHASE_BATCH_MODE_RECEIVING} || mode == ${enum.PURCHASE_BATCH_MODE_COSTING}) {
|
|
||||||
var purchases = $('.purchase_uuid select');
|
|
||||||
purchases.empty();
|
|
||||||
|
|
||||||
var data = {'vendor_uuid': uuid, 'mode': mode};
|
|
||||||
$.get('${url('purchases.batch.eligible_purchases')}', data, function(data) {
|
|
||||||
if (data.error) {
|
|
||||||
alert(data.error);
|
|
||||||
} else {
|
|
||||||
$.each(data.purchases, function(i, purchase) {
|
|
||||||
purchases.append($('<option value="' + purchase.key + '">' + purchase.display + '</option>'));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// TODO: apparently refresh doesn't work right?
|
|
||||||
// http://stackoverflow.com/a/10280078
|
|
||||||
// purchases.selectmenu('refresh');
|
|
||||||
purchases.selectmenu('destroy').selectmenu();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function vendor_cleared() {
|
|
||||||
var purchases = $('.purchase_uuid select');
|
|
||||||
purchases.empty();
|
|
||||||
|
|
||||||
// TODO: apparently refresh doesn't work right?
|
|
||||||
// http://stackoverflow.com/a/10280078
|
|
||||||
// purchases.selectmenu('refresh');
|
|
||||||
purchases.selectmenu('destroy').selectmenu();
|
|
||||||
}
|
|
||||||
|
|
||||||
$(function() {
|
|
||||||
|
|
||||||
$('.field-wrapper.mode select').selectmenu({
|
|
||||||
change: function(event, ui) {
|
|
||||||
show_mode(ui.item.value);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
show_mode(${batch.mode or enum.PURCHASE_BATCH_MODE_ORDERING});
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
</script>
|
|
||||||
</%def>
|
|
||||||
|
|
||||||
<%def name="func_show_mode()">
|
|
||||||
<script type="text/javascript">
|
|
||||||
|
|
||||||
function show_mode(mode) {
|
|
||||||
if (mode == ${enum.PURCHASE_BATCH_MODE_ORDERING}) {
|
|
||||||
$('.field-wrapper.store_uuid').show();
|
|
||||||
$('.field-wrapper.' + purchases_field).hide();
|
|
||||||
$('.field-wrapper.department_uuid').show();
|
|
||||||
$('.field-wrapper.buyer_uuid').show();
|
|
||||||
$('.field-wrapper.date_ordered').show();
|
|
||||||
$('.field-wrapper.date_received').hide();
|
|
||||||
$('.field-wrapper.po_number').show();
|
|
||||||
$('.field-wrapper.invoice_date').hide();
|
|
||||||
$('.field-wrapper.invoice_number').hide();
|
|
||||||
} else if (mode == ${enum.PURCHASE_BATCH_MODE_RECEIVING}) {
|
|
||||||
$('.field-wrapper.store_uuid').hide();
|
|
||||||
$('.field-wrapper.purchase_uuid').show();
|
|
||||||
$('.field-wrapper.department_uuid').hide();
|
|
||||||
$('.field-wrapper.buyer_uuid').hide();
|
|
||||||
$('.field-wrapper.date_ordered').hide();
|
|
||||||
$('.field-wrapper.date_received').show();
|
|
||||||
$('.field-wrapper.invoice_date').show();
|
|
||||||
$('.field-wrapper.invoice_number').show();
|
|
||||||
} else if (mode == ${enum.PURCHASE_BATCH_MODE_COSTING}) {
|
|
||||||
$('.field-wrapper.store_uuid').hide();
|
|
||||||
$('.field-wrapper.purchase_uuid').show();
|
|
||||||
$('.field-wrapper.department_uuid').hide();
|
|
||||||
$('.field-wrapper.buyer_uuid').hide();
|
|
||||||
$('.field-wrapper.date_ordered').hide();
|
|
||||||
$('.field-wrapper.date_received').hide();
|
|
||||||
$('.field-wrapper.invoice_date').show();
|
|
||||||
$('.field-wrapper.invoice_number').show();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
</script>
|
|
||||||
</%def>
|
|
||||||
|
|
||||||
${parent.body()}
|
${parent.body()}
|
||||||
|
|
|
@ -1,97 +1,85 @@
|
||||||
## -*- coding: utf-8; -*-
|
## -*- coding: utf-8; -*-
|
||||||
<%inherit file="/master/index.mako" />
|
<%inherit file="/master/index.mako" />
|
||||||
|
|
||||||
<%def name="extra_javascript()">
|
<%def name="grid_tools()">
|
||||||
${parent.extra_javascript()}
|
${parent.grid_tools()}
|
||||||
|
|
||||||
|
<b-button type="is-primary"
|
||||||
|
@click="changeStatusInit()"
|
||||||
|
:disabled="!selected_uuids.length">
|
||||||
|
Change Status
|
||||||
|
</b-button>
|
||||||
|
|
||||||
|
<b-modal has-modal-card
|
||||||
|
:active.sync="changeStatusShowDialog">
|
||||||
|
<div class="modal-card">
|
||||||
|
|
||||||
|
<header class="modal-card-head">
|
||||||
|
<p class="modal-card-title">Change Status</p>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<section class="modal-card-body">
|
||||||
|
|
||||||
|
<p class="block">
|
||||||
|
Please choose the appropriate status for the selected credits.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<b-field label="Status">
|
||||||
|
<b-select v-model="changeStatusValue">
|
||||||
|
<option v-for="status in changeStatusOptions"
|
||||||
|
:key="status.value"
|
||||||
|
:value="status.value">
|
||||||
|
{{ status.label }}
|
||||||
|
</option>
|
||||||
|
</b-select>
|
||||||
|
</b-field>
|
||||||
|
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<footer class="modal-card-foot">
|
||||||
|
<b-button @click="changeStatusShowDialog = false">
|
||||||
|
Cancel
|
||||||
|
</b-button>
|
||||||
|
<b-button type="is-primary"
|
||||||
|
@click="changeStatusSubmit()"
|
||||||
|
:disabled="changeStatusSubmitting || !changeStatusValue"
|
||||||
|
icon-pack="fas"
|
||||||
|
icon-left="save">
|
||||||
|
{{ changeStatusSubmitting ? "Working, please wait..." : "Save" }}
|
||||||
|
</b-button>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</b-modal>
|
||||||
|
|
||||||
|
${h.form(url('purchases.credits.change_status'), ref='changeStatusForm')}
|
||||||
|
${h.csrf_token(request)}
|
||||||
|
${h.hidden('uuids', **{':value': 'selected_uuids'})}
|
||||||
|
${h.hidden('status', **{':value': 'changeStatusValue'})}
|
||||||
|
${h.end_form()}
|
||||||
|
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
<%def name="modify_this_page_vars()">
|
||||||
|
${parent.modify_this_page_vars()}
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
|
||||||
function update_change_status_button() {
|
${grid.component_studly}Data.changeStatusShowDialog = false
|
||||||
var count = $('.grid tr:not(.header) td.checkbox input:checked').length;
|
${grid.component_studly}Data.changeStatusOptions = ${json.dumps(status_options)|n}
|
||||||
$('button.change-status').button('option', 'disabled', count < 1);
|
${grid.component_studly}Data.changeStatusValue = null
|
||||||
|
${grid.component_studly}Data.changeStatusSubmitting = false
|
||||||
|
|
||||||
|
${grid.component_studly}.methods.changeStatusInit = function() {
|
||||||
|
this.changeStatusValue = null
|
||||||
|
this.changeStatusShowDialog = true
|
||||||
}
|
}
|
||||||
|
|
||||||
$(function() {
|
${grid.component_studly}.methods.changeStatusSubmit = function() {
|
||||||
|
this.changeStatusSubmitting = true
|
||||||
$('.grid-wrapper').on('click', 'tr.header td.checkbox input', function() {
|
this.$refs.changeStatusForm.submit()
|
||||||
update_change_status_button();
|
|
||||||
});
|
|
||||||
|
|
||||||
$('.grid-wrapper').on('click', '.grid tr:not(.header) td.checkbox input', function() {
|
|
||||||
update_change_status_button();
|
|
||||||
});
|
|
||||||
$('.grid-wrapper').on('click', '.grid tr:not(.header)', function() {
|
|
||||||
update_change_status_button();
|
|
||||||
});
|
|
||||||
|
|
||||||
$('button.change-status').click(function() {
|
|
||||||
var uuids = [];
|
|
||||||
$('.grid tr:not(.header) td.checkbox input:checked').each(function() {
|
|
||||||
uuids.push($(this).parents('tr:first').data('uuid'));
|
|
||||||
});
|
|
||||||
if (! uuids.length) {
|
|
||||||
alert("You must first select one or more credits.");
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var form = $('form[name="change-status"]');
|
|
||||||
form.find('[name="uuids"]').val(uuids.toString());
|
|
||||||
|
|
||||||
$('#change-status-dialog').dialog({
|
|
||||||
title: "Change Credit Status",
|
|
||||||
width: 500,
|
|
||||||
height: 300,
|
|
||||||
modal: true,
|
|
||||||
open: function() {
|
|
||||||
// TODO: why must we do this here instead of using auto-enhance ?
|
|
||||||
$('#change-status-dialog select[name="status"]').selectmenu();
|
|
||||||
},
|
|
||||||
buttons: [
|
|
||||||
{
|
|
||||||
text: "Submit",
|
|
||||||
click: function(event) {
|
|
||||||
disable_button(dialog_button(event));
|
|
||||||
form.submit();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: "Cancel",
|
|
||||||
click: function() {
|
|
||||||
$(this).dialog('close');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
</%def>
|
</%def>
|
||||||
|
|
||||||
<%def name="grid_tools()">
|
|
||||||
${parent.grid_tools()}
|
|
||||||
<button type="button" class="change-status" disabled="disabled">Change Status</button>
|
|
||||||
</%def>
|
|
||||||
|
|
||||||
${parent.body()}
|
${parent.body()}
|
||||||
|
|
||||||
<div id="change-status-dialog" style="display: none;">
|
|
||||||
${h.form(url('purchases.credits.change_status'), name='change-status')}
|
|
||||||
${h.csrf_token(request)}
|
|
||||||
${h.hidden('uuids')}
|
|
||||||
|
|
||||||
<br />
|
|
||||||
<p>Please choose the appropriate status for the selected credits.</p>
|
|
||||||
|
|
||||||
<div class="fieldset">
|
|
||||||
|
|
||||||
<div class="field-wrapper status">
|
|
||||||
<label for="status">Status</label>
|
|
||||||
<div class="field">
|
|
||||||
${h.select('status', None, status_options)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
${h.end_form()}
|
|
||||||
</div>
|
|
||||||
|
|
|
@ -1,102 +1,13 @@
|
||||||
## -*- coding: utf-8 -*-
|
## -*- coding: utf-8; -*-
|
||||||
<%inherit file="/base.mako" />
|
<%inherit file="/page.mako" />
|
||||||
<%namespace file="/autocomplete.mako" import="autocomplete" />
|
|
||||||
|
|
||||||
<%def name="title()">${page_title}</%def>
|
<%def name="title()">${page_title}</%def>
|
||||||
|
|
||||||
<%def name="extra_javascript()">
|
|
||||||
${parent.extra_javascript()}
|
|
||||||
<script type="text/javascript">
|
|
||||||
|
|
||||||
var data_modified = false;
|
|
||||||
var okay_to_leave = true;
|
|
||||||
var previous_selections = {};
|
|
||||||
% if weekdays is not Undefined:
|
|
||||||
var weekdays = [
|
|
||||||
% for i, day in enumerate(weekdays, 1):
|
|
||||||
'${day.strftime('%a %d %b %Y')}'${',' if i < len(weekdays) else ''}
|
|
||||||
% endfor
|
|
||||||
];
|
|
||||||
% endif
|
|
||||||
|
|
||||||
window.onbeforeunload = function() {
|
|
||||||
if (! okay_to_leave) {
|
|
||||||
return "If you leave this page, you will lose all unsaved changes!";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function employee_selected(uuid, name) {
|
|
||||||
$('#filter-form').submit();
|
|
||||||
}
|
|
||||||
|
|
||||||
function confirm_leave() {
|
|
||||||
if (data_modified) {
|
|
||||||
if (confirm("If you navigate away from this page now, you will lose " +
|
|
||||||
"unsaved changes.\n\nAre you sure you wish to do this?")) {
|
|
||||||
okay_to_leave = true;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function date_selected(dateText, inst) {
|
|
||||||
if (confirm_leave()) {
|
|
||||||
$('#filter-form').submit();
|
|
||||||
} else {
|
|
||||||
// revert date value
|
|
||||||
$('.week-picker input[name="date"]').val($('.week-picker').data('week'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$(function() {
|
|
||||||
|
|
||||||
$('#filter-form').submit(function() {
|
|
||||||
$('.timesheet-header').mask("Fetching data");
|
|
||||||
});
|
|
||||||
|
|
||||||
$('.timesheet-header select').each(function() {
|
|
||||||
previous_selections[$(this).attr('name')] = $(this).val();
|
|
||||||
});
|
|
||||||
|
|
||||||
$('.timesheet-header select').selectmenu({
|
|
||||||
change: function(event, ui) {
|
|
||||||
if (confirm_leave()) {
|
|
||||||
$('#filter-form').submit();
|
|
||||||
} else {
|
|
||||||
var select = ui.item.element.parents('select');
|
|
||||||
select.val(previous_selections[select.attr('name')]);
|
|
||||||
select.selectmenu('refresh');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$('.timesheet-header a.goto').click(function() {
|
|
||||||
$('.timesheet-header').mask("Fetching data");
|
|
||||||
});
|
|
||||||
|
|
||||||
$('.week-picker button.nav').click(function() {
|
|
||||||
if (confirm_leave()) {
|
|
||||||
$('.week-picker input[name="date"]').val($(this).data('date'));
|
|
||||||
$('#filter-form').submit();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
</script>
|
|
||||||
</%def>
|
|
||||||
|
|
||||||
<%def name="extra_styles()">
|
<%def name="extra_styles()">
|
||||||
${parent.extra_styles()}
|
${parent.extra_styles()}
|
||||||
${h.stylesheet_link(request.static_url('tailbone:static/css/timesheet.css'))}
|
${h.stylesheet_link(request.static_url('tailbone:static/css/timesheet.css'))}
|
||||||
</%def>
|
</%def>
|
||||||
|
|
||||||
<%def name="edit_timetable_javascript()">
|
|
||||||
${h.javascript_link(request.static_url('tailbone:static/js/tailbone.edit-shifts.js'))}
|
|
||||||
</%def>
|
|
||||||
|
|
||||||
<%def name="edit_timetable_styles()">
|
<%def name="edit_timetable_styles()">
|
||||||
<style type="text/css">
|
<style type="text/css">
|
||||||
.timesheet .day {
|
.timesheet .day {
|
||||||
|
@ -304,5 +215,9 @@
|
||||||
|
|
||||||
<%def name="render_extra_totals(employee)"></%def>
|
<%def name="render_extra_totals(employee)"></%def>
|
||||||
|
|
||||||
|
<%def name="page_content()">
|
||||||
${self.timesheet_wrapper()}
|
${self.timesheet_wrapper()}
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
|
||||||
|
${parent.body()}
|
||||||
|
|
|
@ -1,60 +1,6 @@
|
||||||
## -*- coding: utf-8 -*-
|
## -*- coding: utf-8; -*-
|
||||||
<%inherit file="/shifts/base.mako" />
|
<%inherit file="/shifts/base.mako" />
|
||||||
|
|
||||||
<%def name="extra_javascript()">
|
|
||||||
${parent.extra_javascript()}
|
|
||||||
${self.edit_timetable_javascript()}
|
|
||||||
<script type="text/javascript">
|
|
||||||
|
|
||||||
$(function() {
|
|
||||||
|
|
||||||
% if allow_clear:
|
|
||||||
$('.clear-schedule').click(function() {
|
|
||||||
if (confirm("This will remove all shifts from the schedule you're " +
|
|
||||||
"currently viewing.\n\nAre you sure you wish to do this?")) {
|
|
||||||
$(this).button('disable').button('option', 'label', "Clearing...");
|
|
||||||
okay_to_leave = true;
|
|
||||||
$('#clear-schedule-form').submit();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
% endif
|
|
||||||
|
|
||||||
$('#copy-week').datepicker({
|
|
||||||
dateFormat: 'mm/dd/yy'
|
|
||||||
});
|
|
||||||
|
|
||||||
$('.copy-schedule').click(function() {
|
|
||||||
$('#copy-details').dialog({
|
|
||||||
modal: true,
|
|
||||||
title: "Copy from Another Week",
|
|
||||||
width: '500px',
|
|
||||||
buttons: [
|
|
||||||
{
|
|
||||||
text: "Copy Schedule",
|
|
||||||
click: function(event) {
|
|
||||||
if (! $('#copy-week').val()) {
|
|
||||||
alert("You must specify the week from which to copy shift data.");
|
|
||||||
$('#copy-week').focus();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
disable_button(dialog_button(event), "Copying Schedule");
|
|
||||||
$('#copy-schedule-form').submit();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: "Cancel",
|
|
||||||
click: function() {
|
|
||||||
$('#copy-details').dialog('close');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
</%def>
|
|
||||||
|
|
||||||
<%def name="extra_styles()">
|
<%def name="extra_styles()">
|
||||||
${parent.extra_styles()}
|
${parent.extra_styles()}
|
||||||
${self.edit_timetable_styles()}
|
${self.edit_timetable_styles()}
|
||||||
|
@ -97,6 +43,7 @@
|
||||||
</div>
|
</div>
|
||||||
</%def>
|
</%def>
|
||||||
|
|
||||||
|
<%def name="page_content()">
|
||||||
|
|
||||||
${self.timesheet_wrapper(with_edit_form=True)}
|
${self.timesheet_wrapper(with_edit_form=True)}
|
||||||
|
|
||||||
|
@ -137,3 +84,9 @@ ${h.end_form()}
|
||||||
<button type="button"><span class="ui-icon ui-icon-trash"></span></button>
|
<button type="button"><span class="ui-icon ui-icon-trash"></span></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
|
||||||
|
${parent.body()}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
## -*- coding: utf-8 -*-
|
## -*- coding: utf-8; -*-
|
||||||
<%inherit file="/shifts/base.mako" />
|
<%inherit file="/shifts/base.mako" />
|
||||||
|
|
||||||
<%def name="context_menu()">
|
<%def name="context_menu()">
|
||||||
|
@ -25,4 +25,4 @@
|
||||||
</%def>
|
</%def>
|
||||||
|
|
||||||
|
|
||||||
${self.timesheet_wrapper()}
|
${parent.body()}
|
||||||
|
|
|
@ -1,58 +1,6 @@
|
||||||
## -*- coding: utf-8 -*-
|
## -*- coding: utf-8; -*-
|
||||||
<%inherit file="/shifts/base.mako" />
|
<%inherit file="/shifts/base.mako" />
|
||||||
|
|
||||||
<%def name="extra_javascript()">
|
|
||||||
${parent.extra_javascript()}
|
|
||||||
${h.javascript_link(request.static_url('tailbone:static/js/tailbone.timesheet.edit.js'))}
|
|
||||||
<script type="text/javascript">
|
|
||||||
|
|
||||||
show_timepicker = false;
|
|
||||||
|
|
||||||
$(function() {
|
|
||||||
|
|
||||||
$('.timesheet').on('click', '.day', function() {
|
|
||||||
editing_day = $(this);
|
|
||||||
var editor = $('#day-editor');
|
|
||||||
var employee = editing_day.siblings('.employee').text();
|
|
||||||
var date = weekdays[editing_day.get(0).cellIndex - 1];
|
|
||||||
var shifts = editor.children('.shifts');
|
|
||||||
shifts.empty();
|
|
||||||
editing_day.children('.shift:not(.deleted)').each(function() {
|
|
||||||
var uuid = $(this).data('uuid');
|
|
||||||
var times = $.trim($(this).children('span').text()).split(' - ');
|
|
||||||
times[0] = times[0] == '??' ? '' : times[0];
|
|
||||||
times[1] = times[1] == '??' ? '' : times[1];
|
|
||||||
add_shift(false, uuid, times[0], times[1]);
|
|
||||||
});
|
|
||||||
if (! shifts.children('.shift').length) {
|
|
||||||
add_shift();
|
|
||||||
}
|
|
||||||
editor.dialog({
|
|
||||||
modal: true,
|
|
||||||
title: employee + ' - ' + date,
|
|
||||||
position: {my: 'center', at: 'center', of: editing_day},
|
|
||||||
width: 'auto',
|
|
||||||
autoResize: true,
|
|
||||||
buttons: [
|
|
||||||
{
|
|
||||||
text: "Save Changes",
|
|
||||||
click: save_dialog
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: "Cancel",
|
|
||||||
click: function() {
|
|
||||||
editor.dialog('close');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
</script>
|
|
||||||
</%def>
|
|
||||||
|
|
||||||
<%def name="extra_styles()">
|
<%def name="extra_styles()">
|
||||||
${parent.extra_styles()}
|
${parent.extra_styles()}
|
||||||
${self.edit_timetable_styles()}
|
${self.edit_timetable_styles()}
|
||||||
|
@ -88,6 +36,7 @@
|
||||||
${h.csrf_token(request)}
|
${h.csrf_token(request)}
|
||||||
</%def>
|
</%def>
|
||||||
|
|
||||||
|
<%def name="page_content()">
|
||||||
|
|
||||||
${self.timesheet_wrapper(with_edit_form=True, change_employee='confirm_leave')}
|
${self.timesheet_wrapper(with_edit_form=True, change_employee='confirm_leave')}
|
||||||
|
|
||||||
|
@ -102,3 +51,8 @@ ${self.timesheet_wrapper(with_edit_form=True, change_employee='confirm_leave')}
|
||||||
<button type="button"><span class="ui-icon ui-icon-trash"></span></button>
|
<button type="button"><span class="ui-icon ui-icon-trash"></span></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
|
||||||
|
${parent.body()}
|
||||||
|
|
|
@ -1,17 +0,0 @@
|
||||||
## -*- coding: utf-8 -*-
|
|
||||||
<%inherit file="/master/create.mako" />
|
|
||||||
|
|
||||||
<%def name="extra_javascript()">
|
|
||||||
${parent.extra_javascript()}
|
|
||||||
<script type="text/javascript">
|
|
||||||
$(function() {
|
|
||||||
|
|
||||||
$('.field-wrapper.client_uuid select').selectmenu();
|
|
||||||
|
|
||||||
$('.field-wrapper.appliance_type select').selectmenu();
|
|
||||||
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
</%def>
|
|
||||||
|
|
||||||
${parent.body()}
|
|
|
@ -1,17 +0,0 @@
|
||||||
## -*- coding: utf-8 -*-
|
|
||||||
<%inherit file="/master/edit.mako" />
|
|
||||||
|
|
||||||
<%def name="head_tags()">
|
|
||||||
${parent.head_tags()}
|
|
||||||
<script type="text/javascript">
|
|
||||||
$(function() {
|
|
||||||
|
|
||||||
$('.field-wrapper.client_uuid select').selectmenu();
|
|
||||||
|
|
||||||
$('.field-wrapper.appliance_type select').selectmenu();
|
|
||||||
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
</%def>
|
|
||||||
|
|
||||||
${parent.body()}
|
|
|
@ -2,7 +2,7 @@
|
||||||
################################################################################
|
################################################################################
|
||||||
#
|
#
|
||||||
# Rattail -- Retail Software Framework
|
# Rattail -- Retail Software Framework
|
||||||
# Copyright © 2010-2022 Lance Edgar
|
# Copyright © 2010-2023 Lance Edgar
|
||||||
#
|
#
|
||||||
# This file is part of Rattail.
|
# This file is part of Rattail.
|
||||||
#
|
#
|
||||||
|
@ -24,10 +24,6 @@
|
||||||
Views for "true" purchase credits
|
Views for "true" purchase credits
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import unicode_literals, absolute_import
|
|
||||||
|
|
||||||
import six
|
|
||||||
|
|
||||||
from rattail.db import model
|
from rattail.db import model
|
||||||
|
|
||||||
from webhelpers2.html import tags
|
from webhelpers2.html import tags
|
||||||
|
@ -112,7 +108,7 @@ class PurchaseCreditView(MasterView):
|
||||||
g.filters['status'].default_active = True
|
g.filters['status'].default_active = True
|
||||||
g.filters['status'].default_verb = 'not_equal'
|
g.filters['status'].default_verb = 'not_equal'
|
||||||
# TODO: should not have to convert value to string!
|
# TODO: should not have to convert value to string!
|
||||||
g.filters['status'].default_value = six.text_type(self.enum.PURCHASE_CREDIT_STATUS_SATISFIED)
|
g.filters['status'].default_value = str(self.enum.PURCHASE_CREDIT_STATUS_SATISFIED)
|
||||||
|
|
||||||
# g.set_type('upc', 'gpc')
|
# g.set_type('upc', 'gpc')
|
||||||
g.set_type('cases_shorted', 'quantity')
|
g.set_type('cases_shorted', 'quantity')
|
||||||
|
@ -175,7 +171,9 @@ class PurchaseCreditView(MasterView):
|
||||||
def status_options(self):
|
def status_options(self):
|
||||||
options = []
|
options = []
|
||||||
for value in sorted(self.enum.PURCHASE_CREDIT_STATUS):
|
for value in sorted(self.enum.PURCHASE_CREDIT_STATUS):
|
||||||
options.append(tags.Option(self.enum.PURCHASE_CREDIT_STATUS[value], value))
|
options.append({
|
||||||
|
'value': value,
|
||||||
|
'label': self.enum.PURCHASE_CREDIT_STATUS[value]})
|
||||||
return options
|
return options
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|
Loading…
Reference in a new issue