Pengantar Beranda
Laravel 12 Breeze Blade
Selamat datang

Rumah Dokumentasi

Dokumentasi lengkap proyek Sistem Pelaporan UKK berbasis Laravel 12 + Breeze Blade. Tersedia penjelasan setiap controller, model, migrasi, middleware, hingga tampilan Blade.

5Controllers
4Models
4Migrations
30+View Files
Tentang Aplikasi

Aplikasi web untuk mencatat dan mengelola laporan dari siswa. Siswa dapat membuat laporan beserta foto, sedangkan admin mengelola kategori dan memberikan tanggapan/status pada setiap laporan yang masuk.

Peran Pengguna

Admin — kelola laporan, kategori, feedback Siswa — buat & lihat laporan sendiri

Stack Teknologi

  • Framework: Laravel 12 (PHP 8.2+)
  • Auth Starter: Laravel Breeze — Blade stack
  • Frontend: Blade Templates + Tailwind CSS v3 (via Vite)
  • Database: MySQL 8
  • Build Tool: Vite + PostCSS
Alur Aplikasi

Alur Siswa

Register / Login
Dashboard Siswa
Buat Laporan
Upload Foto
Pantau Status

Alur Admin

Login
Dashboard Admin
Lihat Semua Laporan
Beri Feedback
Update Status

Status Laporan

pendingdiprosesselesaiditolak
Instalasi Cepat (Laravel 12)
# Clone & install
git clone <repo-url> && cd UKK
composer install
npm install

# Setup environment
cp .env.example .env
php artisan key:generate

# Setup database (isi .env dulu)
php artisan migrate --seed

# Build assets & jalankan
npm run build
php artisan serve
Laravel 12 menggunakan bootstrap/app.php baru untuk registrasi middleware. Tidak ada lagi Kernel.php — middleware didaftarkan langsung di app.php.
Pengantar

Struktur Folder

Organisasi lengkap file dan direktori pada proyek ini.

app/Core
app/
├── Http/
│   ├── Controllers/
│   │   ├── Auth/                    ← 7 controller bawaan Breeze
│   │   ├── Controller.php           ← Abstract base controller
│   │   ├── KategoriController.php   ← CRUD kategori (Admin)
│   │   ├── LaporanController.php    ← CRUD laporan + feedback
│   │   ├── ProfileController.php    ← Kelola profil user
│   │   └── TanggapanController.php  ← Komentar pada laporan
│   ├── Middleware/
│   │   └── RoleMiddleware.php       ← Cek role admin/siswa
│   └── Requests/
│       ├── StoreKategoriRequest.php
│       ├── StoreLaporanRequest.php
│       ├── StoreTanggapanRequest.php
│       ├── UpdateKategoriRequest.php
│       ├── UpdateLaporanRequest.php
│       ├── UpdateTanggapanRequest.php
│       └── ProfileUpdateRequest.php
├── Models/
│   ├── Kategori.php
│   ├── Laporan.php
│   ├── Tanggapan.php
│   └── User.php
├── Policies/
│   ├── KategoriPolicy.php
│   ├── LaporanPolicy.php
│   └── TanggapanPolicy.php
└── Providers/
    └── AppServiceProvider.php
resources/ & database/Views
resources/views/
├── Admin/
│   ├── dashboard.blade.php
│   ├── Kategori/      ← create, edit, index
│   ├── Laporan/       ← tanggapi
│   └── Tanggapan/     ← index
├── Siswa/
│   ├── dashboard.blade.php
│   └── Laporan/       ← create, edit, index
├── auth/            ← login, register, reset-password, dll
├── layouts/         ← app, guest, navigation
├── components/      ← reusable blade components
└── profile/         ← edit + partials

database/
├── migrations/      ← 4 migration files
├── factories/       ← Kategori, Laporan, Tanggapan, User
└── seeders/         ← DatabaseSeeder + per-model seeders
bootstrap/ (Laravel 12)New in L12
bootstrap/
├── app.php     ← Middleware & routing didaftarkan di sini
└── providers.php
Di Laravel 12, tidak ada lagi app/Http/Kernel.php. Middleware global dan route middleware didaftarkan langsung di bootstrap/app.php menggunakan fluent API.
Pengantar

