diff --git a/app/Filament/Tenant/Pages/Backup.php b/app/Filament/Tenant/Pages/Backup.php new file mode 100644 index 0000000..938fb37 --- /dev/null +++ b/app/Filament/Tenant/Pages/Backup.php @@ -0,0 +1,42 @@ +current(); + if (! $tenant) abort(404); + + // Re-fetch via central scope to avoid issues. + $company = Company::withoutGlobalScopes()->find($tenant->id); + if (! $company) abort(404); + + $svc = app(TenantBackupService::class); + $file = $svc->export($company); + $filename = $svc->filename($company); + + return response()->download($file, $filename, [ + 'Content-Type' => 'application/zip', + ])->deleteFileAfterSend(); + } +} diff --git a/app/Filament/Tenant/Pages/Integrations.php b/app/Filament/Tenant/Pages/Integrations.php new file mode 100644 index 0000000..09f23eb --- /dev/null +++ b/app/Filament/Tenant/Pages/Integrations.php @@ -0,0 +1,119 @@ +current(); + $this->config = (array) ($tenant?->settings['integrations'] ?? []); + } + + public function setStatus(string $key, bool $enabled): void + { + $this->config[$key]['enabled'] = $enabled; + $this->save(); + } + + public function updateField(string $key, string $field, ?string $value): void + { + $this->config[$key][$field] = $value; + } + + public function save(): void + { + $tenant = app(TenantManager::class)->current(); + if (! $tenant) return; + $tenant->update([ + 'settings' => array_merge((array) $tenant->settings, [ + 'integrations' => $this->config, + ]), + ]); + Notification::make()->title('Integrări salvate')->success()->send(); + } + + public function integrations(): array + { + return [ + 'telegram_bot' => [ + 'name' => 'Telegram Bot', + 'icon' => '✈️', + 'color' => '#229ED9', + 'description' => 'Primește cereri din canalul Telegram + trimite confirmări automate.', + 'fields' => [ + 'bot_token' => ['label' => 'Bot Token', 'type' => 'password', 'placeholder' => 'din @BotFather'], + 'chat_id' => ['label' => 'Channel/Chat ID', 'type' => 'text', 'placeholder' => '@psauto_md'], + ], + ], + 'whatsapp_business' => [ + 'name' => 'WhatsApp Business', + 'icon' => '💬', + 'color' => '#25D366', + 'description' => 'Trimite mesaje template către clienți (programări, confirmări).', + 'fields' => [ + 'phone_id' => ['label' => 'Phone Number ID', 'type' => 'text'], + 'token' => ['label' => 'Permanent Access Token', 'type' => 'password'], + ], + ], + 'google_ads' => [ + 'name' => 'Google Ads', + 'icon' => '🔍', + 'color' => '#EA4335', + 'description' => 'Tracking conversii — leadurile generate de campanii Google.', + 'fields' => [ + 'customer_id' => ['label' => 'Customer ID', 'type' => 'text', 'placeholder' => '123-456-7890'], + 'conversion_id' => ['label' => 'Conversion ID', 'type' => 'text'], + ], + ], + 'facebook' => [ + 'name' => 'Facebook / Instagram Lead Ads', + 'icon' => '📘', + 'color' => '#1877F2', + 'description' => 'Importă lead-uri direct din Lead Ads forms.', + 'fields' => [ + 'page_id' => ['label' => 'Page ID', 'type' => 'text'], + 'access_token' => ['label' => 'Access Token', 'type' => 'password'], + ], + ], + 'sms_gateway' => [ + 'name' => 'SMS Gateway', + 'icon' => '📱', + 'color' => '#6366F1', + 'description' => 'Trimite SMS-uri prin gateway local (Moldcell/Orange API).', + 'fields' => [ + 'provider' => ['label' => 'Provider', 'type' => 'text', 'placeholder' => 'moldcell / orange / twilio'], + 'api_key' => ['label' => 'API Key', 'type' => 'password'], + 'sender_id' => ['label' => 'Sender ID', 'type' => 'text'], + ], + ], + 'webhook_in' => [ + 'name' => 'Webhook intrări (formulare site)', + 'icon' => '🪝', + 'color' => '#8B5CF6', + 'description' => 'URL secret pentru POST direct cu lead-uri din site-ul tău.', + 'fields' => [ + 'secret' => ['label' => 'Secret token', 'type' => 'text', 'placeholder' => 'random string'], + ], + ], + ]; + } +} diff --git a/app/Services/TenantBackupService.php b/app/Services/TenantBackupService.php new file mode 100644 index 0000000..d0edf02 --- /dev/null +++ b/app/Services/TenantBackupService.php @@ -0,0 +1,95 @@ +slug . '-' . date('Ymd-His') . '.zip'; + + $zip = new ZipArchive(); + if ($zip->open($file, ZipArchive::CREATE | ZipArchive::OVERWRITE) !== true) { + throw new \RuntimeException('Nu pot crea fișierul ZIP la ' . $file); + } + + // Tables to dump. + $tables = [ + 'company' => Company::withoutGlobalScopes()->where('id', $company->id)->get(), + 'users' => User::all(), + 'clients' => Client::all(), + 'vehicles' => Vehicle::all(), + 'leads' => Lead::all(), + 'deals' => Deal::all(), + 'appointments' => Appointment::all(), + 'work_orders' => WorkOrder::with(['works', 'parts'])->get(), + 'wo_works' => WorkOrderWork::all(), + 'wo_parts' => WorkOrderPart::all(), + 'labors' => Labor::all(), + 'parts' => Part::all(), + 'suppliers' => Supplier::all(), + 'purchases' => Purchase::with('items')->get(), + 'purchase_items' => PurchaseItem::all(), + 'payments' => Payment::all(), + 'expenses' => Expense::all(), + 'employee_profiles' => EmployeeProfile::all(), + ]; + + $manifest = [ + 'tenant' => $company->only(['id', 'slug', 'name', 'city', 'phone', 'email']), + 'exported_at' => now()->toIso8601String(), + 'app_version' => 'AutoCRM-1.0', + 'counts' => [], + ]; + + foreach ($tables as $name => $rows) { + $json = $rows->toJson(JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); + $zip->addFromString("data/{$name}.json", $json); + $manifest['counts'][$name] = $rows->count(); + } + + $zip->addFromString('manifest.json', json_encode($manifest, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); + + // Optionally embed media files (logo + favicon). + foreach (['logo', 'favicon'] as $col) { + $m = $company->getFirstMedia($col); + if ($m && file_exists($m->getPath())) { + $zip->addFile($m->getPath(), 'media/' . $col . '.' . pathinfo($m->getPath(), PATHINFO_EXTENSION)); + } + } + + $zip->close(); + return $file; + } + + public function filename(Company $company): string + { + return 'tenant-' . $company->slug . '-' . date('Ymd-His') . '.zip'; + } +} diff --git a/resources/views/filament/tenant/pages/backup.blade.php b/resources/views/filament/tenant/pages/backup.blade.php new file mode 100644 index 0000000..b9dcbf3 --- /dev/null +++ b/resources/views/filament/tenant/pages/backup.blade.php @@ -0,0 +1,46 @@ + + + +
+

