Fix Final

This commit is contained in:
dinda 2025-07-01 12:02:25 +07:00
parent 5699b9ccec
commit ed196ce89e
188 changed files with 31977 additions and 4 deletions

18
.editorconfig Normal file
View File

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

59
.env.example Normal file
View File

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

13
.gitattributes vendored
View File

@ -1,2 +1,11 @@
# Auto detect text files and perform LF normalization
* text=auto
* 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

19
.gitignore vendored Normal file
View File

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

View File

@ -1,2 +1,66 @@
# Coba
Coba
<p align="center"><a href="https://laravel.com" target="_blank"><img src="https://raw.githubusercontent.com/laravel/art/master/logo-lockup/5%20SVG/2%20CMYK/1%20Full%20Color/laravel-logolockup-cmyk-red.svg" width="400" alt="Laravel Logo"></a></p>
<p align="center">
<a href="https://github.com/laravel/framework/actions"><img src="https://github.com/laravel/framework/workflows/tests/badge.svg" alt="Build Status"></a>
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/dt/laravel/framework" alt="Total Downloads"></a>
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/v/laravel/framework" alt="Latest Stable Version"></a>
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/l/laravel/framework" alt="License"></a>
</p>
## 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).

27
app/Console/Kernel.php Normal file
View File

@ -0,0 +1,27 @@
<?php
namespace App\Console;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
class Kernel extends ConsoleKernel
{
/**
* Define the application's command schedule.
*/
protected function schedule(Schedule $schedule): void
{
// $schedule->command('inspire')->hourly();
}
/**
* Register the commands for the application.
*/
protected function commands(): void
{
$this->load(__DIR__.'/Commands');
require base_path('routes/console.php');
}
}

View File

@ -0,0 +1,30 @@
<?php
namespace App\Exceptions;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Throwable;
class Handler extends ExceptionHandler
{
/**
* The list of the inputs that are never flashed to the session on validation exceptions.
*
* @var array<int, string>
*/
protected $dontFlash = [
'current_password',
'password',
'password_confirmation',
];
/**
* Register the exception handling callbacks for the application.
*/
public function register(): void
{
$this->reportable(function (Throwable $e) {
//
});
}
}

View File

@ -0,0 +1,44 @@
<?php
namespace App\Exports;
use App\Models\Anak;
use Maatwebsite\Excel\Concerns\FromCollection;
use Maatwebsite\Excel\Concerns\WithHeadings;
use Maatwebsite\Excel\Concerns\WithMapping;
class AnakExport implements FromCollection, WithHeadings, WithMapping
{
public function collection()
{
return Anak::with('pengguna')->get();
}
public function headings(): array
{
return [
'NIK',
'Nama Anak',
'Nama Ibu',
'Tempat Lahir',
'Tanggal Lahir',
'Usia',
'Jenis Kelamin',
'Terdaftar Pada'
];
}
public function map($anak): array
{
return [
"'".$anak->pengguna->nik ?? '-',
$anak->nama_anak ?? '-',
$anak->pengguna->nama ?? '-',
$anak->tempat_lahir ?? '-',
$anak->tanggal_lahir ? $anak->tanggal_lahir->format('d/m/Y') : '-',
$anak->usia ?? '-',
$anak->jenis_kelamin ?? '-',
$anak->created_at->format('d/m/Y H:i')
];
}
}

View File

@ -0,0 +1,38 @@
<?php
namespace App\Exports;
use App\Models\Imunisasi;
use Maatwebsite\Excel\Concerns\FromCollection;
use Maatwebsite\Excel\Concerns\WithHeadings;
use Maatwebsite\Excel\Concerns\WithMapping;
class ImunisasiExport implements FromCollection, WithHeadings, WithMapping
{
public function collection()
{
return Imunisasi::with(['anak', 'jenisImunisasi'])->get();
}
public function headings(): array
{
return [
'Nama Anak',
'Tanggal Imunisasi',
'Jenis Imunisasi',
'Status',
'Terdaftar Pada'
];
}
public function map($imunisasi): array
{
return [
$imunisasi->anak->nama_anak ?? '-',
$imunisasi->tanggal ? $imunisasi->tanggal->format('d/m/Y') : '-',
$imunisasi->jenisImunisasi->nama ?? '-',
$imunisasi->status ?? '-',
$imunisasi->created_at->format('d/m/Y H:i')
];
}
}

View File

@ -0,0 +1,62 @@
<?php
namespace App\Exports;
use App\Models\Pengguna;
use Maatwebsite\Excel\Concerns\FromCollection;
use Maatwebsite\Excel\Concerns\WithHeadings;
use Maatwebsite\Excel\Concerns\WithMapping;
use Maatwebsite\Excel\Concerns\WithColumnFormatting;
use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
class PenggunaExport implements FromCollection, WithHeadings, WithMapping, WithColumnFormatting
{
public function collection()
{
return Pengguna::with('anak')->get();
}
public function headings(): array
{
return [
'NIK',
'Nama Ibu',
'Email',
'No. Telp',
'Alamat',
'Nama Anak',
'Usia Anak',
'Jenis Kelamin Anak',
'Tanggal Lahir Anak',
'Tempat Lahir Anak',
'Terdaftar Pada'
];
}
public function map($pengguna): array
{
$anak = $pengguna->anak->first();
return [
"'".$pengguna->nik,
$pengguna->nama,
$pengguna->email,
$pengguna->no_telp,
$pengguna->alamat,
$anak ? $anak->nama_anak : '-',
$anak ? $anak->usia : '-',
$anak ? $anak->jenis_kelamin : '-',
$anak ? $anak->tanggal_lahir->format('d/m/Y') : '-',
$anak ? $anak->tempat_lahir : '-',
$pengguna->created_at->format('d/m/Y H:i')
];
}
public function columnFormats(): array
{
return [
'A' => '@',
'D' => '@'
];
}
}

View File

@ -0,0 +1,42 @@
<?php
namespace App\Exports;
use App\Models\PerkembanganAnak;
use Maatwebsite\Excel\Concerns\FromCollection;
use Maatwebsite\Excel\Concerns\WithHeadings;
use Maatwebsite\Excel\Concerns\WithMapping;
class PerkembanganExport implements FromCollection, WithHeadings, WithMapping
{
public function collection()
{
return PerkembanganAnak::with(['anak', 'anak.pengguna'])->get();
}
public function headings(): array
{
return [
'Nama Anak',
'Nama Ibu',
'Tempat Lahir',
'Tanggal',
'Berat Badan (kg)',
'Tinggi Badan (cm)',
'Terdaftar Pada'
];
}
public function map($perkembangan): array
{
return [
$perkembangan->anak->nama_anak ?? '-',
$perkembangan->anak->pengguna->nama ?? '-',
$perkembangan->anak->tempat_lahir ?? '-',
$perkembangan->tanggal ? $perkembangan->tanggal->format('d/m/Y') : '-',
$perkembangan->berat_badan ?? '-',
$perkembangan->tinggi_badan ?? '-',
$perkembangan->created_at->format('d/m/Y H:i')
];
}
}

View File

@ -0,0 +1,44 @@
<?php
namespace App\Exports;
use App\Models\Stunting;
use Maatwebsite\Excel\Concerns\FromCollection;
use Maatwebsite\Excel\Concerns\WithHeadings;
use Maatwebsite\Excel\Concerns\WithMapping;
class StuntingExport implements FromCollection, WithHeadings, WithMapping
{
public function collection()
{
return Stunting::with(['anak', 'perkembangan'])->get();
}
public function headings(): array
{
return [
'Nama Anak',
'Tanggal Pemeriksaan',
'Usia',
'Tinggi Badan',
'Berat Badan',
'Status',
'Keterangan',
'Terdaftar Pada'
];
}
public function map($stunting): array
{
return [
$stunting->anak->nama_anak ?? '-',
$stunting->tanggal ? $stunting->tanggal->format('d/m/Y') : '-',
$stunting->usia ?? '-',
($stunting->perkembangan->tinggi_badan ?? $stunting->tinggi_badan) . ' cm',
($stunting->perkembangan->berat_badan ?? $stunting->berat_badan) . ' kg',
$stunting->status ?? '-',
$stunting->catatan ?? '-',
$stunting->created_at->format('d/m/Y H:i')
];
}
}

View File

@ -0,0 +1,38 @@
<?php
namespace App\Exports;
use App\Models\Vitamin;
use Maatwebsite\Excel\Concerns\FromCollection;
use Maatwebsite\Excel\Concerns\WithHeadings;
use Maatwebsite\Excel\Concerns\WithMapping;
class VitaminExport implements FromCollection, WithHeadings, WithMapping
{
public function collection()
{
return Vitamin::with(['anak', 'jenisVitamin'])->get();
}
public function headings(): array
{
return [
'Nama Anak',
'Tanggal Vitamin',
'Jenis Vitamin',
'Status',
'Terdaftar Pada'
];
}
public function map($vitamin): array
{
return [
$vitamin->anak->nama_anak ?? '-',
$vitamin->tanggal ? $vitamin->tanggal->format('d/m/Y') : '-',
$vitamin->jenisVitamin->nama ?? '-',
$vitamin->status ?? '-',
$vitamin->created_at->format('d/m/Y H:i')
];
}
}

View File

@ -0,0 +1,189 @@
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Models\Anak;
use App\Models\Pengguna;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
class AnakApiController extends Controller
{
/**
* Get all children for a specific parent
*/
public function getByPenggunaId($pengguna_id)
{
$pengguna = Pengguna::find($pengguna_id);
if (!$pengguna) {
return response()->json([
'status' => 'error',
'message' => 'Data pengguna tidak ditemukan',
], 404);
}
$anak = Anak::where('pengguna_id', $pengguna_id)->get();
// Calculate age in months for each child
$anak->each(function ($item) {
$item->usia_bulan = $this->hitungUsiaBulan($item->tanggal_lahir);
});
return response()->json([
'status' => 'success',
'anak' => $anak,
]);
}
/**
* Get a specific child record
*/
public function show($id)
{
$anak = Anak::find($id);
if (!$anak) {
return response()->json([
'status' => 'error',
'message' => 'Data anak tidak ditemukan',
], 404);
}
// Calculate age in months
$anak->usia_bulan = $this->hitungUsiaBulan($anak->tanggal_lahir);
return response()->json([
'status' => 'success',
'anak' => $anak,
]);
}
/**
* Create a new child record
*/
public function store(Request $request)
{
$validator = Validator::make($request->all(), [
'pengguna_id' => 'required|exists:pengguna,id',
'nama_anak' => 'required|string|max:100',
'tempat_lahir' => 'required|string|max:100',
'tanggal_lahir' => 'required|date',
'jenis_kelamin' => 'required|in:Laki-laki,Perempuan',
]);
if ($validator->fails()) {
return response()->json([
'status' => 'error',
'message' => 'Validation error',
'errors' => $validator->errors()
], 422);
}
// Calculate age based on birth date
$birthDate = new \DateTime($request->tanggal_lahir);
$today = new \DateTime('today');
$interval = $birthDate->diff($today);
$usia = $interval->y . ' tahun ' . $interval->m . ' bulan';
// Add age to request data
$data = $request->all();
$data['usia'] = $usia;
$anak = Anak::create($data);
// Calculate age in months
$anak->usia_bulan = $this->hitungUsiaBulan($anak->tanggal_lahir);
return response()->json([
'status' => 'success',
'message' => 'Data anak berhasil ditambahkan',
'anak' => $anak,
], 201);
}
/**
* Update a child record
*/
public function update(Request $request, $id)
{
$anak = Anak::find($id);
if (!$anak) {
return response()->json([
'status' => 'error',
'message' => 'Data anak tidak ditemukan',
], 404);
}
$validator = Validator::make($request->all(), [
'nama_anak' => 'required|string|max:100',
'tempat_lahir' => 'required|string|max:100',
'tanggal_lahir' => 'required|date',
'jenis_kelamin' => 'required|in:Laki-laki,Perempuan',
]);
if ($validator->fails()) {
return response()->json([
'status' => 'error',
'message' => 'Validation error',
'errors' => $validator->errors()
], 422);
}
// Calculate age based on birth date
$birthDate = new \DateTime($request->tanggal_lahir);
$today = new \DateTime('today');
$interval = $birthDate->diff($today);
$usia = $interval->y . ' tahun ' . $interval->m . ' bulan';
// Add age to request data
$data = $request->all();
$data['usia'] = $usia;
$anak->update($data);
// Calculate age in months
$anak->usia_bulan = $this->hitungUsiaBulan($anak->tanggal_lahir);
return response()->json([
'status' => 'success',
'message' => 'Data anak berhasil diperbarui',
'anak' => $anak,
]);
}
/**
* Delete a child record
*/
public function destroy($id)
{
$anak = Anak::find($id);
if (!$anak) {
return response()->json([
'status' => 'error',
'message' => 'Data anak tidak ditemukan',
], 404);
}
$anak->delete();
return response()->json([
'status' => 'success',
'message' => 'Data anak berhasil dihapus',
]);
}
// Helper method to calculate age in months
private function hitungUsiaBulan($tanggal_lahir)
{
if (!$tanggal_lahir) return 0;
$birthDate = new \DateTime($tanggal_lahir);
$today = new \DateTime('today');
$interval = $birthDate->diff($today);
return ($interval->y * 12) + $interval->m;
}
}

View File

@ -0,0 +1,496 @@
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Models\Anak;
use App\Models\Pengguna;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Facades\Auth;
class AnakController extends Controller
{
/**
* Mendapatkan daftar anak
*/
public function index(Request $request)
{
$user = Auth::user();
// Log untuk debugging
\Log::info("=== Get anak data ===");
\Log::info("Headers: " . json_encode($request->headers->all()));
\Log::info("User: " . ($user ? "ID: {$user->id}, NIK: {$user->nik}, Role: {$user->role}" : "Not authenticated"));
// Jika tidak ada user, return error
if (!$user) {
return response()->json([
'status' => 'error',
'message' => 'Tidak dapat mengidentifikasi pengguna'
], 401);
}
// Default query
$query = Anak::with('pengguna');
if ($user->role === 'parent') {
// Jika user adalah parent, hanya tampilkan anak miliknya
$query->where('pengguna_id', $user->id);
\Log::info("Filtering: only showing children for parent ID {$user->id}");
} else if ($request->has('pengguna_id') && $user->role === 'admin') {
// Admin bisa filter berdasarkan pengguna_id
$query->where('pengguna_id', $request->pengguna_id);
\Log::info("Admin filtering: showing children for parent ID {$request->pengguna_id}");
}
$anakList = $query->get();
\Log::info("Found {$anakList->count()} children records");
return response()->json([
'status' => 'success',
'data' => $anakList
]);
}
/**
* Menyimpan data anak baru
*/
public function store(Request $request)
{
$user = Auth::user();
// Debug headers dan user
\Log::info("=== Store anak data ===");
\Log::info("Headers: " . json_encode($request->headers->all()));
\Log::info("User: " . ($user ? "ID: {$user->id}, NIK: {$user->nik}, Role: {$user->role}" : "Not authenticated"));
\Log::info("Request data:", $request->all());
// Jika masih tidak ada user setelah melewati middleware, return error
if (!$user) {
return response()->json([
'status' => 'error',
'message' => 'Tidak dapat mengidentifikasi pengguna'
], 401);
}
// Validasi input
$validator = Validator::make($request->all(), [
'pengguna_id' => 'sometimes|exists:pengguna,id',
'nama_anak' => 'required|string|max:100',
'tempat_lahir' => 'required|string|max:100',
'tanggal_lahir' => 'required|date',
'jenis_kelamin' => 'required|in:Laki-laki,Perempuan',
'usia' => 'nullable|string|max:20',
]);
if ($validator->fails()) {
return response()->json([
'status' => 'error',
'message' => 'Validasi gagal',
'errors' => $validator->errors()
], 422);
}
try {
// Tentukan pengguna_id
$pengguna_id = null;
if ($user->role === 'parent') {
$pengguna_id = $user->id;
} else if ($user->role === 'admin' && $request->has('pengguna_id')) {
$pengguna_id = $request->pengguna_id;
} else if ($request->has('pengguna_id')) {
// Fallback: gunakan dari request
$pengguna_id = $request->pengguna_id;
}
$anak = new Anak();
$anak->pengguna_id = $pengguna_id;
$anak->nama_anak = $request->nama_anak;
$anak->tempat_lahir = $request->tempat_lahir;
$anak->tanggal_lahir = $request->tanggal_lahir;
$anak->jenis_kelamin = $request->jenis_kelamin;
$anak->usia = $request->usia;
$anak->save();
// Load data pengguna
$anak->load('pengguna');
return response()->json([
'status' => 'success',
'message' => 'Data anak berhasil disimpan',
'data' => $anak
], 201);
} catch (\Exception $e) {
\Log::error("Error: {$e->getMessage()}");
return response()->json([
'status' => 'error',
'message' => 'Gagal menyimpan data',
'error' => $e->getMessage()
], 500);
}
}
/**
* Mendapatkan detail anak
*/
public function show($id)
{
$user = Auth::user();
// Log untuk debugging
\Log::info("=== Show anak data ===");
\Log::info("User: " . ($user ? "ID: {$user->id}, NIK: {$user->nik}, Role: {$user->role}" : "Not authenticated"));
\Log::info("Anak ID: {$id}");
// Jika tidak ada user, return error
if (!$user) {
return response()->json([
'status' => 'error',
'message' => 'Tidak dapat mengidentifikasi pengguna'
], 401);
}
$anak = Anak::with('pengguna')->find($id);
if (!$anak) {
return response()->json([
'status' => 'error',
'message' => 'Data anak tidak ditemukan'
], 404);
}
// Verifikasi akses
if ($user->role !== 'admin' && $anak->pengguna_id !== $user->id) {
return response()->json([
'status' => 'error',
'message' => 'Anda tidak memiliki akses ke data ini'
], 403);
}
return response()->json([
'status' => 'success',
'data' => $anak
]);
}
/**
* Mengupdate data anak
*/
public function update(Request $request, $id)
{
$user = Auth::user();
// Log untuk debugging
\Log::info("=== Update anak data ===");
\Log::info("Headers: " . json_encode($request->headers->all()));
\Log::info("User: " . ($user ? "ID: {$user->id}, NIK: {$user->nik}, Role: {$user->role}" : "Not authenticated"));
\Log::info("Child ID: {$id}");
\Log::info("Request data:", $request->all());
// Jika tidak ada user, return error
if (!$user) {
return response()->json([
'status' => 'error',
'message' => 'Tidak dapat mengidentifikasi pengguna'
], 401);
}
$anak = Anak::find($id);
if (!$anak) {
return response()->json([
'status' => 'error',
'message' => 'Data anak tidak ditemukan'
], 404);
}
// Verifikasi akses
if ($user->role !== 'admin' && $anak->pengguna_id !== $user->id) {
return response()->json([
'status' => 'error',
'message' => 'Anda tidak memiliki akses untuk mengubah data ini'
], 403);
}
$validator = Validator::make($request->all(), [
'nama_anak' => 'required|string|max:100',
'tempat_lahir' => 'required|string|max:100',
'tanggal_lahir' => 'required|date',
'jenis_kelamin' => 'required|in:Laki-laki,Perempuan',
'usia' => 'nullable|string|max:20',
]);
if ($validator->fails()) {
return response()->json([
'status' => 'error',
'message' => 'Validasi gagal',
'errors' => $validator->errors()
], 422);
}
try {
// Admin bisa mengubah pengguna_id
if ($user->role === 'admin' && $request->has('pengguna_id')) {
$anak->pengguna_id = $request->pengguna_id;
}
$anak->nama_anak = $request->nama_anak;
$anak->tempat_lahir = $request->tempat_lahir;
$anak->tanggal_lahir = $request->tanggal_lahir;
$anak->jenis_kelamin = $request->jenis_kelamin;
$anak->usia = $request->usia;
$anak->save();
// Load data pengguna
$anak->load('pengguna');
return response()->json([
'status' => 'success',
'message' => 'Data anak berhasil diperbarui',
'data' => $anak
]);
} catch (\Exception $e) {
\Log::error("Error: {$e->getMessage()}");
return response()->json([
'status' => 'error',
'message' => 'Gagal memperbarui data',
'error' => $e->getMessage()
], 500);
}
}
/**
* Menghapus data anak
*/
public function destroy($id)
{
$user = Auth::user();
// Log untuk debugging
\Log::info("=== Delete anak data ===");
\Log::info("User: " . ($user ? "ID: {$user->id}, NIK: {$user->nik}, Role: {$user->role}" : "Not authenticated"));
\Log::info("Anak ID: {$id}");
// Jika tidak ada user, return error
if (!$user) {
return response()->json([
'status' => 'error',
'message' => 'Tidak dapat mengidentifikasi pengguna'
], 401);
}
$anak = Anak::find($id);
if (!$anak) {
return response()->json([
'status' => 'error',
'message' => 'Data anak tidak ditemukan'
], 404);
}
// Verifikasi akses
if ($user->role !== 'admin' && $anak->pengguna_id !== $user->id) {
return response()->json([
'status' => 'error',
'message' => 'Anda tidak memiliki akses untuk menghapus data ini'
], 403);
}
try {
$anak->delete();
return response()->json([
'status' => 'success',
'message' => 'Data anak berhasil dihapus'
]);
} catch (\Exception $e) {
\Log::error("Error: {$e->getMessage()}");
return response()->json([
'status' => 'error',
'message' => 'Gagal menghapus data',
'error' => $e->getMessage()
], 500);
}
}
/**
* Mencari anak berdasarkan NIK orang tua
*/
public function findByPenggunaNik($nik)
{
$user = Auth::user();
// Log untuk debugging
\Log::info("=== Find anak by NIK ===");
\Log::info("User: " . ($user ? "ID: {$user->id}, NIK: {$user->nik}, Role: {$user->role}" : "Not authenticated"));
\Log::info("Requested NIK: {$nik}");
// Jika tidak ada user, return error
if (!$user) {
return response()->json([
'status' => 'error',
'message' => 'Tidak dapat mengidentifikasi pengguna'
], 401);
}
// Verifikasi akses - hanya admin atau pemilik NIK yang boleh mengakses
if ($user->role !== 'admin' && $user->nik !== $nik) {
return response()->json([
'status' => 'error',
'message' => 'Anda tidak memiliki akses ke data ini'
], 403);
}
try {
$pengguna = Pengguna::where('nik', $nik)->first();
if (!$pengguna) {
return response()->json([
'status' => 'error',
'message' => 'Pengguna dengan NIK tersebut tidak ditemukan'
], 404);
}
$anakList = Anak::where('pengguna_id', $pengguna->id)->get();
return response()->json([
'status' => 'success',
'data' => $anakList
]);
} catch (\Exception $e) {
\Log::error("Error: {$e->getMessage()}");
return response()->json([
'status' => 'error',
'message' => 'Terjadi kesalahan',
'error' => $e->getMessage()
], 500);
}
}
/**
* Menghubungkan anak dengan orang tua
*/
public function linkToParent(Request $request)
{
$user = Auth::user();
// Log untuk debugging
\Log::info("=== Link anak to parent ===");
\Log::info("Headers: " . json_encode($request->headers->all()));
\Log::info("User: " . ($user ? "ID: {$user->id}, NIK: {$user->nik}, Role: {$user->role}" : "Not authenticated"));
\Log::info("Request data:", $request->all());
// Jika tidak ada user, return error
if (!$user) {
return response()->json([
'status' => 'error',
'message' => 'Tidak dapat mengidentifikasi pengguna'
], 401);
}
$validator = Validator::make($request->all(), [
'anak_id' => 'required|exists:anak,id',
'nik' => 'required|exists:pengguna,nik',
]);
if ($validator->fails()) {
return response()->json([
'status' => 'error',
'message' => 'Validasi gagal',
'errors' => $validator->errors()
], 422);
}
try {
$pengguna = Pengguna::where('nik', $request->nik)->first();
$anak = Anak::find($request->anak_id);
// Verifikasi akses
if ($user->role !== 'admin') {
return response()->json([
'status' => 'error',
'message' => 'Hanya admin yang dapat menautkan data anak'
], 403);
}
// Verifikasi pengguna adalah parent
if ($pengguna->role !== 'parent') {
return response()->json([
'status' => 'error',
'message' => 'NIK yang diberikan bukan milik orang tua'
], 400);
}
$anak->pengguna_id = $pengguna->id;
$anak->save();
return response()->json([
'status' => 'success',
'message' => 'Data anak berhasil dikaitkan dengan orang tua',
'data' => [
'anak' => $anak,
'pengguna' => [
'id' => $pengguna->id,
'nik' => $pengguna->nik,
'nama' => $pengguna->nama
]
]
]);
} catch (\Exception $e) {
\Log::error("Error: {$e->getMessage()}");
return response()->json([
'status' => 'error',
'message' => 'Gagal mengaitkan data',
'error' => $e->getMessage()
], 500);
}
}
/**
* Mendapatkan anak berdasarkan ID pengguna
*/
public function getAnakByPenggunaId($pengguna_id)
{
$user = Auth::user();
// Log untuk debugging
\Log::info("=== Get anak by pengguna ID ===");
\Log::info("User: " . ($user ? "ID: {$user->id}, NIK: {$user->nik}, Role: {$user->role}" : "Not authenticated"));
\Log::info("Requested pengguna_id: {$pengguna_id}");
// Jika tidak ada user, return error
if (!$user) {
return response()->json([
'status' => 'error',
'message' => 'Tidak dapat mengidentifikasi pengguna'
], 401);
}
// Verifikasi akses
if ($user->role !== 'admin' && $user->id != $pengguna_id) {
return response()->json([
'status' => 'error',
'message' => 'Anda tidak memiliki akses ke data ini'
], 403);
}
try {
$anakList = Anak::where('pengguna_id', $pengguna_id)->get();
\Log::info("Found {$anakList->count()} children");
return response()->json([
'status' => 'success',
'data' => $anakList
]);
} catch (\Exception $e) {
\Log::error("Error: {$e->getMessage()}");
return response()->json([
'status' => 'error',
'message' => 'Terjadi kesalahan',
'error' => $e->getMessage()
], 500);
}
}
}

View File

@ -0,0 +1,271 @@
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Models\Artikel;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Validator;
class ArtikelApiController extends Controller
{
/**
* Menampilkan daftar artikel dengan pagination
*
* @param Request $request
* @return \Illuminate\Http\JsonResponse
*/
public function index(Request $request)
{
$perPage = $request->input('per_page', 10);
$search = $request->input('search');
$query = Artikel::orderBy('tanggal', 'desc')
->orderBy('created_at', 'desc');
// Filter berdasarkan pencarian jika ada
if ($search) {
$query->where('judul', 'like', "%{$search}%")
->orWhere('isi_artikel', 'like', "%{$search}%");
}
$artikels = $query->paginate($perPage);
// Tambahkan URL lengkap untuk gambar
$artikels->getCollection()->transform(function ($artikel) {
if ($artikel->gambar_artikel) {
$artikel->gambar_url = url('storage/artikel/' . $artikel->gambar_artikel);
}
return $artikel;
});
return response()->json([
'status' => 'success',
'message' => 'Daftar artikel berhasil diambil',
'data' => $artikels
]);
}
/**
* Menampilkan detail artikel
*
* @param int $id
* @return \Illuminate\Http\JsonResponse
*/
public function show($id)
{
try {
$artikel = Artikel::findOrFail($id);
// Tambahkan URL lengkap untuk gambar
if ($artikel->gambar_artikel) {
$artikel->gambar_url = url('storage/artikel/' . $artikel->gambar_artikel);
}
return response()->json([
'status' => 'success',
'message' => 'Detail artikel berhasil diambil',
'data' => $artikel
]);
} catch (\Exception $e) {
return response()->json([
'status' => 'error',
'message' => 'Artikel tidak ditemukan',
'error' => $e->getMessage()
], 404);
}
}
/**
* Menyimpan artikel baru
*
* @param Request $request
* @return \Illuminate\Http\JsonResponse
*/
public function store(Request $request)
{
// Validasi input
$validator = Validator::make($request->all(), [
'judul' => 'required|string|max:255',
'gambar_artikel' => 'required|image|mimes:jpeg,png,jpg,gif|max:2048',
'isi_artikel' => 'required|string',
'tanggal' => 'required|date',
]);
if ($validator->fails()) {
return response()->json([
'status' => 'error',
'message' => 'Validasi gagal',
'errors' => $validator->errors()
], 422);
}
try {
// Upload gambar
$imagePath = null;
if ($request->hasFile('gambar_artikel')) {
$imagePath = $request->file('gambar_artikel')->store('artikel', 'public');
$imagePath = basename($imagePath);
}
// Buat artikel baru
$artikel = Artikel::create([
'judul' => $request->judul,
'gambar_artikel' => $imagePath,
'isi_artikel' => $request->isi_artikel,
'tanggal' => $request->tanggal,
]);
// Tambahkan URL lengkap untuk gambar
if ($artikel->gambar_artikel) {
$artikel->gambar_url = url('storage/artikel/' . $artikel->gambar_artikel);
}
return response()->json([
'status' => 'success',
'message' => 'Artikel berhasil ditambahkan',
'data' => $artikel
], 201);
} catch (\Exception $e) {
return response()->json([
'status' => 'error',
'message' => 'Gagal menambahkan artikel',
'error' => $e->getMessage()
], 500);
}
}
/**
* Mengupdate artikel
*
* @param Request $request
* @param int $id
* @return \Illuminate\Http\JsonResponse
*/
public function update(Request $request, $id)
{
// Validasi input
$validator = Validator::make($request->all(), [
'judul' => 'required|string|max:255',
'gambar_artikel' => 'nullable|image|mimes:jpeg,png,jpg,gif|max:2048',
'isi_artikel' => 'required|string',
'tanggal' => 'required|date',
]);
if ($validator->fails()) {
return response()->json([
'status' => 'error',
'message' => 'Validasi gagal',
'errors' => $validator->errors()
], 422);
}
try {
$artikel = Artikel::findOrFail($id);
// Upload gambar baru jika ada
if ($request->hasFile('gambar_artikel')) {
// Hapus gambar lama jika ada
if ($artikel->gambar_artikel) {
$oldImagePath = 'public/artikel/' . $artikel->gambar_artikel;
if (Storage::exists($oldImagePath)) {
Storage::delete($oldImagePath);
}
}
// Upload gambar baru
$imagePath = $request->file('gambar_artikel')->store('artikel', 'public');
$artikel->gambar_artikel = basename($imagePath);
}
// Update data artikel
$artikel->judul = $request->judul;
$artikel->isi_artikel = $request->isi_artikel;
$artikel->tanggal = $request->tanggal;
$artikel->save();
// Tambahkan URL lengkap untuk gambar
if ($artikel->gambar_artikel) {
$artikel->gambar_url = url('storage/artikel/' . $artikel->gambar_artikel);
}
return response()->json([
'status' => 'success',
'message' => 'Artikel berhasil diperbarui',
'data' => $artikel
]);
} catch (\Exception $e) {
return response()->json([
'status' => 'error',
'message' => 'Artikel tidak ditemukan atau gagal diperbarui',
'error' => $e->getMessage()
], 404);
}
}
/**
* Menghapus artikel
*
* @param int $id
* @return \Illuminate\Http\JsonResponse
*/
public function destroy($id)
{
try {
$artikel = Artikel::findOrFail($id);
// Hapus gambar jika ada
if ($artikel->gambar_artikel) {
$imagePath = 'public/artikel/' . $artikel->gambar_artikel;
if (Storage::exists($imagePath)) {
Storage::delete($imagePath);
}
}
// Hapus artikel
$artikel->delete();
return response()->json([
'status' => 'success',
'message' => 'Artikel berhasil dihapus'
]);
} catch (\Exception $e) {
return response()->json([
'status' => 'error',
'message' => 'Artikel tidak ditemukan atau gagal dihapus',
'error' => $e->getMessage()
], 404);
}
}
/**
* Menampilkan artikel terbaru
*
* @param Request $request
* @return \Illuminate\Http\JsonResponse
*/
public function latest(Request $request)
{
$limit = $request->input('limit', 5);
$artikels = Artikel::orderBy('tanggal', 'desc')
->orderBy('created_at', 'desc')
->limit($limit)
->get();
// Tambahkan URL lengkap untuk gambar
$artikels->transform(function ($artikel) {
if ($artikel->gambar_artikel) {
$artikel->gambar_url = url('storage/artikel/' . $artikel->gambar_artikel);
}
return $artikel;
});
return response()->json([
'status' => 'success',
'message' => 'Artikel terbaru berhasil diambil',
'data' => $artikels
]);
}
}

