647 lines
36 KiB
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="قبلی">‹</button>
|
|
<button id="fi-lb-next" title="بعدی">›</button>
|
|
<div id="fi-lb-counter">1 / 1</div>
|
|
<div id="fi-lb-hint">اسکرول: زوم | دابلکلیک: ریست</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,
|
|
]);
|
|
}
|
|
}
|