mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-20 13:32:00 +00:00
Compare commits
485 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| fa32995802 | |||
| c832c4adeb | |||
| eda3f0f1be | |||
| d0d201bf3a | |||
| c7b04c5bc6 | |||
| c37950a230 | |||
| f88af89562 | |||
| 7da95b80b0 | |||
| 883d68032a | |||
| c74ed18a16 | |||
| e45a9ba5e4 | |||
| de7076e513 | |||
| 6706f361d8 | |||
| 15e6372c30 | |||
| 6dd3593f70 | |||
| 5d376f8783 | |||
| 304d14a6fe | |||
| 4bd6fe8c35 | |||
| 0b0ecd3bc4 | |||
| 58369b8ffa | |||
| cbb4f7421e | |||
| 459605f133 | |||
| 943c0e05b9 | |||
| 9143248e1d | |||
| 4b9d0d2064 | |||
| c8f596ad2a | |||
| a65d00edc8 | |||
| 1e9d02b4b7 | |||
| 135fc2d5d3 | |||
| 189c152745 | |||
| f0f6ec53cb | |||
| a0556ea1f4 | |||
| 81ce36e326 | |||
| 48c31373bf | |||
| d7ce8c667a | |||
| 6290199074 | |||
| 4f3dfb4221 | |||
| a13a51a16f | |||
| 896a0c6de2 | |||
| 9c5dc0dbb5 | |||
| 81003eac63 | |||
| e322e0d078 | |||
| 17e6eef0c5 | |||
| 6114d706ad | |||
| d14fa2ed2b | |||
| 537fc617ff | |||
| 7a6a35568f | |||
| d2c485fdf0 | |||
| 0c49978033 | |||
| 00de4782e7 | |||
| c546bd6b3c | |||
| 258324f092 | |||
| 12a69b7c6c | |||
| b148a09e84 | |||
| adc995dbe7 | |||
| 9cbc703a63 | |||
| 41e6848d75 | |||
| fa21fe8da4 | |||
| ca5b236565 | |||
| 714072aea1 | |||
| 8337fa5f55 | |||
| a9f0696b38 | |||
| 54bff12e1a | |||
| aa17143532 | |||
| 4381e42aaf | |||
| 24ed2cccbe | |||
| a9b0c084f8 | |||
| 16823fa84a | |||
| c30fcd81b2 | |||
| 7f5ae94706 | |||
| 6060ec0f7e | |||
| ef249fee12 | |||
| 71df86c8df | |||
| d61c0ab844 | |||
| b653cc1dab | |||
| 51bce1a2c7 | |||
| e76d881d8a | |||
| 392e211181 | |||
| cebe738beb | |||
| 6e5875a7b7 | |||
| b2044ac7bd | |||
| 8a467c2d65 | |||
| db8cb56984 | |||
| d1d152ef5a | |||
| 82950b0ec0 | |||
| 3110b96305 | |||
| 7e44226a6d | |||
| 3f76cb58fe | |||
| 3cf8f4c89b | |||
| 90ae7c469a | |||
| ae967c5ddb | |||
| e801ba08ad | |||
| e6f5b2493b | |||
| 5f677f5076 | |||
| 2de32dc944 | |||
| ab534e203a | |||
| eaf41805d7 | |||
| 631ebb9346 | |||
| 7e53743b07 | |||
| 70e1aca6c7 | |||
| d0d323954b | |||
| d1c24bc486 | |||
| f998d32b0a | |||
| 3226b22dfb | |||
| 9a51b2876f | |||
| ab9fbc9032 | |||
| d2f24723fc | |||
| 5e710a792f | |||
| 3c8bdfbdac | |||
| 204369e0fe | |||
| 1e2ea79a6a | |||
| 22f1a32e1b | |||
| c24c0817ae | |||
| e53325cdc5 | |||
| 6687f4af98 | |||
| 575a317eed | |||
| bdb3ab1a50 | |||
| f486a659d0 | |||
| 58b4204aab | |||
| c249585bc2 | |||
| 9c114628c7 | |||
| b35d513e44 | |||
| a904c35b7f | |||
| 2e595b5e86 | |||
| 46fa3e57cd | |||
| 79b6d6917d | |||
| 9f24d22a2c | |||
| 06f1d3f6a4 | |||
| e29613a37e | |||
| 6e6675d0a7 | |||
| 32d4c0268f | |||
| 2ab26153fd | |||
| a29bbc9a42 | |||
| e7e0e308c7 | |||
| 1ade8f8a38 | |||
| 791e8e787c | |||
| a2c43a7f1e | |||
| 12202c2021 | |||
| 4127075b13 | |||
| d9fa685ae6 | |||
| 2f4daea253 | |||
| bac72b8eb3 | |||
| 5af9c3ee27 | |||
| 1a76913f3f | |||
| 8b403a4208 | |||
| 0bab704163 | |||
| d550dcbf48 | |||
| 7fdbfe6dfb | |||
| 4e6d2088e1 | |||
| 67b180bf7c | |||
| 7853899486 | |||
| 9a04724095 | |||
| 831995e8e4 | |||
| c8cdb3e772 | |||
| e5b3af3239 | |||
| 0740f2d094 | |||
| 25a97e34c7 | |||
| 1ee1cf9ea9 | |||
| e4a1138d8d | |||
| 41bb05413c | |||
| c746bd94b2 | |||
| acea3a3063 | |||
| b269728ecd | |||
| e7a861d8a1 | |||
| 1a5a76c0f1 | |||
| 838d7277c3 | |||
| 1672705464 | |||
| 9ef4484fb3 | |||
| 645668e1f9 | |||
| fb29cea8d2 | |||
| 1ecdff855e | |||
| 7c6e079f56 | |||
| 41f8067727 | |||
| f733b0750a | |||
| 83d31b7047 | |||
| 966e0886e1 | |||
| a67d353bcb | |||
| ac2f246988 | |||
| e0ce571000 | |||
| 1bcfd9bbb4 | |||
| c561c47eae | |||
| c3338d3e05 | |||
| ba9ae07455 | |||
| c64ff527dd | |||
| f27e34128e | |||
| c8db992b17 | |||
| d76f897840 | |||
| 5e0cc3699f | |||
| 895b7afa25 | |||
| a088189ed1 | |||
| 406cfad31a | |||
| 6c9c0e1839 | |||
| eb02a8b6f7 | |||
| 73f379832c | |||
| 4233c19dc9 | |||
| 403765a2b5 | |||
| d30d7328cf | |||
| 376fa29f7e | |||
| 16d72ebf6f | |||
| 52ad696178 | |||
| 2b3aa9c3ee | |||
| 6fe85fac13 | |||
| 9964e1797a | |||
| e4f554bcd4 | |||
| c25b49c179 | |||
| a573551110 | |||
| 881e2bfc4a | |||
| 474c2a1f7d | |||
| c4de085e11 | |||
| 0676411dd5 | |||
| f05d367a5d | |||
| edb5f30d6c | |||
| 7abe9b7dc6 | |||
| caf68d438f | |||
| fa60f884c1 | |||
| c77968940e | |||
| cfb9b53b54 | |||
| caac9c20e6 | |||
| 8bf7603f66 | |||
| 8c662a5152 | |||
| da92874a40 | |||
| 5113bf4d3f | |||
| 1bdec0c9ae | |||
| 57dbcf3624 | |||
| 501a68267e | |||
| a2a57f758c | |||
| 157dfc75ed | |||
| f5ce898bd2 | |||
| c6a0c542aa | |||
| b0e11095f4 | |||
| 79acdb4b7b | |||
| 19db9a4eac | |||
| 1e0b342bbc | |||
| 23d5a41d56 | |||
| b7a30cc73a | |||
| 93beb86f91 | |||
| 0577f6ce1d | |||
| 76dd2e4c54 | |||
| 156de6112b | |||
| eb0f04310e | |||
| 27d2792a9c | |||
| ec387637ed | |||
| 64e6724664 | |||
| f319a9b5d1 | |||
| e2b35e765c | |||
| 8bfce061e6 | |||
| 64a32fd214 | |||
| 2ee88a2742 | |||
| aa21088e99 | |||
| 06dc869b84 | |||
| df73ee1fdf | |||
| cf78687315 | |||
| 66e6fa84c8 | |||
| dcd5d2692f | |||
| 3c4333021f | |||
| 56a9fc2349 | |||
| 24144f01d4 | |||
| 212fd3b4f2 | |||
| 6f0467918b | |||
| 53ee4cdc1b | |||
| b1a3796eca | |||
| 89318407ea | |||
| 6dcb97bcac | |||
| 1869fa8dc5 | |||
| 4b4b74d07c | |||
| ff9e35eb52 | |||
| 19bca9ec73 | |||
| 6facfd3d3c | |||
| b2f0bd6698 | |||
| e7085ab4ff | |||
| 44e07ddc50 | |||
| 46860a93b9 | |||
| 302da65c59 | |||
| ce8471343c | |||
| 880ff5740d | |||
| 9b53c75f2f | |||
| a4ff4f7b2a | |||
| 754e3d526b | |||
| f662f2951e | |||
| 1fd4b2aba5 | |||
| b75b5956eb | |||
| 478f52c94b | |||
| aa7b6581d9 | |||
| 757893c757 | |||
| a1dc13ceb4 | |||
| 157235433e | |||
| 57831646d9 | |||
| 095190d757 | |||
| 27f58051ad | |||
| a9cdea7318 | |||
| 7dbf880228 | |||
| 24b702548d | |||
| aacdbf0742 | |||
| 96babba4bb | |||
| acd66b0323 | |||
| c17ffc6aff | |||
| 1ea9ee3069 | |||
| 5cf98ed95e | |||
| 558a1788dc | |||
| e2036ab3dc | |||
| a83452a0e4 | |||
| 3f712a638f | |||
| 0ea78fee92 | |||
| b561ed6193 | |||
| d7ae7e00d3 | |||
| f0eabedcb2 | |||
| 94a5ce5604 | |||
| 9b56308cf0 | |||
| 6353b3aee4 | |||
| 527a155997 | |||
| f6163b1f69 | |||
| d771b20956 | |||
| da91201dde | |||
| 9b13ce2be6 | |||
| 764dacc627 | |||
| d1f43c4e42 | |||
| 5c0da471ae | |||
| 95556bfdd7 | |||
| 24269d8c76 | |||
| a6be56e6f2 | |||
| f8f5e8403a | |||
| 9f2add3a57 | |||
| 3051e931ca | |||
| 21cc01fe68 | |||
| 21b9396323 | |||
| ddbf8b0896 | |||
| 3f97ec45f8 | |||
| 1d7f100507 | |||
| 372f1698ca | |||
| 0d5e8383fd | |||
| b9015ed673 | |||
| ca42570a40 | |||
| 16a15fce66 | |||
| 8c507aa410 | |||
| 10749f06da | |||
| 293f457ecb | |||
| 7ceb25ea71 | |||
| 780c0bb9d0 | |||
| b8548b72c9 | |||
| c53f91ec3f | |||
| 96fea80f62 | |||
| d24d50474d | |||
| f3d0e12bcd | |||
| 143d640a1e | |||
| 0e49e29002 | |||
| 8461667ca2 | |||
| 3e7da624aa | |||
| 1968761b5d | |||
| 396ebe5001 | |||
| f5952f5a36 | |||
| 250c42a04b | |||
| e1569c607c | |||
| 3241cc9868 | |||
| 26ec456937 | |||
| 2b9b8e9920 | |||
| 3cac49725f | |||
| 4f0e02a93b | |||
| af60e682ee | |||
| dcebd53c45 | |||
| 1ae2d13335 | |||
| 452139eeed | |||
| eb10dfe29f | |||
| 70bdfc3b43 | |||
| c1bc7beb4a | |||
| 76cd64de5b | |||
| 07691bfd9e | |||
| 1f0c58d264 | |||
| 19ce3989ba | |||
| a136ee1190 | |||
| acd28e5deb | |||
| bfc81da349 | |||
| 64bb87f92f | |||
| f0c2910469 | |||
| 952110d7af | |||
| 6441a38a9d | |||
| 531a257e78 | |||
| be844312d3 | |||
| 6ff19f05fd | |||
| 26093034fa | |||
| 0e5b718fd7 | |||
| 0675d95a2a | |||
| d5294e9b0b | |||
| 8c84e08f3b | |||
| f32e1ceec4 | |||
| f7b0933c0f | |||
| 508a530c3a | |||
| 05a67bdc75 | |||
| 54d2c85677 | |||
| 288e4b92ff | |||
| 7e0dd1bdb1 | |||
| 57e5fafabd | |||
| e53d4e22b2 | |||
| 3c0babb62b | |||
| e7e5456d15 | |||
| d3977a0951 | |||
| 5b1dab2860 | |||
| 8ed12578b4 | |||
| 7ea599168c | |||
| a2345165c1 | |||
| e1c34cf0fb | |||
| 4332881ba6 | |||
| 73cefbb7a3 | |||
| 9ba7b5dba4 | |||
| 172d8efd8e | |||
| 69ecacc1be | |||
| 4bf4981fd4 | |||
| 6dd6147c29 | |||
| c494f8dbd5 | |||
| 211951132a | |||
| b82637fb3b | |||
| eebc9940cc | |||
| 777f0f5e81 | |||
| d29d1f27f8 | |||
| bbe55ee4c3 | |||
| 6dec9268c9 | |||
| b5d9c55fbc | |||
| d941674f9a | |||
| e6c14f57d9 | |||
| f27b261869 | |||
| 6a396ccce6 | |||
| 65e3833cd5 | |||
| 34e9e60173 | |||
| f1a8fda667 | |||
| 36113f6c2a | |||
| e259d1720c | |||
| 78750060de | |||
| ae159b9617 | |||
| 56476c7dd9 | |||
| fa5d09e4fb | |||
| df1b4c29e5 | |||
| 20f6686afc | |||
| 18027f0bb9 | |||
| 2976ffffbf | |||
| 5983a44311 | |||
| 42dd91117e | |||
| 60d0d77dff | |||
| 7723e2a8d3 | |||
| 88fe135cb4 | |||
| 83701a9689 | |||
| e765a7a5fb | |||
| 75a5caa63b | |||
| c40c707c17 | |||
| c3da39ef1b | |||
| 230e966197 | |||
| 62b3894983 | |||
| 8dd1ebdfe4 | |||
| 35c809193b | |||
| e6acfc1214 | |||
| 36b66d9b2f | |||
| 5c73f8f4af | |||
| 2a6f2a1646 | |||
| d40a5dd898 | |||
| ca9205618a | |||
| 14046a1add | |||
| 8ad49a4480 | |||
| 4ff196cb9d | |||
| 0afde48135 | |||
| 9b2930375d | |||
| 8a6a1e6b5c | |||
| 6924aef8c4 | |||
| fa96d7a98a | |||
| 3d3df42576 | |||
| 05886896f1 | |||
| a347024188 | |||
| 6969a2bcb8 | |||
| 8206f7de5f | |||
| a5b392ae76 | |||
| 6cbdff5398 | |||
| 87ce1e50aa | |||
| 2456d64a68 | |||
| c068fe5166 | |||
| 19b7c53ec2 | |||
| 037e4776a8 | |||
| 3be69eeff8 | |||
| a364a860fa | |||
| 8e23a805a9 | |||
| e052166b3a | |||
| 691b49a902 | |||
| 33f5ca2a57 | |||
| 02c44ced92 | |||
| 978285021e | |||
| 7e57debb98 | |||
| a5d8ac47fe | |||
| f127e16c7c | |||
| 2e1b0fef2b |
+44
@@ -0,0 +1,44 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.*
|
||||
.yarn/*
|
||||
!.yarn/patches
|
||||
!.yarn/plugins
|
||||
!.yarn/releases
|
||||
!.yarn/versions
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# next.js
|
||||
/.next/
|
||||
/out/
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
*.pem
|
||||
|
||||
# debug
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# env files (can opt-in for committing if needed)
|
||||
.env*
|
||||
|
||||
# vercel
|
||||
.vercel
|
||||
|
||||
# typescript
|
||||
*.tsbuildinfo
|
||||
next-env.d.ts
|
||||
|
||||
# idea
|
||||
.idea
|
||||
@@ -0,0 +1,76 @@
|
||||
stages: [notify]
|
||||
|
||||
# --- Notify when MR is opened/updated ---
|
||||
notify_discord_mr:
|
||||
stage: notify
|
||||
image: alpine:3.20
|
||||
rules:
|
||||
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
|
||||
variables:
|
||||
WEBHOOK_URL: $DISCORD_WEBHOOK_URL
|
||||
before_script:
|
||||
- apk add --no-cache curl jq
|
||||
script: |
|
||||
MR_URL="${CI_PROJECT_URL}/-/merge_requests/${CI_MERGE_REQUEST_IID}"
|
||||
|
||||
jq -n \
|
||||
--arg repo "$CI_PROJECT_PATH" \
|
||||
--arg mr "#${CI_MERGE_REQUEST_IID}" \
|
||||
--arg url "$MR_URL" \
|
||||
--arg requestor "${GITLAB_USER_LOGIN:-$GITLAB_USER_NAME}" \
|
||||
--arg source "$CI_MERGE_REQUEST_SOURCE_BRANCH_NAME" \
|
||||
--arg target "$CI_MERGE_REQUEST_TARGET_BRANCH_NAME" \
|
||||
--arg title "$CI_MERGE_REQUEST_TITLE" \
|
||||
'{
|
||||
username: "CI Bot - FE",
|
||||
embeds: [{
|
||||
title: "📣 [LTI WEB CLIENT] Merge Request Opened/Updated",
|
||||
description: ($mr + " in " + $repo),
|
||||
url: $url,
|
||||
color: 3447003,
|
||||
fields: [
|
||||
{name: "Author", value: $requestor, inline: true},
|
||||
{name: "Source → Target", value: ($source + " → " + $target), inline: true},
|
||||
{name: "Title", value: $title}
|
||||
]
|
||||
}]
|
||||
}' \
|
||||
| curl -sS -H "Content-Type: application/json" -d @- "$WEBHOOK_URL"
|
||||
|
||||
# --- Notify when MR is merged ---
|
||||
notify_discord_merge:
|
||||
stage: notify
|
||||
image: alpine:3.20
|
||||
rules:
|
||||
# Only run for merge request pipelines that are in merged state
|
||||
- if: '$CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_STATE == "merged"'
|
||||
variables:
|
||||
WEBHOOK_URL: $DISCORD_WEBHOOK_URL
|
||||
before_script:
|
||||
- apk add --no-cache curl jq
|
||||
script: |
|
||||
MR_URL="${CI_PROJECT_URL}/-/merge_requests/${CI_MERGE_REQUEST_IID}"
|
||||
|
||||
jq -n \
|
||||
--arg repo "$CI_PROJECT_PATH" \
|
||||
--arg mr "#${CI_MERGE_REQUEST_IID}" \
|
||||
--arg url "$MR_URL" \
|
||||
--arg requestor "${CI_MERGE_REQUEST_AUTHOR}" \
|
||||
--arg source "$CI_MERGE_REQUEST_SOURCE_BRANCH_NAME" \
|
||||
--arg target "$CI_MERGE_REQUEST_TARGET_BRANCH_NAME" \
|
||||
--arg title "$CI_MERGE_REQUEST_TITLE" \
|
||||
'{
|
||||
username: "CI Bot - FE",
|
||||
embeds: [{
|
||||
title: "✅ [LTI WEB CLIENT] Merge Request Merged",
|
||||
description: ($mr + " has been merged into " + $repo),
|
||||
url: $url,
|
||||
color: 3066993,
|
||||
fields: [
|
||||
{name: "Author", value: $requestor, inline: true},
|
||||
{name: "Source → Target", value: ($source + " → " + $target), inline: true},
|
||||
{name: "Title", value: $title}
|
||||
]
|
||||
}]
|
||||
}' \
|
||||
| curl -sS -H "Content-Type: application/json" -d @- "$WEBHOOK_URL"
|
||||
@@ -0,0 +1,2 @@
|
||||
npm run lint
|
||||
npm run build
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"singleQuote": true,
|
||||
"jsxSingleQuote": true,
|
||||
"endOfLine": "lf",
|
||||
"arrowParens": "always",
|
||||
"bracketSpacing": true,
|
||||
"embeddedLanguageFormatting": "auto",
|
||||
"htmlWhitespaceSensitivity": "css",
|
||||
"printWidth": 80,
|
||||
"proseWrap": "preserve",
|
||||
"quoteProps": "as-needed",
|
||||
"semi": true,
|
||||
"tabWidth": 2,
|
||||
"trailingComma": "es5"
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"singleQuote": true,
|
||||
"jsxSingleQuote": true,
|
||||
"endOfLine": "lf",
|
||||
"arrowParens": "always",
|
||||
"bracketSpacing": true,
|
||||
"embeddedLanguageFormatting": "auto",
|
||||
"htmlWhitespaceSensitivity": "css",
|
||||
"printWidth": 80,
|
||||
"proseWrap": "preserve",
|
||||
"quoteProps": "as-needed",
|
||||
"semi": true,
|
||||
"tabWidth": 2,
|
||||
"trailingComma": "es5"
|
||||
}
|
||||
@@ -1,93 +1,36 @@
|
||||
# LTI - Client
|
||||
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
|
||||
|
||||
## Getting Started
|
||||
|
||||
First, run the development server:
|
||||
|
||||
## Getting started
|
||||
|
||||
To make it easy for you to get started with GitLab, here's a list of recommended next steps.
|
||||
|
||||
Already a pro? Just edit this README.md and make it your own. Want to make it easy? [Use the template at the bottom](#editing-this-readme)!
|
||||
|
||||
## Add your files
|
||||
|
||||
- [ ] [Create](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#create-a-file) or [upload](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#upload-a-file) files
|
||||
- [ ] [Add files using the command line](https://docs.gitlab.com/topics/git/add_files/#add-files-to-a-git-repository) or push an existing Git repository with the following command:
|
||||
|
||||
```
|
||||
cd existing_repo
|
||||
git remote add origin https://gitlab.com/mbugroup/lti-client.git
|
||||
git branch -M main
|
||||
git push -uf origin main
|
||||
```bash
|
||||
npm run dev
|
||||
# or
|
||||
yarn dev
|
||||
# or
|
||||
pnpm dev
|
||||
# or
|
||||
bun dev
|
||||
```
|
||||
|
||||
## Integrate with your tools
|
||||
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
||||
|
||||
- [ ] [Set up project integrations](https://gitlab.com/mbugroup/lti-client/-/settings/integrations)
|
||||
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
|
||||
|
||||
## Collaborate with your team
|
||||
This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
|
||||
|
||||
- [ ] [Invite team members and collaborators](https://docs.gitlab.com/ee/user/project/members/)
|
||||
- [ ] [Create a new merge request](https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html)
|
||||
- [ ] [Automatically close issues from merge requests](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically)
|
||||
- [ ] [Enable merge request approvals](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/)
|
||||
- [ ] [Set auto-merge](https://docs.gitlab.com/user/project/merge_requests/auto_merge/)
|
||||
## Learn More
|
||||
|
||||
## Test and Deploy
|
||||
To learn more about Next.js, take a look at the following resources:
|
||||
|
||||
Use the built-in continuous integration in GitLab.
|
||||
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
|
||||
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
|
||||
|
||||
- [ ] [Get started with GitLab CI/CD](https://docs.gitlab.com/ee/ci/quick_start/)
|
||||
- [ ] [Analyze your code for known vulnerabilities with Static Application Security Testing (SAST)](https://docs.gitlab.com/ee/user/application_security/sast/)
|
||||
- [ ] [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/ee/topics/autodevops/requirements.html)
|
||||
- [ ] [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/ee/user/clusters/agent/)
|
||||
- [ ] [Set up protected environments](https://docs.gitlab.com/ee/ci/environments/protected_environments.html)
|
||||
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
|
||||
|
||||
***
|
||||
## Deploy on Vercel
|
||||
|
||||
# Editing this README
|
||||
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
|
||||
|
||||
When you're ready to make this README your own, just edit this file and use the handy template below (or feel free to structure it however you want - this is just a starting point!). Thanks to [makeareadme.com](https://www.makeareadme.com/) for this template.
|
||||
|
||||
## Suggestions for a good README
|
||||
|
||||
Every project is different, so consider which of these sections apply to yours. The sections used in the template are suggestions for most open source projects. Also keep in mind that while a README can be too long and detailed, too long is better than too short. If you think your README is too long, consider utilizing another form of documentation rather than cutting out information.
|
||||
|
||||
## Name
|
||||
Choose a self-explaining name for your project.
|
||||
|
||||
## Description
|
||||
Let people know what your project can do specifically. Provide context and add a link to any reference visitors might be unfamiliar with. A list of Features or a Background subsection can also be added here. If there are alternatives to your project, this is a good place to list differentiating factors.
|
||||
|
||||
## Badges
|
||||
On some READMEs, you may see small images that convey metadata, such as whether or not all the tests are passing for the project. You can use Shields to add some to your README. Many services also have instructions for adding a badge.
|
||||
|
||||
## Visuals
|
||||
Depending on what you are making, it can be a good idea to include screenshots or even a video (you'll frequently see GIFs rather than actual videos). Tools like ttygif can help, but check out Asciinema for a more sophisticated method.
|
||||
|
||||
## Installation
|
||||
Within a particular ecosystem, there may be a common way of installing things, such as using Yarn, NuGet, or Homebrew. However, consider the possibility that whoever is reading your README is a novice and would like more guidance. Listing specific steps helps remove ambiguity and gets people to using your project as quickly as possible. If it only runs in a specific context like a particular programming language version or operating system or has dependencies that have to be installed manually, also add a Requirements subsection.
|
||||
|
||||
## Usage
|
||||
Use examples liberally, and show the expected output if you can. It's helpful to have inline the smallest example of usage that you can demonstrate, while providing links to more sophisticated examples if they are too long to reasonably include in the README.
|
||||
|
||||
## Support
|
||||
Tell people where they can go to for help. It can be any combination of an issue tracker, a chat room, an email address, etc.
|
||||
|
||||
## Roadmap
|
||||
If you have ideas for releases in the future, it is a good idea to list them in the README.
|
||||
|
||||
## Contributing
|
||||
State if you are open to contributions and what your requirements are for accepting them.
|
||||
|
||||
For people who want to make changes to your project, it's helpful to have some documentation on how to get started. Perhaps there is a script that they should run or some environment variables that they need to set. Make these steps explicit. These instructions could also be useful to your future self.
|
||||
|
||||
You can also document commands to lint the code or run tests. These steps help to ensure high code quality and reduce the likelihood that the changes inadvertently break something. Having instructions for running tests is especially helpful if it requires external setup, such as starting a Selenium server for testing in a browser.
|
||||
|
||||
## Authors and acknowledgment
|
||||
Show your appreciation to those who have contributed to the project.
|
||||
|
||||
## License
|
||||
For open source projects, say how it is licensed.
|
||||
|
||||
## Project status
|
||||
If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers.
|
||||
Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "VERCEL_GIT_COMMIT_REF: $VERCEL_GIT_COMMIT_REF"
|
||||
|
||||
if [[ "$VERCEL_GIT_COMMIT_REF" == "master" || "$VERCEL_GIT_COMMIT_REF" == "development" ]]; then
|
||||
echo "✅ - Build can proceed"
|
||||
exit 1
|
||||
else
|
||||
echo "🛑 - Build cancelled"
|
||||
exit 0
|
||||
fi
|
||||
@@ -0,0 +1,25 @@
|
||||
import { dirname } from "path";
|
||||
import { fileURLToPath } from "url";
|
||||
import { FlatCompat } from "@eslint/eslintrc";
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
|
||||
const compat = new FlatCompat({
|
||||
baseDirectory: __dirname,
|
||||
});
|
||||
|
||||
const eslintConfig = [
|
||||
...compat.extends("next/core-web-vitals", "next/typescript"),
|
||||
{
|
||||
ignores: [
|
||||
"node_modules/**",
|
||||
".next/**",
|
||||
"out/**",
|
||||
"build/**",
|
||||
"next-env.d.ts",
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export default eslintConfig;
|
||||
@@ -0,0 +1,8 @@
|
||||
import type { NextConfig } from 'next';
|
||||
|
||||
const nextConfig: NextConfig = {
|
||||
output: 'export',
|
||||
images: { unoptimized: true },
|
||||
};
|
||||
|
||||
export default nextConfig;
|
||||
Generated
+7015
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,48 @@
|
||||
{
|
||||
"name": "lti-web-client",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "eslint && next dev --turbopack",
|
||||
"build": "next build --turbopack",
|
||||
"start": "next start",
|
||||
"lint": "eslint",
|
||||
"prepare": "husky"
|
||||
},
|
||||
"dependencies": {
|
||||
"@tanstack/match-sorter-utils": "^8.19.4",
|
||||
"@tanstack/react-table": "^8.21.3",
|
||||
"axios": "^1.12.2",
|
||||
"clsx": "^2.1.1",
|
||||
"formik": "^2.4.6",
|
||||
"inputmask": "^5.0.9",
|
||||
"moment": "^2.30.1",
|
||||
"next": "15.5.3",
|
||||
"react": "19.1.0",
|
||||
"react-dom": "19.1.0",
|
||||
"react-hot-toast": "^2.6.0",
|
||||
"react-number-format": "^5.4.4",
|
||||
"react-select": "^5.10.2",
|
||||
"swr": "^2.3.6",
|
||||
"tailwind-merge": "^3.3.1",
|
||||
"use-debounce": "^10.0.6",
|
||||
"yup": "^1.7.0",
|
||||
"zustand": "^5.0.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/eslintrc": "^3",
|
||||
"@iconify/react": "^6.0.2",
|
||||
"@tailwindcss/postcss": "^4",
|
||||
"@types/inputmask": "^5.0.7",
|
||||
"@types/node": "^20",
|
||||
"@types/react": "^19",
|
||||
"@types/react-dom": "^19",
|
||||
"daisyui": "^5.1.12",
|
||||
"eslint": "^9",
|
||||
"eslint-config-next": "15.5.3",
|
||||
"husky": "^9.1.7",
|
||||
"prettier": "3.6.2",
|
||||
"tailwindcss": "^4",
|
||||
"typescript": "^5"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
const config = {
|
||||
plugins: ["@tailwindcss/postcss"],
|
||||
};
|
||||
|
||||
export default config;
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 316 KiB |
@@ -0,0 +1,9 @@
|
||||
const Dashboard = () => {
|
||||
return (
|
||||
<section className='w-full p-4'>
|
||||
<h1 className='text-3xl font-bold text-primary'>Dashboard</h1>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default Dashboard;
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
@@ -0,0 +1,52 @@
|
||||
@import 'tailwindcss';
|
||||
@plugin "daisyui";
|
||||
@import '../styles/daisyui.css';
|
||||
|
||||
@plugin "daisyui/theme" {
|
||||
name: "lti";
|
||||
default: false;
|
||||
prefersdark: false;
|
||||
color-scheme: "light";
|
||||
--color-base-100: oklch(98% 0.001 106.423);
|
||||
--color-base-200: oklch(97% 0.001 106.424);
|
||||
--color-base-300: oklch(92% 0.003 48.717);
|
||||
--color-base-content: oklch(22.389% 0.031 278.072);
|
||||
--color-primary: oklch(60% 0.126 221.723);
|
||||
--color-primary-content: oklch(100% 0 0);
|
||||
--color-secondary: oklch(52% 0.105 223.128);
|
||||
--color-secondary-content: oklch(100% 0 0);
|
||||
--color-accent: oklch(45% 0.085 224.283);
|
||||
--color-accent-content: oklch(100% 0 0);
|
||||
--color-neutral: oklch(39% 0.07 227.392);
|
||||
--color-neutral-content: oklch(100% 0 0);
|
||||
--color-info: oklch(58% 0.158 241.966);
|
||||
--color-info-content: oklch(100% 0 0);
|
||||
--color-success: oklch(62% 0.194 149.214);
|
||||
--color-success-content: oklch(100% 0 0);
|
||||
--color-warning: oklch(85% 0.199 91.936);
|
||||
--color-warning-content: oklch(0% 0 0);
|
||||
--color-error: oklch(57% 0.245 27.325);
|
||||
--color-error-content: oklch(100% 0 0);
|
||||
--radius-selector: 0rem;
|
||||
--radius-field: 0.25rem;
|
||||
--radius-box: 0.25rem;
|
||||
--size-selector: 0.21875rem;
|
||||
--size-field: 0.1875rem;
|
||||
--border: 1px;
|
||||
--depth: 0;
|
||||
--noise: 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
:root {
|
||||
--color-primary: #1f74bf;
|
||||
}
|
||||
|
||||
@theme {
|
||||
--font-inter: var(--font-inter);
|
||||
}
|
||||
|
||||
html {
|
||||
scrollbar-gutter: initial;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import InventoryAdjustmentForm from "@/components/pages/inventory/adjustment/form/InventoryAdjustmentForm";
|
||||
|
||||
const CreateInventoryAdjustment = () => {
|
||||
return (
|
||||
<section className="w-full p-4 flex flex-row justify-center">
|
||||
<InventoryAdjustmentForm/>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
export default CreateInventoryAdjustment;
|
||||
@@ -0,0 +1,11 @@
|
||||
import SuspenseHelper from "@/components/helper/SuspenseHelper"
|
||||
|
||||
const Layout = ({
|
||||
children
|
||||
}: Readonly<{
|
||||
children: React.ReactNode
|
||||
}>) => {
|
||||
return <SuspenseHelper>{children}</SuspenseHelper>
|
||||
}
|
||||
|
||||
export default Layout;
|
||||
@@ -0,0 +1,46 @@
|
||||
'use client';
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import InventoryAdjustmentForm from '@/components/pages/inventory/adjustment/form/InventoryAdjustmentForm';
|
||||
import type { InventoryAdjustment } from '@/types/api/inventory/adjustment';
|
||||
|
||||
const DetailInventoryAdjustment = () => {
|
||||
const router = useRouter();
|
||||
const [inventoryAdjustment, setInventoryAdjustment] = useState<InventoryAdjustment | null>(null);
|
||||
|
||||
// Ambil data dari router state
|
||||
useEffect(() => {
|
||||
console.log("Router State");
|
||||
console.log(window.history.state);
|
||||
const state = window.history.state?.usr as
|
||||
| { inventoryAdjustment?: InventoryAdjustment }
|
||||
| undefined;
|
||||
|
||||
if (state?.inventoryAdjustment) {
|
||||
// jika object dikirim via router.push(state)
|
||||
setInventoryAdjustment(state.inventoryAdjustment);
|
||||
}
|
||||
}, [router]);
|
||||
|
||||
const finalData = inventoryAdjustment;
|
||||
|
||||
console.log("Final Data");
|
||||
console.log(finalData);
|
||||
|
||||
if (!finalData) {
|
||||
return (
|
||||
<div className="w-full flex flex-row justify-center items-center p-4">
|
||||
<span className="loading loading-spinner loading-xl" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<section className="w-full p-4 flex flex-row justify-center">
|
||||
<InventoryAdjustmentForm initialValues={finalData} />
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default DetailInventoryAdjustment;
|
||||
@@ -0,0 +1,11 @@
|
||||
import InventoryAdjustmentTable from '@/components/pages/inventory/adjustment/InventoryAdjustmentTable';
|
||||
|
||||
const InventoryAdjustment = () => {
|
||||
return (
|
||||
<section className='w-full p-4'>
|
||||
<InventoryAdjustmentTable />
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default InventoryAdjustment;
|
||||
@@ -0,0 +1,11 @@
|
||||
import MovementForm from '@/components/pages/inventory/movement/form/MovementForm';
|
||||
|
||||
const AddMovement = () => {
|
||||
return (
|
||||
<div className='w-full p-4 flex flex-row justify-center'>
|
||||
<MovementForm />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AddMovement;
|
||||
@@ -0,0 +1,48 @@
|
||||
'use client';
|
||||
|
||||
import { useRouter, useSearchParams } from 'next/navigation';
|
||||
import useSWR from 'swr';
|
||||
|
||||
import MovementForm from '@/components/pages/inventory/movement/form/MovementForm';
|
||||
import { MovementApi } from '@/services/api/inventory';
|
||||
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
||||
|
||||
const MovementEdit = () => {
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
|
||||
const movementId = searchParams.get('movementId');
|
||||
|
||||
const { data: movement, isLoading: isLoadingMovement } = useSWR(
|
||||
movementId,
|
||||
(id: number) => MovementApi.getSingle(id)
|
||||
);
|
||||
|
||||
if (!movementId) {
|
||||
router.back();
|
||||
|
||||
return (
|
||||
<div className='w-full flex flex-row justify-center items-center p-4'>
|
||||
<span className='loading loading-spinner loading-xl' />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!isLoadingMovement && (!movement || isResponseError(movement))) {
|
||||
router.replace('/404');
|
||||
return;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='w-full p-4 flex flex-row justify-center'>
|
||||
{isLoadingMovement && (
|
||||
<span className='loading loading-spinner loading-xl' />
|
||||
)}
|
||||
{!isLoadingMovement && isResponseSuccess(movement) && (
|
||||
<MovementForm type='edit' initialValues={movement.data} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default MovementEdit;
|
||||
@@ -0,0 +1,11 @@
|
||||
import SuspenseHelper from '@/components/helper/SuspenseHelper';
|
||||
|
||||
const Layout = ({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>) => {
|
||||
return <SuspenseHelper>{children}</SuspenseHelper>;
|
||||
};
|
||||
|
||||
export default Layout;
|
||||
@@ -0,0 +1,48 @@
|
||||
'use client';
|
||||
|
||||
import { useRouter, useSearchParams } from 'next/navigation';
|
||||
import useSWR from 'swr';
|
||||
|
||||
import MovementForm from '@/components/pages/inventory/movement/form/MovementForm';
|
||||
import { MovementApi } from '@/services/api/inventory';
|
||||
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
||||
|
||||
const MovementDetail = () => {
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
|
||||
const movementId = searchParams.get('movementId');
|
||||
|
||||
const { data: movement, isLoading: isLoadingMovement } = useSWR(
|
||||
movementId,
|
||||
(id: number) => MovementApi.getSingle(id)
|
||||
);
|
||||
|
||||
if (!movementId) {
|
||||
router.back();
|
||||
|
||||
return (
|
||||
<div className='w-full flex flex-row justify-center items-center p-4'>
|
||||
<span className='loading loading-spinner loading-xl' />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!isLoadingMovement && (!movement || isResponseError(movement))) {
|
||||
router.replace('/404');
|
||||
return;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='w-full p-4 flex flex-row justify-center'>
|
||||
{isLoadingMovement && (
|
||||
<span className='loading loading-spinner loading-xl' />
|
||||
)}
|
||||
{!isLoadingMovement && isResponseSuccess(movement) && (
|
||||
<MovementForm type='detail' initialValues={movement.data} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default MovementDetail;
|
||||
@@ -0,0 +1,11 @@
|
||||
import MovementTable from '@/components/pages/inventory/movement/MovementTable';
|
||||
|
||||
const Movement = () => {
|
||||
return (
|
||||
<section className='w-full p-4'>
|
||||
<MovementTable />
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default Movement;
|
||||
@@ -0,0 +1,41 @@
|
||||
import type { Metadata, Viewport } from 'next';
|
||||
import { Inter } from 'next/font/google';
|
||||
import '@/app/globals.css';
|
||||
|
||||
import { Toaster } from 'react-hot-toast';
|
||||
import MainDrawer from '@/components/MainDrawer';
|
||||
import RequireAuth from '@/components/helper/RequireAuth';
|
||||
|
||||
const inter = Inter({
|
||||
variable: '--font-inter',
|
||||
subsets: ['latin'],
|
||||
});
|
||||
|
||||
export const viewport: Viewport = {
|
||||
themeColor: '#1f74bf',
|
||||
colorScheme: 'light',
|
||||
initialScale: 1,
|
||||
};
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'LTI',
|
||||
description: 'PT. Lumbung Telur Indonesia',
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
return (
|
||||
<html lang='en' data-theme='lti'>
|
||||
<body className={`${inter.variable} antialiased font-inter`}>
|
||||
<RequireAuth>
|
||||
<MainDrawer>{children}</MainDrawer>
|
||||
</RequireAuth>
|
||||
|
||||
<Toaster />
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import AreaForm from '@/components/pages/master-data/area/form/AreaForm';
|
||||
|
||||
const AddNonstock = () => {
|
||||
return (
|
||||
<div className='w-full p-4 flex flex-row justify-center'>
|
||||
<AreaForm />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AddNonstock;
|
||||
@@ -0,0 +1,47 @@
|
||||
'use client';
|
||||
|
||||
import { useRouter, useSearchParams } from 'next/navigation';
|
||||
import useSWR from 'swr';
|
||||
|
||||
import AreaForm from '@/components/pages/master-data/area/form/AreaForm';
|
||||
|
||||
import { AreaApi } from '@/services/api/master-data';
|
||||
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
||||
|
||||
const AreaEdit = () => {
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
|
||||
const areaId = searchParams.get('areaId');
|
||||
|
||||
const { data: area, isLoading: isLoadingArea } = useSWR(
|
||||
areaId,
|
||||
(id: number) => AreaApi.getSingle(id)
|
||||
);
|
||||
|
||||
if (!areaId) {
|
||||
router.back();
|
||||
|
||||
return (
|
||||
<div className='w-full flex flex-row justify-center items-center p-4'>
|
||||
<span className='loading loading-spinner loading-xl' />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!isLoadingArea && (!area || isResponseError(area))) {
|
||||
router.replace('/404');
|
||||
return;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='w-full p-4 flex flex-row justify-center'>
|
||||
{isLoadingArea && <span className='loading loading-spinner loading-xl' />}
|
||||
{!isLoadingArea && isResponseSuccess(area) && (
|
||||
<AreaForm type='edit' initialValues={area.data} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AreaEdit;
|
||||
@@ -0,0 +1,11 @@
|
||||
import SuspenseHelper from '@/components/helper/SuspenseHelper';
|
||||
|
||||
const Layout = ({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>) => {
|
||||
return <SuspenseHelper>{children}</SuspenseHelper>;
|
||||
};
|
||||
|
||||
export default Layout;
|
||||
@@ -0,0 +1,47 @@
|
||||
'use client';
|
||||
|
||||
import { useRouter, useSearchParams } from 'next/navigation';
|
||||
import useSWR from 'swr';
|
||||
|
||||
import AreaForm from '@/components/pages/master-data/area/form/AreaForm';
|
||||
|
||||
import { AreaApi } from '@/services/api/master-data';
|
||||
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
||||
|
||||
const AreaDetail = () => {
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
|
||||
const areaId = searchParams.get('areaId');
|
||||
|
||||
const { data: area, isLoading: isLoadingArea } = useSWR(
|
||||
areaId,
|
||||
(id: number) => AreaApi.getSingle(id)
|
||||
);
|
||||
|
||||
if (!areaId) {
|
||||
router.back();
|
||||
|
||||
return (
|
||||
<div className='w-full flex flex-row justify-center items-center p-4'>
|
||||
<span className='loading loading-spinner loading-xl' />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!isLoadingArea && (!area || isResponseError(area))) {
|
||||
router.replace('/404');
|
||||
return;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='w-full p-4 flex flex-row justify-center'>
|
||||
{isLoadingArea && <span className='loading loading-spinner loading-xl' />}
|
||||
{!isLoadingArea && isResponseSuccess(area) && (
|
||||
<AreaForm type='detail' initialValues={area.data} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AreaDetail;
|
||||
@@ -0,0 +1,11 @@
|
||||
import AreasTable from '@/components/pages/master-data/area/AreasTable';
|
||||
|
||||
const Nonstock = () => {
|
||||
return (
|
||||
<section className='w-full p-4'>
|
||||
<AreasTable />
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default Nonstock;
|
||||
@@ -0,0 +1,11 @@
|
||||
import BankForm from '@/components/pages/master-data/bank/form/BankForm';
|
||||
|
||||
const AddBank = () => {
|
||||
return (
|
||||
<div className='w-full p-4 flex flex-row justify-center'>
|
||||
<BankForm />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AddBank;
|
||||
@@ -0,0 +1,47 @@
|
||||
'use client';
|
||||
|
||||
import { useRouter, useSearchParams } from 'next/navigation';
|
||||
import useSWR from 'swr';
|
||||
|
||||
import BankForm from '@/components/pages/master-data/bank/form/BankForm';
|
||||
|
||||
import { BankApi } from '@/services/api/master-data';
|
||||
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
||||
|
||||
const BankEdit = () => {
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
|
||||
const bankId = searchParams.get('bankId');
|
||||
|
||||
const { data: bank, isLoading: isLoadingBank } = useSWR(
|
||||
bankId,
|
||||
(id: number) => BankApi.getSingle(id)
|
||||
);
|
||||
|
||||
if (!bankId) {
|
||||
router.back();
|
||||
|
||||
return (
|
||||
<div className='w-full flex flex-row justify-center items-center p-4'>
|
||||
<span className='loading loading-spinner loading-xl' />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!isLoadingBank && (!bank || isResponseError(bank))) {
|
||||
router.replace('/404');
|
||||
return;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='w-full p-4 flex flex-row justify-center'>
|
||||
{isLoadingBank && <span className='loading loading-spinner loading-xl' />}
|
||||
{!isLoadingBank && isResponseSuccess(bank) && (
|
||||
<BankForm type='edit' initialValues={bank.data} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default BankEdit;
|
||||
@@ -0,0 +1,11 @@
|
||||
import SuspenseHelper from '@/components/helper/SuspenseHelper';
|
||||
|
||||
const Layout = ({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>) => {
|
||||
return <SuspenseHelper>{children}</SuspenseHelper>;
|
||||
};
|
||||
|
||||
export default Layout;
|
||||
@@ -0,0 +1,47 @@
|
||||
'use client';
|
||||
|
||||
import { useRouter, useSearchParams } from 'next/navigation';
|
||||
import useSWR from 'swr';
|
||||
|
||||
import BankForm from '@/components/pages/master-data/bank/form/BankForm';
|
||||
|
||||
import { BankApi } from '@/services/api/master-data';
|
||||
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
||||
|
||||
const BankDetail = () => {
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
|
||||
const bankId = searchParams.get('bankId');
|
||||
|
||||
const { data: bank, isLoading: isLoadingBank } = useSWR(
|
||||
bankId,
|
||||
(id: number) => BankApi.getSingle(id)
|
||||
);
|
||||
|
||||
if (!bankId) {
|
||||
router.back();
|
||||
|
||||
return (
|
||||
<div className='w-full flex flex-row justify-center items-center p-4'>
|
||||
<span className='loading loading-spinner loading-xl' />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!isLoadingBank && (!bank || isResponseError(bank))) {
|
||||
router.replace('/404');
|
||||
return;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='w-full p-4 flex flex-row justify-center'>
|
||||
{isLoadingBank && <span className='loading loading-spinner loading-xl' />}
|
||||
{!isLoadingBank && isResponseSuccess(bank) && (
|
||||
<BankForm type='detail' initialValues={bank.data} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default BankDetail;
|
||||
@@ -0,0 +1,11 @@
|
||||
import BanksTable from '@/components/pages/master-data/bank/BanksTable';
|
||||
|
||||
const Bank = () => {
|
||||
return (
|
||||
<section className='w-full p-4'>
|
||||
<BanksTable />
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default Bank;
|
||||
@@ -0,0 +1,11 @@
|
||||
import CustomerForm from "@/components/pages/master-data/customer/form/CustomerForm";
|
||||
|
||||
const AddCustomer = () => {
|
||||
return (
|
||||
<section className="w-full p-4 flex flex-row justify-center">
|
||||
<CustomerForm/>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
export default AddCustomer;
|
||||
@@ -0,0 +1,47 @@
|
||||
'use client';
|
||||
|
||||
import { useRouter, useSearchParams } from 'next/navigation';
|
||||
import useSWR from 'swr';
|
||||
import { CustomerApi } from '@/services/api/master-data';
|
||||
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
||||
import CustomerForm from '@/components/pages/master-data/customer/form/CustomerForm';
|
||||
|
||||
const CustomerEdit = () => {
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
|
||||
const costumerId = searchParams.get('customerId');
|
||||
|
||||
const { data: costumer, isLoading: isLoadingCostumer } = useSWR(
|
||||
costumerId,
|
||||
(id: number) => CustomerApi.getSingle(id)
|
||||
);
|
||||
|
||||
if (!costumerId) {
|
||||
router.back();
|
||||
|
||||
return (
|
||||
<div className='w-full flex flex-row justify-center items-center p-4'>
|
||||
<span className='loading loading-spinner loading-xl' />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!isLoadingCostumer && (!costumer || isResponseError(costumer))) {
|
||||
router.replace('/404');
|
||||
return;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='w-full p-4 flex flex-row justify-center'>
|
||||
{isLoadingCostumer && (
|
||||
<span className='loading loading-spinner loading-xl' />
|
||||
)}
|
||||
{!isLoadingCostumer && isResponseSuccess(costumer) && (
|
||||
<CustomerForm formType='edit' initialValues={costumer.data} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default CustomerEdit;
|
||||
@@ -0,0 +1,11 @@
|
||||
import SuspenseHelper from '@/components/helper/SuspenseHelper';
|
||||
|
||||
const Layout = ({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>) => {
|
||||
return <SuspenseHelper>{children}</SuspenseHelper>;
|
||||
};
|
||||
|
||||
export default Layout;
|
||||
@@ -0,0 +1,45 @@
|
||||
'use client'
|
||||
|
||||
import { useRouter, useSearchParams } from "next/navigation";
|
||||
import useSWR from "swr";
|
||||
import { CustomerApi } from '@/services/api/master-data';
|
||||
import { isResponseError, isResponseSuccess } from "@/lib/api-helper";
|
||||
import CustomerForm from "@/components/pages/master-data/customer/form/CustomerForm";
|
||||
|
||||
const CustomerDetail = () => {
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
|
||||
const costumerId = searchParams.get("customerId");
|
||||
|
||||
const { data: costumer, isLoading: isLoadingCostumer } = useSWR(
|
||||
costumerId,
|
||||
(id: number) => CustomerApi.getSingle(id)
|
||||
);
|
||||
|
||||
if(!costumerId){
|
||||
router.back();
|
||||
|
||||
return (
|
||||
<div className="w-full flex flex-row justify-center items-center p-4">
|
||||
<span className="loading loading-spinner loading-xl" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if(!isLoadingCostumer && (!costumer || isResponseError(costumer))){
|
||||
router.replace("/404");
|
||||
return;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="w-full p-4 flex flex-row justify-center">
|
||||
{isLoadingCostumer && <span className="loading loading-spinner loading-xl" />}
|
||||
{!isLoadingCostumer && isResponseSuccess(costumer) && (
|
||||
<CustomerForm formType="detail" initialValues={costumer.data} />
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
};
|
||||
|
||||
export default CustomerDetail;
|
||||
@@ -0,0 +1,11 @@
|
||||
import CustomersTable from "@/components/pages/master-data/customer/CustomersTable";
|
||||
|
||||
const Customer = () => {
|
||||
return (
|
||||
<section className="w-full p-4">
|
||||
<CustomersTable />
|
||||
</section>
|
||||
)
|
||||
};
|
||||
|
||||
export default Customer;
|
||||
@@ -0,0 +1,11 @@
|
||||
import FcrForm from '@/components/pages/master-data/fcr/form/FcrForm';
|
||||
|
||||
const AddFcr = () => {
|
||||
return (
|
||||
<div className='w-full p-4 flex flex-row justify-center'>
|
||||
<FcrForm />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AddFcr;
|
||||
@@ -0,0 +1,52 @@
|
||||
'use client';
|
||||
|
||||
import { useRouter, useSearchParams } from 'next/navigation';
|
||||
import useSWR from 'swr';
|
||||
|
||||
import FcrForm from '@/components/pages/master-data/fcr/form/FcrForm';
|
||||
|
||||
import { FcrApi } from '@/services/api/master-data';
|
||||
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
||||
import { BaseApiResponse } from '@/types/api/api-general';
|
||||
import { FcrWithStandards } from '@/types/api/master-data/fcr';
|
||||
|
||||
const FcrEdit = () => {
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
|
||||
const fcrId = searchParams.get('fcrId');
|
||||
|
||||
const { data: fcr, isLoading: isLoadingFcr } = useSWR(
|
||||
fcrId,
|
||||
(id: number) =>
|
||||
FcrApi.getSingle(id) as Promise<
|
||||
BaseApiResponse<FcrWithStandards> | undefined
|
||||
>
|
||||
);
|
||||
|
||||
if (!fcrId) {
|
||||
router.back();
|
||||
|
||||
return (
|
||||
<div className='w-full flex flex-row justify-center items-center p-4'>
|
||||
<span className='loading loading-spinner loading-xl' />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!isLoadingFcr && (!fcr || isResponseError(fcr))) {
|
||||
router.replace('/404');
|
||||
return;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='w-full p-4 flex flex-row justify-center'>
|
||||
{isLoadingFcr && <span className='loading loading-spinner loading-xl' />}
|
||||
{!isLoadingFcr && isResponseSuccess(fcr) && (
|
||||
<FcrForm type='edit' initialValues={fcr.data} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default FcrEdit;
|
||||
@@ -0,0 +1,11 @@
|
||||
import SuspenseHelper from '@/components/helper/SuspenseHelper';
|
||||
|
||||
const Layout = ({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>) => {
|
||||
return <SuspenseHelper>{children}</SuspenseHelper>;
|
||||
};
|
||||
|
||||
export default Layout;
|
||||
@@ -0,0 +1,52 @@
|
||||
'use client';
|
||||
|
||||
import { useRouter, useSearchParams } from 'next/navigation';
|
||||
import useSWR from 'swr';
|
||||
|
||||
import FcrForm from '@/components/pages/master-data/fcr/form/FcrForm';
|
||||
|
||||
import { FcrApi } from '@/services/api/master-data';
|
||||
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
||||
import { FcrWithStandards } from '@/types/api/master-data/fcr';
|
||||
import { BaseApiResponse } from '@/types/api/api-general';
|
||||
|
||||
const FcrDetail = () => {
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
|
||||
const fcrId = searchParams.get('fcrId');
|
||||
|
||||
const { data: fcr, isLoading: isLoadingFcr } = useSWR(
|
||||
fcrId,
|
||||
(id: number) =>
|
||||
FcrApi.getSingle(id) as Promise<
|
||||
BaseApiResponse<FcrWithStandards> | undefined
|
||||
>
|
||||
);
|
||||
|
||||
if (!fcrId) {
|
||||
router.back();
|
||||
|
||||
return (
|
||||
<div className='w-full flex flex-row justify-center items-center p-4'>
|
||||
<span className='loading loading-spinner loading-xl' />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!isLoadingFcr && (!fcr || isResponseError(fcr))) {
|
||||
router.replace('/404');
|
||||
return;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='w-full p-4 flex flex-row justify-center'>
|
||||
{isLoadingFcr && <span className='loading loading-spinner loading-xl' />}
|
||||
{!isLoadingFcr && isResponseSuccess(fcr) && (
|
||||
<FcrForm type='detail' initialValues={fcr.data} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default FcrDetail;
|
||||
@@ -0,0 +1,11 @@
|
||||
import FcrsTable from '@/components/pages/master-data/fcr/FcrsTable';
|
||||
|
||||
const Fcr = () => {
|
||||
return (
|
||||
<section className='w-full p-4'>
|
||||
<FcrsTable />
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default Fcr;
|
||||
@@ -0,0 +1,11 @@
|
||||
import FlockForm from "@/components/pages/master-data/flock/form/FlockForm";
|
||||
|
||||
const AddFlock = () => {
|
||||
return (
|
||||
<section className="w-full p-4 flex flex-row justify-center">
|
||||
<FlockForm />
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
export default AddFlock;
|
||||
@@ -0,0 +1,49 @@
|
||||
'use client'
|
||||
|
||||
import FlockForm from "@/components/pages/master-data/flock/form/FlockForm";
|
||||
import { isResponseError, isResponseSuccess } from "@/lib/api-helper";
|
||||
import { FlockApi } from "@/services/api/master-data";
|
||||
import { useRouter, useSearchParams } from "next/navigation";
|
||||
import useSWR from "swr";
|
||||
|
||||
const FlockEdit = () => {
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
|
||||
// Get Query Params
|
||||
const flockId = searchParams.get('flockId');
|
||||
|
||||
// Fetch Data
|
||||
const { data: flock, isLoading: isLoadingFlock } = useSWR(
|
||||
flockId,
|
||||
(id: number) => FlockApi.getSingle(id)
|
||||
);
|
||||
|
||||
if (!flockId) {
|
||||
router.back();
|
||||
|
||||
return (
|
||||
<div className='w-full flex flex-row justify-center items-center p-4'>
|
||||
<span className='loading loading-spinner loading-xl' />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!isLoadingFlock && (!flock || isResponseError(flock))) {
|
||||
router.replace('/404');
|
||||
return;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='w-full p-4 flex flex-row justify-center'>
|
||||
{isLoadingFlock && (
|
||||
<span className='loading loading-spinner loading-xl' />
|
||||
)}
|
||||
{!isLoadingFlock && isResponseSuccess(flock) && (
|
||||
<FlockForm formType='edit' initialValues={flock.data} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default FlockEdit;
|
||||
@@ -0,0 +1,11 @@
|
||||
import SuspenseHelper from "@/components/helper/SuspenseHelper"
|
||||
|
||||
const Layout = ({
|
||||
children
|
||||
}: Readonly<{
|
||||
children: React.ReactNode
|
||||
}>) => {
|
||||
return <SuspenseHelper>{children}</SuspenseHelper>
|
||||
}
|
||||
|
||||
export default Layout;
|
||||
@@ -0,0 +1,46 @@
|
||||
'use client'
|
||||
|
||||
import FlockForm from "@/components/pages/master-data/flock/form/FlockForm";
|
||||
import { isResponseError, isResponseSuccess } from "@/lib/api-helper";
|
||||
import { FlockApi } from "@/services/api/master-data";
|
||||
import { useRouter, useSearchParams } from "next/navigation";
|
||||
import useSWR from "swr";
|
||||
|
||||
const FlockDetail = () => {
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
|
||||
// Get Query Params
|
||||
const flockId = searchParams.get('flockId');
|
||||
|
||||
// Fetch Data
|
||||
const { data: flock, isLoading: isLoadingFlock } = useSWR(flockId, (id: number) => FlockApi.getSingle(id));
|
||||
|
||||
if(!flockId){
|
||||
router.back();
|
||||
|
||||
return (
|
||||
<div className="w-full flex flex-row justify-center items-center p-4">
|
||||
<span className="loading loading-spinner loading-xl" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if(!isLoadingFlock && (!flock || isResponseError(flock))){
|
||||
router.replace('/404');
|
||||
return;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="w-full p-4 flex flex-row justify-center">
|
||||
{isLoadingFlock && (
|
||||
<span className="loading loading-spinner loading-xl" />
|
||||
)}
|
||||
{!isLoadingFlock && isResponseSuccess(flock) && (
|
||||
<FlockForm formType="detail" initialValues={flock.data} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default FlockDetail;
|
||||
@@ -0,0 +1,11 @@
|
||||
import FlockTable from "@/components/pages/master-data/flock/FlocksTable";
|
||||
|
||||
const Flock = () => {
|
||||
return (
|
||||
<section className="w-full p-4">
|
||||
<FlockTable/>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
export default Flock;
|
||||
@@ -0,0 +1,11 @@
|
||||
import KandangForm from '@/components/pages/master-data/kandang/form/KandangForm';
|
||||
|
||||
const AddNonstock = () => {
|
||||
return (
|
||||
<div className='w-full p-4 flex flex-row justify-center'>
|
||||
<KandangForm />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AddNonstock;
|
||||
@@ -0,0 +1,49 @@
|
||||
'use client';
|
||||
|
||||
import { useRouter, useSearchParams } from 'next/navigation';
|
||||
import useSWR from 'swr';
|
||||
|
||||
import KandangForm from '@/components/pages/master-data/kandang/form/KandangForm';
|
||||
|
||||
import { KandangApi } from '@/services/api/master-data';
|
||||
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
||||
|
||||
const KandangEdit = () => {
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
|
||||
const kandangId = searchParams.get('kandangId');
|
||||
|
||||
const { data: kandang, isLoading: isLoadingKandang } = useSWR(
|
||||
kandangId,
|
||||
(id: number) => KandangApi.getSingle(id)
|
||||
);
|
||||
|
||||
if (!kandangId) {
|
||||
router.back();
|
||||
|
||||
return (
|
||||
<div className='w-full flex flex-row justify-center items-center p-4'>
|
||||
<span className='loading loading-spinner loading-xl' />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!isLoadingKandang && (!kandang || isResponseError(kandang))) {
|
||||
router.replace('/404');
|
||||
return;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='w-full p-4 flex flex-row justify-center'>
|
||||
{isLoadingKandang && (
|
||||
<span className='loading loading-spinner loading-xl' />
|
||||
)}
|
||||
{!isLoadingKandang && isResponseSuccess(kandang) && (
|
||||
<KandangForm type='edit' initialValues={kandang.data} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default KandangEdit;
|
||||
@@ -0,0 +1,11 @@
|
||||
import SuspenseHelper from '@/components/helper/SuspenseHelper';
|
||||
|
||||
const Layout = ({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>) => {
|
||||
return <SuspenseHelper>{children}</SuspenseHelper>;
|
||||
};
|
||||
|
||||
export default Layout;
|
||||
@@ -0,0 +1,49 @@
|
||||
'use client';
|
||||
|
||||
import { useRouter, useSearchParams } from 'next/navigation';
|
||||
import useSWR from 'swr';
|
||||
|
||||
import KandangForm from '@/components/pages/master-data/kandang/form/KandangForm';
|
||||
|
||||
import { KandangApi } from '@/services/api/master-data';
|
||||
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
||||
|
||||
const KandangDetail = () => {
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
|
||||
const kandangId = searchParams.get('kandangId');
|
||||
|
||||
const { data: kandang, isLoading: isLoadingKandang } = useSWR(
|
||||
kandangId,
|
||||
(id: number) => KandangApi.getSingle(id)
|
||||
);
|
||||
|
||||
if (!kandangId) {
|
||||
router.back();
|
||||
|
||||
return (
|
||||
<div className='w-full flex flex-row justify-center items-center p-4'>
|
||||
<span className='loading loading-spinner loading-xl' />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!isLoadingKandang && (!kandang || isResponseError(kandang))) {
|
||||
router.replace('/404');
|
||||
return;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='w-full p-4 flex flex-row justify-center'>
|
||||
{isLoadingKandang && (
|
||||
<span className='loading loading-spinner loading-xl' />
|
||||
)}
|
||||
{!isLoadingKandang && isResponseSuccess(kandang) && (
|
||||
<KandangForm type='detail' initialValues={kandang.data} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default KandangDetail;
|
||||
@@ -0,0 +1,11 @@
|
||||
import KandangsTable from '@/components/pages/master-data/kandang/KandangsTable';
|
||||
|
||||
const Nonstock = () => {
|
||||
return (
|
||||
<section className='w-full p-4'>
|
||||
<KandangsTable />
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default Nonstock;
|
||||
@@ -0,0 +1,11 @@
|
||||
import LocationForm from '@/components/pages/master-data/location/form/LocationForm';
|
||||
|
||||
const AddNonstock = () => {
|
||||
return (
|
||||
<div className='w-full p-4 flex flex-row justify-center'>
|
||||
<LocationForm />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AddNonstock;
|
||||
@@ -0,0 +1,49 @@
|
||||
'use client';
|
||||
|
||||
import { useRouter, useSearchParams } from 'next/navigation';
|
||||
import useSWR from 'swr';
|
||||
|
||||
import LocationForm from '@/components/pages/master-data/location/form/LocationForm';
|
||||
|
||||
import { LocationApi } from '@/services/api/master-data';
|
||||
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
||||
|
||||
const LocationEdit = () => {
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
|
||||
const locationId = searchParams.get('locationId');
|
||||
|
||||
const { data: location, isLoading: isLoadingLocation } = useSWR(
|
||||
locationId,
|
||||
(id: number) => LocationApi.getSingle(id)
|
||||
);
|
||||
|
||||
if (!locationId) {
|
||||
router.back();
|
||||
|
||||
return (
|
||||
<div className='w-full flex flex-row justify-center items-center p-4'>
|
||||
<span className='loading loading-spinner loading-xl' />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!isLoadingLocation && (!location || isResponseError(location))) {
|
||||
router.replace('/404');
|
||||
return;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='w-full p-4 flex flex-row justify-center'>
|
||||
{isLoadingLocation && (
|
||||
<span className='loading loading-spinner loading-xl' />
|
||||
)}
|
||||
{!isLoadingLocation && isResponseSuccess(location) && (
|
||||
<LocationForm type='edit' initialValues={location.data} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default LocationEdit;
|
||||
@@ -0,0 +1,11 @@
|
||||
import SuspenseHelper from '@/components/helper/SuspenseHelper';
|
||||
|
||||
const Layout = ({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>) => {
|
||||
return <SuspenseHelper>{children}</SuspenseHelper>;
|
||||
};
|
||||
|
||||
export default Layout;
|
||||
@@ -0,0 +1,49 @@
|
||||
'use client';
|
||||
|
||||
import { useRouter, useSearchParams } from 'next/navigation';
|
||||
import useSWR from 'swr';
|
||||
|
||||
import LocationForm from '@/components/pages/master-data/location/form/LocationForm';
|
||||
|
||||
import { LocationApi } from '@/services/api/master-data';
|
||||
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
||||
|
||||
const LocationDetail = () => {
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
|
||||
const locationId = searchParams.get('locationId');
|
||||
|
||||
const { data: location, isLoading: isLoadingLocation } = useSWR(
|
||||
locationId,
|
||||
(id: number) => LocationApi.getSingle(id)
|
||||
);
|
||||
|
||||
if (!locationId) {
|
||||
router.back();
|
||||
|
||||
return (
|
||||
<div className='w-full flex flex-row justify-center items-center p-4'>
|
||||
<span className='loading loading-spinner loading-xl' />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!isLoadingLocation && (!location || isResponseError(location))) {
|
||||
router.replace('/404');
|
||||
return;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='w-full p-4 flex flex-row justify-center'>
|
||||
{isLoadingLocation && (
|
||||
<span className='loading loading-spinner loading-xl' />
|
||||
)}
|
||||
{!isLoadingLocation && isResponseSuccess(location) && (
|
||||
<LocationForm type='detail' initialValues={location.data} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default LocationDetail;
|
||||
@@ -0,0 +1,11 @@
|
||||
import LocationsTable from '@/components/pages/master-data/location/LocationsTable';
|
||||
|
||||
const Nonstock = () => {
|
||||
return (
|
||||
<section className='w-full p-4'>
|
||||
<LocationsTable />
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default Nonstock;
|
||||
@@ -0,0 +1,11 @@
|
||||
import NonstockForm from '@/components/pages/master-data/nonstock/form/NonstockForm';
|
||||
|
||||
const AddNonstock = () => {
|
||||
return (
|
||||
<div className='w-full p-4 flex flex-row justify-center'>
|
||||
<NonstockForm />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AddNonstock;
|
||||
@@ -0,0 +1,49 @@
|
||||
'use client';
|
||||
|
||||
import { useRouter, useSearchParams } from 'next/navigation';
|
||||
import useSWR from 'swr';
|
||||
|
||||
import NonstockForm from '@/components/pages/master-data/nonstock/form/NonstockForm';
|
||||
|
||||
import { NonstockApi } from '@/services/api/master-data';
|
||||
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
||||
|
||||
const NonstockEdit = () => {
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
|
||||
const nonstockId = searchParams.get('nonstockId');
|
||||
|
||||
const { data: nonstock, isLoading: isLoadingNonstock } = useSWR(
|
||||
nonstockId,
|
||||
(id: number) => NonstockApi.getSingle(id)
|
||||
);
|
||||
|
||||
if (!nonstockId) {
|
||||
router.back();
|
||||
|
||||
return (
|
||||
<div className='w-full flex flex-row justify-center items-center p-4'>
|
||||
<span className='loading loading-spinner loading-xl' />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!isLoadingNonstock && (!nonstock || isResponseError(nonstock))) {
|
||||
router.replace('/404');
|
||||
return;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='w-full p-4 flex flex-row justify-center'>
|
||||
{isLoadingNonstock && (
|
||||
<span className='loading loading-spinner loading-xl' />
|
||||
)}
|
||||
{!isLoadingNonstock && isResponseSuccess(nonstock) && (
|
||||
<NonstockForm type='edit' initialValues={nonstock.data} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default NonstockEdit;
|
||||
@@ -0,0 +1,11 @@
|
||||
import SuspenseHelper from '@/components/helper/SuspenseHelper';
|
||||
|
||||
const Layout = ({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>) => {
|
||||
return <SuspenseHelper>{children}</SuspenseHelper>;
|
||||
};
|
||||
|
||||
export default Layout;
|
||||
@@ -0,0 +1,49 @@
|
||||
'use client';
|
||||
|
||||
import { useRouter, useSearchParams } from 'next/navigation';
|
||||
import useSWR from 'swr';
|
||||
|
||||
import NonstockForm from '@/components/pages/master-data/nonstock/form/NonstockForm';
|
||||
|
||||
import { NonstockApi } from '@/services/api/master-data';
|
||||
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
||||
|
||||
const NonstockDetail = () => {
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
|
||||
const nonstockId = searchParams.get('nonstockId');
|
||||
|
||||
const { data: nonstock, isLoading: isLoadingNonstock } = useSWR(
|
||||
nonstockId,
|
||||
(id: number) => NonstockApi.getSingle(id)
|
||||
);
|
||||
|
||||
if (!nonstockId) {
|
||||
router.back();
|
||||
|
||||
return (
|
||||
<div className='w-full flex flex-row justify-center items-center p-4'>
|
||||
<span className='loading loading-spinner loading-xl' />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!isLoadingNonstock && (!nonstock || isResponseError(nonstock))) {
|
||||
router.replace('/404');
|
||||
return;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='w-full p-4 flex flex-row justify-center'>
|
||||
{isLoadingNonstock && (
|
||||
<span className='loading loading-spinner loading-xl' />
|
||||
)}
|
||||
{!isLoadingNonstock && isResponseSuccess(nonstock) && (
|
||||
<NonstockForm type='detail' initialValues={nonstock.data} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default NonstockDetail;
|
||||
@@ -0,0 +1,11 @@
|
||||
import NonstocksTable from '@/components/pages/master-data/nonstock/NonstocksTable';
|
||||
|
||||
const Nonstock = () => {
|
||||
return (
|
||||
<section className='w-full p-4'>
|
||||
<NonstocksTable />
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default Nonstock;
|
||||
@@ -0,0 +1,11 @@
|
||||
import ProductCategoryForm from "@/components/pages/master-data/product-category/form/ProductCategoryForm";
|
||||
|
||||
const AddProductCategory = () => {
|
||||
return (
|
||||
<div className="w-full p-4 flex flex-row justify-center">
|
||||
<ProductCategoryForm />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AddProductCategory;
|
||||
@@ -0,0 +1,47 @@
|
||||
'use client';
|
||||
|
||||
import { useRouter, useSearchParams } from 'next/navigation';
|
||||
import useSWR from 'swr';
|
||||
|
||||
import ProductCategoryForm from '@/components/pages/master-data/product-category/form/ProductCategoryForm';
|
||||
|
||||
import { ProductCategoryApi } from '@/services/api/master-data';
|
||||
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
||||
|
||||
const ProductCategoryEdit = () => {
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
|
||||
const productCategoryId = searchParams.get('productCategoryId');
|
||||
|
||||
const { data: productCategory, isLoading: isLoadingProductCategory } = useSWR(
|
||||
productCategoryId,
|
||||
(id: number) => ProductCategoryApi.getSingle(id)
|
||||
);
|
||||
|
||||
if (!productCategoryId) {
|
||||
router.back();
|
||||
|
||||
return (
|
||||
<div className='w-full flex flex-row justify-center items-center p-4'>
|
||||
<span className='loading loading-spinner loading-xl' />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!isLoadingProductCategory && (!productCategory || isResponseError(productCategory))) {
|
||||
router.replace('/404');
|
||||
return;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='w-full p-4 flex flex-row justify-center'>
|
||||
{isLoadingProductCategory && <span className='loading loading-spinner loading-xl' />}
|
||||
{!isLoadingProductCategory && isResponseSuccess(productCategory) && (
|
||||
<ProductCategoryForm type='edit' initialValues={productCategory.data} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default ProductCategoryEdit;
|
||||
@@ -0,0 +1,11 @@
|
||||
import SuspenseHelper from '@/components/helper/SuspenseHelper';
|
||||
|
||||
const Layout = ({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>) => {
|
||||
return <SuspenseHelper>{children}</SuspenseHelper>;
|
||||
};
|
||||
|
||||
export default Layout;
|
||||
@@ -0,0 +1,47 @@
|
||||
'use client';
|
||||
|
||||
import { useRouter, useSearchParams } from 'next/navigation';
|
||||
import useSWR from 'swr';
|
||||
|
||||
import ProductCategoryForm from '@/components/pages/master-data/product-category/form/ProductCategoryForm';
|
||||
|
||||
import { ProductCategoryApi } from '@/services/api/master-data';
|
||||
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
||||
|
||||
const ProductCategoryDetail = () => {
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
|
||||
const productCategoryId = searchParams.get('productCategoryId');
|
||||
|
||||
const { data: productCategory, isLoading: isLoadingProductCategory } = useSWR(
|
||||
productCategoryId,
|
||||
(id: number) => ProductCategoryApi.getSingle(id)
|
||||
);
|
||||
|
||||
if (!productCategoryId) {
|
||||
router.back();
|
||||
|
||||
return (
|
||||
<div className='w-full flex flex-row justify-center items-center p-4'>
|
||||
<span className='loading loading-spinner loading-xl' />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!isLoadingProductCategory && (!productCategory || isResponseError(productCategory))) {
|
||||
router.replace('/404');
|
||||
return;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='w-full p-4 flex flex-row justify-center'>
|
||||
{isLoadingProductCategory && <span className='loading loading-spinner loading-xl' />}
|
||||
{!isLoadingProductCategory && isResponseSuccess(productCategory) && (
|
||||
<ProductCategoryForm type='detail' initialValues={productCategory.data} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ProductCategoryDetail;
|
||||
@@ -0,0 +1,11 @@
|
||||
import ProductCategoryTable from "@/components/pages/master-data/product-category/ProductCategoryTable";
|
||||
|
||||
const ProductCategory = () => {
|
||||
return (
|
||||
<section className="w-full p-4">
|
||||
<ProductCategoryTable />
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default ProductCategory;
|
||||
@@ -0,0 +1,11 @@
|
||||
import ProductForm from '@/components/pages/master-data/product/form/ProductForm';
|
||||
|
||||
const AddProduct = () => {
|
||||
return (
|
||||
<div className="w-full p-4 flex flex-row justify-center">
|
||||
<ProductForm />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AddProduct;
|
||||
@@ -0,0 +1,45 @@
|
||||
'use client';
|
||||
|
||||
import { useRouter, useSearchParams } from 'next/navigation';
|
||||
import useSWR from 'swr';
|
||||
|
||||
import ProductForm from '@/components/pages/master-data/product/form/ProductForm';
|
||||
import { ProductApi } from '@/services/api/master-data';
|
||||
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
||||
|
||||
const ProductEdit = () => {
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
|
||||
const productId = searchParams.get('productId');
|
||||
|
||||
const { data: product, isLoading } = useSWR(
|
||||
productId,
|
||||
(id: number) => ProductApi.getSingle(id)
|
||||
);
|
||||
|
||||
if (!productId) {
|
||||
router.back();
|
||||
return (
|
||||
<div className='w-full flex flex-row justify-center items-center p-4'>
|
||||
<span className='loading loading-spinner loading-xl' />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!isLoading && (!product || isResponseError(product))) {
|
||||
router.replace('/404');
|
||||
return;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='w-full p-4 flex flex-row justify-center'>
|
||||
{isLoading && <span className='loading loading-spinner loading-xl' />}
|
||||
{!isLoading && isResponseSuccess(product) && (
|
||||
<ProductForm type='edit' initialValues={product.data} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ProductEdit;
|
||||
@@ -0,0 +1,11 @@
|
||||
import SuspenseHelper from '@/components/helper/SuspenseHelper';
|
||||
|
||||
const Layout = ({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>) => {
|
||||
return <SuspenseHelper>{children}</SuspenseHelper>;
|
||||
};
|
||||
|
||||
export default Layout;
|
||||
@@ -0,0 +1,45 @@
|
||||
'use client';
|
||||
|
||||
import { useRouter, useSearchParams } from 'next/navigation';
|
||||
import useSWR from 'swr';
|
||||
|
||||
import ProductForm from '@/components/pages/master-data/product/form/ProductForm';
|
||||
import { ProductApi } from '@/services/api/master-data';
|
||||
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
||||
|
||||
const ProductDetail = () => {
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
|
||||
const productId = searchParams.get('productId');
|
||||
|
||||
const { data: product, isLoading } = useSWR(
|
||||
productId,
|
||||
(id: number) => ProductApi.getSingle(id)
|
||||
);
|
||||
|
||||
if (!productId) {
|
||||
router.back();
|
||||
return (
|
||||
<div className='w-full flex flex-row justify-center items-center p-4'>
|
||||
<span className='loading loading-spinner loading-xl' />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!isLoading && (!product || isResponseError(product))) {
|
||||
router.replace('/404');
|
||||
return;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='w-full p-4 flex flex-row justify-center'>
|
||||
{isLoading && <span className='loading loading-spinner loading-xl' />}
|
||||
{!isLoading && isResponseSuccess(product) && (
|
||||
<ProductForm type='detail' initialValues={product.data} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ProductDetail;
|
||||
@@ -0,0 +1,11 @@
|
||||
import ProductsTable from "@/components/pages/master-data/product/ProductTable";
|
||||
|
||||
const Product = () => {
|
||||
return (
|
||||
<section className="w-full p-4">
|
||||
<ProductsTable />
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default Product;
|
||||
@@ -0,0 +1,11 @@
|
||||
import SupplierForm from '@/components/pages/master-data/supplier/form/SupplierForm';
|
||||
|
||||
const AddSupplier = () => {
|
||||
return (
|
||||
<section className='w-full p-4 flex flex-row justify-center'>
|
||||
<SupplierForm />
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default AddSupplier;
|
||||
@@ -0,0 +1,49 @@
|
||||
'use client';
|
||||
|
||||
import SupplierForm from '@/components/pages/master-data/supplier/form/SupplierForm';
|
||||
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
||||
import { SupplierApi } from '@/services/api/master-data';
|
||||
import { useSearchParams, useRouter } from 'next/navigation';
|
||||
import useSWR from 'swr';
|
||||
|
||||
const SupplierEdit = () => {
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
|
||||
// Get Query Params
|
||||
const supplierId = searchParams.get('supplierId');
|
||||
|
||||
// Fetch Data
|
||||
const { data: supplier, isLoading: isLoadingSupplier } = useSWR(
|
||||
supplierId,
|
||||
(id: number) => SupplierApi.getSingle(id)
|
||||
);
|
||||
|
||||
if (!supplierId) {
|
||||
router.back();
|
||||
|
||||
return (
|
||||
<div className='w-full flex flex-row justify-center items-center p-4'>
|
||||
<span className='loading loading-spinner loading-xl' />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!isLoadingSupplier && (!supplier || isResponseError(supplier))) {
|
||||
router.replace('/404');
|
||||
return;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='w-full p-4 flex flex-row justify-center'>
|
||||
{isLoadingSupplier && (
|
||||
<span className='loading loading-spinner loading-xl' />
|
||||
)}
|
||||
{!isLoadingSupplier && isResponseSuccess(supplier) && (
|
||||
<SupplierForm formType='edit' initialValues={supplier.data} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SupplierEdit;
|
||||
@@ -0,0 +1,11 @@
|
||||
import SuspenseHelper from '@/components/helper/SuspenseHelper';
|
||||
|
||||
const Layout = ({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>) => {
|
||||
return <SuspenseHelper>{children}</SuspenseHelper>;
|
||||
};
|
||||
|
||||
export default Layout;
|
||||
@@ -0,0 +1,49 @@
|
||||
'use client';
|
||||
|
||||
import SupplierForm from '@/components/pages/master-data/supplier/form/SupplierForm';
|
||||
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
||||
import { SupplierApi } from '@/services/api/master-data';
|
||||
import { useSearchParams, useRouter } from 'next/navigation';
|
||||
import useSWR from 'swr';
|
||||
|
||||
const SupplierDetail = () => {
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
|
||||
// Get Query Params
|
||||
const supplierId = searchParams.get('supplierId');
|
||||
|
||||
// Fetch Data
|
||||
const { data: supplier, isLoading: isLoadingSupplier } = useSWR(
|
||||
supplierId,
|
||||
(id: number) => SupplierApi.getSingle(id)
|
||||
);
|
||||
|
||||
if (!supplierId) {
|
||||
router.back();
|
||||
|
||||
return (
|
||||
<div className='w-full flex flex-row justify-center items-center p-4'>
|
||||
<span className='loading loading-spinner loading-xl' />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!isLoadingSupplier && (!supplier || isResponseError(supplier))) {
|
||||
router.replace('/404');
|
||||
return;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='w-full p-4 flex flex-row justify-center'>
|
||||
{isLoadingSupplier && (
|
||||
<span className='loading loading-spinner loading-xl' />
|
||||
)}
|
||||
{!isLoadingSupplier && isResponseSuccess(supplier) && (
|
||||
<SupplierForm formType='detail' initialValues={supplier.data} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SupplierDetail;
|
||||
@@ -0,0 +1,11 @@
|
||||
import SuppliersTable from "@/components/pages/master-data/supplier/SupplierTable";
|
||||
|
||||
const Supplier = () => {
|
||||
return (
|
||||
<section className='w-full p-4'>
|
||||
<SuppliersTable />
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default Supplier;
|
||||
@@ -0,0 +1,11 @@
|
||||
import UomForm from '@/components/pages/master-data/uom/form/UomForm';
|
||||
|
||||
const AddNonstock = () => {
|
||||
return (
|
||||
<div className='w-full p-4 flex flex-row justify-center'>
|
||||
<UomForm />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AddNonstock;
|
||||
@@ -0,0 +1,46 @@
|
||||
'use client';
|
||||
|
||||
import { useRouter, useSearchParams } from 'next/navigation';
|
||||
import useSWR from 'swr';
|
||||
|
||||
import UomForm from '@/components/pages/master-data/uom/form/UomForm';
|
||||
|
||||
import { UomApi } from '@/services/api/master-data';
|
||||
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
||||
|
||||
const UomEdit = () => {
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
|
||||
const uomId = searchParams.get('uomId');
|
||||
|
||||
const { data: uom, isLoading: isLoadingUom } = useSWR(uomId, (id: number) =>
|
||||
UomApi.getSingle(id)
|
||||
);
|
||||
|
||||
if (!uomId) {
|
||||
router.back();
|
||||
|
||||
return (
|
||||
<div className='w-full flex flex-row justify-center items-center p-4'>
|
||||
<span className='loading loading-spinner loading-xl' />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!isLoadingUom && (!uom || isResponseError(uom))) {
|
||||
router.replace('/404');
|
||||
return;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='w-full p-4 flex flex-row justify-center'>
|
||||
{isLoadingUom && <span className='loading loading-spinner loading-xl' />}
|
||||
{!isLoadingUom && isResponseSuccess(uom) && (
|
||||
<UomForm type='edit' initialValues={uom.data} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default UomEdit;
|
||||
@@ -0,0 +1,11 @@
|
||||
import SuspenseHelper from '@/components/helper/SuspenseHelper';
|
||||
|
||||
const Layout = ({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>) => {
|
||||
return <SuspenseHelper>{children}</SuspenseHelper>;
|
||||
};
|
||||
|
||||
export default Layout;
|
||||
@@ -0,0 +1,46 @@
|
||||
'use client';
|
||||
|
||||
import { useRouter, useSearchParams } from 'next/navigation';
|
||||
import useSWR from 'swr';
|
||||
|
||||
import UomForm from '@/components/pages/master-data/uom/form/UomForm';
|
||||
|
||||
import { UomApi } from '@/services/api/master-data';
|
||||
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
||||
|
||||
const UomDetail = () => {
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
|
||||
const uomId = searchParams.get('uomId');
|
||||
|
||||
const { data: uom, isLoading: isLoadingUom } = useSWR(uomId, (id: number) =>
|
||||
UomApi.getSingle(id)
|
||||
);
|
||||
|
||||
if (!uomId) {
|
||||
router.back();
|
||||
|
||||
return (
|
||||
<div className='w-full flex flex-row justify-center items-center p-4'>
|
||||
<span className='loading loading-spinner loading-xl' />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!isLoadingUom && (!uom || isResponseError(uom))) {
|
||||
router.replace('/404');
|
||||
return;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='w-full p-4 flex flex-row justify-center'>
|
||||
{isLoadingUom && <span className='loading loading-spinner loading-xl' />}
|
||||
{!isLoadingUom && isResponseSuccess(uom) && (
|
||||
<UomForm type='detail' initialValues={uom.data} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default UomDetail;
|
||||
@@ -0,0 +1,11 @@
|
||||
import UomsTable from '@/components/pages/master-data/uom/UomsTable';
|
||||
|
||||
const Nonstock = () => {
|
||||
return (
|
||||
<section className='w-full p-4'>
|
||||
<UomsTable />
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default Nonstock;
|
||||
@@ -0,0 +1,11 @@
|
||||
import WarehouseForm from '@/components/pages/master-data/warehouse/form/WarehouseForm';
|
||||
|
||||
const AddNonstock = () => {
|
||||
return (
|
||||
<div className='w-full p-4 flex flex-row justify-center'>
|
||||
<WarehouseForm />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AddNonstock;
|
||||
@@ -0,0 +1,49 @@
|
||||
'use client';
|
||||
|
||||
import { useRouter, useSearchParams } from 'next/navigation';
|
||||
import useSWR from 'swr';
|
||||
|
||||
import WarehouseForm from '@/components/pages/master-data/warehouse/form/WarehouseForm';
|
||||
|
||||
import { WarehouseApi } from '@/services/api/master-data';
|
||||
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
||||
|
||||
const WarehouseEdit = () => {
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
|
||||
const warehouseId = searchParams.get('warehouseId');
|
||||
|
||||
const { data: warehouse, isLoading: isLoadingWarehouse } = useSWR(
|
||||
warehouseId,
|
||||
(id: number) => WarehouseApi.getSingle(id)
|
||||
);
|
||||
|
||||
if (!warehouseId) {
|
||||
router.back();
|
||||
|
||||
return (
|
||||
<div className='w-full flex flex-row justify-center items-center p-4'>
|
||||
<span className='loading loading-spinner loading-xl' />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!isLoadingWarehouse && (!warehouse || isResponseError(warehouse))) {
|
||||
router.replace('/404');
|
||||
return;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='w-full p-4 flex flex-row justify-center'>
|
||||
{isLoadingWarehouse && (
|
||||
<span className='loading loading-spinner loading-xl' />
|
||||
)}
|
||||
{!isLoadingWarehouse && isResponseSuccess(warehouse) && (
|
||||
<WarehouseForm type='edit' initialValues={warehouse.data} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default WarehouseEdit;
|
||||
@@ -0,0 +1,11 @@
|
||||
import SuspenseHelper from '@/components/helper/SuspenseHelper';
|
||||
|
||||
const Layout = ({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>) => {
|
||||
return <SuspenseHelper>{children}</SuspenseHelper>;
|
||||
};
|
||||
|
||||
export default Layout;
|
||||
@@ -0,0 +1,49 @@
|
||||
'use client';
|
||||
|
||||
import { useRouter, useSearchParams } from 'next/navigation';
|
||||
import useSWR from 'swr';
|
||||
|
||||
import WarehouseForm from '@/components/pages/master-data/warehouse/form/WarehouseForm';
|
||||
|
||||
import { WarehouseApi } from '@/services/api/master-data';
|
||||
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
||||
|
||||
const WarehouseDetail = () => {
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
|
||||
const warehouseId = searchParams.get('warehouseId');
|
||||
|
||||
const { data: warehouse, isLoading: isLoadingWarehouse } = useSWR(
|
||||
warehouseId,
|
||||
(id: number) => WarehouseApi.getSingle(id)
|
||||
);
|
||||
|
||||
if (!warehouseId) {
|
||||
router.back();
|
||||
|
||||
return (
|
||||
<div className='w-full flex flex-row justify-center items-center p-4'>
|
||||
<span className='loading loading-spinner loading-xl' />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!isLoadingWarehouse && (!warehouse || isResponseError(warehouse))) {
|
||||
router.replace('/404');
|
||||
return;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='w-full p-4 flex flex-row justify-center'>
|
||||
{isLoadingWarehouse && (
|
||||
<span className='loading loading-spinner loading-xl' />
|
||||
)}
|
||||
{!isLoadingWarehouse && isResponseSuccess(warehouse) && (
|
||||
<WarehouseForm type='detail' initialValues={warehouse.data} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default WarehouseDetail;
|
||||
@@ -0,0 +1,11 @@
|
||||
import WarehousesTable from '@/components/pages/master-data/warehouse/WarehousesTable';
|
||||
|
||||
const Warehouse = () => {
|
||||
return (
|
||||
<section className='w-full p-4'>
|
||||
<WarehousesTable />
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default Warehouse;
|
||||
@@ -0,0 +1,11 @@
|
||||
import { redirect } from 'next/navigation';
|
||||
|
||||
export default function Home() {
|
||||
redirect('/dashboard');
|
||||
|
||||
return (
|
||||
<main className='w-full h-full min-h-screen flex flex-row justify-center items-center'>
|
||||
<h1>LTI ERP</h1>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import SuspenseHelper from "@/components/helper/SuspenseHelper"
|
||||
|
||||
const Layout = ({
|
||||
children
|
||||
}: Readonly<{
|
||||
children: React.ReactNode
|
||||
}>) => {
|
||||
return <SuspenseHelper>{children}</SuspenseHelper>
|
||||
}
|
||||
|
||||
export default Layout;
|
||||
@@ -0,0 +1,270 @@
|
||||
'use client';
|
||||
|
||||
import Button from '@/components/Button';
|
||||
import SelectInput, { OptionType } from '@/components/input/SelectInput';
|
||||
import Modal, { useModal } from '@/components/Modal';
|
||||
import ConfirmationModal from '@/components/modal/ConfirmationModal';
|
||||
import ChickinForm from '@/components/pages/production/chickin/form/ChickinForm';
|
||||
import Table from '@/components/Table';
|
||||
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
||||
import { cn } from '@/lib/helper';
|
||||
import { ProjectFlockApi } from '@/services/api/production';
|
||||
import { useTableFilter } from '@/services/hooks/useTableFilter';
|
||||
import { BaseApiResponse } from '@/types/api/api-general';
|
||||
import { Kandang } from '@/types/api/master-data/kandang';
|
||||
import { ProjectFlockKandang } from '@/types/api/production/project-flock-kandang';
|
||||
import { Icon } from '@iconify/react';
|
||||
import { useRouter, useSearchParams } from 'next/navigation';
|
||||
import { useState } from 'react';
|
||||
|
||||
import useSWR from 'swr';
|
||||
|
||||
const AddChickin = () => {
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
const projectFlockId = searchParams.get('projectFlockId');
|
||||
|
||||
// Tables Props
|
||||
const { state: tableFilterState } = useTableFilter({
|
||||
initial: { search: '' },
|
||||
paramMap: { page: 'page', pageSize: 'limit' },
|
||||
});
|
||||
|
||||
// States
|
||||
const [selectedKandang, setSelectedKandang] = useState<Kandang | undefined>(
|
||||
undefined
|
||||
);
|
||||
const [projectFlockKandang, setProjectFlockKandang] =
|
||||
useState<BaseApiResponse<ProjectFlockKandang>>();
|
||||
const [isLoadingProjectFlockKandang, setIsLoadingProjectFlockKandang] =
|
||||
useState(false);
|
||||
const [searchProjectFlock, setSearchProjectFlock] = useState('');
|
||||
|
||||
// Fetch Data
|
||||
const { data: projectFlock, isLoading: isLoadingProjectFlock } = useSWR(
|
||||
projectFlockId,
|
||||
(id: number) => ProjectFlockApi.getSingle(id)
|
||||
);
|
||||
const { data: listProjectFlock, isLoading: isLoadingListProjectFlock } =
|
||||
useSWR(
|
||||
`${ProjectFlockApi.basePath}?${new URLSearchParams({
|
||||
search: searchProjectFlock,
|
||||
}).toString()}`,
|
||||
ProjectFlockApi.getAllFetcher
|
||||
);
|
||||
|
||||
const getProjectFlockKandangUrl = `/kandangs/lookup`;
|
||||
// Mapping Options
|
||||
const options = isResponseSuccess(listProjectFlock)
|
||||
? listProjectFlock?.data.map((projectFlock) => {
|
||||
return {
|
||||
value: projectFlock.id,
|
||||
label: `${projectFlock?.flock?.name} - ${projectFlock?.category} - Periode ${projectFlock.period}`,
|
||||
};
|
||||
})
|
||||
: [];
|
||||
|
||||
const chickinModal = useModal();
|
||||
const alertModal = useModal();
|
||||
|
||||
if (!projectFlockId) {
|
||||
router.back();
|
||||
|
||||
return (
|
||||
<div className='w-full flex flex-row justify-center items-center p-4'>
|
||||
<span className='loading loading-spinner loading-xl' />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
!isLoadingProjectFlock &&
|
||||
(!projectFlock || isResponseError(projectFlock))
|
||||
) {
|
||||
router.replace('/404');
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle Function
|
||||
const handleChickinClick = async (kandang: Kandang) => {
|
||||
setIsLoadingProjectFlockKandang(true);
|
||||
setSelectedKandang(kandang);
|
||||
const ProjectFlockKandangRes = await ProjectFlockApi.customRequest<
|
||||
BaseApiResponse<ProjectFlockKandang>,
|
||||
'GET'
|
||||
>(getProjectFlockKandangUrl, {
|
||||
method: 'GET',
|
||||
params: {
|
||||
project_flock_id: projectFlockId ?? 0,
|
||||
kandang_id: kandang.id,
|
||||
},
|
||||
});
|
||||
if (isResponseSuccess(ProjectFlockKandangRes)) {
|
||||
setProjectFlockKandang(ProjectFlockKandangRes);
|
||||
setIsLoadingProjectFlockKandang(false);
|
||||
if (
|
||||
ProjectFlockKandangRes.data.available_quantity &&
|
||||
ProjectFlockKandangRes.data.available_quantity > 0
|
||||
) {
|
||||
chickinModal.openModal();
|
||||
} else {
|
||||
alertModal.openModal();
|
||||
}
|
||||
}
|
||||
};
|
||||
const handleAfterSubmit = () => {
|
||||
chickinModal.closeModal();
|
||||
router.push('/production/chickin');
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{isResponseSuccess(projectFlock) && (
|
||||
<>
|
||||
<section className='w-full p-4'>
|
||||
<header className='flex flex-col gap-4'>
|
||||
<Button
|
||||
href='/production/project-flock'
|
||||
variant='link'
|
||||
className='w-fit p-0 text-primary'
|
||||
>
|
||||
<Icon icon='uil:arrow-left' width={24} height={24} />
|
||||
Kembali
|
||||
</Button>
|
||||
|
||||
<div className='flex flex-col gap-4 w-full my-4'>
|
||||
<div className='max-w-full sm:max-w-1/2 md:max-w-3/5 lg:max-w-2/5'>
|
||||
<SelectInput
|
||||
required
|
||||
isSearchable
|
||||
label='Project Flock'
|
||||
options={options}
|
||||
isLoading={isLoadingListProjectFlock}
|
||||
value={{
|
||||
label: `${projectFlock.data?.flock?.name} - ${projectFlock.data?.category} - Periode ${projectFlock.data?.period}`,
|
||||
value: projectFlock.data?.id,
|
||||
}}
|
||||
onChange={(val) =>
|
||||
router.push(
|
||||
`/production/chickin/add?projectFlockId=${
|
||||
(val as OptionType | null)?.value
|
||||
}`
|
||||
)
|
||||
}
|
||||
onInputChange={(val) => {
|
||||
setSearchProjectFlock(val);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<Table<Kandang>
|
||||
data={projectFlock.data?.kandangs}
|
||||
columns={[
|
||||
{
|
||||
header: '#',
|
||||
cell: (props) =>
|
||||
tableFilterState.pageSize * (tableFilterState.page - 1) +
|
||||
props.row.index +
|
||||
1,
|
||||
},
|
||||
{
|
||||
accessorKey: 'name',
|
||||
header: 'Nama Kandang',
|
||||
},
|
||||
{
|
||||
header: 'Aksi',
|
||||
cell: (props) => {
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
color='success'
|
||||
variant='outline'
|
||||
onClick={() => {
|
||||
handleChickinClick(props.row.original);
|
||||
}}
|
||||
disabled={isLoadingProjectFlockKandang}
|
||||
>
|
||||
<Icon
|
||||
icon='mdi:home-import-outline'
|
||||
width={24}
|
||||
height={24}
|
||||
/>
|
||||
Chickin
|
||||
</Button>
|
||||
</>
|
||||
);
|
||||
},
|
||||
},
|
||||
]}
|
||||
page={undefined}
|
||||
className={{
|
||||
containerClassName: cn({
|
||||
'mb-20':
|
||||
isResponseSuccess(projectFlock) &&
|
||||
projectFlock.data?.kandangs?.length === 0,
|
||||
}),
|
||||
tableWrapperClassName: 'overflow-x-auto min-h-full!',
|
||||
tableClassName: 'font-inter w-full table-auto min-h-full!',
|
||||
headerRowClassName: 'border-b border-b-gray-200',
|
||||
headerColumnClassName:
|
||||
'px-6 py-3 text-xs font-semibold text-gray-500 last:flex last:flex-row last:justify-end',
|
||||
bodyRowClassName: 'border-b border-b-gray-200',
|
||||
bodyColumnClassName:
|
||||
'px-6 py-3 last:flex last:flex-row last:justify-end',
|
||||
paginationClassName: 'hidden',
|
||||
}}
|
||||
/>
|
||||
</section>
|
||||
<Modal ref={chickinModal.ref}>
|
||||
<div className='flex flex-row justify-between items-center'>
|
||||
<h1 className='text-xl font-semibold text-center mb-6'>
|
||||
Chickin Kandang - {selectedKandang?.name}
|
||||
</h1>
|
||||
<Button
|
||||
color='error'
|
||||
variant='link'
|
||||
onClick={chickinModal.closeModal}
|
||||
>
|
||||
<Icon
|
||||
className='text-black'
|
||||
icon='uil:times'
|
||||
width={24}
|
||||
height={24}
|
||||
/>
|
||||
</Button>
|
||||
</div>
|
||||
{isResponseSuccess(projectFlockKandang) &&
|
||||
!isLoadingProjectFlockKandang && (
|
||||
<ChickinForm
|
||||
initialValues={{
|
||||
project_flock_kandang: projectFlockKandang.data,
|
||||
created_user: projectFlock.data?.created_user,
|
||||
created_at: projectFlock.data?.created_at,
|
||||
updated_at: projectFlock.data?.updated_at,
|
||||
approval: projectFlock.data?.approval,
|
||||
}}
|
||||
afterSubmit={handleAfterSubmit}
|
||||
/>
|
||||
)}
|
||||
</Modal>
|
||||
<ConfirmationModal
|
||||
ref={alertModal.ref}
|
||||
type='info'
|
||||
text={`Persediaan Day Old Chick pada kandang (${selectedKandang?.name}) belum ada, mohon isi terlebih dahulu di bagian Persediaan!`}
|
||||
secondaryButton={undefined}
|
||||
primaryButton={{
|
||||
text: 'Ya',
|
||||
color: 'info',
|
||||
onClick: () => {
|
||||
alertModal.closeModal();
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default AddChickin;
|
||||
@@ -0,0 +1,11 @@
|
||||
import SuspenseHelper from "@/components/helper/SuspenseHelper"
|
||||
|
||||
const Layout = ({
|
||||
children
|
||||
}: Readonly<{
|
||||
children: React.ReactNode
|
||||
}>) => {
|
||||
return <SuspenseHelper>{children}</SuspenseHelper>
|
||||
}
|
||||
|
||||
export default Layout;
|
||||
@@ -0,0 +1,351 @@
|
||||
'use client';
|
||||
|
||||
import Button from '@/components/Button';
|
||||
import Card from '@/components/Card';
|
||||
import Modal, { useModal } from '@/components/Modal';
|
||||
import ConfirmationModal from '@/components/modal/ConfirmationModal';
|
||||
import ChickinForm from '@/components/pages/production/chickin/form/ChickinForm';
|
||||
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
||||
import { ChickinApi } from '@/services/api/production';
|
||||
import { BaseApiResponse } from '@/types/api/api-general';
|
||||
import {
|
||||
Chickin,
|
||||
ChickinApprovalPayload,
|
||||
} from '@/types/api/production/chickin';
|
||||
import { Icon } from '@iconify/react';
|
||||
import { useRouter, useSearchParams } from 'next/navigation';
|
||||
import { useState } from 'react';
|
||||
import toast from 'react-hot-toast';
|
||||
import useSWR from 'swr';
|
||||
|
||||
/**
|
||||
* TODO: Refactor code - pindahin detail ke reuseable component
|
||||
* setelah implement approval and reject
|
||||
*/
|
||||
|
||||
const DetailChickin = () => {
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
const chickinId = searchParams.get('chickinId');
|
||||
const [isApproveLoading, setIsApproveLoading] = useState(false);
|
||||
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
|
||||
|
||||
const confirmModal = useModal();
|
||||
const deleteModal = useModal();
|
||||
const chickinModal = useModal();
|
||||
const {
|
||||
data: chickin,
|
||||
isLoading,
|
||||
mutate: refreshChickin,
|
||||
} = useSWR(chickinId, (id: number) => ChickinApi.getSingle(id));
|
||||
|
||||
const [isApprovedDisabled, setIsApprovedDisabled] = useState(
|
||||
// chickin.data?.approval.step_number == 1 ? false : true
|
||||
true
|
||||
);
|
||||
const [isRejectedDisabled, setIsRejectedDisabled] = useState(
|
||||
!isApprovedDisabled
|
||||
);
|
||||
const [approvalAction, setApprovalAction] = useState<'APPROVED' | 'REJECTED'>(
|
||||
!isApprovedDisabled ? 'APPROVED' : 'REJECTED'
|
||||
);
|
||||
|
||||
if (!chickinId) {
|
||||
router.back();
|
||||
|
||||
return (
|
||||
<div className='w-full flex flex-row justify-center items-center p-4'>
|
||||
<span className='loading loading-spinner loading-xl' />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!isLoading && (!chickin || isResponseError(chickin))) {
|
||||
router.replace('/404');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isResponseSuccess(chickin)) {
|
||||
return (
|
||||
<div className='w-full flex flex-row justify-center items-center p-4'>
|
||||
<span className='loading loading-spinner loading-xl' />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const confirmationModalClickHandler = async ({
|
||||
action = 'APPROVED',
|
||||
}: {
|
||||
action: 'APPROVED' | 'REJECTED';
|
||||
}) => {
|
||||
if (chickin?.data.id === undefined) return;
|
||||
setIsApproveLoading(true);
|
||||
const approveChickinRes = await ChickinApi.customRequest<
|
||||
BaseApiResponse<Chickin>,
|
||||
ChickinApprovalPayload
|
||||
>(`/approvals`, {
|
||||
method: 'POST',
|
||||
payload: {
|
||||
action: action,
|
||||
approvable_ids: [chickin.data.id],
|
||||
},
|
||||
});
|
||||
|
||||
if (isResponseSuccess(approveChickinRes)) {
|
||||
if (refreshChickin) {
|
||||
await refreshChickin();
|
||||
}
|
||||
toast.success(approveChickinRes.message as string);
|
||||
}
|
||||
if (isResponseError(approveChickinRes)) {
|
||||
toast.error(approveChickinRes?.message as string);
|
||||
}
|
||||
confirmModal.closeModal();
|
||||
setIsApproveLoading(false);
|
||||
};
|
||||
|
||||
const confirmationModalDeleteClickHandler = async () => {
|
||||
setIsDeleteLoading(true);
|
||||
const deleteProjectFlockRes = await ChickinApi.delete(
|
||||
chickin.data?.id as number
|
||||
);
|
||||
|
||||
if (isResponseSuccess(deleteProjectFlockRes)) {
|
||||
toast.success(deleteProjectFlockRes?.message as string);
|
||||
router.push('/production/chickin');
|
||||
}
|
||||
if (isResponseError(deleteProjectFlockRes)) {
|
||||
toast.error(deleteProjectFlockRes?.message as string);
|
||||
}
|
||||
deleteModal.closeModal();
|
||||
setIsDeleteLoading(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className='w-full p-4 flex flex-col justify-center gap-4'>
|
||||
{isLoading && <span className='loading loading-spinner loading-xl' />}
|
||||
{!isLoading && isResponseSuccess(chickin) && (
|
||||
<>
|
||||
{/* <div className='w-full flex flex-col sm:flex-row gap-2'>
|
||||
<Button
|
||||
variant='outline'
|
||||
color='success'
|
||||
onClick={(() => {
|
||||
if (chickin?.data.id) {
|
||||
setApprovalAction('APPROVED');
|
||||
confirmModal.openModal();
|
||||
}
|
||||
})}
|
||||
disabled={!chickin?.data.id || isApprovedDisabled}
|
||||
className='w-full sm:w-fit'
|
||||
>
|
||||
<Icon icon='material-symbols:check' width={24} height={24} />
|
||||
Approve
|
||||
</Button>
|
||||
<Button
|
||||
variant='outline'
|
||||
color='error'
|
||||
onClick={() => {
|
||||
if (chickin?.data.id) {
|
||||
setApprovalAction('REJECTED');
|
||||
confirmModal.openModal();
|
||||
}
|
||||
}}
|
||||
disabled={!chickin?.data.id || isRejectedDisabled}
|
||||
className='w-full sm:w-fit'
|
||||
>
|
||||
<Icon icon='mdi:times' width={24} height={24} />
|
||||
Reject
|
||||
</Button>
|
||||
</div> */}
|
||||
<Card
|
||||
title='Informasi Umum'
|
||||
variant='bordered'
|
||||
className={{
|
||||
wrapper: 'w-full',
|
||||
}}
|
||||
>
|
||||
<div className='grid grid-cols-2 gap-4 mt-4'>
|
||||
<div className='flex flex-col gap-2'>
|
||||
<div className='font-semibold text-sm'>Flock</div>
|
||||
<div className='text-sm'>
|
||||
{
|
||||
chickin.data.project_flock_kandang?.project_flock.flock
|
||||
.name
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex flex-col gap-2'>
|
||||
<div className='font-semibold text-sm'>Area</div>
|
||||
<div className='text-sm'>
|
||||
{
|
||||
chickin.data.project_flock_kandang?.project_flock.area
|
||||
.name
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex flex-col gap-2'>
|
||||
<div className='font-semibold text-sm'>Kategori</div>
|
||||
<div className='text-sm'>
|
||||
{chickin.data.project_flock_kandang?.project_flock.category}
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex flex-col gap-2'>
|
||||
<div className='font-semibold text-sm'>Lokasi</div>
|
||||
<div className='text-sm'>
|
||||
{
|
||||
chickin.data.project_flock_kandang?.project_flock.location
|
||||
.name
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex flex-col gap-2'>
|
||||
<div className='font-semibold text-sm'>Periode</div>
|
||||
<div className='text-sm'>
|
||||
{chickin.data.project_flock_kandang?.project_flock.period}
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex flex-col gap-2'>
|
||||
<div className='font-semibold text-sm'>Kandang</div>
|
||||
<div className='text-sm'>
|
||||
{chickin.data.project_flock_kandang?.kandang.name}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
<Card
|
||||
title='Detail Chickin'
|
||||
variant='bordered'
|
||||
className={{
|
||||
wrapper: 'w-full',
|
||||
}}
|
||||
>
|
||||
<div className='grid grid-cols-2 gap-4 mt-4'>
|
||||
<div className='flex flex-col gap-2'>
|
||||
<div className='font-semibold text-sm'>Flock Kandang</div>
|
||||
<div className='text-sm'>
|
||||
{
|
||||
chickin.data.project_flock_kandang?.project_flock.flock
|
||||
.name
|
||||
}{' '}
|
||||
- {chickin.data.project_flock_kandang?.kandang.name}
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex flex-col gap-2'>
|
||||
<div className='font-semibold text-sm'>Tanggal Chickin</div>
|
||||
<div className='text-sm'>
|
||||
{chickin.data.chick_in_date
|
||||
? new Date(chickin.data.chick_in_date).toLocaleDateString(
|
||||
'id-ID'
|
||||
)
|
||||
: '-'}
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex flex-col gap-2'>
|
||||
<div className='font-semibold text-sm'>Jumlah (Ekor)</div>
|
||||
<div className='text-sm'>
|
||||
{chickin.data.quantity?.toLocaleString('id-ID')}
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex flex-col gap-2'>
|
||||
<div className='font-semibold text-sm'>Catatan</div>
|
||||
<div className='text-sm'>{chickin.data.note}</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
<div className='w-full flex flex-col sm:flex-row gap-2'>
|
||||
<Button
|
||||
color='error'
|
||||
onClick={() => {
|
||||
deleteModal.openModal();
|
||||
}}
|
||||
>
|
||||
<Icon icon='mdi:times' width={24} height={24} />
|
||||
Delete
|
||||
</Button>
|
||||
<Button color='warning'
|
||||
onClick={() => {
|
||||
chickinModal.openModal();
|
||||
}}
|
||||
>
|
||||
<Icon icon='mdi:pencil-outline' width={24} height={24} />
|
||||
Edit
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<ConfirmationModal
|
||||
ref={deleteModal.ref}
|
||||
type='error'
|
||||
text={`Apakah anda yakin ingin menghapus data Project Flock ini (${chickin?.data.project_flock_kandang?.project_flock.flock.name} - ${chickin?.data.project_flock_kandang?.kandang.name})?`}
|
||||
secondaryButton={{
|
||||
text: 'Tidak',
|
||||
}}
|
||||
primaryButton={{
|
||||
text: 'Ya',
|
||||
color: 'error',
|
||||
isLoading: isDeleteLoading,
|
||||
onClick: confirmationModalDeleteClickHandler,
|
||||
}}
|
||||
/>
|
||||
|
||||
<Modal ref={chickinModal.ref}>
|
||||
<div className='flex flex-row justify-between items-center'>
|
||||
<h1 className='text-xl font-semibold text-center mb-6'>
|
||||
Chickin Kandang -{' '}
|
||||
{chickin?.data?.project_flock_kandang &&
|
||||
chickin?.data?.project_flock_kandang.kandang?.name}
|
||||
</h1>
|
||||
<Button
|
||||
color='error'
|
||||
variant='link'
|
||||
onClick={chickinModal.closeModal}
|
||||
>
|
||||
<Icon
|
||||
className='text-black'
|
||||
icon='uil:times'
|
||||
width={24}
|
||||
height={24}
|
||||
/>
|
||||
</Button>
|
||||
</div>
|
||||
<ChickinForm
|
||||
initialValues={chickin?.data}
|
||||
formType='edit'
|
||||
afterSubmit={() => {
|
||||
refreshChickin();
|
||||
chickinModal.closeModal();
|
||||
}}
|
||||
/>
|
||||
</Modal>
|
||||
|
||||
<ConfirmationModal
|
||||
ref={confirmModal.ref}
|
||||
type={approvalAction == 'APPROVED' ? 'success' : 'error'}
|
||||
text={`Apakah anda yakin ingin ${
|
||||
approvalAction == 'APPROVED' ? 'approve' : 'reject'
|
||||
} chickin berikut? (${
|
||||
chickin?.data.project_flock_kandang?.project_flock.flock.name
|
||||
} - ${chickin?.data.project_flock_kandang?.kandang.name})?`}
|
||||
secondaryButton={{
|
||||
text: 'Tidak',
|
||||
}}
|
||||
primaryButton={{
|
||||
text: 'Ya',
|
||||
color: approvalAction == 'APPROVED' ? 'success' : 'error',
|
||||
isLoading: isApproveLoading,
|
||||
onClick: () => {
|
||||
confirmationModalClickHandler({
|
||||
action: approvalAction,
|
||||
});
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default DetailChickin;
|
||||
@@ -0,0 +1,10 @@
|
||||
import ChickinTable from "@/components/pages/production/chickin/ChickinTable";
|
||||
|
||||
const Chickin = () => {
|
||||
return (
|
||||
<section className="w-full p-4">
|
||||
<ChickinTable/>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
export default Chickin;
|
||||
@@ -0,0 +1,13 @@
|
||||
'use client'
|
||||
|
||||
import ProjectFlockForm from "@/components/pages/production/project-flock/form/ProjectFlockForm";
|
||||
|
||||
const AddProjectFlock = () => {
|
||||
return (
|
||||
<section className="w-full p-4 flex flex-row justify-center">
|
||||
<ProjectFlockForm formType="add"/>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
export default AddProjectFlock;
|
||||
@@ -0,0 +1,46 @@
|
||||
'use client'
|
||||
|
||||
|
||||
import ProjectFlockForm from "@/components/pages/production/project-flock/form/ProjectFlockForm";
|
||||
import { isResponseError, isResponseSuccess } from "@/lib/api-helper";
|
||||
import { ProjectFlockApi } from "@/services/api/production";
|
||||
import { useRouter, useSearchParams } from "next/navigation";
|
||||
import useSWR from "swr";
|
||||
|
||||
const ProjectFlockEdit = () => {
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
|
||||
const projectFlockId = searchParams.get("projectFlockId");
|
||||
|
||||
const { data: projectFlock, isLoading: isLoadingCostumer } = useSWR(
|
||||
projectFlockId,
|
||||
(id: number) => ProjectFlockApi.getSingle(id)
|
||||
);
|
||||
|
||||
if(!projectFlockId){
|
||||
router.back();
|
||||
|
||||
return (
|
||||
<div className="w-full flex flex-row justify-center items-center p-4">
|
||||
<span className="loading loading-spinner loading-xl" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if(!isLoadingCostumer && (!projectFlock || isResponseError(projectFlock))){
|
||||
router.replace("/404");
|
||||
return;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="w-full p-4 flex flex-row justify-center">
|
||||
{isLoadingCostumer && <span className="loading loading-spinner loading-xl" />}
|
||||
{!isLoadingCostumer && isResponseSuccess(projectFlock) && (
|
||||
<ProjectFlockForm formType="edit" initialValues={projectFlock.data} />
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default ProjectFlockEdit;
|
||||
@@ -0,0 +1,11 @@
|
||||
import SuspenseHelper from "@/components/helper/SuspenseHelper"
|
||||
|
||||
const Layout = ({
|
||||
children
|
||||
}: Readonly<{
|
||||
children: React.ReactNode
|
||||
}>) => {
|
||||
return <SuspenseHelper>{children}</SuspenseHelper>
|
||||
}
|
||||
|
||||
export default Layout;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user