Compare commits

...

236 Commits

Author SHA1 Message Date
Fuzi_fauzia b00896d407 validasi mandatory hari 2024-08-28 15:16:05 +07:00
Fuzi_fauzia 7c443080d6 perbaikan UI scheduler 2024-08-27 16:41:09 +07:00
Fuzi_fauzia 98159a1584 penambahan label detail 2024-08-26 14:13:37 +07:00
Fuzi_fauzia 5c5ca65f53 perbaikan label 2024-08-26 11:54:17 +07:00
Fuzi_fauzia ecef25f30d integrasi edit scheduler 2024-08-26 11:43:22 +07:00
Fuzi_fauzia 45e280c007 penyesuaian scheduler 2024-08-26 11:21:56 +07:00
Fuzi_fauzia ca5f0c8c0c integrasi add dan delete scheduler 2024-08-26 11:04:02 +07:00
Fuzi_fauzia b1ee612602 penambahan UI scheduler list 2024-08-23 00:27:24 +07:00
Fuzi_fauzia 9654cf92e8 perbakan UI scheduler 2024-08-22 15:45:48 +07:00
Fuzi_fauzia a2d166ac1a penambahan UI control scheduler 2024-08-22 14:09:13 +07:00
Fuzi_fauzia a9a7e6edd3 warning perubahan data pada list monitoring 2024-08-22 10:42:01 +07:00
Fuzi_fauzia b5e0962079 perbaikan logic save device 2024-08-22 10:10:38 +07:00
Fuzi_fauzia 633effdb9b penambahan pop up table dan export chart 2024-08-21 15:29:28 +07:00
Fuzi_fauzia 7a28e52e89 perbaikan service 2024-08-21 11:51:04 +07:00
Fuzi_fauzia c54fdd0f55 perbaikan alert watt 2024-08-21 10:53:06 +07:00
Fuzi_fauzia 04972e33b1 validasi device 2024-08-21 10:20:59 +07:00
Fuzi_fauzia f81dc7bf66 perubahan icon back 2024-08-20 16:27:49 +07:00
Fuzi_fauzia 2b1f6c0cfe logic save ketika room di remove semua 2024-08-20 16:12:48 +07:00
Fuzi_fauzia 9529da581a alert mandatory monitoring list 2024-08-20 16:04:45 +07:00
Fuzi_fauzia b195055651 perbaikan validasi penempatan device dalam ruangan 2024-08-20 15:37:50 +07:00
Fuzi_fauzia 2a4135c966 perbaikan monitoring list search 2024-08-20 11:45:47 +07:00
Fuzi_fauzia cd1f56e07b filter floor status aktif 2024-08-20 11:37:45 +07:00
Fuzi_fauzia 9377bebcb4 perbaikan UI 2024-08-20 11:02:10 +07:00
Fuzi_fauzia 57bcbcccde hapus validasi name floor 2024-08-20 10:30:31 +07:00
Fuzi_fauzia d753a3a634 perbaikan onchange select floor 2024-08-19 15:16:33 +07:00
Fuzi_fauzia a9d135f8ef integrasi select floor per building id 2024-08-19 14:58:00 +07:00
Fuzi_fauzia 0aac2ff194 perbaikan tampilan cost management 2024-08-19 13:08:35 +07:00
Fuzi_fauzia 379bdb24da perbaikan routing see more 2024-08-19 12:48:09 +07:00
Fuzi_fauzia 21b75ca610 klik detail per floor dan room 2024-08-19 11:29:59 +07:00
Fuzi_fauzia b5cf71d008 penambahan route floor dan room 2024-08-19 10:06:37 +07:00
Fuzi_fauzia cd777f11ad penambahan ui tidak ada data 2024-08-19 09:32:57 +07:00
Fuzi_fauzia f4036b1a06 penambahan filter 2024-08-19 09:26:56 +07:00
Fuzi_fauzia 4fdaa8c1bd penambahan filter dashboard 2024-08-14 12:45:13 +07:00
Fuzi_fauzia 5410d966d0 update versi 2024-08-13 10:16:21 +07:00
irfan.ms bfb828c15c Fixing select Room 2024-08-12 16:13:40 +07:00
Fuzi_fauzia 124f3eef4c penambahan UI pada setiap inputan monitoring list 2024-08-12 13:54:40 +07:00
Fuzi_fauzia 9b4ac3b992 penambahan floor pada room building dan code pada mster floor 2024-08-08 16:19:07 +07:00
Fuzi_fauzia d70edcebd8 perubahan backgound login 2024-07-26 10:16:28 +07:00
Fuzi_fauzia a458f2ce3c update versi 2024-07-26 09:42:03 +07:00
Fuzi_fauzia db9a9e415a perbaikan master 2024-07-26 09:41:23 +07:00
Fuzi_fauzia a9f3da867f update versi 2024-07-25 09:47:13 +07:00
Fuzi_fauzia 03c7e54bc8 penambahan search code 2024-07-25 09:46:28 +07:00
Fuzi_fauzia df1cd2339e perbaikan master room dan detail monitoring 2024-07-23 13:25:47 +07:00
Fuzi_fauzia 2a6cf6410d perbaikan edit master 2024-07-22 15:21:50 +07:00
Fuzi_fauzia 3b3757e2a7 pernambahan code pada master room 2024-07-22 15:09:38 +07:00
Fuzi_fauzia 95fa99799c perbaikan cost management 2024-07-22 13:41:58 +07:00
Fuzi_fauzia 8cc1a2b9fb perubahan background maintenance 2024-07-22 11:35:51 +07:00
Fuzi_fauzia 9d8e31e7fe update UI profil 2024-07-22 11:15:05 +07:00
Fuzi_fauzia c04cafa384 perbaikan tampilan device edit 2024-07-22 11:01:46 +07:00
Fuzi_fauzia e3be28b4b6 perubahan tampilan detail 2024-07-22 09:38:57 +07:00
Fuzi_fauzia da4100993b perbaikan master layout button 2024-07-18 15:15:40 +07:00
Fuzi_fauzia 0c1dd29554 perbaikan master 2024-07-18 14:56:37 +07:00
Fuzi_fauzia f86dfb7829 penjagaan double room 2024-07-18 14:35:07 +07:00
Fuzi_fauzia 9a29e2e4e0 update versi 2024-07-18 14:03:54 +07:00
Fuzi_fauzia 721d4e9af9 penambahan UI untuk room building ID 2024-07-18 14:02:57 +07:00
Fuzi_fauzia 153b47eaaa perbaikan detail building 2024-07-17 17:44:41 +07:00
Fuzi_fauzia c337a8941c penambahan temperature, air quality dan humidity 2024-07-17 15:40:55 +07:00
Fuzi_fauzia bd1cd42ed6 slicing design baru 2024-07-17 11:51:29 +07:00
Fuzi_fauzia 7bc1fa5fe9 slicing cost manager 2024-07-16 16:33:20 +07:00
Fuzi_fauzia 768c9ee6ca perbaikan service 2024-07-16 15:53:22 +07:00
Fuzi_fauzia efb6ae0060 slicing device 2024-07-16 15:04:12 +07:00
Fuzi_fauzia d22eaa50e1 slicing see all control device 2024-07-16 14:24:45 +07:00
Fuzi_fauzia 60d14b2fce perbaikan service 2024-07-16 13:48:21 +07:00
Fuzi_fauzia 6b91c937ab implement refresh token 2024-07-16 13:46:58 +07:00
Fuzi_fauzia fa12fcec50 kwh max lenght 2024-07-16 11:43:08 +07:00
Fuzi_fauzia 9570b77e32 update versi 2024-07-16 11:40:31 +07:00
Fuzi_fauzia dd1deb0812 perbaikan master dan ngecek token jika ada kegiatan 2024-07-16 11:39:44 +07:00
Fuzi_fauzia 751c36209e slicing dashboard 2024-07-15 21:39:12 +07:00
Fuzi_fauzia 4c2d40986e perbaikan master building 2024-07-14 13:56:25 +07:00
Fuzi_fauzia 5782af4c75 perbaikan master edit 2024-07-13 22:47:01 +07:00
Fuzi_fauzia 5b3c9bcba4 perbaikan list building 2024-07-12 16:53:04 +07:00
Fuzi_fauzia 45f3c9b86b perbaikan css 2024-07-12 16:04:47 +07:00
Fuzi_fauzia 9a0d0d919e perbaikan label master room 2024-07-12 15:25:32 +07:00
Fuzi_fauzia 13e5ee01d2 perbaikan master 2024-07-12 15:16:53 +07:00
Fuzi_fauzia a9034c1a32 perbaikan profil, update password dan master 2024-07-11 14:39:06 +07:00
Fuzi_fauzia 28067d1704 penambahan maxleght 2024-07-11 11:45:53 +07:00
Fuzi_fauzia 4ae3b51530 perbaiki kwh 2024-07-11 11:28:41 +07:00
Fuzi_fauzia dc4be31103 perbaikan kwh 2024-07-11 11:23:42 +07:00
Fuzi_fauzia 0b37bcd8ee update versi 2024-07-11 10:26:13 +07:00
Fuzi_fauzia a79493d329 edit room building 2024-07-11 10:24:59 +07:00
Fuzi_fauzia 52481cbc4b salah posisi validator 2024-07-11 10:04:48 +07:00
Fuzi_fauzia d43a26513e perbaikan edit building 2024-07-11 10:03:08 +07:00
Fuzi_fauzia 06a7c9b249 version update 2024-07-11 09:59:54 +07:00
Fuzi_fauzia 876f039603 edit profile 2024-07-11 09:59:06 +07:00
Fuzi_fauzia a455f43df6 update versi 2024-07-10 18:37:08 +07:00
Fuzi_fauzia 3ef7dab91f perbaikan master building 2024-07-10 18:05:07 +07:00
Fuzi_fauzia 8d9840acec perbaikan master 2024-07-10 16:38:19 +07:00
Fuzi_fauzia 2194d2f720 update versi 2024-07-10 15:32:36 +07:00
Fuzi_fauzia d5c34a5c64 perbaikan profiln dan cost management 2024-07-10 15:20:16 +07:00
Fuzi_fauzia 1ed4ad5da0 update icon update password 2024-07-09 16:20:28 +07:00
Fuzi_fauzia f86f727ac7 implement token exp 2024-07-09 14:37:00 +07:00
Fuzi_fauzia d8dc510742 edit profil refresh token 2024-07-09 11:28:30 +07:00
Fuzi_fauzia 1edfbc7149 update label cost 2024-07-08 16:09:40 +07:00
Fuzi_fauzia 15ae21bf9c perbaikan max lenght device 2024-07-08 15:17:23 +07:00
Fuzi_fauzia 4c229e994a penambahan maxleght 2024-07-08 10:31:22 +07:00
Fuzi_fauzia 978127f2c4 update validator user profil 2024-07-08 09:58:41 +07:00
Fuzi_fauzia daef6aafdc perbaikan cost management 2024-07-04 16:43:16 +07:00
Fuzi_fauzia 79b6300bf0 perbaikan profil data 2024-07-04 15:30:46 +07:00
Fuzi_fauzia ae74cf2332 integrasi user profil 2024-07-04 14:23:44 +07:00
Fuzi_fauzia 5913980330 penambahan validasi filter cost management 2024-07-04 10:41:30 +07:00
Fuzi_fauzia 8403bafcd1 perbaikan export cost management 2024-07-04 10:00:48 +07:00
Fuzi_fauzia 3172ba70b2 perbaikan kontrol device 2024-07-04 09:44:39 +07:00
Fuzi_fauzia ccf656dfce integrasi update password 2024-07-03 17:39:46 +07:00
Fuzi_fauzia 4b023b2527 perrbaikan master 2024-07-03 13:25:25 +07:00
Fuzi_fauzia a1b259ac9f perbaikan master 2024-07-03 13:01:36 +07:00
Fuzi_fauzia b095f870dd perbaikan master 2024-07-02 22:45:07 +07:00
Fuzi_fauzia b2d8a8e2f6 perbaikan monitoring icon 2024-07-01 16:18:23 +07:00
Fuzi_fauzia 71377f79cf perbaikan icon control device 2024-07-01 16:14:18 +07:00
Fuzi_fauzia 84dae31196 perbaikan get actual cost 2024-07-01 14:40:58 +07:00
Fuzi_fauzia 123c39e5a2 perbaikan tanggal UCT 2024-07-01 14:33:57 +07:00
Fuzi_fauzia 5afb289070 validasi add new master 2024-07-01 11:49:04 +07:00
Fuzi_fauzia 04e8abc487 filter device 2024-07-01 10:58:22 +07:00
Fuzi_fauzia 97b2445d4f penambahan UI update password 2024-06-28 16:31:55 +07:00
Fuzi_fauzia be071b75b1 perbaikan export cost management 2024-06-28 10:44:45 +07:00
Fuzi_fauzia ca30a991ae export detail 2024-06-28 08:07:17 +07:00
Fuzi_fauzia be40cea2a1 hide menu 2024-06-28 06:58:10 +07:00
Fuzi_fauzia 563165d09c hide menu 2024-06-28 06:45:48 +07:00
Fuzi_fauzia 86239f03eb update versi 2024-06-28 06:43:34 +07:00
Fuzi_fauzia 99391c88bb penambahan export pada cost management dan summary detail 2024-06-28 06:42:42 +07:00
Fuzi_fauzia 8e0a1abcda perbaikan jika tidak ada device pada control device 2024-06-27 18:00:12 +07:00
Fuzi_fauzia f6934579ed perbaikan select kepotong 2024-06-27 15:43:57 +07:00
Fuzi_fauzia 798babdb8a delete statusId 2024-06-27 14:58:33 +07:00
Fuzi_fauzia 4456b3a3f8 update versi 2024-06-27 14:45:30 +07:00
Fuzi_fauzia 197b428d18 perbaikan summary device 2024-06-27 14:41:51 +07:00
Fuzi_fauzia 530b64cb10 penambahan user profile 2024-06-26 16:53:54 +07:00
Fuzi_fauzia 2a5c1ef7b9 update versi 2024-06-26 14:29:00 +07:00
Fuzi_fauzia b5c35ab541 perbaikan interceptor 2024-06-26 14:26:15 +07:00
Fuzi_fauzia 4630bd2265 penambahan button new device 2024-06-26 11:26:02 +07:00
Fuzi_fauzia f04515fb41 perbaikan service device 2024-06-25 22:14:32 +07:00
Fuzi_fauzia f36c8d0240 penyesuaian service 2024-06-25 22:10:31 +07:00
Fuzi_fauzia 89203b9984 penambahan icon pada master category 2024-06-25 21:54:12 +07:00
Fuzi_fauzia fc9947da15 penambahan button cancel device control 2024-06-25 18:06:20 +07:00
Fuzi_fauzia 8ea2f9f23b update versi 2024-06-25 17:25:20 +07:00
Fuzi_fauzia 6261b36074 penyesuaian UI monitoring 2024-06-25 17:22:57 +07:00
Fuzi_fauzia 19b43b8a2d perbaikan device 2024-06-25 16:37:49 +07:00
Fuzi_fauzia 5cbd0f09dc penambahan modal export 2024-06-25 15:39:26 +07:00
Fuzi_fauzia e27493171e penambahan logic pada cctv 2024-06-25 14:45:11 +07:00
Fuzi_fauzia 873e24a6ac menghilangkan water 2024-06-24 12:28:05 +07:00
Fuzi_fauzia 30726d44b4 update versi 2024-06-24 12:14:27 +07:00
Fuzi_fauzia 20859bd463 penambahan "see more" control device 2024-06-24 12:13:41 +07:00
Fuzi_fauzia e1f42b5378 perbaikan tampilan progressbar 2024-06-24 11:04:29 +07:00
Fuzi_fauzia 113221db7f update versi 2024-06-24 10:46:59 +07:00
Fuzi_fauzia b968e8e434 perbaikan detail building 2024-06-24 10:23:38 +07:00
Fuzi_fauzia 23154e6862 perbaikan login dan tampilan cost management 2024-06-23 18:10:04 +07:00
Fuzi_fauzia efaf30d55e perbaikan UI device dan penambahan export 2024-06-23 03:09:40 +07:00
Fuzi_fauzia 25559ee299 install video js 2024-06-23 01:06:38 +07:00
Fuzi_fauzia 2bc467b381 fixing logic cctv 2024-06-22 02:58:33 +07:00
Fuzi_fauzia 1433c55fce hover video player 2024-06-22 02:37:16 +07:00
Fuzi_fauzia 547e6ddc8f update versi 2024-06-22 01:56:42 +07:00
Fuzi_fauzia 169be42e81 hide menu 2024-06-22 01:56:05 +07:00
Fuzi_fauzia 024eb6bfc7 slicing ui cctv 2024-06-22 01:52:09 +07:00
Fuzi_fauzia 1ebe1acc12 penambahan list icon 2024-06-21 14:25:14 +07:00
Fuzi_fauzia b9012f5544 hide menu 2024-06-20 17:22:36 +07:00
Fuzi_fauzia 7b9935585a control device 2024-06-20 17:20:22 +07:00
Fuzi_fauzia 49d37ee7e1 integrasi fimter device 2024-06-20 15:50:40 +07:00
Fuzi_fauzia d1393d6ec5 image baru yang sudah di kompres 2024-06-14 15:38:52 +07:00
Fuzi_fauzia 1f98cbc791 ganti icon 2024-06-14 15:01:55 +07:00
Fuzi_fauzia 60ee717b8b fix progress bar 2024-06-14 14:52:03 +07:00
Fuzi_fauzia 6b962028db fixing cost management 2024-06-14 13:27:32 +07:00
Fuzi_fauzia 2180311f4e push assets 2024-06-14 11:37:00 +07:00
Fuzi_fauzia 6a0b6f4f73 hapus assets 2024-06-14 11:25:12 +07:00
Fuzi_fauzia 31bda7fb98 penyesuaian tampilan dashboard 2024-06-14 09:54:22 +07:00
Fuzi_fauzia fb1ba66cd0 penyasuaian UI dashboard 2024-06-13 23:57:07 +07:00
Fuzi_fauzia 863a493b66 penyesuaian UI cooming soon 2024-06-13 23:01:28 +07:00
Fuzi_fauzia 33007acc77 implement interceptor 2024-06-13 19:32:17 +07:00
Fuzi_fauzia 7a07bf5b07 penyesuaian UI list room 2024-06-13 17:14:14 +07:00
Fuzi_fauzia f6db135ac6 integrasi actual cost 2024-06-13 16:20:09 +07:00
Fuzi_fauzia fc10a44d5d penyesuaian UI cost management 2024-06-13 12:09:22 +07:00
Fuzi_fauzia 11184e4a46 custom UI list monitoring 2024-06-12 19:44:51 +07:00
Fuzi_fauzia fe740dc3c1 penyesuaian UI master 2024-06-12 18:35:55 +07:00
Fuzi_fauzia a42d328222 penyesuaian UI login dan master 2024-06-12 17:28:40 +07:00
Fuzi_fauzia 6f928e2ca4 setting menu device 2024-06-12 15:49:32 +07:00
Fuzi_fauzia 8b2849104e update versi 2024-06-12 15:36:30 +07:00
Fuzi_fauzia 468045384e penyesuaian UI sesuai Figma 2024-06-12 15:17:08 +07:00
Fuzi_fauzia ef118ad33e perbaikan cost management service 2024-06-12 10:08:23 +07:00
Fuzi_fauzia 70d8993141 update versi 2024-06-11 20:45:51 +07:00
Fuzi_fauzia ad91c844d6 integrasi cost management 2024-06-11 20:44:05 +07:00
Fuzi_fauzia 38c936b389 integrasi dashboard energy monitoring 2024-06-06 14:54:14 +07:00
Fuzi_fauzia 3f1c97ebe4 penyesuaian UI device 2024-06-05 13:35:58 +07:00
Fuzi_fauzia 4578a5db61 penyesuaian UI monitoring energy 2024-06-05 11:24:14 +07:00
Fuzi_fauzia a3bfd7b328 penyasuaian UI dan penambahan menu 2024-06-04 20:39:04 +07:00
Fuzi_fauzia 0f1c3d6c33 integrasi list monitoring 2024-06-04 11:37:57 +07:00
Fuzi_fauzia 6061f4e58e integrasi monitoring list 2024-06-03 16:15:23 +07:00
Fuzi_fauzia a72ca4ef59 update versi 2024-05-31 17:23:11 +07:00
Fuzi_fauzia a6c77fe052 integrasi edit device, penyesuaian UI jadi vertical dan penambahan UI list monitoring 2024-05-31 17:21:39 +07:00
Fuzi_fauzia b2c1020c22 penambahan room list 2024-05-31 10:42:23 +07:00
Fuzi_fauzia 6a9451c2b5 disable menu 2024-05-28 16:24:54 +07:00
Fuzi_fauzia eaea625b4b integrasi building detail 2024-05-28 16:24:27 +07:00
Fuzi_fauzia ded0ca21a1 penambahan detail room dan penyesuaian device list 2024-05-28 15:27:47 +07:00
Fuzi_fauzia c6a51e91b1 update versi 2024-05-27 14:49:47 +07:00
Fuzi_fauzia 461735705d integrasi building list dan room list 2024-05-25 15:38:45 +07:00
Fuzi_fauzia e76948bb0a integrasi room 2024-05-22 12:13:56 +07:00
Fuzi_fauzia b250a39d6e penyesuaian warna background 2024-05-22 11:27:28 +07:00
Fuzi_fauzia 690dd7ac57 update versi 2024-05-22 11:06:58 +07:00
Fuzi_fauzia f2d277389d integrasi login 2024-05-22 11:05:47 +07:00
Fuzi_fauzia 608c53ab66 integrasi master building dan penyasuaian UI dashboard monitoring building 2024-05-21 16:14:18 +07:00
Fuzi_fauzia bb10d57139 intergrasi master role 2024-05-21 11:57:17 +07:00
Fuzi_fauzia c5834d5a85 penyesuaian UI add device 2024-05-21 11:33:05 +07:00
Fuzi_fauzia c7cb4f30ea update version 2024-05-20 19:50:04 +07:00
Fuzi_fauzia ca9b129494 integrasi master floor dan master voltage 2024-05-20 19:46:50 +07:00
Fuzi_fauzia 258d4f9cdb integrasi master type, status dan duration use 2024-05-20 19:30:10 +07:00
Fuzi_fauzia 4deb8d057f penembahan versi pada login 2024-05-20 15:03:11 +07:00
Fuzi_fauzia 1a11e3f50c tambah versi dan perbaikan router get dari API 2024-05-20 15:01:00 +07:00
Fuzi_fauzia 7bf0474fbb melepas auth guard sementara 2024-05-20 14:38:42 +07:00
Fuzi_fauzia e92c576844 integrasi table device dan table master category 2024-05-18 20:26:44 +07:00
Fuzi_fauzia cfa140b6ea penambahan service monitoring 2024-05-13 16:07:19 +07:00
Fuzi_fauzia 6239e473b3 penambahan page master status, duration use, role, user 2024-05-07 13:21:46 +07:00
Fuzi_fauzia 066c40b78c disable menu yang tidak terpakai 2024-05-02 14:52:13 +07:00
Fuzi_fauzia 0844add2de perbaikan tampilan monitoring detail 2024-05-02 14:12:30 +07:00
Fuzi_fauzia 3aed024f71 perubahan menu vertical ke horizontal 2024-05-02 12:13:12 +07:00
Fuzi_fauzia 5ce07dc3fd perbaikan routing dan penambahan button back pada page monitoring 2024-04-26 16:36:04 +07:00
Fuzi_fauzia ee2b7e35e6 penambahan button back pada breadcrumb 2024-04-26 15:28:14 +07:00
Fuzi_fauzia 3bd4e1df1c penambahan UI detail monitoring building 2024-04-26 11:20:03 +07:00
Fuzi_fauzia e894196ebd penambahan UI master location, location room dan user access 2024-04-26 11:00:37 +07:00
Fuzi_fauzia 457504442e perbaikan tampilan login dan register 2024-04-26 10:34:08 +07:00
Fuzi_fauzia 5fa8e2314d penambahan edit routing monitoring building 2024-04-25 15:28:09 +07:00
Fuzi_fauzia 3cd43f3cc5 perbaikan style monitoring building 2024-04-25 13:27:22 +07:00
Fuzi_fauzia fcc9a97d99 perbaikan style monitoring building 2024-04-25 12:09:10 +07:00
Fuzi_fauzia a1f3069c05 perbaikan search room dan building 2024-04-24 18:32:04 +07:00
Fuzi_fauzia 49dc5613d2 perbaikan tampilan monitoring room 2024-04-24 16:35:16 +07:00
Fuzi_fauzia 074b1f9674 perbaikan breadcrumb room 2024-04-23 17:06:20 +07:00
Fuzi_fauzia dc987d78c9 penambahan page monitoring building dan monitoring room 2024-04-23 17:02:47 +07:00
Fuzi_fauzia 970c903ff4 perbaikan routing monitoring 2024-04-23 13:30:53 +07:00
Fuzi_fauzia fcdafc8aea penambahan page monitoring building dan master category 2024-04-23 10:49:24 +07:00
Fuzi_fauzia 5187f4e29f perbaikan routing menu monitoring 2024-04-22 00:06:00 +07:00
Fuzi_fauzia 3c7deac1b0 penambahan routing master dan monitoring 2024-04-22 00:04:26 +07:00
Fuzi_fauzia d2e11581db penambahan part estimasi cost 2024-04-21 22:20:42 +07:00
Fuzi_fauzia 4dc2c3f1a7 penambahan form input tambah cost management 2024-04-21 21:55:16 +07:00
Fuzi_fauzia 4f4dde0d07 penambahan page cost management 2024-04-21 15:09:49 +07:00
Fuzi_fauzia 7b4f471047 perubahan label kolom table device 2024-04-20 22:55:45 +07:00
Fuzi_fauzia e3315bd761 penambahan form edit dan view 2024-04-20 16:04:29 +07:00
Fuzi_fauzia dc74154a27 perbaikan form input add device 2024-04-20 14:39:24 +07:00
Fuzi_fauzia 6c9b4f18b4 penambahan form input add data device 2024-04-20 01:31:50 +07:00
Fuzi_fauzia 8f0c2fb913 penambahan modal add device 2024-04-20 01:04:47 +07:00
Fuzi_fauzia dbc89d326f penambahan table device 2024-04-19 16:09:00 +07:00
Fuzi_fauzia 14ad53025f penambahan menu device 2024-04-19 15:51:50 +07:00
462 changed files with 44601 additions and 20993 deletions

38210
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -62,6 +62,8 @@
"js-datepicker": "^5.18.2",
"jspdf": "^2.5.1",
"jspdf-autotable": "^3.5.28",
"jwt-decode": "^4.0.0",
"lodash": "^4.17.21",
"lodash-es": "^4.17.21",
"ng-chartist": "^4.1.0",
"ng-multiselect-dropdown": "^0.3.9",
@ -71,6 +73,7 @@
"ngx-echarts": "^15.0.1",
"ngx-google-places-autocomplete": "^2.0.5",
"ngx-image-cropper": "^6.3.2",
"ngx-mask": "^14.2.4",
"ngx-masonry": "^14.0.1",
"ngx-perfect-scrollbar": "^10.1.1",
"ngx-quill": "^20.0.1",
@ -82,13 +85,16 @@
"quill": "^1.3.7",
"quill-delta": "^5.0.0",
"quill-mention": "^3.1.0",
"remixicon": "^4.3.0",
"rxjs": "^7.5.5",
"sass": "^1.57.1",
"save": "^2.9.0",
"sweetalert2": "^11.11.0",
"tree-ngx": "^4.3.0",
"tslib": "^2.4.1",
"util": "^0.12.5",
"uuid": "^9.0.0",
"video.js": "^8.12.0",
"xlsx": "^0.18.5"
},
"devDependencies": {

View File

@ -18,3 +18,12 @@
transform: translate(40px, 40px) !important;
}
}
.breadcrumb-item::before {
content: none !important; /* Removes any default content, such as "-" */
}
.breadcrumb-item + .breadcrumb-item::before {
content: none !important; /* Removes any default content, such as "-" */
}

View File

@ -1,27 +1,37 @@
<div class="row">
<div class="content-header-left col-md-6 col-12 mb-2 breadcrumb-new" *ngIf="breadcrumb">
<h3 class="content-header-title mb-0 d-inline-block">{{breadcrumb.mainlabel}}</h3>
<div
class="content-header-left col-md-6 col-12 mb-2 breadcrumb-new"
*ngIf="breadcrumb"
>
<button
*ngIf="breadcrumb.isLinkBack"
type="button"
class="btn btn-icon btn-pure secondary mr-1"
routerLink="{{ breadcrumb.linkBack }}"
>
<i class="feather ft-chevron-left" style="color: #242222"></i>
</button>
<h3 class="content-header-title mb-0 d-inline-block" style="color: #242222">
{{ breadcrumb.mainlabel }}
</h3>
<div class="row breadcrumbs-top d-inline-block">
<div class="breadcrumb-wrapper col-12">
<ol class="breadcrumb">
<li class="breadcrumb-item" *ngFor="let link of breadcrumb.links">
<a *ngIf="link.isLink" routerLink="{{link.link}}">{{link.name}}</a>
<span *ngIf="!link.isLink">{{link.name}}</span>
</li>
<ng-container *ngFor="let link of breadcrumb.links; let last = last">
<li class="breadcrumb-item" style="color: #242222">
<ng-container *ngIf="link.isLink; else notLink">
<a routerLink="{{ link.link }}" style="color: #242222">{{ link.name }}</a>
</ng-container>
<ng-template #notLink>
<span style="color: #242222">{{ link.name }}</span>
</ng-template>
<span *ngIf="!last" class="breadcrumb-arrow" style="color: #242222; margin: 0 5px;">
<i class="feather ft-chevron-right"></i>
</span>
</li>
</ng-container>
</ol>
</div>
</div>
</div>
<div class="content-header-right col-md-6 col-12">
<div class="d-inline-block float-md-right" ngbDropdown>
<button class="btn btn-info" id="dropdownBasic1" ngbDropdownToggle>Action</button>
<div ngbDropdownMenu class="arrow _dropdown_mob dropdown-menu-right" aria-labelledby="dropdownBasic1">
<button class="dropdown-item">Calender</button>
<button class="dropdown-item">Cart</button>
<button class="dropdown-item">Support</button>
<div class="dropdown-divider"></div>
<button class="dropdown-item">Settings</button>
</div>
</div>
</div>
</div>

View File

@ -1,29 +1,68 @@
<footer id="footer" class="footer footer-static footer-light navbar-border navbar-shadow" *ngIf="showFooter">
<p class="clearfix blue-grey lighten-2 text-sm-center mb-0 px-2"><span
class="float-md-left d-block d-md-inline-block">Copyright &copy; 2022 <a [routerLink]=""
class="text-bold-800 grey darken-2" href="https://themeforest.net/user/pixinvent/portfolio?ref=pixinvent"
target="_blank">PIXINVENT </a></span><span *ngIf="!hideMadeWithLove"
class="float-md-right d-block d-md-inline-block d-none d-lg-block">Hand-crafted & Made with <i
class="feather ft-heart pink"></i>
<span id="scroll-top"></span></span></p>
<footer
id="footer"
class="footer footer-static footer-light navbar-border navbar-shadow"
*ngIf="showFooter"
style="border: none !important;"
>
<p class="clearfix blue-grey lighten-2 text-sm-center mb-0 px-2">
<span class="float-md-ceter d-block d-md-inline-block"
>Copyright &copy; 2024
<a
[routerLink]=""
class="text-bold-800 grey darken-2"
href="https://allbestsistem.com/"
target="_blank"
style="background-color: #ffffff !important;"
>Smart Building Management Systems (V@2024-08-28.1)
</a></span
>
</p>
</footer>
<footer id="footer" class="footer fixed-bottom footer-light navbar-border navbar-shadow" *ngIf="fixedFooter">
<p class="clearfix blue-grey lighten-2 text-sm-center mb-0 px-2"><span
class="float-md-left d-block d-md-inline-block">Copyright &copy; 2022 <a [routerLink]=""
class="text-bold-800 grey darken-2" href="https://themeforest.net/user/pixinvent/portfolio?ref=pixinvent"
target="_blank">PIXINVENT </a></span><span *ngIf="!hideMadeWithLove"
class="float-md-right d-block d-md-inline-block d-none d-lg-block">Hand-crafted & Made with <i
class="feather ft-heart pink"></i>
<span id="scroll-top"></span></span></p>
<footer
id="footer"
class="footer fixed-bottom footer-light navbar-border navbar-shadow"
*ngIf="fixedFooter"
>
<p class="clearfix blue-grey lighten-2 text-sm-center mb-0 px-2">
<span class="float-md-left d-block d-md-inline-block"
>Copyright &copy; 2022
<a
[routerLink]=""
class="text-bold-800 grey darken-2"
href="https://themeforest.net/user/pixinvent/portfolio?ref=pixinvent"
target="_blank"
>PIXINVENT
</a></span
><span
*ngIf="!hideMadeWithLove"
class="float-md-right d-block d-md-inline-block d-none d-lg-block"
>Hand-crafted & Made with <i class="feather ft-heart pink"></i>
<span id="scroll-top"></span
></span>
</p>
</footer>
<footer id="footer" class="footer fixed-bottom footer-dark navbar-border navbar-shadow" *ngIf="darkFooter">
<p class="clearfix blue-grey lighten-2 text-sm-center mb-0 px-2"><span
class="float-md-left d-block d-md-inline-block">Copyright &copy; 2022 <a [routerLink]=""
class="text-bold-800 grey darken-2" href="https://themeforest.net/user/pixinvent/portfolio?ref=pixinvent"
target="_blank">PIXINVENT </a></span><span *ngIf="!hideMadeWithLove"
class="float-md-right d-block d-md-inline-block d-none d-lg-block">Hand-crafted & Made with <i
class="feather ft-heart pink"></i>
<span id="scroll-top"></span></span></p>
<footer
id="footer"
class="footer fixed-bottom footer-dark navbar-border navbar-shadow"
*ngIf="darkFooter"
>
<p class="clearfix blue-grey lighten-2 text-sm-center mb-0 px-2">
<span class="float-md-left d-block d-md-inline-block"
>Copyright &copy; 2022
<a
[routerLink]=""
class="text-bold-800 grey darken-2"
href="https://themeforest.net/user/pixinvent/portfolio?ref=pixinvent"
target="_blank"
>PIXINVENT
</a></span
><span
*ngIf="!hideMadeWithLove"
class="float-md-right d-block d-md-inline-block d-none d-lg-block"
>Hand-crafted & Made with <i class="feather ft-heart pink"></i>
<span id="scroll-top"></span
></span>
</p>
</footer>

