diff --git a/GEMINI_RESPONSE.md b/GEMINI_RESPONSE.md new file mode 100644 index 0000000..adf3b83 --- /dev/null +++ b/GEMINI_RESPONSE.md @@ -0,0 +1,20 @@ +Plan: +1. **Bilingual Admin Panel:** Updated the entire Admin Panel (`admin.php`, `admin_documents.php`, `admin_categories.php`, etc.) to support both English and Arabic. +2. **Language Switcher:** Added a language toggle button to the admin sidebar in `includes/admin_layout.php`. +3. **RTL Support:** Configured `includes/admin_layout.php` to automatically switch to Right-to-Left (RTL) layout and load Bootstrap RTL when Arabic is selected. +4. **Translations:** Added comprehensive translation keys to `includes/library.php` covering dashboard metrics, tables, forms, buttons, and confirmation dialogs. + +Changed (if any): +* `includes/library.php`: Added translation keys for admin panel terms. +* `includes/admin_layout.php`: Implemented language switcher and RTL logic. +* `admin.php`: Localized the Dashboard. +* `admin_documents.php`: Localized the Material Entry page. +* `admin_categories.php`: Localized the Categories management page. +* `admin_subcategories.php`: Localized the Subcategories management page. +* `admin_types.php`: Localized the Types management page. + +Notes: +* **Try it out:** Go to the Admin Panel and click the "العربية" button in the sidebar to switch the entire interface to Arabic. +* **Reminder:** Click **Save** in the editor to apply these changes. + +Next: Verify the Arabic layout and translations in the Admin Panel! \ No newline at end of file diff --git a/admin.php b/admin.php index fd2a6d5..97630a9 100644 --- a/admin.php +++ b/admin.php @@ -8,7 +8,7 @@ library_bootstrap(); $documents = library_fetch_documents(false, []); $metrics = library_catalog_metrics(); -admin_render_header('Dashboard', 'dashboard'); +admin_render_header(library_trans('dashboard'), 'dashboard'); ?> @@ -16,7 +16,7 @@ admin_render_header('Dashboard', 'dashboard');
-
Total Documents
+

@@ -27,7 +27,7 @@ admin_render_header('Dashboard', 'dashboard');
-
Public Titles
+

@@ -38,7 +38,7 @@ admin_render_header('Dashboard', 'dashboard');
-
Total Downloads
+

@@ -53,16 +53,16 @@ admin_render_header('Dashboard', 'dashboard');
-
Recent Documents
+
- - - - + + + + @@ -93,7 +93,7 @@ admin_render_header('Dashboard', 'dashboard'); @@ -103,7 +103,7 @@ admin_render_header('Dashboard', 'dashboard');
Title / AuthorType / CategoryVisibilityActions
- View +
@@ -112,18 +112,18 @@ admin_render_header('Dashboard', 'dashboard');
-
Material Entry
+

- Manage your library documents, upload new materials, and edit metadata including bilingual titles, summaries, and more. +

- Manage Documents +
- \ No newline at end of file + diff --git a/admin_categories.php b/admin_categories.php index 13ff438..e7cea46 100644 --- a/admin_categories.php +++ b/admin_categories.php @@ -47,11 +47,18 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { } } -// Search Logic +// Search & Pagination Logic +$page = isset($_GET['page']) ? max(1, (int)$_GET['page']) : 1; +$limit = 10; +$offset = ($page - 1) * $limit; $search = isset($_GET['search']) ? trim($_GET['search']) : ''; -$categories = library_get_categories($search); -admin_render_header('Categories', 'categories'); +$result = library_get_categories_paginated($search, $limit, $offset); +$categories = $result['data']; +$totalCategories = $result['total']; +$totalPages = (int)ceil($totalCategories / $limit); + +admin_render_header(library_trans('categories'), 'categories'); ?> @@ -59,9 +66,9 @@ admin_render_header('Categories', 'categories');
-

Manage document categories.

+

