diff --git a/.dart_tool/chrome-device/Default/Preferences b/.dart_tool/chrome-device/Default/Preferences index c7ede24..6b19c03 100644 --- a/.dart_tool/chrome-device/Default/Preferences +++ b/.dart_tool/chrome-device/Default/Preferences @@ -1 +1 @@ -{"accessibility":{"captions":{"headless_caption_enabled":false,"live_caption_language":"en-US"}},"account_tracker_service_last_update":"13401367344816618","ack_existing_ntp_extensions":true,"alternate_error_pages":{"backup":true},"apps":{"shortcuts_arch":"arm64","shortcuts_version":7},"autocomplete":{"retention_policy_last_version":139},"autofill":{"last_version_deduped":139},"bookmark":{"storage_computation_last_update":"13401367344795807"},"commerce_daily_metrics_last_update_time":"13401367344818084","countryid_at_install":18766,"default_search_provider":{"guid":""},"devtools":{"last_open_timestamp":"13401367360809","preferences":{"closeable-tabs":"{\"security\":true,\"freestyler\":true,\"chrome-recorder\":true}","console.sidebar-selected-filter":"\"message\"","console.sidebar.width":"{\"vertical\":{\"size\":0,\"showMode\":\"OnlyMain\"}}","currentDockState":"\"right\"","elements.styles.sidebar.width":"{\"vertical\":{\"size\":0,\"showMode\":\"OnlyMain\"}}","inspector-view.split-view-state":"{\"vertical\":{\"size\":251}}","inspector.drawer-split-view-state":"{\"horizontal\":{\"size\":0,\"showMode\":\"OnlyMain\"}}","inspectorVersion":"39","network-panel-sidebar-state":"{\"vertical\":{\"size\":0,\"showMode\":\"OnlyMain\"}}","network-panel-split-view-state":"{\"vertical\":{\"size\":0}}","network-panel-split-view-waterfall":"{\"vertical\":{\"size\":0}}","panel-selected-tab":"\"network\"","releaseNoteVersionSeen":"80","request-info-form-data-category-expanded":"true","request-info-general-category-expanded":"true","request-info-query-string-category-expanded":"true","request-info-request-headers-category-expanded":"true","request-info-request-payload-category-expanded":"true","request-info-response-headers-category-expanded":"true","resource-view-tab":"\"preview\"","sources-panel-navigator-split-view-state":"{\"vertical\":{\"size\":0,\"showMode\":\"Both\"}}","sources-panel-split-view-state":"{\"vertical\":{\"size\":0,\"showMode\":\"Both\"},\"horizontal\":{\"size\":0,\"showMode\":\"Both\"}}","styles-pane-sidebar-tab-order":"{\"styles\":10,\"computed\":20}","undefined-tab-order":"{\"sources.scope-chain\":10,\"sources.watch\":20}"},"synced_preferences_sync_disabled":{"adorner-settings":"[{\"adorner\":\"grid\",\"isEnabled\":true},{\"adorner\":\"subgrid\",\"isEnabled\":true},{\"adorner\":\"flex\",\"isEnabled\":true},{\"adorner\":\"ad\",\"isEnabled\":true},{\"adorner\":\"scroll-snap\",\"isEnabled\":true},{\"adorner\":\"container\",\"isEnabled\":true},{\"adorner\":\"slot\",\"isEnabled\":true},{\"adorner\":\"top-layer\",\"isEnabled\":true},{\"adorner\":\"reveal\",\"isEnabled\":true},{\"adorner\":\"media\",\"isEnabled\":false},{\"adorner\":\"scroll\",\"isEnabled\":true}]","syncedInspectorVersion":"39"}},"domain_diversity":{"last_reporting_timestamp":"13401367344817366"},"enterprise_profile_guid":"212ecaad-045d-4283-900e-0ae939f88f60","extensions":{"alerts":{"initialized":true},"chrome_url_overrides":{},"last_chrome_version":"139.0.7258.155"},"gaia_cookie":{"changed_time":1756893745.401507,"hash":"2jmj7l5rSw0yVb/vlWAYkK/YBwk=","last_list_accounts_binary_data":"","periodic_report_time_2":"13401367344138154"},"gcm":{"product_category_for_subtypes":"com.chrome.macosx"},"google":{"services":{"signin_scoped_device_id":"ea39ada6-47d4-417f-9d5c-f1c502c5268c"}},"intl":{"selected_languages":"en-GB,en-US,en"},"invalidation":{"per_sender_topics_to_handler":{"1013309121859":{}}},"media":{"engagement":{"schema_version":5}},"media_router":{"receiver_id_hash_token":"TFmFcco4XreqNUwQY2eKeGWy+QpZYp+rDjxpa6uhxaX1bmZWNRwNV6qQgpMEdKzRDuYA+fKolXNjR+36ktsglg=="},"migrated_user_scripts_toggle":true,"ntp":{"num_personal_suggestions":2},"optimization_guide":{"hintsfetcher":{"hosts_successfully_fetched":{}},"predictionmodelfetcher":{"last_fetch_attempt":"13401367354136183","last_fetch_success":"13401367354239053"},"previously_registered_optimization_types":{"ABOUT_THIS_SITE":true,"DIGITAL_CREDENTIALS_LOW_FRICTION":true,"HISTORY_CLUSTERS":true,"LOADING_PREDICTOR":true,"MERCHANT_TRUST_SIGNALS_V2":true,"PRICE_TRACKING":true,"SAVED_TAB_GROUP":true,"V8_COMPILE_HINTS":true}},"password_manager":{"account_store_migrated_to_os_crypt_async":true,"autofillable_credentials_account_store_login_database":false,"autofillable_credentials_profile_store_login_database":false,"profile_store_migrated_to_os_crypt_async":true},"privacy_sandbox":{"first_party_sets_data_access_allowed_initialized":true},"profile":{"avatar_index":26,"background_password_check":{"check_fri_weight":9,"check_interval":"2592000000000","check_mon_weight":6,"check_sat_weight":6,"check_sun_weight":6,"check_thu_weight":9,"check_tue_weight":9,"check_wed_weight":9,"next_check_time":"13402488885020811"},"content_settings":{"exceptions":{"3pcd_heuristics_grants":{},"3pcd_support":{},"abusive_notification_permissions":{},"access_to_get_all_screens_media_in_session":{},"anti_abuse":{},"app_banner":{},"ar":{},"are_suspicious_notifications_allowlisted_by_user":{},"auto_picture_in_picture":{},"auto_select_certificate":{},"automatic_downloads":{},"automatic_fullscreen":{},"autoplay":{},"background_sync":{},"bluetooth_chooser_data":{},"bluetooth_guard":{},"bluetooth_scanning":{},"camera_pan_tilt_zoom":{},"captured_surface_control":{},"client_hints":{},"clipboard":{},"controlled_frame":{},"cookie_controls_metadata":{"http://localhost,*":{"last_modified":"13401367345278874","setting":{}}},"cookies":{},"direct_sockets":{},"direct_sockets_private_network_access":{},"display_media_system_audio":{},"disruptive_notification_permissions":{},"durable_storage":{},"fedcm_idp_registration":{},"fedcm_idp_signin":{"https://accounts.google.com:443,*":{"last_modified":"13401367345405520","setting":{"chosen-objects":[{"idp-origin":"https://accounts.google.com","idp-signin-status":false}]}}},"fedcm_share":{},"file_system_access_chooser_data":{},"file_system_access_extended_permission":{},"file_system_access_restore_permission":{},"file_system_last_picked_directory":{},"file_system_read_guard":{},"file_system_write_guard":{},"formfill_metadata":{},"geolocation":{},"hand_tracking":{},"hid_chooser_data":{},"hid_guard":{},"http_allowed":{},"https_enforced":{},"idle_detection":{},"images":{},"important_site_info":{},"initialized_translations":{},"intent_picker_auto_display":{},"javascript":{},"javascript_jit":{},"javascript_optimizer":{},"keyboard_lock":{},"legacy_cookie_access":{},"legacy_cookie_scope":{},"local_fonts":{},"local_network_access":{},"media_engagement":{"http://localhost:49345,*":{"expiration":"13409148364728473","last_modified":"13401372364728479","lifetime":"7776000000000","setting":{"hasHighScore":false,"lastMediaPlaybackTime":0.0,"mediaPlaybacks":0,"visits":1}}},"media_stream_camera":{},"media_stream_mic":{},"midi_sysex":{},"mixed_script":{},"nfc_devices":{},"notification_interactions":{},"notification_permission_review":{},"notifications":{},"ondevice_languages_downloaded":{},"password_protection":{},"payment_handler":{},"permission_autoblocking_data":{},"permission_autorevocation_data":{},"pointer_lock":{},"popups":{},"private_network_chooser_data":{},"private_network_guard":{},"protocol_handler":{},"reduced_accept_language":{},"safe_browsing_url_check_data":{},"sensors":{},"serial_chooser_data":{},"serial_guard":{},"site_engagement":{"http://localhost:49345,*":{"last_modified":"13401369690412672","setting":{"lastEngagementTime":1.3401369690412616e+16,"lastShortcutLaunchTime":0.0,"pointsAddedToday":8.999999999999998,"rawScore":8.999999999999998}}},"sound":{},"speaker_selection":{},"ssl_cert_decisions":{},"storage_access":{},"storage_access_header_origin_trial":{},"subresource_filter":{},"subresource_filter_data":{},"suspicious_notification_ids":{},"third_party_storage_partitioning":{},"top_level_3pcd_origin_trial":{},"top_level_3pcd_support":{},"top_level_storage_access":{},"tracking_protection":{},"unused_site_permissions":{},"usb_chooser_data":{},"usb_guard":{},"vr":{},"web_app_installation":{},"webid_api":{},"webid_auto_reauthn":{},"window_placement":{}},"pref_version":1},"created_by_version":"139.0.7258.155","creation_time":"13401367344056423","exit_type":"Normal","family_member_role":"not_in_family","isolated_web_app":{"install":{"pending_initialization_count":0}},"last_engagement_time":"13401369690412617","last_time_obsolete_http_credentials_removed":1756893804.14922,"last_time_password_store_metrics_reported":1756893774.14715,"managed":{"locally_parent_approved_extensions":{},"locally_parent_approved_extensions_migration_state":1},"managed_user_id":"","name":"Your Chrome","password_hash_data_list":[],"were_old_google_logins_removed":true},"safebrowsing":{"event_timestamps":{},"hash_real_time_ohttp_expiration_time":"13401626545307839","hash_real_time_ohttp_key":"2AAgETtEl5cjzDpksr9J2MiGvUsUaS87sxbZ6vEn9Mxqv18ABAABAAI=","metrics_last_log_time":"13401367344","scout_reporting_enabled_when_deprecated":false},"safety_hub":{"unused_site_permissions_revocation":{"migration_completed":true}},"saved_tab_groups":{"did_enable_shared_tab_groups_in_last_session":false,"specifics_to_data_migration":true},"segmentation_platform":{"client_result_prefs":"ClIKDXNob3BwaW5nX3VzZXISQQo2DQAAAAAQl4uY44+P5xcaJAocChoNAAAAPxIMU2hvcHBpbmdVc2VyGgVPdGhlchIEEAIYBCADELCLmOOPj+cX","device_switcher_util":{"result":{"labels":["NotSynced"]}},"last_db_compaction_time":"13401244799000000","uma_in_sql_start_time":"13401367344245586"},"sessions":{"event_log":[{"crashed":false,"time":"13401367344151448","type":0},{"did_schedule_command":true,"first_session_service":true,"tab_count":1,"time":"13401372364699597","type":2,"window_count":1}],"session_data_status":5},"settings":{"force_google_safesearch":false},"signin":{"allowed":true,"cookie_clear_on_exit_migration_notice_complete":true},"site_search_settings":{"overridden_keywords":[]},"spellcheck":{"dictionaries":["en-IN"]},"sync":{"data_type_status_for_sync_to_signin":{"app_list":false,"app_settings":false,"apps":false,"arc_package":false,"autofill":false,"autofill_profiles":false,"autofill_valuable":false,"autofill_wallet":false,"autofill_wallet_credential":false,"autofill_wallet_metadata":false,"autofill_wallet_offer":false,"autofill_wallet_usage":false,"bookmarks":false,"collaboration_group":false,"contact_info":false,"cookies":false,"device_info":false,"dictionary":false,"extension_settings":false,"extensions":false,"history":false,"history_delete_directives":false,"incoming_password_sharing_invitation":false,"managed_user_settings":false,"nigori":false,"os_preferences":false,"os_priority_preferences":false,"outgoing_password_sharing_invitation":false,"passwords":false,"plus_address":false,"plus_address_setting":false,"power_bookmark":false,"preferences":false,"printers":false,"printers_authorization_servers":false,"priority_preferences":false,"product_comparison":false,"reading_list":false,"saved_tab_group":false,"search_engines":false,"security_events":false,"send_tab_to_self":false,"sessions":false,"shared_tab_group_account_data":false,"shared_tab_group_data":false,"sharing_message":false,"themes":false,"user_consent":false,"user_events":false,"web_apps":false,"webapks":false,"webauthn_credential":false,"wifi_configurations":false,"workspace_desk":false},"encryption_bootstrap_token_per_account_migration_done":true,"feature_status_for_sync_to_signin":5,"passwords_per_account_pref_migration_done":true},"syncing_theme_prefs_migrated_to_non_syncing":true,"toolbar":{"pinned_cast_migration_complete":true,"pinned_chrome_labs_migration_complete":true},"total_passwords_available_for_account":0,"total_passwords_available_for_profile":0,"translate_site_blacklist":[],"translate_site_blocklist_with_time":{},"web_apps":{"did_migrate_default_chrome_apps":["MigrateDefaultChromeAppToWebAppsGSuite","MigrateDefaultChromeAppToWebAppsNonGSuite"],"last_preinstall_synchronize_version":"139"}} \ No newline at end of file +{"accessibility":{"captions":{"headless_caption_enabled":false,"live_caption_language":"en-US"}},"account_tracker_service_last_update":"13401367344816618","ack_existing_ntp_extensions":true,"alternate_error_pages":{"backup":true},"apps":{"shortcuts_arch":"arm64","shortcuts_version":7},"autocomplete":{"retention_policy_last_version":139},"autofill":{"last_version_deduped":139},"bookmark":{"storage_computation_last_update":"13401367344795807"},"commerce_daily_metrics_last_update_time":"13401367344818084","countryid_at_install":18766,"default_search_provider":{"guid":""},"devtools":{"last_open_timestamp":"13401373108832","preferences":{"closeable-tabs":"{\"security\":true,\"freestyler\":true,\"chrome-recorder\":true}","console.sidebar-selected-filter":"\"message\"","console.sidebar.width":"{\"vertical\":{\"size\":0,\"showMode\":\"OnlyMain\"}}","currentDockState":"\"right\"","elements.styles.sidebar.width":"{\"vertical\":{\"size\":0,\"showMode\":\"OnlyMain\"}}","emulation.device-mode-value":"{\"device\":\"\",\"orientation\":\"\",\"mode\":\"\"}","emulation.device-width":"461","emulation.show-device-mode":"true","inspector-view.split-view-state":"{\"vertical\":{\"size\":428.98046875}}","inspector.drawer-split-view-state":"{\"horizontal\":{\"size\":0,\"showMode\":\"OnlyMain\"}}","inspectorVersion":"39","network-panel-sidebar-state":"{\"vertical\":{\"size\":0,\"showMode\":\"OnlyMain\"}}","network-panel-split-view-state":"{\"vertical\":{\"size\":0}}","network-panel-split-view-waterfall":"{\"vertical\":{\"size\":0}}","panel-selected-tab":"\"resources\"","releaseNoteVersionSeen":"80","request-info-form-data-category-expanded":"true","request-info-general-category-expanded":"true","request-info-query-string-category-expanded":"true","request-info-request-headers-category-expanded":"true","request-info-request-payload-category-expanded":"true","request-info-response-headers-category-expanded":"true","resource-view-tab":"\"preview\"","resources-last-selected-element-path":"[\"storage://http://localhost:58997//local\",\"category://Local storage\"]","resources-local-storage-expanded":"true","sources-panel-navigator-split-view-state":"{\"vertical\":{\"size\":0,\"showMode\":\"Both\"}}","sources-panel-split-view-state":"{\"vertical\":{\"size\":0,\"showMode\":\"Both\"},\"horizontal\":{\"size\":0,\"showMode\":\"Both\"}}","standard-emulated-device-list":"[{\"title\":\"iPhone SE\",\"type\":\"phone\",\"user-agent\":\"Mozilla/5.0 (iPhone; CPU iPhone OS 16_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.6 Mobile/15E148 Safari/604.1\",\"capabilities\":[\"touch\",\"mobile\"],\"screen\":{\"device-pixel-ratio\":2,\"vertical\":{\"width\":375,\"height\":667},\"horizontal\":{\"width\":667,\"height\":375}},\"modes\":[{\"title\":\"default\",\"orientation\":\"vertical\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}},{\"title\":\"default\",\"orientation\":\"horizontal\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}}],\"show-by-default\":true,\"dual-screen\":false,\"foldable-screen\":false,\"show\":\"Default\"},{\"title\":\"iPhone XR\",\"type\":\"phone\",\"user-agent\":\"Mozilla/5.0 (iPhone; CPU iPhone OS 16_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.6 Mobile/15E148 Safari/604.1\",\"capabilities\":[\"touch\",\"mobile\"],\"screen\":{\"device-pixel-ratio\":2,\"vertical\":{\"width\":414,\"height\":896},\"horizontal\":{\"width\":896,\"height\":414}},\"modes\":[{\"title\":\"default\",\"orientation\":\"vertical\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}},{\"title\":\"default\",\"orientation\":\"horizontal\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}}],\"show-by-default\":true,\"dual-screen\":false,\"foldable-screen\":false,\"show\":\"Default\"},{\"title\":\"iPhone 12 Pro\",\"type\":\"phone\",\"user-agent\":\"Mozilla/5.0 (iPhone; CPU iPhone OS 16_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.6 Mobile/15E148 Safari/604.1\",\"capabilities\":[\"touch\",\"mobile\"],\"screen\":{\"device-pixel-ratio\":3,\"vertical\":{\"width\":390,\"height\":844},\"horizontal\":{\"width\":844,\"height\":390}},\"modes\":[{\"title\":\"default\",\"orientation\":\"vertical\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}},{\"title\":\"default\",\"orientation\":\"horizontal\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}}],\"show-by-default\":true,\"dual-screen\":false,\"foldable-screen\":false,\"show\":\"Default\"},{\"title\":\"iPhone 14 Pro Max\",\"type\":\"phone\",\"user-agent\":\"Mozilla/5.0 (iPhone; CPU iPhone OS 16_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.6 Mobile/15E148 Safari/604.1\",\"capabilities\":[\"touch\",\"mobile\"],\"screen\":{\"device-pixel-ratio\":3,\"vertical\":{\"width\":430,\"height\":932},\"horizontal\":{\"width\":932,\"height\":430}},\"modes\":[{\"title\":\"default\",\"orientation\":\"vertical\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}},{\"title\":\"default\",\"orientation\":\"horizontal\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}}],\"show-by-default\":true,\"dual-screen\":false,\"foldable-screen\":false,\"show\":\"Default\"},{\"title\":\"Pixel 3 XL\",\"type\":\"phone\",\"user-agent\":\"Mozilla/5.0 (Linux; Android 11; Pixel 3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Mobile Safari/537.36\",\"capabilities\":[\"touch\",\"mobile\"],\"screen\":{\"device-pixel-ratio\":2.75,\"vertical\":{\"width\":393,\"height\":786},\"horizontal\":{\"width\":786,\"height\":393}},\"modes\":[{\"title\":\"default\",\"orientation\":\"vertical\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}},{\"title\":\"default\",\"orientation\":\"horizontal\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}}],\"show-by-default\":false,\"dual-screen\":false,\"foldable-screen\":false,\"show\":\"Default\",\"user-agent-metadata\":{\"platform\":\"Android\",\"platformVersion\":\"11\",\"architecture\":\"\",\"model\":\"Pixel 3\",\"mobile\":true}},{\"title\":\"Pixel 7\",\"type\":\"phone\",\"user-agent\":\"Mozilla/5.0 (Linux; Android 13; Pixel 7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Mobile Safari/537.36\",\"capabilities\":[\"touch\",\"mobile\"],\"screen\":{\"device-pixel-ratio\":2.625,\"vertical\":{\"width\":412,\"height\":915},\"horizontal\":{\"width\":915,\"height\":412}},\"modes\":[{\"title\":\"default\",\"orientation\":\"vertical\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}},{\"title\":\"default\",\"orientation\":\"horizontal\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}}],\"show-by-default\":true,\"dual-screen\":false,\"foldable-screen\":false,\"show\":\"Default\",\"user-agent-metadata\":{\"platform\":\"Android\",\"platformVersion\":\"13\",\"architecture\":\"\",\"model\":\"Pixel 5\",\"mobile\":true}},{\"title\":\"Samsung Galaxy S8+\",\"type\":\"phone\",\"user-agent\":\"Mozilla/5.0 (Linux; Android 8.0.0; SM-G955U Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Mobile Safari/537.36\",\"capabilities\":[\"touch\",\"mobile\"],\"screen\":{\"device-pixel-ratio\":4,\"vertical\":{\"width\":360,\"height\":740},\"horizontal\":{\"width\":740,\"height\":360}},\"modes\":[{\"title\":\"default\",\"orientation\":\"vertical\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}},{\"title\":\"default\",\"orientation\":\"horizontal\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}}],\"show-by-default\":true,\"dual-screen\":false,\"foldable-screen\":false,\"show\":\"Default\",\"user-agent-metadata\":{\"platform\":\"Android\",\"platformVersion\":\"8.0.0\",\"architecture\":\"\",\"model\":\"SM-G955U\",\"mobile\":true}},{\"title\":\"Samsung Galaxy S20 Ultra\",\"type\":\"phone\",\"user-agent\":\"Mozilla/5.0 (Linux; Android 13; SM-G981B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Mobile Safari/537.36\",\"capabilities\":[\"touch\",\"mobile\"],\"screen\":{\"device-pixel-ratio\":3.5,\"vertical\":{\"width\":412,\"height\":915},\"horizontal\":{\"width\":915,\"height\":412}},\"modes\":[{\"title\":\"default\",\"orientation\":\"vertical\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}},{\"title\":\"default\",\"orientation\":\"horizontal\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}}],\"show-by-default\":true,\"dual-screen\":false,\"foldable-screen\":false,\"show\":\"Default\",\"user-agent-metadata\":{\"platform\":\"Android\",\"platformVersion\":\"13\",\"architecture\":\"\",\"model\":\"SM-G981B\",\"mobile\":true}},{\"title\":\"iPad Mini\",\"type\":\"tablet\",\"user-agent\":\"Mozilla/5.0 (iPad; CPU OS 16_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.6 Mobile/15E148 Safari/604.1\",\"capabilities\":[\"touch\",\"mobile\"],\"screen\":{\"device-pixel-ratio\":2,\"vertical\":{\"width\":768,\"height\":1024},\"horizontal\":{\"width\":1024,\"height\":768}},\"modes\":[{\"title\":\"default\",\"orientation\":\"vertical\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}},{\"title\":\"default\",\"orientation\":\"horizontal\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}}],\"show-by-default\":true,\"dual-screen\":false,\"foldable-screen\":false,\"show\":\"Default\"},{\"title\":\"iPad Air\",\"type\":\"tablet\",\"user-agent\":\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Safari/605.1.15\",\"capabilities\":[\"touch\"],\"screen\":{\"device-pixel-ratio\":2,\"vertical\":{\"width\":820,\"height\":1180},\"horizontal\":{\"width\":1180,\"height\":820}},\"modes\":[{\"title\":\"default\",\"orientation\":\"vertical\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}},{\"title\":\"default\",\"orientation\":\"horizontal\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}}],\"show-by-default\":true,\"dual-screen\":false,\"foldable-screen\":false,\"show\":\"Default\"},{\"title\":\"iPad Pro\",\"type\":\"tablet\",\"user-agent\":\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Safari/605.1.15\",\"capabilities\":[\"touch\"],\"screen\":{\"device-pixel-ratio\":2,\"vertical\":{\"width\":1024,\"height\":1366},\"horizontal\":{\"width\":1366,\"height\":1024}},\"modes\":[{\"title\":\"default\",\"orientation\":\"vertical\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}},{\"title\":\"default\",\"orientation\":\"horizontal\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}}],\"show-by-default\":true,\"dual-screen\":false,\"foldable-screen\":false,\"show\":\"Default\"},{\"title\":\"Surface Pro 7\",\"type\":\"tablet\",\"user-agent\":\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36\",\"capabilities\":[\"touch\",\"mobile\"],\"screen\":{\"device-pixel-ratio\":2,\"vertical\":{\"width\":912,\"height\":1368},\"horizontal\":{\"width\":1368,\"height\":912}},\"modes\":[{\"title\":\"default\",\"orientation\":\"vertical\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}},{\"title\":\"default\",\"orientation\":\"horizontal\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}}],\"show-by-default\":true,\"dual-screen\":false,\"foldable-screen\":false,\"show\":\"Default\"},{\"title\":\"Surface Duo\",\"type\":\"phone\",\"user-agent\":\"Mozilla/5.0 (Linux; Android 11.0; Surface Duo) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Mobile Safari/537.36\",\"capabilities\":[\"touch\",\"mobile\"],\"screen\":{\"device-pixel-ratio\":2.5,\"vertical\":{\"width\":540,\"height\":720},\"horizontal\":{\"width\":720,\"height\":540},\"vertical-spanned\":{\"width\":1114,\"height\":720,\"hinge\":{\"width\":34,\"height\":720,\"x\":540,\"y\":0,\"contentColor\":{\"r\":38,\"g\":38,\"b\":38,\"a\":1}}},\"horizontal-spanned\":{\"width\":720,\"height\":1114,\"hinge\":{\"width\":720,\"height\":34,\"x\":0,\"y\":540,\"contentColor\":{\"r\":38,\"g\":38,\"b\":38,\"a\":1}}}},\"modes\":[{\"title\":\"default\",\"orientation\":\"vertical\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}},{\"title\":\"default\",\"orientation\":\"horizontal\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}},{\"title\":\"spanned\",\"orientation\":\"vertical-spanned\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}},{\"title\":\"spanned\",\"orientation\":\"horizontal-spanned\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}}],\"show-by-default\":true,\"dual-screen\":true,\"foldable-screen\":false,\"show\":\"Default\",\"user-agent-metadata\":{\"platform\":\"Android\",\"platformVersion\":\"11.0\",\"architecture\":\"\",\"model\":\"Surface Duo\",\"mobile\":true}},{\"title\":\"Galaxy Z Fold 5\",\"type\":\"phone\",\"user-agent\":\"Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Mobile Safari/537.36\",\"capabilities\":[\"touch\",\"mobile\"],\"screen\":{\"device-pixel-ratio\":2.625,\"vertical\":{\"width\":344,\"height\":882},\"horizontal\":{\"width\":882,\"height\":344},\"vertical-spanned\":{\"width\":690,\"height\":829,\"hinge\":{\"width\":0,\"height\":829,\"x\":345,\"y\":0,\"contentColor\":{\"r\":38,\"g\":38,\"b\":38,\"a\":0.2},\"outlineColor\":{\"r\":38,\"g\":38,\"b\":38,\"a\":0.7}}},\"horizontal-spanned\":{\"width\":829,\"height\":690,\"hinge\":{\"width\":829,\"height\":0,\"x\":0,\"y\":345,\"contentColor\":{\"r\":38,\"g\":38,\"b\":38,\"a\":0.2},\"outlineColor\":{\"r\":38,\"g\":38,\"b\":38,\"a\":0.7}}}},\"modes\":[{\"title\":\"default\",\"orientation\":\"vertical\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}},{\"title\":\"default\",\"orientation\":\"horizontal\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}},{\"title\":\"spanned\",\"orientation\":\"vertical-spanned\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}},{\"title\":\"spanned\",\"orientation\":\"horizontal-spanned\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}}],\"show-by-default\":true,\"dual-screen\":false,\"foldable-screen\":true,\"show\":\"Default\",\"user-agent-metadata\":{\"platform\":\"Android\",\"platformVersion\":\"10.0\",\"architecture\":\"\",\"model\":\"SM-F946U\",\"mobile\":true}},{\"title\":\"Asus Zenbook Fold\",\"type\":\"tablet\",\"user-agent\":\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36\",\"capabilities\":[\"touch\"],\"screen\":{\"device-pixel-ratio\":1.5,\"vertical\":{\"width\":853,\"height\":1280},\"horizontal\":{\"width\":1280,\"height\":853},\"vertical-spanned\":{\"width\":1706,\"height\":1280,\"hinge\":{\"width\":107,\"height\":1280,\"x\":800,\"y\":0,\"contentColor\":{\"r\":38,\"g\":38,\"b\":38,\"a\":0.2},\"outlineColor\":{\"r\":38,\"g\":38,\"b\":38,\"a\":0.7}}},\"horizontal-spanned\":{\"width\":1280,\"height\":1706,\"hinge\":{\"width\":1706,\"height\":107,\"x\":0,\"y\":800,\"contentColor\":{\"r\":38,\"g\":38,\"b\":38,\"a\":0.2},\"outlineColor\":{\"r\":38,\"g\":38,\"b\":38,\"a\":0.7}}}},\"modes\":[{\"title\":\"default\",\"orientation\":\"vertical\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}},{\"title\":\"default\",\"orientation\":\"horizontal\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}},{\"title\":\"spanned\",\"orientation\":\"vertical-spanned\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}},{\"title\":\"spanned\",\"orientation\":\"horizontal-spanned\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}}],\"show-by-default\":true,\"dual-screen\":false,\"foldable-screen\":true,\"show\":\"Default\",\"user-agent-metadata\":{\"platform\":\"Windows\",\"platformVersion\":\"11.0\",\"architecture\":\"\",\"model\":\"UX9702AA\",\"mobile\":false}},{\"title\":\"Samsung Galaxy A51/71\",\"type\":\"phone\",\"user-agent\":\"Mozilla/5.0 (Linux; Android 8.0.0; SM-G955U Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Mobile Safari/537.36\",\"capabilities\":[\"touch\",\"mobile\"],\"screen\":{\"device-pixel-ratio\":2.625,\"vertical\":{\"width\":412,\"height\":914},\"horizontal\":{\"width\":914,\"height\":412}},\"modes\":[{\"title\":\"default\",\"orientation\":\"vertical\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}},{\"title\":\"default\",\"orientation\":\"horizontal\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}}],\"show-by-default\":true,\"dual-screen\":false,\"foldable-screen\":false,\"show\":\"Default\",\"user-agent-metadata\":{\"platform\":\"Android\",\"platformVersion\":\"8.0.0\",\"architecture\":\"\",\"model\":\"SM-G955U\",\"mobile\":true}},{\"title\":\"Nest Hub Max\",\"type\":\"tablet\",\"user-agent\":\"Mozilla/5.0 (X11; Linux aarch64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36 CrKey/1.54.250320\",\"capabilities\":[\"touch\",\"mobile\"],\"screen\":{\"device-pixel-ratio\":2,\"vertical\":{\"width\":1280,\"height\":800},\"horizontal\":{\"width\":1280,\"height\":800,\"outline\":{\"insets\":{\"left\":92,\"top\":96,\"right\":91,\"bottom\":248},\"image\":\"@url(optimized/google-nest-hub-max-horizontal.avif)\"}}},\"modes\":[{\"title\":\"default\",\"orientation\":\"horizontal\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}}],\"show-by-default\":true,\"dual-screen\":false,\"foldable-screen\":false,\"show\":\"Default\"},{\"title\":\"Nest Hub\",\"type\":\"tablet\",\"user-agent\":\"Mozilla/5.0 (Linux; Android) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36 CrKey/1.54.248666\",\"capabilities\":[\"touch\",\"mobile\"],\"screen\":{\"device-pixel-ratio\":2,\"vertical\":{\"width\":1024,\"height\":600},\"horizontal\":{\"width\":1024,\"height\":600,\"outline\":{\"insets\":{\"left\":82,\"top\":74,\"right\":83,\"bottom\":222},\"image\":\"@url(optimized/google-nest-hub-horizontal.avif)\"}}},\"modes\":[{\"title\":\"default\",\"orientation\":\"horizontal\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}}],\"show-by-default\":true,\"dual-screen\":false,\"foldable-screen\":false,\"show\":\"Default\",\"user-agent-metadata\":{\"platform\":\"Android\",\"platformVersion\":\"\",\"architecture\":\"\",\"model\":\"\",\"mobile\":false}},{\"title\":\"iPhone 4\",\"type\":\"phone\",\"user-agent\":\"Mozilla/5.0 (iPhone; CPU iPhone OS 7_1_2 like Mac OS X) AppleWebKit/537.51.2 (KHTML, like Gecko) Version/7.0 Mobile/11D257 Safari/9537.53\",\"capabilities\":[\"touch\",\"mobile\"],\"screen\":{\"device-pixel-ratio\":2,\"vertical\":{\"width\":320,\"height\":480},\"horizontal\":{\"width\":480,\"height\":320}},\"modes\":[{\"title\":\"default\",\"orientation\":\"vertical\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}},{\"title\":\"default\",\"orientation\":\"horizontal\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}}],\"show-by-default\":false,\"dual-screen\":false,\"foldable-screen\":false,\"show\":\"Default\"},{\"title\":\"iPhone 5/SE\",\"type\":\"phone\",\"user-agent\":\"Mozilla/5.0 (iPhone; CPU iPhone OS 10_3_1 like Mac OS X) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/10.0 Mobile/14E304 Safari/602.1\",\"capabilities\":[\"touch\",\"mobile\"],\"screen\":{\"device-pixel-ratio\":2,\"vertical\":{\"width\":320,\"height\":568,\"outline\":{\"insets\":{\"left\":29,\"top\":105,\"right\":25,\"bottom\":111},\"image\":\"@url(optimized/iPhone5-portrait.avif)\"}},\"horizontal\":{\"width\":568,\"height\":320,\"outline\":{\"insets\":{\"left\":115,\"top\":25,\"right\":115,\"bottom\":28},\"image\":\"@url(optimized/iPhone5-landscape.avif)\"}}},\"modes\":[{\"title\":\"default\",\"orientation\":\"vertical\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}},{\"title\":\"default\",\"orientation\":\"horizontal\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}}],\"show-by-default\":false,\"dual-screen\":false,\"foldable-screen\":false,\"show\":\"Default\"},{\"title\":\"iPhone 6/7/8\",\"type\":\"phone\",\"user-agent\":\"Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1\",\"capabilities\":[\"touch\",\"mobile\"],\"screen\":{\"device-pixel-ratio\":2,\"vertical\":{\"width\":375,\"height\":667,\"outline\":{\"insets\":{\"left\":28,\"top\":105,\"right\":28,\"bottom\":105},\"image\":\"@url(optimized/iPhone6-portrait.avif)\"}},\"horizontal\":{\"width\":667,\"height\":375,\"outline\":{\"insets\":{\"left\":106,\"top\":28,\"right\":106,\"bottom\":28},\"image\":\"@url(optimized/iPhone6-landscape.avif)\"}}},\"modes\":[{\"title\":\"default\",\"orientation\":\"vertical\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}},{\"title\":\"default\",\"orientation\":\"horizontal\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}}],\"show-by-default\":false,\"dual-screen\":false,\"foldable-screen\":false,\"show\":\"Default\"},{\"title\":\"iPhone 6/7/8 Plus\",\"type\":\"phone\",\"user-agent\":\"Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1\",\"capabilities\":[\"touch\",\"mobile\"],\"screen\":{\"device-pixel-ratio\":3,\"vertical\":{\"width\":414,\"height\":736,\"outline\":{\"insets\":{\"left\":26,\"top\":107,\"right\":30,\"bottom\":111},\"image\":\"@url(optimized/iPhone6Plus-portrait.avif)\"}},\"horizontal\":{\"width\":736,\"height\":414,\"outline\":{\"insets\":{\"left\":109,\"top\":29,\"right\":109,\"bottom\":27},\"image\":\"@url(optimized/iPhone6Plus-landscape.avif)\"}}},\"modes\":[{\"title\":\"default\",\"orientation\":\"vertical\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}},{\"title\":\"default\",\"orientation\":\"horizontal\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}}],\"show-by-default\":false,\"dual-screen\":false,\"foldable-screen\":false,\"show\":\"Default\"},{\"title\":\"iPhone X\",\"type\":\"phone\",\"user-agent\":\"Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1\",\"capabilities\":[\"touch\",\"mobile\"],\"screen\":{\"device-pixel-ratio\":3,\"vertical\":{\"width\":375,\"height\":812},\"horizontal\":{\"width\":812,\"height\":375}},\"modes\":[{\"title\":\"default\",\"orientation\":\"vertical\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}},{\"title\":\"default\",\"orientation\":\"horizontal\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}}],\"show-by-default\":false,\"dual-screen\":false,\"foldable-screen\":false,\"show\":\"Default\"},{\"title\":\"BlackBerry Z30\",\"type\":\"phone\",\"user-agent\":\"Mozilla/5.0 (BB10; Touch) AppleWebKit/537.10+ (KHTML, like Gecko) Version/10.0.9.2372 Mobile Safari/537.10+\",\"capabilities\":[\"touch\",\"mobile\"],\"screen\":{\"device-pixel-ratio\":2,\"vertical\":{\"width\":360,\"height\":640},\"horizontal\":{\"width\":640,\"height\":360}},\"modes\":[{\"title\":\"default\",\"orientation\":\"vertical\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}},{\"title\":\"default\",\"orientation\":\"horizontal\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}}],\"show-by-default\":false,\"dual-screen\":false,\"foldable-screen\":false,\"show\":\"Default\"},{\"title\":\"Nexus 4\",\"type\":\"phone\",\"user-agent\":\"Mozilla/5.0 (Linux; Android 4.4.2; Nexus 4 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Mobile Safari/537.36\",\"capabilities\":[\"touch\",\"mobile\"],\"screen\":{\"device-pixel-ratio\":2,\"vertical\":{\"width\":384,\"height\":640},\"horizontal\":{\"width\":640,\"height\":384}},\"modes\":[{\"title\":\"default\",\"orientation\":\"vertical\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}},{\"title\":\"default\",\"orientation\":\"horizontal\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}}],\"show-by-default\":false,\"dual-screen\":false,\"foldable-screen\":false,\"show\":\"Default\",\"user-agent-metadata\":{\"platform\":\"Android\",\"platformVersion\":\"4.4.2\",\"architecture\":\"\",\"model\":\"Nexus 4\",\"mobile\":true}},{\"title\":\"Nexus 5\",\"type\":\"phone\",\"user-agent\":\"Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Mobile Safari/537.36\",\"capabilities\":[\"touch\",\"mobile\"],\"screen\":{\"device-pixel-ratio\":3,\"vertical\":{\"width\":360,\"height\":640},\"horizontal\":{\"width\":640,\"height\":360}},\"modes\":[{\"title\":\"default\",\"orientation\":\"vertical\",\"insets\":{\"left\":0,\"top\":25,\"right\":0,\"bottom\":48},\"image\":\"@url(optimized/google-nexus-5-vertical-default-1x.avif) 1x, @url(optimized/google-nexus-5-vertical-default-2x.avif) 2x\"},{\"title\":\"navigation bar\",\"orientation\":\"vertical\",\"insets\":{\"left\":0,\"top\":80,\"right\":0,\"bottom\":48},\"image\":\"@url(optimized/google-nexus-5-vertical-navigation-1x.avif) 1x, @url(optimized/google-nexus-5-vertical-navigation-2x.avif) 2x\"},{\"title\":\"keyboard\",\"orientation\":\"vertical\",\"insets\":{\"left\":0,\"top\":80,\"right\":0,\"bottom\":312},\"image\":\"@url(optimized/google-nexus-5-vertical-keyboard-1x.avif) 1x, @url(optimized/google-nexus-5-vertical-keyboard-2x.avif) 2x\"},{\"title\":\"default\",\"orientation\":\"horizontal\",\"insets\":{\"left\":0,\"top\":25,\"right\":42,\"bottom\":0},\"image\":\"@url(optimized/google-nexus-5-horizontal-default-1x.avif) 1x, @url(optimized/google-nexus-5-horizontal-default-2x.avif) 2x\"},{\"title\":\"navigation bar\",\"orientation\":\"horizontal\",\"insets\":{\"left\":0,\"top\":80,\"right\":42,\"bottom\":0},\"image\":\"@url(optimized/google-nexus-5-horizontal-navigation-1x.avif) 1x, @url(optimized/google-nexus-5-horizontal-navigation-2x.avif) 2x\"},{\"title\":\"keyboard\",\"orientation\":\"horizontal\",\"insets\":{\"left\":0,\"top\":80,\"right\":42,\"bottom\":202},\"image\":\"@url(optimized/google-nexus-5-horizontal-keyboard-1x.avif) 1x, @url(optimized/google-nexus-5-horizontal-keyboard-2x.avif) 2x\"}],\"show-by-default\":false,\"dual-screen\":false,\"foldable-screen\":false,\"show\":\"Default\",\"user-agent-metadata\":{\"platform\":\"Android\",\"platformVersion\":\"6.0\",\"architecture\":\"\",\"model\":\"Nexus 5\",\"mobile\":true}},{\"title\":\"Nexus 5X\",\"type\":\"phone\",\"user-agent\":\"Mozilla/5.0 (Linux; Android 8.0.0; Nexus 5X Build/OPR4.170623.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Mobile Safari/537.36\",\"capabilities\":[\"touch\",\"mobile\"],\"screen\":{\"device-pixel-ratio\":2.625,\"vertical\":{\"width\":412,\"height\":732,\"outline\":{\"insets\":{\"left\":18,\"top\":88,\"right\":22,\"bottom\":98},\"image\":\"@url(optimized/Nexus5X-portrait.avif)\"}},\"horizontal\":{\"width\":732,\"height\":412,\"outline\":{\"insets\":{\"left\":88,\"top\":21,\"right\":98,\"bottom\":19},\"image\":\"@url(optimized/Nexus5X-landscape.avif)\"}}},\"modes\":[{\"title\":\"default\",\"orientation\":\"vertical\",\"insets\":{\"left\":0,\"top\":24,\"right\":0,\"bottom\":48},\"image\":\"@url(optimized/google-nexus-5x-vertical-default-1x.avif) 1x, @url(optimized/google-nexus-5x-vertical-default-2x.avif) 2x\"},{\"title\":\"navigation bar\",\"orientation\":\"vertical\",\"insets\":{\"left\":0,\"top\":80,\"right\":0,\"bottom\":48},\"image\":\"@url(optimized/google-nexus-5x-vertical-navigation-1x.avif) 1x, @url(optimized/google-nexus-5x-vertical-navigation-2x.avif) 2x\"},{\"title\":\"keyboard\",\"orientation\":\"vertical\",\"insets\":{\"left\":0,\"top\":80,\"right\":0,\"bottom\":342},\"image\":\"@url(optimized/google-nexus-5x-vertical-keyboard-1x.avif) 1x, @url(optimized/google-nexus-5x-vertical-keyboard-2x.avif) 2x\"},{\"title\":\"default\",\"orientation\":\"horizontal\",\"insets\":{\"left\":0,\"top\":24,\"right\":48,\"bottom\":0},\"image\":\"@url(optimized/google-nexus-5x-horizontal-default-1x.avif) 1x, @url(optimized/google-nexus-5x-horizontal-default-2x.avif) 2x\"},{\"title\":\"navigation bar\",\"orientation\":\"horizontal\",\"insets\":{\"left\":0,\"top\":80,\"right\":48,\"bottom\":0},\"image\":\"@url(optimized/google-nexus-5x-horizontal-navigation-1x.avif) 1x, @url(optimized/google-nexus-5x-horizontal-navigation-2x.avif) 2x\"},{\"title\":\"keyboard\",\"orientation\":\"horizontal\",\"insets\":{\"left\":0,\"top\":80,\"right\":48,\"bottom\":222},\"image\":\"@url(optimized/google-nexus-5x-horizontal-keyboard-1x.avif) 1x, @url(optimized/google-nexus-5x-horizontal-keyboard-2x.avif) 2x\"}],\"show-by-default\":false,\"dual-screen\":false,\"foldable-screen\":false,\"show\":\"Default\",\"user-agent-metadata\":{\"platform\":\"Android\",\"platformVersion\":\"8.0.0\",\"architecture\":\"\",\"model\":\"Nexus 5X\",\"mobile\":true}},{\"title\":\"Nexus 6\",\"type\":\"phone\",\"user-agent\":\"Mozilla/5.0 (Linux; Android 7.1.1; Nexus 6 Build/N6F26U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Mobile Safari/537.36\",\"capabilities\":[\"touch\",\"mobile\"],\"screen\":{\"device-pixel-ratio\":3.5,\"vertical\":{\"width\":412,\"height\":732},\"horizontal\":{\"width\":732,\"height\":412}},\"modes\":[{\"title\":\"default\",\"orientation\":\"vertical\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}},{\"title\":\"default\",\"orientation\":\"horizontal\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}}],\"show-by-default\":false,\"dual-screen\":false,\"foldable-screen\":false,\"show\":\"Default\",\"user-agent-metadata\":{\"platform\":\"Android\",\"platformVersion\":\"7.1.1\",\"architecture\":\"\",\"model\":\"Nexus 6\",\"mobile\":true}},{\"title\":\"Nexus 6P\",\"type\":\"phone\",\"user-agent\":\"Mozilla/5.0 (Linux; Android 8.0.0; Nexus 6P Build/OPP3.170518.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Mobile Safari/537.36\",\"capabilities\":[\"touch\",\"mobile\"],\"screen\":{\"device-pixel-ratio\":3.5,\"vertical\":{\"width\":412,\"height\":732,\"outline\":{\"insets\":{\"left\":16,\"top\":94,\"right\":16,\"bottom\":88},\"image\":\"@url(optimized/Nexus6P-portrait.avif)\"}},\"horizontal\":{\"width\":732,\"height\":412,\"outline\":{\"insets\":{\"left\":94,\"top\":17,\"right\":88,\"bottom\":17},\"image\":\"@url(optimized/Nexus6P-landscape.avif)\"}}},\"modes\":[{\"title\":\"default\",\"orientation\":\"vertical\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}},{\"title\":\"default\",\"orientation\":\"horizontal\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}}],\"show-by-default\":false,\"dual-screen\":false,\"foldable-screen\":false,\"show\":\"Default\",\"user-agent-metadata\":{\"platform\":\"Android\",\"platformVersion\":\"8.0.0\",\"architecture\":\"\",\"model\":\"Nexus 6P\",\"mobile\":true}},{\"title\":\"Pixel 2\",\"type\":\"phone\",\"user-agent\":\"Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Mobile Safari/537.36\",\"capabilities\":[\"touch\",\"mobile\"],\"screen\":{\"device-pixel-ratio\":2.625,\"vertical\":{\"width\":411,\"height\":731},\"horizontal\":{\"width\":731,\"height\":411}},\"modes\":[{\"title\":\"default\",\"orientation\":\"vertical\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}},{\"title\":\"default\",\"orientation\":\"horizontal\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}}],\"show-by-default\":false,\"dual-screen\":false,\"foldable-screen\":false,\"show\":\"Default\",\"user-agent-metadata\":{\"platform\":\"Android\",\"platformVersion\":\"8.0\",\"architecture\":\"\",\"model\":\"Pixel 2\",\"mobile\":true}},{\"title\":\"Pixel 2 XL\",\"type\":\"phone\",\"user-agent\":\"Mozilla/5.0 (Linux; Android 8.0.0; Pixel 2 XL Build/OPD1.170816.004) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Mobile Safari/537.36\",\"capabilities\":[\"touch\",\"mobile\"],\"screen\":{\"device-pixel-ratio\":3.5,\"vertical\":{\"width\":411,\"height\":823},\"horizontal\":{\"width\":823,\"height\":411}},\"modes\":[{\"title\":\"default\",\"orientation\":\"vertical\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}},{\"title\":\"default\",\"orientation\":\"horizontal\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}}],\"show-by-default\":false,\"dual-screen\":false,\"foldable-screen\":false,\"show\":\"Default\",\"user-agent-metadata\":{\"platform\":\"Android\",\"platformVersion\":\"8.0.0\",\"architecture\":\"\",\"model\":\"Pixel 2 XL\",\"mobile\":true}},{\"title\":\"Pixel 3\",\"type\":\"phone\",\"user-agent\":\"Mozilla/5.0 (Linux; Android 9; Pixel 3 Build/PQ1A.181105.017.A1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Mobile Safari/537.36\",\"capabilities\":[\"touch\",\"mobile\"],\"screen\":{\"device-pixel-ratio\":2.75,\"vertical\":{\"width\":393,\"height\":786},\"horizontal\":{\"width\":786,\"height\":393}},\"modes\":[{\"title\":\"default\",\"orientation\":\"vertical\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}},{\"title\":\"default\",\"orientation\":\"horizontal\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}}],\"show-by-default\":false,\"dual-screen\":false,\"foldable-screen\":false,\"show\":\"Default\",\"user-agent-metadata\":{\"platform\":\"Android\",\"platformVersion\":\"9\",\"architecture\":\"\",\"model\":\"Pixel 3\",\"mobile\":true}},{\"title\":\"Pixel 4\",\"type\":\"phone\",\"user-agent\":\"Mozilla/5.0 (Linux; Android 10; Pixel 4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Mobile Safari/537.36\",\"capabilities\":[\"touch\",\"mobile\"],\"screen\":{\"device-pixel-ratio\":3,\"vertical\":{\"width\":353,\"height\":745},\"horizontal\":{\"width\":745,\"height\":353}},\"modes\":[{\"title\":\"default\",\"orientation\":\"vertical\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}},{\"title\":\"default\",\"orientation\":\"horizontal\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}}],\"show-by-default\":false,\"dual-screen\":false,\"foldable-screen\":false,\"show\":\"Default\",\"user-agent-metadata\":{\"platform\":\"Android\",\"platformVersion\":\"10\",\"architecture\":\"\",\"model\":\"Pixel 4\",\"mobile\":true}},{\"title\":\"LG Optimus L70\",\"type\":\"phone\",\"user-agent\":\"Mozilla/5.0 (Linux; U; Android 4.4.2; en-us; LGMS323 Build/KOT49I.MS32310c) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/139.0.0.0 Mobile Safari/537.36\",\"capabilities\":[\"touch\",\"mobile\"],\"screen\":{\"device-pixel-ratio\":1.25,\"vertical\":{\"width\":384,\"height\":640},\"horizontal\":{\"width\":640,\"height\":384}},\"modes\":[{\"title\":\"default\",\"orientation\":\"vertical\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}},{\"title\":\"default\",\"orientation\":\"horizontal\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}}],\"show-by-default\":false,\"dual-screen\":false,\"foldable-screen\":false,\"show\":\"Default\",\"user-agent-metadata\":{\"platform\":\"Android\",\"platformVersion\":\"4.4.2\",\"architecture\":\"\",\"model\":\"LGMS323\",\"mobile\":true}},{\"title\":\"Nokia N9\",\"type\":\"phone\",\"user-agent\":\"Mozilla/5.0 (MeeGo; NokiaN9) AppleWebKit/534.13 (KHTML, like Gecko) NokiaBrowser/8.5.0 Mobile Safari/534.13\",\"capabilities\":[\"touch\",\"mobile\"],\"screen\":{\"device-pixel-ratio\":1,\"vertical\":{\"width\":480,\"height\":854},\"horizontal\":{\"width\":854,\"height\":480}},\"modes\":[{\"title\":\"default\",\"orientation\":\"vertical\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}},{\"title\":\"default\",\"orientation\":\"horizontal\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}}],\"show-by-default\":false,\"dual-screen\":false,\"foldable-screen\":false,\"show\":\"Default\"},{\"title\":\"Nokia Lumia 520\",\"type\":\"phone\",\"user-agent\":\"Mozilla/5.0 (compatible; MSIE 10.0; Windows Phone 8.0; Trident/6.0; IEMobile/10.0; ARM; Touch; NOKIA; Lumia 520)\",\"capabilities\":[\"touch\",\"mobile\"],\"screen\":{\"device-pixel-ratio\":1.5,\"vertical\":{\"width\":320,\"height\":533},\"horizontal\":{\"width\":533,\"height\":320}},\"modes\":[{\"title\":\"default\",\"orientation\":\"vertical\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}},{\"title\":\"default\",\"orientation\":\"horizontal\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}}],\"show-by-default\":false,\"dual-screen\":false,\"foldable-screen\":false,\"show\":\"Default\"},{\"title\":\"Microsoft Lumia 550\",\"type\":\"phone\",\"user-agent\":\"Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 550) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Mobile Safari/537.36 Edge/14.14263\",\"capabilities\":[\"touch\",\"mobile\"],\"screen\":{\"device-pixel-ratio\":2,\"vertical\":{\"width\":640,\"height\":360},\"horizontal\":{\"width\":640,\"height\":360}},\"modes\":[{\"title\":\"default\",\"orientation\":\"vertical\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}},{\"title\":\"default\",\"orientation\":\"horizontal\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}}],\"show-by-default\":false,\"dual-screen\":false,\"foldable-screen\":false,\"show\":\"Default\",\"user-agent-metadata\":{\"platform\":\"Android\",\"platformVersion\":\"4.2.1\",\"architecture\":\"\",\"model\":\"Lumia 550\",\"mobile\":true}},{\"title\":\"Microsoft Lumia 950\",\"type\":\"phone\",\"user-agent\":\"Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 950) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Mobile Safari/537.36 Edge/14.14263\",\"capabilities\":[\"touch\",\"mobile\"],\"screen\":{\"device-pixel-ratio\":4,\"vertical\":{\"width\":360,\"height\":640},\"horizontal\":{\"width\":640,\"height\":360}},\"modes\":[{\"title\":\"default\",\"orientation\":\"vertical\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}},{\"title\":\"default\",\"orientation\":\"horizontal\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}}],\"show-by-default\":false,\"dual-screen\":false,\"foldable-screen\":false,\"show\":\"Default\",\"user-agent-metadata\":{\"platform\":\"Android\",\"platformVersion\":\"4.2.1\",\"architecture\":\"\",\"model\":\"Lumia 950\",\"mobile\":true}},{\"title\":\"Galaxy S III\",\"type\":\"phone\",\"user-agent\":\"Mozilla/5.0 (Linux; U; Android 4.0; en-us; GT-I9300 Build/IMM76D) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30\",\"capabilities\":[\"touch\",\"mobile\"],\"screen\":{\"device-pixel-ratio\":2,\"vertical\":{\"width\":360,\"height\":640},\"horizontal\":{\"width\":640,\"height\":360}},\"modes\":[{\"title\":\"default\",\"orientation\":\"vertical\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}},{\"title\":\"default\",\"orientation\":\"horizontal\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}}],\"show-by-default\":false,\"dual-screen\":false,\"foldable-screen\":false,\"show\":\"Default\",\"user-agent-metadata\":{\"platform\":\"Android\",\"platformVersion\":\"4.0\",\"architecture\":\"\",\"model\":\"GT-I9300\",\"mobile\":true}},{\"title\":\"Galaxy S5\",\"type\":\"phone\",\"user-agent\":\"Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Mobile Safari/537.36\",\"capabilities\":[\"touch\",\"mobile\"],\"screen\":{\"device-pixel-ratio\":3,\"vertical\":{\"width\":360,\"height\":640},\"horizontal\":{\"width\":640,\"height\":360}},\"modes\":[{\"title\":\"default\",\"orientation\":\"vertical\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}},{\"title\":\"default\",\"orientation\":\"horizontal\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}}],\"show-by-default\":false,\"dual-screen\":false,\"foldable-screen\":false,\"show\":\"Default\",\"user-agent-metadata\":{\"platform\":\"Android\",\"platformVersion\":\"5.0\",\"architecture\":\"\",\"model\":\"SM-G900P\",\"mobile\":true}},{\"title\":\"Galaxy S8\",\"type\":\"phone\",\"user-agent\":\"Mozilla/5.0 (Linux; Android 7.0; SM-G950U Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Mobile Safari/537.36\",\"capabilities\":[\"touch\",\"mobile\"],\"screen\":{\"device-pixel-ratio\":3,\"vertical\":{\"width\":360,\"height\":740},\"horizontal\":{\"width\":740,\"height\":360}},\"modes\":[{\"title\":\"default\",\"orientation\":\"vertical\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}},{\"title\":\"default\",\"orientation\":\"horizontal\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}}],\"show-by-default\":false,\"dual-screen\":false,\"foldable-screen\":false,\"show\":\"Default\",\"user-agent-metadata\":{\"platform\":\"Android\",\"platformVersion\":\"7.0\",\"architecture\":\"\",\"model\":\"SM-G950U\",\"mobile\":true}},{\"title\":\"Galaxy S9+\",\"type\":\"phone\",\"user-agent\":\"Mozilla/5.0 (Linux; Android 8.0.0; SM-G965U Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Mobile Safari/537.36\",\"capabilities\":[\"touch\",\"mobile\"],\"screen\":{\"device-pixel-ratio\":4.5,\"vertical\":{\"width\":320,\"height\":658},\"horizontal\":{\"width\":658,\"height\":320}},\"modes\":[{\"title\":\"default\",\"orientation\":\"vertical\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}},{\"title\":\"default\",\"orientation\":\"horizontal\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}}],\"show-by-default\":false,\"dual-screen\":false,\"foldable-screen\":false,\"show\":\"Default\",\"user-agent-metadata\":{\"platform\":\"Android\",\"platformVersion\":\"8.0.0\",\"architecture\":\"\",\"model\":\"SM-G965U\",\"mobile\":true}},{\"title\":\"Galaxy Tab S4\",\"type\":\"phone\",\"user-agent\":\"Mozilla/5.0 (Linux; Android 8.1.0; SM-T837A) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36\",\"capabilities\":[\"touch\",\"mobile\"],\"screen\":{\"device-pixel-ratio\":2.25,\"vertical\":{\"width\":712,\"height\":1138},\"horizontal\":{\"width\":1138,\"height\":712}},\"modes\":[{\"title\":\"default\",\"orientation\":\"vertical\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}},{\"title\":\"default\",\"orientation\":\"horizontal\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}}],\"show-by-default\":false,\"dual-screen\":false,\"foldable-screen\":false,\"show\":\"Default\",\"user-agent-metadata\":{\"platform\":\"Android\",\"platformVersion\":\"8.1.0\",\"architecture\":\"\",\"model\":\"SM-T837A\",\"mobile\":false}},{\"title\":\"JioPhone 2\",\"type\":\"phone\",\"user-agent\":\"Mozilla/5.0 (Mobile; LYF/F300B/LYF-F300B-001-01-15-130718-i;Android; rv:48.0) Gecko/48.0 Firefox/48.0 KAIOS/2.5\",\"capabilities\":[\"touch\",\"mobile\"],\"screen\":{\"device-pixel-ratio\":1,\"vertical\":{\"width\":240,\"height\":320},\"horizontal\":{\"width\":320,\"height\":240}},\"modes\":[{\"title\":\"default\",\"orientation\":\"vertical\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}},{\"title\":\"default\",\"orientation\":\"horizontal\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}}],\"show-by-default\":false,\"dual-screen\":false,\"foldable-screen\":false,\"show\":\"Default\",\"user-agent-metadata\":{\"platform\":\"Android\",\"platformVersion\":\"\",\"architecture\":\"\",\"model\":\"LYF/F300B/LYF-F300B-001-01-15-130718-i\",\"mobile\":true}},{\"title\":\"Kindle Fire HDX\",\"type\":\"tablet\",\"user-agent\":\"Mozilla/5.0 (Linux; U; en-us; KFAPWI Build/JDQ39) AppleWebKit/535.19 (KHTML, like Gecko) Silk/3.13 Safari/535.19 Silk-Accelerated=true\",\"capabilities\":[\"touch\",\"mobile\"],\"screen\":{\"device-pixel-ratio\":2,\"vertical\":{\"width\":800,\"height\":1280},\"horizontal\":{\"width\":1280,\"height\":800}},\"modes\":[{\"title\":\"default\",\"orientation\":\"vertical\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}},{\"title\":\"default\",\"orientation\":\"horizontal\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}}],\"show-by-default\":false,\"dual-screen\":false,\"foldable-screen\":false,\"show\":\"Default\"},{\"title\":\"iPad\",\"type\":\"tablet\",\"user-agent\":\"Mozilla/5.0 (iPad; CPU OS 11_0 like Mac OS X) AppleWebKit/604.1.34 (KHTML, like Gecko) Version/11.0 Mobile/15A5341f Safari/604.1\",\"capabilities\":[\"touch\",\"mobile\"],\"screen\":{\"device-pixel-ratio\":2,\"vertical\":{\"width\":768,\"height\":1024,\"outline\":{\"insets\":{\"left\":52,\"top\":114,\"right\":55,\"bottom\":114},\"image\":\"@url(optimized/iPad-portrait.avif)\"}},\"horizontal\":{\"width\":1024,\"height\":768,\"outline\":{\"insets\":{\"left\":112,\"top\":56,\"right\":116,\"bottom\":52},\"image\":\"@url(optimized/iPad-landscape.avif)\"}}},\"modes\":[{\"title\":\"default\",\"orientation\":\"vertical\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}},{\"title\":\"default\",\"orientation\":\"horizontal\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}}],\"show-by-default\":false,\"dual-screen\":false,\"foldable-screen\":false,\"show\":\"Default\"},{\"title\":\"iPad Pro\",\"type\":\"tablet\",\"user-agent\":\"Mozilla/5.0 (iPad; CPU OS 11_0 like Mac OS X) AppleWebKit/604.1.34 (KHTML, like Gecko) Version/11.0 Mobile/15A5341f Safari/604.1\",\"capabilities\":[\"touch\",\"mobile\"],\"screen\":{\"device-pixel-ratio\":2,\"vertical\":{\"width\":1024,\"height\":1366},\"horizontal\":{\"width\":1366,\"height\":1024}},\"modes\":[{\"title\":\"default\",\"orientation\":\"vertical\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}},{\"title\":\"default\",\"orientation\":\"horizontal\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}}],\"show-by-default\":false,\"dual-screen\":false,\"foldable-screen\":false,\"show\":\"Default\"},{\"title\":\"Blackberry PlayBook\",\"type\":\"tablet\",\"user-agent\":\"Mozilla/5.0 (PlayBook; U; RIM Tablet OS 2.1.0; en-US) AppleWebKit/536.2+ (KHTML like Gecko) Version/7.2.1.0 Safari/536.2+\",\"capabilities\":[\"touch\",\"mobile\"],\"screen\":{\"device-pixel-ratio\":1,\"vertical\":{\"width\":600,\"height\":1024},\"horizontal\":{\"width\":1024,\"height\":600}},\"modes\":[{\"title\":\"default\",\"orientation\":\"vertical\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}},{\"title\":\"default\",\"orientation\":\"horizontal\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}}],\"show-by-default\":false,\"dual-screen\":false,\"foldable-screen\":false,\"show\":\"Default\"},{\"title\":\"Nexus 10\",\"type\":\"tablet\",\"user-agent\":\"Mozilla/5.0 (Linux; Android 6.0.1; Nexus 10 Build/MOB31T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36\",\"capabilities\":[\"touch\",\"mobile\"],\"screen\":{\"device-pixel-ratio\":2,\"vertical\":{\"width\":800,\"height\":1280},\"horizontal\":{\"width\":1280,\"height\":800}},\"modes\":[{\"title\":\"default\",\"orientation\":\"vertical\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}},{\"title\":\"default\",\"orientation\":\"horizontal\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}}],\"show-by-default\":false,\"dual-screen\":false,\"foldable-screen\":false,\"show\":\"Default\",\"user-agent-metadata\":{\"platform\":\"Android\",\"platformVersion\":\"6.0.1\",\"architecture\":\"\",\"model\":\"Nexus 10\",\"mobile\":false}},{\"title\":\"Nexus 7\",\"type\":\"tablet\",\"user-agent\":\"Mozilla/5.0 (Linux; Android 6.0.1; Nexus 7 Build/MOB30X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36\",\"capabilities\":[\"touch\",\"mobile\"],\"screen\":{\"device-pixel-ratio\":2,\"vertical\":{\"width\":600,\"height\":960},\"horizontal\":{\"width\":960,\"height\":600}},\"modes\":[{\"title\":\"default\",\"orientation\":\"vertical\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}},{\"title\":\"default\",\"orientation\":\"horizontal\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}}],\"show-by-default\":false,\"dual-screen\":false,\"foldable-screen\":false,\"show\":\"Default\",\"user-agent-metadata\":{\"platform\":\"Android\",\"platformVersion\":\"6.0.1\",\"architecture\":\"\",\"model\":\"Nexus 7\",\"mobile\":false}},{\"title\":\"Galaxy Note 3\",\"type\":\"phone\",\"user-agent\":\"Mozilla/5.0 (Linux; U; Android 4.3; en-us; SM-N900T Build/JSS15J) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30\",\"capabilities\":[\"touch\",\"mobile\"],\"screen\":{\"device-pixel-ratio\":3,\"vertical\":{\"width\":360,\"height\":640},\"horizontal\":{\"width\":640,\"height\":360}},\"modes\":[{\"title\":\"default\",\"orientation\":\"vertical\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}},{\"title\":\"default\",\"orientation\":\"horizontal\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}}],\"show-by-default\":false,\"dual-screen\":false,\"foldable-screen\":false,\"show\":\"Default\",\"user-agent-metadata\":{\"platform\":\"Android\",\"platformVersion\":\"4.3\",\"architecture\":\"\",\"model\":\"SM-N900T\",\"mobile\":true}},{\"title\":\"Galaxy Note II\",\"type\":\"phone\",\"user-agent\":\"Mozilla/5.0 (Linux; U; Android 4.1; en-us; GT-N7100 Build/JRO03C) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30\",\"capabilities\":[\"touch\",\"mobile\"],\"screen\":{\"device-pixel-ratio\":2,\"vertical\":{\"width\":360,\"height\":640},\"horizontal\":{\"width\":640,\"height\":360}},\"modes\":[{\"title\":\"default\",\"orientation\":\"vertical\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}},{\"title\":\"default\",\"orientation\":\"horizontal\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}}],\"show-by-default\":false,\"dual-screen\":false,\"foldable-screen\":false,\"show\":\"Default\",\"user-agent-metadata\":{\"platform\":\"Android\",\"platformVersion\":\"4.1\",\"architecture\":\"\",\"model\":\"GT-N7100\",\"mobile\":true}},{\"title\":\"Moto G4\",\"type\":\"phone\",\"user-agent\":\"Mozilla/5.0 (Linux; Android 6.0.1; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Mobile Safari/537.36\",\"capabilities\":[\"touch\",\"mobile\"],\"screen\":{\"device-pixel-ratio\":3,\"vertical\":{\"width\":360,\"height\":640,\"outline\":{\"insets\":{\"left\":30,\"top\":91,\"right\":30,\"bottom\":74},\"image\":\"@url(optimized/MotoG4-portrait.avif)\"}},\"horizontal\":{\"width\":640,\"height\":360,\"outline\":{\"insets\":{\"left\":91,\"top\":30,\"right\":74,\"bottom\":30},\"image\":\"@url(optimized/MotoG4-landscape.avif)\"}}},\"modes\":[{\"title\":\"default\",\"orientation\":\"vertical\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}},{\"title\":\"default\",\"orientation\":\"horizontal\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}}],\"show-by-default\":false,\"dual-screen\":false,\"foldable-screen\":false,\"show\":\"Default\",\"user-agent-metadata\":{\"platform\":\"Android\",\"platformVersion\":\"6.0.1\",\"architecture\":\"\",\"model\":\"Moto G (4)\",\"mobile\":true}},{\"title\":\"Moto G Power\",\"type\":\"phone\",\"user-agent\":\"Mozilla/5.0 (Linux; Android 11; moto g power (2022)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Mobile Safari/537.36\",\"capabilities\":[\"touch\",\"mobile\"],\"screen\":{\"device-pixel-ratio\":1.75,\"vertical\":{\"width\":412,\"height\":823},\"horizontal\":{\"width\":823,\"height\":412}},\"modes\":[{\"title\":\"default\",\"orientation\":\"vertical\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}},{\"title\":\"default\",\"orientation\":\"horizontal\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}}],\"show-by-default\":false,\"dual-screen\":false,\"foldable-screen\":false,\"show\":\"Default\",\"user-agent-metadata\":{\"platform\":\"Android\",\"platformVersion\":\"11\",\"architecture\":\"\",\"model\":\"moto g power (2022)\",\"mobile\":true}},{\"title\":\"Facebook on Android\",\"type\":\"phone\",\"user-agent\":\"Mozilla/5.0 (Linux; Android 12; Pixel 6 Build/SQ3A.220705.004; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/139.0.0.0 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/407.0.0.0.65;]\",\"capabilities\":[\"touch\",\"mobile\"],\"screen\":{\"device-pixel-ratio\":3.5,\"vertical\":{\"width\":412,\"height\":892},\"horizontal\":{\"width\":892,\"height\":412}},\"modes\":[{\"title\":\"default\",\"orientation\":\"vertical\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}},{\"title\":\"default\",\"orientation\":\"horizontal\",\"insets\":{\"left\":0,\"top\":0,\"right\":0,\"bottom\":0}}],\"show-by-default\":false,\"dual-screen\":false,\"foldable-screen\":false,\"show\":\"Default\",\"user-agent-metadata\":{\"platform\":\"Android\",\"platformVersion\":\"12\",\"architecture\":\"\",\"model\":\"Pixel 6\",\"mobile\":true}}]","styles-pane-sidebar-tab-order":"{\"styles\":10,\"computed\":20}","undefined-tab-order":"{\"sources.scope-chain\":10,\"sources.watch\":20}"},"synced_preferences_sync_disabled":{"adorner-settings":"[{\"adorner\":\"grid\",\"isEnabled\":true},{\"adorner\":\"subgrid\",\"isEnabled\":true},{\"adorner\":\"flex\",\"isEnabled\":true},{\"adorner\":\"ad\",\"isEnabled\":true},{\"adorner\":\"scroll-snap\",\"isEnabled\":true},{\"adorner\":\"container\",\"isEnabled\":true},{\"adorner\":\"slot\",\"isEnabled\":true},{\"adorner\":\"top-layer\",\"isEnabled\":true},{\"adorner\":\"reveal\",\"isEnabled\":true},{\"adorner\":\"media\",\"isEnabled\":false},{\"adorner\":\"scroll\",\"isEnabled\":true}]","syncedInspectorVersion":"39"}},"domain_diversity":{"last_reporting_timestamp":"13401367344817366"},"enterprise_profile_guid":"212ecaad-045d-4283-900e-0ae939f88f60","extensions":{"alerts":{"initialized":true},"chrome_url_overrides":{},"last_chrome_version":"139.0.7258.155"},"gaia_cookie":{"changed_time":1756893745.401507,"hash":"2jmj7l5rSw0yVb/vlWAYkK/YBwk=","last_list_accounts_binary_data":"","periodic_report_time_2":"13401367344138154"},"gcm":{"product_category_for_subtypes":"com.chrome.macosx"},"google":{"services":{"signin_scoped_device_id":"d9889ae8-7386-458b-9719-a49846524aeb"}},"history_clusters":{"all_cache":{"all_keywords":{},"all_timestamp":"13401372977226653"}},"intl":{"selected_languages":"en-GB,en-US,en"},"invalidation":{"per_sender_topics_to_handler":{"1013309121859":{}}},"media":{"engagement":{"schema_version":5}},"media_router":{"receiver_id_hash_token":"TFmFcco4XreqNUwQY2eKeGWy+QpZYp+rDjxpa6uhxaX1bmZWNRwNV6qQgpMEdKzRDuYA+fKolXNjR+36ktsglg=="},"migrated_user_scripts_toggle":true,"ntp":{"num_personal_suggestions":2},"optimization_guide":{"hintsfetcher":{"hosts_successfully_fetched":{}},"predictionmodelfetcher":{"last_fetch_attempt":"13401372404021617","last_fetch_success":"13401372404169802"},"previous_optimization_types_with_filter":{"AMERICAN_EXPRESS_CREDIT_CARD_FLIGHT_BENEFITS":true,"AMERICAN_EXPRESS_CREDIT_CARD_SUBSCRIPTION_BENEFITS":true,"AUTOFILL_ABLATION_SITES_LIST1":true,"AUTOFILL_ABLATION_SITES_LIST2":true,"AUTOFILL_ABLATION_SITES_LIST3":true,"AUTOFILL_ABLATION_SITES_LIST4":true,"AUTOFILL_ABLATION_SITES_LIST5":true,"AUTOFILL_PREDICTION_IMPROVEMENTS_ALLOWLIST":true,"BMO_CREDIT_CARD_AIR_MILES_PARTNER_BENEFITS":true,"BMO_CREDIT_CARD_ALCOHOL_STORE_BENEFITS":true,"BMO_CREDIT_CARD_DINING_BENEFITS":true,"BMO_CREDIT_CARD_DRUGSTORE_BENEFITS":true,"BMO_CREDIT_CARD_ENTERTAINMENT_BENEFITS":true,"BMO_CREDIT_CARD_GROCERY_BENEFITS":true,"BMO_CREDIT_CARD_OFFICE_SUPPLY_BENEFITS":true,"BMO_CREDIT_CARD_RECURRING_BILL_BENEFITS":true,"BMO_CREDIT_CARD_TRANSIT_BENEFITS":true,"BMO_CREDIT_CARD_TRAVEL_BENEFITS":true,"BMO_CREDIT_CARD_WHOLESALE_CLUB_BENEFITS":true,"BUY_NOW_PAY_LATER_ALLOWLIST_AFFIRM":true,"BUY_NOW_PAY_LATER_ALLOWLIST_KLARNA":true,"BUY_NOW_PAY_LATER_ALLOWLIST_ZIP":true,"CAPITAL_ONE_CREDIT_CARD_BENEFITS_BLOCKED":true,"CAPITAL_ONE_CREDIT_CARD_DINING_BENEFITS":true,"CAPITAL_ONE_CREDIT_CARD_ENTERTAINMENT_BENEFITS":true,"CAPITAL_ONE_CREDIT_CARD_GROCERY_BENEFITS":true,"CAPITAL_ONE_CREDIT_CARD_STREAMING_BENEFITS":true,"DIGITAL_CREDENTIALS_LOW_FRICTION":true,"EWALLET_MERCHANT_ALLOWLIST":true,"GLIC_ACTION_PAGE_BLOCK":true,"HISTORY_CLUSTERS":true,"HISTORY_EMBEDDINGS":true,"IBAN_AUTOFILL_BLOCKED":true,"PIX_MERCHANT_ORIGINS_ALLOWLIST":true,"PIX_PAYMENT_MERCHANT_ALLOWLIST":true,"SHARED_CREDIT_CARD_DINING_BENEFITS":true,"SHARED_CREDIT_CARD_ENTERTAINMENT_BENEFITS":true,"SHARED_CREDIT_CARD_FLAT_RATE_BENEFITS_BLOCKLIST":true,"SHARED_CREDIT_CARD_FLIGHT_BENEFITS":true,"SHARED_CREDIT_CARD_GROCERY_BENEFITS":true,"SHARED_CREDIT_CARD_STREAMING_BENEFITS":true,"SHARED_CREDIT_CARD_SUBSCRIPTION_BENEFITS":true,"SHOPPING_PAGE_PREDICTOR":true,"TEXT_CLASSIFIER_ENTITY_DETECTION":true,"VCN_MERCHANT_OPT_OUT_DISCOVER":true,"VCN_MERCHANT_OPT_OUT_MASTERCARD":true,"VCN_MERCHANT_OPT_OUT_VISA":true},"previously_registered_optimization_types":{"ABOUT_THIS_SITE":true,"DIGITAL_CREDENTIALS_LOW_FRICTION":true,"HISTORY_CLUSTERS":true,"LOADING_PREDICTOR":true,"MERCHANT_TRUST_SIGNALS_V2":true,"PRICE_TRACKING":true,"SAVED_TAB_GROUP":true,"V8_COMPILE_HINTS":true}},"password_manager":{"account_store_migrated_to_os_crypt_async":true,"autofillable_credentials_account_store_login_database":false,"autofillable_credentials_profile_store_login_database":false,"profile_store_migrated_to_os_crypt_async":true},"privacy_sandbox":{"first_party_sets_data_access_allowed_initialized":true},"profile":{"avatar_index":26,"background_password_check":{"check_fri_weight":9,"check_interval":"2592000000000","check_mon_weight":6,"check_sat_weight":6,"check_sun_weight":6,"check_thu_weight":9,"check_tue_weight":9,"check_wed_weight":9,"next_check_time":"13402488885020811"},"content_settings":{"exceptions":{"3pcd_heuristics_grants":{},"3pcd_support":{},"abusive_notification_permissions":{},"access_to_get_all_screens_media_in_session":{},"anti_abuse":{},"app_banner":{},"ar":{},"are_suspicious_notifications_allowlisted_by_user":{},"auto_picture_in_picture":{},"auto_select_certificate":{},"automatic_downloads":{},"automatic_fullscreen":{},"autoplay":{},"background_sync":{},"bluetooth_chooser_data":{},"bluetooth_guard":{},"bluetooth_scanning":{},"camera_pan_tilt_zoom":{},"captured_surface_control":{},"client_hints":{},"clipboard":{},"controlled_frame":{},"cookie_controls_metadata":{"http://localhost,*":{"last_modified":"13401373004667841","setting":{}}},"cookies":{},"direct_sockets":{},"direct_sockets_private_network_access":{},"display_media_system_audio":{},"disruptive_notification_permissions":{},"durable_storage":{},"fedcm_idp_registration":{},"fedcm_idp_signin":{"https://accounts.google.com:443,*":{"last_modified":"13401367345405520","setting":{"chosen-objects":[{"idp-origin":"https://accounts.google.com","idp-signin-status":false}]}}},"fedcm_share":{},"file_system_access_chooser_data":{},"file_system_access_extended_permission":{},"file_system_access_restore_permission":{},"file_system_last_picked_directory":{},"file_system_read_guard":{},"file_system_write_guard":{},"formfill_metadata":{},"geolocation":{},"hand_tracking":{},"hid_chooser_data":{},"hid_guard":{},"http_allowed":{},"https_enforced":{},"idle_detection":{},"images":{},"important_site_info":{},"initialized_translations":{},"intent_picker_auto_display":{},"javascript":{},"javascript_jit":{},"javascript_optimizer":{},"keyboard_lock":{},"legacy_cookie_access":{},"legacy_cookie_scope":{},"local_fonts":{},"local_network_access":{},"media_engagement":{"http://localhost:49345,*":{"expiration":"13409148364728473","last_modified":"13401372364728479","lifetime":"7776000000000","setting":{"hasHighScore":false,"lastMediaPlaybackTime":0.0,"mediaPlaybacks":0,"visits":1}},"http://localhost:58997,*":{"expiration":"13409154056403221","last_modified":"13401378056403224","lifetime":"7776000000000","setting":{"hasHighScore":false,"lastMediaPlaybackTime":0.0,"mediaPlaybacks":0,"visits":1}}},"media_stream_camera":{},"media_stream_mic":{},"midi_sysex":{},"mixed_script":{},"nfc_devices":{},"notification_interactions":{},"notification_permission_review":{},"notifications":{},"ondevice_languages_downloaded":{},"password_protection":{},"payment_handler":{},"permission_autoblocking_data":{},"permission_autorevocation_data":{},"pointer_lock":{},"popups":{},"private_network_chooser_data":{},"private_network_guard":{},"protocol_handler":{},"reduced_accept_language":{},"safe_browsing_url_check_data":{},"sensors":{},"serial_chooser_data":{},"serial_guard":{},"site_engagement":{"http://localhost:49345,*":{"last_modified":"13401369690412672","setting":{"lastEngagementTime":1.3401369690412616e+16,"lastShortcutLaunchTime":0.0,"pointsAddedToday":8.999999999999998,"rawScore":8.999999999999998}},"http://localhost:58997,*":{"last_modified":"13401377474039487","setting":{"lastEngagementTime":1.34013774740394e+16,"lastShortcutLaunchTime":0.0,"pointsAddedToday":15.0,"rawScore":15.0}}},"sound":{},"speaker_selection":{},"ssl_cert_decisions":{},"storage_access":{},"storage_access_header_origin_trial":{},"subresource_filter":{},"subresource_filter_data":{},"suspicious_notification_ids":{},"third_party_storage_partitioning":{},"top_level_3pcd_origin_trial":{},"top_level_3pcd_support":{},"top_level_storage_access":{},"tracking_protection":{},"unused_site_permissions":{},"usb_chooser_data":{},"usb_guard":{},"vr":{},"web_app_installation":{},"webid_api":{},"webid_auto_reauthn":{},"window_placement":{}},"pref_version":1},"created_by_version":"139.0.7258.155","creation_time":"13401367344056423","exit_type":"Normal","family_member_role":"not_in_family","isolated_web_app":{"install":{"pending_initialization_count":0}},"last_engagement_time":"13401377474039401","last_time_obsolete_http_credentials_removed":1756893804.14922,"last_time_password_store_metrics_reported":1756893774.14715,"managed":{"locally_parent_approved_extensions":{},"locally_parent_approved_extensions_migration_state":1},"managed_user_id":"","name":"Your Chrome","password_hash_data_list":[],"were_old_google_logins_removed":true},"safebrowsing":{"event_timestamps":{},"hash_real_time_ohttp_expiration_time":"13401626545307839","hash_real_time_ohttp_key":"2AAgETtEl5cjzDpksr9J2MiGvUsUaS87sxbZ6vEn9Mxqv18ABAABAAI=","metrics_last_log_time":"13401367344","scout_reporting_enabled_when_deprecated":false},"safety_hub":{"unused_site_permissions_revocation":{"migration_completed":true}},"saved_tab_groups":{"did_enable_shared_tab_groups_in_last_session":false,"specifics_to_data_migration":true},"segmentation_platform":{"client_result_prefs":"ClIKDXNob3BwaW5nX3VzZXISQQo2DQAAAAAQl4uY44+P5xcaJAocChoNAAAAPxIMU2hvcHBpbmdVc2VyGgVPdGhlchIEEAIYBCADELCLmOOPj+cX","device_switcher_util":{"result":{"labels":["NotSynced"]}},"last_db_compaction_time":"13401244799000000","uma_in_sql_start_time":"13401367344245586"},"sessions":{"event_log":[{"crashed":false,"time":"13401367344151448","type":0},{"did_schedule_command":true,"first_session_service":true,"tab_count":1,"time":"13401372364699597","type":2,"window_count":1},{"crashed":false,"time":"13401372394023713","type":0},{"did_schedule_command":true,"first_session_service":true,"tab_count":1,"time":"13401378056386094","type":2,"window_count":1}],"session_data_status":5},"settings":{"force_google_safesearch":false},"signin":{"allowed":true,"cookie_clear_on_exit_migration_notice_complete":true},"site_search_settings":{"overridden_keywords":[]},"spellcheck":{"dictionaries":["en-IN"]},"sync":{"data_type_status_for_sync_to_signin":{"app_list":false,"app_settings":false,"apps":false,"arc_package":false,"autofill":false,"autofill_profiles":false,"autofill_valuable":false,"autofill_wallet":false,"autofill_wallet_credential":false,"autofill_wallet_metadata":false,"autofill_wallet_offer":false,"autofill_wallet_usage":false,"bookmarks":false,"collaboration_group":false,"contact_info":false,"cookies":false,"device_info":false,"dictionary":false,"extension_settings":false,"extensions":false,"history":false,"history_delete_directives":false,"incoming_password_sharing_invitation":false,"managed_user_settings":false,"nigori":false,"os_preferences":false,"os_priority_preferences":false,"outgoing_password_sharing_invitation":false,"passwords":false,"plus_address":false,"plus_address_setting":false,"power_bookmark":false,"preferences":false,"printers":false,"printers_authorization_servers":false,"priority_preferences":false,"product_comparison":false,"reading_list":false,"saved_tab_group":false,"search_engines":false,"security_events":false,"send_tab_to_self":false,"sessions":false,"shared_tab_group_account_data":false,"shared_tab_group_data":false,"sharing_message":false,"themes":false,"user_consent":false,"user_events":false,"web_apps":false,"webapks":false,"webauthn_credential":false,"wifi_configurations":false,"workspace_desk":false},"encryption_bootstrap_token_per_account_migration_done":true,"feature_status_for_sync_to_signin":5,"passwords_per_account_pref_migration_done":true},"syncing_theme_prefs_migrated_to_non_syncing":true,"toolbar":{"pinned_cast_migration_complete":true,"pinned_chrome_labs_migration_complete":true},"total_passwords_available_for_account":0,"total_passwords_available_for_profile":0,"translate_site_blacklist":[],"translate_site_blocklist_with_time":{},"web_apps":{"did_migrate_default_chrome_apps":["MigrateDefaultChromeAppToWebAppsGSuite","MigrateDefaultChromeAppToWebAppsNonGSuite"],"last_preinstall_synchronize_version":"139"}} \ No newline at end of file diff --git a/.dart_tool/dartpad/web_plugin_registrant.dart b/.dart_tool/dartpad/web_plugin_registrant.dart index 8904f96..59f4c68 100644 --- a/.dart_tool/dartpad/web_plugin_registrant.dart +++ b/.dart_tool/dartpad/web_plugin_registrant.dart @@ -13,6 +13,7 @@ import 'package:firebase_core_web/firebase_core_web.dart'; import 'package:firebase_messaging_web/firebase_messaging_web.dart'; import 'package:flutter_secure_storage_web/flutter_secure_storage_web.dart'; import 'package:google_sign_in_web/google_sign_in_web.dart'; +import 'package:open_file_web/open_file_web.dart'; import 'package:package_info_plus/src/package_info_plus_web.dart'; import 'package:shared_preferences_web/shared_preferences_web.dart'; import 'package:url_launcher_web/url_launcher_web.dart'; @@ -27,6 +28,7 @@ void registerPlugins([final Registrar? pluginRegistrar]) { FirebaseMessagingWeb.registerWith(registrar); FlutterSecureStorageWeb.registerWith(registrar); GoogleSignInPlugin.registerWith(registrar); + OpenFilePlugin.registerWith(registrar); PackageInfoPlusWebPlugin.registerWith(registrar); SharedPreferencesPlugin.registerWith(registrar); UrlLauncherPlugin.registerWith(registrar); diff --git a/.dart_tool/package_config.json b/.dart_tool/package_config.json index 417f7c5..3bd50fe 100644 --- a/.dart_tool/package_config.json +++ b/.dart_tool/package_config.json @@ -25,6 +25,12 @@ "packageUri": "lib/", "languageVersion": "3.0" }, + { + "name": "archive", + "rootUri": "file:///Users/venbateam/.pub-cache/hosted/pub.dev/archive-3.6.1", + "packageUri": "lib/", + "languageVersion": "3.0" + }, { "name": "args", "rootUri": "file:///Users/venbateam/.pub-cache/hosted/pub.dev/args-2.7.0", @@ -97,6 +103,12 @@ "packageUri": "lib/", "languageVersion": "3.4" }, + { + "name": "charcode", + "rootUri": "file:///Users/venbateam/.pub-cache/hosted/pub.dev/charcode-1.4.0", + "packageUri": "lib/", + "languageVersion": "3.0" + }, { "name": "checked_yaml", "rootUri": "file:///Users/venbateam/.pub-cache/hosted/pub.dev/checked_yaml-2.0.4", @@ -145,6 +157,12 @@ "packageUri": "lib/", "languageVersion": "3.4" }, + { + "name": "csslib", + "rootUri": "file:///Users/venbateam/.pub-cache/hosted/pub.dev/csslib-1.0.2", + "packageUri": "lib/", + "languageVersion": "3.1" + }, { "name": "cupertino_icons", "rootUri": "file:///Users/venbateam/.pub-cache/hosted/pub.dev/cupertino_icons-1.0.8", @@ -163,6 +181,12 @@ "packageUri": "lib/", "languageVersion": "2.17" }, + { + "name": "dropdown_search", + "rootUri": "file:///Users/venbateam/.pub-cache/hosted/pub.dev/dropdown_search-6.0.2", + "packageUri": "lib/", + "languageVersion": "2.17" + }, { "name": "easy_loading_button", "rootUri": "file:///Users/venbateam/.pub-cache/hosted/pub.dev/easy_loading_button-0.4.0", @@ -175,6 +199,12 @@ "packageUri": "lib/", "languageVersion": "2.12" }, + { + "name": "excel", + "rootUri": "file:///Users/venbateam/.pub-cache/hosted/pub.dev/excel-4.0.6", + "packageUri": "lib/", + "languageVersion": "3.0" + }, { "name": "fake_async", "rootUri": "file:///Users/venbateam/.pub-cache/hosted/pub.dev/fake_async-1.3.3", @@ -427,6 +457,12 @@ "packageUri": "lib/", "languageVersion": "3.4" }, + { + "name": "html", + "rootUri": "file:///Users/venbateam/.pub-cache/hosted/pub.dev/html-0.15.6", + "packageUri": "lib/", + "languageVersion": "3.2" + }, { "name": "http", "rootUri": "file:///Users/venbateam/.pub-cache/hosted/pub.dev/http-1.5.0", @@ -571,12 +607,72 @@ "packageUri": "lib/", "languageVersion": "3.2" }, + { + "name": "month_picker_dialog", + "rootUri": "file:///Users/venbateam/.pub-cache/hosted/pub.dev/month_picker_dialog-6.5.0", + "packageUri": "lib/", + "languageVersion": "3.0" + }, + { + "name": "nested", + "rootUri": "file:///Users/venbateam/.pub-cache/hosted/pub.dev/nested-1.0.0", + "packageUri": "lib/", + "languageVersion": "2.12" + }, { "name": "nm", "rootUri": "file:///Users/venbateam/.pub-cache/hosted/pub.dev/nm-0.5.0", "packageUri": "lib/", "languageVersion": "2.12" }, + { + "name": "open_file", + "rootUri": "file:///Users/venbateam/.pub-cache/hosted/pub.dev/open_file-3.5.10", + "packageUri": "lib/", + "languageVersion": "2.17" + }, + { + "name": "open_file_android", + "rootUri": "file:///Users/venbateam/.pub-cache/hosted/pub.dev/open_file_android-1.0.6", + "packageUri": "lib/", + "languageVersion": "2.17" + }, + { + "name": "open_file_ios", + "rootUri": "file:///Users/venbateam/.pub-cache/hosted/pub.dev/open_file_ios-1.0.3", + "packageUri": "lib/", + "languageVersion": "2.17" + }, + { + "name": "open_file_linux", + "rootUri": "file:///Users/venbateam/.pub-cache/hosted/pub.dev/open_file_linux-0.0.5", + "packageUri": "lib/", + "languageVersion": "2.17" + }, + { + "name": "open_file_mac", + "rootUri": "file:///Users/venbateam/.pub-cache/hosted/pub.dev/open_file_mac-1.0.3", + "packageUri": "lib/", + "languageVersion": "2.17" + }, + { + "name": "open_file_platform_interface", + "rootUri": "file:///Users/venbateam/.pub-cache/hosted/pub.dev/open_file_platform_interface-1.0.3", + "packageUri": "lib/", + "languageVersion": "2.17" + }, + { + "name": "open_file_web", + "rootUri": "file:///Users/venbateam/.pub-cache/hosted/pub.dev/open_file_web-0.0.4", + "packageUri": "lib/", + "languageVersion": "2.17" + }, + { + "name": "open_file_windows", + "rootUri": "file:///Users/venbateam/.pub-cache/hosted/pub.dev/open_file_windows-0.0.3", + "packageUri": "lib/", + "languageVersion": "2.17" + }, { "name": "package_config", "rootUri": "file:///Users/venbateam/.pub-cache/hosted/pub.dev/package_config-2.2.0", @@ -673,6 +769,12 @@ "packageUri": "lib/", "languageVersion": "2.12" }, + { + "name": "provider", + "rootUri": "file:///Users/venbateam/.pub-cache/hosted/pub.dev/provider-6.1.5+1", + "packageUri": "lib/", + "languageVersion": "2.12" + }, { "name": "pub_semver", "rootUri": "file:///Users/venbateam/.pub-cache/hosted/pub.dev/pub_semver-2.2.0", @@ -841,6 +943,18 @@ "packageUri": "lib/", "languageVersion": "3.5" }, + { + "name": "universal_html", + "rootUri": "file:///Users/venbateam/.pub-cache/hosted/pub.dev/universal_html-2.2.4", + "packageUri": "lib/", + "languageVersion": "2.17" + }, + { + "name": "universal_io", + "rootUri": "file:///Users/venbateam/.pub-cache/hosted/pub.dev/universal_io-2.2.2", + "packageUri": "lib/", + "languageVersion": "2.17" + }, { "name": "universal_platform", "rootUri": "file:///Users/venbateam/.pub-cache/hosted/pub.dev/universal_platform-1.1.0", diff --git a/.dart_tool/package_graph.json b/.dart_tool/package_graph.json index 7bb9225..6c2c897 100644 --- a/.dart_tool/package_graph.json +++ b/.dart_tool/package_graph.json @@ -10,8 +10,10 @@ "animated_bottom_navigation_bar", "connectivity_plus", "cupertino_icons", + "dropdown_search", "easy_loading_button", "equatable", + "excel", "firebase_app_check", "firebase_auth", "firebase_core", @@ -31,11 +33,14 @@ "json_annotation", "jwt_decode", "local_auth", + "month_picker_dialog", + "open_file", "package_info_plus", "pinput", "responsive_framework", "shared_preferences", "toastification", + "universal_html", "url_launcher" ], "devDependencies": [ @@ -112,6 +117,54 @@ "vector_math" ] }, + { + "name": "excel", + "version": "4.0.6", + "dependencies": [ + "archive", + "collection", + "equatable", + "xml" + ] + }, + { + "name": "open_file", + "version": "3.5.10", + "dependencies": [ + "flutter", + "open_file_android", + "open_file_ios", + "open_file_linux", + "open_file_mac", + "open_file_platform_interface", + "open_file_web", + "open_file_windows" + ] + }, + { + "name": "month_picker_dialog", + "version": "6.5.0", + "dependencies": [ + "flutter", + "intl", + "provider" + ] + }, + { + "name": "universal_html", + "version": "2.2.4", + "dependencies": [ + "async", + "charcode", + "collection", + "csslib", + "html", + "meta", + "source_span", + "typed_data", + "universal_io" + ] + }, { "name": "easy_loading_button", "version": "0.4.0", @@ -776,6 +829,137 @@ "term_glyph" ] }, + { + "name": "xml", + "version": "6.5.0", + "dependencies": [ + "collection", + "meta", + "petitparser" + ] + }, + { + "name": "archive", + "version": "3.6.1", + "dependencies": [ + "crypto", + "path" + ] + }, + { + "name": "open_file_platform_interface", + "version": "1.0.3", + "dependencies": [ + "flutter", + "plugin_platform_interface" + ] + }, + { + "name": "open_file_linux", + "version": "0.0.5", + "dependencies": [ + "ffi", + "flutter", + "open_file_platform_interface" + ] + }, + { + "name": "open_file_windows", + "version": "0.0.3", + "dependencies": [ + "ffi", + "flutter", + "open_file_platform_interface" + ] + }, + { + "name": "open_file_mac", + "version": "1.0.3", + "dependencies": [ + "flutter", + "open_file_platform_interface" + ] + }, + { + "name": "open_file_ios", + "version": "1.0.3", + "dependencies": [ + "flutter", + "open_file_platform_interface" + ] + }, + { + "name": "open_file_web", + "version": "0.0.4", + "dependencies": [ + "flutter", + "flutter_web_plugins", + "open_file_platform_interface", + "web" + ] + }, + { + "name": "open_file_android", + "version": "1.0.6", + "dependencies": [ + "flutter", + "open_file_platform_interface" + ] + }, + { + "name": "provider", + "version": "6.1.5+1", + "dependencies": [ + "collection", + "flutter", + "nested" + ] + }, + { + "name": "universal_io", + "version": "2.2.2", + "dependencies": [ + "collection", + "meta", + "typed_data" + ] + }, + { + "name": "typed_data", + "version": "1.4.0", + "dependencies": [ + "collection" + ] + }, + { + "name": "source_span", + "version": "1.10.1", + "dependencies": [ + "collection", + "path", + "term_glyph" + ] + }, + { + "name": "html", + "version": "0.15.6", + "dependencies": [ + "csslib", + "source_span" + ] + }, + { + "name": "charcode", + "version": "1.4.0", + "dependencies": [] + }, + { + "name": "csslib", + "version": "1.0.2", + "dependencies": [ + "source_span" + ] + }, { "name": "lints", "version": "6.0.0", @@ -1255,15 +1439,6 @@ "string_scanner" ] }, - { - "name": "source_span", - "version": "1.10.1", - "dependencies": [ - "collection", - "path", - "term_glyph" - ] - }, { "name": "checked_yaml", "version": "2.0.4", @@ -1331,13 +1506,6 @@ "path" ] }, - { - "name": "typed_data", - "version": "1.4.0", - "dependencies": [ - "collection" - ] - }, { "name": "pool", "version": "1.5.1", @@ -1396,6 +1564,14 @@ "string_scanner" ] }, + { + "name": "petitparser", + "version": "6.1.0", + "dependencies": [ + "collection", + "meta" + ] + }, { "name": "plugin_platform_interface", "version": "2.1.8", @@ -1403,6 +1579,13 @@ "meta" ] }, + { + "name": "nested", + "version": "1.0.0", + "dependencies": [ + "flutter" + ] + }, { "name": "_flutterfire_internals", "version": "1.3.61", @@ -1521,20 +1704,10 @@ ] }, { - "name": "xml", - "version": "6.5.0", + "name": "dropdown_search", + "version": "6.0.2", "dependencies": [ - "collection", - "meta", - "petitparser" - ] - }, - { - "name": "petitparser", - "version": "6.1.0", - "dependencies": [ - "collection", - "meta" + "flutter" ] } ], diff --git a/.flutter-plugins-dependencies b/.flutter-plugins-dependencies index 6c72681..910f161 100644 --- a/.flutter-plugins-dependencies +++ b/.flutter-plugins-dependencies @@ -1 +1 @@ -{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"connectivity_plus","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/connectivity_plus-6.1.5/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"firebase_app_check","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/firebase_app_check-0.4.0+1/","native_build":true,"dependencies":["firebase_core"],"dev_dependency":false},{"name":"firebase_auth","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/firebase_auth-6.0.2/","native_build":true,"dependencies":["firebase_core"],"dev_dependency":false},{"name":"firebase_core","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/firebase_core-4.1.0/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"firebase_messaging","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/firebase_messaging-16.0.1/","native_build":true,"dependencies":["firebase_core"],"dev_dependency":false},{"name":"flutter_secure_storage","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/flutter_secure_storage-9.2.4/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"google_sign_in_ios","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/google_sign_in_ios-6.1.0/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false},{"name":"local_auth_darwin","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/local_auth_darwin-1.6.0/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false},{"name":"package_info_plus","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/package_info_plus-8.3.1/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_foundation","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/path_provider_foundation-2.4.1/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false},{"name":"shared_preferences_foundation","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/shared_preferences_foundation-2.5.4/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false},{"name":"url_launcher_ios","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/url_launcher_ios-6.3.4/","native_build":true,"dependencies":[],"dev_dependency":false}],"android":[{"name":"connectivity_plus","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/connectivity_plus-6.1.5/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"firebase_app_check","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/firebase_app_check-0.4.0+1/","native_build":true,"dependencies":["firebase_core"],"dev_dependency":false},{"name":"firebase_auth","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/firebase_auth-6.0.2/","native_build":true,"dependencies":["firebase_core"],"dev_dependency":false},{"name":"firebase_core","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/firebase_core-4.1.0/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"firebase_messaging","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/firebase_messaging-16.0.1/","native_build":true,"dependencies":["firebase_core"],"dev_dependency":false},{"name":"flutter_plugin_android_lifecycle","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/flutter_plugin_android_lifecycle-2.0.30/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"flutter_secure_storage","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/flutter_secure_storage-9.2.4/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"google_sign_in_android","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/google_sign_in_android-7.0.5/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"local_auth_android","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/local_auth_android-1.0.52/","native_build":true,"dependencies":["flutter_plugin_android_lifecycle"],"dev_dependency":false},{"name":"package_info_plus","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/package_info_plus-8.3.1/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_android","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/path_provider_android-2.2.17/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"shared_preferences_android","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/shared_preferences_android-2.4.11/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"url_launcher_android","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/url_launcher_android-6.3.18/","native_build":true,"dependencies":[],"dev_dependency":false}],"macos":[{"name":"connectivity_plus","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/connectivity_plus-6.1.5/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"firebase_app_check","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/firebase_app_check-0.4.0+1/","native_build":true,"dependencies":["firebase_core"],"dev_dependency":false},{"name":"firebase_auth","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/firebase_auth-6.0.2/","native_build":true,"dependencies":["firebase_core"],"dev_dependency":false},{"name":"firebase_core","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/firebase_core-4.1.0/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"firebase_messaging","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/firebase_messaging-16.0.1/","native_build":true,"dependencies":["firebase_core"],"dev_dependency":false},{"name":"flutter_secure_storage_macos","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/flutter_secure_storage_macos-3.1.3/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"google_sign_in_ios","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/google_sign_in_ios-6.1.0/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false},{"name":"local_auth_darwin","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/local_auth_darwin-1.6.0/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false},{"name":"package_info_plus","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/package_info_plus-8.3.1/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_foundation","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/path_provider_foundation-2.4.1/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false},{"name":"shared_preferences_foundation","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/shared_preferences_foundation-2.5.4/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false},{"name":"url_launcher_macos","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/url_launcher_macos-3.2.3/","native_build":true,"dependencies":[],"dev_dependency":false}],"linux":[{"name":"connectivity_plus","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/connectivity_plus-6.1.5/","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"flutter_secure_storage_linux","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/flutter_secure_storage_linux-1.2.3/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"package_info_plus","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/package_info_plus-8.3.1/","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"path_provider_linux","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/path_provider_linux-2.2.1/","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"shared_preferences_linux","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/shared_preferences_linux-2.4.1/","native_build":false,"dependencies":["path_provider_linux"],"dev_dependency":false},{"name":"url_launcher_linux","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/url_launcher_linux-3.2.1/","native_build":true,"dependencies":[],"dev_dependency":false}],"windows":[{"name":"connectivity_plus","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/connectivity_plus-6.1.5/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"firebase_auth","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/firebase_auth-6.0.2/","native_build":true,"dependencies":["firebase_core"],"dev_dependency":false},{"name":"firebase_core","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/firebase_core-4.1.0/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"flutter_secure_storage_windows","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/flutter_secure_storage_windows-3.1.2/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"local_auth_windows","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/local_auth_windows-1.0.11/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"package_info_plus","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/package_info_plus-8.3.1/","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"path_provider_windows","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/path_provider_windows-2.3.0/","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"shared_preferences_windows","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/shared_preferences_windows-2.4.1/","native_build":false,"dependencies":["path_provider_windows"],"dev_dependency":false},{"name":"url_launcher_windows","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/url_launcher_windows-3.1.4/","native_build":true,"dependencies":[],"dev_dependency":false}],"web":[{"name":"connectivity_plus","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/connectivity_plus-6.1.5/","dependencies":[],"dev_dependency":false},{"name":"firebase_app_check_web","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/firebase_app_check_web-0.2.0+16/","dependencies":["firebase_core_web"],"dev_dependency":false},{"name":"firebase_auth_web","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/firebase_auth_web-6.0.2/","dependencies":["firebase_core_web"],"dev_dependency":false},{"name":"firebase_core_web","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/firebase_core_web-3.1.0/","dependencies":[],"dev_dependency":false},{"name":"firebase_messaging_web","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/firebase_messaging_web-4.0.1/","dependencies":["firebase_core_web"],"dev_dependency":false},{"name":"flutter_secure_storage_web","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/flutter_secure_storage_web-1.2.1/","dependencies":[],"dev_dependency":false},{"name":"google_sign_in_web","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/google_sign_in_web-1.0.0/","dependencies":[],"dev_dependency":false},{"name":"package_info_plus","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/package_info_plus-8.3.1/","dependencies":[],"dev_dependency":false},{"name":"shared_preferences_web","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/shared_preferences_web-2.4.3/","dependencies":[],"dev_dependency":false},{"name":"url_launcher_web","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/url_launcher_web-2.4.1/","dependencies":[],"dev_dependency":false}]},"dependencyGraph":[{"name":"connectivity_plus","dependencies":[]},{"name":"firebase_app_check","dependencies":["firebase_app_check_web","firebase_core"]},{"name":"firebase_app_check_web","dependencies":["firebase_core","firebase_core_web"]},{"name":"firebase_auth","dependencies":["firebase_auth_web","firebase_core"]},{"name":"firebase_auth_web","dependencies":["firebase_core","firebase_core_web"]},{"name":"firebase_core","dependencies":["firebase_core_web"]},{"name":"firebase_core_web","dependencies":[]},{"name":"firebase_messaging","dependencies":["firebase_core","firebase_messaging_web"]},{"name":"firebase_messaging_web","dependencies":["firebase_core","firebase_core_web"]},{"name":"flutter_plugin_android_lifecycle","dependencies":[]},{"name":"flutter_secure_storage","dependencies":["flutter_secure_storage_linux","flutter_secure_storage_macos","flutter_secure_storage_web","flutter_secure_storage_windows"]},{"name":"flutter_secure_storage_linux","dependencies":[]},{"name":"flutter_secure_storage_macos","dependencies":[]},{"name":"flutter_secure_storage_web","dependencies":[]},{"name":"flutter_secure_storage_windows","dependencies":["path_provider"]},{"name":"google_sign_in","dependencies":["google_sign_in_android","google_sign_in_ios","google_sign_in_web"]},{"name":"google_sign_in_android","dependencies":[]},{"name":"google_sign_in_ios","dependencies":[]},{"name":"google_sign_in_web","dependencies":[]},{"name":"local_auth","dependencies":["local_auth_android","local_auth_darwin","local_auth_windows"]},{"name":"local_auth_android","dependencies":["flutter_plugin_android_lifecycle"]},{"name":"local_auth_darwin","dependencies":[]},{"name":"local_auth_windows","dependencies":[]},{"name":"package_info_plus","dependencies":[]},{"name":"path_provider","dependencies":["path_provider_android","path_provider_foundation","path_provider_linux","path_provider_windows"]},{"name":"path_provider_android","dependencies":[]},{"name":"path_provider_foundation","dependencies":[]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_windows","dependencies":[]},{"name":"shared_preferences","dependencies":["shared_preferences_android","shared_preferences_foundation","shared_preferences_linux","shared_preferences_web","shared_preferences_windows"]},{"name":"shared_preferences_android","dependencies":[]},{"name":"shared_preferences_foundation","dependencies":[]},{"name":"shared_preferences_linux","dependencies":["path_provider_linux"]},{"name":"shared_preferences_web","dependencies":[]},{"name":"shared_preferences_windows","dependencies":["path_provider_windows"]},{"name":"url_launcher","dependencies":["url_launcher_android","url_launcher_ios","url_launcher_linux","url_launcher_macos","url_launcher_web","url_launcher_windows"]},{"name":"url_launcher_android","dependencies":[]},{"name":"url_launcher_ios","dependencies":[]},{"name":"url_launcher_linux","dependencies":[]},{"name":"url_launcher_macos","dependencies":[]},{"name":"url_launcher_web","dependencies":[]},{"name":"url_launcher_windows","dependencies":[]}],"date_created":"2025-09-03 15:15:12.642086","version":"3.35.2","swift_package_manager_enabled":{"ios":false,"macos":false}} \ No newline at end of file +{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"connectivity_plus","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/connectivity_plus-6.1.5/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"firebase_app_check","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/firebase_app_check-0.4.0+1/","native_build":true,"dependencies":["firebase_core"],"dev_dependency":false},{"name":"firebase_auth","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/firebase_auth-6.0.2/","native_build":true,"dependencies":["firebase_core"],"dev_dependency":false},{"name":"firebase_core","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/firebase_core-4.1.0/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"firebase_messaging","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/firebase_messaging-16.0.1/","native_build":true,"dependencies":["firebase_core"],"dev_dependency":false},{"name":"flutter_secure_storage","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/flutter_secure_storage-9.2.4/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"google_sign_in_ios","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/google_sign_in_ios-6.1.0/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false},{"name":"local_auth_darwin","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/local_auth_darwin-1.6.0/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false},{"name":"open_file_ios","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/open_file_ios-1.0.3/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"package_info_plus","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/package_info_plus-8.3.1/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_foundation","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/path_provider_foundation-2.4.1/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false},{"name":"shared_preferences_foundation","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/shared_preferences_foundation-2.5.4/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false},{"name":"url_launcher_ios","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/url_launcher_ios-6.3.4/","native_build":true,"dependencies":[],"dev_dependency":false}],"android":[{"name":"connectivity_plus","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/connectivity_plus-6.1.5/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"firebase_app_check","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/firebase_app_check-0.4.0+1/","native_build":true,"dependencies":["firebase_core"],"dev_dependency":false},{"name":"firebase_auth","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/firebase_auth-6.0.2/","native_build":true,"dependencies":["firebase_core"],"dev_dependency":false},{"name":"firebase_core","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/firebase_core-4.1.0/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"firebase_messaging","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/firebase_messaging-16.0.1/","native_build":true,"dependencies":["firebase_core"],"dev_dependency":false},{"name":"flutter_plugin_android_lifecycle","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/flutter_plugin_android_lifecycle-2.0.30/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"flutter_secure_storage","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/flutter_secure_storage-9.2.4/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"google_sign_in_android","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/google_sign_in_android-7.0.5/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"local_auth_android","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/local_auth_android-1.0.52/","native_build":true,"dependencies":["flutter_plugin_android_lifecycle"],"dev_dependency":false},{"name":"open_file_android","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/open_file_android-1.0.6/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"package_info_plus","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/package_info_plus-8.3.1/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_android","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/path_provider_android-2.2.17/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"shared_preferences_android","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/shared_preferences_android-2.4.11/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"url_launcher_android","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/url_launcher_android-6.3.18/","native_build":true,"dependencies":[],"dev_dependency":false}],"macos":[{"name":"connectivity_plus","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/connectivity_plus-6.1.5/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"firebase_app_check","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/firebase_app_check-0.4.0+1/","native_build":true,"dependencies":["firebase_core"],"dev_dependency":false},{"name":"firebase_auth","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/firebase_auth-6.0.2/","native_build":true,"dependencies":["firebase_core"],"dev_dependency":false},{"name":"firebase_core","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/firebase_core-4.1.0/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"firebase_messaging","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/firebase_messaging-16.0.1/","native_build":true,"dependencies":["firebase_core"],"dev_dependency":false},{"name":"flutter_secure_storage_macos","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/flutter_secure_storage_macos-3.1.3/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"google_sign_in_ios","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/google_sign_in_ios-6.1.0/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false},{"name":"local_auth_darwin","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/local_auth_darwin-1.6.0/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false},{"name":"open_file_mac","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/open_file_mac-1.0.3/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"package_info_plus","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/package_info_plus-8.3.1/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_foundation","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/path_provider_foundation-2.4.1/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false},{"name":"shared_preferences_foundation","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/shared_preferences_foundation-2.5.4/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false},{"name":"url_launcher_macos","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/url_launcher_macos-3.2.3/","native_build":true,"dependencies":[],"dev_dependency":false}],"linux":[{"name":"connectivity_plus","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/connectivity_plus-6.1.5/","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"flutter_secure_storage_linux","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/flutter_secure_storage_linux-1.2.3/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"open_file_linux","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/open_file_linux-0.0.5/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"package_info_plus","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/package_info_plus-8.3.1/","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"path_provider_linux","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/path_provider_linux-2.2.1/","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"shared_preferences_linux","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/shared_preferences_linux-2.4.1/","native_build":false,"dependencies":["path_provider_linux"],"dev_dependency":false},{"name":"url_launcher_linux","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/url_launcher_linux-3.2.1/","native_build":true,"dependencies":[],"dev_dependency":false}],"windows":[{"name":"connectivity_plus","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/connectivity_plus-6.1.5/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"firebase_auth","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/firebase_auth-6.0.2/","native_build":true,"dependencies":["firebase_core"],"dev_dependency":false},{"name":"firebase_core","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/firebase_core-4.1.0/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"flutter_secure_storage_windows","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/flutter_secure_storage_windows-3.1.2/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"local_auth_windows","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/local_auth_windows-1.0.11/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"open_file_windows","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/open_file_windows-0.0.3/","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"package_info_plus","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/package_info_plus-8.3.1/","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"path_provider_windows","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/path_provider_windows-2.3.0/","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"shared_preferences_windows","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/shared_preferences_windows-2.4.1/","native_build":false,"dependencies":["path_provider_windows"],"dev_dependency":false},{"name":"url_launcher_windows","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/url_launcher_windows-3.1.4/","native_build":true,"dependencies":[],"dev_dependency":false}],"web":[{"name":"connectivity_plus","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/connectivity_plus-6.1.5/","dependencies":[],"dev_dependency":false},{"name":"firebase_app_check_web","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/firebase_app_check_web-0.2.0+16/","dependencies":["firebase_core_web"],"dev_dependency":false},{"name":"firebase_auth_web","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/firebase_auth_web-6.0.2/","dependencies":["firebase_core_web"],"dev_dependency":false},{"name":"firebase_core_web","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/firebase_core_web-3.1.0/","dependencies":[],"dev_dependency":false},{"name":"firebase_messaging_web","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/firebase_messaging_web-4.0.1/","dependencies":["firebase_core_web"],"dev_dependency":false},{"name":"flutter_secure_storage_web","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/flutter_secure_storage_web-1.2.1/","dependencies":[],"dev_dependency":false},{"name":"google_sign_in_web","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/google_sign_in_web-1.0.0/","dependencies":[],"dev_dependency":false},{"name":"open_file_web","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/open_file_web-0.0.4/","dependencies":[],"dev_dependency":false},{"name":"package_info_plus","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/package_info_plus-8.3.1/","dependencies":[],"dev_dependency":false},{"name":"shared_preferences_web","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/shared_preferences_web-2.4.3/","dependencies":[],"dev_dependency":false},{"name":"url_launcher_web","path":"/Users/venbateam/.pub-cache/hosted/pub.dev/url_launcher_web-2.4.1/","dependencies":[],"dev_dependency":false}]},"dependencyGraph":[{"name":"connectivity_plus","dependencies":[]},{"name":"firebase_app_check","dependencies":["firebase_app_check_web","firebase_core"]},{"name":"firebase_app_check_web","dependencies":["firebase_core","firebase_core_web"]},{"name":"firebase_auth","dependencies":["firebase_auth_web","firebase_core"]},{"name":"firebase_auth_web","dependencies":["firebase_core","firebase_core_web"]},{"name":"firebase_core","dependencies":["firebase_core_web"]},{"name":"firebase_core_web","dependencies":[]},{"name":"firebase_messaging","dependencies":["firebase_core","firebase_messaging_web"]},{"name":"firebase_messaging_web","dependencies":["firebase_core","firebase_core_web"]},{"name":"flutter_plugin_android_lifecycle","dependencies":[]},{"name":"flutter_secure_storage","dependencies":["flutter_secure_storage_linux","flutter_secure_storage_macos","flutter_secure_storage_web","flutter_secure_storage_windows"]},{"name":"flutter_secure_storage_linux","dependencies":[]},{"name":"flutter_secure_storage_macos","dependencies":[]},{"name":"flutter_secure_storage_web","dependencies":[]},{"name":"flutter_secure_storage_windows","dependencies":["path_provider"]},{"name":"google_sign_in","dependencies":["google_sign_in_android","google_sign_in_ios","google_sign_in_web"]},{"name":"google_sign_in_android","dependencies":[]},{"name":"google_sign_in_ios","dependencies":[]},{"name":"google_sign_in_web","dependencies":[]},{"name":"local_auth","dependencies":["local_auth_android","local_auth_darwin","local_auth_windows"]},{"name":"local_auth_android","dependencies":["flutter_plugin_android_lifecycle"]},{"name":"local_auth_darwin","dependencies":[]},{"name":"local_auth_windows","dependencies":[]},{"name":"open_file","dependencies":["open_file_android","open_file_web","open_file_ios","open_file_mac","open_file_windows","open_file_linux"]},{"name":"open_file_android","dependencies":[]},{"name":"open_file_ios","dependencies":[]},{"name":"open_file_linux","dependencies":[]},{"name":"open_file_mac","dependencies":[]},{"name":"open_file_web","dependencies":[]},{"name":"open_file_windows","dependencies":[]},{"name":"package_info_plus","dependencies":[]},{"name":"path_provider","dependencies":["path_provider_android","path_provider_foundation","path_provider_linux","path_provider_windows"]},{"name":"path_provider_android","dependencies":[]},{"name":"path_provider_foundation","dependencies":[]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_windows","dependencies":[]},{"name":"shared_preferences","dependencies":["shared_preferences_android","shared_preferences_foundation","shared_preferences_linux","shared_preferences_web","shared_preferences_windows"]},{"name":"shared_preferences_android","dependencies":[]},{"name":"shared_preferences_foundation","dependencies":[]},{"name":"shared_preferences_linux","dependencies":["path_provider_linux"]},{"name":"shared_preferences_web","dependencies":[]},{"name":"shared_preferences_windows","dependencies":["path_provider_windows"]},{"name":"url_launcher","dependencies":["url_launcher_android","url_launcher_ios","url_launcher_linux","url_launcher_macos","url_launcher_web","url_launcher_windows"]},{"name":"url_launcher_android","dependencies":[]},{"name":"url_launcher_ios","dependencies":[]},{"name":"url_launcher_linux","dependencies":[]},{"name":"url_launcher_macos","dependencies":[]},{"name":"url_launcher_web","dependencies":[]},{"name":"url_launcher_windows","dependencies":[]}],"date_created":"2025-09-06 17:15:58.232521","version":"3.35.2","swift_package_manager_enabled":{"ios":false,"macos":false}} \ No newline at end of file diff --git a/Nhance_Partner.zip b/Nhance_Partner.zip deleted file mode 100644 index a916eee..0000000 Binary files a/Nhance_Partner.zip and /dev/null differ diff --git a/build/6c815e8a3aed76495dd8577cce855812.cache.dill.track.dill b/build/6c815e8a3aed76495dd8577cce855812.cache.dill.track.dill index 4e4e47c..177ac47 100644 Binary files a/build/6c815e8a3aed76495dd8577cce855812.cache.dill.track.dill and b/build/6c815e8a3aed76495dd8577cce855812.cache.dill.track.dill differ diff --git a/build/flutter_assets/NOTICES b/build/flutter_assets/NOTICES index b813b21..c29ceb5 100644 --- a/build/flutter_assets/NOTICES +++ b/build/flutter_assets/NOTICES @@ -1839,7 +1839,33 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. © 2020 GitHub, Inc. -------------------------------------------------------------------------------- +archive + +The MIT License + +Copyright (c) 2013-2021 Brendan Duncan. +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +-------------------------------------------------------------------------------- args +csslib logging Copyright 2013, the Dart project authors. @@ -2327,6 +2353,36 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +-------------------------------------------------------------------------------- +charcode + +Copyright 2014, the Dart project authors. All rights reserved. +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + -------------------------------------------------------------------------------- checked_yaml @@ -4147,6 +4203,30 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- +dropdown_search + +MIT License + +Copyright (c) 2020 Salim Lachdhaf + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +-------------------------------------------------------------------------------- easy_loading_button The MIT License (MIT) @@ -4248,6 +4328,30 @@ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +-------------------------------------------------------------------------------- +excel + +The MIT License (MIT) + +Copyright (C) 2021 - Kawaljeet Singh + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + -------------------------------------------------------------------------------- expat @@ -13561,6 +13665,33 @@ Except as contained in this notice, the name of a copyright holder shall not be used in advertising or otherwise to promote the sale, use or other dealings in these Data Files or Software without prior written authorization of the copyright holder. +-------------------------------------------------------------------------------- +html + +Copyright (c) 2006-2012 The Authors + +Contributors: +James Graham - jg307@cam.ac.uk +Anne van Kesteren - annevankesteren@gmail.com +Lachlan Hunt - lachlan.hunt@lachy.id.au +Matt McDonald - kanashii@kanashii.ca +Sam Ruby - rubys@intertwingly.net +Ian Hickson (Google) - ian@hixie.ch +Thomas Broyer - t.broyer@ltgt.net +Jacques Distler - distler@golem.ph.utexas.edu +Henri Sivonen - hsivonen@iki.fi +Adam Barth - abarth@webkit.org +Eric Seidel - eric@webkit.org +The Mozilla Foundation (contributions from Henri Sivonen since 2008) +David Flanagan (Mozilla) - dflanagan@mozilla.com +Google LLC (contributed the Dart port) - misc@dartlang.org + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + -------------------------------------------------------------------------------- iconsax_flutter @@ -30459,6 +30590,56 @@ material_color_utilities See the License for the specific language governing permissions and limitations under the License. +-------------------------------------------------------------------------------- +month_picker_dialog + +MIT License + +Copyright (c) 2025 Gianluca Bettega + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +-------------------------------------------------------------------------------- +nested +provider + +MIT License + +Copyright (c) 2019 Remi Rousselet + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. -------------------------------------------------------------------------------- nm @@ -30836,6 +31017,43 @@ Exhibit B - "Incompatible With Secondary Licenses" Notice This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0. +-------------------------------------------------------------------------------- +open_file +open_file_android +open_file_ios +open_file_linux +open_file_mac +open_file_platform_interface +open_file_web +open_file_windows + +Copyright 2018 crazecoder. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- pausable_timer @@ -34813,6 +35031,459 @@ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +-------------------------------------------------------------------------------- +universal_html + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +---------------------------------------------------------------------------------------------------- + +This version of "universal_html" contains source code from the Dart package "csslib", which was +obtained from: + https://github.com/dart-lang/csslib + +When the source code was obtained, the original source code had the following license: + +Copyright 2013, the Dart project authors. All rights reserved. +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------------------------------------- + +This version of "universal_html" contains source code from the Dart package "html", which was +obtained copied from: + https://github.com/dart-lang/html + +When the source code was obtained, the original source code had the following license: + +Copyright (c) 2006-2012 The Authors + +Contributors: +James Graham - jg307@cam.ac.uk +Anne van Kesteren - annevankesteren@gmail.com +Lachlan Hunt - lachlan.hunt@lachy.id.au +Matt McDonald - kanashii@kanashii.ca +Sam Ruby - rubys@intertwingly.net +Ian Hickson (Google) - ian@hixie.ch +Thomas Broyer - t.broyer@ltgt.net +Jacques Distler - distler@golem.ph.utexas.edu +Henri Sivonen - hsivonen@iki.fi +Adam Barth - abarth@webkit.org +Eric Seidel - eric@webkit.org +The Mozilla Foundation (contributions from Henri Sivonen since 2008) +David Flanagan (Mozilla) - dflanagan@mozilla.com +Google Inc. (contributed the Dart port) - misc@dartlang.org + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +---------------------------------------------------------------------------------------------------- + +-------------------------------------------------------------------------------- +universal_io + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS -------------------------------------------------------------------------------- universal_platform diff --git a/lib/core/routing/app_router.dart b/lib/core/routing/app_router.dart index bc7c1d6..4efcbef 100644 --- a/lib/core/routing/app_router.dart +++ b/lib/core/routing/app_router.dart @@ -4,6 +4,10 @@ import 'package:nhance_partner/core/routing/routes.dart'; import 'package:nhance_partner/presentation/screens/claims/claimsList.dart'; import 'package:nhance_partner/presentation/screens/dashboard/dashboard.dart'; import '../../data/services/auth_service.dart'; +import '../../presentation/screens/UserManagement/Agent/agent.dart'; +import '../../presentation/screens/UserManagement/Agent/agentList.dart'; +import '../../presentation/screens/UserManagement/Staff/staff.dart'; +import '../../presentation/screens/UserManagement/Staff/staffList.dart'; import '../../presentation/screens/home/home_screen.dart'; import '../../presentation/screens/login/login_screen.dart'; import '../../presentation/screens/splash/splash_screen.dart'; @@ -27,6 +31,46 @@ final GoRouter appRouter = GoRouter( path: AppRoutes.dashboard, builder: (context, state) => const DashboardScreen(), ), + + GoRoute( + path: AppRoutes.agentLst, + // builder: (context, state) => const Agent(), + builder: (context, state) => const AgentList(), + ), + + GoRoute( + path: '/agent/:id', + builder: (context, state) { + final id = state.pathParameters['id']!; + + // if "create", open empty form + if (id == 'create') { + return const Agent(); // create mode + } else { + return Agent(id: id); // update mode + } + }, + ), + + GoRoute( + path: AppRoutes.staffLst, + // builder: (context, state) => const Agent(), + builder: (context, state) => const StaffList(), + ), + + GoRoute( + path: '/staff/:id', + builder: (context, state) { + final id = state.pathParameters['id']!; + // if "create", open empty form + if (id == 'create') { + return const Staff(); // create mode + } else { + return Staff(id: id); // update mode + } + }, + ), + ], redirect: (context, state) async { final loggedIn = await AuthService.isLoggedIn(); diff --git a/lib/core/routing/routes.dart b/lib/core/routing/routes.dart index bcbd074..c8eeff4 100644 --- a/lib/core/routing/routes.dart +++ b/lib/core/routing/routes.dart @@ -3,5 +3,9 @@ class AppRoutes { static const String home = '/home'; static const String dashboard = '/dashboard'; static const String login = '/login'; + static const String agentLst = '/agentLst'; + static const agent = '/agent/:id'; + static const String staffLst = '/staffLst'; + static const staff = '/staff/:id'; } diff --git a/lib/core/services/api_service.dart b/lib/core/services/api_service.dart index e36a228..83df97b 100644 --- a/lib/core/services/api_service.dart +++ b/lib/core/services/api_service.dart @@ -1,11 +1,21 @@ import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; +import 'package:nhance_partner/data/services/auth_service.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'dart:convert'; import '../../data/utils/toastNotification.dart'; +import '../config/env.dart'; +import 'package:universal_html/html.dart' as html; +import 'package:http/http.dart' as http; class ApiService { late final BuildContext context; + dynamic _token; + + + _initializeToken(){ + _token = AuthService.getToken(); + } Future _clearLocalStorageAndRedirect() async { final prefs = await SharedPreferences.getInstance(); @@ -21,12 +31,158 @@ class ApiService { return _handleResponse(response); } - Future> _makePostRequest( - Uri url, Map body, Map headers) async { - final response = await http.post(url, headers: headers, body: body); + // Future> _makePostRequest( + // Uri url, Map body, Map headers) async { + // final response = await http.post(url, headers: headers, body: body); + // return _handleResponse(response); + // } + // + // Future> _handleResponse(http.Response response) async { + // if (response.statusCode == 200) { + // return jsonDecode(response.body); + // } else if (response.statusCode == 401) { + // await _clearLocalStorageAndRedirect(); + // return {}; + // } else { + // throw Exception('Failed to load data'); + // } + // } + + Future _makeGethttpRequest( + Uri url, + Map headers, + ) async { + final response = await http.get(url, headers: headers); + + if (response.statusCode == 200) { + return response; // return full http.Response, not Map + } else { + throw Exception('Failed request: ${response.statusCode}'); + } + } + + + Future> _makePostRequestJson( + Uri url, + Map body, + Map headers, + ) async { + final response = await http.post( + url, + headers: headers, + body: jsonEncode(body), // ✅ convert to JSON + ); return _handleResponse(response); } + Future> fetchAgentIncentiveList(agentId) async { + // print(_token); + if (_token == null) { + await _initializeToken(); + } + + final url = Uri.parse( + 'https://venbait.in/nhance/partner/dev/api/agent/agentIncentiveFileList?agent_id=${agentId}', + ); + final headers = { + 'Authorization': 'Bearer $_token' ?? '', + 'App-Signature': 'nhance-partner-2025-signature-35468846JRhH551HK', + }; + final response = await _makeGetRequest(url, headers); + return response; + } + + Future> fetchAgentUserList() async { + // print(_token); + if (_token == null) { + await _initializeToken(); + } + + final userId = '1'; + final url1 = Uri.parse( + '${Env.apiUrl}/api/agent/agentList?manager_id=${userId}', + ); + final url = Uri.parse( + 'https://venbait.in/nhance/partner/dev/api/agent/agentList?manager_id=${userId}', + ); + final headers = { + 'Authorization': 'Bearer $_token' ?? '', + 'App-Signature': 'nhance-partner-2025-signature-35468846JRhH551HK', + }; + final response = await _makeGetRequest(url, headers); + return response; + } + + Future> findSingleAgentData(id) async { + // print(_token); + if (_token == null) { + await _initializeToken(); + } + // final url = Uri.parse('${Env.apiUrl}/api/agent/agentList'); + final url = Uri.parse( + 'https://venbait.in/nhance/partner/dev/api/agent/findAgent?id=$id', + ); + final headers = { + 'Authorization': 'Bearer $_token' ?? '', + 'App-Signature': 'nhance-partner-2025-signature-35468846JRhH551HK', + }; + final response = await _makeGetRequest(url, headers); + return response; + } + + Future> deleteAgentIncentiveFile(id) async { + // print(_token); + if (_token == null) { + await _initializeToken(); + } + // final url = Uri.parse('${Env.apiUrl}/api/agent/agentList'); + final url = Uri.parse( + 'https://venbait.in/nhance/partner/dev/api/agent/deleteAgentIncentiveFile?id=$id', + ); + final headers = { + 'Authorization': 'Bearer $_token' ?? '', + 'App-Signature': 'nhance-partner-2025-signature-35468846JRhH551HK', + }; + final response = await _makeGetRequest(url, headers); + return response; + } + + Future> fetchStaffUserList() async { + // print(_token); + if (_token == null) { + await _initializeToken(); + } + + final userId = '1'; + // final url = Uri.parse('${Env.apiUrl}/api/agent/agentList'); + final url = Uri.parse( + 'https://venbait.in/nhance/partner/dev/api/staff/staffList?manager_id=${userId}', + ); + final headers = { + 'Authorization': 'Bearer $_token' ?? '', + 'App-Signature': 'nhance-partner-2025-signature-35468846JRhH551HK', + }; + final response = await _makeGetRequest(url, headers); + return response; + } + + Future> findSingleStaffData(id) async { + // print(_token); + if (_token == null) { + await _initializeToken(); + } + // final url = Uri.parse('${Env.apiUrl}/api/staff/findStaff?id=$id'); + final url = Uri.parse( + 'https://venbait.in/nhance/partner/dev/api/staff/findStaff?id=$id', + ); + final headers = { + 'Authorization': 'Bearer $_token' ?? '', + 'App-Signature': 'nhance-partner-2025-signature-35468846JRhH551HK', + }; + final response = await _makeGetRequest(url, headers); + return response; + } + Future> _handleResponse(http.Response response) async { if (response.statusCode == 200) { return jsonDecode(response.body); @@ -37,4 +193,100 @@ class ApiService { throw Exception('Failed to load data'); } } + + Future getPdfDownload(path, id) async { + // final url = Uri.parse( + // 'https://venbait.in/nhance/partner/dev/api/agent/downloadAgentCertificateFile?agent_id=1', + // ); + final url = Uri.parse('https://venbait.in/nhance/partner/dev/$path}'); + + // final token = await getToken(); + + if (_token == null) { + throw Exception('Token not found. Please log in.'); + } + + final headers = { + 'Authorization': 'Bearer $_token' ?? '', + 'App-Signature': 'nhance-partner-2025-signature-35468846JRhH551HK', + }; + final response = await _makeGethttpRequest(url, headers); + + if (response.statusCode == 200) { + try { + print("PDf Dowloaded"); + + // Create a blob from the response body + final blob = html.Blob([response.bodyBytes]); + + // Generate a download URL for the blob + final url = html.Url.createObjectUrlFromBlob(blob); + + // Create a link element to trigger the download + final anchor = html.AnchorElement(href: url) + ..setAttribute('download', 'nhancePartner_$id.pdf') + ..click(); + + // Revoke the download URL to free up resources + html.Url.revokeObjectUrl(url); + } catch (e) { + throw Exception('Error parsing response: $e'); + } + } else if (response.statusCode == 404) { + // showDialog( + // context: context, + // builder: (BuildContext context) { + // return AlertDialog( + // title: Text('File not found.'), + // // content: Text('File not found.'), + // actions: [ + // TextButton( + // child: Text('OK'), + // onPressed: () { + // Navigator.of(context).pop(); // Close the dialog + // }, + // ), + // ], + // ); + // }, + // ); + } else { + throw Exception('Failed to load plans'); + } + } + + Future> updateStatus(id, status, role) async { + final Map data = { + "id": int.parse(id), + "is_active": int.parse(status), + }; + final url; + + if (role == 'staff') { + url = Uri.parse( + 'https://venbait.in/nhance/partner/dev/api/staff/changeStaffStatus', + ); + } else { + url = Uri.parse( + 'https://venbait.in/nhance/partner/dev/api/agent/changeAgentStatus', + ); + } + + // final token = await getToken(); // Fetch token + + if (_token == null) { + throw Exception('Token not found. Please log in.'); + } + + print("data------- $data}"); + + final headers = { + 'Authorization': 'Bearer $_token', + 'Content-Type': 'application/json', + 'app-signature': 'nhance-partner-2025-signature-35468846JRhH551HK', + }; + + final response = await _makePostRequestJson(url, data, headers); + return response; + } } diff --git a/lib/data/utils/Pagination.dart b/lib/data/utils/Pagination.dart index 096eab4..9ed07bf 100644 --- a/lib/data/utils/Pagination.dart +++ b/lib/data/utils/Pagination.dart @@ -7,6 +7,7 @@ class PaginationControls extends StatelessWidget { final int totalItems; final Function(int) onPageChanged; final Function(int) onItemsPerPageChanged; + // final Color? activeColor; const PaginationControls({ @@ -21,123 +22,178 @@ class PaginationControls extends StatelessWidget { @override Widget build(BuildContext context) { - // int totalPages = (totalItems / itemsPerPage).ceil(); final int totalPages = (totalItems / itemsPerPage).ceil(); - // return Row( - // mainAxisAlignment: MainAxisAlignment.end, - // children: [ - // Padding( - // padding: const EdgeInsets.symmetric(vertical: 12), - // child: Row( - // mainAxisAlignment: MainAxisAlignment.center, - // children: [ - // DropdownButton( - // value: itemsPerPage, - // items: - // [5, 10, 15, 20, 50].map((int value) { - // return DropdownMenuItem( - // value: value, - // child: Text( - // ' $value ', - // style: GoogleFonts.poppins(fontSize: 15), - // ), - // ); - // }).toList(), - // onChanged: (newValue) { - // if (newValue != null) { - // onItemsPerPageChanged(newValue); - // onPageChanged(1); // reset to first page - // } - // }, - // ), - // IconButton( - // onPressed: - // currentPage > 1 - // ? () => onPageChanged(currentPage - 1) - // : null, - // icon: Icon(Icons.chevron_left), - // ), - // for (int i = 1; i <= totalPages - 1; i++) - // Padding( - // padding: const EdgeInsets.symmetric(horizontal: 4), - // child: ElevatedButton( - // style: ElevatedButton.styleFrom( - // backgroundColor: - // currentPage == i - 1 ? activeColor : Colors.grey[300], - // foregroundColor: - // currentPage == i - 1 ? Colors.white : Colors.black, - // minimumSize: Size(36, 36), - // padding: EdgeInsets.zero, - // ), - // onPressed: () => onPageChanged(i - 1), - // child: Text(i.toString()), - // ), - // ), - // IconButton( - // onPressed: - // currentPage < totalPages - 1 - // ? () => onPageChanged(currentPage + 1) - // : null, - // icon: Icon(Icons.chevron_right), - // ), - // ], - // ), - // ), - // ], - // ); - return Row( mainAxisAlignment: MainAxisAlignment.end, children: [ + // Dropdown for rows per page DropdownButton( value: itemsPerPage, - items: [10, 20, 50, 100] - .map( - (value) => DropdownMenuItem( - value: value, - child: Text( - '$value', - style: TextStyle(fontSize: 12, fontFamily: "Inter"), - ), - ), - ) - .toList(), - onChanged: (value) { - if (value != null) { - onItemsPerPageChanged(value); + items: [5, 10, 15, 20, 50].map((int value) { + return DropdownMenuItem( + value: value, + child: Text('$value', style: GoogleFonts.poppins(fontSize: 15)), + ); + }).toList(), + onChanged: (newValue) { + if (newValue != null) { + onItemsPerPageChanged(newValue); + onPageChanged(1); // reset to page 1 when rows change } }, - style: TextStyle( - fontSize: 12, - color: Colors.black, - fontFamily: "Inter", - ), - underline: SizedBox(), - iconSize: 16, ), - SizedBox(width: 16), + + // Previous button IconButton( - icon: Icon(Icons.arrow_back, size: 18), - onPressed: currentPage > 0 + onPressed: currentPage > 1 ? () => onPageChanged(currentPage - 1) : null, + icon: Icon(Icons.arrow_back), ), - SingleChildScrollView( - scrollDirection: Axis.horizontal, - child: Row(children: _buildPageButtons()), - ), + // Page buttons + for (int i = 1; i <= totalPages; i++) + Padding( + padding: const EdgeInsets.symmetric(horizontal: 4), + child: ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: currentPage == i + ? Color(0xFFD4F8F3) + : Color(0xFFEDF6F5), + foregroundColor: currentPage == i ? Colors.black : Colors.grey, + minimumSize: const Size(36, 36), + padding: EdgeInsets.zero, + ), + onPressed: () => onPageChanged(i), + child: Text(i.toString()), + ), + ), + // Next button IconButton( - icon: Icon(Icons.arrow_forward, size: 18), - onPressed: (currentPage + 1) * itemsPerPage < totalItems + onPressed: currentPage < totalPages ? () => onPageChanged(currentPage + 1) : null, + icon: Icon(Icons.arrow_forward), ), ], ); } + // Widget build(BuildContext context) { + // // int totalPages = (totalItems / itemsPerPage).ceil(); + // final int totalPages = (widget.totalItems / widget.itemsPerPage).ceil(); + // + // return Row( + // mainAxisAlignment: MainAxisAlignment.end, + // children: [ + // DropdownButton( + // value: itemsPerPage, + // items: [5, 10, 15, 20, 50].map((int value) { + // return DropdownMenuItem( + // value: value, + // child: Text(' $value ', style: GoogleFonts.poppins(fontSize: 15)), + // ); + // }).toList(), + // onChanged: (newValue) { + // setState(() { + // itemsPerPage = newValue!; + // currentPage = 1; // Reset to first page when rows per page changes + // }); + // }, + // ), + // IconButton( + // onPressed: currentPage > 1 + // ? () { + // setState(() { + // currentPage--; + // }); + // } + // : null, + // icon: Icon(Icons.chevron_left), + // ), + // for (int i = 1; i <= (totalItems / itemsPerPage).ceil(); i++) + // Padding( + // padding: const EdgeInsets.symmetric(horizontal: 4), + // child: ElevatedButton( + // style: ElevatedButton.styleFrom( + // backgroundColor: _currentPage == i + // ? Color(0xFF00A6A6) + // : Colors.grey[300], + // foregroundColor: _currentPage == i + // ? Colors.white + // : Colors.black, + // minimumSize: Size(36, 36), + // padding: EdgeInsets.zero, + // ), + // onPressed: () { + // setState(() { + // _currentPage = i; + // }); + // }, + // child: Text(i.toString()), + // ), + // ), + // IconButton( + // onPressed: currentPage < (totalItems / itemsPerPage).ceil() + // ? () { + // setState(() { + // currentPage++; + // }); + // } + // : null, + // icon: Icon(Icons.chevron_right), + // ), + // ], + // // children: [ + // // DropdownButton( + // // value: itemsPerPage, + // // items: [10, 20, 50, 100] + // // .map( + // // (value) => DropdownMenuItem( + // // value: value, + // // child: Text( + // // '$value', + // // style: TextStyle(fontSize: 12, fontFamily: "Inter"), + // // ), + // // ), + // // ) + // // .toList(), + // // onChanged: (value) { + // // if (value != null) { + // // onItemsPerPageChanged(value); + // // } + // // }, + // // style: TextStyle( + // // fontSize: 12, + // // color: Colors.black, + // // fontFamily: "Inter", + // // ), + // // underline: SizedBox(), + // // iconSize: 16, + // // ), + // // SizedBox(width: 16), + // // IconButton( + // // icon: Icon(Icons.arrow_back, size: 18), + // // onPressed: currentPage > 0 + // // ? () => onPageChanged(currentPage - 1) + // // : null, + // // ), + // // + // // SingleChildScrollView( + // // scrollDirection: Axis.horizontal, + // // child: Row(children: _buildPageButtons()), + // // ), + // // + // // IconButton( + // // icon: Icon(Icons.arrow_forward, size: 18), + // // onPressed: (currentPage + 1) * itemsPerPage < totalItems + // // ? () => onPageChanged(currentPage + 1) + // // : null, + // // ), + // // ], + // ); + // } List _buildPageButtons() { final int totalPages = (totalItems / itemsPerPage).ceil(); @@ -183,7 +239,7 @@ class PaginationControls extends StatelessWidget { foregroundColor: currentPage == pageIndex ? Colors.black - : Color(0xFF696969), + : Colors.grey, minimumSize: Size(36, 36), padding: EdgeInsets.zero, ), @@ -198,4 +254,4 @@ class PaginationControls extends StatelessWidget { child: Text('...', style: TextStyle(fontSize: 16)), ); } -} +} \ No newline at end of file diff --git a/lib/data/utils/commonLoaderButton.dart b/lib/data/utils/commonLoaderButton.dart new file mode 100644 index 0000000..18a63f3 --- /dev/null +++ b/lib/data/utils/commonLoaderButton.dart @@ -0,0 +1,48 @@ +import 'package:flutter/material.dart'; +import 'package:easy_loading_button/easy_loading_button.dart'; + +class CommonButton extends StatelessWidget { + final String text; + final Future Function() onPressed; + final double width; + final double height; + final Color backgroundColor; + final Color textColor; + + const CommonButton({ + super.key, + required this.text, + required this.onPressed, + this.width = 200, + this.height = 50, + this.backgroundColor = Colors.blue, + this.textColor = Colors.white, + }); + + @override + Widget build(BuildContext context) { + return EasyButton( + type: EasyButtonType.elevated, + idleStateWidget: Text( + text, + style: TextStyle( + color: textColor, + fontSize: 16, + fontWeight: FontWeight.w600, + ), + ), + loadingStateWidget: const CircularProgressIndicator( + strokeWidth: 3.0, + valueColor: AlwaysStoppedAnimation(Colors.white), + ), + useWidthAnimation: true, + useEqualLoadingStateWidgetDimension: true, + width: width, + height: height, + borderRadius: 8, + contentGap: 6.0, + buttonColor: backgroundColor, + onPressed: onPressed, + ); + } +} diff --git a/lib/presentation/layouts/main_layout.dart b/lib/presentation/layouts/main_layout.dart index 570a54f..5feae24 100644 --- a/lib/presentation/layouts/main_layout.dart +++ b/lib/presentation/layouts/main_layout.dart @@ -57,11 +57,23 @@ class MainLayout extends StatelessWidget { debugPrint("Notifications tapped"); }, ), - drawer: const DrawerMenu(), - body: Column( + body: Row( children: [ - Expanded(child: body), - const Footer(), + const SizedBox( + width: 80, // fixed width for drawer + child: DrawerMenu(), + ), + Expanded( + child: Container( + padding: EdgeInsets.all(10.0), + color: Colors.white, + child: Column( + children: [ + Expanded(child: body), + ], + ), + ), + ), ], ), ), diff --git a/lib/presentation/providers/manager_provider.dart b/lib/presentation/providers/manager_provider.dart new file mode 100644 index 0000000..518db52 --- /dev/null +++ b/lib/presentation/providers/manager_provider.dart @@ -0,0 +1,3 @@ +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +final managerIdProvider = StateProvider((ref) => null); diff --git a/lib/presentation/providers/userRoleProvider.dart b/lib/presentation/providers/userRoleProvider.dart new file mode 100644 index 0000000..0dfe094 --- /dev/null +++ b/lib/presentation/providers/userRoleProvider.dart @@ -0,0 +1,4 @@ +// providers/user_role_provider.dart +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +final userRoleProvider = StateProvider((ref) => null); \ No newline at end of file diff --git a/lib/presentation/screens/UserManagement/Agent/agent.dart b/lib/presentation/screens/UserManagement/Agent/agent.dart new file mode 100644 index 0000000..4d9ab97 --- /dev/null +++ b/lib/presentation/screens/UserManagement/Agent/agent.dart @@ -0,0 +1,551 @@ +import 'dart:convert'; +// import 'dart:io' as html; +import 'dart:typed_data'; // Import for Uint8List +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; +import 'package:http/http.dart' as http; +import 'package:nhance_partner/core/routing/routes.dart'; +import 'package:nhance_partner/presentation/themes/indicators/text_field_theme.dart'; + +import '../../../../core/services/api_service.dart'; +import '../../../../data/utils/Pagination.dart'; +import '../../../../data/utils/validators.dart'; +import '../../../layouts/main_layout.dart'; +import '../../../themes/indicators/export_btn.dart'; +import '../../../themes/indicators/search_field_theme.dart'; +import '../../../themes/indicators/upload_doc_theme.dart'; +import 'package:universal_html/html.dart' as html; + +class Agent extends StatefulWidget { + final String? id; + const Agent({super.key, this.id}); + @override + AgentState createState() => AgentState(); +} + +class AgentState extends State { + final _formKey = GlobalKey(); + late ApiService apiService; + List tabHeader = ['name', 'email', 'mobile', 'code', 'address']; + late String isActive = "1"; + html.File? passportFile; + String? selectedFileNames; + String? passportFileUrlFromApi; + String? selectedId; + + Map controllers = {}; + + String userId = "1"; + + Map dataDetails() { + final data = { + "name": controllers["name"]?.text, + "email": controllers["email"]?.text, + "mobile": controllers["mobile"]?.text, + "address": controllers["address"]?.text, + "agent_code": controllers["code"]?.text, + "is_active": isActive, + "manager_id": userId, + }; + return data; + } + + @override + void initState() { + super.initState(); + apiService = ApiService(); + for (String field in tabHeader) { + controllers[field] = TextEditingController(); + } + updateData(); + } + + void updateData() async { + if (widget.id != null && widget.id != 'create') { + dynamic response = await apiService.findSingleAgentData(widget.id!); + final data = response['data']; + print("updateData - ${response['data']}"); + + if (data == null) return; + setState(() { + selectedId = widget.id; + controllers['name']?.text = data['name'] ?? ''; + controllers['email']?.text = data['email'] ?? ''; + controllers['mobile']?.text = data['mobile'] ?? ''; + controllers['code']?.text = data['agent_code'] ?? ''; + controllers['address']?.text = data['address'] ?? ''; + isActive = data["is_active"]; + + String? apiDocPath = data["certificate_file_name"]; + if (apiDocPath != null && apiDocPath.isNotEmpty) { + selectedFileNames = apiDocPath.split('/').last; + passportFileUrlFromApi = apiDocPath; + passportFile = null; + } else { + selectedFileNames = null; + passportFile = null; + passportFileUrlFromApi = null; + } + }); + } + } + + Future handleSave() async { + if (!_formKey.currentState!.validate()) return; + setState(() { + if (_formKey.currentState!.validate()) { + dataDetails(); + final dataSet = dataDetails(); + print("dataSetAgent - $dataSet"); + createUserData(dataSet); + } else { + // isDisable = false; + } + }); + } + + Future createUserData(Map userData) async { + final bool isUpdating = widget.id != null && widget.id != 'create'; + final id = widget.id; + final uri = Uri.parse( + // isUpdating + // ? '$apiUrl/api/users/update/${widget.id}' + // : '$apiUrl/api/users/create', + isUpdating + ? 'https://venbait.in/nhance/partner/dev/api/agent/updateAgent' + : 'https://venbait.in/nhance/partner/dev/api/agent/createAgent', + ); + dynamic token = + "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOi8vbG9jYWxob3N0L25oYW5jZV9wYXJ0bmVyX2JlLyIsImlhdCI6MTc1NzEzMDM4NCwiZXhwIjoxNzU3MTQ0Nzg0LCJzdWIiOiIxIiwiZGF0YSI6eyJpZCI6IjEiLCJuYW1lIjoiVmFkaXZlbCIsImVtYWlsIjoiZ293dGhhbWNlbGluZTQ2QGdtYWlsLmNvbSIsIm1vYmlsZSI6Ijk4NjU5ODY1OTgiLCJhZGRyZXNzIjoiMjMiLCJhZ2VudF9jb2RlIjoiMSIsImNlcnRpZmljYXRlX2ZpbGVfbmFtZSI6IjE3NTY4NzU5MjBfMTc1Njg3NTkyMF8xNmYyODY0YzkwOTdkN2JjYThiNS5wZGYiLCJlbWFpbF9vdHAiOiI2OTgxNDAiLCJmaXJlYmFzZV9kZXZpY2VfdG9rZW4iOm51bGwsImlzX2FjdGl2ZSI6IjEiLCJjcmVhdGVkX2J5IjpudWxsLCJjcmVhdGVkX29uIjoiMjAyNS0wOS0wMSAxODowMjo0NiIsInVwZGF0ZWRfYnkiOm51bGwsInVwZGF0ZWRfb24iOiIyMDI1LTA5LTA1IDE3OjMwOjI1IiwibWFuYWdlcl9pZCI6IjEifX0.Upo3LsHzOapy1kHEpwhBS7jMRj1yTKgr9B4U92mmUkA"; + if (token == null) { + throw Exception('Token not found. Please log in.'); + } + + // Use MultipartRequest (POST only) + final request = http.MultipartRequest('POST', uri); + request.headers['Authorization'] = 'Bearer $token'; + request.headers['app-signature'] = + 'nhance-partner-2025-signature-35468846JRhH551HK'; + + // If updating, spoof the method Laravel-style + if (isUpdating) { + // request.fields['_method'] = 'PUT'; + request.fields['id'] = id!; + } + + print("USerDAta - $userData"); + + userData.forEach((key, value) { + request.fields[key] = value.toString(); + print("✅ Encoded travel_details2: ${request.fields[key]}"); + }); + + // Attach file if selected + if (passportFile != null) { + try { + final reader = html.FileReader(); + reader.readAsArrayBuffer(passportFile!); + await reader.onLoad.first; + + final data = reader.result as Uint8List; + + final multipartFile = http.MultipartFile.fromBytes( + 'certificate_file_name', + data, + filename: passportFile!.name, + ); + + request.files.add(multipartFile); + print("📎 File attached: ${passportFile!.name}"); + } catch (e) { + print("❌ Failed to read file: $e"); + } + } else { + print("⚠️ No passport file selected."); + } + + print("🚀 Sending request with fields: ${request.fields}"); + + try { + final streamedResponse = await request.send(); + final response = await http.Response.fromStream(streamedResponse); + print("Response status: ${response.statusCode}"); + print("Response body: ${response.body}"); + + if (response.statusCode == 200 || response.statusCode == 201) { + // dispose(); + print("✅ Agent submitted successfully!"); + + print("📨 Response: ${response.body}"); + context.go(AppRoutes.agentLst); + } else { + print("❌ Submission failed. Status: ${response.statusCode}"); + print("📨 Body: ${response.body}"); + + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: Text("Agent Creation Failed"), + content: Text( + "There was a problem in creating user. Please try again.", + ), + actions: [ + TextButton( + child: Text("OK"), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + ], + ); + }, + ); + } + } catch (e) { + print("🔥 Error submitting user: $e"); + } + } + + @override + void dispose() { + for (var controller in controllers.values) { + controller.dispose(); + } + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return MainLayout( + title: "Agent", + body: Container( + // color: Colors.yellow.shade50, + width: MediaQuery.of(context).size.width, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Container( + // height: 30, + // color: Colors.red.shade50, + width: MediaQuery.of(context).size.width, + child: GestureDetector( + onTap: () { + context.go(AppRoutes.agentLst); + }, + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Icon( + Icons.arrow_left_sharp, + size: 35, + color: Color(0xFF425B5B), + ), + Text( + "Agent", + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.w600, + ), + ), + ], + ), + ), + ), + SizedBox(height: 10), + Expanded( + child: Container( + // color: Colors.green, + width: MediaQuery.of(context).size.width, + margin: EdgeInsets.all(8.0), + padding: EdgeInsets.symmetric(horizontal: 14.0, vertical: 20.0), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(8.0), + color: Color(0xFFEDF6F5), + ), + child: Column( + children: [ + Row(children: [Expanded(child: buildFormFields())]), + Spacer(), + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + GestureDetector( + onTap: () { + handleSave(); + }, + child: Container( + padding: EdgeInsets.symmetric( + horizontal: 45.0, + vertical: 8, + ), + + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(8.0), + color: Color(0xFF425B5B), + ), + child: Text( + 'Save', + style: TextStyle(color: Colors.white), + ), + ), + ), + ], + ), + SizedBox(height: 30), + ], + ), + ), + ), + + SizedBox(height: 10), + ], + ), + ), + ); + } + + Widget buildFormFields() { + return Form( + key: _formKey, + child: Column( + children: [ + Row( + children: [ + Expanded(child: buildName()), + SizedBox(width: 25), + Expanded(child: buildEmail()), + ], + ), + SizedBox(height: 20), + Row( + children: [ + Expanded(child: buildPhNumber()), + SizedBox(width: 25), + Expanded(child: buildId()), + ], + ), + SizedBox(height: 20), + Row( + children: [ + Expanded(child: buildAddress()), + SizedBox(width: 25), + Expanded(child: buildUploadDocument()), + ], + ), + SizedBox(height: 20), + // Row( + // children: [ + // Expanded(child: buildIncentiveFile()), + // SizedBox(width: 25), + // Expanded(child: SizedBox.shrink()), + // ], + // ), + ], + ), + ); + } + + Widget buildName() { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text("Full Name", style: _textStyle), + SizedBox(width: 10), + ThemedFormField( + controller: controllers['name']!, + validator: (value) => Validators.requiredField(value, "name"), + txtwidth: MediaQuery.of(context).size.width * 0.26, + ), + ], + ); + } + + Widget buildEmail() { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text("Email", style: _textStyle), + SizedBox(width: 10), + ThemedFormField( + controller: controllers['email']!, + validator: (value) => Validators.email(value, "email"), + txtwidth: MediaQuery.of(context).size.width * 0.26, + ), + ], + ); + } + + Widget buildPhNumber() { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text("Phone Number", style: _textStyle), + SizedBox(width: 10), + ThemedFormField( + controller: controllers['mobile']!, + validator: (value) => Validators.phone(value, "phNumber"), + txtwidth: MediaQuery.of(context).size.width * 0.26, + ), + ], + ); + } + + Widget buildId() { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text("Id", style: _textStyle), + SizedBox(width: 10), + ThemedFormField( + controller: controllers['code']!, + validator: (value) => Validators.requiredField(value, "id"), + txtwidth: MediaQuery.of(context).size.width * 0.26, + ), + ], + ); + } + + Widget buildAddress() { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text("Address", style: _textStyle), + SizedBox(width: 10), + ThemedFormField( + controller: controllers['address']!, + + txtwidth: MediaQuery.of(context).size.width * 0.26, + ), + ], + ); + } + + Widget buildUploadDocument() { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text("Upload Certificate", style: _textStyle), + SizedBox(width: 10), + Column( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + ThemedUploadField( + hintText: selectedFileNames ?? "Upload Document", + txtwidth: MediaQuery.of(context).size.width * 0.26, + onFileSelected: (fileName, file) { + print("Picked file: $fileName (${file.size} bytes)"); + setState(() { + passportFile = file; + }); + }, + ), + SizedBox(height: 5), + + if (passportFile != null || passportFileUrlFromApi != null) + // Centers the text + Container( + color: Colors.white, + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + GestureDetector( + onTap: () { + print('DOWNLOAD - $passportFile'); + + if (passportFile != null) { + try { + // Create a blob from the response body + final blob = html.Blob([passportFile!]); + + // Generate a download URL for the blob + final url = html.Url.createObjectUrlFromBlob(blob); + + // Create a link element to trigger the download + final anchor = html.AnchorElement(href: url) + ..setAttribute( + 'download', + selectedFileNames ?? "document.pdf", + ) + ..click(); + + // Revoke the download URL to free up resources + html.Url.revokeObjectUrl(url); + + print("Download triggered successfully!"); + } catch (e) { + print("Error during download: $e"); + } + } else if (passportFileUrlFromApi != null) { + print("Raw file path: $passportFileUrlFromApi"); + + final path = + 'api/agent/downloadAgentCertificateFile?agent_id=$selectedId'; + + apiService.getPdfDownload(path, selectedId); + } else { + print("No file available to download."); + } + }, + child: Container( + padding: const EdgeInsets.all(5), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(5), + color: Colors.green.shade300, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Text( + "Download", + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w200, + color: Colors.white, + ), + overflow: TextOverflow.ellipsis, + textAlign: TextAlign + .end, // Ensures text is centered within the Text widget + ), + SizedBox(width: 5), + Icon(Icons.download, size: 13, color: Colors.white), + ], + ), + ), + ), + ], + ), + ), + ], + ), + ], + ); + } + + Widget buildIncentiveFile() { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text("Incentive File", style: _textStyle), + SizedBox(width: 10), + ThemedUploadField( + hintText: "Upload Document", + txtwidth: MediaQuery.of(context).size.width * 0.26, + onFileSelected: (fileName, file) { + print("Picked file: $fileName (${file.size} bytes)"); + }, + ), + ], + ); + } + + static const _textStyle = TextStyle( + fontSize: 14, + fontWeight: FontWeight.w600, + ); +} diff --git a/lib/presentation/screens/UserManagement/Agent/agentIncentiveFile.dart b/lib/presentation/screens/UserManagement/Agent/agentIncentiveFile.dart new file mode 100644 index 0000000..3b919cc --- /dev/null +++ b/lib/presentation/screens/UserManagement/Agent/agentIncentiveFile.dart @@ -0,0 +1,719 @@ +// import 'dart:io' as html; +// import 'dart:nativewrappers/_internal/vm/lib/typed_data_patch.dart'; +import 'dart:typed_data'; // Import for Uint8List +import 'package:dropdown_search/dropdown_search.dart'; +import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:http/http.dart' as http; +import 'package:intl/intl.dart'; +import 'package:nhance_partner/presentation/themes/indicators/text_field_theme.dart'; +import 'package:universal_html/html.dart' as html; + +import '../../../../core/services/api_service.dart'; +import '../../../../data/utils/validators.dart'; +import '../../../themes/indicators/date_field_theme.dart'; +import '../../../themes/indicators/input_field_decoration.dart'; +import '../../../themes/indicators/month_field_theme.dart'; +import '../../../themes/indicators/search_field_theme.dart'; +import '../../../themes/indicators/upload_doc_theme.dart'; + +/// function to open the modal +void showUploadIncentiveModal(BuildContext context) { + showDialog( + context: context, + builder: (context) => const UploadIncentiveModal(), + ); +} + +class UploadIncentiveModal extends StatefulWidget { + const UploadIncentiveModal({super.key}); + + @override + UploadIncentiveModalState createState() => UploadIncentiveModalState(); +} + +class UploadIncentiveModalState extends State { + late ApiService apiService; + bool isLoading = false; + final _formKey = GlobalKey(); + final GlobalKey>> dropDownKey = + GlobalKey>>(); + + List> getAgentData = []; + List> filteredData = []; + List> getAgentIncentiveFileData = []; + List> filteredIncentiveData = []; + Map controllers = {}; + + html.File? passportFile; + String? selectedFileNames; + + List tabHeader = ['name', 'agentId', 'date', 'search']; + final userId = 1; + Map dataDetails() { + final data = { + // "date": controllers["date"]?.text, + // "agent_code": controllers["code"]?.text, + // "is_active": isActive, + // "manager_id": userId, + // "agent_id": 'CD001', + "agent_id": controllers["agentId"]?.text, + // "incentive_month": 'Apr 2025', + "incentive_month": controllers["date"]?.text, + "created_by": userId, + }; + return data; + } + + @override + void initState() { + super.initState(); + apiService = ApiService(); + for (String field in tabHeader) { + controllers[field] = TextEditingController(); + } + + getAgentList(); + } + + @override + void dispose() { + for (var controller in controllers.values) { + controller.dispose(); + } + controllers['date']?.dispose(); + controllers['code']?.dispose(); + super.dispose(); + } + + Future refresh() async { + setState(() { + // reset file fields + passportFile = null; + + // selectedFileNames = ""; + selectedFileNames = null; + + getAgentData = []; + filteredData = []; + getAgentIncentiveFileData = []; + filteredIncentiveData = []; + + // clear all text controllers + for (var controller in controllers.values) { + controller.clear(); + } + dropDownKey.currentState?.clear(); // 👈 clear selected agent + controllers['agentId']?.clear(); // clear text field value too + controllers['uploadFile']?.clear(); + }); + } + + void filterData(String query) { + print("FilterDAta - $query"); + setState(() { + filteredIncentiveData = getAgentIncentiveFileData.where((item) { + return (item['incentive_month'] ?? '').toLowerCase().contains( + query.toLowerCase(), + ); + }).toList(); + }); + } + + Future getAgentList() async { + print('getClaimList called'); + setState(() { + isLoading = true; + }); + + try { + final response = await apiService.fetchAgentUserList(); + + if (response['status'] == 'success') { + print('getAgentListData - ${response['data']}'); + setState(() { + getAgentData = List>.from(response['data']); + print('API Data - $getAgentData'); + + filteredData = List.from(getAgentData); + print('originalData - $filteredData'); + }); + } else { + getAgentData = []; + filteredData = []; + } + } catch (e) { + print('Exception occurred: $e'); + } finally { + setState(() { + isLoading = false; + }); + } + } + + Future getAgentIncenctiveFileList(agentId) async { + print('getClaimList agentId - $agentId'); + setState(() { + isLoading = true; + }); + + try { + final response = await apiService.fetchAgentIncentiveList(agentId); + + if (response['status'] == 'success') { + print('AgentIncentive - ${response['data']}'); + setState(() { + getAgentIncentiveFileData = List>.from( + response['data'], + ); + print('API AgentIncentive - $getAgentIncentiveFileData'); + + filteredIncentiveData = List.from(getAgentIncentiveFileData); + print('originalData - $filteredIncentiveData'); + }); + } else { + getAgentIncentiveFileData = []; + } + } catch (e) { + print('Exception occurred: $e'); + } finally { + setState(() { + isLoading = false; + }); + } + } + + Future handleSave() async { + print("Handl1"); + final dataSet = dataDetails(); + print("dataSetAgent - $dataSet"); + print("Handl13f"); + + setState(() { + uploadIncentiveData(dataSet); + }); + if (!_formKey.currentState!.validate()) return; + print("Handl2"); + // setState(() { + // print("Handl3"); + // if (_formKey.currentState!.validate()) { + // dataDetails(); + // final dataSet = dataDetails(); + // print("dataSetAgent - $dataSet"); + // uploadIncentiveData(dataSet); + // } else { + // // isDisable = false; + // } + // }); + } + + Future uploadIncentiveData(Map dataSet) async { + final uri = Uri.parse( + // isUpdating + // ? '$apiUrl/api/users/update/${widget.id}' + // : '$apiUrl/api/users/create', + 'https://venbait.in/nhance/partner/dev/api/agent/uploadAgentIncentiveFile', + ); + dynamic token = + "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOi8vbG9jYWxob3N0L25oYW5jZV9wYXJ0bmVyX2JlLyIsImlhdCI6MTc1NzEzMDM4NCwiZXhwIjoxNzU3MTQ0Nzg0LCJzdWIiOiIxIiwiZGF0YSI6eyJpZCI6IjEiLCJuYW1lIjoiVmFkaXZlbCIsImVtYWlsIjoiZ293dGhhbWNlbGluZTQ2QGdtYWlsLmNvbSIsIm1vYmlsZSI6Ijk4NjU5ODY1OTgiLCJhZGRyZXNzIjoiMjMiLCJhZ2VudF9jb2RlIjoiMSIsImNlcnRpZmljYXRlX2ZpbGVfbmFtZSI6IjE3NTY4NzU5MjBfMTc1Njg3NTkyMF8xNmYyODY0YzkwOTdkN2JjYThiNS5wZGYiLCJlbWFpbF9vdHAiOiI2OTgxNDAiLCJmaXJlYmFzZV9kZXZpY2VfdG9rZW4iOm51bGwsImlzX2FjdGl2ZSI6IjEiLCJjcmVhdGVkX2J5IjpudWxsLCJjcmVhdGVkX29uIjoiMjAyNS0wOS0wMSAxODowMjo0NiIsInVwZGF0ZWRfYnkiOm51bGwsInVwZGF0ZWRfb24iOiIyMDI1LTA5LTA1IDE3OjMwOjI1IiwibWFuYWdlcl9pZCI6IjEifX0.Upo3LsHzOapy1kHEpwhBS7jMRj1yTKgr9B4U92mmUkA"; + if (token == null) { + throw Exception('Token not found. Please log in.'); + } + + // Use MultipartRequest (POST only) + final request = http.MultipartRequest('POST', uri); + request.headers['Authorization'] = 'Bearer $token'; + request.headers['app-signature'] = + 'nhance-partner-2025-signature-35468846JRhH551HK'; + + print("USerDAta - $dataSet"); + + dataSet.forEach((key, value) { + request.fields[key] = value.toString(); + print("✅ Encoded travel_details2: ${request.fields[key]}"); + }); + + // Attach file if selected + if (passportFile != null) { + try { + final reader = html.FileReader(); + reader.readAsArrayBuffer(passportFile!); + await reader.onLoad.first; + + final data = reader.result as Uint8List; + + final multipartFile = http.MultipartFile.fromBytes( + 'incentive_file_name', + data, + filename: passportFile!.name, + ); + + request.files.add(multipartFile); + print("📎 File attached: ${passportFile!.name}"); + } catch (e) { + print("❌ Failed to read file: $e"); + } + } else { + print("⚠️ No passport file selected."); + } + + print("🚀 Sending request with fields: ${request.fields}"); + + try { + final streamedResponse = await request.send(); + final response = await http.Response.fromStream(streamedResponse); + print("Response status: ${response.statusCode}"); + print("Response body: ${response.body}"); + + if (response.statusCode == 200 || response.statusCode == 201) { + // dispose(); + print("✅ Agent submitted successfully!"); + + print("📨 Response: ${response.body}"); + + final data = dataDetails(); // this is a Map + final agentId = data["agent_id"]; + + getAgentIncenctiveFileList(agentId); + // context.go(AppRoutes.agentLst); + } else { + print("❌ Submission failed. Status: ${response.statusCode}"); + print("📨 Body: ${response.body}"); + + // Sort by created_on (latest first) + + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: Text("Agent Creation Failed"), + content: Text( + "There was a problem in creating user. Please try again.", + ), + actions: [ + TextButton( + child: Text("OK"), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + ], + ); + }, + ); + } + } catch (e) { + print("🔥 Error submitting user: $e"); + } + } + + @override + Widget build(BuildContext context) { + return AlertDialog( + backgroundColor: Colors.white, + content: Container( + width: MediaQuery.of(context).size.width * 0.55, + height: MediaQuery.of(context).size.height * 0.7, + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Text( + "Incentive Files", + style: GoogleFonts.poppins( + fontSize: 18, + fontWeight: FontWeight.w500, + ), + ), + Spacer(), + IconButton( + onPressed: () { + refresh(); + }, + icon: Icon(Icons.refresh), + ), + + IconButton( + onPressed: () { + Navigator.pop(context); + }, + icon: Icon(Icons.close), + ), + ], + ), + const SizedBox(height: 5), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Agent Name', style: _labelStyle), + SizedBox(height: 5), + + Container( + width: MediaQuery.of(context).size.width * 0.27, + height: 40, + child: DropdownSearch>( + key: dropDownKey, + selectedItem: null, + items: (filter, infiniteScrollProps) { + return filteredData; // pass the whole object + }, + // itemAsString: (agent) => agent['name'], + // items: (filter, infiniteScrollProps) { + // return getAgentData + // .map((agent) => agent['name'].toString()) + // .toList(); + // }, + itemAsString: (agent) => + agent['name'].toString(), // what to show + compareFn: (item, selectedItem) => + item['id'] == selectedItem['id'], // ✅ compare by id + decoratorProps: DropDownDecoratorProps( + decoration: AppInputDecorations.dropdownDecoration( + label: "Select Agent", + ), + ), + + popupProps: PopupProps.menu( + fit: FlexFit.loose, + showSearchBox: true, + searchFieldProps: TextFieldProps( + decoration: InputDecoration( + hintText: "Search agent...", + enabledBorder: OutlineInputBorder( + borderSide: BorderSide( + color: Colors.grey, + ), // 👈 Normal border + ), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide( + color: Colors.grey, + width: 1.5, + ), // 👈 Focused border + ), + ), + ), + // constraints: BoxConstraints(), + ), + + onChanged: (agent) { + if (agent != null) { + print("Selected Agent Name: ${agent['name']}"); + print("Agent Code: ${agent['agent_code']}"); + print("Agent Id: ${agent['id']}"); + getAgentIncenctiveFileList(agent['id']); + controllers['agentId']?.text = agent['agent_code']; + } + }, + ), + ), + ], + ), + SizedBox(width: 10), + Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Agent ID', style: _labelStyle), + SizedBox(height: 5), + ThemedFormField( + controller: controllers['agentId']!, + txtwidth: MediaQuery.of(context).size.width * 0.27, + txtheight: 40, + backgroundColor: Color(0xFFECECEC), + readOnly: true, + // backgroundColor: Color(0xFFCBCBCB), + ), + ], + ), + ], + ), + const SizedBox(height: 20), + Text("Upload Documents", style: _labelHeaderStyle), + + const SizedBox(height: 15), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + + children: [ + Form( + key: _formKey, + child: Row( + children: [ + Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Month', style: _labelStyle), + SizedBox(height: 5), + ThemedMonthField( + hintText: "Select Month", + txtwidth: MediaQuery.of(context).size.width * 0.27, + txtheight: 40, + backgroundColor: const Color(0xFFECECEC), + validator: (value) => + Validators.requiredField(value, "date"), + borderColor: Colors.grey.shade300, + controller: controllers['date']!, + onDateSelected: (date) { + print("Picked Date: $date"); + // controllers['date']?.text = date as String; + }, + ), + ], + ), + SizedBox(width: 15), + Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Upload File', style: _labelStyle), + SizedBox(height: 5), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ThemedUploadField( + key: ValueKey(selectedFileNames), + // hintText: "Upload Document", + txtheight: 40, + validator: (value) => Validators.requiredField( + value, + "Upload Document", + ), + backgroundColor: Color(0xFFECECEC), + hintText: + (selectedFileNames == null || + selectedFileNames!.isEmpty) + ? "Upload Document" + : selectedFileNames, + txtwidth: + MediaQuery.of(context).size.width * 0.19, + onFileSelected: (fileName, file) { + print( + "Picked file: $fileName (${file.size} bytes)", + ); + setState(() { + passportFile = file; + selectedFileNames = fileName; + }); + }, + ), + SizedBox(width: 15), + Container( + width: + MediaQuery.of(context).size.width * 0.068, + child: ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: Color(0xFF425B5B), + foregroundColor: Colors.white, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular( + 6, + ), // 👈 reduce radius (default ~20) + ), + ), + + onPressed: () { + handleSave(); + // Navigator.pop(context); + }, + child: Text( + "Submit", + style: GoogleFonts.poppins(fontSize: 12), + ), + ), + ), + ], + ), + ], + ), + ], + ), + ), + ], + ), + const SizedBox(height: 15), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('Files', style: _labelHeaderStyle), + ThemedSearchField( + hintText: 'Search', + backgroundColor: Color(0xFFECECEC), + onChanged: filterData, + // onChanged: null, + controller: controllers['search']!, + txtwidth: MediaQuery.of(context).size.width * 0.18, + txtHeight: 35, + ), + ], + ), + const SizedBox(height: 10), + + Expanded( + child: isLoading + ? const Center(child: CircularProgressIndicator()) + : filteredIncentiveData.isEmpty + ? const Center(child: Text("No incentive files found")) + : GridView.builder( + padding: const EdgeInsets.all(8), + gridDelegate: + const SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 2, // two columns + crossAxisSpacing: 10, + mainAxisSpacing: 10, + childAspectRatio: 6.5, // adjust height of rows + ), + itemCount: filteredIncentiveData.length, + itemBuilder: (context, index) { + final file = filteredIncentiveData[index]; + + // final sortedData = [...filteredIncentiveData] + // ..sort((a, b) { + // final dateA = + // DateTime.tryParse(a['created_on'] ?? '') ?? + // DateTime(1970); + // final dateB = + // DateTime.tryParse(b['created_on'] ?? '') ?? + // DateTime(1970); + // return dateB.compareTo(dateA); // latest first + // }); + // final file = sortedData[index]; + return IncentiveFileRow( + fileName: file['incentive_file_name'] ?? "Unknown", + date: file['incentive_month'] ?? "-", + onUpload: () { + print("Upload ${file['id']}"); + final selectedId = file['id']; + final path = + 'api/agent/downloadAgentIncentiveFile?id=$selectedId'; + apiService.getPdfDownload(path, selectedId); + }, + onDelete: () async { + print("Delete ${file['id']}"); + final selectedId = file['id']; + final response = await apiService + .deleteAgentIncentiveFile(selectedId); + print("res - $response"); + + if (response['status'] == 'success') { + print("DELTED"); + + final data = + dataDetails(); // this is a Map + final agentId = data["agent_id"]; + + getAgentIncenctiveFileList(agentId); + // Fluttertoast.showToast( + // msg: response['data'] ?? "Success", + // toastLength: Toast.LENGTH_SHORT, + // gravity: ToastGravity.BOTTOM, + // ); + } else { + // Fluttertoast.showToast( + // msg: "Something went wrong", + // toastLength: Toast.LENGTH_SHORT, + // gravity: ToastGravity.BOTTOM, + // ); + } + }, + ); + }, + ), + ), + ], + ), + ), + ); + } +} + +final _labelStyle = GoogleFonts.poppins( + color: Colors.black, + fontWeight: FontWeight.w600, + fontSize: 12, +); +final _labelHeaderStyle = GoogleFonts.poppins( + fontSize: 14, + fontWeight: FontWeight.w600, +); + +class IncentiveFileRow extends StatelessWidget { + final String fileName; + final String date; + final VoidCallback? onUpload; + final VoidCallback? onDelete; + + const IncentiveFileRow({ + super.key, + required this.fileName, + required this.date, + this.onUpload, + this.onDelete, + }); + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.all(6.0), + // margin: const EdgeInsets.only(bottom: 5), // spacing between rows + decoration: BoxDecoration( + // color: Colors.amber.shade50, + border: Border.all(color: const Color(0xFFE3E3E3)), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + + children: [ + Container( + padding: const EdgeInsets.all(6.0), + decoration: BoxDecoration( + color: const Color(0xFFF4F6F8), + borderRadius: BorderRadius.circular(8.0), + border: Border.all(color: const Color(0xFFE3E3E3)), + ), + child: const Icon(Icons.file_present, color: Color(0xFF838587)), + ), + const SizedBox(width: 10), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + width: 100, + child: Text( + fileName, + style: const TextStyle(fontWeight: FontWeight.w600), + overflow: TextOverflow.ellipsis, + softWrap: true, + maxLines: 1, + ), + ), + Text(date, style: const TextStyle(color: Color(0xFF6E6E6E))), + ], + ), + const Spacer(), + GestureDetector( + onTap: onUpload, + child: const Icon( + Icons.file_download_outlined, + color: Color(0xFF6E6E6E), + ), + ), + SizedBox(width: 10), + GestureDetector( + onTap: onDelete, + child: const Icon(Icons.delete_outlined, color: Color(0xFF6E6E6E)), + ), + ], + ), + ); + } +} diff --git a/lib/presentation/screens/UserManagement/Agent/agentList.dart b/lib/presentation/screens/UserManagement/Agent/agentList.dart new file mode 100644 index 0000000..0e1f958 --- /dev/null +++ b/lib/presentation/screens/UserManagement/Agent/agentList.dart @@ -0,0 +1,467 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; + +import '../../../../core/routing/routes.dart'; +import '../../../../core/services/api_service.dart'; +import '../../../../data/utils/Pagination.dart'; +import '../../../layouts/main_layout.dart'; +import '../../../themes/indicators/export_btn.dart'; +import '../../../themes/indicators/filter_btn.dart'; +import '../../../themes/indicators/search_field_theme.dart'; +import '../../../themes/indicators/text_field_theme.dart'; +import '../../../widgets/custom_action_popup.dart'; +import 'agentIncentiveFile.dart'; + +class AgentList extends StatefulWidget { + const AgentList({super.key}); + @override + AgentListState createState() => AgentListState(); +} + +class AgentListState extends State { + int currentPage = 1; + int itemsPerPage = 10; + late ApiService apiService; + + // List> dataVal = []; + List> getAgentData = []; + List> originalData = []; + List> filteredData = []; + bool isLoading = false; + @override + void initState() { + super.initState(); + apiService = ApiService(); + getAgentList(); + } + + List get _paginatedData { + final startIndex = (currentPage - 1) * itemsPerPage; + final endIndex = (currentPage * itemsPerPage).clamp(0, filteredData.length); + return filteredData.sublist(startIndex, endIndex); + } + + void filterData(String query) { + setState(() { + filteredData = getAgentData.where((item) { + final isActiveStatus = item['is_active'] == "1" ? "active" : "inactive"; + return item['name'].toLowerCase().contains(query.toLowerCase()) || + item['email'].toLowerCase().contains(query.toLowerCase()) || + item['mobile'].toLowerCase().contains(query.toLowerCase()) || + item['address'].toLowerCase().contains(query.toLowerCase()) || + item['agent_code'].toLowerCase().contains(query.toLowerCase()) || + (isActiveStatus.contains(query.toLowerCase())); + }).toList(); + }); + } + + final TextEditingController _searchController = TextEditingController(); + + Future getAgentList() async { + print('getClaimList called'); + setState(() { + isLoading = true; + }); + + try { + final response = await apiService.fetchAgentUserList(); + + if (response['status'] == 'success') { + print('getAgentListData - ${response['data']}'); + setState(() { + getAgentData = List>.from(response['data']); + // print('API Data - $getClaimPolicies'); + // + originalData = getAgentData; + filteredData = List.from(originalData); + // print('originalData - $getClaimPolicies'); + }); + } else { + // getClaimPolicies = []; + // originalData = getClaimPolicies; + // filteredData = List.from(originalData); + // print('originalData - $getClaimPolicies'); + // print('Request failed: ${response['code']}'); + } + } catch (e) { + print('Exception occurred: $e'); + } finally { + setState(() { + isLoading = false; + }); + } + } + + List _buildPopupMenuActions(BuildContext context, dynamic data) { + return [ + GestureDetector( + onTap: () { + Navigator.pop(context); + print('EDIT - ${data['id']}'); + dynamic id = data['id']; + context.go('/agent/$id'); + }, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon(Icons.edit_sharp, color: Color(0xFF319718), size: 18), + SizedBox(width: 10), + Text('Edit'), + ], + ), + ), + ]; + } + + @override + Widget build(BuildContext context) { + return MainLayout( + title: "Agent", + body: Container( + // color: Colors.yellow.shade50, + width: MediaQuery.of(context).size.width, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Container( + // height: 30, + // color: Colors.red.shade50, + width: MediaQuery.of(context).size.width, + child: GestureDetector( + onTap: () { + context.go(AppRoutes.home); + }, + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Icon( + Icons.arrow_left_sharp, + size: 35, + color: Color(0xFF425B5B), + ), + Text( + "Agent", + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.w600, + ), + ), + ], + ), + ), + ), + + SizedBox(height: 10), + Expanded( + child: Container( + // color: Colors.green, + // color: Colors.green.shade50, + width: MediaQuery.of(context).size.width, + + padding: EdgeInsets.all(8.0), + child: Column( + children: [ + Container( + // height: 40, + // color: Colors.pink, + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + ThemedSearchField( + hintText: 'Search', + backgroundColor: Color(0xFFF6F8F8), + onChanged: filterData, + controller: _searchController, + txtwidth: MediaQuery.of(context).size.width * 0.2, + ), + + Spacer(), + + GestureDetector( + onTap: () { + showUploadIncentiveModal(context); + }, + child: Container( + padding: EdgeInsets.all(8.0), + decoration: BoxDecoration( + color: Color(0xFF425B5B), + borderRadius: BorderRadius.circular(8.0), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + 'Incentive File', + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.w600, + fontSize: 14, + ), + ), + SizedBox(width: 10), + Icon( + Icons.file_open_outlined, + color: Colors.white, + ), + ], + ), + ), + ), + SizedBox(width: 10), + ExportBtn( + sheetName: "Agents", + fileName: "agent_list", + data: filteredData, + headers: [ + "id", + "name", + "agent_code", + "email", + "mobile", + "is_active", + ], + ), + + SizedBox(width: 10), + GestureDetector( + onTap: () { + print('Export'); + }, + child: Container( + padding: EdgeInsets.all(8.0), + decoration: BoxDecoration( + color: Color(0xFF425B5B), + borderRadius: BorderRadius.circular(8.0), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon(Icons.add, color: Colors.white), + + SizedBox(width: 10), + + GestureDetector( + onTap: () { + // context.go(AppRoutes.agent / create); + context.go('/agent/create'); + }, + child: Text( + 'Create New Agent', + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.w600, + fontSize: 14, + ), + ), + ), + ], + ), + ), + ), + ], + ), + ), + SizedBox(height: 10), + Expanded(child: _buildClaimsDataTable(context)), + ], + ), + ), + ), + + Container( + // height: 20, + width: MediaQuery.of(context).size.width, + // color: Colors.green.shade50, + child: PaginationControls( + currentPage: currentPage, + itemsPerPage: itemsPerPage, + // totalItems: dataVal.length, + totalItems: filteredData.length, + // activeColor: layoutColor, // your theme color + onPageChanged: (page) { + setState(() { + currentPage = page; + }); + }, + onItemsPerPageChanged: (items) { + setState(() { + itemsPerPage = items; + currentPage = 1; + }); + }, + ), + ), + ], + ), + ), + ); + } + + Widget _buildClaimsDataTable(BuildContext context) { + if (filteredData.isEmpty) { + return const SizedBox( + height: 50, + child: Center(child: Text('No available data')), + ); + } + + final sortedData = [...filteredData] + ..sort((a, b) => int.parse(b['id']) - int.parse(a['id'])); + + return ListView.builder( + // itemCount: filteredData.length + 1, // +1 for header, +1 for pagination + itemCount: sortedData.length + 1, // +1 for header, +1 for pagination + itemBuilder: (context, index) { + if (index == 0) return _buildHeader(); + // if (index == dataVal.length + 1) + // return _buildPagination(context); + final startIndex = ((currentPage - 1) * itemsPerPage); + // final item = filteredData[index - 1]; + final item = sortedData[index - 1]; + final sno = startIndex + index; + return _buildDataRow(item, sno); + }, + ); + } + + Widget _buildHeader() { + return Container( + decoration: BoxDecoration( + color: Color(0xFFEDF6F5), + borderRadius: BorderRadius.circular(6), + ), + padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16), + child: const Row( + children: [ + Expanded(flex: 1, child: Text('S.No.', style: _headerStyle)), + Expanded(flex: 2, child: Text('Agent Name', style: _headerStyle)), + Expanded(flex: 3, child: Text('Email', style: _headerStyle)), + Expanded(flex: 2, child: Text('Phone Number', style: _headerStyle)), + Expanded(flex: 1, child: Text('Id', style: _headerStyle)), + Expanded(flex: 3, child: Text('Address', style: _headerStyle)), + Expanded(flex: 1, child: Text('Action', style: _headerStyle)), + Expanded(flex: 1, child: Text('Action', style: _headerStyle)), + ], + ), + ); + } + + Widget _buildDataRow(Map item, sno) { + return Container( + padding: const EdgeInsets.symmetric(vertical: 6, horizontal: 16), + // margin: const EdgeInsets.only(top: 10), + decoration: BoxDecoration( + color: Colors.white, + // color: Color(0xFFE0F7F9), + border: const Border( + bottom: BorderSide(color: Color(0xFFEAEAEA), width: 1), + ), + borderRadius: BorderRadius.circular(8), + ), + child: Row( + children: [ + Expanded(flex: 1, child: Text('$sno' ?? '-', style: _dataBold)), + Expanded(flex: 2, child: Text(item['name'] ?? '-', style: _dataBold)), + Expanded( + flex: 3, + child: Text(item['email'] ?? '-', style: _dataBold), + ), + Expanded( + flex: 2, + child: Text(item['mobile'] ?? '-', style: _dataBold), + ), + Expanded( + flex: 1, + child: Text(item['agent_code'] ?? '-', style: _dataBold), + ), + Expanded( + flex: 3, + child: Text( + item['address'] ?? '-', + style: _dataBold, + softWrap: true, + maxLines: 3, + ), + ), + Expanded( + flex: 1, + child: Transform.scale( + scale: 0.6, // reduce size (0.7–0.9 works well) + child: Switch( + value: item['is_active'] == "1", + onChanged: (val) { + setState(() { + item['is_active'] = val ? "1" : "0"; + }); + final response = apiService.updateStatus( + item['id'], + val ? "1" : "0", + 'agent', + ); + print("Response - $response"); + }, + activeColor: Color(0xFF425B5B), + activeTrackColor: Color(0xFFB2D8D3), + inactiveThumbColor: Colors.grey.shade400, // thumb when inactive + inactiveTrackColor: Colors.grey.shade300, // track when inactive + ), + ), + ), + Expanded( + flex: 1, + child: Row( + children: [ + PopupMenuButton( + color: Colors.white, + padding: EdgeInsets.zero, + offset: Offset(0, 30), + icon: Icon( + Icons.more_vert, + color: Color(0xFF475569), + size: 14, + ), + itemBuilder: (context) => [ + CustomPopupMenuEntry( + child: Container( + padding: EdgeInsets.symmetric( + horizontal: 8, + vertical: 8, + ), + child: Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + children: _buildPopupMenuActions(context, item), + ), + ), + ), + ], + ), + ], + ), + ), + ], + ), + ); + } + + static final _dataBold = TextStyle( + fontSize: 14, + fontWeight: FontWeight.w400, + color: Color(0xFF000000), + ); + + static final _dataSub = TextStyle( + fontSize: 10, + fontWeight: FontWeight.w300, + color: Color(0xFF585757), + ); + + static const _headerStyle = TextStyle( + color: Colors.black, + fontWeight: FontWeight.bold, + ); +} diff --git a/lib/presentation/screens/UserManagement/Staff/staff.dart b/lib/presentation/screens/UserManagement/Staff/staff.dart new file mode 100644 index 0000000..c88b224 --- /dev/null +++ b/lib/presentation/screens/UserManagement/Staff/staff.dart @@ -0,0 +1,388 @@ +import 'dart:convert'; +// import 'dart:io' as html; +import 'dart:typed_data'; // Import for Uint8List +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; +import 'package:http/http.dart' as http; +import 'package:nhance_partner/core/routing/routes.dart'; +import 'package:nhance_partner/presentation/themes/indicators/text_field_theme.dart'; + +import '../../../../core/services/api_service.dart'; +import '../../../../data/utils/Pagination.dart'; +import '../../../../data/utils/validators.dart'; +import '../../../layouts/main_layout.dart'; +import '../../../themes/indicators/export_btn.dart'; +import '../../../themes/indicators/search_field_theme.dart'; +import '../../../themes/indicators/upload_doc_theme.dart'; +import 'package:universal_html/html.dart' as html; + +class Staff extends StatefulWidget { + final String? id; + const Staff({super.key, this.id}); + @override + StaffState createState() => StaffState(); +} + +class StaffState extends State { + final _formKey = GlobalKey(); + late ApiService apiService; + List tabHeader = ['name', 'email', 'mobile', 'code']; + late String isActive = "1"; + html.File? passportFile; + String? selectedFileNames; + String? passportFileUrlFromApi; + String? selectedId; + + Map controllers = {}; + + String userId = "1"; + + Map dataDetails() { + final data = { + "name": controllers["name"]?.text, + "email": controllers["email"]?.text, + "mobile": controllers["mobile"]?.text, + "emp_id": controllers["code"]?.text, + "is_active": isActive, + "role_id": "2", + "manager_id": userId, + }; + return data; + } + + @override + void initState() { + super.initState(); + apiService = ApiService(); + for (String field in tabHeader) { + controllers[field] = TextEditingController(); + } + updateData(); + } + + void updateData() async { + if (widget.id != null && widget.id != 'create') { + dynamic response = await apiService.findSingleStaffData(widget.id!); + final data = response['data']; + print("updateData - ${response['data']}"); + + if (data == null) return; + setState(() { + selectedId = widget.id; + controllers['name']?.text = data['name'] ?? ''; + controllers['email']?.text = data['email'] ?? ''; + controllers['mobile']?.text = data['mobile'] ?? ''; + controllers['code']?.text = data['emp_id'] ?? ''; + isActive = data["is_active"]; + + String? apiDocPath = data["certificate_file_name"]; + if (apiDocPath != null && apiDocPath.isNotEmpty) { + selectedFileNames = apiDocPath.split('/').last; + passportFileUrlFromApi = apiDocPath; + passportFile = null; + } else { + selectedFileNames = null; + passportFile = null; + passportFileUrlFromApi = null; + } + }); + } + } + + Future handleSave() async { + if (!_formKey.currentState!.validate()) return; + setState(() { + if (_formKey.currentState!.validate()) { + dataDetails(); + final dataSet = dataDetails(); + print("dataSetAgent - $dataSet"); + createUserData(dataSet); + } else { + // isDisable = false; + } + }); + } + + Future createUserData(data) async { + final bool isUpdating = widget.id != null && widget.id != 'create'; + final String? id = isUpdating ? widget.id : null; + final String apiUrldata; + apiUrldata = isUpdating + ? 'https://venbait.in/nhance/partner/dev/api/staff/updateStaff' + : 'https://venbait.in/nhance/partner/dev/api/staff/createStaff'; + + // final token = await getToken(); // Fetch token + dynamic token = + "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOi8vbG9jYWxob3N0L25oYW5jZV9wYXJ0bmVyX2JlLyIsImlhdCI6MTc1NzEzMDM4NCwiZXhwIjoxNzU3MTQ0Nzg0LCJzdWIiOiIxIiwiZGF0YSI6eyJpZCI6IjEiLCJuYW1lIjoiVmFkaXZlbCIsImVtYWlsIjoiZ293dGhhbWNlbGluZTQ2QGdtYWlsLmNvbSIsIm1vYmlsZSI6Ijk4NjU5ODY1OTgiLCJhZGRyZXNzIjoiMjMiLCJhZ2VudF9jb2RlIjoiMSIsImNlcnRpZmljYXRlX2ZpbGVfbmFtZSI6IjE3NTY4NzU5MjBfMTc1Njg3NTkyMF8xNmYyODY0YzkwOTdkN2JjYThiNS5wZGYiLCJlbWFpbF9vdHAiOiI2OTgxNDAiLCJmaXJlYmFzZV9kZXZpY2VfdG9rZW4iOm51bGwsImlzX2FjdGl2ZSI6IjEiLCJjcmVhdGVkX2J5IjpudWxsLCJjcmVhdGVkX29uIjoiMjAyNS0wOS0wMSAxODowMjo0NiIsInVwZGF0ZWRfYnkiOm51bGwsInVwZGF0ZWRfb24iOiIyMDI1LTA5LTA1IDE3OjMwOjI1IiwibWFuYWdlcl9pZCI6IjEifX0.Upo3LsHzOapy1kHEpwhBS7jMRj1yTKgr9B4U92mmUkA"; + if (token == null) { + throw Exception('Token not found. Please log in.'); + } + + if (isUpdating) { + data['id'] = id; // Add plan_id for update + data['updated_by'] = userId; + } else { + data['created_by'] = userId; + } + + print("data------- $data}"); + + try { + final response = await http.post( + Uri.parse(apiUrldata), + headers: { + 'Authorization': 'Bearer $token', + 'Content-Type': 'application/json', + 'app-signature': 'nhance-partner-2025-signature-35468846JRhH551HK', + }, + body: jsonEncode(data), // Convert map to JSON + ); + + if (response.statusCode == 200) { + print("Staff submitted successfully!"); + print("Response: ${response.body}"); + context.go(AppRoutes.staffLst); + } else { + print("Failed to submit plan. Status: ${response.statusCode}"); + print("Error: ${response.body}"); + + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: Text("Staff Creation Failed"), + content: Text( + "There was a problem submitting your plan. Please try again.", + ), + actions: [ + TextButton( + child: Text("OK"), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + ], + ); + }, + ); + } + } catch (e) { + print(" Error submitting Staff: $e"); + } + } + + @override + void dispose() { + for (var controller in controllers.values) { + controller.dispose(); + } + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return MainLayout( + title: "Staff", + body: Container( + // color: Colors.yellow.shade50, + width: MediaQuery.of(context).size.width, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Container( + // height: 30, + // color: Colors.red.shade50, + width: MediaQuery.of(context).size.width, + child: GestureDetector( + onTap: () { + context.go(AppRoutes.staffLst); + }, + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Icon( + Icons.arrow_left_sharp, + size: 35, + color: Color(0xFF425B5B), + ), + Text( + "Staff", + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.w600, + ), + ), + ], + ), + ), + ), + SizedBox(height: 10), + Expanded( + child: Container( + // color: Colors.green, + width: MediaQuery.of(context).size.width, + margin: EdgeInsets.all(8.0), + padding: EdgeInsets.symmetric(horizontal: 14.0, vertical: 20.0), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(8.0), + color: Color(0xFFEDF6F5), + ), + child: Column( + children: [ + Row(children: [Expanded(child: buildFormFields())]), + Spacer(), + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + GestureDetector( + onTap: () { + handleSave(); + }, + child: Container( + padding: EdgeInsets.symmetric( + horizontal: 45.0, + vertical: 8, + ), + + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(8.0), + color: Color(0xFF425B5B), + ), + child: Text( + 'Save', + style: TextStyle(color: Colors.white), + ), + ), + ), + ], + ), + SizedBox(height: 30), + ], + ), + ), + ), + + SizedBox(height: 10), + ], + ), + ), + ); + } + + Widget buildFormFields() { + return Form( + key: _formKey, + child: Column( + children: [ + Row( + children: [ + Expanded(child: buildName()), + SizedBox(width: 25), + Expanded(child: buildEmail()), + ], + ), + SizedBox(height: 20), + Row( + children: [ + Expanded(child: buildPhNumber()), + SizedBox(width: 25), + Expanded(child: buildId()), + ], + ), + SizedBox(height: 20), + + // Row( + // children: [ + // Expanded(child: buildAddress()), + // SizedBox(width: 25), + // Expanded(child: SizedBox.shrink()), + // ], + // ), + ], + ), + ); + } + + Widget buildName() { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text("Full Name *", style: _textStyle), + SizedBox(width: 10), + ThemedFormField( + controller: controllers['name']!, + validator: (value) => Validators.requiredField(value, "name"), + txtwidth: MediaQuery.of(context).size.width * 0.26, + ), + ], + ); + } + + Widget buildEmail() { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text("Email *", style: _textStyle), + SizedBox(width: 10), + ThemedFormField( + controller: controllers['email']!, + validator: (value) => Validators.email(value, "email"), + txtwidth: MediaQuery.of(context).size.width * 0.26, + ), + ], + ); + } + + Widget buildPhNumber() { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text("Phone Number *", style: _textStyle), + SizedBox(width: 10), + ThemedFormField( + controller: controllers['mobile']!, + validator: (value) => Validators.phone(value, "phNumber"), + txtwidth: MediaQuery.of(context).size.width * 0.26, + ), + ], + ); + } + + Widget buildId() { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text("Id *", style: _textStyle), + SizedBox(width: 10), + ThemedFormField( + controller: controllers['code']!, + validator: (value) => Validators.requiredField(value, "id"), + txtwidth: MediaQuery.of(context).size.width * 0.26, + ), + ], + ); + } + + Widget buildAddress() { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text("Role*", style: _textStyle), + SizedBox(width: 10), + ThemedFormField( + controller: controllers['address']!, + + txtwidth: MediaQuery.of(context).size.width * 0.26, + ), + ], + ); + } + + static const _textStyle = TextStyle( + fontSize: 14, + fontWeight: FontWeight.w600, + ); +} diff --git a/lib/presentation/screens/UserManagement/Staff/staffList.dart b/lib/presentation/screens/UserManagement/Staff/staffList.dart new file mode 100644 index 0000000..4e5eb1d --- /dev/null +++ b/lib/presentation/screens/UserManagement/Staff/staffList.dart @@ -0,0 +1,435 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; + +import '../../../../core/routing/routes.dart'; +import '../../../../core/services/api_service.dart'; +import '../../../../data/utils/Pagination.dart'; +import '../../../layouts/main_layout.dart'; +import '../../../themes/indicators/export_btn.dart'; +import '../../../themes/indicators/filter_btn.dart'; +import '../../../themes/indicators/search_field_theme.dart'; +import '../../../themes/indicators/text_field_theme.dart'; +import '../../../widgets/custom_action_popup.dart'; + +class StaffList extends StatefulWidget { + const StaffList({super.key}); + @override + StaffListState createState() => StaffListState(); +} + +class StaffListState extends State { + int currentPage = 1; + int itemsPerPage = 10; + late ApiService apiService; + + // List> dataVal = []; + List> getStaffData = []; + List> originalData = []; + List> filteredData = []; + bool isLoading = false; + + @override + void initState() { + super.initState(); + + apiService = ApiService(); + getStaffList(); + } + + List get _paginatedData { + final startIndex = (currentPage - 1) * itemsPerPage; + final endIndex = (currentPage * itemsPerPage).clamp(0, filteredData.length); + return filteredData.sublist(startIndex, endIndex); + } + + void filterData(String query) { + print("FilterDAta - $query"); + setState(() { + filteredData = getStaffData.where((item) { + final isActiveStatus = item['is_active'] == "1" ? "active" : "inactive"; + + return (item['name'] ?? '').toLowerCase().contains( + query.toLowerCase(), + ) || + (item['email'] ?? '').toLowerCase().contains(query.toLowerCase()) || + (item['mobile'] ?? '').toLowerCase().contains( + query.toLowerCase(), + ) || + (item['address'] ?? '').toLowerCase().contains( + query.toLowerCase(), + ) || + (item['emp_id'] ?? item['agent_code'] ?? '').toLowerCase().contains( + query.toLowerCase(), + ) || + isActiveStatus.contains(query.toLowerCase()); + }).toList(); + }); + } + + final TextEditingController _searchStaffController = TextEditingController(); + + Future getStaffList() async { + print('getClaimList called'); + setState(() { + isLoading = true; + }); + + try { + final response = await apiService.fetchStaffUserList(); + + if (response['status'] == 'success') { + print('getStaffListData - ${response['data']}'); + setState(() { + getStaffData = List>.from(response['data']); + originalData = getStaffData; + filteredData = List.from(originalData); + // print('originalData - $getClaimPolicies'); + }); + } else { + getStaffData = []; + originalData = []; + } + } catch (e) { + print('Exception occurred: $e'); + } finally { + setState(() { + isLoading = false; + }); + } + } + + List _buildPopupMenuActions(BuildContext context, dynamic data) { + return [ + GestureDetector( + onTap: () { + Navigator.pop(context); + print('EDITStaff - ${data['id']}'); + dynamic id = data['id']; + context.go('/staff/$id'); + }, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon(Icons.edit_sharp, color: Color(0xFF319718), size: 18), + SizedBox(width: 10), + Text('Edit'), + ], + ), + ), + ]; + } + + @override + Widget build(BuildContext context) { + return MainLayout( + title: "Staff", + body: Container( + // color: Colors.yellow.shade50, + width: MediaQuery.of(context).size.width, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Container( + // height: 30, + // color: Colors.red.shade50, + width: MediaQuery.of(context).size.width, + child: GestureDetector( + onTap: () { + context.go(AppRoutes.home); + }, + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Icon( + Icons.arrow_left_sharp, + size: 35, + color: Color(0xFF425B5B), + ), + Text( + "Staff", + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.w600, + ), + ), + ], + ), + ), + ), + + SizedBox(height: 10), + Expanded( + child: Container( + // color: Colors.green, + // color: Colors.green.shade50, + width: MediaQuery.of(context).size.width, + + padding: EdgeInsets.all(8.0), + child: Column( + children: [ + Container( + // height: 40, + // color: Colors.pink, + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + ThemedSearchField( + hintText: 'Search', + backgroundColor: Color(0xFFF6F8F8), + onChanged: filterData, + controller: _searchStaffController, + txtwidth: MediaQuery.of(context).size.width * 0.2, + ), + + Spacer(), + + ExportBtn( + sheetName: "Staff", + fileName: "staff_list", + data: filteredData, + headers: [ + "id", + "name", + "email", + "mobile", + "emp_id", + "is_active", + ], + ), + + SizedBox(width: 10), + GestureDetector( + onTap: () { + print('Export'); + }, + child: Container( + padding: EdgeInsets.all(8.0), + decoration: BoxDecoration( + color: Color(0xFF425B5B), + borderRadius: BorderRadius.circular(8.0), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon(Icons.add, color: Colors.white), + + SizedBox(width: 10), + + GestureDetector( + onTap: () { + // context.go(AppRoutes.agent / create); + context.go('/staff/create'); + }, + child: Text( + 'Create New Staff', + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.w600, + fontSize: 14, + ), + ), + ), + ], + ), + ), + ), + ], + ), + ), + SizedBox(height: 10), + Expanded(child: _buildDataTable(context)), + ], + ), + ), + ), + + Container( + // height: 20, + width: MediaQuery.of(context).size.width, + // color: Colors.green.shade50, + child: PaginationControls( + currentPage: currentPage, + itemsPerPage: itemsPerPage, + // totalItems: dataVal.length, + totalItems: filteredData.length, + // activeColor: layoutColor, // your theme color + onPageChanged: (page) { + setState(() { + currentPage = page; + }); + }, + onItemsPerPageChanged: (items) { + setState(() { + itemsPerPage = items; + currentPage = 1; + }); + }, + ), + ), + ], + ), + ), + ); + } + + Widget _buildDataTable(BuildContext context) { + if (filteredData.isEmpty) { + return const SizedBox( + height: 50, + child: Center(child: Text('No available data')), + ); + } + + final sortedData = [...filteredData] + ..sort((a, b) => int.parse(b['id']) - int.parse(a['id'])); + + return ListView.builder( + // itemCount: filteredData.length + 1, // +1 for header, +1 for pagination + itemCount: sortedData.length + 1, // +1 for header, +1 for pagination + itemBuilder: (context, index) { + if (index == 0) return _buildHeader(); + // if (index == dataVal.length + 1) + // return _buildPagination(context); + final startIndex = ((currentPage - 1) * itemsPerPage); + // final item = filteredData[index - 1]; + final item = sortedData[index - 1]; + final sno = startIndex + index; + return _buildDataRow(item, sno); + }, + ); + } + + Widget _buildHeader() { + return Container( + decoration: BoxDecoration( + color: Color(0xFFEDF6F5), + borderRadius: BorderRadius.circular(6), + ), + padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16), + child: const Row( + children: [ + Expanded(flex: 1, child: Text('S.No.', style: _headerStyle)), + Expanded(flex: 2, child: Text('Staff Name', style: _headerStyle)), + Expanded(flex: 3, child: Text('Email', style: _headerStyle)), + Expanded(flex: 2, child: Text('Phone Number', style: _headerStyle)), + Expanded(flex: 1, child: Text('Employee Id', style: _headerStyle)), + Expanded(flex: 1, child: Text('Status', style: _headerStyle)), + Expanded(flex: 1, child: Text('Action', style: _headerStyle)), + ], + ), + ); + } + + Widget _buildDataRow(Map item, sno) { + return Container( + padding: const EdgeInsets.symmetric(vertical: 6, horizontal: 16), + // margin: const EdgeInsets.only(top: 10), + decoration: BoxDecoration( + color: Colors.white, + // color: Color(0xFFE0F7F9), + border: const Border( + bottom: BorderSide(color: Color(0xFFEAEAEA), width: 1), + ), + borderRadius: BorderRadius.circular(8), + ), + child: Row( + children: [ + Expanded(flex: 1, child: Text('$sno' ?? '-', style: _dataBold)), + Expanded(flex: 2, child: Text(item['name'] ?? '-', style: _dataBold)), + Expanded( + flex: 3, + child: Text(item['email'] ?? '-', style: _dataBold), + ), + Expanded( + flex: 2, + child: Text(item['mobile'] ?? '-', style: _dataBold), + ), + Expanded( + flex: 1, + child: Text(item['emp_id'] ?? '-', style: _dataBold), + ), + + Expanded( + flex: 1, + child: Transform.scale( + scale: 0.6, // reduce size (0.7–0.9 works well) + child: Switch( + value: item['is_active'] == "1", + onChanged: (val) { + setState(() { + item['is_active'] = val ? "1" : "0"; + }); + final response = apiService.updateStatus( + item['id'], + val ? "1" : "0", + 'staff', + ); + print("Response - $response"); + }, + activeColor: Color(0xFF425B5B), // thumb when active + activeTrackColor: Color(0xFFB2D8D3), // track when active + inactiveThumbColor: Colors.grey.shade400, // thumb when inactive + inactiveTrackColor: Colors.grey.shade300, // track when inactive + ), + ), + ), + + Expanded( + flex: 1, + child: Row( + children: [ + PopupMenuButton( + color: Colors.white, + padding: EdgeInsets.zero, + offset: Offset(0, 30), + icon: Icon( + Icons.more_vert, + color: Color(0xFF475569), + size: 14, + ), + itemBuilder: (context) => [ + CustomPopupMenuEntry( + child: Container( + padding: EdgeInsets.symmetric( + horizontal: 8, + vertical: 8, + ), + child: Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + children: _buildPopupMenuActions(context, item), + ), + ), + ), + ], + ), + ], + ), + ), + ], + ), + ); + } + + static final _dataBold = TextStyle( + fontSize: 14, + + fontWeight: FontWeight.w400, + color: Color(0xFF000000), + ); + + static final _dataSub = TextStyle( + fontSize: 10, + fontWeight: FontWeight.w300, + color: Color(0xFF585757), + ); + + static const _headerStyle = TextStyle( + color: Colors.black, + fontWeight: FontWeight.bold, + ); +} diff --git a/lib/presentation/screens/dashboard/dashboard.dart b/lib/presentation/screens/dashboard/dashboard.dart index 47d88ac..e12c329 100644 --- a/lib/presentation/screens/dashboard/dashboard.dart +++ b/lib/presentation/screens/dashboard/dashboard.dart @@ -1,19 +1,23 @@ import 'package:flutter/material.dart'; import '../../layouts/main_layout.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; -class DashboardScreen extends StatefulWidget { +import '../../providers/manager_provider.dart'; + +class DashboardScreen extends ConsumerStatefulWidget { const DashboardScreen({super.key}); @override - State createState() => _DashboardScreenState(); + ConsumerState createState() => _DashboardScreenState(); } -class _DashboardScreenState extends State { +class _DashboardScreenState extends ConsumerState { @override Widget build(BuildContext context) { + final mangerId = ref.watch(managerIdProvider); return MainLayout( title: "Home", - body: const Center(child: Text("Dashboard Page Content")), + body: Center(child: Text("Dashboard Page Content $mangerId")), ); } } diff --git a/lib/presentation/screens/login/login_screen.dart b/lib/presentation/screens/login/login_screen.dart index e1c128a..f57868e 100644 --- a/lib/presentation/screens/login/login_screen.dart +++ b/lib/presentation/screens/login/login_screen.dart @@ -7,6 +7,7 @@ import 'package:flutter/services.dart'; import 'package:go_router/go_router.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:http/http.dart' as http; +import 'package:jwt_decode/jwt_decode.dart'; import 'package:nhance_partner/core/config/env.dart'; import 'package:nhance_partner/core/services/platform_helper.dart'; import 'package:nhance_partner/data/utils/validators.dart'; @@ -14,18 +15,23 @@ import 'package:pinput/pinput.dart'; import 'package:shared_preferences/shared_preferences.dart'; import '../../../core/routing/routes.dart'; import '../../../data/services/auth_service.dart'; +import '../../../data/utils/commonLoaderButton.dart'; import '../../../data/utils/toastNotification.dart'; import '../../layouts/responsive_layout.dart'; +import '../../providers/manager_provider.dart'; +import '../../providers/userRoleProvider.dart'; import '../../themes/indicators/text_field_theme.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; -class LoginScreen extends StatefulWidget { + +class LoginScreen extends ConsumerStatefulWidget { const LoginScreen({super.key}); @override - State createState() => _LoginScreenState(); + ConsumerState createState() => _LoginScreenState(); } -class _LoginScreenState extends State { +class _LoginScreenState extends ConsumerState { final _formKey = GlobalKey(); final TextEditingController _emailController = TextEditingController(); final TextEditingController _PhoneNumberController = TextEditingController(); @@ -42,7 +48,6 @@ class _LoginScreenState extends State { dynamic clickedButtonName; dynamic token; - // Future _handleLogin() async { // if (!_formKey.currentState!.validate()) return; // @@ -100,6 +105,50 @@ class _LoginScreenState extends State { _countryController.text = "+91"; } + Future _saveUserRole(String token) async { + final decodedToken = Jwt.parseJwt(token); + final Map? data = decodedToken['data']; + + String role; + + if (data == null || !data.containsKey('role_id')) { + role = "agent"; + } else { + final dynamic roleId = data['role_id']; + + if (roleId.toString() == "1") { + role = "manager"; + } else if (roleId.toString() == "2") { + role = "staff"; + } else { + role = "agent"; // fallback if unexpected value + } + } + + // Save globally + ref.read(userRoleProvider.notifier).state = role; + print("✅ User role set globally: $role"); + // final userRole = ref.watch(userRoleProvider); + } + + Future _saveManagerId(String token) async { + Map? decodedToken = await Jwt.parseJwt(token); + print('decodedToken : $decodedToken'); + final dynamic managerIdRaw = decodedToken['data']?['manager_id']; + final int? managerId = managerIdRaw is int + ? managerIdRaw + : int.tryParse(managerIdRaw.toString()); + + if (managerId != null) { + ref.read(managerIdProvider.notifier).state = managerId; + print("Manager ID saved globally: $managerId"); + } else { + print("Manager ID is null or invalid"); + } + print(managerId); + // final managerId = ref.watch(managerIdProvider); + } + Future sendMobileOrEmailVerify(loginUser) async { try { if (_formKey.currentState!.validate()) { @@ -145,7 +194,7 @@ class _LoginScreenState extends State { } String message = data['data']['message']; if (verificationAgentOrStaff) { - if(switcherStatus == 1){ + if (switcherStatus == 1) { _verifyPhoneNumber(); } else { setState(() { @@ -187,8 +236,11 @@ class _LoginScreenState extends State { // Build payload depending on switcher status final Map payload = (switcherStatus == 1) - ? {'otp_verification': otpVerifyStatus,'mobile': _PhoneNumberController.text.trim()} - : {'otp': otpVerifyStatus,'email': _emailController.text.trim()}; + ? { + 'otp_verification': otpVerifyStatus, + 'mobile': _PhoneNumberController.text.trim(), + } + : {'otp': otpVerifyStatus, 'email': _emailController.text.trim()}; // Send request final response = await http.post( @@ -213,20 +265,32 @@ class _LoginScreenState extends State { if (status == 'success') { ToastHelper.showSuccessToast(context, 'OTP verified successfully'); await AuthService.saveToken(token); - final dynamic tokenData = AuthService.getToken(); - print('token - $tokenData'); + await _saveUserRole(token); + await _saveManagerId(token); + final managerId = ref.watch(managerIdProvider); + print('managerId -- $managerId'); + final userRole = ref.watch(userRoleProvider); + print('userRole -- $userRole'); context.go(AppRoutes.dashboard); } else { - ToastHelper.showWarningToast(context, data['message'] ?? 'Invalid OTP'); + ToastHelper.showWarningToast( + context, + data['message'] ?? 'Invalid OTP', + ); } } else { ToastHelper.showErrorToast( - context, 'Server error: ${response.statusCode}'); + context, + 'Server error: ${response.statusCode}', + ); } } catch (e, stacktrace) { print('Error verifying OTP: $e'); print(stacktrace); - ToastHelper.showErrorToast(context, 'Something went wrong. Please try again.'); + ToastHelper.showErrorToast( + context, + 'Something went wrong. Please try again.', + ); } } @@ -246,8 +310,9 @@ class _LoginScreenState extends State { print('credential $credential'); - final userCredential = - await FirebaseAuth.instance.signInWithCredential(credential); + final userCredential = await FirebaseAuth.instance.signInWithCredential( + credential, + ); // await FirebaseAuth.instance.signInWithCredential(credential); print('otp firebase check'); @@ -279,7 +344,9 @@ class _LoginScreenState extends State { ), ); ToastHelper.showErrorToast( - context, 'OTP expired. Please request a new one.'); + context, + 'OTP expired. Please request a new one.', + ); } else if (e.code == 'invalid-verification-code') { ScaffoldMessenger.of(context).showSnackBar( SnackBar( @@ -288,7 +355,9 @@ class _LoginScreenState extends State { ), ); ToastHelper.showErrorToast( - context, 'Invalid OTP entered. Please try again.'); + context, + 'Invalid OTP entered. Please try again.', + ); } else if (e.code == 'session-expired') { ScaffoldMessenger.of(context).showSnackBar( SnackBar( @@ -297,7 +366,9 @@ class _LoginScreenState extends State { ), ); ToastHelper.showErrorToast( - context, 'Session expired. Try restarting the verification.'); + context, + 'Session expired. Try restarting the verification.', + ); } else { ScaffoldMessenger.of(context).showSnackBar( SnackBar( @@ -313,7 +384,9 @@ class _LoginScreenState extends State { // }); print('Error: $e'); ToastHelper.showErrorToast( - context, 'Failed to verify OTP. Please try again.'); + context, + 'Failed to verify OTP. Please try again.', + ); } } @@ -344,10 +417,7 @@ class _LoginScreenState extends State { errorMessage = 'Verification Failed: ${e.message}'; } ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(errorMessage), - duration: Duration(seconds: 2), - ), + SnackBar(content: Text(errorMessage), duration: Duration(seconds: 2)), ); ToastHelper.showSuccessToast(context, errorMessage); @@ -369,13 +439,15 @@ class _LoginScreenState extends State { enteredEmailOrMobile = true; }); ToastHelper.showSuccessToast( - context, 'Verification code sent to ${enteredMobileNumber}'); + context, + 'Verification code sent to ${enteredMobileNumber}', + ); // setState(() { // _isLoading = false; // }); }, - codeAutoRetrievalTimeout: (String verificationId) async{ + codeAutoRetrievalTimeout: (String verificationId) async { setState(() { _verificationId = verificationId; }); @@ -402,7 +474,7 @@ class _LoginScreenState extends State { print('The provided phone number is not valid.'); } }, - codeSent: (String verificationId, int? resendToken) async{ + codeSent: (String verificationId, int? resendToken) async { final prefs = await SharedPreferences.getInstance(); await prefs.setString('verificationId', verificationId); setState(() { @@ -410,9 +482,11 @@ class _LoginScreenState extends State { _resendToken = resendToken; }); ToastHelper.showSuccessToast( - context, 'Verification code resent to ${mobileNumber}'); + context, + 'Verification code resent to ${mobileNumber}', + ); }, - codeAutoRetrievalTimeout: (String verificationId) async{ + codeAutoRetrievalTimeout: (String verificationId) async { final prefs = await SharedPreferences.getInstance(); await prefs.setString('verificationId', verificationId); setState(() { @@ -590,7 +664,9 @@ class _LoginScreenState extends State { const SizedBox(height: 20), Image.asset( "assets/login/login-content.png", - height: MediaQuery.of(context).size.height * 0.5, // 40% of screen height + height: + MediaQuery.of(context).size.height * + 0.5, // 40% of screen height fit: BoxFit.contain, ), const SizedBox(height: 10), @@ -811,31 +887,43 @@ class _LoginScreenState extends State { child: SizedBox( width: double.infinity, height: 50, - child: ElevatedButton( - style: ElevatedButton.styleFrom( - backgroundColor: Color(0xFF436462), - padding: const EdgeInsets.symmetric(vertical: 16), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - ), - ), - onPressed: () { + child: CommonButton( + text: "Login as Agent", + backgroundColor: Color(0xFF436462), + onPressed: () async { + sendMobileOrEmailVerify('agent'); setState(() { clickedButtonName = 'agent'; }); + await Future.delayed(const Duration(seconds: 5)); }, - child: Text( - 'Login as Agent', - style: TextStyle( - fontSize: ResponsiveLayout.isMobile(context) - ? 18 - : 20, - fontWeight: FontWeight.w600, - color: Colors.white, - ), - ), ), + // ElevatedButton( + // style: ElevatedButton.styleFrom( + // backgroundColor: Color(0xFF436462), + // padding: const EdgeInsets.symmetric(vertical: 16), + // shape: RoundedRectangleBorder( + // borderRadius: BorderRadius.circular(8), + // ), + // ), + // onPressed: () { + // sendMobileOrEmailVerify('agent'); + // setState(() { + // clickedButtonName = 'agent'; + // }); + // }, + // child: Text( + // 'Login as Agent', + // style: TextStyle( + // fontSize: ResponsiveLayout.isMobile(context) + // ? 18 + // : 20, + // fontWeight: FontWeight.w600, + // color: Colors.white, + // ), + // ), + // ), ), ), @@ -858,31 +946,43 @@ class _LoginScreenState extends State { child: SizedBox( width: double.infinity, height: 50, - child: ElevatedButton( - style: ElevatedButton.styleFrom( - backgroundColor: Color(0xFF436462), - padding: const EdgeInsets.symmetric(vertical: 16), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - ), - ), - onPressed: () { + child: CommonButton( + text: "Login as Staff", + backgroundColor: Color(0xFF436462), + onPressed: () async { + sendMobileOrEmailVerify('staff'); setState(() { clickedButtonName = 'staff'; }); + await Future.delayed(const Duration(seconds: 5)); }, - child: Text( - 'Login as Staff', - style: TextStyle( - fontSize: ResponsiveLayout.isMobile(context) - ? 18 - : 20, - fontWeight: FontWeight.w600, - color: Colors.white, - ), - ), ), + // ElevatedButton( + // style: ElevatedButton.styleFrom( + // backgroundColor: Color(0xFF436462), + // padding: const EdgeInsets.symmetric(vertical: 16), + // shape: RoundedRectangleBorder( + // borderRadius: BorderRadius.circular(8), + // ), + // ), + // onPressed: () { + // sendMobileOrEmailVerify('staff'); + // setState(() { + // clickedButtonName = 'staff'; + // }); + // }, + // child: Text( + // 'Login as Staff', + // style: TextStyle( + // fontSize: ResponsiveLayout.isMobile(context) + // ? 18 + // : 20, + // fontWeight: FontWeight.w600, + // color: Colors.white, + // ), + // ), + // ), ), ), ], @@ -973,94 +1073,124 @@ class _LoginScreenState extends State { // ), // ], AnimatedSwitcher( - duration: const Duration(milliseconds: 1000), // animation speed + duration: const Duration( + milliseconds: 1000, + ), // animation speed switchInCurve: Curves.easeInOutCirc, switchOutCurve: Curves.easeOutCirc, child: enteredEmailOrMobile ? Column( - key: const ValueKey('otp_block'), - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Container( - child: Text( - 'OTP', - style: TextStyle( - fontSize: ResponsiveLayout.isMobile(context) ? 14 : 18, - fontWeight: FontWeight.w600, - color: Colors.white, - ), - ), - ), - const SizedBox(height: 10), - Container( - child: Pinput( - length: 6, - defaultPinTheme: defaultPinTheme, - separatorBuilder: (index) => const SizedBox(width: 8), - showCursor: true, - controller: _otpController, - errorPinTheme: defaultPinTheme.copyBorderWith( - border: Border.all(color: Colors.redAccent), - ), - hapticFeedbackType: HapticFeedbackType.lightImpact, - onCompleted: (pin) => debugPrint('onCompleted: $pin'), - onChanged: (value) => debugPrint('onChanged: $value'), - cursor: Column( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - Container( - margin: const EdgeInsets.only(bottom: 9), - width: 22, - height: 1, - color: focusedBorderColor, + key: const ValueKey('otp_block'), + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + child: Text( + 'OTP', + style: TextStyle( + fontSize: ResponsiveLayout.isMobile(context) + ? 14 + : 18, + fontWeight: FontWeight.w600, + color: Colors.white, + ), ), - ], - ), - focusedPinTheme: defaultPinTheme.copyWith( - decoration: defaultPinTheme.decoration!.copyWith( - borderRadius: BorderRadius.circular(8), - border: Border.all(color: focusedBorderColor), ), - ), - submittedPinTheme: defaultPinTheme.copyWith( - decoration: defaultPinTheme.decoration!.copyWith( - color: fillColor, - borderRadius: BorderRadius.circular(19), - border: Border.all(color: focusedBorderColor), + const SizedBox(height: 10), + Container( + child: Pinput( + length: 6, + defaultPinTheme: defaultPinTheme, + separatorBuilder: (index) => + const SizedBox(width: 8), + showCursor: true, + controller: _otpController, + errorPinTheme: defaultPinTheme.copyBorderWith( + border: Border.all(color: Colors.redAccent), + ), + hapticFeedbackType: + HapticFeedbackType.lightImpact, + onCompleted: (pin) => + debugPrint('onCompleted: $pin'), + onChanged: (value) => + debugPrint('onChanged: $value'), + cursor: Column( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Container( + margin: const EdgeInsets.only(bottom: 9), + width: 22, + height: 1, + color: focusedBorderColor, + ), + ], + ), + focusedPinTheme: defaultPinTheme.copyWith( + decoration: defaultPinTheme.decoration! + .copyWith( + borderRadius: BorderRadius.circular(8), + border: Border.all( + color: focusedBorderColor, + ), + ), + ), + submittedPinTheme: defaultPinTheme.copyWith( + decoration: defaultPinTheme.decoration! + .copyWith( + color: fillColor, + borderRadius: BorderRadius.circular(19), + border: Border.all( + color: focusedBorderColor, + ), + ), + ), + ), ), - ), - ), - ), - const SizedBox(height: 20), - SizedBox( - width: 400, - child: ElevatedButton( - style: ElevatedButton.styleFrom( - backgroundColor: const Color(0xFF436462), - padding: const EdgeInsets.symmetric(vertical: 16), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), + const SizedBox(height: 20), + SizedBox( + width: 400, + child: CommonButton( + text: "Continue", + backgroundColor: Color(0xFF436462), + onPressed: () async { + if (switcherStatus == 1) { + verifyFirebaseMobileOTP(); + } else { + verifyMobileOrMailOTP(_otpController.text); + } + await Future.delayed(const Duration(seconds: 5)); + }, + ), + // ElevatedButton( + // style: ElevatedButton.styleFrom( + // backgroundColor: const Color(0xFF436462), + // padding: const EdgeInsets.symmetric( + // vertical: 16, + // ), + // shape: RoundedRectangleBorder( + // borderRadius: BorderRadius.circular(8), + // ), + // ), + // onPressed: () { + // if (switcherStatus == 1) { + // verifyFirebaseMobileOTP(); + // } else { + // verifyMobileOrMailOTP(_otpController.text); + // } + // }, + // child: Text( + // 'Continue', + // style: TextStyle( + // fontSize: ResponsiveLayout.isMobile(context) + // ? 18 + // : 20, + // fontWeight: FontWeight.w600, + // color: Colors.white, + // ), + // ), + // ), ), - ), - onPressed: () { - if(switcherStatus == 1){ - verifyFirebaseMobileOTP(); - } else{ - verifyMobileOrMailOTP(_otpController.text); - } - }, - child: Text( - 'Continue', - style: TextStyle( - fontSize: ResponsiveLayout.isMobile(context) ? 18 : 20, - fontWeight: FontWeight.w600, - color: Colors.white, - ), - ), - ), - ), - ], - ) + ], + ) : const SizedBox.shrink(), // Empty widget when false ), SizedBox(height: 20), diff --git a/lib/presentation/themes/indicators/date_field_theme.dart b/lib/presentation/themes/indicators/date_field_theme.dart new file mode 100644 index 0000000..a9b219e --- /dev/null +++ b/lib/presentation/themes/indicators/date_field_theme.dart @@ -0,0 +1,113 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; +import 'package:month_picker_dialog/month_picker_dialog.dart'; + +class ThemedDateField extends StatefulWidget { + const ThemedDateField({ + super.key, + this.hintText, + this.txtwidth, + this.txtheight, + this.backgroundColor, + this.borderColor, + this.errorBorderColor, + this.onDateSelected, + this.controller, + this.validator, // ✅ new + }); + + final String? hintText; + final double? txtwidth; + final double? txtheight; + final Color? backgroundColor; + final Color? borderColor; + final Color? errorBorderColor; + final TextEditingController? controller; + final Function(DateTime)? onDateSelected; + final String? Function(String? value)? validator; // ✅ new + + @override + State createState() => _ThemedDateFieldState(); +} + +class _ThemedDateFieldState extends State { + DateTime? selectedDate; + + Future pickDate( + BuildContext context, + FormFieldState field, + ) async { + final pickedDate = await showDatePicker( + context: context, + initialDate: selectedDate ?? DateTime.now(), + firstDate: DateTime(2000), + lastDate: DateTime(2100), + initialEntryMode: DatePickerEntryMode.calendarOnly, + ); + + if (pickedDate != null) { + setState(() => selectedDate = pickedDate); + + final formattedDate = DateFormat('dd-MM-yyyy').format(pickedDate); + widget.controller?.text = formattedDate; + widget.onDateSelected?.call(pickedDate); + + field.didChange(formattedDate); // ✅ notify form validation + } + } + + @override + Widget build(BuildContext context) { + return FormField( + validator: widget.validator, + initialValue: widget.controller?.text, + builder: (field) { + final hasError = field.hasError; + return Container( + width: widget.txtwidth ?? MediaQuery.of(context).size.width, + height: widget.txtheight, + child: InkWell( + onTap: () => pickDate(context, field), + borderRadius: BorderRadius.circular(10), + child: Container( + padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 15), + decoration: BoxDecoration( + color: widget.backgroundColor ?? Colors.white, + borderRadius: BorderRadius.circular(10), + border: Border.all( + color: hasError + ? (widget.errorBorderColor ?? Colors.red) + : (widget.borderColor ?? Colors.grey.shade50), + width: 1.5, + ), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Text( + widget.controller?.text.isNotEmpty == true + ? widget.controller!.text + : widget.hintText ?? "Select Date", + style: TextStyle( + fontSize: 12, + color: hasError ? Colors.red : Colors.black, + ), + overflow: TextOverflow.ellipsis, + ), + ), + const Icon( + Icons.calendar_today, + size: 16, + color: Colors.black, + ), + ], + ), + ), + ), + ); + }, + ); + } +} diff --git a/lib/presentation/themes/indicators/export_btn.dart b/lib/presentation/themes/indicators/export_btn.dart new file mode 100644 index 0000000..5b24cb3 --- /dev/null +++ b/lib/presentation/themes/indicators/export_btn.dart @@ -0,0 +1,123 @@ +import 'dart:io'; +import 'package:excel/excel.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:open_file/open_file.dart'; +import 'package:path_provider/path_provider.dart'; + +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:universal_html/html.dart' as html; + +import 'dart:typed_data'; +import 'package:flutter/foundation.dart' show kIsWeb; + +import 'dart:io' show File; // Only used in mobile/desktop +import 'package:path_provider/path_provider.dart'; +import 'package:open_file/open_file.dart'; + +class ExportBtn extends HookWidget { + final String sheetName; + final String fileName; + final List> data; + final List headers; // dynamic headers + + const ExportBtn({ + super.key, + required this.sheetName, + required this.fileName, + required this.data, + required this.headers, + }); + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: () { + ExcelExporter.exportToExcel( + sheetName: sheetName, + data: data, + headers: headers, + fileName: fileName, + ); + }, + child: Container( + padding: const EdgeInsets.all(8.0), + decoration: BoxDecoration( + color: const Color(0xFF425B5B), + borderRadius: BorderRadius.circular(8.0), + ), + child: const Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + 'Export', + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.w600, + fontSize: 14, + ), + ), + SizedBox(width: 20), + Icon(Icons.input_sharp, color: Colors.white), + ], + ), + ), + ); + } +} + +class ExcelExporter { + static Future exportToExcel({ + required String sheetName, + required List> data, + required List headers, + required String fileName, + }) async { + try { + var excel = Excel.createExcel(); + Sheet sheetObject = excel[sheetName]; + + // Add headers + sheetObject.appendRow(headers.map((h) => TextCellValue(h)).toList()); + + // Add data + for (int i = 0; i < data.length; i++) { + var row = []; + for (var key in headers) { + final value = data[i][key]; + if (value == null) { + row.add(TextCellValue('-')); + } else if (value is int) { + row.add(IntCellValue(value)); + } else if (value is double) { + row.add(DoubleCellValue(value)); + } else { + row.add(TextCellValue(value.toString())); + } + } + sheetObject.appendRow(row); + } + + var bytes = excel.encode()!; + + if (kIsWeb) { + // ✅ Web: download as file + final blob = html.Blob([bytes]); + final url = html.Url.createObjectUrlFromBlob(blob); + final anchor = html.AnchorElement(href: url) + ..setAttribute("download", "$fileName.xlsx") + ..click(); + html.Url.revokeObjectUrl(url); + } else { + // ✅ Mobile/Desktop: save to file system + final dir = await getApplicationDocumentsDirectory(); + final file = File('${dir.path}/$fileName.xlsx'); + await file.writeAsBytes(bytes); + await OpenFile.open(file.path); + } + } catch (e) { + print("Error exporting Excel: $e"); + } + } +} diff --git a/lib/presentation/themes/indicators/filter_btn.dart b/lib/presentation/themes/indicators/filter_btn.dart new file mode 100644 index 0000000..934054d --- /dev/null +++ b/lib/presentation/themes/indicators/filter_btn.dart @@ -0,0 +1,75 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/src/widgets/framework.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:http/http.dart' as http; + +class FilterBtn extends HookWidget { + @override + Widget build(BuildContext context) { + // TODO: implement build + + // Future generateExport() async { + // // API endpoint to download the Excel file + // + // // Send GET request to the API + // var url = Uri.parse(Environment.apiUrl + + // 'exportDataByClientPolicyId?client_id=$clintID&client_policy_id=62'); + // + // var response = await http.get( + // url, + // headers: { + // 'Authorization': 'Bearer $_token', + // }, + // ); + // + // if (response.statusCode == 200) { + // // Create a blob from the response body + // final blob = html.Blob([response.bodyBytes]); + // + // // Generate a download URL for the blob + // final url = html.Url.createObjectUrlFromBlob(blob); + // + // // Create a link element to trigger the download + // final anchor = html.AnchorElement(href: url) + // ..setAttribute('download', 'excel_file.xlsx') + // ..click(); + // + // // Revoke the download URL to free up resources + // html.Url.revokeObjectUrl(url); + // } else { + // // Handle error + // print('Failed to download Excel file: ${response.statusCode}'); + // } + // } + + return GestureDetector( + onTap: () { + print('Filter'); + // generateExport(); + }, + child: Container( + padding: EdgeInsets.symmetric(horizontal: 8.0, vertical: 11.5), + decoration: BoxDecoration( + color: Color(0xFFF6F8F8), + borderRadius: BorderRadius.circular(8.0), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + 'Filter', + style: TextStyle( + color: Color(0xFF686868), + fontWeight: FontWeight.w500, + fontSize: 16, + ), + ), + SizedBox(width: 20), + Icon(Icons.filter_list_sharp, color: Color(0xFF686868)), + ], + ), + ), + ); + } +} diff --git a/lib/presentation/themes/indicators/input_field_decoration.dart b/lib/presentation/themes/indicators/input_field_decoration.dart new file mode 100644 index 0000000..0e17127 --- /dev/null +++ b/lib/presentation/themes/indicators/input_field_decoration.dart @@ -0,0 +1,27 @@ +import 'package:flutter/material.dart'; + +class AppInputDecorations { + static InputDecoration dropdownDecoration({String? label, String? hint}) { + return InputDecoration( + labelText: label, + labelStyle: TextStyle(fontSize: 12), + hintText: hint, + filled: true, + fillColor: const Color(0xFFECECEC), + contentPadding: const EdgeInsets.symmetric(vertical: 14, horizontal: 15), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: const BorderSide(color: Color(0xFFFFFFFF)), + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: const BorderSide(color: Color(0xFFFFFFFF)), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: const BorderSide(color: Color(0xFF50A398), width: 2), + ), + floatingLabelBehavior: FloatingLabelBehavior.never, + ); + } +} diff --git a/lib/presentation/themes/indicators/month_field_theme.dart b/lib/presentation/themes/indicators/month_field_theme.dart new file mode 100644 index 0000000..79f21be --- /dev/null +++ b/lib/presentation/themes/indicators/month_field_theme.dart @@ -0,0 +1,112 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; +import 'package:month_picker_dialog/month_picker_dialog.dart'; + +class ThemedMonthField extends StatefulWidget { + const ThemedMonthField({ + super.key, + this.hintText, + this.txtwidth, + this.txtheight, + this.backgroundColor, + this.borderColor, + this.errorBorderColor, + this.onDateSelected, + this.controller, + this.validator, // ✅ new + }); + + final String? hintText; + final double? txtwidth; + final double? txtheight; + final Color? backgroundColor; + final Color? borderColor; + final Color? errorBorderColor; + final TextEditingController? controller; + final Function(DateTime)? onDateSelected; + final String? Function(String? value)? validator; // ✅ new + + @override + State createState() => _ThemedMonthFieldState(); +} + +class _ThemedMonthFieldState extends State { + DateTime? selectedDate; + + Future pickDate( + BuildContext context, + FormFieldState field, + ) async { + final pickedDate = await showMonthPicker( + context: context, + initialDate: selectedDate ?? DateTime.now(), + firstDate: DateTime(2000), + lastDate: DateTime(2100), + ); + + if (pickedDate != null) { + setState(() => selectedDate = pickedDate); + + final formattedDate = DateFormat('MMM yyyy').format(pickedDate); + widget.controller?.text = formattedDate; + widget.onDateSelected?.call(pickedDate); + + field.didChange(formattedDate); // ✅ notify form validation + } + } + + @override + Widget build(BuildContext context) { + return FormField( + validator: widget.validator, + initialValue: widget.controller?.text, + builder: (field) { + final hasError = field.hasError; + return Container( + width: widget.txtwidth ?? MediaQuery.of(context).size.width, + height: widget.txtheight, + child: InkWell( + onTap: () => pickDate(context, field), + borderRadius: BorderRadius.circular(10), + child: Container( + padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 15), + decoration: BoxDecoration( + color: widget.backgroundColor ?? Colors.white, + borderRadius: BorderRadius.circular(10), + border: Border.all( + color: hasError + ? (widget.errorBorderColor ?? Colors.red) + : (widget.borderColor ?? Colors.grey.shade50), + width: 1.5, + ), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Text( + widget.controller?.text.isNotEmpty == true + ? widget.controller!.text + : widget.hintText ?? "Select Date", + style: TextStyle( + fontSize: 12, + color: hasError ? Colors.red : Colors.black, + ), + overflow: TextOverflow.ellipsis, + ), + ), + const Icon( + Icons.calendar_today, + size: 16, + color: Colors.black, + ), + ], + ), + ), + ), + ); + }, + ); + } +} diff --git a/lib/presentation/themes/indicators/search_field_theme.dart b/lib/presentation/themes/indicators/search_field_theme.dart new file mode 100644 index 0000000..829a1a9 --- /dev/null +++ b/lib/presentation/themes/indicators/search_field_theme.dart @@ -0,0 +1,97 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; + +// import 'package:uae_stat/config/my_theme.dart'; + +class ThemedSearchField extends HookWidget { + const ThemedSearchField({ + super.key, + required this.hintText, + this.imgPath, + this.txtwidth, + this.txtHeight, + required this.controller, + this.validator, + this.isObscurable = false, + this.hintColor, + this.backgroundColor, + this.borderColor, + this.errorBorderColor, + this.errorTextColor, + this.readOnly = false, // ✅ new + this.keyboardType, // ✅ new + this.maxLength, // ✅ new + this.inputFormatters, + this.onChanged, + }); + + final String hintText; + final String? imgPath; + final String? Function(String? text)? validator; + final TextEditingController controller; + final void Function(String)? onChanged; + final bool isObscurable; + final Color? hintColor; + final Color? backgroundColor; + final Color? borderColor; + final Color? errorBorderColor; + final Color? errorTextColor; + final double? txtwidth; + final double? txtHeight; + final bool readOnly; + final TextInputType? keyboardType; + final int? maxLength; + final List? inputFormatters; + + @override + Widget build(BuildContext context) { + final isObscured = useState(true); + final boxDecoration = BoxDecoration(borderRadius: BorderRadius.circular(2)); + + final inputDecoration = InputDecoration( + filled: true, + fillColor: backgroundColor ?? Colors.white, + // contentPadding: const EdgeInsets.symmetric(vertical: 12, horizontal: 15), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide(color: Color(0xFFF6F8F8)), + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide(color: Color(0xFFF6F8F8)), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide(color: Color(0xFF50A398), width: 2), + ), + hintText: hintText, + hintStyle: TextStyle( + color: const Color(0xFF686868), + fontSize: (txtHeight ?? 45) <= 45 ? 14 : 16, + ), + // suffixIconConstraints: const BoxConstraints( + // maxWidth: 25 + 16 + 10, + // maxHeight: 25 + (8 * 2), + // ), + suffixIcon: Padding( + padding: const EdgeInsetsDirectional.only(start: 16, end: 10), + child: Icon(Icons.search, color: const Color(0xFF686868), size: 20), + ), + ); + + return Container( + width: txtwidth ?? MediaQuery.of(context).size.width, + height: txtHeight ?? 45, + child: DecoratedBox( + decoration: boxDecoration, + child: TextFormField( + controller: controller, + onChanged: onChanged, + decoration: inputDecoration, + validator: validator, + ), + ), + ); + } +} diff --git a/lib/presentation/themes/indicators/text_field_theme.dart b/lib/presentation/themes/indicators/text_field_theme.dart index 3f9100e..627ca76 100644 --- a/lib/presentation/themes/indicators/text_field_theme.dart +++ b/lib/presentation/themes/indicators/text_field_theme.dart @@ -7,9 +7,10 @@ import 'package:flutter_hooks/flutter_hooks.dart'; class ThemedFormField extends HookWidget { const ThemedFormField({ super.key, - required this.hintText, + this.hintText, this.imgPath, this.txtwidth, + this.txtheight, required this.controller, this.validator, this.isObscurable = false, @@ -24,7 +25,7 @@ class ThemedFormField extends HookWidget { this.inputFormatters, }); - final String hintText; + final String? hintText; final String? imgPath; final String? Function(String? text)? validator; final TextEditingController controller; @@ -38,6 +39,7 @@ class ThemedFormField extends HookWidget { final bool readOnly; final TextInputType? keyboardType; final int? maxLength; + final double? txtheight; final List? inputFormatters; @override @@ -45,6 +47,14 @@ class ThemedFormField extends HookWidget { final isObscured = useState(true); final boxDecoration = BoxDecoration( borderRadius: BorderRadius.circular(10), + // boxShadow: [ + // BoxShadow( + // color: Colors.black26, // shadow color + // offset: Offset(0, 2), // x=0, y=4 → shadow goes downward + // blurRadius: 2, // soften the shadow + // spreadRadius: 0.2, // expand shadow + // ), + // ], ); final obscureBtn = IconButton( onPressed: () => isObscured.value = !isObscured.value, @@ -70,21 +80,30 @@ class ThemedFormField extends HookWidget { contentPadding: const EdgeInsets.symmetric(vertical: 14, horizontal: 15), border: OutlineInputBorder( borderRadius: BorderRadius.circular(10), - borderSide: BorderSide(color: Color(0xFF50A398)), + // borderSide: BorderSide(color: Color(0xFF50A398)), + borderSide: BorderSide(color: Color(0xFFFFFFFF)), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(10), - borderSide: BorderSide(color: Color(0xFF50A398)), - ), - focusedBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(10), - borderSide: BorderSide(color: Color(0xFF50A398), width: 2), + // borderSide: BorderSide(color: Color(0xFF50A398)), + borderSide: BorderSide(color: Color(0xFFFFFFFF)), ), + focusedBorder: readOnly + ? OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + // borderSide: BorderSide(color: Color(0xFF50A398)), + borderSide: BorderSide(color: Color(0xFFFFFFFF)), + ) + : OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: BorderSide(color: Color(0xFF50A398), width: 2), + ), hintText: hintText, hintStyle: TextStyle( // ✅ dynamic hint color color: const Color(0xFFC3C6CB), ), + // labelStyle: TextStyle(fontSize: 11, color: Colors.red), prefixIconConstraints: const BoxConstraints( maxWidth: 25 + 16 + 10, maxHeight: 25 + (8 * 2), @@ -105,6 +124,7 @@ class ThemedFormField extends HookWidget { ); return Container( width: txtwidth ?? MediaQuery.of(context).size.width, + height: txtheight ?? null, child: DecoratedBox( decoration: boxDecoration, child: TextFormField( diff --git a/lib/presentation/themes/indicators/upload_doc_theme.dart b/lib/presentation/themes/indicators/upload_doc_theme.dart new file mode 100644 index 0000000..f12fa9b --- /dev/null +++ b/lib/presentation/themes/indicators/upload_doc_theme.dart @@ -0,0 +1,157 @@ +import 'dart:html' as html; +import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; + +class ThemedUploadField extends FormField { + ThemedUploadField({ + super.key, + String? hintText, + double? txtwidth, + double? txtheight, + Color? backgroundColor, + Color? borderColor, + Color? errorBorderColor, + FormFieldValidator? validator, + Function(String fileName, dynamic file)? onFileSelected, + }) : super( + validator: validator, + builder: (FormFieldState state) { + return _ThemedUploadFieldBody( + hintText: hintText, + txtwidth: txtwidth, + txtheight: txtheight, + backgroundColor: backgroundColor, + borderColor: borderColor, + errorBorderColor: errorBorderColor, + onFileSelected: (fileName, file) { + state.didChange(fileName); // update validation state + onFileSelected?.call(fileName, file); + }, + errorText: state.errorText, + ); + }, + ); +} + +class _ThemedUploadFieldBody extends StatefulWidget { + const _ThemedUploadFieldBody({ + this.hintText, + this.txtwidth, + this.txtheight, + this.backgroundColor, + this.borderColor, + this.errorBorderColor, + this.onFileSelected, + this.errorText, + }); + + final String? hintText; + final double? txtwidth; + final double? txtheight; + final Color? backgroundColor; + final Color? borderColor; + final Color? errorBorderColor; + final Function(String fileName, dynamic file)? onFileSelected; + final String? errorText; + + @override + State<_ThemedUploadFieldBody> createState() => _ThemedUploadFieldBodyState(); +} + +class _ThemedUploadFieldBodyState extends State<_ThemedUploadFieldBody> { + String? selectedFileName; + + void pickPDFWeb() { + final uploadInput = html.FileUploadInputElement(); + uploadInput.accept = '.pdf'; + uploadInput.click(); + + uploadInput.onChange.listen((e) { + final file = uploadInput.files!.first; + + // Ensure PDF type + if (!file.type.contains("pdf")) { + ScaffoldMessenger.of( + context, + ).showSnackBar(SnackBar(content: Text("Only PDF files allowed"))); + return; + } + + // Size check (3 MB) + const maxFileSize = 3 * 1024 * 1024; + if (file.size > maxFileSize) { + ScaffoldMessenger.of( + context, + ).showSnackBar(SnackBar(content: Text("File exceeds 3MB"))); + return; + } + + setState(() => selectedFileName = file.name); + widget.onFileSelected?.call(file.name, file); + }); + } + + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + width: widget.txtwidth ?? MediaQuery.of(context).size.width, + height: widget.txtheight, + child: DecoratedBox( + decoration: BoxDecoration(borderRadius: BorderRadius.circular(10)), + child: InkWell( + onTap: pickPDFWeb, + borderRadius: BorderRadius.circular(10), + child: Container( + padding: const EdgeInsets.symmetric( + vertical: 10, + horizontal: 15, + ), + decoration: BoxDecoration( + color: widget.backgroundColor ?? Colors.white, + borderRadius: BorderRadius.circular(10), + border: Border.all( + color: widget.errorText != null + ? (widget.errorBorderColor ?? Colors.red) + : (widget.borderColor ?? Colors.grey.shade50), + width: 1, + ), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Text( + selectedFileName ?? + widget.hintText ?? + "Upload Documents", + style: TextStyle( + fontSize: 12, + color: widget.errorText != null + ? Colors.red + : Colors.black, + ), + overflow: TextOverflow.ellipsis, + ), + ), + const Icon(Icons.file_upload_outlined, color: Colors.black), + ], + ), + ), + ), + ), + ), + // if (widget.errorText != null) + // Padding( + // padding: const EdgeInsets.only(left: 8, top: 4), + // child: Text( + // widget.errorText!, + // style: const TextStyle(color: Colors.red, fontSize: 10), + // ), + // ), + ], + ); + } +} diff --git a/lib/presentation/widgets/drawerContentWrapper.dart b/lib/presentation/widgets/drawerContentWrapper.dart new file mode 100644 index 0000000..d48b9d7 --- /dev/null +++ b/lib/presentation/widgets/drawerContentWrapper.dart @@ -0,0 +1,25 @@ +import 'package:flutter/material.dart'; + +class DrawerContentWrapper extends StatelessWidget { + final Widget child; + + const DrawerContentWrapper({super.key, required this.child}); + + @override + Widget build(BuildContext context) { + return Column( + children: [ + Container( + height: 45, + width: 50, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(8.0), + color: const Color(0xFFB2D8D3), + ), + child: Center(child: child), + ), + const SizedBox(height: 30), + ], + ); + } +} diff --git a/lib/presentation/widgets/drawer_menu.dart b/lib/presentation/widgets/drawer_menu.dart index 3bcb6ec..a7f0f8e 100644 --- a/lib/presentation/widgets/drawer_menu.dart +++ b/lib/presentation/widgets/drawer_menu.dart @@ -2,31 +2,114 @@ import 'package:flutter/material.dart'; import '../../core/routing/routes.dart'; import 'package:go_router/go_router.dart'; -class DrawerMenu extends StatelessWidget { +import 'drawerContentWrapper.dart'; + +class DrawerMenu extends StatefulWidget { const DrawerMenu({super.key}); + @override + DrawerMenuState createState() => DrawerMenuState(); +} + +class DrawerMenuState extends State { + OverlayEntry? _overlayEntry; + + void _showPopup(BuildContext itemContext) { + final renderBox = itemContext.findRenderObject() as RenderBox; + final offset = renderBox.localToGlobal(Offset.zero); + + _overlayEntry = OverlayEntry( + builder: (context) { + return Positioned( + left: offset.dx + renderBox.size.width, + top: offset.dy, + child: MouseRegion( + onExit: (_) => _hidePopup(), + child: Material( + elevation: 4, + borderRadius: BorderRadius.circular(16), + child: Container( + padding: const EdgeInsets.all(10), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(6), + color: Colors.white, + ), + + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + GestureDetector( + onTap: () { + print("Tappped Agent"); + context.go(AppRoutes.agentLst); + }, + child: const Text( + "Agent", + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w400, + ), + ), + ), + const SizedBox(height: 5), + GestureDetector( + onTap: () { + print("Bf clicked"); + context.go(AppRoutes.staffLst); + }, + child: const Text( + "Staff", + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w400, + ), + ), + ), + ], + ), + ), + ), + ), + ); + }, + ); + + Overlay.of(itemContext).insert(_overlayEntry!); + } + + void _hidePopup() { + print('Hide'); + _overlayEntry?.remove(); + _overlayEntry = null; + } @override Widget build(BuildContext context) { - return Drawer( + return Container( + color: Color(0xFF425B5B), + padding: EdgeInsets.all(13.0), child: ListView( padding: EdgeInsets.zero, children: [ - const DrawerHeader( - decoration: BoxDecoration(color: Colors.blue), - child: Text("Menu", style: TextStyle(color: Colors.white)), - ), - ListTile( - title: const Text("Home"), - onTap: () { - context.go(AppRoutes.home); - Navigator.pop(context); - }, - ), - ListTile( - title: const Text("Login"), - onTap: () { - context.go(AppRoutes.login); - Navigator.pop(context); + // ListTile( + // title: const Text("Home"), + // onTap: () { + // context.go(AppRoutes.home); + // Navigator.pop(context); + // }, + // ), + SizedBox(height: 50), + DrawerContentWrapper(child: Text("A")), + DrawerContentWrapper(child: Text("B")), + DrawerContentWrapper(child: Text("C")), + Builder( + builder: (itemContext) { + return MouseRegion( + onEnter: (_) => _showPopup(itemContext), + child: DrawerContentWrapper( + child: Icon(Icons.person_add_alt, size: 30), + ), + ); }, ), ], diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc index 38dd0bc..9cb15c0 100644 --- a/linux/flutter/generated_plugin_registrant.cc +++ b/linux/flutter/generated_plugin_registrant.cc @@ -7,12 +7,16 @@ #include "generated_plugin_registrant.h" #include +#include #include void fl_register_plugins(FlPluginRegistry* registry) { g_autoptr(FlPluginRegistrar) flutter_secure_storage_linux_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterSecureStorageLinuxPlugin"); flutter_secure_storage_linux_plugin_register_with_registrar(flutter_secure_storage_linux_registrar); + g_autoptr(FlPluginRegistrar) open_file_linux_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "OpenFileLinuxPlugin"); + open_file_linux_plugin_register_with_registrar(open_file_linux_registrar); g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin"); url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar); diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake index 65240e9..dec86cb 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -4,6 +4,7 @@ list(APPEND FLUTTER_PLUGIN_LIST flutter_secure_storage_linux + open_file_linux url_launcher_linux ) diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 1146177..2f0a81b 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -13,6 +13,7 @@ import firebase_messaging import flutter_secure_storage_macos import google_sign_in_ios import local_auth_darwin +import open_file_mac import package_info_plus import path_provider_foundation import shared_preferences_foundation @@ -27,6 +28,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin")) FLTGoogleSignInPlugin.register(with: registry.registrar(forPlugin: "FLTGoogleSignInPlugin")) LocalAuthPlugin.register(with: registry.registrar(forPlugin: "LocalAuthPlugin")) + OpenFilePlugin.register(with: registry.registrar(forPlugin: "OpenFilePlugin")) FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) diff --git a/pubspec.lock b/pubspec.lock index 2c364fb..3cda1ad 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -33,6 +33,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.4.0" + archive: + dependency: transitive + description: + name: archive + sha256: cb6a278ef2dbb298455e1a713bda08524a175630ec643a242c399c932a0a1f7d + url: "https://pub.dev" + source: hosted + version: "3.6.1" args: dependency: transitive description: @@ -129,6 +137,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.4.0" + charcode: + dependency: transitive + description: + name: charcode + sha256: fb0f1107cac15a5ea6ef0a6ef71a807b9e4267c713bb93e00e92d737cc8dbd8a + url: "https://pub.dev" + source: hosted + version: "1.4.0" checked_yaml: dependency: transitive description: @@ -193,6 +209,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.6" + csslib: + dependency: transitive + description: + name: csslib + sha256: "09bad715f418841f976c77db72d5398dc1253c21fb9c0c7f0b0b985860b2d58e" + url: "https://pub.dev" + source: hosted + version: "1.0.2" cupertino_icons: dependency: "direct main" description: @@ -217,6 +241,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.7.11" + dropdown_search: + dependency: "direct main" + description: + name: dropdown_search + sha256: c29b3e5147a82a06a4a08b3b574c51cb48cc17ad89893d53ee72a6f86643622e + url: "https://pub.dev" + source: hosted + version: "6.0.2" easy_loading_button: dependency: "direct main" description: @@ -233,6 +265,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.7" + excel: + dependency: "direct main" + description: + name: excel + sha256: "1a15327dcad260d5db21d1f6e04f04838109b39a2f6a84ea486ceda36e468780" + url: "https://pub.dev" + source: hosted + version: "4.0.6" fake_async: dependency: transitive description: @@ -560,6 +600,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.3.2" + html: + dependency: transitive + description: + name: html + sha256: "6d1264f2dffa1b1101c25a91dff0dc2daee4c18e87cd8538729773c073dbf602" + url: "https://pub.dev" + source: hosted + version: "0.15.6" http: dependency: "direct main" description: @@ -752,6 +800,22 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.0" + month_picker_dialog: + dependency: "direct main" + description: + name: month_picker_dialog + sha256: "8196c59bbd3339ea57de2c372ca51a8cfdbb242d6e45100925b9e1982ba4617d" + url: "https://pub.dev" + source: hosted + version: "6.5.0" + nested: + dependency: transitive + description: + name: nested + sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20" + url: "https://pub.dev" + source: hosted + version: "1.0.0" nm: dependency: transitive description: @@ -760,6 +824,70 @@ packages: url: "https://pub.dev" source: hosted version: "0.5.0" + open_file: + dependency: "direct main" + description: + name: open_file + sha256: d17e2bddf5b278cb2ae18393d0496aa4f162142ba97d1a9e0c30d476adf99c0e + url: "https://pub.dev" + source: hosted + version: "3.5.10" + open_file_android: + dependency: transitive + description: + name: open_file_android + sha256: "58141fcaece2f453a9684509a7275f231ac0e3d6ceb9a5e6de310a7dff9084aa" + url: "https://pub.dev" + source: hosted + version: "1.0.6" + open_file_ios: + dependency: transitive + description: + name: open_file_ios + sha256: "02996f01e5f6863832068e97f8f3a5ef9b613516db6897f373b43b79849e4d07" + url: "https://pub.dev" + source: hosted + version: "1.0.3" + open_file_linux: + dependency: transitive + description: + name: open_file_linux + sha256: d189f799eecbb139c97f8bc7d303f9e720954fa4e0fa1b0b7294767e5f2d7550 + url: "https://pub.dev" + source: hosted + version: "0.0.5" + open_file_mac: + dependency: transitive + description: + name: open_file_mac + sha256: "1440b1e37ceb0642208cfeb2c659c6cda27b25187a90635c9d1acb7d0584d324" + url: "https://pub.dev" + source: hosted + version: "1.0.3" + open_file_platform_interface: + dependency: transitive + description: + name: open_file_platform_interface + sha256: "101b424ca359632699a7e1213e83d025722ab668b9fd1412338221bf9b0e5757" + url: "https://pub.dev" + source: hosted + version: "1.0.3" + open_file_web: + dependency: transitive + description: + name: open_file_web + sha256: e3dbc9584856283dcb30aef5720558b90f88036360bd078e494ab80a80130c4f + url: "https://pub.dev" + source: hosted + version: "0.0.4" + open_file_windows: + dependency: transitive + description: + name: open_file_windows + sha256: d26c31ddf935a94a1a3aa43a23f4fff8a5ff4eea395fe7a8cb819cf55431c875 + url: "https://pub.dev" + source: hosted + version: "0.0.3" package_config: dependency: transitive description: @@ -888,6 +1016,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.5.1" + provider: + dependency: transitive + description: + name: provider + sha256: "4e82183fa20e5ca25703ead7e05de9e4cceed1fbd1eadc1ac3cb6f565a09f272" + url: "https://pub.dev" + source: hosted + version: "6.1.5+1" pub_semver: dependency: transitive description: @@ -1109,6 +1245,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.4.0" + universal_html: + dependency: "direct main" + description: + name: universal_html + sha256: "56536254004e24d9d8cfdb7dbbf09b74cf8df96729f38a2f5c238163e3d58971" + url: "https://pub.dev" + source: hosted + version: "2.2.4" + universal_io: + dependency: transitive + description: + name: universal_io + sha256: "1722b2dcc462b4b2f3ee7d188dad008b6eb4c40bbd03a3de451d82c78bba9aad" + url: "https://pub.dev" + source: hosted + version: "2.2.2" universal_platform: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 3e1d11a..437369c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -62,6 +62,11 @@ dependencies: package_info_plus: ^8.3.1 flutter_lints: ^6.0.0 easy_loading_button: ^0.4.0 + universal_html: ^2.2.4 + month_picker_dialog: ^6.5.0 + open_file: ^3.5.10 + excel: ^4.0.6 + dropdown_search: ^6.0.2 dev_dependencies: flutter_test: