This commit is contained in:
Saufi
2026-06-02 17:35:45 +08:00
commit 4ef99b1f81
148 changed files with 21134 additions and 0 deletions

View File

@@ -0,0 +1,150 @@
@extends('layouts.admin')
@section('title', 'Log Audit')
@section('content')
<div class="d-flex justify-content-between align-items-center mb-4">
<h4 class="fw-bold mb-0"><i class="bi bi-journal-text me-2 text-primary"></i>Log Audit</h4>
<a href="{{ route('admin.audit-logs.export', request()->query()) }}"
class="btn btn-outline-success btn-sm">
<i class="bi bi-download me-1"></i>Eksport CSV
</a>
</div>
{{-- Filter --}}
<div class="card mb-3">
<div class="card-body py-2">
<form method="GET" class="row g-2 align-items-center">
<div class="col-md-2">
<select class="form-select form-select-sm" name="action">
<option value="">Semua Tindakan</option>
@foreach($actions as $action)
<option value="{{ $action }}" {{ request('action') === $action ? 'selected' : '' }}>
{{ $action }}
</option>
@endforeach
</select>
</div>
<div class="col-md-2">
<input type="text" class="form-control form-control-sm" name="search"
value="{{ request('search') }}" placeholder="Cari tindakan / justifikasi...">
</div>
<div class="col-md-2">
<input type="date" class="form-control form-control-sm" name="date_from"
value="{{ request('date_from') }}" placeholder="Dari tarikh">
</div>
<div class="col-md-2">
<input type="date" class="form-control form-control-sm" name="date_to"
value="{{ request('date_to') }}" placeholder="Hingga tarikh">
</div>
<div class="col-auto">
<button class="btn btn-sm btn-outline-secondary" type="submit">
<i class="bi bi-search me-1"></i>Cari
</button>
<a href="{{ route('admin.audit-logs.index') }}" class="btn btn-sm btn-outline-secondary ms-1">
<i class="bi bi-x me-1"></i>Padam
</a>
</div>
</form>
</div>
</div>
{{-- Table --}}
<div class="card">
<div class="card-body p-0">
<div class="table-responsive">
<table class="table table-sm table-hover mb-0">
<thead class="table-light">
<tr>
<th>Masa</th>
<th>Oleh</th>
<th>Tindakan</th>
<th>Sasaran</th>
<th>IP</th>
<th>Butiran</th>
</tr>
</thead>
<tbody>
@forelse($logs as $log)
<tr>
<td class="text-nowrap small text-muted">
{{ $log->created_at?->format('d/m/Y H:i:s') }}
</td>
<td>
<span class="fw-semibold small">{{ $log->actor?->name ?? 'Sistem' }}</span>
@if($log->actor_role)
<span class="badge bg-light text-dark ms-1 small">{{ $log->actor_role }}</span>
@endif
</td>
<td>
<code class="small">{{ $log->action }}</code>
</td>
<td class="small">
{{ $log->targetUser?->name ?? ($log->subject_type ? class_basename($log->subject_type).'#'.$log->subject_id : '—') }}
</td>
<td class="small text-muted">{{ $log->ip_address ?? '—' }}</td>
<td>
<button class="btn btn-sm btn-outline-secondary py-0"
data-bs-toggle="modal"
data-bs-target="#logModal{{ $log->id }}">
<i class="bi bi-eye"></i>
</button>
</td>
</tr>
{{-- Detail Modal --}}
<div class="modal fade" id="logModal{{ $log->id }}" tabindex="-1">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h6 class="modal-title"><code>{{ $log->action }}</code></h6>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body small">
<table class="table table-sm">
<tr><td width="30%">Masa</td><td>{{ $log->created_at?->format('d/m/Y H:i:s') }}</td></tr>
<tr><td>Oleh</td><td>{{ $log->actor?->name ?? 'Sistem' }} ({{ $log->actor_role }})</td></tr>
<tr><td>IP</td><td>{{ $log->ip_address ?? '—' }}</td></tr>
<tr>
<td>Justifikasi</td>
<td>{{ $log->justification ?? '—' }}</td>
</tr>
@if($log->old_values)
<tr>
<td>Nilai Lama</td>
<td><pre class="mb-0 small bg-light p-2 rounded">{{ json_encode($log->old_values, JSON_PRETTY_PRINT|JSON_UNESCAPED_UNICODE) }}</pre></td>
</tr>
@endif
@if($log->new_values)
<tr>
<td>Nilai Baru</td>
<td><pre class="mb-0 small bg-light p-2 rounded">{{ json_encode($log->new_values, JSON_PRETTY_PRINT|JSON_UNESCAPED_UNICODE) }}</pre></td>
</tr>
@endif
</table>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary btn-sm" data-bs-dismiss="modal">Tutup</button>
</div>
</div>
</div>
</div>
@empty
<tr>
<td colspan="6" class="text-center text-muted py-4">
<i class="bi bi-inbox fs-4 d-block mb-2"></i>Tiada rekod log.
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
</div>
@if($logs->hasPages())
<div class="card-footer bg-white">
{{ $logs->links() }}
</div>
@endif
</div>
@endsection

