tambah fungsi upload peserta sebagai hadir
This commit is contained in:
773
package-lock.json
generated
Normal file
773
package-lock.json
generated
Normal file
@@ -0,0 +1,773 @@
|
|||||||
|
{
|
||||||
|
"name": "ecert",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"node_modules/@emnapi/wasi-threads": {
|
||||||
|
"version": "1.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz",
|
||||||
|
"integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@kurkle/color": {
|
||||||
|
"version": "0.3.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.4.tgz",
|
||||||
|
"integrity": "sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/@oxc-project/types": {
|
||||||
|
"version": "0.130.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.130.0.tgz",
|
||||||
|
"integrity": "sha512-ibD2usx9JRu7f5pu2tMKMI4cpA4NgXJQoYRP4pQ7Pxmn1l6k/53qWtQWZayhYy3X4QZkt90Ot+mJEaeXouio6Q==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/Boshen"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@popperjs/core": {
|
||||||
|
"version": "2.11.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
|
||||||
|
"integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/popperjs"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@rolldown/binding-win32-x64-msvc": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-INAycaWuhlOK3wk4mRHGsdgwYWmd9cChdPdE9bwWmy6rn9VqVNYNFGhOdXrofXUxwHIncSiPNb8tNm8knDVIeQ==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": "^20.19.0 || >=22.12.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@rolldown/pluginutils": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/ansi-regex": {
|
||||||
|
"version": "5.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||||
|
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/ansi-styles": {
|
||||||
|
"version": "4.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||||
|
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"color-convert": "^2.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/bootstrap": {
|
||||||
|
"version": "5.3.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.8.tgz",
|
||||||
|
"integrity": "sha512-HP1SZDqaLDPwsNiqRqi5NcP0SSXciX2s9E+RyqJIIqGo+vJeN5AJVM98CXmW/Wux0nQ5L7jeWUdplCEf0Ee+tg==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/twbs"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/bootstrap"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@popperjs/core": "^2.11.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/chalk": {
|
||||||
|
"version": "4.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
|
||||||
|
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"ansi-styles": "^4.1.0",
|
||||||
|
"supports-color": "^7.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/chalk/chalk?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/chalk/node_modules/supports-color": {
|
||||||
|
"version": "7.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
||||||
|
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"has-flag": "^4.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/chart.js": {
|
||||||
|
"version": "4.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.5.1.tgz",
|
||||||
|
"integrity": "sha512-GIjfiT9dbmHRiYi6Nl2yFCq7kkwdkp1W/lp2J99rX0yo9tgJGn3lKQATztIjb5tVtevcBtIdICNWqlq5+E8/Pw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@kurkle/color": "^0.3.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"pnpm": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/cliui": {
|
||||||
|
"version": "8.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
|
||||||
|
"integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"string-width": "^4.2.0",
|
||||||
|
"strip-ansi": "^6.0.1",
|
||||||
|
"wrap-ansi": "^7.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/color-convert": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"color-name": "~1.1.4"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=7.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/color-name": {
|
||||||
|
"version": "1.1.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||||
|
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/concurrently": {
|
||||||
|
"version": "9.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/concurrently/-/concurrently-9.2.1.tgz",
|
||||||
|
"integrity": "sha512-fsfrO0MxV64Znoy8/l1vVIjjHa29SZyyqPgQBwhiDcaW8wJc2W3XWVOGx4M3oJBnv/zdUZIIp1gDeS98GzP8Ng==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"chalk": "4.1.2",
|
||||||
|
"rxjs": "7.8.2",
|
||||||
|
"shell-quote": "1.8.3",
|
||||||
|
"supports-color": "8.1.1",
|
||||||
|
"tree-kill": "1.2.2",
|
||||||
|
"yargs": "17.7.2"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"conc": "dist/bin/concurrently.js",
|
||||||
|
"concurrently": "dist/bin/concurrently.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/open-cli-tools/concurrently?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/detect-libc": {
|
||||||
|
"version": "2.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
|
||||||
|
"integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/emoji-regex": {
|
||||||
|
"version": "8.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||||
|
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/escalade": {
|
||||||
|
"version": "3.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
|
||||||
|
"integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/get-caller-file": {
|
||||||
|
"version": "2.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
|
||||||
|
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "ISC",
|
||||||
|
"engines": {
|
||||||
|
"node": "6.* || 8.* || >= 10.*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/has-flag": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/is-fullwidth-code-point": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/jquery": {
|
||||||
|
"version": "3.7.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz",
|
||||||
|
"integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/laravel-vite-plugin": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/laravel-vite-plugin/-/laravel-vite-plugin-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-Fzocl+X4eQ9jOi0RwdphYRGkUbPJ3ky1pTAST5Ot18cS2gw6d2vldK2eCrlKDVjtibCjCx5qptYDlA0373n7qg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"picocolors": "^1.0.0",
|
||||||
|
"tinyglobby": "^0.2.12",
|
||||||
|
"vite-plugin-full-reload": "^1.1.0"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"clean-orphaned-assets": "bin/clean.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^20.19.0 || >=22.12.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"fontaine": "^0.5.0",
|
||||||
|
"vite": "^8.0.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"fontaine": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/lightningcss": {
|
||||||
|
"version": "1.32.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz",
|
||||||
|
"integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MPL-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"detect-libc": "^2.0.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 12.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/parcel"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"lightningcss-android-arm64": "1.32.0",
|
||||||
|
"lightningcss-darwin-arm64": "1.32.0",
|
||||||
|
"lightningcss-darwin-x64": "1.32.0",
|
||||||
|
"lightningcss-freebsd-x64": "1.32.0",
|
||||||
|
"lightningcss-linux-arm-gnueabihf": "1.32.0",
|
||||||
|
"lightningcss-linux-arm64-gnu": "1.32.0",
|
||||||
|
"lightningcss-linux-arm64-musl": "1.32.0",
|
||||||
|
"lightningcss-linux-x64-gnu": "1.32.0",
|
||||||
|
"lightningcss-linux-x64-musl": "1.32.0",
|
||||||
|
"lightningcss-win32-arm64-msvc": "1.32.0",
|
||||||
|
"lightningcss-win32-x64-msvc": "1.32.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/lightningcss-win32-x64-msvc": {
|
||||||
|
"version": "1.32.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz",
|
||||||
|
"integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MPL-2.0",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 12.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/parcel"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/nanoid": {
|
||||||
|
"version": "3.3.12",
|
||||||
|
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz",
|
||||||
|
"integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==",
|
||||||
|
"dev": true,
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/ai"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"bin": {
|
||||||
|
"nanoid": "bin/nanoid.cjs"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/picocolors": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
|
"node_modules/picomatch": {
|
||||||
|
"version": "2.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz",
|
||||||
|
"integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8.6"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/jonschlinkert"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/postcss": {
|
||||||
|
"version": "8.5.14",
|
||||||
|
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.14.tgz",
|
||||||
|
"integrity": "sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg==",
|
||||||
|
"dev": true,
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/postcss/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "tidelift",
|
||||||
|
"url": "https://tidelift.com/funding/github/npm/postcss"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/ai"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
|
"dependencies": {
|
||||||
|
"nanoid": "^3.3.11",
|
||||||
|
"picocolors": "^1.1.1",
|
||||||
|
"source-map-js": "^1.2.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^10 || ^12 || >=14"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/require-directory": {
|
||||||
|
"version": "2.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
|
||||||
|
"integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/rolldown": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-X0KQHljNnEkWNqqiz9zJrGunh1B0HgOxLXvnFpCOcadzcy5qohZ3tqMEUg00vncoRovXuK3ZqCT9KnnKzoInFQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@oxc-project/types": "=0.130.0",
|
||||||
|
"@rolldown/pluginutils": "^1.0.0"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"rolldown": "bin/cli.mjs"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^20.19.0 || >=22.12.0"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@rolldown/binding-android-arm64": "1.0.1",
|
||||||
|
"@rolldown/binding-darwin-arm64": "1.0.1",
|
||||||
|
"@rolldown/binding-darwin-x64": "1.0.1",
|
||||||
|
"@rolldown/binding-freebsd-x64": "1.0.1",
|
||||||
|
"@rolldown/binding-linux-arm-gnueabihf": "1.0.1",
|
||||||
|
"@rolldown/binding-linux-arm64-gnu": "1.0.1",
|
||||||
|
"@rolldown/binding-linux-arm64-musl": "1.0.1",
|
||||||
|
"@rolldown/binding-linux-ppc64-gnu": "1.0.1",
|
||||||
|
"@rolldown/binding-linux-s390x-gnu": "1.0.1",
|
||||||
|
"@rolldown/binding-linux-x64-gnu": "1.0.1",
|
||||||
|
"@rolldown/binding-linux-x64-musl": "1.0.1",
|
||||||
|
"@rolldown/binding-openharmony-arm64": "1.0.1",
|
||||||
|
"@rolldown/binding-wasm32-wasi": "1.0.1",
|
||||||
|
"@rolldown/binding-win32-arm64-msvc": "1.0.1",
|
||||||
|
"@rolldown/binding-win32-x64-msvc": "1.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/rxjs": {
|
||||||
|
"version": "7.8.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz",
|
||||||
|
"integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/shell-quote": {
|
||||||
|
"version": "1.8.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz",
|
||||||
|
"integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/source-map-js": {
|
||||||
|
"version": "1.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
|
||||||
|
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/string-width": {
|
||||||
|
"version": "4.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
||||||
|
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"emoji-regex": "^8.0.0",
|
||||||
|
"is-fullwidth-code-point": "^3.0.0",
|
||||||
|
"strip-ansi": "^6.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/strip-ansi": {
|
||||||
|
"version": "6.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
||||||
|
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"ansi-regex": "^5.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/supports-color": {
|
||||||
|
"version": "8.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
|
||||||
|
"integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"has-flag": "^4.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/chalk/supports-color?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/tinyglobby": {
|
||||||
|
"version": "0.2.16",
|
||||||
|
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz",
|
||||||
|
"integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"fdir": "^6.5.0",
|
||||||
|
"picomatch": "^4.0.4"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/SuperchupuDev"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/tinyglobby/node_modules/fdir": {
|
||||||
|
"version": "6.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
|
||||||
|
"integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"picomatch": "^3 || ^4"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"picomatch": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/tinyglobby/node_modules/picomatch": {
|
||||||
|
"version": "4.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
|
||||||
|
"integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/jonschlinkert"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/tree-kill": {
|
||||||
|
"version": "1.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz",
|
||||||
|
"integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"bin": {
|
||||||
|
"tree-kill": "cli.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/tslib": {
|
||||||
|
"version": "2.8.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
||||||
|
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "0BSD"
|
||||||
|
},
|
||||||
|
"node_modules/vite": {
|
||||||
|
"version": "8.0.13",
|
||||||
|
"resolved": "https://registry.npmjs.org/vite/-/vite-8.0.13.tgz",
|
||||||
|
"integrity": "sha512-MFtjBYgzmSxmgA4RAfjIyXWpGe1oALnjgUTzzV7QLx/TKxCzjtMH6Fd9/eVK+5Fg1qNoz5VAwsmMs/NofrmJvw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
|
"dependencies": {
|
||||||
|
"lightningcss": "^1.32.0",
|
||||||
|
"picomatch": "^4.0.4",
|
||||||
|
"postcss": "^8.5.14",
|
||||||
|
"rolldown": "1.0.1",
|
||||||
|
"tinyglobby": "^0.2.16"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"vite": "bin/vite.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^20.19.0 || >=22.12.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/vitejs/vite?sponsor=1"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"fsevents": "~2.3.3"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/node": "^20.19.0 || >=22.12.0",
|
||||||
|
"@vitejs/devtools": "^0.1.18",
|
||||||
|
"esbuild": "^0.27.0 || ^0.28.0",
|
||||||
|
"jiti": ">=1.21.0",
|
||||||
|
"less": "^4.0.0",
|
||||||
|
"sass": "^1.70.0",
|
||||||
|
"sass-embedded": "^1.70.0",
|
||||||
|
"stylus": ">=0.54.8",
|
||||||
|
"sugarss": "^5.0.0",
|
||||||
|
"terser": "^5.16.0",
|
||||||
|
"tsx": "^4.8.1",
|
||||||
|
"yaml": "^2.4.2"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/node": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@vitejs/devtools": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"esbuild": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"jiti": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"less": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"sass": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"sass-embedded": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"stylus": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"sugarss": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"terser": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"tsx": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"yaml": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/vite-plugin-full-reload": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/vite-plugin-full-reload/-/vite-plugin-full-reload-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-kz18NW79x0IHbxRSHm0jttP4zoO9P9gXh+n6UTwlNKnviTTEpOlum6oS9SmecrTtSr+muHEn5TUuC75UovQzcA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"picocolors": "^1.0.0",
|
||||||
|
"picomatch": "^2.3.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/vite/node_modules/picomatch": {
|
||||||
|
"version": "4.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
|
||||||
|
"integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/jonschlinkert"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/wrap-ansi": {
|
||||||
|
"version": "7.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
|
||||||
|
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"ansi-styles": "^4.0.0",
|
||||||
|
"string-width": "^4.1.0",
|
||||||
|
"strip-ansi": "^6.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/y18n": {
|
||||||
|
"version": "5.0.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
|
||||||
|
"integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "ISC",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/yargs": {
|
||||||
|
"version": "17.7.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
|
||||||
|
"integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"cliui": "^8.0.1",
|
||||||
|
"escalade": "^3.1.1",
|
||||||
|
"get-caller-file": "^2.0.5",
|
||||||
|
"require-directory": "^2.1.1",
|
||||||
|
"string-width": "^4.2.3",
|
||||||
|
"y18n": "^5.0.5",
|
||||||
|
"yargs-parser": "^21.1.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/yargs-parser": {
|
||||||
|
"version": "21.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
|
||||||
|
"integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "ISC",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -112,6 +112,58 @@ class ParticipantController extends Controller
|
|||||||
return back()->with('success', 'Peserta berjaya ditambah.');
|
return back()->with('success', 'Peserta berjaya ditambah.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function edit(Program $program, ProgramParticipant $pp, Request $request): View
|
||||||
|
{
|
||||||
|
if ($pp->program_id !== $program->id) {
|
||||||
|
abort(403);
|
||||||
|
}
|
||||||
|
|
||||||
|
$pp->load('participant');
|
||||||
|
$filters = $request->only(['search', 'source', 'status', 'page']);
|
||||||
|
|
||||||
|
return view('admin.programs.participants.edit', compact('program', 'pp', 'filters'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function update(Program $program, ProgramParticipant $pp, Request $request): RedirectResponse
|
||||||
|
{
|
||||||
|
if ($pp->program_id !== $program->id) {
|
||||||
|
abort(403);
|
||||||
|
}
|
||||||
|
|
||||||
|
$request->validate([
|
||||||
|
'name' => ['required', 'string', 'max:255'],
|
||||||
|
'no_kp' => ['required', 'string', 'regex:/^\d{12}$/', 'unique:participants,no_kp,' . $pp->participant_id],
|
||||||
|
'email' => ['nullable', 'email', 'max:255'],
|
||||||
|
'phone' => ['nullable', 'string', 'max:20'],
|
||||||
|
'agency' => ['nullable', 'string', 'max:255'],
|
||||||
|
'session' => ['nullable', 'in:pagi,petang,full_day'],
|
||||||
|
]);
|
||||||
|
|
||||||
|
$pp->load('participant');
|
||||||
|
|
||||||
|
DB::transaction(function () use ($pp, $request) {
|
||||||
|
$pp->participant->update([
|
||||||
|
'name' => $request->name,
|
||||||
|
'no_kp' => preg_replace('/[^0-9]/', '', $request->no_kp),
|
||||||
|
'email' => $request->email ?: null,
|
||||||
|
'phone' => $request->phone ?: null,
|
||||||
|
'agency' => $request->agency ?: null,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$pp->update([
|
||||||
|
'pre_registered_session' => $request->session ?: null,
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
AuditLogService::log('participant.updated', $pp->participant);
|
||||||
|
|
||||||
|
$filters = array_filter($request->only(['search', 'source', 'status', 'page']));
|
||||||
|
$indexUrl = route('admin.programs.participants.index', $program)
|
||||||
|
. ($filters ? '?' . http_build_query($filters) : '');
|
||||||
|
|
||||||
|
return redirect($indexUrl)->with('success', 'Maklumat peserta berjaya dikemaskini.');
|
||||||
|
}
|
||||||
|
|
||||||
public function destroy(Program $program, ProgramParticipant $pp): RedirectResponse
|
public function destroy(Program $program, ProgramParticipant $pp): RedirectResponse
|
||||||
{
|
{
|
||||||
if ($pp->program_id !== $program->id) {
|
if ($pp->program_id !== $program->id) {
|
||||||
@@ -129,7 +181,10 @@ class ParticipantController extends Controller
|
|||||||
|
|
||||||
public function importForm(Program $program): View
|
public function importForm(Program $program): View
|
||||||
{
|
{
|
||||||
return view('admin.programs.participants.import', compact('program'));
|
$cutoff = $program->checkin_end_at ?? $program->end_date->endOfDay();
|
||||||
|
$programEnded = now()->gt($cutoff);
|
||||||
|
|
||||||
|
return view('admin.programs.participants.import', compact('program', 'programEnded'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function import(Program $program, Request $request, ParticipantImportService $importer): RedirectResponse
|
public function import(Program $program, Request $request, ParticipantImportService $importer): RedirectResponse
|
||||||
@@ -137,23 +192,41 @@ class ParticipantController extends Controller
|
|||||||
$request->validate([
|
$request->validate([
|
||||||
'csv_file' => ['required', 'file', 'mimes:csv,txt', 'max:5120'],
|
'csv_file' => ['required', 'file', 'mimes:csv,txt', 'max:5120'],
|
||||||
'session' => ['nullable', 'in:pagi,petang,full_day'],
|
'session' => ['nullable', 'in:pagi,petang,full_day'],
|
||||||
|
'mark_attendance' => ['nullable', 'boolean'],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
$cutoff = $program->checkin_end_at ?? $program->end_date->endOfDay();
|
||||||
|
$markAttendance = now()->gt($cutoff) && $request->boolean('mark_attendance');
|
||||||
|
|
||||||
$result = $importer->import(
|
$result = $importer->import(
|
||||||
$program,
|
$program,
|
||||||
$request->file('csv_file'),
|
$request->file('csv_file'),
|
||||||
$request->input('session', $program->default_staff_session)
|
$request->input('session', $program->default_staff_session),
|
||||||
|
$markAttendance
|
||||||
);
|
);
|
||||||
|
|
||||||
AuditLogService::log('participant.imported', $program, [], [
|
AuditLogService::log('participant.imported', $program, [], [
|
||||||
'success' => $result['success'],
|
'success' => $result['success'],
|
||||||
'duplicates' => $result['duplicates'],
|
'duplicates' => $result['duplicates'],
|
||||||
'failed' => $result['failed'],
|
'failed' => $result['failed'],
|
||||||
|
'mark_attendance'=> $markAttendance,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return back()->with('import_result', $result);
|
return back()->with('import_result', $result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function clearParticipants(Program $program): RedirectResponse
|
||||||
|
{
|
||||||
|
$deleted = $program->programParticipants()
|
||||||
|
->where('status', '!=', 'checked_in')
|
||||||
|
->whereDoesntHave('attendance')
|
||||||
|
->delete();
|
||||||
|
|
||||||
|
return redirect()
|
||||||
|
->route('admin.programs.participants.import.form', $program)
|
||||||
|
->with('success', "{$deleted} rekod peserta (belum hadir) telah dipadam.");
|
||||||
|
}
|
||||||
|
|
||||||
public function export(Program $program): \Symfony\Component\HttpFoundation\StreamedResponse
|
public function export(Program $program): \Symfony\Component\HttpFoundation\StreamedResponse
|
||||||
{
|
{
|
||||||
$headers = [
|
$headers = [
|
||||||
|
|||||||
@@ -2,8 +2,10 @@
|
|||||||
|
|
||||||
namespace App\Services;
|
namespace App\Services;
|
||||||
|
|
||||||
|
use App\Models\Attendance;
|
||||||
use App\Models\Participant;
|
use App\Models\Participant;
|
||||||
use App\Models\Program;
|
use App\Models\Program;
|
||||||
|
use App\Models\ProgramParticipant;
|
||||||
use Illuminate\Http\UploadedFile;
|
use Illuminate\Http\UploadedFile;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
use Illuminate\Support\Facades\Validator;
|
use Illuminate\Support\Facades\Validator;
|
||||||
@@ -11,37 +13,54 @@ use League\Csv\Reader;
|
|||||||
|
|
||||||
class ParticipantImportService
|
class ParticipantImportService
|
||||||
{
|
{
|
||||||
public function import(Program $program, UploadedFile $file, ?string $defaultSession): array
|
public function import(Program $program, UploadedFile $file, ?string $defaultSession, bool $markAttendance = false): array
|
||||||
{
|
{
|
||||||
$result = ['success' => 0, 'duplicates' => 0, 'failed' => 0, 'errors' => []];
|
$result = ['success' => 0, 'duplicates' => 0, 'failed' => 0, 'errors' => [], 'all_empty_ic' => false];
|
||||||
|
|
||||||
$csv = Reader::createFromPath($file->getRealPath(), 'r');
|
$csv = Reader::createFromPath($file->getRealPath(), 'r');
|
||||||
$csv->setHeaderOffset(0);
|
$csv->setHeaderOffset(0);
|
||||||
|
|
||||||
// Strip UTF-8 BOM if present (Excel-exported CSV)
|
|
||||||
$csv->setOutputBOM('');
|
$csv->setOutputBOM('');
|
||||||
try {
|
try {
|
||||||
$csv->addStreamFilter('convert.iconv.UTF-8/UTF-8');
|
$csv->addStreamFilter('convert.iconv.UTF-8/UTF-8');
|
||||||
} catch (\Throwable) {}
|
} catch (\Throwable) {}
|
||||||
|
|
||||||
|
// Collect all rows first to detect all_empty_ic
|
||||||
|
$rows = [];
|
||||||
foreach ($csv->getRecords() as $rowNum => $row) {
|
foreach ($csv->getRecords() as $rowNum => $row) {
|
||||||
$row = array_map('trim', $row);
|
$row = array_map('trim', $row);
|
||||||
|
|
||||||
// Normalise header keys (lowercase, strip BOM)
|
|
||||||
$row = array_combine(
|
$row = array_combine(
|
||||||
array_map(fn($k) => strtolower(preg_replace('/[\x{FEFF}\s]/u', '', $k)), array_keys($row)),
|
array_map(fn($k) => strtolower(preg_replace('/[\x{FEFF}\s]/u', '', $k)), array_keys($row)),
|
||||||
array_values($row)
|
array_values($row)
|
||||||
);
|
);
|
||||||
|
$rows[$rowNum] = $row;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($rows)) {
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If every row has an empty no_kp, offer delete instead
|
||||||
|
$noKpValues = array_map(
|
||||||
|
fn($row) => preg_replace('/[^0-9]/', '', $row['no_kp'] ?? $row['nokp'] ?? $row['ic'] ?? ''),
|
||||||
|
$rows
|
||||||
|
);
|
||||||
|
if (count(array_filter($noKpValues)) === 0) {
|
||||||
|
$result['all_empty_ic'] = true;
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
$session = $defaultSession ?? $program->default_staff_session;
|
||||||
|
|
||||||
|
foreach ($rows as $rowNum => $row) {
|
||||||
$data = [
|
$data = [
|
||||||
'name' => $row['name'] ?? $row['nama'] ?? '',
|
'name' => $row['name'] ?? $row['nama'] ?? '',
|
||||||
'no_kp' => preg_replace('/[^0-9]/', '', $row['no_kp'] ?? $row['nokp'] ?? $row['ic'] ?? ''),
|
'no_kp' => preg_replace('/[^0-9]/', '', $row['no_kp'] ?? $row['nokp'] ?? $row['ic'] ?? ''),
|
||||||
'email' => $row['email'] ?? $row['emel'] ?? null,
|
'email' => $row['email'] ?? $row['emel'] ?? null,
|
||||||
'phone' => $row['phone'] ?? $row['telefon'] ?? $row['phone'] ?? null,
|
'phone' => $row['phone'] ?? $row['telefon'] ?? null,
|
||||||
'agency' => $row['agency'] ?? $row['agensi'] ?? $row['jabatan'] ?? null,
|
'agency' => $row['agency'] ?? $row['agensi'] ?? $row['jabatan'] ?? null,
|
||||||
];
|
];
|
||||||
|
|
||||||
// Validate row
|
|
||||||
$validator = Validator::make($data, [
|
$validator = Validator::make($data, [
|
||||||
'name' => ['required', 'string', 'max:255'],
|
'name' => ['required', 'string', 'max:255'],
|
||||||
'no_kp' => ['required', 'digits:12'],
|
'no_kp' => ['required', 'digits:12'],
|
||||||
@@ -56,8 +75,7 @@ class ParticipantImportService
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
DB::transaction(function () use ($program, $data, $defaultSession, &$result) {
|
DB::transaction(function () use ($program, $data, $session, $markAttendance, &$result) {
|
||||||
// Find or create participant by no_kp
|
|
||||||
$participant = Participant::firstOrCreate(
|
$participant = Participant::firstOrCreate(
|
||||||
['no_kp' => $data['no_kp']],
|
['no_kp' => $data['no_kp']],
|
||||||
[
|
[
|
||||||
@@ -69,25 +87,36 @@ class ParticipantImportService
|
|||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
// Check duplicate in this program
|
$pp = $program->programParticipants()
|
||||||
$exists = $program->programParticipants()
|
|
||||||
->where('participant_id', $participant->id)
|
->where('participant_id', $participant->id)
|
||||||
->exists();
|
->first();
|
||||||
|
|
||||||
if ($exists) {
|
if ($pp) {
|
||||||
|
// Participant already registered
|
||||||
|
if ($markAttendance) {
|
||||||
|
$this->recordAttendance($program, $participant, $pp, $session);
|
||||||
$result['duplicates']++;
|
$result['duplicates']++;
|
||||||
|
} else {
|
||||||
|
$result['duplicates']++;
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$program->programParticipants()->create([
|
$newStatus = $markAttendance ? 'checked_in' : 'registered';
|
||||||
|
|
||||||
|
$pp = $program->programParticipants()->create([
|
||||||
'participant_id' => $participant->id,
|
'participant_id' => $participant->id,
|
||||||
'registration_source' => 'import',
|
'registration_source' => 'import',
|
||||||
'is_pre_registered' => true,
|
'is_pre_registered' => true,
|
||||||
'pre_registered_session' => $defaultSession,
|
'pre_registered_session' => $session,
|
||||||
'status' => 'registered',
|
'status' => $newStatus,
|
||||||
'registered_at' => now(),
|
'registered_at' => now(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
if ($markAttendance) {
|
||||||
|
$this->recordAttendance($program, $participant, $pp, $session);
|
||||||
|
}
|
||||||
|
|
||||||
$result['success']++;
|
$result['success']++;
|
||||||
});
|
});
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
@@ -98,4 +127,25 @@ class ParticipantImportService
|
|||||||
|
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function recordAttendance(Program $program, Participant $participant, ProgramParticipant $pp, ?string $session): void
|
||||||
|
{
|
||||||
|
$alreadyAttended = Attendance::where('program_id', $program->id)
|
||||||
|
->where('participant_id', $participant->id)
|
||||||
|
->exists();
|
||||||
|
if ($alreadyAttended) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$pp->update(['status' => 'checked_in']);
|
||||||
|
|
||||||
|
Attendance::create([
|
||||||
|
'program_id' => $program->id,
|
||||||
|
'participant_id' => $participant->id,
|
||||||
|
'program_participant_id' => $pp->id,
|
||||||
|
'attendance_source' => 'import',
|
||||||
|
'attendance_session' => $session ?? 'full_day',
|
||||||
|
'checked_in_at' => now(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
DB::statement("ALTER TABLE attendances MODIFY attendance_source ENUM('pre_registered_staff','walk_in_external','admin_manual','import') NOT NULL");
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
DB::statement("ALTER TABLE attendances MODIFY attendance_source ENUM('pre_registered_staff','walk_in_external','admin_manual') NOT NULL");
|
||||||
|
}
|
||||||
|
};
|
||||||
29
src/package-lock.json
generated
29
src/package-lock.json
generated
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name": "ecert",
|
"name": "src",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
@@ -16,6 +16,29 @@
|
|||||||
"vite": "^8.0.0"
|
"vite": "^8.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@emnapi/core": {
|
||||||
|
"version": "1.10.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz",
|
||||||
|
"integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@emnapi/wasi-threads": "1.2.1",
|
||||||
|
"tslib": "^2.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@emnapi/runtime": {
|
||||||
|
"version": "1.10.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz",
|
||||||
|
"integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@emnapi/wasi-threads": {
|
"node_modules/@emnapi/wasi-threads": {
|
||||||
"version": "1.2.1",
|
"version": "1.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz",
|
||||||
@@ -67,7 +90,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
|
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
|
||||||
"integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
|
"integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "opencollective",
|
"type": "opencollective",
|
||||||
"url": "https://opencollective.com/popperjs"
|
"url": "https://opencollective.com/popperjs"
|
||||||
@@ -920,7 +942,6 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"nanoid": "^3.3.11",
|
"nanoid": "^3.3.11",
|
||||||
"picocolors": "^1.1.1",
|
"picocolors": "^1.1.1",
|
||||||
@@ -1092,7 +1113,6 @@
|
|||||||
"integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
|
"integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
},
|
},
|
||||||
@@ -1123,7 +1143,6 @@
|
|||||||
"integrity": "sha512-MFtjBYgzmSxmgA4RAfjIyXWpGe1oALnjgUTzzV7QLx/TKxCzjtMH6Fd9/eVK+5Fg1qNoz5VAwsmMs/NofrmJvw==",
|
"integrity": "sha512-MFtjBYgzmSxmgA4RAfjIyXWpGe1oALnjgUTzzV7QLx/TKxCzjtMH6Fd9/eVK+5Fg1qNoz5VAwsmMs/NofrmJvw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"lightningcss": "^1.32.0",
|
"lightningcss": "^1.32.0",
|
||||||
"picomatch": "^4.0.4",
|
"picomatch": "^4.0.4",
|
||||||
|
|||||||
@@ -0,0 +1,93 @@
|
|||||||
|
@extends('layouts.admin')
|
||||||
|
|
||||||
|
@section('title', 'Edit Peserta — ' . $pp->participant->name)
|
||||||
|
@section('header', 'Edit Maklumat Peserta')
|
||||||
|
|
||||||
|
@section('breadcrumb')
|
||||||
|
<li class="breadcrumb-item"><a href="{{ route('admin.programs.index') }}">Program</a></li>
|
||||||
|
<li class="breadcrumb-item"><a href="{{ route('admin.programs.show', $program) }}">{{ Str::limit($program->title, 25) }}</a></li>
|
||||||
|
<li class="breadcrumb-item"><a href="{{ route('admin.programs.participants.index', $program) }}">Peserta</a></li>
|
||||||
|
<li class="breadcrumb-item active">Edit</li>
|
||||||
|
@endsection
|
||||||
|
|
||||||
|
@section('content')
|
||||||
|
<div class="row justify-content-center">
|
||||||
|
<div class="col-lg-6">
|
||||||
|
<div class="card border-0 shadow-sm">
|
||||||
|
<div class="card-header bg-white border-bottom py-3">
|
||||||
|
<span class="fw-semibold">
|
||||||
|
<i class="bi bi-person-gear me-2 text-primary"></i>Kemaskini Maklumat Peserta
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<form method="POST" action="{{ route('admin.programs.participants.update', [$program, $pp]) }}">
|
||||||
|
@csrf @method('PUT')
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label fw-medium">Nama Penuh <span class="text-danger">*</span></label>
|
||||||
|
<input type="text" name="name" class="form-control @error('name') is-invalid @enderror"
|
||||||
|
value="{{ old('name', $pp->participant->name) }}" required>
|
||||||
|
@error('name')<div class="invalid-feedback">{{ $message }}</div>@enderror
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label fw-medium">No. Kad Pengenalan <span class="text-danger">*</span></label>
|
||||||
|
<input type="text" name="no_kp" class="form-control @error('no_kp') is-invalid @enderror"
|
||||||
|
value="{{ old('no_kp', $pp->participant->no_kp) }}"
|
||||||
|
placeholder="12 digit tanpa sempang" required>
|
||||||
|
@error('no_kp')<div class="invalid-feedback">{{ $message }}</div>@enderror
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label fw-medium">Emel</label>
|
||||||
|
<input type="email" name="email" class="form-control @error('email') is-invalid @enderror"
|
||||||
|
value="{{ old('email', $pp->participant->email) }}"
|
||||||
|
placeholder="Kosongkan jika tiada">
|
||||||
|
@error('email')<div class="invalid-feedback">{{ $message }}</div>@enderror
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label fw-medium">No. Telefon</label>
|
||||||
|
<input type="text" name="phone" class="form-control @error('phone') is-invalid @enderror"
|
||||||
|
value="{{ old('phone', $pp->participant->phone) }}"
|
||||||
|
placeholder="Kosongkan jika tiada">
|
||||||
|
@error('phone')<div class="invalid-feedback">{{ $message }}</div>@enderror
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label fw-medium">Jabatan / Agensi</label>
|
||||||
|
<input type="text" name="agency" class="form-control @error('agency') is-invalid @enderror"
|
||||||
|
value="{{ old('agency', $pp->participant->agency) }}"
|
||||||
|
placeholder="Kosongkan jika tiada">
|
||||||
|
@error('agency')<div class="invalid-feedback">{{ $message }}</div>@enderror
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-4">
|
||||||
|
<label class="form-label fw-medium">Sesi</label>
|
||||||
|
<select name="session" class="form-select @error('session') is-invalid @enderror">
|
||||||
|
<option value="">— Tiada Sesi —</option>
|
||||||
|
<option value="pagi" {{ old('session', $pp->pre_registered_session) === 'pagi' ? 'selected' : '' }}>Pagi</option>
|
||||||
|
<option value="petang" {{ old('session', $pp->pre_registered_session) === 'petang' ? 'selected' : '' }}>Petang</option>
|
||||||
|
<option value="full_day" {{ old('session', $pp->pre_registered_session) === 'full_day' ? 'selected' : '' }}>Sehari Penuh</option>
|
||||||
|
</select>
|
||||||
|
@error('session')<div class="invalid-feedback">{{ $message }}</div>@enderror
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@foreach($filters as $key => $value)
|
||||||
|
<input type="hidden" name="{{ $key }}" value="{{ $value }}">
|
||||||
|
@endforeach
|
||||||
|
|
||||||
|
@php $backUrl = route('admin.programs.participants.index', $program) . ($filters ? '?' . http_build_query($filters) : ''); @endphp
|
||||||
|
|
||||||
|
<div class="d-flex gap-2">
|
||||||
|
<button type="submit" class="btn btn-primary">
|
||||||
|
<i class="bi bi-check-lg me-1"></i> Simpan
|
||||||
|
</button>
|
||||||
|
<a href="{{ $backUrl }}" class="btn btn-outline-secondary">Batal</a>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endsection
|
||||||
@@ -17,6 +17,30 @@
|
|||||||
{{-- Import Result --}}
|
{{-- Import Result --}}
|
||||||
@if(session('import_result'))
|
@if(session('import_result'))
|
||||||
@php $r = session('import_result'); @endphp
|
@php $r = session('import_result'); @endphp
|
||||||
|
|
||||||
|
{{-- All IC empty — offer delete --}}
|
||||||
|
@if(!empty($r['all_empty_ic']))
|
||||||
|
<div class="card border-0 shadow-sm mb-4 border-start border-4 border-warning">
|
||||||
|
<div class="card-body">
|
||||||
|
<h6 class="fw-semibold mb-2 text-warning">
|
||||||
|
<i class="bi bi-exclamation-triangle me-2"></i>Semua No. K/P Kosong
|
||||||
|
</h6>
|
||||||
|
<p class="small text-muted mb-3">
|
||||||
|
Fail CSV yang dimuat naik tidak mengandungi sebarang No. K/P yang sah.
|
||||||
|
Tiada rekod diimport. Adakah anda ingin <strong>memadam semua peserta belum hadir</strong> dalam program ini?
|
||||||
|
</p>
|
||||||
|
<form method="POST" action="{{ route('admin.programs.participants.clear', $program) }}">
|
||||||
|
@csrf @method('DELETE')
|
||||||
|
<button type="submit" class="btn btn-danger btn-sm"
|
||||||
|
onclick="return confirm('Padam semua peserta yang belum hadir? Tindakan ini tidak boleh dibatalkan.')">
|
||||||
|
<i class="bi bi-trash me-1"></i> Padam Peserta Belum Hadir
|
||||||
|
</button>
|
||||||
|
<a href="{{ route('admin.programs.participants.import.form', $program) }}"
|
||||||
|
class="btn btn-outline-secondary btn-sm ms-2">Batal</a>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@else
|
||||||
<div class="card border-0 shadow-sm mb-4 border-start border-4
|
<div class="card border-0 shadow-sm mb-4 border-start border-4
|
||||||
{{ $r['failed'] > 0 ? 'border-warning' : 'border-success' }}">
|
{{ $r['failed'] > 0 ? 'border-warning' : 'border-success' }}">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
@@ -59,6 +83,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@endif
|
@endif
|
||||||
|
@endif
|
||||||
|
|
||||||
{{-- Upload Form --}}
|
{{-- Upload Form --}}
|
||||||
<div class="card border-0 shadow-sm">
|
<div class="card border-0 shadow-sm">
|
||||||
@@ -79,7 +104,7 @@
|
|||||||
@error('csv_file')<div class="invalid-feedback">{{ $message }}</div>@enderror
|
@error('csv_file')<div class="invalid-feedback">{{ $message }}</div>@enderror
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mb-4">
|
<div class="mb-3">
|
||||||
<label class="form-label fw-medium">Sesi Default</label>
|
<label class="form-label fw-medium">Sesi Default</label>
|
||||||
<select name="session" class="form-select">
|
<select name="session" class="form-select">
|
||||||
<option value="">— Ikut Tetapan Program —</option>
|
<option value="">— Ikut Tetapan Program —</option>
|
||||||
@@ -90,6 +115,24 @@
|
|||||||
<div class="form-text">Sesi yang akan digunakan untuk semua peserta dalam fail ini.</div>
|
<div class="form-text">Sesi yang akan digunakan untuk semua peserta dalam fail ini.</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@if($programEnded)
|
||||||
|
<div class="mb-4 p-3 bg-warning bg-opacity-10 rounded border border-warning-subtle">
|
||||||
|
<div class="form-check">
|
||||||
|
<input class="form-check-input" type="checkbox" name="mark_attendance"
|
||||||
|
value="1" id="markAttendance">
|
||||||
|
<label class="form-check-label fw-medium" for="markAttendance">
|
||||||
|
Tandakan sebagai Data Kehadiran
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="small text-muted mt-1 ms-4">
|
||||||
|
<i class="bi bi-info-circle me-1"></i>
|
||||||
|
Tempoh check-in telah tamat pada <strong>{{ ($program->checkin_end_at ?? $program->end_date->endOfDay())->format('d M Y, H:i') }}</strong>.
|
||||||
|
Jika ditanda, semua peserta dalam fail ini akan direkodkan sebagai <strong>hadir</strong>.
|
||||||
|
Peserta sedia ada dalam program akan dikemaskini status kehadirannya.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
<button type="submit" class="btn btn-primary w-100">
|
<button type="submit" class="btn btn-primary w-100">
|
||||||
<i class="bi bi-upload me-2"></i>Mula Import
|
<i class="bi bi-upload me-2"></i>Mula Import
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -201,6 +201,10 @@
|
|||||||
<i class="bi bi-download"></i>
|
<i class="bi bi-download"></i>
|
||||||
</a>
|
</a>
|
||||||
@endif
|
@endif
|
||||||
|
<a href="{{ route('admin.programs.participants.edit', [$program, $pp]) . (request()->hasAny(['search','source','status','page']) ? '?' . http_build_query(request()->only(['search','source','status','page'])) : '') }}"
|
||||||
|
class="btn btn-sm btn-outline-secondary" title="Edit Peserta">
|
||||||
|
<i class="bi bi-pencil"></i>
|
||||||
|
</a>
|
||||||
@if($pp->status !== 'checked_in')
|
@if($pp->status !== 'checked_in')
|
||||||
<form method="POST"
|
<form method="POST"
|
||||||
action="{{ route('admin.programs.participants.destroy', [$program, $pp]) }}"
|
action="{{ route('admin.programs.participants.destroy', [$program, $pp]) }}"
|
||||||
|
|||||||
@@ -53,9 +53,12 @@ Route::middleware(['auth', 'admin'])->prefix('admin')->name('admin.')->group(fun
|
|||||||
Route::get('/', [ParticipantController::class, 'index'])->name('index');
|
Route::get('/', [ParticipantController::class, 'index'])->name('index');
|
||||||
Route::get('/create', [ParticipantController::class, 'create'])->name('create');
|
Route::get('/create', [ParticipantController::class, 'create'])->name('create');
|
||||||
Route::post('/', [ParticipantController::class, 'store'])->name('store');
|
Route::post('/', [ParticipantController::class, 'store'])->name('store');
|
||||||
|
Route::get('/{pp}/edit', [ParticipantController::class, 'edit'])->name('edit');
|
||||||
|
Route::put('/{pp}', [ParticipantController::class, 'update'])->name('update');
|
||||||
Route::delete('/{pp}', [ParticipantController::class, 'destroy'])->name('destroy');
|
Route::delete('/{pp}', [ParticipantController::class, 'destroy'])->name('destroy');
|
||||||
Route::get('/import', [ParticipantController::class, 'importForm'])->name('import.form');
|
Route::get('/import', [ParticipantController::class, 'importForm'])->name('import.form');
|
||||||
Route::post('/import', [ParticipantController::class, 'import'])->name('import');
|
Route::post('/import', [ParticipantController::class, 'import'])->name('import');
|
||||||
|
Route::delete('/clear', [ParticipantController::class, 'clearParticipants'])->name('clear');
|
||||||
Route::get('/export', [ParticipantController::class, 'export'])->name('export');
|
Route::get('/export', [ParticipantController::class, 'export'])->name('export');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user