View File

@ -2,15 +2,17 @@
<nav
class="top-header header-navbar navbar-expand-md navbar navbar-with-menu navbar-without-dd-arrow navbar-static-top navbar-light navbar-brand-center"
id="top-header" [ngClass]="selectedHeaderNavBarClass">
<div class="navbar-wrapper">
<div class="navbar-wrapper" style="background-color: #2c343b !important;">
<div id="navbar-header" class="navbar-header">
<ul class="nav navbar-nav flex-row">
<li class="nav-item mobile-menu d-md-none mr-auto" ><a
class="nav-link nav-menu-main menu-toggle hidden-xs11" (click)="toggleNavigation($event)"><i
class="feather ft-menu font-large-1" ></i></a></li>
<li class="nav-item"><a [routerLink]="['/dashboard/sales']" class="navbar-brand"><img class="brand-logo" alt="modern admin logo"
<li class="nav-item"><a [routerLink]="['/monitoring']" class="navbar-brand"><img class="brand-logo" alt="modern admin logo"
src="../../../../assets/images/logo/logo.png">
<h3 class="brand-text">{{_themeSettingsConfig.brand.brand_name}}</h3>
<!-- <h3 class="brand-text">{{_themeSettingsConfig.brand.brand_name}}</h3> -->
<!-- <h3 class="brand-text">{{_themeSettingsConfig.brand.brand_name}}</h3> -->
<h3 class="brand-text" style="color: aliceblue;">Hemat</h3>
</a></li>
<li class="nav-item d-md-none"><a class="nav-link open-navbar-container" data-toggle="collapse"
@ -20,7 +22,7 @@
<div class="navbar-container content">
<div class="collapse navbar-collapse show" id="navbar-mobile">
<ul class="nav navbar-nav mr-auto float-left">
<li class="nav-item d-none d-md-block"><a [routerLink]="" class="nav-link nav-menu-main menu-toggle hidden-xs"
<!-- <li class="nav-item d-none d-md-block"><a [routerLink]="" class="nav-link nav-menu-main menu-toggle hidden-xs"
(click)="toggleFixMenu($event)" ><i class="feather ft-menu"></i></a></li>
<li class="nav-item d-none d-md-block"><a [routerLink]="" class="nav-link nav-link-expand"
(click)="toggleFullScreen()" *ngIf ="maximize === 'on'"><i class="ficon feather ft-maximize"></i></a></li>
@ -29,10 +31,10 @@
<div class="search-input" [ngClass]="{'open': isHeaderSearchOpen}">
<input class="input" type="text" placeholder="Explore Modern...">
</div>
</li>
</li> -->
</ul>
<ul class="nav navbar-nav float-right" >
<li class="dropdown-language nav-item" ngbDropdown *ngIf ="internationalization === 'on'">
<!-- <li class="dropdown-language nav-item" ngbDropdown *ngIf ="internationalization === 'on'">
<a [routerLink]="" class="dropdown-toggle nav-link" ngbDropdownToggle id="dropdown-flag">
<i class="flag-icon flag-icon-gb"></i><span class="selected-language"></span>
</a>
@ -50,8 +52,8 @@
<i class="flag-icon flag-icon-de"></i> German
</a>
</div>
</li>
<li class="dropdown-notification nav-item dropdown" ngbDropdown *ngIf ="notification === 'on'">
</li> -->
<!-- <li class="dropdown-notification nav-item dropdown" ngbDropdown *ngIf ="notification === 'on'">
<a class="nav-link nav-link-label" ngbDropdownToggle>
<i class="ficon feather ft-bell"></i>
<span class="badge badge-pill badge-danger badge-up badge-glow">5</span>
@ -124,8 +126,8 @@
<li class="dropdown-menu-footer"><a class="dropdown-item text-muted text-center"
href="javascript:void(0)">Read all notifications</a></li>
</ul>
</li>
<li class="dropdown-notification nav-item" ngbDropdown *ngIf ="email === 'on'">
</li> -->
<!-- <li class="dropdown-notification nav-item" ngbDropdown *ngIf ="email === 'on'">
<a class="nav-link nav-link-label" ngbDropdownToggle><i class="ficon feather ft-mail"></i></a>
<ul class="dropdown-menu-media dropdown-menu-right" ngbDropdownMenu>
<li class="dropdown-menu-header">
@ -185,7 +187,7 @@
<li class="dropdown-menu-footer"><a class="dropdown-item text-muted text-center"
href="javascript:void(0)">Read all messages</a></li>
</ul>
</li>
</li> -->
<li class="dropdown-user nav-item" ngbDropdown>
<a class="nav-link dropdown-user-link" ngbDropdownToggle>

View File

@ -33,3 +33,9 @@
.header-navbar .navbar-header .navbar-brand .brand-text {
padding-left: 11px !important;
}
.brand-text-wrapper {
display: inline-block;
vertical-align: middle;
}

View File

@ -1,44 +1,96 @@
<nav class="header-navbar navbar-expand-md navbar navbar-with-menu navbar-without-dd-arrow fixed-top navbar-shadow"
[ngClass]="selectedHeaderNavBarClass">
<nav
class="header-navbar navbar-expand-md navbar navbar-with-menu navbar-without-dd-arrow fixed-top navbar-shadow"
[ngClass]="selectedHeaderNavBarClass"
>
<div class="navbar-wrapper">
<div id="navbar-header" class="navbar-header" [ngClass]="selectedNavBarHeaderClass"
(mouseenter)="mouseEnter($event)" (mouseleave)="mouseLeave($event)">
<div
id="navbar-header"
class="navbar-header"
[ngClass]="selectedNavBarHeaderClass"
(mouseenter)="mouseEnter($event)"
(mouseleave)="mouseLeave($event)"
style="background-color: #fbfbfb !important"
>
<ul class="nav navbar-nav flex-row">
<!-- Remove position relative in responsive -->
<li class="nav-item mobile-menu d-lg-none mr-auto"><a class="nav-link nav-menu-main menu-toggle hidden-xs11"
(click)="toggleNavigation($event)">
<i class="feather ft-menu font-large-1"></i></a></li>
<li class="nav-item mr-auto"><a [routerLink]="['/dashboard/sales']" class="navbar-brand"
routerLink="/dashboard/sales"><img class="brand-logo" alt="modern admin logo"
[src]="_themeSettingsConfig.brand.logo.value">
<h3 class="brand-text">{{_themeSettingsConfig.brand.brand_name}}</h3>
</a></li>
<li class="nav-item d-none d-md-block nav-toggle">
<a [routerLink]="" class="nav-link modern-nav-toggle pr-0" data-toggle="collapse"
(click)="toggleFixMenu($event)">
<i class="feather toggle-icon font-medium-3 white"
[ngClass]="{'ft-toggle-left': _themeSettingsConfig.menu === 'collapse','ft-toggle-right': _themeSettingsConfig.menu === 'expand'}"></i>
<li class="nav-item mobile-menu d-lg-none mr-auto">
<a
class="nav-link nav-menu-main menu-toggle hidden-xs11"
(click)="toggleNavigation($event)"
>
<i class="feather ft-menu font-large-1"></i
></a>
</li>
<li class="nav-item mr-auto">
<a
[routerLink]="['/monitoring']"
class="navbar-brand"
routerLink="/monitoring"
>
<img
class="brand-logo"
alt="modern admin logo"
src="../../../../assets/images/logo/smart2.png"
/>
<!-- <h3 class="brand-text" style="color: #ffffff;">{{_themeSettingsConfig.brand.brand_name}}</h3> -->
<div
class="brand-text-wrapper"
style="
max-width: 200px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
"
>
<h3 class="brand-text" style="color: #242222; margin: 0">
Smart Building
</h3>
</div>
</a>
</li>
<li class="nav-item d-lg-none"><a class="nav-link open-navbar-container" data-toggle="collapse"
data-target="#navbar-mobile" (click)="toggleNavbar($event)"><i class="la la-ellipsis-v"></i></a></li>
<li class="nav-item d-none d-md-block nav-toggle">
<a
[routerLink]=""
class="nav-link modern-nav-toggle pr-0"
data-toggle="collapse"
(click)="toggleFixMenu($event)"
>
<i
class="feather toggle-icon font-medium-3 white"
[ngClass]="{
'ft-toggle-left': _themeSettingsConfig.menu === 'collapse',
'ft-toggle-right': _themeSettingsConfig.menu === 'expand'
}"
></i>
</a>
</li>
<li class="nav-item d-lg-none">
<a
class="nav-link open-navbar-container"
data-toggle="collapse"
data-target="#navbar-mobile"
(click)="toggleNavbar($event)"
><i class="la la-ellipsis-v"></i
></a>
</li>
</ul>
</div>
<!-- New-->
<div class="navbar-container content" [hidden]="isMobile && !showNavbar">
<div class="collapse navbar-collapse" id="navbar-mobile">
<ul class="nav navbar-nav mr-auto float-left">
<li class="nav-item d-none d-md-block"><a [routerLink]="" class="nav-link nav-link-expand"
<!-- <li class="nav-item d-none d-md-block"><a [routerLink]="" class="nav-link nav-link-expand"
(click)="toggleFullScreen()" *ngIf="maximize === 'on'"><i class="ficon feather ft-maximize"></i></a></li>
<li class="nav-item nav-search"><a [routerLink]="" class="nav-link nav-link-search" (click)="clickSearch()"
*ngIf="search === 'on'"><i class="ficon feather ft-search"></i></a>
<div class="search-input" [ngClass]="{'open': isHeaderSearchOpen}">
<input class="input" type="text" placeholder="Explore Modern...">
</div>
</li>
</li> -->
</ul>
<ul class="nav navbar-nav float-right">
<li class="dropdown-language nav-item" ngbDropdown *ngIf="internationalization === 'on'">
<!-- <li class="dropdown-language nav-item" ngbDropdown *ngIf="internationalization === 'on'">
<a [routerLink]="" class="dropdown-toggle nav-link" ngbDropdownToggle id="dropdown-flag">
<i class="flag-icon flag-icon-gb"></i><span class="selected-language"></span>
</a>
@ -56,84 +108,152 @@
<i class="flag-icon flag-icon-de"></i> German
</a>
</div>
</li>
<li class="dropdown-notification nav-item dropdown" ngbDropdown *ngIf="notification === 'on'">
<a class="nav-link nav-link-label" ngbDropdownToggle>
<i class="ficon feather ft-bell"></i>
</li> -->
<li
class="dropdown-notification nav-item dropdown"
ngbDropdown
*ngIf="notification === 'on'"
>
<!-- <a class="nav-link nav-link-label" ngbDropdownToggle>
<i class="ficon feather ft-bell" style="color: #bef264"></i>
<span class="badge badge-pill badge-danger badge-up badge-glow">5</span>
</a> -->
<a class="nav-link nav-link-label">
<i class="ficon la la-bell" style="color: #242424"></i>
<!-- <span class="badge badge-pill badge-danger badge-up badge-glow">5</span> -->
</a>
<ul class="dropdown-menu-media dropdown-menu-right" ngbDropdownMenu>
<li class="dropdown-menu-header">
<h6 class="dropdown-header m-0"><span class="grey darken-2">Notifications</span></h6><span
class="notification-tag badge badge-default badge-danger float-right m-0">5 New</span>
<h6 class="dropdown-header m-0">
<span class="grey darken-2">Notifications</span>
</h6>
<span
class="notification-tag badge badge-default badge-danger float-right m-0"
>5 New</span
>
</li>
<li class="scrollable-container media-list w-100 ps-container ps-theme-dark ps-active-y" fxFlex="auto" [perfectScrollbar]="config">
<li
class="scrollable-container media-list w-100 ps-container ps-theme-dark ps-active-y"
fxFlex="auto"
[perfectScrollbar]="config"
>
<a href="javascript:void(0)">
<div class="media">
<div class="media-left align-self-center"><i
class="feather ft-plus-square icon-bg-circle bg-cyan"></i>
<div class="media-left align-self-center">
<i
class="feather ft-plus-square icon-bg-circle bg-cyan"
></i>
</div>
<div class="media-body">
<h6 class="media-heading">You have new order!</h6>
<p class="notification-text font-small-3 text-muted">Lorem ipsum dolor sit amet, consectetuer
elit.</p><small>
<time class="media-meta text-muted" datetime="2015-06-11T18:29:20+08:00">30 minutes
ago</time></small>
<p class="notification-text font-small-3 text-muted">
Lorem ipsum dolor sit amet, consectetuer elit.
</p>
<small>
<time
class="media-meta text-muted"
datetime="2015-06-11T18:29:20+08:00"
>30 minutes ago</time
></small
>
</div>
</div>
</a>
<a href="javascript:void(0)">
<div class="media">
<div class="media-left align-self-center"><i
class="feather ft-download-cloud icon-bg-circle bg-red bg-darken-1"></i></div>
<div class="media-left align-self-center">
<i
class="feather ft-download-cloud icon-bg-circle bg-red bg-darken-1"
></i>
</div>
<div class="media-body">
<h6 class="media-heading red darken-1">99% Server load</h6>
<p class="notification-text font-small-3 text-muted">Aliquam tincidunt mauris eu risus.</p><small>
<time class="media-meta text-muted" datetime="2015-06-11T18:29:20+08:00">Five hour
ago</time></small>
<h6 class="media-heading red darken-1">
99% Server load
</h6>
<p class="notification-text font-small-3 text-muted">
Aliquam tincidunt mauris eu risus.
</p>
<small>
<time
class="media-meta text-muted"
datetime="2015-06-11T18:29:20+08:00"
>Five hour ago</time
></small
>
</div>
</div>
</a>
<a href="javascript:void(0)">
<div class="media">
<div class="media-left align-self-center"><i
class="feather ft-alert-triangle icon-bg-circle bg-yellow bg-darken-3"></i></div>
<div class="media-left align-self-center">
<i
class="feather ft-alert-triangle icon-bg-circle bg-yellow bg-darken-3"
></i>
</div>
<div class="media-body">
<h6 class="media-heading yellow darken-3">Warning notifixation</h6>
<p class="notification-text font-small-3 text-muted">Vestibulum auctor dapibus neque.</p><small>
<time class="media-meta text-muted" datetime="2015-06-11T18:29:20+08:00">Today</time></small>
<h6 class="media-heading yellow darken-3">
Warning notifixation
</h6>
<p class="notification-text font-small-3 text-muted">
Vestibulum auctor dapibus neque.
</p>
<small>
<time
class="media-meta text-muted"
datetime="2015-06-11T18:29:20+08:00"
>Today</time
></small
>
</div>
</div>
</a>
<a href="javascript:void(0)">
<div class="media">
<div class="media-left align-self-center"><i
class="feather ft-check-circle icon-bg-circle bg-cyan"></i>
<div class="media-left align-self-center">
<i
class="feather ft-check-circle icon-bg-circle bg-cyan"
></i>
</div>
<div class="media-body">
<h6 class="media-heading">Complete the task</h6><small>
<time class="media-meta text-muted" datetime="2015-06-11T18:29:20+08:00">Last
week</time></small>
<h6 class="media-heading">Complete the task</h6>
<small>
<time
class="media-meta text-muted"
datetime="2015-06-11T18:29:20+08:00"
>Last week</time
></small
>
</div>
</div>
</a>
<a href="javascript:void(0)">
<div class="media">
<div class="media-left align-self-center"><i class="feather ft-file icon-bg-circle bg-teal"></i>
<div class="media-left align-self-center">
<i class="feather ft-file icon-bg-circle bg-teal"></i>
</div>
<div class="media-body">
<h6 class="media-heading">Generate monthly report</h6><small>
<time class="media-meta text-muted" datetime="2015-06-11T18:29:20+08:00">Last
month</time></small>
<h6 class="media-heading">Generate monthly report</h6>
<small>
<time
class="media-meta text-muted"
datetime="2015-06-11T18:29:20+08:00"
>Last month</time
></small
>
</div>
</div>
</a>
</li>
<li class="dropdown-menu-footer"><a class="dropdown-item text-muted text-center"
href="javascript:void(0)">Read all notifications</a></li>
<li class="dropdown-menu-footer">
<a
class="dropdown-item text-muted text-center"
href="javascript:void(0)"
>Read all notifications</a
>
</li>
</ul>
</li>
<li class="dropdown-notification nav-item" ngbDropdown>
<!-- <li class="dropdown-notification nav-item" ngbDropdown>
<a class="nav-link nav-link-label" ngbDropdownToggle *ngIf="email === 'on'"><i
class="ficon feather ft-mail"></i></a>
<ul class="dropdown-menu-media dropdown-menu-right" ngbDropdownMenu>
@ -194,28 +314,47 @@
<li class="dropdown-menu-footer"><a class="dropdown-item text-muted text-center"
href="javascript:void(0)">Read all messages</a></li>
</ul>
</li>
</li> -->
<li class="dropdown-user nav-item" ngbDropdown>
<a class="nav-link dropdown-user-link" ngbDropdownToggle>
<span *ngIf="currentUser.displayName"
class="mr-1 user-name text-bold-700">{{currentUser.displayName}}</span>
<span *ngIf="!currentUser.displayName" class="mr-1 user-name text-bold-700">John Doe</span>
<span
*ngIf="currentUser.displayName"
class="mr-1 user-name text-bold-700"
style="color: #242222"
>{{ currentUser.displayName }}</span
>
<span
*ngIf="!currentUser.displayName"
class="mr-1 user-name text-bold-700"
>John Doe</span
>
<span class="avatar avatar-online">
<img *ngIf="currentUser.photoURL" src="{{currentUser.photoURL}}" alt="avatar">
<img *ngIf="!currentUser.photoURL" src="../../../assets/images/portrait/small/avatar-s-19.png"
alt="avatar">
<img src="{{ urlImage }}" alt="avatar" />
<i></i>
</span>
</a>
<div ngbDropdownMenu class="dropdown-menu dropdown-menu-right" aria-labelledby="dropdownProfileMenu">
<a class="dropdown-item" [routerLink]="['/user/user-profile']"><i class="feather ft-user"></i> Edit
Profile </a>
<a class="dropdown-item" [routerLink]="['/email']"><i class="feather ft-mail"></i> My Inbox </a>
<a class="dropdown-item" [routerLink]="['/todos']"><i class="feather ft-check-square"></i> Task </a>
<a class="dropdown-item" [routerLink]="['/chats']"><i class="feather ft-message-square"></i> Chats </a>
<div
ngbDropdownMenu
class="dropdown-menu dropdown-menu-right"
aria-labelledby="dropdownProfileMenu"
>
<a class="dropdown-item" [routerLink]="'/user-profil'"
><i class="feather ft-user"></i> Edit Profile
</a>
<!-- <a class="dropdown-item"
><i class="feather ft-mail"></i> My Inbox
</a>
<a class="dropdown-item"
><i class="feather ft-check-square"></i> Task
</a>
<a class="dropdown-item"
><i class="feather ft-message-square"></i> Chats
</a> -->
<div class="dropdown-divider"></div>
<a class="dropdown-item" [routerLink]="" (click)="logout()"><i class="feather ft-power"></i> Logout</a>
<a class="dropdown-item" [routerLink]="" (click)="logout()"
><i class="feather ft-power"></i> Logout</a
>
</div>
</li>
</ul>

View File

@ -9,6 +9,7 @@ import { AuthService } from 'src/app/_services/auth.service';
import { Router } from '@angular/router';
import { PerfectScrollbarConfigInterface, PerfectScrollbarComponent, PerfectScrollbarDirective } from 'ngx-perfect-scrollbar';
import { AppConstants } from 'src/app/_helpers/app.constants';
import { LoginService } from 'src/app/content/hemat-app/service/login.service';
const docElmWithBrowsersFullScreenFunctions = document.documentElement as HTMLElement & {
mozRequestFullScreen(): Promise<void>;
@ -30,6 +31,8 @@ export class VerticalComponent implements OnInit, AfterViewInit {
insideTm: any;
outsideTm: any;
urlImage: string = 'https://www.w3schools.com/howto/img_avatar.png';
dataAccount: any;
private _unsubscribeAll: Subject<any>;
private _unsubscribeAllMenu: Subject<any>;
public _themeSettingsConfig: any;
@ -57,7 +60,8 @@ export class VerticalComponent implements OnInit, AfterViewInit {
private _menuSettingsService: MenuSettingsService,
public authService: AuthService,
private router: Router,
private elementRef: ElementRef
private elementRef: ElementRef,
private authServiceLogin: LoginService,
) {
this._unsubscribeAll = new Subject();
this._unsubscribeAllMenu = new Subject();
@ -82,6 +86,13 @@ export class VerticalComponent implements OnInit, AfterViewInit {
if (localStorage.getItem('currentUser')) {
this.currentUser = JSON.parse(localStorage.getItem('currentUser'));
}
if (localStorage.getItem('account_info')) {
this.dataAccount = JSON.parse(localStorage.getItem('account_info'));
this.dataProfil(this.dataAccount.sub);
}
// Subscribe to config changes
this._themeSettingsService.config
.pipe(takeUntil(this._unsubscribeAll))
@ -101,6 +112,14 @@ export class VerticalComponent implements OnInit, AfterViewInit {
this.email = this._themeSettingsConfig.headerIcons.email;
}
dataProfil(userId){
this.authServiceLogin.getDataProfil(userId).subscribe(data => {
if (data.data.image_path !== "https://kapi.absys.ninja/hemat/image/null") {
this.urlImage = data.data.image_path
}
});
}
ngAfterViewInit(): void {
this.refreshView();
}

View File

@ -0,0 +1,23 @@
.menu-item {
position: relative;
}
.menu-item.active::before {
content: '';
position: absolute;
left: 0;
top: 0;
height: 100%;
width: 5px;
background-color: green;
}
.active-link::before {
content: '';
position: absolute;
left: 0;
top: 0;
bottom: 0;
width: 4px;
background-color: green;
}

View File

@ -1,8 +1,8 @@
<div (mouseenter)="mouseEnter($event)" (mouseleave)="mouseLeave($event)" id="main-menu"
class="main-menu menu-fixed menu-dark menu-accordion menu-shadow" data-scroll-to-active="true">
class="main-menu menu-fixed menu-dark menu-accordion menu-shadow" data-scroll-to-active="true" style="background-color: #fbfbfb !important; box-shadow: none !important; border-right: 3px solid #f4f4f4;">
<div id="main-menu-content" class="main-menu-content ps-container ps-theme-light" fxFlex="auto"
[perfectScrollbar]="config">
<ul class="navigation navigation-main" id="main-menu-navigation" data-menu="menu-navigation">
[perfectScrollbar]="config" >
<ul class="navigation navigation-main" id="main-menu-navigation" data-menu="menu-navigation" style="background-color: #fbfbfb !important;">
<!-- Menu -->
{{child?child.title:''}}
<li *ngFor="let child of _menuSettingsConfig.vertical_menu.items" class="" [ngClass]="{
@ -13,22 +13,24 @@
'active': child.isSelected && !child.submenu,
'menu-collapsed-open': child.isSelected && child.submenu,
'hover': child.hover
}">
}" >
<!-- Section -->
<span class="menu-title" *ngIf="child.section">{{child.section}}</span>
<i class="la" *ngIf="child.section" [ngClass]="child.icon" data-toggle="tooltip" data-placement="right"
data-original-title="Support"></i>
data-original-title="Support" style="color: #242222;"></i>
<!-- Root Menu -->
<a *ngIf="child.title && !child.submenu && !child.excludeInVertical && !child.isExternalLink && !child.issupportExternalLink && !child.isStarterkitExternalLink"
routerLink="{{child.page !== 'null'?child.page:router.url}}" (click)="toggleMenu($event, child)">
<i class="la" [ngClass]="child.icon"></i>
<span class="menu-title" data-i18n="">{{child.title}}</span>
routerLink="{{child.page !== 'null'?child.page:router.url}}" (click)="toggleMenu($event, child)"
style="background-color: #fbfbfb !important;">
<i class="la" [ngClass]="child.icon" style="color: #242222;"></i>
<span class="menu-title" data-i18n="" style="color: #242222;">{{child.title}}</span>
<span *ngIf="child.badge" class="badge badge-pill float-right"
[ngClass]="{'badge-info mr-2': child.badge.type==='badge-info' , 'badge-danger':child.badge.type==='badge-danger'}">
[ngClass]="{'badge-info mr-2': child.badge.type==='badge-info' , 'badge-danger':child.badge.type==='badge-danger'}" style="color: #242222;">
{{child.badge.value}}
</span>
</a>
<a *ngIf="child.title && !child.submenu && !child.excludeInVertical && child.isExternalLink"
[href]="child.page" target="_blank" (click)="toggleMenu($event, child)">
<i class="la" [ngClass]="child.icon"></i>
@ -51,20 +53,23 @@
<!-- Submenu -->
<a *ngIf="child.title && child.submenu && !child.excludeInVertical"
routerLink="{{child.page !== 'null'?child.page:router.url}}" (click)="toggleMenu($event, child)">
<i class="la" [ngClass]="child.icon"></i>
<span class="menu-title" data-i18n="">{{child.title}}</span>
routerLink="{{child.page !== 'null'?child.page:router.url}}" (click)="toggleMenu($event, child)" style="background-color: #fbfbfb !important;">
<i class="la" [ngClass]="child.icon" style="color: #242222;"></i>
<span class="menu-title" data-i18n="" style="color: #242222;">{{child.title}}</span>
<span *ngIf="child.badge" class="badge badge-pill float-right"
[ngClass]="{'badge-info mr-2': child.badge.type==='badge-info' , 'badge-danger':child.badge.type==='badge-danger'}">
[ngClass]="{'badge-info mr-2': child.badge.type==='badge-info' , 'badge-danger':child.badge.type==='badge-danger'}" style="color: #242222;">
{{child.badge.value}}
</span>
</a>
<ul *ngIf="child.submenu" class="menu-content" [@popOverState]="child.isOpen">
<ul *ngIf="child.submenu" class="menu-content" [@popOverState]="child.isOpen" style="background-color: #fbfbfb !important;">
<!-- Submenu of Submenu -->
<li *ngFor="let subchild of child.submenu.items" class="isShown"
[ngClass]="{'has-sub':(subchild.submenu),'active': subchild.isSelected && !subchild.submenu, 'open': subchild.isOpen && subchild.submenu}">
<a class="menu-item" *ngIf="!subchild.submenu && !subchild.excludeInVertical" (click)="toggleMenu($event, subchild, true)"
routerLink="{{subchild.page !== 'null'?subchild.page:router.url}}">{{subchild.title}}</a>
<a class="menu-item" *ngIf="!subchild.submenu && !subchild.excludeInVertical"
(click)="toggleMenu($event, subchild, true)"
routerLink="{{subchild.page !== 'null'?subchild.page:router.url}}"
[ngClass]="{'active': subchild.isSelected}"
style="background-color: #fbfbfb !important; color: #242222;">{{subchild.title}}</a>
<a class="menu-item" *ngIf="subchild.submenu && !subchild.excludeInVertical" (click)="toggleMenu($event, subchild, true)"
routerLink="{{subchild.page !== 'null'?subchild.page:router.url}}">{{subchild.title}}</a>
<ul *ngIf="subchild.submenu && !subchild.excludeInVertical" class="menu-content">

View File

@ -384,6 +384,7 @@ export class VerticalnavComponent implements OnInit {
} else if ( child.page === '/chats' && this.loggedInUser.email !== 'john@pixinvent.com') {
this.router.navigate(['/chats']);
}
}
}

View File

@ -1,14 +1,20 @@
<div (window:resize)="onResize($event)"></div>
<app-navigation></app-navigation>
<router-outlet></router-outlet>
<div class="sidenav-overlay d-none" id="sidenav-overlay" (click)="rightbar($event)"></div>
<div
class="sidenav-overlay d-none"
id="sidenav-overlay"
(click)="rightbar($event)"
></div>
<app-footer></app-footer>
<div *ngIf ="customizer === 'on'">
<app-customizer *ngIf="layout === 'vertical'"></app-customizer>
<app-horizontal-customizer *ngIf="layout === 'horizontal'"></app-horizontal-customizer>
</div>
<div *ngIf ="buybutton === 'on'">
<div class="buy-now" >
<!-- <div *ngIf="customizer === 'on'">
<app-customizer *ngIf="layout === 'vertical'"></app-customizer>
<app-horizontal-customizer
*ngIf="layout === 'horizontal'"
></app-horizontal-customizer>
</div> -->
<!-- <div *ngIf="buybutton === 'on'">
<div class="buy-now" >
<a href="https://1.envato.market/modern_admin_angular" target="_blank" class="btn bg-gradient-directional-purple round white btn-purple btn-glow px-2">Buy Now</a>
</div>
</div>
</div> -->

File diff suppressed because it is too large Load Diff

View File

@ -29,5 +29,5 @@ export const ThemeSettingsConfig = {
// value:'http://evolvision.com/wp-content/uploads/2018/01/envelope4-green.png'
},
},
defaultTitleSuffix: 'Modern Admin - Angular 11+ Bootstrap 5 Admin Dashboard Template'
defaultTitleSuffix: ''
};

View File