📦 Export complet date tenant

+

+ Exportă toate datele companiei tale într-un fișier ZIP. Util pentru: +

+ +

+ Conține: clienți, mașini, lead-uri, deal-uri, programări, fișe de lucru cu manopere și piese, + depozit, furnizori, achiziții, plăți, cheltuieli, salarii, useri, logo + favicon, manifest cu metadata. +

+

+ Format: ZIP cu fișiere JSON (1 per tabel) + media + manifest.json. +

+ + + +

+ ⚠ Backup-urile pot conține date sensibile (telefoane, emailuri, plăți). Stochează-le în siguranță. +

+
+
diff --git a/resources/views/filament/tenant/pages/integrations.blade.php b/resources/views/filament/tenant/pages/integrations.blade.php new file mode 100644 index 0000000..5e7d832 --- /dev/null +++ b/resources/views/filament/tenant/pages/integrations.blade.php @@ -0,0 +1,78 @@ + + + +
+ ℹ️ Status: aceste integrări sunt placeholder UI — le configurezi aici, dar implementarea actuală a fiecărui canal e proiect separat. + Pentru moment, valorile salvate sunt accesibile în cod via $tenant->settings['integrations']. +
+ +
+ @foreach ($this->integrations() as $key => $meta) + @php + $cfg = $config[$key] ?? []; + $enabled = $cfg['enabled'] ?? false; + @endphp +
+
+
{{ $meta['icon'] }}
+
{{ $meta['name'] }}
+ +
+
{{ $meta['description'] }}
+ +
+ @foreach ($meta['fields'] as $fkey => $fmeta) +
+ + +
+ @endforeach +
+ +
+ @endforeach +
+