feat: Docker Compose setup untuk development & production
- docker/php/Dockerfile: PHP 8.4-FPM + GD + imagick (PECL) + semua extension Laravel - docker/php/php.ini: upload 20MB, memory 512MB, opcache, Asia/Kuala_Lumpur - docker/php/php-dev.ini: validate_timestamps=1, display_errors=On (dev) - docker/nginx/default.conf: gzip, security headers, static asset caching - docker/entrypoint.sh: tunggu MySQL → migrate → seed AdminSeeder → cache (prod) - docker-compose.yml: dev stack — port 8003, DB host 33060, queue worker - docker-compose.prod.yml: production overrides — storage volume, no DB port exposed - .env.docker: template env untuk Docker (DB_HOST=db) - .dockerignore: exclude node_modules, vendor, .env, logs fix: testGenerate try/catch kembalikan JSON error (bukan HTML 500) fix: loadPreview() semak r.ok, tunjuk error alert, loading spinner Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -54,7 +54,7 @@
|
||||
value="NAMA PESERTA CONTOH" placeholder="Nama untuk pratonton">
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<button type="button" 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
|
||||
</button>
|
||||
</div>
|
||||
@@ -247,18 +247,34 @@ function toggleCertNo(cb) {
|
||||
}
|
||||
|
||||
function loadPreview() {
|
||||
const name = document.getElementById('sampleName').value || 'NAMA PESERTA CONTOH';
|
||||
const name = document.getElementById('sampleName').value.trim() || 'NAMA PESERTA CONTOH';
|
||||
const img = document.getElementById('templatePreview');
|
||||
const btn = document.getElementById('previewBtn');
|
||||
const url = "{{ route('admin.programs.template.test', $program) }}";
|
||||
|
||||
btn.disabled = true;
|
||||
btn.innerHTML = '<span class="spinner-border spinner-border-sm me-1" role="status"></span> Memuatkan...';
|
||||
|
||||
const form = new FormData();
|
||||
form.append('_token', '{{ csrf_token() }}');
|
||||
form.append('sample_name', name);
|
||||
|
||||
fetch(url, { method: 'POST', body: form })
|
||||
.then(r => r.blob())
|
||||
.then(r => {
|
||||
if (!r.ok) return r.json().then(j => { throw new Error(j.error || 'Ralat pelayan (' + r.status + ')'); });
|
||||
return r.blob();
|
||||
})
|
||||
.then(blob => {
|
||||
const prevSrc = img.src;
|
||||
img.src = URL.createObjectURL(blob);
|
||||
if (prevSrc.startsWith('blob:')) URL.revokeObjectURL(prevSrc);
|
||||
})
|
||||
.catch(err => {
|
||||
alert('Gagal jana pratonton: ' + err.message);
|
||||
})
|
||||
.finally(() => {
|
||||
btn.disabled = false;
|
||||
btn.innerHTML = '<i class="bi bi-eye me-1"></i> Pratonton';
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user