View File

@@ -0,0 +1,254 @@
@extends('layouts.admin')
@section('title', 'Dashboard')
@section('content')
<div class="d-flex justify-content-between align-items-center mb-4">
<h4 class="fw-bold mb-0"><i class="bi bi-speedometer2 me-2 text-primary"></i>Dashboard Pentadbir</h4>
<span class="text-muted small">{{ now()->format('d M Y, H:i') }}</span>
</div>
{{-- Baris 1: Pengguna & Projek --}}
<div class="row g-3 mb-3">
<div class="col-md-3">
<div class="card stat-card bg-success text-white h-100">
<div class="card-body">
<div class="d-flex justify-content-between align-items-start">
<div>
<div class="fs-1 fw-bold">{{ $stats['users_active'] }}</div>
<div class="opacity-75 small">Pengguna Aktif</div>
<div class="mt-1 opacity-60" style="font-size:0.78rem">
{{ $stats['users_total'] }} jumlah &bull;
{{ $stats['users_inactive'] }} dinyahaktif
</div>
</div>
<i class="bi bi-people-fill fs-2 opacity-50"></i>
</div>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card stat-card bg-primary text-white h-100">
<div class="card-body">
<div class="d-flex justify-content-between align-items-start">
<div>
<div class="fs-1 fw-bold">{{ $stats['projects_total'] }}</div>
<div class="opacity-75 small">Jumlah Projek</div>
<div class="mt-1 opacity-60" style="font-size:0.78rem">
+{{ $stats['projects_last_7'] }} (7 hari) &bull;
+{{ $stats['projects_last_30'] }} (30 hari)
</div>
</div>
<i class="bi bi-folder-fill fs-2 opacity-50"></i>
</div>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card stat-card bg-info text-white h-100">
<div class="card-body">
<div class="d-flex justify-content-between align-items-start">
<div>
<div class="fs-1 fw-bold">{{ $stats['projects_completed'] }}</div>
<div class="opacity-75 small">Transkripsi Selesai</div>
@php
$pct = $stats['projects_total'] > 0
? round($stats['projects_completed'] / $stats['projects_total'] * 100)
: 0;
@endphp
<div class="mt-1 opacity-60" style="font-size:0.78rem">
{{ $pct }}% kadar kejayaan
</div>
</div>
<i class="bi bi-check-circle-fill fs-2 opacity-50"></i>
</div>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card stat-card bg-warning text-dark h-100">
<div class="card-body">
<div class="d-flex justify-content-between align-items-start">
<div>
@php
$totalBytes = $stats['storage_bytes'];
if ($totalBytes >= 1073741824) {
$storageLabel = number_format($totalBytes / 1073741824, 2) . ' GB';
} elseif ($totalBytes >= 1048576) {
$storageLabel = number_format($totalBytes / 1048576, 1) . ' MB';
} else {
$storageLabel = number_format($totalBytes / 1024, 0) . ' KB';
}
$durationH = intdiv($stats['duration_total_seconds'], 3600);
$durationM = intdiv($stats['duration_total_seconds'] % 3600, 60);
@endphp
<div class="fs-3 fw-bold">{{ $storageLabel }}</div>
<div class="small" style="opacity:0.8">Storan Digunakan</div>
<div class="mt-1" style="font-size:0.78rem; opacity:0.7">
{{ $durationH }}j {{ $durationM }}m jumlah audio
</div>
</div>
<i class="bi bi-hdd-fill fs-2 opacity-50"></i>
</div>
</div>
</div>
</div>
</div>
{{-- Baris 2: Status + Trend --}}
<div class="row g-3 mb-3">
<div class="col-md-4">
<div class="card h-100">
<div class="card-header bg-white fw-semibold">
<i class="bi bi-pie-chart me-2 text-primary"></i>Status Transkripsi
</div>
<div class="card-body pb-2">
<table class="table table-sm mb-0">
<tbody>
@php
$statusRows = [
['bg-secondary', 'Menunggu', $stats['projects_pending']],
['bg-warning text-dark', 'Sedang Diproses', $stats['projects_processing']],
['bg-success', 'Selesai', $stats['projects_completed']],
['bg-danger', 'Gagal', $stats['projects_failed']],
['bg-light text-muted border', 'Dipadam (soft)', $stats['projects_deleted']],
];
@endphp
@foreach($statusRows as [$cls, $label, $count])
<tr>
<td><span class="badge {{ $cls }}">{{ $label }}</span></td>
<td class="fw-bold">{{ $count }}</td>
<td class="text-muted small">
@if($stats['projects_total'] > 0)
{{ round($count / ($stats['projects_total'] ?: 1) * 100) }}%
@endif
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card h-100">
<div class="card-header bg-white fw-semibold">
<i class="bi bi-bar-chart me-2 text-success"></i>Trend 30 Hari Lepas
</div>
<div class="card-body p-0">
@if($trendRaw->isEmpty())
<div class="text-muted small text-center py-4">Tiada data projek dalam 30 hari lepas.</div>
@else
<div class="table-responsive" style="max-height:220px; overflow-y:auto">
<table class="table table-sm table-hover mb-0">
<thead class="table-light sticky-top">
<tr>
<th>Tarikh</th>
<th class="text-center">Baru</th>
<th class="text-center text-success">Selesai</th>
<th class="text-center text-danger">Gagal</th>
</tr>
</thead>
<tbody>
@foreach($trendRaw->sortByDesc('date') as $row)
<tr>
<td class="small">{{ \Carbon\Carbon::parse($row->date)->format('d/m') }}</td>
<td class="text-center">{{ $row->total }}</td>
<td class="text-center text-success">{{ $row->completed }}</td>
<td class="text-center text-danger">{{ $row->failed }}</td>
</tr>
@endforeach
</tbody>
</table>
</div>
@endif
</div>
</div>
</div>
<div class="col-md-4">
<div class="card h-100">
<div class="card-header bg-white fw-semibold">
<i class="bi bi-trophy me-2 text-warning"></i>Pengguna Paling Aktif
</div>
<div class="card-body p-0">
@if($topUsers->isEmpty())
<div class="text-muted small text-center py-4">Tiada data pengguna.</div>
@else
<table class="table table-sm mb-0">
<tbody>
@foreach($topUsers as $i => $u)
<tr>
<td style="width:28px" class="text-center text-muted small">{{ $i + 1 }}</td>
<td>
<div class="small fw-semibold">{{ $u->name }}</div>
<div class="text-muted" style="font-size:0.72rem">{{ $u->email }}</div>
</td>
<td class="text-end fw-bold small">{{ $u->owned_projects_count }}</td>
</tr>
@endforeach
</tbody>
</table>
@endif
</div>
</div>
</div>
</div>
{{-- Baris 3: Jabatan + Tindakan Pantas --}}
<div class="row g-3">
<div class="col-md-6">
<div class="card h-100">
<div class="card-header bg-white fw-semibold">
<i class="bi bi-building me-2 text-secondary"></i>Projek Mengikut Jabatan
</div>
<div class="card-body p-0">
@if($deptStats->isEmpty())
<div class="text-muted small text-center py-4">Tiada data jabatan.</div>
@else
<table class="table table-sm mb-0">
<tbody>
@foreach($deptStats as $dept)
<tr>
<td class="small">{{ $dept->name }}</td>
<td class="text-end">
<span class="badge bg-primary">{{ $dept->project_count }}</span>
</td>
</tr>
@endforeach
</tbody>
</table>
@endif
</div>
</div>
</div>
<div class="col-md-6">
<div class="card h-100">
<div class="card-header bg-white fw-semibold">
<i class="bi bi-lightning me-2 text-warning"></i>Tindakan Pantas
</div>
<div class="card-body d-flex flex-column gap-2">
<a href="{{ route('admin.users.index') }}" class="btn btn-outline-primary">
<i class="bi bi-people me-2"></i>Urus Pengguna
<span class="badge bg-primary ms-1">{{ $stats['users_total'] }}</span>
</a>
<a href="{{ route('admin.users.create') }}" class="btn btn-outline-success">
<i class="bi bi-person-plus me-2"></i>Daftar Pengguna Baru
</a>
<a href="{{ route('admin.projects.index') }}" class="btn btn-outline-secondary">
<i class="bi bi-folder2 me-2"></i>Senarai Projek (Metadata)
<span class="badge bg-secondary ms-1">{{ $stats['projects_total'] }}</span>
</a>
<a href="{{ route('admin.audit-logs.index') }}" class="btn btn-outline-dark">
<i class="bi bi-journal-text me-2"></i>Log Audit
</a>
</div>
</div>
</div>
</div>
@endsection

