import 'package:dio/dio.dart'; import '../core/api_client.dart'; import '../models/business_dashboard_models.dart'; import 'business_dashboard_service.dart'; import 'support_service.dart'; /// سرویس داشبورد پروفایل کاربر /// /// تلاش می‌کند از اندپوینت‌های پروفایل استفاده کند. در صورت عدم وجود /// (مثلاً 404)، برای MVP از fallback محلی استفاده می‌کند تا UI کار کند. class ProfileDashboardService { final ApiClient _apiClient; final BusinessDashboardService _businessService; ProfileDashboardService(this._apiClient) : _businessService = BusinessDashboardService(_apiClient); // --- تعاریف ویجت‌ها --- Future getWidgetDefinitions() async { try { final res = await _apiClient.get>( '/api/v1/profile/dashboard/widgets/definitions', ); final data = res.data?['data'] as Map? ?? const {}; final defs = DashboardDefinitionsResponse.fromJson(data); if (defs.items.isEmpty || defs.columns.isEmpty) { return _fallbackDefinitions(); } return defs; } on DioException catch (e) { // اگر اندپوینت وجود ندارد یا هر خطایی بود، fallback if (e.response?.statusCode == 404) { return _fallbackDefinitions(); } return _fallbackDefinitions(); } catch (_) { return _fallbackDefinitions(); } } // --- پروفایل چیدمان --- Future getLayoutProfile({ required String breakpoint, }) async { try { final res = await _apiClient.get>( '/api/v1/profile/dashboard/layout', query: {'breakpoint': breakpoint}, ); final data = res.data?['data'] as Map? ?? const {}; final profile = DashboardLayoutProfile.fromJson(data); if (profile.items.isEmpty) { // از پیش‌فرض مبتنی بر تعاریف بساز final defs = await getWidgetDefinitions(); return _buildDefaultLayout(defs, breakpoint); } return profile; } on DioException catch (e) { if (e.response?.statusCode == 404) { final defs = await getWidgetDefinitions(); return _buildDefaultLayout(defs, breakpoint); } final defs = await getWidgetDefinitions(); return _buildDefaultLayout(defs, breakpoint); } catch (_) { final defs = await getWidgetDefinitions(); return _buildDefaultLayout(defs, breakpoint); } } Future putLayoutProfile({ required String breakpoint, required List items, }) async { try { final body = { 'breakpoint': breakpoint, 'items': items.map((e) => e.toJson()).toList(), }; final res = await _apiClient.put>( '/api/v1/profile/dashboard/layout', data: body, ); final data = res.data?['data'] as Map? ?? const {}; return DashboardLayoutProfile.fromJson(data); } catch (_) { // اگر ذخیره نشد، همان ورودی را به عنوان حالت فعلی برگردان return DashboardLayoutProfile( breakpoint: breakpoint, columns: _fallbackColumns()[breakpoint] ?? 8, items: items, version: 1, updatedAt: '', ); } } // --- داده‌ی ویجت‌ها (Batch) --- Future> getWidgetsBatchData({ required List widgetKeys, Map? filters, }) async { // تلاش برای استفاده از اندپوینت پروفایل try { final response = await _apiClient.post>( '/api/v1/profile/dashboard/data', data: { 'widget_keys': widgetKeys, 'filters': filters ?? const {}, }, ); if (response.data?['success'] == true) { final data = response.data!['data'] as Map? ?? const {}; final out = Map.from(data); // پر کردن داده‌ی ویجت‌هایی که سرور نداد با fallback return _withFallbackData(out, widgetKeys); } } catch (_) { // ادامه می‌دهیم تا fallback پر شود } return _fallbackBatchData(widgetKeys); } // --- Fallbacks --- DashboardDefinitionsResponse _fallbackDefinitions() { final columns = _fallbackColumns(); final items = [ DashboardWidgetDefinition( key: 'profile_recent_businesses', title: 'کسب‌وکارهای شما', icon: 'business', version: 1, permissionsRequired: const [], defaults: { 'xs': {'colSpan': 1, 'rowSpan': 2}, 'sm': {'colSpan': 2, 'rowSpan': 2}, 'md': {'colSpan': 4, 'rowSpan': 2}, 'lg': {'colSpan': 4, 'rowSpan': 2}, 'xl': {'colSpan': 4, 'rowSpan': 2}, }, ), DashboardWidgetDefinition( key: 'profile_announcements', title: 'اعلان‌ها', icon: 'notifications', version: 1, permissionsRequired: const [], defaults: { 'xs': {'colSpan': 1, 'rowSpan': 2}, 'sm': {'colSpan': 2, 'rowSpan': 2}, 'md': {'colSpan': 4, 'rowSpan': 2}, 'lg': {'colSpan': 4, 'rowSpan': 2}, 'xl': {'colSpan': 4, 'rowSpan': 2}, }, ), DashboardWidgetDefinition( key: 'profile_support_tickets', title: 'تیکت‌های پشتیبانی', icon: 'support_agent', version: 1, permissionsRequired: const [], defaults: { 'xs': {'colSpan': 1, 'rowSpan': 2}, 'sm': {'colSpan': 2, 'rowSpan': 2}, 'md': {'colSpan': 4, 'rowSpan': 2}, 'lg': {'colSpan': 4, 'rowSpan': 2}, 'xl': {'colSpan': 4, 'rowSpan': 2}, }, ), DashboardWidgetDefinition( key: 'profile_onboarding_checklist', title: 'چک‌لیست شروع', icon: 'checklist', version: 1, permissionsRequired: const [], defaults: { 'xs': {'colSpan': 1, 'rowSpan': 2}, 'sm': {'colSpan': 2, 'rowSpan': 2}, 'md': {'colSpan': 4, 'rowSpan': 2}, 'lg': {'colSpan': 4, 'rowSpan': 2}, 'xl': {'colSpan': 4, 'rowSpan': 2}, }, ), ]; return DashboardDefinitionsResponse(columns: columns, items: items); } Map _fallbackColumns() => const { 'xs': 1, 'sm': 4, 'md': 8, 'lg': 12, 'xl': 12, }; DashboardLayoutProfile _buildDefaultLayout( DashboardDefinitionsResponse defs, String breakpoint, ) { final cols = defs.columns[breakpoint] ?? 8; int order = 1; final items = []; for (final d in defs.items) { final dflt = d.defaults[breakpoint] ?? const {}; final colSpan = (dflt['colSpan'] ?? (cols / 2).floor()).clamp(1, cols); final rowSpan = dflt['rowSpan'] ?? 2; items.add(DashboardLayoutItem( key: d.key, order: order++, colSpan: colSpan, rowSpan: rowSpan, hidden: false, )); } return DashboardLayoutProfile( breakpoint: breakpoint, columns: cols, items: items, version: 1, updatedAt: '', ); } Map _fallbackBatchData(List keys) { final out = {}; return _withFallbackData(out, keys); } Map _withFallbackData( Map base, List keys, ) { final out = Map.from(base); for (final k in keys) { if (out.containsKey(k)) continue; if (k == 'profile_recent_businesses') { out[k] = { 'items': >[], }; } else if (k == 'profile_announcements') { out[k] = { 'items': >[ { 'title': 'به حسابیکس خوش آمدید', 'body': 'به‌زودی تجربه داشبورد شخصی‌سازی‌شده را خواهید داشت.', 'time': DateTime.now().toIso8601String(), }, ], }; } else if (k == 'profile_support_tickets') { out[k] = { 'items': >[ { 'id': 1001, 'subject': 'سؤال درباره صدور فاکتور', 'status': 'باز', 'updated_at': DateTime.now().subtract(const Duration(hours: 3)).toIso8601String(), }, { 'id': 1000, 'subject': 'مشکل ورود به حساب', 'status': 'بسته', 'updated_at': DateTime.now().subtract(const Duration(days: 2)).toIso8601String(), }, ], }; } else if (k == 'profile_onboarding_checklist') { out[k] = { 'items': >[ {'key': 'create_business', 'title': 'ایجاد اولین کسب‌وکار', 'done': false}, {'key': 'add_person', 'title': 'افزودن اولین مخاطب', 'done': false}, {'key': 'issue_invoice', 'title': 'صدور اولین فاکتور', 'done': false}, ], }; } } return out; } // کمک‌متد برای تأمین داده واقعی برخی ویجت‌ها (مثل لیست کسب‌وکارها) Future> hydrateSpecialWidgets( Map currentData, List keys, ) async { final out = Map.from(currentData); if (keys.contains('profile_recent_businesses')) { try { final businesses = await _businessService.getUserBusinesses(); out['profile_recent_businesses'] = { 'items': businesses .map((b) => { 'id': b.id, 'name': b.name, 'role': b.role, 'is_owner': b.isOwner, }) .toList(), }; } catch (_) { // در سکوت ادامه می‌دهیم؛ داده‌ی موجود کافی است } } if (keys.contains('profile_support_tickets')) { try { final support = SupportService(_apiClient); final res = await support.searchUserTickets({ 'page': 1, 'limit': 5, 'sort_by': 'updated_at', 'sort_desc': true, }); out['profile_support_tickets'] = { 'items': res.items.map((t) { return { 'id': t.id, 'subject': t.title, 'status': t.status?.name ?? '', 'updated_at': t.updatedAt.toIso8601String(), }; }).toList(), }; } catch (_) { // fallback باقی می‌ماند } } return out; } }