View File

@ -0,0 +1,415 @@
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Models\Pengguna;
use App\Models\Anak;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Facades\DB;
class AuthController extends Controller
{
public function login(Request $request)
{
$validator = Validator::make($request->all(), [
'nik' => 'required|string',
'password' => 'required|string',
]);
if ($validator->fails()) {
return response()->json([
'status' => 'error',
'message' => 'Validation error',
'errors' => $validator->errors()
], 422);
}
$pengguna = Pengguna::where('nik', $request->nik)->first();
if (!$pengguna || !Hash::check($request->password, $pengguna->password)) {
return response()->json([
'status' => 'error',
'message' => 'NIK atau password salah',
], 401);
}
// Check if this is a mobile request (default) or explicitly stated
$isMobile = $request->has('platform') ? $request->platform === 'mobile' : true;
// Check role permissions
if ($isMobile && $pengguna->role !== 'parent') {
return response()->json([
'status' => 'error',
'message' => 'Akun ini tidak memiliki akses mobile. Silakan gunakan aplikasi web.',
], 403);
}
if (!$isMobile && $pengguna->role !== 'admin') {
return response()->json([
'status' => 'error',
'message' => 'Akun ini tidak memiliki akses admin. Silakan gunakan aplikasi mobile.',
], 403);
}
// Load anak data for the user if parent
if ($pengguna->role === 'parent') {
$pengguna->load('anak');
}
// Generate token using Sanctum with role in the token name
$tokenName = $pengguna->role . '_auth_token';
$token = $pengguna->createToken($tokenName)->plainTextToken;
// Prepare response data based on role
if ($pengguna->role === 'parent') {
// Process anak data to match mobile app expectations
$anakData = [];
if ($pengguna->anak && $pengguna->anak->count() > 0) {
foreach ($pengguna->anak as $anak) {
$anakData[] = [
'id' => $anak->id,
'nama' => $anak->nama_anak,
'usia_bulan' => $this->hitungUsiaBulan($anak->tanggal_lahir),
'jenis_kelamin' => $anak->jenis_kelamin,
];
}
}
return response()->json([
'status' => 'success',
'message' => 'Login berhasil',
'token' => $token,
'pengguna' => [
'id' => $pengguna->id,
'nik' => $pengguna->nik,
'nama_ibu' => $pengguna->nama,
'alamat' => $pengguna->alamat,
'usia' => null,
'role' => $pengguna->role,
'anak' => $anakData,
],
]);
} else {
// Admin response
return response()->json([
'status' => 'success',
'message' => 'Login berhasil',
'token' => $token,
'pengguna' => [
'id' => $pengguna->id,
'nik' => $pengguna->nik,
'nama' => $pengguna->nama,
'email' => $pengguna->email,
'role' => $pengguna->role,
],
]);
}
}
public function logout(Request $request)
{
// Revoke token
if ($request->user()) {
$request->user()->currentAccessToken()->delete();
}
return response()->json([
'status' => 'success',
'message' => 'Logout berhasil',
]);
}
// Helper method to calculate age in months
private function hitungUsiaBulan($tanggal_lahir)
{
if (!$tanggal_lahir) return 0;
$birthDate = new \DateTime($tanggal_lahir);
$today = new \DateTime('today');
$interval = $birthDate->diff($today);
return ($interval->y * 12) + $interval->m;
}
public function getUser(Request $request, $nik)
{
$pengguna = Pengguna::where('nik', $nik)->first();
if (!$pengguna) {
return response()->json([
'status' => 'error',
'message' => 'Pengguna tidak ditemukan',
], 404);
}
// Check if user is parent before loading anak data
if ($pengguna->role === 'parent') {
// Load anak data for the user
$pengguna->load('anak');
// Process anak data to match mobile app expectations
$anakData = [];
if ($pengguna->anak && $pengguna->anak->count() > 0) {
foreach ($pengguna->anak as $anak) {
$anakData[] = [
'id' => $anak->id,
'nama' => $anak->nama_anak,
'usia_bulan' => $this->hitungUsiaBulan($anak->tanggal_lahir),
'jenis_kelamin' => $anak->jenis_kelamin,
];
}
}
return response()->json([
'status' => 'success',
'pengguna' => [
'id' => $pengguna->id,
'nik' => $pengguna->nik,
'nama_ibu' => $pengguna->nama,
'alamat' => $pengguna->alamat,
'usia' => null,
'role' => $pengguna->role,
'anak' => $anakData,
],
]);
} else {
// Admin response
return response()->json([
'status' => 'success',
'pengguna' => [
'id' => $pengguna->id,
'nik' => $pengguna->nik,
'nama' => $pengguna->nama,
'email' => $pengguna->email,
'role' => $pengguna->role,
],
]);
}
}
/**
* Get currently authenticated user information
*/
public function user(Request $request)
{
$user = $request->user();
if (!$user) {
return response()->json([
'status' => 'error',
'message' => 'Unauthenticated',
], 401);
}
// Check if user is parent before loading anak data
if ($user->role === 'parent') {
// Load anak data for the user
$user->load('anak');
// Process anak data to match mobile app expectations
$anakData = [];
if ($user->anak && $user->anak->count() > 0) {
foreach ($user->anak as $anak) {
$anakData[] = [
'id' => $anak->id,
'nama' => $anak->nama_anak,
'usia_bulan' => $this->hitungUsiaBulan($anak->tanggal_lahir),
'jenis_kelamin' => $anak->jenis_kelamin,
];
}
}
return response()->json([
'status' => 'success',
'pengguna' => [
'id' => $user->id,
'nik' => $user->nik,
'nama_ibu' => $user->nama,
'alamat' => $user->alamat,
'no_telp' => $user->no_telp,
'email' => $user->email,
'role' => $user->role,
'anak' => $anakData,
],
]);
} else {
// Admin response
return response()->json([
'status' => 'success',
'pengguna' => [
'id' => $user->id,
'nik' => $user->nik,
'nama' => $user->nama,
'email' => $user->email,
'no_telp' => $user->no_telp,
'alamat' => $user->alamat,
'role' => $user->role,
],
]);
}
}
public function register(Request $request)
{
$validator = Validator::make($request->all(), [
'nik' => 'required|string|unique:pengguna,nik',
'nama' => 'required|string|max:100',
'alamat' => 'nullable|string',
'no_telp' => 'nullable|string|max:15',
'password' => 'required|string|min:6',
'email' => 'nullable|email|unique:pengguna,email',
]);
if ($validator->fails()) {
return response()->json([
'status' => 'error',
'message' => 'Validation error',
'errors' => $validator->errors()
], 422);
}
$pengguna = Pengguna::create([
'nik' => $request->nik,
'nama' => $request->nama,
'alamat' => $request->alamat,
'no_telp' => $request->no_telp,
'password' => Hash::make($request->password),
'email' => $request->email,
'role' => 'parent',
]);
// Generate token for immediate login
$tokenName = 'parent_auth_token';
$token = $pengguna->createToken($tokenName)->plainTextToken;
return response()->json([
'status' => 'success',
'message' => 'Pendaftaran berhasil',
'token' => $token,
'pengguna' => [
'id' => $pengguna->id,
'nik' => $pengguna->nik,
'nama_ibu' => $pengguna->nama,
'alamat' => $pengguna->alamat,
'no_telp' => $pengguna->no_telp,
'email' => $pengguna->email,
'role' => $pengguna->role,
],
]);
}
public function update(Request $request, $id)
{
try {
// Enable query logging
DB::enableQueryLog();
// Debug untuk melihat ID yang diterima
\Log::info('Attempting to update user with ID: ' . $id);
// Cari user dengan ID dan log query-nya
$user = \App\Models\Pengguna::where('id', $id)->first();
\Log::info('SQL Query: ' . json_encode(DB::getQueryLog()));
if (!$user) {
\Log::error('User not found with ID: ' . $id . ' in table pengguna');
// Debug: cek isi tabel
$allUsers = DB::table('pengguna')->get();
\Log::info('All users in database: ' . json_encode($allUsers));
return response()->json([
'status' => 'error',
'message' => 'User not found with ID: ' . $id
], 404);
}
\Log::info('Found user: ' . json_encode($user));
// Validasi input
$request->validate([
'nama_ibu' => 'required|string|max:255',
'email' => 'required|email|unique:pengguna,email,' . $id,
'no_telp' => 'required|string|max:15',
'alamat' => 'required|string',
'nik' => 'required|string|size:16|unique:pengguna,nik,' . $id,
]);
// Update data user
$updated = $user->update([
'nama' => $request->nama_ibu, // Menggunakan 'nama' karena di model Pengguna fieldnya adalah 'nama'
'email' => $request->email,
'no_telp' => $request->no_telp,
'alamat' => $request->alamat,
'nik' => $request->nik,
]);
\Log::info('Update result: ' . ($updated ? 'success' : 'failed'));
\Log::info('Update query: ' . json_encode(DB::getQueryLog()));
if (!$updated) {
throw new \Exception('Failed to update user data');
}
// Get fresh data after update
$user->refresh();
// Load anak data if user is parent
if ($user->role === 'parent') {
$user->load('anak');
// Process anak data to match mobile app expectations
$anakData = [];
if ($user->anak && $user->anak->count() > 0) {
foreach ($user->anak as $anak) {
$anakData[] = [
'id' => $anak->id,
'nama' => $anak->nama_anak,
'usia_bulan' => $this->hitungUsiaBulan($anak->tanggal_lahir),
'jenis_kelamin' => $anak->jenis_kelamin,
];
}
}
return response()->json([
'status' => 'success',
'message' => 'Profile updated successfully',
'pengguna' => [
'id' => $user->id,
'nik' => $user->nik,
'nama_ibu' => $user->nama,
'alamat' => $user->alamat,
'no_telp' => $user->no_telp,
'email' => $user->email,
'role' => $user->role,
'anak' => $anakData,
]
]);
}
return response()->json([
'status' => 'success',
'message' => 'Profile updated successfully',
'pengguna' => [
'id' => $user->id,
'nik' => $user->nik,
'nama' => $user->nama,
'email' => $user->email,
'no_telp' => $user->no_telp,
'alamat' => $user->alamat,
'role' => $user->role,
]
]);
} catch (\Exception $e) {
\Log::error('Error updating user: ' . $e->getMessage());
\Log::error('Stack trace: ' . $e->getTraceAsString());
return response()->json([
'status' => 'error',
'message' => $e->getMessage()
], 500);
}
}
}

View File