Database & Migrasi

Empat tabel utama yang digunakan aplikasi beserta relasinya.

Tabel usersMigration
Schema::create('users', function (Blueprint $table) {
    $table->id();
    $table->string('name');
    $table->string('email')->unique();
    $table->timestamp('email_verified_at')->nullable();
    $table->string('password');
    $table->enum('role', ['admin', 'siswa'])->default('siswa');
    $table->rememberToken();
    $table->timestamps();
});
Tabel kategorisMigration
Schema::create('kategoris', function (Blueprint $table) {
    $table->id();
    $table->string('nama');
    $table->text('deskripsi')->nullable();
    $table->timestamps();
});
Tabel laporansMigration
Bug sebelumnya: terdapat dua kolom status sekaligus (enum + string). Versi di bawah sudah diperbaiki — hanya gunakan satu kolom enum.
Schema::create('laporans', function (Blueprint $table) {
    $table->id();
    $table->foreignId('user_id')->constrained()->cascadeOnDelete();
    $table->foreignId('kategori_id')->constrained()->cascadeOnDelete();
    $table->foreignId('admin_id')->nullable()->constrained('users')->nullOnDelete();
    $table->string('lokasi');
    $table->text('deskripsi');
    $table->string('foto')->nullable();

    // ✅ Satu kolom status saja (enum)
    $table->enum('status', ['pending', 'diproses', 'selesai', 'ditolak'])
           ->default('pending');

    $table->text('admin_feedback')->nullable();
    $table->timestamp('diterima_pada')->nullable();
    $table->timestamp('ditanggapi_pada')->nullable();
    $table->timestamp('selesai_pada')->nullable();
    $table->timestamps();
});
Tabel tanggapansMigration
Schema::create('tanggapans', function (Blueprint $table) {
    $table->id();
    $table->foreignId('laporan_id')->constrained()->cascadeOnDelete();
    $table->foreignId('user_id')->constrained()->cascadeOnDelete();
    $table->text('isi');
    $table->timestamps();
});
Relasi Antar Tabel
Dari TabelRelasiKe TabelKeterangan
laporansbelongsTousersPembuat laporan (siswa)
laporansbelongsTokategorisKategori laporan
laporansbelongsTousers (admin)Admin yang menangani
laporanshasManytanggapansKomentar pada laporan
tanggapansbelongsTolaporansLaporan yang dikomentari
tanggapansbelongsTousersPemberi komentar
usershasManylaporansSemua laporan milik user
Pengantar

Routes

Peta semua URL di routes/web.php, dikelompokkan berdasarkan akses pengguna.

Routes Publik
MethodURLFungsi
GET/Halaman welcome
GET/loginForm login
POST/loginProses login
GET/registerForm registrasi
POST/registerProses registrasi
GET/forgot-passwordForm lupa password
POST/forgot-passwordKirim link reset
GET/reset-password/{token}Form reset password
POST/reset-passwordProses reset password
Routes AdminAdmin only
Route::middleware(['auth', 'role:admin'])->prefix('admin')->group(function () {
    Route::get('/dashboard', ...)->name('admin.dashboard');

    // Kategori — full resource
    Route::resource('kategori', KategoriController::class);

    // Feedback laporan
    Route::post('/laporan/{laporan}/feedback', [LaporanController::class, 'feedback'])
         ->name('admin.laporan.feedback');

    // Tanggapan
    Route::resource('tanggapan', TanggapanController::class);
});
MethodURLController@Method
GET/admin/dashboarddashboard admin
GET/admin/kategoriKategoriController@index
GET/admin/kategori/createKategoriController@create
POST/admin/kategoriKategoriController@store
GET/admin/kategori/{id}/editKategoriController@edit
PUT/admin/kategori/{id}KategoriController@update
DELETE/admin/kategori/{id}KategoriController@destroy
POST/admin/laporan/{id}/feedbackLaporanController@feedback
GET/admin/tanggapanTanggapanController@index
Routes SiswaSiswa only
MethodURLController@Method
GET/siswa/dashboarddashboard siswa
GET/laporanLaporanController@index
GET/laporan/createLaporanController@create
POST/laporanLaporanController@store
GET/laporan/{id}/editLaporanController@edit
PUT/laporan/{id}LaporanController@update
DELETE/laporan/{id}LaporanController@destroy
Routes Semua User (auth)Semua yang login
MethodURLController@Method
GET/profileProfileController@edit
PUT/profileProfileController@update
DELETE/profileProfileController@destroy
POST/logoutAuthenticatedSessionController@destroy
Controllers

LaporanController

Controller utama untuk mengelola data laporan. Diakses oleh Admin (feedback) dan Siswa (CRUD laporan sendiri).

Info FileController
  • Path: app/Http/Controllers/LaporanController.php
  • Namespace: App\Http\Controllers
  • Middleware: auth + role:siswa (kecuali feedback: role:admin)
  • Form Requests: StoreLaporanRequest, UpdateLaporanRequest
  • Model: Laporan, Kategori
GET index() /laporan Siswa
Menampilkan daftar laporan milik siswa yang sedang login. Data difilter berdasarkan user_id agar siswa hanya melihat laporan miliknya sendiri.
public function index()
{
    $laporans = Laporan::where('user_id', auth()->id())
                        ->with(['kategori'])
                        ->latest()
                        ->get();

    return view('Siswa.Laporan.index', compact('laporans'));
}
GET create() /laporan/create Siswa
Menampilkan form pembuatan laporan baru. Mengambil semua data kategori untuk dropdown pilihan.
public function create()
{
    $kategoris = Kategori::all();
    return view('Siswa.Laporan.create', compact('kategoris'));
}
POST store() /laporan Siswa
Menyimpan laporan baru ke database. Jika ada foto yang diupload, file disimpan ke storage/app/public/laporan_fotos.
public function store(StoreLaporanRequest $request)
{
    $data = $request->validated();
    $data['user_id'] = auth()->id();

    if ($request->hasFile('foto')) {
        $data['foto'] = $request->file('foto')
                               ->store('laporan_fotos', 'public');
    }

    Laporan::create($data);

    return redirect()->route('laporan.index')
                    ->with('success', 'Laporan berhasil dikirim');
}
GET edit() /laporan/{id}/edit Siswa
Menampilkan form edit laporan. Laporan hanya bisa diedit jika masih berstatus pending.
public function edit(Laporan $laporan)
{
    $kategoris = Kategori::all();
    return view('Siswa.Laporan.edit', compact('laporan', 'kategoris'));
}
PUT update() /laporan/{id} Siswa
Memperbarui data laporan. Jika ada foto baru, foto lama dihapus dari storage lalu diganti dengan yang baru.
public function update(UpdateLaporanRequest $request, Laporan $laporan)
{
    $data = $request->validated();

    if ($request->hasFile('foto')) {
        if ($laporan->foto) {
            Storage::disk('public')->delete($laporan->foto);
        }
        $data['foto'] = $request->file('foto')
                               ->store('laporan_fotos', 'public');
    }

    $laporan->update($data);

    return redirect()->route('laporan.index')
                    ->with('success', 'Laporan berhasil diperbarui');
}
DELETE destroy() /laporan/{id} Siswa
Menghapus laporan beserta foto yang tersimpan di storage.
public function destroy(Laporan $laporan)
{
    if ($laporan->foto) {
        Storage::disk('public')->delete($laporan->foto);
    }
    $laporan->delete();

    return back()->with('success', 'Laporan berhasil dihapus');
}
POST feedback() /admin/laporan/{id}/feedback Admin
Admin memberikan tanggapan dan mengubah status laporan. Jika status ditolak namun feedback kosong, akan diisi teks default otomatis.
public function feedback(Request $request, Laporan $laporan)
{
    // Update status & feedback dari form admin
    $laporan->status         = $request->status;
    $laporan->admin_feedback  = $request->admin_feedback;
    $laporan->ditanggapi_pada = now();

    // Jika ditolak tapi alasan kosong → isi default
    if ($request->status === 'ditolak' && !$request->admin_feedback) {
        $laporan->admin_feedback = 'Laporan ditolak';
    }

    // Update timestamp sesuai status
    if ($request->status === 'diproses') {
        $laporan->diterima_pada = now();
    }
    if ($request->status === 'selesai') {
        $laporan->selesai_pada = now();
    }

    $laporan->save();

    return back()->with('success', 'Tanggapan berhasil dikirim');
}
Controllers

KategoriController

Mengelola data kategori laporan. Hanya dapat diakses oleh Admin.

Info FileController
  • Path: app/Http/Controllers/KategoriController.php
  • Middleware: auth, role:admin
  • Form Requests: StoreKategoriRequest, UpdateKategoriRequest
  • Route: Route::resource('kategori', KategoriController::class)
GETindex()/admin/kategori
Menampilkan daftar semua kategori beserta jumlah laporan di masing-masing kategori.
public function index()
{
    $kategoris = Kategori::withCount('laporans')->latest()->get();
    return view('Admin.Kategori.index', compact('kategoris'));
}
GETcreate()/admin/kategori/create
Menampilkan form tambah kategori baru.
public function create()
{
    return view('Admin.Kategori.create');
}
POSTstore()/admin/kategori
Menyimpan kategori baru ke database setelah melewati validasi StoreKategoriRequest.
public function store(StoreKategoriRequest $request)
{
    Kategori::create($request->validated());
    return redirect()->route('kategori.index')
                    ->with('success', 'Kategori berhasil ditambahkan');
}
GETedit()/admin/kategori/{id}/edit
Menampilkan form edit kategori yang dipilih.
public function edit(Kategori $kategori)
{
    return view('Admin.Kategori.edit', compact('kategori'));
}
PUTupdate()/admin/kategori/{id}
Memperbarui data kategori setelah melewati validasi.
public function update(UpdateKategoriRequest $request, Kategori $kategori)
{
    $kategori->update($request->validated());
    return redirect()->route('kategori.index')
                    ->with('success', 'Kategori berhasil diperbarui');
}
DELETEdestroy()/admin/kategori/{id}
Menghapus kategori. Akan gagal jika masih ada laporan yang terhubung (karena cascadeOnDelete atau foreign key constraint).
public function destroy(Kategori $kategori)
{
    $kategori->delete();
    return back()->with('success', 'Kategori berhasil dihapus');
}
Controllers

TanggapanController

Mengelola komentar/tanggapan pada laporan. Bisa diisi oleh admin maupun siswa.

Info FileController
  • Path: app/Http/Controllers/TanggapanController.php
  • Middleware: auth
  • Form Requests: StoreTanggapanRequest, UpdateTanggapanRequest
  • Model: Tanggapan, Laporan
GETindex()/admin/tanggapanAdmin
Menampilkan semua tanggapan yang masuk (untuk halaman admin). Eager load relasi user dan laporan.
public function index()
{
    $tanggapans = Tanggapan::with(['user', 'laporan'])
                             ->latest()->get();
    return view('Admin.Tanggapan.index', compact('tanggapans'));
}
POSTstore()/tanggapanSemua
Menyimpan komentar baru pada suatu laporan. user_id otomatis diambil dari session login.
public function store(StoreTanggapanRequest $request)
{
    Tanggapan::create([
        ...$request->validated(),
        'user_id' => auth()->id(),
    ]);

    return back()->with('success', 'Tanggapan berhasil dikirim');
}
PUTupdate()/tanggapan/{id}Semua
Memperbarui isi komentar. Hanya user yang membuat komentar yang bisa mengedit (dikontrol via Policy).
public function update(UpdateTanggapanRequest $request, Tanggapan $tanggapan)
{
    $tanggapan->update($request->validated());
    return back()->with('success', 'Tanggapan diperbarui');
}
DELETEdestroy()/tanggapan/{id}Semua
Menghapus komentar. Dikontrol oleh TanggapanPolicy agar hanya pembuat atau admin yang bisa menghapus.
public function destroy(Tanggapan $tanggapan)
{
    $tanggapan->delete();
    return back()->with('success', 'Tanggapan dihapus');
}
Controllers

ProfileController

Mengelola profil user yang sedang login — bawaan Laravel Breeze.

GETedit()/profile
Menampilkan halaman edit profil beserta tiga partial form: info profil, ganti password, dan hapus akun.
public function edit(Request $request): View
{
    return view('profile.edit', [
        'user' => $request->user(),
    ]);
}
PUT/PATCHupdate()/profile
Update nama dan email. Jika email berubah, email_verified_at di-reset ke null agar user verifikasi ulang.
public function update(ProfileUpdateRequest $request): RedirectResponse
{
    $request->user()->fill($request->validated());

    if ($request->user()->isDirty('email')) {
        $request->user()->email_verified_at = null;
    }

    $request->user()->save();

    return redirect()->route('profile.edit')
                    ->with('status', 'profile-updated');
}
DELETEdestroy()/profile
Hapus akun. Password harus dikonfirmasi terlebih dahulu sebelum akun dihapus permanen.
public function destroy(Request $request): RedirectResponse
{
    $request->validateWithBag('userDeletion', [
        'password' => ['required', 'current_password'],
    ]);

    $user = $request->user();

    Auth::logout();
    $user->delete();

    $request->session()->invalidate();
    $request->session()->regenerateToken();

    return redirect('/');
}
Controllers

Auth Controllers

Tujuh controller bawaan Laravel 12 Breeze untuk autentikasi pengguna.

Daftar Auth ControllerBreeze Blade
ControllerMethod UtamaFungsi
AuthenticatedSessionControllercreate, store, destroyForm login, proses login, logout
RegisteredUserControllercreate, storeForm & proses registrasi, assign role default siswa
PasswordResetLinkControllercreate, storeForm & kirim link reset password via email
NewPasswordControllercreate, storeForm & proses set password baru
PasswordControllerupdateUpdate password dari halaman profil
EmailVerificationPromptController__invokeTampilkan halaman verifikasi email
VerifyEmailController__invokeProses verifikasi email dari link
RegisteredUserController@store()POST /register
Registrasi user baru dengan role default siswa. Setelah daftar, langsung login dan redirect ke dashboard siswa.
public function store(Request $request): RedirectResponse
{
    $request->validate([
        'name'     => ['required', 'string', 'max:255'],
        'email'    => ['required', 'email', 'unique:users'],
        'password' => ['required', 'confirmed', Password::defaults()],
    ]);

    $user = User::create([
        'name'     => $request->name,
        'email'    => $request->email,
        'password' => Hash::make($request->password),
        'role'     => 'siswa',   // role default
    ]);

    Auth::login($user);

    return redirect(route('siswa.dashboard'));
}
AuthenticatedSessionController@store()POST /login
Proses login. Setelah berhasil, redirect ke dashboard sesuai role (admin.dashboard atau siswa.dashboard).
public function store(LoginRequest $request): RedirectResponse
{
    $request->authenticate();
    $request->session()->regenerate();

    $role = auth()->user()->role;

    return redirect()->intended(
        $role === 'admin'
            ? route('admin.dashboard')
            : route('siswa.dashboard')
    );
}
Models & Data

Models

Eloquent models beserta relasi dan atribut yang dapat diisi.

LaporanModel
class Laporan extends Model
{
    protected $fillable = [
        'user_id', 'kategori_id', 'admin_id',
        'lokasi', 'deskripsi', 'foto', 'status',
        'admin_feedback', 'diterima_pada',
        'ditanggapi_pada', 'selesai_pada',
    ];

    protected $casts = [
        'diterima_pada'   => 'datetime',
        'ditanggapi_pada' => 'datetime',
        'selesai_pada'    => 'datetime',
    ];

    public function user()     { return $this->belongsTo(User::class); }
    public function kategori() { return $this->belongsTo(Kategori::class); }
    public function admin()    { return $this->belongsTo(User::class, 'admin_id'); }
    public function tanggapans() { return $this->hasMany(Tanggapan::class); }
}
KategoriModel
class Kategori extends Model
{
    protected $fillable = ['nama', 'deskripsi'];

    public function laporans()
    {
        return $this->hasMany(Laporan::class);
    }
}
UserModel
class User extends Authenticatable
{
    protected $fillable  = ['name', 'email', 'password', 'role'];
    protected $hidden    = ['password', 'remember_token'];
    protected $casts     = ['password' => 'hashed'];

    public function laporans()  { return $this->hasMany(Laporan::class); }
    public function tanggapans() { return $this->hasMany(Tanggapan::class); }

    public function isAdmin(): bool
    {
        return $this->role === 'admin';
    }
}
TanggapanModel
class Tanggapan extends Model
{
    protected $fillable = ['laporan_id', 'user_id', 'isi'];

    public function laporan() { return $this->belongsTo(Laporan::class); }
    public function user()    { return $this->belongsTo(User::class); }
}
Models & Data

Seeders & Factories

Mengisi database dengan data awal dan data dummy untuk pengembangan.

DatabaseSeederSeeder
Seeder utama yang memanggil semua seeder lain. Jalankan dengan php artisan db:seed.
public function run(): void
{
    // Buat 1 akun admin
    User::factory()->create([
        'name'     => 'Administrator',
        'email'    => 'admin@ukk.test',
        'role'     => 'admin',
    ]);

    // Panggil seeder lain
    $this->call([
        KategoriSeeder::class,
        LaporanSeeder::class,
        TanggapanSeeder::class,
    ]);
}
KategoriSeederSeeder
Mengisi tabel kategoris dengan data awal kategori laporan sekolah.
public function run(): void
{
    $kategoris = [
        ['nama' => 'Sarana & Prasarana', 'deskripsi' => 'Kerusakan fasilitas sekolah'],
        ['nama' => 'Kebersihan',         'deskripsi' => 'Masalah kebersihan lingkungan'],
        ['nama' => 'Keamanan',           'deskripsi' => 'Insiden keamanan di sekolah'],
        ['nama' => 'Lainnya',            'deskripsi' => 'Kategori umum lainnya'],
    ];

    foreach ($kategoris as $k) {
        Kategori::create($k);
    }
}
Lainnya

Middleware

Middleware custom untuk membatasi akses berdasarkan role pengguna.

RoleMiddlewareMiddleware
Mengecek apakah user yang login memiliki role yang sesuai. Jika tidak, redirect ke halaman utama dengan pesan error.
class RoleMiddleware
{
    public function handle(Request $request, Closure $next, string $role): Response
    {
        if (!auth()->check() || auth()->user()->role !== $role) {
            return redirect('/')->with('error', 'Akses ditolak.');
        }

        return $next($request);
    }
}
Registrasi di Laravel 12 (bootstrap/app.php)
Di Laravel 12 tidak ada Kernel.php. Middleware alias didaftarkan langsung di bootstrap/app.php.
// bootstrap/app.php
return Application::configure(basePath: dirname(__DIR__))
    ->withRouting(web: __DIR__.'/../routes/web.php')
    ->withMiddleware(function (Middleware $middleware) {
        // Daftarkan alias 'role'
        $middleware->alias([
            'role' => \App\Http\Middleware\RoleMiddleware::class,
        ]);
    })
    ->create();
Lainnya

Form Requests

Kelas validasi yang memisahkan logika validasi dari controller.

