betang payment

This commit is contained in:
Zamzam Nurzaman 2024-08-14 17:04:10 +07:00
parent 26f66f2e20
commit a26c9ba300
79 changed files with 2526 additions and 528 deletions

0
.env Normal file
View File

4
.env.development Normal file
View File

@ -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'

4
.env.production Normal file
View File

@ -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'

View File

@ -1,3 +1,7 @@
{ {
"extends": "next/core-web-vitals" "extends": "next",
} "rules": {
"react/no-unescaped-entities": "off",
"@next/next/no-page-custom-font": "off"
}
}

4
.idea/watcherTasks.xml Normal file
View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectTasksOptions" suppressed-tasks="SCSS" />
</project>

View File

@ -1,5 +1,6 @@
/** @type {import('next').NextConfig} */ /** @type {import('next').NextConfig} */
const nextConfig = { const nextConfig = {
reactStrictMode: false,
output: 'export', output: 'export',
images: { images: {
loader: "custom", loader: "custom",

147
package-lock.json generated
View File

@ -15,7 +15,9 @@
"next-image-export-optimizer": "^1.12.3", "next-image-export-optimizer": "^1.12.3",
"nextjs-toploader": "^1.6.6", "nextjs-toploader": "^1.6.6",
"react": "^18", "react": "^18",
"react-dom": "^18" "react-dom": "^18",
"react-hook-form": "^7.52.2",
"sass": "^1.77.8"
}, },
"devDependencies": { "devDependencies": {
"eslint": "^8", "eslint": "^8",
@ -1348,6 +1350,18 @@
"react-dom": ">=16.9.0" "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": { "node_modules/argparse": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
@ -1572,6 +1586,17 @@
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
"dev": true "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": { "node_modules/brace-expansion": {
"version": "1.1.11", "version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@ -1586,7 +1611,6 @@
"version": "3.0.3", "version": "3.0.3",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
"dev": true,
"dependencies": { "dependencies": {
"fill-range": "^7.1.1" "fill-range": "^7.1.1"
}, },
@ -1668,6 +1692,40 @@
"url": "https://github.com/chalk/chalk?sponsor=1" "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": { "node_modules/classnames": {
"version": "2.5.1", "version": "2.5.1",
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz",
@ -2663,7 +2721,6 @@
"version": "7.1.1", "version": "7.1.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
"dev": true,
"dependencies": { "dependencies": {
"to-regex-range": "^5.0.1" "to-regex-range": "^5.0.1"
}, },
@ -2761,6 +2818,19 @@
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
"dev": true "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": { "node_modules/function-bind": {
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
@ -3067,6 +3137,11 @@
"node": ">= 4" "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": { "node_modules/import-fresh": {
"version": "3.3.0", "version": "3.3.0",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
@ -3187,6 +3262,17 @@
"url": "https://github.com/sponsors/ljharb" "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": { "node_modules/is-boolean-object": {
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz",
@ -3264,7 +3350,6 @@
"version": "2.1.1", "version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
"dev": true,
"engines": { "engines": {
"node": ">=0.10.0" "node": ">=0.10.0"
} }
@ -3308,7 +3393,6 @@
"version": "4.0.3", "version": "4.0.3",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
"dev": true,
"dependencies": { "dependencies": {
"is-extglob": "^2.1.1" "is-extglob": "^2.1.1"
}, },
@ -3344,7 +3428,6 @@
"version": "7.0.0", "version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
"dev": true,
"engines": { "engines": {
"node": ">=0.12.0" "node": ">=0.12.0"
} }
@ -3859,6 +3942,14 @@
"react-dom": ">= 16.0.0" "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": { "node_modules/nprogress": {
"version": "0.2.0", "version": "0.2.0",
"resolved": "https://registry.npmjs.org/nprogress/-/nprogress-0.2.0.tgz", "resolved": "https://registry.npmjs.org/nprogress/-/nprogress-0.2.0.tgz",
@ -4125,7 +4216,6 @@
"version": "2.3.1", "version": "2.3.1",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
"dev": true,
"engines": { "engines": {
"node": ">=8.6" "node": ">=8.6"
}, },
@ -4817,11 +4907,37 @@
"react": "^18.3.1" "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": { "node_modules/react-is": {
"version": "16.13.1", "version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" "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": { "node_modules/reflect.getprototypeof": {
"version": "1.0.6", "version": "1.0.6",
"resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.6.tgz", "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.6.tgz",
@ -5011,6 +5127,22 @@
"url": "https://github.com/sponsors/ljharb" "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": { "node_modules/scheduler": {
"version": "0.23.2", "version": "0.23.2",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz",
@ -5493,7 +5625,6 @@
"version": "5.0.1", "version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
"dev": true,
"dependencies": { "dependencies": {
"is-number": "^7.0.0" "is-number": "^7.0.0"
}, },

View File

@ -5,6 +5,7 @@
"scripts": { "scripts": {
"dev": "next dev", "dev": "next dev",
"build": "next build && next-image-export-optimizer", "build": "next build && next-image-export-optimizer",
"build:dev": "NODE_ENV=development next build",
"start": "next start", "start": "next start",
"lint": "next lint", "lint": "next lint",
"dev-export": "next dev -H 0.0.0.0 -p 3000", "dev-export": "next dev -H 0.0.0.0 -p 3000",
@ -13,12 +14,14 @@
"dependencies": { "dependencies": {
"@ant-design/icons": "^5.4.0", "@ant-design/icons": "^5.4.0",
"antd": "^5.20.0", "antd": "^5.20.0",
"framer-motion": "^10.16.4",
"next": "14.2.5", "next": "14.2.5",
"react": "^18",
"react-dom": "^18",
"next-image-export-optimizer": "^1.12.3", "next-image-export-optimizer": "^1.12.3",
"nextjs-toploader": "^1.6.6", "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": { "devDependencies": {
"eslint": "^8", "eslint": "^8",

307
public/css/form.css Normal file
View File

@ -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;
}

View File

@ -38,6 +38,8 @@
} }
@import "form.css";
::-webkit-scrollbar { ::-webkit-scrollbar {
width: 5px; width: 5px;
height: 14px; height: 14px;
@ -111,6 +113,12 @@ button, input {
padding: 10px; padding: 10px;
} }
.btn-dark{
background: #ffffff;
color: #59b8b6;
border: 1px solid #59b8b6;
}
.title-wrap { .title-wrap {
position: relative; position: relative;
display: flex; display: flex;
@ -143,7 +151,7 @@ button, input {
.no-data{ .no-data{
text-align: center; text-align: center;
padding: 30px 20px; padding: 30px 20px;
background: #f5f5f5; background: #f5f5f58f;
width: 100%; width: 100%;
height: 100%; height: 100%;
border-radius: 20px; border-radius: 20px;
@ -173,7 +181,7 @@ button, input {
/*color: #fff;*/ /*color: #fff;*/
/*background: var(--primary-gradient);*/ /*background: var(--primary-gradient);*/
color: var(--primary); color: var(--primary);
background: #f5f5f5; background: #f5f5f58f;
padding: 10px 15px; padding: 10px 15px;
margin-bottom: -12px; margin-bottom: -12px;
border-radius: 20px 20px 0 0; border-radius: 20px 20px 0 0;
@ -201,7 +209,7 @@ button, input {
.ant-tabs-content-holder { .ant-tabs-content-holder {
/*background: var(--primary-gradient);*/ /*background: var(--primary-gradient);*/
background: #f5f5f5; background: #f5f5f58f;
padding: 20px; padding: 20px;
border-radius: 0 20px 20px 20px; border-radius: 0 20px 20px 20px;
} }
@ -285,23 +293,26 @@ button, input {
.search { .search {
position: absolute; position: absolute;
left: 10px; left: 15px;
top:10px; top:10px;
z-index: 200; z-index: 200;
width: 70%; width: 61%;
.form { .form {
border-radius: 30px; border-radius: 30px;
border: 1px solid #fff; 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 { .ant-input-affix-wrapper > textarea.ant-input {
font-weight: 600 !important; font-weight: 400 !important;
color: #353535 !important; color: #353535 !important;
font-size: 12px;
} }
.anticon svg { .anticon
svg {
fill: var(--primary); fill: var(--primary);
margin-right: 10px; margin-right: 10px;
} }
@ -312,14 +323,13 @@ button, input {
.header { .header {
background: linear-gradient(to bottom, #49B1B5, #149A9F); background: linear-gradient(to bottom, #49B1B5, #149A9F);
height: 200px; height: 160px;
width: 100%; width: 100%;
color: #fff; color: #fff;
position: relative; position: relative;
display: flex; display: flex;
justify-content: center; /* Horizontally center */ justify-content: center; /* Horizontally center */
align-items: center; /* Vertically center */ align-items: center; /* Vertically center */
/*border-radius: 0 0 50px 50px;*/ /*border-radius: 0 0 50px 50px;*/
&.hidden { &.hidden {
@ -337,10 +347,10 @@ button, input {
width: 100% !important; width: 100% !important;
left: 0 !important; left: 0 !important;
bottom: 0px !important; bottom: 0px !important;
top: 200px !important; top: 145px !important;
border: none !important; border: none !important;
z-index: 1; z-index: 1;
height: unset !important; height: 90px !important;
} }
@ -353,16 +363,36 @@ button, input {
} }
.logo { .logo {
margin-top: 80px; text-align: center;
img { img {
width: 300px; width: 30px !important;
margin-bottom: -30px;
margin-right: -99px;
} }
.title { .title {
font-weight: 400; font-weight: 400;
text-transform: capitalize; 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 { .top-btn {
@ -385,10 +415,10 @@ button, input {
} }
.location { .location {
margin-top: 10px;
position: absolute; position: absolute;
top: 5px; z-index: 111;
left: 20px;
.title { .title {
font-size: 12px; font-size: 12px;
@ -396,49 +426,76 @@ button, input {
} }
.value { .value {
font-size: 12px; font-size: 11px;
font-weight: 400; 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 { .search {
margin-top: 7px; padding: 0px 79px;
padding: 0 40px; position: absolute;
z-index: 200; z-index: 200;
.form { .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 { .ant-input-affix-wrapper > textarea.ant-input {
font-weight: 600 !important; font-weight: 500 !important;
color: #353535 !important; color: #353535 !important;
font-size: 12px;
} }
.anticon svg { .anticon
svg {
fill: var(--primary); fill: var(--primary);
margin-right: 10px; margin-right: 10px;
} }
;
bottom: -23px;
width: 100%;
left: 0;
right: 0;
} }
} }
.omnichannel { .omnichannel {
margin-top: 120px; margin-top: 100px;
padding: 0 25px; padding: 0 25px;
} }
.card-omni { .card-omni {
/*background: #fff;*/ /* background: #fff; */
background: linear-gradient(to bottom, #fff, #f3feff); background: linear-gradient(to bottom, #fff, #f3feff);
border-radius: 20px; border-radius: 15px;
text-align: center; text-align: center;
border: 1px solid #eeeeee; /*border: 1px solid #eeeeee;*/
padding: 10px 10px 20px; padding: 10px 10px 15px;
/*box-shadow: 9px 10px 18px #d7d7d7;*/ box-shadow: 9px 10px 18px #d7d7d75e;
.icon { .icon {
img { img {
@ -455,7 +512,9 @@ button, input {
height: 30px; height: 30px;
overflow: hidden; overflow: hidden;
} }
;
display: block;
text-decoration: none;
} }
.news { .news {
@ -541,9 +600,11 @@ button, input {
margin-bottom: 10px; margin-bottom: 10px;
display: flex; display: flex;
gap: 15px; gap: 15px;
background: #f5f5f5; background: #f5f5f58f;
padding: 10px 10px 5px; padding: 10px 10px 5px;
border-radius: 15px; border-radius: 15px;
text-decoration: none;
color: inherit;
.image { .image {
border-radius: 10px; border-radius: 10px;
@ -591,4 +652,139 @@ button, input {
color: #fff; color: #fff;
} }
} }
.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;
}

BIN
public/image/layer3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

BIN
public/image/logodummy.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 380 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 598 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 874 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 152 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 194 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 262 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 198 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 368 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 916 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -5,7 +5,9 @@
"image/grouplogo.png": "nVetjZphqR92a3EwnYIlVQcTWHXwzxEeUsfFrZ59W9g=", "image/grouplogo.png": "nVetjZphqR92a3EwnYIlVQcTWHXwzxEeUsfFrZ59W9g=",
"image/layer1.png": "QTEGhbVeWDTZQ+JWYULiLkETaWi0ybiI78gEMC-QL40=", "image/layer1.png": "QTEGhbVeWDTZQ+JWYULiLkETaWi0ybiI78gEMC-QL40=",
"image/layer2.png": "LfddDhf5OGLWosggG0KrRuBq5afq-1sLUBLYpRgryAk=", "image/layer2.png": "LfddDhf5OGLWosggG0KrRuBq5afq-1sLUBLYpRgryAk=",
"image/layer3.png": "a05J2QHjZFTOhXBnlUDVYsXMGxuTVma0aR1skHSj37Y=",
"image/logo.png": "guyrxmHxHTV4YYqWnO5sapp8aB6Gr2jARr5b-eA-Jao=", "image/logo.png": "guyrxmHxHTV4YYqWnO5sapp8aB6Gr2jARr5b-eA-Jao=",
"image/logodummy.png": "dVLj+C82snmcMrm1xUqwzw8zmXmSSGJtOKiaOaFqspo=",
"image/logonew.png": "zLpohFmIYCjwIAZ7n-egh6QTt9zue1N-d-69M8Fnses=", "image/logonew.png": "zLpohFmIYCjwIAZ7n-egh6QTt9zue1N-d-69M8Fnses=",
"image/memphis-colorful.webp": "fHLKCUhSbSismaq1wl2nOnpiuWVohmsMEMX-H8z5Sro=", "image/memphis-colorful.webp": "fHLKCUhSbSismaq1wl2nOnpiuWVohmsMEMX-H8z5Sro=",
"image/sports.webp": "n2DZ2RqxVBlVqJ6uPWYlSVPgBPbrdUUdNGYU3uFlnuU=", "image/sports.webp": "n2DZ2RqxVBlVqJ6uPWYlSVPgBPbrdUUdNGYU3uFlnuU=",

View File

@ -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) => <span>{Helper.numFormat(text)}</span>,
},
{
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(
<>
<TransitionContent>
{contextHolder}
<div className={"detail-va"}>
<table>
<tbody>
<tr>
<td className={"title"}>Nama Institusi</td>
<td className={"value"}>: {dataDetail?.institusi}</td>
</tr>
<tr>
<td className={"title"}>Nama Lengkap</td>
<td className={"value"}>: {dataDetail?.nama}</td>
</tr>
<tr>
<td className={"title"}>Kelas</td>
<td className={"value"}>: {dataDetail?.kelas}</td>
</tr>
<tr>
<td className={"title"}>Nomor VA</td>
<td className={"value"}>: {dataDetail?.nomorVA}</td>
</tr>
</tbody>
</table>
<hr className={"border"}/>
<Table
rowSelection={{
type: 'checkbox',
...rowSelection,
}}
columns={columns}
dataSource={data}
rowKey={"id"}
pagination={false}
/>
<div style={{display:'flex',gap:'10px',marginTop:'30px'}}>
{/*<button className={"button btn-full btn-dark"}>Batal</button>*/}
<button onClick={actPayment} className={"button btn-full"}>Bayar</button>
</div>
</div>
</TransitionContent>
<Modal open={isModalOpen} closeIcon={false} footer={null} >
<div className={"modal-payment"}>
<div className={"title"}>Detail Pembayaran</div>
<div className={"sub-title"}>Daftar Produk yang akan dibayarkan</div>
</div>
<div className={"detail-va"} style={{marginTop:'20px'}}>
<table>
<tbody>
<tr>
<td className={"title"}>Nama Institusi</td>
<td className={"value"}>: {dataDetail?.institusi}</td>
</tr>
<tr>
<td className={"title"}>Nama Lengkap</td>
<td className={"value"}>: {dataDetail?.nama}</td>
</tr>
<tr>
<td className={"title"}>Kelas</td>
<td className={"value"}>: {dataDetail?.kelas}</td>
</tr>
<tr>
<td className={"title"}>Total Tagihan</td>
<td className={"value"}>: {Helper.numFormat(selectedRow.reduce((sum, current) => sum + current.tagihan, 0))}</td>
</tr>
</tbody>
</table>
<table className={"zn-table"} style={{marginTop:'30px'}}>
<thead>
<tr>
<th>Keterangan</th>
<th style={{textAlign:'right'}}>Tagihan</th>
</tr>
</thead>
<tbody>
{selectedRow?.map((v,k) => (
<tr key={k}>
<td>{v.namaProduk}</td>
<td style={{textAlign:'right'}}>{Helper.numFormat(v.tagihan)}</td>
</tr>
))}
</tbody>
</table>
<div className={"notes"}>Apakah data yang dipilih sudah benar ? pastikan cek data terlebih dahulu dan klik tombol bayar untuk melakukan pembayaran</div>
<div style={{display:'flex',gap:'10px',marginTop:'30px'}}>
<button onClick={()=>setIsModalOpen(false)} className={"button btn-full btn-dark"}>Batal</button>
<button onClick={actSendPayment} className={"button btn-full"}>Bayar</button>
</div>
</div>
</Modal>
<Modal open={modalPin} closeIcon={false} footer={null}>
{(statusPIN) ?
<TransitionContent>
<div>
<div className={"modal-payment"} style={{textAlign:"center"}}>
<div className={"title"}>BERHASIL</div>
<div className={"sub-title"} style={{marginTop:'10px'}}>Transaksi Berhasil, silahkan lakukan pembayaran <br/> melalui <b>Mobile Banking</b> </div>
<a href={"#"} className={"button btn-full"} style={{marginTop:'20px'}}>Buka Mobile Banking</a>
</div>
</div>
</TransitionContent>
:
<TransitionContent>
<div>
<div className={"modal-payment"} style={{textAlign:"center"}}>
<div className={"title"}>PIN SMS</div>
<div className={"sub-title"}>Masukan PIN SMS anda</div>
</div>
<div style={{marginTop:'30px'}}>
<Input.OTP variant={"filled"} size={"large"} value={pin} type={"number"} length={6} onKeyPress={(event) => {
if (!/[0-9]/.test(event.key)) {
event.preventDefault();
}
}} onChange={(text)=>setPin(text)} />
</div>
<div style={{display:'flex',gap:'10px',marginTop:'30px'}}>
<button onClick={()=>setModalPin(false)} className={"button btn-full btn-dark"}>Batal</button>
<button onClick={sendPin} className={"button btn-full"}>OK</button>
</div>
</div>
</TransitionContent>
}
</Modal>
</>
)
}

View File

@ -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(
<>
<motion.div
className={"animate-box"}
initial={{ opacity: 0, y: 0 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: 0 }}
transition={{
duration: 1,
ease: "easeInOut",
times: [0, 0.5, 1],
loop: Infinity,
repeatDelay: 1
}}
>
<Input.Number
title={'Nomor VA'}
name={'nomorVA'}
minlength={3}
maxlength={20}
setReadonly={false}
required={true}
register={register}
error={errors}
/>
<button onClick={handleSubmit(submitVA)} className={"button btn-full"}>Inquiry No VA</button>
</motion.div>
</>
)
}

View File

@ -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 (<>
<MainLayout>
<section className={`header-small`} style={{background: "var(--primary-gradient)", backdropFilter: "blur(8px)"}}>
<div className={"top-btn close"}>
<CloseOutlined style={{marginTop: '7px'}}/>
</div>
<div className={"search"} style={{marginLeft: '50px', marginTop: '4px', fontSize: '13px', fontWeight: '600'}}>
Betang Payment
</div>
<Link href={"/"} className={"top-btn back"} style={{color:'#fff'}}>
<LeftOutlined style={{marginTop: '7px'}}/>
</Link>
</section>
<section style={{height: "100vh", padding: '80px 30px'}}>
{(statusVA) ?
<DetailVA/>
:
<InquiryVA sendNoVA={sendNoVA}/>
}
</section>
</MainLayout>
</>)
}

View File

@ -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 (<Row gutter={[10, 10]}>
<Col span={6}>
<div className={"card-omni"}>
<div className={"icon"}>
<ExportedImage alt={"card omni"} className={"image-export"} src={"/logo/garuda.png"} fill={true} />
</div>
<div className={"title"}>Garuda Indonesia</div>
</div>
</Col>
<Col span={6}>
<div className={"card-omni"}>
<div className={"icon"}>
<ExportedImage alt={"card omni"} className={"image-export"} src={"/logo/traveloka.png"} fill={true} />
</div>
<div className={"title"}>Traveloka</div>
</div>
</Col>
<Col span={6}>
<div className={"card-omni"}>
<div className={"icon"}>
<ExportedImage alt={"card omni"} className={"image-export"} src={"/logo/woos.png"} fill={true} />
</div>
<div className={"title"}>Woosh</div>
</div>
</Col>
<Col span={6}>
<div className={"card-omni"}>
<div className={"icon"}>
<ExportedImage alt={"card omni"} className={"image-export"} src={"/logo/kai.png"} fill={true} />
</div>
<div className={"title"}>KAI</div>
</div>
</Col>
<Col span={6}>
<div className={"card-omni"}>
<div className={"icon"}>
<ExportedImage alt={"card omni"} className={"image-export"} src={"/logo/garuda.png"} fill={true} />
</div>
<div className={"title"}>Garuda Indonesia</div>
</div>
</Col>
<Col span={6}>
<div className={"card-omni"}>
<div className={"icon"}>
<ExportedImage alt={"card omni"} className={"image-export"} src={"/logo/traveloka.png"} fill={true} />
</div>
<div className={"title"}>Traveloka</div>
</div>
</Col>
<Col span={6}>
<div className={"card-omni"}>
<div className={"icon"}>
<ExportedImage alt={"card omni"} className={"image-export"} src={"/logo/woos.png"} fill={true} />
</div>
<div className={"title"}>Woosh</div>
</div>
</Col>
<Col span={6}>
<div className={"card-omni"}>
<div className={"icon"}>
<ExportedImage alt={"card omni"} className={"image-export"} src={"/logo/kai.png"} fill={true} />
</div>
<div className={"title"}>KAI</div>
</div>
</Col>
<Col span={6}>
<div className={"card-omni"}>
<div className={"icon"}>
<ExportedImage alt={"card omni"} className={"image-export"} src={"/logo/garuda.png"} fill={true} />
</div>
<div className={"title"}>Garuda Indonesia</div>
</div>
</Col>
<Col span={6}>
<div className={"card-omni"}>
<div className={"icon"}>
<ExportedImage alt={"card omni"} className={"image-export"} src={"/logo/traveloka.png"} fill={true} />
</div>
<div className={"title"}>Traveloka</div>
</div>
</Col>
<Col span={6}>
<div className={"card-omni"}>
<div className={"icon"}>
<ExportedImage alt={"card omni"} className={"image-export"} src={"/logo/woos.png"} fill={true} />
</div>
<div className={"title"}>Woosh</div>
</div>
</Col>
<Col span={6}>
<div className={"card-omni"}>
<div className={"icon"}>
<ExportedImage alt={"card omni"} className={"image-export"} src={"/logo/kai.png"} fill={true} />
</div>
<div className={"title"}>KAI</div>
</div>
</Col>
<Col span={6}>
<div className={"card-omni"}>
<div className={"icon"}>
<ExportedImage alt={"card omni"} className={"image-export"} src={"/logo/garuda.png"} fill={true} />
</div>
<div className={"title"}>Garuda Indonesia</div>
</div>
</Col>
<Col span={6}>
<div className={"card-omni"}>
<div className={"icon"}>
<ExportedImage alt={"card omni"} className={"image-export"} src={"/logo/traveloka.png"} fill={true} />
</div>
<div className={"title"}>Traveloka</div>
</div>
</Col>
<Col span={6}>
<div className={"card-omni"}>
<div className={"icon"}>
<ExportedImage alt={"card omni"} className={"image-export"} src={"/logo/woos.png"} fill={true} />
</div>
<div className={"title"}>Woosh</div>
</div>
</Col>
<Col span={6}>
<div className={"card-omni"}>
<div className={"icon"}>
<ExportedImage alt={"card omni"} className={"image-export"} src={"/logo/kai.png"} fill={true} />
</div>
<div className={"title"}>KAI</div>
</div>
</Col>
</Row>
)
}
const items = [
{
key: '1', label: 'Fashion & Kecantikan ', children: <CardOmni/>,
}, {
key: '2', label: 'Rekreasi', children: <NoData/>,
}, {
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(
<MainLayout>
<HeaderSmallDetail/>
<section className={"omnichannel"}>
<div className={"list"}>
<Tabs defaultActiveKey="1" items={items} onChange={onChange}/>
</div>
</section>
<ExportedImage alt={"layer bottom"} className={"layer-bottom"} src={"/image/layer2.png"} fill={true} />
</MainLayout>
)
}

View File

@ -1,164 +1,57 @@
"use client" "use client"
import HeaderSmallDetail from "@/app/component/HeaderSmallDetail"; import MainLayout from "@/component/MainLayout";
import {Tabs} from "antd";
import NoData from "@/app/component/NoData";
import MainLayout from "@/app/component/MainLayout";
import ExportedImage from "next-image-export-optimizer"; 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 = () => { useEffect(() => {
return (<> getDataDetail()
<div className={"list-news"}> }, []);
<div className={"content-list-news"}>
<div>
<ExportedImage alt={"berita"} className={"image-export image"} src={"/berita/berita.jpeg"} fill={true} />
</div>
<div>
<div className={"category"}>Olahraga</div>
<div className={"title"}>Chelsea Keropos, Lini Belakang Jadi...</div>
<div className={"subtitle"}><span className={"sumber"}>Detik.com</span> | 30 Menit yang lalu</div>
</div>
</div>
<div className={"content-list-news"}>
<div>
<ExportedImage alt={"berita"} className={"image-export image"} src={"/berita/berita.jpeg"} fill={true} />
</div>
<div>
<div className={"category"}>Olahraga</div>
<div className={"title"}>Chelsea Keropos, Lini Belakang Jadi...</div>
<div className={"subtitle"}><span className={"sumber"}>Detik.com</span> | 30 Menit yang lalu</div>
</div>
</div>
<div className={"content-list-news"}>
<div>
<ExportedImage alt={"berita"} className={"image-export image"} src={"/berita/berita.jpeg"} fill={true} />
</div>
<div>
<div className={"category"}>Olahraga</div>
<div className={"title"}>Chelsea Keropos, Lini Belakang Jadi...</div>
<div className={"subtitle"}><span className={"sumber"}>Detik.com</span> | 30 Menit yang lalu</div>
</div>
</div>
<div className={"content-list-news"}>
<div>
<ExportedImage alt={"berita"} className={"image-export image"} src={"/berita/berita.jpeg"} fill={true} />
</div>
<div>
<div className={"category"}>Olahraga</div>
<div className={"title"}>Chelsea Keropos, Lini Belakang Jadi...</div>
<div className={"subtitle"}><span className={"sumber"}>Detik.com</span> | 30 Menit yang lalu</div>
</div>
</div>
<div className={"content-list-news"}>
<div>
<ExportedImage alt={"berita"} className={"image-export image"} src={"/berita/berita.jpeg"} fill={true} />
</div>
<div>
<div className={"category"}>Olahraga</div>
<div className={"title"}>Chelsea Keropos, Lini Belakang Jadi...</div>
<div className={"subtitle"}><span className={"sumber"}>Detik.com</span> | 30 Menit yang lalu</div>
</div>
</div>
<div className={"content-list-news"}>
<div>
<ExportedImage alt={"berita"} className={"image-export image"} src={"/berita/berita.jpeg"} fill={true} />
</div>
<div>
<div className={"category"}>Olahraga</div>
<div className={"title"}>Chelsea Keropos, Lini Belakang Jadi...</div>
<div className={"subtitle"}><span className={"sumber"}>Detik.com</span> | 30 Menit yang lalu</div>
</div>
</div>
<div className={"content-list-news"}>
<div>
<ExportedImage alt={"berita"} className={"image-export image"} src={"/berita/berita.jpeg"} fill={true} />
</div>
<div>
<div className={"category"}>Olahraga</div>
<div className={"title"}>Chelsea Keropos, Lini Belakang Jadi...</div>
<div className={"subtitle"}><span className={"sumber"}>Detik.com</span> | 30 Menit yang lalu</div>
</div>
</div>
<div className={"content-list-news"}>
<div>
<ExportedImage alt={"berita"} className={"image-export image"} src={"/berita/berita.jpeg"} fill={true} />
</div>
<div>
<div className={"category"}>Olahraga</div>
<div className={"title"}>Chelsea Keropos, Lini Belakang Jadi...</div>
<div className={"subtitle"}><span className={"sumber"}>Detik.com</span> | 30 Menit yang lalu</div>
</div>
</div>
<div className={"content-list-news"}>
<div>
<ExportedImage alt={"berita"} className={"image-export image"} src={"/berita/berita.jpeg"} fill={true} />
</div>
<div>
<div className={"category"}>Olahraga</div>
<div className={"title"}>Chelsea Keropos, Lini Belakang Jadi...</div>
<div className={"subtitle"}><span className={"sumber"}>Detik.com</span> | 30 Menit yang lalu</div>
</div>
</div>
<div className={"content-list-news"}>
<div>
<ExportedImage alt={"berita"} className={"image-export image"} src={"/berita/berita.jpeg"} fill={true} />
</div>
<div>
<div className={"category"}>Olahraga</div>
<div className={"title"}>Chelsea Keropos, Lini Belakang Jadi...</div>
<div className={"subtitle"}><span className={"sumber"}>Detik.com</span> | 30 Menit yang lalu</div>
</div>
</div>
</div>
</>)
}
const items = [{
key: '1', label: 'Semua ', children: <ListBerita/>,
}, {
key: '2', label: 'Olahraga', children: <NoData/>,
}, {
key: '3', label: 'Dunia', children: <NoData/>,
}, {
key: '4', label: 'Ekonomi', children: <NoData/>,
}, {
key: '5', label: 'Keluarga', children: <NoData/>,
}, {
key: '6', label: 'Politik', children: <NoData/>,
},
];
return (<MainLayout> return (<MainLayout>
<HeaderSmallDetail/> <section className={`header-small`} style={{background: "#ffffff75", backdropFilter: "blur(8px)"}}>
<div className={"top-btn close"}>
<section className={"news"} style={{paddingBottom: '100px'}}> <CloseOutlined style={{marginTop: '7px', color: '#000'}}/>
<div className={"list"} style={{marginTop: '100px'}}>
<Tabs defaultActiveKey="1" items={items} onChange={onChange}/>
</div> </div>
<div className={"search"} style={{marginLeft: '50px', marginTop: '4px', color: '#000', fontSize: '13px', fontWeight: '600'}}>
{dataDetail?.source_name}
</div>
<Link href={"/"} className={"top-btn back"}>
<LeftOutlined style={{marginTop: '7px', color: '#000'}}/>
</Link>
</section>
<section className={"detail-berita"}>
<div className={"img"}><img src={dataDetail?.image_url}/></div>
<div className={"content-detail"}>
<div className={"title"}>{dataDetail?.title}</div>
<div className={"description"}>{dataDetail?.description}</div>
<div>
<a className={"button btn-full"} href={dataDetail?.link} target={"_blank"}>Link Sumber</a>
</div>
</div>
{/*<iframe style={{height: '100vh', width: '100%'}} src={dataDetail?.url}/>*/}
</section> </section>
<ExportedImage alt={"layer bottom"} className={"layer-bottom"} src={"/image/layer2.png"} fill={true} />
<ExportedImage alt={"layer bottom"} className={"layer-bottom"} src={"/image/layer2.png"} fill={true}/>
</MainLayout>) </MainLayout>)
} }

View File

@ -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 (<MainLayout>
<section className={`header-small`} style={{background: "#ffffff75", backdropFilter: "blur(8px)"}}>
<div className={"top-btn close"}>
<CloseOutlined style={{marginTop: '7px', color: '#000'}}/>
</div>
<div className={"search"} style={{marginLeft: '50px', marginTop: '4px', color: '#000', fontSize: '13px', fontWeight: '600'}}>
{paymnetData?.name}
</div>
<Link href={"/"} className={"top-btn back"}>
<LeftOutlined style={{marginTop: '7px', color: '#000'}}/>
</Link>
</section>
<section style={{paddingTop: '30px', background: '#fff'}}>
<iframe style={{height: "calc(100vh - 30px)",width:"100%"}} src={paymnetData?.url}/>
</section>
{/*<ExportedImage alt={"layer bottom"} className={"layer-bottom"} src={"/image/layer2.png"} fill={true}/>*/}
</MainLayout>)
}

View File

@ -1,6 +1,6 @@
import "./globals.css"; import "@@/css/globals.scss";
import localFont from 'next/font/local' import localFont from 'next/font/local'
import LoadingPage from "@/app/component/LoadingPage"; import LoadingPage from "@/component/LoadingPage";
const poppins = localFont({ const poppins = localFont({
@ -36,8 +36,9 @@ const poppins = localFont({
export const metadata = { export const metadata = {
title: "Omnichannel Kalteng", title: "Betang Portal Payment Kalteng",
description: "Web Omnichannel", description: "Web Omnichannel",
icons: { icons: {
icon: '/image/logo.png', icon: '/image/logo.png',
shortcut: '/image/logo.png', shortcut: '/image/logo.png',
@ -50,6 +51,7 @@ export const viewport = {
width: 'device-width', width: 'device-width',
initialScale: 1, initialScale: 1,
maximumScale: 1, maximumScale: 1,
themeColor: "#159B9F"
} }
export default function RootLayout({ children }) { export default function RootLayout({ children }) {

View File

@ -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 (<Row gutter={[10, 10]}>
{listPayment?.map((v,k) => (
<Col span={6} key={k}>
<Link href={`/detailPayment?id=${v?.id}`} className={"card-omni"}>
<div className={"icon"}>
{(v?.logo) ?
<ExportedImage alt={"card omni"} className={"image-export"} src={v?.logo} fill={true} />
:
<CreditCardOutlined style={{color:'var(--primary)',fontSize:'20px',height:'40px'}} />
}
</div>
<div className={"title"}>{v?.name}</div>
</Link>
</Col>
))}
</Row>
)
}
const items = [
{
key: '1', label: 'Semua ', children: <CardOmni/>,
}, {
key: '2', label: 'Rekreasi', children: <NoData/>,
}, {
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(
<MainLayout>
<HeaderSmallDetail/>
<section className={"omnichannel"}>
<div className={"list"}>
<Tabs defaultActiveKey="1" items={items} onChange={onChange}/>
</div>
</section>
<ExportedImage alt={"layer bottom"} className={"layer-bottom"} src={"/image/layer2.png"} fill={true} />
</MainLayout>
)
}

View File

@ -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 (<div className={"list-news"}>
{listNews?.map((v,k) => (
<Link href={`/detailBerita?id=${v?.article_id}`} className={"content-list-news"} key={k}>
<div>
<img alt={v?.image_url} className={"image-export image"} src={v?.image_url} />
</div>
<div>
<div className={"category"}>{v?.category?.toString()}</div>
<div className={"title"}>{Helper.limitString(v?.title,30) }</div>
<div className={"subtitle"}><span className={"sumber"}>{v?.source_name}</span> | { Helper.getDifferenceDate(new Date(),v?.pubDate) }</div>
</div>
</Link>
))}
</div>)
}
const items = [{
key: '1', label: 'Semua ', children: <ListBerita/>,
}, {
key: '2', label: 'Olahraga', children: <NoData/>,
}, {
key: '3', label: 'Dunia', children: <NoData/>,
}, {
key: '4', label: 'Ekonomi', children: <NoData/>,
}, {
key: '5', label: 'Keluarga', children: <NoData/>,
}, {
key: '6', label: 'Politik', children: <NoData/>,
},
];
return (<MainLayout>
<HeaderSmallDetail/>
<section className={"news"} style={{paddingBottom: '100px'}}>
<div className={"list"} style={{marginTop: '100px'}}>
<Tabs defaultActiveKey="1" items={items} onChange={onChange}/>
</div>
</section>
<ExportedImage alt={"layer bottom"} className={"layer-bottom"} src={"/image/layer2.png"} fill={true} />
</MainLayout>)
}

View File

@ -1,18 +1,73 @@
"use client" "use client"
import {Carousel, Col, Input, Row, Tabs} from "antd"; 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 {useEffect, useState} from "react";
import Header from "@/app/component/Header"; import Header from "@/component/Header";
import HeaderSmall from "@/app/component/HeaderSmall"; import HeaderSmall from "@/component/HeaderSmall";
import Link from "next/link"; import Link from "next/link";
import LoadingPage from "@/app/component/LoadingPage"; import LoadingPage from "@/component/LoadingPage";
import MainLayout from "@/app/component/MainLayout"; import MainLayout from "@/component/MainLayout";
import ExportedImage from "next-image-export-optimizer"; 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() { export default function Home() {
const [isVisible, setIsVisible] = useState(true); 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 = () => { const handleScroll = () => {
if (window.scrollY > 150) { if (window.scrollY > 150) {
setIsVisible(false); setIsVisible(false);
@ -26,100 +81,75 @@ export default function Home() {
return () => { return () => {
window.removeEventListener('scroll', handleScroll); window.removeEventListener('scroll', handleScroll);
}; };
}
useEffect(() => {
scrollEffect()
getListPayment()
// getNews()
}, []); }, []);
const onChange = (key) => {
console.log(key);
};
const CardOmni = () => { const CardOmni = () => {
return (<Row gutter={[10, 10]}> return (<Row gutter={[10, 10]}>
<Col span={6}>
<div className={"card-omni"}>
<div className={"icon"}>
<ExportedImage alt={"card omni"} className={"image-export"} src={"/logo/garuda.png"} fill={true} />
</div>
<div className={"title"}>Garuda Indonesia</div>
</div>
</Col>
<Col span={6}>
<div className={"card-omni"}>
<div className={"icon"}>
<ExportedImage alt={"card omni"} className={"image-export"} src={"/logo/traveloka.png"} fill={true} />
</div>
<div className={"title"}>Traveloka</div>
</div>
</Col>
<Col span={6}>
<div className={"card-omni"}>
<div className={"icon"}>
<ExportedImage alt={"card omni"} className={"image-export"} src={"/logo/woos.png"} fill={true} />
</div>
<div className={"title"}>Woosh</div>
</div>
</Col>
<Col span={6}>
<div className={"card-omni"}>
<div className={"icon"}>
<ExportedImage alt={"card omni"} className={"image-export"} src={"/logo/kai.png"} fill={true} />
</div>
<div className={"title"}>KAI</div>
</div>
</Col>
<Col span={6}>
<div className={"card-omni"}>
<div className={"icon"}>
<ExportedImage alt={"card omni"} className={"image-export"} src={"/logo/garuda.png"} fill={true} />
</div>
<div className={"title"}>Garuda Indonesia</div>
</div>
</Col>
<Col span={6}>
<div className={"card-omni"}>
<div className={"icon"}>
<ExportedImage alt={"card omni"} className={"image-export"} src={"/logo/traveloka.png"} fill={true} />
</div>
<div className={"title"}>Traveloka</div>
</div>
</Col>
<Col span={6}>
<div className={"card-omni"}>
<div className={"icon"}>
<ExportedImage alt={"card omni"} className={"image-export"} src={"/logo/woos.png"} fill={true} />
</div>
<div className={"title"}>Woosh</div>
</div>
</Col>
<Col span={6}>
<div className={"card-omni"}>
<div className={"icon"}>
<ExportedImage alt={"card omni"} className={"image-export"} src={"/logo/kai.png"} fill={true} />
</div>
<div className={"title"}>KAI</div>
</div>
</Col>
{listPayment.map((v,k) => (
<Col span={6} key={k}>
<Link href={(v?.url) ? v?.url:`/detailPayment?id=${v?.id}`} className={"card-omni"}>
<div className={"icon"}>
{(v?.logo) ?
<ExportedImage alt={"card omni"} className={"image-export"} src={v?.logo} fill={true} />
:
<CreditCardOutlined style={{color:'var(--primary)',fontSize:'20px',height:'40px'}} />
}
</div>
<div className={"title"}>{v?.name}</div>
</Link>
</Col>
))}
</Row> </Row>
) )
} }
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 = [ const items = [
{ {
key: '1', label: 'Fashion & Kecantikan ', children: <CardOmni/>, key: '1', label: 'Semua ', children: <CardOmni/>,
}, { }, {
key: '2', label: 'Rekreasi', children: 'Tidak Ada Data', key: '2', label: 'Rekreasi', children: <NoData/>,
}, { }, {
key: '3', label: 'Donasi & Zakat', children: 'Tidak Ada Data', key: '3', label: 'Donasi & Zakat', children: <NoData/>,
}, { }, {
key: '4', label: 'Hobi & Event', children: 'Tidak Ada Data', key: '4', label: 'Hobi & Event', children: <NoData/>,
}, { }, {
key: '5', label: 'Travel', children: 'Tidak Ada Data', key: '5', label: 'Travel', children: <NoData/>,
}, { }, {
key: '6', label: 'Elektronik & Barang Digital ', children: 'Tidak Ada Data', key: '6', label: 'Elektronik & Barang Digital ', children: <NoData/>,
}, },
]; ];
return (<> return (<>
@ -134,11 +164,11 @@ export default function Home() {
<div className={"title"}>Waktunya Belanja</div> <div className={"title"}>Waktunya Belanja</div>
<div className={"sub-title"}>Semua yang Kamu perlu, ada di sini</div> <div className={"sub-title"}>Semua yang Kamu perlu, ada di sini</div>
</div> </div>
<Link href={"/detailBelanja"} className={"button"} type={"button"}>Lihat Semua</Link> <Link href={"/listBelanja"} className={"button"} type={"button"}>Lihat Semua</Link>
</div> </div>
<div className={"list"}> <div className={"list"}>
<Tabs defaultActiveKey="1" items={items} onChange={onChange}/> <Tabs defaultActiveKey="1" items={items} />
</div> </div>
</section> </section>
@ -153,70 +183,45 @@ export default function Home() {
</div> </div>
<div className={"slider"}> <div className={"slider"}>
<Carousel arrows infinite={true}> {(listNews) ?
<div className={"berita"}> <Carousel arrows infinite={true}>
<div className={"layer-berita"}></div> {listNews?.slice(0,5)?.map((v,k) => (
<ExportedImage alt={"berita"} className={"image-export img"} src={"/berita/berita.jpeg"} fill={true} /> <Link href={`/detailBerita?id=${v?.article_id}`} className={"berita"} key={k}>
<div className={"content-berita"}> <div className={"layer-berita"}></div>
<div className={"category"}>Olahraga</div> <img alt={v?.image_url} className={"image-export img"} src={v?.image_url} />
<div className={"title"}>Chelsea Keropos, Lini Belakang Jadi PR Maresca</div> <div className={"content-berita"}>
<div className={"subtitle"}><span className={"sumber"}>Detik.com</span> | 30 Menit yang lalu</div> {/*<div className={"category"}>{v?.category?.toString()}</div>*/}
</div> <div className={"title"}>{v?.title}</div>
</div> <div className={"subtitle"}><span className={"sumber"}>{v?.source_name}</span> | { Helper.getDifferenceDate(new Date(),v?.pubDate) }</div>
<div className={"berita"}> </div>
<div className={"layer-berita"}></div> </Link>
<ExportedImage alt={"berita"} className={"image-export img"} src={"/berita/berita.jpeg"} fill={true} /> ))}
<div className={"content-berita"}> </Carousel>:'loading...' }
<div className={"category"}>Olahraga</div>
<div className={"title"}>Chelsea Keropos, Lini Belakang Jadi PR Maresca</div>
<div className={"subtitle"}><span className={"sumber"}>Detik.com</span> | 30 Menit yang lalu</div>
</div>
</div>
</Carousel>
</div> </div>
<div className={"list-news"}> <div className={"list-news"}>
<div className={"content-list-news"}> {listNews?.slice(5,10)?.map((v,k) => (
<div> <Link href={`/detailBerita?id=${v?.article_id}`} className={"content-list-news"} key={k}>
<ExportedImage alt={"berita"} className={"image-export image"} src={"/berita/berita.jpeg"} fill={true} /> <div>
</div> <img alt={v?.image_url} className={"image-export image"} src={v?.image_url} />
<div> </div>
<div className={"category"}>Olahraga</div> <div>
<div className={"title"}>Chelsea Keropos, Lini Belakang Jadi...</div> <div className={"category"}>{v?.category?.toString()}</div>
<div className={"subtitle"}><span className={"sumber"}>Detik.com</span> | 30 Menit yang lalu</div> <div className={"title"}>{Helper.limitString(v?.title,30) }</div>
</div> <div className={"subtitle"}><span className={"sumber"}>{v?.source_name}</span> | { Helper.getDifferenceDate(new Date(),v?.pubDate) }</div>
</div>
</Link>
))}
</div>
<div className={"content-list-news"}>
<div>
<ExportedImage alt={"berita"} className={"image-export image"} src={"/berita/berita.jpeg"} fill={true} />
</div>
<div>
<div className={"category"}>Olahraga</div>
<div className={"title"}>Chelsea Keropos, Lini Belakang Jadi...</div>
<div className={"subtitle"}><span className={"sumber"}>Detik.com</span> | 30 Menit yang lalu</div>
</div>
</div>
<div className={"content-list-news"}>
<div>
<ExportedImage alt={"berita"} className={"image-export image"} src={"/berita/berita.jpeg"} fill={true} />
</div>
<div>
<div className={"category"}>Olahraga</div>
<div className={"title"}>Chelsea Keropos, Lini Belakang Jadi...</div>
<div className={"subtitle"}><span className={"sumber"}>Detik.com</span> | 30 Menit yang lalu</div>
</div>
</div> <Link href={"/listBerita"} className={"button btn-full"} type={"button"}>Lihat Semua</Link>
<Link href={"/detailBerita"} className={"button btn-full"} type={"button"}>Lihat Semua</Link>
</div> </div>
</section> </section>
<section className={"footer"}> <section className={"footer"}>
<div className={"title"}>produk atau jasa di menu omnichannel disediakan dan sepenuhnya menjadi tanggungjawab penyedia produk atau jasa terkait. baca syarat dan ketentuan lengkap disini</div> <div className={"title"}>produk atau jasa di menu Betang Portal Payment disediakan dan sepenuhnya menjadi tanggungjawab penyedia produk atau jasa terkait. baca syarat dan ketentuan lengkap disini</div>
</section> </section>

View File

@ -1,14 +1,46 @@
import {BellOutlined, CloseOutlined, EnvironmentOutlined, SearchOutlined} from "@ant-design/icons"; import {BellOutlined, CloseOutlined, EnvironmentOutlined, SearchOutlined} from "@ant-design/icons";
import {Input} from "antd"; import {Input} from "antd";
import ExportedImage from "next-image-export-optimizer"; import ExportedImage from "next-image-export-optimizer";
import {useEffect, useState} from "react";
export default function Header({isVisible}) { 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( return(
<> <>
<section className={`header ${isVisible ? 'visible' : 'hidden'}`}> <section className={`header ${isVisible ? 'visible' : 'hidden'}`}>
<div className={"location"}> <div onClick={getLocation} className={"location"}>
<div className={"value"}><EnvironmentOutlined style={{marginRight: '5px'}}/> <div className={"value"}><EnvironmentOutlined style={{marginRight: '5px'}}/>
Kota Bandung, Jawa Barat {(location?.name) ? location?.name:"Aktifkan Lokasi"}
</div> </div>
</div> </div>
<div className={"top-btn notification"}> <div className={"top-btn notification"}>
@ -23,15 +55,17 @@ export default function Header({isVisible}) {
<div> <div>
<ExportedImage <ExportedImage
className={"image-export"} className={"image-export"}
src="/image/logonew.png" src="/image/logodummy.png"
fill={true} fill={true}
alt="logo" alt="logo"
/> />
<div className={"title"}>Betang</div>
<div className={"sub-title"}>Portal Payment</div>
</div> </div>
</div> </div>
<div className={"search"}> <div className={"search"}>
<Input className={"form"} placeholder={"Cari Tiket Konser Sekarang !"} prefix={<SearchOutlined/>}/> <Input className={"form"} placeholder={"Cari Tiket Sekarang !"} prefix={<SearchOutlined/>}/>
</div> </div>
</div> </div>

View File

@ -14,7 +14,7 @@ export default function HeaderSmall({isVisible}) {
</div> </div>
<div className={"search"}> <div className={"search"}>
<Input className={"form"} placeholder={"Cari Tiket Konser Sekarang !"} prefix={<SearchOutlined/>}/> <Input className={"form"} placeholder={"Cari Apapun !"} prefix={<SearchOutlined/>}/>
</div> </div>
<ExportedImage className={"image-export layer"} src={"/image/layer1.png"} fill={true} alt="layer" /> <ExportedImage className={"image-export layer"} src={"/image/layer1.png"} fill={true} alt="layer" />

View File

@ -3,7 +3,7 @@ import {Input} from "antd";
import Link from "next/link"; import Link from "next/link";
import ExportedImage from "next-image-export-optimizer"; import ExportedImage from "next-image-export-optimizer";
export default function HeaderSmallDetail({isVisible}) { export default function HeaderSmallDetail({isVisible,noSearch}) {
return( return(
<> <>
<section className={`header-small ${isVisible ? 'hidden' : 'visible'}`}> <section className={`header-small ${isVisible ? 'hidden' : 'visible'}`}>
@ -14,9 +14,10 @@ export default function HeaderSmallDetail({isVisible}) {
<LeftOutlined style={{marginTop: '7px',color:'#fff'}}/> <LeftOutlined style={{marginTop: '7px',color:'#fff'}}/>
</Link> </Link>
<div className={"search"} style={{marginLeft:'50px'}}> {(noSearch) ? '':<div className={"search"} style={{marginLeft:'50px'}}>
<Input className={"form"} placeholder={"Cari Tiket Konser Sekarang !"} prefix={<SearchOutlined/>}/> <Input className={"form"} placeholder={"Cari Apapun !"} prefix={<SearchOutlined/>}/>
</div> </div>}
<ExportedImage className={"image-export layer"} src={"/image/layer1.png"} fill={true} alt="layer" /> <ExportedImage className={"image-export layer"} src={"/image/layer1.png"} fill={true} alt="layer" />
</section> </section>

View File

@ -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 (
<div className={`form-group mb-10 ${error[name]?.message ? "error" : ""}`}>
<div className="floating-label-content">
<DatePicker
{...register(name, validateList)}
name={name}
id={name}
format={newFormat}
onChange={onChange}
// defaultValue={(defaultDate) ? dayjs(defaultDate,'YYYY-MM-DD'):null}
minDate={minDate ? dayjs(minDate, 'YYYY-MM-DD') : null}
maxDate={maxDate ? dayjs(maxDate, 'YYYY-MM-DD') : null}
value={value ? dayjs(value, 'YYYY-MM-DD') : null}
placeholder={placeholder || "Pilih Tanggal"}
picker={picker == "default" ? undefined : picker}
className="form-control borderInput floating-input"
/>
<label className="floating-label" htmlFor={name}>
{" "}
{title}{" "}{(required) ? <span className={"fw-7 text-danger"}>*</span>:''}
</label>
{error[name] && (
<div className="error-form">{error[name]?.message}</div>
)}
</div>
</div>
);
};
export default InputDate;

View File

@ -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 (
<div className={`form-group mb-10 ${(error[name]?.message) ? 'error' : ''}`} style={{marginTop: '-15px'}}>
<div className="floating-label-content">
<Space direction="vertical" size={0} className='w-full'>
<label className="floating-label" style={{zIndex: '1', top: '0'}} htmlFor={name}> {title} </label>
<RangePicker
{...register(name, validateList)}
name={name}
id={name}
presets={rangePresets}
onChange={onChange}
format={newFormat}
picker={picker == 'default' ? undefined : picker}
className="form-control borderInput floating-input"
style={{
display: 'flex'
}}/>
</Space>
{error[name] && <div className="error-form">{error[name]?.message}</div>}
</div>
</div>
);
};
export default InputDateRange;

View File

@ -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 (
<>
<div className={`form-group mb-10 ${(error[name]?.message) ? 'error' : ''}`}>
<div className="floating-label-content">
<input
type="input"
{...register(name, validateList)}
className="form-control borderInput floating-input"
name={name}
id={name}
maxLength={maxlength ? maxlength : 250}
placeholder={placeholder ? placeholder : `.`}
onInput={handleInput}
/>
<label className="floating-label" htmlFor={name}>
{title}
</label>
{error[name] && <div className="error-form">{error[name]?.message}</div>}
</div>
</div>
</>
);
};
export default InputEmail;

View File

@ -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 (
<>
<div className="fs-8 mb-1">{title}</div>
<div className="fs-10 text-muted mb-4">Image Format PNG,JPG,JPEG Max 500 Kb. Recommendation resolution is 800 x 800</div>
<div className="image-input mb-4">
<input type="file" name={name} accept="image/*" id={"imageInput_"+name} />
<div className="content-btn">
<label htmlFor={"imageInput_"+name} className="image-button btn btn-light-primary btn-sm btn-circle btn-sm"><i className='bx bx-image-add fs-6'></i></label>
<button type="button" id={"changeImage_"+name} className="btn btn-light-danger btn-sm btn-circle btn-sm"><i className='bx bx-trash fs-8'></i></button>
</div>
<img src="/img/no-image.jpg" id={"imagePreview_"+name} className="image-preview" />
</div>
</>
);
};
export default InputImage;

View File

@ -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 (
<>
<div className={`form-group mb-10 ${(error[name]?.message) ? 'error' : ''}`}>
<div className="floating-label-content">
{(satuanInput) ? <div className={"satuanInput"}>{satuanInput}</div>:''}
<input
type="input"
readOnly={setReadonly}
{...register(name, validateList)}
className="form-control borderInput floating-input"
style={setStyle}
name={name}
id={name}
maxLength={maxlength ? maxlength : 250}
placeholder={placeholder ? placeholder : `.`}
onInput={(allowDecimal) ? handleInputDecimal : handleInput}
/>
<label className="floating-label" htmlFor={name}>
{title} {(required) ? <span className={"fw-7 text-danger"}>*</span>:''}
</label>
{error[name] && <div className="error-form">{error[name]?.message}</div>}
</div>
</div>
</>
);
};
export default InputMoney;

View File

@ -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 (
<>
<div className={`form-group mb-10 ${(error[name]?.message) ? 'error' : ''}`}>
<div className="floating-label-content">
{(satuanInput) ? <div className={"satuanInput"}>{satuanInput}</div>:''}
<input
type="input"
readOnly={setReadonly}
{...register(name, validateList)}
className="form-control borderInput floating-input"
name={name}
id={name}
maxLength={maxlength ? maxlength : 250}
minLength={minlength ? minlength : 0}
placeholder={placeholder ? placeholder : `.`}
onInput={handleInput}
/>
<label className="floating-label" htmlFor={name}>
{title} {(required) ? <span className={"fw-7 text-danger"}>*</span>:''}
</label>
{error[name] && <div className="error-form">{error[name]?.message}</div>}
</div>
</div>
</>
);
};
export default InputNumber;

View File

@ -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 (
<>
<div className={`form-group mb-10 ${(error[name]) ? 'error' : ''}`}>
<div className="floating-label-content">
<input
type={showPassword ? 'text' : 'password'}
ref={inputRef}
{...register && {...register(name, validateList)}}
className="form-control borderInput floating-input sh-password"
name={name}
id={name}
maxLength={maxlength ? maxlength : 250}
placeholder={placeholder ? placeholder : ` `}
onInput={handleInput}
/>
<div className="icon-password" onClick={togglePassword}>
<span className="input-group-text icon-eye">{showPassword ? <EyeFilled style={{fontSize: '20px'}}/> : <EyeInvisibleFilled style={{fontSize: '20px'}}/>} </span>
</div>
<label className="floating-label" htmlFor={name}>
{title}
</label>
{error[name] && <div className="error-form">{error[name]?.message}</div>}
</div>
</div>
</>
);
};
export default InputPassword;

View File

@ -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 (
<>
<div
className={`form-group mb-10 ${
error[name]?.message ? "error" : ""
}`}
>
<div className="floating-label-content">
<InputNumber
min={0}
max={100}
formatter={(value) => 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}
/>
<label className="floating-label" htmlFor={name}>
{title}
</label>
{error[name] && (
<div className="error-form">{error[name]?.message}</div>
)}
</div>
</div>
</>
);
};
export default InputPercentage;

View File

@ -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 (
<>
<div className={`form-group mb-10 ${error[name]?.message ? "error" : ""}`}>
<div className="floating-label-content">
<Select
disabled={setReadonly || setDisabled}
// defaultValue="lucy"
{...register(name, validateList)}
name={name}
showSearch={withSearch}
id={name}
value={val}
allowClear
loading={loading}
onChange={handleChange}
// onSearch={onSearch}
filterOption={filterOption}
style={{width: '100%'}}
placeholder={"Pilih Data"}
className="form-control borderInput floating-input"
options={options}
/>
<label className="floating-label" htmlFor={name}>
{title} {(required) ? <span className={"fw-7 text-danger"}>*</span>:''}
</label>
{error[name] && <div className="error-form">{error[name]?.message}</div>}
</div>
</div>
</>
);
};
export default InputSelect;

View File

@ -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 (
<>
<div className={`form-group mb-10 ${(error[name]?.message) ? 'error' : ''}`}>
<div className="floating-label-content">
<input
type="input"
readOnly={setReadonly}
{...register(name, validateList)}
className="form-control borderInput floating-input"
name={name}
id={name}
maxLength={maxlength ? maxlength : 250}
placeholder={placeholder ? placeholder : `.`}
/>
<label className="floating-label" htmlFor={name}>
{title} {(required) ? <span className={"fw-7 text-danger"}>*</span>:''}
</label>
{error[name] && <div className="error-form">{error[name]?.message}</div>}
</div>
</div>
</>
);
};
export default InputText;

View File

@ -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 (
<>
<div className={`form-group mb-10 ${(error[name]?.message) ? 'error' : ''}`}>
<div className="floating-label-content">
<textarea readOnly={setReadonly} style={{width: '100%'}} {...props} className={`form-control borderInput floating-input ${className}`} {...register(name, validateList)} name={name} id={name} maxLength={maxlength ? maxlength : 250} cols="30" rows="10">
</textarea>
<label className="floating-label" htmlFor={name}>
{title} {(required) ? <span className={"fw-7 text-danger"}>*</span>:''}
</label>
{error[name] && <div className="error-form">{error[name]?.message}</div>}
</div>
</div>
</>
);
};
export default InputTextarea;

View File

@ -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 (
<>
<div className={`form-group mb-10 ${(error[name]?.message) ? 'error' : ''}`}>
<div className="floating-label-content">
<input
type="input"
readOnly={setReadonly}
{...register(name, validateList)}
className="form-control borderInput floating-input"
name={name}
id={name}
maxLength={maxlength ? maxlength : 250}
minLength={minlength ? minlength : 0}
placeholder={placeholder ? placeholder : ` `}
onInput={handleInput}
/>
<label className="floating-label" htmlFor={name}>
{title}
</label>
{error[name] && <div className="error-form">{error[name]?.message}</div>}
</div>
</div>
</>
);
};
export default InputUsername;

View File

@ -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

View File

@ -1,4 +1,4 @@
import LoadingPage from "@/app/component/LoadingPage"; import LoadingPage from "@/component/LoadingPage";
import {motion} from "framer-motion" import {motion} from "framer-motion"
export default function MainLayout({children}) { export default function MainLayout({children}) {
return( return(

View File

@ -0,0 +1,20 @@
import {motion} from "framer-motion";
export default function TransitionContent({children}) {
return(
<motion.div
className={"animate-box"}
initial={{ opacity: 0, y: 0 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: 0 }}
transition={{
duration: 1,
ease: "easeInOut",
times: [0, 0.5, 1],
loop: Infinity,
repeatDelay: 1
}}
>{children}</motion.div>
)
}

37
src/lib/API.jsx Normal file
View File

@ -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"
}
}
}
}

185
src/lib/Helper.jsx Normal file
View File

@ -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)
}
}