diff --git a/.env b/.env
new file mode 100644
index 0000000..e69de29
diff --git a/.env.development b/.env.development
new file mode 100644
index 0000000..00d140d
--- /dev/null
+++ b/.env.development
@@ -0,0 +1,4 @@
+NEXT_PUBLIC_API_URL='https://sam-kalteng-api.basys.co.id'
+NEXT_PUBLIC_API_URL_NEWS='https://cors-anywhere.herokuapp.com/https://newsapi.org/v2'
+NEXT_PUBLIC_API_URL_NEWS_MEDIASTACK='https://api.mediastack.com/v1'
+NEXT_PUBLIC_API_URL_NEWS_NEWDATA='https://newsdata.io/api/1'
diff --git a/.env.production b/.env.production
new file mode 100644
index 0000000..00d140d
--- /dev/null
+++ b/.env.production
@@ -0,0 +1,4 @@
+NEXT_PUBLIC_API_URL='https://sam-kalteng-api.basys.co.id'
+NEXT_PUBLIC_API_URL_NEWS='https://cors-anywhere.herokuapp.com/https://newsapi.org/v2'
+NEXT_PUBLIC_API_URL_NEWS_MEDIASTACK='https://api.mediastack.com/v1'
+NEXT_PUBLIC_API_URL_NEWS_NEWDATA='https://newsdata.io/api/1'
diff --git a/.eslintrc.json b/.eslintrc.json
index bffb357..b31f14d 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -1,3 +1,7 @@
{
- "extends": "next/core-web-vitals"
-}
+ "extends": "next",
+ "rules": {
+ "react/no-unescaped-entities": "off",
+ "@next/next/no-page-custom-font": "off"
+ }
+}
\ No newline at end of file
diff --git a/.idea/watcherTasks.xml b/.idea/watcherTasks.xml
new file mode 100644
index 0000000..fb0d65a
--- /dev/null
+++ b/.idea/watcherTasks.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/next.config.mjs b/next.config.mjs
index e6813eb..1f3f3ec 100644
--- a/next.config.mjs
+++ b/next.config.mjs
@@ -1,5 +1,6 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
+ reactStrictMode: false,
output: 'export',
images: {
loader: "custom",
diff --git a/package-lock.json b/package-lock.json
index fb92f8d..95a1259 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -15,7 +15,9 @@
"next-image-export-optimizer": "^1.12.3",
"nextjs-toploader": "^1.6.6",
"react": "^18",
- "react-dom": "^18"
+ "react-dom": "^18",
+ "react-hook-form": "^7.52.2",
+ "sass": "^1.77.8"
},
"devDependencies": {
"eslint": "^8",
@@ -1348,6 +1350,18 @@
"react-dom": ">=16.9.0"
}
},
+ "node_modules/anymatch": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
+ "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
+ "dependencies": {
+ "normalize-path": "^3.0.0",
+ "picomatch": "^2.0.4"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
"node_modules/argparse": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
@@ -1572,6 +1586,17 @@
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
"dev": true
},
+ "node_modules/binary-extensions": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
+ "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@@ -1586,7 +1611,6 @@
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
- "dev": true,
"dependencies": {
"fill-range": "^7.1.1"
},
@@ -1668,6 +1692,40 @@
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
+ "node_modules/chokidar": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
+ "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
+ "dependencies": {
+ "anymatch": "~3.1.2",
+ "braces": "~3.0.2",
+ "glob-parent": "~5.1.2",
+ "is-binary-path": "~2.1.0",
+ "is-glob": "~4.0.1",
+ "normalize-path": "~3.0.0",
+ "readdirp": "~3.6.0"
+ },
+ "engines": {
+ "node": ">= 8.10.0"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/chokidar/node_modules/glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "dependencies": {
+ "is-glob": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
"node_modules/classnames": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz",
@@ -2663,7 +2721,6 @@
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
- "dev": true,
"dependencies": {
"to-regex-range": "^5.0.1"
},
@@ -2761,6 +2818,19 @@
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
"dev": true
},
+ "node_modules/fsevents": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+ "hasInstallScript": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
"node_modules/function-bind": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
@@ -3067,6 +3137,11 @@
"node": ">= 4"
}
},
+ "node_modules/immutable": {
+ "version": "4.3.7",
+ "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.7.tgz",
+ "integrity": "sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw=="
+ },
"node_modules/import-fresh": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
@@ -3187,6 +3262,17 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/is-binary-path": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+ "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+ "dependencies": {
+ "binary-extensions": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/is-boolean-object": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz",
@@ -3264,7 +3350,6 @@
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
- "dev": true,
"engines": {
"node": ">=0.10.0"
}
@@ -3308,7 +3393,6 @@
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
- "dev": true,
"dependencies": {
"is-extglob": "^2.1.1"
},
@@ -3344,7 +3428,6 @@
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
- "dev": true,
"engines": {
"node": ">=0.12.0"
}
@@ -3859,6 +3942,14 @@
"react-dom": ">= 16.0.0"
}
},
+ "node_modules/normalize-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/nprogress": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/nprogress/-/nprogress-0.2.0.tgz",
@@ -4125,7 +4216,6 @@
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
- "dev": true,
"engines": {
"node": ">=8.6"
},
@@ -4817,11 +4907,37 @@
"react": "^18.3.1"
}
},
+ "node_modules/react-hook-form": {
+ "version": "7.52.2",
+ "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.52.2.tgz",
+ "integrity": "sha512-pqfPEbERnxxiNMPd0bzmt1tuaPcVccywFDpyk2uV5xCIBphHV5T8SVnX9/o3kplPE1zzKt77+YIoq+EMwJp56A==",
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/react-hook-form"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17 || ^18 || ^19"
+ }
+ },
"node_modules/react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
},
+ "node_modules/readdirp": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
+ "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+ "dependencies": {
+ "picomatch": "^2.2.1"
+ },
+ "engines": {
+ "node": ">=8.10.0"
+ }
+ },
"node_modules/reflect.getprototypeof": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.6.tgz",
@@ -5011,6 +5127,22 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/sass": {
+ "version": "1.77.8",
+ "resolved": "https://registry.npmjs.org/sass/-/sass-1.77.8.tgz",
+ "integrity": "sha512-4UHg6prsrycW20fqLGPShtEvo/WyHRVRHwOP4DzkUrObWoWI05QBSfzU71TVB7PFaL104TwNaHpjlWXAZbQiNQ==",
+ "dependencies": {
+ "chokidar": ">=3.0.0 <4.0.0",
+ "immutable": "^4.0.0",
+ "source-map-js": ">=0.6.2 <2.0.0"
+ },
+ "bin": {
+ "sass": "sass.js"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
"node_modules/scheduler": {
"version": "0.23.2",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz",
@@ -5493,7 +5625,6 @@
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
- "dev": true,
"dependencies": {
"is-number": "^7.0.0"
},
diff --git a/package.json b/package.json
index 401d196..531fd5c 100644
--- a/package.json
+++ b/package.json
@@ -5,6 +5,7 @@
"scripts": {
"dev": "next dev",
"build": "next build && next-image-export-optimizer",
+ "build:dev": "NODE_ENV=development next build",
"start": "next start",
"lint": "next lint",
"dev-export": "next dev -H 0.0.0.0 -p 3000",
@@ -13,12 +14,14 @@
"dependencies": {
"@ant-design/icons": "^5.4.0",
"antd": "^5.20.0",
+ "framer-motion": "^10.16.4",
"next": "14.2.5",
- "react": "^18",
- "react-dom": "^18",
"next-image-export-optimizer": "^1.12.3",
"nextjs-toploader": "^1.6.6",
- "framer-motion": "^10.16.4"
+ "react": "^18",
+ "react-dom": "^18",
+ "react-hook-form": "^7.52.2",
+ "sass": "^1.77.8"
},
"devDependencies": {
"eslint": "^8",
diff --git a/public/css/form.css b/public/css/form.css
new file mode 100644
index 0000000..3cce9fb
--- /dev/null
+++ b/public/css/form.css
@@ -0,0 +1,307 @@
+.form-control {
+ display: block;
+ width: 100%;
+ padding: 10px 17px;
+ height: 40px;
+ font-size: 13px;
+ font-weight: 600;
+ border-radius: 20px;
+ color: #181c32;
+ background-color: #ffffff;
+ border: 1px solid #e1e3ea;
+ transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
+}
+
+.ant-select-selection-item {
+ font-weight: 600 !important;
+ font-size: 13px !important;
+}
+
+.form-group {
+ margin-bottom: 25px;
+}
+
+.ant-select-disabled {
+ background: #fff;
+ border: 0;
+
+ .ant-select-selector {
+ background: #fff !important;
+ color: #181c32 !important;
+ }
+
+ .ant-select-arrow {
+ display: none;
+ }
+
+}
+
+.ant-picker-disabled {
+ background-color: #fff !important;
+ border: 0;
+
+ input {
+ color: #181c32 !important;
+ font-weight: 600;
+ }
+}
+
+
+@media (prefers-reduced-motion: reduce) {
+ .form-control {
+ transition: none;
+ }
+}
+
+.form-control[type="file"] {
+ overflow: hidden;
+}
+
+.form-control[type="file"]:not(:disabled):not([readonly]) {
+ cursor: pointer;
+}
+
+.form-control:focus {
+ color: #181c32;
+ background-color: #ffffff;
+ border-color: var(#b5b5c3);
+ outline: 0;
+ /* box-shadow: 0px 2px 10px rgba(0, 0, 0, 0.08); */
+}
+
+.form-control::-webkit-date-and-time-value {
+ height: 1.5em;
+}
+
+.form-control::placeholder {
+ font-size: 14px;
+ color: #a1a5b7;
+ font-weight: 400;
+ opacity: 1;
+}
+
+.form-control:disabled,
+.form-control[readonly] {
+ background-color: #fff;
+ border: none;
+ opacity: 1;
+}
+
+/* ======== FLOATING LABEL ======== */
+
+.floating-label-content {
+ position: relative;
+ width: 100%;
+ margin-bottom: 20px;
+}
+
+.floating-label-content textarea {
+ min-height: 120px;
+ padding: 10px 20px;
+}
+
+.floating-label {
+ position: absolute;
+ left: 15px;
+ top: 8px;
+ font-size: 14px;
+ font-weight: 500;
+ padding: 0 7px;
+ background: #fff;
+ color: #757575;
+ cursor: text;
+ transition: 0.2s ease all;
+ border-radius: 20px;
+}
+
+.floating-input:focus,
+.floating-select:focus {
+ outline: none;
+}
+
+.floating-input:focus~.floating-label {
+ top: -10px;
+ left: 10px;
+ font-size: 12px;
+}
+
+.floating-input:not(:-moz-placeholder-shown)~.floating-label {
+ top: -10px;
+ left: 10px;
+ font-size: 12px;
+}
+
+.floating-input:not(:-ms-input-placeholder)~.floating-label {
+ top: -10px;
+ left: 10px;
+ font-size: 12px;
+}
+
+.floating-input:not(:placeholder-shown)~.floating-label {
+ top: -10px;
+ left: 10px;
+ font-size: 12px;
+}
+
+.floating-select:not([value=""]):valid~.floating-label {
+ top: -10px;
+ left: 10px;
+ font-size: 12px;
+}
+
+textarea {
+ min-height: 120px;
+ padding: 14px 20px;
+}
+
+/* ======== END FLOATING LABEL ======== */
+
+.label-form {
+ font-size: 14px;
+ font-weight: 400;
+ color: #1f2233;
+ margin-bottom: 4px;
+}
+
+.icon-password {
+ cursor: pointer;
+ position: absolute;
+ right: 15px;
+ color: #c1c1c1;
+ top: 0px;
+ font-size: 25px;
+ border-radius: 27px;
+}
+
+/* INPUT IMAGE */
+.image-input {
+ position: relative;
+}
+
+.image-input .content-btn {
+ display: flex;
+ position: absolute;
+ left: -11px;
+ top: -10px;
+ gap: 3px;
+ flex-direction: column;
+ z-index: 4;
+}
+
+.image-input input {
+ display: none;
+}
+
+.image-input label {
+ display: block;
+ color: #FFF;
+ background: #000;
+ padding: 0.3rem 0.6rem;
+ font-size: 115%;
+ cursor: pointer;
+}
+
+.image-input label i {
+ font-size: 125%;
+ margin-right: 0.3rem;
+}
+
+.image-input label:hover i {
+ animation: shake 0.35s;
+}
+
+// .ant-upload-wrapper .ant-upload {
+// background-size: contain !important;
+// width: 100% !important;
+// height: 200px !important;
+// border-radius: 20px;
+// border: 1px dashed #f1f1f1;
+// }
+
+.image-input span {
+ display: none;
+ text-align: center;
+ cursor: pointer;
+}
+
+.image-preview{
+ width: 100%;
+ height: 315px;
+ background-size: contain;
+}
+
+// .ant-image-mask-info {
+// border-radius: 20px;
+// }
+
+@keyframes shake {
+ 0% {
+ transform: rotate(0deg);
+ }
+
+ 25% {
+ transform: rotate(10deg);
+ }
+
+ 50% {
+ transform: rotate(0deg);
+ }
+
+ 75% {
+ transform: rotate(-10deg);
+ }
+
+ 100% {
+ transform: rotate(0deg);
+ }
+}
+
+/* INPUT IMAGE */
+
+
+.searchInput {
+ border-radius: 20px;
+ padding-left: 2.6rem;
+ background-color: #fff !important;
+ transition: all 0.2s;
+ width: 100px;
+ border: 1px solid #0049af24;
+ height: 37px;
+}
+
+.searchInput:focus {
+ background-color: #fff !important;
+ width: 300px !important;
+ transition: all 0.2s;
+}
+
+
+.icon-search {
+ position: absolute;
+ top: 11px;
+ left: 13px;
+ font-size: 18px;
+}
+
+.error-form {
+ font-size: 11px;
+ color: var(--danger);
+ margin-top: 5px;
+ margin-left: 22px;
+}
+
+.error .form-control {
+ border: 1px solid var(--danger);
+}
+
+.error .floating-label {
+ color: var(--danger);
+}
+
+.ant-select-selector {
+ padding: 0 !important;
+}
+
+.ant-select-selection-search {
+ left: 0 !important;
+}
\ No newline at end of file
diff --git a/src/app/globals.css b/public/css/globals.scss
similarity index 71%
rename from src/app/globals.css
rename to public/css/globals.scss
index eccbf78..ec36c54 100644
--- a/src/app/globals.css
+++ b/public/css/globals.scss
@@ -38,6 +38,8 @@
}
+@import "form.css";
+
::-webkit-scrollbar {
width: 5px;
height: 14px;
@@ -111,6 +113,12 @@ button, input {
padding: 10px;
}
+.btn-dark{
+ background: #ffffff;
+ color: #59b8b6;
+ border: 1px solid #59b8b6;
+}
+
.title-wrap {
position: relative;
display: flex;
@@ -143,7 +151,7 @@ button, input {
.no-data{
text-align: center;
padding: 30px 20px;
- background: #f5f5f5;
+ background: #f5f5f58f;
width: 100%;
height: 100%;
border-radius: 20px;
@@ -173,7 +181,7 @@ button, input {
/*color: #fff;*/
/*background: var(--primary-gradient);*/
color: var(--primary);
- background: #f5f5f5;
+ background: #f5f5f58f;
padding: 10px 15px;
margin-bottom: -12px;
border-radius: 20px 20px 0 0;
@@ -201,7 +209,7 @@ button, input {
.ant-tabs-content-holder {
/*background: var(--primary-gradient);*/
- background: #f5f5f5;
+ background: #f5f5f58f;
padding: 20px;
border-radius: 0 20px 20px 20px;
}
@@ -285,23 +293,26 @@ button, input {
.search {
position: absolute;
- left: 10px;
+ left: 15px;
top:10px;
z-index: 200;
- width: 70%;
+ width: 61%;
.form {
border-radius: 30px;
border: 1px solid #fff;
}
- .ant-input-affix-wrapper > input.ant-input,
+ .ant-input-affix-wrapper >
+ input.ant-input,
.ant-input-affix-wrapper > textarea.ant-input {
- font-weight: 600 !important;
+ font-weight: 400 !important;
color: #353535 !important;
+ font-size: 12px;
}
- .anticon svg {
+ .anticon
+ svg {
fill: var(--primary);
margin-right: 10px;
}
@@ -312,14 +323,13 @@ button, input {
.header {
background: linear-gradient(to bottom, #49B1B5, #149A9F);
- height: 200px;
+ height: 160px;
width: 100%;
color: #fff;
position: relative;
display: flex;
justify-content: center; /* Horizontally center */
align-items: center; /* Vertically center */
-
/*border-radius: 0 0 50px 50px;*/
&.hidden {
@@ -337,10 +347,10 @@ button, input {
width: 100% !important;
left: 0 !important;
bottom: 0px !important;
- top: 200px !important;
+ top: 145px !important;
border: none !important;
z-index: 1;
- height: unset !important;
+ height: 90px !important;
}
@@ -353,16 +363,36 @@ button, input {
}
.logo {
- margin-top: 80px;
-
+ text-align: center;
img {
- width: 300px;
+ width: 30px !important;
+ margin-bottom: -30px;
+ margin-right: -99px;
}
.title {
font-weight: 400;
text-transform: capitalize;
}
+ ;
+ position: absolute;
+ left: 0;
+ right: 0;
+ top: 66px;
+ /* background: #00000033; */
+ /* padding: 8px 0 0 15px; */
+ /* border-radius: 20px; */
+
+ .title{
+ font-size: 16px;
+ font-weight: 600;
+ text-transform: uppercase;
+ }
+ .sub-title{
+ font-size: 10px;
+ font-weight: 300;
+ margin-top: -3px;
+ }
}
.top-btn {
@@ -385,10 +415,10 @@ button, input {
}
.location {
- margin-top: 10px;
position: absolute;
- top: 5px;
- left: 20px;
+ z-index: 111;
+
+
.title {
font-size: 12px;
@@ -396,49 +426,76 @@ button, input {
}
.value {
- font-size: 12px;
+ font-size: 11px;
font-weight: 400;
}
+ ;
+ left: 15px;
+ bottom: 35px;
+ top: 12px;
+ height: 26px;
+ background: #ffffff26;
+ padding: 4px 20px;
+ border-radius: 28px;
+ }
+
+ .ant-input-affix-wrapper >
+ input.ant-input:hover + .search{
+ background: red;
}
.search {
- margin-top: 7px;
- padding: 0 40px;
+ padding: 0px 79px;
+ position: absolute;
z-index: 200;
+
+
.form {
- border-radius: 15px;
+ border-radius: 40px;
+ padding: 10px 25px;
+ border: none;
+ box-shadow: 0 6px 15px #00000026;
+ text-align: center;
}
- .ant-input-affix-wrapper > input.ant-input,
+ .ant-input-affix-wrapper >
+ input.ant-input,
.ant-input-affix-wrapper > textarea.ant-input {
- font-weight: 600 !important;
+ font-weight: 500 !important;
color: #353535 !important;
+ font-size: 12px;
+
+
+
}
- .anticon svg {
+ .anticon
+ svg {
fill: var(--primary);
margin-right: 10px;
}
+ ;
+ bottom: -23px;
+ width: 100%;
+ left: 0;
+ right: 0;
}
-
}
.omnichannel {
- margin-top: 120px;
+ margin-top: 100px;
padding: 0 25px;
-
-
}
.card-omni {
- /*background: #fff;*/
+ /* background: #fff; */
background: linear-gradient(to bottom, #fff, #f3feff);
- border-radius: 20px;
+ border-radius: 15px;
text-align: center;
- border: 1px solid #eeeeee;
- padding: 10px 10px 20px;
- /*box-shadow: 9px 10px 18px #d7d7d7;*/
+ /*border: 1px solid #eeeeee;*/
+ padding: 10px 10px 15px;
+ box-shadow: 9px 10px 18px #d7d7d75e;
.icon {
img {
@@ -455,7 +512,9 @@ button, input {
height: 30px;
overflow: hidden;
}
-
+;
+ display: block;
+ text-decoration: none;
}
.news {
@@ -541,9 +600,11 @@ button, input {
margin-bottom: 10px;
display: flex;
gap: 15px;
- background: #f5f5f5;
+ background: #f5f5f58f;
padding: 10px 10px 5px;
border-radius: 15px;
+ text-decoration: none;
+ color: inherit;
.image {
border-radius: 10px;
@@ -591,4 +652,139 @@ button, input {
color: #fff;
}
-}
\ No newline at end of file
+}
+
+.detail-berita{
+ width: 100%;
+ height: 100vh;
+ padding-top: 48px;
+
+ .content-detail{
+ padding: 30px;
+ }
+
+ .img img{
+ width: 100%;
+ height: 200px;
+ object-fit: cover;
+ }
+
+ .title{
+ font-size: 13px;
+ font-weight: 600;
+ text-align: justify;
+ }
+ .description{
+ font-size: 12px;
+ font-weight: 400;
+ margin-top: 20px;
+ margin-bottom: 40px;
+ text-align: justify;
+ }
+
+
+}
+
+.detail-va{
+ table {
+ font-size: 12px;
+ width: 100%;
+
+ .title{
+ font-weight: 400;
+ width: 40% !important;
+ }
+
+ .value{
+ font-weight: 600;
+ }
+ }
+
+ .ant-checkbox-wrapper-disabled {
+ cursor: not-allowed;
+ display: none;
+ }
+
+ .ant-table-wrapper .ant-table-thead >tr>th,
+ .ant-table-wrapper .ant-table-thead >tr>td {
+ //color: #fff;
+ //background: #4fb4b5;
+ color: #51b5b5;
+ background: #51b5b536;
+ }
+
+ .ant-table-wrapper .ant-table-tbody .ant-table-row.ant-table-row-selected >.ant-table-cell {
+ background: #daefef57;
+ }
+
+ .notes{
+ margin: 50px 0 10px;
+ font-size: 10px;
+ font-weight: 400;
+ color: var(--text-muted);
+ text-align: justify;
+ }
+}
+
+hr.border{
+ border: 1px dashed #e5e5e5;
+ margin: 20px 0;
+}
+
+.ant-checkbox-checked .ant-checkbox-inner {
+ background-color: #58b8b6 !important;
+ border-color: #58b8b6 !important;
+}
+
+.zn-table {
+ width: 100%;
+ border-collapse: collapse;
+ font-size: 12px;
+ background: #fff;
+
+ tr th{
+ padding: 10px 30px;
+ color: #51b5b5;
+ background: #51b5b536;
+ text-align: left;
+ }
+
+ tr td {
+ text-align: left;
+ padding: 10px 30px;
+ border-top: 1px solid #ededed;
+ font-weight: 500;
+ }
+
+ tfoot tr td{
+ background: #e2ffe3;
+ font-weight: 600;
+ }
+}
+
+.modal-payment{
+
+ .title{
+ font-size: 14px;
+ font-weight: 600;
+ color: var(--primary);
+ text-transform: uppercase;
+ }
+ .sub-title {
+ font-size: 12px;
+ font-weight: 400;
+ color: var(--text-muted);
+ }
+}
+
+.ant-modal .ant-modal-content {
+ border-radius: 15px;
+}
+
+
+
+
+
+
+
+
diff --git a/public/image/layer3.png b/public/image/layer3.png
new file mode 100644
index 0000000..3eeaea1
Binary files /dev/null and b/public/image/layer3.png differ
diff --git a/public/image/logodummy.png b/public/image/logodummy.png
new file mode 100644
index 0000000..7e4fd09
Binary files /dev/null and b/public/image/logodummy.png differ
diff --git a/public/image/nextImageExportOptimizer/layer3-opt-10.WEBP b/public/image/nextImageExportOptimizer/layer3-opt-10.WEBP
new file mode 100644
index 0000000..23f20a4
Binary files /dev/null and b/public/image/nextImageExportOptimizer/layer3-opt-10.WEBP differ
diff --git a/public/image/nextImageExportOptimizer/layer3-opt-1080.WEBP b/public/image/nextImageExportOptimizer/layer3-opt-1080.WEBP
new file mode 100644
index 0000000..bfab8f0
Binary files /dev/null and b/public/image/nextImageExportOptimizer/layer3-opt-1080.WEBP differ
diff --git a/public/image/nextImageExportOptimizer/layer3-opt-1200.WEBP b/public/image/nextImageExportOptimizer/layer3-opt-1200.WEBP
new file mode 100644
index 0000000..fa6f4fa
Binary files /dev/null and b/public/image/nextImageExportOptimizer/layer3-opt-1200.WEBP differ
diff --git a/public/image/nextImageExportOptimizer/layer3-opt-128.WEBP b/public/image/nextImageExportOptimizer/layer3-opt-128.WEBP
new file mode 100644
index 0000000..bc12f56
Binary files /dev/null and b/public/image/nextImageExportOptimizer/layer3-opt-128.WEBP differ
diff --git a/public/image/nextImageExportOptimizer/layer3-opt-16.WEBP b/public/image/nextImageExportOptimizer/layer3-opt-16.WEBP
new file mode 100644
index 0000000..1786abb
Binary files /dev/null and b/public/image/nextImageExportOptimizer/layer3-opt-16.WEBP differ
diff --git a/public/image/nextImageExportOptimizer/layer3-opt-1920.WEBP b/public/image/nextImageExportOptimizer/layer3-opt-1920.WEBP
new file mode 100644
index 0000000..e3aefb9
Binary files /dev/null and b/public/image/nextImageExportOptimizer/layer3-opt-1920.WEBP differ
diff --git a/public/image/nextImageExportOptimizer/layer3-opt-2048.WEBP b/public/image/nextImageExportOptimizer/layer3-opt-2048.WEBP
new file mode 100644
index 0000000..335c5e5
Binary files /dev/null and b/public/image/nextImageExportOptimizer/layer3-opt-2048.WEBP differ
diff --git a/public/image/nextImageExportOptimizer/layer3-opt-256.WEBP b/public/image/nextImageExportOptimizer/layer3-opt-256.WEBP
new file mode 100644
index 0000000..a4bab75
Binary files /dev/null and b/public/image/nextImageExportOptimizer/layer3-opt-256.WEBP differ
diff --git a/public/image/nextImageExportOptimizer/layer3-opt-32.WEBP b/public/image/nextImageExportOptimizer/layer3-opt-32.WEBP
new file mode 100644
index 0000000..3a50aa3
Binary files /dev/null and b/public/image/nextImageExportOptimizer/layer3-opt-32.WEBP differ
diff --git a/public/image/nextImageExportOptimizer/layer3-opt-384.WEBP b/public/image/nextImageExportOptimizer/layer3-opt-384.WEBP
new file mode 100644
index 0000000..42cc5fa
Binary files /dev/null and b/public/image/nextImageExportOptimizer/layer3-opt-384.WEBP differ
diff --git a/public/image/nextImageExportOptimizer/layer3-opt-3840.WEBP b/public/image/nextImageExportOptimizer/layer3-opt-3840.WEBP
new file mode 100644
index 0000000..fbc3b0e
Binary files /dev/null and b/public/image/nextImageExportOptimizer/layer3-opt-3840.WEBP differ
diff --git a/public/image/nextImageExportOptimizer/layer3-opt-48.WEBP b/public/image/nextImageExportOptimizer/layer3-opt-48.WEBP
new file mode 100644
index 0000000..66ac620
Binary files /dev/null and b/public/image/nextImageExportOptimizer/layer3-opt-48.WEBP differ
diff --git a/public/image/nextImageExportOptimizer/layer3-opt-64.WEBP b/public/image/nextImageExportOptimizer/layer3-opt-64.WEBP
new file mode 100644
index 0000000..93fce4e
Binary files /dev/null and b/public/image/nextImageExportOptimizer/layer3-opt-64.WEBP differ
diff --git a/public/image/nextImageExportOptimizer/layer3-opt-640.WEBP b/public/image/nextImageExportOptimizer/layer3-opt-640.WEBP
new file mode 100644
index 0000000..035ede8
Binary files /dev/null and b/public/image/nextImageExportOptimizer/layer3-opt-640.WEBP differ
diff --git a/public/image/nextImageExportOptimizer/layer3-opt-750.WEBP b/public/image/nextImageExportOptimizer/layer3-opt-750.WEBP
new file mode 100644
index 0000000..a2fa31c
Binary files /dev/null and b/public/image/nextImageExportOptimizer/layer3-opt-750.WEBP differ
diff --git a/public/image/nextImageExportOptimizer/layer3-opt-828.WEBP b/public/image/nextImageExportOptimizer/layer3-opt-828.WEBP
new file mode 100644
index 0000000..5033bf9
Binary files /dev/null and b/public/image/nextImageExportOptimizer/layer3-opt-828.WEBP differ
diff --git a/public/image/nextImageExportOptimizer/layer3-opt-96.WEBP b/public/image/nextImageExportOptimizer/layer3-opt-96.WEBP
new file mode 100644
index 0000000..192f54b
Binary files /dev/null and b/public/image/nextImageExportOptimizer/layer3-opt-96.WEBP differ
diff --git a/public/image/nextImageExportOptimizer/logodummy-opt-10.WEBP b/public/image/nextImageExportOptimizer/logodummy-opt-10.WEBP
new file mode 100644
index 0000000..3ac78d1
Binary files /dev/null and b/public/image/nextImageExportOptimizer/logodummy-opt-10.WEBP differ
diff --git a/public/image/nextImageExportOptimizer/logodummy-opt-1080.WEBP b/public/image/nextImageExportOptimizer/logodummy-opt-1080.WEBP
new file mode 100644
index 0000000..f36dd95
Binary files /dev/null and b/public/image/nextImageExportOptimizer/logodummy-opt-1080.WEBP differ
diff --git a/public/image/nextImageExportOptimizer/logodummy-opt-1200.WEBP b/public/image/nextImageExportOptimizer/logodummy-opt-1200.WEBP
new file mode 100644
index 0000000..f36dd95
Binary files /dev/null and b/public/image/nextImageExportOptimizer/logodummy-opt-1200.WEBP differ
diff --git a/public/image/nextImageExportOptimizer/logodummy-opt-128.WEBP b/public/image/nextImageExportOptimizer/logodummy-opt-128.WEBP
new file mode 100644
index 0000000..f36dd95
Binary files /dev/null and b/public/image/nextImageExportOptimizer/logodummy-opt-128.WEBP differ
diff --git a/public/image/nextImageExportOptimizer/logodummy-opt-16.WEBP b/public/image/nextImageExportOptimizer/logodummy-opt-16.WEBP
new file mode 100644
index 0000000..23464c9
Binary files /dev/null and b/public/image/nextImageExportOptimizer/logodummy-opt-16.WEBP differ
diff --git a/public/image/nextImageExportOptimizer/logodummy-opt-1920.WEBP b/public/image/nextImageExportOptimizer/logodummy-opt-1920.WEBP
new file mode 100644
index 0000000..f36dd95
Binary files /dev/null and b/public/image/nextImageExportOptimizer/logodummy-opt-1920.WEBP differ
diff --git a/public/image/nextImageExportOptimizer/logodummy-opt-2048.WEBP b/public/image/nextImageExportOptimizer/logodummy-opt-2048.WEBP
new file mode 100644
index 0000000..f36dd95
Binary files /dev/null and b/public/image/nextImageExportOptimizer/logodummy-opt-2048.WEBP differ
diff --git a/public/image/nextImageExportOptimizer/logodummy-opt-256.WEBP b/public/image/nextImageExportOptimizer/logodummy-opt-256.WEBP
new file mode 100644
index 0000000..f36dd95
Binary files /dev/null and b/public/image/nextImageExportOptimizer/logodummy-opt-256.WEBP differ
diff --git a/public/image/nextImageExportOptimizer/logodummy-opt-32.WEBP b/public/image/nextImageExportOptimizer/logodummy-opt-32.WEBP
new file mode 100644
index 0000000..3affafb
Binary files /dev/null and b/public/image/nextImageExportOptimizer/logodummy-opt-32.WEBP differ
diff --git a/public/image/nextImageExportOptimizer/logodummy-opt-384.WEBP b/public/image/nextImageExportOptimizer/logodummy-opt-384.WEBP
new file mode 100644
index 0000000..f36dd95
Binary files /dev/null and b/public/image/nextImageExportOptimizer/logodummy-opt-384.WEBP differ
diff --git a/public/image/nextImageExportOptimizer/logodummy-opt-3840.WEBP b/public/image/nextImageExportOptimizer/logodummy-opt-3840.WEBP
new file mode 100644
index 0000000..f36dd95
Binary files /dev/null and b/public/image/nextImageExportOptimizer/logodummy-opt-3840.WEBP differ
diff --git a/public/image/nextImageExportOptimizer/logodummy-opt-48.WEBP b/public/image/nextImageExportOptimizer/logodummy-opt-48.WEBP
new file mode 100644
index 0000000..5d3fc3c
Binary files /dev/null and b/public/image/nextImageExportOptimizer/logodummy-opt-48.WEBP differ
diff --git a/public/image/nextImageExportOptimizer/logodummy-opt-64.WEBP b/public/image/nextImageExportOptimizer/logodummy-opt-64.WEBP
new file mode 100644
index 0000000..f36dd95
Binary files /dev/null and b/public/image/nextImageExportOptimizer/logodummy-opt-64.WEBP differ
diff --git a/public/image/nextImageExportOptimizer/logodummy-opt-640.WEBP b/public/image/nextImageExportOptimizer/logodummy-opt-640.WEBP
new file mode 100644
index 0000000..f36dd95
Binary files /dev/null and b/public/image/nextImageExportOptimizer/logodummy-opt-640.WEBP differ
diff --git a/public/image/nextImageExportOptimizer/logodummy-opt-750.WEBP b/public/image/nextImageExportOptimizer/logodummy-opt-750.WEBP
new file mode 100644
index 0000000..f36dd95
Binary files /dev/null and b/public/image/nextImageExportOptimizer/logodummy-opt-750.WEBP differ
diff --git a/public/image/nextImageExportOptimizer/logodummy-opt-828.WEBP b/public/image/nextImageExportOptimizer/logodummy-opt-828.WEBP
new file mode 100644
index 0000000..f36dd95
Binary files /dev/null and b/public/image/nextImageExportOptimizer/logodummy-opt-828.WEBP differ
diff --git a/public/image/nextImageExportOptimizer/logodummy-opt-96.WEBP b/public/image/nextImageExportOptimizer/logodummy-opt-96.WEBP
new file mode 100644
index 0000000..f36dd95
Binary files /dev/null and b/public/image/nextImageExportOptimizer/logodummy-opt-96.WEBP differ
diff --git a/public/next-image-export-optimizer-hashes.json b/public/next-image-export-optimizer-hashes.json
index 50fc997..e8eae1e 100644
--- a/public/next-image-export-optimizer-hashes.json
+++ b/public/next-image-export-optimizer-hashes.json
@@ -5,7 +5,9 @@
"image/grouplogo.png": "nVetjZphqR92a3EwnYIlVQcTWHXwzxEeUsfFrZ59W9g=",
"image/layer1.png": "QTEGhbVeWDTZQ+JWYULiLkETaWi0ybiI78gEMC-QL40=",
"image/layer2.png": "LfddDhf5OGLWosggG0KrRuBq5afq-1sLUBLYpRgryAk=",
+ "image/layer3.png": "a05J2QHjZFTOhXBnlUDVYsXMGxuTVma0aR1skHSj37Y=",
"image/logo.png": "guyrxmHxHTV4YYqWnO5sapp8aB6Gr2jARr5b-eA-Jao=",
+ "image/logodummy.png": "dVLj+C82snmcMrm1xUqwzw8zmXmSSGJtOKiaOaFqspo=",
"image/logonew.png": "zLpohFmIYCjwIAZ7n-egh6QTt9zue1N-d-69M8Fnses=",
"image/memphis-colorful.webp": "fHLKCUhSbSismaq1wl2nOnpiuWVohmsMEMX-H8z5Sro=",
"image/sports.webp": "n2DZ2RqxVBlVqJ6uPWYlSVPgBPbrdUUdNGYU3uFlnuU=",
diff --git a/src/app/betangPayment/detailVA.jsx b/src/app/betangPayment/detailVA.jsx
new file mode 100644
index 0000000..3bb2014
--- /dev/null
+++ b/src/app/betangPayment/detailVA.jsx
@@ -0,0 +1,270 @@
+import {useState} from "react";
+import {Table, message, Modal, Input} from "antd";
+import {Helper} from "@/lib/Helper";
+import TransitionContent from "@/component/TransitionContent";
+
+export default function DetailVA() {
+ const [isModalOpen, setIsModalOpen] = useState(false);
+ const [modalPin, setModalPin] = useState(false)
+ const [statusPIN, setStatusPIN] = useState(false)
+ const [pin, setPin] = useState(null)
+ const [messageApi, contextHolder] = message.useMessage();
+ const [selectedRow, setSelectedRow] = useState([])
+ const [dataDetail, setDataDetail] = useState({
+ institusi:'Hasanka',
+ nama:'Bagus',
+ kelas:'2',
+ nomorVA:'1029381029380129'
+ })
+
+ const rowSelection = {
+ onChange: (selectedRowKeys, selectedRows) => {
+ setSelectedRow(selectedRows)
+ // console.log(`selectedRowKeys: ${selectedRowKeys}`, 'selectedRows: ', selectedRows);
+ },
+ getCheckboxProps: (record) => ({
+ disabled: record.paymentStatus === 1,
+ }),
+ };
+
+ const columns = [
+ {
+ title: 'Nama Produk',
+ dataIndex: 'namaProduk',
+ },
+ {
+ title: 'Total Tagihan',
+ dataIndex: 'tagihan',
+ align:'right',
+ render: (text) => {Helper.numFormat(text)} ,
+ },
+ {
+ title: 'Keterangan',
+ dataIndex: 'keterangan',
+ },
+ ];
+ const data = [
+ {
+ id: '1',
+ namaProduk: 'SPP Januari 2024',
+ tagihan: 200000,
+ keterangan: 'Terbayar',
+ paymentStatus: 1
+ },
+ {
+ id: '2',
+ namaProduk: 'SPP Februari 2024',
+ tagihan: 30000,
+ keterangan: 'Belum Terbayar',
+ paymentStatus: 0
+ },
+ {
+ id: '3',
+ namaProduk: 'Uang Pembangunan',
+ tagihan: 400000,
+ keterangan: 'Belum Terbayar',
+ paymentStatus: 0
+ },
+ {
+ id: '4',
+ namaProduk: 'Uang Pakaian',
+ tagihan: 200000,
+ keterangan: 'Belum Terbayar',
+ paymentStatus: 0
+ },
+ ];
+
+ const actPayment = () => {
+ if(selectedRow.length === 0){
+ messageApi.open({
+ type: 'warning',
+ content: 'Pilih Produk Terlebih Dahulu',
+ });
+ return false
+ }
+
+ setIsModalOpen(true)
+ }
+
+ const actModalPIN = () => {
+ setIsModalOpen(false)
+ setModalPin(true)
+ setStatusPIN(false)
+ }
+
+ const sendPin = () => {
+ console.log(pin)
+
+ if(pin === null){
+ messageApi.open({
+ type: 'warning',
+ content: 'PIN Tidak Sesuai',
+ });
+ setPin(null)
+ return false
+ }
+
+ setStatusPIN(true)
+ setPin(null)
+ }
+
+ const actSendPayment = () => {
+ console.log('send payment')
+ }
+
+
+
+
+ return(
+ <>
+
+ {contextHolder}
+
+
+
+
+ Nama Institusi
+ : {dataDetail?.institusi}
+
+
+ Nama Lengkap
+ : {dataDetail?.nama}
+
+
+ Kelas
+ : {dataDetail?.kelas}
+
+
+ Nomor VA
+ : {dataDetail?.nomorVA}
+
+
+
+
+
+
+
+
+
+ {/*Batal */}
+ Bayar
+
+
+
+
+
+
+
Detail Pembayaran
+
Daftar Produk yang akan dibayarkan
+
+
+
+
+
+
+
+ Nama Institusi
+ : {dataDetail?.institusi}
+
+
+ Nama Lengkap
+ : {dataDetail?.nama}
+
+
+ Kelas
+ : {dataDetail?.kelas}
+
+
+ Total Tagihan
+ : {Helper.numFormat(selectedRow.reduce((sum, current) => sum + current.tagihan, 0))}
+
+
+
+
+
+
+
+ Keterangan
+ Tagihan
+
+
+
+ {selectedRow?.map((v,k) => (
+
+ {v.namaProduk}
+ {Helper.numFormat(v.tagihan)}
+
+ ))}
+
+
+
+
Apakah data yang dipilih sudah benar ? pastikan cek data terlebih dahulu dan klik tombol bayar untuk melakukan pembayaran
+
+
+ setIsModalOpen(false)} className={"button btn-full btn-dark"}>Batal
+ Bayar
+
+
+
+
+
+
+
+
+ {(statusPIN) ?
+
+
+
+
BERHASIL
+
Transaksi Berhasil, silahkan lakukan pembayaran melalui Mobile Banking
+
Buka Mobile Banking
+
+
+
+ :
+
+
+
+
PIN SMS
+
Masukan PIN SMS anda
+
+
+
+ {
+ if (!/[0-9]/.test(event.key)) {
+ event.preventDefault();
+ }
+ }} onChange={(text)=>setPin(text)} />
+
+
+
+
+
+ setModalPin(false)} className={"button btn-full btn-dark"}>Batal
+ OK
+
+
+
+ }
+
+
+
+
+
+
+
+
+
+
+ >
+ )
+}
\ No newline at end of file
diff --git a/src/app/betangPayment/inquiryVA.jsx b/src/app/betangPayment/inquiryVA.jsx
new file mode 100644
index 0000000..44498b4
--- /dev/null
+++ b/src/app/betangPayment/inquiryVA.jsx
@@ -0,0 +1,47 @@
+"use client"
+import {useForm} from "react-hook-form";
+import Input from "@/component/Input";
+import {motion} from "framer-motion";
+import LoadingPage from "@/component/LoadingPage";
+
+
+export default function InquiryVA({sendNoVA}) {
+ const {
+ register, handleSubmit, reset, setValue, formState: {errors},
+ } = useForm();
+
+ const submitVA = (data) => {
+ sendNoVA(data)
+ }
+
+ return(
+ <>
+
+
+ Inquiry No VA
+
+
+ >
+ )
+}
\ No newline at end of file
diff --git a/src/app/betangPayment/page.jsx b/src/app/betangPayment/page.jsx
new file mode 100644
index 0000000..2f7711c
--- /dev/null
+++ b/src/app/betangPayment/page.jsx
@@ -0,0 +1,44 @@
+"use client"
+import MainLayout from "@/component/MainLayout";
+import {useEffect, useState} from "react";
+import {CloseOutlined, LeftOutlined} from "@ant-design/icons";
+import Link from "next/link";
+import InquiryVA from "@/app/betangPayment/inquiryVA";
+import DetailVA from "@/app/betangPayment/detailVA";
+
+
+export default function Page() {
+ const [statusVA, setStatusVA] = useState(false)
+
+ const sendNoVA = (va) => {
+ console.log(va)
+ setStatusVA(true)
+ }
+
+ return (<>
+
+
+
+
+
+
+ Betang Payment
+
+
+
+
+
+
+ {(statusVA) ?
+
+ :
+
+ }
+
+
+
+
+
+
+ >)
+}
\ No newline at end of file
diff --git a/src/app/detailBelanja/page.jsx b/src/app/detailBelanja/page.jsx
deleted file mode 100644
index 78847af..0000000
--- a/src/app/detailBelanja/page.jsx
+++ /dev/null
@@ -1,182 +0,0 @@
-"use client"
-import HeaderSmall from "@/app/component/HeaderSmall";
-import {Col, Row, Tabs} from "antd";
-import {ShoppingOutlined} from "@ant-design/icons";
-import HeaderSmallDetail from "@/app/component/HeaderSmallDetail";
-import NoData from "@/app/component/NoData";
-import MainLayout from "@/app/component/MainLayout";
-import ExportedImage from "next-image-export-optimizer";
-
-export default function page() {
-
- const onChange = () => {
-
- }
-
- const CardOmni = () => {
- return (
-
-
-
-
-
-
Garuda Indonesia
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Garuda Indonesia
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Garuda Indonesia
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Garuda Indonesia
-
-
-
-
-
-
-
-
-
-
-
-
-
- )
- }
-
- const items = [
- {
- key: '1', label: 'Fashion & Kecantikan ', children: ,
- }, {
- key: '2', label: 'Rekreasi', children: ,
- }, {
- key: '3', label: 'Donasi & Zakat', children: 'Tidak Ada Data',
- }, {
- key: '4', label: 'Hobi & Event', children: 'Tidak Ada Data',
- }, {
- key: '5', label: 'Travel', children: 'Tidak Ada Data',
- }, {
- key: '6', label: 'Elektronik & Barang Digital ', children: 'Tidak Ada Data',
- },
-
- ];
-
- return(
-
-
-
-
-
-
- )
-}
\ No newline at end of file
diff --git a/src/app/detailBerita/page.jsx b/src/app/detailBerita/page.jsx
index 76780fd..a41a241 100644
--- a/src/app/detailBerita/page.jsx
+++ b/src/app/detailBerita/page.jsx
@@ -1,164 +1,57 @@
"use client"
-import HeaderSmallDetail from "@/app/component/HeaderSmallDetail";
-import {Tabs} from "antd";
-import NoData from "@/app/component/NoData";
-import MainLayout from "@/app/component/MainLayout";
+import MainLayout from "@/component/MainLayout";
import ExportedImage from "next-image-export-optimizer";
+import {CloseOutlined, LeftOutlined} from "@ant-design/icons";
+import Link from "next/link";
+import {useEffect, useState} from "react";
+import {API} from "@/lib/API";
-export default function page() {
+export default function Page() {
+ const [dataDetail, setDataDetail] = useState(null)
- const onChange = () => {
+ const getDataDetail = async () => {
+ let queryString = window.location.search;
+ let urlParams = new URLSearchParams(queryString);
+ let idBerita = urlParams.get('id')
+
+ let res = await API.GET_NEWS('/latest?id=' + idBerita)
+ setDataDetail(res?.result?.results[0])
}
- const ListBerita = () => {
- return (<>
-
-
-
-
-
-
-
Olahraga
-
Chelsea Keropos, Lini Belakang Jadi...
-
Detik.com | 30 Menit yang lalu
-
-
-
-
-
-
-
-
-
Olahraga
-
Chelsea Keropos, Lini Belakang Jadi...
-
Detik.com | 30 Menit yang lalu
-
-
-
-
-
-
-
-
-
Olahraga
-
Chelsea Keropos, Lini Belakang Jadi...
-
Detik.com | 30 Menit yang lalu
-
-
-
-
-
-
-
-
-
Olahraga
-
Chelsea Keropos, Lini Belakang Jadi...
-
Detik.com | 30 Menit yang lalu
-
-
-
-
-
-
-
-
-
Olahraga
-
Chelsea Keropos, Lini Belakang Jadi...
-
Detik.com | 30 Menit yang lalu
-
-
-
-
-
-
-
-
-
Olahraga
-
Chelsea Keropos, Lini Belakang Jadi...
-
Detik.com | 30 Menit yang lalu
-
-
-
-
-
-
-
-
-
Olahraga
-
Chelsea Keropos, Lini Belakang Jadi...
-
Detik.com | 30 Menit yang lalu
-
-
-
-
-
-
-
-
-
Olahraga
-
Chelsea Keropos, Lini Belakang Jadi...
-
Detik.com | 30 Menit yang lalu
-
-
-
-
-
-
-
-
-
Olahraga
-
Chelsea Keropos, Lini Belakang Jadi...
-
Detik.com | 30 Menit yang lalu
-
-
-
-
-
-
-
-
-
Olahraga
-
Chelsea Keropos, Lini Belakang Jadi...
-
Detik.com | 30 Menit yang lalu
-
-
-
-
-
-
-
-
- >)
- }
-
- const items = [{
- key: '1', label: 'Semua ', children: ,
- }, {
- key: '2', label: 'Olahraga', children: ,
- }, {
- key: '3', label: 'Dunia', children: ,
- }, {
- key: '4', label: 'Ekonomi', children: ,
- }, {
- key: '5', label: 'Keluarga', children: ,
- }, {
- key: '6', label: 'Politik', children: ,
- },
-
- ];
+ useEffect(() => {
+ getDataDetail()
+ }, []);
return (
-
-
-
-
-
+
+
+
+
+ {dataDetail?.source_name}
+
+
+
+
+
+
+
+
+
{dataDetail?.title}
+
{dataDetail?.description}
+
+
+
+
+ {/*
-
+
+
)
}
\ No newline at end of file
diff --git a/src/app/detailPayment/page.jsx b/src/app/detailPayment/page.jsx
new file mode 100644
index 0000000..d9beeae
--- /dev/null
+++ b/src/app/detailPayment/page.jsx
@@ -0,0 +1,49 @@
+"use client"
+import MainLayout from "@/component/MainLayout";
+import ExportedImage from "next-image-export-optimizer";
+import {CloseOutlined, LeftOutlined} from "@ant-design/icons";
+import Link from "next/link";
+import {useEffect, useState} from "react";
+
+export default function Page() {
+
+ const [paymnetData, setPaymnetData] = useState({
+ name: '', url: ''
+ })
+
+ const getPaymentData = async () => {
+ let queryString = window.location.search;
+ let urlParams = new URLSearchParams(queryString);
+ let id = urlParams.get('id')
+
+ setPaymnetData({
+ name: "Telkomsel", url: 'https://www.telkomsel.com/shops/channel/o2o?utm_source=wec&utm_medium=inapp&utm_campaign=omni-bankkalteng&target=bankkalteng&theme=bankkalteng&embed=on'
+ })
+ }
+
+ useEffect(() => {
+ getPaymentData()
+ }, []);
+
+ return (
+
+
+
+
+
+
+ {paymnetData?.name}
+
+
+
+
+
+
+
+
+ {/**/}
+
+ )
+}
\ No newline at end of file
diff --git a/src/app/layout.js b/src/app/layout.js
index 803e288..1934b3d 100644
--- a/src/app/layout.js
+++ b/src/app/layout.js
@@ -1,6 +1,6 @@
-import "./globals.css";
+import "@@/css/globals.scss";
import localFont from 'next/font/local'
-import LoadingPage from "@/app/component/LoadingPage";
+import LoadingPage from "@/component/LoadingPage";
const poppins = localFont({
@@ -36,8 +36,9 @@ const poppins = localFont({
export const metadata = {
- title: "Omnichannel Kalteng",
+ title: "Betang Portal Payment Kalteng",
description: "Web Omnichannel",
+
icons: {
icon: '/image/logo.png',
shortcut: '/image/logo.png',
@@ -50,6 +51,7 @@ export const viewport = {
width: 'device-width',
initialScale: 1,
maximumScale: 1,
+ themeColor: "#159B9F"
}
export default function RootLayout({ children }) {
diff --git a/src/app/listBelanja/page.jsx b/src/app/listBelanja/page.jsx
new file mode 100644
index 0000000..208793d
--- /dev/null
+++ b/src/app/listBelanja/page.jsx
@@ -0,0 +1,111 @@
+"use client"
+import HeaderSmall from "@/component/HeaderSmall";
+import {Col, Row, Tabs} from "antd";
+import {CreditCardOutlined, ShoppingOutlined} from "@ant-design/icons";
+import HeaderSmallDetail from "@/component/HeaderSmallDetail";
+import NoData from "@/component/NoData";
+import MainLayout from "@/component/MainLayout";
+import ExportedImage from "next-image-export-optimizer";
+import Link from "next/link";
+import {useEffect, useState} from "react";
+
+export default function Page() {
+ const [listPayment, setListPayment] = useState([])
+
+
+ const getListPayment = () => {
+ const dataDummy = [
+ {
+ id:1,
+ name:'Traveloka Hotel',
+ logo:'/logo/traveloka.png'
+ },
+ {
+ id:2,
+ name:'Traveloka Flight',
+ logo:'/logo/traveloka.png'
+ },
+ {
+ id:3,
+ name:'KAI',
+ logo:'/logo/kai.png'
+ },
+ {
+ id:4,
+ name:'Virtual Account',
+ logo:null
+ },
+ {
+ id:5,
+ name:'Telkomsel',
+ logo:null
+ }
+ ]
+
+ setListPayment(dataDummy)
+ }
+
+
+ const onChange = () => {
+
+ }
+
+ const CardOmni = () => {
+ return (
+
+ {listPayment?.map((v,k) => (
+
+
+
+ {(v?.logo) ?
+
+ :
+
+ }
+
+ {v?.name}
+
+
+ ))}
+
+
+
+ )
+ }
+
+ const items = [
+ {
+ key: '1', label: 'Semua ', children: ,
+ }, {
+ key: '2', label: 'Rekreasi', children: ,
+ }, {
+ key: '3', label: 'Donasi & Zakat', children: 'Tidak Ada Data',
+ }, {
+ key: '4', label: 'Hobi & Event', children: 'Tidak Ada Data',
+ }, {
+ key: '5', label: 'Travel', children: 'Tidak Ada Data',
+ }, {
+ key: '6', label: 'Elektronik & Barang Digital ', children: 'Tidak Ada Data',
+ },
+
+ ];
+
+ useEffect(() => {
+ getListPayment()
+ }, []);
+
+ return(
+
+
+
+
+
+
+ )
+}
\ No newline at end of file
diff --git a/src/app/listBerita/page.jsx b/src/app/listBerita/page.jsx
new file mode 100644
index 0000000..7031cf4
--- /dev/null
+++ b/src/app/listBerita/page.jsx
@@ -0,0 +1,76 @@
+"use client"
+import HeaderSmallDetail from "@/component/HeaderSmallDetail";
+import {Tabs} from "antd";
+import NoData from "@/component/NoData";
+import MainLayout from "@/component/MainLayout";
+import ExportedImage from "next-image-export-optimizer";
+import {API} from "@/lib/API";
+import {useEffect, useState} from "react";
+import Link from "next/link";
+import {Helper} from "@/lib/Helper";
+
+export default function Page() {
+ const [listNews, setListNews] = useState()
+
+ const getNews = async () => {
+ let res = await API.GET_NEWS('/latest?country=id')
+ setListNews(res?.result?.results)
+ }
+
+
+ const onChange = () => {
+
+ }
+
+ useEffect(() => {
+ getNews()
+ }, []);
+
+ const ListBerita = () => {
+ return (
+ {listNews?.map((v,k) => (
+
+
+
+
+
+
{v?.category?.toString()}
+
{Helper.limitString(v?.title,30) }
+
{v?.source_name} | { Helper.getDifferenceDate(new Date(),v?.pubDate) }
+
+
+ ))}
+
+
)
+ }
+
+ const items = [{
+ key: '1', label: 'Semua ', children: ,
+ }, {
+ key: '2', label: 'Olahraga', children: ,
+ }, {
+ key: '3', label: 'Dunia', children: ,
+ }, {
+ key: '4', label: 'Ekonomi', children: ,
+ }, {
+ key: '5', label: 'Keluarga', children: ,
+ }, {
+ key: '6', label: 'Politik', children: ,
+ },
+
+ ];
+
+ return (
+
+
+
+
+
+
+
+ )
+}
\ No newline at end of file
diff --git a/src/app/page.js b/src/app/page.js
index d2aab51..ecb3f8b 100644
--- a/src/app/page.js
+++ b/src/app/page.js
@@ -1,18 +1,73 @@
"use client"
import {Carousel, Col, Input, Row, Tabs} from "antd";
-import {BellOutlined, CloseOutlined, EnvironmentOutlined, ReadOutlined, SearchOutlined, ShoppingOutlined} from "@ant-design/icons";
+import {BellOutlined, CloseOutlined, CreditCardOutlined, EnvironmentOutlined, MoneyCollectFilled, ReadOutlined, SearchOutlined, ShoppingOutlined} from "@ant-design/icons";
import {useEffect, useState} from "react";
-import Header from "@/app/component/Header";
-import HeaderSmall from "@/app/component/HeaderSmall";
+import Header from "@/component/Header";
+import HeaderSmall from "@/component/HeaderSmall";
import Link from "next/link";
-import LoadingPage from "@/app/component/LoadingPage";
-import MainLayout from "@/app/component/MainLayout";
+import LoadingPage from "@/component/LoadingPage";
+import MainLayout from "@/component/MainLayout";
import ExportedImage from "next-image-export-optimizer";
+import {API} from "@/lib/API";
+import {Helper} from "@/lib/Helper";
+import NoData from "@/component/NoData";
export default function Home() {
const [isVisible, setIsVisible] = useState(true);
+ const [listPayment, setListPayment] = useState([])
+ const [listNews, setListNews] = useState([])
+
+
- useEffect(() => {
+ const getListPayment = () => {
+ const dataDummy = [
+ {
+ id:1,
+ name:'Traveloka Hotel',
+ logo:'/logo/traveloka.png',
+ url:null
+ },
+ {
+ id:2,
+ name:'Traveloka Flight',
+ logo:'/logo/traveloka.png',
+ url:null
+ },
+ {
+ id:3,
+ name:'KAI',
+ logo:'/logo/kai.png',
+ url:null
+ },
+ {
+ id:4,
+ name:'Virtual Account',
+ logo:null,
+ url:null
+ },
+ {
+ id:5,
+ name:'Telkomsel',
+ logo:null,
+ url:null
+ },
+ {
+ id:6,
+ name:'Betang Payment',
+ logo:null,
+ url:'/betangPayment'
+ }
+ ]
+
+ setListPayment(dataDummy)
+ }
+
+ const getNews = async () => {
+ let res = await API.GET_NEWS('/latest?country=id')
+ setListNews(res?.result?.results)
+ }
+
+ const scrollEffect = () => {
const handleScroll = () => {
if (window.scrollY > 150) {
setIsVisible(false);
@@ -26,100 +81,75 @@ export default function Home() {
return () => {
window.removeEventListener('scroll', handleScroll);
};
+ }
+
+ useEffect(() => {
+ scrollEffect()
+ getListPayment()
+ // getNews()
+
}, []);
- const onChange = (key) => {
- console.log(key);
- };
+
const CardOmni = () => {
return (
-
-
-
-
-
-
Garuda Indonesia
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Garuda Indonesia
-
-
-
-
-
-
-
-
-
-
-
+ {listPayment.map((v,k) => (
+
+
+
+ {(v?.logo) ?
+
+ :
+
+ }
+
+ {v?.name}
+
+
+ ))}
)
}
+ const dummy = [
+ {
+ idKategori:'xxx',
+ namaKategori:'xxx',
+ menu:[
+ {
+ id:1,
+ name:'Traveloka Hotel',
+ logo:'/logo/traveloka.png',
+ url:null
+ },
+ {
+ id:2,
+ name:'Traveloka Flight',
+ logo:'/logo/traveloka.png',
+ url:null
+ },
+ ]
+ }
+ ]
+
const items = [
{
- key: '1', label: 'Fashion & Kecantikan ', children: ,
+ key: '1', label: 'Semua ', children: ,
}, {
- key: '2', label: 'Rekreasi', children: 'Tidak Ada Data',
+ key: '2', label: 'Rekreasi', children: ,
}, {
- key: '3', label: 'Donasi & Zakat', children: 'Tidak Ada Data',
+ key: '3', label: 'Donasi & Zakat', children: ,
}, {
- key: '4', label: 'Hobi & Event', children: 'Tidak Ada Data',
+ key: '4', label: 'Hobi & Event', children: ,
}, {
- key: '5', label: 'Travel', children: 'Tidak Ada Data',
+ key: '5', label: 'Travel', children: ,
}, {
- key: '6', label: 'Elektronik & Barang Digital ', children: 'Tidak Ada Data',
+ key: '6', label: 'Elektronik & Barang Digital ', children: ,
},
-
];
return (<>
@@ -134,11 +164,11 @@ export default function Home() {
Waktunya Belanja
Semua yang Kamu perlu, ada di sini
- Lihat Semua
+ Lihat Semua
-
+
@@ -153,70 +183,45 @@ export default function Home() {
-
-
-
-
-
-
Olahraga
-
Chelsea Keropos, Lini Belakang Jadi PR Maresca
-
Detik.com | 30 Menit yang lalu
-
-
-
-
-
-
-
Olahraga
-
Chelsea Keropos, Lini Belakang Jadi PR Maresca
-
Detik.com | 30 Menit yang lalu
-
-
+ {(listNews) ?
+
+ {listNews?.slice(0,5)?.map((v,k) => (
+
+
+
+
+ {/*
{v?.category?.toString()}
*/}
+
{v?.title}
+
{v?.source_name} | { Helper.getDifferenceDate(new Date(),v?.pubDate) }
+
+
+ ))}
+ :'loading...' }
-
-
-
-
-
-
-
Olahraga
-
Chelsea Keropos, Lini Belakang Jadi...
-
Detik.com | 30 Menit yang lalu
-
+ {listNews?.slice(5,10)?.map((v,k) => (
+
+
+
+
+
+
{v?.category?.toString()}
+
{Helper.limitString(v?.title,30) }
+
{v?.source_name} | { Helper.getDifferenceDate(new Date(),v?.pubDate) }
+
+
+ ))}
-
-
-
-
-
-
-
Olahraga
-
Chelsea Keropos, Lini Belakang Jadi...
-
Detik.com | 30 Menit yang lalu
-
-
-
-
-
-
-
-
Olahraga
-
Chelsea Keropos, Lini Belakang Jadi...
-
Detik.com | 30 Menit yang lalu
-
-
-
-
Lihat Semua
+
Lihat Semua
- produk atau jasa di menu omnichannel disediakan dan sepenuhnya menjadi tanggungjawab penyedia produk atau jasa terkait. baca syarat dan ketentuan lengkap disini
+ produk atau jasa di menu Betang Portal Payment disediakan dan sepenuhnya menjadi tanggungjawab penyedia produk atau jasa terkait. baca syarat dan ketentuan lengkap disini
diff --git a/src/app/component/Header.jsx b/src/component/Header.jsx
similarity index 51%
rename from src/app/component/Header.jsx
rename to src/component/Header.jsx
index 45f99a2..2fc707c 100644
--- a/src/app/component/Header.jsx
+++ b/src/component/Header.jsx
@@ -1,14 +1,46 @@
import {BellOutlined, CloseOutlined, EnvironmentOutlined, SearchOutlined} from "@ant-design/icons";
import {Input} from "antd";
import ExportedImage from "next-image-export-optimizer";
+import {useEffect, useState} from "react";
export default function Header({isVisible}) {
+ const [location, setLocation] = useState()
+
+ const getLocation = async () => {
+
+ if ("geolocation" in navigator) {
+ navigator.geolocation.getCurrentPosition(
+ async (position) => {
+
+ let response = await fetch(`https://geocode.maps.co/reverse?lat=${position.coords.latitude}&lon=${position.coords.longitude}`);
+ const locationName = await response.json();
+ console.log(locationName)
+
+ setLocation({
+ latitude: position.coords.latitude,
+ longitude: position.coords.longitude,
+ name:`${locationName.address.village}, ${locationName.address.city}`
+ });
+ },
+ (error) => {
+ setError(error.message);
+ }
+ );
+ } else {
+ setError("Geolocation is not supported by this browser.");
+ }
+ }
+
+ useEffect(() => {
+ console.log(location)
+ }, [location]);
+
return(
<>
-
+
- Kota Bandung, Jawa Barat
+ {(location?.name) ? location?.name:"Aktifkan Lokasi"}
@@ -23,15 +55,17 @@ export default function Header({isVisible}) {
+
Betang
+
Portal Payment
- }/>
+ }/>
diff --git a/src/app/component/HeaderSmall.jsx b/src/component/HeaderSmall.jsx
similarity index 94%
rename from src/app/component/HeaderSmall.jsx
rename to src/component/HeaderSmall.jsx
index 4d88bf5..83afcca 100644
--- a/src/app/component/HeaderSmall.jsx
+++ b/src/component/HeaderSmall.jsx
@@ -14,7 +14,7 @@ export default function HeaderSmall({isVisible}) {
- }/>
+ }/>
diff --git a/src/app/component/HeaderSmallDetail.jsx b/src/component/HeaderSmallDetail.jsx
similarity index 79%
rename from src/app/component/HeaderSmallDetail.jsx
rename to src/component/HeaderSmallDetail.jsx
index 784f045..72e15b3 100644
--- a/src/app/component/HeaderSmallDetail.jsx
+++ b/src/component/HeaderSmallDetail.jsx
@@ -3,7 +3,7 @@ import {Input} from "antd";
import Link from "next/link";
import ExportedImage from "next-image-export-optimizer";
-export default function HeaderSmallDetail({isVisible}) {
+export default function HeaderSmallDetail({isVisible,noSearch}) {
return(
<>
diff --git a/src/component/Input/InputDate.jsx b/src/component/Input/InputDate.jsx
new file mode 100644
index 0000000..476f194
--- /dev/null
+++ b/src/component/Input/InputDate.jsx
@@ -0,0 +1,69 @@
+import {Helper} from "@/lib/Helper";
+import {DatePicker} from "antd";
+import dayjs from "dayjs";
+
+const InputDate = ({
+ title,
+ name,
+ register,
+ error,
+ placeholder,
+ maxlength,
+ required,
+ setValue,
+ value,
+ minDate,
+ maxDate,
+ picker = "default",
+ format = 'DD MMM YYYY',
+}) => {
+ let setRequired = required
+ ? { required: `Data ${title} Harus di Isi` }
+ : {};
+ let setMaxLength = maxlength
+ ? {
+ maxLength: {
+ value: maxlength,
+ message: `Maksimal ${maxlength} Karakter`,
+ },
+ }
+ : {};
+
+ let validateList = { ...setRequired, ...setMaxLength };
+
+ const onChange = (_, dateString) => {
+ setValue(name, dayjs(dateString, 'DD MMM YYYY'), { shouldValidate: true });
+ };
+
+ const newFormat = Helper.setFormatInputDate(picker, format);
+
+ return (
+
+
+
+
+ {" "}
+ {title}{" "}{(required) ? * :''}
+
+ {error[name] && (
+
{error[name]?.message}
+ )}
+
+
+ );
+};
+
+export default InputDate;
diff --git a/src/component/Input/InputDateRange.jsx b/src/component/Input/InputDateRange.jsx
new file mode 100644
index 0000000..344b9ee
--- /dev/null
+++ b/src/component/Input/InputDateRange.jsx
@@ -0,0 +1,66 @@
+import {Helper} from "@/lib/Helper";
+import {DatePicker, Space} from 'antd';
+import dayjs from 'dayjs';
+
+const { RangePicker } = DatePicker;
+
+const rangePresets = [
+ { label: 'Seminggu terakhir', value: [dayjs().add(-7, 'd'), dayjs()] },
+ { label: 'Bulan ini', value: [dayjs().startOf("month") , dayjs().endOf("month")] },
+ { label: 'Bulan lalu', value: [dayjs().startOf("month").month(dayjs().month() - 1), dayjs().endOf("month").month(dayjs().month() - 1)] },
+ {label: 'Tahun ini', value: [dayjs().startOf("year"), dayjs().endOf("year")]},
+ {label: 'Tahun lalu', value: [dayjs().startOf('year').year(dayjs().year() - 1), dayjs().endOf('year').year(dayjs().year() - 1)]},
+];
+
+const InputDateRange = ({
+ title,
+ name,
+ register,
+ error,
+ placeholder,
+ maxlength,
+ required,
+ setValue,
+ format = 'DD MMM YYYY',
+ picker = 'default',
+ }) => {
+ let setRequired = (required) ? { required: `Data ${title} Harus di Isi` } : {}
+ let setMaxLength = (maxlength) ? { maxLength: { value: maxlength, message: `Maksimal ${maxlength} Karakter` } } : {}
+
+ let validateList = {...setRequired,...setMaxLength}
+
+ const onChange = (_, dateString) => {
+ setValue(name, {
+ startDate: dateString[0],
+ endDate: dateString[1]
+ }, { shouldValidate: true })
+ };
+
+ const newFormat = Helper.setFormatInputDate(picker, format)
+
+ return (
+
+
+
+ {title}
+
+
+ {error[name] &&
{error[name]?.message}
}
+
+
+ );
+ };
+
+ export default InputDateRange;
+
\ No newline at end of file
diff --git a/src/component/Input/InputEmail.jsx b/src/component/Input/InputEmail.jsx
new file mode 100644
index 0000000..bc0ca14
--- /dev/null
+++ b/src/component/Input/InputEmail.jsx
@@ -0,0 +1,50 @@
+const InputEmail = ({
+ title,
+ name,
+ register,
+ error,
+ placeholder,
+ maxlength,
+ required,
+ }) => {
+ let setRequired = (required) ? {required: "Data must be filled in"} : {}
+ let setMaxLength = (maxlength) ? {maxLength: {value: maxlength, message: `Maksimal ${maxlength} Karakter`}} : {}
+ let setEmail = {
+ pattern: {
+ value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
+ message: 'Format E-Mail Salah',
+ }
+ }
+
+ let validateList = {...setRequired, ...setMaxLength, ...setEmail}
+
+ const handleInput = (e) => {
+ e.target.value = e.target.value.replace(/[^a-zA-Z0-9_!@#$-%+.=&]/g, "");
+ };
+ return (
+ <>
+
+
+
+
+ {title}
+
+ {error[name] &&
{error[name]?.message}
}
+
+
+
+ >
+ );
+};
+
+export default InputEmail;
+
\ No newline at end of file
diff --git a/src/component/Input/InputImage.jsx b/src/component/Input/InputImage.jsx
new file mode 100644
index 0000000..eadca09
--- /dev/null
+++ b/src/component/Input/InputImage.jsx
@@ -0,0 +1,63 @@
+"use client"
+import {useEffect} from "react";
+
+const InputImage = ({
+ title,
+ name,
+ register,
+ error,
+ placeholder,
+ maxlength,
+ required,
+}) => {
+ let setRequired = (required) ? { required: "Data must be filled in" } : {}
+ let setMaxLength = (maxlength) ? { maxLength: { value: maxlength, message: `Maksimal ${maxlength} Karakter` } } : {}
+
+ let validateList = {...setRequired,...setMaxLength}
+
+ useEffect(() => {
+ // $('#changeImage_'+name).hide();
+ // $('#imageInput_'+name).on('change', function() {
+ // let inputImg = $(this);
+ // if(inputImg.val().length > 0) {
+ // let fileReader = new FileReader();
+ // fileReader.onload = function (data) {
+ // $("#imagePreview_"+name).attr('src', data.target.result);
+ // }
+ // fileReader.readAsDataURL(inputImg.prop('files')[0]);
+ // $("#imagePreview_"+name).css('display', 'block');
+ // $('#changeImage_'+name).css('display', 'block');
+
+ // }
+ // });
+
+ // $('#changeImage_'+name).on('click', function() {
+ // let $control = $(this);
+ // $control.css('display', 'none');
+ // $('#imageInput_'+name).val('');
+ // let $preview = $("#imagePreview_"+name);
+ // $preview.attr('src', '/img/no-image.jpg');
+ // });
+ }, [])
+
+
+ return (
+ <>
+ {title}
+ Image Format PNG,JPG,JPEG Max 500 Kb. Recommendation resolution is 800 x 800
+
+
+
+
+
+
+
+
+
+
+
+ >
+ );
+};
+
+export default InputImage;
diff --git a/src/component/Input/InputMoney.jsx b/src/component/Input/InputMoney.jsx
new file mode 100644
index 0000000..16f70bd
--- /dev/null
+++ b/src/component/Input/InputMoney.jsx
@@ -0,0 +1,99 @@
+const InputMoney = ({
+ title,
+ name,
+ register,
+ error,
+ placeholder,
+ maxlength,
+ required,
+ setReadonly,
+ satuanInput,
+ allowDecimal
+ }) => {
+
+ let setRequired = (required) ? {required: `Data ${title} Harus di Isi`} : {}
+ let setMaxLength = (maxlength) ? {maxLength: {value: maxlength, message: `Maksimal ${maxlength} Karakter`}} : {}
+
+ let validateList = {...setRequired, ...setMaxLength}
+
+ const handleInput = (e) => {
+ // e.target.value = e.target.value.replace(/[^0-9]/g, '');
+ var separator = ".";
+ var a = e.target.value;
+ var b = a.replace(/[^\d]/g, "");
+ var c = "";
+ var panjang = b.length;
+ var j = 0;
+ for (var i = panjang; i > 0; i--) {
+ j = j + 1;
+ if (((j % 3) == 1) && (j != 1)) {
+ c = b.substr(i - 1, 1) + separator + c;
+ } else {
+ c = b.substr(i - 1, 1) + c;
+ }
+ }
+ e.target.value = c
+ };
+
+ const handleInputDecimal = (event) => {
+ const inputField = event.target;
+ let value = inputField.value;
+
+ // Remove any characters that are not digits or the comma
+ value = value.replace(/[^\d,]/g, '');
+
+ // Ensure there is only one comma
+ const commaIndex = value.indexOf(',');
+ if (commaIndex !== -1) {
+ const beforeComma = value.slice(0, commaIndex);
+ const afterComma = value.slice(commaIndex + 1).replace(/,/g, ''); // Remove any additional commas
+ value = beforeComma + ',' + afterComma;
+ }
+
+ // Format the integer part with thousand separators
+ const parts = value.split(',');
+ parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, '.');
+
+ // Limit to two decimal places
+ if (parts.length > 1) {
+ parts[1] = parts[1].slice(0, 2);
+ }
+
+ // Join the parts back together
+ value = parts.join(',');
+
+
+ event.target.value = value
+ }
+
+ let setStyle = (satuanInput) ? {textAlign:'right',paddingRight:'40px'} :{}
+ return (
+ <>
+
+
+ {(satuanInput) ?
{satuanInput}
:''}
+
+
+ {title} {(required) ? * :''}
+
+ {error[name] &&
{error[name]?.message}
}
+
+
+
+ >
+ );
+};
+
+export default InputMoney;
+
\ No newline at end of file
diff --git a/src/component/Input/InputNumber.jsx b/src/component/Input/InputNumber.jsx
new file mode 100644
index 0000000..96bed52
--- /dev/null
+++ b/src/component/Input/InputNumber.jsx
@@ -0,0 +1,56 @@
+const InputNumber = ({
+ title,
+ name,
+ register,
+ error,
+ placeholder,
+ maxlength,
+ minlength,
+ required,
+ setReadonly,
+ satuanInput,
+ maxNominal,
+ minNominal
+ }) => {
+
+ let setRequired = (required) ? {required: `Data ${title} Harus di Isi`} : {}
+ let setMaxLength = (maxlength) ? {maxLength: {value: maxlength, message: `Maksimal ${maxlength} Karakter`}} : {}
+ let setMinLength = (minlength) ? {maxLength: {value: minlength, message: `Minimal ${minlength} Karakter`}} : {}
+ let setMaxNominal = (maxNominal) ? {max: {value: maxNominal, message: `Maksimal ${maxNominal} ${satuanInput ?? ''}`}} : {}
+ let setMinNominal = (minNominal) ? {min: {value: minNominal, message: `Minimal ${minNominal} ${satuanInput ?? ''}`}} : {}
+
+ let validateList = {...setRequired, ...setMaxLength, ...setMaxNominal,...setMinNominal}
+
+ const handleInput = (e) => {
+ e.target.value = e.target.value.replace(/[^0-9]/g, '');
+ };
+ return (
+ <>
+
+
+ {(satuanInput) ?
{satuanInput}
:''}
+
+
+ {title} {(required) ? * :''}
+
+ {error[name] &&
{error[name]?.message}
}
+
+
+
+ >
+ );
+};
+
+export default InputNumber;
+
\ No newline at end of file
diff --git a/src/component/Input/InputPassword.jsx b/src/component/Input/InputPassword.jsx
new file mode 100644
index 0000000..7efa987
--- /dev/null
+++ b/src/component/Input/InputPassword.jsx
@@ -0,0 +1,61 @@
+"use client"
+import {useRef, useState} from "react";
+import {EyeFilled, EyeInvisibleFilled} from '@ant-design/icons';
+
+const InputPassword = ({
+ title,
+ name,
+ register,
+ error,
+ placeholder,
+ maxlength,
+ required,
+ }) => {
+
+
+ const [showPassword, setShowPassword] = useState(false);
+ const inputRef = useRef(null);
+
+ let setRequired = (required) ? {required: `${title} Harus di Isi`} : {}
+ let setMaxLength = (maxlength) ? {maxLength: {value: maxlength, message: `Maksimal ${maxlength} Karakter`}} : {}
+ let validateList = {...setRequired, ...setMaxLength}
+
+ const handleInput = (e) => {
+ e.target.value = e.target.value.replace(/[^a-zA-Z0-9_!@#$-%+=&]/g, "");
+ };
+ const togglePassword = () => {
+ setShowPassword(prevState => !prevState);
+ };
+
+
+ return (
+ <>
+
+
+
+
+ {showPassword ? : }
+
+
+ {title}
+
+ {error[name] &&
{error[name]?.message}
}
+
+
+
+ >
+ );
+};
+
+export default InputPassword;
+
\ No newline at end of file
diff --git a/src/component/Input/InputPercentage.jsx b/src/component/Input/InputPercentage.jsx
new file mode 100644
index 0000000..29b0538
--- /dev/null
+++ b/src/component/Input/InputPercentage.jsx
@@ -0,0 +1,67 @@
+import {InputNumber} from 'antd'
+
+const InputPercentage = ({
+ title,
+ name,
+ register,
+ error,
+ placeholder,
+ maxlength,
+ required,
+ setReadonly,
+ setValue,
+ val
+}) => {
+ let setRequired = required
+ ? { required: `Data ${title} Harus di Isi` }
+ : {};
+ let setMaxLength = maxlength
+ ? {
+ maxLength: {
+ value: maxlength,
+ message: `Maksimal ${maxlength} Karakter`,
+ },
+ }
+ : {};
+
+ let validateList = { ...setRequired, ...setMaxLength };
+
+ const onChange = (value) => {
+ setValue(name, value, {shouldValidate: true})
+ };
+
+ return (
+ <>
+
+
+
value ? `${value}` : ''}
+ parser={(value) => value.replace('%', '')}
+ readOnly={setReadonly}
+ {...register(name, validateList)}
+ className="form-control borderInput floating-input"
+ name={name}
+ maxLength={maxlength ? maxlength : 250}
+ placeholder={placeholder ? placeholder : `.`}
+ onChange={onChange}
+ value={val}
+ />
+
+ {title}
+
+ {error[name] && (
+ {error[name]?.message}
+ )}
+
+
+ >
+ );
+};
+
+export default InputPercentage;
diff --git a/src/component/Input/InputSelect.jsx b/src/component/Input/InputSelect.jsx
new file mode 100644
index 0000000..636cff7
--- /dev/null
+++ b/src/component/Input/InputSelect.jsx
@@ -0,0 +1,66 @@
+import {Select} from 'antd';
+
+const InputSelect = ({
+ title,
+ name,
+ register,
+ error,
+ placeholder,
+ required,
+ options,
+ setValue,
+ setReadonly,
+ setDisabled,
+ val,
+ loading,
+ withSearch = true,
+ getDataOnChange
+ }) => {
+ let setRequired = (required) ? {required: `Data ${title} Harus di Isi`} : {}
+ let validateList = {...setRequired};
+
+ const handleChange = (value) => {
+ setValue(name, value, {shouldValidate: true})
+ if (getDataOnChange) getDataOnChange(value, setValue)
+ };
+ // const onSearch = (value) => {
+ // console.log('search:', value);
+ // };
+
+ const filterOption = (input, option) =>
+ (option?.label ?? '').toLowerCase().includes(input.toLowerCase());
+
+
+ return (
+ <>
+
+
+
+
+ {title} {(required) ? * :''}
+
+ {error[name] &&
{error[name]?.message}
}
+
+
+ >
+ );
+};
+
+export default InputSelect;
diff --git a/src/component/Input/InputText.jsx b/src/component/Input/InputText.jsx
new file mode 100644
index 0000000..ca09827
--- /dev/null
+++ b/src/component/Input/InputText.jsx
@@ -0,0 +1,41 @@
+const InputText = ({
+ title,
+ name,
+ register,
+ error,
+ placeholder,
+ maxlength,
+ required,
+ setReadonly
+ }) => {
+ let setRequired = (required) ? {required: `Data ${title} Harus di Isi`} : {}
+ let setMaxLength = (maxlength) ? {maxLength: {value: maxlength, message: `Maksimal ${maxlength} Karakter`}} : {}
+
+ let validateList = {...setRequired, ...setMaxLength}
+
+ return (
+ <>
+
+
+
+
+ {title} {(required) ? * :''}
+
+ {error[name] &&
{error[name]?.message}
}
+
+
+
+ >
+ );
+};
+
+export default InputText;
diff --git a/src/component/Input/InputTextarea.jsx b/src/component/Input/InputTextarea.jsx
new file mode 100644
index 0000000..df5caaf
--- /dev/null
+++ b/src/component/Input/InputTextarea.jsx
@@ -0,0 +1,36 @@
+const InputTextarea = ({
+ title,
+ name,
+ register,
+ error,
+ maxlength,
+ required,
+ setReadonly,
+ className,
+ ...props
+ }) => {
+ let setRequired = (required) ? {required: ` ${title} Harus di Isi`} : {}
+ let setMaxLength = (maxlength) ? {maxLength: {value: maxlength, message: `Maksimal ${maxlength} Karakter`}} : {}
+
+ let validateList = {...setRequired, ...setMaxLength}
+
+ return (
+ <>
+
+
+
+
+
+ {title} {(required) ? * :''}
+
+ {error[name] &&
{error[name]?.message}
}
+
+
+
+ >
+ );
+};
+
+export default InputTextarea;
diff --git a/src/component/Input/InputUsername.jsx b/src/component/Input/InputUsername.jsx
new file mode 100644
index 0000000..b4d3086
--- /dev/null
+++ b/src/component/Input/InputUsername.jsx
@@ -0,0 +1,49 @@
+const InputUsername = ({
+ title,
+ name,
+ register,
+ error,
+ placeholder,
+ maxlength,
+ minlength,
+ required,
+ setReadonly
+ }) => {
+ let setRequired = (required) ? {required: `${title} Harus di Isi`} : {}
+ let setMaxLength = (maxlength) ? {maxLength: {value: maxlength, message: `Maksimal ${maxlength} Karakter`}} : {}
+ let setMinLength = (minlength) ? {minLength: {value: minlength, message: `Minimal ${minlength} Karakter`}} : {}
+
+ let validateList = {...setRequired, ...setMaxLength, ...setMinLength}
+
+ const handleInput = (e) => {
+ e.target.value = e.target.value.replace(/[^a-zA-Z0-9_!@#$-%+=&]/g, "");
+ };
+ return (
+ <>
+
+
+
+
+ {title}
+
+ {error[name] &&
{error[name]?.message}
}
+
+
+
+ >
+ );
+};
+
+export default InputUsername;
+
\ No newline at end of file
diff --git a/src/component/Input/index.jsx b/src/component/Input/index.jsx
new file mode 100644
index 0000000..8313077
--- /dev/null
+++ b/src/component/Input/index.jsx
@@ -0,0 +1,27 @@
+import InputEmail from "./InputEmail";
+import InputImage from './InputImage';
+import InputNumber from './InputNumber';
+import InputPassword from "./InputPassword";
+import InputSelect from './InputSelect';
+import InputText from "./InputText";
+import InputTextarea from "./InputTextarea";
+import InputUsername from "./InputUsername";
+import InputDate from './InputDate';
+import InputDateRange from './InputDateRange';
+import InputPercentage from "./InputPercentage";
+
+const Input = {
+ Text: InputText,
+ Number: InputNumber,
+ Select: InputSelect,
+ Username: InputUsername,
+ Email: InputEmail,
+ Password: InputPassword,
+ Textarea: InputTextarea,
+ Image: InputImage,
+ DateRange: InputDateRange,
+ Date: InputDate,
+ Percentage: InputPercentage,
+}
+
+export default Input
diff --git a/src/app/component/LoadingPage.jsx b/src/component/LoadingPage.jsx
similarity index 100%
rename from src/app/component/LoadingPage.jsx
rename to src/component/LoadingPage.jsx
diff --git a/src/app/component/MainLayout.jsx b/src/component/MainLayout.jsx
similarity index 91%
rename from src/app/component/MainLayout.jsx
rename to src/component/MainLayout.jsx
index 67e6b9a..4030af4 100644
--- a/src/app/component/MainLayout.jsx
+++ b/src/component/MainLayout.jsx
@@ -1,4 +1,4 @@
-import LoadingPage from "@/app/component/LoadingPage";
+import LoadingPage from "@/component/LoadingPage";
import {motion} from "framer-motion"
export default function MainLayout({children}) {
return(
diff --git a/src/app/component/NoData.jsx b/src/component/NoData.jsx
similarity index 100%
rename from src/app/component/NoData.jsx
rename to src/component/NoData.jsx
diff --git a/src/component/TransitionContent.jsx b/src/component/TransitionContent.jsx
new file mode 100644
index 0000000..15afd79
--- /dev/null
+++ b/src/component/TransitionContent.jsx
@@ -0,0 +1,20 @@
+
+import {motion} from "framer-motion";
+
+export default function TransitionContent({children}) {
+ return(
+ {children}
+ )
+}
\ No newline at end of file
diff --git a/src/lib/API.jsx b/src/lib/API.jsx
new file mode 100644
index 0000000..4079981
--- /dev/null
+++ b/src/lib/API.jsx
@@ -0,0 +1,37 @@
+// const baseUrlNews = process.env.NEXT_PUBLIC_API_URL_NEWS;
+const baseUrlNews = process.env.NEXT_PUBLIC_API_URL_NEWS_NEWDATA;
+
+const keyNews = 'ec08743d94634489a99f938524798910'
+const key2='157a21339fd4a2433c1f23b79f63696c'
+const keyNewdata = 'pub_50761888ae4caa07368a73121b66b5bc50110'
+
+export const API = {
+ GET_NEWS,
+};
+
+async function GET_NEWS(url) {
+ try {
+ const res = await fetch(baseUrlNews+url, {
+ method: 'GET',
+ headers:{
+ "X-ACCESS-KEY" : keyNewdata
+ }
+ })
+
+ const result = await res.json()
+
+ return {
+ status: res.status,
+ statusText: res.statusText,
+ result: result
+ }
+ } catch (res) {
+ return {
+ status: 500,
+ statusText: "Error API Connection ",
+ result: {
+ message :"Error API Connection"
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/lib/Helper.jsx b/src/lib/Helper.jsx
new file mode 100644
index 0000000..88b1677
--- /dev/null
+++ b/src/lib/Helper.jsx
@@ -0,0 +1,185 @@
+export const Helper = {
+ today,
+ formatDate,
+ numFormatClear,
+ numFormat,
+ numFormatDec,
+ getDifferenceDate,
+ limitString
+}
+
+function today() {
+ var date = new Date();
+ var year = date.getFullYear();
+ return year + "-" + ("0" + (date.getMonth() + 1)).slice(-2) + "-" + ("0" + date.getDate()).slice(-2);
+}
+
+
+function formatDate(dates, type) {
+ var date = new Date(dates);
+ var monthNames = [
+ "Januari",
+ "Februari",
+ "Maret",
+ "April",
+ "Mei",
+ "Juni",
+ "Juli",
+ "Agustus",
+ "September",
+ "Oktober",
+ "November",
+ "Desember",
+ ];
+
+ var monthNamesSmall = [
+ "Jan",
+ "Feb",
+ "Mar",
+ "Apr",
+ "Mei",
+ "Jun",
+ "Jul",
+ "Ags",
+ "Sep",
+ "Okt",
+ "Nov",
+ "Des",
+ ];
+
+ var day = date.getDate();
+ var monthIndex = date.getMonth();
+ var year = date.getFullYear();
+ var hour = date.getHours();
+ var minute = date.getMinutes();
+ var sec = date.getSeconds();
+
+ switch (type) {
+ case "d":
+ return day;
+ case "m":
+ return monthIndex + 1;
+ case "mm":
+ return monthNamesSmall[monthIndex];
+ case "M":
+ return monthNames[monthIndex];
+ case "Y":
+ return year;
+ case 'd-mm-Y':
+ return day + ' ' + monthNamesSmall[(monthIndex)] + ' ' + year;
+ case "d-m-Y":
+ return day + "-" + (monthIndex + 1) + "-" + year;
+ case "Y-m-d":
+ return year + "-" + (monthIndex + 1) + "-" + day;
+ case "d-M-Y":
+ return day + " " + monthNames[monthIndex] + " " + year;
+ case "d-m-Y H:i:s":
+ return (
+ day +
+ "-" +
+ (monthIndex + 1) +
+ "-" +
+ year +
+ ", " +
+ hour +
+ ":" +
+ minute +
+ ":" +
+ sec
+ );
+ case "d-M-Y H:i:s":
+ return (
+ day +
+ " " +
+ monthNames[monthIndex] +
+ " " +
+ year +
+ ", " +
+ hour +
+ ":" +
+ minute +
+ ":" +
+ sec
+ );
+
+ case "dMYHis":
+ return (
+ day + monthIndex + year + hour + minute + sec
+ );
+ }
+}
+
+
+function getDifferenceDate(dateA, dateB) {
+
+ const date1 = new Date(dateA);
+ const date2 = new Date(dateB);
+ // Convert both dates to timestamps
+ const timestamp1 = date1.getTime();
+ const timestamp2 = date2.getTime();
+
+ // Calculate the difference in milliseconds
+ let diffInMs = Math.abs(timestamp2 - timestamp1);
+
+ // Calculate days
+ const days = Math.floor(diffInMs / (1000 * 60 * 60 * 24));
+ diffInMs -= days * 1000 * 60 * 60 * 24;
+
+ // Calculate hours
+ const hours = Math.floor(diffInMs / (1000 * 60 * 60));
+ diffInMs -= hours * 1000 * 60 * 60;
+
+ // Calculate minutes
+ const minutes = Math.floor(diffInMs / (1000 * 60));
+ diffInMs -= minutes * 1000 * 60;
+
+ return `${hours} jam yang lalu`;
+}
+
+function numFormatClear(data) {
+ return data.replace(/\./g, "");
+}
+
+function limitString(text, limit) {
+ return text.length > limit ? text.substring(0, limit) + "..." : text;
+}
+
+function numFormatDec(amount, decimalCount = 2, decimal = ",", thousands = ".") {
+ try {
+ decimalCount = Math.abs(decimalCount);
+ decimalCount = isNaN(decimalCount) ? 2 : decimalCount;
+
+ const negativeSign = amount < 0 ? "-" : "";
+
+ let i = parseInt(amount = Math.abs(Number(amount) || 0).toFixed(decimalCount)).toString();
+ let j = (i.length > 3) ? i.length % 3 : 0;
+
+ return negativeSign + (j ? i.substr(0, j) + thousands : '') + i.substr(j).replace(/(\d{3})(?=\d)/g, "$1" + thousands) + (decimalCount ? decimal + Math.abs(amount - i).toFixed(decimalCount).slice(2) : "");
+ } catch (e) {
+ console.log(e)
+ }
+}
+
+function numFormat(amount, decimalCount = 0, decimal = ",", thousands = ".") {
+ // try {
+ // console.log(value)
+ // let val = (value / 1).toFixed(0).replace(".", ",");
+ // return val.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ".");
+ // }catch (e) {
+ // console.log(e)
+ // }
+
+ try {
+ decimalCount = Math.abs(decimalCount);
+ decimalCount = isNaN(decimalCount) ? 2 : decimalCount;
+
+ const negativeSign = amount < 0 ? "-" : "";
+
+ let i = parseInt(amount = Math.abs(Number(amount) || 0).toFixed(decimalCount)).toString();
+ let j = (i.length > 3) ? i.length % 3 : 0;
+
+ return negativeSign + (j ? i.substr(0, j) + thousands : '') + i.substr(j).replace(/(\d{3})(?=\d)/g, "$1" + thousands) + (decimalCount ? decimal + Math.abs(amount - i).toFixed(decimalCount).slice(2) : "");
+ } catch (e) {
+ console.log(e)
+ }
+}
\ No newline at end of file