@ -107,11 +107,17 @@ export class AuthService {
doLogout() {
return new Promise<void>((resolve, reject) => {
if (firebase.auth().currentUser) {
console.log('masuk atas');
localStorage.removeItem('currentUser');
localStorage.removeItem('account_info');
localStorage.removeItem('access_token');
localStorage.removeItem('remember');
this.afAuth.signOut();
resolve();
} else {
console.log('masuk bawah');
localStorage.removeItem('account_info');
localStorage.removeItem('access_token');
localStorage.removeItem('currentUser');
resolve();
}

View File

@ -53,6 +53,8 @@ export class TableApiService {
}
private extractData(res: Response) {
console.log(res);
const body = res;
return body || {};
}

View File

@ -1,37 +1,280 @@
import { Injectable } from '@angular/core';
import * as FileSaver from 'file-saver';
import * as XLSX from 'xlsx';
import { Injectable } from "@angular/core";
import * as FileSaver from "file-saver";
import * as XLSX from "xlsx";
const EXCEL_TYPE =
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8';
const EXCEL_EXTENSION = '.xlsx';
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8";
const EXCEL_EXTENSION = ".xlsx";
@Injectable({
providedIn: 'root'
providedIn: "root",
})
export class TableexcelService {
constructor() { }
constructor() {}
public exportAsExcelFile(json: any[], excelFileName: string): void {
const worksheet: XLSX.WorkSheet = XLSX.utils.json_to_sheet(json);
console.log('worksheet', worksheet);
console.log("worksheet", worksheet);
const workbook: XLSX.WorkBook = {
Sheets: { data: worksheet },
SheetNames: ['data']
SheetNames: ["data"],
};
const excelBuffer: any = XLSX.write(workbook, {
bookType: 'xlsx',
type: 'array'
bookType: "xlsx",
type: "array",
});
this.saveAsExcelFile(excelBuffer, excelFileName);
}
private saveAsExcelFile(buffer: any, fileName: string): void {
const data: Blob = new Blob([buffer], {
type: EXCEL_TYPE
type: EXCEL_TYPE,
});
FileSaver.saveAs(
data,
fileName + '_export_' + new Date().getTime() + EXCEL_EXTENSION
fileName + "_export_" + new Date().getTime() + EXCEL_EXTENSION
);
}
public exportAsExcelFileDevice(
json: any[],
excelFileName: string,
columns: string[]
): void {
const filteredJson = json.map((item) => {
const filteredItem = {};
columns.forEach((column) => {
filteredItem[column] = item[column];
});
return filteredItem;
});
const worksheet: XLSX.WorkSheet = XLSX.utils.json_to_sheet(filteredJson);
const columnWidths = [
{ wch: 40 },
{ wch: 30 },
{ wch: 30 },
{ wch: 10 },
{ wch: 20 },
{ wch: 20 },
{ wch: 20 },
{ wch: 20 },
];
worksheet["!cols"] = columnWidths;
const header = [
"Device",
"Building",
"Room",
"Watt",
"Category",
"Status",
"Type",
"Voltage",
];
XLSX.utils.sheet_add_aoa(worksheet, [header]);
const workbook: XLSX.WorkBook = {
Sheets: { data: worksheet },
SheetNames: ["data"],
};
const excelBuffer: any = XLSX.write(workbook, {
bookType: "xlsx",
type: "array",
});
this.saveAsExcelFile(excelBuffer, excelFileName);
}
public exportAsExcelFileCostManage(
json: any[],
excelFileName: string,
columns: string[]
): void {
// Filter the json data based on the columns
const filteredJson = json.map((item) => {
const filteredItem = {};
columns.forEach((column) => {
filteredItem[column] = item[column];
});
return filteredItem;
});
// Calculate totals for estimation_cost and total_use
const totalEstimationCost = filteredJson.reduce(
(sum, item) => sum + (item["estimation_cost"] || 0),
0
);
const totalUse = filteredJson.reduce(
(sum, item) => sum + (item["total_use"] || 0),
0
);
// Create the worksheet
const worksheet: XLSX.WorkSheet = XLSX.utils.json_to_sheet(filteredJson);
const columnWidths = [
{ wch: 40 },
{ wch: 30 },
{ wch: 20 },
{ wch: 30 },
{ wch: 30 },
{ wch: 30 },
];
worksheet["!cols"] = columnWidths;
// Add header
const header = [
"Building",
"Room",
"Category",
"Total Use",
"Estimation Cost",
"Date",
];
XLSX.utils.sheet_add_aoa(worksheet, [header]);
// Add totals row
const totalsRow = [
"Totals",
"",
"",
`Total: ${totalUse} Kwh`,
`Total: ${totalEstimationCost}`,
"",
];
XLSX.utils.sheet_add_aoa(worksheet, [totalsRow], { origin: -1 });
// Create the workbook
const workbook: XLSX.WorkBook = {
Sheets: { data: worksheet },
SheetNames: ["data"],
};
// Write the workbook and save it
const excelBuffer: any = XLSX.write(workbook, {
bookType: "xlsx",
type: "array",
});
this.saveAsExcelFile(excelBuffer, excelFileName);
}
public exportAsExcelFileManageDetail(
json: any[],
excelFileName: string,
columns: string[]
): void {
const filteredJson = json.map((item) => {
const filteredItem = {};
columns.forEach((column) => {
filteredItem[column] = item[column];
});
return filteredItem;
});
const worksheet: XLSX.WorkSheet = XLSX.utils.json_to_sheet(filteredJson);
const columnWidths = [
{ wch: 30 },
{ wch: 40 },
{ wch: 30 },
{ wch: 30 },
{ wch: 20 },
{ wch: 20 },
{ wch: 20 },
{ wch: 20 },
{ wch: 20 },
];
worksheet["!cols"] = columnWidths;
const header = [
"Periode",
"Device",
"Room",
"Category",
"Estimation Cost",
"Total Kwh",
"Watt",
"Duration",
"Price Kwh",
];
XLSX.utils.sheet_add_aoa(worksheet, [header]);
const workbook: XLSX.WorkBook = {
Sheets: { data: worksheet },
SheetNames: ["data"],
};
const excelBuffer: any = XLSX.write(workbook, {
bookType: "xlsx",
type: "array",
});
this.saveAsExcelFile(excelBuffer, excelFileName);
}
public exportAsExcelFileMonitoringDetail(
json: any[],
excelFileName: string,
columns: string[],
mode: any
): void {
const filteredJson = json.map((item) => {
const filteredItem = {};
columns.forEach((column) => {
filteredItem[column] = item[column];
});
return filteredItem;
});
const worksheet: XLSX.WorkSheet = XLSX.utils.json_to_sheet(filteredJson);
let columnWidths = [];
if (mode === "building") {
columnWidths = [
{ wch: 30 },
{ wch: 40 },
{ wch: 30 },
{ wch: 30 },
{ wch: 30 },
{ wch: 30 },
];
} else if (mode === "floor") {
columnWidths = [
{ wch: 40 },
{ wch: 30 },
{ wch: 30 },
{ wch: 30 },
{ wch: 30 },
];
} else if (mode === "room") {
columnWidths = [{ wch: 30 }, { wch: 30 }, { wch: 30 }, { wch: 30 }];
}
worksheet["!cols"] = columnWidths;
let header = [];
if (mode === "building") {
header = [
"Floor",
"Room",
"Device",
"Duration",
"Price Kwh",
"Estimation Cost",
];
} else if (mode === "floor") {
header = ["Room", "Device", "Duration", "Price Kwh", "Estimation Cost"];
} else if (mode === "room") {
header = ["Device", "Duration", "Price Kwh", "Estimation Cost"];
}
XLSX.utils.sheet_add_aoa(worksheet, [header]);
const workbook: XLSX.WorkBook = {
Sheets: { data: worksheet },
SheetNames: ["data"],
};
const excelBuffer: any = XLSX.write(workbook, {
bookType: "xlsx",
type: "array",
});
this.saveAsExcelFile(excelBuffer, excelFileName);
}
}

View File

@ -74,7 +74,8 @@ export class AppComponent implements OnInit {
}
}
if (this.title && this.router.url !== '/') {
this.titleService.setTitle(this.title + ' - ' + this._themeSettingsConfig.defaultTitleSuffix);
this.titleService.setTitle(this.title);
// this.titleService.setTitle(this.title + ' - ' + this._themeSettingsConfig.defaultTitleSuffix);
} else {
if ((this.router.url === '/' || this.router.url === '/login' || this.router.url === '/register') &&
!localStorage.getItem('remember')) {

View File

@ -5,7 +5,7 @@ import {
HammerGestureConfig
} from '@angular/platform-browser';
import { ReactiveFormsModule } from '@angular/forms';
import { HttpClientModule } from '@angular/common/http';
import { HTTP_INTERCEPTORS, HttpClientModule } from '@angular/common/http';
import {
NgbModule,
NgbCarouselConfig,
@ -67,7 +67,8 @@ import { FullLayoutComponent } from './_layout/full-layout/full-layout.component
import { ToastrModule } from 'ngx-toastr';
import { UserService } from './_api/user/user.service';
import { PrivacyPolicyComponent } from './login/privacy-policy/privacy-policy.component';
import { TermsConditionComponent } from './login/terms-condition/terms-condition.component';;
import { TermsConditionComponent } from './login/terms-condition/terms-condition.component';
import { HttpErrorInterceptorService } from './interceptors/http-error-interceptor.service';
@NgModule({
imports: [
@ -93,7 +94,7 @@ import { TermsConditionComponent } from './login/terms-condition/terms-condition
LoadingBarRouterModule,
BlockUIModule.forRoot({
template: BlockTemplateComponent
})
}),
],
declarations: [
AppComponent,
@ -117,7 +118,7 @@ import { TermsConditionComponent } from './login/terms-condition/terms-condition
BlockTemplateComponent,
FullLayoutComponent,
PrivacyPolicyComponent,
TermsConditionComponent,
TermsConditionComponent
],
providers: [
AuthGuard,
@ -136,6 +137,7 @@ import { TermsConditionComponent } from './login/terms-condition/terms-condition
},
NgbCarouselConfig,
NgbModalConfig,
{ provide: HTTP_INTERCEPTORS, useClass: HttpErrorInterceptorService, multi: true }
],
bootstrap: [AppComponent],
exports: [RouterModule]

View File

@ -48,6 +48,38 @@ const appRoutes: Routes = [
children: [
{ path: 'logout', component: LoginComponent, canActivate: [AuthGuard] },
{ path: 'changelog', component: ChangelogComponent, canActivate: [AuthGuard] },
{
path: 'monitoring', loadChildren: () => import('../app/content/hemat-app/monitoring/monitoring.module').then(m => m.MonitoringModule)
, canActivate: [AuthGuard]
},
{
path: 'calculator', loadChildren: () => import('../app/content/hemat-app/calculator/calculator.module').then(m => m.CalculatorModule)
, canActivate: [AuthGuard]
},
{
path: 'list-monitoring', loadChildren: () => import('../app/content/hemat-app/list-monitoring/list-monitoring.module').then(m => m.ListMonitoringModule)
, canActivate: [AuthGuard]
},
{
path: 'device', loadChildren: () => import('../app/content/hemat-app/device/device.module').then(m => m.DeviceModule)
, canActivate: [AuthGuard]
},
{
path: 'cost-management', loadChildren: () => import('../app/content/hemat-app/cost-management/cost-management.module').then(m => m.CostManagementModule)
, canActivate: [AuthGuard]
},
{
path: 'user-access', loadChildren: () => import('../app/content/hemat-app/user-access/user-access.module').then(m => m.UserAccessModule)
, canActivate: [AuthGuard]
},
{
path: 'master', loadChildren: () => import('../app/content/hemat-app/master/master.module').then(m => m.MasterModule)
, canActivate: [AuthGuard]
},
{
path: 'user-profil', loadChildren: () => import('../app/content/hemat-app/user-profile/user-profile.module').then(m => m.UserProfileModule)
, canActivate: [AuthGuard]
},
{
path: 'dashboard', loadChildren: () => import('../app/content/dashboard/dashboard.module').then(m => m.DashboardModule)
, canActivate: [AuthGuard]

View File

@ -1,196 +1,241 @@
<div class="app-content content">
<div class="content-overlay"></div>
<div class="content-wrapper">
<div class="content-header row">
</div>
<div class="content-body"><!-- Hospital Info cards -->
<div class="row">
<div class="col-xl-3 col-lg-6 col-md-6 col-12">
<div class="card pull-up">
<div class="card-content">
<div class="card-body">
<div class="media d-flex">
<div class="align-self-center">
<i class="la la-user-md font-large-2 success"></i>
</div>
<div class="media-body text-right">
<h5 class="text-muted text-bold-500">Doctors Available</h5>
<h3 class="text-bold-600">122</h3>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-xl-3 col-lg-6 col-md-6 col-12">
<div class="card pull-up">
<div class="card-content">
<div class="card-body">
<div class="media d-flex">
<div class="align-self-center">
<i class="la la-stethoscope font-large-2 warning"></i>
</div>
<div class="media-body text-right">
<h5 class="text-muted text-bold-500">Visiting Doctors</h5>
<h3 class="text-bold-600">34</h3>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-xl-3 col-lg-6 col-md-6 col-12">
<div class="card pull-up">
<div class="card-content">
<div class="card-body">
<div class="media d-flex">
<div class="align-self-center">
<i class="la la-calendar-check-o font-large-2 info"></i>
</div>
<div class="media-body text-right">
<h5 class="text-muted text-bold-500">Today's Inquiry</h5>
<h3 class="text-bold-600">3.5K</h3>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-xl-3 col-lg-6 col-md-6 col-12">
<div class="card pull-up">
<div class="card-content">
<div class="card-body">
<div class="media d-flex">
<div class="align-self-center">
<i class="la la-bed font-large-2 danger"></i>
</div>
<div class="media-body text-right">
<h5 class="text-muted text-bold-500">Rooms Available</h5>
<h3 class="text-bold-600">179</h3>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Hospital Info cards Ends -->
<!-- Appointment Bar Line Chart -->
<div class="row">
<div class="col-12">
<div class="card">
<section id="chartjs-bar-charts">
<div class="content-overlay"></div>
<div class="content-wrapper">
<div class="content-header row"></div>
<div class="content-body">
<!-- Hospital Info cards -->
<div class="row">
<div class="col-12" *blockUI="'barCharts'; message: 'Loading'">
<m-card [options]="options" (reloadFunction)="reloadBarCharts($event)">
<ng-container mCardHeaderTitle>
Appointment
</ng-container>
<ng-container mCardBody>
<div class="z">
<canvas class="barchart" height="328" baseChart [datasets]="barChartData" [labels]="barChartLabels"
[options]="barChartOptions" [legend]="barChartLegend"
[chartType]="barChartType"></canvas>
<div class="col-xl-3 col-lg-6 col-md-6 col-12">
<div class="card pull-up">
<div class="card-content">
<div class="card-body">
<div class="media d-flex">
<div class="align-self-center">
<i class="la la-user-md font-large-2 success"></i>
</div>
<div class="media-body text-right">
<h5 class="text-muted text-bold-500">Doctors Available</h5>
<h3 class="text-bold-600">122</h3>
</div>
</div>
</div>
</ng-container>
</m-card>
</div>
</div>
</div>
<div class="col-xl-3 col-lg-6 col-md-6 col-12">
<div class="card pull-up">
<div class="card-content">
<div class="card-body">
<div class="media d-flex">
<div class="align-self-center">
<i class="la la-stethoscope font-large-2 warning"></i>
</div>
<div class="media-body text-right">
<h5 class="text-muted text-bold-500">Visiting Doctors</h5>
<h3 class="text-bold-600">34</h3>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-xl-3 col-lg-6 col-md-6 col-12">
<div class="card pull-up">
<div class="card-content">
<div class="card-body">
<div class="media d-flex">
<div class="align-self-center">
<i class="la la-calendar-check-o font-large-2 info"></i>
</div>
<div class="media-body text-right">
<h5 class="text-muted text-bold-500">Today's Inquiry</h5>
<h3 class="text-bold-600">3.5K</h3>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-xl-3 col-lg-6 col-md-6 col-12">
<div class="card pull-up">
<div class="card-content">
<div class="card-body">
<div class="media d-flex">
<div class="align-self-center">
<i class="la la-bed font-large-2 danger"></i>
</div>
<div class="media-body text-right">
<h5 class="text-muted text-bold-500">Rooms Available</h5>
<h3 class="text-bold-600">179</h3>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
</div>
</div>
</div>
<!-- Appointment Bar Line Chart Ends -->
<!-- Hospital Info cards Ends -->
<!-- Appointment Table -->
<div class="row" matchHeight="card">
<div class="col-12 col-md-4">
<div class="card">
<div class="card-header">
<h4 class="card-title">Doctors Available</h4>
</div>
<div class="card-content">
<div class="table-responsive">
<table id="recent-orders" class="table table-hover table-xl mb-0">
<tbody>
<tr *ngFor = "let doctor of doctors ">
<td class="text-truncate p-1 border-top-0">
<div class="avatar avatar-md">
<img class="media-object rounded-circle" [src]= doctor.image
alt="Avatar">
<!-- Appointment Bar Line Chart -->
<div class="row">
<div class="col-12">
<div class="card">
<section id="chartjs-bar-charts">
<div class="row">
<div class="col-12" *blockUI="'barCharts'; message: 'Loading'">
<m-card
[options]="options"
(reloadFunction)="reloadBarCharts($event)"
>
<ng-container mCardHeaderTitle> Appointment </ng-container>
<ng-container mCardBody>
<div class="z">
<canvas
class="barchart"
height="328"
baseChart
[datasets]="barChartData"
[labels]="barChartLabels"
[options]="barChartOptions"
[legend]="barChartLegend"
[chartType]="barChartType"
></canvas>
</div>
</ng-container>
</m-card>
</div>
</div>
</td>
<td class="text-truncate pl-0 border-top-0">
<div class="name">{{doctor.name}}</div>
<div class="designation text-light font-small-2">Dentist</div>
</td>
<td class="text-right border-top-0">
<a href="hospital-book-appointment.html" class="btn btn-sm btn-outline-success">Book Appointment</a>
</td>
</tr>
</tbody>
</table>
</section>
</div>
</div>
</div>
<!-- Appointment Bar Line Chart Ends -->
<!-- Appointment Table -->
<div class="row" matchHeight="card">
<div class="col-12 col-md-4">
<div class="card">
<div class="card-header">
<h4 class="card-title">Doctors Available</h4>
</div>
<div class="card-content">
<div class="table-responsive">
<table
id="recent-orders"
class="table table-hover table-xl mb-0"
>
<tbody>
<tr *ngFor="let doctor of doctors">
<td class="text-truncate p-1 border-top-0">
<div class="avatar avatar-md">
<img
class="media-object rounded-circle"
[src]="doctor.image"
alt="Avatar"
/>
</div>
</td>
<td class="text-truncate pl-0 border-top-0">
<div class="name">{{ doctor.name }}</div>
<div class="designation text-light font-small-2">
Dentist
</div>
</td>
<td class="text-right border-top-0">
<a
href="hospital-book-appointment.html"
class="btn btn-sm btn-outline-success"
>Book Appointment</a
>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
<div id="recent-appointments" class="col-12 col-md-8">
<div class="card">
<div class="card-header">
<h4 class="card-title">Recent Appointments</h4>
<a class="heading-elements-toggle"
><i class="la la-ellipsis-v font-medium-3"></i
></a>
<div class="heading-elements">
<ul class="list-inline mb-0">
<li>
<a
class="btn btn-sm btn-danger box-shadow-2 round btn-min-width pull-right"
href="hospital-book-appointment.html"
target="_blank"
>View all</a
>
</li>
</ul>
</div>
</div>
<div class="card-content mt-1">
<div class="table-responsive">
<table
id="recent-orders-doctors"
class="table table-hover table-xl mb-0"
>
<thead>
<tr>
<th class="border-top-0">Doctor</th>
<th class="border-top-0">Patients</th>
<th class="border-top-0">Specialities</th>
<th class="border-top-0">Timings</th>
<th class="border-top-0">Amount</th>
</tr>
</thead>
<tbody>
<tr class="pull-up" *ngFor="let doctor of doctorList">
<td class="text-truncate">{{ doctor.name }}</td>
<td class="text-truncate p-1">
<ul class="list-unstyled users-list m-0">
<li
data-toggle="tooltip"
data-popup="tooltip-custom"
data-original-title="Kimberly Simmons"
class="avatar avatar-sm pull-up"
*ngFor="let imageUrl of doctor.image"
>
<img
class="media-object rounded-circle"
[src]="imageUrl"
alt="Avatar"
/>
</li>
<li class="avatar avatar-sm">
<span class="badge badge-info">{{
doctor.bagde
}}</span>
</li>
</ul>
</td>
<td>
<button
type="button"
class="btn btn-sm btn-outline-{{ doctor.type }} round"
>
{{ doctor.designation }}
</button>
</td>
<td class="text-truncate">{{ doctor.time }}</td>
<td class="text-truncate">{{ doctor.amount }}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<!-- Appointment Table Ends -->
</div>
</div>
</div>
<div id="recent-appointments" class="col-12 col-md-8">
<div class="card">
<div class="card-header">
<h4 class="card-title">Recent Appointments</h4>
<a class="heading-elements-toggle"><i class="la la-ellipsis-v font-medium-3"></i></a>
<div class="heading-elements">
<ul class="list-inline mb-0">
<li><a class="btn btn-sm btn-danger box-shadow-2 round btn-min-width pull-right" href="hospital-book-appointment.html"
target="_blank">View all</a></li>
</ul>
</div>
</div>
<div class="card-content mt-1">
<div class="table-responsive">
<table id="recent-orders-doctors" class="table table-hover table-xl mb-0">
<thead>
<tr>
<th class="border-top-0">Doctor</th>
<th class="border-top-0">Patients</th>
<th class="border-top-0">Specialities</th>
<th class="border-top-0">Timings</th>
<th class="border-top-0">Amount</th>
</tr>
</thead>
<tbody>
<tr class="pull-up" *ngFor="let doctor of doctorList">
<td class="text-truncate">{{doctor.name}}</td>
<td class="text-truncate p-1">
<ul class="list-unstyled users-list m-0">
<li data-toggle="tooltip" data-popup="tooltip-custom" data-original-title="Kimberly Simmons" class="avatar avatar-sm pull-up" *ngFor="let imageUrl of doctor.image">
<img class="media-object rounded-circle" [src]="imageUrl" alt="Avatar">
</li>
<li class="avatar avatar-sm">
<span class="badge badge-info">{{doctor.bagde}}</span>
</li>
</ul>
</td>
<td>
<button type="button" class="btn btn-sm btn-outline-{{doctor.type}} round">{{doctor.designation}}</button>
</td>
<td class="text-truncate">{{doctor.time}}</td>
<td class="text-truncate">{{doctor.amount}}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<!-- Appointment Table Ends -->
</div>
</div>
</div>
<!-- END: Content-->
<!-- END: Content-->

View File

@ -52,7 +52,7 @@
}
:host ::ng-deep .donut-chart2 .ct-series-b .ct-slice-donut {
stroke: #ff4961;
stroke: #7949ff;
stroke-width: 5.5px !important;
}
:host ::ng-deep .donut-chart2 {

View File

@ -24,7 +24,7 @@
</fieldset>
<div class="row py-2">
<div class="col-12 col-sm-6 col-md-6 mb-1">
<a [routerLink]="['/dashboard/sales']" class="btn btn-primary btn-block"><i class="feather ft-home"></i>
<a [routerLink]="['/monitoring']" class="btn btn-primary btn-block"><i class="feather ft-home"></i>
Home</a>
</div>
<div class="col-12 col-sm-6 col-md-6 mb-1">

View File

@ -14,7 +14,7 @@
<div class="card border-grey border-lighten-3 px-1 py-1 box-shadow-3 m-0">
<div class="card-body">
<span class="card-title text-center">
<img src="../../../../../assets/images/logo/logo-dark-lg.png" class="img-fluid mx-auto d-block pt-2"
<img src="../../../../../assets/images/logo/smart.png" class="img-fluid mx-auto d-block pt-2"
width="250" alt="logo">
</span>
</div>

View File

@ -7,7 +7,7 @@
<div class="col-12 d-flex align-items-center justify-content-center">
<div class="col-lg-6 col-10">
<img class="img-fluid mx-auto d-block pb-3 pt-4 width-65-per"
src="../../../../../assets/images/logo/logo-dark-lg.png" alt="Modern Search">
src="../../../../../assets/images/logo/smart.png" alt="Modern Search">
<fieldset class="form-group position-relative">
<input type="text" class="form-control form-control-xl input-xl" id="iconLeft1"
placeholder="Explore Modern ...">

View File

@ -0,0 +1 @@
<app-coming-soon></app-coming-soon>

View File

@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { CalculationComponent } from './calculation.component';
describe('CalculationComponent', () => {
let component: CalculationComponent;
let fixture: ComponentFixture<CalculationComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ CalculationComponent ]
})
.compileComponents();
fixture = TestBed.createComponent(CalculationComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,10 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-calculation',
templateUrl: './calculation.component.html',
styleUrls: ['./calculation.component.css']
})
export class CalculationComponent {
}

View File

@ -0,0 +1 @@
<p>calculator works!</p>

View File

@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { CalculatorComponent } from './calculator.component';
describe('CalculatorComponent', () => {
let component: CalculatorComponent;
let fixture: ComponentFixture<CalculatorComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ CalculatorComponent ]
})
.compileComponents();
fixture = TestBed.createComponent(CalculatorComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,10 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-calculator',
templateUrl: './calculator.component.html',
styleUrls: ['./calculator.component.css']
})
export class CalculatorComponent {
}

View File

@ -0,0 +1,50 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { CalculationComponent } from './calculation/calculation.component';
import { ReductionComponent } from './reduction/reduction.component';
import { CardModule } from '../../partials/general/card/card.module';
import { BreadcrumbModule } from 'src/app/_layout/breadcrumb/breadcrumb.module';
import { NgSelectModule } from '@ng-select/ng-select';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { ClipboardModule } from 'ngx-clipboard';
import { PerfectScrollbarModule } from 'ngx-perfect-scrollbar';
import { NgxDatatableModule } from '@swimlane/ngx-datatable';
import { BlockUIModule } from 'ng-block-ui';
import { BlockTemplateComponent } from 'src/app/_layout/blockui/block-template.component';
import { RouterModule } from '@angular/router';
import { ComingSoonComponent } from '../coming-soon/coming-soon.component';
@NgModule({
declarations: [
CalculationComponent,
ReductionComponent,
ComingSoonComponent
],
imports: [
CommonModule,
CardModule,
BreadcrumbModule,
NgSelectModule,
FormsModule,
ReactiveFormsModule,
ClipboardModule,
PerfectScrollbarModule,
NgxDatatableModule,
BlockUIModule.forRoot({
template: BlockTemplateComponent
}),
RouterModule.forChild([
{
path: 'calculation',
component: CalculationComponent
},
{
path: 'reduction',
component: ReductionComponent
},
])
]
})
export class CalculatorModule { }

View File

@ -0,0 +1 @@
<app-coming-soon></app-coming-soon>

View File

@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ReductionComponent } from './reduction.component';
describe('ReductionComponent', () => {
let component: ReductionComponent;
let fixture: ComponentFixture<ReductionComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ ReductionComponent ]
})
.compileComponents();
fixture = TestBed.createComponent(ReductionComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,10 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-reduction',
templateUrl: './reduction.component.html',
styleUrls: ['./reduction.component.css']
})
export class ReductionComponent {
}

View File

@ -0,0 +1,43 @@
.bg-maintenance-image {
background-size: cover;
display: flex;
justify-content: center;
align-items: center;
}
.centered-card {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
}
.box-shadow-2 {
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}
.box-shadow-3 {
box-shadow: 0 3px 20px rgba(0, 0, 0, 0.2);
}
.card {
background-color: rgba(255, 255, 255, 0.5);
border: 1px solid rgba(255, 255, 255, 0.3);
padding: 20px;
max-width: 400px;
width: 100%;
}
.spinner {
animation: spin 1s linear infinite;
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}

View File

@ -0,0 +1,33 @@
<div class="app-content content bg-maintenance-image">
<div class="content-wrapper">
<div class="content-body">
<section class="flexbox-container">
<div class="centered-card">
<div class="card box-shadow-3">
<div class="card-body">
<span class="card-title text-center">
<img
src="assets/images/logo/smart.png"
class="img-fluid mx-auto d-block pt-2"
width="250"
alt="logo"
/>
</span>
</div>
<div class="card-body text-center">
<h3 style="font-weight: 600; color: #000000">Coming Soon</h3>
<p style="color: #000000">
We're working hard to bring you this page. <br />
Please check back later.
</p>
<div class="mt-2">
<i class="la la-cog spinner font-large-2"></i>
</div>
</div>
<hr />
</div>
</div>
</section>
</div>
</div>
</div>

View File

@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ComingSoonComponent } from './coming-soon.component';
describe('ComingSoonComponent', () => {
let component: ComingSoonComponent;
let fixture: ComponentFixture<ComingSoonComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ ComingSoonComponent ]
})
.compileComponents();
fixture = TestBed.createComponent(ComingSoonComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,10 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-coming-soon',
templateUrl: './coming-soon.component.html',
styleUrls: ['./coming-soon.component.css']
})
export class ComingSoonComponent {
}

View File

@ -0,0 +1,4 @@
:host ::ng-deep .ng-select .ng-select-container {
/* color: #ffffff !important; */
/* background-color: #252525 !important; */
}

View File

@ -0,0 +1,12 @@
<ng-select
[items]="icons"
bindLabel="name"
(change)="onSelect($event)"
[(ngModel)]="selectedIcon"
bindValue="icon"
placeholder="Select an icon"
>
<ng-template ng-option-tmp let-item="item">
<i [ngClass]="item.icon"></i> {{ item.name }}
</ng-template>
</ng-select>

View File

