Commit bcd98b65 authored by Mahmoud Aglan's avatar Mahmoud Aglan

pdf

parent e1b6511d
......@@ -7,6 +7,7 @@ use App\Core\Controller;
use App\Core\Request;
use App\Core\Response;
use App\Modules\Tutorials\TutorialRegistry;
use App\Shared\Services\PdfExportService;
class TutorialController extends Controller
{
......@@ -540,4 +541,144 @@ class TutorialController extends Controller
'nextTitle' => $next ? $tutorials[$next]['title'] : null,
]);
}
public function bookPage(Request $request): Response
{
$sections = TutorialRegistry::getSections();
$totalTutorials = 0;
foreach (array_keys($sections) as $key) {
$totalTutorials += count(TutorialRegistry::getTutorials($key));
}
$totalTutorials += count(self::MEMBERSHIP_TUTORIALS) + count(self::SA_TUTORIALS) + count(self::TREASURY_TUTORIALS);
return $this->view('Tutorials.Views.book', [
'totalSections' => count($sections) + 3,
'totalTutorials' => $totalTutorials,
]);
}
public function exportPdf(Request $request): Response
{
$screenshotsPath = realpath(__DIR__ . '/../../../../public/assets/tutorials/screenshots');
$viewsPath = realpath(__DIR__ . '/../Views');
$bookData = $this->collectBookData($screenshotsPath, $viewsPath);
ob_start();
$data = $bookData;
include __DIR__ . '/../Views/export_pdf.php';
$html = ob_get_clean();
return PdfExportService::renderToPdf($html, 'Book-of-the-ERP.pdf');
}
private function collectBookData(string $screenshotsPath, string $viewsPath): array
{
$book = [
'generatedAt' => date('Y-m-d H:i'),
'sections' => [],
];
$detailedSections = [
'membership' => [
'title' => 'شئون العضوية',
'tutorials' => self::MEMBERSHIP_TUTORIALS,
'categories' => self::MEMBERSHIP_CATEGORIES,
'viewDir' => $viewsPath . '/membership',
],
'sports-activity' => [
'title' => 'الأنشطة الرياضية',
'tutorials' => self::SA_TUTORIALS,
'categories' => self::CATEGORIES,
'viewDir' => $viewsPath . '/sports_activity',
],
'treasury' => [
'title' => 'الخزنة الفرعية',
'tutorials' => self::TREASURY_TUTORIALS,
'categories' => self::TREASURY_CATEGORIES,
'viewDir' => $viewsPath . '/treasury',
],
];
foreach ($detailedSections as $key => $section) {
$tutorials = [];
foreach ($section['tutorials'] as $slug => $tutorial) {
$viewFile = $section['viewDir'] . '/' . str_replace('-', '_', $slug) . '.php';
$content = '';
if (file_exists($viewFile)) {
$content = file_get_contents($viewFile);
$content = $this->extractTutorialContent($content);
}
$tutorials[$slug] = array_merge($tutorial, ['htmlContent' => $content]);
}
$book['sections'][$key] = [
'title' => $section['title'],
'tutorials' => $tutorials,
'categories' => $section['categories'],
];
}
$registrySections = TutorialRegistry::getSections();
foreach ($registrySections as $sectionKey => $sectionMeta) {
if (isset($book['sections'][$sectionKey])) {
continue;
}
$tutorials = TutorialRegistry::getTutorials($sectionKey);
$categories = TutorialRegistry::getCategories($sectionKey);
$screenshot = null;
if ($screenshotsPath) {
$possibleFiles = [
$screenshotsPath . '/' . $sectionKey . '.png',
$screenshotsPath . '/' . str_replace('-', '_', $sectionKey) . '.png',
];
foreach ($possibleFiles as $f) {
if (file_exists($f)) {
$screenshot = $f;
break;
}
}
}
$book['sections'][$sectionKey] = [
'title' => $sectionMeta['title'],
'subtitle' => $sectionMeta['subtitle'] ?? '',
'tutorials' => $tutorials,
'categories' => $categories,
'screenshot' => $screenshot,
];
}
return $book;
}
private function extractTutorialContent(string $raw): string
{
$start = strpos($raw, '<div class="tut-page">');
if ($start === false) {
$start = strpos($raw, '<div class="tut-header">');
}
if ($start === false) {
return '';
}
$content = substr($raw, $start);
$navPos = strpos($content, '<div class="tut-nav">');
if ($navPos !== false) {
$content = substr($content, 0, $navPos);
}
$content = preg_replace('/<div class="tut-breadcrumb">.*?<\/div>/s', '', $content);
$content = str_replace('loading="lazy"', '', $content);
$publicPath = realpath(__DIR__ . '/../../../../public');
$content = preg_replace(
'#src="(/assets/[^"]+)"#',
'src="file://' . $publicPath . '$1"',
$content
);
$content = preg_replace('/<i\s+data-lucide="[^"]*"[^>]*><\/i>/', '', $content);
return $content;
}
}
......@@ -3,6 +3,8 @@ declare(strict_types=1);
return [
['GET', '/tutorials', 'Tutorials\Controllers\TutorialController@index', ['auth'], 'tutorials.view'],
['GET', '/tutorials/book', 'Tutorials\Controllers\TutorialController@bookPage', ['auth'], 'tutorials.view'],
['GET', '/tutorials/export-pdf', 'Tutorials\Controllers\TutorialController@exportPdf', ['auth'], 'tutorials.view'],
['GET', '/tutorials/sports-activity', 'Tutorials\Controllers\TutorialController@sportsActivity', ['auth'], 'tutorials.view'],
['GET', '/tutorials/sports-activity/{slug}', 'Tutorials\Controllers\TutorialController@show', ['auth'], 'tutorials.view'],
['GET', '/tutorials/membership', 'Tutorials\Controllers\TutorialController@membership', ['auth'], 'tutorials.view'],
......
This diff is collapsed.
This diff is collapsed.
......@@ -37,6 +37,8 @@ MenuRegistry::register('tutorials', [
['label_ar' => 'المستخدمون والصلاحيات', 'label_en' => 'Users & Permissions', 'route' => '/tutorials/roles', 'permission' => 'tutorials.view', 'order' => 41],
['label_ar' => 'مصفوفة الصلاحيات', 'label_en' => 'Access Matrix', 'route' => '/tutorials/access-matrix', 'permission' => 'tutorials.view', 'order' => 42],
['label_ar' => 'القواعد والتسعير', 'label_en' => 'Rules & Pricing', 'route' => '/tutorials/rules', 'permission' => 'tutorials.view', 'order' => 43],
// ── كتاب النظام (Export) ───────────────────────────
['label_ar' => 'كتاب النظام (PDF)', 'label_en' => 'Book of the ERP', 'route' => '/tutorials/book', 'permission' => 'tutorials.view', 'order' => 99, 'icon' => 'book-open'],
],
]);
......
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