View File

@@ -0,0 +1,215 @@
@extends('layouts.admin')
@section('title', 'Senarai Projek')
@section('content')
<div class="d-flex justify-content-between align-items-center mb-4">
<div>
<h4 class="fw-bold mb-0"><i class="bi bi-folder2 me-2 text-primary"></i>Senarai Projek</h4>
<p class="text-muted small mb-0 mt-1">Metadata sahaja kandungan audio dan teks tidak dipaparkan.</p>
</div>
<a href="{{ route('admin.dashboard') }}" class="btn btn-outline-secondary">
<i class="bi bi-speedometer2 me-2"></i>Kembali ke Dashboard
</a>
</div>
{{-- Filter --}}
<div class="card mb-3">
<div class="card-body py-2">
<form method="GET" class="row g-2 align-items-center">
<div class="col-md-3">
<input type="text" class="form-control form-control-sm" name="search"
value="{{ request('search') }}" placeholder="Cari tajuk projek...">
</div>
<div class="col-md-2">
<select class="form-select form-select-sm" name="status">
<option value="">Semua Status</option>
<option value="pending" {{ request('status') === 'pending' ? 'selected' : '' }}>Menunggu</option>
<option value="processing" {{ request('status') === 'processing' ? 'selected' : '' }}>Sedang Diproses</option>
<option value="completed" {{ request('status') === 'completed' ? 'selected' : '' }}>Selesai</option>
<option value="failed" {{ request('status') === 'failed' ? 'selected' : '' }}>Gagal</option>
</select>
</div>
<div class="col-md-3">
<select class="form-select form-select-sm" name="owner_id">
<option value="">Semua Pemilik</option>
@foreach($owners as $owner)
<option value="{{ $owner->id }}" {{ request('owner_id') == $owner->id ? 'selected' : '' }}>
{{ $owner->name }}
</option>
@endforeach
</select>
</div>
<div class="col-auto">
<button class="btn btn-sm btn-outline-secondary" type="submit">
<i class="bi bi-search me-1"></i>Cari
</button>
<a href="{{ route('admin.projects.index') }}" class="btn btn-sm btn-outline-secondary ms-1">Padam</a>
</div>
</form>
</div>
</div>
{{-- Table --}}
<div class="card">
<div class="card-body p-0">
<div class="table-responsive">
<table class="table table-hover mb-0">
<thead class="table-light">
<tr>
<th>Tajuk</th>
<th>Pemilik</th>
<th>Status</th>
<th>Saiz Fail</th>
<th>Tempoh</th>
<th>Kolaborator</th>
<th>Tarikh Cipta</th>
<th style="width:80px">Tindakan</th>
</tr>
</thead>
<tbody>
@php
$statusMap = [
'pending' => ['bg-secondary', 'Menunggu'],
'processing' => ['bg-warning text-dark', 'Sedang Diproses'],
'completed' => ['bg-success', 'Selesai'],
'failed' => ['bg-danger', 'Gagal'],
];
@endphp
@forelse($projects as $project)
<tr class="{{ $project->deleted_at ? 'table-secondary text-muted' : '' }}">
<td>
<span class="fw-semibold">{{ $project->title }}</span>
@if($project->deleted_at)
<span class="badge bg-secondary ms-1 small">Dipadam</span>
@endif
</td>
<td>
<span class="small">{{ $project->owner->name ?? '—' }}</span>
<div class="text-muted" style="font-size:0.75rem">{{ $project->owner->email ?? '' }}</div>
</td>
<td>
@php [$cls, $label] = $statusMap[$project->transcription_status] ?? ['bg-secondary', $project->transcription_status]; @endphp
<span class="badge {{ $cls }}">{{ $label }}</span>
</td>
<td class="small">
@php
$bytes = $project->file_size;
echo $bytes >= 1048576
? number_format($bytes/1048576, 1) . ' MB'
: number_format($bytes/1024, 0) . ' KB';
@endphp
</td>
<td class="small">
@if($project->duration_seconds)
{{ intdiv($project->duration_seconds, 60) }}:{{ str_pad($project->duration_seconds % 60, 2, '0', STR_PAD_LEFT) }}
@else
@endif
</td>
<td class="text-center">
<span class="badge bg-light text-dark border">
{{ $project->projectCollaborators->count() }}
</span>
</td>
<td class="small text-muted text-nowrap">
{{ $project->created_at->format('d/m/Y H:i') }}
</td>
<td>
@if(! $project->deleted_at)
<button class="btn btn-sm btn-outline-warning"
title="Pindah Pemilikan"
data-bs-toggle="modal"
data-bs-target="#modalTransfer{{ $project->id }}"
data-project-title="{{ $project->title }}"
data-project-owner="{{ $project->owner->name ?? '—' }}">
<i class="bi bi-arrow-left-right"></i>
</button>
@endif
</td>
</tr>
@empty
<tr>
<td colspan="8" class="text-center text-muted py-4">
<i class="bi bi-inbox fs-4 d-block mb-2"></i>Tiada projek ditemui.
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
</div>
@if($projects->hasPages())
<div class="card-footer bg-white">
{{ $projects->links() }}
</div>
@endif
</div>
<div class="mt-2 text-muted small">
<i class="bi bi-info-circle me-1"></i>
Kandungan audio, teks transkripsi, dan ulasan projek tidak dipaparkan kepada pentadbir.
</div>
{{-- Transfer Ownership Modals --}}
@foreach($projects as $project)
@if(! $project->deleted_at)
<div class="modal fade" id="modalTransfer{{ $project->id }}" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h6 class="modal-title">
<i class="bi bi-arrow-left-right me-2 text-warning"></i>Pindah Pemilikan Projek
</h6>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<form method="POST" action="{{ route('admin.projects.transfer-owner', $project) }}">
@csrf
<div class="modal-body">
<div class="alert alert-warning py-2 small">
<i class="bi bi-exclamation-triangle me-1"></i>
Tindakan ini akan memindahkan semua hak pemilik projek
<strong>"{{ $project->title }}"</strong> kepada pengguna lain.
Pemilik asal: <strong>{{ $project->owner->name ?? '—' }}</strong>
</div>
<div class="mb-3">
<label class="form-label fw-semibold">Pemilik Baru <span class="text-danger">*</span></label>
<select class="form-select" name="new_owner_id" required>
<option value=""> Pilih pengguna </option>
@foreach($owners as $owner)
@if($owner->id !== $project->owner_user_id)
<option value="{{ $owner->id }}">{{ $owner->name }}</option>
@endif
@endforeach
</select>
</div>
<div class="mb-3">
<label class="form-label fw-semibold">
Justifikasi <span class="text-danger">*</span>
<span class="text-muted fw-normal small">(min. 10 aksara)</span>
</label>
<textarea class="form-control" name="justification" rows="3"
placeholder="Nyatakan sebab pemindahan projek ini..."
minlength="10" maxlength="1000" required></textarea>
<div class="form-text">Justifikasi ini akan direkodkan dalam log audit.</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Batal</button>
<button type="submit" class="btn btn-warning"
onclick="return confirm('Sahkan pemindahan pemilikan projek ini?')">
<i class="bi bi-arrow-left-right me-1"></i>Pindah Pemilikan
</button>
</div>
</form>
</div>
</div>
</div>
@endif
@endforeach
@endsection