@ -0,0 +1,515 @@
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use App\Models\Imunisasi;
use App\Models\JadwalImunisasi;
use App\Models\Anak;
use App\Models\JenisImunisasi;
use Illuminate\Support\Facades\Validator;
use Carbon\Carbon;
class ImunisasiApiController extends Controller
{
/**
* Get all imunisasi records with filtering options
*/
public function index(Request $request)
{
$query = Imunisasi::with(['anak', 'jenisImunisasi']);
// Filter by anak_id if provided
if ($request->has('anak_id')) {
$query->where('anak_id', $request->anak_id);
}
// Filter by status if provided
if ($request->has('status')) {
$query->where('status', $request->status);
}
// Get paginated results or all if limit=0
if ($request->has('limit') && $request->limit > 0) {
$imunisasi = $query->orderBy('tanggal', 'desc')->paginate($request->limit);
} else {
$imunisasi = $query->orderBy('tanggal', 'desc')->get();
}
return response()->json([
'status' => 'success',
'data' => $imunisasi
]);
}
/**
* Get an imunisasi record by ID
*/
public function show($id)
{
$imunisasi = Imunisasi::with(['anak', 'jenisImunisasi'])->find($id);
if (!$imunisasi) {
return response()->json([
'status' => 'error',
'message' => 'Data imunisasi tidak ditemukan'
], 404);
}
return response()->json([
'status' => 'success',
'data' => $imunisasi
]);
}
/**
* Update imunisasi status and date
*/
public function update(Request $request, $id)
{
$validator = Validator::make($request->all(), [
'tanggal' => 'sometimes|date',
'status' => 'sometimes|in:' . implode(',', Imunisasi::$statusList),
]);
if ($validator->fails()) {
return response()->json([
'status' => 'error',
'message' => 'Validasi gagal',
'errors' => $validator->errors()
], 422);
}
$imunisasi = Imunisasi::find($id);
if (!$imunisasi) {
return response()->json([
'status' => 'error',
'message' => 'Data imunisasi tidak ditemukan'
], 404);
}
// Keep track of old status for comparison
$oldStatus = $imunisasi->status;
// Only update tanggal and status fields
$updateData = [];
if ($request->has('tanggal')) {
$updateData['tanggal'] = $request->tanggal;
}
if ($request->has('status')) {
$updateData['status'] = $request->status;
}
$imunisasi->update($updateData);
// If status changed and jadwal_imunisasi_id is set, update jadwal status
if ($request->has('status') && $oldStatus !== $request->status && $imunisasi->jadwal_imunisasi_id) {
$this->updateJadwalStatus($imunisasi->jadwal_imunisasi_id);
}
return response()->json([
'status' => 'success',
'message' => 'Data imunisasi berhasil diperbarui',
'data' => $imunisasi->fresh()->load(['anak', 'jenisImunisasi'])
]);
}
/**
* Get imunisasi data for a specific child
*/
public function getByAnakId($anakId)
{
$anak = Anak::find($anakId);
if (!$anak) {
return response()->json([
'status' => 'error',
'message' => 'Data anak tidak ditemukan'
], 404);
}
$imunisasi = Imunisasi::with('jenisImunisasi')
->where('anak_id', $anakId)
->orderBy('tanggal', 'desc')
->get();
return response()->json([
'status' => 'success',
'data' => $imunisasi
]);
}
/**
* Get scheduled imunisasi and check implementation status
*/
public function getJadwalWithStatus(Request $request)
{
// Get all scheduled imunisasi
$query = JadwalImunisasi::with('jenisImunisasi')
->orderBy('tanggal', 'desc');
// Filter by date range if provided
if ($request->has('start_date') && $request->has('end_date')) {
$query->whereBetween('tanggal', [$request->start_date, $request->end_date]);
}
$jadwalImunisasi = $query->get();
// Prepare response with implementation status
$result = [];
foreach ($jadwalImunisasi as $jadwal) {
// For each jadwal, check if there are matching imunisasi records
$implementedCount = Imunisasi::where('jenis_id', $jadwal->jenis_imunisasi_id)
->where('tanggal', $jadwal->tanggal)
->count();
// Add status information
$item = [
'jadwal' => $jadwal,
'jenis_imunisasi' => $jadwal->jenisImunisasi,
'implementation_count' => $implementedCount,
'is_implemented' => $implementedCount > 0
];
$result[] = $item;
}
return response()->json([
'status' => 'success',
'data' => $result
]);
}
/**
* Get jadwal imunisasi for a specific child based on their age
*/
public function getJadwalForAnak($anakId)
{
$anak = Anak::find($anakId);
if (!$anak) {
return response()->json([
'status' => 'error',
'message' => 'Data anak tidak ditemukan'
], 404);
}
// Calculate child's age in days
$tanggalLahir = Carbon::parse($anak->tanggal_lahir);
$umurHari = $tanggalLahir->diffInDays(Carbon::now());
// Get upcoming scheduled imunisasi
$jadwalImunisasi = JadwalImunisasi::with('jenisImunisasi')
->whereHas('jenisImunisasi', function($query) use ($umurHari) {
// Filter jadwal where child's age is within min and max age range
$query->where('min_umur_hari', '<=', $umurHari)
->where('max_umur_hari', '>=', $umurHari);
})
->where('tanggal', '>=', Carbon::today()->format('Y-m-d'))
->orderBy('tanggal', 'asc')
->get();
// Check if each jadwal is already implemented for this child
$result = [];
foreach ($jadwalImunisasi as $jadwal) {
// Check if this imunisasi is already scheduled/implemented for the child
$existingImunisasi = Imunisasi::where('anak_id', $anakId)
->where('jenis_id', $jadwal->jenis_imunisasi_id)
->first();
$result[] = [
'jadwal' => $jadwal,
'jenis_imunisasi' => $jadwal->jenisImunisasi,
'child_age_days' => $umurHari,
'is_eligible' => true,
'is_implemented' => $existingImunisasi ? true : false,
'imunisasi' => $existingImunisasi
];
}
return response()->json([
'status' => 'success',
'anak' => [
'id' => $anak->id,
'nama' => $anak->nama_anak,
'tanggal_lahir' => $anak->tanggal_lahir,
'umur_hari' => $umurHari
],
'data' => $result
]);
}
/**
* Check implementation status of a schedule
*/
public function checkImplementationStatus(Request $request)
{
$validator = Validator::make($request->all(), [
'jadwal_id' => 'required|exists:jadwal_imunisasi,id',
]);
if ($validator->fails()) {
return response()->json(['errors' => $validator->errors()], 422);
}
$jadwalId = $request->jadwal_id;
$jadwal = JadwalImunisasi::with('jenisImunisasi')->findOrFail($jadwalId);
// Make sure jadwal implementation status is up-to-date
$this->updateJadwalStatus($jadwalId);
$jadwal->refresh();
// Count implementation even if jadwal_imunisasi_id is not set
$implementationCount = Imunisasi::where('jenis_id', $jadwal->jenis_imunisasi_id)
->where('tanggal', $jadwal->tanggal)
->count();
// Count completed implementations
$completedCount = Imunisasi::where('jenis_id', $jadwal->jenis_imunisasi_id)
->where('tanggal', $jadwal->tanggal)
->whereIn('status', [Imunisasi::STATUS_SELESAI_SESUAI, Imunisasi::STATUS_SELESAI_TIDAK_SESUAI])
->count();
$response = [
'success' => true,
'data' => [
[
'id' => $jadwal->id,
'jenis_imunisasi' => $jadwal->jenisImunisasi,
'is_implemented' => $jadwal->is_implemented,
'implementation_count' => $implementationCount,
'completed_count' => $completedCount
]
]
];
return response()->json($response);
}
/**
* Confirm implementation of a schedule
*/
public function confirmImplementation($id)
{
$jadwal = JadwalImunisasi::findOrFail($id);
// Update jadwal implementation status based on completed imunisasi records
$this->updateJadwalStatus($jadwal->id);
return response()->json([
'success' => true,
'message' => 'Jadwal imunisasi berhasil diperiksa status implementasinya.'
]);
}
/**
* Get eligible children for a schedule
*/
public function getEligibleChildren($jadwalId)
{
$jadwal = JadwalImunisasi::with('jenisImunisasi')->findOrFail($jadwalId);
$jenisImunisasi = $jadwal->jenisImunisasi;
// Get all children
$allChildren = Anak::all();
$eligibleChildren = [];
foreach ($allChildren as $anak) {
// Calculate age in days
$tanggalLahir = Carbon::parse($anak->tanggal_lahir);
$umurHari = $tanggalLahir->diffInDays(Carbon::now());
// Check if already registered
$isRegistered = Imunisasi::where('anak_id', $anak->id)
->where('jadwal_imunisasi_id', $jadwalId)
->exists();
$isImplemented = Imunisasi::where('anak_id', $anak->id)
->where('jadwal_imunisasi_id', $jadwalId)
->where('status', 'Sudah')
->exists();
// Check age eligibility - use min_umur_hari and max_umur_hari instead of min_usia_hari and max_usia_hari
if ($umurHari >= $jenisImunisasi->min_umur_hari && $umurHari <= $jenisImunisasi->max_umur_hari) {
$eligibleChildren[] = [
'anak' => $anak,
'is_registered' => $isRegistered,
'is_implemented' => $isImplemented,
'child_age_days' => $umurHari
];
}
}
return response()->json([
'success' => true,
'data' => $eligibleChildren
]);
}
/**
* Create imunisasi records from a jadwal
*/
public function createFromJadwal(Request $request)
{
$validator = Validator::make($request->all(), [
'jadwal_imunisasi_id' => 'required|exists:jadwal_imunisasi,id',
'anak_id' => 'required|exists:anak,id',
]);
if ($validator->fails()) {
return response()->json([
'status' => 'error',
'message' => 'Validasi gagal',
'errors' => $validator->errors()
], 422);
}
// Get the jadwal
$jadwal = JadwalImunisasi::findOrFail($request->jadwal_imunisasi_id);
// Get anak and verify age eligibility
$anak = Anak::findOrFail($request->anak_id);
$tanggalLahir = Carbon::parse($anak->tanggal_lahir);
$umurHari = $tanggalLahir->diffInDays(Carbon::now());
// Get jenis imunisasi
$jenisImunisasi = JenisImunisasi::findOrFail($jadwal->jenis_imunisasi_id);
// Check if child's age is within the eligible range
if ($umurHari < $jenisImunisasi->min_umur_hari || $umurHari > $jenisImunisasi->max_umur_hari) {
return response()->json([
'status' => 'error',
'message' => 'Anak tidak memenuhi syarat umur untuk imunisasi ini',
'data' => [
'umur_anak_hari' => $umurHari,
'min_umur_hari' => $jenisImunisasi->min_umur_hari,
'max_umur_hari' => $jenisImunisasi->max_umur_hari
]
], 422);
}
// Check if imunisasi already exists for this child and jadwal
$existingImunisasi = Imunisasi::where('anak_id', $request->anak_id)
->where('jenis_id', $jadwal->jenis_imunisasi_id)
->first();
if ($existingImunisasi) {
return response()->json([
'status' => 'error',
'message' => 'Data imunisasi untuk anak dan jadwal ini sudah ada',
'data' => $existingImunisasi
], 422);
}
// Create new imunisasi record with jadwal_imunisasi_id
$imunisasi = Imunisasi::create([
'anak_id' => $request->anak_id,
'jenis_id' => $jadwal->jenis_imunisasi_id,
'jadwal_imunisasi_id' => $jadwal->id,
'tanggal' => $jadwal->tanggal,
'status' => Imunisasi::STATUS_BELUM
]);
return response()->json([
'status' => 'success',
'message' => 'Data imunisasi berhasil dibuat dari jadwal',
'data' => $imunisasi->load(['anak', 'jenisImunisasi'])
], 201);
}
/**
* Mark a jadwal as complete and register all eligible children
*/
public function completeJadwal($id)
{
try {
$jadwal = JadwalImunisasi::with('jenisImunisasi')->findOrFail($id);
// Get all children
$allChildren = Anak::all();
$eligibleChildren = [];
$debug_info = [];
foreach ($allChildren as $anak) {
// Calculate age in days
$tanggalLahir = Carbon::parse($anak->tanggal_lahir);
$umurHari = $tanggalLahir->diffInDays(Carbon::now());
// Log info for debugging
$debug_info[] = [
'anak_id' => $anak->id,
'nama' => $anak->nama_anak,
'tanggal_lahir' => $anak->tanggal_lahir,
'umur_hari' => $umurHari,
'min_umur_hari' => $jadwal->jenisImunisasi->min_umur_hari,
'max_umur_hari' => $jadwal->jenisImunisasi->max_umur_hari,
'eligible' => ($umurHari >= $jadwal->jenisImunisasi->min_umur_hari && $umurHari <= $jadwal->jenisImunisasi->max_umur_hari)
];
// Check age eligibility
if ($umurHari >= $jadwal->jenisImunisasi->min_umur_hari && $umurHari <= $jadwal->jenisImunisasi->max_umur_hari) {
$eligibleChildren[] = $anak;
}
}
// Register each eligible child for this imunisasi if not already registered
$registered = 0;
foreach ($eligibleChildren as $anak) {
// Check if already registered
$existing = Imunisasi::where('anak_id', $anak->id)
->where('jenis_id', $jadwal->jenis_imunisasi_id)
->first();
if (!$existing) {
// Create new imunisasi record, status awal Belum
Imunisasi::create([
'anak_id' => $anak->id,
'jenis_id' => $jadwal->jenis_imunisasi_id,
'jadwal_imunisasi_id' => $jadwal->id,
'tanggal' => $jadwal->tanggal,
'status' => Imunisasi::STATUS_BELUM
]);
$registered++;
}
}
// Status jadwal independent dari status imunisasi
// Tombol "Selesai" di jadwal.blade.php akan mengubah status jadwal secara langsung
// Jika tidak ada anak yang eligible, tampilkan pesan yang jelas
$message = $registered > 0
? "Jadwal imunisasi berhasil diselesaikan dan $registered anak ditambahkan ke daftar imunisasi."
: "Jadwal imunisasi berhasil diselesaikan tetapi tidak ada anak yang memenuhi syarat umur (min: {$jadwal->jenisImunisasi->min_umur_hari} hari, max: {$jadwal->jenisImunisasi->max_umur_hari} hari).";
return response()->json([
'success' => true,
'message' => $message,
'data' => [
'jadwal' => $jadwal->fresh(),
'eligible_children_count' => count($eligibleChildren),
'registered_children' => $registered,
'debug_info' => $debug_info
]
]);
} catch (\Exception $e) {
return response()->json([
'success' => false,
'message' => 'Gagal menandai jadwal sebagai selesai: ' . $e->getMessage(),
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString()
], 500);
}
}
/**
* Update implementation status of jadwal - now independent of imunisasi status
*/
private function updateJadwalStatus($jadwalId)
{
// Status jadwal sekarang independent dari status imunisasi
// Akan diubah langsung menggunakan endpoint khusus
return JadwalImunisasi::findOrFail($jadwalId);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,27 @@
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Models\Pengguna;
use Illuminate\Http\Request;
class PenggunaController extends Controller
{
public function findByNik($nik)
{
$pengguna = Pengguna::where('nik', $nik)->first();
if (!$pengguna) {
return response()->json([
'success' => false,
'message' => 'Pengguna tidak ditemukan'
], 404);
}
return response()->json([
'success' => true,
'data' => $pengguna
]);
}
}

View File

@ -0,0 +1,159 @@
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use App\Models\PerkembanganAnak;
use Illuminate\Support\Facades\Validator;
use Carbon\Carbon;
class PerkembanganAnakApiController extends Controller
{
/**
* Get all growth data for a child
*/
public function getByAnakId($anakId)
{
try {
$perkembangan = PerkembanganAnak::where('anak_id', $anakId)
->orderBy('tanggal', 'asc')
->get();
return response()->json([
'status' => 'success',
'perkembangan' => $perkembangan,
'child_info' => [
'id' => $anakId,
'records_count' => $perkembangan->count()
]
]);
} catch (\Exception $e) {
return response()->json([
'status' => 'error',
'message' => 'Failed to get growth data: ' . $e->getMessage()
], 500);
}
}
/**
* Store new growth data
* Always creates a new record to maintain history
*/
public function store(Request $request)
{
try {
$validator = Validator::make($request->all(), [
'anak_id' => 'required|exists:anak,id',
'berat_badan' => 'required|numeric|min:0',
'tinggi_badan' => 'required|numeric|min:0',
]);
if ($validator->fails()) {
return response()->json([
'status' => 'error',
'message' => 'Validation failed',
'errors' => $validator->errors()
], 422);
}
// Set tanggal ke waktu saat ini
$currentDate = Carbon::now();
// Create new record with current date
$perkembangan = new PerkembanganAnak();
$perkembangan->anak_id = $request->anak_id;
$perkembangan->tanggal = $currentDate;
$perkembangan->berat_badan = $request->berat_badan;
$perkembangan->tinggi_badan = $request->tinggi_badan;
$perkembangan->save();
return response()->json([
'status' => 'success',
'message' => 'Growth data saved successfully',
'perkembangan' => $perkembangan
]);
} catch (\Exception $e) {
\Log::error('Error saving perkembangan: ' . $e->getMessage());
return response()->json([
'status' => 'error',
'message' => 'Failed to save growth data: ' . $e->getMessage()
], 500);
}
}
/**
* Update existing growth data
* Instead of updating, creates a new record and marks the old one as updated
*/
public function update(Request $request, $id)
{
try {
$validator = Validator::make($request->all(), [
'anak_id' => 'required|exists:anak,id',
'tanggal' => 'required|date',
'berat_badan' => 'required|numeric|min:0',
'tinggi_badan' => 'required|numeric|min:0',
]);
if ($validator->fails()) {
return response()->json([
'status' => 'error',
'message' => 'Validation failed',
'errors' => $validator->errors()
], 422);
}
// Get the old record
$oldRecord = PerkembanganAnak::findOrFail($id);
// Create new record with updated data
$newRecord = PerkembanganAnak::create([
'anak_id' => $request->anak_id,
'tanggal' => $request->tanggal,
'berat_badan' => $request->berat_badan,
'tinggi_badan' => $request->tinggi_badan,
'updated_from_id' => $id, // Reference to the old record
]);
// Mark old record as updated
$oldRecord->update([
'is_updated' => true,
'updated_by_id' => $newRecord->id
]);
return response()->json([
'status' => 'success',
'message' => 'Growth data updated successfully',
'perkembangan' => $newRecord,
'old_record' => $oldRecord
]);
} catch (\Exception $e) {
return response()->json([
'status' => 'error',
'message' => 'Failed to update growth data: ' . $e->getMessage()
], 500);
}
}
/**
* Delete growth data
*/
public function destroy($id)
{
try {
$perkembangan = PerkembanganAnak::findOrFail($id);
$perkembangan->delete();
return response()->json([
'status' => 'success',
'message' => 'Growth data deleted successfully'
]);
} catch (\Exception $e) {
return response()->json([
'status' => 'error',
'message' => 'Failed to delete growth data: ' . $e->getMessage()
], 500);
}
}
}

View File

@ -0,0 +1,262 @@
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Models\Stunting;
use App\Models\Anak;
use App\Models\PerkembanganAnak;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
class StuntingApiController extends Controller
{
protected $stuntingCalculator;
public function __construct()
{
// Fix error "Class App\Services\StuntingCalculator not found"
try {
$this->stuntingCalculator = app()->make('App\Services\StuntingCalculator');
} catch (\Throwable $e) {
// Buat manual jika injeksi gagal
require_once app_path('Services/StuntingCalculator.php');
$this->stuntingCalculator = new \App\Services\StuntingCalculator();
}
}
/**
* Get all stunting records for a specific child
*/
public function getByAnakId($anak_id)
{
$anak = Anak::find($anak_id);
if (!$anak) {
return response()->json([
'status' => 'error',
'message' => 'Data anak tidak ditemukan',
], 404);
}
$stunting = Stunting::with('perkembangan')
->where('anak_id', $anak_id)
->orderBy('tanggal', 'desc')
->get();
return response()->json([
'status' => 'success',
'stunting' => $stunting,
]);
}
/**
* Get a specific stunting record
*/
public function show($id)
{
$stunting = Stunting::with('perkembangan')->find($id);
if (!$stunting) {
return response()->json([
'status' => 'error',
'message' => 'Data stunting tidak ditemukan',
], 404);
}
return response()->json([
'status' => 'success',
'stunting' => $stunting,
]);
}
/**
* Calculate stunting status based on age, height, and weight
*/
public function calculateStatus(Request $request)
{
$validator = Validator::make($request->all(), [
'usia' => 'required|string',
'tinggi_badan' => 'required|numeric',
'berat_badan' => 'required|numeric',
]);
if ($validator->fails()) {
return response()->json([
'status' => 'error',
'message' => 'Validation error',
'errors' => $validator->errors()
], 422);
}
// Extract usia dalam bulan
$ageInMonths = $this->stuntingCalculator->extractAgeInMonths($request->usia);
// Jika usia kurang dari 24 bulan atau lebih dari 60 bulan
if ($ageInMonths < 24 || $ageInMonths > 60) {
return response()->json([
'status' => 'error',
'message' => 'Usia harus antara 24-60 bulan untuk perhitungan stunting',
], 422);
}
// Hitung status stunting
$statusByHeight = $this->stuntingCalculator->determineStatusByHeight(
$ageInMonths,
$request->tinggi_badan
);
$statusByWeight = $this->stuntingCalculator->determineStatusByWeight(
$ageInMonths,
$request->berat_badan
);
$finalStatus = $this->stuntingCalculator->determineStatus(
$ageInMonths,
$request->tinggi_badan,
$request->berat_badan
);
return response()->json([
'status' => 'success',
'usia_bulan' => $ageInMonths,
'tinggi_badan' => $request->tinggi_badan,
'berat_badan' => $request->berat_badan,
'status_by_height' => $statusByHeight,
'status_by_weight' => $statusByWeight,
'final_status' => $finalStatus,
]);
}
/**
* Create a new stunting record
*/
public function store(Request $request)
{
$validator = Validator::make($request->all(), [
'anak_id' => 'required|exists:anak,id',
'tanggal' => 'required|date',
'usia' => 'required|string',
'catatan' => 'nullable|string',
'status' => 'nullable|in:Stunting,Resiko Stunting,Tidak Stunting',
'perkembangan_id' => 'required|exists:perkembangan_anak,id',
]);
if ($validator->fails()) {
return response()->json([
'status' => 'error',
'message' => 'Validation error',
'errors' => $validator->errors()
], 422);
}
// Get perkembangan for height and weight
$perkembangan = PerkembanganAnak::findOrFail($request->perkembangan_id);
// Extract usia dalam bulan
$ageInMonths = $this->stuntingCalculator->extractAgeInMonths($request->usia);
// Calculate stunting status
$calculatedStatus = $this->stuntingCalculator->determineStatus(
$ageInMonths,
$perkembangan->tinggi_badan,
$perkembangan->berat_badan
);
// Use provided status or calculated status
$data = $request->all();
$data['status'] = $request->status ?? $calculatedStatus;
$data['tinggi_badan'] = $perkembangan->tinggi_badan;
$data['berat_badan'] = $perkembangan->berat_badan;
$stunting = Stunting::create($data);
return response()->json([
'status' => 'success',
'message' => 'Data stunting berhasil ditambahkan',
'stunting' => $stunting,
'calculated_status' => $calculatedStatus
], 201);
}
/**
* Update a stunting record
*/
public function update(Request $request, $id)
{
$stunting = Stunting::find($id);
if (!$stunting) {
return response()->json([
'status' => 'error',
'message' => 'Data stunting tidak ditemukan',
], 404);
}
$validator = Validator::make($request->all(), [
'tanggal' => 'required|date',
'usia' => 'required|string',
'catatan' => 'nullable|string',
'status' => 'nullable|in:Stunting,Resiko Stunting,Tidak Stunting',
'perkembangan_id' => 'required|exists:perkembangan_anak,id',
]);
if ($validator->fails()) {
return response()->json([
'status' => 'error',
'message' => 'Validation error',
'errors' => $validator->errors()
], 422);
}
// Get perkembangan for height and weight
$perkembangan = PerkembanganAnak::findOrFail($request->perkembangan_id);
// Extract usia dalam bulan
$ageInMonths = $this->stuntingCalculator->extractAgeInMonths($request->usia);
// Calculate stunting status
$calculatedStatus = $this->stuntingCalculator->determineStatus(
$ageInMonths,
$perkembangan->tinggi_badan,
$perkembangan->berat_badan
);
// Get data to update
$data = $request->all();
$data['status'] = $request->status ?? $calculatedStatus;
$data['tinggi_badan'] = $perkembangan->tinggi_badan;
$data['berat_badan'] = $perkembangan->berat_badan;
$stunting->update($data);
return response()->json([
'status' => 'success',
'message' => 'Data stunting berhasil diperbarui',
'stunting' => $stunting,
'calculated_status' => $calculatedStatus
]);
}
/**
* Delete a stunting record
*/
public function destroy($id)
{
$stunting = Stunting::find($id);
if (!$stunting) {
return response()->json([
'status' => 'error',
'message' => 'Data stunting tidak ditemukan',
], 404);
}
$stunting->delete();
return response()->json([
'status' => 'success',
'message' => 'Data stunting berhasil dihapus',
]);
}
}

View File

@ -0,0 +1,515 @@
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use App\Models\Vitamin;
use App\Models\JadwalVitamin;
use App\Models\Anak;
use App\Models\JenisVitamin;
use Illuminate\Support\Facades\Validator;
use Carbon\Carbon;
class VitaminApiController extends Controller
{
/**
* Get all vitamin records with filtering options
*/
public function index(Request $request)
{
$query = Vitamin::with(['anak', 'jenisVitamin']);
// Filter by anak_id if provided
if ($request->has('anak_id')) {
$query->where('anak_id', $request->anak_id);
}
// Filter by status if provided
if ($request->has('status')) {
$query->where('status', $request->status);
}
// Get paginated results or all if limit=0
if ($request->has('limit') && $request->limit > 0) {
$vitamin = $query->orderBy('tanggal', 'desc')->paginate($request->limit);
} else {
$vitamin = $query->orderBy('tanggal', 'desc')->get();
}
return response()->json([
'status' => 'success',
'data' => $vitamin
]);
}
/**
* Get a vitamin record by ID
*/
public function show($id)
{
$vitamin = Vitamin::with(['anak', 'jenisVitamin'])->find($id);
if (!$vitamin) {
return response()->json([
'status' => 'error',
'message' => 'Data vitamin tidak ditemukan'
], 404);
}
return response()->json([
'status' => 'success',
'data' => $vitamin
]);
}
/**
* Update vitamin status and date
*/
public function update(Request $request, $id)
{
$validator = Validator::make($request->all(), [
'tanggal' => 'sometimes|date',
'status' => 'sometimes|in:' . implode(',', Vitamin::$statusList),
]);
if ($validator->fails()) {
return response()->json([
'status' => 'error',
'message' => 'Validasi gagal',
'errors' => $validator->errors()
], 422);
}
$vitamin = Vitamin::find($id);
if (!$vitamin) {
return response()->json([
'status' => 'error',
'message' => 'Data vitamin tidak ditemukan'
], 404);
}
// Keep track of old status for comparison
$oldStatus = $vitamin->status;
// Only update tanggal and status fields
$updateData = [];
if ($request->has('tanggal')) {
$updateData['tanggal'] = $request->tanggal;
}
if ($request->has('status')) {
$updateData['status'] = $request->status;
}
$vitamin->update($updateData);
// If status changed and jadwal_vitamin_id is set, update jadwal status
if ($request->has('status') && $oldStatus !== $request->status && $vitamin->jadwal_vitamin_id) {
$this->updateJadwalStatus($vitamin->jadwal_vitamin_id);
}
return response()->json([
'status' => 'success',
'message' => 'Data vitamin berhasil diperbarui',
'data' => $vitamin->fresh()->load(['anak', 'jenisVitamin'])
]);
}
/**
* Get vitamin data for a specific child
*/
public function getByAnakId($anakId)
{
$anak = Anak::find($anakId);
if (!$anak) {
return response()->json([
'status' => 'error',
'message' => 'Data anak tidak ditemukan'
], 404);
}
$vitamin = Vitamin::with('jenisVitamin')
->where('anak_id', $anakId)
->orderBy('tanggal', 'desc')
->get();
return response()->json([
'status' => 'success',
'data' => $vitamin
]);
}
/**
* Get scheduled vitamin and check implementation status
*/
public function getJadwalWithStatus(Request $request)
{
// Get all scheduled vitamin
$query = JadwalVitamin::with('jenisVitamin')
->orderBy('tanggal', 'desc');
// Filter by date range if provided
if ($request->has('start_date') && $request->has('end_date')) {
$query->whereBetween('tanggal', [$request->start_date, $request->end_date]);
}
$jadwalVitamin = $query->get();
// Prepare response with implementation status
$result = [];
foreach ($jadwalVitamin as $jadwal) {
// For each jadwal, check if there are matching vitamin records
$implementedCount = Vitamin::where('jenis_id', $jadwal->jenis_vitamin_id)
->where('tanggal', $jadwal->tanggal)
->count();
// Add status information
$item = [
'jadwal' => $jadwal,
'jenis_vitamin' => $jadwal->jenisVitamin,
'implementation_count' => $implementedCount,
'is_implemented' => $implementedCount > 0
];
$result[] = $item;
}
return response()->json([
'status' => 'success',
'data' => $result
]);
}
/**
* Get jadwal vitamin for a specific child based on their age
*/
public function getJadwalForAnak($anakId)
{
$anak = Anak::find($anakId);
if (!$anak) {
return response()->json([
'status' => 'error',
'message' => 'Data anak tidak ditemukan'
], 404);
}
// Calculate child's age in days
$tanggalLahir = Carbon::parse($anak->tanggal_lahir);
$umurHari = $tanggalLahir->diffInDays(Carbon::now());
// Get upcoming scheduled vitamin
$jadwalVitamin = JadwalVitamin::with('jenisVitamin')
->whereHas('jenisVitamin', function($query) use ($umurHari) {
// Filter jadwal where child's age is within min and max age range
$query->where('min_umur_hari', '<=', $umurHari)
->where('max_umur_hari', '>=', $umurHari);
})
->where('tanggal', '>=', Carbon::today()->format('Y-m-d'))
->orderBy('tanggal', 'asc')
->get();
// Check if each jadwal is already implemented for this child
$result = [];
foreach ($jadwalVitamin as $jadwal) {
// Check if this vitamin is already scheduled/implemented for the child
$existingVitamin = Vitamin::where('anak_id', $anakId)
->where('jenis_id', $jadwal->jenis_vitamin_id)
->first();
$result[] = [
'jadwal' => $jadwal,
'jenis_vitamin' => $jadwal->jenisVitamin,
'child_age_days' => $umurHari,
'is_eligible' => true,
'is_implemented' => $existingVitamin ? true : false,
'vitamin' => $existingVitamin
];
}
return response()->json([
'status' => 'success',
'anak' => [
'id' => $anak->id,
'nama' => $anak->nama_anak,
'tanggal_lahir' => $anak->tanggal_lahir,
'umur_hari' => $umurHari
],
'data' => $result
]);
}
/**
* Check implementation status of a schedule
*/
public function checkImplementationStatus(Request $request)
{
$validator = Validator::make($request->all(), [
'jadwal_id' => 'required|exists:jadwal_vitamin,id',
]);
if ($validator->fails()) {
return response()->json(['errors' => $validator->errors()], 422);
}
$jadwalId = $request->jadwal_id;
$jadwal = JadwalVitamin::with('jenisVitamin')->findOrFail($jadwalId);
// Make sure jadwal implementation status is up-to-date
$this->updateJadwalStatus($jadwalId);
$jadwal->refresh();
// Count implementation even if jadwal_vitamin_id is not set
$implementationCount = Vitamin::where('jenis_id', $jadwal->jenis_vitamin_id)
->where('tanggal', $jadwal->tanggal)
->count();
// Count completed implementations
$completedCount = Vitamin::where('jenis_id', $jadwal->jenis_vitamin_id)
->where('tanggal', $jadwal->tanggal)
->where('status', Vitamin::STATUS_SELESAI)
->count();
$response = [
'success' => true,
'data' => [
[
'id' => $jadwal->id,
'jenis_vitamin' => $jadwal->jenisVitamin,
'is_implemented' => $jadwal->is_implemented,
'implementation_count' => $implementationCount,
'completed_count' => $completedCount
]
]
];
return response()->json($response);
}
/**
* Confirm implementation of a schedule
*/
public function confirmImplementation($id)
{
$jadwal = JadwalVitamin::findOrFail($id);
// Update jadwal implementation status based on completed vitamin records
$this->updateJadwalStatus($jadwal->id);
return response()->json([
'success' => true,
'message' => 'Jadwal vitamin berhasil diperiksa status implementasinya.'
]);
}
/**
* Get eligible children for a schedule
*/
public function getEligibleChildren($jadwalId)
{
$jadwal = JadwalVitamin::with('jenisVitamin')->findOrFail($jadwalId);
$jenisVitamin = $jadwal->jenisVitamin;
// Get all children
$allChildren = Anak::all();
$eligibleChildren = [];
foreach ($allChildren as $anak) {
// Calculate age in months
$birthDate = Carbon::parse($anak->tanggal_lahir);
$ageInMonths = $birthDate->diffInMonths(Carbon::now());
// Check if already registered
$isRegistered = Vitamin::where('anak_id', $anak->id)
->where('jadwal_vitamin_id', $jadwalId)
->exists();
$isImplemented = Vitamin::where('anak_id', $anak->id)
->where('jadwal_vitamin_id', $jadwalId)
->where('status', 'Sudah')
->exists();
// Check age eligibility with the correct field names
if ($ageInMonths >= $jenisVitamin->min_umur_bulan && $ageInMonths <= $jenisVitamin->max_umur_bulan) {
$eligibleChildren[] = [
'anak' => $anak,
'is_registered' => $isRegistered,
'is_implemented' => $isImplemented,
'child_age_months' => $ageInMonths
];
}
}
return response()->json([
'success' => true,
'data' => $eligibleChildren
]);
}
/**
* Create vitamin records from a jadwal
*/
public function createFromJadwal(Request $request)
{
$validator = Validator::make($request->all(), [
'jadwal_vitamin_id' => 'required|exists:jadwal_vitamin,id',
'anak_id' => 'required|exists:anak,id',
]);
if ($validator->fails()) {
return response()->json([
'status' => 'error',
'message' => 'Validasi gagal',
'errors' => $validator->errors()
], 422);
}
// Get the jadwal
$jadwal = JadwalVitamin::findOrFail($request->jadwal_vitamin_id);
// Get anak and verify age eligibility
$anak = Anak::findOrFail($request->anak_id);
$tanggalLahir = Carbon::parse($anak->tanggal_lahir);
$umurBulan = $tanggalLahir->diffInMonths(Carbon::parse($jadwal->tanggal));
// Get jenis vitamin
$jenisVitamin = JenisVitamin::findOrFail($jadwal->jenis_vitamin_id);
// Check if child's age is within the eligible range (using months for vitamin)
if ($umurBulan < $jenisVitamin->min_umur_bulan || $umurBulan > $jenisVitamin->max_umur_bulan) {
return response()->json([
'status' => 'error',
'message' => 'Anak tidak memenuhi syarat umur untuk vitamin ini',
'data' => [
'umur_anak_bulan' => $umurBulan,
'min_umur_bulan' => $jenisVitamin->min_umur_bulan,
'max_umur_bulan' => $jenisVitamin->max_umur_bulan
]
], 422);
}
// Check if vitamin already exists for this child and jadwal
$existingVitamin = Vitamin::where('anak_id', $request->anak_id)
->where('jenis_id', $jadwal->jenis_vitamin_id)
->first();
if ($existingVitamin) {
return response()->json([
'status' => 'error',
'message' => 'Data vitamin untuk anak dan jadwal ini sudah ada',
'data' => $existingVitamin
], 422);
}
// Create new vitamin record with jadwal_vitamin_id
$vitamin = Vitamin::create([
'anak_id' => $request->anak_id,
'jenis_id' => $jadwal->jenis_vitamin_id,
'jadwal_vitamin_id' => $jadwal->id,
'tanggal' => $jadwal->tanggal,
'status' => Vitamin::STATUS_BELUM
]);
return response()->json([
'status' => 'success',
'message' => 'Data vitamin berhasil dibuat dari jadwal',
'data' => $vitamin->load(['anak', 'jenisVitamin'])
], 201);
}
/**
* Mark a jadwal as complete and register all eligible children
*/
public function completeJadwal($id)
{
try {
$jadwal = JadwalVitamin::with('jenisVitamin')->findOrFail($id);
// Get all children
$allChildren = Anak::all();
$eligibleChildren = [];
$debug_info = [];
foreach ($allChildren as $anak) {
// Calculate age in months
$birthDate = Carbon::parse($anak->tanggal_lahir);
$ageInMonths = $birthDate->diffInMonths(Carbon::now());
// Log info for debugging
$debug_info[] = [
'anak_id' => $anak->id,
'nama' => $anak->nama_anak,
'tanggal_lahir' => $anak->tanggal_lahir,
'umur_bulan' => $ageInMonths,
'min_umur_bulan' => $jadwal->jenisVitamin->min_umur_bulan,
'max_umur_bulan' => $jadwal->jenisVitamin->max_umur_bulan,
'eligible' => ($ageInMonths >= $jadwal->jenisVitamin->min_umur_bulan && $ageInMonths <= $jadwal->jenisVitamin->max_umur_bulan)
];
// Check age eligibility
if ($ageInMonths >= $jadwal->jenisVitamin->min_umur_bulan && $ageInMonths <= $jadwal->jenisVitamin->max_umur_bulan) {
$eligibleChildren[] = $anak;
}
}
// Register each eligible child for this vitamin if not already registered
$registered = 0;
foreach ($eligibleChildren as $anak) {
// Check if already registered
$existing = Vitamin::where('anak_id', $anak->id)
->where('jenis_id', $jadwal->jenis_vitamin_id)
->first();
if (!$existing) {
// Create new vitamin record dengan status awal Belum
Vitamin::create([
'anak_id' => $anak->id,
'jenis_id' => $jadwal->jenis_vitamin_id,
'jadwal_vitamin_id' => $jadwal->id,
'tanggal' => $jadwal->tanggal,
'status' => 'Belum'
]);
$registered++;
}
}
// Status jadwal independent dari status vitamin
// Tombol "Selesai" di jadwal.blade.php akan mengubah status jadwal secara langsung
// Jika tidak ada anak yang eligible, tampilkan pesan yang jelas
$message = $registered > 0
? "Jadwal vitamin berhasil diselesaikan dan $registered anak ditambahkan ke daftar vitamin."
: "Jadwal vitamin berhasil diselesaikan tetapi tidak ada anak yang memenuhi syarat umur (min: {$jadwal->jenisVitamin->min_umur_bulan} bulan, max: {$jadwal->jenisVitamin->max_umur_bulan} bulan).";
return response()->json([
'success' => true,
'message' => $message,
'data' => [
'jadwal' => $jadwal->fresh(),
'eligible_children_count' => count($eligibleChildren),
'registered_children' => $registered,
'debug_info' => $debug_info
]
]);
} catch (\Exception $e) {
return response()->json([
'success' => false,
'message' => 'Gagal menandai jadwal sebagai selesai: ' . $e->getMessage(),
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString()
], 500);
}
}
/**
* Update implementation status of jadwal - now independent of vitamin status
*/
private function updateJadwalStatus($jadwalId)
{
// Status jadwal sekarang independent dari status vitamin
// Akan diubah langsung menggunakan endpoint khusus
return JadwalVitamin::findOrFail($jadwalId);
}
}

View File

@ -0,0 +1,107 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Str;
use App\Models\Artikel;
class ArtikelController extends Controller
{
protected $artikel = []; // Simpan artikel dalam memori untuk demonstrasi
public function index(Request $request)
{
// Get perPage parameter, default to 9
$perPage = $request->input('perPage', 9);
// Ambil semua artikel dari database, urutkan berdasarkan tanggal terbaru
$artikels = Artikel::orderBy('tanggal', 'desc')
->orderBy('created_at', 'desc')
->paginate($perPage);
return view('artikel', compact('artikels'));
}
public function store(Request $request)
{
// Validasi input
$request->validate([
'judul' => 'required|string|max:255',
'gambar_artikel' => 'required|image|mimes:jpeg,png,jpg,gif|max:2048',
'isi_artikel' => 'required|string',
'tanggal' => 'required|date',
]);
// Simpan gambar ke dalam storage/artikel
$imagePath = null;
if ($request->hasFile('gambar_artikel')) {
$imagePath = $request->file('gambar_artikel')->store('artikel', 'public');
// Ambil hanya nama file, bukan path lengkap
$imagePath = basename($imagePath);
}
// Buat artikel baru dan simpan ke database
Artikel::create([
'judul' => $request->judul,
'gambar_artikel' => $imagePath, // Simpan hanya nama file
'isi_artikel' => $request->isi_artikel,
'tanggal' => $request->tanggal,
]);
return redirect()->route('artikel.index')->with('success', 'Artikel berhasil ditambahkan!');
}
/**
* Menghapus artikel dari database
*/
public function destroy($id)
{
$artikel = Artikel::findOrFail($id);
// Hapus file gambar jika ada
if ($artikel->gambar_artikel) {
$imagePath = 'public/artikel/' . $artikel->gambar_artikel;
if (\Storage::exists($imagePath)) {
\Storage::delete($imagePath);
}
}
// Hapus artikel dari database
$artikel->delete();
return redirect()->route('artikel.index')->with('success', 'Artikel berhasil dihapus!');
}
/**
* Menampilkan detail artikel
*/
public function show($id)
{
$artikel = Artikel::findOrFail($id);
return view('artikel-detail', compact('artikel'));
}
/**
* Mencari artikel berdasarkan judul
*/
public function search(Request $request)
{
$keyword = $request->input('keyword');
$perPage = $request->input('perPage', 9);
// Jika keyword kosong, kembalikan semua artikel
if (empty($keyword)) {
return redirect()->route('artikel.index');
}
// Cari artikel berdasarkan judul atau isi artikel
$artikels = Artikel::where('judul', 'like', '%' . $keyword . '%')
->orWhere('isi_artikel', 'like', '%' . $keyword . '%')
->orderBy('tanggal', 'desc')
->orderBy('created_at', 'desc')
->paginate($perPage);
return view('artikel', compact('artikels', 'keyword'));
}
}

View File

@ -0,0 +1,12 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Routing\Controller as BaseController;
class Controller extends BaseController
{
use AuthorizesRequests, ValidatesRequests;
}

View File

@ -0,0 +1,70 @@
<?php
namespace App\Http\Controllers;
use App\Models\Pengguna;
use App\Services\DashboardService;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
use Exception;
use Illuminate\Support\Facades\Log;
class DashboardController extends Controller
{
protected $dashboardService;
public function __construct(DashboardService $dashboardService)
{
$this->dashboardService = $dashboardService;
}
public function index()
{
try {
// Get dashboard statistics using the service
$stats = $this->dashboardService->getDashboardStats();
} catch (Exception $e) {
// Log the error
Log::error('Dashboard stats error: ' . $e->getMessage());
// Provide default values if there's an error
$stats = [
'total_stunting' => 0,
'total_pengguna' => 0,
'total_anak' => 0,
'total_petugas' => 0
];
}
return view('dashboard', compact('stats'));
}
public function store(Request $request)
{
$validator = Validator::make($request->all(), [
'nik' => 'required|numeric|digits:16|unique:pengguna,nik',
'nama_ibu' => 'required|string|max:100',
'nama_anak' => 'required|string|max:100',
'no_telp' => 'nullable|numeric|digits_between:1,15',
'alamat' => 'required|string',
'tanggal_lahir' => 'required|date',
'tempat_lahir' => 'required|string|max:100',
'jenis_kelamin' => 'required|in:Laki-laki,Perempuan',
'usia' => 'required|string|max:50',
'password' => 'required|string|min:8',
]);
if ($validator->fails()) {
return redirect()->back()
->withErrors($validator)
->withInput();
}
$data = $request->all();
$data['password'] = bcrypt($request->password);
Pengguna::create($data);
return redirect()->back()->with('success', 'Data pengguna berhasil ditambahkan!');
}
}

View File

@ -0,0 +1,155 @@
<?php
namespace App\Http\Controllers;
use App\Models\Anak;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
use App\Exports\AnakExport;
use Maatwebsite\Excel\Facades\Excel;
class DataAnakController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index(Request $request)
{
$search = $request->input('search');
$perPage = $request->input('perPage', 10); // Default 10 data per halaman
// Log untuk debugging
\Log::info('=========== DATA ANAK DEBUG ===========');
\Log::info('Mencoba mengambil data anak');
// Cek jumlah total data tanpa relasi
$totalDataTanpaRelasi = Anak::count();
\Log::info('Total data anak tanpa relasi: ' . $totalDataTanpaRelasi);
// Log semua data anak untuk debugging
$allAnakData = Anak::get();
\Log::info('Data anak yang ada di database: ' . count($allAnakData));
foreach ($allAnakData as $index => $anakItem) {
\Log::info("Data anak #{$index}: ID={$anakItem->id}, Nama={$anakItem->nama_anak}, PenggunaID={$anakItem->pengguna_id}");
}
// Ubah query untuk menampilkan semua data anak
// Gunakan eager loading untuk memuat data pengguna
$query = Anak::with('pengguna');
if ($search) {
$query->where(function($q) use ($search) {
$q->where('nama_anak', 'like', "%{$search}%")
->orWhere('tempat_lahir', 'like', "%{$search}%")
->orWhereHas('pengguna', function($sq) use ($search) {
$sq->where('nik', 'like', "%{$search}%")
->orWhere('nama', 'like', "%{$search}%");
});
});
}
$anak = $query->orderBy('created_at', 'desc')->paginate($perPage);
// Log jumlah data setelah filter
\Log::info('Total data anak setelah filter: ' . $anak->total());
// Cek relasi pengguna
$adaRelasiPengguna = Anak::has('pengguna')->count();
\Log::info('Total data anak dengan relasi pengguna: ' . $adaRelasiPengguna);
// Cek apakah pengguna yang terkait adalah role 'parent'
$penggunaParent = \App\Models\Pengguna::where('role', 'parent')->count();
\Log::info('Total pengguna dengan role parent: ' . $penggunaParent);
// Tampilkan pengguna IDs
$penggunaIds = \App\Models\Pengguna::where('role', 'parent')->pluck('id')->toArray();
\Log::info('ID Pengguna dengan role parent: ' . implode(', ', $penggunaIds));
// Pastikan setiap anak memiliki data pengguna jika ada pengguna_id
foreach ($anak as $anakItem) {
if ($anakItem->pengguna_id && !$anakItem->pengguna) {
// Jika pengguna_id ada tapi relasi pengguna kosong, coba muat manual
$anakItem->pengguna = \App\Models\Pengguna::find($anakItem->pengguna_id);
\Log::info("Memuat manual data pengguna untuk anak ID={$anakItem->id}, PenggunaID={$anakItem->pengguna_id}");
}
}
\Log::info('=========== END DEBUG ===========');
return view('data_anak', compact('anak', 'search'));
}
/**
* Display the specified resource.
*/
public function show(string $id)
{
$anak = Anak::with('pengguna')->findOrFail($id);
if (request()->ajax()) {
return response()->json([
'anak' => $anak,
'pengguna' => $anak->pengguna
]);
}
$action = 'show';
return view('data_anak', compact('anak', 'action'));
}
/**
* Show the form for editing the specified resource.
*/
public function edit(string $id)
{
$anak = Anak::with('pengguna')->findOrFail($id);
$orangtua = \App\Models\Pengguna::where('role', 'parent')->get();
$action = 'edit';
return view('data_anak', compact('anak', 'action', 'orangtua'));
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, string $id)
{
$validator = Validator::make($request->all(), [
'pengguna_id' => 'required|exists:pengguna,id',
'nama_anak' => 'required|string|max:100',
'tempat_lahir' => 'required|string|max:100',
'tanggal_lahir' => 'required|date',
'usia' => 'required|string|max:10',
'jenis_kelamin' => 'required|in:Laki-laki,Perempuan',
]);
if ($validator->fails()) {
return redirect()->back()
->withErrors($validator)
->withInput();
}
$anak = Anak::findOrFail($id);
$anak->update($request->all());
return redirect()->route('anak.index')
->with('success', 'Data anak berhasil diperbarui!');
}
/**
* Remove the specified resource from storage.
*/
public function destroy(string $id)
{
$anak = Anak::findOrFail($id);
$anak->delete();
return redirect()->route('anak.index')
->with('success', 'Data anak berhasil dihapus!');
}
public function excel()
{
return Excel::download(new AnakExport, 'data-anak.xlsx');
}
}

View File

@ -0,0 +1,199 @@
<?php
namespace App\Http\Controllers;
use App\Models\Pengguna;
use App\Models\Anak;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
class DataOrangTuaController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index(Request $request)
{
$search = $request->input('search');
$perPage = $request->input('perPage', 10);
$query = Pengguna::where('role', 'parent');
if ($search) {
$query->where(function($q) use ($search) {
$q->where('nik', 'like', "%{$search}%")
->orWhere('nama', 'like', "%{$search}%")
->orWhere('no_telp', 'like', "%{$search}%");
});
}
$pengguna = $query->orderBy('created_at', 'desc')->paginate($perPage);
// Load anak relation for each pengguna
$pengguna->each(function ($item) {
$item->load('anak');
});
return view('data_orangtua', compact('pengguna', 'search'));
}
/**
* Show the form for creating a new resource.
*/
public function create()
{
$action = 'create';
return view('data_orangtua', compact('action'));
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request)
{
$validator = Validator::make($request->all(), [
'nik' => 'required|numeric|digits:16|unique:pengguna,nik',
'nama' => 'required|string|max:100',
'email' => 'nullable|email|max:100|unique:pengguna,email',
'password' => 'required|string|min:8',
'no_telp' => 'nullable|numeric|digits_between:1,15',
'alamat' => 'required|string',
'nama_anak' => 'required|string|max:100',
'tempat_lahir' => 'required|string|max:100',
'tanggal_lahir' => 'required|date',
'jenis_kelamin' => 'required|in:Laki-laki,Perempuan',
'usia' => 'required|string|max:50',
]);
if ($validator->fails()) {
return redirect()->back()
->withErrors($validator)
->withInput();
}
$pengguna = Pengguna::create([
'nik' => $request->nik,
'nama' => $request->nama,
'email' => $request->email,
'password' => bcrypt($request->password),
'role' => 'parent',
'no_telp' => $request->no_telp,
'alamat' => $request->alamat,
]);
Anak::create([
'pengguna_id' => $pengguna->id,
'nama_anak' => $request->nama_anak,
'tempat_lahir' => $request->tempat_lahir,
'tanggal_lahir' => $request->tanggal_lahir,
'jenis_kelamin' => $request->jenis_kelamin,
'usia' => $request->usia,
]);
return redirect()->route('data_orangtua.index')
->with('success', 'Data orang tua berhasil ditambahkan!');
}
/**
* Display the specified resource.
*/
public function show(string $nik)
{
$pengguna = Pengguna::with('anak')->where('role', 'parent')->findOrFail($nik);
if (request()->ajax()) {
return response()->json([
'pengguna' => $pengguna,
'anak' => $pengguna->anak
]);
}
$action = 'show';
return view('data_orangtua', compact('pengguna', 'action'));
}
/**
* Show the form for editing the specified resource.
*/
public function edit(string $nik)
{
$pengguna = Pengguna::with('anak')->where('role', 'parent')->findOrFail($nik);
$action = 'edit';
return view('data_orangtua', compact('pengguna', 'action'));
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, string $nik)
{
$validator = Validator::make($request->all(), [
'nama' => 'required|string|max:100',
'email' => 'nullable|email|max:100|unique:pengguna,email,' . $nik . ',nik',
'no_telp' => 'nullable|numeric|digits_between:1,15',
'alamat' => 'required|string',
'password' => 'nullable|string|min:8',
'nama_anak' => 'required|string|max:100',
'tempat_lahir' => 'required|string|max:100',
'tanggal_lahir' => 'required|date',
'jenis_kelamin' => 'required|in:Laki-laki,Perempuan',
'usia' => 'required|string|max:50',
]);
if ($validator->fails()) {
return redirect()->back()
->withErrors($validator)
->withInput();
}
$pengguna = Pengguna::findOrFail($nik);
$pengguna->update([
'nama' => $request->nama,
'email' => $request->email,
'no_telp' => $request->no_telp,
'alamat' => $request->alamat,
]);
if ($request->password) {
$pengguna->update([
'password' => bcrypt($request->password),
]);
}
// Check if anak relation exists, if not create it
if ($pengguna->anak->isEmpty()) {
Anak::create([
'pengguna_id' => $pengguna->id,
'nama_anak' => $request->nama_anak,
'tempat_lahir' => $request->tempat_lahir,
'tanggal_lahir' => $request->tanggal_lahir,
'jenis_kelamin' => $request->jenis_kelamin,
'usia' => $request->usia,
]);
} else {
// Update the first child
$pengguna->anak->first()->update([
'nama_anak' => $request->nama_anak,
'tempat_lahir' => $request->tempat_lahir,
'tanggal_lahir' => $request->tanggal_lahir,
'jenis_kelamin' => $request->jenis_kelamin,
'usia' => $request->usia,
]);
}
return redirect()->route('data_orangtua.index')
->with('success', 'Data orang tua berhasil diperbarui!');
}
/**
* Remove the specified resource from storage.
*/
public function destroy(string $nik)
{
$pengguna = Pengguna::findOrFail($nik);
$pengguna->delete();
return redirect()->route('data_orangtua.index')
->with('success', 'Data orang tua berhasil dihapus!');
}
}

