Updated TheTool Frontend Framework & Table
This commit is contained in:
101
Layout/default/DocumentationCheck/DeviceTest.php
Normal file
101
Layout/default/DocumentationCheck/DeviceTest.php
Normal file
@@ -0,0 +1,101 @@
|
||||
<?php /** @noinspection PhpUndefinedClassInspection
|
||||
* @var string $mfLayoutPackage
|
||||
* @var TYPE_NAME $git_merge_ts
|
||||
*/
|
||||
|
||||
$JSGlobals = ["BASE_URL" => self::getUrl("Domain"),
|
||||
"DASHBOARD_URL" => self::getUrl("Dashboard"),
|
||||
"MFAPPNAME" => MFAPPNAME_SLUG,
|
||||
"PAGE_TITLE" => "Domains",
|
||||
"PATH" => [
|
||||
["text" => MFAPPNAME_SLUG, "href" => self::getUrl("Dashboard")],
|
||||
["text" => "Voice Calls History", "href" => self::getUrl("VoiceCallHistory")]
|
||||
],
|
||||
"DEVICE_API_URL" => self::getUrl("Device/api"),
|
||||
];
|
||||
|
||||
$additionalJS = [
|
||||
"plugins/vue/vue.js",
|
||||
"plugins/axios/axios.min.js",
|
||||
"plugins/moment/moment.min.js",
|
||||
"plugins/daterangepicker/daterangepicker.js",
|
||||
"plugins/xlsx/xlsx.min.js",
|
||||
"plugins/vue/tt-components/tt-table.js",
|
||||
"plugins/vue/tt-components/tt-page-title.js",
|
||||
"plugins/vue/tt-components/tt-select.js",
|
||||
"plugins/vue/tt-components/tt-datepicker.js",
|
||||
"plugins/vue/tt-components/tt-input.js",
|
||||
"plugins/vue/tt-components/tt-autocomplete.js",
|
||||
"plugins/vue/tt-components/tt-icon-select.js",
|
||||
"plugins/vue/tt-components/tt-number-range.js",
|
||||
];
|
||||
$additionalCSS = [
|
||||
"plugins/daterangepicker/daterangepicker.css",
|
||||
'plugins/vue/tt-components/css/tt-table.css',
|
||||
];
|
||||
|
||||
include(realpath(dirname(__FILE__) . "/../../$mfLayoutPackage") . "/header.php"); ?>
|
||||
|
||||
<div id="app">
|
||||
<tt-page-title :title="window['TT_CONFIG']['PAGE_TITLE']" :path="window['TT_CONFIG']['PATH']"></tt-page-title>
|
||||
|
||||
<tt-table :fetch-url="window['TT_CONFIG']['DEVICE_API_URL'] + '?do=getDevices'" :config="DeviceTableConfig" small
|
||||
ref="table">
|
||||
<template v-slot:top-buttons>
|
||||
<button type="button" class="btn btn-primary" @click="window.alert(1)">
|
||||
<i class="fas fa-plus"></i>Neues Device anlegen
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<template v-slot:actions="{row}">
|
||||
<a @click="window.alert(row.id)"><i class="far fa-edit text-primary" title="Bearbeiten"></i></a>
|
||||
<a @click="window.alert(row.id)"><i class="fas fa-trash text-danger" title="Löschen"></i></a>
|
||||
</template>
|
||||
|
||||
<template v-slot:power="{row}">
|
||||
{{ parseInt(row.power) }} Watt
|
||||
</template>
|
||||
|
||||
<template v-slot:price="{row}">
|
||||
{{ parseInt(row.price) }} €
|
||||
</template>
|
||||
|
||||
<template v-slot:locationText="{row}">
|
||||
<a :title="row.locationText" :href="row.locationUrl" v-text="row.locationText" target="_blank"></a>
|
||||
</template>
|
||||
|
||||
</tt-table>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
window: window,
|
||||
DeviceTableConfig: {
|
||||
headers: [
|
||||
{text: "Device Name", key: "name"},
|
||||
{text: "Geräte Typ", key: "devicetype"},
|
||||
{text: "Hersteller", key: "devicemanufactor"},
|
||||
{text: "Pop/Adresse", key: "locationText"},
|
||||
{text: "IP-Adresse", key: "ip"},
|
||||
{text: "MAC-Adresse", key: "mac"},
|
||||
{text: "Seriennummer", key: "serial"},
|
||||
{text: "Preis", key: "price", filter: "numberRange"},
|
||||
{text: "max. Leistung", key: "power", filter: "numberRange"},
|
||||
{
|
||||
text: "Backup", key: "backup", filter: "iconSelect", filterOptions: [
|
||||
{value: "ok", text: "Letztes Backup jünger als 48 Stunden", icon: "fa-regular fa-circle-check text-success"},
|
||||
{value: "aged", text: "Letztes Backup älter als 48 Stunden", icon: "fa-regular fa-circle-xmark text-danger"},
|
||||
{value: "na", text: "Kein Backup", icon: "fa-regular fa-ban text-primary"}
|
||||
]},
|
||||
{text: "Aktionen", key: "actions"},
|
||||
],
|
||||
tableHeader: 'Devices',
|
||||
key: 'Device',
|
||||
},
|
||||
},
|
||||
})
|
||||
</script>
|
||||
<?php include(realpath(dirname(__FILE__) . "/../../$mfLayoutPackage") . "/footer.php"); ?>
|
||||
|
||||
66
Layout/default/DocumentationCheck/Index.php
Normal file
66
Layout/default/DocumentationCheck/Index.php
Normal file
@@ -0,0 +1,66 @@
|
||||
<?php /** @noinspection PhpUndefinedClassInspection
|
||||
* @var string $mfLayoutPackage
|
||||
* @var TYPE_NAME $git_merge_ts
|
||||
*/
|
||||
|
||||
//additional css /css/views/RaspberryDisplay.css
|
||||
|
||||
$JSGlobals = ["BASE_URL" => self::getUrl("DocumentationCheck"),
|
||||
"DASHBOARD_URL" => self::getUrl("Dashboard"),
|
||||
"MFAPPNAME" => MFAPPNAME_SLUG,
|
||||
"PAGE_TITLE" => "Documentation Check",
|
||||
"PATH" => [
|
||||
["text" => MFAPPNAME_SLUG, "href" => self::getUrl("Dashboard")],
|
||||
["text" => "Documentation Check", "href" => self::getUrl("DocumentationCheck")]
|
||||
],
|
||||
"DOCUMENTATION_CHECK_API_URL" => self::getUrl("DocumentationCheck/api"),
|
||||
];
|
||||
|
||||
$additionalJS = ["plugins/vue/vue.js",
|
||||
"plugins/axios/axios.min.js",
|
||||
"plugins/vue/tt-components/tt-page-title.js",
|
||||
"plugins/vue/tt-components/tt-table.js",
|
||||
];
|
||||
$additionalCSS = [
|
||||
'plugins/vue/tt-components/css/tt-table.css',
|
||||
];
|
||||
|
||||
include(realpath(dirname(__FILE__) . "/../../$mfLayoutPackage") . "/header.php"); ?>
|
||||
|
||||
<div id="app">
|
||||
<tt-page-title :title="window['TT_CONFIG']['PAGE_TITLE']" :path="window['TT_CONFIG']['PATH']"></tt-page-title>
|
||||
|
||||
<tt-table :fetch-url="window['TT_CONFIG']['DOCUMENTATION_CHECK_API_URL'] + '?do=getDocumentationFails'" :table-config="DocumentationCheckTableConfig" small ref="table">
|
||||
</tt-table>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
window: window,
|
||||
DocumentationCheckTableConfig: {
|
||||
headers: [
|
||||
{text: 'Device Name', key: 'deviceName', filter: false},
|
||||
{text: 'Device IP', key: 'deviceIP', filter: false},
|
||||
{text: 'Zabbix Host Name', key: 'zabbixHostName', filter: false},
|
||||
{text: 'Zabbix Host IP', key: 'zabbixHostIP', filter: false},
|
||||
{text: 'Device In Zabbix', key: 'checkDeviceInZabbix', filter: false},
|
||||
{text: 'Device Has ICMP Ping', key: 'checkDeviceHasICMPPing', filter: false},
|
||||
{text: 'Device Has Same Name', key: 'checkDeviceHasSameName', filter: false},
|
||||
{text: 'Device Has Same IP', key: 'checkDeviceHasSameIP', filter: false},
|
||||
],
|
||||
tableHeader: 'Monitoring Dokumentations Check',
|
||||
|
||||
|
||||
},
|
||||
refreshLoading: false,
|
||||
autoRefresh: null,
|
||||
},
|
||||
mounted() {
|
||||
$('[data-toggle="tooltip"]').tooltip();
|
||||
},
|
||||
})
|
||||
</script>
|
||||
<?php include(realpath(dirname(__FILE__) . "/../../$mfLayoutPackage") . "/footer.php"); ?>
|
||||
|
||||
@@ -17,12 +17,23 @@ $JSGlobals = ["BASE_URL" => self::getUrl("Domain"),
|
||||
"DOMAIN_API_URL" => self::getUrl("Domain/api"),
|
||||
];
|
||||
|
||||
$additionalJS = ["plugins/vue/vue.js",
|
||||
$additionalJS = [
|
||||
"plugins/vue/vue.js",
|
||||
"plugins/axios/axios.min.js",
|
||||
"plugins/vue/tt-components/tt-page-title.js",
|
||||
"plugins/moment/moment.min.js",
|
||||
"plugins/daterangepicker/daterangepicker.js",
|
||||
"plugins/xlsx/xlsx.min.js",
|
||||
"plugins/vue/tt-components/tt-table.js",
|
||||
"plugins/vue/tt-components/tt-page-title.js",
|
||||
"plugins/vue/tt-components/tt-select.js",
|
||||
"plugins/vue/tt-components/tt-datepicker.js",
|
||||
"plugins/vue/tt-components/tt-input.js",
|
||||
"plugins/vue/tt-components/tt-autocomplete.js",
|
||||
"plugins/vue/tt-components/tt-icon-select.js",
|
||||
"plugins/vue/tt-components/tt-number-range.js",
|
||||
];
|
||||
$additionalCSS = [
|
||||
"plugins/daterangepicker/daterangepicker.css",
|
||||
'plugins/vue/tt-components/css/tt-table.css',
|
||||
];
|
||||
|
||||
@@ -32,8 +43,8 @@ include(realpath(dirname(__FILE__) . "/../../$mfLayoutPackage") . "/header.php")
|
||||
<!-- start page title -->
|
||||
<tt-page-title :title="window['TT_CONFIG']['PAGE_TITLE']" :path="window['TT_CONFIG']['PATH']"></tt-page-title>
|
||||
|
||||
<tt-table :fetch-url="window['TT_CONFIG']['DOMAIN_API_URL'] + '?do=getDomains'" :table-config="domainsTableConfig"
|
||||
small ref="table">
|
||||
<tt-table :fetch-url="window['TT_CONFIG']['DOMAIN_API_URL'] + '?do=getDomains'" :config="domainsTableConfig"
|
||||
small ssr ref="table">
|
||||
<template v-slot:top-buttons>
|
||||
<button type="button" class="btn btn-primary" @click="reloadDomains">
|
||||
<template v-if="reloadDomainsLoading">
|
||||
@@ -70,18 +81,6 @@ include(realpath(dirname(__FILE__) . "/../../$mfLayoutPackage") . "/header.php")
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<!-- Slot to show if Domain is in Plesk -->
|
||||
<template v-slot:pleskid="{ row }">
|
||||
<i v-if="row.pleskId !== null" class="fas fa-check text-success"></i>
|
||||
<i v-else class="fas fa-times text-danger"></i>
|
||||
</template>
|
||||
|
||||
<!-- Convert all Dates to human readable format -->
|
||||
<template v-slot:crdate="{ row }">{{ new Date(row.crDate * 1000).toLocaleDateString() }}</template>
|
||||
<template v-slot:exdate="{ row }">{{ new Date(row.exDate * 1000).toLocaleDateString() }}</template>
|
||||
<template v-slot:redate="{ row }">{{ new Date(row.reDate * 1000).toLocaleDateString() }}</template>
|
||||
<template v-slot:update="{ row }">{{ new Date(row.upDate * 1000).toLocaleDateString() }}</template>
|
||||
|
||||
<!-- Registrant Admin Tech Billing from domainContacts -->
|
||||
<template v-slot:registrant="{ row }">
|
||||
{{ domainContacts[row.registrant] ? domainContacts[row.registrant]["name"] : '' }}
|
||||
@@ -159,19 +158,25 @@ include(realpath(dirname(__FILE__) . "/../../$mfLayoutPackage") . "/header.php")
|
||||
domainsTableConfig() {
|
||||
const base = {
|
||||
headers: [
|
||||
{text: "ID", key: "inwxRoId", "filter": false},
|
||||
{text: "DNS", key: "inwxRoId", filter: false, sortable: false},
|
||||
{text: "Domain", key: "domain"},
|
||||
{text: "Plesk", key: "pleskId", filter: false},
|
||||
{text: "Created Date", key: "crDate", filter: false},
|
||||
{text: "Expiration Date", key: "exDate", filter: false},
|
||||
{text: "Renewal Date", key: "reDate", filter: false},
|
||||
{text: "Updated Date", key: "upDate", filter: false},
|
||||
{text: "Transfer Lock", key: "transferLock"},
|
||||
{text: "Authorization Code", key: "authCode"},
|
||||
{text: "Registrant ID", key: "registrant"},
|
||||
{text: "Admin ID", key: "admin"},
|
||||
{text: "Tech ID", key: "tech"},
|
||||
{text: "Billing ID", key: "billing"},
|
||||
{text: "Plesk", key: "pleskId", filter: 'iconSelect', filterOptions: [
|
||||
{value: 1, text: 'Yes', icon: 'fas fa-check text-success'},
|
||||
{value: 0, text: 'No', icon: 'fas fa-times text-danger'}
|
||||
], sortable: false},
|
||||
{text: "Created Date", key: "crDate", filter: "date"},
|
||||
{text: "Expiration Date", key: "exDate", filter: "date"},
|
||||
{text: "Renewal Date", key: "reDate", filter: "date"},
|
||||
{text: "Updated Date", key: "upDate", filter: "date"},
|
||||
{text: "Transfer Lock", key: "transferLock", filter: 'iconSelect', filterOptions: [
|
||||
{value: 1, text: 'Locked', icon: 'fas fa-lock text-danger'},
|
||||
{value: 0, text: 'Unlocked', icon: 'fas fa-unlock text-success'}
|
||||
]},
|
||||
{text: "Authorization Code", key: "authCode", sortable: false},
|
||||
{text: "Registrant ID", key: "registrant", sortable: false},
|
||||
{text: "Admin ID", key: "admin", sortable: false},
|
||||
{text: "Tech ID", key: "tech", sortable: false},
|
||||
{text: "Billing ID", key: "billing", sortable: false},
|
||||
{text: "Name Servers", key: "ns"}
|
||||
],
|
||||
tableHeader: 'Bestellungen',
|
||||
|
||||
@@ -16,12 +16,23 @@ $JSGlobals = ["BASE_URL" => self::getUrl("Domain"),
|
||||
"HISTORIC_TICKET_API_URL" => self::getUrl("HistoricTicket/api"),
|
||||
];
|
||||
|
||||
$additionalJS = ["plugins/vue/vue.js",
|
||||
$additionalJS = [
|
||||
"plugins/vue/vue.js",
|
||||
"plugins/axios/axios.min.js",
|
||||
"plugins/vue/tt-components/tt-page-title.js",
|
||||
"plugins/moment/moment.min.js",
|
||||
"plugins/daterangepicker/daterangepicker.js",
|
||||
"plugins/xlsx/xlsx.min.js",
|
||||
"plugins/vue/tt-components/tt-table.js",
|
||||
"plugins/vue/tt-components/tt-page-title.js",
|
||||
"plugins/vue/tt-components/tt-select.js",
|
||||
"plugins/vue/tt-components/tt-datepicker.js",
|
||||
"plugins/vue/tt-components/tt-input.js",
|
||||
"plugins/vue/tt-components/tt-autocomplete.js",
|
||||
"plugins/vue/tt-components/tt-icon-select.js",
|
||||
"plugins/vue/tt-components/tt-number-range.js",
|
||||
];
|
||||
$additionalCSS = [
|
||||
"plugins/daterangepicker/daterangepicker.css",
|
||||
'plugins/vue/tt-components/css/tt-table.css',
|
||||
];
|
||||
|
||||
@@ -32,8 +43,8 @@ include(realpath(dirname(__FILE__) . "/../../$mfLayoutPackage") . "/header.php")
|
||||
<tt-page-title :title="window['TT_CONFIG']['PAGE_TITLE']" :path="window['TT_CONFIG']['PATH']"></tt-page-title>
|
||||
|
||||
<tt-table :fetch-url="window['TT_CONFIG']['HISTORIC_TICKET_API_URL'] + '?do=getHistoricTickets'"
|
||||
:table-config="historicTicketTableConfig"
|
||||
small ref="table">
|
||||
:config="historicTicketTableConfig"
|
||||
small ssr ref="table">
|
||||
<template v-slot:top-buttons>
|
||||
<!-- add input for global search with label and bootstrap class-->
|
||||
<div class="input-group mb-3">
|
||||
@@ -74,11 +85,11 @@ include(realpath(dirname(__FILE__) . "/../../$mfLayoutPackage") . "/header.php")
|
||||
<div class="modal-body">
|
||||
<tt-table
|
||||
:fetch-url="`${window['TT_CONFIG']['HISTORIC_TICKET_API_URL']}?do=findHistoricTicket&query=${globalSearch}`"
|
||||
:table-config="globalSearchModalTableConfig"
|
||||
:config="globalSearchModalTableConfig"
|
||||
small ref="table">
|
||||
|
||||
<template v-slot:ctime="{ row }">
|
||||
{{ new Date(row.ctime * 1000).toLocaleString() }}
|
||||
{{ window.moment(row.ctime * 1000).format('DD.MM.YYYY HH:mm') }}
|
||||
</template>
|
||||
|
||||
<template v-slot:ticket_number="{ row }">
|
||||
@@ -136,26 +147,52 @@ include(realpath(dirname(__FILE__) . "/../../$mfLayoutPackage") . "/header.php")
|
||||
globalSearchModal: false,
|
||||
globalSearchModalTableConfig: {
|
||||
headers: [
|
||||
{text: 'Ticket Number', key: 'ticket_number', filter: false},
|
||||
{text: 'Erstellt', key: 'ctime', filter: false},
|
||||
{text: 'Subject', key: 'ticket_subject', filter: false},
|
||||
{text: 'Message', key: 'ticket_message', filter: false},
|
||||
{text: 'Ticket Number', key: 'ticket_number', filter: false, sortable: false},
|
||||
{text: 'Erstellt', key: 'ctime', filter: false, sortable: false},
|
||||
{text: 'Subject', key: 'ticket_subject', filter: false, sortable: false},
|
||||
{text: 'Message', key: 'ticket_message', filter: false, sortable: false},
|
||||
],
|
||||
tableHeader: 'Suchergebnisse',
|
||||
key: 'HistoricTicketGlobalSearch',
|
||||
},
|
||||
historicTicketTableConfig: {
|
||||
headers: [
|
||||
{text: 'Ticket Number', key: 'ticket_number', filter: 'search'},
|
||||
{text: 'Erstellt', key: 'ctime', filter: false},
|
||||
{text: 'Subject', key: 'subject', filter: 'search'},
|
||||
{text: 'Type', key: 'type', filter: 'search'},
|
||||
{text: 'Status', key: 'status', filter: 'search'},
|
||||
{text: 'Name', key: 'first_name', filter: 'search'},
|
||||
{text: 'Email', key: 'email', filter: 'search'},
|
||||
{text: 'Phone', key: 'phone', filter: 'search'},
|
||||
{text: 'Subject', key: 'subject', filter: 'search', sortable: false},
|
||||
{text: 'Type', key: 'type', filter: 'select', filterOptions: [
|
||||
{value: 'BACKOFFICE', text: 'BACKOFFICE'},
|
||||
{value: 'KUNDENANFRAGEN', text: 'KUNDENANFRAGEN'},
|
||||
{value: 'STÖRUNGEN', text: 'STÖRUNGEN'},
|
||||
{value: 'ALLGEMEINES', text: 'ALLGEMEINES'},
|
||||
{value: 'TERMIN VEREINBART', text: 'TERMIN VEREINBART'},
|
||||
{value: 'VERRECHNEN AB DATUM', text: 'VERRECHNEN AB DATUM'},
|
||||
{value: 'ONLINE-TICKETS', text: 'ONLINE-TICKETS'},
|
||||
{value: 'KÜNDIGUNG', text: 'KÜNDIGUNG'},
|
||||
{value: 'BESTELLUNGEN', text: 'BESTELLUNGEN'},
|
||||
{value: 'PORTIERUNG', text: 'PORTIERUNG'},
|
||||
{value: 'KABEL-TV', text: 'KABEL-TV'},
|
||||
{value: 'TIEFBAU', text: 'TIEFBAU'},
|
||||
{value: 'ENERGIE STEIERMARK', text: 'ENERGIE STEIERMARK'},
|
||||
{value: 'INTERN', text: 'INTERN'},
|
||||
{value: 'FELIX', text: 'FELIX'},
|
||||
{value: '0', text: '0'},
|
||||
{value: 'JETTEN', text: 'JETTEN'},
|
||||
]},
|
||||
{text: 'Status', key: 'status', filter: 'select', filterOptions: [
|
||||
{value: 'Geschlossen', text: 'Geschlossen'},
|
||||
{value: 'In Evidenz', text: 'In Evidenz'},
|
||||
{value: 'In Bearbeitung', text: 'In Bearbeitung'},
|
||||
{value: 'Business In Bearbeitung', text: 'Business In Bearbeitung'},
|
||||
{value: 'Business Angebot gelegt', text: 'Business Angebot gelegt'},
|
||||
]},
|
||||
{text: 'Name', key: 'first_name', filter: 'search', sortable: false},
|
||||
{text: 'Email', key: 'email', filter: 'search', sortable: false},
|
||||
{text: 'Phone', key: 'phone', filter: 'search', sortable: false},
|
||||
],
|
||||
defaultPageSize: 25,
|
||||
tableHeader: 'Bestellungen',
|
||||
tableHeader: 'Historische Tickets',
|
||||
key: 'HistoricTicket',
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
||||
@@ -16,12 +16,23 @@ $JSGlobals = ["BASE_URL" => self::getUrl("VoiceCallActive"),
|
||||
"VOICE_CALL_ACTIVE_API_URL" => self::getUrl("VoiceCallActive/api"),
|
||||
];
|
||||
|
||||
$additionalJS = ["plugins/vue/vue.js",
|
||||
$additionalJS = [
|
||||
"plugins/vue/vue.js",
|
||||
"plugins/axios/axios.min.js",
|
||||
"plugins/vue/tt-components/tt-page-title.js",
|
||||
"plugins/moment/moment.min.js",
|
||||
"plugins/daterangepicker/daterangepicker.js",
|
||||
"plugins/xlsx/xlsx.min.js",
|
||||
"plugins/vue/tt-components/tt-table.js",
|
||||
"plugins/vue/tt-components/tt-page-title.js",
|
||||
"plugins/vue/tt-components/tt-select.js",
|
||||
"plugins/vue/tt-components/tt-datepicker.js",
|
||||
"plugins/vue/tt-components/tt-input.js",
|
||||
"plugins/vue/tt-components/tt-autocomplete.js",
|
||||
"plugins/vue/tt-components/tt-icon-select.js",
|
||||
"plugins/vue/tt-components/tt-number-range.js",
|
||||
];
|
||||
$additionalCSS = [
|
||||
"plugins/daterangepicker/daterangepicker.css",
|
||||
'plugins/vue/tt-components/css/tt-table.css',
|
||||
];
|
||||
|
||||
@@ -30,8 +41,8 @@ include(realpath(dirname(__FILE__) . "/../../$mfLayoutPackage") . "/header.php")
|
||||
<div id="app">
|
||||
<tt-page-title :title="window['TT_CONFIG']['PAGE_TITLE']" :path="window['TT_CONFIG']['PATH']"></tt-page-title>
|
||||
|
||||
<tt-table :fetch-url="window['TT_CONFIG']['VOICE_CALL_ACTIVE_API_URL'] + '?do=getActiveCalls'" :table-config="VoiceCallActiveTableConfig"
|
||||
small ref="table">
|
||||
<tt-table :fetch-url="window['TT_CONFIG']['VOICE_CALL_ACTIVE_API_URL'] + '?do=getActiveCalls'" :config="VoiceCallActiveTableConfig"
|
||||
small ssr ref="table">
|
||||
|
||||
<template v-slot:top-buttons>
|
||||
<button type="button" class="btn btn-primary" @click="refresh" data-toggle="tooltip" data-placement="bottom" title="Refreshing too often will run into API-Rate limits and will cause errors.">
|
||||
@@ -57,7 +68,7 @@ include(realpath(dirname(__FILE__) . "/../../$mfLayoutPackage") . "/header.php")
|
||||
</template>
|
||||
|
||||
<template v-slot:answer_time="{ row }">
|
||||
{{ !isNaN(new Date(row.answer_time)) ? new Date(row.answer_time).toLocaleString() : 'Call is not running' }}
|
||||
{{ !isNaN(new Date(row.answer_time)) ? window.moment(row.answer_time, 'YYYY-MM-DD HH:mm:ss Z').format('DD.MM.YYYY HH:mm:ss') : 'Call is not running' }}
|
||||
</template>
|
||||
|
||||
<template v-slot:status="{ row }">
|
||||
@@ -71,15 +82,6 @@ include(realpath(dirname(__FILE__) . "/../../$mfLayoutPackage") . "/header.php")
|
||||
</tt-table>
|
||||
</div>
|
||||
|
||||
<!-- TODO: download from cdn to local -->
|
||||
|
||||
<link href="https://cdn.jsdelivr.net/gh/gitbrent/bootstrap4-toggle@3.6.1/css/bootstrap4-toggle.min.css" rel="stylesheet">
|
||||
<script src="https://cdn.jsdelivr.net/gh/gitbrent/bootstrap4-toggle@3.6.1/js/bootstrap4-toggle.min.js"></script>
|
||||
|
||||
<script type="text/javascript" src="https://cdn.jsdelivr.net/momentjs/latest/moment.min.js"></script>
|
||||
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/daterangepicker/daterangepicker.min.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/daterangepicker/daterangepicker.css" />
|
||||
|
||||
<style>
|
||||
.voice-green {
|
||||
background-color: #6CAE75 !important
|
||||
@@ -114,15 +116,15 @@ include(realpath(dirname(__FILE__) . "/../../$mfLayoutPackage") . "/header.php")
|
||||
}
|
||||
},
|
||||
headers: [
|
||||
{text: 'Call ID', key: 'id', filter: false},
|
||||
{text: 'Status', key: 'status', filter: false},
|
||||
{text: 'Answer Time', key: 'answer_time', filter: false},
|
||||
{text: 'Duration', key: 'duration', filter: false},
|
||||
{text: 'Source', key: 'src', filter: false},
|
||||
{text: 'Device Type', key: 'device_type', filter: false},
|
||||
{text: 'Destination', key: 'localized_dst', filter: false},
|
||||
{text: 'Destination User', key: 'dst_user', filter: false},
|
||||
{text: 'Destination Device Extension', key: 'dst_device_extension', filter: false},
|
||||
{text: 'Call ID', key: 'id', filter: false, sortable: false},
|
||||
{text: 'Status', key: 'status', filter: false, sortable: false},
|
||||
{text: 'Answer Time', key: 'answer_time', filter: false, sortable: false},
|
||||
{text: 'Duration', key: 'duration', filter: false, sortable: false},
|
||||
{text: 'Source', key: 'src', filter: false, sortable: false},
|
||||
{text: 'Device Type', key: 'device_type', filter: false, sortable: false},
|
||||
{text: 'Destination', key: 'localized_dst', filter: false, sortable: false},
|
||||
{text: 'Destination User', key: 'dst_user', filter: false, sortable: false},
|
||||
{text: 'Destination Device Extension', key: 'dst_device_extension', filter: false, sortable: false},
|
||||
],
|
||||
tableHeader: 'Active Voice Calls',
|
||||
},
|
||||
@@ -130,6 +132,7 @@ include(realpath(dirname(__FILE__) . "/../../$mfLayoutPackage") . "/header.php")
|
||||
autoRefresh: null,
|
||||
},
|
||||
mounted() {
|
||||
//TODO: create vue tooltip component
|
||||
$('[data-toggle="tooltip"]').tooltip();
|
||||
|
||||
const _this = this;
|
||||
|
||||
@@ -16,12 +16,23 @@ $JSGlobals = ["BASE_URL" => self::getUrl("Domain"),
|
||||
"VOICE_CALL_HISTORY_API_URL" => self::getUrl("VoiceCallHistory/api"),
|
||||
];
|
||||
|
||||
$additionalJS = ["plugins/vue/vue.js",
|
||||
$additionalJS = [
|
||||
"plugins/vue/vue.js",
|
||||
"plugins/axios/axios.min.js",
|
||||
"plugins/vue/tt-components/tt-page-title.js",
|
||||
"plugins/moment/moment.min.js",
|
||||
"plugins/daterangepicker/daterangepicker.js",
|
||||
"plugins/xlsx/xlsx.min.js",
|
||||
"plugins/vue/tt-components/tt-table.js",
|
||||
"plugins/vue/tt-components/tt-page-title.js",
|
||||
"plugins/vue/tt-components/tt-select.js",
|
||||
"plugins/vue/tt-components/tt-datepicker.js",
|
||||
"plugins/vue/tt-components/tt-input.js",
|
||||
"plugins/vue/tt-components/tt-autocomplete.js",
|
||||
"plugins/vue/tt-components/tt-icon-select.js",
|
||||
"plugins/vue/tt-components/tt-number-range.js",
|
||||
];
|
||||
$additionalCSS = [
|
||||
"plugins/daterangepicker/daterangepicker.css",
|
||||
'plugins/vue/tt-components/css/tt-table.css',
|
||||
];
|
||||
|
||||
@@ -30,8 +41,8 @@ include(realpath(dirname(__FILE__) . "/../../$mfLayoutPackage") . "/header.php")
|
||||
<div id="app">
|
||||
<tt-page-title :title="window['TT_CONFIG']['PAGE_TITLE']" :path="window['TT_CONFIG']['PATH']"></tt-page-title>
|
||||
|
||||
<tt-table :fetch-url="window['TT_CONFIG']['VOICE_CALL_HISTORY_API_URL'] + '?do=getCalls'" :table-config="VoiceCallHistoryTableConfig"
|
||||
small ref="table">
|
||||
<tt-table :fetch-url="window['TT_CONFIG']['VOICE_CALL_HISTORY_API_URL'] + '?do=getCalls'" :config="VoiceCallHistoryTableConfig"
|
||||
small ssr ref="table">
|
||||
<template v-slot:top-buttons>
|
||||
<button type="button" class="btn btn-primary" @click="importCallsFromToday">
|
||||
<template v-if="importCallsFromTodayLoading">
|
||||
@@ -47,13 +58,6 @@ include(realpath(dirname(__FILE__) . "/../../$mfLayoutPackage") . "/header.php")
|
||||
</tt-table>
|
||||
</div>
|
||||
|
||||
<!-- TODO: download from cdn to local -->
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-select@1.13.14/dist/css/bootstrap-select.min.css">
|
||||
|
||||
<script type="text/javascript" src="https://cdn.jsdelivr.net/momentjs/latest/moment.min.js"></script>
|
||||
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/daterangepicker/daterangepicker.min.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/daterangepicker/daterangepicker.css" />
|
||||
|
||||
<script>
|
||||
new Vue({
|
||||
el: '#app',
|
||||
@@ -63,13 +67,17 @@ include(realpath(dirname(__FILE__) . "/../../$mfLayoutPackage") . "/header.php")
|
||||
headers: [
|
||||
{text: "Call-ID", key: "uid"},
|
||||
{text: "Voice Account", key: "voice_account"},
|
||||
{text: "Time Range", key: "start", filter: "dateRange"},
|
||||
{text: "Time Range", key: "start", filter: "date"},
|
||||
{text: "Source", key: "source"},
|
||||
{text: "Destination", key: "destination"},
|
||||
{text: "Billable", key: "billable"},
|
||||
{text: "Billable", key: "billable", filter: "iconSelect", filterOptions: [
|
||||
{value: 1, text: 'Yes', icon: 'fas fa-check text-success'},
|
||||
{value: 0, text: 'No', icon: 'fas fa-times text-danger'}
|
||||
]},
|
||||
{text: "Duration", key: "duration"},
|
||||
],
|
||||
tableHeader: 'Voice Call History',
|
||||
key: 'VoiceCallHistory',
|
||||
},
|
||||
importCallsFromTodayLoading: false,
|
||||
},
|
||||
|
||||
@@ -250,7 +250,43 @@ class DeviceController extends mfBaseController
|
||||
$ont = $this->request->ont;
|
||||
$data = [];
|
||||
|
||||
|
||||
switch ($do) {
|
||||
case "getDevices":
|
||||
$devices = DeviceModel::getAll();
|
||||
|
||||
foreach ($devices as $device) {
|
||||
$locationText = "";
|
||||
$locationUrl = "";
|
||||
|
||||
if (trim($device->pop->name)) {
|
||||
$locationText = $device->pop->name;
|
||||
$locationUrl = self::getUrl("Pop", "Detail", ["id" => $device->pop->id]);
|
||||
} else if (trim($device->addr_street)) {
|
||||
$locationText = $device->addr_street . " " . $device->addr_number . ", " . $device->addr_zip . " " . $device->addr_city;
|
||||
$locationUrl = "http://maps.google.com/?q=" . $locationText;
|
||||
} else if (trim($device->gps_lat)) {
|
||||
$locationText = $device->gps_lat . " , " . $device->gps_long;
|
||||
$locationUrl = "http://maps.google.com/?q=" . $locationText;
|
||||
}
|
||||
|
||||
$data[] = [
|
||||
"name" => $device->name,
|
||||
"devicetype" => $device->devicetype->name,
|
||||
"devicemanufactor" => $device->devicetype->devicemanufactor->name,
|
||||
"locationText" => $locationText,
|
||||
"locationUrl" => $locationUrl,
|
||||
"ip" => $device->ip,
|
||||
"mac" => $device->mac,
|
||||
"serial" => $device->serial,
|
||||
"price" => $device->price != "0.0" ? $device->price : $device->devicetype->price,
|
||||
"power" => $device->power != "0.0" ? $device->power : $device->devicetype->power,
|
||||
"backup" => $device->last_config_backup ? (time() - $device->last_config_backup <= 172800 ? 'ok' : 'aged') : 'na',
|
||||
];
|
||||
}
|
||||
|
||||
die(json_encode($data));
|
||||
break;
|
||||
case "getconfig":
|
||||
$return = $this->getConfig($id, $format, $filename);
|
||||
break;
|
||||
|
||||
9
application/DocumentationCheck/DocumentationCheck.php
Normal file
9
application/DocumentationCheck/DocumentationCheck.php
Normal file
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @property mixed|null $name
|
||||
*/
|
||||
class DocumentationCheck extends mfBaseModel
|
||||
{
|
||||
|
||||
}
|
||||
113
application/DocumentationCheck/DocumentationCheckController.php
Normal file
113
application/DocumentationCheck/DocumentationCheckController.php
Normal file
@@ -0,0 +1,113 @@
|
||||
<?php
|
||||
|
||||
class DocumentationCheckController extends mfBaseController {
|
||||
private User $me;
|
||||
private string $ZABBIX_API_URL = ZABBIX_API_URL;
|
||||
private string $ZABBIX_API_KEY = ZABBIX_API_KEY;
|
||||
|
||||
private Zabbix $zabbix;
|
||||
|
||||
|
||||
protected function init(): void {
|
||||
$me = new User();
|
||||
$me->loadMe();
|
||||
$this->layout()->set("me", $me);
|
||||
$this->me = $me;
|
||||
|
||||
if (!$this->me->isAdmin()) {
|
||||
$this->redirect("dashboard");
|
||||
}
|
||||
|
||||
$this->zabbix = new Zabbix($this->ZABBIX_API_URL, $this->ZABBIX_API_KEY);
|
||||
}
|
||||
|
||||
protected function indexAction(): void {
|
||||
$this->layout()->setTemplate("DocumentationCheck/DeviceTest");
|
||||
}
|
||||
|
||||
protected function apiAction() {
|
||||
$do = $this->request->do;
|
||||
|
||||
if (!$this->me->isAdmin()) {
|
||||
$this->redirect("dashboard");
|
||||
}
|
||||
|
||||
switch ($do) {
|
||||
case "getDocumentationFails":
|
||||
$return = $this->getDocumentationFails();
|
||||
break;
|
||||
default:
|
||||
$return = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!$return) {
|
||||
$return = [
|
||||
"status" => "error",
|
||||
"message" => "Invalid request."
|
||||
];
|
||||
}
|
||||
|
||||
die(json_encode($return));
|
||||
}
|
||||
|
||||
private function getDocumentationFails(): array {
|
||||
$icmpItems = $this->zabbix->getICMPItems([]);
|
||||
$zabbixICMPItems = [];
|
||||
|
||||
foreach ($icmpItems as $item) {
|
||||
if (!isset($zabbixICMPItems[$item['hostid']])) {
|
||||
$zabbixICMPItems[$item['hostid']] = [];
|
||||
}
|
||||
$zabbixICMPItems[$item['hostid']][] = $item;
|
||||
}
|
||||
|
||||
$zabbixHostIPToId = [];
|
||||
$zabbixHostNameToId = [];
|
||||
$zabbixHosts = [];
|
||||
|
||||
foreach ($this->zabbix->getHosts("") as $host) {
|
||||
$zabbixHostIPToId[$host['host']] = $host['hostid'];
|
||||
$zabbixHostNameToId[$host['name']] = $host['hostid'];
|
||||
$zabbixHosts[$host['hostid']] = $host;
|
||||
}
|
||||
|
||||
$devices = DeviceModel::getAll();
|
||||
|
||||
//loop through all devices and join them by either IP or hostname and then return {device, zabbixHost}
|
||||
$hosts = [];
|
||||
foreach ($devices as $device) {
|
||||
$deviceIP = $device->ip;
|
||||
$deviceHostname = $device->name;
|
||||
$zabbixHost = null;
|
||||
|
||||
if (isset($zabbixHostIPToId[$deviceIP])) {
|
||||
$zabbixHost = $zabbixHosts[$zabbixHostIPToId[$deviceIP]];
|
||||
} else if (isset($zabbixHostNameToId[$deviceHostname])) {
|
||||
$zabbixHost = $zabbixHosts[$zabbixHostNameToId[$deviceHostname]];
|
||||
}
|
||||
|
||||
$hosts[] = [
|
||||
"zabbixHostId" => $zabbixHost['hostid'],
|
||||
"zabbixHostName" => $zabbixHost['name'],
|
||||
"zabbixHostIP" => $zabbixHost['host'],
|
||||
"deviceName" => $device->name,
|
||||
"deviceIP" => $device->ip,
|
||||
"checkDeviceInZabbix" => $zabbixHost !== null,
|
||||
"checkDeviceHasICMPPing" => isset($zabbixICMPItems[$zabbixHost['hostid']]),
|
||||
"checkDeviceHasSameName" => $zabbixHost['name'] === $device->name,
|
||||
"checkDeviceHasSameIP" => $zabbixHost['host'] === $device->ip
|
||||
// "raw" => [
|
||||
// "zabbixHost" => $zabbixHost,
|
||||
// "device" => $device->toArray()
|
||||
// ]
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
"status" => "success",
|
||||
"rows" => $hosts
|
||||
];
|
||||
|
||||
}
|
||||
}
|
||||
@@ -66,12 +66,6 @@ class DomainController extends mfBaseController {
|
||||
die(json_encode($return));
|
||||
}
|
||||
|
||||
protected function importDomain(): void {
|
||||
// use plesk api to get all domains
|
||||
|
||||
|
||||
}
|
||||
|
||||
protected function importAllDomains(): array {
|
||||
|
||||
try {
|
||||
@@ -119,15 +113,16 @@ class DomainController extends mfBaseController {
|
||||
}
|
||||
|
||||
private function getAllDomains(): array {
|
||||
|
||||
$json = json_decode(file_get_contents('php://input'), true);
|
||||
|
||||
$filters = $json['filters'] ?? [];
|
||||
$order = $json['order'] ?? [];
|
||||
$page = $json['pagination']['page'] ?? 1;
|
||||
$perPage = $json['pagination']['per_page'] ?? 10;
|
||||
|
||||
$domains = DomainModel::getAllDomains($filters, $perPage, $perPage * $page - $perPage);
|
||||
$totalRows = DomainModel::countDomains($filters);
|
||||
$domains = DomainModel::getAllDomains($filters, $perPage, $perPage * $page - $perPage, $order);
|
||||
$filtered_available = DomainModel::countDomains($filters);
|
||||
$totalRows = DomainModel::countDomains([]);
|
||||
|
||||
return [
|
||||
"rows" => $domains,
|
||||
@@ -135,6 +130,7 @@ class DomainController extends mfBaseController {
|
||||
"page" => $page,
|
||||
"total_pages" => ceil($totalRows / $perPage),
|
||||
"per_page" => $perPage,
|
||||
"filtered_available" => intval($filtered_available),
|
||||
"total_rows" => intval($totalRows)
|
||||
]
|
||||
];
|
||||
@@ -156,7 +152,6 @@ class DomainController extends mfBaseController {
|
||||
dns_get_record($domain, DNS_SOA),
|
||||
dns_get_record($domain, DNS_SRV),
|
||||
dns_get_record($domain, DNS_AAAA),
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -93,31 +93,35 @@ class DomainModel {
|
||||
}
|
||||
|
||||
public static function getSqlFilter($filters): string {
|
||||
$sql = isset($filters['domain']) ? self::generateFilterCondition($filters['domain'], "domain") : "";
|
||||
$sql .= isset($filters['crDate']) ? " AND `crDate` = " . $filters['crDate'] : "";
|
||||
$sql .= isset($filters['exDate']) ? " AND `exDate` = " . $filters['exDate'] : "";
|
||||
$sql .= isset($filters['reDate']) ? " AND `reDate` = " . $filters['reDate'] : "";
|
||||
$sql .= isset($filters['upDate']) ? " AND `upDate` = " . $filters['upDate'] : "";
|
||||
$sql .= isset($filters['status']) ? " AND `status` = '" . $filters['status'] . "'" : "";
|
||||
$sql .= isset($filters['transferLock']) && $filters['transferLock'] == 1 ? " AND `transferLock` = true" : "";
|
||||
$sql .= isset($filters['authCode']) ? self::generateFilterCondition($filters['authCode'], "authCode") : "";
|
||||
$sql = isset($filters['domain']) ? Helper::generateFilterCondition($filters['domain'], "domain") : "";
|
||||
$sql .= isset($filters['crDate']) ? Helper::generateFilterCondition($filters['crDate'], "crDate") : "";
|
||||
$sql .= isset($filters['exDate']) ? Helper::generateFilterCondition($filters['exDate'], "exDate") : "";
|
||||
$sql .= isset($filters['reDate']) ? Helper::generateFilterCondition($filters['reDate'], "reDate") : "";
|
||||
$sql .= isset($filters['upDate']) ? Helper::generateFilterCondition($filters['upDate'], "upDate") : "";
|
||||
$sql .= isset($filters['status']) ? Helper::generateFilterCondition($filters['status'], "status") : "";
|
||||
$sql .= isset($filters['transferLock']) ? Helper::generateFilterCondition($filters['transferLock'], "transferLock") : "";
|
||||
$sql .= isset($filters['authCode']) ? Helper::generateFilterCondition($filters['authCode'], "authCode") : "";
|
||||
$sql .= isset($filters['registrant']) && $filters['registrant'] !== 'all' ? " AND `registrant` = " . $filters['registrant'] : "";
|
||||
$sql .= isset($filters['admin']) && $filters['admin'] !== 'all' ? " AND `admin` = " . $filters['admin'] : "";
|
||||
$sql .= isset($filters['tech']) && $filters['tech'] !== 'all' ? " AND `tech` = " . $filters['tech'] : "";
|
||||
$sql .= isset($filters['billing']) && $filters['billing'] !== 'all' ? " AND `billing` = " . $filters['billing'] : "";
|
||||
$sql .= isset($filters['ns']) ? self::generateFilterCondition($filters['ns'], "ns") : "";
|
||||
$sql .= isset($filters['ns']) ? Helper::generateFilterCondition($filters['ns'], "ns") : "";
|
||||
$sql .= isset($filters['pleskId']) ? " AND `pleskId` " . ($filters['pleskId'] === "0" ? "IS NULL" : "IS NOT NULL") : "";
|
||||
return $sql;
|
||||
|
||||
}
|
||||
|
||||
public static function getAllDomains($filters, $limit = null, $offset = 0): array {
|
||||
public static function getAllDomains($filters, $limit = null, $offset = 0, $order = null): array {
|
||||
$db = FronkDB::singleton();
|
||||
|
||||
$sql = "SELECT * FROM `Domain` WHERE 1 " . self::getSqlFilter($filters);
|
||||
$sql .= $order === null || $order['key'] === null ? "" : " ORDER BY `" . $order['key'] . "` " . $order['order'];
|
||||
$sql .= $limit === null ? "" : " LIMIT " . $limit . " OFFSET " . $offset;
|
||||
|
||||
$result = $db->query($sql);
|
||||
$rows = [];
|
||||
while ($row = $result->fetch_assoc()) {
|
||||
$row['pleskId'] = $row['pleskId'] === null ? 0 : 1;
|
||||
$rows[] = new DomainModel($row);
|
||||
}
|
||||
|
||||
|
||||
@@ -54,19 +54,22 @@ class HistoricTicketController extends mfBaseController {
|
||||
$json = json_decode(file_get_contents('php://input'), true);
|
||||
|
||||
$filters = $json['filters'] ?? [];
|
||||
$order = $json['order'] ?? [];
|
||||
$page = $json['pagination']['page'] ?? 1;
|
||||
$perPage = $json['pagination']['per_page'] ?? 10;
|
||||
|
||||
$historicTickets = HistoricTicketModel::getAllHistoricTickets($filters, $perPage, ($page - 1) * $perPage);
|
||||
$total = HistoricTicketModel::countHistoricTickets($filters);
|
||||
$historicTickets = HistoricTicketModel::getAllHistoricTickets($filters, $perPage, ($page - 1) * $perPage, $order);
|
||||
$filtered_available = HistoricTicketModel::countHistoricTickets($filters);
|
||||
$totalRows = HistoricTicketModel::countHistoricTickets([]);
|
||||
|
||||
return [
|
||||
"rows" => $historicTickets,
|
||||
"pagination" => [
|
||||
"page" => $page,
|
||||
"total_pages" => ceil($total / $perPage),
|
||||
"total_pages" => ceil($totalRows / $perPage),
|
||||
"per_page" => $perPage,
|
||||
"total_rows" => intval($total)
|
||||
"filtered_available" => intval($filtered_available),
|
||||
"total_rows" => intval($totalRows)
|
||||
]
|
||||
];
|
||||
}
|
||||
@@ -88,16 +91,6 @@ class HistoricTicketController extends mfBaseController {
|
||||
];
|
||||
}
|
||||
|
||||
$rows = HistoricTicketModel::findTicket($query);
|
||||
|
||||
return [
|
||||
"rows" => $rows,
|
||||
"pagination" => [
|
||||
"page" => 1,
|
||||
"total_pages" => 1,
|
||||
"per_page" => count($rows),
|
||||
"total_rows" => count($rows)
|
||||
]
|
||||
];
|
||||
return HistoricTicketModel::findTicket($query);
|
||||
}
|
||||
}
|
||||
@@ -88,11 +88,13 @@ class HistoricTicketModel {
|
||||
return $sql;
|
||||
}
|
||||
|
||||
public static function getAllHistoricTickets($filters, $limit = null, $offset = 0): array {
|
||||
public static function getAllHistoricTickets($filters, $limit = null, $offset = 0, $order = null): array {
|
||||
$db = FronkDB::singleton();
|
||||
$sql = "SELECT * FROM `HistoricTicket` WHERE 1 " . self::getSqlFilter($filters) . " ORDER BY `ticket_number` DESC";
|
||||
$sql = "SELECT * FROM `HistoricTicket` WHERE 1 " . self::getSqlFilter($filters);
|
||||
$sql .= $order === null || $order['key'] === null ? " ORDER BY `ticket_number` DESC " : " ORDER BY `" . $order['key'] . "` " . $order['order'];
|
||||
$sql .= $limit === null ? "" : " LIMIT " . $limit . " OFFSET " . $offset;
|
||||
|
||||
// die($sql);
|
||||
$result = $db->query($sql);
|
||||
$rows = [];
|
||||
while ($row = $result->fetch_assoc()) {
|
||||
@@ -162,7 +164,7 @@ class HistoricTicketModel {
|
||||
"table_entry" => "ticket",
|
||||
"ticket_id" => $ticket["id"],
|
||||
"ticket_number" => $ticket["ticket_number"],
|
||||
"ctime" => $ticket["ctime"],
|
||||
"ctime" => intval($ticket["ctime"]),
|
||||
"ticket_subject" => $ticket["subject"],
|
||||
];
|
||||
}
|
||||
@@ -173,7 +175,7 @@ class HistoricTicketModel {
|
||||
"ticket_id" => $message["ticket_id"],
|
||||
"ticket_number" => $message["ticket_number"],
|
||||
"ticket_subject" => $message["subject"],
|
||||
"ctime" => $message["ctime"],
|
||||
"ctime" => intval($message["ctime"]),
|
||||
"ticket_message" => $message["content"],
|
||||
];
|
||||
}
|
||||
|
||||
@@ -69,11 +69,13 @@ class VoiceCallHistoryController extends mfBaseController {
|
||||
$json = json_decode(file_get_contents('php://input'), true);
|
||||
|
||||
$filters = $json['filters'] ?? [];
|
||||
$order = $json['order'] ?? [];
|
||||
$page = $json['pagination']['page'] ?? 1;
|
||||
$perPage = $json['pagination']['per_page'] ?? 10;
|
||||
|
||||
$calls = VoiceCallHistoryModel::getVoiceCallHistory($filters, $perPage, $perPage * $page - $perPage);
|
||||
$totalRows = VoiceCallHistoryModel::countVoiceCallHistory($filters);
|
||||
$calls = VoiceCallHistoryModel::getVoiceCallHistory($filters, $perPage, $perPage * $page - $perPage, $order);
|
||||
$filtered_available = VoiceCallHistoryModel::countVoiceCallHistory($filters);
|
||||
$totalRows = VoiceCallHistoryModel::countVoiceCallHistory([]);
|
||||
|
||||
return [
|
||||
"rows" => $calls,
|
||||
@@ -81,11 +83,9 @@ class VoiceCallHistoryController extends mfBaseController {
|
||||
"page" => $page,
|
||||
"total_pages" => ceil($totalRows / $perPage),
|
||||
"per_page" => $perPage,
|
||||
"filtered_available" => intval($filtered_available),
|
||||
"total_rows" => intval($totalRows)
|
||||
]
|
||||
];
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
<?php
|
||||
|
||||
class VoiceCallHistoryModel {
|
||||
public $uid;
|
||||
public $voice_account;
|
||||
@@ -18,28 +17,6 @@ class VoiceCallHistoryModel {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate SQL Filter condition (space separated) for a given column.
|
||||
*
|
||||
* @param string|null $filterValue The filter value to match against.
|
||||
* @param string $columnName The name of the column in the database table.
|
||||
* @return string The SQL condition generated based on the filter value and column name.
|
||||
*/
|
||||
public static function generateFilterCondition(?string $filterValue, string $columnName): string {
|
||||
$sql = "";
|
||||
|
||||
if ($filterValue === "0" || $filterValue === "1") {
|
||||
$sql .= " AND `$columnName` = " . $filterValue;
|
||||
} else if (!empty($filterValue)) {
|
||||
$filterItems = explode(" ", $filterValue);
|
||||
foreach ($filterItems as $item) {
|
||||
$sql .= " AND `$columnName` LIKE '%" . $item . "%'";
|
||||
}
|
||||
}
|
||||
|
||||
return $sql;
|
||||
}
|
||||
|
||||
//TODO: combine these two functions into one
|
||||
public static function importCallsFromKolmisoft($callHistory): array {
|
||||
{
|
||||
@@ -114,22 +91,24 @@ class VoiceCallHistoryModel {
|
||||
if (isset($filters['start']['from']) && isset($filters['start']['to'])) {
|
||||
$sql = " AND `start` >= FROM_UNIXTIME(" . $filters['start']['from'] . ") AND `start` <= FROM_UNIXTIME(" . $filters['start']['to'] . ")";
|
||||
}
|
||||
$sql .= isset($filters['uid']) ? self::generateFilterCondition($filters['uid'], "uid") : "";
|
||||
$sql .= isset($filters['voice_account']) ? self::generateFilterCondition($filters['voice_account'], "voice_account") : "";
|
||||
$sql .= isset($filters['uid']) ? Helper::generateFilterCondition($filters['uid'], "uid") : "";
|
||||
$sql .= isset($filters['voice_account']) ? Helper::generateFilterCondition($filters['voice_account'], "voice_account") : "";
|
||||
$sql .= isset($filters['end']) ? " AND `start` <= '" . $filters['end'] . "'" : "";
|
||||
$sql .= isset($filters['source']) ? self::generateFilterCondition($filters['source'], "source") : "";
|
||||
$sql .= isset($filters['destination']) ? self::generateFilterCondition($filters['destination'], "destination") : "";
|
||||
$sql .= isset($filters['billable']) ? self::generateFilterCondition($filters['billable'], "billable") : "";
|
||||
$sql .= isset($filters['duration']) ? self::generateFilterCondition($filters['duration'], "duration") : "";
|
||||
$sql .= isset($filters['source']) ? Helper::generateFilterCondition($filters['source'], "source") : "";
|
||||
$sql .= isset($filters['destination']) ? Helper::generateFilterCondition($filters['destination'], "destination") : "";
|
||||
$sql .= isset($filters['billable']) ? Helper::generateFilterCondition($filters['billable'], "billable") : "";
|
||||
$sql .= isset($filters['duration']) ? Helper::generateFilterCondition($filters['duration'], "duration") : "";
|
||||
|
||||
return $sql . " ORDER BY `start` DESC";
|
||||
return $sql;
|
||||
}
|
||||
|
||||
public static function getVoiceCallHistory($filters, $limit = null, $offset = 0): array {
|
||||
public static function getVoiceCallHistory($filters, $limit = null, $offset = 0, $order = null) {
|
||||
$db = FronkDB::singleton();
|
||||
$sql = "SELECT * FROM `VoiceCallHistory` WHERE 1 " . self::getSqlFilter($filters);
|
||||
$sql .= $order === null || $order['key'] === null ? " ORDER BY `start` DESC" : " ORDER BY `" . $order['key'] . "` " . $order['order'];
|
||||
$sql .= $limit === null ? "" : " LIMIT " . $limit . " OFFSET " . $offset;
|
||||
|
||||
// die($sql);
|
||||
$result = $db->query($sql);
|
||||
$rows = [];
|
||||
while ($row = $result->fetch_assoc()) {
|
||||
|
||||
30
lib/Helper/Helper.php
Normal file
30
lib/Helper/Helper.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
class Helper {
|
||||
/**
|
||||
* Generate SQL Filter condition (space separated) for a given column.
|
||||
*
|
||||
* @param string|null $filterValue The filter value to match against.
|
||||
* @param string $columnName The name of the column in the database table.
|
||||
* @return string The SQL condition generated based on the filter value and column name.
|
||||
* @noinspection PhpMissingParamTypeInspection
|
||||
*/
|
||||
public static function generateFilterCondition($filterValue, string $columnName): string {
|
||||
$sql = "";
|
||||
|
||||
if (is_array($filterValue)) {
|
||||
if (isset($filterValue['from']) && isset($filterValue['to'])) {
|
||||
$sql = " AND `$columnName` >= " . $filterValue['from'] . " AND `$columnName` <= " . $filterValue['to'];
|
||||
}
|
||||
} else if ($filterValue === "0" || $filterValue === "1") {
|
||||
$sql .= " AND `$columnName` = " . $filterValue;
|
||||
} else if (!empty($filterValue)) {
|
||||
$filterItems = explode(" ", $filterValue);
|
||||
foreach ($filterItems as $item) {
|
||||
$sql .= " AND `$columnName` LIKE '%" . $item . "%'";
|
||||
}
|
||||
}
|
||||
|
||||
return $sql;
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
{
|
||||
"name": "daterangepicker",
|
||||
"main": [
|
||||
"daterangepicker.js",
|
||||
"daterangepicker.css"
|
||||
],
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"test",
|
||||
"tests",
|
||||
"moment.js",
|
||||
"moment.min.js"
|
||||
],
|
||||
"dependencies": {
|
||||
"jquery": "1.9.1 - 3",
|
||||
"moment": ">=2.9.0"
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @version: 3.0.5
|
||||
* @version: 3.1
|
||||
* @author: Dan Grossman http://www.dangrossman.info/
|
||||
* @copyright: Copyright (c) 2012-2019 Dan Grossman. All rights reserved.
|
||||
* @license: Licensed under the MIT license. See http://www.opensource.org/licenses/mit-license.php
|
||||
@@ -11,7 +11,7 @@
|
||||
// AMD. Make globaly available as well
|
||||
define(['moment', 'jquery'], function (moment, jquery) {
|
||||
if (!jquery.fn) jquery.fn = {}; // webpack server rendering
|
||||
if (typeof moment !== 'function' && moment.default) moment = moment.default
|
||||
if (typeof moment !== 'function' && moment.hasOwnProperty('default')) moment = moment['default']
|
||||
return factory(moment, jquery);
|
||||
});
|
||||
} else if (typeof module === 'object' && module.exports) {
|
||||
@@ -386,7 +386,7 @@
|
||||
this.container.find('.drp-calendar.left').addClass('single');
|
||||
this.container.find('.drp-calendar.left').show();
|
||||
this.container.find('.drp-calendar.right').hide();
|
||||
if (!this.timePicker) {
|
||||
if (!this.timePicker && this.autoApply) {
|
||||
this.container.addClass('auto-apply');
|
||||
}
|
||||
}
|
||||
@@ -417,14 +417,14 @@
|
||||
.on('mouseenter.daterangepicker', 'td.available', $.proxy(this.hoverDate, this))
|
||||
.on('change.daterangepicker', 'select.yearselect', $.proxy(this.monthOrYearChanged, this))
|
||||
.on('change.daterangepicker', 'select.monthselect', $.proxy(this.monthOrYearChanged, this))
|
||||
.on('change.daterangepicker', 'select.hourselect,select.minuteselect,select.secondselect,select.ampmselect', $.proxy(this.timeChanged, this))
|
||||
.on('change.daterangepicker', 'select.hourselect,select.minuteselect,select.secondselect,select.ampmselect', $.proxy(this.timeChanged, this));
|
||||
|
||||
this.container.find('.ranges')
|
||||
.on('click.daterangepicker', 'li', $.proxy(this.clickRange, this))
|
||||
.on('click.daterangepicker', 'li', $.proxy(this.clickRange, this));
|
||||
|
||||
this.container.find('.drp-buttons')
|
||||
.on('click.daterangepicker', 'button.applyBtn', $.proxy(this.clickApply, this))
|
||||
.on('click.daterangepicker', 'button.cancelBtn', $.proxy(this.clickCancel, this))
|
||||
.on('click.daterangepicker', 'button.cancelBtn', $.proxy(this.clickCancel, this));
|
||||
|
||||
if (this.element.is('input') || this.element.is('button')) {
|
||||
this.element.on({
|
||||
@@ -526,9 +526,9 @@
|
||||
this.renderTimePicker('left');
|
||||
this.renderTimePicker('right');
|
||||
if (!this.endDate) {
|
||||
this.container.find('.right .calendar-time select').attr('disabled', 'disabled').addClass('disabled');
|
||||
this.container.find('.right .calendar-time select').prop('disabled', true).addClass('disabled');
|
||||
} else {
|
||||
this.container.find('.right .calendar-time select').removeAttr('disabled').removeClass('disabled');
|
||||
this.container.find('.right .calendar-time select').prop('disabled', false).removeClass('disabled');
|
||||
}
|
||||
}
|
||||
if (this.endDate)
|
||||
@@ -1014,16 +1014,18 @@
|
||||
updateFormInputs: function() {
|
||||
|
||||
if (this.singleDatePicker || (this.endDate && (this.startDate.isBefore(this.endDate) || this.startDate.isSame(this.endDate)))) {
|
||||
this.container.find('button.applyBtn').removeAttr('disabled');
|
||||
this.container.find('button.applyBtn').prop('disabled', false);
|
||||
} else {
|
||||
this.container.find('button.applyBtn').attr('disabled', 'disabled');
|
||||
this.container.find('button.applyBtn').prop('disabled', true);
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
move: function() {
|
||||
var parentOffset = { top: 0, left: 0 },
|
||||
containerTop;
|
||||
containerTop,
|
||||
drops = this.drops;
|
||||
|
||||
var parentRightEdge = $(window).width();
|
||||
if (!this.parentEl.is('body')) {
|
||||
parentOffset = {
|
||||
@@ -1033,10 +1035,21 @@
|
||||
parentRightEdge = this.parentEl[0].clientWidth + this.parentEl.offset().left;
|
||||
}
|
||||
|
||||
if (this.drops == 'up')
|
||||
containerTop = this.element.offset().top - this.container.outerHeight() - parentOffset.top;
|
||||
else
|
||||
switch (drops) {
|
||||
case 'auto':
|
||||
containerTop = this.element.offset().top + this.element.outerHeight() - parentOffset.top;
|
||||
if (containerTop + this.container.outerHeight() >= this.parentEl[0].scrollHeight) {
|
||||
containerTop = this.element.offset().top - this.container.outerHeight() - parentOffset.top;
|
||||
drops = 'up';
|
||||
}
|
||||
break;
|
||||
case 'up':
|
||||
containerTop = this.element.offset().top - this.container.outerHeight() - parentOffset.top;
|
||||
break;
|
||||
default:
|
||||
containerTop = this.element.offset().top + this.element.outerHeight() - parentOffset.top;
|
||||
break;
|
||||
}
|
||||
|
||||
// Force the container to it's actual width
|
||||
this.container.css({
|
||||
@@ -1046,7 +1059,7 @@
|
||||
});
|
||||
var containerWidth = this.container.outerWidth();
|
||||
|
||||
this.container[this.drops == 'up' ? 'addClass' : 'removeClass']('drop-up');
|
||||
this.container.toggleClass('drop-up', drops == 'up');
|
||||
|
||||
if (this.opens == 'left') {
|
||||
var containerRight = parentRightEdge - this.element.offset().left - this.element.outerWidth();
|
||||
@@ -1341,7 +1354,7 @@
|
||||
|
||||
if (this.singleDatePicker) {
|
||||
this.setEndDate(this.startDate);
|
||||
if (!this.timePicker)
|
||||
if (!this.timePicker && this.autoApply)
|
||||
this.clickApply();
|
||||
}
|
||||
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 157 KiB |
@@ -1,210 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html dir="ltr" lang="en-US">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>A date range picker for Bootstrap</title>
|
||||
<link href="http://netdna.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" type="text/css" media="all" href="../../daterangepicker.css" />
|
||||
<style type="text/css">
|
||||
.demo { position: relative; }
|
||||
.demo i {
|
||||
position: absolute; bottom: 10px; right: 24px; top: auto; cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body style="margin: 60px 0">
|
||||
|
||||
<div class="container">
|
||||
|
||||
<h1 style="margin: 0 0 20px 0">Configuration Builder</h1>
|
||||
|
||||
<div class="well configurator">
|
||||
|
||||
<form>
|
||||
<div class="row">
|
||||
|
||||
<div class="col-md-4">
|
||||
|
||||
<div class="form-group">
|
||||
<label for="parentEl">parentEl</label>
|
||||
<input type="text" class="form-control" id="parentEl" value="" placeholder="body">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="startDate">startDate</label>
|
||||
<input type="text" class="form-control" id="startDate" value="07/01/2015">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="endDate">endDate</label>
|
||||
<input type="text" class="form-control" id="endDate" value="07/15/2015">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="minDate">minDate</label>
|
||||
<input type="text" class="form-control" id="minDate" value="" placeholder="MM/DD/YYYY">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="maxDate">maxDate</label>
|
||||
<input type="text" class="form-control" id="maxDate" value="" placeholder="MM/DD/YYYY">
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" id="autoApply"> autoApply
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" id="singleDatePicker"> singleDatePicker
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" id="showDropdowns"> showDropdowns
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" id="showWeekNumbers"> showWeekNumbers
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" id="showISOWeekNumbers"> showISOWeekNumbers
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" id="timePicker"> timePicker
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" id="timePicker24Hour"> timePicker24Hour
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="timePickerIncrement">timePickerIncrement (in minutes)</label>
|
||||
<input type="text" class="form-control" id="timePickerIncrement" value="1">
|
||||
</div>
|
||||
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" id="timePickerSeconds"> timePickerSeconds
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" id="dateLimit"> dateLimit (with example date range span)
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" id="ranges"> ranges (with example predefined ranges)
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" id="locale"> locale (with example settings)
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" id="linkedCalendars" checked="checked"> linkedCalendars
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" id="autoUpdateInput" checked="checked"> autoUpdateInput
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" id="alwaysShowCalendars"> alwaysShowCalendars
|
||||
</label>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
|
||||
<div class="form-group">
|
||||
<label for="opens">opens</label>
|
||||
<select id="opens" class="form-control">
|
||||
<option value="right" selected>right</option>
|
||||
<option value="left">left</option>
|
||||
<option value="center">center</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="drops">drops</label>
|
||||
<select id="drops" class="form-control">
|
||||
<option value="down" selected>down</option>
|
||||
<option value="up">up</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="buttonClasses">buttonClasses</label>
|
||||
<input type="text" class="form-control" id="buttonClasses" value="btn btn-sm">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="applyClass">applyClass</label>
|
||||
<input type="text" class="form-control" id="applyClass" value="btn-success">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="cancelClass">cancelClass</label>
|
||||
<input type="text" class="form-control" id="cancelClass" value="btn-default">
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
|
||||
<div class="col-md-4 col-md-offset-2 demo">
|
||||
<h4>Your Date Range Picker</h4>
|
||||
<input type="text" id="config-demo" class="form-control">
|
||||
<i class="glyphicon glyphicon-calendar fa fa-calendar"></i>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<h4>Configuration</h4>
|
||||
|
||||
<div class="well">
|
||||
<textarea id="config-text" style="height: 300px; width: 100%; padding: 10px"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<script type="text/javascript" src="require.js" data-main="main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,141 +0,0 @@
|
||||
requirejs.config({
|
||||
"paths": {
|
||||
"jquery": "https://code.jquery.com/jquery-1.11.3.min",
|
||||
"moment": "../../moment",
|
||||
"daterangepicker": "../../daterangepicker"
|
||||
}
|
||||
});
|
||||
|
||||
requirejs(['jquery', 'moment', 'daterangepicker'] , function ($, moment) {
|
||||
$(document).ready(function() {
|
||||
|
||||
$('#config-text').keyup(function() {
|
||||
eval($(this).val());
|
||||
});
|
||||
|
||||
$('.configurator input, .configurator select').change(function() {
|
||||
updateConfig();
|
||||
});
|
||||
|
||||
$('.demo i').click(function() {
|
||||
$(this).parent().find('input').click();
|
||||
});
|
||||
|
||||
$('#startDate').daterangepicker({
|
||||
singleDatePicker: true,
|
||||
startDate: moment().subtract(6, 'days')
|
||||
});
|
||||
|
||||
$('#endDate').daterangepicker({
|
||||
singleDatePicker: true,
|
||||
startDate: moment()
|
||||
});
|
||||
|
||||
updateConfig();
|
||||
|
||||
function updateConfig() {
|
||||
var options = {};
|
||||
|
||||
if ($('#singleDatePicker').is(':checked'))
|
||||
options.singleDatePicker = true;
|
||||
|
||||
if ($('#showDropdowns').is(':checked'))
|
||||
options.showDropdowns = true;
|
||||
|
||||
if ($('#showWeekNumbers').is(':checked'))
|
||||
options.showWeekNumbers = true;
|
||||
|
||||
if ($('#showISOWeekNumbers').is(':checked'))
|
||||
options.showISOWeekNumbers = true;
|
||||
|
||||
if ($('#timePicker').is(':checked'))
|
||||
options.timePicker = true;
|
||||
|
||||
if ($('#timePicker24Hour').is(':checked'))
|
||||
options.timePicker24Hour = true;
|
||||
|
||||
if ($('#timePickerIncrement').val().length && $('#timePickerIncrement').val() != 1)
|
||||
options.timePickerIncrement = parseInt($('#timePickerIncrement').val(), 10);
|
||||
|
||||
if ($('#timePickerSeconds').is(':checked'))
|
||||
options.timePickerSeconds = true;
|
||||
|
||||
if ($('#autoApply').is(':checked'))
|
||||
options.autoApply = true;
|
||||
|
||||
if ($('#dateLimit').is(':checked'))
|
||||
options.dateLimit = { days: 7 };
|
||||
|
||||
if ($('#ranges').is(':checked')) {
|
||||
options.ranges = {
|
||||
'Today': [moment(), moment()],
|
||||
'Yesterday': [moment().subtract(1, 'days'), moment().subtract(1, 'days')],
|
||||
'Last 7 Days': [moment().subtract(6, 'days'), moment()],
|
||||
'Last 30 Days': [moment().subtract(29, 'days'), moment()],
|
||||
'This Month': [moment().startOf('month'), moment().endOf('month')],
|
||||
'Last Month': [moment().subtract(1, 'month').startOf('month'), moment().subtract(1, 'month').endOf('month')]
|
||||
};
|
||||
}
|
||||
|
||||
if ($('#locale').is(':checked')) {
|
||||
options.locale = {
|
||||
format: 'MM/DD/YYYY HH:mm',
|
||||
separator: ' - ',
|
||||
applyLabel: 'Apply',
|
||||
cancelLabel: 'Cancel',
|
||||
fromLabel: 'From',
|
||||
toLabel: 'To',
|
||||
customRangeLabel: 'Custom',
|
||||
daysOfWeek: ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr','Sa'],
|
||||
monthNames: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
|
||||
firstDay: 1
|
||||
};
|
||||
}
|
||||
|
||||
if (!$('#linkedCalendars').is(':checked'))
|
||||
options.linkedCalendars = false;
|
||||
|
||||
if (!$('#autoUpdateInput').is(':checked'))
|
||||
options.autoUpdateInput = false;
|
||||
|
||||
if ($('#alwaysShowCalendars').is(':checked'))
|
||||
options.alwaysShowCalendars = true;
|
||||
|
||||
if ($('#parentEl').val().length)
|
||||
options.parentEl = $('#parentEl').val();
|
||||
|
||||
if ($('#startDate').val().length)
|
||||
options.startDate = $('#startDate').val();
|
||||
|
||||
if ($('#endDate').val().length)
|
||||
options.endDate = $('#endDate').val();
|
||||
|
||||
if ($('#minDate').val().length)
|
||||
options.minDate = $('#minDate').val();
|
||||
|
||||
if ($('#maxDate').val().length)
|
||||
options.maxDate = $('#maxDate').val();
|
||||
|
||||
if ($('#opens').val().length && $('#opens').val() != 'right')
|
||||
options.opens = $('#opens').val();
|
||||
|
||||
if ($('#drops').val().length && $('#drops').val() != 'down')
|
||||
options.drops = $('#drops').val();
|
||||
|
||||
if ($('#buttonClasses').val().length && $('#buttonClasses').val() != 'btn btn-sm')
|
||||
options.buttonClasses = $('#buttonClasses').val();
|
||||
|
||||
if ($('#applyClass').val().length && $('#applyClass').val() != 'btn-success')
|
||||
options.applyClass = $('#applyClass').val();
|
||||
|
||||
if ($('#cancelClass').val().length && $('#cancelClass').val() != 'btn-default')
|
||||
options.cancelClass = $('#cancelClass').val();
|
||||
|
||||
$('#config-text').val("$('#demo').daterangepicker(" + JSON.stringify(options, null, ' ') + ", function(start, end, label) {\n console.log(\"New date range selected: ' + start.format('YYYY-MM-DD') + ' to ' + end.format('YYYY-MM-DD') + ' (predefined range: ' + label + ')\");\n});");
|
||||
|
||||
$('#config-demo').daterangepicker(options, function(start, end, label) { console.log('New date range selected: ' + start.format('YYYY-MM-DD') + ' to ' + end.format('YYYY-MM-DD') + ' (predefined range: ' + label + ')'); });
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
});
|
||||
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
RequireJS 2.2.0 Copyright jQuery Foundation and other contributors.
|
||||
Released under MIT license, http://github.com/requirejs/requirejs/LICENSE
|
||||
*/
|
||||
var requirejs,require,define;
|
||||
(function(ga){function ka(b,c,d,g){return g||""}function K(b){return"[object Function]"===Q.call(b)}function L(b){return"[object Array]"===Q.call(b)}function y(b,c){if(b){var d;for(d=0;d<b.length&&(!b[d]||!c(b[d],d,b));d+=1);}}function X(b,c){if(b){var d;for(d=b.length-1;-1<d&&(!b[d]||!c(b[d],d,b));--d);}}function x(b,c){return la.call(b,c)}function e(b,c){return x(b,c)&&b[c]}function D(b,c){for(var d in b)if(x(b,d)&&c(b[d],d))break}function Y(b,c,d,g){c&&D(c,function(c,e){if(d||!x(b,e))!g||"object"!==
|
||||
typeof c||!c||L(c)||K(c)||c instanceof RegExp?b[e]=c:(b[e]||(b[e]={}),Y(b[e],c,d,g))});return b}function z(b,c){return function(){return c.apply(b,arguments)}}function ha(b){throw b;}function ia(b){if(!b)return b;var c=ga;y(b.split("."),function(b){c=c[b]});return c}function F(b,c,d,g){c=Error(c+"\nhttp://requirejs.org/docs/errors.html#"+b);c.requireType=b;c.requireModules=g;d&&(c.originalError=d);return c}function ma(b){function c(a,n,b){var h,k,f,c,d,l,g,r;n=n&&n.split("/");var q=p.map,m=q&&q["*"];
|
||||
if(a){a=a.split("/");k=a.length-1;p.nodeIdCompat&&U.test(a[k])&&(a[k]=a[k].replace(U,""));"."===a[0].charAt(0)&&n&&(k=n.slice(0,n.length-1),a=k.concat(a));k=a;for(f=0;f<k.length;f++)c=k[f],"."===c?(k.splice(f,1),--f):".."===c&&0!==f&&(1!==f||".."!==k[2])&&".."!==k[f-1]&&0<f&&(k.splice(f-1,2),f-=2);a=a.join("/")}if(b&&q&&(n||m)){k=a.split("/");f=k.length;a:for(;0<f;--f){d=k.slice(0,f).join("/");if(n)for(c=n.length;0<c;--c)if(b=e(q,n.slice(0,c).join("/")))if(b=e(b,d)){h=b;l=f;break a}!g&&m&&e(m,d)&&
|
||||
(g=e(m,d),r=f)}!h&&g&&(h=g,l=r);h&&(k.splice(0,l,h),a=k.join("/"))}return(h=e(p.pkgs,a))?h:a}function d(a){E&&y(document.getElementsByTagName("script"),function(n){if(n.getAttribute("data-requiremodule")===a&&n.getAttribute("data-requirecontext")===l.contextName)return n.parentNode.removeChild(n),!0})}function m(a){var n=e(p.paths,a);if(n&&L(n)&&1<n.length)return n.shift(),l.require.undef(a),l.makeRequire(null,{skipMap:!0})([a]),!0}function r(a){var n,b=a?a.indexOf("!"):-1;-1<b&&(n=a.substring(0,
|
||||
b),a=a.substring(b+1,a.length));return[n,a]}function q(a,n,b,h){var k,f,d=null,g=n?n.name:null,p=a,q=!0,m="";a||(q=!1,a="_@r"+(Q+=1));a=r(a);d=a[0];a=a[1];d&&(d=c(d,g,h),f=e(v,d));a&&(d?m=f&&f.normalize?f.normalize(a,function(a){return c(a,g,h)}):-1===a.indexOf("!")?c(a,g,h):a:(m=c(a,g,h),a=r(m),d=a[0],m=a[1],b=!0,k=l.nameToUrl(m)));b=!d||f||b?"":"_unnormalized"+(T+=1);return{prefix:d,name:m,parentMap:n,unnormalized:!!b,url:k,originalName:p,isDefine:q,id:(d?d+"!"+m:m)+b}}function u(a){var b=a.id,
|
||||
c=e(t,b);c||(c=t[b]=new l.Module(a));return c}function w(a,b,c){var h=a.id,k=e(t,h);if(!x(v,h)||k&&!k.defineEmitComplete)if(k=u(a),k.error&&"error"===b)c(k.error);else k.on(b,c);else"defined"===b&&c(v[h])}function A(a,b){var c=a.requireModules,h=!1;if(b)b(a);else if(y(c,function(b){if(b=e(t,b))b.error=a,b.events.error&&(h=!0,b.emit("error",a))}),!h)g.onError(a)}function B(){V.length&&(y(V,function(a){var b=a[0];"string"===typeof b&&(l.defQueueMap[b]=!0);G.push(a)}),V=[])}function C(a){delete t[a];
|
||||
delete Z[a]}function J(a,b,c){var h=a.map.id;a.error?a.emit("error",a.error):(b[h]=!0,y(a.depMaps,function(h,f){var d=h.id,g=e(t,d);!g||a.depMatched[f]||c[d]||(e(b,d)?(a.defineDep(f,v[d]),a.check()):J(g,b,c))}),c[h]=!0)}function H(){var a,b,c=(a=1E3*p.waitSeconds)&&l.startTime+a<(new Date).getTime(),h=[],k=[],f=!1,g=!0;if(!aa){aa=!0;D(Z,function(a){var l=a.map,e=l.id;if(a.enabled&&(l.isDefine||k.push(a),!a.error))if(!a.inited&&c)m(e)?f=b=!0:(h.push(e),d(e));else if(!a.inited&&a.fetched&&l.isDefine&&
|
||||
(f=!0,!l.prefix))return g=!1});if(c&&h.length)return a=F("timeout","Load timeout for modules: "+h,null,h),a.contextName=l.contextName,A(a);g&&y(k,function(a){J(a,{},{})});c&&!b||!f||!E&&!ja||ba||(ba=setTimeout(function(){ba=0;H()},50));aa=!1}}function I(a){x(v,a[0])||u(q(a[0],null,!0)).init(a[1],a[2])}function O(a){a=a.currentTarget||a.srcElement;var b=l.onScriptLoad;a.detachEvent&&!ca?a.detachEvent("onreadystatechange",b):a.removeEventListener("load",b,!1);b=l.onScriptError;a.detachEvent&&!ca||a.removeEventListener("error",
|
||||
b,!1);return{node:a,id:a&&a.getAttribute("data-requiremodule")}}function P(){var a;for(B();G.length;){a=G.shift();if(null===a[0])return A(F("mismatch","Mismatched anonymous define() module: "+a[a.length-1]));I(a)}l.defQueueMap={}}var aa,da,l,R,ba,p={waitSeconds:7,baseUrl:"./",paths:{},bundles:{},pkgs:{},shim:{},config:{}},t={},Z={},ea={},G=[],v={},W={},fa={},Q=1,T=1;R={require:function(a){return a.require?a.require:a.require=l.makeRequire(a.map)},exports:function(a){a.usingExports=!0;if(a.map.isDefine)return a.exports?
|
||||
v[a.map.id]=a.exports:a.exports=v[a.map.id]={}},module:function(a){return a.module?a.module:a.module={id:a.map.id,uri:a.map.url,config:function(){return e(p.config,a.map.id)||{}},exports:a.exports||(a.exports={})}}};da=function(a){this.events=e(ea,a.id)||{};this.map=a;this.shim=e(p.shim,a.id);this.depExports=[];this.depMaps=[];this.depMatched=[];this.pluginMaps={};this.depCount=0};da.prototype={init:function(a,b,c,h){h=h||{};if(!this.inited){this.factory=b;if(c)this.on("error",c);else this.events.error&&
|
||||
(c=z(this,function(a){this.emit("error",a)}));this.depMaps=a&&a.slice(0);this.errback=c;this.inited=!0;this.ignore=h.ignore;h.enabled||this.enabled?this.enable():this.check()}},defineDep:function(a,b){this.depMatched[a]||(this.depMatched[a]=!0,--this.depCount,this.depExports[a]=b)},fetch:function(){if(!this.fetched){this.fetched=!0;l.startTime=(new Date).getTime();var a=this.map;if(this.shim)l.makeRequire(this.map,{enableBuildCallback:!0})(this.shim.deps||[],z(this,function(){return a.prefix?this.callPlugin():
|
||||
this.load()}));else return a.prefix?this.callPlugin():this.load()}},load:function(){var a=this.map.url;W[a]||(W[a]=!0,l.load(this.map.id,a))},check:function(){if(this.enabled&&!this.enabling){var a,b,c=this.map.id;b=this.depExports;var h=this.exports,k=this.factory;if(!this.inited)x(l.defQueueMap,c)||this.fetch();else if(this.error)this.emit("error",this.error);else if(!this.defining){this.defining=!0;if(1>this.depCount&&!this.defined){if(K(k)){if(this.events.error&&this.map.isDefine||g.onError!==
|
||||
ha)try{h=l.execCb(c,k,b,h)}catch(d){a=d}else h=l.execCb(c,k,b,h);this.map.isDefine&&void 0===h&&((b=this.module)?h=b.exports:this.usingExports&&(h=this.exports));if(a)return a.requireMap=this.map,a.requireModules=this.map.isDefine?[this.map.id]:null,a.requireType=this.map.isDefine?"define":"require",A(this.error=a)}else h=k;this.exports=h;if(this.map.isDefine&&!this.ignore&&(v[c]=h,g.onResourceLoad)){var f=[];y(this.depMaps,function(a){f.push(a.normalizedMap||a)});g.onResourceLoad(l,this.map,f)}C(c);
|
||||
this.defined=!0}this.defining=!1;this.defined&&!this.defineEmitted&&(this.defineEmitted=!0,this.emit("defined",this.exports),this.defineEmitComplete=!0)}}},callPlugin:function(){var a=this.map,b=a.id,d=q(a.prefix);this.depMaps.push(d);w(d,"defined",z(this,function(h){var k,f,d=e(fa,this.map.id),M=this.map.name,r=this.map.parentMap?this.map.parentMap.name:null,m=l.makeRequire(a.parentMap,{enableBuildCallback:!0});if(this.map.unnormalized){if(h.normalize&&(M=h.normalize(M,function(a){return c(a,r,!0)})||
|
||||
""),f=q(a.prefix+"!"+M,this.map.parentMap),w(f,"defined",z(this,function(a){this.map.normalizedMap=f;this.init([],function(){return a},null,{enabled:!0,ignore:!0})})),h=e(t,f.id)){this.depMaps.push(f);if(this.events.error)h.on("error",z(this,function(a){this.emit("error",a)}));h.enable()}}else d?(this.map.url=l.nameToUrl(d),this.load()):(k=z(this,function(a){this.init([],function(){return a},null,{enabled:!0})}),k.error=z(this,function(a){this.inited=!0;this.error=a;a.requireModules=[b];D(t,function(a){0===
|
||||
a.map.id.indexOf(b+"_unnormalized")&&C(a.map.id)});A(a)}),k.fromText=z(this,function(h,c){var d=a.name,f=q(d),M=S;c&&(h=c);M&&(S=!1);u(f);x(p.config,b)&&(p.config[d]=p.config[b]);try{g.exec(h)}catch(e){return A(F("fromtexteval","fromText eval for "+b+" failed: "+e,e,[b]))}M&&(S=!0);this.depMaps.push(f);l.completeLoad(d);m([d],k)}),h.load(a.name,m,k,p))}));l.enable(d,this);this.pluginMaps[d.id]=d},enable:function(){Z[this.map.id]=this;this.enabling=this.enabled=!0;y(this.depMaps,z(this,function(a,
|
||||
b){var c,h;if("string"===typeof a){a=q(a,this.map.isDefine?this.map:this.map.parentMap,!1,!this.skipMap);this.depMaps[b]=a;if(c=e(R,a.id)){this.depExports[b]=c(this);return}this.depCount+=1;w(a,"defined",z(this,function(a){this.undefed||(this.defineDep(b,a),this.check())}));this.errback?w(a,"error",z(this,this.errback)):this.events.error&&w(a,"error",z(this,function(a){this.emit("error",a)}))}c=a.id;h=t[c];x(R,c)||!h||h.enabled||l.enable(a,this)}));D(this.pluginMaps,z(this,function(a){var b=e(t,a.id);
|
||||
b&&!b.enabled&&l.enable(a,this)}));this.enabling=!1;this.check()},on:function(a,b){var c=this.events[a];c||(c=this.events[a]=[]);c.push(b)},emit:function(a,b){y(this.events[a],function(a){a(b)});"error"===a&&delete this.events[a]}};l={config:p,contextName:b,registry:t,defined:v,urlFetched:W,defQueue:G,defQueueMap:{},Module:da,makeModuleMap:q,nextTick:g.nextTick,onError:A,configure:function(a){a.baseUrl&&"/"!==a.baseUrl.charAt(a.baseUrl.length-1)&&(a.baseUrl+="/");if("string"===typeof a.urlArgs){var b=
|
||||
a.urlArgs;a.urlArgs=function(a,c){return(-1===c.indexOf("?")?"?":"&")+b}}var c=p.shim,h={paths:!0,bundles:!0,config:!0,map:!0};D(a,function(a,b){h[b]?(p[b]||(p[b]={}),Y(p[b],a,!0,!0)):p[b]=a});a.bundles&&D(a.bundles,function(a,b){y(a,function(a){a!==b&&(fa[a]=b)})});a.shim&&(D(a.shim,function(a,b){L(a)&&(a={deps:a});!a.exports&&!a.init||a.exportsFn||(a.exportsFn=l.makeShimExports(a));c[b]=a}),p.shim=c);a.packages&&y(a.packages,function(a){var b;a="string"===typeof a?{name:a}:a;b=a.name;a.location&&
|
||||
(p.paths[b]=a.location);p.pkgs[b]=a.name+"/"+(a.main||"main").replace(na,"").replace(U,"")});D(t,function(a,b){a.inited||a.map.unnormalized||(a.map=q(b,null,!0))});(a.deps||a.callback)&&l.require(a.deps||[],a.callback)},makeShimExports:function(a){return function(){var b;a.init&&(b=a.init.apply(ga,arguments));return b||a.exports&&ia(a.exports)}},makeRequire:function(a,n){function m(c,d,f){var e,r;n.enableBuildCallback&&d&&K(d)&&(d.__requireJsBuild=!0);if("string"===typeof c){if(K(d))return A(F("requireargs",
|
||||
"Invalid require call"),f);if(a&&x(R,c))return R[c](t[a.id]);if(g.get)return g.get(l,c,a,m);e=q(c,a,!1,!0);e=e.id;return x(v,e)?v[e]:A(F("notloaded",'Module name "'+e+'" has not been loaded yet for context: '+b+(a?"":". Use require([])")))}P();l.nextTick(function(){P();r=u(q(null,a));r.skipMap=n.skipMap;r.init(c,d,f,{enabled:!0});H()});return m}n=n||{};Y(m,{isBrowser:E,toUrl:function(b){var d,f=b.lastIndexOf("."),g=b.split("/")[0];-1!==f&&("."!==g&&".."!==g||1<f)&&(d=b.substring(f,b.length),b=b.substring(0,
|
||||
f));return l.nameToUrl(c(b,a&&a.id,!0),d,!0)},defined:function(b){return x(v,q(b,a,!1,!0).id)},specified:function(b){b=q(b,a,!1,!0).id;return x(v,b)||x(t,b)}});a||(m.undef=function(b){B();var c=q(b,a,!0),f=e(t,b);f.undefed=!0;d(b);delete v[b];delete W[c.url];delete ea[b];X(G,function(a,c){a[0]===b&&G.splice(c,1)});delete l.defQueueMap[b];f&&(f.events.defined&&(ea[b]=f.events),C(b))});return m},enable:function(a){e(t,a.id)&&u(a).enable()},completeLoad:function(a){var b,c,d=e(p.shim,a)||{},g=d.exports;
|
||||
for(B();G.length;){c=G.shift();if(null===c[0]){c[0]=a;if(b)break;b=!0}else c[0]===a&&(b=!0);I(c)}l.defQueueMap={};c=e(t,a);if(!b&&!x(v,a)&&c&&!c.inited)if(!p.enforceDefine||g&&ia(g))I([a,d.deps||[],d.exportsFn]);else return m(a)?void 0:A(F("nodefine","No define call for "+a,null,[a]));H()},nameToUrl:function(a,b,c){var d,k,f,m;(d=e(p.pkgs,a))&&(a=d);if(d=e(fa,a))return l.nameToUrl(d,b,c);if(g.jsExtRegExp.test(a))d=a+(b||"");else{d=p.paths;k=a.split("/");for(f=k.length;0<f;--f)if(m=k.slice(0,f).join("/"),
|
||||
m=e(d,m)){L(m)&&(m=m[0]);k.splice(0,f,m);break}d=k.join("/");d+=b||(/^data\:|^blob\:|\?/.test(d)||c?"":".js");d=("/"===d.charAt(0)||d.match(/^[\w\+\.\-]+:/)?"":p.baseUrl)+d}return p.urlArgs&&!/^blob\:/.test(d)?d+p.urlArgs(a,d):d},load:function(a,b){g.load(l,a,b)},execCb:function(a,b,c,d){return b.apply(d,c)},onScriptLoad:function(a){if("load"===a.type||oa.test((a.currentTarget||a.srcElement).readyState))N=null,a=O(a),l.completeLoad(a.id)},onScriptError:function(a){var b=O(a);if(!m(b.id)){var c=[];
|
||||
D(t,function(a,d){0!==d.indexOf("_@r")&&y(a.depMaps,function(a){if(a.id===b.id)return c.push(d),!0})});return A(F("scripterror",'Script error for "'+b.id+(c.length?'", needed by: '+c.join(", "):'"'),a,[b.id]))}}};l.require=l.makeRequire();return l}function pa(){if(N&&"interactive"===N.readyState)return N;X(document.getElementsByTagName("script"),function(b){if("interactive"===b.readyState)return N=b});return N}var g,B,C,H,O,I,N,P,u,T,qa=/(\/\*([\s\S]*?)\*\/|([^:]|^)\/\/(.*)$)/mg,ra=/[^.]\s*require\s*\(\s*["']([^'"\s]+)["']\s*\)/g,
|
||||
U=/\.js$/,na=/^\.\//;B=Object.prototype;var Q=B.toString,la=B.hasOwnProperty,E=!("undefined"===typeof window||"undefined"===typeof navigator||!window.document),ja=!E&&"undefined"!==typeof importScripts,oa=E&&"PLAYSTATION 3"===navigator.platform?/^complete$/:/^(complete|loaded)$/,ca="undefined"!==typeof opera&&"[object Opera]"===opera.toString(),J={},w={},V=[],S=!1;if("undefined"===typeof define){if("undefined"!==typeof requirejs){if(K(requirejs))return;w=requirejs;requirejs=void 0}"undefined"===typeof require||
|
||||
K(require)||(w=require,require=void 0);g=requirejs=function(b,c,d,m){var r,q="_";L(b)||"string"===typeof b||(r=b,L(c)?(b=c,c=d,d=m):b=[]);r&&r.context&&(q=r.context);(m=e(J,q))||(m=J[q]=g.s.newContext(q));r&&m.configure(r);return m.require(b,c,d)};g.config=function(b){return g(b)};g.nextTick="undefined"!==typeof setTimeout?function(b){setTimeout(b,4)}:function(b){b()};require||(require=g);g.version="2.2.0";g.jsExtRegExp=/^\/|:|\?|\.js$/;g.isBrowser=E;B=g.s={contexts:J,newContext:ma};g({});y(["toUrl",
|
||||
"undef","defined","specified"],function(b){g[b]=function(){var c=J._;return c.require[b].apply(c,arguments)}});E&&(C=B.head=document.getElementsByTagName("head")[0],H=document.getElementsByTagName("base")[0])&&(C=B.head=H.parentNode);g.onError=ha;g.createNode=function(b,c,d){c=b.xhtml?document.createElementNS("http://www.w3.org/1999/xhtml","html:script"):document.createElement("script");c.type=b.scriptType||"text/javascript";c.charset="utf-8";c.async=!0;return c};g.load=function(b,c,d){var m=b&&b.config||
|
||||
{},e;if(E){e=g.createNode(m,c,d);e.setAttribute("data-requirecontext",b.contextName);e.setAttribute("data-requiremodule",c);!e.attachEvent||e.attachEvent.toString&&0>e.attachEvent.toString().indexOf("[native code")||ca?(e.addEventListener("load",b.onScriptLoad,!1),e.addEventListener("error",b.onScriptError,!1)):(S=!0,e.attachEvent("onreadystatechange",b.onScriptLoad));e.src=d;if(m.onNodeCreated)m.onNodeCreated(e,m,c,d);P=e;H?C.insertBefore(e,H):C.appendChild(e);P=null;return e}if(ja)try{setTimeout(function(){},
|
||||
0),importScripts(d),b.completeLoad(c)}catch(q){b.onError(F("importscripts","importScripts failed for "+c+" at "+d,q,[c]))}};E&&!w.skipDataMain&&X(document.getElementsByTagName("script"),function(b){C||(C=b.parentNode);if(O=b.getAttribute("data-main"))return u=O,w.baseUrl||-1!==u.indexOf("!")||(I=u.split("/"),u=I.pop(),T=I.length?I.join("/")+"/":"./",w.baseUrl=T),u=u.replace(U,""),g.jsExtRegExp.test(u)&&(u=O),w.deps=w.deps?w.deps.concat(u):[u],!0});define=function(b,c,d){var e,g;"string"!==typeof b&&
|
||||
(d=c,c=b,b=null);L(c)||(d=c,c=null);!c&&K(d)&&(c=[],d.length&&(d.toString().replace(qa,ka).replace(ra,function(b,d){c.push(d)}),c=(1===d.length?["require"]:["require","exports","module"]).concat(c)));S&&(e=P||pa())&&(b||(b=e.getAttribute("data-requiremodule")),g=J[e.getAttribute("data-requirecontext")]);g?(g.defQueue.push([b,c,d]),g.defQueueMap[b]=!0):V.push([b,c,d])};define.amd={jQuery:!0};g.exec=function(b){return eval(b)};g(w)}})(this);
|
||||
@@ -1,11 +0,0 @@
|
||||
# Browserify example
|
||||
|
||||
Two steps need to be done for this to work
|
||||
|
||||
In the project root
|
||||
|
||||
npm install
|
||||
|
||||
In this folder
|
||||
|
||||
../../node_modules/.bin/browserify main.js -o bundle.js
|
||||
@@ -1,209 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html dir="ltr" lang="en-US">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>A date range picker for Bootstrap</title>
|
||||
<link href="http://netdna.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" type="text/css" media="all" href="../../daterangepicker.css" />
|
||||
<style type="text/css">
|
||||
.demo { position: relative; }
|
||||
.demo i {
|
||||
position: absolute; bottom: 10px; right: 24px; top: auto; cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body style="margin: 60px 0">
|
||||
|
||||
<div class="container">
|
||||
|
||||
<h1 style="margin: 0 0 20px 0">Configuration Builder</h1>
|
||||
|
||||
<div class="well configurator">
|
||||
|
||||
<form>
|
||||
<div class="row">
|
||||
|
||||
<div class="col-md-4">
|
||||
|
||||
<div class="form-group">
|
||||
<label for="parentEl">parentEl</label>
|
||||
<input type="text" class="form-control" id="parentEl" value="" placeholder="body">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="startDate">startDate</label>
|
||||
<input type="text" class="form-control" id="startDate" value="07/01/2015">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="endDate">endDate</label>
|
||||
<input type="text" class="form-control" id="endDate" value="07/15/2015">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="minDate">minDate</label>
|
||||
<input type="text" class="form-control" id="minDate" value="" placeholder="MM/DD/YYYY">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="maxDate">maxDate</label>
|
||||
<input type="text" class="form-control" id="maxDate" value="" placeholder="MM/DD/YYYY">
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" id="autoApply"> autoApply
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" id="singleDatePicker"> singleDatePicker
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" id="showDropdowns"> showDropdowns
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" id="showWeekNumbers"> showWeekNumbers
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" id="showISOWeekNumbers"> showISOWeekNumbers
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" id="timePicker"> timePicker
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" id="timePicker24Hour"> timePicker24Hour
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="timePickerIncrement">timePickerIncrement (in minutes)</label>
|
||||
<input type="text" class="form-control" id="timePickerIncrement" value="1">
|
||||
</div>
|
||||
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" id="timePickerSeconds"> timePickerSeconds
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" id="dateLimit"> dateLimit (with example date range span)
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" id="ranges"> ranges (with example predefined ranges)
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" id="locale"> locale (with example settings)
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" id="linkedCalendars" checked="checked"> linkedCalendars
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" id="autoUpdateInput" checked="checked"> autoUpdateInput
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" id="alwaysShowCalendars"> alwaysShowCalendars
|
||||
</label>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
|
||||
<div class="form-group">
|
||||
<label for="opens">opens</label>
|
||||
<select id="opens" class="form-control">
|
||||
<option value="right" selected>right</option>
|
||||
<option value="left">left</option>
|
||||
<option value="center">center</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="drops">drops</label>
|
||||
<select id="drops" class="form-control">
|
||||
<option value="down" selected>down</option>
|
||||
<option value="up">up</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="buttonClasses">buttonClasses</label>
|
||||
<input type="text" class="form-control" id="buttonClasses" value="btn btn-sm">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="applyClass">applyClass</label>
|
||||
<input type="text" class="form-control" id="applyClass" value="btn-success">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="cancelClass">cancelClass</label>
|
||||
<input type="text" class="form-control" id="cancelClass" value="btn-default">
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
|
||||
<div class="col-md-4 col-md-offset-2 demo">
|
||||
<h4>Your Date Range Picker</h4>
|
||||
<input type="text" id="config-demo" class="form-control">
|
||||
<i class="glyphicon glyphicon-calendar fa fa-calendar"></i>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<h4>Configuration</h4>
|
||||
|
||||
<div class="well">
|
||||
<textarea id="config-text" style="height: 300px; width: 100%; padding: 10px"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<script type="text/javascript" src="bundle.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,135 +0,0 @@
|
||||
require('../../daterangepicker.js');
|
||||
var $ = require('jquery'),
|
||||
moment = require('moment');
|
||||
|
||||
$(document).ready(function() {
|
||||
|
||||
$('#config-text').keyup(function() {
|
||||
eval($(this).val());
|
||||
});
|
||||
|
||||
$('.configurator input, .configurator select').change(function() {
|
||||
updateConfig();
|
||||
});
|
||||
|
||||
$('.demo i').click(function() {
|
||||
$(this).parent().find('input').click();
|
||||
});
|
||||
|
||||
$('#startDate').daterangepicker({
|
||||
singleDatePicker: true,
|
||||
startDate: moment().subtract(6, 'days')
|
||||
});
|
||||
|
||||
$('#endDate').daterangepicker({
|
||||
singleDatePicker: true,
|
||||
startDate: moment()
|
||||
});
|
||||
|
||||
updateConfig();
|
||||
|
||||
function updateConfig() {
|
||||
var options = {};
|
||||
|
||||
if ($('#singleDatePicker').is(':checked'))
|
||||
options.singleDatePicker = true;
|
||||
|
||||
if ($('#showDropdowns').is(':checked'))
|
||||
options.showDropdowns = true;
|
||||
|
||||
if ($('#showWeekNumbers').is(':checked'))
|
||||
options.showWeekNumbers = true;
|
||||
|
||||
if ($('#showISOWeekNumbers').is(':checked'))
|
||||
options.showISOWeekNumbers = true;
|
||||
|
||||
if ($('#timePicker').is(':checked'))
|
||||
options.timePicker = true;
|
||||
|
||||
if ($('#timePicker24Hour').is(':checked'))
|
||||
options.timePicker24Hour = true;
|
||||
|
||||
if ($('#timePickerIncrement').val().length && $('#timePickerIncrement').val() != 1)
|
||||
options.timePickerIncrement = parseInt($('#timePickerIncrement').val(), 10);
|
||||
|
||||
if ($('#timePickerSeconds').is(':checked'))
|
||||
options.timePickerSeconds = true;
|
||||
|
||||
if ($('#autoApply').is(':checked'))
|
||||
options.autoApply = true;
|
||||
|
||||
if ($('#dateLimit').is(':checked'))
|
||||
options.dateLimit = { days: 7 };
|
||||
|
||||
if ($('#ranges').is(':checked')) {
|
||||
options.ranges = {
|
||||
'Today': [moment(), moment()],
|
||||
'Yesterday': [moment().subtract(1, 'days'), moment().subtract(1, 'days')],
|
||||
'Last 7 Days': [moment().subtract(6, 'days'), moment()],
|
||||
'Last 30 Days': [moment().subtract(29, 'days'), moment()],
|
||||
'This Month': [moment().startOf('month'), moment().endOf('month')],
|
||||
'Last Month': [moment().subtract(1, 'month').startOf('month'), moment().subtract(1, 'month').endOf('month')]
|
||||
};
|
||||
}
|
||||
|
||||
if ($('#locale').is(':checked')) {
|
||||
options.locale = {
|
||||
format: 'MM/DD/YYYY HH:mm',
|
||||
separator: ' - ',
|
||||
applyLabel: 'Apply',
|
||||
cancelLabel: 'Cancel',
|
||||
fromLabel: 'From',
|
||||
toLabel: 'To',
|
||||
customRangeLabel: 'Custom',
|
||||
daysOfWeek: ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr','Sa'],
|
||||
monthNames: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
|
||||
firstDay: 1
|
||||
};
|
||||
}
|
||||
|
||||
if (!$('#linkedCalendars').is(':checked'))
|
||||
options.linkedCalendars = false;
|
||||
|
||||
if (!$('#autoUpdateInput').is(':checked'))
|
||||
options.autoUpdateInput = false;
|
||||
|
||||
if ($('#alwaysShowCalendars').is(':checked'))
|
||||
options.alwaysShowCalendars = true;
|
||||
|
||||
if ($('#parentEl').val().length)
|
||||
options.parentEl = $('#parentEl').val();
|
||||
|
||||
if ($('#startDate').val().length)
|
||||
options.startDate = $('#startDate').val();
|
||||
|
||||
if ($('#endDate').val().length)
|
||||
options.endDate = $('#endDate').val();
|
||||
|
||||
if ($('#minDate').val().length)
|
||||
options.minDate = $('#minDate').val();
|
||||
|
||||
if ($('#maxDate').val().length)
|
||||
options.maxDate = $('#maxDate').val();
|
||||
|
||||
if ($('#opens').val().length && $('#opens').val() != 'right')
|
||||
options.opens = $('#opens').val();
|
||||
|
||||
if ($('#drops').val().length && $('#drops').val() != 'down')
|
||||
options.drops = $('#drops').val();
|
||||
|
||||
if ($('#buttonClasses').val().length && $('#buttonClasses').val() != 'btn btn-sm')
|
||||
options.buttonClasses = $('#buttonClasses').val();
|
||||
|
||||
if ($('#applyClass').val().length && $('#applyClass').val() != 'btn-success')
|
||||
options.applyClass = $('#applyClass').val();
|
||||
|
||||
if ($('#cancelClass').val().length && $('#cancelClass').val() != 'btn-default')
|
||||
options.cancelClass = $('#cancelClass').val();
|
||||
|
||||
$('#config-text').val("$('#demo').daterangepicker(" + JSON.stringify(options, null, ' ') + ", function(start, end, label) {\n console.log(\"New date range selected: ' + start.format('YYYY-MM-DD') + ' to ' + end.format('YYYY-MM-DD') + ' (predefined range: ' + label + ')\");\n});");
|
||||
|
||||
$('#config-demo').daterangepicker(options, function(start, end, label) { console.log('New date range selected: ' + start.format('YYYY-MM-DD') + ' to ' + end.format('YYYY-MM-DD') + ' (predefined range: ' + label + ')'); });
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
1
public/plugins/daterangepicker/moment.min.js
vendored
1
public/plugins/daterangepicker/moment.min.js
vendored
File diff suppressed because one or more lines are too long
@@ -1,17 +0,0 @@
|
||||
Package.describe({
|
||||
name: 'dangrossman:bootstrap-daterangepicker',
|
||||
version: '3.0.5',
|
||||
summary: 'Date range picker component',
|
||||
git: 'https://github.com/dangrossman/daterangepicker',
|
||||
documentation: 'README.md'
|
||||
});
|
||||
|
||||
Package.onUse(function(api) {
|
||||
api.versionsFrom('METEOR@0.9.0.1');
|
||||
|
||||
api.use('momentjs:moment@2.22.1', ["client"]);
|
||||
api.use('jquery@3.3.1', ["client"]);
|
||||
|
||||
api.addFiles('daterangepicker.js', ["client"]);
|
||||
api.addFiles('daterangepicker.css', ["client"]);
|
||||
});
|
||||
@@ -1,152 +0,0 @@
|
||||
body {
|
||||
font-size: 15px;
|
||||
line-height: 1.6em;
|
||||
position: relative;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.navbar .nav-item {
|
||||
padding: 8px 0 8px 20px;
|
||||
}
|
||||
|
||||
.navbar .nav-link {
|
||||
font-weight: bold;
|
||||
font-size: 14px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.navbar-expand-sm .navbar-nav .nav-link {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.well {
|
||||
background: #f5f5f5;
|
||||
border-radius: 4px;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 20px;
|
||||
margin-bottom: 1em;
|
||||
padding-bottom: 5px;
|
||||
border-bottom: 1px dotted #08c;
|
||||
}
|
||||
|
||||
h1:before {
|
||||
content: '#';
|
||||
color: #666;
|
||||
position: relative;
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
padding: 0;
|
||||
margin: 20px 0 0 0;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
h2 a {
|
||||
color: #444;
|
||||
display: block;
|
||||
background: #eee;
|
||||
padding: 8px 12px;
|
||||
margin-bottom: 0;
|
||||
cursor: default;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
input.form-control {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.collapsable {
|
||||
border: 1px solid #eee;
|
||||
padding: 12px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
label {
|
||||
font-size: 13px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.gist {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.gist .blob-wrapper.data {
|
||||
max-height: 350px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.list-group-item {
|
||||
padding: 4px 0;
|
||||
border: 0;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.leftcol {
|
||||
position: absolute;
|
||||
top: 180px;
|
||||
}
|
||||
|
||||
.rightcol {
|
||||
max-width: 950px;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1300px;
|
||||
}
|
||||
|
||||
@media (min-width: 980px) {
|
||||
.rightcol {
|
||||
margin-left: 320px;
|
||||
}
|
||||
}
|
||||
|
||||
p, pre {
|
||||
margin-bottom: 2em;
|
||||
}
|
||||
|
||||
ul.nobullets {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
ul.nobullets li {
|
||||
padding-bottom: 1em;
|
||||
margin-bottom: 1em;
|
||||
border-bottom: 1px dotted #ddd;
|
||||
}
|
||||
|
||||
input[type="text"] {
|
||||
padding: 6px;
|
||||
width: 100%;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
#footer {
|
||||
background: #222;
|
||||
margin-top: 80px;
|
||||
padding: 10px;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
}
|
||||
#footer a:link, #footer a:visited {
|
||||
color: #fff;
|
||||
border-bottom: 1px dotted #fff;
|
||||
}
|
||||
#jumbo {
|
||||
background: #c1deef;
|
||||
color: #000;
|
||||
padding: 20px 0;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
#jumbo h1 {
|
||||
font-size: 28px;
|
||||
}
|
||||
#jumbo .btn {
|
||||
border-radius: 0;
|
||||
font-size: 16px;
|
||||
}
|
||||
@@ -1,179 +0,0 @@
|
||||
$(document).ready(function() {
|
||||
|
||||
$('#config-text').keyup(function() {
|
||||
eval($(this).val());
|
||||
});
|
||||
|
||||
$('.configurator input, .configurator select').change(function() {
|
||||
updateConfig();
|
||||
});
|
||||
|
||||
$('.demo i').click(function() {
|
||||
$(this).parent().find('input').click();
|
||||
});
|
||||
|
||||
$('#startDate').daterangepicker({
|
||||
singleDatePicker: true,
|
||||
startDate: moment().subtract(6, 'days')
|
||||
});
|
||||
|
||||
$('#endDate').daterangepicker({
|
||||
singleDatePicker: true,
|
||||
startDate: moment()
|
||||
});
|
||||
|
||||
//updateConfig();
|
||||
|
||||
function updateConfig() {
|
||||
var options = {};
|
||||
|
||||
if ($('#singleDatePicker').is(':checked'))
|
||||
options.singleDatePicker = true;
|
||||
|
||||
if ($('#showDropdowns').is(':checked'))
|
||||
options.showDropdowns = true;
|
||||
|
||||
if ($('#minYear').val().length && $('#minYear').val() != 1)
|
||||
options.minYear = parseInt($('#minYear').val(), 10);
|
||||
|
||||
if ($('#maxYear').val().length && $('#maxYear').val() != 1)
|
||||
options.maxYear = parseInt($('#maxYear').val(), 10);
|
||||
|
||||
if ($('#showWeekNumbers').is(':checked'))
|
||||
options.showWeekNumbers = true;
|
||||
|
||||
if ($('#showISOWeekNumbers').is(':checked'))
|
||||
options.showISOWeekNumbers = true;
|
||||
|
||||
if ($('#timePicker').is(':checked'))
|
||||
options.timePicker = true;
|
||||
|
||||
if ($('#timePicker24Hour').is(':checked'))
|
||||
options.timePicker24Hour = true;
|
||||
|
||||
if ($('#timePickerIncrement').val().length && $('#timePickerIncrement').val() != 1)
|
||||
options.timePickerIncrement = parseInt($('#timePickerIncrement').val(), 10);
|
||||
|
||||
if ($('#timePickerSeconds').is(':checked'))
|
||||
options.timePickerSeconds = true;
|
||||
|
||||
if ($('#autoApply').is(':checked'))
|
||||
options.autoApply = true;
|
||||
|
||||
if ($('#maxSpan').is(':checked'))
|
||||
options.maxSpan = { days: 7 };
|
||||
|
||||
if ($('#ranges').is(':checked')) {
|
||||
options.ranges = {
|
||||
'Today': [moment(), moment()],
|
||||
'Yesterday': [moment().subtract(1, 'days'), moment().subtract(1, 'days')],
|
||||
'Last 7 Days': [moment().subtract(6, 'days'), moment()],
|
||||
'Last 30 Days': [moment().subtract(29, 'days'), moment()],
|
||||
'This Month': [moment().startOf('month'), moment().endOf('month')],
|
||||
'Last Month': [moment().subtract(1, 'month').startOf('month'), moment().subtract(1, 'month').endOf('month')]
|
||||
};
|
||||
}
|
||||
|
||||
if ($('#locale').is(':checked')) {
|
||||
options.locale = {
|
||||
format: 'MM/DD/YYYY',
|
||||
separator: ' - ',
|
||||
applyLabel: 'Apply',
|
||||
cancelLabel: 'Cancel',
|
||||
fromLabel: 'From',
|
||||
toLabel: 'To',
|
||||
customRangeLabel: 'Custom',
|
||||
weekLabel: 'W',
|
||||
daysOfWeek: ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr','Sa'],
|
||||
monthNames: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
|
||||
firstDay: 1
|
||||
};
|
||||
}
|
||||
|
||||
if (!$('#linkedCalendars').is(':checked'))
|
||||
options.linkedCalendars = false;
|
||||
|
||||
if (!$('#autoUpdateInput').is(':checked'))
|
||||
options.autoUpdateInput = false;
|
||||
|
||||
if (!$('#showCustomRangeLabel').is(':checked'))
|
||||
options.showCustomRangeLabel = false;
|
||||
|
||||
if ($('#alwaysShowCalendars').is(':checked'))
|
||||
options.alwaysShowCalendars = true;
|
||||
|
||||
if ($('#parentEl').val().length)
|
||||
options.parentEl = $('#parentEl').val();
|
||||
|
||||
if ($('#startDate').val().length)
|
||||
options.startDate = $('#startDate').val();
|
||||
|
||||
if ($('#endDate').val().length)
|
||||
options.endDate = $('#endDate').val();
|
||||
|
||||
if ($('#minDate').val().length)
|
||||
options.minDate = $('#minDate').val();
|
||||
|
||||
if ($('#maxDate').val().length)
|
||||
options.maxDate = $('#maxDate').val();
|
||||
|
||||
if ($('#opens').val().length && $('#opens').val() != 'right')
|
||||
options.opens = $('#opens').val();
|
||||
|
||||
if ($('#drops').val().length && $('#drops').val() != 'down')
|
||||
options.drops = $('#drops').val();
|
||||
|
||||
if ($('#buttonClasses').val().length && $('#buttonClasses').val() != 'btn btn-sm')
|
||||
options.buttonClasses = $('#buttonClasses').val();
|
||||
|
||||
if ($('#applyButtonClasses').val().length && $('#applyButtonClasses').val() != 'btn-primary')
|
||||
options.applyButtonClasses = $('#applyButtonClasses').val();
|
||||
|
||||
if ($('#cancelButtonClasses').val().length && $('#cancelButtonClasses').val() != 'btn-default')
|
||||
options.cancelClass = $('#cancelButtonClasses').val();
|
||||
|
||||
$('#config-demo').daterangepicker(options, function(start, end, label) { console.log('New date range selected: ' + start.format('YYYY-MM-DD') + ' to ' + end.format('YYYY-MM-DD') + ' (predefined range: ' + label + ')'); });
|
||||
|
||||
if (typeof options.ranges !== 'undefined') {
|
||||
options.ranges = {};
|
||||
}
|
||||
|
||||
var option_text = JSON.stringify(options, null, ' ');
|
||||
|
||||
var replacement = "ranges: {\n"
|
||||
+ " 'Today': [moment(), moment()],\n"
|
||||
+ " 'Yesterday': [moment().subtract(1, 'days'), moment().subtract(1, 'days')],\n"
|
||||
+ " 'Last 7 Days': [moment().subtract(6, 'days'), moment()],\n"
|
||||
+ " 'Last 30 Days': [moment().subtract(29, 'days'), moment()],\n"
|
||||
+ " 'This Month': [moment().startOf('month'), moment().endOf('month')],\n"
|
||||
+ " 'Last Month': [moment().subtract(1, 'month').startOf('month'), moment().subtract(1, 'month').endOf('month')]\n"
|
||||
+ " }";
|
||||
option_text = option_text.replace(new RegExp('"ranges"\: \{\}', 'g'), replacement);
|
||||
|
||||
$('#config-text').val("$('#demo').daterangepicker(" + option_text + ", function(start, end, label) {\n console.log('New date range selected: ' + start.format('YYYY-MM-DD') + ' to ' + end.format('YYYY-MM-DD') + ' (predefined range: ' + label + ')');\n});");
|
||||
|
||||
}
|
||||
|
||||
$(window).scroll(function (event) {
|
||||
var scroll = $(window).scrollTop();
|
||||
if (scroll > 180) {
|
||||
$('.leftcol').css('position', 'fixed');
|
||||
$('.leftcol').css('top', '10px');
|
||||
} else {
|
||||
$('.leftcol').css('position', 'absolute');
|
||||
$('.leftcol').css('top', '180px');
|
||||
}
|
||||
});
|
||||
|
||||
var bg = new Trianglify({
|
||||
x_colors: ["#e1f3fd", "#eeeeee", "#407dbf"],
|
||||
y_colors: 'match_x',
|
||||
width: document.body.clientWidth,
|
||||
height: 150,
|
||||
stroke_width: 0,
|
||||
cell_size: 20
|
||||
});
|
||||
|
||||
$('#jumbo').css('background-image', 'url(' + bg.png() + ')');
|
||||
|
||||
});
|
||||
@@ -1,744 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
|
||||
<title>Date Range Picker — JavaScript Date & Time Picker Library</title>
|
||||
|
||||
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.0/umd/popper.min.js" integrity="sha384-cs/chFZiN24E4KMATLdqdvsezGxaGsi4hLGOzlXwp5UZB1LY//20VyM2taTB4QvJ" crossorigin="anonymous"></script>
|
||||
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/js/bootstrap.min.js" integrity="sha384-uefMccjFJAIv6A+rW+L4AHf99KvxDjWSu1z9VI8SKNVmz4sk7buKt/6v9KI65qnm" crossorigin="anonymous"></script>
|
||||
|
||||
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.22.1/moment.min.js"></script>
|
||||
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/trianglify/0.2.1/trianglify.min.js"></script>
|
||||
|
||||
<script type="text/javascript" src="daterangepicker.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="daterangepicker.css" />
|
||||
|
||||
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/css/bootstrap.min.css" integrity="sha384-9gVQ4dYFwwWSjIDZnLEWnxCjeSWFphJiwGPXr1jddIhOegiu1FwO5qRGvFXOdJZ4" crossorigin="anonymous">
|
||||
|
||||
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.0.10/css/all.css" integrity="sha384-+d0P83n9kaQMCwj8F4RJB66tzIwOKmrdb46+porD/OvrJ+37WqIM7UoBtwHO6Nlg" crossorigin="anonymous">
|
||||
|
||||
<script src="website.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="website.css" />
|
||||
|
||||
<meta name="google-site-verification" content="1fP-Eo9i1ozV4MUlqZv2vsLv1r7tvYutUb6i38v0_vg" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<nav class="navbar navbar-expand-sm navbar-dark bg-dark" style="padding: 0; justify-content: space-between">
|
||||
<div class="container">
|
||||
<ul class="navbar-nav">
|
||||
<li class="nav-item" style="padding-left: 0"><a class="nav-link" href="/" style="color: #eeeeee"><i class="fa fa-calendar"></i> Date Range Picker</a></li>
|
||||
</ul>
|
||||
<ul class="navbar-nav">
|
||||
<li class="nav-item"><a class="nav-link" href="https://www.improvely.com" style="color: #33beff"><i class="fa fa-chart-bar"></i> Improvely</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="https://www.w3counter.com" style="color: #ff9999"><i class="fa fa-chart-pie"></i> W3Counter</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="https://www.websitegoodies.com" style="color: #f49a16"><i class="fa fa-wrench"></i> Website Goodies</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div id="jumbo">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-6 col-sm-12">
|
||||
|
||||
<h1 style="margin: 0 0 10px 0">Date Range Picker</h1>
|
||||
<p style="font-size: 18px; margin-bottom: 0">
|
||||
A JavaScript component for choosing date ranges,
|
||||
dates and times.
|
||||
</p>
|
||||
|
||||
</div>
|
||||
<div class="col-md-6 col-sm-12" style="text-align: right; padding-right: 0">
|
||||
|
||||
<div style="margin-bottom: 10px; margin-right: 15px">
|
||||
<a href="https://github.com/dangrossman/daterangepicker" class="btn btn-light"><i class="fab fa-github-alt"></i> View on GitHub</a>
|
||||
|
||||
|
||||
|
||||
<a href="https://github.com/dangrossman/daterangepicker/archive/master.zip" class="btn btn-success"><i class="fas fa-download"></i> Download ZIP</a>
|
||||
</div>
|
||||
|
||||
<div style="margin-right: 0px">
|
||||
<iframe src="https://ghbtns.com/github-btn.html?user=dangrossman&repo=daterangepicker&type=star&count=true&size=large" frameborder="0" scrolling="0" width="160px" height="30px"></iframe>
|
||||
|
||||
<iframe src="https://ghbtns.com/github-btn.html?user=dangrossman&repo=daterangepicker&type=watch&count=true&size=large&v=2" frameborder="0" scrolling="0" width="160px" height="30px"></iframe>
|
||||
|
||||
<iframe src="https://ghbtns.com/github-btn.html?user=dangrossman&repo=daterangepicker&type=fork&count=true&size=large" frameborder="0" scrolling="0" width="160px" height="30px"></iframe>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container" style="padding: 0 30px" id="main">
|
||||
<div class="row">
|
||||
<div class="leftcol d-none d-lg-block d-xl-block">
|
||||
<div id="menu" class="list-group" style="margin-bottom: 10px">
|
||||
<a class="list-group-item" href="#usage">Getting Started</a>
|
||||
<a class="list-group-item" href="#examples">Examples</a>
|
||||
<a class="list-group-item" href="#options">Options, Methods & Events</a>
|
||||
<a class="list-group-item" href="#config">Configuration Generator</a>
|
||||
<a class="list-group-item" href="#license">License & Comments</a>
|
||||
</div>
|
||||
|
||||
<div style="width: 300px; min-width: 300px">
|
||||
<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
|
||||
<!-- DRP Responsive -->
|
||||
<ins class="adsbygoogle"
|
||||
style="display:block"
|
||||
data-ad-client="ca-pub-9095657276960731"
|
||||
data-ad-slot="8963174596"
|
||||
data-ad-format="auto"></ins>
|
||||
<script>
|
||||
(adsbygoogle = window.adsbygoogle || []).push({});
|
||||
</script>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="rightcol">
|
||||
|
||||
<p>Originally created for reports at <a href="https://www.improvely.com">Improvely</a>, the Date Range Picker can be attached to any webpage element to pop up two calendars for selecting dates, times, or predefined ranges like "Last 30 Days".</p>
|
||||
|
||||
<a style="display: block; height: 300px; background: url('drp.png') top right no-repeat; background-size: cover; border: 1px solid #ccc; margin-bottom: 30px" href="https://awio.iljmp.com/5/drpdemo" title="Click for a Live Demo"></a>
|
||||
|
||||
<h1><a id="usage" href="#usage">Getting Started</a></h1>
|
||||
|
||||
<p>
|
||||
To get started, include jQuery, Moment.js and Date Range Picker's files in your webpage:
|
||||
</p>
|
||||
|
||||
<script src="https://gist.github.com/dangrossman/4879503153c6a7a0b3b6ebd64e0383b7.js"></script>
|
||||
|
||||
<p style="margin-bottom: 10px">
|
||||
Then attach a date range picker to whatever you want to trigger it:
|
||||
</p>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-8">
|
||||
<label>Code:</label>
|
||||
<script src="https://gist.github.com/dangrossman/f460cf1243d8ffb08c749730e89c2f3d.js"></script>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<label>Produces:</label>
|
||||
<input type="text" name="dates" class="form-control pull-right" />
|
||||
<script>
|
||||
$(function() {
|
||||
$('input[name="dates"]').daterangepicker({ startDate: moment(), endDate: moment().add(2, 'day')});
|
||||
})
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
You can customize Date Range Picker with options, and get notified when the user chooses new dates by providing a callback function.
|
||||
</p>
|
||||
|
||||
<h1><a id="examples" href="#examples">Examples</a></h1>
|
||||
|
||||
<!-- Example 1 -->
|
||||
<h2><a data-toggle="nothing" href="#example1">Simple Date Range Picker With a Callback</a></h2>
|
||||
<div class="collapsable" id="example1">
|
||||
<div class="row">
|
||||
<div class="col-8">
|
||||
<label>Code:</label>
|
||||
<script src="https://gist.github.com/dangrossman/e118af4dbadc5177d7494dba9d3295d1.js"></script>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<label>Produces:</label>
|
||||
<input type="text" name="daterange" class="form-control" value="1/01/2018 - 01/15/2018" />
|
||||
|
||||
<script>
|
||||
$(function() {
|
||||
$('input[name="daterange"]').daterangepicker({
|
||||
opens: 'left'
|
||||
}, function(start, end, label) {
|
||||
console.log("A new date selection was made: " + start.format('YYYY-MM-DD') + ' to ' + end.format('YYYY-MM-DD'));
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Example 2 -->
|
||||
<h2><a data-toggle="nothing" href="#example2">Date Range Picker With Times</a></h2>
|
||||
<div class="collapsable" id="example2">
|
||||
<div class="row">
|
||||
<div class="col-8">
|
||||
<label>Code:</label>
|
||||
<script src="https://gist.github.com/dangrossman/14db17599e24652db7401ed2448eb91a.js"></script>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<label>Produces:</label>
|
||||
<input type="text" name="datetimes" class="form-control pull-right" />
|
||||
|
||||
<script>
|
||||
$(function() {
|
||||
$('input[name="datetimes"]').daterangepicker({
|
||||
timePicker: true,
|
||||
startDate: moment().startOf('hour'),
|
||||
endDate: moment().startOf('hour').add(32, 'hour'),
|
||||
locale: {
|
||||
format: 'M/DD hh:mm A'
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Example 3 -->
|
||||
<h2><a data-toggle="nothing" href="#example3">Single Date Picker</a></h2>
|
||||
<div class="collapsable" id="example3">
|
||||
<div class="row">
|
||||
<div class="col-8">
|
||||
<label>Code:</label>
|
||||
<script src="https://gist.github.com/dangrossman/98d8f1c304328c191b1ad33ac21354fd.js"></script>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<label>Produces:</label>
|
||||
<input type="text" name="birthday" class="form-control pull-right" value="10/24/1984" />
|
||||
|
||||
<script>
|
||||
$(function() {
|
||||
$('input[name="birthday"]').daterangepicker({
|
||||
singleDatePicker: true,
|
||||
showDropdowns: true,
|
||||
minYear: 1901,
|
||||
maxYear: parseInt(moment().format('YYYY'),10)
|
||||
}, function(start, end, label) {
|
||||
var years = moment().diff(start, 'years');
|
||||
alert("You are " + years + " years old!");
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Example 4 -->
|
||||
<h2><a data-toggle="nothing" href="#example4">Predefined Date Ranges</a></h2>
|
||||
<div class="collapsable" id="example4">
|
||||
<div class="row">
|
||||
<div class="col-8">
|
||||
<label>Code:</label>
|
||||
<script src="https://gist.github.com/dangrossman/8c6747b82572bc860364f17258004dbb.js"></script>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<label>Produces:</label>
|
||||
|
||||
<div id="reportrange" class="pull-right" style="background: #fff; cursor: pointer; padding: 5px 10px; border: 1px solid #ccc; width: 100%">
|
||||
<i class="fa fa-calendar"></i>
|
||||
<span></span> <i class="fa fa-caret-down"></i>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
$(function() {
|
||||
|
||||
var start = moment().subtract(29, 'days');
|
||||
var end = moment();
|
||||
|
||||
function cb(start, end) {
|
||||
$('#reportrange span').html(start.format('MMMM D, YYYY') + ' - ' + end.format('MMMM D, YYYY'));
|
||||
}
|
||||
|
||||
$('#reportrange').daterangepicker({
|
||||
startDate: start,
|
||||
endDate: end,
|
||||
ranges: {
|
||||
'Today': [moment(), moment()],
|
||||
'Yesterday': [moment().subtract(1, 'days'), moment().subtract(1, 'days')],
|
||||
'Last 7 Days': [moment().subtract(6, 'days'), moment()],
|
||||
'Last 30 Days': [moment().subtract(29, 'days'), moment()],
|
||||
'This Month': [moment().startOf('month'), moment().endOf('month')],
|
||||
'Last Month': [moment().subtract(1, 'month').startOf('month'), moment().subtract(1, 'month').endOf('month')]
|
||||
}
|
||||
}, cb);
|
||||
|
||||
cb(start, end);
|
||||
|
||||
});
|
||||
</script>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Example 5 -->
|
||||
<h2><a data-toggle="nothing" href="#example5">Input Initially Empty</a></h2>
|
||||
<div class="collapsable" id="example5">
|
||||
<div class="row">
|
||||
<div class="col-8">
|
||||
<label>Code:</label>
|
||||
<script src="https://gist.github.com/dangrossman/d50376f3467f69e7fb5570afd07dc921.js"></script>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<label>Produces:</label>
|
||||
<input type="text" name="datefilter" class="form-control pull-right" value="" />
|
||||
|
||||
<script type="text/javascript">
|
||||
$(function() {
|
||||
|
||||
$('input[name="datefilter"]').daterangepicker({
|
||||
autoUpdateInput: false,
|
||||
locale: {
|
||||
cancelLabel: 'Clear'
|
||||
}
|
||||
});
|
||||
|
||||
$('input[name="datefilter"]').on('apply.daterangepicker', function(ev, picker) {
|
||||
$(this).val(picker.startDate.format('MM/DD/YYYY') + ' - ' + picker.endDate.format('MM/DD/YYYY'));
|
||||
});
|
||||
|
||||
$('input[name="datefilter"]').on('cancel.daterangepicker', function(ev, picker) {
|
||||
$(this).val('');
|
||||
});
|
||||
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h1 style="margin-top: 30px"><a id="options" href="#options">Options</a></h1>
|
||||
|
||||
<ul class="nobullets">
|
||||
<li>
|
||||
<code>startDate</code> (Date or string) The beginning date of the initially selected date range. If you provide a string, it must match the date format string set in your <code>locale</code> setting.
|
||||
</li>
|
||||
<li>
|
||||
<code>endDate</code>: (Date or string) The end date of the initially selected date range.
|
||||
</li>
|
||||
<li>
|
||||
<code>minDate</code>: (Date or string) The earliest date a user may select.
|
||||
</li>
|
||||
<li>
|
||||
<code>maxDate</code>: (Date or string) The latest date a user may select.
|
||||
</li>
|
||||
<li>
|
||||
<code>maxSpan</code>: (object) The maximum span between the selected start and end dates. Check off <code>maxSpan</code> in the configuration generator for an example of how to use this. You can provide any object the <code>moment</code> library would let you add to a date.
|
||||
</li>
|
||||
<li>
|
||||
<code>showDropdowns</code>: (true/false) Show year and month select boxes above calendars to jump to a specific month and year.
|
||||
</li>
|
||||
<li>
|
||||
<code>minYear</code>: (number) The minimum year shown in the dropdowns when <code>showDropdowns</code> is set to true.
|
||||
</li>
|
||||
<li>
|
||||
<code>maxYear</code>: (number) The maximum year shown in the dropdowns when <code>showDropdowns</code> is set to true.
|
||||
</li>
|
||||
<li>
|
||||
<code>showWeekNumbers</code>: (true/false) Show localized week numbers at the start of each week on the calendars.
|
||||
</li>
|
||||
<li>
|
||||
<code>showISOWeekNumbers</code>: (true/false) Show ISO week numbers at the start of each week on the calendars.
|
||||
</li>
|
||||
<li>
|
||||
<code>timePicker</code>: (true/false) Adds select boxes to choose times in addition to dates.
|
||||
</li>
|
||||
<li>
|
||||
<code>timePickerIncrement</code>: (number) Increment of the minutes selection list for times (i.e. 30 to allow only selection of times ending in 0 or 30).
|
||||
</li>
|
||||
<li>
|
||||
<code>timePicker24Hour</code>: (true/false) Use 24-hour instead of 12-hour times, removing the AM/PM selection.
|
||||
</li>
|
||||
<li>
|
||||
<code>timePickerSeconds</code>: (true/false) Show seconds in the timePicker.
|
||||
</li>
|
||||
<li>
|
||||
<code>ranges</code>: (object) Set predefined date ranges the user can select from. Each key is the label for the range, and its value an array with two dates representing the bounds of the range. Click <code>ranges</code> in the configuration generator for examples.
|
||||
</li>
|
||||
<li>
|
||||
<code>showCustomRangeLabel</code>: (true/false) Displays "Custom Range" at
|
||||
the end of the list of predefined ranges, when the <code>ranges</code> option is used.
|
||||
This option will be highlighted whenever the current date range selection does not match one of the predefined ranges. Clicking it will display the calendars to select a new range.
|
||||
</li>
|
||||
<li>
|
||||
<code>alwaysShowCalendars</code>: (true/false) Normally, if you use the <code>ranges</code> option to specify pre-defined date ranges, calendars for choosing a custom date range are not shown until the user clicks "Custom Range". When this option is set to true, the calendars for choosing a custom date range are always shown instead.
|
||||
</li>
|
||||
<li>
|
||||
<code>opens</code>: ('left'/'right'/'center') Whether the picker appears aligned to the left, to the right, or centered under the HTML element it's attached to.
|
||||
</li>
|
||||
<li>
|
||||
<code>drops</code>: ('down'/'up') Whether the picker appears below (default) or above the HTML element it's attached to.
|
||||
</li>
|
||||
<li>
|
||||
<code>buttonClasses</code>: (string) CSS class names that will be added to both the apply and cancel buttons.
|
||||
</li>
|
||||
<li>
|
||||
<code>applyButtonClasses</code>: (string) CSS class names that will be added only to the apply button.
|
||||
</li>
|
||||
<li>
|
||||
<code>cancelButtonClasses</code>: (string) CSS class names that will be added only to the cancel button.
|
||||
</li>
|
||||
<li>
|
||||
<code>locale</code>: (object) Allows you to provide localized strings for buttons and labels, customize the date format, and change the first day of week for the calendars.
|
||||
Check off <code>locale</code> in the configuration generator to see how
|
||||
to customize these options.
|
||||
</li>
|
||||
<li>
|
||||
<code>singleDatePicker</code>: (true/false) Show only a single calendar to choose one date, instead of a range picker with two calendars. The start and end dates provided to your callback will be the same single date chosen.
|
||||
</li>
|
||||
<li>
|
||||
<code>autoApply</code>: (true/false) Hide the apply and cancel buttons, and automatically apply a new date range as soon as two dates are clicked.
|
||||
</li>
|
||||
<li>
|
||||
<code>linkedCalendars</code>: (true/false) When enabled, the two calendars displayed will always be for two sequential months (i.e. January and February), and both will be advanced when clicking the left or right arrows above the calendars. When disabled, the two calendars can be individually advanced and display any month/year.
|
||||
</li>
|
||||
<li>
|
||||
<code>isInvalidDate</code>: (function) A function that is passed each date in the two
|
||||
calendars before they are displayed, and may return true or false to indicate whether
|
||||
that date should be available for selection or not.
|
||||
</li>
|
||||
<li>
|
||||
<code>isCustomDate</code>: (function) A function that is passed each date in the two
|
||||
calendars before they are displayed, and may return a string or array of CSS class names
|
||||
to apply to that date's calendar cell.
|
||||
</li>
|
||||
<li>
|
||||
<code>autoUpdateInput</code>: (true/false) Indicates whether the date range picker should
|
||||
automatically update the value of the <code><input></code> element it's attached to at initialization and when the selected dates change.
|
||||
</li>
|
||||
<li>
|
||||
<code>parentEl</code>: (string) jQuery selector of the parent element that the date range picker will be added to, if not provided this will be 'body'
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h1 style="margin-top: 30px"><a id="methods" href="#methods">Methods</a></h1>
|
||||
|
||||
<p>
|
||||
You can programmatically update the <code>startDate</code> and <code>endDate</code>
|
||||
in the picker using the <code>setStartDate</code> and <code>setEndDate</code> methods.
|
||||
You can access the Date Range Picker object and its functions and properties through
|
||||
data properties of the element you attached it to.
|
||||
</p>
|
||||
|
||||
<script src="https://gist.github.com/dangrossman/8ff9b1220c9b5682e8bd.js"></script>
|
||||
|
||||
<br/>
|
||||
|
||||
<ul class="nobullets">
|
||||
<li>
|
||||
<code>setStartDate(Date or string)</code>: Sets the date range picker's currently selected start date to the provided date
|
||||
</li>
|
||||
<li>
|
||||
<code>setEndDate(Date or string)</code>: Sets the date range picker's currently selected end date to the provided date
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<p style="margin: 0"><b>Example usage:</b></p>
|
||||
|
||||
<script src="https://gist.github.com/dangrossman/e1a8effbaeacb50a1e31.js"></script>
|
||||
|
||||
<h1 style="margin-top: 30px"><a id="events" href="#events">Events</a></h1>
|
||||
|
||||
<p>
|
||||
Several events are triggered on the element you attach the picker to, which you can listen for.
|
||||
</p>
|
||||
|
||||
<ul class="nobullets">
|
||||
<li>
|
||||
<code>show.daterangepicker</code>: Triggered when the picker is shown
|
||||
</li>
|
||||
<li>
|
||||
<code>hide.daterangepicker</code>: Triggered when the picker is hidden
|
||||
</li>
|
||||
<li>
|
||||
<code>showCalendar.daterangepicker</code>: Triggered when the calendar(s) are shown
|
||||
</li>
|
||||
<li>
|
||||
<code>hideCalendar.daterangepicker</code>: Triggered when the calendar(s) are hidden
|
||||
</li>
|
||||
<li>
|
||||
<code>apply.daterangepicker</code>: Triggered when the apply button is clicked,
|
||||
or when a predefined range is clicked
|
||||
</li>
|
||||
<li>
|
||||
<code>cancel.daterangepicker</code>: Triggered when the cancel button is clicked
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<p>
|
||||
Some applications need a "clear" instead of a "cancel" functionality, which can be achieved by changing the button label and watching for the cancel event:
|
||||
</p>
|
||||
|
||||
<script src="https://gist.github.com/dangrossman/1bea78da703f2896564d.js"></script>
|
||||
|
||||
<br/>
|
||||
|
||||
<p>
|
||||
While passing in a callback to the constructor is the easiest way to listen for changes in the selected date range, you can also do something every time the apply button is clicked even if the selection hasn't changed:
|
||||
</p>
|
||||
|
||||
<script src="https://gist.github.com/dangrossman/0c6c911fea1459b5fd13.js"></script>
|
||||
|
||||
<h1 style="margin-top: 30px"><a id="config" href="#config">Configuration Generator</a></h1>
|
||||
|
||||
<div class="well configurator">
|
||||
|
||||
<form>
|
||||
<div class="row">
|
||||
|
||||
<div class="col-md-4">
|
||||
|
||||
<div class="form-group">
|
||||
<label for="parentEl">parentEl</label>
|
||||
<input type="text" class="form-control" id="parentEl" value="" placeholder="body">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="startDate">startDate</label>
|
||||
<input type="text" class="form-control" id="startDate" value="07/01/2017">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="endDate">endDate</label>
|
||||
<input type="text" class="form-control" id="endDate" value="07/15/2017">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="minDate">minDate</label>
|
||||
<input type="text" class="form-control" id="minDate" value="" placeholder="MM/DD/YYYY">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="maxDate">maxDate</label>
|
||||
<input type="text" class="form-control" id="maxDate" value="" placeholder="MM/DD/YYYY">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="opens">opens</label>
|
||||
<select id="opens" class="form-control">
|
||||
<option value="right" selected>right</option>
|
||||
<option value="left">left</option>
|
||||
<option value="center">center</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="drops">drops</label>
|
||||
<select id="drops" class="form-control">
|
||||
<option value="down" selected>down</option>
|
||||
<option value="up">up</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" id="showDropdowns"> showDropdowns
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="minYear">minYear</label>
|
||||
<input type="text" class="form-control" id="minYear" value="">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="maxYear">maxYear</label>
|
||||
<input type="text" class="form-control" id="maxYear" value="">
|
||||
</div>
|
||||
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" id="showWeekNumbers"> showWeekNumbers
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" id="showISOWeekNumbers"> showISOWeekNumbers
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" id="singleDatePicker"> singleDatePicker
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" id="timePicker"> timePicker
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" id="timePicker24Hour"> timePicker24Hour
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="timePickerIncrement">timePickerIncrement (in minutes)</label>
|
||||
<input type="text" class="form-control" id="timePickerIncrement" value="1">
|
||||
</div>
|
||||
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" id="timePickerSeconds"> timePickerSeconds
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" id="maxSpan"> maxSpan (example value)
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" id="locale"> locale (example settings)
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" id="autoApply"> autoApply
|
||||
</label>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
|
||||
<div class="form-group">
|
||||
<label for="buttonClasses">buttonClasses</label>
|
||||
<input type="text" class="form-control" id="buttonClasses" value="btn btn-sm">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="applyButtonClasses">applyButtonClasses</label>
|
||||
<input type="text" class="form-control" id="applyButtonClasses" value="btn-primary">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="cancelButtonClasses">cancelButtonClasses</label>
|
||||
<input type="text" class="form-control" id="cancelButtonClasses" value="btn-default">
|
||||
</div>
|
||||
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" id="ranges"> ranges (with example predefined ranges)
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" id="alwaysShowCalendars"> alwaysShowCalendars
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" id="showCustomRangeLabel" checked="checked"> showCustomRangeLabel
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" id="linkedCalendars" checked="checked"> linkedCalendars
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" id="autoUpdateInput" checked="checked"> autoUpdateInput
|
||||
</label>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
|
||||
<div class="col-4 demo">
|
||||
<div style="position: relative">
|
||||
<h2 style="margin-bottom: 15px">Your Date Range Picker</h2>
|
||||
<input type="text" id="config-demo" class="form-control">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-8">
|
||||
<h2 style="margin-bottom: 15px">Your Configuration to Copy</h2>
|
||||
|
||||
<div class="well">
|
||||
<textarea id="config-text" style="height: 300px; width: 100%; padding: 10px"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- License -->
|
||||
|
||||
<h1 style="margin-top: 30px"><a id="license" href="#license">License</a></h1>
|
||||
|
||||
<p>The MIT License (MIT)</p>
|
||||
|
||||
<p>Copyright (c) 2012-2019 <a href="http://www.dangrossman.info">Dan Grossman</a></p>
|
||||
|
||||
<p>
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
</p>
|
||||
|
||||
<p>
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
</p>
|
||||
|
||||
<h1 style="margin-top: 30px"><a id="config" href="#comments">Comments</a></h1>
|
||||
|
||||
<div id="disqus_thread"></div>
|
||||
<script type="text/javascript">
|
||||
/* * * CONFIGURATION VARIABLES: EDIT BEFORE PASTING INTO YOUR WEBPAGE * * */
|
||||
var disqus_url = 'http://www.dangrossman.info/2012/08/20/a-date-range-picker-for-twitter-bootstrap/';
|
||||
var disqus_identifier = '1045 http://www.dangrossman.info/?p=1045';
|
||||
var disqus_container_id = 'disqus_thread';
|
||||
var disqus_shortname = 'dangrossman';
|
||||
var disqus_title = "A Date Range Picker for Bootstrap";
|
||||
|
||||
/* * * DON'T EDIT BELOW THIS LINE * * */
|
||||
(function() {
|
||||
var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;
|
||||
dsq.src = 'https://' + disqus_shortname + '.disqus.com/embed.js';
|
||||
(document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
|
||||
})();
|
||||
</script>
|
||||
<noscript>Please enable JavaScript to view the <a href="https://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Begin W3Counter Tracking Code -->
|
||||
<script type="text/javascript" src="https://www.w3counter.com/tracker.js?id=90840"></script>
|
||||
<!-- End W3Counter Tracking Code -->
|
||||
|
||||
<script type="text/javascript">
|
||||
var im_domain = 'awio';
|
||||
var im_project_id = 48;
|
||||
(function(e,t){window._improvely=[];var n=e.getElementsByTagName("script")[0];var r=e.createElement("script");r.type="text/javascript";r.src="https://"+im_domain+".iljmp.com/improvely.js";r.async=true;n.parentNode.insertBefore(r,n);if(typeof t.init=="undefined"){t.init=function(e,t){window._improvely.push(["init",e,t])};t.goal=function(e){window._improvely.push(["goal",e])};t.conversion=function(e){window._improvely.push(["conversion",e])};t.label=function(e){window._improvely.push(["label",e])}}window.improvely=t;t.init(im_domain,im_project_id)})(document,window.improvely||[])
|
||||
</script>
|
||||
|
||||
<div id="footer">
|
||||
Copyright © 2012-2018 <a href="http://www.dangrossman.info/">Dan Grossman</a>.
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,152 +0,0 @@
|
||||
body {
|
||||
font-size: 15px;
|
||||
line-height: 1.6em;
|
||||
position: relative;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.navbar .nav-item {
|
||||
padding: 8px 0 8px 20px;
|
||||
}
|
||||
|
||||
.navbar .nav-link {
|
||||
font-weight: bold;
|
||||
font-size: 14px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.navbar-expand-sm .navbar-nav .nav-link {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.well {
|
||||
background: #f5f5f5;
|
||||
border-radius: 4px;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 20px;
|
||||
margin-bottom: 1em;
|
||||
padding-bottom: 5px;
|
||||
border-bottom: 1px dotted #08c;
|
||||
}
|
||||
|
||||
h1:before {
|
||||
content: '#';
|
||||
color: #666;
|
||||
position: relative;
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
padding: 0;
|
||||
margin: 20px 0 0 0;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
h2 a {
|
||||
color: #444;
|
||||
display: block;
|
||||
background: #eee;
|
||||
padding: 8px 12px;
|
||||
margin-bottom: 0;
|
||||
cursor: default;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
input.form-control {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.collapsable {
|
||||
border: 1px solid #eee;
|
||||
padding: 12px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
label {
|
||||
font-size: 13px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.gist {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.gist .blob-wrapper.data {
|
||||
max-height: 350px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.list-group-item {
|
||||
padding: 4px 0;
|
||||
border: 0;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.leftcol {
|
||||
position: absolute;
|
||||
top: 180px;
|
||||
}
|
||||
|
||||
.rightcol {
|
||||
max-width: 950px;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1300px;
|
||||
}
|
||||
|
||||
@media (min-width: 980px) {
|
||||
.rightcol {
|
||||
margin-left: 320px;
|
||||
}
|
||||
}
|
||||
|
||||
p, pre {
|
||||
margin-bottom: 2em;
|
||||
}
|
||||
|
||||
ul.nobullets {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
ul.nobullets li {
|
||||
padding-bottom: 1em;
|
||||
margin-bottom: 1em;
|
||||
border-bottom: 1px dotted #ddd;
|
||||
}
|
||||
|
||||
input[type="text"] {
|
||||
padding: 6px;
|
||||
width: 100%;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
#footer {
|
||||
background: #222;
|
||||
margin-top: 80px;
|
||||
padding: 10px;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
}
|
||||
#footer a:link, #footer a:visited {
|
||||
color: #fff;
|
||||
border-bottom: 1px dotted #fff;
|
||||
}
|
||||
#jumbo {
|
||||
background: #c1deef;
|
||||
color: #000;
|
||||
padding: 20px 0;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
#jumbo h1 {
|
||||
font-size: 28px;
|
||||
}
|
||||
#jumbo .btn {
|
||||
border-radius: 0;
|
||||
font-size: 16px;
|
||||
}
|
||||
@@ -1,179 +0,0 @@
|
||||
$(document).ready(function() {
|
||||
|
||||
$('#config-text').keyup(function() {
|
||||
eval($(this).val());
|
||||
});
|
||||
|
||||
$('.configurator input, .configurator select').change(function() {
|
||||
updateConfig();
|
||||
});
|
||||
|
||||
$('.demo i').click(function() {
|
||||
$(this).parent().find('input').click();
|
||||
});
|
||||
|
||||
$('#startDate').daterangepicker({
|
||||
singleDatePicker: true,
|
||||
startDate: moment().subtract(6, 'days')
|
||||
});
|
||||
|
||||
$('#endDate').daterangepicker({
|
||||
singleDatePicker: true,
|
||||
startDate: moment()
|
||||
});
|
||||
|
||||
//updateConfig();
|
||||
|
||||
function updateConfig() {
|
||||
var options = {};
|
||||
|
||||
if ($('#singleDatePicker').is(':checked'))
|
||||
options.singleDatePicker = true;
|
||||
|
||||
if ($('#showDropdowns').is(':checked'))
|
||||
options.showDropdowns = true;
|
||||
|
||||
if ($('#minYear').val().length && $('#minYear').val() != 1)
|
||||
options.minYear = parseInt($('#minYear').val(), 10);
|
||||
|
||||
if ($('#maxYear').val().length && $('#maxYear').val() != 1)
|
||||
options.maxYear = parseInt($('#maxYear').val(), 10);
|
||||
|
||||
if ($('#showWeekNumbers').is(':checked'))
|
||||
options.showWeekNumbers = true;
|
||||
|
||||
if ($('#showISOWeekNumbers').is(':checked'))
|
||||
options.showISOWeekNumbers = true;
|
||||
|
||||
if ($('#timePicker').is(':checked'))
|
||||
options.timePicker = true;
|
||||
|
||||
if ($('#timePicker24Hour').is(':checked'))
|
||||
options.timePicker24Hour = true;
|
||||
|
||||
if ($('#timePickerIncrement').val().length && $('#timePickerIncrement').val() != 1)
|
||||
options.timePickerIncrement = parseInt($('#timePickerIncrement').val(), 10);
|
||||
|
||||
if ($('#timePickerSeconds').is(':checked'))
|
||||
options.timePickerSeconds = true;
|
||||
|
||||
if ($('#autoApply').is(':checked'))
|
||||
options.autoApply = true;
|
||||
|
||||
if ($('#maxSpan').is(':checked'))
|
||||
options.maxSpan = { days: 7 };
|
||||
|
||||
if ($('#ranges').is(':checked')) {
|
||||
options.ranges = {
|
||||
'Today': [moment(), moment()],
|
||||
'Yesterday': [moment().subtract(1, 'days'), moment().subtract(1, 'days')],
|
||||
'Last 7 Days': [moment().subtract(6, 'days'), moment()],
|
||||
'Last 30 Days': [moment().subtract(29, 'days'), moment()],
|
||||
'This Month': [moment().startOf('month'), moment().endOf('month')],
|
||||
'Last Month': [moment().subtract(1, 'month').startOf('month'), moment().subtract(1, 'month').endOf('month')]
|
||||
};
|
||||
}
|
||||
|
||||
if ($('#locale').is(':checked')) {
|
||||
options.locale = {
|
||||
format: 'MM/DD/YYYY',
|
||||
separator: ' - ',
|
||||
applyLabel: 'Apply',
|
||||
cancelLabel: 'Cancel',
|
||||
fromLabel: 'From',
|
||||
toLabel: 'To',
|
||||
customRangeLabel: 'Custom',
|
||||
weekLabel: 'W',
|
||||
daysOfWeek: ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr','Sa'],
|
||||
monthNames: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
|
||||
firstDay: 1
|
||||
};
|
||||
}
|
||||
|
||||
if (!$('#linkedCalendars').is(':checked'))
|
||||
options.linkedCalendars = false;
|
||||
|
||||
if (!$('#autoUpdateInput').is(':checked'))
|
||||
options.autoUpdateInput = false;
|
||||
|
||||
if (!$('#showCustomRangeLabel').is(':checked'))
|
||||
options.showCustomRangeLabel = false;
|
||||
|
||||
if ($('#alwaysShowCalendars').is(':checked'))
|
||||
options.alwaysShowCalendars = true;
|
||||
|
||||
if ($('#parentEl').val().length)
|
||||
options.parentEl = $('#parentEl').val();
|
||||
|
||||
if ($('#startDate').val().length)
|
||||
options.startDate = $('#startDate').val();
|
||||
|
||||
if ($('#endDate').val().length)
|
||||
options.endDate = $('#endDate').val();
|
||||
|
||||
if ($('#minDate').val().length)
|
||||
options.minDate = $('#minDate').val();
|
||||
|
||||
if ($('#maxDate').val().length)
|
||||
options.maxDate = $('#maxDate').val();
|
||||
|
||||
if ($('#opens').val().length && $('#opens').val() != 'right')
|
||||
options.opens = $('#opens').val();
|
||||
|
||||
if ($('#drops').val().length && $('#drops').val() != 'down')
|
||||
options.drops = $('#drops').val();
|
||||
|
||||
if ($('#buttonClasses').val().length && $('#buttonClasses').val() != 'btn btn-sm')
|
||||
options.buttonClasses = $('#buttonClasses').val();
|
||||
|
||||
if ($('#applyButtonClasses').val().length && $('#applyButtonClasses').val() != 'btn-primary')
|
||||
options.applyButtonClasses = $('#applyButtonClasses').val();
|
||||
|
||||
if ($('#cancelButtonClasses').val().length && $('#cancelButtonClasses').val() != 'btn-default')
|
||||
options.cancelClass = $('#cancelButtonClasses').val();
|
||||
|
||||
$('#config-demo').daterangepicker(options, function(start, end, label) { console.log('New date range selected: ' + start.format('YYYY-MM-DD') + ' to ' + end.format('YYYY-MM-DD') + ' (predefined range: ' + label + ')'); });
|
||||
|
||||
if (typeof options.ranges !== 'undefined') {
|
||||
options.ranges = {};
|
||||
}
|
||||
|
||||
var option_text = JSON.stringify(options, null, ' ');
|
||||
|
||||
var replacement = "ranges: {\n"
|
||||
+ " 'Today': [moment(), moment()],\n"
|
||||
+ " 'Yesterday': [moment().subtract(1, 'days'), moment().subtract(1, 'days')],\n"
|
||||
+ " 'Last 7 Days': [moment().subtract(6, 'days'), moment()],\n"
|
||||
+ " 'Last 30 Days': [moment().subtract(29, 'days'), moment()],\n"
|
||||
+ " 'This Month': [moment().startOf('month'), moment().endOf('month')],\n"
|
||||
+ " 'Last Month': [moment().subtract(1, 'month').startOf('month'), moment().subtract(1, 'month').endOf('month')]\n"
|
||||
+ " }";
|
||||
option_text = option_text.replace(new RegExp('"ranges"\: \{\}', 'g'), replacement);
|
||||
|
||||
$('#config-text').val("$('#demo').daterangepicker(" + option_text + ", function(start, end, label) {\n console.log('New date range selected: ' + start.format('YYYY-MM-DD') + ' to ' + end.format('YYYY-MM-DD') + ' (predefined range: ' + label + ')');\n});");
|
||||
|
||||
}
|
||||
|
||||
$(window).scroll(function (event) {
|
||||
var scroll = $(window).scrollTop();
|
||||
if (scroll > 180) {
|
||||
$('.leftcol').css('position', 'fixed');
|
||||
$('.leftcol').css('top', '10px');
|
||||
} else {
|
||||
$('.leftcol').css('position', 'absolute');
|
||||
$('.leftcol').css('top', '180px');
|
||||
}
|
||||
});
|
||||
|
||||
var bg = new Trianglify({
|
||||
x_colors: ["#e1f3fd", "#eeeeee", "#407dbf"],
|
||||
y_colors: 'match_x',
|
||||
width: document.body.clientWidth,
|
||||
height: 150,
|
||||
stroke_width: 0,
|
||||
cell_size: 20
|
||||
});
|
||||
|
||||
$('#jumbo').css('background-image', 'url(' + bg.png() + ')');
|
||||
|
||||
});
|
||||
@@ -1,45 +0,0 @@
|
||||
# TheTool Vue Frontend Framework
|
||||
|
||||
## Components
|
||||
|
||||
### tt-page-title
|
||||
|
||||
The `tt-page-title` is used to create the Title and breadcumbs on the top of the page
|
||||
#### Props
|
||||
|
||||
- `title`: String - The title of the current page. This will be displayed prominently at the top of the breadcrumb navigation bar.
|
||||
- `path`: Array - An array of objects representing the breadcrumb path. Each object in the array should have the following properties:
|
||||
- `text`: String - The display text for the breadcrumb item.
|
||||
- `href`: String - The URL that the breadcrumb item links to.
|
||||
|
||||
#### Usage
|
||||
|
||||
Include the Component on the View
|
||||
```php
|
||||
$additionalJS = ["plugins/vue/tt-components/tt-page-title.js"];
|
||||
```
|
||||
|
||||
Then use it inside the Vue App
|
||||
```vue
|
||||
<tt-page-title :title="'Your Page Title'" :path="[
|
||||
{ text: 'Home', href: '/' },
|
||||
{ text: 'Library', href: '/library' },
|
||||
{ text: 'Data', href: '/library/data' } // Current Page
|
||||
]"></tt-page-title>
|
||||
```
|
||||
|
||||
### tt-loader
|
||||
|
||||
The `tt-loader` is used to display a loader
|
||||
|
||||
#### Usage
|
||||
|
||||
Include the Component on the View
|
||||
```php
|
||||
$additionalJS = ["plugins/vue/tt-components/tt-loader.js"];
|
||||
$additionalCSS = ["plugins/vue/tt-components/css/tt-loader.css"];
|
||||
```
|
||||
|
||||
Then use it inside the Vue App in the container where it should get full width
|
||||
```vue
|
||||
<tt-loader></tt-loader>
|
||||
@@ -15,6 +15,15 @@
|
||||
background-size: 50px 50px;
|
||||
content: "";
|
||||
}
|
||||
|
||||
.tt-table .form-group {
|
||||
margin-bottom: 4px !important;
|
||||
}
|
||||
|
||||
.tt-table-card .page-link {
|
||||
padding: 5px .75rem !important;
|
||||
}
|
||||
|
||||
.tt-table {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
127
public/plugins/vue/tt-components/tt-autocomplete.js
Normal file
127
public/plugins/vue/tt-components/tt-autocomplete.js
Normal file
@@ -0,0 +1,127 @@
|
||||
Vue.component('tt-autocomplete', {
|
||||
template: `
|
||||
<div class="form-group">
|
||||
<slot name="prepend"></slot>
|
||||
<label :for="label">{{ label }}</label>
|
||||
<div class="autocomplete position-relative">
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
v-model="displayValue"
|
||||
@input="onInput"
|
||||
@focus="onFocus"
|
||||
@blur="onBlur"
|
||||
style="padding-right: 30px;"
|
||||
/>
|
||||
<slot name="append"></slot>
|
||||
|
||||
<ul v-show="showSuggestions && displayValue.length > 0 || isLoading"
|
||||
class="dropdown-menu show dropdown-shadow">
|
||||
|
||||
<div v-show="isLoading" class="loader"></div>
|
||||
|
||||
<template v-show="showSuggestions && items.length > 0 && isLoading !== true">
|
||||
<li
|
||||
v-for="(item, index) in items.slice(0, 10)"
|
||||
:key="item.value"
|
||||
:class="{'active': value === item.value}"
|
||||
class="dropdown-item"
|
||||
@click.prevent="selectSuggestion(item)"
|
||||
style="cursor: pointer;"
|
||||
>
|
||||
{{ item.text }}
|
||||
</li>
|
||||
<!-- display more search results available define it more precisely in German -->
|
||||
<li v-show="items.length > 10" class="dropdown-item disabled">
|
||||
Mehr Suchergebnisse vorhanden. Bitte genauer eingeben
|
||||
</li>
|
||||
</template>
|
||||
<li v-show="items.length === 0 && isLoading === false && displayValue.length > 3" class="dropdown-item disabled">
|
||||
Keine Suchergebnisse vorhanden.
|
||||
</li>
|
||||
<li v-show="displayValue.length < 3" class="dropdown-item disabled">
|
||||
Bitte mindestens 3 Zeichen eingeben
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
// TODO: Implement giving the option without the need of an API || need to use computed property to filter the items
|
||||
// TODO: Fix the weirdness with timeout and selecting the suggestion
|
||||
props: {
|
||||
value: {
|
||||
type: [String, Number]
|
||||
},
|
||||
label: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
apiUrl: String,
|
||||
},
|
||||
async mounted() {
|
||||
if (this.value) {
|
||||
const response = await axios.get(`${this.apiUrl}&autocomplete=1&searchedID=${this.value}`);
|
||||
this.displayValue = response.data[0].text;
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
items: [],
|
||||
displayValue: '',
|
||||
isLoading: false,
|
||||
showSuggestions: false,
|
||||
cursor: -1,
|
||||
fetchSuggestionsDebounceTimer: null,
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
value(newValue) {
|
||||
const selectedItem = this.items.find(item => item.value === newValue);
|
||||
this.displayValue = selectedItem ? selectedItem.text : '';
|
||||
},
|
||||
apiUrl() {
|
||||
this.fetchSuggestions();
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
onInput(event) {
|
||||
this.displayValue = event.target.value;
|
||||
this.fetchSuggestions();
|
||||
},
|
||||
onFocus() {
|
||||
this.showSuggestions = true;
|
||||
},
|
||||
onBlur() {
|
||||
setTimeout(() => {
|
||||
this.showSuggestions = false;
|
||||
}, 200);
|
||||
},
|
||||
fetchSuggestions() {
|
||||
if (!this.apiUrl || this.displayValue.length < 3) return;
|
||||
|
||||
this.isLoading = true;
|
||||
clearTimeout(this.fetchSuggestionsDebounceTimer);
|
||||
|
||||
this.fetchSuggestionsDebounceTimer = setTimeout(() => {
|
||||
// Simulate the API call
|
||||
setTimeout(async () => {
|
||||
const response = await axios.get(`${this.apiUrl}&autocomplete=1&q=${this.displayValue}`);
|
||||
if (response.data?.status === 'error') {
|
||||
this.items = [];
|
||||
} else {
|
||||
this.items = response.data
|
||||
}
|
||||
this.isLoading = false;
|
||||
|
||||
this.fetchSuggestionsDebounceTimer = null;
|
||||
}, 100);
|
||||
}, 300); // Adjust the 300ms debounce time as needed
|
||||
}
|
||||
,
|
||||
selectSuggestion(item) {
|
||||
this.$emit('input', item.value);
|
||||
this.displayValue = item.text;
|
||||
this.showSuggestions = false;
|
||||
},
|
||||
},
|
||||
});
|
||||
98
public/plugins/vue/tt-components/tt-datepicker.js
Normal file
98
public/plugins/vue/tt-components/tt-datepicker.js
Normal file
@@ -0,0 +1,98 @@
|
||||
Vue.component('tt-date-picker', {
|
||||
template: `
|
||||
<input type="text" class="form-control form-control-sm" ref="input"
|
||||
style="cursor: pointer;background-color: #ffffff">
|
||||
`, props: ['value'],
|
||||
data() {
|
||||
return {
|
||||
inputValue: '',
|
||||
isInitialized: false,
|
||||
moment: window.moment,
|
||||
locale: {
|
||||
"format": "DD.MM.YYYY HH:mm",
|
||||
"separator": " - ",
|
||||
"applyLabel": "Übernehmen",
|
||||
"cancelLabel": "Löschen",
|
||||
"fromLabel": "Von",
|
||||
"toLabel": "Bis",
|
||||
"customRangeLabel": "Benutzerdefiniert",
|
||||
"weekLabel": "W",
|
||||
"daysOfWeek": [
|
||||
"So",
|
||||
"Mo",
|
||||
"Di",
|
||||
"Mi",
|
||||
"Do",
|
||||
"Fr",
|
||||
"Sa"
|
||||
],
|
||||
"monthNames": [
|
||||
"Januar",
|
||||
"Februar",
|
||||
"März",
|
||||
"April",
|
||||
"Mai",
|
||||
"Juni",
|
||||
"Juli",
|
||||
"August",
|
||||
"September",
|
||||
"Oktober",
|
||||
"November",
|
||||
"Dezember"
|
||||
],
|
||||
"firstDay": 1
|
||||
},
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.isInitialized = true;
|
||||
$(this.$refs.input).daterangepicker({
|
||||
autoUpdateInput: true,
|
||||
startDate: this.value?.from ? this.moment.unix(this.value.from) : undefined,
|
||||
endDate: this.value?.to ? this.moment.unix(this.value.to) : undefined,
|
||||
timePicker: true,
|
||||
timePicker24Hour: true,
|
||||
locale: this.locale,
|
||||
});
|
||||
|
||||
const _this = this;
|
||||
$(this.$refs.input).on('apply.daterangepicker', function (ev, picker) {
|
||||
_this.$emit('input', {
|
||||
from: picker.startDate.unix(),
|
||||
to: picker.endDate.unix()
|
||||
}
|
||||
);
|
||||
$(_this.$refs.input).val(picker.startDate.format(_this.locale.format) + _this.locale.separator + picker.endDate.format(_this.locale.format));
|
||||
});
|
||||
|
||||
function checkIfAppliedElseClear() {
|
||||
$(_this.$refs.input).val('');
|
||||
}
|
||||
|
||||
function clearIfCancelled() {
|
||||
_this.$emit('input', {from: null, to: null});
|
||||
$(_this.$refs.input).val('');
|
||||
}
|
||||
|
||||
$(this.$refs.input).on('cancel.daterangepicker', clearIfCancelled);
|
||||
$(this.$refs.input).on('hide.daterangepicker', checkIfAppliedElseClear);
|
||||
|
||||
// if value from or to is undefined then clear the input field
|
||||
if (!this.value || this.value.from === null || this.value.to === null) {
|
||||
$(_this.$refs.input).val('');
|
||||
}
|
||||
|
||||
},
|
||||
watch: {
|
||||
value: function (newVal, oldVal) {
|
||||
if (this.isInitialized) {
|
||||
if (!newVal || newVal.from === null || newVal.to === null) {
|
||||
$(this.$refs.input).val('');
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
beforeDestroy() {
|
||||
$(this.$refs.input).off('apply.daterangepicker');
|
||||
},
|
||||
})
|
||||
57
public/plugins/vue/tt-components/tt-icon-select.js
Normal file
57
public/plugins/vue/tt-components/tt-icon-select.js
Normal file
@@ -0,0 +1,57 @@
|
||||
Vue.component('tt-icon-select', {
|
||||
props: ['options', 'label', 'value'],
|
||||
data() {
|
||||
return {
|
||||
selectedOption: this.options.find(option => option.value.toString() === this.value) || null,
|
||||
isOpen: false,
|
||||
observer: null,
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
// Dynamically add CSS to disable default dropdown caret
|
||||
const style = document.createElement('style');
|
||||
style.innerHTML = `.tt-select .dropdown-toggle::after { display: none; }`;
|
||||
document.body.appendChild(style);
|
||||
|
||||
document.addEventListener('click', this.handleClick);
|
||||
|
||||
this.observer = new ResizeObserver(this.calculateOffset.bind(this));
|
||||
this.observer.observe(this.$refs.select);
|
||||
this.calculateOffset();
|
||||
},
|
||||
beforeDestroy() {
|
||||
document.removeEventListener('click', this.handleClick);
|
||||
this.observer.disconnect();
|
||||
},
|
||||
methods: {
|
||||
selectOption(option) {
|
||||
this.selectedOption = option;
|
||||
this.$emit('input', option ? option.value.toString() : '');
|
||||
this.isOpen = false;
|
||||
},
|
||||
calculateOffset() {
|
||||
const offset = (this.$refs.select.offsetWidth - 64.41) / 2;
|
||||
this.$refs.select.querySelector('.dropdown-menu').style.left = `${offset}px`;
|
||||
},
|
||||
handleClick() {
|
||||
this.isOpen = this.$refs.selectedIcon.contains(event.target) ? !this.isOpen : false;
|
||||
},
|
||||
},
|
||||
template: `
|
||||
<div class="form-group tt-select" style="user-select: none;margin-bottom: 0">
|
||||
<div class="dropdown" :class="{'show': isOpen}">
|
||||
<i v-if="selectedOption !== null" :class="selectedOption.icon" style="font-size: 24px; cursor: pointer" ref="selectedIcon"></i>
|
||||
<span v-else style="cursor: pointer" ref="selectedIcon">Alle<i class="fas fa-caret-down"></i></span>
|
||||
<div style="display: grid; justify-items: center;" ref="select">
|
||||
<div class="dropdown-menu" :class="{'show': isOpen}" style="min-width: unset !important;">
|
||||
<a class="dropdown-item text-center" href="#" @click.prevent="selectOption(null)">Alle</a>
|
||||
<a v-for="option in options" class="dropdown-item text-center" href="#"
|
||||
@click.prevent="selectOption(option)">
|
||||
<i :class="option.icon" style="font-size: 24px"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
});
|
||||
36
public/plugins/vue/tt-components/tt-input.js
Normal file
36
public/plugins/vue/tt-components/tt-input.js
Normal file
@@ -0,0 +1,36 @@
|
||||
Vue.component('tt-input', {
|
||||
props: {
|
||||
label: String,
|
||||
type: String,
|
||||
placeholder: String,
|
||||
required: Boolean,
|
||||
value: [String, Number],
|
||||
hint: String,
|
||||
additionalProps: Object,
|
||||
sm: {type: Boolean, default: false},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
inputValue: this.value,
|
||||
};
|
||||
},
|
||||
template: `
|
||||
<div class="form-group">
|
||||
<slot name="prepend"></slot>
|
||||
<label
|
||||
v-if="label"
|
||||
:for="label">{{ label }}</label>
|
||||
<input :type="type"
|
||||
:name="label"
|
||||
class="form-control"
|
||||
:class="{'form-control-sm': sm}"
|
||||
:placeholder="placeholder"
|
||||
:required="required"
|
||||
v-bind="additionalProps"
|
||||
v-model="inputValue"
|
||||
@input="$emit('input', $event.target.value)"
|
||||
>
|
||||
<small v-if="hint" class="form-text text-muted">{{ hint }}</small>
|
||||
</div>
|
||||
`
|
||||
});
|
||||
@@ -12,4 +12,4 @@ Vue.component('tt-loader', {
|
||||
</div>
|
||||
</transition>
|
||||
`
|
||||
});
|
||||
});
|
||||
|
||||
48
public/plugins/vue/tt-components/tt-number-range.js
Normal file
48
public/plugins/vue/tt-components/tt-number-range.js
Normal file
@@ -0,0 +1,48 @@
|
||||
Vue.component('tt-number-range', {
|
||||
props: {
|
||||
valueFrom: {type: Number, default: 0},
|
||||
valueTo: {type: Number, default: 0},
|
||||
returnText: {type: Boolean, default: false}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
inputValueFrom: this.valueFrom || '', inputValueTo: this.valueTo || '',
|
||||
};
|
||||
}, watch: {
|
||||
valueFrom(newValue) {
|
||||
this.inputValueFrom = newValue;
|
||||
}, valueTo(newValue) {
|
||||
this.inputValueTo = newValue;
|
||||
}
|
||||
}, methods: {
|
||||
updateValue() {
|
||||
if (this.returnText !== true) {
|
||||
this.$emit('input', {target: {value: {from: this.inputValueFrom, to: this.inputValueTo}}});
|
||||
} else if (this.returnText === true) {
|
||||
if (this.inputValueFrom === '' && this.inputValueTo === '') {
|
||||
this.$emit('input', '');
|
||||
} else if (this.inputValueFrom === '') {
|
||||
this.$emit('input', '<' + this.inputValueTo);
|
||||
} else if (this.inputValueTo === '') {
|
||||
this.$emit('input', '>' + this.inputValueFrom);
|
||||
} else {
|
||||
this.$emit('input', this.inputValueFrom + '-' + this.inputValueTo);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, template: `
|
||||
<div style="display:grid;grid-template-columns: 1fr 1fr;grid-gap: 4px;">
|
||||
<slot name="prepend"></slot>
|
||||
<input type="number"
|
||||
class="form-control form-control-sm"
|
||||
v-model.number="inputValueFrom"
|
||||
@input="updateValue"
|
||||
>
|
||||
<input type="number"
|
||||
class="form-control form-control-sm"
|
||||
v-model.number="inputValueTo"
|
||||
@input="updateValue"
|
||||
>
|
||||
</div>
|
||||
`
|
||||
});
|
||||
30
public/plugins/vue/tt-components/tt-select.js
Normal file
30
public/plugins/vue/tt-components/tt-select.js
Normal file
@@ -0,0 +1,30 @@
|
||||
Vue.component('tt-select', {
|
||||
props: ['options', 'label', 'required', 'value', 'suffix'],
|
||||
data() {
|
||||
return {
|
||||
selectedOption: '',
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.selectedOption = this.value;
|
||||
},
|
||||
watch: {
|
||||
value(newValue) {
|
||||
this.selectedOption = newValue;
|
||||
},
|
||||
},
|
||||
template: `
|
||||
<div class="form-group">
|
||||
<label v-if="label" :for="label">{{ label }}</label>
|
||||
<select class="form-control form-control-sm" :required="required" v-model="selectedOption"
|
||||
@change="$emit('input', $event.target.value)">
|
||||
<template v-for="option of options">
|
||||
<option v-if="['string','number'].includes(typeof option)" :value="option">{{ option }}
|
||||
<template v-if="suffix"> {{ suffix }}</template>
|
||||
</option>
|
||||
<option v-else :value="option.value">{{ option.text }}</option>
|
||||
</template>
|
||||
</select>
|
||||
</div>
|
||||
`
|
||||
});
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
//TODO: tt-autocomplete , tt-select aswell as tt-input should be used for filtering
|
||||
//TODO: Add sorting functionality
|
||||
//TODO: Add export to excel and pdf functionality
|
||||
@@ -7,94 +6,17 @@
|
||||
//TODO: Add new prop serverSide to disable pagination and filtering on the client side
|
||||
//TODO: Add filtering function if serverSide is disabled
|
||||
//TODO: Add JSDoc for various functions and props
|
||||
//TODO: Fixed Table Header
|
||||
|
||||
/**
|
||||
* @typedef {Object} ttTableColumnConfig
|
||||
* @property {string} text - The display text of the column.
|
||||
* @property {string} key - The unique key of the column.
|
||||
* @property {string} filter - Indicates if filtering is enabled for the column.
|
||||
* @property {boolean} sortEnabled - Indicates if sorting is enabled for the column.
|
||||
* @property {boolean} sortable - Indicates if sorting is enabled for the column.
|
||||
* @property {string} class - The CSS class(es) applied to the column.
|
||||
*/
|
||||
|
||||
//TODO: export this to its own file
|
||||
Vue.component('tt-date-range', {
|
||||
template: `
|
||||
<input type="text" class="form-control form-control-sm" ref="input" @click="initialize" style="cursor: pointer;background-color: #ffffff">
|
||||
`, props: ['value'],
|
||||
data() {
|
||||
return {
|
||||
inputValue: '',
|
||||
isInitialized: false,
|
||||
locale: {
|
||||
"format": "DD.MM.YYYY HH:mm",
|
||||
"separator": " - ",
|
||||
"applyLabel": "Übernehmen",
|
||||
"cancelLabel": "Abbrechen",
|
||||
"fromLabel": "Von",
|
||||
"toLabel": "Bis",
|
||||
"customRangeLabel": "Benutzerdefiniert",
|
||||
"weekLabel": "W",
|
||||
"daysOfWeek": [
|
||||
"So",
|
||||
"Mo",
|
||||
"Di",
|
||||
"Mi",
|
||||
"Do",
|
||||
"Fr",
|
||||
"Sa"
|
||||
],
|
||||
"monthNames": [
|
||||
"Januar",
|
||||
"Februar",
|
||||
"März",
|
||||
"April",
|
||||
"Mai",
|
||||
"Juni",
|
||||
"Juli",
|
||||
"August",
|
||||
"September",
|
||||
"Oktober",
|
||||
"November",
|
||||
"Dezember"
|
||||
],
|
||||
"firstDay": 1
|
||||
},
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
initialize() {
|
||||
if (!this.isInitialized) {
|
||||
this.isInitialized = true;
|
||||
$(this.$refs.input).daterangepicker({
|
||||
autoUpdateInput: true,
|
||||
timePicker: true,
|
||||
timePicker24Hour: true,
|
||||
locale: this.locale,
|
||||
});
|
||||
|
||||
this.$refs.input.click();
|
||||
|
||||
const _this = this;
|
||||
$(this.$refs.input).on('apply.daterangepicker', function(ev, picker) {
|
||||
console.log('now emitting chang', picker.startDate.unix(), picker.endDate.unix());
|
||||
_this.$emit('change', {
|
||||
target: {
|
||||
value: {
|
||||
from: picker.startDate.unix() + 7200,
|
||||
to: picker.endDate.unix() + 7200
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
beforeDestroy() {
|
||||
$(this.$refs.input).off('apply.daterangepicker');
|
||||
},
|
||||
})
|
||||
|
||||
Vue.component('tt-table', {
|
||||
template: `
|
||||
<div class="card tt-table-card">
|
||||
@@ -102,16 +24,6 @@ Vue.component('tt-table', {
|
||||
<!-- Top Buttons -->
|
||||
<div
|
||||
style="display:grid; grid-template-columns: auto auto auto auto auto; grid-gap: 8px; padding-bottom: 8px">
|
||||
<button v-if="excelExport" class="btn btn-success" @click="exportToExcel">
|
||||
<i class="fa fa-file
|
||||
"></i>
|
||||
Excel
|
||||
</button>
|
||||
<button v-if="pdfExport" class="btn btn-danger" @click="exportToPdf">
|
||||
<i class="fa fa-file
|
||||
"></i>
|
||||
PDF
|
||||
</button>
|
||||
<slot name="top-buttons"></slot>
|
||||
</div>
|
||||
|
||||
@@ -119,7 +31,15 @@ Vue.component('tt-table', {
|
||||
<nav aria-label="Page navigation">
|
||||
<div
|
||||
style="display:grid; grid-template-columns: 1fr 1fr;padding-bottom: 8px;align-items:center; justify-content: space-between">
|
||||
<h4 style="margin: 0">{{ tableConfig.tableHeader }}</h4>
|
||||
<!-- if excelExport is true, show the export button fontawesome icon excel -->
|
||||
<div style="display:flex;align-items: center;">
|
||||
|
||||
<i v-if="!Object.values(columns).every(column => column.filter === false)" title="Filter zurücksetzen" @click="filters = {}; window.notify('success','Filter zurückgesetzt')" class="fa-solid trash-undo" style="font-size: 24px;margin-right: 8px;cursor: pointer; color: var(--orange)"></i>
|
||||
|
||||
<h4 style="margin: 0">{{ config.tableHeader }}</h4>
|
||||
<i v-if="excelExport" title="EXCEL Export" @click="exportToExcel" class="fa fa-file-excel" style="font-size: 24px;margin-left: 8px;cursor: pointer; color: var(--success)"></i>
|
||||
</div>
|
||||
|
||||
<div v-if="pagination && pagination.total_rows > 0"
|
||||
style="display:grid; grid-template-rows: auto auto; grid-template-columns: auto auto; grid-auto-flow: column; grid-gap: 4px; justify-content: end">
|
||||
<ul class="pagination" style="margin: 0">
|
||||
@@ -131,7 +51,7 @@ Vue.component('tt-table', {
|
||||
</li>
|
||||
<li class="page-item" v-for="pageNumber in pagesToDisplay"
|
||||
v-bind:class="{ 'active disabled': pageNumber === pagination.page }">
|
||||
<a class="page-link disabled" href="#"
|
||||
<a class="page-link" v-bind:class="{ 'active disabled': pageNumber === pagination.page }" href="#"
|
||||
v-on:click.prevent="fetchRows(pageNumber)">{{ pageNumber }}</a>
|
||||
</li>
|
||||
<li class="page-item" v-bind:class="{ disabled: pagination.page === pagination.total_pages }">
|
||||
@@ -144,7 +64,7 @@ Vue.component('tt-table', {
|
||||
</ul>
|
||||
<span class="text-center"
|
||||
v-text="Math.min(pagination.page * pagination.per_page - pagination.per_page + 1, pagination.total_rows)
|
||||
+ ' bis ' + Math.min(pagination.page * pagination.per_page, pagination.total_rows) + ' von ' + pagination.total_rows"></span>
|
||||
+ ' bis ' + Math.min(pagination.page * pagination.per_page, pagination.total_rows) + ' von ' + (pagination.total_rows === pagination.filtered_available ? pagination.total_rows : pagination.filtered_available + ' ('+pagination.total_rows+')')"></span>
|
||||
<select v-model="pagination.per_page" v-on:change="fetchRows(1)" class="form-control form-control-sm">
|
||||
<option value="10">10</option>
|
||||
<option value="25">25</option>
|
||||
@@ -158,20 +78,29 @@ Vue.component('tt-table', {
|
||||
<!-- Table -->
|
||||
<table
|
||||
:class="['table','tt-table','table-condensed',{ 'loading': loading },{ 'table-striped': striped },{ 'table-bordered': bordered },{ 'table-hover': hover },{ 'table-sm': small }]">
|
||||
<thead>
|
||||
<thead style="border-width: 2px">
|
||||
<tr>
|
||||
<th scope="col" v-for="column in columns" :style="'vertical-align: top; text-align: center;' + (column.filter === 'dateRange' ? 'min-width: 260px;' : '')">
|
||||
<div style="text-align:center">{{ column.text }}</div>
|
||||
<input v-if="column.filter === 'search'" type=text v-on:input="applyFilter($event, column.key)"
|
||||
class="form-control form-control-sm">
|
||||
<select v-if="column.filter === 'select'" v-on:change="applyFilter($event, column.key)"
|
||||
class="form-control form-control-sm">
|
||||
<option value="all">Alle</option>
|
||||
<option v-for="filterOption in column.filterOptions" :value="filterOption.value">
|
||||
{{ filterOption.text }}
|
||||
</option>
|
||||
</select>
|
||||
<tt-date-range v-if="column.filter === 'dateRange'" v-on:change="applyFilter($event, column.key)"></tt-date-range>
|
||||
<th scope="col" v-for="column in columns"
|
||||
:style="'vertical-align: top; text-align: center;' + (column.filter === 'dateRange' ? 'min-width: 260px;' : '')">
|
||||
|
||||
<div style="text-align:center; white-space: nowrap;word-break: keep-all;" :style="{ 'cursor': column.sortable ? 'pointer' : 'default' }"
|
||||
@click="column.sortable ? setOrder(column.key) : undefined">
|
||||
{{ column.text }}
|
||||
<i
|
||||
v-if="column.sortable"
|
||||
:class="getSortIconClass(column.key)"></i>
|
||||
</div>
|
||||
|
||||
<tt-input v-if="column.filter === 'search'" sm v-model="filters[column.key]"></tt-input>
|
||||
<tt-icon-select v-else-if="column.filter === 'iconSelect'" :options="column.filterOptions"
|
||||
v-model="filters[column.key]"></tt-icon-select>
|
||||
<tt-number-range v-else-if="column.filter === 'numberRange'" returnText
|
||||
v-model="filters[column.key]"></tt-number-range>
|
||||
<tt-select v-else-if="column.filter === 'select'"
|
||||
:options="[{text: 'Alle', value: undefined}, ...column.filterOptions]"
|
||||
v-model="filters[column.key]"></tt-select>
|
||||
<tt-date-picker v-else-if="column.filter === 'date'" v-model="filters[column.key]"></tt-date-picker>
|
||||
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -182,37 +111,96 @@ Vue.component('tt-table', {
|
||||
<td :colspan="Object.keys(columns).length" :rowspan="5" class="text-center">Keine Ergebnisse!</td>
|
||||
</tr>
|
||||
|
||||
<tr v-else-if="pagination === null || rows === null" style="height: 150px">
|
||||
<tr v-else-if="(pagination === null && ssr === true) || rows === null"
|
||||
style="height: 150px">
|
||||
<td :colspan="Object.keys(columns).length" class="text-center">Laden...</td>
|
||||
</tr>
|
||||
|
||||
<tr v-for="row in rows" :class="typeof tableConfig.customRowClass === 'function' ? tableConfig.customRowClass(row) : ''">
|
||||
<template v-for="(value, key) in columns">
|
||||
<td :class="columns[key].class">
|
||||
<tr v-for="row in (ssr === false ? computedRows : rows)"
|
||||
:class="typeof config.customRowClass === 'function' ? config.customRowClass(row) : ''">
|
||||
<template v-for="(column, key) in columns">
|
||||
<td :class="{ 'text-center': column.filter === 'iconSelect', [columns[key].class]: true }">
|
||||
<slot :name="key.toLowerCase()" :value="row[key]" :row="row">
|
||||
<span
|
||||
v-html="row[key] === null || typeof row[key] === 'undefined' ? null : row[key]?.toString()?.replace('\\n', '<br>')"></span>
|
||||
|
||||
<span v-if="column.filter === 'date'">{{ row[key] ? (moment.unix(row[key]).isValid() ? moment.unix(row[key]).format('DD.MM.YYYY HH:mm') : moment(row[key]).format('DD.MM.YYYY HH:mm')) : '' }}</span>
|
||||
<i v-else-if="column.filter === 'iconSelect'" :class="columns[key].filterOptions.find(option => option.value.toString() === row[key].toString())?.icon"></i>
|
||||
<span v-else v-html="row[key] === null || typeof row[key] === 'undefined' ? null : row[key]?.toString()?.replace('\\n', '<br>')"></span>
|
||||
|
||||
</slot>
|
||||
</td>
|
||||
</template>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<!-- Pagination Controls -->
|
||||
<nav aria-label="Page navigation">
|
||||
<div style="display:grid; grid-template-columns: 1fr;padding-bottom: 8px;align-items:center; justify-content: end">
|
||||
|
||||
<div v-if="pagination && pagination.total_rows > 0"
|
||||
style="display:grid; grid-template-rows: auto auto; grid-template-columns: auto auto; grid-auto-flow: column; grid-gap: 4px; justify-content: end">
|
||||
|
||||
<span class="text-center"
|
||||
v-text="Math.min(pagination.page * pagination.per_page - pagination.per_page + 1, pagination.total_rows)
|
||||
+ ' bis ' + Math.min(pagination.page * pagination.per_page, pagination.total_rows) + ' von ' + (pagination.total_rows === pagination.filtered_available ? pagination.total_rows : pagination.filtered_available + ' ('+pagination.total_rows+')')"></span>
|
||||
|
||||
|
||||
<ul class="pagination" style="margin: 0">
|
||||
<li class="page-item" v-bind:class="{ disabled: pagination.page === 1 }">
|
||||
<a class="page-link" href="#" v-on:click.prevent="fetchRows(1)" aria-label="First">
|
||||
<span aria-hidden="true">«</span>
|
||||
<span class="sr-only">First</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="page-item" v-for="pageNumber in pagesToDisplay"
|
||||
v-bind:class="{ 'active disabled': pageNumber === pagination.page }">
|
||||
<a class="page-link" v-bind:class="{ 'active disabled': pageNumber === pagination.page }" href="#"
|
||||
v-on:click.prevent="fetchRows(pageNumber)">{{ pageNumber }}</a>
|
||||
</li>
|
||||
<li class="page-item" v-bind:class="{ disabled: pagination.page === pagination.total_pages }">
|
||||
<a class="page-link" href="#" v-on:click.prevent="fetchRows(pagination.total_pages)"
|
||||
aria-label="Last">
|
||||
<span aria-hidden="true">»</span>
|
||||
<span class="sr-only">Last</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<span class="text-center">Einträge pro Seite</span>
|
||||
<select v-model="pagination.per_page" v-on:change="fetchRows(1)" class="form-control form-control-sm">
|
||||
<option value="10">10</option>
|
||||
<option value="25">25</option>
|
||||
<option value="50">50</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
`, props: {
|
||||
fetchUrl: String, striped: {
|
||||
type: Boolean, default: true
|
||||
}, bordered: {
|
||||
type: Boolean, default: true
|
||||
}, hover: {
|
||||
type: Boolean, default: true
|
||||
}, small: Boolean, excelExport: Boolean, pdfExport: Boolean, tableConfig: {
|
||||
type: Object, default: () => ({})
|
||||
}
|
||||
fetchUrl: String,
|
||||
striped: {type: Boolean, default: true},
|
||||
bordered: {type: Boolean, default: true},
|
||||
hover: {type: Boolean, default: true},
|
||||
sticky: {type: Boolean, default: true},
|
||||
small: {type: Boolean, default: true},
|
||||
excelExport: {type: Boolean, default: false},
|
||||
config: {type: Object, default: () => ({}), required: true},
|
||||
ssr: {type: Boolean, default: false}
|
||||
}, data() {
|
||||
return {
|
||||
loading: false, rows: null, pagination: null, filters: {}, debounceTimeout: null, latestFetchTimestamp: null
|
||||
window: window,
|
||||
moment: window.moment,
|
||||
XLSX: window.XLSX,
|
||||
loading: false,
|
||||
rows: null,
|
||||
rawRows: null,
|
||||
pagination: null,
|
||||
filters: {},
|
||||
debounceTimeout: null,
|
||||
latestFetchTimestamp: null,
|
||||
order: {
|
||||
key: null,
|
||||
order: 'asc' // default sort order
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
@@ -237,14 +225,30 @@ Vue.component('tt-table', {
|
||||
* @param {number} page The page number to fetch data for.
|
||||
* @async
|
||||
*/
|
||||
async fetchData(page= 0) {
|
||||
async fetchData(page = 0) {
|
||||
try {
|
||||
if (this.ssr === false) {
|
||||
const response = await axios.get(this.fetchUrl);
|
||||
this.rawRows = response.data
|
||||
|
||||
|
||||
this.pagination = {
|
||||
page: page++,
|
||||
per_page: this.pagination?.per_page ? parseInt(this.pagination.per_page) : 10,
|
||||
total_rows: this.rawRows.length,
|
||||
total_pages: this.rawRows.length / this.pagination?.per_page,
|
||||
filtered_available: this.rawRows.length
|
||||
};
|
||||
this.loading = false;
|
||||
return
|
||||
}
|
||||
|
||||
const fetchTimestamp = Date.now();
|
||||
this.latestFetchTimestamp = fetchTimestamp;
|
||||
const response = await axios.post(this.fetchUrl, {
|
||||
pagination: {
|
||||
page: Math.max(page, 1), per_page: this.pagination?.per_page ? this.pagination.per_page : 10,
|
||||
}, filters: this.filters,
|
||||
}, filters: this.filters, order: this.order
|
||||
});
|
||||
|
||||
if (fetchTimestamp !== this.latestFetchTimestamp) return;
|
||||
@@ -276,12 +280,89 @@ Vue.component('tt-table', {
|
||||
},
|
||||
applyFilter(event, key) {
|
||||
this.$set(this.filters, key, event.target.value); // Ensure reactivity
|
||||
},
|
||||
saveSettingsToLocalStorage() {
|
||||
localStorage.setItem(`tt-table-${this.config.key}`, JSON.stringify({
|
||||
filters: this.filters,
|
||||
pagination: this.pagination,
|
||||
}));
|
||||
},
|
||||
parseSettingsFromLocalStorage() {
|
||||
const settings = JSON.parse(localStorage.getItem(`tt-table-${this.config.key}`));
|
||||
if (settings) {
|
||||
this.filters = settings.filters;
|
||||
this.pagination = settings.pagination;
|
||||
}
|
||||
},
|
||||
setOrder(key) {
|
||||
if (this.order.key === key) {
|
||||
// if current order is desc then set key to null
|
||||
if (this.order.order === 'desc') {
|
||||
this.order.key = null;
|
||||
this.order.order = 'asc';
|
||||
} else {
|
||||
// if current order is asc then set order to desc
|
||||
this.order.order = 'desc';
|
||||
}
|
||||
} else {
|
||||
// Set new key and default to ascending
|
||||
this.order.key = key;
|
||||
this.order.order = 'asc';
|
||||
}
|
||||
},
|
||||
getSortIconClass(key) {
|
||||
if (this.order.key === key) {
|
||||
return this.order.order === 'asc' ? 'fa fa-sort-asc' : 'fa fa-sort-desc';
|
||||
}
|
||||
return 'fa fa-sort'; // default icon when not sorted
|
||||
},
|
||||
exportToExcel() {
|
||||
const wb = this.XLSX.utils.book_new();
|
||||
|
||||
let data = typeof this.config.customExcelProcessor === 'function' ? this.config.customExcelProcessor(this.rawRows) : JSON.parse(JSON.stringify(this.rawRows));
|
||||
|
||||
// convert all columns with date with momentjs
|
||||
|
||||
data = data.map(row => {
|
||||
for (const key in row) {
|
||||
if (this.columns[key].filter === 'date') {
|
||||
row[key] = this.moment(row[key]).format('DD.MM.YYYY HH:mm');
|
||||
}
|
||||
}
|
||||
return row;
|
||||
});
|
||||
|
||||
const ws = this.XLSX.utils.json_to_sheet(data);
|
||||
|
||||
for (const cell in ws) {
|
||||
if (cell.startsWith('!')) continue; // Skip non-cell properties like '!ref'
|
||||
const cellValue = ws[cell].v;
|
||||
if (cellValue.toString().includes('\n')) {
|
||||
console.log('Found newline in cell:', cell, cellValue);
|
||||
if (!ws[cell].s) ws[cell].s = {};
|
||||
ws[cell].s.alignment = {wrapText: true, vertical: 'center'};
|
||||
} else {
|
||||
ws[cell].s = {alignment: {vertical: 'center'}};
|
||||
}
|
||||
}
|
||||
|
||||
this.XLSX.utils.book_append_sheet(wb, ws, "Sheet1");
|
||||
this.XLSX.writeFile(wb, 'export.xlsx');
|
||||
}
|
||||
}, watch: {
|
||||
filters: {
|
||||
handler: function () {
|
||||
this.fetchRows(this.pagination.page, true).then();
|
||||
if (this.ssr) {
|
||||
this.fetchRows(this.pagination?.page || 1, true).then();
|
||||
}
|
||||
this.saveSettingsToLocalStorage();
|
||||
}, deep: true
|
||||
},
|
||||
pagination: {
|
||||
handler: function () {
|
||||
this.saveSettingsToLocalStorage();
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
}, computed: {
|
||||
/**
|
||||
@@ -289,7 +370,7 @@ Vue.component('tt-table', {
|
||||
* @return {ttTableColumnConfig} The columns configuration.
|
||||
*/
|
||||
columns() {
|
||||
return this.tableConfig.headers.reduce((columns, column) => {
|
||||
return this.config.headers.reduce((columns, column) => {
|
||||
if (!column.key) {
|
||||
console.warn('WARN: tt-table: Column text or key is not defined:', column);
|
||||
return columns; // Continue to the next iteration without modifying the accumulator
|
||||
@@ -299,7 +380,7 @@ Vue.component('tt-table', {
|
||||
key: column.key,
|
||||
filter: column.filter !== undefined ? column.filter : 'search',
|
||||
filterOptions: column.filterOptions || undefined,
|
||||
sortEnabled: column.sortEnabled !== undefined ? column.sortEnabled : true,
|
||||
sortable: column.sortable !== undefined ? column.sortable : true,
|
||||
class: column.class !== undefined ? column.class : ''
|
||||
};
|
||||
return columns;
|
||||
@@ -307,7 +388,7 @@ Vue.component('tt-table', {
|
||||
|
||||
}, pagesToDisplay() {
|
||||
let range = 2; // Number of pages before and after the current page
|
||||
let start = (this.pagination.page < 4 ? 1 : this.pagination.page - range) ;
|
||||
let start = (this.pagination.page < 4 ? 1 : this.pagination.page - range);
|
||||
let end = (this.pagination.page + range > this.pagination.total_pages ? this.pagination.total_pages : this.pagination.page + range);
|
||||
if (end < 5) end = 5;
|
||||
|
||||
@@ -326,12 +407,160 @@ Vue.component('tt-table', {
|
||||
}
|
||||
|
||||
return pagesArray;
|
||||
},
|
||||
computedRows() {
|
||||
if (!this.rawRows || this.ssr === true) return null;
|
||||
console.time('Filtering and pagination');
|
||||
|
||||
function handleRangeFilter(filter, value) {
|
||||
if (filter[0] === '<') {
|
||||
const bound = parseFloat(filter.slice(1));
|
||||
return value <= bound;
|
||||
} else if (filter[0] === '>') {
|
||||
const bound = parseFloat(filter.slice(1));
|
||||
return value >= bound;
|
||||
} else if (filter.includes('-')) {
|
||||
const bounds = filter.split('-').map(Number);
|
||||
return value >= bounds[0] && value <= bounds[1];
|
||||
}
|
||||
return false; // Fallback for any non-matching cases
|
||||
}
|
||||
|
||||
|
||||
const data = this.rawRows;
|
||||
const output = [];
|
||||
const filters = this.filters;
|
||||
console.log(filters)
|
||||
const filtersLength = Object.keys(filters).length;
|
||||
const headers = this.columns;
|
||||
const dataLength = data.length;
|
||||
|
||||
for (var i = 0; i < dataLength; ++i) {
|
||||
const row = data[i];
|
||||
if (Object.keys(filters).length === 0) {
|
||||
output.push(row);
|
||||
continue;
|
||||
}
|
||||
|
||||
let match = true;
|
||||
for (var j = 0; j < filtersLength; ++j) {
|
||||
// find header by filter key
|
||||
const filter = Object.keys(filters)[j];
|
||||
const header = headers[filter];
|
||||
|
||||
const filterValue = filters[filter];
|
||||
if (filterValue === '') continue;
|
||||
if (filterValue === "!") continue;
|
||||
|
||||
if (header.filter === 'search') {
|
||||
|
||||
const isNegated = filterValue.startsWith('!');
|
||||
const targetValue = (row[header.key] ? row[header.key].toString().toLowerCase() : '');
|
||||
const substrings = (isNegated ? filterValue.slice(1) : filterValue).split(' ').map(s => s.toLowerCase());
|
||||
|
||||
let substringMatch = true;
|
||||
for (var k = 0, klen = substrings.length; k < klen; ++k) {
|
||||
if (!targetValue.includes(substrings[k])) {
|
||||
substringMatch = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ((isNegated && substringMatch) || (!isNegated && !substringMatch)) {
|
||||
match = false;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
} else if (header.filter === 'numberRange') {
|
||||
const rangeMatch = handleRangeFilter(filterValue, parseFloat(row[header.key]));
|
||||
if (!rangeMatch) {
|
||||
match = false;
|
||||
break;
|
||||
}
|
||||
} else if (header.filter === 'select' || header.filter === 'iconSelect') {
|
||||
if (filterValue === '') continue;
|
||||
if (filterValue !== row[header.key].toString()) {
|
||||
match = false;
|
||||
break;
|
||||
}
|
||||
} else if (header.filter === 'date') {
|
||||
if (!filterValue.from || !filterValue.to) continue;
|
||||
|
||||
let rowDate = new Date(row[header.key]).getTime() / 1000;
|
||||
|
||||
if (rowDate < filterValue.from || rowDate > filterValue.to) {
|
||||
match = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (match) {
|
||||
output.push(row);
|
||||
}
|
||||
}
|
||||
|
||||
// order the output using this.order in most performant way
|
||||
if (this.order.key) {
|
||||
function naturalSort(a, b) {
|
||||
return a.localeCompare(b, undefined, {numeric: true, sensitivity: 'base'});
|
||||
}
|
||||
|
||||
const isDateColumn = this.columns[this.order.key].filter === 'date';
|
||||
|
||||
output.sort((a, b) => {
|
||||
|
||||
let valueA = isDateColumn ? new Date(a[this.order.key]).getTime() : a[this.order.key];
|
||||
let valueB = isDateColumn ? new Date(b[this.order.key]).getTime() : b[this.order.key];
|
||||
|
||||
if (valueA === valueB) return 0;
|
||||
|
||||
if (this.order.order === 'asc') {
|
||||
return typeof valueA === 'string' ? naturalSort(valueA, valueB) : (valueA < valueB ? -1 : 1);
|
||||
} else {
|
||||
return typeof valueA === 'string' ? naturalSort(valueB, valueA) : (valueA > valueB ? -1 : 1);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
console.timeEnd('Filtering and pagination');
|
||||
// Pagination and slice logic
|
||||
this.pagination.total_pages = Math.ceil(output.length / this.pagination.per_page);
|
||||
this.pagination.filtered_available = output.length;
|
||||
const perPage = this.pagination.per_page;
|
||||
const page = this.pagination.page;
|
||||
const startIndex = (page - 1) * perPage;
|
||||
const endIndex = startIndex + perPage;
|
||||
this.rows = output.slice(startIndex, Math.min(endIndex, output.length));
|
||||
return this.rows;
|
||||
}
|
||||
|
||||
}, mounted() {
|
||||
if(this.tableConfig.defaultPageSize) {
|
||||
this.pagination = {page: 1, per_page: this.tableConfig.defaultPageSize, total_rows: null, total_pages: 1};
|
||||
},
|
||||
beforeMount() {
|
||||
this.parseSettingsFromLocalStorage();
|
||||
},
|
||||
mounted() {
|
||||
if (this.config.hasOwnProperty('defaultPageSize') && this.config.defaultPageSize) {
|
||||
this.pagination = {page: 1, per_page: this.config.defaultPageSize, total_rows: null, total_pages: 1};
|
||||
}
|
||||
|
||||
// if ssr is true then register watcher for order
|
||||
if (this.ssr) {
|
||||
this.$watch('order', () => {
|
||||
this.fetchRows(this.pagination.page, true).then();
|
||||
}, {deep: true});
|
||||
}
|
||||
|
||||
// if sticky is true then add style element to style thead sticky
|
||||
if (this.sticky) {
|
||||
const style = document.createElement('style');
|
||||
style.innerHTML = `table thead th { position: sticky; top: 0; z-index: 1; background-color: white; }`;
|
||||
document.head.appendChild(style);
|
||||
}
|
||||
|
||||
this.fetchRows().then();
|
||||
},
|
||||
})
|
||||
28
public/plugins/vue/tt-components/tt-textarea.js
Normal file
28
public/plugins/vue/tt-components/tt-textarea.js
Normal file
@@ -0,0 +1,28 @@
|
||||
Vue.component('tt-textarea', {
|
||||
props: {
|
||||
label: String,
|
||||
required: Boolean,
|
||||
value: String,
|
||||
rows: {
|
||||
type: String,
|
||||
default: "3",
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
currentText: this.value,
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
value(newValue) {
|
||||
this.currentText = newValue;
|
||||
},
|
||||
},
|
||||
template: `
|
||||
<div class="form-group">
|
||||
<label :for="label">{{ label }}</label>
|
||||
<textarea class="form-control" :id="label" :required="required" v-model="currentText"
|
||||
@input="$emit('input', $event.target.value)" :rows="rows"></textarea>
|
||||
</div>
|
||||
`
|
||||
});
|
||||
3
public/plugins/xlsx/xlsx.min.js
vendored
Normal file
3
public/plugins/xlsx/xlsx.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
public/plugins/xlsx/xlsx.min.js.map.js
Normal file
1
public/plugins/xlsx/xlsx.min.js.map.js
Normal file
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user