View File

@@ -0,0 +1,93 @@
@extends('layouts.admin')
@section('title', 'Daftar Pengguna Baru')
@section('content')
<div class="d-flex justify-content-between align-items-center mb-4">
<h4 class="fw-bold mb-0"><i class="bi bi-person-plus me-2 text-primary"></i>Daftar Pengguna Baru</h4>
<a href="{{ route('admin.users.index') }}" class="btn btn-outline-secondary">
<i class="bi bi-arrow-left me-1"></i>Kembali
</a>
</div>
<div class="card">
<div class="card-body">
<form method="POST" action="{{ route('admin.users.store') }}">
@csrf
<div class="row g-3">
<div class="col-md-6">
<label for="name" class="form-label fw-semibold">Nama Penuh <span class="text-danger">*</span></label>
<input type="text" class="form-control @error('name') is-invalid @enderror"
id="name" name="name" value="{{ old('name') }}" required>
@error('name')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="col-md-6">
<label for="email" class="form-label fw-semibold">E-mel <span class="text-danger">*</span></label>
<input type="email" class="form-control @error('email') is-invalid @enderror"
id="email" name="email" value="{{ old('email') }}" required>
@error('email')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="col-md-6">
<label for="password" class="form-label fw-semibold">Kata Laluan <span class="text-danger">*</span></label>
<input type="password" class="form-control @error('password') is-invalid @enderror"
id="password" name="password" required>
<div class="form-text">Minimum 8 aksara, huruf besar, huruf kecil, dan nombor.</div>
@error('password')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="col-md-6">
<label for="password_confirmation" class="form-label fw-semibold">Sahkan Kata Laluan <span class="text-danger">*</span></label>
<input type="password" class="form-control"
id="password_confirmation" name="password_confirmation" required>
</div>
<div class="col-md-6">
<label for="role" class="form-label fw-semibold">Peranan <span class="text-danger">*</span></label>
<select class="form-select @error('role') is-invalid @enderror" id="role" name="role" required>
<option value="user" {{ old('role', 'user') === 'user' ? 'selected' : '' }}>Pengguna Jabatan</option>
<option value="admin" {{ old('role') === 'admin' ? 'selected' : '' }}>Pentadbir</option>
</select>
@error('role')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="col-md-6">
<label for="department_id" class="form-label fw-semibold">Jabatan</label>
<select class="form-select @error('department_id') is-invalid @enderror"
id="department_id" name="department_id">
<option value=""> Pilih Jabatan </option>
@foreach($departments as $dept)
<option value="{{ $dept->id }}"
{{ old('department_id') == $dept->id ? 'selected' : '' }}>
{{ $dept->name }}
</option>
@endforeach
</select>
@error('department_id')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
</div>
<hr class="my-4">
<div class="d-flex gap-2">
<button type="submit" class="btn btn-primary">
<i class="bi bi-person-check me-1"></i>Daftarkan Pengguna
</button>
<a href="{{ route('admin.users.index') }}" class="btn btn-outline-secondary">Batal</a>
</div>
</form>
</div>
</div>
@endsection

