Bug 28376: Replace jQueryUI date/timepicker with Flatpickr

This patch is a proof of concept demonstrating how jQueryUI date & time
pickers could be replaced using the Flatpickr library
(https://flatpickr.js.org/).

NEW: I've modified the default configuration of Flatpickr instances so
that a "Clear date" link is automatically appended. This eliminates the
need to add a button to the markup and event handling for each case.

NEW: Date/time formatting should be corrected in this revised patch.

The patch modifies three pages as test cases:
 - Circulation -> Renew (with SpecifyDueDates enabled), to demonstrate
   date and time selection.
   - NEW: You can also test the datepicker shown when you renew an
     on-hold item. This demonstrates a configuration which requires that
     the selection be after today.
 - Administration -> Patron categories -> New category, to demonstrate a
   calendar-only date picker enforcing a date after today.
 - NEW: Reports -> Patrons. The "Date of birth" fields are linked so
   that the second cannot be before the first.

I've made some customizations to the default Flatpickr library's CSS and
incorporated it into staff-global.scss, so you must rebuild the staff
client SCSS
(https://wiki.koha-community.org/wiki/Working_with_SCSS_in_the_OPAC_and_staff_client).

Signed-off-by: David Nind <david@davidnind.com>
Signed-off-by: Tomas Cohen Arazi <tomascohen@theke.io>

Signed-off-by: Jonathan Druart <jonathan.druart@bugs.koha-community.org>
This commit is contained in:
Owen Leonard 2021-05-20 15:48:58 +00:00 committed by Jonathan Druart
parent 1393e48d8e
commit f892ae8bf1
10 changed files with 1095 additions and 44 deletions

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,960 @@
@import "vars";
@-webkit-keyframes fpFadeInDown {
from {
opacity: 0;
transform: translate3d(0, -20px, 0);
}
to {
opacity: 1;
transform: translate3d(0, 0, 0);
}
}
@-moz-keyframes fpFadeInDown {
from {
opacity: 0;
transform: translate3d(0, -20px, 0);
}
to {
opacity: 1;
transform: translate3d(0, 0, 0);
}
}
@-ms-keyframes fpFadeInDown {
from {
opacity: 0;
transform: translate3d(0, -20px, 0);
}
to {
opacity: 1;
transform: translate3d(0, 0, 0);
}
}
@-o-keyframes fpFadeInDown {
from {
opacity: 0;
transform: translate3d(0, -20px, 0);
}
to {
opacity: 1;
transform: translate3d(0, 0, 0);
}
}
@keyframes fpFadeInDown {
from {
opacity: 0;
transform: translate3d(0, -20px, 0);
}
to {
opacity: 1;
transform: translate3d(0, 0, 0);
}
}
.flatpickr-calendar {
animation: none;
background: transparent;
border-radius: 5px;
border: 0;
box-sizing: border-box;
direction: ltr;
display: none;
font-size: 14px;
line-height: 24px;
opacity: 0;
padding: 0;
position: absolute;
text-align: center;
touch-action: manipulation;
visibility: hidden;
width: $calendarWidth;
@if variable-exists( "noCalendarBorder" ) {
box-shadow: 0 3px 13px rgba(black, 0.08);
}
@else {
background: $calendarBackground;
box-shadow: 1px 1px 3px 0 #666;
}
&.open,
&.inline {
opacity: 1;
max-height: 640px;
visibility: visible;
}
&.open {
display: inline-block;
z-index: 99999;
}
&.animate.open {
animation: fpFadeInDown 300ms $bezier;
}
&.inline {
display: block;
position: relative;
top: 2px;
}
&.static {
position: absolute;
top: calc(100% + 2px);
&.open {
z-index: 999;
display: block;
}
}
&.multiMonth {
.flatpickr-days .dayContainer:nth-child(n+1) {
& .flatpickr-day.inRange:nth-child(7n+7) {
box-shadow: none !important;
}
}
.flatpickr-days .dayContainer:nth-child(n+2) {
& .flatpickr-day.inRange:nth-child(7n+1) {
box-shadow: -2px 0 0 #e6e6e6, 5px 0 0 #e6e6e6;
}
}
}
.hasWeeks,
.hasTime {
.dayContainer {
border-bottom: 0;
border-bottom-right-radius: 0;
border-bottom-left-radius: 0;
}
}
@if variable-exists( "noCalendarBorder" ) {
.hasWeeks .dayContainer {
border-left: 0;
}
}
&.hasTime {
.flatpickr-time {
height: $timeHeight;
border-top: 1px solid $calendarBorderColor;
}
@if variable-exists( "noCalendarBorder" ) {
.flatpickr-innerContainer {
border-bottom: 0;
}
.flatpickr-time {
border: 1px solid $calendarBorderColor;
}
}
}
&.noCalendar.hasTime {
.flatpickr-time {
height: auto;
}
}
&:before,
&:after {
position: absolute;
display: block;
pointer-events: none;
border: solid transparent;
content: '';
height: 0;
width: 0;
left: 22px;
}
&.rightMost,
&.arrowRight {
&:before,
&:after {
left: auto;
right: 22px;
}
}
&.arrowCenter {
&:before,
&:after {
left: 50%;
right: 50%;
}
}
&:before {
border-width: 5px;
margin: 0 -5px;
}
&:after {
border-width: 4px;
margin: 0 -4px;
}
&.arrowTop {
&:before,
&:after {
bottom: 100%;
}
&:before {
border-bottom-color: $calendarBorderColor;
}
&:after {
@if variable-exists( "noCalendarBorder" ) {
border-bottom-color: $monthBackground;
}
@else {
border-bottom-color: $calendarBackground;
}
}
}
&.arrowBottom {
&:before,
&:after {
top: 100%;
}
&:before {
border-top-color: $calendarBorderColor;
}
&:after {
@if variable-exists( "noCalendarBorder" ) {
border-top-color: $monthBackground;
}
@else {
border-top-color: $calendarBackground;
}
}
}
&:focus {
outline: 0;
}
}
.flatpickr-wrapper {
position: relative;
display: inline-block;
}
.flatpickr-months {
display: flex;
.flatpickr-month {
@if variable-exists( "noCalendarBorder" ) {
border-radius: 5px 5px 0 0;
}
background: $monthBackground;
color: $monthForeground;
fill: $monthForeground;
height: $monthNavHeight;
line-height: 1;
text-align: center;
position: relative;
user-select: none;
overflow: hidden;
flex: 1;
}
.flatpickr-prev-month,
.flatpickr-next-month {
text-decoration: none;
cursor: pointer;
position: absolute;
top: 0;
height: $monthNavHeight;
padding: 10px;
z-index: 3;
color: $monthForeground;
fill: $monthForeground;
&.flatpickr-disabled {
display: none;
}
i {
position: relative;
}
&.flatpickr-prev-month {
/*!
/*rtl:begin:ignore*/
/*
*/
left: 0;
/*!
/*rtl:end:ignore*/
/*
*/
}
&.flatpickr-next-month {
/*!
/*rtl:begin:ignore*/
/*
*/
right: 0;
/*!
/*rtl:end:ignore*/
/*
*/
}
&:hover {
color: $todayColor;
svg {
@if variable-exists( "arrow_hover_color" ){
fill: $arrow_hover_color;
}
@else {
fill: $todayColor;
}
}
}
svg {
width: 14px;
height: 14px;
path {
transition: fill 0.1s;
fill: inherit;
}
}
}
}
.numInputWrapper {
position: relative;
height: auto;
input,
span {
display: inline-block;
}
input {
width: 100%;
&::-ms-clear {
display: none;
}
&::-webkit-outer-spin-button,
&::-webkit-inner-spin-button {
margin: 0;
-webkit-appearance: none;
}
}
span {
position: absolute;
right: 0;
width: $timecontrols;
padding: 0 4px 0 2px;
height: 50%;
line-height: 50%;
opacity: 0;
cursor: pointer;
border: 1px solid rgba($dayForeground, 0.15);
box-sizing: border-box;
&:hover {
background: rgba($invertedBg, 0.1);
}
&:active {
background: rgba($invertedBg, 0.2);
}
&:after {
display: block;
content: "";
position: absolute;
}
&.arrowUp {
top: 0;
border-bottom: 0;
&:after {
border-left: 4px solid transparent;
border-right: 4px solid transparent;
border-bottom: 4px solid rgba($dayForeground, 0.6);
top: 26%;
}
}
&.arrowDown {
top: 50%;
&:after {
border-left: 4px solid transparent;
border-right: 4px solid transparent;
border-top: 4px solid rgba($dayForeground, 0.6);
top: 40%;
}
}
svg {
width: inherit;
height: auto;
path {
fill: rgba($monthForeground, 0.5);
}
}
}
&:hover {
background: rgba($invertedBg, 0.05);
span {
opacity: 1;
}
}
}
.flatpickr-current-month {
font-size: 135%;
line-height: inherit;
font-weight: 300;
color: inherit;
position: absolute;
width: 75%;
left: 12.5%;
padding: 0.22 * $monthNavHeight 0 0 0;
line-height: 1;
height: $monthNavHeight;
display: inline-block;
text-align: center;
transform: translate3d(0px, 0px, 0px);
span.cur-month {
font-family: inherit;
font-weight: 700;
color: inherit;
display: inline-block;
margin-left: 0.5ch;
padding: 0;
&:hover {
background: rgba($invertedBg, 0.05);
}
}
.numInputWrapper {
width: 8ch;
display: inline-block;
span.arrowUp:after {
border-bottom-color: $monthForeground;
}
span.arrowDown:after {
border-top-color: $monthForeground;
}
}
input.cur-year {
background: transparent;
box-sizing: border-box;
color: inherit;
cursor: text;
padding: 0 0 0 0.5ch;
margin: 0;
display: inline-block;
font-size: inherit;
font-family: inherit;
font-weight: 300;
line-height: inherit;
height: auto;
border: 0;
border-radius: 0;
vertical-align: initial;
-webkit-appearance: textfield;
-moz-appearance: textfield;
appearance: textfield;
&:focus {
outline: 0;
}
&[disabled],
&[disabled]:hover {
font-size: 100%;
color: rgba($monthForeground, 0.5);
background: transparent;
pointer-events: none;
}
}
.flatpickr-monthDropdown-months {
appearance: none;
background-image: url('data:image/svg+xml;charset=utf8,%3csvg fill="%23000000" fill-opacity="0.54" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"%3e%3cpath d="M7 10l5 5 5-5z"/%3e%3cpath d="M0 0h24v24H0z" fill="none"/%3e%3c/svg%3e');
background-position: 100% 50%;
background-repeat: no-repeat;
background-size: 24px 24px;
border: none;
border-radius: 0;
box-sizing: border-box;
color: inherit;
cursor: pointer;
font-size: 90%;
font-family: inherit;
font-weight: 300;
height: auto;
line-height: 120%;
margin: -1px 0 0 0;
outline: none;
padding: 4px 28px 4px 4px;
position: relative;
vertical-align: initial;
width: auto;
&:focus,
&:active {
outline: none;
}
.flatpickr-monthDropdown-month {
background-color: $monthBackground;
outline: none;
padding: 0;
}
}
}
.flatpickr-weekdays {
background: $weekdaysBackground;
text-align: center;
overflow: hidden;
width: 100%;
display: flex;
align-items: center;
height: $weekdaysHeight;
.flatpickr-weekdaycontainer {
display: flex;
flex: 1;
}
}
span.flatpickr-weekday {
cursor: default;
font-size: 90%;
background: $monthBackground;
color: $weekdaysForeground;
line-height: 1;
margin: 0;
text-align: center;
display: block;
flex: 1;
font-weight: bolder;
}
.dayContainer,
.flatpickr-weeks {
padding: 1px 0 0 0;
}
.flatpickr-days {
position: relative;
overflow: hidden;
display: flex;
align-items: flex-start;
width: $daysWidth;
&:focus {
outline: 0;
}
@if variable-exists( "noCalendarBorder" ) {
border-left: 1px solid $calendarBorderColor;
border-right: 1px solid $calendarBorderColor;
}
}
.dayContainer {
padding: 0;
outline: 0;
text-align: left;
width: $daysWidth;
min-width: $daysWidth;
max-width: $daysWidth;
box-sizing: border-box;
display: inline-block;
display: -ms-flexbox;
display: flex;
flex-wrap: wrap;
-ms-flex-wrap: wrap;
-ms-flex-pack: justify;
justify-content: space-around;
transform: translate3d(0px, 0px, 0px);
opacity: 1;
&+.dayContainer {
box-shadow: -1px 0 0 $calendarBorderColor;
}
}
.flatpickr-day {
background: none;
border: 1px solid transparent;
border-radius: 150px;
box-sizing: border-box;
color: $dayForeground;
cursor: pointer;
font-weight: 400;
width: 14.2857143%;
flex-basis: 14.2857143%;
max-width: $daySize;
height: $daySize;
line-height: $daySize;
margin: 0;
display: inline-block;
position: relative;
justify-content: center;
text-align: center;
&,
&.prevMonthDay,
&.nextMonthDay {
&.inRange,
&.today.inRange,
&:hover,
&:focus {
cursor: pointer;
outline: 0;
background: $dayHoverBackground;
border-color: $dayHoverBackground;
}
}
&.today {
border-color: $todayColor;
&:hover,
&:focus {
border-color: $todayColor;
background: $todayColor;
@if variable-exists( "today_fg_color" ){
color: $today_fg_color;
}
@else {
color: white;
}
}
}
&.selected,
&.startRange,
&.endRange {
&,
&.inRange,
&:focus,
&:hover,
&.prevMonthDay,
&.nextMonthDay {
background: $selectedDayBackground;
box-shadow: none;
@if variable-exists( "selectedDayForeground" ){
color: $selectedDayForeground;
}
@else {
color: white;
}
border-color: $selectedDayBackground;
}
&.startRange {
border-radius: 50px 0 0 50px;
}
&.endRange {
border-radius: 0 50px 50px 0;
}
&.startRange+.endRange:not(:nth-child(7n+1)) {
box-shadow: -5 * $dayMargin 0 0 $selectedDayBackground;
}
&.startRange.endRange {
border-radius: 50px;
}
}
&.inRange {
border-radius: 0;
box-shadow: -2.5 * $dayMargin 0 0 $dayHoverBackground, 2.5 * $dayMargin 0 0 $dayHoverBackground;
}
&.flatpickr-disabled,
&.flatpickr-disabled:hover,
&.prevMonthDay,
&.nextMonthDay,
&.notAllowed,
&.notAllowed.prevMonthDay,
&.notAllowed.nextMonthDay {
color: rgba($dayForeground, 0.3);
background: transparent;
@if variable-exists( "disabled_border_color" ){
border-color: $disabled_border_color;
}
@else {
border-color: transparent;
}
cursor: default;
}
&.flatpickr-disabled,
&.flatpickr-disabled:hover {
cursor: not-allowed;
color: rgba($dayForeground, 0.1);
}
&.week.selected {
border-radius: 0;
box-shadow: -2.5 * $dayMargin 0 0 $selectedDayBackground, 2.5 * $dayMargin 0 0 $selectedDayBackground;
}
&.hidden {
visibility: hidden;
}
}
.rangeMode .flatpickr-day {
margin-top: 1px;
}
.flatpickr-weekwrapper {
float: left;
.flatpickr-weeks {
padding: 0 12px;
@if variable-exists( "noCalendarBorder" ) {
border-left: 1px solid $calendarBorderColor;
}
@else {
box-shadow: 1px 0 0 $calendarBorderColor;
}
}
.flatpickr-weekday {
float: none;
width: 100%;
line-height: $weekdaysHeight;
}
span.flatpickr-day {
&,
&:hover {
display: block;
width: 100%;
max-width: none;
color: rgba($dayForeground, 0.3);
background: transparent;
cursor: default;
border: none;
}
}
}
.flatpickr-innerContainer {
display: block;
display: flex;
box-sizing: border-box;
overflow: hidden;
@if variable-exists( "noCalendarBorder" ) {
background: $calendarBackground;
border-bottom: 1px solid $calendarBorderColor;
}
}
.flatpickr-rContainer {
display: inline-block;
padding: 0;
box-sizing: border-box;
}
.flatpickr-time {
text-align: center;
outline: 0;
display: block;
height: 0;
line-height: $timeHeight;
max-height: $timeHeight;
box-sizing: border-box;
overflow: hidden;
display: flex;
@if variable-exists( "noCalendarBorder" ) {
background: $calendarBackground;
border-radius: 0 0 5px 5px;
}
&:after {
content: "";
display: table;
clear: both;
}
.numInputWrapper {
flex: 1;
width: 40%;
height: $timeHeight;
float: left;
span.arrowUp:after {
border-bottom-color: $dayForeground;
}
span.arrowDown:after {
border-top-color: $dayForeground;
}
}
&.hasSeconds .numInputWrapper {
width: 26%;
}
&.time24hr .numInputWrapper {
width: 49%;
}
input {
background: transparent;
box-shadow: none;
border: 0;
border-radius: 0;
text-align: center;
margin: 0;
padding: 0;
height: inherit;
line-height: inherit;
color: $dayForeground;
font-size: 14px;
position: relative;
box-sizing: border-box;
-webkit-appearance: textfield;
-moz-appearance: textfield;
appearance: textfield;
&.flatpickr-hour {
font-weight: bold;
}
&.flatpickr-minute,
&.flatpickr-second {
font-weight: 400;
}
&:focus {
outline: 0;
border: 0;
}
}
.flatpickr-time-separator,
.flatpickr-am-pm {
height: inherit;
float: left;
line-height: inherit;
color: $dayForeground;
font-weight: bold;
width: 2%;
user-select: none;
align-self: center;
}
.flatpickr-am-pm {
outline: 0;
width: 18%;
cursor: pointer;
text-align: center;
font-weight: 400;
}
input,
.flatpickr-am-pm {
&:hover,
&:focus {
background: lighten($dayHoverBackground, 3);
}
}
}
.flatpickr-input {
background-repeat: no-repeat;
background-position: 3px;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='#369' class='bi bi-calendar3'%3E%3Cg stroke-width='1.333'%3E%3Cpath d='M10.5 0h-9A1.5 1.5 0 0 0 0 1.5v9A1.5 1.5 0 0 0 1.5 12h9a1.5 1.5 0 0 0 1.5-1.5v-9A1.5 1.5 0 0 0 10.5 0zM.75 2.893c0-.355.336-.643.75-.643h9c.414 0 .75.288.75.643v7.714c0 .355-.336.643-.75.643h-9c-.414 0-.75-.288-.75-.643z'/%3E%3Cpath d='M4.875 5.25a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5zm2.25 0a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5zm2.25 0a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5zM2.625 7.5a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5zm2.25 0a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5zm2.25 0a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5zm2.25 0a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5zm-6.75 2.25a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5zm2.25 0a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5zm2.25 0a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5z'/%3E%3C/g%3E%3C/svg%3E");
border-style: inset;
border-width: 1px;
padding: 3px 3px 3px 20px;
&:focus {
border-radius: 0;
}
&[readonly] {
cursor: pointer;
}
}
.flatpickr-day {
border-radius: 0;
}

View file

@ -0,0 +1,37 @@
$bezier: cubic-bezier(0.23, 1, 0.32, 1);
$slideTime: 400ms;
$daySize: 39px;
$padding: $daySize / 16;
$dayMargin: 2px;
$daysWidth: $daySize * 7 + $dayMargin * 14 + $padding * 2 + 2;
$calendarWidth: $daysWidth;
$monthNavHeight: 34px !default;
$weekdaysHeight: 28px !default;
$timeHeight: 40px;
$calendarBackground: #ffffff !default;
$calendarBorderColor: #e6e6e6 !default;
$monthForeground: rgba(black, 0.9) !default;
$arrow_hover_color: #f64747 !default;
$monthBackground: transparent !default;
$weekdaysBackground: transparent !default;
$weekdaysForeground: rgba(black, 0.54) !default;
$dayForeground: #000 !default;
$dayHoverBackground: #e6e6e6 !default;
$todayColor: #538200 !default;
$selectedDayBackground: #FFF4C6 !default;
$selectedDayForeground: #000;
$invertedBg: invert($calendarBackground);
$timecontrols: 25px;

View file

@ -1,5 +1,6 @@
@import "mixins";
@import "tables";
@import "flatpickr";
::selection {
background: #538200;
@ -54,6 +55,11 @@ a {
padding: 0;
}
&.clear_date {
color: #C33;
font-size: 130%;
}
&.close {
&:hover {
color: #538200;
@ -1809,6 +1815,10 @@ i {
&[type="submit"] {
background: #FFF none;
}
&.flatpickr-input {
padding: 3px 3px 3px 20px;
}
}
li {
@ -3761,10 +3771,6 @@ input.renew {
padding: .5em;
}
.dialog input#renewonholdduedate {
padding: 2px;
}
.date-select {
label {
display: inline-block;

View file

@ -2,13 +2,50 @@
[% USE Koha %]
[% USE raw %]
<!-- calendar.inc -->
[% FILTER collapse %]
<script>
var debug = "[% debug | html %]";
var dateformat_pref = "[% Koha.Preference('dateformat ') | html %]";
var dateformat_string = [% IF ( dateformat == "us" ) %]"mm/dd/yy"[% ELSIF ( dateformat == "metric" ) %]"dd/mm/yy"[% ELSIF ( dateformat == "dmydot" ) %]"dd.mm.yy"[% ELSE %]"yy-mm-dd"[% END %];
var flatpickr_dateformat_string = [% IF ( dateformat == "us" ) %]"m/d/Y"[% ELSIF ( dateformat == "metric" ) %]"d/m/Y"[% ELSIF ( dateformat == "dmydot" ) %]"d.m.Y"[% ELSE %]"Y-m-d"[% END %];
var sentmsg = 0;
var bidi = [% IF(bidi) %] true[% ELSE %] false[% END %];
var calendarFirstDayOfWeek = '[% Koha.Preference('CalendarFirstDayOfWeek') | html %]';
var flatpickr_timeformat_string = [% IF Koha.Preference('TimeFormat') == '12hr' %]"h:i K"[% ELSE %]"H:i"[% END %];
var flatpickr_timeformat = [% IF Koha.Preference('TimeFormat') == '12hr' %]false[% ELSE %]true[% END %];
</script>
<!-- / calendar.inc -->
[% Asset.js("js/calendar.js") | $raw %]
[% Asset.js("lib/flatpickr/flatpickr.min.js") | $raw %]
<script>
flatpickr.l10ns.default.weekdays = flatpickr_weekdays;
flatpickr.l10ns.default.months = flatpickr_months;
flatpickr.setDefaults({
allowInput: true,
dateFormat: flatpickr_dateformat_string,
nextArrow: '<i class="fa fa-fw fa-arrow-right"></i>',
prevArrow: '<i class="fa fa-fw fa-arrow-left"></i>',
time_24hr: flatpickr_timeformat,
locale: {
"firstDayOfWeek": calendarFirstDayOfWeek
},
onReady: function( selectedDates, dateStr, instance ){
/* When flatpickr instance is created, automatically append a "clear date" link */
$(instance.input)
.after( $("<a/>")
.attr("href","#")
.addClass("clear_date")
.on("click", function(){
instance.clear();
})
.addClass("fa fa-fw fa-remove")
.attr("aria-hidden", true)
.attr("aria-label", _("Clear date") )
);
},
onClose: function( selectedDates, dateText, instance) {
validate_date( selectedDates, instance );
},
});
</script>
[% END %]

View file

@ -683,6 +683,7 @@
[% MACRO jsinclude BLOCK %]
[% Asset.js("js/admin-menu.js") | $raw %]
[% Asset.js("js/messaging-preference-form.js") | $raw %]
[% Asset.js("lib/flatpickr/flatpickr.min.js") | $raw %]
[% INCLUDE 'calendar.inc' %]
[% INCLUDE 'datatables.inc' %]
[% INCLUDE 'columns_settings.inc' %]

View file

@ -147,7 +147,7 @@
<input type="hidden" name="override_limit" value="1" />
<input type="hidden" name="override_holds" value="1" />
<div>
<label for="renewonholdduedate">Renewal due date:</label> <input type="text" size="12" id="renewonholdduedate" name="renewonholdduedate" value="" />
<label for="renewonholdduedate">Renewal due date:</label> <input type="text" size="20" id="renewonholdduedate" name="renewonholdduedate" value="" />
</div>
<button type="submit" class="approve"><i class="fa fa-check"></i> Override and renew</button>
</form>
@ -220,7 +220,6 @@
<div class="date-select" id="renew_date_override_fields">
<div><label for="hard_due_date" class="hint">Renewal due date [% INCLUDE 'date-format.inc' %]:</label></div>
<input type="text" size="20" id="hard_due_date" name="hard_due_date" value="[% hard_due_date | $KohaDates with_hours => 1 %]" />
<button type="button" class="action btn btn-default btn-xs" id="cleardate" name="cleardate">Clear</button>
</div> <!-- /.date-select -->
</div>
</fieldset>
@ -245,8 +244,6 @@
[% MACRO jsinclude BLOCK %]
[% INCLUDE 'calendar.inc' %]
[% Asset.js("lib/jquery/plugins/jquery-ui-timepicker-addon.min.js") | $raw %]
[% INCLUDE 'timepicker.inc' %]
[% IF error %]
<script>
$( document ).ready(function() {
@ -256,34 +253,18 @@
[% END %]
<script>
$( document ).ready(function() {
$("#renewonholdduedate").datetimepicker({
onClose: function(dateText, inst) {
validate_date(dateText, inst);
},
minDate: 1, // require that renewal date is after today
hour: 23,
minute: 59
}).on('change', function(e) {
if ( ! is_valid_date( $(this).val() ) ) {$(this).val('');}
var renewonholdduedate = $("#renewonholdduedate").flatpickr({
enableTime: true,
dateFormat: flatpickr_dateformat_string + " " + flatpickr_timeformat_string,
minDate: new Date().fp_incr(1)
});
[% IF Koha.Preference('SpecifyDueDate') %]
$("#hard_due_date").datetimepicker({
onClose: function(dateText, inst) {
validate_date(dateText, inst);
},
hour: 23,
minute: 59
}).on("change", function(e, value) {
if ( ! is_valid_date( $(this).val() ) ) {$(this).val("");}
});
$("#cleardate").on("click",function(e){
e.preventDefault();
this.form.hard_due_date.value = '';
this.form.barcode.focus();
var hard_due_date = $("#hard_due_date").flatpickr({
enableTime: true,
dateFormat: flatpickr_dateformat_string + " " + flatpickr_timeformat_string,
});
[% END %]
});
</script>
[% END %]

View file

@ -1,5 +1,6 @@
[% USE Branches %]
[% USE Koha %]
[% USE Asset %]
[% SET footerjs = 1 %]
[% INCLUDE 'doc-head-open.inc' %]
<title>[% IF ( do_it ) %]Patrons statistics &rsaquo; Results[% ELSE %]Patrons statistics[% END %] &rsaquo; Reports &rsaquo; Koha</title>
@ -176,13 +177,16 @@
</td>
</tr>
<tr>
<td>Date of birth</td>
<td colspan="2"></td>
<td><label for="from">From</label> <input type="text" size="10" id="from" name="Filter" class="datepickerfrom" />
<label for="to">To</label> <input size="10" id="to" name="Filter" value="" type="text" class="datepickerto" />
<td>Date of birth</td>
<td colspan="2"></td>
<td>
<label for="from">From</label>
<input type="text" size="10" id="from" name="Filter" class="" />
<label for="to">To</label>
<input size="10" id="to" name="Filter" value="" type="text" class="" />
<span class="hint">[% INCLUDE 'date-format.inc' %]</span>
</td>
</tr>
</td>
</tr>
<tr>
<td>Gender</td>
<td><input type="radio" name="Line" value="sex" /></td>
@ -326,6 +330,19 @@
[% MACRO jsinclude BLOCK %]
[% INCLUDE 'calendar.inc' %]
<script>
var startPicker = $("#from").flatpickr({
onClose: function( selectedDates, dateText, instance) {
validate_date( selectedDates, instance );
endPicker.set('minDate', selectedDates[0]);
}
});
var endPicker = $("#to").flatpickr({
onClose: function( selectedDates, dateText, instance) {
validate_date( selectedDates, instance );
},
});
</script>
[% END %]
[% INCLUDE 'intranet-bottom.inc' %]

View file

@ -1,5 +1,5 @@
/* global debug sentmsg __ dateformat_pref dateformat_string bidi calendarFirstDayOfWeek */
/* exported DateTime_from_syspref */
/* exported DateTime_from_syspref flatpickr_weekdays flatpickr_months */
var MSG_PLEASE_ENTER_A_VALID_DATE = ( __("Please enter a valid date (should match %s).") );
if (debug > 1) {
alert("dateformat: " + dateformat_pref + "\ndebug is on (level " + debug + ")");
@ -8,7 +8,6 @@ if (debug > 1) {
function is_valid_date(date) {
// An empty string is considered as a valid date for convenient reasons.
if (date === '') return 1;
var dateformat = dateformat_string;
if (dateformat == 'us') {
if (date.search(/^\d{2}\/\d{2}\/\d{4}($|\s)/) == -1) return 0;
@ -24,7 +23,7 @@ function is_valid_date(date) {
dateformat = 'dd.mm.yy';
}
try {
$.datepicker.parseDate(dateformat, date);
flatpickr.parseDate(date, dateformat);
} catch (e) {
return 0;
}
@ -49,7 +48,7 @@ function validate_date(dateText, inst) {
if (!is_valid_date(dateText)) {
var dateformat_str = get_dateformat_str( dateformat_pref );
alert(MSG_PLEASE_ENTER_A_VALID_DATE.format(dateformat_str));
$('#' + inst.id).val('');
inst.clear();
}
}
@ -162,6 +161,16 @@ jQuery.validator.addMethod("date_on_or_after",
return to >= from;
});
var flatpickr_weekdays = {
shorthand: [ __("Sun"), __("Mon"), __("Tue"), __("Wed"), __("Thu"), __("Fri"), __("Sat")],
longhand: [ __("Sunday"), __("Monday"), __("Tuesday"), __("Wednesday"), __("Thursday"), __("Friday"), __("Saturday") ]
};
var flatpickr_months = {
shorthand: [ __("Jan"), __("Feb"), __("Mar"), __("Apr"), __("May"), __("Jun"), __("Jul"), __("Aug"), __("Sep"), __("Oct"), __("Nov"), __("Dec")],
longhand: [ __("January"), __("February"), __("March"), __("April"), __("May"), __("June"), __("July"), __("August"), __("September"), __("October"), __("November"), __("December")]
};
$(document).ready(function () {
$.datepicker.setDefaults({

View file

@ -21,6 +21,7 @@ jQuery.validator.addMethod( "enrollment_period", function(){
}, __("Please choose an enrollment period in months OR by date.")
);
flatpickr.l10ns.default.firstDayOfWeek = calendarFirstDayOfWeek;
$(document).ready(function() {
KohaTable("patron_categories", {
@ -39,9 +40,9 @@ $(document).ready(function() {
"exportColumns": [0,1,2,3,4,5,6,7,8,9,10,11,12],
}, columns_settings);
$("#enrolmentperioddate").datepicker({
minDate: 1
}); // Require that "until date" be in the future
$("#enrolmentperioddate").flatpickr({
minDate: new Date().fp_incr(1)
});
if ($("#branches option:selected").length < 1) {
$("#branches option:first").attr("selected", "selected");