@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { SelectIconComponent } from './select-icon.component';
describe('SelectIconComponent', () => {
let component: SelectIconComponent;
let fixture: ComponentFixture<SelectIconComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ SelectIconComponent ]
})
.compileComponents();
fixture = TestBed.createComponent(SelectIconComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,35 @@
import { Component, EventEmitter, Input, Output } from "@angular/core";
import { MasterService } from "../../service/master-api.service";
interface Icon {
name: string;
icon: string;
}
@Component({
selector: "app-select-icon",
templateUrl: "./select-icon.component.html",
styleUrls: ["./select-icon.component.css"],
})
export class SelectIconComponent {
// @Output() iconSelected: EventEmitter<string> = new EventEmitter<string>();
@Output() iconSelected = new EventEmitter<string>();
@Input() selectedIcon = undefined;
icons: any[] = [];
constructor(private masterService: MasterService) {}
ngOnInit(): void {
this.masterService.getIconData().subscribe((res) => {
this.icons = res.rows;
});
if (!this.selectedIcon) {
this.selectedIcon = undefined;
}
}
onSelect(icon: Icon): void {
if (icon) {
this.iconSelected.emit(icon.icon);
} else {
this.iconSelected.emit(undefined);
}
}
}

View File

@ -0,0 +1,15 @@
import { NgModule } from "@angular/core";
import { CommonModule } from "@angular/common";
import { SelectIconComponent } from "./select-icon.component";
import { NgbModule } from "@ng-bootstrap/ng-bootstrap";
import { NgSelectModule } from "@ng-select/ng-select";
import { FormsModule } from "@angular/forms";
@NgModule({
declarations: [SelectIconComponent],
imports: [CommonModule, NgbModule, NgSelectModule, FormsModule],
exports: [
SelectIconComponent
]
})
export class SelectIconModule {}

View File

@ -0,0 +1,443 @@
:host ::ng-deep .progress-bar {
background-color: #bef264 !important;
}
:host ::ng-deep .donut-chart2 .ct-series-a .ct-slice-donut {
stroke: #8a8a8a;
stroke-width: 20px !important;
}
:host ::ng-deep .donut-chart2 .ct-series-b .ct-slice-donut {
stroke: #bef264;
stroke-width: 20px !important;
}
:host ::ng-deep .donut-chart2 .ct-label {
fill: #ffffff;
color: rgb(255, 255, 255);
font-size: 12px;
line-height: 1;
}
:host ::ng-deep .sp-line-total-cost .ct-series-a .ct-point {
stroke: #bef264;
}
:host ::ng-deep .sp-line-total-cost .ct-series-a .ct-line {
stroke: #bef264;
}
:host ::ng-deep .sp-line-total-cost .ct-series-a .ct-area {
fill: #bef264;
fill-opacity: 1;
}
:host ::ng-deep .sp-line-total-cost .ct-point {
stroke-width: 0px;
}
.ct-chart-bar .ct-series .ct-bar {
stroke-width: 20px !important; /* Atur lebar bar sesuai kebutuhan */
}
.ct-chart-bar .ct-label.ct-horizontal {
margin-left: -10px !important; /* Mengatur margin label horizontal */
margin-right: -10px !important;
}
:host ::ng-deep .donut-chart3 .ct-series-a .ct-bar {
stroke: #bef264;
fill: none;
stroke-width: 30px;
}
:host ::ng-deep .donut-chart3 .ct-series-b .ct-bar {
stroke: #bef264;
fill: none;
stroke-width: 30px;
}
:host ::ng-deep .donut-chart3 .ct-label {
fill: #ffffff;
color: rgb(255, 255, 255);
font-size: 12px;
line-height: 1;
margin-right: 20px;
}
:host ::ng-deep .ct-label.ct-horizontal {
font-size: 12px; /* Adjust font size */
transform: rotate(-45deg); /* Rotate labels if needed for better fit */
text-anchor: end;
margin-top: 10px;
}
:host ::ng-deep .ct-chart-bar .ct-labels .ct-label.ct-horizontal {
margin-right: 20px; /* Adjust margin for labels */
}
.chart-title {
font-size: 18px;
margin-bottom: 10px;
font-weight: bold;
}
/* Hide the default calendar icon */
input[type="month"]::-webkit-calendar-picker-indicator {
background-color: #ffffff;
}
/* table */
:host
::ng-deep
.ngx-datatable.bootstrap
.datatable-header
.datatable-header-cell
.datatable-header-cell-label {
font-family: inherit;
font-size: medium;
font-weight: bold;
color: #6b6f82;
}
:host ::ng-deep .ngx-datatable .datatable-row-center,
.ngx-datatable .datatable-row-group,
.ngx-datatable .datatable-row-right {
position: relative;
height: 50px !important;
}
:host ::ng-deep .datatable-icon-right:before {
font-family: "icofont";
font-style: normal;
}
:host ::ng-deep .datatable-icon-skip:before {
font-family: "icofont";
font-style: normal;
}
:host ::ng-deep .datatable-icon-left:before {
font-family: "icofont";
font-style: normal;
}
:host ::ng-deep .datatable-icon-left:before {
content: "\2039";
font-size: x-large;
}
:host ::ng-deep .datatable-icon-prev:before {
content: "\00AB";
font-size: x-large;
}
:host ::ng-deep .datatable-icon-right:before {
content: "\203A";
font-size: x-large;
}
:host ::ng-deep .datatable-icon-skip:before {
content: "\00BB";
font-size: x-large;
}
:host
::ng-deep
.ngx-datatable.bootstrap
.datatable-footer
.datatable-pager
.datatable-icon-left,
.ngx-datatable.bootstrap
.datatable-footer
.datatable-pager
.datatable-icon-right,
.ngx-datatable.bootstrap
.datatable-footer
.datatable-pager
.datatable-icon-prev {
font-size: 16px;
line-height: 22px;
padding: 0px 09px;
background-color: #d4d2e7;
}
:host
::ng-deep
.ngx-datatable.bootstrap
.datatable-footer
.datatable-pager
.datatable-icon-right,
.ngx-datatable.bootstrap
.datatable-footer
.datatable-pager
.datatable-icon-right {
font-size: 16px;
line-height: 22px;
padding: 0px 09px;
background-color: #d4d2e7;
}
:host
::ng-deep
.ngx-datatable.bootstrap
.datatable-footer
.datatable-pager
.datatable-icon-skip {
font-size: 16px;
line-height: 22px;
padding: 0px 09px;
background-color: #d4d2e7;
}
:host
::ng-deep
.ngx-datatable.bootstrap
.datatable-footer
.datatable-pager
.datatable-icon-prev {
font-size: 16px;
line-height: 22px;
padding: 0px 09px;
background-color: #d4d2e7;
}
:host
::ng-deep
.datatable-footer
.datatable-pager
ul
li:not(.disabled).active
a,
.ngx-datatable.bootstrap[_ngcontent-c11]
.datatable-footer[_ngcontent-c11]
.datatable-pager[_ngcontent-c11]
ul[_ngcontent-c11]
li[_ngcontent-c11]:not(.disabled):hover
a[_ngcontent-c11] {
background-color: #d4d2e7;
font-weight: bold;
font-size: larger;
}
:host ::ng-deep .ngx-datatable.bootstrap .datatable-footer .datatable-pager a {
height: 32px;
min-width: 34px;
line-height: 22px;
padding: 0;
border-radius: 3px;
margin: 0 3px;
text-align: center;
vertical-align: top;
padding-top: 3px;
text-decoration: none;
vertical-align: bottom;
color: #7c8091;
}
:host
::ng-deep
.ngx-datatable.bootstrap
.datatable-footer
.datatable-pager
.datatable-icon-left,
.ngx-datatable.bootstrap[_ngcontent-c11]
.datatable-footer[_ngcontent-c11]
.datatable-pager[_ngcontent-c11]
.datatable-icon-right[_ngcontent-c11],
.ngx-datatable.bootstrap[_ngcontent-c11]
.datatable-footer[_ngcontent-c11]
.datatable-pager[_ngcontent-c11]
.datatable-icon-prev[_ngcontent-c11] {
font-size: 14px;
line-height: 9px;
padding: 0px 08px;
background-color: #ffffff;
}
:host
::ng-deep
.ngx-datatable.bootstrap
.datatable-footer
.datatable-pager
.datatable-icon-left,
.ngx-datatable.bootstrap[_ngcontent-c11]
.datatable-footer[_ngcontent-c11]
.datatable-pager[_ngcontent-c11]
.datatable-icon-right[_ngcontent-c11],
.ngx-datatable.bootstrap[_ngcontent-c11]
.datatable-footer[_ngcontent-c11]
.datatable-pager[_ngcontent-c11]
.datatable-icon-prev[_ngcontent-c11] {
font-size: 0px;
line-height: 22px;
padding: 0px 09px;
background-color: #ffffff;
}
:host
::ng-deep
.ngx-datatable.bootstrap
.datatable-footer
.datatable-pager
.datatable-icon-right,
.ngx-datatable.bootstrap[_ngcontent-c11]
.datatable-footer[_ngcontent-c11]
.datatable-pager[_ngcontent-c11]
.datatable-icon-right[_ngcontent-c11],
.ngx-datatable.bootstrap[_ngcontent-c11]
.datatable-footer[_ngcontent-c11]
.datatable-pager[_ngcontent-c11]
.datatable-icon-prev[_ngcontent-c11] {
font-size: 0px;
line-height: 22px;
padding: 0px 09px;
background-color: #ffffff;
}
:host
::ng-deep
.ngx-datatable.bootstrap
.datatable-footer
.datatable-pager
.datatable-icon-skip,
.ngx-datatable.bootstrap[_ngcontent-c11]
.datatable-footer[_ngcontent-c11]
.datatable-pager[_ngcontent-c11]
.datatable-icon-right[_ngcontent-c11],
.ngx-datatable.bootstrap[_ngcontent-c11]
.datatable-footer[_ngcontent-c11]
.datatable-pager[_ngcontent-c11]
.datatable-icon-prev[_ngcontent-c11] {
font-size: 0px;
line-height: 22px;
padding: 0px 09px;
background-color: #ffffff;
}
:host
::ng-deep
.ngx-datatable.bootstrap
.datatable-footer
.datatable-pager
.datatable-icon-prev,
.ngx-datatable.bootstrap[_ngcontent-c11]
.datatable-footer[_ngcontent-c11]
.datatable-pager[_ngcontent-c11]
.datatable-icon-right[_ngcontent-c11],
.ngx-datatable.bootstrap[_ngcontent-c11]
.datatable-footer[_ngcontent-c11]
.datatable-pager[_ngcontent-c11]
.datatable-icon-prev[_ngcontent-c11] {
font-size: 0px;
line-height: 22px;
padding: 0px 09px;
background-color: #ffffff;
}
:host
::ng-deep
.ngx-datatable.bootstrap
.datatable-footer
.datatable-pager
ul
li:not(.disabled):hover
a {
background-color: #545454;
font-weight: bold;
color: white;
}
:host
::ng-deep
.ngx-datatable.bootstrap
.datatable-footer
.datatable-pager
ul
li:not(.disabled).active
a,
.ngx-datatable.bootstrap
.datatable-footer
.datatable-pager
ul
li:not(.disabled):hover
a {
background-color: #37A647;
font-weight: bold;
color: white;
}
:host ::ng-deep .ngx-datatable.bootstrap .datatable-footer {
background: #DDE1E6;
color: #242222;
margin-top: -1px;
overflow: inherit;
}
:host ::ng-deep .ngx-datatable.bootstrap .datatable-header {
background: #DDE1E6;
color: #242222;
font-weight: bold;
height: unset !important;
overflow: inherit;
}
:host ::ng-deep .ngx-datatable .datatable-footer .datatable-pager {
flex: 0 0 0%;
}
:host ::ng-deep .ngx-datatable .datatable-footer .datatable-pager .pager {
display: flex;
}
:host
::ng-deep
.ngx-datatable
.datatable-footer
.selected-count
.datatable-pager {
flex: 0 0 0%;
}
:host ::ng-deep .ngx-datatable {
display: -webkit-box;
}
:host ::ng-deep .ng-select .ng-select-container {
color: #242222 !important;
background-color: #FBFBFB !important;
height: 40px !important;
border-radius: 12px !important;
box-shadow: 0 2px 4px rgba(36, 34, 34, 0.2) !important; /* Bayangan lebih tipis */
}
:host ::ng-deep .ng-select .ng-select-container .ng-value-container .ng-input>input {
color: #242222 !important;
}
:host ::ng-deep .ngx-datatable.bootstrap .datatable-body-row {
background-color: #FBFBFB; /* Black color for table rows */
}
:host ::ng-deep .ngx-datatable.bootstrap .datatable-body-row:hover {
background-color: #DDE1E6; /* Darker black for hover effect */
}
.text-custom-label{
color: #242222 !important;
font-family: "Open Sans", sans-serif !important;
font-size: 16px;
}
.text-custom-data{
color: #242222 !important;
font-family: "Open Sans", sans-serif !important;
font-size: 24px;
font-weight: 700;
}
.style-custom-label{
color: #242222 !important;
}

View File

@ -0,0 +1,411 @@
<div class="app-content content" style="background-color: #FBFBFB !important">
<div class="content-wrapper">
<div class="content-header row mb-1">
<app-breadcrumb class="col-12" [breadcrumb]="breadcrumb"></app-breadcrumb>
</div>
<div class="content-body">
<section>
<div class="row">
<div class="col-12 col-sm-6 col-md-6 col-lg-6">
<div class="card" style="background-color: #DDE1E6 !important">
<div
class="card-header"
style="background-color: #DDE1E6 !important"
>
<h4
class="card-title text-center text-custom-label"
>
Comparison of Previous Month Costs
</h4>
<hr
style="
border-top: 4px solid #242222;
border-color: #242222 !important;
"
/>
</div>
<div class="card-body">
<div
echarts
[options]="chartOption"
class="echart-container"
style="height: 150% !important"
></div>
</div>
</div>
</div>
<div class="col-12 col-sm-6 col-md-6 col-lg-6">
<div class="card" style="background-color: #DDE1E6 !important">
<div
class="card-header"
style="background-color: #DDE1E6 !important"
>
<h4
class="card-title text-center text-custom-label"
>
Comparison of Actual Costs and Estimated Costs
</h4>
<hr
style="
border-top: 4px solid #242222;
border-color: #242222 !important;
"
/>
</div>
<div class="card-body">
<div
echarts
[options]="chartOption2"
class="echart-container"
style="height: 150% !important"
></div>
</div>
</div>
</div>
</div>
</section>
<section id="configuration">
<div class="card" style="background-color: #FBFBFB !important">
<div class="card-content">
<div class="card-body">
<div class="row">
<div class="col-6 col-sm-3 col-md-3 col-lg-3 col-xl-3">
<div class="form-group">
<ng-select
[items]="dataBuildingList"
[searchable]="true"
bindLabel="name"
bindValue="id"
placeholder="Select Building"
[(ngModel)]="buildingSelected"
>
</ng-select>
</div>
</div>
<div class="col-6 col-sm-3 col-md-3 col-lg-3 col-xl-3">
<div class="form-group">
<input
type="month"
class="form-control"
[(ngModel)]="dateSelected"
id="month12"
(focus)="focusElement('month12')"
/>
</div>
</div>
<div class="col-6 col-sm-3 col-md-3 col-lg-3 col-xl-3">
<div class="form-group">
<ng-select
[items]="dataMasterCategori"
[searchable]="true"
bindLabel="name"
bindValue="id"
placeholder="Select Category"
[(ngModel)]="categorySelected"
>
</ng-select>
</div>
</div>
<div class="col-6 col-sm-3 col-md-3 col-lg-3 col-xl-3">
<div class="d-flex">
<button
type="button"
class="btn btn-outline-success"
(click)="doFilter()"
style="
background-color: #37A647 !important;
border-color: #37A647 !important;
color: #ffffff;
"
[disabled]="spinnerFilterActive"
>
<i
class="la la-search"
style="color: #ffffff !important"
*ngIf="!spinnerFilterActive"
></i>
<i
class="la la-spinner spinner"
style="color: #ffffff !important"
*ngIf="spinnerFilterActive"
></i>
</button>
</div>
</div>
</div>
<div class="row mb-2">
<div class="col-6 col-sm-6 col-md-6 col-lg-6 col-xl-3">
<div class="form-group">
<div class="input-group">
<input
type="text"
class="form-control"
placeholder="Kwh"
[(ngModel)]="kwhTerm"
disabled
/>
<div
class="input-group-append"
style="background-color: #FBFBFB !important"
>
<span
class="input-group-text"
style="
background-color: #FBFBFB !important;
color: #242222;
height: 40px !important;
"
>Kwh</span
>
</div>
</div>
</div>
</div>
<div class="col-6 col-sm-6 col-md-6 col-lg-6 col-xl-3">
<div class="form-group">
<div class="input-group">
<input
type="text"
class="form-control"
placeholder="Cost"
mask="separator"
thousandSeparator="."
[(ngModel)]="costTerm"
disabled
/>
<div
class="input-group-append"
style="background-color: #FBFBFB !important"
>
<span
class="input-group-text"
style="
background-color: #FBFBFB !important;
color: #242222;
height: 40px !important;
"
>IDR</span
>
</div>
</div>
</div>
</div>
<div class="col-12 col-sm-12 col-md-12 col-lg-12 col-xl-6">
<div class="d-flex justify-content-end">
<button
class="btn btn-secondary mr-2"
style="
width: 100%;
background-color: #37A647 !important;
border-color: #37A647 !important;
color: #ffffff !important;
"
(click)="export()"
[disabled]="spinnerExportActive"
>
<i
class="la la-spinner spinner"
*ngIf="spinnerExportActive"
></i>
<i class="ri-export-line" *ngIf="!spinnerExportActive"></i
>&nbsp;
<span>Export</span>
</button>
<button
class="btn btn-secondary mr-2"
style="
width: 100%;
background-color: #37A647 !important;
border-color: #37A647 !important;
color: #ffffff !important;
"
(click)="syncData()"
[disabled]="spinnerActive"
>
<i
class="la la-spinner spinner"
*ngIf="spinnerActive"
></i>
<i class="la la-spinner" *ngIf="!spinnerActive"></i>&nbsp;
<span>Syncing Data</span>
</button>
<button
class="btn btn-secondary"
style="
width: 100%;
background-color: #37A647 !important;
border-color: #37A647 !important;
color: #ffffff !important;
"
(click)="addFieldValue()"
>
<i class="feather ft-plus"></i>&nbsp;
<span>Add Actual Cost</span>
</button>
</div>
</div>
</div>
<div class="card-dashboard">
<ngx-datatable
class="bootstrap table-bordered"
[limit]="10"
[rows]="data_cost"
[columnMode]="'force'"
[headerHeight]="50"
[footerHeight]="50"
[rowHeight]="50"
fxFlex="auto"
[scrollbarH]="true"
>
<ngx-datatable-column
name="#"
[flexGrow]="0.5"
[minWidth]="30"
>
<ng-template
ngx-datatable-cell-template
let-rowIndex="rowIndex"
>
<p class="style-custom-label">
{{ rowIndex + 1 }}
</p>
</ng-template>
</ngx-datatable-column>
<ngx-datatable-column
name="building"
[flexGrow]="1"
[minWidth]="90"
>
<ng-template ngx-datatable-header-template>
<span class="style-custom-label">Building</span>
</ng-template>
<ng-template let-value="value" ngx-datatable-cell-template>
<p class="style-custom-label">{{ value }}</p>
</ng-template>
</ngx-datatable-column>
<ngx-datatable-column
name="categoryName"
[flexGrow]="1"
[minWidth]="90"
>
<ng-template ngx-datatable-header-template>
<span class="style-custom-label">Category</span>
</ng-template>
<ng-template let-value="value" ngx-datatable-cell-template>
<p class="style-custom-label">{{ value }}</p>
</ng-template>
</ngx-datatable-column>
<ngx-datatable-column
name="roomName"
[flexGrow]="1"
[minWidth]="90"
>
<ng-template ngx-datatable-header-template>
<span class="style-custom-label">Room</span>
</ng-template>
<ng-template let-value="value" ngx-datatable-cell-template>
<p class="style-custom-label">{{ value }}</p>
</ng-template>
</ngx-datatable-column>
<ngx-datatable-column
name="estimationCost"
[flexGrow]="1"
[minWidth]="90"
>
<ng-template ngx-datatable-header-template>
<span class="style-custom-label"
>Estimation Cost</span
>
</ng-template>
<ng-template let-value="value" ngx-datatable-cell-template>
<p class="style-custom-label">
{{
value.toLocaleString("id-ID", {
style: "currency",
currency: "IDR"
})
}}
</p>
</ng-template>
</ngx-datatable-column>
<ngx-datatable-column
name="totalUse"
[flexGrow]="1"
[minWidth]="90"
>
<ng-template ngx-datatable-header-template>
<span class="style-custom-label">Total Kwh</span>
</ng-template>
<ng-template let-value="value" ngx-datatable-cell-template>
<p class="style-custom-label">{{ value }}</p>
kWh
</ng-template>
</ngx-datatable-column>
<!-- <ngx-datatable-column
name="endDate"
[flexGrow]="1"
[minWidth]="90"
>
<ng-template ngx-datatable-header-template>
<span class="style-custom-label">Tanggal</span>
</ng-template>
<ng-template let-value="value" ngx-datatable-cell-template>
<p class="style-custom-label">
{{ value | date : "MM/yyyy" }}
</p>
</ng-template>
</ngx-datatable-column> -->
<ngx-datatable-column
name="status_id"
[flexGrow]="1"
[minWidth]="90"
>
<ng-template ngx-datatable-header-template>
<span class="style-custom-label">Status</span>
</ng-template>
<ng-template ngx-datatable-cell-template let-value="value">
<p class="style-custom-label">
{{ value === 2 ? "Aktif" : "Tidak Aktif" }}
</p>
</ng-template>
</ngx-datatable-column>
<ngx-datatable-column
name="Actions"
[flexGrow]="1"
[minWidth]="150"
>
<ng-template ngx-datatable-header-template>
<span class="style-custom-label">Actions</span>
</ng-template>
<ng-template
ngx-datatable-cell-template
let-rowIndex="rowIndex"
let-row="row"
>
<button
class="btn btn-sm btn-warning mr-1"
style="
background-color: #DDE1E6 !important;
border-color: #37A647 !important;
"
(click)="viewRow(row)"
>
<i
class="ficon ri-export-line"
style="color: #37A647 !important"
></i>
</button>
</ng-template>
</ngx-datatable-column>
</ngx-datatable>
</div>
</div>
</div>
</div>
</section>
</div>
</div>
</div>

View File

@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { CostManagementComponent } from './cost-management.component';
describe('CostManagementComponent', () => {
let component: CostManagementComponent;
let fixture: ComponentFixture<CostManagementComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ CostManagementComponent ]
})
.compileComponents();
fixture = TestBed.createComponent(CostManagementComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,473 @@
import { Component, OnInit } from "@angular/core";
import { BuildingService } from "../service/monitoring-api.service";
import { CostManagementService } from "../service/cost-management.service";
import { NgbModal } from "@ng-bootstrap/ng-bootstrap";
import { ModalAddActualComponent } from "./modal-add-actual/modal-add-actual.component";
import "chartist-plugin-tooltips";
import { ModalExportComponent } from "./modal-export/modal-export.component";
import { TableexcelService } from "src/app/_services/tableexcel.service";
import { ToastrService } from "ngx-toastr";
import { LoginService } from "../service/login.service";
@Component({
selector: "app-cost-management",
templateUrl: "./cost-management.component.html",
styleUrls: ["./cost-management.component.css"],
})
export class CostManagementComponent implements OnInit {
public breadcrumb: any;
data: any;
filteredRows: any[];
data_cost: any;
searchTerm: string = "";
kwhTerm: string = "";
costTerm: string = "";
rows: any = [];
dataMasterCategori: any;
dataBuildingList: any;
public focucedElement = "";
spinnerActive = false;
spinnerActiveActual = false;
spinnerFilterActive = false;
barChart: any;
dateSelected: any;
buildingSelected: any;
categorySelected: any;
dataCompPrev: any;
dataCompAct: any;
storedData: any;
chartOption: any;
chartOption2: any;
spinnerExportActive = false;
constructor(
private monitoringApiService: BuildingService,
private costService: CostManagementService,
private modalService: NgbModal,
private tableexcelService: TableexcelService,
private toastr: ToastrService,
private authService: LoginService
) {}
ngOnInit() {
this.authService.startTokenCheck();
this.authService.startTrackingActivity();
this.breadcrumb = {
mainlabel: "Cost Management",
links: [
{
name: "Home",
isLink: false,
},
{
name: "Cost Management",
isLink: false,
},
],
};
this.storedData = JSON.parse(localStorage.getItem("currentUser"));
this.buildingSelected = this.storedData.buildingId;
const currentDate = new Date();
this.dateSelected = currentDate.toISOString().slice(0, 7);
this.fetchData(
this.storedData.buildingId,
this.dateSelected,
this.categorySelected
);
this.dataListMaster();
this.dataListBuilding();
this.dataCompPrevMonthCost(this.storedData.buildingId);
this.dataCompActEstCost(this.storedData.buildingId);
}
fetchData(id, period, category) {
this.costService
.getCostManagement(id, period, category)
.subscribe((response) => {
this.data = response.results.data;
this.filteredRows = this.data;
let kwhData = parseFloat(response.results.kwh);
this.kwhTerm = kwhData.toFixed(1);
this.costTerm = response.results.cost;
this.data_cost = this.filteredRows.map((item) => ({
building: item.name,
roomName: item.room_name,
categoryName: item.category_name,
estimationCost: item.estimation_cost,
totalUse: item.total_use.toFixed(1),
endDate: this.convertToUTC7(item.end_date),
// endDate: item.end_date,
statusId: item.status_id,
categoryId: item.category_id,
roomId: item.room_id,
}));
console.log(this.data_cost);
});
}
convertToUTC7(dateString: string): string {
const date = new Date(dateString);
const utc7Offset = 7 * 60; // UTC+7 in minutes
const localOffset = date.getTimezoneOffset();
const totalOffset = utc7Offset - localOffset;
const utc7Date = new Date(date.getTime() + totalOffset * 60 * 1000);
return utc7Date.toISOString();
}
dataListMaster() {
this.monitoringApiService.getMasterListData().subscribe((data) => {
const dataCategory = data.data.find(
(item) => item.name === "master_category"
).headerDetailParam;
this.dataMasterCategori = dataCategory.filter(
(item) => item.statusName.toLowerCase() === "aktif"
);
});
}
dataListBuilding() {
this.monitoringApiService.getBuildingList().subscribe((data) => {
this.dataBuildingList = data.data.filter(
(item) => item.statusName.toLowerCase() === "aktif"
);
});
}
dataCompPrevMonthCost(buildingId) {
this.costService.getCompPrevMonthCost(buildingId).subscribe((data) => {
this.dataCompPrev = data.data;
this.chartOption = {
tooltip: {
trigger: "axis",
axisPointer: {
type: "shadow",
},
formatter: function (params) {
var tar = params[1];
return tar.name + "<br/>" + tar.seriesName + " : " + tar.value;
},
},
grid: {
left: "25%",
right: "25%",
top: "20%",
bottom: "20%",
},
color: ["#37A647"],
xAxis: {
type: "category",
splitLine: { show: false },
data: [this.dataCompPrev[0].name, this.dataCompPrev[1].name],
axisLine: {
show: true,
lineStyle: {
color: "#37A647",
width: 7,
},
},
axisTick: {
show: false,
},
axisLabel: {
show: false,
},
},
yAxis: {
type: "value",
axisLine: {
show: false,
},
axisTick: {
show: false,
},
splitLine: {
show: false,
},
axisLabel: {
show: false,
},
},
series: [
// {
// name: "Placeholder",
// type: "bar",
// stack: "Total",
// itemStyle: {
// borderColor: "transparent",
// color: "transparent",
// },
// emphasis: {
// itemStyle: {
// borderColor: "transparent",
// color: "transparent",
// },
// },
// data: [1000, 1000],
// },
{
name: "Cost",
type: "bar",
stack: "Total",
label: {
show: true,
position: "top",
color: "#242222",
formatter: function (params) {
return `Rp. ${params.value.toLocaleString()}`;
},
},
// barWidth: "50%",
data: [this.dataCompPrev[0].rupiah, this.dataCompPrev[1].rupiah],
},
],
};
});
}
dataCompActEstCost(buildingId) {
this.costService.getCompActEstCost(buildingId).subscribe((data) => {
this.dataCompAct = data.data[0];
this.chartOption2 = {
tooltip: {
trigger: "axis",
axisPointer: {
type: "shadow",
},
formatter: function (params) {
var tar = params[1];
return tar.name + "<br/>" + tar.seriesName + " : " + tar.value;
},
},
grid: {
left: "25%",
right: "25%",
top: "20%",
bottom: "20%",
},
color: ["#37A647"],
xAxis: {
type: "category",
splitLine: { show: false },
data: ["Estimation Cost", "Actual Cost"],
axisLine: {
show: true,
lineStyle: {
color: "#37A647",
width: 7,
},
},
axisTick: {
show: false,
},
axisLabel: {
show: false,
},
},
yAxis: {
type: "value",
axisLine: {
show: false,
},
axisTick: {
show: false,
},
splitLine: {
show: false,
},
axisLabel: {
show: false,
},
},
series: [
// {
// name: "Placeholder",
// type: "bar",
// stack: "Total",
// itemStyle: {
// borderColor: "transparent",
// color: "transparent",
// },
// emphasis: {
// itemStyle: {
// borderColor: "transparent",
// color: "transparent",
// },
// },
// data: [2500, 2500],
// },
{
name: "Cost",
type: "bar",
stack: "Total",
label: {
show: true,
position: "top",
color: "#242222",
formatter: function (params) {
return `Rp. ${params.value.toLocaleString()}`;
},
},
// barWidth: "50%",
data: [this.dataCompAct.est_cost, this.dataCompAct.real_cost],
},
],
};
});
}
syncData() {
const dataDate = {
buildingId: this.buildingSelected,
periode: this.dateSelected,
};
this.spinnerActive = true;
this.costService.getSyncCost(dataDate).subscribe((data) => {
this.dataCompPrevMonthCost(this.storedData.buildingId);
this.dataCompActEstCost(this.storedData.buildingId);
});
setTimeout(() => {
this.spinnerActive = false;
this.fetchData(
this.buildingSelected,
this.dateSelected,
this.categorySelected
);
}, 3000);
}
actualData() {
this.spinnerActiveActual = true;
this.costService
.getRealCostByBuildingId(4, this.dateSelected)
.subscribe((data) => {
// console.log(data.data[0]);
// this.costTerm = data.data[0].est_cost
});
setTimeout(() => {
this.spinnerActiveActual = false;
this.fetchData(
this.buildingSelected,
this.dateSelected,
this.categorySelected
);
}, 3000);
}
doFilter() {
if (this.dateSelected) {
this.spinnerFilterActive = true;
setTimeout(() => {
this.spinnerFilterActive = false;
this.fetchData(
this.buildingSelected,
this.dateSelected,
this.categorySelected
);
}, 3000);
} else {
this.toastr.error("Warning", "Format date tidak valid.", {
timeOut: 5000,
closeButton: true,
});
}
}
addFieldValue() {
const modalRef = this.modalService.open(ModalAddActualComponent, {
size: "sm",
centered: true,
});
modalRef.componentInstance.buildingId = this.buildingSelected;
modalRef.componentInstance.periode = this.dateSelected;
modalRef.result.then(
(result) => {
if (result) {
this.rows.push(result);
this.rows = [...this.rows];
this.dataCompActEstCost(this.buildingSelected);
}
},
(reason) => {
console.log(`Dismissed: ${reason}`);
}
);
}
focusElement(focucedElement: any) {
this.focucedElement = focucedElement;
}
filterRows() {
if (!this.searchTerm) {
this.filteredRows = [...this.data.rows];
} else {
this.filteredRows = this.data.rows.filter((row) =>
this.rowContainsSearchTerm(row)
);
}
}
rowContainsSearchTerm(row: any): boolean {
const searchTermLC = this.searchTerm.toLowerCase();
return Object.values(row).some(
(value) =>
value !== null && value.toString().toLowerCase().includes(searchTermLC)
);
}
viewRow(row) {
const modalRef = this.modalService.open(ModalExportComponent, {
size: "xl",
centered: true,
});
modalRef.componentInstance.buildingId = this.buildingSelected;
modalRef.componentInstance.dataRow = row;
modalRef.result.then(
(result) => {
if (result) {
this.rows.push(result);
this.rows = [...this.rows];
}
},
(reason) => {
console.log(`Dismissed: ${reason}`);
}
);
}
export() {
this.spinnerExportActive = true;
setTimeout(() => {
const columnsToExport = [
"building",
"roomName",
"categoryName",
"totalUse",
"estimationCost",
"endDate",
];
this.tableexcelService.exportAsExcelFileCostManage(
this.data_cost,
"Smart_building_cost_management",
columnsToExport
);
this.spinnerExportActive = false;
}, 3000);
}
}

View File

@ -0,0 +1,63 @@
import { NgModule } from '@angular/core';
import { CommonModule, DatePipe } from '@angular/common';
import { CostManagementComponent } from './cost-management.component';
import { RouterModule } from '@angular/router';
import { BreadcrumbModule } from 'src/app/_layout/breadcrumb/breadcrumb.module';
import { NgxDatatableModule } from '@swimlane/ngx-datatable';
import { BlockUIModule } from 'ng-block-ui';
import { BlockTemplateComponent } from 'src/app/_layout/blockui/block-template.component';
import { CardModule } from '../../partials/general/card/card.module';
import { NgSelectModule } from '@ng-select/ng-select';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { ClipboardModule } from 'ngx-clipboard';
import { PerfectScrollbarModule } from 'ngx-perfect-scrollbar';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
import { NgChartsModule } from 'ng2-charts';
import { ChartistModule } from 'ng-chartist';
import { MatchHeightModule } from '../../partials/general/match-height/match-height.module';
import { ModalAddActualComponent } from './modal-add-actual/modal-add-actual.component';
import { NgxMaskModule, IConfig } from 'ngx-mask';
import { ModalExportComponent } from './modal-export/modal-export.component'
import { NgxEchartsModule } from 'ngx-echarts';
export const options: Partial<null|IConfig> | (() => Partial<IConfig>) = null;
@NgModule({
declarations: [
CostManagementComponent,
ModalAddActualComponent,
ModalExportComponent
],
imports: [
CommonModule,
CardModule,
BreadcrumbModule,
NgSelectModule,
FormsModule,
NgbModule,
ReactiveFormsModule,
ClipboardModule,
PerfectScrollbarModule,
NgxDatatableModule,
NgChartsModule,
ChartistModule,
MatchHeightModule,
NgxEchartsModule.forRoot({
echarts: () => import('echarts')
}),
NgxMaskModule.forRoot(),
BlockUIModule.forRoot({
template: BlockTemplateComponent
}),
RouterModule.forChild([
{
path: '',
component: CostManagementComponent
}
])
],
providers: [
DatePipe // Add DatePipe to providers
],
})
export class CostManagementModule { }

View File

@ -0,0 +1,56 @@
/* modal-add-edit.component.css */
::ng-deep .modal-backdrop.show {
z-index: auto !important;
}
::ng-deep .input-group-append .btn {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
border-radius: 0;
border-left: 0;
flex-grow: 0;
border-left: 1px solid #ced4da;
padding: 0.375rem 0.75rem;
}
::ng-deep .input-group {
display: flex;
flex-wrap: nowrap; /* Prevents wrapping of the items */
align-items: center;
}
::ng-deep .form-control {
flex-grow: 1; /* Ensures select takes up available space */
padding-right: 0.5rem;
}
::ng-deep .input-group select,
::ng-deep .input-group .input-group-append .btn {
padding-right: 5px; /* Adjust padding if necessary */
}
::ng-deep .input-group .form-control {
margin-right: 2px; /* Adjust margin to make space */
}
.form-group {
margin-bottom: 1rem;
}
.form-control {
display: block;
width: 100%;
height: calc(1.5em + 0.75rem + 2px);
padding: 0.375rem 0.75rem;
font-size: 1rem;
font-weight: 400;
line-height: 1.5;
color: #495057;
background-color: #fff;
background-clip: padding-box;
border: 1px solid #ced4da;
border-radius: 0.25rem;
transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
}

View File

@ -0,0 +1,67 @@
<div class="modal-body" style="background-color: #FBFBFB !important">
<h4 style="color: #242222">Input the actual cost of your expenses</h4>
<p style="color: #242222">Building : {{ buildingName }}</p>
<p style="color: #242222">Periode : {{ formattedDate }}</p>
<form [formGroup]="myForm">
<div class="form-group">
<div class="input-group">
<div
class="input-group-prepend"
style="background-color: #FBFBFB !important"
>
<span
class="input-group-text"
id="basic-addon1"
style="
background-color: #FBFBFB !important;
color: #242222;
height: calc(1.5em + 0.75rem + 2px) !important;
"
>Rp</span
>
</div>
<input
type="text"
class="form-control"
id="real_cost"
aria-describedby="basic-addon1"
formControlName="real_cost"
placeholder="..............."
mask="separator"
thousandSeparator="."
/>
</div>
</div>
</form>
</div>
<div class="modal-footer justify-content-between" style="background-color: #FBFBFB !important; border-style: none !important;">
<button
type="button"
class="btn"
style="
color: #242222;
width: 25%;
background-color: #FBFBFB !important;
border-color: #FBFBFB !important;
border-radius: 10px;
"
(click)="activeModal.dismiss('Cross click')"
>
Close
</button>
<button
type="button"
class="btn"
style="
color: #242222 !important;
width: 25%;
background-color: #DDE1E6 !important;
border-color: #DDE1E6 !important;
border-radius: 10px;
"
(click)="addRow()"
>
Add
</button>
</div>

View File

@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ModalAddActualComponent } from './modal-add-actual.component';
describe('ModalAddActualComponent', () => {
let component: ModalAddActualComponent;
let fixture: ComponentFixture<ModalAddActualComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ ModalAddActualComponent ]
})
.compileComponents();
fixture = TestBed.createComponent(ModalAddActualComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,100 @@
import { Component, Input } from "@angular/core";
import { FormBuilder, FormGroup, Validators } from "@angular/forms";
import { NgbActiveModal } from "@ng-bootstrap/ng-bootstrap";
import { CostManagementService } from "../../service/cost-management.service";
import { LoginService } from "../../service/login.service";
@Component({
selector: "app-modal-add-actual",
templateUrl: "./modal-add-actual.component.html",
styleUrls: ["./modal-add-actual.component.css"],
})
export class ModalAddActualComponent {
@Input() buildingId: number;
@Input() periode: any;
myForm: FormGroup;
dateCurrent: any;
dataCost: any;
formattedDate: any;
formattedDate2: any;
buildingName: any;
constructor(
public activeModal: NgbActiveModal,
private fb: FormBuilder,
private costService: CostManagementService,
private authService: LoginService
) {
this.createForm();
}
ngOnInit() {
this.authService.startTokenCheck();
this.authService.startTrackingActivity();
const currentDate = new Date();
this.dateCurrent = currentDate.toISOString().slice(0, 7);
this.dateFormat();
this.getBuildingById();
this.datalistcost();
}
dateFormat() {
let year = this.periode.slice(0, 4);
let month = this.periode.slice(5, 7);
let monthNames = [
"Januari",
"Februari",
"Maret",
"April",
"Mei",
"Juni",
"Juli",
"Agustus",
"September",
"Oktober",
"November",
"Desember",
];
let monthName = monthNames[parseInt(month) - 1];
this.formattedDate = `${monthName} ${year}`;
this.formattedDate2 = `${year}-${month}`;
}
createForm() {
this.myForm = this.fb.group({
real_cost: ["", Validators.required],
});
}
getBuildingById() {
this.costService.getBUildingById(this.buildingId).subscribe((data) => {
this.buildingName = data.data.name;
});
}
datalistcost() {
this.costService
.getRealCostByBuildingId(this.buildingId, this.formattedDate2)
.subscribe((data) => {
this.dataCost = data.data[0];
this.myForm.patchValue({
real_cost: data.data[0].real_cost ? data.data[0].real_cost : 0,
});
});
}
addRow() {
if (this.myForm.valid) {
this.costService
.putRealCost(this.myForm.value, this.dataCost.id)
.subscribe((data) => {
console.log(data);
this.activeModal.close(this.myForm.value);
});
}
}
}

View File

@ -0,0 +1,444 @@
:host ::ng-deep .progress-bar {
background-color: #bef264 !important;
}
:host ::ng-deep .donut-chart2 .ct-series-a .ct-slice-donut {
stroke: #8a8a8a;
stroke-width: 20px !important;
}
:host ::ng-deep .donut-chart2 .ct-series-b .ct-slice-donut {
stroke: #bef264;
stroke-width: 20px !important;
}
:host ::ng-deep .donut-chart2 .ct-label {
fill: #ffffff;
color: rgb(255, 255, 255);
font-size: 12px;
line-height: 1;
}
:host ::ng-deep .sp-line-total-cost .ct-series-a .ct-point {
stroke: #bef264;
}
:host ::ng-deep .sp-line-total-cost .ct-series-a .ct-line {
stroke: #bef264;
}
:host ::ng-deep .sp-line-total-cost .ct-series-a .ct-area {
fill: #bef264;
fill-opacity: 1;
}
:host ::ng-deep .sp-line-total-cost .ct-point {
stroke-width: 0px;
}
.ct-chart-bar .ct-series .ct-bar {
stroke-width: 20px !important; /* Atur lebar bar sesuai kebutuhan */
}
.ct-chart-bar .ct-label.ct-horizontal {
margin-left: -10px !important; /* Mengatur margin label horizontal */
margin-right: -10px !important;
}
:host ::ng-deep .donut-chart3 .ct-series-a .ct-bar {
stroke: #bef264;
fill: none;
stroke-width: 30px;
}
:host ::ng-deep .donut-chart3 .ct-series-b .ct-bar {
stroke: #bef264;
fill: none;
stroke-width: 30px;
}
:host ::ng-deep .donut-chart3 .ct-label {
fill: #ffffff;
color: rgb(255, 255, 255);
font-size: 12px;
line-height: 1;
margin-right: 20px;
}
:host ::ng-deep .ct-label.ct-horizontal {
font-size: 12px; /* Adjust font size */
transform: rotate(-45deg); /* Rotate labels if needed for better fit */
text-anchor: end;
margin-top: 10px;
}
:host ::ng-deep .ct-chart-bar .ct-labels .ct-label.ct-horizontal {
margin-right: 20px; /* Adjust margin for labels */
}
.chart-title {
font-size: 18px;
margin-bottom: 10px;
font-weight: bold;
}
/* Hide the default calendar icon */
input[type="month"]::-webkit-calendar-picker-indicator {
background-color: #ffffff;
}
/* table */
:host
::ng-deep
.ngx-datatable.bootstrap
.datatable-header
.datatable-header-cell
.datatable-header-cell-label {
font-family: inherit;
font-size: medium;
font-weight: bold;
color: #6b6f82;
}
:host ::ng-deep .ngx-datatable .datatable-row-center,
.ngx-datatable .datatable-row-group,
.ngx-datatable .datatable-row-right {
position: relative;
height: 50px !important;
}
:host ::ng-deep .datatable-icon-right:before {
font-family: "icofont";
font-style: normal;
}
:host ::ng-deep .datatable-icon-skip:before {
font-family: "icofont";
font-style: normal;
}
:host ::ng-deep .datatable-icon-left:before {
font-family: "icofont";
font-style: normal;
}
:host ::ng-deep .datatable-icon-left:before {
content: "\2039";
font-size: x-large;
}
:host ::ng-deep .datatable-icon-prev:before {
content: "\00AB";
font-size: x-large;
}
:host ::ng-deep .datatable-icon-right:before {
content: "\203A";
font-size: x-large;
}
:host ::ng-deep .datatable-icon-skip:before {
content: "\00BB";
font-size: x-large;
}
:host
::ng-deep
.ngx-datatable.bootstrap
.datatable-footer
.datatable-pager
.datatable-icon-left,
.ngx-datatable.bootstrap
.datatable-footer
.datatable-pager
.datatable-icon-right,
.ngx-datatable.bootstrap
.datatable-footer
.datatable-pager
.datatable-icon-prev {
font-size: 16px;
line-height: 22px;
padding: 0px 09px;
background-color: #d4d2e7;
}
:host
::ng-deep
.ngx-datatable.bootstrap
.datatable-footer
.datatable-pager
.datatable-icon-right,
.ngx-datatable.bootstrap
.datatable-footer
.datatable-pager
.datatable-icon-right {
font-size: 16px;
line-height: 22px;
padding: 0px 09px;
background-color: #d4d2e7;
}
:host
::ng-deep
.ngx-datatable.bootstrap
.datatable-footer
.datatable-pager
.datatable-icon-skip {
font-size: 16px;
line-height: 22px;
padding: 0px 09px;
background-color: #d4d2e7;
}
:host
::ng-deep
.ngx-datatable.bootstrap
.datatable-footer
.datatable-pager
.datatable-icon-prev {
font-size: 16px;
line-height: 22px;
padding: 0px 09px;
background-color: #d4d2e7;
}
:host
::ng-deep
.datatable-footer
.datatable-pager
ul
li:not(.disabled).active
a,
.ngx-datatable.bootstrap[_ngcontent-c11]
.datatable-footer[_ngcontent-c11]
.datatable-pager[_ngcontent-c11]
ul[_ngcontent-c11]
li[_ngcontent-c11]:not(.disabled):hover
a[_ngcontent-c11] {
background-color: #d4d2e7;
font-weight: bold;
font-size: larger;
}
:host ::ng-deep .ngx-datatable.bootstrap .datatable-footer .datatable-pager a {
height: 32px;
min-width: 34px;
line-height: 22px;
padding: 0;
border-radius: 3px;
margin: 0 3px;
text-align: center;
vertical-align: top;
padding-top: 3px;
text-decoration: none;
vertical-align: bottom;
color: #7c8091;
}
:host
::ng-deep
.ngx-datatable.bootstrap
.datatable-footer
.datatable-pager
.datatable-icon-left,
.ngx-datatable.bootstrap[_ngcontent-c11]
.datatable-footer[_ngcontent-c11]
.datatable-pager[_ngcontent-c11]
.datatable-icon-right[_ngcontent-c11],
.ngx-datatable.bootstrap[_ngcontent-c11]
.datatable-footer[_ngcontent-c11]
.datatable-pager[_ngcontent-c11]
.datatable-icon-prev[_ngcontent-c11] {
font-size: 14px;
line-height: 9px;
padding: 0px 08px;
background-color: #ffffff;
}
:host
::ng-deep
.ngx-datatable.bootstrap
.datatable-footer
.datatable-pager
.datatable-icon-left,
.ngx-datatable.bootstrap[_ngcontent-c11]
.datatable-footer[_ngcontent-c11]
.datatable-pager[_ngcontent-c11]
.datatable-icon-right[_ngcontent-c11],
.ngx-datatable.bootstrap[_ngcontent-c11]
.datatable-footer[_ngcontent-c11]
.datatable-pager[_ngcontent-c11]
.datatable-icon-prev[_ngcontent-c11] {
font-size: 0px;
line-height: 22px;
padding: 0px 09px;
background-color: #ffffff;
}
:host
::ng-deep
.ngx-datatable.bootstrap
.datatable-footer
.datatable-pager
.datatable-icon-right,
.ngx-datatable.bootstrap[_ngcontent-c11]
.datatable-footer[_ngcontent-c11]
.datatable-pager[_ngcontent-c11]
.datatable-icon-right[_ngcontent-c11],
.ngx-datatable.bootstrap[_ngcontent-c11]
.datatable-footer[_ngcontent-c11]
.datatable-pager[_ngcontent-c11]
.datatable-icon-prev[_ngcontent-c11] {
font-size: 0px;
line-height: 22px;
padding: 0px 09px;
background-color: #ffffff;
}
:host
::ng-deep
.ngx-datatable.bootstrap
.datatable-footer
.datatable-pager
.datatable-icon-skip,
.ngx-datatable.bootstrap[_ngcontent-c11]
.datatable-footer[_ngcontent-c11]
.datatable-pager[_ngcontent-c11]
.datatable-icon-right[_ngcontent-c11],
.ngx-datatable.bootstrap[_ngcontent-c11]
.datatable-footer[_ngcontent-c11]
.datatable-pager[_ngcontent-c11]
.datatable-icon-prev[_ngcontent-c11] {
font-size: 0px;
line-height: 22px;
padding: 0px 09px;
background-color: #ffffff;
}
:host
::ng-deep
.ngx-datatable.bootstrap
.datatable-footer
.datatable-pager
.datatable-icon-prev,
.ngx-datatable.bootstrap[_ngcontent-c11]
.datatable-footer[_ngcontent-c11]
.datatable-pager[_ngcontent-c11]
.datatable-icon-right[_ngcontent-c11],
.ngx-datatable.bootstrap[_ngcontent-c11]
.datatable-footer[_ngcontent-c11]
.datatable-pager[_ngcontent-c11]
.datatable-icon-prev[_ngcontent-c11] {
font-size: 0px;
line-height: 22px;
padding: 0px 09px;
background-color: #ffffff;
}
:host
::ng-deep
.ngx-datatable.bootstrap
.datatable-footer
.datatable-pager
ul
li:not(.disabled):hover
a {
background-color: #545454;
font-weight: bold;
color: white;
}
:host
::ng-deep
.ngx-datatable.bootstrap
.datatable-footer
.datatable-pager
ul
li:not(.disabled).active
a,
.ngx-datatable.bootstrap
.datatable-footer
.datatable-pager
ul
li:not(.disabled):hover
a {
background-color: #252525;
font-weight: bold;
color: white;
}
:host ::ng-deep .ngx-datatable.bootstrap .datatable-footer {
background: #DDE1E6;
color: #242222;
margin-top: -1px;
overflow: inherit;
}
:host ::ng-deep .ngx-datatable.bootstrap .datatable-header {
background: #DDE1E6;
color: #242222;
font-weight: bold;
height: unset !important;
overflow: inherit;
}
:host ::ng-deep .ngx-datatable .datatable-footer .datatable-pager {
flex: 0 0 0%;
}
:host ::ng-deep .ngx-datatable .datatable-footer .datatable-pager .pager {
display: flex;
}
:host
::ng-deep
.ngx-datatable
.datatable-footer
.selected-count
.datatable-pager {
flex: 0 0 0%;
}
:host ::ng-deep .ngx-datatable {
display: -webkit-box;
}
:host ::ng-deep .ng-select .ng-select-container {
color: #242222 !important;
background-color: #FBFBFB !important;
height: 40px !important;
border-radius: 12px !important;
box-shadow: 0 2px 4px rgba(36, 34, 34, 0.2) !important; /* Bayangan lebih tipis */
}
:host ::ng-deep .ng-select .ng-select-container .ng-value-container .ng-input>input {
color: #242222 !important;
}
:host ::ng-deep .ngx-datatable.bootstrap .datatable-body-row {
background-color: #FBFBFB; /* Black color for table rows */
}
:host ::ng-deep .ngx-datatable.bootstrap .datatable-body-row:hover {
background-color: #DDE1E6; /* Darker black for hover effect */
}
.text-custom-label{
color: #242222 !important;
font-family: "Open Sans", sans-serif !important;
font-size: 16px;
}
.text-custom-data{
color: #242222 !important;
font-family: "Open Sans", sans-serif !important;
font-size: 24px;
font-weight: 700;
}
.style-custom-label{
color: #242222 !important;
}
::ng-deep .modal-backdrop.show {
z-index: auto !important;
}

View File

@ -0,0 +1,156 @@
<div class="modal-body" style="background-color: #FBFBFB !important">
<h4 style="color: #242222; margin-bottom: 20px !important">
Comparison of Water and Electricity Costs > {{ dataRow.building }}
</h4>
<p style="color: #242222">Room : {{ dataRow?.roomName }}</p>
<p style="color: #242222">Category : {{ dataRow?.categoryName }}</p>
<div class="card-dashboard">
<ngx-datatable
class="bootstrap table-bordered"
[limit]="10"
[rows]="data_cost"
[columnMode]="'force'"
[headerHeight]="50"
[footerHeight]="50"
[rowHeight]="50"
fxFlex="auto"
[scrollbarH]="true"
>
<ngx-datatable-column name="#" [flexGrow]="0.5" [minWidth]="30">
<ng-template ngx-datatable-cell-template let-rowIndex="rowIndex">
<p style="color: #242222 !important">
{{ rowIndex + 1 }}
</p>
</ng-template>
</ngx-datatable-column>
<ngx-datatable-column name="periode" [flexGrow]="1" [minWidth]="90">
<ng-template ngx-datatable-header-template>
<span style="color: #242222 !important">Tanggal</span>
</ng-template>
<ng-template let-value="value" ngx-datatable-cell-template>
<p style="color: #242222 !important">
{{ value | date : "dd/MM/yyyy" }}
</p>
</ng-template>
</ngx-datatable-column>
<ngx-datatable-column name="deviceName" [flexGrow]="1" [minWidth]="90">
<ng-template ngx-datatable-header-template>
<span style="color: #242222 !important">Device</span>
</ng-template>
<ng-template let-value="value" ngx-datatable-cell-template>
<p style="color: #242222 !important">{{ value }}</p>
</ng-template>
</ngx-datatable-column>
<ngx-datatable-column name="roomName" [flexGrow]="1" [minWidth]="90">
<ng-template ngx-datatable-header-template>
<span style="color: #242222 !important">Room</span>
</ng-template>
<ng-template let-value="value" ngx-datatable-cell-template>
<p style="color: #242222 !important">{{ value }}</p>
</ng-template>
</ngx-datatable-column>
<ngx-datatable-column name="categoryName" [flexGrow]="1" [minWidth]="90">
<ng-template ngx-datatable-header-template>
<span style="color: #242222 !important">Category</span>
</ng-template>
<ng-template let-value="value" ngx-datatable-cell-template>
<p style="color: #242222 !important">{{ value }}</p>
</ng-template>
</ngx-datatable-column>
<ngx-datatable-column
name="estimationCost"
[flexGrow]="1"
[minWidth]="90"
>
<ng-template ngx-datatable-header-template>
<span style="color: #242222 !important">Estimation Cost</span>
</ng-template>
<ng-template let-value="value" ngx-datatable-cell-template>
<p style="color: #242222 !important">
{{
value.toLocaleString("id-ID", {
style: "currency",
currency: "IDR"
})
}}
</p>
</ng-template>
</ngx-datatable-column>
<ngx-datatable-column name="totalKwh" [flexGrow]="1" [minWidth]="90">
<ng-template ngx-datatable-header-template>
<span style="color: #242222 !important">Total Kwh</span>
</ng-template>
<ng-template let-value="value" ngx-datatable-cell-template>
<p style="color: #242222 !important">{{ value }}</p>
</ng-template>
</ngx-datatable-column>
<ngx-datatable-column name="watt" [flexGrow]="1" [minWidth]="90">
<ng-template ngx-datatable-header-template>
<span style="color: #242222 !important">Watt</span>
</ng-template>
<ng-template let-value="value" ngx-datatable-cell-template>
<p style="color: #242222 !important">{{ value }}</p>
</ng-template>
</ngx-datatable-column>
<ngx-datatable-column name="duration" [flexGrow]="1" [minWidth]="90">
<ng-template ngx-datatable-header-template>
<span style="color: #242222 !important">Duration</span>
</ng-template>
<ng-template let-value="value" ngx-datatable-cell-template>
<p style="color: #242222 !important">{{ value }}</p>
</ng-template>
</ngx-datatable-column>
<ngx-datatable-column name="priceKwh" [flexGrow]="1" [minWidth]="90">
<ng-template ngx-datatable-header-template>
<span style="color: #242222 !important">Price Kwh</span>
</ng-template>
<ng-template let-value="value" ngx-datatable-cell-template>
<p style="color: #242222 !important">
{{
value.toLocaleString("id-ID", {
style: "currency",
currency: "IDR"
})
}}
</p>
</ng-template>
</ngx-datatable-column>
</ngx-datatable>
</div>
</div>
<div
class="modal-footer justify-content-between"
style="background-color: #FBFBFB !important; border-style: none !important"
>
<button
type="button"
class="btn"
style="
color: #242222;
width: 25%;
background-color: #FBFBFB !important;
border-color: #242222 !important;
border-radius: 10px;
"
(click)="activeModal.dismiss('Cross click')"
>
Close
</button>
<button
type="button"
class="btn"
style="
color: #242222 !important;
width: 25%;
background-color: #DDE1E6 !important;
border-color: #DDE1E6 !important;
border-radius: 10px;
"
[disabled]="spinnerExportActive"
(click)="export()"
>
<i class="la la-spinner spinner" *ngIf="spinnerExportActive"></i>
Export
</button>
</div>

View File

@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ModalExportComponent } from './modal-export.component';
describe('ModalExportComponent', () => {
let component: ModalExportComponent;
let fixture: ComponentFixture<ModalExportComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ ModalExportComponent ]
})
.compileComponents();
fixture = TestBed.createComponent(ModalExportComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,103 @@
import { Component, Input } from "@angular/core";
import { NgbActiveModal } from "@ng-bootstrap/ng-bootstrap";
import { DeviceService } from "../../service/device.service";
import { CostManagementService } from "../../service/cost-management.service";
import { TableexcelService } from "src/app/_services/tableexcel.service";
import { LoginService } from "../../service/login.service";
@Component({
selector: "app-modal-export",
templateUrl: "./modal-export.component.html",
styleUrls: ["./modal-export.component.css"],
})
export class ModalExportComponent {
@Input() buildingId: number;
@Input() dataRow: any;
data: any;
filteredRows: any[];
data_device: any[];
kwhTerm: string = "";
costTerm: string = "";
data_cost: any;
dataExport: any;
formattedEndDate: any;
spinnerExportActive = false;
constructor(
public activeModal: NgbActiveModal,
private costService: CostManagementService,
private tableexcelService: TableexcelService,
private authService: LoginService
) {}
ngOnInit() {
this.authService.startTokenCheck();
this.authService.startTrackingActivity();
const dateRow = this.convertToUTC7(this.dataRow.endDate)
this.formattedEndDate = dateRow.slice(0, 7);
}
ngAfterViewInit(): void {
console.log(this.dataRow);
this.fetchData(
this.dataRow.categoryId,
this.dataRow.roomId,
this.formattedEndDate
);
}
fetchData(category, room, period) {
this.costService
.getCostDetail(category, room, period)
.subscribe((response) => {
this.dataExport = response.data;
console.log(this.dataExport);
this.data_cost = response.data.map((item) => ({
deviceName: item.device_name,
roomName: item.room_name,
categoryName: item.category_name,
estimationCost: item.estimation_cost,
totalKwh: item.total_kwh,
watt: item.watt,
duration: item.duration,
priceKwh: item.price_kwh,
// periode: item.periode,
periode: this.convertToUTC7(item.periode)
}));
console.log(this.data_cost);
});
}
convertToUTC7(dateString: string): string {
const date = new Date(dateString);
const utc7Offset = 7 * 60; // UTC+7 in minutes
const localOffset = date.getTimezoneOffset();
const totalOffset = utc7Offset - localOffset;
const utc7Date = new Date(date.getTime() + totalOffset * 60 * 1000);
return utc7Date.toISOString();
}
export() {
this.spinnerExportActive = true;
setTimeout(() => {
const columnsToExport = [
"periode",
"deviceName",
"roomName",
"categoryName",
"estimationCost",
"totalKwh",
"watt",
"duration",
"priceKwh",
];
this.tableexcelService.exportAsExcelFileManageDetail(
this.data_cost,
"export_detail_cost_management",
columnsToExport
);
this.spinnerExportActive = false;
this.activeModal.close("Export completed");
}, 3000);
}
}

View File

@ -0,0 +1,153 @@
.form-control.is-invalid {
border-color: #CACFE7;
background-image: none !important;
}
.div.basicInfoCard {
height: 700px;
}
:host ::ng-deep .block-ui-wrapper {
background: rgba(255, 249, 249, 0.5) !important;
}
:host ::ng-deep .block-ui-wrapper {
background: rgba(255, 249, 249, 0.5) !important;
}
:host ::ng-deep .btn-light:not(:disabled):not(.disabled):active {
color: unset !important;
background-color: unset !important;
border-color: #d3d9df !important;
}
:host ::ng-deep .btn-light:hover:not(.disabled):active {
background-color: #e2e6ea !important;
border-color: #dae0e5 !important;
}
:host ::ng-deep .btn-light {
color: unset !important;
background-color: unset !important;
border-color: unset !important;
}
:host ::ng-deep .bg-primary {
background-color: #007bff !important;
}
:host ::ng-deep .text-white {
color: #fff !important;
}
:host ::ng-deep .custom-day {
text-align: center;
padding: .185rem .25rem;
display: inline-block;
height: 2rem;
width: 2rem;
}
:host ::ng-deep .custom-day:active {
color: #6d7183 !important;
background-color: #fff !important;
border-block-color: rgb(2, 117, 216) !important;
}
.bg-light {
background-color: #f8f9fa !important;
}
:host ::ng-deep .hidden {
display: block !important;
}
.ngb-dp-weekday {
color: #17a2b8;
}
.ngb-dp-week-number,
.ngb-dp-weekday {
line-height: 2rem;
text-align: center;
font-style: italic;
}
.ngb-datepicker-month-view {
pointer-events: auto;
}
.small {
font-size: 80%;
font-weight: 400;
}
.ngb-dp-day {
cursor: pointer !important;
}
.ngb-dp-month {
pointer-events: none;
}
.btn-light:hover {
color: #212529 !important;
background-color: #e2e6ea !important;
border-color: #dae0e5 !important;
}
.ngb-datepicker-month-view {
pointer-events: auto;
}
:host ::ng-deep .ngb-dp-header {
background-color: var(--light) !important;
}
:host ::ng-deep .ngb-dp-weekdays {
background-color: var(--light);
}
:host ::ng-deep .ngb-dp-month-name {
background-color: var(--light);
}
.ngb-dp-header {
border-bottom: 0;
border-radius: .25rem .25rem 0 0;
padding-top: .25rem;
}
.ngb-dp-day,
.ngb-dp-week-number,
.ngb-dp-weekday {
width: 2rem;
height: 2rem;
}
.custom-day {
text-align: center;
padding: 0.185rem 0.25rem;
display: inline-block;
height: 2rem;
width: 2rem;
}
.custom-day.focused {
background-color: #e6e6e6;
}
.custom-day.range,
.custom-day:hover {
background-color: rgb(2, 117, 216);
color: white;
}
.custom-day.faded {
background-color: rgba(2, 117, 216, 0.5);
}
:host ::ng-deep .block-ui-wrapper {
background: rgba(255, 249, 249, 0.5) !important;
}

View File

@ -0,0 +1,294 @@
<div class="app-content content" style="background-color: #fbfbfb !important">
<div class="content-wrapper">
<div class="content-header row mb-1">
<app-breadcrumb class="col-12" [breadcrumb]="breadcrumb"></app-breadcrumb>
</div>
<div class="content-body">
<section id="basic-form-layouts">
<div class="row">
<div class="col-12" *blockUI="'projectInfo'; message: 'Loading'">
<div class="card" style="background-color: #fbfbfb !important">
<div class="card-content">
<div
class="card-header"
style="background-color: #fbfbfb !important"
>
<h2 style="color: #242222">
{{
isEditMode()
? "Edit Device"
: isViewMode()
? "View Device"
: "Add New Device"
}}
</h2>
</div>
<div class="card-body">
<form
[formGroup]="projectInfo"
(ngSubmit)="onProjectInfoSubmit()"
>
<div class="form-body">
<h4 class="form-section" style="color: #242222">
<i class="feather ft-user" style="color: #242222"></i>
General Information
</h4>
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label for="name" style="color: #242222"
>Device Name *</label
>
<input
type="text"
id="name"
class="form-control"
formControlName="name"
placeholder="Device Name"
maxlength="50"
[ngClass]="{
'is-invalid': submitted && f.name.errors
}"
/>
<small
class="form-text text-muted danger"
*ngIf="submitted && f.name.errors"
class="invalid-feedback"
>
<div *ngIf="f.name.errors.required">
Device Name is required
</div>
</small>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label for="categoryId" style="color: #242222"
>Category *</label
>
<div class="input-group">
<select
id="categoryId"
class="form-control custom-select"
formControlName="categoryId"
[ngClass]="{
'is-invalid': submitted && f.categoryId.errors
}"
>
<option
*ngFor="let data of dataMasterCategori"
[value]="data.id"
>
{{ data.name }}
</option>
</select>
</div>
<div
*ngIf="
projectInfo.get('categoryId').invalid &&
projectInfo.get('categoryId').touched
"
class="text-danger"
>
<small class="text-danger"
>Category is required.</small
>
</div>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label for="voltageId" style="color: #242222"
>Voltage *</label
>
<div class="input-group">
<select
id="voltageId"
class="form-control custom-select"
formControlName="voltageId"
[ngClass]="{
'is-invalid': submitted && f.voltageId.errors
}"
>
<option
*ngFor="let data of dataMasterVoltage"
[value]="data.id"
>
{{ data.name }}
</option>
</select>
</div>
<div
*ngIf="
projectInfo.get('voltageId').invalid &&
projectInfo.get('voltageId').touched
"
class="text-danger"
>
<small class="text-danger"
>Voltage is required.</small
>
</div>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label for="typeId" style="color: #242222"
>Type *</label
>
<select
id="typeId"
class="form-control custom-select"
formControlName="typeId"
[ngClass]="{
'is-invalid': submitted && f.typeId.errors
}"
>
<option
*ngFor="let data of dataMasterType"
[value]="data.id"
>
{{ data.name }}
</option>
</select>
<small
class="form-text text-muted danger"
*ngIf="submitted && f.typeId.errors"
class="invalid-feedback"
>
<div *ngIf="f.typeId.errors.required">
Type is required
</div>
</small>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label for="durationId" style="color: #242222"
>Duration Use *</label
>
<select
id="durationId"
class="form-control custom-select"
formControlName="durationId"
[ngClass]="{
'is-invalid': submitted && f.durationId.errors
}"
>
<option
*ngFor="let data of dataMasterDuration"
[value]="data.id"
>
{{ data.name }}
</option>
</select>
<small
class="form-text text-muted danger"
*ngIf="submitted && f.durationId.errors"
class="invalid-feedback"
>
<div *ngIf="f.durationId.errors.required">
Duration is required
</div>
</small>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label for="roomBuildingId" style="color: #242222"
>Location Room *</label
>
<select
id="roomBuildingId"
class="form-control custom-select"
formControlName="roomBuildingId"
[ngClass]="{
'is-invalid':
submitted && f.roomBuildingId.errors
}"
>
<option
*ngFor="let data of dataBuildingRoomList"
[value]="data.id"
>
{{ data.name }}
</option>
</select>
<small
class="form-text text-muted danger"
*ngIf="submitted && f.roomBuildingId.errors"
class="invalid-feedback"
>
<div *ngIf="f.roomBuildingId.errors.required">
location room is required
</div>
</small>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label for="watt" style="color: #242222"
>Watt *</label
>
<input
type="text"
id="watt"
class="form-control"
formControlName="watt"
maxlength="10"
placeholder="Watt"
[ngClass]="{
'is-invalid': submitted && f.watt.errors
}"
/>
<div
*ngIf="
projectInfo.get('watt').invalid &&
projectInfo.get('watt').touched
"
class="text-danger"
>
<small class="text-danger">Please enter a valid Watt.</small>
</div>
</div>
</div>
</div>
</div>
<div class="form-actions">
<button
type="button"
class="btn btn-warning mr-1"
style="
color: #242222 !important;
background-color: #fbfbfb !important;
border-color: #fbfbfb !important;
"
(click)="cancel()"
>
<i *ngIf="mode === 'edit'" class="feather ft-x"></i>
<i *ngIf="mode === 'view'" class="la la-arrow-left"></i>
{{ mode === "edit" ? "Cancel" : "Back" }}
</button>
<button
type="submit"
class="btn btn-primary"
style="
color: #ffffff !important;
background-color: #37a647 !important;
"
(click)="saveEdit()"
*ngIf="mode === 'edit'"
>
<i class="la la-check"></i> Save
</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</section>
</div>
</div>
</div>