View File

@@ -0,0 +1,81 @@
@extends('layouts.admin')
@section('title', 'Kemaskini E-mel Pengguna')
@section('content')
<div class="d-flex justify-content-between align-items-center mb-4">
<h4 class="fw-bold mb-0"><i class="bi bi-pencil me-2 text-primary"></i>Kemaskini E-mel Pengguna</h4>
<a href="{{ route('admin.users.index') }}" class="btn btn-outline-secondary">
<i class="bi bi-arrow-left me-1"></i>Kembali
</a>
</div>
<div class="row">
<div class="col-md-6">
{{-- User Info Card --}}
<div class="card mb-3">
<div class="card-header bg-light fw-semibold">Maklumat Pengguna</div>
<div class="card-body">
<table class="table table-sm mb-0">
<tr>
<td class="text-muted" width="35%">Nama</td>
<td class="fw-semibold">{{ $user->name }}</td>
</tr>
<tr>
<td class="text-muted">E-mel Semasa</td>
<td>{{ $user->email }}</td>
</tr>
<tr>
<td class="text-muted">Jabatan</td>
<td>{{ $user->department?->name ?? '—' }}</td>
</tr>
<tr>
<td class="text-muted">Status</td>
<td>
@if($user->is_active)
<span class="badge bg-success">Aktif</span>
@else
<span class="badge bg-secondary">Dinyahaktif</span>
@endif
</td>
</tr>
</table>
</div>
</div>
{{-- Email Update Form --}}
<div class="card">
<div class="card-header bg-light fw-semibold">Kemaskini E-mel</div>
<div class="card-body">
<form method="POST" action="{{ route('admin.users.update-email', $user) }}">
@csrf @method('PATCH')
<div class="mb-3">
<label for="email" class="form-label fw-semibold">E-mel Baru <span class="text-danger">*</span></label>
<input type="email" class="form-control @error('email') is-invalid @enderror"
id="email" name="email" value="{{ old('email', $user->email) }}" required>
@error('email')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="mb-4">
<label for="justification" class="form-label fw-semibold">Justifikasi</label>
<textarea class="form-control" id="justification" name="justification"
rows="3" maxlength="500"
placeholder="Nyatakan sebab perubahan e-mel...">{{ old('justification') }}</textarea>
<div class="form-text">Perubahan ini akan direkod dalam log audit.</div>
</div>
<div class="d-flex gap-2">
<button type="submit" class="btn btn-primary">
<i class="bi bi-envelope-check me-1"></i>Kemaskini E-mel
</button>
<a href="{{ route('admin.users.index') }}" class="btn btn-outline-secondary">Batal</a>
</div>
</form>
</div>
</div>
</div>
</div>
@endsection

