Ei kuvausta
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

app-academy-dashboard.js 17KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544
  1. /**
  2. * Academy Dashboard charts and datatable
  3. */
  4. 'use strict';
  5. // Hour pie chart
  6. document.addEventListener('DOMContentLoaded', function (e) {
  7. let labelColor, headingColor, borderColor, fontFamily, legendColor;
  8. labelColor = config.colors.textMuted;
  9. headingColor = config.colors.headingColor;
  10. borderColor = config.colors.borderColor;
  11. fontFamily = config.fontFamily;
  12. legendColor = config.colors.bodyColor;
  13. // Donut Chart Colors
  14. const chartColors = {
  15. donut: {
  16. series1: 'color-mix(in sRGB, ' + config.colors.success + ' 80%, ' + config.colors.black + ')',
  17. series2: 'color-mix(in sRGB, ' + config.colors.success + ' 90%, ' + config.colors.black + ')',
  18. series3: config.colors.success,
  19. series4: 'color-mix(in sRGB, ' + config.colors.success + ' 80%, ' + config.colors.cardColor + ')',
  20. series5: 'color-mix(in sRGB, ' + config.colors.success + ' 60%, ' + config.colors.cardColor + ')',
  21. series6: 'color-mix(in sRGB, ' + config.colors.success + ' 40%, ' + config.colors.cardColor + ')'
  22. }
  23. };
  24. const leadsReportChartEl = document.querySelector('#leadsReportChart'),
  25. leadsReportChartConfig = {
  26. chart: {
  27. height: 170,
  28. width: 150,
  29. parentHeightOffset: 0,
  30. type: 'donut'
  31. },
  32. labels: ['36h', '56h', '16h', '32h', '56h', '16h'],
  33. series: [23, 35, 10, 20, 35, 23],
  34. colors: [
  35. chartColors.donut.series1,
  36. chartColors.donut.series2,
  37. chartColors.donut.series3,
  38. chartColors.donut.series4,
  39. chartColors.donut.series5,
  40. chartColors.donut.series6
  41. ],
  42. stroke: {
  43. width: 0
  44. },
  45. dataLabels: {
  46. enabled: false,
  47. formatter: function (val, opt) {
  48. return parseInt(val) + '%';
  49. }
  50. },
  51. legend: {
  52. show: false
  53. },
  54. tooltip: {
  55. theme: false
  56. },
  57. grid: {
  58. padding: {
  59. top: 0
  60. }
  61. },
  62. states: {
  63. hover: {
  64. filter: {
  65. type: 'none'
  66. }
  67. },
  68. active: {
  69. filter: {
  70. type: 'none'
  71. }
  72. }
  73. },
  74. plotOptions: {
  75. pie: {
  76. donut: {
  77. size: '70%',
  78. labels: {
  79. show: true,
  80. value: {
  81. fontSize: '1.125rem',
  82. fontFamily: fontFamily,
  83. color: headingColor,
  84. fontWeight: 500,
  85. offsetY: -20,
  86. formatter: function (val) {
  87. return parseInt(val) + '%';
  88. }
  89. },
  90. name: {
  91. offsetY: 20,
  92. fontFamily: fontFamily
  93. },
  94. total: {
  95. show: true,
  96. fontSize: '.9375rem',
  97. fontFamily: fontFamily,
  98. label: 'Total',
  99. color: labelColor,
  100. formatter: function (w) {
  101. return '231h';
  102. }
  103. }
  104. }
  105. }
  106. }
  107. }
  108. };
  109. if (typeof leadsReportChartEl !== undefined && leadsReportChartEl !== null) {
  110. const leadsReportChart = new ApexCharts(leadsReportChartEl, leadsReportChartConfig);
  111. leadsReportChart.render();
  112. }
  113. // datatbale bar chart
  114. const horizontalBarChartEl = document.querySelector('#horizontalBarChart'),
  115. horizontalBarChartConfig = {
  116. chart: {
  117. height: 360,
  118. type: 'bar',
  119. toolbar: {
  120. show: false
  121. }
  122. },
  123. plotOptions: {
  124. bar: {
  125. horizontal: true,
  126. barHeight: '60%',
  127. distributed: true,
  128. startingShape: 'rounded',
  129. borderRadiusApplication: 'end',
  130. borderRadius: 7
  131. }
  132. },
  133. grid: {
  134. strokeDashArray: 10,
  135. borderColor: borderColor,
  136. xaxis: {
  137. lines: {
  138. show: true
  139. }
  140. },
  141. yaxis: {
  142. lines: {
  143. show: false
  144. }
  145. },
  146. padding: {
  147. top: -35,
  148. bottom: -12
  149. }
  150. },
  151. colors: [
  152. config.colors.primary,
  153. config.colors.info,
  154. config.colors.success,
  155. config.colors.secondary,
  156. config.colors.danger,
  157. config.colors.warning
  158. ],
  159. fill: {
  160. opacity: [1, 1, 1, 1, 1, 1]
  161. },
  162. dataLabels: {
  163. enabled: true,
  164. style: {
  165. colors: [config.colors.white],
  166. fontWeight: 400,
  167. fontSize: '13px',
  168. fontFamily: fontFamily
  169. },
  170. formatter: function (val, opts) {
  171. return horizontalBarChartConfig.labels[opts.dataPointIndex];
  172. },
  173. offsetX: 0,
  174. dropShadow: {
  175. enabled: false
  176. }
  177. },
  178. labels: ['UI Design', 'UX Design', 'Music', 'Animation', 'React', 'SEO'],
  179. series: [
  180. {
  181. data: [35, 20, 14, 12, 10, 9]
  182. }
  183. ],
  184. xaxis: {
  185. categories: ['6', '5', '4', '3', '2', '1'],
  186. axisBorder: {
  187. show: false
  188. },
  189. axisTicks: {
  190. show: false
  191. },
  192. labels: {
  193. style: {
  194. colors: labelColor,
  195. fontFamily: fontFamily,
  196. fontSize: '13px'
  197. },
  198. formatter: function (val) {
  199. return `${val}%`;
  200. }
  201. }
  202. },
  203. yaxis: {
  204. max: 35,
  205. labels: {
  206. style: {
  207. colors: [labelColor],
  208. fontFamily: fontFamily,
  209. fontSize: '13px'
  210. }
  211. }
  212. },
  213. tooltip: {
  214. enabled: true,
  215. style: {
  216. fontSize: '12px'
  217. },
  218. onDatasetHover: {
  219. highlightDataSeries: false
  220. },
  221. custom: function ({ series, seriesIndex, dataPointIndex, w }) {
  222. return '<div class="px-3 py-2">' + '<span>' + series[seriesIndex][dataPointIndex] + '%</span>' + '</div>';
  223. }
  224. },
  225. legend: {
  226. show: false
  227. }
  228. };
  229. if (typeof horizontalBarChartEl !== undefined && horizontalBarChartEl !== null) {
  230. const horizontalBarChart = new ApexCharts(horizontalBarChartEl, horizontalBarChartConfig);
  231. horizontalBarChart.render();
  232. }
  233. //radial Barchart
  234. function radialBarChart(color, value, show) {
  235. const radialBarChartOpt = {
  236. chart: {
  237. height: show == 'true' ? 60 : 55,
  238. width: show == 'true' ? 58 : 45,
  239. type: 'radialBar'
  240. },
  241. plotOptions: {
  242. radialBar: {
  243. hollow: {
  244. size: show == 'true' ? '50%' : '25%'
  245. },
  246. dataLabels: {
  247. show: show == 'true' ? true : false,
  248. value: {
  249. offsetY: -10,
  250. fontSize: '15px',
  251. fontWeight: 500,
  252. fontFamily: fontFamily,
  253. color: headingColor
  254. }
  255. },
  256. track: {
  257. background: config.colors_label.secondary
  258. }
  259. }
  260. },
  261. stroke: {
  262. lineCap: 'round'
  263. },
  264. colors: [color],
  265. grid: {
  266. padding: {
  267. top: show == 'true' ? -12 : -15,
  268. bottom: show == 'true' ? -17 : -15,
  269. left: show == 'true' ? -17 : -5,
  270. right: -15
  271. }
  272. },
  273. series: [value],
  274. labels: show == 'true' ? [''] : ['Progress']
  275. };
  276. return radialBarChartOpt;
  277. }
  278. const chartProgressList = document.querySelectorAll('.chart-progress');
  279. if (chartProgressList) {
  280. chartProgressList.forEach(function (chartProgressEl) {
  281. const color = config.colors[chartProgressEl.dataset.color],
  282. series = chartProgressEl.dataset.series;
  283. const progress_variant = chartProgressEl.dataset.progress_variant;
  284. const optionsBundle = radialBarChart(color, series, progress_variant);
  285. const chart = new ApexCharts(chartProgressEl, optionsBundle);
  286. chart.render();
  287. });
  288. }
  289. // datatable
  290. // Variable declaration for table
  291. const dt_academy_course = document.querySelector('.datatables-academy-course'),
  292. logoObj = {
  293. angular:
  294. '<span class="badge bg-label-danger rounded p-1_5"><i class="icon-base bx bxl-angular icon-28px"></i></span>',
  295. figma:
  296. '<span class="badge bg-label-warning rounded p-1_5"><i class="icon-base bx bxl-figma icon-28px"></i></span>',
  297. react: '<span class="badge bg-label-info rounded p-1_5"><i class="icon-base bx bxl-react icon-28px"></i></span>',
  298. art: '<span class="badge bg-label-success rounded p-1_5"><i class="icon-base bx bxs-color icon-28px"></i></span>',
  299. fundamentals:
  300. '<span class="badge bg-label-primary rounded p-1_5"><i class="icon-base bx bx-diamond icon-28px"></i></span>'
  301. };
  302. if (dt_academy_course) {
  303. let tableTitle = document.createElement('h5');
  304. tableTitle.classList.add('card-title', 'mb-0', 'text-nowrap', 'text-md-start', 'text-center');
  305. tableTitle.innerHTML = 'Course you are taking';
  306. let dt_course = new DataTable(dt_academy_course, {
  307. ajax: assetsPath + 'json/app-academy-dashboard.json',
  308. columns: [
  309. // columns according to JSON
  310. { data: 'id' },
  311. { data: 'id', orderable: false, render: DataTable.render.select() },
  312. { data: 'course name' },
  313. { data: 'time' },
  314. { data: 'progress' },
  315. { data: 'status' }
  316. ],
  317. columnDefs: [
  318. {
  319. // For Responsive
  320. className: 'control',
  321. searchable: false,
  322. orderable: false,
  323. responsivePriority: 2,
  324. targets: 0,
  325. render: function (data, type, full, meta) {
  326. return '';
  327. }
  328. },
  329. {
  330. // For Checkboxes
  331. targets: 1,
  332. orderable: false,
  333. searchable: false,
  334. responsivePriority: 3,
  335. checkboxes: true,
  336. render: function () {
  337. return '<input type="checkbox" class="dt-checkboxes form-check-input">';
  338. },
  339. checkboxes: {
  340. selectAllRender: '<input type="checkbox" class="form-check-input">'
  341. }
  342. },
  343. {
  344. targets: 2,
  345. responsivePriority: 2,
  346. render: (data, type, full) => {
  347. const { logo, course, user, image } = full;
  348. const output = image
  349. ? `<img src="${assetsPath}img/avatars/${image}" alt="Avatar" class="rounded-circle">`
  350. : (() => {
  351. // Generate initials and random state for badge
  352. const states = ['success', 'danger', 'warning', 'info', 'dark', 'primary', 'secondary'];
  353. const state = states[Math.floor(Math.random() * states.length)];
  354. const initials = (user.match(/\b\w/g) || []).reduce((acc, char) => acc + char.toUpperCase(), '');
  355. return `<span class="avatar-initial rounded-circle bg-label-${state}">${initials}</span>`;
  356. })();
  357. // Create full row output
  358. return `
  359. <div class="d-flex align-items-center">
  360. <span class="me-4">${logoObj[logo]}</span>
  361. <div>
  362. <a class="text-heading text-truncate fw-medium mb-2 text-wrap" href="${baseUrl}app/academy/course-details">
  363. ${course}
  364. </a>
  365. <div class="d-flex align-items-center mt-1">
  366. <div class="avatar-wrapper me-2">
  367. <div class="avatar avatar-xs">
  368. ${output}
  369. </div>
  370. </div>
  371. <small class="text-nowrap text-heading">${user}</small>
  372. </div>
  373. </div>
  374. </div>
  375. `;
  376. }
  377. },
  378. {
  379. targets: 3,
  380. responsivePriority: 3,
  381. render: data => {
  382. const duration = moment.duration(data);
  383. const hours = Math.floor(duration.asHours());
  384. const minutes = Math.floor(duration.asMinutes()) - hours * 60;
  385. const formattedTime = `${hours}h ${minutes}m`;
  386. return `<span class="fw-medium text-nowrap text-heading">${formattedTime}</span>`;
  387. }
  388. },
  389. {
  390. targets: 4,
  391. render: (data, type, full) => {
  392. const { status: statusNumber, number: averageNumber } = full;
  393. return `
  394. <div class="d-flex align-items-center gap-3">
  395. <p class="fw-medium mb-0 text-heading">${statusNumber}</p>
  396. <div class="progress w-100" style="height: 8px;">
  397. <div
  398. class="progress-bar"
  399. style="width: ${statusNumber}"
  400. aria-valuenow="${statusNumber}"
  401. aria-valuemin="0"
  402. aria-valuemax="100">
  403. </div>
  404. </div>
  405. <small>${averageNumber}</small>
  406. </div>
  407. `;
  408. }
  409. },
  410. {
  411. targets: 5,
  412. render: (data, type, full) => {
  413. const { user_number: userNumber, note, view } = full;
  414. return `
  415. <div class="d-flex align-items-center justify-content-between">
  416. <div class="w-px-75 d-flex align-items-center">
  417. <i class="icon-base bx bx-user icon-lg me-1_5 text-primary"></i>
  418. <span>${userNumber}</span>
  419. </div>
  420. <div class="w-px-75 d-flex align-items-center">
  421. <i class="icon-base bx bx-book-open icon-lg me-1_5 text-info"></i>
  422. <span>${note}</span>
  423. </div>
  424. <div class="w-px-75 d-flex align-items-center">
  425. <i class="icon-base bx bx-video icon-lg me-1_5 text-danger"></i>
  426. <span>${view}</span>
  427. </div>
  428. </div>
  429. `;
  430. }
  431. }
  432. ],
  433. select: {
  434. style: 'multi',
  435. selector: 'td:nth-child(2)'
  436. },
  437. order: [[2, 'desc']],
  438. layout: {
  439. topStart: {
  440. rowClass: 'row card-header border-bottom mx-0 px-3 py-2',
  441. features: [tableTitle]
  442. },
  443. topEnd: {
  444. search: {
  445. placeholder: 'Search Course',
  446. text: '_INPUT_'
  447. }
  448. },
  449. bottomStart: {
  450. rowClass: 'row mx-3 justify-content-between',
  451. features: ['info']
  452. },
  453. bottomEnd: 'paging'
  454. },
  455. lengthMenu: [5],
  456. language: {
  457. paginate: {
  458. next: '<i class="icon-base bx bx-chevron-right scaleX-n1-rtl icon-18px"></i>',
  459. previous: '<i class="icon-base bx bx-chevron-left scaleX-n1-rtl icon-18px"></i>',
  460. first: '<i class="icon-base bx bx-chevrons-left scaleX-n1-rtl icon-18px"></i>',
  461. last: '<i class="icon-base bx bx-chevrons-right scaleX-n1-rtl icon-18px"></i>'
  462. }
  463. },
  464. // For responsive popup
  465. responsive: {
  466. details: {
  467. display: DataTable.Responsive.display.modal({
  468. header: function (row) {
  469. const data = row.data();
  470. return 'Details of ' + data['order'];
  471. }
  472. }),
  473. type: 'column',
  474. renderer: function (api, rowIdx, columns) {
  475. const data = columns
  476. .map(function (col) {
  477. return col.title !== '' // Do not show row in modal popup if title is blank (for check box)
  478. ? `<tr data-dt-row="${col.rowIndex}" data-dt-column="${col.columnIndex}">
  479. <td>${col.title}:</td>
  480. <td>${col.data}</td>
  481. </tr>`
  482. : '';
  483. })
  484. .join('');
  485. if (data) {
  486. const div = document.createElement('div');
  487. div.classList.add('table-responsive');
  488. const table = document.createElement('table');
  489. div.appendChild(table);
  490. table.classList.add('table');
  491. table.classList.add('datatables-basic');
  492. const tbody = document.createElement('tbody');
  493. tbody.innerHTML = data;
  494. table.appendChild(tbody);
  495. return div;
  496. }
  497. return false;
  498. }
  499. }
  500. }
  501. });
  502. }
  503. // Filter form control to default size
  504. // ? setTimeout used for data-table initialization
  505. setTimeout(() => {
  506. const elementsToModify = [
  507. { selector: '.dt-search .form-control', classToRemove: 'form-control-sm' },
  508. { selector: '.dt-length .form-select', classToRemove: 'form-select-sm' },
  509. { selector: '.dt-layout-table', classToRemove: 'row mt-2' }
  510. ];
  511. // Delete record
  512. elementsToModify.forEach(({ selector, classToRemove, classToAdd }) => {
  513. document.querySelectorAll(selector).forEach(element => {
  514. classToRemove.split(' ').forEach(className => element.classList.remove(className));
  515. if (classToAdd) {
  516. element.classList.add(classToAdd);
  517. }
  518. });
  519. });
  520. }, 100);
  521. });