View File

@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { AddEditDeviceComponent } from './add-edit-device.component';
describe('AddEditDeviceComponent', () => {
let component: AddEditDeviceComponent;
let fixture: ComponentFixture<AddEditDeviceComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ AddEditDeviceComponent ]
})
.compileComponents();
fixture = TestBed.createComponent(AddEditDeviceComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,308 @@
import { Component, OnInit, ViewChild } from "@angular/core";
import { FormBuilder, FormGroup, NgForm, Validators } from "@angular/forms";
import { ActivatedRoute, Router } from "@angular/router";
import { BlockUI, NgBlockUI } from "ng-block-ui";
import { BuildingService } from "../../service/monitoring-api.service";
import { LoginService } from "../../service/login.service";
import Swal from "sweetalert2";
@Component({
selector: "app-add-edit-device",
templateUrl: "./add-edit-device.component.html",
styleUrls: ["./add-edit-device.component.css"],
})
export class AddEditDeviceComponent implements OnInit {
@ViewChild("f", { read: true }) userProfileForm: NgForm;
dataMasterCategori: any;
dataMasterVoltage: any;
dataMasterType: any;
dataMasterDuration: any;
dataMasterStatus: any;
dataBuildingRoomList: any;
deviceId: any;
model: any = {};
dataDevice: any;
mode: string;
@BlockUI("projectInfo") blockUIProjectInfo: NgBlockUI;
public breadcrumb: any;
projectInfo: FormGroup;
submitted = false;
constructor(
private formBuilder: FormBuilder,
private router: Router,
private route: ActivatedRoute,
private monitoringApiService: BuildingService,
private authService: LoginService
) {}
ngOnInit() {
this.authService.startTokenCheck();
this.authService.startTrackingActivity();
this.route.params.subscribe((params) => {
const id = params["id"];
this.deviceId = id;
if (id) {
this.loadDevice(id);
this.dataListMaster();
this.dataListRoomBuilding();
}
});
this.setBreadcrumb();
this.route.data.subscribe((data) => {
this.mode = data.mode;
});
this.projectInfo = this.formBuilder.group({
name: ["", Validators.required],
categoryId: ["", Validators.required],
voltageId: ["", Validators.required],
typeId: ["", Validators.required],
durationId: ["", Validators.required],
roomBuildingId: ["", Validators.required],
watt: ["", [Validators.required, Validators.maxLength(15), Validators.pattern(/^[0-9]{1,7}$/)]],
});
}
loadDevice(deviceId: string) {
this.monitoringApiService.getDeviceById(deviceId).subscribe((data) => {
this.dataDevice = data;
console.log(this.dataDevice);
this.formGetDevice(data);
});
}
dataListMaster() {
this.monitoringApiService.getMasterListData().subscribe((data) => {
const dataCategory = data.data.find(
(item) => item.name === "master_category"
).headerDetailParam;
this.dataMasterCategori = dataCategory.filter(
(item) =>
item.statusName.toLowerCase() === "aktif" ||
item.status.toLowerCase() === "71"
);
const dataVoltage = data.data.find(
(item) => item.name === "master_voltage"
).headerDetailParam;
this.dataMasterVoltage = dataVoltage.filter(
(item) =>
item.statusName.toLowerCase() === "aktif" ||
item.status.toLowerCase() === "71"
);
const dataType = data.data.find(
(item) => item.name === "master_type"
).headerDetailParam;
this.dataMasterType = dataType.filter(
(item) =>
item.statusName.toLowerCase() === "aktif" ||
item.status.toLowerCase() === "71"
);
const dataDuration = data.data.find(
(item) => item.name === "master_duration"
).headerDetailParam;
this.dataMasterDuration = dataDuration.filter(
(item) =>
item.statusName.toLowerCase() === "aktif" ||
item.status.toLowerCase() === "71"
);
const dataStatus = data.data.find(
(item) => item.name === "master_status"
).headerDetailParam;
this.dataMasterStatus = dataStatus.filter(
(item) =>
item.statusName.toLowerCase() === "aktif" ||
item.status.toLowerCase() === "71"
);
});
}
dataListRoomBuilding() {
this.monitoringApiService.getBuildingRoomList().subscribe((data) => {
const newArray = data.results.data.map((item) => ({
id: item.id,
name: `${item.buildingEntity.name} (${item.roomEntity.name})`,
}));
this.dataBuildingRoomList = newArray;
});
}
formGetDevice(data) {
this.projectInfo.patchValue({
name: data.data.name,
categoryId: data.data.categoryId,
voltageId: data.data.voltageId,
typeId: data.data.typeId,
durationId: data.data.durationId,
roomBuildingId: data.data.roomBuildingId,
watt: data.data.watt,
});
if (this.mode === "view") {
this.formDisable();
}
if (this.projectInfo.get("roomBuildingId").value) {
this.projectInfo.get("roomBuildingId").disable();
}
}
formDisable() {
this.projectInfo.get("name").disable();
this.projectInfo.get("categoryId").disable();
this.projectInfo.get("voltageId").disable();
this.projectInfo.get("typeId").disable();
this.projectInfo.get("durationId").disable();
this.projectInfo.get("roomBuildingId").disable();
this.projectInfo.get("watt").disable();
}
setBreadcrumb() {
if (this.isAddMode()) {
this.breadcrumb = {
mainlabel: "Device",
links: [
{
name: "Home",
isLink: false,
},
{
name: "Device",
isLink: false,
},
{
name: "Add new device",
isLink: false,
},
],
};
} else if (this.isEditMode()) {
this.breadcrumb = {
mainlabel: "Device",
links: [
{
name: "Home",
isLink: false,
},
{
name: "Device",
isLink: false,
},
{
name: "Edit new device",
isLink: false,
},
],
};
} else if (this.isViewMode()) {
this.breadcrumb = {
mainlabel: "Device",
links: [
{
name: "Home",
isLink: false,
},
{
name: "Device",
isLink: false,
},
{
name: "View new device",
isLink: false,
},
],
};
}
}
isEditMode() {
return this.mode === "edit";
}
isViewMode() {
return this.mode === "view";
}
isAddMode() {
return this.mode === "add";
}
get f() {
return this.projectInfo.controls;
}
onProjectInfoSubmit() {
this.submitted = true;
if (this.projectInfo.invalid) {
return;
}
}
keyPress(event: any) {
const pattern = /[0-9\+\-\ ]/;
const inputChar = String.fromCharCode(event.charCode);
if (event.keyCode !== 8 && !pattern.test(inputChar)) {
event.preventDefault();
}
}
saveEdit() {
if (this.projectInfo.valid) {
if (this.dataDevice.data.roomBuildingId) {
this.monitoringApiService
.putDevice(this.projectInfo.value, this.deviceId)
.subscribe((data) => {
this.router.navigate(["/device"]);
});
} else {
Swal.fire({
title: "Apakah kamu yakin?",
text: "Data lokasi device tidak dapat diubah!",
icon: "warning",
showCancelButton: true,
confirmButtonColor: "#37a647",
cancelButtonColor: "#d33",
confirmButtonText: "Yes, save it!",
allowOutsideClick: false,
}).then((result) => {
if (result.isConfirmed) {
this.monitoringApiService
.putDevice(this.projectInfo.value, this.deviceId)
.subscribe((data) => {
this.router.navigate(["/device"]);
});
Swal.fire({
title: "Saved!",
text: "Data berhasil disimpan.",
icon: "success",
});
}
});
}
} else {
this.markFormGroupTouched(this.projectInfo)
}
}
markFormGroupTouched(formGroup: FormGroup) {
(Object as any).values(formGroup.controls).forEach((control) => {
control.markAsTouched();
if (control.controls) {
this.markFormGroupTouched(control);
}
});
}
cancel() {
this.router.navigate(["/device"]);
}
}

View File

@ -0,0 +1,3 @@
::ng-deep .modal-backdrop.show {
z-index: auto !important;
}

View File

@ -0,0 +1,144 @@
<div class="modal-header" style="background-color: #fbfbfb !important">
<h4 class="modal-title" style="color: #242222">{{ labelModal }}</h4>
<button
type="button"
class="close"
aria-label="Close"
(click)="activeModal.dismiss('Cross click')"
>
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body" style="background-color: #fbfbfb !important">
<form [formGroup]="myForm">
<div class="form-row">
<div class="form-group col-md-12">
<label for="name" style="color: #242222">Name:</label>
<input
type="text"
class="form-control"
id="name"
formControlName="name"
maxlength="50"
/>
<div
*ngIf="myForm.get('name').touched && myForm.get('name').invalid"
class="text-danger"
>
Name is required.
</div>
</div>
<div class="form-group col-md-12">
<label for="timeset" style="color: #242222">Time</label>
<input
type="time"
class="form-control"
formControlName="timeset"
id="timeset"
/>
<div
*ngIf="myForm.get('timeset').touched && myForm.get('timeset').invalid"
class="text-danger"
>
Time On is required.
</div>
</div>
<div class="form-group col-md-12">
<label for="switch" style="color: #242222">Switch:</label>
<select
id="switch"
class="form-control custom-select"
formControlName="switch"
>
<option *ngFor="let data of dataSwitch" [value]="data.value">
{{ data.label }}
</option>
</select>
<div
*ngIf="myForm.get('switch').touched && myForm.get('switch').invalid"
class="text-danger"
>
Switch is required.
</div>
</div>
<!-- Checkbox for Days of the Week -->
<div class="form-group col-md-12">
<label style="color: #242222">Select Days:</label>
<div *ngFor="let day of daysOfWeek">
<div class="form-check">
<input
type="checkbox"
class="form-check-input"
[formControlName]="day.value"
(change)="onDayChange(day.value, $event.target.checked)"
/>
<label class="form-check-label" style="color: #242222">{{
day.label
}}</label>
</div>
</div>
<div *ngIf="myForm.hasError('noDaySelected') && myForm.touched" class="text-danger">
Please select at least one day.
</div>
</div>
<!-- Checkbox for Repeat -->
<div class="form-group col-md-12">
<label style="color: #242222">Repeat:</label>
<div class="form-check">
<input
type="checkbox"
class="form-check-input"
formControlName="recurring"
/>
<label class="form-check-label" style="color: #242222">Yes</label>
</div>
</div>
<!-- <div class="form-group col-md-12">
<label for="active" style="color: #242222">Active:</label>
<select
id="active"
class="form-control custom-select"
formControlName="active"
>
<option *ngFor="let data of dataActive" [value]="data.value">
{{ data.label }}
</option>
</select>
<div
*ngIf="myForm.get('active').touched && myForm.get('active').invalid"
class="text-danger"
>
Active is required.
</div>
</div> -->
</div>
</form>
</div>
<div class="modal-footer" style="background-color: #fbfbfb !important">
<button
type="button"
class="btn btn-secondary"
style="
color: #242222 !important;
background-color: #fbfbfb !important;
border-color: #fbfbfb !important;
"
(click)="activeModal.dismiss('Cross click')"
>
Close
</button>
<button
type="button"
style="color: #ffffff !important; background-color: #37a647 !important"
class="btn btn-primary"
(click)="save()"
>
Save
</button>
</div>

View File

@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ControlSchedulerComponent } from './control-scheduler.component';
describe('ControlSchedulerComponent', () => {
let component: ControlSchedulerComponent;
let fixture: ComponentFixture<ControlSchedulerComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ ControlSchedulerComponent ]
})
.compileComponents();
fixture = TestBed.createComponent(ControlSchedulerComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,175 @@
import { Component, Input } from "@angular/core";
import {
FormBuilder,
FormControl,
FormGroup,
Validators,
} from "@angular/forms";
import { NgbActiveModal } from "@ng-bootstrap/ng-bootstrap";
import { DeviceService } from "../../service/device.service";
@Component({
selector: "app-control-scheduler",
templateUrl: "./control-scheduler.component.html",
styleUrls: ["./control-scheduler.component.css"],
})
export class ControlSchedulerComponent {
@Input() deviceId: any;
@Input() data: any;
@Input() mode: any;
labelModal: string = "";
myForm: FormGroup;
dataSwitch = [
{
label: "On",
value: true,
},
{
label: "Off",
value: false,
},
];
dataActive = [
{
label: "Active",
value: true,
},
{
label: "Non Active",
value: false,
},
];
daysOfWeek = [
{ label: "Monday", value: "monday" },
{ label: "Tuesday", value: "tuesday" },
{ label: "Wednesday", value: "wednesday" },
{ label: "Thursday", value: "thursday" },
{ label: "Friday", value: "friday" },
{ label: "Saturday", value: "saturday" },
{ label: "Sunday", value: "sunday" },
];
selectedDays: string[] = [];
constructor(
public activeModal: NgbActiveModal,
private fb: FormBuilder,
private deviceService: DeviceService
) {}
ngOnInit() {
this.createForm();
if (this.mode === "add") {
this.labelModal = "Add Scheduler";
} else if (this.mode === "edit") {
this.editForm();
this.labelModal = "Edit Scheduler";
}
}
createForm() {
const controls = {
name: ["", Validators.required],
timeset: ["", Validators.required],
active: [true],
switch: [false],
recurring: [true], // Default value for repeat checkbox
device_id: this.deviceId,
};
// Initialize checkboxes for each day of the week
this.daysOfWeek.forEach((day) => {
controls[day.value] = [false]; // Each day starts as unchecked
});
this.myForm = this.fb.group(controls);
this.myForm.setValidators(this.atLeastOneDaySelectedValidator());
}
editForm() {
this.selectedDays = this.data.days || [];
this.myForm.patchValue({
name: this.data.name,
timeset: this.data.timeset,
recurring: this.data.recurring,
active: this.data.active,
switch: this.data.switch,
});
// Patch the form with the selected days
this.selectedDays.forEach((day) => {
this.myForm.patchValue({
[day]: true,
});
});
}
atLeastOneDaySelectedValidator() {
return (formGroup: FormGroup) => {
const selectedDays = this.daysOfWeek.some(
(day) => formGroup.get(day.value)?.value
);
return selectedDays ? null : { noDaySelected: true };
};
}
onDayChange(day: string, isChecked: boolean) {
if (isChecked) {
this.selectedDays.push(day);
} else {
this.selectedDays = this.selectedDays.filter((d) => d !== day);
}
}
save() {
if (this.myForm.valid) {
const formValues = this.myForm.value;
// Collect selected days' labels
const selectedDays = this.daysOfWeek
.filter((day) => formValues[day.value])
.map((day) => day.value);
// Construct the final output
let result = {};
if (this.mode === "add") {
result = {
name: formValues.name,
device_id: formValues.device_id,
timeset: formValues.timeset,
recurring: formValues.recurring,
active: formValues.active,
switch: formValues.switch,
days: selectedDays,
};
} else {
result = {
id: this.data.id,
name: formValues.name,
device_id: formValues.device_id,
timeset: formValues.timeset,
recurring: formValues.recurring,
active: formValues.active,
switch: formValues.switch,
days: selectedDays,
};
}
this.activeModal.close(result);
} else {
this.markFormGroupTouched(this.myForm);
}
}
markFormGroupTouched(formGroup: FormGroup) {
(Object as any).values(formGroup.controls).forEach((control) => {
control.markAsTouched();
if (control.controls) {
this.markFormGroupTouched(control);
}
});
}
}

View File

@ -0,0 +1,54 @@
:host ::ng-deep .ng-select .ng-select-container {
color: #242222 !important;
background-color: #FBFBFB !important;
height: 40px !important;
border-radius: 12px !important;
box-shadow: 0 2px 4px rgba(36, 34, 34, 0.2) !important; /* Bayangan lebih tipis */
}
.background-round {
background-color: #252525 !important;
padding: 8px;
border-radius: 50%;
border: 2px solid #BEF264;
border-color: #BEF264 !important;
}
.text-custom-name{
color: #242222 !important;
font-family: "Open Sans", sans-serif !important;
font-size: 16px;
font-weight: 600;
}
.text-custom-category{
color: #242222 !important;
font-family: "Open Sans", sans-serif !important;
font-size: 12px;
}
.menu-popup {
position: absolute;
right: 0;
top: 30px;
background-color: white;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
border-radius: 5px;
z-index: 1000;
}
.menu-popup ul {
list-style-type: none;
margin: 0;
padding: 0;
}
.menu-popup ul li {
padding: 10px 20px;
cursor: pointer;
}
.menu-popup ul li:hover {
background-color: #f0f0f0;
}

View File

@ -0,0 +1,208 @@
<div class="app-content content" style="background-color: #fbfbfb !important">
<div class="content-wrapper">
<div class="content-header row mb-1">
<app-breadcrumb class="col-12" [breadcrumb]="breadcrumb"></app-breadcrumb>
</div>
<div class="content-body">
<section id="configuration">
<div class="row">
<div class="col-12">
<div
class="card"
style="
background-color: #fbfbfb !important;
box-shadow: none !important;
"
>
<div class="card-content">
<div class="card-body">
<div class="row">
<div class="col-3">
<div class="form-group">
<ng-select
class="select-custom"
[items]="dataBuildingList"
[searchable]="true"
bindLabel="name"
bindValue="id"
placeholder="Select Building"
[(ngModel)]="buildingSelected"
style="width: 100% !important"
>
</ng-select>
</div>
</div>
<div class="col-3">
<div class="form-group">
<ng-select
class="select-custom"
[items]="dataMasterCategori"
[searchable]="true"
bindLabel="name"
bindValue="id"
placeholder="Select Category"
[(ngModel)]="categorySelected"
>
</ng-select>
</div>
</div>
<div class="col-3">
<div class="form-group">
<ng-select
class="select-custom"
[items]="dataMasterStatus"
[searchable]="true"
bindLabel="name"
bindValue="id"
placeholder="Select Status"
[(ngModel)]="statusSelected"
>
</ng-select>
</div>
</div>
<div class="col-3 text-left">
<div class="d-flex">
<button
type="button"
class="btn ml-2"
(click)="doFilter()"
style="
background-color: #37a647 !important;
border-color: #ffffff !important;
border-radius: 12px;
color: #ffffff;
"
[disabled]="spinnerFilterActive"
>
<i
class="la la-search"
style="color: #ffffff !important"
*ngIf="!spinnerFilterActive"
></i>
<i
class="la la-spinner spinner"
style="color: #ffffff !important"
*ngIf="spinnerFilterActive"
></i>
</button>
<button
type="button"
class="btn btn-outline-success ml-2"
(click)="doCancelFilter()"
style="
background-color: #fbfbfb !important;
border-color: #6b6b6b !important;
color: #6b6b6b;
border-radius: 12px;
"
>
Cancel
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<section id="configuration">
<div class="row">
<div class="col-12" *ngIf="filteredRows?.length === 0">
<div
class="card"
style="
background-color: #fbfbfb !important;
box-shadow: none !important;
"
>
<div class="card-content">
<div class="card-body">
<p class="text-center" style="color: #242222">
No data available
</p>
</div>
</div>
</div>
</div>
<div class="col-lg-4 col-12" *ngFor="let item of filteredDeviceRows">
<div
class="card"
style="background-color: #dde1e6; position: relative"
>
<div class="card-content">
<div class="card-body">
<div class="media d-flex">
<div class="align-self-center">
<div
style="
background-color: #37a647;
border-radius: 50%;
width: 50px;
height: 50px;
display: flex;
align-items: center;
justify-content: center;
"
>
<i
class="{{
item.category_icon
}} font-large-1 blue-grey d-block"
style="color: #ffffff !important"
></i>
</div>
<div style="margin-top: 10px">
<span class="text-custom-name">{{ item.name }}</span>
<br />
<span class="text-custom-category">{{
item.category_name
}}</span>
</div>
</div>
<div style="position: absolute; top: 10px; right: 10px">
<button
(click)="toggleMenu(item.id)"
class="btn"
style="
background: none;
border: none;
font-size: 20px;
padding: 0;
"
>
&#x22EE;
</button>
<div *ngIf="item.menuOpen" class="menu-popup">
<ul>
<li (click)="schedulerItem(item)">scheduler</li>
</ul>
</div>
</div>
<div
class="ui-switch-container"
style="position: absolute; bottom: 10px; right: 10px"
>
<ui-switch
style="border-color: #bef264 !important"
class="switchery"
switchColor="black"
color="rgb(55, 166, 71)"
size="small"
[checked]="item.status_id === 2"
(change)="switchChanged($event, item)"
></ui-switch>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
</div>
</div>
</div>