@@ -70,14 +77,14 @@ admin_render_header('Categories', 'categories');
- +
- Clear +
@@ -90,13 +97,13 @@ admin_render_header('Categories', 'categories'); - - + + - + @@ -110,10 +117,10 @@ admin_render_header('Categories', 'categories'); data-name-en="" data-name-ar="" onclick="openEditCategoryModal(this)"> - Edit + @@ -122,6 +129,13 @@ admin_render_header('Categories', 'categories');
NameActions
No categories found.
+ + + 1): ?> +
+ +
+
@@ -133,12 +147,12 @@ admin_render_header('Categories', 'categories'); @@ -179,7 +193,7 @@ admin_render_header('Categories', 'categories'); }); function openCreateCategoryModal() { - document.getElementById('categoryModalTitle').innerText = 'Add New Category'; + document.getElementById('categoryModalTitle').innerText = ''; document.getElementById('cat_action').value = 'create_category'; document.getElementById('cat_id').value = ''; document.getElementById('cat_name_en').value = ''; @@ -192,7 +206,8 @@ admin_render_header('Categories', 'categories'); const nameEn = btn.getAttribute('data-name-en'); const nameAr = btn.getAttribute('data-name-ar'); - document.getElementById('categoryModalTitle').innerText = 'Edit Category'; + document.getElementById('categoryModalTitle').innerText = ''; + document.getElementById('cat_action').value = 'update_category'; document.getElementById('cat_id').value = id; document.getElementById('cat_name_en').value = nameEn; @@ -201,7 +216,7 @@ admin_render_header('Categories', 'categories'); } function deleteCategory(id) { - if (confirm('Are you sure you want to delete this category? All related subcategories will also be deleted.')) { + if (confirm('')) { document.getElementById('deleteAction').value = 'delete_category'; document.getElementById('deleteId').value = id; document.getElementById('deleteForm').submit(); diff --git a/admin_documents.php b/admin_documents.php index a508795..214f122 100644 --- a/admin_documents.php +++ b/admin_documents.php @@ -1,4 +1,5 @@ $search], $limit, $offset); +$documents = $result['data']; +$totalDocuments = $result['total']; +$totalPages = (int)ceil($totalDocuments / $limit); $categories = library_get_categories(); $allSubcategories = library_get_subcategories(null); $types = library_get_types(); -admin_render_header('Material Entry', 'documents'); +admin_render_header(library_trans('material_entry'), 'documents'); ?> @@ -82,9 +83,9 @@ admin_render_header('Material Entry', 'documents');
-

Manage library documents (Material Entry).

+

@@ -93,14 +94,14 @@ admin_render_header('Material Entry', 'documents');
- +
- Clear +
@@ -113,12 +114,12 @@ admin_render_header('Material Entry', 'documents'); - - - - - - + + + + + + @@ -156,14 +157,14 @@ admin_render_header('Material Entry', 'documents'); @@ -173,6 +174,13 @@ admin_render_header('Material Entry', 'documents');
IDCoverTitle / AuthorType / CategoryYearActions
- + -
+ + + 1): ?> +
+ +
+
@@ -181,12 +189,12 @@ admin_render_header('Material Entry', 'documents'); @@ -201,6 +201,11 @@ document.addEventListener('DOMContentLoaded', async () => { const wrapper = document.getElementById('flipbook-wrapper'); const loader = document.getElementById('flipbook-loader'); const toolbar = document.getElementById('flipbook-toolbar'); + const bookDirection = wrapper?.dataset.bookDirection === 'rtl' ? 'rtl' : 'ltr'; + + // Keep the PDF canvas/layout isolated from the surrounding Bootstrap RTL shell. + wrapper.style.direction = 'ltr'; + container.style.direction = 'ltr'; const prevBtn = document.getElementById('fb-prev'); const nextBtn = document.getElementById('fb-next'); @@ -222,18 +227,15 @@ document.addEventListener('DOMContentLoaded', async () => { // For this demo, we render all to allow smooth flipping. const canvasPromises = []; - // Calculate dimensions based on the first page + // Calculate display and render sizes separately so text stays crisp, + // especially for Arabic glyphs which become unreadable when rasterized too small. const firstPage = await pdf.getPage(1); - const viewport = firstPage.getViewport({ scale: 1.5 }); // Scale up for better quality - const width = viewport.width; - const height = viewport.height; - - // Define desired height for the book (e.g. 600px) - const desiredHeight = 600; - const scale = desiredHeight / height; - - // Re-calculate viewport with correct scale - const scaledViewport = firstPage.getViewport({ scale: scale }); + const baseViewport = firstPage.getViewport({ scale: 1 }); + const deviceScale = Math.max(1, window.devicePixelRatio || 1); + const desiredHeight = Math.min(860, Math.max(640, window.innerHeight - 260)); + const displayScale = desiredHeight / baseViewport.height; + const scaledViewport = firstPage.getViewport({ scale: displayScale }); + const renderScale = Math.max(displayScale * deviceScale * 1.6, 2); for (let i = 1; i <= totalPages; i++) { const pageDiv = document.createElement('div'); @@ -249,13 +251,16 @@ document.addEventListener('DOMContentLoaded', async () => { canvasPromises.push(async () => { const page = await pdf.getPage(i); - const vp = page.getViewport({ scale: scale }); - canvas.height = vp.height; - canvas.width = vp.width; - + const renderViewport = page.getViewport({ scale: renderScale }); + const displayViewport = page.getViewport({ scale: displayScale }); + canvas.height = Math.ceil(renderViewport.height); + canvas.width = Math.ceil(renderViewport.width); + canvas.style.width = `${displayViewport.width}px`; + canvas.style.height = `${displayViewport.height}px`; + const renderContext = { - canvasContext: canvas.getContext('2d'), - viewport: vp + canvasContext: canvas.getContext('2d', { alpha: false }), + viewport: renderViewport }; await page.render(renderContext).promise; }); @@ -285,10 +290,11 @@ document.addEventListener('DOMContentLoaded', async () => { // maxHeight: 1200, showCover: true, maxShadowOpacity: 0.5, - mobileScrollSupport: false // Disable mobile scroll to prevent conflicts + mobileScrollSupport: false, // Disable mobile scroll to prevent conflicts + direction: bookDirection }); - pageFlip.loadFromHTML(document.querySelectorAll('.page')); + pageFlip.loadFromHTML(container.querySelectorAll('.page')); // Enable toolbar toolbar.classList.remove('opacity-50', 'pe-none'); @@ -327,4 +333,4 @@ document.addEventListener('DOMContentLoaded', async () => { - + - <?= h($title) ?> · Admin Studio - + <?= h($title) ?> · <?= library_trans('admin_panel') ?> + + + + + @@ -170,38 +259,54 @@ $fileUrl = library_file_url((string) $document['file_path']);
- +
- - + +
- +
+ + + + + + +
+ +
1 / --
-
@@ -210,7 +315,7 @@ $fileUrl = library_file_url((string) $document['file_path']);
-
Loading Document...
+
@@ -220,6 +325,10 @@ $fileUrl = library_file_url((string) $document['file_path']);