user()->can('View:SyncPage'); } public static function shouldRegisterNavigation(): bool { return false; } public static function getNavigationIcon(): \BackedEnum|string|null { return 'heroicon-o-arrows-right-left'; } public static function getNavigationGroup(): ?string { return __('navigation.groups.system'); } public string $report = ''; public string $importReport = ''; public bool $importing = false; public $sqlFile = null; public static function getNavigationLabel(): string { return __('navigation.sync.label'); } public function getTitle(): string { return __('navigation.sync.title'); } public function getPendingCount(): int { return SyncLog::changesSince((int) OsurgInitial::val('our_sync_cursor', 0))->count(); } public function getLastSyncTime(): string { $last = SyncLog::where('action', 'synced')->latest('id')->first(); if (! $last) { return __('navigation.sync.never'); } return Jalalian::fromCarbon($last->datetime->timezone('Asia/Tehran'))->format('Y/m/d H:i:s'); } protected function rules(): array { return [ 'sqlFile' => ['required', 'file', 'max:307200'], ]; } protected function getHeaderActions(): array { return [ Action::make('sync') ->label(__('navigation.sync.sync_with_peer')) ->icon('heroicon-o-arrows-right-left') ->color('primary') ->requiresConfirmation() ->modalHeading(__('navigation.sync.confirm_heading')) ->modalDescription(__('navigation.sync.confirm_description')) ->modalSubmitActionLabel(__('navigation.sync.confirm_button')) ->action('runSync'), Action::make('backup') ->label(__('navigation.backup.label')) ->icon('heroicon-o-arrow-down-tray') ->color('success') ->action('downloadBackup'), ]; } public function runSync(): void { $service = SyncService::fromConfig(); if (! $service->isConfigured()) { Notification::make() ->title(__('navigation.sync.error_title')) ->body(__('navigation.sync.error_not_configured')) ->danger() ->send(); return; } $result = $service->sync(); $this->report = implode("\n", $result['report']); Notification::make() ->title($result['success'] ? __('navigation.sync.success') : __('navigation.sync.failed')) ->body(__('navigation.sync.see_report')) ->color($result['success'] ? 'success' : 'warning') ->send(); } public function startImport(): void { $this->validate(); $this->importing = true; try { @set_time_limit(600); $tmpPath = $this->sqlFile->getRealPath(); $service = new OsurgImportService(); $result = $service->import($tmpPath); $this->importReport = implode("\n", $result['report']); Notification::make() ->title($result['success'] ? __('navigation.import.success') : __('navigation.import.failed')) ->body(__('navigation.sync.see_report')) ->color($result['success'] ? 'success' : 'danger') ->send(); } finally { $this->sqlFile = null; $this->importing = false; } } public function downloadBackup(): \Symfony\Component\HttpFoundation\StreamedResponse { $data = [ 'exported_at' => now()->toDateTimeString(), 'last_sync_id' => SyncLog::lastSyncId(), 'changes' => SyncLog::all()->toArray(), ]; $filename = 'matab-backup-' . now()->format('Y-m-d_H-i-s') . '.json'; return response()->streamDownload(function () use ($data) { echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT); }, $filename, ['Content-Type' => 'application/json']); } }