View File

@ -0,0 +1,347 @@
<?php
namespace App\Http\Controllers;
use App\Models\Imunisasi;
use App\Models\Anak;
use App\Models\JenisImunisasi;
use App\Models\JadwalImunisasi;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
use Carbon\Carbon;
use App\Exports\ImunisasiExport;
use Maatwebsite\Excel\Facades\Excel;
class ImunisasiController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index(Request $request)
{
$search = $request->input('search');
$perPage = $request->input('perPage', 10); // Default 10 data per halaman
$query = Imunisasi::with(['anak', 'jenisImunisasi']);
if ($search) {
$query->whereHas('anak', function($q) use ($search) {
$q->where('nama_anak', 'like', "%{$search}%");
})
->orWhereHas('jenisImunisasi', function($q) use ($search) {
$q->where('nama', 'like', "%{$search}%");
})
->orWhere('status', 'like', "%{$search}%");
}
$imunisasi = $query->orderBy('created_at', 'desc')->paginate($perPage);
// Dapatkan semua jadwal imunisasi yang tersedia
$availableJadwal = $this->getAvailableJadwalImunisasi();
return view('imunisasi', compact('imunisasi', 'search', 'availableJadwal'));
}
/**
* Show the form for creating a new resource.
*/
public function create()
{
$action = 'create';
$dataAnak = Anak::all();
$jenisImunisasi = JenisImunisasi::all();
// Dapatkan semua jadwal imunisasi yang tersedia
$availableJadwal = $this->getAvailableJadwalImunisasi();
return view('imunisasi', compact('action', 'dataAnak', 'jenisImunisasi', 'availableJadwal'));
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request)
{
$validator = Validator::make($request->all(), [
'anak_id' => 'required|exists:anak,id',
'jenis_id' => 'required|exists:jenis_imunisasi,id',
'tanggal' => 'required|date',
'status' => 'required|in:' . implode(',', Imunisasi::$statusList),
]);
if ($validator->fails()) {
return redirect()->back()
->withErrors($validator)
->withInput();
}
// Validasi apakah umur anak sesuai dengan jenis imunisasi
$anak = Anak::findOrFail($request->anak_id);
$jenisImunisasi = JenisImunisasi::findOrFail($request->jenis_id);
$tanggalLahir = Carbon::parse($anak->tanggal_lahir);
$umurHari = $tanggalLahir->diffInDays(Carbon::parse($request->tanggal));
if ($umurHari < $jenisImunisasi->min_umur_hari || $umurHari > $jenisImunisasi->max_umur_hari) {
return redirect()->back()
->with('error', 'Umur anak (' . $umurHari . ' hari) tidak sesuai dengan rentang umur yang disarankan untuk imunisasi ' . $jenisImunisasi->nama . ' (' . $jenisImunisasi->min_umur_hari . '-' . $jenisImunisasi->max_umur_hari . ' hari)')
->withInput();
}
Imunisasi::create($request->all());
return redirect()->route('imunisasi.index')
->with('success', 'Data imunisasi berhasil ditambahkan!');
}
/**
* Display the specified resource.
*/
public function show(string $id)
{
$imunisasi = Imunisasi::with(['anak', 'jenisImunisasi'])->findOrFail($id);
if (request()->ajax()) {
return response()->json([
'imunisasi' => $imunisasi,
'anak' => $imunisasi->anak,
'jenisImunisasi' => $imunisasi->jenisImunisasi
]);
}
$action = 'show';
return view('imunisasi', compact('imunisasi', 'action'));
}
/**
* Show the form for editing the specified resource.
*/
public function edit(string $id)
{
$imunisasi = Imunisasi::with(['anak', 'jenisImunisasi'])->findOrFail($id);
if (request()->ajax()) {
// Get all children and immunization types for dropdowns
$dataAnak = Anak::all();
$jenisImunisasi = JenisImunisasi::all();
// Format data tanggal untuk input HTML
$imunisasi->tanggal = optional($imunisasi->tanggal)->format('Y-m-d');
// Convert ID ke integer untuk mempermudah perbandingan di JavaScript
$formattedData = [
'imunisasi' => [
'id' => (int)$imunisasi->id,
'anak_id' => (int)$imunisasi->anak_id,
'jenis_id' => (int)$imunisasi->jenis_id,
'tanggal' => $imunisasi->tanggal,
'status' => $imunisasi->status,
],
'anak' => $imunisasi->anak,
'jenisImunisasi' => $imunisasi->jenisImunisasi,
'dataAnak' => $dataAnak->map(function($anak) {
$anak->id = (int)$anak->id;
return $anak;
}),
'jenisImunisasiList' => $jenisImunisasi->map(function($jenis) {
$jenis->id = (int)$jenis->id;
return $jenis;
})
];
return response()->json($formattedData);
}
$dataAnak = Anak::all();
$jenisImunisasi = JenisImunisasi::all();
$action = 'edit';
return view('imunisasi', compact('imunisasi', 'action', 'dataAnak', 'jenisImunisasi'));
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, string $id)
{
// If this is a JSON request (likely from AJAX or API)
if ($request->expectsJson() || $request->ajax()) {
$validator = Validator::make($request->all(), [
'status' => 'required|in:' . implode(',', Imunisasi::$statusList),
]);
if ($validator->fails()) {
return response()->json(['errors' => $validator->errors()], 422);
}
$imunisasi = Imunisasi::findOrFail($id);
$imunisasi->status = $request->status;
$imunisasi->save();
return response()->json([
'success' => true,
'message' => 'Status imunisasi berhasil diperbarui!',
'data' => $imunisasi
]);
}
// Regular form submission
$validator = Validator::make($request->all(), [
'anak_id' => 'required|exists:anak,id',
'jenis_id' => 'required|exists:jenis_imunisasi,id',
'tanggal' => 'required|date',
'status' => 'required|in:' . implode(',', Imunisasi::$statusList),
]);
if ($validator->fails()) {
if ($request->ajax()) {
return response()->json(['errors' => $validator->errors()], 422);
}
return redirect()->back()
->withErrors($validator)
->withInput();
}
$imunisasi = Imunisasi::findOrFail($id);
$imunisasi->update($request->all());
if ($request->ajax()) {
return response()->json([
'success' => true,
'message' => 'Data imunisasi berhasil diperbarui!'
]);
}
return redirect()->route('imunisasi.index')
->with('success', 'Data imunisasi berhasil diperbarui!');
}
/**
* Remove the specified resource from storage.
*/
public function destroy(string $id)
{
$imunisasi = Imunisasi::findOrFail($id);
$imunisasi->delete();
return redirect()->route('imunisasi.index')
->with('success', 'Data imunisasi berhasil dihapus!');
}
/**
* Mendapatkan jadwal imunisasi yang tersedia untuk anak-anak berdasarkan umur
*/
private function getAvailableJadwalImunisasi()
{
// Dapatkan semua jadwal imunisasi yang akan datang
$upcomingJadwal = JadwalImunisasi::with('jenisImunisasi')
->where('tanggal', '>=', Carbon::today())
->orderBy('tanggal', 'asc')
->get();
// Dapatkan semua anak
$dataAnak = Anak::all();
$availableJadwal = [];
// Untuk setiap jadwal, cek anak-anak yang memenuhi syarat umur
foreach ($upcomingJadwal as $jadwal) {
$jenisImunisasi = $jadwal->jenisImunisasi;
if (!$jenisImunisasi) continue;
$eligibleChildren = [];
// Cek setiap anak
foreach ($dataAnak as $anak) {
$tanggalLahir = Carbon::parse($anak->tanggal_lahir);
$umurHari = $tanggalLahir->diffInDays(Carbon::now());
// Periksa apakah anak berada dalam rentang umur yang sesuai
if ($umurHari >= $jenisImunisasi->min_umur_hari && $umurHari <= $jenisImunisasi->max_umur_hari) {
// Cek apakah anak sudah terdaftar untuk imunisasi ini
$existingImunisasi = Imunisasi::where('anak_id', $anak->id)
->where('jenis_id', $jadwal->jenis_imunisasi_id)
->first();
$eligibleChildren[] = [
'anak' => $anak,
'umur_hari' => $umurHari,
'already_registered' => $existingImunisasi ? true : false,
'existing_imunisasi' => $existingImunisasi
];
}
}
// Jika ada anak yang memenuhi syarat, tambahkan jadwal ke daftar
if (count($eligibleChildren) > 0) {
$availableJadwal[] = [
'jadwal' => $jadwal,
'jenis_imunisasi' => $jenisImunisasi,
'eligible_children' => $eligibleChildren
];
}
}
return $availableJadwal;
}
/**
* Mendaftarkan anak untuk imunisasi dari jadwal yang sudah ada
*/
public function registerFromJadwal(Request $request)
{
$validator = Validator::make($request->all(), [
'anak_id' => 'required|exists:anak,id',
'jadwal_imunisasi_id' => 'required|exists:jadwal_imunisasi,id',
]);
if ($validator->fails()) {
return redirect()->back()
->withErrors($validator)
->withInput();
}
// Dapatkan jadwal imunisasi
$jadwal = JadwalImunisasi::with('jenisImunisasi')->findOrFail($request->jadwal_imunisasi_id);
// Verifikasi anak
$anak = Anak::findOrFail($request->anak_id);
// Verifikasi umur anak
$tanggalLahir = Carbon::parse($anak->tanggal_lahir);
$umurHari = $tanggalLahir->diffInDays(Carbon::now());
if ($umurHari < $jadwal->jenisImunisasi->min_umur_hari || $umurHari > $jadwal->jenisImunisasi->max_umur_hari) {
return redirect()->back()
->with('error', 'Umur anak (' . $umurHari . ' hari) tidak sesuai dengan rentang umur yang disarankan untuk imunisasi ' . $jadwal->jenisImunisasi->nama . ' (' . $jadwal->jenisImunisasi->min_umur_hari . '-' . $jadwal->jenisImunisasi->max_umur_hari . ' hari)')
->withInput();
}
// Cek apakah anak sudah terdaftar untuk imunisasi ini
$existingImunisasi = Imunisasi::where('anak_id', $anak->id)
->where('jenis_id', $jadwal->jenis_imunisasi_id)
->first();
if ($existingImunisasi) {
return redirect()->back()
->with('error', 'Anak sudah terdaftar untuk imunisasi ' . $jadwal->jenisImunisasi->nama)
->withInput();
}
// Buat data imunisasi baru
Imunisasi::create([
'anak_id' => $anak->id,
'jenis_id' => $jadwal->jenis_imunisasi_id,
'tanggal' => $jadwal->tanggal,
'status' => Imunisasi::STATUS_BELUM
]);
return redirect()->route('imunisasi.index')
->with('success', 'Anak berhasil didaftarkan untuk imunisasi!');
}
public function excel()
{
return Excel::download(new ImunisasiExport, 'data-imunisasi.xlsx');
}
}

View File

@ -0,0 +1,143 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class InformasiController extends Controller
{
public function index()
{
$informasi = [
'dashboard' => [
'judul' => 'Dashboard',
'deskripsi' => 'Halaman dashboard menampilkan ringkasan statistik penting untuk posyandu, seperti:',
'fitur' => [
'Total Kasus Stunting',
'Total Pengguna',
'Total Data Anak',
'Total Petugas',
'Dan terdapat fitur lainnya'
]
],
'jadwal' => [
'judul' => 'Jadwal',
'deskripsi' => 'Halaman jadwal berisi informasi dan manajemen jadwal posyandu, meliputi:',
'fitur' => [
'Jadwal imunisasi anak',
'Jadwal pemberian vitamin',
'Jadwal pemeriksaan kesehatan',
'Penambahan, pengubahan, dan penghapusan jadwal',
'Pelacakan status implementasi jadwal'
]
],
'petugas' => [
'judul' => 'Petugas',
'deskripsi' => 'Halaman petugas digunakan untuk mengelola data petugas posyandu, dengan fitur:',
'fitur' => [
'Tambah petugas baru',
'Edit data petugas',
'Hapus petugas (khusus kader utama)',
'Pembagian status kader (utama dan anggota)',
'Pencarian dan filter data petugas'
]
],
'artikel' => [
'judul' => 'Artikel',
'deskripsi' => 'Halaman artikel untuk berbagi informasi kesehatan dan edukasi, dengan fitur:',
'fitur' => [
'Tambah artikel baru',
'Edit artikel',
'Hapus artikel',
'Tampilan daftar artikel',
'Detail artikel'
]
],
'pengguna' => [
'judul' => 'Data Pengguna',
'deskripsi' => 'Halaman data pengguna untuk mengelola akun pengguna sistem, mencakup:',
'fitur' => [
'Daftar pengguna',
'Tambah pengguna baru',
'Edit data pengguna',
'Hapus pengguna',
'Pengaturan hak akses'
]
],
'perkembangan_anak' => [
'judul' => 'Perkembangan Anak',
'deskripsi' => 'Halaman perkembangan anak digunakan untuk memantau dan mencatat pertumbuhan serta perkembangan anak, dengan fitur:',
'fitur' => [
'Rekam data perkembangan anak',
'Grafik pertumbuhan (berat badan, tinggi badan)',
'Penilaian status gizi',
'Riwayat pemeriksaan kesehatan',
'Catatan perkembangan motorik dan kognitif',
'Filter dan pencarian data anak'
]
],
'imunisasi' => [
'judul' => 'Imunisasi',
'deskripsi' => 'Halaman imunisasi untuk mengelola dan memantau proses imunisasi anak, mencakup:',
'fitur' => [
'Daftar jenis imunisasi',
'Rekam data imunisasi anak',
'Jadwal imunisasi',
'Status kelengkapan imunisasi',
'Pengingat imunisasi yang akan datang',
'Laporan status imunisasi'
]
],
'data_orangtua' => [
'judul' => 'Data Orang Tua',
'deskripsi' => 'Halaman data orang tua untuk mengelola informasi kontak dan identitas orang tua/wali, dengan:',
'fitur' => [
'Tambah data orang tua',
'Edit informasi orang tua',
'Hapus data orang tua',
'Informasi kontak',
'Alamat lengkap',
'Pencarian dan filter data'
]
],
'data_anak' => [
'judul' => 'Data Anak',
'deskripsi' => 'Halaman data anak untuk mencatat dan mengelola informasi anak, meliputi:',
'fitur' => [
'Tambah data anak baru',
'Edit profil anak',
'Hapus data anak',
'Informasi identitas anak',
'Riwayat kesehatan',
'Pencarian dan filter data anak'
]
],
'vitamin' => [
'judul' => 'Vitamin',
'deskripsi' => 'Halaman vitamin untuk mengelola pemberian vitamin dan suplemen pada anak, dengan:',
'fitur' => [
'Daftar jenis vitamin',
'Rekam data pemberian vitamin',
'Jadwal pemberian vitamin',
'Status pemberian vitamin',
'Pengingat jadwal vitamin',
'Laporan konsumsi vitamin'
]
],
'stunting' => [
'judul' => 'Stunting',
'deskripsi' => 'Halaman stunting untuk memantau dan menangani kasus stunting pada anak, mencakup:',
'fitur' => [
'Penilaian status stunting',
'Perhitungan indeks antropometri',
'Riwayat pemeriksaan stunting',
'Rekomendasi intervensi',
'Grafik perkembangan status gizi',
'Pencatatan riwayat penanganan'
]
]
];
return view('informasi', compact('informasi'));
}
}

View File

@ -0,0 +1,435 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\JadwalPemeriksaan;
use App\Models\JadwalImunisasi;
use App\Models\JadwalVitamin;
use App\Models\JenisImunisasi;
use App\Models\JenisVitamin;
use Illuminate\Support\Collection;
use Illuminate\Pagination\LengthAwarePaginator;
class JadwalController extends Controller
{
public function index(Request $request)
{
// Get perPage parameter, default to 10
$perPage = $request->input('perPage', 10);
$jenis = $request->input('jenis', 'semua');
$search = $request->input('search', '');
// Prepare queries for each jadwal type
$queryPemeriksaan = JadwalPemeriksaan::orderBy('tanggal', 'desc')->orderBy('waktu', 'desc')->orderBy('created_at', 'desc');
$queryImunisasi = JadwalImunisasi::with('jenisImunisasi')->orderBy('tanggal', 'desc')->orderBy('waktu', 'desc')->orderBy('created_at', 'desc');
$queryVitamin = JadwalVitamin::with('jenisVitamin')->orderBy('tanggal', 'desc')->orderBy('waktu', 'desc')->orderBy('created_at', 'desc');
// Apply search filter if provided
if ($request->has('search') && $request->search != '') {
$search = $request->search;
$queryPemeriksaan->where(function($q) use ($search) {
$q->where('judul', 'like', "%{$search}%")
->orWhere('tanggal', 'like', "%{$search}%")
->orWhere('waktu', 'like', "%{$search}%");
});
// For Imunisasi, search through relationship
$queryImunisasi->whereHas('jenisImunisasi', function($q) use ($search) {
$q->where('nama', 'like', "%{$search}%");
})->orWhere('tanggal', 'like', "%{$search}%");
// For Vitamin, search through relationship
$queryVitamin->whereHas('jenisVitamin', function($q) use ($search) {
$q->where('nama', 'like', "%{$search}%");
})->orWhere('tanggal', 'like', "%{$search}%");
}
// Get data based on the selected type
$jadwalPemeriksaan = ($jenis == 'semua' || $jenis == 'pemeriksaan') ? $queryPemeriksaan->paginate($perPage) : collect([]);
$jadwalImunisasi = ($jenis == 'semua' || $jenis == 'imunisasi') ? $queryImunisasi->paginate($perPage) : collect([]);
$jadwalVitamin = ($jenis == 'semua' || $jenis == 'vitamin') ? $queryVitamin->paginate($perPage) : collect([]);
// Create a collection of all jadwals for the combined view
$allJadwal = new Collection();
// Add pemeriksaan items with type information
if ($jenis == 'semua' || $jenis == 'pemeriksaan') {
foreach ($queryPemeriksaan->get() as $jp) {
$allJadwal->push((object)[
'id' => $jp->id,
'nama' => $jp->judul,
'jenis' => 'pemeriksaan rutin',
'tanggal' => $jp->tanggal,
'waktu' => $jp->waktu,
'is_implemented' => $jp->is_implemented,
'min_umur_hari' => null,
'max_umur_hari' => null,
'min_umur_bulan' => null,
'max_umur_bulan' => null,
'keterangan' => null
]);
}
}
// Add imunisasi items with type information
if ($jenis == 'semua' || $jenis == 'imunisasi') {
foreach ($queryImunisasi->get() as $ji) {
$allJadwal->push((object)[
'id' => $ji->id,
'nama' => $ji->jenisImunisasi->nama ?? 'Imunisasi',
'jenis' => 'imunisasi',
'tanggal' => $ji->tanggal,
'waktu' => $ji->waktu,
'is_implemented' => $ji->is_implemented,
'min_umur_hari' => $ji->jenisImunisasi->min_umur_hari ?? null,
'max_umur_hari' => $ji->jenisImunisasi->max_umur_hari ?? null,
'min_umur_bulan' => null,
'max_umur_bulan' => null,
'keterangan' => $ji->jenisImunisasi->keterangan ?? null
]);
}
}
// Add vitamin items with type information
if ($jenis == 'semua' || $jenis == 'vitamin') {
foreach ($queryVitamin->get() as $jv) {
$allJadwal->push((object)[
'id' => $jv->id,
'nama' => $jv->jenisVitamin->nama ?? 'Vitamin',
'jenis' => 'vitamin',
'tanggal' => $jv->tanggal,
'waktu' => $jv->waktu,
'is_implemented' => $jv->is_implemented,
'min_umur_hari' => null,
'max_umur_hari' => null,
'min_umur_bulan' => $jv->jenisVitamin->min_umur_bulan ?? null,
'max_umur_bulan' => $jv->jenisVitamin->max_umur_bulan ?? null,
'keterangan' => $jv->jenisVitamin->keterangan ?? null
]);
}
}
// Sort the combined collection - urut dari yang terbaru
$allJadwal = $allJadwal->sortByDesc('tanggal')->sortByDesc('waktu')->sortByDesc('created_at');
// Reset index dan tambahkan nomor urut
$allJadwal = $allJadwal->values();
// Tambahkan nomor urut ke setiap item
$allJadwal = $allJadwal->map(function($item, $index) {
$item->nomor_urut = $index + 1;
return $item;
});
// Paginate the combined collection
$page = request()->input('page', 1);
$jadwal = new LengthAwarePaginator(
$allJadwal->forPage($page, $perPage),
$allJadwal->count(),
$perPage,
$page,
['path' => request()->url(), 'query' => request()->query()]
);
// Get data for dropdowns
$jenisImunisasi = JenisImunisasi::all();
$jenisVitamin = JenisVitamin::all();
return view('jadwal', compact(
'jadwal',
'jadwalPemeriksaan',
'jadwalImunisasi',
'jadwalVitamin',
'jenisImunisasi',
'jenisVitamin',
'jenis',
'search'
));
}
// Create Jadwal Pemeriksaan
public function storePemeriksaan(Request $request)
{
$request->validate([
'tanggal' => 'required|date',
'waktu' => 'required|date_format:H:i',
]);
try {
JadwalPemeriksaan::create([
'judul' => 'Posyandu',
'tanggal' => $request->tanggal,
'waktu' => $request->waktu,
]);
return redirect()->route('jadwal')->with('success', 'Jadwal pemeriksaan berhasil ditambahkan.');
} catch (\Exception $e) {
return redirect()->route('jadwal')->with('error', 'Gagal menambahkan jadwal pemeriksaan: ' . $e->getMessage());
}
}
// Create Jadwal Imunisasi
public function storeImunisasi(Request $request)
{
$request->validate([
'jenis_imunisasi_id' => 'required|exists:jenis_imunisasi,id',
'tanggal' => 'required|date',
'waktu' => 'required|date_format:H:i',
]);
try {
JadwalImunisasi::create([
'jenis_imunisasi_id' => $request->jenis_imunisasi_id,
'tanggal' => $request->tanggal,
'waktu' => $request->waktu,
'is_implemented' => false,
]);
return redirect()->route('jadwal')->with('success', 'Jadwal imunisasi berhasil ditambahkan.');
} catch (\Exception $e) {
return redirect()->route('jadwal')->with('error', 'Gagal menambahkan jadwal imunisasi: ' . $e->getMessage());
}
}
// Create Jadwal Vitamin
public function storeVitamin(Request $request)
{
$request->validate([
'jenis_vitamin_id' => 'required|exists:jenis_vitamin,id',
'tanggal' => 'required|date',
'waktu' => 'required|date_format:H:i',
]);
try {
JadwalVitamin::create([
'jenis_vitamin_id' => $request->jenis_vitamin_id,
'tanggal' => $request->tanggal,
'waktu' => $request->waktu,
'is_implemented' => false,
]);
return redirect()->route('jadwal')->with('success', 'Jadwal vitamin berhasil ditambahkan.');
} catch (\Exception $e) {
return redirect()->route('jadwal')->with('error', 'Gagal menambahkan jadwal vitamin: ' . $e->getMessage());
}
}
// Delete Jadwal Pemeriksaan
public function destroyPemeriksaan($id)
{
try {
$jadwal = JadwalPemeriksaan::findOrFail($id);
$jadwal->delete();
return redirect()->route('jadwal')->with('success', 'Jadwal pemeriksaan berhasil dihapus.');
} catch (\Exception $e) {
return redirect()->route('jadwal')->with('error', 'Gagal menghapus jadwal pemeriksaan: ' . $e->getMessage());
}
}
// Delete Jadwal Imunisasi
public function destroyImunisasi($id)
{
try {
$jadwal = JadwalImunisasi::findOrFail($id);
$jadwal->delete();
return redirect()->route('jadwal')->with('success', 'Jadwal imunisasi berhasil dihapus.');
} catch (\Exception $e) {
return redirect()->route('jadwal')->with('error', 'Gagal menghapus jadwal imunisasi: ' . $e->getMessage());
}
}
// Delete Jadwal Vitamin
public function destroyVitamin($id)
{
try {
$jadwal = JadwalVitamin::findOrFail($id);
$jadwal->delete();
return redirect()->route('jadwal')->with('success', 'Jadwal vitamin berhasil dihapus.');
} catch (\Exception $e) {
return redirect()->route('jadwal')->with('error', 'Gagal menghapus jadwal vitamin: ' . $e->getMessage());
}
}
// Edit Jadwal Pemeriksaan
public function editPemeriksaan($id)
{
$jadwal = JadwalPemeriksaan::findOrFail($id);
return response()->json([
'jadwal' => $jadwal
]);
}
// Edit Jadwal Imunisasi
public function editImunisasi($id)
{
$jadwal = JadwalImunisasi::with('jenisImunisasi')->findOrFail($id);
$jenisImunisasi = JenisImunisasi::all();
return response()->json([
'jadwal' => $jadwal,
'jenisImunisasi' => $jenisImunisasi
]);
}
// Edit Jadwal Vitamin
public function editVitamin($id)
{
$jadwal = JadwalVitamin::with('jenisVitamin')->findOrFail($id);
$jenisVitamin = JenisVitamin::all();
return response()->json([
'jadwal' => $jadwal,
'jenisVitamin' => $jenisVitamin
]);
}
// Update Jadwal Pemeriksaan
public function updatePemeriksaan(Request $request, $id)
{
try {
$validator = \Validator::make($request->all(), [
'tanggal' => 'required|date',
'waktu' => 'required|date_format:H:i',
]);
if ($validator->fails()) {
if ($request->expectsJson() || $request->ajax()) {
return response()->json([
'success' => false,
'message' => 'Validasi gagal',
'errors' => $validator->errors()
], 422);
}
return redirect()->back()->withErrors($validator)->withInput();
}
$jadwal = JadwalPemeriksaan::findOrFail($id);
$jadwal->update([
'judul' => 'Posyandu',
'tanggal' => $request->tanggal,
'waktu' => $request->waktu,
]);
if ($request->expectsJson() || $request->ajax()) {
return response()->json([
'success' => true,
'message' => 'Jadwal pemeriksaan berhasil diperbarui.'
]);
}
return redirect()->route('jadwal')->with('success', 'Jadwal pemeriksaan berhasil diperbarui.');
} catch (\Exception $e) {
if ($request->expectsJson() || $request->ajax()) {
return response()->json([
'success' => false,
'message' => 'Gagal memperbarui jadwal pemeriksaan: ' . $e->getMessage()
], 500);
}
return redirect()->route('jadwal')->with('error', 'Gagal memperbarui jadwal pemeriksaan: ' . $e->getMessage());
}
}
// Update Jadwal Imunisasi
public function updateImunisasi(Request $request, $id)
{
try {
$validator = \Validator::make($request->all(), [
'jenis_imunisasi_id' => 'required|exists:jenis_imunisasi,id',
'tanggal' => 'required|date',
'waktu' => 'required|date_format:H:i',
]);
if ($validator->fails()) {
if ($request->expectsJson() || $request->ajax()) {
return response()->json([
'success' => false,
'message' => 'Validasi gagal',
'errors' => $validator->errors()
], 422);
}
return redirect()->back()->withErrors($validator)->withInput();
}
$jadwal = JadwalImunisasi::findOrFail($id);
$jadwal->update([
'jenis_imunisasi_id' => $request->jenis_imunisasi_id,
'tanggal' => $request->tanggal,
'waktu' => $request->waktu,
'is_implemented' => $jadwal->is_implemented ?? false,
]);
if ($request->expectsJson() || $request->ajax()) {
return response()->json([
'success' => true,
'message' => 'Jadwal imunisasi berhasil diperbarui.'
]);
}
return redirect()->route('jadwal')->with('success', 'Jadwal imunisasi berhasil diperbarui.');
} catch (\Exception $e) {
if ($request->expectsJson() || $request->ajax()) {
return response()->json([
'success' => false,
'message' => 'Gagal memperbarui jadwal imunisasi: ' . $e->getMessage()
], 500);
}
return redirect()->route('jadwal')->with('error', 'Gagal memperbarui jadwal imunisasi: ' . $e->getMessage());
}
}
// Update Jadwal Vitamin
public function updateVitamin(Request $request, $id)
{
try {
$validator = \Validator::make($request->all(), [
'jenis_vitamin_id' => 'required|exists:jenis_vitamin,id',
'tanggal' => 'required|date',
'waktu' => 'required|date_format:H:i',
]);
if ($validator->fails()) {
if ($request->expectsJson() || $request->ajax()) {
return response()->json([
'success' => false,
'message' => 'Validasi gagal',
'errors' => $validator->errors()
], 422);
}
return redirect()->back()->withErrors($validator)->withInput();
}
$jadwal = JadwalVitamin::findOrFail($id);
$jadwal->update([
'jenis_vitamin_id' => $request->jenis_vitamin_id,
'tanggal' => $request->tanggal,
'waktu' => $request->waktu,
'is_implemented' => $jadwal->is_implemented ?? false,
]);
if ($request->expectsJson() || $request->ajax()) {
return response()->json([
'success' => true,
'message' => 'Jadwal vitamin berhasil diperbarui.'
]);
}
return redirect()->route('jadwal')->with('success', 'Jadwal vitamin berhasil diperbarui.');
} catch (\Exception $e) {
if ($request->expectsJson() || $request->ajax()) {
return response()->json([
'success' => false,
'message' => 'Gagal memperbarui jadwal vitamin: ' . $e->getMessage()
], 500);
}
return redirect()->route('jadwal')->with('error', 'Gagal memperbarui jadwal vitamin: ' . $e->getMessage());
}
}
}

View File

@ -0,0 +1,122 @@
<?php
namespace App\Http\Controllers;
use App\Models\JenisImunisasi;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
class JenisImunisasiController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index(Request $request)
{
$search = $request->input('search');
$perPage = $request->input('perPage', 10); // Default 10 data per halaman
$query = JenisImunisasi::query();
if ($search) {
$query->where('nama', 'like', "%{$search}%")
->orWhere('keterangan', 'like', "%{$search}%");
}
$jenisImunisasi = $query->orderBy('created_at', 'desc')->paginate($perPage);
return view('jenis_imunisasi', compact('jenisImunisasi', 'search'));
}
/**
* Show the form for creating a new resource.
*/
public function create()
{
$action = 'create';
$jenisOptions = JenisImunisasi::$jenisImunisasi; // Mengambil enum dari model
return view('jenis_imunisasi', compact('action', 'jenisOptions'));
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request)
{
$validator = Validator::make($request->all(), [
'nama' => 'required|in:' . implode(',', JenisImunisasi::$jenisImunisasi),
'min_umur_hari' => 'required|integer|min:0',
'max_umur_hari' => 'required|integer|gte:min_umur_hari',
'keterangan' => 'nullable|string',
]);
if ($validator->fails()) {
return redirect()->back()
->withErrors($validator)
->withInput();
}
JenisImunisasi::create($request->all());
return redirect()->route('jenis_imunisasi.index')
->with('success', 'Data jenis imunisasi berhasil ditambahkan!');
}
/**
* Display the specified resource.
*/
public function show(string $id)
{
$jenisImunisasi = JenisImunisasi::findOrFail($id);
$action = 'show';
return view('jenis_imunisasi', compact('jenisImunisasi', 'action'));
}
/**
* Show the form for editing the specified resource.
*/
public function edit(string $id)
{
$jenisImunisasi = JenisImunisasi::findOrFail($id);
$jenisOptions = JenisImunisasi::$jenisImunisasi; // Mengambil enum dari model
$action = 'edit';
return view('jenis_imunisasi', compact('jenisImunisasi', 'action', 'jenisOptions'));
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, string $id)
{
$validator = Validator::make($request->all(), [
'nama' => 'required|in:' . implode(',', JenisImunisasi::$jenisImunisasi),
'min_umur_hari' => 'required|integer|min:0',
'max_umur_hari' => 'required|integer|gte:min_umur_hari',
'keterangan' => 'nullable|string',
]);
if ($validator->fails()) {
return redirect()->back()
->withErrors($validator)
->withInput();
}
$jenisImunisasi = JenisImunisasi::findOrFail($id);
$jenisImunisasi->update($request->all());
return redirect()->route('jenis_imunisasi.index')
->with('success', 'Data jenis imunisasi berhasil diperbarui!');
}
/**
* Remove the specified resource from storage.
*/
public function destroy(string $id)
{
$jenisImunisasi = JenisImunisasi::findOrFail($id);
$jenisImunisasi->delete();
return redirect()->route('jenis_imunisasi.index')
->with('success', 'Data jenis imunisasi berhasil dihapus!');
}
}

