desain dashboard, outlet, perusahaan, agen

This commit is contained in:
Zamzam Nurzaman 2024-10-24 16:58:46 +07:00
parent 7f080c735f
commit d3705835bb
48 changed files with 3299 additions and 1145 deletions

View File

@ -1 +1,2 @@
NEXT_PUBLIC_API_URL='https://api-silos-kpr.basys.co.id' //NEXT_PUBLIC_API_URL='https://api-silos-kpr.basys.co.id'
NEXT_PUBLIC_API_URL='https://digital-attendance-api.basys.co.id'

168
package-lock.json generated
View File

@ -10,6 +10,8 @@
"dependencies": { "dependencies": {
"@ant-design/icons": "^5.3.0", "@ant-design/icons": "^5.3.0",
"antd": "^5.12.5", "antd": "^5.12.5",
"aos": "^3.0.0-beta.6",
"apexcharts": "^3.49.1",
"cookies-next": "^4.1.1", "cookies-next": "^4.1.1",
"dayjs": "^1.11.10", "dayjs": "^1.11.10",
"docxtemplater": "^3.40.0", "docxtemplater": "^3.40.0",
@ -22,16 +24,15 @@
"next": "14.2.15", "next": "14.2.15",
"next-image-export-optimizer": "^1.12.3", "next-image-export-optimizer": "^1.12.3",
"next-pwa": "^5.6.0", "next-pwa": "^5.6.0",
"nextjs-toploader": "^1.6.6", "nextjs-toploader": "^1.6.12",
"react": "^18", "react": "^18",
"react-apexcharts": "^1.4.1",
"react-dom": "^18", "react-dom": "^18",
"react-hook-form": "^7.45.4", "react-hook-form": "^7.45.4",
"react-query": "^3.39.3", "react-query": "^3.39.3",
"sass": "^1.69.5", "sass": "^1.69.5",
"styled-components": "^5.3.9", "styled-components": "^5.3.9",
"uuid": "^10.0.0", "uuid": "^10.0.0",
"zingchart": "^2.9.14",
"zingchart-react": "^3.2.0",
"zustand": "^4.4.7" "zustand": "^4.4.7"
}, },
"devDependencies": { "devDependencies": {
@ -3293,6 +3294,11 @@
"integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==",
"peer": true "peer": true
}, },
"node_modules/@yr/monotone-cubic-spline": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@yr/monotone-cubic-spline/-/monotone-cubic-spline-1.0.3.tgz",
"integrity": "sha512-FQXkOta0XBSUPHndIKON2Y9JeQz5ZeMqLYZVVK93FliNBFm7LNMIZmY6FrMEB9XPcDbE2bekMbZD6kzDkxwYjA=="
},
"node_modules/acorn": { "node_modules/acorn": {
"version": "8.13.0", "version": "8.13.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.13.0.tgz", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.13.0.tgz",
@ -3432,6 +3438,30 @@
"react-dom": ">=16.9.0" "react-dom": ">=16.9.0"
} }
}, },
"node_modules/aos": {
"version": "3.0.0-beta.6",
"resolved": "https://registry.npmjs.org/aos/-/aos-3.0.0-beta.6.tgz",
"integrity": "sha512-VLWrpq8bfAWcetynVHMMrqdC+89Qq/Ym6UBJbHB4crIwp3RR8uq1dNGgsFzoDl03S43rlVMK+na3r5+oUCZsYw==",
"dependencies": {
"classlist-polyfill": "^1.2.0",
"lodash.debounce": "^4.0.8",
"lodash.throttle": "^4.1.1"
}
},
"node_modules/apexcharts": {
"version": "3.54.1",
"resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-3.54.1.tgz",
"integrity": "sha512-E4et0h/J1U3r3EwS/WlqJCQIbepKbp6wGUmaAwJOMjHUP4Ci0gxanLa7FR3okx6p9coi4st6J853/Cb1NP0vpA==",
"dependencies": {
"@yr/monotone-cubic-spline": "^1.0.3",
"svg.draggable.js": "^2.2.2",
"svg.easing.js": "^2.0.0",
"svg.filter.js": "^2.0.2",
"svg.pathmorphing.js": "^0.1.3",
"svg.resize.js": "^1.4.3",
"svg.select.js": "^3.0.1"
}
},
"node_modules/archiver": { "node_modules/archiver": {
"version": "5.3.2", "version": "5.3.2",
"resolved": "https://registry.npmjs.org/archiver/-/archiver-5.3.2.tgz", "resolved": "https://registry.npmjs.org/archiver/-/archiver-5.3.2.tgz",
@ -4198,6 +4228,11 @@
"node": ">=6.0" "node": ">=6.0"
} }
}, },
"node_modules/classlist-polyfill": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/classlist-polyfill/-/classlist-polyfill-1.2.0.tgz",
"integrity": "sha512-GzIjNdcEtH4ieA2S8NmrSxv7DfEV5fmixQeyTmqmRmRJPGpRBaSnA2a0VrCjyT8iW8JjEdMbKzDotAJf+ajgaQ=="
},
"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",
@ -7138,6 +7173,11 @@
"resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz",
"integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==" "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA=="
}, },
"node_modules/lodash.throttle": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz",
"integrity": "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ=="
},
"node_modules/lodash.union": { "node_modules/lodash.union": {
"version": "4.6.0", "version": "4.6.0",
"resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz", "resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz",
@ -8558,6 +8598,18 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/react-apexcharts": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/react-apexcharts/-/react-apexcharts-1.4.1.tgz",
"integrity": "sha512-G14nVaD64Bnbgy8tYxkjuXEUp/7h30Q0U33xc3AwtGFijJB9nHqOt1a6eG0WBn055RgRg+NwqbKGtqPxy15d0Q==",
"dependencies": {
"prop-types": "^15.8.1"
},
"peerDependencies": {
"apexcharts": "^3.41.0",
"react": ">=0.13"
}
},
"node_modules/react-dom": { "node_modules/react-dom": {
"version": "18.3.1", "version": "18.3.1",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
@ -9651,6 +9703,89 @@
"node": ">=12.0.0" "node": ">=12.0.0"
} }
}, },
"node_modules/svg.draggable.js": {
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/svg.draggable.js/-/svg.draggable.js-2.2.2.tgz",
"integrity": "sha512-JzNHBc2fLQMzYCZ90KZHN2ohXL0BQJGQimK1kGk6AvSeibuKcIdDX9Kr0dT9+UJ5O8nYA0RB839Lhvk4CY4MZw==",
"dependencies": {
"svg.js": "^2.0.1"
},
"engines": {
"node": ">= 0.8.0"
}
},
"node_modules/svg.easing.js": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/svg.easing.js/-/svg.easing.js-2.0.0.tgz",
"integrity": "sha512-//ctPdJMGy22YoYGV+3HEfHbm6/69LJUTAqI2/5qBvaNHZ9uUFVC82B0Pl299HzgH13rKrBgi4+XyXXyVWWthA==",
"dependencies": {
"svg.js": ">=2.3.x"
},
"engines": {
"node": ">= 0.8.0"
}
},
"node_modules/svg.filter.js": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/svg.filter.js/-/svg.filter.js-2.0.2.tgz",
"integrity": "sha512-xkGBwU+dKBzqg5PtilaTb0EYPqPfJ9Q6saVldX+5vCRy31P6TlRCP3U9NxH3HEufkKkpNgdTLBJnmhDHeTqAkw==",
"dependencies": {
"svg.js": "^2.2.5"
},
"engines": {
"node": ">= 0.8.0"
}
},
"node_modules/svg.js": {
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/svg.js/-/svg.js-2.7.1.tgz",
"integrity": "sha512-ycbxpizEQktk3FYvn/8BH+6/EuWXg7ZpQREJvgacqn46gIddG24tNNe4Son6omdXCnSOaApnpZw6MPCBA1dODA=="
},
"node_modules/svg.pathmorphing.js": {
"version": "0.1.3",
"resolved": "https://registry.npmjs.org/svg.pathmorphing.js/-/svg.pathmorphing.js-0.1.3.tgz",
"integrity": "sha512-49HWI9X4XQR/JG1qXkSDV8xViuTLIWm/B/7YuQELV5KMOPtXjiwH4XPJvr/ghEDibmLQ9Oc22dpWpG0vUDDNww==",
"dependencies": {
"svg.js": "^2.4.0"
},
"engines": {
"node": ">= 0.8.0"
}
},
"node_modules/svg.resize.js": {
"version": "1.4.3",
"resolved": "https://registry.npmjs.org/svg.resize.js/-/svg.resize.js-1.4.3.tgz",
"integrity": "sha512-9k5sXJuPKp+mVzXNvxz7U0uC9oVMQrrf7cFsETznzUDDm0x8+77dtZkWdMfRlmbkEEYvUn9btKuZ3n41oNA+uw==",
"dependencies": {
"svg.js": "^2.6.5",
"svg.select.js": "^2.1.2"
},
"engines": {
"node": ">= 0.8.0"
}
},
"node_modules/svg.resize.js/node_modules/svg.select.js": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/svg.select.js/-/svg.select.js-2.1.2.tgz",
"integrity": "sha512-tH6ABEyJsAOVAhwcCjF8mw4crjXSI1aa7j2VQR8ZuJ37H2MBUbyeqYr5nEO7sSN3cy9AR9DUwNg0t/962HlDbQ==",
"dependencies": {
"svg.js": "^2.2.5"
},
"engines": {
"node": ">= 0.8.0"
}
},
"node_modules/svg.select.js": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/svg.select.js/-/svg.select.js-3.0.1.tgz",
"integrity": "sha512-h5IS/hKkuVCbKSieR9uQCj9w+zLHoPh+ce19bBYyqF53g6mnPB8sAtIbe1s9dh2S2fCmYX2xel1Ln3PJBbK4kw==",
"dependencies": {
"svg.js": "^2.6.5"
},
"engines": {
"node": ">= 0.8.0"
}
},
"node_modules/tapable": { "node_modules/tapable": {
"version": "2.2.1", "version": "2.2.1",
"resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
@ -10815,33 +10950,6 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/zingchart": {
"version": "2.9.15",
"resolved": "https://registry.npmjs.org/zingchart/-/zingchart-2.9.15.tgz",
"integrity": "sha512-o0doeMS5VxL+TsQXzkZzNuMBHzlw7wCG/1TN/wAXhs2QOnABfGGz6g4TFx+EMzZYaTx0ZRKM8B+fSYTlHRDu0A=="
},
"node_modules/zingchart-constants": {
"version": "1.0.5",
"resolved": "git+ssh://git@github.com/zingchart/zingchart-constants.git#37aaeb291bbaab2174d317c1182bbca6a8f70da5",
"license": "ISC"
},
"node_modules/zingchart-react": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/zingchart-react/-/zingchart-react-3.2.0.tgz",
"integrity": "sha512-VTGhSBIUbbf5dLpVHxyoFRYB7rkx4umlhXKa6zJR0YjVu5tUE4pvogHu9lX5LjeTTXbPRW8quMqs+f+5d9juKw==",
"dependencies": {
"zingchart": "latest",
"zingchart-constants": "github:zingchart/zingchart-constants#master"
},
"engines": {
"node": ">=14",
"npm": ">=5"
},
"peerDependencies": {
"react": ">=15.0.0",
"react-dom": ">=15.0.0"
}
},
"node_modules/zip-stream": { "node_modules/zip-stream": {
"version": "4.1.1", "version": "4.1.1",
"resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-4.1.1.tgz", "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-4.1.1.tgz",

View File

@ -10,11 +10,9 @@
"export": "next build && next-image-export-optimizer" "export": "next build && next-image-export-optimizer"
}, },
"dependencies": { "dependencies": {
"react": "^18",
"react-dom": "^18",
"next": "14.2.15",
"@ant-design/icons": "^5.3.0", "@ant-design/icons": "^5.3.0",
"antd": "^5.12.5", "antd": "^5.12.5",
"aos": "^3.0.0-beta.6",
"cookies-next": "^4.1.1", "cookies-next": "^4.1.1",
"dayjs": "^1.11.10", "dayjs": "^1.11.10",
"docxtemplater": "^3.40.0", "docxtemplater": "^3.40.0",
@ -24,17 +22,20 @@
"html2canvas": "^1.4.1", "html2canvas": "^1.4.1",
"jspdf": "^2.5.1", "jspdf": "^2.5.1",
"jspdf-autotable": "^3.8.2", "jspdf-autotable": "^3.8.2",
"next": "14.2.15",
"next-image-export-optimizer": "^1.12.3", "next-image-export-optimizer": "^1.12.3",
"next-pwa": "^5.6.0", "next-pwa": "^5.6.0",
"nextjs-toploader": "^1.6.6", "nextjs-toploader": "^1.6.12",
"react": "^18",
"react-dom": "^18",
"react-hook-form": "^7.45.4", "react-hook-form": "^7.45.4",
"react-query": "^3.39.3", "react-query": "^3.39.3",
"sass": "^1.69.5", "sass": "^1.69.5",
"styled-components": "^5.3.9", "styled-components": "^5.3.9",
"uuid": "^10.0.0", "uuid": "^10.0.0",
"zustand": "^4.4.7", "zustand": "^4.4.7",
"zingchart": "^2.9.14", "apexcharts": "^3.49.1",
"zingchart-react": "^3.2.0" "react-apexcharts": "^1.4.1"
}, },
"devDependencies": { "devDependencies": {
"eslint": "^8", "eslint": "^8",

View File

@ -1,8 +1,8 @@
.btn.btn-primary { .btn.btn-primary {
color: var(--primary-inverse); color: var(--primary-inverse);
border-color: var(--primary); border: var(--bg-gradient-primary);
background-color: var(--primary); background: var(--bg-gradient-primary);
} }
.btn.btn-primary:hover, .btn.btn-primary:hover,
@ -112,7 +112,8 @@
height: 35px; height: 35px;
padding: 0 28px; padding: 0 28px;
border-radius: 15px; border-radius: 15px;
font-size: 11px; font-size: 12px;
font-weight: 600;
border: none; border: none;
outline: none; outline: none;
cursor: pointer; cursor: pointer;

View File

@ -102,9 +102,10 @@
.floating-label { .floating-label {
position: absolute; position: absolute;
left: 15px; left: 9px;
z-index: 111;
top: 8px; top: 8px;
font-size: 14px; font-size: 12px;
font-weight: 500; font-weight: 500;
padding: 0 7px; padding: 0 7px;
background: #fff; background: #fff;

View File

@ -344,24 +344,37 @@
.nav-account { .nav-account {
position: relative;
//background: #efefef;
border-radius: 35px; border-radius: 35px;
position: absolute;
bottom: 56px;
width: 100%;
padding: 0px 7px;
transition: 0.5s ease-in-out;
} }
.nav-account .container-account .account { .nav-account .container-account .account {
display: flex; display: flex;
justify-content: end; justify-content: space-between;
align-items: center; align-items: center;
gap: 1rem; gap: 1rem;
//background: rgba(255, 255, 255, 0);
padding: 8px 10px 8px 45px;
border-radius: 35px; border-radius: 35px;
cursor: pointer; cursor: pointer;
transition: 0.5s ease-in-out;
} }
.nav-account .container-account .account .text { .nav-account .container-account .account .text {
text-align: end; text-align: start;
width: 0;
margin-left: -15px;
opacity: 0;
visibility: hidden;
}
.nav-account .container-account .account .text.active{
width: 100%;
margin-left: 10px;
opacity:1;
visibility: visible;
} }
.nav-account .detail-account .header .text .name { .nav-account .detail-account .header .text .name {
@ -405,26 +418,56 @@
} }
.nav-account .detail-account { .nav-account .detail-account {
background: #fff;
height: 0;
right: 0;
opacity: 0; opacity: 0;
visibility: hidden; visibility: hidden;
background: #fff;
right: 0;
left: 65px;
top: -200px;
height: 270px;
position: absolute; position: absolute;
transition: all 0.3s ease; transition: all 0.3s ease;
top: 100%;
margin-top: 10px; margin-top: 10px;
min-width: 250px; min-width: 250px;
border-radius: 20px; border-radius: 20px;
box-shadow: 0px 0px 50px 0px rgba(82, 63, 105, 0.15);
} }
.nav-account .container-account:hover .detail-account { .nav-account .detail-account.active {
opacity: 0;
visibility: hidden;
background: #fff;
right: 0;
left: 305px;
top: -200px;
height: 270px;
position: absolute;
transition: all 0.3s ease;
margin-top: 10px;
min-width: 250px;
border-radius: 20px;
}
.nav-account:hover .detail-account {
height: auto; height: auto;
opacity: 1; opacity: 1;
visibility: visible; visibility: visible;
transition: all 0.5s ease; transition: all 0.5s ease;
transform: translateY(-10px); right: 0;
left: 55px;
top: -180px;
z-index: 1000;
}
.nav-account:hover .detail-account.active {
height: auto;
opacity: 1;
visibility: visible;
transition: all 0.5s ease;
right: 0;
left: 255px;
top: -180px;
z-index: 1000;
} }
.nav-account .detail-account .header { .nav-account .detail-account .header {
@ -909,6 +952,7 @@
height:35px; height:35px;
position: absolute; position: absolute;
bottom: 10px; bottom: 10px;
right: 8px;
//background: #00000038; //background: #00000038;
background: rgba(0, 0, 0, 0.1882352941); background: rgba(0, 0, 0, 0.1882352941);
border-radius: 26px; border-radius: 26px;

View File

@ -50,7 +50,7 @@
color: var(--primary); color: var(--primary);
font-weight: 600; font-weight: 600;
text-align: start; text-align: start;
background:#d4e8ed; background:#0179c217;
border-bottom: 1px solid #ffffff; border-bottom: 1px solid #ffffff;
padding: 11px 16px; padding: 11px 16px;
} }

View File

@ -1,5 +1,6 @@
:root { :root {
--primary: #0179c2; --primary: #0179c2;
--primary-dark: #01314e;
--success: #50cd89; --success: #50cd89;
--info: #1a98ff; --info: #1a98ff;
--warning: #FEB82F; --warning: #FEB82F;
@ -7,7 +8,7 @@
--secondary: #aaa; --secondary: #aaa;
--dark: #181c32; --dark: #181c32;
--dark-grey: #666; --dark-grey: #666;
--primary-light: #9fd6ff; --primary-light: #0179c217;
--secondary-light: #f9f9f9; --secondary-light: #f9f9f9;
--success-light: #e8fff3; --success-light: #e8fff3;
--info-light: #dbe8ff; --info-light: #dbe8ff;
@ -15,7 +16,8 @@
--danger-light: #fff5f8; --danger-light: #fff5f8;
--dark-light: #e4e6e7; --dark-light: #e4e6e7;
--white: #fff; --white: #fff;
--text-muted: #9A9A9A; --text-muted: #a9a9a9;
--text-muted-reverse: #ffffff85;
--success-inverse: #ffffff; --success-inverse: #ffffff;
--info-inverse: #ffffff; --info-inverse: #ffffff;
--warning-inverse: #ffffff; --warning-inverse: #ffffff;
@ -53,16 +55,17 @@
--color-step6:#326F71; --color-step6:#326F71;
--color-step7:#1e5c6b; --color-step7:#1e5c6b;
--color-logo-green: #61c300;
--color-logo-orange: #e77c01;
--color-logo-purple: #a800c1;
//--color-step1:#50cd89; --bg-gradient-transaksi: linear-gradient(316deg, #17374b, var(--primary));
//--color-step2:#43af7f; --bg-gradient-orange: linear-gradient(316deg, #17374b, var(--color-logo-orange));
//--color-step3:#369375; --bg-gradient-green: linear-gradient(316deg, #17374b, var(--color-logo-green));
//--color-step4:#29776b; --bg-gradient-purple: linear-gradient(316deg, #17374b, var(--color-logo-purple));
//--color-step5:#1e5c6b; --bg-gradient-dark: linear-gradient(316deg, #17374b, var(--dark));
//--color-step6:#1c5064; --bg-gradient-primary: linear-gradient(336deg, #0a3865, var(--primary));
//--color-step7:#19445e;
--bg-menu: linear-gradient(90deg, #003775 0%, #0057b9 104.42%);
--bg-sub-menu: #f8f8f840; --bg-sub-menu: #f8f8f840;
} }
@ -110,6 +113,7 @@ button {
body { body {
overflow-x: hidden; overflow-x: hidden;
background: #000;
} }
.bg-nav { .bg-nav {
@ -131,6 +135,14 @@ body {
} }
.nextjs-toploader {
z-index: 9999;
position: fixed;
top: 0;
left: 0;
right: 0;
}
.bg-nav-hov{ .bg-nav-hov{
background: rgba(0, 0, 0, 0.7); background: rgba(0, 0, 0, 0.7);
height: 100px; height: 100px;
@ -140,6 +152,16 @@ body {
z-index: 2; z-index: 2;
} }
.card-content{
background: #ffffffe0;
width: 100%;
height: 100%;
border-radius: 30px;
padding: 30px;
overflow: auto;
backdrop-filter: blur(8px);
}
section { section {
position: fixed; position: fixed;
// top: 10rem; // top: 10rem;
@ -147,12 +169,14 @@ section {
overflow: scroll; overflow: scroll;
height: 100vh; height: 100vh;
width: 100%; width: 100%;
padding: 15px 35px 150px; padding: 15px 10px;
padding-left: 300px; padding-left: 280px;
border-radius: 15px 15px 0 0; border-radius: 20px;
transition: all 0.5s ease; transition: all 0.5s ease;
background: #f3f3f3; //background: #f3f3f3;
//background: url(/img/pat29.png); background: url(/img/bg14.jpg);
background-size: cover;
box-shadow: 0px 0px 50px 0px rgba(82, 63, 105, 0.15); box-shadow: 0px 0px 50px 0px rgba(82, 63, 105, 0.15);
&::-webkit-scrollbar { &::-webkit-scrollbar {
height: 0; height: 0;
@ -166,7 +190,7 @@ section {
} }
section.content.hover{ section.content.hover{
padding-left: 120px; padding-left: 80px;
} }
.resultCard { .resultCard {
@ -253,7 +277,6 @@ section.content.hover{
color: var(--dark-grey); color: var(--dark-grey);
padding: 8px 12px; padding: 8px 12px;
border: 0; border: 0;
width: 100%;
/* border-top: 1px dashed #e7e7e7; */ /* border-top: 1px dashed #e7e7e7; */
outline: 0; outline: 0;
background: transparent; background: transparent;
@ -614,7 +637,7 @@ section.content.hover{
align-items: start; align-items: start;
flex-direction: column; flex-direction: column;
margin-right: 10px; margin-right: 10px;
margin-top: 10px; margin-top: -45px;
width: 50%; width: 50%;
.breadCrumb { .breadCrumb {
@ -1789,6 +1812,8 @@ hr.border{
} }
.check-auth{ .check-auth{
color: #fff;
.container { .container {
height: 100vh; height: 100vh;
position: relative; position: relative;
@ -1833,10 +1858,10 @@ hr.border{
.side-menu-container{ .side-menu-container{
position: fixed; position: fixed;
left: 10px; left: 14px;
z-index: 111; z-index: 111;
height: 95vh; height: 97vh;
top: 18px; top: 14px;
//background: var(--primary); //background: var(--primary);
border-radius: 30px; border-radius: 30px;
background: linear-gradient(316deg, #17374b, var(--primary)); background: linear-gradient(316deg, #17374b, var(--primary));
@ -1858,7 +1883,7 @@ hr.border{
&:hover { &:hover {
background-color: rgba(0, 0, 0, 0.7) !important; background-color: rgba(0, 0, 0, 0.2) !important;
} }
} }
@ -2399,3 +2424,11 @@ hr.border{
font-weight: 300; font-weight: 300;
} }
} }
.ant-dropdown .ant-dropdown-menu .ant-dropdown-menu-item,
.ant-dropdown-menu-submenu .ant-dropdown-menu .ant-dropdown-menu-item,
.ant-dropdown .ant-dropdown-menu .ant-dropdown-menu-submenu-title,
.ant-dropdown-menu-submenu .ant-dropdown-menu .ant-dropdown-menu-submenu-title {
padding: 0;
}

BIN
public/img/gedung.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 KiB

View File

@ -10,6 +10,7 @@ import {getCookie} from "cookies-next";
import {LoadingOutlined} from "@ant-design/icons"; import {LoadingOutlined} from "@ant-design/icons";
import usePreventBackNavigation from "@/hooks/usePreventBackNavigation"; import usePreventBackNavigation from "@/hooks/usePreventBackNavigation";
import CheckAuth from "@/components/util/CheckAuth"; import CheckAuth from "@/components/util/CheckAuth";
import packageJson from "@@/package.json";
export default function Login() { export default function Login() {
@ -37,10 +38,8 @@ export default function Login() {
return setErrorLogin(result.message); return setErrorLogin(result.message);
} }
await setAuth(result.token, result.users.id); await setAuth(result.token, result.userId);
localStorage.setItem('user_roleId', result.roleId);
let userData = await API.GET('/ref/user/' + result?.users?.id)
localStorage.setItem('user_roleId', userData?.result?.users?.idBranch);
router.push("/main/dashboard"); router.push("/main/dashboard");
}; };
@ -104,7 +103,7 @@ export default function Login() {
/> />
</form> </form>
<div className={"text-muted fw-light fs-7"} style={{position:"absolute",bottom:'20px',left:'20px'}}>v0.1.0</div> <div className={"text-muted fw-light fs-7"} style={{position:"absolute",bottom:'20px',left:'20px'}}>v{packageJson.version}</div>
</div> </div>
</div> </div>
</div> </div>

View File

@ -0,0 +1,77 @@
import React, {useEffect, useState} from "react";
import {Col, Dropdown, Row} from "antd";
import "./style.scss";
import {CheckCircleOutlined, CloseCircleOutlined, ContainerOutlined, EditOutlined, SettingOutlined, UserOutlined} from "@ant-design/icons";
export default function CardAgen({data,modalOpen}) {
return (<Row gutter={[20, 5]}>
{data && data?.map((v, k) => {
let items = [
{
key: '1',
label: (<button onClick={() => modalOpen(v?.karyawanId,'edit')} className="dropdown-item w-full">
<EditOutlined className="text-muted me-2"/>Ubah
</button>),
},
{
key: '2',
label: (<button onClick={() => modalOpen(v?.karyawanId,'detail')} className="dropdown-item w-full">
<ContainerOutlined className="text-muted me-2"/>Detail
</button>),
}
]
return (<Col key={k} xxl={6} span={8}>
<div className="card-karyawan">
<div className={"content-title"}>
<div className={"name"}>{v?.karyawanNm}</div>
<div className={"position"}>{v?.jabatanNm}</div>
</div>
<div className={"container-karyawan"}>
<div className={"content-karyawan"}>
<div className={"title"}>ID</div>
<div className={"value"}>{v?.nik}</div>
</div>
<div className={"content-karyawan"}>
<div className={"title"}>username</div>
<div className={"value"}>{v?.username}</div>
</div>
<div className={"content-karyawan"}>
<div className={"title"}>telpon/hp</div>
<div className={"value"}>{v?.phoneNo}</div>
</div>
</div>
<div className={"content-btn"}>
<Dropdown trigger={["click"]} placement="bottomLeft" arrow menu={{items}}>
<button type="button" className="btn btn-primary my-1 btn-sm" data-bs-toggle="dropdown">
<SettingOutlined/> Pengaturan
</button>
</Dropdown>
</div>
<div className={`content-status ${(v?.isActive) ? 'bg-success-light' : 'bg-danger-light'}`}>
{(v?.isActive) ? <div className={"status-flag text-success"}>
<CheckCircleOutlined className="text-success me-2"/>
<div>Aktif</div>
</div> : <div className={"status-flag text-danger"}>
<CloseCircleOutlined className="text-danger me-2"/>
<div>Tidak Aktif</div>
</div>}
</div>
<div className={"content-foto"}>
{(v?.profilePict) ? <img alt={"agen"} className={"img"} src={v?.profilePict}/> : <UserOutlined className={"icon"}/>}
</div>
</div>
</Col>
)
})}
</Row>)
}

View File

@ -0,0 +1,96 @@
import {Col, Modal, Row, Spin} from "antd";
import {CloseOutlined} from "@ant-design/icons";
import Input from "@/components/util/Input";
import React, {useEffect, useState} from "react";
import {useForm} from "react-hook-form";
import {Helper} from "@/lib/Helper";
import {DropdownAPI} from "@/lib/DropdownAPI";
export default function FormAgen({modalStatus, actClose, data, loadingModal,jenis}) {
const [viewReadonly, setViewReadonly] = useState(false)
const [dropdownLoading, setDropdownLoading] = useState(false)
const [dropdown, setDropdown] = useState(
{
roleId : [],
branchId : [],
jabatanId : [],
jenisKelaminId : [],
agamaId : [],
statusAgenId : []
}
)
const {
register, setValue, watch, getValues, formState: {errors},
} = useForm();
const dropdownInit = async () => {
setDropdownLoading(true)
let listRole = await DropdownAPI.role();
setDropdownLoading(false)
}
const TypeAction = () => {
if(jenis === 'detail'){
return(
<></>
)
}else{
return(
<button className="btn btn-primary btn-sm">Simpan</button>
)
}
}
useEffect(() => {
if (jenis === 'detail'){
setViewReadonly(true);
}else{
setViewReadonly(false);
}
}, [jenis]);
useEffect(() => {
if (data){
setValue('karyawanNm',data?.karyawanNm)
setValue('roleId',data?.roleId)
}
}, [data]);
useEffect(() => {
dropdownInit()
}, []);
return (<Modal centered closeIcon={false} open={modalStatus} width={"800px"} footer={null}>
<Spin spinning={loadingModal}>
<div className="btn btn-circle btn-light-primary zn-close" onClick={actClose}>
<CloseOutlined/>
</div>
<div className="px-6 py-4 cardDark">
<div className="fs-6 fw-bolder text-uppercase text-primary">Data Agen</div>
<div className="fs-7 fw-ligth text-muted">Data Agen</div>
<div className="separator my-3"/>
<div style={{overflowY:'auto',overflowX:'hidden',height:'600px'}} className="my-4 p-2">
<Row gutter={15}>
<Col span={6}>
<Input.Text title={"Nama Agen"} name={"karyawanNm"} minlength={"15"} maxlength={"100"} setReadonly={viewReadonly} required register={register} error={errors}/>
</Col>
</Row>
</div>
</div>
<div className="card-footer text-right py-3 px-5 zn-bg-modal">
<TypeAction/>
</div>
</Spin>
</Modal>
)
}

View File

@ -0,0 +1,251 @@
"use client"
import SearchInput from "@/components/util/SearchInput";
import WrapperContent from "@/components/util/WrapperContent";
import React, {useEffect, useState} from "react";
import CardAgen from "./CardAgen";
import {API} from "@/lib/API";
import notifStore from "@/store/notifStore";
import FormAgen from "@/app/main/daftarAgen/FormAgen";
import {ReloadOutlined} from "@ant-design/icons";
export default function DaftarAgen() {
const {notifOpen} = notifStore()
const [searchText, setSearchText] = useState(null)
const [dataAgen, setDataAgen] = useState([])
const [modalAgen, setModalAgen] = useState({
loadingModal: false, modalStatus: false, jenis: null, data: []
})
const handleSearch = (event) => {
const handler = setTimeout(() => {
setSearchText(event.target.value);
}, 1000);
return () => {
clearTimeout(handler);
};
};
const getAgen = async () => {
// let res = await API.GET('/ref/karyawan')
// if (res.status !== 200) {
// notifOpen("Gagal", res.result.message, "danger");
// return false
// }
let dummyData = {
"page": 0,
"size": 1,
"totalPages": 1,
"totalElements": 1,
"data": [
{
"karyawanId": 3,
"profilePict": null,
"username": "karyawanbackend",
"roleId": 4,
"roleNm": "Backend Developer",
"isActive": true,
"statusUserId": 2,
"statusUserNm": "Baru",
"jenisKelaminId": 1,
"jenisKelaminNm": "Laki-laki",
"email": "karyawanbackend@basys.co.id",
"phoneNo": "085300000003",
"companyId": 1,
"companyNm": "PT Bayu Sinergi Solusi",
"branchId": 1,
"branchCode": "001",
"branchNm": "Cabang Utama PT. Basys",
"divisiId": 1,
"divisiNm": "Backend Developer",
"jabatanId": 1,
"jabatanNm": "Outlet Alzam Store",
"statusAgenId": 1,
"statusAgenNm": "Pegawai Tetap",
"spvId": null,
"spvUsername": null,
"spvNm": null,
"recognizeId": "e12ff5c2-919b-4e40-8841-3b876696d619",
"hasRecognized": false,
"nik": "0000000000000002",
"nip": "0000000000000002",
"npwp": 2,
"birthPlace": "Kota Bandung",
"birthDt": "2000-09-17",
"address": "Jalan Jalan 2",
"tanggalBekerja": "2023-01-01",
"agamaId": 1,
"agamaNm": "Islam",
"tanggalBerhentiBekerja": null,
"userCrtId": 1,
"userCrtNm": "Webmin Basys",
"userCrtUsername": "webminbasys",
"userUpdtId": 1,
"userUpdtNm": "Webmin Basys",
"userUpdtUsername": "webminbasys",
"crtdt": "2024-07-31 11:28:26",
"uptdt": "2024-07-31 11:28:26",
"karyawanNm": "Nama Agen A"
}, {
"karyawanId": 3,
"profilePict": null,
"username": "karyawanbackend",
"roleId": 4,
"roleNm": "Backend Developer",
"isActive": true,
"statusUserId": 2,
"statusUserNm": "Baru",
"jenisKelaminId": 1,
"jenisKelaminNm": "Laki-laki",
"email": "karyawanbackend@basys.co.id",
"phoneNo": "085300000003",
"companyId": 1,
"companyNm": "PT Bayu Sinergi Solusi",
"branchId": 1,
"branchCode": "001",
"branchNm": "Cabang Utama PT. Basys",
"divisiId": 1,
"divisiNm": "Backend Developer",
"jabatanId": 1,
"jabatanNm": "Outlet Alzam Store",
"statusAgenId": 1,
"statusAgenNm": "Pegawai Tetap",
"spvId": null,
"spvUsername": null,
"spvNm": null,
"recognizeId": "e12ff5c2-919b-4e40-8841-3b876696d619",
"hasRecognized": false,
"nik": "0000000000000002",
"nip": "0000000000000002",
"npwp": 2,
"birthPlace": "Kota Bandung",
"birthDt": "2000-09-17",
"address": "Jalan Jalan 2",
"tanggalBekerja": "2023-01-01",
"agamaId": 1,
"agamaNm": "Islam",
"tanggalBerhentiBekerja": null,
"userCrtId": 1,
"userCrtNm": "Webmin Basys",
"userCrtUsername": "webminbasys",
"userUpdtId": 1,
"userUpdtNm": "Webmin Basys",
"userUpdtUsername": "webminbasys",
"crtdt": "2024-07-31 11:28:26",
"uptdt": "2024-07-31 11:28:26",
"karyawanNm": "Nama Agen A"
}, {
"karyawanId": 3,
"profilePict": null,
"username": "karyawanbackend",
"roleId": 4,
"roleNm": "Backend Developer",
"isActive": true,
"statusUserId": 2,
"statusUserNm": "Baru",
"jenisKelaminId": 1,
"jenisKelaminNm": "Laki-laki",
"email": "karyawanbackend@basys.co.id",
"phoneNo": "085300000003",
"companyId": 1,
"companyNm": "PT Bayu Sinergi Solusi",
"branchId": 1,
"branchCode": "001",
"branchNm": "Cabang Utama PT. Basys",
"divisiId": 1,
"divisiNm": "Backend Developer",
"jabatanId": 1,
"jabatanNm": "Outlet Alzam Store",
"statusAgenId": 1,
"statusAgenNm": "Pegawai Tetap",
"spvId": null,
"spvUsername": null,
"spvNm": null,
"recognizeId": "e12ff5c2-919b-4e40-8841-3b876696d619",
"hasRecognized": false,
"nik": "0000000000000002",
"nip": "0000000000000002",
"npwp": 2,
"birthPlace": "Kota Bandung",
"birthDt": "2000-09-17",
"address": "Jalan Jalan 2",
"tanggalBekerja": "2023-01-01",
"agamaId": 1,
"agamaNm": "Islam",
"tanggalBerhentiBekerja": null,
"userCrtId": 1,
"userCrtNm": "Webmin Basys",
"userCrtUsername": "webminbasys",
"userUpdtId": 1,
"userUpdtNm": "Webmin Basys",
"userUpdtUsername": "webminbasys",
"crtdt": "2024-07-31 11:28:26",
"uptdt": "2024-07-31 11:28:26",
"karyawanNm": "Nama Agen A"
}
]
}
// setDataAgen(res.result.data)
setDataAgen(dummyData.data)
}
const modalOpen = async (id, type) => {
setModalAgen(prev => ({
...prev, modalStatus: true, jenis: type, loadingModal: true
}));
let res = await API.GET('/ref/karyawan/' + id)
setModalAgen(prev => ({
...prev, data: res.result, loadingModal: false
}));
}
useEffect(() => {
getAgen()
}, []);
return (<>
<WrapperContent>
<div className="containers">
<div className="headContent">
<div className="containerTitle">
<div className="breadCrumb">
<div className="text">Data Agen</div>
<div className="text">Daftar Agen</div>
</div>
<div className="title text-dark-grey left">Daftar Agen</div>
</div>
<div className="filter mb-3">
<SearchInput handleSearch={handleSearch} style={{marginRight: "10px"}}/>
<button type="button" className="btn btn-primary" onClick={() => modalOpen(null, 'tambah')}>Registrasi Agen</button>
<button
type="button"
className="btn btn-circle btn-light-primary"
onClick={() => getAgen()}
>
<ReloadOutlined/>
</button>
</div>
</div>
<div className="bodyContent">
<CardAgen modalOpen={modalOpen} data={dataAgen}/>
</div>
</div>
</WrapperContent>
<FormAgen
loadingModal={modalAgen?.loadingModal}
modalStatus={modalAgen?.modalStatus}
jenis={modalAgen?.jenis}
actClose={() => {
setModalAgen(prev => ({
...prev, modalStatus: false, loadingModal: false
}));
}}
data={modalAgen?.data}/>
</>)
}

View File

@ -0,0 +1,80 @@
.card-karyawan{
background: #fff;
padding: 10px;
border-radius: 25px;
box-shadow: 0px 8px 50px rgb(0 0 0 / 8%);
margin-top: 15px;
transition: 0.5s ease-in-out;
&:hover{
box-shadow: 0px 25px 50px rgb(0 0 0 / 15%);
}
.content-title{
background: #f7f7f7;
padding: 10px 15px;
border-radius: 20px;
width: 60%;
.name{
font-weight: 600;
font-size: 14px;
color: var(--dark);
}
.position{
font-weight: 400;
font-size: 12px;
color: var(--text-muted);
}
}
.container-karyawan{
padding: 15px;
}
.content-karyawan{
margin-bottom: 15px;
.title{
font-size: 12px;
font-weight: 400;
color: var(--text-muted);
}
.value{
font-size: 13px;
font-weight: 500;
color: #0c111c;
}
}
.content-status{
position: absolute;
top: 5px;
right: 20px;
border-radius: 20px;
padding: 3px 30px 3px 10px;
.status-flag{
display: flex;
gap: 0px;
font-weight: 400;
font-size: 12px;
}
}
.content-foto{
position: absolute;
bottom: 10px;
right: 5px;
.icon{
font-size: 180px;
color: rgba(0, 0, 0, 0.05);
}
.img{
width: 150px;
object-fit: contain;
}
}
}

View File

@ -0,0 +1,80 @@
import React, {useEffect, useState} from "react";
import {Col, Dropdown, Row} from "antd";
import "./style.scss";
import {CheckCircleOutlined, CloseCircleOutlined, ContainerOutlined, DeleteOutlined, EditOutlined, SettingOutlined, UserOutlined} from "@ant-design/icons";
export default function CardOutlet({data,modalOpen, deleteData}) {
return (<Row gutter={[20, 5]}>
{data && data?.map((v, k) => {
let items = [
{
key: '1',
label: (<button onClick={() => modalOpen(v?.branchId,'edit')} className="dropdown-item w-full">
<EditOutlined className="text-muted me-2"/>Ubah
</button>),
},
{
key: '2',
label: (<button onClick={() => modalOpen(v?.branchId,'detail')} className="dropdown-item w-full">
<ContainerOutlined className="text-muted me-2"/>Detail
</button>),
},
{
key: '3',
label: (<button onClick={() => deleteData(v?.branchId)} className="dropdown-item w-full">
<DeleteOutlined className="text-muted me-2"/>Hapus
</button>),
}
]
return (<Col key={k} xxl={8} span={12}>
<div className="card-branch">
<div className={`content-status ${(v?.isActive) ? 'bg-success-light' : 'bg-danger-light'}`}>
{(v?.isActive) ? <div className={"status-flag text-success"}>
<CheckCircleOutlined className="text-success me-2"/>
<div>Aktif</div>
</div> : <div className={"status-flag text-danger"}>
<CloseCircleOutlined className="text-danger me-2"/>
<div>Tidak Aktif</div>
</div>}
</div>
<div>
<div className={"content-title"}>
<div className={"fs-7 text-muted text-uppercase"}>Perusahaan A</div>
<div className={"name"}>Nama Outlet A</div>
<div className={"position"}>{v?.address}</div>
</div>
<div className={"content-btn"}>
<Dropdown trigger={["click"]} placement="bottomLeft" arrow menu={{items}}>
<button type="button" className="btn btn-primary my-1 btn-sm" data-bs-toggle="dropdown">
<SettingOutlined/> Pengaturan
</button>
</Dropdown>
</div>
</div>
<div className={"content-detail"}>
<div className={"content-branch"}>
<div className={"title"}>Jenis Outlet</div>
<div className={"value"}>Jenis Outlet A</div>
</div>
<div className={"content-branch"}>
<div className={"title"}>Penanggungjawab</div>
<div className={"value"}>Zamzam Nurzaman</div>
</div>
</div>
</div>
</Col>
)
})}
</Row>)
}

View File

@ -0,0 +1,194 @@
import { Col, Modal, Row, Spin } from "antd";
import { CloseOutlined } from "@ant-design/icons";
import Input from "@/components/util/Input";
import React, { useEffect, useState } from "react";
import { useForm } from "react-hook-form";
import dynamic from "next/dynamic";
let timer;
export default function FormOutlet({
modalStatus,
actClose, actStoreData,
data,
loadingModal,
jenis,
}) {
const [viewReadonly, setViewReadonly] = useState(false);
const [latlon, setLatlon] = useState(null);
const [showMap, setShowMap] = useState(false);
const {
register,
setValue,
watch,
getValues,
reset,
handleSubmit,
formState: { errors },
} = useForm();
const TypeAction = () => {
if (jenis === "detail") {
return <></>;
} else {
return <button className="btn btn-primary btn-sm" onClick={handleSubmit(onSubmit)}>Simpan</button>;
}
};
const onSubmit = async (data) => {
actStoreData(data)
}
useEffect(() => {
if (jenis === "detail") {
setViewReadonly(true);
} else {
setViewReadonly(false);
}
}, [jenis]);
useEffect(() => {
if (data) {
console.log(data)
setValue('address',data.address)
setValue('picNm', data.picNm)
setValue('phoneNo', data.phoneNo)
setValue('lat', data.lat)
setValue('lon', data.lon)
setValue('companyNm', data.companyNm)
setValue('wilayahId', data?.wilayah?.wilayahId)
setValue('companyId', data.companyId)
setLatlon({
lat: data.lat,
lon: data.lon
})
}
}, [data]);
useEffect(() => {
if (modalStatus === true){
setLatlon(null)
reset()
timer = setTimeout(() => {
setShowMap(true);
}, 2000);
}
}, [modalStatus]);
useEffect(() => {
setLatlon({
lat: watch('lat'),
lon:watch('lon')
})
}, [watch('lat')]);
return (
<Modal
centered
closeIcon={false}
open={modalStatus}
width={"600px"}
footer={null}
>
<Spin spinning={loadingModal}>
<div
className="btn btn-circle btn-light-primary zn-close"
onClick={actClose}
>
<CloseOutlined />
</div>
<div className="px-6 py-4 cardDark">
<div className="fs-6 fw-bolder text-uppercase text-primary">
Data Outlet
</div>
<div className="fs-7 fw-ligth text-muted">Data Outlet</div>
<div className="separator my-3" />
<div
style={{ overflowY: "auto", overflowX: "hidden", height: "600px" }}
className="my-4 p-2"
>
<form onSubmit={handleSubmit(onSubmit)}>
<Row gutter={15}>
<Col span={24}>
<Input.Text
title={"Nama Outlet"}
name={"companyNm"}
minlength={"15"}
maxlength={"200"}
setReadonly={viewReadonly}
required
register={register}
error={errors}
/>
</Col>
<Col span={24}>
<Input.Textarea
title={"Alamat"}
name={"address"}
maxlength={"200"}
setReadonly={viewReadonly}
required
register={register}
error={errors}
/>
</Col>
<Col span={24}>
<Input.Number
title={"No HP"}
name={"phoneNo"}
minlength={"10"}
maxlength={"15"}
setReadonly={viewReadonly}
required
register={register}
error={errors}
/>
</Col>
<Col span={24}>
<Input.Text
title={"Penanggungjawab"}
name={"picNm"}
minlength={"15"}
maxlength={"200"}
setReadonly={viewReadonly}
required
register={register}
error={errors}
/>
</Col>
<Col span={24}>
<Input.SelectRemote
title={"Wilayah"}
name={"wilayahId"}
endPoint={"/ref/wilayah"}
val={getValues("wilayahId")}
setValue={setValue}
setReadonly={viewReadonly}
required
register={register}
error={errors}
/>
</Col>
</Row>
</form>
</div>
</div>
<div className="card-footer text-right py-3 px-5 zn-bg-modal">
<TypeAction />
</div>
</Spin>
</Modal>
);
}

View File

@ -0,0 +1,185 @@
"use client"
import SearchInput from "@/components/util/SearchInput";
import WrapperContent from "@/components/util/WrapperContent";
import React, {useEffect, useState} from "react";
import CardOutlet from "./CardOutlet";
import {API} from "@/lib/API";
import notifStore from "@/store/notifStore";
import FormOutlet from "./FormOutlet";
import {ReloadOutlined} from "@ant-design/icons";
import {Pagination} from "antd";
import confirmStore from "@/store/confirmStore";
import "./style.scss"
export default function DaftarOutlet() {
const {notifOpen} = notifStore()
const {confirmOpen, confirmClose, setConfirmLoading} = confirmStore();
const [searchText, setSearchText] = useState(null)
const [currentPage, setCurrentPage] = useState(1)
const [dataCabang, setDataCabang] = useState([])
const [modalCabang, setModalCabang] = useState({
loadingModal:false,
modalStatus:false,
jenis:null,
data:[]
})
const handleSearch = (event) => {
const handler = setTimeout(() => {
setSearchText(event.target.value);
}, 1000);
return () => {
clearTimeout(handler);
};
};
const getCabang = async (page,size) => {
let setPage = (page) ? page : 0;
let setSize = (size) ? size : 6;
let res = await API.GET(`/ref/branch?page=${setPage}&size=${setSize}`)
if(res.status !== 200){
notifOpen("Gagal", res.result.message, "danger");
return false
}
setDataCabang(res.result)
}
const modalOpen = async (id,type) => {
if (type === 'tambah'){
setModalCabang(prev => ({
...prev,
modalStatus: true,
jenis: type,
data:null
}));
}else{
setModalCabang(prev => ({
...prev,
modalStatus: true,
jenis: type,
loadingModal: true
}));
let res = await API.GET('/ref/branch/' + id)
setModalCabang(prev => ({
...prev,
data: res.result,
loadingModal: false
}));
}
}
const storeData = async (data) => {
let res = await API.POST('/ref/branch', data)
if (res.status === 200) {
setModalCabang(prev => ({
...prev,
modalStatus: false,
}));
notifOpen("Berhasil", res.result.message);
await getCabang();
setCurrentPage(1)
} else {
notifOpen("Gagal", res.result.message, "danger");
}
}
const deleteData = async (id) => {
confirmOpen("Hapus Data", "Yakin Hapus Data Ini", async () => {
setConfirmLoading(true);
let response = await API.DELETE(`/ref/branch/${id}`);
if (response.status === 200) {
notifOpen("Berhasil", "berhasil hapus data");
await getCabang();
setConfirmLoading(false);
confirmClose();
setCurrentPage(1)
} else {
console.log(response);
setConfirmLoading(false);
notifOpen("Gagal", response.result.message, "danger");
confirmClose();
}
});
};
const onChangePage = (page, pageSize) => {
console.log({page, pageSize})
setCurrentPage(page)
getCabang(page-1,pageSize)
}
const onChangePageSize = (current, size) => {
console.log({current, size})
}
useEffect(() => {
getCabang()
}, []);
return(<>
<WrapperContent>
<div className="containers">
<div className="headContent">
<div className="containerTitle">
<div className="breadCrumb">
<div className="text">Data Outlet</div>
<div className="text">Daftar Outlet</div>
</div>
<div className="title text-dark-grey left">Daftar Outlet</div>
</div>
<div className="filter mb-3">
<SearchInput handleSearch={handleSearch} style={{marginRight: "10px"}}/>
<button type="button" className="btn btn-primary" onClick={() => modalOpen(null,'tambah')}>Registrasi Outlet</button>
<button
type="button"
className="btn btn-circle btn-light-primary"
onClick={() => getCabang()}
>
<ReloadOutlined/>
</button>
</div>
</div>
<div className="bodyContent">
<CardOutlet modalOpen={modalOpen} deleteData={deleteData} data={dataCabang?.data}/>
<Pagination
style={{marginTop:'30px',borderRadius:'20px',padding:'10px'}}
align="center"
total={dataCabang?.totalElements}
showTotal={(total,range) => `${range[0]}-${range[1]} dari ${total} Data`}
defaultPageSize={6}
pageSizeOptions={['6', '12', '24']}
showSizeChanger={true}
defaultCurrent={1}
current={currentPage}
onChange={onChangePage}
onShowSizeChange={onChangePageSize}
/>
</div>
</div>
</WrapperContent>
<FormOutlet
loadingModal={modalCabang?.loadingModal}
modalStatus={modalCabang?.modalStatus}
jenis={modalCabang?.jenis}
actClose={()=>{
setModalCabang(prev => ({
...prev,
modalStatus: false,
loadingModal: false
}));
}}
actStoreData={storeData}
data={modalCabang?.data} />
</>)
}

View File

@ -0,0 +1,95 @@
.card-branch{
background: #fff;
padding: 10px 10px;
border-radius: 25px;
box-shadow: 0px 8px 50px rgb(0 0 0 / 8%);
margin-top: 15px;
transition: 0.5s ease-in-out;
position: relative;
display: flex;
justify-content: space-between;
&:hover{
box-shadow: 0px 25px 50px rgb(0 0 0 / 15%);
}
.content-detail{
text-align: right;
background: #F1F1F1;
padding: 30px;
border-radius: 20px;
}
.content-title{
margin-bottom: 20px;
margin-top: 10px;
padding: 10px 15px;
border-radius: 20px;
width: 100%;
.name{
margin-top: 10px;
font-weight: 700;
font-size: 14px;
color: var(--dark);
text-transform: uppercase;
}
.position{
font-weight: 300;
font-size: 12px;
}
}
.container-branch{
padding: 15px;
}
.content-branch{
margin-bottom: 15px;
.title{
font-size: 12px;
font-weight: 400;
color: var(--text-muted);
}
.value{
font-size: 13px;
font-weight: 500;
color: #0c111c;
}
}
.content-status{
position: absolute;
top: -10px;
left: 30px;
border-radius: 20px;
padding: 3px 30px 3px 10px;
.status-flag{
display: flex;
gap: 0px;
font-weight: 400;
font-size: 12px;
}
}
.content-foto{
position: absolute;
top: 0px;
right: 0px;
border-radius: 25px;
.icon{
font-size: 180px;
color: rgba(0, 0, 0, 0.05);
}
.img{
width: 170px;
height: 250px;
object-fit: cover;
border-radius: 0 25px 100px 100px;
}
}
}

View File

@ -0,0 +1,79 @@
import React, {useEffect, useState} from "react";
import {Col, Dropdown, Row} from "antd";
import "./style.scss";
import {CheckCircleOutlined, CloseCircleOutlined, ContainerOutlined, DeleteOutlined, EditOutlined, SettingOutlined, UserOutlined} from "@ant-design/icons";
export default function CardPerusahaan({data,modalOpen, deleteData}) {
return (<Row gutter={[20, 5]}>
{data && data?.map((v, k) => {
let items = [
{
key: '1',
label: (<button onClick={() => modalOpen(v?.companyId,'edit')} className="dropdown-item w-full">
<EditOutlined className="text-muted me-2"/>Ubah
</button>),
},
{
key: '2',
label: (<button onClick={() => modalOpen(v?.companyId,'detail')} className="dropdown-item w-full">
<ContainerOutlined className="text-muted me-2"/>Detail
</button>),
},
{
key: '3',
label: (<button onClick={() => deleteData(v?.companyId)} className="dropdown-item w-full">
<DeleteOutlined className="text-muted me-2"/>Hapus
</button>),
}
]
return (<Col key={k} xxl={8} span={12}>
<div className="card-company">
<div className={"content-title"}>
<div className={"name"}>{v?.companyNm}</div>
<div className={"position"}>{v?.address}</div>
</div>
<div className={"container-company"}>
<div className={"content-company"}>
<div className={"title"}>Jenis Perusahaan</div>
<div className={"value"}>PT (Perseroan Terbatas)</div>
</div>
<div className={"content-company"}>
<div className={"title"}>Jenis Bisnis</div>
<div className={"value"}>Perdagangan</div>
</div>
</div>
<div className={"content-btn"}>
<Dropdown trigger={["click"]} placement="bottomLeft" arrow menu={{items}}>
<button type="button" className="btn btn-primary my-1 btn-sm" data-bs-toggle="dropdown">
<SettingOutlined/> Pengaturan
</button>
</Dropdown>
</div>
<div className={`content-status ${(v?.isActive) ? 'bg-success-light' : 'bg-danger-light'}`}>
{(v?.isActive) ? <div className={"status-flag text-success"}>
<CheckCircleOutlined className="text-success me-2"/>
<div>Aktif</div>
</div> : <div className={"status-flag text-danger"}>
<CloseCircleOutlined className="text-danger me-2"/>
<div>Tidak Aktif</div>
</div>}
</div>
<div className={"content-foto"}>
{(v?.profilePict) ? <img alt={"company"} className={"img"} src={v?.profilePict}/> : <img className={"img"} src={"/img/gedung.jpeg"}/>}
</div>
</div>
</Col>
)
})}
</Row>)
}

View File

@ -0,0 +1,193 @@
import { Col, Modal, Row, Spin } from "antd";
import { CloseOutlined } from "@ant-design/icons";
import Input from "@/components/util/Input";
import React, { useEffect, useState } from "react";
import { useForm } from "react-hook-form";
let timer;
export default function FormPerusahaan({
modalStatus,
actClose, actStoreData,
data,
loadingModal,
jenis,
}) {
const [viewReadonly, setViewReadonly] = useState(false);
const [latlon, setLatlon] = useState(null);
const [showMap, setShowMap] = useState(false);
const {
register,
setValue,
watch,
getValues,
reset,
handleSubmit,
formState: { errors },
} = useForm();
const TypeAction = () => {
if (jenis === "detail") {
return <></>;
} else {
return <button className="btn btn-primary btn-sm" onClick={handleSubmit(onSubmit)}>Simpan</button>;
}
};
const onSubmit = async (data) => {
actStoreData(data)
}
useEffect(() => {
if (jenis === "detail") {
setViewReadonly(true);
} else {
setViewReadonly(false);
}
}, [jenis]);
useEffect(() => {
if (data) {
console.log(data)
setValue('address',data.address)
setValue('picNm', data.picNm)
setValue('phoneNo', data.phoneNo)
setValue('lat', data.lat)
setValue('lon', data.lon)
setValue('companyNm', data.companyNm)
setValue('wilayahId', data?.wilayah?.wilayahId)
setValue('companyId', data.companyId)
setLatlon({
lat: data.lat,
lon: data.lon
})
}
}, [data]);
useEffect(() => {
if (modalStatus === true){
setLatlon(null)
reset()
timer = setTimeout(() => {
setShowMap(true);
}, 2000);
}
}, [modalStatus]);
useEffect(() => {
setLatlon({
lat: watch('lat'),
lon:watch('lon')
})
}, [watch('lat')]);
return (
<Modal
centered
closeIcon={false}
open={modalStatus}
width={"600px"}
footer={null}
>
<Spin spinning={loadingModal}>
<div
className="btn btn-circle btn-light-primary zn-close"
onClick={actClose}
>
<CloseOutlined />
</div>
<div className="px-6 py-4 cardDark">
<div className="fs-6 fw-bolder text-uppercase text-primary">
Data Perusahaan
</div>
<div className="fs-7 fw-ligth text-muted">Data Perusahaan</div>
<div className="separator my-3" />
<div
style={{ overflowY: "auto", overflowX: "hidden", height: "600px" }}
className="my-4 p-2"
>
<form onSubmit={handleSubmit(onSubmit)}>
<Row gutter={15}>
<Col span={24}>
<Input.Text
title={"Nama Perusahaan"}
name={"companyNm"}
minlength={"15"}
maxlength={"200"}
setReadonly={viewReadonly}
required
register={register}
error={errors}
/>
</Col>
<Col span={24}>
<Input.Textarea
title={"Alamat"}
name={"address"}
maxlength={"200"}
setReadonly={viewReadonly}
required
register={register}
error={errors}
/>
</Col>
<Col span={24}>
<Input.Number
title={"No HP"}
name={"phoneNo"}
minlength={"10"}
maxlength={"15"}
setReadonly={viewReadonly}
required
register={register}
error={errors}
/>
</Col>
<Col span={24}>
<Input.Text
title={"Penanggungjawab"}
name={"picNm"}
minlength={"15"}
maxlength={"200"}
setReadonly={viewReadonly}
required
register={register}
error={errors}
/>
</Col>
<Col span={24}>
<Input.SelectRemote
title={"Wilayah"}
name={"wilayahId"}
endPoint={"/ref/wilayah"}
val={getValues("wilayahId")}
setValue={setValue}
setReadonly={viewReadonly}
required
register={register}
error={errors}
/>
</Col>
</Row>
</form>
</div>
</div>
<div className="card-footer text-right py-3 px-5 zn-bg-modal">
<TypeAction />
</div>
</Spin>
</Modal>
);
}

View File

@ -0,0 +1,170 @@
"use client"
import SearchInput from "@/components/util/SearchInput";
import WrapperContent from "@/components/util/WrapperContent";
import React, {useEffect, useState} from "react";
import CardPerusahaan from "./CardPerusahaan";
import {API} from "@/lib/API";
import notifStore from "@/store/notifStore";
import {ReloadOutlined} from "@ant-design/icons";
import {Pagination} from "antd";
import confirmStore from "@/store/confirmStore";
import FormPerusahaan from "@/app/main/daftarPerusahaan/FormPerusahaan";
export default function DaftarPerusahaan() {
const {notifOpen} = notifStore()
const {confirmOpen, confirmClose, setConfirmLoading} = confirmStore();
const [searchText, setSearchText] = useState(null)
const [currentPage, setCurrentPage] = useState(1)
const [dataPerusahaan, setDataPerusahaan] = useState([])
const [modalPerusahaan, setModalPerusahaan] = useState({
loadingModal: false, modalStatus: false, jenis: null, data: []
})
const handleSearch = (event) => {
const handler = setTimeout(() => {
setSearchText(event.target.value);
}, 1000);
return () => {
clearTimeout(handler);
};
};
const getPerusahaan = async (page, size) => {
let setPage = (page) ? page : 0;
let setSize = (size) ? size : 6;
let res = await API.GET(`/ref/company?page=${setPage}&size=${setSize}`)
if (res.status !== 200) {
notifOpen("Gagal", res.result.message, "danger");
return false
}
setDataPerusahaan(res.result)
}
const modalOpen = async (id, type) => {
if (type === 'tambah') {
setModalPerusahaan(prev => ({
...prev, modalStatus: true, jenis: type, data: null
}));
} else {
setModalPerusahaan(prev => ({
...prev, modalStatus: true, jenis: type, loadingModal: true
}));
let res = await API.GET('/ref/company/' + id)
setModalPerusahaan(prev => ({
...prev, data: res.result, loadingModal: false
}));
}
}
const storeData = async (data) => {
let res = await API.POST('/ref/company', data)
if (res.status === 200) {
setModalPerusahaan(prev => ({
...prev, modalStatus: false,
}));
notifOpen("Berhasil", res.result.message);
await getPerusahaan();
setCurrentPage(1)
} else {
notifOpen("Gagal", res.result.message, "danger");
}
}
const deleteData = async (id) => {
confirmOpen("Hapus Data", "Yakin Hapus Data Ini", async () => {
setConfirmLoading(true);
let response = await API.DELETE(`/ref/company/${id}`);
if (response.status === 200) {
notifOpen("Berhasil", "berhasil hapus data");
await getPerusahaan();
setConfirmLoading(false);
confirmClose();
setCurrentPage(1)
} else {
console.log(response);
setConfirmLoading(false);
notifOpen("Gagal", response.result.message, "danger");
confirmClose();
}
});
};
const onChangePage = (page, pageSize) => {
console.log({page, pageSize})
setCurrentPage(page)
getPerusahaan(page - 1, pageSize)
}
const onChangePageSize = (current, size) => {
console.log({current, size})
}
useEffect(() => {
getPerusahaan()
}, []);
return (<>
<WrapperContent>
<div className="containers">
<div className="headContent">
<div className="containerTitle">
<div className="breadCrumb">
<div className="text">Data Perusahaan</div>
<div className="text">Daftar Perusahaan</div>
</div>
<div className="title text-dark-grey left">Daftar Perusahaan</div>
</div>
<div className="filter mb-3">
<SearchInput handleSearch={handleSearch} style={{marginRight: "10px"}}/>
<button type="button" className="btn btn-primary" onClick={() => modalOpen(null, 'tambah')}>Registrasi Perusahaan</button>
<button
type="button"
className="btn btn-circle btn-light-primary"
onClick={() => getPerusahaan()}
>
<ReloadOutlined/>
</button>
</div>
</div>
<div className="bodyContent">
<CardPerusahaan modalOpen={modalOpen} deleteData={deleteData} data={dataPerusahaan?.data}/>
<Pagination
style={{marginTop: '30px', borderRadius: '20px', padding: '10px'}}
align="center"
total={dataPerusahaan?.totalElements}
showTotal={(total, range) => `${range[0]}-${range[1]} dari ${total} Data`}
defaultPageSize={6}
pageSizeOptions={['6', '12', '24']}
showSizeChanger={true}
defaultCurrent={1}
current={currentPage}
onChange={onChangePage}
onShowSizeChange={onChangePageSize}
/>
</div>
</div>
</WrapperContent>
<FormPerusahaan
loadingModal={modalPerusahaan?.loadingModal}
modalStatus={modalPerusahaan?.modalStatus}
jenis={modalPerusahaan?.jenis}
actClose={()=>{
setModalPerusahaan(prev => ({
...prev,
modalStatus: false,
loadingModal: false
}));
}}
actStoreData={storeData}
data={modalPerusahaan?.data} />
</>)
}

View File

@ -0,0 +1,84 @@
.card-company{
background: #fff;
padding: 15px 20px;
border-radius: 25px;
box-shadow: 0px 8px 50px rgb(0 0 0 / 8%);
margin-top: 15px;
transition: 0.5s ease-in-out;
position: relative;
&:hover{
box-shadow: 0px 25px 50px rgb(0 0 0 / 15%);
}
.content-title{
margin-top: 15px;
padding: 10px 15px;
border-radius: 20px;
width: 60%;
.name{
font-weight: 600;
font-size: 14px;
color: var(--dark);
text-transform: uppercase;
}
.position{
font-weight: 300;
font-size: 12px;
}
}
.container-company{
padding: 15px;
}
.content-company{
margin-bottom: 15px;
.title{
font-size: 12px;
font-weight: 400;
color: var(--text-muted);
}
.value{
font-size: 13px;
font-weight: 500;
color: #0c111c;
}
}
.content-status{
position: absolute;
top: -10px;
left: 30px;
border-radius: 20px;
padding: 3px 30px 3px 10px;
.status-flag{
display: flex;
gap: 0px;
font-weight: 400;
font-size: 12px;
}
}
.content-foto{
position: absolute;
top: 0px;
right: 0px;
border-radius: 25px;
.icon{
font-size: 180px;
color: rgba(0, 0, 0, 0.05);
}
.img{
width: 170px;
height: 250px;
object-fit: cover;
border-radius: 0 25px 100px 100px;
}
}
}

View File

@ -0,0 +1,107 @@
import dynamic from "next/dynamic";
import {useEffect, useState} from "react";
import {Helper} from "@/lib/Helper";
const ReactApexChart = dynamic(() => import("react-apexcharts"), {ssr: false});
export default function GrafikHari() {
const [grafik, setGrafik] = useState([]);
const [optionGrafik, setOptionGrafik] = useState(
{
chart: {
type: 'area',
},
dataLabels: {
enabled: false
},
stroke: {
curve: 'smooth'
},
fill: {
type: 'gradient',
gradient: {
shadeIntensity: 1,
opacityFrom: 0.1,
opacityTo: 0.9,
stops: [0, 100]
}
},
yaxis: {
labels: {
formatter: (val) => {
return Helper.numFormat(val)
},
},
},
tooltip: {
shared: true,
intersect: false,
y: {
formatter: function (y) {
if (typeof y !== "undefined") {
return Helper.numFormat(y);
}
return y;
}
}
},
colors: ['#0179c2', '#930200'],
legend: {
markers: {
fillColors: ['#0179c2', '#930200']
}
}
}
)
const getChart = (data) => {
let result = [
{
name: 'Transaksi',
data: [1000000,21000000,31000000,41000000,41000000,71000000, 11000000, 21000000, 31000000, 10000004, 10000004, 71000000, 1000000, 21000000, 31000000, 41000000, 41000000, 71000000, 11000000, 21000000, 31000000, 10000004, 10000004, 71000000]
},
]
let data_jam = [];
for (let i = 1; i <= 24; i++) {
data_jam.push(i + ':00')
}
setOptionGrafik(prev => ({
...prev,
xaxis: {
categories: data_jam
// categories: ["Jan",
// "Feb",
// "Mar",
// "Apr",
// "Mei",
// "Jun",
// "Jul",
// "Ags",
// "Sep",
// "Okt",
// "Nov",
// "Des"]
},
}));
setGrafik(result);
}
useEffect(() => {
getChart()
}, []);
return(<>
<div>
<div className={"fw-bold fs-6"}>Grafik Transaksi Hari Ini</div>
<div className={"fw-normal fs-7 mt-1 text-muted"}>Grafik Transaksi Hari Ini</div>
<ReactApexChart options={optionGrafik} series={grafik} type="area" height={450}/>
</div>
</>)
}

View File

@ -0,0 +1,106 @@
import dynamic from "next/dynamic";
import {useEffect, useState} from "react";
import {Helper} from "@/lib/Helper";
const ReactApexChart = dynamic(() => import("react-apexcharts"), {ssr: false});
export default function GrafikTahun() {
const [grafik, setGrafik] = useState([]);
const [optionGrafik, setOptionGrafik] = useState(
{
chart: {
type: 'area',
},
dataLabels: {
enabled: false
},
stroke: {
curve: 'smooth'
},
fill: {
type: 'gradient',
gradient: {
shadeIntensity: 1,
opacityFrom: 0.1,
opacityTo: 0.9,
stops: [0, 100]
}
},
yaxis: {
labels: {
formatter: (val) => {
return Helper.numFormat(val)
},
},
},
tooltip: {
shared: true,
intersect: false,
y: {
formatter: function (y) {
if (typeof y !== "undefined") {
return Helper.numFormat(y);
}
return y;
}
}
},
colors: ['#0179c2', '#930200'],
legend: {
markers: {
fillColors: ['#0179c2', '#930200']
}
}
}
)
const getChart = (data) => {
let result = [
{
name: 'Transaksi',
data: [1000000,21000000,31000000,41000000,41000000,71000000, 11000000, 21000000, 31000000, 10000004, 10000004, 71000000]
},
]
let data_jam = [];
for (let i = 1; i <= 24; i++) {
data_jam.push(i + ':00')
}
setOptionGrafik(prev => ({
...prev,
xaxis: {
categories: ["Jan",
"Feb",
"Mar",
"Apr",
"Mei",
"Jun",
"Jul",
"Ags",
"Sep",
"Okt",
"Nov",
"Des"]
},
}));
setGrafik(result);
}
useEffect(() => {
getChart()
}, []);
return(<>
<div>
<div className={"fw-bold fs-6"}>Grafik Semua Transaksi </div>
<div className={"fw-normal fs-7 mt-1 text-muted"}>Data Grafik Semua Transaksi</div>
<ReactApexChart options={optionGrafik} series={grafik} type="area" height={450}/>
</div>
</>)
}

View File

@ -0,0 +1,130 @@
import {IdcardOutlined} from "@ant-design/icons";
export default function SegmentAgen() {
return(
<div className={"card"} style={{textAlign: 'right', padding: "30px",border:"none",
background:'#ffffff3b',
boxShadow:'0px 8px 29px #00000012'
}}>
<div className={"transaksi-hari light"}>
<div className={"title"}>Transaksi Per Agen</div>
<div className={"subTitle"}>Perolehan Transaksi Per Agen 10 Teratas</div>
<div className={"content reward-agen"}>
<div className={"name"}>
<div className={"icon"}><IdcardOutlined/></div>
<div>
<div>Agen Alzam Zain Hamizan</div>
<div className={"text-muted"}>Outlet Alzam Store</div>
</div>
</div>
<div className={"nominal"}>
<div className={"amount"}><small>Rp</small> 20.000.000</div>
<div className={"count"}>Dari <b>100</b> Transaksi</div>
</div>
</div>
<div className={"content"}>
<div className={"name"}>
<div className={"icon"}><IdcardOutlined/></div>
<div>Agen A</div>
</div>
<div className={"nominal"}>
<div className={"amount"}><small>Rp</small> 10.000.000</div>
<div className={"count"}>Dari <b>100</b> Transaksi</div>
</div>
</div>
<div className={"content"}>
<div className={"name"}>
<div className={"icon"}><IdcardOutlined/></div>
<div>Agen A</div>
</div>
<div className={"nominal"}>
<div className={"amount"}><small>Rp</small> 10.000.000</div>
<div className={"count"}>Dari <b>100</b> Transaksi</div>
</div>
</div>
<div className={"content"}>
<div className={"name"}>
<div className={"icon"}><IdcardOutlined/></div>
<div>Agen A</div>
</div>
<div className={"nominal"}>
<div className={"amount"}><small>Rp</small> 10.000.000</div>
<div className={"count"}>Dari <b>100</b> Transaksi</div>
</div>
</div>
<div className={"content"}>
<div className={"name"}>
<div className={"icon"}><IdcardOutlined/></div>
<div>Agen A</div>
</div>
<div className={"nominal"}>
<div className={"amount"}><small>Rp</small> 10.000.000</div>
<div className={"count"}>Dari <b>100</b> Transaksi</div>
</div>
</div>
<div className={"content"}>
<div className={"name"}>
<div className={"icon"}><IdcardOutlined/></div>
<div>Agen A</div>
</div>
<div className={"nominal"}>
<div className={"amount"}><small>Rp</small> 10.000.000</div>
<div className={"count"}>Dari <b>100</b> Transaksi</div>
</div>
</div>
<div className={"content"}>
<div className={"name"}>
<div className={"icon"}><IdcardOutlined/></div>
<div>Agen A</div>
</div>
<div className={"nominal"}>
<div className={"amount"}><small>Rp</small> 10.000.000</div>
<div className={"count"}>Dari <b>100</b> Transaksi</div>
</div>
</div>
<div className={"content"}>
<div className={"name"}>
<div className={"icon"}><IdcardOutlined/></div>
<div>Agen A</div>
</div>
<div className={"nominal"}>
<div className={"amount"}><small>Rp</small> 10.000.000</div>
<div className={"count"}>Dari <b>100</b> Transaksi</div>
</div>
</div>
<div className={"content"}>
<div className={"name"}>
<div className={"icon"}><IdcardOutlined/></div>
<div>Agen A</div>
</div>
<div className={"nominal"}>
<div className={"amount"}><small>Rp</small> 10.000.000</div>
<div className={"count"}>Dari <b>100</b> Transaksi</div>
</div>
</div>
<div className={"content"}>
<div className={"name"}>
<div className={"icon"}><IdcardOutlined/></div>
<div>Agen A</div>
</div>
<div className={"nominal"}>
<div className={"amount"}><small>Rp</small> 10.000.000</div>
<div className={"count"}>Dari <b>100</b> Transaksi</div>
</div>
</div>
</div>
</div>
)
}

View File

@ -0,0 +1,56 @@
import {CreditCardOutlined} from "@ant-design/icons";
export default function SegmentJenisTransaksi() {
return (<div className={"card"} style={{textAlign: 'right', padding: "30px", background: 'var(--bg-gradient-transaksi)'}}>
<div className={"transaksi-hari"}>
<div className={"title"}>Transaksi Per Jenis Pembayaran</div>
<div className={"subTitle"}>Perolehan Transaksi Per Jenis Pembayaran</div>
<div className={"content"}>
<div className={"name"}>
<div className={"icon"}><CreditCardOutlined/></div>
<div>Jenis Pembayaran A</div>
</div>
<div className={"nominal"}>
<div className={"amount"}><small>Rp</small> 10.000.000</div>
<div className={"count"}>Dari <b>100</b> Transaksi</div>
</div>
</div>
<div className={"content"}>
<div className={"name"}>
<div className={"icon"}><CreditCardOutlined/></div>
<div>Jenis Pembayaran A</div>
</div>
<div className={"nominal"}>
<div className={"amount"}><small>Rp</small> 10.000.000</div>
<div className={"count"}>Dari <b>100</b> Transaksi</div>
</div>
</div>
<div className={"content"}>
<div className={"name"}>
<div className={"icon"}><CreditCardOutlined/></div>
<div>Jenis Pembayaran A</div>
</div>
<div className={"nominal"}>
<div className={"amount"}><small>Rp</small> 10.000.000</div>
<div className={"count"}>Dari <b>100</b> Transaksi</div>
</div>
</div>
<div className={"content"}>
<div className={"name"}>
<div className={"icon"}><CreditCardOutlined/></div>
<div>Jenis Pembayaran A</div>
</div>
<div className={"nominal"}>
<div className={"amount"}><small>Rp</small> 10.000.000</div>
<div className={"count"}>Dari <b>100</b> Transaksi</div>
</div>
</div>
</div>
</div>)
}

View File

@ -0,0 +1,131 @@
import {InboxOutlined} from "@ant-design/icons";
export default function SegmentProduk() {
return(
<div className={"card"} style={{textAlign: 'right', padding: "30px",
background:'#ffffff3b',
boxShadow: '0px 8px 29px #00000012',
border:'none'}}>
<div className={"transaksi-hari light"}>
<div className={"title"}>Transaksi Per Produk</div>
<div className={"subTitle"}>Perolehan Transaksi Per Produk 10 Teratas</div>
<div className={"content reward-product"}>
<div className={"name"}>
<div className={"icon"}><InboxOutlined/></div>
<div>
<div>Produk A</div>
<div className={"text-muted"}>Outlet Alzam Store</div>
</div>
</div>
<div className={"nominal"}>
<div className={"amount"}><small>Rp</small> 20.000.000</div>
<div className={"count"}>Dari <b>100</b> Transaksi</div>
</div>
</div>
<div className={"content"}>
<div className={"name"}>
<div className={"icon"}><InboxOutlined/></div>
<div>Produk A</div>
</div>
<div className={"nominal"}>
<div className={"amount"}><small>Rp</small> 10.000.000</div>
<div className={"count"}>Dari <b>100</b> Transaksi</div>
</div>
</div>
<div className={"content"}>
<div className={"name"}>
<div className={"icon"}><InboxOutlined/></div>
<div>Produk A</div>
</div>
<div className={"nominal"}>
<div className={"amount"}><small>Rp</small> 10.000.000</div>
<div className={"count"}>Dari <b>100</b> Transaksi</div>
</div>
</div>
<div className={"content"}>
<div className={"name"}>
<div className={"icon"}><InboxOutlined/></div>
<div>Produk A</div>
</div>
<div className={"nominal"}>
<div className={"amount"}><small>Rp</small> 10.000.000</div>
<div className={"count"}>Dari <b>100</b> Transaksi</div>
</div>
</div>
<div className={"content"}>
<div className={"name"}>
<div className={"icon"}><InboxOutlined/></div>
<div>Produk A</div>
</div>
<div className={"nominal"}>
<div className={"amount"}><small>Rp</small> 10.000.000</div>
<div className={"count"}>Dari <b>100</b> Transaksi</div>
</div>
</div>
<div className={"content"}>
<div className={"name"}>
<div className={"icon"}><InboxOutlined/></div>
<div>Produk A</div>
</div>
<div className={"nominal"}>
<div className={"amount"}><small>Rp</small> 10.000.000</div>
<div className={"count"}>Dari <b>100</b> Transaksi</div>
</div>
</div>
<div className={"content"}>
<div className={"name"}>
<div className={"icon"}><InboxOutlined/></div>
<div>Produk A</div>
</div>
<div className={"nominal"}>
<div className={"amount"}><small>Rp</small> 10.000.000</div>
<div className={"count"}>Dari <b>100</b> Transaksi</div>
</div>
</div>
<div className={"content"}>
<div className={"name"}>
<div className={"icon"}><InboxOutlined/></div>
<div>Produk A</div>
</div>
<div className={"nominal"}>
<div className={"amount"}><small>Rp</small> 10.000.000</div>
<div className={"count"}>Dari <b>100</b> Transaksi</div>
</div>
</div>
<div className={"content"}>
<div className={"name"}>
<div className={"icon"}><InboxOutlined/></div>
<div>Produk A</div>
</div>
<div className={"nominal"}>
<div className={"amount"}><small>Rp</small> 10.000.000</div>
<div className={"count"}>Dari <b>100</b> Transaksi</div>
</div>
</div>
<div className={"content"}>
<div className={"name"}>
<div className={"icon"}><InboxOutlined/></div>
<div>Produk A</div>
</div>
<div className={"nominal"}>
<div className={"amount"}><small>Rp</small> 10.000.000</div>
<div className={"count"}>Dari <b>100</b> Transaksi</div>
</div>
</div>
</div>
</div>
)
}

View File

@ -0,0 +1,83 @@
import {FieldTimeOutlined, UserOutlined} from "@ant-design/icons";
export default function SegmentTransaksiHari() {
return(
<div className={"card"} style={{textAlign: 'right', padding: "30px", background: 'var(--bg-gradient-transaksi)'}}>
<div className={"transaksi-hari"}>
<div className={"title"}>Transaksi Per Hari</div>
<div className={"subTitle"}>Total Transaksi Per Hari</div>
<div className={"content"}>
<div className={"name"}>
<div className={"icon"}><FieldTimeOutlined/></div>
<div>Minggu</div>
</div>
<div className={"nominal"}>
<div className={"amount"}><small>Rp</small> 10.000.000</div>
<div className={"count"}>Dari <b>100</b> Transaksi</div>
</div>
</div>
<div className={"content"}>
<div className={"name"}>
<div className={"icon"}><FieldTimeOutlined/></div>
<div>Senin</div>
</div>
<div className={"nominal"}>
<div className={"amount"}><small>Rp</small> 10.000.000</div>
<div className={"count"}>Dari <b>100</b> Transaksi</div>
</div>
</div>
<div className={"content"}>
<div className={"name"}>
<div className={"icon"}><FieldTimeOutlined/></div>
<div>Selasa</div>
</div>
<div className={"nominal"}>
<div className={"amount"}><small>Rp</small> 10.000.000</div>
<div className={"count"}>Dari <b>100</b> Transaksi</div>
</div>
</div>
<div className={"content"}>
<div className={"name"}>
<div className={"icon"}><FieldTimeOutlined/></div>
<div>Rabu</div>
</div>
<div className={"nominal"}>
<div className={"amount"}><small>Rp</small> 10.000.000</div>
<div className={"count"}>Dari <b>100</b> Transaksi</div>
</div>
</div>
<div className={"content"}>
<div className={"name"}>
<div className={"icon"}><FieldTimeOutlined/></div>
<div>Kamis</div>
</div>
<div className={"nominal"}>
<div className={"amount"}><small>Rp</small> 10.000.000</div>
<div className={"count"}>Dari <b>100</b> Transaksi</div>
</div>
</div>
<div className={"content"}>
<div className={"name"}>
<div className={"icon"}><FieldTimeOutlined/></div>
<div>Jum'at</div>
</div>
<div className={"nominal"}>
<div className={"amount"}><small>Rp</small> 10.000.000</div>
<div className={"count"}>Dari <b>100</b> Transaksi</div>
</div>
</div>
<div className={"content"}>
<div className={"name"}>
<div className={"icon"}><FieldTimeOutlined/></div>
<div>Sabtu</div>
</div>
<div className={"nominal"}>
<div className={"amount"}><small>Rp</small> 10.000.000</div>
<div className={"count"}>Dari <b>100</b> Transaksi</div>
</div>
</div>
</div>
</div>
)
}

View File

@ -1,5 +1,187 @@
"use client"
import {Col, Row, Spin} from "antd";
import Input from "@/components/util/Input";
import {useState} from "react";
import {useForm} from "react-hook-form";
import "./style.scss"
import GrafikHari from "@/app/main/dashboard/GrafikHari";
import CardTransaksiHari from "@/app/main/dashboard/SegmentTransaksiHari";
import SegmentTransaksiHari from "@/app/main/dashboard/SegmentTransaksiHari";
import SegmentAgen from "@/app/main/dashboard/SegmentAgen";
import GrafikTahun from "@/app/main/dashboard/GrafikTahun";
import SegmentProduk from "@/app/main/dashboard/SegmentProduk";
import SegmentJenisTransaksi from "@/app/main/dashboard/SegmentJenisTransaksi";
import {CreditCardOutlined, IdcardOutlined, InboxOutlined, ShakeOutlined, ShopOutlined} from "@ant-design/icons";
export default function Dashboard() { export default function Dashboard() {
return( const {register, setValue, watch, getValues, formState: {errors},} = useForm();
<>dashboard</>
) const [dropdownData, setDropdownData] = useState({});
const [dropdownLoading, setDropdownLoading] = useState({
company: false, outlet: false,
});
return (<div className={"dashboard"}>
<div className="headContent">
<div className="containerTitle">
<div className="breadCrumb">
<div className="text">Dashboard</div>
<div className="text">Data Dashboard</div>
</div>
<div className="title text-dark-grey left text-uppercase">Dashboard</div>
</div>
<div>
<div style={{display: "flex", gap: '10px', justifyContent: 'end'}}>
<div style={{width: '250px'}}>
<Input.DateRange
title={"Tanggal"}
name={"filterSkimKredit"}
register={register}
error={errors}
options={dropdownData?.productType}
val={getValues("filterSkimKredit")}
placeholder={"Filter Produk"}
setValue={setValue}
/>
</div>
<div style={{width: '250px'}}>
<Spin spinning={dropdownLoading?.company}>
<Input.Select
title={"Filter Company"}
name={"filterCompany"}
register={register}
error={errors}
options={dropdownData?.company}
val={getValues("filterCompany")}
setValue={setValue}
/>
</Spin>
</div>
<div style={{width: '250px'}}>
<Spin spinning={dropdownLoading?.outlet}>
<Input.Select
title={"Filter Outlet"}
name={"filterOutlet"}
register={register}
error={errors}
options={dropdownData?.outlet}
val={getValues("filterOutlet")}
setValue={setValue}
/>
</Spin>
</div>
</div>
</div>
</div>
<div className={'content'}>
<Row gutter={[30,30]}>
<Col span={8} xxl={6}>
<Row gutter={[15, 15]}>
<Col span={12}>
<div className={"card dark"}>
<div className={"icon-summary"}><IdcardOutlined/></div>
<div className={"title"}>Total Agen</div>
<div className={"nominal"}>100.000</div>
</div>
</Col>
<Col span={12}>
<div className={"card purple"}>
<div className={"icon-summary"}><ShopOutlined/></div>
<div className={"title"}>Total Outlet</div>
<div className={"nominal"}>1.000</div>
</div>
</Col>
<Col span={12}>
<div className={"card green"}>
<div className={"icon-summary"}><ShakeOutlined/></div>
<div className={"title"}>Total EDC Aktif</div>
<div className={"nominal"}>100.000</div>
</div>
</Col>
<Col span={12}>
<div className={"card orange"}>
<div className={"icon-summary"}><InboxOutlined/></div>
<div className={"title"}>Total Produk</div>
<div className={"nominal"}>5.000</div>
</div>
</Col>
</Row>
</Col>
<Col span={16} xxl={18}>
<div className={"card"} style={{textAlign:'right',padding:"30px"}}>
<Row>
<Col span={8}>
<div className={"transaksi"}>
<div className={"title text-primary"}>Total Transaksi</div>
<div className={"nominal low "}><small>Rp</small> 500.000.000</div>
<div className={"desc mt-3"}>Dari <b className={"text-primary"}>100</b> Transaksi yang di lakukan. Transaksi rata-rata perhari adalah <b className={"text-primary"}>10</b> Transaksi dengan nominal Rata-rata sebesar <b className={"text-primary"}>Rp 100.000</b> </div>
</div>
</Col>
<Col span={8}>
<div>
<div className={"title text-success"}>Omzet</div>
<div className={"nominal low"}><small>Rp</small> 50.000.000</div>
<div className={"desc"}>Dari <b>100</b> Transaksi</div>
</div>
<div className={"mt-5"}>
<div className={"title text-success"}>Laba</div>
<div className={"nominal low"}><small>Rp</small> 50.000.000</div>
<div className={"desc"}>Dari <b>100</b> Transaksi</div>
</div>
</Col>
<Col span={8}>
<div>
<div className={"title text-danger"}>Fee Agen</div>
<div className={"nominal low"}><small>Rp</small> 10.000.000</div>
<div className={"desc"}>Dari <b>100</b> Transaksi</div>
</div>
<div className={"mt-5"}>
<div className={"title text-danger"}>Fee Bank</div>
<div className={"nominal low"}><small>Rp</small> 10.000.000</div>
<div className={"desc"}>Dari <b>100</b> Transaksi</div>
</div>
</Col>
</Row>
</div>
</Col>
<Col span={16} xxl={18}>
<div className={"mt-2"}>
<GrafikHari/>
</div>
<div className={"mt-5"}>
<GrafikTahun/>
</div>
</Col>
<Col span={8} xxl={6}>
<SegmentTransaksiHari/>
<div className={"mt-3"}>
<SegmentJenisTransaksi/>
</div>
</Col>
</Row>
<Row gutter={[30, 30]}>
<Col span={12}>
<div className={"mt-3"}>
<SegmentAgen/>
</div>
</Col>
<Col span={12}>
<div className={"mt-3"}>
<SegmentProduk/>
</div>
</Col>
</Row>
</div>
</div>)
} }

View File

@ -0,0 +1,229 @@
.dashboard {
.card {
background: #fff;
color: #000;
text-align: center;
padding: 25px;
//border:2px solid #f3f3f3;
box-shadow: none;
border-radius: 25px;
.icon-summary{
position: absolute;
top: 0;
font-size: 72px;
opacity: 0.09;
}
.title {
font-size: 13px;
font-weight: 400;
color: #000;
}
.desc {
font-size: 12px;
color: #a1a1a1;
}
.nominal {
font-size: 24px;
font-weight: 600;
&.low {
font-size: 18px;
}
small {
font-size: 13px;
font-weight: 400;
color: var(--text-muted);
}
}
.transaksi {
text-align: left;
margin-top: 20px;
//padding: 12px 20px;
}
&.dark {
border-bottom: 3px solid var(--dark);
.nominal {
color: var(--dark);
}
.icon-summary{
color: var(--dark);
}
}
&.green {
border-bottom: 3px solid var(--color-logo-green);
.nominal {
color: var(--color-logo-green);
}
.icon-summary {
color: var(--color-logo-green);
}
}
&.purple {
border-bottom: 3px solid var(--color-logo-purple);
.nominal {
color: var(--color-logo-purple);
}
.icon-summary {
color: var(--color-logo-purple);
}
}
&.orange {
border-bottom: 3px solid var(--color-logo-orange);
.nominal {
color: var(--color-logo-orange);
}
.icon-summary {
color: var(--color-logo-orange);
}
}
&.primary {
border-bottom: 3px solid var(--primary);
.nominal {
color: var(--primary);
}
}
}
.transaksi-hari {
color: #fff !important;
&.light {
color: var(--dark) !important;
.subTitle {
color: var(--text-muted);
}
.title {
color: var(--dark) !important;
}
.nominal {
.amount {
small {
color: var(--text-muted) !important;
}
}
.count {
color: var(--text-muted) !important;
}
}
}
.title {
font-size: 14px;
font-weight: 600;
color: #fff !important;
}
.subTitle {
font-size: 12px;
font-weight: 400;
color: var(--text-muted-reverse);
margin-bottom: 30px;
}
.content {
display: flex;
justify-content: space-between;
border-top: 1px solid rgb(255 255 255 / 9%);
padding: 15px 0;
&.reward-product {
background: #f7963e24;
border-radius: 20px;
padding: 15px;
margin: 0 -15px;
.icon {
color: #ffffff !important;
background: #f7963e;
}
}
&.reward-agen {
background: #181c3214;
border-radius: 20px;
padding: 15px;
margin: 0 -15px;
.icon {
color: #ffffff !important;
background: var(--dark);
}
}
.icon {
font-size: 16px;
background: rgba(0, 0, 0, 0.06);
width: 30px;
height: 30px;
padding: 3px 7px;
border-radius: 50%;
}
.name {
font-size: 12px;
font-weight: 600;
display: flex;
gap: 10px;
align-items: center;
text-align: left;
}
.nominal {
.amount {
font-size: 16px;
font-weight: 600;
small {
font-size: 12px;
font-weight: 400;
color: var(--text-muted-reverse);
}
}
.count {
font-size: 11px;
color: var(--text-muted-reverse);
}
}
}
}
}

View File

@ -3,33 +3,33 @@
import React, {useEffect, useState} from "react"; import React, {useEffect, useState} from "react";
import {QueryClient, QueryClientProvider} from "react-query"; import {QueryClient, QueryClientProvider} from "react-query";
import dynamic from "next/dynamic";
import ModalConfirm from '@/components/util/ModalConfirm'; import ModalConfirm from '@/components/util/ModalConfirm';
import ModalNotif from "@/components/util/ModalNotif"; import ModalNotif from "@/components/util/ModalNotif";
import ChangePassword from "@/app/main/ChangePassword"; import ChangePassword from "@/app/main/ChangePassword";
import ChangeProfile from "@/app/main/ChangeProfile"; import ChangeProfile from "@/app/main/ChangeProfile";
import LoadingPage from "@/components/util/LoadingPage";
import {getCookie} from "cookies-next"; import {getCookie} from "cookies-next";
import {API} from "@/lib/API"; import {API} from "@/lib/API";
import {notFound, usePathname, useRouter} from "next/navigation"; import {usePathname, useRouter} from "next/navigation";
import {LoadingOutlined} from "@ant-design/icons";
import ModalSessionTimeout from "@/components/util/ModalSessionTimeout"; import ModalSessionTimeout from "@/components/util/ModalSessionTimeout";
import useIdleDetection from "@/hooks/useIdleDetection"; import useIdleDetection from "@/hooks/useIdleDetection";
import MenuList from "@/components/master/Menu"; import MenuList from "@/components/master/Menu";
import menuStore from "@/store/menuStore"; import menuStore from "@/store/menuStore";
import CheckAuth from "@/components/util/CheckAuth"; import CheckAuth from "@/components/util/CheckAuth";
import LoadingPage from "@/components/util/LoadingPage";
const Navbar = dynamic(() => import("@/components/master/Navbar"), {ssr: false,}); import {Spin} from "antd";
import {LoadingOutlined} from "@ant-design/icons";
const queryClient = new QueryClient(); const queryClient = new QueryClient();
export default function MainLayout({children}) { export default function MainLayout({children}) {
const pathname = usePathname() const pathname = usePathname()
const router = useRouter(); const router = useRouter();
const [checkAuth, setCheckAuth] = useState(true) const [checkAuth, setCheckAuth] = useState(true)
const {isIdle, setIsIdle} = useIdleDetection(); const {isIdle, setIsIdle} = useIdleDetection();
const {toggleStatus} = menuStore() const {toggleStatus, loadingPageStatus} = menuStore()
const checkToken = async () => { const checkToken = async () => {
@ -61,36 +61,37 @@ export default function MainLayout({children}) {
} }
useEffect(() => { useEffect(() => {
checkToken() checkToken()
}, []); }, []);
return (
<QueryClientProvider client={queryClient}> return (<QueryClientProvider client={queryClient}>
<> <>
{(checkAuth) ? {(checkAuth) ? <CheckAuth/> : <>
<CheckAuth/>
: <>
{isIdle && <ModalSessionTimeout setIsIdle={setIsIdle}/>} {isIdle && <ModalSessionTimeout setIsIdle={setIsIdle}/>}
<MenuList/> {/*<LoadingPage/>*/}
<MenuList />
{/*<Navbar/>*/} <section className={`content ${(toggleStatus) ? 'hover' : ''}`}>
<LoadingPage/> <div className={'card-content'}>
<div id="backdrop" className=""> <Spin percent="auto" size={"large"} indicator={<LoadingOutlined spin/>} style={{color:'var(--primary)'}} tip={<div className={"mt-3"}>Loading</div>} spinning={loadingPageStatus}>
<button className="hideBar"/>
{children}
</Spin>
</div> </div>
<section className={`content ${(toggleStatus) ? 'hover':''}`}>{children}</section> </section>
<ChangePassword/> <ChangePassword/>
<ChangeProfile/> <ChangeProfile/>
<ModalConfirm/> <ModalConfirm/>
<ModalNotif/> <ModalNotif/>
</> </>}
}
</> </>
</QueryClientProvider> </QueryClientProvider>);
);
} }

View File

@ -1,6 +1,5 @@
"use client"; "use client";
import React, {useEffect, useRef, useState} from "react"; import React, {useEffect, useRef, useState} from "react";
import breadcrumbStore from "@/store/breadcrumbStore";
import modalStore from "@/store/modal"; import modalStore from "@/store/modal";
import confirmStore from "@/store/confirmStore"; import confirmStore from "@/store/confirmStore";
import {API} from "@/lib/API"; import {API} from "@/lib/API";
@ -22,7 +21,6 @@ const MenuPrivilage = () => {
const formRef = useRef(); const formRef = useRef();
const {modalOpen, modalClose, FormId, modalStat, setModalLoading, setModalDetail, actionType} = modalStore(); const {modalOpen, modalClose, FormId, modalStat, setModalLoading, setModalDetail, actionType} = modalStore();
const {setSubTitle, setTitle} = breadcrumbStore();
const {confirmOpen, confirmClose, setConfirmLoading} = confirmStore(); const {confirmOpen, confirmClose, setConfirmLoading} = confirmStore();
const {notifOpen} = notifStore() const {notifOpen} = notifStore()

View File

@ -3,8 +3,6 @@ import {useEffect, useState} from "react";
import {DropdownAPI} from "@/lib/DropdownAPI"; import {DropdownAPI} from "@/lib/DropdownAPI";
import RefTemplate from "@/components/refTemplate/Main"; import RefTemplate from "@/components/refTemplate/Main";
import BadgeStatus from "@/components/util/BadgeStatus";
import BadgeStatusApproval from "@/components/util/BadgeStatusApproval";
export default function UserList() { export default function UserList() {
const [listForm, setListForm] = useState() const [listForm, setListForm] = useState()
@ -142,15 +140,11 @@ export default function UserList() {
{ {
title: "Status", title: "Status",
dataIndex: 'active', dataIndex: 'active',
render: (active) => <BadgeStatus active={active}/>,
sorter: (a, b) => a.active - b.active,
align: 'center' align: 'center'
}, },
{ {
title: "Status Persetujuan", title: "Status Persetujuan",
dataIndex: 'statusApprovalId', dataIndex: 'statusApprovalId',
render: (statusApprovalId) => <BadgeStatusApproval approvalId={statusApprovalId}/>,
sorter: (a, b) => a.statusApprovalId - b.statusApprovalId,
align: 'center' align: 'center'
}, },
]} ]}

View File

@ -7,6 +7,7 @@ import {LoadingOutlined} from "@ant-design/icons";
import {getCookie} from "cookies-next"; import {getCookie} from "cookies-next";
import {useRouter} from "next/navigation"; import {useRouter} from "next/navigation";
import {API} from "@/lib/API"; import {API} from "@/lib/API";
import CheckAuth from "@/components/util/CheckAuth";
export default function Home(){ export default function Home(){
const router = useRouter(); const router = useRouter();
@ -30,17 +31,7 @@ export default function Home(){
return ( return (
<> <>
<div className="check-auth"> <CheckAuth/>
<div className="container">
<div className="center">
<img src="/img/logo.png" className="logo" />
<div className="title">SILOS KPR - Tapera Connect</div>
<div className="subtitle">Bank Kalteng</div>
<div className="check">check authorization</div>
<LoadingOutlined className="icon-check" />
</div>
</div>
</div>
<ExportedImage <ExportedImage
className="bg-login-auth" className="bg-login-auth"

View File

@ -1,24 +1,35 @@
"use client"; "use client";
import Link from "next/link"; import Link from "next/link";
import React, {useEffect, useState} from "react"; import React, {useEffect, useState} from "react";
import {BranchesOutlined, CloseOutlined, MenuOutlined, MenuUnfoldOutlined} from "@ant-design/icons"; import {BranchesOutlined, DoubleLeftOutlined, DoubleRightOutlined, MoonOutlined, SunOutlined, UserOutlined} from "@ant-design/icons";
import {Menu} from "antd"; import {Menu, Switch} from "antd";
import {API} from "@/lib/API"; import {API} from "@/lib/API";
import menuStore from "@/store/menuStore"; import menuStore from "@/store/menuStore";
import notifStore from "@/store/notifStore"; import notifStore from "@/store/notifStore";
import packageJson from '@@/package.json'; import packageJson from '@@/package.json';
import modalStore from "@/store/modal";
import useAuth from "@/hooks/useAuth";
import {usePathname} from "next/navigation";
const MenuList = () => { const MenuList = () => {
let valTheme = localStorage.getItem('valTheme')
let state = null
if (valTheme == 'true') {
state = true
document.body.classList.add('darkMode')
} else {
state = false
}
const pathname = usePathname()
const [MenuList, setMenuList] = useState([]) const [menuList, setMenuList] = useState([])
const [menuTapera, setMenuTapera] = useState([]) const {setModalPassword, setModalProfile} = modalStore()
const [menuSilos, setMenuSilos] = useState([])
const [menuUsers, setMenuUsers] = useState([])
const [menuManajemenData, setMenuManajemenData] = useState([])
const [current, setCurrent] = useState(null); const [current, setCurrent] = useState(null);
const [collapsed, setCollapsed] = useState(false); const [collapsed, setCollapsed] = useState(false);
const {toggleStatus, setToggle} = menuStore() const {toggleStatus, setToggle, setLoadingPageStatus} = menuStore()
const {notifOpen} = notifStore() const {notifOpen} = notifStore()
const {removeAuth} = useAuth()
const [isTheme, setTheme] = useState(state);
const toggleCollapsed = () => { const toggleCollapsed = () => {
setCollapsed(!collapsed); setCollapsed(!collapsed);
@ -40,11 +51,11 @@ const MenuList = () => {
let resultListChildThird = null let resultListChildThird = null
if (vChild.childs) { if (vChild.childs) {
resultListChildThird = vChild?.childs?.map((vChildThird, kChildThird) => { resultListChildThird = vChild?.childs?.map((vChildThird, kChildThird) => {
if(vChildThird.url){ if (vChildThird.url) {
return { return {
label: (<Link href={vChildThird.url}>{vChildThird.menuNm}</Link>), key: vChildThird.menuId, icon: (<div dangerouslySetInnerHTML={{__html: v.iconCss,}}/>), label: (<Link onClick={() => eventChange(vChildThird.url)} href={vChildThird.url}>{vChildThird.menuNm}</Link>), key: vChildThird.menuId, icon: (<div dangerouslySetInnerHTML={{__html: v.iconCss,}}/>),
} }
}else{ } else {
return { return {
label: vChildThird.menuNm, key: vChildThird.menuId, icon: (<div dangerouslySetInnerHTML={{__html: v.iconCss,}}/>), label: vChildThird.menuNm, key: vChildThird.menuId, icon: (<div dangerouslySetInnerHTML={{__html: v.iconCss,}}/>),
} }
@ -53,11 +64,11 @@ const MenuList = () => {
}) })
} }
if(vChild.url){ if (vChild.url) {
tmpChild.push({ tmpChild.push({
label: (<Link href={vChild.url}>{vChild.menuNm}</Link>), key: vChild.menuId, icon: (<div dangerouslySetInnerHTML={{__html: v.iconCss,}}/>), children: resultListChildThird label: (<Link onClick={() => eventChange(vChild.url)} href={vChild.url}>{vChild.menuNm}</Link>), key: vChild.menuId, icon: (<div dangerouslySetInnerHTML={{__html: v.iconCss,}}/>), children: resultListChildThird
}) })
}else{ } else {
tmpChild.push({ tmpChild.push({
label: vChild.menuNm, key: vChild.menuId, icon: (<div dangerouslySetInnerHTML={{__html: v.iconCss,}}/>), children: resultListChildThird label: vChild.menuNm, key: vChild.menuId, icon: (<div dangerouslySetInnerHTML={{__html: v.iconCss,}}/>), children: resultListChildThird
@ -69,14 +80,14 @@ const MenuList = () => {
tmpChild = null tmpChild = null
} }
if(v.url){ if (v.url) {
tmpMenu.push({ tmpMenu.push({
label: (<Link href={v.url}>{v.menuNm}</Link>), key: v.menuId, icon: (<div dangerouslySetInnerHTML={{__html: v.iconCss,}}/>), children: tmpChild label: (<Link onClick={()=>eventChange(v.url)} href={v.url}>{v.menuNm}</Link>), key: v.menuId, icon: (<div dangerouslySetInnerHTML={{__html: v.iconCss,}}/>), children: tmpChild
}) })
}else{ } else {
tmpMenu.push({ tmpMenu.push({
label: v.menuNm, key: v.menuId, icon: (<div dangerouslySetInnerHTML={{__html: v.iconCss,}}/>), children:[{ label: v.menuNm, key: v.menuId, icon: (<div dangerouslySetInnerHTML={{__html: v.iconCss,}}/>), children: [{
key: 'g1', label: 'Menu '+v.menuNm, type: 'group', children: tmpChild key: 'g1', label: 'Menu ' + v.menuNm, type: 'group', children: tmpChild
}] }]
}) })
@ -86,16 +97,20 @@ const MenuList = () => {
console.log(tmpMenu) console.log(tmpMenu)
setMenuTapera(tmpMenu) // setMenuTapera(tmpMenu)
// setMenuTapera(tmpMenu.filter((v)=>v.key === 1)) // setMenuTapera(tmpMenu.filter((v)=>v.key === 1))
// setMenuSilos(tmpMenu.filter((v)=>v.key === 2)) // setMenuSilos(tmpMenu.filter((v)=>v.key === 2))
// setMenuManajemenData(tmpMenu.filter((v)=>v.key === 3)) // setMenuManajemenData(tmpMenu.filter((v)=>v.key === 3))
// setMenuUsers(tmpMenu.filter((v)=>v.key === 8)) setMenuList(tmpMenu.filter((v)=>v.key === 8))
} }
} }
const eventChange = (url) => {
if (pathname !== url) setLoadingPageStatus(true)
}
const getMenuDummy = () => { const getMenuDummy = () => {
const items = [{ const items = [{
key: 'sub1', label: <div>Tapera Connect</div>, icon: <BranchesOutlined/>, children: [{ key: 'sub1', label: <div>Tapera Connect</div>, icon: <BranchesOutlined/>, children: [{
@ -113,6 +128,45 @@ const MenuList = () => {
setMenuList(items) setMenuList(items)
} }
const modalChangeProfile = () => {
setModalProfile(true)
}
const modalChangePassword = (type) => {
setModalPassword(true, type)
}
const ViewImage = ({fotoMember}) => {
if (fotoMember == null) return <UserOutlined className="userOutline" style={{background: '#eef6ff', borderRadius: '20px', padding: '10px'}}/>;
let getImage = '/';
return <img src={getImage} className="foto-member-nav"/>;
};
const logout = async () => {
let response = await API.POST('/auth/logout');
if (response.status === 200) {
await removeAuth()
// router.push("/login");
}
}
const buttonHandler = (val) => {
setTheme((status) => !status);
let theme = null
if (val != true) {
theme = 'lightMode'
document.body.classList.add(theme);
document.body.classList.remove('darkMode');
} else {
theme = 'darkMode'
document.body.classList.add(theme);
document.body.classList.remove('lightMode');
}
localStorage.setItem('setTheme', theme)
localStorage.setItem('valTheme', val)
};
useEffect(() => { useEffect(() => {
getMenu() getMenu()
// getMenuDummy() // getMenuDummy()
@ -124,9 +178,9 @@ const MenuList = () => {
return (<> return (<>
<div className={toggleStatus === true ? "side-menu-container": "side-menu-container active"}> <div className={toggleStatus === true ? "side-menu-container" : "side-menu-container active"}>
<div className={toggleStatus === true ? 'logo-menu':'logo-menu active'}> <div className={toggleStatus === true ? 'logo-menu' : 'logo-menu active'}>
<div className={"content-logo"}> <div className={"content-logo"}>
<img src="/img/logo.png" className={"img-logo"} alt="logo"/> <img src="/img/logo.png" className={"img-logo"} alt="logo"/>
<div className={'title'}> <div className={'title'}>
@ -141,17 +195,52 @@ const MenuList = () => {
<Menu <Menu
forceSubMenuRender={true} forceSubMenuRender={true}
defaultSelectedKeys={['105']} defaultSelectedKeys={['105']}
defaultOpenKeys={['1']} // defaultOpenKeys={['1']}
inlineCollapsed={toggleStatus} inlineCollapsed={toggleStatus}
mode="inline" mode="inline"
selectedKeys={[current]} selectedKeys={[current]}
items={menuTapera} items={menuList}
/> />
</div> </div>
</div> </div>
<button className={"btn-menu-toggle"} onClick={setToggle}><MenuUnfoldOutlined/></button> <div className="nav-account">
<div className="container-account">
<div className="account">
<div className={toggleStatus === true ? 'text ' : 'text active'}>
<div className="name">Zamzam</div>
<div className="role">Administrator</div>
</div>
<ViewImage fotoMember={null}/>
</div>
<div className={toggleStatus === true ? 'detail-account ' : 'detail-account active'}>
<div className="header">
<ViewImage fotoMember={null}/>
<div className="text">
<div className="name">Administrator</div>
<div className="role">Administrator</div>
</div>
</div>
<div className="feature">
<button type="button" style={{cursor: 'pointer'}} onClick={() => modalChangeProfile()}>Ubah Profil</button>
<button type="button" style={{cursor: 'pointer'}} onClick={() => modalChangePassword('normal')}>Ubah Password</button>
<div className="switchTheme">
<Switch style={{width: '50px'}} checkedChildren={<MoonOutlined/>} unCheckedChildren={<SunOutlined/>} onChange={buttonHandler} checked={isTheme}/>
</div>
</div>
<div className="containerSignOut">
<button type="button" style={{cursor: 'pointer'}} onClick={() => logout()}>Sign Out</button>
</div>
</div>
</div>
</div>
<button className={"btn-menu-toggle"} onClick={setToggle}>{toggleStatus ? <DoubleRightOutlined/> : <DoubleLeftOutlined/>}</button>
</div> </div>

View File

@ -1,351 +0,0 @@
"use client";
import React, {useEffect, useState} from "react";
import {BellOutlined, CloseOutlined, MenuFoldOutlined, MenuOutlined, MenuUnfoldOutlined, MoonOutlined, SunOutlined, UserOutlined} from '@ant-design/icons';
import useAuth from "@/hooks/useAuth";
import {useRouter} from "next/navigation";
import {useAPI} from "@/hooks/useAPI";
import modalStore from "@/store/modal";
import {API} from "@/lib/API";
import {Button, Drawer, Menu, Switch} from "antd";
import Link from "next/link";
import notifStore from "@/store/notifStore";
import ExportedImage from "next-image-export-optimizer"
import menuStore from "@/store/menuStore";
function Navbar() {
const router = useRouter();
const {notifOpen} = notifStore()
const {setToggle} = menuStore()
const {data: dataUser, isSuccess} = useAPI.GET('user', '/ref/user/' + localStorage.getItem('userId'))
const {removeAuth} = useAuth()
const [MenuList, setMenuList] = useState([])
const {setModalPassword, setModalProfile} = modalStore()
const [current, setCurrent] = useState(null);
const [collapsed, setCollapsed] = useState(false);
const ViewImage = ({fotoMember}) => {
if (fotoMember == null) return <UserOutlined className="userOutline" style={{background: '#eef6ff', borderRadius: '20px', padding: '10px'}}/>;
let getImage = '/';
return <img src={getImage} className="foto-member-nav"/>;
};
const logout = async () => {
let response = await API.GET('/auth/logout');
console.log(response)
if (response.status === 200) {
await removeAuth()
router.push("/login");
}
}
let valTheme = localStorage.getItem('valTheme')
let state = null
if (valTheme == 'true') {
state = true
document.body.classList.add('darkMode')
} else {
state = false
}
const [isTheme, setTheme] = useState(state);
const buttonHandler = (val) => {
setTheme((status) => !status);
let theme = null
if (val != true) {
theme = 'lightMode'
document.body.classList.add(theme);
document.body.classList.remove('darkMode');
} else {
theme = 'darkMode'
document.body.classList.add(theme);
document.body.classList.remove('lightMode');
}
localStorage.setItem('setTheme', theme)
localStorage.setItem('valTheme', val)
};
const modalChangeProfile = () => {
setModalProfile(true)
}
const modalChangePassword = (type) => {
console.log(type)
setModalPassword(true, type)
}
const getMenu = async () => {
let menu = await API.GET('/ref/menu');
if (menu.status !== 200) {
notifOpen("Gagal", menu?.result?.message, "danger");
} else {
let tmpMenu = []
menu?.result?.forEach((v, k) => {
let tmpChild = []
if (v.childs) {
v?.childs?.forEach((vChild, kChild) => {
let resultListChildThird = null
if (vChild.childs) {
resultListChildThird = vChild?.childs?.map((vChildThird, kChildThird) => {
return {
label: (<Link href={vChildThird.url}>{vChildThird.menuNm}</Link>),
key: vChildThird.menuId,
icon: (<div dangerouslySetInnerHTML={{__html: v.iconCss,}}/>),
}
})
}
tmpChild.push(
{
label: (<Link href={vChild.url}>{vChild.menuNm}</Link>),
key: vChild.menuId,
icon: (<div dangerouslySetInnerHTML={{__html: v.iconCss,}}/>),
children: resultListChildThird
}
)
})
} else {
tmpChild = null
}
tmpMenu.push(
{
label: (<Link href={v.url}>{v.menuNm}</Link>),
key: v.menuId,
icon: (<div dangerouslySetInnerHTML={{__html: v.iconCss,}}/>),
children: tmpChild
},
)
})
setMenuList(tmpMenu)
}
}
const checkFirstLogin = () => {
if (dataUser?.result?.userStatusId === 3) {
modalChangePassword('first')
}
}
const checkPasswordExpired = () => {
if (dataUser?.result?.passwordExpired) {
modalChangePassword('expired')
}
}
useEffect(() => {
if (isSuccess) {
checkFirstLogin()
checkPasswordExpired()
}
}, [isSuccess]);
useEffect(() => {
getMenu()
}, []);
const onClickMenu = (e) => {
// console.log('click ', e);
setCollapsed(!collapsed);
// document.querySelector(".backdrop").classList.remove("backdropMenu");
// document.querySelector(".menuInline").classList.remove("in");
// document.querySelector(".menuInline").classList.add("out");
setCurrent(e.key);
};
const [scroll, setScroll] = useState(false);
useEffect(() => {
window.addEventListener("scroll", () => {
setScroll(window.scrollY > 15);
});
}, []);
const [openDraw, setOpenDraw] = useState(false);
const showDrawer = () => {
setOpenDraw(true);
};
const closeDraw = () => {
setOpenDraw(false);
};
const toggleCollapsed = () => {
setCollapsed(!collapsed);
};
return (
<>
{/*<div className={"bg-nav-hov"}></div>*/}
<nav className={scroll ? 'navbar navScroll' : 'navbar'}>
<div className="nav-start">
<button className="btnMenuShow" onClick={toggleCollapsed}>
<MenuOutlined/>
</button>
<div className="nav-logo">
<button className={"btn-menu-toggle"} onClick={setToggle}><MenuUnfoldOutlined /></button>
<img src="/img/logo-white.png" alt=""/>
<div>
<div className="fw-bold text-uppercase titleText">
<small>v0.1.0</small>{" "}
</div>
{/*<div className="version fs-10">System Activity Monitoring</div>*/}
</div>
</div>
{collapsed ? <div id="backdrop" className="backdropMenu"></div> : ''}
<div className={collapsed ? 'menuInline in' : 'menuInline out'}>
<button className="btnMenuHide" onClick={toggleCollapsed}>
<CloseOutlined/>
</button>
{/*<Menu*/}
{/* mode="inline"*/}
{/* onClick={onClickMenu}*/}
{/* selectedKeys={[current]}*/}
{/* items={MenuList}*/}
{/*/>*/}
</div>
<div className="menuHorizontal">
{/*<Menu*/}
{/* onClick={onClickMenu}*/}
{/* selectedKeys={[current]}*/}
{/* mode="horizontal"*/}
{/* items={MenuList}*/}
{/*/>*/}
</div>
</div>
<div className="nav-end">
<div className="nav-account" style={{display: 'flex'}}>
<div className="container-account">
<div className="account">
<div className="text">
<div className="name">{dataUser?.result?.fullName}</div>
<div className="role">{dataUser?.result?.roleNm}</div>
</div>
<ViewImage fotoMember={null}/>
</div>
<div className="detail-account">
<div className="header">
<ViewImage fotoMember={null}/>
<div className="text">
<div className="name">{dataUser?.result?.username}</div>
<div className="role">Cabang {dataUser?.result?.branchNm}</div>
</div>
</div>
<div className="feature">
<button type="button" style={{cursor: 'pointer'}} onClick={() => modalChangeProfile()}>Ubah Profil</button>
<button type="button" style={{cursor: 'pointer'}} onClick={() => modalChangePassword('normal')}>Ubah Password</button>
<div className="switchTheme">
<Switch style={{width: '50px'}} checkedChildren={<MoonOutlined/>} unCheckedChildren={<SunOutlined/>} onChange={buttonHandler} checked={isTheme}/>
</div>
</div>
<div className="containerSignOut">
<button type="button" style={{cursor: 'pointer'}} onClick={() => logout()}>Sign Out</button>
</div>
</div>
</div>
</div>
<button className="btnNotif" onClick={showDrawer}>
<BellOutlined/>
<div className="count">9</div>
</button>
</div>
</nav>
<Drawer
title="Notifikasi"
placement='right'
closable={true}
onClose={closeDraw}
open={openDraw}
key='right'
>
<ul className="navNotif">
<li className="containerNotif">
<div className="date text-primary">
<span className="shape"></span>
<span>20 Januari 2024</span>
</div>
<div className="notification notif-unread">
<div className="containerIcon">
<div className="icon">
<BellOutlined/>
</div>
</div>
<div className="desc">
<div className="title">Aktifitas</div>
<div className="subtitle">update hasil aktifitas</div>
</div>
</div>
<div className="notification notif-unread">
<div className="containerIcon">
<div className="icon">
<BellOutlined/>
</div>
</div>
<div className="desc">
<div className="title">Aktifitas</div>
<div className="subtitle">update hasil aktifitas</div>
</div>
</div>
<div className="notification notif-unread">
<div className="containerIcon">
<div className="icon">
<BellOutlined/>
</div>
</div>
<div className="desc">
<div className="title">Aktifitas</div>
<div className="subtitle">update hasil aktifitas</div>
</div>
</div>
</li>
<li className="containerNotif">
<div className="date text-primary">
<span className="shape"></span>
<span>19 Januari 2024</span>
</div>
<div className="notification notif-unread">
<div className="containerIcon">
<div className="icon">
<BellOutlined/>
</div>
</div>
<div className="desc">
<div className="title">Aktifitas</div>
<div className="subtitle">update hasil aktifitas</div>
</div>
</div>
</li>
</ul>
</Drawer>
<div style={{background:"red"}}></div>
<ExportedImage
className="bg-nav"
src={"/img/bg152.jpg"}
fill={true}
// priority={true}
alt="background nav"
/>
</>
);
}
export default React.memo(Navbar);

View File

@ -1,6 +1,5 @@
"use client"; "use client";
import React, {useEffect, useRef, useState} from "react"; import React, {useEffect, useRef, useState} from "react";
import breadcrumbStore from "@/store/breadcrumbStore";
import modalStore from "@/store/modal"; import modalStore from "@/store/modal";
import confirmStore from "@/store/confirmStore"; import confirmStore from "@/store/confirmStore";
import {API} from "@/lib/API"; import {API} from "@/lib/API";
@ -33,7 +32,7 @@ const RefTemplate = ({
}) => { }) => {
const formRef = useRef(); const formRef = useRef();
const {modalOpen, modalClose, FormId, modalStat, setModalLoading, setModalDetail, actionType} = modalStore(); const {modalOpen, modalClose, FormId, modalStat, setModalLoading, setModalDetail, actionType} = modalStore();
const {setSubTitle, setTitle, setShow} = breadcrumbStore();
const {confirmOpen, confirmClose, setConfirmLoading} = confirmStore(); const {confirmOpen, confirmClose, setConfirmLoading} = confirmStore();
const {notifOpen} = notifStore() const {notifOpen} = notifStore()
@ -458,8 +457,6 @@ const RefTemplate = ({
}; };
useEffect(() => { useEffect(() => {
setTitle(refTitle);
setSubTitle(refSubTitle);
initColumn(); initColumn();
if (!serverSide){ if (!serverSide){
getData(); getData();
@ -486,8 +483,6 @@ const RefTemplate = ({
<WrapperContent> <WrapperContent>
<div className="containers"> <div className="containers">
<div className="headContent"> <div className="headContent">
<div className="containerTitle"> <div className="containerTitle">
<div className="breadCrumb"> <div className="breadCrumb">
<div className="text">{refTitle}</div> <div className="text">{refTitle}</div>
@ -496,7 +491,7 @@ const RefTemplate = ({
<div className="title text-dark-grey left">{refSubTitle}</div> <div className="title text-dark-grey left">{refSubTitle}</div>
</div> </div>
<div className="filter mt-3"> <div className="filter">
<SearchInput handleSearch={handleSearch}/> <SearchInput handleSearch={handleSearch}/>
<div className={"container-act-toolbar mt-0"} style={{ width: 'auto' }}> <div className={"container-act-toolbar mt-0"} style={{ width: 'auto' }}>
<ToolBar getData={getData} <ToolBar getData={getData}

View File

@ -2,47 +2,37 @@ import {Helper} from "@/lib/Helper";
import {DatePicker, Space} from 'antd'; import {DatePicker, Space} from 'antd';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
const { RangePicker } = DatePicker; const {RangePicker} = DatePicker;
const rangePresets = [ const rangePresets = [
{ label: 'Seminggu terakhir', value: [dayjs().add(-7, 'd'), dayjs()] }, {label: 'Hari ini', value: [dayjs().startOf("day"), dayjs().endOf("day")]},
{ label: 'Bulan ini', value: [dayjs().startOf("month") , dayjs().endOf("month")] }, {label: '7 Hari Terakhir', value: [dayjs().add(-7, 'd'), dayjs()]},
{ label: 'Bulan lalu', value: [dayjs().startOf("month").month(dayjs().month() - 1), dayjs().endOf("month").month(dayjs().month() - 1)] }, {label: 'Bulan ini', value: [dayjs().startOf("month"), dayjs().endOf("month")]},
{label: 'Tahun ini', value: [dayjs().startOf("year"), dayjs().endOf("year")]}, {label: '1 Bulan Terakhir', value: [dayjs().add(-1, 'M'), dayjs()]},
{label: 'Tahun lalu', value: [dayjs().startOf('year').year(dayjs().year() - 1), dayjs().endOf('year').year(dayjs().year() - 1)]}, {label: '3 Bulan Terakhir', value: [dayjs().add(-3, 'M'), dayjs()]},
]; ];
const InputDateRange = ({ const InputDateRange = ({
title, title, name, register, error, placeholder, maxlength, required, setValue, format = 'DD MMM YYYY', picker = 'default',
name,
register,
error,
placeholder,
maxlength,
required,
setValue,
format = 'DD MMM YYYY',
picker = 'default',
}) => { }) => {
let setRequired = (required) ? { required: `Data ${title} Harus di Isi` } : {} let setRequired = (required) ? {required: `Data ${title} Harus di Isi`} : {}
let setMaxLength = (maxlength) ? { maxLength: { value: maxlength, message: `Maksimal ${maxlength} Karakter` } } : {} let setMaxLength = (maxlength) ? {maxLength: {value: maxlength, message: `Maksimal ${maxlength} Karakter`}} : {}
let validateList = {...setRequired,...setMaxLength} let validateList = {...setRequired, ...setMaxLength}
const onChange = (_, dateString) => { const onChange = (_, dateString) => {
setValue(name, { setValue(name, {
startDate: dateString[0], startDate: dateString[0], endDate: dateString[1]
endDate: dateString[1] }, {shouldValidate: true})
}, { shouldValidate: true })
}; };
const newFormat = Helper.setFormatInputDate(picker, format) const newFormat = Helper.setFormatInputDate(picker, format)
return ( return (<div className={`form-group mb-10 ${(error[name]?.message) ? 'error' : ''}`} style={{marginTop: '-18px'}}>
<div className={`form-group mb-10 ${(error[name]?.message) ? 'error' : ''}`} style={{marginTop: '-15px'}}>
<div className="floating-label-content"> <div className="floating-label-content">
<Space direction="vertical" size={0} className='w-full'> <Space direction="vertical" size={0} className='w-full'>
<label className="floating-label" style={{zIndex: '1', top: '0'}} htmlFor={name}> {title} </label> <label className="floating-label" htmlFor={name}> {title} </label>
<RangePicker <RangePicker
{...register(name, validateList)} {...register(name, validateList)}
name={name} name={name}
@ -58,9 +48,8 @@ const InputDateRange = ({
</Space> </Space>
{error[name] && <div className="error-form">{error[name]?.message}</div>} {error[name] && <div className="error-form">{error[name]?.message}</div>}
</div> </div>
</div> </div>);
); };
};
export default InputDateRange; export default InputDateRange;

View File

@ -13,6 +13,7 @@ const InputSelect = ({
setDisabled, setDisabled,
val, val,
loading, loading,
style,
withSearch = true, withSearch = true,
getDataOnChange getDataOnChange
}) => { }) => {
@ -33,7 +34,7 @@ const InputSelect = ({
return ( return (
<> <>
<div className={`form-group mb-10 ${error[name]?.message ? "error" : ""}`}> <div className={`form-group mb-10 ${error[name]?.message ? "error" : ""}`} style={style}>
<div className="floating-label-content"> <div className="floating-label-content">
<Select <Select
disabled={setReadonly || setDisabled} disabled={setReadonly || setDisabled}

View File

@ -0,0 +1,138 @@
import { Select } from "antd";
import {useEffect, useState} from "react";
import { API } from "@/lib/API";
let timeout;
let currentValue;
const InputSelectRemote = ({
title,
name,
endPoint,
register,
error,
required,
setValue,
setReadonly,
setDisabled,
setMultiple,
val,
withSearch = true,
getDataOnChange,
}) => {
const [data, setData] = useState([]);
const [loading, setLoading] = useState(false);
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 defaultData = async () => {
setLoading(true);
let res = await API.GET(endPoint + "?page=0&size=10");
const data = res.result.data.map((v) => ({
value: v.wilayahId,
text: v.wil,
}));
setData(data)
setLoading(false);
}
const fetch = (value, callback) => {
if (timeout) {
clearTimeout(timeout);
timeout = null;
}
currentValue = value;
const getData = async () => {
setLoading(true);
let res = await API.GET(endPoint + "?page=0&size=10&search=" + value);
console.log(res);
const data = res.result.data.map((v) => ({
value: v.wilayahId,
text: v.wil,
}));
setLoading(false);
callback(data);
};
if (value) {
timeout = setTimeout(getData, 1000);
} else {
callback([]);
}
};
const handleSearch = (newValue) => {
setData([[]])
fetch(newValue, setData);
};
useEffect(() => {
defaultData()
}, []);
const getById = async (id) => {
setLoading(true);
let res = await API.GET(endPoint + "/" + id);
setLoading(false);
setData([{
value: res?.result?.wilayahId,
text: res?.result?.wil,
}]);
}
useEffect(() => {
if (val){
getById(val)
}
}, [val]);
return (
<>
<div
className={`form-group mb-10 ${error[name]?.message ? "error" : ""}`}
>
<div className="floating-label-content">
<Select
disabled={setReadonly || setDisabled}
mode={setMultiple ? "multiple" : false}
{...register(name, validateList)}
name={name}
showSearch={withSearch}
id={name}
value={val}
loading={loading}
onChange={handleChange}
onSearch={handleSearch}
filterOption={false}
allowClear={true}
style={{ width: "100%" }}
placeholder={"Ketik Untuk Mencari Data " + title}
className="form-control borderInput floating-input"
notFoundContent={<div>Data tidak ditemukan</div>}
options={(data || []).map((d) => ({
value: d.value,
label: d.text,
}))}
/>
<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 InputSelectRemote;

View File

@ -15,12 +15,14 @@ import InputCustom from "./InputCustom";
import InputPercentage from "./InputPercentage"; import InputPercentage from "./InputPercentage";
import InputSwitch from "./InputSwitch"; import InputSwitch from "./InputSwitch";
import InputAntFile from "./InputAntFile"; import InputAntFile from "./InputAntFile";
import InputSelectRemote from "@/components/util/Input/InputSelectRemote";
const Input = { const Input = {
Text: InputText, Text: InputText,
Number: InputNumber, Number: InputNumber,
Money: InputMoney, Money: InputMoney,
Select: InputSelect, Select: InputSelect,
SelectRemote: InputSelectRemote,
Username: InputUsername, Username: InputUsername,
Email: InputEmail, Email: InputEmail,
Password: InputPassword, Password: InputPassword,
@ -36,23 +38,4 @@ const Input = {
AntFile: InputAntFile AntFile: InputAntFile
} }
export const REF_INPUT_NAME = {
TEXT: 'Text',
NUMBER: 'Number',
MONEY: 'Money',
SELECT: 'Select',
USERNAME: 'Username',
EMAIL: 'Email',
PASSWORD: 'Password',
TEXTAREA: 'Textarea',
IMAGE: 'Image',
DATERANGE: 'DateRange',
TEXT_TASK: 'TextTask',
DATE: 'Date',
UPLOAD_IMAGE: 'UploadImage',
CUSTOM: 'Custom',
PERCENTAGE: 'Percentage',
SWITCH: 'Switch',
}
export default Input export default Input

View File

@ -1,12 +1,22 @@
import {motion} from "framer-motion" import {motion} from "framer-motion"
import {useEffect} from "react";
import {useRouter} from "next/navigation";
import menuStore from "@/store/menuStore";
export default function WrapperContent({children,type}) { export default function WrapperContent({children,type}) {
const router = useRouter()
const {setLoadingPageStatus} = menuStore()
useEffect(() => {
setLoadingPageStatus(false)
}, [router]);
return( return(
<> <>
<motion.div <motion.div
initial={{ opacity: 0, y: 20 }} initial={{ opacity: 0, x: 40 }}
animate={{ opacity: 1, y: 0 }} animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, y: 20 }} exit={{ opacity: 0, x: 0 }}
> >
{children} {children}
<div className={(type) ? 'setImageBoard':''}></div> <div className={(type) ? 'setImageBoard':''}></div>

View File

@ -9,451 +9,89 @@ const role = async () => {
}; };
}); });
} }
const cabang = async () => { const cabang = async () => {
let response = await API.GET(`/ref/branch`); let response = await API.GET(`/ref/branch`);
return response?.result?.data?.map((v) => { return response?.result?.data?.map((v) => {
return { return {
value: v.kdCab, value: v.branchId,
label: v.namaCabang, label: v.nm,
}; };
}); });
} }
const productType = async () => { const divisi = async () => {
let response = await API.GET(`/ref/productType`); let response = await API.GET(`/ref/divisi`);
return response?.result?.data?.map((v) => { return response?.result?.data?.map((v) => {
return { return {
value: v.productTypeId, value: v.divisiId,
label: v.definition, label: v.divisiNm,
}; };
}); });
} }
const product = async () => { const agama = async () => {
let response = await API.GET(`/ref/product`); let response = await API.GET(`/ref/agama`);
return response?.result?.data?.map((v) => { return response?.result?.data?.map((v) => {
return { return {
value: v.id, value: v.agamaId,
label: v.prodName, label: v.agamaNm,
}; };
}); });
} }
const jabatan = async () => {
const branchType = async () => { let response = await API.GET(`/ref/jabatan`);
let response = await API.GET(`/ref/branch-type`);
return response?.result?.data?.map((v) => { return response?.result?.data?.map((v) => {
return { return {
value: v.id, value: v.jabatanId,
label: v.definition, label: v.jabatanNm,
}; };
}); });
} }
const branch = async () => {
let response = await API.GET(`/ref/branch`);
return response?.result?.data?.map((v) => {
return {
value: v.id,
label: v.branchName,
};
});
}
const wilayah = async () => {
let response = await API.GET(`/ref/wilayah`);
return response?.result?.data?.map((v) => {
return {
value: v.wilayahId,
label: v.wil,
};
});
}
const bank = async () => {
let response = await API.GET(`/ref/bank`);
return response?.result?.data?.map((v) => {
return {
value: v.bankCode,
label: v.bankDescription,
};
});
}
const productGroup = async () => {
let response = await API.GET(`/ref/group-product`);
return response?.result?.data?.map((v) => {
return {
value: v.id,
label: v.productGrpName,
};
});
}
const developer = async () => {
let response = await API.GET(`/ref/developer`);
return response?.result?.data?.map((v) => {
return {
value: v.id,
label: v.developerName,
};
});
}
const productCode = async () => {
let response = await API.GET(`/cbs/loan-product`);
return response?.result?.data?.map((v) => {
return {
value: v.prodid,
label: v.prodnm,
};
});
}
const aplId = async () => {
let response = await API.GET(`/cbs-config/applid`);
return response?.result?.data?.map((v) => {
return {
value: v.parmid,
label: v.parmnm,
};
});
}
const govPRG = async () => {
let response = await API.GET(`/cbs-config/govprg`);
return response?.result?.data?.map((v) => {
return {
value: v.parmid,
label: v.parmnm,
};
});
}
const proposalType = async () => {
let response = await API.GET(`/ref/proposal-type`);
return response?.result?.data?.map((v) => {
return {
value: v.id,
label: v.definition,
};
});
}
const workflowStatus = async () => {
let response = await API.GET(`/ref/workflow-status`);
return response?.result?.data?.map((v) => {
return {
value: v.id,
label: v.statusDefinition,
};
});
}
const groupUser = async () => {
let response = await API.GET(`/ref/user-group`);
return response?.result?.data?.map((v) => {
return {
value: v.id,
label: v.groupDefinition,
};
});
}
const workflowType = async () => {
let response = await API.GET(`/ref/workflow-type`);
return response?.result?.data?.map((v) => {
return {
value: v.id,
label: v.definition,
};
});
}
const pekerjaanPPDPP = async () => {
let response = await API.GET(`/ref/pekerjaan-ppdpp`);
return response?.result?.data?.map((v) => {
return {
value: v.pekerjaan,
label: v.nmPekerjaan,
};
});
}
const statusPembiayaan = async () => {
let response = await API.GET(`/ref/status-pembiayaan`);
return response?.result?.data?.map((v) => {
return {
value: v.statusPembiayaan,
label: v.namaStatusPembiayaan,
};
});
}
const pekerjaan = async () => {
let response = await API.GET(`/ref/pekerjaan`);
return response?.result?.data?.map((v) => {
return {
value: v.idPekerjaan,
label: v.nmPekerjaan,
};
});
}
const accountTypeFLPP = async () => {
let response = await API.GET(`/ref/jenis_accflpp`);
return response?.result?.data?.map((v) => {
return {
value: v.id,
label: v.definition,
};
});
}
const masterGroupScoring = async () => {
let response = await API.GET(`/ref/master-group-scoring`);
return response?.result?.data?.map((v) => {
return {
value: v.id,
label: v.paramName,
};
});
}
const developerScoring = async () => {
let response = await API.GET(`/ref/developer-param-scoring`);
return response?.result?.data?.map((v) => {
return {
value: v.id,
label: v.definition,
};
});
}
const productTapera = async (filter = '') => {
if ( filter ) {
filter = '?' + new URLSearchParams(filter).toString()
}
let response = await API.GET(`/ref-tapera/product${filter}`);
return response?.result?.data?.map((v) => {
return {
value: v.id,
label: v.namaProduk,
};
});
}
const jenisPerumahanTapera = async () => {
let response = await API.GET(`/ref-tapera/jenis-perumahan`);
return response?.result?.data?.map((v) => {
return {
value: v.id,
label: v.definition,
};
});
}
const perumahanTapera = async () => {
let response = await API.GET(`/ref-tapera/rumah`);
return response?.result?.data?.map((v) => {
return {
value: v.idRumah,
label: v.namaPerumahan,
...v
};
});
}
const wilayahTapera = async () => {
let response = await API.GET(`/ref-tapera/wilayah/dropdown`);
return response?.result?.map((v) => {
return {
value: v.wilayahId,
label: v.wil,
};
});
}
const jenisImbPbg = async () => {
let response = await API.GET(`/ref-tapera/jenis-imb/dropdown`);
return response?.result?.map((v) => {
return {
value: v.id,
label: v.definition,
};
});
}
const segemenPekerjaan = async () => {
let response = await API.GET(`/ref-tapera/segmen-pekerjaan/dropdown`);
return response?.result?.map((v) => {
return {
value: v.segmenCode,
label: v.definition,
};
});
}
const jenisAkadTapera = async () => {
let response = await API.GET(`/ref-tapera/jenis-akad/dropdown`);
return response?.result?.map((v) => {
return {
value: v.id,
label: v.definition,
};
});
}
const jenisBungaTapera = async () => {
let response = await API.GET(`/ref-tapera/jenis-bunga/dropdown`);
return response?.result?.map((v) => {
return {
value: v.id,
label: v.definition,
};
});
}
const jenisPembayaranAngsuran = async () => {
let response = await API.GET(`/ref-tapera/jenis-bayar-angsuran/dropdown`);
return response?.result?.map((v) => {
return {
value: v.id,
label: v.definition,
};
});
}
const jenisKelamin = async () => { const jenisKelamin = async () => {
let response = await API.GET(`/ref/jenis-kelamin`); let response = await API.GET(`/ref/jenisKelamin`);
return response?.result?.data?.map((v) => { return response?.result?.data?.map((v) => {
return { return {
value: v.id, value: v.jenisKelaminId,
label: v.description, label: v.jenisKelaminNm,
}; };
}); });
} }
const statusKaryawan = async () => {
const statusKawin = async () => { let response = await API.GET(`/ref/statusKaryawan`);
let response = await API.GET(`/ref/status-kawin`);
return response?.result?.data?.map((v) => { return response?.result?.data?.map((v) => {
return { return {
value: v.idStatusKawin, value: v.statusKaryawanId,
label: v.nmStatusKawin, label: v.statusKaryawanNm,
}; };
}); });
} }
const jenisPembiayaan = async () => { const karyawan = async () => {
let response = await API.GET(`/ref-tapera/jenis-pembiayaan`); let response = await API.GET(`/ref/karyawan`);
return response?.result?.data?.map((v) => { return response?.result?.data?.map((v) => {
return { return {
value: v.id, value: v.karyawanId,
label: v.definition, label: v.karyawanNm,
}; };
}); });
} }
const jenisPenjadwalan = async () => {
const prinsipPembiayaan = async () => { let response = await API.GET(`/ref/tipeQuotes`);
let response = await API.GET(`/ref-tapera/prinsip-pembiayaan`);
return response?.result?.data?.map((v) => { return response?.result?.data?.map((v) => {
return { return {
value: v.id, value: v.tipeQuotesId,
label: v.definition, label: v.tipeQuotesNm,
}; };
}); });
} }
const tipeProgram = async () => {
let response = await API.GET(`/ref-tapera/tipe-program`);
return response?.result?.data?.map((v) => {
return {
value: v.id,
label: v.definition,
};
});
}
const lokasiPerumahanTapera = async () => {
let response = await API.GET(`/ref-tapera/perumahaan/dropdown`);
return response?.result?.map((v) => {
return {
value: v.idLokasi,
label: v.namaPerumahan,
};
});
}
const kolektibilitas = async () => {
let response = await API.GET(`/ref-tapera/kolektibilitas`);
return response?.result?.data?.map((v) => {
return {
value: v.id,
label: v.definition,
};
});
}
const developerTapera = async () => {
let response = await API.GET(`/ref-tapera/developer`);
return response?.result?.data?.map((v) => {
return {
value: v.id,
label: v.developerName,
};
});
}
const jenisSertifikat = async () => {
let response = await API.GET(`/ref/jenis-sertifikat`);
return response?.result?.data?.map((v) => {
return {
value: v.id,
label: v.defJenis,
};
});
}
export const DropdownAPI = { export const DropdownAPI = {
role, role,
cabang, cabang,
productType, divisi,
branchType, agama,
branch, jabatan,
bank,
wilayah,
productGroup,
productCode,
aplId,
govPRG,
developer,
product,
proposalType,
workflowStatus,
groupUser,
workflowType,
pekerjaanPPDPP,
statusPembiayaan,
pekerjaan,
accountTypeFLPP,
masterGroupScoring,
developerScoring,
productTapera,
jenisPerumahanTapera,
perumahanTapera,
wilayahTapera,
jenisImbPbg,
segemenPekerjaan,
jenisAkadTapera,
jenisBungaTapera,
jenisPembayaranAngsuran,
jenisKelamin, jenisKelamin,
statusKawin, statusKaryawan,
jenisPembiayaan, karyawan,
prinsipPembiayaan, jenisPenjadwalan
tipeProgram,
lokasiPerumahanTapera,
kolektibilitas,
developerTapera,
jenisSertifikat
}; };

View File

@ -1,15 +0,0 @@
import {create} from 'zustand';
const breadcrumbStore = create((set) => ({
title: '',
subTitle: '',
widthTitle: '0px',
setTitle: (data) => set(() => ({title: data})),
setSubTitle: (data) => set(() => ({subTitle: data})),
setHide: () => set(() => ({widthTitle: '0px'})),
setShow: () => set(() => ({widthTitle: '270px'})),
}));
export default breadcrumbStore;

View File

@ -2,7 +2,9 @@ import {create} from 'zustand';
const menuStore = create((set) => ({ const menuStore = create((set) => ({
toggleStatus: true, toggleStatus: true,
loadingPageStatus:false,
setToggle: () => set((prev) => ({toggleStatus: !prev.toggleStatus})), setToggle: () => set((prev) => ({toggleStatus: !prev.toggleStatus})),
setLoadingPageStatus: (stat) => set((prev) => ({loadingPageStatus: stat})),
})); }));
export default menuStore; export default menuStore;

View File

@ -1,96 +0,0 @@
import {create} from "zustand";
const modalDashboardStore = create((set) => ({
modalStat: false,
modalLoading: false,
FormId: null,
actionType: null,
typeData: null,
typeKolek: null,
datatableDetail: false,
datatableDetailCabang: false,
datatableDetailKolektability: false,
datatableDetailNasabah: false,
dataNasabah: false,
kdCabangKonsol: null,
kdCabang: null,
nmCabangKonsol: null,
nmCabang: null,
customerId: null,
title: null,
subTitle: null,
modalOpen: (id, action) =>
set(() => ({
modalStat: true,
modalLoading: true,
FormId: id,
actionType: action
})),
modalClose: () =>
set(() => ({
modalStat: false,
FormId: null,
})),
setModalLoading: (status, type) =>
set(() => ({
modalLoading: status,
})),
setTypeData: (text) =>
set(() => ({
typeData: text
})),
setTypeKolek: (text) =>
set(() => ({
typeKolek: text
})),
setDatatableDetail: (status) =>
set(() => ({
datatableDetail: status
})),
setDatatableDetailCabang: (status) =>
set(() => ({
datatableDetailCabang: status
})),
setDatatableDetailKolektability: (status) =>
set(() => ({
datatableDetailKolektability: status
})),
setDatatableDetailNasabah: (status) =>
set(() => ({
datatableDetailNasabah: status
})),
setDataNasabah: (status) =>
set(() => ({
dataNasabah: status
})),
setKdCabangKonsol: (status) =>
set(() => ({
kdCabangKonsol: status
})),
setKdCabang: (status) =>
set(() => ({
kdCabang: status
})),
setNmCabangKonsol: (status) =>
set(() => ({
nmCabangKonsol: status
})),
setNmCabang: (status) =>
set(() => ({
nmCabang: status
})),
setCustomerId: (status) =>
set(() => ({
customerId: status
})),
setTitle: (status) =>
set(() => ({
title: status
})),
setSubTitle: (status) =>
set(() => ({
subTitle: status
}))
}));
export default modalDashboardStore;

View File

@ -1,9 +0,0 @@
import {create} from 'zustand';
const useStore = create((set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
decrement: () => set((state) => ({ count: state.count - 1 })),
}));
export default useStore;