feat: susun semula layout urus template sijil
- Panduan Template di bahagian atas, boleh lipat/kembang - Template Aktif (kiri) bersebelahan Konfigurasi Teks (kanan) — col-lg-6 - Auto-detect portrait/landscape dari naturalWidth/naturalHeight imej - Portrait: max-height 520px | Landscape: max-height 340px - Badge orientasi (hijau=Landscape, biru=Portrait) dalam header kad - Laras tinggi juga untuk pratonton upload form Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -17,14 +17,71 @@
|
|||||||
|
|
||||||
@section('content')
|
@section('content')
|
||||||
|
|
||||||
<div class="row g-4">
|
@php
|
||||||
{{-- Left: Current template --}}
|
$config = $template?->config_json ?? [];
|
||||||
<div class="col-md-7">
|
$fields = $config['fields'] ?? [];
|
||||||
|
$imgWidth = $config['width'] ?? 0;
|
||||||
|
$imgHeight = $config['height'] ?? 0;
|
||||||
|
$isPortrait = $imgHeight > $imgWidth && $imgWidth > 0;
|
||||||
|
@endphp
|
||||||
|
|
||||||
@if($template)
|
{{-- ── Panduan Template (atas, boleh lipat) ────────────────────────────── --}}
|
||||||
<div class="card border-0 shadow-sm mb-4">
|
<div class="card border-0 bg-light mb-4">
|
||||||
|
<div class="card-header bg-transparent border-0 py-2 d-flex justify-content-between align-items-center"
|
||||||
|
role="button" data-bs-toggle="collapse" data-bs-target="#guidePanel" aria-expanded="false">
|
||||||
|
<span class="fw-semibold small"><i class="bi bi-info-circle me-2 text-primary"></i>Panduan Template</span>
|
||||||
|
<i class="bi bi-chevron-down small text-muted" id="guideChevron"></i>
|
||||||
|
</div>
|
||||||
|
<div class="collapse" id="guidePanel">
|
||||||
|
<div class="card-body pt-0 pb-3">
|
||||||
|
<div class="row g-3">
|
||||||
|
<div class="col-md-4">
|
||||||
|
<div class="d-flex gap-2">
|
||||||
|
<i class="bi bi-aspect-ratio text-primary mt-1 flex-shrink-0"></i>
|
||||||
|
<div class="small text-muted">
|
||||||
|
Resolusi disyorkan <strong>1754 × 1240px</strong> (A4 landscape 150dpi)
|
||||||
|
atau <strong>1240 × 1754px</strong> (portrait).
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4">
|
||||||
|
<div class="d-flex gap-2">
|
||||||
|
<i class="bi bi-cursor text-primary mt-1 flex-shrink-0"></i>
|
||||||
|
<div class="small text-muted">
|
||||||
|
Koordinat <strong>(0, 0)</strong> adalah sudut kiri atas imej.
|
||||||
|
X bertambah ke kanan, Y bertambah ke bawah.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4">
|
||||||
|
<div class="d-flex gap-2">
|
||||||
|
<i class="bi bi-eye text-primary mt-1 flex-shrink-0"></i>
|
||||||
|
<div class="small text-muted">
|
||||||
|
Guna butang <strong>Pratonton</strong> untuk semak kedudukan teks
|
||||||
|
sebelum jana sijil sebenar.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@if($template)
|
||||||
|
|
||||||
|
{{-- ── Template Aktif (kiri) + Konfigurasi (kanan) ─────────────────────── --}}
|
||||||
|
<div class="row g-4">
|
||||||
|
|
||||||
|
{{-- Kiri: Template Aktif --}}
|
||||||
|
<div class="col-lg-6">
|
||||||
|
<div class="card border-0 shadow-sm h-100">
|
||||||
<div class="card-header bg-white py-3 d-flex justify-content-between align-items-center">
|
<div class="card-header bg-white py-3 d-flex justify-content-between align-items-center">
|
||||||
<h6 class="mb-0 fw-semibold"><i class="bi bi-image me-2 text-primary"></i>Template Aktif</h6>
|
<div class="d-flex align-items-center gap-2">
|
||||||
|
<h6 class="mb-0 fw-semibold"><i class="bi bi-image me-2 text-primary"></i>Template Aktif</h6>
|
||||||
|
<span id="orientationBadge" class="badge bg-secondary" style="font-size:.7rem;">
|
||||||
|
{{ $isPortrait ? 'Portrait' : ($imgWidth > 0 ? 'Landscape' : '—') }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
<form method="POST" action="{{ route('admin.programs.template.destroy', $program) }}"
|
<form method="POST" action="{{ route('admin.programs.template.destroy', $program) }}"
|
||||||
onsubmit="return confirm('Padam template sijil ini? Tindakan ini tidak boleh diundur.')">
|
onsubmit="return confirm('Padam template sijil ini? Tindakan ini tidak boleh diundur.')">
|
||||||
@csrf @method('DELETE')
|
@csrf @method('DELETE')
|
||||||
@@ -33,52 +90,64 @@
|
|||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body d-flex flex-column">
|
||||||
<div class="text-center mb-3">
|
|
||||||
<div class="cert-preview-wrapper border rounded overflow-hidden" style="max-height:380px;">
|
{{-- Image viewer — tinggi berubah ikut orientasi --}}
|
||||||
<img src="{{ route('admin.programs.template.preview', $program) }}"
|
<div id="previewWrapper"
|
||||||
id="templatePreview"
|
class="border rounded overflow-hidden mb-2 d-flex align-items-center justify-content-center bg-light"
|
||||||
alt="Template Preview"
|
style="max-height:{{ $isPortrait ? '520px' : '340px' }}; transition: max-height .3s ease;">
|
||||||
class="img-fluid w-100"
|
<img src="{{ route('admin.programs.template.preview', $program) }}"
|
||||||
style="object-fit:contain;">
|
id="templatePreview"
|
||||||
</div>
|
alt="Template Preview"
|
||||||
<div class="mt-2 text-muted small">{{ $template->original_filename }}</div>
|
class="img-fluid"
|
||||||
|
style="max-width:100%; max-height:{{ $isPortrait ? '520px' : '340px' }}; object-fit:contain;">
|
||||||
|
</div>
|
||||||
|
<div class="text-muted small text-center mb-3">
|
||||||
|
{{ $template->original_filename }}
|
||||||
|
@if($imgWidth > 0)
|
||||||
|
· <span id="dimensionLabel">{{ $imgWidth }} × {{ $imgHeight }} px</span>
|
||||||
|
@endif
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{-- Test Generate --}}
|
{{-- Jana Pratonton --}}
|
||||||
<div class="border rounded p-3 bg-light">
|
<div class="border rounded p-3 bg-light mt-auto">
|
||||||
<label class="form-label small fw-medium">Jana Pratonton</label>
|
<label class="form-label small fw-medium mb-2">Jana Pratonton</label>
|
||||||
<div class="row g-2">
|
<div class="row g-2">
|
||||||
<div class="col-8">
|
<div class="col-8">
|
||||||
<input type="text" id="sampleName" class="form-control form-control-sm"
|
<input type="text" id="sampleName" class="form-control form-control-sm"
|
||||||
value="NAMA PESERTA CONTOH" placeholder="Nama untuk pratonton">
|
value="NAMA PESERTA CONTOH" placeholder="Nama untuk pratonton">
|
||||||
</div>
|
</div>
|
||||||
<div class="col-4">
|
<div class="col-4">
|
||||||
<button type="button" id="previewBtn" class="btn btn-sm btn-primary w-100" onclick="loadPreview()">
|
<button type="button" id="previewBtn" class="btn btn-sm btn-primary w-100"
|
||||||
|
onclick="loadPreview()">
|
||||||
<i class="bi bi-eye me-1"></i> Pratonton
|
<i class="bi bi-eye me-1"></i> Pratonton
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{{-- Config Editor --}}
|
{{-- Kanan: Konfigurasi Kedudukan Teks --}}
|
||||||
<div class="card border-0 shadow-sm">
|
<div class="col-lg-6">
|
||||||
|
<div class="card border-0 shadow-sm h-100">
|
||||||
<div class="card-header bg-white py-3">
|
<div class="card-header bg-white py-3">
|
||||||
<h6 class="mb-0 fw-semibold"><i class="bi bi-sliders me-2 text-warning"></i>Konfigurasi Kedudukan Teks</h6>
|
<h6 class="mb-0 fw-semibold"><i class="bi bi-sliders me-2 text-warning"></i>Konfigurasi Kedudukan Teks</h6>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
@php $config = $template->config_json ?? []; $fields = $config['fields'] ?? []; @endphp
|
<p class="text-muted small mb-3">
|
||||||
|
Koordinat X dan Y dikira dari sudut kiri atas imej (piksel).
|
||||||
|
@if($imgWidth > 0)
|
||||||
|
Saiz imej: <strong>{{ $imgWidth }} × {{ $imgHeight }} px</strong>.
|
||||||
|
@endif
|
||||||
|
</p>
|
||||||
|
|
||||||
<form method="POST" action="{{ route('admin.programs.template.config', $program) }}">
|
<form method="POST" action="{{ route('admin.programs.template.config', $program) }}">
|
||||||
@csrf @method('PUT')
|
@csrf @method('PUT')
|
||||||
|
|
||||||
<p class="text-muted small mb-3">
|
{{-- Nama Peserta --}}
|
||||||
Koordinat X dan Y dikira dari sudut kiri atas imej (piksel).
|
|
||||||
Imej template: <strong>{{ $config['width'] ?? '—' }} × {{ $config['height'] ?? '—' }}</strong> piksel.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
{{-- Name field --}}
|
|
||||||
<div class="card border mb-3">
|
<div class="card border mb-3">
|
||||||
<div class="card-header py-2 bg-light">
|
<div class="card-header py-2 bg-light">
|
||||||
<span class="fw-medium small">Nama Peserta</span>
|
<span class="fw-medium small">Nama Peserta</span>
|
||||||
@@ -86,12 +155,12 @@
|
|||||||
<div class="card-body py-3">
|
<div class="card-body py-3">
|
||||||
<div class="row g-2">
|
<div class="row g-2">
|
||||||
<div class="col-4">
|
<div class="col-4">
|
||||||
<label class="form-label small">X (Piksel)</label>
|
<label class="form-label small">X (px)</label>
|
||||||
<input type="number" name="fields[name][x]" class="form-control form-control-sm"
|
<input type="number" name="fields[name][x]" class="form-control form-control-sm"
|
||||||
value="{{ $fields['name']['x'] ?? 800 }}">
|
value="{{ $fields['name']['x'] ?? 800 }}">
|
||||||
</div>
|
</div>
|
||||||
<div class="col-4">
|
<div class="col-4">
|
||||||
<label class="form-label small">Y (Piksel)</label>
|
<label class="form-label small">Y (px)</label>
|
||||||
<input type="number" name="fields[name][y]" class="form-control form-control-sm"
|
<input type="number" name="fields[name][y]" class="form-control form-control-sm"
|
||||||
value="{{ $fields['name']['y'] ?? 400 }}">
|
value="{{ $fields['name']['y'] ?? 400 }}">
|
||||||
</div>
|
</div>
|
||||||
@@ -102,7 +171,8 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-4">
|
<div class="col-4">
|
||||||
<label class="form-label small">Warna</label>
|
<label class="form-label small">Warna</label>
|
||||||
<input type="color" name="fields[name][font_color]" class="form-control form-control-color form-control-sm"
|
<input type="color" name="fields[name][font_color]"
|
||||||
|
class="form-control form-control-color form-control-sm"
|
||||||
value="{{ $fields['name']['font_color'] ?? '#1a3a6b' }}">
|
value="{{ $fields['name']['font_color'] ?? '#1a3a6b' }}">
|
||||||
</div>
|
</div>
|
||||||
<div class="col-4">
|
<div class="col-4">
|
||||||
@@ -117,9 +187,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{-- Certificate No (optional) --}}
|
{{-- No. Sijil (pilihan) --}}
|
||||||
<div class="card border mb-4">
|
<div class="card border mb-4">
|
||||||
<div class="card-header py-2 bg-light d-flex justify-content-between">
|
<div class="card-header py-2 bg-light d-flex justify-content-between align-items-center">
|
||||||
<span class="fw-medium small">No. Sijil <span class="text-muted">(Pilihan)</span></span>
|
<span class="fw-medium small">No. Sijil <span class="text-muted">(Pilihan)</span></span>
|
||||||
<div class="form-check form-switch mb-0">
|
<div class="form-check form-switch mb-0">
|
||||||
<input class="form-check-input" type="checkbox" id="showCertNo"
|
<input class="form-check-input" type="checkbox" id="showCertNo"
|
||||||
@@ -128,15 +198,16 @@
|
|||||||
<label class="form-check-label small" for="showCertNo">Papar</label>
|
<label class="form-check-label small" for="showCertNo">Papar</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body py-3" id="certNoFields" {{ isset($fields['certificate_no']) ? '' : 'style=display:none' }}>
|
<div class="card-body py-3" id="certNoFields"
|
||||||
|
{{ isset($fields['certificate_no']) ? '' : 'style=display:none' }}>
|
||||||
<div class="row g-2">
|
<div class="row g-2">
|
||||||
<div class="col-4">
|
<div class="col-4">
|
||||||
<label class="form-label small">X</label>
|
<label class="form-label small">X (px)</label>
|
||||||
<input type="number" name="fields[certificate_no][x]" class="form-control form-control-sm"
|
<input type="number" name="fields[certificate_no][x]" class="form-control form-control-sm"
|
||||||
value="{{ $fields['certificate_no']['x'] ?? 800 }}">
|
value="{{ $fields['certificate_no']['x'] ?? 800 }}">
|
||||||
</div>
|
</div>
|
||||||
<div class="col-4">
|
<div class="col-4">
|
||||||
<label class="form-label small">Y</label>
|
<label class="form-label small">Y (px)</label>
|
||||||
<input type="number" name="fields[certificate_no][y]" class="form-control form-control-sm"
|
<input type="number" name="fields[certificate_no][y]" class="form-control form-control-sm"
|
||||||
value="{{ $fields['certificate_no']['y'] ?? 460 }}">
|
value="{{ $fields['certificate_no']['y'] ?? 460 }}">
|
||||||
</div>
|
</div>
|
||||||
@@ -147,7 +218,8 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-4">
|
<div class="col-4">
|
||||||
<label class="form-label small">Warna</label>
|
<label class="form-label small">Warna</label>
|
||||||
<input type="color" name="fields[certificate_no][font_color]" class="form-control form-control-color form-control-sm"
|
<input type="color" name="fields[certificate_no][font_color]"
|
||||||
|
class="form-control form-control-color form-control-sm"
|
||||||
value="{{ $fields['certificate_no']['font_color'] ?? '#555555' }}">
|
value="{{ $fields['certificate_no']['font_color'] ?? '#555555' }}">
|
||||||
</div>
|
</div>
|
||||||
<div class="col-4">
|
<div class="col-4">
|
||||||
@@ -168,9 +240,15 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
@else
|
</div><!-- /.row -->
|
||||||
{{-- Upload Form --}}
|
|
||||||
|
@else
|
||||||
|
|
||||||
|
{{-- Tiada template — form upload --}}
|
||||||
|
<div class="row g-4">
|
||||||
|
<div class="col-lg-7">
|
||||||
<div class="card border-0 shadow-sm">
|
<div class="card border-0 shadow-sm">
|
||||||
<div class="card-header bg-white py-3">
|
<div class="card-header bg-white py-3">
|
||||||
<h6 class="mb-0 fw-semibold"><i class="bi bi-upload me-2 text-primary"></i>Muat Naik Template Sijil</h6>
|
<h6 class="mb-0 fw-semibold"><i class="bi bi-upload me-2 text-primary"></i>Muat Naik Template Sijil</h6>
|
||||||
@@ -178,11 +256,13 @@
|
|||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div class="alert alert-info small mb-4">
|
<div class="alert alert-info small mb-4">
|
||||||
<i class="bi bi-lightbulb me-1"></i>
|
<i class="bi bi-lightbulb me-1"></i>
|
||||||
Muat naik imej template sijil dalam format <strong>JPG atau PNG</strong>.
|
Format <strong>JPG atau PNG</strong>, maksimum <strong>10MB</strong>.
|
||||||
Saiz maksimum: <strong>10MB</strong>. Resolusi disyorkan: <strong>1754 × 1240px</strong> (A4 landscape 150dpi).
|
Resolusi disyorkan: <strong>1754 × 1240px</strong> (A4 landscape) atau
|
||||||
|
<strong>1240 × 1754px</strong> (portrait).
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form method="POST" action="{{ route('admin.programs.template.store', $program) }}" enctype="multipart/form-data">
|
<form method="POST" action="{{ route('admin.programs.template.store', $program) }}"
|
||||||
|
enctype="multipart/form-data">
|
||||||
@csrf
|
@csrf
|
||||||
<div class="mb-4">
|
<div class="mb-4">
|
||||||
<label class="form-label fw-medium">Fail Template <span class="text-danger">*</span></label>
|
<label class="form-label fw-medium">Fail Template <span class="text-danger">*</span></label>
|
||||||
@@ -195,7 +275,7 @@
|
|||||||
|
|
||||||
<div id="imagePreviewBox" class="mb-4 d-none">
|
<div id="imagePreviewBox" class="mb-4 d-none">
|
||||||
<label class="form-label small text-muted">Pratonton:</label>
|
<label class="form-label small text-muted">Pratonton:</label>
|
||||||
<div class="border rounded overflow-hidden">
|
<div id="uploadPreviewWrapper" class="border rounded overflow-hidden">
|
||||||
<img id="imagePreviewEl" src="" alt="preview" class="img-fluid w-100">
|
<img id="imagePreviewEl" src="" alt="preview" class="img-fluid w-100">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -206,46 +286,79 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@endif
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{{-- Right: Tips --}}
|
|
||||||
<div class="col-md-5">
|
|
||||||
<div class="card border-0 bg-light">
|
|
||||||
<div class="card-body p-4">
|
|
||||||
<h6 class="fw-semibold mb-3"><i class="bi bi-info-circle me-2 text-primary"></i>Panduan Template</h6>
|
|
||||||
<ul class="small text-muted mb-0">
|
|
||||||
<li class="mb-2">Gunakan imej resolusi tinggi (≥1600px lebar) untuk hasil cetak berkualiti.</li>
|
|
||||||
<li class="mb-2">Pastikan ruang untuk nama peserta tidak dihalang oleh grafik template.</li>
|
|
||||||
<li class="mb-2">Koordinat (0,0) adalah sudut <strong>kiri atas</strong> imej.</li>
|
|
||||||
<li class="mb-2">Gunakan butang <strong>Pratonton</strong> untuk menyemak kedudukan teks sebelum jana sijil sebenar.</li>
|
|
||||||
<li class="mb-0">Sijil dijana dalam format JPG.</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@endif
|
||||||
|
|
||||||
@endsection
|
@endsection
|
||||||
|
|
||||||
@push('scripts')
|
@push('scripts')
|
||||||
<script>
|
<script>
|
||||||
function previewImage(input) {
|
// ── Auto-detect orientasi dan laras tinggi viewer ─────────────────────────────
|
||||||
if (input.files && input.files[0]) {
|
function applyOrientation(naturalW, naturalH) {
|
||||||
const reader = new FileReader();
|
const wrapper = document.getElementById('previewWrapper');
|
||||||
reader.onload = e => {
|
const img = document.getElementById('templatePreview');
|
||||||
document.getElementById('imagePreviewEl').src = e.target.result;
|
const badge = document.getElementById('orientationBadge');
|
||||||
document.getElementById('imagePreviewBox').classList.remove('d-none');
|
if (!wrapper || !img) return;
|
||||||
};
|
|
||||||
reader.readAsDataURL(input.files[0]);
|
const isPortrait = naturalH > naturalW;
|
||||||
|
const maxH = isPortrait ? '520px' : '340px';
|
||||||
|
|
||||||
|
wrapper.style.maxHeight = maxH;
|
||||||
|
img.style.maxHeight = maxH;
|
||||||
|
|
||||||
|
if (badge) {
|
||||||
|
badge.textContent = isPortrait ? 'Portrait' : 'Landscape';
|
||||||
|
badge.className = 'badge ' + (isPortrait ? 'bg-info' : 'bg-success');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Jalankan sekali apabila imej template dimuatkan
|
||||||
|
const templateImg = document.getElementById('templatePreview');
|
||||||
|
if (templateImg) {
|
||||||
|
if (templateImg.complete && templateImg.naturalWidth) {
|
||||||
|
applyOrientation(templateImg.naturalWidth, templateImg.naturalHeight);
|
||||||
|
} else {
|
||||||
|
templateImg.addEventListener('load', function () {
|
||||||
|
applyOrientation(this.naturalWidth, this.naturalHeight);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Upload pratonton (form muat naik) ─────────────────────────────────────────
|
||||||
|
function previewImage(input) {
|
||||||
|
if (!input.files || !input.files[0]) return;
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.onload = e => {
|
||||||
|
const el = document.getElementById('imagePreviewEl');
|
||||||
|
const box = document.getElementById('imagePreviewBox');
|
||||||
|
el.src = e.target.result;
|
||||||
|
box.classList.remove('d-none');
|
||||||
|
|
||||||
|
// Laras pratonton upload ikut orientasi
|
||||||
|
el.onload = function () {
|
||||||
|
const wrap = document.getElementById('uploadPreviewWrapper');
|
||||||
|
if (wrap) wrap.style.maxHeight = this.naturalHeight > this.naturalWidth ? '520px' : '340px';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
reader.readAsDataURL(input.files[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Toggle No. Sijil ──────────────────────────────────────────────────────────
|
||||||
function toggleCertNo(cb) {
|
function toggleCertNo(cb) {
|
||||||
document.getElementById('certNoFields').style.display = cb.checked ? '' : 'none';
|
document.getElementById('certNoFields').style.display = cb.checked ? '' : 'none';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── Toggle panduan (ikon chevron) ─────────────────────────────────────────────
|
||||||
|
document.getElementById('guidePanel')?.addEventListener('show.bs.collapse', () => {
|
||||||
|
document.getElementById('guideChevron').className = 'bi bi-chevron-up small text-muted';
|
||||||
|
});
|
||||||
|
document.getElementById('guidePanel')?.addEventListener('hide.bs.collapse', () => {
|
||||||
|
document.getElementById('guideChevron').className = 'bi bi-chevron-down small text-muted';
|
||||||
|
});
|
||||||
|
|
||||||
|
// ── Jana Pratonton ────────────────────────────────────────────────────────────
|
||||||
function loadPreview() {
|
function loadPreview() {
|
||||||
const name = document.getElementById('sampleName').value.trim() || 'NAMA PESERTA CONTOH';
|
const name = document.getElementById('sampleName').value.trim() || 'NAMA PESERTA CONTOH';
|
||||||
const img = document.getElementById('templatePreview');
|
const img = document.getElementById('templatePreview');
|
||||||
|
|||||||
Reference in New Issue
Block a user