View File

@ -0,0 +1,122 @@
<?php
namespace App\Http\Controllers;
use App\Models\JenisVitamin;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
class JenisVitaminController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index(Request $request)
{
$search = $request->input('search');
$perPage = $request->input('perPage', 10); // Default 10 data per halaman
$query = JenisVitamin::query();
if ($search) {
$query->where('nama', 'like', "%{$search}%")
->orWhere('keterangan', 'like', "%{$search}%");
}
$jenisVitamin = $query->orderBy('created_at', 'desc')->paginate($perPage);
return view('jenis_vitamin', compact('jenisVitamin', 'search'));
}
/**
* Show the form for creating a new resource.
*/
public function create()
{
$action = 'create';
$jenisOptions = JenisVitamin::$jenisVitamin; // Mengambil enum dari model
return view('jenis_vitamin', compact('action', 'jenisOptions'));
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request)
{
$validator = Validator::make($request->all(), [
'nama' => 'required|in:' . implode(',', JenisVitamin::$jenisVitamin),
'min_umur_bulan' => 'required|integer|min:0',
'max_umur_bulan' => 'required|integer|gte:min_umur_bulan',
'keterangan' => 'nullable|string',
]);
if ($validator->fails()) {
return redirect()->back()
->withErrors($validator)
->withInput();
}
JenisVitamin::create($request->all());
return redirect()->route('jenis_vitamin.index')
->with('success', 'Data jenis vitamin berhasil ditambahkan!');
}
/**
* Display the specified resource.
*/
public function show(string $id)
{
$jenisVitamin = JenisVitamin::findOrFail($id);
$action = 'show';
return view('jenis_vitamin', compact('jenisVitamin', 'action'));
}
/**
* Show the form for editing the specified resource.
*/
public function edit(string $id)
{
$jenisVitamin = JenisVitamin::findOrFail($id);
$jenisOptions = JenisVitamin::$jenisVitamin; // Mengambil enum dari model
$action = 'edit';
return view('jenis_vitamin', compact('jenisVitamin', 'action', 'jenisOptions'));
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, string $id)
{
$validator = Validator::make($request->all(), [
'nama' => 'required|in:' . implode(',', JenisVitamin::$jenisVitamin),
'min_umur_bulan' => 'required|integer|min:0',
'max_umur_bulan' => 'required|integer|gte:min_umur_bulan',
'keterangan' => 'nullable|string',
]);
if ($validator->fails()) {
return redirect()->back()
->withErrors($validator)
->withInput();
}
$jenisVitamin = JenisVitamin::findOrFail($id);
$jenisVitamin->update($request->all());
return redirect()->route('jenis_vitamin.index')
->with('success', 'Data jenis vitamin berhasil diperbarui!');
}
/**
* Remove the specified resource from storage.
*/
public function destroy(string $id)
{
$jenisVitamin = JenisVitamin::findOrFail($id);
$jenisVitamin->delete();
return redirect()->route('jenis_vitamin.index')
->with('success', 'Data jenis vitamin berhasil dihapus!');
}
}

View File

@ -0,0 +1,205 @@
<?php
namespace App\Http\Controllers;
use App\Models\Pengguna;
use App\Models\Anak;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
use App\Exports\PenggunaExport;
use Maatwebsite\Excel\Facades\Excel;
class PenggunaController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index(Request $request)
{
$search = $request->input('search');
$perPage = $request->input('perPage', 10); // Default 10 data per halaman
$query = Pengguna::where('role', 'parent');
if ($search) {
$query->where(function($q) use ($search) {
$q->where('nik', 'like', "%{$search}%")
->orWhere('nama', 'like', "%{$search}%")
->orWhere('no_telp', 'like', "%{$search}%");
});
}
$pengguna = $query->orderBy('created_at', 'desc')->paginate($perPage);
// Load anak relation for each pengguna
$pengguna->each(function ($item) {
$item->load('anak');
});
return view('pengguna', compact('pengguna', 'search'));
}
/**
* Show the form for creating a new resource.
*/
public function create()
{
$action = 'create';
return view('pengguna', compact('action'));
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request)
{
$validator = Validator::make($request->all(), [
'nik' => 'required|numeric|digits:16|unique:pengguna,nik',
'nama' => 'required|string|max:100',
'email' => 'nullable|email|max:100|unique:pengguna,email',
'password' => 'required|string|min:8',
'no_telp' => 'nullable|numeric|digits_between:1,15',
'alamat' => 'required|string',
'nama_anak' => 'required|string|max:100',
'tempat_lahir' => 'required|string|max:100',
'tanggal_lahir' => 'required|date',
'jenis_kelamin' => 'required|in:Laki-laki,Perempuan',
'usia' => 'required|string|max:50',
]);
if ($validator->fails()) {
return redirect()->back()
->withErrors($validator)
->withInput();
}
$pengguna = Pengguna::create([
'nik' => $request->nik,
'nama' => $request->nama,
'email' => $request->email,
'password' => bcrypt($request->password),
'role' => 'parent',
'no_telp' => $request->no_telp,
'alamat' => $request->alamat,
]);
Anak::create([
'pengguna_id' => $pengguna->id,
'nama_anak' => $request->nama_anak,
'tempat_lahir' => $request->tempat_lahir,
'tanggal_lahir' => $request->tanggal_lahir,
'jenis_kelamin' => $request->jenis_kelamin,
'usia' => $request->usia,
]);
return redirect()->route('pengguna.index')
->with('success', 'Data pengguna berhasil ditambahkan!');
}
/**
* Display the specified resource.
*/
public function show(string $nik)
{
$pengguna = Pengguna::with('anak')->where('role', 'parent')->where('nik', $nik)->firstOrFail();
if (request()->ajax()) {
return response()->json([
'pengguna' => $pengguna,
'anak' => $pengguna->anak
]);
}
return redirect()->route('pengguna.index');
}
/**
* Show the form for editing the specified resource.
*/
public function edit(string $nik)
{
$pengguna = Pengguna::with('anak')->where('role', 'parent')->findOrFail($nik);
$action = 'edit';
return view('pengguna', compact('pengguna', 'action'));
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, string $nik)
{
$validator = Validator::make($request->all(), [
'nama' => 'required|string|max:100',
'email' => 'nullable|email|max:100|unique:pengguna,email,' . $nik . ',nik',
'no_telp' => 'nullable|numeric|digits_between:1,15',
'alamat' => 'required|string',
'password' => 'nullable|string|min:8',
'nama_anak' => 'required|string|max:100',
'tempat_lahir' => 'required|string|max:100',
'tanggal_lahir' => 'required|date',
'jenis_kelamin' => 'required|in:Laki-laki,Perempuan',
'usia' => 'required|string|max:50',
]);
if ($validator->fails()) {
return redirect()->back()
->withErrors($validator)
->withInput();
}
$pengguna = Pengguna::with('anak')->findOrFail($nik);
$pengguna->update([
'nama' => $request->nama,
'email' => $request->email,
'no_telp' => $request->no_telp,
'alamat' => $request->alamat,
]);
if ($request->password) {
$pengguna->update([
'password' => bcrypt($request->password),
]);
}
// Check if anak relation exists, if not create it
if ($pengguna->anak->isEmpty()) {
Anak::create([
'pengguna_id' => $pengguna->id,
'nama_anak' => $request->nama_anak,
'tempat_lahir' => $request->tempat_lahir,
'tanggal_lahir' => $request->tanggal_lahir,
'jenis_kelamin' => $request->jenis_kelamin,
'usia' => $request->usia,
]);
} else {
// Update the first child
$pengguna->anak->first()->update([
'nama_anak' => $request->nama_anak,
'tempat_lahir' => $request->tempat_lahir,
'tanggal_lahir' => $request->tanggal_lahir,
'jenis_kelamin' => $request->jenis_kelamin,
'usia' => $request->usia,
]);
}
return redirect()->route('pengguna.index')
->with('success', 'Data pengguna berhasil diperbarui!');
}
/**
* Remove the specified resource from storage.
*/
public function destroy(string $nik)
{
$pengguna = Pengguna::findOrFail($nik);
$pengguna->delete();
return redirect()->route('pengguna.index')
->with('success', 'Data pengguna berhasil dihapus!');
}
public function excel()
{
return Excel::download(new PenggunaExport, 'data-pengguna.xlsx');
}
}

View File

@ -0,0 +1,342 @@
<?php
namespace App\Http\Controllers;
use App\Models\PerkembanganAnak;
use App\Models\Anak;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;
use App\Exports\PerkembanganExport;
use Maatwebsite\Excel\Facades\Excel;
class PerkembanganAnakController extends Controller
{
public function index(Request $request)
{
$search = $request->input('search');
$perPage = $request->input('perPage', 10); // Default 10 data per halaman
// Ambil semua anak yang memiliki setidaknya satu data perkembangan
// Muat relasi perkembangan anak terbaru
$query = Anak::has('perkembanganAnak')->with(['perkembanganAnak' => function($query) {
$query->orderBy('tanggal', 'desc')->orderBy('created_at', 'desc'); // Ambil yang terbaru berdasarkan tanggal
}, 'pengguna']);
if ($search) {
$query->where(function($q) use ($search) {
$q->where('nama_anak', 'like', "%{$search}%")
->orWhereHas('perkembanganAnak', function($sq) use ($search) {
$sq->where('tanggal', 'like', "%{$search}%");
});
});
}
// Dapatkan hasil paginasi dari query Anak
$anakPaginator = $query->orderBy('created_at', 'desc')->paginate($perPage);
// Sekarang, proses koleksi Anak di halaman saat ini untuk mendapatkan data perkembangan terbaru
$perkembanganCollection = collect();
foreach ($anakPaginator as $anak) {
if ($anak->perkembanganAnak && $anak->perkembanganAnak->count() > 0) {
$latest = $anak->perkembanganAnak->first(); // Ambil data perkembangan terbaru
if ($latest) {
$latest->setRelation('anak', $anak); // Tambahkan relasi anak
$perkembanganCollection->push($latest);
}
}
}
// Gunakan paginator yang ada tetapi dengan koleksi baru
$perkembangan = new \Illuminate\Pagination\LengthAwarePaginator(
$perkembanganCollection,
$anakPaginator->total(),
$anakPaginator->perPage(),
$anakPaginator->currentPage(),
['path' => \Illuminate\Support\Facades\Request::url()]
);
// Set query string dari request asli ke paginator
$perkembangan->appends($request->query());
return view('perkembangan_anak', compact('perkembangan', 'search'));
}
public function create()
{
$action = 'create';
$dataAnak = Anak::all();
return view('perkembangan_anak', compact('action', 'dataAnak'));
}
public function store(Request $request)
{
$validator = Validator::make($request->all(), [
'anak_id' => 'required|exists:anak,id',
'tanggal' => 'required|date',
'berat_badan' => 'required|numeric|between:0,999.99',
'tinggi_badan' => 'required|numeric|between:0,999.99',
]);
if ($validator->fails()) {
if ($request->ajax()) {
return response()->json([
'success' => false,
'message' => 'Validasi gagal',
'errors' => $validator->errors()
], 422);
}
return redirect()->back()
->withErrors($validator)
->withInput();
}
try {
$perkembangan = PerkembanganAnak::create($request->all());
if ($request->ajax()) {
return response()->json([
'success' => true,
'message' => 'Data perkembangan anak berhasil ditambahkan!',
'data' => $perkembangan
]);
}
return redirect()->route('perkembangan.index')
->with('success', 'Data perkembangan anak berhasil ditambahkan!');
} catch (\Exception $e) {
\Log::error('Error storing perkembangan: ' . $e->getMessage());
if ($request->ajax()) {
return response()->json([
'success' => false,
'message' => 'Gagal menyimpan data: ' . $e->getMessage()
], 500);
}
return redirect()->back()
->with('error', 'Gagal menyimpan data: ' . $e->getMessage())
->withInput();
}
}
public function show($id)
{
try {
$perkembangan = PerkembanganAnak::with(['anak.pengguna'])->findOrFail($id);
if (request()->ajax()) {
return response()->json([
'success' => true,
'perkembangan' => $perkembangan,
'anak' => $perkembangan->anak
]);
}
$action = 'show';
return view('perkembangan_anak', compact('perkembangan', 'action'));
} catch (\Exception $e) {
\Log::error('Error in show perkembangan: ' . $e->getMessage());
if (request()->ajax()) {
return response()->json([
'success' => false,
'message' => 'Terjadi kesalahan: ' . $e->getMessage()
], 500);
}
return redirect()->route('perkembangan.index')
->with('error', 'Terjadi kesalahan saat mengambil data: ' . $e->getMessage());
}
}
public function edit($id)
{
try {
$perkembangan = PerkembanganAnak::with('anak')->findOrFail($id);
$dataAnak = \App\Models\Anak::all();
if (request()->ajax()) {
return response()->json([
'success' => true,
'perkembangan' => $perkembangan,
'anak' => $perkembangan->anak,
'dataAnak' => $dataAnak
]);
}
$action = 'edit';
$return_to_riwayat = request()->query('return_to_riwayat');
return view('perkembangan_anak', compact('perkembangan', 'action', 'dataAnak', 'return_to_riwayat'));
} catch (\Exception $e) {
\Log::error('Error in edit perkembangan: ' . $e->getMessage());
if (request()->ajax()) {
return response()->json([
'success' => false,
'message' => 'Terjadi kesalahan: ' . $e->getMessage()
], 500);
}
return redirect()->route('perkembangan.index')
->with('error', 'Terjadi kesalahan saat mengambil data: ' . $e->getMessage());
}
}
public function update(Request $request, $id)
{
$validator = Validator::make($request->all(), [
'anak_id' => 'required|exists:anak,id',
'tanggal' => 'required|date',
'berat_badan' => 'required|numeric|between:0,999.99',
'tinggi_badan' => 'required|numeric|between:0,999.99',
]);
if ($validator->fails()) {
if ($request->ajax()) {
return response()->json(['errors' => $validator->errors()], 422);
}
return redirect()->back()
->withErrors($validator)
->withInput();
}
try {
// Get the old record
$oldRecord = PerkembanganAnak::findOrFail($id);
// Create new record with updated data
$newRecord = new PerkembanganAnak();
$newRecord->anak_id = $request->anak_id;
$newRecord->tanggal = $request->tanggal;
$newRecord->berat_badan = $request->berat_badan;
$newRecord->tinggi_badan = $request->tinggi_badan;
$newRecord->updated_from_id = $id; // Reference to the old record
$newRecord->save();
// Mark old record as updated
$oldRecord->update([
'is_updated' => true,
'updated_by_id' => $newRecord->id
]);
// Update related stunting records if any
if ($oldRecord) {
$terkaitStunting = \App\Models\Stunting::where('perkembangan_id', $oldRecord->id)->get();
foreach ($terkaitStunting as $stunting) {
$stunting->perkembangan_id = $newRecord->id;
$stunting->save();
}
}
if ($request->ajax()) {
return response()->json([
'success' => true,
'message' => 'Data perkembangan anak berhasil diperbarui!',
'data' => [
'old_record' => $oldRecord,
'new_record' => $newRecord,
'redirect_url' => $request->header('Referer') ?: route('perkembangan.index')
]
]);
}
// Periksa jika ada parameter return_to_riwayat
if ($request->has('return_to_riwayat')) {
$anak_id = $request->input('return_to_riwayat');
return redirect()->route('perkembangan.riwayat', $anak_id)
->with('success', 'Data perkembangan anak berhasil diperbarui!');
}
// Periksa jika request berasal dari halaman riwayat dan redirect kembali ke halaman tersebut
$referrer = $request->header('Referer');
if ($referrer && strpos($referrer, 'perkembangan/riwayat') !== false) {
return redirect($referrer)
->with('success', 'Data perkembangan anak berhasil diperbarui!');
}
return redirect()->route('perkembangan.index')
->with('success', 'Data perkembangan anak berhasil diperbarui!');
} catch (\Exception $e) {
\Log::error('Error updating perkembangan: ' . $e->getMessage());
if ($request->ajax()) {
return response()->json([
'success' => false,
'message' => 'Gagal memperbarui data: ' . $e->getMessage()
], 500);
}
return redirect()->back()
->with('error', 'Gagal memperbarui data: ' . $e->getMessage())
->withInput();
}
}
public function destroy($id)
{
try {
$perkembangan = PerkembanganAnak::findOrFail($id);
// Cek apakah ada data stunting yang terkait dengan perkembangan ini
$terkaitStunting = \App\Models\Stunting::where('perkembangan_id', $perkembangan->id)->count();
if ($terkaitStunting > 0) {
return redirect()->route('perkembangan.index')
->with('error', 'Data perkembangan anak tidak dapat dihapus karena masih terkait dengan data stunting! Hapus data stunting terkait terlebih dahulu.');
}
$perkembangan->delete();
return redirect()->route('perkembangan.index')
->with('success', 'Data perkembangan anak berhasil dihapus!');
} catch (\Exception $e) {
return redirect()->route('perkembangan.index')
->with('error', 'Gagal menghapus data: ' . $e->getMessage());
}
}
public function riwayat($anak_id)
{
try {
// Cari data anak
$anak = \App\Models\Anak::findOrFail($anak_id);
// Ambil parameter perPage dari request dengan default 5 data per halaman
$perPage = request()->input('perPage', 5);
// Log untuk debugging
\Log::info('Riwayat perPage: ' . $perPage);
\Log::info('Request all: ' . json_encode(request()->all()));
// Ambil semua data perkembangan untuk anak ini dengan pagination
$perkembangan = PerkembanganAnak::where('anak_id', $anak_id)
->orderBy('tanggal', 'desc')
->paginate($perPage)
->appends(['perPage' => $perPage]); // Penting! Menambahkan parameter ke link pagination
if ($perkembangan->isEmpty()) {
return redirect()->route('perkembangan.index')
->with('error', 'Tidak ada data riwayat perkembangan untuk anak ini');
}
$action = 'riwayat';
return view('perkembangan_anak', compact('perkembangan', 'anak', 'action'));
} catch (\Exception $e) {
\Log::error('Error in riwayat perkembangan: ' . $e->getMessage());
return redirect()->route('perkembangan.index')
->with('error', 'Terjadi kesalahan saat mengambil data riwayat: ' . $e->getMessage());
}
}
public function excel()
{
return Excel::download(new PerkembanganExport, 'data-perkembangan-anak.xlsx');
}
}

View File

@ -0,0 +1,123 @@
<?php
namespace App\Http\Controllers;
use App\Models\Pengguna;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
class PetugasController extends Controller
{
public function index(Request $request)
{
$search = $request->input('search');
$perPage = $request->input('perPage', 10);
$query = Pengguna::where('role', 'admin');
if ($search) {
$query->where(function($q) use ($search) {
$q->where('nik', 'like', "%{$search}%")
->orWhere('nama', 'like', "%{$search}%")
->orWhere('email', 'like', "%{$search}%")
->orWhere('no_telp', 'like', "%{$search}%");
});
}
$petugas = $query->orderBy('created_at', 'desc')->paginate($perPage);
return view('petugas', compact('petugas', 'search'));
}
public function store(Request $request)
{
$validator = Validator::make($request->all(), [
'nik' => 'required|string|size:16|unique:pengguna,nik',
'nama' => 'required|string|max:100',
'email' => 'required|email|unique:pengguna,email',
'password' => 'required|string|min:8',
'no_telp' => 'nullable|string|max:15',
'alamat' => 'nullable|string',
'kader_status' => 'required|in:utama,anggota'
]);
if ($validator->fails()) {
return redirect()->back()
->withErrors($validator)
->withInput();
}
Pengguna::create([
'nik' => $request->nik,
'nama' => $request->nama,
'email' => $request->email,
'password' => bcrypt($request->password),
'role' => 'admin',
'no_telp' => $request->no_telp,
'alamat' => $request->alamat,
'kader_status' => $request->kader_status
]);
return redirect()->route('petugas.index')
->with('success', 'Data petugas berhasil ditambahkan!');
}
public function update(Request $request, string $id)
{
$validator = Validator::make($request->all(), [
'nama' => 'required|string|max:100',
'email' => 'required|email|unique:pengguna,email,' . $id . ',nik',
'password' => 'nullable|string|min:8',
'no_telp' => 'nullable|string|max:15',
'alamat' => 'nullable|string',
'kader_status' => 'required|in:utama,anggota'
]);
if ($validator->fails()) {
return redirect()->back()
->withErrors($validator)
->withInput();
}
$petugas = Pengguna::where('nik', $id)->firstOrFail();
$data = [
'nama' => $request->nama,
'email' => $request->email,
'no_telp' => $request->no_telp,
'alamat' => $request->alamat,
'kader_status' => $request->kader_status
];
if ($request->filled('password')) {
$data['password'] = bcrypt($request->password);
}
$petugas->update($data);
return redirect()->route('petugas.index')
->with('success', 'Data petugas berhasil diperbarui!');
}
public function destroy(string $id)
{
$currentUser = auth()->user();
$petugas = Pengguna::where('nik', $id)->firstOrFail();
if ($currentUser->isKaderUtama()) {
if ($petugas->nik === $currentUser->nik) {
return redirect()->route('petugas.index')
->with('error', 'Anda tidak dapat menghapus akun sendiri!');
}
if ($petugas->isKaderAnggota()) {
$petugas->delete();
return redirect()->route('petugas.index')
->with('success', 'Data petugas berhasil dihapus!');
}
}
return redirect()->route('petugas.index')
->with('error', 'Anda tidak memiliki izin untuk menghapus akun ini!');
}
}

View File

@ -0,0 +1,69 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Profile;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
class ProfileController extends Controller
{
/**
* Display the user's profile.
*/
public function index()
{
// Get current logged-in admin user
$user = Auth::user();
// Find the admin profile from the pengguna table
$profile = Profile::where('id', $user->id)
->where('role', 'admin')
->first();
// If no admin profile exists, redirect to dashboard
if (!$profile) {
return redirect()->route('dashboard')
->with('error', 'Profil admin tidak ditemukan');
}
return view('profile', compact('profile'));
}
/**
* Update user profile
*/
public function update(Request $request)
{
// Get current logged-in admin user
$user = Auth::user();
// Validate only password fields
$request->validate([
'password' => 'nullable|string|min:6|confirmed',
]);
// Find the admin profile
$profile = Profile::where('id', $user->id)
->where('role', 'admin')
->first();
// If no admin profile exists, redirect to dashboard
if (!$profile) {
return redirect()->route('dashboard')
->with('error', 'Profil admin tidak ditemukan');
}
// Only update password if provided - other fields remain unchanged
if ($request->filled('password')) {
$profile->password = Hash::make($request->password);
$profile->save();
return redirect()->route('profile.index')
->with('success', 'Password berhasil diperbarui');
}
return redirect()->route('profile.index')
->with('info', 'Tidak ada perubahan yang dilakukan');
}
}

View File

@ -0,0 +1,307 @@
<?php
namespace App\Http\Controllers;
use App\Models\Stunting;
use App\Models\Anak;
use App\Models\PerkembanganAnak;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
use App\Exports\StuntingExport;
use Maatwebsite\Excel\Facades\Excel;
class StuntingController extends Controller
{
protected $stuntingCalculator;
public function __construct()
{
// Fix error "Class App\Services\StuntingCalculator not found"
try {
$this->stuntingCalculator = app()->make('App\Services\StuntingCalculator');
} catch (\Throwable $e) {
// Buat manual jika injeksi gagal
require_once app_path('Services/StuntingCalculator.php');
$this->stuntingCalculator = new \App\Services\StuntingCalculator();
}
}
/**
* Display a listing of the resource.
*/
public function index(Request $request)
{
$search = $request->input('search');
$perPage = $request->input('perPage', 10); // Default 10 data per halaman
// Ambil semua anak yang memiliki setidaknya satu data stunting
$query = Anak::has('stunting')->with(['stunting' => function($query) {
$query->latest('tanggal'); // Ambil yang terbaru berdasarkan tanggal
}]);
if ($search) {
$query->where(function($q) use ($search) {
$q->where('nama_anak', 'like', "%{$search}%")
->orWhereHas('stunting', function($sq) use ($search) {
$sq->where('status', 'like', "%{$search}%")
->orWhere('catatan', 'like', "%{$search}%");
});
});
}
// Dapatkan hasil paginasi dari query Anak
$anakPaginator = $query->paginate($perPage);
// Proses koleksi Anak untuk mendapatkan data stunting terbaru
$stuntingCollection = collect();
foreach ($anakPaginator as $anak) {
if ($anak->stunting && $anak->stunting->count() > 0) {
$latest = $anak->stunting->first(); // Ambil data stunting terbaru
if ($latest) {
$latest->setRelation('anak', $anak);
$stuntingCollection->push($latest);
}
}
}
// Gunakan paginator yang ada dengan koleksi baru
$stunting = new \Illuminate\Pagination\LengthAwarePaginator(
$stuntingCollection,
$anakPaginator->total(),
$anakPaginator->perPage(),
$anakPaginator->currentPage(),
['path' => \Illuminate\Support\Facades\Request::url()]
);
// Set query string dari request asli ke paginator
$stunting->appends($request->query());
return view('stunting', compact('stunting', 'search'));
}
/**
* Show the form for creating a new resource.
*/
public function create()
{
$action = 'create';
$dataAnak = Anak::all();
$dataPerkembangan = PerkembanganAnak::with('anak')->orderBy('tanggal', 'desc')->get();
return view('stunting', compact('action', 'dataAnak', 'dataPerkembangan'));
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request)
{
$validator = Validator::make($request->all(), [
'anak_id' => 'required|exists:anak,id',
'tanggal' => 'required|date',
'usia' => 'required|string|max:10',
'catatan' => 'nullable|string',
'status' => 'nullable|in:Stunting,Resiko Stunting,Tidak Stunting',
'perkembangan_id' => 'required|exists:perkembangan_anak,id',
]);
if ($validator->fails()) {
return redirect()->back()
->withErrors($validator)
->withInput();
}
// Ambil data perkembangan untuk mendapatkan tinggi_badan dan berat_badan
$perkembangan = PerkembanganAnak::findOrFail($request->perkembangan_id);
// Extract usia dalam bulan
$ageInMonths = $this->stuntingCalculator->extractAgeInMonths($request->usia);
// Hitung status stunting berdasarkan usia dan tinggi badan
$calculatedStatus = $this->stuntingCalculator->determineStatus(
$ageInMonths,
$perkembangan->tinggi_badan,
$perkembangan->berat_badan
);
// Gunakan status yang dihitung atau yang dipilih pengguna jika ada
$status = $request->status ?? $calculatedStatus;
// Buat data stunting baru dengan nilai dari form dan data dari perkembangan
$stunting = new Stunting();
$stunting->anak_id = $request->anak_id;
$stunting->tanggal = $request->tanggal;
$stunting->usia = $request->usia;
$stunting->catatan = $request->catatan;
$stunting->status = $status;
$stunting->perkembangan_id = $request->perkembangan_id;
// Ambil tinggi_badan dan berat_badan langsung dari perkembangan terkait
$stunting->tinggi_badan = $perkembangan->tinggi_badan;
$stunting->berat_badan = $perkembangan->berat_badan;
$stunting->save();
return redirect()->route('stunting.index')
->with('success', 'Data stunting berhasil ditambahkan!');
}
/**
* Display the specified resource.
*/
public function show(string $id)
{
$stunting = Stunting::with(['anak', 'perkembangan'])->findOrFail($id);
if (request()->ajax()) {
return response()->json([
'stunting' => $stunting,
'anak' => $stunting->anak,
'perkembangan' => $stunting->perkembangan
]);
}
$action = 'show';
return view('stunting', compact('stunting', 'action'));
}
/**
* Show the form for editing the specified resource.
*/
public function edit(string $id)
{
$stunting = Stunting::with(['anak', 'perkembangan'])->findOrFail($id);
if (request()->ajax()) {
// Get all children and perkembangan for dropdown
$dataAnak = Anak::all();
$dataPerkembangan = PerkembanganAnak::with('anak')->orderBy('tanggal', 'desc')->get();
return response()->json([
'stunting' => $stunting,
'anak' => $stunting->anak,
'perkembangan' => $stunting->perkembangan,
'dataAnak' => $dataAnak,
'dataPerkembangan' => $dataPerkembangan
]);
}
$action = 'edit';
$dataAnak = Anak::all();
$dataPerkembangan = PerkembanganAnak::with('anak')->orderBy('tanggal', 'desc')->get();
return view('stunting', compact('stunting', 'action', 'dataAnak', 'dataPerkembangan'));
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, string $id)
{
$validator = Validator::make($request->all(), [
'anak_id' => 'required|exists:anak,id',
'tanggal' => 'required|date',
'usia' => 'required|string|max:10',
'catatan' => 'nullable|string',
'status' => 'nullable|in:Stunting,Resiko Stunting,Tidak Stunting',
'perkembangan_id' => 'required|exists:perkembangan_anak,id',
]);
if ($validator->fails()) {
if ($request->ajax()) {
return response()->json(['errors' => $validator->errors()], 422);
}
return redirect()->back()
->withErrors($validator)
->withInput();
}
$stunting = Stunting::findOrFail($id);
// Ambil data perkembangan untuk mendapatkan tinggi_badan dan berat_badan
$perkembangan = PerkembanganAnak::findOrFail($request->perkembangan_id);
// Extract usia dalam bulan
$ageInMonths = $this->stuntingCalculator->extractAgeInMonths($request->usia);
// Hitung status stunting berdasarkan usia dan tinggi badan
$calculatedStatus = $this->stuntingCalculator->determineStatus(
$ageInMonths,
$perkembangan->tinggi_badan,
$perkembangan->berat_badan
);
// Gunakan status yang dihitung atau yang dipilih pengguna jika ada
$status = $request->status ?? $calculatedStatus;
// Update data stunting dengan nilai dari form dan data dari perkembangan
$stunting->anak_id = $request->anak_id;
$stunting->tanggal = $request->tanggal;
$stunting->usia = $request->usia;
$stunting->catatan = $request->catatan;
$stunting->status = $status;
$stunting->perkembangan_id = $request->perkembangan_id;
// Ambil tinggi_badan dan berat_badan langsung dari perkembangan terkait
$stunting->tinggi_badan = $perkembangan->tinggi_badan;
$stunting->berat_badan = $perkembangan->berat_badan;
$stunting->save();
if ($request->ajax()) {
return response()->json([
'success' => true,
'message' => 'Data stunting berhasil diperbarui!'
]);
}
return redirect()->route('stunting.index')
->with('success', 'Data stunting berhasil diperbarui!');
}
/**
* Remove the specified resource from storage.
*/
public function destroy(string $id)
{
$stunting = Stunting::findOrFail($id);
$stunting->delete();
return redirect()->route('stunting.index')
->with('success', 'Data stunting berhasil dihapus!');
}
/**
* Menampilkan riwayat stunting untuk satu anak.
*/
public function riwayat($anak_id)
{
try {
// Cari data anak
$anak = Anak::findOrFail($anak_id);
// Ambil parameter perPage dari request dengan default 5 data per halaman
$perPage = request()->input('perPage', 5);
// Ambil semua data stunting untuk anak ini dengan pagination
$stunting = Stunting::where('anak_id', $anak_id)
->orderBy('tanggal', 'desc')
->paginate($perPage)
->appends(['perPage' => $perPage]);
if ($stunting->isEmpty()) {
return redirect()->route('stunting.index')
->with('error', 'Tidak ada data riwayat stunting untuk anak ini');
}
$action = 'riwayat';
return view('stunting', compact('stunting', 'anak', 'action'));
} catch (\Exception $e) {
return redirect()->route('stunting.index')
->with('error', 'Terjadi kesalahan saat mengambil data riwayat: ' . $e->getMessage());
}
}
public function excel()
{
return Excel::download(new StuntingExport, 'data-stunting.xlsx');
}
}

