From 03fe4b1fd4780668ba30baaaf5d8561476144edf Mon Sep 17 00:00:00 2001 From: Wian Drs Date: Fri, 31 Jul 2026 13:57:55 +0700 Subject: [PATCH] Giant Update --- package-lock.json | 552 +++++++++++++++++- package.json | 1 + public/customer-default.svg | 15 + src/assets/icons/index.js | 10 + src/components/AppHeader.vue | 96 ++- src/components/AppSidebar.vue | 29 + src/components/AppSidebarNav.js | 2 + src/components/WalletHeaderWidget.vue | 38 ++ .../base/BaseResponsiveDataView.vue | 163 +++++- .../customers/CustomerLocationMap.vue | 161 +++++ src/router/index.js | 149 +++++ src/services/billingService.js | 14 + src/services/customerService.js | 42 ++ src/services/notificationService.js | 75 +++ src/services/paymentGatewayService.js | 8 + src/services/topologyService.js | 60 ++ src/services/walletService.js | 6 + src/utils/imageCompression.js | 48 ++ src/utils/responsiveViewMode.js | 18 + src/views/billing/BillingProfiles.vue | 109 ++++ src/views/billing/Invoices.vue | 176 ++++++ src/views/customers/CustomerDetailPage.vue | 205 +++++++ src/views/customers/CustomerFormPage.vue | 290 +++++++++ src/views/customers/Customers.vue | 217 +++++++ .../NotificationBroadcasts.vue | 155 +++++ .../NotificationChannelDashboard.vue | 244 ++++++++ .../PaymentGatewaySettings.vue | 75 +++ src/views/payment-gateway/PaymentLogs.vue | 33 ++ src/views/topology/TopologyMap.vue | 157 +++++ src/views/topology/TopologyNetwork.vue | 66 +++ src/views/topology/TopologyResourceView.vue | 287 +++++++++ src/views/wallet/WalletDashboard.vue | 171 ++++++ 32 files changed, 3635 insertions(+), 37 deletions(-) create mode 100644 public/customer-default.svg create mode 100644 src/components/WalletHeaderWidget.vue create mode 100644 src/components/customers/CustomerLocationMap.vue create mode 100644 src/services/billingService.js create mode 100644 src/services/customerService.js create mode 100644 src/services/notificationService.js create mode 100644 src/services/paymentGatewayService.js create mode 100644 src/services/topologyService.js create mode 100644 src/services/walletService.js create mode 100644 src/utils/imageCompression.js create mode 100644 src/utils/responsiveViewMode.js create mode 100644 src/views/billing/BillingProfiles.vue create mode 100644 src/views/billing/Invoices.vue create mode 100644 src/views/customers/CustomerDetailPage.vue create mode 100644 src/views/customers/CustomerFormPage.vue create mode 100644 src/views/customers/Customers.vue create mode 100644 src/views/notifications-service/NotificationBroadcasts.vue create mode 100644 src/views/notifications-service/NotificationChannelDashboard.vue create mode 100644 src/views/payment-gateway/PaymentGatewaySettings.vue create mode 100644 src/views/payment-gateway/PaymentLogs.vue create mode 100644 src/views/topology/TopologyMap.vue create mode 100644 src/views/topology/TopologyNetwork.vue create mode 100644 src/views/topology/TopologyResourceView.vue create mode 100644 src/views/wallet/WalletDashboard.vue diff --git a/package-lock.json b/package-lock.json index e9cb8441..9e6cd287 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,6 +21,7 @@ "bootstrap": "^5.3.8", "bootstrap-icons": "^1.13.1", "chart.js": "^4.5.1", + "maplibre-gl": "^3.6.2", "pinia": "^3.0.4", "simplebar-vue": "^2.4.2", "sweetalert2": "^11.26.25", @@ -527,6 +528,83 @@ "integrity": "sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==", "license": "MIT" }, + "node_modules/@mapbox/geojson-rewind": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@mapbox/geojson-rewind/-/geojson-rewind-0.5.2.tgz", + "integrity": "sha512-tJaT+RbYGJYStt7wI3cq4Nl4SXxG8W7JDG5DMJu97V25RnbNg3QtQtf+KD+VLjNpWKYsRvXDNmNrBgEETr1ifA==", + "license": "ISC", + "dependencies": { + "get-stream": "^6.0.1", + "minimist": "^1.2.6" + }, + "bin": { + "geojson-rewind": "geojson-rewind" + } + }, + "node_modules/@mapbox/jsonlint-lines-primitives": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@mapbox/jsonlint-lines-primitives/-/jsonlint-lines-primitives-2.0.3.tgz", + "integrity": "sha512-0SElaV0uMxEnxzBhhX9WTuPyUeMsAN/SS0i16tjuba4/mio63MG9khjC1a0JAiPGXAwvwm4UfHJURCN7nyudQg==", + "license": "MIT", + "engines": { + "node": ">= 22" + } + }, + "node_modules/@mapbox/point-geometry": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@mapbox/point-geometry/-/point-geometry-0.1.0.tgz", + "integrity": "sha512-6j56HdLTwWGO0fJPlrZtdU/B13q8Uwmo18Ck2GnGgN9PCFyKTZ3UbXeEdRFh18i9XQ92eH2VdtpJHpBD3aripQ==", + "license": "ISC" + }, + "node_modules/@mapbox/tiny-sdf": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@mapbox/tiny-sdf/-/tiny-sdf-2.2.0.tgz", + "integrity": "sha512-LVL4wgI9YAum5V+LNVQO6QgFBPw7/MIIY4XJPNsPDMrjEwcE+JfKk1LuIl8GnF197ejVdC9QdPaxrx5gfgdGXg==", + "license": "BSD-2-Clause" + }, + "node_modules/@mapbox/unitbezier": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@mapbox/unitbezier/-/unitbezier-0.0.1.tgz", + "integrity": "sha512-nMkuDXFv60aBr9soUG5q+GvZYL+2KZHVvsqFCzqnkGEf46U2fvmytHaEVc1/YZbiLn8X+eR3QzX1+dwDO1lxlw==", + "license": "BSD-2-Clause" + }, + "node_modules/@mapbox/vector-tile": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@mapbox/vector-tile/-/vector-tile-1.3.1.tgz", + "integrity": "sha512-MCEddb8u44/xfQ3oD+Srl/tNcQoqTw3goGk2oLsrFxOTc3dUp+kAnby3PvAeeBYSMSjSPD1nd1AJA6W49WnoUw==", + "license": "BSD-3-Clause", + "dependencies": { + "@mapbox/point-geometry": "~0.1.0" + } + }, + "node_modules/@mapbox/whoots-js": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@mapbox/whoots-js/-/whoots-js-3.1.0.tgz", + "integrity": "sha512-Es6WcD0nO5l+2BOQS4uLfNPYQaNDfbot3X1XUoloz+x0mPDS3eeORZJl06HXjwBG1fOGwCRnzK88LMdxKRrd6Q==", + "license": "ISC", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@maplibre/maplibre-gl-style-spec": { + "version": "19.3.3", + "resolved": "https://registry.npmjs.org/@maplibre/maplibre-gl-style-spec/-/maplibre-gl-style-spec-19.3.3.tgz", + "integrity": "sha512-cOZZOVhDSulgK0meTsTkmNXb1ahVvmTmWmfx9gRBwc6hq98wS9JP35ESIoNq3xqEan+UN+gn8187Z6E4NKhLsw==", + "license": "ISC", + "dependencies": { + "@mapbox/jsonlint-lines-primitives": "~2.0.2", + "@mapbox/unitbezier": "^0.0.1", + "json-stringify-pretty-compact": "^3.0.0", + "minimist": "^1.2.8", + "rw": "^1.3.3", + "sort-object": "^3.0.3" + }, + "bin": { + "gl-style-format": "dist/gl-style-format.mjs", + "gl-style-migrate": "dist/gl-style-migrate.mjs", + "gl-style-validate": "dist/gl-style-validate.mjs" + } + }, "node_modules/@napi-rs/wasm-runtime": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.5.tgz", @@ -1163,6 +1241,12 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/geojson": { + "version": "7946.0.16", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz", + "integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==", + "license": "MIT" + }, "node_modules/@types/jsesc": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/@types/jsesc/-/jsesc-2.5.1.tgz", @@ -1176,6 +1260,38 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/mapbox__point-geometry": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/@types/mapbox__point-geometry/-/mapbox__point-geometry-0.1.4.tgz", + "integrity": "sha512-mUWlSxAmYLfwnRBmgYV86tgYmMIICX4kza8YnE/eIlywGe2XoOxlpVnXWwir92xRLjwyarqwpu2EJKD2pk0IUA==", + "license": "MIT" + }, + "node_modules/@types/mapbox__vector-tile": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@types/mapbox__vector-tile/-/mapbox__vector-tile-1.3.4.tgz", + "integrity": "sha512-bpd8dRn9pr6xKvuEBQup8pwQfD4VUyqO/2deGjfpe6AwC8YRlyEipvefyRJUSiCJTZuCb8Pl1ciVV5ekqJ96Bg==", + "license": "MIT", + "dependencies": { + "@types/geojson": "*", + "@types/mapbox__point-geometry": "*", + "@types/pbf": "*" + } + }, + "node_modules/@types/pbf": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/pbf/-/pbf-3.0.5.tgz", + "integrity": "sha512-j3pOPiEcWZ34R6a6mN07mUkM4o4Lwf6hPNt8eilOeZhTFbxFXmKhvXl9Y28jotFPaI1bpPDJsbCprUoNke6OrA==", + "license": "MIT" + }, + "node_modules/@types/supercluster": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@types/supercluster/-/supercluster-7.1.3.tgz", + "integrity": "sha512-Z0pOY34GDFl3Q6hUFYf3HkTwKEE02e7QgtJppBt+beEAxnyOpJua+voGFvxINBHa06GwLFFym7gRPY2SiKIfIA==", + "license": "MIT", + "dependencies": { + "@types/geojson": "*" + } + }, "node_modules/@vitejs/plugin-vue": { "version": "6.0.7", "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-6.0.7.tgz", @@ -1427,6 +1543,24 @@ "dev": true, "license": "Python-2.0" }, + "node_modules/arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/ast-kit": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/ast-kit/-/ast-kit-2.2.0.tgz", @@ -1631,6 +1765,25 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/bytewise": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/bytewise/-/bytewise-1.1.0.tgz", + "integrity": "sha512-rHuuseJ9iQ0na6UDhnrRVDh8YnWVlU6xM3VH6q/+yHDeUH2zIhUzP+2/h3LIrhLDBtTqzWpE3p3tP/boefskKQ==", + "license": "MIT", + "dependencies": { + "bytewise-core": "^1.2.2", + "typewise": "^1.0.3" + } + }, + "node_modules/bytewise-core": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/bytewise-core/-/bytewise-core-1.2.3.tgz", + "integrity": "sha512-nZD//kc78OOxeYtRlVk8/zXqTB4gf/nlguL1ggWA8FuchMyOxcyHR4QPQZMUmA7czC+YnaBrPUCubqAWe50DaA==", + "license": "MIT", + "dependencies": { + "typewise-core": "^1.2" + } + }, "node_modules/call-bind-apply-helpers": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", @@ -1870,6 +2023,12 @@ "node": ">= 0.4" } }, + "node_modules/earcut": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/earcut/-/earcut-2.2.4.tgz", + "integrity": "sha512-/pjZsA1b4RPHbeWZQn66SWS8nZZWLQQ23oE3Eam7aroEFGEvwKAsJfZ9ytiEMycfzXWpca4FA9QIOehf7PocBQ==", + "license": "ISC" + }, "node_modules/electron-to-chromium": { "version": "1.5.376", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.376.tgz", @@ -2155,6 +2314,18 @@ "integrity": "sha512-D+42+T12DdIlJM3uepa55qGiL3sYdLBOxIl2ifQCzCHz4c7eiolaHsi3BIqEr7JxBzxv2pYZQX9kw16ziMcEmw==", "license": "MIT" }, + "node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "license": "MIT", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -2317,6 +2488,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/geojson-vt": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/geojson-vt/-/geojson-vt-3.2.1.tgz", + "integrity": "sha512-EvGQQi/zPrDA6zr6BnJD/YhwAkBP8nnJ9emh3EnHQKVMfg/MRVtPbMYdgVy/IaEmn4UfagD2a6fafPDL5hbtwg==", + "license": "ISC" + }, "node_modules/get-intrinsic": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", @@ -2354,6 +2531,33 @@ "node": ">= 0.4" } }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gl-matrix": { + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/gl-matrix/-/gl-matrix-3.4.4.tgz", + "integrity": "sha512-latSnyDNt/8zYUB6VIJ6PCh2jBjJX6gnDsoCZ7LyW7GkqrD51EWwa9qCoGixj8YqBtETQK/xY7OmpTF8xz1DdQ==", + "license": "MIT" + }, "node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -2367,6 +2571,32 @@ "node": ">=10.13.0" } }, + "node_modules/global-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", + "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", + "license": "MIT", + "dependencies": { + "ini": "^1.3.5", + "kind-of": "^6.0.2", + "which": "^1.3.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/global-prefix/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, "node_modules/globals": { "version": "16.5.0", "resolved": "https://registry.npmjs.org/globals/-/globals-16.5.0.tgz", @@ -2476,6 +2706,26 @@ "node": ">= 6" } }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -2520,6 +2770,21 @@ "node": ">=0.8.19" } }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "license": "ISC" + }, + "node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -2543,6 +2808,18 @@ "node": ">=0.10.0" } }, + "node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "license": "MIT", + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-what": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/is-what/-/is-what-5.5.0.tgz", @@ -2559,9 +2836,17 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, "license": "ISC" }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/js-yaml": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz", @@ -2618,6 +2903,12 @@ "dev": true, "license": "MIT" }, + "node_modules/json-stringify-pretty-compact": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/json-stringify-pretty-compact/-/json-stringify-pretty-compact-3.0.0.tgz", + "integrity": "sha512-Rc2suX5meI0S3bfdZuA7JMFBGkJ875ApfVyq2WHELjBiiG22My/l7/8zPpH/CfFVQHuVLd8NLR0nv6vi0BYYKA==", + "license": "MIT" + }, "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", @@ -2630,6 +2921,12 @@ "node": ">=6" } }, + "node_modules/kdbush": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/kdbush/-/kdbush-4.1.0.tgz", + "integrity": "sha512-e9vurzrXJQrFX6ckpHP3bvj5l+9CnYzkxDNnNQ1h2QTqdWsUAJgXiKdGNcOa1EY85dU8KbQ+z/FdQdB7P+9yfQ==", + "license": "ISC" + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -2640,6 +2937,15 @@ "json-buffer": "3.0.1" } }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -2992,6 +3298,46 @@ "url": "https://github.com/sponsors/sxzz" } }, + "node_modules/maplibre-gl": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/maplibre-gl/-/maplibre-gl-3.6.2.tgz", + "integrity": "sha512-krg2KFIdOpLPngONDhP6ixCoWl5kbdMINP0moMSJFVX7wX1Clm2M9hlNKXS8vBGlVWwR5R3ZfI6IPrYz7c+aCQ==", + "license": "BSD-3-Clause", + "dependencies": { + "@mapbox/geojson-rewind": "^0.5.2", + "@mapbox/jsonlint-lines-primitives": "^2.0.2", + "@mapbox/point-geometry": "^0.1.0", + "@mapbox/tiny-sdf": "^2.0.6", + "@mapbox/unitbezier": "^0.0.1", + "@mapbox/vector-tile": "^1.3.1", + "@mapbox/whoots-js": "^3.1.0", + "@maplibre/maplibre-gl-style-spec": "^19.3.3", + "@types/geojson": "^7946.0.13", + "@types/mapbox__point-geometry": "^0.1.4", + "@types/mapbox__vector-tile": "^1.3.4", + "@types/pbf": "^3.0.5", + "@types/supercluster": "^7.1.3", + "earcut": "^2.2.4", + "geojson-vt": "^3.2.1", + "gl-matrix": "^3.4.3", + "global-prefix": "^3.0.0", + "kdbush": "^4.0.2", + "murmurhash-js": "^1.0.0", + "pbf": "^3.2.1", + "potpack": "^2.0.0", + "quickselect": "^2.0.0", + "supercluster": "^8.0.1", + "tinyqueue": "^2.0.3", + "vt-pbf": "^3.1.3" + }, + "engines": { + "node": ">=16.14.0", + "npm": ">=8.1.0" + }, + "funding": { + "url": "https://github.com/maplibre/maplibre-gl-js?sponsor=1" + } + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -3035,6 +3381,15 @@ "node": "*" } }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/mitt": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", @@ -3082,6 +3437,12 @@ "integrity": "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==", "license": "MIT" }, + "node_modules/murmurhash-js": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/murmurhash-js/-/murmurhash-js-1.0.0.tgz", + "integrity": "sha512-TvmkNhkv8yct0SVBSy+o8wYzXjE4Zz3PCesbfs8HiCXXdcTuocApFv11UWlNFWKYsP2okqrhb7JNlSm9InBhIw==", + "license": "MIT" + }, "node_modules/nanoid": { "version": "3.3.15", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.15.tgz", @@ -3226,6 +3587,19 @@ "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", "license": "MIT" }, + "node_modules/pbf": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/pbf/-/pbf-3.3.0.tgz", + "integrity": "sha512-XDF38WCH3z5OV/OVa8GKUNtLAyneuzbCisx7QUCF8Q6Nutx0WnJrQe5O+kOtBlLfRNUws98Y58Lblp+NJG5T4Q==", + "license": "BSD-3-Clause", + "dependencies": { + "ieee754": "^1.1.12", + "resolve-protobuf-schema": "^2.1.0" + }, + "bin": { + "pbf": "bin/pbf" + } + }, "node_modules/perfect-debounce": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz", @@ -3331,6 +3705,12 @@ "dev": true, "license": "MIT" }, + "node_modules/potpack": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/potpack/-/potpack-2.1.0.tgz", + "integrity": "sha512-pcaShQc1Shq0y+E7GqJqvZj8DTthWV1KeHGdi0Z6IAin2Oi3JnLCOfwnCo84qc+HAp52wT9nK9H7FAJp5a44GQ==", + "license": "ISC" + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -3341,6 +3721,12 @@ "node": ">= 0.8.0" } }, + "node_modules/protocol-buffers-schema": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.6.1.tgz", + "integrity": "sha512-VG2K63Igkiv9p76tk1lilczEK1cT+kCjKtkdhw1dQZV3k3IXJbd3o6Ho8b9zJZaHSnT2hKe4I+ObmX9w6m5SmQ==", + "license": "MIT" + }, "node_modules/proxy-from-env": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz", @@ -3376,6 +3762,12 @@ ], "license": "MIT" }, + "node_modules/quickselect": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-2.0.0.tgz", + "integrity": "sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw==", + "license": "ISC" + }, "node_modules/readdirp": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.0.0.tgz", @@ -3399,6 +3791,15 @@ "node": ">=4" } }, + "node_modules/resolve-protobuf-schema": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/resolve-protobuf-schema/-/resolve-protobuf-schema-2.1.0.tgz", + "integrity": "sha512-kI5ffTiZWmJaS/huM8wZfEMer1eRd7oJQhDuxeCLe3t7N7mX3z94CN0xPxBQxFYQTSNz9T0i+v6inKqSdK8xrQ==", + "license": "MIT", + "dependencies": { + "protocol-buffers-schema": "^3.3.1" + } + }, "node_modules/rfdc": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", @@ -3439,6 +3840,12 @@ "@rolldown/binding-win32-x64-msvc": "1.0.3" } }, + "node_modules/rw": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", + "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==", + "license": "BSD-3-Clause" + }, "node_modules/sass": { "version": "1.101.0", "resolved": "https://registry.npmjs.org/sass/-/sass-1.101.0.tgz", @@ -3479,6 +3886,21 @@ "node": ">=10" } }, + "node_modules/set-value": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", + "license": "MIT", + "dependencies": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -3551,6 +3973,41 @@ } } }, + "node_modules/sort-asc": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/sort-asc/-/sort-asc-0.2.0.tgz", + "integrity": "sha512-umMGhjPeHAI6YjABoSTrFp2zaBtXBej1a0yKkuMUyjjqu6FJsTF+JYwCswWDg+zJfk/5npWUUbd33HH/WLzpaA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sort-desc": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/sort-desc/-/sort-desc-0.2.0.tgz", + "integrity": "sha512-NqZqyvL4VPW+RAxxXnB8gvE1kyikh8+pR+T+CXLksVRN9eiQqkQlPwqWYU0mF9Jm7UnctShlxLyAt1CaBOTL1w==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sort-object": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/sort-object/-/sort-object-3.0.3.tgz", + "integrity": "sha512-nK7WOY8jik6zaG9CRwZTaD5O7ETWDLZYMM12pqY8htll+7dYeqGfEUPcUBHOpSJg2vJOrvFIY2Dl5cX2ih1hAQ==", + "license": "MIT", + "dependencies": { + "bytewise": "^1.1.0", + "get-value": "^2.0.2", + "is-extendable": "^0.1.1", + "sort-asc": "^0.2.0", + "sort-desc": "^0.2.0", + "union-value": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", @@ -3569,6 +4026,43 @@ "node": ">=0.10.0" } }, + "node_modules/split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "license": "MIT", + "dependencies": { + "extend-shallow": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/split-string/node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", + "license": "MIT", + "dependencies": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/split-string/node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "license": "MIT", + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -3582,6 +4076,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/supercluster": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/supercluster/-/supercluster-8.0.1.tgz", + "integrity": "sha512-IiOea5kJ9iqzD2t7QJq/cREyLHTtSmUT6gQsweojg9WH2sYJqZK9SswTu6jrscO6D1G5v5vYZ9ru/eq85lXeZQ==", + "license": "ISC", + "dependencies": { + "kdbush": "^4.0.2" + } + }, "node_modules/superjson": { "version": "2.2.6", "resolved": "https://registry.npmjs.org/superjson/-/superjson-2.2.6.tgz", @@ -3633,6 +4136,12 @@ "url": "https://github.com/sponsors/SuperchupuDev" } }, + "node_modules/tinyqueue": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/tinyqueue/-/tinyqueue-2.0.3.tgz", + "integrity": "sha512-ppJZNDuKGgxzkHihX8v9v9G5f+18gzaTfrukGrq6ueg0lmH4nqVnA2IPG0AEH3jKEk2GRJCUhDoqpoiw3PHLBA==", + "license": "ISC" + }, "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", @@ -3653,12 +4162,42 @@ "node": ">= 0.8.0" } }, + "node_modules/typewise": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typewise/-/typewise-1.0.3.tgz", + "integrity": "sha512-aXofE06xGhaQSPzt8hlTY+/YWQhm9P0jYUp1f2XtmW/3Bk0qzXcyFWAtPoo2uTGQj1ZwbDuSyuxicq+aDo8lCQ==", + "license": "MIT", + "dependencies": { + "typewise-core": "^1.2.0" + } + }, + "node_modules/typewise-core": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/typewise-core/-/typewise-core-1.2.0.tgz", + "integrity": "sha512-2SCC/WLzj2SbUwzFOzqMCkz5amXLlxtJqDKTICqg30x+2DZxcfZN2MvQZmGfXWKNWaKK9pBPsvkcwv8bF/gxKg==", + "license": "MIT" + }, "node_modules/ufo": { "version": "1.6.4", "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.4.tgz", "integrity": "sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==", "license": "MIT" }, + "node_modules/union-value": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", + "license": "MIT", + "dependencies": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^2.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/unplugin": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-3.0.0.tgz", @@ -3815,6 +4354,17 @@ } } }, + "node_modules/vt-pbf": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/vt-pbf/-/vt-pbf-3.1.3.tgz", + "integrity": "sha512-2LzDFzt0mZKZ9IpVF2r69G9bXaP2Q2sArJCmcCgvfTdCCZzSyz4aCLoQyUilu37Ll56tCblIZrXFIjNUpGIlmA==", + "license": "MIT", + "dependencies": { + "@mapbox/point-geometry": "0.1.0", + "@mapbox/vector-tile": "^1.3.1", + "pbf": "^3.2.1" + } + }, "node_modules/vue": { "version": "3.5.38", "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.38.tgz", diff --git a/package.json b/package.json index d1239c83..ef459777 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "bootstrap": "^5.3.8", "bootstrap-icons": "^1.13.1", "chart.js": "^4.5.1", + "maplibre-gl": "^3.6.2", "pinia": "^3.0.4", "simplebar-vue": "^2.4.2", "sweetalert2": "^11.26.25", diff --git a/public/customer-default.svg b/public/customer-default.svg new file mode 100644 index 00000000..7551c455 --- /dev/null +++ b/public/customer-default.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + Dokumentasi Customer + diff --git a/src/assets/icons/index.js b/src/assets/icons/index.js index 97be2201..4e444635 100644 --- a/src/assets/icons/index.js +++ b/src/assets/icons/index.js @@ -43,7 +43,10 @@ import { cilCode, cilCommentSquare, cilContrast, + cilCreditCard, cilCursor, + cilDataTransferDown, + cilDataTransferUp, cilDrop, cilDollar, cilEnvelopeClosed, @@ -76,9 +79,11 @@ import { cilStar, cilSun, cilTask, + cilTransfer, cilUser, cilUserFemale, cilUserFollow, + cilWallet, cilXCircle, cilSquare, // new add cilBarcode, @@ -109,7 +114,10 @@ export const iconsSet = Object.assign( cilCode, cilCommentSquare, cilContrast, + cilCreditCard, cilCursor, + cilDataTransferDown, + cilDataTransferUp, cilDrop, cilDollar, cilEnvelopeClosed, @@ -142,9 +150,11 @@ export const iconsSet = Object.assign( cilStar, cilSun, cilTask, + cilTransfer, cilUser, cilUserFemale, cilUserFollow, + cilWallet, cilXCircle, cilSquare, // new add cilBarcode, diff --git a/src/components/AppHeader.vue b/src/components/AppHeader.vue index 8ce993a9..d78f8e4d 100644 --- a/src/components/AppHeader.vue +++ b/src/components/AppHeader.vue @@ -1,23 +1,68 @@ @@ -39,14 +84,28 @@ onMounted(() => { - - + + - - + {{ unreadCount > 9 ? '9+' : unreadCount }} + + +
Notifikasi{{ unreadCount }} belum dibaca
+ +
Belum ada notifikasi
+
+ - - + + @@ -106,3 +165,12 @@ onMounted(() => { --> + + diff --git a/src/components/AppSidebar.vue b/src/components/AppSidebar.vue index c11bb4e8..7145fad7 100644 --- a/src/components/AppSidebar.vue +++ b/src/components/AppSidebar.vue @@ -5,6 +5,7 @@ import { RouterLink } from 'vue-router' import logo from '@/assets/images/Logo_big.png' import { sygnet } from '@/assets/brand/sygnet' import { AppSidebarNav } from '@/components/AppSidebarNav.js' +import WalletHeaderWidget from '@/components/WalletHeaderWidget.vue' import { useSidebarStore } from '@/stores/sidebar.js' const sidebar = useSidebarStore() @@ -46,9 +47,37 @@ const sidebar = useSidebarStore() + + + diff --git a/src/components/AppSidebarNav.js b/src/components/AppSidebarNav.js index 4c455960..a49e7081 100644 --- a/src/components/AppSidebarNav.js +++ b/src/components/AppSidebarNav.js @@ -26,6 +26,8 @@ const isActiveLink = (route, link) => { const targetPath = normalizePath(link) const activeMenuPath = route.meta?.activeMenu ? normalizePath(route.meta.activeMenu) + : route.meta?.activeMenuBase && route.params?.scope + ? normalizePath(`${route.meta.activeMenuBase}/${route.params.scope}`) : null return currentPath === targetPath || activeMenuPath === targetPath diff --git a/src/components/WalletHeaderWidget.vue b/src/components/WalletHeaderWidget.vue new file mode 100644 index 00000000..588dbb70 --- /dev/null +++ b/src/components/WalletHeaderWidget.vue @@ -0,0 +1,38 @@ + + + + + diff --git a/src/components/base/BaseResponsiveDataView.vue b/src/components/base/BaseResponsiveDataView.vue index 251a689d..4fa41da3 100644 --- a/src/components/base/BaseResponsiveDataView.vue +++ b/src/components/base/BaseResponsiveDataView.vue @@ -27,15 +27,41 @@ aria-label="Search" /> - - - +
+ + Filter ({{ activeFilterCount }}) + +
+
+ Filter Data + +
+
+
+ {{ field.label }} + + + + + +
+
+
+ Reset + Terapkan +
+
+
@@ -58,6 +84,8 @@ :src="item.image" :alt="item.name || `Item ${rowIndex + 1}`" class="mobile-image" + loading="lazy" + decoding="async" @error="onImageError($event, item)" /> @@ -137,6 +165,8 @@ :src="item.image" :alt="item.name || 'avatar'" class="table-list-mobile-image" + loading="lazy" + decoding="async" @error="onImageError($event, item)" /> @@ -174,6 +204,8 @@ :src="item.image" :alt="item.name || 'avatar'" class="table-avatar" + loading="lazy" + decoding="async" @error="onImageError($event, item)" /> @@ -244,16 +276,14 @@ + + diff --git a/src/components/customers/CustomerLocationMap.vue b/src/components/customers/CustomerLocationMap.vue new file mode 100644 index 00000000..b005dd2e --- /dev/null +++ b/src/components/customers/CustomerLocationMap.vue @@ -0,0 +1,161 @@ + + + + + diff --git a/src/router/index.js b/src/router/index.js index 515d5a67..73598b8c 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -394,6 +394,155 @@ const routes = [ component: () => import('@/views/nas/NasResourceView.vue'), meta: { nasResource: 'webfig' }, }, + { + path: '/customers/orders', + name: 'Customer List Order', + component: () => import('@/views/customers/Customers.vue'), + meta: { customerScope: 'orders' }, + }, + { + path: '/customers/orders/create', + name: 'Registrasi Customer', + component: () => import('@/views/customers/CustomerFormPage.vue'), + meta: { activeMenu: '/customers/orders' }, + }, + { + path: '/customers/orders/:id/edit', + name: 'Edit Customer', + component: () => import('@/views/customers/CustomerFormPage.vue'), + meta: { activeMenu: '/customers/orders' }, + }, + { + path: '/customers/:scope/:id/detail', + name: 'Detail Customer', + component: () => import('@/views/customers/CustomerDetailPage.vue'), + meta: { activeMenuBase: '/customers' }, + }, + { + path: '/customers/active', + name: 'Customer Aktif', + component: () => import('@/views/customers/Customers.vue'), + meta: { customerScope: 'active' }, + }, + { + path: '/customers/inactive', + name: 'Customer Tidak Aktif', + component: () => import('@/views/customers/Customers.vue'), + meta: { customerScope: 'inactive' }, + }, + { + path: '/customers/unmanaged', + name: 'Customer Unmanage', + component: () => import('@/views/customers/Customers.vue'), + meta: { customerScope: 'unmanaged' }, + }, + { + path: '/customers/trash', + name: 'Sampah Customer', + component: () => import('@/views/customers/Customers.vue'), + meta: { customerScope: 'trash' }, + }, + { + path: '/billing/profiles', + name: 'Profile Tagihan', + component: () => import('@/views/billing/BillingProfiles.vue'), + }, + { + path: '/billing/running', + name: 'Tagihan Berjalan', + component: () => import('@/views/billing/Invoices.vue'), + meta: { invoiceScope: 'running' }, + }, + { + path: '/billing/overdue', + name: 'Tunggakan', + component: () => import('@/views/billing/Invoices.vue'), + meta: { invoiceScope: 'overdue' }, + }, + { + path: '/billing/paid', + name: 'Tagihan Lunas', + component: () => import('@/views/billing/Invoices.vue'), + meta: { invoiceScope: 'paid' }, + }, + { + path: '/topology/map', + name: 'Peta Jaringan', + component: () => import('@/views/topology/TopologyMap.vue'), + }, + { + path: '/topology/devices', + name: 'Perangkat Topologi', + component: () => import('@/views/topology/TopologyResourceView.vue'), + meta: { topologyResource: 'node' }, + }, + { + path: '/topology/links', + name: 'Jalur Kabel', + component: () => import('@/views/topology/TopologyResourceView.vue'), + meta: { topologyResource: 'link' }, + }, + { + path: '/topology/network', + name: 'Topologi Jaringan', + component: () => import('@/views/topology/TopologyNetwork.vue'), + }, + { + path: '/topology/device-types', + name: 'Kategori Perangkat', + component: () => import('@/views/topology/TopologyResourceView.vue'), + meta: { topologyResource: 'device-type' }, + }, + { + path: '/notifications-service/broadcasts', + name: 'Pesan Siaran', + component: () => import('@/views/notifications-service/NotificationBroadcasts.vue'), + }, + { + path: '/notifications-service/system', + name: 'Notifikasi Aplikasi', + component: () => import('@/views/notifications-service/NotificationChannelDashboard.vue'), + meta: { notificationChannel: 'system' }, + }, + { + path: '/notifications-service/whatsapp-official', + name: 'WhatsApp Official', + component: () => import('@/views/notifications-service/NotificationChannelDashboard.vue'), + meta: { notificationChannel: 'whatsapp_official' }, + }, + { + path: '/notifications-service/whatsapp-unofficial', + name: 'WhatsApp Unofficial', + component: () => import('@/views/notifications-service/NotificationChannelDashboard.vue'), + meta: { notificationChannel: 'whatsapp_unofficial' }, + }, + { + path: '/notifications-service/email', + name: 'Email Notifikasi', + component: () => import('@/views/notifications-service/NotificationChannelDashboard.vue'), + meta: { notificationChannel: 'email' }, + }, + { + path: '/notifications-service/telegram', + name: 'Telegram Notifikasi', + component: () => import('@/views/notifications-service/NotificationChannelDashboard.vue'), + meta: { notificationChannel: 'telegram' }, + }, + { + path: '/payment-gateway/settings', + name: 'Setting Payment Gateway', + component: () => import('@/views/payment-gateway/PaymentGatewaySettings.vue'), + }, + { + path: '/payment-gateway/logs', + name: 'Log Pembayaran', + component: () => import('@/views/payment-gateway/PaymentLogs.vue'), + }, + { + path: '/wallet', + name: 'Dompet', + component: () => import('@/views/wallet/WalletDashboard.vue'), + }, { path: '/users', name: 'Users', diff --git a/src/services/billingService.js b/src/services/billingService.js new file mode 100644 index 00000000..4f4e3d3b --- /dev/null +++ b/src/services/billingService.js @@ -0,0 +1,14 @@ +import api from '@/api/axios' + +export async function getBillingProfiles(params = {}) { return (await api.get('/billing/profiles', { params })).data } +export async function getBillingProfile(id) { return (await api.get(`/billing/profiles/${id}`)).data } +export async function createBillingProfile(payload) { return (await api.post('/billing/profiles', payload)).data } +export async function updateBillingProfile(id, payload) { return (await api.put(`/billing/profiles/${id}`, payload)).data } +export async function deleteBillingProfile(id) { return (await api.delete(`/billing/profiles/${id}`)).data } +export async function getInvoices(scope, params = {}) { return (await api.get(`/billing/invoices/${scope}`, { params })).data } +export async function getInvoice(scope, id) { return (await api.get(`/billing/invoices/${scope}/${id}`)).data } +export async function getInvoiceOptions() { return (await api.get('/billing/invoice-options')).data } +export async function createInvoice(payload) { return (await api.post('/billing/invoices', payload)).data } +export async function updateInvoice(id, payload) { return (await api.put(`/billing/invoices/${id}`, payload)).data } +export async function payInvoice(scope, id, payload) { return (await api.post(`/billing/invoices/${scope}/${id}/pay`, payload)).data } +export async function cancelInvoice(id) { return (await api.post(`/billing/invoices/${id}/cancel`)).data } diff --git a/src/services/customerService.js b/src/services/customerService.js new file mode 100644 index 00000000..fe349df3 --- /dev/null +++ b/src/services/customerService.js @@ -0,0 +1,42 @@ +import api from '@/api/axios' + +export async function getCustomers(scope, params = {}) { + const { data } = await api.get(`/customers/${scope}`, { params }) + return data +} +export async function getCustomerOptions() { + const { data } = await api.get('/customers/options') + return data +} +export async function getCustomer(scope, id) { + const { data } = await api.get(`/customers/${scope}/${id}`) + return data +} +export async function createCustomer(payload) { + const { data } = await api.post('/customers', payload) + return data +} +export async function updateCustomer(id, payload) { + const { data } = await api.put(`/customers/${id}`, payload) + return data +} +export async function activateCustomer(id, payload) { + const { data } = await api.post(`/customers/${id}/activate`, payload) + return data +} +export async function deactivateCustomer(id) { + const { data } = await api.post(`/customers/${id}/deactivate`) + return data +} +export async function trashCustomer(id) { + const { data } = await api.delete(`/customers/${id}`) + return data +} +export async function restoreCustomer(id) { + const { data } = await api.post(`/customers/${id}/restore`) + return data +} +export async function forceDeleteCustomer(id) { + const { data } = await api.delete(`/customers/${id}/force`) + return data +} diff --git a/src/services/notificationService.js b/src/services/notificationService.js new file mode 100644 index 00000000..741cc927 --- /dev/null +++ b/src/services/notificationService.js @@ -0,0 +1,75 @@ +import api from '@/api/axios' + +const channelPaths = { + system: 'system', + whatsapp_official: 'whatsapp-official', + whatsapp_unofficial: 'whatsapp-unofficial', + email: 'email', + telegram: 'telegram', +} + +const channelBase = (channel) => `/notifications/channels/${channelPaths[channel]}` + +export async function getNotificationEvents() { + return (await api.get('/notifications/events')).data +} +export async function getNotificationChannels(channel) { + return (await api.get(`${channelBase(channel)}/settings`)).data +} +export async function createNotificationChannel(channel, payload) { + return (await api.post(`${channelBase(channel)}/settings`, payload)).data +} +export async function updateNotificationChannel(channel, id, payload) { + return (await api.put(`${channelBase(channel)}/settings/${id}`, payload)).data +} +export async function testNotificationChannel(channel, id) { + return (await api.post(`${channelBase(channel)}/settings/${id}/test`)).data +} +export async function getNotificationTemplates(channel) { + return (await api.get(`${channelBase(channel)}/templates`)).data +} +export async function createNotificationTemplate(channel, payload) { + return (await api.post(`${channelBase(channel)}/templates`, payload)).data +} +export async function updateNotificationTemplate(channel, id, payload) { + return (await api.put(`${channelBase(channel)}/templates/${id}`, payload)).data +} +export async function getNotificationLogs(channel, params = {}) { + return (await api.get(`${channelBase(channel)}/logs`, { params })).data +} +export async function getNotificationPreferences(params = {}) { + return (await api.get('/notifications/preferences', { params })).data +} +export async function saveNotificationPreferences(preferences) { + return (await api.put('/notifications/preferences', { preferences })).data +} +export async function getBroadcasts(params = {}) { + return (await api.get('/notifications/broadcasts', { params })).data +} +export async function previewBroadcast(payload) { + return (await api.post('/notifications/broadcasts/preview', payload)).data +} +export async function createBroadcast(payload) { + return (await api.post('/notifications/broadcasts', payload)).data +} +export async function approveBroadcast(id) { + return (await api.post(`/notifications/broadcasts/${id}/approve`)).data +} +export async function getMyNotifications(params = {}) { + return (await api.get('/me/notifications', { params })).data +} +export async function markNotificationRead(uuid) { + return (await api.put(`/me/notifications/${uuid}/read`)).data +} +export async function getCustomerNotificationContacts(customerId) { + return (await api.get(`/customers/${customerId}/notification-contacts`)).data +} +export async function createCustomerNotificationContact(customerId, payload) { + return (await api.post(`/customers/${customerId}/notification-contacts`, payload)).data +} +export async function updateCustomerNotificationContact(customerId, id, payload) { + return (await api.put(`/customers/${customerId}/notification-contacts/${id}`, payload)).data +} +export async function deleteCustomerNotificationContact(customerId, id) { + return (await api.delete(`/customers/${customerId}/notification-contacts/${id}`)).data +} diff --git a/src/services/paymentGatewayService.js b/src/services/paymentGatewayService.js new file mode 100644 index 00000000..f0980345 --- /dev/null +++ b/src/services/paymentGatewayService.js @@ -0,0 +1,8 @@ +import api from '@/api/axios' + +export const getPaymentProviders = async () => (await api.get('/payment-gateway/providers')).data +export const getPaymentSettings = async () => (await api.get('/payment-gateway/settings')).data +export const createGatewayAccount = async (payload) => (await api.post('/payment-gateway/accounts', payload)).data +export const updateGatewayAccount = async (id, payload) => (await api.put(`/payment-gateway/accounts/${id}`, payload)).data +export const saveTenantPaymentSetting = async (payload) => (await api.put('/payment-gateway/tenant-setting', payload)).data +export const getPaymentTransactions = async (params = {}) => (await api.get('/payment-gateway/transactions', { params })).data diff --git a/src/services/topologyService.js b/src/services/topologyService.js new file mode 100644 index 00000000..a9fd45b5 --- /dev/null +++ b/src/services/topologyService.js @@ -0,0 +1,60 @@ +import api from '@/api/axios' + +const paths = { + 'device-type': 'device-types', + node: 'nodes', + link: 'links', +} + +const endpoint = (resource, id = null) => { + const base = `/topology/${paths[resource]}` + return id ? `${base}/${id}` : base +} + +export async function getTopologyResources(resource, params = {}) { + return (await api.get(endpoint(resource), { params })).data +} + +export async function getTopologyResource(resource, id) { + return (await api.get(endpoint(resource, id))).data +} + +export async function createTopologyResource(resource, payload) { + return (await api.post(endpoint(resource), payload)).data +} + +export async function updateTopologyResource(resource, id, payload) { + return (await api.put(endpoint(resource, id), payload)).data +} + +export async function deleteTopologyResource(resource, id) { + return (await api.delete(endpoint(resource, id))).data +} + +export async function getTopologyOptions() { + return (await api.get('/topology/options')).data +} + +export async function getTopologyMap(params = {}) { + return (await api.get('/topology/map', { params })).data +} + +export async function getNodePorts(nodeId) { + return (await api.get(`/topology/nodes/${nodeId}/ports`)).data +} + +export async function createNodePort(nodeId, payload) { + return (await api.post(`/topology/nodes/${nodeId}/ports`, payload)).data +} + +export async function updateNodePort(id, payload) { + return (await api.put(`/topology/ports/${id}`, payload)).data +} + +export async function deleteNodePort(id) { + return (await api.delete(`/topology/ports/${id}`)).data +} + +export async function traceTopology(nodeId, direction = 'downstream') { + return (await api.get(`/topology/nodes/${nodeId}/${direction}`)).data +} diff --git a/src/services/walletService.js b/src/services/walletService.js new file mode 100644 index 00000000..2573de06 --- /dev/null +++ b/src/services/walletService.js @@ -0,0 +1,6 @@ +import api from '@/api/axios' + +export const getMyWallets = async () => (await api.get('/wallets')).data +export const getWalletLedger = async (id, params = {}) => (await api.get(`/wallets/${id}/ledger`, { params })).data +export const transferWallet = async (id, payload) => (await api.post(`/wallets/${id}/transfer`, payload)).data +export const withdrawWallet = async (id, payload) => (await api.post(`/wallets/${id}/withdraw`, payload)).data diff --git a/src/utils/imageCompression.js b/src/utils/imageCompression.js new file mode 100644 index 00000000..a2d259f4 --- /dev/null +++ b/src/utils/imageCompression.js @@ -0,0 +1,48 @@ +const MAX_CUSTOMER_IMAGE_BYTES = 500 * 1024 + +function canvasBlob(canvas, type, quality) { + return new Promise((resolve) => canvas.toBlob(resolve, type, quality)) +} + +export async function compressCustomerImage(file) { + if (!file.type.startsWith('image/')) throw new Error('File harus berupa gambar.') + + const bitmap = await createImageBitmap(file) + const maxDimension = 1920 + let scale = Math.min(1, maxDimension / Math.max(bitmap.width, bitmap.height)) + let width = Math.max(1, Math.round(bitmap.width * scale)) + let height = Math.max(1, Math.round(bitmap.height * scale)) + const canvas = document.createElement('canvas') + const context = canvas.getContext('2d', { alpha: false }) + let blob = null + let quality = 0.86 + + for (let attempt = 0; attempt < 10; attempt += 1) { + canvas.width = width + canvas.height = height + context.fillStyle = '#ffffff' + context.fillRect(0, 0, width, height) + context.drawImage(bitmap, 0, 0, width, height) + blob = await canvasBlob(canvas, 'image/webp', quality) + + if (blob && blob.size <= MAX_CUSTOMER_IMAGE_BYTES) break + if (quality > 0.58) quality -= 0.08 + else { + width = Math.max(640, Math.round(width * 0.82)) + height = Math.max(360, Math.round(height * 0.82)) + } + } + + bitmap.close() + if (!blob || blob.size > MAX_CUSTOMER_IMAGE_BYTES) { + throw new Error('Gambar tidak dapat dikompresi hingga di bawah 500 KB.') + } + + const baseName = file.name.replace(/\.[^.]+$/, '') + return new File([blob], `${baseName}.webp`, { + type: 'image/webp', + lastModified: Date.now(), + }) +} + +export { MAX_CUSTOMER_IMAGE_BYTES } diff --git a/src/utils/responsiveViewMode.js b/src/utils/responsiveViewMode.js new file mode 100644 index 00000000..1056abcb --- /dev/null +++ b/src/utils/responsiveViewMode.js @@ -0,0 +1,18 @@ +export const RESPONSIVE_VIEW_MODE_STORAGE_KEY = 'manja-responsive-data-view-mode' +export const RESPONSIVE_VIEW_MODE_EVENT = 'manja-responsive-data-view-mode-change' + +export const getResponsiveViewMode = () => { + const storedMode = localStorage.getItem(RESPONSIVE_VIEW_MODE_STORAGE_KEY) + return storedMode === 'card' || storedMode === 'table' ? storedMode : 'table' +} + +export const saveResponsiveViewMode = (mode) => { + if (mode !== 'card' && mode !== 'table') return + + localStorage.setItem(RESPONSIVE_VIEW_MODE_STORAGE_KEY, mode) + window.dispatchEvent( + new CustomEvent(RESPONSIVE_VIEW_MODE_EVENT, { + detail: mode, + }), + ) +} diff --git a/src/views/billing/BillingProfiles.vue b/src/views/billing/BillingProfiles.vue new file mode 100644 index 00000000..9366f0ee --- /dev/null +++ b/src/views/billing/BillingProfiles.vue @@ -0,0 +1,109 @@ + + + diff --git a/src/views/billing/Invoices.vue b/src/views/billing/Invoices.vue new file mode 100644 index 00000000..ca248347 --- /dev/null +++ b/src/views/billing/Invoices.vue @@ -0,0 +1,176 @@ + + + diff --git a/src/views/customers/CustomerDetailPage.vue b/src/views/customers/CustomerDetailPage.vue new file mode 100644 index 00000000..c5285557 --- /dev/null +++ b/src/views/customers/CustomerDetailPage.vue @@ -0,0 +1,205 @@ + + + + + diff --git a/src/views/customers/CustomerFormPage.vue b/src/views/customers/CustomerFormPage.vue new file mode 100644 index 00000000..2a86ea21 --- /dev/null +++ b/src/views/customers/CustomerFormPage.vue @@ -0,0 +1,290 @@ + + + + + diff --git a/src/views/customers/Customers.vue b/src/views/customers/Customers.vue new file mode 100644 index 00000000..e3a36b18 --- /dev/null +++ b/src/views/customers/Customers.vue @@ -0,0 +1,217 @@ + + + diff --git a/src/views/notifications-service/NotificationBroadcasts.vue b/src/views/notifications-service/NotificationBroadcasts.vue new file mode 100644 index 00000000..91584e9b --- /dev/null +++ b/src/views/notifications-service/NotificationBroadcasts.vue @@ -0,0 +1,155 @@ + + + + + diff --git a/src/views/notifications-service/NotificationChannelDashboard.vue b/src/views/notifications-service/NotificationChannelDashboard.vue new file mode 100644 index 00000000..12faf2f1 --- /dev/null +++ b/src/views/notifications-service/NotificationChannelDashboard.vue @@ -0,0 +1,244 @@ + + + + + diff --git a/src/views/payment-gateway/PaymentGatewaySettings.vue b/src/views/payment-gateway/PaymentGatewaySettings.vue new file mode 100644 index 00000000..a7f419d7 --- /dev/null +++ b/src/views/payment-gateway/PaymentGatewaySettings.vue @@ -0,0 +1,75 @@ + + + + + diff --git a/src/views/payment-gateway/PaymentLogs.vue b/src/views/payment-gateway/PaymentLogs.vue new file mode 100644 index 00000000..2107c7e7 --- /dev/null +++ b/src/views/payment-gateway/PaymentLogs.vue @@ -0,0 +1,33 @@ + + diff --git a/src/views/topology/TopologyMap.vue b/src/views/topology/TopologyMap.vue new file mode 100644 index 00000000..d0efb9a2 --- /dev/null +++ b/src/views/topology/TopologyMap.vue @@ -0,0 +1,157 @@ + + + + + diff --git a/src/views/topology/TopologyNetwork.vue b/src/views/topology/TopologyNetwork.vue new file mode 100644 index 00000000..4fd210ab --- /dev/null +++ b/src/views/topology/TopologyNetwork.vue @@ -0,0 +1,66 @@ + + + + + diff --git a/src/views/topology/TopologyResourceView.vue b/src/views/topology/TopologyResourceView.vue new file mode 100644 index 00000000..1d96cca5 --- /dev/null +++ b/src/views/topology/TopologyResourceView.vue @@ -0,0 +1,287 @@ + + + + + diff --git a/src/views/wallet/WalletDashboard.vue b/src/views/wallet/WalletDashboard.vue new file mode 100644 index 00000000..e3bb4979 --- /dev/null +++ b/src/views/wallet/WalletDashboard.vue @@ -0,0 +1,171 @@ + + +