View File

@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { DeviceControlComponent } from './device-control.component';
describe('DeviceControlComponent', () => {
let component: DeviceControlComponent;
let fixture: ComponentFixture<DeviceControlComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ DeviceControlComponent ]
})
.compileComponents();
fixture = TestBed.createComponent(DeviceControlComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,213 @@
import { Component, HostListener } from "@angular/core";
import { Router } from "@angular/router";
import { BuildingService } from "../../service/monitoring-api.service";
import { DeviceService } from "../../service/device.service";
import { ToastrService } from "ngx-toastr";
import { LoginService } from "../../service/login.service";
import { NgbModal } from "@ng-bootstrap/ng-bootstrap";
import { ControlSchedulerComponent } from "../control-scheduler/control-scheduler.component";
@Component({
selector: "app-device-control",
templateUrl: "./device-control.component.html",
styleUrls: ["./device-control.component.css"],
})
export class DeviceControlComponent {
data: any;
filteredRows: any[];
filteredDeviceRows: any[];
searchTerm: string = "";
buildingSelected: any;
statusSelected: any;
categorySelected: any;
dataMasterCategori: any;
dataBuildingList: any;
dataMasterStatus: any;
storedData: any;
spinnerFilterActive = false;
switchState: boolean;
public breadcrumb: any;
spinnerActive: boolean = false;
singlebasicSelected: any;
constructor(
private router: Router,
private deviceService: DeviceService,
private monitoringApiService: BuildingService,
private toastr: ToastrService,
private authService: LoginService,
private modalService: NgbModal,
) {}
ngOnInit() {
this.authService.startTokenCheck();
this.authService.startTrackingActivity();
this.breadcrumb = {
mainlabel: "Device",
links: [
{
name: "Home",
isLink: false,
link: "/dashboard/sales",
},
{
name: "Device",
isLink: false,
},
],
};
this.storedData = JSON.parse(localStorage.getItem("currentUser"));
this.buildingSelected = this.storedData.buildingId;
this.fetchData(
this.buildingSelected,
this.categorySelected,
this.statusSelected
);
this.dataListMaster();
this.dataListBuilding();
}
fetchData(buildingSelected, categorySelected, statusSelected) {
this.deviceService
.getDeviceData(buildingSelected, categorySelected, statusSelected)
.subscribe((res) => {
this.data = res;
this.filteredRows = this.data.results.data;
this.filteredDeviceRows = this.filterDevices(this.filteredRows);
});
}
filterDevices(devices: any[]): any[] {
return devices.filter((device) =>
device.mapping.some(
(map) => map.code.startsWith("switch") && map.type === "Boolean"
)
);
}
dataListMaster() {
this.monitoringApiService.getMasterListData().subscribe((data) => {
const dataCategory = data.data.find(
(item) => item.name === "master_category"
).headerDetailParam;
const dataStatus = data.data.find(
(item) => item.name === "master_status"
).headerDetailParam;
this.dataMasterCategori = dataCategory.filter(
(item) => item.statusName.toLowerCase() === "aktif"
);
this.dataMasterStatus = dataStatus.filter((item) => item.statusName.toLowerCase() === "aktif");
});
}
dataListBuilding() {
this.monitoringApiService.getBuildingList().subscribe((data) => {
this.dataBuildingList = data.data.filter((item) => item.statusName.toLowerCase() === "aktif");
});
}
doFilter() {
if (!this.buildingSelected) {
this.toastr.error("Warning", "Filter Building tidak boleh kosong.", {
timeOut: 5000,
closeButton: true,
});
} else {
this.spinnerFilterActive = true;
this.fetchData(
this.buildingSelected,
this.categorySelected,
this.statusSelected
);
setTimeout(() => {
this.spinnerFilterActive = false;
}, 3000);
}
}
doCancelFilter() {
this.storedData = JSON.parse(localStorage.getItem("currentUser"));
this.buildingSelected = this.storedData.buildingId;
this.categorySelected = undefined;
this.statusSelected = undefined;
this.fetchData(this.buildingSelected, 0, 0);
}
filterRows() {
if (!this.searchTerm) {
this.filteredRows = [...this.data.results.data];
} else {
this.filteredRows = this.data.results.data.filter((row) =>
this.rowContainsSearchTerm(row)
);
}
}
rowContainsSearchTerm(row: any): boolean {
const searchTermLC = this.searchTerm.toLowerCase();
return Object.values(row).some(
(value) =>
value !== null && value.toString().toLowerCase().includes(searchTermLC)
);
}
switchChanged(ev, data) {
const requestData = {
id: data.id,
code: data.mapping[0].code,
value: ev,
command_type: "on_off",
};
this.deviceService.deviceSwitch(requestData).subscribe((res) => {
console.log(res);
});
}
toggleMenu(itemId) {
this.filteredDeviceRows.forEach(item => {
if (item.id === itemId) {
item.menuOpen = !item.menuOpen;
} else {
item.menuOpen = false; // Close other menus
}
});
}
schedulerItem(row) {
console.log(row);
this.router.navigate(["/device/scheduler", row.id]);
}
// schedulerItem(item) {
// const modalRef = this.modalService.open(ControlSchedulerComponent, {
// size: "md",
// backdrop: "static",
// keyboard: false,
// centered: true
// });
// modalRef.componentInstance.device = item;
// modalRef.result.then(
// (result) => {
// if (result) {
// console.log(result);
// }
// },
// (reason) => {
// console.log(`Dismissed: ${reason}`);
// }
// );
// // Handle edit action
// }
@HostListener('document:click', ['$event'])
clickout(event) {
if (!event.target.closest('.menu-popup') && !event.target.closest('.btn')) {
this.filteredDeviceRows.forEach(item => {
item.menuOpen = false;
});
}
}
}

View File

@ -0,0 +1,350 @@
:host
::ng-deep
.ngx-datatable.bootstrap
.datatable-header
.datatable-header-cell
.datatable-header-cell-label {
font-family: inherit;
font-size: medium;
font-weight: bold;
color: #6b6f82;
}
:host ::ng-deep .ngx-datatable .datatable-row-center,
.ngx-datatable .datatable-row-group,
.ngx-datatable .datatable-row-right {
position: relative;
height: 50px !important;
}
:host ::ng-deep .datatable-icon-right:before {
font-family: "icofont";
font-style: normal;
}
:host ::ng-deep .datatable-icon-skip:before {
font-family: "icofont";
font-style: normal;
}
:host ::ng-deep .datatable-icon-left:before {
font-family: "icofont";
font-style: normal;
}
:host ::ng-deep .datatable-icon-left:before {
content: "\2039";
font-size: x-large;
}
:host ::ng-deep .datatable-icon-prev:before {
content: "\00AB";
font-size: x-large;
}
:host ::ng-deep .datatable-icon-right:before {
content: "\203A";
font-size: x-large;
}
:host ::ng-deep .datatable-icon-skip:before {
content: "\00BB";
font-size: x-large;
}
:host
::ng-deep
.ngx-datatable.bootstrap
.datatable-footer
.datatable-pager
.datatable-icon-left,
.ngx-datatable.bootstrap
.datatable-footer
.datatable-pager
.datatable-icon-right,
.ngx-datatable.bootstrap
.datatable-footer
.datatable-pager
.datatable-icon-prev {
font-size: 16px;
line-height: 22px;
padding: 0px 09px;
background-color: #d4d2e7;
}
:host
::ng-deep
.ngx-datatable.bootstrap
.datatable-footer
.datatable-pager
.datatable-icon-right,
.ngx-datatable.bootstrap
.datatable-footer
.datatable-pager
.datatable-icon-right {
font-size: 16px;
line-height: 22px;
padding: 0px 09px;
background-color: #d4d2e7;
}
:host
::ng-deep
.ngx-datatable.bootstrap
.datatable-footer
.datatable-pager
.datatable-icon-skip {
font-size: 16px;
line-height: 22px;
padding: 0px 09px;
background-color: #d4d2e7;
}
:host
::ng-deep
.ngx-datatable.bootstrap
.datatable-footer
.datatable-pager
.datatable-icon-prev {
font-size: 16px;
line-height: 22px;
padding: 0px 09px;
background-color: #d4d2e7;
}
:host
::ng-deep
.datatable-footer
.datatable-pager
ul
li:not(.disabled).active
a,
.ngx-datatable.bootstrap[_ngcontent-c11]
.datatable-footer[_ngcontent-c11]
.datatable-pager[_ngcontent-c11]
ul[_ngcontent-c11]
li[_ngcontent-c11]:not(.disabled):hover
a[_ngcontent-c11] {
background-color: #d4d2e7;
font-weight: bold;
font-size: larger;
}
:host ::ng-deep .ngx-datatable.bootstrap .datatable-footer .datatable-pager a {
height: 32px;
min-width: 34px;
line-height: 22px;
padding: 0;
border-radius: 3px;
margin: 0 3px;
text-align: center;
vertical-align: top;
padding-top: 3px;
text-decoration: none;
vertical-align: bottom;
color: #7c8091;
}
:host
::ng-deep
.ngx-datatable.bootstrap
.datatable-footer
.datatable-pager
.datatable-icon-left,
.ngx-datatable.bootstrap[_ngcontent-c11]
.datatable-footer[_ngcontent-c11]
.datatable-pager[_ngcontent-c11]
.datatable-icon-right[_ngcontent-c11],
.ngx-datatable.bootstrap[_ngcontent-c11]
.datatable-footer[_ngcontent-c11]
.datatable-pager[_ngcontent-c11]
.datatable-icon-prev[_ngcontent-c11] {
font-size: 14px;
line-height: 9px;
padding: 0px 08px;
background-color: #ffffff;
}
:host
::ng-deep
.ngx-datatable.bootstrap
.datatable-footer
.datatable-pager
.datatable-icon-left,
.ngx-datatable.bootstrap[_ngcontent-c11]
.datatable-footer[_ngcontent-c11]
.datatable-pager[_ngcontent-c11]
.datatable-icon-right[_ngcontent-c11],
.ngx-datatable.bootstrap[_ngcontent-c11]
.datatable-footer[_ngcontent-c11]
.datatable-pager[_ngcontent-c11]
.datatable-icon-prev[_ngcontent-c11] {
font-size: 0px;
line-height: 22px;
padding: 0px 09px;
background-color: #ffffff;
}
:host
::ng-deep
.ngx-datatable.bootstrap
.datatable-footer
.datatable-pager
.datatable-icon-right,
.ngx-datatable.bootstrap[_ngcontent-c11]
.datatable-footer[_ngcontent-c11]
.datatable-pager[_ngcontent-c11]
.datatable-icon-right[_ngcontent-c11],
.ngx-datatable.bootstrap[_ngcontent-c11]
.datatable-footer[_ngcontent-c11]
.datatable-pager[_ngcontent-c11]
.datatable-icon-prev[_ngcontent-c11] {
font-size: 0px;
line-height: 22px;
padding: 0px 09px;
background-color: #ffffff;
}
:host
::ng-deep
.ngx-datatable.bootstrap
.datatable-footer
.datatable-pager
.datatable-icon-skip,
.ngx-datatable.bootstrap[_ngcontent-c11]
.datatable-footer[_ngcontent-c11]
.datatable-pager[_ngcontent-c11]
.datatable-icon-right[_ngcontent-c11],
.ngx-datatable.bootstrap[_ngcontent-c11]
.datatable-footer[_ngcontent-c11]
.datatable-pager[_ngcontent-c11]
.datatable-icon-prev[_ngcontent-c11] {
font-size: 0px;
line-height: 22px;
padding: 0px 09px;
background-color: #ffffff;
}
:host
::ng-deep
.ngx-datatable.bootstrap
.datatable-footer
.datatable-pager
.datatable-icon-prev,
.ngx-datatable.bootstrap[_ngcontent-c11]
.datatable-footer[_ngcontent-c11]
.datatable-pager[_ngcontent-c11]
.datatable-icon-right[_ngcontent-c11],
.ngx-datatable.bootstrap[_ngcontent-c11]
.datatable-footer[_ngcontent-c11]
.datatable-pager[_ngcontent-c11]
.datatable-icon-prev[_ngcontent-c11] {
font-size: 0px;
line-height: 22px;
padding: 0px 09px;
background-color: #ffffff;
}
:host
::ng-deep
.ngx-datatable.bootstrap
.datatable-footer
.datatable-pager
ul
li:not(.disabled):hover
a {
background-color: #545454;
font-weight: bold;
color: white;
}
:host
::ng-deep
.ngx-datatable.bootstrap
.datatable-footer
.datatable-pager
ul
li:not(.disabled).active
a,
.ngx-datatable.bootstrap
.datatable-footer
.datatable-pager
ul
li:not(.disabled):hover
a {
background-color: #37A647;
font-weight: bold;
color: white;
}
:host ::ng-deep .ngx-datatable.bootstrap .datatable-footer {
background: #DDE1E6;
color: #242222;
margin-top: -1px;
overflow: inherit;
}
:host ::ng-deep .ngx-datatable.bootstrap .datatable-header {
background: #DDE1E6;
color: #242222;
font-weight: bold;
height: unset !important;
overflow: inherit;
}
:host ::ng-deep .ngx-datatable .datatable-footer .datatable-pager {
flex: 0 0 0%;
}
:host ::ng-deep .ngx-datatable .datatable-footer .datatable-pager .pager {
display: flex;
}
:host
::ng-deep
.ngx-datatable
.datatable-footer
.selected-count
.datatable-pager {
flex: 0 0 0%;
}
:host ::ng-deep .ngx-datatable {
display: -webkit-box;
}
:host ::ng-deep .ng-select .ng-select-container {
color: #242222 !important;
background-color: #FBFBFB !important;
height: 40px !important;
border-radius: 12px !important;
box-shadow: 0 2px 4px rgba(36, 34, 34, 0.2) !important; /* Bayangan lebih tipis */
}
:host ::ng-deep .ng-select .ng-select-container .ng-value-container .ng-input>input {
color: #242222 !important;
}
:host ::ng-deep .ngx-datatable.bootstrap .datatable-body-row {
background-color: #FBFBFB; /* Black color for table rows */
}
:host ::ng-deep .ngx-datatable.bootstrap .datatable-body-row:hover {
background-color: #DDE1E6; /* Darker black for hover effect */
}
.text-custom-label{
color: #242222 !important;
font-family: "Open Sans", sans-serif !important;
font-size: 16px;
}
.text-custom-data{
color: #242222 !important;
font-family: "Open Sans", sans-serif !important;
font-size: 24px;
font-weight: 700;
}
.style-custom-label{
color: #242222 !important;
}

View File

@ -0,0 +1,516 @@
<div class="app-content content" style="background-color: #FBFBFB !important">
<div class="content-wrapper">
<div class="content-header row mb-1">
<app-breadcrumb class="col-12" [breadcrumb]="breadcrumb"></app-breadcrumb>
</div>
<div class="content-body">
<section id="configuration">
<div class="row">
<div class="col-lg-4 col-12">
<div class="card" style="background-color: #DDE1E6 !important; box-shadow: none !important;">
<div class="card-content">
<div class="card-body">
<div class="media d-flex">
<div class="media-body text-left">
<h6 class="text-custom-label">
Total Device
</h6>
<h3 class="text-custom-data">
{{ summaryTotal?.length }}
</h3>
</div>
<div class="align-self-center">
<div
style="
background-color: #37A647;
border-radius: 50%;
width: 50px;
height: 50px;
display: flex;
align-items: center;
justify-content: center;
"
>
<i
class="ri-device-line primary font-large-1 float-right"
style="color: #ffffff !important"
></i>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-lg-4 col-12">
<div class="card" style="background-color: #DDE1E6 !important; box-shadow: none !important;">
<div class="card-content">
<div class="card-body">
<div class="media d-flex">
<div class="media-body text-left">
<h6 class="text-custom-label">
Total Device Active
</h6>
<h3 class="text-custom-data">{{ totalOn }}</h3>
</div>
<div class="align-self-center">
<div
style="
background-color: #37A647;
border-radius: 50%;
width: 50px;
height: 50px;
display: flex;
align-items: center;
justify-content: center;
"
>
<i
class="feather ft-wifi primary font-large-1 float-right"
style="color: #ffffff !important"
></i>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-lg-4 col-12">
<div class="card" style="background-color: #DDE1E6 !important; box-shadow: none !important;">
<div class="card-content">
<div class="card-body">
<div class="media d-flex">
<div class="media-body text-left">
<h6 class="text-custom-label">
Total Device Non-Active
</h6>
<h3 class="text-custom-data">{{ totalOff }}</h3>
</div>
<div class="align-self-center">
<div
style="
background-color: #37A647;
border-radius: 50%;
width: 50px;
height: 50px;
display: flex;
align-items: center;
justify-content: center;
"
>
<i
class="feather ft-wifi-off primary font-large-1 float-right"
style="color: #ffffff !important"
></i>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<section id="configuration">
<div class="row">
<div class="col-12">
<div class="card" style="background-color: #FBFBFB !important; box-shadow: none;">
<div class="card-content">
<div class="card-body">
<div class="row mb-2">
<div class="col-3">
<div class="form-group">
<ng-select
class="select-custom"
[items]="dataBuildingList"
[searchable]="true"
[disabled]="newDeviceActive"
bindLabel="name"
bindValue="id"
placeholder="Select Building"
[(ngModel)]="buildingSelected"
>
</ng-select>
</div>
</div>
<div class="col-3">
<div class="form-group">
<ng-select
class="select-custom"
[items]="dataMasterCategori"
[searchable]="true"
[disabled]="newDeviceActive"
bindLabel="name"
bindValue="id"
placeholder="Select Category"
[(ngModel)]="categorySelected"
>
</ng-select>
</div>
</div>
<div class="col-3">
<div class="form-group">
<ng-select
class="select-custom"
[items]="dataMasterStatus"
[searchable]="true"
[disabled]="newDeviceActive"
bindLabel="name"
bindValue="id"
placeholder="Select Status"
[(ngModel)]="statusSelected"
>
</ng-select>
</div>
</div>
<div class="col-3 text-left">
<div class="d-flex">
<button
type="button"
class="btn ml-2"
(click)="doFilter()"
style="
background-color: #37A647 !important;
border-color: #37A647 !important;
border-radius: 12px;
color: #ffffff;
"
[disabled]="spinnerFilterActive || newDeviceActive"
>
<i
class="la la-search"
style="color: #ffffff !important"
*ngIf="!spinnerFilterActive"
></i>
<i
class="la la-spinner spinner"
style="color: #ffffff !important"
*ngIf="spinnerFilterActive"
></i>
</button>
<button
type="button"
class="btn ml-2"
(click)="doCancelFilter()"
style="
background-color: #FBFBFB !important;
border-color: #242222 !important;
color: #242222;
border-radius: 12px;
"
>
Cancel
</button>
</div>
</div>
</div>
<div class="row mb-2">
<div class="col-6 col-md-3 mb-2">
<button
class="btn btn-secondary btn-block"
[disabled]="spinnerActive"
(click)="addDevice()"
style="
background-color: #37A647 !important;
border-color: #37A647 !important;
color: #ffffff !important;
"
>
<i
class="la la-spinner spinner"
*ngIf="spinnerActive"
></i>
<i class="feather ft-plus" *ngIf="!spinnerActive"></i>
&nbsp;
<span style="font-weight: 600">Synchronization</span>
</button>
</div>
<div class="col-6 col-md-3 mb-2">
<button
class="btn btn-secondary btn-block"
[disabled]="spinnerExportActive"
(click)="exportDevice()"
style="
background-color: #37A647 !important;
border-color: #37A647 !important;
color: #ffffff !important;
"
>
<i
class="la la-spinner spinner"
*ngIf="spinnerExportActive"
></i>
<i
class="ri-export-fill"
*ngIf="!spinnerExportActive"
></i>
&nbsp;
<span style="font-weight: 600">Export</span>
</button>
</div>
<div class="col-6 col-md-3 mb-2">
<button
class="btn btn-secondary btn-block"
*ngIf="!newDeviceActive"
[disabled]="spinnerNewDeviceActive"
(click)="newDevice()"
style="
background-color: #37A647 !important;
border-color: #37A647 !important;
color: #ffffff !important;
"
>
<i
class="la la-spinner spinner"
*ngIf="spinnerNewDeviceActive"
></i>
<i
class="ri-sticky-note-add-fill"
*ngIf="!spinnerNewDeviceActive"
></i>
&nbsp;
<span style="font-weight: 600">New Device</span>
</button>
<button
class="btn btn-secondary btn-block"
*ngIf="newDeviceActive"
[disabled]="spinnerNewDeviceActive"
(click)="allDevice()"
style="
background-color: #37A647 !important;
border-color: #37A647 !important;
color: #ffffff !important;
"
>
<i
class="la la-spinner spinner"
*ngIf="spinnerNewDeviceActive"
></i>
<i
class="ri-arrow-left-circle-line"
*ngIf="!spinnerNewDeviceActive"
></i>
&nbsp;
<span style="font-weight: 600">All Device</span>
</button>
</div>
</div>
<div class="card-dashboard">
<ngx-datatable
class="bootstrap table-bordered"
[limit]="5"
[rows]="data_device"
[columnMode]="'force'"
[headerHeight]="50"
[footerHeight]="50"
[rowHeight]="50"
fxFlex="auto"
[scrollbarH]="true"
>
<ngx-datatable-column
name="#"
[flexGrow]="1"
[minWidth]="10"
>
<ng-template
ngx-datatable-cell-template
let-rowIndex="rowIndex"
>
{{ rowIndex + 1 }}
</ng-template>
</ngx-datatable-column>
<ngx-datatable-column
name="icon"
[flexGrow]="1"
[minWidth]="20"
>
<ng-template ngx-datatable-header-template>
<span class="style-custom-label">Image</span>
</ng-template>
<ng-template ngx-datatable-cell-template let-row="row">
<span class="avatar avatar-sm rounded-circle">
<img [src]="row.icon" /><i></i
></span>
</ng-template>
</ngx-datatable-column>
<ngx-datatable-column
name="Name"
[flexGrow]="1"
[minWidth]="150"
>
<ng-template ngx-datatable-header-template>
<span class="style-custom-label"
>Device Name</span
>
</ng-template>
<ng-template
let-value="value"
ngx-datatable-cell-template
>
<p class="style-custom-label">{{ value }}</p>
</ng-template>
</ngx-datatable-column>
<ngx-datatable-column
name="buildingName"
[flexGrow]="1"
[minWidth]="90"
>
<ng-template ngx-datatable-header-template>
<span class="style-custom-label"
>Building</span
>
</ng-template>
<ng-template
let-value="value"
ngx-datatable-cell-template
>
<p class="style-custom-label">{{ value }}</p>
</ng-template>
</ngx-datatable-column>
<ngx-datatable-column
name="categoryName"
[flexGrow]="1"
[minWidth]="90"
>
<ng-template ngx-datatable-header-template>
<span class="style-custom-label"
>Category</span
>
</ng-template>
<ng-template
ngx-datatable-cell-template
let-value="value"
>
<p class="style-custom-label">
{{ value }}
</p>
</ng-template>
</ngx-datatable-column>
<ngx-datatable-column
name="watt"
[flexGrow]="1"
[minWidth]="90"
>
<ng-template ngx-datatable-header-template>
<span class="style-custom-label">Watt</span>
</ng-template>
<ng-template
ngx-datatable-cell-template
let-value="value"
>
<p class="style-custom-label">{{ value }}</p>
</ng-template>
</ngx-datatable-column>
<ngx-datatable-column
name="typeName"
[flexGrow]="1"
[minWidth]="90"
>
<ng-template ngx-datatable-header-template>
<span class="style-custom-label">Type</span>
</ng-template>
<ng-template
ngx-datatable-cell-template
let-value="value"
>
<p class="style-custom-label">
{{ value }}
</p>
</ng-template>
</ngx-datatable-column>
<ngx-datatable-column
name="voltageName"
[flexGrow]="1"
[minWidth]="90"
>
<ng-template ngx-datatable-header-template>
<span class="style-custom-label">Voltage</span>
</ng-template>
<ng-template
ngx-datatable-cell-template
let-value="value"
>
<p class="style-custom-label">
{{ value }}
</p>
</ng-template>
</ngx-datatable-column>
<!-- <ngx-datatable-column
name="statusName"
[flexGrow]="1"
[minWidth]="90"
>
<ng-template ngx-datatable-header-template>
<span style="color: #ffffff !important">Status</span>
</ng-template>
<ng-template
ngx-datatable-cell-template
let-value="value"
>
<p style="color: #ffffff !important">
{{ value }}
</p>
</ng-template>
</ngx-datatable-column> -->
<ngx-datatable-column
name="Actions"
[flexGrow]="1"
[minWidth]="150"
>
<ng-template ngx-datatable-header-template>
<span class="style-custom-label">Actions</span>
</ng-template>
<ng-template
ngx-datatable-cell-template
let-rowIndex="rowIndex"
let-row="row"
>
<button
class="btn btn-sm btn-info mr-1"
style="
background-color: #DDE1E6 !important;
border-color: #37A647 !important;
"
(click)="viewRow(row)"
>
<i
class="ficon feather ft-eye"
style="color: #37A647 !important"
></i>
</button>
<button
class="btn btn-sm btn-warning mr-1"
style="
background-color: #DDE1E6 !important;
border-color: #37A647 !important;
"
(click)="editRow(row)"
>
<i
class="ficon feather ft-edit"
style="color: #37A647 !important"
></i>
</button>
<!-- <button
class="btn btn-sm btn-danger"
(click)="deleteRow(row)"
>
<i class="ficon feather ft-trash-2"></i>
</button> -->
</ng-template>
</ngx-datatable-column>
</ngx-datatable>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
</div>
</div>
</div>

View File

@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { DeviceComponent } from './device.component';
describe('DeviceComponent', () => {
let component: DeviceComponent;
let fixture: ComponentFixture<DeviceComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ DeviceComponent ]
})
.compileComponents();
fixture = TestBed.createComponent(DeviceComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,303 @@
import { Component, OnInit, ViewChild } from "@angular/core";
import { NgbModal } from "@ng-bootstrap/ng-bootstrap";
import { TableApiService } from "src/app/_services/table-api.service";
import { ModalAddEditComponent } from "./modal-add-edit/modal-add-edit.component";
import { ActivatedRoute, Router } from "@angular/router";
import { BuildingService } from "../service/monitoring-api.service";
import { DeviceService } from "../service/device.service";
import { ToastrService } from "ngx-toastr";
import * as FileSaver from "file-saver";
import * as XLSX from "xlsx";
import { TableexcelService } from "src/app/_services/tableexcel.service";
import { LoginService } from "../service/login.service";
@Component({
selector: "app-device",
templateUrl: "./device.component.html",
styleUrls: ["./device.component.css"],
})
export class DeviceComponent implements OnInit {
data: any;
totalOn: any;
totalOff: any;
filteredRows: any[];
summaryTotal: any[];
searchTerm: string = "";
buildingSelected: any;
statusSelected: any;
categorySelected: any;
dataMasterCategori: any;
dataBuildingList: any;
dataMasterStatus: any;
data_device: any;
spinnerFilterActive = false;
storedData: any;
rows: any = [];
public breadcrumb: any;
spinnerActive: boolean = false;
spinnerExportActive: boolean = false;
spinnerNewDeviceActive: boolean = false;
newDeviceActive: boolean = false;
singlebasicSelected: any;
constructor(
private modalService: NgbModal,
private router: Router,
private monitoringApiService: BuildingService,
private deviceService: DeviceService,
private toastr: ToastrService,
private tableexcelService: TableexcelService,
private authService: LoginService
) {}
ngOnInit() {
this.authService.startTokenCheck();
this.authService.startTrackingActivity();
this.breadcrumb = {
mainlabel: "Device",
links: [
{
name: "Home",
isLink: false,
link: "/dashboard/sales",
},
{
name: "Device",
isLink: false,
},
],
};
this.storedData = JSON.parse(localStorage.getItem("currentUser"));
this.buildingSelected = this.storedData.buildingId;
if (this.newDeviceActive === true) {
}
this.fetchData(
this.buildingSelected,
this.categorySelected,
this.statusSelected
);
this.summaryDevice(
this.buildingSelected,
this.categorySelected,
this.statusSelected
);
this.dataListMaster();
this.dataListBuilding();
}
fetchData(buildingSelected, categorySelected, statusSelected) {
this.newDeviceActive = false;
this.buildingSelected = buildingSelected;
this.deviceService
.getDeviceData(buildingSelected, categorySelected, statusSelected)
.subscribe((res) => {
this.data = res;
this.filteredRows = this.data.results.data;
this.data_device = this.filteredRows.map((item) => ({
buildingName: item.building_name,
id: item.id,
name: item.name,
icon: item.icon,
watt: item.watt,
categoryName: item.category_name,
typeName: item.type_name,
voltageName: item.voltage_name,
statusName: item.status_name,
}));
});
}
summaryDevice(buildingSelected, categorySelected, statusSelected) {
this.deviceService
.getDeviceData(buildingSelected, categorySelected, statusSelected)
.subscribe((res) => {
this.summaryTotal = res.results.data;
this.totalOn = res.results.data.filter(
(row) => row.status_id === 2
).length;
this.totalOff = res.results.data.filter(
(row) => row.status_id === 3
).length;
});
}
newDevice() {
this.spinnerNewDeviceActive = true;
this.newDeviceActive = true;
this.deviceService.getDeviceData(0, 0, 0).subscribe((res) => {
this.data = res;
this.filteredRows = this.data.results.data;
this.spinnerNewDeviceActive = false;
this.data_device = this.filteredRows.map((item) => ({
buildingName: item.building_name,
id: item.id,
name: item.name,
icon: item.icon,
watt: item.watt,
categoryName: item.category_name,
typeName: item.type_name,
voltageName: item.voltage_name,
statusName: item.status_name,
}));
});
}
allDevice() {
this.fetchData(
this.buildingSelected,
this.categorySelected,
this.statusSelected
);
}
dataListMaster() {
this.monitoringApiService.getMasterListData().subscribe((data) => {
const dataCategory = data.data.find(
(item) => item.name === "master_category"
).headerDetailParam;
const dataStatus = data.data.find(
(item) => item.name === "master_status"
).headerDetailParam;
this.dataMasterCategori = dataCategory.filter(
(item) => item.statusName.toLowerCase() === "aktif"
);
this.dataMasterStatus = dataStatus.filter((item) => item.statusName.toLowerCase() === "aktif");
});
}
dataListBuilding() {
this.monitoringApiService.getBuildingList().subscribe((data) => {
this.dataBuildingList = data.data.filter((item) => item.statusName.toLowerCase() === "aktif");
});
}
doFilter() {
if (!this.buildingSelected) {
this.toastr.error("Warning", "Filter Building tidak boleh kosong.", {
timeOut: 5000,
closeButton: true,
});
} else {
this.spinnerFilterActive = true;
this.fetchData(
this.buildingSelected,
this.categorySelected,
this.statusSelected
);
setTimeout(() => {
this.spinnerFilterActive = false;
}, 3000);
}
}
doCancelFilter() {
this.storedData = JSON.parse(localStorage.getItem("currentUser"));
this.buildingSelected = this.storedData.buildingId;
this.categorySelected = undefined;
this.statusSelected = undefined;
this.fetchData(this.buildingSelected, 0, 0);
}
filterRows() {
if (!this.searchTerm) {
this.filteredRows = [...this.data.results.data];
} else {
this.filteredRows = this.data.results.data.filter((row) =>
this.rowContainsSearchTerm(row)
);
}
}
rowContainsSearchTerm(row: any): boolean {
const searchTermLC = this.searchTerm.toLowerCase();
return Object.values(row).some(
(value) =>
value !== null && value.toString().toLowerCase().includes(searchTermLC)
);
}
addFieldValue() {
const modalRef = this.modalService.open(ModalAddEditComponent, {
size: "lg",
});
modalRef.componentInstance.newAttribute = {
id: null,
name: "",
position: "",
office: "",
age: "",
salary: "",
startdate: "",
};
modalRef.result.then(
(result) => {
if (result) {
this.rows.push(result);
this.rows = [...this.rows];
}
},
(reason) => {
console.log(`Dismissed: ${reason}`);
}
);
}
viewRow(row) {
this.router.navigate(["/device/view", row.id]);
}
editRow(row) {
this.router.navigate(["/device/edit", row.id]);
}
deleteRow(row) {
console.log("Delete row:", row);
}
addDevice(): void {
// Aktifkan spinner
this.spinnerActive = true;
this.deviceService.getSyncDeviceData().subscribe((res) => {
console.log(res);
});
setTimeout(() => {
this.spinnerActive = false;
this.toastr.success("Success", "Sync Completed.", {
timeOut: 2000,
closeButton: true,
});
this.fetchData(
this.buildingSelected,
this.categorySelected,
this.statusSelected
);
}, 3000);
}
exportDevice() {
this.spinnerExportActive = true;
setTimeout(() => {
const columnsToExport = [
"name",
"building_name",
"room_name",
"watt",
"category_name",
"status_name",
"type_name",
"voltage_name",
];
this.tableexcelService.exportAsExcelFileDevice(
this.filteredRows,
"Smart_building_list_device",
columnsToExport
);
this.spinnerExportActive = false;
}, 3000);
}
}

View File

@ -0,0 +1,81 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { DeviceComponent } from './device.component';
import { RouterModule } from '@angular/router';
import { NgxDatatableModule } from '@swimlane/ngx-datatable';
import { NgSelectModule } from '@ng-select/ng-select';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { ClipboardModule } from 'ngx-clipboard';
import { CardModule } from '../../partials/general/card/card.module';
import { BreadcrumbModule } from 'src/app/_layout/breadcrumb/breadcrumb.module';
import { BlockUIModule } from 'ng-block-ui';
import { BlockTemplateComponent } from '../../../_layout/blockui/block-template.component';
import { PerfectScrollbarModule } from 'ngx-perfect-scrollbar';
import { AddEditDeviceComponent } from './add-edit-device/add-edit-device.component';
import { ModalAddEditComponent } from './modal-add-edit/modal-add-edit.component';
import { DeviceControlComponent } from './device-control/device-control.component';
import { UiSwitchModule } from 'ngx-ui-switch';
import { ControlSchedulerComponent } from './control-scheduler/control-scheduler.component';
import { SchedulerListComponent } from './scheduler-list/scheduler-list.component';
@NgModule({
declarations: [
DeviceComponent,
AddEditDeviceComponent,
ModalAddEditComponent,
DeviceControlComponent,
ControlSchedulerComponent,
SchedulerListComponent
],
imports: [
CommonModule,
CardModule,
BreadcrumbModule,
NgSelectModule,
FormsModule,
ReactiveFormsModule,
ClipboardModule,
PerfectScrollbarModule,
UiSwitchModule,
NgxDatatableModule,
BlockUIModule.forRoot({
template: BlockTemplateComponent
}),
RouterModule.forChild([
{
path: '',
component: DeviceComponent
},
{
path: 'control-device',
component: DeviceControlComponent,
},
{
path: 'add-row',
component: AddEditDeviceComponent,
data: { mode: 'add' }
},
{
path: 'edit/:id',
component: AddEditDeviceComponent,
data: { mode: 'edit' }
},
{
path: 'view/:id',
component: AddEditDeviceComponent,
data: { mode: 'view' }
},
{
path: 'scheduler/:id',
component: SchedulerListComponent,
data: { mode: 'scheduler' }
}
]),
]
})
export class DeviceModule { }

View File

@ -0,0 +1,55 @@
/* modal-add-edit.component.css */
::ng-deep .modal-backdrop.show {
z-index: auto !important;
}
::ng-deep .input-group-append .btn {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
border-radius: 0;
border-left: 0;
flex-grow: 0;
border-left: 1px solid #ced4da;
padding: 0.375rem 0.75rem;
}
::ng-deep .input-group {
display: flex;
flex-wrap: nowrap; /* Prevents wrapping of the items */
align-items: center;
}
::ng-deep .form-control {
flex-grow: 1; /* Ensures select takes up available space */
padding-right: 0.5rem;
}
::ng-deep .input-group select,
::ng-deep .input-group .input-group-append .btn {
padding-right: 5px; /* Adjust padding if necessary */
}
::ng-deep .input-group .form-control {
margin-right: 2px; /* Adjust margin to make space */
}
.form-group {
margin-bottom: 1rem;
}
.form-control {
display: block;
width: 100%;
height: calc(1.5em + 0.75rem + 2px);
padding: 0.375rem 0.75rem;
font-size: 1rem;
font-weight: 400;
line-height: 1.5;
color: #495057;
background-color: #fff;
background-clip: padding-box;
border: 1px solid #ced4da;
border-radius: 0.25rem;
transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
}

View File

@ -0,0 +1,43 @@
<div class="modal-header">
<h4 class="modal-title">Add New Row</h4>
<button type="button" class="close" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<form [formGroup]="myForm">
<div class="form-row">
<div class="form-group col-md-6">
<label for="name">Name:</label>
<input type="text" class="form-control" id="name" formControlName="name">
</div>
<div class="form-group col-md-6">
<label for="position">Position:</label>
<input type="text" class="form-control" id="position" formControlName="position">
</div>
</div>
<div class="form-row">
<div class="form-group col-md-6">
<label for="age">Age:</label>
<input type="number" class="form-control" id="age" formControlName="age">
</div>
<div class="form-group col-md-6">
<label for="department">Department:</label>
<div class="input-group">
<select class="form-control" formControlName="department">
<option>IT</option>
<option>HR</option>
<option>Finance</option>
</select>
<div class="input-group-append">
<button class="btn btn-outline-secondary" type="button" (click)="addDepartment()">+</button>
</div>
</div>
</div>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" (click)="activeModal.dismiss('Cross click')">Close</button>
<button type="button" class="btn btn-primary" (click)="addRow()">Save Changes</button>
</div>

View File

@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ModalAddEditComponent } from './modal-add-edit.component';
describe('ModalAddEditComponent', () => {
let component: ModalAddEditComponent;
let fixture: ComponentFixture<ModalAddEditComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ ModalAddEditComponent ]
})
.compileComponents();
fixture = TestBed.createComponent(ModalAddEditComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,41 @@
import { Component, Input } from "@angular/core";
import { NgbActiveModal } from "@ng-bootstrap/ng-bootstrap";
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
@Component({
selector: "app-modal-add-edit",
templateUrl: "./modal-add-edit.component.html",
styleUrls: ["./modal-add-edit.component.css"],
})
export class ModalAddEditComponent {
@Input() newAttribute: any = {};
myForm: FormGroup;
constructor(public activeModal: NgbActiveModal, private fb: FormBuilder) {
this.createForm();
}
createForm() {
this.myForm = this.fb.group({
name: ['', Validators.required],
position: ['', Validators.required],
age: [null, [Validators.required, Validators.min(18)]],
department: ['', Validators.required]
});
}
addRow() {
if (this.myForm.valid) {
this.activeModal.close(this.myForm.value);
}
}
addDepartment() {
// Example function to simulate adding a department
let newDept = prompt("Enter new department name:");
if (newDept) {
// Normally you would add to a database or a service
alert(`Department ${newDept} added! (simulated)`);
}
}
}

View File

@ -0,0 +1,54 @@
.app-content {
padding-top: 20px;
}
.card {
border-radius: 15px;
overflow: hidden;
}
.card-header {
padding: 1.5rem;
border-bottom: 1px solid #ddd;
}
.card-body {
padding: 2rem;
}
.list-group-item {
background-color: #f7f8fa;
margin-bottom: 10px;
border-radius: 10px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.list-group-item:hover {
transform: translateY(-5px);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
}
.list-group-item h5 {
margin-bottom: 5px;
}
.list-group-item .btn-outline-secondary,
.list-group-item .btn-outline-danger {
border-radius: 20px;
padding: 0.25rem 0.75rem;
}
.btn-light {
background-color: #e8eaf6;
color: #3f51b5;
border: none;
border-radius: 20px;
padding: 0.5rem 1.25rem;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.btn-light:hover {
background-color: #d1d9ff;
color: #303f9f;
}

View File

@ -0,0 +1,113 @@
<div class="app-content content" style="background-color: #fbfbfb !important">
<div class="content-wrapper">
<div class="content-header row mb-1">
<app-breadcrumb class="col-12" [breadcrumb]="breadcrumb"></app-breadcrumb>
</div>
<div class="content-body">
<section id="basic-form-layouts">
<div class="row">
<div class="col-6" *blockUI="'projectInfo'; message: 'Loading'">
<div class="card" style="background-color: #fbfbfb !important">
<div class="card-content">
<div
class="card-header d-flex justify-content-between align-items-center"
style="background-color: #fbfbfb !important"
>
<h4 class="form-section m-0" style="color: #242222">
<i class="feather ft-calendar" style="color: #242222"></i>
{{ deviceName ? deviceName : '' }}
</h4>
<button
class="btn"
style="
background-color: #37a647 !important;
border-color: #37a647 !important;
color: #ffffff !important;
"
(click)="addSchedulerItem()"
>
<span style="font-weight: 600">Add Schedule</span>
</button>
</div>
<div class="card-body">
<div class="form-body">
<div class="row">
<div class="col-md-12">
<div *ngIf="schedules.length > 0; else noSchedules">
<ul class="list-group">
<li
class="list-group-item d-flex justify-content-between align-items-center mb-3"
*ngFor="let schedule of listScheduler"
>
<!-- Tambahkan checkbox di sini -->
<div class="d-flex align-items-center">
<div class="mr-2">
<input
type="checkbox"
[(ngModel)]="schedule.selected"
(change)="checkSelected()"
/>
</div>
<div (click)="editSchedule(schedule)">
<h5 style="color: #242222">
{{ schedule.name }} ({{
schedule.switch ? "On" : "Off"
}})
</h5>
<h2 class="mb-0" style="color: #242222">
{{ schedule.timeset }}
</h2>
</div>
</div>
<div>
<ui-switch
style="border-color: #bef264 !important"
class="switchery"
switchColor="white"
color="rgb(55, 166, 71)"
size="small"
[checked]="schedule.active"
(change)="switchChanged($event, schedule)"
></ui-switch>
</div>
</li>
</ul>
<!-- Tambahkan tombol delete di sini -->
<div class="d-flex justify-content-center">
<button
*ngIf="hasSelected"
class="btn btn-danger mt-2"
(click)="deleteSelectedSchedules()"
style="
background-color: transparent !important;
border-color: #37a647 !important;
"
>
<i
class="ficon la la-trash"
style="color: #37a647 !important"
></i>
</button>
</div>
</div>
<ng-template #noSchedules>
<div class="alert alert-info text-center">
<strong>No schedules available.</strong> Click the
"Add Schedule" button to create one.
</div>
</ng-template>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
</div>
</div>
</div>

View File

@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { SchedulerListComponent } from './scheduler-list.component';
describe('SchedulerListComponent', () => {
let component: SchedulerListComponent;
let fixture: ComponentFixture<SchedulerListComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ SchedulerListComponent ]
})
.compileComponents();
fixture = TestBed.createComponent(SchedulerListComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,192 @@
import { Component } from "@angular/core";
import { NgbModal } from "@ng-bootstrap/ng-bootstrap";
import { ControlSchedulerComponent } from "../control-scheduler/control-scheduler.component";
import { ActivatedRoute } from "@angular/router";
import { DeviceService } from "../../service/device.service";
import Swal from "sweetalert2";
@Component({
selector: "app-scheduler-list",
templateUrl: "./scheduler-list.component.html",
styleUrls: ["./scheduler-list.component.css"],
})
export class SchedulerListComponent {
public breadcrumb: any;
listScheduler: any;
deviceId: any;
deviceName: any;
hasSelected: boolean = false;
schedules = [
{
id: 1,
title: "Lampu Depan ON",
time: "08:55",
},
{
id: 2,
title: "Lampu Depan OFF",
time: "09:50",
},
];
constructor(
private modalService: NgbModal,
private route: ActivatedRoute,
private deviceService: DeviceService
) {}
ngOnInit(): void {
this.route.params.subscribe((params) => {
const id = params["id"];
this.deviceId = id;
if (id) {
this.dataListScheduler(id);
this.dataDevice(id);
}
});
this.breadcrumb = {
mainlabel: "Scheduler List",
linkBack: "/device/control-device",
isLinkBack: true,
links: [
{
name: "Home",
isLink: false,
link: "/dashboard/sales",
},
{
name: "Control Device",
isLink: false,
},
{
name: "Scheduler List",
isLink: false,
},
],
};
}
dataListScheduler(id) {
this.deviceService.getDeviceScheduler(id).subscribe((data) => {
this.listScheduler = data.results;
console.log(this.listScheduler);
});
}
dataDevice(id) {
this.deviceService.getDeviceByid(id).subscribe((data) => {
this.deviceName = data.data.name
});
}
switchChanged(e, item) {
const data = {
id: item.id,
active: e,
};
this.deviceService.putDeviceSchedulerActive(data, item.id).subscribe((res) => {
this.dataListScheduler(this.deviceId);
});
}
addSchedulerItem() {
const modalRef = this.modalService.open(ControlSchedulerComponent, {
size: "md",
backdrop: "static",
keyboard: false,
centered: true,
});
modalRef.componentInstance.deviceId = this.deviceId;
modalRef.componentInstance.mode = "add";
modalRef.result.then(
(result) => {
if (result) {
this.deviceService.postDeviceScheduler(result).subscribe((res) => {
this.dataListScheduler(this.deviceId);
});
}
},
(reason) => {
console.log(`Dismissed: ${reason}`);
}
);
// Handle edit action
}
editSchedule(data) {
const modalRef = this.modalService.open(ControlSchedulerComponent, {
size: "md",
backdrop: "static",
keyboard: false,
centered: true,
});
modalRef.componentInstance.deviceId = this.deviceId;
modalRef.componentInstance.data = data;
modalRef.componentInstance.mode = "edit";
modalRef.result.then(
(result) => {
if (result) {
this.deviceService
.putDeviceScheduler(result, data.id)
.subscribe((res) => {
this.dataListScheduler(this.deviceId);
});
}
},
(reason) => {
console.log(`Dismissed: ${reason}`);
}
);
}
checkSelected() {
this.hasSelected = this.listScheduler.some((schedule) => schedule.selected);
}
deleteSelectedSchedules() {
const selectedSchedules = this.listScheduler.filter(
(schedule) => schedule.selected
);
if (selectedSchedules.length > 0) {
Swal.fire({
title: "Apakah kamu yakin ingin menghapus jadwal yang dipilih?",
text: "Tindakan ini tidak dapat dibatalkan.",
icon: "warning",
showCancelButton: true,
confirmButtonColor: "#37a647",
cancelButtonColor: "#d33",
confirmButtonText: "Ya, hapus!",
cancelButtonText: "Batal",
allowOutsideClick: false,
}).then((result) => {
if (result.isConfirmed) {
selectedSchedules.forEach((schedule) => {
this.deleteSchedule(schedule.id);
});
Swal.fire({
title: "Terhapus!",
text: "Jadwal yang dipilih berhasil dihapus.",
icon: "success",
});
}
});
} else {
Swal.fire({
title: "Tidak ada jadwal yang dipilih!",
text: "Pilih setidaknya satu jadwal untuk dihapus.",
icon: "info",
});
}
}
deleteSchedule(id) {
this.deviceService.deleteDeviceScheduler(id).subscribe(() => {
this.dataListScheduler(this.deviceId);
this.hasSelected = false;
});
}
}

View File

@ -0,0 +1,280 @@
<div class="app-content content" style="background-color: #fbfbfb !important">
<div class="content-wrapper">
<div class="content-header row mb-1">
<app-breadcrumb class="col-12" [breadcrumb]="breadcrumb"></app-breadcrumb>
</div>
<div class="content-body">
<section id="basic-form-layouts">
<div class="row">
<div class="col-12" *blockUI="'projectInfo'; message: 'Loading'">
<div class="card" style="background-color: #fbfbfb !important">
<div class="card-content">
<div
class="card-header"
style="background-color: #fbfbfb !important"
>
<h2 style="color: #242222">
{{
isEditMode()
? "Edit List Monitoring"
: isViewMode()
? "View List Monitoring"
: "Add New List Monitoring"
}}
</h2>
</div>
<div class="card-body">
<form
[formGroup]="projectInfo"
(ngSubmit)="onProjectInfoSubmit()"
>
<div class="form-body">
<!-- <h4 class="form-section" style="color: #242222">
<i class="feather ft-user" style="color: #242222"></i>
General Information
</h4> -->
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label for="buildingId" style="color: #242222"
>Building *</label
>
<div class="input-group">
<select
id="buildingId"
class="form-control"
formControlName="buildingId"
[ngClass]="{
'is-invalid': submitted && f.buildingId.errors
}"
>
<option
*ngFor="let data of dataBuildingList"
[value]="data.id"
>
{{ data.name }}
</option>
</select>
<button
class="btn btn-danger ml-2"
type="button"
style="
color: #ffffff !important;
border-color: #37a647 !important;
background-color: #37a647 !important;
"
(click)="modalAddBuilding()"
>
<i class="feather ft-plus"> New Building</i>
</button>
</div>
<div *ngIf="projectInfo.get('buildingId').touched && projectInfo.get('buildingId').invalid" class="text-danger">
Building is required.
</div>
</div>
<div class="form-group">
<label for="floorId" style="color: #242222"
>Floor *</label
>
<div class="input-group">
<select
id="floorId"
class="form-control"
formControlName="floorId"
[ngClass]="{
'is-invalid': submitted && f.floorId.errors
}"
>
<option
*ngFor="let data of dataFloorList"
[value]="data.id"
>
{{ data.name }}
</option>
</select>
<button
class="btn btn-danger ml-2"
type="button"
style="
color: #ffffff !important;
border-color: #37a647 !important;
background-color: #37a647 !important;
"
(click)="modalAddFloor()"
>
<i class="feather ft-plus"> New Floor</i>
</button>
</div>
<div *ngIf="projectInfo.get('floorId').touched && projectInfo.get('floorId').invalid" class="text-danger">
Floor is required.
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="form-group" *ngIf="mode === 'add'">
<div class="" formArrayName="userArray">
<div
class=""
*ngFor="
let userForm of userFormGroup.controls;
let i = index
"
>
<div [formGroupName]="i" class="">
<div class="input-group mb-1">
<select
id="roomId{{i}}"
class="form-control"
formControlName="roomId"
(change)="validateDouble($event, i)"
[ngClass]="{
'is-invalid':
submitted && f.roomId.errors
}"
>
<option
*ngFor="let list of dataRoomList; trackBy: trackByFn"
[value]="list.id"
>
{{ list.name }}
</option>
</select>
<button
class="btn btn-danger"
type="button"
style="
color: #ffffff !important;
border-color: #a64f37 !important;
background-color: #a64f37 !important;
"
(click)="removePhone(i)"
>
<i class="feather ft-x"></i>
</button>
</div>
</div>
</div>
</div>
<button
type="button"
class="btn btn-primary"
[disabled]="disableButton"
style="
color: #ffffff !important;
border-color: #37a647 !important;
background-color: #37a647 !important;
"
(click)="addPhone()"
>
<i class="feather ft-plus"></i> Add Room
</button>
<button
type="button"
class="btn btn-primary ml-2"
style="
color: #ffffff !important;
border-color: #37a647 !important;
background-color: #37a647 !important;
"
(click)="modalAddNewRoom()"
>
New Room
</button>
</div>
<div class="form-group" *ngIf="mode !== 'add'">
<label for="roomId">Room *</label>
<div class="input-group">
<select
id="roomId"
class="form-control"
formControlName="roomId"
[ngClass]="{
'is-invalid': submitted && f.roomId.errors
}"
>
<option
*ngFor="let data of dataRoomList"
[value]="data.id"
>
{{ data.name }}
</option>
</select>
</div>
<small
class="form-text text-muted danger"
*ngIf="submitted && f.roomId.errors"
class="invalid-feedback"
>
<div *ngIf="f.roomId.errors.required">
Status is required
</div>
</small>
</div>
<div class="form-group">
<label for="statusId" style="color: #242222"
>Status *</label
>
<div class="input-group">
<select
id="statusId"
class="form-control"
formControlName="statusId"
[ngClass]="{
'is-invalid': submitted && f.statusId.errors
}"
>
<option
*ngFor="let data of dataMasterStatus"
[value]="data.id"
>
{{ data.name }}
</option>
</select>
</div>
<div *ngIf="projectInfo.get('statusId').touched && projectInfo.get('statusId').invalid" class="text-danger">
Status is required.
</div>
</div>
</div>
</div>
</div>
<div class="form-actions">
<button
type="button"
class="btn btn-warning mr-1"
style="
color: #242222 !important;
border-color: #242222 !important;
background-color: #fbfbfb !important;
"
(click)="cancel()"
>
<i class="feather ft-x"></i> Cancel
</button>
<button
type="submit"
class="btn btn-primary"
[disabled]="disableButtonSave"
style="
color: #ffffff !important;
border-color: #37a647 !important;
background-color: #37a647 !important;
"
(click)="save()"
>
<i class="la la-check"></i> Save
</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</section>
</div>
</div>
</div>