View File

@ -0,0 +1,347 @@
<?php
namespace App\Http\Controllers;
use App\Models\Vitamin;
use App\Models\JenisVitamin;
use App\Models\Anak;
use App\Models\JadwalVitamin;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
use Carbon\Carbon;
use App\Exports\VitaminExport;
use Maatwebsite\Excel\Facades\Excel;
class VitaminController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index(Request $request)
{
$search = $request->input('search');
$perPage = $request->input('perPage', 10); // Default 10 data per halaman
$query = Vitamin::with(['anak', 'jenisVitamin']);
if ($search) {
$query->whereHas('anak', function($q) use ($search) {
$q->where('nama_anak', 'like', "%{$search}%");
})
->orWhereHas('jenisVitamin', function($q) use ($search) {
$q->where('nama', 'like', "%{$search}%");
})
->orWhere('status', 'like', "%{$search}%");
}
$vitamin = $query->orderBy('created_at', 'desc')->paginate($perPage);
// Dapatkan semua jadwal vitamin yang tersedia
$availableJadwal = $this->getAvailableJadwalVitamin();
return view('vitamin', compact('vitamin', 'search', 'availableJadwal'));
}
/**
* Show the form for creating a new resource.
*/
public function create()
{
$action = 'create';
$dataAnak = Anak::all();
$jenisVitamin = JenisVitamin::all();
// Dapatkan semua jadwal vitamin yang tersedia
$availableJadwal = $this->getAvailableJadwalVitamin();
return view('vitamin', compact('action', 'dataAnak', 'jenisVitamin', 'availableJadwal'));
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request)
{
$validator = Validator::make($request->all(), [
'anak_id' => 'required|exists:anak,id',
'jenis_id' => 'required|exists:jenis_vitamin,id',
'tanggal' => 'required|date',
'status' => 'required|in:Belum,Selesai',
]);
if ($validator->fails()) {
return redirect()->back()
->withErrors($validator)
->withInput();
}
// Validasi apakah umur anak sesuai dengan jenis vitamin
$anak = Anak::findOrFail($request->anak_id);
$jenisVitamin = JenisVitamin::findOrFail($request->jenis_id);
$tanggalLahir = Carbon::parse($anak->tanggal_lahir);
$umurBulan = $tanggalLahir->diffInMonths(Carbon::parse($request->tanggal));
if ($umurBulan < $jenisVitamin->min_umur_bulan || $umurBulan > $jenisVitamin->max_umur_bulan) {
return redirect()->back()
->with('error', 'Umur anak (' . $umurBulan . ' bulan) tidak sesuai dengan rentang umur yang disarankan untuk vitamin ' . $jenisVitamin->nama . ' (' . $jenisVitamin->min_umur_bulan . '-' . $jenisVitamin->max_umur_bulan . ' bulan)')
->withInput();
}
Vitamin::create($request->all());
return redirect()->route('vitamin.index')
->with('success', 'Data vitamin berhasil ditambahkan!');
}
/**
* Display the specified resource.
*/
public function show(string $id)
{
$vitamin = Vitamin::with(['anak', 'jenisVitamin'])->findOrFail($id);
if (request()->ajax()) {
return response()->json([
'vitamin' => $vitamin,
'anak' => $vitamin->anak,
'jenisVitamin' => $vitamin->jenisVitamin
]);
}
$action = 'show';
return view('vitamin', compact('vitamin', 'action'));
}
/**
* Show the form for editing the specified resource.
*/
public function edit(string $id)
{
$vitamin = Vitamin::with(['anak', 'jenisVitamin'])->findOrFail($id);
if (request()->ajax()) {
// Get all children and vitamin types for dropdowns
$dataAnak = Anak::all();
$jenisVitamin = JenisVitamin::all();
// Format data tanggal untuk input HTML
$vitamin->tanggal = Carbon::parse($vitamin->tanggal)->format('Y-m-d');
// Convert ID ke integer untuk mempermudah perbandingan di JavaScript
$formattedData = [
'vitamin' => [
'id' => (int)$vitamin->id,
'anak_id' => (int)$vitamin->anak_id,
'jenis_id' => (int)$vitamin->jenis_id,
'tanggal' => $vitamin->tanggal,
'status' => $vitamin->status,
],
'anak' => $vitamin->anak,
'jenisVitamin' => $vitamin->jenisVitamin,
'dataAnak' => $dataAnak->map(function($anak) {
$anak->id = (int)$anak->id;
return $anak;
}),
'jenisVitaminList' => $jenisVitamin->map(function($jenis) {
$jenis->id = (int)$jenis->id;
return $jenis;
})
];
return response()->json($formattedData);
}
$dataAnak = Anak::all();
$jenisVitamin = JenisVitamin::all();
$action = 'edit';
return view('vitamin', compact('vitamin', 'action', 'dataAnak', 'jenisVitamin'));
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, string $id)
{
// If this is a JSON request (likely from AJAX or API)
if ($request->expectsJson() || $request->ajax()) {
$validator = Validator::make($request->all(), [
'status' => 'required|in:' . implode(',', Vitamin::$statusList),
]);
if ($validator->fails()) {
return response()->json(['errors' => $validator->errors()], 422);
}
$vitamin = Vitamin::findOrFail($id);
$vitamin->status = $request->status;
$vitamin->save();
return response()->json([
'success' => true,
'message' => 'Status vitamin berhasil diperbarui!',
'data' => $vitamin
]);
}
// Regular form submission
$validator = Validator::make($request->all(), [
'anak_id' => 'required|exists:anak,id',
'jenis_id' => 'required|exists:jenis_vitamin,id',
'tanggal' => 'required|date',
'status' => 'required|in:' . implode(',', Vitamin::$statusList),
]);
if ($validator->fails()) {
if ($request->ajax()) {
return response()->json(['errors' => $validator->errors()], 422);
}
return redirect()->back()
->withErrors($validator)
->withInput();
}
$vitamin = Vitamin::findOrFail($id);
$vitamin->update($request->all());
if ($request->ajax()) {
return response()->json([
'success' => true,
'message' => 'Data vitamin berhasil diperbarui!'
]);
}
return redirect()->route('vitamin.index')
->with('success', 'Data vitamin berhasil diperbarui!');
}
/**
* Remove the specified resource from storage.
*/
public function destroy(string $id)
{
$vitamin = Vitamin::findOrFail($id);
$vitamin->delete();
return redirect()->route('vitamin.index')
->with('success', 'Data vitamin berhasil dihapus!');
}
/**
* Mendapatkan jadwal vitamin yang tersedia untuk anak-anak berdasarkan umur
*/
private function getAvailableJadwalVitamin()
{
// Dapatkan semua jadwal vitamin yang akan datang
$upcomingJadwal = JadwalVitamin::with('jenisVitamin')
->where('tanggal', '>=', Carbon::today())
->orderBy('tanggal', 'asc')
->get();
// Dapatkan semua anak
$dataAnak = Anak::all();
$availableJadwal = [];
// Untuk setiap jadwal, cek anak-anak yang memenuhi syarat umur
foreach ($upcomingJadwal as $jadwal) {
$jenisVitamin = $jadwal->jenisVitamin;
if (!$jenisVitamin) continue;
$eligibleChildren = [];
// Cek setiap anak
foreach ($dataAnak as $anak) {
$tanggalLahir = Carbon::parse($anak->tanggal_lahir);
$umurBulan = $tanggalLahir->diffInMonths(Carbon::parse($jadwal->tanggal));
// Periksa apakah anak berada dalam rentang umur yang sesuai
if ($umurBulan >= $jenisVitamin->min_umur_bulan && $umurBulan <= $jenisVitamin->max_umur_bulan) {
// Cek apakah anak sudah terdaftar untuk vitamin ini
$existingVitamin = Vitamin::where('anak_id', $anak->id)
->where('jenis_id', $jadwal->jenis_vitamin_id)
->first();
$eligibleChildren[] = [
'anak' => $anak,
'umur_bulan' => $umurBulan,
'already_registered' => $existingVitamin ? true : false,
'existing_vitamin' => $existingVitamin
];
}
}
// Jika ada anak yang memenuhi syarat, tambahkan jadwal ke daftar
if (count($eligibleChildren) > 0) {
$availableJadwal[] = [
'jadwal' => $jadwal,
'jenis_vitamin' => $jenisVitamin,
'eligible_children' => $eligibleChildren
];
}
}
return $availableJadwal;
}
/**
* Mendaftarkan anak untuk vitamin dari jadwal yang sudah ada
*/
public function registerFromJadwal(Request $request)
{
$validator = Validator::make($request->all(), [
'anak_id' => 'required|exists:anak,id',
'jadwal_vitamin_id' => 'required|exists:jadwal_vitamin,id',
]);
if ($validator->fails()) {
return redirect()->back()
->withErrors($validator)
->withInput();
}
// Dapatkan jadwal vitamin
$jadwal = JadwalVitamin::with('jenisVitamin')->findOrFail($request->jadwal_vitamin_id);
// Verifikasi anak
$anak = Anak::findOrFail($request->anak_id);
// Verifikasi umur anak
$tanggalLahir = Carbon::parse($anak->tanggal_lahir);
$umurBulan = $tanggalLahir->diffInMonths(Carbon::parse($jadwal->tanggal));
if ($umurBulan < $jadwal->jenisVitamin->min_umur_bulan || $umurBulan > $jadwal->jenisVitamin->max_umur_bulan) {
return redirect()->back()
->with('error', 'Umur anak (' . $umurBulan . ' bulan) tidak sesuai dengan rentang umur yang disarankan untuk vitamin ' . $jadwal->jenisVitamin->nama . ' (' . $jadwal->jenisVitamin->min_umur_bulan . '-' . $jadwal->jenisVitamin->max_umur_bulan . ' bulan)')
->withInput();
}
// Cek apakah anak sudah terdaftar untuk vitamin ini
$existingVitamin = Vitamin::where('anak_id', $anak->id)
->where('jenis_id', $jadwal->jenis_vitamin_id)
->first();
if ($existingVitamin) {
return redirect()->back()
->with('error', 'Anak sudah terdaftar untuk vitamin ' . $jadwal->jenisVitamin->nama)
->withInput();
}
// Buat data vitamin baru
Vitamin::create([
'anak_id' => $anak->id,
'jenis_id' => $jadwal->jenis_vitamin_id,
'tanggal' => $jadwal->tanggal,
'status' => Vitamin::STATUS_BELUM
]);
return redirect()->route('vitamin.index')
->with('success', 'Anak berhasil didaftarkan untuk vitamin!');
}
public function excel()
{
return Excel::download(new VitaminExport, 'data-vitamin.xlsx');
}
}

View File

@ -0,0 +1,51 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Models\Pengguna;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
class LoginController extends Controller
{
public function showLoginForm()
{
return view('auth.login');
}
public function login(Request $request)
{
$request->validate([
'nik' => 'required',
'password' => 'required',
]);
$credentials = $request->only('nik', 'password');
// Coba login dengan NIK
if (Auth::attempt(['nik' => $credentials['nik'], 'password' => $credentials['password']])) {
return redirect()->intended('dashboard');
}
// Jika gagal, coba login dengan email
$user = Pengguna::where('email', $credentials['nik'])->first();
if ($user && Hash::check($credentials['password'], $user->password)) {
Auth::login($user);
return redirect()->intended('dashboard');
}
return back()->withErrors([
'nik' => 'NIK/Email atau password salah.',
])->withInput($request->only('nik'));
}
public function logout(Request $request)
{
Auth::logout();
$request->session()->invalidate();
$request->session()->regenerateToken();
return redirect('/');
}
}

70
app/Http/Kernel.php Normal file
View File

@ -0,0 +1,70 @@
<?php
namespace App\Http;
use Illuminate\Foundation\Http\Kernel as HttpKernel;
class Kernel extends HttpKernel
{
/**
* The application's global HTTP middleware stack.
*
* These middleware are run during every request to your application.
*
* @var array<int, class-string|string>
*/
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<string, array<int, class-string|string>>
*/
protected $middlewareGroups = [
'web' => [
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
'api' => [
\Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
\Illuminate\Routing\Middleware\ThrottleRequests::class.':api',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
];
/**
* The application's middleware aliases.
*
* Aliases may be used instead of class names to conveniently assign middleware to routes and groups.
*
* @var array<string, class-string|string>
*/
protected $middlewareAliases = [
'auth' => \App\Http\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'auth.session' => \Illuminate\Session\Middleware\AuthenticateSession::class,
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
'can' => \Illuminate\Auth\Middleware\Authorize::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
'precognitive' => \Illuminate\Foundation\Http\Middleware\HandlePrecognitiveRequests::class,
'signed' => \App\Http\Middleware\ValidateSignature::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
'admin.role' => \App\Http\Middleware\AdminMiddleware::class,
'parent.role' => \App\Http\Middleware\ParentMiddleware::class,
];
}

View File

@ -0,0 +1,27 @@
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class AdminMiddleware
{
/**
* Handle an incoming request.
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/
public function handle(Request $request, Closure $next): Response
{
if (!$request->user() || $request->user()->role !== 'admin') {
return response()->json([
'status' => 'error',
'message' => 'Anda tidak memiliki akses untuk fitur ini',
], 403);
}
return $next($request);
}
}

View File

@ -0,0 +1,17 @@
<?php
namespace App\Http\Middleware;
use Illuminate\Auth\Middleware\Authenticate as Middleware;
use Illuminate\Http\Request;
class Authenticate extends Middleware
{
/**
* Get the path the user should be redirected to when they are not authenticated.
*/
protected function redirectTo(Request $request): ?string
{
return $request->expectsJson() ? null : route('login');
}
}

View File

@ -0,0 +1,17 @@
<?php
namespace App\Http\Middleware;
use Illuminate\Cookie\Middleware\EncryptCookies as Middleware;
class EncryptCookies extends Middleware
{
/**
* The names of the cookies that should not be encrypted.
*
* @var array<int, string>
*/
protected $except = [
//
];
}

View File

@ -0,0 +1,27 @@
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class ParentMiddleware
{
/**
* Handle an incoming request.
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/
public function handle(Request $request, Closure $next): Response
{
if (!$request->user() || $request->user()->role !== 'parent') {
return response()->json([
'status' => 'error',
'message' => 'Anda tidak memiliki akses untuk fitur ini',
], 403);
}
return $next($request);
}
}

View File

@ -0,0 +1,17 @@
<?php
namespace App\Http\Middleware;
use Illuminate\Foundation\Http\Middleware\PreventRequestsDuringMaintenance as Middleware;
class PreventRequestsDuringMaintenance extends Middleware
{
/**
* The URIs that should be reachable while maintenance mode is enabled.
*
* @var array<int, string>
*/
protected $except = [
//
];
}

View File

@ -0,0 +1,30 @@
<?php
namespace App\Http\Middleware;
use App\Providers\RouteServiceProvider;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Symfony\Component\HttpFoundation\Response;
class RedirectIfAuthenticated
{
/**
* Handle an incoming request.
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/
public function handle(Request $request, Closure $next, string ...$guards): Response
{
$guards = empty($guards) ? [null] : $guards;
foreach ($guards as $guard) {
if (Auth::guard($guard)->check()) {
return redirect(RouteServiceProvider::HOME);
}
}
return $next($request);
}
}

View File

@ -0,0 +1,19 @@
<?php
namespace App\Http\Middleware;
use Illuminate\Foundation\Http\Middleware\TrimStrings as Middleware;
class TrimStrings extends Middleware
{
/**
* The names of the attributes that should not be trimmed.
*
* @var array<int, string>
*/
protected $except = [
'current_password',
'password',
'password_confirmation',
];
}

View File

@ -0,0 +1,20 @@
<?php
namespace App\Http\Middleware;
use Illuminate\Http\Middleware\TrustHosts as Middleware;
class TrustHosts extends Middleware
{
/**
* Get the host patterns that should be trusted.
*
* @return array<int, string|null>
*/
public function hosts(): array
{
return [
$this->allSubdomainsOfApplicationUrl(),
];
}
}

View File

@ -0,0 +1,28 @@
<?php
namespace App\Http\Middleware;
use Illuminate\Http\Middleware\TrustProxies as Middleware;
use Illuminate\Http\Request;
class TrustProxies extends Middleware
{
/**
* The trusted proxies for this application.
*
* @var array<int, string>|string|null
*/
protected $proxies;
/**
* The headers that should be used to detect proxies.
*
* @var int
*/
protected $headers =
Request::HEADER_X_FORWARDED_FOR |
Request::HEADER_X_FORWARDED_HOST |
Request::HEADER_X_FORWARDED_PORT |
Request::HEADER_X_FORWARDED_PROTO |
Request::HEADER_X_FORWARDED_AWS_ELB;
}

View File

@ -0,0 +1,22 @@
<?php
namespace App\Http\Middleware;
use Illuminate\Routing\Middleware\ValidateSignature as Middleware;
class ValidateSignature extends Middleware
{
/**
* The names of the query string parameters that should be ignored.
*
* @var array<int, string>
*/
protected $except = [
// 'fbclid',
// 'utm_campaign',
// 'utm_content',
// 'utm_medium',
// 'utm_source',
// 'utm_term',
];
}

View File

@ -0,0 +1,17 @@
<?php
namespace App\Http\Middleware;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;
class VerifyCsrfToken extends Middleware
{
/**
* The URIs that should be excluded from CSRF verification.
*
* @var array<int, string>
*/
protected $except = [
//
];
}

57
app/Models/Anak.php Normal file
View File

@ -0,0 +1,57 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Anak extends Model
{
use HasFactory;
protected $table = 'anak';
protected $primaryKey = 'id';
public $incrementing = true;
protected $fillable = [
'pengguna_id',
'nama_anak',
'tempat_lahir',
'tanggal_lahir',
'jenis_kelamin',
'usia',
];
protected $casts = [
'tanggal_lahir' => 'date',
];
/**
* Get the pengguna that owns the anak.
*/
public function pengguna()
{
return $this->belongsTo(Pengguna::class, 'pengguna_id', 'id');
}
public function imunisasi()
{
return $this->hasMany(Imunisasi::class, 'anak_id');
}
/**
* Get the perkembangan records for the anak.
*/
public function perkembanganAnak()
{
return $this->hasMany(PerkembanganAnak::class, 'anak_id');
}
/**
* Get the stunting records for the anak.
*/
public function stunting()
{
return $this->hasMany(Stunting::class, 'anak_id');
}
}

14
app/Models/Artikel.php Normal file
View File

@ -0,0 +1,14 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Artikel extends Model
{
use HasFactory;
protected $table = 'artikel'; // Menentukan nama tabel
protected $fillable = ['judul', 'gambar_artikel', 'isi_artikel', 'tanggal']; // Kolom yang dapat diisi
}

33
app/Models/DataAnak.php Normal file
View File

@ -0,0 +1,33 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class DataAnak extends Model
{
use HasFactory;
protected $table = 'anak';
protected $primaryKey = 'id';
public $incrementing = true;
protected $fillable = [
'pengguna_id',
'nama_anak',
'tempat_lahir',
'tanggal_lahir',
'jenis_kelamin',
'usia'
];
protected $casts = [
'tanggal_lahir' => 'date'
];
public function pengguna()
{
return $this->belongsTo(Pengguna::class, 'pengguna_id');
}
}

View File

@ -0,0 +1,24 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class DataOrangTua extends Model
{
use HasFactory;
protected $table = 'data_orangtua';
protected $primaryKey = 'nik';
protected $keyType = 'string';
public $incrementing = false;
protected $fillable = [
'nik',
'nama_ibu',
'no_telp',
'alamat',
'nama_anak'
];
}

48
app/Models/Imunisasi.php Normal file
View File

@ -0,0 +1,48 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Imunisasi extends Model
{
use HasFactory;
protected $table = 'imunisasi';
protected $primaryKey = 'id';
// Constants for status enum values
const STATUS_BELUM = 'Belum';
const STATUS_SELESAI_SESUAI = 'Selesai Sesuai';
const STATUS_SELESAI_TIDAK_SESUAI = 'Selesai Tidak Sesuai';
// List of all valid status values
public static $statusList = [
self::STATUS_BELUM,
self::STATUS_SELESAI_SESUAI,
self::STATUS_SELESAI_TIDAK_SESUAI,
];
protected $fillable = [
'anak_id',
'jenis_id',
'tanggal',
'status',
'jadwal_imunisasi_id'
];
protected $casts = [
'tanggal' => 'date',
];
public function anak()
{
return $this->belongsTo(Anak::class, 'anak_id');
}
public function jenisImunisasi()
{
return $this->belongsTo(JenisImunisasi::class, 'jenis_id');
}
}

View File

@ -0,0 +1,36 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class JadwalImunisasi extends Model
{
use HasFactory;
protected $table = 'jadwal_imunisasi';
protected $fillable = [
'jenis_imunisasi_id',
'tanggal',
'waktu',
'is_implemented'
];
protected $appends = ['status'];
public function jenisImunisasi()
{
return $this->belongsTo(JenisImunisasi::class, 'jenis_imunisasi_id');
}
public function imunisasi()
{
return $this->hasMany(Imunisasi::class, 'jadwal_imunisasi_id');
}
public function getStatusAttribute()
{
return $this->is_implemented ? 'Selesai' : 'Belum';
}
}

View File

@ -0,0 +1,18 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class JadwalPemeriksaan extends Model
{
use HasFactory;
protected $table = 'jadwal_pemeriksaan';
protected $fillable = ['judul', 'tanggal', 'waktu', 'is_implemented'];
protected $casts = [
'is_implemented' => 'boolean',
];
}

View File

@ -0,0 +1,36 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class JadwalVitamin extends Model
{
use HasFactory;
protected $table = 'jadwal_vitamin';
protected $fillable = [
'jenis_vitamin_id',
'tanggal',
'waktu',
'is_implemented'
];
protected $appends = ['status'];
public function jenisVitamin()
{
return $this->belongsTo(JenisVitamin::class, 'jenis_vitamin_id');
}
public function vitamin()
{
return $this->hasMany(Vitamin::class, 'jadwal_vitamin_id');
}
public function getStatusAttribute()
{
return $this->is_implemented ? 'Selesai' : 'Belum';
}
}

View File

@ -0,0 +1,44 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class JenisImunisasi extends Model
{
use HasFactory;
protected $table = 'jenis_imunisasi';
protected $primaryKey = 'id';
// Konstanta untuk nilai enum
const JENIS_HB0 = 'HB-0';
const JENIS_BCG_POLIO1 = 'BCG & Polio 1';
const JENIS_DPT_HB_HIP1_POLIO2 = 'DPT-HB-HIP 1 & Polio 2';
const JENIS_DPT_HB_HIP2_POLIO3 = 'DPT-HB-HIP 2 & Polio 3';
const JENIS_DPT_HB_HIP3_POLIO4 = 'DPT-HB-HIP 3 & Polio 4';
const JENIS_CAMPAK = 'Campak';
// Daftar semua jenis imunisasi yang valid
public static $jenisImunisasi = [
self::JENIS_HB0,
self::JENIS_BCG_POLIO1,
self::JENIS_DPT_HB_HIP1_POLIO2,
self::JENIS_DPT_HB_HIP2_POLIO3,
self::JENIS_DPT_HB_HIP3_POLIO4,
self::JENIS_CAMPAK,
];
protected $fillable = [
'nama',
'min_umur_hari',
'max_umur_hari',
'keterangan'
];
public function imunisasi()
{
return $this->hasMany(Imunisasi::class, 'jenis_id');
}
}

View File

@ -0,0 +1,36 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class JenisVitamin extends Model
{
use HasFactory;
protected $table = 'jenis_vitamin';
protected $primaryKey = 'id';
// Konstanta untuk nilai enum
const JENIS_A_BIRU = 'A Biru';
const JENIS_A_MERAH = 'A Merah';
// Daftar semua jenis vitamin yang valid
public static $jenisVitamin = [
self::JENIS_A_BIRU,
self::JENIS_A_MERAH,
];
protected $fillable = [
'nama',
'min_umur_bulan',
'max_umur_bulan',
'keterangan'
];
public function vitamin()
{
return $this->hasMany(Vitamin::class, 'jenis_id');
}
}

55
app/Models/Pengguna.php Normal file
View File

@ -0,0 +1,55 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;
class Pengguna extends Authenticatable
{
use HasApiTokens, HasFactory, Notifiable;
protected $table = 'pengguna';
protected $primaryKey = 'nik';
protected $keyType = 'string';
public $incrementing = false;
protected $fillable = [
'nik',
'nama',
'email',
'password',
'role',
'no_telp',
'alamat',
'kader_status'
];
protected $hidden = [
'password',
'remember_token',
];
protected $casts = [
'email_verified_at' => 'datetime',
'password' => 'hashed',
];
public function anak()
{
return $this->hasMany(Anak::class, 'pengguna_id', 'id');
}
public function isKaderUtama()
{
return $this->kader_status === 'utama';
}
public function isKaderAnggota()
{
return $this->kader_status === 'anggota';
}
}

View File

@ -0,0 +1,164 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Carbon\Carbon;
use App\Services\PerkembanganAnakCalculator;
class PerkembanganAnak extends Model
{
use HasFactory, SoftDeletes;
protected $table = 'perkembangan_anak';
protected $primaryKey = 'id';
protected $fillable = [
'anak_id',
'tanggal',
'berat_badan',
'tinggi_badan',
'status_berat_badan',
'status_tinggi_badan',
'updated_from_id',
'is_updated',
'updated_by_id'
];
protected $casts = [
'tanggal' => 'datetime',
'berat_badan' => 'decimal:2',
'tinggi_badan' => 'decimal:2',
'is_updated' => 'boolean'
];
// Konstanta untuk status
const STATUS_SANGAT_KURANG = 'Sangat Kurang';
const STATUS_KURANG = 'Kurang';
const STATUS_RESIKO_KURANG = 'Resiko Kurang';
const STATUS_NORMAL = 'Normal';
const STATUS_RESIKO_LEBIH = 'Resiko Lebih';
const STATUS_LEBIH = 'Lebih';
const STATUS_SANGAT_LEBIH = 'Sangat Lebih';
/**
* Boot method untuk menghitung status sebelum menyimpan
*/
protected static function boot()
{
parent::boot();
static::creating(function ($model) {
// Validasi berat badan
if ($model->berat_badan <= 0) {
throw new \Exception('Berat badan harus lebih dari 0');
}
// Validasi tinggi badan
if ($model->tinggi_badan <= 0) {
throw new \Exception('Tinggi badan harus lebih dari 0');
}
// Hitung status saat membuat record baru
$model->hitungStatus();
});
static::updating(function ($model) {
// Hitung ulang status saat mengupdate record
$model->hitungStatus();
});
static::saving(function ($model) {
$model->hitungStatus();
});
}
public function anak()
{
return $this->belongsTo(Anak::class, 'anak_id');
}
public function stunting()
{
return $this->hasMany(Stunting::class, 'perkembangan_id');
}
// Helper method untuk mendapatkan status pertumbuhan
public function getStatusPertumbuhan()
{
return [
'status_bb' => $this->status_berat_badan,
'status_tb' => $this->status_tinggi_badan,
];
}
/**
* Menghitung status tinggi badan dan berat badan berdasarkan usia
*/
public function hitungStatus()
{
$calculator = new PerkembanganAnakCalculator();
// Hitung usia dalam bulan
$tanggalLahir = Carbon::parse($this->anak->tanggal_lahir);
$tanggalPemeriksaan = Carbon::parse($this->tanggal);
$usiaBulan = $tanggalLahir->diffInMonths($tanggalPemeriksaan);
// Hitung status tinggi badan
if ($this->tinggi_badan) {
$this->status_tinggi_badan = $calculator->hitungStatusTinggiBadan($usiaBulan, $this->tinggi_badan);
}
// Hitung status berat badan
if ($this->berat_badan) {
$this->status_berat_badan = $calculator->hitungStatusBeratBadan($usiaBulan, $this->berat_badan);
}
return $this;
}
// Relationships
public function oldRecord()
{
return $this->belongsTo(PerkembanganAnak::class, 'updated_from_id');
}
public function newRecord()
{
return $this->hasOne(PerkembanganAnak::class, 'updated_from_id');
}
// Validation rules
public static function rules()
{
return [
'anak_id' => 'required|exists:anak,id',
'tanggal' => 'required|date',
'berat_badan' => 'required|numeric|min:0',
'tinggi_badan' => 'required|numeric|min:0',
];
}
// Scopes
public function scopeActive($query)
{
return $query->where('is_updated', false);
}
public function scopeUpdated($query)
{
return $query->where('is_updated', true);
}
public function scopeLatest($query)
{
return $query->orderBy('tanggal', 'desc');
}
public function scopeOldest($query)
{
return $query->orderBy('tanggal', 'asc');
}
}

24
app/Models/Petugas.php Normal file
View File

@ -0,0 +1,24 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
use Illuminate\Auth\Authenticatable;
class Petugas extends Model implements AuthenticatableContract
{
use HasFactory, Authenticatable;
protected $table = 'petugas';
protected $fillable = [
'nik',
'nama',
'email',
'password',
'no_telp',
'alamat'
];
public $timestamps = false; // Nonaktifkan timestamps
}

30
app/Models/Profile.php Normal file
View File

@ -0,0 +1,30 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Profile extends Model
{
use HasFactory;
protected $table = 'pengguna';
protected $primaryKey = 'id';
protected $fillable = [
'nik',
'nama',
'email',
'no_telp',
'alamat',
'role'
];
// In this system, the 'pengguna' table is used for all user types
// We use the 'role' column to determine if a user is admin or parent
public function isAdmin()
{
return $this->role === 'admin';
}
}

53
app/Models/Stunting.php Normal file
View File

@ -0,0 +1,53 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Stunting extends Model
{
use HasFactory;
protected $table = 'stunting';
protected $primaryKey = 'id';
// Constants for status enum values
const STATUS_STUNTING = 'Stunting';
const STATUS_RESIKO_STUNTING = 'Resiko Stunting';
const STATUS_TIDAK_STUNTING = 'Tidak Stunting';
// List of all valid status values
public static $statusList = [
self::STATUS_STUNTING,
self::STATUS_RESIKO_STUNTING,
self::STATUS_TIDAK_STUNTING,
];
protected $fillable = [
'anak_id',
'tanggal',
'usia',
'berat_badan',
'tinggi_badan',
'catatan',
'status',
'perkembangan_id'
];
protected $casts = [
'tanggal' => 'date',
'berat_badan' => 'decimal:2',
'tinggi_badan' => 'decimal:2'
];
public function anak()
{
return $this->belongsTo(Anak::class, 'anak_id');
}
public function perkembangan()
{
return $this->belongsTo(PerkembanganAnak::class, 'perkembangan_id');
}
}

46
app/Models/Vitamin.php Normal file
View File

@ -0,0 +1,46 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Vitamin extends Model
{
use HasFactory;
protected $table = 'vitamin';
protected $primaryKey = 'id';
// Constants for status enum values
const STATUS_BELUM = 'Belum';
const STATUS_SELESAI = 'Selesai';
// List of all valid status values
public static $statusList = [
self::STATUS_BELUM,
self::STATUS_SELESAI,
];
protected $fillable = [
'anak_id',
'jenis_id',
'tanggal',
'status',
'jadwal_vitamin_id'
];
protected $casts = [
'tanggal' => 'date',
];
public function anak()
{
return $this->belongsTo(Anak::class, 'anak_id');
}
public function jenisVitamin()
{
return $this->belongsTo(JenisVitamin::class, 'jenis_id');
}
}

View File

@ -0,0 +1,32 @@
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*/
public function register(): void
{
$this->app->singleton('App\Services\StuntingCalculator', function ($app) {
// Fix error "Class App\Services\StuntingCalculator not found"
$calculatorPath = app_path('Services/StuntingCalculator.php');
if (file_exists($calculatorPath)) {
require_once $calculatorPath;
}
return new \App\Services\StuntingCalculator();
});
}
/**
* Bootstrap any application services.
*/
public function boot(): void
{
//
}
}

View File

@ -0,0 +1,26 @@
<?php
namespace App\Providers;
// use Illuminate\Support\Facades\Gate;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
class AuthServiceProvider extends ServiceProvider
{
/**
* The model to policy mappings for the application.
*
* @var array<class-string, class-string>
*/
protected $policies = [
//
];
/**
* Register any authentication / authorization services.
*/
public function boot(): void
{
//
}
}

View File

@ -0,0 +1,19 @@
<?php
namespace App\Providers;
use Illuminate\Support\Facades\Broadcast;
use Illuminate\Support\ServiceProvider;
class BroadcastServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Broadcast::routes();
require base_path('routes/channels.php');
}
}

