249 lines
12 KiB
PHP
249 lines
12 KiB
PHP
<div class="flex items-center gap-1">
|
|
|
|
<button
|
|
x-data
|
|
@click="$dispatch('theme-changed', $store.theme === 'dark' ? 'light' : 'dark')"
|
|
class="flex items-center justify-center w-9 h-9 rounded-lg text-gray-500 hover:text-gray-700 hover:bg-gray-100 dark:text-gray-400 dark:hover:text-gray-200 dark:hover:bg-gray-700 transition"
|
|
>
|
|
<span x-show="$store.theme === 'dark'" x-cloak>
|
|
<x-heroicon-o-sun class="w-5 h-5" />
|
|
</span>
|
|
<span x-show="$store.theme !== 'dark'" x-cloak>
|
|
<x-heroicon-o-moon class="w-5 h-5" />
|
|
</span>
|
|
</button>
|
|
|
|
@can('View:SyncPage')
|
|
<div
|
|
class="relative"
|
|
x-data="{
|
|
open: false,
|
|
showConfirm: false,
|
|
showProgress: false,
|
|
progress: 0,
|
|
statusText: '',
|
|
done: false,
|
|
failed: false,
|
|
reportLines: [],
|
|
|
|
startSync() {
|
|
this.showConfirm = false;
|
|
this.showProgress = true;
|
|
this.progress = 0;
|
|
this.statusText = 'در حال اتصال به سیستم مقابل...';
|
|
this.done = false;
|
|
this.failed = false;
|
|
this.reportLines = [];
|
|
|
|
// Simulate initial progress
|
|
this.progress = 15;
|
|
|
|
let progressInterval = setInterval(() => {
|
|
if (this.progress < 85) {
|
|
this.progress += Math.random() * 10;
|
|
if (this.progress > 40 && this.progress < 60) {
|
|
this.statusText = 'در حال دریافت تغییرات...';
|
|
} else if (this.progress >= 60) {
|
|
this.statusText = 'در حال ارسال تغییرات...';
|
|
}
|
|
}
|
|
}, 500);
|
|
|
|
fetch('{{ route('sync.run') }}', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'X-CSRF-TOKEN': document.querySelector('meta[name=csrf-token]')?.content || '{{ csrf_token() }}',
|
|
'Accept': 'application/json',
|
|
},
|
|
})
|
|
.then(r => r.json())
|
|
.then(data => {
|
|
clearInterval(progressInterval);
|
|
this.progress = 100;
|
|
this.reportLines = data.report || [];
|
|
if (data.success) {
|
|
this.statusText = 'همگامسازی با موفقیت انجام شد';
|
|
this.done = true;
|
|
} else {
|
|
this.statusText = 'همگامسازی با خطا مواجه شد';
|
|
this.failed = true;
|
|
}
|
|
})
|
|
.catch(err => {
|
|
clearInterval(progressInterval);
|
|
this.progress = 100;
|
|
this.statusText = 'خطا در اتصال به سرور';
|
|
this.failed = true;
|
|
this.reportLines = [err.message || 'خطای ناشناخته'];
|
|
});
|
|
},
|
|
|
|
closeModal() {
|
|
this.showProgress = false;
|
|
this.showConfirm = false;
|
|
if (this.done) {
|
|
window.location.reload();
|
|
}
|
|
}
|
|
}"
|
|
@click.outside="open = false"
|
|
>
|
|
|
|
<button
|
|
@click="open = !open"
|
|
class="flex items-center justify-center w-9 h-9 rounded-lg text-gray-500 hover:text-gray-700 hover:bg-gray-100 dark:text-gray-400 dark:hover:text-gray-200 dark:hover:bg-gray-700 transition"
|
|
title="{{ __('navigation.groups.system') }}"
|
|
>
|
|
<x-heroicon-o-cog-6-tooth class="w-5 h-5" />
|
|
</button>
|
|
|
|
<div
|
|
x-show="open"
|
|
x-transition:enter="transition ease-out duration-100"
|
|
x-transition:enter-start="opacity-0 scale-95"
|
|
x-transition:enter-end="opacity-100 scale-100"
|
|
x-transition:leave="transition ease-in duration-75"
|
|
x-transition:leave-start="opacity-100 scale-100"
|
|
x-transition:leave-end="opacity-0 scale-95"
|
|
class="absolute inset-s-0 mt-1 w-52 rounded-xl bg-white dark:bg-gray-800 shadow-lg ring-1 ring-gray-200 dark:ring-gray-700 z-50 py-1"
|
|
style="display:none"
|
|
>
|
|
@can('View:SyncPage')
|
|
<button
|
|
type="button"
|
|
@click="open = false; showConfirm = true"
|
|
class="flex items-center gap-3 px-4 py-2.5 text-sm text-gray-700 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-700 transition w-full"
|
|
>
|
|
<x-heroicon-o-arrows-right-left class="w-4 h-4 text-primary-500 shrink-0" />
|
|
{{ __('navigation.sync.label') }}
|
|
</button>
|
|
@endcan
|
|
|
|
@can('View:SyncPage')
|
|
<a
|
|
href="{{ \App\Filament\Pages\SyncPage::getUrl() }}"
|
|
@click="open = false"
|
|
class="flex items-center gap-3 px-4 py-2.5 text-sm text-gray-700 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-700 transition"
|
|
>
|
|
<x-heroicon-o-document-magnifying-glass class="w-4 h-4 text-info-500 shrink-0" />
|
|
{{ __('navigation.sync.details') }}
|
|
</a>
|
|
@endcan
|
|
|
|
@can('View:SyncPage')
|
|
<a
|
|
href="{{ route('backup.download') }}"
|
|
@click="open = false"
|
|
class="flex items-center gap-3 px-4 py-2.5 text-sm text-gray-700 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-700 transition"
|
|
>
|
|
<x-heroicon-o-arrow-down-tray class="w-4 h-4 text-success-500 shrink-0" />
|
|
{{ __('navigation.backup.label') }}
|
|
</a>
|
|
@endcan
|
|
</div>
|
|
|
|
<template x-teleport="body">
|
|
<div
|
|
x-show="showConfirm"
|
|
x-transition.opacity
|
|
class="fixed inset-0 z-999 flex items-center justify-center bg-black/50 backdrop-blur-sm"
|
|
style="display:none"
|
|
>
|
|
<div
|
|
@click.outside="showConfirm = false"
|
|
class="bg-white dark:bg-gray-800 rounded-2xl shadow-2xl w-full max-w-md mx-4 overflow-hidden"
|
|
>
|
|
<div class="px-6 pt-6 pb-4">
|
|
<div class="flex items-center gap-3 mb-4">
|
|
<div class="flex items-center justify-center w-10 h-10 rounded-full bg-primary-50 dark:bg-primary-900/30">
|
|
<x-heroicon-o-arrows-right-left class="w-5 h-5 text-primary-500" />
|
|
</div>
|
|
<h3 class="text-lg font-semibold text-gray-900 dark:text-white">همگامسازی دو طرفه</h3>
|
|
</div>
|
|
<p class="text-sm text-gray-600 dark:text-gray-400">
|
|
اطلاعات این سیستم با سیستم مقابل همگامسازی خواهد شد. آیا مطمئنید؟
|
|
</p>
|
|
</div>
|
|
<div class="flex items-center justify-end gap-3 px-6 py-4 bg-gray-50 dark:bg-gray-900/50">
|
|
<button
|
|
@click="showConfirm = false"
|
|
class="px-4 py-2 text-sm font-medium text-gray-700 dark:text-gray-300 bg-white dark:bg-gray-700 border border-gray-300 dark:border-gray-600 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-600 transition"
|
|
>
|
|
انصراف
|
|
</button>
|
|
<button
|
|
@click="startSync()"
|
|
class="px-4 py-2 text-sm font-medium text-white bg-primary-600 rounded-lg hover:bg-primary-700 transition"
|
|
>
|
|
بله، همگام کن
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<template x-teleport="body">
|
|
<div
|
|
x-show="showProgress"
|
|
x-transition.opacity
|
|
class="fixed inset-0 z-999 flex items-center justify-center bg-black/50 backdrop-blur-sm"
|
|
style="display:none"
|
|
>
|
|
<div class="bg-white dark:bg-gray-800 rounded-2xl shadow-2xl w-full max-w-md mx-4 overflow-hidden">
|
|
<div class="px-6 pt-6 pb-4">
|
|
<div class="flex items-center gap-3 mb-5">
|
|
<div class="flex items-center justify-center w-10 h-10 rounded-full"
|
|
:class="failed ? 'bg-danger-50 dark:bg-danger-900/30' : (done ? 'bg-success-50 dark:bg-success-900/30' : 'bg-primary-50 dark:bg-primary-900/30')">
|
|
<template x-if="!done && !failed">
|
|
<svg class="w-5 h-5 text-primary-500 animate-spin" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
|
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
|
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"></path>
|
|
</svg>
|
|
</template>
|
|
<template x-if="done">
|
|
<x-heroicon-o-check-circle class="w-5 h-5 text-success-500" />
|
|
</template>
|
|
<template x-if="failed">
|
|
<x-heroicon-o-x-circle class="w-5 h-5 text-danger-500" />
|
|
</template>
|
|
</div>
|
|
<h3 class="text-lg font-semibold text-gray-900 dark:text-white">همگامسازی</h3>
|
|
</div>
|
|
<div class="w-full bg-gray-200 dark:bg-gray-700 rounded-full h-3 mb-3 overflow-hidden">
|
|
<div
|
|
class="h-3 rounded-full transition-all duration-500 ease-out"
|
|
:class="failed ? 'bg-danger-500' : (done ? 'bg-success-500' : 'bg-primary-500')"
|
|
:style="'width: ' + Math.min(progress, 100) + '%'"
|
|
></div>
|
|
</div>
|
|
|
|
<p class="text-sm text-gray-600 dark:text-gray-400 mb-1" x-text="statusText"></p>
|
|
<p class="text-xs text-gray-400 dark:text-gray-500" x-text="Math.round(Math.min(progress, 100)) + '%'"></p>
|
|
<template x-if="reportLines.length > 0">
|
|
<div class="mt-4 max-h-40 overflow-y-auto bg-gray-50 dark:bg-gray-900/50 rounded-lg p-3">
|
|
<template x-for="(line, i) in reportLines" :key="i">
|
|
<p class="text-xs text-gray-600 dark:text-gray-400 leading-relaxed" x-text="line"></p>
|
|
</template>
|
|
</div>
|
|
</template>
|
|
</div>
|
|
|
|
<div class="flex items-center justify-end px-6 py-4 bg-gray-50 dark:bg-gray-900/50">
|
|
<button
|
|
x-show="done || failed"
|
|
@click="closeModal()"
|
|
class="px-4 py-2 text-sm font-medium text-white rounded-lg transition"
|
|
:class="done ? 'bg-success-600 hover:bg-success-700' : 'bg-gray-600 hover:bg-gray-700'"
|
|
x-text="done ? 'بستن' : 'بستن'"
|
|
></button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
</div>
|
|
@endcan
|
|
|
|
</div>
|