설명 없음
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-ecommerce-product-list.js 27KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681
  1. /**
  2. * app-ecommerce-product-list
  3. */
  4. 'use strict';
  5. // Datatable (js)
  6. document.addEventListener('DOMContentLoaded', function (e) {
  7. let borderColor, bodyBg, headingColor;
  8. borderColor = config.colors.borderColor;
  9. bodyBg = config.colors.bodyBg;
  10. headingColor = config.colors.headingColor;
  11. // Variable declaration for table
  12. const dt_product_table = document.querySelector('.datatables-products'),
  13. productAdd = baseUrl + 'app/ecommerce/product/add',
  14. statusObj = {
  15. 1: { title: 'Scheduled', class: 'bg-label-warning' },
  16. 2: { title: 'Publish', class: 'bg-label-success' },
  17. 3: { title: 'Inactive', class: 'bg-label-danger' }
  18. },
  19. categoryObj = {
  20. 0: { title: 'Household' },
  21. 1: { title: 'Office' },
  22. 2: { title: 'Electronics' },
  23. 3: { title: 'Shoes' },
  24. 4: { title: 'Accessories' },
  25. 5: { title: 'Game' }
  26. },
  27. stockObj = {
  28. 0: { title: 'Out_of_Stock' },
  29. 1: { title: 'In_Stock' }
  30. },
  31. stockFilterValObj = {
  32. 0: { title: 'Out of Stock' },
  33. 1: { title: 'In Stock' }
  34. };
  35. // E-commerce Products datatable
  36. if (dt_product_table) {
  37. var dt_products = new DataTable(dt_product_table, {
  38. ajax: assetsPath + 'json/ecommerce-product-list.json',
  39. columns: [
  40. // columns according to JSON
  41. { data: 'id' },
  42. { data: 'id', orderable: false, render: DataTable.render.select() },
  43. { data: 'product_name' },
  44. { data: 'category' },
  45. { data: 'stock' },
  46. { data: 'sku' },
  47. { data: 'price' },
  48. { data: 'quantity' },
  49. { data: 'status' },
  50. { data: 'id' }
  51. ],
  52. columnDefs: [
  53. {
  54. // For Responsive
  55. className: 'control',
  56. searchable: false,
  57. orderable: false,
  58. responsivePriority: 2,
  59. targets: 0,
  60. render: function (data, type, full, meta) {
  61. return '';
  62. }
  63. },
  64. {
  65. // For Checkboxes
  66. targets: 1,
  67. orderable: false,
  68. searchable: false,
  69. responsivePriority: 3,
  70. checkboxes: true,
  71. render: function () {
  72. return '<input type="checkbox" class="dt-checkboxes form-check-input">';
  73. },
  74. checkboxes: {
  75. selectAllRender: '<input type="checkbox" class="form-check-input">'
  76. }
  77. },
  78. {
  79. targets: 2,
  80. responsivePriority: 1,
  81. render: function (data, type, full, meta) {
  82. let name = full['product_name'],
  83. id = full['id'],
  84. productBrand = full['product_brand'],
  85. image = full['image'];
  86. let output;
  87. if (image) {
  88. // For Product image
  89. output = `<img src="${assetsPath}img/ecommerce-images/${image}" alt="Product-${id}" class="rounded">`;
  90. } else {
  91. // For Product badge
  92. let stateNum = Math.floor(Math.random() * 6);
  93. let states = ['success', 'danger', 'warning', 'info', 'dark', 'primary', 'secondary'];
  94. let state = states[stateNum];
  95. let initials = (productBrand.match(/\b\w/g) || []).slice(0, 2).join('').toUpperCase();
  96. output = `<span class="avatar-initial rounded-2 bg-label-${state}">${initials}</span>`;
  97. }
  98. // Creates full output for Product name and product_brand
  99. let rowOutput = `
  100. <div class="d-flex justify-content-start align-items-center product-name">
  101. <div class="avatar-wrapper">
  102. <div class="avatar avatar me-2 me-sm-4 rounded-2 bg-label-secondary">${output}</div>
  103. </div>
  104. <div class="d-flex flex-column">
  105. <h6 class="text-nowrap mb-0">${name}</h6>
  106. <small class="text-truncate d-none d-sm-block">${productBrand}</small>
  107. </div>
  108. </div>
  109. `;
  110. return rowOutput;
  111. }
  112. },
  113. {
  114. targets: 3,
  115. responsivePriority: 5,
  116. render: function (data, type, full, meta) {
  117. let category = categoryObj[full['category']].title;
  118. if (type === 'display') {
  119. let categoryBadgeObj = {
  120. Household: `
  121. <span class="w-px-30 h-px-30 rounded-circle d-flex justify-content-center align-items-center bg-label-warning me-4">
  122. <i class="icon-base bx bx-briefcase icon-18px"></i>
  123. </span>`,
  124. Office: `
  125. <span class="w-px-30 h-px-30 rounded-circle d-flex justify-content-center align-items-center bg-label-info me-4">
  126. <i class="icon-base bx bx-home-smile icon-18px"></i>
  127. </span>`,
  128. Electronics: `
  129. <span class="w-px-30 h-px-30 rounded-circle d-flex justify-content-center align-items-center bg-label-danger me-4">
  130. <i class="icon-base bx bx-headphone icon-18px"></i>
  131. </span>`,
  132. Shoes: `
  133. <span class="w-px-30 h-px-30 rounded-circle d-flex justify-content-center align-items-center bg-label-success me-4">
  134. <i class="icon-base bx bx-walk icon-18px"></i>
  135. </span>`,
  136. Accessories: `
  137. <span class="w-px-30 h-px-30 rounded-circle d-flex justify-content-center align-items-center bg-label-secondary me-4">
  138. <i class="icon-base bx bxs-watch icon-18px"></i>
  139. </span>`,
  140. Game: `
  141. <span class="w-px-30 h-px-30 rounded-circle d-flex justify-content-center align-items-center bg-label-primary me-4">
  142. <i class="icon-base bx bx-laptop icon-18px"></i>
  143. </span>`
  144. };
  145. return `
  146. <span class="text-truncate d-flex align-items-center text-heading">
  147. ${categoryBadgeObj[category] || ''}${category}
  148. </span>`;
  149. } else {
  150. return category;
  151. }
  152. }
  153. },
  154. {
  155. targets: 4,
  156. orderable: false,
  157. responsivePriority: 3,
  158. render: function (data, type, full, meta) {
  159. let stock = full['stock'];
  160. let stockTitle = stockObj[stock].title;
  161. if (type === 'display') {
  162. let stockSwitchObj = {
  163. Out_of_Stock: `
  164. <label class="switch switch-primary switch-sm">
  165. <input type="checkbox" class="switch-input" id="switch">
  166. <span class="switch-toggle-slider">
  167. <span class="switch-off"></span>
  168. </span>
  169. </label>`,
  170. In_Stock: `
  171. <label class="switch switch-primary switch-sm">
  172. <input type="checkbox" class="switch-input" checked>
  173. <span class="switch-toggle-slider">
  174. <span class="switch-on"></span>
  175. </span>
  176. </label>`
  177. };
  178. return `
  179. <span class="text-truncate">
  180. ${stockSwitchObj[stockTitle]}
  181. <span class="d-none">${stockTitle}</span>
  182. </span>`;
  183. } else {
  184. return stockTitle;
  185. }
  186. }
  187. },
  188. {
  189. // Sku
  190. targets: 5,
  191. render: function (data, type, full, meta) {
  192. const sku = full['sku'];
  193. return '<span>' + sku + '</span>';
  194. }
  195. },
  196. {
  197. // price
  198. targets: 6,
  199. render: function (data, type, full, meta) {
  200. const price = full['price'];
  201. return '<span>' + price + '</span>';
  202. }
  203. },
  204. {
  205. // qty
  206. targets: 7,
  207. responsivePriority: 4,
  208. render: function (data, type, full, meta) {
  209. const qty = full['qty'];
  210. return '<span>' + qty + '</span>';
  211. }
  212. },
  213. {
  214. // Status
  215. targets: -2,
  216. render: function (data, type, full, meta) {
  217. const status = full['status'];
  218. return (
  219. '<span class="badge ' +
  220. statusObj[status].class +
  221. '" text-capitalized>' +
  222. statusObj[status].title +
  223. '</span>'
  224. );
  225. }
  226. },
  227. {
  228. targets: -1,
  229. title: 'Actions',
  230. searchable: false,
  231. orderable: false,
  232. render: function (data, type, full, meta) {
  233. return `
  234. <div class="d-inline-block text-nowrap">
  235. <button class="btn btn-icon"><i class="icon-base bx bx-edit icon-md"></i></button>
  236. <button class="btn btn-icon dropdown-toggle hide-arrow" data-bs-toggle="dropdown">
  237. <i class="icon-base bx bx-dots-vertical-rounded icon-md"></i>
  238. </button>
  239. <div class="dropdown-menu dropdown-menu-end m-0">
  240. <a href="javascript:void(0);" class="dropdown-item">View</a>
  241. <a href="javascript:void(0);" class="dropdown-item">Suspend</a>
  242. </div>
  243. </div>
  244. `;
  245. }
  246. }
  247. ],
  248. select: {
  249. style: 'multi',
  250. selector: 'td:nth-child(2)'
  251. },
  252. order: [2, 'asc'],
  253. layout: {
  254. topStart: {
  255. rowClass: 'card-header d-flex border-top rounded-0 flex-wrap py-0 flex-column flex-md-row align-items-start',
  256. features: [
  257. {
  258. search: {
  259. className: 'me-5 ms-n4 pe-5 mb-n6 mb-md-0',
  260. placeholder: 'Search Product',
  261. text: '_INPUT_'
  262. }
  263. }
  264. ]
  265. },
  266. topEnd: {
  267. rowClass: 'row m-3 my-0 justify-content-between',
  268. features: [
  269. {
  270. pageLength: {
  271. menu: [10, 25, 50, 100],
  272. text: '_MENU_'
  273. },
  274. buttons: [
  275. {
  276. extend: 'collection',
  277. className: 'btn btn-label-secondary dropdown-toggle me-4',
  278. text: '<span class="d-flex align-items-center gap-2"><i class="icon-base bx bx-export icon-xs"></i> <span class="d-none d-sm-inline-block">Export</span></span>',
  279. buttons: [
  280. {
  281. extend: 'print',
  282. text: `<span class="d-flex align-items-center"><i class="icon-base bx bx-printer me-1"></i>Print</span>`,
  283. className: 'dropdown-item',
  284. exportOptions: {
  285. columns: [3, 4, 5, 6, 7],
  286. format: {
  287. body: function (inner, coldex, rowdex) {
  288. if (inner.length <= 0) return inner;
  289. // Check if inner is HTML content
  290. if (inner.indexOf('<') > -1) {
  291. const parser = new DOMParser();
  292. const doc = parser.parseFromString(inner, 'text/html');
  293. // Get all text content
  294. let text = '';
  295. // Handle specific elements
  296. const userNameElements = doc.querySelectorAll('.product-name');
  297. if (userNameElements.length > 0) {
  298. userNameElements.forEach(el => {
  299. // Get text from nested structure
  300. const nameText =
  301. el.querySelector('.fw-medium')?.textContent ||
  302. el.querySelector('.d-block')?.textContent ||
  303. el.textContent;
  304. text += nameText.trim() + ' ';
  305. });
  306. } else {
  307. // Get regular text content
  308. text = doc.body.textContent || doc.body.innerText;
  309. }
  310. return text.trim();
  311. }
  312. return inner;
  313. }
  314. }
  315. },
  316. customize: function (win) {
  317. win.document.body.style.color = config.colors.headingColor;
  318. win.document.body.style.borderColor = config.colors.borderColor;
  319. win.document.body.style.backgroundColor = config.colors.bodyBg;
  320. const table = win.document.body.querySelector('table');
  321. table.classList.add('compact');
  322. table.style.color = 'inherit';
  323. table.style.borderColor = 'inherit';
  324. table.style.backgroundColor = 'inherit';
  325. }
  326. },
  327. {
  328. extend: 'csv',
  329. text: `<span class="d-flex align-items-center"><i class="icon-base bx bx-file me-1"></i>Csv</span>`,
  330. className: 'dropdown-item',
  331. exportOptions: {
  332. columns: [3, 4, 5, 6, 7],
  333. format: {
  334. body: function (inner, coldex, rowdex) {
  335. if (inner.length <= 0) return inner;
  336. // Parse HTML content
  337. const parser = new DOMParser();
  338. const doc = parser.parseFromString(inner, 'text/html');
  339. let text = '';
  340. // Handle product-name elements specifically
  341. const userNameElements = doc.querySelectorAll('.product-name');
  342. if (userNameElements.length > 0) {
  343. userNameElements.forEach(el => {
  344. // Get text from nested structure - try different selectors
  345. const nameText =
  346. el.querySelector('.fw-medium')?.textContent ||
  347. el.querySelector('.d-block')?.textContent ||
  348. el.textContent;
  349. text += nameText.trim() + ' ';
  350. });
  351. } else {
  352. // Handle other elements (status, role, etc)
  353. text = doc.body.textContent || doc.body.innerText;
  354. }
  355. return text.trim();
  356. }
  357. }
  358. }
  359. },
  360. {
  361. extend: 'excel',
  362. text: `<span class="d-flex align-items-center"><i class="icon-base bx bxs-file-export me-1"></i>Excel</span>`,
  363. className: 'dropdown-item',
  364. exportOptions: {
  365. columns: [3, 4, 5, 6, 7],
  366. format: {
  367. body: function (inner, coldex, rowdex) {
  368. if (inner.length <= 0) return inner;
  369. // Parse HTML content
  370. const parser = new DOMParser();
  371. const doc = parser.parseFromString(inner, 'text/html');
  372. let text = '';
  373. // Handle product-name elements specifically
  374. const userNameElements = doc.querySelectorAll('.product-name');
  375. if (userNameElements.length > 0) {
  376. userNameElements.forEach(el => {
  377. // Get text from nested structure - try different selectors
  378. const nameText =
  379. el.querySelector('.fw-medium')?.textContent ||
  380. el.querySelector('.d-block')?.textContent ||
  381. el.textContent;
  382. text += nameText.trim() + ' ';
  383. });
  384. } else {
  385. // Handle other elements (status, role, etc)
  386. text = doc.body.textContent || doc.body.innerText;
  387. }
  388. return text.trim();
  389. }
  390. }
  391. }
  392. },
  393. {
  394. extend: 'pdf',
  395. text: `<span class="d-flex align-items-center"><i class="icon-base bx bxs-file-pdf me-1"></i>Pdf</span>`,
  396. className: 'dropdown-item',
  397. exportOptions: {
  398. columns: [3, 4, 5, 6, 7],
  399. format: {
  400. body: function (inner, coldex, rowdex) {
  401. if (inner.length <= 0) return inner;
  402. // Parse HTML content
  403. const parser = new DOMParser();
  404. const doc = parser.parseFromString(inner, 'text/html');
  405. let text = '';
  406. // Handle product-name elements specifically
  407. const userNameElements = doc.querySelectorAll('.product-name');
  408. if (userNameElements.length > 0) {
  409. userNameElements.forEach(el => {
  410. // Get text from nested structure - try different selectors
  411. const nameText =
  412. el.querySelector('.fw-medium')?.textContent ||
  413. el.querySelector('.d-block')?.textContent ||
  414. el.textContent;
  415. text += nameText.trim() + ' ';
  416. });
  417. } else {
  418. // Handle other elements (status, role, etc)
  419. text = doc.body.textContent || doc.body.innerText;
  420. }
  421. return text.trim();
  422. }
  423. }
  424. }
  425. },
  426. {
  427. extend: 'copy',
  428. text: `<i class="icon-base bx bx-copy me-1"></i>Copy`,
  429. className: 'dropdown-item',
  430. exportOptions: {
  431. columns: [3, 4, 5, 6, 7],
  432. format: {
  433. body: function (inner, coldex, rowdex) {
  434. if (inner.length <= 0) return inner;
  435. // Parse HTML content
  436. const parser = new DOMParser();
  437. const doc = parser.parseFromString(inner, 'text/html');
  438. let text = '';
  439. // Handle product-name elements specifically
  440. const userNameElements = doc.querySelectorAll('.product-name');
  441. if (userNameElements.length > 0) {
  442. userNameElements.forEach(el => {
  443. // Get text from nested structure - try different selectors
  444. const nameText =
  445. el.querySelector('.fw-medium')?.textContent ||
  446. el.querySelector('.d-block')?.textContent ||
  447. el.textContent;
  448. text += nameText.trim() + ' ';
  449. });
  450. } else {
  451. // Handle other elements (status, role, etc)
  452. text = doc.body.textContent || doc.body.innerText;
  453. }
  454. return text.trim();
  455. }
  456. }
  457. }
  458. }
  459. ]
  460. },
  461. {
  462. text: '<i class="icon-base bx bx-plus me-0 me-sm-1 icon-xs"></i><span class="d-none d-sm-inline-block">Add Product</span>',
  463. className: 'add-new btn btn-primary',
  464. action: function () {
  465. window.location.href = productAdd;
  466. }
  467. }
  468. ]
  469. }
  470. ]
  471. },
  472. bottomStart: {
  473. rowClass: 'row mx-3 justify-content-between',
  474. features: ['info']
  475. },
  476. bottomEnd: 'paging'
  477. },
  478. language: {
  479. paginate: {
  480. next: '<i class="icon-base bx bx-chevron-right scaleX-n1-rtl icon-18px"></i>',
  481. previous: '<i class="icon-base bx bx-chevron-left scaleX-n1-rtl icon-18px"></i>',
  482. first: '<i class="icon-base bx bx-chevrons-left scaleX-n1-rtl icon-18px"></i>',
  483. last: '<i class="icon-base bx bx-chevrons-right scaleX-n1-rtl icon-18px"></i>'
  484. }
  485. },
  486. // For responsive popup
  487. responsive: {
  488. details: {
  489. display: DataTable.Responsive.display.modal({
  490. header: function (row) {
  491. const data = row.data();
  492. return 'Details of ' + data['product_name'];
  493. }
  494. }),
  495. type: 'column',
  496. renderer: function (api, rowIdx, columns) {
  497. const data = columns
  498. .map(function (col) {
  499. return col.title !== '' // Do not show row in modal popup if title is blank (for check box)
  500. ? `<tr data-dt-row="${col.rowIndex}" data-dt-column="${col.columnIndex}">
  501. <td>${col.title}:</td>
  502. <td>${col.data}</td>
  503. </tr>`
  504. : '';
  505. })
  506. .join('');
  507. if (data) {
  508. const div = document.createElement('div');
  509. div.classList.add('table-responsive');
  510. const table = document.createElement('table');
  511. div.appendChild(table);
  512. table.classList.add('table');
  513. const tbody = document.createElement('tbody');
  514. tbody.innerHTML = data;
  515. table.appendChild(tbody);
  516. return div;
  517. }
  518. return false;
  519. }
  520. }
  521. },
  522. initComplete: function () {
  523. const api = this.api();
  524. // Adding status filter once table is initialized
  525. api.columns(-2).every(function () {
  526. const column = this;
  527. const select = document.createElement('select');
  528. select.id = 'ProductStatus';
  529. select.className = 'form-select text-capitalize';
  530. select.innerHTML = '<option value="">Status</option>';
  531. document.querySelector('.product_status').appendChild(select);
  532. select.addEventListener('change', function () {
  533. const val = select.value ? `^${select.value}$` : '';
  534. column.search(val, true, false).draw();
  535. });
  536. column
  537. .data()
  538. .unique()
  539. .sort()
  540. .each(function (d) {
  541. const option = document.createElement('option');
  542. option.value = statusObj[d].title;
  543. option.textContent = statusObj[d].title;
  544. select.appendChild(option);
  545. });
  546. });
  547. // Adding category filter once table is initialized
  548. api.columns(3).every(function () {
  549. const column = this;
  550. const select = document.createElement('select');
  551. select.id = 'ProductCategory';
  552. select.className = 'form-select text-capitalize';
  553. select.innerHTML = '<option value="">Category</option>';
  554. document.querySelector('.product_category').appendChild(select);
  555. select.addEventListener('change', function () {
  556. const val = select.value ? `^${select.value}$` : '';
  557. column.search(val, true, false).draw();
  558. });
  559. column
  560. .data()
  561. .unique()
  562. .sort()
  563. .each(function (d) {
  564. const option = document.createElement('option');
  565. option.value = categoryObj[d].title;
  566. option.textContent = categoryObj[d].title;
  567. select.appendChild(option);
  568. });
  569. });
  570. // Adding stock filter once table is initialized
  571. api.columns(4).every(function () {
  572. const column = this;
  573. const select = document.createElement('select');
  574. select.id = 'ProductStock';
  575. select.className = 'form-select text-capitalize';
  576. select.innerHTML = '<option value="">Stock</option>';
  577. document.querySelector('.product_stock').appendChild(select);
  578. select.addEventListener('change', function () {
  579. const val = select.value ? `^${select.value}$` : '';
  580. column.search(val, true, false).draw();
  581. });
  582. column
  583. .data()
  584. .unique()
  585. .sort()
  586. .each(function (d) {
  587. const option = document.createElement('option');
  588. option.value = stockObj[d].title;
  589. option.textContent = stockObj[d].title;
  590. select.appendChild(option);
  591. });
  592. });
  593. }
  594. });
  595. }
  596. // Filter form control to default size
  597. // ? setTimeout used for product-list table initialization
  598. setTimeout(() => {
  599. const elementsToModify = [
  600. { selector: '.dt-buttons .btn', classToRemove: 'btn-secondary' },
  601. { selector: '.dt-search .form-control', classToRemove: 'form-control-sm', classToAdd: 'ms-0' },
  602. { selector: '.dt-search', classToAdd: 'mb-0 mb-md-6' },
  603. { selector: '.dt-length .form-select', classToRemove: 'form-select-sm' },
  604. { selector: '.dt-length', classToAdd: 'mb-md-6 mb-4' },
  605. { selector: '.dt-layout-table', classToRemove: 'row mt-2' },
  606. { selector: '.dt-layout-start', classToAdd: 'mt-0' },
  607. {
  608. selector: '.dt-layout-end',
  609. classToRemove: 'justify-content-between',
  610. classToAdd: 'justify-content-md-between justify-content-center d-flex flex-wrap gap-2 mb-md-0 mb-4 mt-0'
  611. },
  612. { selector: '.dt-layout-full', classToRemove: 'col-md col-12', classToAdd: 'table-responsive' }
  613. ];
  614. // Delete record
  615. elementsToModify.forEach(({ selector, classToRemove, classToAdd }) => {
  616. document.querySelectorAll(selector).forEach(element => {
  617. if (classToRemove) {
  618. classToRemove.split(' ').forEach(className => element.classList.remove(className));
  619. }
  620. if (classToAdd) {
  621. classToAdd.split(' ').forEach(className => element.classList.add(className));
  622. }
  623. });
  624. });
  625. }, 100);
  626. });