View File

@ -0,0 +1,38 @@
<?php
namespace App\Providers;
use Illuminate\Auth\Events\Registered;
use Illuminate\Auth\Listeners\SendEmailVerificationNotification;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Event;
class EventServiceProvider extends ServiceProvider
{
/**
* The event to listener mappings for the application.
*
* @var array<class-string, array<int, class-string>>
*/
protected $listen = [
Registered::class => [
SendEmailVerificationNotification::class,
],
];
/**
* Register any events for your application.
*/
public function boot(): void
{
//
}
/**
* Determine if events and listeners should be automatically discovered.
*/
public function shouldDiscoverEvents(): bool
{
return false;
}
}

View File

@ -0,0 +1,40 @@
<?php
namespace App\Providers;
use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Support\Facades\Route;
class RouteServiceProvider extends ServiceProvider
{
/**
* The path to your application's "home" route.
*
* Typically, users are redirected here after authentication.
*
* @var string
*/
public const HOME = '/dashboard';
/**
* Define your route model bindings, pattern filters, and other route configuration.
*/
public function boot(): void
{
RateLimiter::for('api', function (Request $request) {
return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip());
});
$this->routes(function () {
Route::middleware('api')
->prefix('api')
->group(base_path('routes/api.php'));
Route::middleware('web')
->group(base_path('routes/web.php'));
});
}
}

View File

@ -0,0 +1,98 @@
<?php
namespace App\Services;
use App\Models\Pengguna;
use App\Models\Anak;
use App\Models\Stunting;
use Exception;
use Illuminate\Support\Facades\Log;
class DashboardService
{
/**
* Get all dashboard statistics for the main dashboard
*
* @return array
*/
public function getDashboardStats(): array
{
return [
'total_stunting' => $this->getTotalStuntingCases(),
'total_pengguna' => $this->getTotalUsers(),
'total_anak' => $this->getTotalChildren(),
'total_petugas' => $this->getTotalStaff()
];
}
/**
* Get the total number of stunting cases
*
* @return int
*/
public function getTotalStuntingCases(): int
{
try {
// Dapatkan subquery untuk mendapatkan ID stunting terbaru untuk setiap anak
$latestStuntingIds = Stunting::select('id')
->whereIn('id', function($query) {
$query->selectRaw('MAX(id)')
->from('stunting')
->groupBy('anak_id');
});
// Hitung total kasus stunting dari record terbaru
return Stunting::whereIn('id', $latestStuntingIds)
->where('status', 'Stunting')
->count();
} catch (Exception $e) {
Log::error('Error counting stunting cases: ' . $e->getMessage());
return 0;
}
}
/**
* Get the total number of users (parents)
*
* @return int
*/
public function getTotalUsers(): int
{
try {
return Pengguna::where('role', 'parent')->count();
} catch (Exception $e) {
Log::error('Error counting users: ' . $e->getMessage());
return 0;
}
}
/**
* Get the total number of children
*
* @return int
*/
public function getTotalChildren(): int
{
try {
return Anak::count();
} catch (Exception $e) {
Log::error('Error counting children: ' . $e->getMessage());
return 0;
}
}
/**
* Get the total number of staff
*
* @return int
*/
public function getTotalStaff(): int
{
try {
return Pengguna::where('role', 'admin')->count();
} catch (Exception $e) {
Log::error('Error counting staff: ' . $e->getMessage());
return 0;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,205 @@
<?php
namespace App\Services;
class StuntingCalculator
{
// Status constants
const STATUS_STUNTING = 'Stunting';
const STATUS_RESIKO_STUNTING = 'Resiko Stunting';
const STATUS_TIDAK_STUNTING = 'Tidak Stunting';
// Tinggi badan standar sesuai usia (min stunting, min resiko stunting, min tidak stunting)
protected $standardHeightByAge = [
'24' => [0, 81.1, 84.1], // ≤ 81.0: Stunting, 81.1-84.0: Resiko, ≥ 84.1: Tidak Stunting
'25' => [0, 81.8, 84.9],
'26' => [0, 82.6, 85.6],
'27' => [0, 83.2, 86.4],
'28' => [0, 83.9, 87.1],
'29' => [0, 84.6, 87.8],
'30' => [0, 85.2, 88.5],
'31' => [0, 85.8, 89.2],
'32' => [0, 86.5, 89.9],
'33' => [0, 87.0, 90.5],
'34' => [0, 87.6, 91.1],
'35' => [0, 88.2, 91.8],
'36' => [0, 88.8, 92.4],
'37' => [0, 89.3, 93.0],
'38' => [0, 89.9, 93.6],
'39' => [0, 90.4, 94.2],
'40' => [0, 91.0, 94.7],
'41' => [0, 91.5, 95.3],
'42' => [0, 92.0, 95.9],
'43' => [0, 92.5, 96.4],
'44' => [0, 93.1, 97.0],
'45' => [0, 93.6, 97.5],
'46' => [0, 94.1, 98.1],
'47' => [0, 94.5, 98.6],
'48' => [0, 95.0, 99.1],
'49' => [0, 95.5, 99.7],
'50' => [0, 96.0, 100.2],
'51' => [0, 96.5, 100.7],
'52' => [0, 97.0, 101.2],
'53' => [0, 97.5, 101.7],
'54' => [0, 97.9, 102.3],
'55' => [0, 98.4, 102.8],
'56' => [0, 98.9, 103.3],
'57' => [0, 99.4, 103.8],
'58' => [0, 99.8, 104.3],
'59' => [0, 100.3, 104.8],
'60' => [0, 100.8, 105.3]
];
// Berat badan standar sesuai usia (min stunting, min resiko stunting, min tidak stunting)
protected $standardWeightByAge = [
'24' => [0, 9.1, 10.2], // ≤ 9.0: Stunting, 9.1-10.1: Resiko, ≥ 10.2: Tidak Stunting
'25' => [0, 9.3, 10.3],
'26' => [0, 9.5, 10.5],
'27' => [0, 9.6, 10.7],
'28' => [0, 9.8, 10.9],
'29' => [0, 9.9, 11.1],
'30' => [0, 10.1, 11.2],
'31' => [0, 10.2, 11.4],
'32' => [0, 10.4, 11.6],
'33' => [0, 10.5, 11.7],
'34' => [0, 10.6, 11.9],
'35' => [0, 10.8, 12.0],
'36' => [0, 10.9, 12.2],
'37' => [0, 11.0, 12.4],
'38' => [0, 11.2, 12.5],
'39' => [0, 11.3, 12.7],
'40' => [0, 11.4, 12.8],
'41' => [0, 11.6, 13.0],
'42' => [0, 11.7, 13.1],
'43' => [0, 11.8, 13.3],
'44' => [0, 11.9, 13.4],
'45' => [0, 12.1, 13.6],
'46' => [0, 12.2, 13.7],
'47' => [0, 12.3, 13.9],
'48' => [0, 12.4, 14.0],
'49' => [0, 12.5, 14.2],
'50' => [0, 12.7, 14.3],
'51' => [0, 12.8, 14.5],
'52' => [0, 12.9, 14.6],
'53' => [0, 13.0, 14.8],
'54' => [0, 13.1, 14.9],
'55' => [0, 13.3, 15.1],
'56' => [0, 13.4, 15.2],
'57' => [0, 13.5, 15.3],
'58' => [0, 13.6, 15.5],
'59' => [0, 13.7, 15.6],
'60' => [0, 13.8, 15.8]
];
/**
* Menentukan status stunting berdasarkan tinggi badan
*
* @param int $age Usia dalam bulan (24-60)
* @param float $height Tinggi badan dalam cm
* @return string Status stunting
*/
public function determineStatusByHeight($age, $height)
{
// Konversi ke string untuk lookup dalam array
$age = (string) $age;
// Jika usia tidak dalam range yang ditentukan
if (!isset($this->standardHeightByAge[$age])) {
return self::STATUS_TIDAK_STUNTING; // Default
}
$standards = $this->standardHeightByAge[$age];
// Bandingkan tinggi badan dengan standar
if ($height <= $standards[0]) {
return self::STATUS_STUNTING;
} elseif ($height >= $standards[0] && $height < $standards[1]) {
return self::STATUS_STUNTING;
} elseif ($height >= $standards[1] && $height < $standards[2]) {
return self::STATUS_RESIKO_STUNTING;
} else {
return self::STATUS_TIDAK_STUNTING;
}
}
/**
* Menentukan status stunting berdasarkan berat badan
*
* @param int $age Usia dalam bulan (24-60)
* @param float $weight Berat badan dalam kg
* @return string Status stunting
*/
public function determineStatusByWeight($age, $weight)
{
// Konversi ke string untuk lookup dalam array
$age = (string) $age;
// Jika usia tidak dalam range yang ditentukan
if (!isset($this->standardWeightByAge[$age])) {
return self::STATUS_TIDAK_STUNTING; // Default
}
$standards = $this->standardWeightByAge[$age];
// Bandingkan berat badan dengan standar
if ($weight <= $standards[0]) {
return self::STATUS_STUNTING;
} elseif ($weight >= $standards[0] && $weight < $standards[1]) {
return self::STATUS_STUNTING;
} elseif ($weight >= $standards[1] && $weight < $standards[2]) {
return self::STATUS_RESIKO_STUNTING;
} else {
return self::STATUS_TIDAK_STUNTING;
}
}
/**
* Menentukan status stunting berdasarkan tinggi dan berat badan
* Hanya melihat tinggi badan untuk keputusan akhir sesuai pedoman WHO
*
* @param int $age Usia dalam bulan (24-60)
* @param float $height Tinggi badan dalam cm
* @param float $weight Berat badan dalam kg (opsional)
* @return string Status stunting
*/
public function determineStatus($age, $height, $weight = null)
{
// Stunting lebih diutamakan berdasarkan tinggi badan
return $this->determineStatusByHeight($age, $height);
}
/**
* Extract usia dalam bulan dari string usia
* Contoh input: "24 bulan" atau "2 tahun"
*
* @param string $usiaString
* @return int
*/
public function extractAgeInMonths($usiaString)
{
$usiaString = strtolower($usiaString);
// Cek apakah mengandung "bulan"
if (strpos($usiaString, 'bulan') !== false) {
// Ambil angka dari string
preg_match('/(\d+)/', $usiaString, $matches);
return (int) $matches[1];
}
// Cek apakah mengandung "tahun"
if (strpos($usiaString, 'tahun') !== false) {
// Ambil angka dari string
preg_match('/(\d+)/', $usiaString, $matches);
// Konversi tahun ke bulan
return (int) $matches[1] * 12;
}
// Jika hanya angka, asumsikan dalam bulan
if (is_numeric($usiaString)) {
return (int) $usiaString;
}
// Default
return 0;
}
}

53
artisan Normal file
View File

@ -0,0 +1,53 @@
#!/usr/bin/env php
<?php
define('LARAVEL_START', microtime(true));
/*
|--------------------------------------------------------------------------
| Register The Auto Loader
|--------------------------------------------------------------------------
|
| Composer provides a convenient, automatically generated class loader
| for our application. We just need to utilize it! We'll require it
| into the script here so that we do not have to worry about the
| loading of any of our classes manually. It's great to relax.
|
*/
require __DIR__.'/vendor/autoload.php';
$app = require_once __DIR__.'/bootstrap/app.php';
/*
|--------------------------------------------------------------------------
| Run The Artisan Application
|--------------------------------------------------------------------------
|
| When we run the console application, the current CLI command will be
| executed in this console and the response sent back to a terminal
| or another output device for the developers. Here goes nothing!
|
*/
$kernel = $app->make(Illuminate\Contracts\Console\Kernel::class);
$status = $kernel->handle(
$input = new Symfony\Component\Console\Input\ArgvInput,
new Symfony\Component\Console\Output\ConsoleOutput
);
/*
|--------------------------------------------------------------------------
| Shutdown The Application
|--------------------------------------------------------------------------
|
| Once Artisan has finished running, we will fire off the shutdown events
| so that any final work may be done by the application before we shut
| down the process. This is the last thing to happen to the request.
|
*/
$kernel->terminate($input, $status);
exit($status);

55
bootstrap/app.php Normal file
View File

@ -0,0 +1,55 @@
<?php
/*
|--------------------------------------------------------------------------
| Create The Application
|--------------------------------------------------------------------------
|
| The first thing we will do is create a new Laravel application instance
| which serves as the "glue" for all the components of Laravel, and is
| the IoC container for the system binding all of the various parts.
|
*/
$app = new Illuminate\Foundation\Application(
$_ENV['APP_BASE_PATH'] ?? dirname(__DIR__)
);
/*
|--------------------------------------------------------------------------
| Bind Important Interfaces
|--------------------------------------------------------------------------
|
| Next, we need to bind some important interfaces into the container so
| we will be able to resolve them when needed. The kernels serve the
| incoming requests to this application from both the web and CLI.
|
*/
$app->singleton(
Illuminate\Contracts\Http\Kernel::class,
App\Http\Kernel::class
);
$app->singleton(
Illuminate\Contracts\Console\Kernel::class,
App\Console\Kernel::class
);
$app->singleton(
Illuminate\Contracts\Debug\ExceptionHandler::class,
App\Exceptions\Handler::class
);
/*
|--------------------------------------------------------------------------
| Return The Application
|--------------------------------------------------------------------------
|
| This script returns the application instance. The instance is given to
| the calling script so we can separate the building of the instances
| from the actual running of the application and sending responses.
|
*/
return $app;

2
bootstrap/cache/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*
!.gitignore

67
composer.json Normal file
View File

@ -0,0 +1,67 @@
{
"name": "laravel/laravel",
"type": "project",
"description": "The skeleton application for the Laravel framework.",
"keywords": ["laravel", "framework"],
"license": "MIT",
"require": {
"php": "^8.1",
"guzzlehttp/guzzle": "^7.2",
"laravel/framework": "^10.10",
"laravel/sanctum": "^3.3",
"laravel/tinker": "^2.8",
"maatwebsite/excel": "^3.1"
},
"require-dev": {
"fakerphp/faker": "^1.9.1",
"laravel/pint": "^1.0",
"laravel/sail": "^1.18",
"mockery/mockery": "^1.4.4",
"nunomaduro/collision": "^7.0",
"phpunit/phpunit": "^10.1",
"spatie/laravel-ignition": "^2.0"
},
"autoload": {
"psr-4": {
"App\\": "app/",
"Database\\Factories\\": "database/factories/",
"Database\\Seeders\\": "database/seeders/"
}
},
"autoload-dev": {
"psr-4": {
"Tests\\": "tests/"
}
},
"scripts": {
"post-autoload-dump": [
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
"@php artisan package:discover --ansi"
],
"post-update-cmd": [
"@php artisan vendor:publish --tag=laravel-assets --ansi --force"
],
"post-root-package-install": [
"@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
],
"post-create-project-cmd": [
"@php artisan key:generate --ansi"
]
},
"extra": {
"laravel": {
"dont-discover": []
}
},
"config": {
"optimize-autoloader": true,
"preferred-install": "dist",
"sort-packages": true,
"allow-plugins": {
"pestphp/pest-plugin": true,
"php-http/discovery": true
}
},
"minimum-stability": "stable",
"prefer-stable": true
}

8729
composer.lock generated Normal file

File diff suppressed because it is too large Load Diff

190
config/app.php Normal file
View File

@ -0,0 +1,190 @@
<?php
use Illuminate\Support\Facades\Facade;
use Illuminate\Support\ServiceProvider;
return [
/*
|--------------------------------------------------------------------------
| Application Name
|--------------------------------------------------------------------------
|
| This value is the name of your application. This value is used when the
| framework needs to place the application's name in a notification or
| any other location as required by the application or its packages.
|
*/
'name' => env('APP_NAME', 'Laravel'),
/*
|--------------------------------------------------------------------------
| Application Environment
|--------------------------------------------------------------------------
|
| This value determines the "environment" your application is currently
| running in. This may determine how you prefer to configure various
| services the application utilizes. Set this in your ".env" file.
|
*/
'env' => env('APP_ENV', 'production'),
/*
|--------------------------------------------------------------------------
| Application Debug Mode
|--------------------------------------------------------------------------
|
| When your application is in debug mode, detailed error messages with
| stack traces will be shown on every error that occurs within your
| application. If disabled, a simple generic error page is shown.
|
*/
'debug' => (bool) env('APP_DEBUG', false),
/*
|--------------------------------------------------------------------------
| Application URL
|--------------------------------------------------------------------------
|
| This URL is used by the console to properly generate URLs when using
| the Artisan command line tool. You should set this to the root of
| your application so that it is used when running Artisan tasks.
|
*/
'url' => env('APP_URL', 'http://localhost'),
'asset_url' => env('ASSET_URL'),
/*
|--------------------------------------------------------------------------
| Application Timezone
|--------------------------------------------------------------------------
|
| Here you may specify the default timezone for your application, which
| will be used by the PHP date and date-time functions. We have gone
| ahead and set this to a sensible default for you out of the box.
|
*/
'timezone' => 'UTC',
/*
|--------------------------------------------------------------------------
| Application Locale Configuration
|--------------------------------------------------------------------------
|
| The application locale determines the default locale that will be used
| by the translation service provider. You are free to set this value
| to any of the locales which will be supported by the application.
|
*/
'locale' => 'en',
/*
|--------------------------------------------------------------------------
| Application Fallback Locale
|--------------------------------------------------------------------------
|
| The fallback locale determines the locale to use when the current one
| is not available. You may change the value to correspond to any of
| the language folders that are provided through your application.
|
*/
'fallback_locale' => 'en',
/*
|--------------------------------------------------------------------------
| Faker Locale
|--------------------------------------------------------------------------
|
| This locale will be used by the Faker PHP library when generating fake
| data for your database seeds. For example, this will be used to get
| localized telephone numbers, street address information and more.
|
*/
'faker_locale' => 'en_US',
/*
|--------------------------------------------------------------------------
| Encryption Key
|--------------------------------------------------------------------------
|
| This key is used by the Illuminate encrypter service and should be set
| to a random, 32 character string, otherwise these encrypted strings
| will not be safe. Please do this before deploying an application!
|
*/
'key' => env('APP_KEY'),
'cipher' => 'AES-256-CBC',
/*
|--------------------------------------------------------------------------
| Maintenance Mode Driver
|--------------------------------------------------------------------------
|
| These configuration options determine the driver used to determine and
| manage Laravel's "maintenance mode" status. The "cache" driver will
| allow maintenance mode to be controlled across multiple machines.
|
| Supported drivers: "file", "cache"
|
*/
'maintenance' => [
'driver' => 'file',
// 'store' => 'redis',
],
/*
|--------------------------------------------------------------------------
| Autoloaded Service Providers
|--------------------------------------------------------------------------
|
| The service providers listed here will be automatically loaded on the
| request to your application. Feel free to add your own services to
| this array to grant expanded functionality to your applications.
|
*/
'providers' => ServiceProvider::defaultProviders()->merge([
/*
* Package Service Providers...
*/
Maatwebsite\Excel\ExcelServiceProvider::class,
/*
* Application Service Providers...
*/
App\Providers\AppServiceProvider::class,
App\Providers\AuthServiceProvider::class,
// App\Providers\BroadcastServiceProvider::class,
App\Providers\EventServiceProvider::class,
App\Providers\RouteServiceProvider::class,
])->toArray(),
/*
|--------------------------------------------------------------------------
| Class Aliases
|--------------------------------------------------------------------------
|
| This array of class aliases will be registered when this application
| is started. However, feel free to register as many as you wish as
| the aliases are "lazy" loaded so they don't hinder performance.
|
*/
'aliases' => Facade::defaultAliases()->merge([
// 'Example' => App\Facades\Example::class,
'Excel' => Maatwebsite\Excel\Facades\Excel::class,
])->toArray(),
];

125
config/auth.php Normal file
View File

@ -0,0 +1,125 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Authentication Defaults
|--------------------------------------------------------------------------
|
| This option controls the default authentication "guard" and password
| reset options for your application. You may change these defaults
| as required, but they're a perfect start for most applications.
|
*/
'defaults' => [
'guard' => 'web',
'passwords' => 'pengguna',
],
/*
|--------------------------------------------------------------------------
| Authentication Guards
|--------------------------------------------------------------------------
|
| Next, you may define every authentication guard for your application.
| Of course, a great default configuration has been defined for you
| here which uses session storage and the Eloquent user provider.
|
| All authentication drivers have a user provider. This defines how the
| users are actually retrieved out of your database or other storage
| mechanisms used by this application to persist your user's data.
|
| Supported: "session"
|
*/
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'pengguna',
],
'petugas' => [
'driver' => 'session',
'provider' => 'petugas',
],
],
/*
|--------------------------------------------------------------------------
| User Providers
|--------------------------------------------------------------------------
|
| All authentication drivers have a user provider. This defines how the
| users are actually retrieved out of your database or other storage
| mechanisms used by this application to persist your user's data.
|
| If you have multiple user tables or models you may configure multiple
| sources which represent each model / table. These sources may then
| be assigned to any extra authentication guards you have defined.
|
| Supported: "database", "eloquent"
|
*/
'providers' => [
'pengguna' => [
'driver' => 'eloquent',
'model' => App\Models\Pengguna::class,
],
'petugas' => [
'driver' => 'eloquent',
'model' => App\Models\Petugas::class,
],
// 'users' => [
// 'driver' => 'database',
// 'table' => 'users',
// ],
],
/*
|--------------------------------------------------------------------------
| Resetting Passwords
|--------------------------------------------------------------------------
|
| You may specify multiple password reset configurations if you have more
| than one user table or model in the application and you want to have
| separate password reset settings based on the specific user types.
|
| The expiry time is the number of minutes that each reset token will be
| considered valid. This security feature keeps tokens short-lived so
| they have less time to be guessed. You may change this as needed.
|
| The throttle setting is the number of seconds a user must wait before
| generating more password reset tokens. This prevents the user from
| quickly generating a very large amount of password reset tokens.
|
*/
'passwords' => [
'pengguna' => [
'provider' => 'pengguna',
'table' => 'password_reset_tokens',
'expire' => 60,
'throttle' => 60,
],
],
/*
|--------------------------------------------------------------------------
| Password Confirmation Timeout
|--------------------------------------------------------------------------
|
| Here you may define the amount of seconds before a password confirmation
| times out and the user is prompted to re-enter their password via the
| confirmation screen. By default, the timeout lasts for three hours.
|
*/
'password_timeout' => 10800,
];

71
config/broadcasting.php Normal file
View File

@ -0,0 +1,71 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Default Broadcaster
|--------------------------------------------------------------------------
|
| This option controls the default broadcaster that will be used by the
| framework when an event needs to be broadcast. You may set this to
| any of the connections defined in the "connections" array below.
|
| Supported: "pusher", "ably", "redis", "log", "null"
|
*/
'default' => env('BROADCAST_DRIVER', 'null'),
/*
|--------------------------------------------------------------------------
| Broadcast Connections
|--------------------------------------------------------------------------
|
| Here you may define all of the broadcast connections that will be used
| to broadcast events to other systems or over websockets. Samples of
| each available type of connection are provided inside this array.
|
*/
'connections' => [
'pusher' => [
'driver' => 'pusher',
'key' => env('PUSHER_APP_KEY'),
'secret' => env('PUSHER_APP_SECRET'),
'app_id' => env('PUSHER_APP_ID'),
'options' => [
'cluster' => env('PUSHER_APP_CLUSTER'),
'host' => env('PUSHER_HOST') ?: 'api-'.env('PUSHER_APP_CLUSTER', 'mt1').'.pusher.com',
'port' => env('PUSHER_PORT', 443),
'scheme' => env('PUSHER_SCHEME', 'https'),
'encrypted' => true,
'useTLS' => env('PUSHER_SCHEME', 'https') === 'https',
],
'client_options' => [
// Guzzle client options: https://docs.guzzlephp.org/en/stable/request-options.html
],
],
'ably' => [
'driver' => 'ably',
'key' => env('ABLY_KEY'),
],
'redis' => [
'driver' => 'redis',
'connection' => 'default',
],
'log' => [
'driver' => 'log',
],
'null' => [
'driver' => 'null',
],
],
];

111
config/cache.php Normal file
View File

@ -0,0 +1,111 @@
<?php
use Illuminate\Support\Str;
return [
/*
|--------------------------------------------------------------------------
| Default Cache Store
|--------------------------------------------------------------------------
|
| This option controls the default cache connection that gets used while
| using this caching library. This connection is used when another is
| not explicitly specified when executing a given caching function.
|
*/
'default' => env('CACHE_DRIVER', 'file'),
/*
|--------------------------------------------------------------------------
| Cache Stores
|--------------------------------------------------------------------------
|
| Here you may define all of the cache "stores" for your application as
| well as their drivers. You may even define multiple stores for the
| same cache driver to group types of items stored in your caches.
|
| Supported drivers: "apc", "array", "database", "file",
| "memcached", "redis", "dynamodb", "octane", "null"
|
*/
'stores' => [
'apc' => [
'driver' => 'apc',
],
'array' => [
'driver' => 'array',
'serialize' => false,
],
'database' => [
'driver' => 'database',
'table' => 'cache',
'connection' => null,
'lock_connection' => null,
],
'file' => [
'driver' => 'file',
'path' => storage_path('framework/cache/data'),
'lock_path' => storage_path('framework/cache/data'),
],
'memcached' => [
'driver' => 'memcached',
'persistent_id' => env('MEMCACHED_PERSISTENT_ID'),
'sasl' => [
env('MEMCACHED_USERNAME'),
env('MEMCACHED_PASSWORD'),
],
'options' => [
// Memcached::OPT_CONNECT_TIMEOUT => 2000,
],
'servers' => [
[
'host' => env('MEMCACHED_HOST', '127.0.0.1'),
'port' => env('MEMCACHED_PORT', 11211),
'weight' => 100,
],
],
],
'redis' => [
'driver' => 'redis',
'connection' => 'cache',
'lock_connection' => 'default',
],
'dynamodb' => [
'driver' => 'dynamodb',
'key' => env('AWS_ACCESS_KEY_ID'),
'secret' => env('AWS_SECRET_ACCESS_KEY'),
'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
'table' => env('DYNAMODB_CACHE_TABLE', 'cache'),
'endpoint' => env('DYNAMODB_ENDPOINT'),
],
'octane' => [
'driver' => 'octane',
],
],
/*
|--------------------------------------------------------------------------
| Cache Key Prefix
|--------------------------------------------------------------------------
|
| When utilizing the APC, database, memcached, Redis, or DynamoDB cache
| stores there might be other applications using the same cache. For
| that reason, you may prefix every cache key to avoid collisions.
|
*/
'prefix' => env('CACHE_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_cache_'),
];

34
config/cors.php Normal file
View File

@ -0,0 +1,34 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Cross-Origin Resource Sharing (CORS) Configuration
|--------------------------------------------------------------------------
|
| Here you may configure your settings for cross-origin resource sharing
| or "CORS". This determines what cross-origin operations may execute
| in web browsers. You are free to adjust these settings as needed.
|
| To learn more: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
|
*/
'paths' => ['api/*', 'sanctum/csrf-cookie'],
'allowed_methods' => ['*'],
'allowed_origins' => ['*'],
'allowed_origins_patterns' => [],
'allowed_headers' => ['*'],
'exposed_headers' => [],
'max_age' => 0,
'supports_credentials' => false,
];

151
config/database.php Normal file
View File

@ -0,0 +1,151 @@
<?php
use Illuminate\Support\Str;
return [
/*
|--------------------------------------------------------------------------
| Default Database Connection Name
|--------------------------------------------------------------------------
|
| Here you may specify which of the database connections below you wish
| to use as your default connection for all database work. Of course
| you may use many connections at once using the Database library.
|
*/
'default' => env('DB_CONNECTION', 'mysql'),
/*
|--------------------------------------------------------------------------
| Database Connections
|--------------------------------------------------------------------------
|
| Here are each of the database connections setup for your application.
| Of course, examples of configuring each database platform that is
| supported by Laravel is shown below to make development simple.
|
|
| All database work in Laravel is done through the PHP PDO facilities
| so make sure you have the driver for your particular database of
| choice installed on your machine before you begin development.
|
*/
'connections' => [
'sqlite' => [
'driver' => 'sqlite',
'url' => env('DATABASE_URL'),
'database' => env('DB_DATABASE', database_path('database.sqlite')),
'prefix' => '',
'foreign_key_constraints' => env('DB_FOREIGN_KEYS', true),
],
'mysql' => [
'driver' => 'mysql',
'url' => env('DATABASE_URL'),
'host' => env('DB_HOST', '127.0.0.1'),
'port' => env('DB_PORT', '3306'),
'database' => env('DB_DATABASE', 'forge'),
'username' => env('DB_USERNAME', 'forge'),
'password' => env('DB_PASSWORD', ''),
'unix_socket' => env('DB_SOCKET', ''),
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
'prefix' => '',
'prefix_indexes' => true,
'strict' => true,
'engine' => null,
'options' => extension_loaded('pdo_mysql') ? array_filter([
PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
]) : [],
],
'pgsql' => [
'driver' => 'pgsql',
'url' => env('DATABASE_URL'),
'host' => env('DB_HOST', '127.0.0.1'),
'port' => env('DB_PORT', '5432'),
'database' => env('DB_DATABASE', 'forge'),
'username' => env('DB_USERNAME', 'forge'),
'password' => env('DB_PASSWORD', ''),
'charset' => 'utf8',
'prefix' => '',
'prefix_indexes' => true,
'search_path' => 'public',
'sslmode' => 'prefer',
],
'sqlsrv' => [
'driver' => 'sqlsrv',
'url' => env('DATABASE_URL'),
'host' => env('DB_HOST', 'localhost'),
'port' => env('DB_PORT', '1433'),
'database' => env('DB_DATABASE', 'forge'),
'username' => env('DB_USERNAME', 'forge'),
'password' => env('DB_PASSWORD', ''),
'charset' => 'utf8',
'prefix' => '',
'prefix_indexes' => true,
// 'encrypt' => env('DB_ENCRYPT', 'yes'),
// 'trust_server_certificate' => env('DB_TRUST_SERVER_CERTIFICATE', 'false'),
],
],
/*
|--------------------------------------------------------------------------
| Migration Repository Table
|--------------------------------------------------------------------------
|
| This table keeps track of all the migrations that have already run for
| your application. Using this information, we can determine which of
| the migrations on disk haven't actually been run in the database.
|
*/
'migrations' => 'migrations',
/*
|--------------------------------------------------------------------------
| Redis Databases
|--------------------------------------------------------------------------
|
| Redis is an open source, fast, and advanced key-value store that also
| provides a richer body of commands than a typical key-value system
| such as APC or Memcached. Laravel makes it easy to dig right in.
|
*/
'redis' => [
'client' => env('REDIS_CLIENT', 'phpredis'),
'options' => [
'cluster' => env('REDIS_CLUSTER', 'redis'),
'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_database_'),
],
'default' => [
'url' => env('REDIS_URL'),
'host' => env('REDIS_HOST', '127.0.0.1'),
'username' => env('REDIS_USERNAME'),
'password' => env('REDIS_PASSWORD'),
'port' => env('REDIS_PORT', '6379'),
'database' => env('REDIS_DB', '0'),
],
'cache' => [
'url' => env('REDIS_URL'),
'host' => env('REDIS_HOST', '127.0.0.1'),
'username' => env('REDIS_USERNAME'),
'password' => env('REDIS_PASSWORD'),
'port' => env('REDIS_PORT', '6379'),
'database' => env('REDIS_CACHE_DB', '1'),
],
],
];

