Commit 6fb137f8 authored by Mahmoud Aglan's avatar Mahmoud Aglan

fixes

parent 055dd94f
......@@ -253,7 +253,9 @@ class BookingWizardController extends Controller
{
$this->authorize('sa.booking_wizard.use');
$errors = $this->validate($request->allPost(), [
$data = $request->all();
$validator = new \App\Core\Validator();
$result = $validator->validate($data, [
'unit_id' => 'required|integer',
'date' => 'required|date',
'start_time' => 'required|string',
......@@ -261,7 +263,7 @@ class BookingWizardController extends Controller
'participants' => 'required|integer|min:1',
'booker_name' => 'required|string|min:2|max:200',
]);
if ($errors) {
if ($result->fails()) {
return $this->json(['success' => false, 'error' => 'بيانات الحجز غير مكتملة']);
}
......
......@@ -197,7 +197,9 @@ $capacityColor = $capacityPct >= 90 ? '#DC2626' : ($capacityPct >= 70 ? '#D97706
<?= csrf_field() ?>
<div style="flex:1;min-width:200px;">
<label class="form-label" style="font-size:11px;">إضافة لاعب</label>
<select name="player_id" class="form-input" style="font-size:12px;" required id="addPlayerSelect">
<select name="player_id" class="form-select" style="font-size:12px;" required id="addPlayerSelect"
data-searchable="true"
data-placeholder="ابحث عن لاعب بالاسم...">
<option value="">-- اختر لاعب --</option>
<?php foreach ($players as $p): ?>
<option value="<?= (int) $p['player_id'] ?>" data-enrollment="<?= (int) $p['enrollment_id'] ?>"><?= e($p['player_name']) ?></option>
......
......@@ -558,7 +558,7 @@ class TutorialController extends Controller
public function exportPdf(Request $request): Response
{
set_time_limit(1800);
set_time_limit(600);
$dlToken = $_GET['dl'] ?? '';
......@@ -572,6 +572,8 @@ class TutorialController extends Controller
include __DIR__ . '/../Views/export_pdf.php';
$html = ob_get_clean();
$html = $this->inlineImages($html);
$wkhtmltopdf = null;
foreach (['/usr/local/bin/wkhtmltopdf', '/usr/bin/wkhtmltopdf'] as $path) {
if (file_exists($path) && is_executable($path)) {
......@@ -591,10 +593,11 @@ class TutorialController extends Controller
$cmd = escapeshellarg($wkhtmltopdf)
. ' --encoding utf-8 --page-size A4'
. ' --margin-top 15 --margin-bottom 15 --margin-left 12 --margin-right 12'
. ' --enable-local-file-access'
. ' --disable-local-file-access'
. ' --disable-javascript'
. ' --no-stop-slow-scripts'
. ' --javascript-delay 200'
. ' --image-quality 85'
. ' --image-quality 70'
. ' --image-dpi 150'
. ' --print-media-type'
. ' ' . escapeshellarg($tmpInput)
. ' ' . escapeshellarg($tmpOutput)
......@@ -710,6 +713,79 @@ class TutorialController extends Controller
return $book;
}
private function inlineImages(string $html): string
{
return (string) preg_replace_callback(
'#src="file://([^"]+\.(png|jpg|jpeg|gif|webp))"#i',
function (array $m): string {
$path = $m[1];
if (!file_exists($path)) {
return $m[0];
}
$resized = $this->resizeForPdf($path);
$mime = match (strtolower($m[2])) {
'jpg', 'jpeg' => 'image/jpeg',
'gif' => 'image/gif',
'webp' => 'image/webp',
default => 'image/png',
};
return 'src="data:' . $mime . ';base64,' . base64_encode($resized) . '"';
},
$html
);
}
private function resizeForPdf(string $path): string
{
$info = @getimagesize($path);
if (!$info) {
return (string) file_get_contents($path);
}
[$origW, $origH, $type] = $info;
$maxW = 1200;
if ($origW <= $maxW) {
$img = match ($type) {
IMAGETYPE_PNG => @imagecreatefrompng($path),
IMAGETYPE_JPEG => @imagecreatefromjpeg($path),
IMAGETYPE_GIF => @imagecreatefromgif($path),
IMAGETYPE_WEBP => @imagecreatefromwebp($path),
default => null,
};
if (!$img) {
return (string) file_get_contents($path);
}
ob_start();
imagejpeg($img, null, 65);
imagedestroy($img);
return (string) ob_get_clean();
}
$newW = $maxW;
$newH = (int) round($origH * ($maxW / $origW));
$src = match ($type) {
IMAGETYPE_PNG => @imagecreatefrompng($path),
IMAGETYPE_JPEG => @imagecreatefromjpeg($path),
IMAGETYPE_GIF => @imagecreatefromgif($path),
IMAGETYPE_WEBP => @imagecreatefromwebp($path),
default => null,
};
if (!$src) {
return (string) file_get_contents($path);
}
$dst = imagecreatetruecolor($newW, $newH);
imagecopyresampled($dst, $src, 0, 0, 0, 0, $newW, $newH, $origW, $origH);
imagedestroy($src);
ob_start();
imagejpeg($dst, null, 65);
imagedestroy($dst);
return (string) ob_get_clean();
}
private function extractTutorialContent(string $raw): string
{
$start = strpos($raw, '<div class="tut-page">');
......
......@@ -161,7 +161,7 @@ function startExport() {
progressStep.textContent = step.label;
stepIndex++;
if (stepIndex < exportSteps.length) {
stepTimer = setTimeout(animateStep, 90000);
stepTimer = setTimeout(animateStep, 8000);
}
}
animateStep();
......@@ -191,7 +191,7 @@ function startExport() {
}
}, 2000);
// Safety timeout: 25 minutes max
// Safety timeout: 5 minutes max
setTimeout(function() {
if (document.getElementById('duringExport').style.display !== 'none') {
clearInterval(cookieTimer);
......@@ -201,7 +201,7 @@ function startExport() {
var f = document.getElementById('exportFrame');
if (f) f.remove();
}
}, 1500000);
}, 300000);
}
function finishExport(success) {
......
......@@ -156,6 +156,17 @@ var SearchableSelect = (function() {
};
Instance.prototype.selectValue = function(value) {
// For AJAX results, the option may not exist in the original <select>
if (value && !Array.from(this.select.options).some(function(o) { return o.value === value; })) {
var found = this.filteredOptions.find(function(o) { return o.value === value; });
if (found) {
var newOpt = document.createElement('option');
newOpt.value = found.value;
newOpt.textContent = found.text;
this.select.appendChild(newOpt);
this.options.push({value: found.value, text: found.text, group: found.group || '', el: newOpt});
}
}
this.select.value = value;
this.syncLabel();
this.close();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment