Без опису
Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

app-logistics-dashboard.js 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496
  1. /**
  2. * Logistics Dashboard
  3. */
  4. 'use strict';
  5. document.addEventListener('DOMContentLoaded', function (e) {
  6. let labelColor, headingColor, borderColor, legendColor, fontFamily;
  7. labelColor = config.colors.textMuted;
  8. headingColor = config.colors.headingColor;
  9. borderColor = config.colors.borderColor;
  10. legendColor = config.colors.bodyColor;
  11. fontFamily = config.fontFamily;
  12. // Chart Colors
  13. const chartColors = {
  14. donut: {
  15. series1: config.colors.success,
  16. series2: 'color-mix(in sRGB, ' + config.colors.success + ' 80%, ' + config.colors.cardColor + ')',
  17. series3: 'color-mix(in sRGB, ' + config.colors.success + ' 60%, ' + config.colors.cardColor + ')',
  18. series4: 'color-mix(in sRGB, ' + config.colors.success + ' 40%, ' + config.colors.cardColor + ')'
  19. },
  20. line: {
  21. series1: config.colors.warning,
  22. series2: config.colors.primary,
  23. series3: '#7367f029'
  24. }
  25. };
  26. // Shipment statistics Chart
  27. // --------------------------------------------------------------------
  28. const shipmentEl = document.querySelector('#shipmentStatisticsChart'),
  29. shipmentConfig = {
  30. series: [
  31. {
  32. name: 'Shipment',
  33. type: 'column',
  34. data: [38, 45, 33, 38, 32, 50, 48, 40, 42, 37]
  35. },
  36. {
  37. name: 'Delivery',
  38. type: 'line',
  39. data: [23, 28, 23, 32, 28, 44, 32, 38, 26, 34]
  40. }
  41. ],
  42. chart: {
  43. height: 320,
  44. type: 'line',
  45. stacked: false,
  46. parentHeightOffset: 0,
  47. toolbar: { show: false },
  48. zoom: { enabled: false }
  49. },
  50. markers: {
  51. size: 5,
  52. colors: [config.colors.white],
  53. strokeColors: chartColors.line.series2,
  54. hover: { size: 6 },
  55. borderRadius: 4
  56. },
  57. stroke: {
  58. curve: 'smooth',
  59. width: [0, 3],
  60. lineCap: 'round'
  61. },
  62. legend: {
  63. show: true,
  64. position: 'bottom',
  65. markers: {
  66. size: 4,
  67. offsetX: -3,
  68. strokeWidth: 0
  69. },
  70. height: 40,
  71. itemMargin: {
  72. horizontal: 10,
  73. vertical: 0
  74. },
  75. fontSize: '15px',
  76. fontFamily: fontFamily,
  77. fontWeight: 400,
  78. labels: {
  79. colors: headingColor,
  80. useSeriesColors: false
  81. },
  82. offsetY: 5
  83. },
  84. grid: {
  85. strokeDashArray: 8,
  86. borderColor
  87. },
  88. colors: [chartColors.line.series1, chartColors.line.series2],
  89. fill: {
  90. opacity: [1, 1]
  91. },
  92. plotOptions: {
  93. bar: {
  94. columnWidth: '30%',
  95. startingShape: 'rounded',
  96. endingShape: 'rounded',
  97. borderRadius: 4
  98. }
  99. },
  100. dataLabels: { enabled: false },
  101. xaxis: {
  102. tickAmount: 10,
  103. categories: ['1 Jan', '2 Jan', '3 Jan', '4 Jan', '5 Jan', '6 Jan', '7 Jan', '8 Jan', '9 Jan', '10 Jan'],
  104. labels: {
  105. style: {
  106. colors: labelColor,
  107. fontSize: '13px',
  108. fontFamily: fontFamily,
  109. fontWeight: 400
  110. }
  111. },
  112. axisBorder: { show: false },
  113. axisTicks: { show: false }
  114. },
  115. yaxis: {
  116. tickAmount: 4,
  117. min: 0,
  118. max: 50,
  119. labels: {
  120. style: {
  121. colors: labelColor,
  122. fontSize: '13px',
  123. fontFamily: fontFamily,
  124. fontWeight: 400
  125. },
  126. formatter: function (val) {
  127. return val + '%';
  128. }
  129. }
  130. },
  131. responsive: [
  132. {
  133. breakpoint: 1400,
  134. options: {
  135. chart: { height: 320 },
  136. xaxis: { labels: { style: { fontSize: '10px' } } },
  137. legend: {
  138. fontSize: '13px'
  139. }
  140. }
  141. },
  142. {
  143. breakpoint: 1025,
  144. options: {
  145. chart: { height: 415 },
  146. plotOptions: { bar: { columnWidth: '50%' } }
  147. }
  148. },
  149. {
  150. breakpoint: 982,
  151. options: { plotOptions: { bar: { columnWidth: '30%' } } }
  152. },
  153. {
  154. breakpoint: 480,
  155. options: {
  156. chart: { height: 250 },
  157. legend: { offsetY: 7 }
  158. }
  159. }
  160. ]
  161. };
  162. if (typeof shipmentEl !== undefined && shipmentEl !== null) {
  163. const shipment = new ApexCharts(shipmentEl, shipmentConfig);
  164. shipment.render();
  165. }
  166. // Reasons for delivery exceptions Chart
  167. // --------------------------------------------------------------------
  168. const deliveryExceptionsChartE1 = document.querySelector('#deliveryExceptionsChart'),
  169. deliveryExceptionsChartConfig = {
  170. chart: {
  171. height: 396,
  172. parentHeightOffset: 0,
  173. type: 'donut'
  174. },
  175. labels: ['Incorrect address', 'Weather conditions', 'Federal Holidays', 'Damage during transit'],
  176. series: [13, 25, 22, 40],
  177. colors: [
  178. chartColors.donut.series1,
  179. chartColors.donut.series2,
  180. chartColors.donut.series3,
  181. chartColors.donut.series4
  182. ],
  183. stroke: {
  184. width: 0
  185. },
  186. dataLabels: {
  187. enabled: false,
  188. formatter: function (val, opt) {
  189. return parseInt(val) + '%';
  190. }
  191. },
  192. legend: {
  193. show: true,
  194. position: 'bottom',
  195. offsetY: 10,
  196. markers: {
  197. size: 5
  198. },
  199. itemMargin: {
  200. horizontal: 15,
  201. vertical: 5
  202. },
  203. fontSize: '13px',
  204. fontFamily: fontFamily,
  205. fontWeight: 400,
  206. labels: {
  207. colors: headingColor,
  208. useSeriesColors: false
  209. }
  210. },
  211. tooltip: {
  212. theme: false
  213. },
  214. grid: {
  215. padding: {
  216. top: 15
  217. }
  218. },
  219. plotOptions: {
  220. pie: {
  221. donut: {
  222. size: '77%',
  223. labels: {
  224. show: true,
  225. value: {
  226. fontSize: '24px',
  227. fontFamily: fontFamily,
  228. color: headingColor,
  229. fontWeight: 500,
  230. offsetY: -20,
  231. formatter: function (val) {
  232. return parseInt(val) + '%';
  233. }
  234. },
  235. name: {
  236. offsetY: 30,
  237. fontFamily: fontFamily
  238. },
  239. total: {
  240. show: true,
  241. fontSize: '15px',
  242. fontFamily: fontFamily,
  243. color: legendColor,
  244. label: 'AVG. Exceptions',
  245. formatter: function (w) {
  246. return '30%';
  247. }
  248. }
  249. }
  250. }
  251. }
  252. },
  253. responsive: [
  254. {
  255. breakpoint: 1025,
  256. options: {
  257. chart: {
  258. height: 380
  259. }
  260. }
  261. }
  262. ]
  263. };
  264. if (typeof deliveryExceptionsChartE1 !== undefined && deliveryExceptionsChartE1 !== null) {
  265. const deliveryExceptionsChart = new ApexCharts(deliveryExceptionsChartE1, deliveryExceptionsChartConfig);
  266. deliveryExceptionsChart.render();
  267. }
  268. // DataTable (js)
  269. // --------------------------------------------------------------------
  270. const dt_dashboard_table = document.querySelector('.dt-route-vehicles');
  271. // On route vehicles DataTable
  272. if (dt_dashboard_table) {
  273. var dt_dashboard = new DataTable(dt_dashboard_table, {
  274. ajax: assetsPath + 'json/logistics-dashboard.json',
  275. columns: [
  276. { data: 'id' },
  277. { data: 'id', orderable: false, render: DataTable.render.select() },
  278. { data: 'location' },
  279. { data: 'start_city' },
  280. { data: 'end_city' },
  281. { data: 'warnings' },
  282. { data: 'progress' }
  283. ],
  284. columnDefs: [
  285. {
  286. // For Responsive
  287. className: 'control',
  288. orderable: false,
  289. searchable: false,
  290. responsivePriority: 2,
  291. targets: 0,
  292. render: function (data, type, full, meta) {
  293. return '';
  294. }
  295. },
  296. {
  297. // For Checkboxes
  298. targets: 1,
  299. orderable: false,
  300. searchable: false,
  301. responsivePriority: 3,
  302. checkboxes: true,
  303. render: function () {
  304. return '<input type="checkbox" class="dt-checkboxes form-check-input">';
  305. },
  306. checkboxes: {
  307. selectAllRender: '<input type="checkbox" class="form-check-input">'
  308. }
  309. },
  310. {
  311. targets: 2,
  312. responsivePriority: 1,
  313. render: (data, type, full) => {
  314. const location = full['location'];
  315. return `
  316. <div class="d-flex justify-content-start align-items-center user-name">
  317. <div class="avatar-wrapper">
  318. <div class="avatar me-4">
  319. <span class="avatar-initial rounded-circle bg-label-secondary">
  320. <i class="icon-base bx bxs-truck icon-lg"></i>
  321. </span>
  322. </div>
  323. </div>
  324. <div class="d-flex flex-column">
  325. <a class="text-heading text-nowrap fw-medium" href="${baseUrl}app/logistics/fleet">VOL-${location}</a>
  326. </div>
  327. </div>
  328. `;
  329. }
  330. },
  331. {
  332. targets: 3,
  333. render: (data, type, full) => {
  334. const { start_city, start_country } = full;
  335. return `
  336. <div class="text-body">
  337. ${start_city}, ${start_country}
  338. </div>
  339. `;
  340. }
  341. },
  342. {
  343. targets: 4,
  344. render: (data, type, full) => {
  345. const { end_city, end_country } = full;
  346. return `
  347. <div class="text-body">
  348. ${end_city}, ${end_country}
  349. </div>
  350. `;
  351. }
  352. },
  353. {
  354. targets: -2,
  355. render: (data, type, full) => {
  356. const statusNumber = full['warnings'];
  357. const status = {
  358. 1: { title: 'No Warnings', class: 'bg-label-success' },
  359. 2: { title: 'Temperature Not Optimal', class: 'bg-label-warning' },
  360. 3: { title: 'Ecu Not Responding', class: 'bg-label-danger' },
  361. 4: { title: 'Oil Leakage', class: 'bg-label-info' },
  362. 5: { title: 'Fuel Problems', class: 'bg-label-primary' }
  363. };
  364. const warning = status[statusNumber];
  365. if (!warning) {
  366. return data;
  367. }
  368. return `
  369. <span class="badge rounded ${warning.class}">
  370. ${warning.title}
  371. </span>
  372. `;
  373. }
  374. },
  375. {
  376. targets: -1,
  377. render: (data, type, full) => {
  378. const progress = full['progress'];
  379. return `
  380. <div class="d-flex align-items-center">
  381. <div class="progress w-100" style="height: 8px;">
  382. <div
  383. class="progress-bar"
  384. role="progressbar"
  385. style="width: ${progress}%"
  386. aria-valuenow="${progress}"
  387. aria-valuemin="0"
  388. aria-valuemax="100">
  389. </div>
  390. </div>
  391. <div class="text-body ms-3">${progress}%</div>
  392. </div>
  393. `;
  394. }
  395. }
  396. ],
  397. select: {
  398. style: 'multi',
  399. selector: 'td:nth-child(2)'
  400. },
  401. order: [2, 'asc'],
  402. layout: {
  403. topStart: {
  404. rowClass: '',
  405. features: []
  406. },
  407. topEnd: {},
  408. bottomStart: {
  409. rowClass: 'row mx-3 justify-content-between',
  410. features: ['info']
  411. },
  412. bottomEnd: 'paging'
  413. },
  414. lengthMenu: [5],
  415. language: {
  416. paginate: {
  417. next: '<i class="icon-base bx bx-chevron-right scaleX-n1-rtl icon-18px"></i>',
  418. previous: '<i class="icon-base bx bx-chevron-left scaleX-n1-rtl icon-18px"></i>',
  419. first: '<i class="icon-base bx bx-chevrons-left scaleX-n1-rtl icon-18px"></i>',
  420. last: '<i class="icon-base bx bx-chevrons-right scaleX-n1-rtl icon-18px"></i>'
  421. }
  422. },
  423. responsive: {
  424. details: {
  425. display: DataTable.Responsive.display.modal({
  426. header: function (row) {
  427. const data = row.data();
  428. return 'Details of ' + data['location'];
  429. }
  430. }),
  431. type: 'column',
  432. renderer: function (api, rowIdx, columns) {
  433. const data = columns
  434. .map(function (col) {
  435. return col.title !== '' // Do not show row in modal popup if title is blank (for check box)
  436. ? `<tr data-dt-row="${col.rowIndex}" data-dt-column="${col.columnIndex}">
  437. <td>${col.title}:</td>
  438. <td>${col.data}</td>
  439. </tr>`
  440. : '';
  441. })
  442. .join('');
  443. if (data) {
  444. const div = document.createElement('div');
  445. div.classList.add('table-responsive');
  446. const table = document.createElement('table');
  447. div.appendChild(table);
  448. table.classList.add('table');
  449. const tbody = document.createElement('tbody');
  450. tbody.innerHTML = data;
  451. table.appendChild(tbody);
  452. return div;
  453. }
  454. return false;
  455. }
  456. }
  457. }
  458. });
  459. }
  460. setTimeout(() => {
  461. const elementsToModify = [
  462. { selector: '.dt-layout-start', classToAdd: 'my-0' },
  463. { selector: '.dt-layout-end', classToAdd: 'my-0' },
  464. { selector: '.dt-layout-table', classToRemove: 'row mt-2', classToAdd: 'mt-n2' }
  465. ];
  466. // Delete record
  467. elementsToModify.forEach(({ selector, classToRemove, classToAdd }) => {
  468. document.querySelectorAll(selector).forEach(element => {
  469. if (classToRemove) {
  470. classToRemove.split(' ').forEach(className => element.classList.remove(className));
  471. }
  472. if (classToAdd) {
  473. classToAdd.split(' ').forEach(className => element.classList.add(className));
  474. }
  475. });
  476. });
  477. }, 100);
  478. });