View File

@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { AddEditListComponent } from './add-edit-list.component';
describe('AddEditListComponent', () => {
let component: AddEditListComponent;
let fixture: ComponentFixture<AddEditListComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ AddEditListComponent ]
})
.compileComponents();
fixture = TestBed.createComponent(AddEditListComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,465 @@
import { Component, ViewChild } from "@angular/core";
import {
FormArray,
FormBuilder,
FormGroup,
NgForm,
Validators,
} from "@angular/forms";
import { ActivatedRoute, Router } from "@angular/router";
import { BlockUI, NgBlockUI } from "ng-block-ui";
import { BuildingService } from "../../service/monitoring-api.service";
import { LoginService } from "../../service/login.service";
import { ToastrService } from "ngx-toastr";
import { NgbModal } from "@ng-bootstrap/ng-bootstrap";
import { AddEditMasterBuildingComponent } from "../../master/master-building/add-edit-master-building/add-edit-master-building.component";
import { AddEditMasterComponent } from "../../master/add-edit-master/add-edit-master.component";
import { AddEditMasterRoomComponent } from "../../master/master-room/add-edit-master-room/add-edit-master-room.component";
import Swal from "sweetalert2";
@Component({
selector: "app-add-edit-list",
templateUrl: "./add-edit-list.component.html",
styleUrls: ["./add-edit-list.component.css"],
})
export class AddEditListComponent {
// @ViewChild("f", { read: true }) userProfileForm: NgForm;
model: any = {};
mode: string;
public breadcrumb: any;
projectInfo: FormGroup;
submitted = false;
dataBuildingList: any;
dataRoomList: any;
dataRoomListSementara: any;
dataFloorList: any;
dataMasterStatus: any;
roombuildingId: any;
dataRoomBuilding: any;
disableButton: boolean = true;
disableButtonSave: boolean = true;
lewatModal: boolean = false;
// userProfileForm: FormGroup;
public userList: FormArray;
@BlockUI("projectInfo") blockUIProjectInfo: NgBlockUI;
get userFormGroup() {
return this.projectInfo.get("userArray") as FormArray;
}
constructor(
private formBuilder: FormBuilder,
private router: Router,
private route: ActivatedRoute,
private monitoringApiService: BuildingService,
private authService: LoginService,
private toastr: ToastrService,
private modalService: NgbModal
) {}
ngOnInit() {
this.authService.startTokenCheck();
this.authService.startTrackingActivity();
this.route.data.subscribe((data) => {
this.mode = data.mode;
});
this.route.params.subscribe((params) => {
const id = params["id"];
this.roombuildingId = id;
if (id) {
this.loadRoomBuilding(id);
}
});
this.setBreadcrumb();
this.projectInfo = this.formBuilder.group({
buildingId: ["", Validators.required],
roomId: [""],
statusId: ["", Validators.required],
floorId: ["", Validators.required],
userArray: this.formBuilder.array([this.createRoom()]),
});
this.userList = this.projectInfo.get("userArray") as FormArray;
this.dataListBuilding();
this.dataListRoom();
this.dataListMaster();
this.dataListFloor();
}
createRoom(): FormGroup {
return this.formBuilder.group({
roomId: ["", Validators.required],
});
}
addPhone() {
this.userList.push(this.createRoom());
this.disableButton = true;
this.disableButtonSave = true;
}
removePhone(index) {
this.userList.removeAt(index);
this.isRoomDuplicate(index);
if (this.userList.value.length === 0) {
this.disableButtonSave = true;
}
}
validateDouble(event: any, index: number) {
const selectedRoomId = (event.target as HTMLSelectElement).value;
if (this.isRoomDuplicate(index)) {
this.disableButton = true;
this.disableButtonSave = true;
this.toastr.error("Room", "Data yang ada masukan double!", {
timeOut: 2000,
closeButton: true,
});
} else {
this.disableButton = false;
this.disableButtonSave = false;
}
}
isRoomDuplicate(index: number): boolean {
const selectedRooms = this.userFormGroup.value.map((room) => room.roomId);
const currentRoomId = selectedRooms[index];
const roomOccurrences = selectedRooms.filter(
(roomId) => roomId === currentRoomId
).length;
if (roomOccurrences === 0) {
this.disableButton = false;
this.disableButtonSave = false;
}
return roomOccurrences > 1;
}
setBreadcrumb() {
if (this.isAddMode()) {
this.breadcrumb = {
mainlabel: "List Monitoring",
links: [
{
name: "Home",
isLink: false,
},
{
name: "List Monitoring",
isLink: false,
},
{
name: "Add New List Monitoring",
isLink: false,
},
],
};
} else if (this.isEditMode()) {
this.breadcrumb = {
mainlabel: "List Monitoring",
links: [
{
name: "Home",
isLink: false,
},
{
name: "List Monitoring",
isLink: false,
},
{
name: "Edit New List Monitoring",
isLink: false,
},
],
};
}
}
loadRoomBuilding(deviceId: string) {
this.monitoringApiService
.getRoomBuildingById(deviceId)
.subscribe((data) => {
this.dataRoomBuilding = data;
this.formGetDevice(data);
});
}
formGetDevice(data) {
this.projectInfo.patchValue({
buildingId: data.data.buildingId,
roomId: data.data.roomId,
floorId: data.data.floorId,
statusId: data.data.statusId,
});
if (this.mode === "view") {
this.formDisable();
}
}
formDisable() {
this.projectInfo.get("buildingId").disable();
this.projectInfo.get("roomId").disable();
this.projectInfo.get("statusId").disable();
}
isEditMode() {
return this.mode === "edit";
}
isViewMode() {
return this.mode === "view";
}
isAddMode() {
return this.mode === "add";
}
get f() {
return this.projectInfo.controls;
}
onProjectInfoSubmit() {
this.submitted = true;
if (this.projectInfo.invalid) {
return;
}
}
keyPress(event: any) {
const pattern = /[0-9\+\-\ ]/;
const inputChar = String.fromCharCode(event.charCode);
if (event.keyCode !== 8 && !pattern.test(inputChar)) {
event.preventDefault();
}
}
dataListBuilding() {
this.monitoringApiService.getBuildingList().subscribe((data) => {
this.dataBuildingList = data.data
.filter(
(item) =>
item.statusName.toLowerCase() === "aktif" || item.status_id === 71
)
.sort((a, b) => b.id - a.id);
if (this.lewatModal) {
this.projectInfo.patchValue({
buildingId: this.dataBuildingList[0],
});
this.lewatModal = false;
}
});
}
dataListRoom() {
this.monitoringApiService.getListRoomDataUnmap().subscribe((data) => {
const newArray = data.data.map((item) => ({
id: item.id,
name: `${item.name} (${item.code})`,
})).sort((a, b) => b.id - a.id);
this.dataRoomList = newArray;
});
}
trackByFn(index: number, option: any): any {
return option.id;
}
dataListFloor() {
this.monitoringApiService.getListFloorDataUnmap(6).subscribe((data) => {
const newArray = data.data.map((item) => ({
id: item.id,
name: `${item.name} (${item.code})`,
status: item.status_name
})).sort((a, b) => b.id - a.id);
this.dataFloorList = newArray.filter((item) => item.status.toLowerCase() === "aktif");
if (this.lewatModal) {
this.projectInfo.patchValue({
floorId: this.dataFloorList[0],
});
this.lewatModal = false;
}
});
}
dataListMaster() {
this.monitoringApiService.getMasterListData().subscribe((data) => {
const dataStatus = data.data.find(
(item) => item.name === "master_status"
).headerDetailParam;
this.dataMasterStatus = dataStatus.filter(
(item) =>
item.statusName.toLowerCase() === "aktif" ||
item.status.toLowerCase() === "71"
);
});
}
save() {
if (this.mode === "add") {
if (this.projectInfo.invalid) {
this.markFormGroupTouched(this.projectInfo)
return;
}
const formData = this.projectInfo.value;
const transformedData = {
buildingId: formData.buildingId,
roomId: formData.userArray.map((room) => room.roomId),
statusId: formData.statusId,
floorId: formData.floorId,
};
this.monitoringApiService
.postBatchBuilding(transformedData)
.subscribe((res) => {
this.router.navigate(["/list-monitoring"]);
});
} else {
delete this.projectInfo.value.userArray;
this.monitoringApiService
.putBuildingRoom(this.projectInfo.value, this.roombuildingId)
.subscribe((data) => {
this.router.navigate(["/list-monitoring"]);
});
}
}
markFormGroupTouched(formGroup: FormGroup) {
(Object as any).values(formGroup.controls).forEach((control) => {
control.markAsTouched();
if (control.controls) {
this.markFormGroupTouched(control);
}
});
}
cancel() {
if (this.projectInfo.dirty) { // Check if form has unsaved changes
Swal.fire({
title: 'Are you sure?',
text: "You have unsaved changes. Do you really want to leave?",
icon: 'warning',
showCancelButton: true,
confirmButtonColor: '#37a647',
cancelButtonColor: '#d33',
confirmButtonText: 'Yes, leave',
cancelButtonText: 'No, stay'
}).then((result) => {
if (result.isConfirmed) {
this.router.navigate(["/list-monitoring"]);
}
});
} else {
this.router.navigate(["/list-monitoring"]);
}
}
modalAddBuilding() {
const modalRef = this.modalService.open(AddEditMasterBuildingComponent, {
size: "lg",
backdrop: "static",
keyboard: false,
});
modalRef.componentInstance.mode = "add";
modalRef.result.then(
(result) => {
if (result) {
if (
this.dataBuildingList.some(
(value) =>
value.name.trim().toLowerCase() ===
result.name.trim().toLowerCase()
)
) {
this.toastr.error("Warning", "Data yang anda masukan double.", {
timeOut: 5000,
closeButton: true,
});
} else {
this.monitoringApiService
.postMasterBuildingParam(result)
.subscribe((res) => {
this.dataListBuilding();
this.lewatModal = true;
});
}
}
},
(reason) => {
console.log(`Dismissed: ${reason}`);
}
);
}
modalAddFloor() {
const modalRef = this.modalService.open(AddEditMasterComponent, {
size: "lg",
backdrop: "static",
keyboard: false,
});
modalRef.componentInstance.headerId = 6;
modalRef.componentInstance.mode = "add";
modalRef.componentInstance.floor = true;
modalRef.result.then(
(result) => {
if (result) {
this.monitoringApiService
.postHeaderDetailParam(result)
.subscribe((res) => {
this.dataListFloor();
this.lewatModal = true;
if (res.status === 400) {
this.toastr.error("Warning", "Data yang anda masukan double.", {
timeOut: 5000,
closeButton: true,
});
}
});
}
},
(reason) => {
console.log(`Dismissed: ${reason}`);
}
);
}
modalAddNewRoom(){
const modalRef = this.modalService.open(AddEditMasterRoomComponent, {
size: "lg",
backdrop: "static",
keyboard: false,
});
modalRef.componentInstance.mode = "add";
modalRef.result.then(
(result) => {
if (result) {
const filteredData = {
name: result.name,
code: result.code.trim(),
description: result.description,
};
this.monitoringApiService
.postMasterRoomParam(filteredData)
.subscribe((res) => {
this.dataListRoom();
}, (error) => {
this.toastr.error("Error", error.error.message, {
timeOut: 2000,
closeButton: true,
});
});
}
},
(reason) => {
console.log(`Dismissed: ${reason}`);
}
);
}
}

View File

@ -0,0 +1,350 @@
:host
::ng-deep
.ngx-datatable.bootstrap
.datatable-header
.datatable-header-cell
.datatable-header-cell-label {
font-family: inherit;
font-size: medium;
font-weight: bold;
color: #6b6f82;
}
:host ::ng-deep .ngx-datatable .datatable-row-center,
.ngx-datatable .datatable-row-group,
.ngx-datatable .datatable-row-right {
position: relative;
height: 50px !important;
}
:host ::ng-deep .datatable-icon-right:before {
font-family: "icofont";
font-style: normal;
}
:host ::ng-deep .datatable-icon-skip:before {
font-family: "icofont";
font-style: normal;
}
:host ::ng-deep .datatable-icon-left:before {
font-family: "icofont";
font-style: normal;
}
:host ::ng-deep .datatable-icon-left:before {
content: "\2039";
font-size: x-large;
}
:host ::ng-deep .datatable-icon-prev:before {
content: "\00AB";
font-size: x-large;
}
:host ::ng-deep .datatable-icon-right:before {
content: "\203A";
font-size: x-large;
}
:host ::ng-deep .datatable-icon-skip:before {
content: "\00BB";
font-size: x-large;
}
:host
::ng-deep
.ngx-datatable.bootstrap
.datatable-footer
.datatable-pager
.datatable-icon-left,
.ngx-datatable.bootstrap
.datatable-footer
.datatable-pager
.datatable-icon-right,
.ngx-datatable.bootstrap
.datatable-footer
.datatable-pager
.datatable-icon-prev {
font-size: 16px;
line-height: 22px;
padding: 0px 09px;
background-color: #d4d2e7;
}
:host
::ng-deep
.ngx-datatable.bootstrap
.datatable-footer
.datatable-pager
.datatable-icon-right,
.ngx-datatable.bootstrap
.datatable-footer
.datatable-pager
.datatable-icon-right {
font-size: 16px;
line-height: 22px;
padding: 0px 09px;
background-color: #d4d2e7;
}
:host
::ng-deep
.ngx-datatable.bootstrap
.datatable-footer
.datatable-pager
.datatable-icon-skip {
font-size: 16px;
line-height: 22px;
padding: 0px 09px;
background-color: #d4d2e7;
}
:host
::ng-deep
.ngx-datatable.bootstrap
.datatable-footer
.datatable-pager
.datatable-icon-prev {
font-size: 16px;
line-height: 22px;
padding: 0px 09px;
background-color: #d4d2e7;
}
:host
::ng-deep
.datatable-footer
.datatable-pager
ul
li:not(.disabled).active
a,
.ngx-datatable.bootstrap[_ngcontent-c11]
.datatable-footer[_ngcontent-c11]
.datatable-pager[_ngcontent-c11]
ul[_ngcontent-c11]
li[_ngcontent-c11]:not(.disabled):hover
a[_ngcontent-c11] {
background-color: #d4d2e7;
font-weight: bold;
font-size: larger;
}
:host ::ng-deep .ngx-datatable.bootstrap .datatable-footer .datatable-pager a {
height: 32px;
min-width: 34px;
line-height: 22px;
padding: 0;
border-radius: 3px;
margin: 0 3px;
text-align: center;
vertical-align: top;
padding-top: 3px;
text-decoration: none;
vertical-align: bottom;
color: #7c8091;
}
:host
::ng-deep
.ngx-datatable.bootstrap
.datatable-footer
.datatable-pager
.datatable-icon-left,
.ngx-datatable.bootstrap[_ngcontent-c11]
.datatable-footer[_ngcontent-c11]
.datatable-pager[_ngcontent-c11]
.datatable-icon-right[_ngcontent-c11],
.ngx-datatable.bootstrap[_ngcontent-c11]
.datatable-footer[_ngcontent-c11]
.datatable-pager[_ngcontent-c11]
.datatable-icon-prev[_ngcontent-c11] {
font-size: 14px;
line-height: 9px;
padding: 0px 08px;
background-color: #ffffff;
}
:host
::ng-deep
.ngx-datatable.bootstrap
.datatable-footer
.datatable-pager
.datatable-icon-left,
.ngx-datatable.bootstrap[_ngcontent-c11]
.datatable-footer[_ngcontent-c11]
.datatable-pager[_ngcontent-c11]
.datatable-icon-right[_ngcontent-c11],
.ngx-datatable.bootstrap[_ngcontent-c11]
.datatable-footer[_ngcontent-c11]
.datatable-pager[_ngcontent-c11]
.datatable-icon-prev[_ngcontent-c11] {
font-size: 0px;
line-height: 22px;
padding: 0px 09px;
background-color: #ffffff;
}
:host
::ng-deep
.ngx-datatable.bootstrap
.datatable-footer
.datatable-pager
.datatable-icon-right,
.ngx-datatable.bootstrap[_ngcontent-c11]
.datatable-footer[_ngcontent-c11]
.datatable-pager[_ngcontent-c11]
.datatable-icon-right[_ngcontent-c11],
.ngx-datatable.bootstrap[_ngcontent-c11]
.datatable-footer[_ngcontent-c11]
.datatable-pager[_ngcontent-c11]
.datatable-icon-prev[_ngcontent-c11] {
font-size: 0px;
line-height: 22px;
padding: 0px 09px;
background-color: #ffffff;
}
:host
::ng-deep
.ngx-datatable.bootstrap
.datatable-footer
.datatable-pager
.datatable-icon-skip,
.ngx-datatable.bootstrap[_ngcontent-c11]
.datatable-footer[_ngcontent-c11]
.datatable-pager[_ngcontent-c11]
.datatable-icon-right[_ngcontent-c11],
.ngx-datatable.bootstrap[_ngcontent-c11]
.datatable-footer[_ngcontent-c11]
.datatable-pager[_ngcontent-c11]
.datatable-icon-prev[_ngcontent-c11] {
font-size: 0px;
line-height: 22px;
padding: 0px 09px;
background-color: #ffffff;
}
:host
::ng-deep
.ngx-datatable.bootstrap
.datatable-footer
.datatable-pager
.datatable-icon-prev,
.ngx-datatable.bootstrap[_ngcontent-c11]
.datatable-footer[_ngcontent-c11]
.datatable-pager[_ngcontent-c11]
.datatable-icon-right[_ngcontent-c11],
.ngx-datatable.bootstrap[_ngcontent-c11]
.datatable-footer[_ngcontent-c11]
.datatable-pager[_ngcontent-c11]
.datatable-icon-prev[_ngcontent-c11] {
font-size: 0px;
line-height: 22px;
padding: 0px 09px;
background-color: #ffffff;
}
:host
::ng-deep
.ngx-datatable.bootstrap
.datatable-footer
.datatable-pager
ul
li:not(.disabled):hover
a {
background-color: #545454;
font-weight: bold;
color: white;
}
:host
::ng-deep
.ngx-datatable.bootstrap
.datatable-footer
.datatable-pager
ul
li:not(.disabled).active
a,
.ngx-datatable.bootstrap
.datatable-footer
.datatable-pager
ul
li:not(.disabled):hover
a {
background-color: #37A647;
font-weight: bold;
color: white;
}
:host ::ng-deep .ngx-datatable.bootstrap .datatable-footer {
background: #DDE1E6;
color: #242222;
margin-top: -1px;
overflow: inherit;
}
:host ::ng-deep .ngx-datatable.bootstrap .datatable-header {
background: #DDE1E6;
color: #242222;
font-weight: bold;
height: unset !important;
overflow: inherit;
}
:host ::ng-deep .ngx-datatable .datatable-footer .datatable-pager {
flex: 0 0 0%;
}
:host ::ng-deep .ngx-datatable .datatable-footer .datatable-pager .pager {
display: flex;
}
:host
::ng-deep
.ngx-datatable
.datatable-footer
.selected-count
.datatable-pager {
flex: 0 0 0%;
}
:host ::ng-deep .ngx-datatable {
display: -webkit-box;
}
:host ::ng-deep .ng-select .ng-select-container {
color: #242222 !important;
background-color: #FBFBFB !important;
height: 40px !important;
border-radius: 12px !important;
box-shadow: 0 2px 4px rgba(36, 34, 34, 0.2) !important; /* Bayangan lebih tipis */
}
:host ::ng-deep .ng-select .ng-select-container .ng-value-container .ng-input>input {
color: #242222 !important;
}
:host ::ng-deep .ngx-datatable.bootstrap .datatable-body-row {
background-color: #FBFBFB; /* Black color for table rows */
}
:host ::ng-deep .ngx-datatable.bootstrap .datatable-body-row:hover {
background-color: #DDE1E6; /* Darker black for hover effect */
}
.text-custom-label{
color: #242222 !important;
font-family: "Open Sans", sans-serif !important;
font-size: 16px;
}
.text-custom-data{
color: #242222 !important;
font-family: "Open Sans", sans-serif !important;
font-size: 24px;
font-weight: 700;
}
.style-custom-label{
color: #242222 !important;
}

View File

@ -0,0 +1,172 @@
<div class="app-content content" style="background-color: #FBFBFB !important">
<div class="content-wrapper" style="height: 900px !important;">
<div class="content-header row mb-1">
<app-breadcrumb class="col-12" [breadcrumb]="breadcrumb"></app-breadcrumb>
</div>
<div class="content-body">
<section id="configuration">
<div class="row">
<div class="col-12">
<div class="card" style="background-color: #FBFBFB !important">
<div class="card-content">
<div class="card-body">
<div class="row mb-2">
<div class="col-md-6">
<input
type="text"
class="form-control"
placeholder="Search..."
[(ngModel)]="searchTerm"
(input)="filterRows()"
(touchstart)="onTouchStart($event)"
/>
</div>
<div class="col-md-6 text-right">
<button
class="btn btn-secondary"
[routerLink]="['/list-monitoring/add-row']"
style="
background-color: #37A647 !important;
border-color: #37A647 !important;
color: #ffffff !important;
"
>
<i class="feather ft-plus" style="color: #ffffff"></i
>&nbsp;
<span style="font-weight: 600">Add new data</span>
</button>
</div>
</div>
<div class="card-dashboard">
<ngx-datatable
class="bootstrap table-bordered"
[limit]="10"
[rows]="filteredRows"
[columnMode]="'force'"
[headerHeight]="50"
[footerHeight]="50"
[rowHeight]="50"
fxFlex="auto"
[scrollbarH]="true"
>
<ngx-datatable-column
name="#"
[flexGrow]="1"
[minWidth]="10"
>
<ng-template
ngx-datatable-cell-template
let-rowIndex="rowIndex"
>
<p class="style-custom-label">{{ rowIndex + 1 }}</p>
</ng-template>
</ngx-datatable-column>
<ngx-datatable-column
name="buildingEntity"
[flexGrow]="1"
[minWidth]="90"
>
<ng-template ngx-datatable-header-template>
<span class="style-custom-label">Building</span>
</ng-template>
<ng-template
let-value="value"
ngx-datatable-cell-template
>
<p class="style-custom-label">{{ value.name }}</p>
</ng-template>
</ngx-datatable-column>
<ngx-datatable-column
name="roomEntity"
[flexGrow]="1"
[minWidth]="90"
>
<ng-template ngx-datatable-header-template>
<span class="style-custom-label">Room</span>
</ng-template>
<ng-template
ngx-datatable-cell-template
let-value="value"
>
<p class="style-custom-label">{{ value.name }}</p>
</ng-template>
</ngx-datatable-column>
<ngx-datatable-column
name="floorEntity"
[flexGrow]="1"
[minWidth]="90"
>
<ng-template ngx-datatable-header-template>
<span class="style-custom-label">Floor</span>
</ng-template>
<ng-template
ngx-datatable-cell-template
let-value="value"
>
<p class="style-custom-label">{{ value.name }}</p>
</ng-template>
</ngx-datatable-column>
<ngx-datatable-column
name="statusEntity"
[flexGrow]="1"
[minWidth]="90"
>
<ng-template ngx-datatable-header-template>
<span class="style-custom-label">Status</span>
</ng-template>
<ng-template
ngx-datatable-cell-template
let-value="value"
>
<p class="style-custom-label">{{ value.name }}</p>
</ng-template>
</ngx-datatable-column>
<ngx-datatable-column
name="Actions"
[flexGrow]="1"
[minWidth]="150"
>
<ng-template ngx-datatable-header-template>
<span class="style-custom-label">Actions</span>
</ng-template>
<ng-template
ngx-datatable-cell-template
let-rowIndex="rowIndex"
let-row="row"
>
<!-- <button
class="btn btn-sm btn-warning mr-1"
style="background-color: #FBFBFB !important; border-color: #37A647 !important;"
(click)="editRow(row)"
>
<i class="ficon feather ft-edit" style="color: #37A647 !important;"></i>
</button> -->
<button
class="btn btn-sm btn-danger"
style="
color: #37a647 !important;
border-color: #DDE1E6 !important;
background-color: #DDE1E6 !important;
"
(click)="deleteRow(row)"
>
<i class="ficon feather ft-trash-2" style="color: #37A647 !important;"></i>
</button>
</ng-template>
</ngx-datatable-column>
</ngx-datatable>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
</div>
</div>
</div>

View File

@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ListMonitoringComponent } from './list-monitoring.component';
describe('ListMonitoringComponent', () => {
let component: ListMonitoringComponent;
let fixture: ComponentFixture<ListMonitoringComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ ListMonitoringComponent ]
})
.compileComponents();
fixture = TestBed.createComponent(ListMonitoringComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,108 @@
import { Component } from "@angular/core";
import { Router } from "@angular/router";
import { TableApiService } from "src/app/_services/table-api.service";
import { BuildingService } from "../service/monitoring-api.service";
import Swal from "sweetalert2";
import { LoginService } from "../service/login.service";
import { ToastrService } from "ngx-toastr";
@Component({
selector: "app-list-monitoring",
templateUrl: "./list-monitoring.component.html",
styleUrls: ["./list-monitoring.component.css"],
})
export class ListMonitoringComponent {
public breadcrumb: any;
data: any;
filteredRows: any[];
searchTerm: string = "";
rows: any = [];
constructor(
private router: Router,
private monitoringApiService: BuildingService,
private authService: LoginService,
private toastr: ToastrService,
) {}
ngOnInit() {
this.authService.startTokenCheck();
this.authService.startTrackingActivity();
this.breadcrumb = {
mainlabel: "List Monitoring",
links: [
{
name: "Home",
isLink: false,
},
{
name: "List Monitoring",
isLink: false,
},
],
};
this.fetchData();
}
fetchData() {
this.monitoringApiService.getBuildingRoomList().subscribe((res) => {
this.data = res;
// this.filteredRows = this.data.results.data;
this.filteredRows = this.data.results.data.sort((a, b) => b.id - a.id);
});
}
filterRows() {
if (!this.searchTerm) {
this.filteredRows = [...this.data.results.data];
} else {
const searchTermLC = this.searchTerm.toLowerCase();
this.filteredRows = this.data.results.data.filter((row) =>
this.rowContainsSearchTerm(row, searchTermLC)
);
}
}
rowContainsSearchTerm(row: any, searchTermLC: string): boolean {
return (
row.buildingEntity.name.toLowerCase().includes(searchTermLC) ||
row.roomEntity.name.toLowerCase().includes(searchTermLC) ||
row.statusEntity.name.toLowerCase().includes(searchTermLC) ||
row.floorEntity?.name.toLowerCase().includes(searchTermLC)
);
}
viewRow(row) {
this.router.navigate(["/list-monitoring/view", row.id]);
}
editRow(row) {
this.router.navigate(["/list-monitoring/edit", row.id]);
}
deleteRow(row) {
const confirmDelete = confirm("Are you sure you want to delete this item?");
if (confirmDelete) {
this.monitoringApiService.deleteRoomBuilding(row.id).subscribe(
(res) => {
this.fetchData();
this.toastr.success("Success", "Delete Completed.", {
timeOut: 2000,
closeButton: true,
});
},
(error) => {
console.error(error);
this.toastr.error("Error", "Data sedang digunakan!", {
timeOut: 2000,
closeButton: true,
});
}
);
}
}
onTouchStart(event: Event) {
event.preventDefault(); // Add this if necessary
}
}

View File

@ -0,0 +1,60 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterModule } from '@angular/router';
import { NgxDatatableModule } from '@swimlane/ngx-datatable';
import { NgSelectModule } from '@ng-select/ng-select';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { ClipboardModule } from 'ngx-clipboard';
import { CardModule } from '../../partials/general/card/card.module';
import { BreadcrumbModule } from 'src/app/_layout/breadcrumb/breadcrumb.module';
import { BlockUIModule } from 'ng-block-ui';
import { BlockTemplateComponent } from '../../../_layout/blockui/block-template.component';
import { PerfectScrollbarModule } from 'ngx-perfect-scrollbar';
import { ListMonitoringComponent } from './list-monitoring.component';
import { AddEditListComponent } from './add-edit-list/add-edit-list.component';
@NgModule({
declarations: [
ListMonitoringComponent,
AddEditListComponent
],
imports: [
CommonModule,
CardModule,
BreadcrumbModule,
NgSelectModule,
FormsModule,
ReactiveFormsModule,
ClipboardModule,
PerfectScrollbarModule,
NgxDatatableModule,
BlockUIModule.forRoot({
template: BlockTemplateComponent
}),
RouterModule.forChild([
{
path: '',
component: ListMonitoringComponent
},
{
path: 'add-row',
component: AddEditListComponent,
data: { mode: 'add' }
},
{
path: 'edit/:id',
component: AddEditListComponent,
data: { mode: 'edit' }
},
{
path: 'view/:id',
component: AddEditListComponent,
data: { mode: 'view' }
}
])
]
})
export class ListMonitoringModule { }