View File

@@ -0,0 +1,125 @@
@extends('layouts.admin')
@section('title', 'Senarai Pengguna')
@section('content')
<div class="d-flex justify-content-between align-items-center mb-4">
<h4 class="fw-bold mb-0"><i class="bi bi-people me-2 text-primary"></i>Senarai Pengguna</h4>
<a href="{{ route('admin.users.create') }}" class="btn btn-primary">
<i class="bi bi-person-plus me-1"></i>Daftar Pengguna Baru
</a>
</div>
{{-- Filter --}}
<div class="card mb-3">
<div class="card-body py-2">
<form method="GET" class="row g-2 align-items-center">
<div class="col-md-4">
<input type="text" class="form-control form-control-sm" name="search"
value="{{ request('search') }}" placeholder="Cari nama atau e-mel...">
</div>
<div class="col-md-3">
<select class="form-select form-select-sm" name="status">
<option value="">Semua Status</option>
<option value="active" {{ request('status') === 'active' ? 'selected' : '' }}>Aktif</option>
<option value="inactive" {{ request('status') === 'inactive' ? 'selected' : '' }}>Dinyahaktif</option>
</select>
</div>
<div class="col-auto">
<button class="btn btn-sm btn-outline-secondary" type="submit">
<i class="bi bi-search me-1"></i>Cari
</button>
<a href="{{ route('admin.users.index') }}" class="btn btn-sm btn-outline-secondary ms-1">Padam Carian</a>
</div>
</form>
</div>
</div>
{{-- Table --}}
<div class="card">
<div class="card-body p-0">
<div class="table-responsive">
<table class="table table-hover mb-0">
<thead class="table-light">
<tr>
<th>Nama</th>
<th>E-mel</th>
<th>Jabatan</th>
<th>Status</th>
<th>Log Masuk Terakhir</th>
<th>Tindakan</th>
</tr>
</thead>
<tbody>
@forelse($users as $user)
<tr>
<td>
<span class="fw-semibold">{{ $user->name }}</span>
</td>
<td>{{ $user->email }}</td>
<td>{{ $user->department?->name ?? '—' }}</td>
<td>
@if($user->is_active)
<span class="badge bg-success">Aktif</span>
@else
<span class="badge bg-secondary">Dinyahaktif</span>
@endif
</td>
<td>
{{ $user->last_login_at?->format('d/m/Y H:i') ?? 'Belum pernah' }}
</td>
<td>
<div class="d-flex gap-1">
<a href="{{ route('admin.users.edit', $user) }}"
class="btn btn-sm btn-outline-primary" title="Edit E-mel">
<i class="bi bi-pencil"></i>
</a>
@if($user->is_active)
<form method="POST" action="{{ route('admin.users.deactivate', $user) }}"
onsubmit="return confirm('Dinyahaktifkan akaun {{ $user->name }}?')">
@csrf @method('PATCH')
<button class="btn btn-sm btn-outline-warning" title="Nyahaktif">
<i class="bi bi-person-x"></i>
</button>
</form>
@else
<form method="POST" action="{{ route('admin.users.activate', $user) }}">
@csrf @method('PATCH')
<button class="btn btn-sm btn-outline-success" title="Aktifkan">
<i class="bi bi-person-check"></i>
</button>
</form>
@endif
@if(! $user->hasUsage())
<form method="POST" action="{{ route('admin.users.destroy', $user) }}"
onsubmit="return confirm('PADAM akaun {{ $user->name }} secara kekal?')">
@csrf @method('DELETE')
<button class="btn btn-sm btn-outline-danger" title="Padam">
<i class="bi bi-trash"></i>
</button>
</form>
@endif
</div>
</td>
</tr>
@empty
<tr>
<td colspan="6" class="text-center text-muted py-4">
<i class="bi bi-inbox fs-4 d-block mb-2"></i>Tiada pengguna ditemui.
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
</div>
@if($users->hasPages())
<div class="card-footer bg-white">
{{ $users->links() }}
</div>
@endif
</div>
@endsection