refactored search with free search term support (#1064)
This commit is contained in:
@@ -32,6 +32,7 @@ import KimaiAPILink from "./plugins/KimaiAPILink";
|
||||
import KimaiAlert from "./plugins/KimaiAlert";
|
||||
import KimaiAutocomplete from "./plugins/KimaiAutocomplete";
|
||||
import KimaiToolbarAction from "./plugins/KimaiToolbarAction";
|
||||
import KimaiSearchButtons from "./plugins/KimaiSearchButtons";
|
||||
|
||||
export default class KimaiLoader {
|
||||
|
||||
@@ -54,7 +55,8 @@ export default class KimaiLoader {
|
||||
kimai.registerPlugin(new KimaiDateRangePicker('.content-wrapper'));
|
||||
kimai.registerPlugin(new KimaiDateTimePicker('.content-wrapper'));
|
||||
kimai.registerPlugin(new KimaiDatatable('table.dataTable'));
|
||||
kimai.registerPlugin(new KimaiToolbar());
|
||||
kimai.registerPlugin(new KimaiToolbar('form.header-search'));
|
||||
kimai.registerPlugin(new KimaiSearchButtons('.content-header'));
|
||||
kimai.registerPlugin(new KimaiSelectDataAPI('select[data-related-select]'));
|
||||
kimai.registerPlugin(new KimaiAlternativeLinks('.alternative-link'));
|
||||
kimai.registerPlugin(new KimaiAjaxModalForm('.modal-ajax-form'));
|
||||
|
||||
@@ -44,16 +44,24 @@ export default class KimaiDatatable extends KimaiPlugin {
|
||||
for (let eventName of events.split(' ')) {
|
||||
document.addEventListener(eventName, handle);
|
||||
}
|
||||
|
||||
if (this.getContainer().getConfiguration().get('autoReloadDatatable')) {
|
||||
document.addEventListener('toolbar-change', handle);
|
||||
} else {
|
||||
document.addEventListener('pagination-change', handle);
|
||||
}
|
||||
}
|
||||
|
||||
reloadDatatable() {
|
||||
const durations = this.getContainer().getPlugin('timesheet-duration');
|
||||
const form = jQuery('.toolbar form');
|
||||
const toolbarSelector = this.getContainer().getPlugin('toolbar').getSelector();
|
||||
|
||||
const form = jQuery(toolbarSelector);
|
||||
let loading = '<div class="overlay"><i class="fas fa-sync fa-spin"></i></div>';
|
||||
jQuery('section.content').append(loading);
|
||||
|
||||
// remove the empty fields to prevent errors
|
||||
let formData = jQuery('.toolbar form :input')
|
||||
let formData = jQuery(toolbarSelector + ' :input')
|
||||
.filter(function(index, element) {
|
||||
return jQuery(element).val() != '';
|
||||
})
|
||||
|
||||
@@ -19,6 +19,10 @@ export default class KimaiJqueryPluginInitializer extends KimaiPlugin {
|
||||
jQuery('.dropdown-toggle').dropdown();
|
||||
// activate the tooltip functionality
|
||||
jQuery('[data-toggle="tooltip"]').tooltip();
|
||||
// enable all selectpicker in adhoc forms (like invoice and export)
|
||||
$('.selectpicker').selectpicker({
|
||||
container: 'body'
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
49
assets/js/plugins/KimaiSearchButtons.js
Normal file
49
assets/js/plugins/KimaiSearchButtons.js
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/*!
|
||||
* [KIMAI] KimaiSearchButtons: handles events of search buttons and the filter dropdown
|
||||
*/
|
||||
|
||||
import jQuery from 'jquery';
|
||||
import KimaiPlugin from "../KimaiPlugin";
|
||||
|
||||
/**
|
||||
* FIXME refactor me and merge with KimaiToolbar
|
||||
*/
|
||||
export default class KimaiSearchButtons extends KimaiPlugin {
|
||||
|
||||
constructor(selector) {
|
||||
super();
|
||||
this.selector = selector;
|
||||
}
|
||||
|
||||
init() {
|
||||
const self = this;
|
||||
|
||||
$(document).on('click', this.selector + ' .search-toggle', function (e) {
|
||||
e.stopPropagation();
|
||||
jQuery(self.selector).toggleClass('search-open');
|
||||
jQuery(self.selector + ' form.header-search').toggleClass('hidden-xs');
|
||||
jQuery(self.selector + ' form.header-search .dropdown-toggle').dropdown('toggle');
|
||||
jQuery(self.selector + ' form.header-search input#searchTerm').focus();
|
||||
});
|
||||
|
||||
$(document).on('click', this.selector + ' .search-cancel', function (e) {
|
||||
e.preventDefault();
|
||||
jQuery(self.selector).toggleClass('search-open');
|
||||
jQuery(self.selector + ' form.header-search .dropdown-toggle').dropdown('toggle');
|
||||
jQuery(self.selector + ' form.header-search').toggleClass('hidden-xs');
|
||||
});
|
||||
|
||||
// prevent that the dropdown closes, when a form input is changed - eg. a select option was clicked
|
||||
$(document).on('click', this.selector + ' .dropdown-menu', function (e) {
|
||||
e.stopPropagation();
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -14,12 +14,22 @@ import KimaiPlugin from "../KimaiPlugin";
|
||||
|
||||
export default class KimaiToolbar extends KimaiPlugin {
|
||||
|
||||
init() {
|
||||
const datatable = this.getContainer().getPlugin('datatable');
|
||||
constructor(selector) {
|
||||
super();
|
||||
this.selector = selector;
|
||||
}
|
||||
|
||||
// This catches all clicks on the pagination and prevents the default action, as we want to relad the page via JS
|
||||
getId() {
|
||||
return 'toolbar';
|
||||
}
|
||||
|
||||
init() {
|
||||
const formSelector = this.getSelector();
|
||||
const self = this;
|
||||
|
||||
// This catches all clicks on the pagination and prevents the default action, as we want to reload the page via JS
|
||||
jQuery('body').on('click', 'div.navigation ul.pagination li a', function(event) {
|
||||
let pager = jQuery(".toolbar form input[name='page']");
|
||||
let pager = jQuery(formSelector + " input#page");
|
||||
if (pager.length === 0) {
|
||||
return;
|
||||
}
|
||||
@@ -29,41 +39,59 @@ export default class KimaiToolbar extends KimaiPlugin {
|
||||
let page = urlParts[urlParts.length-1];
|
||||
pager.val(page);
|
||||
pager.trigger('change');
|
||||
self.getContainer().getPlugin('event').trigger('pagination-change');
|
||||
return false;
|
||||
});
|
||||
|
||||
// Reset the page if any other value is changed, otherwise we might end up with a limited set
|
||||
// of data which does not support the given page - and it would be just wrong to stay in the same page
|
||||
jQuery('.toolbar form input').change(function (event) {
|
||||
jQuery(this.selector +' input').change(function (event) {
|
||||
switch (event.target.id) {
|
||||
case 'page':
|
||||
break;
|
||||
default:
|
||||
jQuery('.toolbar form input#page').val(1);
|
||||
jQuery(formSelector + ' input#page').val(1);
|
||||
}
|
||||
datatable.reloadDatatable();
|
||||
self.triggerChange();
|
||||
});
|
||||
|
||||
jQuery('.toolbar form select').change(function (event) {
|
||||
jQuery(formSelector + ' select').change(function (event) {
|
||||
let reload = true;
|
||||
switch (event.target.id) {
|
||||
case 'customer':
|
||||
if (jQuery('.toolbar form select#project').length > 0) {
|
||||
if (jQuery(formSelector + ' select#project').length > 0) {
|
||||
reload = false;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'project':
|
||||
if (jQuery('.toolbar form select#activity').length > 0) {
|
||||
if (jQuery(formSelector + ' select#activity').length > 0) {
|
||||
reload = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
jQuery('.toolbar form input#page').val(1);
|
||||
jQuery(formSelector + ' input#page').val(1);
|
||||
|
||||
if (reload) {
|
||||
datatable.reloadDatatable();
|
||||
self.triggerChange();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers an event, that everyone can listen for.
|
||||
*/
|
||||
triggerChange() {
|
||||
this.getContainer().getPlugin('event').trigger('toolbar-change');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the CSS selector to target the toolbar form.
|
||||
*
|
||||
* @returns {string}
|
||||
*/
|
||||
getSelector() {
|
||||
return this.selector;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -24,11 +24,13 @@ export default class KimaiToolbarAction extends KimaiPlugin {
|
||||
|
||||
init() {
|
||||
const self = this;
|
||||
const toolbarSelector = this.getContainer().getPlugin('toolbar').getSelector();
|
||||
|
||||
document.addEventListener('click', function(event) {
|
||||
let target = event.target;
|
||||
while (target !== null && !target.matches('body')) {
|
||||
if (target.classList.contains(self.selector)) {
|
||||
const form = document.querySelector('div.toolbar form.navbar-form');
|
||||
const form = document.querySelector(toolbarSelector);
|
||||
if (form === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
@import 'error-page';
|
||||
@import 'print';
|
||||
@import 'content';
|
||||
@import 'content-header';
|
||||
@import 'toolbar';
|
||||
@import 'sidebar';
|
||||
@import 'footer';
|
||||
|
||||
52
assets/sass/content-header.scss
Normal file
52
assets/sass/content-header.scss
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
.content-header {
|
||||
height: 50px;
|
||||
border-top: 1px solid rgba(0, 0, 0, 0.1);
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1) !important;
|
||||
background-color: #fff !important;
|
||||
padding: 10px 0 0 10px;
|
||||
|
||||
h1 {
|
||||
padding-top: 3px;
|
||||
float: left;
|
||||
small {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Page based action buttons in the upper right corner of the content area */
|
||||
.content-header>.breadcrumb {
|
||||
position: absolute;
|
||||
float: right;
|
||||
background: transparent;
|
||||
top: 0;
|
||||
right: 0;
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
@media (max-width: $screen-md-min) {
|
||||
.content-header>.breadcrumb {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: $screen-sm-min) {
|
||||
.content-header>.breadcrumb {
|
||||
right: 10px;
|
||||
}
|
||||
|
||||
.content-header {
|
||||
h1 {
|
||||
small {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,43 +8,8 @@
|
||||
.content {
|
||||
padding: 15px 0;
|
||||
}
|
||||
.content-header {
|
||||
border-top: 1px solid rgba(0, 0, 0, 0.1);
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1) !important;
|
||||
background-color: #fff !important;
|
||||
padding: 15px 0 15px 10px;
|
||||
|
||||
h1 {
|
||||
small {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.content-header>.breadcrumb {
|
||||
position: relative;
|
||||
margin-top: 5px;
|
||||
top: 0;
|
||||
right: 0;
|
||||
float: none;
|
||||
background: #d2d6de;
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
@media (min-width: $screen-sm-min) {
|
||||
.content-header>.breadcrumb {
|
||||
right: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: $screen-sm-min) {
|
||||
.content-header {
|
||||
h1 {
|
||||
small {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
}
|
||||
.content {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
@@ -5,13 +5,6 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/* Page based action buttons in the upper right corner of the content area */
|
||||
.content-header > .breadcrumb {
|
||||
position: absolute;
|
||||
float: right;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.toolbar-pad {
|
||||
padding: 10px;
|
||||
}
|
||||
@@ -19,6 +12,7 @@
|
||||
/* The filter form is available on most pages above the datatable */
|
||||
@media (min-width: $screen-sm-min) {
|
||||
.toolbar {
|
||||
// @deprecated since 1.2 - using the toolbar is deprecated and will be removed with 2.0
|
||||
form.navbar-form {
|
||||
font-size: $font-size-base;
|
||||
.form-control {
|
||||
@@ -39,3 +33,58 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* Search form with filter dropdown */
|
||||
.content-header {
|
||||
form {
|
||||
width: 200px;
|
||||
float: left;
|
||||
.container-fluid {
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
}
|
||||
.form-group {
|
||||
margin: 0 0 5px 0;
|
||||
}
|
||||
input.search-has-error {
|
||||
color: $red;
|
||||
}
|
||||
.input-group-addon.has-error {
|
||||
color: $red;
|
||||
border-color: $red;
|
||||
}
|
||||
input.has-error {
|
||||
border-color: $red;
|
||||
}
|
||||
ul.dropdown-menu {
|
||||
max-height: 100vh;
|
||||
overflow-y: auto;
|
||||
padding-top: 10px;
|
||||
width: 500px;
|
||||
box-shadow: 0 8px 17px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
|
||||
max-width: 90vw;
|
||||
}
|
||||
a.search-cancel {
|
||||
margin-top: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: $screen-xs-min) {
|
||||
.content-header.search-open {
|
||||
h1 {
|
||||
display: none;
|
||||
}
|
||||
.breadcrumb {
|
||||
display: none;
|
||||
}
|
||||
form.header-search {
|
||||
padding: 0 10px 0 0;
|
||||
float: none;
|
||||
width: 100%;
|
||||
ul.dropdown-menu {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user