Flag saluran bermasalah + pautan Call/WhatsApp

- Halaman baharu "Saluran Bermasalah": senarai semua saluran dalam DUN
  (dengan KTM & telefonnya), tanda/buang tanda bermasalah berserta
  catatan; admin DUN terhad ke DUN sendiri, superadmin semua DUN
- Dashboard: baris saluran bermasalah diserlahkan kuning berserta
  lencana BERMASALAH dan catatan
- No. telefon KTM kini ada butang "📞 Call" (tel:) dan "WhatsApp"
  (wa.me, dinormalkan ke format 60xxxxxxxxx) di dashboard dan halaman
  saluran bermasalah

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Saufi
2026-07-11 03:55:20 +08:00
parent 57c20353c7
commit 322687c1d1
11 changed files with 400 additions and 3 deletions

View File

@@ -0,0 +1,123 @@
<?php
namespace App\Http\Controllers;
use App\Models\SaluranBermasalah;
use App\Models\Sppm\Dun;
use App\Models\Sppm\PetugasAssignment;
use App\Models\Sppm\PusatMengundi;
use App\Models\Sppm\Saluran;
use Illuminate\Http\Request;
class SaluranBermasalahController extends Controller
{
/**
* Senarai semua saluran dalam DUN berserta status bermasalah.
* Admin boleh tanda / buang tanda saluran bermasalah dari sini.
*/
public function index(Request $request)
{
$user = $request->user();
$dunId = $user->isSuperadmin()
? ($request->filled('dun_id') ? $request->integer('dun_id') : null)
: (int) $user->dun_id;
$senaraiDun = $user->isSuperadmin()
? Dun::where('is_active', true)->orderBy('code')->get()
: collect();
$senaraiSaluran = Saluran::whereHas('pusatMengundi', function ($q) use ($dunId) {
if ($dunId) {
$q->where('dun_id', $dunId);
}
})
->when($request->filled('pusat_id'), fn ($q) => $q->where('pusat_mengundi_id', $request->integer('pusat_id')))
->with('pusatMengundi')
->get()
->sortBy([
fn ($a, $b) => strcmp($a->pusatMengundi?->nama ?? '', $b->pusatMengundi?->nama ?? ''),
fn ($a, $b) => ($a->nombor_saluran ?? 0) <=> ($b->nombor_saluran ?? 0),
])
->values();
// KTM bagi setiap saluran (untuk paparan nama & telefon)
$ktm = PetugasAssignment::ktm()
->whereIn('saluran_id', $senaraiSaluran->pluck('id'))
->with('permohonan')
->get()
->keyBy('saluran_id');
$bermasalah = SaluranBermasalah::whereIn('saluran_id', $senaraiSaluran->pluck('id'))
->with('dilaporOleh')
->get()
->keyBy('saluran_id');
$senaraiPusat = PusatMengundi::when($dunId, fn ($q) => $q->where('dun_id', $dunId))
->orderBy('nama')
->get(['id', 'nama']);
return view('saluran.index', [
'senaraiSaluran' => $senaraiSaluran,
'ktm' => $ktm,
'bermasalah' => $bermasalah,
'senaraiPusat' => $senaraiPusat,
'senaraiDun' => $senaraiDun,
'dunId' => $dunId,
]);
}
/**
* Tanda saluran sebagai bermasalah (dengan catatan pilihan).
*/
public function store(Request $request)
{
$data = $request->validate([
'saluran_id' => ['required', 'integer'],
'catatan' => ['nullable', 'string', 'max:500'],
]);
$saluran = Saluran::with('pusatMengundi')->findOrFail($data['saluran_id']);
$this->pastikanDunDibenarkan($request, $saluran);
SaluranBermasalah::updateOrCreate(
['saluran_id' => $saluran->id],
[
'pusat_mengundi_id' => $saluran->pusat_mengundi_id,
'dun_id' => $saluran->pusatMengundi->dun_id,
'catatan' => $data['catatan'] ?? null,
'dilapor_oleh_user_id' => $request->user()->id,
],
);
return back()->with('status', 'Saluran '.$saluran->nombor_saluran.' ('.$saluran->pusatMengundi->nama.') ditanda BERMASALAH.');
}
/**
* Buang tanda bermasalah pada saluran.
*/
public function destroy(Request $request, int $saluranId)
{
$saluran = Saluran::with('pusatMengundi')->findOrFail($saluranId);
$this->pastikanDunDibenarkan($request, $saluran);
SaluranBermasalah::where('saluran_id', $saluran->id)->delete();
return back()->with('status', 'Tanda bermasalah pada Saluran '.$saluran->nombor_saluran.' ('.$saluran->pusatMengundi->nama.') telah dibuang.');
}
/**
* Admin DUN hanya boleh urus saluran dalam DUN sendiri.
*/
private function pastikanDunDibenarkan(Request $request, Saluran $saluran): void
{
$user = $request->user();
if ($user->isSuperadmin()) {
return;
}
if ((int) $saluran->pusatMengundi?->dun_id !== (int) $user->dun_id) {
abort(403, 'Saluran ini bukan dalam DUN anda.');
}
}
}