import 'package:flutter/material.dart'; import '../../../../core/utils/media_url.dart'; class GeneralSettings { const GeneralSettings({ this.defaultBranch = '', this.branchCodeFormat = 'BR-{000}', this.timeZone = 'Asia/Kolkata', this.language = 'en', this.currency = 'INR', this.dateFormat = 'dd/MM/yyyy', this.timeFormat = 'HH:mm', this.numberFormat = '1,234.56', }); final String defaultBranch; final String branchCodeFormat; final String timeZone; final String language; final String currency; final String dateFormat; final String timeFormat; final String numberFormat; GeneralSettings copyWith({ String? defaultBranch, String? branchCodeFormat, String? timeZone, String? language, String? currency, String? dateFormat, String? timeFormat, String? numberFormat, }) { return GeneralSettings( defaultBranch: defaultBranch ?? this.defaultBranch, branchCodeFormat: branchCodeFormat ?? this.branchCodeFormat, timeZone: timeZone ?? this.timeZone, language: language ?? this.language, currency: currency ?? this.currency, dateFormat: dateFormat ?? this.dateFormat, timeFormat: timeFormat ?? this.timeFormat, numberFormat: numberFormat ?? this.numberFormat, ); } Map toJson() => { 'defaultBranch': defaultBranch, 'branchCodeFormat': branchCodeFormat, 'timeZone': timeZone, 'language': language, 'currency': currency, 'dateFormat': dateFormat, 'timeFormat': timeFormat, 'numberFormat': numberFormat, }; factory GeneralSettings.fromJson(Map json) => GeneralSettings( defaultBranch: json['defaultBranch'] as String? ?? '', branchCodeFormat: json['branchCodeFormat'] as String? ?? 'BR-{000}', timeZone: json['timeZone'] as String? ?? 'Asia/Kolkata', language: json['language'] as String? ?? 'en', currency: json['currency'] as String? ?? 'INR', dateFormat: json['dateFormat'] as String? ?? 'dd/MM/yyyy', timeFormat: json['timeFormat'] as String? ?? 'HH:mm', numberFormat: json['numberFormat'] as String? ?? '1,234.56', ); } class CompanyProfileSettings { const CompanyProfileSettings({ this.companyName = '', this.companyCode = '', this.registrationNumber = '', this.gstNumber = '', this.address = '', this.city = '', this.state = '', this.pincode = '', this.email = '', this.phone = '', this.website = '', this.logoUrl = '', this.faviconUrl = '', }); final String companyName; final String companyCode; final String registrationNumber; final String gstNumber; final String address; final String city; final String state; final String pincode; final String email; final String phone; final String website; final String logoUrl; final String faviconUrl; CompanyProfileSettings copyWith({ String? companyName, String? companyCode, String? registrationNumber, String? gstNumber, String? address, String? city, String? state, String? pincode, String? email, String? phone, String? website, String? logoUrl, String? faviconUrl, }) { return CompanyProfileSettings( companyName: companyName ?? this.companyName, companyCode: companyCode ?? this.companyCode, registrationNumber: registrationNumber ?? this.registrationNumber, gstNumber: gstNumber ?? this.gstNumber, address: address ?? this.address, city: city ?? this.city, state: state ?? this.state, pincode: pincode ?? this.pincode, email: email ?? this.email, phone: phone ?? this.phone, website: website ?? this.website, logoUrl: logoUrl ?? this.logoUrl, faviconUrl: faviconUrl ?? this.faviconUrl, ); } Map toJson() => { 'companyName': companyName, 'companyCode': companyCode, 'registrationNumber': registrationNumber, 'gstNumber': gstNumber, 'address': address, 'city': city, 'state': state, 'pincode': pincode, 'email': email, 'phone': phone, 'website': website, 'logoUrl': logoUrl, 'faviconUrl': faviconUrl, }; /// Payload for `PUT /settings/company` ([CompanySettingsBody]). Map toApiJson() => { 'org_name': companyName, 'gstin': gstNumber, 'mobile': phone, 'email': email, 'website': website, 'address': address, 'city': city, 'state': state, 'pincode': pincode, }; factory CompanyProfileSettings.fromJson(Map json) => CompanyProfileSettings.fromApiJson(json); factory CompanyProfileSettings.fromApiJson(Map json) => CompanyProfileSettings( companyName: json['org_name'] as String? ?? json['companyName'] as String? ?? '', companyCode: json['companyCode'] as String? ?? '', registrationNumber: json['registrationNumber'] as String? ?? '', gstNumber: json['gstin'] as String? ?? json['gstNumber'] as String? ?? '', address: json['address'] as String? ?? '', city: json['city'] as String? ?? '', state: json['state'] as String? ?? '', pincode: json['pincode'] as String? ?? '', email: json['email'] as String? ?? '', phone: json['mobile'] as String? ?? json['phone'] as String? ?? '', website: json['website'] as String? ?? '', logoUrl: resolveMediaUrl( json['logo_url'] as String? ?? json['logoUrl'] as String? ?? json['logo'] as String?, ) ?? '', faviconUrl: resolveMediaUrl( json['favicon_url'] as String? ?? json['faviconUrl'] as String? ?? json['favicon'] as String?, ) ?? '', ); } class UiPreferencesSettings { const UiPreferencesSettings({ this.sidebarExpanded = true, this.navigationLayout = 'sidebar', this.dashboardLayout = 'default', this.tableDensity = 'comfortable', this.paginationSize = 20, }); final bool sidebarExpanded; final String navigationLayout; final String dashboardLayout; final String tableDensity; final int paginationSize; UiPreferencesSettings copyWith({ bool? sidebarExpanded, String? navigationLayout, String? dashboardLayout, String? tableDensity, int? paginationSize, }) { return UiPreferencesSettings( sidebarExpanded: sidebarExpanded ?? this.sidebarExpanded, navigationLayout: navigationLayout ?? this.navigationLayout, dashboardLayout: dashboardLayout ?? this.dashboardLayout, tableDensity: tableDensity ?? this.tableDensity, paginationSize: paginationSize ?? this.paginationSize, ); } Map toJson() => { 'sidebarExpanded': sidebarExpanded, 'navigationLayout': navigationLayout, 'dashboardLayout': dashboardLayout, 'tableDensity': tableDensity, 'paginationSize': paginationSize, }; factory UiPreferencesSettings.fromJson(Map json) => UiPreferencesSettings( sidebarExpanded: json['sidebarExpanded'] as bool? ?? true, navigationLayout: json['navigationLayout'] as String? ?? 'sidebar', dashboardLayout: json['dashboardLayout'] as String? ?? 'default', tableDensity: json['tableDensity'] as String? ?? 'comfortable', paginationSize: json['paginationSize'] as int? ?? 20, ); } class AssetSettingsConfig { const AssetSettingsConfig({ this.codePrefix = 'AST', this.runningNumberLength = 6, this.autoGenerateCode = true, this.warrantyReminderDays = 30, this.expiryAlertDays = 7, this.qrEnabled = true, this.qrFormat = 'asset_id', this.labelSize = '50x25mm', this.enabledStatuses = const [ 'available', 'allocated', 'maintenance', 'lost', 'disposed', 'retired', ], }); final String codePrefix; final int runningNumberLength; final bool autoGenerateCode; final int warrantyReminderDays; final int expiryAlertDays; final bool qrEnabled; final String qrFormat; final String labelSize; final List enabledStatuses; AssetSettingsConfig copyWith({ String? codePrefix, int? runningNumberLength, bool? autoGenerateCode, int? warrantyReminderDays, int? expiryAlertDays, bool? qrEnabled, String? qrFormat, String? labelSize, List? enabledStatuses, }) { return AssetSettingsConfig( codePrefix: codePrefix ?? this.codePrefix, runningNumberLength: runningNumberLength ?? this.runningNumberLength, autoGenerateCode: autoGenerateCode ?? this.autoGenerateCode, warrantyReminderDays: warrantyReminderDays ?? this.warrantyReminderDays, expiryAlertDays: expiryAlertDays ?? this.expiryAlertDays, qrEnabled: qrEnabled ?? this.qrEnabled, qrFormat: qrFormat ?? this.qrFormat, labelSize: labelSize ?? this.labelSize, enabledStatuses: enabledStatuses ?? this.enabledStatuses, ); } Map toJson() => { 'codePrefix': codePrefix, 'runningNumberLength': runningNumberLength, 'autoGenerateCode': autoGenerateCode, 'warrantyReminderDays': warrantyReminderDays, 'expiryAlertDays': expiryAlertDays, 'qrEnabled': qrEnabled, 'qrFormat': qrFormat, 'labelSize': labelSize, 'enabledStatuses': enabledStatuses, }; factory AssetSettingsConfig.fromJson(Map json) => AssetSettingsConfig( codePrefix: json['codePrefix'] as String? ?? 'AST', runningNumberLength: json['runningNumberLength'] as int? ?? 6, autoGenerateCode: json['autoGenerateCode'] as bool? ?? true, warrantyReminderDays: json['warrantyReminderDays'] as int? ?? 30, expiryAlertDays: json['expiryAlertDays'] as int? ?? 7, qrEnabled: json['qrEnabled'] as bool? ?? true, qrFormat: json['qrFormat'] as String? ?? 'asset_id', labelSize: json['labelSize'] as String? ?? '50x25mm', enabledStatuses: (json['enabledStatuses'] as List?) ?.map((e) => e.toString()) .toList() ?? const [ 'available', 'allocated', 'maintenance', 'lost', 'disposed', 'retired', ], ); } class NotificationSettingsConfig { const NotificationSettingsConfig({ this.emailAssetAllocation = true, this.emailAssetReturn = true, this.emailMaintenanceRequest = true, this.emailWarrantyExpiry = true, this.smsEnabled = false, this.pushEnabled = true, this.inAppEnabled = true, }); final bool emailAssetAllocation; final bool emailAssetReturn; final bool emailMaintenanceRequest; final bool emailWarrantyExpiry; final bool smsEnabled; final bool pushEnabled; final bool inAppEnabled; NotificationSettingsConfig copyWith({ bool? emailAssetAllocation, bool? emailAssetReturn, bool? emailMaintenanceRequest, bool? emailWarrantyExpiry, bool? smsEnabled, bool? pushEnabled, bool? inAppEnabled, }) { return NotificationSettingsConfig( emailAssetAllocation: emailAssetAllocation ?? this.emailAssetAllocation, emailAssetReturn: emailAssetReturn ?? this.emailAssetReturn, emailMaintenanceRequest: emailMaintenanceRequest ?? this.emailMaintenanceRequest, emailWarrantyExpiry: emailWarrantyExpiry ?? this.emailWarrantyExpiry, smsEnabled: smsEnabled ?? this.smsEnabled, pushEnabled: pushEnabled ?? this.pushEnabled, inAppEnabled: inAppEnabled ?? this.inAppEnabled, ); } Map toJson() => { 'emailAssetAllocation': emailAssetAllocation, 'emailAssetReturn': emailAssetReturn, 'emailMaintenanceRequest': emailMaintenanceRequest, 'emailWarrantyExpiry': emailWarrantyExpiry, 'smsEnabled': smsEnabled, 'pushEnabled': pushEnabled, 'inAppEnabled': inAppEnabled, }; factory NotificationSettingsConfig.fromJson(Map json) => NotificationSettingsConfig( emailAssetAllocation: json['emailAssetAllocation'] as bool? ?? true, emailAssetReturn: json['emailAssetReturn'] as bool? ?? true, emailMaintenanceRequest: json['emailMaintenanceRequest'] as bool? ?? true, emailWarrantyExpiry: json['emailWarrantyExpiry'] as bool? ?? true, smsEnabled: json['smsEnabled'] as bool? ?? false, pushEnabled: json['pushEnabled'] as bool? ?? true, inAppEnabled: json['inAppEnabled'] as bool? ?? true, ); } class EmailConfigurationSettings { const EmailConfigurationSettings({ this.smtpHost = '', this.smtpPort = 587, this.smtpUsername = '', this.smtpPassword = '', this.senderEmail = '', this.senderName = '', this.allocationTemplate = 'Your asset {{asset_name}} has been allocated.', this.returnTemplate = 'Your asset {{asset_name}} has been returned.', this.maintenanceTemplate = 'Maintenance request created for {{asset_name}}.', this.warrantyTemplate = 'Warranty for {{asset_name}} expires on {{date}}.', }); final String smtpHost; final int smtpPort; final String smtpUsername; final String smtpPassword; final String senderEmail; final String senderName; final String allocationTemplate; final String returnTemplate; final String maintenanceTemplate; final String warrantyTemplate; EmailConfigurationSettings copyWith({ String? smtpHost, int? smtpPort, String? smtpUsername, String? smtpPassword, String? senderEmail, String? senderName, String? allocationTemplate, String? returnTemplate, String? maintenanceTemplate, String? warrantyTemplate, }) { return EmailConfigurationSettings( smtpHost: smtpHost ?? this.smtpHost, smtpPort: smtpPort ?? this.smtpPort, smtpUsername: smtpUsername ?? this.smtpUsername, smtpPassword: smtpPassword ?? this.smtpPassword, senderEmail: senderEmail ?? this.senderEmail, senderName: senderName ?? this.senderName, allocationTemplate: allocationTemplate ?? this.allocationTemplate, returnTemplate: returnTemplate ?? this.returnTemplate, maintenanceTemplate: maintenanceTemplate ?? this.maintenanceTemplate, warrantyTemplate: warrantyTemplate ?? this.warrantyTemplate, ); } Map toJson() => { 'smtpHost': smtpHost, 'smtpPort': smtpPort, 'smtpUsername': smtpUsername, 'smtpPassword': smtpPassword, 'senderEmail': senderEmail, 'senderName': senderName, 'allocationTemplate': allocationTemplate, 'returnTemplate': returnTemplate, 'maintenanceTemplate': maintenanceTemplate, 'warrantyTemplate': warrantyTemplate, }; /// Payload for `PUT /settings/email` ([EmailSettingsBody]). /// Omits blank password so an existing SMTP password is not cleared. Map toApiJson() { final payload = { 'smtp_host': smtpHost, 'smtp_port': smtpPort, 'smtp_username': smtpUsername, 'sender_email': senderEmail, 'sender_name': senderName, }; if (smtpPassword.isNotEmpty) { payload['smtp_password'] = smtpPassword; } return payload; } factory EmailConfigurationSettings.fromJson(Map json) => EmailConfigurationSettings.fromApiJson(json); factory EmailConfigurationSettings.fromApiJson(Map json) => EmailConfigurationSettings( smtpHost: json['smtp_host'] as String? ?? json['smtpHost'] as String? ?? '', smtpPort: (json['smtp_port'] as num?)?.toInt() ?? (json['smtpPort'] as num?)?.toInt() ?? 587, smtpUsername: json['smtp_username'] as String? ?? json['smtpUsername'] as String? ?? '', smtpPassword: json['smtp_password'] as String? ?? json['smtpPassword'] as String? ?? '', senderEmail: json['sender_email'] as String? ?? json['senderEmail'] as String? ?? '', senderName: json['sender_name'] as String? ?? json['senderName'] as String? ?? '', allocationTemplate: json['allocationTemplate'] as String? ?? 'Your asset {{asset_name}} has been allocated.', returnTemplate: json['returnTemplate'] as String? ?? 'Your asset {{asset_name}} has been returned.', maintenanceTemplate: json['maintenanceTemplate'] as String? ?? 'Maintenance request created for {{asset_name}}.', warrantyTemplate: json['warrantyTemplate'] as String? ?? 'Warranty for {{asset_name}} expires on {{date}}.', ); } class SecuritySettingsConfig { const SecuritySettingsConfig({ this.minPasswordLength = 8, this.passwordExpiryDays = 90, this.otpEnabled = true, this.mfaEnabled = false, this.sessionTimeoutMinutes = 30, this.autoLogout = true, this.concurrentLoginControl = false, this.loginAttemptLimit = 5, this.accountLockDurationMinutes = 15, this.auditLoggingEnabled = true, this.ipWhitelist = '', }); final int minPasswordLength; final int passwordExpiryDays; final bool otpEnabled; final bool mfaEnabled; final int sessionTimeoutMinutes; final bool autoLogout; final bool concurrentLoginControl; final int loginAttemptLimit; final int accountLockDurationMinutes; final bool auditLoggingEnabled; final String ipWhitelist; SecuritySettingsConfig copyWith({ int? minPasswordLength, int? passwordExpiryDays, bool? otpEnabled, bool? mfaEnabled, int? sessionTimeoutMinutes, bool? autoLogout, bool? concurrentLoginControl, int? loginAttemptLimit, int? accountLockDurationMinutes, bool? auditLoggingEnabled, String? ipWhitelist, }) { return SecuritySettingsConfig( minPasswordLength: minPasswordLength ?? this.minPasswordLength, passwordExpiryDays: passwordExpiryDays ?? this.passwordExpiryDays, otpEnabled: otpEnabled ?? this.otpEnabled, mfaEnabled: mfaEnabled ?? this.mfaEnabled, sessionTimeoutMinutes: sessionTimeoutMinutes ?? this.sessionTimeoutMinutes, autoLogout: autoLogout ?? this.autoLogout, concurrentLoginControl: concurrentLoginControl ?? this.concurrentLoginControl, loginAttemptLimit: loginAttemptLimit ?? this.loginAttemptLimit, accountLockDurationMinutes: accountLockDurationMinutes ?? this.accountLockDurationMinutes, auditLoggingEnabled: auditLoggingEnabled ?? this.auditLoggingEnabled, ipWhitelist: ipWhitelist ?? this.ipWhitelist, ); } Map toJson() => { 'minPasswordLength': minPasswordLength, 'passwordExpiryDays': passwordExpiryDays, 'otpEnabled': otpEnabled, 'mfaEnabled': mfaEnabled, 'sessionTimeoutMinutes': sessionTimeoutMinutes, 'autoLogout': autoLogout, 'concurrentLoginControl': concurrentLoginControl, 'loginAttemptLimit': loginAttemptLimit, 'accountLockDurationMinutes': accountLockDurationMinutes, 'auditLoggingEnabled': auditLoggingEnabled, 'ipWhitelist': ipWhitelist, }; factory SecuritySettingsConfig.fromJson(Map json) => SecuritySettingsConfig( minPasswordLength: json['minPasswordLength'] as int? ?? 8, passwordExpiryDays: json['passwordExpiryDays'] as int? ?? 90, otpEnabled: json['otpEnabled'] as bool? ?? true, mfaEnabled: json['mfaEnabled'] as bool? ?? false, sessionTimeoutMinutes: json['sessionTimeoutMinutes'] as int? ?? 30, autoLogout: json['autoLogout'] as bool? ?? true, concurrentLoginControl: json['concurrentLoginControl'] as bool? ?? false, loginAttemptLimit: json['loginAttemptLimit'] as int? ?? 5, accountLockDurationMinutes: json['accountLockDurationMinutes'] as int? ?? 15, auditLoggingEnabled: json['auditLoggingEnabled'] as bool? ?? true, ipWhitelist: json['ipWhitelist'] as String? ?? '', ); } class AppSettings { const AppSettings({ this.general = const GeneralSettings(), this.companyProfile = const CompanyProfileSettings(), this.uiPreferences = const UiPreferencesSettings(), this.asset = const AssetSettingsConfig(), this.notifications = const NotificationSettingsConfig(), this.email = const EmailConfigurationSettings(), this.security = const SecuritySettingsConfig(), }); final GeneralSettings general; final CompanyProfileSettings companyProfile; final UiPreferencesSettings uiPreferences; final AssetSettingsConfig asset; final NotificationSettingsConfig notifications; final EmailConfigurationSettings email; final SecuritySettingsConfig security; AppSettings copyWith({ GeneralSettings? general, CompanyProfileSettings? companyProfile, UiPreferencesSettings? uiPreferences, AssetSettingsConfig? asset, NotificationSettingsConfig? notifications, EmailConfigurationSettings? email, SecuritySettingsConfig? security, }) { return AppSettings( general: general ?? this.general, companyProfile: companyProfile ?? this.companyProfile, uiPreferences: uiPreferences ?? this.uiPreferences, asset: asset ?? this.asset, notifications: notifications ?? this.notifications, email: email ?? this.email, security: security ?? this.security, ); } Map toJson() => { 'general': general.toJson(), 'companyProfile': companyProfile.toJson(), 'uiPreferences': uiPreferences.toJson(), 'asset': asset.toJson(), 'notifications': notifications.toJson(), 'email': email.toJson(), 'security': security.toJson(), }; factory AppSettings.fromJson(Map json) => AppSettings( general: GeneralSettings.fromJson( json['general'] as Map? ?? {}, ), companyProfile: CompanyProfileSettings.fromJson( json['companyProfile'] as Map? ?? {}, ), uiPreferences: UiPreferencesSettings.fromJson( json['uiPreferences'] as Map? ?? {}, ), asset: AssetSettingsConfig.fromJson( json['asset'] as Map? ?? {}, ), notifications: NotificationSettingsConfig.fromJson( json['notifications'] as Map? ?? {}, ), email: EmailConfigurationSettings.fromJson( json['email'] as Map? ?? {}, ), security: SecuritySettingsConfig.fromJson( json['security'] as Map? ?? {}, ), ); } class SettingsSection { const SettingsSection({ required this.id, required this.title, required this.subtitle, required this.icon, required this.route, this.phase = 1, this.hidden = false, }); final String id; final String title; final String subtitle; final IconData icon; final String route; final int phase; /// When true, the card stays defined but is not shown on the Settings hub. final bool hidden; } const phase1SettingsSections = [ SettingsSection( id: 'general', title: 'General', subtitle: 'Regional, branch, and default preferences', icon: Icons.tune_outlined, route: '/settings/general', ), SettingsSection( id: 'company-profile', title: 'Company Profile', subtitle: 'Company information and branding assets', icon: Icons.business_outlined, route: '/settings/company-profile', ), SettingsSection( id: 'appearance', title: 'Appearance', subtitle: 'Theme, branding, and UI preferences', icon: Icons.palette_outlined, route: '/settings/appearance', ), SettingsSection( id: 'roles', title: 'Roles & Permissions', subtitle: 'Manage roles, permissions, and menu access', icon: Icons.security_outlined, route: '/users-roles?tab=permissions', ), SettingsSection( id: 'asset', title: 'Asset Settings', subtitle: 'Asset codes, statuses, warranty, and QR', icon: Icons.inventory_2_outlined, route: '/settings/asset', hidden: true, ), SettingsSection( id: 'notifications', title: 'Notifications', subtitle: 'Email, SMS, push, and in-app alerts', icon: Icons.notifications_outlined, route: '/settings/notifications', hidden: true, ), SettingsSection( id: 'email', title: 'Email Configuration', subtitle: 'SMTP server settings', icon: Icons.email_outlined, route: '/settings/email', ), SettingsSection( id: 'security', title: 'Security', subtitle: 'Authentication, session, and audit policies', icon: Icons.lock_outline, route: '/settings/security', hidden: true, ), ]; /// Phase 2 cards are kept for later; currently hidden on the Settings hub. const phase2SettingsSections = [ SettingsSection( id: 'workflow', title: 'Workflow Settings', subtitle: 'Approval workflows and approvers', icon: Icons.account_tree_outlined, route: '/settings/workflow', phase: 2, hidden: true, ), SettingsSection( id: 'dashboard', title: 'Dashboard', subtitle: 'KPI visibility and widget layout', icon: Icons.dashboard_outlined, route: '/settings/dashboard', phase: 2, hidden: true, ), SettingsSection( id: 'reports', title: 'Reports', subtitle: 'Export options and scheduled reports', icon: Icons.assessment_outlined, route: '/settings/reports', phase: 2, hidden: true, ), SettingsSection( id: 'storage', title: 'Storage', subtitle: 'File upload limits and storage provider', icon: Icons.cloud_upload_outlined, route: '/settings/storage', phase: 2, hidden: true, ), SettingsSection( id: 'audit', title: 'Audit Logs', subtitle: 'Log retention and tracked events', icon: Icons.history_outlined, route: '/settings/audit', phase: 2, hidden: true, ), SettingsSection( id: 'mobile', title: 'Mobile Settings', subtitle: 'Mobile login and sync preferences', icon: Icons.phone_android_outlined, route: '/settings/mobile', phase: 2, hidden: true, ), SettingsSection( id: 'integrations', title: 'Integrations', subtitle: 'SSO, SMS gateway, and cloud storage', icon: Icons.extension_outlined, route: '/settings/integrations', phase: 2, hidden: true, ), ]; /// Visible Phase 1 cards for the Settings hub. List get visiblePhase1SettingsSections => phase1SettingsSections.where((s) => !s.hidden).toList(); /// Visible Phase 2 cards for the Settings hub (empty while all are hidden). List get visiblePhase2SettingsSections => phase2SettingsSections.where((s) => !s.hidden).toList();