feat: auto-generate PDF after second validation - Trigger PDF generation via /report/{accessnumber}/pdf endpoint after VAL2 - Add preview dialog for admin/lab/superuser roles - Update role configs with previewEnabled flag
This commit is contained in:
parent
dcb09804f5
commit
9e374103fa
12
TODO.md
12
TODO.md
@ -1,9 +1,13 @@
|
|||||||
# Project Checklist: Glen RME & Lab Management System
|
# Project Checklist: Glen RME & Lab Management System
|
||||||
|
|
||||||
**Last Updated:** 20260205
|
**Last Updated:** 20260212
|
||||||
|
|
||||||
Pending:
|
Pending:
|
||||||
- Test and fix Reprint label
|
- preview result for validate for su adm lab
|
||||||
|
- auto generate pdf after 2 val
|
||||||
|
- add datetime val1 val2
|
||||||
|
- sample other for MCU is annoying
|
||||||
|
- report2 go to cmod
|
||||||
|
|
||||||
Completed:
|
Completed:
|
||||||
- Update User Role levels (Standardize roles: Superuser, Admin, Lab, Phlebo, CS)
|
- Update User Role levels (Standardize roles: Superuser, Admin, Lab, Phlebo, CS)
|
||||||
@ -35,4 +39,6 @@ Completed:
|
|||||||
- Reprint Label (Add functionality to reprint labels)
|
- Reprint Label (Add functionality to reprint labels)
|
||||||
- Create Eng Result UI UX on request dashboard
|
- Create Eng Result UI UX on request dashboard
|
||||||
- Test and fix PDF Generation
|
- Test and fix PDF Generation
|
||||||
- Print Result Audit (Track when result reports are printed/exported, log user and timestamp)
|
- Print Result Audit (Track when result reports are printed/exported, log user and timestamp)
|
||||||
|
- Test and fix Reprint label
|
||||||
|
- fasten the load of val page
|
||||||
@ -234,8 +234,17 @@
|
|||||||
<tr><td>TC-071</td><td>Concurrent Validation ⚡</td><td>2 user buka validation dialog bersamaan</td><td>Validasi berhasil, tidak conflict ✅</td><td>FITUR CROSS-ROLE 🤝</td><td><input type="checkbox"> ✅ <br> <input type="checkbox"> ❌</td><td><input type="text" value="___________"></td></tr>
|
<tr><td>TC-071</td><td>Concurrent Validation ⚡</td><td>2 user buka validation dialog bersamaan</td><td>Validasi berhasil, tidak conflict ✅</td><td>FITUR CROSS-ROLE 🤝</td><td><input type="checkbox"> ✅ <br> <input type="checkbox"> ❌</td><td><input type="text" value="___________"></td></tr>
|
||||||
<tr><td>TC-072</td><td>Concurrent Sample Collection ⚡</td><td>2 user collect tube berbeda bersamaan</td><td>Semua berhasil tercatat ✅</td><td>FITUR CROSS-ROLE 🧪</td><td><input type="checkbox"> ✅ <br> <input type="checkbox"> ❌</td><td><input type="text" value="___________"></td></tr>
|
<tr><td>TC-072</td><td>Concurrent Sample Collection ⚡</td><td>2 user collect tube berbeda bersamaan</td><td>Semua berhasil tercatat ✅</td><td>FITUR CROSS-ROLE 🧪</td><td><input type="checkbox"> ✅ <br> <input type="checkbox"> ❌</td><td><input type="text" value="___________"></td></tr>
|
||||||
<tr><td>TC-073</td><td>Session Timeout ⏱️</td><td>Tunggu session timeout</td><td>Redirect ke login 🔄</td><td>FITUR CROSS-ROLE 🔐</td><td><input type="checkbox"> ✅ <br> <input type="checkbox"> ❌</td><td><input type="text" value="___________"></td></tr>
|
<tr><td>TC-073</td><td>Session Timeout ⏱️</td><td>Tunggu session timeout</td><td>Redirect ke login 🔄</td><td>FITUR CROSS-ROLE 🔐</td><td><input type="checkbox"> ✅ <br> <input type="checkbox"> ❌</td><td><input type="text" value="___________"></td></tr>
|
||||||
<tr><td>TC-074</td><td>Print Labels 🏷️</td><td>Print individual/collection/all</td><td>Semua labels tercetak 🖨️</td><td>ADM, LAB, PHLEB</td><td><input type="checkbox"> ✅ <br> <input type="checkbox"> ❌</td><td><input type="text" value="___________"></td></tr>
|
<tr><td>TC-074</td><td>Password Hashing Security 🔒</td><td>Buat user → cek database</td><td>Password dalam HASH bukan plain 🛡️</td><td>FITUR CROSS-ROLE 🔐</td><td><input type="checkbox"> ✅ <br> <input type="checkbox"> ❌</td><td><input type="text" value="___________"></td></tr>
|
||||||
<tr><td>TC-075</td><td>Edit Comment ✏️</td><td>Edit comment di dashboard</td><td>Comment berubah tersimpan 💾</td><td>SU</td><td><input type="checkbox"> ✅ <br> <input type="checkbox"> ❌</td><td><input type="text" value="___________"></td></tr>
|
<tr><td>TC-075</td><td>Legacy Read-Only 👀</td><td>Cek koneksi & fungsi Firebird</td><td>Hanya READ dari Firebird, TIDAK WRITE 🚫✍️</td><td>FITUR CROSS-ROLE 🗄️</td><td><input type="checkbox"> ✅ <br> <input type="checkbox"> ❌</td><td><input type="text" value="___________"></td></tr>
|
||||||
|
<tr><td>TC-076</td><td>Print Labels 🏷️</td><td>Print individual/collection/all</td><td>Semua labels tercetak 🖨️</td><td>ADM, LAB, PHLEB</td><td><input type="checkbox"> ✅ <br> <input type="checkbox"> ❌</td><td><input type="text" value="___________"></td></tr>
|
||||||
|
<tr><td>TC-077</td><td>Edit Comment ✏️</td><td>Edit comment di dashboard</td><td>Comment berubah tersimpan 💾</td><td>SU</td><td><input type="checkbox"> ✅ <br> <input type="checkbox"> ❌</td><td><input type="text" value="___________"></td></tr>
|
||||||
|
</table>
|
||||||
|
<h2>📋 Next Plan 📋</h2>
|
||||||
|
<table class="data-table">
|
||||||
|
<tr><th>ID</th><th>Judul Test Case</th><th>Langkah Utama</th><th>Expected Result</th><th>Role</th><th>Hasil</th><th>Issue/Jawaban</th></tr>
|
||||||
|
<tr><td>NP-001</td><td>Collect Sample 🧪</td><td>Buka dialog sample → Collect</td><td>STATUS=1, COLLECTIONDATE & USERID diset ✅</td><td>SU, ADM, LAB</td><td><input type="checkbox"> ✅ <br> <input type="checkbox"> ❌</td><td><input type="text" value="___________"></td></tr>
|
||||||
|
<tr><td>NP-002</td><td>Un-Collect Sample ↩️</td><td>Buka dialog sample → Un-Collect</td><td>STATUS di-reset, audit log tercatat 📝</td><td>SU, ADM, LAB</td><td><input type="checkbox"> ✅ <br> <input type="checkbox"> ❌</td><td><input type="text" value="___________"></td></tr>
|
||||||
|
<tr><td>NP-004</td><td>Sample Collection Buttons Enabled ✅</td><td>Buka dialog sample</td><td>Tombol Collect/Un-Coll/Un-Recv enabled 🔘</td><td>ADMIN</td><td><input type="checkbox"> ✅ <br> <input type="checkbox"> ❌</td><td><input type="text" value="___________"></td></tr>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@ -11,6 +11,7 @@ $roleConfig = $config['admin'];
|
|||||||
<?= $this->include('shared/dialog_unval'); ?>
|
<?= $this->include('shared/dialog_unval'); ?>
|
||||||
<?= $this->include('shared/dialog_audit'); ?>
|
<?= $this->include('shared/dialog_audit'); ?>
|
||||||
<?= $this->include('shared/dialog_results_generate'); ?>
|
<?= $this->include('shared/dialog_results_generate'); ?>
|
||||||
|
<?= $this->include('shared/dialog_preview'); ?>
|
||||||
</main>
|
</main>
|
||||||
<?= $this->endSection(); ?>
|
<?= $this->endSection(); ?>
|
||||||
|
|
||||||
|
|||||||
@ -11,6 +11,7 @@ $roleConfig = $config['lab'];
|
|||||||
<?= $this->include('shared/dialog_unval'); ?>
|
<?= $this->include('shared/dialog_unval'); ?>
|
||||||
<?= $this->include('shared/dialog_audit'); ?>
|
<?= $this->include('shared/dialog_audit'); ?>
|
||||||
<?= $this->include('shared/dialog_results_generate'); ?>
|
<?= $this->include('shared/dialog_results_generate'); ?>
|
||||||
|
<?= $this->include('shared/dialog_preview'); ?>
|
||||||
</main>
|
</main>
|
||||||
<?= $this->endSection(); ?>
|
<?= $this->endSection(); ?>
|
||||||
|
|
||||||
|
|||||||
@ -19,6 +19,7 @@
|
|||||||
return [
|
return [
|
||||||
'admin' => [
|
'admin' => [
|
||||||
'title' => 'Admin Dashboard',
|
'title' => 'Admin Dashboard',
|
||||||
|
'previewEnabled' => true,
|
||||||
'sampleDialog' => [
|
'sampleDialog' => [
|
||||||
'commentEditable' => true,
|
'commentEditable' => true,
|
||||||
'showCollectButtons' => true,
|
'showCollectButtons' => true,
|
||||||
@ -31,6 +32,7 @@ return [
|
|||||||
],
|
],
|
||||||
'cs' => [
|
'cs' => [
|
||||||
'title' => 'CS Dashboard',
|
'title' => 'CS Dashboard',
|
||||||
|
'previewEnabled' => false,
|
||||||
'sampleDialog' => [
|
'sampleDialog' => [
|
||||||
'commentEditable' => false,
|
'commentEditable' => false,
|
||||||
'showCollectButtons' => false,
|
'showCollectButtons' => false,
|
||||||
@ -42,6 +44,7 @@ return [
|
|||||||
],
|
],
|
||||||
'lab' => [
|
'lab' => [
|
||||||
'title' => 'Lab Analyst Dashboard',
|
'title' => 'Lab Analyst Dashboard',
|
||||||
|
'previewEnabled' => true,
|
||||||
'sampleDialog' => [
|
'sampleDialog' => [
|
||||||
'commentEditable' => true,
|
'commentEditable' => true,
|
||||||
'showCollectButtons' => true,
|
'showCollectButtons' => true,
|
||||||
@ -54,6 +57,7 @@ return [
|
|||||||
],
|
],
|
||||||
'phlebo' => [
|
'phlebo' => [
|
||||||
'title' => 'Phlebotomist Dashboard',
|
'title' => 'Phlebotomist Dashboard',
|
||||||
|
'previewEnabled' => false,
|
||||||
'sampleDialog' => [
|
'sampleDialog' => [
|
||||||
'commentEditable' => false,
|
'commentEditable' => false,
|
||||||
'showCollectButtons' => true,
|
'showCollectButtons' => true,
|
||||||
@ -66,6 +70,7 @@ return [
|
|||||||
],
|
],
|
||||||
'superuser' => [
|
'superuser' => [
|
||||||
'title' => 'Superuser Dashboard',
|
'title' => 'Superuser Dashboard',
|
||||||
|
'previewEnabled' => true,
|
||||||
'sampleDialog' => [
|
'sampleDialog' => [
|
||||||
'commentEditable' => true,
|
'commentEditable' => true,
|
||||||
'showCollectButtons' => true,
|
'showCollectButtons' => true,
|
||||||
|
|||||||
@ -206,7 +206,32 @@
|
|||||||
<p>2: <span x-text="req.VAL2USER"></span></p>
|
<p>2: <span x-text="req.VAL2USER"></span></p>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
|
<?php
|
||||||
|
$configFile = include __DIR__ . '/config.php';
|
||||||
|
$roleMap = ['superuser' => 'superuser', 'admin' => 'admin', 'lab analyst' => 'lab', 'phlebotomist' => 'phlebo', 'customer service' => 'cs'];
|
||||||
|
$userRole = strtolower(session('userrole') ?? '');
|
||||||
|
$configKey = $roleMap[$userRole] ?? '';
|
||||||
|
$previewEnabled = $configFile[$configKey]['previewEnabled'] ?? false;
|
||||||
|
?>
|
||||||
<td>
|
<td>
|
||||||
|
<?php if ($previewEnabled): ?>
|
||||||
|
<template x-if="!req.VAL1USER || !req.VAL2USER">
|
||||||
|
<button @click="openPreviewDialog(req)"
|
||||||
|
class="btn btn-xs w-full btn-warning">
|
||||||
|
<i class="fa fa-clock mr-1"></i>
|
||||||
|
<span class="text-xs">Preview</span>
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
|
<template x-if="req.VAL1USER && req.VAL2USER">
|
||||||
|
<div class="dropdown dropdown-end dropdown-hover">
|
||||||
|
<div tabindex="0" role="button"
|
||||||
|
class="btn btn-xs w-full btn-success text-white">
|
||||||
|
<i class="fa fa-clipboard-check mr-1"></i>
|
||||||
|
<span class="text-xs">Ready</span>
|
||||||
|
</div>
|
||||||
|
<ul tabindex="0"
|
||||||
|
class="dropdown-content menu bg-base-100 rounded-box z-[1] w-40 p-2 shadow-lg border border-base-300">
|
||||||
|
<?php else: ?>
|
||||||
<div class="dropdown dropdown-end dropdown-hover">
|
<div class="dropdown dropdown-end dropdown-hover">
|
||||||
<div tabindex="0" role="button"
|
<div tabindex="0" role="button"
|
||||||
class="btn btn-xs w-full"
|
class="btn btn-xs w-full"
|
||||||
@ -216,6 +241,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<ul tabindex="0"
|
<ul tabindex="0"
|
||||||
class="dropdown-content menu bg-base-100 rounded-box z-[1] w-40 p-2 shadow-lg border border-base-300">
|
class="dropdown-content menu bg-base-100 rounded-box z-[1] w-40 p-2 shadow-lg border border-base-300">
|
||||||
|
<?php endif; ?>
|
||||||
<template x-if="req.VAL1USER && req.VAL2USER">
|
<template x-if="req.VAL1USER && req.VAL2USER">
|
||||||
<div>
|
<div>
|
||||||
<li>
|
<li>
|
||||||
@ -236,6 +262,7 @@
|
|||||||
</li>
|
</li>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
<?php if (!$previewEnabled): ?>
|
||||||
<template x-if="!req.VAL1USER || !req.VAL2USER">
|
<template x-if="!req.VAL1USER || !req.VAL2USER">
|
||||||
<div>
|
<div>
|
||||||
<li class="disabled opacity-50 cursor-not-allowed">
|
<li class="disabled opacity-50 cursor-not-allowed">
|
||||||
@ -245,8 +272,13 @@
|
|||||||
</li>
|
</li>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
<?php endif; ?>
|
||||||
</ul>
|
</ul>
|
||||||
|
<?php if ($previewEnabled): ?>
|
||||||
|
</template>
|
||||||
|
<?php else: ?>
|
||||||
</div>
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<div class="dropdown dropdown-end dropdown-hover">
|
<div class="dropdown dropdown-end dropdown-hover">
|
||||||
|
|||||||
41
app/Views/shared/dialog_preview.php
Normal file
41
app/Views/shared/dialog_preview.php
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
<dialog class="modal" :open="isDialogPreviewOpen" @keydown.escape="closePreviewDialog()">
|
||||||
|
<div class="modal-box w-2/3 max-w-5xl" x-trap.noreturn="isDialogPreviewOpen">
|
||||||
|
<!-- Request info header -->
|
||||||
|
<div class="bg-base-200 p-3 rounded mb-3">
|
||||||
|
<div class="grid grid-cols-4 gap-2 text-sm">
|
||||||
|
<div>Access#: <span x-text="previewAccessnumber" class="font-mono font-bold"></span></div>
|
||||||
|
<div>Patient: <span x-text="previewItem?.PATNAME || previewItem?.Name"></span></div>
|
||||||
|
<div>MRN: <span x-text="previewItem?.PATNUMBER?.substring(14) || previewItem?.PATNUMBER"></span></div>
|
||||||
|
<div>Tests: <span x-text="(previewItem?.TESTS || previewItem?.TESTNAMES || '').substring(0,40) + '...'"></span></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex justify-between items-center mb-2">
|
||||||
|
<h3 class="font-bold text-lg">Preview Result</h3>
|
||||||
|
<button class="btn btn-sm btn-ghost" @click="closePreviewDialog()" aria-label="Close">
|
||||||
|
<i class="fa fa-times"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p class="mb-2 flex gap-2">
|
||||||
|
<button id="preview-validate-btn" class="btn btn-sm btn-success"
|
||||||
|
@click="validateFromPreview(previewAccessnumber, '<?=session('userid');?>')"
|
||||||
|
:disabled="!isPreviewIframeLoaded || isPreviewValidating">
|
||||||
|
<span x-text="isPreviewValidating ? 'Validating...' : 'Validate'"></span>
|
||||||
|
<span x-show="isPreviewValidating" class="loading loading-spinner loading-xs ml-1"></span>
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-sm btn-ghost" @click="closePreviewDialog()">
|
||||||
|
Close (Esc)
|
||||||
|
</button>
|
||||||
|
</p>
|
||||||
|
<iframe id="preview-iframe" x-ref="previewIframe" :src="getPreviewUrl()" @load="onPreviewIframeLoad()" width="100%" height="500px"
|
||||||
|
class="border border-base-300 rounded"></iframe>
|
||||||
|
|
||||||
|
<!-- Loading overlay -->
|
||||||
|
<template x-if="isPreviewValidating">
|
||||||
|
<div class="absolute inset-0 bg-base-100/80 flex items-center justify-center z-10 rounded-box">
|
||||||
|
<span class="loading loading-spinner loading-lg text-success"></span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</dialog>
|
||||||
@ -414,6 +414,60 @@ document.addEventListener('alpine:init', () => {
|
|||||||
.catch(() => this.showToast('Print failed', 'error'));
|
.catch(() => this.showToast('Print failed', 'error'));
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/*
|
||||||
|
preview dialog methods
|
||||||
|
*/
|
||||||
|
isDialogPreviewOpen: false,
|
||||||
|
previewAccessnumber: null,
|
||||||
|
previewItem: null,
|
||||||
|
isPreviewIframeLoaded: false,
|
||||||
|
isPreviewValidating: false,
|
||||||
|
openPreviewDialog(item) {
|
||||||
|
this.previewItem = item;
|
||||||
|
this.previewAccessnumber = item.SP_ACCESSNUMBER;
|
||||||
|
this.isPreviewIframeLoaded = false;
|
||||||
|
this.isPreviewValidating = false;
|
||||||
|
this.isDialogPreviewOpen = true;
|
||||||
|
},
|
||||||
|
closePreviewDialog() {
|
||||||
|
this.isDialogPreviewOpen = false;
|
||||||
|
this.previewItem = null;
|
||||||
|
this.previewAccessnumber = null;
|
||||||
|
this.isPreviewIframeLoaded = false;
|
||||||
|
},
|
||||||
|
getPreviewUrl() {
|
||||||
|
return `${BASEURL}/report/${this.previewAccessnumber}`;
|
||||||
|
},
|
||||||
|
onPreviewIframeLoad() {
|
||||||
|
this.isPreviewIframeLoaded = true;
|
||||||
|
},
|
||||||
|
validateFromPreview(accessnumber, userid) {
|
||||||
|
if (!this.isPreviewIframeLoaded || this.isPreviewValidating) return;
|
||||||
|
|
||||||
|
this.isPreviewValidating = true;
|
||||||
|
fetch(`${BASEURL}/api/requests/validate/${accessnumber}`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' }
|
||||||
|
})
|
||||||
|
.then(res => res.json())
|
||||||
|
.then(data => {
|
||||||
|
this.isPreviewValidating = false;
|
||||||
|
if (data.val) {
|
||||||
|
this.showToast(`Validated (val${data.val}): ${accessnumber}`, 'success');
|
||||||
|
this.fetchList();
|
||||||
|
this.closePreviewDialog();
|
||||||
|
} else if (data.message && data.message.includes('already validate')) {
|
||||||
|
this.showToast('You have already validated this request', 'error');
|
||||||
|
} else {
|
||||||
|
this.showToast(data.message || 'Validation failed', 'error');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
this.isPreviewValidating = false;
|
||||||
|
this.showToast('Validation failed', 'error');
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
showToast(message, type = 'success') {
|
showToast(message, type = 'success') {
|
||||||
const toast = document.createElement('div');
|
const toast = document.createElement('div');
|
||||||
toast.className = `alert alert-${type} fixed top-4 right-4 z-50`;
|
toast.className = `alert alert-${type} fixed top-4 right-4 z-50`;
|
||||||
|
|||||||
@ -212,6 +212,16 @@ document.addEventListener('alpine:init', () => {
|
|||||||
}).then(response => response.json()).then(data => {
|
}).then(response => response.json()).then(data => {
|
||||||
if (data.val === 2) {
|
if (data.val === 2) {
|
||||||
this.showToast(`Validated (val2): ${accessnumber} - PDF queued`);
|
this.showToast(`Validated (val2): ${accessnumber} - PDF queued`);
|
||||||
|
// Trigger PDF auto-generation after val2
|
||||||
|
fetch(`${BASEURL}/report/${accessnumber}/pdf`).then(res => res.json()).then(pdfData => {
|
||||||
|
if (pdfData.success) {
|
||||||
|
console.log('PDF generation queued:', pdfData.jobId);
|
||||||
|
} else {
|
||||||
|
console.error('PDF generation failed:', pdfData.error);
|
||||||
|
}
|
||||||
|
}).catch(err => {
|
||||||
|
console.error('PDF generation request failed:', err);
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
this.showToast(`Validated: ${accessnumber}`);
|
this.showToast(`Validated: ${accessnumber}`);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,8 @@
|
|||||||
<?= $this->extend('shared/layout'); ?>
|
<?php
|
||||||
|
$config = include __DIR__ . '/../shared/config.php';
|
||||||
|
$roleConfig = $config['superuser'];
|
||||||
|
?>
|
||||||
|
<?= $this->extend('shared/layout', ['roleConfig' => $roleConfig]); ?>
|
||||||
|
|
||||||
<?= $this->section('content'); ?>
|
<?= $this->section('content'); ?>
|
||||||
<main class="p-4 flex-1 flex flex-col gap-2 overflow-hidden min-h-0" x-data="dashboard">
|
<main class="p-4 flex-1 flex flex-col gap-2 overflow-hidden min-h-0" x-data="dashboard">
|
||||||
@ -7,6 +11,7 @@
|
|||||||
<?= $this->include('shared/dialog_unval'); ?>
|
<?= $this->include('shared/dialog_unval'); ?>
|
||||||
<?= $this->include('shared/dialog_audit'); ?>
|
<?= $this->include('shared/dialog_audit'); ?>
|
||||||
<?= $this->include('shared/dialog_results_generate'); ?>
|
<?= $this->include('shared/dialog_results_generate'); ?>
|
||||||
|
<?= $this->include('shared/dialog_preview'); ?>
|
||||||
</main>
|
</main>
|
||||||
<?= $this->endSection(); ?>
|
<?= $this->endSection(); ?>
|
||||||
|
|
||||||
|
|||||||
@ -1,11 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>403 Forbidden</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<p>Directory access is forbidden.</p>
|
|
||||||
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
Loading…
x
Reference in New Issue
Block a user