first commit
This commit is contained in:
25
resources/js/app.js
Normal file
25
resources/js/app.js
Normal file
@@ -0,0 +1,25 @@
|
||||
import $ from 'jquery';
|
||||
window.$ = window.jQuery = $;
|
||||
|
||||
import * as bootstrap from 'bootstrap';
|
||||
window.bootstrap = bootstrap;
|
||||
|
||||
// Aktifkan tooltip & auto-dismiss alert
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
document.querySelectorAll('[data-bs-toggle="tooltip"]').forEach((el) => new bootstrap.Tooltip(el));
|
||||
|
||||
setTimeout(() => {
|
||||
document.querySelectorAll('.alert-auto-dismiss').forEach((el) => {
|
||||
bootstrap.Alert.getOrCreateInstance(el).close();
|
||||
});
|
||||
}, 5000);
|
||||
|
||||
// Pengesahan ringkas sebelum hantar borang berbahaya
|
||||
document.querySelectorAll('form[data-confirm]').forEach((form) => {
|
||||
form.addEventListener('submit', (e) => {
|
||||
if (!window.confirm(form.getAttribute('data-confirm'))) {
|
||||
e.preventDefault();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
76
resources/sass/app.scss
Normal file
76
resources/sass/app.scss
Normal file
@@ -0,0 +1,76 @@
|
||||
// Sistem Pemantauan Kemampanan Pusat Data MBIP — gaya aplikasi
|
||||
$primary: #0a5c36; // hijau kemampanan
|
||||
$secondary: #5c6b73;
|
||||
|
||||
@import "bootstrap/scss/bootstrap";
|
||||
@import "bootstrap-icons/font/bootstrap-icons.css";
|
||||
|
||||
body {
|
||||
background-color: #f4f6f8;
|
||||
font-size: 0.925rem;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
min-height: 100vh;
|
||||
background: linear-gradient(180deg, #0a5c36 0%, #074026 100%);
|
||||
}
|
||||
|
||||
.sidebar .nav-link {
|
||||
color: rgba(255, 255, 255, 0.85);
|
||||
border-radius: 0.375rem;
|
||||
margin-bottom: 2px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.sidebar .nav-link:hover,
|
||||
.sidebar .nav-link.active {
|
||||
background-color: rgba(255, 255, 255, 0.15);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.sidebar .nav-link i {
|
||||
width: 1.25rem;
|
||||
text-align: center;
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
|
||||
.sidebar-brand {
|
||||
color: #fff;
|
||||
font-weight: 700;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.card {
|
||||
border: none;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
.stat-card .stat-value {
|
||||
font-size: 1.75rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.table thead th {
|
||||
font-size: 0.8rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.03em;
|
||||
color: #6c757d;
|
||||
}
|
||||
|
||||
.checklist-section-header {
|
||||
background-color: #e9f3ee;
|
||||
border-left: 4px solid $primary;
|
||||
}
|
||||
|
||||
.chat-message {
|
||||
border-radius: 0.5rem;
|
||||
padding: 0.5rem 0.75rem;
|
||||
margin-bottom: 0.5rem;
|
||||
background: #fff;
|
||||
border: 1px solid #e3e7ea;
|
||||
}
|
||||
|
||||
.chat-message.internal {
|
||||
background: #fff8e1;
|
||||
border-color: #ffe082;
|
||||
}
|
||||
38
resources/views/audit/index.blade.php
Normal file
38
resources/views/audit/index.blade.php
Normal file
@@ -0,0 +1,38 @@
|
||||
@extends('layouts.app')
|
||||
@section('title', 'Log Audit')
|
||||
@section('heading', 'Log Audit')
|
||||
@section('page-title', 'Log Audit / Jejak Aktiviti')
|
||||
|
||||
@section('content')
|
||||
<div class="card mb-3"><div class="card-body">
|
||||
<form class="row g-2">
|
||||
<div class="col-md-4">
|
||||
<select name="log" class="form-select">
|
||||
<option value="">Semua log</option>
|
||||
@foreach ($logNames as $ln)<option value="{{ $ln }}" @selected(request('log')===$ln)>{{ $ln }}</option>@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-2 d-grid"><button class="btn btn-outline-primary">Tapis</button></div>
|
||||
</form>
|
||||
</div></div>
|
||||
|
||||
<div class="card"><div class="card-body p-0">
|
||||
<table class="table table-sm table-hover mb-0 align-middle">
|
||||
<thead><tr><th class="ps-3">Masa</th><th>Log</th><th>Aktiviti</th><th>Oleh</th><th>Subjek</th></tr></thead>
|
||||
<tbody>
|
||||
@forelse ($logs as $log)
|
||||
<tr>
|
||||
<td class="ps-3 small">{{ $log->created_at->format('d/m/Y H:i:s') }}</td>
|
||||
<td><span class="badge bg-light text-dark">{{ $log->log_name }}</span></td>
|
||||
<td class="small">{{ $log->description }}</td>
|
||||
<td class="small">{{ $log->causer?->name ?? 'Sistem' }}</td>
|
||||
<td class="small text-muted">{{ class_basename($log->subject_type) }} #{{ $log->subject_id }}</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr><td colspan="5" class="text-center text-muted py-3">Tiada log.</td></tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div></div>
|
||||
<div class="mt-2">{{ $logs->links() }}</div>
|
||||
@endsection
|
||||
51
resources/views/auth/login.blade.php
Normal file
51
resources/views/auth/login.blade.php
Normal file
@@ -0,0 +1,51 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ms">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Log Masuk — Pemantauan Kemampanan Pusat Data MBIP</title>
|
||||
@vite(['resources/sass/app.scss', 'resources/js/app.js'])
|
||||
</head>
|
||||
<body class="d-flex align-items-center" style="min-height:100vh; background:linear-gradient(135deg,#0a5c36,#074026);">
|
||||
<div class="container">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-6 col-lg-5 col-xl-4">
|
||||
<div class="text-center text-white mb-4">
|
||||
<i class="bi bi-buildings" style="font-size:2.5rem"></i>
|
||||
<h5 class="mt-2 mb-0">MBIP</h5>
|
||||
<p class="opacity-75 small mb-0">Sistem Pemantauan Kemampanan Pusat Data</p>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-body p-4">
|
||||
<h5 class="card-title mb-3">Log Masuk</h5>
|
||||
|
||||
@if ($errors->any())
|
||||
<div class="alert alert-danger py-2">{{ $errors->first() }}</div>
|
||||
@endif
|
||||
|
||||
<form method="POST" action="{{ route('login') }}">
|
||||
@csrf
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Emel</label>
|
||||
<input type="email" name="email" value="{{ old('email') }}" class="form-control @error('email') is-invalid @enderror" required autofocus>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Kata Laluan</label>
|
||||
<input type="password" name="password" class="form-control" required>
|
||||
</div>
|
||||
<div class="form-check mb-3">
|
||||
<input type="checkbox" name="remember" id="remember" class="form-check-input">
|
||||
<label for="remember" class="form-check-label">Ingat saya</label>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-success w-100">
|
||||
<i class="bi bi-box-arrow-in-right me-1"></i> Log Masuk
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-center text-white-50 small mt-3 mb-0">© {{ date('Y') }} Majlis Bandaraya Iskandar Puteri</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
23
resources/views/checklist/templates/_formula_modal.blade.php
Normal file
23
resources/views/checklist/templates/_formula_modal.blade.php
Normal file
@@ -0,0 +1,23 @@
|
||||
<div class="modal fade" id="editFormula{{ $formula->id }}" tabindex="-1"><div class="modal-dialog"><div class="modal-content">
|
||||
<form method="POST" action="{{ route('checklist.formulas.update', $formula) }}">
|
||||
@csrf @method('PUT')
|
||||
<div class="modal-header"><h5 class="modal-title">Kemaskini Formula {{ $formula->result_token }}</h5><button type="button" class="btn-close" data-bs-dismiss="modal"></button></div>
|
||||
<div class="modal-body">
|
||||
<div class="mb-2"><label class="form-label">Token Hasil</label><input name="result_token" value="{{ $formula->result_token }}" class="form-control" required></div>
|
||||
<div class="mb-2"><label class="form-label">Label</label><input name="label" value="{{ $formula->label }}" class="form-control"></div>
|
||||
<div class="mb-2"><label class="form-label">Ungkapan</label><input name="expression" value="{{ $formula->expression }}" class="form-control" required></div>
|
||||
<div class="row g-2 mb-2">
|
||||
<div class="col-6"><label class="form-label">Unit</label><input name="unit" value="{{ $formula->unit }}" class="form-control"></div>
|
||||
<div class="col-6"><label class="form-label">Turutan</label><input name="order" type="number" value="{{ $formula->order }}" class="form-control"></div>
|
||||
</div>
|
||||
<div class="form-check mb-2"><input type="checkbox" name="guard_div_zero" value="1" class="form-check-input" id="guard{{ $formula->id }}" @checked($formula->guard_div_zero)><label for="guard{{ $formula->id }}" class="form-check-label">Lindung pembahagian dengan sifar</label></div>
|
||||
<div class="mb-2"><label class="form-label">Seksyen berkaitan</label>
|
||||
<select name="checklist_section_id" class="form-select">
|
||||
<option value="">— Tiada —</option>
|
||||
@foreach ($template->sections as $s)<option value="{{ $s->id }}" @selected($formula->checklist_section_id==$s->id)>{{ $s->code }} — {{ $s->title }}</option>@endforeach
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer"><button class="btn btn-success">Simpan</button></div>
|
||||
</form>
|
||||
</div></div></div>
|
||||
49
resources/views/checklist/templates/_item_modal.blade.php
Normal file
49
resources/views/checklist/templates/_item_modal.blade.php
Normal file
@@ -0,0 +1,49 @@
|
||||
@php
|
||||
$isEdit = (bool) $item;
|
||||
$modalId = $isEdit ? 'editItem'.$item->id : 'addItem'.$section->id;
|
||||
$action = $isEdit ? route('checklist.items.update', $item) : route('checklist.items.store', $section);
|
||||
$optsText = $isEdit && is_array($item->options) ? implode("\n", $item->options) : '';
|
||||
@endphp
|
||||
<div class="modal fade" id="{{ $modalId }}" tabindex="-1"><div class="modal-dialog modal-lg"><div class="modal-content">
|
||||
<form method="POST" action="{{ $action }}">
|
||||
@csrf
|
||||
@if($isEdit) @method('PUT') @endif
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">{{ $isEdit ? 'Kemaskini Item '.$item->code : 'Tambah Item — Seksyen '.$section->code }}</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="row g-2">
|
||||
<div class="col-12"><label class="form-label">Letak dalam Skop</label>
|
||||
<select name="checklist_scope_id" class="form-select" {{ $section->scopes->isEmpty() ? 'disabled' : '' }}>
|
||||
<option value="">— Tiada (terus di bawah seksyen) —</option>
|
||||
@foreach ($section->scopes as $sc)<option value="{{ $sc->id }}" @selected(($item->checklist_scope_id ?? null)==$sc->id)>{{ $sc->code }} — {{ $sc->title }}</option>@endforeach
|
||||
</select>
|
||||
@if($section->scopes->isEmpty())
|
||||
<div class="form-text text-warning">Seksyen ini belum ada skop. Tambah skop dahulu untuk meletakkan item dalam skop.</div>
|
||||
@else
|
||||
<div class="form-text">Pilih skop atau biarkan "Tiada" untuk item terus di bawah seksyen.</div>
|
||||
@endif
|
||||
</div>
|
||||
<div class="col-md-3"><label class="form-label">Kod <span class="text-danger">*</span></label><input name="code" value="{{ $item->code ?? '' }}" class="form-control" placeholder="A.1.i" required></div>
|
||||
<div class="col-md-3"><label class="form-label">Token Formula</label><input name="formula_token" value="{{ $item->formula_token ?? '' }}" class="form-control" placeholder="SKOP1"></div>
|
||||
<div class="col-md-3"><label class="form-label">Unit</label><input name="unit" value="{{ $item->unit ?? '' }}" class="form-control" placeholder="tCO2e"></div>
|
||||
<div class="col-md-3"><label class="form-label">Turutan</label><input name="order" type="number" value="{{ $item->order ?? ($section->items->count()+1) }}" class="form-control"></div>
|
||||
<div class="col-12"><label class="form-label">Label <span class="text-danger">*</span></label><input name="label" value="{{ $item->label ?? '' }}" class="form-control" required></div>
|
||||
<div class="col-12"><label class="form-label">Penerangan</label><textarea name="description" class="form-control">{{ $item->description ?? '' }}</textarea></div>
|
||||
<div class="col-md-6"><label class="form-label">Jenis Input</label>
|
||||
<select name="input_type" class="form-select">
|
||||
@foreach ($inputTypes as $val => $lbl)<option value="{{ $val }}" @selected(($item->input_type ?? 'number')===$val)>{{ $lbl }}</option>@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-6 d-flex align-items-end gap-3">
|
||||
<div class="form-check"><input type="checkbox" name="is_required" value="1" class="form-check-input" id="{{ $modalId }}req" @checked($item->is_required ?? false)><label for="{{ $modalId }}req" class="form-check-label">Wajib</label></div>
|
||||
<div class="form-check"><input type="checkbox" name="is_calculated" value="1" class="form-check-input" id="{{ $modalId }}calc" @checked($item->is_calculated ?? false)><label for="{{ $modalId }}calc" class="form-check-label">Dikira</label></div>
|
||||
</div>
|
||||
<div class="col-12"><label class="form-label">Formula item (jika dikira)</label><input name="formula" value="{{ $item->formula ?? '' }}" class="form-control" placeholder="A - B"></div>
|
||||
<div class="col-12"><label class="form-label">Pilihan (untuk jenis select) — satu baris satu pilihan</label><textarea name="options" rows="3" class="form-control" placeholder="GBI GreenRE LEED">{{ $optsText }}</textarea></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer"><button class="btn btn-success">Simpan</button></div>
|
||||
</form>
|
||||
</div></div></div>
|
||||
17
resources/views/checklist/templates/_section_modal.blade.php
Normal file
17
resources/views/checklist/templates/_section_modal.blade.php
Normal file
@@ -0,0 +1,17 @@
|
||||
{{-- Modal edit seksyen --}}
|
||||
<div class="modal fade" id="editSection{{ $section->id }}" tabindex="-1"><div class="modal-dialog"><div class="modal-content">
|
||||
<form method="POST" action="{{ route('checklist.sections.update', $section) }}">
|
||||
@csrf @method('PUT')
|
||||
<div class="modal-header"><h5 class="modal-title">Kemaskini Seksyen {{ $section->code }}</h5><button type="button" class="btn-close" data-bs-dismiss="modal"></button></div>
|
||||
<div class="modal-body">
|
||||
<div class="row g-2">
|
||||
<div class="col-4"><label class="form-label">Kod</label><input name="code" value="{{ $section->code }}" class="form-control" required></div>
|
||||
<div class="col-8"><label class="form-label">Tajuk</label><input name="title" value="{{ $section->title }}" class="form-control" required></div>
|
||||
<div class="col-12"><label class="form-label">Penerangan</label><textarea name="description" class="form-control">{{ $section->description }}</textarea></div>
|
||||
<div class="col-6"><label class="form-label">Turutan</label><input name="order" type="number" value="{{ $section->order }}" class="form-control"></div>
|
||||
<div class="col-6 d-flex align-items-end"><div class="form-check form-switch"><input type="checkbox" name="is_formula_enabled" value="1" class="form-check-input" id="secFormula{{ $section->id }}" @checked($section->is_formula_enabled)><label for="secFormula{{ $section->id }}" class="form-check-label">Formula didayakan</label></div></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer"><button class="btn btn-success">Simpan</button></div>
|
||||
</form>
|
||||
</div></div></div>
|
||||
175
resources/views/checklist/templates/builder.blade.php
Normal file
175
resources/views/checklist/templates/builder.blade.php
Normal file
@@ -0,0 +1,175 @@
|
||||
@extends('layouts.app')
|
||||
@section('title', 'Pembina Templat')
|
||||
@section('heading', 'Templat Senarai Semak')
|
||||
@section('page-title', 'Pembina: '.$template->name)
|
||||
@section('subtitle', 'Versi '.$template->version.($template->is_active ? ' • Aktif' : ''))
|
||||
|
||||
@section('actions')
|
||||
<button class="btn btn-outline-secondary" data-bs-toggle="modal" data-bs-target="#editTemplateModal"><i class="bi bi-gear me-1"></i> Tetapan Templat</button>
|
||||
<a href="{{ route('checklist-templates.index') }}" class="btn btn-link">Kembali</a>
|
||||
@endsection
|
||||
|
||||
@section('content')
|
||||
@php($inputTypes = App\Models\ChecklistItem::INPUT_TYPES)
|
||||
|
||||
<div class="row g-3">
|
||||
<div class="col-lg-8">
|
||||
{{-- ============ SEKSYEN ============ --}}
|
||||
@foreach ($template->sections as $section)
|
||||
<div class="card mb-3">
|
||||
<div class="card-header checklist-section-header d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<span class="badge bg-success me-1">{{ $section->code }}</span>
|
||||
<strong>{{ $section->title }}</strong>
|
||||
@if($section->is_formula_enabled)<span class="badge bg-info ms-1">Formula</span>@endif
|
||||
</div>
|
||||
<div class="btn-group btn-group-sm">
|
||||
<button class="btn btn-outline-secondary" data-bs-toggle="modal" data-bs-target="#editSection{{ $section->id }}"><i class="bi bi-pencil"></i></button>
|
||||
<form method="POST" action="{{ route('checklist.sections.destroy', $section) }}" data-confirm="Padam seksyen '{{ $section->code }}' beserta skop & item?">@csrf @method('DELETE')<button class="btn btn-outline-danger"><i class="bi bi-trash"></i></button></form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
@if($section->description)<p class="small text-muted">{{ $section->description }}</p>@endif
|
||||
|
||||
{{-- Skop --}}
|
||||
@if($section->scopes->isNotEmpty())
|
||||
<div class="mb-2">
|
||||
<h6 class="small text-uppercase text-muted">Skop</h6>
|
||||
@foreach ($section->scopes as $scope)
|
||||
<div class="d-flex justify-content-between align-items-center border rounded px-2 py-1 mb-1">
|
||||
<span><span class="badge bg-secondary">{{ $scope->code }}</span> {{ $scope->title }}</span>
|
||||
<form method="POST" action="{{ route('checklist.scopes.destroy', $scope) }}" data-confirm="Padam skop ini?">@csrf @method('DELETE')<button class="btn btn-sm btn-link text-danger p-0"><i class="bi bi-x-lg"></i></button></form>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
@endif
|
||||
|
||||
{{-- Borang tambah skop --}}
|
||||
<details class="mb-3"><summary class="small text-primary" style="cursor:pointer">+ Tambah skop</summary>
|
||||
<form method="POST" action="{{ route('checklist.scopes.store', $section) }}" class="row g-2 mt-1">
|
||||
@csrf
|
||||
<div class="col-3"><input name="code" class="form-control form-control-sm" placeholder="Kod (SKOP1)" required></div>
|
||||
<div class="col-5"><input name="title" class="form-control form-control-sm" placeholder="Tajuk skop" required></div>
|
||||
<div class="col-2"><input name="order" type="number" class="form-control form-control-sm" placeholder="Turutan" value="0"></div>
|
||||
<div class="col-2 d-grid"><button class="btn btn-sm btn-outline-success">Tambah</button></div>
|
||||
</form>
|
||||
</details>
|
||||
|
||||
{{-- Item --}}
|
||||
<h6 class="small text-uppercase text-muted">Item</h6>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-sm align-middle">
|
||||
<thead><tr><th>Kod</th><th>Label</th><th>Skop</th><th>Token</th><th>Jenis</th><th>Unit</th><th>Wajib</th><th></th></tr></thead>
|
||||
<tbody>
|
||||
@forelse ($section->items as $item)
|
||||
<tr>
|
||||
<td>{{ $item->code }}</td>
|
||||
<td>{{ $item->label }}@if($item->is_calculated)<span class="badge bg-info ms-1">dikira</span>@endif</td>
|
||||
<td>@if($item->scope)<span class="badge bg-secondary">{{ $item->scope->code }}</span>@else<span class="text-muted small">— Tiada —</span>@endif</td>
|
||||
<td><code>{{ $item->formula_token ?: '—' }}</code></td>
|
||||
<td><span class="small">{{ $inputTypes[$item->input_type] ?? $item->input_type }}</span></td>
|
||||
<td>{{ $item->unit ?: '—' }}</td>
|
||||
<td>{!! $item->is_required ? '<i class="bi bi-check-lg text-success"></i>' : '—' !!}</td>
|
||||
<td class="text-end">
|
||||
<button class="btn btn-sm btn-link p-0 me-1" data-bs-toggle="modal" data-bs-target="#editItem{{ $item->id }}"><i class="bi bi-pencil"></i></button>
|
||||
<form method="POST" action="{{ route('checklist.items.destroy', $item) }}" class="d-inline" data-confirm="Padam item ini?">@csrf @method('DELETE')<button class="btn btn-sm btn-link text-danger p-0"><i class="bi bi-trash"></i></button></form>
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr><td colspan="8" class="text-center text-muted">Tiada item.</td></tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{{-- Borang tambah item --}}
|
||||
<button class="btn btn-sm btn-outline-primary" data-bs-toggle="modal" data-bs-target="#addItem{{ $section->id }}"><i class="bi bi-plus-lg"></i> Tambah Item</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Modal edit seksyen --}}
|
||||
@include('checklist.templates._section_modal', ['section' => $section, 'mode' => 'edit'])
|
||||
{{-- Modal tambah item --}}
|
||||
@include('checklist.templates._item_modal', ['section' => $section, 'item' => null, 'inputTypes' => $inputTypes])
|
||||
{{-- Modal edit item --}}
|
||||
@foreach ($section->items as $item)
|
||||
@include('checklist.templates._item_modal', ['section' => $section, 'item' => $item, 'inputTypes' => $inputTypes])
|
||||
@endforeach
|
||||
@endforeach
|
||||
|
||||
{{-- Tambah seksyen --}}
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h6>Tambah Seksyen Baharu</h6>
|
||||
<form method="POST" action="{{ route('checklist.sections.store', $template) }}" class="row g-2">
|
||||
@csrf
|
||||
<div class="col-md-2"><input name="code" class="form-control" placeholder="Kod (A)" required></div>
|
||||
<div class="col-md-5"><input name="title" class="form-control" placeholder="Tajuk seksyen" required></div>
|
||||
<div class="col-md-2"><input name="order" type="number" class="form-control" placeholder="Turutan" value="{{ $template->sections->count() + 1 }}"></div>
|
||||
<div class="col-md-2"><div class="form-check mt-2"><input type="checkbox" name="is_formula_enabled" value="1" class="form-check-input" id="newSecFormula"><label for="newSecFormula" class="form-check-label small">Formula</label></div></div>
|
||||
<div class="col-md-1 d-grid"><button class="btn btn-success">+</button></div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- ============ FORMULA ============ --}}
|
||||
<div class="col-lg-4">
|
||||
<div class="card position-sticky" style="top:70px">
|
||||
<div class="card-header bg-white fw-semibold">Formula Seksyen</div>
|
||||
<div class="card-body">
|
||||
<p class="small text-muted">Token tersedia: <em>formula_token</em> setiap item & <em>result_token</em> formula sebelumnya. Cth: <code>SKOP1 + SKOP2 + SKOP3</code>, <code>A - B</code>, <code>D / A * 100</code>.</p>
|
||||
@forelse ($template->formulas as $f)
|
||||
<div class="border rounded p-2 mb-2">
|
||||
<div class="d-flex justify-content-between">
|
||||
<strong><code>{{ $f->result_token }}</code> = {{ $f->expression }}</strong>
|
||||
<div>
|
||||
<button class="btn btn-sm btn-link p-0 me-1" data-bs-toggle="modal" data-bs-target="#editFormula{{ $f->id }}"><i class="bi bi-pencil"></i></button>
|
||||
<form method="POST" action="{{ route('checklist.formulas.destroy', $f) }}" class="d-inline" data-confirm="Padam formula?">@csrf @method('DELETE')<button class="btn btn-sm btn-link text-danger p-0"><i class="bi bi-trash"></i></button></form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="small text-muted">{{ $f->label }} @if($f->unit)({{ $f->unit }})@endif @if($f->guard_div_zero)<span class="badge bg-warning text-dark">guard ÷0</span>@endif</div>
|
||||
</div>
|
||||
@include('checklist.templates._formula_modal', ['template' => $template, 'formula' => $f])
|
||||
@empty
|
||||
<p class="text-muted small">Tiada formula.</p>
|
||||
@endforelse
|
||||
|
||||
<hr>
|
||||
<h6 class="small">Tambah Formula</h6>
|
||||
<form method="POST" action="{{ route('checklist.formulas.store', $template) }}">
|
||||
@csrf
|
||||
<div class="mb-2"><input name="result_token" class="form-control form-control-sm" placeholder="Token hasil (cth: A)" required></div>
|
||||
<div class="mb-2"><input name="label" class="form-control form-control-sm" placeholder="Label"></div>
|
||||
<div class="mb-2"><input name="expression" class="form-control form-control-sm" placeholder="Ungkapan (cth: A - B)" required></div>
|
||||
<div class="row g-2 mb-2">
|
||||
<div class="col-6"><input name="unit" class="form-control form-control-sm" placeholder="Unit"></div>
|
||||
<div class="col-6"><input name="order" type="number" class="form-control form-control-sm" placeholder="Turutan" value="{{ $template->formulas->count()+1 }}"></div>
|
||||
</div>
|
||||
<div class="form-check mb-2"><input type="checkbox" name="guard_div_zero" value="1" class="form-check-input" id="guardNew"><label for="guardNew" class="form-check-label small">Lindung pembahagian dengan sifar</label></div>
|
||||
<select name="checklist_section_id" class="form-select form-select-sm mb-2">
|
||||
<option value="">— Kaitkan seksyen (pilihan) —</option>
|
||||
@foreach ($template->sections as $s)<option value="{{ $s->id }}">{{ $s->code }} — {{ $s->title }}</option>@endforeach
|
||||
</select>
|
||||
<button class="btn btn-sm btn-success w-100">Tambah Formula</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Modal tetapan templat --}}
|
||||
<div class="modal fade" id="editTemplateModal" tabindex="-1"><div class="modal-dialog"><div class="modal-content">
|
||||
<form method="POST" action="{{ route('checklist-templates.update', $template) }}">
|
||||
@csrf @method('PUT')
|
||||
<div class="modal-header"><h5 class="modal-title">Tetapan Templat</h5><button type="button" class="btn-close" data-bs-dismiss="modal"></button></div>
|
||||
<div class="modal-body">
|
||||
<div class="mb-2"><label class="form-label">Nama</label><input name="name" value="{{ $template->name }}" class="form-control" required></div>
|
||||
<div class="mb-2"><label class="form-label">Versi</label><input name="version" value="{{ $template->version }}" class="form-control" required></div>
|
||||
<div class="mb-2"><label class="form-label">Penerangan</label><textarea name="description" class="form-control">{{ $template->description }}</textarea></div>
|
||||
<div class="form-check form-switch"><input type="checkbox" name="is_active" value="1" class="form-check-input" id="tplActive" @checked($template->is_active)><label for="tplActive" class="form-check-label">Templat aktif</label></div>
|
||||
</div>
|
||||
<div class="modal-footer"><button class="btn btn-success">Simpan</button></div>
|
||||
</form>
|
||||
</div></div></div>
|
||||
@endsection
|
||||
18
resources/views/checklist/templates/create.blade.php
Normal file
18
resources/views/checklist/templates/create.blade.php
Normal file
@@ -0,0 +1,18 @@
|
||||
@extends('layouts.app')
|
||||
@section('title', 'Templat Baharu')
|
||||
@section('heading', 'Templat Senarai Semak')
|
||||
@section('page-title', 'Cipta Templat Baharu')
|
||||
@section('content')
|
||||
<div class="card"><div class="card-body">
|
||||
<form method="POST" action="{{ route('checklist-templates.store') }}">
|
||||
@csrf
|
||||
<div class="row g-3">
|
||||
<div class="col-md-8"><label class="form-label">Nama Templat <span class="text-danger">*</span></label><input type="text" name="name" value="{{ old('name') }}" class="form-control" required></div>
|
||||
<div class="col-md-4"><label class="form-label">Versi</label><input type="text" name="version" value="{{ old('version','1.0') }}" class="form-control"></div>
|
||||
<div class="col-12"><label class="form-label">Penerangan</label><textarea name="description" rows="2" class="form-control">{{ old('description') }}</textarea></div>
|
||||
<div class="col-12"><div class="form-check form-switch"><input type="checkbox" name="is_active" value="1" id="isActive" class="form-check-input" @checked(old('is_active'))><label for="isActive" class="form-check-label">Tetapkan sebagai templat aktif</label></div></div>
|
||||
</div>
|
||||
<div class="mt-4"><button class="btn btn-success"><i class="bi bi-save me-1"></i> Simpan & Bina</button><a href="{{ route('checklist-templates.index') }}" class="btn btn-link">Batal</a></div>
|
||||
</form>
|
||||
</div></div>
|
||||
@endsection
|
||||
35
resources/views/checklist/templates/index.blade.php
Normal file
35
resources/views/checklist/templates/index.blade.php
Normal file
@@ -0,0 +1,35 @@
|
||||
@extends('layouts.app')
|
||||
@section('title', 'Templat Senarai Semak')
|
||||
@section('heading', 'Templat Senarai Semak')
|
||||
@section('page-title', 'Templat Senarai Semak')
|
||||
@section('subtitle', 'Reka bentuk borang senarai semak secara dinamik — JPP boleh ubah seksyen, item & formula tanpa perubahan kod.')
|
||||
|
||||
@section('actions')
|
||||
<a href="{{ route('checklist-templates.create') }}" class="btn btn-success"><i class="bi bi-plus-lg me-1"></i> Templat Baharu</a>
|
||||
@endsection
|
||||
|
||||
@section('content')
|
||||
<div class="card"><div class="card-body p-0">
|
||||
<table class="table table-hover align-middle mb-0">
|
||||
<thead><tr><th class="ps-3">Nama Templat</th><th>Versi</th><th>Seksyen</th><th>Status</th><th></th></tr></thead>
|
||||
<tbody>
|
||||
@forelse ($templates as $t)
|
||||
<tr>
|
||||
<td class="ps-3 fw-semibold">{{ $t->name }}</td>
|
||||
<td>{{ $t->version }}</td>
|
||||
<td><span class="badge bg-light text-dark">{{ $t->sections_count }}</span></td>
|
||||
<td>@if($t->is_active)<span class="badge bg-success">Aktif</span>@else<span class="badge bg-secondary">Tidak Aktif</span>@endif</td>
|
||||
<td class="text-end pe-3">
|
||||
<a href="{{ route('checklist-templates.builder', $t) }}" class="btn btn-sm btn-primary"><i class="bi bi-tools me-1"></i> Bina</a>
|
||||
@unless($t->is_active)
|
||||
<form method="POST" action="{{ route('checklist-templates.activate', $t) }}" class="d-inline">@csrf<button class="btn btn-sm btn-outline-success">Aktifkan</button></form>
|
||||
@endunless
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr><td colspan="5" class="text-center text-muted py-3">Tiada templat.</td></tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div></div>
|
||||
@endsection
|
||||
63
resources/views/consultants/_form.blade.php
Normal file
63
resources/views/consultants/_form.blade.php
Normal file
@@ -0,0 +1,63 @@
|
||||
@csrf
|
||||
<div class="row g-3">
|
||||
<div class="col-md-8">
|
||||
<label class="form-label">Nama Perunding <span class="text-danger">*</span></label>
|
||||
<input type="text" name="nama_perunding" value="{{ old('nama_perunding', $consultant->nama_perunding) }}" class="form-control @error('nama_perunding') is-invalid @enderror" required>
|
||||
@error('nama_perunding')<div class="invalid-feedback">{{ $message }}</div>@enderror
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Status</label>
|
||||
<select name="aktif" class="form-select">
|
||||
<option value="1" @selected(old('aktif', $consultant->aktif ?? true))>Aktif</option>
|
||||
<option value="0" @selected(!old('aktif', $consultant->aktif ?? true))>Tidak Aktif</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">No. Pendaftaran Syarikat</label>
|
||||
<input type="text" name="no_pendaftaran_syarikat" value="{{ old('no_pendaftaran_syarikat', $consultant->no_pendaftaran_syarikat) }}" class="form-control">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Emel</label>
|
||||
<input type="email" name="emel" value="{{ old('emel', $consultant->emel) }}" class="form-control">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Telefon</label>
|
||||
<input type="text" name="telefon" value="{{ old('telefon', $consultant->telefon) }}" class="form-control">
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<label class="form-label">Alamat</label>
|
||||
<textarea name="alamat" rows="2" class="form-control">{{ old('alamat', $consultant->alamat) }}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (!$consultant->user_id)
|
||||
<hr class="my-4">
|
||||
<div class="form-check form-switch mb-3">
|
||||
<input type="checkbox" name="buat_akaun" value="1" id="buatAkaun" class="form-check-input" @checked(old('buat_akaun')) onchange="document.getElementById('akaunFields').classList.toggle('d-none', !this.checked)">
|
||||
<label for="buatAkaun" class="form-check-label fw-semibold">Cipta akaun log masuk untuk perunding ini</label>
|
||||
</div>
|
||||
<div id="akaunFields" class="row g-3 {{ old('buat_akaun') ? '' : 'd-none' }}">
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Nama Pengguna</label>
|
||||
<input type="text" name="user_name" value="{{ old('user_name', $consultant->nama_perunding) }}" class="form-control @error('user_name') is-invalid @enderror">
|
||||
@error('user_name')<div class="invalid-feedback">{{ $message }}</div>@enderror
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Emel Akaun</label>
|
||||
<input type="email" name="user_email" value="{{ old('user_email', $consultant->emel) }}" class="form-control @error('user_email') is-invalid @enderror">
|
||||
@error('user_email')<div class="invalid-feedback">{{ $message }}</div>@enderror
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Kata Laluan</label>
|
||||
<input type="password" name="user_password" class="form-control @error('user_password') is-invalid @enderror">
|
||||
@error('user_password')<div class="invalid-feedback">{{ $message }}</div>@enderror
|
||||
</div>
|
||||
</div>
|
||||
@else
|
||||
<div class="alert alert-light border mt-3 mb-0"><i class="bi bi-link-45deg"></i> Akaun log masuk telah dipautkan.</div>
|
||||
@endif
|
||||
|
||||
<div class="mt-4">
|
||||
<button type="submit" class="btn btn-success"><i class="bi bi-save me-1"></i> Simpan</button>
|
||||
<a href="{{ route('consultants.index') }}" class="btn btn-link">Batal</a>
|
||||
</div>
|
||||
12
resources/views/consultants/create.blade.php
Normal file
12
resources/views/consultants/create.blade.php
Normal file
@@ -0,0 +1,12 @@
|
||||
@extends('layouts.app')
|
||||
@section('title', 'Daftar Perunding')
|
||||
@section('heading', 'Perunding')
|
||||
@section('page-title', 'Daftar Perunding Baharu')
|
||||
|
||||
@section('content')
|
||||
<div class="card"><div class="card-body">
|
||||
<form method="POST" action="{{ route('consultants.store') }}">
|
||||
@include('consultants._form')
|
||||
</form>
|
||||
</div></div>
|
||||
@endsection
|
||||
13
resources/views/consultants/edit.blade.php
Normal file
13
resources/views/consultants/edit.blade.php
Normal file
@@ -0,0 +1,13 @@
|
||||
@extends('layouts.app')
|
||||
@section('title', 'Kemaskini Perunding')
|
||||
@section('heading', 'Perunding')
|
||||
@section('page-title', 'Kemaskini Perunding')
|
||||
|
||||
@section('content')
|
||||
<div class="card"><div class="card-body">
|
||||
<form method="POST" action="{{ route('consultants.update', $consultant) }}">
|
||||
@method('PUT')
|
||||
@include('consultants._form')
|
||||
</form>
|
||||
</div></div>
|
||||
@endsection
|
||||
55
resources/views/consultants/index.blade.php
Normal file
55
resources/views/consultants/index.blade.php
Normal file
@@ -0,0 +1,55 @@
|
||||
@extends('layouts.app')
|
||||
@section('title', 'Senarai Perunding')
|
||||
@section('heading', 'Perunding')
|
||||
@section('page-title', 'Senarai Perunding')
|
||||
|
||||
@section('actions')
|
||||
@can('create', App\Models\Consultant::class)
|
||||
<a href="{{ route('consultants.create') }}" class="btn btn-success"><i class="bi bi-plus-lg me-1"></i> Daftar Perunding</a>
|
||||
@endcan
|
||||
@endsection
|
||||
|
||||
@section('content')
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<form class="row g-2 mb-3">
|
||||
<div class="col-md-6">
|
||||
<input type="text" name="cari" value="{{ request('cari') }}" class="form-control" placeholder="Cari nama / no pendaftaran / emel...">
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<select name="status" class="form-select">
|
||||
<option value="">Semua status</option>
|
||||
<option value="aktif" @selected(request('status')==='aktif')>Aktif</option>
|
||||
<option value="tidak_aktif" @selected(request('status')==='tidak_aktif')>Tidak Aktif</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-3 d-grid"><button class="btn btn-outline-primary"><i class="bi bi-search me-1"></i> Cari</button></div>
|
||||
</form>
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover align-middle">
|
||||
<thead><tr><th>Nama Perunding</th><th>No. Pendaftaran</th><th>Emel</th><th>Pusat Data</th><th>Status</th><th></th></tr></thead>
|
||||
<tbody>
|
||||
@forelse ($consultants as $c)
|
||||
<tr>
|
||||
<td><a href="{{ route('consultants.show', $c) }}" class="text-decoration-none fw-semibold">{{ $c->nama_perunding }}</a></td>
|
||||
<td>{{ $c->no_pendaftaran_syarikat ?: '—' }}</td>
|
||||
<td>{{ $c->emel ?: '—' }}</td>
|
||||
<td><span class="badge bg-light text-dark">{{ $c->data_centres_count }}</span></td>
|
||||
<td>
|
||||
<span class="badge bg-{{ $c->aktif ? 'success' : 'secondary' }}">{{ $c->aktif ? 'Aktif' : 'Tidak Aktif' }}</span>
|
||||
</td>
|
||||
<td class="text-end">
|
||||
<a href="{{ route('consultants.edit', $c) }}" class="btn btn-sm btn-outline-secondary"><i class="bi bi-pencil"></i></a>
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr><td colspan="6" class="text-center text-muted py-3">Tiada perunding dijumpai.</td></tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{{ $consultants->links() }}
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
63
resources/views/consultants/show.blade.php
Normal file
63
resources/views/consultants/show.blade.php
Normal file
@@ -0,0 +1,63 @@
|
||||
@extends('layouts.app')
|
||||
@section('title', 'Maklumat Perunding')
|
||||
@section('heading', 'Perunding')
|
||||
@section('page-title', $consultant->nama_perunding)
|
||||
|
||||
@section('actions')
|
||||
@can('update', $consultant)
|
||||
<a href="{{ route('consultants.edit', $consultant) }}" class="btn btn-outline-secondary"><i class="bi bi-pencil me-1"></i> Kemaskini</a>
|
||||
@endcan
|
||||
@endsection
|
||||
|
||||
@section('content')
|
||||
<div class="row g-3">
|
||||
<div class="col-lg-4">
|
||||
<div class="card"><div class="card-body">
|
||||
<h6 class="text-muted">Maklumat Perunding</h6>
|
||||
<dl class="row mb-0 small">
|
||||
<dt class="col-5">Status</dt><dd class="col-7"><span class="badge bg-{{ $consultant->aktif ? 'success':'secondary' }}">{{ $consultant->aktif ? 'Aktif':'Tidak Aktif' }}</span></dd>
|
||||
<dt class="col-5">No. Pendaftaran</dt><dd class="col-7">{{ $consultant->no_pendaftaran_syarikat ?: '—' }}</dd>
|
||||
<dt class="col-5">Emel</dt><dd class="col-7">{{ $consultant->emel ?: '—' }}</dd>
|
||||
<dt class="col-5">Telefon</dt><dd class="col-7">{{ $consultant->telefon ?: '—' }}</dd>
|
||||
<dt class="col-5">Alamat</dt><dd class="col-7">{{ $consultant->alamat ?: '—' }}</dd>
|
||||
<dt class="col-5">Akaun</dt><dd class="col-7">{{ $consultant->user?->email ?? 'Tiada akaun' }}</dd>
|
||||
</dl>
|
||||
</div></div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-8">
|
||||
<div class="card mb-3"><div class="card-header bg-white fw-semibold">Pusat Data Ditugaskan (Aktif)</div>
|
||||
<div class="card-body p-0"><table class="table mb-0">
|
||||
<thead><tr><th class="ps-3">Pusat Data</th><th>Lokasi</th><th>Status</th></tr></thead>
|
||||
<tbody>
|
||||
@forelse ($consultant->dataCentres as $dc)
|
||||
<tr><td class="ps-3"><a href="{{ route('data-centres.show',$dc) }}" class="text-decoration-none">{{ $dc->nama_pusat_data }}</a></td><td>{{ $dc->lokasi_pusat_data }}</td><td><span class="badge bg-light text-dark">{{ $dc->status }}</span></td></tr>
|
||||
@empty
|
||||
<tr><td colspan="3" class="text-center text-muted py-3">Tiada pusat data aktif.</td></tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table></div>
|
||||
</div>
|
||||
|
||||
<div class="card"><div class="card-header bg-white fw-semibold">Sejarah Penugasan</div>
|
||||
<div class="card-body p-0"><table class="table mb-0 small">
|
||||
<thead><tr><th class="ps-3">Pusat Data</th><th>Mula</th><th>Tamat</th><th>Sebab</th><th>Oleh</th><th>Status</th></tr></thead>
|
||||
<tbody>
|
||||
@forelse ($consultant->assignments as $a)
|
||||
<tr>
|
||||
<td class="ps-3">{{ $a->dataCentre->nama_pusat_data ?? '—' }}</td>
|
||||
<td>{{ $a->start_date?->format('d/m/Y') }}</td>
|
||||
<td>{{ $a->end_date?->format('d/m/Y') ?: '—' }}</td>
|
||||
<td>{{ $a->reason ?: '—' }}</td>
|
||||
<td>{{ $a->assignedBy->name ?? '—' }}</td>
|
||||
<td><span class="badge bg-{{ $a->is_active ? 'success':'secondary' }}">{{ $a->is_active ? 'Aktif':'Tamat' }}</span></td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr><td colspan="6" class="text-center text-muted py-3">Tiada rekod.</td></tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
72
resources/views/dashboard/consultant.blade.php
Normal file
72
resources/views/dashboard/consultant.blade.php
Normal file
@@ -0,0 +1,72 @@
|
||||
@extends('layouts.app')
|
||||
@section('title', 'Papan Pemuka Perunding')
|
||||
@section('heading', 'Papan Pemuka')
|
||||
@section('page-title', 'Papan Pemuka Perunding')
|
||||
@section('subtitle', $consultant?->nama_perunding)
|
||||
|
||||
@section('actions')
|
||||
<a href="{{ route('submissions.create') }}" class="btn btn-success"><i class="bi bi-plus-lg me-1"></i> Mula Serahan Baharu</a>
|
||||
@endsection
|
||||
|
||||
@section('content')
|
||||
@unless ($consultant)
|
||||
<div class="alert alert-warning">Akaun anda belum dipautkan kepada profil perunding. Sila hubungi JPP.</div>
|
||||
@else
|
||||
<div class="row g-3 mb-4">
|
||||
@foreach ([
|
||||
['Pusat Data Ditugaskan', $dataCentres->count(), 'hdd-rack', 'primary'],
|
||||
['Serahan Tertunggak', $pendingCount, 'hourglass-split', 'warning'],
|
||||
['Permohonan Pembetulan', $correctionCount, 'pencil-square', 'danger'],
|
||||
['Jumlah Serahan', $submissions->count(), 'file-earmark-text', 'info'],
|
||||
] as [$label, $value, $icon, $color])
|
||||
<div class="col-6 col-xl-3">
|
||||
<div class="card stat-card h-100"><div class="card-body">
|
||||
<div class="d-flex justify-content-between align-items-start">
|
||||
<div><div class="text-muted small">{{ $label }}</div><div class="stat-value text-{{ $color }}">{{ $value }}</div></div>
|
||||
<i class="bi bi-{{ $icon }} fs-3 text-{{ $color }} opacity-50"></i>
|
||||
</div>
|
||||
</div></div>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
@if ($activeCycle)
|
||||
<div class="alert alert-info d-flex align-items-center">
|
||||
<i class="bi bi-info-circle me-2"></i>
|
||||
<div>Kitaran aktif: <strong>Pembaharuan {{ $activeCycle->renewal_year }} / Pelaporan {{ $activeCycle->reporting_year }}</strong>.
|
||||
Tarikh tutup serahan: <strong>{{ $activeCycle->cut_off_date?->format('d/m/Y') }}</strong>
|
||||
@if ($activeCycle->isOverdue()) <span class="badge bg-danger ms-1">Lampau Tempoh</span> @endif
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header bg-white fw-semibold">Serahan Saya</div>
|
||||
<div class="card-body p-0">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover mb-0 align-middle">
|
||||
<thead><tr><th class="ps-3">Pusat Data</th><th>Kitaran</th><th>Status</th><th>Dikemaskini</th><th></th></tr></thead>
|
||||
<tbody>
|
||||
@forelse ($submissions as $s)
|
||||
<tr>
|
||||
<td class="ps-3">{{ $s->dataCentre->nama_pusat_data }}</td>
|
||||
<td>Pembaharuan {{ $s->reportingCycle->renewal_year }} <span class="text-muted">/ Pelaporan {{ $s->reportingCycle->reporting_year }}</span></td>
|
||||
<td><span class="badge bg-{{ $s->statusBadgeClass() }}">{{ $s->statusLabel() }}</span></td>
|
||||
<td class="text-muted small">{{ $s->updated_at->format('d/m/Y H:i') }}</td>
|
||||
<td class="text-end pe-3">
|
||||
@if ($s->isEditableByConsultant())
|
||||
<a href="{{ route('submissions.edit', $s) }}" class="btn btn-sm btn-outline-primary">Isi</a>
|
||||
@endif
|
||||
<a href="{{ route('submissions.show', $s) }}" class="btn btn-sm btn-outline-secondary">Lihat</a>
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr><td colspan="5" class="text-center text-muted py-3">Tiada serahan lagi.</td></tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endunless
|
||||
@endsection
|
||||
96
resources/views/dashboard/jpp.blade.php
Normal file
96
resources/views/dashboard/jpp.blade.php
Normal file
@@ -0,0 +1,96 @@
|
||||
@extends('layouts.app')
|
||||
@section('title', 'Papan Pemuka JPP')
|
||||
@section('heading', 'Papan Pemuka')
|
||||
@section('page-title', 'Papan Pemuka JPP')
|
||||
@section('subtitle', $activeCycle ? "Kitaran aktif: Pembaharuan {$activeCycle->renewal_year} / Pelaporan {$activeCycle->reporting_year}" : 'Tiada kitaran aktif')
|
||||
|
||||
@section('content')
|
||||
<div class="row g-3 mb-4">
|
||||
@foreach ([
|
||||
['Pusat Data Aktif', $totalDataCentres, 'hdd-rack', 'primary'],
|
||||
['Perunding Aktif', $activeConsultants, 'people', 'info'],
|
||||
['Telah Serah (kitaran aktif)', $submittedCount, 'check2-circle', 'success'],
|
||||
['Belum Serah', $notSubmittedCount, 'hourglass-split', 'warning'],
|
||||
['Lampau Tempoh', $overdueCount, 'exclamation-octagon', 'danger'],
|
||||
] as [$label, $value, $icon, $color])
|
||||
<div class="col-6 col-md-4 col-xl">
|
||||
<div class="card stat-card h-100">
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-between align-items-start">
|
||||
<div>
|
||||
<div class="text-muted small">{{ $label }}</div>
|
||||
<div class="stat-value text-{{ $color }}">{{ number_format($value) }}</div>
|
||||
</div>
|
||||
<i class="bi bi-{{ $icon }} fs-3 text-{{ $color }} opacity-50"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
<div class="row g-3">
|
||||
<div class="col-lg-5">
|
||||
<div class="card h-100">
|
||||
<div class="card-header bg-white fw-semibold">Pecahan Status Serahan (Kitaran Aktif)</div>
|
||||
<div class="card-body">
|
||||
@forelse (\App\Models\Submission::STATUSES as $key => $label)
|
||||
@php($count = $statusBreakdown[$key] ?? 0)
|
||||
<div class="d-flex justify-content-between align-items-center py-1">
|
||||
<span><span class="badge bg-{{ (new \App\Models\Submission(['status'=>$key]))->statusBadgeClass() }}">{{ $label }}</span></span>
|
||||
<strong>{{ $count }}</strong>
|
||||
</div>
|
||||
@empty
|
||||
<p class="text-muted mb-0">Tiada data.</p>
|
||||
@endforelse
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-7">
|
||||
<div class="card h-100">
|
||||
<div class="card-header bg-white fw-semibold">Pusat Data Tertinggi Mengikut Anggaran Karbon (A)</div>
|
||||
<div class="card-body p-0">
|
||||
<table class="table table-sm mb-0">
|
||||
<thead><tr><th class="ps-3">Pusat Data</th><th class="text-end pe-3">Jumlah A (tCO2e)</th></tr></thead>
|
||||
<tbody>
|
||||
@forelse ($topDataCentres as $row)
|
||||
<tr><td class="ps-3">{{ $row->nama_pusat_data }}</td><td class="text-end pe-3">{{ number_format($row->jumlah, 2) }}</td></tr>
|
||||
@empty
|
||||
<tr><td colspan="2" class="text-center text-muted py-3">Tiada data karbon lagi.</td></tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-header bg-white fw-semibold">Jumlah Anggaran Karbon (A, B, C, D, E) Mengikut Tahun Pelaporan</div>
|
||||
<div class="card-body p-0">
|
||||
<div class="table-responsive">
|
||||
<table class="table mb-0">
|
||||
<thead><tr><th class="ps-3">Tahun Pelaporan</th><th>A — Pelepasan</th><th>B — Pengurangan</th><th>C — Baki</th><th>D — Bersih</th><th>E — % Pengurangan</th></tr></thead>
|
||||
<tbody>
|
||||
@forelse ($carbonByYear as $year => $tokens)
|
||||
@php($map = $tokens->keyBy('result_token'))
|
||||
<tr>
|
||||
<td class="ps-3 fw-semibold">{{ $year }}</td>
|
||||
<td>{{ number_format($map['A']->jumlah ?? 0, 2) }}</td>
|
||||
<td>{{ number_format($map['B']->jumlah ?? 0, 2) }}</td>
|
||||
<td>{{ number_format($map['C']->jumlah ?? 0, 2) }}</td>
|
||||
<td>{{ number_format($map['D']->jumlah ?? 0, 2) }}</td>
|
||||
<td>{{ number_format($map['E']->jumlah ?? 0, 2) }}%</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr><td colspan="6" class="text-center text-muted py-3">Tiada data karbon lagi.</td></tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
40
resources/views/data_centres/_form.blade.php
Normal file
40
resources/views/data_centres/_form.blade.php
Normal file
@@ -0,0 +1,40 @@
|
||||
@csrf
|
||||
<div class="row g-3">
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Nama Pusat Data <span class="text-danger">*</span></label>
|
||||
<input type="text" name="nama_pusat_data" value="{{ old('nama_pusat_data', $dataCentre->nama_pusat_data) }}" class="form-control @error('nama_pusat_data') is-invalid @enderror" required>
|
||||
@error('nama_pusat_data')<div class="invalid-feedback">{{ $message }}</div>@enderror
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Tajuk Permohonan</label>
|
||||
<input type="text" name="tajuk_permohonan" value="{{ old('tajuk_permohonan', $dataCentre->tajuk_permohonan) }}" class="form-control">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Lokasi Pusat Data</label>
|
||||
<input type="text" name="lokasi_pusat_data" value="{{ old('lokasi_pusat_data', $dataCentre->lokasi_pusat_data) }}" class="form-control">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Status</label>
|
||||
<select name="status" class="form-select">
|
||||
<option value="aktif" @selected(old('status', $dataCentre->status ?? 'aktif')==='aktif')>Aktif</option>
|
||||
<option value="tidak_aktif" @selected(old('status', $dataCentre->status)==='tidak_aktif')>Tidak Aktif</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Keluasan Tapak (ekar)</label>
|
||||
<input type="number" step="0.01" name="keluasan_tapak" value="{{ old('keluasan_tapak', $dataCentre->keluasan_tapak) }}" class="form-control">
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Kapasiti IT Load (MW)</label>
|
||||
<input type="number" step="0.01" name="it_load" value="{{ old('it_load', $dataCentre->it_load) }}" class="form-control">
|
||||
<div class="form-text">Kapasiti penjanaan elektrik komponen IT.</div>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<label class="form-label">Alamat</label>
|
||||
<textarea name="alamat" rows="2" class="form-control">{{ old('alamat', $dataCentre->alamat) }}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-4">
|
||||
<button type="submit" class="btn btn-success"><i class="bi bi-save me-1"></i> Simpan</button>
|
||||
<a href="{{ route('data-centres.index') }}" class="btn btn-link">Batal</a>
|
||||
</div>
|
||||
9
resources/views/data_centres/create.blade.php
Normal file
9
resources/views/data_centres/create.blade.php
Normal file
@@ -0,0 +1,9 @@
|
||||
@extends('layouts.app')
|
||||
@section('title', 'Daftar Pusat Data')
|
||||
@section('heading', 'Pusat Data')
|
||||
@section('page-title', 'Daftar Pusat Data Baharu')
|
||||
@section('content')
|
||||
<div class="card"><div class="card-body">
|
||||
<form method="POST" action="{{ route('data-centres.store') }}">@include('data_centres._form')</form>
|
||||
</div></div>
|
||||
@endsection
|
||||
9
resources/views/data_centres/edit.blade.php
Normal file
9
resources/views/data_centres/edit.blade.php
Normal file
@@ -0,0 +1,9 @@
|
||||
@extends('layouts.app')
|
||||
@section('title', 'Kemaskini Pusat Data')
|
||||
@section('heading', 'Pusat Data')
|
||||
@section('page-title', 'Kemaskini Pusat Data')
|
||||
@section('content')
|
||||
<div class="card"><div class="card-body">
|
||||
<form method="POST" action="{{ route('data-centres.update', $dataCentre) }}">@method('PUT')@include('data_centres._form')</form>
|
||||
</div></div>
|
||||
@endsection
|
||||
47
resources/views/data_centres/index.blade.php
Normal file
47
resources/views/data_centres/index.blade.php
Normal file
@@ -0,0 +1,47 @@
|
||||
@extends('layouts.app')
|
||||
@section('title', 'Senarai Pusat Data')
|
||||
@section('heading', 'Pusat Data')
|
||||
@section('page-title', 'Senarai Pusat Data')
|
||||
|
||||
@section('actions')
|
||||
@can('create', App\Models\DataCentre::class)
|
||||
<a href="{{ route('data-centres.create') }}" class="btn btn-success"><i class="bi bi-plus-lg me-1"></i> Daftar Pusat Data</a>
|
||||
@endcan
|
||||
@endsection
|
||||
|
||||
@section('content')
|
||||
<div class="card"><div class="card-body">
|
||||
<form class="row g-2 mb-3">
|
||||
<div class="col-md-6"><input type="text" name="cari" value="{{ request('cari') }}" class="form-control" placeholder="Cari nama / tajuk permohonan..."></div>
|
||||
<div class="col-md-3">
|
||||
<select name="status" class="form-select">
|
||||
<option value="">Semua status</option>
|
||||
<option value="aktif" @selected(request('status')==='aktif')>Aktif</option>
|
||||
<option value="tidak_aktif" @selected(request('status')==='tidak_aktif')>Tidak Aktif</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-3 d-grid"><button class="btn btn-outline-primary"><i class="bi bi-search me-1"></i> Cari</button></div>
|
||||
</form>
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover align-middle">
|
||||
<thead><tr><th>Nama Pusat Data</th><th>Lokasi</th><th>IT Load (MW)</th><th>Perunding Aktif</th><th>Status</th><th></th></tr></thead>
|
||||
<tbody>
|
||||
@forelse ($dataCentres as $dc)
|
||||
<tr>
|
||||
<td><a href="{{ route('data-centres.show', $dc) }}" class="text-decoration-none fw-semibold">{{ $dc->nama_pusat_data }}</a><div class="small text-muted">{{ $dc->tajuk_permohonan }}</div></td>
|
||||
<td>{{ $dc->lokasi_pusat_data ?: '—' }}</td>
|
||||
<td>{{ $dc->it_load ? number_format($dc->it_load,2) : '—' }}</td>
|
||||
<td>{{ $dc->currentConsultant?->nama_perunding ?? '—' }}</td>
|
||||
<td><span class="badge bg-{{ $dc->status==='aktif'?'success':'secondary' }}">{{ $dc->status==='aktif'?'Aktif':'Tidak Aktif' }}</span></td>
|
||||
<td class="text-end"><a href="{{ route('data-centres.show', $dc) }}" class="btn btn-sm btn-outline-secondary">Lihat</a></td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr><td colspan="6" class="text-center text-muted py-3">Tiada pusat data dijumpai.</td></tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{{ $dataCentres->links() }}
|
||||
</div></div>
|
||||
@endsection
|
||||
93
resources/views/data_centres/show.blade.php
Normal file
93
resources/views/data_centres/show.blade.php
Normal file
@@ -0,0 +1,93 @@
|
||||
@extends('layouts.app')
|
||||
@section('title', 'Maklumat Pusat Data')
|
||||
@section('heading', 'Pusat Data')
|
||||
@section('page-title', $dataCentre->nama_pusat_data)
|
||||
@section('subtitle', $dataCentre->tajuk_permohonan)
|
||||
|
||||
@section('actions')
|
||||
@can('update', $dataCentre)
|
||||
<a href="{{ route('data-centres.edit', $dataCentre) }}" class="btn btn-outline-secondary"><i class="bi bi-pencil me-1"></i> Kemaskini</a>
|
||||
@endcan
|
||||
@endsection
|
||||
|
||||
@section('content')
|
||||
<div class="row g-3">
|
||||
<div class="col-lg-5">
|
||||
<div class="card mb-3"><div class="card-body">
|
||||
<h6 class="text-muted">Maklumat Pusat Data</h6>
|
||||
<dl class="row mb-0 small">
|
||||
<dt class="col-5">Status</dt><dd class="col-7"><span class="badge bg-{{ $dataCentre->status==='aktif'?'success':'secondary' }}">{{ $dataCentre->status }}</span></dd>
|
||||
<dt class="col-5">Lokasi</dt><dd class="col-7">{{ $dataCentre->lokasi_pusat_data ?: '—' }}</dd>
|
||||
<dt class="col-5">Alamat</dt><dd class="col-7">{{ $dataCentre->alamat ?: '—' }}</dd>
|
||||
<dt class="col-5">Keluasan Tapak</dt><dd class="col-7">{{ $dataCentre->keluasan_tapak ? number_format($dataCentre->keluasan_tapak,2).' ekar' : '—' }}</dd>
|
||||
<dt class="col-5">IT Load</dt><dd class="col-7">{{ $dataCentre->it_load ? number_format($dataCentre->it_load,2).' MW' : '—' }}</dd>
|
||||
<dt class="col-5">Perunding Aktif</dt><dd class="col-7">{{ $dataCentre->currentConsultant?->nama_perunding ?? 'Tiada' }}</dd>
|
||||
</dl>
|
||||
</div></div>
|
||||
|
||||
@can('assignConsultant', $dataCentre)
|
||||
<div class="card"><div class="card-header bg-white fw-semibold">Tukar / Tugaskan Perunding</div><div class="card-body">
|
||||
<form method="POST" action="{{ route('data-centres.assign', $dataCentre) }}">
|
||||
@csrf
|
||||
<div class="mb-2">
|
||||
<label class="form-label">Perunding</label>
|
||||
<select name="consultant_id" class="form-select" required>
|
||||
<option value="">— Pilih perunding —</option>
|
||||
@foreach ($consultants as $c)
|
||||
<option value="{{ $c->id }}">{{ $c->nama_perunding }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-2"><label class="form-label">Tarikh Mula</label><input type="date" name="start_date" class="form-control" value="{{ now()->toDateString() }}"></div>
|
||||
<div class="mb-2"><label class="form-label">Sebab Perubahan</label><textarea name="reason" rows="2" class="form-control"></textarea></div>
|
||||
<button class="btn btn-success btn-sm w-100" data-confirm="Penugasan perunding aktif sedia ada akan ditamatkan. Teruskan?"><i class="bi bi-person-check me-1"></i> Tugaskan</button>
|
||||
</form>
|
||||
@if ($dataCentre->current_consultant_id)
|
||||
<form method="POST" action="{{ route('data-centres.end-assignment', $dataCentre) }}" class="mt-2">
|
||||
@csrf
|
||||
<button class="btn btn-outline-danger btn-sm w-100" data-confirm="Tamatkan penugasan perunding aktif tanpa pengganti?"><i class="bi bi-person-x me-1"></i> Tamatkan Penugasan Aktif</button>
|
||||
</form>
|
||||
@endif
|
||||
</div></div>
|
||||
@endcan
|
||||
</div>
|
||||
|
||||
<div class="col-lg-7">
|
||||
<div class="card mb-3"><div class="card-header bg-white fw-semibold">Sejarah Penugasan Perunding</div>
|
||||
<div class="card-body p-0"><table class="table mb-0 small">
|
||||
<thead><tr><th class="ps-3">Perunding</th><th>Mula</th><th>Tamat</th><th>Sebab</th><th>Status</th></tr></thead>
|
||||
<tbody>
|
||||
@forelse ($dataCentre->assignments as $a)
|
||||
<tr>
|
||||
<td class="ps-3">{{ $a->consultant->nama_perunding ?? '—' }}</td>
|
||||
<td>{{ $a->start_date?->format('d/m/Y') }}</td>
|
||||
<td>{{ $a->end_date?->format('d/m/Y') ?: '—' }}</td>
|
||||
<td>{{ $a->reason ?: '—' }}</td>
|
||||
<td><span class="badge bg-{{ $a->is_active?'success':'secondary' }}">{{ $a->is_active?'Aktif':'Tamat' }}</span></td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr><td colspan="5" class="text-center text-muted py-3">Tiada rekod penugasan.</td></tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table></div>
|
||||
</div>
|
||||
|
||||
<div class="card"><div class="card-header bg-white fw-semibold">Serahan Pusat Data Ini</div>
|
||||
<div class="card-body p-0"><table class="table mb-0 small">
|
||||
<thead><tr><th class="ps-3">Kitaran</th><th>Status</th><th></th></tr></thead>
|
||||
<tbody>
|
||||
@forelse ($dataCentre->submissions as $s)
|
||||
<tr>
|
||||
<td class="ps-3">Pembaharuan {{ $s->reportingCycle->renewal_year }} / Pelaporan {{ $s->reportingCycle->reporting_year }}</td>
|
||||
<td><span class="badge bg-{{ $s->statusBadgeClass() }}">{{ $s->statusLabel() }}</span></td>
|
||||
<td class="text-end pe-3"><a href="{{ route('submissions.show',$s) }}" class="btn btn-sm btn-outline-secondary">Lihat</a></td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr><td colspan="3" class="text-center text-muted py-3">Tiada serahan.</td></tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
75
resources/views/layouts/app.blade.php
Normal file
75
resources/views/layouts/app.blade.php
Normal file
@@ -0,0 +1,75 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ms">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="csrf-token" content="{{ csrf_token() }}">
|
||||
<title>@yield('title', 'Pemantauan Kemampanan Pusat Data') — MBIP</title>
|
||||
@vite(['resources/sass/app.scss', 'resources/js/app.js'])
|
||||
</head>
|
||||
<body>
|
||||
<div class="d-flex">
|
||||
{{-- Sidebar --}}
|
||||
<nav class="sidebar text-white p-3 d-none d-lg-block" style="width: 270px;">
|
||||
@include('layouts.sidebar')
|
||||
</nav>
|
||||
|
||||
{{-- Offcanvas sidebar untuk mobil --}}
|
||||
<div class="offcanvas offcanvas-start sidebar text-white" tabindex="-1" id="sidebarMobile">
|
||||
<div class="offcanvas-header">
|
||||
<h6 class="offcanvas-title">Menu</h6>
|
||||
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="offcanvas"></button>
|
||||
</div>
|
||||
<div class="offcanvas-body p-3">
|
||||
@include('layouts.sidebar')
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex-grow-1 min-vw-0">
|
||||
{{-- Bar atas --}}
|
||||
<header class="bg-white border-bottom px-3 py-2 d-flex align-items-center justify-content-between sticky-top">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<button class="btn btn-outline-secondary btn-sm d-lg-none" type="button" data-bs-toggle="offcanvas" data-bs-target="#sidebarMobile">
|
||||
<i class="bi bi-list"></i>
|
||||
</button>
|
||||
<span class="fw-semibold text-truncate">@yield('heading', 'Papan Pemuka')</span>
|
||||
</div>
|
||||
<div class="dropdown">
|
||||
<a href="#" class="d-flex align-items-center text-decoration-none dropdown-toggle text-dark" data-bs-toggle="dropdown">
|
||||
<i class="bi bi-person-circle fs-5 me-2"></i>
|
||||
<span class="d-none d-sm-inline">{{ auth()->user()->name }}</span>
|
||||
</a>
|
||||
<ul class="dropdown-menu dropdown-menu-end">
|
||||
<li class="dropdown-header">
|
||||
{{ auth()->user()->email }}<br>
|
||||
<small class="text-muted">{{ auth()->user()->getRoleNames()->join(', ') }}</small>
|
||||
</li>
|
||||
<li><hr class="dropdown-divider"></li>
|
||||
<li>
|
||||
<form method="POST" action="{{ route('logout') }}">
|
||||
@csrf
|
||||
<button class="dropdown-item text-danger" type="submit"><i class="bi bi-box-arrow-right me-2"></i>Log Keluar</button>
|
||||
</form>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="p-3 p-md-4">
|
||||
@include('layouts.flash')
|
||||
|
||||
<div class="d-flex flex-wrap justify-content-between align-items-center mb-3 gap-2">
|
||||
<div>
|
||||
<h4 class="mb-0">@yield('page-title', View::yieldContent('heading'))</h4>
|
||||
@hasSection('subtitle')<small class="text-muted">@yield('subtitle')</small>@endif
|
||||
</div>
|
||||
<div>@yield('actions')</div>
|
||||
</div>
|
||||
|
||||
@yield('content')
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
@stack('scripts')
|
||||
</body>
|
||||
</html>
|
||||
25
resources/views/layouts/flash.blade.php
Normal file
25
resources/views/layouts/flash.blade.php
Normal file
@@ -0,0 +1,25 @@
|
||||
@if (session('success'))
|
||||
<div class="alert alert-success alert-dismissible alert-auto-dismiss fade show" role="alert">
|
||||
<i class="bi bi-check-circle me-1"></i> {{ session('success') }}
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if (session('error'))
|
||||
<div class="alert alert-danger alert-dismissible fade show" role="alert">
|
||||
<i class="bi bi-exclamation-triangle me-1"></i> {{ session('error') }}
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if ($errors->any())
|
||||
<div class="alert alert-danger alert-dismissible fade show" role="alert">
|
||||
<strong><i class="bi bi-exclamation-circle me-1"></i> Sila betulkan ralat berikut:</strong>
|
||||
<ul class="mb-0 mt-1">
|
||||
@foreach ($errors->all() as $error)
|
||||
<li>{{ $error }}</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||||
</div>
|
||||
@endif
|
||||
98
resources/views/layouts/sidebar.blade.php
Normal file
98
resources/views/layouts/sidebar.blade.php
Normal file
@@ -0,0 +1,98 @@
|
||||
@php($u = auth()->user())
|
||||
<a href="{{ route('dashboard') }}" class="sidebar-brand text-decoration-none d-block mb-4">
|
||||
<i class="bi bi-buildings me-1"></i> MBIP
|
||||
<div class="small fw-normal opacity-75" style="font-size:.72rem">Pemantauan Kemampanan Pusat Data</div>
|
||||
</a>
|
||||
|
||||
<ul class="nav nav-pills flex-column">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {{ request()->routeIs('dashboard') ? 'active' : '' }}" href="{{ route('dashboard') }}">
|
||||
<i class="bi bi-speedometer2"></i> Papan Pemuka
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {{ request()->routeIs('submissions.*') ? 'active' : '' }}" href="{{ route('submissions.index') }}">
|
||||
<i class="bi bi-file-earmark-text"></i> Serahan
|
||||
</a>
|
||||
</li>
|
||||
|
||||
@can('pusat_data.urus')
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {{ request()->routeIs('data-centres.*') ? 'active' : '' }}" href="{{ route('data-centres.index') }}">
|
||||
<i class="bi bi-hdd-rack"></i> Pusat Data
|
||||
</a>
|
||||
</li>
|
||||
@elsecan('serahan.urus_sendiri')
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {{ request()->routeIs('data-centres.*') ? 'active' : '' }}" href="{{ route('data-centres.index') }}">
|
||||
<i class="bi bi-hdd-rack"></i> Pusat Data Saya
|
||||
</a>
|
||||
</li>
|
||||
@endcan
|
||||
|
||||
@can('perunding.urus')
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {{ request()->routeIs('consultants.*') ? 'active' : '' }}" href="{{ route('consultants.index') }}">
|
||||
<i class="bi bi-people"></i> Perunding
|
||||
</a>
|
||||
</li>
|
||||
@endcan
|
||||
|
||||
@can('kitaran.urus')
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {{ request()->routeIs('reporting-cycles.*') ? 'active' : '' }}" href="{{ route('reporting-cycles.index') }}">
|
||||
<i class="bi bi-arrow-repeat"></i> Kitaran Pelaporan
|
||||
</a>
|
||||
</li>
|
||||
@endcan
|
||||
|
||||
@can('templat.urus')
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {{ request()->routeIs('checklist-templates.*') ? 'active' : '' }}" href="{{ route('checklist-templates.index') }}">
|
||||
<i class="bi bi-ui-checks"></i> Templat Senarai Semak
|
||||
</a>
|
||||
</li>
|
||||
@endcan
|
||||
|
||||
@can('peringatan.lihat')
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {{ request()->routeIs('reminders.*') ? 'active' : '' }}" href="{{ route('reminders.index') }}">
|
||||
<i class="bi bi-bell"></i> Peringatan / Belum Serah
|
||||
</a>
|
||||
</li>
|
||||
@endcan
|
||||
|
||||
@can('laporan.lihat')
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {{ request()->routeIs('reports.*') ? 'active' : '' }}" href="{{ route('reports.index') }}">
|
||||
<i class="bi bi-bar-chart"></i> Laporan & Statistik
|
||||
</a>
|
||||
</li>
|
||||
@endcan
|
||||
|
||||
@canany(['pengguna.urus', 'tetapan.urus'])
|
||||
<li class="nav-item mt-3"><span class="text-uppercase small opacity-50 px-3">Pentadbiran</span></li>
|
||||
@endcanany
|
||||
|
||||
@can('pengguna.urus')
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {{ request()->routeIs('users.*') ? 'active' : '' }}" href="{{ route('users.index') }}">
|
||||
<i class="bi bi-person-gear"></i> Pengguna
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {{ request()->routeIs('audit.*') ? 'active' : '' }}" href="{{ route('audit.index') }}">
|
||||
<i class="bi bi-clock-history"></i> Log Audit
|
||||
</a>
|
||||
</li>
|
||||
@endcan
|
||||
|
||||
@can('tetapan.urus')
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {{ request()->routeIs('settings.*') ? 'active' : '' }}" href="{{ route('settings.edit') }}">
|
||||
<i class="bi bi-gear"></i> Tetapan
|
||||
</a>
|
||||
</li>
|
||||
@endcan
|
||||
</ul>
|
||||
60
resources/views/reminders/index.blade.php
Normal file
60
resources/views/reminders/index.blade.php
Normal file
@@ -0,0 +1,60 @@
|
||||
@extends('layouts.app')
|
||||
@section('title', 'Senarai Peringatan')
|
||||
@section('heading', 'Peringatan')
|
||||
@section('page-title', 'Senarai Peringatan / Belum Serah')
|
||||
@section('subtitle', 'Pusat data aktif dengan perunding aktif yang belum menghantar serahan untuk kitaran dipilih.')
|
||||
|
||||
@section('content')
|
||||
<div class="card mb-3"><div class="card-body">
|
||||
<form class="row g-2">
|
||||
<div class="col-md-4">
|
||||
<select name="cycle" class="form-select">
|
||||
@foreach ($cycles as $c)<option value="{{ $c->id }}" @selected(optional($cycle)->id==$c->id)>Pembaharuan {{ $c->renewal_year }} / Pelaporan {{ $c->reporting_year }}</option>@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<select name="consultant" class="form-select">
|
||||
<option value="">Semua perunding</option>
|
||||
@foreach ($consultants as $c)<option value="{{ $c->id }}" @selected(request('consultant')==$c->id)>{{ $c->nama_perunding }}</option>@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="form-check mt-2"><input type="checkbox" name="overdue" value="1" class="form-check-input" id="ovd" @checked(request('overdue'))><label for="ovd" class="form-check-label">Lampau tempoh sahaja</label></div>
|
||||
</div>
|
||||
<div class="col-md-2 d-grid"><button class="btn btn-outline-primary"><i class="bi bi-funnel me-1"></i> Tapis</button></div>
|
||||
</form>
|
||||
</div></div>
|
||||
|
||||
@if ($cycle)
|
||||
<div class="alert alert-{{ $cycle->isOverdue() ? 'danger' : 'info' }} d-flex justify-content-between">
|
||||
<span>Kitaran: <strong>Pelaporan {{ $cycle->reporting_year }}</strong> • Tarikh tutup: <strong>{{ $cycle->cut_off_date?->format('d/m/Y') }}</strong></span>
|
||||
<span>{{ $rows->count() }} pusat data belum serah @if($cycle->isOverdue())<span class="badge bg-danger">Lampau Tempoh</span>@endif</span>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="card"><div class="card-body p-0">
|
||||
<table class="table table-hover align-middle mb-0">
|
||||
<thead><tr><th class="ps-3">Pusat Data</th><th>Perunding</th><th>Emel</th><th>Status</th><th></th></tr></thead>
|
||||
<tbody>
|
||||
@forelse ($rows as $row)
|
||||
<tr class="{{ $row->is_overdue ? 'table-danger' : '' }}">
|
||||
<td class="ps-3 fw-semibold">{{ $row->data_centre->nama_pusat_data }}</td>
|
||||
<td>{{ $row->consultant?->nama_perunding ?? '—' }}</td>
|
||||
<td class="small">{{ $row->consultant?->emel ?? '—' }}</td>
|
||||
<td>
|
||||
@if($row->status==='belum_mula')<span class="badge bg-secondary">Belum Mula</span>
|
||||
@else<span class="badge bg-warning text-dark">Draf</span>@endif
|
||||
@if($row->is_overdue)<span class="badge bg-danger">Lampau</span>@endif
|
||||
</td>
|
||||
<td class="text-end pe-3">
|
||||
@if($row->submission)<a href="{{ route('submissions.show', $row->submission) }}" class="btn btn-sm btn-outline-secondary">Lihat</a>@endif
|
||||
<a href="{{ route('data-centres.show', $row->data_centre) }}" class="btn btn-sm btn-outline-secondary">Pusat Data</a>
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr><td colspan="5" class="text-center text-muted py-3">Tiada pusat data tertunggak — semua telah menghantar. 🎉</td></tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div></div>
|
||||
@endsection
|
||||
51
resources/views/reporting_cycles/_form.blade.php
Normal file
51
resources/views/reporting_cycles/_form.blade.php
Normal file
@@ -0,0 +1,51 @@
|
||||
@csrf
|
||||
<div class="row g-3">
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Tahun Pembaharuan <span class="text-danger">*</span></label>
|
||||
<input type="number" name="renewal_year" id="renewalYear" value="{{ old('renewal_year', $cycle->renewal_year) }}" class="form-control @error('renewal_year') is-invalid @enderror" required>
|
||||
@error('renewal_year')<div class="invalid-feedback">{{ $message }}</div>@enderror
|
||||
<div class="form-text">Tahun pelaporan (auto): <strong id="reportingYearPreview">{{ $cycle->renewal_year ? $cycle->renewal_year - 2 : '—' }}</strong></div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Tarikh Tutup Serahan</label>
|
||||
<input type="date" name="cut_off_date" value="{{ old('cut_off_date', $cycle->cut_off_date?->toDateString()) }}" class="form-control">
|
||||
<div class="form-text">Kosongkan untuk guna tetapan lalai JPP.</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Tempoh Lesen (tahun) <span class="text-danger">*</span></label>
|
||||
<input type="number" name="licence_period_year" value="{{ old('licence_period_year', $cycle->licence_period_year ?? 1) }}" class="form-control" required min="1">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Templat Senarai Semak</label>
|
||||
<select name="checklist_template_id" class="form-select">
|
||||
<option value="">— Guna templat aktif —</option>
|
||||
@foreach ($templates as $t)
|
||||
<option value="{{ $t->id }}" @selected(old('checklist_template_id', $cycle->checklist_template_id)==$t->id)>{{ $t->name }} (v{{ $t->version }}) {{ $t->is_active ? '★' : '' }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Status Kitaran</label>
|
||||
<select name="status" class="form-select">
|
||||
<option value="aktif" @selected(old('status', $cycle->status ?? 'aktif')==='aktif')>Aktif</option>
|
||||
<option value="tutup" @selected(old('status', $cycle->status)==='tutup')>Tutup</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<label class="form-label">Catatan</label>
|
||||
<textarea name="catatan" rows="2" class="form-control">{{ old('catatan', $cycle->catatan) }}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-4">
|
||||
<button type="submit" class="btn btn-success"><i class="bi bi-save me-1"></i> Simpan</button>
|
||||
<a href="{{ route('reporting-cycles.index') }}" class="btn btn-link">Batal</a>
|
||||
</div>
|
||||
|
||||
@push('scripts')
|
||||
<script>
|
||||
document.getElementById('renewalYear')?.addEventListener('input', function () {
|
||||
const v = parseInt(this.value, 10);
|
||||
document.getElementById('reportingYearPreview').textContent = isNaN(v) ? '—' : (v - 2);
|
||||
});
|
||||
</script>
|
||||
@endpush
|
||||
9
resources/views/reporting_cycles/create.blade.php
Normal file
9
resources/views/reporting_cycles/create.blade.php
Normal file
@@ -0,0 +1,9 @@
|
||||
@extends('layouts.app')
|
||||
@section('title', 'Cipta Kitaran')
|
||||
@section('heading', 'Kitaran Pelaporan')
|
||||
@section('page-title', 'Cipta Kitaran Pelaporan')
|
||||
@section('content')
|
||||
<div class="card"><div class="card-body">
|
||||
<form method="POST" action="{{ route('reporting-cycles.store') }}">@include('reporting_cycles._form')</form>
|
||||
</div></div>
|
||||
@endsection
|
||||
14
resources/views/reporting_cycles/edit.blade.php
Normal file
14
resources/views/reporting_cycles/edit.blade.php
Normal file
@@ -0,0 +1,14 @@
|
||||
@extends('layouts.app')
|
||||
@section('title', 'Kemaskini Kitaran')
|
||||
@section('heading', 'Kitaran Pelaporan')
|
||||
@section('page-title', 'Kemaskini Kitaran Pelaporan')
|
||||
@section('content')
|
||||
<div class="card"><div class="card-body">
|
||||
<form method="POST" action="{{ route('reporting-cycles.update', $cycle) }}">@method('PUT')@include('reporting_cycles._form')</form>
|
||||
</div></div>
|
||||
|
||||
<form method="POST" action="{{ route('reporting-cycles.destroy', $cycle) }}" class="mt-3" data-confirm="Padam kitaran ini?">
|
||||
@csrf @method('DELETE')
|
||||
<button class="btn btn-outline-danger btn-sm"><i class="bi bi-trash me-1"></i> Padam Kitaran</button>
|
||||
</form>
|
||||
@endsection
|
||||
40
resources/views/reporting_cycles/index.blade.php
Normal file
40
resources/views/reporting_cycles/index.blade.php
Normal file
@@ -0,0 +1,40 @@
|
||||
@extends('layouts.app')
|
||||
@section('title', 'Kitaran Pelaporan')
|
||||
@section('heading', 'Kitaran Pelaporan')
|
||||
@section('page-title', 'Kitaran Pelaporan / Pembaharuan Lesen')
|
||||
|
||||
@section('actions')
|
||||
<a href="{{ route('reporting-cycles.create') }}" class="btn btn-success"><i class="bi bi-plus-lg me-1"></i> Cipta Kitaran</a>
|
||||
@endsection
|
||||
|
||||
@section('content')
|
||||
<div class="alert alert-light border small">
|
||||
<i class="bi bi-info-circle me-1"></i> Peraturan: <strong>Tahun pelaporan = Tahun pembaharuan − 2</strong>. Contoh: pembaharuan 2028 ⇒ pelaporan 2026 (data Jan–Dis 2026). Pembaharuan lesen dibuat pada/sebelum tarikh tutup (lalai 1 Disember).
|
||||
</div>
|
||||
<div class="card"><div class="card-body p-0">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover align-middle mb-0">
|
||||
<thead><tr><th class="ps-3">Tahun Pembaharuan</th><th>Tahun Pelaporan</th><th>Tempoh Pelaporan</th><th>Tarikh Tutup</th><th>Tempoh Lesen</th><th>Serahan</th><th>Status</th><th></th></tr></thead>
|
||||
<tbody>
|
||||
@forelse ($cycles as $c)
|
||||
<tr>
|
||||
<td class="ps-3 fw-semibold">{{ $c->renewal_year }}</td>
|
||||
<td>{{ $c->reporting_year }}</td>
|
||||
<td class="small">{{ $c->period_start?->format('d/m/Y') }} – {{ $c->period_end?->format('d/m/Y') }}</td>
|
||||
<td>{{ $c->cut_off_date?->format('d/m/Y') }} @if($c->isOverdue())<span class="badge bg-danger">Lampau</span>@endif</td>
|
||||
<td>{{ $c->licence_period_year }} tahun</td>
|
||||
<td><span class="badge bg-light text-dark">{{ $c->submissions_count }}</span></td>
|
||||
<td><span class="badge bg-{{ $c->status==='aktif'?'success':'secondary' }}">{{ $c->status==='aktif'?'Aktif':'Tutup' }}</span></td>
|
||||
<td class="text-end pe-3">
|
||||
<a href="{{ route('reporting-cycles.edit', $c) }}" class="btn btn-sm btn-outline-secondary"><i class="bi bi-pencil"></i></a>
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr><td colspan="8" class="text-center text-muted py-3">Tiada kitaran lagi.</td></tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div></div>
|
||||
<div class="mt-2">{{ $cycles->links() }}</div>
|
||||
@endsection
|
||||
69
resources/views/reports/index.blade.php
Normal file
69
resources/views/reports/index.blade.php
Normal file
@@ -0,0 +1,69 @@
|
||||
@extends('layouts.app')
|
||||
@section('title', 'Laporan & Statistik')
|
||||
@section('heading', 'Laporan')
|
||||
@section('page-title', 'Laporan & Statistik Karbon')
|
||||
|
||||
@section('content')
|
||||
<div class="card mb-3"><div class="card-body">
|
||||
<form class="row g-2">
|
||||
<div class="col-md-4">
|
||||
<select name="cycle" class="form-select">
|
||||
<option value="">Semua kitaran</option>
|
||||
@foreach ($cycles as $c)<option value="{{ $c->id }}" @selected($cycleId==$c->id)>Pembaharuan {{ $c->renewal_year }} / Pelaporan {{ $c->reporting_year }}</option>@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-2 d-grid"><button class="btn btn-outline-primary">Tapis</button></div>
|
||||
</form>
|
||||
</div></div>
|
||||
|
||||
<div class="row g-3 mb-3">
|
||||
<div class="col-lg-6">
|
||||
<div class="card h-100"><div class="card-header bg-white fw-semibold">Trend Jumlah Pelepasan (A) Mengikut Tahun Pelaporan</div>
|
||||
<div class="card-body p-0"><table class="table table-sm mb-0">
|
||||
<thead><tr><th class="ps-3">Tahun</th><th class="text-end pe-3">Jumlah A (tCO2e)</th></tr></thead>
|
||||
<tbody>
|
||||
@forelse ($trend as $year => $jumlah)
|
||||
<tr><td class="ps-3">{{ $year }}</td><td class="text-end pe-3">{{ number_format($jumlah,2) }}</td></tr>
|
||||
@empty
|
||||
<tr><td colspan="2" class="text-center text-muted py-3">Tiada data.</td></tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<div class="card h-100"><div class="card-header bg-white fw-semibold">Pecahan Status Serahan</div>
|
||||
<div class="card-body">
|
||||
@foreach (App\Models\Submission::STATUSES as $k => $v)
|
||||
<div class="d-flex justify-content-between py-1"><span class="badge bg-{{ (new App\Models\Submission(['status'=>$k]))->statusBadgeClass() }}">{{ $v }}</span><strong>{{ $statusBreakdown[$k] ?? 0 }}</strong></div>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card"><div class="card-header bg-white fw-semibold">Hasil Karbon Mengikut Pusat Data & Tahun Pelaporan</div>
|
||||
<div class="card-body p-0"><div class="table-responsive">
|
||||
<table class="table table-sm mb-0">
|
||||
<thead><tr><th class="ps-3">Pusat Data</th><th>Tahun</th><th>A</th><th>B</th><th>C</th><th>D</th><th>E (%)</th></tr></thead>
|
||||
<tbody>
|
||||
@forelse ($rows as $key => $group)
|
||||
@php($map = $group->keyBy('result_token'))
|
||||
@php([$nama, $tahun] = explode('|', $key))
|
||||
<tr>
|
||||
<td class="ps-3">{{ $nama }}</td>
|
||||
<td>{{ $tahun }}</td>
|
||||
<td>{{ number_format(optional($map->get('A'))->value ?? 0, 2) }}</td>
|
||||
<td>{{ number_format(optional($map->get('B'))->value ?? 0, 2) }}</td>
|
||||
<td>{{ number_format(optional($map->get('C'))->value ?? 0, 2) }}</td>
|
||||
<td>{{ number_format(optional($map->get('D'))->value ?? 0, 2) }}</td>
|
||||
<td>{{ number_format(optional($map->get('E'))->value ?? 0, 2) }}</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr><td colspan="7" class="text-center text-muted py-3">Tiada data karbon.</td></tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div></div>
|
||||
</div>
|
||||
@endsection
|
||||
34
resources/views/settings/edit.blade.php
Normal file
34
resources/views/settings/edit.blade.php
Normal file
@@ -0,0 +1,34 @@
|
||||
@extends('layouts.app')
|
||||
@section('title', 'Tetapan')
|
||||
@section('heading', 'Tetapan')
|
||||
@section('page-title', 'Tetapan Aplikasi')
|
||||
|
||||
@section('content')
|
||||
<form method="POST" action="{{ route('settings.update') }}">
|
||||
@csrf @method('PUT')
|
||||
@foreach ($settings as $group => $items)
|
||||
<div class="card mb-3">
|
||||
<div class="card-header bg-white fw-semibold text-capitalize">{{ $group }}</div>
|
||||
<div class="card-body">
|
||||
@foreach ($items as $s)
|
||||
<div class="row mb-3 align-items-center">
|
||||
<div class="col-md-4"><label class="form-label mb-0">{{ $s->label ?? $s->key }}</label><div class="form-text">{{ $s->description }}</div></div>
|
||||
<div class="col-md-8">
|
||||
@if($s->type === 'boolean')
|
||||
<div class="form-check form-switch"><input type="checkbox" name="settings[{{ $s->key }}]" value="1" class="form-check-input" @checked($s->value)></div>
|
||||
@elseif($s->type === 'integer')
|
||||
<input type="number" name="settings[{{ $s->key }}]" value="{{ $s->value }}" class="form-control" style="max-width:200px">
|
||||
@elseif($s->type === 'date')
|
||||
<input type="date" name="settings[{{ $s->key }}]" value="{{ $s->value }}" class="form-control" style="max-width:240px">
|
||||
@else
|
||||
<input type="text" name="settings[{{ $s->key }}]" value="{{ $s->value }}" class="form-control">
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
<button class="btn btn-success"><i class="bi bi-save me-1"></i> Simpan Tetapan</button>
|
||||
</form>
|
||||
@endsection
|
||||
39
resources/views/submissions/_comments.blade.php
Normal file
39
resources/views/submissions/_comments.blade.php
Normal file
@@ -0,0 +1,39 @@
|
||||
@php($isJpp = auth()->user()->can('serahan.lihat_semua'))
|
||||
<div class="card mb-3" id="komen">
|
||||
<div class="card-header bg-white fw-semibold"><i class="bi bi-chat-dots me-1"></i> Perbualan / Komen</div>
|
||||
<div class="card-body">
|
||||
<div class="mb-3" style="max-height:420px; overflow-y:auto">
|
||||
@forelse ($submission->comments as $comment)
|
||||
{{-- Komen dalaman hanya kelihatan kepada kakitangan JPP --}}
|
||||
@if ($comment->is_internal && ! $isJpp) @continue @endif
|
||||
<div class="chat-message {{ $comment->is_internal ? 'internal' : '' }}">
|
||||
<div class="d-flex justify-content-between">
|
||||
<strong class="small">{{ $comment->user?->name ?? 'Pengguna' }}
|
||||
@if($comment->is_internal)<span class="badge bg-warning text-dark">Dalaman</span>@endif
|
||||
</strong>
|
||||
<span class="text-muted" style="font-size:.72rem">{{ $comment->created_at->format('d/m/Y H:i') }}</span>
|
||||
</div>
|
||||
<div class="small">{!! nl2br(e($comment->body)) !!}</div>
|
||||
@can('komen.padam')
|
||||
<form method="POST" action="{{ route('comments.destroy', $comment) }}" data-confirm="Padam mesej ini (soft delete, rekod audit disimpan)?">@csrf @method('DELETE')<button class="btn btn-link btn-sm text-danger p-0" style="font-size:.72rem">Padam</button></form>
|
||||
@endcan
|
||||
</div>
|
||||
@empty
|
||||
<p class="text-muted small">Tiada mesej lagi.</p>
|
||||
@endforelse
|
||||
</div>
|
||||
|
||||
@can('comment', $submission)
|
||||
<form method="POST" action="{{ route('comments.store', $submission) }}">
|
||||
@csrf
|
||||
<textarea name="body" rows="2" class="form-control mb-2" placeholder="Tulis mesej..." required></textarea>
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
@if($isJpp)
|
||||
<div class="form-check"><input type="checkbox" name="is_internal" value="1" class="form-check-input" id="internalChk"><label for="internalChk" class="form-check-label small">Mesej dalaman (JPP sahaja)</label></div>
|
||||
@else<span></span>@endif
|
||||
<button class="btn btn-sm btn-success"><i class="bi bi-send me-1"></i> Hantar</button>
|
||||
</div>
|
||||
</form>
|
||||
@endcan
|
||||
</div>
|
||||
</div>
|
||||
55
resources/views/submissions/_corrections_panel.blade.php
Normal file
55
resources/views/submissions/_corrections_panel.blade.php
Normal file
@@ -0,0 +1,55 @@
|
||||
@php($canReview = auth()->user()->can('review', $submission))
|
||||
@php($canRespond = auth()->user()->can('respondCorrection', $submission))
|
||||
<div class="card mb-3" id="pembetulan">
|
||||
<div class="card-header bg-white fw-semibold"><i class="bi bi-pencil-square me-1"></i> Permohonan Pembetulan</div>
|
||||
<div class="card-body">
|
||||
@forelse ($submission->correctionRequests as $cr)
|
||||
<div class="border rounded p-2 mb-2">
|
||||
<div class="d-flex justify-content-between">
|
||||
<strong class="small">m/s {{ $cr->page_reference }} — {{ $cr->location }}</strong>
|
||||
<span class="badge bg-{{ $cr->status==='terbuka'?'warning text-dark':($cr->status==='dijawab'?'info':'success') }}">{{ App\Models\SubmissionCorrectionRequest::STATUSES[$cr->status] ?? $cr->status }}</span>
|
||||
</div>
|
||||
<div class="small">{{ $cr->description }}</div>
|
||||
<div class="text-muted" style="font-size:.75rem">Oleh {{ $cr->createdBy?->name }} • {{ $cr->created_at->format('d/m/Y') }}</div>
|
||||
|
||||
@if($cr->consultant_response)
|
||||
<div class="bg-light rounded p-2 mt-1 small"><strong>Maklum balas perunding:</strong> {{ $cr->consultant_response }}</div>
|
||||
@endif
|
||||
|
||||
@if($canRespond && $cr->status !== 'selesai')
|
||||
<form method="POST" action="{{ route('corrections.respond', $cr) }}" class="mt-2">
|
||||
@csrf
|
||||
<textarea name="consultant_response" rows="2" class="form-control form-control-sm mb-1" placeholder="Maklum balas / tindakan pembetulan" required>{{ $cr->consultant_response }}</textarea>
|
||||
<button class="btn btn-sm btn-outline-primary">Hantar Maklum Balas</button>
|
||||
</form>
|
||||
@endif
|
||||
|
||||
@if($canReview && $cr->status !== 'selesai')
|
||||
<form method="POST" action="{{ route('corrections.resolve', $cr) }}" class="mt-1">@csrf<button class="btn btn-sm btn-link text-success p-0">Tandakan selesai</button></form>
|
||||
@endif
|
||||
</div>
|
||||
@empty
|
||||
<p class="text-muted small mb-2">Tiada permohonan pembetulan.</p>
|
||||
@endforelse
|
||||
|
||||
@if($canReview)
|
||||
<hr>
|
||||
<form method="POST" action="{{ route('corrections.store', $submission) }}">
|
||||
@csrf
|
||||
<label class="form-label small fw-semibold">Tambah Permohonan Pembetulan</label>
|
||||
<div class="row g-2 mb-1">
|
||||
<div class="col-4"><input name="page_reference" class="form-control form-control-sm" placeholder="No. m/s" required></div>
|
||||
<div class="col-8"><input name="location" class="form-control form-control-sm" placeholder="Lokasi/seksyen/item" required></div>
|
||||
</div>
|
||||
<select name="checklist_item_id" class="form-select form-select-sm mb-1">
|
||||
<option value="">— Kaitkan item (pilihan) —</option>
|
||||
@foreach (($template?->sections ?? []) as $s)
|
||||
@foreach ($s->items as $it)<option value="{{ $it->id }}">{{ $s->code }} • {{ $it->code }} — {{ \Illuminate\Support\Str::limit($it->label, 40) }}</option>@endforeach
|
||||
@endforeach
|
||||
</select>
|
||||
<textarea name="description" rows="2" class="form-control form-control-sm mb-1" placeholder="Apa yang perlu dibetulkan?" required></textarea>
|
||||
<button class="btn btn-sm btn-warning w-100">Tambah Pembetulan</button>
|
||||
</form>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
32
resources/views/submissions/_documents_panel.blade.php
Normal file
32
resources/views/submissions/_documents_panel.blade.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<div class="card mb-3">
|
||||
<div class="card-header bg-white fw-semibold"><i class="bi bi-file-earmark-pdf me-1"></i> Laporan PDF</div>
|
||||
<div class="card-body">
|
||||
@if ($submission->isEditableByConsultant() && auth()->user()->can('update', $submission))
|
||||
<form method="POST" action="{{ route('documents.store', $submission) }}" enctype="multipart/form-data" class="mb-3">
|
||||
@csrf
|
||||
<input type="file" name="report" accept="application/pdf" class="form-control form-control-sm mb-2" required>
|
||||
<button class="btn btn-sm btn-outline-primary w-100"><i class="bi bi-upload me-1"></i> Muat Naik (versi baharu)</button>
|
||||
<div class="form-text">PDF sahaja. Versi lama dikekalkan.</div>
|
||||
</form>
|
||||
@endif
|
||||
|
||||
@forelse ($submission->documents as $doc)
|
||||
<div class="d-flex justify-content-between align-items-center border rounded p-2 mb-1 {{ $doc->is_active ? 'border-success' : '' }}">
|
||||
<div class="small">
|
||||
<i class="bi bi-file-earmark-pdf text-danger"></i> v{{ $doc->version }}
|
||||
@if($doc->is_active)<span class="badge bg-success">Aktif</span>@endif
|
||||
<div class="text-muted text-truncate" style="max-width:160px">{{ $doc->original_filename }}</div>
|
||||
<div class="text-muted">{{ $doc->humanSize() }} • {{ $doc->created_at->format('d/m/Y H:i') }}</div>
|
||||
</div>
|
||||
<div class="text-end">
|
||||
<a href="{{ route('documents.download', $doc) }}" class="btn btn-sm btn-link p-0"><i class="bi bi-download"></i></a>
|
||||
@if (!$doc->is_active && auth()->user()->can('update', $submission) && $submission->isEditableByConsultant())
|
||||
<form method="POST" action="{{ route('documents.active', $doc) }}">@csrf<button class="btn btn-sm btn-link p-0" title="Jadikan aktif"><i class="bi bi-check2-circle"></i></button></form>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@empty
|
||||
<p class="text-muted small mb-0">Tiada laporan dimuat naik.</p>
|
||||
@endforelse
|
||||
</div>
|
||||
</div>
|
||||
46
resources/views/submissions/_item_field.blade.php
Normal file
46
resources/views/submissions/_item_field.blade.php
Normal file
@@ -0,0 +1,46 @@
|
||||
@php
|
||||
$val = old("answers.{$item->id}.value", $answer->value ?? '');
|
||||
$page = old("answers.{$item->id}.page_reference", $answer->page_reference ?? '');
|
||||
$note = old("answers.{$item->id}.consultant_note", $answer->consultant_note ?? '');
|
||||
$disabled = (!$editable || $item->is_calculated) ? 'disabled' : '';
|
||||
@endphp
|
||||
<div class="mb-3 pb-2 border-bottom">
|
||||
<label class="form-label mb-1">
|
||||
<span class="text-muted small">{{ $item->code }}</span> {{ $item->label }}
|
||||
@if($item->is_required)<span class="text-danger">*</span>@endif
|
||||
@if($item->unit)<span class="badge bg-light text-dark">{{ $item->unit }}</span>@endif
|
||||
@if($item->is_calculated)<span class="badge bg-info">dikira automatik</span>@endif
|
||||
</label>
|
||||
@if($item->description)<div class="form-text mb-1">{{ $item->description }}</div>@endif
|
||||
|
||||
<div class="row g-2">
|
||||
<div class="col-md-5">
|
||||
@switch($item->input_type)
|
||||
@case('textarea')
|
||||
<textarea name="answers[{{ $item->id }}][value]" rows="2" class="form-control form-control-sm" {{ $disabled }}>{{ $val }}</textarea>
|
||||
@break
|
||||
@case('select')
|
||||
<select name="answers[{{ $item->id }}][value]" class="form-select form-select-sm" {{ $disabled }}>
|
||||
<option value="">— Pilih —</option>
|
||||
@foreach (($item->options ?? []) as $opt)<option value="{{ $opt }}" @selected($val===$opt)>{{ $opt }}</option>@endforeach
|
||||
</select>
|
||||
@break
|
||||
@case('checkbox')
|
||||
<div class="form-check"><input type="hidden" name="answers[{{ $item->id }}][value]" value="0"><input type="checkbox" name="answers[{{ $item->id }}][value]" value="1" class="form-check-input" @checked($val=='1') {{ $disabled }}></div>
|
||||
@break
|
||||
@case('number')
|
||||
@case('calculated')
|
||||
<input type="number" step="any" name="answers[{{ $item->id }}][value]" value="{{ $val }}" class="form-control form-control-sm" {{ $disabled }}>
|
||||
@break
|
||||
@default
|
||||
<input type="text" name="answers[{{ $item->id }}][value]" value="{{ $val }}" class="form-control form-control-sm" {{ $disabled }}>
|
||||
@endswitch
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<input type="text" name="answers[{{ $item->id }}][page_reference]" value="{{ $page }}" class="form-control form-control-sm" placeholder="No. m/s laporan" {{ $editable ? '' : 'disabled' }}>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<input type="text" name="answers[{{ $item->id }}][consultant_note]" value="{{ $note }}" class="form-control form-control-sm" placeholder="Catatan perunding" {{ $editable ? '' : 'disabled' }}>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
17
resources/views/submissions/_results_panel.blade.php
Normal file
17
resources/views/submissions/_results_panel.blade.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<div class="card mb-3">
|
||||
<div class="card-header bg-white fw-semibold"><i class="bi bi-calculator me-1"></i> Hasil Formula (dikira automatik)</div>
|
||||
<div class="card-body p-0">
|
||||
<table class="table table-sm mb-0">
|
||||
<tbody>
|
||||
@forelse ($submission->results as $r)
|
||||
<tr>
|
||||
<td class="ps-3"><code>{{ $r->result_token }}</code> <span class="small text-muted">{{ $r->label }}</span></td>
|
||||
<td class="text-end pe-3 fw-semibold">{{ $r->value !== null ? number_format($r->value, 2) : '—' }} {{ $r->unit }}</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr><td class="text-center text-muted py-3">Simpan draf untuk mengira formula.</td></tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
87
resources/views/submissions/_review_panel.blade.php
Normal file
87
resources/views/submissions/_review_panel.blade.php
Normal file
@@ -0,0 +1,87 @@
|
||||
@php($canReview = auth()->user()->can('review', $submission))
|
||||
<div class="card mb-3 border-primary">
|
||||
<div class="card-header bg-white fw-semibold text-primary"><i class="bi bi-clipboard-check me-1"></i> Tindakan JPP</div>
|
||||
<div class="card-body">
|
||||
@if($canReview)
|
||||
{{-- Tukar status --}}
|
||||
<form method="POST" action="{{ route('submissions.status', $submission) }}" class="mb-3">
|
||||
@csrf
|
||||
<label class="form-label small fw-semibold">Tukar Status</label>
|
||||
<select name="status" class="form-select form-select-sm mb-2" id="statusSelect">
|
||||
@foreach (App\Models\Submission::STATUSES as $k => $v)<option value="{{ $k }}" @selected($submission->status===$k)>{{ $v }}</option>@endforeach
|
||||
</select>
|
||||
<div class="mb-2" id="hardcopySentWrap" style="display:none">
|
||||
<label class="form-label small">Tarikh hardcopy dihantar ke Bahagian Kawalan Perancangan</label>
|
||||
<input type="date" name="hardcopy_sent_date" class="form-control form-control-sm" value="{{ $submission->hardcopy_sent_date?->toDateString() }}">
|
||||
</div>
|
||||
<textarea name="notes" rows="2" class="form-control form-control-sm mb-2" placeholder="Nota status (pilihan)"></textarea>
|
||||
<button class="btn btn-sm btn-primary w-100">Kemaskini Status</button>
|
||||
<div class="form-text">Status "Pembetulan Perunding" perlu ada permohonan pembetulan terbuka.</div>
|
||||
</form>
|
||||
|
||||
{{-- Pegawai bertanggungjawab --}}
|
||||
<form method="POST" action="{{ route('submissions.assign-officer', $submission) }}" class="mb-3">
|
||||
@csrf
|
||||
<label class="form-label small fw-semibold">Pegawai Bertanggungjawab</label>
|
||||
<div class="input-group input-group-sm">
|
||||
<select name="assigned_officer_id" class="form-select">
|
||||
<option value="">— Tiada —</option>
|
||||
@foreach ($officers as $o)<option value="{{ $o->id }}" @selected($submission->assigned_officer_id==$o->id)>{{ $o->name }}</option>@endforeach
|
||||
</select>
|
||||
<button class="btn btn-outline-primary">Set</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{{-- Nota semakan keseluruhan --}}
|
||||
<form method="POST" action="{{ route('submissions.review-note', $submission) }}" class="mb-3">
|
||||
@csrf
|
||||
<label class="form-label small fw-semibold">Nota Semakan</label>
|
||||
<textarea name="review_notes" rows="2" class="form-control form-control-sm mb-2">{{ $submission->review_notes }}</textarea>
|
||||
<button class="btn btn-sm btn-outline-secondary w-100">Simpan Nota</button>
|
||||
</form>
|
||||
@endif
|
||||
|
||||
{{-- Salinan keras --}}
|
||||
@can('markHardcopy', $submission)
|
||||
<form method="POST" action="{{ route('submissions.hardcopy', $submission) }}" class="mb-3">
|
||||
@csrf
|
||||
<label class="form-label small fw-semibold">Dokumen Salinan Keras (Hardcopy)</label>
|
||||
<div class="form-check"><input type="hidden" name="hardcopy_received" value="0"><input type="checkbox" name="hardcopy_received" value="1" class="form-check-input" id="hcReceived" @checked($submission->hardcopy_received)><label for="hcReceived" class="form-check-label">Telah diterima</label></div>
|
||||
<input type="date" name="hardcopy_received_date" class="form-control form-control-sm my-2" value="{{ $submission->hardcopy_received_date?->toDateString() }}">
|
||||
<button class="btn btn-sm btn-outline-secondary w-100">Simpan</button>
|
||||
</form>
|
||||
@endcan
|
||||
|
||||
{{-- Kunci / buka kunci --}}
|
||||
@can('lock', $submission)
|
||||
<hr>
|
||||
@if(!$submission->is_locked)
|
||||
<form method="POST" action="{{ route('submissions.lock', $submission) }}">
|
||||
@csrf
|
||||
<input type="text" name="lock_reason" class="form-control form-control-sm mb-2" placeholder="Sebab kunci (pilihan)">
|
||||
<button class="btn btn-sm btn-danger w-100" data-confirm="Kunci serahan ini? Perunding tidak boleh mengubah."><i class="bi bi-lock me-1"></i> Kunci Serahan</button>
|
||||
</form>
|
||||
@else
|
||||
<form method="POST" action="{{ route('submissions.unlock', $submission) }}">
|
||||
@csrf
|
||||
@if($submission->lock_reason)<div class="small text-muted mb-1">Sebab kunci: {{ $submission->lock_reason }}</div>@endif
|
||||
<input type="text" name="lock_reason" class="form-control form-control-sm mb-2" placeholder="Sebab buka kunci (wajib)" required>
|
||||
<button class="btn btn-sm btn-warning w-100"><i class="bi bi-unlock me-1"></i> Buka Kunci</button>
|
||||
</form>
|
||||
@endif
|
||||
@endcan
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@push('scripts')
|
||||
<script>
|
||||
(function () {
|
||||
const sel = document.getElementById('statusSelect');
|
||||
const wrap = document.getElementById('hardcopySentWrap');
|
||||
if (sel && wrap) {
|
||||
const toggle = () => wrap.style.display = sel.value === 'selesai' ? 'block' : 'none';
|
||||
sel.addEventListener('change', toggle); toggle();
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
@endpush
|
||||
35
resources/views/submissions/create.blade.php
Normal file
35
resources/views/submissions/create.blade.php
Normal file
@@ -0,0 +1,35 @@
|
||||
@extends('layouts.app')
|
||||
@section('title', 'Mula Serahan')
|
||||
@section('heading', 'Serahan')
|
||||
@section('page-title', 'Mula Serahan Baharu')
|
||||
|
||||
@section('content')
|
||||
<div class="card"><div class="card-body">
|
||||
@if ($dataCentres->isEmpty())
|
||||
<div class="alert alert-warning mb-0">Anda tiada pusat data aktif yang ditugaskan. Sila hubungi JPP.</div>
|
||||
@elseif ($cycles->isEmpty())
|
||||
<div class="alert alert-warning mb-0">Tiada kitaran pelaporan aktif buat masa ini.</div>
|
||||
@else
|
||||
<form method="POST" action="{{ route('submissions.store') }}">
|
||||
@csrf
|
||||
<div class="row g-3">
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Pusat Data <span class="text-danger">*</span></label>
|
||||
<select name="data_centre_id" class="form-select" required>
|
||||
<option value="">— Pilih pusat data —</option>
|
||||
@foreach ($dataCentres as $dc)<option value="{{ $dc->id }}">{{ $dc->nama_pusat_data }}</option>@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Kitaran Pelaporan <span class="text-danger">*</span></label>
|
||||
<select name="reporting_cycle_id" class="form-select" required>
|
||||
<option value="">— Pilih kitaran —</option>
|
||||
@foreach ($cycles as $c)<option value="{{ $c->id }}">Pembaharuan {{ $c->renewal_year }} / Pelaporan {{ $c->reporting_year }} (tutup {{ $c->cut_off_date?->format('d/m/Y') }})</option>@endforeach
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-4"><button class="btn btn-success"><i class="bi bi-arrow-right me-1"></i> Teruskan ke Borang Senarai Semak</button></div>
|
||||
</form>
|
||||
@endif
|
||||
</div></div>
|
||||
@endsection
|
||||
78
resources/views/submissions/fill.blade.php
Normal file
78
resources/views/submissions/fill.blade.php
Normal file
@@ -0,0 +1,78 @@
|
||||
@extends('layouts.app')
|
||||
@section('title', 'Borang Senarai Semak')
|
||||
@section('heading', 'Serahan')
|
||||
@section('page-title', 'Borang Senarai Semak')
|
||||
@section('subtitle', $submission->dataCentre->nama_pusat_data.' • Pelaporan '.$submission->reportingCycle->reporting_year)
|
||||
|
||||
@section('actions')
|
||||
<a href="{{ route('submissions.show', $submission) }}" class="btn btn-link">Lihat Ringkasan</a>
|
||||
@endsection
|
||||
|
||||
@section('content')
|
||||
@if (!$submission->isEditableByConsultant())
|
||||
<div class="alert alert-warning"><i class="bi bi-lock me-1"></i> Serahan ini telah dikunci / selesai dan tidak boleh diubah.</div>
|
||||
@endif
|
||||
|
||||
@if ($submission->correctionRequests->where('status','!=','selesai')->count())
|
||||
<div class="alert alert-warning">
|
||||
<strong><i class="bi bi-pencil-square me-1"></i> Permohonan pembetulan daripada JPP:</strong>
|
||||
<ul class="mb-0 mt-1">
|
||||
@foreach ($submission->correctionRequests->where('status','!=','selesai') as $cr)
|
||||
<li>[m/s {{ $cr->page_reference }}] <strong>{{ $cr->location }}</strong> — {{ $cr->description }}
|
||||
<a href="{{ route('submissions.show', $submission) }}#pembetulan" class="small">jawab di sini</a></li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="row g-3">
|
||||
<div class="col-lg-8">
|
||||
<form method="POST" action="{{ route('submissions.update', $submission) }}" id="checklistForm">
|
||||
@csrf @method('PUT')
|
||||
|
||||
@forelse ($template->sections as $section)
|
||||
<div class="card mb-3">
|
||||
<div class="card-header checklist-section-header"><span class="badge bg-success me-1">{{ $section->code }}</span> <strong>{{ $section->title }}</strong></div>
|
||||
<div class="card-body">
|
||||
@if($section->description)<p class="small text-muted">{{ $section->description }}</p>@endif
|
||||
|
||||
@php($renderItem = function($item) use ($answers) {
|
||||
return [$item, $answers[$item->id] ?? null];
|
||||
})
|
||||
|
||||
{{-- Item terus di bawah seksyen (tanpa skop) --}}
|
||||
@foreach ($section->items->whereNull('checklist_scope_id') as $item)
|
||||
@include('submissions._item_field', ['item' => $item, 'answer' => $answers[$item->id] ?? null, 'editable' => $submission->isEditableByConsultant()])
|
||||
@endforeach
|
||||
|
||||
{{-- Item dikumpul mengikut skop --}}
|
||||
@foreach ($section->scopes as $scope)
|
||||
<div class="border-start border-3 ps-3 mb-3">
|
||||
<h6 class="mt-2"><span class="badge bg-secondary">{{ $scope->code }}</span> {{ $scope->title }}</h6>
|
||||
@if($scope->description)<p class="small text-muted">{{ $scope->description }}</p>@endif
|
||||
@foreach ($scope->items as $item)
|
||||
@include('submissions._item_field', ['item' => $item, 'answer' => $answers[$item->id] ?? null, 'editable' => $submission->isEditableByConsultant()])
|
||||
@endforeach
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
@empty
|
||||
<div class="alert alert-info">Templat senarai semak tidak mempunyai seksyen.</div>
|
||||
@endforelse
|
||||
|
||||
@if ($submission->isEditableByConsultant())
|
||||
<div class="d-flex gap-2 mb-4">
|
||||
<button type="submit" class="btn btn-outline-primary"><i class="bi bi-save me-1"></i> Simpan Draf</button>
|
||||
<button type="submit" formaction="{{ route('submissions.submit', $submission) }}" class="btn btn-success" data-confirm="Hantar serahan secara muktamad kepada JPP?"><i class="bi bi-send me-1"></i> Hantar Serahan</button>
|
||||
</div>
|
||||
@endif
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-4">
|
||||
@include('submissions._documents_panel', ['submission' => $submission])
|
||||
@include('submissions._results_panel', ['submission' => $submission])
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
57
resources/views/submissions/index.blade.php
Normal file
57
resources/views/submissions/index.blade.php
Normal file
@@ -0,0 +1,57 @@
|
||||
@extends('layouts.app')
|
||||
@section('title', 'Senarai Serahan')
|
||||
@section('heading', 'Serahan')
|
||||
@section('page-title', 'Senarai Serahan')
|
||||
|
||||
@section('actions')
|
||||
@can('create', App\Models\Submission::class)
|
||||
<a href="{{ route('submissions.create') }}" class="btn btn-success"><i class="bi bi-plus-lg me-1"></i> Mula Serahan</a>
|
||||
@endcan
|
||||
@endsection
|
||||
|
||||
@section('content')
|
||||
<div class="card"><div class="card-body">
|
||||
<form class="row g-2 mb-3">
|
||||
<div class="col-md-3">
|
||||
<select name="cycle" class="form-select">
|
||||
<option value="">Semua kitaran</option>
|
||||
@foreach ($cycles as $c)<option value="{{ $c->id }}" @selected(request('cycle')==$c->id)>Pembaharuan {{ $c->renewal_year }} / Pelaporan {{ $c->reporting_year }}</option>@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<select name="status" class="form-select">
|
||||
<option value="">Semua status</option>
|
||||
@foreach (App\Models\Submission::STATUSES as $k => $v)<option value="{{ $k }}" @selected(request('status')===$k)>{{ $v }}</option>@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-2 d-grid"><button class="btn btn-outline-primary"><i class="bi bi-funnel me-1"></i> Tapis</button></div>
|
||||
</form>
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover align-middle">
|
||||
<thead><tr><th>Pusat Data</th><th>Kitaran</th><th>Perunding</th><th>Status</th><th>Pegawai</th><th>Dihantar</th><th></th></tr></thead>
|
||||
<tbody>
|
||||
@forelse ($submissions as $s)
|
||||
<tr>
|
||||
<td class="fw-semibold">{{ $s->dataCentre->nama_pusat_data }}</td>
|
||||
<td class="small">Pemb. {{ $s->reportingCycle->renewal_year }} / Pel. {{ $s->reportingCycle->reporting_year }}</td>
|
||||
<td class="small">{{ $s->consultant->nama_perunding }}</td>
|
||||
<td><span class="badge bg-{{ $s->statusBadgeClass() }}">{{ $s->statusLabel() }}</span> @if($s->is_locked)<i class="bi bi-lock-fill text-danger" title="Dikunci"></i>@endif</td>
|
||||
<td class="small">{{ $s->assignedOfficer?->name ?? '—' }}</td>
|
||||
<td class="small">{{ $s->submitted_at?->format('d/m/Y') ?? '—' }}</td>
|
||||
<td class="text-end">
|
||||
@if ($s->isEditableByConsultant() && auth()->user()->can('update', $s))
|
||||
<a href="{{ route('submissions.edit', $s) }}" class="btn btn-sm btn-outline-primary">Isi</a>
|
||||
@endif
|
||||
<a href="{{ route('submissions.show', $s) }}" class="btn btn-sm btn-outline-secondary">Lihat</a>
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr><td colspan="7" class="text-center text-muted py-3">Tiada serahan.</td></tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{{ $submissions->links() }}
|
||||
</div></div>
|
||||
@endsection
|
||||
111
resources/views/submissions/show.blade.php
Normal file
111
resources/views/submissions/show.blade.php
Normal file
@@ -0,0 +1,111 @@
|
||||
@extends('layouts.app')
|
||||
@section('title', 'Butiran Serahan')
|
||||
@section('heading', 'Serahan')
|
||||
@section('page-title', $submission->dataCentre->nama_pusat_data)
|
||||
@section('subtitle', 'Pelaporan '.$submission->reportingCycle->reporting_year.' • Pembaharuan '.$submission->reportingCycle->renewal_year)
|
||||
|
||||
@section('actions')
|
||||
@if ($submission->isEditableByConsultant() && auth()->user()->can('update', $submission))
|
||||
<a href="{{ route('submissions.edit', $submission) }}" class="btn btn-outline-primary"><i class="bi bi-pencil me-1"></i> Isi / Kemaskini</a>
|
||||
@endif
|
||||
@endsection
|
||||
|
||||
@section('content')
|
||||
@php($canReview = auth()->user()->can('review', $submission))
|
||||
<div class="d-flex flex-wrap gap-2 mb-3 align-items-center">
|
||||
<span class="badge bg-{{ $submission->statusBadgeClass() }} fs-6">{{ $submission->statusLabel() }}</span>
|
||||
@if($submission->is_locked)<span class="badge bg-danger"><i class="bi bi-lock-fill"></i> Dikunci</span>@endif
|
||||
<span class="text-muted small">Perunding: {{ $submission->consultant->nama_perunding }}</span>
|
||||
@if($submission->submitted_at)<span class="text-muted small">• Dihantar: {{ $submission->submitted_at->format('d/m/Y H:i') }}</span>@endif
|
||||
</div>
|
||||
|
||||
<div class="row g-3">
|
||||
<div class="col-lg-8">
|
||||
{{-- ===== Jawapan senarai semak ===== --}}
|
||||
@foreach ($template?->sections ?? [] as $section)
|
||||
<div class="card mb-3">
|
||||
<div class="card-header checklist-section-header"><span class="badge bg-success me-1">{{ $section->code }}</span> <strong>{{ $section->title }}</strong></div>
|
||||
<div class="card-body p-0">
|
||||
<table class="table table-sm mb-0 align-middle">
|
||||
<thead><tr><th class="ps-3">Item</th><th>Nilai</th><th>m/s</th><th>Catatan Perunding</th>@if($canReview)<th>Semakan</th>@endif</tr></thead>
|
||||
<tbody>
|
||||
@foreach ($section->items as $item)
|
||||
@php($a = $answers[$item->id] ?? null)
|
||||
<tr>
|
||||
<td class="ps-3"><span class="text-muted small">{{ $item->code }}</span> {{ $item->label }}</td>
|
||||
<td>{{ $a?->value !== null && $a?->value !== '' ? $a->value : '—' }} <span class="text-muted small">{{ $item->unit }}</span></td>
|
||||
<td>{{ $a?->page_reference ?: '—' }}</td>
|
||||
<td class="small">{{ $a?->consultant_note ?: '—' }}</td>
|
||||
@if($canReview)
|
||||
<td>
|
||||
@if($a)
|
||||
<button class="btn btn-sm btn-link p-0" data-bs-toggle="modal" data-bs-target="#reviewAns{{ $a->id }}">
|
||||
@php($vs = $a->validation_status)
|
||||
<span class="badge bg-{{ $vs==='sah'?'success':($vs==='tidak_sah'?'danger':'secondary') }}">{{ ['belum_semak'=>'Belum Semak','sah'=>'Sah','tidak_sah'=>'Tidak Sah'][$vs] ?? $vs }}</span>
|
||||
</button>
|
||||
@else — @endif
|
||||
</td>
|
||||
@endif
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
|
||||
@include('submissions._comments', ['submission' => $submission])
|
||||
</div>
|
||||
|
||||
{{-- ===== Panel sisi ===== --}}
|
||||
<div class="col-lg-4">
|
||||
@include('submissions._results_panel', ['submission' => $submission])
|
||||
@include('submissions._documents_panel', ['submission' => $submission])
|
||||
|
||||
@if($canReview || auth()->user()->can('markHardcopy', $submission))
|
||||
@include('submissions._review_panel', ['submission' => $submission, 'officers' => $officers])
|
||||
@endif
|
||||
|
||||
@include('submissions._corrections_panel', ['submission' => $submission])
|
||||
|
||||
{{-- Sejarah status --}}
|
||||
<div class="card mb-3"><div class="card-header bg-white fw-semibold"><i class="bi bi-clock-history me-1"></i> Sejarah Status</div>
|
||||
<div class="card-body p-0"><ul class="list-group list-group-flush small">
|
||||
@forelse ($submission->statusHistories as $h)
|
||||
<li class="list-group-item">
|
||||
<span class="badge bg-secondary">{{ App\Models\Submission::STATUSES[$h->from_status] ?? $h->from_status ?? '—' }}</span> →
|
||||
<span class="badge bg-primary">{{ App\Models\Submission::STATUSES[$h->to_status] ?? $h->to_status }}</span>
|
||||
<div class="text-muted">{{ $h->changedBy?->name }} • {{ $h->created_at->format('d/m/Y H:i') }}</div>
|
||||
@if($h->notes)<div>{{ $h->notes }}</div>@endif
|
||||
</li>
|
||||
@empty
|
||||
<li class="list-group-item text-muted">Tiada sejarah.</li>
|
||||
@endforelse
|
||||
</ul></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Modal semakan item (pegawai) --}}
|
||||
@if($canReview)
|
||||
@foreach ($submission->answers as $a)
|
||||
<div class="modal fade" id="reviewAns{{ $a->id }}" tabindex="-1"><div class="modal-dialog"><div class="modal-content">
|
||||
<form method="POST" action="{{ route('answers.review', $a) }}">
|
||||
@csrf
|
||||
<div class="modal-header"><h6 class="modal-title">Semakan Item {{ $a->item->code ?? '' }}</h6><button type="button" class="btn-close" data-bs-dismiss="modal"></button></div>
|
||||
<div class="modal-body">
|
||||
<div class="mb-2"><label class="form-label">Status Pengesahan</label>
|
||||
<select name="validation_status" class="form-select">
|
||||
<option value="belum_semak" @selected($a->validation_status==='belum_semak')>Belum Semak</option>
|
||||
<option value="sah" @selected($a->validation_status==='sah')>Sah</option>
|
||||
<option value="tidak_sah" @selected($a->validation_status==='tidak_sah')>Tidak Sah</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-2"><label class="form-label">Nota Pegawai</label><textarea name="officer_note" class="form-control">{{ $a->officer_note }}</textarea></div>
|
||||
</div>
|
||||
<div class="modal-footer"><button class="btn btn-success btn-sm">Simpan</button></div>
|
||||
</form>
|
||||
</div></div></div>
|
||||
@endforeach
|
||||
@endif
|
||||
@endsection
|
||||
17
resources/views/users/_form.blade.php
Normal file
17
resources/views/users/_form.blade.php
Normal file
@@ -0,0 +1,17 @@
|
||||
@csrf
|
||||
<div class="row g-3">
|
||||
<div class="col-md-6"><label class="form-label">Nama <span class="text-danger">*</span></label><input name="name" value="{{ old('name', $user->name) }}" class="form-control @error('name') is-invalid @enderror" required>@error('name')<div class="invalid-feedback">{{ $message }}</div>@enderror</div>
|
||||
<div class="col-md-6"><label class="form-label">Emel <span class="text-danger">*</span></label><input type="email" name="email" value="{{ old('email', $user->email) }}" class="form-control @error('email') is-invalid @enderror" required>@error('email')<div class="invalid-feedback">{{ $message }}</div>@enderror</div>
|
||||
<div class="col-md-6"><label class="form-label">Telefon</label><input name="phone" value="{{ old('phone', $user->phone) }}" class="form-control"></div>
|
||||
<div class="col-md-6"><label class="form-label">Jawatan</label><input name="jawatan" value="{{ old('jawatan', $user->jawatan) }}" class="form-control"></div>
|
||||
<div class="col-md-6"><label class="form-label">Peranan <span class="text-danger">*</span></label>
|
||||
<select name="role" class="form-select" required>
|
||||
<option value="">— Pilih peranan —</option>
|
||||
@foreach ($roles as $r)<option value="{{ $r->name }}" @selected(old('role', $user->roles->first()?->name)===$r->name)>{{ $r->name }}</option>@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-6 d-flex align-items-end"><div class="form-check form-switch"><input type="checkbox" name="is_active" value="1" class="form-check-input" id="uActive" @checked(old('is_active', $user->is_active ?? true))><label for="uActive" class="form-check-label">Akaun aktif</label></div></div>
|
||||
<div class="col-md-6"><label class="form-label">Kata Laluan {{ $user->exists ? '(biar kosong jika tidak tukar)' : '' }}</label><input type="password" name="password" class="form-control @error('password') is-invalid @enderror">@error('password')<div class="invalid-feedback">{{ $message }}</div>@enderror</div>
|
||||
<div class="col-md-6"><label class="form-label">Sahkan Kata Laluan</label><input type="password" name="password_confirmation" class="form-control"></div>
|
||||
</div>
|
||||
<div class="mt-4"><button class="btn btn-success"><i class="bi bi-save me-1"></i> Simpan</button><a href="{{ route('users.index') }}" class="btn btn-link">Batal</a></div>
|
||||
7
resources/views/users/create.blade.php
Normal file
7
resources/views/users/create.blade.php
Normal file
@@ -0,0 +1,7 @@
|
||||
@extends('layouts.app')
|
||||
@section('title', 'Tambah Pengguna')
|
||||
@section('heading', 'Pengguna')
|
||||
@section('page-title', 'Tambah Pengguna')
|
||||
@section('content')
|
||||
<div class="card"><div class="card-body"><form method="POST" action="{{ route('users.store') }}">@include('users._form')</form></div></div>
|
||||
@endsection
|
||||
7
resources/views/users/edit.blade.php
Normal file
7
resources/views/users/edit.blade.php
Normal file
@@ -0,0 +1,7 @@
|
||||
@extends('layouts.app')
|
||||
@section('title', 'Kemaskini Pengguna')
|
||||
@section('heading', 'Pengguna')
|
||||
@section('page-title', 'Kemaskini Pengguna')
|
||||
@section('content')
|
||||
<div class="card"><div class="card-body"><form method="POST" action="{{ route('users.update', $user) }}">@method('PUT')@include('users._form')</form></div></div>
|
||||
@endsection
|
||||
39
resources/views/users/index.blade.php
Normal file
39
resources/views/users/index.blade.php
Normal file
@@ -0,0 +1,39 @@
|
||||
@extends('layouts.app')
|
||||
@section('title', 'Pengurusan Pengguna')
|
||||
@section('heading', 'Pengguna')
|
||||
@section('page-title', 'Pengurusan Pengguna')
|
||||
|
||||
@section('actions')
|
||||
<a href="{{ route('users.create') }}" class="btn btn-success"><i class="bi bi-plus-lg me-1"></i> Tambah Pengguna</a>
|
||||
@endsection
|
||||
|
||||
@section('content')
|
||||
<div class="card"><div class="card-body">
|
||||
<form class="row g-2 mb-3"><div class="col-md-6"><input name="cari" value="{{ request('cari') }}" class="form-control" placeholder="Cari nama / emel..."></div><div class="col-md-2 d-grid"><button class="btn btn-outline-primary">Cari</button></div></form>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover align-middle">
|
||||
<thead><tr><th>Nama</th><th>Emel</th><th>Jawatan</th><th>Peranan</th><th>Status</th><th></th></tr></thead>
|
||||
<tbody>
|
||||
@forelse ($users as $u)
|
||||
<tr>
|
||||
<td class="fw-semibold">{{ $u->name }}</td>
|
||||
<td>{{ $u->email }}</td>
|
||||
<td class="small">{{ $u->jawatan ?: '—' }}</td>
|
||||
<td>@foreach ($u->roles as $r)<span class="badge bg-info">{{ $r->name }}</span>@endforeach</td>
|
||||
<td><span class="badge bg-{{ $u->is_active?'success':'secondary' }}">{{ $u->is_active?'Aktif':'Tidak Aktif' }}</span></td>
|
||||
<td class="text-end">
|
||||
<a href="{{ route('users.edit', $u) }}" class="btn btn-sm btn-outline-secondary"><i class="bi bi-pencil"></i></a>
|
||||
@if($u->id !== auth()->id())
|
||||
<form method="POST" action="{{ route('users.destroy', $u) }}" class="d-inline" data-confirm="Padam pengguna ini?">@csrf @method('DELETE')<button class="btn btn-sm btn-outline-danger"><i class="bi bi-trash"></i></button></form>
|
||||
@endif
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr><td colspan="6" class="text-center text-muted py-3">Tiada pengguna.</td></tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{{ $users->links() }}
|
||||
</div></div>
|
||||
@endsection
|
||||
223
resources/views/welcome.blade.php
Normal file
223
resources/views/welcome.blade.php
Normal file
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user