Dr-Panel/app/Providers/Filament/AdminPanelProvider.php

647 lines
36 KiB
PHP

<?php
namespace App\Providers\Filament;
use App\Filament\Pages\Auth\Login;
use App\Http\Middleware\SetLocale;
use BezhanSalleh\FilamentShield\FilamentShieldPlugin;
use Filament\Actions\Action;
use Filament\Http\Middleware\Authenticate;
use Filament\View\PanelsRenderHook;
use Filament\Http\Middleware\AuthenticateSession;
use Filament\Http\Middleware\DisableBladeIconComponents;
use Filament\Http\Middleware\DispatchServingFilamentEvent;
use Filament\Navigation\NavigationGroup;
use Filament\Panel;
use Filament\PanelProvider;
use Filament\Support\Colors\Color;
use Filament\Support\Enums\Width;
use Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse;
use Illuminate\Cookie\Middleware\EncryptCookies;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken;
use Illuminate\Routing\Middleware\SubstituteBindings;
use Illuminate\Session\Middleware\StartSession;
use Illuminate\View\Middleware\ShareErrorsFromSession;
class AdminPanelProvider extends PanelProvider
{
public function boot(): void
{
\Filament\Support\Facades\FilamentAsset::register([
\Filament\Support\Assets\Js::make('audio-speed', asset('js/audio-speed.js')),
]);
}
public function panel(Panel $panel): Panel
{
return $panel
->default()
->id('admin')
->path('admin')
->login(Login::class)
->colors([
'primary' => Color::Blue,
'orange' => Color::Orange,
])
->brandLogo(function () {
$key = app()->getLocale() === 'fa' ? 'listlogo_per.png' : 'listlogo_eng.png';
$attachment = \Illuminate\Support\Facades\Cache::remember(
'brand_logo_' . $key,
now()->addHours(24),
fn () => \App\Models\OsurgInitial::where('init_parameter', $key)->value('attachment')
);
return $attachment ? asset('storage/' . $attachment) : null;
})
->brandLogoHeight('1.75rem')
->brandName('')
->favicon(asset('favicon.svg'))
->font('Vazirmatn', url: \Illuminate\Support\Facades\Vite::asset('resources/css/vazirmatn.css'), provider: \Filament\FontProviders\LocalFontProvider::class)
->renderHook(
PanelsRenderHook::BODY_START,
fn (): string => <<<'HTML'
<script>
(function () {
if (localStorage.getItem('isOpen') === null) {
localStorage.setItem('isOpen', 'false');
}
if (localStorage.getItem('isOpenDesktop') === null) {
localStorage.setItem('isOpenDesktop', 'false');
}
})();
</script>
HTML,
)
->renderHook(
PanelsRenderHook::TOPBAR_LOGO_AFTER,
fn (): string => view('components.persian-datetime')->render(),
)
->renderHook(
PanelsRenderHook::GLOBAL_SEARCH_BEFORE,
fn (): string => \Blade::render('@livewire(\'settings-dropdown\')'),
)
->renderHook(
PanelsRenderHook::BODY_END,
fn (): string => <<<'HTML'
<style>
#fi-ucg-backdrop{position:fixed;inset:0;z-index:99998;background:rgba(0,0,0,.55);backdrop-filter:blur(3px)}
#fi-ucg-wrap{position:fixed;inset:0;z-index:99999;display:flex;align-items:center;justify-content:center;padding:1rem}
#fi-ucg-card{background:#fff;border-radius:.75rem;box-shadow:0 20px 50px -10px rgba(0,0,0,.18);padding:1.5rem;max-width:26rem;width:100%;border:1px solid #fecaca}
:root.dark #fi-ucg-card{background:#1e2533;border-color:#7f1d1d}
#fi-ucg-icon{width:2.75rem;height:2.75rem;border-radius:50%;background:#fee2e2;display:flex;align-items:center;justify-content:center;flex-shrink:0}
:root.dark #fi-ucg-icon{background:rgba(127,29,15,.35)}
#fi-ucg-title{font-weight:700;font-size:.9375rem;color:#991b1b;margin:0 0 .35rem}
:root.dark #fi-ucg-title{color:#fca5a5}
#fi-ucg-desc{font-size:.875rem;color:#6b7280;line-height:1.6;margin:0}
:root.dark #fi-ucg-desc{color:#9ca3af}
#fi-ucg-footer{display:flex;gap:.625rem;justify-content:flex-start;margin-top:1.5rem;flex-wrap:wrap}
.fi-ucg-btn{display:inline-flex;align-items:center;justify-content:center;padding:.5rem 1rem;border-radius:.5rem;font-size:.875rem;font-weight:500;cursor:pointer;border:none;transition:background .15s,opacity .15s;outline:none}
.fi-ucg-btn-cancel{background:#f3f4f6;color:#374151;border:1px solid #d1d5db}
.fi-ucg-btn-cancel:hover{background:#e5e7eb}
:root.dark .fi-ucg-btn-cancel{background:#374151;color:#d1d5db;border-color:#4b5563}
:root.dark .fi-ucg-btn-cancel:hover{background:#4b5563}
.fi-ucg-btn-discard{background:#dc2626;color:#fff}
.fi-ucg-btn-discard:hover{background:#b91c1c}
:root.dark .fi-ucg-btn-discard{background:#dc2626}
:root.dark .fi-ucg-btn-discard:hover{background:#b91c1c}
</style>
<script>
/* Unsaved-changes guard — runs synchronously before Livewire @script */
(function () {
/* ── i18n ── */
function texts() {
var fa = document.documentElement.lang === 'fa';
return fa ? {
dir:'rtl', title:'تغییرات ذخیره نشده',
desc:'اطلاعات وارد شده هنوز ذخیره نشده‌اند.<br>در صورت خروج، تمام تغییرات از دست خواهد رفت.',
cancel:'بازگشت به ویرایش', discard:'خروج بدون ذخیره',
} : {
dir:'ltr', title:'Unsaved Changes',
desc:'You have unsaved changes.<br>Leaving now will permanently discard them.',
cancel:'Keep Editing', discard:'Leave Without Saving',
};
}
/* ── dirty-state helper ── */
/* savedDataHash is #[Locked] in Livewire — never try to set it from JS.
Instead use a bypass flag so the next navigate event is skipped. */
var bypassNextNavigate = false;
function isDirty(wire) {
try {
if (!wire || wire?.__instance?.effects?.redirect) return false;
return window.jsMd5(JSON.stringify(wire.data).replace(/\\/g, '')) !== wire.savedDataHash;
} catch (_) { return false; }
}
/* ── registered wires (for beforeunload) ── */
var registeredWires = [];
function registerWire(w) { if (!registeredWires.includes(w)) registeredWires.push(w); }
document.addEventListener('livewire:navigated', function () {
bypassNextNavigate = false;
registeredWires = registeredWires.filter(function (w) {
try { return typeof w.savedDataHash !== 'undefined'; } catch (_) { return false; }
});
});
/* ── Alpine component (registered before Alpine starts) ── */
document.addEventListener('alpine:init', function () {
Alpine.data('fiUnsavedGuard', function () {
return {
show:false, pendingUrl:null, pendingWire:null,
pendingIsSpa:false, navigating:false, t:texts(),
init() {
window.$fiUnsavedGuard = this;
document.addEventListener('livewire:navigated', () => {
this.navigating = false;
this.t = texts();
});
},
open(url, wire, isSpa) {
this.pendingUrl=url; this.pendingWire=wire;
this.pendingIsSpa=!!isSpa; this.show=true;
},
cancel() { this.show=false; this.pendingUrl=null; this.pendingWire=null; },
discard() {
var url=this.pendingUrl;
this.show=false; this.pendingUrl=null; this.pendingWire=null;
this.navigating=true;
if (!url) { history.back(); return; }
window.location.href = url;
},
};
});
});
/* ── override Filament's hooks (must happen before @script runs) ── */
window.setUpSpaModeUnsavedDataChangesAlert = function ({ resolveLivewireComponentUsing, $wire }) {
registerWire($wire);
document.addEventListener('livewire:navigate', function (event) {
if (bypassNextNavigate) { return; } /* reset is handled by livewire:navigated so all listeners see the flag */
if (typeof resolveLivewireComponentUsing() === 'undefined') return;
if (!isDirty($wire)) return;
event.preventDefault();
var url = event.detail?.url;
window.$fiUnsavedGuard?.open(url ? url.toString() : null, $wire, true);
});
};
window.setUpUnsavedDataChangesAlert = function ({ $wire }) {
registerWire($wire);
document.addEventListener('click', function (e) {
var link = e.target.closest('a[href]:not([target="_blank"]):not([href^="javascript"]):not([href^="#"])');
if (!link) return;
var href = link.getAttribute('href');
if (!href || href === window.location.pathname) return;
if (!isDirty($wire)) return;
e.preventDefault(); e.stopPropagation();
window.$fiUnsavedGuard?.open(link.href, $wire, false);
}, true);
};
/* ── beforeunload: only for real tab/browser close ── */
window.addEventListener('beforeunload', function (event) {
if (window.$fiUnsavedGuard?.navigating) return;
for (var i = 0; i < registeredWires.length; i++) {
if (isDirty(registeredWires[i])) { event.preventDefault(); event.returnValue = true; return; }
}
});
})();
</script>
<div
x-data="fiUnsavedGuard"
x-cloak
@keydown.escape.window="if (show) cancel()"
>
<template x-if="show">
<div>
<div id="fi-ucg-backdrop" @click="cancel()"></div>
<div id="fi-ucg-wrap">
<div id="fi-ucg-card" :dir="t.dir" role="dialog" aria-modal="true">
<div style="display:flex;align-items:flex-start;gap:.875rem">
<div id="fi-ucg-icon">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.75" stroke="#dc2626" style="width:1.25rem;height:1.25rem">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126ZM12 15.75h.007v.008H12v-.008Z"/>
</svg>
</div>
<div>
<p id="fi-ucg-title" x-text="t.title"></p>
<p id="fi-ucg-desc" x-html="t.desc"></p>
</div>
</div>
<div id="fi-ucg-footer">
<button @click="cancel()" type="button" class="fi-ucg-btn fi-ucg-btn-cancel" x-text="t.cancel"></button>
<button @click="discard()" type="button" class="fi-ucg-btn fi-ucg-btn-discard" x-text="t.discard"></button>
</div>
</div>
</div>
</div>
</template>
</div>
<script>
(function () {
var speeds = [0.5, 1, 1.5, 2];
document.addEventListener('click', function (e) {
var btn = e.target.closest('.filepond--media-player-button-playbackrate');
if (!btn) return;
var item = btn.closest('.filepond--item, .filepond--root');
if (!item) return;
var media = item.querySelector('audio, video');
if (!media) return;
var current = media.playbackRate || 1;
var idx = speeds.indexOf(current);
var next = speeds[(idx + 1) % speeds.length];
media.playbackRate = next;
btn.textContent = next + 'x';
});
})();
</script>
<script>
(function () {
/* Fix FilePond gallery: remove inline styles set by filepond JS that conflict with our CSS */
function fixGalleryItems() {
document.querySelectorAll('.fi-photo-gallery .filepond--item').forEach(function (item) {
item.style.removeProperty('width');
item.style.removeProperty('height');
item.style.removeProperty('transform');
item.style.removeProperty('left');
item.style.removeProperty('top');
});
document.querySelectorAll('.fi-photo-gallery .filepond--image-preview-wrapper').forEach(function (w) {
w.style.removeProperty('transform');
w.style.removeProperty('margin-top');
});
document.querySelectorAll('.fi-photo-gallery .filepond--list-scroller').forEach(function (el) {
el.style.removeProperty('height');
el.style.removeProperty('top');
el.style.removeProperty('transform');
});
document.querySelectorAll('.fi-photo-gallery .filepond--root').forEach(function (root) {
root.style.removeProperty('height');
});
}
var _fixTimer = null;
var _observer = new MutationObserver(function (mutations) {
if (_fixTimer !== null) return;
var relevant = mutations.some(function (m) {
return m.target && m.target.closest &&
m.target.closest('.fi-photo-gallery');
});
if (!relevant) return;
_fixTimer = requestAnimationFrame(function () {
_fixTimer = null;
fixGalleryItems();
});
});
_observer.observe(document.body, { subtree: true, attributes: true, attributeFilter: ['style'] });
document.addEventListener('DOMContentLoaded', fixGalleryItems);
document.addEventListener('livewire:navigated', fixGalleryItems);
})();
</script>
HTML,
)
->renderHook(
PanelsRenderHook::BODY_END,
fn (): string => <<<'HTML'
<div id="fi-lightbox">
<div id="fi-lb-wrap">
<img id="fi-lb-img" src="" alt="">
<div id="fi-lb-caption" style="display:none">
<div id="fi-lb-cap-header">
<div id="fi-lb-cap-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="none" stroke="rgba(20,30,60,0.85)" stroke-width="1.5" style="width:14px;height:14px;"><path stroke-linecap="round" stroke-linejoin="round" d="M7 8h6M7 11h4m1 7l-4-4H5a2 2 0 01-2-2V6a2 2 0 012-2h10a2 2 0 012 2v5a2 2 0 01-2 2h-2l-3 3z"/></svg></div>
<span id="fi-lb-cap-title">یادداشت پزشک</span>
<button id="fi-lb-cap-close" title="پنهان کردن"></button>
</div>
<div id="fi-lb-cap-body"></div>
</div>
<button id="fi-lb-cap-toggle" title="نمایش یادداشت"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5" style="width:20px;height:20px;"><path stroke-linecap="round" stroke-linejoin="round" d="M7 8h6M7 11h4m1 7l-4-4H5a2 2 0 01-2-2V6a2 2 0 012-2h10a2 2 0 012 2v5a2 2 0 01-2 2h-2l-3 3z"/></svg></button>
<button id="fi-lb-close" title="بستن"></button>
<button id="fi-lb-prev" title="قبلی">&#8249;</button>
<button id="fi-lb-next" title="بعدی">&#8250;</button>
<div id="fi-lb-counter">1 / 1</div>
<div id="fi-lb-hint">اسکرول: زوم &nbsp;|&nbsp; دابل‌کلیک: ریست</div>
</div>
</div>
<script>
(function () {
'use strict';
var lb = document.getElementById('fi-lightbox');
var lbWrap = document.getElementById('fi-lb-wrap');
var lbImg = document.getElementById('fi-lb-img');
var lbCnt = document.getElementById('fi-lb-counter');
var lbPrev = document.getElementById('fi-lb-prev');
var lbNext = document.getElementById('fi-lb-next');
var lbClose = document.getElementById('fi-lb-close');
var lbCaption = document.getElementById('fi-lb-caption');
var lbCapBody = document.getElementById('fi-lb-cap-body');
var lbCapClose = document.getElementById('fi-lb-cap-close');
var lbCapToggle = document.getElementById('fi-lb-cap-toggle');
var lbCapTitleEl = document.getElementById('fi-lb-cap-title');
if (!lb) return;
var images = [], captions = [], sections = [], currentIdx = 0, zoom = 1;
var isPanning = false, panStartX = 0, panStartY = 0, panX = 0, panY = 0;
var manuallyHidden = false, autoHidden = false;
var capTitles = {
before: { fa: 'توضیحات قبل از عمل', en: 'Pre-Op Notes' },
after: { fa: 'توضیحات بعد از عمل', en: 'Post-Op Notes' },
default: { fa: 'یادداشت پزشک', en: 'Doctor\'s Notes' },
};
function updateTitle() {
if (!lbCapTitleEl) return;
var sec = sections[currentIdx] || 'default';
var locale = document.documentElement.lang === 'fa' ? 'fa' : 'en';
lbCapTitleEl.textContent = (capTitles[sec] || capTitles['default'])[locale];
}
function applyTransform() {
lbImg.style.transform = 'scale(' + zoom + ') translate(' + panX + 'px, ' + panY + 'px)';
lbImg.classList.toggle('lb-zoomed', zoom > 1);
var hasCaption = lbCaption && lbCaption.style.display !== 'none';
if (hasCaption && zoom > 1 && !lbCaption.classList.contains('lb-cap-hidden')) {
lbCaption.classList.add('lb-cap-hidden');
lb.classList.add('lb-cap-collapsed');
if (lbCapToggle) lbCapToggle.classList.add('lb-cap-toggle-visible');
autoHidden = true;
} else if (zoom <= 1 && autoHidden && !manuallyHidden) {
lbCaption.classList.remove('lb-cap-hidden');
lb.classList.remove('lb-cap-collapsed');
if (lbCapToggle) lbCapToggle.classList.remove('lb-cap-toggle-visible');
autoHidden = false;
}
}
function updateCaption() {
var cap = captions[currentIdx] || '';
if (cap) {
if (lbCapBody) lbCapBody.innerHTML = cap;
lbCaption.style.display = '';
lb.classList.remove('lb-no-caption');
/* Start open by default */
lbCaption.classList.remove('lb-cap-hidden');
lb.classList.remove('lb-cap-collapsed');
if (lbCapToggle) lbCapToggle.classList.remove('lb-cap-toggle-visible');
} else {
lbCaption.style.display = 'none';
if (lbCapBody) lbCapBody.innerHTML = '';
lb.classList.add('lb-no-caption');
if (lbCapToggle) lbCapToggle.classList.remove('lb-cap-toggle-visible');
}
}
function showLightbox(imgs, idx, caps, secs) {
images = imgs;
captions = caps || [];
sections = secs || [];
currentIdx = idx;
zoom = 1; panX = 0; panY = 0;
manuallyHidden = false; autoHidden = false;
applyTransform();
updateCounter();
updateCaption();
updateTitle();
loadImage();
lb.classList.add('lb-open');
document.addEventListener('keydown', onKey);
}
function hideLightbox() {
lb.classList.remove('lb-open');
document.removeEventListener('keydown', onKey);
lbImg.src = '';
}
function navigate(dir) {
if (images.length < 2) return;
currentIdx = (currentIdx + dir + images.length) % images.length;
zoom = 1; panX = 0; panY = 0;
applyTransform();
loadImage();
updateCounter();
updateCaption();
updateTitle();
}
function loadImage() {
lbImg.style.opacity = '0';
var src = images[currentIdx];
var tmp = new Image();
tmp.onload = function () { lbImg.src = src; lbImg.style.opacity = '1'; };
tmp.onerror = function () { lbImg.src = src; lbImg.style.opacity = '1'; };
tmp.src = src;
}
function updateCounter() {
lbCnt.textContent = (currentIdx + 1) + ' / ' + images.length;
var show = images.length > 1;
lbPrev.style.visibility = show ? '' : 'hidden';
lbNext.style.visibility = show ? '' : 'hidden';
}
function onKey(e) {
if (e.key === 'Escape') hideLightbox();
else if (e.key === 'ArrowLeft') navigate(-1);
else if (e.key === 'ArrowRight') navigate(1);
else if (e.key === '+' || e.key === '=') { zoom = Math.min(8, zoom + 0.2); applyTransform(); }
else if (e.key === '-') { zoom = Math.max(0.3, zoom - 0.2); if (zoom <= 1) { panX = 0; panY = 0; } applyTransform(); }
else if (e.key === '0') { zoom = 1; panX = 0; panY = 0; applyTransform(); }
}
lbWrap.addEventListener('wheel', function (e) {
e.preventDefault();
zoom = Math.max(0.3, Math.min(8, zoom + (e.deltaY < 0 ? 0.1 : -0.1)));
if (zoom <= 1) { panX = 0; panY = 0; }
applyTransform();
}, { passive: false });
lbImg.addEventListener('dblclick', function () {
zoom = 1; panX = 0; panY = 0; applyTransform();
});
lbImg.addEventListener('mousedown', function (e) {
if (zoom <= 1) return;
isPanning = true;
panStartX = e.clientX - panX * zoom;
panStartY = e.clientY - panY * zoom;
lbImg.classList.add('lb-dragging');
e.preventDefault();
});
document.addEventListener('mousemove', function (e) {
if (!isPanning) return;
panX = (e.clientX - panStartX) / zoom;
panY = (e.clientY - panStartY) / zoom;
lbImg.style.transform = 'scale(' + zoom + ') translate(' + panX + 'px, ' + panY + 'px)';
});
document.addEventListener('mouseup', function () {
if (isPanning) { isPanning = false; lbImg.classList.remove('lb-dragging'); }
});
lbWrap.addEventListener('click', function (e) { if (e.target === lbWrap) hideLightbox(); });
lbClose.addEventListener('click', hideLightbox);
lbPrev.addEventListener('click', function () { navigate(-1); });
lbNext.addEventListener('click', function () { navigate(1); });
if (lbCapClose) lbCapClose.addEventListener('click', function () {
manuallyHidden = true;
lbCaption.classList.add('lb-cap-hidden');
lb.classList.add('lb-cap-collapsed');
if (lbCapToggle) lbCapToggle.classList.add('lb-cap-toggle-visible');
});
if (lbCapToggle) lbCapToggle.addEventListener('click', function () {
manuallyHidden = false; autoHidden = false;
lbCaption.classList.remove('lb-cap-hidden');
lb.classList.remove('lb-cap-collapsed');
lbCapToggle.classList.remove('lb-cap-toggle-visible');
});
document.addEventListener('click', function (e) {
var thumb = e.target.closest('[data-lb-src]');
if (!thumb) return;
e.preventDefault();
var group = thumb.getAttribute('data-lb-group');
var all = group
? Array.from(document.querySelectorAll('[data-lb-group="' + group + '"]'))
: [thumb];
var urls = all.map(function (el) { return el.getAttribute('data-lb-src'); });
var caps = all.map(function (el) { return el.getAttribute('data-lb-caption') || ''; });
var secs = all.map(function (el) { return el.getAttribute('data-lb-section') || ''; });
var idx = all.indexOf(thumb);
showLightbox(urls, Math.max(0, idx), caps, secs);
});
document.addEventListener('click', function (e) {
var preview = e.target.closest('.filepond--image-preview-wrapper, .filepond--image-preview');
if (!preview) return;
var item = preview.closest('.filepond--item');
var root = item && item.closest('.filepond--root');
if (!item || !root) return;
e.preventDefault();
e.stopPropagation();
var allItems = Array.from(root.querySelectorAll('.filepond--item'));
var clickedIdx = allItems.indexOf(item);
var capWrapper = root.closest('[data-lb-section]');
var lbSec = capWrapper ? capWrapper.getAttribute('data-lb-section') : '';
var lbCap = capWrapper ? capWrapper.getAttribute('data-lb-caption') : '';
function mkArrays(urls) {
return { caps: urls.map(function() { return lbCap; }), secs: urls.map(function() { return lbSec; }) };
}
var pond = null;
var alpineEl = root.closest('[x-data]');
if (alpineEl && window.Alpine) {
try { var ad = Alpine.$data(alpineEl); if (ad && ad.pond) pond = ad.pond; } catch (e1) {}
}
if (!pond) {
var inp = root.querySelector('input[type="file"], input.filepond');
if (inp && inp._filepond) pond = inp._filepond;
}
if (pond) {
try {
var pondFiles = pond.getFiles();
var urls1 = [];
pondFiles.forEach(function (f) {
var sid = f.serverId;
if (!sid) return;
if (typeof sid !== 'string') { try { sid = JSON.parse(sid); } catch(pe) {} }
if (typeof sid === 'string') {
urls1.push(sid.startsWith('http') || sid.startsWith('/') ? sid : '/storage/' + sid);
}
});
if (urls1.length) { var a1=mkArrays(urls1); showLightbox(urls1, Math.min(clickedIdx, urls1.length - 1), a1.caps, a1.secs); return; }
} catch (e2) {}
}
var wireInput = root.querySelector('input[wire\\:model], input[wire\\:model\\.live], input[wire\\:model\\.blur]');
var wireModel = wireInput && (wireInput.getAttribute('wire:model') || wireInput.getAttribute('wire:model.live') || wireInput.getAttribute('wire:model.blur'));
var wireEl = root.closest('[wire\\:id]');
var wireId = wireEl && wireEl.getAttribute('wire:id');
if (wireModel && wireId && window.Livewire) {
try {
var comp = window.Livewire.find(wireId);
var files = comp.$wire.get(wireModel);
if (files) {
var arr = Array.isArray(files) ? files : Object.values(files);
if (arr.length) {
var arr2=arr.map(function(f){return '/storage/'+f;}); var a2=mkArrays(arr2);
showLightbox(arr2, Math.min(clickedIdx, arr2.length-1), a2.caps, a2.secs);
return;
}
}
} catch (e3) {}
}
var imgs = [], fbIdx = 0;
allItems.forEach(function (it) {
var canvas = it.querySelector('canvas');
if (canvas) {
try {
var src = canvas.toDataURL('image/jpeg', 0.95);
if (it === item) fbIdx = imgs.length;
imgs.push(src);
} catch (ex) {}
}
});
if (imgs.length) { var a3=mkArrays(imgs); showLightbox(imgs, fbIdx, a3.caps, a3.secs); }
}, true);
})();
</script>
HTML,
)
->userMenuItems([
Action::make('switch_language')
->label(fn () => app()->getLocale() === 'fa' ? 'English' : 'فارسی')
->icon('heroicon-o-language')
->url(fn () => route('locale.switch', app()->getLocale() === 'fa' ? 'en' : 'fa'))
->openUrlInNewTab(false)
->extraAttributes(fn () => [
'x-on:click.prevent' => 'window.location.href = \'' . route('locale.switch', app()->getLocale() === 'fa' ? 'en' : 'fa') . '\'',
]),
])
->navigationGroups([
NavigationGroup::make('بیماران و ویزیت'),
NavigationGroup::make('بیماری‌ها و روش‌های درمان'),
NavigationGroup::make('اطلاعات کلینیک'),
NavigationGroup::make('تنظیمات'),
])
->sidebarFullyCollapsibleOnDesktop()
->spa()
->unsavedChangesAlerts()
->maxContentWidth(Width::Full)
->viteTheme('resources/css/filament/admin/theme.css')
->discoverResources(in: app_path('Filament/Resources'), for: 'App\Filament\Resources')
->plugins([
FilamentShieldPlugin::make(),
])
->discoverPages(in: app_path('Filament/Pages'), for: 'App\Filament\Pages')
->pages([])
->discoverWidgets(in: app_path('Filament/Widgets'), for: 'App\Filament\Widgets')
->widgets([])
->middleware([
EncryptCookies::class,
AddQueuedCookiesToResponse::class,
StartSession::class,
AuthenticateSession::class,
ShareErrorsFromSession::class,
VerifyCsrfToken::class,
SubstituteBindings::class,
DisableBladeIconComponents::class,
DispatchServingFilamentEvent::class,
SetLocale::class,
])
->authMiddleware([
Authenticate::class,
]);
}
}