From b3119623c94fc68024852be278494739432edbab Mon Sep 17 00:00:00 2001 From: fahrezy27 <158013346+fahrezy27@users.noreply.github.com> Date: Wed, 24 Jun 2026 18:14:20 +0700 Subject: [PATCH] first commit --- .editorconfig | 18 + .env.example | 59 + .gitattributes | 11 + .gitignore | 19 + README.md | 66 + app/Console/Kernel.php | 27 + app/Exceptions/Handler.php | 30 + .../Auth/ForgotPasswordController.php | 30 + .../Auth/ResetPasswordController.php | 52 + app/Http/Controllers/Controller.php | 12 + app/Http/Controllers/DashboardController.php | 194 + app/Http/Controllers/LaporanController.php | 80 + app/Http/Controllers/MakananController.php | 87 + app/Http/Controllers/PanduanController.php | 11 + app/Http/Controllers/PasienController.php | 98 + app/Http/Controllers/ProsesSawController.php | 429 + .../Controllers/UserDashboardController.php | 11 + .../Controllers/UserLaporanController.php | 45 + .../Controllers/UserPanduanController.php | 11 + .../Controllers/UserProsesSawController.php | 445 + app/Http/Kernel.php | 68 + app/Http/Middleware/Authenticate.php | 17 + app/Http/Middleware/EncryptCookies.php | 17 + .../PreventRequestsDuringMaintenance.php | 17 + .../Middleware/RedirectIfAuthenticated.php | 30 + app/Http/Middleware/TrimStrings.php | 19 + app/Http/Middleware/TrustHosts.php | 20 + app/Http/Middleware/TrustProxies.php | 28 + app/Http/Middleware/ValidateSignature.php | 22 + app/Http/Middleware/VerifyCsrfToken.php | 17 + app/Models/Laporan.php | 33 + app/Models/Makanan.php | 27 + app/Models/Pasien.php | 25 + app/Models/RekomendasiUser.php | 28 + app/Models/User.php | 27 + app/Providers/AppServiceProvider.php | 26 + app/Providers/AuthServiceProvider.php | 26 + app/Providers/BroadcastServiceProvider.php | 19 + app/Providers/EventServiceProvider.php | 38 + app/Providers/RouteServiceProvider.php | 40 + artisan | 53 + bootstrap/app.php | 55 + bootstrap/cache/.gitignore | 2 + composer.json | 66 + composer.lock | 8214 +++++++++++++++++ config/app.php | 188 + config/auth.php | 115 + config/broadcasting.php | 71 + config/cache.php | 111 + config/cors.php | 34 + config/database.php | 151 + config/filesystems.php | 76 + config/hashing.php | 54 + config/logging.php | 131 + config/mail.php | 134 + config/queue.php | 109 + config/sanctum.php | 83 + config/services.php | 34 + config/session.php | 214 + config/view.php | 36 + database/.gitignore | 1 + database/factories/UserFactory.php | 44 + .../2014_10_12_000000_create_users_table.php | 30 + ...000_create_password_reset_tokens_table.php | 28 + ..._08_19_000000_create_failed_jobs_table.php | 32 + ...01_create_personal_access_tokens_table.php | 33 + .../2026_03_03_062357_create_pasien_table.php | 33 + ...2026_03_03_065726_create_makanan_table.php | 38 + ...2026_03_05_153520_create_laporan_table.php | 26 + ...4_21_011631_add_rasa_to_makanans_table.php | 27 + ...14640_add_preferensi_to_laporans_table.php | 25 + ...035125_add_porsi_saji_to_makanan_table.php | 26 + ...9_043731_add_tekstur_to_makanans_table.php | 25 + ...05_04_124728_add_tb_bb_to_pasien_table.php | 31 + ...add_komposisi_makanan_to_makanan_table.php | 25 + ...p_preferensi_makanan_from_pasien_table.php | 26 + ...0_add_snapshot_pasien_to_laporan_table.php | 35 + ...5_20_150016_add_porsi_to_laporan_table.php | 31 + ...6_add_frekuensi_makan_to_laporan_table.php | 28 + ..._133337_create_rekomendasi_users_table.php | 47 + ...6_06_13_143401_add_role_to_users_table.php | 28 + database/seeders/DatabaseSeeder.php | 22 + package.json | 13 + phpunit.xml | 32 + public/.htaccess | 21 + public/favicon.ico | 0 public/index.php | 55 + public/robots.txt | 2 + resources/css/app.css | 0 resources/js/app.js | 1 + resources/js/bootstrap.js | 32 + .../views/auth/forgot-password.blade.php | 70 + resources/views/auth/login.blade.php | 377 + resources/views/auth/register.blade.php | 403 + resources/views/auth/reset-password.blade.php | 97 + resources/views/dashboard/index.blade.php | 411 + resources/views/laporan/cetak.blade.php | 451 + resources/views/laporan/detail.blade.php | 686 ++ resources/views/laporan/index.blade.php | 315 + resources/views/layouts/app.blade.php | 170 + resources/views/layouts/user.blade.php | 176 + resources/views/makanan/create.blade.php | 245 + resources/views/makanan/detail.blade.php | 220 + resources/views/makanan/edit.blade.php | 255 + resources/views/makanan/index.blade.php | 337 + resources/views/panduan/index.blade.php | 171 + resources/views/pasien/create.blade.php | 230 + resources/views/pasien/edit.blade.php | 203 + resources/views/pasien/index.blade.php | 179 + resources/views/profile/edit.blade.php | 259 + resources/views/proses_saw/index.blade.php | 511 + .../views/user/dashboard/index.blade.php | 99 + resources/views/user/laporan/index.blade.php | 167 + resources/views/user/laporan/show.blade.php | 670 ++ resources/views/user/panduan/index.blade.php | 141 + resources/views/user/profile/edit.blade.php | 259 + .../views/user/proses_saw/index.blade.php | 200 + .../vendor/pagination/bootstrap-4.blade.php | 46 + .../vendor/pagination/bootstrap-5.blade.php | 88 + .../views/vendor/pagination/default.blade.php | 46 + .../vendor/pagination/semantic-ui.blade.php | 36 + .../pagination/simple-bootstrap-4.blade.php | 27 + .../pagination/simple-bootstrap-5.blade.php | 29 + .../pagination/simple-default.blade.php | 19 + .../pagination/simple-tailwind.blade.php | 25 + .../vendor/pagination/tailwind.blade.php | 106 + resources/views/welcome.blade.php | 133 + routes/api.php | 19 + routes/channels.php | 18 + routes/console.php | 19 + routes/web.php | 190 + storage/app/.gitignore | 3 + storage/app/public/.gitignore | 2 + storage/framework/.gitignore | 9 + storage/framework/cache/.gitignore | 3 + storage/framework/cache/data/.gitignore | 2 + storage/framework/sessions/.gitignore | 2 + storage/framework/testing/.gitignore | 2 + storage/framework/views/.gitignore | 2 + storage/logs/.gitignore | 2 + tests/CreatesApplication.php | 21 + tests/Feature/ExampleTest.php | 19 + tests/TestCase.php | 10 + tests/Unit/ExampleTest.php | 16 + vite.config.js | 11 + 145 files changed, 21214 insertions(+) create mode 100644 .editorconfig create mode 100644 .env.example create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 README.md create mode 100644 app/Console/Kernel.php create mode 100644 app/Exceptions/Handler.php create mode 100644 app/Http/Controllers/Auth/ForgotPasswordController.php create mode 100644 app/Http/Controllers/Auth/ResetPasswordController.php create mode 100644 app/Http/Controllers/Controller.php create mode 100644 app/Http/Controllers/DashboardController.php create mode 100644 app/Http/Controllers/LaporanController.php create mode 100644 app/Http/Controllers/MakananController.php create mode 100644 app/Http/Controllers/PanduanController.php create mode 100644 app/Http/Controllers/PasienController.php create mode 100644 app/Http/Controllers/ProsesSawController.php create mode 100644 app/Http/Controllers/UserDashboardController.php create mode 100644 app/Http/Controllers/UserLaporanController.php create mode 100644 app/Http/Controllers/UserPanduanController.php create mode 100644 app/Http/Controllers/UserProsesSawController.php create mode 100644 app/Http/Kernel.php create mode 100644 app/Http/Middleware/Authenticate.php create mode 100644 app/Http/Middleware/EncryptCookies.php create mode 100644 app/Http/Middleware/PreventRequestsDuringMaintenance.php create mode 100644 app/Http/Middleware/RedirectIfAuthenticated.php create mode 100644 app/Http/Middleware/TrimStrings.php create mode 100644 app/Http/Middleware/TrustHosts.php create mode 100644 app/Http/Middleware/TrustProxies.php create mode 100644 app/Http/Middleware/ValidateSignature.php create mode 100644 app/Http/Middleware/VerifyCsrfToken.php create mode 100644 app/Models/Laporan.php create mode 100644 app/Models/Makanan.php create mode 100644 app/Models/Pasien.php create mode 100644 app/Models/RekomendasiUser.php create mode 100644 app/Models/User.php create mode 100644 app/Providers/AppServiceProvider.php create mode 100644 app/Providers/AuthServiceProvider.php create mode 100644 app/Providers/BroadcastServiceProvider.php create mode 100644 app/Providers/EventServiceProvider.php create mode 100644 app/Providers/RouteServiceProvider.php create mode 100644 artisan create mode 100644 bootstrap/app.php create mode 100644 bootstrap/cache/.gitignore create mode 100644 composer.json create mode 100644 composer.lock create mode 100644 config/app.php create mode 100644 config/auth.php create mode 100644 config/broadcasting.php create mode 100644 config/cache.php create mode 100644 config/cors.php create mode 100644 config/database.php create mode 100644 config/filesystems.php create mode 100644 config/hashing.php create mode 100644 config/logging.php create mode 100644 config/mail.php create mode 100644 config/queue.php create mode 100644 config/sanctum.php create mode 100644 config/services.php create mode 100644 config/session.php create mode 100644 config/view.php create mode 100644 database/.gitignore create mode 100644 database/factories/UserFactory.php create mode 100644 database/migrations/2014_10_12_000000_create_users_table.php create mode 100644 database/migrations/2014_10_12_100000_create_password_reset_tokens_table.php create mode 100644 database/migrations/2019_08_19_000000_create_failed_jobs_table.php create mode 100644 database/migrations/2019_12_14_000001_create_personal_access_tokens_table.php create mode 100644 database/migrations/2026_03_03_062357_create_pasien_table.php create mode 100644 database/migrations/2026_03_03_065726_create_makanan_table.php create mode 100644 database/migrations/2026_03_05_153520_create_laporan_table.php create mode 100644 database/migrations/2026_04_21_011631_add_rasa_to_makanans_table.php create mode 100644 database/migrations/2026_04_21_014640_add_preferensi_to_laporans_table.php create mode 100644 database/migrations/2026_04_28_035125_add_porsi_saji_to_makanan_table.php create mode 100644 database/migrations/2026_04_29_043731_add_tekstur_to_makanans_table.php create mode 100644 database/migrations/2026_05_04_124728_add_tb_bb_to_pasien_table.php create mode 100644 database/migrations/2026_05_07_153633_add_komposisi_makanan_to_makanan_table.php create mode 100644 database/migrations/2026_05_10_131503_drop_preferensi_makanan_from_pasien_table.php create mode 100644 database/migrations/2026_05_20_020330_add_snapshot_pasien_to_laporan_table.php create mode 100644 database/migrations/2026_05_20_150016_add_porsi_to_laporan_table.php create mode 100644 database/migrations/2026_05_22_031916_add_frekuensi_makan_to_laporan_table.php create mode 100644 database/migrations/2026_06_13_133337_create_rekomendasi_users_table.php create mode 100644 database/migrations/2026_06_13_143401_add_role_to_users_table.php create mode 100644 database/seeders/DatabaseSeeder.php create mode 100644 package.json create mode 100644 phpunit.xml create mode 100644 public/.htaccess create mode 100644 public/favicon.ico create mode 100644 public/index.php create mode 100644 public/robots.txt create mode 100644 resources/css/app.css create mode 100644 resources/js/app.js create mode 100644 resources/js/bootstrap.js create mode 100644 resources/views/auth/forgot-password.blade.php create mode 100644 resources/views/auth/login.blade.php create mode 100644 resources/views/auth/register.blade.php create mode 100644 resources/views/auth/reset-password.blade.php create mode 100644 resources/views/dashboard/index.blade.php create mode 100644 resources/views/laporan/cetak.blade.php create mode 100644 resources/views/laporan/detail.blade.php create mode 100644 resources/views/laporan/index.blade.php create mode 100644 resources/views/layouts/app.blade.php create mode 100644 resources/views/layouts/user.blade.php create mode 100644 resources/views/makanan/create.blade.php create mode 100644 resources/views/makanan/detail.blade.php create mode 100644 resources/views/makanan/edit.blade.php create mode 100644 resources/views/makanan/index.blade.php create mode 100644 resources/views/panduan/index.blade.php create mode 100644 resources/views/pasien/create.blade.php create mode 100644 resources/views/pasien/edit.blade.php create mode 100644 resources/views/pasien/index.blade.php create mode 100644 resources/views/profile/edit.blade.php create mode 100644 resources/views/proses_saw/index.blade.php create mode 100644 resources/views/user/dashboard/index.blade.php create mode 100644 resources/views/user/laporan/index.blade.php create mode 100644 resources/views/user/laporan/show.blade.php create mode 100644 resources/views/user/panduan/index.blade.php create mode 100644 resources/views/user/profile/edit.blade.php create mode 100644 resources/views/user/proses_saw/index.blade.php create mode 100644 resources/views/vendor/pagination/bootstrap-4.blade.php create mode 100644 resources/views/vendor/pagination/bootstrap-5.blade.php create mode 100644 resources/views/vendor/pagination/default.blade.php create mode 100644 resources/views/vendor/pagination/semantic-ui.blade.php create mode 100644 resources/views/vendor/pagination/simple-bootstrap-4.blade.php create mode 100644 resources/views/vendor/pagination/simple-bootstrap-5.blade.php create mode 100644 resources/views/vendor/pagination/simple-default.blade.php create mode 100644 resources/views/vendor/pagination/simple-tailwind.blade.php create mode 100644 resources/views/vendor/pagination/tailwind.blade.php create mode 100644 resources/views/welcome.blade.php create mode 100644 routes/api.php create mode 100644 routes/channels.php create mode 100644 routes/console.php create mode 100644 routes/web.php create mode 100644 storage/app/.gitignore create mode 100644 storage/app/public/.gitignore create mode 100644 storage/framework/.gitignore create mode 100644 storage/framework/cache/.gitignore create mode 100644 storage/framework/cache/data/.gitignore create mode 100644 storage/framework/sessions/.gitignore create mode 100644 storage/framework/testing/.gitignore create mode 100644 storage/framework/views/.gitignore create mode 100644 storage/logs/.gitignore create mode 100644 tests/CreatesApplication.php create mode 100644 tests/Feature/ExampleTest.php create mode 100644 tests/TestCase.php create mode 100644 tests/Unit/ExampleTest.php create mode 100644 vite.config.js diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..8f0de65 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,18 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_size = 4 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true + +[*.md] +trim_trailing_whitespace = false + +[*.{yml,yaml}] +indent_size = 2 + +[docker-compose.yml] +indent_size = 4 diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..ea0665b --- /dev/null +++ b/.env.example @@ -0,0 +1,59 @@ +APP_NAME=Laravel +APP_ENV=local +APP_KEY= +APP_DEBUG=true +APP_URL=http://localhost + +LOG_CHANNEL=stack +LOG_DEPRECATIONS_CHANNEL=null +LOG_LEVEL=debug + +DB_CONNECTION=mysql +DB_HOST=127.0.0.1 +DB_PORT=3306 +DB_DATABASE=laravel +DB_USERNAME=root +DB_PASSWORD= + +BROADCAST_DRIVER=log +CACHE_DRIVER=file +FILESYSTEM_DISK=local +QUEUE_CONNECTION=sync +SESSION_DRIVER=file +SESSION_LIFETIME=120 + +MEMCACHED_HOST=127.0.0.1 + +REDIS_HOST=127.0.0.1 +REDIS_PASSWORD=null +REDIS_PORT=6379 + +MAIL_MAILER=smtp +MAIL_HOST=mailpit +MAIL_PORT=1025 +MAIL_USERNAME=null +MAIL_PASSWORD=null +MAIL_ENCRYPTION=null +MAIL_FROM_ADDRESS="hello@example.com" +MAIL_FROM_NAME="${APP_NAME}" + +AWS_ACCESS_KEY_ID= +AWS_SECRET_ACCESS_KEY= +AWS_DEFAULT_REGION=us-east-1 +AWS_BUCKET= +AWS_USE_PATH_STYLE_ENDPOINT=false + +PUSHER_APP_ID= +PUSHER_APP_KEY= +PUSHER_APP_SECRET= +PUSHER_HOST= +PUSHER_PORT=443 +PUSHER_SCHEME=https +PUSHER_APP_CLUSTER=mt1 + +VITE_APP_NAME="${APP_NAME}" +VITE_PUSHER_APP_KEY="${PUSHER_APP_KEY}" +VITE_PUSHER_HOST="${PUSHER_HOST}" +VITE_PUSHER_PORT="${PUSHER_PORT}" +VITE_PUSHER_SCHEME="${PUSHER_SCHEME}" +VITE_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}" diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..fcb21d3 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,11 @@ +* text=auto eol=lf + +*.blade.php diff=html +*.css diff=css +*.html diff=html +*.md diff=markdown +*.php diff=php + +/.github export-ignore +CHANGELOG.md export-ignore +.styleci.yml export-ignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7fe978f --- /dev/null +++ b/.gitignore @@ -0,0 +1,19 @@ +/.phpunit.cache +/node_modules +/public/build +/public/hot +/public/storage +/storage/*.key +/vendor +.env +.env.backup +.env.production +.phpunit.result.cache +Homestead.json +Homestead.yaml +auth.json +npm-debug.log +yarn-error.log +/.fleet +/.idea +/.vscode diff --git a/README.md b/README.md new file mode 100644 index 0000000..1a4c26b --- /dev/null +++ b/README.md @@ -0,0 +1,66 @@ +
+ + + +## About Laravel + +Laravel is a web application framework with expressive, elegant syntax. We believe development must be an enjoyable and creative experience to be truly fulfilling. Laravel takes the pain out of development by easing common tasks used in many web projects, such as: + +- [Simple, fast routing engine](https://laravel.com/docs/routing). +- [Powerful dependency injection container](https://laravel.com/docs/container). +- Multiple back-ends for [session](https://laravel.com/docs/session) and [cache](https://laravel.com/docs/cache) storage. +- Expressive, intuitive [database ORM](https://laravel.com/docs/eloquent). +- Database agnostic [schema migrations](https://laravel.com/docs/migrations). +- [Robust background job processing](https://laravel.com/docs/queues). +- [Real-time event broadcasting](https://laravel.com/docs/broadcasting). + +Laravel is accessible, powerful, and provides tools required for large, robust applications. + +## Learning Laravel + +Laravel has the most extensive and thorough [documentation](https://laravel.com/docs) and video tutorial library of all modern web application frameworks, making it a breeze to get started with the framework. + +You may also try the [Laravel Bootcamp](https://bootcamp.laravel.com), where you will be guided through building a modern Laravel application from scratch. + +If you don't feel like reading, [Laracasts](https://laracasts.com) can help. Laracasts contains thousands of video tutorials on a range of topics including Laravel, modern PHP, unit testing, and JavaScript. Boost your skills by digging into our comprehensive video library. + +## Laravel Sponsors + +We would like to extend our thanks to the following sponsors for funding Laravel development. If you are interested in becoming a sponsor, please visit the [Laravel Partners program](https://partners.laravel.com). + +### Premium Partners + +- **[Vehikl](https://vehikl.com/)** +- **[Tighten Co.](https://tighten.co)** +- **[WebReinvent](https://webreinvent.com/)** +- **[Kirschbaum Development Group](https://kirschbaumdevelopment.com)** +- **[64 Robots](https://64robots.com)** +- **[Curotec](https://www.curotec.com/services/technologies/laravel/)** +- **[Cyber-Duck](https://cyber-duck.co.uk)** +- **[DevSquad](https://devsquad.com/hire-laravel-developers)** +- **[Jump24](https://jump24.co.uk)** +- **[Redberry](https://redberry.international/laravel/)** +- **[Active Logic](https://activelogic.com)** +- **[byte5](https://byte5.de)** +- **[OP.GG](https://op.gg)** + +## Contributing + +Thank you for considering contributing to the Laravel framework! The contribution guide can be found in the [Laravel documentation](https://laravel.com/docs/contributions). + +## Code of Conduct + +In order to ensure that the Laravel community is welcoming to all, please review and abide by the [Code of Conduct](https://laravel.com/docs/contributions#code-of-conduct). + +## Security Vulnerabilities + +If you discover a security vulnerability within Laravel, please send an e-mail to Taylor Otwell via [taylor@laravel.com](mailto:taylor@laravel.com). All security vulnerabilities will be promptly addressed. + +## License + +The Laravel framework is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT). diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php new file mode 100644 index 0000000..e6b9960 --- /dev/null +++ b/app/Console/Kernel.php @@ -0,0 +1,27 @@ +command('inspire')->hourly(); + } + + /** + * Register the commands for the application. + */ + protected function commands(): void + { + $this->load(__DIR__.'/Commands'); + + require base_path('routes/console.php'); + } +} diff --git a/app/Exceptions/Handler.php b/app/Exceptions/Handler.php new file mode 100644 index 0000000..56af264 --- /dev/null +++ b/app/Exceptions/Handler.php @@ -0,0 +1,30 @@ + + */ + protected $dontFlash = [ + 'current_password', + 'password', + 'password_confirmation', + ]; + + /** + * Register the exception handling callbacks for the application. + */ + public function register(): void + { + $this->reportable(function (Throwable $e) { + // + }); + } +} diff --git a/app/Http/Controllers/Auth/ForgotPasswordController.php b/app/Http/Controllers/Auth/ForgotPasswordController.php new file mode 100644 index 0000000..44a748b --- /dev/null +++ b/app/Http/Controllers/Auth/ForgotPasswordController.php @@ -0,0 +1,30 @@ +validate([ + 'email' => 'required|email|exists:users,email', + ]); + + $status = Password::sendResetLink( + $request->only('email') + ); + + return $status === Password::RESET_LINK_SENT + ? back()->with('success', 'Link reset password berhasil dikirim.') + : back()->with('error', 'Gagal mengirim link reset password.'); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Auth/ResetPasswordController.php b/app/Http/Controllers/Auth/ResetPasswordController.php new file mode 100644 index 0000000..4b5adff --- /dev/null +++ b/app/Http/Controllers/Auth/ResetPasswordController.php @@ -0,0 +1,52 @@ + $token, + 'email' => $request->email, + ]); + } + + public function reset(Request $request) + { + $request->validate([ + 'token' => 'required', + 'email' => 'required|email', + 'password' => 'required|min:6|confirmed', + ]); + + $status = Password::reset( + $request->only( + 'email', + 'password', + 'password_confirmation', + 'token' + ), + + function ($user, $password) { + + $user->password = Hash::make($password); + + $user->remember_token = Str::random(60); + + $user->save(); + } + ); + + return $status === Password::PASSWORD_RESET + ? redirect()->route('login') + ->with('success', 'Password berhasil diubah.') + : back()->with('error', 'Token tidak valid.'); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Controller.php b/app/Http/Controllers/Controller.php new file mode 100644 index 0000000..77ec359 --- /dev/null +++ b/app/Http/Controllers/Controller.php @@ -0,0 +1,12 @@ += 0; $i--) { + + $tanggal = Carbon::now()->subDays($i); + + $pasienHarian[] = Pasien::whereDate( + 'created_at', + $tanggal + )->count(); + } + + // MINGGUAN (4 minggu) + $pasienMingguan = []; + + for ($i = 3; $i >= 0; $i--) { + + $start = Carbon::now() + ->subWeeks($i) + ->startOfWeek(); + + $end = Carbon::now() + ->subWeeks($i) + ->endOfWeek(); + + $pasienMingguan[] = Pasien::whereBetween( + 'created_at', + [$start, $end] + )->count(); + } + + // BULANAN (6 bulan) + $pasienBulanan = []; + + for ($i = 5; $i >= 0; $i--) { + + $bulan = Carbon::now()->subMonths($i); + + $pasienBulanan[] = Pasien::whereMonth( + 'created_at', + $bulan->month + )->whereYear( + 'created_at', + $bulan->year + )->count(); + } + + // ========================= + // DATA MAKANAN + // ========================= + + $makananHarian = []; + + for ($i = 6; $i >= 0; $i--) { + + $tanggal = Carbon::now()->subDays($i); + + $makananHarian[] = Makanan::whereDate( + 'created_at', + $tanggal + )->count(); + } + + $makananMingguan = []; + + for ($i = 3; $i >= 0; $i--) { + + $start = Carbon::now() + ->subWeeks($i) + ->startOfWeek(); + + $end = Carbon::now() + ->subWeeks($i) + ->endOfWeek(); + + $makananMingguan[] = Makanan::whereBetween( + 'created_at', + [$start, $end] + )->count(); + } + + $makananBulanan = []; + + for ($i = 5; $i >= 0; $i--) { + + $bulan = Carbon::now()->subMonths($i); + + $makananBulanan[] = Makanan::whereMonth( + 'created_at', + $bulan->month + )->whereYear( + 'created_at', + $bulan->year + )->count(); + } + + // ========================= + // DATA LAPORAN + // ========================= + + $laporanHarian = []; + + for ($i = 6; $i >= 0; $i--) { + + $tanggal = Carbon::now()->subDays($i); + + $laporanHarian[] = Laporan::whereDate( + 'created_at', + $tanggal + )->count(); + } + + $laporanMingguan = []; + + for ($i = 3; $i >= 0; $i--) { + + $start = Carbon::now() + ->subWeeks($i) + ->startOfWeek(); + + $end = Carbon::now() + ->subWeeks($i) + ->endOfWeek(); + + $laporanMingguan[] = Laporan::whereBetween( + 'created_at', + [$start, $end] + )->count(); + } + + $laporanBulanan = []; + + for ($i = 5; $i >= 0; $i--) { + + $bulan = Carbon::now()->subMonths($i); + + $laporanBulanan[] = Laporan::whereMonth( + 'created_at', + $bulan->month + )->whereYear( + 'created_at', + $bulan->year + )->count(); + } + + // ========================= + // VIEW + // ========================= + return view('dashboard.index', compact( + + 'pasienCount', + 'makananCount', + 'laporanCount', + + 'pasienHarian', + 'pasienMingguan', + 'pasienBulanan', + + 'makananHarian', + 'makananMingguan', + 'makananBulanan', + + 'laporanHarian', + 'laporanMingguan', + 'laporanBulanan' + )); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/LaporanController.php b/app/Http/Controllers/LaporanController.php new file mode 100644 index 0000000..4625c3c --- /dev/null +++ b/app/Http/Controllers/LaporanController.php @@ -0,0 +1,80 @@ +filled('nama_pasien')) { + $query->where('nama_pasien', 'like', '%' . $request->nama_pasien . '%'); + } + + if ($request->filled('tanggal_dari')) { + $query->whereDate('created_at', '>=', $request->tanggal_dari); + } + + if ($request->filled('tanggal_sampai')) { + $query->whereDate('created_at', '<=', $request->tanggal_sampai); + } + + $laporan = $query->latest() + ->paginate(10) + ->withQueryString(); + + return view('laporan.index', compact('laporan')); + } + + public function detail($id) +{ + $laporan = Laporan::findOrFail($id); + $hasil = []; + + if (!empty($laporan->hasil_json)) { + $decoded = json_decode($laporan->hasil_json, true); + + $hasil = collect($decoded)->map(function($item) { + return [ + 'nama_makanan' => $item['nama_makanan'] ?? ($item['makanan']['nama_makanan'] ?? 'Tanpa Nama'), + + // Tambahkan pengecekan fallback jika tersimpan di dalam nested array 'makanan' + 'porsi_saji' => $item['porsi_saji'] ?? ($item['makanan']['porsi_saji'] ?? '-'), + + 'cara_pengolahan' => $item['cara_pengolahan'] ?? ($item['makanan']['cara_pengolahan'] ?? '-'), + 'alternatif_serupa' => $item['alternatif_serupa'] ?? ($item['makanan']['alternatif_serupa'] ?? '-'), + 'karbohidrat' => $item['karbohidrat'] ?? ($item['makanan']['karbohidrat'] ?? 0), + 'lemak' => $item['lemak'] ?? ($item['kandungan_lemak'] ?? 0), + 'serat' => $item['serat'] ?? ($item['kandungan_serat'] ?? 0), + 'kalori' => $item['kalori'] ?? ($item['makanan']['kalori'] ?? 0), + 'protein' => $item['protein'] ?? ($item['makanan']['protein'] ?? 0), + 'skor' => $item['skor'] ?? 0, + ]; + })->sortByDesc('skor')->values()->all(); + } + + return view('laporan.detail', compact('laporan', 'hasil')); +} + + public function destroy($id) + { + $laporan = Laporan::findOrFail($id); + $laporan->delete(); + + return redirect() + ->route('laporan.index') + ->with('success', 'Data laporan berhasil dihapus'); + } + + public function cetak($id) +{ + $laporan = \App\Models\Laporan::with('pasien')->findOrFail($id); + + return view('laporan.cetak', compact('laporan')); +} +} \ No newline at end of file diff --git a/app/Http/Controllers/MakananController.php b/app/Http/Controllers/MakananController.php new file mode 100644 index 0000000..7ba7aed --- /dev/null +++ b/app/Http/Controllers/MakananController.php @@ -0,0 +1,87 @@ +paginate(10); + return view('makanan.index', compact('makanan')); + } + + public function create() + { + return view('makanan.create'); + } + + public function store(Request $request) + { + $request->validate([ + 'nama_makanan' => 'required|string|max:255', + 'protein' => 'required|numeric', + 'kandungan_lemak' => 'required|numeric', + 'karbohidrat'=> 'required|numeric', + 'kandungan_serat' => 'required|numeric', + 'kalori' => 'required|numeric', + 'porsi_saji' => 'required|string|max:255', + 'tekstur' => 'required|in:lunak,sedang,keras', + 'cara_pengolahan' => 'required|string', + 'alternatif_serupa' => 'nullable|string', + 'komposisi_makanan' => 'nullable|string', + ]); + + Makanan::create($request->all()); + + return redirect()->route('makanan.index') + ->with('success', 'Data makanan berhasil ditambahkan.'); + } + + public function edit($id) + { + $makanan = Makanan::findOrFail($id); + return view('makanan.edit', compact('makanan')); + } + + public function update(Request $request, $id) + { + $request->validate([ + 'nama_makanan' => 'required|string|max:255', + 'protein' => 'required|numeric', + 'kandungan_lemak' => 'required|numeric', + 'karbohidrat'=> 'required|numeric', + 'kandungan_serat' => 'required|numeric', + 'kalori' => 'required|numeric', + 'porsi_saji' => 'required|string|max:255', + 'tekstur' => 'required|in:lunak,sedang,keras', + 'cara_pengolahan' => 'required|string', + 'alternatif_serupa' => 'nullable|string', + 'komposisi_makanan' => 'nullable|string', + ]); + + $makanan = Makanan::findOrFail($id); + $makanan->update($request->all()); + + return redirect()->route('makanan.index') + ->with('success', 'Data makanan berhasil diperbarui.'); + } + + public function destroy($id) + { + $makanan = Makanan::findOrFail($id); + $makanan->delete(); + + return redirect()->route('makanan.index') + ->with('success', 'Data makanan berhasil dihapus.'); + } + + public function show($id) + { + $makanan = Makanan::findOrFail($id); + + return view('makanan.detail', compact('makanan')); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/PanduanController.php b/app/Http/Controllers/PanduanController.php new file mode 100644 index 0000000..93b28ab --- /dev/null +++ b/app/Http/Controllers/PanduanController.php @@ -0,0 +1,11 @@ +get(); + return view('pasien.index', compact('pasien')); + } + + /** + * Tampilkan form tambah pasien + */ + public function create() + { + return view('pasien.create'); + } + + /** + * Simpan data pasien baru + */ + public function store(Request $request) + { + $validated = $request->validate([ + 'nama' => 'required|string|max:255', + 'umur' => 'required|integer|min:1', + 'jenis_kelamin' => 'required|in:Laki-laki,Perempuan', + 'berat_badan' => 'required|numeric|min:1', + 'tinggi_badan' => 'required|numeric|min:1', + 'gejala' => 'required|array|min:1', + 'gejala.*' => 'in:nyeri lambung,mual,asam lambung naik', + 'aktivitas_harian' => 'required|string', + ]); + + // 🔥 ubah array gejala jadi string + $validated['gejala'] = implode(',', $validated['gejala']); + + Pasien::create($validated); + + return redirect()->route('pasien.index') + ->with('success', 'Data pasien berhasil ditambahkan.'); + } + + /** + * Tampilkan form edit pasien + */ + public function edit($id) + { + $pasien = Pasien::findOrFail($id); + return view('pasien.edit', compact('pasien')); + } + + /** + * Update data pasien + */ + public function update(Request $request, $id) + { + $validated = $request->validate([ + 'nama' => 'required|string|max:255', + 'umur' => 'required|integer|min:1', + 'jenis_kelamin' => 'required|in:Laki-laki,Perempuan', + 'berat_badan' => 'required|numeric|min:1', + 'tinggi_badan' => 'required|numeric|min:1', + 'gejala' => 'required|array|min:1', + 'gejala.*' => 'in:nyeri lambung,mual,asam lambung naik', + 'aktivitas_harian' => 'required|string', + ]); + + // 🔥 ubah array gejala jadi string + $validated['gejala'] = implode(',', $validated['gejala']); + + $pasien = Pasien::findOrFail($id); + $pasien->update($validated); + + return redirect()->route('pasien.index') + ->with('success', 'Data pasien berhasil diperbarui.'); + } + + /** + * Hapus data pasien + */ + public function destroy($id) + { + $pasien = Pasien::findOrFail($id); + $pasien->delete(); + + return redirect()->route('pasien.index') + ->with('success', 'Data pasien berhasil dihapus.'); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/ProsesSawController.php b/app/Http/Controllers/ProsesSawController.php new file mode 100644 index 0000000..2d43a25 --- /dev/null +++ b/app/Http/Controllers/ProsesSawController.php @@ -0,0 +1,429 @@ +validate([ + 'pasien_id' => 'required|exists:pasien,id' + ]); + + $pasien = Pasien::findOrFail($request->pasien_id); + + $gejalaList = explode(',', strtolower($pasien->gejala ?? '')); + $gejalaList = array_map('trim', $gejalaList); + + if (empty($gejalaList) || in_array('tidak ada keluhan', $gejalaList)) { + return redirect()->back() + ->with('error', 'Pasien tidak memiliki gejala gastritis.'); + } + + $kalori_harian = $this->hitungKaloriHarian($pasien); + + $makanans = Makanan::query(); + + if (in_array('nyeri lambung', $gejalaList)) { + $makanans = $makanans->where('kandungan_lemak', '<=', 7); + } + + if (in_array('mual', $gejalaList)) { + $makanans = $makanans + ->where('kandungan_serat', '<=', 4) + ->whereIn('tekstur', ['lunak', 'sedang']); + } else { + $makanans = $makanans + ->whereIn('tekstur', ['lunak', 'sedang']); + } + + $makanans = $makanans->get(); + + if ($makanans->isEmpty()) { + return redirect()->back() + ->with('error', 'Tidak ada makanan yang sesuai.'); + } + + $hasil = $this->hitungSAW( + $makanans, + $gejalaList, + $pasien->aktivitas_harian, + $kalori_harian, + $pasien->jenis_kelamin + ); + + if (empty($hasil)) { + return redirect()->back() + ->with('error', 'Perhitungan SAW gagal.'); + } + + usort($hasil, fn($a, $b) => $b['skor'] <=> $a['skor']); + + $terbaik = $hasil[0]; + + $hasil = array_slice($hasil, 0, 10); + + $jumlah_porsi = 1; + + if ($pasien->berat_badan >= 70 || $pasien->aktivitas_harian == 'Berat') { + $ukuran_porsi = 'Mangkuk Sedang'; + } else { + $ukuran_porsi = 'Mangkuk Kecil'; + } + + if ($pasien->aktivitas_harian == 'Berat') { + $frekuensi_makan = '3x makan utama + 3x snack'; + } elseif ($pasien->aktivitas_harian == 'Sedang') { + $frekuensi_makan = '3x makan utama + 2-3x snack'; + } else { + $frekuensi_makan = '3x makan utama + 2x snack'; + } + + $hasil_json = array_map(function ($item) use ($jumlah_porsi, $ukuran_porsi, $frekuensi_makan) { + return [ + 'id' => $item['makanan']->id ?? null, + 'nama_makanan' => $item['nama_makanan'] ?? '-', + 'tekstur' => $item['tekstur'] ?? '-', + 'porsi_saji' => $item['porsi_saji'] ?? '-', + 'jumlah_porsi' => $jumlah_porsi, + 'ukuran_porsi' => $ukuran_porsi, + 'frekuensi_makan' => $frekuensi_makan, + 'cara_pengolahan' => $item['cara_pengolahan'] ?? '-', + 'alternatif_serupa' => $item['alternatif_serupa'] ?? '-', + 'komposisi_makanan' => $item['komposisi_makanan'] ?? '-', + 'karbohidrat' => $item['karbohidrat'] ?? 0, + 'lemak' => $item['lemak'] ?? 0, + 'serat' => $item['serat'] ?? 0, + 'kalori' => $item['kalori'] ?? 0, + 'protein' => $item['protein'] ?? 0, + 'skor' => round($item['skor'], 4) + ]; + }, $hasil); + + Laporan::create([ + 'pasien_id' => $pasien->id, + 'nama_pasien' => $pasien->nama, + 'gejala' => $pasien->gejala, + 'aktivitas_harian' => $pasien->aktivitas_harian, + 'berat_badan' => $pasien->berat_badan, + 'tinggi_badan' => $pasien->tinggi_badan, + 'umur' => $pasien->umur, + 'jenis_kelamin' => $pasien->jenis_kelamin, + 'makanan_rekomendasi' => $terbaik['makanan']->nama_makanan, + 'nilai_saw' => round($terbaik['skor'], 4), + 'hasil_json' => json_encode($hasil_json), + 'kebutuhan_kalori' => round($kalori_harian), + 'jumlah_porsi' => $jumlah_porsi, + 'ukuran_porsi' => $ukuran_porsi, + 'frekuensi_makan' => $frekuensi_makan + ]); + + return redirect() + ->route('laporan.index') + ->with('success', 'Rekomendasi berhasil'); + } + + private function hitungKaloriHarian($pasien) + { + if ($pasien->jenis_kelamin == 'Laki-laki') { + $bmr = + 66 + + (13.7 * $pasien->berat_badan) + + (5 * $pasien->tinggi_badan) - + (6.8 * $pasien->umur); + } else { + $bmr = + 655 + + (9.6 * $pasien->berat_badan) + + (1.8 * $pasien->tinggi_badan) - + (4.7 * $pasien->umur); + } + + $faktor = [ + 'Ringan' => 1.2, + 'Sedang' => 1.55, + 'Berat' => 1.9 + ]; + + return $bmr * ($faktor[$pasien->aktivitas_harian] ?? 1.2); + } + + private function hitungSAW($makanans, $gejalaList, $aktivitas, $kalori_harian, $jenis_kelamin) + { + $bobot = $this->tentukanBobot($gejalaList, $aktivitas); + + $max_protein = max($makanans->max('protein'), 1); + $max_karbo = max($makanans->max('karbohidrat'), 1); + + $hasil = []; + + foreach ($makanans as $makanan) { + + $n_protein = ($makanan->protein / $max_protein) * 0.85; + $n_karbo = ($makanan->karbohidrat / $max_karbo) * 0.85; + + if ($aktivitas == 'Berat') { + $ideal_kalori = $kalori_harian * 0.12; + } elseif ($aktivitas == 'Sedang') { + $ideal_kalori = $kalori_harian * 0.09; + } else { + $ideal_kalori = $kalori_harian * 0.06; + } + + if (in_array('mual', $gejalaList)) { + $ideal_kalori *= 0.90; + } + + $ideal_kalori = max($ideal_kalori, 1); + + $selisih = abs($makanan->kalori - $ideal_kalori); + + $n_kalori = 1 / (1 + ($selisih / $ideal_kalori)); + $n_kalori *= 0.80; + + // ========================= + // PENYESUAIAN ENERGI BERDASARKAN JENIS KELAMIN + // ========================= + if ($jenis_kelamin == 'Laki-laki') { + + if ($makanan->kalori >= ($ideal_kalori * 0.60)) { + $n_kalori *= 1.10; + } + + if ($makanan->protein >= 5) { + $n_protein *= 1.08; + } + + if ($makanan->karbohidrat >= 30) { + $n_karbo *= 1.05; + } + + } else { + + if ($makanan->kalori > ($ideal_kalori * 0.95)) { + $n_kalori *= 0.96; + } + } + + if ($aktivitas == 'Ringan') { + if ($makanan->kalori > ($ideal_kalori * 1.80)) { + $n_kalori *= 0.35; + $n_karbo *= 0.75; + } elseif ($makanan->kalori > ($ideal_kalori * 1.35)) { + $n_kalori *= 0.55; + $n_karbo *= 0.85; + } + } + + if ($aktivitas == 'Sedang') { + if ($makanan->kalori > ($ideal_kalori * 1.90)) { + $n_kalori *= 0.60; + $n_karbo *= 0.85; + } + } + + if ($aktivitas == 'Berat') { + if ($makanan->kalori >= ($ideal_kalori * 0.80)) { + $n_kalori *= 1.12; + } + + if ($makanan->protein >= 5) { + $n_protein *= 1.12; + } + + if ($makanan->karbohidrat >= 30) { + $n_karbo *= 1.08; + } + } + + $n_protein = min($n_protein, 1); + $n_karbo = min($n_karbo, 1); + $n_kalori = min($n_kalori, 1); + + if ($makanan->kandungan_lemak <= 2) { + $n_lemak = 0.90; + } elseif ($makanan->kandungan_lemak <= 4) { + $n_lemak = 0.78; + } elseif ($makanan->kandungan_lemak <= 6) { + $n_lemak = 0.60; + } else { + $n_lemak = 0.40; + } + + if ($jenis_kelamin != 'Laki-laki' && $makanan->kandungan_lemak > 4) { + $n_lemak *= 0.92; + } + + if ($makanan->kandungan_serat <= 1) { + $n_serat = 0.90; + } elseif ($makanan->kandungan_serat <= 2) { + $n_serat = 0.78; + } elseif ($makanan->kandungan_serat <= 4) { + $n_serat = 0.60; + } else { + $n_serat = 0.40; + } + + $n_tekstur = match (strtolower($makanan->tekstur)) { + 'lunak' => 0.90, + 'sedang' => 0.70, + default => 0.40 + }; + + if (in_array('mual', $gejalaList)) { + if ($makanan->kandungan_lemak > 5) { + $n_lemak *= 0.70; + } + + if ($makanan->kandungan_serat > 2) { + $n_serat *= 0.75; + } + + if (strtolower($makanan->tekstur) != 'lunak') { + $n_tekstur *= 0.85; + } + } + + if (in_array('nyeri lambung', $gejalaList)) { + if ($makanan->kandungan_serat > 3) { + $n_serat *= 0.60; + } + + if ($makanan->kandungan_lemak > 5) { + $n_lemak *= 0.75; + } + } + + $skor = + ($n_protein * $bobot['protein']) + + ($n_lemak * $bobot['lemak']) + + ($n_karbo * $bobot['karbohidrat']) + + ($n_serat * $bobot['serat']) + + ($n_kalori * $bobot['kalori']) + + ($n_tekstur * $bobot['tekstur']); + + $faktorAktivitasSkor = match ($aktivitas) { + 'Ringan' => 0.92, + 'Sedang' => 1.00, + 'Berat' => 1.10, + default => 1.00 + }; + + $skor = $skor * $faktorAktivitasSkor; + + // ========================= + // PENYESUAIAN SKOR AKHIR BERDASARKAN KEBUTUHAN ENERGI + // ========================= + if ($jenis_kelamin == 'Laki-laki') { + $skor *= match ($aktivitas) { + 'Ringan' => 1.02, + 'Sedang' => 1.03, + 'Berat' => 1.04, + default => 1.02 + }; + } else { + $skor *= 0.98; + } + + $skor = min($skor, 1); + + $hasil[] = [ + 'makanan' => $makanan, + 'nama_makanan' => $makanan->nama_makanan, + 'protein' => $makanan->protein, + 'lemak' => $makanan->kandungan_lemak, + 'karbohidrat' => $makanan->karbohidrat, + 'serat' => $makanan->kandungan_serat, + 'kalori' => $makanan->kalori, + 'tekstur' => $makanan->tekstur, + 'porsi_saji' => $makanan->porsi_saji, + 'cara_pengolahan' => $makanan->cara_pengolahan, + 'alternatif_serupa' => $makanan->alternatif_serupa, + 'komposisi_makanan' => $makanan->komposisi_makanan, + 'skor' => round($skor, 4) + ]; + } + + return $hasil; + } + + private function tentukanBobot($gejalaList, $aktivitas) + { + if (in_array('mual', $gejalaList)) { + + if ($aktivitas == 'Berat') { + return [ + 'tekstur' => 0.18, + 'serat' => 0.14, + 'lemak' => 0.17, + 'karbohidrat' => 0.21, + 'protein' => 0.13, + 'kalori' => 0.17 + ]; + } + + if ($aktivitas == 'Sedang') { + return [ + 'tekstur' => 0.25, + 'serat' => 0.16, + 'lemak' => 0.18, + 'karbohidrat' => 0.18, + 'protein' => 0.10, + 'kalori' => 0.13 + ]; + } + + return [ + 'tekstur' => 0.45, + 'serat' => 0.22, + 'lemak' => 0.18, + 'karbohidrat' => 0.07, + 'protein' => 0.05, + 'kalori' => 0.03 + ]; + } + + if ($aktivitas == 'Berat') { + return [ + 'lemak' => 0.20, + 'tekstur' => 0.16, + 'serat' => 0.10, + 'protein' => 0.16, + 'karbohidrat' => 0.18, + 'kalori' => 0.20 + ]; + } + + if ($aktivitas == 'Sedang') { + return [ + 'lemak' => 0.30, + 'tekstur' => 0.24, + 'serat' => 0.14, + 'protein' => 0.12, + 'karbohidrat' => 0.12, + 'kalori' => 0.08 + ]; + } + + return [ + 'lemak' => 0.36, + 'tekstur' => 0.27, + 'serat' => 0.16, + 'protein' => 0.09, + 'karbohidrat' => 0.07, + 'kalori' => 0.05 + ]; + } +} \ No newline at end of file diff --git a/app/Http/Controllers/UserDashboardController.php b/app/Http/Controllers/UserDashboardController.php new file mode 100644 index 0000000..14e9c38 --- /dev/null +++ b/app/Http/Controllers/UserDashboardController.php @@ -0,0 +1,11 @@ +latest() + ->get(); + + return view('user.laporan.index', compact('laporans')); + } + + public function show($id) + { + $laporan = RekomendasiUser::where('user_id', Auth::id()) + ->findOrFail($id); + + $detail = json_decode($laporan->hasil_json, true); + + return view('user.laporan.show', compact( + 'laporan', + 'detail' + )); + } + + public function destroy($id) + { + $laporan = RekomendasiUser::where( + 'user_id', + auth()->id() + )->findOrFail($id); + + $laporan->delete(); + + return redirect() + ->route('user.laporan.index') + ->with('success', 'Riwayat rekomendasi berhasil dihapus.'); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/UserPanduanController.php b/app/Http/Controllers/UserPanduanController.php new file mode 100644 index 0000000..8d858bc --- /dev/null +++ b/app/Http/Controllers/UserPanduanController.php @@ -0,0 +1,11 @@ +validate([ + 'nama' => 'required', + 'umur' => 'required|numeric', + 'jenis_kelamin' => 'required', + 'berat_badan' => 'required|numeric', + 'tinggi_badan' => 'required|numeric', + 'gejala' => 'required', + 'aktivitas_harian' => 'required' + ]); + + $pasien = (object)[ + 'nama' => $request->nama, + 'umur' => $request->umur, + 'jenis_kelamin' => $request->jenis_kelamin, + 'berat_badan' => $request->berat_badan, + 'tinggi_badan' => $request->tinggi_badan, + 'gejala' => implode(',', $request->gejala), + 'aktivitas_harian' => $request->aktivitas_harian, + ]; + + $gejalaList = explode(',', strtolower($pasien->gejala ?? '')); + $gejalaList = array_map('trim', $gejalaList); + + if (empty($gejalaList) || in_array('tidak ada keluhan', $gejalaList)) { + return redirect()->back() + ->with('error', 'Pasien tidak memiliki gejala gastritis.'); + } + + $kalori_harian = $this->hitungKaloriHarian($pasien); + + $makanans = Makanan::query(); + + if (in_array('nyeri lambung', $gejalaList)) { + $makanans = $makanans->where('kandungan_lemak', '<=', 7); + } + + if (in_array('mual', $gejalaList)) { + $makanans = $makanans + ->where('kandungan_serat', '<=', 4) + ->whereIn('tekstur', ['lunak', 'sedang']); + } else { + $makanans = $makanans + ->whereIn('tekstur', ['lunak', 'sedang']); + } + + $makanans = $makanans->get(); + + if ($makanans->isEmpty()) { + return redirect()->back() + ->with('error', 'Tidak ada makanan yang sesuai.'); + } + + $hasil = $this->hitungSAW( + $makanans, + $gejalaList, + $pasien->aktivitas_harian, + $kalori_harian, + $pasien->jenis_kelamin + ); + + if (empty($hasil)) { + return redirect()->back() + ->with('error', 'Perhitungan SAW gagal.'); + } + + usort($hasil, fn($a, $b) => $b['skor'] <=> $a['skor']); + + $terbaik = $hasil[0]; + + $hasil = array_slice($hasil, 0, 10); + + $jumlah_porsi = 1; + + if ($pasien->berat_badan >= 70 || $pasien->aktivitas_harian == 'Berat') { + $ukuran_porsi = 'Mangkuk Sedang'; + } else { + $ukuran_porsi = 'Mangkuk Kecil'; + } + + if ($pasien->aktivitas_harian == 'Berat') { + $frekuensi_makan = '3x makan utama + 3x snack'; + } elseif ($pasien->aktivitas_harian == 'Sedang') { + $frekuensi_makan = '3x makan utama + 2-3x snack'; + } else { + $frekuensi_makan = '3x makan utama + 2x snack'; + } + + $hasil_json = array_map(function ($item) use ($jumlah_porsi, $ukuran_porsi, $frekuensi_makan) { + return [ + 'id' => $item['makanan']->id ?? null, + 'nama_makanan' => $item['nama_makanan'] ?? '-', + 'tekstur' => $item['tekstur'] ?? '-', + 'porsi_saji' => $item['porsi_saji'] ?? '-', + 'jumlah_porsi' => $jumlah_porsi, + 'ukuran_porsi' => $ukuran_porsi, + 'frekuensi_makan' => $frekuensi_makan, + 'cara_pengolahan' => $item['cara_pengolahan'] ?? '-', + 'alternatif_serupa' => $item['alternatif_serupa'] ?? '-', + 'komposisi_makanan' => $item['komposisi_makanan'] ?? '-', + 'karbohidrat' => $item['karbohidrat'] ?? 0, + 'lemak' => $item['lemak'] ?? 0, + 'serat' => $item['serat'] ?? 0, + 'kalori' => $item['kalori'] ?? 0, + 'protein' => $item['protein'] ?? 0, + 'skor' => round($item['skor'], 4) + ]; + }, $hasil); + + RekomendasiUser::create([ + 'user_id' => auth()->id(), + + 'nama' => $pasien->nama, + 'umur' => $pasien->umur, + + 'jenis_kelamin' => $pasien->jenis_kelamin, + + 'berat_badan' => $pasien->berat_badan, + 'tinggi_badan' => $pasien->tinggi_badan, + + 'gejala' => $pasien->gejala, + 'aktivitas_harian' => $pasien->aktivitas_harian, + + 'makanan_rekomendasi' => $terbaik['makanan']->nama_makanan, + + 'nilai_saw' => round($terbaik['skor'], 4), + + 'kebutuhan_kalori' => round($kalori_harian), + + 'hasil_json' => json_encode($hasil_json) + ]); + + return redirect() + ->route('user.laporan.index') + ->with('success', 'Rekomendasi berhasil'); + } + + private function hitungKaloriHarian($pasien) + { + if ($pasien->jenis_kelamin == 'Laki-laki') { + $bmr = + 66 + + (13.7 * $pasien->berat_badan) + + (5 * $pasien->tinggi_badan) - + (6.8 * $pasien->umur); + } else { + $bmr = + 655 + + (9.6 * $pasien->berat_badan) + + (1.8 * $pasien->tinggi_badan) - + (4.7 * $pasien->umur); + } + + $faktor = [ + 'Ringan' => 1.2, + 'Sedang' => 1.55, + 'Berat' => 1.9 + ]; + + return $bmr * ($faktor[$pasien->aktivitas_harian] ?? 1.2); + } + + private function hitungSAW($makanans, $gejalaList, $aktivitas, $kalori_harian, $jenis_kelamin) + { + $bobot = $this->tentukanBobot($gejalaList, $aktivitas); + + $max_protein = max($makanans->max('protein'), 1); + $max_karbo = max($makanans->max('karbohidrat'), 1); + + $hasil = []; + + foreach ($makanans as $makanan) { + + $n_protein = ($makanan->protein / $max_protein) * 0.85; + $n_karbo = ($makanan->karbohidrat / $max_karbo) * 0.85; + + if ($aktivitas == 'Berat') { + $ideal_kalori = $kalori_harian * 0.12; + } elseif ($aktivitas == 'Sedang') { + $ideal_kalori = $kalori_harian * 0.09; + } else { + $ideal_kalori = $kalori_harian * 0.06; + } + + if (in_array('mual', $gejalaList)) { + $ideal_kalori *= 0.90; + } + + $ideal_kalori = max($ideal_kalori, 1); + + $selisih = abs($makanan->kalori - $ideal_kalori); + + $n_kalori = 1 / (1 + ($selisih / $ideal_kalori)); + $n_kalori *= 0.80; + + // ========================= + // PENYESUAIAN ENERGI BERDASARKAN JENIS KELAMIN + // ========================= + if ($jenis_kelamin == 'Laki-laki') { + + if ($makanan->kalori >= ($ideal_kalori * 0.60)) { + $n_kalori *= 1.10; + } + + if ($makanan->protein >= 5) { + $n_protein *= 1.08; + } + + if ($makanan->karbohidrat >= 30) { + $n_karbo *= 1.05; + } + + } else { + + if ($makanan->kalori > ($ideal_kalori * 0.95)) { + $n_kalori *= 0.96; + } + } + + if ($aktivitas == 'Ringan') { + if ($makanan->kalori > ($ideal_kalori * 1.80)) { + $n_kalori *= 0.35; + $n_karbo *= 0.75; + } elseif ($makanan->kalori > ($ideal_kalori * 1.35)) { + $n_kalori *= 0.55; + $n_karbo *= 0.85; + } + } + + if ($aktivitas == 'Sedang') { + if ($makanan->kalori > ($ideal_kalori * 1.90)) { + $n_kalori *= 0.60; + $n_karbo *= 0.85; + } + } + + if ($aktivitas == 'Berat') { + if ($makanan->kalori >= ($ideal_kalori * 0.80)) { + $n_kalori *= 1.12; + } + + if ($makanan->protein >= 5) { + $n_protein *= 1.12; + } + + if ($makanan->karbohidrat >= 30) { + $n_karbo *= 1.08; + } + } + + $n_protein = min($n_protein, 1); + $n_karbo = min($n_karbo, 1); + $n_kalori = min($n_kalori, 1); + + if ($makanan->kandungan_lemak <= 2) { + $n_lemak = 0.90; + } elseif ($makanan->kandungan_lemak <= 4) { + $n_lemak = 0.78; + } elseif ($makanan->kandungan_lemak <= 6) { + $n_lemak = 0.60; + } else { + $n_lemak = 0.40; + } + + if ($jenis_kelamin != 'Laki-laki' && $makanan->kandungan_lemak > 4) { + $n_lemak *= 0.92; + } + + if ($makanan->kandungan_serat <= 1) { + $n_serat = 0.90; + } elseif ($makanan->kandungan_serat <= 2) { + $n_serat = 0.78; + } elseif ($makanan->kandungan_serat <= 4) { + $n_serat = 0.60; + } else { + $n_serat = 0.40; + } + + $n_tekstur = match (strtolower($makanan->tekstur)) { + 'lunak' => 0.90, + 'sedang' => 0.70, + default => 0.40 + }; + + if (in_array('mual', $gejalaList)) { + if ($makanan->kandungan_lemak > 5) { + $n_lemak *= 0.70; + } + + if ($makanan->kandungan_serat > 2) { + $n_serat *= 0.75; + } + + if (strtolower($makanan->tekstur) != 'lunak') { + $n_tekstur *= 0.85; + } + } + + if (in_array('nyeri lambung', $gejalaList)) { + if ($makanan->kandungan_serat > 3) { + $n_serat *= 0.60; + } + + if ($makanan->kandungan_lemak > 5) { + $n_lemak *= 0.75; + } + } + + $skor = + ($n_protein * $bobot['protein']) + + ($n_lemak * $bobot['lemak']) + + ($n_karbo * $bobot['karbohidrat']) + + ($n_serat * $bobot['serat']) + + ($n_kalori * $bobot['kalori']) + + ($n_tekstur * $bobot['tekstur']); + + $faktorAktivitasSkor = match ($aktivitas) { + 'Ringan' => 0.92, + 'Sedang' => 1.00, + 'Berat' => 1.10, + default => 1.00 + }; + + $skor = $skor * $faktorAktivitasSkor; + + // ========================= + // PENYESUAIAN SKOR AKHIR BERDASARKAN KEBUTUHAN ENERGI + // ========================= + if ($jenis_kelamin == 'Laki-laki') { + $skor *= match ($aktivitas) { + 'Ringan' => 1.02, + 'Sedang' => 1.03, + 'Berat' => 1.04, + default => 1.02 + }; + } else { + $skor *= 0.98; + } + + $skor = min($skor, 1); + + $hasil[] = [ + 'makanan' => $makanan, + 'nama_makanan' => $makanan->nama_makanan, + 'protein' => $makanan->protein, + 'lemak' => $makanan->kandungan_lemak, + 'karbohidrat' => $makanan->karbohidrat, + 'serat' => $makanan->kandungan_serat, + 'kalori' => $makanan->kalori, + 'tekstur' => $makanan->tekstur, + 'porsi_saji' => $makanan->porsi_saji, + 'cara_pengolahan' => $makanan->cara_pengolahan, + 'alternatif_serupa' => $makanan->alternatif_serupa, + 'komposisi_makanan' => $makanan->komposisi_makanan, + 'skor' => round($skor, 4) + ]; + } + + return $hasil; + } + + private function tentukanBobot($gejalaList, $aktivitas) + { + if (in_array('mual', $gejalaList)) { + + if ($aktivitas == 'Berat') { + return [ + 'tekstur' => 0.18, + 'serat' => 0.14, + 'lemak' => 0.17, + 'karbohidrat' => 0.21, + 'protein' => 0.13, + 'kalori' => 0.17 + ]; + } + + if ($aktivitas == 'Sedang') { + return [ + 'tekstur' => 0.25, + 'serat' => 0.16, + 'lemak' => 0.18, + 'karbohidrat' => 0.18, + 'protein' => 0.10, + 'kalori' => 0.13 + ]; + } + + return [ + 'tekstur' => 0.45, + 'serat' => 0.22, + 'lemak' => 0.18, + 'karbohidrat' => 0.07, + 'protein' => 0.05, + 'kalori' => 0.03 + ]; + } + + if ($aktivitas == 'Berat') { + return [ + 'lemak' => 0.20, + 'tekstur' => 0.16, + 'serat' => 0.10, + 'protein' => 0.16, + 'karbohidrat' => 0.18, + 'kalori' => 0.20 + ]; + } + + if ($aktivitas == 'Sedang') { + return [ + 'lemak' => 0.30, + 'tekstur' => 0.24, + 'serat' => 0.14, + 'protein' => 0.12, + 'karbohidrat' => 0.12, + 'kalori' => 0.08 + ]; + } + + return [ + 'lemak' => 0.36, + 'tekstur' => 0.27, + 'serat' => 0.16, + 'protein' => 0.09, + 'karbohidrat' => 0.07, + 'kalori' => 0.05 + ]; + } +} \ No newline at end of file diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php new file mode 100644 index 0000000..494c050 --- /dev/null +++ b/app/Http/Kernel.php @@ -0,0 +1,68 @@ + + */ + protected $middleware = [ + // \App\Http\Middleware\TrustHosts::class, + \App\Http\Middleware\TrustProxies::class, + \Illuminate\Http\Middleware\HandleCors::class, + \App\Http\Middleware\PreventRequestsDuringMaintenance::class, + \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class, + \App\Http\Middleware\TrimStrings::class, + \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class, + ]; + + /** + * The application's route middleware groups. + * + * @var array+ Masukkan email akun Anda. Sistem akan mengirimkan link untuk mengatur ulang password. +
+ + @if(session('success')) ++ Masukkan password baru untuk akun Anda. +
+ + @if(session('error')) ++ Overview data sistem rekomendasi makanan gastritis +
++ Data pasien terdaftar +
++ Alternatif makanan tersedia +
++ Total proses SAW dilakukan +
++ Aplikasi ini digunakan untuk membantu proses pemilihan makanan yang lebih sesuai + bagi penderita gastritis berdasarkan kondisi pasien. Sistem mengolah data gejala, + aktivitas harian, berat badan, tinggi badan, jenis kelamin, serta data kandungan + makanan untuk menghasilkan rekomendasi menggunakan metode + + Simple Additive Weighting (SAW) + . +
+ ++ Hasil rekomendasi tidak hanya menampilkan makanan dengan nilai SAW tertinggi, + tetapi juga memberikan informasi kebutuhan kalori harian, jumlah porsi, ukuran porsi, + frekuensi makan, komposisi makanan, dan alternatif makanan serupa. Dengan demikian, + aplikasi ini dapat membantu petugas dalam membuat laporan rekomendasi makanan + yang lebih terarah, mudah dipahami, dan sesuai dengan kondisi pasien. +
+ ++ Sistem mempertimbangkan gejala gastritis, aktivitas harian, + berat badan, tinggi badan, umur, dan jenis kelamin pasien. +
++ Data makanan dihitung melalui normalisasi, pembobotan kriteria, + nilai preferensi, dan perangkingan alternatif makanan. +
++ Sistem menampilkan makanan terbaik beserta skor SAW, porsi, + ukuran porsi, frekuensi makan, komposisi, dan alternatif serupa. +
++ Hasil rekomendasi disimpan sebagai riwayat laporan dan dapat + dilihat kembali atau dicetak sebagai dokumen rekomendasi. +
++ Visualisasi data berdasarkan periode waktu +
+ +Metode Simple Additive Weighting (SAW)
+| ID Laporan | +: #{{ $laporan->id }} | +
| Waktu | +: {{ $laporan->created_at->format('d M Y') }} | +
| Nama Petugas | +: {{ $namaAdmin }} | +
| Nama Pasien | +: {{ $laporan->nama_pasien }} | +
| Jenis Kelamin | +: {{ $jenis_kelamin }} | +
| Umur | +: {{ $umur }} tahun | +
| Tinggi Pasien | +: {{ $tb }} cm | +
| Berat Pasien | +: {{ $bb }} kg | +
| Gejala Pasien | +: {{ ucwords(str_replace(',', ', ', $laporan->gejala)) }} | +
| Aktivitas Pasien | +: {{ ucwords($aktivitas_laporan) }} | +
| Kebutuhan Kalori Harian | +: {{ number_format($kebutuhan_kalori) }} kkal / hari | +
| Rekomendasi Jumlah Porsi | +: {{ $jumlah_porsi }} porsi | +
| Ukuran Porsi | +: {{ $ukuran_porsi }} | +
| Frekuensi Makan | +: {{ $frekuensi_makan }} | +
| Nama Makanan | +: {{ $top['nama_makanan'] ?? '-' }} | +
| Nilai SAW | +: {{ number_format($top['skor'] ?? 0, 4) }} | +
| Tekstur | +: {{ ucfirst($top['tekstur'] ?? '-') }} | +
| Porsi Saji Awal | +: {{ $top['porsi_saji'] ?? '-' }} | +
| Rekomendasi Porsi | +: {{ $jumlah_porsi }} porsi, ukuran {{ $ukuran_porsi }} | +
| Cara Pengolahan | +: {{ $top['cara_pengolahan'] ?? '-' }} | +
| Komposisi Makanan | +: {{ $top['komposisi_makanan'] ?? '-' }} | +
| Alternatif Serupa | +: {{ $top['alternatif_serupa'] ?? '-' }} | +
{{ $penjelasan }}
+ ++ Pasien dianjurkan mengonsumsi {{ $jumlah_porsi }} porsi + dengan ukuran {{ $ukuran_porsi }} pada setiap makan utama. + Porsi ini disesuaikan agar pasien tetap mendapatkan asupan makanan yang cukup, + tetapi tidak makan terlalu banyak dalam satu waktu. +
+ ++ Selain itu, pasien dianjurkan makan secara teratur sebanyak + {{ $frekuensi_makan }} agar lambung tidak kosong terlalu lama + dan kebutuhan energi harian tetap terpenuhi. + + Snack yang dimaksud adalah makanan ringan yang aman untuk lambung, + seperti makanan yang tidak pedas, tidak asam, tidak berminyak, + dan mudah dicerna. +
+| Rank | +Nama Makanan | +Protein | +Lemak | +Karbo | +Serat | +Kalori | +Tekstur | +Porsi Awal | +Rek. Porsi | +Ukuran | +Pengolahan | +Skor | +
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| {{ $index + 1 }} | +{{ $item['nama_makanan'] ?? '-' }} | +{{ number_format($item['protein'] ?? 0, 1) }} g | +{{ number_format($item['lemak'] ?? 0, 1) }} g | +{{ number_format($item['karbohidrat'] ?? 0, 1) }} g | +{{ number_format($item['serat'] ?? 0, 1) }} g | +{{ number_format($item['kalori'] ?? 0, 0) }} kkal | +{{ ucfirst($item['tekstur'] ?? '-') }} | +{{ $item['porsi_saji'] ?? '-' }} | ++ {{ $item['jumlah_porsi'] ?? $jumlah_porsi }} porsi + | ++ {{ $item['ukuran_porsi'] ?? $ukuran_porsi }} + | +{{ $item['cara_pengolahan'] ?? '-' }} | +{{ number_format($item['skor'] ?? 0, 4) }} | +
| Rank | +Nama Makanan | +Komposisi Makanan | +Alternatif Serupa | +
|---|---|---|---|
| {{ $index + 1 }} | +{{ $item['nama_makanan'] ?? '-' }} | +{{ $item['komposisi_makanan'] ?? '-' }} | +{{ $item['alternatif_serupa'] ?? '-' }} | +
+ Catatan: Laporan ini dihasilkan berdasarkan proses perhitungan metode Simple Additive Weighting (SAW) + dengan mempertimbangkan gejala gastritis, kandungan nutrisi makanan, tekstur makanan, aktivitas pasien, + kebutuhan energi harian, jumlah porsi, ukuran porsi, serta pola makan yang dianjurkan. +
++ Analisis rekomendasi makanan menggunakan metode + + Simple Additive Weighting (SAW) + +
+ + ++ Nama Pasien +
+ ++ {{ $laporan->nama_pasien }} +
+ ++ Waktu +
+ ++ {{ $laporan->created_at->format('d M Y') }} +
+ ++ Gejala +
+ ++ {{ ucwords(str_replace(',', ', ', $laporan->gejala)) }} +
+ ++ Aktivitas +
+ ++ {{ ucwords($laporan->aktivitas_harian ?? '-') }} +
+ ++ Berat +
+ ++ {{ $laporan->berat_badan ?? 0 }} kg +
+ ++ Tinggi +
+ ++ {{ $laporan->tinggi_badan ?? 0 }} cm +
+ ++ Jumlah Porsi +
+ ++ {{ $jumlah_porsi }} Porsi +
+ ++ Ukuran Porsi +
+ ++ {{ $ukuran_porsi }} +
+ ++ Frekuensi Makan +
+ ++ {{ $frekuensi_makan }} +
+ ++ + Menu makanan ini memiliki nilai tertinggi berdasarkan + perhitungan metode + + Simple Additive Weighting (SAW) + + dengan mempertimbangkan kondisi gejala gastritis, + kandungan nutrisi, + tekstur makanan, + dan kebutuhan energi pasien. + +
+ ++ Skor SAW +
+ ++ {!! $penjelasan !!} +
+ + @if(!empty($jumlah_porsi) && !empty($ukuran_porsi)) + + @if(str_contains($gejala, 'mual')) + ++ Pasien dianjurkan mengonsumsi + {{ $jumlah_porsi }} porsi + dengan ukuran + {{ $ukuran_porsi }} + pada setiap makan utama agar makanan lebih nyaman diterima oleh lambung dan tidak memperberat rasa mual. + Konsumsi makanan tetap dianjurkan secara bertahap agar pasien tetap nyaman saat makan. +
+ + @elseif(str_contains($gejala, 'nyeri lambung')) + ++ Pasien dianjurkan mengonsumsi makanan dengan ukuran + {{ $ukuran_porsi }} + pada setiap makan utama. + Jumlah porsi disesuaikan dengan kebutuhan energi dan aktivitas pasien, namun konsumsi makanan tetap dianjurkan secara bertahap agar lambung tidak bekerja terlalu berat. +
+ + @endif + ++ Selain itu, pasien dianjurkan makan secara teratur sebanyak + {{ $frekuensi_makan }} + agar lambung tidak kosong terlalu lama dan kebutuhan energi harian tetap terpenuhi. + Snack yang dimaksud adalah makanan ringan yang aman untuk lambung, seperti makanan yang tidak pedas, tidak asam, tidak berminyak, dan mudah dicerna. +
+ + @endif + ++ Hasil perangkingan makanan berdasarkan metode SAW +
+| Rank | +Menu Makanan | +Protein | +Lemak | +Karbo | +Serat | +Kalori | +Skor SAW | +Detail | + +
|---|---|---|---|---|---|---|---|---|
|
+
+ @if($rank == 1)
+
+
+ 1
+
+
+ @elseif($rank == 2)
+
+
+ 2
+
+
+ @elseif($rank == 3)
+
+
+ 3
+
+
+ @else
+
+
+ {{ $rank }}
+
+
+ @endif
+
+ |
+
+
+
+
+
+
+
+
+ {{ $item['nama_makanan'] ?? '-' }}
+
+
+
+
+
+ {{ ucfirst($item['tekstur'] ?? '-') }}
+
+
+
+ {{ $item['cara_pengolahan'] ?? '-' }}
+
+
+
+
+ |
+
+
+
+ {{ number_format($item['protein'] ?? 0,1) }}g
+
+ |
+
+
+
+ {{ number_format($item['lemak'] ?? 0,1) }}g
+
+ |
+
+
+
+ {{ number_format($item['karbohidrat'] ?? 0,1) }}g
+
+ |
+
+
+
+ {{ number_format($item['serat'] ?? 0,1) }}g
+
+ |
+
+
+
+
+ {{ number_format($item['kalori'] ?? 0,0) }} kkal
+
+
+ |
+
+
+
+
+ {{ number_format($item['skor'] ?? 0,4) }}
+
+
+ |
+
+ + + + + | + +
|
+
+
+
+
+
+
+
+
+
+ + Informasi Makanan ++ +
+
+
+
+
+ Tekstur:
+ {{ ucfirst($item['tekstur'] ?? '-') }}
+
+
+
+ Porsi Saji Awal:
+ {{ $item['porsi_saji'] ?? '-' }}
+
+
+
+ Rekomendasi Jumlah Porsi:
+ {{ $jumlah_porsi }} Porsi
+
+
+
+ Ukuran Porsi:
+ {{ $ukuran_porsi }}
+
+
+
+ Cara Pengolahan:
+ {{ $item['cara_pengolahan'] ?? '-' }}
+
+
+ @if(!empty($item['alternatif_serupa']))
+
+
+ Alternatif:
+ {{ $item['alternatif_serupa'] }}
+
+
+ @endif
+
+
+
+
+
+ + Komposisi Makanan ++ + @if(!empty($item['komposisi_makanan'])) + +
+
+ @foreach(explode(',', $item['komposisi_makanan']) as $komposisi)
+
+
+ {{ trim($komposisi) }}
+
+
+ @endforeach
+
+
+
+ @else
+
+ + Tidak ada data komposisi. + + + @endif + + |
+
+ ||||||||
+ Riwayat hasil rekomendasi makanan berdasarkan metode SAW +
+| No | +Pasien | +Gejala | +Nilai SAW | +Waktu | +Aksi | +
|---|---|---|---|---|---|
| + {{ $laporan->firstItem() + $loop->index }} + | + ++ {{ $item->nama_pasien }} + | + ++ {{ ucwords(str_replace(',', ', ', $item->gejala)) }} + | + ++ {{ number_format($item->nilai_saw,4) }} + | + ++ {{ $item->created_at->format('d M Y') }} + | + + +
+
+
+
+
+ Detail
+
+
+
+
+
+
+ |
+
+
+ Masukkan data makanan untuk digunakan dalam proses rekomendasi +
++ Informasi lengkap kandungan dan data makanan +
+ ++ Detail kandungan nutrisi dan informasi makanan +
+ ++ {{ $makanan->protein }} gram +
++ {{ $makanan->kandungan_lemak }} gram +
++ {{ $makanan->karbohidrat }} gram +
++ {{ $makanan->kandungan_serat }} gram +
++ {{ $makanan->kalori }} kkal +
++ {{ $makanan->porsi_saji ?? '-' }} +
++ {{ $makanan->cara_pengolahan ?? '-' }} +
++ {{ $makanan->alternatif_serupa ?? '-' }} +
++ {{ $makanan->komposisi_makanan ?? '-' }} +
++ Perbarui data makanan yang digunakan dalam sistem rekomendasi +
++ Kelola data makanan untuk proses rekomendasi +
++ Total {{ $makanan->total() }} makanan tersedia +
+| + Nama Makanan + | + ++ Protein + | + ++ Lemak + | + ++ Karbo + | + ++ Serat + | + ++ Kalori + | + ++ Tekstur + | + ++ Aksi + | + +
|---|---|---|---|---|---|---|---|
|
+
+
+
+
+ {{ $m->nama_makanan }}
+
+
+
+
+ |
+
+
+ + {{ number_format($m->protein, 1) }}g + | + ++ {{ number_format($m->kandungan_lemak, 1) }}g + | + ++ {{ number_format($m->karbohidrat, 1) }}g + | + ++ {{ number_format($m->kandungan_serat, 1) }}g + | + ++ + + {{ number_format($m->kalori, 0) }} kkal + + + | + + ++ + + {{ ucfirst($m->tekstur) }} + + + | + + +
+
+
+
+
+
+
+ Detail
+
+
+
+
+
+ Edit
+
+
+
+
+
+
+
+ |
+
+
|
+
+
+
+
+
+
+ 🍲
+
+
+ + Belum Ada Data Makanan ++ ++ Tambahkan data makanan terlebih dahulu + + + |
+
+ |||||||
+ Halaman ini berisi tata cara penggunaan aplikasi + rekomendasi makanan sehat untuk penderita gastritis + menggunakan metode Simple Additive Weighting (SAW). +
++ Masuk menggunakan username dan password. +
++ Pengguna melakukan login untuk mengakses seluruh fitur sistem. +
+ ++ Tambahkan data pasien gastritis. +
++ Input data pasien meliputi umur, berat badan, + tinggi badan, gejala, dan aktivitas harian pasien. +
+ ++ Tambahkan alternatif makanan. +
++ Data makanan terdiri dari kandungan nutrisi, + tekstur makanan, cara pengolahan, alternatif makanan, + dan komposisi makanan. +
+ ++ Melakukan perhitungan rekomendasi. +
++ Sistem akan menghitung nilai SAW berdasarkan + kondisi pasien dan data makanan yang tersedia. +
+ ++ Menampilkan hasil ranking makanan. +
++ Sistem menampilkan rekomendasi makanan terbaik, + ranking makanan, nilai SAW, kebutuhan kalori, + dan detail nutrisi makanan yang dapat dicetak + menjadi laporan. +
+ ++ Perbarui informasi pasien sesuai data terbaru. +
++ Kelola data pasien untuk proses rekomendasi +
+| Nama | +Umur | +Berat (kg) | +Tinggi (cm) | +Jenis Kelamin | +Gejala | +Aktivitas Fisik | +Aksi | +|
|---|---|---|---|---|---|---|---|---|
| + {{ $p->nama }} + | + ++ {{ $p->umur }} + | + ++ {{ $p->berat_badan }} + | + ++ {{ $p->tinggi_badan }} + | + ++ {{ $p->jenis_kelamin }} + | + ++ {{ ucwords(str_replace(',', ', ', $p->gejala)) }} + | + ++ {{ $p->aktivitas_harian }} + | + + +
+
+
+
+ Edit
+
+
+
+
+
+ |
+
+ |
|
+
+ 👤
+
+ Belum ada data pasien + |
+ ||||||||
+ Kelola informasi akun dan keamanan password +
++ Update informasi akun dan email Anda. +
++ Gunakan password yang aman untuk melindungi akun Anda. +
+ ++ Setelah akun dihapus, seluruh data akun akan terhapus permanen. +
+ ++ Pilih data pasien untuk menjalankan proses perhitungan rekomendasi makanan + menggunakan metode Simple Additive Weighting (SAW). +
++ Ketik nama pasien pada kolom pencarian agar lebih mudah menemukan data pasien. +
+ ++ Data pasien akan muncul setelah pasien dipilih. +
++ Silakan cari dan pilih pasien pada panel sebelah kiri untuk melihat detail data pasien sebelum proses SAW dilakukan. +
+ ++ Pasien Terpilih +
+ ++ Data pasien siap digunakan untuk proses rekomendasi. +
+ +Umur
+-
+Jenis Kelamin
+-
+Aktivitas
+-
+Berat Badan
+-
+Tinggi Badan
+-
+Gejala
+-
++ Sistem akan memfilter makanan berdasarkan gejala pasien, menghitung normalisasi kriteria, + mengalikan dengan bobot, lalu menghasilkan ranking rekomendasi makanan terbaik. +
++ Sistem Rekomendasi Makanan Gastritis membantu pengguna memperoleh + rekomendasi makanan yang sesuai berdasarkan gejala gastritis, + aktivitas harian, dan kebutuhan energi menggunakan metode + + Simple Additive Weighting (SAW) + . +
+ ++ Menggunakan metode SAW untuk menentukan alternatif makanan terbaik. +
++ Protein, Lemak, Karbohidrat, Serat, Kalori, dan Tekstur makanan. +
++ Membantu penderita gastritis memilih makanan yang lebih sesuai. +
++ Riwayat hasil rekomendasi makanan yang pernah Anda lakukan. +
+ +| + No + | + ++ Nama + | + ++ Gejala + | + ++ Aktivitas + | + ++ Nilai SAW + | + ++ Tanggal + | + ++ Aksi + | + +
|---|---|---|---|---|---|---|
| + {{ $loop->iteration }} + | + ++ {{ $laporan->nama }} + | + ++ {{ ucwords($laporan->gejala) }} + | + ++ {{ $laporan->aktivitas_harian }} + | + ++ {{ number_format($laporan->nilai_saw, 4) }} + | + ++ {{ $laporan->created_at->format('d M Y') }} + | + + +
+
+
+
+
+ Detail
+
+
+
+
+
+
+ |
+
+
+ Analisis rekomendasi makanan menggunakan metode + + Simple Additive Weighting (SAW) + +
+ + ++ Nama Pasien +
+ ++ {{ $laporan->nama }} +
+ ++ Waktu +
+ ++ {{ $laporan->created_at->format('d M Y') }} +
+ ++ Gejala +
+ ++ {{ ucwords(str_replace(',', ', ', $laporan->gejala)) }} +
+ ++ Aktivitas +
+ ++ {{ ucwords($laporan->aktivitas_harian ?? '-') }} +
+ ++ Berat +
+ ++ {{ $laporan->berat_badan ?? 0 }} kg +
+ ++ Tinggi +
+ ++ {{ $laporan->tinggi_badan ?? 0 }} cm +
+ ++ Jumlah Porsi +
+ ++ {{ $jumlah_porsi }} Porsi +
+ ++ Ukuran Porsi +
+ ++ {{ $ukuran_porsi }} +
+ ++ Frekuensi Makan +
+ ++ {{ $frekuensi_makan }} +
+ ++ + Menu makanan ini memiliki nilai tertinggi berdasarkan + perhitungan metode + + Simple Additive Weighting (SAW) + + dengan mempertimbangkan kondisi gejala gastritis, + kandungan nutrisi, + tekstur makanan, + dan kebutuhan energi pasien. + +
+ ++ Skor SAW +
+ ++ {!! $penjelasan !!} +
+ + @if(!empty($jumlah_porsi) && !empty($ukuran_porsi)) + + @if(str_contains($gejala, 'mual')) + ++ Pasien dianjurkan mengonsumsi + {{ $jumlah_porsi }} porsi + dengan ukuran + {{ $ukuran_porsi }} + pada setiap makan utama agar makanan lebih nyaman diterima oleh lambung dan tidak memperberat rasa mual. + Konsumsi makanan tetap dianjurkan secara bertahap agar pasien tetap nyaman saat makan. +
+ + @elseif(str_contains($gejala, 'nyeri lambung')) + ++ Pasien dianjurkan mengonsumsi makanan dengan ukuran + {{ $ukuran_porsi }} + pada setiap makan utama. + Jumlah porsi disesuaikan dengan kebutuhan energi dan aktivitas pasien, namun konsumsi makanan tetap dianjurkan secara bertahap agar lambung tidak bekerja terlalu berat. +
+ + @endif + ++ Selain itu, pasien dianjurkan makan secara teratur sebanyak + {{ $frekuensi_makan }} + agar lambung tidak kosong terlalu lama dan kebutuhan energi harian tetap terpenuhi. + Snack yang dimaksud adalah makanan ringan yang aman untuk lambung, seperti makanan yang tidak pedas, tidak asam, tidak berminyak, dan mudah dicerna. +
+ + @endif + ++ Hasil perangkingan makanan berdasarkan metode SAW +
+| Rank | +Menu Makanan | +Protein | +Lemak | +Karbo | +Serat | +Kalori | +Skor SAW | +Detail | + +
|---|---|---|---|---|---|---|---|---|
|
+
+ @if($rank == 1)
+
+
+ 1
+
+
+ @elseif($rank == 2)
+
+
+ 2
+
+
+ @elseif($rank == 3)
+
+
+ 3
+
+
+ @else
+
+
+ {{ $rank }}
+
+
+ @endif
+
+ |
+
+
+
+
+
+
+
+
+ {{ $item['nama_makanan'] ?? '-' }}
+
+
+
+
+
+ {{ ucfirst($item['tekstur'] ?? '-') }}
+
+
+
+ {{ $item['cara_pengolahan'] ?? '-' }}
+
+
+
+
+ |
+
+
+
+ {{ number_format($item['protein'] ?? 0,1) }}g
+
+ |
+
+
+
+ {{ number_format($item['lemak'] ?? 0,1) }}g
+
+ |
+
+
+
+ {{ number_format($item['karbohidrat'] ?? 0,1) }}g
+
+ |
+
+
+
+ {{ number_format($item['serat'] ?? 0,1) }}g
+
+ |
+
+
+
+
+ {{ number_format($item['kalori'] ?? 0,0) }} kkal
+
+
+ |
+
+
+
+
+ {{ number_format($item['skor'] ?? 0,4) }}
+
+
+ |
+
+ + + + + | + +
|
+
+
+
+
+
+
+
+
+
+ + Informasi Makanan ++ +
+
+
+
+
+ Tekstur:
+ {{ ucfirst($item['tekstur'] ?? '-') }}
+
+
+
+ Porsi Saji Awal:
+ {{ $item['porsi_saji'] ?? '-' }}
+
+
+
+ Rekomendasi Jumlah Porsi:
+ {{ $jumlah_porsi }} Porsi
+
+
+
+ Ukuran Porsi:
+ {{ $ukuran_porsi }}
+
+
+
+ Cara Pengolahan:
+ {{ $item['cara_pengolahan'] ?? '-' }}
+
+
+ @if(!empty($item['alternatif_serupa']))
+
+
+ Alternatif:
+ {{ $item['alternatif_serupa'] }}
+
+
+ @endif
+
+
+
+
+
+ + Komposisi Makanan ++ + @if(!empty($item['komposisi_makanan'])) + +
+
+ @foreach(explode(',', $item['komposisi_makanan']) as $komposisi)
+
+
+ {{ trim($komposisi) }}
+
+
+ @endforeach
+
+
+
+ @else
+
+ + Tidak ada data komposisi. + + + @endif + + |
+
+ ||||||||
+ Halaman ini berisi tata cara penggunaan aplikasi + rekomendasi makanan sehat untuk penderita gastritis + menggunakan metode Simple Additive Weighting (SAW). +
++ Masuk menggunakan username dan password. +
++ Pengguna melakukan login untuk mengakses seluruh fitur sistem. +
+ ++ Tambahkan data diri anda. +
++ Input data diri meliputi umur, berat badan, + tinggi badan, gejala, dan aktivitas harian pasien. +
+ ++ Melakukan perhitungan rekomendasi. +
++ Sistem akan menghitung nilai SAW berdasarkan + kondisi pasien dan data makanan yang tersedia. +
+ ++ Menampilkan hasil ranking makanan. +
++ Sistem menampilkan rekomendasi makanan terbaik, + ranking makanan, nilai SAW, kebutuhan kalori, + dan detail nutrisi makanan. +
+ ++ Kelola informasi akun dan keamanan password +
++ Update informasi akun dan email Anda. +
++ Gunakan password yang aman untuk melindungi akun Anda. +
+ ++ Lengkapi data diri dan kondisi yang sedang dialami. + Sistem akan menghitung kebutuhan kalori serta memberikan + rekomendasi makanan menggunakan metode SAW. +
+ ++ Laravel has wonderful documentation covering every aspect of the framework. Whether you are a newcomer or have prior experience with Laravel, we recommend reading our documentation from beginning to end. +
++ Laracasts offers thousands of video tutorials on Laravel, PHP, and JavaScript development. Check them out, see for yourself, and massively level up your development skills in the process. +
++ Laravel News is a community driven portal and newsletter aggregating all of the latest and most important news in the Laravel ecosystem, including new package releases and tutorials. +
+