Commit b2561485 by Natthaphat

ยก model myProtal มาใส่ ติดตั้ง package เพิ่ม ## npm i --force ด้วย

parent b67a3eb0
......@@ -25,9 +25,17 @@
"@fortawesome/angular-fontawesome": "^0.14.1",
"@kolkov/angular-editor": "^3.0.0-beta.0",
"@ks89/angular-modal-gallery": "^11.1.1",
"@ng-bootstrap/ng-bootstrap": "^15.1.1",
"@ng-select/ng-select": "^12.0.6",
"@ngx-translate/core": "^15.0.0",
"@ngx-translate/http-loader": "^8.0.0",
"@syncfusion/ej2-angular-base": "^29.2.4",
"@syncfusion/ej2-angular-charts": "^29.2.4",
"@syncfusion/ej2-angular-dropdowns": "^29.2.4",
"@syncfusion/ej2-angular-grids": "^29.2.4",
"@syncfusion/ej2-angular-inputs": "^29.2.4",
"@syncfusion/ej2-angular-pivotview": "^29.2.4",
"@syncfusion/ej2-grids": "^30.1.39",
"@tailwindcss/forms": "^0.5.7",
"@types/google.maps": "^3.58.1",
"@types/googlemaps": "^3.43.3",
......@@ -39,9 +47,11 @@
"date-fns": "^3.3.1",
"echarts": "^5.4.3",
"face-api.js": "^0.22.2",
"file-saver": "^2.0.5",
"filepond": "^4.30.6",
"filepond-plugin-get-file": "^1.1.0",
"firebase": "^10.7.1",
"flatpickr": "^4.6.13",
"font-awesome": "^4.7.0",
"i": "^0.3.7",
"jwt-decode": "^4.0.0",
......@@ -86,6 +96,7 @@
"@angular-devkit/build-angular": "^17.0.5",
"@angular/cli": "^17.1.1",
"@angular/compiler-cli": "^17.0.0",
"@types/file-saver": "^2.0.7",
"@types/jasmine": "~5.1.0",
"@types/react": "^18.2.46",
"jasmine-core": "~5.1.0",
......@@ -4995,6 +5006,23 @@
"integrity": "sha512-YbrUWREPGEjE/FU6foXcAT1YbVwqD/jkYnY1dFb0o4AxtP3s4xKBthlELjndZih8uwsDWgQZx1eNskRNe2BgZQ==",
"license": "MIT"
},
"node_modules/@ng-bootstrap/ng-bootstrap": {
"version": "15.1.1",
"resolved": "https://registry.npmjs.org/@ng-bootstrap/ng-bootstrap/-/ng-bootstrap-15.1.1.tgz",
"integrity": "sha512-nZlIMMggtI3IHkGs0XPrUIUdpeEzQvfGV9M4I9IvCqiS2n4RwWoUvWK1ICo4csZqFNBDlCQx956gO6ZZUSL2mw==",
"license": "MIT",
"dependencies": {
"tslib": "^2.3.0"
},
"peerDependencies": {
"@angular/common": "^16.0.0",
"@angular/core": "^16.0.0",
"@angular/forms": "^16.0.0",
"@angular/localize": "^16.0.0",
"@popperjs/core": "^2.11.6",
"rxjs": "^6.5.3 || ^7.4.0"
}
},
"node_modules/@ng-select/ng-select": {
"version": "12.0.7",
"resolved": "https://registry.npmjs.org/@ng-select/ng-select/-/ng-select-12.0.7.tgz",
......@@ -6186,9 +6214,9 @@
"license": "MIT"
},
"node_modules/@svgdotjs/svg.draggable.js": {
"version": "3.0.5",
"resolved": "https://registry.npmjs.org/@svgdotjs/svg.draggable.js/-/svg.draggable.js-3.0.5.tgz",
"integrity": "sha512-ljL/fB0tAjRfFOJGhXpr7rEx9DJ6D7Pxt3AXvgxjEM17g6wK3Ho9nXhntraOMx8JLZdq4NBMjokeXMvnQzJVYA==",
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/@svgdotjs/svg.draggable.js/-/svg.draggable.js-3.0.6.tgz",
"integrity": "sha512-7iJFm9lL3C40HQcqzEfezK2l+dW2CpoVY3b77KQGqc8GXWa6LhhmX5Ckv7alQfUXBuZbjpICZ+Dvq1czlGx7gA==",
"license": "MIT",
"peer": true,
"peerDependencies": {
......@@ -6196,13 +6224,13 @@
}
},
"node_modules/@svgdotjs/svg.filter.js": {
"version": "3.0.8",
"resolved": "https://registry.npmjs.org/@svgdotjs/svg.filter.js/-/svg.filter.js-3.0.8.tgz",
"integrity": "sha512-YshF2YDaeRA2StyzAs5nUPrev7npQ38oWD0eTRwnsciSL2KrRPMoUw8BzjIXItb3+dccKGTX3IQOd2NFzmHkog==",
"version": "3.0.9",
"resolved": "https://registry.npmjs.org/@svgdotjs/svg.filter.js/-/svg.filter.js-3.0.9.tgz",
"integrity": "sha512-/69XMRCDoam2HgC4ldHIaDgeQf1ViHIsa0Ld4uWgiXtZ+E24DWHe/9Ib6kbNiZ7WRIdlVokUDR1Fg0kjIpkfbw==",
"license": "MIT",
"peer": true,
"dependencies": {
"@svgdotjs/svg.js": "^3.1.1"
"@svgdotjs/svg.js": "^3.2.4"
},
"engines": {
"node": ">= 0.8.0"
......@@ -6234,9 +6262,9 @@
}
},
"node_modules/@svgdotjs/svg.select.js": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/@svgdotjs/svg.select.js/-/svg.select.js-4.0.2.tgz",
"integrity": "sha512-5gWdrvoQX3keo03SCmgaBbD+kFftq0F/f2bzCbNnpkkvW6tk4rl4MakORzFuNjvXPWwB4az9GwuvVxQVnjaK2g==",
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/@svgdotjs/svg.select.js/-/svg.select.js-4.0.3.tgz",
"integrity": "sha512-qkMgso1sd2hXKd1FZ1weO7ANq12sNmQJeGDjs46QwDVsxSRcHmvWKL2NDF7Yimpwf3sl5esOLkPqtV2bQ3v/Jg==",
"license": "MIT",
"peer": true,
"engines": {
......@@ -6246,6 +6274,1083 @@
"@svgdotjs/svg.js": "^3.2.4"
}
},
"node_modules/@syncfusion/ej2-angular-base": {
"version": "29.2.10",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-angular-base/-/ej2-angular-base-29.2.10.tgz",
"integrity": "sha512-jU9K1pybX7CLEkWO7ufQmGKuSBWl1rsUOm9x04BLonGNUp6fSUXOqN1niruhhOE1ZlSHGqzKp1PTjR6EmMe6Xw==",
"hasInstallScript": true,
"license": "SEE LICENSE IN license",
"dependencies": {
"@syncfusion/ej2-base": "~29.2.4",
"@syncfusion/ej2-icons": "~29.2.4"
}
},
"node_modules/@syncfusion/ej2-angular-base/node_modules/@syncfusion/ej2-base": {
"version": "29.2.11",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-base/-/ej2-base-29.2.11.tgz",
"integrity": "sha512-f1yM+6606zde7MR4q4d/q6foR3viHDA1/SkkTbLVwBME7I/0AcMG/i0eeTWJfJS8dgn91DGrsrEl0nZ9IA1CEQ==",
"license": "SEE LICENSE IN license",
"dependencies": {
"@syncfusion/ej2-icons": "~29.2.4"
},
"bin": {
"syncfusion-license": "bin/syncfusion-license.js"
}
},
"node_modules/@syncfusion/ej2-angular-base/node_modules/@syncfusion/ej2-icons": {
"version": "29.2.4",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-icons/-/ej2-icons-29.2.4.tgz",
"integrity": "sha512-2DYZglxLM++w0S2wB2bcr/Akk1MODdVhfFYLaLqe6mDHDRicFeTEyvnGPx2SHS2EXXlNNFm2GfNWoNNxgs5MhQ==",
"license": "SEE LICENSE IN license"
},
"node_modules/@syncfusion/ej2-angular-charts": {
"version": "29.2.11",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-angular-charts/-/ej2-angular-charts-29.2.11.tgz",
"integrity": "sha512-cO4vSTpDQxy4rdvEb1OlSRT2270YCBAUH9pdz9EhZI4lz3XCgcXL/ztv4D3V2MggvPQWU9yOpqjKv1Hg9EwrvQ==",
"license": "SEE LICENSE IN license",
"dependencies": {
"@syncfusion/ej2-angular-base": "~29.2.10",
"@syncfusion/ej2-base": "~29.2.11",
"@syncfusion/ej2-charts": "29.2.11"
}
},
"node_modules/@syncfusion/ej2-angular-charts/node_modules/@syncfusion/ej2-base": {
"version": "29.2.11",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-base/-/ej2-base-29.2.11.tgz",
"integrity": "sha512-f1yM+6606zde7MR4q4d/q6foR3viHDA1/SkkTbLVwBME7I/0AcMG/i0eeTWJfJS8dgn91DGrsrEl0nZ9IA1CEQ==",
"license": "SEE LICENSE IN license",
"dependencies": {
"@syncfusion/ej2-icons": "~29.2.4"
},
"bin": {
"syncfusion-license": "bin/syncfusion-license.js"
}
},
"node_modules/@syncfusion/ej2-angular-charts/node_modules/@syncfusion/ej2-icons": {
"version": "29.2.4",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-icons/-/ej2-icons-29.2.4.tgz",
"integrity": "sha512-2DYZglxLM++w0S2wB2bcr/Akk1MODdVhfFYLaLqe6mDHDRicFeTEyvnGPx2SHS2EXXlNNFm2GfNWoNNxgs5MhQ==",
"license": "SEE LICENSE IN license"
},
"node_modules/@syncfusion/ej2-angular-dropdowns": {
"version": "29.2.11",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-angular-dropdowns/-/ej2-angular-dropdowns-29.2.11.tgz",
"integrity": "sha512-RHqkitoaf+d34iBJJSbrbvB+9+B1ILPxYzxR0V+k/wThMd6XXYN8/vCO3g0KrG9vRB/OGOY6JjRJ5YPG9sP2/g==",
"license": "SEE LICENSE IN license",
"dependencies": {
"@syncfusion/ej2-angular-base": "~29.2.10",
"@syncfusion/ej2-base": "~29.2.11",
"@syncfusion/ej2-dropdowns": "29.2.11"
}
},
"node_modules/@syncfusion/ej2-angular-dropdowns/node_modules/@syncfusion/ej2-base": {
"version": "29.2.11",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-base/-/ej2-base-29.2.11.tgz",
"integrity": "sha512-f1yM+6606zde7MR4q4d/q6foR3viHDA1/SkkTbLVwBME7I/0AcMG/i0eeTWJfJS8dgn91DGrsrEl0nZ9IA1CEQ==",
"license": "SEE LICENSE IN license",
"dependencies": {
"@syncfusion/ej2-icons": "~29.2.4"
},
"bin": {
"syncfusion-license": "bin/syncfusion-license.js"
}
},
"node_modules/@syncfusion/ej2-angular-dropdowns/node_modules/@syncfusion/ej2-buttons": {
"version": "29.2.5",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-buttons/-/ej2-buttons-29.2.5.tgz",
"integrity": "sha512-FbMi9rUi/7i8mpPA6KRaO7qW/3wIeH759u2/Ag0iMQUEdtdK7NvOCSFF29WESeBjW6GmEJ8zkW0340ymgXZruQ==",
"license": "SEE LICENSE IN license",
"dependencies": {
"@syncfusion/ej2-base": "~29.2.4"
}
},
"node_modules/@syncfusion/ej2-angular-dropdowns/node_modules/@syncfusion/ej2-data": {
"version": "29.2.4",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-data/-/ej2-data-29.2.4.tgz",
"integrity": "sha512-2R+N65OPq+yfwTXyBz7NdD8MHGoxUbJSEWGT0kNEGMiobiI4AL+hfMYEhXWXE+DqL5Pm0txA4gTXQprFXXbccg==",
"license": "SEE LICENSE IN license",
"dependencies": {
"@syncfusion/ej2-base": "~29.2.4"
}
},
"node_modules/@syncfusion/ej2-angular-dropdowns/node_modules/@syncfusion/ej2-dropdowns": {
"version": "29.2.11",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-dropdowns/-/ej2-dropdowns-29.2.11.tgz",
"integrity": "sha512-NtWQexED+1T4Ef/vPkG+C9ANkUD3QPvXelyQaS2+j+mzipOhSBGiDcJYIE1R1wtqC3BhLfMZBPJbuXGc0p+mmQ==",
"license": "SEE LICENSE IN license",
"dependencies": {
"@syncfusion/ej2-base": "~29.2.11",
"@syncfusion/ej2-data": "~29.2.4",
"@syncfusion/ej2-inputs": "~29.2.5",
"@syncfusion/ej2-lists": "~29.2.4",
"@syncfusion/ej2-navigations": "~29.2.8",
"@syncfusion/ej2-notifications": "~29.2.4",
"@syncfusion/ej2-popups": "~29.2.8"
}
},
"node_modules/@syncfusion/ej2-angular-dropdowns/node_modules/@syncfusion/ej2-icons": {
"version": "29.2.4",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-icons/-/ej2-icons-29.2.4.tgz",
"integrity": "sha512-2DYZglxLM++w0S2wB2bcr/Akk1MODdVhfFYLaLqe6mDHDRicFeTEyvnGPx2SHS2EXXlNNFm2GfNWoNNxgs5MhQ==",
"license": "SEE LICENSE IN license"
},
"node_modules/@syncfusion/ej2-angular-dropdowns/node_modules/@syncfusion/ej2-inputs": {
"version": "29.2.5",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-inputs/-/ej2-inputs-29.2.5.tgz",
"integrity": "sha512-3ZqMl005qL0tENHcKvBfKUHMYQ/S0FRYjUDQVDPRsg28tE0Lz87oyamrxLYne3zDBEJ7kxWWnaNpgrTVvxsVNw==",
"license": "SEE LICENSE IN license",
"dependencies": {
"@syncfusion/ej2-base": "~29.2.4",
"@syncfusion/ej2-buttons": "~29.2.5",
"@syncfusion/ej2-popups": "~29.2.4",
"@syncfusion/ej2-splitbuttons": "~29.2.4"
}
},
"node_modules/@syncfusion/ej2-angular-dropdowns/node_modules/@syncfusion/ej2-lists": {
"version": "29.2.4",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-lists/-/ej2-lists-29.2.4.tgz",
"integrity": "sha512-U64nGdZMjnhHG/+YCTF+WhJ3BoUxFyN2DXxv2qlC+wtayMbfioDRwqSQgxTkH8XE4TgrkRFenRtj5SbYLKliSw==",
"license": "SEE LICENSE IN license",
"dependencies": {
"@syncfusion/ej2-base": "~29.2.4",
"@syncfusion/ej2-buttons": "~29.2.4",
"@syncfusion/ej2-data": "~29.2.4",
"@syncfusion/ej2-popups": "~29.2.4"
}
},
"node_modules/@syncfusion/ej2-angular-dropdowns/node_modules/@syncfusion/ej2-navigations": {
"version": "29.2.8",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-navigations/-/ej2-navigations-29.2.8.tgz",
"integrity": "sha512-u14J+dEocqGIKgWCC1eiATv1nh40dgILsGPpGbYS+h9Y7y4YMBRzL2UCcuNHXgPKL7SiJ5lCaVORFco8cxyh6A==",
"license": "SEE LICENSE IN license",
"dependencies": {
"@syncfusion/ej2-base": "~29.2.4",
"@syncfusion/ej2-buttons": "~29.2.5",
"@syncfusion/ej2-data": "~29.2.4",
"@syncfusion/ej2-inputs": "~29.2.5",
"@syncfusion/ej2-lists": "~29.2.4",
"@syncfusion/ej2-popups": "~29.2.8"
}
},
"node_modules/@syncfusion/ej2-angular-dropdowns/node_modules/@syncfusion/ej2-notifications": {
"version": "29.2.4",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-notifications/-/ej2-notifications-29.2.4.tgz",
"integrity": "sha512-rB9OuGHWVfnl9aBno+4KRH3aESbakLKYT70V7wjTxwS7u11XbxYwLiA4/YKsTQg/zrgOtcr3Ma1sVOeMYW17gQ==",
"license": "SEE LICENSE IN license",
"dependencies": {
"@syncfusion/ej2-base": "~29.2.4",
"@syncfusion/ej2-buttons": "~29.2.4",
"@syncfusion/ej2-popups": "~29.2.4"
}
},
"node_modules/@syncfusion/ej2-angular-dropdowns/node_modules/@syncfusion/ej2-popups": {
"version": "29.2.8",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-popups/-/ej2-popups-29.2.8.tgz",
"integrity": "sha512-LsXfTiFd6dyoE8XIxFOgC24TeQCqgLdjp3VOg0tWvyuWiZ/skH2sBlF2Y1CWqNJS07OKhtcjLY1h446RTBlalQ==",
"license": "SEE LICENSE IN license",
"dependencies": {
"@syncfusion/ej2-base": "~29.2.4",
"@syncfusion/ej2-buttons": "~29.2.5"
}
},
"node_modules/@syncfusion/ej2-angular-dropdowns/node_modules/@syncfusion/ej2-splitbuttons": {
"version": "29.2.4",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-splitbuttons/-/ej2-splitbuttons-29.2.4.tgz",
"integrity": "sha512-sZKs0IZJ6+rDK2OLRl+m5Rs1ai84GE9NV2hpDG5CPFv5uM5WoOf33qYOyp6BSuBerpobpycoXDWH/mh1fZSCDw==",
"license": "SEE LICENSE IN license",
"dependencies": {
"@syncfusion/ej2-base": "~29.2.4",
"@syncfusion/ej2-popups": "~29.2.4"
}
},
"node_modules/@syncfusion/ej2-angular-grids": {
"version": "29.2.11",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-angular-grids/-/ej2-angular-grids-29.2.11.tgz",
"integrity": "sha512-b3LEMk0ol6+crlYtslaPsKGiVoS/XFU/M/koNiyxuX932qntjhMhyPQQW1sYap96u3lzTW9wef6/Y3aye7qnqQ==",
"license": "SEE LICENSE IN license",
"dependencies": {
"@syncfusion/ej2-angular-base": "~29.2.10",
"@syncfusion/ej2-base": "~29.2.11",
"@syncfusion/ej2-grids": "29.2.11"
}
},
"node_modules/@syncfusion/ej2-angular-grids/node_modules/@syncfusion/ej2-base": {
"version": "29.2.11",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-base/-/ej2-base-29.2.11.tgz",
"integrity": "sha512-f1yM+6606zde7MR4q4d/q6foR3viHDA1/SkkTbLVwBME7I/0AcMG/i0eeTWJfJS8dgn91DGrsrEl0nZ9IA1CEQ==",
"license": "SEE LICENSE IN license",
"dependencies": {
"@syncfusion/ej2-icons": "~29.2.4"
},
"bin": {
"syncfusion-license": "bin/syncfusion-license.js"
}
},
"node_modules/@syncfusion/ej2-angular-grids/node_modules/@syncfusion/ej2-buttons": {
"version": "29.2.5",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-buttons/-/ej2-buttons-29.2.5.tgz",
"integrity": "sha512-FbMi9rUi/7i8mpPA6KRaO7qW/3wIeH759u2/Ag0iMQUEdtdK7NvOCSFF29WESeBjW6GmEJ8zkW0340ymgXZruQ==",
"license": "SEE LICENSE IN license",
"dependencies": {
"@syncfusion/ej2-base": "~29.2.4"
}
},
"node_modules/@syncfusion/ej2-angular-grids/node_modules/@syncfusion/ej2-calendars": {
"version": "29.2.11",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-calendars/-/ej2-calendars-29.2.11.tgz",
"integrity": "sha512-RphIYxXwaDv59QiYjswIntNyDxOqd3oQqi2G9Z3zRD+Ez1u+ybY9koaWbj5Dq/5AsFhdNCwHhyEPX9gACZMsvA==",
"license": "SEE LICENSE IN license",
"dependencies": {
"@syncfusion/ej2-base": "~29.2.11",
"@syncfusion/ej2-buttons": "~29.2.5",
"@syncfusion/ej2-inputs": "~29.2.5",
"@syncfusion/ej2-lists": "~29.2.4",
"@syncfusion/ej2-popups": "~29.2.8"
}
},
"node_modules/@syncfusion/ej2-angular-grids/node_modules/@syncfusion/ej2-compression": {
"version": "29.2.4",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-compression/-/ej2-compression-29.2.4.tgz",
"integrity": "sha512-mcgFmgtITGxoX4zV4LrFGAOHOQD8Z7is85vQE+k2Bf4OI6rZsLNIQFm6KEFg+sZMOSrABzKzg49yrktmlus4TQ==",
"license": "SEE LICENSE IN license",
"dependencies": {
"@syncfusion/ej2-file-utils": "~29.2.4"
}
},
"node_modules/@syncfusion/ej2-angular-grids/node_modules/@syncfusion/ej2-data": {
"version": "29.2.4",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-data/-/ej2-data-29.2.4.tgz",
"integrity": "sha512-2R+N65OPq+yfwTXyBz7NdD8MHGoxUbJSEWGT0kNEGMiobiI4AL+hfMYEhXWXE+DqL5Pm0txA4gTXQprFXXbccg==",
"license": "SEE LICENSE IN license",
"dependencies": {
"@syncfusion/ej2-base": "~29.2.4"
}
},
"node_modules/@syncfusion/ej2-angular-grids/node_modules/@syncfusion/ej2-dropdowns": {
"version": "29.2.11",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-dropdowns/-/ej2-dropdowns-29.2.11.tgz",
"integrity": "sha512-NtWQexED+1T4Ef/vPkG+C9ANkUD3QPvXelyQaS2+j+mzipOhSBGiDcJYIE1R1wtqC3BhLfMZBPJbuXGc0p+mmQ==",
"license": "SEE LICENSE IN license",
"dependencies": {
"@syncfusion/ej2-base": "~29.2.11",
"@syncfusion/ej2-data": "~29.2.4",
"@syncfusion/ej2-inputs": "~29.2.5",
"@syncfusion/ej2-lists": "~29.2.4",
"@syncfusion/ej2-navigations": "~29.2.8",
"@syncfusion/ej2-notifications": "~29.2.4",
"@syncfusion/ej2-popups": "~29.2.8"
}
},
"node_modules/@syncfusion/ej2-angular-grids/node_modules/@syncfusion/ej2-excel-export": {
"version": "29.2.4",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-excel-export/-/ej2-excel-export-29.2.4.tgz",
"integrity": "sha512-N3VAdlhP+Wq/Uyl82ZSx6x8zL3slgtEKzWb13nrfas7cdJicYrbx02YZru5/euaeE3yGTlbKmhjjLLbkVFU/kQ==",
"license": "SEE LICENSE IN license",
"dependencies": {
"@syncfusion/ej2-base": "~29.2.4",
"@syncfusion/ej2-compression": "~29.2.4"
}
},
"node_modules/@syncfusion/ej2-angular-grids/node_modules/@syncfusion/ej2-file-utils": {
"version": "29.2.4",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-file-utils/-/ej2-file-utils-29.2.4.tgz",
"integrity": "sha512-nnvZ+1xVULe418habMciZnDAoMOM+TqFaFpO9kf4Cct9RPYUr22Tsvu84CZI7yzzKj/dLfD4n5TRRCHVo+5vVA==",
"license": "SEE LICENSE IN license"
},
"node_modules/@syncfusion/ej2-angular-grids/node_modules/@syncfusion/ej2-grids": {
"version": "29.2.11",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-grids/-/ej2-grids-29.2.11.tgz",
"integrity": "sha512-11jI7ve/qxQVsA4kP+v0Y1bEfO7kFCCtBekag80Fy+yio88Wcr5c1Q9SxgsmZtX+Kzk6oSymS497/zNcBDsUJw==",
"license": "SEE LICENSE IN license",
"dependencies": {
"@syncfusion/ej2-base": "~29.2.11",
"@syncfusion/ej2-buttons": "~29.2.5",
"@syncfusion/ej2-calendars": "~29.2.11",
"@syncfusion/ej2-compression": "~29.2.4",
"@syncfusion/ej2-data": "~29.2.4",
"@syncfusion/ej2-dropdowns": "~29.2.11",
"@syncfusion/ej2-excel-export": "~29.2.4",
"@syncfusion/ej2-file-utils": "~29.2.4",
"@syncfusion/ej2-inputs": "~29.2.5",
"@syncfusion/ej2-lists": "~29.2.4",
"@syncfusion/ej2-navigations": "~29.2.8",
"@syncfusion/ej2-notifications": "~29.2.4",
"@syncfusion/ej2-pdf-export": "~29.2.4",
"@syncfusion/ej2-popups": "~29.2.8",
"@syncfusion/ej2-splitbuttons": "~29.2.4"
}
},
"node_modules/@syncfusion/ej2-angular-grids/node_modules/@syncfusion/ej2-icons": {
"version": "29.2.4",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-icons/-/ej2-icons-29.2.4.tgz",
"integrity": "sha512-2DYZglxLM++w0S2wB2bcr/Akk1MODdVhfFYLaLqe6mDHDRicFeTEyvnGPx2SHS2EXXlNNFm2GfNWoNNxgs5MhQ==",
"license": "SEE LICENSE IN license"
},
"node_modules/@syncfusion/ej2-angular-grids/node_modules/@syncfusion/ej2-inputs": {
"version": "29.2.5",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-inputs/-/ej2-inputs-29.2.5.tgz",
"integrity": "sha512-3ZqMl005qL0tENHcKvBfKUHMYQ/S0FRYjUDQVDPRsg28tE0Lz87oyamrxLYne3zDBEJ7kxWWnaNpgrTVvxsVNw==",
"license": "SEE LICENSE IN license",
"dependencies": {
"@syncfusion/ej2-base": "~29.2.4",
"@syncfusion/ej2-buttons": "~29.2.5",
"@syncfusion/ej2-popups": "~29.2.4",
"@syncfusion/ej2-splitbuttons": "~29.2.4"
}
},
"node_modules/@syncfusion/ej2-angular-grids/node_modules/@syncfusion/ej2-lists": {
"version": "29.2.4",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-lists/-/ej2-lists-29.2.4.tgz",
"integrity": "sha512-U64nGdZMjnhHG/+YCTF+WhJ3BoUxFyN2DXxv2qlC+wtayMbfioDRwqSQgxTkH8XE4TgrkRFenRtj5SbYLKliSw==",
"license": "SEE LICENSE IN license",
"dependencies": {
"@syncfusion/ej2-base": "~29.2.4",
"@syncfusion/ej2-buttons": "~29.2.4",
"@syncfusion/ej2-data": "~29.2.4",
"@syncfusion/ej2-popups": "~29.2.4"
}
},
"node_modules/@syncfusion/ej2-angular-grids/node_modules/@syncfusion/ej2-navigations": {
"version": "29.2.8",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-navigations/-/ej2-navigations-29.2.8.tgz",
"integrity": "sha512-u14J+dEocqGIKgWCC1eiATv1nh40dgILsGPpGbYS+h9Y7y4YMBRzL2UCcuNHXgPKL7SiJ5lCaVORFco8cxyh6A==",
"license": "SEE LICENSE IN license",
"dependencies": {
"@syncfusion/ej2-base": "~29.2.4",
"@syncfusion/ej2-buttons": "~29.2.5",
"@syncfusion/ej2-data": "~29.2.4",
"@syncfusion/ej2-inputs": "~29.2.5",
"@syncfusion/ej2-lists": "~29.2.4",
"@syncfusion/ej2-popups": "~29.2.8"
}
},
"node_modules/@syncfusion/ej2-angular-grids/node_modules/@syncfusion/ej2-notifications": {
"version": "29.2.4",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-notifications/-/ej2-notifications-29.2.4.tgz",
"integrity": "sha512-rB9OuGHWVfnl9aBno+4KRH3aESbakLKYT70V7wjTxwS7u11XbxYwLiA4/YKsTQg/zrgOtcr3Ma1sVOeMYW17gQ==",
"license": "SEE LICENSE IN license",
"dependencies": {
"@syncfusion/ej2-base": "~29.2.4",
"@syncfusion/ej2-buttons": "~29.2.4",
"@syncfusion/ej2-popups": "~29.2.4"
}
},
"node_modules/@syncfusion/ej2-angular-grids/node_modules/@syncfusion/ej2-pdf-export": {
"version": "29.2.4",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-pdf-export/-/ej2-pdf-export-29.2.4.tgz",
"integrity": "sha512-Cb5QKYqhuvEb96DzqJT6/nLuan8FkA40mU4Lggg5Ujo1OdIK3Ca5GolM7HStTct4DnU02UKuXTrqoL5umGTfKQ==",
"license": "SEE LICENSE IN license",
"dependencies": {
"@syncfusion/ej2-compression": "~29.2.4"
}
},
"node_modules/@syncfusion/ej2-angular-grids/node_modules/@syncfusion/ej2-popups": {
"version": "29.2.8",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-popups/-/ej2-popups-29.2.8.tgz",
"integrity": "sha512-LsXfTiFd6dyoE8XIxFOgC24TeQCqgLdjp3VOg0tWvyuWiZ/skH2sBlF2Y1CWqNJS07OKhtcjLY1h446RTBlalQ==",
"license": "SEE LICENSE IN license",
"dependencies": {
"@syncfusion/ej2-base": "~29.2.4",
"@syncfusion/ej2-buttons": "~29.2.5"
}
},
"node_modules/@syncfusion/ej2-angular-grids/node_modules/@syncfusion/ej2-splitbuttons": {
"version": "29.2.4",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-splitbuttons/-/ej2-splitbuttons-29.2.4.tgz",
"integrity": "sha512-sZKs0IZJ6+rDK2OLRl+m5Rs1ai84GE9NV2hpDG5CPFv5uM5WoOf33qYOyp6BSuBerpobpycoXDWH/mh1fZSCDw==",
"license": "SEE LICENSE IN license",
"dependencies": {
"@syncfusion/ej2-base": "~29.2.4",
"@syncfusion/ej2-popups": "~29.2.4"
}
},
"node_modules/@syncfusion/ej2-angular-inputs": {
"version": "29.2.5",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-angular-inputs/-/ej2-angular-inputs-29.2.5.tgz",
"integrity": "sha512-839rdXnftfuXQXeLE/u3InLwNmjR/NiQifM7F1BY6ZfoKNAm2jfZMI+t3sX7j87/DBIeUPaUkJNhkjjhl1b2Jg==",
"license": "SEE LICENSE IN license",
"dependencies": {
"@syncfusion/ej2-angular-base": "~29.2.4",
"@syncfusion/ej2-base": "~29.2.4",
"@syncfusion/ej2-inputs": "29.2.5"
}
},
"node_modules/@syncfusion/ej2-angular-inputs/node_modules/@syncfusion/ej2-base": {
"version": "29.2.11",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-base/-/ej2-base-29.2.11.tgz",
"integrity": "sha512-f1yM+6606zde7MR4q4d/q6foR3viHDA1/SkkTbLVwBME7I/0AcMG/i0eeTWJfJS8dgn91DGrsrEl0nZ9IA1CEQ==",
"license": "SEE LICENSE IN license",
"dependencies": {
"@syncfusion/ej2-icons": "~29.2.4"
},
"bin": {
"syncfusion-license": "bin/syncfusion-license.js"
}
},
"node_modules/@syncfusion/ej2-angular-inputs/node_modules/@syncfusion/ej2-buttons": {
"version": "29.2.5",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-buttons/-/ej2-buttons-29.2.5.tgz",
"integrity": "sha512-FbMi9rUi/7i8mpPA6KRaO7qW/3wIeH759u2/Ag0iMQUEdtdK7NvOCSFF29WESeBjW6GmEJ8zkW0340ymgXZruQ==",
"license": "SEE LICENSE IN license",
"dependencies": {
"@syncfusion/ej2-base": "~29.2.4"
}
},
"node_modules/@syncfusion/ej2-angular-inputs/node_modules/@syncfusion/ej2-icons": {
"version": "29.2.4",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-icons/-/ej2-icons-29.2.4.tgz",
"integrity": "sha512-2DYZglxLM++w0S2wB2bcr/Akk1MODdVhfFYLaLqe6mDHDRicFeTEyvnGPx2SHS2EXXlNNFm2GfNWoNNxgs5MhQ==",
"license": "SEE LICENSE IN license"
},
"node_modules/@syncfusion/ej2-angular-inputs/node_modules/@syncfusion/ej2-inputs": {
"version": "29.2.5",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-inputs/-/ej2-inputs-29.2.5.tgz",
"integrity": "sha512-3ZqMl005qL0tENHcKvBfKUHMYQ/S0FRYjUDQVDPRsg28tE0Lz87oyamrxLYne3zDBEJ7kxWWnaNpgrTVvxsVNw==",
"license": "SEE LICENSE IN license",
"dependencies": {
"@syncfusion/ej2-base": "~29.2.4",
"@syncfusion/ej2-buttons": "~29.2.5",
"@syncfusion/ej2-popups": "~29.2.4",
"@syncfusion/ej2-splitbuttons": "~29.2.4"
}
},
"node_modules/@syncfusion/ej2-angular-inputs/node_modules/@syncfusion/ej2-popups": {
"version": "29.2.8",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-popups/-/ej2-popups-29.2.8.tgz",
"integrity": "sha512-LsXfTiFd6dyoE8XIxFOgC24TeQCqgLdjp3VOg0tWvyuWiZ/skH2sBlF2Y1CWqNJS07OKhtcjLY1h446RTBlalQ==",
"license": "SEE LICENSE IN license",
"dependencies": {
"@syncfusion/ej2-base": "~29.2.4",
"@syncfusion/ej2-buttons": "~29.2.5"
}
},
"node_modules/@syncfusion/ej2-angular-inputs/node_modules/@syncfusion/ej2-splitbuttons": {
"version": "29.2.4",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-splitbuttons/-/ej2-splitbuttons-29.2.4.tgz",
"integrity": "sha512-sZKs0IZJ6+rDK2OLRl+m5Rs1ai84GE9NV2hpDG5CPFv5uM5WoOf33qYOyp6BSuBerpobpycoXDWH/mh1fZSCDw==",
"license": "SEE LICENSE IN license",
"dependencies": {
"@syncfusion/ej2-base": "~29.2.4",
"@syncfusion/ej2-popups": "~29.2.4"
}
},
"node_modules/@syncfusion/ej2-angular-pivotview": {
"version": "29.2.10",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-angular-pivotview/-/ej2-angular-pivotview-29.2.10.tgz",
"integrity": "sha512-9hzFYNup01xU556Vzlz1A9sd+VQQ2YJ91o77H4Ft0qSUd4O1fdobP8QeYkpy8VGEk5tlch0M+VS6bv9NeggXfw==",
"license": "SEE LICENSE IN license",
"dependencies": {
"@syncfusion/ej2-angular-base": "~29.2.10",
"@syncfusion/ej2-base": "~29.2.4",
"@syncfusion/ej2-pivotview": "29.2.10"
}
},
"node_modules/@syncfusion/ej2-angular-pivotview/node_modules/@syncfusion/ej2-base": {
"version": "29.2.11",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-base/-/ej2-base-29.2.11.tgz",
"integrity": "sha512-f1yM+6606zde7MR4q4d/q6foR3viHDA1/SkkTbLVwBME7I/0AcMG/i0eeTWJfJS8dgn91DGrsrEl0nZ9IA1CEQ==",
"license": "SEE LICENSE IN license",
"dependencies": {
"@syncfusion/ej2-icons": "~29.2.4"
},
"bin": {
"syncfusion-license": "bin/syncfusion-license.js"
}
},
"node_modules/@syncfusion/ej2-angular-pivotview/node_modules/@syncfusion/ej2-icons": {
"version": "29.2.4",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-icons/-/ej2-icons-29.2.4.tgz",
"integrity": "sha512-2DYZglxLM++w0S2wB2bcr/Akk1MODdVhfFYLaLqe6mDHDRicFeTEyvnGPx2SHS2EXXlNNFm2GfNWoNNxgs5MhQ==",
"license": "SEE LICENSE IN license"
},
"node_modules/@syncfusion/ej2-base": {
"version": "30.1.38",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-base/-/ej2-base-30.1.38.tgz",
"integrity": "sha512-cj01hiMSAt7QB2bnA0hI1IGolYnqXpmv4I4cWyBB+D8V2LIiWIfqZA4I3LuffPeo60BV96XOeI6NnseK2RTIuQ==",
"license": "SEE LICENSE IN license",
"dependencies": {
"@syncfusion/ej2-icons": "~30.1.37"
},
"bin": {
"syncfusion-license": "bin/syncfusion-license.js"
}
},
"node_modules/@syncfusion/ej2-buttons": {
"version": "30.1.37",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-buttons/-/ej2-buttons-30.1.37.tgz",
"integrity": "sha512-31dPIDSkraFlR8+sCz2NtM1tg9TSBN2YRHC7mBZGDWX6B26ie4zbe63S22l0ytaoMDt3XHqExsk7MLlnG3LR8g==",
"license": "SEE LICENSE IN license",
"dependencies": {
"@syncfusion/ej2-base": "~30.1.37"
}
},
"node_modules/@syncfusion/ej2-calendars": {
"version": "30.1.37",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-calendars/-/ej2-calendars-30.1.37.tgz",
"integrity": "sha512-9g41zBRizXilbRJWHJgqRYJPpX0WJF5qJwGNpgvsBnyYdolYQy///x6c3oYo9W6YG28N0QDavdbOaxwprj4ghg==",
"license": "SEE LICENSE IN license",
"dependencies": {
"@syncfusion/ej2-base": "~30.1.37",
"@syncfusion/ej2-buttons": "~30.1.37",
"@syncfusion/ej2-inputs": "~30.1.37",
"@syncfusion/ej2-lists": "~30.1.37",
"@syncfusion/ej2-popups": "~30.1.37"
}
},
"node_modules/@syncfusion/ej2-charts": {
"version": "29.2.11",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-charts/-/ej2-charts-29.2.11.tgz",
"integrity": "sha512-OJ3BlnrPVC/p2rEQvv2l9RtjnAAZu8aszLVVuWlXi3ivjbt9MZb4AiA6f8alHDxKFbfUKoOnTnx/wGLNHUqmTA==",
"license": "SEE LICENSE IN license",
"dependencies": {
"@syncfusion/ej2-base": "~29.2.11",
"@syncfusion/ej2-calendars": "~29.2.11",
"@syncfusion/ej2-data": "~29.2.4",
"@syncfusion/ej2-excel-export": "~29.2.4",
"@syncfusion/ej2-navigations": "~29.2.8",
"@syncfusion/ej2-pdf-export": "~29.2.4",
"@syncfusion/ej2-svg-base": "~29.2.4"
}
},
"node_modules/@syncfusion/ej2-charts/node_modules/@syncfusion/ej2-base": {
"version": "29.2.11",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-base/-/ej2-base-29.2.11.tgz",
"integrity": "sha512-f1yM+6606zde7MR4q4d/q6foR3viHDA1/SkkTbLVwBME7I/0AcMG/i0eeTWJfJS8dgn91DGrsrEl0nZ9IA1CEQ==",
"license": "SEE LICENSE IN license",
"dependencies": {
"@syncfusion/ej2-icons": "~29.2.4"
},
"bin": {
"syncfusion-license": "bin/syncfusion-license.js"
}
},
"node_modules/@syncfusion/ej2-charts/node_modules/@syncfusion/ej2-buttons": {
"version": "29.2.5",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-buttons/-/ej2-buttons-29.2.5.tgz",
"integrity": "sha512-FbMi9rUi/7i8mpPA6KRaO7qW/3wIeH759u2/Ag0iMQUEdtdK7NvOCSFF29WESeBjW6GmEJ8zkW0340ymgXZruQ==",
"license": "SEE LICENSE IN license",
"dependencies": {
"@syncfusion/ej2-base": "~29.2.4"
}
},
"node_modules/@syncfusion/ej2-charts/node_modules/@syncfusion/ej2-calendars": {
"version": "29.2.11",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-calendars/-/ej2-calendars-29.2.11.tgz",
"integrity": "sha512-RphIYxXwaDv59QiYjswIntNyDxOqd3oQqi2G9Z3zRD+Ez1u+ybY9koaWbj5Dq/5AsFhdNCwHhyEPX9gACZMsvA==",
"license": "SEE LICENSE IN license",
"dependencies": {
"@syncfusion/ej2-base": "~29.2.11",
"@syncfusion/ej2-buttons": "~29.2.5",
"@syncfusion/ej2-inputs": "~29.2.5",
"@syncfusion/ej2-lists": "~29.2.4",
"@syncfusion/ej2-popups": "~29.2.8"
}
},
"node_modules/@syncfusion/ej2-charts/node_modules/@syncfusion/ej2-compression": {
"version": "29.2.4",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-compression/-/ej2-compression-29.2.4.tgz",
"integrity": "sha512-mcgFmgtITGxoX4zV4LrFGAOHOQD8Z7is85vQE+k2Bf4OI6rZsLNIQFm6KEFg+sZMOSrABzKzg49yrktmlus4TQ==",
"license": "SEE LICENSE IN license",
"dependencies": {
"@syncfusion/ej2-file-utils": "~29.2.4"
}
},
"node_modules/@syncfusion/ej2-charts/node_modules/@syncfusion/ej2-data": {
"version": "29.2.4",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-data/-/ej2-data-29.2.4.tgz",
"integrity": "sha512-2R+N65OPq+yfwTXyBz7NdD8MHGoxUbJSEWGT0kNEGMiobiI4AL+hfMYEhXWXE+DqL5Pm0txA4gTXQprFXXbccg==",
"license": "SEE LICENSE IN license",
"dependencies": {
"@syncfusion/ej2-base": "~29.2.4"
}
},
"node_modules/@syncfusion/ej2-charts/node_modules/@syncfusion/ej2-excel-export": {
"version": "29.2.4",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-excel-export/-/ej2-excel-export-29.2.4.tgz",
"integrity": "sha512-N3VAdlhP+Wq/Uyl82ZSx6x8zL3slgtEKzWb13nrfas7cdJicYrbx02YZru5/euaeE3yGTlbKmhjjLLbkVFU/kQ==",
"license": "SEE LICENSE IN license",
"dependencies": {
"@syncfusion/ej2-base": "~29.2.4",
"@syncfusion/ej2-compression": "~29.2.4"
}
},
"node_modules/@syncfusion/ej2-charts/node_modules/@syncfusion/ej2-file-utils": {
"version": "29.2.4",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-file-utils/-/ej2-file-utils-29.2.4.tgz",
"integrity": "sha512-nnvZ+1xVULe418habMciZnDAoMOM+TqFaFpO9kf4Cct9RPYUr22Tsvu84CZI7yzzKj/dLfD4n5TRRCHVo+5vVA==",
"license": "SEE LICENSE IN license"
},
"node_modules/@syncfusion/ej2-charts/node_modules/@syncfusion/ej2-icons": {
"version": "29.2.4",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-icons/-/ej2-icons-29.2.4.tgz",
"integrity": "sha512-2DYZglxLM++w0S2wB2bcr/Akk1MODdVhfFYLaLqe6mDHDRicFeTEyvnGPx2SHS2EXXlNNFm2GfNWoNNxgs5MhQ==",
"license": "SEE LICENSE IN license"
},
"node_modules/@syncfusion/ej2-charts/node_modules/@syncfusion/ej2-inputs": {
"version": "29.2.5",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-inputs/-/ej2-inputs-29.2.5.tgz",
"integrity": "sha512-3ZqMl005qL0tENHcKvBfKUHMYQ/S0FRYjUDQVDPRsg28tE0Lz87oyamrxLYne3zDBEJ7kxWWnaNpgrTVvxsVNw==",
"license": "SEE LICENSE IN license",
"dependencies": {
"@syncfusion/ej2-base": "~29.2.4",
"@syncfusion/ej2-buttons": "~29.2.5",
"@syncfusion/ej2-popups": "~29.2.4",
"@syncfusion/ej2-splitbuttons": "~29.2.4"
}
},
"node_modules/@syncfusion/ej2-charts/node_modules/@syncfusion/ej2-lists": {
"version": "29.2.4",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-lists/-/ej2-lists-29.2.4.tgz",
"integrity": "sha512-U64nGdZMjnhHG/+YCTF+WhJ3BoUxFyN2DXxv2qlC+wtayMbfioDRwqSQgxTkH8XE4TgrkRFenRtj5SbYLKliSw==",
"license": "SEE LICENSE IN license",
"dependencies": {
"@syncfusion/ej2-base": "~29.2.4",
"@syncfusion/ej2-buttons": "~29.2.4",
"@syncfusion/ej2-data": "~29.2.4",
"@syncfusion/ej2-popups": "~29.2.4"
}
},
"node_modules/@syncfusion/ej2-charts/node_modules/@syncfusion/ej2-navigations": {
"version": "29.2.8",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-navigations/-/ej2-navigations-29.2.8.tgz",
"integrity": "sha512-u14J+dEocqGIKgWCC1eiATv1nh40dgILsGPpGbYS+h9Y7y4YMBRzL2UCcuNHXgPKL7SiJ5lCaVORFco8cxyh6A==",
"license": "SEE LICENSE IN license",
"dependencies": {
"@syncfusion/ej2-base": "~29.2.4",
"@syncfusion/ej2-buttons": "~29.2.5",
"@syncfusion/ej2-data": "~29.2.4",
"@syncfusion/ej2-inputs": "~29.2.5",
"@syncfusion/ej2-lists": "~29.2.4",
"@syncfusion/ej2-popups": "~29.2.8"
}
},
"node_modules/@syncfusion/ej2-charts/node_modules/@syncfusion/ej2-pdf-export": {
"version": "29.2.4",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-pdf-export/-/ej2-pdf-export-29.2.4.tgz",
"integrity": "sha512-Cb5QKYqhuvEb96DzqJT6/nLuan8FkA40mU4Lggg5Ujo1OdIK3Ca5GolM7HStTct4DnU02UKuXTrqoL5umGTfKQ==",
"license": "SEE LICENSE IN license",
"dependencies": {
"@syncfusion/ej2-compression": "~29.2.4"
}
},
"node_modules/@syncfusion/ej2-charts/node_modules/@syncfusion/ej2-popups": {
"version": "29.2.8",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-popups/-/ej2-popups-29.2.8.tgz",
"integrity": "sha512-LsXfTiFd6dyoE8XIxFOgC24TeQCqgLdjp3VOg0tWvyuWiZ/skH2sBlF2Y1CWqNJS07OKhtcjLY1h446RTBlalQ==",
"license": "SEE LICENSE IN license",
"dependencies": {
"@syncfusion/ej2-base": "~29.2.4",
"@syncfusion/ej2-buttons": "~29.2.5"
}
},
"node_modules/@syncfusion/ej2-charts/node_modules/@syncfusion/ej2-splitbuttons": {
"version": "29.2.4",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-splitbuttons/-/ej2-splitbuttons-29.2.4.tgz",
"integrity": "sha512-sZKs0IZJ6+rDK2OLRl+m5Rs1ai84GE9NV2hpDG5CPFv5uM5WoOf33qYOyp6BSuBerpobpycoXDWH/mh1fZSCDw==",
"license": "SEE LICENSE IN license",
"dependencies": {
"@syncfusion/ej2-base": "~29.2.4",
"@syncfusion/ej2-popups": "~29.2.4"
}
},
"node_modules/@syncfusion/ej2-compression": {
"version": "30.1.37",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-compression/-/ej2-compression-30.1.37.tgz",
"integrity": "sha512-QA5oAJZDaAONO12zwVCc1IqYTmiwthSoWaMoE0jgQR+mpYR8XmlMi1VOvP8Jtu+HlfQMaEGcMQ++UM5UeZW4+g==",
"license": "SEE LICENSE IN license",
"dependencies": {
"@syncfusion/ej2-file-utils": "~30.1.37"
}
},
"node_modules/@syncfusion/ej2-data": {
"version": "30.1.38",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-data/-/ej2-data-30.1.38.tgz",
"integrity": "sha512-BdqvjLzzK4OuUR1YlzPSG3SmeGg1mrLz/6ih5oD9dSpRXDoMG24bpO1rwCK7mjy8Dp9IJ8mliyCbPfoDycxM9Q==",
"license": "SEE LICENSE IN license",
"dependencies": {
"@syncfusion/ej2-base": "~30.1.38"
}
},
"node_modules/@syncfusion/ej2-dropdowns": {
"version": "30.1.39",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-dropdowns/-/ej2-dropdowns-30.1.39.tgz",
"integrity": "sha512-AnNCR5RRtRa4Svuza4Zk34qMVp7cPxpahi8r+4hxZZJVp856DvC34VPX2AQeRpe+s1/+NNtGo0eaqMmamJvu3w==",
"license": "SEE LICENSE IN license",
"dependencies": {
"@syncfusion/ej2-base": "~30.1.38",
"@syncfusion/ej2-data": "~30.1.38",
"@syncfusion/ej2-inputs": "~30.1.38",
"@syncfusion/ej2-lists": "~30.1.37",
"@syncfusion/ej2-navigations": "~30.1.39",
"@syncfusion/ej2-notifications": "~30.1.37",
"@syncfusion/ej2-popups": "~30.1.37"
}
},
"node_modules/@syncfusion/ej2-excel-export": {
"version": "30.1.37",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-excel-export/-/ej2-excel-export-30.1.37.tgz",
"integrity": "sha512-AMOTpy/uzb6QzKHgQdofA2P0Ec1KLmfT9vDXPzmF3npsPbqL0a+6K73MaZZKjW8rBrwohnbJV2R+acotpW/Seg==",
"license": "SEE LICENSE IN license",
"dependencies": {
"@syncfusion/ej2-base": "~30.1.37",
"@syncfusion/ej2-compression": "~30.1.37"
}
},
"node_modules/@syncfusion/ej2-file-utils": {
"version": "30.1.37",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-file-utils/-/ej2-file-utils-30.1.37.tgz",
"integrity": "sha512-1r0rWrXEsXsRJhG0yOs8Hp2nGARLsBCD4u602R4IxLDrockIPVJv4gz0rIw4dDtFW7bpAa8J+yWXtr6fW29Bhw==",
"license": "SEE LICENSE IN license"
},
"node_modules/@syncfusion/ej2-grids": {
"version": "30.1.39",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-grids/-/ej2-grids-30.1.39.tgz",
"integrity": "sha512-bXvF6UbImOM2PhXOcAtXy08RsBiO6hAbDsw+vVYMTPTXfe2iYWufYAyjIddFYfdPTJsi3VBPMf0Uk84rFDlinw==",
"license": "SEE LICENSE IN license",
"dependencies": {
"@syncfusion/ej2-base": "~30.1.38",
"@syncfusion/ej2-buttons": "~30.1.37",
"@syncfusion/ej2-calendars": "~30.1.37",
"@syncfusion/ej2-compression": "~30.1.37",
"@syncfusion/ej2-data": "~30.1.38",
"@syncfusion/ej2-dropdowns": "~30.1.39",
"@syncfusion/ej2-excel-export": "~30.1.37",
"@syncfusion/ej2-file-utils": "~30.1.37",
"@syncfusion/ej2-inputs": "~30.1.38",
"@syncfusion/ej2-lists": "~30.1.37",
"@syncfusion/ej2-navigations": "~30.1.39",
"@syncfusion/ej2-notifications": "~30.1.37",
"@syncfusion/ej2-pdf-export": "~30.1.38",
"@syncfusion/ej2-popups": "~30.1.37",
"@syncfusion/ej2-splitbuttons": "~30.1.39"
}
},
"node_modules/@syncfusion/ej2-icons": {
"version": "30.1.37",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-icons/-/ej2-icons-30.1.37.tgz",
"integrity": "sha512-bgPklX0jiaLESbWJDNx8f8PqVexwbRJHoz4kDgOJR4VN4JoOtC7Zw47Wb+eFwj13Ped1lTfuPfmU8sI1s5INeQ==",
"license": "SEE LICENSE IN license"
},
"node_modules/@syncfusion/ej2-inputs": {
"version": "30.1.38",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-inputs/-/ej2-inputs-30.1.38.tgz",
"integrity": "sha512-slZHBwCbCCus4L41FvUBejOciHHfjx2XuVXx+dxmWh4IStSRn0T40WKMC8Nz8BY3X4/Wc70AbDnYcOq99erqvg==",
"license": "SEE LICENSE IN license",
"dependencies": {
"@syncfusion/ej2-base": "~30.1.38",
"@syncfusion/ej2-buttons": "~30.1.37",
"@syncfusion/ej2-popups": "~30.1.37",
"@syncfusion/ej2-splitbuttons": "~30.1.37"
}
},
"node_modules/@syncfusion/ej2-lists": {
"version": "30.1.37",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-lists/-/ej2-lists-30.1.37.tgz",
"integrity": "sha512-Tot/4yjBoBlCPr49EKPL/yDHFoPhHlgp3bK6+LG5jgThQE+guB5sSlqlNl3Noui25oMTRcXlt29SHZwstdpsNg==",
"license": "SEE LICENSE IN license",
"dependencies": {
"@syncfusion/ej2-base": "~30.1.37",
"@syncfusion/ej2-buttons": "~30.1.37",
"@syncfusion/ej2-data": "~30.1.37",
"@syncfusion/ej2-popups": "~30.1.37"
}
},
"node_modules/@syncfusion/ej2-navigations": {
"version": "30.1.39",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-navigations/-/ej2-navigations-30.1.39.tgz",
"integrity": "sha512-mehYOsIcgGEMHbSwdnxxSqcPwhyQpVCJ6VgLESBikkTIww3NAFwRkhMcs4o4n0z+3KyySUVKNi1zU13mzlYkoQ==",
"license": "SEE LICENSE IN license",
"dependencies": {
"@syncfusion/ej2-base": "~30.1.38",
"@syncfusion/ej2-buttons": "~30.1.37",
"@syncfusion/ej2-data": "~30.1.38",
"@syncfusion/ej2-inputs": "~30.1.38",
"@syncfusion/ej2-lists": "~30.1.37",
"@syncfusion/ej2-popups": "~30.1.37"
}
},
"node_modules/@syncfusion/ej2-notifications": {
"version": "30.1.37",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-notifications/-/ej2-notifications-30.1.37.tgz",
"integrity": "sha512-ksXk9lTf0Iw5+4EarYjxG1rKU+e9ZbCOaxvl/PN+MP1w1BAFwu4jRwpE9mbPF2FQUxcL1xNCxTnspSQaH36f4Q==",
"license": "SEE LICENSE IN license",
"dependencies": {
"@syncfusion/ej2-base": "~30.1.37",
"@syncfusion/ej2-buttons": "~30.1.37",
"@syncfusion/ej2-popups": "~30.1.37"
}
},
"node_modules/@syncfusion/ej2-pdf-export": {
"version": "30.1.38",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-pdf-export/-/ej2-pdf-export-30.1.38.tgz",
"integrity": "sha512-iWgy/40rgKGweC3CJozn3MxcwKRg4OVoS7bnpkF+aDWFlJu4qV2bjZawacwASCitca+0jSs5aT7KOc24cmqJ4w==",
"license": "SEE LICENSE IN license",
"dependencies": {
"@syncfusion/ej2-compression": "~30.1.37"
}
},
"node_modules/@syncfusion/ej2-pivotview": {
"version": "29.2.10",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-pivotview/-/ej2-pivotview-29.2.10.tgz",
"integrity": "sha512-O/DQIQ8JPghxqPyTSo39VHuw1WsCgpHEycAeOFRP4Jn4LjfcKJFjZ76WVTrwNftnAUuuVrzyAVUwv7rY567k9w==",
"license": "SEE LICENSE IN license",
"dependencies": {
"@syncfusion/ej2-base": "~29.2.4",
"@syncfusion/ej2-buttons": "~29.2.5",
"@syncfusion/ej2-calendars": "~29.2.10",
"@syncfusion/ej2-charts": "~29.2.10",
"@syncfusion/ej2-compression": "~29.2.4",
"@syncfusion/ej2-data": "~29.2.4",
"@syncfusion/ej2-dropdowns": "~29.2.10",
"@syncfusion/ej2-excel-export": "~29.2.4",
"@syncfusion/ej2-file-utils": "~29.2.4",
"@syncfusion/ej2-grids": "~29.2.10",
"@syncfusion/ej2-inputs": "~29.2.5",
"@syncfusion/ej2-lists": "~29.2.4",
"@syncfusion/ej2-navigations": "~29.2.8",
"@syncfusion/ej2-pdf-export": "~29.2.4",
"@syncfusion/ej2-popups": "~29.2.8",
"@syncfusion/ej2-splitbuttons": "~29.2.4",
"@syncfusion/ej2-svg-base": "~29.2.4"
}
},
"node_modules/@syncfusion/ej2-pivotview/node_modules/@syncfusion/ej2-base": {
"version": "29.2.11",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-base/-/ej2-base-29.2.11.tgz",
"integrity": "sha512-f1yM+6606zde7MR4q4d/q6foR3viHDA1/SkkTbLVwBME7I/0AcMG/i0eeTWJfJS8dgn91DGrsrEl0nZ9IA1CEQ==",
"license": "SEE LICENSE IN license",
"dependencies": {
"@syncfusion/ej2-icons": "~29.2.4"
},
"bin": {
"syncfusion-license": "bin/syncfusion-license.js"
}
},
"node_modules/@syncfusion/ej2-pivotview/node_modules/@syncfusion/ej2-buttons": {
"version": "29.2.5",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-buttons/-/ej2-buttons-29.2.5.tgz",
"integrity": "sha512-FbMi9rUi/7i8mpPA6KRaO7qW/3wIeH759u2/Ag0iMQUEdtdK7NvOCSFF29WESeBjW6GmEJ8zkW0340ymgXZruQ==",
"license": "SEE LICENSE IN license",
"dependencies": {
"@syncfusion/ej2-base": "~29.2.4"
}
},
"node_modules/@syncfusion/ej2-pivotview/node_modules/@syncfusion/ej2-calendars": {
"version": "29.2.11",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-calendars/-/ej2-calendars-29.2.11.tgz",
"integrity": "sha512-RphIYxXwaDv59QiYjswIntNyDxOqd3oQqi2G9Z3zRD+Ez1u+ybY9koaWbj5Dq/5AsFhdNCwHhyEPX9gACZMsvA==",
"license": "SEE LICENSE IN license",
"dependencies": {
"@syncfusion/ej2-base": "~29.2.11",
"@syncfusion/ej2-buttons": "~29.2.5",
"@syncfusion/ej2-inputs": "~29.2.5",
"@syncfusion/ej2-lists": "~29.2.4",
"@syncfusion/ej2-popups": "~29.2.8"
}
},
"node_modules/@syncfusion/ej2-pivotview/node_modules/@syncfusion/ej2-compression": {
"version": "29.2.4",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-compression/-/ej2-compression-29.2.4.tgz",
"integrity": "sha512-mcgFmgtITGxoX4zV4LrFGAOHOQD8Z7is85vQE+k2Bf4OI6rZsLNIQFm6KEFg+sZMOSrABzKzg49yrktmlus4TQ==",
"license": "SEE LICENSE IN license",
"dependencies": {
"@syncfusion/ej2-file-utils": "~29.2.4"
}
},
"node_modules/@syncfusion/ej2-pivotview/node_modules/@syncfusion/ej2-data": {
"version": "29.2.4",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-data/-/ej2-data-29.2.4.tgz",
"integrity": "sha512-2R+N65OPq+yfwTXyBz7NdD8MHGoxUbJSEWGT0kNEGMiobiI4AL+hfMYEhXWXE+DqL5Pm0txA4gTXQprFXXbccg==",
"license": "SEE LICENSE IN license",
"dependencies": {
"@syncfusion/ej2-base": "~29.2.4"
}
},
"node_modules/@syncfusion/ej2-pivotview/node_modules/@syncfusion/ej2-dropdowns": {
"version": "29.2.11",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-dropdowns/-/ej2-dropdowns-29.2.11.tgz",
"integrity": "sha512-NtWQexED+1T4Ef/vPkG+C9ANkUD3QPvXelyQaS2+j+mzipOhSBGiDcJYIE1R1wtqC3BhLfMZBPJbuXGc0p+mmQ==",
"license": "SEE LICENSE IN license",
"dependencies": {
"@syncfusion/ej2-base": "~29.2.11",
"@syncfusion/ej2-data": "~29.2.4",
"@syncfusion/ej2-inputs": "~29.2.5",
"@syncfusion/ej2-lists": "~29.2.4",
"@syncfusion/ej2-navigations": "~29.2.8",
"@syncfusion/ej2-notifications": "~29.2.4",
"@syncfusion/ej2-popups": "~29.2.8"
}
},
"node_modules/@syncfusion/ej2-pivotview/node_modules/@syncfusion/ej2-excel-export": {
"version": "29.2.4",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-excel-export/-/ej2-excel-export-29.2.4.tgz",
"integrity": "sha512-N3VAdlhP+Wq/Uyl82ZSx6x8zL3slgtEKzWb13nrfas7cdJicYrbx02YZru5/euaeE3yGTlbKmhjjLLbkVFU/kQ==",
"license": "SEE LICENSE IN license",
"dependencies": {
"@syncfusion/ej2-base": "~29.2.4",
"@syncfusion/ej2-compression": "~29.2.4"
}
},
"node_modules/@syncfusion/ej2-pivotview/node_modules/@syncfusion/ej2-file-utils": {
"version": "29.2.4",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-file-utils/-/ej2-file-utils-29.2.4.tgz",
"integrity": "sha512-nnvZ+1xVULe418habMciZnDAoMOM+TqFaFpO9kf4Cct9RPYUr22Tsvu84CZI7yzzKj/dLfD4n5TRRCHVo+5vVA==",
"license": "SEE LICENSE IN license"
},
"node_modules/@syncfusion/ej2-pivotview/node_modules/@syncfusion/ej2-grids": {
"version": "29.2.11",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-grids/-/ej2-grids-29.2.11.tgz",
"integrity": "sha512-11jI7ve/qxQVsA4kP+v0Y1bEfO7kFCCtBekag80Fy+yio88Wcr5c1Q9SxgsmZtX+Kzk6oSymS497/zNcBDsUJw==",
"license": "SEE LICENSE IN license",
"dependencies": {
"@syncfusion/ej2-base": "~29.2.11",
"@syncfusion/ej2-buttons": "~29.2.5",
"@syncfusion/ej2-calendars": "~29.2.11",
"@syncfusion/ej2-compression": "~29.2.4",
"@syncfusion/ej2-data": "~29.2.4",
"@syncfusion/ej2-dropdowns": "~29.2.11",
"@syncfusion/ej2-excel-export": "~29.2.4",
"@syncfusion/ej2-file-utils": "~29.2.4",
"@syncfusion/ej2-inputs": "~29.2.5",
"@syncfusion/ej2-lists": "~29.2.4",
"@syncfusion/ej2-navigations": "~29.2.8",
"@syncfusion/ej2-notifications": "~29.2.4",
"@syncfusion/ej2-pdf-export": "~29.2.4",
"@syncfusion/ej2-popups": "~29.2.8",
"@syncfusion/ej2-splitbuttons": "~29.2.4"
}
},
"node_modules/@syncfusion/ej2-pivotview/node_modules/@syncfusion/ej2-icons": {
"version": "29.2.4",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-icons/-/ej2-icons-29.2.4.tgz",
"integrity": "sha512-2DYZglxLM++w0S2wB2bcr/Akk1MODdVhfFYLaLqe6mDHDRicFeTEyvnGPx2SHS2EXXlNNFm2GfNWoNNxgs5MhQ==",
"license": "SEE LICENSE IN license"
},
"node_modules/@syncfusion/ej2-pivotview/node_modules/@syncfusion/ej2-inputs": {
"version": "29.2.5",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-inputs/-/ej2-inputs-29.2.5.tgz",
"integrity": "sha512-3ZqMl005qL0tENHcKvBfKUHMYQ/S0FRYjUDQVDPRsg28tE0Lz87oyamrxLYne3zDBEJ7kxWWnaNpgrTVvxsVNw==",
"license": "SEE LICENSE IN license",
"dependencies": {
"@syncfusion/ej2-base": "~29.2.4",
"@syncfusion/ej2-buttons": "~29.2.5",
"@syncfusion/ej2-popups": "~29.2.4",
"@syncfusion/ej2-splitbuttons": "~29.2.4"
}
},
"node_modules/@syncfusion/ej2-pivotview/node_modules/@syncfusion/ej2-lists": {
"version": "29.2.4",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-lists/-/ej2-lists-29.2.4.tgz",
"integrity": "sha512-U64nGdZMjnhHG/+YCTF+WhJ3BoUxFyN2DXxv2qlC+wtayMbfioDRwqSQgxTkH8XE4TgrkRFenRtj5SbYLKliSw==",
"license": "SEE LICENSE IN license",
"dependencies": {
"@syncfusion/ej2-base": "~29.2.4",
"@syncfusion/ej2-buttons": "~29.2.4",
"@syncfusion/ej2-data": "~29.2.4",
"@syncfusion/ej2-popups": "~29.2.4"
}
},
"node_modules/@syncfusion/ej2-pivotview/node_modules/@syncfusion/ej2-navigations": {
"version": "29.2.8",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-navigations/-/ej2-navigations-29.2.8.tgz",
"integrity": "sha512-u14J+dEocqGIKgWCC1eiATv1nh40dgILsGPpGbYS+h9Y7y4YMBRzL2UCcuNHXgPKL7SiJ5lCaVORFco8cxyh6A==",
"license": "SEE LICENSE IN license",
"dependencies": {
"@syncfusion/ej2-base": "~29.2.4",
"@syncfusion/ej2-buttons": "~29.2.5",
"@syncfusion/ej2-data": "~29.2.4",
"@syncfusion/ej2-inputs": "~29.2.5",
"@syncfusion/ej2-lists": "~29.2.4",
"@syncfusion/ej2-popups": "~29.2.8"
}
},
"node_modules/@syncfusion/ej2-pivotview/node_modules/@syncfusion/ej2-notifications": {
"version": "29.2.4",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-notifications/-/ej2-notifications-29.2.4.tgz",
"integrity": "sha512-rB9OuGHWVfnl9aBno+4KRH3aESbakLKYT70V7wjTxwS7u11XbxYwLiA4/YKsTQg/zrgOtcr3Ma1sVOeMYW17gQ==",
"license": "SEE LICENSE IN license",
"dependencies": {
"@syncfusion/ej2-base": "~29.2.4",
"@syncfusion/ej2-buttons": "~29.2.4",
"@syncfusion/ej2-popups": "~29.2.4"
}
},
"node_modules/@syncfusion/ej2-pivotview/node_modules/@syncfusion/ej2-pdf-export": {
"version": "29.2.4",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-pdf-export/-/ej2-pdf-export-29.2.4.tgz",
"integrity": "sha512-Cb5QKYqhuvEb96DzqJT6/nLuan8FkA40mU4Lggg5Ujo1OdIK3Ca5GolM7HStTct4DnU02UKuXTrqoL5umGTfKQ==",
"license": "SEE LICENSE IN license",
"dependencies": {
"@syncfusion/ej2-compression": "~29.2.4"
}
},
"node_modules/@syncfusion/ej2-pivotview/node_modules/@syncfusion/ej2-popups": {
"version": "29.2.8",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-popups/-/ej2-popups-29.2.8.tgz",
"integrity": "sha512-LsXfTiFd6dyoE8XIxFOgC24TeQCqgLdjp3VOg0tWvyuWiZ/skH2sBlF2Y1CWqNJS07OKhtcjLY1h446RTBlalQ==",
"license": "SEE LICENSE IN license",
"dependencies": {
"@syncfusion/ej2-base": "~29.2.4",
"@syncfusion/ej2-buttons": "~29.2.5"
}
},
"node_modules/@syncfusion/ej2-pivotview/node_modules/@syncfusion/ej2-splitbuttons": {
"version": "29.2.4",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-splitbuttons/-/ej2-splitbuttons-29.2.4.tgz",
"integrity": "sha512-sZKs0IZJ6+rDK2OLRl+m5Rs1ai84GE9NV2hpDG5CPFv5uM5WoOf33qYOyp6BSuBerpobpycoXDWH/mh1fZSCDw==",
"license": "SEE LICENSE IN license",
"dependencies": {
"@syncfusion/ej2-base": "~29.2.4",
"@syncfusion/ej2-popups": "~29.2.4"
}
},
"node_modules/@syncfusion/ej2-popups": {
"version": "30.1.37",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-popups/-/ej2-popups-30.1.37.tgz",
"integrity": "sha512-royzgtNf2BE330yBySDvLcnMHfDq1xxFKK4YocQJt5PAdYrm5mxIAmxrbbAO0LElI4GN0yQU8Xy4ne7VjqPzJg==",
"license": "SEE LICENSE IN license",
"dependencies": {
"@syncfusion/ej2-base": "~30.1.37",
"@syncfusion/ej2-buttons": "~30.1.37"
}
},
"node_modules/@syncfusion/ej2-splitbuttons": {
"version": "30.1.39",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-splitbuttons/-/ej2-splitbuttons-30.1.39.tgz",
"integrity": "sha512-MViD8imxEHukX7tvtHWquc76+pL95NB86YPUFb2lx3gxwLUBTIA/KVS2ZmAlQXYaFI7RZQPGopgIbwfb/Zhz5A==",
"license": "SEE LICENSE IN license",
"dependencies": {
"@syncfusion/ej2-base": "~30.1.38",
"@syncfusion/ej2-popups": "~30.1.37"
}
},
"node_modules/@syncfusion/ej2-svg-base": {
"version": "29.2.4",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-svg-base/-/ej2-svg-base-29.2.4.tgz",
"integrity": "sha512-rLEnJTBQ1AxmfiBo4T/sGwG0fOTVPyLVa3NDMx9Bre6yIjEbezHyRu5ujrFubgVDNLQZ1StkEnGlGVduPrr3fA==",
"license": "SEE LICENSE IN license",
"dependencies": {
"@syncfusion/ej2-base": "~29.2.4"
}
},
"node_modules/@syncfusion/ej2-svg-base/node_modules/@syncfusion/ej2-base": {
"version": "29.2.11",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-base/-/ej2-base-29.2.11.tgz",
"integrity": "sha512-f1yM+6606zde7MR4q4d/q6foR3viHDA1/SkkTbLVwBME7I/0AcMG/i0eeTWJfJS8dgn91DGrsrEl0nZ9IA1CEQ==",
"license": "SEE LICENSE IN license",
"dependencies": {
"@syncfusion/ej2-icons": "~29.2.4"
},
"bin": {
"syncfusion-license": "bin/syncfusion-license.js"
}
},
"node_modules/@syncfusion/ej2-svg-base/node_modules/@syncfusion/ej2-icons": {
"version": "29.2.4",
"resolved": "https://registry.npmjs.org/@syncfusion/ej2-icons/-/ej2-icons-29.2.4.tgz",
"integrity": "sha512-2DYZglxLM++w0S2wB2bcr/Akk1MODdVhfFYLaLqe6mDHDRicFeTEyvnGPx2SHS2EXXlNNFm2GfNWoNNxgs5MhQ==",
"license": "SEE LICENSE IN license"
},
"node_modules/@tailwindcss/forms": {
"version": "0.5.10",
"resolved": "https://registry.npmjs.org/@tailwindcss/forms/-/forms-0.5.10.tgz",
......@@ -6473,6 +7578,13 @@
"@types/send": "*"
}
},
"node_modules/@types/file-saver": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/@types/file-saver/-/file-saver-2.0.7.tgz",
"integrity": "sha512-dNKVfHd/jk0SkR/exKGj2ggkB45MAkzvWCaqLUUgkyjITkGNzH8H+yUwr+BLJUBjZOe9w8X3wgmXhZDRg1ED6A==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/geojson": {
"version": "7946.0.16",
"resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz",
......@@ -7223,9 +8335,9 @@
}
},
"node_modules/apexcharts": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-4.4.0.tgz",
"integrity": "sha512-JGsHeQEKDlQh1rob8aBai9/HKvXIpbZ83TnobKZAcdOELf+oQZaxZyAnbbldr6PPBdCgG2zzzLaP1dtEsJxzWw==",
"version": "4.7.0",
"resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-4.7.0.tgz",
"integrity": "sha512-iZSrrBGvVlL+nt2B1NpqfDuBZ9jX61X9I2+XV0hlYXHtTwhwLTHDKGXjNXAgFBDLuvSYCB/rq2nPWVPRv2DrGA==",
"license": "MIT",
"peer": true,
"dependencies": {
......@@ -7859,9 +8971,9 @@
"license": "MIT"
},
"node_modules/chart.js": {
"version": "4.4.7",
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.7.tgz",
"integrity": "sha512-pwkcKfdzTMAU/+jNosKhNL2bHtJc/sSmYgVbuGTEDhzkrhmyihmP7vUc/5ZK9WopidMDHNe3Wm7jOd/WhuHWuw==",
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.5.0.tgz",
"integrity": "sha512-aYeC/jDgSEx8SHWZvANYMioYMZ2KX02W6f6uVfyteuCGcadDLcYVHdfdygsTQkQ4TKn5lghoojAsPj5pu0SnvQ==",
"license": "MIT",
"peer": true,
"dependencies": {
......@@ -9566,6 +10678,12 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/file-saver": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.5.tgz",
"integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==",
"license": "MIT"
},
"node_modules/filepond": {
"version": "4.32.7",
"resolved": "https://registry.npmjs.org/filepond/-/filepond-4.32.7.tgz",
......@@ -9723,8 +10841,7 @@
"version": "4.6.13",
"resolved": "https://registry.npmjs.org/flatpickr/-/flatpickr-4.6.13.tgz",
"integrity": "sha512-97PMG/aywoYpB4IvbvUJi0RQi8vearvU0oov1WW3k0WZPBMrTQVqekSX5CjSG/M4Q3i6A/0FKXC7RyAoAUUSPw==",
"license": "MIT",
"peer": true
"license": "MIT"
},
"node_modules/flatted": {
"version": "3.3.2",
......
......@@ -30,9 +30,17 @@
"@fortawesome/angular-fontawesome": "^0.14.1",
"@kolkov/angular-editor": "^3.0.0-beta.0",
"@ks89/angular-modal-gallery": "^11.1.1",
"@ng-bootstrap/ng-bootstrap": "^15.1.1",
"@ng-select/ng-select": "^12.0.6",
"@ngx-translate/core": "^15.0.0",
"@ngx-translate/http-loader": "^8.0.0",
"@syncfusion/ej2-angular-base": "^29.2.4",
"@syncfusion/ej2-angular-dropdowns": "^29.2.4",
"@syncfusion/ej2-angular-grids": "^29.2.4",
"@syncfusion/ej2-angular-inputs": "^29.2.4",
"@syncfusion/ej2-angular-pivotview": "^29.2.4",
"@syncfusion/ej2-angular-charts": "^29.2.4",
"@syncfusion/ej2-grids": "^30.1.39",
"@tailwindcss/forms": "^0.5.7",
"@types/google.maps": "^3.58.1",
"@types/googlemaps": "^3.43.3",
......@@ -44,9 +52,11 @@
"date-fns": "^3.3.1",
"echarts": "^5.4.3",
"face-api.js": "^0.22.2",
"file-saver": "^2.0.5",
"filepond": "^4.30.6",
"filepond-plugin-get-file": "^1.1.0",
"firebase": "^10.7.1",
"flatpickr": "^4.6.13",
"font-awesome": "^4.7.0",
"i": "^0.3.7",
"jwt-decode": "^4.0.0",
......@@ -91,6 +101,7 @@
"@angular-devkit/build-angular": "^17.0.5",
"@angular/cli": "^17.1.1",
"@angular/compiler-cli": "^17.0.0",
"@types/file-saver": "^2.0.7",
"@types/jasmine": "~5.1.0",
"@types/react": "^18.2.46",
"jasmine-core": "~5.1.0",
......@@ -103,4 +114,4 @@
"tailwindcss": "^3.3.2",
"typescript": "~5.2.2"
}
}
}
\ No newline at end of file
......@@ -7,6 +7,7 @@ import { QuillModule } from 'ngx-quill';
export const admin: Routes = [
{
path: 'admin', children: [{
//////////////MyJob/////////////////
path: 'home',
loadComponent: () =>
import('./home-common/home-common.component').then((m) => m.HomeCommonComponent),
......@@ -81,6 +82,12 @@ export const admin: Routes = [
loadComponent: () =>
import('./company-department/career-cluster/career-cluster.component').then((m) => m.CareerClusterComponent),
},
//////////////MyPortal/////////////////
{
path: 'excel-list',
loadComponent: () =>
import('./myportal/set-excel-reports/excel-list/excel-list.component').then((m) => m.ExcelListComponent),
},
//////////////emp/////////////////
{
path: 'emp/department',
......
<div class="row">
<div class="col-12">
<div class="card card-body">
<h4 class="card-title">รายการเอกสาร</h4>
<div class="row align-items-center mb-2">
<div class="col-4 col-lg-3 text-right">
Search By
</div>
<div class="col-8 col-lg-3">
<select class="custom-select" [(ngModel)]="searchBy">
<option style="color: red" [class.d-none]="searchBy==''" [value]="''">
{{searchBy!=''?'ยกเลิก':''}}</option>
<option [value]="'templateId'">รหัสกลุ่มแม่แบบ</option>
<option [value]="'module'">โมดูล</option>
<option [value]="'tname'">ชื่อกลุ่มรายงาน (ภาษาไทย)</option>
<option [value]="'ename'">ชื่อกลุ่มรายงาน (ภาษาอังกฤษ)</option>
<option [value]="'tdesc'">รายละเอียด (ภาษาไทย)</option>
<option [value]="'edesc'">รายละเอียด (ภาษาอังกฤษ)</option>
</select>
</div>
</div>
<div class="row align-items-center mb-2">
<div class="col-4 col-lg-3 text-right">
Condition
</div>
<div class="col-8 col-lg-3">
<select class="custom-select" [(ngModel)]="condition">
<option style="color: red" [class.d-none]="condition==''" [value]="''">
{{condition!=''?'ยกเลิก':''}}</option>
<option value="includes">คำในประโยค</option>
<option value="lt">น้อยกว่า</option>
<option value="gt">มากกว่า</option>
<option value="eq">เท่ากับ</option>
<option value="lte">น้อยกว่าเท่ากับ</option>
<option value="gte">มากกว่าเท่ากับ</option>
<option value="neq">ไม่เท่ากับ</option>
</select>
</div>
</div>
<div class="row align-items-center mb-2">
<div class="col-4 col-lg-3 text-right">
Key Value
</div>
<div class="col-8 col-lg-3">
<input type="text" class="form-control" [(ngModel)]="searchValue">
</div>
</div>
<div class="row justify-content-center align-items-center mb-2">
<button type="button" class="btn btn-info"
(click)="openTemplate.clear();templateListSearch()">ค้นหา</button>
</div>
<div class="row m-0 justify-content-end align-items-center">
<button type="button" class="btn btn-info" (click)="openAddGroupModal(addGroupModal)">Add Group</button>
</div>
<div class="table-responsive">
<table class="table mb-0 no-wrap v-middle ">
<thead class="bg-info text-white">
<tr>
<th class="text-center" scope="col">ชื่อกลุ่มรายงาน (ภาษาไทย)</th>
<th class="text-center" scope="col">ชื่อกลุ่มรายงาน (ภาษาอังกฤษ)</th>
<th class="text-center" scope="col" style="min-width: 200px">สร้างโดย</th>
<th class="text-center" scope="col" style="min-width:120px">วันที่สร้าง</th>
<th class="text-center" scope="col">สร้างเมื่อ</th>
<th class="text-center" scope="col" style="min-width: 120px;">โมดูล</th>
<th class="text-center" scope="col">Add file</th>
</tr>
</thead>
<tbody *ngIf="!template.filter.length">
<tr style="background-color:#ebf2f6">
<td colspan="7" class="text-center">ไม่พบข้อมูล</td>
</tr>
</tbody>
<tbody *ngIf="template.filter.length">
<ng-container
*ngFor="let items of template.filter | slice: (page-1) * pageSize : (page-1) * pageSize + pageSize ; let i = index">
<tr (mouseenter)="tableHover.set(items.templateId,!tableHover.get(items.templateId))"
(mouseleave)="tableHover.clear()"
[ngStyle]="{'background-color':tableHover.get(items.templateId)?'rgb(201 223 235)':'#ebf2f6'}">
<td colspan="6" class="font-16 font-medium">
<span class="cursor-pointer text-info" style="text-decoration: underline;"
(click)="openAddGroupModal(addGroupModal,items)">
{{items.tname}}
</span>
<span *ngIf="items.tdesc">
-{{items.tdesc}}
</span>
</td>
<td class="text-center">
<ng-container *ngIf="items.templateFile.length">
<ng-container *ngIf="!openTemplate.get(items.templateId)">
<button type="button" title="เปิด" class="btn"
style="border:3px solid #2962ff;border-radius: 10%;" (click)="
openTemplate.set(items.templateId,true)"
(mouseenter)="buttonHover.set(items.templateId+'open',!buttonHover.get(items.templateId))"
(mouseleave)="buttonHover.clear()"
[ngStyle]="{'background-color':buttonHover.get(items.templateId+'open')?'#dfdfdf':'#ffffff'}">
<i class="fa fa-arrow-right text-info"></i>
</button>
</ng-container>
<ng-container *ngIf="openTemplate.get(items.templateId)">
<button type="button" title="ปิด" class="btn"
style="border:3px solid #2962ff;border-radius: 10%;" (click)="
openTemplate.set(items.templateId,false)"
(mouseenter)="buttonHover.set(items.templateId+'close',!buttonHover.get(items.templateId))"
(mouseleave)="buttonHover.clear()"
[ngStyle]="{'background-color':buttonHover.get(items.templateId+'close')?'#dfdfdf':'#ffffff'}">
<i class="fa fa-arrow-down text-info"></i>
</button>
</ng-container>
&nbsp;
</ng-container>
<button type="button" title="เพิ่ม EXCCHILD" class="btn"
style="border:3px solid #2962ff;border-radius: 10%;"
(click)="openAddChildModal(addChildModal,items,'add')"
(mouseenter)="buttonHover.set(items.templateId+'add',!buttonHover.get(items.templateId))"
(mouseleave)="buttonHover.clear()"
[ngStyle]="{'background-color':buttonHover.get(items.templateId+'add')?'#dfdfdf':'#ffffff'}">
<i class="fa fa-floppy-o text-info"></i>
</button>
&nbsp;
<button type="button" title="ลบ" class="btn"
style="border:3px solid #2962ff;border-radius: 10%;"
(mouseenter)="buttonHover.set(items.templateId+'delete',!buttonHover.get(items.templateId))"
(mouseleave)="buttonHover.clear()"
[ngStyle]="{'background-color':buttonHover.get(items.templateId+'delete')?'#dfdfdf':'#ffffff'}"
(click)="deleteTemplate(items)">
<i class="fa fa-trash text-danger"></i>
</button>
</td>
</tr>
<ng-container *ngIf="openTemplate.get(items.templateId)">
<ng-container
*ngFor="let item of items.templateFile | slice: (page-1) * pageSize : (page-1) * pageSize + pageSize ; let i = index">
<tr (mouseenter)="tableHover.set(item.fileName,!tableHover.get(item.fileName))"
(mouseleave)="tableHover.clear()"
[ngStyle]="{'background-color':tableHover.get(item.fileName)?'rgb(201 223 235)':'#ffffff'}">
<ng-container *ngIf="downloadTemplateFileLoading.get(item.fileName)">
<td colspan="7" class="text-center">
<div *ngFor="let item of [1,2,3]" class="spinner-grow text-info mx-1"
role="status">
<span class="sr-only">Loading...</span>
</div>
</td>
</ng-container>
<ng-container *ngIf="!downloadTemplateFileLoading.get(item.fileName)">
<td style="white-space: normal !important;word-break: break-word;">
<i *ngIf="item.menuActive=='0'"
class="fa fa-times text-danger cursor-pointer"
style="text-decoration: underline;"
(click)="menuActiveTemplateFile(item)"></i>
<i *ngIf="item.menuActive=='1'"
class="fa fa-check text-success cursor-pointer"
style="text-decoration: underline;"
(click)="menuActiveTemplateFile(item)"></i>
&nbsp;
<span class="cursor-pointer text-info"
style="text-decoration: underline;"
(click)="openAddChildModal(addChildModal,item,'edit')">
{{item.tdesc}}
</span>
</td>
<td style="white-space: normal !important;word-break: break-word;">
{{item.edesc}}</td>
<td class="text-center"
style="white-space: normal !important;word-break: break-word;">
<!-- {{item.createBy.thFullName}}</td> -->
<td class="text-center"
style="white-space: normal !important;word-break: break-word;">
{{formatISOToLocal(item.createDate).date}}</td>
<td class="text-center"
style="white-space: normal !important;word-break: break-word;">
{{formatISOToLocal(item.createDate).time}}</td>
<td class="text-center"
style="white-space: normal !important;word-break: break-word;">
{{item.module}}</td>
<td class="text-center" class="text-center">
<button type="button" title="Download File" class="btn"
style="border:3px solid #2962ff;border-radius: 10%"
(mouseenter)="buttonHover.set(item.fileName+'copy',!buttonHover.get(item.fileName))"
(mouseleave)="buttonHover.clear()"
[ngStyle]="{'background-color':buttonHover.get(item.fileName+'copy')?'#dfdfdf':'#ffffff'}"
(click)="downloadTemplateFile(item)">
<i class="fa fa-files-o text-info"></i>
</button>
&nbsp;
<button type="button" title="Print" class="btn"
style="border:3px solid #2962ff;border-radius: 10%"
(mouseenter)="buttonHover.set(item.fileName+'print',!buttonHover.get(item.fileName))"
(mouseleave)="buttonHover.clear()"
[ngStyle]="{'background-color':buttonHover.get(item.fileName+'print')?'#dfdfdf':'#ffffff'}"
(click)="openPrintModal(printModal,item);">
<i class="fa fa-print text-info"></i>
</button>
&nbsp;
<button type="button" title="ลบ" class="btn"
style="border:3px solid #2962ff;border-radius: 10%;"
(mouseenter)="buttonHover.set(item.fileName+'delete',!buttonHover.get(item.fileName))"
(mouseleave)="buttonHover.clear()"
[ngStyle]="{'background-color':buttonHover.get(item.fileName+'delete')?'#dfdfdf':'#ffffff'}"
(click)="deleteTemplateFile(item)">
<i class="fa fa-trash text-danger"></i>
</button>
</td>
</ng-container>
</tr>
</ng-container>
</ng-container>
</ng-container>
</tbody>
</table>
</div>
<div class="d-flex justify-content-end py-2">
<select class="custom-select m-r-5" style="width: auto" [(ngModel)]="pageSize" (ngModelChange)="page">
<option [ngValue]="10">รายการต่อหน้า: 10</option>
<option [ngValue]="50">รายการต่อหน้า: 50</option>
<option [ngValue]="100">รายการต่อหน้า: 100</option>
</select>
<ngb-pagination [(page)]="page" [pageSize]="pageSize" [collectionSize]="template.filter.length"
[maxSize]="3" [rotate]="true">
<ng-template ngbPaginationPrevious>ก่อนหน้า</ng-template>
<ng-template ngbPaginationNext>ถัดไป</ng-template>
</ngb-pagination>
</div>
<div class="row justify-content-end align-items-center">
<i class="fa fa-times text-danger"></i>&nbsp;=&nbsp;used on menu&nbsp;&nbsp;&nbsp;
<i class="fa fa-check text-success"></i>&nbsp;=&nbsp;unused on menu&nbsp;&nbsp;&nbsp;
<i class="fa fa-files-o text-info"></i>&nbsp;=&nbsp;copy template&nbsp;&nbsp;&nbsp;
<i class="fa fa-print text-info"></i>&nbsp;=&nbsp;print report
</div>
</div>
</div>
</div>
<ng-template #addGroupModal let-modal>
<div class="modal-header">
<h5 class="modal-title" id="edittemplateLabel">EXCHEAD, Template header excel</h5>
<button type="button" class="close" (click)="modal.dismiss()" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<div class="form-group row">
<label for="nameth" class="col-sm-4 col-form-label">
รหัสกลุ่มแม่แบบ</label>
<div class="col-sm-8">
<input type="text" class="form-control" disabled [value]="bodyTemplate.data.templateId">
</div>
</div>
<div class="form-group row">
<label for="nameth" class="col-sm-4 col-form-label">ชื่อกลุ่มรายงาน (ภาษาไทย)<span
style="color: red">*</span></label>
<div class="col-sm-8">
<input type="text" class="form-control" [(ngModel)]="bodyTemplate.data.tname">
</div>
</div>
<div class="form-group row">
<label for="nameeng" class="col-sm-4 col-form-label">ชื่อกลุ่มรายงาน (ภาษาอังกฤษ)</label>
<div class="col-sm-8">
<input type="text" class="form-control" [(ngModel)]="bodyTemplate.data.ename">
</div>
</div>
<div class="form-group row">
<label for="position" class="col-sm-4 col-form-label">รายละเอียด (ภาษาไทย)</label>
<div class="col-sm-8">
<textarea class="form-control" [(ngModel)]="bodyTemplate.data.tdesc"></textarea>
</div>
</div>
<div class="form-group row">
<label for="position" class="col-sm-4 col-form-label">รายละเอียด (ภาษาอังกฤษ)</label>
<div class="col-sm-8">
<textarea class="form-control" [(ngModel)]="bodyTemplate.data.edesc"></textarea>
</div>
</div>
<div class="form-group row">
<label for="position" class="col-sm-4 col-form-label">โมดูล<span style="color: red">*</span></label>
<div class="col-sm-8">
<select class=" custom-select" [disabled]="bodyTemplate.status=='edit'"
[(ngModel)]="bodyTemplate.data.module">
<option style="color: red" [class.d-none]="bodyTemplate.data.module==''" [value]="''">
{{bodyTemplate.data.module!=''?'ยกเลิก':''}}</option>
<option *ngFor="let item of module.data" [ngValue]="item.code">{{item.tdesc}}</option>
</select>
</div>
</div>
</div>
<div class="modal-footer ">
<button type="submit" class="btn btn-info" [disabled]="!bodyTemplate.data.tname||!bodyTemplate.data.module"
(click)="modal.close()">บันทึก</button>
<button type="button" class="btn btn-danger" (click)="modal.dismiss()">ปิด</button>
</div>
</ng-template>
<ng-template #addChildModal let-modal>
<div class="modal-header">
<h5 class="modal-title" id="edittemplateLabel">EXCCHILD, Template file excel</h5>
<button type="button" class="close" (click)="modal.dismiss()" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<div class="form-group row ml-0">
<div class="custom-control custom-radio col-sm-4 d-flex align-items-center">
<input type="radio" class="custom-control-input cursor-pointer" name="excelFile" id="excelFilePortal"
[checked]="templateFileType=='portal'" (click)="templateFileTypeChange()">
<label class="custom-control-label cursor-pointer"
(click)="templateFileTypeChange()">เลือกเอ็กเซลล์ไฟล์จาก
Portal<span *ngIf="templateFileType=='portal'" style="color: red">*</span></label>
</div>
<div class="col-sm-8 d-flex align-items-center">
<ng-container *ngIf="templateFileType=='portal'">
<input *ngIf="templateFile.portalId" type="text" class="form-control col-10 bg-white"
[value]="templateFile.portalId" readonly>
<button type="button" class="btn btn-info mr-2 col-auto"
(click)="openExcelPortalModal(excel_portalModal)"><i
class="fa fa-search text-white"></i></button>
</ng-container>
</div>
</div>
<div class="form-group row ml-0">
<div class="custom-control custom-radio col-sm-4">
<input type="radio" class="custom-control-input cursor-pointer" name="excelFile" id="excelFileUpload"
[checked]="templateFileType=='upload'" (click)="templateFileTypeChange()">
<label class="custom-control-label cursor-pointer"
(click)="templateFileTypeChange()">อัพโหลดเอ็กเซลล์ไฟล์<span *ngIf="templateFileType=='upload'"
style="color: red">*</span></label>
</div>
<div class="col-sm-8 d-flex align-items-center">
<ng-container *ngIf="templateFileType=='upload'">
<input #fileInput type="file" accept=".xlsx" (change)="onFileSelected($event)"
style="display:none;" />
<button (click)="fileInput.click()">เลือกไฟล์</button>
&nbsp;
<span *ngIf="templateFileName">{{ templateFileName }}</span>
</ng-container>
</div>
</div>
<div class="form-group row">
<label for="nameth" class="col-sm-4 col-form-label">รายละเอียด (ภาษาไทย)<span
style="color: red">*</span></label>
<div class="col-sm-8">
<input type="text" class="form-control" [(ngModel)]="templateFile.tdesc">
</div>
</div>
<div class="form-group row">
<label for="nameeng" class="col-sm-4 col-form-label">รายละเอียด (ภาษาอังกฤษ)</label>
<div class="col-sm-8">
<input type="text" class="form-control" [(ngModel)]="templateFile.edesc">
</div>
</div>
<div class="form-group row">
<label for="position" class="col-sm-4 col-form-label">แบ่งปัน</label>
<div class="row col-sm-8">
<div class="custom-control custom-radio m-2">
<input type="radio" class="custom-control-input cursor-pointer" name="share" id="shareNo"
[checked]="templateFile.isShare=='0'" (click)="templateFile.isShare='0'">
<label class="custom-control-label cursor-pointer" for="shareNo">ไม่ใช่</label>
</div>
<div class="custom-control custom-radio m-2">
<input type="radio" class="custom-control-input cursor-pointer" name="share" id="shareYes"
[checked]="templateFile.isShare=='1'" (click)="templateFile.isShare='1'">
<label class="custom-control-label cursor-pointer" for="shareYes">ใช่</label>
</div>
</div>
</div>
<div class="form-group row">
<label for="position" class="col-sm-4 col-form-label">แสดงรูปแบบ pivot</label>
<div class="row col-sm-8">
<div class="custom-control custom-radio m-2">
<input type="radio" class="custom-control-input cursor-pointer" name="pivot" id="pivotNo"
[checked]="templateFile.isPivot=='0'" (click)="templateFile.isPivot='0'">
<label class="custom-control-label cursor-pointer" for="pivotNo">ไม่ใช่</label>
</div>
<div class="custom-control custom-radio m-2">
<input type="radio" class="custom-control-input cursor-pointer" name="pivot" id="pivotYes"
[checked]="templateFile.isPivot=='1'" (click)="templateFile.isPivot='1'">
<label class="custom-control-label cursor-pointer" for="pivotYes">ใช่</label>
</div>
</div>
</div>
<div class="form-group row">
<label for="position" class="col-sm-4 col-form-label">แสดงรูปแบบ datagrid</label>
<div class="row col-sm-8">
<div class="custom-control custom-radio m-2">
<input type="radio" class="custom-control-input cursor-pointer" name="datagt" id="datagtNo"
[checked]="templateFile.isDataGrid=='0'" (click)="templateFile.isDataGrid='0'">
<label class="custom-control-label cursor-pointer" for="datagtNo">ไม่ใช่</label>
</div>
<div class="custom-control custom-radio m-2">
<input type="radio" class="custom-control-input cursor-pointer" name="datagt" id="datagtYes"
[checked]="templateFile.isDataGrid=='1'" (click)="templateFile.isDataGrid='1'">
<label class="custom-control-label cursor-pointer" for="datagtYes">ใช่</label>
</div>
</div>
</div>
</div>
<div class="modal-footer ">
<button type="submit" class="btn btn-info"
[disabled]="(!templateFile.portalId&&!templateFileName)||!templateFile.tdesc"
(click)="postTemplateFile()">บันทึก</button>
<button type="button" class="btn btn-danger" (click)="modal.dismiss()">ปิด</button>
</div>
</ng-template>
<ng-template #excel_portalModal let-modal>
<div class="modal-header">
<h5 class="modal-title" id="edittemplateLabel">EXCEL_PORTAL</h5>
<button type="button" class="close" (click)="modal.dismiss()" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<div class="row align-items-center mb-2">
<div class="col-12 col-lg-3 mb-2">
<select class="custom-select" [(ngModel)]="excelPortalSearch.groupId">
<option value="" *ngIf="excelPortalSearch.groupId==''" disabled selected hidden>เลือกกลุ่ม
</option>
<option style="color: red" [class.d-none]="excelPortalSearch.groupId==''" [value]="''">
{{excelPortalSearch.groupId!=''?'ยกเลิก':''}}</option>
<option *ngFor="let item of excelPortalGroup.data; let i=index" [ngValue]="item.groupId">
{{item.thName}}
</option>
</select>
</div>
<div class="col-12 col-lg-3 mb-2">
<select class="custom-select" [(ngModel)]="excelPortalSearch.tags">
<option value="" *ngIf="excelPortalSearch.tags==''" disabled selected hidden>เลือกtags
</option>
<option style="color: red" [class.d-none]="excelPortalSearch.tags==''" [value]="''">
{{excelPortalSearch.tags!=''?'ยกเลิก':''}}</option>
<option *ngFor="let item of excelPortalTags.data; let i=index" [ngValue]="item.value">{{item.value}}
</option>
</select>
</div>
<div class="col-12 col-lg-6 d-flex justify-content-end mb-2">
<div class="col-12 col-lg-6 p-0">
<input type="text" class="form-control" placeholder="ค้นหา" [(ngModel)]="excelPortalSearch.search">
</div>
</div>
</div>
<div class="table-responsive">
<table class="table mb-0 no-wrap v-middle ">
<thead class="bg-info text-white">
<tr>
<th class="text-center" scope="col">Ex.</th>
<th class="text-center" scope="col">VDO</th>
<th class="text-center" scope="col" style="min-width: 200px">Name</th>
<th class="text-center" scope="col" style="min-width: 200px">Description</th>
<th class="text-center" scope="col">Database Support</th>
<th class="text-center" scope="col">Pivot</th>
<th class="text-center" scope="col">DataGrid</th>
<th class="text-center" scope="col">Download</th>
<th class="text-center" scope="col">Last Download</th>
<th class="text-center" scope="col">Upload Date</th>
</tr>
</thead>
<tbody *ngIf="!excelPortalFilter().length">
<tr style="background-color:#ebf2f6">
<td colspan="10" class="text-center">ไม่พบข้อมูล</td>
</tr>
</tbody>
<tbody *ngIf="excelPortalFilter().length">
<ng-container
*ngFor="let item of excelPortalFilter() | slice: (pageModal-1) * pageSizeModal : (pageModal-1) * pageSizeModal + pageSizeModal ; let i = index">
<tr (mouseenter)="tableHover.set(item.excelId,!tableHover.get(item.excelId))"
(mouseleave)="tableHover.clear()"
[ngStyle]="{'background-color':tableHover.get(item.excelId)?'rgb(201 223 235)':'#ebf2f6'}">
<td><img *ngIf="item.getImg()" class="border p-1" [src]="item.getImg()">
</td>
<td>
<button *ngIf="item.link1" type="button" class="btn btn-info"
(click)="openVideoModal(videoModal,item.link1)"><i
class="fa fa-search text-white"></i></button>
</td>
<td class="cursor-pointer" (click)="modal.close(item)"
style="white-space: normal !important;word-break: break-word;">
{{item.thName}}
</td>
<td class="cursor-pointer" (click)="modal.close(item)"
style="white-space: normal !important;word-break: break-word;">
{{item.thDesc}}
</td>
<td class="cursor-pointer" (click)="modal.close(item)"
style="white-space: normal !important;word-break: break-word;">
{{item.dbSupport}}
</td>
<td class="cursor-pointer" (click)="modal.close(item)"
style="white-space: normal !important;word-break: break-word;">
{{item.isPivot}}
</td>
<td class="cursor-pointer" (click)="modal.close(item)"
style="white-space: normal !important;word-break: break-word;">
{{item.isDataGrid}}
</td>
<td class="cursor-pointer" (click)="modal.close(item)"
style="white-space: normal !important;word-break: break-word;">
{{item.dwTime}}
</td>
<td class="cursor-pointer" (click)="modal.close(item)"
style="white-space: normal !important;word-break: break-word;">
{{item.downloadDate}} {{item.downloadTime}}
</td>
<td class="cursor-pointer" (click)="modal.close(item)"
style="white-space: normal !important;word-break: break-word;">
{{item.uploadDate}} {{item.uploadTime}}
</td>
</tr>
</ng-container>
</tbody>
</table>
</div>
<div class="d-flex justify-content-end py-2">
<select class="custom-select m-r-5" style="width: auto" [(ngModel)]="pageSizeModal"
(ngModelChange)="pageModal">
<option [ngValue]="10">รายการต่อหน้า: 10</option>
<option [ngValue]="50">รายการต่อหน้า: 50</option>
<option [ngValue]="100">รายการต่อหน้า: 100</option>
</select>
<ngb-pagination [(page)]="pageModal" [pageSize]="pageSizeModal"
[collectionSize]="excelPortalFilter().length" [maxSize]="3" [rotate]="true">
<ng-template ngbPaginationPrevious>ก่อนหน้า</ng-template>
<ng-template ngbPaginationNext>ถัดไป</ng-template>
</ngb-pagination>
</div>
</div>
<div class="modal-footer ">
<button type="button" class="btn btn-danger" (click)="modal.dismiss()">ปิด</button>
</div>
</ng-template>
<ng-template #videoModal let-modal>
<div style="width: 100%; height: 70vh;">
<iframe class="w-100 h-100" [src]="videoLink | safeUrl" frameborder="0" allow="autoplay; encrypted-media"
allowfullscreen>
</iframe>
</div>
</ng-template>
<ng-template #printModal let-modal>
<div class="modal-header">
<h5 class="modal-title" id="edittemplateLabel">Excel Report</h5>
<button type="button" class="close" (click)="modal.dismiss()" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<div class="row">
<div class="d-flex col-12 justify-content-center" *ngIf="loading||loadingExcel">
<div *ngFor="let item of [1,2,3]" class="spinner-grow text-info mx-1" role="status">
<span class="sr-only">Loading...</span>
</div>
</div>
<ng-container *ngIf="!loading&&!loadingExcel">
<ng-container *ngFor="let item of variableSheet; let i=index">
<div class="col-12 row">
<div class="col-3 form-group justify-content-center font-weight-bold control-label col-form-label font-14"
style="text-align: end;">
{{item.label}}
</div>
<div class="col-3 d-flex form-group align-items-center">
<ng-container *ngIf="item.type=='text'">
<input type="text" class="form-control" [(ngModel)]="item.value">
</ng-container>
<ng-container *ngIf="item.type=='list'">
<select class="custom-select" [(ngModel)]="item.value">
<option *ngFor="let list of item.option" [value]="list.value">
{{list.text}}
</option>
</select>
</ng-container>
<ng-container *ngIf="item.type=='radio'">
<div class="row m-0">
<ng-container *ngFor="let radioItem of item.option">
<div class="col-auto custom-control custom-radio">
<input type="radio" class="custom-control-input"
[name]="'group-' + item.label" [id]="radioItem.text+radioItem.value"
[value]="radioItem.value" [(ngModel)]="item.value">
<label class="custom-control-label"
[for]="radioItem.text+radioItem.value">{{radioItem.text}}</label>
</div>
</ng-container>
</div>
</ng-container>
<ng-container *ngIf="item.type=='help'">
<div class="input-group">
<input type="text" readonly class="form-control bg-white cursor-pointer"
[value]="item.value.tdesc" (click)="openModalData(item,modalData)">
<div class="input-group-append">
<button class="btn mr-0 btn-info" type="button"
(click)="openModalData(item,modalData)">
<i class="fa fa-search"></i>
</button>
</div>
</div>
</ng-container>
<ng-container *ngIf="item.type=='calendar'">
<div class="input-group">
<input class="form-control bg-white cursor-pointer"
placeholder="{{ 'dd.mm.yyyy' }}" name="dp1" ngbDatepicker #d1="ngbDatepicker"
[(ngModel)]="select[item.key]" readonly (click)="d1.toggle()"
(ngModelChange)="formatNgbDate(item.key,select[item.key])" #c1="ngModel"
(change)="validateDate(c1)">
<div class="input-group-append">
<button class="btn btn-outline-info no-shadow" (click)="d1.toggle()"
type="button">
<i class="far fa-calendar-alt"></i>
</button>
</div>
</div>
</ng-container>
</div>
<div class="col">
<ng-container *ngIf="item.type=='help'">
<button type="button" class="btn btn-danger btn-sm btn-circle"
(click)="item.value={id:'',tdesc:'',edesc:''}"><i class="fa fa-times"></i>
</button>
</ng-container>
<ng-container *ngIf="item.type=='calendar'">
<button type="button" class="btn btn-danger btn-sm btn-circle"
(click)="select[item.key]=null ;formatNgbDate(item.key)"><i class="fa fa-times"></i>
</button>
</ng-container>
<ng-container *ngIf="item.type=='list'">
<button type="button" class="btn btn-danger btn-sm btn-circle"
(click)="item.value=''"><i class="fa fa-times"></i>
</button>
</ng-container>
</div>
</div>
</ng-container>
<!-- <div *ngIf="variableSheet.length&&!loadingExcel"
class="col-12 justify-content-center align-content-center d-flex" style="margin-bottom: 1rem;">
<button type="submit" class="btn btn-info waves-effect waves-light btn-w-100"
(click)="dowloadExcelReport()">
{{"Print" }}
</button>
</div>
<div *ngIf="loadingExcel" class="col-12 justify-content-center align-content-center d-flex"
style="margin-bottom: 1rem;">
<div *ngFor="let item of [1,2,3]" class="spinner-grow text-info mx-1" role="status">
<span class="sr-only">Loading...</span>
</div>
</div> -->
<div *ngIf="!variableSheet.length" class="col-12 justify-content-center align-content-center d-flex"
style="margin-bottom: 1rem;margin-top: 1rem;">
<div
class="col-3 justify-content-center text-center font-weight-bold control-label col-form-label font-14">
{{'No Data Found' }}
</div>
</div>
<div class="row col-12"
*ngIf="variableSheet.length&&(excelReport.isDataGrid=='1'||excelReport.isPivot=='1')">
<div class="col-12 d-flex justify-content-center align-content-center">
<button type="button" *ngIf="excelReport.isDataGrid=='1'" class="btn btn-info"
(click)="getExcelData(gridModal,'grid')">Datagrid</button>
<ng-container *ngIf="excelReport.isDataGrid=='1'&&excelReport.isPivot=='1'">
&nbsp;
</ng-container>
<button type="button" *ngIf="excelReport.isPivot=='1'" class="btn btn-info"
(click)="getExcelData(pivotModal,'pivot')">Pivot</button>
</div>
</div>
</ng-container>
</div>
</div>
<div class="modal-footer ">
<ng-container *ngIf="variableSheet.length">
<button type="submit" class="btn btn-info waves-effect waves-light btn-w-100" [disabled]="loadingExcel"
(click)="dowloadExcelReport()">
{{"Print" }}
</button>
</ng-container>
<button type="button" class="btn btn-danger" (click)="modal.dismiss()">ปิด</button>
</div>
</ng-template>
<ng-template #modalData let-modal>
<div class="modal-header">
<h5 class="modal-title" id="edittemplateLabel"> {{modalDetail.text.cardHead }}</h5>
<button type="button" class="close" (click)="modal.dismiss()" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<div class="d-flex mb-1">
<input type="text" placeholder="{{'systemcode.search' }} {{modalDetail.text.search[0]}}"
class=" form-control w-75 border-color-gray-full-focus" [(ngModel)]='searchModal'>
</div>
<div class="table-responsive">
<table class="table table-hover table-striped-myhr table-sm mb-0 no-wrap v-middle ">
<thead class="bg-info ">
<tr class="text-white font-weight-normal">
<th class="font-weight-normal text-center" scope="col"
*ngFor="let item of modalDetail.text.tableHead">
{{item}}
</th>
</tr>
</thead>
<tbody *ngIf="!valueDetailFilter().length">
<tr>
<td colspan="9" class="font-weight-normal text-center">
{{"No Data Found" }}
</td>
</tr>
</tbody>
<tbody *ngIf="valueDetailFilter().length">
<tr class="cursor-pointer"
*ngFor="let item of valueDetailFilter() | slice: (pageModal-1) * pageSizeModal : (pageModal-1) * pageSizeModal + pageSizeModal; let i=index"
(click)="selectData(item);modal.dismiss()"
(mouseenter)="tableHover.set(item.id,!tableHover.get(item.id))"
(mouseleave)="tableHover.clear()"
[ngStyle]="{'background-color':tableHover.get(item.id)?'rgb(201 223 235)':'#ffffff'}">
<td class="align-middle text-center">
{{item.id}}
</td>
<td class="align-middle text-center">
{{item.tdesc}}
</td>
<td class="align-middle text-center">
{{item.edesc}}
</td>
</tr>
</tbody>
</table>
</div>
<div class="d-flex justify-content-end p-2">
<select class="custom-select m-r-5 border-color-gray-full-focus" style="width: auto"
[(ngModel)]="pageSizeModal" (ngModelChange)="pageModal=1">
<option *ngFor="let item of [10,50,100]" [ngValue]="item">
{{"Items per page"}}: {{item}}
</option>
</select>
<ngb-pagination [collectionSize]="valueDetailFilter().length" [(page)]="pageModal"
[pageSize]="pageSizeModal" [maxSize]="3" [rotate]="true">
<ng-template ngbPaginationPrevious>{{"Prev"}}</ng-template>
<ng-template ngbPaginationNext>{{"Next"}}</ng-template>
</ngb-pagination>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-danger" (click)="modal.close()">
{{"ปิด"}}
</button>
</div>
</ng-template>
<ng-template #gridModal let-modal>
<div class="modal-header">
<h5 class="modal-title" id="edittemplateLabel">GridData</h5>
<button type="button" class="close" (click)="modal.dismiss()" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<app-datagrid-syncfution [searchSettings]="searchSettings" [dataSource]="dataList" [columns]="columns"
[gridLayout]="setPerspective" [sendLayout]="gridLayout.stimulate" (layout)="gridLayout.data=$event">
</app-datagrid-syncfution>
</div>
<div class="modal-footer">
<button *ngIf="!loadingExcel" type="button" class="btn btn-info"
(click)="gridLayout.stimulate=!gridLayout.stimulate;savePerspective('grid')">
{{"Save Perspective"}}
</button>
<div *ngIf="loadingExcel" class="row" style="width: 120px;">
<div *ngFor="let item of [1,2,3]" class="spinner-grow text-info mx-1" role="status">
<span class="sr-only">Loading...</span>
</div>
</div>
<button type="button" class="btn btn-danger" (click)="modal.close()">
{{"ปิด"}}
</button>
</div>
</ng-template>
<ng-template #pivotModal let-modal>
<div class="modal-header">
<h5 class="modal-title" id="edittemplateLabel">Pivot</h5>
<button type="button" class="close" (click)="modal.dismiss()" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<app-pivot-syncfution [dataSource]="dataList" [columns]="pivotColumns" [templateId]="excelReport.templateId"
[fileName]="excelReport.fileName" [pivotLayout]="setPerspective" [sendLayout]="pivotLayout.stimulate"
(layout)="pivotLayout.data=$event">
</app-pivot-syncfution>
</div>
<div class="modal-footer">
<button *ngIf="!loadingExcel" type="button" class="btn btn-info"
(click)="pivotLayout.stimulate=!pivotLayout.stimulate;savePerspective('pivot')">
{{"Save Perspective"}}
</button>
<div *ngIf="loadingExcel" class="row" style="width: 120px;">
<div *ngFor="let item of [1,2,3]" class="spinner-grow text-info mx-1" role="status">
<span class="sr-only">Loading...</span>
</div>
</div>
<button type="button" class="btn btn-danger" (click)="modal.close()">
{{"ปิด"}}
</button>
</div>
</ng-template>
\ No newline at end of file
import { ChangeDetectorRef, Component, ElementRef, OnInit, TemplateRef, ViewChild } from '@angular/core';
import { ActivatedRoute, RouterModule } from '@angular/router';
import { forkJoin, Subscription } from 'rxjs';
import { NgbDate, NgbDatepickerModule, NgbModal, NgbModalRef, NgbPaginationModule } from '@ng-bootstrap/ng-bootstrap';
import { ColumnModel } from '@syncfusion/ej2-angular-grids';
import * as FileSaver from 'file-saver';
import { MyTemplateFileModel, MyTemplateModel, TemplateFileModel, TemplateModel } from '../../../../models/template.model';
import { ModuleModel, MyModuleModel } from '../../../../models/module.model';
import { ExcelPortalGroupModel, ExcelPortalModel, ExcelPortalTagsModel, MyExcelPortalGroupModel, MyExcelPortalModel, MyExcelPortalTagsModel } from '../../../../models/excel-portal.model';
import { ExcelReportService } from '../../../../services/excel-report.service';
import { CustomCubeService } from '../../../../services/custom-cube.service';
import { AlertModalComponent } from '../../../../../alert-modal/alert-modal.component';
import { ConfirmModalComponent } from '../../../../../confirm-modal/confirm-modal.component';
import { CommonModule } from '@angular/common';
import { TranslateModule } from '@ngx-translate/core';
import { NgSelectModule } from '@ng-select/ng-select';
import { SharedModule } from '../../../../../shared/shared.module';
import { MatPaginator } from '@angular/material/paginator';
import { QuillModule } from 'ngx-quill';
import { MatDialogModule } from '@angular/material/dialog';
import { FileUploadModule } from 'ng2-file-upload';
import { FormsModule, NgModel } from '@angular/forms';
import { DatagridSyncfutionComponent } from '../../../../../datagrid-syncfution/datagrid-syncfution.component';
import { PivotSyncfutionComponent } from "../../../../../pivot-syncfution/pivot-syncfution.component";
import { SafeUrlPipe } from '../../../../../../pipe/safe-url.pipe';
export interface ModalDetail {
text: { cardHead: string, search: string[], tableHead: string[] }
}
interface ValueDetailItem {
id: string;
tdesc: string;
edesc: string;
}
@Component({
standalone: true,
selector: 'app-excel-list',
templateUrl: './excel-list.component.html',
styleUrls: ['./excel-list.component.scss'],
imports: [
CommonModule,
SharedModule,
TranslateModule,
NgSelectModule,
FormsModule,
RouterModule,
FileUploadModule,
QuillModule,
MatDialogModule,
NgbPaginationModule,
DatagridSyncfutionComponent,
PivotSyncfutionComponent,
NgbDatepickerModule,
SafeUrlPipe
],
})
export class ExcelListComponent implements OnInit {
template: { data: TemplateModel[], filter: TemplateModel[], loading: boolean } = { data: [], filter: [], loading: false }
templateFile: TemplateFileModel = new MyTemplateFileModel()
module: { data: ModuleModel[], loading: boolean } = { data: [], loading: false }
page = 1
pageSize = 10
pageModal = 1
pageSizeModal = 10
searchBy = ''
condition = ''
searchValue = ''
openTemplate: Map<string, boolean> = new Map<string, boolean>()
bodyTemplate: {
status: 'add' | 'edit',
data: {
templateId: string,
tname: string,
ename: string,
tdesc: string,
edesc: string,
module: string
}
} = {
status: 'add',
data: {
templateId: '',
tname: '',
ename: '',
tdesc: '',
edesc: '',
module: ''
}
}
excelPortalSearch: {
groupId: string,
tags: string,
search: string
} = {
groupId: '',
tags: '',
search: ''
}
excelPortal: { data: ExcelPortalModel[], loading: boolean } = { data: [], loading: false }
excelPortalGroup: { data: ExcelPortalGroupModel[], loading: boolean } = { data: [], loading: false }
excelPortalTags: { data: ExcelPortalTagsModel[], loading: boolean } = { data: [], loading: false }
videoLink = ""
templateFileType: 'portal' | 'upload' = 'portal'
templateFileName = ''
tableHover: Map<string, boolean> = new Map<string, boolean>()
buttonHover: Map<string, boolean> = new Map<string, boolean>()
addChildModalRef?: NgbModalRef
@ViewChild('fileInput') fileInput!: ElementRef<HTMLInputElement>;
downloadTemplateFileLoading: Map<string, boolean> = new Map<string, boolean>()
changeDate = new Date();
select: any = {}
excelReport?: any
variableSheet: any = []
loading = false
loadingExcel = false
valueDetail: ValueDetailItem[] = [];
modalDetail: ModalDetail = {
text: {
cardHead: '',
search: [],
tableHead: []
}
}
keySelect = ""
searchModal = ""
getTemplateFileSubscription?: Subscription
searchSettings = {
fields: [],
operator: 'contains',
ignoreCase: false
};
columns: ColumnModel[] = []
pivotColumns: any[] = []
dataList: any[] = []
gridLayout: { stimulate: boolean, data: string } = { stimulate: false, data: '' }
pivotLayout: { stimulate: boolean, data: string } = { stimulate: false, data: '' }
setPerspective = ""
constructor(private excelReportService: ExcelReportService,
private modalService: NgbModal,
private customCubeService: CustomCubeService,
private cdr: ChangeDetectorRef) {
}
ngOnInit(): void {
this.getExcelList()
this.getModuleList()
this.getExcelPortalList()
this.getExcelPortalgGroupList()
this.getExcelPortalTagsList()
}
getExcelPortalList() {
this.excelPortal.loading = true
this.excelReportService.getExcelPortalList().subscribe(response => {
this.excelPortal.data = response.map(e => new MyExcelPortalModel(e))
this.excelPortal.loading = false
}, error => {
this.excelPortal.loading = false
})
}
excelPortalFilter() {
const search = this.excelPortalSearch.search?.trim();
let result = this.excelPortal.data.filter(x =>
x.group?.groupId?.includes(this.excelPortalSearch.groupId) ||
x.tags?.some(e => e.value?.includes(this.excelPortalSearch.tags))
);
if (search) {
result = result.filter(x =>
(x.thName).includes(search) ||
(x.thDesc).includes(search) ||
(x.dbSupport).includes(search) ||
String(x.isPivot ?? 0).includes(search) ||
String(x.isDataGrid ?? 0).includes(search) ||
((x.downloadDate + x.downloadTime)).includes(search) ||
((x.uploadDate + x.uploadTime)).includes(search)
);
}
return result;
}
getExcelPortalgGroupList() {
this.excelPortalGroup.loading = true
this.excelReportService.getExcelPortalgGroupList().subscribe(response => {
this.excelPortalGroup.data = response.map(e => new MyExcelPortalGroupModel(e))
this.excelPortalGroup.loading = false
}, error => {
this.excelPortalGroup.loading = false
})
}
getExcelPortalTagsList() {
this.excelPortalTags.loading = true
this.excelReportService.getExcelPortalTagsList().subscribe(response => {
this.excelPortalTags.data = response.map(e => new MyExcelPortalTagsModel(e))
this.excelPortalTags.loading = false
}, error => {
this.excelPortalTags.loading = false
})
}
getExcelList() {
this.template.loading = true
this.excelReportService.getExcelList().subscribe(response => {
this.template.data = response.map(e => new MyTemplateModel(e))
this.template.filter = response.map(e => new MyTemplateModel(e))
this.template.loading = false
}, error => {
this.template.loading = false
})
}
getModuleList() {
this.module.loading = true
this.excelReportService.getModuleList().subscribe(response => {
this.module.data = response.map(e => new MyModuleModel(e))
this.module.loading = false
}, error => {
this.module.loading = false
})
}
postTemplate() {
}
openAlertModal(message?: string) {
const modalRef = this.modalService.open(AlertModalComponent, {
centered: true,
backdrop: 'static'
})
modalRef.componentInstance.message = message ? message : ""
modalRef.result.then(result => {
}, reason => {
})
}
openAddGroupModal(targetModal: TemplateRef<any>, data?: TemplateModel) {
if (data) {
this.bodyTemplate.status = 'edit'
this.bodyTemplate.data = {
templateId: data.templateId,
tname: data.tname,
ename: data.ename,
tdesc: data.tdesc,
edesc: data.edesc,
module: data.module
}
} else {
this.bodyTemplate.status = 'add'
this.bodyTemplate.data = {
templateId: '',
tname: '',
ename: '',
tdesc: '',
edesc: '',
module: ''
}
}
const modalRef = this.modalService.open(targetModal, {
centered: true,
backdrop: 'static',
size: 'lg'
})
modalRef.result.then(result => {
const modalConfirmRef = this.modalService.open(ConfirmModalComponent, {
centered: true,
backdrop: 'static',
})
modalConfirmRef.componentInstance.message = 'คุณต้องการบันทึกข้อมูลหรือไม่'
modalConfirmRef.result.then(result => {
this.excelReportService.postTemplate(this.bodyTemplate.data).subscribe((response: any) => {
this.openAlertModal(response.message)
if (response.success) {
this.getExcelList()
this.getModuleList()
}
}, error => { this.openAlertModal(error.message) })
}, reject => { })
}, reject => { })
}
deleteTemplate(template: TemplateModel) {
const modalConfirmRef = this.modalService.open(ConfirmModalComponent, {
centered: true,
backdrop: 'static',
})
modalConfirmRef.componentInstance.message = 'คุณต้องการลบข้อมูลหรือไม่'
modalConfirmRef.result.then(result => {
this.excelReportService.deleteTemplate(template).subscribe(response => {
if (response.success) {
this.openAlertModal(response.message)
this.getExcelList()
} else {
this.openAlertModal(response.message)
}
}, error => {
this.openAlertModal(error.message)
})
}, reject => { })
}
openAddChildModal(targetModal: TemplateRef<any>, data: TemplateModel | TemplateFileModel, status: 'add' | 'edit') {
if (status == 'add') {
this.templateFile = new MyTemplateFileModel({ templateId: data.templateId, module: data.module })
this.templateFileType = 'portal'
this.templateFileName = ''
} else if (status == 'edit') {
this.templateFile = new MyTemplateFileModel(data)
this.templateFileType = this.templateFile.portalId ? 'portal' : 'upload'
this.templateFileName = this.templateFile.portalId ? '' : this.templateFile.fileName
this.templateFile.realFileNameOld = this.templateFile.fileName
}
this.addChildModalRef = this.modalService.open(targetModal, {
centered: true,
backdrop: 'static',
size: 'lg'
})
}
openExcelPortalModal(targetModal: TemplateRef<any>) {
this.pageModal = 1
this.pageSizeModal = 10
const modalRef = this.modalService.open(targetModal, {
centered: true,
backdrop: 'static',
size: 'xl'
})
modalRef.result.then(result => {
this.templateFile.portalId = result.excelId
}, reject => { })
}
openVideoModal(targetModal: TemplateRef<any>, videoLink: string) {
this.videoLink = this.convertToEmbedUrl(videoLink)
this.modalService.open(targetModal, {
centered: true,
windowClass: 'my-dialog-video-preview'
})
}
convertToEmbedUrl(youtubeUrl: string): string {
try {
const url = new URL(youtubeUrl);
if (url.hostname.includes('youtube.com') && url.searchParams.has('v')) {
const videoId = url.searchParams.get('v');
return `https://www.youtube.com/embed/${videoId}`;
}
if (url.hostname === 'youtu.be') {
const videoId = url.pathname.slice(1); // เอา path หลัง youtu.be/
return `https://www.youtube.com/embed/${videoId}`;
}
} catch (e) {
// ถ้า url ผิดรูปแบบ
console.warn('Invalid YouTube URL:', youtubeUrl);
}
// ถ้าไม่เข้าเงื่อนไขใดเลย ให้คืนลิงก์เดิม
return youtubeUrl;
}
templateListSearch() {
if (!this.searchBy || !this.condition || !this.searchValue) {
this.template.filter = this.template.data.map(e => new MyTemplateModel(e))
}
const conditionMap: { [key: string]: (a: any, b: any) => boolean } = {
includes: (a, b) => (a || '').toString().toLowerCase().includes((b || '').toString().toLowerCase()),
lt: (a, b) => parseFloat(a) < parseFloat(b),
gt: (a, b) => parseFloat(a) > parseFloat(b),
eq: (a, b) => a == b,
lte: (a, b) => parseFloat(a) <= parseFloat(b),
gte: (a, b) => parseFloat(a) >= parseFloat(b),
neq: (a, b) => a != b,
};
const compareFn = conditionMap[this.condition];
if (!compareFn) {
this.template.filter = this.template.data.map(e => new MyTemplateModel(e))
}
this.template.filter = this.template.data.filter(item => {
const value = (item as any)[this.searchBy];
return compareFn(value, this.searchValue);
}).map(e => new MyTemplateModel(e));
}
onFileSelected(event: any) {
const file: File = event.target.files[0];
if (!file) {
alert('กรุณาเลือกไฟล์');
return;
}
const allowedExtensions = ['xls', 'xlsx'];
const fileExtension = file.name.split('.').pop()?.toLowerCase();
if (!allowedExtensions.includes(fileExtension || '')) {
alert('รองรับเฉพาะไฟล์ Excel (.xls, .xlsx) เท่านั้น');
return;
}
const reader = new FileReader();
reader.onload = () => {
const base64String = (reader.result as string).split(',')[1]; // เอาเฉพาะส่วน base64
// ✅ สร้าง payload
const payload = {
filename: file.name,
filetype: file.type,
fileExtension: fileExtension,
data: base64String
};
this.templateFileName = payload.filename
this.templateFile.fileData = payload.data
this.templateFile.fileType = payload.fileExtension || ''
this.fileInput.nativeElement.value = '';
};
reader.readAsDataURL(file); // อ่านแบบ Base64
}
postTemplateFile() {
const modalConfirmRef = this.modalService.open(ConfirmModalComponent, {
centered: true,
backdrop: 'static',
})
modalConfirmRef.componentInstance.message = 'คุณต้องการบันทึกข้อมูลหรือไม่'
modalConfirmRef.result.then(result => {
this.addChildModalRef?.close()
this.templateFile.createDate = new Date().toISOString();
this.excelReportService.postTemplateFile(this.templateFile).subscribe(response => {
if (response.success) {
this.openAlertModal(response.message)
this.getExcelList()
} else {
this.openAlertModal(response.message)
}
}, error => {
this.openAlertModal(error.message)
})
}, reject => { })
}
menuActiveTemplateFile(templateFile: TemplateFileModel) {
const modalConfirmRef = this.modalService.open(ConfirmModalComponent, {
centered: true,
backdrop: 'static',
})
modalConfirmRef.componentInstance.message = 'คุณต้องการ' + (templateFile.menuActive == '1' ? 'ปิด' : 'เปิด') + 'การใช้งานหรือไม่'
modalConfirmRef.result.then(result => {
templateFile.menuActive = templateFile.menuActive == '1' ? '0' : '1'
this.excelReportService.postTemplateFile(templateFile).subscribe(response => {
if (response.success) {
this.openAlertModal(response.message)
this.getExcelList()
} else {
this.openAlertModal(response.message)
}
}, error => {
this.openAlertModal(error.message)
})
}, reject => { })
}
formatISOToLocal(isoString: string) {
const date = new Date(isoString);
const day = String(date.getDate()).padStart(2, '0');
const month = String(date.getMonth() + 1).padStart(2, '0'); // เดือนเริ่มที่ 0
const year = date.getFullYear();
const hours = String(date.getHours()).padStart(2, '0');
const minutes = String(date.getMinutes()).padStart(2, '0');
return { date: `${day}-${month}-${year}`, time: `${hours}:${minutes}` };
}
deleteTemplateFile(templateFile: TemplateFileModel) {
const modalConfirmRef = this.modalService.open(ConfirmModalComponent, {
centered: true,
backdrop: 'static',
})
modalConfirmRef.componentInstance.message = 'คุณต้องการลบข้อมูลหรือไม่'
modalConfirmRef.result.then(result => {
this.excelReportService.deleteTemplateFile(templateFile).subscribe(response => {
if (response.success) {
this.openAlertModal(response.message)
this.getExcelList()
} else {
this.openAlertModal(response.message)
}
}, error => {
this.openAlertModal(error.message)
})
}, reject => { })
}
downloadTemplateFile(templateFile: TemplateFileModel) {
this.downloadTemplateFileLoading.set(templateFile.fileName, true)
this.excelReportService.downloadTemplateFile(templateFile.fileName).subscribe((response: any) => {
const blob = new Blob([response], { type: 'application/octet-stream' });
FileSaver.saveAs(blob, templateFile.fileName);
this.downloadTemplateFileLoading.clear()
}, error => {
this.downloadTemplateFileLoading.clear()
this.openAlertModal(error.message)
})
}
openPrintModal(targetModal: TemplateRef<any>, templateFile: TemplateFileModel) {
this.getTemplateFile(templateFile)
this.modalService.open(targetModal, {
centered: true,
size: 'lg'
})
}
getTemplateFile(templateFile: TemplateFileModel) {
this.loading = true
this.excelReport = undefined
this.variableSheet = []
this.valueDetail = []
this.getTemplateFileSubscription = this.excelReportService.getTemplateFile(templateFile.templateId, templateFile.fileName).subscribe(response => {
this.excelReport = response
this.getExcelColumn()
if (this.excelReport.param) {
Object.entries(this.excelReport.param.variableSheet).forEach(([key, value]) => {
const data = value as any
if (data.type == 'text') {
this.variableSheet.push({
...data, value: data.valueDefault || '', key: key
})
} else if (data.type == 'list') {
this.variableSheet.push({
...data, value: data.valueDefault || '', key: key, option: data.option.split('customize|')[1].split(',').map((x: any) => {
const [value, text] = x.split('#')
return { value: value || '', text: text || '' }
})
})
} else if (data.type == 'radio') {
this.variableSheet.push({
...data, value: data.valueDefault || '', key: key, option: data.option.split('customize|')[1].split(',').map((x: any) => {
const [value, text] = x.split('#')
return { value: value || '', text: text || '' }
})
})
} else if (data.type == 'help') {
this.variableSheet.push({ ...data, value: data.valueDefault ? { id: data.valueDefault, tdesc: data.valueDefault, edesc: data.valueDefault } : { id: "", tdesc: "", edesc: "" }, key: key })
} else if (data.type == 'calendar') {
this.variableSheet.push({ ...data, value: data.valueDefault || '', key: key })
const [d, m, y] = data.valueDefault ? data.valueDefault.split('-').map(Number) : [null, null, null]
this.select[key] = y ? new NgbDate(y, m, d) : null
}
})
}
this.loading = false
this.cdr.detectChanges()
}, (err) => {
this.loading = false
})
}
openModalData(data: any, modal: any) {
this.searchModal = ''
this.page = 1
this.pageSize = 10
this.valueDetail = data.valueDetail.map((x: any) => ({ id: x.id || '', tdesc: x.tdesc || '', edesc: x.edesc || '' }))
this.keySelect = data.key
this.modalDetail = {
text: {
cardHead: "Table " + data.table,
search: ["Table " + data.table],
tableHead: ['ID', 'detailTH', 'detailENG']
}
}
this.modalService.open(modal, { centered: true, size: 'lg' })
}
valueDetailFilter(): ValueDetailItem[] {
const search = this.searchModal.toLowerCase();
return this.valueDetail.filter(item =>
item.id.toLowerCase().includes(search) ||
item.tdesc.toLowerCase().includes(search) ||
item.edesc.toLowerCase().includes(search)
);
}
selectData(data: any) {
const item = this.variableSheet.find((i: any) => i.key === this.keySelect);
if (item) item.value = data;
}
formatNgbDate(key: string, date?: NgbDate) {
if (date) {
const day = String(date.day).padStart(2, '0');
const month = String(date.month).padStart(2, '0');
const year = date.year;
const item = this.variableSheet.find((i: any) => i.key === key);
if (item) item.value = `${day}-${month}-${year}`
} else {
const item = this.variableSheet.find((i: any) => i.key === key);
if (item) item.value = ''
}
}
validateDate(control: NgModel) {
if (!control.value) {
// ว่าง หรือวันที่ไม่ถูกต้อง
this.openAlertModal('กรุณาเลือกวันที่ให้ถูกต้อง');
}
}
dowloadExcelReport() {
this.loadingExcel = true
const fileName = this.excelReport.param.excelFile
const param = this.excelReport.param.variableName
const data = this.variableSheet.map((item: any) => {
if (item.type == 'help') {
return "__" + item.key + "=" + item.value.id
} else if (item.type == 'calendar' || item.type == 'list' || item.type == 'radio' || item.type == 'text') {
return "__" + item.key + "=" + item.value
}
return ""
}).join('|')
const body = {
fileName: fileName,
paramObj: data
}
this.excelReportService.printExcelReport(body).subscribe((res: any) => {
const blob = new Blob([res], { type: 'application/octet-stream' });
FileSaver.saveAs(blob, fileName);
this.loadingExcel = false
this.cdr.detectChanges()
}, (err) => {
this.loadingExcel = false
this.openAlertModal(err.message)
this.cdr.detectChanges()
})
}
openGridModal(targetModal: TemplateRef<any>, setPerspective: string) {
this.modalService.open(targetModal, {
centered: true,
backdrop: 'static',
windowClass: 'my-dialog-big-screen'
})
setTimeout(() => {
this.setPerspective = setPerspective
}, 10)
}
openPivotModal(targetModal: TemplateRef<any>, setPerspective: string) {
this.modalService.open(targetModal, {
centered: true,
backdrop: 'static',
windowClass: 'my-dialog-big-screen'
})
setTimeout(() => {
this.setPerspective = setPerspective
}, 10)
}
ngOnDestroy(): void {
this.getTemplateFileSubscription?.unsubscribe()
}
templateFileTypeChange() {
if (this.templateFileName || this.templateFile.portalId) {
const modalConfirmRef = this.modalService.open(ConfirmModalComponent, {
centered: true,
backdrop: 'static',
})
modalConfirmRef.componentInstance.message = 'คุณต้องการยกเลิกไฟล์ที่เลือกหรือไม่'
modalConfirmRef.result.then(result => {
this.onToggleTemplateFile()
}, reject => { })
} else {
this.onToggleTemplateFile()
}
}
onToggleTemplateFile() {
if (this.templateFileType == 'portal') {
this.templateFileType = 'upload'
this.templateFile.portalId = ''
} else if (this.templateFileType == 'upload') {
this.templateFileType = 'portal';
this.templateFile.fileData = '';
this.templateFile.fileType = '';
this.templateFileName = ''
}
}
getExcelColumn() {
const body = {
templateId: this.excelReport.templateId,
fileName: this.excelReport.fileName
}
this.searchSettings = {
fields: [],
operator: 'contains',
ignoreCase: false
};
this.columns = []
this.pivotColumns = []
this.customCubeService.getExcelColumn(body).subscribe((response: any) => {
this.searchSettings = {
fields: response.map((e: any) => e.split(':')[0]),
operator: 'contains',
ignoreCase: false
};
response.forEach((e: any, i: number) => {
const [field, headerText] = e.split(':')
this.columns.push({
field: field,
headerText: headerText,
type: "string",
isPrimaryKey: i == 0,
})
this.pivotColumns.push({
name: field,
caption: headerText
})
});
}, error => {
})
}
getExcelData(targetModal: TemplateRef<any>, type: 'grid' | 'pivot') {
this.loadingExcel = true
this.dataList = []
this.setPerspective = ''
const body: any = {
templateId: this.excelReport.templateId,
fileName: this.excelReport.fileName
};
this.variableSheet.forEach((item: any, index: number) => {
const i = index + 1;
body[`key${i}`] = item.key;
if (item.type == 'help') {
body[`val${i}`] = item.value.id
} else if (item.type == 'calendar' || item.type == 'list' || item.type == 'radio' || item.type == 'text') {
body[`val${i}`] = item.value
}
});
forkJoin({
excelData: this.customCubeService.getExcelData(body),
excelPerspective: this.customCubeService.getExcelPerspective(body),
}).subscribe(response => {
this.dataList = response.excelData as any
this.loadingExcel = false
if (type == 'grid') {
this.openGridModal(targetModal, (response.excelPerspective as any).dataGridStr)
} else if (type == 'pivot') {
this.openPivotModal(targetModal, (response.excelPerspective as any).pivotStr)
}
this.cdr.detectChanges()
}, error => {
this.loadingExcel = false
})
}
savePerspective(type: 'grid' | 'pivot') {
if (type == 'grid') {
this.loadingExcel = true
setTimeout(() => {
const body = {
templateId: this.excelReport.templateId,
fileName: this.excelReport.fileName,
dataGridStr: this.gridLayout.data,
pivotStr: "",
}
this.customCubeService.saveExcelPerspective(body).subscribe(response => {
this.loadingExcel = false
this.openAlertModal(response.message)
}, error => {
this.loadingExcel = false
this.openAlertModal(error.message)
})
this.cdr.detectChanges();
}, 500);
} else if (type == 'pivot') {
this.loadingExcel = true
setTimeout(() => {
const body = {
templateId: this.excelReport.templateId,
fileName: this.excelReport.fileName,
dataGridStr: '',
pivotStr: this.pivotLayout.data,
}
this.customCubeService.saveExcelPerspective(body).subscribe(response => {
this.loadingExcel = false
this.openAlertModal(response.message)
}, error => {
this.loadingExcel = false
this.openAlertModal(error.message)
})
this.cdr.detectChanges();
}, 500);
}
}
}
\ No newline at end of file
/* tslint:disable:no-unused-variable */
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { DebugElement } from '@angular/core';
import { SetExcelReportsComponent } from './set-excel-reports.component';
describe('SetExcelReportsComponent', () => {
let component: SetExcelReportsComponent;
let fixture: ComponentFixture<SetExcelReportsComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ SetExcelReportsComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(SetExcelReportsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-set-excel-reports',
templateUrl: './set-excel-reports.component.html',
styleUrls: ['./set-excel-reports.component.css']
})
export class SetExcelReportsComponent implements OnInit {
constructor() { }
ngOnInit() {
}
}
export interface AlertModel {
actionStatus: string
fail: boolean
memberId: any
message: string
none: boolean
resultObject: any
state: string
statusCode: number
success: boolean
}
\ No newline at end of file
import { environment } from "../../../environments/environment"
export interface ExcelPortalModel {
getImg: any
approveBy: string
approveDate: string
approveTime: string
dbSupport: string
downloadDate: string
downloadTime: string
dwTime: number
engDesc: string
engName: string
excelId: string
fileData: string
fileType: string
group: ExcelPortalGroupModel
isDataGrid: number
isPivot: number
link1: string
referId: string
remark: string
tags: ExcelPortalTagsModel[]
thDesc: string
thName: string
thumbnail: string
uploadBy: string
uploadDate: string
uploadTime: string
}
export class MyExcelPortalModel implements ExcelPortalModel {
approveBy: string
approveDate: string
approveTime: string
dbSupport: string
downloadDate: string
downloadTime: string
dwTime: number
engDesc: string
engName: string
excelId: string
fileData: string
fileType: string
group: ExcelPortalGroupModel
isDataGrid: number
isPivot: number
link1: string
referId: string
remark: string
tags: ExcelPortalTagsModel[]
thDesc: string
thName: string
thumbnail: string
uploadBy: string
uploadDate: string
uploadTime: string
constructor(data?: Partial<ExcelPortalModel>) {
this.approveBy = data?.approveBy || ''
this.approveDate = data?.approveDate || ''
this.approveTime = data?.approveTime || ''
this.dbSupport = data?.dbSupport || ''
this.downloadDate = data?.downloadDate || ''
this.downloadTime = data?.downloadTime || ''
this.dwTime = data?.dwTime ?? 0
this.engDesc = data?.engDesc || ''
this.engName = data?.engName || ''
this.excelId = data?.excelId || ''
this.fileData = data?.fileData || ''
this.fileType = data?.fileType || ''
this.group = new MyExcelPortalGroupModel(data?.group || {})
this.isDataGrid = data?.isDataGrid ?? 0
this.isPivot = data?.isPivot ?? 0
this.link1 = data?.link1 || ''
this.referId = data?.referId || ''
this.remark = data?.remark || ''
this.tags = data?.tags?.map(e => new MyExcelPortalTagsModel(e)) || []
this.thDesc = data?.thDesc || ''
this.thName = data?.thName || ''
this.thumbnail = data?.thumbnail || ''
this.uploadBy = data?.uploadBy || ''
this.uploadDate = data?.uploadDate || ''
this.uploadTime = data?.uploadTime || ''
}
getImg() {
return this.thumbnail ? environment.baseUrl + "files/image/" + this.thumbnail : ''
}
}
export interface ExcelPortalGroupModel {
engName: string
groupId: string
groupType: number
thName: string
}
export class MyExcelPortalGroupModel implements ExcelPortalGroupModel {
engName: string
groupId: string
groupType: number
thName: string
constructor(data?: Partial<ExcelPortalGroupModel>) {
this.engName = data?.engName || ''
this.groupId = data?.groupId || ''
this.groupType = data?.groupType ?? 0
this.thName = data?.thName || ''
}
}
export interface ExcelPortalTagsModel {
display: string
value: string
}
export class MyExcelPortalTagsModel implements ExcelPortalTagsModel {
display: string
value: string
constructor(data?: Partial<ExcelPortalTagsModel>) {
this.display = data?.display || ''
this.value = data?.value || ''
}
}
\ No newline at end of file
export interface ModuleModel {
code: string
tdesc: string
edesc: string
}
export class MyModuleModel implements ModuleModel {
code: string
tdesc: string
edesc: string
constructor(data: Partial<ModuleModel>) {
this.code = data?.code || ''
this.tdesc = data?.tdesc || ''
this.edesc = data?.edesc || ''
}
}
\ No newline at end of file
export interface TemplateFileMiniModel {
edesc: string
fileName: string
itemId: string
module: string
tdesc: string
templateId: string
}
export class MyTemplateFileMiniModel implements TemplateFileMiniModel {
edesc: string
fileName: string
itemId: string
module: string
tdesc: string
templateId: string
constructor(data?: Partial<TemplateFileMiniModel>) {
this.edesc = data?.edesc || ''
this.fileName = data?.fileName || ''
this.itemId = data?.itemId || ''
this.module = data?.module || ''
this.tdesc = data?.tdesc || ''
this.templateId = data?.templateId || ''
}
}
export interface PrefixModel {
edesc: string
prefixId: string
tdesc: string
}
export class MyPrefixModel implements PrefixModel {
edesc: string
prefixId: string
tdesc: string
constructor(data?: Partial<PrefixModel>) {
this.edesc = data?.edesc || ''
this.prefixId = data?.prefixId || ''
this.tdesc = data?.tdesc || ''
}
}
export interface CreateByModel {
efname: string
elname: string
email: string
employeeId: string
engFullName: string
fname: string
lname: string
picture: string
prefix: PrefixModel
sex: string
thFullName: string
}
export class MyCreateByModel implements CreateByModel {
efname: string
elname: string
email: string
employeeId: string
engFullName: string
fname: string
lname: string
picture: string
prefix: PrefixModel
sex: string
thFullName: string
constructor(data?: Partial<CreateByModel>) {
this.efname = data?.efname || ''
this.elname = data?.elname || ''
this.email = data?.email || ''
this.employeeId = data?.employeeId || ''
this.engFullName = data?.engFullName || ''
this.fname = data?.fname || ''
this.lname = data?.lname || ''
this.picture = data?.picture || ''
this.prefix = new MyPrefixModel(data?.prefix)
this.sex = data?.sex || ''
this.thFullName = data?.thFullName || ''
}
}
export interface TemplateFileModel {
createDate: string
edesc: string
fileName: string
isDataGrid: string
isPivot: string
isShare: string
itemId: string
menuActive: string
module: string
page: string
param: any | null
portalId: string
realFileName: string
realFileNameOld: string
tdesc: string
templateId: string
uponMenu: string
fileData: string
fileType: string
}
export class MyTemplateFileModel implements TemplateFileModel {
createDate: string
edesc: string
fileName: string
isDataGrid: string
isPivot: string
isShare: string
itemId: string
menuActive: string
module: string
page: string
param: any | null
portalId: string
realFileName: string
realFileNameOld: string
tdesc: string
templateId: string
uponMenu: string
fileData: string
fileType: string
constructor(data?: Partial<TemplateFileModel>) {
this.createDate = data?.createDate || ''
this.edesc = data?.edesc || ''
this.fileName = data?.fileName || ''
this.isDataGrid = data?.isDataGrid || '0'
this.isPivot = data?.isPivot || '0'
this.isShare = data?.isShare || '0'
this.itemId = data?.itemId || ''
this.menuActive = data?.menuActive || ''
this.module = data?.module || ''
this.page = data?.page || ''
this.param = data?.param
this.portalId = data?.portalId || ''
this.realFileName = data?.realFileName || ''
this.realFileNameOld = data?.realFileNameOld || ''
this.tdesc = data?.tdesc || ''
this.templateId = data?.templateId || ''
this.uponMenu = data?.uponMenu || ''
this.fileData = data?.fileData || ''
this.fileType = data?.fileType || ''
}
}
export interface TemplateModel {
templateId: string
tname: string
ename: string
tdesc: string
edesc: string
module: string
templateFile: TemplateFileModel[]
}
export class MyTemplateModel implements TemplateModel {
templateId: string
tname: string
ename: string
tdesc: string
edesc: string
module: string
templateFile: TemplateFileModel[]
constructor(data?: Partial<TemplateModel>) {
this.templateId = data?.templateId || ''
this.tname = data?.tname || ''
this.ename = data?.ename || ''
this.tdesc = data?.tdesc || ''
this.edesc = data?.edesc || ''
this.module = data?.module || ''
this.templateFile = data?.templateFile?.map(x => new MyTemplateFileModel(x)) || []
}
}
\ No newline at end of file
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { QuillModule } from 'ngx-quill';
import { MyportalComponent } from './myportal.component';
import { CommonModule } from '@angular/common';
export const admin: Routes = [
{
path: 'admin', children: [{
path: 'home',
loadComponent: () =>
import('./home-common/home-common.component').then((m) => m.HomeCommonComponent),
},
]
}
];
@NgModule({
imports: [
CommonModule,
RouterModule.forChild(admin),
QuillModule.forRoot(),
],
exports: [RouterModule],
declarations: [MyportalComponent]
})
export class MyPortalModule {
static routes = admin;
}
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { TranslateService } from '@ngx-translate/core';
import { Observable } from 'rxjs';
import { environment } from '../../../environments/environment';
import { AlertModel } from '../models/alert.model';
@Injectable({
providedIn: 'root'
})
export class CustomCubeService {
constructor(private http: HttpClient,
private translateService: TranslateService
) { }
getExcelColumn(body: {
templateId: string,
fileName: string,
}) {
return this.http.post(environment.baseUrl + "customcube/get-excel-column?companyid=eb2f4f30-edaf-11ee-a69a-c7680edc0e47", body)
}
getExcelData(body: any) {
return this.http.post(environment.baseUrl + "customcube/get-excel-data?companyid=eb2f4f30-edaf-11ee-a69a-c7680edc0e47", body)
}
saveExcelPerspective(body: {
templateId: string,
fileName: string,
dataGridStr: string,
pivotStr: string,
}): Observable<AlertModel> {
return this.http.post<AlertModel>(environment.baseUrl + "customcube/save-excel-perspective?companyid=eb2f4f30-edaf-11ee-a69a-c7680edc0e47", body)
}
getExcelPerspective(body: {
templateId: string,
fileName: string,
}) {
return this.http.post(environment.baseUrl + "customcube/get-excel-perspective?companyid=eb2f4f30-edaf-11ee-a69a-c7680edc0e47", body)
}
}
\ No newline at end of file
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { TranslateService } from '@ngx-translate/core';
import { Observable } from 'rxjs';
import { TemplateFileModel, TemplateModel } from '../models/template.model';
import { environment } from '../../../environments/environment';
import { ModuleModel } from '../models/module.model';
import { ExcelPortalGroupModel, ExcelPortalModel, ExcelPortalTagsModel } from '../models/excel-portal.model';
import { AlertModel } from '../models/alert.model';
import { TemplateFileMiniModel } from '../models/template-file-mini.model';
@Injectable({
providedIn: 'root'
})
export class ExcelReportService {
constructor(private http: HttpClient,
private translateService: TranslateService
) { }
getExcelList(): Observable<TemplateModel[]> {
return this.http.get<TemplateModel[]>(environment.baseUrl + "/template/lists?companyid=eb2f4f30-edaf-11ee-a69a-c7680edc0e47")
}
getModuleList(): Observable<ModuleModel[]> {
return this.http.get<ModuleModel[]>(environment.baseUrl + "/template/lists/module?companyid=eb2f4f30-edaf-11ee-a69a-c7680edc0e47")
}
getExcelPortalList(): Observable<ExcelPortalModel[]> {
return this.http.get<ExcelPortalModel[]>(environment.baseUrl + "/excel-center/content/lists")
}
getExcelPortalgGroupList(): Observable<ExcelPortalGroupModel[]> {
return this.http.get<ExcelPortalGroupModel[]>(environment.baseUrl + "/portal-group/lists")
}
getExcelPortalTagsList(): Observable<ExcelPortalTagsModel[]> {
return this.http.get<ExcelPortalTagsModel[]>(environment.baseUrl + "/tag/lists")
}
postTemplate(body: {
templateId: string,
tname: string,
ename: string,
tdesc: string,
edesc: string,
module: string
}) {
return this.http.post(environment.baseUrl + "/template?companyid=eb2f4f30-edaf-11ee-a69a-c7680edc0e47", body)
}
deleteTemplate(body: TemplateModel): Observable<AlertModel> {
let option = {
headers: new HttpHeaders({
"Content-Type": "application/json",
}),
body: body
}
return this.http.delete<AlertModel>(environment.baseUrl + "/template?companyid=eb2f4f30-edaf-11ee-a69a-c7680edc0e47", option)
}
postTemplateFile(body: TemplateFileModel): Observable<AlertModel> {
return this.http.post<AlertModel>(environment.baseUrl + "/template-file?companyid=eb2f4f30-edaf-11ee-a69a-c7680edc0e47", body)
}
deleteTemplateFile(body: TemplateFileModel): Observable<AlertModel> {
let option = {
headers: new HttpHeaders({
"Content-Type": "application/json",
}),
body: body
}
return this.http.delete<AlertModel>(environment.baseUrl + "/template-file?companyid=eb2f4f30-edaf-11ee-a69a-c7680edc0e47", option)
}
downloadTemplateFile(fileName: string) {
return this.http.get(environment.baseUrl + "/template-file/download/excel/" + fileName + "?companyid=eb2f4f30-edaf-11ee-a69a-c7680edc0e47", { responseType: 'blob' })
}
printExcelReport(body: {
fileName: string,
paramObj: string
}) {
return this.http.post(environment.baseUrl + "/template-file/export-to-excel?companyid=eb2f4f30-edaf-11ee-a69a-c7680edc0e47", body, { responseType: 'blob' })
}
getTemplateFile(templateid: string, filename: string): Observable<AlertModel> {
return this.http.get<AlertModel>(environment.baseUrl + "/template-file/" + templateid + "/" + filename + "?companyid=eb2f4f30-edaf-11ee-a69a-c7680edc0e47",)
}
getTemplateFileLists(): Observable<TemplateFileMiniModel[]> {
return this.http.get<TemplateFileMiniModel[]>(environment.baseUrl + "/template-file/menuitem/mini/lists?companyid=eb2f4f30-edaf-11ee-a69a-c7680edc0e47",)
}
}
\ No newline at end of file
<div class="modal-header">
<h5 class="modal-title" id="editLabel">ข้อความแจ้งเตือน</h5>
<button type="button" class="close" (click)="activeModal.dismiss('dismiss')" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<ng-container *ngIf="innerHTML!=undefined then InnerHTML else Message"></ng-container>
<ng-template #Message>
<div class="modal-body">
{{message}}
</div>
</ng-template>
<ng-template #InnerHTML>
<div class="modal-body" [innerHTML]="innerHTML">
</div>
</ng-template>
\ No newline at end of file
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { AlertModalComponent } from './alert-modal.component';
describe('AlertModalComponent', () => {
let component: AlertModalComponent;
let fixture: ComponentFixture<AlertModalComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ AlertModalComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(AlertModalComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { Component, Input, OnInit } from '@angular/core';
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
@Component({
selector: 'app-alert-modal',
templateUrl: './alert-modal.component.html',
styleUrls: ['./alert-modal.component.scss']
})
export class AlertModalComponent implements OnInit {
@Input() message: string = ""
@Input() innerHTML?: string
constructor(public activeModal: NgbActiveModal) { }
ngOnInit(): void {
}
}
<div class="modal-header">
<h5 class="modal-title" id="editLabel">ข้อความแจ้งเตือน</h5>
<button type="button" class="close" (click)="activeModal.dismiss('dismiss')" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
{{message}}
</div>
<div class="modal-footer">
<button type="button" class="btn btn-info" (click)="activeModal.close('close')">
ยืนยัน
</button>
<button type="button" class="btn btn-danger" (click)="activeModal.dismiss('dismiss')">
ยกเลิก
</button>
</div>
\ No newline at end of file
import { Component, Input, OnInit } from '@angular/core';
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
@Component({
selector: 'app-confirm-modal',
templateUrl: './confirm-modal.component.html',
styleUrls: ['./confirm-modal.component.scss']
})
export class ConfirmModalComponent implements OnInit {
@Input() message: string = "";
constructor(public activeModal: NgbActiveModal) { }
ngOnInit(): void {
}
}
<ejs-grid #grid id='Grid' [dataSource]="dataSourceSearch" [allowFiltering]="allowFiltering"
[filterSettings]="filterSettings" (actionComplete)="actionComplete($event)" [searchSettings]="searchSettings"
[groupSettings]="groupSettings" [toolbar]='toolbarOptions' [editSettings]="editSettings" allowPaging='true'
allowGrouping='true' allowSorting='true' [showColumnMenu]="true" (columnMenuClick)="onColumnMenuClick($event)"
allowFiltering='true' [allowPdfExport]='true' (toolbarClick)='toolbarClick($event)' [allowExcelExport]='true'
[selectionSettings]="selectionOptions" (detailDataBound)='detailDataBound($event)' width="auto" allowReordering='true'
[loadingIndicator]='loadingIndicator' [query]="query" rowHeight='60' allowEditing='false' [pageSettings]='initialPage'
[allowMultiSorting]='true' [columnMenuItems]="columnMenuItems">
<e-columns>
<e-column *ngFor="let col of columns" [field]="col.field" [headerText]="col.headerText" [width]="col.width"
[format]="col.format" [textAlign]="'center'" [isPrimaryKey]="col.isPrimaryKey" [editType]="false"
[validationRules]="col.validationRules" [allowEditing]="false" [allowSorting]="'true'" [allowFiltering]="true"
[visible]="col.visible" [type]="col.type">
<ng-template #headerTemplate let-data>
<span class="font-size-12px font-weight-700">{{ col.headerText }}</span>
</ng-template>
</e-column>
</e-columns>
<e-aggregates>
<e-aggregate>
<e-columns>
<e-column *ngFor="let col of aggregatesSum" [field]="col.field" [type]="'Sum'" [footerTemplate]="'Sum: ${Sum}'"
[groupFooterTemplate]="'Sum: ${Sum}'" [groupCaptionTemplate]="col.groupCaptionTemplate" [format]="col.format">
</e-column>
</e-columns>
</e-aggregate>
<e-aggregate>
<e-columns>
<e-column *ngFor="let col of aggregatesCount" [field]="col.field" [type]="'Count'"
[footerTemplate]="'Count: ${Count}'" [groupFooterTemplate]="'Count: ${Count}'"
[groupCaptionTemplate]="col.groupCaptionTemplate" [format]="col.format">
</e-column>
</e-columns>
</e-aggregate>
<e-aggregate>
<e-columns>
<e-column *ngFor="let col of aggregatesAvg" [field]="col.field" [type]="'Average'"
[footerTemplate]="'Average: ${Average}'" [groupFooterTemplate]="'Average: ${Average}'"
[groupCaptionTemplate]="col.groupCaptionTemplate" [format]="col.format">
</e-column>
</e-columns>
</e-aggregate>
<e-aggregate>
<e-columns>
<e-column *ngFor="let col of aggregatesMin" [field]="col.field" [type]="'Min'" [footerTemplate]="'Min: ${Min}'"
[groupFooterTemplate]="'Min: ${Min}'" [groupCaptionTemplate]="col.groupCaptionTemplate" [format]="col.format">
</e-column>
</e-columns>
</e-aggregate>
<e-aggregate>
<e-columns>
<e-column *ngFor="let col of aggregatesMax" [field]="col.field" [type]="'Max'" [footerTemplate]="'Max: ${Max}'"
[groupFooterTemplate]="'Max: ${Max}'" [groupCaptionTemplate]="col.groupCaptionTemplate" [format]="col.format">
</e-column>
</e-columns>
</e-aggregate>
</e-aggregates>
</ejs-grid>
\ No newline at end of file
// th{
// position: relative; // เทียบเท่า class "relative"
// padding: 10px; // เทียบเท่า class "px-10px py-10px" (อาจเปลี่ยนตามต้องการ)
// background-color: rgb(96 165 250 / 0.1); // ตัวอย่างแทน "bg-soft-secondary"
// color: #2b2b2b; // ตัวอย่างแทน "text-primary"
// text-align: center !important; // เทียบเท่า "!text-center"
// // หากต้องการดีไซน์อื่น ๆ เพิ่มเติมก็ใส่ในนี้ได้เลย เช่น:
// font-weight: 600;
// border-bottom: 1px solid #eee;
// }
.e-headercell,
.e-detailheadercell {
background-color: rgb(96 165 250 / 0.1) !important;
}
.e-pager .e-currentitem, .e-pager .e-currentitem:hover {
background: rgb(96 165 250) !important;
color: #fff;
opacity: 1 !important;
}
.e-checkbox-wrapper .e-frame.e-check, .e-css.e-checkbox-wrapper .e-frame.e-check {
background-color: rgb(96 165 250) !important;
border-color: transparent;
color: #fff;
}
.e-checkbox-wrapper .e-frame, .e-css.e-checkbox-wrapper .e-frame {
border: 1px solid !important;
border-radius: 2px;
box-sizing: border-box;
cursor: pointer;
display: inline-block;
font-family: "e-icons";
height: 18px;
line-height: 10px;
padding: 2px 0;
text-align: center;
vertical-align: middle;
width: 1rem !important;
border-color: #64748b !important;
}
.e-grid td.e-selectionbackground {
background-color: #aec2ec !important;
}
import {
Component,
Input,
Output,
EventEmitter,
ViewEncapsulation,
OnInit,
ViewChild,
SimpleChanges,
ChangeDetectorRef
} from '@angular/core';
import {
EditService,
SearchService,
ReorderService,
SortService,
GroupService,
ColumnMenuService,
PageService,
FilterService,
SelectionSettingsModel,
ToolbarItems,
ToolbarService,
GridComponent,
PdfExportService,
ExcelExportService,
DetailRowService,
DetailDataBoundEventArgs,
Grid,
AggregateService,
PdfExportProperties,
LoadingIndicatorModel,
ExcelExportProperties,
Column,
ColumnMenuClickEventArgs,
GridModule
} from '@syncfusion/ej2-angular-grids';
import { Query } from '@syncfusion/ej2-data';
import { L10n, setCulture } from '@syncfusion/ej2-base';
import { NgbModal, NgbPaginationModule } from '@ng-bootstrap/ng-bootstrap';
import { AlertModalComponent } from '../alert-modal/alert-modal.component';
import { ColumnModel, FilterSettingsModel, GroupSettingsModel } from '@syncfusion/ej2-angular-grids';
import { CommonModule } from '@angular/common';
import { SharedModule } from '../shared/shared.module';
import { TranslateModule } from '@ngx-translate/core';
import { FormsModule } from '@angular/forms';
import { FileUploadModule } from 'ng2-file-upload';
import { MatDialogModule } from '@angular/material/dialog';
import { NgSelectModule } from '@ng-select/ng-select';
import { RouterModule } from '@angular/router';
import { QuillModule } from 'ngx-quill';
setCulture('th-TH');
L10n.load({
'en-US': {
'pager': {
'currentPageInfo': '',
'totalItemsInfo': '{1} to {2} of {0}',
}
},
'th-TH': {
'grid': {
'EmptyRecord': 'ไม่มีข้อมูลที่จะแสดง',
'Item': '1 รายการ',
'Items': '{0} รายการ'
},
'pager': {
'All': 'ทั้งหมด',
'pagerAllDropDown': 'รายการ',
'currentPageInfo': '',
'totalItemsInfo': '{0} รายการ',
'totalItemInfo': '{0} รายการ',
'totalRecordsInfo': '{0} รายการ',
'firstPageTooltip': 'หน้าแรก',
'lastPageTooltip': 'หน้าสุดท้าย',
'nextPageTooltip': 'ถัดไป',
'previousPageTooltip': 'ก่อนหน้า',
'nextPagerTooltip': 'ถัดไป',
'previousPagerTooltip': 'ก่อนหน้า',
'pageInput': '{0}',
'page': 'หน้า',
'pagerDropDown': 'รายการ',
'pagerAll': 'ทั้งหมด',
'pageSize': 'รายการต่อหน้า',
'pageSizeAll': 'ทั้งหมด',
'pageCount': 'จำนวนหน้า',
'pageCountAll': 'ทั้งหมด'
}
}
});
@Component({
standalone: true,
imports: [
CommonModule,
SharedModule,
TranslateModule,
NgSelectModule,
FormsModule,
RouterModule,
FileUploadModule,
QuillModule,
MatDialogModule,
NgbPaginationModule,
GridModule
],
selector: 'app-datagrid-syncfution',
templateUrl: './datagrid-syncfution.component.html',
styleUrls: ['./datagrid-syncfution.component.scss'],
providers: [AggregateService, SortService, GroupService, ColumnMenuService, PageService, FilterService, ToolbarService, PdfExportService, ExcelExportService, DetailRowService, ReorderService, EditService, SearchService],
encapsulation: ViewEncapsulation.None
})
export class DatagridSyncfutionComponent implements OnInit {
@ViewChild('grid') public grid?: GridComponent;
@Input() dataSource: any[] = [];
dataSourceSearch: any[] = [];
dataSourceFilter: any[] = [];
@Output() sendSelectData = new EventEmitter<any>();
@Input() columns: ColumnModel[] = [];
// @Input() toolbarOptions?: ToolbarItems[]
@Input() toolbarOptions?: ToolbarItems[] = ['Print', 'ExcelExport', 'CsvExport'];
@Input() searchSettings: any = {
fields: [],
operator: 'contains',
ignoreCase: true,
};
@Input() selectedItems: { key: string, count: number, data: Map<string, boolean> } = { key: '', count: 0, data: new Map<string, boolean>() };
selectedItemsAll = false
@Output() sendSelectedItems = new EventEmitter<any>();
@Input() modalName = ''
@Input() canChild = false
@Output() sendNextPage = new EventEmitter<any>();
@Input() childView = false
@Output() sendNextPageView = new EventEmitter<any>();
@Input() canDownload = false
@Output() sendFileDownload = new EventEmitter<any>();
@Input() childForm = false
@Output() sendNextPageForm = new EventEmitter<any>();
modalStatus = '';
@Output() createFormEvent = new EventEmitter<any>();
@Input() checkBoxSetting = true
@Input() actionSetting = true
@Input() detailSetting = false
@Input() childList = ''
@Input() searchText = ''
aggregatesSum: any[] = [];
aggregatesCount: any[] = [];
aggregatesAvg: any[] = [];
aggregatesMin: any[] = [];
aggregatesMax: any[] = [];
@Input() childColumns: ColumnModel[] = [];
@Input() allowSorting = true;
@Input() allowFiltering = true;
@Input() filterSettings?: FilterSettingsModel = { type: 'Excel' };;
@Input() groupSettings?: GroupSettingsModel = { allowReordering: true, showGroupedColumn: true, showDropArea: false };
public selectionOptions?: SelectionSettingsModel = { checkboxOnly: true };
@Input() editSettings? = { allowEditing: true, mode: 'Batch' };
@Input() initialPage? = { pageSizes: true, pageSize: 10 };
@Input() canDelete = true
@Input() canEdit = true
// ... เป็นต้น
public query: Query = new Query().addParams('dataCount', '1000');
loadingIndicator: LoadingIndicatorModel = { indicatorType: 'Shimmer' };
// ตัวอย่าง event เมื่อมีการลบ / แก้ไข
@Output() actionBegin = new EventEmitter<any>();
// อาจมี event อื่น ๆ เช่น pageChange, filterChange, ฯลฯ
// แล้วแต่ logic ที่ต้องการให้ parent รู้
// @Output() pageChanged = new EventEmitter<number>();
// @Output() filterChanged = new EventEmitter<FilterEventArgs>();
// ...
public columnMenuItems: any[] = [
'AutoFit', 'AutoFitAll', 'SortAscending', 'SortDescending',
'Group', 'Ungroup', 'ColumnChooser', 'Filter',
{ text: 'Sum', id: 'aggregate_sum' },
{ text: 'Count', id: 'aggregate_count' },
{ text: 'Average', id: 'aggregate_average' },
{ text: 'Min', id: 'aggregate_min' },
{ text: 'Max', id: 'aggregate_max' }
];
@Input() sendLayout = false
@Input() gridLayout = ''
@Output() layout = new EventEmitter<any>();
constructor(private cdr: ChangeDetectorRef
) {
}
ngOnInit(): void {
// this.query = new Query().addParams('dataCount', '1000');
this.loadingIndicator = { indicatorType: 'Shimmer' };
// ถ้ามี logic ของ child เอง เช่นตั้งค่าเริ่มต้น
// ให้เขียนตรงนี้ได้
}
ngOnChanges(changes: SimpleChanges): void {
this.query = new Query().addParams('dataCount', '1000');
this.loadingIndicator = { indicatorType: 'Shimmer' };
if (changes) {
if (changes['searchText']) {
this.search(changes['searchText'].currentValue)
}
if (changes['dataSource']) {
this.dataSourceSearch = JSON.parse(JSON.stringify(this.dataSource || []))
this.dataSourceFilter = JSON.parse(JSON.stringify(this.dataSource || []))
}
if (changes['gridLayout']) {
if (changes['gridLayout'].currentValue) {
const layout = JSON.parse(changes['gridLayout'].currentValue)
this.grid?.setProperties(layout)
}
}
if (changes['sendLayout']) {
if (this.grid) {
const layout = this.grid?.getPersistData();
if (layout) {
this.layout.emit(layout)
}
}
}
}
}
onSelectData(args: any) {
this.sendSelectData.emit(args);
}
onNextPage(args: any) {
this.sendNextPage.emit(args);
}
toolbarClick(args: any): void {
if (args.item.id === 'Grid_excelexport') {
let exportProperties: ExcelExportProperties = {
columns: this.columns.map(col => ({
field: col.field,
headerText: col.headerText
})) as Column[]
};
this.grid?.excelExport(exportProperties);
} else if (args.item.id === 'Grid_csvexport') {
let exportColumns = this.columns.map(col => ({
field: col.field!,
headerText: col.headerText!
}));
this.grid?.csvExport({ columns: exportColumns as Column[] });
} else if (args.item.id === 'Grid_print') {
const checkBoxSetting = this.checkBoxSetting
this.checkBoxSetting = false
const actionSetting = this.actionSetting
this.actionSetting = false
this.cdr.detectChanges()
setTimeout(() => {
this.checkBoxSetting = checkBoxSetting
this.actionSetting = actionSetting
this.cdr.detectChanges()
}, 1000)
}
}
search(text: string) {
if (this.grid) {
if (this.searchSettings.fields.length) {
this.dataSourceSearch = JSON.parse(JSON.stringify(this.filterData(text, this.searchSettings.fields)));
this.dataSourceFilter = JSON.parse(JSON.stringify(this.filterData(text, this.searchSettings.fields)));
} else {
(this.grid as GridComponent).search(text);
}
}
}
filterData(text: string, fields: string[]): any[] {
return this.dataSource.filter(item => {
return fields.some(field => {
const value = field.split('.').reduce((obj, key) => obj?.[key], item);
return value?.toString().toLowerCase().includes(text.toLowerCase());
}
);
});
}
detailDataBound(e: DetailDataBoundEventArgs) {
let detail = new Grid({
// ─────────────────────────────────────────────────────────────
// เดิมเป็น data.filter(...) เทียบ EmployeeID
// เปลี่ยนมาใช้ e.data.massessesList โดยตรง
// ─────────────────────────────────────────────────────────────
dataSource: (e.data as any)[this.childList.toString()],
columns: this.childColumns
});
detail.appendTo((e.detailElement as HTMLElement).querySelector('.custom-grid') as HTMLElement);
// let detail = new Grid({
// dataSource: data.filter((item: Object) => (item as ColumnSpanDataType)[this.childList] === (e.data as any)[this.childList]),
// columns: [
// { field: 'OrderID', headerText: 'Order ID', width: 110 },
// { field: 'CustomerID', headerText: 'Customer Name', width: 140 },
// { field: 'ShipCountry', headerText: 'Ship Country', width: 150 }
// ]
// });
// detail.appendTo((e.detailElement as HTMLElement).querySelector('.custom-grid') as HTMLElement);
}
toggleSelection(key: string, value: boolean) {
this.selectedItems.data.set(key, value);
this.selectedItems.count = Array.from(this.selectedItems.data.values()).filter(v => v).length;
this.sendSelectedItems.emit(this.selectedItems);
}
toggleSelectionAll() {
this.selectedItemsAll = !this.selectedItemsAll;
this.selectedItems.data.forEach((_, key) => {
if (this.dataSourceFilter.find((x: any) => x[this.selectedItems.key] == key)) {
this.selectedItems.data.set(key, this.selectedItemsAll);
}
});
this.selectedItems.count = Array.from(this.selectedItems.data.values()).filter(v => v).length;
this.sendSelectedItems.emit(this.selectedItems);
}
actionComplete(args: any) {
if (args.requestType === 'filtering') {
if (this.grid) {
this.dataSourceFilter = ((this.grid as GridComponent).getFilteredRecords() as any).length ? ((this.grid as GridComponent).getFilteredRecords() as any) : JSON.parse(JSON.stringify(this.dataSourceSearch || []));
}
}
}
onColumnMenuClick(args: ColumnMenuClickEventArgs): void {
if (!args.item.id) { return; }
// เช็คว่าเป็น aggregate_... ไหม
if (args.item.id.startsWith('aggregate_')) {
const colField = (args.column as any)?.field;
if (!colField) { return; }
// ดีบักดูว่า user คลิกอะไร
// console.log('Column Menu Click =>', args.item.id, 'Field:', colField);
// ถอดเอาคำว่า aggregate_ ออก
const selectedAgg = args.item.id.split('_')[1]; // sum, count, average, min, max, custom
// เอาไปแก้ไขค่าใน this.aggregates ให้ตรงคอลัมน์
// สมมุติว่า field ใน aggregates ไม่ซ้ำกัน
// const aggIndex = this.aggregates.findIndex(a => a.field === colField);
if (selectedAgg === 'sum') {
if (this.aggregatesSum.find(a => a.field === colField)) {
this.aggregatesSum = this.aggregatesSum.filter(a => a.field !== colField);
} else {
this.aggregatesSum.push({
field: colField,
type: 'Sum',
footerTemplate: 'Sum: ${Sum}'
});
}
this.cdr.detectChanges()
}
else if (selectedAgg === 'count') {
this.aggregatesCount.push({
field: colField,
type: 'Count',
footerTemplate: 'Count: ${Count}'
});
} else if (selectedAgg === 'average') {
this.aggregatesAvg.push({
field: colField,
type: 'Average',
footerTemplate: 'Avg: ${Average}'
});
}
else if (selectedAgg === 'min') {
this.aggregatesMin.push({
field: colField,
type: 'Min',
footerTemplate: 'Min: ${Min}'
});
}
else if (selectedAgg === 'max') {
this.aggregatesMax.push({
field: colField,
type: 'Max',
footerTemplate: 'Max: ${Max}'
});
}
// if (aggIndex !== -1) {
// switch (selectedAgg) {
// case 'sum': this.aggregates[aggIndex].type = 'Sum'; break;
// case 'count': this.aggregates[aggIndex].type = 'Count'; break;
// case 'average': this.aggregates[aggIndex].type = 'Average'; break;
// case 'min': this.aggregates[aggIndex].type = 'Min'; break;
// case 'max': this.aggregates[aggIndex].type = 'Max'; break;
// case 'custom': this.aggregates[aggIndex].type = 'Custom';
// // ถ้ามีฟังก์ชัน customAggregate ที่ต้องการ ก็เซ็ตด้วย
// // this.aggregates[aggIndex].customAggregate = this.myCustomFunction;
// break;
// }
// } else {
// if (selectedAgg === 'sum') {
// this.aggregates.push({
// field: colField,
// type: 'Sum'
// });
// }
// else if (selectedAgg === 'count') {
// this.aggregates.push({
// field: colField,
// type: 'Count'
// });
// }
// }
// จากนั้น refresh grid เพื่อให้ aggregates คำนวณใหม่
setTimeout(() => {
this.grid?.refresh();
}, 500);
// this.grid?.refresh();
}
}
onNextPageView(data: any) {
this.sendNextPageView.emit(data);
}
onFileDownload(data: any) {
this.sendFileDownload.emit(data);
}
onNextPageForm(data: any) {
this.sendNextPageForm.emit(data);
}
}
<ejs-pivotview #pivotview id='PivotView' [dataSourceSettings]="dataSourceSettings" [displayOption]='displayOption'
[toolbar]='toolbarOptions' [allowExcelExport]='true' [allowNumberFormatting]='true'
[allowConditionalFormatting]='true' [allowPdfExport]='true' [showToolbar]='true' [allowCalculatedField]='true'
[showFieldList]='true' width='100%' height='550'></ejs-pivotview>
\ No newline at end of file
// th{
// position: relative; // เทียบเท่า class "relative"
// padding: 10px; // เทียบเท่า class "px-10px py-10px" (อาจเปลี่ยนตามต้องการ)
// background-color: rgb(96 165 250 / 0.1); // ตัวอย่างแทน "bg-soft-secondary"
// color: #2b2b2b; // ตัวอย่างแทน "text-primary"
// text-align: center !important; // เทียบเท่า "!text-center"
// // หากต้องการดีไซน์อื่น ๆ เพิ่มเติมก็ใส่ในนี้ได้เลย เช่น:
// font-weight: 600;
// border-bottom: 1px solid #eee;
// }
.e-headercell,
.e-detailheadercell {
background-color: rgb(96 165 250 / 0.1) !important;
}
.e-pager .e-currentitem, .e-pager .e-currentitem:hover {
background: rgb(96 165 250) !important;
color: #fff;
opacity: 1 !important;
}
.e-checkbox-wrapper .e-frame.e-check, .e-css.e-checkbox-wrapper .e-frame.e-check {
background-color: rgb(96 165 250) !important;
border-color: transparent;
color: #fff;
}
.e-checkbox-wrapper .e-frame, .e-css.e-checkbox-wrapper .e-frame {
border: 1px solid !important;
border-radius: 2px;
box-sizing: border-box;
cursor: pointer;
display: inline-block;
font-family: "e-icons";
height: 18px;
line-height: 10px;
padding: 2px 0;
text-align: center;
vertical-align: middle;
width: 1rem !important;
border-color: #64748b !important;
}
.e-grid td.e-selectionbackground {
background-color: #aec2ec !important;
}
import { CommonModule } from "@angular/common";
import { ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output, SimpleChanges, ViewChild } from "@angular/core";
import {
IDataOptions,
PivotView,
ToolbarItems,
GroupingBarService,
ToolbarService,
PDFExportService,
ExcelExportService,
DisplayOption,
IDataSet,
DataSourceSettings,
PivotViewComponent,
FieldList,
PivotViewModule
} from '@syncfusion/ej2-angular-pivotview';
import { DataSourceSettingsModel } from '@syncfusion/ej2-pivotview/src/model/datasourcesettings-model';
import { SharedModule } from "../shared/shared.module";
@Component({
standalone: true,
imports: [
CommonModule,
SharedModule,
PivotViewModule
],
selector: 'app-pivot-syncfution',
templateUrl: './pivot-syncfution.component.html',
styleUrls: ['./pivot-syncfution.component.scss'],
providers: [
GroupingBarService,
ToolbarService,
PDFExportService,
ExcelExportService
]
})
export class PivotSyncfutionComponent implements OnInit {
dataSourceSettings: DataSourceSettingsModel = {
dataSource: [],
expandAll: false,
enableSorting: true,
drilledMembers: [],
columns: [],
values: [],
rows: [],
formatSettings: [],
filters: []
};
@Input() dataSource: any
@Input() columns: any
@Input() templateId!: string;
@Input() fileName!: string;
toolbarOptions: ToolbarItems[] = ['Grid', 'Chart', 'Export', 'SubTotal', 'GrandTotal', 'Formatting', 'FieldList']
displayOption: DisplayOption = { view: 'Both' } as DisplayOption;
@ViewChild('pivotview') public pivotview?: PivotViewComponent;
@Input() sendLayout = false
@Input() pivotLayout = ''
@Output() layout = new EventEmitter<any>();
constructor(private cdr: ChangeDetectorRef) { }
ngOnInit(): void {
}
ngOnChanges(changes: SimpleChanges): void {
if (changes['dataSource']?.currentValue || changes['columns']?.currentValue) {
this.dataSourceSettings = {
dataSource: this.dataSource,
columns: this.columns,
values: [],
rows: [],
expandAll: true,
enableSorting: true,
drilledMembers: [],
filters: [],
formatSettings: []
};
}
if (changes['pivotLayout']) {
if (changes['pivotLayout'].currentValue) {
const layout = JSON.parse(changes['pivotLayout'].currentValue)
this.pivotview?.setProperties(layout)
}
}
if (changes['sendLayout']) {
if (this.pivotview) {
const layout = this.pivotview?.getPersistData();
if (layout) {
this.layout.emit(layout)
}
}
}
}
}
\ No newline at end of file
......@@ -93,7 +93,7 @@ export class NavService implements OnDestroy {
getCommonMenu() {
return [
// Dashboard
{ headTitle: 'ผู้ดูแลระบบ' },
{ headTitle: 'MyJob' },
{
icon: 'news',
path: '/admin/pdpa-manage',
......@@ -131,30 +131,24 @@ export class NavService implements OnDestroy {
{ path: '/admin/provinces', title: 'จัดการจังหวัด', type: 'link' },
],
},
{ headTitle: 'Myportal' },
{ headTitle: 'MyPortal' },
{
icon: 'news',
path: '/admin/pdpa-manage',
title: 'จัดการ PDPA',
type: 'link',
icon: 'user',
path: '/admin/set-excel-reports',
title: 'ตั้งรายงานเอ็กเซล',
type: 'sub',
children: [
{ path: '/admin/excel-list', title: 'เพิ่มรายงาน Excel', type: 'link' },
{ path: '/admin/member-manage', title: 'เปิด-ปิด การใช้รายงาน Excel', type: 'link' }
],
},
{
icon: 'receipt',
path: '/admin/manage-articles',
title: 'จัดการบทความ',
title: 'DataSource Tabel',
type: 'link',
},
{
icon: 'user',
path: '/admin/company-departments',
title: 'จัดการผู้ใช้',
type: 'sub',
children: [
{ path: '/admin/manage-companys', title: 'จัดการบริษัท', type: 'link' },
{ path: '/admin/member-manage', title: 'จัดการผู้สมัครงาน', type: 'link' }
],
},
{
icon: 'buildings',
path: '/admin/company-departments',
title: 'ทะเบียนบริษัท',
......
import { Pipe, PipeTransform } from '@angular/core';
import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser';
@Pipe({
name: 'safeUrl',
standalone: true
})
export class SafeUrlPipe implements PipeTransform {
constructor(private sanitizer: DomSanitizer) { }
transform(url: string): SafeResourceUrl {
return this.sanitizer.bypassSecurityTrustResourceUrl(url);
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment