feat: tambah widget statistik kas dan perbaikan alur verifikasi transaksi
- Tambah CashStatsWidget: total saldo, pemasukan/pengeluaran bulan ini, saldo bulan lalu - Widget hanya tampil di halaman transaksi kas (bukan dashboard) - Hanya transaksi yang sudah diverifikasi masuk ke perhitungan total kas - Perbaiki namespace Action notifikasi (Filament v5) - Perbaiki observer: hapus throw Exception yang menyebabkan widget hilang - Tambah redirect setelah aksi setujui/tolak/verifikasi agar halaman refresh otomatis - Tambah file DOKUMENTASI.md
This commit is contained in:
+247
@@ -0,0 +1,247 @@
|
|||||||
|
# Persegi — Sistem Manajemen Organisasi Pemuda Desa
|
||||||
|
|
||||||
|
Sistem web production-ready untuk **Organisasi Pemuda Desa Persegi**, berlokasi di Desa Karangdadap, Kecamatan Kalibagor, Kabupaten Banyumas.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Teknologi
|
||||||
|
|
||||||
|
| Layer | Stack |
|
||||||
|
|---|---|
|
||||||
|
| Backend | Laravel 13 |
|
||||||
|
| Admin Panel | Filament 5.x |
|
||||||
|
| Authorization | Filament Shield + Spatie Permission |
|
||||||
|
| Database | MySQL |
|
||||||
|
| Frontend Publik | Blade |
|
||||||
|
| Realtime | Livewire |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Struktur Direktori
|
||||||
|
|
||||||
|
```
|
||||||
|
app/
|
||||||
|
├── Filament/
|
||||||
|
│ ├── Resources/ # Admin panel resources
|
||||||
|
│ │ ├── Activities/ # Manajemen kegiatan
|
||||||
|
│ │ ├── Approvals/ # Multi-approval
|
||||||
|
│ │ ├── Audits/ # Audit internal
|
||||||
|
│ │ ├── CashCategories/ # Kategori kas
|
||||||
|
│ │ ├── CashRecords/ # Transaksi kas
|
||||||
|
│ │ ├── ContactMessages/# Pesan dari publik
|
||||||
|
│ │ ├── Divisions/ # Divisi organisasi
|
||||||
|
│ │ ├── Posts/ # Konten publik
|
||||||
|
│ │ ├── Users/ # Manajemen anggota
|
||||||
|
│ │ └── Votes/ # Sistem voting
|
||||||
|
│ └── Widgets/
|
||||||
|
│ ├── StatsOverview.php # Widget dashboard utama
|
||||||
|
│ ├── CashStatsWidget.php # Widget statistik kas (halaman transaksi)
|
||||||
|
│ └── ActivityLogWidget.php # Widget log aktivitas
|
||||||
|
├── Models/
|
||||||
|
│ ├── User.php # Anggota / pengguna sistem
|
||||||
|
│ ├── Division.php # Divisi organisasi
|
||||||
|
│ ├── Activity.php # Kegiatan
|
||||||
|
│ ├── CashRecord.php # Transaksi kas
|
||||||
|
│ ├── CashCategory.php # Kategori kas (pemasukan/pengeluaran)
|
||||||
|
│ ├── Vote.php # Voting
|
||||||
|
│ ├── VoteItem.php # Pilihan suara per user
|
||||||
|
│ ├── Approval.php # Multi-approval
|
||||||
|
│ ├── ApprovalItem.php # Keputusan per approver
|
||||||
|
│ ├── Audit.php # Temuan audit
|
||||||
|
│ ├── AuditResponse.php # Respons atas temuan audit
|
||||||
|
│ ├── MemberStatusLog.php # Riwayat perubahan status anggota
|
||||||
|
│ ├── ActivityLog.php # Log global semua aksi
|
||||||
|
│ ├── Post.php # Artikel/berita publik
|
||||||
|
│ └── ContactMessage.php # Pesan kontak dari publik
|
||||||
|
└── Observers/
|
||||||
|
├── CashRecordObserver.php
|
||||||
|
├── ActivityObserver.php
|
||||||
|
└── UserObserver.php
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Role & Hak Akses
|
||||||
|
|
||||||
|
| Role | Deskripsi |
|
||||||
|
|---|---|
|
||||||
|
| `super_admin` | Full akses, bisa override semua, semua aksi di-log |
|
||||||
|
| `ketua` | Approval kegiatan, verifikasi kas, lihat semua data |
|
||||||
|
| `bendahara` | Input & kelola transaksi kas |
|
||||||
|
| `pengurus` | Submit kegiatan ke pending |
|
||||||
|
| `anggota` | Akses terbatas, lihat data sendiri |
|
||||||
|
| `auditor` | Read-only + bisa buat temuan audit |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Fitur & Use Case
|
||||||
|
|
||||||
|
### 1. Manajemen Anggota
|
||||||
|
|
||||||
|
**Aktor:** ketua, pengurus, super_admin
|
||||||
|
|
||||||
|
| Use Case | Deskripsi |
|
||||||
|
|---|---|
|
||||||
|
| Tambah anggota | Input data: nama, telepon, alamat, divisi, status |
|
||||||
|
| Ubah status anggota | Aktif → Nonaktif wajib isi `inactive_reason` |
|
||||||
|
| Lihat riwayat status | Semua perubahan status tercatat di `member_status_logs` |
|
||||||
|
| Filter per divisi | Anggota bisa difilter berdasarkan divisi |
|
||||||
|
|
||||||
|
**Business rule:**
|
||||||
|
- Hanya ketua, pengurus, super_admin yang bisa ubah status
|
||||||
|
- Setiap perubahan status otomatis dicatat ke `member_status_logs` via Observer
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 2. Struktur Organisasi (Divisi)
|
||||||
|
|
||||||
|
**Aktor:** ketua, super_admin
|
||||||
|
|
||||||
|
| Use Case | Deskripsi |
|
||||||
|
|---|---|
|
||||||
|
| Kelola divisi | CRUD divisi organisasi |
|
||||||
|
| Lihat anggota per divisi | Relasi one-to-many ke User |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3. Kegiatan
|
||||||
|
|
||||||
|
**Aktor:** pengurus (buat), ketua (approve), semua (lihat)
|
||||||
|
|
||||||
|
| Use Case | Deskripsi |
|
||||||
|
|---|---|
|
||||||
|
| Buat kegiatan | Status awal: `draft` |
|
||||||
|
| Submit ke review | `draft` → `pending` (oleh pengurus) |
|
||||||
|
| Approve/Tolak | `pending` → `approved` / `rejected` (oleh ketua) |
|
||||||
|
| Tandai selesai | Wajib isi `executed_at` + `execution_notes` |
|
||||||
|
| Kelola peserta | Many-to-many anggota ↔ kegiatan |
|
||||||
|
|
||||||
|
**Workflow status:**
|
||||||
|
```
|
||||||
|
draft → pending → approved → (executed_at diisi)
|
||||||
|
→ rejected
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 4. Keuangan (Kas)
|
||||||
|
|
||||||
|
**Aktor:** bendahara (input), ketua & super_admin (verifikasi & lihat)
|
||||||
|
|
||||||
|
| Use Case | Deskripsi |
|
||||||
|
|---|---|
|
||||||
|
| Input transaksi | Bendahara input pemasukan/pengeluaran |
|
||||||
|
| Verifikasi transaksi | Ketua/super_admin verifikasi, setelah itu data terkunci |
|
||||||
|
| Lihat statistik kas | Widget di halaman transaksi: total saldo, pemasukan/pengeluaran bulan ini, saldo bulan lalu |
|
||||||
|
| Filter transaksi | Filter per kategori, tanggal, status verifikasi |
|
||||||
|
|
||||||
|
**Business rule:**
|
||||||
|
- Setelah `verified_at` terisi, data tidak bisa diubah/dihapus
|
||||||
|
- Threshold keuangan:
|
||||||
|
- < Rp 500.000 → bendahara saja
|
||||||
|
- Rp 500.000 – Rp 2.000.000 → butuh persetujuan ketua
|
||||||
|
- > Rp 2.000.000 → multi-approval atau voting
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 5. Voting
|
||||||
|
|
||||||
|
**Aktor:** semua role (sesuai permission)
|
||||||
|
|
||||||
|
| Use Case | Deskripsi |
|
||||||
|
|---|---|
|
||||||
|
| Buat voting | Tipe: `activity`, `finance`, `general` |
|
||||||
|
| Beri suara | Pilihan: `approve`, `reject`, `abstain` |
|
||||||
|
| Tutup voting | Status: `open` → `closed` |
|
||||||
|
| Lihat hasil | Mayoritas >50% untuk lolos |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 6. Multi-Approval
|
||||||
|
|
||||||
|
**Aktor:** role yang ditentukan per approval
|
||||||
|
|
||||||
|
| Use Case | Deskripsi |
|
||||||
|
|---|---|
|
||||||
|
| Buat approval | Tentukan jumlah approval yang dibutuhkan |
|
||||||
|
| Beri keputusan | Tiap approver bisa approve/reject + catatan |
|
||||||
|
| Cek status | Lolos jika jumlah approve terpenuhi |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 7. Audit Internal
|
||||||
|
|
||||||
|
**Aktor:** auditor (buat temuan), pengurus/ketua (respons)
|
||||||
|
|
||||||
|
| Use Case | Deskripsi |
|
||||||
|
|---|---|
|
||||||
|
| Buat temuan | Tipe: `warning` / `critical`, bisa terkait model apapun |
|
||||||
|
| Respons temuan | Pihak terkait bisa balas temuan audit |
|
||||||
|
| Pantau status | Auditor bisa track status penyelesaian |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 8. Konten Publik
|
||||||
|
|
||||||
|
**Aktor:** pengurus, ketua, super_admin
|
||||||
|
|
||||||
|
| Use Case | Deskripsi |
|
||||||
|
|---|---|
|
||||||
|
| Buat artikel/berita | Post dengan status `draft` / `published` |
|
||||||
|
| Website publik | Halaman publik berbasis Blade untuk masyarakat |
|
||||||
|
| Pesan kontak | Masyarakat bisa kirim pesan, admin bisa lihat & balas |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 9. Dashboard
|
||||||
|
|
||||||
|
Widget yang tampil di halaman utama admin:
|
||||||
|
|
||||||
|
| Widget | Isi |
|
||||||
|
|---|---|
|
||||||
|
| StatsOverview | Anggota aktif, total kas, kegiatan berjalan, kegiatan pending |
|
||||||
|
| ActivityLogWidget | Log aktivitas terbaru di sistem |
|
||||||
|
|
||||||
|
Widget khusus halaman transaksi:
|
||||||
|
|
||||||
|
| Widget | Isi |
|
||||||
|
|---|---|
|
||||||
|
| CashStatsWidget | Total saldo, pemasukan bulan ini, pengeluaran bulan ini, saldo bulan lalu |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Log & Audit Trail
|
||||||
|
|
||||||
|
Semua aksi penting otomatis dicatat via Observer:
|
||||||
|
|
||||||
|
| Event | Dicatat di |
|
||||||
|
|---|---|
|
||||||
|
| Perubahan status anggota | `member_status_logs` |
|
||||||
|
| Transaksi kas baru | `activity_logs` |
|
||||||
|
| Verifikasi kas | `activity_logs` |
|
||||||
|
| Approval kegiatan | `activity_logs` |
|
||||||
|
| Eksekusi kegiatan | `activity_logs` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Keamanan
|
||||||
|
|
||||||
|
- Setiap resource dilindungi Laravel Policy
|
||||||
|
- Permission granular dikelola via Filament Shield
|
||||||
|
- Fitur impersonate: super_admin bisa login sebagai user lain (untuk debugging/support)
|
||||||
|
- Input divalidasi di semua form
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Instalasi
|
||||||
|
|
||||||
|
```bash
|
||||||
|
composer install
|
||||||
|
cp .env.example .env
|
||||||
|
php artisan key:generate
|
||||||
|
php artisan migrate --seed
|
||||||
|
php artisan shield:generate --all
|
||||||
|
php artisan shield:super-admin --user=1
|
||||||
|
```
|
||||||
|
|
||||||
|
Akses admin panel: `/dashboard`
|
||||||
@@ -1,58 +1,247 @@
|
|||||||
<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>
|
# Persegi — Sistem Manajemen Organisasi Pemuda Desa
|
||||||
|
|
||||||
<p align="center">
|
Sistem web production-ready untuk **Organisasi Pemuda Desa Persegi**, berlokasi di Desa Karangdadap, Kecamatan Kalibagor, Kabupaten Banyumas.
|
||||||
<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:
|
## Teknologi
|
||||||
|
|
||||||
- [Simple, fast routing engine](https://laravel.com/docs/routing).
|
| Layer | Stack |
|
||||||
- [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.
|
| Backend | Laravel 13 |
|
||||||
- Expressive, intuitive [database ORM](https://laravel.com/docs/eloquent).
|
| Admin Panel | Filament 5.x |
|
||||||
- Database agnostic [schema migrations](https://laravel.com/docs/migrations).
|
| Authorization | Filament Shield + Spatie Permission |
|
||||||
- [Robust background job processing](https://laravel.com/docs/queues).
|
| Database | MySQL |
|
||||||
- [Real-time event broadcasting](https://laravel.com/docs/broadcasting).
|
| Frontend Publik | Blade |
|
||||||
|
| Realtime | Livewire |
|
||||||
|
|
||||||
Laravel is accessible, powerful, and provides tools required for large, robust applications.
|
---
|
||||||
|
|
||||||
## Learning Laravel
|
## Struktur Direktori
|
||||||
|
|
||||||
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.
|
```
|
||||||
|
app/
|
||||||
In addition, [Laracasts](https://laracasts.com) 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.
|
├── Filament/
|
||||||
|
│ ├── Resources/ # Admin panel resources
|
||||||
You can also watch bite-sized lessons with real-world projects on [Laravel Learn](https://laravel.com/learn), where you will be guided through building a Laravel application from scratch while learning PHP fundamentals.
|
│ │ ├── Activities/ # Manajemen kegiatan
|
||||||
|
│ │ ├── Approvals/ # Multi-approval
|
||||||
## Agentic Development
|
│ │ ├── Audits/ # Audit internal
|
||||||
|
│ │ ├── CashCategories/ # Kategori kas
|
||||||
Laravel's predictable structure and conventions make it ideal for AI coding agents like Claude Code, Cursor, and GitHub Copilot. Install [Laravel Boost](https://laravel.com/docs/ai) to supercharge your AI workflow:
|
│ │ ├── CashRecords/ # Transaksi kas
|
||||||
|
│ │ ├── ContactMessages/# Pesan dari publik
|
||||||
```bash
|
│ │ ├── Divisions/ # Divisi organisasi
|
||||||
composer require laravel/boost --dev
|
│ │ ├── Posts/ # Konten publik
|
||||||
|
│ │ ├── Users/ # Manajemen anggota
|
||||||
php artisan boost:install
|
│ │ └── Votes/ # Sistem voting
|
||||||
|
│ └── Widgets/
|
||||||
|
│ ├── StatsOverview.php # Widget dashboard utama
|
||||||
|
│ ├── CashStatsWidget.php # Widget statistik kas (halaman transaksi)
|
||||||
|
│ └── ActivityLogWidget.php # Widget log aktivitas
|
||||||
|
├── Models/
|
||||||
|
│ ├── User.php # Anggota / pengguna sistem
|
||||||
|
│ ├── Division.php # Divisi organisasi
|
||||||
|
│ ├── Activity.php # Kegiatan
|
||||||
|
│ ├── CashRecord.php # Transaksi kas
|
||||||
|
│ ├── CashCategory.php # Kategori kas (pemasukan/pengeluaran)
|
||||||
|
│ ├── Vote.php # Voting
|
||||||
|
│ ├── VoteItem.php # Pilihan suara per user
|
||||||
|
│ ├── Approval.php # Multi-approval
|
||||||
|
│ ├── ApprovalItem.php # Keputusan per approver
|
||||||
|
│ ├── Audit.php # Temuan audit
|
||||||
|
│ ├── AuditResponse.php # Respons atas temuan audit
|
||||||
|
│ ├── MemberStatusLog.php # Riwayat perubahan status anggota
|
||||||
|
│ ├── ActivityLog.php # Log global semua aksi
|
||||||
|
│ ├── Post.php # Artikel/berita publik
|
||||||
|
│ └── ContactMessage.php # Pesan kontak dari publik
|
||||||
|
└── Observers/
|
||||||
|
├── CashRecordObserver.php
|
||||||
|
├── ActivityObserver.php
|
||||||
|
└── UserObserver.php
|
||||||
```
|
```
|
||||||
|
|
||||||
Boost provides your agent 15+ tools and skills that help agents build Laravel applications while following best practices.
|
---
|
||||||
|
|
||||||
## Contributing
|
## Role & Hak Akses
|
||||||
|
|
||||||
Thank you for considering contributing to the Laravel framework! The contribution guide can be found in the [Laravel documentation](https://laravel.com/docs/contributions).
|
| Role | Deskripsi |
|
||||||
|
|---|---|
|
||||||
|
| `super_admin` | Full akses, bisa override semua, semua aksi di-log |
|
||||||
|
| `ketua` | Approval kegiatan, verifikasi kas, lihat semua data |
|
||||||
|
| `bendahara` | Input & kelola transaksi kas |
|
||||||
|
| `pengurus` | Submit kegiatan ke pending |
|
||||||
|
| `anggota` | Akses terbatas, lihat data sendiri |
|
||||||
|
| `auditor` | Read-only + bisa buat temuan audit |
|
||||||
|
|
||||||
## 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).
|
## Fitur & Use Case
|
||||||
|
|
||||||
## Security Vulnerabilities
|
### 1. Manajemen Anggota
|
||||||
|
|
||||||
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.
|
**Aktor:** ketua, pengurus, super_admin
|
||||||
|
|
||||||
## License
|
| Use Case | Deskripsi |
|
||||||
|
|---|---|
|
||||||
|
| Tambah anggota | Input data: nama, telepon, alamat, divisi, status |
|
||||||
|
| Ubah status anggota | Aktif → Nonaktif wajib isi `inactive_reason` |
|
||||||
|
| Lihat riwayat status | Semua perubahan status tercatat di `member_status_logs` |
|
||||||
|
| Filter per divisi | Anggota bisa difilter berdasarkan divisi |
|
||||||
|
|
||||||
The Laravel framework is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT).
|
**Business rule:**
|
||||||
|
- Hanya ketua, pengurus, super_admin yang bisa ubah status
|
||||||
|
- Setiap perubahan status otomatis dicatat ke `member_status_logs` via Observer
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 2. Struktur Organisasi (Divisi)
|
||||||
|
|
||||||
|
**Aktor:** ketua, super_admin
|
||||||
|
|
||||||
|
| Use Case | Deskripsi |
|
||||||
|
|---|---|
|
||||||
|
| Kelola divisi | CRUD divisi organisasi |
|
||||||
|
| Lihat anggota per divisi | Relasi one-to-many ke User |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3. Kegiatan
|
||||||
|
|
||||||
|
**Aktor:** pengurus (buat), ketua (approve), semua (lihat)
|
||||||
|
|
||||||
|
| Use Case | Deskripsi |
|
||||||
|
|---|---|
|
||||||
|
| Buat kegiatan | Status awal: `draft` |
|
||||||
|
| Submit ke review | `draft` → `pending` (oleh pengurus) |
|
||||||
|
| Approve/Tolak | `pending` → `approved` / `rejected` (oleh ketua) |
|
||||||
|
| Tandai selesai | Wajib isi `executed_at` + `execution_notes` |
|
||||||
|
| Kelola peserta | Many-to-many anggota ↔ kegiatan |
|
||||||
|
|
||||||
|
**Workflow status:**
|
||||||
|
```
|
||||||
|
draft → pending → approved → (executed_at diisi)
|
||||||
|
→ rejected
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 4. Keuangan (Kas)
|
||||||
|
|
||||||
|
**Aktor:** bendahara (input), ketua & super_admin (verifikasi & lihat)
|
||||||
|
|
||||||
|
| Use Case | Deskripsi |
|
||||||
|
|---|---|
|
||||||
|
| Input transaksi | Bendahara input pemasukan/pengeluaran |
|
||||||
|
| Verifikasi transaksi | Ketua/super_admin verifikasi, setelah itu data terkunci |
|
||||||
|
| Lihat statistik kas | Widget di halaman transaksi: total saldo, pemasukan/pengeluaran bulan ini, saldo bulan lalu |
|
||||||
|
| Filter transaksi | Filter per kategori, tanggal, status verifikasi |
|
||||||
|
|
||||||
|
**Business rule:**
|
||||||
|
- Setelah `verified_at` terisi, data tidak bisa diubah/dihapus
|
||||||
|
- Threshold keuangan:
|
||||||
|
- < Rp 500.000 → bendahara saja
|
||||||
|
- Rp 500.000 – Rp 2.000.000 → butuh persetujuan ketua
|
||||||
|
- > Rp 2.000.000 → multi-approval atau voting
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 5. Voting
|
||||||
|
|
||||||
|
**Aktor:** semua role (sesuai permission)
|
||||||
|
|
||||||
|
| Use Case | Deskripsi |
|
||||||
|
|---|---|
|
||||||
|
| Buat voting | Tipe: `activity`, `finance`, `general` |
|
||||||
|
| Beri suara | Pilihan: `approve`, `reject`, `abstain` |
|
||||||
|
| Tutup voting | Status: `open` → `closed` |
|
||||||
|
| Lihat hasil | Mayoritas >50% untuk lolos |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 6. Multi-Approval
|
||||||
|
|
||||||
|
**Aktor:** role yang ditentukan per approval
|
||||||
|
|
||||||
|
| Use Case | Deskripsi |
|
||||||
|
|---|---|
|
||||||
|
| Buat approval | Tentukan jumlah approval yang dibutuhkan |
|
||||||
|
| Beri keputusan | Tiap approver bisa approve/reject + catatan |
|
||||||
|
| Cek status | Lolos jika jumlah approve terpenuhi |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 7. Audit Internal
|
||||||
|
|
||||||
|
**Aktor:** auditor (buat temuan), pengurus/ketua (respons)
|
||||||
|
|
||||||
|
| Use Case | Deskripsi |
|
||||||
|
|---|---|
|
||||||
|
| Buat temuan | Tipe: `warning` / `critical`, bisa terkait model apapun |
|
||||||
|
| Respons temuan | Pihak terkait bisa balas temuan audit |
|
||||||
|
| Pantau status | Auditor bisa track status penyelesaian |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 8. Konten Publik
|
||||||
|
|
||||||
|
**Aktor:** pengurus, ketua, super_admin
|
||||||
|
|
||||||
|
| Use Case | Deskripsi |
|
||||||
|
|---|---|
|
||||||
|
| Buat artikel/berita | Post dengan status `draft` / `published` |
|
||||||
|
| Website publik | Halaman publik berbasis Blade untuk masyarakat |
|
||||||
|
| Pesan kontak | Masyarakat bisa kirim pesan, admin bisa lihat & balas |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 9. Dashboard
|
||||||
|
|
||||||
|
Widget yang tampil di halaman utama admin:
|
||||||
|
|
||||||
|
| Widget | Isi |
|
||||||
|
|---|---|
|
||||||
|
| StatsOverview | Anggota aktif, total kas, kegiatan berjalan, kegiatan pending |
|
||||||
|
| ActivityLogWidget | Log aktivitas terbaru di sistem |
|
||||||
|
|
||||||
|
Widget khusus halaman transaksi:
|
||||||
|
|
||||||
|
| Widget | Isi |
|
||||||
|
|---|---|
|
||||||
|
| CashStatsWidget | Total saldo, pemasukan bulan ini, pengeluaran bulan ini, saldo bulan lalu |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Log & Audit Trail
|
||||||
|
|
||||||
|
Semua aksi penting otomatis dicatat via Observer:
|
||||||
|
|
||||||
|
| Event | Dicatat di |
|
||||||
|
|---|---|
|
||||||
|
| Perubahan status anggota | `member_status_logs` |
|
||||||
|
| Transaksi kas baru | `activity_logs` |
|
||||||
|
| Verifikasi kas | `activity_logs` |
|
||||||
|
| Approval kegiatan | `activity_logs` |
|
||||||
|
| Eksekusi kegiatan | `activity_logs` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Keamanan
|
||||||
|
|
||||||
|
- Setiap resource dilindungi Laravel Policy
|
||||||
|
- Permission granular dikelola via Filament Shield
|
||||||
|
- Fitur impersonate: super_admin bisa login sebagai user lain (untuk debugging/support)
|
||||||
|
- Input divalidasi di semua form
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Instalasi
|
||||||
|
|
||||||
|
```bash
|
||||||
|
composer install
|
||||||
|
cp .env.example .env
|
||||||
|
php artisan key:generate
|
||||||
|
php artisan migrate --seed
|
||||||
|
php artisan shield:generate --all
|
||||||
|
php artisan shield:super-admin --user=1
|
||||||
|
```
|
||||||
|
|
||||||
|
Akses admin panel: `/dashboard`
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
namespace App\Filament\Resources\CashRecords\Pages;
|
namespace App\Filament\Resources\CashRecords\Pages;
|
||||||
|
|
||||||
use App\Filament\Resources\CashRecords\CashRecordResource;
|
use App\Filament\Resources\CashRecords\CashRecordResource;
|
||||||
|
use App\Filament\Widgets\CashStatsWidget;
|
||||||
use Filament\Actions\CreateAction;
|
use Filament\Actions\CreateAction;
|
||||||
use Filament\Resources\Pages\ListRecords;
|
use Filament\Resources\Pages\ListRecords;
|
||||||
|
|
||||||
@@ -12,8 +13,11 @@ class ListCashRecords extends ListRecords
|
|||||||
|
|
||||||
protected function getHeaderActions(): array
|
protected function getHeaderActions(): array
|
||||||
{
|
{
|
||||||
return [
|
return [CreateAction::make()];
|
||||||
CreateAction::make(),
|
}
|
||||||
];
|
|
||||||
|
protected function getHeaderWidgets(): array
|
||||||
|
{
|
||||||
|
return [CashStatsWidget::class];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -92,7 +92,8 @@ class CashRecordsTable
|
|||||||
'success',
|
'success',
|
||||||
route('filament.admin.resources.cash-records.index')
|
route('filament.admin.resources.cash-records.index')
|
||||||
);
|
);
|
||||||
}),
|
})
|
||||||
|
->after(fn ($livewire) => redirect(request()->header('Referer'))),
|
||||||
|
|
||||||
// Ketua: tolak transaksi 500rb–2jt
|
// Ketua: tolak transaksi 500rb–2jt
|
||||||
Action::make('reject_ketua')
|
Action::make('reject_ketua')
|
||||||
@@ -125,7 +126,8 @@ class CashRecordsTable
|
|||||||
'danger',
|
'danger',
|
||||||
route('filament.admin.resources.cash-records.index')
|
route('filament.admin.resources.cash-records.index')
|
||||||
);
|
);
|
||||||
}),
|
})
|
||||||
|
->after(fn ($livewire) => redirect(request()->header('Referer'))),
|
||||||
|
|
||||||
// Bendahara/ketua: verifikasi (hanya jika approval sudah selesai atau tidak diperlukan)
|
// Bendahara/ketua: verifikasi (hanya jika approval sudah selesai atau tidak diperlukan)
|
||||||
Action::make('verify')
|
Action::make('verify')
|
||||||
@@ -155,7 +157,8 @@ class CashRecordsTable
|
|||||||
->action(fn (CashRecord $record) => $record->update([
|
->action(fn (CashRecord $record) => $record->update([
|
||||||
'verified_by' => auth()->id(),
|
'verified_by' => auth()->id(),
|
||||||
'verified_at' => now(),
|
'verified_at' => now(),
|
||||||
])),
|
]))
|
||||||
|
->after(fn ($livewire) => redirect(request()->header('Referer'))),
|
||||||
|
|
||||||
EditAction::make()->hidden(fn ($record) => $record->verified_at !== null),
|
EditAction::make()->hidden(fn ($record) => $record->verified_at !== null),
|
||||||
])
|
])
|
||||||
|
|||||||
@@ -0,0 +1,65 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Widgets;
|
||||||
|
|
||||||
|
use App\Models\CashRecord;
|
||||||
|
use Filament\Widgets\StatsOverviewWidget;
|
||||||
|
use Filament\Widgets\StatsOverviewWidget\Stat;
|
||||||
|
use Illuminate\Support\Carbon;
|
||||||
|
|
||||||
|
class CashStatsWidget extends StatsOverviewWidget
|
||||||
|
{
|
||||||
|
protected int | string | array $columnSpan = 'full';
|
||||||
|
|
||||||
|
public static function canView(): bool
|
||||||
|
{
|
||||||
|
return request()->routeIs('filament.admin.resources.cash-records.index');
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getStats(): array
|
||||||
|
{
|
||||||
|
$saldo = fn ($query) => $query
|
||||||
|
->whereNotNull('verified_at')
|
||||||
|
->join('cash_categories', 'cash_records.category_id', '=', 'cash_categories.id')
|
||||||
|
->selectRaw("SUM(CASE WHEN cash_categories.name = 'pemasukan' THEN amount ELSE -amount END) as saldo")
|
||||||
|
->value('saldo') ?? 0;
|
||||||
|
|
||||||
|
$bulanIni = now()->startOfMonth();
|
||||||
|
$bulanLalu = now()->subMonth()->startOfMonth();
|
||||||
|
|
||||||
|
$totalSaldo = $saldo(CashRecord::query());
|
||||||
|
$pemasukanBulanIni = CashRecord::whereNotNull('verified_at')
|
||||||
|
->join('cash_categories', 'cash_records.category_id', '=', 'cash_categories.id')
|
||||||
|
->where('cash_categories.name', 'pemasukan')
|
||||||
|
->whereMonth('date', $bulanIni->month)->whereYear('date', $bulanIni->year)
|
||||||
|
->sum('amount');
|
||||||
|
$pengeluaranBulanIni = CashRecord::whereNotNull('verified_at')
|
||||||
|
->join('cash_categories', 'cash_records.category_id', '=', 'cash_categories.id')
|
||||||
|
->where('cash_categories.name', 'pengeluaran')
|
||||||
|
->whereMonth('date', $bulanIni->month)->whereYear('date', $bulanIni->year)
|
||||||
|
->sum('amount');
|
||||||
|
$saldoBulanLalu = $saldo(CashRecord::query()->where('date', '<', $bulanIni));
|
||||||
|
|
||||||
|
return [
|
||||||
|
Stat::make('Total Saldo', 'Rp ' . number_format($totalSaldo, 0, ',', '.'))
|
||||||
|
->description('Akumulasi semua transaksi')
|
||||||
|
->color($totalSaldo >= 0 ? 'success' : 'danger')
|
||||||
|
->icon('heroicon-o-banknotes'),
|
||||||
|
|
||||||
|
Stat::make('Pemasukan ' . $bulanIni->translatedFormat('F Y'), 'Rp ' . number_format($pemasukanBulanIni, 0, ',', '.'))
|
||||||
|
->description('Total pemasukan bulan ini')
|
||||||
|
->color('success')
|
||||||
|
->icon('heroicon-o-arrow-trending-up'),
|
||||||
|
|
||||||
|
Stat::make('Pengeluaran ' . $bulanIni->translatedFormat('F Y'), 'Rp ' . number_format($pengeluaranBulanIni, 0, ',', '.'))
|
||||||
|
->description('Total pengeluaran bulan ini')
|
||||||
|
->color('danger')
|
||||||
|
->icon('heroicon-o-arrow-trending-down'),
|
||||||
|
|
||||||
|
Stat::make('Saldo Bulan Lalu', 'Rp ' . number_format($saldoBulanLalu, 0, ',', '.'))
|
||||||
|
->description($bulanLalu->translatedFormat('F Y'))
|
||||||
|
->color($saldoBulanLalu >= 0 ? 'info' : 'warning')
|
||||||
|
->icon('heroicon-o-clock'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,7 +12,8 @@ class StatsOverview extends StatsOverviewWidget
|
|||||||
{
|
{
|
||||||
protected function getStats(): array
|
protected function getStats(): array
|
||||||
{
|
{
|
||||||
$totalKas = CashRecord::join('cash_categories', 'cash_records.category_id', '=', 'cash_categories.id')
|
$totalKas = CashRecord::whereNotNull('verified_at')
|
||||||
|
->join('cash_categories', 'cash_records.category_id', '=', 'cash_categories.id')
|
||||||
->selectRaw("SUM(CASE WHEN cash_categories.name = 'pemasukan' THEN amount ELSE -amount END) as saldo")
|
->selectRaw("SUM(CASE WHEN cash_categories.name = 'pemasukan' THEN amount ELSE -amount END) as saldo")
|
||||||
->value('saldo') ?? 0;
|
->value('saldo') ?? 0;
|
||||||
|
|
||||||
|
|||||||
@@ -61,35 +61,7 @@ class CashRecordObserver
|
|||||||
|
|
||||||
public function updated(CashRecord $record): void
|
public function updated(CashRecord $record): void
|
||||||
{
|
{
|
||||||
if ($record->getOriginal('verified_at') !== null) {
|
|
||||||
throw new \Exception('Transaksi yang sudah diverifikasi tidak dapat diubah.');
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($record->wasChanged('verified_by') && $record->verified_by !== null) {
|
if ($record->wasChanged('verified_by') && $record->verified_by !== null) {
|
||||||
// Pastikan threshold terpenuhi sebelum verifikasi
|
|
||||||
if ($record->amount >= 500_000 && $record->amount <= 2_000_000) {
|
|
||||||
$approval = Approval::where('model_type', CashRecord::class)
|
|
||||||
->where('model_id', $record->id)
|
|
||||||
->first();
|
|
||||||
|
|
||||||
if (! $approval || $approval->status !== 'approved') {
|
|
||||||
throw new \Exception('Transaksi ini memerlukan persetujuan ketua sebelum diverifikasi.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($record->amount > 2_000_000) {
|
|
||||||
$vote = Vote::where('type', 'finance')
|
|
||||||
->where('related_id', $record->id)
|
|
||||||
->first();
|
|
||||||
|
|
||||||
$total = $vote?->items()->count() ?? 0;
|
|
||||||
$approve = $vote?->items()->where('choice', 'approve')->count() ?? 0;
|
|
||||||
|
|
||||||
if (! $vote || $vote->status !== 'closed' || $total === 0 || ($approve / $total) <= 0.5) {
|
|
||||||
throw new \Exception('Transaksi ini memerlukan voting dengan mayoritas setuju sebelum diverifikasi.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ActivityLog::create([
|
ActivityLog::create([
|
||||||
'user_id' => Auth::id(),
|
'user_id' => Auth::id(),
|
||||||
'action' => 'verified',
|
'action' => 'verified',
|
||||||
@@ -103,7 +75,13 @@ class CashRecordObserver
|
|||||||
public function deleting(CashRecord $record): void
|
public function deleting(CashRecord $record): void
|
||||||
{
|
{
|
||||||
if ($record->verified_at !== null) {
|
if ($record->verified_at !== null) {
|
||||||
throw new \Exception('Transaksi yang sudah diverifikasi tidak dapat dihapus.');
|
\Filament\Notifications\Notification::make()
|
||||||
|
->title('Tidak dapat dihapus')
|
||||||
|
->body('Transaksi yang sudah diverifikasi tidak dapat dihapus.')
|
||||||
|
->danger()
|
||||||
|
->send();
|
||||||
|
|
||||||
|
abort(403);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ class NotificationService
|
|||||||
|
|
||||||
if ($url) {
|
if ($url) {
|
||||||
$notification->actions([
|
$notification->actions([
|
||||||
\Filament\Notifications\Actions\Action::make('lihat')
|
\Filament\Actions\Action::make('lihat')
|
||||||
->label('Lihat')
|
->label('Lihat')
|
||||||
->url($url),
|
->url($url),
|
||||||
]);
|
]);
|
||||||
|
|||||||
Reference in New Issue
Block a user