taklimat ulangan+profil
This commit is contained in:
@@ -59,6 +59,23 @@ class CheckinController extends Controller
|
|||||||
])->onlyInput('no_kp');
|
])->onlyInput('no_kp');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Pernah hadir di sesi taklimat LAIN? Taklimat ulangan hanya untuk
|
||||||
|
// petugas yang belum pernah hadir langsung — tak perlu rekod semula.
|
||||||
|
$rekodSesiLain = KehadiranTaklimat::where('permohonan_id', $petugas->id)
|
||||||
|
->where('sesi_taklimat_id', '!=', $sesi->id)
|
||||||
|
->with('sesi.dun')
|
||||||
|
->orderBy('hadir_at')
|
||||||
|
->first();
|
||||||
|
|
||||||
|
if ($rekodSesiLain) {
|
||||||
|
return view('checkin.pernah-hadir', [
|
||||||
|
'sesi' => $sesi,
|
||||||
|
'petugas' => $petugas,
|
||||||
|
'kehadiranSebelumIni' => $rekodSesiLain,
|
||||||
|
'sesiSebelumIni' => $rekodSesiLain->sesi,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
// Jika sudah daftar dalam sesi ini, papar semula slip sedia ada
|
// Jika sudah daftar dalam sesi ini, papar semula slip sedia ada
|
||||||
$kehadiran = KehadiranTaklimat::where('sesi_taklimat_id', $sesi->id)
|
$kehadiran = KehadiranTaklimat::where('sesi_taklimat_id', $sesi->id)
|
||||||
->where('permohonan_id', $petugas->id)
|
->where('permohonan_id', $petugas->id)
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ class KaunterController extends Controller
|
|||||||
{
|
{
|
||||||
$user = $request->user();
|
$user = $request->user();
|
||||||
|
|
||||||
if ($user->isPengurus() && (int) $sesi->dun_id !== (int) $user->dun_id) {
|
if (! $user->isAdmin() && (int) $sesi->dun_id !== (int) $user->dun_id) {
|
||||||
abort(403, 'Sesi ini bukan untuk DUN anda.');
|
abort(403, 'Sesi ini bukan untuk DUN anda.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
67
app/Http/Controllers/PasswordResetController.php
Normal file
67
app/Http/Controllers/PasswordResetController.php
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Hash;
|
||||||
|
use Illuminate\Support\Facades\Password;
|
||||||
|
|
||||||
|
class PasswordResetController extends Controller
|
||||||
|
{
|
||||||
|
public function showLinkRequestForm()
|
||||||
|
{
|
||||||
|
return view('auth.lupa-katalaluan');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function sendResetLink(Request $request)
|
||||||
|
{
|
||||||
|
$request->validate(['email' => ['required', 'email']]);
|
||||||
|
|
||||||
|
$status = Password::sendResetLink($request->only('email'));
|
||||||
|
|
||||||
|
return $status === Password::RESET_LINK_SENT
|
||||||
|
? back()->with('status', 'Pautan set semula kata laluan telah dihantar ke e-mel anda.')
|
||||||
|
: back()->withErrors(['email' => $this->mesejRalat($status)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function showResetForm(Request $request, string $token)
|
||||||
|
{
|
||||||
|
return view('auth.set-kata-laluan', [
|
||||||
|
'token' => $token,
|
||||||
|
'email' => $request->query('email', ''),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function reset(Request $request)
|
||||||
|
{
|
||||||
|
$data = $request->validate([
|
||||||
|
'token' => ['required', 'string'],
|
||||||
|
'email' => ['required', 'email'],
|
||||||
|
'password' => ['required', 'string', 'min:8', 'confirmed'],
|
||||||
|
], [
|
||||||
|
'password.min' => 'Kata laluan perlu sekurang-kurangnya 8 aksara.',
|
||||||
|
'password.confirmed' => 'Pengesahan kata laluan tidak sepadan.',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$status = Password::reset(
|
||||||
|
$data,
|
||||||
|
function ($user, $password) {
|
||||||
|
$user->forceFill(['password' => Hash::make($password)])->save();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return $status === Password::PASSWORD_RESET
|
||||||
|
? redirect()->route('login')->with('status', 'Kata laluan berjaya ditetapkan semula. Sila log masuk.')
|
||||||
|
: back()->withErrors(['email' => $this->mesejRalat($status)])->onlyInput('email');
|
||||||
|
}
|
||||||
|
|
||||||
|
private function mesejRalat(string $status): string
|
||||||
|
{
|
||||||
|
return match ($status) {
|
||||||
|
Password::INVALID_USER => 'Akaun dengan e-mel ini tidak ditemui.',
|
||||||
|
Password::RESET_THROTTLED => 'Sila tunggu sebentar sebelum cuba lagi.',
|
||||||
|
Password::INVALID_TOKEN => 'Pautan set semula tidak sah atau telah tamat tempoh.',
|
||||||
|
default => 'Gagal memproses permintaan. Sila cuba lagi.',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
94
app/Http/Controllers/PenggunaController.php
Normal file
94
app/Http/Controllers/PenggunaController.php
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Models\Sppm\Dun;
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Hash;
|
||||||
|
use Illuminate\Validation\Rule;
|
||||||
|
|
||||||
|
class PenggunaController extends Controller
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Peranan yang boleh didaftarkan oleh pengurus DUN (untuk DUN sendiri sahaja).
|
||||||
|
*/
|
||||||
|
private const PERANAN_PENGURUS = ['pengurus', 'kaunter'];
|
||||||
|
|
||||||
|
private const PERANAN_ADMIN = ['admin', 'pengurus', 'kaunter'];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Senarai pengguna. Pengurus hanya nampak pengguna DUN sendiri.
|
||||||
|
*/
|
||||||
|
public function index(Request $request)
|
||||||
|
{
|
||||||
|
$user = $request->user();
|
||||||
|
|
||||||
|
$pengguna = User::query()
|
||||||
|
->when(
|
||||||
|
$user->isPengurus(),
|
||||||
|
fn ($q) => $q->where('dun_id', $user->dun_id)->whereIn('role', self::PERANAN_PENGURUS)
|
||||||
|
)
|
||||||
|
->with('dun')
|
||||||
|
->orderBy('role')
|
||||||
|
->orderBy('name')
|
||||||
|
->get();
|
||||||
|
|
||||||
|
return view('pengguna.index', compact('pengguna'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function create(Request $request)
|
||||||
|
{
|
||||||
|
$user = $request->user();
|
||||||
|
|
||||||
|
$senaraiDun = Dun::where('is_active', true)
|
||||||
|
->when($user->isPengurus(), fn ($q) => $q->where('id', $user->dun_id))
|
||||||
|
->orderBy('code')
|
||||||
|
->get();
|
||||||
|
|
||||||
|
$peranan = $user->isAdmin() ? self::PERANAN_ADMIN : self::PERANAN_PENGURUS;
|
||||||
|
|
||||||
|
return view('pengguna.create', compact('senaraiDun', 'peranan'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function store(Request $request)
|
||||||
|
{
|
||||||
|
$user = $request->user();
|
||||||
|
$perananDibenarkan = $user->isAdmin() ? self::PERANAN_ADMIN : self::PERANAN_PENGURUS;
|
||||||
|
|
||||||
|
$data = $request->validate([
|
||||||
|
'name' => ['required', 'string', 'max:255'],
|
||||||
|
'email' => ['required', 'email', 'max:255', 'unique:users,email'],
|
||||||
|
'role' => ['required', 'string', Rule::in($perananDibenarkan)],
|
||||||
|
'dun_id' => ['nullable', 'integer'],
|
||||||
|
'password' => ['required', 'string', 'min:8', 'confirmed'],
|
||||||
|
], [
|
||||||
|
'email.unique' => 'E-mel ini sudah didaftarkan.',
|
||||||
|
'password.min' => 'Kata laluan perlu sekurang-kurangnya 8 aksara.',
|
||||||
|
'password.confirmed' => 'Pengesahan kata laluan tidak sepadan.',
|
||||||
|
]);
|
||||||
|
|
||||||
|
if ($user->isPengurus()) {
|
||||||
|
// Pengurus hanya boleh daftar untuk DUN sendiri, tidak kira input.
|
||||||
|
$data['dun_id'] = $user->dun_id;
|
||||||
|
} elseif ($data['role'] === 'admin') {
|
||||||
|
$data['dun_id'] = null;
|
||||||
|
} elseif (empty($data['dun_id'])) {
|
||||||
|
return back()->withErrors(['dun_id' => 'Sila pilih DUN untuk peranan ini.'])->withInput();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! empty($data['dun_id']) && ! Dun::where('id', $data['dun_id'])->where('is_active', true)->exists()) {
|
||||||
|
return back()->withErrors(['dun_id' => 'DUN tidak sah.'])->withInput();
|
||||||
|
}
|
||||||
|
|
||||||
|
User::create([
|
||||||
|
'name' => $data['name'],
|
||||||
|
'email' => $data['email'],
|
||||||
|
'role' => $data['role'],
|
||||||
|
'dun_id' => $data['dun_id'] ?? null,
|
||||||
|
'password' => Hash::make($data['password']),
|
||||||
|
]);
|
||||||
|
|
||||||
|
return redirect()->route('pengguna.index')->with('status', 'Pengguna baharu berjaya didaftarkan.');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Models\KehadiranTaklimat;
|
||||||
use App\Models\SesiTaklimat;
|
use App\Models\SesiTaklimat;
|
||||||
use App\Models\Sppm\PetugasPermohonan;
|
use App\Models\Sppm\PetugasPermohonan;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
@@ -9,13 +10,15 @@ use Illuminate\Http\Request;
|
|||||||
class PengurusController extends Controller
|
class PengurusController extends Controller
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Senarai petugas hadir dan tidak/belum hadir bagi satu sesi taklimat.
|
* Senarai petugas bagi satu sesi taklimat, dipecahkan kepada 3 kumpulan:
|
||||||
|
* hadir (sesi ini), pernah hadir (sesi taklimat lain), dan
|
||||||
|
* tidak/belum hadir langsung (tidak pernah hadir mana-mana sesi).
|
||||||
*/
|
*/
|
||||||
public function senarai(Request $request, SesiTaklimat $sesi)
|
public function senarai(Request $request, SesiTaklimat $sesi)
|
||||||
{
|
{
|
||||||
$user = $request->user();
|
$user = $request->user();
|
||||||
|
|
||||||
if ($user->isPengurus() && (int) $sesi->dun_id !== (int) $user->dun_id) {
|
if (! $user->isAdmin() && (int) $sesi->dun_id !== (int) $user->dun_id) {
|
||||||
abort(403, 'Sesi ini bukan untuk DUN anda.');
|
abort(403, 'Sesi ini bukan untuk DUN anda.');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29,15 +32,28 @@ class PengurusController extends Controller
|
|||||||
|
|
||||||
$kehadiran = $sesi->kehadiran()->get()->keyBy('permohonan_id');
|
$kehadiran = $sesi->kehadiran()->get()->keyBy('permohonan_id');
|
||||||
|
|
||||||
[$hadir, $tidakHadir] = $semuaPetugas->partition(
|
$belumHadirSesiIni = $semuaPetugas->reject(fn (PetugasPermohonan $p) => $kehadiran->has($p->id));
|
||||||
fn (PetugasPermohonan $p) => $kehadiran->has($p->id)
|
|
||||||
|
// Antara yang belum hadir sesi ini, semak siapa pernah hadir di sesi LAIN.
|
||||||
|
$kehadiranSesiLain = KehadiranTaklimat::whereIn('permohonan_id', $belumHadirSesiIni->pluck('id'))
|
||||||
|
->where('sesi_taklimat_id', '!=', $sesi->id)
|
||||||
|
->with('sesi.dun')
|
||||||
|
->orderBy('hadir_at')
|
||||||
|
->get()
|
||||||
|
->unique('permohonan_id')
|
||||||
|
->keyBy('permohonan_id');
|
||||||
|
|
||||||
|
[$hadirSesiLain, $tidakHadir] = $belumHadirSesiIni->partition(
|
||||||
|
fn (PetugasPermohonan $p) => $kehadiranSesiLain->has($p->id)
|
||||||
);
|
);
|
||||||
|
|
||||||
return view('pengurus.senarai', [
|
return view('pengurus.senarai', [
|
||||||
'sesi' => $sesi,
|
'sesi' => $sesi,
|
||||||
'hadir' => $hadir->values(),
|
'hadir' => $semuaPetugas->filter(fn (PetugasPermohonan $p) => $kehadiran->has($p->id))->values(),
|
||||||
|
'hadirSesiLain' => $hadirSesiLain->values(),
|
||||||
'tidakHadir' => $tidakHadir->values(),
|
'tidakHadir' => $tidakHadir->values(),
|
||||||
'kehadiran' => $kehadiran,
|
'kehadiran' => $kehadiran,
|
||||||
|
'kehadiranSesiLain' => $kehadiranSesiLain,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
37
app/Http/Controllers/ProfilController.php
Normal file
37
app/Http/Controllers/ProfilController.php
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Hash;
|
||||||
|
|
||||||
|
class ProfilController extends Controller
|
||||||
|
{
|
||||||
|
public function edit(Request $request)
|
||||||
|
{
|
||||||
|
return view('profil.edit', ['pengguna' => $request->user()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function updatePassword(Request $request)
|
||||||
|
{
|
||||||
|
$data = $request->validate([
|
||||||
|
'kata_laluan_semasa' => ['required', 'string'],
|
||||||
|
'kata_laluan_baharu' => ['required', 'string', 'min:8', 'confirmed'],
|
||||||
|
], [
|
||||||
|
'kata_laluan_semasa.required' => 'Sila masukkan kata laluan semasa.',
|
||||||
|
'kata_laluan_baharu.required' => 'Sila masukkan kata laluan baharu.',
|
||||||
|
'kata_laluan_baharu.min' => 'Kata laluan baharu perlu sekurang-kurangnya 8 aksara.',
|
||||||
|
'kata_laluan_baharu.confirmed' => 'Pengesahan kata laluan baharu tidak sepadan.',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$user = $request->user();
|
||||||
|
|
||||||
|
if (! Hash::check($data['kata_laluan_semasa'], $user->password)) {
|
||||||
|
return back()->withErrors(['kata_laluan_semasa' => 'Kata laluan semasa tidak tepat.']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$user->update(['password' => Hash::make($data['kata_laluan_baharu'])]);
|
||||||
|
|
||||||
|
return back()->with('status', 'Kata laluan berjaya dikemaskini.');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,14 +9,14 @@ use Illuminate\Http\Request;
|
|||||||
class SesiTaklimatController extends Controller
|
class SesiTaklimatController extends Controller
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Senarai sesi taklimat. Pengurus hanya nampak DUN sendiri.
|
* Senarai sesi taklimat. Pengurus & kaunter hanya nampak DUN sendiri.
|
||||||
*/
|
*/
|
||||||
public function index(Request $request)
|
public function index(Request $request)
|
||||||
{
|
{
|
||||||
$user = $request->user();
|
$user = $request->user();
|
||||||
|
|
||||||
$sesi = SesiTaklimat::withCount('kehadiran')
|
$sesi = SesiTaklimat::withCount('kehadiran')
|
||||||
->when($user->isPengurus(), fn ($q) => $q->where('dun_id', $user->dun_id))
|
->when(! $user->isAdmin(), fn ($q) => $q->where('dun_id', $user->dun_id))
|
||||||
->orderByDesc('tarikh')
|
->orderByDesc('tarikh')
|
||||||
->get();
|
->get();
|
||||||
|
|
||||||
@@ -106,7 +106,7 @@ class SesiTaklimatController extends Controller
|
|||||||
{
|
{
|
||||||
$user = $request->user();
|
$user = $request->user();
|
||||||
|
|
||||||
if ($user->isPengurus() && (int) $sesi->dun_id !== (int) $user->dun_id) {
|
if (! $user->isAdmin() && (int) $sesi->dun_id !== (int) $user->dun_id) {
|
||||||
abort(403, 'Sesi ini bukan untuk DUN anda.');
|
abort(403, 'Sesi ini bukan untuk DUN anda.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,10 +3,13 @@
|
|||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
// use Illuminate\Contracts\Auth\MustVerifyEmail;
|
// use Illuminate\Contracts\Auth\MustVerifyEmail;
|
||||||
|
use App\Models\Sppm\Dun;
|
||||||
|
use App\Notifications\ResetKataLaluanNotification;
|
||||||
use Database\Factories\UserFactory;
|
use Database\Factories\UserFactory;
|
||||||
use Illuminate\Database\Eloquent\Attributes\Fillable;
|
use Illuminate\Database\Eloquent\Attributes\Fillable;
|
||||||
use Illuminate\Database\Eloquent\Attributes\Hidden;
|
use Illuminate\Database\Eloquent\Attributes\Hidden;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||||
use Illuminate\Notifications\Notifiable;
|
use Illuminate\Notifications\Notifiable;
|
||||||
|
|
||||||
@@ -27,6 +30,24 @@ class User extends Authenticatable
|
|||||||
return $this->role === 'pengurus';
|
return $this->role === 'pengurus';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function isKaunter(): bool
|
||||||
|
{
|
||||||
|
return $this->role === 'kaunter';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function dun(): BelongsTo
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Dun::class, 'dun_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Guna e-mel dalam Bahasa Melayu untuk pautan set semula kata laluan.
|
||||||
|
*/
|
||||||
|
public function sendPasswordResetNotification(#[\SensitiveParameter] $token): void
|
||||||
|
{
|
||||||
|
$this->notify(new ResetKataLaluanNotification($token));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the attributes that should be cast.
|
* Get the attributes that should be cast.
|
||||||
*
|
*
|
||||||
|
|||||||
38
app/Notifications/ResetKataLaluanNotification.php
Normal file
38
app/Notifications/ResetKataLaluanNotification.php
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Notifications;
|
||||||
|
|
||||||
|
use Illuminate\Notifications\Messages\MailMessage;
|
||||||
|
use Illuminate\Notifications\Notification;
|
||||||
|
|
||||||
|
class ResetKataLaluanNotification extends Notification
|
||||||
|
{
|
||||||
|
public function __construct(private readonly string $token)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<int,string>
|
||||||
|
*/
|
||||||
|
public function via(mixed $notifiable): array
|
||||||
|
{
|
||||||
|
return ['mail'];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toMail(mixed $notifiable): MailMessage
|
||||||
|
{
|
||||||
|
$url = url(route('password.reset', [
|
||||||
|
'token' => $this->token,
|
||||||
|
'email' => $notifiable->getEmailForPasswordReset(),
|
||||||
|
], false));
|
||||||
|
|
||||||
|
$minit = config('auth.passwords.users.expire');
|
||||||
|
|
||||||
|
return (new MailMessage)
|
||||||
|
->subject('Set Semula Kata Laluan - Kehadiran Taklimat SPR')
|
||||||
|
->line('Kami menerima permintaan untuk set semula kata laluan akaun anda.')
|
||||||
|
->action('Set Semula Kata Laluan', $url)
|
||||||
|
->line("Pautan ini akan tamat tempoh dalam {$minit} minit.")
|
||||||
|
->line('Jika anda tidak membuat permintaan ini, sila abaikan e-mel ini.');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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::table('users', function (Blueprint $table) {
|
||||||
|
$table->enum('role', ['admin', 'pengurus', 'kaunter'])->default('admin')->change();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('users', function (Blueprint $table) {
|
||||||
|
$table->enum('role', ['admin', 'pengurus'])->default('admin')->change();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
1656
package-lock.json
generated
Normal file
1656
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -46,6 +46,11 @@ a { color: var(--biru); }
|
|||||||
font-size: 0.9rem;
|
font-size: 0.9rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.topbar .pengguna a {
|
||||||
|
color: #fff;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
.container {
|
.container {
|
||||||
max-width: 960px;
|
max-width: 960px;
|
||||||
margin: 1.25rem auto;
|
margin: 1.25rem auto;
|
||||||
@@ -199,6 +204,7 @@ table.jadual tbody tr:nth-child(even) { background: #f8f9fb; }
|
|||||||
.lencana-merah { background: #fdecea; color: var(--merah); }
|
.lencana-merah { background: #fdecea; color: var(--merah); }
|
||||||
.lencana-kelabu { background: #ececec; color: #555; }
|
.lencana-kelabu { background: #ececec; color: #555; }
|
||||||
.lencana-biru { background: #e8f0fe; color: var(--biru); }
|
.lencana-biru { background: #e8f0fe; color: var(--biru); }
|
||||||
|
.lencana-kuning { background: #fbf0d9; color: var(--kuning); }
|
||||||
|
|
||||||
.baris-tindakan {
|
.baris-tindakan {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -287,6 +293,7 @@ table.jadual tbody tr:nth-child(even) { background: #f8f9fb; }
|
|||||||
}
|
}
|
||||||
.tajuk-jadual.hijau { border-color: var(--hijau); color: var(--hijau); }
|
.tajuk-jadual.hijau { border-color: var(--hijau); color: var(--hijau); }
|
||||||
.tajuk-jadual.merah { border-color: var(--merah); color: var(--merah); }
|
.tajuk-jadual.merah { border-color: var(--merah); color: var(--merah); }
|
||||||
|
.tajuk-jadual.kuning { border-color: var(--kuning); color: var(--kuning); }
|
||||||
|
|
||||||
.jadual-kepala .cari-input {
|
.jadual-kepala .cari-input {
|
||||||
width: auto;
|
width: auto;
|
||||||
|
|||||||
@@ -22,5 +22,7 @@
|
|||||||
|
|
||||||
<button type="submit" class="btn btn-penuh">Log Masuk</button>
|
<button type="submit" class="btn btn-penuh">Log Masuk</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
<p class="teks-kecil mt-1"><a href="{{ route('password.request') }}">Lupa kata laluan?</a></p>
|
||||||
</div>
|
</div>
|
||||||
@endsection
|
@endsection
|
||||||
|
|||||||
25
resources/views/auth/lupa-katalaluan.blade.php
Normal file
25
resources/views/auth/lupa-katalaluan.blade.php
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
@extends('layouts.app')
|
||||||
|
|
||||||
|
@section('tajuk', 'Lupa Kata Laluan')
|
||||||
|
@section('saiz-container', 'container-kecil')
|
||||||
|
|
||||||
|
@section('kandungan')
|
||||||
|
<div class="kad">
|
||||||
|
<h1>Lupa Kata Laluan</h1>
|
||||||
|
<p class="teks-kecil">Masukkan e-mel akaun anda. Pautan set semula kata laluan akan dihantar ke e-mel tersebut.</p>
|
||||||
|
|
||||||
|
@if ($errors->any())
|
||||||
|
<div class="makluman makluman-ralat">{{ $errors->first() }}</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
<form method="POST" action="{{ route('password.email') }}">
|
||||||
|
@csrf
|
||||||
|
<label for="email">E-mel</label>
|
||||||
|
<input type="email" id="email" name="email" value="{{ old('email') }}" required autofocus>
|
||||||
|
|
||||||
|
<button type="submit" class="btn btn-penuh">Hantar Pautan Set Semula</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<p class="teks-kecil mt-1"><a href="{{ route('login') }}">Kembali ke Log Masuk</a></p>
|
||||||
|
</div>
|
||||||
|
@endsection
|
||||||
30
resources/views/auth/set-kata-laluan.blade.php
Normal file
30
resources/views/auth/set-kata-laluan.blade.php
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
@extends('layouts.app')
|
||||||
|
|
||||||
|
@section('tajuk', 'Set Semula Kata Laluan')
|
||||||
|
@section('saiz-container', 'container-kecil')
|
||||||
|
|
||||||
|
@section('kandungan')
|
||||||
|
<div class="kad">
|
||||||
|
<h1>Set Semula Kata Laluan</h1>
|
||||||
|
|
||||||
|
@if ($errors->any())
|
||||||
|
<div class="makluman makluman-ralat">{{ $errors->first() }}</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
<form method="POST" action="{{ route('password.update') }}">
|
||||||
|
@csrf
|
||||||
|
<input type="hidden" name="token" value="{{ $token }}">
|
||||||
|
|
||||||
|
<label for="email">E-mel</label>
|
||||||
|
<input type="email" id="email" name="email" value="{{ old('email', $email) }}" required autofocus>
|
||||||
|
|
||||||
|
<label for="password">Kata Laluan Baharu</label>
|
||||||
|
<input type="password" id="password" name="password" required minlength="8">
|
||||||
|
|
||||||
|
<label for="password_confirmation">Sahkan Kata Laluan Baharu</label>
|
||||||
|
<input type="password" id="password_confirmation" name="password_confirmation" required minlength="8">
|
||||||
|
|
||||||
|
<button type="submit" class="btn btn-penuh">Set Semula Kata Laluan</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
@endsection
|
||||||
30
resources/views/checkin/pernah-hadir.blade.php
Normal file
30
resources/views/checkin/pernah-hadir.blade.php
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
@extends('layouts.app')
|
||||||
|
|
||||||
|
@section('tajuk', 'Pernah Hadir')
|
||||||
|
@section('saiz-container', 'container-kecil')
|
||||||
|
|
||||||
|
@section('kandungan')
|
||||||
|
<div class="kad teks-tengah">
|
||||||
|
<div class="makluman makluman-info">
|
||||||
|
Anda telah pun hadir taklimat sebelum ini. Tiada keperluan untuk daftar semula.
|
||||||
|
</div>
|
||||||
|
<p class="teks-kecil mb-0">Status</p>
|
||||||
|
<div class="slip-nombor">Pernah Hadir</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="kad">
|
||||||
|
<table class="slip-butiran">
|
||||||
|
<tr><th>Nama</th><td><strong>{{ $petugas->nama_ic }}</strong></td></tr>
|
||||||
|
<tr><th>No. KP</th><td>{{ $petugas->no_kp }}</td></tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<h2 class="tajuk-jadual" style="margin-top:1.5rem;">Sesi Taklimat Sebelum Ini</h2>
|
||||||
|
<table class="slip-butiran">
|
||||||
|
<tr><th>DUN</th><td>{{ $sesiSebelumIni->dun->code ?? '' }} — {{ $sesiSebelumIni->dun->nama ?? '-' }}</td></tr>
|
||||||
|
<tr><th>Sesi</th><td>{{ $sesiSebelumIni->nama }} ({{ $sesiSebelumIni->tarikh->format('d/m/Y') }})</td></tr>
|
||||||
|
<tr><th>Lokasi</th><td>{{ $sesiSebelumIni->lokasi }}</td></tr>
|
||||||
|
<tr><th>Nombor Siri</th><td>{{ $kehadiranSebelumIni->nombor_siri_papar }}</td></tr>
|
||||||
|
<tr><th>Masa Daftar</th><td>{{ $kehadiranSebelumIni->hadir_at->format('d/m/Y h:i A') }}</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
@endsection
|
||||||
@@ -13,8 +13,20 @@
|
|||||||
Kehadiran Taklimat SPR 2026
|
Kehadiran Taklimat SPR 2026
|
||||||
</a>
|
</a>
|
||||||
@auth
|
@auth
|
||||||
|
@php
|
||||||
|
$labelPeranan = match (auth()->user()->role) {
|
||||||
|
'admin' => 'Admin',
|
||||||
|
'pengurus' => 'Pengurus DUN',
|
||||||
|
'kaunter' => 'Kaunter',
|
||||||
|
default => auth()->user()->role,
|
||||||
|
};
|
||||||
|
@endphp
|
||||||
<div class="pengguna">
|
<div class="pengguna">
|
||||||
<span>{{ auth()->user()->name }} ({{ auth()->user()->role === 'admin' ? 'Admin' : 'Pengurus DUN' }})</span>
|
@if (auth()->user()->isAdmin() || auth()->user()->isPengurus())
|
||||||
|
<a href="{{ route('pengguna.index') }}">Pengguna</a>
|
||||||
|
@endif
|
||||||
|
<a href="{{ route('profil.edit') }}">Profil</a>
|
||||||
|
<span>{{ auth()->user()->name }} ({{ $labelPeranan }})</span>
|
||||||
<form method="POST" action="{{ route('logout') }}" style="margin:0">
|
<form method="POST" action="{{ route('logout') }}" style="margin:0">
|
||||||
@csrf
|
@csrf
|
||||||
<button type="submit" class="btn btn-kecil btn-merah">Log Keluar</button>
|
<button type="submit" class="btn btn-kecil btn-merah">Log Keluar</button>
|
||||||
|
|||||||
82
resources/views/pengguna/create.blade.php
Normal file
82
resources/views/pengguna/create.blade.php
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
@extends('layouts.app')
|
||||||
|
|
||||||
|
@section('tajuk', 'Daftar Pengguna Baharu')
|
||||||
|
@section('saiz-container', 'container-kecil')
|
||||||
|
|
||||||
|
@section('kandungan')
|
||||||
|
<div class="kad">
|
||||||
|
<h1>Daftar Pengguna Baharu</h1>
|
||||||
|
@unless (auth()->user()->isAdmin())
|
||||||
|
<p class="teks-kecil">Pengguna yang didaftarkan akan ditetapkan untuk DUN anda sendiri.</p>
|
||||||
|
@endunless
|
||||||
|
|
||||||
|
@if ($errors->any())
|
||||||
|
<div class="makluman makluman-ralat">{{ $errors->first() }}</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
<form method="POST" action="{{ route('pengguna.store') }}">
|
||||||
|
@csrf
|
||||||
|
<label for="name">Nama</label>
|
||||||
|
<input type="text" id="name" name="name" value="{{ old('name') }}" required autofocus>
|
||||||
|
|
||||||
|
<label for="email">E-mel</label>
|
||||||
|
<input type="email" id="email" name="email" value="{{ old('email') }}" required>
|
||||||
|
|
||||||
|
<label for="role">Peranan</label>
|
||||||
|
<select id="role" name="role" required>
|
||||||
|
<option value="">— Pilih Peranan —</option>
|
||||||
|
@foreach ($peranan as $kod)
|
||||||
|
<option value="{{ $kod }}" @selected(old('role') === $kod)>
|
||||||
|
{{ match ($kod) {
|
||||||
|
'admin' => 'Admin',
|
||||||
|
'pengurus' => 'Pengurus DUN',
|
||||||
|
'kaunter' => 'Kaunter',
|
||||||
|
default => $kod,
|
||||||
|
} }}
|
||||||
|
</option>
|
||||||
|
@endforeach
|
||||||
|
</select>
|
||||||
|
|
||||||
|
@if (auth()->user()->isAdmin())
|
||||||
|
<div id="baris-dun">
|
||||||
|
<label for="dun_id">DUN</label>
|
||||||
|
<select id="dun_id" name="dun_id">
|
||||||
|
<option value="">— Pilih DUN —</option>
|
||||||
|
@foreach ($senaraiDun as $dun)
|
||||||
|
<option value="{{ $dun->id }}" @selected(old('dun_id') == $dun->id)>
|
||||||
|
{{ $dun->code }} — {{ $dun->nama }}
|
||||||
|
</option>
|
||||||
|
@endforeach
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
<label for="password">Kata Laluan</label>
|
||||||
|
<input type="password" id="password" name="password" required minlength="8">
|
||||||
|
|
||||||
|
<label for="password_confirmation">Sahkan Kata Laluan</label>
|
||||||
|
<input type="password" id="password_confirmation" name="password_confirmation" required minlength="8">
|
||||||
|
|
||||||
|
<button type="submit" class="btn btn-penuh">Daftar Pengguna</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@if (auth()->user()->isAdmin())
|
||||||
|
<script>
|
||||||
|
(function () {
|
||||||
|
var role = document.getElementById('role');
|
||||||
|
var barisDun = document.getElementById('baris-dun');
|
||||||
|
var dunSelect = document.getElementById('dun_id');
|
||||||
|
|
||||||
|
function kemaskini() {
|
||||||
|
var perluDun = role.value !== '' && role.value !== 'admin';
|
||||||
|
barisDun.style.display = perluDun ? '' : 'none';
|
||||||
|
dunSelect.required = perluDun;
|
||||||
|
}
|
||||||
|
|
||||||
|
role.addEventListener('change', kemaskini);
|
||||||
|
kemaskini();
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
@endif
|
||||||
|
@endsection
|
||||||
48
resources/views/pengguna/index.blade.php
Normal file
48
resources/views/pengguna/index.blade.php
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
@extends('layouts.app')
|
||||||
|
|
||||||
|
@section('tajuk', 'Senarai Pengguna')
|
||||||
|
|
||||||
|
@section('kandungan')
|
||||||
|
<div class="kad">
|
||||||
|
<div style="display:flex; justify-content:space-between; align-items:center; flex-wrap:wrap; gap:0.5rem;">
|
||||||
|
<h1 class="mb-0">Pengguna</h1>
|
||||||
|
<a href="{{ route('pengguna.create') }}" class="btn">+ Daftar Pengguna</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@if ($pengguna->isEmpty())
|
||||||
|
<div class="kad teks-tengah">
|
||||||
|
<p>Tiada pengguna lagi.</p>
|
||||||
|
</div>
|
||||||
|
@else
|
||||||
|
<div class="kad" style="overflow-x:auto; padding:0;">
|
||||||
|
<table class="jadual">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Nama</th>
|
||||||
|
<th>E-mel</th>
|
||||||
|
<th>Peranan</th>
|
||||||
|
<th>DUN</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
@foreach ($pengguna as $p)
|
||||||
|
<tr>
|
||||||
|
<td>{{ $p->name }}</td>
|
||||||
|
<td>{{ $p->email }}</td>
|
||||||
|
<td>
|
||||||
|
{{ match ($p->role) {
|
||||||
|
'admin' => 'Admin',
|
||||||
|
'pengurus' => 'Pengurus DUN',
|
||||||
|
'kaunter' => 'Kaunter',
|
||||||
|
default => $p->role,
|
||||||
|
} }}
|
||||||
|
</td>
|
||||||
|
<td>{{ $p->dun ? $p->dun->code . ' — ' . $p->dun->nama : '-' }}</td>
|
||||||
|
</tr>
|
||||||
|
@endforeach
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
@endsection
|
||||||
@@ -13,13 +13,17 @@
|
|||||||
|
|
||||||
<div class="stat-grid">
|
<div class="stat-grid">
|
||||||
<div class="stat">
|
<div class="stat">
|
||||||
<div class="nilai">{{ $hadir->count() + $tidakHadir->count() }}</div>
|
<div class="nilai">{{ $hadir->count() + $hadirSesiLain->count() + $tidakHadir->count() }}</div>
|
||||||
<div class="label">Jumlah Petugas</div>
|
<div class="label">Jumlah Petugas</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="stat">
|
<div class="stat">
|
||||||
<div class="nilai" style="color:var(--hijau)">{{ $hadir->count() }}</div>
|
<div class="nilai" style="color:var(--hijau)">{{ $hadir->count() }}</div>
|
||||||
<div class="label">Hadir</div>
|
<div class="label">Hadir</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="stat">
|
||||||
|
<div class="nilai" style="color:var(--kuning)">{{ $hadirSesiLain->count() }}</div>
|
||||||
|
<div class="label">Pernah Hadir (Sesi Lain)</div>
|
||||||
|
</div>
|
||||||
<div class="stat">
|
<div class="stat">
|
||||||
<div class="nilai" style="color:var(--merah)">{{ $tidakHadir->count() }}</div>
|
<div class="nilai" style="color:var(--merah)">{{ $tidakHadir->count() }}</div>
|
||||||
<div class="label">Tidak / Belum Hadir</div>
|
<div class="label">Tidak / Belum Hadir</div>
|
||||||
@@ -81,6 +85,55 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{{-- ============= PERNAH HADIR (SESI LAIN) ============= --}}
|
||||||
|
<div class="kad jadual-kad" data-alat data-per-page="10" style="padding:0;">
|
||||||
|
<div class="jadual-kepala">
|
||||||
|
<h2 class="tajuk-jadual kuning">PERNAH HADIR - SESI LAIN ({{ $hadirSesiLain->count() }})</h2>
|
||||||
|
<input type="text" class="cari-input tiada-cetak" data-cari
|
||||||
|
placeholder="Cari nama, telefon, pusat mengundi…" aria-label="Cari senarai pernah hadir sesi lain">
|
||||||
|
</div>
|
||||||
|
<div class="jadual-bekas">
|
||||||
|
<table class="jadual jadual-responsif">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Nama</th>
|
||||||
|
<th>No. Telefon</th>
|
||||||
|
<th>Emel</th>
|
||||||
|
<th>Pusat Mengundi</th>
|
||||||
|
<th>Saluran</th>
|
||||||
|
<th>Jawatan</th>
|
||||||
|
<th>Sesi Dihadiri</th>
|
||||||
|
<th>Tarikh Hadir</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
@forelse ($hadirSesiLain as $p)
|
||||||
|
@php
|
||||||
|
$penempatan = $p->butiranPenempatan();
|
||||||
|
$rekod = $kehadiranSesiLain->get($p->id);
|
||||||
|
@endphp
|
||||||
|
<tr class="baris-data">
|
||||||
|
<td data-label="Nama">{{ $p->nama_ic }}</td>
|
||||||
|
<td data-label="No. Telefon">{{ $p->no_telefon ?? '-' }}</td>
|
||||||
|
<td data-label="Emel">{{ $p->emel ?? '-' }}</td>
|
||||||
|
<td data-label="Pusat Mengundi">{{ $penempatan['pusat_mengundi'] }}</td>
|
||||||
|
<td data-label="Saluran">{{ $penempatan['saluran'] }}</td>
|
||||||
|
<td data-label="Jawatan">{{ $penempatan['jawatan'] }}</td>
|
||||||
|
<td data-label="Sesi Dihadiri">{{ $rekod?->sesi?->nama }}</td>
|
||||||
|
<td data-label="Tarikh Hadir">{{ $rekod?->hadir_at?->format('d/m/Y') }}</td>
|
||||||
|
</tr>
|
||||||
|
@empty
|
||||||
|
<tr class="baris-tiada"><td colspan="8" class="teks-tengah">Tiada petugas dalam kumpulan ini.</td></tr>
|
||||||
|
@endforelse
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div class="jadual-kaki tiada-cetak">
|
||||||
|
<span class="teks-kecil" data-info></span>
|
||||||
|
<div class="paging" data-pager></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{{-- ================ TIDAK / BELUM HADIR ================ --}}
|
{{-- ================ TIDAK / BELUM HADIR ================ --}}
|
||||||
<div class="kad jadual-kad" data-alat data-per-page="10" style="padding:0;">
|
<div class="kad jadual-kad" data-alat data-per-page="10" style="padding:0;">
|
||||||
<div class="jadual-kepala">
|
<div class="jadual-kepala">
|
||||||
|
|||||||
52
resources/views/profil/edit.blade.php
Normal file
52
resources/views/profil/edit.blade.php
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
@extends('layouts.app')
|
||||||
|
|
||||||
|
@section('tajuk', 'Profil Saya')
|
||||||
|
@section('saiz-container', 'container-kecil')
|
||||||
|
|
||||||
|
@section('kandungan')
|
||||||
|
<div class="kad">
|
||||||
|
<h1>Profil Saya</h1>
|
||||||
|
<table class="slip-butiran">
|
||||||
|
<tr><th>Nama</th><td>{{ $pengguna->name }}</td></tr>
|
||||||
|
<tr><th>E-mel</th><td>{{ $pengguna->email }}</td></tr>
|
||||||
|
<tr>
|
||||||
|
<th>Peranan</th>
|
||||||
|
<td>
|
||||||
|
{{ match ($pengguna->role) {
|
||||||
|
'admin' => 'Admin',
|
||||||
|
'pengurus' => 'Pengurus DUN',
|
||||||
|
'kaunter' => 'Kaunter',
|
||||||
|
default => $pengguna->role,
|
||||||
|
} }}
|
||||||
|
@if ($pengguna->dun)
|
||||||
|
— {{ $pengguna->dun->code }} {{ $pengguna->dun->nama }}
|
||||||
|
@endif
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="kad">
|
||||||
|
<h2 class="mb-0">Tukar Kata Laluan</h2>
|
||||||
|
|
||||||
|
@if ($errors->any())
|
||||||
|
<div class="makluman makluman-ralat">{{ $errors->first() }}</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
<form method="POST" action="{{ route('profil.kata-laluan') }}">
|
||||||
|
@csrf
|
||||||
|
@method('PUT')
|
||||||
|
|
||||||
|
<label for="kata_laluan_semasa">Kata Laluan Semasa</label>
|
||||||
|
<input type="password" id="kata_laluan_semasa" name="kata_laluan_semasa" required>
|
||||||
|
|
||||||
|
<label for="kata_laluan_baharu">Kata Laluan Baharu</label>
|
||||||
|
<input type="password" id="kata_laluan_baharu" name="kata_laluan_baharu" required minlength="8">
|
||||||
|
|
||||||
|
<label for="kata_laluan_baharu_confirmation">Sahkan Kata Laluan Baharu</label>
|
||||||
|
<input type="password" id="kata_laluan_baharu_confirmation" name="kata_laluan_baharu_confirmation" required minlength="8">
|
||||||
|
|
||||||
|
<button type="submit" class="btn btn-penuh">Kemaskini Kata Laluan</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
@endsection
|
||||||
@@ -6,10 +6,12 @@
|
|||||||
<div class="kad">
|
<div class="kad">
|
||||||
<div style="display:flex; justify-content:space-between; align-items:center; flex-wrap:wrap; gap:0.5rem;">
|
<div style="display:flex; justify-content:space-between; align-items:center; flex-wrap:wrap; gap:0.5rem;">
|
||||||
<h1 class="mb-0">Sesi Taklimat</h1>
|
<h1 class="mb-0">Sesi Taklimat</h1>
|
||||||
<div class="baris-tindakan">
|
@unless (auth()->user()->isKaunter())
|
||||||
<a href="{{ route('sesi.eksport') }}" class="btn btn-luar">⬇ Export Kehadiran</a>
|
<div class="baris-tindakan">
|
||||||
<a href="{{ route('sesi.create') }}" class="btn">+ Cipta Sesi</a>
|
<a href="{{ route('sesi.eksport') }}" class="btn btn-luar">⬇ Export Kehadiran</a>
|
||||||
</div>
|
<a href="{{ route('sesi.create') }}" class="btn">+ Cipta Sesi</a>
|
||||||
|
</div>
|
||||||
|
@endunless
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -32,12 +32,14 @@
|
|||||||
<a href="{{ route('sesi.qr', $sesi) }}" class="btn">Papar / Cetak QR Code</a>
|
<a href="{{ route('sesi.qr', $sesi) }}" class="btn">Papar / Cetak QR Code</a>
|
||||||
<a href="{{ route('kaunter.index', $sesi) }}" class="btn btn-hijau">Kaunter Borang</a>
|
<a href="{{ route('kaunter.index', $sesi) }}" class="btn btn-hijau">Kaunter Borang</a>
|
||||||
<a href="{{ route('pengurus.senarai', $sesi) }}" class="btn btn-luar">Senarai Kehadiran</a>
|
<a href="{{ route('pengurus.senarai', $sesi) }}" class="btn btn-luar">Senarai Kehadiran</a>
|
||||||
<form method="POST" action="{{ route('sesi.toggle', $sesi) }}" style="margin:0">
|
@unless (auth()->user()->isKaunter())
|
||||||
@csrf
|
<form method="POST" action="{{ route('sesi.toggle', $sesi) }}" style="margin:0">
|
||||||
<button type="submit" class="btn {{ $sesi->is_active ? 'btn-merah' : 'btn-hijau' }}">
|
@csrf
|
||||||
{{ $sesi->is_active ? 'Tutup Pendaftaran' : 'Buka Pendaftaran' }}
|
<button type="submit" class="btn {{ $sesi->is_active ? 'btn-merah' : 'btn-hijau' }}">
|
||||||
</button>
|
{{ $sesi->is_active ? 'Tutup Pendaftaran' : 'Buka Pendaftaran' }}
|
||||||
</form>
|
</button>
|
||||||
|
</form>
|
||||||
|
@endunless
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,10 @@ use App\Http\Controllers\AuthController;
|
|||||||
use App\Http\Controllers\CheckinController;
|
use App\Http\Controllers\CheckinController;
|
||||||
use App\Http\Controllers\EksportKehadiranController;
|
use App\Http\Controllers\EksportKehadiranController;
|
||||||
use App\Http\Controllers\KaunterController;
|
use App\Http\Controllers\KaunterController;
|
||||||
|
use App\Http\Controllers\PasswordResetController;
|
||||||
use App\Http\Controllers\PengurusController;
|
use App\Http\Controllers\PengurusController;
|
||||||
|
use App\Http\Controllers\PenggunaController;
|
||||||
|
use App\Http\Controllers\ProfilController;
|
||||||
use App\Http\Controllers\SesiTaklimatController;
|
use App\Http\Controllers\SesiTaklimatController;
|
||||||
use Illuminate\Support\Facades\Route;
|
use Illuminate\Support\Facades\Route;
|
||||||
|
|
||||||
@@ -19,20 +22,39 @@ Route::get('/login', [AuthController::class, 'showLogin'])->name('login');
|
|||||||
Route::post('/login', [AuthController::class, 'login'])->name('login.attempt');
|
Route::post('/login', [AuthController::class, 'login'])->name('login.attempt');
|
||||||
Route::post('/logout', [AuthController::class, 'logout'])->name('logout');
|
Route::post('/logout', [AuthController::class, 'logout'])->name('logout');
|
||||||
|
|
||||||
|
// Lupa kata laluan
|
||||||
|
Route::get('/lupa-katalaluan', [PasswordResetController::class, 'showLinkRequestForm'])->name('password.request');
|
||||||
|
Route::post('/lupa-katalaluan', [PasswordResetController::class, 'sendResetLink'])->name('password.email');
|
||||||
|
Route::get('/set-kata-laluan/{token}', [PasswordResetController::class, 'showResetForm'])->name('password.reset');
|
||||||
|
Route::post('/set-kata-laluan', [PasswordResetController::class, 'reset'])->name('password.update');
|
||||||
|
|
||||||
Route::middleware(['auth'])->group(function () {
|
Route::middleware(['auth'])->group(function () {
|
||||||
// Pengurusan sesi taklimat (admin & pengurus dun)
|
// Profil sendiri: tukar kata laluan (semua peranan)
|
||||||
Route::get('/sesi', [SesiTaklimatController::class, 'index'])->name('sesi.index');
|
Route::get('/profil', [ProfilController::class, 'edit'])->name('profil.edit');
|
||||||
Route::get('/sesi/eksport-kehadiran', EksportKehadiranController::class)->name('sesi.eksport');
|
Route::put('/profil/kata-laluan', [ProfilController::class, 'updatePassword'])->name('profil.kata-laluan');
|
||||||
Route::get('/sesi/cipta', [SesiTaklimatController::class, 'create'])->name('sesi.create');
|
|
||||||
Route::post('/sesi', [SesiTaklimatController::class, 'store'])->name('sesi.store');
|
|
||||||
Route::get('/sesi/{sesi}', [SesiTaklimatController::class, 'show'])->name('sesi.show');
|
|
||||||
Route::get('/sesi/{sesi}/qr', [SesiTaklimatController::class, 'qr'])->name('sesi.qr');
|
|
||||||
Route::post('/sesi/{sesi}/toggle', [SesiTaklimatController::class, 'toggle'])->name('sesi.toggle');
|
|
||||||
|
|
||||||
// Kaunter: rekod nombor siri & serah borang
|
Route::middleware(['role:admin,pengurus'])->group(function () {
|
||||||
Route::get('/sesi/{sesi}/kaunter', [KaunterController::class, 'index'])->name('kaunter.index');
|
// Pengurusan sesi taklimat (cipta/tutup/eksport) — bukan kaunter
|
||||||
Route::post('/sesi/{sesi}/kaunter/{kehadiran}/serah', [KaunterController::class, 'serahBorang'])->name('kaunter.serah');
|
Route::get('/sesi/eksport-kehadiran', EksportKehadiranController::class)->name('sesi.eksport');
|
||||||
|
Route::get('/sesi/cipta', [SesiTaklimatController::class, 'create'])->name('sesi.create');
|
||||||
|
Route::post('/sesi', [SesiTaklimatController::class, 'store'])->name('sesi.store');
|
||||||
|
Route::post('/sesi/{sesi}/toggle', [SesiTaklimatController::class, 'toggle'])->name('sesi.toggle');
|
||||||
|
|
||||||
// Pengurus dun: senarai hadir / tidak hadir
|
// Pengurusan pengguna: admin daftar sesiapa, pengurus daftar kaunter/pengurus DUN sendiri
|
||||||
Route::get('/sesi/{sesi}/senarai', [PengurusController::class, 'senarai'])->name('pengurus.senarai');
|
Route::get('/pengguna', [PenggunaController::class, 'index'])->name('pengguna.index');
|
||||||
|
Route::get('/pengguna/cipta', [PenggunaController::class, 'create'])->name('pengguna.create');
|
||||||
|
Route::post('/pengguna', [PenggunaController::class, 'store'])->name('pengguna.store');
|
||||||
|
});
|
||||||
|
|
||||||
|
Route::middleware(['role:admin,pengurus,kaunter'])->group(function () {
|
||||||
|
// Lihat sesi & kehadiran (admin, pengurus dun, kaunter — dibatasi DUN sendiri kecuali admin)
|
||||||
|
Route::get('/sesi', [SesiTaklimatController::class, 'index'])->name('sesi.index');
|
||||||
|
Route::get('/sesi/{sesi}', [SesiTaklimatController::class, 'show'])->name('sesi.show');
|
||||||
|
Route::get('/sesi/{sesi}/qr', [SesiTaklimatController::class, 'qr'])->name('sesi.qr');
|
||||||
|
Route::get('/sesi/{sesi}/senarai', [PengurusController::class, 'senarai'])->name('pengurus.senarai');
|
||||||
|
|
||||||
|
// Kaunter: rekod nombor siri & serah borang
|
||||||
|
Route::get('/sesi/{sesi}/kaunter', [KaunterController::class, 'index'])->name('kaunter.index');
|
||||||
|
Route::post('/sesi/{sesi}/kaunter/{kehadiran}/serah', [KaunterController::class, 'serahBorang'])->name('kaunter.serah');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user