380
config/excel.php Normal file
View File

@ -0,0 +1,380 @@
<?php
use Maatwebsite\Excel\Excel;
use PhpOffice\PhpSpreadsheet\Reader\Csv;
return [
'exports' => [
/*
|--------------------------------------------------------------------------
| Chunk size
|--------------------------------------------------------------------------
|
| When using FromQuery, the query is automatically chunked.
| Here you can specify how big the chunk should be.
|
*/
'chunk_size' => 1000,
/*
|--------------------------------------------------------------------------
| Pre-calculate formulas during export
|--------------------------------------------------------------------------
*/
'pre_calculate_formulas' => false,
/*
|--------------------------------------------------------------------------
| Enable strict null comparison
|--------------------------------------------------------------------------
|
| When enabling strict null comparison empty cells ('') will
| be added to the sheet.
*/
'strict_null_comparison' => false,
/*
|--------------------------------------------------------------------------
| CSV Settings
|--------------------------------------------------------------------------
|
| Configure e.g. delimiter, enclosure and line ending for CSV exports.
|
*/
'csv' => [
'delimiter' => ',',
'enclosure' => '"',
'line_ending' => PHP_EOL,
'use_bom' => false,
'include_separator_line' => false,
'excel_compatibility' => false,
'output_encoding' => '',
'test_auto_detect' => true,
],
/*
|--------------------------------------------------------------------------
| Worksheet properties
|--------------------------------------------------------------------------
|
| Configure e.g. default title, creator, subject,...
|
*/
'properties' => [
'creator' => '',
'lastModifiedBy' => '',
'title' => '',
'description' => '',
'subject' => '',
'keywords' => '',
'category' => '',
'manager' => '',
'company' => '',
],
],
'imports' => [
/*
|--------------------------------------------------------------------------
| Read Only
|--------------------------------------------------------------------------
|
| When dealing with imports, you might only be interested in the
| data that the sheet exists. By default we ignore all styles,
| however if you want to do some logic based on style data
| you can enable it by setting read_only to false.
|
*/
'read_only' => true,
/*
|--------------------------------------------------------------------------
| Ignore Empty
|--------------------------------------------------------------------------
|
| When dealing with imports, you might be interested in ignoring
| rows that have null values or empty strings. By default rows
| containing empty strings or empty values are not ignored but can be
| ignored by enabling the setting ignore_empty to true.
|
*/
'ignore_empty' => false,
/*
|--------------------------------------------------------------------------
| Heading Row Formatter
|--------------------------------------------------------------------------
|
| Configure the heading row formatter.
| Available options: none|slug|custom
|
*/
'heading_row' => [
'formatter' => 'slug',
],
/*
|--------------------------------------------------------------------------
| CSV Settings
|--------------------------------------------------------------------------
|
| Configure e.g. delimiter, enclosure and line ending for CSV imports.
|
*/
'csv' => [
'delimiter' => null,
'enclosure' => '"',
'escape_character' => '\\',
'contiguous' => false,
'input_encoding' => Csv::GUESS_ENCODING,
],
/*
|--------------------------------------------------------------------------
| Worksheet properties
|--------------------------------------------------------------------------
|
| Configure e.g. default title, creator, subject,...
|
*/
'properties' => [
'creator' => '',
'lastModifiedBy' => '',
'title' => '',
'description' => '',
'subject' => '',
'keywords' => '',
'category' => '',
'manager' => '',
'company' => '',
],
/*
|--------------------------------------------------------------------------
| Cell Middleware
|--------------------------------------------------------------------------
|
| Configure middleware that is executed on getting a cell value
|
*/
'cells' => [
'middleware' => [
//\Maatwebsite\Excel\Middleware\TrimCellValue::class,
//\Maatwebsite\Excel\Middleware\ConvertEmptyCellValuesToNull::class,
],
],
],
/*
|--------------------------------------------------------------------------
| Extension detector
|--------------------------------------------------------------------------
|
| Configure here which writer/reader type should be used when the package
| needs to guess the correct type based on the extension alone.
|
*/
'extension_detector' => [
'xlsx' => Excel::XLSX,
'xlsm' => Excel::XLSX,
'xltx' => Excel::XLSX,
'xltm' => Excel::XLSX,
'xls' => Excel::XLS,
'xlt' => Excel::XLS,
'ods' => Excel::ODS,
'ots' => Excel::ODS,
'slk' => Excel::SLK,
'xml' => Excel::XML,
'gnumeric' => Excel::GNUMERIC,
'htm' => Excel::HTML,
'html' => Excel::HTML,
'csv' => Excel::CSV,
'tsv' => Excel::TSV,
/*
|--------------------------------------------------------------------------
| PDF Extension
|--------------------------------------------------------------------------
|
| Configure here which Pdf driver should be used by default.
| Available options: Excel::MPDF | Excel::TCPDF | Excel::DOMPDF
|
*/
'pdf' => Excel::DOMPDF,
],
/*
|--------------------------------------------------------------------------
| Value Binder
|--------------------------------------------------------------------------
|
| PhpSpreadsheet offers a way to hook into the process of a value being
| written to a cell. In there some assumptions are made on how the
| value should be formatted. If you want to change those defaults,
| you can implement your own default value binder.
|
| Possible value binders:
|
| [x] Maatwebsite\Excel\DefaultValueBinder::class
| [x] PhpOffice\PhpSpreadsheet\Cell\StringValueBinder::class
| [x] PhpOffice\PhpSpreadsheet\Cell\AdvancedValueBinder::class
|
*/
'value_binder' => [
'default' => Maatwebsite\Excel\DefaultValueBinder::class,
],
'cache' => [
/*
|--------------------------------------------------------------------------
| Default cell caching driver
|--------------------------------------------------------------------------
|
| By default PhpSpreadsheet keeps all cell values in memory, however when
| dealing with large files, this might result into memory issues. If you
| want to mitigate that, you can configure a cell caching driver here.
| When using the illuminate driver, it will store each value in the
| cache store. This can slow down the process, because it needs to
| store each value. You can use the "batch" store if you want to
| only persist to the store when the memory limit is reached.
|
| Drivers: memory|illuminate|batch
|
*/
'driver' => 'memory',
/*
|--------------------------------------------------------------------------
| Batch memory caching
|--------------------------------------------------------------------------
|
| When dealing with the "batch" caching driver, it will only
| persist to the store when the memory limit is reached.
| Here you can tweak the memory limit to your liking.
|
*/
'batch' => [
'memory_limit' => 60000,
],
/*
|--------------------------------------------------------------------------
| Illuminate cache
|--------------------------------------------------------------------------
|
| When using the "illuminate" caching driver, it will automatically use
| your default cache store. However if you prefer to have the cell
| cache on a separate store, you can configure the store name here.
| You can use any store defined in your cache config. When leaving
| at "null" it will use the default store.
|
*/
'illuminate' => [
'store' => null,
],
/*
|--------------------------------------------------------------------------
| Cache Time-to-live (TTL)
|--------------------------------------------------------------------------
|
| The TTL of items written to cache. If you want to keep the items cached
| indefinitely, set this to null. Otherwise, set a number of seconds,
| a \DateInterval, or a callable.
|
| Allowable types: callable|\DateInterval|int|null
|
*/
'default_ttl' => 10800,
],
/*
|--------------------------------------------------------------------------
| Transaction Handler
|--------------------------------------------------------------------------
|
| By default the import is wrapped in a transaction. This is useful
| for when an import may fail and you want to retry it. With the
| transactions, the previous import gets rolled-back.
|
| You can disable the transaction handler by setting this to null.
| Or you can choose a custom made transaction handler here.
|
| Supported handlers: null|db
|
*/
'transactions' => [
'handler' => 'db',
'db' => [
'connection' => null,
],
],
'temporary_files' => [
/*
|--------------------------------------------------------------------------
| Local Temporary Path
|--------------------------------------------------------------------------
|
| When exporting and importing files, we use a temporary file, before
| storing reading or downloading. Here you can customize that path.
| permissions is an array with the permission flags for the directory (dir)
| and the create file (file).
|
*/
'local_path' => storage_path('framework/cache/laravel-excel'),
/*
|--------------------------------------------------------------------------
| Local Temporary Path Permissions
|--------------------------------------------------------------------------
|
| Permissions is an array with the permission flags for the directory (dir)
| and the create file (file).
| If omitted the default permissions of the filesystem will be used.
|
*/
'local_permissions' => [
// 'dir' => 0755,
// 'file' => 0644,
],
/*
|--------------------------------------------------------------------------
| Remote Temporary Disk
|--------------------------------------------------------------------------
|
| When dealing with a multi server setup with queues in which you
| cannot rely on having a shared local temporary path, you might
| want to store the temporary file on a shared disk. During the
| queue executing, we'll retrieve the temporary file from that
| location instead. When left to null, it will always use
| the local path. This setting only has effect when using
| in conjunction with queued imports and exports.
|
*/
'remote_disk' => null,
'remote_prefix' => null,
/*
|--------------------------------------------------------------------------
| Force Resync
|--------------------------------------------------------------------------
|
| When dealing with a multi server setup as above, it's possible
| for the clean up that occurs after entire queue has been run to only
| cleanup the server that the last AfterImportJob runs on. The rest of the server
| would still have the local temporary file stored on it. In this case your
| local storage limits can be exceeded and future imports won't be processed.
| To mitigate this you can set this config value to be true, so that after every
| queued chunk is processed the local temporary file is deleted on the server that
| processed it.
|
*/
'force_resync_remote' => null,
],
];

76
config/filesystems.php Normal file
View File

@ -0,0 +1,76 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Default Filesystem Disk
|--------------------------------------------------------------------------
|
| Here you may specify the default filesystem disk that should be used
| by the framework. The "local" disk, as well as a variety of cloud
| based disks are available to your application. Just store away!
|
*/
'default' => env('FILESYSTEM_DISK', 'local'),
/*
|--------------------------------------------------------------------------
| Filesystem Disks
|--------------------------------------------------------------------------
|
| Here you may configure as many filesystem "disks" as you wish, and you
| may even configure multiple disks of the same driver. Defaults have
| been set up for each driver as an example of the required values.
|
| Supported Drivers: "local", "ftp", "sftp", "s3"
|
*/
'disks' => [
'local' => [
'driver' => 'local',
'root' => storage_path('app'),
'throw' => false,
],
'public' => [
'driver' => 'local',
'root' => storage_path('app/public'),
'url' => env('APP_URL').'/storage',
'visibility' => 'public',
'throw' => false,
],
's3' => [
'driver' => 's3',
'key' => env('AWS_ACCESS_KEY_ID'),
'secret' => env('AWS_SECRET_ACCESS_KEY'),
'region' => env('AWS_DEFAULT_REGION'),
'bucket' => env('AWS_BUCKET'),
'url' => env('AWS_URL'),
'endpoint' => env('AWS_ENDPOINT'),
'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false),
'throw' => false,
],
],
/*
|--------------------------------------------------------------------------
| Symbolic Links
|--------------------------------------------------------------------------
|
| Here you may configure the symbolic links that will be created when the
| `storage:link` Artisan command is executed. The array keys should be
| the locations of the links and the values should be their targets.
|
*/
'links' => [
public_path('storage') => storage_path('app/public'),
],
];

54
config/hashing.php Normal file
View File

@ -0,0 +1,54 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Default Hash Driver
|--------------------------------------------------------------------------
|
| This option controls the default hash driver that will be used to hash
| passwords for your application. By default, the bcrypt algorithm is
| used; however, you remain free to modify this option if you wish.
|
| Supported: "bcrypt", "argon", "argon2id"
|
*/
'driver' => 'bcrypt',
/*
|--------------------------------------------------------------------------
| Bcrypt Options
|--------------------------------------------------------------------------
|
| Here you may specify the configuration options that should be used when
| passwords are hashed using the Bcrypt algorithm. This will allow you
| to control the amount of time it takes to hash the given password.
|
*/
'bcrypt' => [
'rounds' => env('BCRYPT_ROUNDS', 12),
'verify' => true,
],
/*
|--------------------------------------------------------------------------
| Argon Options
|--------------------------------------------------------------------------
|
| Here you may specify the configuration options that should be used when
| passwords are hashed using the Argon algorithm. These will allow you
| to control the amount of time it takes to hash the given password.
|
*/
'argon' => [
'memory' => 65536,
'threads' => 1,
'time' => 4,
'verify' => true,
],
];

131
config/logging.php Normal file
View File

@ -0,0 +1,131 @@
<?php
use Monolog\Handler\NullHandler;
use Monolog\Handler\StreamHandler;
use Monolog\Handler\SyslogUdpHandler;
use Monolog\Processor\PsrLogMessageProcessor;
return [
/*
|--------------------------------------------------------------------------
| Default Log Channel
|--------------------------------------------------------------------------
|
| This option defines the default log channel that gets used when writing
| messages to the logs. The name specified in this option should match
| one of the channels defined in the "channels" configuration array.
|
*/
'default' => env('LOG_CHANNEL', 'stack'),
/*
|--------------------------------------------------------------------------
| Deprecations Log Channel
|--------------------------------------------------------------------------
|
| This option controls the log channel that should be used to log warnings
| regarding deprecated PHP and library features. This allows you to get
| your application ready for upcoming major versions of dependencies.
|
*/
'deprecations' => [
'channel' => env('LOG_DEPRECATIONS_CHANNEL', 'null'),
'trace' => false,
],
/*
|--------------------------------------------------------------------------
| Log Channels
|--------------------------------------------------------------------------
|
| Here you may configure the log channels for your application. Out of
| the box, Laravel uses the Monolog PHP logging library. This gives
| you a variety of powerful log handlers / formatters to utilize.
|
| Available Drivers: "single", "daily", "slack", "syslog",
| "errorlog", "monolog",
| "custom", "stack"
|
*/
'channels' => [
'stack' => [
'driver' => 'stack',
'channels' => ['single'],
'ignore_exceptions' => false,
],
'single' => [
'driver' => 'single',
'path' => storage_path('logs/laravel.log'),
'level' => env('LOG_LEVEL', 'debug'),
'replace_placeholders' => true,
],
'daily' => [
'driver' => 'daily',
'path' => storage_path('logs/laravel.log'),
'level' => env('LOG_LEVEL', 'debug'),
'days' => 14,
'replace_placeholders' => true,
],
'slack' => [
'driver' => 'slack',
'url' => env('LOG_SLACK_WEBHOOK_URL'),
'username' => 'Laravel Log',
'emoji' => ':boom:',
'level' => env('LOG_LEVEL', 'critical'),
'replace_placeholders' => true,
],
'papertrail' => [
'driver' => 'monolog',
'level' => env('LOG_LEVEL', 'debug'),
'handler' => env('LOG_PAPERTRAIL_HANDLER', SyslogUdpHandler::class),
'handler_with' => [
'host' => env('PAPERTRAIL_URL'),
'port' => env('PAPERTRAIL_PORT'),
'connectionString' => 'tls://'.env('PAPERTRAIL_URL').':'.env('PAPERTRAIL_PORT'),
],
'processors' => [PsrLogMessageProcessor::class],
],
'stderr' => [
'driver' => 'monolog',
'level' => env('LOG_LEVEL', 'debug'),
'handler' => StreamHandler::class,
'formatter' => env('LOG_STDERR_FORMATTER'),
'with' => [
'stream' => 'php://stderr',
],
'processors' => [PsrLogMessageProcessor::class],
],
'syslog' => [
'driver' => 'syslog',
'level' => env('LOG_LEVEL', 'debug'),
'facility' => LOG_USER,
'replace_placeholders' => true,
],
'errorlog' => [
'driver' => 'errorlog',
'level' => env('LOG_LEVEL', 'debug'),
'replace_placeholders' => true,
],
'null' => [
'driver' => 'monolog',
'handler' => NullHandler::class,
],
'emergency' => [
'path' => storage_path('logs/laravel.log'),
],
],
];

134
config/mail.php Normal file
View File

@ -0,0 +1,134 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Default Mailer
|--------------------------------------------------------------------------
|
| This option controls the default mailer that is used to send any email
| messages sent by your application. Alternative mailers may be setup
| and used as needed; however, this mailer will be used by default.
|
*/
'default' => env('MAIL_MAILER', 'smtp'),
/*
|--------------------------------------------------------------------------
| Mailer Configurations
|--------------------------------------------------------------------------
|
| Here you may configure all of the mailers used by your application plus
| their respective settings. Several examples have been configured for
| you and you are free to add your own as your application requires.
|
| Laravel supports a variety of mail "transport" drivers to be used while
| sending an e-mail. You will specify which one you are using for your
| mailers below. You are free to add additional mailers as required.
|
| Supported: "smtp", "sendmail", "mailgun", "ses", "ses-v2",
| "postmark", "log", "array", "failover", "roundrobin"
|
*/
'mailers' => [
'smtp' => [
'transport' => 'smtp',
'url' => env('MAIL_URL'),
'host' => env('MAIL_HOST', 'smtp.mailgun.org'),
'port' => env('MAIL_PORT', 587),
'encryption' => env('MAIL_ENCRYPTION', 'tls'),
'username' => env('MAIL_USERNAME'),
'password' => env('MAIL_PASSWORD'),
'timeout' => null,
'local_domain' => env('MAIL_EHLO_DOMAIN'),
],
'ses' => [
'transport' => 'ses',
],
'postmark' => [
'transport' => 'postmark',
// 'message_stream_id' => null,
// 'client' => [
// 'timeout' => 5,
// ],
],
'mailgun' => [
'transport' => 'mailgun',
// 'client' => [
// 'timeout' => 5,
// ],
],
'sendmail' => [
'transport' => 'sendmail',
'path' => env('MAIL_SENDMAIL_PATH', '/usr/sbin/sendmail -bs -i'),
],
'log' => [
'transport' => 'log',
'channel' => env('MAIL_LOG_CHANNEL'),
],
'array' => [
'transport' => 'array',
],
'failover' => [
'transport' => 'failover',
'mailers' => [
'smtp',
'log',
],
],
'roundrobin' => [
'transport' => 'roundrobin',
'mailers' => [
'ses',
'postmark',
],
],
],
/*
|--------------------------------------------------------------------------
| Global "From" Address
|--------------------------------------------------------------------------
|
| You may wish for all e-mails sent by your application to be sent from
| the same address. Here, you may specify a name and address that is
| used globally for all e-mails that are sent by your application.
|
*/
'from' => [
'address' => env('MAIL_FROM_ADDRESS', 'hello@example.com'),
'name' => env('MAIL_FROM_NAME', 'Example'),
],
/*
|--------------------------------------------------------------------------
| Markdown Mail Settings
|--------------------------------------------------------------------------
|
| If you are using Markdown based email rendering, you may configure your
| theme and component paths here, allowing you to customize the design
| of the emails. Or, you may simply stick with the Laravel defaults!
|
*/
'markdown' => [
'theme' => 'default',
'paths' => [
resource_path('views/vendor/mail'),
],
],
];

109
config/queue.php Normal file
View File

@ -0,0 +1,109 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Default Queue Connection Name
|--------------------------------------------------------------------------
|
| Laravel's queue API supports an assortment of back-ends via a single
| API, giving you convenient access to each back-end using the same
| syntax for every one. Here you may define a default connection.
|
*/
'default' => env('QUEUE_CONNECTION', 'sync'),
/*
|--------------------------------------------------------------------------
| Queue Connections
|--------------------------------------------------------------------------
|
| Here you may configure the connection information for each server that
| is used by your application. A default configuration has been added
| for each back-end shipped with Laravel. You are free to add more.
|
| Drivers: "sync", "database", "beanstalkd", "sqs", "redis", "null"
|
*/
'connections' => [
'sync' => [
'driver' => 'sync',
],
'database' => [
'driver' => 'database',
'table' => 'jobs',
'queue' => 'default',
'retry_after' => 90,
'after_commit' => false,
],
'beanstalkd' => [
'driver' => 'beanstalkd',
'host' => 'localhost',
'queue' => 'default',
'retry_after' => 90,
'block_for' => 0,
'after_commit' => false,
],
'sqs' => [
'driver' => 'sqs',
'key' => env('AWS_ACCESS_KEY_ID'),
'secret' => env('AWS_SECRET_ACCESS_KEY'),
'prefix' => env('SQS_PREFIX', 'https://sqs.us-east-1.amazonaws.com/your-account-id'),
'queue' => env('SQS_QUEUE', 'default'),
'suffix' => env('SQS_SUFFIX'),
'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
'after_commit' => false,
],
'redis' => [
'driver' => 'redis',
'connection' => 'default',
'queue' => env('REDIS_QUEUE', 'default'),
'retry_after' => 90,
'block_for' => null,
'after_commit' => false,
],
],
/*
|--------------------------------------------------------------------------
| Job Batching
|--------------------------------------------------------------------------
|
| The following options configure the database and table that store job
| batching information. These options can be updated to any database
| connection and table which has been defined by your application.
|
*/
'batching' => [
'database' => env('DB_CONNECTION', 'mysql'),
'table' => 'job_batches',
],
/*
|--------------------------------------------------------------------------
| Failed Queue Jobs
|--------------------------------------------------------------------------
|
| These options configure the behavior of failed queue job logging so you
| can control which database and table are used to store the jobs that
| have failed. You may change them to any database / table you wish.
|
*/
'failed' => [
'driver' => env('QUEUE_FAILED_DRIVER', 'database-uuids'),
'database' => env('DB_CONNECTION', 'mysql'),
'table' => 'failed_jobs',
],
];

83
config/sanctum.php Normal file
View File

@ -0,0 +1,83 @@
<?php
use Laravel\Sanctum\Sanctum;
return [
/*
|--------------------------------------------------------------------------
| Stateful Domains
|--------------------------------------------------------------------------
|
| Requests from the following domains / hosts will receive stateful API
| authentication cookies. Typically, these should include your local
| and production domains which access your API via a frontend SPA.
|
*/
'stateful' => explode(',', env('SANCTUM_STATEFUL_DOMAINS', sprintf(
'%s%s',
'localhost,localhost:3000,127.0.0.1,127.0.0.1:8000,::1',
Sanctum::currentApplicationUrlWithPort()
))),
/*
|--------------------------------------------------------------------------
| Sanctum Guards
|--------------------------------------------------------------------------
|
| This array contains the authentication guards that will be checked when
| Sanctum is trying to authenticate a request. If none of these guards
| are able to authenticate the request, Sanctum will use the bearer
| token that's present on an incoming request for authentication.
|
*/
'guard' => ['web'],
/*
|--------------------------------------------------------------------------
| Expiration Minutes
|--------------------------------------------------------------------------
|
| This value controls the number of minutes until an issued token will be
| considered expired. This will override any values set in the token's
| "expires_at" attribute, but first-party sessions are not affected.
|
*/
'expiration' => null,
/*
|--------------------------------------------------------------------------
| Token Prefix
|--------------------------------------------------------------------------
|
| Sanctum can prefix new tokens in order to take advantage of numerous
| security scanning initiatives maintained by open source platforms
| that notify developers if they commit tokens into repositories.
|
| See: https://docs.github.com/en/code-security/secret-scanning/about-secret-scanning
|
*/
'token_prefix' => env('SANCTUM_TOKEN_PREFIX', ''),
/*
|--------------------------------------------------------------------------
| Sanctum Middleware
|--------------------------------------------------------------------------
|
| When authenticating your first-party SPA with Sanctum you may need to
| customize some of the middleware Sanctum uses while processing the
| request. You may change the middleware listed below as required.
|
*/
'middleware' => [
'authenticate_session' => Laravel\Sanctum\Http\Middleware\AuthenticateSession::class,
'encrypt_cookies' => App\Http\Middleware\EncryptCookies::class,
'verify_csrf_token' => App\Http\Middleware\VerifyCsrfToken::class,
],
];

34
config/services.php Normal file
View File

@ -0,0 +1,34 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Third Party Services
|--------------------------------------------------------------------------
|
| This file is for storing the credentials for third party services such
| as Mailgun, Postmark, AWS and more. This file provides the de facto
| location for this type of information, allowing packages to have
| a conventional file to locate the various service credentials.
|
*/
'mailgun' => [
'domain' => env('MAILGUN_DOMAIN'),
'secret' => env('MAILGUN_SECRET'),
'endpoint' => env('MAILGUN_ENDPOINT', 'api.mailgun.net'),
'scheme' => 'https',
],
'postmark' => [
'token' => env('POSTMARK_TOKEN'),
],
'ses' => [
'key' => env('AWS_ACCESS_KEY_ID'),
'secret' => env('AWS_SECRET_ACCESS_KEY'),
'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
],
];

214
config/session.php Normal file
View File

@ -0,0 +1,214 @@
<?php
use Illuminate\Support\Str;
return [
/*
|--------------------------------------------------------------------------
| Default Session Driver
|--------------------------------------------------------------------------
|
| This option controls the default session "driver" that will be used on
| requests. By default, we will use the lightweight native driver but
| you may specify any of the other wonderful drivers provided here.
|
| Supported: "file", "cookie", "database", "apc",
| "memcached", "redis", "dynamodb", "array"
|
*/
'driver' => env('SESSION_DRIVER', 'file'),
/*
|--------------------------------------------------------------------------
| Session Lifetime
|--------------------------------------------------------------------------
|
| Here you may specify the number of minutes that you wish the session
| to be allowed to remain idle before it expires. If you want them
| to immediately expire on the browser closing, set that option.
|
*/
'lifetime' => env('SESSION_LIFETIME', 120),
'expire_on_close' => false,
/*
|--------------------------------------------------------------------------
| Session Encryption
|--------------------------------------------------------------------------
|
| This option allows you to easily specify that all of your session data
| should be encrypted before it is stored. All encryption will be run
| automatically by Laravel and you can use the Session like normal.
|
*/
'encrypt' => false,
/*
|--------------------------------------------------------------------------
| Session File Location
|--------------------------------------------------------------------------
|
| When using the native session driver, we need a location where session
| files may be stored. A default has been set for you but a different
| location may be specified. This is only needed for file sessions.
|
*/
'files' => storage_path('framework/sessions'),
/*
|--------------------------------------------------------------------------
| Session Database Connection
|--------------------------------------------------------------------------
|
| When using the "database" or "redis" session drivers, you may specify a
| connection that should be used to manage these sessions. This should
| correspond to a connection in your database configuration options.
|
*/
'connection' => env('SESSION_CONNECTION'),
/*
|--------------------------------------------------------------------------
| Session Database Table
|--------------------------------------------------------------------------
|
| When using the "database" session driver, you may specify the table we
| should use to manage the sessions. Of course, a sensible default is
| provided for you; however, you are free to change this as needed.
|
*/
'table' => 'sessions',
/*
|--------------------------------------------------------------------------
| Session Cache Store
|--------------------------------------------------------------------------
|
| While using one of the framework's cache driven session backends you may
| list a cache store that should be used for these sessions. This value
| must match with one of the application's configured cache "stores".
|
| Affects: "apc", "dynamodb", "memcached", "redis"
|
*/
'store' => env('SESSION_STORE'),
/*
|--------------------------------------------------------------------------
| Session Sweeping Lottery
|--------------------------------------------------------------------------
|
| Some session drivers must manually sweep their storage location to get
| rid of old sessions from storage. Here are the chances that it will
| happen on a given request. By default, the odds are 2 out of 100.
|
*/
'lottery' => [2, 100],
/*
|--------------------------------------------------------------------------
| Session Cookie Name
|--------------------------------------------------------------------------
|
| Here you may change the name of the cookie used to identify a session
| instance by ID. The name specified here will get used every time a
| new session cookie is created by the framework for every driver.
|
*/
'cookie' => env(
'SESSION_COOKIE',
Str::slug(env('APP_NAME', 'laravel'), '_').'_session'
),
/*
|--------------------------------------------------------------------------
| Session Cookie Path
|--------------------------------------------------------------------------
|
| The session cookie path determines the path for which the cookie will
| be regarded as available. Typically, this will be the root path of
| your application but you are free to change this when necessary.
|
*/
'path' => '/',
/*
|--------------------------------------------------------------------------
| Session Cookie Domain
|--------------------------------------------------------------------------
|
| Here you may change the domain of the cookie used to identify a session
| in your application. This will determine which domains the cookie is
| available to in your application. A sensible default has been set.
|
*/
'domain' => env('SESSION_DOMAIN'),
/*
|--------------------------------------------------------------------------
| HTTPS Only Cookies
|--------------------------------------------------------------------------
|
| By setting this option to true, session cookies will only be sent back
| to the server if the browser has a HTTPS connection. This will keep
| the cookie from being sent to you when it can't be done securely.
|
*/
'secure' => env('SESSION_SECURE_COOKIE'),
/*
|--------------------------------------------------------------------------
| HTTP Access Only
|--------------------------------------------------------------------------
|
| Setting this value to true will prevent JavaScript from accessing the
| value of the cookie and the cookie will only be accessible through
| the HTTP protocol. You are free to modify this option if needed.
|
*/
'http_only' => true,
/*
|--------------------------------------------------------------------------
| Same-Site Cookies
|--------------------------------------------------------------------------
|
| This option determines how your cookies behave when cross-site requests
| take place, and can be used to mitigate CSRF attacks. By default, we
| will set this value to "lax" since this is a secure default value.
|
| Supported: "lax", "strict", "none", null
|
*/
'same_site' => 'lax',
/*
|--------------------------------------------------------------------------
| Partitioned Cookies
|--------------------------------------------------------------------------
|
| Setting this value to true will tie the cookie to the top-level site for
| a cross-site context. Partitioned cookies are accepted by the browser
| when flagged "secure" and the Same-Site attribute is set to "none".
|
*/
'partitioned' => false,
];

36
config/view.php Normal file
View File

@ -0,0 +1,36 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| View Storage Paths
|--------------------------------------------------------------------------
|
| Most templating systems load templates from disk. Here you may specify
| an array of paths that should be checked for your views. Of course
| the usual Laravel view path has already been registered for you.
|
*/
'paths' => [
resource_path('views'),
],
/*
|--------------------------------------------------------------------------
| Compiled View Path
|--------------------------------------------------------------------------
|
| This option determines where all the compiled Blade templates will be
| stored for your application. Typically, this is within the storage
| directory. However, as usual, you are free to change this value.
|
*/
'compiled' => env(
'VIEW_COMPILED_PATH',
realpath(storage_path('framework/views'))
),
];

1
database/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
*.sqlite*

View File

@ -0,0 +1,44 @@
<?php
namespace Database\Factories;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\User>
*/
class UserFactory extends Factory
{
/**
* The current password being used by the factory.
*/
protected static ?string $password;
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
{
return [
'name' => fake()->name(),
'email' => fake()->unique()->safeEmail(),
'email_verified_at' => now(),
'password' => static::$password ??= Hash::make('password'),
'remember_token' => Str::random(10),
];
}
/**
* Indicate that the model's email address should be unverified.
*/
public function unverified(): static
{
return $this->state(fn (array $attributes) => [
'email_verified_at' => null,
]);
}
}

View File

@ -0,0 +1,28 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('password_reset_tokens', function (Blueprint $table) {
$table->string('email')->primary();
$table->string('token');
$table->timestamp('created_at')->nullable();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('password_reset_tokens');
}
};

Some files were not shown because too many files have changed in this diff Show More