View File

@ -0,0 +1,54 @@
/* modal-add-edit.component.css */
::ng-deep .modal-backdrop.show {
z-index: auto !important;
}
/*
::ng-deep .input-group-append .btn {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
border-radius: 0;
border-left: 0;
flex-grow: 0;
border-left: 1px solid #ced4da;
padding: 0.375rem 0.75rem;
}
::ng-deep .input-group {
display: flex;
flex-wrap: nowrap;
align-items: center;
}
::ng-deep .form-control {
flex-grow: 1;
padding-right: 0.5rem;
}
::ng-deep .input-group select,
::ng-deep .input-group .input-group-append .btn {
padding-right: 5px;
}
::ng-deep .input-group .form-control {
margin-right: 2px;
}
.form-group {
margin-bottom: 1rem;
}
.form-control {
display: block;
width: 100%;
height: calc(1.5em + 0.75rem + 2px);
padding: 0.375rem 0.75rem;
font-size: 1rem;
font-weight: 400;
line-height: 1.5;
color: #495057;
background-color: #fff;
background-clip: padding-box;
border: 1px solid #ced4da;
border-radius: 0.25rem;
transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
} */

View File

@ -0,0 +1,79 @@
<div class="modal-header" style="background-color: #FBFBFB !important">
<h4 class="modal-title" style="color: #242222">{{labelModal}}</h4>
<button type="button" class="close" aria-label="Close" (click)="activeModal.dismiss('Cross click')">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body" style="background-color: #FBFBFB !important">
<form [formGroup]="myForm">
<div class="form-row">
<div class="form-group col-md-6">
<label for="name" style="color: #242222">Name:</label>
<input
type="text"
class="form-control"
id="name"
formControlName="name"
maxlength="50"
/>
<div *ngIf="myForm.get('name').touched && myForm.get('name').invalid" class="text-danger">
Name is required.
</div>
</div>
<div class="form-group col-md-6" *ngIf="floor">
<label for="code" style="color: #242222">Code:</label>
<input
type="text"
class="form-control"
id="code"
formControlName="code"
maxlength="10"
(input)="toUppercase($event)"
/>
<div *ngIf="myForm.get('code').touched && myForm.get('code').invalid" class="text-danger">
code is required.
</div>
</div>
<div class="form-group col-md-6" *ngIf="category">
<label for="icon" style="color: #242222">Icon:</label>
<app-select-icon [selectedIcon]="selectedIcon" (iconSelected)="onIconSelected($event)"></app-select-icon>
<div *ngIf="myForm.get('icon').touched && myForm.get('icon').invalid" class="text-danger">
Icon is required.
</div>
</div>
<div class="form-group col-md-6">
<label for="status" style="color: #242222">Status:</label>
<select
id="projectinput5"
class="form-control custom-select"
formControlName="status"
>
<option *ngFor="let data of dataMasterStatus" [value]="data.id">
{{ data.name }}
</option>
</select>
<div *ngIf="myForm.get('status').touched && myForm.get('status').invalid" class="text-danger">
Status is required.
</div>
</div>
</div>
</form>
</div>
<div class="modal-footer" style="background-color: #FBFBFB !important">
<button
type="button"
class="btn btn-secondary"
style="color: #242222 !important; background-color: #FBFBFB !important; border-color: #FBFBFB !important;"
(click)="activeModal.dismiss('Cross click')"
>
Close
</button>
<button
type="button"
style="color: #ffffff !important; background-color: #37A647 !important"
class="btn btn-primary"
(click)="addRow()"
>
Save
</button>
</div>

View File

@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { AddEditMasterComponent } from './add-edit-master.component';
describe('AddEditMasterComponent', () => {
let component: AddEditMasterComponent;
let fixture: ComponentFixture<AddEditMasterComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ AddEditMasterComponent ]
})
.compileComponents();
fixture = TestBed.createComponent(AddEditMasterComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,127 @@
import { Component, Input, OnInit } from "@angular/core";
import { FormBuilder, FormGroup, Validators } from "@angular/forms";
import { NgbActiveModal } from "@ng-bootstrap/ng-bootstrap";
import { BuildingService } from "../../service/monitoring-api.service";
import { LoginService } from "../../service/login.service";
@Component({
selector: "app-add-edit-master",
templateUrl: "./add-edit-master.component.html",
styleUrls: ["./add-edit-master.component.css"],
})
export class AddEditMasterComponent implements OnInit {
@Input() headerId: number;
@Input() dataRow: any;
@Input() mode: any;
@Input() category: boolean = false;
@Input() floor: boolean = false;
myForm: FormGroup;
dataMasterStatus: any;
selectedIcon: string = '';
labelModal: string = '';
constructor(
public activeModal: NgbActiveModal,
private fb: FormBuilder,
private monitoringApiService: BuildingService,
private authService: LoginService
) {}
ngOnInit() {
this.authService.startTokenCheck();
this.authService.startTrackingActivity();
this.createForm();
this.dataListMaster();
if (this.mode === "add") {
this.labelModal = "Add Row"
} else {
this.labelModal = "Edit Row"
}
if (this.dataRow) {
this.editForm();
}
}
createForm() {
const formControls = {
name: ["", Validators.required],
status: ["", Validators.required],
headerId: this.headerId
};
if (this.category) {
formControls['icon'] = [undefined, Validators.required];
}
if (this.floor) {
formControls['code'] = ['', Validators.required];
}
this.myForm = this.fb.group(formControls);
}
toUppercase(event: Event) {
const input = event.target as HTMLInputElement;
input.value = input.value.toUpperCase();
this.myForm.get('code').setValue(input.value);
}
editForm() {
const formControls = {
id: this.dataRow.id,
name: [this.dataRow.name, Validators.required],
status: [this.dataRow.status, Validators.required],
headerId: this.dataRow.header_id,
};
if (this.category) {
formControls['icon'] = [this.dataRow.icon || undefined , Validators.required];
this.selectedIcon = this.dataRow.icon || undefined;
}
if (this.floor) {
formControls['code'] = [this.dataRow.code || '' , Validators.required];
}
this.myForm = this.fb.group(formControls);
}
dataListMaster() {
this.monitoringApiService.getMasterListData().subscribe((data) => {
const dataCategory = data.data.find(
(item) => item.name === "master_status"
).headerDetailParam;
this.dataMasterStatus = dataCategory.filter(item => item.statusName.toLowerCase() === "aktif" || item.status.toLowerCase() === "71" || item.status.toLowerCase() === "71");
});
}
addRow() {
if (this.myForm.valid) {
this.activeModal.close(this.myForm.value);
} else {
this.markFormGroupTouched(this.myForm)
}
}
markFormGroupTouched(formGroup: FormGroup) {
(Object as any).values(formGroup.controls).forEach((control) => {
control.markAsTouched();
if (control.controls) {
this.markFormGroupTouched(control);
}
});
}
addDepartment() {
let newDept = prompt("Enter new department name:");
if (newDept) {
alert(`Department ${newDept} added! (simulated)`);
}
}
onIconSelected(icon: string): void {
this.selectedIcon = icon;
this.myForm.get('icon').setValue(icon); // Set nilai icon ke dalam form
}
}

View File

@ -0,0 +1,73 @@
/* modal-add-edit.component.css */
::ng-deep .modal-backdrop.show {
z-index: auto !important;
}
::ng-deep .input-group-append .btn {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
border-radius: 0;
border-left: 0;
flex-grow: 0;
border-left: 1px solid #ced4da;
padding: 0.375rem 0.75rem;
}
::ng-deep .input-group {
display: flex;
flex-wrap: nowrap; /* Prevents wrapping of the items */
align-items: center;
}
::ng-deep .form-control {
flex-grow: 1; /* Ensures select takes up available space */
padding-right: 0.5rem;
}
::ng-deep .input-group select,
::ng-deep .input-group .input-group-append .btn {
padding-right: 5px; /* Adjust padding if necessary */
}
::ng-deep .input-group .form-control {
margin-right: 2px; /* Adjust margin to make space */
}
.form-group {
margin-bottom: 1rem;
}
.form-control {
display: block;
width: 100%;
height: calc(1.5em + 0.75rem + 2px);
padding: 0.375rem 0.75rem;
font-size: 1rem;
font-weight: 400;
line-height: 1.5;
color: #495057;
background-color: #fff;
background-clip: padding-box;
border: 1px solid #ced4da;
border-radius: 0.25rem;
transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
}
::ng-deep .no-arrow::-webkit-inner-spin-button,
.no-arrow::-webkit-outer-spin-button {
-webkit-appearance: none;
margin: 0;
}
::ng-deep .no-arrow {
-moz-appearance: textfield;
}
/* Add this CSS to style invalid form controls */
.is-invalid {
border-color: #dc3545;
}
.text-danger {
color: #dc3545;
}

Some files were not shown because too many files have changed in this diff Show More