Daftar Form RequestValidation
ClassDipakai diField yang Divalidasi
StoreLaporanRequestLaporanController@storekategori_id*, lokasi*, deskripsi*, foto?
UpdateLaporanRequestLaporanController@updatekategori_id*, lokasi*, deskripsi*, foto?
StoreKategoriRequestKategoriController@storenama*, deskripsi?
UpdateKategoriRequestKategoriController@updatenama*, deskripsi?
StoreTanggapanRequestTanggapanController@storelaporan_id*, isi*
UpdateTanggapanRequestTanggapanController@updateisi*
ProfileUpdateRequestProfileController@updatename*, email*

* = required, ? = nullable/opsional

StoreLaporanRequest
public function rules(): array
{
    return [
        'kategori_id' => ['required', 'exists:kategoris,id'],
        'lokasi'      => ['required', 'string', 'max:255'],
        'deskripsi'   => ['required', 'string'],
        'foto'        => ['nullable', 'image', 'mimes:jpg,png,jpeg', 'max:2048'],
    ];
}
Lainnya

Views (Blade)

Halaman tampilan yang dibuat dengan Blade — bawaan Laravel 12 Breeze stack.

Views AdminAdmin only
FileRouteFungsi
Admin/dashboard.blade.php/admin/dashboardStatistik ringkasan & laporan terbaru
Admin/Kategori/index.blade.php/admin/kategoriTabel semua kategori + tombol aksi
Admin/Kategori/create.blade.php/admin/kategori/createForm tambah kategori
Admin/Kategori/edit.blade.php/admin/kategori/{id}/editForm edit kategori
Admin/Laporan/tanggapi.blade.php/admin/laporan/{id}/tanggapiForm feedback + update status laporan
Admin/Tanggapan/index.blade.php/admin/tanggapanDaftar semua tanggapan
Views SiswaSiswa only
FileRouteFungsi
Siswa/dashboard.blade.php/siswa/dashboardRingkasan laporan milik siswa
Siswa/Laporan/index.blade.php/laporanDaftar laporan + status
Siswa/Laporan/create.blade.php/laporan/createForm kirim laporan baru
Siswa/Laporan/edit.blade.php/laporan/{id}/editForm edit laporan
Layouts & Components Breeze
FileFungsi
layouts/app.blade.phpLayout utama (user terautentikasi) — include navigation
layouts/guest.blade.phpLayout halaman tamu (login/register)
layouts/navigation.blade.phpNavbar responsif — menu beda untuk admin vs siswa
components/text-input.blade.phpInput field reusable dengan styling Tailwind
components/primary-button.blade.phpTombol submit utama
components/input-error.blade.phpTampilkan pesan error validasi per field
components/input-label.blade.phpLabel form dengan styling konsisten
components/modal.blade.phpKomponen modal konfirmasi (delete, dll)
components/dropdown.blade.phpDropdown menu responsif
Lainnya

Policies

Authorization policies untuk mengontrol siapa yang boleh melakukan aksi tertentu.

Daftar Policy
PolicyModelMethod yang Dikontrol
LaporanPolicyLaporanupdate, delete — hanya pembuat laporan
KategoriPolicyKategoriviewAny, create, update, delete — hanya admin
TanggapanPolicyTanggapanupdate, delete — pembuat atau admin
LaporanPolicy
class LaporanPolicy
{
    public function update(User $user, Laporan $laporan): bool
    {
        // Hanya pembuat laporan yang boleh edit
        return $user->id === $laporan->user_id;
    }

    public function delete(User $user, Laporan $laporan): bool
    {
        return $user->id === $laporan->user_id;
    }
}
Cara Pakai di Controller
// Di controller, cukup gunakan $this->authorize()
public function destroy(Laporan $laporan)
{
    $this->authorize('delete', $laporan); // LaporanPolicy@delete

    $laporan->delete();
    return back()->with('success', 'Laporan dihapus');
}
Di Laravel 12, Policy otomatis terdeteksi lewat konvensi nama (Model + "Policy"). Tidak perlu mendaftarkan manual di AuthServiceProvider.