From d1acc1d367a98a73f0b6331825db14d26835ee90 Mon Sep 17 00:00:00 2001 From: egecankomur Date: Wed, 10 Jun 2026 23:22:15 +0300 Subject: [PATCH] =?UTF-8?q?Initial=20commit=20=E2=80=94=20DLS=20lab-app=20?= =?UTF-8?q?Flutter=20project?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 52 + .metadata | 45 + README.md | 16 + analysis_options.yaml | 28 + android/.gitignore | 14 + android/app/build.gradle.kts | 45 + android/app/google-services.json | 29 + android/app/src/debug/AndroidManifest.xml | 7 + android/app/src/main/AndroidManifest.xml | 45 + .../com/kovakyazilim/labapp/MainActivity.kt | 5 + .../res/drawable-v21/launch_background.xml | 12 + .../main/res/drawable/launch_background.xml | 12 + .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 5110 bytes .../res/mipmap-hdpi/ic_launcher_round.png | Bin 0 -> 5110 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 2804 bytes .../res/mipmap-mdpi/ic_launcher_round.png | Bin 0 -> 2804 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 8059 bytes .../res/mipmap-xhdpi/ic_launcher_round.png | Bin 0 -> 8059 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 15680 bytes .../res/mipmap-xxhdpi/ic_launcher_round.png | Bin 0 -> 15680 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 24763 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.png | Bin 0 -> 24763 bytes .../app/src/main/res/values-night/styles.xml | 18 + android/app/src/main/res/values/strings.xml | 4 + android/app/src/main/res/values/styles.xml | 18 + android/app/src/profile/AndroidManifest.xml | 7 + android/build.gradle.kts | 24 + android/gradle.properties | 2 + .../gradle/wrapper/gradle-wrapper.properties | 5 + android/settings.gradle.kts | 27 + database.md | 418 ++++++ ios/.gitignore | 34 + ios/Flutter/AppFrameworkInfo.plist | 26 + ios/Flutter/Debug.xcconfig | 2 + ios/Flutter/Release.xcconfig | 2 + ios/Podfile | 46 + ios/Podfile.lock | 147 ++ ios/Runner.xcodeproj/project.pbxproj | 746 ++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/WorkspaceSettings.xcsettings | 8 + .../xcshareddata/xcschemes/Runner.xcscheme | 101 ++ .../contents.xcworkspacedata | 10 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/WorkspaceSettings.xcsettings | 8 + ios/Runner/AppDelegate.swift | 13 + .../AppIcon.appiconset/Contents.json | 122 ++ .../Icon-App-1024x1024@1x.png | Bin 0 -> 326031 bytes .../AppIcon.appiconset/Icon-App-20x20@1x.png | Bin 0 -> 718 bytes .../AppIcon.appiconset/Icon-App-20x20@2x.png | Bin 0 -> 2068 bytes .../AppIcon.appiconset/Icon-App-20x20@3x.png | Bin 0 -> 3825 bytes .../AppIcon.appiconset/Icon-App-29x29@1x.png | Bin 0 -> 1335 bytes .../AppIcon.appiconset/Icon-App-29x29@2x.png | Bin 0 -> 3628 bytes .../AppIcon.appiconset/Icon-App-29x29@3x.png | Bin 0 -> 6992 bytes .../AppIcon.appiconset/Icon-App-40x40@1x.png | Bin 0 -> 2068 bytes .../AppIcon.appiconset/Icon-App-40x40@2x.png | Bin 0 -> 5974 bytes .../AppIcon.appiconset/Icon-App-40x40@3x.png | Bin 0 -> 11334 bytes .../AppIcon.appiconset/Icon-App-60x60@2x.png | Bin 0 -> 11334 bytes .../AppIcon.appiconset/Icon-App-60x60@3x.png | Bin 0 -> 22076 bytes .../AppIcon.appiconset/Icon-App-76x76@1x.png | Bin 0 -> 5614 bytes .../AppIcon.appiconset/Icon-App-76x76@2x.png | Bin 0 -> 17058 bytes .../Icon-App-83.5x83.5@2x.png | Bin 0 -> 20045 bytes .../LaunchImage.imageset/Contents.json | 23 + .../LaunchImage.imageset/LaunchImage.png | Bin 0 -> 68 bytes .../LaunchImage.imageset/LaunchImage@2x.png | Bin 0 -> 68 bytes .../LaunchImage.imageset/LaunchImage@3x.png | Bin 0 -> 68 bytes .../LaunchImage.imageset/README.md | 5 + ios/Runner/Base.lproj/LaunchScreen.storyboard | 37 + ios/Runner/Base.lproj/Main.storyboard | 26 + ios/Runner/GoogleService-Info.plist | 30 + ios/Runner/Info.plist | 53 + ios/Runner/Runner-Bridging-Header.h | 1 + ios/Runner/Runner.entitlements | 8 + ios/Runner/RunnerDebug.entitlements | 8 + ios/RunnerTests/RunnerTests.swift | 12 + lib/core/api/pocketbase_client.dart | 25 + lib/core/auth/auth_repository.dart | 100 ++ lib/core/l10n/app_strings.dart | 777 ++++++++++ lib/core/providers/auth_provider.dart | 195 +++ lib/core/providers/locale_provider.dart | 39 + lib/core/router/app_router.dart | 496 +++++++ lib/core/router/router_provider.dart | 52 + lib/core/services/ai_actions.dart | 171 +++ lib/core/services/ai_context_builder.dart | 226 +++ lib/core/services/ai_service.dart | 71 + lib/core/services/job_history_service.dart | 117 ++ lib/core/services/notification_service.dart | 64 + lib/core/services/realtime_service.dart | 37 + lib/core/theme/app_theme.dart | 299 ++++ lib/core/utils/currency_formatter.dart | 35 + lib/core/utils/file_download_helper.dart | 40 + lib/core/widgets/app_search_field.dart | 72 + lib/core/widgets/gradient_app_bar.dart | 328 +++++ lib/core/widgets/pill_tabs.dart | 121 ++ lib/core/widgets/tooth_logo.dart | 104 ++ lib/features/auth/auth_widgets.dart | 104 ++ lib/features/auth/onboarding_repository.dart | 32 + lib/features/auth/onboarding_screen.dart | 461 ++++++ lib/features/auth/sign_in_screen.dart | 888 +++++++++++ lib/features/auth/sign_up_screen.dart | 619 ++++++++ .../clinic_connections_repository.dart | 40 + .../clinic_connections_screen.dart | 441 ++++++ .../dashboard/clinic_dashboard_screen.dart | 1230 ++++++++++++++++ .../finance/clinic_finance_repository.dart | 49 + .../clinic/finance/clinic_finance_screen.dart | 534 +++++++ .../clinic/jobs/clinic_job_detail_screen.dart | 749 ++++++++++ .../clinic/jobs/clinic_jobs_repository.dart | 177 +++ .../clinic/jobs/clinic_jobs_screen.dart | 570 +++++++ lib/features/clinic/jobs/new_job_screen.dart | 1067 ++++++++++++++ .../clinic_patient_detail_screen.dart | 717 +++++++++ .../patients/clinic_patients_repository.dart | 67 + .../patients/clinic_patients_screen.dart | 575 ++++++++ .../settings/clinic_settings_screen.dart | 680 +++++++++ .../connections/connection_detail_screen.dart | 581 ++++++++ .../connection_stats_repository.dart | 124 ++ .../lab_connections_repository.dart | 30 + .../connections/lab_connections_screen.dart | 453 ++++++ .../lab/dashboard/lab_dashboard_screen.dart | 883 +++++++++++ .../lab/discounts/discount_repository.dart | 92 ++ .../lab/discounts/discounts_screen.dart | 940 ++++++++++++ .../lab/finance/lab_finance_repository.dart | 42 + .../lab/finance/lab_finance_screen.dart | 467 ++++++ .../lab/jobs/lab_all_jobs_screen.dart | 896 +++++++++++ .../lab/jobs/lab_job_detail_screen.dart | 764 ++++++++++ .../lab/jobs/lab_jobs_inbound_screen.dart | 335 +++++ .../lab/jobs/lab_jobs_repository.dart | 131 ++ .../lab/products/lab_products_repository.dart | 39 + .../lab/products/lab_products_screen.dart | 618 ++++++++ .../lab/settings/lab_settings_screen.dart | 785 ++++++++++ lib/features/shared/ai_chat_screen.dart | 930 ++++++++++++ lib/features/shared/job_files_panel.dart | 619 ++++++++ lib/features/shared/job_files_repository.dart | 59 + lib/features/shared/reports_repository.dart | 325 ++++ lib/features/shared/reports_screen.dart | 690 +++++++++ .../shared/tenant_team_repository.dart | 82 ++ lib/features/shared/tenant_team_screen.dart | 742 ++++++++++ lib/main.dart | 131 ++ lib/models/clinic_discount.dart | 68 + lib/models/connection.dart | 53 + lib/models/finance_entry.dart | 62 + lib/models/job.dart | 312 ++++ lib/models/job_file.dart | 84 ++ lib/models/patient.dart | 49 + lib/models/prosthetic_product.dart | 45 + lib/models/tenant.dart | 145 ++ lib/models/tenant_invite.dart | 37 + lib/models/user_profile.dart | 31 + linux/.gitignore | 1 + linux/CMakeLists.txt | 128 ++ linux/flutter/CMakeLists.txt | 88 ++ linux/flutter/generated_plugin_registrant.cc | 15 + linux/flutter/generated_plugin_registrant.h | 15 + linux/flutter/generated_plugins.cmake | 25 + linux/runner/CMakeLists.txt | 26 + linux/runner/main.cc | 6 + linux/runner/my_application.cc | 148 ++ linux/runner/my_application.h | 21 + macos/.gitignore | 7 + macos/Flutter/Flutter-Debug.xcconfig | 2 + macos/Flutter/Flutter-Release.xcconfig | 2 + macos/Flutter/GeneratedPluginRegistrant.swift | 18 + macos/Podfile | 42 + macos/Podfile.lock | 42 + macos/Runner.xcodeproj/project.pbxproj | 812 ++++++++++ .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/xcschemes/Runner.xcscheme | 99 ++ .../contents.xcworkspacedata | 10 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + macos/Runner/AppDelegate.swift | 13 + .../AppIcon.appiconset/Contents.json | 68 + .../AppIcon.appiconset/app_icon_1024.png | Bin 0 -> 326031 bytes .../AppIcon.appiconset/app_icon_128.png | Bin 0 -> 13069 bytes .../AppIcon.appiconset/app_icon_16.png | Bin 0 -> 580 bytes .../AppIcon.appiconset/app_icon_256.png | Bin 0 -> 40756 bytes .../AppIcon.appiconset/app_icon_32.png | Bin 0 -> 1548 bytes .../AppIcon.appiconset/app_icon_512.png | Bin 0 -> 126807 bytes .../AppIcon.appiconset/app_icon_64.png | Bin 0 -> 4321 bytes macos/Runner/Base.lproj/MainMenu.xib | 343 +++++ macos/Runner/Configs/AppInfo.xcconfig | 14 + macos/Runner/Configs/Debug.xcconfig | 2 + macos/Runner/Configs/Release.xcconfig | 2 + macos/Runner/Configs/Warnings.xcconfig | 13 + macos/Runner/DebugProfile.entitlements | 16 + macos/Runner/Info.plist | 32 + macos/Runner/MainFlutterWindow.swift | 29 + macos/Runner/Release.entitlements | 14 + macos/RunnerTests/RunnerTests.swift | 12 + maestro/flows/00_login_clinic.yaml | 11 + maestro/flows/00_login_lab.yaml | 11 + maestro/flows/00_logout.yaml | 14 + maestro/flows/01_lab_send_prova.yaml | 28 + maestro/flows/02_clinic_approve_prova.yaml | 20 + maestro/flows/03_lab_send_final.yaml | 30 + maestro/flows/04_clinic_deliver.yaml | 30 + maestro/flows/05_new_job_no_black_screen.yaml | 38 + maestro/login_flow.yaml | 10 + pb_hooks/jobs_notifications.pb.js | 129 ++ pubspec.lock | 1311 +++++++++++++++++ pubspec.yaml | 60 + test/widget_test.dart | 7 + web/favicon.png | Bin 0 -> 917 bytes web/icons/Icon-192.png | Bin 0 -> 5292 bytes web/icons/Icon-512.png | Bin 0 -> 8252 bytes web/icons/Icon-maskable-192.png | Bin 0 -> 5594 bytes web/icons/Icon-maskable-512.png | Bin 0 -> 20998 bytes web/index.html | 38 + web/manifest.json | 35 + windows/.gitignore | 17 + windows/CMakeLists.txt | 108 ++ windows/flutter/CMakeLists.txt | 109 ++ .../flutter/generated_plugin_registrant.cc | 17 + windows/flutter/generated_plugin_registrant.h | 15 + windows/flutter/generated_plugins.cmake | 26 + windows/runner/CMakeLists.txt | 40 + windows/runner/Runner.rc | 121 ++ windows/runner/flutter_window.cpp | 71 + windows/runner/flutter_window.h | 33 + windows/runner/main.cpp | 43 + windows/runner/resource.h | 16 + windows/runner/resources/app_icon.ico | Bin 0 -> 33772 bytes windows/runner/runner.exe.manifest | 14 + windows/runner/utils.cpp | 65 + windows/runner/utils.h | 19 + windows/runner/win32_window.cpp | 288 ++++ windows/runner/win32_window.h | 102 ++ 225 files changed, 31294 insertions(+) create mode 100644 .gitignore create mode 100644 .metadata create mode 100644 README.md create mode 100644 analysis_options.yaml create mode 100644 android/.gitignore create mode 100644 android/app/build.gradle.kts create mode 100644 android/app/google-services.json create mode 100644 android/app/src/debug/AndroidManifest.xml create mode 100644 android/app/src/main/AndroidManifest.xml create mode 100644 android/app/src/main/kotlin/com/kovakyazilim/labapp/MainActivity.kt create mode 100644 android/app/src/main/res/drawable-v21/launch_background.xml create mode 100644 android/app/src/main/res/drawable/launch_background.xml create mode 100644 android/app/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png create mode 100644 android/app/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png create mode 100644 android/app/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png create mode 100644 android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png create mode 100644 android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png create mode 100644 android/app/src/main/res/values-night/styles.xml create mode 100644 android/app/src/main/res/values/strings.xml create mode 100644 android/app/src/main/res/values/styles.xml create mode 100644 android/app/src/profile/AndroidManifest.xml create mode 100644 android/build.gradle.kts create mode 100644 android/gradle.properties create mode 100644 android/gradle/wrapper/gradle-wrapper.properties create mode 100644 android/settings.gradle.kts create mode 100644 database.md create mode 100644 ios/.gitignore create mode 100644 ios/Flutter/AppFrameworkInfo.plist create mode 100644 ios/Flutter/Debug.xcconfig create mode 100644 ios/Flutter/Release.xcconfig create mode 100644 ios/Podfile create mode 100644 ios/Podfile.lock create mode 100644 ios/Runner.xcodeproj/project.pbxproj create mode 100644 ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings create mode 100644 ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme create mode 100644 ios/Runner.xcworkspace/contents.xcworkspacedata create mode 100644 ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings create mode 100644 ios/Runner/AppDelegate.swift create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png create mode 100644 ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json create mode 100644 ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png create mode 100644 ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png create mode 100644 ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png create mode 100644 ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md create mode 100644 ios/Runner/Base.lproj/LaunchScreen.storyboard create mode 100644 ios/Runner/Base.lproj/Main.storyboard create mode 100644 ios/Runner/GoogleService-Info.plist create mode 100644 ios/Runner/Info.plist create mode 100644 ios/Runner/Runner-Bridging-Header.h create mode 100644 ios/Runner/Runner.entitlements create mode 100644 ios/Runner/RunnerDebug.entitlements create mode 100644 ios/RunnerTests/RunnerTests.swift create mode 100644 lib/core/api/pocketbase_client.dart create mode 100644 lib/core/auth/auth_repository.dart create mode 100644 lib/core/l10n/app_strings.dart create mode 100644 lib/core/providers/auth_provider.dart create mode 100644 lib/core/providers/locale_provider.dart create mode 100644 lib/core/router/app_router.dart create mode 100644 lib/core/router/router_provider.dart create mode 100644 lib/core/services/ai_actions.dart create mode 100644 lib/core/services/ai_context_builder.dart create mode 100644 lib/core/services/ai_service.dart create mode 100644 lib/core/services/job_history_service.dart create mode 100644 lib/core/services/notification_service.dart create mode 100644 lib/core/services/realtime_service.dart create mode 100644 lib/core/theme/app_theme.dart create mode 100644 lib/core/utils/currency_formatter.dart create mode 100644 lib/core/utils/file_download_helper.dart create mode 100644 lib/core/widgets/app_search_field.dart create mode 100644 lib/core/widgets/gradient_app_bar.dart create mode 100644 lib/core/widgets/pill_tabs.dart create mode 100644 lib/core/widgets/tooth_logo.dart create mode 100644 lib/features/auth/auth_widgets.dart create mode 100644 lib/features/auth/onboarding_repository.dart create mode 100644 lib/features/auth/onboarding_screen.dart create mode 100644 lib/features/auth/sign_in_screen.dart create mode 100644 lib/features/auth/sign_up_screen.dart create mode 100644 lib/features/clinic/connections/clinic_connections_repository.dart create mode 100644 lib/features/clinic/connections/clinic_connections_screen.dart create mode 100644 lib/features/clinic/dashboard/clinic_dashboard_screen.dart create mode 100644 lib/features/clinic/finance/clinic_finance_repository.dart create mode 100644 lib/features/clinic/finance/clinic_finance_screen.dart create mode 100644 lib/features/clinic/jobs/clinic_job_detail_screen.dart create mode 100644 lib/features/clinic/jobs/clinic_jobs_repository.dart create mode 100644 lib/features/clinic/jobs/clinic_jobs_screen.dart create mode 100644 lib/features/clinic/jobs/new_job_screen.dart create mode 100644 lib/features/clinic/patients/clinic_patient_detail_screen.dart create mode 100644 lib/features/clinic/patients/clinic_patients_repository.dart create mode 100644 lib/features/clinic/patients/clinic_patients_screen.dart create mode 100644 lib/features/clinic/settings/clinic_settings_screen.dart create mode 100644 lib/features/lab/connections/connection_detail_screen.dart create mode 100644 lib/features/lab/connections/connection_stats_repository.dart create mode 100644 lib/features/lab/connections/lab_connections_repository.dart create mode 100644 lib/features/lab/connections/lab_connections_screen.dart create mode 100644 lib/features/lab/dashboard/lab_dashboard_screen.dart create mode 100644 lib/features/lab/discounts/discount_repository.dart create mode 100644 lib/features/lab/discounts/discounts_screen.dart create mode 100644 lib/features/lab/finance/lab_finance_repository.dart create mode 100644 lib/features/lab/finance/lab_finance_screen.dart create mode 100644 lib/features/lab/jobs/lab_all_jobs_screen.dart create mode 100644 lib/features/lab/jobs/lab_job_detail_screen.dart create mode 100644 lib/features/lab/jobs/lab_jobs_inbound_screen.dart create mode 100644 lib/features/lab/jobs/lab_jobs_repository.dart create mode 100644 lib/features/lab/products/lab_products_repository.dart create mode 100644 lib/features/lab/products/lab_products_screen.dart create mode 100644 lib/features/lab/settings/lab_settings_screen.dart create mode 100644 lib/features/shared/ai_chat_screen.dart create mode 100644 lib/features/shared/job_files_panel.dart create mode 100644 lib/features/shared/job_files_repository.dart create mode 100644 lib/features/shared/reports_repository.dart create mode 100644 lib/features/shared/reports_screen.dart create mode 100644 lib/features/shared/tenant_team_repository.dart create mode 100644 lib/features/shared/tenant_team_screen.dart create mode 100644 lib/main.dart create mode 100644 lib/models/clinic_discount.dart create mode 100644 lib/models/connection.dart create mode 100644 lib/models/finance_entry.dart create mode 100644 lib/models/job.dart create mode 100644 lib/models/job_file.dart create mode 100644 lib/models/patient.dart create mode 100644 lib/models/prosthetic_product.dart create mode 100644 lib/models/tenant.dart create mode 100644 lib/models/tenant_invite.dart create mode 100644 lib/models/user_profile.dart create mode 100644 linux/.gitignore create mode 100644 linux/CMakeLists.txt create mode 100644 linux/flutter/CMakeLists.txt create mode 100644 linux/flutter/generated_plugin_registrant.cc create mode 100644 linux/flutter/generated_plugin_registrant.h create mode 100644 linux/flutter/generated_plugins.cmake create mode 100644 linux/runner/CMakeLists.txt create mode 100644 linux/runner/main.cc create mode 100644 linux/runner/my_application.cc create mode 100644 linux/runner/my_application.h create mode 100644 macos/.gitignore create mode 100644 macos/Flutter/Flutter-Debug.xcconfig create mode 100644 macos/Flutter/Flutter-Release.xcconfig create mode 100644 macos/Flutter/GeneratedPluginRegistrant.swift create mode 100644 macos/Podfile create mode 100644 macos/Podfile.lock create mode 100644 macos/Runner.xcodeproj/project.pbxproj create mode 100644 macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme create mode 100644 macos/Runner.xcworkspace/contents.xcworkspacedata create mode 100644 macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 macos/Runner/AppDelegate.swift create mode 100644 macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png create mode 100644 macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png create mode 100644 macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png create mode 100644 macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png create mode 100644 macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png create mode 100644 macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png create mode 100644 macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png create mode 100644 macos/Runner/Base.lproj/MainMenu.xib create mode 100644 macos/Runner/Configs/AppInfo.xcconfig create mode 100644 macos/Runner/Configs/Debug.xcconfig create mode 100644 macos/Runner/Configs/Release.xcconfig create mode 100644 macos/Runner/Configs/Warnings.xcconfig create mode 100644 macos/Runner/DebugProfile.entitlements create mode 100644 macos/Runner/Info.plist create mode 100644 macos/Runner/MainFlutterWindow.swift create mode 100644 macos/Runner/Release.entitlements create mode 100644 macos/RunnerTests/RunnerTests.swift create mode 100644 maestro/flows/00_login_clinic.yaml create mode 100644 maestro/flows/00_login_lab.yaml create mode 100644 maestro/flows/00_logout.yaml create mode 100644 maestro/flows/01_lab_send_prova.yaml create mode 100644 maestro/flows/02_clinic_approve_prova.yaml create mode 100644 maestro/flows/03_lab_send_final.yaml create mode 100644 maestro/flows/04_clinic_deliver.yaml create mode 100644 maestro/flows/05_new_job_no_black_screen.yaml create mode 100644 maestro/login_flow.yaml create mode 100644 pb_hooks/jobs_notifications.pb.js create mode 100644 pubspec.lock create mode 100644 pubspec.yaml create mode 100644 test/widget_test.dart create mode 100644 web/favicon.png create mode 100644 web/icons/Icon-192.png create mode 100644 web/icons/Icon-512.png create mode 100644 web/icons/Icon-maskable-192.png create mode 100644 web/icons/Icon-maskable-512.png create mode 100644 web/index.html create mode 100644 web/manifest.json create mode 100644 windows/.gitignore create mode 100644 windows/CMakeLists.txt create mode 100644 windows/flutter/CMakeLists.txt create mode 100644 windows/flutter/generated_plugin_registrant.cc create mode 100644 windows/flutter/generated_plugin_registrant.h create mode 100644 windows/flutter/generated_plugins.cmake create mode 100644 windows/runner/CMakeLists.txt create mode 100644 windows/runner/Runner.rc create mode 100644 windows/runner/flutter_window.cpp create mode 100644 windows/runner/flutter_window.h create mode 100644 windows/runner/main.cpp create mode 100644 windows/runner/resource.h create mode 100644 windows/runner/resources/app_icon.ico create mode 100644 windows/runner/runner.exe.manifest create mode 100644 windows/runner/utils.cpp create mode 100644 windows/runner/utils.h create mode 100644 windows/runner/win32_window.cpp create mode 100644 windows/runner/win32_window.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..87d5858 --- /dev/null +++ b/.gitignore @@ -0,0 +1,52 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.build/ +.buildlog/ +.history +.svn/ +.swiftpm/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.pub-cache/ +.pub/ +/build/ +/coverage/ + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +# Android Studio will place build artifacts here +/android/app/debug +/android/app/profile +/android/app/release + +# MCP server config — contains API keys and credentials +.mcp.json + +# Claude Code project settings — local dev tooling only +.claude/ diff --git a/.metadata b/.metadata new file mode 100644 index 0000000..83b34eb --- /dev/null +++ b/.metadata @@ -0,0 +1,45 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: "f6ff1529fd6d8af5f706051d9251ac9231c83407" + channel: "stable" + +project_type: app + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: f6ff1529fd6d8af5f706051d9251ac9231c83407 + base_revision: f6ff1529fd6d8af5f706051d9251ac9231c83407 + - platform: android + create_revision: f6ff1529fd6d8af5f706051d9251ac9231c83407 + base_revision: f6ff1529fd6d8af5f706051d9251ac9231c83407 + - platform: ios + create_revision: f6ff1529fd6d8af5f706051d9251ac9231c83407 + base_revision: f6ff1529fd6d8af5f706051d9251ac9231c83407 + - platform: linux + create_revision: f6ff1529fd6d8af5f706051d9251ac9231c83407 + base_revision: f6ff1529fd6d8af5f706051d9251ac9231c83407 + - platform: macos + create_revision: f6ff1529fd6d8af5f706051d9251ac9231c83407 + base_revision: f6ff1529fd6d8af5f706051d9251ac9231c83407 + - platform: web + create_revision: f6ff1529fd6d8af5f706051d9251ac9231c83407 + base_revision: f6ff1529fd6d8af5f706051d9251ac9231c83407 + - platform: windows + create_revision: f6ff1529fd6d8af5f706051d9251ac9231c83407 + base_revision: f6ff1529fd6d8af5f706051d9251ac9231c83407 + + # User provided section + + # List of Local paths (relative to this file) that should be + # ignored by the migrate tool. + # + # Files that are not part of the templates will be ignored by default. + unmanaged_files: + - 'lib/main.dart' + - 'ios/Runner.xcodeproj/project.pbxproj' diff --git a/README.md b/README.md new file mode 100644 index 0000000..8e5bfde --- /dev/null +++ b/README.md @@ -0,0 +1,16 @@ +# lab_app + +A new Flutter project. + +## Getting Started + +This project is a starting point for a Flutter application. + +A few resources to get you started if this is your first Flutter project: + +- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) +- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) + +For help getting started with Flutter development, view the +[online documentation](https://docs.flutter.dev/), which offers tutorials, +samples, guidance on mobile development, and a full API reference. diff --git a/analysis_options.yaml b/analysis_options.yaml new file mode 100644 index 0000000..0d29021 --- /dev/null +++ b/analysis_options.yaml @@ -0,0 +1,28 @@ +# This file configures the analyzer, which statically analyzes Dart code to +# check for errors, warnings, and lints. +# +# The issues identified by the analyzer are surfaced in the UI of Dart-enabled +# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be +# invoked from the command line by running `flutter analyze`. + +# The following line activates a set of recommended lints for Flutter apps, +# packages, and plugins designed to encourage good coding practices. +include: package:flutter_lints/flutter.yaml + +linter: + # The lint rules applied to this project can be customized in the + # section below to disable rules from the `package:flutter_lints/flutter.yaml` + # included above or to enable additional rules. A list of all available lints + # and their documentation is published at https://dart.dev/lints. + # + # Instead of disabling a lint rule for the entire project in the + # section below, it can also be suppressed for a single line of code + # or a specific dart file by using the `// ignore: name_of_lint` and + # `// ignore_for_file: name_of_lint` syntax on the line or in the file + # producing the lint. + rules: + # avoid_print: false # Uncomment to disable the `avoid_print` rule + # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/android/.gitignore b/android/.gitignore new file mode 100644 index 0000000..be3943c --- /dev/null +++ b/android/.gitignore @@ -0,0 +1,14 @@ +gradle-wrapper.jar +/.gradle +/captures/ +/gradlew +/gradlew.bat +/local.properties +GeneratedPluginRegistrant.java +.cxx/ + +# Remember to never publicly share your keystore. +# See https://flutter.dev/to/reference-keystore +key.properties +**/*.keystore +**/*.jks diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts new file mode 100644 index 0000000..f8fd821 --- /dev/null +++ b/android/app/build.gradle.kts @@ -0,0 +1,45 @@ +plugins { + id("com.android.application") + id("kotlin-android") + id("com.google.gms.google-services") + // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. + id("dev.flutter.flutter-gradle-plugin") +} + +android { + namespace = "com.kovakyazilim.labapp" + compileSdk = flutter.compileSdkVersion + ndkVersion = flutter.ndkVersion + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + } + + kotlinOptions { + jvmTarget = JavaVersion.VERSION_17.toString() + } + + defaultConfig { + // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). + applicationId = "com.kovakyazilim.labapp" + // You can update the following values to match your application needs. + // For more information, see: https://flutter.dev/to/review-gradle-config. + minSdk = flutter.minSdkVersion + targetSdk = flutter.targetSdkVersion + versionCode = flutter.versionCode + versionName = flutter.versionName + } + + buildTypes { + release { + // TODO: Add your own signing config for the release build. + // Signing with the debug keys for now, so `flutter run --release` works. + signingConfig = signingConfigs.getByName("debug") + } + } +} + +flutter { + source = "../.." +} diff --git a/android/app/google-services.json b/android/app/google-services.json new file mode 100644 index 0000000..0e06756 --- /dev/null +++ b/android/app/google-services.json @@ -0,0 +1,29 @@ +{ + "project_info": { + "project_number": "751114036897", + "project_id": "dlslabapp", + "storage_bucket": "dlslabapp.firebasestorage.app" + }, + "client": [ + { + "client_info": { + "mobilesdk_app_id": "1:751114036897:android:5da96f1a9691458099f2e0", + "android_client_info": { + "package_name": "com.kovakyazilim.labapp" + } + }, + "oauth_client": [], + "api_key": [ + { + "current_key": "AIzaSyBjRvRur8zmczbDNOad0PGuFy19XRGS8QE" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [] + } + } + } + ], + "configuration_version": "1" +} \ No newline at end of file diff --git a/android/app/src/debug/AndroidManifest.xml b/android/app/src/debug/AndroidManifest.xml new file mode 100644 index 0000000..399f698 --- /dev/null +++ b/android/app/src/debug/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..4bff20e --- /dev/null +++ b/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/android/app/src/main/kotlin/com/kovakyazilim/labapp/MainActivity.kt b/android/app/src/main/kotlin/com/kovakyazilim/labapp/MainActivity.kt new file mode 100644 index 0000000..a5f71e5 --- /dev/null +++ b/android/app/src/main/kotlin/com/kovakyazilim/labapp/MainActivity.kt @@ -0,0 +1,5 @@ +package com.kovakyazilim.labapp + +import io.flutter.embedding.android.FlutterActivity + +class MainActivity : FlutterActivity() diff --git a/android/app/src/main/res/drawable-v21/launch_background.xml b/android/app/src/main/res/drawable-v21/launch_background.xml new file mode 100644 index 0000000..f74085f --- /dev/null +++ b/android/app/src/main/res/drawable-v21/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/android/app/src/main/res/drawable/launch_background.xml b/android/app/src/main/res/drawable/launch_background.xml new file mode 100644 index 0000000..304732f --- /dev/null +++ b/android/app/src/main/res/drawable/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..20a79243e4cf5e745f99f58b579d3a5d48f1cbad GIT binary patch literal 5110 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 Ye{Dhr-IG+>tN;K207*qoM6N<$f@r^*Z~y=R literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..20a79243e4cf5e745f99f58b579d3a5d48f1cbad GIT binary patch literal 5110 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 Ye{Dhr-IG+>tN;K207*qoM6N<$f@r^*Z~y=R literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..e04dca99032f21817cde015da32b4b36cc4a493c GIT binary patch literal 2804 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 literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..ded3046123e651f04706e28d5662f3591df36978 GIT binary patch literal 15680 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 literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..36b5574b8cdda5996779eb7a13cd29aee4d4815a GIT binary patch literal 24763 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 literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..36b5574b8cdda5996779eb7a13cd29aee4d4815a GIT binary patch literal 24763 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 literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/values-night/styles.xml b/android/app/src/main/res/values-night/styles.xml new file mode 100644 index 0000000..06952be --- /dev/null +++ b/android/app/src/main/res/values-night/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..522466a --- /dev/null +++ b/android/app/src/main/res/values/strings.xml @@ -0,0 +1,4 @@ + + + DLS + diff --git a/android/app/src/main/res/values/styles.xml b/android/app/src/main/res/values/styles.xml new file mode 100644 index 0000000..cb1ef88 --- /dev/null +++ b/android/app/src/main/res/values/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/android/app/src/profile/AndroidManifest.xml b/android/app/src/profile/AndroidManifest.xml new file mode 100644 index 0000000..399f698 --- /dev/null +++ b/android/app/src/profile/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/android/build.gradle.kts b/android/build.gradle.kts new file mode 100644 index 0000000..dbee657 --- /dev/null +++ b/android/build.gradle.kts @@ -0,0 +1,24 @@ +allprojects { + repositories { + google() + mavenCentral() + } +} + +val newBuildDir: Directory = + rootProject.layout.buildDirectory + .dir("../../build") + .get() +rootProject.layout.buildDirectory.value(newBuildDir) + +subprojects { + val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name) + project.layout.buildDirectory.value(newSubprojectBuildDir) +} +subprojects { + project.evaluationDependsOn(":app") +} + +tasks.register("clean") { + delete(rootProject.layout.buildDirectory) +} diff --git a/android/gradle.properties b/android/gradle.properties new file mode 100644 index 0000000..fbee1d8 --- /dev/null +++ b/android/gradle.properties @@ -0,0 +1,2 @@ +org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError +android.useAndroidX=true diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..e4ef43f --- /dev/null +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-all.zip diff --git a/android/settings.gradle.kts b/android/settings.gradle.kts new file mode 100644 index 0000000..e4e86fb --- /dev/null +++ b/android/settings.gradle.kts @@ -0,0 +1,27 @@ +pluginManagement { + val flutterSdkPath = + run { + val properties = java.util.Properties() + file("local.properties").inputStream().use { properties.load(it) } + val flutterSdkPath = properties.getProperty("flutter.sdk") + require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" } + flutterSdkPath + } + + includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") + + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} + +plugins { + id("dev.flutter.flutter-plugin-loader") version "1.0.0" + id("com.android.application") version "8.11.1" apply false + id("org.jetbrains.kotlin.android") version "2.2.20" apply false + id("com.google.gms.google-services") version "4.4.2" apply false +} + +include(":app") diff --git a/database.md b/database.md new file mode 100644 index 0000000..8a73e3e --- /dev/null +++ b/database.md @@ -0,0 +1,418 @@ +# DLS — Veritabanı Referansı (Directus) + +> **Backend:** Appwrite → **Directus** migrasyonu. Kaynak gerçek bu dokümandır. +> Oluşturulma: 2026-06-05 (Directus MCP ile canlıdan inşa edildi). + +## 1. Bağlantı bilgileri + +| | Değer | +|---|---| +| Directus URL | (Coolify'da set edilecek) | +| Auth | Directus built-in JWT (email + şifre) | +| Storage | Directus Files (directus_files) | +| Tenant izolasyonu | `tenants` + `tenant_members` tabloları | + +> **Appwrite → Directus eşlemesi:** +> - `Appwrite Team` → `tenants` koleksiyonu +> - `Appwrite Team membership` → `tenant_members` koleksiyonu +> - `Appwrite Auth` → `directus_users` (Directus built-in) +> - `Appwrite Storage buckets` → `directus_files` (tek bucket, `kind` alanıyla ayrım) +> - Row-level security → Directus policies + `$CURRENT_USER` + Flutter tarafında `tenant_id` filtresi + +## 2. Uygulama özeti (DLS — Dental Lab System) + +**Diş klinikleri ↔ diş laboratuvarları** arasındaki protez iş alışverişini dijitalleştirir. +- Klinik bir hasta için protez işi açar → bağlı laboratuvara yollar. +- Lab gelen kutusundan görür, durum adımlarını işler: **Ölçü → Alt Yapı Prova → Üst Yapı Prova → Cila/Bitim**. +- Tamamlanınca iş `sent` → klinik teslim alınca `delivered`. +- Her iki taraf finansal akışı kendi defterinde izler. + +## 3. Multi-tenancy & yetki modeli + +- **Tenant = `tenants` satırı.** Her tenant'ın bir `kind`'ı var: `clinic` veya `lab`. +- Kullanıcı → `tenant_members` üzerinden bir veya birden fazla tenant'a bağlanır. +- **`member_number`** (12 hane, unique): tenant'ın **bağlantı kodu**. Login'de kullanılmaz; sadece iki tenant'ı eşlemek için. +- Flutter tarafında her sorguda `tenant_id` (veya `clinic_tenant_id`/`lab_tenant_id`) filtresi **zorunlu**. +- Cross-tenant tablolar (`connections`, `jobs`, `job_files`, `job_status_history`): hem `clinic_tenant_id` hem `lab_tenant_id` taşır — iki taraf da erişir. + +## 4. Koleksiyonlar (17) + +> Notasyon: `IDX`=index, `UNQ`=unique, `FK`=foreign key, `DEF`=default. +> Tüm tablolarda sistem alanları: `id` (uuid PK), `date_created`, `date_updated` (varsa). + +### `tenants` — tenant profili (Appwrite Team karşılığı) +| Alan | Tip | Not | +|---|---|---| +| id | uuid | PK | +| kind | enum[lab\|clinic] | tenant türü · **IDX** | +| member_number | string(12) | bağlantı kodu · **UNQ** | +| company_name | string(255) | | +| company_tax_id | string(50) | | +| company_address | text | | +| company_email | string(255) | | +| company_phone | string(30) | | +| logo | uuid | → `directus_files` | +| default_currency | string(8) | DEF: `TRY` | +| status | enum[active\|suspended] | DEF: `active` | + +### `tenant_members` — kullanıcı ↔ tenant üyeliği +| Alan | Tip | Not | +|---|---|---| +| id | uuid | PK | +| tenant_id | uuid | → `tenants` CASCADE · **IDX** | +| user_id | uuid | → `directus_users` CASCADE | +| role | enum[owner\|admin\|member] | DEF: `member` | + +### `profiles` — kullanıcı başına ek bilgi +| Alan | Tip | Not | +|---|---|---| +| id | uuid | PK | +| tenant_id | uuid | → `tenants` | +| user_id | uuid | → `directus_users` | +| display_name | string(255) | | +| phone | string(30) | | +| title | string(100) | | + +### `connections` — iki tenant arası bağlantı +| Alan | Tip | Not | +|---|---|---| +| id | uuid | PK | +| clinic_tenant_id | uuid | → `tenants` · **IDX** | +| lab_tenant_id | uuid | → `tenants` · **IDX** | +| status | enum[pending\|approved\|rejected] | DEF: `pending` · **IDX** | +| requested_by | uuid | → `directus_users` | +| requested_at | timestamp | | +| approved_at | timestamp | | +| rejected_at | timestamp | | + +> **Unique constraint:** `(clinic_tenant_id, lab_tenant_id)` — aynı çift iki kez bağlanamaz. Flutter tarafında kontrol edilmeli; DB constraint Directus MCP ile eklenemez, migration SQL ile ekle. + +### `patients` — klinik hasta kayıtları +| Alan | Tip | Not | +|---|---|---| +| id | uuid | PK | +| clinic_tenant_id | uuid | → `tenants` · **IDX** | +| created_by | uuid | → `directus_users` | +| patient_code | string(50) | klinik içinde unique (soft) | +| first_name | string(100) | | +| last_name | string(100) | | +| phone | string(30) | | +| date_of_birth | date | | +| notes | text | | +| archived | boolean | DEF: false | + +### `jobs` — protez işi (çekirdek tablo — en ağır) +| Alan | Tip | Not | +|---|---|---| +| id | uuid | PK | +| clinic_tenant_id | uuid | → `tenants` · **IDX** | +| lab_tenant_id | uuid | → `tenants` · **IDX** | +| created_by | uuid | → `directus_users` | +| patient_id | uuid | → `patients` SET NULL · **IDX** | +| patient_code | string(50) | | +| prosthetic_id | uuid | → `prosthetics` SET NULL | +| prosthetic_type | enum[metal_porselen\|zirkonyum\|implant_ustu_zirkonyum\|gecici\|e_max\|diger] | | +| member_count | integer | üye (diş) sayısı | +| teeth | json | diş numaraları dizisi `List` | +| color | string(20) | Vita renk kodu | +| description | text | | +| price | decimal(10,2) | | +| currency | string(8) | | +| status | enum[pending\|in_progress\|sent\|delivered\|cancelled] | DEF: `pending` · **IDX** | +| current_step | enum[olcu\|alt_yapi_prova\|ust_yapi_prova\|cila_bitim] | | +| location | enum[at_clinic\|at_lab] | DEF: `at_clinic` | +| due_date | timestamp | | + +**Query pattern (Flutter):** +``` +// Lab gelen kutusu +GET /items/jobs?filter[lab_tenant_id][_eq]=$tenantId&filter[status][_eq]=pending + &sort=-date_created&limit=50&page=1 + +// Klinik giden işler +GET /items/jobs?filter[clinic_tenant_id][_eq]=$tenantId + &sort=-date_created&limit=50&page=1 + +// Filtre + arama +&filter[status][_in]=pending,in_progress +&search=hasta_kodu +``` + +### `job_files` — işe bağlı dosyalar +| Alan | Tip | Not | +|---|---|---| +| id | uuid | PK | +| job_id | uuid | → `jobs` CASCADE · **IDX** | +| clinic_tenant_id | uuid | → `tenants` CASCADE · **IDX** | +| lab_tenant_id | uuid | → `tenants` CASCADE · **IDX** | +| uploaded_by | uuid | → `directus_users` | +| file_id | uuid | → `directus_files` SET NULL | +| kind | enum[scan\|image\|document] | | +| name | string(255) | | +| size | integer | bayt | +| mime_type | string(100) | | +| archived_at | timestamp | soft-delete; set → download disabled | + +### `job_status_history` — stepper denetim izi +| Alan | Tip | Not | +|---|---|---| +| id | uuid | PK | +| job_id | uuid | → `jobs` CASCADE · **IDX** | +| clinic_tenant_id | uuid | → `tenants` | +| lab_tenant_id | uuid | → `tenants` | +| step | enum[olcu\|alt_yapi_prova\|ust_yapi_prova\|cila_bitim] | | +| completed_by | uuid | → `directus_users` | +| completed_at | timestamp | | +| note | text | | + +### `prosthetics` — lab ürün kataloğu +| Alan | Tip | Not | +|---|---|---| +| id | uuid | PK | +| tenant_id | uuid | → `tenants` · **IDX** | +| created_by | uuid | → `directus_users` | +| name | string(255) | | +| type | enum[...prosthetic_types...] | | +| unit_price | decimal(10,2) | | +| currency | string(8) | DEF: `TRY` | +| archived | boolean | DEF: false · **IDX** | + +### `finance_entries` — tek-taraflı defter +| Alan | Tip | Not | +|---|---|---| +| id | uuid | PK | +| tenant_id | uuid | → `tenants` · **IDX** | +| created_by | uuid | → `directus_users` | +| job_id | uuid | → `jobs` SET NULL | +| counterpart_tenant_id | uuid | → `tenants` SET NULL | +| type | enum[income\|expense\|receivable\|payable] | | +| amount | decimal(12,2) | | +| currency | string(8) | | +| status | enum[pending\|paid\|cancelled] | DEF: `pending` · **IDX** | +| date | timestamp | **IDX** | +| description | text | | + +> **Cross-tenant sync (Directus Flow):** İş `sent`/`delivered` olunca: +> - Lab tarafı → `receivable/pending` satır +> - Klinik tarafı → `payable/pending` satır +> Idempotent: `(job_id, tenant_id, type)` kombinasyonu varsa atla. + +### `payments` — ödeme kayıtları +| Alan | Tip | Not | +|---|---|---| +| id | uuid | PK | +| tenant_id | uuid | → `tenants` | +| counterpart_tenant_id | uuid | → `tenants` | +| direction | enum[inflow\|outflow] | | +| amount | decimal(12,2) | | +| currency | string(8) | | +| payment_date | timestamp | | +| method | string(30) | | +| notes | text | | +| recorded_by | uuid | → `directus_users` | +| status | enum[pending\|confirmed\|rejected] | DEF: `confirmed` | + +### `clinic_pricing` — kliniğe özel lab fiyatı +| Alan | Tip | Not | +|---|---|---| +| id | uuid | PK | +| lab_tenant_id | uuid | → `tenants` CASCADE | +| clinic_tenant_id | uuid | → `tenants` CASCADE | +| prosthetic_type | enum[...] | | +| custom_unit_price | decimal(10,2) | | +| discount_percent | decimal(5,2) | | +| currency | string(8) | | +| created_by | uuid | → `directus_users` | + +> **Unique:** `(lab_tenant_id, clinic_tenant_id, prosthetic_type)` — Flutter tarafında kontrol et. + +### `notifications` +| Alan | Tip | Not | +|---|---|---| +| id | uuid | PK | +| tenant_id | uuid | → `tenants` · **IDX** | +| user_id | uuid | → `directus_users` CASCADE | +| job_id | uuid | → `jobs` SET NULL | +| connection_id | uuid | → `connections` SET NULL | +| message | string(500) | | +| read | boolean | DEF: false · **IDX** | +| severity | enum[info\|warning] | DEF: `info` | + +### `audit_logs` +| Alan | Tip | Not | +|---|---|---| +| id | uuid | PK | +| tenant_id | uuid | → `tenants` | +| user_id | uuid | → `directus_users` | +| action | enum[create\|update\|delete] | | +| entity_type | string(50) | | +| entity_id | string(36) | | +| changes | json | | +| ip_address | string(50) | | +| user_agent | string(500) | | + +### `invite_links` +| Alan | Tip | Not | +|---|---|---| +| id | uuid | PK | +| tenant_id | uuid | → `tenants` CASCADE | +| code | string(32) | **UNQ** | +| email | string(255) | | +| role | enum[admin\|member] | | +| status | enum[pending\|accepted\|cancelled\|expired] | DEF: `pending` | +| invited_by | uuid | → `directus_users` | +| expires_at | timestamp | | +| accepted_at | timestamp | | +| accepted_by | uuid | → `directus_users` | + +### `user_preferences` +| Alan | Tip | Not | +|---|---|---| +| id | uuid | PK | +| user_id | uuid | → `directus_users` CASCADE | +| theme | enum[light\|dark\|system] | DEF: `system` | +| color_theme | string(50) | | + +## 5. İş akışı (jobs lifecycle) + +``` +Klinik açar (pending) + → Lab "İşleme Al" (in_progress, currentStep: alt_yapi_prova, location: at_lab) + → Lab "Kliniğe Gönder" (location: at_clinic) — prova için + → Klinik "Provayı Onayla" (currentStep++, location: at_lab) + → Klinik "Düzeltme İste" (location: at_lab, step aynı) + → [Cila/Bitim sonrası] Lab "Kliniğe Gönder" (status: sent, location: at_clinic) + → Klinik "Teslim Al" (status: delivered) + → finance-sync tetiklenir + → job_files arşivlenir (archived_at set) +``` + +**Adım sırası:** `olcu → alt_yapi_prova → ust_yapi_prova → cila_bitim` + +> Not: `acceptJob` = olcu tamamlandı anlamına gelir; `currentStep` direkt `alt_yapi_prova`'ya atlar. + +## 6. Query Optimizasyon Kuralları (Flutter) + +### Zorunlu filtreler — hiç ihmal etme +```dart +// Her jobs sorgusunda tenant filtresi ZORUNLU +filter: { + 'lab_tenant_id': {'_eq': tenantId}, // lab tarafı + // veya + 'clinic_tenant_id': {'_eq': tenantId}, // klinik tarafı +} +``` + +### Sayfalama — sonsuz liste / cursor +```dart +// İlk yükleme +limit: 30, page: 1 + +// Sonraki sayfa (offset bazlı) +limit: 30, offset: 30 + +// Toplam sayı için ayrı istek (pahalı — sadece gerektiğinde) +meta: 'filter_count' +``` + +### Field projection — sadece gerekeni çek +```dart +// Jobs listesi — detay alanlarını çekme +fields: ['id', 'patient_code', 'prosthetic_type', 'status', + 'current_step', 'location', 'date_created', 'due_date', + 'clinic_tenant_id.company_name', 'lab_tenant_id.company_name'] + +// Jobs detay — tam veri +fields: ['*', 'patient_id.*', 'job_files.*'] +``` + +### Relation join — N+1 önleme +```dart +// Kötü: jobs çek → her iş için ayrı tenant sorgusu +// İyi: nested fields ile tek sorguda +fields: ['*', 'clinic_tenant_id.company_name', 'lab_tenant_id.company_name'] +``` + +### Finance listesi — tarih aralığı ile kes +```dart +filter: { + 'tenant_id': {'_eq': tenantId}, + 'date': {'_gte': '2026-01-01'}, // son 6 ay gibi + 'status': {'_neq': 'cancelled'}, +} +sort: ['-date'] +limit: 50 +``` + +## 7. Klinik vs Lab — Ayrı Akışlar + +| Ekran | Klinik | Lab | +|---|---|---| +| Ana sayfa | Gönderilen işler özeti, bekleyen ödemeler | Gelen işler özeti, işlemdeki işler | +| İşler ana liste | Giden işler (`clinic_tenant_id`) | Gelen işler (`lab_tenant_id`) | +| İş aksiyon butonu | Provayı Onayla / Düzeltme İste / Teslim Al | İşleme Al / Kliniğe Gönder | +| Ürünler | — | Katalog CRUD | +| Hastalar | Hasta kayıtları | — | +| Bağlantı kur | Lab'ı `member_number` ile ara | Gelen talepleri onayla/reddet | +| Finans | Borçlar (payable) | Alacaklar (receivable) | + +## 8. Flutter → Directus API pattern + +```dart +// Auth +POST /auth/login + body: { email, password } + returns: { access_token, refresh_token, expires } + +// Token yenileme +POST /auth/refresh + body: { refresh_token } + +// Koleksiyon okuma +GET /items/{collection}?filter[field][_eq]=value&limit=30&sort=-date_created + +// Tekil kayıt +GET /items/{collection}/{id}?fields=*,relation.* + +// Oluşturma +POST /items/{collection} + body: { field: value, ... } + +// Güncelleme +PATCH /items/{collection}/{id} + body: { field: value } + +// Dosya yükleme +POST /files + Content-Type: multipart/form-data + field: file (binary) + returns: { id, filename_download, ... } +``` + +## 9. Directus Flows (server-side otomasyonlar) + +Aşağıdaki iş mantıkları Flutter client'tan değil Directus Flow'dan tetiklenmeli: + +| Tetikleyici | Flow | Açıklama | +|---|---|---| +| `jobs.status → sent\|delivered` | finance-sync | Lab receivable + klinik payable oluştur (idempotent) | +| `jobs.status → delivered` | archive-job-files | `job_files.archived_at` set et | +| `jobs` UPDATE | audit-log | `audit_logs` satır yaz | +| `connections.status → approved\|rejected` | notify-connection | İlgili tarafa bildirim gönder | + +> Flows henüz oluşturulmadı — bir sonraki adım. + +## 10. Eksik kısıtlamalar (SQL ile eklenecek) + +Directus MCP composite unique constraint desteklemiyor; veritabanına direkt SQL ile eklenecek: + +```sql +-- connections çifti unique +ALTER TABLE connections + ADD CONSTRAINT connections_pair_unique UNIQUE (clinic_tenant_id, lab_tenant_id); + +-- clinic_pricing üçlüsü unique +ALTER TABLE clinic_pricing + ADD CONSTRAINT clinic_pricing_triple_unique + UNIQUE (lab_tenant_id, clinic_tenant_id, prosthetic_type); +``` diff --git a/ios/.gitignore b/ios/.gitignore new file mode 100644 index 0000000..7a7f987 --- /dev/null +++ b/ios/.gitignore @@ -0,0 +1,34 @@ +**/dgph +*.mode1v3 +*.mode2v3 +*.moved-aside +*.pbxuser +*.perspectivev3 +**/*sync/ +.sconsign.dblite +.tags* +**/.vagrant/ +**/DerivedData/ +Icon? +**/Pods/ +**/.symlinks/ +profile +xcuserdata +**/.generated/ +Flutter/App.framework +Flutter/Flutter.framework +Flutter/Flutter.podspec +Flutter/Generated.xcconfig +Flutter/ephemeral/ +Flutter/app.flx +Flutter/app.zip +Flutter/flutter_assets/ +Flutter/flutter_export_environment.sh +ServiceDefinitions.json +Runner/GeneratedPluginRegistrant.* + +# Exceptions to above rules. +!default.mode1v3 +!default.mode2v3 +!default.pbxuser +!default.perspectivev3 diff --git a/ios/Flutter/AppFrameworkInfo.plist b/ios/Flutter/AppFrameworkInfo.plist new file mode 100644 index 0000000..1dc6cf7 --- /dev/null +++ b/ios/Flutter/AppFrameworkInfo.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + App + CFBundleIdentifier + io.flutter.flutter.app + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + App + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + MinimumOSVersion + 13.0 + + diff --git a/ios/Flutter/Debug.xcconfig b/ios/Flutter/Debug.xcconfig new file mode 100644 index 0000000..ec97fc6 --- /dev/null +++ b/ios/Flutter/Debug.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" +#include "Generated.xcconfig" diff --git a/ios/Flutter/Release.xcconfig b/ios/Flutter/Release.xcconfig new file mode 100644 index 0000000..c4855bf --- /dev/null +++ b/ios/Flutter/Release.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" +#include "Generated.xcconfig" diff --git a/ios/Podfile b/ios/Podfile new file mode 100644 index 0000000..f11ec86 --- /dev/null +++ b/ios/Podfile @@ -0,0 +1,46 @@ +# Uncomment this line to define a global platform for your project +# platform :ios, '13.0' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_ios_podfile_setup + +target 'Runner' do + use_frameworks! + + flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) + target 'RunnerTests' do + inherit! :search_paths + end +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_ios_build_settings(target) + target.build_configurations.each do |config| + config.build_settings['DEBUG_INFORMATION_FORMAT'] = 'dwarf-with-dsym' if config.name == 'Release' + end + end +end diff --git a/ios/Podfile.lock b/ios/Podfile.lock new file mode 100644 index 0000000..bb6c76d --- /dev/null +++ b/ios/Podfile.lock @@ -0,0 +1,147 @@ +PODS: + - DKImagePickerController/Core (4.3.9): + - DKImagePickerController/ImageDataManager + - DKImagePickerController/Resource + - DKImagePickerController/ImageDataManager (4.3.9) + - DKImagePickerController/PhotoGallery (4.3.9): + - DKImagePickerController/Core + - DKPhotoGallery + - DKImagePickerController/Resource (4.3.9) + - DKPhotoGallery (0.0.19): + - DKPhotoGallery/Core (= 0.0.19) + - DKPhotoGallery/Model (= 0.0.19) + - DKPhotoGallery/Preview (= 0.0.19) + - DKPhotoGallery/Resource (= 0.0.19) + - SDWebImage + - SwiftyGif + - DKPhotoGallery/Core (0.0.19): + - DKPhotoGallery/Model + - DKPhotoGallery/Preview + - SDWebImage + - SwiftyGif + - DKPhotoGallery/Model (0.0.19): + - SDWebImage + - SwiftyGif + - DKPhotoGallery/Preview (0.0.19): + - DKPhotoGallery/Model + - DKPhotoGallery/Resource + - SDWebImage + - SwiftyGif + - DKPhotoGallery/Resource (0.0.19): + - SDWebImage + - SwiftyGif + - file_picker (0.0.1): + - DKImagePickerController/PhotoGallery + - Flutter + - Flutter (1.0.0) + - onesignal_flutter (5.5.8): + - Flutter + - OneSignalXCFramework (= 5.5.2) + - OneSignalXCFramework (5.5.2): + - OneSignalXCFramework/OneSignalComplete (= 5.5.2) + - OneSignalXCFramework/OneSignal (5.5.2): + - OneSignalXCFramework/OneSignalCore + - OneSignalXCFramework/OneSignalExtension + - OneSignalXCFramework/OneSignalLiveActivities + - OneSignalXCFramework/OneSignalNotifications + - OneSignalXCFramework/OneSignalOSCore + - OneSignalXCFramework/OneSignalOutcomes + - OneSignalXCFramework/OneSignalUser + - OneSignalXCFramework/OneSignalComplete (5.5.2): + - OneSignalXCFramework/OneSignal + - OneSignalXCFramework/OneSignalInAppMessages + - OneSignalXCFramework/OneSignalLocation + - OneSignalXCFramework/OneSignalCore (5.5.2) + - OneSignalXCFramework/OneSignalExtension (5.5.2): + - OneSignalXCFramework/OneSignalCore + - OneSignalXCFramework/OneSignalOSCore + - OneSignalXCFramework/OneSignalOutcomes + - OneSignalXCFramework/OneSignalInAppMessages (5.5.2): + - OneSignalXCFramework/OneSignalCore + - OneSignalXCFramework/OneSignalNotifications + - OneSignalXCFramework/OneSignalOSCore + - OneSignalXCFramework/OneSignalOutcomes + - OneSignalXCFramework/OneSignalUser + - OneSignalXCFramework/OneSignalLiveActivities (5.5.2): + - OneSignalXCFramework/OneSignalCore + - OneSignalXCFramework/OneSignalOSCore + - OneSignalXCFramework/OneSignalUser + - OneSignalXCFramework/OneSignalLocation (5.5.2): + - OneSignalXCFramework/OneSignalCore + - OneSignalXCFramework/OneSignalNotifications + - OneSignalXCFramework/OneSignalOSCore + - OneSignalXCFramework/OneSignalUser + - OneSignalXCFramework/OneSignalNotifications (5.5.2): + - OneSignalXCFramework/OneSignalCore + - OneSignalXCFramework/OneSignalExtension + - OneSignalXCFramework/OneSignalOSCore + - OneSignalXCFramework/OneSignalOutcomes + - OneSignalXCFramework/OneSignalOSCore (5.5.2): + - OneSignalXCFramework/OneSignalCore + - OneSignalXCFramework/OneSignalOutcomes (5.5.2): + - OneSignalXCFramework/OneSignalCore + - OneSignalXCFramework/OneSignalOSCore + - OneSignalXCFramework/OneSignalUser (5.5.2): + - OneSignalXCFramework/OneSignalCore + - OneSignalXCFramework/OneSignalNotifications + - OneSignalXCFramework/OneSignalOSCore + - OneSignalXCFramework/OneSignalOutcomes + - SDWebImage (5.21.7): + - SDWebImage/Core (= 5.21.7) + - SDWebImage/Core (5.21.7) + - share_plus (0.0.1): + - Flutter + - shared_preferences_foundation (0.0.1): + - Flutter + - FlutterMacOS + - sqflite_darwin (0.0.4): + - Flutter + - FlutterMacOS + - SwiftyGif (5.4.5) + +DEPENDENCIES: + - file_picker (from `.symlinks/plugins/file_picker/ios`) + - Flutter (from `Flutter`) + - onesignal_flutter (from `.symlinks/plugins/onesignal_flutter/ios`) + - share_plus (from `.symlinks/plugins/share_plus/ios`) + - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`) + - sqflite_darwin (from `.symlinks/plugins/sqflite_darwin/darwin`) + +SPEC REPOS: + trunk: + - DKImagePickerController + - DKPhotoGallery + - OneSignalXCFramework + - SDWebImage + - SwiftyGif + +EXTERNAL SOURCES: + file_picker: + :path: ".symlinks/plugins/file_picker/ios" + Flutter: + :path: Flutter + onesignal_flutter: + :path: ".symlinks/plugins/onesignal_flutter/ios" + share_plus: + :path: ".symlinks/plugins/share_plus/ios" + shared_preferences_foundation: + :path: ".symlinks/plugins/shared_preferences_foundation/darwin" + sqflite_darwin: + :path: ".symlinks/plugins/sqflite_darwin/darwin" + +SPEC CHECKSUMS: + DKImagePickerController: 946cec48c7873164274ecc4624d19e3da4c1ef3c + DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60 + file_picker: a0560bc09d61de87f12d246fc47d2119e6ef37be + Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467 + onesignal_flutter: 75c70a45a8d97e685273a14f04521ec121611458 + OneSignalXCFramework: 2f46ff87ccefd9afe8e3b5f9fe357072191205ff + SDWebImage: e9fc87c1aab89a8ab1bbd74eba378c6f53be8abf + share_plus: 50da8cb520a8f0f65671c6c6a99b3617ed10a58a + shared_preferences_foundation: 7036424c3d8ec98dfe75ff1667cb0cd531ec82bb + sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0 + SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4 + +PODFILE CHECKSUM: d106a30630b099793eceddfd6e3c64af8abe90f1 + +COCOAPODS: 1.16.2 diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj new file mode 100644 index 0000000..8df3ce8 --- /dev/null +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,746 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 54; + objects = { + +/* Begin PBXBuildFile section */ + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; }; + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 604C3E861E9F106B3AA6CCEF /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9221FFF6D01C1D6BF22E5ADA /* Pods_Runner.framework */; }; + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; + C9B4075C0256AC482F1CDD77 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AFD5D57E8CDD24E2D84F3E9B /* Pods_RunnerTests.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 331C8085294A63A400263BE5 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 97C146E61CF9000F007C117D /* Project object */; + proxyType = 1; + remoteGlobalIDString = 97C146ED1CF9000F007C117D; + remoteInfo = Runner; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 9705A1C41CF9048500538489 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 0E5C1D2C3B5E98A6A4BF3DE7 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; + 331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 468E8F6599C9CD0A40663335 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + 58AF9370CD56571E93F6935E /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; }; + 5EDEE6A633E332DED57944E4 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 7A46E2402004ACC00F45B6B1 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; + 9221FFF6D01C1D6BF22E5ADA /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; + 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; + 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + AFD5D57E8CDD24E2D84F3E9B /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + F2CF6120D4E22FEE6FB14E1F /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; + F766D9502FD64D820017E914 /* RunnerDebug.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = RunnerDebug.entitlements; sourceTree = ""; }; + F766D9512FD64D820017E916 /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 97C146EB1CF9000F007C117D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 604C3E861E9F106B3AA6CCEF /* Pods_Runner.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D66E69B2ECA4A5318EDFF043 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + C9B4075C0256AC482F1CDD77 /* Pods_RunnerTests.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 331C8082294A63A400263BE5 /* RunnerTests */ = { + isa = PBXGroup; + children = ( + 331C807B294A618700263BE5 /* RunnerTests.swift */, + ); + path = RunnerTests; + sourceTree = ""; + }; + 61E03C992BD404E58CDC96E8 /* Pods */ = { + isa = PBXGroup; + children = ( + 468E8F6599C9CD0A40663335 /* Pods-Runner.debug.xcconfig */, + 0E5C1D2C3B5E98A6A4BF3DE7 /* Pods-Runner.release.xcconfig */, + 5EDEE6A633E332DED57944E4 /* Pods-Runner.profile.xcconfig */, + 7A46E2402004ACC00F45B6B1 /* Pods-RunnerTests.debug.xcconfig */, + F2CF6120D4E22FEE6FB14E1F /* Pods-RunnerTests.release.xcconfig */, + 58AF9370CD56571E93F6935E /* Pods-RunnerTests.profile.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; + 6F5B57133C9496CF920C3E25 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 9221FFF6D01C1D6BF22E5ADA /* Pods_Runner.framework */, + AFD5D57E8CDD24E2D84F3E9B /* Pods_RunnerTests.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 9740EEB11CF90186004384FC /* Flutter */ = { + isa = PBXGroup; + children = ( + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 9740EEB31CF90195004384FC /* Generated.xcconfig */, + ); + name = Flutter; + sourceTree = ""; + }; + 97C146E51CF9000F007C117D = { + isa = PBXGroup; + children = ( + 9740EEB11CF90186004384FC /* Flutter */, + 97C146F01CF9000F007C117D /* Runner */, + 97C146EF1CF9000F007C117D /* Products */, + 331C8082294A63A400263BE5 /* RunnerTests */, + 61E03C992BD404E58CDC96E8 /* Pods */, + 6F5B57133C9496CF920C3E25 /* Frameworks */, + ); + sourceTree = ""; + }; + 97C146EF1CF9000F007C117D /* Products */ = { + isa = PBXGroup; + children = ( + 97C146EE1CF9000F007C117D /* Runner.app */, + 331C8081294A63A400263BE5 /* RunnerTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 97C146F01CF9000F007C117D /* Runner */ = { + isa = PBXGroup; + children = ( + F766D9502FD64D820017E914 /* RunnerDebug.entitlements */, + F766D9512FD64D820017E916 /* Runner.entitlements */, + 97C146FA1CF9000F007C117D /* Main.storyboard */, + 97C146FD1CF9000F007C117D /* Assets.xcassets */, + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, + 97C147021CF9000F007C117D /* Info.plist */, + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, + ); + path = Runner; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 331C8080294A63A400263BE5 /* RunnerTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; + buildPhases = ( + 416DB929131C2E55EE26F9D1 /* [CP] Check Pods Manifest.lock */, + 331C807D294A63A400263BE5 /* Sources */, + 331C807F294A63A400263BE5 /* Resources */, + D66E69B2ECA4A5318EDFF043 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 331C8086294A63A400263BE5 /* PBXTargetDependency */, + ); + name = RunnerTests; + productName = RunnerTests; + productReference = 331C8081294A63A400263BE5 /* RunnerTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 97C146ED1CF9000F007C117D /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 42D030382DFE9994547FE992 /* [CP] Check Pods Manifest.lock */, + 9740EEB61CF901F6004384FC /* Run Script */, + 97C146EA1CF9000F007C117D /* Sources */, + 97C146EB1CF9000F007C117D /* Frameworks */, + 97C146EC1CF9000F007C117D /* Resources */, + 9705A1C41CF9048500538489 /* Embed Frameworks */, + 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + 7B78FAA6FA9F3F82D81A2E70 /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Runner; + productName = Runner; + productReference = 97C146EE1CF9000F007C117D /* Runner.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 97C146E61CF9000F007C117D /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = YES; + LastUpgradeCheck = 1510; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 331C8080294A63A400263BE5 = { + CreatedOnToolsVersion = 14.0; + TestTargetID = 97C146ED1CF9000F007C117D; + }; + 97C146ED1CF9000F007C117D = { + CreatedOnToolsVersion = 7.3.1; + LastSwiftMigration = 1100; + }; + }; + }; + buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 97C146E51CF9000F007C117D; + productRefGroup = 97C146EF1CF9000F007C117D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 97C146ED1CF9000F007C117D /* Runner */, + 331C8080294A63A400263BE5 /* RunnerTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 331C807F294A63A400263BE5 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 97C146EC1CF9000F007C117D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", + ); + name = "Thin Binary"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; + }; + 416DB929131C2E55EE26F9D1 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + 42D030382DFE9994547FE992 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + 7B78FAA6FA9F3F82D81A2E70 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + 9740EEB61CF901F6004384FC /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Run Script"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 331C807D294A63A400263BE5 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 97C146EA1CF9000F007C117D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 331C8086294A63A400263BE5 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 97C146ED1CF9000F007C117D /* Runner */; + targetProxy = 331C8085294A63A400263BE5 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 97C146FA1CF9000F007C117D /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C146FB1CF9000F007C117D /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C147001CF9000F007C117D /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 249021D3217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + STRING_CATALOG_GENERATE_SYMBOLS = YES; + SUPPORTED_PLATFORMS = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Profile; + }; + 249021D4217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = XP74C5889V; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.kovakyazilim.labapp; + PRODUCT_NAME = DLS; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Profile; + }; + 331C8088294A63A400263BE5 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7A46E2402004ACC00F45B6B1 /* Pods-RunnerTests.debug.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.kovakyazilim.labapp.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Debug; + }; + 331C8089294A63A400263BE5 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = F2CF6120D4E22FEE6FB14E1F /* Pods-RunnerTests.release.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.kovakyazilim.labapp.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Release; + }; + 331C808A294A63A400263BE5 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 58AF9370CD56571E93F6935E /* Pods-RunnerTests.profile.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.kovakyazilim.labapp.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Profile; + }; + 97C147031CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + STRING_CATALOG_GENERATE_SYMBOLS = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 97C147041CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + STRING_CATALOG_GENERATE_SYMBOLS = YES; + SUPPORTED_PLATFORMS = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 97C147061CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/RunnerDebug.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = XP74C5889V; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.kovakyazilim.labapp; + PRODUCT_NAME = DLS; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Debug; + }; + 97C147071CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = XP74C5889V; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.kovakyazilim.labapp; + PRODUCT_NAME = DLS; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 331C8088294A63A400263BE5 /* Debug */, + 331C8089294A63A400263BE5 /* Release */, + 331C808A294A63A400263BE5 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147031CF9000F007C117D /* Debug */, + 97C147041CF9000F007C117D /* Release */, + 249021D3217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147061CF9000F007C117D /* Debug */, + 97C147071CF9000F007C117D /* Release */, + 249021D4217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 97C146E61CF9000F007C117D /* Project object */; +} diff --git a/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..f9b0d7c --- /dev/null +++ b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 0000000..a27d68e --- /dev/null +++ b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/Runner.xcworkspace/contents.xcworkspacedata b/ios/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..21a3cc1 --- /dev/null +++ b/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..f9b0d7c --- /dev/null +++ b/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/ios/Runner/AppDelegate.swift b/ios/Runner/AppDelegate.swift new file mode 100644 index 0000000..6266644 --- /dev/null +++ b/ios/Runner/AppDelegate.swift @@ -0,0 +1,13 @@ +import Flutter +import UIKit + +@main +@objc class AppDelegate: FlutterAppDelegate { + override func application( + _ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? + ) -> Bool { + GeneratedPluginRegistrant.register(with: self) + return super.application(application, didFinishLaunchingWithOptions: launchOptions) + } +} diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..d36b1fa --- /dev/null +++ b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,122 @@ +{ + "images" : [ + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@3x.png", + "scale" : "3x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@3x.png", + "scale" : "3x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@3x.png", + "scale" : "3x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@2x.png", + "scale" : "2x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@3x.png", + "scale" : "3x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@1x.png", + "scale" : "1x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@1x.png", + "scale" : "1x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@1x.png", + "scale" : "1x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@2x.png", + "scale" : "2x" + }, + { + "size" : "83.5x83.5", + "idiom" : "ipad", + "filename" : "Icon-App-83.5x83.5@2x.png", + "scale" : "2x" + }, + { + "size" : "1024x1024", + "idiom" : "ios-marketing", + "filename" : "Icon-App-1024x1024@1x.png", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..3b8f85cad86ed61be85aa5a18be2594a08e46897 GIT binary patch literal 326031 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%0A7n@J%}z)hOIJ62{FI49FhM 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

  • K+VKBec(sma`Yw>|Mle0rYQI=YU zf9-V8#bR<000bPi&<}ClAVDMCf2J4d*LXbM`n=kj!Hc@0E%!Sk&)kj|SxGIOMVOW;lo_Zaq z!obB`h!(&GuL-EWRZ39-JPfl#YTH8cP6+o~nbo5$#S)W|(HUOHxUX*?sQqLzQeZz& zNlcHAJY|xGo$=~iR}`!L952Rw|BcYv3@^j{+$VGU)CVZn|~d%BH>KDMmIO!Mn3F7X_}9Go(*XC#;AKdILat%ejA-x8?Cvh2I{F%%VM5J(S;ahoyj6-u8@D z2pHVBK3_?LT*jxSxu=gNl5IU=&gFp5i${LHmBos)>UoKTy~X%x9YGd@(tE0PADqvj zBMqks8le4I<^&3ES6HUwdcROa*bV@lFO(a^#V)ABIT~*Wp~^2FL2!IV2>b0kh^W>5 z4-~7P%}Q5k>(McmJEJt~BZu+jfZCd;U8%c~5+vb#(ZoWmJs9*J1)~0a@w+#Ot3RW> zUG6TFskkX0)zHXc|D{iFo|Q%~oK>Hd@JvvB;qK5+ z?@SytY1rT2ua6r05l)A@H3^7+@Ic8M*T-74fMgW<8ms+RQb2WUAgWu&>u9WAa-k;v z2Q0|TVw`Qx)8{RPDeLJ6lOUhf7L>c)7P+sfNU5~u*@yC)(r}#1wBAjOz z(tf3-BT-p#@cuZ#vlV|namoC}z+py4#?@`NNgHF}=hPtloN_pQ=RegovYs@U7OhRL z8dOdLxj~`X-ACrKQNGL1Xas%NI{Y)ifHfLG#@vd()%Bb0C4>C0wVyb zcMTxzm&0|W*y^Fy9)KcnEMfv*wKEQaSaEsuX6i2Q{r1evydU5?NUm@n@&Z|0SfcsT z#f}mK1ytC7D+5=TuYMtq{1HD&9a_KaRKWrBWS%LDik2c)JpG?44{6q;M?CJm;?)6) zjP6<+BtJggTA%hgI{xU_O}M9O|1z!k-S%$@y0zT*3DO={EdCgAz`XZB*B2eCF}`0J zC!bV^z$YFHS?uzvVnb@Ic_aHFg58+aHERYYlJ9s&wrCKVz~t!G?3pm@KJpM66fBJLd5u*~ zQZ3vy5UPG#nU?JRAv^#I#2E?Ncz;5qiQ4=VcEpCl z*2BCcg+<%8eYX~-GaK$U=>D%tJLoM;VDF*qGa?M0%@=0}i*Mwh@}`3F?qys8yvKR) z>2-2sqrk;0o=6b^f;}orqNc)^I^!2b+K?p@)wE6XgjBkB_;p{}T5ihg?!6AF=%S|5 zdy?rTk{fZqm$h#27wbmhh{?#Wx`k)XR`*n`lEptU>&5e&;htizmFxC&l{GrXrAUhM zY`E1CNNQatT$Lku*SJHi&Nsl5@M9>s+!|wFgQVhG7}1M58;-Gu7E@O<60n_T9eZoe zm_?9Br##tZVDdaIGz&!yCa{noN>f}8)oJG$tKf57CIp~S27$)wejl*1K!`c%F9>!} zw}1*r8R$iiUU#32P2=5seCYYFhFB;JJB}B{tLqINP!aD#fldg&Rd?~w<%=Pj2P_Ei zh%&?mp)ZHd#WLv8sZ%f7a+8lFZR}a=vY4Qk@!xIz?_%@8Sukp7_7YD>!9k_gdmQjoZsZ_Evi zHNSb{UF{^0A*{OBORi2*79bC{&MO zn0xEMr*YBGV7CeAAfn3RPLOf54{l;mCt8kl52wRLe%SV925|%!2>js~f%T22=Op>$ zY*Tt*IiHV^m7!5N_-qUJ5wQbA*!WmgLW%CW(nMNGK5I!z(n$YZOytaZPW<;&GmQ)- zhQ(MGx{t-OK1*91;htRE**>;0s$F2+*84IwGA16JSY+GK#lA!^kn_8&DZ|z&rpYO@ zbRGvEjKeqCGs0a1&=oJs(;$TltXj$JY#b)?5wg@i;Xaam8Z0_((CIRF5lFSA3yHjs(GFGy?*#cqDfov zsC-_q#KV%PPZW4$P6Yma7U~g;QvpQT&8gO;aj`hT;Gb>#tA7kF-WO!t*K7NFR?__> z%+7q&85inVLBsXZUz2E~V^`-{$S4cq+ZSI*Q0A@H4f&Ggjl(fu$RmqzqvF3Y!HL%f z0Sfhbn_{D1-{C*~H7LEozH-a}Kf~}hrYqOY8@m|++=pA($B1lan*DPa-QTkLzo_tk z0OA_ z!^+;4@!0TJYg$Hz$<#-g8`y=*PWKv$Yx<|e#NV6<13w1Hp-nA=Csrg@Eav!cgSD(UqPCkT2S}~Aqjwm(|(oZsE+G=gyO&a_vhM`Do z3ZIK<*?cT-sS`WUQsS#&d@fU35YzD`uCk=|zPH$bujYke&4VR4pub5St zN6&-?T?@tcdQ%Wq?7Qv7qcG)Bc#4#Fm=yt*M#C+h9OtXbqaxs2_8yIUpL^{a31uMvp})L3#zdleo)4lw5^&P2?)1$00`HsLv{ zwWTZrBKDW{#)@35MVX&fNRYOhs+1+Rt7JSuTKD$~(!q}n?MX=M1+2}(NGTfCox?{X zBv$hpceiqm!WX0?`pmZ+wl1R041zzXibHd{yH|c*;e|C9dlX8%3#~cG9m$%g+N%G#DhuXn7f4E(oJ`-VrU3GG z_a0zv(q90XVA1o-iuY24a5ZmCZ1g?b-=-!0_G1yGlR*EsbiF)E-Zf%^4_+>y&eYTO zdY6n9AWvN%?D6`ESSo{0Ds-(LJxdD_fk+Z*kFb$@Yh;*x?1?v+<$fO!WFSA#Gs(Gu z&DKPSLCW>W2;D2-_$tB~K7gHVs*4TSs-|59Lr(ls{d{MI@;k*Am6WVF0v7p*u*sYV zEAD443hZFY)uCc*KDJs*~W<+$Z>Y*eah26I!`#HKe74Qc+|rxp!jE(yDsxm&)@eUPC56*#0F>Rs?m>%wDNvaisXyEX-wS~q?a z?=9~?OZydp(ur$>J(!5l?p`hQhnO4{fe$KOJq${iFy1Do7%3Z$TsBY-*|}R6-~RWs z!&E0TEN}Jr1Ijapw7M(zW;Q%oG^6{YhuBoZgRJ(T!=L;8SRZOv zerg{sXFRCFk%Gm8YTx^xYWqUfPPHbEYwRqZirHHUm-6}b#M03xKfKgaODzS%0C>iM zgqo%f6t8!Of7PZVW!9M5-%dz30hE}nAKeDrG#DWbdP_q)(Bghc`M7_$fWBXP3_p}` zMlj-%G0(&I;N|35UgT|H2eU|p_|VSTf*?u=5~q6F{io0^d|o*q#ZW%N^6*wbt`5ZN zCZH8F7=UVN`y}pa#D8N&`O!pW7p8jM>vK!!xxfq6g;q)lQ%7_DMV*xXA176$vy9U3 zcFVKp8o=Xb8Rc)Z#zx~XEl^xpWW0ESb)L$rWCX`PwTCtG0fwOoF^o-#GX5-~=h*bj zxxzjkm_%$5#lA9D#Y%yFyj*f}#H(p!P_;=51V$eH6u9h3yIr-fKCAw^%@lTaWkSFvd+(K>2K#Lk{J#@QqG5=!enb&=9zZXch zGfP8T$O0TP`1TRF+CHq#UbCG(8rA|y)pJfZ@J^(^vpz4?Z4pR~$K{XjgaWHb z3~F;aV{cS{Wzgnwh&KN{3sq6zck;M>5H>_#=uYnNvxJt8Sq7uO=C7& z$2S@|%G1;F>m>>EvOQa*p!>Y_{HOC3!OLF8gRhQ0Qe5+S;Q?z}ypiUi?gM0By}NzJ z=G~?Mb_~%^WJMt|60C|Gqxv zkO3ZuKclks2VZfK;j34p5O_p@Qir-$3S)Q05&?js)x7}JUM}y;8GS~lOM8Jf>@-0r zI*)!}YFEFadRZsOi2urZ1WDW1GLe&t`$ssYoo8ak@Y#djyRT1W?N86{RV=^lDs%Gk zqg+Di?_%X(#MV6$U3GdcICMya=7t538-9T$dal@ECgQ3c>CPUw8;R(hXMufBNr)4( z>$SY0tAql$?q6Ulvuy>UwlKS!Jp!R_*Z{z9g0s=vD{OTn+jkzPpG zmJshg{Ddm7UdH}ti$mdcguHal8=ZNXd149=jhnQ>8t*e?ud7qDW~pX}$EP)TXO)c> z2BjTkR-1h(>@IZ@TW0{jWS6L}eT%4MN1jQCMu@Gq>X!dBk165eR2mUZu1XcQ;`H1X zn5tLS4g4t^e#>`K9|L)UC#gQ{+F%0-2*Y93xZ^NtXzquKbVN4GEigy2mpsX|73+-m zcfI*u3I_BCXJeiR()~eXDg$=B1jl#{lO#T`H~0yldLK0e9>~oKl|NRLqNWBW9lVni z-0Q~`UzerNSHK8kO>Z;|2}MD4&Pp!(1S@-WWT3UnO3ThDu*h*8SFfNXxK zuhAE>XTavPd)Wj$HGu*p+ev3`{*)W zMBJyc^91jlz(b`+4u#b2?$};m^?may{oWCEjoFtM=hfE))mQtc`T}g-2`s2h_nQH` z7VVr3%*P(lHQVpZ6;|(D#xze~=HF@W3sU4euthQ!5hz3B#F={x6?M%Vt-fjIu ze=lf@Qf&n1F|lqbe31{Wu4t#7!Lg8Hyk$Ji`AZ4!@NvtYYzbob3b%=%;g)WJ*^02B zvmXL3ULw+82;B=!K6`=NYl{QdHThvU<;j6q(3m64{gA$%(kMdMMsO3la>m`iB+R(q zNp~fKnhc%!4^H*y`^~Bp{HFJm_%Y=pLb3o{M|IGT;uJh^M~f?N9>9NThuWs-9X-CavNjSWvf5C-ya<;0w)4}fHrrYt2yT26G*6kT}=#tSB%kuc2 zo8)JI;-T!~&s3LnOfG-2y&faSX9_Xua=LH?0el~#Ak5e0e1+u}DDwFJaN*uRJx&H6 z|HJ?Of@>cy5(TiWRy{^i*sdpn)>KmaJ9)sF2W8ojo1GM<1xbpY58J8jwr$HZ1IE}8 z{YyKb+OF>;oj{s)lArC2SO>;_1R}K^ zj|yN)_Yj;S)eIuJ(A+*!khQuM*%L0}-aH-1+*kakw|AbA0PC6gKX}r<$K}rh{{OBJ zveL!!{hpAi*SJG^v|`P=6eVnLTyDw&w+(eD1d-2Q(Q1s*B-mQyMren~0`X>?(x+-k zYW&&NRmf;@nd8@Z#>&^_4DG4ai2@A25p|chA2Pa(y=-+}qAWhZK>#;$91EZ&7E18} z=dkkp99Fm#%*a9mB*NDwn9|ASMK2LDQahHcdM#^t0n@G~gzzPs2=3Hw4ai{Rpk5&M zygps&7Vwwx4x}Ikl8j&=@yiLj5Mtljo(${{7u?P|(yJ6I7Xr(?rwwF1n81)bAlj>M zvI6|?MnEECFAFC)JD^}+IX{!BCWl`=pO4~@uk_b^9EHw0b{V3U4f=Ve&hxYV6#p5K z1~YZo+vNZF9|@Q>zUwb!e6czZgHnxIo-+iU|Enm{s{WPtDMqwWsn@U$o>cQrr$~^t zezu5~4VWz^llfEcK<0(!0@zEtTB4WUa*Q~ln#Aa19|c9f*IDTD4SdF3c_=Yk`Nena z(VLe3>qYOz=WpVjI+W;rk?1Y*dCQ?4U`U`1kF%>X)0e(6ILBxHAZpNp?w#_qd&DLz z&go;J#zHl=DV@BM(J9N@#hW`D-`LE9Y&liAc9=6y;(oRSh8rjbmK~^#* z@UZs4{g~Q?Bj(riHG)l~iA+1q#LCp|at|iQdKG5-fDi2huHQ2gEWOX7A&~-R=3Q@K z|Nn*()NC`j$iW645)b(Gl;3t9Iwa*C(viFw6(9-ecN++F%X$wf#HB|x)dOoIgN?T+ zU^fW(WhU6%Pw+MBRE)r=0to_^t@%?qcdnw*O#`+i~y0g+$ zse8dhp+X`X5Nw7p6ZPP3KioLyJJtZB4Y7&fatuyY9N2IAtxvi`?zPEi4UNrbZ`Ud{ zc*lLia&LeAb&ZH?Yxl#O=fxgB*S%Hq2%Hr)4wc?qZF}RYR;$9+sZE0k0Ggq%K^40# zn7s$hmaHR|pan*lqgRUrJktB@|7TS<(~(xVeV|A(1(j8nI}XUsmS6!lIv^L;k8A_( zUTyHf4l4%iu)+aZ$kGOK#0`pD*>8J%EX2(mi5O`k_miZ8+PULc1j#$FQR$gwJkh`C z9u^$g#JcRj5VfG;1w^TVXeNtNQX^J0IPOh@q zR%NeiQt%)H{9}dxw6H^eQ%>a4?xz$iP~>v>5&lQ*cZ%{XDvI>mM0qLAn;c1qLWAc~ zjgZ4@*{V=E40zD7jp^~6@;EL*mHzH+p<7XqRoNxED4+xP&laxu=6XUeB~p4n|4S&B z)9#1L=f!S6%Y5?hh1XKO^kyD=Vx}(+b#YLfiVgo2L*^2*z*9ALJ`y>gAN68yP`>sp z{JKr=d9apH!KeBvJOrLsX?zI`#Fi{ZXdqTD934S&hRL*dIQ<5t)9<;6UxMuKtK}n50FfK0!D~FEUW*V_n{gPK7i&S)WoER)1Zk31hX z&Yd5W9LhX89>KRroR~-jp|Y={JQlNlC|W!rx&FYynr+;hC@MyyLYHurPi z`sUr`&&J0ObvgnJFI%W9K9H+WnsTm^$h<8Z+lxfevPm>M#Wf$TPQOr%cR{r;e~l!S z^p5EoO=0=c`aWxgw3%qMy?pVKTF&!{l3MtxDiStl2y^4vpfW=}o(HbiQ`X5)1m^}E zu&J+U4L1c}d$Rltgj&|T*nnG7aWo;olnSj%@P$CD7kM{AU~8UjQ>R_qj?tpR({az7YQ>*Xn1KxbHo|T;Bmv=DrTVj5O*yuun{%gcbp(Y^cukEao)0Ki0!7@IuS@$&r zJ*wR81H3#3fl&05t(=sg1+F$C(hTApw)3_qzxBLV0Q7_wf&8puIK*3&_U^}8N)6k6 z@@Z-s;DDkbYd!$aNz?X;-KB_KPwQB8Z-a+s5^|A&XH)Eri1K^&Wy(ERsP5?le)f0p zJ!tk0y>J8H!H_2IjmmE+{hN)C3(%oaYp9gPwBNGhX%w;cI>&kQf5DGRvM(-yv#d4r zH9$q6&s=$qrbmdYsaSo{3l~u@qkiwk8o@*SN-+xBnR=4ql{H5}(IaYu%{;X|-K<-s zt%jGi;o)|2xh0u*cyPM-5Hw=aYIBlzV)JHJ9iwfj5l*W5rPO64+&v+y9bv!w9_0 zRxongoKcu`nzBj~`QzN!m7zF34D{$SGo;NDyf?VE;AbPYh?t1cb&SEY)ZIjbbhj_# zBuSZy;oK?|d7Qvw)xVsI>20%V9Ia2CDdJJK!|_2}LN(fVQCIsh2W(I0ckyK&$mA+b z(Xnr($ch#SBBQxlMi-L(%3^o(#^$S0y@X1xX?7sVNS$CMTeXqNX&!3p{B*Z^u~fZu ztleUY1cIP}vh`spUqb)-eBN;3If?d}RIri68P>f~i`JpoP}0K{RrFVLLiT$bi$B@E^G^F9kf>p|JM&t5f-L!U-!M5cap(v0PAY{S4UP zoJB**)n1Ak(dl8fV6`ANvXG_Yi?|O2;#1(G-Z-kgc2N4@I%V!eK}gMKWv1I*`0x9sc!070Huwi)@cfy$;Wo^EpCyHV)Jrs{?Pv z{+^#q+N$XWvAcbfSKPwe08r<1vA9T`ZR34f&C&@?*Je0oohxc1D+*VpVEP|_76N$k z1G)IU&v${AauaOw-1bIXOHhjn>xSySjyKsY%|7>iffm}M5*KHz5mKwWSTWpO3i$yY z{_sg<1sOE#tJ8r(+mypiALPg7>L@S_x&8`K!=y5k+qMu}!WxXlvKb=_WG7kB1ivfX z!C!&}e#%`dHUUR&JeIo_t%D{v7K2KZtAJ>-{Fh20sZc^+!@}DVbXwkn?wA~LSKckp zTAoUA_o!??+YDBrQG+(hw*-L3OAw9)Gw6%UXaXkVq+)||siY^8>=&Hf%?|Xly$lL# z=#+>|h0`zeH-@n>7xk65t+p7g3F!q?JY*`(z|Wh|M1k9YH+)BUj&Fqz@W#vbs0biA zWxY^CNAZ@2a6c#|QVcr<+cE?i;6!aa8Yz^7jChLrJw;m#zIJSBQFHn0fR2>YVTd!TRAF%`#Npj-cszd- z$D)?bI7Q<{SXN%Vbd@E}Z^F3o!Fd@R5+jEsHWQd!49uX}9zqyG`!qtS-|=7HCetDE zvImiuV^(vIDo6XZB}LHEJ5i8)SwB#jeLN%dsNI+X6D%FVBpLu|K}v#-7`&6k+Pm`K zVLn-J-;t|qLx(p4*scfHE;zl%*Z{RZ_!~bv@?H*?*&s)Nr{$QU{|OZz?mb9 zA*2cv0&h2YV8=PhTI0OhkPVlkaAYDyp%UT9zOwi2rQpD4>rVNZ(_AgU8PYdYj5cP3|)KS@)8JO5TPBrR8^#AH&P(NCWL&bq_)(%+3ar zZ_wPMX?e5|USnc^Gl9|s7u){Beja$@&wDihENafsjRQtV>R4@b>_PY(W<7a#<1lvb z^G6EfhSKok#)HTf?=@t3!WeV7{Ny`i6B}WYl3MrQn;x)KGt>jm$S-_5lzNVsl)}ju z8~3Qh+{9%jJ)h-5#DjRi2@}XRI}bk*WM1Xw7e{#E`bOx-{$7ZUWs;vB^!O4h?mGI? ziga$JR_6c*Q*6NVdFq| zJ!|hd6-R}i<>|xMGr=aJ^{0XK18W`owjF$%;qis%7bc+5pk1c7Y9o~P!<5>m+FHSA zv@!n|z`WvR`zK}skFf>OiM!K>7J_-aGcTxy-nqV2GaUDd`+Zulmmwa~)ngykg*giC z*#0#$+k8-`hnpv#-9_0F)RWG+D1PT2b%GbK%X=fzsMG-}xa<#B$VWu4?+sqEFuzow zH=-=dWgEoRq6A#xtiU{NmAB}k8AGyrmqM7zJ@sC0h16Se)OXzFfb|QwwcL*ICYJVe z@{3(NIYE-T_I8o^{nMW(f<~t_{TU0Du;~5>jc`K@?JODL9GT(Eu|ys>G$K-QF;Fi{YLz4oRPh(JP&)5qcEX$&I4}+0b0aS z_^1^{8DUwtz}c4GksVh#tR+$eiL0$WP>8typJhG((2Etf$vku%{IMg299ruK; z3ml{#SR0NXU8Lz@=R!4+e4f*F@(Mmes6OA3+d-Uh(Ka=e9-lqCdjs5T&<>0hSy&Sj zbZD=MUmw^_12T1C1MjP^zxkHk6B~=C$N^>&n9LvDGp4VUVBsxy6UN3OdLNS+Te0J( zY;GrGu#YeY9?@ZG%|hyj=~a~Mg^#K~cnihM{bEH5wm|FXWLl9=r?7C)QF zVDZSffDR%pl&#RJlV2$)V7jaB$tH}UEij|jUhdqAVF16|i2A9;?fp}{*W*j~rMJR{ z?Mz3V(ZHs{cGz%eHXTX+;h3jlUJZT!^!4pl189+tcMT(?`kNbcZwTMZdjwz2KBg!U z-k%jS`h^3@(nnxrUlb$s!Pfx)!V&Pgd>z0$-k6l20~=hK*`968O*ZXNud;0?1Wn$n z4O&jfLz@Y0YX#$0D^Wy{b=l%=t#Kj@+=z+yPWGQ~fl|A=;ed?SfUW%kYUaqu{Yi|W z(kU<2ci^{zvp|LOYVNqp6s*E=GGC9}FlP}AgAMGVQpH=zt1z15ULu+mWfN^TXXq|x zY0jq!$}`0M8b~Fjz{^Bmj2XfbqhaKpAHdMC+m|6uImxrZ?%Q+Y%v^hM9e-m%Ff0G= z+S&=R(W!60_TgBvq^J?Q9LfFzf-!#-{c&fPvBcZ5{=H-ZfUgK^a&@GRvp|~&z7yd% zBO|UWlHLv7qtov*{{r=9wyvl&+n+F$D;Cd!g%%*E3z9w}9Dr_L5W^@r&izcp&2d%v z8o~iMAEL{JDQ7l_B>ci*-K;>=Ncc!AxnBrmb#c4TV)#d)+p=U`xVDh&xrqjk1@kiz z)EMvg{DeLj!*Q@_s|9@J0=l$Rtn7O$Dh2K-TP|iIHb2ECK%t_T%PdPtxdNxl>o#z@ z2+Lh%tZJO(tO*G^EW9qi_gQzTXQ2?t74;6PrUhOtP63gynry@-Rp8|--Cyat%Lyok zlUeELi8bwIi6;{j@sj(<8tRbBs|%~I(d^Po;}q{xtg8t zy7tqX#OU;O|J+1oVFc+zl6S$+Bnij-8T7~LTmguLB0xmoqaNvj2!EU06Rc`TZ-z`M z40fNQgH5t0Ii+FE&23Pe|8%x+#y6ymTXxhGA_RYn z9)=^7Kp=X&Nv2zo5vMa#_=ef9eh!J|R75baH(9<9iKcPO2^@vbww{%Ho z$EFWq=zgHf-ujOcZJsJDEDMf(Dykozs)Cks&IcH*r!o{2^f&3vFU7rH0kg9l*8uF6 zw$Pa7%mzs2He@~Nu}T&g{AY7Eg6oAiN;|lHgckb(YryX+#t5CwztFbYr*wa@TKT!_ ze&)C4!l~obj1SvAoBCJZbTG?At6QLATi6GU&hrIn-m*cuRDM-PfQ0X?)Z+dndX}$f z1lNm>OS$$*z&?5Zft}+wwe_QC=R}d)t^leyO9NJ;(HHQl*LlgGDZu2jn*L2&7V}bT zcCY}DfVr9eXKt>7++ahFJ}f;FGRX@LDX{8T`VJ!JfWuYxy^RtHP?qq&C`&I1{sBtd z-sk-!3>99%bxOZgjI8G^AQFDqd$i;OH$+DkHn%Ik%+}tJ3eCxOd-B*u#cwc6k6hrKQ_`6SExUS-#GK>` z8IbMSnFq!o-068w&=u^I-)`J?YT63+AcigB@hp%SrxyU5pY(4tj z-;qt5z&XcuV*C~L;Vth6ERCZ$Yx(ZZe`86%+p+yQmMB&lnj9~{F^YRn>(qdZkp`~$ zMO$ya9rt8{>WqDiZ3LLOc~eB>;zj1TU24eglRqyN_(Y70Jd6Vt<3Gj1pY%IHx4Ga= zo8E`c()|$cgiaJ5TkrW6#1Ls)CiH7dHC|Zeye(219zMO0+I+uG>2Wimu;~an(j`ywXqIPu)~&(uDI;<^y7^rQ)ci0y}FDXN8Ua8%hY)!ag9G^pRHgW z+i2hG(&CuvNiCk9Z!PpTc#%f>wIO!v#Y?rJq~tiMPVjrOZNiH-iIrk^Li&A|GvX!o z=JfI}>CX|+eMA*~GFM`M<%0wRTZ;+?D(LPWLB2CQ9YDqT9lHI|)PDNP7oJkZ0s(Xm z-TT8-mR!EL5ECKtT@Gd05kbZv(#4K!nH;=*VV8Fq98v(gsucNrGqj&BxUCB>UqDDs z7KG%4rX?Q5{w_lPq?N{(VS+f}(vQZ)tk7LNB;KB++()MWV&7@JWRScs8+=#Ro8R&} zbEM^`qdiz8FMgejuL5@pMmDy6hCy9D+^Lx&tu4?Jxw{T#y!tMStd~38W{(8e0TrEkLlHJh=oan93n;BKt z8M*P1hI>I|uerutBGk<@`FVE_s&9#eapT+nlK9feDJtw62YYxVm%g3QrJo6(o1Kzi zU0juL_SSsUxJ37n717R?UPU^Fvi?y>X-#~?=x37oda(jEsyWE4AyZm|-H}8c3@jc5 zhRNn7UebddlwaS3Kvwv*2&Wet>2AyanBo8M>~n2jo0vJAP&ITqdFb5>jYs&O@u+S) zoV>Ud-7g4tHHevmvOlKAGvD!$C~Pyf4Nho zS4XOSLBbGB0_34eD5a_BatpMw(Vpri(?;wzhZ64%@XUnyVVDrPDBP-f!zi}zj5;&1 zD@2$+K&5&rXM9`DDitfL)8(^r79M>_6d#?&(_>6(x}>+r-Q~>9%rpVPhL;e%+^4DQ zdeg};g-0+(_SxMd%Ra;K9_o(%J^))j4!WoAO4`wWR6QtFPGQMD`N}ZiL zI~nbj%svwAn0bGL0rOeLTcPppk{N6iJG24Z=n9^Vbq)lnM!~zze)1ZSzC0NczE0;x zA@Ta+zDWxChx(rJ;8|*c>kiuBn~F0md||wfZ#y+8 z3P9@t_!=}8Q=zd4zV7Nr7*WSx4I!h-c5WH@e92H-nA3plcu(U`Ua-P4avldgbT4AP zSa8kOd8yoXakpFy@Vyd!`?bYdlZ)tQ#L~dZsR}JS$e(6In2wkUF#w!T&G zaAZeY)BX-l=;j>uhkH;`{ooSF*G(R4$tY%($J`p$jE<8Ovf`O})T{-sYpQNzIq427QiGY;9-tXd%$XiKd(HQ- zzT4P<<;FQTcEy{A#G$KUib^$MFl`?oRdh23^7mN$FkvJ$y+1v5Hv(PMc^r+xQS-ou z+2`v=Wc^mAe&msro!6v?@&-V?k2PCzbVN17zULqRH4#$~oG&T9;<}axr!iu=(}-B^ zAgYbfh6O^Hd1G1V#>k)_4GUkL+CYsuxtZEN;;{V8TkIYr7-Oyjpf&}qt@5wdM)S`7 ztF^g!t4SiBX6D|tZGBs6oQQLgP_AC+5qjA(5jW-`FM&9I#66ao&ljSlEd=l5{M`bhaWeb(2`S>2UM5RW%3)K=`@7^v$}aLfd!Pr&MD}Zi46nGSH)MZuRm9;SMF-7c zd0AU<&$EpQfL|r8OK|B*CNWMj4)#3^(q-&?tgpam=GX z`zSAlNsO@zT)rO5{jM=^qsUN=VIH<=PRnqsIpRAYzD6m=nTTT#Y}c-O49xdN_)$>< zWLZsjco8TainOFe8q{YV_+XUa`&P7!91mJ4eVA|&qN7bBaE5L!JbEVJVF`YgIXO=4 z;7xP)VB`R=bjr_G@$Tsl-gh=hyR!1NUwi+IzwM{YpPq50ze27Pr7(yJe~vD$vf9!s z9N=?~z#E9()%>Zimw{1^_4k?^?HK=QX6vZ56F##zcC_dk?|@C%l4ra+6DYTqoJP+btt&$L{(0zs+=w+>b2 zQ5Q026sL3NT@ub&-4?zrzmt1^C}esRP-~7AWz=XulH3TAf}vK97Z^=1^M0@` zXFJ7&y!ytwI>y+>kZVSEKiDUh{&#=4QA5G6XX9Jy0sQs6#~4Ec))&Py)0 zhLPJ2+>&?btQ^v1Pyy3$>&7ACK6%|SI-Le&IfI{dH%ZX!ZO3M|4%M_}D0`w~|8#Sh zOtX719gWiIviYNeEnPUYnz!}ezm=QlrJCm92Dj-&wyCyix@+>bHhbjoA7~zNu+4?e z9|aHF^~lpao*ZKMlOmB&P>^2vTOiIHH2VXAc+3GIItS$LRc7$%hAu3lg5-(t{45p*Ys73vQ2 zSSKZQi**GFwd?aZ*dol7hsk~!CrgfB8^yg9KYe`AzcQxdBNunu&paigGGb9L*CJp@ zHX1XjNfIRKFYRXs0#nbNaCXXgFbKFyU=S7^pJwh|enR4FZei!xtG4d>Y--#orQ+Z! zP==E?*j|5xAtCL(l4^Ii#Vu;9%a6I?xhx7IRIHP&x%MUw4%TTVUia4oY?QO7ZPn7| zk9|A!AhB{35)2zC;q9LXD>1kfN?Dk|S)e|I%IbO(NG2ZENnBaEm*g2kd_uNO#;%q%0_B1g!)#?fi$(`X^ zs-QB-RDN`Y6Z;zS%=rfR`9Yw6F1L?)iFXjtn19f_Yrxy5ap2wSeZ~t9%G~%rltcnc zqACNic)x~GH91fFt8p)~jsaxrLBr=e<*F)>v5cE*tW^#yd2$O1%W1h|+^z=;cg;~U zlssn`@KYr^2<(DGcjt*xw61=kxL&2G4CHw_iYUz<-rS=tPH0SCiRoeWerIy$b?g~I zTB^tvZK0U266L|Cgi4+^w*$u}%=x~OL{8eqxldEnmb3VqRGbN1B}3s^{T=&1DVL&mBIibb@_lqRi|im(xi`c^9flU zld=;0*&9uPH;d$s0`l|=zDp}hy{g=pWwX+YDBJpE_Wv0B>Zq!^uHOSFA*G;nN~wT! zN`o|rfP#Rev`8x{AT1>!-Ho&~N=hi*Eh!C3$D!`p2lVlIzxTWM{^uBi!?EX{^H+1N z9nzNYq$HO=?^{}(sC?IxW&@^RFD9!yUpHZBp6cD8WQVy=lofOrn=T@Zj zl5_UI??Eri>mBWT_m6gwpuRU{cPQb;1d`y)5oR<+>S?eaV&L-G6!=J z9Cw=44%AZup!a|DJ+8>xRm*E7o4yW#&AsmT6)%&;siRiNM_sOTyTPJopyKY9B!g?S zP>#Vlo&Iyb>N9U|qh)9ycHt9Kv0>li6go|;U`jm<)xfyx;L*ci&W|V!K6Ck~e%!ra zifLOOZ6IBsHpAYSyj5zGB*Os?Er8?Z^Kfl2`Q;!;Ob3&{uu~jhYW+X&{ouuW?^9Tb zWQEP*!`<=r;O=0ld3y8%fpZK%uP1y{x@HS*?sAM^LCAVBarjN4c4C8O&W2$>PGHLj zBtg=?OQSo+Tq!1>Tm@YDmF0lvV8V9;G5$56@$XuA>3q|Q^bX_uE6{@nZ^-zf?65iU zub7_eVE$A7rj8T$mM}+JmOZUZHFdl0Cv#bugh%?IsPKH_6MCvj!B7__7;4YC`StUh zc=KC!?;W(1(Vp>ERm~%ojbbhgPhnZUaAg8zmHM5p z#UeO2*~|s|>5w8=KR95J#vvcyQ*gQv`1W=E^UeMXb#hyi(@uceP`iPt<2eVWPIIM< zHS-^a{%VV^!6*KUdVJ33_k}YPW{MEt9%=n#GiB6KUj_FrEY^g90ImEr^$aQJ>Z&cV zfuYF)5+kK~xnwN?m^1ya7Lb0@D7dt3JmrB+pclZzx>&z}Z_jWv3y8R!6ZT6wFxJux z01b$;rHOypYBcvK7K;UUs9liBmn(6c&`y3Td#YV?7wN2?l^))rYBaD@W{G+zWnvXo%K9424d7JXK z&1UkzWGuNOips=m7v} za-5j^PC@O;Z+aPz%t6>tN$pEu3hAX#uw*g9@UHcnaI^7Va6|E?7w&aAy1tFLl%G2< zT%qRG;H+fKMiIGlT~L zxxb5W)cN>@;MWd|YYtQx+-t8(vRp~t-Iw1Y^OE6o8eJU{lfyKI$WIy-4@O;8#a*}S z-*V6Mh#>0s&wn!@-d(P|d<6fw(z)kQQuh-&89N-u*}h*BRxF_LxnTN4dV5SK`-x6c zI}sx&hB}BYcQ?ym;`m9m9Jp4Lrn4$d`@(07XD4U3^eugn{A5DKfa|5wfzouIa|Y~V zqKRw5ThtE$aE}S_uT?uTf5rhj(f_8P^g*qmLSfl~w-OM3%mw-JXj?UBw)_sfn8NeX z&u#aZkNS}(fo}6mR*j@`H zwJ`~e^g(8fM+Xofu!jG2?nlz>`5>C@6ua+4?TRw+e)8_O@$K*NUE33XFtDaAsyF@bWm*4MSw3aCBBy#!F%pyN;RXFg zg*wiM(n0@My+Tn0D@|~bDWYCvB{05YInVgJHJpp;eu5=X4KSCi3)UZP+)%ez&A{qh zeZ$iC7ar8U(8Sz-YCs+Xz4%|ymQd>tq=-=&k<)v?F5>ll|3xFv4n9VN*vE}B?LzzR zDKS!qH~%fZ9_Ql@@4o(X=L9AqEAsF1+k$duDBTa|WiX)lQUfhP@1C>@6IK5T|E<3a zGn7;Avxb{i)rn=Tmi3IFE6fl(P&$5mjX}5*iJ_vag8puDP%WYLQhU)glD;Sv-37pf zto&49tzMkh)t}I1sgKYgdW2@xWH!{XInGM`J5)&{UWzS4PjjD-lxVwu!n9^i}l_EOPmxxEq)8JEEZby-DtI zvqjS6N_RbcNjO~x?6w$!z5DCP>92MlEwVMR13|De|A^olnXIe0ytH^jgo1vsuOPBx z{IQC_$6&nHmK&A?xBD=cF^Rvo2+M78ySxdnJ)=7vAaT}l+n$d|klntYTwy3R z>34m2iq&Xt-(g@*`sC+9PjhfKG&HRi-L6HlV#;QI$g^XDzymEz_sh=;moXYD zjRWlP;%oG5c0<)d3pYN~`4R_EHW=M80nWol9Be@)8Q~4z8HGzDwk|5ISIl`UV#7&e z=|?D{j^xJLc=EZZI`{7U(Pa&UrbELEDS&`Z4;aTjt;qLeFP-V_^#ETFf9Gp^S%6sD z1KPRxiQnDX(*aH#-X%D=9RBi03EROSzJ_!mzne`v_j{0*4Z5*#}=(p4pL{ zF@0-4!(li{+*o!>S_n5s6MFLN+4|?Cxh=bUZ)guLI=#$VF$}#I`y>0^T zkT(|cickTH1Q2iycUe;W33WxI^N)5bOF zT$DKo_kG@jl+{KRv}hNbk6Y-?ak|2WYvQtu>K=1RF0$!-Cxgf!qi0S3M6BU&G4Sby zCK@=%rN&BW{&?K!Hnb8m77M-lwsHoDSD)42mVJS3BA}k=fWKX{KO$vY2#TpMRGJ2k8Fw190*IOO8VD_C9i7tr2bQYO2^Z;-Zad2d6dF08ArNV$kB@@+FA*!7ac)cCbPUi@Zd;d4Ap^ z+Q@*F^ON*6q3l>_cgu7D=darvT8Q0%CtU!{x66>k=Me9Ftqj6C25-FaOT@5`2+)%x=Z`jDNhT7;{rtz+!C>Z6Hy^h3LE zjK9JF#&B`-V6WAriT|6|4d$4I$ zUHIU@G%f42!z@vIe98qwWmkrGe!eO03!b@V@WOpX>(iP^M?s2RUc8UGrc|xSWD~hxcQ3Mx#0OpFW$wepNLEFSnbomo+9V;tTGf!M| z0N1^G!xpQ_fW`hXq6-h+Ac7T04QGN6F8Mfs`-z_M-S`*f$vUSzZ@MHieiEEYQhBa3 zX782vxoe{Y1$|0^At+Dg>hzmQ_7p*&%IpfUKO$6spB0~`?gBEPoF<`RKm-IdUBz$o z-f({RS>-r0gf=*coP|Kz)bv0vy;?wH>to`_58vY9_4;>@*qvLan)TVd40paMGhtrK zs_ihltD&M(>y@)b8^2{U+pGs4i%fIWyIoM{+|fv?pSKuVFP0QJO%>+w*~pQYZvm>t z_t*RHH4)_?^&6cUBc-ZlLloraw~-AD)!3e0;?thsFIdizMWJt>gfEaB)4LyDH!%pT zXn7}FE`+zv{Ffvsn+1zZvFjF9h3WY&{ro6>&y(I1)+4NZZ9*62l44y~3yQWLilgUD zBlhF`;Fhl1GcH`Ksf-{}82uF^g1NuF>CDmlNOi2h%hLjE$bCWZa-y8%g*jB9nIAMb zuMILU#7bZXVd>vw)K9$Y8<ytB~R@A#oat_40 zt~-={EDdMhyoR9u$Q`?KKB<292UUAO1m|`68Cu&g?`*IM1*n z_9aawmnMBVw|rhcXXC8}3Yv&#!yHzjJ#~pF@STb}*`A^h-jc;vHr^NUaFVY4g&#iR z&P9diGHo&CNx`pBD1EfgSCc+ShYU) z;Q*uhTXgIBx~f2MwUzlk1>$%fk~!&}-1pbS4t43np?5|ss|3*hXB!k-&rZOxgZyh6 zCDcGGo1JUr-(5oZ%n@|So7?f1IYXB|o=Cf+S3 zR7V`KZLwcYK&v4T+$d9Qg6cR?hyV1s%><^!=T>lnl5T-nkhV{=TMw$qmES00bachV z5;9Yv=@q*k>P(V%cmE^FqwgUmV1*L0Q^)#vT4Ym35xsI@XefR>)q$ZJ;dwrhh+rUk z>*#&}t{|o~XCQn=QM6TMWUzsBAsl{q4d)DNV-%Ehzp zNm8n>VRMp^nys;XaSLl2MC7O>BJaBVI&_{g?~bFts@J`={Oq`+?VZv49Tc>>c z(L*&09<&-s`7A^4T}G2gBQ}p~b;DUc!U?D=q_KJiz_XYZ~}$xj#}lVK$EAA+Zj%>6t|Y6uL!1Z!-Xo`j!_ark0`WAvrom0-#@l} zi;l%sx_CwF7bk0?s0|)|>d()eNVKY9PZe^gs=MNLu+4f?Bn8^ko}=b!E%e8F1>fOU zvZpGWM>4xRnZZ4(c4fFFm<2V8n(l-yFSka+?&!tHF16*ZQLX*g5AnU3Nx@vTL|ZKP zDfdapca~9BM`HObna4#NHETY`S?&-==@>3oqVu?LEzQ_0jb;v3duVX9{F1cUGE>+M z%V-+G#V_|(>)UD=ba@-g?YUcrA@SfNJF^chfAW##n|VEV)8$ub^R#gL*xpVyhaJv@ zNdF<7hb5eIv~k8ub(gF@U+0W;pB$n|^@(LOj8G*HuE;V+YxKu7?Br z(}&5u2dr6WDQNL7QbBwNN&$S>Pvs0F)*1zg)P=6*kCW~VR#g{Q)TXm(gGZEbEuS#~ zwn5fx4`mSu&qA>7sE1}?fMlpZho8O>_H-y&Dxn9J{EPu_T3 zXt#Yo1pT%505e_E#%_IXim_b&%EJ4x19F)Gw3l3ia*=oj!tx^o+A{5EYTLKX#;}IY zxUUNSX%Cd%NMEGPjH%E_l=b`A*^`~rlbi_}eo!fR`Y~dS{a9ck4k?a-?llW)9*7880J-(9?NIhBdW|aXeEB~qL6tk$~FUO%B-V7G=MKiJ-}p; zkYbtyyx^5Q2DpCaKZ&1g3hKa=E`f@qRw=vU4n(62A6%uP@FKDGtlfYO4^)_RG~jY} zwcFbT_EkoApLC!VG}prZ>JOR17vO7z=lx;x!Qf6lu0Cz#&5Ag)NbFe`tbIWxwRP`= zLCuW4Z{TE?Z4k%h0uM#X?&)HvERG4k%kp$l9*$bJq(mdw)(ToAZ#(Bj#6WVYC(K^S zY#E6YwfWNk!_iwS!?Dd~UE-TFEPCYYgUS`_O$BItgY5#j_K%!KmKxpk4slT{cF+|n z;$+Xny8>0>nzbBq89zBnhFBkRDOW^HYz-#+20&#Q3JQlGJk7~|rPl7$g>{4XQg$M! z%c033GpNdf8zoj|;xj`HEE!(i{VBBeFR=dER~gM-HK;+gapIrZ5+ZL56(&E4ZN4PA zjkQVV4)M}vENfLCd_$olS4r`; zn;bE~{K8tsOS2hcVwno}NKnw|ynU0ksh;DqbwL8Egj!6;H!L=*U~Bx7YqO7KRB~_M zkoe_Hq6wBCK z&7QswB5GYrD4d^y2N(3PI4tOOtY3`$BJ#|d9T@b z&YaCxuahUAGOzM+s7_x^hBp>h&Zo`eQvH-CO*l(njdluqUnHR$9kcwi4nNYDE&VyF z^F33W;YZ`0Gn1U}BX8`IJxl}RYWdY^S~DTcUFd$BXA54>xRXQ#Ys)PShKgFB66{CZ zQt~80i(`p7CJZ`b2E%r1e_@Db5Ff7zTh=x6l$MEP7J+)M-G_6Nfr3;8RmRU|$6b)7 z52qhCqIB$w1o2q~1)y!hQXlC!fWlzC;9QtiK1UDRHP1zq;U(hAOjkZNp1q-{Qo&bu z4UB)-$u4GC=G+if30^jFy($Mx`1cvI zIeIvafV6bqfSL5=-d8og1yyNyOkz$EA-q@vVO?3(50yRN;vf%r3`91Y->vl+Vn6SX z8Qo$zL`9ef*7_~ zR>|+{WNQg&FAS~D_~8?RCV$r_k-6*x>0`6GR}6dysgb+!#CNRFm0V}kC&D6ev===q zIV|i>_ZxLrWavwto9G*5emc`ur)b&Q`P6TN7yc@?w~MWe^G5sIi}5u}oH^u%0qZpI zYRZUAD@2#K$I??+c1hmm)*8d&w-*~8;{aF^wHIC4vW?M=3I_j3+N$$Fw5MFqwhcK1 z>nMssX7G*)-woJVfWOf8@$T6P<#7^{oe%7m;HU_yf)*yFt!F@pH_C9ZP&eAe%)>57 zGx&`s%zKiY{{v8Q1kxQQqmfs=9nLOa$Dr}^4uQR9>{x)^O#IN(lyW)P&{=1L;J^qfH*f%E9ud&K)MUc9yW)dmMFDme5U{(&c zi`{99sLO*(82v=4_BN~LyyHrSwoU214w=@8^<|n(x4wM^@STF6|o ztree29Y}oIV&gf7lLgXif-T3$^{5@xAYo1zNdQ_^_5PV)D<*ZK@xn)H$* z?Suw{;>})yvXnXmc^pKJAviF(4psy;-YN1>r#5D_0z-B2Gd0GV5@us!oCyc+tq{kU zJvVGOC(02w5AXh_9g`nxKFpFcsw&l!25XhaS8`NjUmTkC3>KB#&$UlW=YAa(=E*Xc zDMTaX{aX4-Y8D>6KO4#dR0#C~i;1gbuDm20y4(2%RC? z-)Qnfwd6b1U=UOcCv%IB5a9~OMSJ_SQK!Co)LgV;NrH!9ZKscC+$M6SKFFBH zt+`$GOz%t52+y9D?hGffAPL_my@1})<@W`@i0<936I+Q0BNd|9uwq+-;Dk13*$p3S zAh%3ADKt$?I33I^o0=WiY}dfj@h@KwbIGvxrfjq`?gzinrb*N(n%;dRURy63uD2h2 zXb*?#+Sao19%8WRYzmYU`Z&C4A9FP9Kwm383(~RY%F#OgR%xofA_N zT#D=JxybKzwc0pa^w!2RVpsM?d*X6+<*cG7nFMqL7`L`;p@4OxoeqaPC$hjgq;nsw zVP}eQxZoz@lB^pwAnN6k3y}godvbCYqn{pox;jp%1*SS(gF!XBbQawS?Ms32vVX~t zXAacC(+&G0!nIv#*bYKJoq?3Gw0m66rhLR2kRteC+9}JU`1Lo@SS)*A+rYS`kgFB4 zu^K>*IPDa~E(FUKp)a!u&9W-V@h7cFDsqiuD}R>`7$(ZD7@|LVyKTn1i9izZyzm+V zJ7+~Zg9gQ@+R?BQEHth}N%-3@*WKOBek!*P(&5m)e zrf-+rpmn}A{qF6mnK`8JsFcNzk5^G;EDk3=sF~ON>5_S;Wu&IA0aMhod-|-qw2XTG z^{gEI-`%8`aP=MGhtK%@zuoIJS&Y_*eQS7swXlaNh_}J$WUn>iDK;FnhpAP8%@ngH zUrGYQAP_OODocs&R-P4@kw|zx5`VJ+cdL9~b+*+1g6Yi-z zP`2pH^=4FQ^+jpEJA+%SxTldl-rpNU^HjlU!KRCyY`}qJ*`Vi?2mU!!$oEV#Fv;AF zNPd%a+*1hi`UCu)w9sB`8K1@+$#TJe3WH1dWiL{r7dvchi*Mqf-02(VDDG{t07331 z?U><1sy?r1C6v@vw+DWS&)D02Xw80}I_r1x2MjQD!A2i#ZKd`)J9F6LLw6Hair zcbrn?rF|pjzmfi2G@h%JZeBTrN+GOlDYoYR4JjgE5`m|Tx;S93N-0R?lG-b!uz!D_ zdZRA2gQpgDV9v(I)4{1Oklb52(l54*#GnJDNd?d;wk!Pn2OSeA>kYL#SX%wH@wueV zt8!zB_o8}<`QC3W$n8e_P;FJ!plp(6y8nV_kcqTul<22(a={p8!h4IrQ6Xe;ECJ~M zM6$F~{5s@4Ukp-6!)fyBWm?*AAAEph+{RY5?D>epA!({5G|wsx8fFQP|HE_CoWwl} z6Nq}z*JxC-+q8%PU42&QUCl-Qowukz!?^s%^F}0vnouF+e7CVQgTv1!EPBUl zM}|FHS>yf`nAcx*J{kV%P+|PCx^I_kt_9IAj&n5IKlDc?l^Lt~G9ra6?@cMP#Lyp6 zdm9wD`iwBz3Q{V4F-kst<-G(kFX_~UgI`yA`zG=GXN{AZC7kV@cF+p2DFn~Q7y{%= zULQZ>*71>WQwo<1&oqin93$g;g&wOP%53GC z6`DV$?$Ve=NXAp0$iM2WPkM|3zs_>`H{8GW=9(WGwkl6)Tkw-f+Z530+FFu3S|w?1 z8CCzS=pPy20O66`%bQh@246y&!4DELt7fUjZP+G!w0*fg9D9V81UB5*I>ru~j z{v)+Le>%)=Bc=)sWq2eCj6h%!H1k3YY3lq_OVapK6yt2m(yC@IkJ9N5yjvn%#)LN1!s|nXNAV`osx`7e}u#^D#KG-DLVCd z>X3G9*0$+Tp6t)Xa!X3RNbdaaG{&4S2$;1t_B|n@TaPi^++S^KCTo673#LxEdt$?9 zjE`kv7G&3)`CTo&yU*=_ZO(X}#hrVK0=wm|$OMulTW#5R; zo1u}y&>D=$@;B6tr~(eexh4P$$7R3Dfip>PW8ayu)qMx-NqAVdp~j|ugo%w}Ev z1$1Iqzs#7kt|R=3DFSKy_$2Tx?Rvw4*wo%(j0mK*pdBJkLmefHs>5eyAC4tZaYEq0dt5v6q-zmJk7FP6WhKCW0 zmq-GocPXu<*+V_~!k6*VZHGvq zj}350f^1v(1t<};7{EW&Or8w;EU>j30^USqD8S3a(wdvq*Q@7ps-N9T zHwQWfnu!J%Z0*LJyE*VH*Q;!a6GByP|D`T>B=%mtMgmq0>4g<@G2Uj@nZ!{FLdu-; z(!Wi!jh%|vI}?UI!p@=MU)1D07cn$s9uLuIER`G${uyv4h&B>0Bq)%7kBjR6sF70l z=~`LxcyDhIQo`LDBS%T%=r|3UnvbUT3h>XzLd!`@XH+{+VCgbv=42MS@A6# z(RDSBwaTJRSXMsVcN1|nGrbdeBcBqOGqab9Uv8LeUWO%Gno!Wt-Yp|@y(t~tay36( zQ|C)YXR-=|ZN8vf?+aH`HzZq6WBf$yHBwtgA*eJsB#S>tP=NtJkhJJ2Imcm(+lX?? z&gY6G628g4AzB*e4gQMK7Ai>;2}Btnxr8?6F8Ld;OhJmyM& zv&65n)I)@Mr{QM06fbfbj^t$WR?FQKaYGJW06Hc?%^W>r>1y6;UxIN_-#%(&DaL_F z+Qa1*cg20}s)!Oao*@mHLXlj{7LOg`L-Ic1X3EVY^P|8i$E&B&M~`;)n;3LQcvc%K zFL@Ubo8u)|#3U$|*R;%=_bpuvx#v0sH@;uZ$>{cT5>ZphfA(j{O@&8du|^Dx!EP$} zVQ^77!CQIHE0e-^l5C63gsUe#inaH0BzG_{TAXvUMOEXmy4(GrNv2rRO=o?xx>Gul z)oY>DCpU)Pca0yC>~X7(5J3vZFrLN=GG-zxGZ*y>nb+DKQ48!{Lr83`QlJ?JsZB8B zB)jXme#)&OS#=8RU9NXygemaCPHt=^n*gj$bS_@&3yx6XPb6m( z9e6enIsef%3{%RCZ!*bL9^x*!)K;v6&kz~U)uIg#>`MYm#-Kc;{whj0d)Ku}R53z} zyr(v-4D5*UCQkXy(@R!9*XK4#s+3N$lV_xTbST9PdLTcMqh}lGCoi1$o$ek3eW_do z-6H1U+BdYZ0XeYwH1*ogzE~~w#r+JpOqO$@R@*2_V9=zr6t{)8Oj1Bn=;^3y)35xQ zq=r|d4U`^rjR&Jg)I?j$B&1v>;p`y7;&1V^)cy8qct^PN#Ex4-2C=adThB{ofJNOz`Ebv2_Gnyd)+ zj;1S=Qm|e_>Au$VRJ+*ywv2eedi9;K+P>1to30eKG$$ON zW*2Sgg>>p)Ep=z=%#_%lwe8FqZ~E$f&9XI+M;05t1l4Igz5hms{e1gs+4f~MbK;xhTn*?JtTrzr}|>9XoKi*g=@g)6zd18tZjcLIwgId)mSVK&-XRv42`ZZiD4sPN6@f%hl_+FC z&lunU0|#Z;PO%`(vpd(ho?5=!to^P~Y>s z#2~*DhlG-oAvmlqAk)3Z*p-_y=PCMPcO16}%(@bTVXF{IM%XJHl<+%u{=_WChv}~Ust(I*6nejxh*a>{x_sT?82Y1_@aRQ zOzi|G`_Hmq8x;><`XYVjs^2|)``$j`yc~v@j*ry-v2(^TCZ^rwx8oWn6?axR(^gp~ zqh;H@%0Rp<**oDk#i$a%f;1p?o@O?yT*};G%)1SufpZl!w$7L6Vruzxj3oCtR~&{5LOniQol<|L_8%BM>K(b@!q_EiHcets(xa z$alwe%U@z2KTzM02s!+a31tCYK=)urKb!u|+z@@Ur)987d#-!h&`_H0%tWc9In&u; z@_(TGt1J|zK6PPTXB>UP88nn#)>8-KLRzhn!lgo|@t+o6kW*GXYUy6abw3F|oPHp( zL?YtmxAZ*DE>`&n>#Wj6fg~Y}M~J;{tlKbuh2Af*OGgkgqS+Np&^|Dfx+BF}vIf-9 z6GB~6?S7zFRr~sHY6%Hb4);e~$SZB!Ysl{wUoMWA01d!n)3QUIIJ;pEj?XJtI$2>|e zpb1ma?c0FkxrhIwg1$wWK|!+{8Yu2nn&RtK-&ISVCoj$l;jb%X*YsSBw=d&-KB!uh z=KJdYfMbuiO=O4s5EV02WIP8U5pog(Q95-m^;0Qe802kdzxWX`s^>n`_lAq$K_5fS zozEovLPWHWG3x3i#~D*XrXHRo_kVtHcYY12B{gbCO=j`+1NdUeyyxUAY#y*9a?o?y z)+%@AQ12y-Qs|JhnD(;mpz6d|T|<0{;K~VgYKcXksq8w(@!Zx8(`T}i2Vajj*q`YP zy?x=nv^+70Qav_c^dnmYKv6OCbD8$bh1(+O7;Gu15)aPY|Aj$AsW{+>?H9_x~>u2Bw_x?e7mKw3phdf8uL|) z_<;Gnu~ZOJWA`{T`LyKtIzCEy06+Tj+lxVhSA%8F?Vz3 zp=DH%v9l=ZCoRqU5?Fd{&h4o>O^+PoXzVZHs1xoCD}Ih|i0dLaCS zk+4Y2f(2^85dz(KL3)4}iOzK0_W1dA3rCoEn^92(ehq);5vyWYG{Yq@cwYaP) z;SeLY7o&qI4?;i2?ElTLCw;Dv($ELB*amW3Dj;`F^c>Ob<>l^b27WbyATz_+(Ev-Z z>fvP=X0-~UC^`_Jyw1GW^iHsd1-9Xa+LLk{tDcMd?L_4hn4s<^8{&sknq7Pf(%5BR z&TQi{QTh=a-6T0`(MrSxqb}Y>X54m>ou4L4S+Q1C4usM0d%&fDqJvySbWnvL+e^>; z=ry3|AlTZcp6hD0iNSi9Cts*X_4e4*r1!H&;@2hmtHLIe4atqt=P4l##FA}SwD3O~ z=q{SSLL*C2FA%6{uT+JzV|Su6h!u3NKW$WN>!DeW|K;y?hnY~Y8~aM!YtlnErg7$R zg2xBjTVHFZ1~-B?IZlvAgkn%mVKO{K{Dq2T8L9oIvf~C?wKs$0zxkWb{Xi7S)uFea z@9!cd-|UF}1$u}`*33c_yR&{Lq#5iwJ&VMaF;Ns|d5Y4GqMrCVQxj*zc=|6g7n0pe zYPzj{G_dyI?kD~WX93d{SlB-Qf+Lddt4(}w5erStwaCM~IDxjs6WW>}Dtn8B7E45V(dw=j!$&iULe$~->9%yUCdH>f#$N2c&fp-*tTsDAhc=JV1T zJowW4mCFeWf<~s2_|Xzk8)`DfE>duYj~jiz1k_F7oPBOr$MP+jZ=7O%|M+X0ztrH^ zRBeKtrwZ$98s^}*iZucT(U(IPO()1R2;8B89neb&QtbNjn~7`^?|vI1dON%>}F zU;&v#0N!ANn06r!racj!Y5(M-BjZYX22eg4oW+q2Xmu&nQjx}Z7rGTnfJlR?8u+Sw z&ex#8?uL~@((u0S335FqZb30f9TX|>QHDPtDEWV3+#!jQYXK$*xz#I$3xcGbKPk{Z z{?ECBEhJDbxiuZ**Dc|UKZt9`ZOSn&rCXUIC=N0pr1#(Azgi)to_sFkns!wYDEzkP zNLjztpWAaLCgsG*PGjbOssz}>#7YHAG1OEBh^E?)-U_5=y-}0S+*!oD{QQ^n87F}@ zJOPcg^FR`=pdGctlOkMuioR}JKKsM|;n8NH_`L(?73`50@Gntd{&{ufK4gA)m0!Ws zIj~)==_~pR08VC8QDv#Pcb)jhgN)On)|SZ zM&KtNg)|V@UHGYqPNfIPupS=HkZX8R8cXJaH`&v5^b|n+aN*bm8-G2g)+1`uz*3EY zELFrqzW?9?ptKMX5!epYgt7*fS~Oo4m;fc95KRG)&P@i(RKK|fxr?^YSv}WYhVgXl zUSO3(?zvB%{hvH9FqNX`(8Y3*@@%@dFQyWzlNk%B6MjCLe3O-cIE`Iz%NQM+>CJ(J z;nTglw2asQo_dxMPQxsgqt>Lm3~KOa(fOdKV&Wy8u)%(9bEd@f2TqS1xKxFRrXSb2 z`UjN*i29^llWZm=)XwKDf1lA*j^nu;|M06mVSjIN0Q6~r%*ziP6HceFPW_I!B2S)p zbyy=E6}Re<&n=!_F8aRX!-AB8SH5n}v(=lFI z)J`O9Q6_RAmiR9$VjI3K3@x&0vu4#G*-n!%TxMf~!iWGgz#<^lCkZ?U@zz^G0f!Ua zF4JQl8Bh_b>taLk{{~k~R~+b})dP_-LjK$enf&lxY)fMzNYIWW1nu8L@ZnWvC*(l{ ztx$)bGd{n&DJi2y?klFne`v+~cBG35i0t|UXZKAk$}cuK4sgY&37v(hkA;k}6a6W1 zDH%K-jfDMiqorfQ&qy8hcD3U^_#w@PRC-zZvw7Z`*K5}5hQ~OSu=2Rt4LYg;j~)ju zGh&I}9LM|4SidS=?vuoX;Saa?5+2p&y`N8^?2CCJK)@+yDNqw^t)Hj7{KAuf@SAC1FCO@iC8r<9!W z-$Y$ek}bZg2q@fViq9&koyOzFbkVJIJ4~Bd?|Xt7xfFXZLW!WSuW%Gg`N`t7r((Ws z|E*%;&(i^BeFd2HXnRv7vDcGSHOdOp=lg*Oe>cZ+lE`e2f$-N`bFy%ZR@0Zqy0vcC zbp-CYalUS5pSmd@jK4bymsk1kLwI(^ngdmsa8;J{CTg1$fPL)3_IA+M~p;f zC;sjr2D=M8qN%)yj`@aF;$HxE>0%@j85xlXlPS=&&tfBlul$5bFKHz=*y4q(qjR=# zAjn;IfG6TAUP8vxoKCRIOmU4?-oS6@TYjNcd2-wTquro^=M73xjC1kqZ}J>`-aC$i zrsDOWaF6JB|G0aqL3{xDNP#bo2+EU=VsN!q_q-4)Ee$L}Wc@bA|B3zFH$-=6_Xfqx z2rdiYLlQW7%p;+5Jbg%S@8zys3XrbVRJFTl=L`wPab zsb*2%n_f-@G1DlPlHhtt%d89>GGfK&ZQRF?_vpCCv@u+N$nFkJv>NdlhsbbZR76l~ z_VFdkP@XiUw7z~vJx6=&WpLCMWqTYl<5@Es(jFpXn@(#RpG^6ROqsZjRNH+%lfeZk zj30eWT*A3syz~9bHPB^&fzv_eb6)~nfyfdnU0NpNN$h#V7(-u*GKkMq-*MpnWR0tV zHSEeIed85Q8lhtqOqp{nj4KbcurbiW-*tf&7ELQV$s*`4Qq?=TK?2A;&uTEV1tgB} zki2V$Xu!7bjlb=+_qK{JGI0-nQf&td}K`Lym7 zE_^m0qt}Q+2UZyr3Gk8r;U1vbAUc_{njIMSw2~O59W8ZGus0EmTQ#8t5v~@-qqjXV zJ>|c%^jj-PU(l>C;no&zhq!H+EXz+Ar`BMA@fcU?KlE+MXYPnW~ zN<=vLF8%i4)yuu949Pe_#mRr!7npY}^6>aoxlvR@1X2Hb}Pd<}jr=anq`_`*qaevQF# z;sKp9Bdt9(7>#h7%>J+Jr_Y4&X=ohg{`47>lhefwo+s2y%_wi_o!RSPM5IWGJ1M;{=rs^C52U`JQe>P!6t7Kh zw-`AHAuOnKj)+p=($W3f@nacO-V*UY#R2#k?wX(jlV|RdaG@U~&3>6&QtTb7;rj$D zYkQS5nOz3I>VJ$w3uxJ)udkv$9VL`UX;K}eg3nR26A3;PJA?@>KU!2@JJ5C4JlVU< z-3OX#0?|~8j*_@9l%6(HhIajuhgYJ-ZPtv2vf)iyJ(m+&JEYXE9I-uBuc6yF` zLa*BZzYs-CJ?7=Lz4Q=AeU8`nM2_)li!HKTdVcL6swuoKVRQ2f$T>9Fdpp2F7jE%o zvaU?6uhXAxmB!IGcYup=|C69Fq4M>jdth^@-jQRXYOH!ex9z)U`qxH>A^ zW{}^zONpO$6T&U4qF-_vy}maD6GmI`;R8m`OvD(K1RIb$0&%08wehiNa(YsHjCA2v zpNwc@B>0dqn~8+|?t}dyDE;Lw#pMzabV3*0gEuIg-iD@9R{_GzcIe$Dv zhJLj(jqSoz1pF`?N{*XdP=|XOjLUqJDq9kvt-VAx!r!}NET(}RHDf)*z2!Dr85-cw zUGJ1AlmlOo4S|1?4b(((KY(P@aP^ATXNvmCKa1Rk(WIp_AH7xK%zj1UUQm^CyG=|3 zP7rNJwe?1pG}p_=hz!gljr6~}4p78%G*iv^KYV?6Jk{<0zm=n`r0k6Bn-j9PvO^S= zm6@F_*&}J*2O)`&>8AaJbMne3q_xl{B`~G}?f7c_AN0--iJ?HDa9bhZt za1nD``YTA3rmdHdiA?F2daz5OD_9bxzx~_@-Io_X{hpd(WL5b)&okwVj1{PB%O9Rc zPw>>r*#>1@B12u5Z)Z7{O6bNw0}UL_sd{?A#Dd^gEq`{dLqapx=B9P;=#f*VAKq{x zDNJadm9s38^ed_{Eh=lC@Vs~rqR{UP11(k)T9;2n9o-=@ypG(Cntm%3@*CA#e%uU8 zxsR;+3X;HVwV+5mQyyUhNGBJ*c zunS$bSHSiaX?DS@{vqJ*1%7ZEPo)~$tuqnk%I^fo)9;1*v4c0o zhQ46@!?n-L&8N9)Hv&1q<0c0^Zt=-H67JCBW~M{nXh_dQw&|2C@@ zKK~%w-#QeES^%Bu0=!(3M5xT#DCZ zW{P!7xzeg?Ri^#6K9votoB{}Or681wpqQ^=$^!dRkY39**{!;J&p6; z(f8BQ*nZr7`~4@S3Fm~byDhGu+FP6Qh{g|q$yRzswyW7`w^z38eOZ;nhhMkBzOZ2+ z*t5L{vSQ2DQ>4DWSh3Wv!V(7SCzlqy`mCA`Arjpma82g_Gis?__ZYQ&Z8U*@%-_`r z87rEpoP;Uxm|+4hb>;K8pEFw+r1U{TQ`^G{rIE?vQ~*XO5B5`l`UqOg-95K;T5#!C z&tt1GL)tSDB^gLiXiA0JF*>Zdm!Bi@KtA)F_QrMCQi)ic+r=}nb!Nn9RQRbVWc08E z$TYVP2L_3JScIWSiWmOTwz7-Pk%SwUM$;{5MPN&xOkphjT!Eb1QBq6#()p4~Ip7`t zGQAoM0}*f~vWUC~0AJy+E3x)W>SfJs8boK+2QHbTcgm1;%|n8+4lCkJmwrC=ZFeB9 z^pW}`y7f8h@#kH%FB?juO@8#|MtQ~it(rCbUMIHOSHPpDPj-1$D1E99oR)G^IQF!B zRj>N0p!;YKqmjc5`n2sQIdm`LCpumsEsphQ=UiB&9mr7Fta`VaHOQoX*kF|M!cZ~m z2OPH;?FeJ2jB#*4HxtTk68lCe6C7{0r}Y3X>BCv_XIjn;`uE4%Zql z$q%4(TmTLw1o;CZsOwi3qc3GzPrAEEKe0ATG5-fl5hWDrBZM!C%DJ~COYy%B zGH#N^&fFK#OU$H=P@?Ud~8C+gnte1G+Yt)C{-2w>t&|!ZRjKjI}NcHlZsKD>@O8JK3*- zr26VuEl%ksAImu+VQl zo&g{uWcml50H|a059%N+Md@vnsX!Dh>?-#gx(8~d*a-`iF;etRpB8Zkprbg$_Jp6J z#FMlw@R5R~ftyv`j+~boYp0Zhx`hhp=lt0|3T#X|t_DqaYW*sC%if~MZ$p36*zQ^M zguE;>sqz)hkmhyi157%#_2*elx`jz6-w%hb#Kf|YC_L0*syyzHB&tYNEV zK%9z)+OkAt3m069@R7;6GKppbcJ?WGq9VMUJyk<aM3*?2mxV^M#M<`T9eo(I(Rb&<(&;~TCe=NxZeNeGf+ExL@dBbA?qROP z*#RYkyvrCCW1M0lLo>?bD2#LkW)U)FFxn$vWCCRU;XywzrCbdX+n|i2B#+xn;iW{vmhrhLYCw~u>$75?mWHSrfx)p)}4DbOCR|Q%!n=vrESdqjExs5pj&id9pdPCZP@74Uog>3BH!@1w}l5WU=Fs;9q5u z1NVmLkHyxtBZc()oIsp zpm8vlALzz|gM-n^*e9P?q3MpCy1A zFIIf)gH0D=tm)F#Le+BXbl_*gH2UyTZ!{OT>xo9vMQMjZX-LD$p|1k2i=wQYJys4O zAUS7!==^Mqe?$GR4ZKuGmsPv1XQ#NE6I_@CvA5*goWjajnZlr!+N80m%G2%qE9Y`% zUYvi_n~^0uGYF)R*{&4??-JE#`nT4-B%g}{kOCZc!`w|a%cw$X4Qkm#S@Yt71li*f zeyKC!9?%XYmnRswh z8ziTuq@v*a5sHaYir{mp?e~yiM$N`pI7hi$6=G$-k7@<|&%seCwy_&>eF%B=;ewC) z;j^;*I)%&Ad!gaUYP{0Nx4S$)5mIWl@o|nni&f1BjS+ti%VM`{B%s$uY99B5Fzd{K zu3CIDNU2JJ}w>ct1LEI z5x4uoAJav^0AzALdpIcNUcO>>g8Sv2b(})n15%dSqMHt64>cJ)O2iolt?H7CvsyMC zPibueAw!J(#To2V_NGZ`2tfQCohQ>W)A!RB2o%w5&|$eT^30h_aP1A4B()lGx;M&9 zP2tkgp8b5sXC&Qw&);fqdPcKR`#UL z$NQYPEE3=~BP5xqGK?SN9(K0y64Tv@d%o?Ik-Ii4)t@Ar6@9cIU<8ktUl8DY1V4$6 zFHVV|kp6bMj91({Z5K9BHZgOu*DL#+jVLN@O-E-`T7Og1wgv?)B2;fM&>BZD>kVLI z)8_LmMc~uzqlZMRj2meNTafUvqiQ&%2UWLS81FHGn-GHrA%^^k4ob&6uHx*tedWX9 zW#hv~90YSPkC`YIf~baB|CAGA6cJu(^L*uuZQ@yhjw7s9;rN2Cw))(1I>Jq6qWfp7 ztVfv})i;py2nN02&QXO?TuQ#Xebu)|bW2;=H158l@LkeaQe|Xgvyq^KXxS@UQvK=U ztVmB{^*q_V$>kqg5bA|^Fm>Dwd41YF=R2?W*Ulck}k6pq1beESsDRgF_u`;c$sVL6vqQnQn z28K+--WWJl1Hhlx){SwTj)wG z_w}vUmH63x9{rV4&CUhVp*%;FDGOFS;*TkQdQ}OV{j0q}gTv+^Wjek=Wvt!^ot)4`N|8&o-b|f)>ZLE_LG>GJ zrkUjVK~e2PC`1t7*#V0}mN=bJm>bfG@!qmmC~UQ0HR3^ViDp_p$***4F5K-bKU zI0iq~WAS62hz*5(d>g5h;PIKw43w}M*)$vBJfU~YV>`|7%q$UegbqL@Mw4re*1aSX z=#8taYYGgf=jtQOqdh^_A!0euhj)NNGF(hgMg=Dh5djdX8|f7=En^Azb!42{7R4ON zwO(act=*{l1-}K|(10NSudHhdp{gF;o$23(^&OILtf<)=Rr?i+o~8dNHE6-ZPO93* z>BC=K@_kTAZHv`Im+OyOT$9dZx9DKZ4W5GmPtmo zpvh@B*ra)>4>-U(3y=5ECm$(o|6`w z3El)8v_imvDU0NIDxWP7W|?)wRz1cx-sm=TQ4S;hc5vuTivnVFE|d3+wW`}FX4z+tZP9e_gB&?&wms|z7e+BGJ7X`_r{dX<$IT8tS;c0Si^9wxb39vk3lt_)i zB1s2WL^Q{kxiI?(*tKxyadWV*M8N{`ZBi(ENIA#~bKGKBID!R^mI4lus>pd@r19&> zLh;j6XL-p|CGOjaTESPEjV!LKc}fW6U8hNClBC0v23{{HIR%SY0m=roIO()aIFLR? zMUHv>0J03U6L?Y&DiJ|~35`c!Bmyp4^BWL+p>zF>MXruZfptBPp4VOgMz?~dk1U)# zoffCTyl;VBXOJ28=U#pGuvE&0rp)X_KLeoGP=`OZ$xWZ+a@i~*Pu_gdI`SnbfBg)$ z47k{9d>f!g@V*UcrekEsh7#Rs{@2ZpOeW9+#|kdXU~U3+^mbPUvr>%+E|(kgK`#P!FMiI{Zv3#vy%T}=H(E2F3`4zX?plM|le@kf;f zi{1^h&?@A!u-Nq*4d`{wuR##?ho@XOM{3w!N)VWG(Wqh-JAfwb^3qv?=`;CQ#?}+* z-QiHwRw7G4cbpOuwVwKvvE9%8@;ki42{e8`c+8Mq2Yy;*UK?i4Ce;E~SbjjuenS2` zX51sI9hg9U!e%^RZ$z3wn_oaqme$#AG2t;BVl`(0A z4Sc4SDxYj^}bi6+jijMl^W(kr8 z3j(U~=?=wwv)fz1jRC-fZieRnx*g|joaw-#*6qgUL=f^{q^k7|Kowt4kIBkISbsBT zoU6X1<<{wlG&4}ysg09~gQo6U<>0+8zi#De1k+@t_a*#!LEaQXN$Q0Chi|?iJp@uW zH!ZB$7b$!i5J8bw{j+&N@>D$mxBBe^o~b)G@W1rw_9KX%JFo4#)*Vkxj<@KjGNk-Q zbjk1$BiVqS*Hnl8r|O z>hT7FGV*K+;uNU2J+XqAcWw0I#xu@=D}P*><(b_nCMkUh1V-qWv${`(7NPyP;%=F$ zq#B``^==%EuT^@du`H5B0d1D{+{OVjY~r{E1iZlQTGA@HLy*kD-Yi7iS>vTkoaYT%n)*I+ed%^2oyjlp{OR%;6y35a>1DghF1Hoy4R)9IUznJrINz{BS#WJe;SDvf9Y zy;vNhg>ZWDVMeOnW%)Yf>4(B`YY>E#`WZ0uDGk)i?_`u>A!M%LLRpDXjA}B$?I7Rt>z6yn zLyBS5QLGjGQ+Vtkf%Sz4Ku8;ZC=7z`V9*lmouq-iPP)+xt-qf$IVM&3Ig{#j^_4ph zy=P0l9KI(J&97#36*T(Gy|vA7Fjb6H$msq4XSI7)cx0h{j~GI)Hqg~0 z6Y(Q8{G0y_*27irW#V3bHi@?VLOxuPmrqa0Jg~M|)#1jGW-1zgO5QR%mH-qhC?)5dPc1dFeI(#h_IYix-^~dK z=0lnM95|Cd4GOKzAba&|9f*Y8EK>A9XL8#8eZL_Jon^XYVeag1j1!D|2q7NUsfO84 zCxF_eM_zv!C&Z&cT@wU@U2bz+%KVE&*FYp1Upj0hLTQ42fCGB7ssm?^yb@f1p#2%e z&3lXi$u-1nIiXzPRBL%3(k;jY%&L`5CYhT^AK2jcLwX0q$iz@{m%;9=u97@Mnvau} z`SEazhAdK)nzU%A;ALs8tG$oY>xpkej~0@H zKH|(|?=tNjA6+{`-}vo~$mYFbC8zeU0cKN0$ssF>E5#&98`(POiQ!{kKI-=4vDqqo z8@!s*=h2!t>}G0ODX`1p2lP#Fm0G+T(J(#u(G*EvSS{H$d4eQE=hUDL`wd5>G!F1GOh}NLleRSPj^}3@7D@|dAFzu znK~eT^t4{?#}}#;5KKjLS)Vn{7&>7yAvnh>27rdiZSSSo#bv`s91cH5B_+*o*cH=% zdh=kPagQR3zvA)MkJVSfDAURidT;hy6k{#|>AG7yxfUL+7B5NOZDi+1PXv+1-kH)O zj?JT}HogvWS1*A~V%KoGK7ywY{UK532IDNtrmr@+udU#@q#nXNc8F0E@{HQuWnnvo z0{}<7k{VM3oZlg%6jY)KtVtbGGxOYKEK6z%Mp5|&+D%4r>t50I4gn@wOQ+YA8jjaV zIJ|?o5qb0xqG{Nw^GVdqk;x8>O0Tq-3v#IdZ^*9;iIKP7k(P%Y>ccl86}061EzT;M#8NX$#{>2+A5Y$NSq8K4exE)T>UXw$c9=Cbtqa)s_+b#-;z+{hja8I(hNOMcG#O_Zp^^1DJzzvN_E6%qs)1C&kt(91d}JXg8lTw$}vPF1Deos zp%CIIc}`9CU_{VyQLP+OEkQP+q}!Hg z% z-7L*n_hn>Lt#?fPll1;6VO9ThxEl^LaKh@7RM$E zdhZ5)U6n+eodC|+Azc{A`1GO+C_dcT%a}YWoc_=(Q+keYdmCCqdP+oz1e}LMR}_>I zHj@Pzf&bqvSv|9IBmnGohBM%QLBe~^axE_5AQh5?zZvcMdw&$1{?I~M>pf$X0}Jlh za@VixlPhSN@gcJ;FW0#eJ(%clQxj>I0(DeDYijIZ`u$>4`eTtsM7Xx|+m$J!I`aV9^ zK{KW!|8vZS#C|dQA1awarYFtwC&f8XZU-veYByzBC(fLLnwGsrCk8#pKHGp;TiDd= zG_X|t0A-XA0-gR;_u3I0#ZO0wq2M==KY-SV88vtim!VCbprB-@$#~{(g&1SL4CJPP z=K8_g)(#8Ao`j)0V6%V;_~yeJ722h)39vAu-z@C#+r|?4VPm1^To{c_^>rb9ow)J- zUmRLbABm@1HcfgUu4uv}4D+mgi?BMke)(hx3HDXv|1qjO`|8+g331cwlQq+o7hgI` z@gypN^jt?*)&jA}G-}sX#%Ap7R0f= zc%vstR)H$;JjfJVHzSMGMnMdM%XfwGKQrgr`=6PsQ56zS-e^h0`Aezd)Mp*QmoWa^ zFxk@A)fA})-^0B!M<4jpY%6y)_}IJo>WyqyY+{o0kq$h zYX^0d3dxRx;BbmL_?rO$9U5D|qs8p^DscIpb&wgRks)F&KEB!r$K%Kj;&Gh@9TgNb zMm~~7AaB*gcID>sEN)2aDY-r(BR-5S48kgNerNGdA`Wr?LDc9-FKh?OVC`R>= zIMZO)Jw_&8Ch|-|;IxQkjv(EUX}=w!8`oKU7YV_GcCGZfvX$@5`*o9R-Zg!X=11?mCLctK9tNGPX3qyx$erjTWVI**1)Z@el zuRQy{T1_WF5GbOI0hCz+lG>WE{2?*29rxUa1`QpW(jWdzdaYuRKwjk^93mlR4ipvB zn`a?ePz?NIlAm~W!X_G{757-94TOF)pVU8-Ujve){`yiPr3MW&|#=^4=4hR4W z?808O&nt#4Yj-I=s8idj;(auUeu{y&61;F<7{H#jH1H7>VA60UceM%whSeW zRY+w#_(Pyv2_!V)!^)La#}tN6WLnADcfW3&RhJ-ztL@_1wY^@g>ziRAztzbcp$LdpgoZM`Fn+5^XtY?_sGtvnoi$|xk&rhIX%dN5+GT83Z-`C zUc26ZVe3qBak7;?H@ABz5(1a>%vzK#wS}OJ5p1sHYgqUjrDVKE;PyukEgZT+!2p)< zHtbzBP7lNnu$z&nX#n}pNOd(Z(I_Iaz&3e%K+E+8sqoL>(p^rR=q z?^l;>1RUfHKxpBom_N2fZRq`dCC^5)s}GflK`95Ni+|soU>_;D2jMqQu%Mb#;&Ek? zJuCm4ccpkAJ#W*ovvb@(0C2MG4fOb{@bV^D=R%v0lAQ*P4f^B#b(YHb)V#yw9buAm_T)?WUAjH|9uerWfvOq{>u1!VC^klzmuk zO0W7{Navw5bRDNFkGdF57A82lHojxrPThU8mhW5Ie8G-xWO@ z-P~Y&HDX1<#8kLzdtn$Gw}U>lnNX07~A)24Bz7P2oiyH6sfbky=O*3 zFGorhiSan~w0MCiBp&{4*6oqUBk@fds@iBcrFGTLT;9e_&4llxY&-kiRoD zm8djPFqJCyjkAhrr^45~K=MZ*;^gq_1s0a0kE!Ir4kl_|AOKWU0w&1f18Q=`>=hJIWzdtEb zyY!6khNhN=602`(;pNw{pZ3W2T%RC!JC^i}- z@iZb{oy=LKf4JQlk+GHL5TQSG&+;_TLPE@kiuUFjL_`)$5I$?m2#)>*|!)3FM8 zP#nc)2rMEwVL@ma$&cqPSrXRteXH7DAfk<~U}52HzY(Zw8}xqxj9$+XBc%Q18vKQL zDpF3tH=<#bFF~Pm%C}k&ZGdpgZ_W-&p8hKgP(h&T=d9jAV2BD8JPXlF%~pi_5{-TE8!@Eb*2dL=%_?)S4;rSqGYib_LH z^Kr!}aj^Zb{v1Z?4H~7yuL3<;`{^=CN;BS~Y0PCA3(o0BW z9}+n+0B2}Q-KT@FIUXo3py8OldJ|lAmZgJ_y0ixS9?{JSx|1ff1^w9P-uJt8SJs%2 z&bXN2!-Ts24(ZGBh4LpeV*8GW#g?=d!yYm|Gtt{6I!c~~4juvtF5WtyBkCG#Q_8ay z+^uj9O{8CsuHNpHi=r6o5X=>8^5?%ZyRYp8YycHB=%{^weg+U@EFdxFxGn+sVpcZ! zNG5xP6niTrU0*l#U_vvUk=KfZHwhim3w)1(LjJd0y*s`-7Jk6+gAQcSfk96=qjyq( zd*rKLh~p|vNg{|hOkp0!zrZTp%oO+zQ{>D~yiW~cL`>z$*g=e>a-{`vN--jHabEC! znhsp=7AXv10_6``*RS*HDN4^|HUQ7Ce7<)~+GLA4`nO9*8{EByTaKTqsIM@3)2(qX zU&20r!2BF1zm{l5QOz@H)v*Ucr@(d}q!;c0jU>=wz;>TYb#>>%iT@E6`K^40Mc0U= zA9@?5x23>I>RwsgQ}!w(=koYgd8CAQHaU1*;6;!&+IDk3uj0;Gb%)i_G~1qXlIQi- zS=~dzx*SZq@0ot>K6;sIpn~}Bs3Ns+)W7A0mGqBMf7)XD$~EI&aEPg|9`FFX+7zP7 zJ32*^=Ip5(U0U(rox?JVW9yK2fSktn+OPM&Mzmb~zN{|&`JPGZi_f1;7Sx`9Kd%rq zV@j%p4+T}=bG&;dI$BhB7<5osp5ZX}1SEku?D_8;I{ar2fq!?5v2!mR4yy3Bpmbkh z-PeCnM`|lbT(e<{u#*Z}5wh0oYFxe1jlDkCq^8fAk%EJs=7BZ|8VdF?LtUK&huc28#tot<`-bzN#}%z$DJqW8d}h3BCuP&)QG1(F9O+ttb+e0i{UaUpOvVtEQO z@J7b`8$sZbp5Gq?E~Bi)LEzF!;su5|N^O?kVYvmlu4!AqxXK=jzZlP*;%uu?= z70g)*7KI{8hZod^XC>~M{WE8^NLWcZny|c^&J_QtJ9g>KC5Zq{&$(X#{p)0jOKW zaf}V)N?r*$jC8J`Sa9t{`kCnDA~1*~xW5Uc^?kxffgdf_RDmV8Ek5v)y}a@&XeS98Yh19Ptcw5g_c z!IvW*Spm()wNX6?oXj<-{{b#qQh;G4!LrWlh$H`ns80o-T73Ni&aLCYIJE9!tp?-h zSz#T$5wS6daEyUddlzVDUceYH9vJwl{w-9pIsmB|)LL*b?2`WcsXqYcu96zDIqyaf z$BRjDcrfFSfZ&b!@dCTW@jrve);n(5tE`TgxoaxyeVc8eMbJ|?xY<$q#^pf)uADf( z7KfE{b5mpAe$H#I%(V1nbJ}l_Y7{ALC-%r(hP84BEYDfu|K9V@*(S}fu^N)x_BF8##I`bg7HNYu|}l?aCN=IXunQ=g+hH9fSk zTd=tLlQ|>1#(p04=VC2u#{=~q%q+e;Jc~9Zb>Fu<9l{hiUxovBzgP6eZJ6L0d}9n~biQJ46e@{nU6@62k?rvmn%5 zo{UwxIUw3myZ@C|m-M_)1`q)XU;z$oc2<5+ixy$R8%AK5876K5NkD;ikWs7o#R^v6 zVnsFkS(CJ`0GvYv>84CTH$^jVPGEV*_!)huWsuVKk5D%*#33hp2VI#la1}YNRS;+~ z0>CO2jTn(LzQqWRuz6^-h|;{x<$qnP?9&w>{cyglu*92bu39GjQ}qN@Q=E$TO7aaL}*)vf3ZyTdo)=MnQ2PCX?51zS$*=$@yOyEzOu$h-CUlp@3~^y>G^W5I8=H4bT*Bd)iF+A0XCOm2=*s-2<1d;V4q9|4q4 z#3DKl)F7vhqUL6T?b^*So<$PA?G5AA=99eK`KN*N9+9K8TL>@2JMb*l%WhNu5=jSz zEQ=Bg&I>KV+pyL1o9uXHXD=GQtSOsOo54eBtSw4n7=R zg0m&6LSTUgARhXi2bRVmCG`YWNrlALFHpVsZw-80>TL{6JWBR|6Az3W;EK1Ae+E&A zD)&;KCIFGYoqHhicS4k1LlLyIqKts)4*7&O&mb|?%n=Pt3*nZY|K6_9fUZ!SEWn@s z=G%L4y{etryv^?W#?9%Us&B9$@Q5Lb++o!~6Zt`yB=T9-4>$b5qhWThyI=U%Jt6O& zeQ4n-c{4xQ%3*ttr`jiWorZfZ;#c9;(2}2H%kXlbpTf*?Y7uWEm1$~WNi3J5)1=8i z+3t3Ylz}0(DPD0r72U+xGmTH7{(3wm8Q-fuF9w=Z=um!k~V zHsadY_gDEic%;B(Z${jLVlS2^Igrj1PesfdGl36wZgiBtLlqY7WYi=OVPY>;dUjwV ze9R>9>ly#cC}D-*pRS`=g}@gm1b<(<@b!whc4Iz(FI;k0S@T2ptuVNh9h_vkCP0g; zT7vl#9n3*7oxJplnUAJ!9I2r?PrLwlg{%JjKF5_zSSG`M9;xe}Bx#uW88Ctgn9*dv zD~)u;ySm1pb;S&in{j94HNX9p-_NBe>}&v$^~ghO{KB11(+@OPqkiE>XETo6gi!2v zv-Ot~-_TZQW#%i4)((4|zJ=r*=U@inCVj!DjBrx*$l$LWw+iW^(BiFvT&mloL1tWY z(jV(A&W(NU|4ep|mw$_I@kC#Uk48e7G+kJQG*h>naA? z`J#ug)U8`J`TsCoC|l>%s6n5JtV<`&W7G=1zl!mNnIldMIpH`VeXC%ONekBBfXB4H zyv!ft?swppgCo7-op7=Mipl^Tc4q-SNsJ>m(eponkXc?7Wp#@yp7XVX$S$kNzb@cE zbIDXSod#lP3?v!2Sm(o*42x}tJj}%z{ts*$nj-Z_?^*SQnaSfFM`&v${xsNH=}3W$ zZ;O)RJZ^Qnr$rwl`ULs5c9wHSJU-D!0(JBLeo!y=ih?>Nq~QhH*<7acmbM}P^Q4B! z5GNW_s6|40!4Z^DvGZthp|T1S@^s2EuwY>BmbVdUqgj!3dEOd1glq4-I^TY?q9Bz# zf4qLnGvE%3{mrnLRfBGWn9!0mf99ecw6jNoHT8MT*GwXouhY65SzDIFpsy$LmC1uk z_#X?jq_j(>1xbn7@~U>~|0lfD}j*fjEJavpG&cM`f5=WnHd)CvlR8W`|tYml2b za;9a!&^9lGQfjC4oz-}c7V~NO;s6053q9E=4Eb7u{Mx~w$)_511Z?a;M4eC0L%(|a zEou{^utQhzjTaQgc>fY*Sw+)A56t6yvY9e0z8%~DgKupi`5c6JA$Xg%`-?b$h6vjC z3qvDen01-m?p(w3+`^WDJZ^ci?KxL&@ZomSQ}(z%`L%QhgLAC@#Iy|4XoiS zDM1`YUy8)%K08AIY~vTLY#ef(;sDs1j%3kfSPO*NlJfRBkl6f9Qtm26{hGpr(jaUE z16y$Q3io{F$;U@QN69YoJz;22-6#Lgl=2bs?LbqycV6JZ`aOr9bNB7%QXT7_;FjMR zsf+Yr??`E(HY44iX84i+(sR214ErOC?y=W5 zAmB^Hdt@~5KuZ_^H9jb=vFLJ}A1sxHxGLBV4064{-H`*<=@)YQoX>G(=+*@*l0NQ3 z0jY?tNo2-Xuu33i=B67RdmP8g8Tb(ex%qG#LL+sMnSEm&+hYTngt!?a)=ut&EQ zzLfUQs+sxg#}Cp9CQs>d({5&e?8Y}$PS^Og z&yDl$yMp;kkJhh|PD;g711rTP8!w;x5TVVCAw6&X5m*>ZAd;>R;%;WD0ltCr_*j`e z|5=EFWt)i^_5rn1X^RBrrUI&B-2x*Z+yjAMh`3NlVUtuC0mxt;+Iy5Cd__8a4FpF+ zUy0qfOPo0gfNqvAP-L+u2`RE_nlO^ZW;*!Nz8?t)xhWZY5GJ)vYc(XCS*D?P;0?q3 zzaVZr$crB2ZvF*}o}(I3!yc9xQd`qX{0pt0;*8n|rl zeXki&I%KDboF9W;Psxt~7AHpEOB(M!F7oZm;r_N}U@58;{&U=h>GMSRs`-zEB|o0d z(57OdbKsvZN-gs0{3z2jFPK{+Yf?h}^wQ<`poZE`kBEPefMf+CEqNRG3K7qe5hIZ# zp8dAt9(!GVZ(`nAaud;7@?EBCC9GfI%6~XH$)(pm&nMjQMX3h|uEQlo{i;bc`P%c}VhRl;=Tf`hQ-nI>w7n zLoTQ2FZzS+`C5`adT$iV82DGuh^1=hUOr=k$ET!;$-bRe7hEy>3)Ld|-v!*K<`NdE z74M+Sv;{=vZxGZo)AN;*n>^|D57j4_Q>42Q6)SUPMCg=y<#l#FxhV+Jg72cF>8r0L znyD`51@#gHb?>dTzAnB+Cs0&#&E3zcW9`!G#r?pQ!4{rTSLV)86wB1h-n1akQO|43 zAE|LuXF@E(iI~cmvYkDr0HXbv@bfFnqa0`;Tj7Cx%!hly$(C&P2va_L$|39{SGLZ8 z!9Ke7kVo>i_v}#+){($P5enyjyKGl7Hyqr}>jgg(f(c4XDiDVsfNnAsY>iDfFL zed|$E=tWIQU_euFKXN9A*0sQ-^N*fqLT30{oD=NJ0b7w~=~|oj3>=CQX04h)E%Bht zIqlhD(L|RwocpC{e`i0Bj?qPn1hAFTFMs7e*k2o-CUQbai?<%Ek`^GO3e@Xtp zBr!HU9Sq`d*s-+P{|HtS;I4rMrFqX>nh5g57e_Z%9-sT)z?SZD zFkxwn3UZT-e{&?@5UxEdP+3Fx$|vM@ddbfx>(w$z@5USlv!qj-T5Xuqbof6SJ-msF zvU1^78g`V{&v(rQm+YPS01Ld)hI!l<%Rlejt1P}n<@{uVL$X$IdZ_R0hQGj+@_NM5 z&)AnwO=Fl#Q(}IOh(GU6Su%T3w8o&jsMxcaodpzy(+GKPq`{F;D?`ROn|al1BhpgG z-3dneYYE=Omq`vIDnQ*CNO4DrBs78}2Nhk;nb{V=YU!l!KY_Ew@|QrM2P!hX@$_Mu z0bj(H`Ly?Xre1hsX_lA4p`(H%%R?f!M+))aK~h~5x(|~oIH6r4cBB!uO`tXmW&0Rj z5DATE;1%<=$Bp>{ s&PZt-CK$VI!o&ui37|SHF$ohu)TGxa75z$brKw;5}!#Bmv zgTz>INu=o3fJ@Q#D3$1v|qcMpZPk-x~GoMzVq+|&D~4^y*CHqyO{p=iGi=Ui_<@F7p#yOM9~8rc|@7hJWFu_zhBNgF-|?$ZXgAAbV9X4y6Quc z0w-2dSj+@IWYzv>8xzP|MtZ@U9sv%51=*lKB`3_`!Z_EB^r1y2bmvOpVdpk8w$qyy4i*8xLY84aAl49*i)gFNOi47ju z9fDeglj2J9;c4U&kUoTBLA4`MkK#aQBo-7c1YLprA&GS}OOY5$`p9(I_4Ia5{lTF( zf~l8#RPgBsKlJ)tn#>eBu6gJzm4}VIZ3Q}SM6&zllBsmsFW8>G*=@2r+G3z9aBA$5Unr1<5@sQ$H`UZGBBZ8iS z#}f~JJcYpH**;DiTjA%;CpB3$U&SIu7vC&-wkVkA?&+e?FjPU})vx&A+beB!K;?#Y z+@45j;E;bAy8aYOjjWn}A0twYaDhd>TRrxWqy44^huy?aEoCTC{!(JnD~oTnU;z_-cLuEwXUlyNCHGmbmwKX2QY}&KVkPKLFp>d;GML zRj-W-DACwbsB=C0`~EHYfl-To!y>6%F;*>MSo({_N`LfG+Wdxnd)V{{7*l)%^Vz)q zA)pkYiRh42&5|ZJ%ESIZT-LPuPrawObBAhT)(x4oR>mBUa&x+!zOcpm5K{hjTtHv%{slbH;6se4(;y`?w5= zy=i=0I|8?+;`F)(rR=*WOBD+WCv6xEsO%`pZgX=34}#N(&Y0*g#I#12e4L3(3-Q~4@00A{q{gHyd~%{Ll_Rw=b z`u)HoF;a2Q-(IILMxftWW~5xy$U8mDi&b-ZXTFH%jp*irwtc{>l63-1%>2T-Pmc|j zgF4uBJjikc%LNHvqV9Vg8dokp4!)hG6Rx9n>1x+?5z!xavbr~Lk97ZXR~%_q^@+xi zP$h}>G-`k{%ax-fmgOQ(j9*54nO@lMmmNe*O$d%(7-2AD^tOD^cAG zXbd|)UZ%d3cGTK*7;0fJO{w%8NgMuQwO@&QCC(r*4wU&^CINqOuww+@2Gjs*(aXir zz~VDKp`D31EHrdHZIWlN2Ma>OT9BQ1RU^tjyyhlw&3de4z{<3t0Ne9Cn@Iyk@#e-M zAW>4-jA(OZ1&I{>IrricnxUJ-oRMMc-M1xKPZBtf|DY-?rKoC>mhsz9o znFfIkqaOmj;J*`F*703WF~@;)UGR_+79yn|9SR&wNbem9sCn07eZ)H+oAQD{l+)fl ziy&#}Yq+jU)N2Q|0!$16WP;!rLWj<;Q(ko|=V<~%!kH5IaLkrbE0@hN(b#j3-c+Sf zfW0KZZ%M)twwhZIkBtr1h++X>1l7R=y#5+dVI^Ce^M8~fSWpNQUpnP97x_V0WI0}D z5JCB9XDPBQF@B4Pv?(5FLnyZdX+EH`y~w3<8`tLr^LYtn-ws{9Ys)l_EcPSb>_PX|KAO;49eOTjbSI&YR<9`>W3E z)aRE=U}s8G)cN-xE2ID`O$H#?Vz?CFR56|Oms$X?h7d>89Ydp|*9oAY5IvBHZcyzX z_Z}XA3RrN$B%O$LDovLxAQgjyuavE`c)>MPXh!cW>g#g>ZGp-F(iSp`W`Oa3NG1ZJ zE?n-xq%bk7cr%1urm*2>uus=soE6T!f;eYvRZ9&y=!FQHF~YhntQDbL=XR&tFdtMq z07o#*sXeHk0zMOXSk{FLJ;bR;kq`eOrC}JY9&`IrriK7gnh^HMu42&_kROoviu z=3+eUB+E~sY_G0)FJZG@YUVj?zVd>W@A3pAsl4IXp53(rQVOo#o@`2R^}Z3bxOv9B z_D1G{Q?IfLea_tlKh{)>?)p#cwFUimy9}b(rL{YsmDmqf@9omJ7^pnH&|zMrK1KiQ zT7tzc2b0MDI{CJ2XR9&I%8z$UCZn%zm=Cv_g3Tr`C#LnbExKjDtKl3!-&&IN{$s+y zIlf#RBsNv93l0Bu@hC|>I*HBEY8FU+;2@RT^{=oa$AKqG_~8e}cDe9-HbX7te8T_o z&z4EJ_0Z!=$$8djN}#j$C$lRq7+A-FTeenN>d-9T#68q>4Z42$9u*7N#LC-xIU1?J$AW~}jU$X_GFE|i=QI5uiY@5FU z?tf)IfqJmgB=A5T@>F8(YAc7V&{TLd!+c*e`f2BAME-wA6KkAV8RRiU8+{)I#@V1; z;w4>tSXZ#ixKBtkIM8c!X_8gQN%O?BVFMK^t@%gtXf;0r^o3OIs1t9sJF8b0nD$I| zcbZ4_Lc7&TaHHZpZgZ^6=x^{gL}c&<%~nx-_C6$@}w@KY$=js@jTLn zbsKn`aO?z9jf!pZDG#+Ip@M(bbyE--qJs+lplDq0s^3La<^H`%2C_zfRYI$K<6YTY zp-vX`gn|~USgohgTbvaitb}{uwupSt6@hQuy0jG576H+U!yX#^ehOYa@I%Qle&{%~ z#-2}yK8zlH?$KCr3PoP>OhDWTN)G`qD1<4cLS&X4NF6_`KWGPEQZu9tNU`D$+e#zkgKpkkMWuU+rvef7CT z_tRN0pw{PS=v6K^+im*9E9S5xLNg!eMDUu{F>We!^?!S6jOE`|u=~=2Ey6-+1{bBea(c}Xd0?2zU>{aRNK%5#Gq`p)f%y)$ z9Vt1MxWe2<%>R$1B>DuFlsx^WXyFfrg>(fcyl;!QM~U#x-aU4Yko zer7>aL*xP7zE|lN)|0Z1YKbPKLA0ii0|dj&_dyxmT}W|Qwf$CLApY?KS&+57jQKaO z!2l&PU&0#__gx}8A(&LJFzxsH!XR@~s})vxupM^g@@jMB(eL?rrJie!Rya4OLPH-P zT*8l=4OHlHL33GsHCl$JeSPA4?`)~;X8Xv#y)~od8ds6=vFImrvR0#(k81OMAAn|M z%F_Yk?G%8PY?q*yHN)Z^g&RZU75xg_-|&}r$Bj9ksO+i#(b(+6;CD#hOYiDgqcRV| z51`R#ca#4q>HlNwz2m9=|NnoZNQ97`EtHX20ViRCRik?tltBk$5BBQLPkFk z28_{flmp(5@!T?VpwIQzAN1kA)Yx?N!v37Ri-zEpX^6XR28E z7j|%0=nl_s3x;L2ex!E43q3>i$Lf-d;!pUpR%n9t2z=*5ME6iZy991B0jRg-P9a8v zdX3)q;qO-bf4jmkG5iMBiZ75W?60g0x1Dk{FSlfB`9K4183a=3jGw?UZd)^29&m8E zIFP|U8eZBBoG4iLq_bPWIm&B(Z52YZg);22j+d9m9Wmg%LS*t3i_A$7DCCpCc@iHtC4fybehAEx${~GK%X6 zuKx(Y^5)y~$(Fy8_FAy>w0PE9`6%-0{H!85D*5`Nr&5V>CXf6LnWyPfnz7n$9s=K{ zRwJQrG%M{mOWSQ7@L<)8%3K5y^aaKDX5$$M1`Z;-UOKI0wn&uVy=*o7?e}vx6UBOv z`4cCs$7lVED>UL-Ts#y)35ByPql0Rqal+?!r2F z*Ks5?R>%I+p=-fFrjx+PFGfO4jPs9}8svnErBSkbTUf%`v zHhQzoR)r)##r#?Sr>sF@wpVPU#EK#Q6kurL%wGRt(cgvvg(V9S*m)bHz~6alpIs{c z9Bs_>A`uMjP`PC{XZh0R3lPkH>P^@o2Hvxcous&BcBGcF`1GBjzENZSFApr%t+p7zY+|BaP~g$4;29QJUezJq7INzk{PjG8y0E8NCIFWD4L z%QhAybRx=zWdSw3x7_rBbni)G5ZcH9g}0)$(qlgie0AwC)||Ac$&ZI-&b6;rjvw;= zizU@Vtu%?N=Ipx9x3Il87efnLYU#lH`@7)Hb)HO10oN!i3KZ_kS<&>Y=3~F}HrbaB zjU0~iyuZ^-_O`wr`^cIb9;L5OYF>CM7dEXrOnIJ9;lkKFZns0BFK6_sg+{aBtayC`HZmTh%l7C3ut{<ArMSxh6E8}xIahY) z33$D8{E|Iw>cQ8$gJqF;sd}4V7!}6DST7-1_CohGJZXxE?xOPMX%m!2@n7-4vhI-2 zzU;@VtYRNwtF)%l#!O4kW6;x*+d3Opxg=qvnB+P(4($;K&Uo10IurVZK7kyS1a43K)C=VVo2rpIqYh03dM;|L;?4KquNJZ_( z&$4KRfiv6F)W}yw3U|a$QBleorM3&hNpyRIE2T{~vi6*T2WbMbeu`dA#88M=q9B^9 zm1%rfoH%^Crgt?lo=?cw!21mBs}Cwi9nlUXYRv0_ZY&dPOO7w2bT~0?UEumWInN|c z3R{6;Ec7)GAq#!#5z?1>D$l$Izsqy{aGk1$J>HTMi?BQ4vuu>+dh~?A_5STr&M!%}sizKp z9Q1oXL2&DMHQsjo*<>PP;8UZ)=8RbFHc6k~A@Cm4|1=xLV75nYa;#J~!S$OPFayFz zY+tM>>`&me3%?rOd2A6gooS?q9+i|Mh%+gD1BBFgvXns5^JN|<^U!fsxTvA>|{A97DapSYe>bA#EXB{Q3(7j3ME9uA~F zkbvQL$?!x>;8Ws01>gF111E$r`rw4nlMu{F;g~rI9tAzvQ!0hfrt)R3IccZ7aUULd zh0Z9|zp@zoUU7%i;iW-7mHeBZ(BHf0jq1Ms3L}9d%z2w;5hhrP7w?7RCZNfoeh=Em z+p0j@bsKl+>n&oi7BydxIw7|V=^}MgO7jX z%}`c<+mCL6ah??4xd<;(8$A^kRrh!+&y0d8lO9IBgS;0JeDM?e0_kVVyT90B1L7OA z{`!W>RN3tTOPTzk(Lf884!d`eEMG5EWXCelJsAZYxT!xzHQrN{^v1E#?tPu10{LWX z)#$f^>i``-HNRI-Q*3NclyP8^5T~mPbekchXO=EV7-O2Hw}d_f9qJ z81NwB9Z7Kgi3ZJ)e?r6C-m4=6m@_6&k*Rh?G$k>Q8=rJ1JZ~S0ehdD|q=%2)CHSw3 z40cj*Y`y*(9p18Jx>UsM1cm{lH=dVA(2(`FaMQC!hd{r(1a;qQkobmS6F?_V3(#`m z*&P3Cd#x7?UBiaFf-G+;zqFWZxKlw;BD8fMJU$LdZ!zdZW%~b0R6N6yM%un3w8Wm3 z8oZa%eDyZd1_bq$d6v=D_(`bP5CFh%E%Q5x2R{&Hs0sE@>TZsv!tQFyo=?)U@+58v zUflb_m!W3A{M15U4}RCLcW1f15QZvOF znXx+wX`jz%ZN%J_$%B^SDV@zc|MZn@!Z$ho3IE+woj0cxNH9+OMRK$wd!U&s_LuAyaG#VR82*EyS8~7Mc}p!Mp}MKN zP%^^x!K-xQjHFgBeZS_X!4)YPG7-ubrWHARG3a_Ln02o*n_tJwWENn2v@}kHmd0g! z{T(u#E`q(2D)ECc=&9Dvig)1amFH+}R7UGMc83)`Lj&hFU;1a~sS(TbZSy>5K@nte zexVWW4U|>e5r8@>*{<|s($BM3Dvzx$C**qk6hN$%iq72dE?EJX zTQtR3f}Q@Wg$t+?4S+M@g9~m51D|c1v@kcYyk6n>-t0gZ_c|~NHdvGq07amzb-jI;9~q1d!o&8qqRbSnaP26;i?#$)6WmKuEl!{c}7%65=S(kjcNY zK~O9e@yR?3b__mWi@6fLD2TqL0H$7{UQ6eLjiG~W?*iI$;uXHQEclqR3^dnCGcj;T zQQT3#>T6f^-XFsd#~;a{)l~>UKu^r{vtXR}U&U*Kz|(^qjE>9W=8wS#UU8qb7|3;5 ztWG*fd`{1kGdr@hRB+kQ(*A2|inRs@?KA@4o@BN>euV$}6}Z0dEl;mb1doEan=Rrc zyjB!#nT1^1rYz2NHJNLKD^WBR93eR)ue5n!V=}S3@mg9a8i1GtK~G&f=x6L6U0Xg4 zhNg?q(3CCFsu7=e2Rxi5xyxQkus9y28Z+a1QVel$yY|zI@yjb=GjD58=mv6o&hJhM zC?U+tgpBQOsKwZ*ZQypqM-M$)#XRSa{f2Aj_ney!&N@I$r_x$%L= zWoHpQvH-=F6MD)O8}^p~hrz?pOL$>xdN6R*DT@%rds6dHg4nZ`*mWLk!Bhh2zR-va zkqKZNEJ+L~_H8ZcfS#hUlk7b`1)e;4Cawfs8_HP8sGt=aaN5+_LB3;ki^3?(^Ah;R z$~NIZ>lcMmq8N&|D05&Mgf>`1x5o!*HFeTOXWOEL-skBJ8W?O1zDT37#(T`f)v;W& z5;L$7Cx?y4DyP&ozQ)ONA^+B0RRgXZ>mJd(Wb-ev?5DMfOhglTDhr)8Wj-&MmX?1M zY*{;~JP#Dv+^MIY;=y$l~~BOVK#RgG7A zrB)Dt-8uZ=r7nUz){J1;<%9i?d6WGN^OSzt;L4aqA(P9hV%{rvQR>K|62q&{HxFVA z$BYRjr=8TOw%=}NyFW@{_2xRbV0?^w2-`Esz%G}UL*Fjf%==a`cgJW!oT4JX_&t6K z*Gj`^;p1V_w7M2=K9We!eQja5Smax*pu%=c%iR2Kzu6;pBbtwogbjJ#8OB+@_pA0U zh$;{*JndLAcJk^9(d*%JL$ws@Ejc`li5Wc6H0+VsGBj&eTB059=adQL7wXsiax67i z)Zp1=DmjA;#agFqKkOP@D)qpYdKnQ*_QZ+-Chw@b*DAc$;{5r}trj(!lpN1T5_8OR zG4Wd(u1q~!Fc|*FLU_~Kn-|W=GI&hE2K`r$AS$fq7r*Kwl!j7c3b+u?*gO`j)6}1f zOU7*v@Q03SY;z(P`9fA3r>?=GOg6bIXjj$V7_D_+CxQ5?zIzk@ZeOy z=+498QTs+o)pz(jdzM}vckg<+s%&dipBP>+(0G<0#}%k#jJqCG={=(2Ly|8O`XwYR zqun*{oGm{QWm7-Z2=|t(rkGpt-LDfkOz;?-+~VSh)Lw7qRDSUl+Y0^Ok^#&2!wn@5 zhL%UE1IP`6%Z`T$iAgvc66gI441<5BpT@rpR!BV@WqW-u;%uHUw)VBV1ZGnVg97*? zL&?r^#6~YGHiOl&eNz|)llZU~LJaw3GuPV(bdCjW-Y|USGSTtjhw14DKiN7ZCDPXK zYkZPq=~?^c+4g{K2Rn_m2gYIKtX?(f%%X;JdxZTt)g>R5W!J0p!Go7)QZd%dN5c!X zlyE6g(ODyn6-8>35~TQsW9`Z^@%wNI`I(M4{qYt#119&LrHWStfiKFn<4!zuWiq`q z$Z%P&pT&Up+?h1EhHYmEyI>0LnKv8{>{vTw*)?>+xG3|tLO&Kec52_!o4tVlCVTW| zGI3*4cyCClCI8dwgs)ExzIFr#7}fLh-?1+jAHUzxDy(h(=&D#Nwj4^Vy5!?KB`-tU z=Ug~nhtJn|pfZBV$_2|b5!VUXt98^=SU)~>Up&~BJmAACHso#)xA(iThA7lkUx^c$ z+o3w(F0h}cDqAirQZy3u`5X^P)-y)iZLNF8tfpVHOGm zmwmXJh>F5$`?O_FUuh@j;uuti&2$ZxSs1!!CHFV0CDgggj+tOlTsuiTQxDHK_9x4k z=uh6B6#d?FJ3(WT-s6!(1iU(EFc#%`_32`$`%ikC2kKR`=$P7w4S|&HRT^@G{zh@F zvNV`gt15iSCi($xb zo*HSwC_a~z7k>6hIocj>5=LS7&x?$t3Kws%4HjL>QETDCvKg7!KAzx!U)g*?=n>A8 zLL6~98P08y)3aHBr7Fp}I2Wh{lktu8>xX|y$zu(EdS91whHZJQbM@Xzmb`ypeoVZq z!>Xnn!B~?1ch|?-oL`>VCWp%yEw#N4Pm^3?;fs}O6Uh@Mb>`^es_i?sJ4kh4IUX>& z_F}r9h@m3&z@&+!Ksv}|@+#uXbX;Mq*Oq+!?d3fWb+T+~ysoMYK4+Cv9C>lsgXgbO zswhd65{L%Uk;uozcL`~Q-2$Rsc7Ma3uBqFqHVO4`z3y{%vd6jnBOd(K(FH{6;*|JQ zU3%qJJhb>{afjZCtkGa|lv3LfQrft9%-$EhYJXX^ous=@ApZ$jJ66V%7io2tQO8{O z9i&yoC9OyEUWlvMC8*}Z%%0Qs=QunA@7r8s0D!Q39mHQ z(utSnT{(FQUSDymgOWuJ|4X+5N^Z1-`hRrWd%w%F$?yuE@!fXV-MvS(CsVAZ%rmPNzX>{SJKurWXSU%hda2xYycH7~ z?OZg56{%~@NL!_SCQBH*2g(6vo6{8Wx_<#JAu^X-R_4XJ4hWAD|6dL-9e%~ zv1Q5c>{3w)AlMPuLAH^2lp~*w?a^gA)_qQt%ZE0lg{tYoT<7Q3S6^&LW?piKF-+Gu zhr9H+h6rH2%QuZW*JkC!<^Mou!{_m-Cn2C|Npg2eE8mXyAx{+O#=4?QsT7Htpl&{= z!N%j<=RS&CQloNah@m)7$nisy*h&itYY~T2vp99u&4n>>c}MHJEq}J(?d|C|%*d*s zER9j8gC{~0TkG_Wb#BB61pKI6`>d5wVTnW)Y^1vMh|VRiic{6&JoTr?72mvo@T$Kw zl)N+GyzaUgxvn?AEtqCFKCN@DDropERIWE47;?;I3T6Qs|o*eGv%YoGee5uU(U`^-TKa&QnO{yOVz|SMC6%do7nG_*Ly z=esq?jXF&jE{}Fw9ZT&qd(EEWh=0SLe9K5q=F2tv(N$@hQI;ev92%xre(%lLm>IY) zRJXr-pqfF7dyG`YKe3Cr7L!~tx4PwM{bMrd^e}m)S)c+k5N1$wf_O2%;>KKU%!`!< zmW6et*qxEc#qG=Zn}-d0wU+f?9r_fisv64?YSu_$RF;hdmAV(ZGPT6GE=v#0^J%Iq zR3&=g><_D*ZqRX%8$75gbSsH(NC*Adb?qy|9X_mYo(cFY4<>g5?yR{)@_e`!I<}^z zUPU>7&Avp`ZnBtTQ$`=>43(Dt=0mUac>ALg9>{JGEOgdikPhlpX7sNpb?IK~ebLXY zPP{hwQm7dHY*tu!yM7f#0#i7ds{C7>>7T@SkQ;?;Y$ zvl46DBlpw9<@cZS;kXsfc<`$_)$}E}4&om)tN6qUT)xLeDa!X=jwi6ejQS_b_4pmE zM0j;SE}qE6!3t$RTDl!Y$*lkvOUJ1J$Fc>d>(yT~XGV>F^tl}GV?CfA1 zYE6~K##on+rmr4UV@IJJf$XPikHsS?FU4_kEX_Hui%x%IHZos&H1k?S{apELao70K z=zZy9&)Fauz?vB%v|hS_lcEHq~3q-9J$qXb<%AB?;T4)K&PJ)`&kJr zj`I4h{0aqIqGj|HT`=vLGTp|e>3KTi)_m5CW{Nn4R%5x2d>Sif0NJO!oRO~yb-R3P zphl}wj!}*J@2(c&s{7PL2Pm0bxR=J4CG6Lj@s_}yh|X$b0gTSO{1v-`q8r=0Ni7gHzp!)!lb~!_y&Imc(uv z?o#Cs^Cm}GMdI-8j1GqIc~J+zGMHx@m}<->X1N~Ax?dEVKk7l`7-cbM$6>%3QR@U!q>;y_y=`BoQt3)-N!r*dPuLp^-w zeAMlTwNV7B-L#=AS+u-^DZA}>Z!rC!#6y?X&G`A+JH`}l#|+-20m3+ z_(xJ|wr--6Qd?PtXhQX@w9D(J3mYHJwiEjm^dfWoIs|OWI?6!(y>k6ED)%sWL$$lI zO(I-niGm}elJC2hO?H(i@w>zVc08D0^9vDsD(MTJ7+k1h+!>F|Z0enRTK{BGFb%%g zFy;Je{R$tB$UIU7#SB_&`+b>rJ=h0YeAuC>305UJru>SyVfG+!Qlr!rr_ICT6;EV> z_aZhXpE5q$Pht+Ux;zfP^akxFaX)t5?YAB7Io&*N{C)x+D*2nt1uER1|ZH`Oxbt!Lul zB$S#)5+Op8bR{sx84dW%K8;Q7@|@^ruMK*58qEE0MAn=o#G(qx>IeHQ=&EX!e9_!g z`X@y39xdF2@A=$)DZKRx^>uB5dG5{Ne=k7ZzZW2mUX!mP8u%juYS~{=Z(wwNWxCJJ zVtVw*K|`fNlI135u71uEw(WQjsZuTD$8qtFu9)s&E{3f{RT!5w6a?F4teR>o1(DQ; z@O*c=P;k0!HQeP1AwJiv3v78r*-Q1C|Ka*Gs}I(nXXgi<59g**ukAE9$Gx=eCVKlA z?KMQM?r5D>_jXH~g>Emo#jT>(TKy6lSvOPPBd)uO1C`7nc$+i|7hmJ zn`LC5VA5)Z)pzw*!Di>_$}8I(2LmVY&Avxj};VQhJ1X%`b2&$ z7uV%qa&KW>gt_MZUUc>T9+$rz2Mq+~Jg$$hJC?do{#ZVlK24N2094+VCjZ@ZPJGOo zE4KH97us@{NzEp=r9XfbNKgNrh3(>Lx`BE%=Wq+_9YUYS$_*Lx!*2${G#l4b^aV}C z@oPsGR?%J5Lt-!$cc;p#(i_ZRPXWW~l)*G4_PhZQASj_7v|+YLVUl8;5mKF(OenFs7cG*sV2 zwU0ro?wj|^-rI#@-w~X;`gQNSw`WZ7g<6d}8cp@m93w6@Qp!&C3EK0s*=LK>`(Ms?xMM z@!FP^`&5mc86rp(pXB?1X#=ZTET*~Vfr+R29 zoFIOcrd#MjBDzhBsmbf~Kep`=fr?;@*^1yS+@c>lv$ZaYTsTA)9WVEQVGh6x7zeVP zP@I&V$W@SFRZ71mqZLd?5`J8AOc;~M2jtMJEu>YYb=D@=-fLgmF#YXo{hT5BKkfPJfVxXXGXe^_S(CsXU&*- zu)XB@k3l%td|N@RmHGVOm$+)Hm>E`WI!ozf;+zD8VQBPy-Mmc1>D=@t!2zL^T`Q06 zyQmYH7xf15#Sg-ZS~RU|6m`uM2N%2_nb*D2ELvQ@LP%b&QSC*n5A(UY>b;zRMN?g_ z$mQ2t7Mbm48&b1P8F_uy3v6{`q;uE695594k zlozm1VY+Ct_mwz54+2w-=};Q7?5hQEmdmAYCm|l;__k;=2il4p+(iIXKsX%)JCkmm z6)nF2rW)>9sJ`Z-ECv|R1JFE_i46~Y@h2607jKi{O$4=0PqBmNI=cOFfar=V?B&%c>xN^VV3N8SE-g>~~e+?sMMjU3K2PEy$lC zw+-ul#5g@=BJ{I#s9&2nVL%ix80OC&XsU3~v|RMb#$x!v&+$NlF%Dx%s{AEqrTqaa z>=cJN9mMIT^n%60M+8N&DyvAAti#-+JQ`?|W=4-^Kd0IWG{(8+4Ry~ofR!rD9!(yO zv(})b+LihIv7=b(oxC?u5QH9yb|ZgP)qTSo;Y^4+jo=UzzS@w=)AHE16g=? zlI__3;!1y#L+pW>7{#?W%-bHXxx81W_jDK(*E}^4*q|AH-(48NRMqg^ro+@T(pd^) z?K&<}PM0mdgYEvh(gb!j&5A@RtqeZl`Y`y;I@fs(_-o6Hxi%X&;o`2#q_?&7KRHKA z*g*|b4e`L291ugbSvmdJI>jC2MR*?tiIED?L7n1if582yu6LNGSrVZ5LHhEW_o7kd z+^xm7Ng5C24GH7$W0b`_u+;5+6AbQ!Uk*fkdBP+Ha&HEHYIW*#Zf$U15gG&ko`K+b zfg{tB%U#}~ewId!YiTD`K=3Yf@*flY8T8{7RUhxc@@*JQ!2ZG>UcJv^oJLyIH!U^f zLG`gnil%Ut;`R;XI4`-cMLb=8NXn;8*BEl zsidnB$Id?f#!+^=_7Snin~|9q&VwKJ$RSu)P0XxoEh_9q)SeIG%XW=PAB67nD~+*q z*%Wa>l?0f`CYg_yZ$au1^P=cz{=~di9e^Ox;{jm?5XiTk|B;_?~NVc5RTre&E;cqNu#dZp|_JU)CEu{%x1; zR!BzQPdQPLc;2|)^BrDN@8rGN=kp?O8O&WGz_Tx`_A=IghT=l50?eynGHhRlzY`N_ zM%{m(2TZL`Tb&i&hq+n)o^aTQx=v_DfJvEp^&Hu>Z>bw=IGAlbi7VX{hZ3(Ci@pkv3E{b*0Y_^xZ>PkRNaWe8yeEvY zI7wc^97}TL%T;4ePS1_F@C@b-TR0CkgB}Hfmhx0G;yNpsRL>kusvU#z z7Ybn7YPOR(p5a&~$N6#GS#EasDUKFdy`pN!s5vv#O@;7%7jWj1rD`g<4I{kgIT%!% zMT2*qV#XOtX9&;_It!F_9aU6kOdRi!4&Pz?mx#gV_7@-t#`wIj8^k3Gonn2mPTk# zm&esgQ5V5he~Q}Phsz>Y$$Xk8@*h%;A#rPz6#0q1CG+xOdu`X9l%foaPM~CNah%fT zcB*;JWXu0qGYnbJk}@;Os7Qx<={%oCj!+(daS&*vz(3ps9EZI6@|i^;Xii?s=~C4 z-04*wWHw$R+lIlVOMlC#e25J3uZQIROP*vM#PiSsjp@50+_6*IVAF&Renjly+KkI0 zt@2R-9c0pZhIaP6`KnTqv-wbjB;W+80E0ar)+*d@UC(*Hz9VyPgx4`MGfho`3e9cx zl3)D*!;ksswN#wQ*-uG~x$I*NCCxXJD|43gN-<&#AoQfa3H^~6^SH`zDmmiVuhoNl z%l@JyLw0;7P{a9_4GGc6XNyER!{6%VEs;v^?H9dgr{zyu597&`9GKc{%L(qUCU_&< zS-AOw1=8fW@WQGWJ^D@m!UkJ0>_% zSGF7Y>W=Alvb)*<@%|F8e21$NID-qj1*Ea~c`xSI^tP-u$8@uOBhKsq%?)Dn&F1$U ziviP%KTZM-v>81f3iSj$t@+GFL=X25gkb*{LU5R$qmPnJI9~uWi=nlU zjr2ATf?_KFGZ*4Qu+^_zkDI=63|{!XURFj1S;f>Yb+}*6a)mE%Puf(Z?cgKb>blVY zsArc14QO*dQpv{?Uclh^hmsTZQWzX>BK~Wo{1?kJr%%O~?Q;Jmhf5gT#=7DQFOyZAmX7=Bp3j`L2D04`iO7UVwi~{l)UYkw9pq@Rh@>(|RKIFrdzBGayYCt+ zCEwp#)~OzFy*bNbf8jnqr&!J4p78_^^2c zSWxUqzo(=n)ysK1#I5XHa73&0F= z@+j^i&OtT@bYGR)Y`XIc(GI4Pqaoi>Ax1W6%Y1%K_skEJ;q3TP)1H$KagpP-L*%WL_2cQm zi=FeR*Mrw%)+c~bKS~vYeo_-GGAVJ1%PyA!^fPn{qVDFo^8YhM!En@PYhcX!*#fh2 zc28+P?Kev)^0*8}tJ|?YxQDu#V&o3h1@jn==vncd(>56B{vbQKAH#lrhz0*kM)@7j z$yCKLcsRiBv&={k(p0~S<@REud%(KTgHgT*o#ob!`w;t{8bo(l7KeA~xLS=xDiMqNRlv823B7DV3i^=~0Kb4XD_HOFjN!gX5 zOG#@X!v7)#CN{IlRaWnBe{Da9EVX2QdcDh|ay#lOnN^~XC))t&57AOr7IiLHprxsg}S((R#R6Jlr)AMlUB*&DaRV;JG7cM+o!Aw z`aj-8*6pxM4e)YPvcuKct^;YHHJekEug#yKiRqM%N?wWK8qml-E4X1x{pzPzkx>mH zZIhqPM7wU(n4*}LhcX6B`A>2HBHeM$u){L44St3S)*#wW()LRO-xkbHAs`oE)KZ^g2@&1xeMg(xvkGg2FLd&_7 zH|NUXok|2sKomPvyD`SHOS~{g0}m-Uja!7KX=f}caVLTbkbCh@Ow46R;YAo zCHQ0Nu9F>7{Ypg=BGzD|kj+c&Ah^kYOqk)dq-D93ze6^hTHgL9gQRzUYi;ztCm(hW z9x$lbbbKz8SL9Evpa2x!9G#<_r?^Wx>?lzcp}8&dEC#F1jgsvbsRd8 z%7p<-H`A~VcD<+=fpIm2Y;=Oj_pZo053(6AUJG}T&NT6^8bPE7;)8Sj-ZxQkN%zfv zP@!Ze6Cyp{+~RTSnGup<^B(Y1KdbqE3!FFL;wlbGw{4e0dSsi)g zhnL}3Wve3MZO*qWj&O1uF`>x4A_F=lhV!nW`l|1ffrQ|r(gHtX2@rr!9Fa=|j9TWm z$oVTr?^k9G(sv1hTB}t|T*$p;7s(hl$|i3kmT3H`5*Qa8+A`hoDbaP-noQYvt?%&< z*Gb}4vlBx0ewjhuk&B-=TkH0^yxTz|RZ~~%oIA%crCupK@ngl`gw2b}8})9_RNM&J zu&HyKP;U6#hCiq2yVW0}_QIQS>wpgV1nR_5s&2Gv^M)8ubofKuLCeWFGztz>&ruc{ z!!-^F_;v0j1M5*U16dh0gVNPs`^@BVDjtxdlo&`PlgfutS@1H@D$AJvjNiYeUT-Sa zb6rZw@q58SuJMDsN&4@=Qu7%sHB2TrXc=2>F6!04+i4MuZpH*)onRrv8q7pId;#Bs z7_q$f7$SZS(+5wfu$bGhBKA7R5tn!)XT4mBK|24B_6gNn@PU#j{94qiRK0p?U7y1t zX@?3f0PQd3;%CoSp=@f2sAK*g1q-gc7Fs{Lb`=(z{tV zJ`Q^)jZHtOdUsDde!bgjYU0Vk)9UX%gQ{;;?v$q$sg6_5kju0|HMgXHSZ8kHC)sC> z0|YLwB|F~-Sx|E`blFUlLD=wY67`kiW|FZ#JSRD1e)Vmko?2mF;v8Eb$;|rCOr_h# zSOzV~3#J9y%@Ls(!=U+Z)G(q0p$6_V<7@erhea0yksO<5mfQO_sj!Y8?JxgcPrXFO zoSBgT(;d9#Lb!tsH44P@sn8aNxU(L7$2l5|_+6RNgApxyFzN+^(R)-RA6(uU@*<2k zKX~@{hUx&k2r!Hn!5848H%6c3S{5rK3%EGqRYmv$@?SV<%S6XZwNS+21NmF`vZWIE zAAbtGJ@b1th2gsvdT^KZV;sHE2J?txi0GS>5|<@6Lb2_#T#Hz>HsZpnxmHTniBksb zDp>>P5^yXa*>lgR?~A%{H(sI1zLGI%^)TL`kM~kNaw)`Ycgke{IfWnRn#@z_DL$tZ zLv`T_8vGfNR9E5DVak=D9|2|kb$s~7o#`}pRIJO>F1}!C6v^X|#U2Bz%ola_VI@P3Nwo_RHwl{{$e|J+4@w-(5msfP@4Y}VLp5fCSEt$PB zwn}D+^}wKkMeQj`y1#?W6+Jl_v`=umL6cZp;LWkvVVB*?SG1AhnmudJ$n8dSTvsgY z{#av70yp0REoBp}jDk%<9aw5?4++pU9QZKXK9@F`02( zf9D8%WA;hdn(q;}-FegPFkKX{gz?b#sTIbh@2viE77FO}Y5_(-A&&oZCAE|oIdhLE zkqQ?>8tPT@D`r`JkC5O`r$W5#sKeFeXf`?ZV`?P1s)-jDEA zwn0GGXX*-kAN7WCwlH;mbTeM4Yql{HZTW1G9Ba8CPI+NW>EM<`!4tyq}*Sx=7zXGxPi!d~+k6zPO{mM^- zyjT`{zH0*~=PKjoshVTC`=maHd+HZp2&QVv_K45t4Ldsx`m0RJ_7fj+{opk;B8=%` zDvf!09OK^(Mf=6pvF;=&IB^>FcR1 zjW2O~D^F)rOC?ch6VZA{NmQnq^__}w8o3f7U-IlLoyOKTj&-Z=NeOFh%Nv3u?0uno zLixMov)4e|l^~F_zxlI9A1N6;V$`$d3c0x+|9iKZDV&>#unN81)PoQf=34)(YQr4fE(C*9A{0~F{ z`Y+^iI5e(6)XBS$6%t8%-!Mjy{Pp+1-I`FIB5C1GSWi?Eq`+? zpN+!c|1apM?g>D=KWTL4xX+<&bW1*RM&{(NcFYah@dmx;Cn`krf574Ys~LNUApcr_ z0$L``LOcBm_>a6scwv9ILtEi~)i)*L^=*G+;%Y3a@^s#T3I+P6OO5=z^lU#b!InR; zNs{?~3xi5`ms*0vM*~|e5;!<(YiRL4w8ry- zx23wQzk{}h4^BDj9-JGIJD4T=)5;h}*RM*X`OZr(rY@Rlb#%BNPgfI>yAk;bsq$oZ zI@S2hy$|w~1&;d&IW$53bK)xDro=z%_kUFK45*Upbz)_x2Bl?e7-O7+yFB;?8mM6U zDvQ^3z#ClgW%Z%;Uu)6lzE7clxPxK%ULqujnI-K1ZqNt$5X{f=TmdgptaR!*xR8(C z|I-9IuCp`_85u79HZoAggo2iDq{Co`_5_;E=cUF*W@S@q|1yBK&lqJikW*jcKFbXq zVA!BRB2={o)`xEHH3jBLNx+M25WkQ|UrI&vn=S4{N-U!wPw*PY<|^8;_JKMFT} zsQjuWn^qWhkUl+dan9(Yp)ryWOCVb*JOnm9<@({z&1jw;O5U7st;>2mryynLpJkhj zrUc-q6;!@0@Ok%MRfW{@KXKGKv}(tHq}tKAm%E*&A2qm_kM!iwm;zTfr)bE5Z#oFI z`nAK|O3k%qV)&_r0AU+y6us9CkEB zw)%Tg;(!a7VmRFkY)4)5Kd%PDk(v3Y^Cst2h$cm2)jr7WI0$YuvrYXE1%7lMB#>?=&g zt_DHgkSRL#8vCHy7s}O8z5_PmY!oS;tC#5k=+0Q3sgY)VKV;Qoly}}>f7A2gm zdcRQb`KQ9VnDW-n^2|127+HJHCY6Xba4zfw2uO(iM`+sr|29@NpN9h z?@;;6Vfe@kTj~@)3Eo`P9}4Y}Hoyq)0>aNjApAgM^`AZ#!8SslgBhZQKwqd%liQ3P zrw^vXE>Drq8kwrzToZMIukyjWm#eSHUHVB8mtyrk0XVwh!{QXNUq4~&gb0kCFx{YY z-98CAz)=BmdR)6b(KQ#u2amJoKjfI5{C9btGv#77y-j{gJrp41BU?m6H8rpko`w!^ z@|wl+AX|j_iKmd{1C{48rBEQHm&oZmI8xwq-ZIFOSzLTf{P5*1p%d(d_pPOOcOCIJ zWC}h>$ER`T901#X_J%QitV$}qe2I`D*U**c3N?}k3Z25&;;%~@H@SN4=bxu%(>hsW z#N}zJF759yIZIk>lsEM5V_N2$1f=8vS-Q2Wmo?S$wQSIZqi2X1URVLHD6N|yGUBv1rP5ww%4Aga&Miq$omSv%i~wS8)vnxh1-sIZ4n^87`Z|hh@kXt$k>F!NxDA%fMJA zASuCXQqQUFf>qf)fnv+U^QvEd{X1yCzB}aCZ{8*x&q3=xlOLRwAeT~!E0;Yi4s$M- zlUu!MEQFS56CMWOX_$U2aw+QWd*5K;pa&#cfi$$52iYX5b%qwQleIgk8G-~Kw^NS= zDidy`vPcsDqx0&zOw_Z0GiSQnep8Zr5ou?anD`z`HHkzrd}=^`1N9navL7VU%82BM zEBCwOQ=&o@Wq%64s+XT3cWXM%EwBPuL$4Lp%5QK8tn66<(iX*l> zR)o;nU5&{snoI6jnP5RG(5e_vfpUQgRO!vM$nkH2b+-*I+=8`u)eF}ZtWv#61^9BA%c1iDy*@R~b&Xy*%8F@%?`1Ek=Y7 zE_TQ*)kJW`NBD7YgE=cFp%*%Okx`R4e7R>4nmRX>Qb89@vt=EwA7^|U9JnGD7` z1HCsA4kR*4KZfBxvn?U8un~G+7GA7bc+f5~CF!3qkKDUBSNkbqfU$WZSV@I2yKgKO zNrkKh0W|^vEuZ-r1~S%rmCTfD$aUeWOei+KO?QN`XC-<~0tI?@oh2#K3|weXi1PVC zf%f3Jvef!9k|*~|921mUu*KOd27=UgbxltG;8PlUhD6Z*m+UC zvZHg!1N)hTo+cZ zvE-3y6LIh!?{FnJw@&St+X9G&Ucs90b2UC}+leD6!aCQE7VnGb*-Qrr*^#Rfz>XU3 z@uacC*5)^x=1IqR7^NT&Be$iGX3lPTETJoPf13@tYq*+(zUZidE;^hs7adWzF#|T;_EEgE90(;zFJakV z3Q>I1=DLV-o)!m8I|OV)>LEtpbp}_@XwyH&!?*|jsej^3eMLxf$k&=N zL;S@<{#~-e4f%3a*njho$XIP{1lQ_wQnYz7$ym&pORBY8J79F|_}QgvP1YA7`*L_1 z%~HC_v2x$_@5J#pE?NAH*j_Ds;HOXh)#--FJsW|(jVBpYyJoa2T)a%uZWM zQ5ssQ&HuEUa^tdMdT^P1RNckJ;G$Fdvi4WMZEz16x@>AE^I`W=l@{rWN7N{0@e{3n z?5m>cKAf<*t-fD|A*t03Ps;bIFlB%PkV)WyoW6fDXZ$LU>E}$0zOTB#yq8J^JUHQy zW(bZc9~C3xH_Aujf1!}4T};w1AKZR1 zx_Cb{-1c)|*FI1pDKF_xj00)0PEcRF0qHZS$_R zqq=>x9sjjE0h{C1_#L4{#vlyp;(1m{!DK#G{I%9sUbFF;KK&_~j(K|h?+^F3_KP#H zACNbu!&5$yGq*7bab!^od1a*x-02I`tSTi?@$)UPe3EHA-#Gd6IK!-DtXIew`>}`? z62sYf<7gN9<(c*4D{sw_+FH#bHSwPkZ^#Yfl`+eKD3`_B$v+;Kx|+;~)xQ0h5~+wbNOb}OSV=;fKLbYg7pUmfpm(Ucx=fj#Qk8b?6P`skY~Dr81@xS0QU+^^;s+;Y39pb% z_d%Bf?QSJ%C?<}Wh9ys5Wkw&=3D%T4A1+Xvyk-Q5;mDYKNrS=bKa!yG9cfOdi~Hp) zcfLAB6Hqum@*Vdd#H!%{3I!+}Bo{(~-^hvT^4Z@261cE_PjNL|U& zmO$AVhEz`Iph5oL7p@>cQra9h8Yz|RDgdgdN^!A{-TcWo1uQ{p%P*9OS~?Ad^NIQY z<|v(siYH%f2bq=HKqI)z-y`_r&$IXlU%2%keISUfqzZ1Ml|X12}0=EkXsT zj|B7^o==3x>{u9Iu}Zf?;5J7$8$|A8rCo%LDvF zFRKwOjwjWoEYp0hnk@c6W=|ls8|uE)QmIX7VW`Sunun)|mHJxj+VJchOs5v!zb`e-Jj~5t|1)^`D|i!JE^pMQdibmlC;N0UbA_M1n_M2vl)_%0qNkiO z)mUjtS}%3CmB6LUKKe#`90zBJ`vFV=%Op~13cYPd^sYg8(i@V*vatIebsV3tJu2`h7-CZ$Yq_`8)ACt@&?=*lOzD z?WaVP4#9HSQ&=uD`IlS7SYsUoSNIURr19itDYvqrE{KrnDvR)05OC!v#!ZH^(e@^{Ogy+Q$A ziwX1_tL1WQ(b9A5lJ3gBABbYzsH)C&Q7QQNb88xJTTrEt9NekoMpF8nVJ?<5FHU-( z+N+mCi1!dncY2}nm;^7WIf0LmAaePn1tK{8Ec~8VN1ngbNp&Bnqk-db9mZ59b{3#r zVU#^Y;Ac|*#bZOpbrqo2MD8+dS)z@NK%WrFx7Mg$1Y2@J=vzK$`;{z`m);k)vy!XQ zTiRQ^|G8U>LqGxeke8(lpJTfASBU7Zsjb*d3P{*oYav3ob_cd4Oz0gl(n>!lTp`eX zf&_r>CBo9s)V3`ApF1Q^%FK?xN^fDe(2a4TiM)J15K+;}PXxvmRmyc5=>^ zY`s?N{Ef&&*ry>2cA`V$TLe=YJg;&SS%-bG5B4-)Q^Th!$)rS>C`5pRPGmLbfL94^ z-OyO>s5Ql~@bw?e2pR|Kvs*N1Bd&qjE_7JM={cZ*K6$?!laC~`EZ09@h>GS~sbdL# zSqVe`#4=D?em|NE_g(m4_kqC5+C|164r;q&O)eP&4sxi;9C9pInSp0DO`Z4~)SaV) zec1H=__#k>8IKy%69Cp5GC^{pVLfz4?9)89#4j5LuzJ^`e{23MiEW)?Gt7?^D0jnP zR`wmJ4T}rp1lk;&5esijgWU~E3ulyGc0~LB;v>m==2z1S_>M=a*Z+BD3$nP=a-!^w z&n1)lnIPiQz6%%q_grFgO0h&E$QZfUBJQ;9v3;9=Psc0mTY!;2C8Dnb@d?jRwOw%` zppzK-lU{wY6@35J+j2yjf})iDh{P-%b7pU($;y+FbHjT#xjxv)(+RCF>l74-Mysie zEVLy1Vh32qj`I?nyJqFnIp?ab8JJ^9O`hNu3ZN_dS@tOVgbY1u9I=JzJw}F?C?@Pl zTI!gg1^fkPb()q(8`jtL2JvO%^e{`zSY84347nH&L)DAgzf8}yNag$JETlyBH#Ts@ z_es5DrMPGB$`&nokNqY-ItFo^xxMg$0t1%sg^BhTNK-rDe7J#z8^L?ptIVBas3-&s zR(JIH=lWAgu2B68H6f-jh*o(u(C$0tke|NY6tSiZ^^IKtePb{v4-Id3cX9O*LbyM? ziLEGe{!gLwXT%zzAw}ATJ0(hU`BZ8a?+BTg3HE~9-YgvaMX#ulq8pUn7g8dSB3 zX4IO(#Vg!LUgfZm76XD0uv7}WlwD{X0h;0DIa!@N*f#hwC!oJ@lYqkhAr8N1)(uAU zmTFJ02z+S*t6_J2`w1Yrc+&om&|b&+K5>oj3-6wEdAer@b@G~YBOhXedB?J1J9?a0 zfRUXj?{}SAE~n-j;>L^``pMiG%T3cg&H@`8UVi$-=K;+>mKFykzp68&D7^B_dVP;w3G-WC)5rXxM{OHgFXCo0wSMf65|MXnDs;X`k)6*B>`mT@i5E}C_jeb+WMSf^u#`XE0d)^|)5g(}3LhNlH8Xm)% zT${eF^`Gl_ppt0;SjVfwM!}2S@Hy0M1Vy#~ZC>(3n~@HPw75Y*wZWhav{PLS*0hPo z7=J3WF`sswhx<-!b!?44*^d97bY^JX84w3OWI%o4gNsgJxJaev0#$e6=DE11Qpb*; z9vu>)(*9+G>LHt=KyT#N4i6?M{~W)~$TsD@8(V?NG17=%nwJ#yX#6 z3huJB2d}9{TEqjcPG34k8+g}-eT>R;JFGr=5$ju~fRDCBak`Qw8`GI0K5u7H$y<7% zDo+O|R8}@LmVY4Mnj{hE-4hedy^)j{y+;uk<;@gne$(BkpTv-+ zr{FQoDv?m5#}bsBc(J@EIsK7^YQDavf&yLoHELKn=lKaky;oZb!+Y2MIk%MiC} z{-m!+0p5J!AFqC89r!QZA z`EFbk3GsuXAk#Kcog9wJQ;?p^*^nEenWmxy>C|6IO-Mf52=XxF|MdMnwIyzKu`;$ur+SPeZMM9w;a&Q)w0D0qdy`XG z+y^%nx4l(*e7>}9FkTW*=HyHvlXzHhT0V_r&BIzto?h=`pS*JT^)b07`?Q)9Ys6SO zsldyBxbnn#;5Mg7*6m6}U3?r*4P-f@Crsew4}g_wnQ;byFa%SEMD2Nc;18*T7rq`# zSk&62s-pE62=d=jtofThb0I?doIe7oNd965N&X22HYd{YTJ43~BH#Pd0xh2dvHkwb zS>~&s0DeXg(8+vT1-RUn?{0%s6cplb?Bk!`yzXDf-fq9(k1QK(%s%^o?}DMa0M@hvG9Qo( zz5mCj{wS1mAj19qx{uIg4;eo_P4zTW^J!{ic$s*_Hq{LW$o51Ft_r6HJ_NE3WYzIG zL4B4q;_L&Gx+2A1mq_#?>xY&3YVi?PFQ5JNJ>Ten>3oH;4iR{Jl4=z9O@5&kext=l zmelLr+6~8N9FJ)^Nzk2f4;YNuvUxRFkre81@o}=#GOdPg;%Fx8;v+LvDK+2A*^Mfx z*5-59I`XW$i)Jq4$e0nQg7!G@%Qx94fL}h93VQj2KmN9BfPuzRly9o(mNNzdyj)uv z80S%-5?ijy`FSx53@pkOR6jt$W246`XVA8p?wjTURmdc@Zc!YoqA=qAj(YQ>l8=X< z{MDog_>2D&^~GPHB;dZ&8xAe(-3&C|#q~Sb5|V$7dtIXC?n5X(&DwKdE0C=P@S+?)xXyAP~{5jZ)=*jC&rd*q$e9+tb7& z_C@xR73Q8iN{XN$&a;mOlPw^nrnaC=mgt&dTGAz`(CSVF*Jn62j!j$F=7Yy-HJCZO zazTmHl%V}M)Eyeq?clwA{MZ|A*i(5JVgx8OuAT}C#}eb3m$X`XgB{*o;ydD-eo<*O zbtHf$r5!jIE-9K(o-lX2th0XI%Yw_g2eTlS2al61zb8#Qms#@-?v38zhV7X$GPmi< z=1M~);EdpBhfJEkDiCSc!fzgIAPr>dHkWZ{vQ{*{q+KYbYgtOf(uc?Y#N#)s#8=n0 zno4Ym!3PTGn!_OR^-?O8GVKW&WT(%;vNi}UYcy-mq`P1CT@_2bsKYmK;D@FFKlDSd z^L_TBDx0^H)q0NKx`2&&9DJrW}$OuZNsW!79RVf;*`xCj+sjy0dnhmdT0TEvjb*Qz_k>2VA)k0eBuPHDs7hH zph=YT1N-dJij{*rT*z#y3$lXo%PaoDg%L0Rgd6ixA0D55$$chs%1jDbDmM#x-FBQM zljiOqnPI^wV<{@Gs#pNbVL}SikP^cKjv@8`{pP-E`|hf`g8Z)}dG}*)gpYWkX(xW) zsU!W}J@Rt<&fc=Pt4~@h-sv{*;~p0xZ~YbGOgI)TP)~g0wKjnThL%#aNsMoy>qc%b ziOIP4%;jx={TPjD#hp*<>^%t%7fL#29v8E8XPC`ah9&#HI&P})B1!I!0FRQoCHVWL zCNU7~h@4RLyu+IxDf-k6$m`JNw&u0B@d1G-^7IOt$41~`tZU};TJWK@)!cF2;t?5A zb0-fS2jxsu;IVcCs?wpC!298OD`EtXQdDvS$p}&bVT|~!0^{w+!Fyd!jxKWo#BK^Q zio$rUL5qCPK7$%~5x_QX{b~;P=>z(vC>Hs)P6wzuiQ0rS z&TLtn&sR91D$(l1J`w}=u(6cz3|r8*qP;*?itckp$8K<22+qKc=mv)8|SYUW!9XbsUK@ZZu(Jdf7@H8 zr(rV|WF&A^-c*LZ$%36o?CoHS8$puX3~#5yxgtWN99(wfZ}B9D%r|T|$5#~ca`o5W`#HvyCV$z zAr$bsGPXDpZH01SCM{KNRAPskWB0BFKv`O}z7(d-}6q4yoFtQS#CoxgTcL>1T%$%ABA=^j*va+)@&HU59oBO9>|>#@I-!0e0281q;5_w`tO-a04S*UF)@Bz(|z3|o=2oK4EQre&dFrr z0DeEocrvHW<3LDf210spT54^VKMR>TY>?*9DF7LD=#_ma=vS)1mkm1H!^d42{(5US zM;9G+%jlwk5iT<+t?hpNTB5n>VRTY9boP-W%s)6+9>E{}pA#8WbtfhgAq!1p_Ja%W z=xxQ ze&C>_K7B>{6P8{qk4i6fsUs^fo;h{Yr{b*IsQWfgTK3@w)q#A+CmYpEC{Ss&x%yt# z^80ijgNNs}?obSC7JXs=gc)KqX%zmh2KK&d_q}ft+F4+U77Qdw9=yAOg980|e-*_F zxIB<=cs)^nx2dDP9ruwcWr(!_p)X~cT?uo|MvFKe#?x2d@8EIbsFwYHzVpK3EI0gx zmXGnh)YDwBTV#)Lv=Sd--~AXFl4JI4Po%VaeaCH>J;?2z1Isp_i0aJa-)!AK9C&** zP=D!yodx`kWcT_6W~d!@WJX=nHb@PoDpXqY0;e%PYaM&%@3p7}#YwD#zsgx<7jRQ~h8^?h+suagPpSx88)hfjGb!N(Uw$>t66nuU6PvaTrk! z`-l)d3S`tn7}S`|vH5zn%MS+3vC@Y@-)!UJEG96G$tz1}R&2Zcl)puknO?(Q;k zY)MG2^{)BIg_*&)wqAUvd--9%2#5-)#jseq$1k2=PS%mdd*W;KVfk8rRJujsJ?vby zSvooVWM<)?Fy0!bw$_A=fNkh&+(IMF8!NUuY>3B@rOH zpL{60qD(Vbb^9Mnn8aK8Mc<&rWY-M@fpu78=o8X7K%QWZPzeNl+<8?1an&CcJ7llIteB>7IubOdNEq>kG~nt;i}fG#^nC0IOenBVQn||fsOM1?3Tp!mBGZe-WA<+ zdORBfN^QaRRN*{N5Sh3X%TVuX;_lo&Xtl187Fw4!k-Q{!vpLjXr zDoN@k@Hnt~UYevVh_4>)4mFyht&4Q{egMWaS-K3}`>KdQ59<~2#Gp=y2DA@X7bxCb zluF~av@VJQ=wkj8O+~Jbv2}z(A6sFB%v{Oi^ONBEEYv5&k;myF;9odShtY)fO~j)` za~=IOw9inW={KHvvQ{09wMalv^jvR?mH`ckOHHU~HS|#E6@`}yD`r#x>PJWmOjmr?ToJWQm--d_O;)-9_bjZD#!6q(Pn@CvwoeUGJDPQ zr^HnVu&5Y%!qqE0^V*PP^6D=)0}l*|cV~b?CvwxA6}89(nxb4c@Y^E$k9GZiO>y2< zO`K0z9{w)uxb9OlP+r@DhxAk9$|@i%_V@JCVI&ai2>3-ZV>hIBDbZr~K*A&6Q z-s4<3($sUH(&^JTMkCM_8UFvlSlEjb+q;=)T03g@T zOt(^#F}b809`8xw@yGUf*#yHktT)qtn&Wa4i@h&ZDk%jn;=bP{T|Q-|`kDPRMdbo< znQohD|Fz8vLaG+)R-vl*lY$E-NKfF^-JcF96GZx(+4 z90DyC#PTYS*}5~foDd0?yAW0nGNZt$1K$fzpCD)}&z*e&HUuAJ7axeGG>rrdgW14# zB>yq@LcuBCaJiPP(O3s5>xt8*hz_!TZsy_o_f{4LW6)24!xGmfR9cPhJgz{sQ$ZUbXr~$iwbfQ1UP2q7 zZV+k#%sZ;uRB69%u1i$B^7&Qk5lNN_D%#X;Khf*$bUrf`EHAf)SRrvf@nneIFoSjX zQh@7rktD3rnNPx&%qy34gaYiyxni70GPn!mO}@f&pcSAW17jilnc#5q^St_X4qgp< z%kwMMm0~LbA%#vDgK@WSn56rX$_m1wPi(R7wu)k24{sOdY4P@{$xU6cq+9Gg!=S8l zVfS;iLlNig&y+P!!27@#ybqp57Dd7D1C>-(nvWRvRy2;*+7~hBaAG@M@j}bnO}_6| zxfxN_ZiGIf?*llSB`!2I+|&jD7THtpR84!!;3h6mD#)o zTo(Z?kkqp;coXZvDGD0+6vgaCJEHqQ3XqPS+;Hbj^g}%hkVU)ORg3m$aerU}qwdYX zjt+4uVC*Joq0lkFr2h3ZjE#LQ|DlH%E5|{C2J?87Mz07e=T@)7;r_eE$s^hHoQ(2{ z+^P4nCvkNOP9226JF72TmHjzJJ0^-e^yna)X1J(_mHGt|*5*+PPeA+a#s-@uas-4$ z!xA1eIkL{!Hu&-J6_gbNZW8}D10_h|zy{rjG-o|pvIURB)GWLys&5ZhujA2Nay#Ku z&>b~)%y-rb6+ zO7|Zv#*_iEbEXNbtlAsm)bC5u`OCojnc+PG$GC3#Olf_Hxy3_7b|cl2$M4y0^BmuOZrS35d1{;rlBFS zv??)(?TcOBQ(t1F&_Ebt#dd^Hi3$r=QhPD8$+f$T>N!49V&O^`ke?2u~#(5R3oHsQ0e4RyOb;W9m+K>B_Z#Nd=1J82`_nAmfzqR_FayZ=;Nvx#792S@4nq6^1IKXdvKDD+ zp$h-|uFm#YBJc0jCft<6`*B4W+0gUEzca*2JzL8s!b}TFeft|-E;cpj(?G2dX;J7m z!cgK(uHld%;giulh3lnYiFgGs5uw~bI4VO_`8D+^0r@w$0>JYKr*AILcyC0c-&V`U zQ4GK9yN>nrs%r0*Z&;azF5|5lDzbU?4V-87bj$T8FU<3fQuapZKYbu{GHpFhwPaZ_ zc1P~9Q3)6k=AaQFp`e}=leNwQj0h$d1tmhjxTrWQgB};SuOVjB0k-thN;#K3EuAN3`%YE}! zDhXF)c)sHd%5$UWJ;ThjPqXe957eC;TaP<0aUIFh98mNt_i;gyV*MsKoX~W?uLqp# z{NKyOstDAE2DD<)r?orRK%E-ku;2u%3uu1XYdPIVhy^&F=JFDqJ}(Z##yE*k+|Cg; z5~!%@>!d5((}bbSmh^f5&;^(oM97&W8mVw6t2>bHEm#eB3pv>GU=;3>y!-KYaF$e^;<`(j0}l|5x9zNodw?BSP;2~?fDAS z=n#Kb0(v>LSQ4sR%k>V2v8#xls@_bTzdh4ZQM?w+lCd#`Y1n!{{40!HGtgy zMs*wqi!kuKz?uW}WB?)`U|M62cqk&@$hFfFFz^5J_CtOpp(J|syl@jP4F!cU-?mZBob-fUz`UQvK<43Y?!d+C#+H`s>R+pcbJPz zbFmQpE8=Wt8^W=;6NQTjZrdEf`fL!ak6JXXcKk2Rp?V2^3g-1>7RskWEd3S{6tm}^rSr1I&SoN4uJK&PA`_>=n&2#UJ~bl+e8{dbstc2LOQn3q#;z&$S zD>}T($G)jp1TOwFYbTZA~PeBRy z36MW{o+FG(#RkcvJ2Odx9Y;Vc_R}eucm!%4 z6L4oM@%eb~Kz}sxhvMzO$(pJr3vxjqNPt^7u}Z9pLhAEc00!*2w2E~;K_l6}{8rq{6Q23WG9!C+}n#l)+aD8_9 zG0{)O3uTzDQYctm-oE~B?Km8hZUxXLVE>At8f3u!l`!sxDEU2f!;qkYE(Aco{c7xA z>t(SSiroiAHIG11P12Uj{f?qL%6<D=@A3 zZ|ecfk2Xu434#X2bcK7qsc*`>=pL- zi4R5fiilTN&D`_Jt#_r~-!~EU@q6H!E@!e4!*rDxyaC)M;39a98 zpgOKxPlRyI!K0lJMCJ=Dz_o`e;XaCo2A?GUCjy8rLI6@U6y5@=R+%jWuh+5SXE&zl?hS8CiR9bqy9V3&5H^3qX@0CDw zJaZZX0DYXFnG896zYS@QNn1u9b! zP1kjp2(H4oPOvoTCBfV>;t8mKr|?8T23n6P-T^l+b@Q{XKh)UPrFpjPp@%?-N6b5s zn5!g0>%*3w*$$AGpyc%r`vMDMkkf-s4bxKIf)>azN3cL*fCbWF4_vy_6>SSBx{@0b zk&@_rI4q{RG`nqm(nD~~cczZ4ipcdwHlResM)$t#U>4#RB2R*jvflgDCyFSrKPjd_ z3=Yp;N5eq$U~y25mOCOWz9{33a9#dOSpu|(x!TiXf6Wb7vahK3v_9BjY@>`rsxI(}XNDLlVPCVU^d~Yo+ z>W2kh@y~qiH=n&&i6r3&;|^V}%`>PKQ1vO=NY0U+mEKp9vz}|hs{!|>&V~&J2X5t# z4Ti||AbvC=Ko?L(n4J0dj<|K9Np=C}-|zWnK@KCX7!2I*bR|AI{PtE@=CI+2`ryy-g?qJ| z>a(&CAxW0gkP4T^mpNaxn#h~fJFBly``n=E$QQML+q-HY7Kbxd@J2H3M+<02r4RC8 z-0?m^oEH4sI+{#BAur*j{so!1IQ}2kx{ZZYAGr3&acpSOXsh;UNF}Fga*LjfN;^rz z&`>mX%4=u zqHB12?~VN}@j=d$rfU08&u^w-LQ_hV*gIhpFp|6S(|8^eHvQfLsRob^v8&JD8T2Xe))_cX7_H6j{FclgU6Qyr#pv#P6 zvIisJWd{z4&k$^&fnXi5v%9WSX+UJXEn?7)O*SC}?&;z>(YB#5;i2&OpOk|LHs;ur|_A zRN)Y#NT<-$q@hd$JsZbM^(igNpnB;i0_F7b94&@mPxYYkVZ;cFNN>!B4G0iab6JQ<+58-jzgz#%Az z3k9+5Iebr@4oEZzVsFGUw_7~OcpX;iKTydwJ6cT1N7Qzbwc;oO4&>$9_Ehob`-JG> zW^iR3g@+d2j{R;vZKi}<{-yo=G^Eg{iPbN(SE?^*`GT!oSXGiAu;-FHS z39qRxfGl50h72qw3h5)BiS+HRcs$M{Ox-ZUF$p3&TOaK~in~IES_?>VosMs+JEg_i zZ<2q#8xDUq9tUffOMsf#?B6}dW`+70?!UPFybu}oc2c{_X(oWqwNgeH_jh%|$XlJU zC%?@z0&-RRE;D{}>6l=xvuBy%qYVvdrw!l|cQBc9JOiXf6MUBcep}p*3g4NNWC{k2 z9UJ;s43NkR%!h*1Awz9gJYaDu1tB3+rBZnBG87h&baggUkV1&vn%jC@aWE2`;tiE+ zDFP=f{w*x!kGJ^HqbbCdq|*glME|)V3?p6K?|HQ1BbX$gCU44Thv~8ZbQnVMhf!Nj zp{{H_k>bFALO&C_b`XE0QK@uii5@z(5L?VWB@ubvic%%Du8s^{`TZlp@!%5f(FHLO zb$7F3XA;~1M2&;J?tL`ob`WW}ngf%DVTz#r?24oKWU{a45jl|!kVw_Oq{YP0Jq6@5 z#}TM{h0lCC49o-@Q#a!L<7$&-@wb_$kNk4fef0y3%ja zJ>&7G{O_2?JlzR?4XTN^KkYPK&706?+8R{d$*&Z70k{a!Y&X~7&pmiqIi;giKhg3X! zoGoj#1l`sO@S(-FQ%r%f2r{7CS`3|0bugw-Iho;(Jit{3Y&U+26@r-dXVje>#SX^2 zUg)enI^)~vkS9tuzFMLTZaUgr(;l#?1*ce{86ivGVp4|}!9|+t1x{O~TjO6Qp~TVS zP5ieDeds{~*U9au>(l`@gaV*4FE24{Tu)sAM_0d>koS?%V9v3Gi`+nx^Q0#qoI8q! z5VfYAcpp?Sgvm9M4xB=U0t5S^L8c-LNPxn)3QzSXb0>YajVbFyOe1q9#kV>_XtROS zlk|6q5L=tarivehIR5gux8!E(6SL5?|9(HzrN>(sdJ6au_lWe!GBo22&CO6TPY1u$XW=)FC`Mc#6) z=K=?KFeFJ8+;N^#+&goboTcy0azGweu+VAYV!~XRSsJ-fktYzr_NgFp&Wu<+$c|_1 zC)TP5e$|E-6d}}><9iTd)SN${kaN<+4p0hjlAtUB&0Bx>;&>nBB|G5+|)R{m0Tm;pm$M+=(8#9JatN0mZT=Hws&^g_ z%CG}B2K}DNw|gZujXNC^1kDZOUi^cz;8MEy+S-6dWw*2P9~O^Yly)>#_0SMNE9lyf z96H!L_(yOE6(pJ8#FSX^;&(}$qENwppN0hrxFkQOi}YmlnHcx%-|86S)B)`es|+_C z2o$Km?s&wXo?)A0pd5Z}*#^Ul25)%yR_uRW_;3EaQ^I@F<>R@h<$0Pq z*VRh5aC)e3MnRR(c^nS42ZrQ-PnLE~vY4QwoC?#}Dk*akYopK72bB>=eLk?)@L6VT z>+2wYIvx1a*nm}`+J~1;pBja91Q+QGf1Kvt`qoO)c~{)f%je9{mf)|6YJ66dIYQ?0`=i=y z_Rno^VZ5{%Yh`sV8qhcI`Cx#IRO{DT3brXB7$qQKK{JXi{u%8Lzus3XgsFCkpLuyP z3QTN<==g>_MI;i%%-nQsh1(81u1~_#Dh6z-?>Ix7W1l#S~Q=LK#;G3Z8Gf}xQL0MVKWkB98%@qygcK@^f1}=8SxFD z^jm7yKw_t;cW0ly+v|lETTMxe(L`Gp{0}RVlI9jxz_opl!S0D%!Dy|g{8JjNF{*e=c!l$4# z}R zPokrzcjzkwjL27g;bX!_sWxg}Q#BAPR113qZLr3e~g5~AFD9*}$?VzVqg4kOSP?VuKA;7%f- zj$JdLJ;6l-y55hvkQLXF`9I<@p#}7)2LY7lfF=A1pgdzEM1b?OycFK00^$xa^?m_; z@_<@+ny4&CJH|MrIDRV5{tdSDMJ{O7aCq#d!70}@+?X{Hb!>8wi$uT?yd~;ySxW7J z9vDBe1^vzTCHI8MC0%`-+DkV>ZF-MocXmF}wzM93d#YF=)sfIzS+$u)t0kF1sh93M zQpZoPT_22^0vRg4rskiU02TX~TYFG~dXf>8_n1t73Qt=$21nQd@dl)#3xzu%b_CSu z6Lje^Af0?wGCUfvEdXW+5$RF@UlYK#2#iErE)VmEn<)-Ot8jSZ*cv&)ydA31QM|rVmEpI{Q0E97iTNKA1(*^{ zN^PW_%6t1l4$r*(A@|tA_N2TTg?&mY1cPqt#V7r8+ffLK6h{gjsm;%3VZCmHIu{O} z4rT?{+#|$WG~21hc_$O))SCfc>Luzd#pi+xON6i)THT_dQ^nz$QLByrKaZNIG(`BC zX`aZ#I1QM2ydn!A*mD#Eyr4knG-X3=BQh3vR7Aj|GT8U1%x*k`J*tysTGNn zS5(F7aZ!T>E5ik&Gg%Xx>R=gtIDV_8cwFbnhS9Y?6>W3FEN(pKJo&Cor>dps-Jg5C z_WCW0rV={F*c%UnJFepiT4Wc!)aPeU(X`o>9%qI>B)SeJ8}WV=Q`?u1v)0|6z83X9 zjLl51(GihNjPcN02N>EyDiuA;c+ib_sqQLzx zcphvcqN4n+jIvL~ncHt8uQVdO7;U2GHIN9*tj?V|^jr?%-q`2ux5X zj-VJgAYhq80#+;}VBrfwS@rwRfw$4L54}t$YPX}P*t*1b8sPr;YSI#>1d4Lf*)p^N zn=EA!zMOUCa`N@Thee4Co}GR1-h~L zWCVoEYwzAcG*`Rg^u*J{wFRcL!BeZeLsLkJ_v`jM0gleP`1P$*p`6m3f_ad0(jAIa&L!U3Hv_fF!GA?z4aONaXIN}zgd$d zxemrmcXi8DU`*4l8(=)#U}@eFkrN}cY@Tnr99BFaI70h3<#KV)ffr#9U&4WZ{5ly} zkbuw@u7j+ch*LpSb(Q(bEgkmhluYS0rM<6M&sf9>!{m0A+B>*k>~wBd%iGLIS-6adseU!E{bpms zZlzTtd7L4?l+VzhyPR;%~7PHwUs_3BM20+S`( zXUM!1m5zF&FUmjU89xSnFr4;eVgC~TVnF!Y+&E539hz^sBuYX-be|-tqdF2t>{h44 z*&LL4X}keWzLrYN8EBckEjsKMZL`Bmoe3*xgu-wC+&>`yGKzg#cxq=%3vL$;REpK1 znOK-ifxa1`bQ6K6si7ul$A5a++PlL}AV&oYRlU&JL9|d6w;{faiLhhh=85G%UA7;9 z5*Mq7j|4hC363y>cB;YMo4g3+c4kx~70rGll~22nQ;Q{#se}Rc<_%P;Grne_ zT`HMWP@cWpm_c{B`JqJ2QzdM+5eoqoLs={#cEOS0ittp?)L8GNU z#3>yPvXu}at&;;Am$Xjlr*yi`#hD)G`v`Rznfx_WHWyS${)+y;whP*-P(z$P$zL=+ zA|7juY)+U^uLXNMs`1^bbF4ovn^^x0pZ5&kks9e>aMccdz9AyfCwel>u~QU8X#mxx z$6Wug4i3U6fhv<24_9!%f#3R0BapBkm*0Ce0HF-4WawinN9s#ZDumKrKGrjlg(9?z z9s99RF#;S|M?Vyxri^aOke7>sQsc(&ikN?S8s0I4zJTgmtA|d(t{n9CVzoeNSyc#_H=9{x79v++g#YIBlxM(b%Kc(osVZ}f=`F4A4ePr-^?({AbqT!*6 z)J0aEOz7nxn17S0b;)c0xk#PvXKI-gE|i$;k>ubMP!zm&nzSr*zunAJ}s?9 z`5_bserL@WcNg>9}rFvz05D~)6LN=PRAdpRJOIi)D{xbeAx7V z?i{XD`vCp)v>7XiS+9X>vyRH|R^00%vUM-?&_kYfO|cj>H0b0W=_);vvIId2F_GtQ z*%#uF650cnkc64EmGK?KEEA7hnFaV;D3+b%(8{X-dth{flpO9y-Y=f(z-a0+(e|sCK zO3D$AhdgXemr+M}!a(Q}ZFyh-+UwZ zcEmq=FQIwEMLvv@qMT)C?M&P4@0HuRD}&=1KQ}GhlZr-nSP_zC1N39*zW6tJHAXEd zz|tY6zsQqWcG8(3_|dC_!nf%eeVRMhhoV;?yTzG_~yS(@|A;Ad{Kwz&)&;HGgyt(2c5 ztNO8ig5%)#`7+m+MIc$?`d#BHYDlvRjRIxV{hTmEAC*?#`9=9>yYNdmi_7y_cG?RL zVIGW^0D{m#hE;*vmwr`LKhCY_FwvLNT*r{8x$A5`$sqL^G*QoS0tkX9C3dIOJIV<e_PcL191*?h_?loOdkw#gNTBQN!@)LYC zQ+0R_jq-GS!(jkMX2nBbSvuqc*!Pbb~SCn>r{+D?Ntn%cZN5IE2Wv(pE2%R4Ib%?$E8;f{2e4_`>Rh5YzdYuRC zTj@eL%)YgB(j5TOP?+Zbo*`d8*Dc~?h+-=!Y#xDfRdwE5*rbj^0C_5L$2$W$7eI0X zdpd$5Q8MasTHetq@}sMgnDODOKsL7bl-wQgt!R|zo$uKa(<~ejU1bW=3tt!2L3XaG zxNq!vWG-E^oE;tp5A|}m?#RoZ>gTlHSg{dOVCsd&WB)>F(Pdn>iF#3r)i!3*lhIfF$ZHbK25v@(&%03o{fe$(%0TG ze$%;g7I>OD@fP26;%g-2zKJoJ8-G@D)M5bWlwvndGuYw2(N1NkH;M4=goL{ zG2{lWc#qmIgvOEb+|bn+orOOF&t37)o(a$fs@7H^4p|IuZ=yd-qWX0W$zMV5=896ThRJ6dPb-N>4&CSr@uK^JXt79 zg~H?gp^Dq1`@<+H`dSdKI1;+t2W4m2;q1(5?77cN*{J6LjXw_gZCue5Uatgw3XAdp zbiC@sFyC}kd;~59=t}BvUj)bb^0>#U9!i1(xhwFwn53)VxX6_yom1=@qS#PaCOq?OP7mU4^AHm*%kIC?1@+&HFZy;`UJTKy}niU_?Yw}~AXgLABIJ96ML0$AgY1345-vD9)Bez0y}I3d8TD)!pBmr=y@k*jq0c~QG9v~@DNMhE zViKI73$puEe8f_jAXH+XZT9as0jS`C5;L;M3Ib$UZxg^@?y9rNB+*LCfuMD$C zmyXjKiD4PgbJ`c&V+|NAZ&s{a2@;Pf;-&pLSogN}~@G{J&+C4Rmo@7`obSYV(p z2lsIu#Iovyh>;UoN!2he$BLaO#R7KSDcV}5I(0~J%sX`}fUDPmk(*#(sB#oAD%=Y} zmLJNbiL4lVNb$iPKw#QnWV6*Nb3gVjJbVsL-}MBiUQeP8ZD(<(g6wg0JL2Q@pWv1s zgu~P1M6@y=WnR!_$V-i8l5pd7Fh{!!$jO|9HUaI0XwbMd=mPq9LQNU>`*?2i_CX{> zsp!#Kj&xK!CjJ&AKLc^GxE#^AMKA`s)Py{e?}(f-`NW?E20~{#$fx47tU?J)=f8_V zWw_scG%YoGdeDv6JVK5!a%dtNZB>DMj02y1&oK7EcL91Dpt*3*V|ACiuaf)iLd`>V z3%jtX9E21Op0D?D(n4|`DT%ATVMe;45&m!F>Nz9Amq7od=JT;?WxDWnMiGT|3~`p6 zp+G)`7h^0^lnn;tA4^YYWQIwTkXjs*J}&3zLnGfbtBA>Xr2Dt zcKeFXQn;VtZAXl3{M9oCx?HLCmuf9H#y##h@x^@|vrMmi&!&Am*f`}k#ujy>&$TH^ zgqMq+UGs`2y$}YHx}zKi2q4H*L7_ScMNDlbQ=)`y^D7_DcD5J}js4*|w<{uOB2Qou zsvmq)*@BkqlJLOD3ETu@qXN!SZTrG%&%}iQ`%cNn8dvNQuUInmn3zwH+en;fU}^TL zd)F;}ZuM#gPO-1X3Duzv4CNsg=H#7nLc1aS2ZYYzJzUl=NH0B-meJ9nDc&hhr$kie z`{86r7rmPBP9%12^<$4j+?;0WR(VX9V<(pUVc>)3$6sc&cPzwpYwFm~y!%D7hFL`~ z;HfH2`?&s<(l_)*lkqb$TKC{rIq@n z5Y{w}b3hd42Zxgp&UEe+l53d`9?zc8$Px{#)@W%eL>9_tSM<lx6 zDPAs9DNJOLrgfRj$>(NR_IF|OuM-cRui?#5V5RZnW}dcxW-k3~RBfinpT3m3&$C`F zE9=~yi`KN~E_CyzG?_mSHy#F!01uE~*Vp!LYRI3%`Ty8^&!8x`?cKMVC|Pn2q9g$U zr4J04w0DOMYB-O1;?q8&>D!y28p*M66#KwnkB8djDQNb{C zSVy-2yib{5Bx^v2HS-mmvYK}76WrcjFaCHa^F17X+qapi*qj`pPxHSm4@Pka< z7O$0xcviNK_N-2~NJ#c-Fo3G3DeJ_;Qf)J0!Do)rjly|39ZDqyVe)-{gI6;Wl^LJNCE zHkJ1|(x4$r@ieVa-sFfmx$|u?REtiz`*@wLWOA^s2F1w)Et+; zE_H`K)2T1^#}4ANGWP+K4|{2hu-WwdXb8h0yudqt zF-4aMd zQ=ltQY|3E%c8UFRtN#Zd5u#&x1oxI)F9}88qWb!j<*FvP;&x^|G4(3D<*>4awu*6 z^BT_NFHPd9=zcUm3;J7ajZvJKN>EIP7i|$7A16$!P$Hn!iz|0E^;2;=h zzUzw3AJkluCSk9N?NLHH@>mt6r)$%1;;k0z&OD|4WYF5f-hstFtiB_r)N#s2=~6|9 z!iK1kMhwF{iuOtPApwMl!1%?{d}S#srt)mAHPZ{ET-RbhtTbaeR!b!ty*F=1NStLa zi5GtQIrsyAqM*m)=?Q-_yOMw_l!Vk}Mc-Xa;KIJwOqncf-&75$x)1j$5UfAT&E?Z> zd|I2ZgNs~zx2$$%3Q=R%dx>(qI;7b_cH4_&M!z=v)XKRLT$r)U-uWAxD`1$!_IRjU zI(n!2!<5|XLZ^qiTXITy1CdKoMPDUkrjAH_oYlalG*_#}t`1T(KaKC#*1nuZTA8qE z!t|<{&2jiCYiEFBTq0gtPEEMB(vib*@dY0r^TcKOyfF$1^N1E+8*{Bp_Z4)sJswG34}-`O7ops$|~sN01x0PLopGzZX8p<9x9AV880K zc}o!zH<71byviC8o*ot73mYpMYq0Zg%JpXT^KL)?o)GpTZ8&b&fLXV28_R8`j7dxf zR0ixPR-3AD*_y%GUU&}CQhUs5f|^CLppc)2d17Uh4{5~c$cfCQ>Z7#72vKsvJL)NP zmbXOkN)BSR1Qd}K=lF|Tg!m8?U$d&`dE2~EM-bKTH;*tH@^yd1m~5QJ0q2iC9@^Q= zr+w`LctNrijmxlnX(*O70+$;exx@XOh@Y9tL6@Lq*j~p`ue~R=AMnC zB1GkWju8lveoMH1C#P4qlOtNa@kU@_{a1H2Ue*V*g(`oJrBsQ&7A?HAmDDas{?e^qAglktlzRp<9J>a5(2(P`tbK!$JbivFI+|SJM zoMV`g9U`d?YX5kR$7pFuypnY40&ijjJhsJ0gz=cVBZFVc#xZn#(0%H9DCTHn`5|k~ z<1%v9H$2|^8Gp{le{__Iax@#p!=wtkAVOiQ+Db$!Gl&feOL=>-dSJ)1750&sD;T+HR^9%PuvY zH)#?YZYY~Zde)`j^%97;_OD^VCg}uUFZ<&AF}(8RSM>_}u+I>1o;9Bh^fT z`V^0-zdacojycfFR!jIOxmQHT%CVe-*HOgbo_KcA)8wmx*q;(oF0azvY`XL&ie4_P z=-ZN19Nt^ShN-qff7T7qksijS%gy~xPY~Y$&j~A9UCO+^tuyu%LET!LMN{I>@N}W9 zO8{2))%|>+X2b%$i?qN3!kFn?uj;lwA9xtv+SL)kKC^5W!^<}Ee5Dt3WbQi){HO+R zmi?{b2idc=Y|pKy^)IG;72s`M^eWQD#?FblZj|uZ@!93DL~#kSSwDj^tW(Esnn)&- zByd{>icK3>ypTPv(RBF(g*^4S!Kk+iBIJ46N2+a*LQZ{w7pNl%9JtdurpeWU55Hau z+CmChfmMUT?9E@f+a>6~ML((+hJk93H>&*9Y(PZYG}llXT@4!66{ow~fLtRyoJub6 zseXJF7a8VXNRD7GJV(Lh7w?owGyd4pXuXL&T=34&K#wya`c1fYVFUkQ_?TGo{TI9q z*4e0#ouyj6M?F@Ch*SOLA8yOw=PZWkmU$DX@g<7L6GT)!=JefeICOk)jZ;bgyksbz z#Jt3vh1ZK-5^B=0`WT$0NTug++Do*~-t(47fxbgtE&{NCk`!#tsXFj4Bh_EQKa~UG zpC)A{G}g6^=xT4dUu%t_14oRtcf>EKNyS>4hNEf*9OFqyy;MC4XDN8fd;#!0M|Nf!Nq@7%c+f4Cf$nntmM5g$BzOJ0mVWdUJrG)$an z_wJ5;#fOR;H-}fm^RYeN5)PIhiQKk7sh5#SGLz|L7UpJsnLTgx2GND56e_Y0 zspUPfldwP26!AVroDO9lNS^ADf4BREzoYXOOgh9J}K7Ac{9^GbEalZ%i-1F zoIoQJ-X^#S6A3W)1?*Qpr=>bo0L8C3!HzMCxMdD$sA?Y4}-td(}Ut2G9A?U6lKje^s-wrUiZ0`MZsg;BJ6{ zvI`y?BXfD*-N+$+150|L(Gv@r#WW`+rIQ>N6+0hGCD(0g&1WiRB02oU4<8PrGWy% zHy^oA+=WU)2-@SlIrT-Md~i;$N!H$jMIZlh5MzSuN~1r_JgPzSMS|KJyfROmC5E?V z?w3T9ZyX6faZKyIf=IO5(F4->D_(ZO_^1!evBNzWAedI76&S`=%>hip;YscZgQsuYXLITZi4X#UE>TFrfZx?Q1cj@Wi|iE9SN___m{|)P&zY>>t}Uz z-z0gw+{>t*UC%HQC43(X>JH9aEnujfjbzZv2FkjKD9axwHzrCq4 zu?fb9gN;TZ#NfmmEKdzf&ju8BNgS%9N1pHkKR$0C3Dr4j&jPmmb-r`5YI%sP{y%u)Lu@^27K^@3MzuB%?ULs134bskn#ys3PU#@WXZU zZfbwQN53L|SX83Uew5#a4h~>{_KGf)T4-T-c1K@*o`xE93h}8ff)35+RS|i=RSDHR zOYs=aG6mZ7oJj|F8hABmBy^Fg*3LxlO)`OZyfZK|s`^fEbMB1jA*d+^j%$ zqv!Vb?eA)AD&r%nd>$&h|+uVNNhee7F$J%fb11vEl#UBxzXn*t)P2s z)f7#vz423LP~&FURLR;@zqjm>d_b0m!2MkIM?au5RGwKo@vF`-{GAkrNJQzUtl;I) zS;!+5Ghd}Y5em;L;DibQft8Wsw5?!N2ybjy0%k$?SiNm52LEni3Q#%r`Xr~xfeDWPG zzo(wKrcTb7n~tI(UI?y&9f(h2r|P?up6sU35z?Y)6?{xuwQFpN56j~^qTQP)*QMzG zr?lPL02c1rCq^e4^n(`#@1)+D17#aLsok2As|(fv;Jx_Zl*;iu!{g>~p;;TXx^%y* z)|eL^Rh8XVJAt5c#@@yFAxP3l`LgIw4$%>vieyrgEFu{n;p5{_7|7H%oF!4nqjI~f znnA7jFjY7)Jp(9`cG4%jXWQPwFF$eA1oxlI?>QDs2L!-kyzOx&b$EgjcC4UFpD$v@?-W;%4co19wzXLc03`6r>sB=5~8?SmT4~(=|)tK(IfHq^vLM%x6}j} ziPE-D;5ww0(f*=J=Z6ek7OWZruUmuPQovHZhGtO1CEhqn)x7tMZt?})a#Pdp-*j=H z2D0Aq6PDFS>&a7417Z87x)6KoLN~83oQ>od<2(Cxw%(Z>FKN~7-r6DpmV)Rr%Wk;u zsHZ71`g^N@LVclcBMhigfdQxpUr~5S3W~lMj}v-E3^KCnKm##8p^NkJLFcv*%{3-b z`wwL1SK{M>l->31)D*JnK6p?R=xF?t{zQ;QcrIg(VBO!i7yOK*CHH6N@P)Rz+n2+0 z;KL&aRjBPABYK)k*k!hD{3r^0n{?dV;ZcU04gr*K`k!Lc5l}p}8Qg}(H zLwP79d0q{kpbXs&zJ{`iR}$}e>KaS3Hm$HfOR(@=a(Qt){G58NZ%NnIge2V%fjKkI zK@o~Uq9-dPX4hmVw`SW{D<3_1IA{KnCloHDatc#6D2zUzXxl3rw@7L9XJTS5Va5>A zJM+OwP}RUyJfeC__$*VP&H;au`hL7YN~~nKFw}YWDc^nJx{XmO^{wlwT4cz^5j6Lt z+qvalGP&ANlvcfEt7gJeA??!GojixHy1@^Y>h+i$p|F-8F=ndx*gyk`k#+mT$W0(d zPVH8jKUKJybUK6mSRu)W9fMR_7@R~!zJSc?*c2EOZQP^@)b1RU_J*Dc z6B3tV;8a{fyK?PkD3beI$qa4f;MwRVg4`Cj zKtjj6I2rUb(Q=84fd^tyv6-5F%_UbSA5j;UWghyH%b8YY)K#w+SX2LKV?weR>36u^ zTTtIGp439u^EHwO$)(IA&66`K9~6!4dR zH>@P4agoT`0IOr}49OI4!;*+RfI!5UqK2iF6|QEOB8LRh0y7_qKdlf&-$Jvh`8~*B z;reU%j>1&OGM1m18v1!SXPwz4ZUA?cs|=b#AQqAX{Y?uBxLLngxqbSUKw&iHo(i`% z8U=Ib6o=l7_D!7Fg#G?R7ORU|Lmda=zq^i=z62SGYjJtP3jI2&0#ss}XaEAp7{ z-tpd@6{ot)@$PtPuat9bK#%Z48NHO>|4f){Shee(ym8a%jid7I==1p;5e4oeu0))- zQfxfjhZ`^F8@&sXmb+K9O+B?!jj08Oc@EZDL0e#gcySGHUw@}qI)lf{^*QcZ#y2<33H4>9f~!O%mT+~ko`Xe3ElZNqce`5ZIENR zy}fs!PLlfW3|AmVpHAo*xeppiKbG%aeC?-|z|Kb|ogmFj|CHMNBGjuCz-+<&o{1 z-nF(D<>`H$sS1G^83wRAy$;uQ27VLCuk-ddS9>m+%=p3p67Xbe*qPM*Ju&FQhT08j8-OyNv9w2o4+5fLuF2CV~mPlb*snL!=Y!y z6@_f1(=*yr$t>X=sr>Z#m+lD{p)^^_%QU2S_>Ue9e+w*5(H(nsje_%gA~pNv zCr;cM9n&e5s(;82pAVaN?#d5|#rFAHGTWwTg}QX>xWd8~7Ls8R{76t$qv?)g1a`PR z9uHpSF8UDY;~1Iir-)XnX+{Pdt5T<_hDXYamj|;~NZFtl<0_4^daYf$9r#sIVsGQN z?ACfssBY9Rwz#b>Tp{7=amg{@EOxb*DYz?nm&O;RD|*#WCT_7Vw;Z(Xsa@PA>J6Dz z@smZyBC;Vf?Mb-VG;i9Wi=v$6nfAGghD*AhqK)#dYvAJpEdhlc+r(2Z#Da?Eagl<3 zPIWlURR#hu3*rCN_v2-mRf3K{rG7P`kD0SJyi^B@{9((XMxOm&nszpURMMRk6e&MqqKW9!K*J z|JKFOmDx1-aWOhwNnwC~*ex6wYpeIR<_iu573^-!PjcvnqP_2!ti}uSQ>JDAQptR^ z6=fooui35k&GoA$$vVv;PW;|R8^Ao5!x@J+^dc4uRT@DuD0p0)kP04GlCzSl5BqD z8{)2#V#r@v`k9_7cYVFb^6T|fQS@UnSjV>^B>gH^4~tW!&QgkHcKMg^iL!L*O1Xj65GA|z%~|JBIBiE@Qau~H172sjmCQSy8Tkc zZsE|tzTA*H5uzyX^OmGnHN-64Yca^WVh@an2abk_KPqofKH+?QSti?wT|JZJVM5UB zSbc2CMIe`-B@|SP{_drQ-~RdF@}wf@H#j>!teQl8=+k2RaXX-oN-qV!?8jaBg@EYs zT&{-YR(p}S_p0XoC+?=a#O)nEw3H2){qYjk&Je}8QYpw2F=nUdd2BN$edGd@L;6eY zhMVWh*e&glPIZH$E^<%JCe>u`^+*ilJk`GP@`QB=(*w)n*3bWv0qpuov|iiuxKF5dV-Cx@|_4D!~)sE}?>x5f>yr z635si-6V2sYU5p(pI9H8mmH`D{kK<{1f{sT%kW-po)%_G#!q%c^D)5PjDN;}=3{b! zu+SwfZD3`8tSSHiR@Sa<{rds(q zM1q&lUn&K-9;^Dm^D67ZaW6`DuyGI=wr)K=ZR7zAF%~ou_4BQ$ZblKO$F4XR>C4=2 zeV(2GYz=-~r5X1X^DE5VkFj9r~s3fgiT{AoZ0C0v@R8%Tkq8E?OD$CD%rgO4Q7c z;S79vn*Hu{;w#V*B$xXfQ0je{XD<;W0$-&g<8I^VL1uRQhaVgbJ;=FWjl~)d>G2 z>8m2@MyODJ#T0zzt&_n$T|_0^S2Pgq%l$%3?2TUtbWAJ^(bMObQ-{6Rf;!y!(aOZ> zWJl~{kN0kd;EJLjSeyr`)@ibHpR7P1h~-Z5f%NlZV_Zndd}ZwZND3ejl}EIB7tIgt z5)dErmEpf>nd25taES#?(P_4-JZ7F7rBUXlvb&a8#s*IuT9sb+C$rV1=cz-=?NpLY z@!!KPjjod~HApGlROf1R`jA98{z)VJ9g^e&s&|qW_GWlVJa@s$t1GvRv(=L%!X(@i zZYVP(kKc2;3Km6Ld9J$J?XWHJP86DpF-7Wot}`s^cLCyI}%42&<&UPiO%U(@EC=G%3=P|0C`&d zRucFvo%GAO)oztEU&jiAPmYWnd|UIoE&)P~5dpRUqZAZ1gxTA^5r3=o&#fl9nV6$* zh_tqMML!?+YPawkKa41m*dN&#^xssr^D!jL_Qe%tx@q=i{89G&k?3eAQhc*(>qK(7 zrq{_FU#6?#oAn96w@w_)NpIPELD(B@RU3MxJw%149Ex@(fGJq`0_}v$0yQ%{AE=qA zKp0p0q{Mt5WscVK=Vohx*AP%+flR*|2e``9CkZZ9C`TKRCLX)_mT*h=e5)c!C(-(a zQ`VYM4ETl9H$g*DJa)1|*|1>74SF=*ZynixCrHkZ(8=Z%(+8;?A4LMA6eDp1qjY^1 z^pBo07Ki;~Evm&WQ4dr?CZ8fW;!bN2flIM|;vilpU6#Lz1hrd0egH2(6SFNtjd0bbyFT=0_U<9}>4B(MHV3#(ZSQM$4iN}NXNv3>}9 z22giX)yMcPOwkl3gsC=U^|wu zK+XJvPDb1hNyZCC$9*fB{pCdD7!s%CltUo}15B}dQYR#5xk>gM0ub33ucvoWuEiF8 zHvJ#KbE68L8_>S2MDDLIV0Q!E5X-pzxUPkx+T}CB%CU;!f}>20{&(;BmWWihd$;o7 zt4a}^y7grO7x~T0j2=IkW;~DTm7xNDUskiA%b{&1!rnpaqu#EF$ubdg!uazkt)-0i z(JoT`pM4GvQ!TQ6DuxO%wQtQdL z@I;L=X7FD&dOqDIlKW`gS69a~&ipQ`0%MT6v*0tgQe4xccuj_nl`KW5N^K|&?gjX@ zAZx9A6Te-Itc)ADzg81q{ZS&RBQviF?9;ZMeM=$UDlc=}#j`ngSzI)07b%ywr5lo9 zh>Ff*d?A#UfGBP$4Q-nx@}t7mKWufB#|f4%B`JDNtB&J%v9mF-%uX}UameSuB1{xf ziuWdHeV|r^S65ihM^a9&uQr1gh5(JJ)4e|HTaTXMi!!;>BqbLZ@E43^>C-txj`%sQ za?w2aSl!AKdWM-!7j*ec6(cK7GJFb&=O0n!*T$XPQ#_?#zq32)eGPG}Kj{!Q`byA( zpCzPmw*7Kd8saW~SZ4x=t%+0V7n6xpghcq{(H6V*HO*c8g3v4eoP#`$Rh_5cL}qin zMrht5gZC@Eb8{9yf1st5v~yn1IE8@-!kHcV(*%z`nPr)ci6I zY`d`55qa4in1egjobP$Q>yrR-{iIrB2THRRM=c`lercR~2ZteX@I&e)USOQeqF?ka z-wp=DVY6;%g~!SQRWS$jP4D%K@_8azp~Fp`?QbH{8J%sLQ7&VdXP%DMal+Dt+OLc?7;gOu&zZ}cp3fY!F1)*xr^6tYRI z`EE=eJ&ih?!?fXSWVTI#O!ap}-j2;nT3w$-OQT$A-k^#wtQ7a(TB&%e(+@vkS_dZE z!x7{yY&6otI^AU@2?W(Tfr~F{V6YuutTeo3+;H(}lAl*5{;6fvDKzC@<`ddO4%qG& z7ewMVR_S)7+j zSig10--*T|RHI7&ZiDj|yZvyPA4zYwq)e`VZO$W3#AqVUIL%wVr59?smk!k~#U(XY z964@9=g^HP7bmlN%gwryG?7B!;a)^OCX(S(gIBgG54}Xx)s}{^T!3ud>HTp}E>5o_ z5sKHyH6K2G;1z|qV(KSotn{W0gKU|zc{dgUNDuS)ICT5-jWjEj)2g`3{BciyQ2yN1 za(q+kLl)U~5*1=b*4ozyqZ+>A{7P+6If;#K3Vj{xGiJn$y4Lf@2q?{gPb~sPKRm7i zx(JRD$5?(fPgKzJ%`=Svws66cB2+A9y20F%!ut0mEo`+3$Io^d)xhXHT4Lm*8xt4r zD$gRC-#*7;J(rRhgk5%UaFdr)(l@R{H-RIJ7OU+rnxEkLZz56z*>^J*XK;(&)~tip+Nx%zAH^aH-Zm}(0JMUZ4-}6 z9sUV+4U>4l50=jbLs*yP9=g*>%Phj|&oF*b?@lZOzM?YeF2jiic@s#CM{Oi?Q${I7 zVd@lG0u^+gY9S_$U%0F?s5dbUY#L0ClzAAZ=2Xy{pQvG`C5mvWq}lfB#kl8kayDrp z+nzk%a;sqne!+Cmg#;k{j$Js_RsA;{nA9a#uRHWhp%yR7L1RriNO~xA;Xl-M+pTf$0F6G^vn(U zK>DKtRBe#o{x1pzl=VQnCqJ+);Hwt4YDv;3uW^ggfM3v(;Sw0dMaowt(!$718r(BE zo!$$VSK;Ui)|K_F`e-1~qSrgKq03lpycK61>sbP~48qD+Ji)|i1#A*?l|e4Lp|#C* zKx(+hP-L9yRCtfdq;)xIy>y03p)H1he3Fi#XW3AY{rGmxc$`cs>8*Ug#8lWN*O1-7 zRv}u_Nve$;$^Dj~)yF4T7G*$6;_=Sq0DpgA$fwIHqT{l@on!?cvk7 zuU5?D^zxyUy4yV?=vv5EHGOHXGdq*{UU2>nP#ue*i3Y~w!6UE9=+z$m*PI`LM0lFy z?LutbhKRdrwaHT?Bl6|m4VlU_LnfgbM{$SxVl7_8%y5yI<>u1LW7|nTaeNc|2>Yqo z#9!cE2;4%~4Q=e*y78+-w#DHxHC{ps+js)6s?wymT?4z})iSFqBhiel;P{K2PLk#T zz<-+)3v))V^DFWVg@dg_Q-01kpJur>-gLBbwHtH-O>nnr#~8bmy);D~3`* z7#E%@tu7rKWB};WB#b)$z%hIa)dCZi!3+iHi$%T|&0p(4iF0>#o+0QoUrB5n!nT4; zwxN;`G>psZ`Rq05KC>pV-||)L0{(qL(eQ>FLh@XV%hhHi#L50EB+n_1T?e*mx;Z(> zth%r%mJ3ntr*S@de)%Vtz4&2+(4oIQ4-nK_!%8W64N(k_c%Y$q@gyf76YpC!$-9`+ zV5ktPVlYhPx89CWDPYB-EWkvn$P4*fHt&-9LssgOj;d$a)}JXa zH4oJ1Pi}yfy;IDJoN}Zl4dy2c`qwYIi1aq(IhlBAQVcensJ8RZboYdo6#hnZf&DuO8HNY?Pf3MI7&{PJF}e_JB8^V+?DdIB*uBJ}7;yVvobh^s#>`h2R<2ye z7kfgd;MX_RFf}5<1%Bid?;Qr+<|L_YgBLzUQ@BVVOMH8Ea}#IaepUK0dol$l1`Ery z%1q9Hq~~L1`a`rDw#hdGE|589BI|pjF49 z3F(?@X`jz%PM>%rRP?Jrv3sD4!8Nc05GwIZ=66kdDEs_xX4sb1z2iUJFbrJthie)f z?lA63bUDuEh0kzrUA(;loRCSZjvzh)w>x3s0vANploxynb9|P21N4EOL?NgxEWe=< z-6J{ZMz6BJ@I9KPdhWB9?c)QtZz*IOxG?_%#Q!k?kkg|_NBZ-hig`yIL41?a3 zZvPwEoSM%tKv)Vz+;9*NTWD#zeVJsR^9c-b1x)%v#8;l#!|P)ULFsF>6ty1j4o|B- zGED`_@pR3=u$UqtMspI%{q(5&gd4!(Ls-UU3hEl8xg2t@!cn3+6e9R12(?b=iv~V4 z>nJ}JDwQpOxyHx%_Hbh7WBUH%t9^*>^@sAipVhjG6R zcUv}qMDeJ<;Vj#@w97=rDW~(HbDF*3nf|BJbw#J^9}>0%iBNTaUO9ltWBq#Ob+^T? zbaG;FYRJ!QRVxbO%z^m(8V3Pb`AN%^{Y6?9FJ6TYGOQl__4K<@M^`9|rg33siY&c9 zqrMus@h;kr6Nsd9si?2Lrw~ElA)c9Nbv~r$Nf_TgR5~Yn5Yx(uuq$jxG}Hs}@oXP} z<_5V`KCw2zfos9nVb}(kxhJef5$Vv(2YCFVywPP&oBUZn`r}47$8Rcm3PEo(&{nElfy((u-l?Hp z(9F-jqn3UCQokeSa#P&HIc`?df0v(~Eb|xtEvl=2GWchU?gq+0hJb z3_6Ap3&k+lu@$*!fP&cb+5nBgWNlt+tijqX2GDgN`YiCheo-v^9($cxI=_4Cxub~B)RyZ(hR zLw^F`vA^YR7@}YA^=Z|zivA#XBai9Fo$SzY{oK@kyd`@YGkN)Uas8`!Sz3fu1?S#V zz0{Dj6#PAHhdy@~5XV(haPmt!)MAk|s8NKwl}q{AMtbbv)4)+xbeneE3bbhu3i%B@ zQQl<<=^z}T_s$*k+Pwbbck~Wh(kPD%q#M2_@?#i2zvxbhqW1urA9Rv_J$3ElFAs#A zI=ikWB4UGNCM7l63A$@gx4OQ~q=x5w7W&GUPHt~{1NiZbZQ|WkTv%F=e{7s!w3+RW zLdb(rLoMAWbr^%;N-F*I^22{R#Z*Js_#O>CXwTDD^mJV`K-kZ$q<3W;V><@Q0J7;d zUKTJBoF47PDgy*q2iGVSo{mmjL1$~R@ekVXL@@RY{eIz+SYkedO@Lgkeu3~ zgyDZYxo03rHxo3ZY4s%-N=p(w53smc0oeE72Jy3|&QO33s4+4EuQi4N{jTvV{l4$( zokP3MqqY(+v(@6!qxhe^(tO@#FZA`^Wp%uj-NyefKiy^8x4?3i=&am*v84@QrZ-qA z=3qR{}nE(5R#|||(=D8um{H_Mp?y)dd*UUL~)hyK5#w3#ZDQ zdCd=$3YKO*twKqh&w8!lk~6;s8N!HBvB1;OpfnlTdf`0v)#dh+YtEN7zc4Eq7{W5? zrWn*Um^-S^#lPgV!K0y zw`D|nAGb2z`(`M(dI^QIe<*cef;M}y)=f5UW$#Eqy#jHavz5J_ zhDvBJXIES?Kf6&?26f!XT$~YRNQFfMMiJFY5MX523I14cyn0xisPjp)632V&+f0Hy zQeZ6xn30chfX6Rj@5TML1SoY7e_mNC-UlS3pzR30MQ52T=Hvp0ZX*;gXUTRjtCQ0{ zf+7oDpBi`7lJ=791Hon5++1K!g4+>gtqTIs=nMrHau_~6@k{BOG7vzJ0I{UNr6|o) zQ?eFTZgMegZFDc3aBNRRy>V-PH}g2fafxsch^Pi&gp!!q&8$4z-YvZU`2jz9>iM^i zwsG4OL!pa+4+iwvWBH69(cozjSf#j(NxhFBCx8NvxU1T}KJK3V8n`UP^&py+s!8%cEZn6}R z{?Tt;mZtMafJjR>-B6ZY?L`rhmFc?H8ZgMfXD0=+fdE3Ac7#eD1!WIVD4AF*B9#W1%Y}I?*FO z$B_gV;}*d$jD4Qi_e_QWmaGjV!>(oFNYPv|Hk`W(aW}NCEFWHNiWQrkNxz!U{K})y zm$w>5N9yu|d)l<5i-fR<;8S0L{;cU(Hf|C4rf%~~#f3dQ`(J!*|3@9w?}@GQRARLT zYL2P{tXb~sKc7F)FVN|nNbNq}M?8b#DfHE>fejDgo%~ws=YhKY{WeyDp7X>~5&V`= zY1<~Qg?hxkisu;|2)PDmK%vGEcm~;4@rAXCG>|SxG(7>}IBx*QApji5kAdTaG(dE;%77~r{J4;3EC6~>7MJ;m68l{g`bp*yo0lIf(c2q7cT#t9=XTR#kzDb<@-Mafx^?cw_=rt<= z6Hol0ge1|w*HcWjqw=zU%{XbxEb!B%)Q0X_wecp(7*YS1#Suji9;9~ywo3Ly8U z0@k6xB??|w8(1-bq|>EY11Z$5e21KS0j7A~jG)3fJ*!>&{-=1?tyoG`4)W1e;^AqY zy}OF+wapQ}S1~cgJa#$Mlx^q9?Pm8)@EHat(-K8zpHqyd&-pJqIqng;Z`>X2MV`sK>dDeNZe3l{dQg0ctKe+K zj`BJd8)ydFHjmz47a>w+ICi(Lb>Y3gt5|dQ&1K|XH<0pulwAGI0yXKL=nVzLl-}kW z`U>$Ut?$D9WSg(J&sb|PK-MOdFE-8;++8vN%~k2N)(3yiRXS&g4!GylS1f|2j&671 z5SWbrX@2Tlcn=>%{vHGT-}n>R)j6G2KjuJNMlfu?nJsFj%#YQjAz9ht=h7*;kj*w2 zhn{*v_@a6ez@bED&ptzMoMHqK7&WP5v4FglijqrwcM$`${PNA+S)S&b1R>jEs~9NPvX!`(U9hc`8ePU@^yl>ZFFz@uOHCOn z;}ObR0*M|4o>=SEVx7q6OePkwE`Bs`*@Bjc03HJM5@^AZneIJME)-0fO86Q|(yUDc zD4|7upk4dlLWZct5rCn0Fa_&Nl|F(5r{665!^bQ8^ZotzC86^$mOZAEgRd?RAXz#a z_;~(dHRH@x?6A8}Sn?m*E8UQR+p3^?y{$Z@c{*N^iJt8lkN?|nq`uiU?u^bshfjBp z{cIqrfP0GG(g}g0xorm}j|dKBVDZrt2VUlpsspg7{x9Q_jig-8ogIXhh_v#{lQ(Z$d)?RvFhsewDZ}}TNCns)%q3N{=G(h;8PAc{w2@X!0+pL zqs^o4bPnWRt){AR8$AyRz|br?;BvkKgzv`+ooRPHDE$2U4jr`C4-{iRzkP&8G4Bo6 zJJ0`>d%xGVukr=#h*x%E=xD^yr`uo?J3j&Ol zf6*6fqJRXk(I%RKbu8LHi;@Ur7N;%tG{wOXZ`>f}{^5MSq=qt?E(q8L9uF2aMllZI zwacYO%N}?J+WPlI5HGQH87E15Cr}nbz71^tC%COIT;@-fhYIl_)$JDG?Z;8#F=Yfr z+;t}xfGu4-4)Q39wLhn^%&texm86Nkqru4gb%9<#UeJ*)$HuH9wF93g5B#!T3mszJ z4JsC(xvbq8?Sm@!`>=#Rw5v(f6eN87Z6^jOBrBv{?4p0!oON#lGlWlrhh zY`Z((_XNph5l7^9)yb&PCxVI!4cS$Nr?|5;hJP^v_Cp!a!|8#eHFCaG;*cTQ&`I3Q zv;V1b{P>W;E)Qsr8Wd)?n+0|?>f`SDeR*e+ws2vJwhA(_mA+NAWp}M@x^b-eLE12z zRm_W~ypoN2d#?U1XEQ3F7*uF$gQawDQt-&802BFLk5mkPY4hsU1{Tb`R`S1f<{;}n zSpa6-J^BlEu|IWMT%Q0DuIPrUQzUO2^wJOb|2Xvkk#>C-gJy-YX^)@WOdBvyzGYE zRYxFnY-%*!6d+gbDmI`PqXGx(qd~B+@JS}ymvTJw78Y#pB56q>LoB7DF_08$nnV)P@E&|6k3#QGT1wpp|-o3wT z0CU6G1M(Pq03dYyokdT10)fSs4%oW#ea`GJ_|-mW<0OP%{g2Sb&Y8lb-Ww(qQI~(N zP=q$8PPgV>ftKyhTU!BbLX!*e;1EV{-iPGh;oglyg6faRp(6_kJDLyYpwUfqocgb zV3XJjdcfxkk9VXRtZi(g8A>SAv3N#ka|qB_Xzp5JKX;vO@da_`%oB!XG0Ym9-4z9( z1;d`vXkx}!R$(5HGGAtW653rfSD1>H)5M`=>334dwEkFdxX)?$b1I2P0{Whn_^2TdnJo*3bbh8-t_S|s+HYx&JKAFuL&tT}vC_f~R z`DDwW39L(g?sjl#!@SwN1WHieloLQlHh-?U}|< zpZNq9pPihY?`n+Q2hsU0*$~1|Xi;I%4O0 zS%1VR5{s)lm1KB=nbQEQtA~Gh<$3z2Ly-#fi4hl?Qy5%!9#J)iTEYb_*%1y|yX1gP z=3d!zE~4kNMg4|v``9;Z|m!{YsnQtYyLY1OdvQda1n)rg@M z)mtA|{k|YkwW)b#WaK{UvPy1S*Y{olW2UVwd~?3Fx$$El+b3#Sk-fJ_-|=}czrs^v zNqevuPb3S53#BXCn_*C13)%lGqqqGenBkjeg_XwPNO|P7-N;q}HO(7{~GE4u5&B;H2&E3e>zubu# z^npuhAah>A71QtC)aJ|kb(J~+mrqoSz(#7&vZmzbsYZ7O){P1>NiRA9>@zE>KGP%S z--o&+yvAi}V@MqK-U>-IC|CRsM5h9x7qgK-ZYL{F%-tW{5ibHiY$69d9zpeUHDjy! z=`3S#jtVr3ZBPDGN_CTG_EtZ_^(1t;cIUb1Pm1JD+nT^?gyLL|-(H#MRYP?~UO^-Q ze{eaIGgbO>Dk>mO%OUg+d2OD#CB30epP<9(Zq1Q)>sE4+B7Nkclx4zu|J{D?N zr|9n{bkKJUHg~dBfB(v(e}s48(9TT%-*#qRt4RP->d%*BZ=w3ea~&}N@!ba6dgVFq z3Men^wjzTE7TDA*f=Jy_3ajFD?aqo`p~Cn$zemBuQEG1J}Ws6JH)*vMgse{tp7EL;UJg27xLTD z5OZDvK&_we87PRcPIMCdFb92R-4T+E?3m+!E~^3t)uWyH}UmqOWtVj(JQd>Y0k+N++_`XQXa%L;qm-Or6 z;{U_hn+HO@zW@J&P+2CaWC@X_4Q0v>(*^Ks-cY7B{eo1yVF{Oob% zx;{@~NeuWGivkVEWHb`|2B}J1Akq1%l{G_@_Y5?QD^MbjVW(bxgQSIvKQ8EaQ__&; z2!GNjQ>SIswFkiENF<>*dDYqU*)4H86MwX?Nq29ggHq zs~&iWG+jRw9wHTJde=#G(h4t%W0~D}YH7n1N5kL(NSWPj0=KENw8TnC>Znp+DKvr&s9Rt&c+nFUD`VkUixm(j&D`y$?vrlQP%vXN49Vw`o zUGP7dI!3>pdU6j2cijTl)17S2kAZ|8Ze9#ytASnr2{(1af6R}%>{V@pcaB#Kju-S` z1YGFr9wYw8{^HELJ$-+9je(%V`ojmf*xd3IqXx7Hq|4YJg%+M27U~`iJXD3EQNNka zAgq3SlKPk|5;d)cMS`|#8k2kBFW$ptzT3)M#;K3Vjy7*OECn7#eS9Z>Bf>98*6`P& z7_hel#fo=1AeJB`0%HCjcLdwFOC$0q&M0~89xtMW*F<3wl-PEQ8(TWug<_ctz3)k# zO~FnaQNk*tIg5_mL>5yD&>mzR6jU+L#x+k(#ZmZ$TIaLXSiIJ{F2XQi(#XGnv>;-?_U!keLzF>iLxx5st8@&Jns>hSt=Tg^6VP7V35M zF?g)a=%*%eQfuIlqJ-I5htHVmamY zFL?HKE_wEieF>SphQyJ~2wfeuZ6vE(=p=$VIf*bg#*HNjsZ)xO)NP&QA*NxoKh{(q z-g(PvrX*hK^>pQmYBwE2rDchHOga#C9q%o;B60dgh`0$$8x+-KoN-!&XQeS=^}Am8XbkdK7wk=+DA2WId^3m(>A^m(67fZZbt!Ym^KD&?r&C)4bv`cD5a(xf1tne(atVr^?>-XTdD=e+P|ALLYrjO9^ z!5&?R2A1v4`P!pkIraV`pGsC&I?yMyFzppNO~7FvqkI`YYlnn9XLyvrrcNOeL_tl& zHV*SZM{`z_;|X(_6KL+uutUOkFGICrv43HfE!gpBFx(cI#N9=hv;%Jq`uC+3_GE&$ z9uhXa@!^~36K0R`4QXOS{Ei#x!5F?gI2+fvVl|N@pZvoB@Tq7o`tN!&tkaN~oB(fQ z2JZFO#^jP&VCA<7_LNru+e||-)|;>k>&(?Qu0(PK@``I7tb89UQ+ad5d4WB!NE}(Y z_g;BK#>%%zjS+L#I!!fI;HdV)VqTtYk2w1sfm6?hx4<7nwwIhuBh0__>MOL!>C4=o zJ5GVd7~2U&$hj#TOMa`vwwCt~etCob-~AH+{zKT5;~!G#VzrHK+6AZ_PfwhBb>-`Z zS?Y#84W-hvXxazd^jIs=eTec7+S?w%h?+}7j?OouLZw+toC|~c6f_o05-QP$Sno(( z)79Ng_wVwcCQUS=_#$mj?IvqMt<&WtYz2#%`m}so5OmvU+l8qJbeM)fkd60WAv^*diL|*IWNO6SXxn@Y7h}(bTY9 zdq-MZZ%)5qVAQ~&9b2lqwD;39r<|^>G$t&1qLeV^Pj#o^!^ueuUhODq2W4~>`x_mB zHJP^m;wJsV(`?Zk;rnk*@JQFYN8WM%+H%aRlVF)Win@6aAgEk|)2Jw&~Y z9iCCkF@nmVW8~NN7!G2sMw7e%Rd{p$FJ3FZ-zbWc zU^zPbx%Yoq1R8$Qdthe6vy8i0H2b>RUPZ3Ju7kGa&l3QELrYMa;SJHR*~Ij-to4Ql zNvplq(y^}(P_k^|^<96_3KpCK;iWr4u)qdP;mq5Q2L}DICJm^FO!AQxi3L4k@O;15 z>WdQ#*28oYaw_v`%=72HKL}ieNvL)F2y_a{Yvb&T%v5QkQ6bYDcCse;(KE5PeT$MN zZAXAsCEAd|Mvwli_zLEY$ToTYSb`LM+P=W82%)kkU*;$*H9_op58J(60d{Y_)Gcnp z_>0ZI8mznQ<>A%Y6}I%BWSoxMu8sT98HFxH3weAhbcecmuyxQEtOaI`wH}I0! zqBmMK14emJXy3E)pIMWl4lHqw_9aCXqZhX9U^1HD(VE&^DS0p(U$0UPxQFkZ5s&Lg zS*mQ+YPx&)nZymKN+*^XM_@efx1a;Ykr8$fYDc?iL6@=k4TM!>2?G0pk_L&nLyKEF zU|P~$auHtghZCTCm;hU!bh1_meqPB(k!@Mr-5#_o%IGfoh5l6hGxlev7aT)>NZW#t z99N@#rM1z1n@w2S{qvEuZr{hiZg7(t!H-2O0|)8CVR})kU-I}fz6o|C7g+@R^PKpS zPXZTERo5GoK$SK>0)-8H9;HGz*QR4UlV7jE8l4f;T>zWuf^FGF4;}X7U?D5i%sGGi z9B>b|Zv<8Fxnn-SZTk48m+{yDPO$X8!Uc3WPgvrh)`AWHvCY3z>%P^G<&8^==2z!6 z{=qs0Xp=bi52%uY8YQRp|K;Sx5L#9)ZAiu#P7}5pmw?x2ts)hs^A( zkY<<`Yg6t8jui}=coDw{nur1){u2QRM|Z|?o#hlqSF#~u7Fa|}zDeP-GgksNy1u<5 zeK&7yE3jxE4W|y^X=<}~uth{wQT1D+=y8#D6nR>xJgXCJ*eIREKxas3+{ModpMato zGxFJam&kOBzBc)E=!_`vHu(_Ab~HtSmu^=KE9vwZlUX)ziVB47oXNAo&f-00GQCC%iC7t0v}Q{V>Ug7=vBU1|Wx*oFr^0pC zFCff-6*Q~5>LQ^Eha5EhE^rB|+)w8g9zj`YuYI(B1-CHH!$0Lp=+VXQn?L0 z?KNxNHWdtD=;Z-yAHLw|nF7qRcyKN_Ve=!T;=;s~> znb>+3seku;wlShK(x7S_En*L07MsO7zP>#>ug6_TeWC*$DRdw>M6PdnL;iDbe5KBp zm_;=MUL!~W_E_C?dn_vRHCKC}oM|+|zz6|^O|`+Y7dV)VId^YH7A6nzT#s!IZbbvd zD4ak>L83IGsz3SEDp1?bdrexDL>SX58AtQKU$owu3;eCdI;MV$RaB29Y&J$K-OZ3E%!t0Vkdjuq6;4WCoonC!K#OY z=E=Wi>*xLM=VsvGJ=m)Q>dhX*1d1}UZA^l&8Jh`RFeIvyK9k#tp5zXG@X`zBO%cd@ zBpt~6vcVP0;YM~NmZ3)(qWg$Czcovy-s0#pO7*#j{3bA<^teA4*ltViv~#~08u!aT z<`L8L%P^x^SA#OBf?6m>LhRUsnn(u)WJZ5+hbbbTix>%{kuS!15 z6W0K9g|+0r01{hUaz-Ky9S7_{Zns>`)FWqzI`9vRYE4it3V9`W15?ly>I_<8sUe*NG%epn&!Ch=jAxqC-hfjHZDa6fQ zCiKnnKo%yeXGIXwid`sy&0xbM(d)xWx<5r;>#^CPTn`2~*h^;04)MUyveb*E?rlH^ z7;_Zn0KoZZKIQ4#&Vc&$%heM62NvLfV;Dg(V8PGS>bqS+o&`OoqdwYEiyF-ob{hf{ zQQ(507!I-i8R;cz2XI(VpKU_>OcQm$$}5cQAQ*Qopn`AR`N5t}%}p^uOnOw?C@jP| zJ`di0J!bt!klPNhl80U^5`iWg`(I(rm%nHXKdoZ<)g*PMhWsKRFaNRH00IllX8vQf z;j5%uUny-a5H*=ZSWi0FB{bYTez}dr4di4RPD6W=i>a8LB`QFdk-w{MK-uk%{fpHp zeQYfWn2n(4m6ob+G!?wB%5M%*9l&s+RA@mD6Bu2abIM5?SO*YaK*FP8dtYJ=Xq`gi zvcw$A&Irh!R)Lxrt7@Z(@(xwZz|@uG@eDz5^$yLZIWAbGccd@bV=wS`3LEE{^QT5> ztP^~9^#5?VWs(TAI5Xa--0rffg`mN-N-T0^SiX|^-BnA$1Xgcf^yz^wJ+_x z?h5JtCcT2Zkxw6Cpu!^zy%t^Gf91ss%XO`0PGwsL#{Vmi=i{$Kyb#|;KvIoEPV}>K zEV6jO8vF1E;=>C@x!Tn>)~(8{zB_C@56FKB)GU~vrZ6J3{tAMZyX`e2L}>{m&OIf1 zxV|JYK?6t>d@el}~$MX?FK;0ok#!_;+{X^-*-I0asA^)>UFghpQRr3JJs;Zgxe2OT^E5VzryAMeNX;#ak3HMnho3-G%%X8 z?Z#TZ$Bc)37s)w-(kUB1uFkDUTiw6KCfTag8@{)tUP*`rP?UiEZrqX&;Qt@Sn$ct_?I&R(9Qe(P(4KjZLJBk#Q@<$7 z?QusPr4oO;`036J)~a!n>7~0iIZOP*YF(S2ghVb++ zcv*qxXJEB7qGj~!54~K3 zVjhctTCf3Qcqwjn;B9Qwp%%>a#S23XxhOL23 zT(y}q?{>b3Sp8pX(EoEdDO*Pvzco87@loM1OaF(@ajF2piC6O|arYW33LKpaAH|UCu_hq=<3>y^p-YD4G&f$dqeSI*zmi{My%!y^3u9;i3V5^Q3D7Yn z$re}6v|gM5{I5ey+_}jEbZh(c7|{%208*=v*aVi@L4vjI2_RpP31fHA6ar=U+~_BD z(sXAt0UAY7vZ0RovHb{XRZ@jw;Qp`VeFi;vXAe!*S!c&T`OE-^1rGxQKJ%LeWKWELhpbZorqt7u?UMecAJTXV??|icrPFc&JGEmaJN2zJV%-ei7_c!hJX!M@?All@vT=3k zXiQ)R(;*E3$B=wV4_O7mO%C8zk56~>p~!5=Hfm~HprpEt`ZmXIgto3>?te2=C-FRg zW2VkoT$jn=nP6;xC>^lB9uSrPpIE7<#u$YyTg{mGrG)&Na=q!~ZFhZ*(e3in|GD+? zPoqYzm{^@g*vMCmi|Qbp&zmrId|V6wOJHp3!HhGpE6D@hTV_MZp7pr7wMF)*35d5H zKMwK6c{#L%ZY^Dy7Dr2Sqnws(8W+jyix_2(nBL+>uR6>#Q`lxiEK-`m!F1~vVPr#HuP|y?+%7SF=?IzG{ig|I6#1e=<)JPX~I_+_GmgVVcQR6LO1(?KiF5V99BDP_%_o=`|00NQ3zf z3LIjclp^}>i~pN5Mdn~8Ji@SIv*UkyZ|-lUlO$ko=A^#=H{{5uEBOhUrsCkG(3vTL zqBN?HjTMk*t_-o3s_{AnTfvsR3drJ+^Goi1f8Y$k^naY)Cf0{anhosmTu&*O4t>o9 zf~kyXh)aqQ-O>KgO4ztx*mUb_-0SJkoURUtZ0}WE2+g6XLLy$hk(n%QWSj@2MG0ga zKX5m+eMsdIA&>OZGavUoX0`ukNzjMn_zZjb0IV(Chg9!j&c6g)(_uSZpV#tqS7%}T z+ix4r9^7u+`{r_JTuf2oFw7N9oKFuVLWQF~uI>|=d0nHbV9J5kt>tyP z^N?n?#7>wW^Qh;6c1p6>S}{>hFt+E_55VeliVz5KlK8Ctm#r#(TurNg8$2LxC^5nL zvjz~3k8p)9MpXu=$01Y)g}k6-AHJ zKZ>3s3x%Bpu(`?9vKHG*YJ(lSf-;oaV)^UMw`Oz zgPyhb;4Ny70B>p%iNJhc=&_Qq;Yj($NTUSu=ZQm$yfENZ4}f8?1;1&W2*exD8D7PZ zbQ`}!xL@XaG!t$9Y##mtaNk9`&jM_JWC#6zoA5qCU;xPCl6K_HZ`HMdk50lVcJ9Of zZVp`MxF#|hU;=hIGB?OzFoJcOGuw)N@Ti^sp8&S$E?rZ1q(3E`x0uH<;Je z@sd^-(G^H*o)@yc2U&Q-*_0=_Oq#lOL%tgCj47Q;d&FIa^nj;LT=my(@^iis-37Gy zEZEa-1r8xVsX7QNRlp7SK6fM1C+MvFqx4IE674(sw?MHFVTLd#wABrqSq10|Gz*17 zp5Bxljkt_$c{L1JlRs`?wk9UpD-O8Hb83bWG2|PjZ2A-o0@bz4=OUKH;Y~-*=>wUf zR_yIrAK;-V6{Nw@z=IHi)+|YkcR#A)%`~A*1EE{bY8}ehUGQCNChqgHwtRL`C#azVfqHm!$<|92_!!N;>J5&p;x;cKeTTXHNkg;gK7kj#PC>9;r|>9dh@cw)4c*@ zpUbRIYp^c&-L8RZjb&oBlqNb4{;-V7eR+U7+}JWT1c znLfhVowAtoOxl@vQGvbfciXD`l%u-cM*!LLmh7QD)$&ghy98hPggYl}a){L1g*zRL z?S`N^4m6b_H0R!Ht4m>Jgd*C=!(He#?KuDOGzVBO(zSd7Gi~CpU7YWWF?prc4>b4- zNZ=V>I;WwFX8KrPNthR6M@qpxn5;AJ(oMc?v&6il8>4~Ery#GdbQ_bxGOi5tSYT@& z^p&t>99p#w3PEVLGHvy7e|Sh5xXe^sR-R^iCJ}8SNFLj?bF)Icq0#&tY`ztN^A7?b zy&-BCt6gf?ee9jvpD)Q@od<#F*|~^p{R`A4L=6ceZD1Vd??@Xh0&w#8QTz(`(y!8c z>HPoMONUk8>pZujVrOiEwJN`fyqVg6_SA}p;%O{HN;g%KWAJX_r``TvKX~c0G*S8|!y7z`^049{0V@tepe~{Nl@`K_aCMh+G?{_3 zllEF($`E==MTnM*CGCubMyauwbKKAkA8=jvWDC){Mk(2b!q`o2A{)9w0(#aY{qSQ; zWs5-d)vZxIL_mB?mH5?63xQY*qhYV(GMilr3m$hrK`5%sM(n93VA|?{wxh(_^&g2fy_W>e2v*Pu?K!HG%hsZlK0mkbrx8^+ ze}Gh_yQv+%yP)8UNL`n$o}Q@zCOWWU-}Sl#?!Mg2X#^0bES@p>?rU2B{XH7q^o(k? zd-_0u1^W%L4^U?m;y9N{swH!kT^r+gQapQJmngF`m7hIlr0$^qctei8eI4k+(D|u> zIJ)zl(B+Pj2TZW@-OBB?n$I1J)51vi zuUY<}$}Fv{0b&TF8^yV^2SD(^-}dNv)GBeSxP1@6u(|fFtw}1@no>_#GQ&Iy3Dfg} zG({yGrq`?k+5ky2C>@|3l3Uk9)G1Rr$R~PFb5}tA+c#8MDP&X!Q1!g0G(QW-e08s3 zLjdlJvXayhIw~mqy>$+1f%`(!hUd7unDdjorjt}X#-VN!SMK?%XoM1iq#!Rq-W^H>|^2xfG z--)-bg0BJbHnSyDgReU`;8&|`Fc)|&->hZ*g5)=3g;_y_r+fplA*Z}AJdoPzreCGf zXKHAciU%!G8A;c+R=BGP?}St77I$%8%3uG9^Z-b)v+L;e%I(3H(Tl+46!Vcjp zc%FW;&tqZEg~g6v_FFcE?+M@uj?0Pja7c+S*q$<_S;eddNIV<{k_<0u4kS;hQh=cy zV2h5N)3`7ydv70bcHOF8iks0H*k2YWmzP6YC^Uapt4izy1o}l2oU& zAgEslnR`D>bNbKKpdERBfGYSy^nPQqLPhQ84()AcVpasRTU3blD3CC|yUjO(0g^wy zPBG|4K<#c?6fgO~g4Dj`%XR9?M+puP&q)zb*7x9ilzbF&QhAj!{=6NxbZI(=mhn5uxTm z$yVna>=E^v4UxT-{ZMjqrll~k?3PF||NT#UZW&)pJH~xhgNXFBgr^IbC7r;=L_b;< z-k1>iQ*CK;~hsiq)GRhqQ$Hzaj;ykI^ahNp;_vopEB>Pu+sr|HCJAaoslvw8B5am>sF0 z{y-*%FT(ywx259~I0h5k;}@*GY>&W9S*?>hJHiMQaA8icp{DL2MZ5voq*=E{vJl7<{v^3>M4KnXl_7zG3zwULt6(GFpy>*I z95kRo@2b%K5&T*--<6XRx^f1h8*%X z$zTMLx{Qk9h>%p~&>-f%Tu3D|mZ9;hyCNVZw0S2`&;p^Rwu8rSMUys1v&ssoI4i`h z4A5d(t^`!?LKTpq_Ky?G-{l;8IJ+Lfx~UUxdOS%U$YQktmAUWQH&?-wq&$&8L6P(> z=By&$=@QeJB{>Dw&Yi*gH-PNj1{l=9n%H%V&T5HQ>ms3EP8_T9apoo%YV&-T>0L#d%;&(gwjnU7X{+U zP0|M@ok)e)VI43mWwEaK?x!aOl`;DYc zerm{h1NRkW+~9etrBF3dcuy<-HVGMKxB#pCiJC8Ho?+Hl#0<>%^*O%!-9Wb~!Cm5R ze=67)kGt_I{^&=ZrSaF_5OUQvE9ay6-PXYK(fQp!Nc|=wA&xTqzGXhmUP^2$hBVH+ zpO#EL4OS5~{YEL@Zm3hltywSZ!y!j^#OlblflS%9N;DjPw)iMAO zWxTDqr^*<(T^NTyc2t3)tD~TJCAu8UEoLvWiI42U4QPZ@nhLW~Jr|G!sLj=@Lx|FY zn!%}222s=#Est)3aHM~rH~yOOaFsQ;TxH4L-xaMDjC;lerY%+g%ju)3-=@Krg{l75 z!~GqC3#h-Q+fGj#Cn*8Wx{E=&;UQSlejMysxt(1W9zo|vSJVxp!#8^`D7@>K@JZ%& zd+~rSP=Tf}%ohem^ZdULIKtD9P%T+bOi-FPJ2YeGql5|1LCKng)45hT>A<3T{8f-{Qwu?I-#m}DHk zi;laJSNFBIAaX0}We&qnkD@lS0k43YnPdXXeQX~zD>M%RBk@3*VG_?I(2i4Ib(<_v z{qYTzqzBNQlYsy5n$_|1)M=rFN(OE{Gw+RVbd~RZZ}*tRsExClER)$V3xz) zGpA1;wi;mC7banncbx>OUdZ5YTT6h~(#joM`nF^ycwc;y`*0KoZ0MOiWP!KQ9(5fh zu$^N0)Sci9$7JiV%@m(?{BCpAHUHS{U4eSNz%kk!4U{_AFdURky(`Nmdy&P)YtSKU z%_9FD=hh?zO3y|~yOHaNw5nUNLD0E;GJ%y+kDZq8aZfFjj(N`s)oAdXoKB&^oQ7{IT?NbLz`wX@(MR*Ag7+fWo z0Dua_9u^qZUKM zZL&z@@<3Xfc37rGTwE&(bMWAX?XBJ$?&*fH-Z_&Jbn<85QQ+v3Bp`gn2y(6_m|x<+ zN?9xxQ9I@T44=(6{`~Ig6_@_wm^!Y-;tJ7|=A3p>~ z4ivmAfoOm0v&{-I1d}2)DVc~)!kTt|mq3EKxYy6$Cee;-shK%7Jxk!}5S5pP1d7%S z1~>`_g_4`nEW+#U{(}Qj_IT zkL(r=T`X+D@#a3CXC5~zE7z63 zz^6OYv_s5#5@dm7wB8^ z@$~ZdSaF0Q*!HT)t-h_fM)p9?${enZjq(CDweeOg7t}U73*@BvRzgTWmjbdRL~T^+ zF+5f_S@-Df8n)7nkt4dkwXsnG^!rV5iooK{t##Djel8O#MM8H_W7+G}NR_hq0I2x} zc3if z$tqiSPMMPVlj{BrYjvV^%}7*0fJn-V)yKE*_7@di-z_Yb!p(epVBJQUqha2r+oB5P z61m-LoG`ib>P}v=P`)9OasQd%*k>VixeOD4Qyx00Y4Q9Mi zy+yzwyCe6TZhhPrl3wtmCr0)>{vI0at9snky{;9B-eS*a+~t3c=`*`0m;o?6*&VAS zcI?}|H_v=t>3)@n3HhOyX{XKi?W|Upf|$9_x4@hix>F}TOPCSHxlfZ=kIC>O$2GYv z3)SZ1f^42*-Xa@6CmX!q8=tAH!eG92cz>PVh>rB_gW<|JBk?)8^W={lU<0zDSyskD zqDG}sBo7mO=f2{tFS=?%C%Dguuq2iAsO6SAEa0**s1N6lV-pFnM=Mo zV2Bw}lQYhUxE13*cb?&ER7Vx~hdoRNBYTeRsSxz;lR3tTp~>bs%@&M$tbgI_>x){` zHgnnjvz+%OWkaWBF4s653FLchk97T9)q8DIP>4k|tJ=?ZJ!{&?O2=6{MKe9FexPkseEMm9 zr{Ezte2KjpH>?%ugH0`bh^(U zCXKxm74+Ggm}{2C&q|1^wI;z$=??Y<52CDr)lNvxQD07ax?|4HEMX~ZKg~XxtWOEPYF1YL zhEdE$RhPf&PR2AldrQzHXWA`$!7h<*j(6A8_jAv@JJ3*&A016{kk;0&sz;1i*(K4# z_9K3HDQsIqyvCf06;(IY`?N~fB* z;>`!VlP*kE_n^IqwT4>+1U$sa(b8^J6H(FX?KoT4qSOMSQrzfNxW+?oOW1bj^5;Ce z8&yd!!@Kk$x7G=gJzAao<(yH57~8h=fSN7Goa0pFqe+%^=JmoyJxKc(f=E>r{!!p# zDTZ#Y2)&0XQX>|f{?0pf-KdP!(v)g~T#TU+Nmt|O_sVVYdoBFkI>RdcxJ5L!4;M|X z>AS30o67I_q>(RkJ0^CTt7EtRi@-FpL>@xJMZ-~sIc=DMmw4F5#G}1Pt*VytkwaOG zyETUTT9&Qn!u?#m#-XAek5ZEjc4-x^4SO}d%~npjp$Qy3o(YA=8J?ugjs7_wFzOmp zKgl`bL#FwH-<|}zi->&SO3aBoIeh3{k4xZnaj{*Iz2zi{E~_@`se3}3*4~KCcAm|M zXuWlZ0T<9@3ZFb1|3dGQ-J$11kJVmnaeEgZxtD5n)b2e;BbK>3eGg?P;);V!Rv%9K z?QC#e`W-~pMXeTpURS4($lGV85vqY7LZ~&YQmFY5=wt3|TR}mA1xp@pyCN;0VA4uc z5h_TE#^&`9QSkXg!{?9OgDYxzV0l2WnLBjaTVke@Knv}?3r*q5qSx(~zOEo^g_ITN z$aKl_nFYTrAI%vV6k_FPKQ~j~c74jJqnKy^mRpn&s9UP#uB3OvKKBySt2Ixeeqb`r zxZLQBZakCl{T`eDU?1B6C#QU#y@DaPt<45|1)}U8g-k!U=x%Rhb-tSig0QcXOY$GUzj%G& znHV%exW2i@)J=og)6~LiA$H#avN9tdh)=v%mfgeFy)t^%;;GGUT={CL*RGuBpSYgP z)uvj)yS}wWRVdEwx`)Y~&0hpJ_i|yfBpMD``)$V$uCd;}w(?!z?r(K0uei^I-$LRb1?9?mg83CHaeaAVM~YbY!7^KZUY`FJ~1f|GXD`wlk-Z#lfD zRC_E^$!X+FB3CMJYdRaEDAwa3x9dZe&t@JhQCVFCSL*D%6rNUEszMVI@K9(+v$&QZ zDWes`P&uUcLfv3^-tywyhn>i#iY$aobcf6C9%bL|#9`Kt=J~D+8gBa=o!+KJo|!g` zd|dvnaAR+?mS|KJ8yu_dvy=#kRTWsu%1X%Tq?EgvYAve*#P%zt#Rdp)HPCI zpe>R0_-1vPMojG92{{AOW&Y0A?U7^2CuS;t?B{SipEw<~_X3{#USv~HyLwHe zV*R*8SL+Ow7?Gqp8dO1*Ql`DsET-+vm(F9WXBq!!Rb)lnDIl)eviF1hUiqq$RGolc zUc0$k0r12nc3!qNnLppYrJJdQL%(H9MS-1xvvI8yV|xIHql7qL6H<0#+E0d-3<`m^ zwQX|LC>hDgsm`B6In|m=p<<_xBsUOMT3HftnIn$p!p195Z%2!rpFSd~7YdE7venS} zqQ-uo+r)|G>Biz-bMfJeB?%oT1zyXv366zDR}JmD8Dez@?=d)j50WLLA@s~cMJe1| z5ybO?>t(VCJ}pOsMeBt`oc}prBQA3>#dIHc$Ic7BZ?w~&Y7E4e_w7fzct{`APfwM5 zaH{XQUhv5yWo-xI)8Dd$cNOCAy1ImMrTDiCET6GDy?BCe(Vpb7Zsz^oA5kBY zY||SqB*tN)@V0jcPw1b-pGOGa56IcA-@?yn`Dx7O!xvK6gCzJAW=1;N9uGNtjo7lg zZe8h#_j{kT1)bP&&InhzH_r}x7CedjjQte@Nt!SEn(h&8xO~4e?Lpqa=ocLmzaDSC z`SsmFqn9ix>EYag3jX^3XLzpYi`ZZaCh`MYupdUgpNdyc9%DpEulmOK`c8wSgv3#h zd&$H*R&+O%)yeT^#$L5d)Wb%FM%xxnp&8_CJrZe(h)$n{l%)Lf>rUBNQbtG-vp3=9 zV0(LeT6r5}0dev$w)(F9HdFm+(pdpRX$c5sL!|KrS?(kb2kE-!hq}+?V`&DT){pN| z4l5hu!I~hHHh-W!Qrs;x+g@tu_;#}YkSPbp0B0n7ir~Oej%OnC;&oT_6M{e>!1ZXg zJB~44@>%{C7gn7(s(W(%w(FfyWiLspwRcY(>24KY`~0qaI{g@l8lt*(U#?LI?|OlP+F0`*_XSpxG)lTThcDhi>Kr_uMLri4kz)6)(m?q2<`Yvn07T zhtZKM`%*tzjBK*L)wSbXS|ix9LEIa@vyDQDWDAaYE$5RoEalxCY{f)K7sBQp1PdTVyK zr0I#CmCdmNxvJW1FgQUGPQ0g)AAUaUnS#2~LZDHoz1@HzPbfbWvd5oScWoXf$M_jV zdgXmAH8?G8@BvYs5!T24F#5f8e-uL(Gh$%Pg^J=Y=H}nE%&Y@!19w)Y1s;@(=Gn_F zRJ#@&$nv)9@-KWp&ts`jT25C4mV2X>O{3tJHK`dcrF8=RN2_{O`7*Zax7-PBb1Boa zVc59(Z7`9UF~o(HwZ+a=`ns|Ft;mU}k&+i;PWIgX9$l){*rVP-Dnb|fg2%%5Sn+>m zr(Q`uezJ@h^KkoBoo`S>H#FauphGkUP zlPC^H?PkW8tJXE zK0YcBS}$%IueuB_LCpYXV|hpEbK8~^d}n!Y_dO~$_4l@&J1Wqm8}{Z(lmrnM!xrw| zHe*t8EcV8aaRQ?r{Hjoo*q7=l9N-zF`V1QPlft^`VR>Ux8T9p?X|exO8Kjj@8x6x#CglrUQtUH(N(Ov8#%OeE9SdJBF8TRGTYE=H%q0$z{==WJ(dq zdDy!zWT@x$1yM;`!KeQe$0e@4v(GaC)xM_5``&p{1Dd8wYVXX`On>^Vr7}US^0C~* zje&ZVnXIZDcU#y0D9zY4lTF@FnZVXN|&W^Ql8DC=eY>swG zGQVMBdy=EGTTvP=&v@1r_c1!+Rg~Hl__9=*OkyXm##&r@^)uKlTN1onGSpxM_YL)r zNx@e5GwUOcDahK9L*rkSsW~1_e6!`E4%3;?7X=PS&+mK&o*ug&e(vvElO3D2Ntc9P zTaZ3Q4`n+tcpoQDIcRXd@((1puSxHS^&Bg@7MtW*XM42VueJB*s>lB6Y(}ylgmNYM z&h&biFQxj=*8U@WE=LDvSL?S_Y&yfrWN%j`nzD_n{H{#T2{p~#2Q+V3S{Q$NXceo9 zbxIyNEA`=LXU}%*(7rm>N6tbtvQy#9)?heWJRHWoA?e4M$>ZuQGbfuX)=_uQ87Rmd zOGH(bgQUZO!J8f8MV`UH7S)=;~P*9%=4Lx5ev5N=o##N`;a}DK1 zb(**BGj!bAm;s`azq))ODi+Qr2dmbPfUJGr9l2)5ctdK;)_=>|;~AGYeh~N0(+vbyro9 z7+zSE_GR4a_~~;|@Ij#arMn3SA5Xl9jSGL8zsvpGO?}(^eGkln$f2Uw8Yk9KPSx+s z>v)@t+~EPd*(5Y{a_mla*4bQG$lRU9IZkKYEvu`P(b%C=ca{G&58@E1sgG$?SV+#! zvUB7#=XLj}(5UNN2*ssLf}fUcx4)3)#uXCHHqT@7-*y#<08PW4eTLbyZ$+lwF6+c#+ApE?{2edwO^IA1q3vzl6g1QU5{IMZ zE<*CV_>s|w=p1Ri*=D=h+}}?iX@`&J5x5v z!N7$}zR)H0LqN{{_s3K@ZP!MxO_M5%(rz$5!FFMsBc#yZTl(NNL(J&iQMA{PzK}+b z-qE8mZmC1o2-mEjCcelh^R5O_QDWGDfpgt?#q-jPuUg4?#QlQQj#zbjjo`-}`q>DL zpXC;EU#s&}X9gh}eA)FbTz0#%Xj|aRj)uLvHjGFNaHptF@0CJ)n`u?Pa0K&3SRB;5 zBg3>tY>jGGmi@X^Nd-lEDRuYCz_1evc zxiQyFJ{MVL_)#u6!PT&G9;oXiFAG}32W#QEQg{ZGDA|smhmKlGjBtQ7{l?+MbBdk& z@bZh{Iu~^uKR~QxMm%M@&AsVuTJl5W*WS#688Ps-*WU@n5+sLk=VU&+2jiy;8Za~v>ar6*TqBQc2$M4*rZr< zT$o8cwHuj3p@9Qs?={sNj?5iZIDS-F({b?aJFxx(-y1P@1`GW<;p1zhCP)_N(Ki*@NWfmB{XDK&dFI4+ZwZ9`` zw}ER(m*JM<>P{7<;X`Og?j5NSo8Apf_aB;k@TpGVP?Cj(;N7M$ARd&*ImGLfY6_ORK`!O$}$ zjkdska-3Zc)}G{_>TAM^A<(weBo-7y7K_dR#v7LwB6f-il-ZBz@AS!%Kv1mooF7w zV66OSWfwa0%#HL^GPTy3bwI6YS#gASQ^++)qpq6Ist!+xkNgSJL=NFwuWzifny{3S@S}5Kfk2X%oCS6(E z%ynJvJ}ev$#IVEF@S}l-DmB|@86$g{7vg4LoiIc51G%t!Ez?eRp|~Mc16~ZhQjX@O z%e0H-u-VSq*(ax;zuP#ad&TnO*tPjrAn{*Nc*`1;*TDHhP(jLDW#r6$?Yi?E?Ix99 zmJZi?v6$!PANvgM)$w&kGn#<&C5Up+%ObTrslew>9z4%ZmJ2^@lld;zYcv(~1EQ0c zblwTQNSd64Vspt5fzh&QGpMEvo#%3Z&ii2Dy$Xiks~fweDa{5Zw3imDf}NnOa`wSa zeCChf$(<1Q_&nEVMQ!m9200$>LtJ&nCH_j`4=J9*G!m4^KLHZK+t+HZaM6|BL*bm? z!CS=#Yx3;sQh5Vyr?AcS75+Tv)m_4cY!i)yPIGK6;r+cdZr(?058TQzl~E`dIPTCsx-%B8~zqKn;vrSrzl&#HiymgIxD5#!NK^6~k_p6`A7(V{J;`)Ej z*{^J2&-ox9lJHP_o8qUiQ&X!lHZYriFWhw1nyDdZ*O4NeK#KZgoOJfp4};LeiDb?l zLb+3yD>(3T*LIya_fjv$4Xq=p=ejcIla2mWaoOOwfx7!EW9LVHV@6yuh`QYA+C}j7 zB^n9N+`xKxYUNWfLuTg@)kG$K?Q<9{=SA&yMMBXOoF%5$R2ZyVn5uN;VxC(2c10lP zF$y(ikfkA{>lk$_=MF{cC?U_IKXZ!EopJ5FL88lx|41*cpO=)IjcK<@5D#aF zsQf5wv;NGH6PNoR7y-4$p>V7DWFiQlQPdLR}?K+EqD-sDJ23sZ08ivG~E4V%?(;wbV73hQ@t} zWz633IQB=M&Fi?6uKemzftLYv28(j-e%z9KGZj!`%UxBcE&>?jCiM4zkv9 z)<8ntC$paQluWbWeWTWl-tQvo8tJv}sGWF@^85vhO`m_y@m}$Nw)~isag_IQR_IS# zL(!%UuS8F{Ix7h!xV;`G=WN>ZSZy82A)--R)$9_FbwEc`vS5a_&codjoWt7=pWa&) z+GoI7ziriM>rC#9(~mU#yBTJs%@}Hh-PyJc)j_pFL#sde1unI$mTRYAj`1e`+;KF6 zuz7y=#X+J@WqfZ9^VVpXBih+;ZU7TbGA0PL{FP8pJBNmX?mU4{4sU3{xqaRF9`5=f{62v{v5=B};R^oI!06h6mlwoXEw`MJN% z(6_kE!Q`ii;QK#$eczT_NvkbLMm!pzuu5e^%o=a6%~_(j?;b>M|&k9I7NGF_c}x0#HJ;&8`GrY z_fou`e<}!0=x)afo-erSu5l347{$Y;*;1SHEzoAF9Ej!bK_l2&a`a}Mx>4fLwM&ma zpRAKe3&#HdMRX?!4)?$oYwhTpn|Zd{4|?2pJNiQWoi_VXj;dG9^T)(RtY5P@NSj+M zo|3g6scB*>{Iy+5Z2NkR+l) z5+YjM$*Pc%L=mEe&@!{jUS&i@q{t?sC1qrEqfn8IQeA}y58kM-U&Vw5I7%4c@YmJcf+s3p zrO;Of#ap~!r8q^datkv;eCr!dn~!i>!Qrs?w79W0*SJl|W6sqqNtIeA&%llD|Z*bsFkJ}|6$h2Hk{F|0+o11%o)41*>!a-I)&r1V(4(B4WA z*C>`Kr<&`gc$hW%^;-EVOZMP5abGIce2!ig`Ry0}bPhIL+2bYxFw7Of3~p|y8+=C6 z@#UyC(Dz-Gu;jwlWcec|o1Q{}mW@_Ij_X$#IxeZQyXL;mNtX)ZPc@3z(~A{ZKS);T zwqH6fqdAztNm$;Sa%JN-y*I@N*c@!lwkU7&Q?mG*c%o0`56aLwdFb0@3bVe0-<;z+ z&ma;Qq((g8COajn#S5ooBSsKFwTW4db{oD|)b(Ax6(g~f3H#D6VmY9d>v{KoG+W8b>k>GiZ!Y%oB zo`Xy((V=nLt@N}Y2?_Fc9~hjFsU+ISr#qMnKDSt0TUVxA>70+eYIwMexg1A9SU`hj zc^bQEd{2#JiKCdic8;(P}svNGBcl}n%-N(h5I`u-wzE4$c59-p9;p@h6XLpf>QjRKa#fwT-EG z0FRJ^9_p4qWk=U129r+(|MWB4no@o&j4e4NF#p)vDGG)cpJ>H#6hlb#P?puIrdS@_ z022Xu4Sl*P((1UlNMjBiA-yRmjLhhno1{9q-vDIJ0gmL=#w~4BhdHy7J_MGJrr-Tp zKE5ODIEEaY_2m^=0(#E)_-xpIwq3c-SnoxqkwJ@4N548RtfL})_@}na-JV(%8JsaS zTYb`KFKihY3HMD>>61qamLK~Q$-*N6je@hLiO%yKN)ObHTWAX;s9jn*2}b~;8-0$c zkGK@QJ8wNU>kU~$0bf!2?dFF7(Jhcl0BaA&ROhc~X`XYRx$8!YH5L~Akx1gT?76!K z{VE7!*)lwJOQlp?36}negTad>+bTe!*XYMk(C<;+v1Q7{wJ1IRajqy=AQx+F@?X#WE1C2>Y5n3adNPsOXydfG)SJmTnKd z3F`%1)%XDtmJ{xG?GU>v9PAD%7W;d-45;&8OIU3=5EcB)Z}&P+%|jx+6Z7|>q4@lz zWFR#ZF}jjbu{M30mKn;1!8ZE1wR>ISA4$xO>Ga{Kih3%{*h&2O^0r^Uy;5Afvs*9P z`ZL^ROq9B{j>DzG_N884ckSu;1Atw-v{4h=YC>3v5t)zb6lFHP(8u|@YwRiAGSt!3 zL7eMmQlli?6WLY=;viG#ELTJus?XDQ)l)^=q&cRe&f-$Kp>#OWo!36{ZPk1}aUy_7 z(VchQoBmnNRp+aG(@Jvu82w$px;;T;mubizeG0kJ1II8Jpr>17;$J%acJmi;!=7?g zz{G1PCTzT6OCnsaC<6CmSU|Tf77(^+FI(oYUo3(R!dkY#!z_gRTi z?+OY(&)oIGW8(zoL!@)X{u$q$H?Pi&^jP4&pPDk?W2LozZ&Pv^6jIVNpWA6e z>FnERL-n0&RkPTJdd9bTh>4scOHEJ#TNfsLnntNp$>NQ_J#hp4LvMZr0K0QjQ-<9WXxS-qG2gf#O_S+)(e zgT)NyEnD0i5yCmbAgjP>^u&rvld86wCwMXQ- zroH^;hr*UNnbk(=QIX${H%PWd_8DI2*ENg4S7)h~Kw(;STJ_m!F+YG~VhqHgt?Y7g z3dA&9o#)K-cCWHlLtGsL&=6in{)GtNdZ@}fr4eW`<6LPfD*LUug}d8G+)u9U32E|r zxn|T?s)6O4LfM{kW-o)J$!uzv%8k@-QjYT->ZtDE%-0)pMBEx}mby53n!+I5OfP-4+S}>#rKNcU zcI;5N9H$9$V>WY=7Kg-$!w?!Wu-#aXGmRcjWS z{Pu&Py7s8ca|r$SE?k+QBRLAs?x`gb^0%va*HS_3mC&Ix+4@9KGJYz2AWK96)c zI{0p%X1%7-kA(QGd4|>l%I7McX+D-;w-pBs+>VhqRwLqDQSM02sz1db?Gb{e%#zls z9X}j*u*CoP{3P?XJS_9=lS-qvWWB9_Y&16<%HByBcK0_ zv)B$sdUYc`h?JO5*taa!t&h4)DF_$dj4g*Jsm}%%Yd;J3>iDp!XX<4;j;e2nBY*N` zXSKcSstva#HK5jnrfdmk-3S{hj$DHcWa>+(OYiOa2ul93 zcv-@DoU53!YKuPCLBf2mOqgiHH`Esn@vbOy9y%>$JoP!rjT%H9(*eY}@ZnjBj^N*7 zOBffOaqF9ceLY`pg$G+Xp7SE1!f6LXebK(>Y_b(+m~(vD$aIH`M%NQ$F)qx3AOt|q z=3ize5*R#Ag6%Od_(X1^y%0s*5N-b+?-9NboA@v*{LY&1@T6DXqO8{V-N$4Bj_`;wt$+^0`pLB66pHB4P_B&GVtbhli0khb znPsJz4W*Emp<$acqr%XUyE$F&x*Oc*1S~$q(Ib^^wqs~+Pjhfy|4xt`)LTks>Joz` zNcsye*uL8NjCXVKT_K#@HXs>1Z)c0gT*S9 z2>gh5(g`1HiUBd$4)p9ngfT$nBi>N;NU0m3lg6(5D!4!E0L;s`^Sh z&Rn%;dN!y%U|-@r5lTtIM>di1kxsLXDfBBAu|>s8UD+Vm<)E(iuenX}Tw?AtMZ-o3 zQEU6Lsg!6#2h1u)0yQW=vi zx()WecI)3pgkJ^`E-=TH7;~)Zau+|DHvzFP(l%bryi);0`vy0G?>UBYcpB4`-&%($ zKRy&;izwm=Y;;UB9RAiT@3rmG2I`}XAG(9hzHxG!x_Vv~O#helyOXVbh3wxLh~{3F z# zUy&+$!~|9bo%L4Br##m6rq< zBoR}tua6iuvTXV8l=Hbn^c>cB>EN|73 z0_|G)nq_R(@^%rH-gIcoTubMz6!$ey#ghl88A~l^w-Oy+r0C$mTVASg`@SBjaeR?F zohV}-|1M`35hiurs|Utgtj)44vp?F;WqiWnW+nCgk(e|39sT@NilEGhWz((Cekru` zj+WoHY?>ISIAG(oMR#V4U^p!+f%6gPhk-9Fm;Kxzi3 zG?+k7A(V8*@Lt@Bzq!V){XD?s|20CvSMx^n*;{)MnWdPo&P_^s6~z0 za;${Ud(^A?dXMA$=jTrH7CY9q4JBe7X7^!V*v3g? zt$M$-F(_Su&M$YioP9UaBr&&kCsPXjjcY}MEd6aMS40iktAiQJ$^cbd!hQt+EOB6} z?Ko?9R&VrBbE+=kAuZYivjpDBP*0{0&Y5U&yK`37(9G$|&h}EQPPdlmc<=o%C!K<5vBZe1JsQUthwo zXup4_c{z9Ot1;59OA4*ORl(AxXAfCEK7{nSSh+9vYpzP15)5*V=m=!2tSqUZhHfy) z$DPz?_-}*ORXrbD`G)$>MLFvT>HgJkOV@))oq_NhUzy-^GCrrJZt&(Vs>d3LODW3j zp=9-8Yybh^9Vv{~e;eGB{yREb=b`t$y=jZvpmJGJPTZYD=wtV<`$?8G(SYLO{ecsQ zP1klmHeEn{A0FvwjsCUO)Tc+=*1zwm9bc_YPN8HlbNLC8#!-PI&%ES4_c2K)ZNc|O zyLOJeD;>N$9Xrx>I^TS1j^Mg-LoupsU$;K4A)zHA6%TsU>bJkLaG5?#0XWu3NGGmg zFt2nlZ^VHBnhNJl`Xha;IO6cyyl%fC9K}Qr@Q6^>+>@<9ndvv%r@Fl7d}yUd-;DO? z?Je`~I_WALHpAz8#A52F&D<_a#ZEKB^lS8%1Tl3o0uNNl!1EXY?1QKef1IlhZtIw ztr8hPj&B9Q657xo8MR3}%$8EP$au`0bKx12^RaZe+Wx$$Zho&UUVK$GGs3U(Q@?s( zvP=n}=mEcay?^+CakO;}k&cmjZ8Y~yo%kuW{NJq;1R^*H2F?!oQ@9T#65_Vb`&q_J z08BObyq$wMJTnr0wc?o>mltNbf3YaV*J0%!N|&-H0>?`hHJXuut@x~_(xSR-7|5KC7{Y3Q5a9QEWQ%+2 zK0?A7)c|MAVccA2rFNTFRaA54NqwM*$hUuz!G3F0S@Y})VVM*oEHlXE1WMerl_#4B zK>xR7B0!7E?@0D#J{^Q^X|34ly$Uqa)xJ+AqI-`Cy)B;K_HLM}vN&pz^4B;nmz|Yr zh)NxQ@Rdc@iuXm<1|sxUU|X zl9{#CSVzQX4rTMPdtUGUlCbw}bJIKI9G|CU#WGa5^O6s&>rrU=Q0%;8h)Aj{Bs&X-JNk+_t) zWcbO^3uOpX;jz5A2{^4tp4O%2)4II*1oM*w6nNw;c#M=}K__0BGSNQj{%$)cr*Btl z^F!j?br3>+)gf9rRy}XJ@pxyij}Cv`2mNKcO}PfQ_{UO%vAS;hL2h@7EU^LYruq2h zY?l&Ny&KbLFennCZ+YT@T`Bpq!Cj#5yZN$TK7u*Z3lx5@ zzgkJI)dwvF!^)SAT^mZNw#-s9(LRW9>k=%z={6&-#h0uTxrbnc9NwR$Zagyejf)$+!P2nsFo|?3$Deq*0MSRze(}&>H@xb; zFidW3HlGdo7u8}5^di0p#)2pixCAIs>DU@CF~ZNoU+861$Z|6{0@YYCJ{C)FSWmV0 z^V@1Eoz1P$*p~)BN4I3bnb-@F%t4pS|62D&8#E2?Zu**N!1=TQG%g9zp_N6sSAwqX z8A>!Pe?t%;XwSaqeK@%99$YyBUArNyGF@ z#*F+mf%;s3em@-i7!*l~e8N%}yWOcB`VQeT@`>)~3+j07-u)@0xiiKtP0iiexQOtL z$hF1G*&vtVm6MfqbFA66xTP=X>g09Ao3Ktw&daQ7iyevPXv)NQ`wt%4=F-@9X&pL| zk#L|Jy|yao?SXaYJX6`1Xt{WCAetOpMRgY}QImSV!>`h=di21>9ETHSGZb%J^5KZO z8Co%rKOB;6^q&k067u2Kz+9aP*nL1&d9Q!{{fD$9I5P7wUHL#`KG0jI#VW6Vl2fZ( zaYc|xLovF`B(JrpahddJN#LAdc%e~Uu`2hBCDVS#CC_%Tm7Z?!?^^JZR)VJ4{J&DZiKFt_;y0cAT!aZj-E`p>unM zEb0iSKjrZ=ISh-*ZE1*Pka1>#`8j8YvN%8mBI27YKD7cw@IGr+gZN=&pDy^K&NdIr z#Aj3$9QAY%RB`G`c1reG$*YiaXcaQV}Lgv&ec1}DXE2rdJ7n6ecsQFvVM*+Ecb>Y3lMnCf*FTV&p>m4U%51V+NT_dUdMKmgDjaxdpy};?XkT*g0q}{;0BU~xdAj35k zUEZf*g{g|U$O>_et*!ble=7MxZ zQ^cr#CKy{{7PMbUlK35KjH#+gIKT!FXqu=wjz&4wB!Acid8R?WEBiX1n?#q-YlBME%RR7-fq9yFFV43W(-9B=mR+G<470; z>p$3;HX|BXbH?`-DEWURIH?YT&GyH0X!99sBa7a%etAQC>Arok;~S*{>9X6dS>g?n z+gP?_pe^$WPr!3hs6^jLSgwlp+wrX)OG_q1O5_D-wKAmI>w&H57iMev??}xpCIWdH z>rLqXPM;$8fw011wnl~qj;Pw6coT{n%_;g!6};CiY^g!`z8K=SY+@Pq29LSvk(Xw}!bNc-Xp5?rNQ2u(k(!8)_!Yrus@sj~&U zu@S63&*k!$O!#Zu?@r^uSkW|1KlqAB$WPYdm(tJ>22%%(FF{eSrwRTPW$bwDqqcKxa|G*El{$8vX!!N?P8*++ixa%pV1U zEI6(cPX15X-^e6i;;lFZ?H!gDgXt6sUt$Uc&SBtWDZ!;z3^9xYCHP+(EVcC}QzO)` z%Opd=x~D3ys1YW5@3G~~_3;7iE!N|Q`rY=}(7O%SM9kOPTq`c2?`<0Bm5V0JxjkPi zZ>Fh@+JOYbHMt()HD@0oyJcD4usy9(Y`Zhg^ils~Km5ZUP=?$o7mOoQyq|?X{u{x| zj~uh=;Fx{osF(2mRHBaLKihb4*JG&vlxLA8$#l;g#p*)&tN=$@^79ruv{fMG_dn5o z1)oHE{kN?vD87bclJ4m>re8?TF?i^Jg#GeyMpY{5oD|gwvbe?!nN+pyp1Bge2{@j~RLNoEa))~Quy;Y&=pA@OYEqH6c_gx{^ zC}vcnvTfgwj10Pe97zX97q*LhF|%+Xf<|Tj$CDv3gAs4Dxz3p4iY7180yEJ3EZ$K+P3Xk)y@Yk z1G5?3E(Trq3&9q7hyXYaP)kN-5vMT-9nU9;6*@-BL=i^4E%76L)c^_8Jlct8l&32hQ1Z{c=jGCwHe=j-I#lHxfRM! zH+>V@OE#WI#jSb0c}df4yP)a*)+M}G{^?U&&5C2jZPA?Zm|DokQDMt~w^$dJc4J3M zyIC+qP@ny~F!6lIUo&*STiQB%2ve-YOm`%OaJWwkmiBYatM%U4kIv@%l~@$PA*F+L z^Inhft?zyhk9IQ<`|3kJ9UMF13$lCXy%UNa#G%KN7XtHWCTnhE6Bah^V$J;3hlH@* zE+xO^2Jams&f1J;>b`E6IrxdA?$m&keK&M-yl~SnSLw;(fQ>b|J!bNHKc3!Nap$!c zV3TUH^qCtzDjP9x$~Y_LOD<0e*S~oVPqpXh2&~8*o_o_8ef10PfMX&@GGfJ$XYW@B zn*=7~vUt=Gs0{j3Z*t!_qTFAUQBLn?%Udz72cJu8bhXr87L+hl_XKr~lZDwWbzB|= zWlt!vb$-}~(Ex_6O2L)T+&3Nzhecl_lPBrY0kwsYhQBsM5Z+QYRD~yjB}{oj9I<3b zL=;#yy5c`Jk2?(~HW#&mAZPv9tFCD~KJm7qsV zjj$4KA^u`zC@Ot2xr>A0XO{mZ&9tR>0Xzk6XW z{FNTX8HB1-W}J(BolPFD@)~J}23=!_R~$!dQw|8}smZz9y=8n%vzJ% zSmPztv#8(NwBoz86^&PRpcE~0kG`g%+2;Q8D@H7AD;D6%8 z08IA&tr`Mgvd|IZTCfhjzfh%SQ8#ptEKX_SCr?;PJ1HN?dxdA!aQd1Kdh3;xd*(q* z)@RF{70|)sGv`}J5@Xzsi9T9TkGL^vyVn-8q$YZ*BI3n-P0obT#s*Z|_)(`ohwWHU=WnD|pX_>xB zdJ0mVo)I7Ix*OhWr4FB=x6vVSzDZWj3Hz zP2EHQab3UnFU2F=Rw>&-@-&Ko76o8lEKTmrfA0@uHR+ce4$I|RIJWDFI4;5Qx#ebB z-IJvh_4t0)=G=yn2rl`Iy{_yGh`18JSN1UGi&lBbbvBPs#ZmpC$me(SDFBk%a^9V; zr4#bwPOqGq-&t4Vr8psQ)tuGniUn*9KaKfxn!7%9x(~~hwntBI*P?we8$jGDcv;>; zPnXb^u?%0o@%|^@TF<`nFQn}{@bB$hs0M zo0M3#SoD`aU`MXHymERph@Q>0q4e0kJq1Y={hX^96ZY)(h3t{b38P@Xe-ljex&LxO zJ;YKxiNn*s4)t4Ze7klrcU23tk;|ZqNX>i?O1Ko>cnS!?!a)eu9Tb8UohDWlvv*xd z*`7}2U=k(OHY9#tmg5z2*q@+#vVQA#FjUBDIadVM{Q7aha6;0!-!O@Wl69(zBZjFhhpea{)|}ZX#Vgp8dA$?^=8v7 z&*gO=algjXOYJc+9?N6t`7+8&k?$C=?m8;rCEqgbtxKqI+R6o)<(^F05NP0~WRINlk^9yZ>??_uyI8$jj{pt8$PalzL}zH4 zIk`bjopOrB+;Tl=gTxoXbQ2Gskc(hm_1oJO*A?s$nHphT7;`65ARG9O^~ZfZkTj8_ zoZdRcP^qJ#xB7TilGt5vBckODjs&(iJ`ggv->7|pCf5`AR;n-qL8+_skWP_wIGALD zD>eiP{$gN~sn7PB1wAip43>Xw3~T5t*LHhrRfq*`%V}-eY?#9jQXyeHBwmqodNyN` zR~eMOH%MeIs8z&#Groyv$T*)ALy8p?{?b33=)rPuyhN>i>pRs_PA=lg@1MsWe}4_^ zRK1e87%D&g@s$TWSSf+TtiBcz82UTF`qlIO=35arD)y78Oa~lGXS`jgAAZATi}#2 zE()io0`8s*=9H)%WZRojpP*N5xc(-Te+0B>Pqdyr&>(|Rlk?5Sdb$+F?5#_=Nmmqe zRm#ei$>n@T<ANG(c!L-J02#ZLML>wAp}!*%M1*sa`S#9USXM>3c^66q2Ud0=b9 zO|!eEK86Cn2FbXlC~y=mh4gN#A3St7fB0=_LW0I@CNTS$7;c$&A$#)M?bx+D^QT!X zbuEaV^ZJj2(j)1zwGB)RWNg=sD~nxD}{i}{moC~oMg-1wq3-}@gcM_zvbX$EnWL1Lm2bg z3F`inHvFmahwUodnwTFORqBjuQ_?B=UcCuhL&g44^);J7uayHwfCTO8*VXT9w5-jc z;;S4U-Dvu)Kf!^dq83Cd>ir#uMANtM{=`$f6QIoYvpIw)B@!)*^4#KHDE~0nCjs{7 zqB;;WL{;OLiy7c!Sj3vpg6*-oumI2{O~nHhRR=Ff)+Ak$AGAI3%qRW!7d?I_4bjvu zQ(pq(&Ql+)_Q!ChQL>l;!^w;n4R`p)*h6sheXYJ@5^tk*53Nc%2b zf_nBA&S&2fJRy;BH-H@W>SAq=6<*odVuNalVRQ_~)5u^ddrnpgXcmX+s6FP*-V2?R z^b=8p_5jR8NpR5D=@$fbE?9OxZiS8!-MMW!Ys#cU%Rd}W8Ieh!K6%FNp zu?9m>#2KA;-tuXdzh9pUSW=Dt(HTBgc&UCurp{4yEMs&zStOvad0Pf(*>r!4cidO$eo7AQI9kTk znWIMn#rKv@PMd?LEi%uqbFlNop1JNl#Y^V=yc9$XM*9gC zis%&R303lUU4K2E$fp3THBw)y%aVAz)1KI}dFMA*o-mf6?PHe{g07qgT1wc2|5$T* z2?@VYbKi|LB9>DRb9?ml-hOEad44#AW>};(;WQphXefY)3@L?aXmTidI>?Ek1TWe3 z+ojo>E6)WFaLc9NOX=_QrYTf0(c1Zgd89%?j*E#(ZdT4(8`qZWQijTCm1 zNk>)ZaZN5{YZ2lm)&m{({#V$OnA~T~#zVySE}uuEvveJXz4w3ths%S5_(Lb8US`(% z$;Tm^O+T-kq0bDuV(KQYnO#O*8oXt+rBe*&!jF;zM*WgOq`j9brrIxy$LD35P?BWQ^#>J5NSE z+y0L27&b9A9-=e;68@nSx1I2J5wcxYge=mLdA5XyD>lyhPJ-jVqe>*c4uTP(I3sAa zr}{3C+xCC9fIsMOD(zY9D|6G>ab9NivrKd=qJR<&IM`V_zF zlz;UeOI0|7Ry3$b*7M(p9o@Q)x>nKe>}R{(r}F2-b*+w@g@)DJxCR}fn%RH*$&qJd z3J4O9R|+?r63r9=x)gK&ZDS~4Hlqh!!Gyn5^-Et3?SOIA`pd*78EL=(YYnal;b$D9A$k59&+Ogn6UxPI`}WGuh&^*0*> zxAWql3H-K$9$;x}SMXG>e{0&p_5gp#K#sEuWpW)I%7c4uPh!h)S5x*=VC5REEKLos zsCBgJow?Wqc{I_6H=_fAF&$v4xWpXtIP4oYNzM-Hsd&;}>?N^fY^XG3SANfG*sbn1 zP2U84wE{lDrC(8;qq@MHFCyGBL2;*%)ZFizI2>`_QUQr_4}KM*Xt-f|bD;Mq^HlHA zRPVDBuRbVNnY+>pxO_rUxAZ_DSj>lI?&fl8HhO|82!=5+N7B2B3ULGT5-nw)oou(_TK|%|8mDA zf8<8ZqmtIHpRcB8A))p!PrYJ#_WU$6Tt21)=m&2{Gd8E(uAiv;Xf z1Q_numoCbq9~t`EEF|HEU3Qs6bZGA)9csSz!X>X0w!O=y&IQH5i?_=EHu9YC%7QSU z`!~RrvX4!gW$>Eb#jCP=xg7foLvf$x3=-xqd$ev7dsX{tBf_*@3OVM}KhQ_p`Zppp z2&XqWB2nuaIvLJHbx*U#OZTi4~R)ozx^}FnPd{fDvKTBH;p&*nrv~~P{gawzbq$p zVcuui%AEw!(8u>l1n~sf;;Jiv=C77s4vZrCSub1yicOo!!EFp&KUbQy#_4c_CSCB6 zC|V2NAk7U~JA)EpmSh@ydqvMpPLtG4jbwGx*`gyw#gG=bRPZZmqx>M`+ef1C>O!;` z(2bEt0T<0K(K&Wc(Ead8$8~N?yu$51O+A0bsK(xB>i#3UUDw6m(hs8yfprqnqq}XF%XzgRHR%D z_zq^>sGab*GxAcbVxvY@5>k@+I3M!KN+S8b|8>0oCmL;WX=QGLovmED3v~ zwtp0uq&=|=XHa(V)0HR0wllODjH!IDz~1pGqVdQa#Ud`@W{clpfPe9A+W(5^`F8Om z3}q6RZfF^z2M&&~Zq`{4UF>MA3E~^O#&?!I4Z@iXd9|nClz|rcn=M&nv44g*KfXo8 zCq>`z0%dkyrH}LURE4^}-jD1R%olef5ua`1P5D%rScE$}ozF%>3vNiP*QXl(MNaKgz0a{1Qh~`P)dl5CL^lMRqduBdZWch+ zAsqvR6OEVe!z_WN+$2Pc{^M!1XeBKhSHnud@740mcY1CCdup#+n@?vz{h*hP%iK;|otHQ}V*{DfXs=E9 z7vK~0$UWYdMN`rg78n3-tf!uQ0-#*((q{V>^!p=~Hj$HJOn14ByfW`7*Z*8LPGBJ0Xv{!6z<#!lql}XUQ{MWHil&hY`x&HfI6Q|?JweqF5=sL zOysc~3I7MFo?^vQ)~3(DJT8pjs9ef2WymTE3(M8Tw<(8Hw_avAO>VA^T-iZK3-sq5 zWq^9gZ^04q_E+^K?iIstL6ETuiiekUWtz=@tB{y`3ur>iW-YgIfu^5L!!p6u>MFYf z&+P^tYffd`(F(}Q$ymXBhp8vv>Hf=3+xcc_7^u%cz1X6E#%F1@`q?PQ{A)6dRDKLa zq*>xG8-1_ecGJbz=cDkwE`L^vC%J~p<*R$eBuW=hH_6rp1s{d-T#Gp`XKey`8)Pr^Lnf4Zwr%rmurXO&Z*_~CU(XtNMYJ3$Q*4_SfM}31y}Ht z>FCcOU&^esmS4J0ShipdtE6aUh3i+|AHl~@t~a=|4P8}peDi8xRV;6<2xb^-vsv1s zUSNBaX`G*A<-|9IrYnivxzIXvvraNjDOUdlILkE&kII<2q5RW`H_kl1fWs7 zPjyTCrfkr-s=q5WO1Pf9Fjd{nnm!hKM0Z>r#?e+)qF2g!7#-W`g(}=amhc)c2qJtV zw?1=2S-*-rrZ;u6W{2SZuP8SXlhak-hI#$OLT%BaDRRiz`@8>9AfxZ~*v&>DLm`P9 z?LQ2yNFkvMFXSN695B@Pi(mP6iSE`dzRNO9h&CSP_ zY5_Id|5n6W4M`)6a#%+n3v`6Eaj&(;{B?`aGD2-Uzmia!Phkrn0z$avvomq|#CgUF zeQl};2D4W2BmVfid#;C~eu_3#dU*10zq&>#733ekwCmy<>_r#PKkWXsQ0QFr(AXz= z2vc<`{nV#Qr1`eVug;asaAW6qhqSQh9tcabu_P(7J}HH>vQpeHaVUM5&VWqqC)gc>oYAEfUcT`Oc5T|- z@bU%00KQPt2|}8_08~~^T#ME~`uGIpQOG5H5~g zcdcRuVT~ht*BR#XBh?(6SVHM>c}%~#3kj52!}_@&ko6?J_!1`vb^`ax@OK+zOO}^+ zfH&6*uQDi1nT;JN=Vz$1_^8qA?pku+1`kke$keQzSl$b9Yvd)Er)- z7t@`9Qjv2nyOy-TTJJr_Q;jMgirxOidb$!{&YVn*xkzaGL1|652;M`5U+^dZt@5$L z`Q>|3g3!NCm$%y(V7NIg!e=vZtyiF-DhK&U(DL-~EaFuXoW4PRT^H@pra%oUnC#&f z2z=912B-ufXRz^x9LIo(#$yEbKvePIeHDlQTNIAi$>Wqby4oAfO1FOb+UTh0*@h*0sZY{O8Yx?_xMXmtd;CfZ}}owJ(^ad3_*} zxdrG)-r4s9<2XzA&nD9&n|_)+@&<+_gYlv{z9st;WB_`OLJ=Ysgto4b4a`$|p}+Nj z4kEbvL~}n_mH~8MQjtB9r7m@SaI%6pB7^VY8Wwx;#XS4W&^zfF_M=a5DZh0o{e(jv zhHK^=;)$>|L0+D_xIKkpt9G%Nlf(@M`;>l7vainY<;-T?^gZme2V2T1QphHVtJ{lk zZVp$s9$`dx?eKqQ2!opp{sG&^v^FpieAd7{S2AEEZHyxoAtB7%n*jdai~2@LsThoU z?|QYFf^v!1A=2F4jwWSkhc^tEfU~3?`*}4|kPwS$Anz_LA+>mKw}{bY7Y5j2{<+WY zlX{2W8nhN25yci+s%22MWMXXD_SBTKmEs7j>K z7y<$Nk@%_-_!6nvwwp&{RX!s8a=8VO<;_JbSW}Fx{H|Fcs{L_NHLnLMAy^agf4m+m zBX3SH|A)dnaq@5}05;wO6Vz4|5s-YaF=RWh+UN*?HlG8FOP)K$1fLK~ICp6|wTQ7- znJBOC_BCXy0vWI2I<9rRZz22Ht*s&OcQ$`&(SgklB8F`w=YfTd$fB|uIL213qY%HL zs<-pH7Q<1`Gcdc2r`51=8pRJ6n}M=n{{SuTbScH~$X1TttSa9Snej^^gS6yFUg89g zT;u1g_SaRrZn1NeT^iLHf&I09wwhs3r^$2XetG~*H>~tqvXWwk*C04@7k3<65bK(c z;=XNf9%V-`9}$9+pYn zNJJsa)_{9;er8b|dn36_Vs*=krZIvfw{$&He5Sib8EL7Z4YqB|#7TgQsXJkohsO;( z)jT}zHT2y}(zk@`HqK$?nkeEgjp>LXInmV42lCyPQtW%9NjDf704(`7Xx&&MjFsI2 zi=$4(*M^6!;SdK*5+#P6E*AVC_t&d_0`{sJ&O4@q>?50Uk6sZ+c8^cU?y*sKm2W6p zt!^oT5h8*)Ok_a&nL(;Efp(NFG+N~!Ykcq2B7jV!cJ`FZez#%w%|aBL?_9}i&sQ=F zWAE=X`0C$LTX9@L`h;{3$__Xq59W>TqBy3HhbL&iM_zfy8rBNJ=jx4Cag}`$*7kwG z2-?XDiXPH4X0pX>PFQupB(?%>J%w;Pl;e{rK5hvOi(6HZ* zXV)ahSgG}H38MpaH?IGpAu91u)T#PX;IfJXzC7E>kKGyy4CR#zh(@Oc8vVlqM5F%= z)bA2Yf_%kCp`TBef1ccAv5YFI>f%PD4xwCayl-Fb#$Ky;2iXjn9kW=gA4)*Hc~Y(7 zvRyWjK}8@{YxmN4x%8sHC)NZH(pn8)W<3P*&d4)O;`)Y8s?KZeJqrtqp53bjCE>uxA3j-kd^{?yB6Sl$X`J_k< z-$2$&p6_4Klj2NN@RWmoE||ePBLE>T{K{z_Tw&crX@q?FI`e1ACj^i#Rj$L;q6TXY!82l`|v^VMp{t zC#~=Y?Fo;_?#BS)oX$!LtGkJ;O~_p9rRiCBn3Q$$IkMu38kVty-FJrjV~y=Y@rp7c zIGwF_EoBe=u*8700e-PU6}_m-+8M#y>W}{)FMLR+Tc~c9RA+t4IV^sI(Z$kX3X}}p z2Zi0k82UuN7MLHmFpy~;uX%Y!Z;TMV!W!Yk!?VQUjM7rKo^ypYUhIbTQ0-MMhjoVL zgz}JF;j_>xuCXCiq8@>Tq*eG%lNa*fYx&xZO-j*jZ*FgS3JT~)6l6%JO?_4S)}_7H zn3OuUK+<|%#e4G)9Pcx3`w<3F$}T8M8Q76Wal>g*W_$D~Vkg2MTbap1(A6C+MLl#W zs7P!2L%2Fa^rw`}Hv95KF^qd6qHYF!Ab_Ncd35HcVk3BhU7CXm_t8rMR*_b`1{ z#yillIqLNti3Ssm4<3_3Zy)qQh5+CT`_^3c?L_(3WSNijlFY}Rm-km$<0zW>dtg3z z)opCi7)`1@*winfu4Bz~GYg}ucyxl#EP@J36$*2GVv3(?ue9XP>)L%C5o?}TfF%i1 zNV9Txv?R#{&G%N6YY&K{lQ5O)`5KB#as1jWi{bUd>4Q^GI~fM?)DPlu;}AV@{&tk1 zX=865A(ezIkaY@?`B+VY&!URe*>Pg@-qL4L!xJp`!?s*1a>r^F@yZIvl7-2?!Vv=% z9O(el{8)55dn92QsHPnt0+&2|et0t+vaJ8$N37&m!o><5O*^`ca4fw?5RijS{oE70 zq=$_(Re32GVXM>%F}m1+Y%+Ftpyy?( zcvpnU2u*C6czN%K7ft~>2sq}oeM4OXDh>2zyTuCc3#g_hp&)A@?v7t(4tdb^8C?HI z<=_I-Ov7inM#W}hJ73otFNYLH*qt?HZ-`si;)fGHwXApt-eRN}9bMnJRCVaOjU-sienIQRD*jVfwt){{u+?h(8BJb3q_BK_zlK98__s1b6&kAY zW@Qu6T)#pd=K7$8x!$rEt{K5%79`_n_NS&|zJ74JeisOe>nW(QLRV!?kgoQDcsJe) z3CN1f#oP@L9|_6q^N@}Wo9NJE@z=U%ApwC4bq^iAk(QaqVqw_|IV$^@DUQG7HWenK z^8acr&haO<@B$~6njIbCPg9pwCfA;YWQ1b#fm$5aWq~5#g2c6p`oDCo`U%Y&eAXsf7?7)sRR6J==<|ZkvgM=S!i{e4 zZm3e}FSJ5>w~mtwGJml{_j|FpLy`MDA@N8T;&2a?61@XqknP$heB0ReRlv!h@B>E* zrvSWc$6^&C&-25*O6N(sC`Y$$Ean0MI$>UyO7?E~}|I z5c@UF+FI(n5$Cv~^BZWyIT+HdBSsQFZNbDKNBUO0tGA{!xZzs+`lht_3Vi0}!+pr$_@(>MdWP^b)%akTtAc@}#nNgLG;c=AHXkUFTcevC(WeNlx? zSz(IHfxWw}(3k^vDi$IgNF7-R zlIDBYHwf;hy5^=NeFQ>2?IQJV|D<#d==!(@6YTp)NLJ-O#N}SEyG4aIsYM8ZU(WHh zA^5j3P)D3&my71R-so#TY#_0!h;rUzayqnm78Uw7HO_9m(dM=V(b*iML!V#Dh{l(n zCW#xd1be^?-buDrO+F@0$KXwfsze2N@Ww_*^m~GhWVkR^#Fm=x0kLzNke>EJkg zv9yNBrI1}DLl>N4lI{BLn@B)eU5UupgFwb=&g^Np9R^S_s_P8tGJW~rmNyVzei)x; zQc$IrN0t6H0|EIVAv+yo2~nimKCvguR#~Gp?3;vK7-|6RcYHk&ZIYGrMEv0D^QHb;a?d+9ecY2E9ZTz>`G2r0LAHYE}00#uxC*|D9 zCaBoAp<=HnKW7f6t+QjVQS>%e>amypk%P;7Y78_+)ZiV<)R)Tkv9MW*%d4j6YTzF# zLozxhwtTz&A&s$>^l=aUsi8juB+8zIKFvOBSYRRy>1-x@;Qt<_q+2lfiN4CXNxJwU z$BKj;UX7DHV*;}kocHBRZE*cPK{CR_gAc`A|w@3n_5VC14F{}LN zw>(&)p59LV+R~!wxI10HKhzy5&w#iOG9j)S6~9mB(x=i8UrsWBptw^xlS2$%``Q=m zrn}a*F{ve934Gkek9#0%%6z|uGZ3w^r_eb00_92I^aEzz7dOt7Qd~;v5p>^664kI~ z62A(BmJ&Y_Z#-M4ul?xZ>nasNH)m;d*GY3ut81yVl}vDYVpw+6xkw6L$LT3SCs-kY zguS&6EY*F?HxG$QIBTwd2peqFYp3&m8Pt5_fVIGY%*vuhKTcKrfhzDURv^zJCa9d5 zN_L#Q#&|ctW@`phWLku>&*wJM!$#feqd3(kgk~)f{ zVqF!^V5B!p=~Dfr1or}J3N*1};PLf|q`8X98y_FNT~b(@#5M%}r}DCVi)vaFG?9=4 z#;~aP-(-w(e>g`y3)*APm@aC~0HU-7$bKEB2A6sj^33pk#|O$Dq%{MSGQvN4xdNeW z+Y!5(Yv_S%yUibVznDEB9E!5Lz=Z{O+wE>1l9ur{ox7E{QN%9%=?BZ_q3x{1PpyEW z6)O);;=bh|ls?h}aLF*GC;9yuM+9ykJcVKg_BXaNS;#-L#Ky>9OJiht-s`ort2t#@ z(CYfoLHKku@+MyXUVk`FRQ6D>^^Giw*agHUY@%^Y;1Iy{kCwO*;HA{Mx0f?6i0{?2 zk}{C^z7&vpVNLS$Z;PJjo@s`_l!mI0p4ikL%s*h_4$T!&g6fD8%vq3rz!n_6HwxX* zAn=_VYW#u}e^FJ&(R-MJFj9Bqc=Q-MsxrvEm~9_`DG^|TH~dm`P7ht2+sL+MRaJ)Y z=<4jp`+lvpT}@>|RpqyP!!dx%zVmHQk>o}SkmNf7X|-pNL_T*2N2Tjhdl=wGV8pxL zf;u0)_XkPpR@_xe?!5a7!WWamv}Z#;lavQuFh;)ZZ>Mh_CThmauK&)SaINf1e_tCP zTgmO1#_vE0mTw4Dzf(+~`re?L*Lt6qbV*Nn5qr96O?PodYf~WHw`HaT zNkY=3B%!a~&jBJMqhu~bkOQ`Sf3;We!Z~+u{~I(rp`e0Xqn%SxXn)$Z?q)V@uD(cU zsj%hDv6E>ABW)gJB}inMV#Sy^2s;$lel?vPVW_M}@Kr*+TZr-m*o? z&fc5v|NZ%VRqp$_f9HSBbI$#o^PIY$-;T1P^aa(j5zMIWk8&7b4rB80@$jWbl#>_#+dTM^LL1x5$UHx z1#o6-AqyO6mYog@xI0InH_D;eB!TI*V>Sg>c>pdK`fGoK%db);6~{?qkaE2!@P@BD z&`|#NUnT`EtHw}REg$3go$1l0)6NkO;+WOpKu8hBRt{8gLkoLPQ&Ni{gXKJbGH)Q{zZ2F}(p_r;2|+#CC; zjCnnB^Dgl&?Q73wbZ}wiW~KJcSnfer>-Sy{4tjucURMUY*WZye-2Iv(cEs8pqz9rP zV9{Amt#ux8^tYi+(s1$pd;V~o5~UqODw^WBEhO1sSY8+G3hYFQ$KY_LzALkXYAFV5 z*J?fy4DIVxKp9|Y$biR8z9R6m8gWjXi(4%FrRKNBv@Q2nMhhH|eH0Q2r_REl$+xZh zDMag1K#f7IarpY)arn-#PcXeA1A*5so>~liu(hv3hY6Ff`1fi)wUt9Zx}u3B_t5hX8K#dN!|S`Wzyl9GlRt& z0Ox?vr6swFZsyxK<+-Qsw*uM~rk+hPh8~Sit?F%?_x=fhBcE#+eE6&;YfuKxbjP`X zuj&!+?Km!@== zKXiQhOT6;$>@fnCVKqnPIn31%jI|?dA z=SO;mfnNw$m6NR?^^n=V0TiP+UNFQCi#UIK!Cj~qR34Y2r|hJVZ36C+F~cf8pjwY^ zHw@T8io(&q6$NIJ8H+}k))EZb^9y}uU$f=P%geQ>?Gamr_H_{l7Luo)zezmQKatnV&VK1B_ z+FnyxOnvzDYC}`p@%qe9H|&MMF__!NQ?3eb@H(LX#`_=F$!7fF(YXGf)o*!7pE{}X z-%vIbg8A0arS(r(sEFD>-vaabDs9m}{kW;1%b|1c+VPr)wlY2B_bCjU3d~zZdnE68 z1>-jcTNi^7P$-jd2g}pOaer^qh}DCUz9f;haXy*vz^bUd$*z1HK-lAXtHpA zC#MMqpfomU^4CnkEU}Z%(SJXPH087aF$*HDS~$nR0MGj@)eQ@O1X41?lgWPxZ5Ezb zjhz-JJ@gbep~Gf%peCnL@wT2t9p}>!iv_=)%5mU2UnZlS+PA5~MsNZy=n(ABK=~^X ztM1n-TTKa)a@ z`Y%R`d}cgeFZ~y#wqS1Qy^32aw+a<{ePIP3&12W246W~K`Kjh5>g{&0z~R$WFtT+F z6$W3aL+G>d_Sz36r~oAs zmpOE^$@26ul+z8v$I7RRrXy+oEkq1doCa4eli*>LEBk&!=gtE=*F_z-J&c07Ze-Hw za>HLGBGM@M|68HNV^gmv!ci*fGus7Q;n87cmXcr~=;~jkbL}V`gp}pf*7aqNg}GZ8 zi)#Dv29V>02bAUreaLlDd>}hQ!&@hA0*z)_l7D>&FEBfHyhI3~=-}9i=pa8!xw7B- zWK5gby`*nmLPl)ICwUrHq|8e1m{})&9)}K_d!t#`m4;<0UqTVHtk^%qOmp}x`dTLj z9k^;aV~t!IZRK1(B1(VttJU`E*Z~M96df;Yn58+w7BgL=2$8Y;rTyW`(KPb(*lIjD z3N|Tp$PB6ycn4wN9pul3&neHGQ97?M>Cj}XzKLZ%@wrx2$*eDT-Lpj;9_|Csso|8(mtxTQ94bhiiHQXb3iO&3DY7CWi9hD2ebF9iRd+8NyqBz}klEuOEoo zMZM(?6Xb>#`GM%50FEga!PP$oi>B6!G-1_)lC`&Vp>t9zRlI#3m{Q{=O2cc`xq>!s zeI6A$YcosUs0B;d$R8tXXUA5P)8vkxX((~J1-8*5H$5h&4?0(N0{l06U>oMRG?moe`pcI zRc1#~&~i9Peq*Z-bn|k>JInd_9p3Ro1Q0+0MrD)*Cq7HfmaeBshh+BRX=K9}5C5b# zp&(BN#XP+*bR`~+GvMRc%+ZDqIi<&)OC3o1hPE$d53g#Fp}5JQGSJZWHsSyKYp=Bu zJ|OX6cg>1EWKQa!CA7k>WMU#-s*jXQ{_ATS@BRZ{k2Rs!^K|yYP=9ve9jKakJvEOk zimUx zVx*>SRF0vjIv|~DlWjja35wE&o&-TF6Tt|Uh?-r9Kg76Zbc_6I#j`w&+3LiI_QQWe zQx4q)+&Uh3yL^E(j?lj#s|>7Npdjl*$b^A6{(xf(#J{B>C`4Wi=Sw!XRlpX4j--YH z;k%9LO6?WvWMDG%BB>}?H2QKVNb`mN)b9v66ks-ZSSvML{aCu6Wg48TW{Ik}a$Y>5 z7cc}!uV|wd0im|eZ|>blAy0>ZXO0{%l|2A@@;Fq~D-zqTIyln{V*A_1x?9(lwT?18 z_Ovv2apf2b*NPethtDOB)}}P{4o~}Id;)2QYNptF1>=WiLh)qVn|#rUN0k(CiuO}8 zV9Y(TOhZEE=c~2Ia$I=8VNf|M;meP>D}_r3&ZSLc`E48d{UOYEV8Wb&MolXjQG#WM zCKVlo%89ecnsnf9f}LXEW+WH}Wd@QQJ8Is6VjgzB=t0&2cD_`Or6Evw;raJRS-ixoGA-;le=s7@!f+W8wyl3AOD8?LiUp_YOLsBflAt1lih7 zee!i;o=><=_RC@bPe9eE{~Th=YZ~guDS;eBDON_)s=)8ne=WXH<0)u(A0!Q5Uu6)$ zX_@c8Jn((F9R31Vao1GQcHI#adQXUZYv{|7Us|g?Hos|jeU>}7`iLc>a_9SuZdo}3 zN_n~na=8xSw-WxWRNjk8v86B-IdvE85hoFL{XhB_kE-Xv3~F@XFG+5L-QqD}mA7ZV87OXm_cFI$ zMNMJ69K3hnXS26m8eeQsGO;C^sC4P81gddIP5v_DG5X4->+<;1tdTF6Y1#-=+QO$k zlS@v@r5g#yQWVi^&L=}#D(4(CejYwFtB2fS6R8E6tRUwyc(7>6EDzlJLYVM^TVIQy zsQ)LEyV(%|!pk7~IUDyma1+SaSKBb4Ch!-uO3`|Ym0wK(Ku3^Pkm(q=ssT$LGR_>| zk^M1mOuxaxUgEYehXAgH5PUEXvgCW9&Eg@bl`|flU?1}IB>-CULW0r30DC}P$s;&_ z-UsE+K`3ho&!gp-)P*km$2dd|%$s|bd@n)bs6iCnVRY)cN*Hq7Couh=BiAl9SzXi?F7-J!R$zJ2fE?ne(#6q=GBTS3+tF}hiI6;+4K z7L9}LR?ntlY<4eb&ImzE9NrLCu5YUce3mf#1UK?#W!(eX6xd6aA~)ja5fBQHWs14> z$)-!)voOkWwe|tDb4-}zv;x>UcKvBCyq52==tU^D#erd14vm$9ln{xm^Z{+Z*n!q_ zMfOJ<&~5>E2OP@LhxfVuNzai6*s~t{${}*pgL{7m+>c)hL>)r$YA${NJ;kz^RuKG) zNMUhM4NB_v9GTs1r3+gxe+&wkAt+!eTYTTH{};~s7qP(Ke)KT(H^8m7jcW=qYn+1< zL<=4D$1-5O#3vzX>v}IRj*AV+uyBCL3nq7azoOr}&j>+=k7oZ5P_&8>E9qc9_pl-~ zk)%=r+^BrR%5fNqJV2fcg&R5;c>QJdta8&qPV&VRnw~3iD#lFLYwlnwbTh@n7~t9b zw`w8#TYSOl9%I_DJ30nvL3G)IG0011A{1lub`mr8johdiru5X+9?*>S z>Fdaf)CI4|S|F1xB|KU@?63;DZdeq3&wD_8o{JOO{*cz8w!no}9_WE1Wp&-v?SO+X zFkEkky;J0f>_<(Fc+?gtw!q{D+Ls2nk3iXrzucM-;?_o}L?e_)ZH{336S#m3e<(~By?)lD z>C%iW%9`4*JWVQlkk_=Tul+FR~~ z;Ja#JS(J9ravCJ`}xFf9aTsk0!>|8GAplc(MjH5EfD{0+mjdP0=bIkq*=3 zug}1C9idZ`de~R!e}Ulrl7k}jO)M8&sup){dP2lwK@Cw^fF$+YyFFl_N3&qSirF~> z?g|!pRIZLWqa|eaKtMNk)M4~XQM{4Q-u`#6@CV@B5C}@;{)JLcGbq6R8L8hU-D^;% zc+!-Mk^_X^&X-WegIg?o8r=BZKb$l5f1m%Veh4=H*y|o=0RHm4J9cr$JTH?O(*HJ( z^*?w&8PGsbE<{rpYQ=65zbBq7;NT}|c&z6YWw6UVGMp`jY9B1_5Hj|6Y4HJ5Wxbc) zj~btN7$;Jux|x#xI%|>N!0ruVp08mmY(t`VewMI3DOZjd#Bu3Z(m_QrRCE`To$p~N zem2g{SU(YUYKC7>!LmV>15d|C)L30e`?&0kM= z{tZulkqhl0!YmQKGPQ<}9NnWpY%E$=SJDlBxk^#l3jRQjl$-lnIH*tM-A5FjpRQ{! z)J=1i9F}5l@!>^pP5~Xtm`8zh$e4RR{0d8rs?uh=dW3pJz#ZME?*a4k0;F4g><9RT z_4ZwI_w!?xYc z#C<$8-ydpQL5*_&)#D15)A3wWX*ds-VHZdRz)wN*7}x^SS`-aTn2RX;L8FzFOVA9r zm<9OlJr10ja1Frg9!pc4lt5;k_AHOc9c5(`r!(k(%8^hbqKw8*<)k(h|M#i3Y}qYp0J22$PJua zDKB4dMAG$wsY>zi*7#jXa9B#xjkmH8|Hr(iB)^~j{E!}+sSRkntO9MgILnq4$RA*YI52=Br|P6S{@4m^oSvcR zWqLBLYtH@8$8qI07_edbJzxXZr^FjzV`cPBd-#}Yix!#9whqcaEd-9m0<9D(Z|&!j zAMFj(KadUUIl|w*VLAaS6Coorthd7R_3R*&3b}XvpRgyzxMIzQ1DKYkqK7*lhypwI zSS2?gpB#ioiy`oo0`3?Xp3`WNHL2V4Zemp{0ds#qAqPGDBR0T4YfB%TvllIMIvnkkG;81cdHgnR+D9zqHcdaEwj{CST`e8Z`(qjHs&d-e zT)F!Lh#~^AF->pVRZ9kaPXO6>ePRbBFpw>WX1NF3|HvZv(3BIm6_e_S=#&lzq(uF@ zLj4w_AfFVdOko^=>^)mzB!gsF)C}Nz$2_jE(8MHCV)j<}u7@0-kt8TqU|3Bpx@-Sk zWQ=dYTb1@HcxRk_WfC}s;K~V2o6S^%D9s5B>)CNOV0}vWKOTnRXe=}sLXoeurO9rb zw+P1bT%aya@#zDl-Pmy#*Z!j8QngN<95HCF3-%qi4!G`Pb{anu z+3AaSoOrZvO|K}Bs8<@|bnP;^a2WQ&4Ed*SLXS-#-JVzYM1N91KNFK*T{nT-Zhn$# zWF~rs#A}1CDv2(MH4n09N>@u%AK6e~_8{;>j9A;haM?MYYB4T1Q5gne2piSMpx;@( zcSLrPy&>Nuc(gyt*&o5}1Am9aE{D#k;+pTYxyjI{xmcz(J2@4f zV|o~H%*%}|AM%P{`VDsjDTe2KUj;k?d(E!wyAPbpgoH$aFXz8M$mV5uk_e`zz^2{j zb?QWtdRgCpPsVy3$LG0j+UJ8syOiJpo)($f%!|6+mtaDfhBra`8q>cj7yN3DE7w)i z8Bn=8zgU4KD{ZKBD-eI7c~Ld%HIf12G)EZwZpdL*-D|4`jNLk}e+T z4x_jFc56kA5~32EsL*j3SlrRkB3by+zA`5;+&`NTzj59HF{vDf5>Ms_43;L`n$7@8@y+jcjK;7I)+KtNP>0ggf`ySZubq zX5N`~AP;g%w(iY~3C}ESJ9Y43b*gIIryR{zJ3whNFz^Umb4iiVMaFB(J9imtI(F z`kYLeu5L!ydpc_SU_8}^+tTus0&2OR?qhME(lo@vr$y*w(3w}h8#z`)W``|@? z4YgWrIp!@v1-vMX0RAL*X()`^nojvx2hkNsno{tgwFWhHX_gp7B(*w6uLq` zLr7@bF5ZlT=%Q#hx$~vncSXa>zRR*rI)ev0nB1Pq8IN-$Y>>YbH@o`_ZLV3{gi}pn zICimu+T?oXBNMjk$!)voTCeapDyTL*+h(kU-yskckfUOU@X+< zcURXo`0Z`mU~yr^n!t^IY_b%#2hi5r+OrCWpLtQ${}$ z2w`HK^C_2R)sm0s(pO+sDwqRn0L2Tu@_21SdzOYr4|~hB3Z%aypU*rVIQmj_sU_gK zcH+I!^jU%Zwa{hfN=zCWn&%{vJ56tzknYU6U6sEs`VFVC(5}i#c8PDiQ7ba{c3mp( zd>Svly-q%bcQGTa1(PDHT!B$pyxXryUxp^(b|O<-Oz1}fT>aDLo5cLK2-_^@>P;=y z@XASU9wQ&(!9J>Uxl^mu^@b#*Y+C&TZns=GNwd_F*ld!5-%bPzAsJk|#1xctsCnjz zy%P6utgg$|ESTJt;vq6@ua2Mqi8;Cn{l5W&VN8_tLpM7 z?g6daPqb{`&|R?%o#=p`^I2-*x3aGKX&!Q-9)$=Rq8NMWmz}Dcy|JK;Mj?JOMadn8 zEi38d_jN2cjF)H8(uN8XKXt1IWdEXl)iBsiW|G=8WlBJK<+L5T$uL4dR>qsEy$wx( zxj$UbtM_7sS?1%>b4yP&w>=xb466XAKbk%`(Bbi`r)}#@q@}&4ahF1jlpWz|)trDfFdKk+L~YW^ zzg)%q@E)o>h?~J2m^C06-sXgrqKFeI>(d%A}Zc;#x zP9bIs4G8hJqs{NgSo^gxaVA+)erP&;QGw2O6<0HHA~Hzh2_iR8+aN8 znR-~Q1)_aFvze$8NcIES5^I7Kr0|P%&qX!p<`kgaS`9$%R3Ml4y%uGWk!ur3k3*Bd zca~XJX;jhKoxTU(%c>-<7pkC{4H)g77ot9d!EPVkZZ&Vjn+O7BFEOvK&4z=A* zp}NT9e{>NA><3O_dRi)mwQ_-#)dHZ6+`9p{kpRK#Ap_rQhq8zzCh3@2E(b?#`d6zT z`Use0C02i=3r~)S7T-`L|l-L)>m)+dw)8_xVl>jRKqP;X~!}eS$%{le%6Hs(6E6l0+zlS0rO6m=5^0D9Eyw zrr2vz8!6t5jAeTBR#-nwn$;$O-vE@a*U|EU4yS@X&!DfoX3)_ErW3j!OVE9JmMV#cxXYc_Xisn|g6_`;p)GFMz2c$3@rq`>B)(+p z63uCyp161H4d5Z+650mGHO<_cYEgNo5WRk@@WRKNd)hY$<&0prx%s|9HolFtR6Z$8+iP(<6gG!N{tku9?!R-JP@KszmeR zY;?}P4QBOg(w$031@zJ@kQ*JB4P--Fuh#ok^|V(dJaRY)( ziR?OUN%Xr6>FdB}@v`ItjfJNmxO!!L!JR5vK{-JAebe`D4E1~~S(D^MlKVZvGH*Ri z&KcJI%x3%F%EguT-C^6UZgrIB8~agc_1KZe7=7F*vYF$OUd029zL)_`!m7_+XOXL; zT)Fyi<-(?4HQe`*Uw6|zn(5}dz~M?~l))sDaA1!b3zMr#6#5R^V?j@G= z|9T^|ixmGS?2?z1I(MRW{a~oWiL}7z#lyijjlNf4$tjRx*0s_=&yEmm(An7vEX{HK zek2g8zw7rQ<70n08=ZU254Y{WICXat=!>M_A8m{=>+YBaDjf!QrshYky^BKJYZ(2R zt?0lmJ}nn0sAY?eU`NNwb(A9K=1*Eh6mra|23Wg>lA=&cjN-7xOjz|1bmvFv=Aw0X~bgKhYV@b@cd2@>NU zWNr`~GZ8tf_nV$=1Kc*!uY!p&rmG9j?w_S2k^oKQaqxuH5O(V#FOJKc7>_4}fKgN< zWqp-Wi!h;6e1a5F9Y{WSmRkt^iT@|?!W?K(K<`A`2gqxr2}38Dea-212`_1?#>oaB zarYYT$9rV9^tCfiUc>0o?cV?t2I({ke{t=u^Bg<@WJ99P2+JqdX%8&@pPBQ7TH;*l zSwg*!ms=F~k0BSBfoixRgipV>{X_3c`*`qcQCGf$rK&JiAv zBqIt`EcQ>jWz`{)aQ%0w^45I!UWC8;C`;lQ76Bsr7NOKY&{Z}VX5CgZ&JHFRT)7cC zFq9l--mT5QT~DUPl*yT-U{lx7Xa6*({;h|UUiUvx@QGSw+iALGwSbm=%Su@%&@Ff5 zazrlI%*QqqxCP(B%j#X|ItAd3)ox#K5K`V0oVge6tS8a3yGh~p0%7EyT}M2sk+eaT zAJi^iZ-S=o5ch-@5Y41+3hGA<0wpnFRNc4lt!Zvvpt`YH)P=ztpECn~_cpW^yj`4i zlFb6z8ps>EV!X&o%<}Z~75?;)K?*tgzJ+0;&(CNsbPIH7s0ZgIbF^F!Z*8aLZc2D5 zlqQ#94v~1k4`=h@4$ka}NGvSZs7YphlUVh@lh|37%_&RR?~pvqHxVVVin}R}XRSwP zKX<=Oa(6ql&=GnhZ4pObzLgfmZ#xD0**tRFyUNk&UAF2r<`K9gttRTUFI9c&z{HcV za%*Jmr>A%+T4jG<4ErYfrZf$cH;%QZmhTI0t2lj=GCcj$AI~7r@%@#P>M`sCk2M+9Io~jP2!40r;zcU{kMaZ%SNV_OCZnGQsYgi8d|V~G*Mnz^ z6$nq$0{{TN(C;F*L8*21<49EJVOw%)4gQA|abY#V5D3iGHoC^$%5JN(h!oblmGM&K zLlx_$)CNocR~bw4vxbC05O*Z`KOc-LWv1b2je@AuK7Jv7`JsR>v6yTGGVi3K1*O`< zy7UDNQgqLa5KWLG)jVo3O5GICSqv$(-pi?~MxJe^d~Ge1UYHZ`Jkf5Ud%v{Gy`7H} z=qkk=_NnpvV`Ul0)oF*f&c{O+Po}-tR4v@3M83an@8IK(Cb&CoClUT!}_i>0Xsq} zryj<_&aayuJTSn^qEZTXA>YR|EqGK;7h!;o;o$8i-QQSs9+p&s?a9(qt3MI&q|55c z^GdNUaskwp-*!pM!PYHkY`1aw$R=oa(q%^ondf)tyxQ8KX{DD;Sg-aC$J1y*t{Q+~ z)rgDPBV&gE$>LB}ErnUR=9fS`zZ>x5IMA&DEwKM>YcQbDy4 zq3ejQWXF$Tkr;b7LjCc6-MM>HOK(~^>T_akiobHQU6s^Aw0RbnSasoDHK0VXRdmhA=1V@w8%wF)buyS@DCWKFMPo`Epc~#uVVJhn6)0Fm9ej zV~z)cm~4k*+hh4A74vVrGZ~h*ZlrD$y{Ge!YXTntO{d50biDZnC!iv#1z9S(lkQ#* zI(f5b^CubDq}vUa*Ljw`;$ebE?-#Dlp#1_@}^s-FtUPfCRx!a3-8f(H0{LxtGt3`FjZSOAS&*++l-^@D9 z^}C&WF`=P6wI!+H12bn^Ej7s;+8LA_xS*7B5$v!lE_*+ z%w)Nt&^_pHv{dN0PFRcGZsb$jV=5o^e|$>Bbgw>|H^e2*{>;_fL;Fj=H8Uo;|DRgartld{pqwi{F<<8abon&<+87@)AcgqR;jntA?p4cc=2ZtaQXTLNV~SP z{3eS-Rw;9y0T!!bW;_P*Dy`0CIWf;z<>&JurYIIdv7z-2k)XJGgOLM!;{zQr)z_Td zl!D5(h}^HHaJ5PnUx!Le!se~{U(}8D60phz$swS<*HD9xQ4rPngQtK_N^c@s0u4c( ze|B*WXfmFlYxJ9gLRRmJ1rZX8xAPDoiNe_Gbu!EZ^kzllSW3Z(Cbrhrig$)|-;aCR zK(g_k4M4T{;aoz$eU*ZXiJzrR*9E&jSJwsRyO{c5>2NhJ@O)&G9@Sr7t1p!J6&{Ay zqijcnKX;Sb$cv;!#-S-g2`laWq5K=De>`y;>)vl!KD43V6T$+(>rRen)jOqTDXu&_LJLreRK&jqIm3FWXs`euRq@*@oX9$kWiAJWA7I^qZyhi@mK0z*l_-b=2!s8 zp(*(P|M-5PhRrlNkFm0WHL-wmviSp+MllIA&k^WpknJhZ5wqY)c2iq`+Rcih?eDv9 zFWuXz#n8#O;LmnB9hPw|hpqeoKzV>diEoF}YXGxm(-D zIBoV;XO2P!pZZMEv`Bt?dt{Hy34O9(wMF=v>a&lIaPU=Wdz`0CbUgxkNBKy#qUGB~ z1@P;rJF;;vE1T#%Tqb8-DRI1$ZlJV*q-i?rpK>eOPh2WMZXcFu4>o%S$5?IaX&;F( zT*s<=wwW;#1aT7Rll-%68T-ji%)ftTrNA0-UQs`hX2OBQmeWPWovhBtUJ3XzkuzflUyPeUyTXf+!0%E^t ze|Vr0KlrhqCbR|Ew}K7mjnDZoGR^OJ0~XUgi~F~&LtW<@N>BmD$fufW`|8Sf>@q2h6b< zQeHUi`pzlDBM4*NIAK_Io`U3gN=Tlk@^>T6+VsUPzMaKxr@CG9zubf;zQ*V@JuKlT zeYt+9dXwYs=_?2p?>7BA-Vm<+A3a$A0WaVuzc`8;;zZO$34n4&p@lCz!P9e=kuTA@ z^<7T-E#=s+V@Y)Cu0+@L8OP&(vQ#J3>-u-fm7({Uq5u~&_eixk#!#gRH7hGyIFpMa z;ScJtFudCwe&=I4NJaP?&{-pWuT1u-;3Z@pexw2n#d6(^K0xiDDgq#-cu^$ol-7?m zV!a04B^zw@$L&=MI8F%<;xTgb?0Sqh_K~hjqqsa|H7m&Ham&LV9mSf(`y5X>27-C+ zxXxW0Z5(q5kgvRpd3iDcpe!K6bKg^<5?3+Zmu`p2&U&=`7;)Qf9fU@4q=#nnJ(C(hS7*oHK6cw3N8LoKBZ;m~a!^#bR zom_K>l1HCFXQHw`4#%-3IO#P1Cnfte3W!Z+`LDBdFY7W&+W}%sT zrx3Yhw)%MJ*@fVBpy)iRczLh?;wgx{QNk0yAFZh7O`{5VC6w4Y$pFa#1uWhyw$(iN zoq~n9l-sR}KXwTyU%*2YT=4!WLArP~jWZ0wU7XyjOi|y8fZ%xCNA$urSGlgj($+qzcLLsV$h z{P-iLgy*;xfzx=uA#W;A*?lbVIhydIP3z{9rE!%59-Sh8!J|tbAn5F>z0_&I%GS-^ zoy29U>dJ!&DZXjlH1bBw!lo{7O^f{Y{Qn{NvS)Fvvt$-fJ$}SA-C#ZjL@EVBbGJU{ z(gwRyLI_hi#8FE9Ea#6V}MRq0D!zWy$L5eRQ`7e??M&S<`6%+K@XDF~^y z{qptCTX@6Odky%A0lx~a_+xFVpcF>NPV@)@x$g;IeQwDlcW#~AlpXcL-y{l zL&>sRF9wUHLWj1I z4)qEx{Tat$(xtI;PG$6;9smKw!}WR*GTkHh^iC3$tg$(7(KO~+gMKly4ci*-t~uu3 zFud{kB$=E$$3kxtB8or(+Zs|9f8$&M-`4^CW{^i!B0fN{5mOj81`x2OI`>xJKfn}i!WBDcnz8)_pb zBow)ccVd1)H$J_!Wai{5ZfL(;3~_T&0{HLUiR#SnFey;Do(wSBSX%#Sm0z6+kjB^p z`&E`)kj8kjY1r?-wwQ_%XX|506V0z!jN!gpPn2XXqBh$_i5sa-p8h576dQruyM#xE zuW~&J_rE+p`uPPZXt|Q;u~UZx?bj^2>vGa`<+L}y9JA@wYvsE|bVY6ifDEeouk9nN z)^7)H`6H`XQ@OT^{En(vH;D$_)BrV%2-;T#DOGjt<07-q5(S&1-H+XF&H zr`eYVNB4hje=<+TjK}osJeBrZomB73vpFwF8cgPDYylF~3+gNC{FMa9G(t(neoG`x z#2g4vH_RYH7s!=j@P|Rv;eL`1Bt^_I>t0})@~rG-M)?_qm40W*pI?t{DtxssxaCA( zsQM9@eG5mpBP%c@66bOWnGX4m~`buhq zo$#nWpmGb{JwF<5{=@lu21`(kd`Z^V=5MM&l3*XtRgfl8&P?^~y>zCSVw|r#TdI%> z^8RG!PvZ&Mj!7-tPeB`K<#ADge)WS3gltEF2&IazK@_}BmL_X-NnHlax48hQN(7*4 z0s6qQ)9`4i%b>|=cGUTpqtJ5=$(+#u;R(_PP8ib~#wWptzI zGcFSzYNBXF{9~;gD=J?p_O)eQd}j+vz6Oz4+5FjUAl)Yb=S+swGJxoMN|4T%duC>JL zI?3u-x{IIk?UU1W?HP)DSBWF)G z0Ryh%(1KUnv~`lTR{#at_T?@7Vk{X7;E|Im^EiJK*m$|2q-0nMiRp#tn|~=D1shgc zo3KjR1gv-z@85$Jk0ZCIOY!c#O!|Q)Z#=D{5+%B(1)qfZr8T}tSH2>XnX^}@_(W~@ z*P`A6XwVDgKe;6P#MigSOu`w*zHp>rD{1lZSG8Zmu+E+x9KR?uY9Z-?ToQz>T`h@& z{!j1QY>)BP zJOBAkSaQxyobo(|OI(}&O=0f7a8}{_vkS-Sxe$?yB2dru96{VF1&Ukg`k0|_O^MO! z9skJrwc5;9Ae+f%63x3Gz)b$q1_k}zAXu!0#1A<1woY~2oQ>e-%mtdW(Pe;o_dr|0 zrC)dIF9~XE`q%`e7L;S>C?V-wuK)2B9B%VG`hUF>p0>sNDG;5*i=h0|oVj7BB-P5B zDY5`qL(;;bjoU`})b`QpK9WXY;J*ERpm>Pefad&+GvWRPS5C!(NOT-K3$+iV_J3+gL#u)}5%ljQ;hr;>gY2rDw4RieO?%? z6ZJt*tF}%ra>@A1vj6*?pd5`4?FkVPJVK`19Rkqrb4EWI$XmHXEITQc*d7Q;C}gS+ z@>b4xKkb82!~uMeqxw3!X)KzM7+?a^0Tt^nu0jd8zsW?h7{b$hq1DNd79d*gH6HAA z9y-4J#Jr@#pW#5_zq*HC#XqUG>Iiq)Qw1OrzNXbAO?;U9baz}U9`#%(lNhu%PW2}8 zke^Syr1@MNM@59l$y;g6qjhM(YnJ37N#pS!x?_1ND$aUA>bwAOkG~Yz8YXbqJjsY=D$OV)g(l=~4&77OT z2QXB|j7qDtfI*T;#V>CF*`GXfL^xqxsu zRE64l@q0H6VNSFSQW&2yvg@Za0!h< zf_zE^7i{2Uw9#GUA)f2dv0-5Rn8~l1WQ94Gl~R(((IP{;b&1=KVf@sa&RlRZ_m9Xi zK{|Rz!sC^^{h-V+>yJ-nX@urW6Id4h&iX{EwXbn?w*p63C7(pfgG8c`0-St{h~IA< zW!ZTGeT5b970iX@Qrlm37eC@_KDXqo`4s8lL=PBBrF*?lY5v%TL10j`i7^^(r(?|8 zntToS*Zvn_RJ`p!=FfFQW*;L?`_O!Bw--jwI41lyIOjPlEva9h&FX}Lq3c&nyN+WD zzdfa}Tx!_@f__J~yB2)2z0Eq>B~OhYj(rnwYXzq@<>Krg3u`3l%l|?ZzHci3>v0_LA$f7Asx})W8;pNcLZA3F_-8t%Hztk8c(& zx^v($P~n}e2XVi+u?@dc*Jj99ertZ8Y7}}E=GB{zMu4?Mg5kith_AEF#p!(u&)ZzR zvjC^y(ifOx)7w@lTF385*3WbxUAIftEA_jnAGj@&G`R<>)0^AVivnDQ;ca#FVM@N! zU1__#G0qqYSOtf_sa&jG^3B=tl*id2=VCCgYEE43Z)*MBHUdXL#`N)1$*F8$`TYO~ z3m0N3v?KE#x?w;qqd?C-%?m@mXFw$T&daZFf=^!}voG%_&MKe2aVc~_=U=@%kh&rq zSWBYU5ow|Sw3hw>WiLZe_LkKgb>lDHK@g^z0?#ao{rDZ+1{UmfI$0E1PHv#xhyhOl zZ+|?D=7E^y8Pt#X*k1x9I~5pw!6De*Py!JsP5=|b0YntAwWrDo2fyW<(KZSt5 zPhp&Tn^}w$1QLGk$e>)igl>o_5P?pBvVltEpU1Af-6~Gz7W8RoV2A-R)Bmw9k|P&V zhRs`XlM1bpK;!P#g7Clw8Jq(BOYA_BDqfnf$+a>C|&5nds4i zM}AXHk)5YpWjO9}gShiGZ_1Q?MHrDP`4eq4yl9`X6<}P@BJk+X+|A+=lv;nc_UL3? zqWN2w-lFPKAn4gQ+Wn7`N{V6>TIJ^Rf$n6nK3TjN95qV)zjVXLg66`5pePpVZ5z|& zS`00{LeLy&&+kLfHXu%+O&(UA{s7n78EQE4q1fJUJbntH#DAT8z!fEOK*k1?{)9CS zRAG8%H53EBD@U;cJ^ZKc0IK)z7a=d6u*2{=p>bf-#eswYrg)bY_5Sqe_Z43AM;}ab zUl;(}C|c0#waHzO3?bph$FM0}_FtR!*snwKIQAfG24V;9mmx)3_g;OY5&U|&CilRo z7khZ5kzkEzvzj61`L}qC5rCU`_trhoNRU_r2`4L%=obye1<`Vsm7eRI>=-~Kd4pz4 z^5gA(PGOKfMzJ$rRQ+WPN`-V8KZxPESx-2#a<~ooSIh7;TG4d3SY4`L`!H}fn!EyS zQ##P%?v13uvQ`6Xuc$?bPF7&JGbpubL{yo`duo#ol-C#zoKY|~yB|hx`fv?#B zzV;mEwz2?>b0%9}56*P}pY(*fu|cMjFJJ)_4r)^C_ERW-?ij;n*Sq@BDdH~Ya8`R- zEnEX?-y#)2U9fHyvv&a=9WYiXu!e)4$7ussBJpsCbJx-Fv=#z(fQ6?H0?|-TM+99K z&wl@n1mgs+#8bQE`sP0)Qjj3SHG?6DY0*#(?N@aHMmFXt>K9fD+z#I^Y_|9~U)q^Vqdx z={DW&JVve{h*r!F(Qbt{*5NcRD6m#@2521PSJHUkh+5Ln;Uci^F3V6#C)=TO$N2na z6Toc!7wvB9_W{~#5Jq-4#^Bp@ANr4~v;xqD4&n# z!m~J|rA_83J~R+Yv}=A-8d@>W6xgq!8+U%B3g~_B(HO9!O;?DhwqqTeQpjG%<~*PK9F6bq_q(3w`R{vOo$GtK?)(0{ z-|yFYe?IHTBIG}Fz>mIt*i8rri1?bv{%bI>p4)@w)d6!oZ>v%+K!ZaJHmcCf5Wwgb z^AB$u?%QVL2m-b4iU=V6yz~VzIbJ2z0Sa&Thgb+lfWraoZEqX+$rq4(FQ}Z(>!0GUe`1t3WR%&vF-KMuyl7^qSA2N^iwgFEDtKdLd6cvr}*lICnjysy6G zDAI7X^;A>tX1xoUtJ&$Fx6hwz{ z{Wvx4{VjL?d4vt|T-5fnz%jcmG$W55;3Z*XAw>%MIgP?^Lit3mGbNu+D_J;G$Uy$# zJMewP7p zy1oQ8>CrilpLX1imFu&9uySDIG#84<>5`fWunNFi0zU<1t4%@jZdrX37Y{qa=QM`fph=<1j#waS^1+K65$U2xxy z=hcz!)u=g<_u47_l_UqoRVH(D13+n}`KM~md(9qsgAmVQ+hjj@1CRA2TQ90#oEu~! zKhLki+-@U~3kk4;&hMA;3yx`O*l! zG=rDSE3B>@P)w#APY$Nmx>5q)gwY@v2*RRr4n%4ad3hA_3!-Z)Gy*WbYEqj28 z7+mshId7fhXmXTnoqvr1qQnK29~Fao)Z1KHK&v4@n{-NilFcIeI-~{3iV|g?GixUd z`+b+&M+;dV+3ei{Gi+W0jsM-6VGGfW{szVn#Jg?i6||SHX%7o1E_{Z1gInV<&H07- z%~eg9+?LJIUhi?VwHx4>T$;^yI=w71UaJc>I5+zp_})%7^8NiF)#Q7Ew?j}GpXf!F zZ2euT(3SKw9SKN+%EHTv^>5#SUp<|Bzh!Aaz5r9r{_v`gUhGu3IflJhV+v;p*o^FU zU~2x@I$q+^@twf@J+FOufAs5_=P!DWCpiLtCedI1R$+Y4yH6I=yOHttP~2V>HUgX- zRz zZD3e791e=xeKqeAHt4Yny_Z&{QCGwD8cp}Aq&;?2rHHRhU)+U5Q3{<)KTTC?zaHx4 zk6f_DCYMr5tf2w0cHa%OaWKO-k~8DpMVt(rH_^FV^Uk}}+`aYT@zvi969LtYd0Q-< zt~EOH)-;-NSDwj6`I8A`3w)>Wn6rV-mG_TK^pF~nt|;fbdKoekP(ezac$C%9m%bYi z0B+OFzHG<4R=ckgjlot;{?;FIEQ~-aJqQvJcucni+6Fn3n*`rkHtx6>2MA86{;q2& z_RH4#)TB+0P_W-aGnGlbj{P%r*xMS@$`ui zu)-1rj3hh2d+aC0fUyY^eVhP9FoYRkReeAh*fC9isDb?ez(GS~m4FNIc2*`N7a6Gp zF9xQJhzZ6N1oVbKpoRW2{$$edM*i2|DAp*n`S6_&wJkE_u+&k2%K-49Eo=btYD_~Z zl`UYLsZA1dFf=iS#9R}IjQOx{UxKD7R_NC$W4k|hfj0mRU|tvc!{t9Az5n}T&oBJUO-dA} zX&!0pS0B#jeGSS4xS$pAPxT2mgh5TgwYSJT`ue3Pa^SFhW%j|aQK4BxQ%}`n!L!G2 zPo3tvPv6-gva17Ejupm#F7W8E>>XEc_k6V89WOQV26!$P$TyboEal&Kf2~jYE30;L zf?A_?Ci79K$r(cPwf*rx5mty<_STFa_PmfVZ&sRhSV%R6)ji}PQDOK}+rlVFlH#-M znqF5>f>k2Nst4^QVa#gPVCS>4pPO3LPS_14V3$Sl&)>C5v}UOsq{;Ax0GpBd zH+oyG5=A+Pw73kMu^rlaTbHb(9j}}bdV0yEO|vKC zqu-HTu=8M1Cb;(Dk2QIbX6iidKi*epLamY`eE4H*hx7`b&c5-nJl!2zw+G@zS(a(k z-9mKI*de=L8j76yBRf#FfsC%z*f=70(fggIjG|~Of~-os@6&-nDft=lkhMtFh2DzI z(HkQm>`uL1xj8p~ViMk~w<8MwY=zf8#=X<`z^OuSeM)t{a96J3ZZP+bUy*~&jRnD= z7qi>qUAI;c!*0#3aRC^{EW?UTe}tbigU41nFmap*RO{H;DpBOp~@}nTXwA!IhF)hfKO2G%FWt8Bb@uCNq&XnlpV65j0r{Wnl&mU>#Iuf6uMIAH4r~!u}5yfzW*=pJJ1W z`1}0*H;5pgO#|iW%hB@6JPHUrq>e>HGw^$&^f#R;TyLCm`KIa3!T0Cj>PSe-=1pfv z;?6Jpa@$KL2*C)91mqdKueAH?j}Iss6nncJ%15*aCkV2h7aYp%<^n`tCcqGElb6y! z)(0p(tSS;EgU+jKNM2S?T(o`JE_|CZ$f=Imnb#=XmZ8ra@O)oRCF962=;g^lBQ+j?1Ni$S&8kC69f8CEoHoc-fK!n6YxgHxWGZlugOG)E(vtK9{d0l>2Tf1^ zbC@-msX+@wK!iA{J^IeRG6c|dh-FCCJAf)~E#2gi2}8~i;$bMhdEhd5F>pZjs?D)% z8s+js@7Dx`$6Upn!onkI9sPEjSuk#=&;@E;ooWF&$5K`W*4?@ThVvRm8-dMUrF{F% zJb~(H+j)3?mR~yS$UNhWK?F78)}sOsgMmyB>unJ)_UjAR@GHZg@hfH9qI-pApZmr^ zC{lmPh6^LP^{x!;Dlq9t#?Wlo91-}*B@RMI-l-_j!NcK4w^7b^1V`c@hsKD0dn z?-BTkxt9e{f4li(y#l_;z&xU&tqTA2M>^%%BuaJ`w-JC1L_uueKrWxcGkMhoj;Ol>ax({* z1X|{k8B5K7_MM~bkxRRd86~O{=Tg+aoMcNfLj+y@u`aRu#7VLZyc0gq)>mcmzCQ=O zPf(QfRC18?R$n>Rw_t||Ep%jagxkWXqALaO>bi#6e&7PQ<|+ zGq(Ftv`nlySiWReb^e!zPj!a(iG}*Qy5xujB+(Y7liFWsQawHr^`*}#wgY`|BC zjuur^4$VecI)wA&y)7{_{rXGAUD`S3IzAVG6qfRzHS^68`WB4L_qh&!rE>FX6(!pM%e^&uLdvkc3I^8}NILkH}JPsm|#PuG!U5*#6NL=S~fjx5ZH029c zXgLuD!v%VZ-)aL3;SkUt39+BH`|U?&(S=?EWQ29Cf)S5^1qj*?cDf#_08pDK4?$-A zN7h|$N%EjWkZcnGh`+#If>B@buv?S`P)cVxFZVoJJZ|BcP^!oNyKm5LWryu0&IfaY z*vS;U28GqdA{Iq}yuXv}S@_u3P|VrDPX_dkFfGcm;0`}U-?08YwyKb@hzig&XyBWq zLG&NyxJjS5zvIGtHkR=SYIwwJyc76o+OC$I`6vJ!0vg#TOJqdK!%>lApYm9oy&+av zw$_DJv@-R7|s!`DZN_P$!S9TG+X<W$Ef!Lh-#3N*8g^9SEo=i@AOQIQJU}%xyT^ zWcU8WyDr36%sg{I*O=f##X`PcG$qLfD6K1wNvNzCBTNWN@EF09AH#>)7a2 zo9p-{Gb7^F*E`E1iiz0Ew|_N*-5-j8j=k4pbrMqTq|;4nIg$cIQ#u6=t&a&SC7VB~ zU1ETJAOV`4gyJpT|6)IIB4P+9A{Z3p)-4IjJam#>)_l;um439xXj3Ic_0Ah!J*_w|SvFw7}&i9pSs!`3<**_om} zDJXa*_2F+8u;8PVe_ZVV>{e@%^!oYoq;%&{0te_FaQyw6pzz7krjOCev>%K!B3^vV z&q%!F|6~Ohzu{>XBno_zBfuxwzWZEWtn>}|?t@QI2I$pD8l9LGyvZeXb5q32&$^Og zv0{Re&t27dtqxu=HD+H=i@2^UhL9$f| zO17E;FESX6Fktna)cr>yo3P|RZ9x?&vWj9SVC`JZYR8^VF^cTyKogK{Bx=(ro$^h} zZ!aO|@x!LSfbaKL&pWIHSUw|bV7a6$77gd~CbUtgged+8V5h7{SRLj(3{qR30s;m; z0o9T@OI1}v)sTh+EM?G4^l-dfBJ?lG*I!Cdrwdv3JHgjr^WlHU?^_c%+zCilx@>DR zgdLCGdjB=+u`%u38U9-SvIvEg2;+4v*7HJR0)1SMD!ho#e03|Y0q-A6Qd|VO<%TMd zeb(!ky*(-fQ!ck983vx92vn~jt19g?c_H;8SV%=8Wjl!F1{h=)j)4g^-)Aow&%28} z@Yp5?9r0oLgQhbJB608b$6pJWr}olnCo-TeKt}Urq8Wihhh0nD38)GY;BuSfo|CH@ z)jqg|yEHXJi@6L0QQ0FR4d6eUX5yH;zB`81$+?-hu2SY4bFGNJ16%3bhU2P3C(BTCXXe;s2r<#g&-6_t-zJMqcKtki9}>f7?J)&HULdYyr;reXOZg7BFW z4D`C=rL}A%*dco@Gj5WtSL7X+t=LwJCt`hlAlVc8*8jY)lrh8v8$@rcKG>%?djA30 zjha1~AMZITD@{YhQufPk>YSO87n;Iby|1(@)C;bdfA@9ZlSv8E2!(?J>Rb9ntci#Z zY758(Gt@E|je9$~RI2y;;HM z3}?naBi)>wTQJ)^1I3BeVK4C;eCXu_{FG;NUSZ{A!XxU7x-M*)p{-W=NNe06nsdhQF1^a?KPow-E-fxf3Ik zdT(N2pyI%AX~!4hDSV` zMhIToF9~%cP)P1bkJ;D9ohg>^sNvd*gZ&<+U^G>G8{lpKjt~q=e0cZn+n}NW=i-XZ z8=uP!2GXki_Ml0~5j$VmCfI@O_d=S;=3LDZM9p5iHG72fWN+h@V`v^ek2cw|Ny&L& zSbAcZK`TDc{*;X8pBpSR!VS0U3;ADYMuF ztt~F^)^HfMTQlsvR4rm4SFWO(V<_LsnvZdRGHykq-TVqh5mZeipqS7asvjRyl!*(t zq_7J3nSaktc}N37bAaalaMC6FedQ+0$Hx;ErihR#wT8pJ?@;I$F6PLW+t*w4h2M<8!k(=asV zT`zOsH^#mR-bx0rkf(AgQY;8z6UNxK!J-v{R86U?Gn4s8h9K}8oOi@iUIGX0pS~=! zUXPIMRtK)W{E!DIhR>iW2thd7BP`rf@EJ$jYute&mwXWX?)Yy-Qv~HfYFo1+L;%{6 zMn$40G)Pi*3C8}l$5%ZCw1+@FP^Zc^Se||lJJA!1UpCV!u#yBQh(IU{;rFp`;_$lJleNCzu2$Nk!{tE1!Bguf^+j z>~=YBaa(WWel!O&BKlvyAoFEm^R-GSZ?h2K)t>}@flL6Ke{uL|+^x$`!pWPzR}hzZ zy0hlnL_p2Q|MmzKOz)Oa2$Yr(g=A{SoGIB5G3bR9LlOdb)cab$ z=(G3|3UX|V=IIq>NMJsoYJld1HsDCyKTN5#?<4<4coUR4|2-lhy>$lg*vs{Sh5rso z|3S|IIMl0Xd70S|Mtw|onVf031_mt{Xy|k!q(lBPn2+|`g&ZFdH?TMP?Zw)1LsQXS zPEZ+$sVwlE-i@Z4dR~a~{uTJyJkqGa+bCj@B=4TngDbgAB;0)gULowE?8|U`F=!&R&uQ)0zqW(|7 ze6D5izKK0p2Wh&lS|A2Tp=nhrqXQ}zW+ZP#Z>pY#CY+1Tq>6tm|2d+ zZ#@(vy`Q!wZG4hOJcACzFaCW0wc`EcJ5A&FruA2WF#vr3_xEsk7X7?EI6kw%{^lmW zL-1!RiT=v$Tq?;EY5k4Bb!X4TUK(D(rmNvGWGT}R&w(jPx;@Zvg*m%0%p!eroRym- zv;^yve|=nNQg;9`F{{Ko{jE8OarYq`1P?O!_!F2$eMp-#@eJDcjFy#!jOa17?5Y;p zh6qNHJ3{yP?RM5o!BN;c%10h>Ev5hPT9Q9?=+}6?YWG_Rdef;p>eArm=0N zII2F)$v`O(Z?AwpadGZ2<2FJEAjcAq!H}xp3P_N8MSc3QN*zqTLJLrIH3~&p-qZlS z3wm7wNP_}I4FZ634MHt(Ky-+)JSay5zLhSDG5e29O&n0SV7IczTxUa0>0jK5b z5bq5I=pf`az033S7L=zV>6V(vrJTcH0WZA%u&0Q5#6gI-kbM4JITrhM>hRF1L}UGTp8PwE0IV=z_>1XRS(IuZxP-dQ1OVTLeFn%oDI@J0hMQ0g+dYO z)kwjo;^$U6A$>yeTxjDhh{qFI^UD)3@7V*h6Yk1u?N90j$Y3L|eBR>#RyrkOoH)AL zF;C`Nk%4{KEkx4dfYdBQ<$4&yt?Ac}*=r=`SJ2OUEeWG*=n2Xo&=Mvj`COhl*Hbls zc7U*NTG=ZuE8cx;D|(oF=x5sePQ;#3k=?HSTAii?gXbPQulO}Jd_xA*6buXur`$`j zr}3z0n9G%O6%6ez8xkb$*Txj+;#S(W-mdKOkqJ*dw9H@KN**9Hf=NWcKreVTrD|VX zL>yVu`eS|_wA)F*+C+-trR99ZeRESDNxgDmB7FEavBmKx*VGD7Xv+}Nf4+TtF!n3n zo%~@3t)ZELzUb{1$3yV=h!$wonE_d?(W-VeWtGraGyE6c+ze4}AhzR&<%g9z#;qg>)}^t%Vq^pA z`7|U#tM{M<%e=R#IweJJvSf2q{`eSunA5n2!^sgns!t-h%4*g}lW>M-@cts$BpZS6 z2ivdzKK`0Zs`Z={O^2!0Te>)%+}E2XZ!{%^%rKQ-+=*gAHzqBd@LWpbe!}hcgd`64 z#@Pw!ws4O5#PJ^dr;JCuZfpgXedV9PlTQavp8gz)fs5Kd{$L;QdPf4M#$W}$$IKc3 zh@0$y-krOhSk5GMH)-X}I9XoGfgrZ~k}|wMx z;;Q;jC;knNB|~-(=49jQ*g)=_cdY@Sh9rcTrU+zHf)pId(8YkO%P#u2gmdm@k=eGo zpG#`E;0YF@vkTMOhfqsaxl1Nt6hkQKC+On{^5{jV5FH6wS-u94L$9O1)E@&MG!?O1 zL^J%DJ{ZNC4fgyd!_JsD39u4mpZYypCtd6=Bl6fRwc6jIs8f<0P^lNzWu6{kN}A_s zC6$m^X-st}x;}9gGD&P?bIqWi_J=bw!V8|~WHN4(2vba3Tak4gGYq!~l%#!s>c~8; z$Bmt4n@#ngt2ruhxb1~$eSAcG{?`t_*rzAishGE}P*n?vh-KecSJd8db- zf*cu4$~q4)6JKSLKDk{Q0LJcmV2#|RYCj0RVEK5l5V30{&@%<;wtcPCHNJ%Y+sw)v zj9DC5?r`CBLa!P5R*TRWY$KAroXl>q3Z)IC1<**UgPHBw{>2V;1JCH8Mw6G6jZ zdp5#-Tl{-@33fiocMi%ll4n=y*>sNk%CN6B9738;>z)35C(|6xT301Wo`;O0kkgE! zeG5_BKZB+!?qBD>w`t`3?fR8`f8m)1hhG-dOqFU9<5~z zP*&X@l!gPvJA(q!t51jk#*R@6a(%Ju#%NKFMQ`4bW8jB-g8c#a<;GkRHEYOPuwan2 z4RI!^jXg}*lcJyk8DO)zuknQ$Q;(Saua59K#iiBZ6G;C!Hvi#JyD0^ZD7_wJ>EWR% zR-B))%TCy$1$vu03fc06WC+`*I>r{+2^#YYBY&~kj8du_t~Ifx+mh2G%r*tI@f&dF zwsb5~!O{;U(psn40c&-T*D{GVV(>BXe~~G+QfpjV`E2Pe??AECGs4=kjo2dCYQb7-Xys&+(D9 zEao|Tou1HwZ*1Fa>g`VbZkW=`2> zUX`jz?UF*!uJNQaWCKunmO^V|Npki*FmqV|Y{ObCrt%1q0$0N`P~>#j*Lq5fvdlkA zN$a8Zy<)J5a%gj}+V{xdZpbQTOp|_0H*8m+)`}b~nE~ACV`H_s%b#P4Qw=QPLKQ=l z0HxgoDD6ze6T~WdKC#jv1!9m8c>9snZ_A{&B+(u(4e6{-+{zxH>6rL_qK6{oGF@f} ztJ`rN*|WNJX7Ke7^T*wGtU25>xbS$+&wxY4H4vP8O9c@+1)RG#?!`hAkgEoLY>@>R zfkJzOmpN`vl*48m-7ky*k;hMhzj?-pJyVZR3AG zd{ktYzi}c_5fH&W7$2w=2ken*e!VD=n6vsK?$x26C0+LO6<0pd&pS}tWLApmDuoO*H)t4#J&>ejsss&4|( zVowN(5-vUm!j=rKopJk8oB7Hs+IpAJIi!vq-5I<8l%KnK4b_6mHQFBI7O4?4_0^La zH&#xURoLYc9y|?v2@UMSPZQ;v)$kGNZ}j?Bs?=pCd96{qH$u9bSCW!O(qY+W%6iaW z$|&*Qe-Z2$wb$+)aJR>!3bk})zv%0%`d=MjFo5C8&BX#dz?K;fuPtD|Ak3d4ZbGpq zOBgm|JY||*)*k7Iag}?=Uw;9{4SaYjSJnDC1a1q@&(y@VZDoi!-EDXp2Dl=^Gs3Vy zWe3pzKkWcp6b9%6%#>=#ddv(6TP=i6koMCa6PbWb+y z)2DY$A)D9g!S?ICt5qU%>?;98@tqi)lQMdOnRCXR+&4d3JUo-ubnwixlX&>Y0wMPY~U&f1U-q6nEG9uFuf1(0a0- zbPEwAJa>0&>S=ib&GaJ`jB-HEC2tm&z}Nuviz`Q zfJoshSheHunI%{?Sm;2Wk~NY6v3De<>plB*KUWOmB65LnNddwm%Sz}+GiCuDAAK+7 zqr>SxYRplY+b@M$ima%cgR9a7sMBZzro&k34=(>Xn67dYM zKjJ~>+0_)O|J8r3o!}6M%|?cG;8{;wK9?&xd8wEg0fDfS81N*Mi922c6yhz*M|J%c zNjx>>7!Je13ZD-16dA|g6KN{jLf=qq5@pM#DL@8_yc5E!T7*5jVa8xx6SEZ=AhZSI z&C7^XHn)(yxCw#E_Ind*k?8kGU)#*HnBC{kANL~gvgXUl~QM) zP$vi9R{pg#peVeg(UtxZfMG&0nkSobEH|O|5LiZ@`#Y<#4Zq#A> z362ar>kAP(t?I*#?VIEC^B^XtZ1zS^BvPn_ zVI}@)2}(lu-V9Tc*+fyV{+WfW4yFK<+?yX%1c~`X=~Ax`t37HS>uo-i+7wYW@*xvg zv&kVLQVx2U&#gWKttOy;yVVg$7w99vCW*BI%h|oiT81*?SX|FZ?Bg?M+LqxG6dEko z_erSx;LE8**oU|elfA44L?@W6HAioCm7A7!g3aKie@;VV0>cXyV9RUU%XQWblN8(^ zh$8dz!v&NXy-)c%*m%!GQ8ewo`q1~HNNVfrd<%Cx(NM9CoLO|k6SpN8TnsnWj_FPi z6wOWVM|T&vjC1Wb2UQMfGS;TWx^@?HV7jyB{zOl~hnRT@92QF+K@($5T#pADEL(%( zLJ(3wcq9Ay$!6Dl{dvUtwr;j-_-qp?oxY^|NUz3VgQY58yK~@HswGO_OyRO^Un3B$oGk<_7GJNtm4ly|VB&S<0t-WJiN{0d zC*UrEPy!CT2Rymu;LoIy;P$RD3f8f}Z_yW~*=_G4x}Q;ww?SL{tPtQ<5F)w*e&Px7 zqq;$83PeU8RQUdAosoh`2&D=2-%iPC(7by4YxrqlD3x!=?p7!%YgYijnE7{#stWSF zN&wOL)@7fP0k>)gT&hdIm;>ZUo`asKTbMqSJ0*(s^_R^d1+PSqfE zAQ{`x=cKy)PoO#UrRc{^e*K_nl~ELx_?(_h|4YDXSNv)fzzJt{%kTs9yPnT_7Zbg_ zdG&D;W?~quShsqNYpg7ik%64|au)Jt>j?jO{x{pNAwdV!CT~~8-(JWTNAdF! zi8`XgI+zLf9`Uji;}R91GFOOcg6Wb5Qr80NU0gWGv8ISPNL!1Uxo_KwILE#zAv$}I zWRKcQk6QS+V49{R-|C%bUYo9R$zf~@CnL+=S|Pv%Qj{`)M3Q{}9!i!Fgtoc?5^RA@ z3->?{`hcVPAFstDDQO$vTICGCcK!_RkGuH5{99lJyPSrW22w)L(Qs|$q0v4a0$97+ z=H7I1crLUe8w!tjG&hv;!JR(IKj$%#!G2B`Nl$EQsmd_zW;m4G+B4c~IgwdoInF`Z zQR|0ZKc_O^cJ147(3f9a-y8CrBji5MPJNm1tzCaV)=uiN*}exNDU>x(6j5?^)286- zwS)@EglfslIy{5j7zx{q=<3NSgEpi{&tS`a89&l&*57%%8&J`7+QgQ99^?dMZafr(p#Q_!R1=P5T(|R$zdgON$t>bP$GPwp1Cv8>b^h2CQ|55jy&&! zL(nZ78l|}Z8KtT_z-4zv_s)dM7pO~tyC$^QWwEChouya7CQ;M4iGw7z$+Ql2ws()> z=!%)8CMXunhi;Dz=)3>F9E}0Wv`omC_clr_HvdQFw0fuP?S@Aklr=N}znMY!4U0Ge z=%hPp@RNYQuncUPJwMMIIWT&0_~% zJDiM6?}6R~J3dWc2!2e1eX`H(ZK~wX#yoJ>gM}q-KaFhXmV2KZ@dAxP`kO4UI-{apgG2%BK%(L ztLW<8kP32$#S(t-d0t++wgeS&MnhT^RHgpm;@o_-c3Tm+fA9u*6TnvsB?1lbK954yJs z7JO``Eie^MmoYIk&_Qw?^_>VQ$_R-%ffmZQ&A-?e{}=s^`rXkFS9SQpx~ctoYTFjB;D?w8JRv+{WQpxIZ-?M*IBz8 z(#T1hJ@ShzN0bsqULzSJr;pdbny-xb?n}(D(#j-E*^eL;bBM8VJWo;cSq#>H6U0;~ z!3|uxL1H1-n1NUB!G1fy3eC){02`_o-Q&fC(({2^u27U}!FEV{40=UlC#`@}Lx7y{ zreLcg0PqoN+w+-K@5Mj`_@~y-15vDEv2Qj>2p&ixW$}kEazKJh)4}G+V}Clrt9ib! ztNLFSE=jFMaDucsw1eG)G1*?)0DoLi4adU zWE7G*Gel{&cSzrRJY2kQ@Il~5L-2XlhW~< zNkN*Vk@k$EXbIES>RzWY;U4t6=HmND_fJ%AaC-of!H+OHpUAeJShemAo8GUeTx&?V@z?WEcE_C;w2$ zVfV3dGyUzyL08vqZZ1|LbB4!f@pNtmCNU;D6Xy8Qlblj`3Zaqby)&>_)=N|T2`ENb zZPmhn{oJ=g*O909%-C z11Q`tkjmI_dLT#2GViKyj{Mkylg37s;qb*ui?9_bR@xT2tAg$S9E)c|7XKa#`HO>e z*hB*tnS#fN0@2B!JV;dgOQF_(a8|QAcvtMaEaK!!JxoW7RTo?F-zETFgxxHkEnQ?N z#9=h1I&II^t<^^|>@D>#Bae{Gh~~xdT3eWEy%kq;JehDW+I4q~f1rFRY+7Wc2RPg9 zc|^CmMqS+3BUOt=Gl#=>M|aky7HZn_%a4^|q;^a<9qBvlirc<&Wa2b6k+-lYWrdhc> zvGT*fn%J1coE4wL?FE*|F#AIDQH?=1mVrLG)q8^`%FF;gko(pp^p}EJ+%x6%xCn{< zq!7I-?k_iXHa(3nA@S?wKW-1g-TTNOU9;@-mC~A|ivtV0lKHYRCRQ_F(d+DCQj?)O zFftet^yGq?)ehf6fA8#GM`%~mJUUDQTv_nGc@D3_f*wB|9#d9P#@n}n^^&2xP*V=+jV|VNGXX1lFe4w3Y*00- zA`q&K)|&B_x@Yu)sz$$ckezoYUl9R;!;qw?JOi|hBbbG7&w%cBMS95PzoXEFpHGeT zxxSQ-!Lk^@)u#67$FnfFH&}8G5><4n3I#Te<U6qPy>#7}|n{d{QAsbngM8C119$ zQ9^NK${y)?&$(r->+>lXdh1?o9nJ0C zk#3wzVeS}m+A+H=SR;~ipo3W;w}%3vEhdD_Jc+c$*%{Fl7`RX^!;i9=cUoJnb>u#G zdYl>M@W|o*>_lSe7pAI6S0S<$Z#JbT?gG`IiRv(uWfjgL4A>lka8M6Mf*#BReUk}3 zV5Z;418d)t>u~`nlAMy%N0-5~21W|$$nAm#5h05Ie(WqHwWs|HGyhVk=X};AXdJj%50tWld5v}{ruddo+jLPA_{avyIGS$Evda}C}pcgY!6ydhKjR0xER}dGyX<5@oHgPSh35GUM}gF%ZF3D-ww1ZeD)Gxo zjen}=iySX71gAlR=~B74`EOAnO%xY!!B8X=+R1a5-$p*1=_JU4VAGV`l(~S0n>PYE z-^0O{FtkDoH>}r9bP6KitduRxrTcgo3z|P{cEHO{pB*fpp)RJSsLj7@#wr^MhvPW6!4e;`>k7&74v0 zgUl6Xl}pc;6Fp7Hn_FQ9?@?DaX<@yF(pCy@#WSu5o6q2Uh({z$+esLhR-*BH&lR0j zP@We@v>F&0@RDT}zij8+Jcgf$_I7w~j9%vvzg2+0tYr{e;fTTunNM%&5?(>!`wQ9p zH_Bz`j;C!NFhwbC-memhb6SSr z$XAQ3g!@A}PmAW^(OPi1pSCc)@MuD*!b?tIe1y~=nj=A^V2)kb>U8+u2SRf)XbFNM zG}dvBSGa{{-kn_`bydVcjB3iQ3ScwBt=X`eb@=TH*6>BUMnvY{DT_=>Xf&v)+!+N< zEm#5OX|>K3$XFE)_D&q*5w-Gm(VF2jXDT>%VxTuT?_(f61q>VtkKE(d^w9$8siysy z+!Zf@-S~s8{dG6NF(snO&E&nliL$t zI<@FlH)BmD>vuSNDCmZGsVTy&88{SlJx1lZoHG*zSt!v~1U;P1W?-n)KLJPLq-u~KRK1!q^lHY_{J>dFU zo;Sj*;VTm+$%F(u(ScXcB;2V-xW{Y3iCc1O1YF^&vKVF0Hz#G^W}=nCh>MjKinRaZ6~}uE_&0m)jmYk2s0bP>SPPOOI428$-OP54oqhja5J=wKzusF0lXm z>Etqs$9sT=C<4~X6K0LSX$W35ex66D6w3$QpZ%jho#M`j6V($U#QSLi&fT|}W_nO# z$w8zZMglK!ODm_5VzKD;TZk&0Wrg0jR+VHXz+KrZ^6~7_T`n#>l;44133X4%IRs^s zSB`a?wE*D76HpKJ-FBLaBY!?^rl14=$ER(nP^ds0FijC&V6UUTke&s|e#GYe$Ofbj z^t83dgK&kpf+d{WCjMCJzYkk#Oi`s+)wxn}v;mBBm#=I-bpBR$CI9Pd)LC3~4k=jy z<1%YO7T{w(TNs+}m+fCC9-~Z^6LGa>-!)LESMk z{oK{{Z#M1{6WwKwHhXjN;0E@sKa;6WMfU|@$4d$oCWy_ z`Tp`r02+ctXK03X(#`A zfsDyUnJ}==pO+vC|EBm0()!fX_PvM(zD{&yfV z7w3aoXNFx!6YMZ#Plq;W3&CX&Z8cORRr%(RAEEVeNeAY_6F!llvyJ9r4$?g6_>kpF z@ce0M_BTB=7dU!Pxa|<^cXCj4b9KwU45_zA7euS4O5R+|CZZ%eT>I<_M?oF1LGxTS z;B-}uC+1PFtHOJ92LTYtO6pJ08^+PAQrzx?(Ibr!ax z;0-A1WV5Q8ON!0t(lsW`sT{bs61s9_&SS4=)ChAI7$4>uoWrCXw_mIVLo&OACVsq# z$MP%s1dG;LaYzrFJe=6;&E*A9!ykV#(Qy9;lt(L}F7%cVyQ&PT5(!(Y86m-y=X2u9#YM z9`sPX@{v;-)gd8NfM6|W%r&XC7RUwt{}A@(;ZV1K`}o)sqU^gUOSWW7mLY3Jl#*;y zku_ULmN7~tBqW5a$&$5@-H=e(_p(QJ*#|RYX1>>Z)cv_X&-46_-@n!IZjNg?&+~P@ zma9$$@c_q^zEJC_1{9!aqzl^;1<;CLq3+0{p4gHNYFN6Ff|3%V*xzP8ZVk4Hc?ybB ztV+zKAh%{H+1|rVppC6|wOXJD^amU)a&hnEw53oCAyX{p@TvS1Q@xaUI~xobD@%6D zAe?{vx9Fi8>E#Z(w#}lyqxPQFBrNH53>EL{`GOR7#ew-xH-9T~T5y|Xylm@`xe=^% zRzurzFeAN?*Ea8Jd{UXu8~@rDW*1r_)xgrzpISqsCwoTHA}=)Lkf!}jsZlhrp-7&+ z>#dMCY5vP@!e7!HF6H%28F6%d-pi&$!L8oL;i6<`g@FkbtxG*+>Bw7J*OxI5($npK zS^Lh$8ZWp7w7W_Tn`j`n=f3&qpzo+QP<+_N-7ietq*Vu_+$$VYMXp#~y!4M+{2C~< zDGD%VarSd&M}0Aj93>7rj)AI%3aF~#OxpHWf5AJEUYq7nJ&3;1s_M|2^*C}o+RoaY za1vD5>Ufn`wY1E7{z(#ox)Q6CED@q07NzQiVRRz=nAB6rY1>%2{xaW!Sh6Q+zR%x1 z*T6h>r~%-ty&nGA@%_xLw^amA^Zuo^V}qk-ogZrzZ%glidPAm20)jZA?>(_YH4=J2 z0<=ID;T!V1!kJkXdmcC8Tx7~l6`YkwNVaYW&c4)R4@Q_b{l%Ok#%dt#3oUzJf$u>j zN4!MvYq7oUG+)wYZCiomLIk1ZM&vP({S{N@J;iNaq}<)3fXSf(;AUnjSo(v?Wy-NbRt zf*y=WVACXTvgbzVxi3=nTatYNXcyw!>E`(d^gjuCwzF-;w978l-#?;R3*I@%t*5LY zfq}3q7l(o7*LDY&nP&au#Pl~F#zm+Imv8;07NGhAzK0RC=WANmiWYROWmia>! zDh4g=r&a;CChZW3P^6^RlF4*;!;3iYk7a=UCK~?Ueh`eN_GoNFeFc9(4wUfa%I0Of zOFItVeyp5!tPOO_HrX*P8F zfuEI}6{{|iswgjt2fVR91?o9v2VfYg?prq6QDPxGX-oTFWzzO@72P+=AW@t6vE6y@ z<)xhCiPw;C{6!q!O`p0f3)NSRPfs{MU#fP8T=}p?$&>hy)Vt2UHMZ_9Uuml*r8)Nt znO;`;=oJp|#ocrP&T}aPSGQ7#lD4G{*Lc>&d>I9aGJ!Y#dk>Z9zE+-{6leCwqE(BCYC%R_!s02;JbM{^AlXz z&u9)_7eueWR&wlfCuRhIvT}v51r>)C2EeI5w)vnvxukbCH<>jezCM8^a=f3;O0ZCs zB@%CjJyPb|E&;4C2PPBavR6Y-r^$YNEy*hCq05To4=|h;%$>3Sz7BBSTH)gV6!gLG z??ZzAELA`F{Z)QarZ4YS$rVT41O?`U2P$d5r){!ZwFQFUAy3(xTsH!dt%H_ zv!*Tj={`lAh1iyxJiy8Q$^F6`KwPQ;Q?h>JCyJK}lqK}uGwe;QUrowgDa$S%L14IOT`#_PA3HAR@_NLWf^kw7rndH@_~U$bTPR}v*>72j z(=H?ospg)w@oXyYqDw7Kjl<41A#fUJ{^zJ?DyD1YOW(Lx)1MxB>J$sxY`X>T7VrnG zs@xbSn_$H)#f=5_=3UMy7&$BTC=9OCo%oJ#6wv7mH8knnA16R*!U-1J;q!1r z%ldnFFcynrdoGY>=)*W&7R6$wr<=fqMzDIh%{<)Iq7Tr#8PIE^o77t!B`ZL^f9+pM zf)@PkpPm*-*idLmWkMzU1ln$I8}(Giaa55$7@2Q`X!QAA=w&~>VD#4pa))?a>5JR67xA zU>iB;&D|9_fb!x}geBCrwW85%XMO3BG!0)G$yT#^PhqdA{PC==gBW6@_9!%Qus5i~7_)z{5>SGKT<#;!Av*mwh4|VxI0LR@&l4YNyujLyNf$7N*!)h}g^ss>XGrQj2w>b? zdRoYDAqF+kU_yy+qpZ`#470 zc6W|$&f0Z$)%~m@&pe)=?O;F<>sLtk+a7a_d1E_r+)?x}E6*9?%8IdHqOPLpgqDiw z%5shP`r_u*lDl_ic!yfUW5R}M?NY-BgJ}yt27}iZX$uXUg~U4(#hf>A44_i5#q=V0 z9RYg$5ma*KkNJ^?^rbA$4w?>K7i=5Y97z9L4njl7D?Xff*N}nCuW?HHW3SB>{FVpi6f(gsy_TB?hj;um@NucOo-jkTvDFu=MIUR(azoX$RmGYUluO32 z;8#~dE3;XXim&N2tJG0}ny8Op1DeuEEk96l z^%5o;LqCrjLw_WX*n7U>vs(2zGc|AZ)oY20prPo``CXS9zr7ck_%3hu%*@KIj|}r> zH?)^8hnRi)qG|F%B=i1Mju|NJn*Qf?tcjfk?OO0>c7Lr5ysLMCZV5=8~PjNm1 zz>oQz2DQ?0z3wZmhYKZZ?y~~V;4USY9qVB&ShKJs(*p_=^a|>rK-K{YWKo^hJfxSb z(}SWFIy-D7KyjsDxw(k;(6KdNL68u{NSCyNs+=aB>CdygSB7*hP3$|sH1BhP{#O;+ zXNm6Vuaks1e=rbbd<#^Vk;7RE|0X&s@&m^%*^*ESN zpyF=A1Jt@pREgs>p;#k-gmg7%3hp@Qy0Oj>?_4+w6eiQa$0FL0Vf$_lM`=lmV=sE% zxr%iAYffF@6zbDF71WzdwFBk}FbKl#v;G`}-)~!ybSGSmaI0TKkV;pd`F%LNyYj`L zHqhqXmzLu7NOjw0$L1L&`a*gm3)jS}zVJ;p@>=iuOL95m-B5Nr-43ro_akYK;5!6g zP`+he;apeseqi&3OUqU=YCaXTDjI#5XD3^~)gL01Z;pq*yBN%8m(#Z*#v<4Kz!{6uy+P)EacV@)9Ho(hHr&(~q0;LcYyG z+H$6hj_&x8pXnH`FuStFZ}y4B{&!*xslM)hdWoim)2vOnx4>yU!~o#ulno!e^Aso<{&P&!%_^IQMhF*0#rUAD zT~!}28naFs{H~0lv=QX*4R`aG87^}SbX)JO)dl|t!zcZQr;5R(nPY~D za=L8*Tm0EiQjJUbO$Do20Zl32t#>;kqKZs^W^7>FCp8b#vF-UOze}re zc)ielnOjm%RHov*qmg5;b6bJM!*=;|E`#<)MhIKQ@^^Nxw{lr)Y@^Vny2^-b_yLol ziyRk~EBp`f{4F%w51;_RFP?tvbDMJ&;#n2?TW6Uni~Sm3Sw7u z|J-jO@+lT2%m25WgwB23au7K8TJt7DTd?+_{h~9SHz3pPAteK4y1QYRkgSDKwN&z; z9}vi9R>Ty>LNdOs7KCf;xfMyXihXg9+MjK6UjUu8#~5EcZ%dDQBna%`-~0A3>9adz z+ElQ@$mIp0j?!>ee4l7X= z(jBW`lq)h^I2UzxrIqdrVYaWUWPVnkL=h}lKA+u1!tLUHW&JyiuX}G!!|S*2)TG*8 zYH^!jjtxSN(|-_f3AF7p_Z5k9Z1z<# z^61bIpADZlPzTx0>%wd`ZO(#sNP>!L&Nwih`OcFr!!tKHO*VwZtYaG{oC?<1`zu~h zsZqdATPX-U#nKM^S$tqKN!b*|!%J;7Pcs^JipiBd-lkpcPZJ1h>0b(4f4SmM|MgIXO%ix(5}};U}p0 z;_Y-WCa#fJ-{)O*IaKyMX_m?2VL9a06x}fCEt!|MoB3!ob?#VBVOfXf5_l>iS77N_ z-xnQ0ZZ9vQL~v{xL4QmaQQ4cBtRXf&Gr#ii+TIIYW!ViB0TDDBb*^Q8(@=L%Z1ZD% zyL`sSTO$X{QV%CmM4m!Ykk?!PGL@ERXMv)S57Ou=m-Cw!x0C{jUD~LHXTUa4?qY@s zq`^vb>Fw6H-rJ~mF@k1jv=*=>Q+11<9xtoQ$bU`$J;PaExK&#ky8KAW!Q4=*?Q(%Fo=hDzmkdG)ZVb? zLyM&!G$n&SA;$lx-YtoZ)DX`jle7MmFfb}_khgOQ^m7PGdEt(q3k!7?n!R+%c_4|Z zP`cY<#7wWePW8b9S8YdzviOfQkc?Duy?wi>-@#Dk%S(-(91n9}uRV!iP{3#Ai*F@z zMjYb*Yst_02s`W!k+7}zJ3n(F-SWQR?8XNZ{6zZ=t%iy+6f@4{7m8RPDlWmZAJtI$5bR&p~z>|4OKBa4*=LC3T z@S&{rxI2%F)9d+99{_-E`Lu=V)V~2;;b}9+-@2Ip$)$#%OTzWDMOZdL#o-xEKjVK% z7xIe#0=CRqX-}&skUK_ZRHEx5h?_#8xasRntD(7Sq*@D zI;LRI`J!agu!+=+z3hz}BW%Nzg>PLcETUozE?QpyQ4;MJx#(GJxG=3jFs4x3vB#Lg zz~FPJ&v2c?HP3tZ3o?3`BaGv;2i{7U}J71?aE?aIpZ8 z9?*SSIqEH$6g9z=S!2bbs|EB>V~OQY$?gh~05}C~D!uHn3z&2dRZcn~!B~tO9!@n5 z@KOqZ5)hBgTNds!1CWs93clx?Pz4{W2U7{UGchuUIcwa|(!Q?mOZ%C^5Br<2@SOwm zU@em+?CqC;d#dz8Nq+9nQ8bhC4v1kS4m>9HZb&$qya7C3w;i1a0yUR%oyNQ4Z@52| z9_MB>i47PaX5}u!cOFeLUW?^Jua_lv32c*a zsE@jKW43i?)8HpQ*tpbH$zGNBw61y5{zxye-mqHAF~=?yrpq5})x#5AU4QR{K?he{ z+_!gO1nq`}Rv$2>>2!p%krVT|#$c7ddv7S>< z!IltEi_fFf%SL5N9Z{XvnTD@Y^~{vRV1`mh_yb0g2k3)0!bt8r z#(T0CP8B?6pfY;*S^Hx*SN<*2{8_5&bpx->o|I>pf9tSmX!s~mMsw=I2<@?Yox8*5 z2U*HAPW>#dXMQQc4|HN%@&GvOdsz2*@-&4*{GDYe0;=mm>aCuPuC-#)#K$|~VXMr@ z&fHfxqWMx6GA}92RW%x`S0<|9TpsuRWX&_J3&#~H*y7veeUKejAVfd)osa!adXAX- z>?|gM0=&n9sCfF_f`y=wn=0>5NJoPb?VNhO{+H8i#Mv#STNisWmDB6Z$dbFTRela6qNQsyJ;{qWdhG*yJtZ<=vH=X%Jbl`AI!WLa z*8i%)H4Di3Ww%1qthpfbXmg7PU_L#kD+G-7{yM*)Xv1tc&Ff-h4kS{cr_DiY;fs^E zTiB#sYU6VDgQG>7pZbll<|xptLyP>bt-WVf;?mVcXhD~aXr~d#?_8h*=LH1=ZfvYO zDCAvcl6>k1B)itq^}>NiAE^Vh-@i<*3x42g>d0Uh|B=ZcFzlCIT$~X@;!tiqr$vk6 z9>Ye*&m7p=?D}>TUY3L3-)bj;gs4&9YDDktpz#x@*6UzfewnxS>bhgG^6sRRHKW24<^5*b)clTJ*LF=b0|grD-#l0N**(`4 zw1fIC9aG-D`gqarR`(_=jGWR|3&WX`NtF!~ZCy)FHl2$OF_hokEo~hScA)`b6X@a- z1o)u7-AyJ|fHmI~po5$%mekn+v#SGn{DNtQtmbngtOq#IFdWxZg z;1LMg&r?)D>oWiW50EM>+iUZFw)pLupGj;|%vNa@lG{TnrR!S{`jeet)nxrBA)oDd zeY#MMTs_lfBKu|U10At&34nYQ1oAa2IbcN2@-}~6SEe>+;QR!hv<9X!g7-Ds69e}s z3V--K>^Z3z6@FQ=ec)1GZH9DXs)_5)#Tz#U*>IGJw?oZXKlGS=$WF(^(36&hG3CTk z*dYS?z}mLg&KjOXI5M_ocxR3cg;Rx(sLz&7OP%pj$@-Exu&@q0Yt-vNz0zt74KA>z*4dUAiw+oM4xSg(MJmx%ccuX6GZPy0V@^%na zXnY%Kv$$H|I>Xr7qTkJK@`z@Ju(xLkdDnF4^F71;zsQV}D-4c7Gtqln)5xKeB1?c! z(sNn7BQOhkh_6d|6mlpQfh}*X&{^2(B~;(dSakI|>lEmo`uOQux_cBKfKe8@yh5u! zDh0C6d0?|i2BlFLOR~^#0ka951#C&Tx0`zQ-^>&w=;O8r!G7d{L0@J#wWSn2`$4C; zc7}ODGVPACxzv833s9l=AhnL>sBS;f##4O&?J*8M*ARY@x|)N+$i2g02cGyl+7?AT z8Ju&5Va!;Ek1R7aUv}H3q!l%a0NsZ_G6o*qDT^zRa75aDbH6gds|Z0p^X*jZl&^Pj zKo^)d<*)YpCurUAZ+ru|cY%aKpmxw&X>@vq>f1RjWyH~hd_6Nq%ZKgC!txo{vx^&^ z{J70%aUw4)y;4DQ$;c=z`JguPiZgeCUYz-fko>u98#Jaz{~j~ptvvW%)!vk3byPip zl>@U=l|I2cu-T9gW?%QOd#2-$_zRv?``jAh6hPURwQ%A zkJ~TiUFToRIfBcus?}FD{YvMJZ{Z)>Eu35xfI3V%0%HIp4o1sIuo zJg}h-{h&&pKsFOnS%F|jx@B6uYX-}m&Lx&4KJV(M5RBvy!7~i=+$?j z?k)updZuZ~rTrF0ju%SAzQnImFuL(-TWZ<6SUzZXF#Mut_LV23ogsTquk;2GL43n$ zG#*ho^#$a~cW1UvZ*SwPyM~GU@3u&|s0qRxny88+5e{Q)18+DR&UV#RIomzPxUyP^ zB$W^QizYC*#(`VWTt(rfsGSxIZOCQXML|ZML7vGIn`|% z8_FCi)e8fp9Xceg15R#S5WT*ol$m`do*ZhG+<6=lD zhCgdB{&%no<;cfHCOTWvuLUd+Eyr+AR}OdKB_a-~hu@2#=IDh0x= z_8v=uAzw>T0HCmIlBhh@pECNE-t!Mo*w*rNQkq~7Jl-q2rQ9||4dm z>-oQfnabO^%C25!)$(*|Ftkn!J$PAO@Alh|JSV<4oG3g}bCCF5WkGI7!w$~2HYSTi z>~!Q4hA0R`jy=+*dPx0dGHP<;*ncQIk<$nRxWOTy zx88Yb-;&>FB&Z7Q3bZxg8p>5nt#LtnWe2W9F{+PwrId4^kRx9C|Rge5TRS z+(m%9Oye3Aur;FNPG)6%2Xbn%;iDWV(zhHrXs6*&{qpLES)t7rfs8>1C!30Kh-zEr z#;*3=kcz;vfX23Qhn|eKh%~(ESg{(h8=pLv0I)w`Ql8YcjwaI8zy~+ws+6y#6javx zoOQ7)HA93km9}wxyn0zg>pefUQIEfA-(2chYeAr5Esn>3Yp9wNKw3|#&FAIZt5636 z=J?Nm*>1(Yg8nIJnQpw=diA6~a+CS;zc_vQhGKumSKZ`5zRR4_m~f?GIw6bYx4`W` z=Fj_IWX|7u>>pWcG<1IG_?quQ$aCfw8tMa-{xNgC;U7xx=S+Hk&lEIMk+;D=oBMTI ztNcGHb;7C?h6U|VhtODB7+Mh|BIwfm@`s0N@@z>h@6S)=sX2g{wJi(&29;uFS zlknzu9X);%U234Z$Z8aB5N;Xo@v1<#Tu%>o`j|!O5+kO&#fmdBdtAWO!d;Qf?9xr0 zH#`Qpa0gFW4j!_zu|0clc=3u?w||`fXrDmP*_{c*E?2}MX`p2}kw4Ez%qCjqtsck7 z%)E947?wtPx_|eYtbYLL7`3_+bE?ABD}Ug5^7ciu?1Qc@CujI$xO-0a4P_XIush}( zVh@VTFq!8rd0o7j8+BefWFQw1{Mx8j=jXG5B<9h~fO%PXt}0aU$j@W7yKU@^3Rve{ z!ds~yK7=#c?tp)R80EGk-)m~{il|juAYXX>pz1K)l>hQusC1Ly)9K6t9(3G4-C$Ks zJzUF~0g5fLgC`Y0X^+|N!7YC3Ln{!k7rO%gLir#6qWKkz$RAqYgD(>-mwhG@ z25IV#jkVlpeV0`(vEZF>so=bCZ+cj~*7C-|XJL<1o@{)gtfNq+sLgPydoL%fd?ZWu z^fBpC4{;f(d7YDTpV*rvI7AO{dXp|O-qsiAG0eSq;px4H)7&o4t5Q{Fxi(9-$0wTb zMaJ8{r1~Z9uG0lovo`oKyzeRXUd^5=gZ4}YbXEHc=AjC>Jh z^)&O6r^p7c{%FHS@Wyu5vum8wZ9Y+9yeRsKBh>L)`NcJt`Zvn49gf@dd0M065% z4EFRX*3oo(7Q{Pig2Y*p`9-77j%Y766^t)wjd?kGlPe-X7&XiE%JS!==xN#*(eHW} zcaAQ|yVr4KdDdHZEnb+!4aP1#!iX@v3ew82dwhn~Y1SGep*yEo;^V2IZm}k@96%|% zM+q}`>RqP(?E6kv}-x;oN`A@KE?;yqoQYNG#h?@q?T9*Ww>_OjoYTmQ8WmG}EN zbv%m?YfuLS4{t4;FnAuW4Tc-I#`8UTB+jFFQW#Azg@JEh5+Lo8it*n_>!@jE(!1g% z1l4XFnw~WG1TKr#U0(S;nx_9qoGXqlM)}yOOvfx);k=EfT27*hl++INv8T~!ao1V~ z`NFF=w2mY<&1wc6K)OF;Ng3xwZSN2YWZ|TQz2Lp=Dl7wPPZdG@B#1|Sb*DL_uRixF z6t~8FdT8=AtDn{^GneJz8PU^9hGLP*cgG^Ke&4Tgp)klFGF9+ne2*I~71iZAiM&dc zO~wc5c`*U@bs*#Ae(?9&!Kr-nLmGqlnYVYQq0Sb}WbgIa~KP zQP+JBCMH_S$E4MPXv`waOXNGlJHZG3LZPQMULon%db5?o-d69m%l4c^lFP-Z=%mIA|ag@=wkTjZ9{ zw-GDm^YV$t?13{%xd>v~L!vTiYmR`X z`MF9YwW2XmLD*H&Hc3eT?53XYYmrL$-fDxJo6VLtJn7+@DhuY?^tQM16mu3Ey$uvLhi<|VSt7Uze!a^&&`mH6ch(RZrvm>d z$vW3RMzaUN4Rjl3O$`wP!3#08;WhybYNn40DT*ToE`_^kymbs-8miCI9bcr zeiny6`mNvHPsc{J61{M_u1dbLGEMRs8Hnf|(N25>eN9k z35Y$0`qehn8gEeE9dVupjs%?R~gyE zm`6$^b-_@$)}X02)Mg6$1e(6XuUF+DmJ>#O{n*X+{v{oaUsg}lTb?W5bbH*y^V7FU zL9=?9`|Xb?u%>RHvV5srsAJ=Sbsq#z<+4sU2?o$vPM2yCbwpbKx&=)2E! z$C@(j#B8C4N!gTX`;LyyCJiiCtZ62qMNGIp^I&$`c})0fmgBN~hYZ^*YpKmA8aHt7 zXbqzjZk%x%oS6OEU7`_I@k<>1uL4C0H`EJqE-Sp9pQVy_&-oWsP~druy*xghn$s(| zzhF^@pnP3@xo827ktH@Hq!ExLaOeGYovaQR=?AsR;x|Iz zs{u0=U2UWp-@P7LG={Y9@T=3DhIP~r{^j;omQtX%VO^;M}(oaE?;2_t#+}kH(;G4AVz$;_r3o&5*mU?M1^_cXA znV>jN?$ASp&f=jf5O4_$no3xhp%-Il!P%!?a^Do!jR;KlKbLRQd#7%(IyjR(O&}4v z9L*!Usb6>l!FWs>r@*sQ>cUy<&Gq&nui^X9O#L#XJ(HkQNq}y@8r`I&{c9~t z>NoL0ao`LzhN1%LJvQWjwaV8AHO-4xPJD|=ymUH3T6h7lh@Yq;&&l=|qMoGGpHtS~ z`@Sbrsd#_w2zU~)5Mc9Yz9S%&Pu?gBy*aarT}jI$IRtlY&4siQdr%Lh&fm%}fp5Jq zKr8t%2E6)`=EvG{Pp66=yaaP@TAg*}T<=*~r;hTIDP{iZy0NX%7KmKrxroJUOs^nF z3o{E~@Dc58=fwptJZM>Jb}^MmgPj#TnWC_*5i^9OfGwW$Way(-l~_-4?lHJIajpk& zPAP+OjpHoUzy%A89?wbj-hYAF6U@2V_-oJE`yN%>XL%Ba6Uy?(^#n^-*iOk;*qiM| z=BS*$5wz5eg4vTIoq&yxqV{%(d9tWY(q`XXyKZd1x~+*}SKM=Mi+rm4 zVIBw`0BPNQ*{WW=Y*to@ek?mlO`$PI(v^_@)NBSkj(1|({oijLlluHz5YL3(YGWom zT#cR35Jc@76Psn>d+r1+?C&@`PCLEY@vf;>;%~%8Rq;qMiE6|0^ZmNkOGoAH5=1sb z?d;jQZbcYWtKBk5Jr_yKSb9v5uCr;C^&9u<+0;G@4@%Bgb;k1n*3h=3@X3#c+?pVu zL5~uAw+%2BHw>lGFo7j4!LiD}3=BFIfzqnuo4%I)MPiw`?pSZJkwBAx{g7bEP4@2{ z9TA-Y**_Apiv4R`ZnD+c7^}p$vJ@fkFE|F6|D5!~ZI1idi^n@^<-yIdEUi{bcWlnl z*A^77#@aNvpkXk~uS%ny-OrzWbD_I((U4i8#v|QEt+$=7-4dDZxSaoYi-0J@ji`y- z;lJ-%89$($c3EK`(;jc}|Nry+4r+BGhjgYv9~s-4Tobh0N?Kvg!fSIiZw027poyBPJSYaX=vhwXqt3}GBlpQ^SgL`n&e1ID+^cBCbbalu- zKbUy4jQzvw=r4Dg-+ZTiArr;r(wJo*JbC`bPUUH|G9jMIt{6W?N zco*Bg8oLzUf?4u^km-e7%8L$a9&rjB-PZ#;IzihY2j_UaGxCOPZV>h21HP%<^wC2H zbKHiuYG#@`;G1oz#?Sz;QTQm5M&NFO^3Ger(Py{(>w!L~J2WZn;oiLk(~yDFTfbA@ zp>6cLuXH>-pyTl47>;x^aF--m6yJXrPEMOB#NOGD4gc5NZE{u*$Eb9FdZT;=iMT3_&{=G9)v}p$-6PsyBsgr8fubc`W zvIgGWOr5-iOXWC<>kP2qh#jAhE>02LyDhkS0k(&oB(A|7+#ZMU{^Zh0n%r)sa8ey# z@rQxZ5q2X1?E;ORJ1-!LhG;;9i7f>`Vi?2xssg9k^WA}`_?k8E4P3@Tj31pwbD-=L z1_2B9e=+*bG4SD(>XqK`%vaX}#dK}cobFLHE(<3q14j9~@>IHWL?zQF{|#Ws6-$@s zkJE)b!JVzre#eep(B$n3uh{fHE9vP*9iW8~F=772(n^>1`vR=CYKWPnR}F8tW!RMf z5UUs<)&Q}A-@n>Bt()6wv@2UjpjajIWG`j^JL9N_!sl2cU88@LZV0tW*a1nJMi|50 z+^G3goc0)jO(uHaNl~lYyVt_&JDG#9aJ(0KuMUnXCGXGgfHUk+UmonidhNjuaOnav ze?BAkh%8bu+W$JSSL(9J>+(fZ-p5ii}$S8k2k za=a4~qwYeX=l77AluSBEY}wTmJuAV&9E zCN`%LLDQ?m>5Y-ETg&=SdXDqWJhr2Ee9^2FeFTs`gR!&H4fAtb0-SUBptqPu9B;}G z+C+KMJx@g_%a4~zHX#Yh^YCcMqB8VtU5F01BAAz825oqTn;uis3fqmVQnf~4_i#bEf~djiGYbBXVm*~Xx4kx#qqZ9`|tJD0BysbhuptL-~y z$7dN}CdTn2?2*P-0(IF9;c<)XfBD`*R#ZIcviIf#KtzGzAa>cmf%JoE4&i@Q@-*&8 z(skygJT{}F$kvg0+rgB_;txRJ$XSB^Ul+zZg5}~^ktA^yMj8>^Bg(?(=!iE}`~!%K zh&>7H8W>=)w}ZlyWcAzS(r)3K40gg!hA*h7cBCt+23MS1W)8FpqV-6DW)X#JP=j?4MV@E$osN=)u==<--RmNEYROh z!=x^Y-F-UX=;|5`Y%*SVj+J9IZY`J1}*}-GI2>w*f zhT*A{0h39-hB_77c^;=bv6s#dwiw)97mUZhpIB?!7-^+J?U3-OU%pu+F6^rxpK6uA zKIZn=TjE78HjzwW@VzCJ)Go6Uhke5BGO5zhXes*058p*E>j}s?PM_;wG}Pcua+vyw zujy^6@z9Cwvo$D`?FV7`{|Dmn z6mu6Fzun6VYP@!HmBFt@7N)80bi?&rX)MP?9!KQMMXD)vp+f(2mUSf=%=;w&7w5XX zbjdhZXn{B>>tdCLALPk0s%!AxcOroo#`=EBU9F>$zU?({qm2pSKtGkr=XI?rYqpCi z#nMEl;IvI+ViSTG1MD!HZM`VvZPJt&Q4(uULcg0>?0ZGS(TRQOofh2uDpj68AnC#6 z@k%%sh63obxA;fnLZ_qBE`|Mjk0f6zY^t`YBojE~1y<8_)aV`1LnZ^Yy4#P z`r0nOdei%+u?y$xNe1s7pYSET>66d#6KS;XFQF0~rsva`e|6EiZ_I^|wqyt3g5qCQ z&|RS@O$uE3*W2HlmT^?GW~VC;KiA5lU(k?CDNC%V5v5(Y=gR920})@j8CeBD%j(@* ziAsr3fP#4c^z}2}hsCo6lLQidCqd*-Tl*Vw#MCOJdD1G$j+po?gl*q}btbd6Je356 zp38;%k^+3}A;8Lie9D)Zp)R5x%uBp8A>9lB_h2be0smYc5MEw1pZzxv^~zGf5Qwi> zI@Aj3JZT`b@{GBVh`|oM`7;W2haB!P&9x&PjARbd zMB976!DRvQ{Oz4sFMkY89)Cgn=j0=;Z^Xx!OYYU8emNkqVI+KU^n`}Mc2vx& zoMm*>w_j&fG&a!n>je15Nd4Xn+Jaa&`|QWE*0uG^JF>$}}#O4MMhFai!N8k&4sNOel?A8!Q%28bYP^bNIciXhg2f%7^{ z>GA1h(rXXopWr{Ib2(1QWX~-tyy4diPH?o378HbkX7!zSYPs_8Q77AddoS@doobN} zLW&R_Nfns)mb54*zCHe%Scf1mp!bpx8&m-GrmhjQN%L*11Y_dsy?fKx8PakWenXXn zLD>Z1&4F+rOe2W=1TiH_nUyY;>zRt}Q=HM4H=e$~XXtEy!{VIH+FD&rum4$wzF`_! z1B!V29XF(GI16&irl0A&T*LZLMu!64$F83zHpf6L&_&d**C)MR8-p*c5;m+ihS=A9 zaU`5EkqL`+$IziJnpCb&zFuV}(b=kIjvi>$OV7bS^4_G}bQoW$2D%#q>F&x;CWS3-4lwBA?=!IN(DcwNNhVLYDH(BG#2~uY4SiUYOi&Axm#r&mf(@ zqVFD>`t)h8bNtCQ|uL-$A*>%X@0`X5NJecl_F?V6xtKO&}Ajd&zVD_%5b*qVAz zh$86WTsOjckHvU2BZqfDYzu@x)!e~5uwCJ?!`Gvex}|?KrB9}3w8);iAnm%o8b|t! zCP~%@uI>~Nj_yb|!1ag`LBFHEx#z!(#cs9B41X2GO2aoYh?vN^$aP1 zsnis!y5W_0=@d`Y6n?%?f$F!K2#O|qz;2a6Ef8&HVNBNL41lm!f}}m|D;e45JmI|F|_EyMQ06GKe5M zrSAz1MFFcs_V^wB9Pl4wq5H!zK^Wr z*a0Lq+JpnC10~EugHU^P>$5i$mnesIbZi=koqhC$pR2f+=@jYqlLz;IT?nQcROWhn z2@7qM-neA?582?UH(cGD_mX-J9c#F-#)r#~5S$8AeKy~MIajCUk?MA6QXN)r;H$%t zC7NrUmP&u{P?GbWaBM$HPs4y10~qU9#SR_mCt7e9PLLsJ{y3!)7RptKKjf|~QeT4Y zj3V*?GFt;1xQgFs{S}dX^tZ&FQ|F1?8}i`@&2L{t=%m$~sHy&JQWUgF&mB#vWepC; zR|k}R=0f3u9vA0*g$3`*Gzo}qIE+84 z;y>i_Xo9H^wor@sRCg9iD{zK**R(`}nOh!+qN@Qqf4!M-Al5{iy&YR8pPu|_s@9Xhc4cN@jg(nWF-4=dE~^xcNg3S4JY}^{YR1mzW|jiq1}W#X7f$n zNW$M*-bA56-Pyf2+eXtL6A<|??%EfAjN2heA)4yZ+I)g9N9a9Ul3WZ`jzkNFV3_or zuGBl_muTumuW)M0b$_-t>>6=0RA$@Lkq_P5-94IGsckIp+m55VT8|2*2^yH~_!7w*x&@IP^rJjSj;TAsTgb^v$-Y^zz|--$p;_2Ei}; ziwmTx6OEyD$|zz!N$B4CQcIO~t6q7QP!EIoT)O`#bN%7dRg-g);kRtoQ`n5;QWR>| zFqt9b6wm>XxcuuyoSP=9U3&!C2QM8g3T5q%OUwchhkLo~yF2=o+Vi;(jiD>U_qAk>z8WQ$nb z^9(&aR(a{N!$Gw%DWOeWh-rK@D*}xh!=EQ5&k4UIIuHqn^_e+h6Z}F@fFN-Xfpb9+ z!%!ICbpvIp5=o73iupHkL%G;XQsg$QwB4gb3KZqrF5R#&vRPY4Rsu?i$T2!xE2L|l zG!`1lApU-qR-)BV`D8Ql&S`yK7rk4_p~$w~W6lkcbaP15E*wf6>yeX{JBFnQgQz_q z&y<48{3C(W%(8M5$^M_XCOfu;uw=EL-Li$O%!+CazyjFV>VITFI~|r1_MIzh{nOK( zr_p~Sn@=G>nwjge-d8}`1G4c$Uyg3@(uLUH#c#%3&gki2Qx=^8kpKKj)>)h72X-Q0>=-oGH+*lDev5tfuB`opOJnI}R+WO*kSVAI- z&*e9_DECQSn)CpUz`%*ZvefP=r+vh+-?2u*zea!W#D6_0s!&?VHFHnIPjb5M0<``e z$zEdCAZIBErCvn%c*ETAqGPh)uw8*Wjg>=@%lrMv1t_tee=9L~*VVeL$2Psdth7JNg(*p_kSStKx%eLU|o_EB5%{R?IQAwg{fI1j7oh6EE#MBi<;Hj z+jw?L>JKi=POP6O02yPB57cg51-q)oVr56>>`Mgu`Tm-ex1P!mS~+}EN#~j?ICcJK z@7a-R6_(d3W6Hs|(!*+ArPlPlx+e~CG|WhLa!M%rRneoV7iVKP#Z>{k*l}br&&RJj zng9F>8>r~)!mGfS+5`#G=;aQ$i3~yYm+_>-moCdwHD|jf)rq`^cGseS$(4qeB_;9w zK4r;Xb6$Qzacf)o({O;j-$BDm6!WRNY3K>s)Xu2|Q))4s@EBpRIg~--{R*vk($4@- zMvp)xy1%RL@j|lm@2aOQ|M<~C6)mvB3t-h`Yh{=#YsAN`mVPhW-(HtSi@y)eUsuDc-cc2L4fHDQJ_ciAu z|JpayXLjbQ$Pvj~wLTD#EP;Sz#o8qr5UnA|u1ML@kEUM#-@tMXuM2D5JyXL+|1IjL zZN#(Q+`ysgbH_v`kyO2PA_{VoQ~tz@w$+W3V)r@7@%v|fJ#P_7894=&s1MT-TftwR zY*8d-8W&SGi)o9mbzs4xt!elfUm`F37`l!Ly)i*(LIic8Hs)q3C|us4>I`L8EfJ-H zL=1d;hM5qC7DpGJsJCVq%gA+&K?=7^e7BX_d8Fs9pH_HD{}|$U z=R98T$>)3H%535I?;i0=gU_0knDHMcb%uhImFG~49dxJ$6&}gf-8hL2mQH%Y#b4T; zZ^T%2&8%E@M#c)6c5D<|6g&@`{ zG6dBh448PZL!yNB>ryHma{6fSKZ6loibu&0%DOM`1pIOTV=h|!mR+;@n}{s0HyHv3 z!XLIhgU>(I5fsI(PxXPiEd2j~{c}|_Y~n3LKV}R}f*1uY?Oy85)C^h+C;CkU+pogH zqi$(-O#r^-+WYLyF{!>GpCQY73?q7U4VxI(&bdI5$aI|6F-fEDpri(O7VZ|P8kp$? zHd9N^w&yGBOe2j!#pION)>()+!Ue?~eP6f}ammop-cv4NI9z#^(ll=i}RD6(q7s*9&O37<5*^7&SKX4Gm% z+#sign(O(-h|5pkt_Ym))SD1Ib?oCNF`aR$^tyNjZ<{6es`V4rya(>%1&!P_@V$!m z-yRzX<3V7Jm_=lJa~P z>i;qg^))6zB1%sBYR1AMIwt@+Y+JI?WS(JpJnbWLUm#MO+|IG90fHcMj#^>7LzcX!c=x-Qpe6XzX43 zC2EInU(1_4{M^m8Tb-!+i)zhYPTyWTj3O!9xVRVSjK|Gw8LK+a`Cpx8!6lvl{n%@m4W$yHbbBG4v3&@3jx2=?>{Gzxy}nInVpN-}|qZa><^( z_gZ`HweMlB_{oqvreG|9E(SUhPZM$2CgV}^m4py71=Fb=d#-3$V`>{cD_!^Hfkz&$ zL`8!L&eOyNFI2;pf|2JEm3srd6z+M|tA-hX0rP%y7<50co4I%GhS3>Rn4j`u{o2et zeGRWT`jy?p^X+N1Im^pAzINr%r0aCI%>u6MC{8pkZp7Z*08cLS6q7631=OD8g{_{* z0tA@EGutv_q_VH+wU}6WvcWGMl485lL>n%1O8!&}ca(6^-;-mTWs!gFiPIT@y#)P_ z%N@!&-x=>7kbt2#x}ioHENn|rkAdxEnz2&EQZMrFJG7vaU@JkY-+?!mzE*u>pB;nxO%0{m3D02iuoMm_uGuR? z>;k@@##pDX%@~iCC2xMji7B8nB!Hs7y1=hI!ATi4R@8%`_NAbzIi;=TY{u*Oq~UbI zXpg7?h!O5p^gnm#ybdD0j+{LYm=Y&5f+J2VukfBdJ1_m!{pS;Hp$fC=kshcFLhy zYs))AnU*&L5s7c9v{g?&4BA)(nKju1uyq=svt%AZ*8qVec0c8Rs6+-5K5iouKsEPv z*cUlYyX9=Qu%8j1_K(`2M&iph;8+X7pOw`A_*q>0d_aju>{<^pNVX!f(aZqV1Q3?~ zR8b`I6w%M}KR<(OyxuZlm!4|JC1<@m-c7J=u~#{DTln=2EaDQ+*ZI$1{h9>tEy#PG z_cTw+dEy;XWH!~@XTm)trw4!Mh+}RMFX6lr<5Y{bVGnj%u-?$$NVM0^apUJ4U(lVW z2{FV;&O}P*jjJ00=rWL2+svU@$x{dB-HihpvhQ`1{WC2t-*iQ2A4^Sqy3A3FE7@qw zunqe)uuL6vIpCov$F4*)Of2z9BE0B~j=Qk|t)dGXs~2pSaXHR|2N^E?LN{>dvpz~0<~J_u45K`;U+{>KRL)vER- z#T9ExNS;_D25buaEN_QYpT+L7y(ys?{U0qz!sXG9idd_|^(jf_D9I=9IQax|rRPIM z!X%W1xrAhg_m-tZ>3;c5?i_d?HDw;u9c$p6PRShC<-Id!J{)E_Dm}rDful=pd+s6h~%g z5IeXS3HnYr#})EYWpQ3)iT?S6B)?j%wEGhf`1ml3)~QI=-I!$eKm)=O;z; z1rMB*IvnBIT1s`(Z__x348zIZ2c7*`Ao5}$^6cBb(Vz&zi#RL269x(nd{cQz#+!t! z_g&g+jf%H$@+u`qr=q1kD`E8L@wHwPK0SvQ?A8xZ4!&{nRVn$}U8laWN<)@9j`HZ& zL#B4M-Dsfe1te$OH}+i!Qj67h&I;F6qX(MZSp}=ET2z=O3E#>fBgoG&{pSVn(eSR zFyZzfvmqq`YD}A#B_ID5ZBpc24P}O^@ecZ)-#d9=wR@UYck-N7ve8(G4Ptu3QVB_)3nAJhJtA=cQR0pkOy?G7( zZF}v3nT*a%#LvgUbRRc1`@aL&oVmx#900TCI?z?hn-S{1hiKh z^ybDazQ!*zmDYzMsB1kUoq#`<0&3yA;i81dZLqiHf?a$ZgWHvGFW^B2{0?6b}%rYc(Ugj!A zpS;4_9XR+xG|ZpR_97s9LMa_hD};^T>zY=6(&E)~!49LBJ{F|ZJrWFX*zxU|G~@!i z{spk>#|KraW&&{tXugSU7ubC98PGs0@lr!Wjtkoo+Sez|oH<8?(P&DUPNj_ux~s2A zu><*(LznY@zQ|&*`OO9up-Q+6Cx6TV6qv7{#Sh9SE)*E};%vq%LFRk1g~2^A(SjXE zd9X53{w?xm<{4pmyN+K<OD{7;+4l(H*zG1*;Pq;L#k2-la4A7%{|u7I7ayQ5+hXvf6Neg?zz9OypbE<&=aB_ zsmJqFS6=(;Oa^g7gY#dmHqZBV-}zF#(dBUTJr&*cOlK+QI#|_n7e~=-wC`v~9`Lq= zln}rMi#upOolZA&DkRvYbp}aiK4FTTe|ioH=+=Rztb;BZU(OW2jS#79l*DT~)UN}o zeQaw}_@?I~ud<+i_$I1;Ekdwq)F0L5ve$`r9|858DLnEF8aIhvJQ{lODr^J6lVZmS zTIm6F`tX7^AQd0|&mh8nUA*7eKB7!$+VQ1SwJ@q*bU1Cf7m z)gL}{8ZHP{8Md;h!XNd71tpu61!>r!M@R903& zxmmbgyz0-NoGgs{N~h1-?7(}sTzwns3RCXE5-C~ccmJ*_ zw`x|nJeqs7*)K+DqSR7)KRSqeUNjo^U&XJSvs3?kpJEwWv7y2o3NhEdiA>0lN@Y@||0>yk&=>H2NaME2<$vb)=p)QUhkA8U%!A*t3+kG9hC8<<-Jrlc;Vcw91jH6>YlU46&F?^*|6IU^~`$ZiPctN-ez+-^?QtNJv>cLa>lB9ufp!USU7Zx zC391-QcPkDt~W;5*fG{|L`EIPyS+a+M2Mt@eL6L}Z?jkCOq8h-k%H-OruOaYVT+8M z@H69LTlE!ZOzAmy*hvyM#}3EuF+HU3pFARnd*==r$Du31u1iQ8hn8WCqMP!7P800(o z%4Mtz3j8~wQG)0x@YA{af7g98=I6A`CeML6W)=B5_%_#SH;q+kovaI498;iaYWPFL zR5P45A|9WEs}}o|U&FD%L9)m(%cVKd{0huPOrJQ?gdW0HGnTD8i+N8yjm20sn(v0j z8|5VwHT-aojq}htf8W<#KgL7IZ8_2?nZeDmb#n6H%|XFvu(Tn*qLCmHCI%Zgu?=k1 zUT>*C;GXDRnBi%l8~hI7znMdeRYtLb=Os6AN2kAj)%k!k!2V$a(I(5H+t-e#V#zX3 ztb_bgr@yK=Og!$bzvj^~X~^rh>-zKeXpyQ277I}E`}Bb(3XJGW>=AWE-)To3v6QsB zGn0Uv0hsDpmiud7jK&ocs>v+_!TATk{xr|>Nfa6QGNDbU(o!% zCb&CTv4$yak3T__YTA@8{9#SNK*Rw1 zC!7hLtg*+9ZB0G7Rx^ScGH<;lI~=4TKFi-6qv!LLo6U1RIxX6~@oq}x*Q|tM5Y4~h zq%_apcXVe|Kcd#1c^l#)No9BHQ809(Wu=(OgOkHcJK>y$w^Zls-}=d|)FOPN_`c-0 zJJ0&|C%(G$Ex<&_gr-?=t%lq;@zM(T#M@aEvMKsh&v+Vx(l&m#QngQwG?F;W$f^_% zrQ?zYxWG`ga^3lVqEZq_Qk5z(#K+$%(%V&zP5r-?G`jxr2sRx_0z+->lBC@rqNz>i zQPZy0)&zP|Bc`6q3{&cRKS+NNA z1B^`-F~ct6)85be`i*5Q?5?n7*O=LL@`*j4THV-Y1NP9!m$&cD3(Jcr>ol@&PGI$( zV^xUSG8$49xE#w}nj`fTE*oOPd8n-WP&G7mPyR{C^ue`?U}Cw_?Yf&QSRq1WQp z*7A#&NV?(gQWFiAI6f&$b{ZQ>{|xflN0iLry!dvhZ;C}_@$Admts&t46T;5>vdWtx znQxok1Bt;EzJ)u&(VyjI1t4zRXW8oL?#}l^-Cd$PhL@W?nP`kzv?|sj}wB zPoUsQ5A5Ojz}V~*b%nXEq!OvVmZ=Kl?kM|_Y}5Gs@5)-)mzrKRE>zt@++H>9D?;vX zL)iOMl6kLvW&q*4`lesCnAF3S1z$1X)5%>Bzg7GYquQ!k(anrycM!ZU|CV@_u(|tD zi;U-9+`yH-d+3DhFYB;p=P(}TvBibSiEose*+kc`Zj>a;)&BHpPi^$5_9~{bGzL~+ zw9=CCat!G0YKubUjt^C+N*UFFdUN2nQ-f9vTwZ`d^>jueBP^djbOwnB*-KuU=l(<} zNDVathM(QXy!qmfI#0MfMr+5kp818~3@j6ek3`MZZGm>2SpIJwHfZi#DZ;~Z81F8A z4bX@2%5U_E)%)A(OL#V-iGA}N%Q2FMheTVlR@$e}q2*e~^dh#Q7wdL+|2508B9#;X zAKZB1+urki?&R9T#p8Y>PJ4-|L7b>Jr}MA0OFy>|7dIAVA6-+fQH49eG)=soN8;b} z&*_w#-ejC{cjaYZ*;O&ZEn2k~(%+qieRW6zJ)E`rL}fjKC_e=rDCvSseAwO%~F^q<=K13SNW- zjE~s;PF-}CzQs=rIUWcGoGT)yfCX6f&>iy2lOlF=wk};U(!4aJ=J>n3sjP)%k6%M8 zSO8;~LoOt#xBhqxSfmUTd`@hC=?8syM-}cM{W@8KUJvWrv!XeN3^bzOu_d9NA9T!p z44)3afrN2xc$-Mu1nJ$ZBCX}C#p1!-l*aCbW21|ku0Y8Fo3$Urbow&$MNBoL1udh< z)6H8rsdUeKM4pI@f0GW$_psk2mbbdsARRPb>4CuDx^4BMK3vl?hHjkq=BvKjZR82~ zO3{AJ(`U^`yXRsGl8Nh?eY3M%J2kZ9)f4Mm=TOI-k-KgsaK5P#rADJmC(7IV3Ig;% zsSGYLOe?4AYL?d#ii>rlYXaz{(n4)?pW}-3703z&JFp>Ry$GdjZEsTrnjuA@{G2eICh{S=Otvhd&wv@|Vf&`Cvu4uYu~fDUYSmagZaVWD+giTAxJ58RYGGlR{g zW6@hVbkr^+GOQ17Y%=;J$kLK#WW-Q(ll!yXp_%v5M2rwHQB?7) zZyp)wB^F$6Q!-O16ETUC>5N#1-A#-ACeX|u%^9E|Mll~Wa58^+%b?bX2T@C7_~Q$3 zD^kIlIOZj`Mh!L6pm#NR&PK-V~D2Tb$e%?;JlI4fwHzcU0rs{ZF|$6&NH zT_CxW;Ozudk!+4Db*)1DfnxYE4@$qwod8h;Q33P6j%pLNNxUd@2j8g}M5&Ki111$7 z7o%aW?16&g9IAs8?*$)ZYs z5QYehu4DUblZdXHruGcg#1BLaU9}}ZqQcGkeZm+G3jpZ&T$;OdsJC}}`*{{ev_P#^ z(PhGU)n;Z5nt5M6=_y*SbK(rm*BzgZvaGr<>EJ%~YAtGKo@`j4n?XLgt7xDcjP>xx z=VdE65-{}FQHR}9e3D(4G(-vyK5SVVK>bnJ06c`>BDgxzo&x(NtzinLo(wYDW|w!9 zmwr6a^4pSLxU1eQd5PpAnEg;N4ZZBo?sF8cV*`;X0Q9dEdNk^wsmuP`L?O5Wy!1ae z1R9+>+rW4~%qea}y+sK*8j%Oyg8T2S>0Z?m1TfgV1+)iJl<0-O2iNk*8}fqebJJUt z{vlIXvAW?Py-st7{5{);&$_+K?f|ymmy$Ml<}R@4!bp~mi5qY*9C`8MoMrjiIz*D* z)M}4&hEf@lZHNr4iOu@rEs0;M<)Ejpd2OwI6yhF=`xwho8S!^_mTtx)>baYd3v>qN zLgukLar5Oe4Q6;7S{&)6KlCHqs4JmSWCO7igivKJb#& zcg*a^Rv_)yK{WOL&o($Fjz;LQJ!kYni$ISI;K%}cG8{v7PSFp{v8c$~pUp*?+KmjX z;bo(l&S&oNN-Jeez;rM+#0J(Rpm^zDsx4qLkS%HHCv4V}ow^i~7M-Aj+twb^%&e0< z@25xHzVdTUvXQRx=)>1}gxILXqOWdWuwI>N#8Ra}mwk{WspUJiD?<22pM5kooI$vb zJgcVkQY!ZxOx-?poK$UHdYjFZ|F}?K0#I%>9utRfg+O@)G>ABV9Wh2m*2Ki#{wIdc zy%|sp4Q2dxYnc=wYgEfwYAcvN%5wi{e9y*5%S*0;E1PqFXo;(8y1_BG0|ilOMvG6a zEQ}3EYbg&O?O<+YfK}{hGcf_+IvtONz>lC+amesn{G+RBsq~LVgM2RPD`^;pY-eiJ z?Agy+f0wS97tW&hb(kDk$|^??y%y)h8eY?UsGKJ?)1-02%433YQA5(iR<}NjeMa3a z@5X}Ft4kT4wwN`qZQ(~D$lsY)8?V~*WSdQgW?q1+@^VQ{?1AFMyPuz@*gdqWxB7DI zCW7Nr{PYK{KC<+u=PUcq%WFpAYI3^whN5y0s%U0h=FS&9q6<<{Zr>21Ao&!638A;K;M3KQ2ng6Vd)pMN{w1HsSw zPX8M(*fqeW+qL>N*1`L8!sW=r3oX)LlT2UvBKO9ILj$pI0W&`2Bp zd(J;=Uo~U#gdC&~uE~_mVESmFxCW%oHnHrmPkUUG5Rt|j*CO#XYWQ!6s#9J+>MjZ_ ze;Bsq=)5C?QOJ6tbw+WRt)nrzc|^nI$WeJ}Rl(YynXjBT|K3}0D|@%{20U`2-z?aC z&yzCa!;Vsh^+Mz9ibL+58{r2wvqaT_k`Joj6y%`or4=x13!bjr$Yzm7lbKy1>U};P zJKP?vsi}N41{+mus7&sOprRZ~v=00&wv$Zis=Z>XK?Z#t0F+LR9$ zV4i(?Ee9xNiuJ$DnQyIDyn(7P)z9255bTyg1ic-$Rt87F=pw^s)DHx5xS%xqFwQ57 zHfKpHH8Bb>^M&T))V_Rh`+J@Q9P+#9I1V95GI$kk9kY3M3#qY4J-)ZzCh*kJ2@)Rx z9W;&eP9QB5%jTtcoNN{Trxg;8}M}kLJ08JLn52*9Tyqf);V@BqhK0d z(Xqog*7z{YNVdME|MDY3P{gLV+2NZbbk~9R%wIL4`|d5XN^8ZD)o52Efqr=5+lk;W za3aTC)Z>=wc0nlOdC;a0K;1=Jds>BmR*bjhO<2VTz%O$~m~Cg0?vdD2?<2&Z5c$PzsVC%a_O3 zwBz?-sxkUTFq3`2t_`5oy*%Z;DQ%07$NIe9>?A5_mOL!}z#StP* z=N={PDlI_AVM$9|z`;zY4h-_>iBIzvk}$aFiPA6jnL$rn5RUi07gvGla3l7@tsJ)s3p);6Pv7BtlEWYs7~}2|2vGy(ep>ph6a!Ogq9}Fbi@E>wMovNVZJN` zl(W&Fa=y;BT=GohAym$cf+=+tl2gN#M7zP~Y&dgx{uQ4!7Ia1iVmrC~KM#~WaEIrW zJt%xqSz=z*f3>7<|Aq8+&3fO*uN_XV`*)MYJ6z()};5xwBMe<|w--5Rj! z>w#;hF4+BfK7l*wgy<-B=K4N{ML$fM2I!wq%6@90)BCC zlY3&U=#iuYJX0d@UNZBdE!M01_k*Oo%qEC>NFtX1VawizBE_9*r$#3!e)Cp<8QU9j zoW#zP0gL_IEpH7J&j9OjEN7OotY`z&7^wyDd+`%?&-er9E3yQ`<(F3#PDx!i^yJ*9>+U#cR8xo5Tc0=1J`X6k@`g{dPq-c* z9Y$1p2h%wz80%a)(`I&t=&5Xj3>jL0EklX2OJugwj-m82eD2abcV#)Wxw$B^0mbgMH#Y%@9etxqdMP;xvPVey^3IhPIMG=Q zagVuPwa1xx;m5g6%}9GC#yg&$YYdt)iM)&_`JEV@XK4&NeG{=WyS(vCl5t3lNy0k*`qcV!w(d1?g@4Fb-5GK{I zGT)Q`9L|kr3tu8?(_Wxu743LigFou$x8T~H9T_&%=+-)a_W>Y=4UKmRP#@{;p(Dr? zVP46#CHgU0K30S}Dm-o||O8Fc5q107XNa<1(bh%#+2TGUwM>LqUEqvd`0#2f- z$~mDz`jnet@PIlaQlkwBkRKTrsL~oe+R7ju)@XCn01(3RqZ4ux?XE-$Za)2=P|{LK zQBBn!^#$|)4xWYG6Xxui44`4ZiKuc@v^_G3VruW;z0EcKm~dP-l*&(WB38LVBm&P} z7R49hG80SsfR#lOc0@Bz4+Ss4Hbh=HrI^a`#jftc3O7>#?8EgzMs>Z_^1t{^zde5% zg_*Ny3%jT8KcN76U!N2kxTP>cFixP{nzgnpGdMx-YZcn_b6oxUZ_!Hn+YhHPH2%q~dPyUmLaj1w8z&`h)GPV;<_V@s>Vl5pAa7UuGgF!Rn8 z@1OLgJFj|vklwy4|2;T8t?xSl9(>=CKhOnV*jHshJqJC@o0Hw*`E_*u4pN#m`zIOd=WMtm&260un9=FR1Yjs_OhaLH-;ew!FPl-T!N zg;7`dO$NS~BK)0K^hC#VW5}})!v1Qdo8VQ9#HZrgRIcBY#L6tB%21D62-*I)rDR|# zV^ERs^2KQr6SF!k2@N5!UY|VxjW17-sA@m~Z(ZWC4u_N&FQ=0j(7^LVP;n5A4hA$D zVldao0Ez1#P@NS+)URC1OZ934Q}HuH0ohu_p2Z-rrf|*CL5BZ#G1gRiNMQAxe>CQX zo`)6u;?)>{wRYnfFbL(K#Blxx3)Dcfz^^cEJMoLQr%Cord2P;NLS1z8eQE6UP9tYk zO}^dNwk$PTqEIU-1}8`A z4WW{`%ewb`!sup!n9y)1^{k6;NQ`6Wckn{~1kVNN{nc-G=p~KjE@fvzs>-S!>N16z zn8RO`b)hZ|59`mtCT~rCM~MaQ_KBZPJZC4`xH8k_|%IhxaWiT{~`JODu6y z$+z9J+FGsu@YGLcCU1H`VVWgPqUV*I^c*0~E+q@>MQ1+-{r?%UeWx^wkp1ujU8jr|V%(;i-ndxg*O6vn#L#(D;-wHmQ z!zsLaB|SDfTdpK0KTY+JaGIZItMy3tP- zu{ymBRoD)OsDpfX? zNP#sN=cn<>Jq+0+youRaD%~5?-wDSp?y})_d6xQ`HSoh+eM(-Eg#~MrcTQep9(-Efp0F8HX`)Nfs&wnSs`xTfw!&wR9*GL7w18n{kZ?*QuH=8T%rBv}|f zl~W5{hRzhx^S}biH?BFzm+qt}QQDHn-$#et4>z_+S&4%f2|@TRGl;5NN=RnwdcGk2 z0m4=Dft|;}N#$4}J!`r>znA?>h?SHAR&oOrlSNEsF*IYDPt&medaZLqywu!K1hlu+ z=p#IMoBJfj_vyCBk9-^+78)S|sUsT~KJ7LW;Y~VbB}U4kqXuVvED`|1 zcfQWZ4mX~%hu18| zBMKBvqI%rESnP9?BjlDekd7~75sL1?t;c2opcUS-0()p{QA|AIAMPqsFrY=RxNH#Q5ja=5?y}PIA@?B90F{^obl-zF-?;cvb^Bw6mQWKUn+^t3!B z0CAg2(JL)j0?IHq_!jDh86flpqi${fZ315O$h%tFJW-lvvkGlEq|3B8B6#Z&{rJoI z;v3+E`>#-II>w?G#{(L)Zj%p7EAS;vaZ5={8BlZZHS=^Zspv73rMDc@p3nqnUW;rE zx5HQrn0T=EtVE!B(dTz_ll#SvAv$}v!W^et#`aaFSA0BwedWMK;7;N#_e;uWFNHLe z*pNjAYrHq!BYSU;Lz^S_+>+!y*Ef9?o8i$^{+>g? z-&U$=C;`Ofc<=}ZM?@XSwh6%Etg)mWhjAuwW~5m2GeZwDqTpwF0d!1JjNlg3=G~5@ z@GL%@6)sWIRrj@D{9uOWy|}SN(4Nq~0Aa2oV^>8A6%YdwYx?jr@lp(^Y30}~IL@dr zUn0i^d_T+BG8)a^~&_xshQW@#=#mmA~aL+fV;(%_N4)PN($M&VlouaRP z`rIG|beIZ*^Z2!n3*g9D{mlZefrYzrw)RKlS&_X=25TYqM|)koV3%U^^^4xG4qyD~ zPfl_q&MMk(v3*gFlIKd^VO$gT(eeCpC)SGWWPVQ@-B1?$x5*JrB}_~Duo=>=F0!{` z%;GxAEwQv$g-Rhh!s;KkoF*v@i21sJE=Q$zYxXGAec}t{hvWTdL&R z5-bwtxq%#xH-(<06WtKmXk4MR&{eycYHX%q^jP^_tfrBbL|oc#rS?q5v=vup*Xgtv z$3&JjqFb+)qr1+ZcMdl=aBcL{9&#Csof7CceKA2M)}$n&r=@nj*j5Cu^U1$q0_T#0 z^I-F}MX$CEhM!Gubg_ALfJ?8{6;z4c5?E$m-SISa{{%?Ct!Y$ysyx!%-$5(=06Y$I zGw(Z*$$XnJylf^Dm;!_y|78*JIb0N(1q6ZI4U1X z=5__WBUSWudVi+m{kzr*pSioS&k~6g_ox{m`*YE0uN6wiVX2}m1h3+HhLs)7d4Ba-dMDTX}!1+w7Vc4 z+vg}dE|c@wl<;6yEM=|`(4|Wd`k9|!!2XT*!4Rs-fVYcqak{^+a7AqK_{gsipheNW4 zzveZ4^9$B69aSpeyAyt$2~AVGcJEntR=7wrSVqHCN)>>AOC5(b^A(Q3P5m&qh$aJnS}y zwKo_wj>X4z3l^D@5;<2;KTZ2NI#ux$LeDx&~s6;Ekqc7j}ZmPkJ7had3k z?BEu?X=>|z^`Z>ixww5A>qiS@mL)>V26!VdBQZ2r8!tE_*z}WQkR3 zWhfzAdkE&m$4`EVbb^ffI>y=NcCj%)W*x#nojy6n!i1*FlLYa8Q-RkP%pIyZBGg}? z>D|%7d$4v_o!k#v8r*5-BrO(DcJa1Xu;JY2MBQL!d+IsX|`Dc{=(n}Yo?{^ZX z`6GpSh|2ZNs!>vJ_JeDGIp1&@J~MJBU50De@q5)?50QDB$)lj%96N8+Ohpv9iS3MP zY6~cEKP=d!$jn^8#Y}K#fVoNK{?MH8t`w}U7JE%RirI#U6gf$82r%Ii>#e+=zUo~Y zsh9|MEFJcOT^o59IG67^gfLwFhB?$`6xk!Qsoirz43^EtJ;Il5C?PQ{ePo4dLv=7t zandDw4G}Lg;=-6A@O!ONEMNZOWern5-`|ex1tD^18SF`1@F=U3orDcX+F?{_{4`W> z`OsWQFuqUp(!g>*X6iY;sK%qkn-XtMspUnj^#wUG5H=O(|JbX*pFVN{yH*j?e0F)(lI*q1e<%#g=Hyz+C-5N58qFc$B!+ z<|}vFI9+KWr2&>to&2qfupNT|2B%8ujXlYj)UpIzDv^%zCh;uljU6+a-_F5a)T$o3Mb4i%Y>kH6PCWr4Y+@E?9@lXOCak#9|F{8^sXh8E~uzsKjo zC~1!-=m`Pmq5n^avLCDsrYRdO>L=X3J6A3#;gT&v;Wmb%{U?QrpB*cKTNgsZ^v2m$ z$!7I;#&Om^?o#51Voc25ubPZ#g$>c0MtwJk^|4W|zm56B-seRz_2@nPPcOsx@MNaB~Qd;AD`l#neB@ z))3pbvzhiq1I%w^>8J!$De!9f!-02b?X10~0b_Hdx8<`*N@MCBqD`EB##{B_(~S4+?uadadnQ&VW0+B-sHiGCf4T+(-J1`>x70xkyZ^VVu6XCS5ATq{vCyE~qgf zj&aoAC#nwrkx4D~eL$=qb$PjZj5!Qbiv$l`IP6b!{{6!U<3o?|l_@p0b5?n9k^&ZR z>1X-hiSUY$2&XfMYfGB{C$fD6$o7D9-$FeXyA?Sq;LH|IHYjcyhv#m+xZzI@y0-G6 z!N6S006t!?V`59+aQoGU0)q(bMa^rI*hL9p%B0QZ@!U}qGiU3|`we`Zn3jx5bH1(- zlt_W?fV*WARMf@uSDWo%JqHhcpJYm{*c$cXFVJZoOtHq%-g7HMu;!oK>|&UIW0gGL zdhU??o87r$ncF|`Z=5ux7?1Q+ih2e8+WY+Hoc8$uN)Inf_(cL$Ac+r8N|^rnVW zXOnBrL#1f&?SjwNmzf)JQzcYcD{}7VdsSzfz{B}{mIU55T@O=7kM*M zXiqAr!Bae7Zo5+NjR9;0lMfeN5PcV-fzFLu`EuKrm(Ypy*$(|wflnkvmZERpQ#=XR zEa@kz%SKEo)KB1N5~aAKr;}UrKYf-!&JeByt9nXCZ(urgUz+td<fU zouh6sxZQ=fr+*8f(@FKxyLXd$jMi)Q{z2DGs?neoazDQ_PK#yE0M7lfgo)Ykbg%Oi+olaE5`RME9X=K~ z)5|uILzox4at2um_KK-u%2vJZLH|bJVuAZ+2~_Ym@Xn}A2793+`<*Q`ed-v`>eZRUns8>6xgD{p_sdu4#GciodE5KkIbO$>V+Zmd8uF>5D(8;W`XS1!T*)^9ZPuYB}7It?T# zXZ?IbI`%168saD{N23rPAf%Kg2kd^B40Fr~T69mCbbx=a_4MAI`HOuw7P33q+&rMd zV099Xhh%^}?FNKKXg=HVI?;2K5m*To`Eh^D`tM4suV?Cy`GN)Z#NOPv>5=xM2AH{u zADGHx>0A}I8iU;HNTsO+k8TS#%h{jJ-`)=XHt_RS(-$#(_xjP?wYac$(&oWr)%~5OJY0*CNnfu-cR7(M8XLQeve8~o1mz=pOPMGc)7@_muL~(3En8Vd z#qQoqw13nc`=$Cm$Q@4F`cIy9=I2dJjSAuMcV6W2n{u$*Bkvd6@x3auimZ1e_E`?fzk|M0 z6CnoKBj6;G^bfOk*sqZG@QDT}ud~D)7TqVXRrGr?1I1$${GoirJXl{{y)kG<-tI=N&!EY^YKXSmD zc6{kEj^{P-#ka#UO0o~4KK6cDiSAO72Lh3EBtTUA48!Q9Zaowls$=AIVlNoO&a=Pu z;2;T_GNfD*u8R%JQM$!IztJCDuPR{yjmGuEQgPX@{MmCX9(1nb6FIBz#fp<=>@cyB`Arkjq>mG}|m^N5xEM%a4R8HwgidNU9{O7Szjvrh5MYoZ^{y#*e(fbb`vX>1fKgnA=NJ7lh;i*b3r3C>kTmvoAKwM7Nq)bITY#JiWC`;{B}^@O`kgTwO(#1Kk7Y zjgW~|t^4d!BZ)++IwX5MtosG+P4am~Wsgh#cj^eO5^=VJq|B!Tf$jD%WX(Pv?nC%pqI%C9MGCTW%cHWJx~}vbqdKfHDBx zw+zB}YQNH=-V^3NWxQn)N96ALyQ7#Lx(|Hu?g|yc{vp8SM8QyX)bB3c)OjHX}PuX2NA~Nqh8XYC-p2;1kYb5{T%K#8P}^@ zmQJ7Ju=oEym=qsQ{lzW#t?g}N2l?rVrHkI;chOrQjK-7WJ#uB-I0)Z1ufZ-Uu*+w9 zZF257g0vO|a|5vZ0YCBU=!rFv|6MxfM|xF4`>VQO8QDX0-80}!L;(Ph)7ZH8j0|gD z>I#x0FI9)dl}A z=)ho>sS$jrCEs_ke;11M=YH{0J;Qq!QoIT;6b>yAj*_xZq-|c%1ZX7uY8{P*0`tCx3vqmG;MQN<6; z)pxerS47Ql`8*>WOIbr-F#@}Eq{(PB)GsmyHolAybA~3XcB?MOR8Oo$+n$3t z7v(+C(k@Jj9T&rJh`kZyJ`KP9r)J2R_Jzz3-E-ezYq8P`p3Gz+*~MIqJ{=IsN%3`S zD9(A1>D)CprEJzJyzJMUCprc4V|`E9r2KltUtX;N6O$Ag`ioyYhRX)sD$j@NlfQIZ zDORNh>MaNB^#*l@;`u3aP)*pnPA(o#!La-i-PIxw{yPkHj-@yL2}3;;hPwYd4Ck&- z&wZ*)CQ@&Klwf4}k(9TUPq6{o2LvwZXGI(Jx1G;;2j$3S0VI(=ALi)Fb|0n~4S136 zp^;JW4v+I=T8H@5S8>oeDxS*Zn2X5l2F=J(Yzvty3dy2dr^S9dW}OsiUFo=2Kvo9z zJjbFC1widO-fyR>G3a<5t%>Xijc>#hnv7x?bsR4qCjG#y}70Z>iY{M&NH2P(6@ zcA=cd_nv8sn?BmP@s!xtCzt%P4Nu)h3&@HVT0mDd)!I*_A{SuQ87l%VDxpUx_|CCi zIhV`$1o-U>Pc#HwxSOO>T~ut2eYp3r(#>b|GUv_HQ8gv(%yoxU zcyleWY}6sqMRo6-{2~(^{>_AJVUHO4ZV{kMIiz1&{|prdiEb~EDLhQio;^H>rL1Nv2eB0s*@popg$4#IZlty(5q(6(CP z18zfepObA#!Zy)Jd)e4Ule7X`dOqns5QLr>zV_g8s4;LzuUYkN9x3bKwX0LmxPDeJ z!tu_^PeNxva^bD_(L~dh!uN@r^FpSL+yqik3p}ZH+6apbSkJnn-i%M>w{-KP+M*E# zRJS!%d^{y-MXd&l71`zVbPEJ{OC@u|PX_-od|OCrV#7ty3;OjX@n6%t#tc0{neBX= zrC!Ba6cY0`>1AXA!u@ycADG}J-9Hop-5Ko-@5#Du6~3R=0nMNnGy{RJ<8L9D z0D~?EPy)(MOrFW;cDbN&Vs5ebL8`PW$vl^m4(Di*Ko<4EybDF>E0XFIRNJAbd9RT1 zBMj4U>Um#InI3{RKTe7-45VK$3>l~;G@mZrG8(7CoO+)6Voe_LH^aT_PlkG?P}4WZ zSN901j_Xs_kR?TfpH|*AasXWR_Eh#a!xiH`+Gl%DO zyNDF$|MB%D{!q8y`<6 z-0n9)3WPrzKl4ZkNQXbe+FTPi6)g@eu}65dT=IEVi&Q}SKlrtH(OVV{bwZr7S@A(| z^wCU?KU`?Ub8sdFkcr?acU+BR`7wR*&_~p6 zWJ2@r+v0cQqirvk6x%f%xtxMp$uB-f*9>_Pi3qYnmxXxv9WY;Srs^yHe$-*ckNagJ z>H304BHbr|eY_kFPA?9QeN+IzH=uE3eW;$`2(R<(#jp7GmY+IWhe~4ySI&Sd9)Z-J zL<`!8U-BGqlKz>B1}WgIFfc={se1|6;!2=ef*bJ9Dgq7wogi!SCfi{0l)Lr31V4Cp zN*(OyGb_~8HCDVjg;{zC6SdCXTRWoh0t;;g;X1?YD-?jRC~$l;m_GFQc;){eh&1{LZT`VsO#BC zPqJhGP;dWOz|>+IDb|*JKuca*ZV5R3xJLN+Xq#}r?8a_LEUISniaU0qv+9mB zF(0xU2mvyDfI#iQ=a5y`A39`l`@$m>Vx@{alm#Y?OrGIwUvtBRiK$~#YnGJ!94hAp z;Yu(cC^uD!dICb;8HWpxq%*Hsi9ns1mw46^(&-Pcx)kyZFW!1)uVgx97zM@sE~JsA zM*vrMzE1RMQlnv1CY(7}>QAdf0DT}cS(&=Gj)hT=Pg_4c4N;#ydORZzM+vxY4 z4r^0C-Oe@eIjw|_q}=lcCundj)M?V!v!N$X{;s;IG)uTvRLO$eKBO-lt(ME|+XVdn zP=k$Mi~g4&8LJvkT*A{0fok3m!>dRiTt? zQ35Kb9DBa>=-9s_UTfGVEfX)Vw^AKlF0p)HSETdeU$!xm0YNd&W)3fPZ)pn|>+V!} z1m2LQN6+LNEZp-gMl|8(^aM>vQGSh-9Q4|*^=?#9+ap(^*l6#!f*bcV# zJkZsE;^cO!ojjPXhFgJJR)3qmz5KsSM{UC%z#b);fm`3Q*k`EDE-ocq-r|EuL7G)_ zifdICi@vB*6Gw%9l0bxvY1hAP4;)rxxA!PDc>1>B$afPxa_AxAo#gZwNt>8w^2)jD z%{(E~18F|=Z<*FM152CTO?e}evdu2o4{@qS#EO2AklgFmk}Hy zJ3S7;k>Zr(i=nkZ0UyBL9W-~W!G8HoJ_>riWf6V6@|((MH9z%-p4D8p90;~kTxI^o zGHTIDA>3K~n`^ymW4=&?SIk+Zfx_e_Jo-5^$BE-+ctD)L5H)v!qI}Q8j9TH#$HQ(T+2^R0Jh9I=DXm%38r11BEB zPVU9E(e6g2NXxvGH(nWzEFa?dos|<{!*lfkPM*IbrIV87I+t*13TaA0cqFCFBsGoI2>v=b(jj?QUY8`;fg*M^UtW_8=|oKpH;l0 znd{aICe^*wxI1`|YyFp;p6_XM?mA=G`0}$0$>xU-^@Yf9%jeru88=pzHJ?@^UYEw)yNi2DSl7K4vv)HQHAMZTygvj1(q`b`3<@p*b)18o^?$&B`m z6F)>^O1tKWS_*Z=S}20}un6^R9~KnEoDeP}JdcCEQcnlii#&xcZa5T70^atwx%N+1 zr(FlR&6Y5;Ll`QH|IxKS5*Y_0rza0aI+j%7=9?wk-^G#*~Us zcF|bsGm=Q&o$0J{4I<993H$CE?^B<%gZm4?>&GXPbGa{kx0jz!WX&xcz&(7sd?qd< zYQeTy`DU!K(yb!vYvP9JFQ~&3FJp`KyPIUjl0W43tbdA1;WIvY^ULD)_AQ{_vlUkE z9&u}0324)N`mL$L{Q+?vmQfd0e!Xbp^p7ah%NCo;F_B*4cf5~wP9KU`P4Oi%`5lY- zbhJ7bgu9zLKzcWlEd6-V2E;qr*XTcqy$$S^A-Vn3O8z_8D?zY>pIIJn+m4QQ7T=P! z<`FqX7A0ghIyqS|B6{I;Jk?&O@Tp{7J$M_A6UQdvT$ixV&RDhpdFguK_J7+i%zAK{ z9$5IS9Sq34Fzw$zH?^E`?Z64^3P<$S(fy;t=OyzJ@h_a=d6@`UbSumKc*5Qj!FIKq z*b$)NE{C^wEbD2Yo!dpeB<@n zsqF1I$8h*|lkWxVd$3kB22KI{G7jyKLCcSO*3@f2v0VYh_9L8vKu2097ZlsedZ=b) zm&+C=kK6dRMGqB4^8SX6*n`PpcEQ^$XpQKIbccwfYxZ& zL@9`G!bXYNE}I12%IeM3M6XcRxtCM!LH`xtytkkLkAMQ4J!0MsM%$S`(Z-6EqjKR> zR5=7`zG4Kv#Nn$MTS;ZGj8ohx+UN|f_Ra;g}()hCOVvhJHLRiOF$eI^Ut-* ziCpbH0SQ^s155rWWd}3%6-_ASpHx>YA2XHD$h=EdICv;Be-som>L2==V%`$J9aN+) z*_L&vXGj%Sd9GI4>)%}1Rj{CX4-FmRARGs)JZh zIuo=)*<0l$yq{cRfx}uyQ5S68y`?>8ofLj19Dg}+Ffw~Q%pNdw@947%s&gM!0-N3R z$sq08#@nZb)%~f7V6bAYvt5{F@SRLLd>cL#7H=ykHHQJ+EQ^8iLx0=vaNtO;)2VRE zR@j@@_~7v=`Tj~X(-apMwg`l{2ss9#`LQf50W+>!Z~4t#-=ZLrH&`-%;v#BibmRdm z=b_GX9=Mn;tWRLdg*7pr1^tcwc)Ez|*UO6b$Zp%JJKW__YB~mE+AWBut+(z!+-FlX zBppFp)>&jr00EoKwwOX_)*DD@qoLoAS7*0F=!Ey(cY=B)4A>* zrp=_RKr0{A@u5JPwGlta>kfbW-<1sXO=t#p*|P6F)`_@lv3oawkS|LEqLzM({_qN; zUU}lYDcf(>2}5u_uM%W^6WD7y`M<5#cbyGX!xeYQ7B;X8UNakps(GU%p>OKSPO7J` z%MdQHa{vOUG`vn6pG+x+IA43QfchP=R&K--aPbWTRD2c65{_TH_k_sxk{rUjJ@20n zT5yGJ{NSs$CHpJIOQ!ioew_JgYyV{QV}(p3t$8x5J?V=ro3ZY06{kIwYtDi#>>0i8 zp{7x;f0YzM!Cv97yuTils!(!c;x#)tbO?ng0Fl#N3Dh-uJXV|?EN$7lZ_jvgW{AvG;P@SQt7vy z?(f>m*p3}rydV0Dd-LE94aqNx0v+j{mw29wbF%t@cp)CQ=hS~ zu?7`da->(EQ)nbqs}y1~kLK-z++*ewP8li2rAg^turFH<35KLAvqVjWm<>HuaiTU< z|AI`%y2%CS%Coue_HUo~Bmn^a68si?FR-}{e!f+(1Y1H?y7nuP!A)4=S*3@=X&ipu zLNQwjjomEbR+gwal?iQb18uYKlcF{@m&VyR`a-n8YiR6M(9IK}r*($D52)beR}Llf zYIi?KgyT^C`FtByc%v52Wyiddwt%OH;&9@ZUr70R5gXC|X&7M3(D|19ig;S09 z7Fh77aRVTdPpw-66o1=5DLk|{^7tFaHiN6knd8n4d~qV!a`*rk17da}XzwA7s#dDF z`#&^}z2Y|U=uE&Q$n|O0w}1gg9>C8<%VNqZfLr_7D62`cM4YNBYyGws=s&+mD1x*L zyVLm-Kcg&pB|j&S--pM4-?L=#FXuMbGfuKJy99aatgc{QCS)(Yw$uo3AeS(HHPe0q zAwK;$?Qfsjd`?U{tT72j4+5O>&0H>b$+0rM`V_vQyps}c>Rvb?Tc^r8OIPqj`&ZiHkU2%89As8H6DyV z-#-rhg)ES?0EBt+C*wy=dEbpkM5X0#TRu~Txda_Hs% z>{apHWBEorQ*S9Lr$EXl{J#4@df8|b%ZYK+i3W2gRh>idp`+z567DTbSZI7|&_|6g zB~omE|KPLk9p!NzIQ+T%x}wr~?{>+ivb706o28bb^(fuk=(xSP$Qh=;YeRq5Yn@rta|^vO7{XDE8Kc z-Rn>)9>6$LBW^4GL9w2PlR!$hhQ21{Z3t)-ZEWl_|67juL-mGa?s_%WUnONEIJh1!(vZMuPvel1Rfk&=8LI-CvQ-8N8}OvD#>hexWdnl`&SEcbHCoj>s_ zDnaw$KOpWp^tr)jN&Yo=no8i8v4up|SEvKqc-GLDU9OwC^!1hrne-O&RmLnZ>Ot0_ zC~4Gm7e1X-?6`dSx!BT^C*Im}`DT-syA;EDbj4I01O>!bX?SDzLXM;Fuauz0Cit@(90S}3e$4z(JoYdQCU_fG3Y&X2_kCd%iVGhciZ%jz@7~y7TagO#t=(5} zt;p%?as(rGnPjO+oS|9UPS=}PML=CO+;FLqhvk0147cwwxg~dZ@v(-$g`?Pnvbr|i zMRsmS;L6+ii7wLbFSM*7-&hk3vlBGArxL<@_&9(o3pcHYAl{`eAFj8Fg7ew*v&_so zy9Q*{{Xy*oI&FSRj%l9lCEJmzKdr%1NL2852Os($*6sbjtb1U!baCP1qpOSHk_%TB z*&WCoXrtFj!^3G{0wW`d)#Pn`mKH~r;5Q4otadijauqe+wA(K~5_ehJNM?Clw3~a? z6E8EO$bK4~>4S|9JM;VHL#u?26g4$|#V%J9lzfzHPzj)<%*yL(AUHeg1;+HV+HYp7 z-okF{ZN2qFbh)Uj6C)WGiP$_|*d4DxrgoAZfd=R!=fQ5l2;Oxt`m~iKd~_v<6>;%v zfQ?+QkVKx@{^6sbouF3KIaaDD``^5~qo%}DN+udeZxPTy&sCV)Vk01-m8o3y4;D3d zhU`FlnE5AhTlZ2Pdtsxy5i-EaJO5buRj=S9QNh$kddd#6vO7_pFI1Ezg><3%)+6-8 zfQ0lCwSirVzs{NTP)9T|3+Hs71RN(BMFFHfec~-?HksYI?+ctwG(`PQ^O4Q(7zsb` z(%_3AqY+&7Kvt9!793PBiK|`FT=C8MTOLb10_rHXd4~Ja zzxBe+lGZN@)(saOz`RnOrbU!0jT5%MB^Yp^`%ziED{Q!;5`+E3Z5@;}Yp-Jto*{0s z^f$IR`k>X!;?wpw0f2z+G`N{!59F0x?yA{SGkB&@_h$bTH|vSycaFJEHO3pM#QI2j z?KS#*QRf`Hu1Jf0ad`b%$-lnJ=ZQD@3OF2bE{B=A*BX|0SP3jQR!u*?PZmW!3bkcX zXequ0{%7CjXi=mpR`_Xy!_)VbKbk4_8Jl;v+<7sILc@7bp5=hB+!d1kPgp)4hPbD# ztcV4EEMlJfvUkhh?Jg+A)Y#b+#l-{cn?vb^X)zBti|PE(zCxFBRG|g>q$O2&4dE&B zmP7e~wewrn*7VxV9mMIC`!%>I+euvFxzEw3b1xY&oHb%T^1tl!8>>Tr ziD@Qa2koPrN4uRr-oPA*b&)-fEkXv!Zv%h+L(vM3R#QZ(n<@q&S&iHRtKG~AWL)=~ zkkZ@v$zNin9({{;Y0#xpr>j~v=A5_KZb({?tOk5RS2j(MoR?QYNQ z;Fqp^QKqs~%F`{xrhI|#5DhsEA$AhpJK`ikJbAh0wktA+TiN(z4 zf28t28&WSBVfJYV+7Kw@S9q*Zxffghv~FxJ+MBzdHup{up$-~G`2uMh1zQKY07RlH z6p3UjmWXvxqz)c94wfQOSANQgsC_Gcx4!kz^8m6_ss8U7Rh@fT#3tZ0|F|J zy5+nhTh3#z2?E5L9f_DGtAdfrmqVhAAfwkETdM@s=5yyz^~a#^P_Z{88v+?xNd-{oR#JfdytMP2O=Vr+v)`)jtpDwKp%)908Lhq(2FEEtbKbyo+CX;Nv=3AHMlvhNZ z^Cbwg!z}0v;4)o43;759=E$%8CqhMFqTP8FrBMMQcNH%g^U4rxV=ULtvNXHiC?WM8 z=BDL%<^EP9#&`ZU%K2>-O9>j}Fq4_LLh8kp5I&N;W5boWqt{MaR#3W^q^A+qj z=2AegCWt)*PE~=N8cja`Ju${N9R45kFV9aI(b3Dk7j z;AZ_|hWlB*L(L-X_ddSnrPrvTl=6vu>)HIpaEZ&5Ld~b(xM1AkrQ6zt0X)qhG)4Kx z46-_{v(<*IL~pO2d94(`k}*pwUF}1YA$C0j94m^hA$AD{5W)T_v4N(m_1Gaun-KsB zx^_NUGjG9`;?+4w#OkLAOTu+Jw}&VhwunkG`>G%~CwRH8FsDSJ+BM2|?k@}Fj6vd| zbN9eL3N`!6<=04rHVZxs^NpzfnYV!Ut@%Eae`K=u)-X-AB-lmUeUZXB1!JEr&Arvm zKJ9X6Vj0fzreXfL$2J3ie;yChlKji_c5GeOOvpDWeQT#u~H14kh=FjGC{sOLUwj*2?v{I zX@w~@N)_TEM~S}bbp z$Bl~{VfIo6AcdIJfE42V9_6**c-v-B_`_(LDKz=87i?-%gKF61)UwCKAkCy0qpHlc zDW(sy$Sx#7C}LIg5lL?jv7(Xss|x>NwF(s!tunKu#5B^aUC7M!ua7e-p)Ts8mKpuA zCnoVc>sHTSPrU7+JM=RmSu+CVE0t2IUz^RnY}YAY7-t47Y-KXF=29K&Ue!wJ2Nwny zHJc*adj!UuvpV}eY1mc1a9pu7QgiaMTho~tuBdYlDV)`qu_w4%+Amnt5nG!l>@!5s z;tmI{{IM50RN@nkeF~?6nmP__;u^+OYp7u&fczmE(+-baY5ATYMIzEe&%oFI{fpCn!7Mdv4?+eV+(o`Vy-Hmh@s~X)`(x#Y?euunC(W7h)n_VH)>`nwnxnQ>W$SlmHBrL@wg`xrB-gToa%mHnl?oUlj?ZbCVN zH3kb5OQaGyo&x4a$H1hML$bo$uZ0v2jsLLi2dL(9eTB+h7=v7z`EQpdy4$l+-f0Z7 z<5M)Y>pT{9=>Qd`f2U9kXASl6iUuPMx27X8)j1APtppCEbSqy(-^MZj{qqj-PSG8K z`pa3rC8O3*?>Dl$+L1rYrOB{md|2)P{ZPt!t(Lc4xx~mkt$*_sUqNTjq=|QPLG{~q zDgBMYb>2OBKR$-)naSAaFB=m$Nrs2y_U+Xi(N@@uqPxHyUJjH?Tc=Ug=$xR-ABCwQ zb_=e9tl5S+7jFRO+l<8PK}k&=CYAp?t9%%R?0k)CU667Fc4jl}yvI20ZLI!sf^Fc( z&s5P@7Afpt2h$KV4YTT4>N%8jWiU06TnLBz#~fAYw-z284%w?q&D!+Su~^azXh8>t zcl;&d3u+F1vp_3nmph`}I8kxOc4d2Y4&nX#0!v8W_YqgU!-`MUlZXh?>d(hPKl%cl zJuzn#ddxUzXK>(Q3sv_z_$``g&<%T0$Qfw8zjKtz6kh-OrWhbux5vk6JCm-9Fk zT#1Epz3dM4ihT|TK#Ay#*ab#*E+JU(Krn>bFiHUR3bot?f@mvVn?Ssh^k~ClO^pD- zwoC;7S_vOb1N&eP7wh!524|xXFX&O6FgZ*OmkUXz;hm;otK8q_qOW;*VfYGM!;%BeNR^X z7~hJ%f2d7(pz%U?N%h;_b5Ty+Gg=qFS7(SG&eP=A%J-jC_yTV^$@uYpzs{~S=d}=? zH0RPsk^b2I!t^ET#tk2o+c(^bMyb76C`E+WNcl0!vZVlON-7n6`&jG$+hAxYhonzC zOup0v47UH5!C)gNcX5XS*!qP=AgP0*;HmLw3*`@kL~wn$3A zkIR1afS=3#tj(+GQf&>*Xu`sArw!$oj#>kqYI7Yej?9W)~mq4Pm-= zN?>B`DBxW@@t5F!JWKt+!8+U;?fZf{hYAXG6|Vem8*f0(_-bHo=3iwtYY~@Ztkm zf}ZO*={kg`zYz(1oc^osWBt`~scTJ!jdyaKaVLpUmuULYib5q9Q9t(?IQkX;SUxF7 zvrTb6boaJpa+C6B;oLTd!eus+;bFObVkPzKY{bQlcDxX!JA3{IM(&7otULooC{s9< z<|xQ67J4SHZ0W&9!AH#IrOOHok zV9LaH54)HI{(+O(H%!w`X31FzDT7qkZPsEdz$TpdEh~0#@c09bWn~MQrRSB_c9kxD z+p>sg`0GxAdG1NlF+Ku@ci~E_Ni%n+q#WuYM_o~KBO{SwbwSM1%n8hto}XIo%C2O( z>(S$x`HCu96rCuiD!xX0!1Pl+u}5fR1>}s)zmhQd8p6V*FY5o7EryUSLMg)9gH6Ut zA+47{32mbxsq+C~v~8SiK%_18Fm(2zqpS&VlPM1vpi8mb9;%?+o@2rh#48waR6(cA z_%BR7F;ktGwPSp8n>gHHUWbMZZV9P7to867G_ z8e+~qU$&MJ$~?_aI}9AGA2d9GUvC~+0`e|$YHz_c3&+9=rAeGMqPq(hr9H~f9g=-KWldUg{cbhqZvkXyP)3}?V&s`nmn=lV1qUbD?z|O$8Slrc-*ScyK zpiBVaKGzh4g;?c2c0meiqgVx{;CD3EmuXbOIc%f8F@!=F$aAVGsVsv{CQ22YFVaRt zuSB&=bG#^H2kmHCzX|=L)`Xie&@>MCNJ)c~F@z;VlY>}xX2KKjoWCQkOUvSz^DO}} z_Cg4QDyyi=&*CgSKP~9jHEkCaAWyPBRhRWX)+w0Jj1nS6PgxA=R7D|Y@NAMjlUZm} zLR6ovdBEi&+n*;lE<%3V8I;&BoL)}?!>kn4IFWNteEr@Kw7ZkA2U!#_h5`7x(IP}D zaqOx%*lwkf8sLu`UzqnM;&68B&+KI$qfhM- zIgavQZgv?vJnj>(11*|9M~7zPH8(Q-HU;M1xoVZOT0_aTxaHK7oSfVd-8Jp|uAYt5 zWaZ>@c(1YV{+DttyVqgO8Y z%3niFeI<%aq?2{=W<|(yi)3KS37?j@iALc(ub@j{pZQP{vl3g zS`LMj4jpc5#e2!E4HnH7^E)ri`i9q&O0H6dblJBoNh!9;N_A$s1i*fnP<9rW@^TCs z@lMMC8Oj`SfCUtJ$DrxonCCMfjE;gZa=$J7gV%0J|B#0B3x^M%J<+3S&vJO|%{@MX zsX->n)O8}lFZL3fDY(CWf#Mzekjd#(M|BgGcuXmkJtu#NUQ+Hb|I7X! zwpgrhe#^Ppb!OzecXt=Y5ZY>?xM6ZH(kWC2`+YwDqYY-)iq&=59ChMciV(v( z#2t?^n+(g$*NkQL*qe&bMuf}Zy)?~>enuuQ_2pEipCG#wccc1(jCGnrx{j;k;M$4~ zX1XJW1HcGmmP#H@;B50qc{n9`*0G|~Fny-HAD^K^2rRE1%YBGxBK8T2fqxFsngo6u zx*KBx=Ef2iz&Xd1Ro7O}+UrK3mSI-k1IIyUOihMBxL>%P$~FM|w=t%&b!rwrdT-lI ziT(#^b*ahQLyfnPrVSy@8QI4bqM%zi43qP%x+?!=Byv$T$63Mq9joyKmwAAaT(II& z(F+yw^Ef#QiWU-7>A4~|4Wx} zaB%rj@bikgrRMWFr>gGSjJcRNd9oHc^0tys_HhyK1%il^T%y&E=PJr(?c(ZO3NpqV?eX!pGg|Vp~QTUCL)Op^MLG z>nNzq4(&+Wpb<&6o)W{GFkFpQAT zuCVJ(H-jNp4sf=6c= zOsR!7 zQc`mxojzgUN?FEFODrj^s`O zuzj1*uI{Md*TTWt>90rYQIutgm+92sd&EU<$t;1bfe#+F9hTzLrBx&HR}N{Nsf9gP zo6}w|uP5F7)KPB0PMI%%L%)M^8*--M{I|nJA-C8x6Z_H8Jc0Ag0x0$Y+<*=#L-uZew3+QvKH@w{(((v`j(TDsu1glePiBW2-hZx78HerBWytAptsUZuEI)L z>V9au(a9KrV=Z$8jyeS#WfOJSw4W2S!o>tucJ%e8vEubn%-8;U0P)WaBb5sWiLTp? zT0gLd-cvX8LX+1-0WFXz=HNPv?Pwd5hO*}s^&>)iQE&N;mrTVcByzRduC43lcM+Pp z`i`*sUD6JWoLY_rSD!mC!Ur|(U6InX9OsYhcdjfv-oXvys}#O;85q)CvT9S7N=*Qn z<_ZLkOz;d(%Kqw*OX@pEd7h>kA^q?~-zgXuGg}LHCSB9-mn~>< z=uU`$%@yS)ZSKX~zRF z8@74Z73W}M<>tc+U2js2##_DxibEifnXwOdD5$(WaRvy1;cU?AR(l)C{wo^1qB}yG-o5uS?&F2`KL#>KOZPFb&IW+b6dD` zPHV||Zv42ic=}QY?cRKU4Xh-|WVF@Pw){nGOFc}Js_bkzs4<;o=(Dhwrf0i*MIr$E z$A%mK*dV7&gi`%}FWpkzLNK5U%|VTph`^gbb0#98>@(C8Vba!i$c!AXrT0fre@m~g zSDGoRO5q-(1S2u96{JoHx0{jO(XTP%Os5o_HTqO};edIfgU~GhBNA>mVVro5Gl;K) zJzVYcCp}x1(7iV)^IXD6{cCBP%BZ@_axmAK!W8<=A)HO3Gr<++Fyl=8T7|HBV_3lQ z)sW(BuqZCy|sC~q8{@oz@)5*~5= zWi11Cz0DH{Wn7fLUTdZqC>1ruezPp@ppj!5|1ks1-}_hNXWzI7CE>5p3~NzQy}pKnV{gu=NA&?nd>e(Nh?F75x-#u+Pr~!!nz`| zK^C*Uaf$}^b&FA9V0SE)^0T*=(fKB+^6ABXQD3D9J(P?>9ZZw2TLpp!El%UH&ql9!r)u=qxxef=Un{>^^tv zX@8tEF_cyiw7jvrb_&^{pa_(Erhz zH@!SZ^QV0G5)M3)=&)IGiqr%$?`Po!L7*mRERf72g801rCq7+=Rt^>{NwdgqUdUPK z^g1!`LpiuYvp#ceaiSKkEMZ<|FrFh1fueQwLGT%9nO_txFih_`l8M9XFznC!WiGpK z<`t4}P!PB}l3(8dHAswjpysnLiMZ95MOJU2nSk$vjna^*2=FuGjCcyy2l0S!H7h|GDNpiM!yJtF2PMG@~O+Bo6LcsyXo5-K+vTqRQ% zV+bw&G4-!zJ|_)J#y(>9G7DC$_T`M^TqBLKZ`CZPk3)k`Xr zD;Wfwg02E;lt8`X?jBZ{5;@PCGkO536ON^E7|w)8sun90ERhH8Zx#<`kK7{9dws(^ z)+OFh0jEb9s@G&~IfDpGs%-F6nYuDk3;H^Cnc%})ZnX0q2lGjk;tq6GCP$tlgQqM& z-j-0_DI?(~kJgR3AtwJ81+z3B4s8rppL6fvEi(!9h%bUE3_0@>8&D;p!2l$RG3&HB zO801Kmt?URjZ|3d6gFazbu6v)nfXvzYN`yF(DbBP<@Apm_RsUkzE68D&G(|09S|WN zzZz}7BF$=@hWK_ORFGX~mlL{AJqcNY*v&xYytxZ;V(Qs>!$H@aT>h0j%M2P<;QjA% zdigaa`h(_qY0dvE{`*Z{r=|d!??Uu!1|`-}$bj2b+~j5a>ci|n_rV)%q1S~UFr$7jj}(sZ&^+8vegCk1Z1=Jzh&>8I+)q zdcS^(b%X2cq?1o{7s-csXg1B1W6Hz~fHxqv(;g2J5eX?~q0g;y3ZM^7%~9PMbTFkG zV=gs!EL``qD=Z^Ks)_Zb+JZGj^#GUY)12)t@Aa%*s&GDJkY0JEqd@dc-0eG$_vcv+Jh zE$l`)k*4m6fKCoWvZeneS$ZdOy;x*m=OqX)`k*{{H7Z_|L|pO;k-Xcl$AT9WrdWST z{p|^`r?YD>Y}1-ioO88pQt@h2hZz&-`A%#er*OJzQuF%0-^}hwHZ%mtTJHK%_MD=L z{j_#iXW@%qa)s)Q>w=)I+UFbMHnKFm+o=0#)9X&du>o!!#t_Pn0ph;LXdWuoApT4P z&kx)pqfX2a@ffNx6DRrZITC{_{Fz)7h0z=8vt%-!NiW*5y}PC8K1EQj-wT#0F`_2; zW&QohW`9sqRRxMi-%qZu52vY6Oy?*ihDMo{7qVaJ)XP0L8PCz!S=Qa$x_b1f?q^=K znZ#|4TT&jQs3_8n5G9P?9IIb324Ai(@rV?2ufssN7sfBVfc6W$%(%J4?y;h?jm<;l z98Kr?@R(wcp0Ynh7+GRjUSI#MnRZ9)Sv|(UKL?uSlnVtEqZ*rIH}>R{W;45(zY-=8 zL9=Im_E<7`yzmg75pBxka=oEHu<~ z7IQw7!z(nKckeskZ_Tb?MtH*uOJ@$wg}cp+izU?l{>Xj#TmxAAKEqWKRbv*|e1Rx2TGsQj>>aYpH zUn#UNmAX?sr1o=laSiOWS4(Lz%7OW{F+;p<_?1VN3(%GcXkP({ql?rjYhC9&G^?Sh zrDhmT9U1pQOX3@bc1$&U^KkxOYh0*fV7bfsg9LI+i|Iv>Hyuhjpi`gXB4cb#80M07 zb`P{S=!D&?^av}|xIsPH6Gmj9#O>qrkvBDhX z1U^qukuGK?&4%%s0g^sRr{raVq}dJTSr$Xvhams-heHx2_M4=daf9{&1$}<0@7i8U z80yGPkJD9n3Bo|keYHg&isTBy_Rsf)A2N}eJ4HfsmK{vh)-;itpY7zh%Xi{?Q@{=V z;;IGb3d+1+x{fHu(ax=@SEXz^&!VvVy%M}mHuPEGgzd|*Bj6*uZ_EWBVXjzWYsK9) zw8aYgqFdd@st;vq7}StOmCJ^?O5rW3XSqgIVE&bNUFMSz-LO%{(KO zKqNXGo4R@fZ7FscwwYE!^PB%bE=?L>?^eALV{rpiaAw{@kd%sHt{7(K@jlGsqtq1<#KYZM1aE{aRR39TVTMpP~*)s|5l%1%^_DSFSOy{^l~^q@{HfEO>4QQ zx|w`=;s>YMcc2<&g_{?`J1pjP5({UMh&BgoSRuY7r;MvPRVh>Hd+1vE(T0Y#x7vGt zw+%?tO2$5(qwGd!(q#MTX!ONiOuR0|oU*Pydw)FTl-N8FP)vqnhbnxUl4!q$P4j@o z7R$XjNUy~Q<;3c)N$02YIRP`v8rj(}x{9IwrFTl84#j762m|Y300UYap*g(J2fZC! z^+lNZ7&Cij?r<>gENPYOTIaXCeyh*iuMh=J{z@SiZAo((dtN^3cIkkiaheqGtgS6WqJz;y@8l9*-w zOJ=@uv|mvN1$&?yI~SC0U^!pj2-3&!JY|fy-m$7PLqCVOXOlH*wsh7VPwDRs+6^&+ zj*huU!T3`(dPnC6t6`>Rz$kCMgX zou_lFo%`)Z)Wyw@{*GofU#v1WNi;9%VidO{FG;GLH=LR9REm&2;~oh{L?l8NqusP5{(R%eikaSRp_^Lm?Q2cp zm30xc>xZ#Frnx2OV_PIOJsh9KFR^CW9-MZ*ypUkY`{gQ!XLd(g;mC-Q4~}j2b1b__ zehE%p#^GJ(+m>_mXjAt%waLU~L+|Q9%?pgy%YEAA$VRvVf4Tv(KU^^_7*9VI3nowJ zDV^z+k-Ay~^&1u6bG*i$WR@g=<_(=pW@idglt_OVD}IBwXjGh7g+CfM2J!DPh0XEQ z6e35d>I*1O(^+P3LLc4V{p{BrnOm!G5u#k%<58#j#m=JgIm%D4}*>non~6 z{#4K#1Qt3vZ}NRjw5Fo&?W4S_99iuri^#_P^}{sdO{)R3oi_hedm|D_M-OHMsvVV`41vW5)^+9q|J3bwNi zh!t`PEbkcCzS=|WKYMSsqMP%EvgFA1j6!Z3Ngtc{`qm${rU%Yq&tQMr>clM0D@2u4 zf3`_$d7jcHFddu!yYv-yXF!IM+E7=jp0P@7{AJthmYL}dqk)3NCH|G3N$qKEE?l`2sR>`;_jB(|#Q<6VK=`JEiE`6q;zU?l4nn>f93gRpFYXPkK&k&%@8H z>;6{k`Q=ws`$VIs*Tgi$Jo2+AWN#&b8n!-z)zq6Gd$gOSpa9omI9k4Ys~MNAJj)~D)_wQl$iE~d34Mqg8W zvys<1bZ|Vnu2;{&BL%VhY1-lV8%<=FfV5UIMu8`sCl>G0PcN2Grn+aft|P5D@;6@cMJO zjdvFfXgllu)_PqtJp;-#&^g(zeQWhnofXNhcHnz@`S-ouS03A`ndW`nNckMJgi;;n zvXP3wdjr4oGvW8z%B%Q>x3g@<%&{r7`vvpE*9L@KZsxx`5fIz%*9Sg-YQ}i^nO%-g zN?mF>CXLsN@;WojpUO&jtaV9z{FUB@(DZOIj=eLTS}1)!qwvX^aQ3{~5X_~a%;r>K zr`99)AbXXHp%nosvR5?_G(lFv3I=Q~&IF1SF_3@~ zWhaan_OMt8BLgHPG9w}h5Fj9g5!UxaeV*s@`2)VceeR#|rQGLzuJ`r6-q*R$@xG1j zIHGjvfkNsrAF)PpOIGJp#73qwCCkJKD)BXM&6HUof{YBjQD2MpE(|BId=VL&y7P-1 zU*+P(L~an`52_(zAt9T0_PX9B#~3KA&N{}45pX?VbqPeS2P$k7=tNVYs9AUN!D|1i z>_BhjyN2fl@h$!G8r$euY#CD%d=x=txsd`z< zgnH4J*u(wKNI$3FHx8$j-xazzu&$*sEhki78JLHK^MvKDwQl=~|$@AhvF|s9c zdPdtlA(v?1CH7ZK;)@m@^IM-suY~PD+6+bMBUtMzT2Z%*FaZ?l)iGvU*MQUvZLYvw zq$h6jYi;`KisOH?`CS>ZTu1ePpRoZ;>ZewEuiGQ)X7~fagLYB3O~(!yndf&!5me{y znP28+FGdAX97?UOY>u4E*}lCfS{c+G*}LnQ_v!?NL+uWDVc<@w;bzXiHm`8TFh2Ma zwti?FiH%~9JVM!|^|c{b#by~#HW$Tjz3Vn2?p5UoWu|nYwHG~x4-hV*ElOYL)CXP?Hnm&SGjZeM0Dt3u=vd0gmyqm{@DXL%3y%S5OX7r$d%-lKxHs|H zCqCfRLJZFqWc~=I{?D;VJ9`S9gYkn->#r(=$%t=M=oY2H5$5?Xo_M@i?DNh~gSP6X z(Ge^M2UgE(vt%TCtq(r!+!-(+_CeMuDVOl*%@XE=Vhv%%Sfm_S&8IqciCKdBXbN4dl@Yf( zMa^(aq%Y3YUSRzwq16}{!H?Ttw+R}b-ib{hv3Jw4tT+GGHLcd25KHISvwIPBjVHce zXY8Q+U8l?BvKCN~Da3mesFJ>gj}hhA{?p=(%2CE#ryh418~rIqZ98;bchZN*)~9sm z^u>e?Iqmm4XB?JJARn=kJoO z{~$Os+?XX6ax+(BK(OXy=zn6>Q#F#QTaeTc@x1|+zT&RzfO!(MK(Dj1G(ehlTaOdi=e^d(_pX2+#bzBu5Of?Yxf0=`@j7b6j`vi#^oz4 z%~J-fJhh|^H zC#3pq57Tgd7*`iUeMpM=uEE*x!edFI^l!SXlu;Qo_1?4(d!AL7s^pKdBir;Jo6n^2 zN)s=PP;S*FyRRQZhEZvYt1qBX)?exkeg&#nHi@lBZ2MTu-kav0WvvNYR}VKIy#LSi!r-)ueCE=5f*$$-(?_ zD^koO=wo%QY4`LVfNk znJIP)>-CO0>|;^R?vFu{&?FZ!&B+WA-(Aox@ltE3KW#%FUv+e*)?6yI zPOON&LZVBA<`aOJ!Mn%g#j_`ytwL|;IQ~gWV~+z6>BS~#^iAmr?&7-!dbq(bcQMz6 z95(z8z10MPPVVx`SAUAQ-v{Q3?ESjf9nH%^mUSVcdNr#y!=G_TTl0gJrBo+T?vYe& z46oHEz{7s=XNl)-tJQn;GpO9Lj)+O31IH|Nm}3Wz#lCqF`TMRVS=nMq;)PKPzOK=! zp^jrEb*H!;tDq}SE~q|6K1b|u%SZil!ht_N4Wt}3hn^{zZYhQfUXz{zkNWoS*ewuG z>aZtoCleK%;rJ~qqitXX5xZQgxMCk4K-JYvFo4h&R zao^y4#!yW02I6DP?dA=8gqXt2AA2X%BRUKKVAOJUdym;Or2oKDVy{a325Dw{*$oE} z^c(7MAP8Cn?Jc1Ax26_#tK@@(plpWTBo&>&)e^iW%;(y@yS>7wug;hD?K)C%gE%oi z{?CNP)u8X0^F0sS`WL8fFJ_*yBYq^KmY+=3?kN==JF;{nC_IrSu9@)W2S(4w>l%3B z)tBNM7fx+DDpS(yX8bugnKSRGc)|QJG7qtX9C}<1EKt#Gwl*Nbn=0S7<7k_kp!fgK z*{jCx-~6cbL88EVQ0%$zaj4!U5_=!nD@mRqcTyQ5$xYx|7lC2YJw3(7uQcoAEXY^P zmpn-?Hznm~+qqv49IA)?X?z;q4|=IlKs`^(Z0RBFOcUd9T5nrK69XxbG0?-cHH?>q zdKe4_-0)K=A-P`22yYYe(X^~h5I&^37;^fTQ&qu!;_ueh%_`=%t(qYbCZR~77eaZ&FU zGy@|K=A(FN%mH678_8pI-RV2m;L{GJHZLSvRv8ar&l~d(r)9K7HsvEF@-SsdjDIE~ z_*bAnknrY6D<@jl#x+ydYJ zI^QJsf0R}-Xr9L};S;{vOIG;TE3f7v#Xj$1Cr^5Fk)&{F*TQ3DMHw&bkrifhy`J;>|f!E~=ipbTKee(Q9TYeA;ibI;kpqL_ZSK zEFGViC{%K9a6|Uq3#8DG3T2@7kERE4i4{0i>_#?QkKPd5JLOYr2->5<%a)yxCm!>8 z-(A8t>QhucEn%Xa05{7c(T!%dNC6#C>pz25r8{-##K%#so#Vo=I(qG^wa zaAI5#6zMLkQJ-j|vb+bzmxmCq*TUv=cwj5#A+k|Nwd+voK81$^~p{$Fws_yXytSW6&>a1!1p=5Ibo!8=P z?-$HvGG80<;;`ldG%G&7FH)aeW&k+@0;_(9rtO#MJVz|-MzUhDI{>(0r_i@OQNuV@ z60G-`rYbITqy+DR49bjphoC%m)7yp_qkZA;^FoA^=Re}>ly@I_6sh+3ZPj_7y31?>}dp_eK6nrtYEtE6rSK-xbioIUA%=9hc# zZ_1-$(hFme=AGYe%w}j~_tgH5OACRwST51FwLTcchGgeS{l=q5mY;E!yg9M$YqK{> z4T`kFy3LJ8V*VDB`sQsWDc)qZj~dF6;_VhZhKG9@$p%e0wr(`x)F$RzSiT_yJ^Cf& zbk77lugM=6wN%P_DF$}g_`_gze*6&^$9UG70N5DBGsRo^A zUL!_uCzzuI_CbjI)x&>JASz%+zs2pyfkDky6(b55?ybHm=_ zyUK<2Dqt;kdh!KlhdgrLA=(x@?b4_MgxsJ5=3hibz}CZ<7-RSee~8AL60@hIqbh`VPl zjz^i><1>wd1-U3Eu+lD3u^?^vbq8<_F_}@Xd?J(GYK7JG@W^DGs!nH7*81LuY(*#~ z-OKYcMv4oyVi3)W?wzN563u9AejYRr8TdV8!b0juoe?QZ2D(<>sSI&`Gdsh{I7ySu zMQ4<1P&QJB305&Y3px)&5QlKR2()=LNlG%{c=#<*Px$;|CSMwQnnCj3ehHcVnw6Dv z(MTq&g4bJ{6hW__A`-RTT)z{;ZT@;T|A&i@r__p`TLrk(=L_J!9z>B2?K!KtaivET zcK)ylSn@qwS%38=f?ZoH+=_D05S^@&&B1g7v~o8rFu{up+$FAVNt7YKzkp^CEb@wy ztp;Z|73FpGRh>a`7v*bBNDokgsW$Ye0uME*L zB)tOX2+vEJee3R531cjF#V3Oo#>Nu|#^py|$neVnzc8M5lqRed zoDggtC->UCwxEexF=5k8qbda6Txb4#wp)JFD`8CuqKJqEpRD>Z7*f4k4}7XUD+=a| z^3Q25D6Bka%T5L40xf^>?1S`U%nO%$m$>(bF(cBiO7$l9`R7!>t_$avzN4%kQ@?G! z))Gt-_&pp&*O`~*ue=`p)nDgSuW4_JmdTjKaK~MleLUH^)IJvL@9f;*DyHPrEV@%% zf`k0qX_F<$v57Tf1g`|!c`OceufZzRMuO(p|_W>xqVBr(h-M z(88U^=geIX(LV!19Zs)HRzj}ZOrK7A6>x=TX7dcjyas2jyC%fqnc^q;s(=?wghh?e zF*}u*1@;u-QW%9?w*h#T#Xd7xVp+_t1-3ShoY!&E=h*uL?t5+y$TK3yG;wUeJ`KwR z5{M!r?!#J6*q3{6gZ!-@#ag zyz$3dcbRA?&#Q`DP!|)|OY^YyK4uhLLE;OO$yK`)7YXHgoI0^TB-Gr{E7)(;Nw5`! z(O)%L;+lNX$j#}gA$XS?$V=Gi$gN7UqqV}%97QL4(vorJI-H`qg+H}%-a4+2YnltI zDjp2WYZ&Dr$xAN}=UCmPp#JZxb!kt*fJjQ95T?F6+{!EZI7En^$Bm zPEJ%bFCK}|RLtjfR7Um?+ReqaK?(Ngp*pxAmu8NNHsAhIzWiK1ny*$Qm0mL9vCynI zY$$pt0=X|xLnX>R+bttud>_E{gsx~ussD{4kn4BnORq{FxVJX@`Lvkp3f`Y-0B;d1 zZ#-MguHPfQ$T+ty6<=6S2&(Y@4iC=kF7a$bM!&WcY^IM{sgHJR6Oz@0kYaj@ZIu~3H{bEUgRz1QgL&`Br1FGT8!Tl)kafZTXmPq?&|tR(jZwJ7dt(K4Z*WBhWz|4Q zglk+c{{Hz@IF}N)`Uuzz(2z>hA)89_q(s$r?^usALX>1pXG&!UppNt?<yx^{rpT19;vIxa0 z#@Ex0Q!+pp0c=N}W3`9G=Qa(f?jE>)O&_jlq*CHy^ieuPE zs8=y_X}uc86@lvJOw92Ht)iV8(!+vc#=Pm!p=a@^K69dcH4JIo2BsdMFNW5CBx9bb z`vG%@k~mNo>A}2Hx;BvEK*D`zXeXrpRLBi3IecxRwZycV z(OX#cp{|1Dt-~)Op(R6All8xq{3wDfBkCcLgx(40b!@sDJ}S*0=%pIj$?bsc=fSV@ zw>~PHBzXf@KNbeX{J}p)UAXVwbT#Pih2*xt>lFl@rN`gdF;thwYkCdcjV;m$?TE1# z2DuyE!v&m)d&&3WODLN8yiOAu$=h(CfP^j`s>=I-%RE^Or16Fopnua8`qe?6hj*`+ z$+VGJTBARC>D<2epbXF1Q>0x%*5O~1E5j!#AtS<_`T5J}oVyqkLjyZ7Yt}#@N@qm> z{A{w-#-q*1?1f~4hIZCKN9TLr_Pdy{@%iM`_$d-5Y?n@xP91sib%WHeD$H?A#mJ4v z?)cZNq8$1!^VO=$8vBdbwZD~m0q9-wPu~AV(pI2jj-cE*-7Wp*C{Z?ZV`Sk#%)-3_ z58r9Qi%4VIJfbh;h$UR39`!H5NRyT*b&duLq6Lr9BdH7a4Y#nv`Y6uV`NL|k^9lCT z&*LZRLIs%}hKQmPQu>!~^T9^WvV)Te`f1ZUC_d_J0PR1B6M#292%Y|GyP=%#%^GLF zzz%!1T>alOMmE4MHN#?U(Zkq>{(#IUQKq$89aEKKgKk`batHdviuYpEGr2oEXoLs2s~8wTA@J4dnB; z4M2wsp_t-`$p-zr_iA)SN!qHrj$!(z!ByphgN#=-&eXy$56$vR_@dSVSEZ!%V?3rd z;J6eKhP~+%2m2~Uec!^l(Ij~Ql!We%H-Qy)#?FC;NUN1csrp+-A{as4F`;d}qeccf z+4{7VKIR2~onjz3A0rriB_V?6{;{LgFi+qxGb9frwb-EZ+X_=%Jir>XFz3U?WA(BmliT5peYOE-{P#)k7q920m$%C)pOro3Eirx6 zU2gG*h@o{58=OZduO!b;Y19Q1L!yt;(tA6qUu!KuMfS_2IT=wiOg&~-{%~j2WY5wM zm$~kQRUSIe#bZp#{oqZTBw!BGh?-xOlr~YlJQcn`^28_@SBX({xXscKL1BW8(hKoj zivFBtHT!DVy(fCArFSvsiou?OG2#1EmSP{%+qu!$!2f-RZxDtv_;{ycJX z#{uL_sw;Brpws$`J2oTI@9QJ}uz2*9?7qALlqopJqsTPY;6h*+hEayY4ZvE}Hy;Lg zN>a@hlLri;Km+m&BO&VyJPEYFzCOrMSTi8 z!KgGiSJe%k=ZQ)odh1k$Pd04-v3vdE@%rqRF3-c`I=TN=+`hmM##S}C@|`N3$RN!a zLQUeMrbdp(Vrh(~6-*i!90?{(bcc2&cG9~h)Kojg69r8-Y%F-wO71hi!Y_|)S)Kt+8q`(Yn=LBTdfIIF^;wjs%=nU4O-|2YKmH<7jji#xTMs^#pap^gEOZ zUoy30&?^q2c}p1r@t=u(>}eQ~qgK4=S&n?KX-4<2^l2JTh&|xsybr*1FWhq9!a+IH z_Y%}U?jZxF_qWA9(m_4G%u8S{dho_N5^&6?!Sx{tM-9_@JFW%p3K;mk@#=-J`lsb! zC%jQ?eWXMdGu=oBB5yQVe*u!xyb}Vk_t4b+Lwd6u-e_$C^@}JuS#qIVm~AF?j|M8E z@Yj{`r@wrG?T$UH?keAVjghC6ID|-d62i2k+~up zJQUSZj!=G|twK-o&?9C)-R}5t>Q^oIC7)1TKPR_0c5-r+NZ<3JtrfWBMDX#9O}(ai zI2om-(iB|sY3gk?Xwv85>z^<*IwuG>6#8{^MXGf7qc<5~ZGH3c3gn%iG0t1OC6ILk z%foq-i^?KfGO>o6)f1H%fhe9jo0Lvs({P&5(WdY&QL<=ZFkGuor?x`=B%|{S$aXyl1XuT=>u|~T45N9emmvaQ z)ZC$jtYl5hwaP2?ltRVfNk6;M(B?T4`SAjJJ3JIqfovG{gfeFIVcAVn9=up|b^~k) zm&S4mbgF^BF!+7r>RR~V6OM-7_(Wrvr}QJHjeq)h5+HwqCz}vHLBW9cyrsy;Q^CMZ zxO(g|_aLK)lzyscO*_AF7Gu{H)47#;7vs_WCM$f;$AcF|K`783xzN@JzJ#5v*q#V= zf`cTsPk(=nR^8DrRJzrh%K>LCOT+rtNBj~C%77!nd+R7d%t23nX(_ygkGZFhXxN@* zv?TSSO>*4opc@3&fLN=;<7x@BO15F$lL2<4VVU!9zN@$&GJG_vSegt|(Xc=Q0n82X zTvd0T3Qr+Z_O}2|In0L!Z!zPCDe4V&0Thl9hBDAA0%&iKnSvR=Br;Zv7O7EtBFAZ zsIc46-ypF+#yqtr)AdY7_#WE;kUBngC}y%)e~*$MQ2X?T`N%E5?ghCUWZanD#|vT< zN;MOBQ=sD(o=Q%ygEq1zPHbDv-5#{h`uhjqJU}$XNTHkPa4I#XE6q==VXvlPGCDb! zEzC-386U(jrVj;A##Dmpzs5R3wC7s9;bXBelMiAz{zgXxh0=;GZx4gUS{=ZD_2=oy zhC5GV7YPlsD^*Bh_ zI5?t5;fyx|m4lT`2pI1Ehx3i{!*yLGzY4nOgJLtD#BKNxTn=K4zcJ*3T;6h`L|b2H zuFFcyq|l&|u?JDRd_EbM2M!8va{t%nJJ>F|sttqtl9c@$rOP;GF^cKah$Jwf?nr-D zjorFSSeFT!wJ6V2x=CxxT65>Nn`&}uY<5e#-g7Yuj&6&q$40oyj1H2??SfXbowEA! z`vqa$nB{{RnlF(Qq8r>POE~xCwqQn?j}B*7WlQ>|0^x8fYlRM6|C1OQeen&d z;6rO9BVw}mP*TQjhK))350L~lB(L2EaXzNzxSd~_8j6GdZNUna^~AP_4m z{3l>`bAKXYshOrtYY=GS&61X64;){D4w zCWmQb#FsDFfM4m4n;Ou$)A|8JlwI8gSxy0N;jPKw`bGD#ro?_ztCkv!2!Et#F2pu0 zf}WVV^8ANq3Hp0K9{YJnq8(|_@=_LlaA{aw_gE?(85OxpV%zmAR#P)kz?XK9Yc)c* zVG+!;&rPx|9=VpbzKL6b%$}1EQN_euR2+WgZPdD&kMD6M7RL;G=BjcIezF6e7uH)Y zr!fbmfgTm86!bLz?$@Zbo>2eIO-%Aa@{qn$bki>=4tVyVs`858{KOd_SFq3u+z(J@ z$W1*tqnbLgH|KScPS}N6cl_MOHN^#$-ny%nF8FK61$&spTcx=3wkcCu`0AsnacEq|5NE+U@PoG(9_PTXD_D9HQ-EtlGv+_zbOrRG+0|OD@jrSL zt+;NZ^@;soV4+f7Rp(2v{?BdwU|+xwcCw5=9qkvXF}u!ktfiCwTMq;zUVsYuW}zI_ zXG|)W{c>fZxM24wRE3yMpt$2m>?&^`p{sCQz_%9(g_kji4 zuwHt4x^^wj>cRNaGF9<<&(U!mw#N4E>Rid9b*A*yj50jqe5{_TuXbTbGVR-oNmuO-R8c{=9#v>G-v+usooER{}7D){%RH z8EwL&AM$)*-YSfZvr%UfOhdc!TL;d;w!Hj^3+K%0mS5}?z-R3xISAXZ%Q0X~lV0^3 zpL@JJG@%gO)H7}yL(2fkZhE(!zepCrIG%lLcEZ1qKk1APCXU|~?oHNMP=U?l&OzR$ zfP;6C8?wJER3+=Kr^ZiBQEy#cb2L&2I2@eOUMvkPg(mJB*tP!S!5UMoU70u3H#_5i z@8lf1H^~(Yy%&zPPHTK!qd|%L19ca8S>9vF>^FeP z5YA@oUP|kY%}dAytOrYS((k@&A63UXiARz14K%}${Z!5kGB+a_L)A=%9r6q>a>B-DLe zN8avh`U#PdSbpPfj_DpU3albw#0i1Geb`LI-N!04OL@XS6t4{Kb{uF0q@no^fBd1U z^pv+kX>T=13Vx$`#_}5LxjU=f)5@s0-55g!dhZ)>$9uTu8S8fD&zI&q9N?gw$aq}V zf@2f#IE77JNW)}S+uF8P(nf=WPz7a$h6|P0g}qt}@YZdR$MOhigVHh`Z-siq6qqj_ zM;gMu&P>Y_zTpuPyDZ$_cUbDmdNaPN+W0VRfMQXn?7@4dR;`0joVa{8Pdy8TSWrW~ zp@W+}1DzK#6Fy;E4aCZ7SMgj*VzE)=j;7UTMtY7c!%`G6tvkYp%HCNSX61itzE&Fl zu)T8LiA0IZ6DkFrk@<{nh13F@1)1U9_$k!yw%R)3PN319Q32v!Nwc$QJJZ>9e%kPK z_C$v|l$E(!wK7JpKfN@-Sr2I>#Z9uARc1}@P_m8TcF4=qz}CFXPg3Y2I7D)r+AM!4 zHdEhp2OL{F;0n*q%#3OJP2UwnVitf|vyRkV!d(u|*>5l($~H_LVdl3nw?IyV4GGBG z#M-s+p~-+it+HO<(n+OxLYvU=wQp&{HDp3K-Uj2phzko>V z)y4*!g&OkT_c^0gZ8V&H4rg5OKdfV+2YHz&m)PZVh1*w|V^(Rj@whS*Vf2PF zMSJrJX^=|`)5b}EczZ0FF|IyZ&f;cEf0FnBN$oW>fxPqp(hnZf6>i_08naHLk7Jij zgt6dPQMg;t7;j1AaEs@w(%EBtQHOQQ{OPoK{^^F&zCk;GiTV~uz#aLdm7gb=wfS!t zdKC``>?OwaZ+eT>8C=aEPl@?UAyEs(G=yPserjTn4QN;Y0)s#*sR6#2 zup?G|#l=naXBLkQtJaw6p;aw@VUf}ED?wrn8Vpywx)rjVCa=r3iH$Gwmlj`V1rVg2 zPFJ)Bl)Tb}Ycyd=AVSF|Henef>M{e{R$oBYuE|vpC*DZ9y6~vS!f7ly0B62bS1PNi zP2no550JZ8(8+O(gsl+G-vGcjYi=j}rrMx{#|16_qj%}7_)1m=n#FZnErgvm=l?cm zQ?3iS$&jbNv2m0HXLRTaZoN)v83W%?##YO{A;{V}JCdJ6K^8SL^x-fpUHj9qV? zMxv*_ODI$`yx|ncGS$6>dJjmrW-pKIM!rMS7wmevAAw;f*myAdeAK9i8C6^6EJy%< zog#NUFT)5Q9tcTQjVkl61~RmC61ulT*5rrez@hIGdfSaR=NC;;HXHdi6}Q(ODnR6? zXd#f;*ZFtq=D<^_K$Z4KPkwKxcvIq z%H%&-Lgc9hS1jcJZuAj||HJ4b4FAU?x!aRp{~t=^W@5`9NB!~F G-~S)VTE(vb literal 0 HcmV?d00001 diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..abe70cc44a4e47e78c3a9bebe3a26dc134a98ffd GIT binary patch literal 718 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 literal 0 HcmV?d00001 diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..0e9616e48ee9b13abfb5f68d2f967121e212df90 GIT binary patch literal 1335 zcmV-71<3k|P)x zOKeqD6o&tM?xhID){3Eq1c<1Bs6_^bM``d0F(wXlAOw-biHVOmAr6d89T+CYN8-dp zk$?jom>2_56pIQvP^%!ILcoMrBGy()ZRx$&?_c|z+fr#{;Ec8RdY*IEchq1JU61I>Mxg7?@+U3TAXDglzgR6U|EP|91ph@bfz@tDX1G;Xtg!i_eAkoa*9=uIErP!n842STsv9H?UC~Y6#I-t9mV(u~AHoSk$QjOiR~%y7((6M z;$edf$bHRq$2Dr{20Vck0lI}lrU7DTs2ke@$#S?~qKLT@EyT?^Oansm7`Q>s!=oX1 zpiF82D^0i}A7e1ofnD!J=7A=;mkv z%F2f0t;BI3%S}8W5sgp@*5HO~QhQt|DCLl_jRZ#LWQ0F`n z4QfnO1!PEV)E=z@a!%X;`ZS3B(AcDa0%~s%KBhv;&H*_uS1M?#*MU_5RYykw0RvJs z(6JRr-MM`MWlsuiYj4$zwr2fy{EW&>Xkb>ZX#VUr-M4JMu9`7bf1VxIhX;=7*r`4s z30!5uYp+PQe8pjKa!Hc8y2ei0rffjY%sBd>4)TzV0QQNrY7QOQHJ=}Sr zJoWNN+P3=#jot@=!N#OaHg$TFy4Eez zwAN`l@a{JnI5VhUcRs0BCY&h!Ogv}Zt7@6qtYr@^)lmPCx_2JX@VOCSX~LM)|28Bj z$IG1X5PnCd#qgVEYUX(F+SIA@!{^n#<7@r#-ZR{Dak=`zm(;cXc1@W&MSI@-T!S2M z%@m;i)A6}FwQoRU1B{hV>le6b)dDSCd4mq^J*u7;9_FEoOUHBXXz}U=I`YNO`f=aU z05zucBF7;GwB!ts7a&6+s(s!yTD5+;22Ky?({1~;YwLP-b16MK=C-#GN!>js zwRZCk-LY}GT4uFq-;Vw2J@I=?8Bl;;2CDA?%4n$b30cR#k8qaD>J9M|Z` zs2*M0q1QLx&rMSE+7~sr>scb{@vR@|tzA8;G*xuxrcREyqL1I$%NKl(&h!o_66fpg zd`JPyO-zX{z4unlTQW!8+rL!*>A#}Z)-p{;-hM(ozns>Z&D*tm>jrgPKT8WAc};x- zLo}_cb=H;Y+OUEz_m~cTb~tLDmY~>B{QoI|n}$*bttP(SrYo9sjxUjvOH|PZRxOOL zb?hqgXPacp73t_>76!m;|zL`aFbT z=M^+H;je&{-_IUN@cdesbb(Sx14`tL*rI7SOt3L_{utup%RLb>J_W@dXSjwHiq=L_(4uOY;C%=f&&`xe#iJhL)mCM$LI~C7dr89`P$gAz)UE zUk!=cnEV@>OK?IO0YdDz4k1o0apU5U6i;9^WKw7$4Y(V#$Hb3~D@1BTnB1iDpU{Rt t4}?1s6QKA!$ENrar6u|G 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 literal 0 HcmV?d00001 diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..7cbbbf07d6d535a506ff5c57410f954d2fd03a17 GIT binary patch literal 11334 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 ZA8Ky+UI))jmKF9dU2T1>lKa+g{sTyOqOSk| literal 0 HcmV?d00001 diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..5184411ad20d334a36eacc76d4a71cb9828ca986 GIT binary patch literal 5614 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 NgQu&X%Q~loCIDoB1;zjX literal 0 HcmV?d00001 diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..a2a9f9588ec4dac40db3a9f83b7e77ec8efab597 GIT binary patch literal 20045 zcmZU)Ra~3V(mhh2jn^ZpB?viWYZw3-0dP;!qqyad(2d`n=7XV0_t%QVsT7{O|J?8TS31P~qs!8zll+NpTIg zjN^9H`5)R}vfRB12;PMg#)>oG!ihuEzTv29;OPF5NA#Nyqm@#0{;XK6*t{ZJOz!Nm zh#F$~4e{M%Uc*}!v_WBrzi+6@C_FzBOa9lBM{S!^(CRJOYo0IV)2Tz|wXn_c&l}*W z=a2R^m7px`lZ)OIR*T*=o`P>K>n!}iS;5C`@=T#@_0s^Y6n`1R;N6$;#9XO3rT6*Y z1w~OU-6)-r#U9Ql8(V(NUtvvv4rml_`&}wRATEnj5c*kXY*NX}F+D~P_)n?|*&3D> zKEvTr%hL6=1DkENRf}3mZJqV&YPL#PK083E*-iUi(VQcTAgr^8|9okS>>rLbu7 z;nq_u+Oi&fecXu?bf_rjPjOS99b9ny0j}qmIF(3a8km@J+!hwQPB-rVrHX;e(K|JF z!QOYuISe$-o45DP%}YwhSaIx-{3Rd@ijjaueyw7>>U&IPx56Q;Op8&oDEoS)3)t=$ zI{t!1;=QE#8Jj){+fnlI%{XYKlvqP`N{qgEEu+n{I%gaHfXac&gETs0j!c2~aju5S zT=&CcA1CoDP2agy@Xk+Ao%VGm2PN$IjD;95p1@SxW1P;Di8zG=R(ENySj^vGrHv zs1c<LT2y}tJSOa%sMN_sf;FsQ`=NlxQRAfqYNl4^vgx0OmCd6ZKTz_Rlm!wZS0Ewf znYZPQlCbW=sSo*;p<|9f54RneP$N5^UX3eEt@Pf3^pA=PE46$^_;H`;Jt7ozKD}Wg zl|M6q)`36q9TzcSP!hhEh|X^BK0N(AH%ZRiL1xEaT;B9cq(jA=*qdkBC`j}!dG}xN z6d!SENJ8QVff|VzOxAYsJSYgm4*z|+HQ!XC8S9+1x+Y6*N-ZjqaVPROK_i(Ho2w9h z9UZwsHhyA^2l(?O{2UE88EiF#=%<4aBm5g!-%dP+Ndm zPG7=}4@7{rxlY;56(|ydHK`Z`RJCMgg=$SwEK%UI#i`*L|6FdqrR8TtVa97xpFKHR zx`-mSJ!(o-+A6h9EizI+zj1)leSS+7Q`@G3v1=rV=%AIC#EPh_1x*Y?8e?hz%(65k zCENhLygrPj=3$e9t>Y~Cb!8_o^X(XOHe@^8P;hlg?dZF4qRj%_#Za8Ip6?Lb}KH@wcRsGIGMwk*{um-&ecFW-T+g-%0eLU!RlVdrgG_(n7*! zRhx6fp>LQHmDUQ$(6&^WO51pFDwIcF-1AQ`X-Su?QyI==ut4W+dUGC}_lW77N-PQW}}H_rHywr9!MhHjX-Aaa9DgIfKyLXG6GOn6z_ zZ1Bq1zHpfbmh4B9y1DvMp~=2@fK+f;lJR5_)i5ZhX?i+|rZ4<6R4=-~FC)~AL-N|> z6ShN+c%&6#>hD}A9d<5OKKqaIUJ{F{$ZmSd+_Xu`H7+b=w8v$Xl3;_miO~4@9mh{v zCTxnP1V1OaQ=nLv7F7{Ye`R%62BgXNyeBEwu-xZq^YgE%#*2zJXYgDP@ZJ`|vZLtM zvhQ6a9Y;Fvxb2mwvucX(S)C5?9R%fe048^K*a^shr-6gq& zB=7JWgyzza`M3N?o3gXofu0GYaCm+ju>>D0UA+7mEuCem=}DK$95c1^55+Jr>1)4j zG-eDuO#?#g6-HP1 zfiBa`Zr6}5GF|C^VnaZ4IYH28m!`SUb#I79Aj20nD?~Sa9tcFzlP(naRUXHVvq`l@ z!(OY5eeAd_yn6R@Qa0YIh>fd0wDV_qY=|_$6rc_9x*K!&!@*iYXl4|X{`-R??ELT9 zTD507+*z80Bp49iAES-IPL#IeK3n@gDpdXt2R$#XhS?yq)9x7RUA(1-E=U@u07JY) zHMOwPZ>~9tV5SxZpaQKvRUa}--U-|lw+8W2@*`)P5H%+8ftHtAsQf=eO7-Fs2Q(Gx z_xYxU$fkKux)HLYxVp(Ya`R(gStK}q*jTt4NJI~FIQWuAlZ?a9PfgJ%32ej>(J(lM z3@}hZ_P93R&5HU|8tVwz_D z%kBFw8baQVj+m)2A}wMD$0pOP^+#MK6kvX;XjyN=%4w~V((!+``-%5>S&87#{;XPF z%J*3>zCk?nU8jgZJ!L|JJ{D8tu?vAAD=aeMcexBzZy({_vrUYn8STX#SZ z5->Id$-qLi70iPM>LJSZumosLHD!ZTx=+1?7y%>Am&8H5HoGQl2t|3_xRYvE{Tb~d zy>;&iw@4}6s>1z}$ul~oPn@s~`{BHfAHPS~4S(Wt>9Yx+X(V%pq$4+~*6-lSZC@^nX#$J!GaklPk1T?cDIAd_NLs|ZT85A>_;IAdA_jb4 zDg=#vygV^^Q=}rev-pAEO_CQfrE9e#n!6h!KvZ+zIi)_QHz;+ba1ME?r3-s~?w24_ z;SPuU;|>Ov+Xdi!=C^d(YpeJ}ZTb8ZCV-1l^@j_8;||bYY(XQXSfS;HdA`~At80Q6 zXvAZ2n*hQnh!#J)VRZ9^!~QQQ-s|f4lO`1}wt>w}eo(x<tQ*#S+LWwhRQR7+^bbnQX%L$20v12Xg8qziVR#An zKubqSH7MN&*|ZOQ%o-~h<|fu2KR2orY+{@o@Fk2&KHHS1ngtF9x zQjN*8=3S)L$z^`X($0()KqSr`<)X;OWNTWy@* zmwIi|Akbwm=MOj)At;oI0%kEe>o^ol|FVQ+KLEnact71hM2+j`E&4aq7Y{Q!mT*+t zR-x!3-7o!Zw77nPxV2eyHAAx20$~=9U>+rycMtyj!aauTzJb6dtfn+$D@)z80%?&- z@-NxECDtof>a99=vFpH@ut*M}HH+7o4hsbWpu@@njAW)hr1-2^fT|Au~jsq9=II*|y7=ts!u3sky^V6F&oF;w} z59blIs9zKzrY2K0&~!O^Bm3Z3-|V3H54FY`7}2d2x^^CBIBh@IiD*QDrTfl(UH$Jb z5qabLG)2I|DqeOTs z9;0BS@3FV zTkfItvjRE}zOf4E#J%dvfduC!@XlAris3jPtNd`9i-u)=%~wV(Hb$h;@kqJK&` zkXMiGqw2MF2@!KS;Sd16;rI>11nY|&DGWSxmqs>nKY z18GRA%#8U=C7ysfoHR!&Oer7JEMA(X|EF;(S-pfut7wRp{Owy#SK4(GS@87|mD_`@ zQb%PB^oRdyi1Co*WOOl0Q5-wX=(*IAEANK^ufa0VK5l*gyB$lou-H*5G=7< zI)kzV0B5nbb(P@)){3+~qMsPGei1+5;>BtFO$3!r6mgqM&;r1`MQ}y%EF39Y!+QMm z_(|{%8(gQ-zfRku6|If|CAIamkdJy1gF2uD8Y}-vP}8$18FDeZPYkLp{{@4Yj$bKL z-CvyO34Glgg$tR!7D^0%>aTm-!In}gs%nhsKTKZWj(`l_&J;K3s`ln?NgpsEy|j$l za!|id4&IydR1H?zII>p<_*j+VynhqRsqu`htx7_&kw;Rfr+BIe+|aQN->Zf-({4uP zt^&69F@I@^rKN%1AY1OL%k)bGXgy}dw8&0f0>~tz@ByaLjfcecTo1B_i0i43cOrkSj>Wmb=D(xIrmxWXaovm1sBVEt#?$n^ z3%`|hTOm)n`NxZ08D2Khn*1GhsA-M*w0p-$GdgFHdrHxmls@9kHfu)eLVCJ@Qv1GR()~-b?+3XBZ{bXPG`3(ahDsbsK4ddF zwVmLM0*#){K#@C;%+D`Pb@SQvvKfGf z8gI))!EPW`_#6iZO|U4Vit`K`r8S{{^9Dn_FaD z`eSqdVp}3gGNe;HpF>}cOh-0+$p$M>tbu(kBFEhOnq6k_r)BUA$)q--fEYU51H(^rUvL%pCD_7<0)2<;vIb0R z(o6jl0nV<0?Y0_9O9uIYlBsI9ZF9eWWbm>>?go##6%Vd5lYQ9s>5SosLenqNb038&9=Rk!1r1>YzGY-rp=l;%qCbNGcKt#4ED1iJhaZ zSB8}gyuTEuZk;o9F{5qA&+YVW?Q?IS@D%Zi^>%Q$6o6J_r+0~_Gz#RPLI9hL&QFq+ zbQYOy_fk5nj5(93I6s{&L2C-^>97sv5da z7azm^(#nW%M2hkFeSkW}-ox{Mt1{+^>(j&XSWII<>zAQ7o|C_@Y?5rvs$(!<1ZKV6 zYFn1hvl{S}*qrCKUOR0CrVSz;BA!vJ6zyug^Ag7^nE4#&kIG7vMgk`=ZoEobuiFe_B9>@``>^;Ub-Q`x0j!osM*L6#!b>Kw@ku426?|A_Daz^%)See z0B`TB=H)EjwI^iOZQ#p-zs%m!? z$qd&?IMGxXdHBI;v>w_>B%JL6!s2ogRIS(=$hVc+2yiBDFpW-HfaQ)fa}irFkL|am z@PmP+NHXsVw#lpeG3ibGBnK;iSJ%5Va^m$Q;5#%bU3vNTL{$JCwgZMqfCySWGt5|; zoR_qr=#5$}qNu^U~lHF18)#{mI2-EN2s<$f5q*GOo^awsIHS z!*dofm=-+caF{ni$Gs5t#qlk6LWZe)rN$?RU2)^EB7JoH;=*>6yeEnMZz7%CAsIDk<02ea8y3q^`dgh7; zf_e=+KNz;HUiRzRC>ensc#1h&5E=z?`JEV+HvRF8xro-T_Kc308L$LUDuLKVe=y1! z&yi@q#Kxa9QTL{J-~hG(J8*@YBV@VBTH%s-#+#PQiQXhYFOi#rKaNMeCLpC%e5{@T zcDB(sr<$Mf#1BMDTqKI5-xm9Z*`uOlRinjIl{*NiA;pp;Y;VY;p&6nz!(l|u^&XlR zh?r*KTjBT_%-axYXcMpG9;{|4&95WvK^eHemX#w~Mdcm)Wo4NsB7mdTF~=H*aA8j= zF9nzC`pVKdt?P{|3kL>Q()5487c8;dS$*rd=fDuYLVQW#RZFMXFSzS(iz9l4t{6neF(lqme>AcvBqD6Vmk8LU%=!bMRLCgu$4 zxib6C zX&aL<#8%iLHMaBBGpnIk+k%22PI9D=xU?Y2q%U(bP4a+(3U#RHO zA9)a^*+he*v+ImKt8BxY*f;?;(FxAv*8S0I8MA>6witpjR>T#-9s#}ElJwAtQK`kz zVkB9&BrYFXzeK-Feo->6LoM#2W@)P;B)VRjtu~_$%UIPR;0~k)b=Qtd+GUiZSED&7 ze&r~rk&)2g51=j$ksM5?YA8h!*=?25VnuCY=Hv#w`yzI@QNpIU$g}{0N^$=$WIP9> z&e3L(DNu>+Sq}?_uBRkqtd5PUcO@spmy)@w4*R$uzaV0dW6b%E6jPj9n2k({SP4K@ zuQC>p6OuQfSzVk5MJe6ULpWV@%aHwuKTbNlM-bbey# zx5)7$uI$%d8`NS`Cq>6e$N5eaTCI|R-L&LWvK-OKTG zr*t@7#+PftnLcbUH%U239E=uuoK5c&-8^wK;dFSNluW>r3ZuM6sm{ofL4dewJsdU2 z)vB1rtfI`HLi)O+EbN&w>0OR28!RU``AonkvJd(VGTPkLiKWyrG+q}vHZmA;c6?B@5iRz)e@Vz-liL_ z1&X*;hS)20q|R;uv;<6|;Jw+iC5wszQ`$D2@yS{1A9kGr)`hus8<)IqbxqhQ z8_+)N8p$d4Pi1eMKL^Oi>Lp%3asS7nS`S`h8rns<*@_f$sn6vnNeJ{R0ez}`%&LYi z)^?`;1+I!x3mNeWYb!gUrnG8iv@V@l@5rK!=nR7q5Ocif1wT+z%y;Qj$%=(^8()9J!PL_#L3?-?Ak%+U)6uhM}B z$i%_6b!3}s=r(pteJZ1p^@tMvtQLqBh7doe(W;If8=9x!thUh}y!`EvkXinQZ~Ld@ zyM)G{f+zxvL!Y%F9QiqujMRW~X?zCmyh_tC4_i8?ivK`C8^-BQeEp?(R*Fu>W6t!d z?rD3woMvcNCKQFgCfQ~Z^8YLtps7NkTk2d)NQL`3ZM&ey6SMWt4Vzl>kltmz@foAwqCv@(gO_ zU)F(e*EQK=val-(#Ky%TPL@LICs*YFu!cx)pC=h?cxo+ZG`f?0dC;s*;M%&+mw7^g zR^7IzKl11FF{gQqg5DF}MAW$xgYsL)p2hO|UYJ|IieB_FRADXg47~!0UMm3sG{*7N zw&M@^jrXUn?J%!XXZ60wwfGd#eDnWtt)FB6>h%s}!*cw82tEzif)>Rl@YBtNQa+~H z0+c$I0!|wAqQ+Wx+I{X1L=@t)C5S@Y~ByCWa+T&MC1 zz5m9c<`_%M>xT7GqT({y4^j&J_bT0rc3T(O=ldf;Yu_JthO=GGe!V~a1`@p#$O^e6 zRlq2$FNh|OB2?7r(6rvmDp>m*kw|~}>j0P&4%?ZR*fCUL95i_Pfypp8 z-&048yAxu#i8R+ORQoYqmmXKYwXUY)+#}&!GZBL^HK?ro{jI}jH zP-z~Z(>`qB1HA~qZtK0!Hw4-Ai*`IcopyY`KObl_a)+#WQ8}pdGaIv5?MzKqe^i$H z^}8O(p>6M74Q3T+8@nbE%(4)DXdMw+yVDdju9T1U-DuCWd5|g`fMX+T+8u^<%HVrz zp(nDA%g}-md;zD#0~T?e{5MbIKHMnodrFbcM)iJfwRC6%+!c&f;~XE07cwp;#oTr- z7`(kfeyjrglxbgaO)^md8V$}4fy@-%yKCOP&dnV6UdxQZjKZhaBN9GWsDi63Yw~NH z2+F)()(=~mX<}dn`l7opB62ql^<{<(?5@~j@H=O?XAf&uI3EE#^{1ZdKoL=x z4s5;zPs0jzKYlOM@T6UB@1O6V7|B2iIR8WB{nuGNdTO2Qaof`qSIp{Gnw)MDzpK?^gj9v(e>u-6u+z)5{+R3_eTA5fAV#QP{BImT=Kv?X~L zy*#nox}p_(x;Gg-vo>sa6R}1rqIuSQtNU}HT5=^)6l=8$FyJF^n5_Ids;qnR;Jb7V zzE85uPzoweqaydit!G@BI0IZF(;})iuk9x*U3V5Xj;tq#_Gj4I6D4&#WpH_@bmUdN7@eY~K*6;IOhB==J2;1q#6wfKiKy7w9 zfuSS#kWHX-H;P%QpE4xtes7n9sB^1b!vqrxPo9ssD$Ou>5-=zIzp_}x3U&wIZLQin z*2X8s68&fmLW{!4iw{}ggHaZ=He5=~>f2MpMd3zw+cbyAd{a%b{f37TRkh3ehxYPL z9}Mp9QaBuZo`nv*xaFB*C8HkFTS6RKZ^x3QygzSG0$tC=wHM$YZ^PM>m zUCmguw&L45c|$)!-2an}4s2++9Z+ntrJ#yUfKse%}d@nF~dr@4tD}3{3MEc3~OgmtGel_I!Ps}S(3y7$*(C}4UOgH9} zMwYZS_GIzp>FDW&?^2z(@-6xV_+^`IB*Odjo#0*&&Hre#c5G9{_91se_3*~;F_w$> z!dIe^Tyr04#157|1b`Xz<*W=M(#Y_i z@2_tajH&0|x>>Eu(e6Q+WEL;gny~o&%J>LPAmdeEC0Z5NrqAXE|m)zTT8zb&k8c$xTovm zPu7tJ+fYc(Bd%)wqq1+kPmU~yXa2)vGyIELtNY0i{ZJutj>le2HV5ySCC3aqM>lNO z&K;Lz#wWuyNhfWN$}L0ODtiWx&q z+x;JN1;oy6HFyQ{@U}+F9gY(*E0GhZ%si+rrMNQ4)5@a7+Y)GA9BbDyn2NvoQo-WANW9H#wZ`&zO!+BS#cPGJ{8<=d#O_( zIRIX*FQ!sFpSRHO$DXE2m7$8&ULzOZeBd^{{t=Y3i7<@5R18_j83}Tj2iTk!=N8Zn@vvR`f*o zA(ia^X%$^V3z}4syLpJ)9Q#e(l<5Ai%ciyn>$1sWpoqK@pX`*Oy!~IBZK!yl{pD}a z$UEixYC*%twmA-;o8``~a|gpJr|t{J*PSJ&UH-Y}qU;T2Im#}!5u%lQ^klNN{os}a z_f8lxofj+qr=%SaSR-WWSIRIQBV2&{)Ab4^aSkJ{r!^kg0`$la7X5EP0`T7B5+Lzz zi%75S=45EOU+*egbD44uP{p6p7A?>BYpKJ=d)6 zRY+xSbG!~ZBNE1+7l9~ffiU+_2hKd2EQ2DfsNX^jy= z-sl#KwE&MzN??@Q|M0S4HCnA0o+JG&9cAX-BeS0%y~Mvu}0}Q_jd9$nG_hsMp4Eqj3^oWue>GHa4b^ZDG>cfJu>HE~$hL;H_nDImGft`em8^ro{A4{qVVsC!5@!fI=?W*InV z-|P(CQnO-Li9-aZnna-f{G!M>{uD3)l=JwGBa2xvr_vvn3~hEur%flsb(zP5^og>k z{p!{*3}H659L6xDG(UB&g$N1UA637gt`e=`Shikkt_Z*OHcp*wV0*c_PjZN|4Szl2 zi-*=B1#LV|QYLGJ0fY$(@!YsUiT@Rp_zk1ufY=XEVtN3Poj=EbjSsoRB(#Aea zZ{yJO4ZDd_-^#?O+h9fN)TpVoBA*9BcrO_}JmA(K*)YGiAo>pM`*w&#Ke?8+1|HBb zizEB^AAQN+_0UM0x7c~i0!n|_ntz|N-D{@Kb!SJ4S-hNAi-J3EpUK}{eQ34W9Oyh8 zNj|;*wKN56zFQb*cf9Y;_K{m9qQD`yY<;ruTD&O4{(V^L^U?-8MhpzD`ck&6{ag$a zW%=Dx-3$|zk`lTSNHpQS3FQtDQ>`T9hc*3kubK@;y!aWjGfIsdrjQs4#k%o%fpjtM ze|mCj!2|~n1Cjwqvso}y~fTx(vi&0jjL>Dh|hAwJ2T-c$ayk#ixr;$JD3ihe%?003gqQAkBy7jCuJTcM3ILjU>$!2z7+pGVHLA`mRsJ$1iHr{V8*T$rW7i7@%zX&n?=_OoS~amAqjSxZ_QKDOw%wz z?H`oHg8spwXcXswT_QT|+a3c>ybcU~4R~IP-)0yXv82yDZ(=XIx({k*oIq4VHq0NO zuUpNRl$LjtiNX&)&kr%y^iz(}G<)HhN3Fq0>Tn zxa~(Xj*Pz+!JI_(48v zC>0GKf1k5v{+}+Dd_WUwp_C;?=Vu4Z`%zcP3cs~EvyHCsh2M_g;l5z`lPs@bkNZJo z#}jlU>cmk3R)_hx-%lHE7%E4efbm=5QycwW5TVt>i`eP-X;=r0rOIav{bJv}N%+j?wQ}bi@=|Y5ct*ec zTJ=gYIyP=;l@f7W7DnOKbU#i@9c2-}vvv`#UU|eS1PSle~%sagJv6FA{K19{k_Nh|i ztjZ7A*aj!w<%b)}kjDGU#aC1ZM-dLV7ejpaLx*Rc<$d@55q6!EOAa6Mxd@phJRYK< z&PzhdJdsenI=$@9*VUVj=AP_m#@CCfJp_gWcZj0r6^FH_VFzY417h zXTW0@rTfu^5X6HG%Off|IDBuKQKMY4`8<~WHHY|1i_R#d zuFw5KtaBabjj&upd6NBd2pMy)8i^m{t#zSikYR8qm@+oFgh|&tBt!z%N;^*r81S_Gz58Nk?T7Ny(9`iBpWnheox=Gq?OeN9TFwYa)bvT9c7-dZ@7T}7x@WGC z>h8?Z*ZV;W_SX~5>@cAWO7HoVI3UR!hOEs=918>uUpj|&ncTuW3xZTa5{-)81=;q6o7Rr-=(~# zZLYV{eqs)CUY}J~&c&5-nCA9a2Vzm1nc9AsnExt476cxDEeNRTc(|@^TE3(vQUkWh3GAdalD(VVPIBJSAmVvpj*_TQ2ZRL77>0W%TJiU=l6PB{LHlxWg8Cmf*q_s zpDig=JY|#?)uI$Kzx(NRL5^?NA&$lOm@if_94Ub_BtB|B1Fh zB$XS+y^rbj=#q4 zGX^@0+z)NmvhWWlSwAXVU6HKTFvBD`X@cv4|8N9l^fh+8h+T|Kpg4wrk|6h^J9eGV z5T}7lgqXYhonoKE`{jP9=tIY(ENR8(qPd17w{iJbJa6G&Q0EksWxq|gI^bFtbX!;{ zqE2B7=B~O%F5MCMjT1&&yMiIuF=szDn|1;78CWa?fI6&dkz9xUAmm!Q;Kc3*(gDfS zpbFd>wda1y7fLSa%VLPv*hN|KW!R3_P*~{gW<;in#J?#iH@2iVAigKXNB7PwB`k4NitXK3`!B?jt)ou05Zm|FmJ%nk7{tK!sK7~3DW-^Mf!E37T@)3oA zLC2lFK&~L56aCreip4}oG% zKjty>P$B~aumRs!L4jTwE4+chsL#Qi|K`bNG4JR%Z*Ds=qXO=FAJHja)fm(`I};_{ zDpC|!8m{y`Q|y@{%Mmk%AX%^bD5o)E84nGfaO3zfo1+))YmOm1j1(_BcfI#Fls;bG zv)?l-OfMLwITZ)%mq>`vOs+({XhXSfVbACDG5^K>NBkd$$m+13@;kFJWD(Gz%{Cr^ zx2QIjY&V7|e`@y$dvZbOc4BOwj^4sD55N_``!za)5sX>wX>T!wn8gw<^{d~G=?{$5e9Oo-GR9ue(h>jmF-!~Lgx!+k*-ho%1`(Y=g zTW{v=RZ!<;{0>|7Pu(IRi_xe>fHza3hJmp^8{O`4O=X%dU3;t}fX2rcR!ZB_PwT?lO}HjKB>S)K><@Z zzmEh`Ezy|~&>Vi@wLvLD9F&}>r35MC)lz0`WkCphaaOu^MKec{#-R%mpr5=hg+Gk} z(xyZQpa0?myEZP0vr za>V$|+9V6yl{OlSlBH0HYJH!YKobq&2!DI7@NqMj+ZJ;pzLnv_fn{O*0$5EevNkt~ z|7cF3A&9{1AVx^I!~#^}s^c0e$uT$yD*Yz#FE z&%<6H8()0Hu?t6+kjwJlBzHk0^>G2Mk!bBmsu&6{e+b&@oS#(S*W6rSHp&5AfdR_b z+$4jEd8U+YJsP{2g#QerG8)9l2!)dzB7vhs29R~KLhxWi&@jQzrb}wK$KO=g}$*o1k+nZ2BBiUGopl4lvK&62s50;FG*qc3!$Kge*WxMezi)QCUVVg4@^Ce!_6p zcFz9v=LT&&|B%!PdgdvkwIOC3j|ql$F4fVc05KlYr`iK!k$=L+jqD^@;Z-(>Ilf<{;bt@YrR-`7`S6H}yhpvOFEfHt#9H)px z;Oosy2=9jWFq=#QwxCz3a}Jd6nGKXbwc(|4pB)7c^^{|b17eaS`oDgd1L zFrjsB5Z1l%`i6X*S)jO_8t`5^vq{J%gf`^=>*VVHnNZ*GDIF@=PH`M;tPZ_6A{;i0 z%?asHnU@kmI`iJxVG)L2oJ@r!5jNyVQefRJfwP zx-ZW}3vUeO4gPAac(5_N>+Gq}URR{Y`^cvU6C<3D=)wtTs_TE&0S`)*{hIQAZK9}K zjfyn<%O|G}DY+BED zooV-q&Xdoj6H@bn@ zV<>nfF*UqJGH~?IfU#jOU9a?*(gL;I(JR{}^m>9ZJG|3@p2Tx&1scWdyxxxsMLL!x$g@(uXcIChXtISRO9eN zur6G_9b%{z4=j;45xP?~a7@?^ciXlkcSaUnIu88L%Z^*3e5trX0z zrv2{qQ9pw0Q7O`|2QL6(SjR0hyB3px>Ss4y?F|Gu8s)M8uaSp_VILQh1CeC?+1o?A zN0L{=C)7qELp&NzEy>oE{@6`LRhj(wnxq?&JU-57UNSae*WOVJzSu zdJ2^fP5@V$iPSQ*pY_$#wLDTUop%W5FC{nDKAj;sWK6iO@)&uKUSvnKj=z9^v%yZK zm8e1UCnrDc14R$qDRVoA`kAZ4(DQ6;I9Ki9IS5W+<#+s7#Yec?L@s*VC{ z=Z_}_gVCxWsfVvs>w)~ClY0rj675F=jL2`;W{KBH9*M%b9fxl`D@cVTe5Wi4QID>2 zY|+%ZjK_jUbKI<^ZXMnK;`9mso+Cq__~mxQJNCbleFa zOpYQhNB|2mQW2=`WbH7nXACVTC@>4S$smO9W>Pqo$i2voZo^Lmm`Gm!;yepD+Ioby zdVct`9Y8)1y?ff!`@w(=`a$uR$_o&1UDZl@%tL31kGg_!&Vt@VBKlwvI<$&su2L08$TGJp zk*TQ;bk7E8xLA*8J4@6mf+)4M-YW58kceEM-$UJM&<4G)3M+$M+m}@&*$hUP7}@St zynbCnvkLLQ;~uEDFk;K&^!(j^jn)2>AK@|sqORJ$c={wd^j`q>JxJ52vc^tx2^xhY zHw{r#UtP!M~NVH2jfebZ4}6OH$*;NMfezcv^$A->ehx!h7S_ zNUIh+TA_z|%s)g(DaGH>mqGGqI^`bt;F6Qa>%-ltW@lmJyHLH8KRigHX;M?%tWGmz zi>x<+m}0Xd)WYrJrXc@iQYFZ*UthYYGm+QoH?B>sT2Zq{-%C0%qW6 ze2M0JNNz(-)Zl*nZn9NT@4Yxjz^c-h#q_G>6bBozd)*-`y=G2P2_r=1Tilc_e*hB>hs%40kGaGZ*(n1OG z3On|ff5G1>9ZCId?O&Bkc71<4%0*{7nOWLCn>k*$$0SDM$%+k6mk~|R_|jY%@hVTW zsgwynI-Vk19Jl&cRo~qqf_)XPtwp#j-Oc*>GgY@e3!5(Es>)Wni-0>~GHYh*q>eY6 z0UIgcF*(ol%j7|HTVv~jV4E>^Q~shgO3Vi)(8m%=xCxoZD!(5N{48wmxPfzS_7XIC z(e(5+?Oy3(AI0cYiwm=15RLa?)h3FBAAkBL*)!&^<&Pkjp2=kYNMyTRx)>xBC;h#n zd437$IA6P~qp%ig1z6%7Qs~d`Jkp^KZ0zIqk1L^Ot(8ikH|anQ6EI$#RBWBdbK~A%nZLizbb3BqrG1QW#kojxsUl;;C$Na zg{|pPblW^(^$dz3Oi>r$N@dz0z&d88bp$F9J0HN{7wbaMwcVdAMr_ZiIYaZod z(W%HqJ6~E$hzSqq*L52QLm!Qj)FWaCl0GC0>%D=6IFx(XLcKzAq0Gj9_M@z|;l5y8 z-jRg!<-s=el|$G{JzeGu(IaeOz4A4xQb{^-y-*5Gj}Kw_;!L^ITVB!@o4nNrU&s_& zmolcf(WM2i+pa*>m7e{aSKaW=l+TQ`4Nu~sW51gjJAtPYrEYWjiiWgGZMKg^kBT}8 zwfWgKz=;cUEgbpyYsN1uNJ)`+-P+QVw$zX|ca#9_Tq4(BiCLrNz;jfH&n{OLK1m|gnPJ})dun= zJ-&QIu?_I_meMedwfItk=hK3g3o7u=O+;OWIFsHt!rk=GS*av#{syF89Q(EXO{DW< ztD*Ht9$86nNgez3(=)&RmQwEQR!Tw5`26Y9CyoxL^x1+kebCE9s_d5!L$`-VXoJjE zVRnQE09g+eAH-TLlQ<)&Q?zUQ&QO7)a-uZ1FFRrz0}n6kgCOQs2%{8) z(-lEOy!@N+LAuUzabRL}Bo4*ZMwaiKOB<%(CogIqYmrt^9_=zF!k8WF6 zsT`D%?UTlfHX(>jlK(8~5K;ye=i2Li+IV$kaWv$Xfue{uLW?x>(G>O+Go&s5GXKc~ z&;DwBIT)hLM4dv`z{I)hzkM$N)*G^AA=*%+8=vtK80pwb+@e@*{|OZ% zH-Wxv`;dy0euPQM=sg?> zniKR8-7A-`Z~q2whND=V;74brx%020l9h@&*Sq73aJf>F)JA+<3=_w5yg68ol>C}3 zAdBH4iufKJCpsBdBqmRN6WR2@yZ+Aw+kiI-PgHjW?zyF~iTlp!w2MQfUBI3H0Whnu A?f?J) literal 0 HcmV?d00001 diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json new file mode 100644 index 0000000..0bedcf2 --- /dev/null +++ b/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "LaunchImage.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png new file mode 100644 index 0000000000000000000000000000000000000000..9da19eacad3b03bb08bbddbbf4ac48dd78b3d838 GIT binary patch literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v literal 0 HcmV?d00001 diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..9da19eacad3b03bb08bbddbbf4ac48dd78b3d838 GIT binary patch literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v literal 0 HcmV?d00001 diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..9da19eacad3b03bb08bbddbbf4ac48dd78b3d838 GIT binary patch literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v literal 0 HcmV?d00001 diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md new file mode 100644 index 0000000..89c2725 --- /dev/null +++ b/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md @@ -0,0 +1,5 @@ +# Launch Screen Assets + +You can customize the launch screen with your own desired assets by replacing the image files in this directory. + +You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. \ No newline at end of file diff --git a/ios/Runner/Base.lproj/LaunchScreen.storyboard b/ios/Runner/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..f2e259c --- /dev/null +++ b/ios/Runner/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/Runner/Base.lproj/Main.storyboard b/ios/Runner/Base.lproj/Main.storyboard new file mode 100644 index 0000000..f3c2851 --- /dev/null +++ b/ios/Runner/Base.lproj/Main.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/Runner/GoogleService-Info.plist b/ios/Runner/GoogleService-Info.plist new file mode 100644 index 0000000..b58464b --- /dev/null +++ b/ios/Runner/GoogleService-Info.plist @@ -0,0 +1,30 @@ + + + + + API_KEY + AIzaSyBoMcoZAInKHTvsmg-4jakJxzP_YnRfQ6k + GCM_SENDER_ID + 751114036897 + PLIST_VERSION + 1 + BUNDLE_ID + com.kovakyazilim.labapp + PROJECT_ID + dlslabapp + STORAGE_BUCKET + dlslabapp.firebasestorage.app + IS_ADS_ENABLED + + IS_ANALYTICS_ENABLED + + IS_APPINVITE_ENABLED + + IS_GCM_ENABLED + + IS_SIGNIN_ENABLED + + GOOGLE_APP_ID + 1:751114036897:ios:aae969759ebc15a199f2e0 + + \ No newline at end of file diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist new file mode 100644 index 0000000..5f331be --- /dev/null +++ b/ios/Runner/Info.plist @@ -0,0 +1,53 @@ + + + + + CADisableMinimumFrameDurationOnPhone + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + DLS + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + DLS + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleSignature + ???? + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSRequiresIPhoneOS + + UIApplicationSupportsIndirectInputEvents + + UIBackgroundModes + + remote-notification + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/ios/Runner/Runner-Bridging-Header.h b/ios/Runner/Runner-Bridging-Header.h new file mode 100644 index 0000000..308a2a5 --- /dev/null +++ b/ios/Runner/Runner-Bridging-Header.h @@ -0,0 +1 @@ +#import "GeneratedPluginRegistrant.h" diff --git a/ios/Runner/Runner.entitlements b/ios/Runner/Runner.entitlements new file mode 100644 index 0000000..903def2 --- /dev/null +++ b/ios/Runner/Runner.entitlements @@ -0,0 +1,8 @@ + + + + + aps-environment + development + + diff --git a/ios/Runner/RunnerDebug.entitlements b/ios/Runner/RunnerDebug.entitlements new file mode 100644 index 0000000..903def2 --- /dev/null +++ b/ios/Runner/RunnerDebug.entitlements @@ -0,0 +1,8 @@ + + + + + aps-environment + development + + diff --git a/ios/RunnerTests/RunnerTests.swift b/ios/RunnerTests/RunnerTests.swift new file mode 100644 index 0000000..86a7c3b --- /dev/null +++ b/ios/RunnerTests/RunnerTests.swift @@ -0,0 +1,12 @@ +import Flutter +import UIKit +import XCTest + +class RunnerTests: XCTestCase { + + func testExample() { + // If you add code to the Runner application, consider adding tests here. + // See https://developer.apple.com/documentation/xctest for more information about using XCTest. + } + +} diff --git a/lib/core/api/pocketbase_client.dart b/lib/core/api/pocketbase_client.dart new file mode 100644 index 0000000..78f2dc2 --- /dev/null +++ b/lib/core/api/pocketbase_client.dart @@ -0,0 +1,25 @@ +import 'package:pocketbase/pocketbase.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +const _kAuthKey = 'pb_auth'; + +class PocketBaseClient { + PocketBaseClient._({required this.pb}); + static PocketBaseClient? _instance; + static PocketBaseClient get instance => _instance!; + final PocketBase pb; + + static Future init() async { + final prefs = await SharedPreferences.getInstance(); + final stored = prefs.getString(_kAuthKey); + + final store = AsyncAuthStore( + save: (String data) => prefs.setString(_kAuthKey, data), + initial: stored, + ); + + _instance = PocketBaseClient._( + pb: PocketBase('https://pocket.kovaksoft.com', authStore: store), + ); + } +} diff --git a/lib/core/auth/auth_repository.dart b/lib/core/auth/auth_repository.dart new file mode 100644 index 0000000..a9f687b --- /dev/null +++ b/lib/core/auth/auth_repository.dart @@ -0,0 +1,100 @@ +import 'package:pocketbase/pocketbase.dart'; +import '../api/pocketbase_client.dart'; +import '../../models/tenant.dart'; +import '../../models/user_profile.dart'; + +class AuthRepository { + AuthRepository._(); + static final instance = AuthRepository._(); + + PocketBase get _pb => PocketBaseClient.instance.pb; + + Future login(String email, String password) async { + await _pb.collection('users').authWithPassword(email, password); + return _buildAuthResult(); + } + + Future logout() async { + _pb.authStore.clear(); + } + + Future isLoggedIn() async { + if (!_pb.authStore.isValid) return false; + try { + await _pb.collection('users').authRefresh(); + return true; + } catch (_) { + _pb.authStore.clear(); + return false; + } + } + + Future register({ + required String email, + required String password, + String? firstName, + String? lastName, + }) async { + await _pb.collection('users').create(body: { + 'email': email, + 'password': password, + 'passwordConfirm': password, + 'emailVisibility': true, + if (firstName != null && firstName.isNotEmpty) 'first_name': firstName, + if (lastName != null && lastName.isNotEmpty) 'last_name': lastName, + }); + return login(email, password); + } + + Future refreshSession() async { + try { + await _pb.collection('users').authRefresh(); + } catch (_) {} + return _buildAuthResult(); + } + + Future updateUserLanguage(String userId, String languageCode) async { + await _pb.collection('users').update(userId, body: { + 'preferred_language': languageCode, + }); + } + + Future updateTenant( + String id, { + String? companyName, + String? defaultCurrency, + }) async { + final body = {}; + if (companyName != null) body['company_name'] = companyName; + if (defaultCurrency != null) body['default_currency'] = defaultCurrency; + if (body.isEmpty) return; + await _pb.collection('tenants').update(id, body: body); + } + + Future _buildAuthResult() async { + final record = _pb.authStore.record!; + final user = UserProfile.fromJson(record.toJson()); + List tenants = []; + try { + tenants = await _fetchUserTenants(record.id); + } catch (_) {} + return AuthResult(user: user, tenants: tenants); + } + + Future> _fetchUserTenants(String userId) async { + final result = await _pb.collection('tenant_members').getList( + filter: 'user_id = "$userId"', + expand: 'tenant_id', + perPage: 50, + ); + return result.items + .map((r) => TenantMembership.fromJson(r.toJson())) + .toList(); + } +} + +class AuthResult { + const AuthResult({required this.user, required this.tenants}); + final UserProfile user; + final List tenants; +} diff --git a/lib/core/l10n/app_strings.dart b/lib/core/l10n/app_strings.dart new file mode 100644 index 0000000..b984a27 --- /dev/null +++ b/lib/core/l10n/app_strings.dart @@ -0,0 +1,777 @@ +// ignore_for_file: lines_longer_than_80_chars +class AppStrings { + const AppStrings({ + required this.settings, + required this.userInfo, + required this.labInfo, + required this.clinicInfo, + required this.labName, + required this.clinicName, + required this.currency, + required this.status, + required this.active, + required this.role, + required this.connections, + required this.clinicConnections, + required this.clinicConnectionsSub, + required this.labConnections, + required this.labConnectionsSub, + required this.otherMemberships, + required this.management, + required this.team, + required this.teamSub, + required this.discounts, + required this.discountsSub, + required this.reports, + required this.reportsSub, + required this.aiAssistant, + required this.aiAssistantSub, + required this.signOut, + required this.signOutTitle, + required this.signOutConfirm, + required this.cancel, + required this.save, + required this.edit, + required this.editLabInfo, + required this.editClinicInfo, + required this.labNameHint, + required this.clinicNameHint, + required this.preferences, + required this.appLanguage, + required this.languageSelection, + required this.currencySelection, + required this.languageTurkish, + required this.languageEnglish, + required this.languageRussian, + required this.languageArabic, + required this.languageGerman, + required this.type, + required this.roleOwner, + required this.roleAdmin, + required this.roleTechnician, + required this.roleDelivery, + required this.roleFinance, + required this.roleDoctor, + required this.roleMember, + required this.tenantKindClinic, + required this.tenantKindLab, + required this.signInWelcome, + required this.signInSubtitle, + required this.emailAddress, + required this.password, + required this.emailRequired, + required this.passwordRequired, + required this.signIn, + required this.noAccount, + required this.signUp, + required this.signInHeadline, + required this.signInTagline, + required this.footerCopyright, + required this.signUpTitle, + required this.signUpSubtitle, + required this.firstName, + required this.lastName, + required this.firstNameHint, + required this.lastNameHint, + required this.emailHint, + required this.passwordHint, + required this.confirmPassword, + required this.confirmPasswordHint, + required this.passwordMismatch, + required this.alreadyHaveAccount, + required this.finance, + required this.pendingReceivable, + required this.collected, + required this.pending, + required this.sortNewest, + required this.sortAmountDesc, + required this.sortAmountAsc, + required this.noPendingEntries, + required this.noPaidEntries, + required this.sort, + required this.retry, + required this.errorPrefix, + required this.laboratoryCategory, + required this.clinicCategory, + required this.jobsTitle, + required this.dashboardTitle, + required this.productsTitle, + required this.patientsTitle, + required this.close, + required this.confirm, + required this.currencyTRY, + required this.currencyUSD, + required this.currencyEUR, + required this.currencyGBP, + required this.currencyAED, + }); + + // ── General ─────────────────────────────────────────────────────────────── + final String cancel; + final String save; + final String edit; + final String preferences; + final String close; + final String confirm; + final String retry; + final String errorPrefix; + final String sort; + + // ── Settings ────────────────────────────────────────────────────────────── + final String settings; + final String userInfo; + final String labInfo; + final String clinicInfo; + final String labName; + final String clinicName; + final String currency; + final String status; + final String active; + final String role; + final String connections; + final String clinicConnections; + final String clinicConnectionsSub; + final String labConnections; + final String labConnectionsSub; + final String otherMemberships; + final String management; + final String team; + final String teamSub; + final String discounts; + final String discountsSub; + final String reports; + final String reportsSub; + final String aiAssistant; + final String aiAssistantSub; + final String signOut; + final String signOutTitle; + final String signOutConfirm; + final String editLabInfo; + final String editClinicInfo; + final String labNameHint; + final String clinicNameHint; + final String appLanguage; + final String languageSelection; + final String currencySelection; + final String languageTurkish; + final String languageEnglish; + final String languageRussian; + final String languageArabic; + final String languageGerman; + final String type; + + // ── Roles & tenant ──────────────────────────────────────────────────────── + final String roleOwner; + final String roleAdmin; + final String roleTechnician; + final String roleDelivery; + final String roleFinance; + final String roleDoctor; + final String roleMember; + final String tenantKindClinic; + final String tenantKindLab; + + // ── Auth ────────────────────────────────────────────────────────────────── + final String signInWelcome; + final String signInSubtitle; + final String emailAddress; + final String password; + final String emailRequired; + final String passwordRequired; + final String signIn; + final String noAccount; + final String signUp; + final String signInHeadline; + final String signInTagline; + final String footerCopyright; + final String signUpTitle; + final String signUpSubtitle; + final String firstName; + final String lastName; + final String firstNameHint; + final String lastNameHint; + final String emailHint; + final String passwordHint; + final String confirmPassword; + final String confirmPasswordHint; + final String passwordMismatch; + final String alreadyHaveAccount; + + // ── Finance ─────────────────────────────────────────────────────────────── + final String finance; + final String pendingReceivable; + final String collected; + final String pending; + final String sortNewest; + final String sortAmountDesc; + final String sortAmountAsc; + final String noPendingEntries; + final String noPaidEntries; + + // ── Navigation / categories ─────────────────────────────────────────────── + final String laboratoryCategory; + final String clinicCategory; + final String jobsTitle; + final String dashboardTitle; + final String productsTitle; + final String patientsTitle; + + // ── Currencies ──────────────────────────────────────────────────────────── + final String currencyTRY; + final String currencyUSD; + final String currencyEUR; + final String currencyGBP; + final String currencyAED; + + // ── Helpers ─────────────────────────────────────────────────────────────── + String tenantSelected(String name) { + if (this == ar) return '$name تم الاختيار.'; + if (this == ru) return '$name выбрана.'; + if (this == de) return '$name ausgewählt.'; + if (this == en) return '$name selected.'; + return '$name seçildi.'; + } + + static AppStrings of(String languageCode) => switch (languageCode) { + 'en' => en, + 'ru' => ru, + 'ar' => ar, + 'de' => de, + _ => tr, + }; + + // ── Turkish ─────────────────────────────────────────────────────────────── + static const tr = AppStrings( + cancel: 'İptal', + save: 'Kaydet', + edit: 'Düzenle', + preferences: 'Tercihler', + close: 'Kapat', + confirm: 'Onayla', + retry: 'Tekrar Dene', + errorPrefix: 'Hata', + sort: 'Sıralama', + settings: 'Ayarlar', + userInfo: 'Kullanıcı Bilgileri', + labInfo: 'Laboratuvar Bilgileri', + clinicInfo: 'Klinik Bilgileri', + labName: 'Laboratuvar Adı', + clinicName: 'Klinik Adı', + currency: 'Para Birimi', + status: 'Durum', + active: 'Aktif', + role: 'Rol', + connections: 'Bağlantılar', + clinicConnections: 'Klinik Bağlantıları', + clinicConnectionsSub: 'Bağlı klinikler ve istekler', + labConnections: 'Laboratuvar Bağlantıları', + labConnectionsSub: 'Bağlı lablar ve talepler', + otherMemberships: 'Diğer Üyelikler', + management: 'Yönetim', + team: 'Ekip', + teamSub: 'Üyeler ve davetler', + discounts: 'İndirimler', + discountsSub: 'Klinik ve ürün bazlı özel indirimler', + reports: 'Raporlar', + reportsSub: 'İş geçmişi, finans ve analiz', + aiAssistant: 'AI Asistan', + aiAssistantSub: 'İşler ve finans hakkında soru sor', + signOut: 'Çıkış Yap', + signOutTitle: 'Çıkış Yap', + signOutConfirm: 'Hesabınızdan çıkış yapmak istiyor musunuz?', + editLabInfo: 'Laboratuvar Bilgilerini Düzenle', + editClinicInfo: 'Klinik Bilgilerini Düzenle', + labNameHint: 'Laboratuvar adını girin', + clinicNameHint: 'Klinik adını girin', + appLanguage: 'Uygulama Dili', + languageSelection: 'Dil Seçimi', + currencySelection: 'Para Birimi Seçimi', + languageTurkish: 'Türkçe', + languageEnglish: 'English', + languageRussian: 'Русский', + languageArabic: 'العربية', + languageGerman: 'Deutsch', + type: 'Tür', + roleOwner: 'Sahibi', + roleAdmin: 'Yönetici', + roleTechnician: 'Teknisyen', + roleDelivery: 'Teslimat Elemanı', + roleFinance: 'Finans Elemanı', + roleDoctor: 'Hekim', + roleMember: 'Üye', + tenantKindClinic: 'Klinik', + tenantKindLab: 'Laboratuvar', + signInWelcome: 'Tekrar hoş geldiniz', + signInSubtitle: 'Hesabınıza giriş yapın', + emailAddress: 'E-posta adresi', + password: 'Şifre', + emailRequired: 'E-posta gereklidir', + passwordRequired: 'Şifre gereklidir', + signIn: 'Giriş Yap', + noAccount: 'Hesabın yok mu?', + signUp: 'Kayıt Ol', + signInHeadline: 'Dental Lab\nYönetimini\nBasitleştirin.', + signInTagline: 'İş takibi, klinik bağlantısı ve\ngerçek zamanlı durum izleme.', + footerCopyright: '© 2025 Dental Lab Sistemi · KovakSoft', + signUpTitle: 'Hesap Oluştur', + signUpSubtitle: 'DLS\'e kaydolun', + firstName: 'Ad', + lastName: 'Soyad', + firstNameHint: 'Adınızı girin', + lastNameHint: 'Soyadınızı girin', + emailHint: 'E-posta adresinizi girin', + passwordHint: 'Şifrenizi girin', + confirmPassword: 'Şifre Tekrar', + confirmPasswordHint: 'Şifrenizi tekrar girin', + passwordMismatch: 'Şifreler eşleşmiyor', + alreadyHaveAccount: 'Zaten hesabın var mı?', + finance: 'Finans', + pendingReceivable: 'Bekleyen Alacak', + collected: 'Tahsil Edilen', + pending: 'Bekleyen', + sortNewest: 'Yeniden Eskiye', + sortAmountDesc: 'Tutara Göre (Büyükten Küçüğe)', + sortAmountAsc: 'Tutara Göre (Küçükten Büyüğe)', + noPendingEntries: 'Bekleyen alacak yok', + noPaidEntries: 'Tahsil edilen kayıt yok', + laboratoryCategory: 'LABORATUVAR', + clinicCategory: 'KLİNİK', + jobsTitle: 'İşler', + dashboardTitle: 'Özet', + productsTitle: 'Ürünler', + patientsTitle: 'Hastalar', + currencyTRY: 'Türk Lirası (₺)', + currencyUSD: 'US Dollar (\$)', + currencyEUR: 'Euro (€)', + currencyGBP: 'British Pound (£)', + currencyAED: 'UAE Dirham (د.إ)', + ); + + // ── English ─────────────────────────────────────────────────────────────── + static const en = AppStrings( + cancel: 'Cancel', + save: 'Save', + edit: 'Edit', + preferences: 'Preferences', + close: 'Close', + confirm: 'Confirm', + retry: 'Retry', + errorPrefix: 'Error', + sort: 'Sort', + settings: 'Settings', + userInfo: 'User Information', + labInfo: 'Laboratory Information', + clinicInfo: 'Clinic Information', + labName: 'Laboratory Name', + clinicName: 'Clinic Name', + currency: 'Currency', + status: 'Status', + active: 'Active', + role: 'Role', + connections: 'Connections', + clinicConnections: 'Clinic Connections', + clinicConnectionsSub: 'Connected clinics and requests', + labConnections: 'Laboratory Connections', + labConnectionsSub: 'Connected labs and requests', + otherMemberships: 'Other Memberships', + management: 'Management', + team: 'Team', + teamSub: 'Members and invitations', + discounts: 'Discounts', + discountsSub: 'Custom discounts by clinic and product', + reports: 'Reports', + reportsSub: 'Job history, finance and analytics', + aiAssistant: 'AI Assistant', + aiAssistantSub: 'Ask about jobs and finance', + signOut: 'Sign Out', + signOutTitle: 'Sign Out', + signOutConfirm: 'Are you sure you want to sign out?', + editLabInfo: 'Edit Laboratory Info', + editClinicInfo: 'Edit Clinic Info', + labNameHint: 'Enter laboratory name', + clinicNameHint: 'Enter clinic name', + appLanguage: 'App Language', + languageSelection: 'Language Selection', + currencySelection: 'Currency Selection', + languageTurkish: 'Türkçe', + languageEnglish: 'English', + languageRussian: 'Русский', + languageArabic: 'العربية', + languageGerman: 'Deutsch', + type: 'Type', + roleOwner: 'Owner', + roleAdmin: 'Admin', + roleTechnician: 'Technician', + roleDelivery: 'Delivery Staff', + roleFinance: 'Finance Staff', + roleDoctor: 'Doctor', + roleMember: 'Member', + tenantKindClinic: 'Clinic', + tenantKindLab: 'Laboratory', + signInWelcome: 'Welcome back', + signInSubtitle: 'Sign in to your account', + emailAddress: 'Email address', + password: 'Password', + emailRequired: 'Email is required', + passwordRequired: 'Password is required', + signIn: 'Sign In', + noAccount: "Don't have an account?", + signUp: 'Sign Up', + signInHeadline: 'Simplify Dental\nLab Management.', + signInTagline: 'Job tracking, clinic connections, and\nreal-time status monitoring.', + footerCopyright: '© 2025 Dental Lab System · KovakSoft', + signUpTitle: 'Create Account', + signUpSubtitle: 'Sign up for DLS', + firstName: 'First Name', + lastName: 'Last Name', + firstNameHint: 'Enter your first name', + lastNameHint: 'Enter your last name', + emailHint: 'Enter your email address', + passwordHint: 'Enter your password', + confirmPassword: 'Confirm Password', + confirmPasswordHint: 'Re-enter your password', + passwordMismatch: 'Passwords do not match', + alreadyHaveAccount: 'Already have an account?', + finance: 'Finance', + pendingReceivable: 'Outstanding Balance', + collected: 'Collected', + pending: 'Pending', + sortNewest: 'Newest to Oldest', + sortAmountDesc: 'By Amount (High to Low)', + sortAmountAsc: 'By Amount (Low to High)', + noPendingEntries: 'No outstanding balance', + noPaidEntries: 'No collected records', + laboratoryCategory: 'LABORATORY', + clinicCategory: 'CLINIC', + jobsTitle: 'Jobs', + dashboardTitle: 'Overview', + productsTitle: 'Products', + patientsTitle: 'Patients', + currencyTRY: 'Turkish Lira (₺)', + currencyUSD: 'US Dollar (\$)', + currencyEUR: 'Euro (€)', + currencyGBP: 'British Pound (£)', + currencyAED: 'UAE Dirham (د.إ)', + ); + + // ── Russian ─────────────────────────────────────────────────────────────── + static const ru = AppStrings( + cancel: 'Отмена', + save: 'Сохранить', + edit: 'Изменить', + preferences: 'Предпочтения', + close: 'Закрыть', + confirm: 'Подтвердить', + retry: 'Повторить', + errorPrefix: 'Ошибка', + sort: 'Сортировка', + settings: 'Настройки', + userInfo: 'Информация о пользователе', + labInfo: 'Информация о лаборатории', + clinicInfo: 'Информация о клинике', + labName: 'Название лаборатории', + clinicName: 'Название клиники', + currency: 'Валюта', + status: 'Статус', + active: 'Активный', + role: 'Роль', + connections: 'Подключения', + clinicConnections: 'Подключения к клиникам', + clinicConnectionsSub: 'Подключённые клиники и запросы', + labConnections: 'Подключения к лабораториям', + labConnectionsSub: 'Подключённые лаборатории и запросы', + otherMemberships: 'Другие членства', + management: 'Управление', + team: 'Команда', + teamSub: 'Участники и приглашения', + discounts: 'Скидки', + discountsSub: 'Специальные скидки по клинике и продукту', + reports: 'Отчёты', + reportsSub: 'История заказов, финансы и аналитика', + aiAssistant: 'ИИ-ассистент', + aiAssistantSub: 'Задавайте вопросы о заказах и финансах', + signOut: 'Выйти', + signOutTitle: 'Выйти', + signOutConfirm: 'Вы уверены, что хотите выйти из аккаунта?', + editLabInfo: 'Редактировать информацию о лаборатории', + editClinicInfo: 'Редактировать информацию о клинике', + labNameHint: 'Введите название лаборатории', + clinicNameHint: 'Введите название клиники', + appLanguage: 'Язык приложения', + languageSelection: 'Выбор языка', + currencySelection: 'Выбор валюты', + languageTurkish: 'Türkçe', + languageEnglish: 'English', + languageRussian: 'Русский', + languageArabic: 'العربية', + languageGerman: 'Deutsch', + type: 'Тип', + roleOwner: 'Владелец', + roleAdmin: 'Администратор', + roleTechnician: 'Техник', + roleDelivery: 'Сотрудник доставки', + roleFinance: 'Финансовый сотрудник', + roleDoctor: 'Врач', + roleMember: 'Участник', + tenantKindClinic: 'Клиника', + tenantKindLab: 'Лаборатория', + signInWelcome: 'Добро пожаловать', + signInSubtitle: 'Войдите в свой аккаунт', + emailAddress: 'Адрес эл. почты', + password: 'Пароль', + emailRequired: 'Эл. почта обязательна', + passwordRequired: 'Пароль обязателен', + signIn: 'Войти', + noAccount: 'Нет аккаунта?', + signUp: 'Зарегистрироваться', + signInHeadline: 'Упростите управление\nзубной лабораторией.', + signInTagline: 'Отслеживание заказов, связь с клиниками\nи мониторинг в реальном времени.', + footerCopyright: '© 2025 Dental Lab System · KovakSoft', + signUpTitle: 'Создать аккаунт', + signUpSubtitle: 'Зарегистрироваться в DLS', + firstName: 'Имя', + lastName: 'Фамилия', + firstNameHint: 'Введите ваше имя', + lastNameHint: 'Введите вашу фамилию', + emailHint: 'Введите адрес эл. почты', + passwordHint: 'Введите ваш пароль', + confirmPassword: 'Подтверждение пароля', + confirmPasswordHint: 'Повторите ваш пароль', + passwordMismatch: 'Пароли не совпадают', + alreadyHaveAccount: 'Уже есть аккаунт?', + finance: 'Финансы', + pendingReceivable: 'Задолженность', + collected: 'Получено', + pending: 'Ожидающие', + sortNewest: 'Сначала новые', + sortAmountDesc: 'По сумме (убывание)', + sortAmountAsc: 'По сумме (возрастание)', + noPendingEntries: 'Нет задолженностей', + noPaidEntries: 'Нет оплаченных записей', + laboratoryCategory: 'ЛАБОРАТОРИЯ', + clinicCategory: 'КЛИНИКА', + jobsTitle: 'Заказы', + dashboardTitle: 'Обзор', + productsTitle: 'Продукты', + patientsTitle: 'Пациенты', + currencyTRY: 'Турецкая лира (₺)', + currencyUSD: 'Доллар США (\$)', + currencyEUR: 'Евро (€)', + currencyGBP: 'Британский фунт (£)', + currencyAED: 'Дирхам ОАЭ (د.إ)', + ); + + // ── Arabic ──────────────────────────────────────────────────────────────── + static const ar = AppStrings( + cancel: 'إلغاء', + save: 'حفظ', + edit: 'تعديل', + preferences: 'التفضيلات', + close: 'إغلاق', + confirm: 'تأكيد', + retry: 'إعادة المحاولة', + errorPrefix: 'خطأ', + sort: 'ترتيب', + settings: 'الإعدادات', + userInfo: 'معلومات المستخدم', + labInfo: 'معلومات المختبر', + clinicInfo: 'معلومات العيادة', + labName: 'اسم المختبر', + clinicName: 'اسم العيادة', + currency: 'العملة', + status: 'الحالة', + active: 'نشط', + role: 'الدور', + connections: 'الاتصالات', + clinicConnections: 'اتصالات العيادة', + clinicConnectionsSub: 'العيادات المتصلة والطلبات', + labConnections: 'اتصالات المختبر', + labConnectionsSub: 'المختبرات المتصلة والطلبات', + otherMemberships: 'عضويات أخرى', + management: 'الإدارة', + team: 'الفريق', + teamSub: 'الأعضاء والدعوات', + discounts: 'الخصومات', + discountsSub: 'خصومات مخصصة حسب العيادة والمنتج', + reports: 'التقارير', + reportsSub: 'تاريخ الأعمال والمالية والتحليلات', + aiAssistant: 'مساعد الذكاء الاصطناعي', + aiAssistantSub: 'اسأل عن الأعمال والمالية', + signOut: 'تسجيل الخروج', + signOutTitle: 'تسجيل الخروج', + signOutConfirm: 'هل أنت متأكد من تسجيل الخروج؟', + editLabInfo: 'تعديل معلومات المختبر', + editClinicInfo: 'تعديل معلومات العيادة', + labNameHint: 'أدخل اسم المختبر', + clinicNameHint: 'أدخل اسم العيادة', + appLanguage: 'لغة التطبيق', + languageSelection: 'اختيار اللغة', + currencySelection: 'اختيار العملة', + languageTurkish: 'Türkçe', + languageEnglish: 'English', + languageRussian: 'Русский', + languageArabic: 'العربية', + languageGerman: 'Deutsch', + type: 'النوع', + roleOwner: 'المالك', + roleAdmin: 'المسؤول', + roleTechnician: 'فني', + roleDelivery: 'موظف توصيل', + roleFinance: 'موظف مالي', + roleDoctor: 'طبيب', + roleMember: 'عضو', + tenantKindClinic: 'عيادة', + tenantKindLab: 'مختبر', + signInWelcome: 'مرحباً بعودتك', + signInSubtitle: 'سجّل دخولك إلى حسابك', + emailAddress: 'البريد الإلكتروني', + password: 'كلمة المرور', + emailRequired: 'البريد الإلكتروني مطلوب', + passwordRequired: 'كلمة المرور مطلوبة', + signIn: 'تسجيل الدخول', + noAccount: 'ليس لديك حساب؟', + signUp: 'إنشاء حساب', + signInHeadline: 'بسّط إدارة\nمختبر الأسنان.', + signInTagline: 'تتبع الأعمال والتواصل مع العيادات\nومراقبة الحالة في الوقت الفعلي.', + footerCopyright: '© 2025 Dental Lab System · KovakSoft', + signUpTitle: 'إنشاء حساب', + signUpSubtitle: 'سجّل في DLS', + firstName: 'الاسم الأول', + lastName: 'اسم العائلة', + firstNameHint: 'أدخل اسمك الأول', + lastNameHint: 'أدخل اسم عائلتك', + emailHint: 'أدخل بريدك الإلكتروني', + passwordHint: 'أدخل كلمة مرورك', + confirmPassword: 'تأكيد كلمة المرور', + confirmPasswordHint: 'أعد إدخال كلمة مرورك', + passwordMismatch: 'كلمتا المرور غير متطابقتين', + alreadyHaveAccount: 'لديك حساب بالفعل؟', + finance: 'المالية', + pendingReceivable: 'المستحقات', + collected: 'المحصّل', + pending: 'معلّق', + sortNewest: 'الأحدث أولاً', + sortAmountDesc: 'حسب المبلغ (تنازلي)', + sortAmountAsc: 'حسب المبلغ (تصاعدي)', + noPendingEntries: 'لا توجد مستحقات', + noPaidEntries: 'لا توجد سجلات محصّلة', + laboratoryCategory: 'المختبر', + clinicCategory: 'العيادة', + jobsTitle: 'الأعمال', + dashboardTitle: 'نظرة عامة', + productsTitle: 'المنتجات', + patientsTitle: 'المرضى', + currencyTRY: 'ليرة تركية (₺)', + currencyUSD: 'دولار أمريكي (\$)', + currencyEUR: 'يورو (€)', + currencyGBP: 'جنيه إسترليني (£)', + currencyAED: 'درهم إماراتي (د.إ)', + ); + + // ── German ──────────────────────────────────────────────────────────────── + static const de = AppStrings( + cancel: 'Abbrechen', + save: 'Speichern', + edit: 'Bearbeiten', + preferences: 'Einstellungen', + close: 'Schließen', + confirm: 'Bestätigen', + retry: 'Wiederholen', + errorPrefix: 'Fehler', + sort: 'Sortieren', + settings: 'Einstellungen', + userInfo: 'Benutzerinformationen', + labInfo: 'Laborinformationen', + clinicInfo: 'Klinikinformationen', + labName: 'Laborname', + clinicName: 'Klinikname', + currency: 'Währung', + status: 'Status', + active: 'Aktiv', + role: 'Rolle', + connections: 'Verbindungen', + clinicConnections: 'Klinikverbindungen', + clinicConnectionsSub: 'Verbundene Kliniken und Anfragen', + labConnections: 'Laborverbindungen', + labConnectionsSub: 'Verbundene Labore und Anfragen', + otherMemberships: 'Andere Mitgliedschaften', + management: 'Verwaltung', + team: 'Team', + teamSub: 'Mitglieder und Einladungen', + discounts: 'Rabatte', + discountsSub: 'Individuelle Rabatte nach Klinik und Produkt', + reports: 'Berichte', + reportsSub: 'Auftragsverlauf, Finanzen und Analysen', + aiAssistant: 'KI-Assistent', + aiAssistantSub: 'Fragen zu Aufträgen und Finanzen stellen', + signOut: 'Abmelden', + signOutTitle: 'Abmelden', + signOutConfirm: 'Sind Sie sicher, dass Sie sich abmelden möchten?', + editLabInfo: 'Laborinformationen bearbeiten', + editClinicInfo: 'Klinikinformationen bearbeiten', + labNameHint: 'Laborname eingeben', + clinicNameHint: 'Klinikname eingeben', + appLanguage: 'App-Sprache', + languageSelection: 'Sprachauswahl', + currencySelection: 'Währungsauswahl', + languageTurkish: 'Türkçe', + languageEnglish: 'English', + languageRussian: 'Русский', + languageArabic: 'العربية', + languageGerman: 'Deutsch', + type: 'Typ', + roleOwner: 'Inhaber', + roleAdmin: 'Administrator', + roleTechnician: 'Techniker', + roleDelivery: 'Liefermitarbeiter', + roleFinance: 'Finanzmitarbeiter', + roleDoctor: 'Arzt', + roleMember: 'Mitglied', + tenantKindClinic: 'Klinik', + tenantKindLab: 'Labor', + signInWelcome: 'Willkommen zurück', + signInSubtitle: 'Melden Sie sich in Ihrem Konto an', + emailAddress: 'E-Mail-Adresse', + password: 'Passwort', + emailRequired: 'E-Mail ist erforderlich', + passwordRequired: 'Passwort ist erforderlich', + signIn: 'Anmelden', + noAccount: 'Kein Konto?', + signUp: 'Registrieren', + signInHeadline: 'Dental-Labor-Verwaltung\nvereinfachen.', + signInTagline: 'Auftragsverfolgung, Klinikverbindungen\nund Echtzeitüberwachung.', + footerCopyright: '© 2025 Dental Lab System · KovakSoft', + signUpTitle: 'Konto erstellen', + signUpSubtitle: 'Bei DLS registrieren', + firstName: 'Vorname', + lastName: 'Nachname', + firstNameHint: 'Vornamen eingeben', + lastNameHint: 'Nachnamen eingeben', + emailHint: 'E-Mail-Adresse eingeben', + passwordHint: 'Passwort eingeben', + confirmPassword: 'Passwort bestätigen', + confirmPasswordHint: 'Passwort erneut eingeben', + passwordMismatch: 'Passwörter stimmen nicht überein', + alreadyHaveAccount: 'Haben Sie bereits ein Konto?', + finance: 'Finanzen', + pendingReceivable: 'Ausstehende Forderungen', + collected: 'Eingezogen', + pending: 'Ausstehend', + sortNewest: 'Neueste zuerst', + sortAmountDesc: 'Nach Betrag (absteigend)', + sortAmountAsc: 'Nach Betrag (aufsteigend)', + noPendingEntries: 'Keine ausstehenden Forderungen', + noPaidEntries: 'Keine eingezogenen Einträge', + laboratoryCategory: 'LABOR', + clinicCategory: 'KLINIK', + jobsTitle: 'Aufträge', + dashboardTitle: 'Übersicht', + productsTitle: 'Produkte', + patientsTitle: 'Patienten', + currencyTRY: 'Türkische Lira (₺)', + currencyUSD: 'US-Dollar (\$)', + currencyEUR: 'Euro (€)', + currencyGBP: 'Britisches Pfund (£)', + currencyAED: 'VAE-Dirham (د.إ)', + ); +} diff --git a/lib/core/providers/auth_provider.dart b/lib/core/providers/auth_provider.dart new file mode 100644 index 0000000..3fe3ee9 --- /dev/null +++ b/lib/core/providers/auth_provider.dart @@ -0,0 +1,195 @@ +import 'package:flutter/widgets.dart' show Locale; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:pocketbase/pocketbase.dart'; +import '../auth/auth_repository.dart'; +import '../services/notification_service.dart'; +import '../../models/tenant.dart'; +import '../../models/user_profile.dart'; +import 'locale_provider.dart'; + +class AuthState { + const AuthState({ + this.profile, + this.activeTenant, + this.memberships = const [], + this.isLoading = true, + this.error, + }); + + final UserProfile? profile; + final TenantMembership? activeTenant; + final List memberships; + final bool isLoading; + final String? error; + + bool get isAuthenticated => profile != null; + + AuthState copyWith({ + UserProfile? profile, + TenantMembership? activeTenant, + List? memberships, + bool? isLoading, + String? error, + bool clearError = false, + }) => + AuthState( + profile: profile ?? this.profile, + activeTenant: activeTenant ?? this.activeTenant, + memberships: memberships ?? this.memberships, + isLoading: isLoading ?? this.isLoading, + error: clearError ? null : (error ?? this.error), + ); +} + +class AuthNotifier extends StateNotifier { + AuthNotifier({this.onLocaleLoaded}) : super(const AuthState()) { + _init(); + } + + final void Function(String languageCode)? onLocaleLoaded; + final _repo = AuthRepository.instance; + + Future _init() async { + final loggedIn = await _repo.isLoggedIn(); + if (!loggedIn) { + state = const AuthState(isLoading: false); + return; + } + try { + final result = await _repo.refreshSession(); + state = AuthState( + profile: result.user, + memberships: result.tenants, + activeTenant: + result.tenants.isEmpty ? null : result.tenants.first, + isLoading: false, + ); + final isLab = result.tenants.isNotEmpty && result.tenants.first.tenant.isLab; + NotificationService.loginUser(result.user.id, isLab: isLab); + _applyLocale(result.user.preferredLanguage); + } catch (_) { + state = const AuthState(isLoading: false); + } + } + + void _applyLocale(String? code) { + if (code != null && code.isNotEmpty) { + onLocaleLoaded?.call(code); + } + } + + Future signIn(String email, String password) async { + state = state.copyWith(isLoading: true, clearError: true); + try { + final result = await _repo.login(email, password); + state = AuthState( + profile: result.user, + memberships: result.tenants, + activeTenant: + result.tenants.isEmpty ? null : result.tenants.first, + isLoading: false, + ); + final isLab = result.tenants.isNotEmpty && result.tenants.first.tenant.isLab; + NotificationService.loginUser(result.user.id, isLab: isLab); + _applyLocale(result.user.preferredLanguage); + } catch (e) { + state = state.copyWith(isLoading: false, error: _parseError(e)); + } + } + + Future register({ + required String email, + required String password, + String? firstName, + String? lastName, + }) async { + state = state.copyWith(isLoading: true, clearError: true); + try { + final result = await _repo.register( + email: email, + password: password, + firstName: firstName, + lastName: lastName, + ); + state = AuthState( + profile: result.user, + memberships: result.tenants, + activeTenant: + result.tenants.isEmpty ? null : result.tenants.first, + isLoading: false, + ); + } catch (e) { + state = state.copyWith(isLoading: false, error: _parseError(e)); + rethrow; + } + } + + Future signOut() async { + await _repo.logout(); + await NotificationService.logoutUser(); + state = const AuthState(isLoading: false); + } + + void setActiveTenant(TenantMembership membership) { + state = state.copyWith(activeTenant: membership); + } + + Future refresh() async { + try { + final result = await _repo.refreshSession(); + final currentId = state.activeTenant?.tenant.id; + final newActive = currentId != null + ? result.tenants.firstWhere( + (m) => m.tenant.id == currentId, + orElse: () => result.tenants.isNotEmpty + ? result.tenants.first + : state.activeTenant!, + ) + : (result.tenants.isNotEmpty ? result.tenants.first : null); + state = state.copyWith( + profile: result.user, + memberships: result.tenants, + activeTenant: newActive, + ); + } catch (_) {} + } + + Future updateLanguage(String languageCode) async { + final userId = state.profile?.id; + if (userId == null) return; + await _repo.updateUserLanguage(userId, languageCode); + } + + Future updateTenantInfo({ + required String tenantId, + required String companyName, + String? defaultCurrency, + }) async { + await _repo.updateTenant( + tenantId, + companyName: companyName, + defaultCurrency: defaultCurrency, + ); + await refresh(); + } + + String _parseError(Object e) { + if (e is ClientException) { + final code = e.statusCode; + if (code == 400 || code == 401 || code == 403) { + return 'E-posta veya şifre hatalı.'; + } + final msg = e.response['message'] as String? ?? ''; + if (msg.isNotEmpty) return msg; + } + return 'Bağlantı hatası. Lütfen tekrar deneyin.'; + } +} + +final authProvider = + StateNotifierProvider((ref) { + return AuthNotifier( + onLocaleLoaded: (code) => + ref.read(localeProvider.notifier).setLocale(Locale(code)), + ); +}); diff --git a/lib/core/providers/locale_provider.dart b/lib/core/providers/locale_provider.dart new file mode 100644 index 0000000..b21a1a6 --- /dev/null +++ b/lib/core/providers/locale_provider.dart @@ -0,0 +1,39 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import '../l10n/app_strings.dart'; + +const _kLocaleKey = 'app_locale'; + +class LocaleNotifier extends StateNotifier { + LocaleNotifier(Locale initial) : super(initial); + + Future setLocale(Locale locale) async { + state = locale; + final prefs = await SharedPreferences.getInstance(); + await prefs.setString(_kLocaleKey, locale.languageCode); + } + + static Future load() async { + final prefs = await SharedPreferences.getInstance(); + final code = prefs.getString(_kLocaleKey) ?? 'tr'; + return Locale(code); + } +} + +final localeProvider = StateNotifierProvider( + (ref) => LocaleNotifier(const Locale('tr')), +); + +final stringsProvider = Provider((ref) { + final locale = ref.watch(localeProvider); + return AppStrings.of(locale.languageCode); +}); + +const supportedLocales = [ + Locale('tr'), + Locale('en'), + Locale('ru'), + Locale('ar'), + Locale('de'), +]; diff --git a/lib/core/router/app_router.dart b/lib/core/router/app_router.dart new file mode 100644 index 0000000..e942c67 --- /dev/null +++ b/lib/core/router/app_router.dart @@ -0,0 +1,496 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:go_router/go_router.dart'; +import '../theme/app_theme.dart'; +import '../widgets/tooth_logo.dart'; +import '../providers/auth_provider.dart'; +import '../../models/tenant.dart'; +import '../../features/auth/sign_in_screen.dart'; +import '../../features/auth/sign_up_screen.dart'; +import '../../features/auth/onboarding_screen.dart'; +import '../../features/clinic/dashboard/clinic_dashboard_screen.dart'; +import '../../features/clinic/jobs/clinic_jobs_screen.dart'; +import '../../features/clinic/jobs/clinic_job_detail_screen.dart'; +import '../../features/clinic/jobs/new_job_screen.dart'; +import '../../features/clinic/patients/clinic_patients_screen.dart'; +import '../../features/clinic/patients/clinic_patient_detail_screen.dart'; +import '../../features/clinic/connections/clinic_connections_screen.dart'; +import '../../features/clinic/finance/clinic_finance_screen.dart'; +import '../../features/clinic/settings/clinic_settings_screen.dart'; +import '../../features/lab/dashboard/lab_dashboard_screen.dart'; +import '../../features/lab/jobs/lab_jobs_inbound_screen.dart'; +import '../../features/lab/jobs/lab_all_jobs_screen.dart'; +import '../../features/lab/jobs/lab_job_detail_screen.dart'; +import '../../features/lab/products/lab_products_screen.dart'; +import '../../features/lab/connections/lab_connections_screen.dart'; +import '../../features/lab/finance/lab_finance_screen.dart'; +import '../../features/lab/settings/lab_settings_screen.dart'; +import '../../features/shared/reports_screen.dart'; +import '../../features/shared/ai_chat_screen.dart'; +import '../../features/lab/discounts/discounts_screen.dart'; +import '../../features/lab/connections/connection_detail_screen.dart'; +import '../../models/connection.dart'; + +// Auth routes +const routeSignIn = '/sign-in'; +const routeSignUp = '/sign-up'; +const routeOnboarding = '/onboarding'; + +// Clinic routes +const routeClinicDashboard = '/clinic/dashboard'; +const routeClinicJobs = '/clinic/jobs'; +const routeClinicJobDetail = '/clinic/jobs/:jobId'; +const routeClinicJobNew = '/clinic/jobs/new'; +const routeClinicPatients = '/clinic/patients'; +const routeClinicPatientDetail = '/clinic/patients/:patientId'; +const routeClinicConnections = '/clinic/connections'; +const routeClinicFinance = '/clinic/finance'; +const routeClinicSettings = '/clinic/settings'; +const routeClinicReports = '/clinic/reports'; +const routeClinicAi = '/clinic/ai'; + +// Lab routes +const routeLabDashboard = '/lab/dashboard'; +const routeLabJobsInbound = '/lab/jobs/inbound'; +const routeLabJobsAll = '/lab/jobs'; +const routeLabJobDetail = '/lab/jobs/:jobId'; +const routeLabProducts = '/lab/products'; +const routeLabConnections = '/lab/connections'; +const routeLabFinance = '/lab/finance'; +const routeLabSettings = '/lab/settings'; +const routeLabReports = '/lab/reports'; +const routeLabAi = '/lab/ai'; +const routeLabDiscounts = '/lab/discounts'; + +List buildRoutes() => [ + GoRoute(path: routeSignIn, builder: (_, __) => const SignInScreen()), + GoRoute(path: routeSignUp, builder: (_, __) => const SignUpScreen()), + GoRoute(path: routeOnboarding, builder: (_, __) => const OnboardingScreen()), + + // ── Clinic shell ────────────────────────────────────────────────────── + ShellRoute( + builder: (context, state, child) => _ClinicShell(child: child), + routes: [ + GoRoute(path: routeClinicDashboard, builder: (_, __) => const ClinicDashboardScreen()), + GoRoute( + path: routeClinicJobs, + builder: (_, __) => const ClinicJobsScreen(), + routes: [ + GoRoute(path: 'new', builder: (_, __) => const NewJobScreen()), + GoRoute( + path: ':jobId', + builder: (_, s) => ClinicJobDetailScreen(jobId: s.pathParameters['jobId']!), + ), + ], + ), + GoRoute( + path: routeClinicPatients, + builder: (_, __) => const ClinicPatientsScreen(), + routes: [ + GoRoute( + path: ':patientId', + builder: (_, s) => ClinicPatientDetailScreen(patientId: s.pathParameters['patientId']!), + ), + ], + ), + GoRoute(path: routeClinicConnections, builder: (_, __) => const ClinicConnectionsScreen()), + GoRoute(path: routeClinicFinance, builder: (_, __) => const ClinicFinanceScreen()), + GoRoute(path: routeClinicSettings, builder: (_, __) => const ClinicSettingsScreen()), + GoRoute(path: routeClinicReports, builder: (_, __) => const ReportsScreen()), + GoRoute(path: routeClinicAi, builder: (_, __) => const AiChatScreen()), + ], + ), + + // ── Lab shell ───────────────────────────────────────────────────────── + ShellRoute( + builder: (context, state, child) => _LabShell(child: child), + routes: [ + GoRoute(path: routeLabDashboard, builder: (_, __) => const LabDashboardScreen()), + GoRoute(path: routeLabJobsInbound, builder: (_, __) => const LabJobsInboundScreen()), + GoRoute( + path: routeLabJobsAll, + builder: (_, __) => const LabAllJobsScreen(), + routes: [ + GoRoute( + path: ':jobId', + builder: (_, s) => LabJobDetailScreen(jobId: s.pathParameters['jobId']!), + ), + ], + ), + GoRoute(path: routeLabProducts, builder: (_, __) => const LabProductsScreen()), + GoRoute( + path: routeLabConnections, + builder: (_, __) => const LabConnectionsScreen(), + routes: [ + GoRoute( + path: ':connectionId/detail', + builder: (_, s) { + final extra = s.extra as Map?; + final connection = extra?['connection'] as Connection?; + final labTenantId = extra?['labTenantId'] as String? ?? ''; + if (connection == null) { + return const Scaffold( + body: Center(child: Text('Bağlantı bulunamadı')), + ); + } + return ConnectionDetailScreen( + connection: connection, labTenantId: labTenantId); + }, + ), + ], + ), + GoRoute(path: routeLabDiscounts, builder: (_, __) => const DiscountsScreen()), + GoRoute(path: routeLabFinance, builder: (_, __) => const LabFinanceScreen()), + GoRoute(path: routeLabSettings, builder: (_, __) => const LabSettingsScreen()), + GoRoute(path: routeLabReports, builder: (_, __) => const ReportsScreen()), + GoRoute(path: routeLabAi, builder: (_, __) => const AiChatScreen()), + ], + ), + ]; + +// ── Nav item descriptor ─────────────────────────────────────────────────────── + +class _NavItem { + const _NavItem({ + required this.route, + required this.icon, + required this.selectedIcon, + required this.label, + required this.visible, + }); + final String route; + final Icon icon; + final Icon selectedIcon; + final String label; + final bool Function(TenantMembership?) visible; +} + +// ── Clinic shell ────────────────────────────────────────────────────────────── + +class _ClinicShell extends ConsumerStatefulWidget { + const _ClinicShell({required this.child}); + final Widget child; + + @override + ConsumerState<_ClinicShell> createState() => _ClinicShellState(); +} + +class _ClinicShellState extends ConsumerState<_ClinicShell> { + int _index = 0; + + static final _allItems = [ + _NavItem(route: routeClinicDashboard, icon: const Icon(Icons.home_outlined), selectedIcon: const Icon(Icons.home_rounded), label: 'Ana Sayfa', visible: (_) => true), + _NavItem(route: routeClinicJobs, icon: const Icon(Icons.work_outline_rounded), selectedIcon: const Icon(Icons.work_rounded), label: 'İşler', visible: (m) => m?.showJobs ?? true), + _NavItem(route: routeClinicPatients, icon: const Icon(Icons.people_outline_rounded), selectedIcon: const Icon(Icons.people_rounded), label: 'Hastalar', visible: (m) => m?.showPatients ?? true), + _NavItem(route: routeClinicFinance, icon: const Icon(Icons.account_balance_outlined), selectedIcon: const Icon(Icons.account_balance_rounded), label: 'Finans', visible: (m) => m?.showFinance ?? true), + _NavItem(route: routeClinicSettings, icon: const Icon(Icons.settings_outlined), selectedIcon: const Icon(Icons.settings_rounded), label: 'Ayarlar', visible: (_) => true), + ]; + + @override + Widget build(BuildContext context) { + final membership = ref.watch(authProvider).activeTenant; + final items = _allItems.where((it) => it.visible(membership)).toList(); + final clampedIndex = _index.clamp(0, items.length - 1); + final isDesktop = MediaQuery.sizeOf(context).width > AppLayout.sidebarBreakpoint; + + void onTap(int i) { + setState(() => _index = i); + context.go(items[i].route); + } + + if (isDesktop) { + return Scaffold( + backgroundColor: AppColors.background, + body: Row( + children: [ + _DesktopSidebar(destinations: items, selectedIndex: clampedIndex, onTap: onTap), + Expanded(child: widget.child), + ], + ), + ); + } + + return Scaffold( + body: widget.child, + bottomNavigationBar: NavigationBar( + selectedIndex: clampedIndex, + onDestinationSelected: onTap, + destinations: [ + for (final it in items) + Semantics( + label: it.label, + button: true, + child: NavigationDestination(icon: it.icon, selectedIcon: it.selectedIcon, label: it.label), + ), + ], + ), + ); + } +} + +// ── Lab shell ───────────────────────────────────────────────────────────────── + +class _LabShell extends ConsumerStatefulWidget { + const _LabShell({required this.child}); + final Widget child; + + @override + ConsumerState<_LabShell> createState() => _LabShellState(); +} + +class _LabShellState extends ConsumerState<_LabShell> { + int _index = 0; + + static final _allItems = [ + _NavItem(route: routeLabDashboard, icon: const Icon(Icons.home_outlined), selectedIcon: const Icon(Icons.home_rounded), label: 'Ana Sayfa', visible: (_) => true), + _NavItem(route: routeLabJobsAll, icon: const Icon(Icons.work_outline_rounded), selectedIcon: const Icon(Icons.work_rounded), label: 'İşler', visible: (m) => m?.showJobs ?? true), + _NavItem(route: routeLabProducts, icon: const Icon(Icons.inventory_2_outlined), selectedIcon: const Icon(Icons.inventory_2_rounded), label: 'Ürünler', visible: (m) => m?.showProducts ?? true), + _NavItem(route: routeLabFinance, icon: const Icon(Icons.account_balance_outlined), selectedIcon: const Icon(Icons.account_balance_rounded), label: 'Finans', visible: (m) => m?.showFinance ?? true), + _NavItem(route: routeLabSettings, icon: const Icon(Icons.settings_outlined), selectedIcon: const Icon(Icons.settings_rounded), label: 'Ayarlar', visible: (_) => true), + ]; + + @override + Widget build(BuildContext context) { + final membership = ref.watch(authProvider).activeTenant; + final items = _allItems.where((it) => it.visible(membership)).toList(); + final clampedIndex = _index.clamp(0, items.length - 1); + final isDesktop = MediaQuery.sizeOf(context).width > AppLayout.sidebarBreakpoint; + + void onTap(int i) { + setState(() => _index = i); + context.go(items[i].route); + } + + if (isDesktop) { + return Scaffold( + backgroundColor: AppColors.background, + body: Row( + children: [ + _DesktopSidebar(destinations: items, selectedIndex: clampedIndex, onTap: onTap), + Expanded(child: widget.child), + ], + ), + ); + } + + return Scaffold( + body: widget.child, + bottomNavigationBar: NavigationBar( + selectedIndex: clampedIndex, + onDestinationSelected: onTap, + destinations: [ + for (final it in items) + Semantics( + label: it.label, + button: true, + child: NavigationDestination(icon: it.icon, selectedIcon: it.selectedIcon, label: it.label), + ), + ], + ), + ); + } +} + +// ── Desktop sidebar ─────────────────────────────────────────────────────────── + +class _DesktopSidebar extends StatefulWidget { + const _DesktopSidebar({ + required this.destinations, + required this.selectedIndex, + required this.onTap, + }); + + final List<_NavItem> destinations; + final int selectedIndex; + final ValueChanged onTap; + + // Must match the toolbarHeight used in desktop SliverAppBar headers + static const double headerHeight = 64; + static const double _openWidth = 220; + static const double _closedWidth = 64; + + @override + State<_DesktopSidebar> createState() => _DesktopSidebarState(); +} + +class _DesktopSidebarState extends State<_DesktopSidebar> { + bool _open = true; + + @override + Widget build(BuildContext context) { + return AnimatedContainer( + duration: const Duration(milliseconds: 220), + curve: Curves.easeInOut, + width: _open ? _DesktopSidebar._openWidth : _DesktopSidebar._closedWidth, + decoration: const BoxDecoration( + color: AppColors.surface, + border: Border(right: BorderSide(color: AppColors.border)), + boxShadow: [BoxShadow(color: Color(0x08000000), blurRadius: 8, offset: Offset(2, 0))], + ), + child: ClipRect( + child: Column( + children: [ + // Header + Container( + height: _DesktopSidebar.headerHeight, + decoration: const BoxDecoration( + gradient: LinearGradient(colors: [AppColors.primary, AppColors.accent]), + border: Border(bottom: BorderSide(color: AppColors.border)), + ), + padding: const EdgeInsets.symmetric(horizontal: 16), + child: Row( + children: [ + Container( + width: 32, + height: 32, + decoration: BoxDecoration( + color: Colors.white.withValues(alpha: 0.15), + borderRadius: BorderRadius.circular(9), + border: Border.all(color: Colors.white.withValues(alpha: 0.25)), + ), + child: const Center(child: ToothLogo(size: 18, color: Colors.white)), + ), + if (_open) ...[ + const SizedBox(width: 10), + const Text( + 'DLS', + style: TextStyle(color: Colors.white, fontSize: 17, fontWeight: FontWeight.w800, letterSpacing: 1), + ), + ], + ], + ), + ), + + // Nav items + Expanded( + child: SingleChildScrollView( + child: Column( + children: [ + const SizedBox(height: 8), + for (int i = 0; i < widget.destinations.length; i++) + _SidebarItem( + icon: widget.destinations[i].icon, + selectedIcon: widget.destinations[i].selectedIcon, + label: widget.destinations[i].label, + selected: widget.selectedIndex == i, + open: _open, + onTap: () => widget.onTap(i), + ), + const SizedBox(height: 8), + ], + ), + ), + ), + + // Toggle button + Container( + decoration: const BoxDecoration( + border: Border(top: BorderSide(color: AppColors.border)), + ), + child: Material( + color: Colors.transparent, + child: InkWell( + onTap: () => setState(() => _open = !_open), + child: SizedBox( + height: 48, + child: Row( + mainAxisAlignment: _open ? MainAxisAlignment.start : MainAxisAlignment.center, + children: [ + if (_open) const SizedBox(width: 20), + AnimatedRotation( + duration: const Duration(milliseconds: 220), + turns: _open ? 0.5 : 0, + child: const Icon(Icons.chevron_right_rounded, color: AppColors.textMuted, size: 20), + ), + if (_open) ...[ + const SizedBox(width: 8), + const Text('Daralt', style: TextStyle(fontSize: 13, fontWeight: FontWeight.w500, color: AppColors.textMuted)), + ], + ], + ), + ), + ), + ), + ), + ], + ), + ), + ); + } +} + +// ── Sidebar nav item ────────────────────────────────────────────────────────── + +class _SidebarItem extends StatelessWidget { + const _SidebarItem({ + required this.icon, + required this.selectedIcon, + required this.label, + required this.selected, + required this.open, + required this.onTap, + }); + + final Widget icon; + final Widget selectedIcon; + final String label; + final bool selected; + final bool open; + final VoidCallback onTap; + + @override + Widget build(BuildContext context) { + final item = Padding( + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2), + child: Material( + color: selected ? const Color(0xFFDBEAFE) : Colors.transparent, + borderRadius: BorderRadius.circular(10), + child: InkWell( + onTap: onTap, + borderRadius: BorderRadius.circular(10), + child: SizedBox( + height: 40, + child: open + ? Padding( + padding: const EdgeInsets.symmetric(horizontal: 12), + child: Row( + children: [ + IconTheme( + data: IconThemeData( + color: selected ? AppColors.primary : AppColors.textSecondary, + size: 20, + ), + child: selected ? selectedIcon : icon, + ), + const SizedBox(width: 12), + Text( + label, + style: TextStyle( + fontSize: 14, + fontWeight: selected ? FontWeight.w600 : FontWeight.w500, + color: selected ? AppColors.primary : AppColors.textSecondary, + ), + ), + ], + ), + ) + : Center( + child: IconTheme( + data: IconThemeData( + color: selected ? AppColors.primary : AppColors.textSecondary, + size: 20, + ), + child: selected ? selectedIcon : icon, + ), + ), + ), + ), + ), + ); + + if (!open) { + return Tooltip(message: label, preferBelow: false, child: item); + } + return item; + } +} diff --git a/lib/core/router/router_provider.dart b/lib/core/router/router_provider.dart new file mode 100644 index 0000000..379b867 --- /dev/null +++ b/lib/core/router/router_provider.dart @@ -0,0 +1,52 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:go_router/go_router.dart'; +import '../providers/auth_provider.dart'; +import 'app_router.dart'; + +// Bridges Riverpod auth state changes to GoRouter's Listenable interface +class _AuthRouterNotifier extends ChangeNotifier { + _AuthRouterNotifier(this._ref) { + _ref.listen(authProvider, (_, __) => notifyListeners()); + } + final Ref _ref; +} + +final routerProvider = Provider((ref) { + final notifier = _AuthRouterNotifier(ref); + + return GoRouter( + refreshListenable: notifier, + initialLocation: routeSignIn, + redirect: (context, state) { + final auth = ref.read(authProvider); + + if (auth.isLoading) return null; + + final loc = state.matchedLocation; + final onLoginOrRegister = loc == routeSignIn || loc == routeSignUp; + final onAuthPage = onLoginOrRegister || loc == routeOnboarding; + + if (!auth.isAuthenticated) { + return onAuthPage ? null : routeSignIn; + } + + // Authenticated but no tenant → onboarding + if (auth.activeTenant == null) { + return loc == routeOnboarding ? null : routeOnboarding; + } + + final isLab = auth.activeTenant!.tenant.isLab; + + if (onAuthPage) { + return isLab ? routeLabDashboard : routeClinicDashboard; + } + + if (isLab && loc.startsWith('/clinic')) return routeLabDashboard; + if (!isLab && loc.startsWith('/lab')) return routeClinicDashboard; + + return null; + }, + routes: buildRoutes(), + ); +}); diff --git a/lib/core/services/ai_actions.dart b/lib/core/services/ai_actions.dart new file mode 100644 index 0000000..de40cd9 --- /dev/null +++ b/lib/core/services/ai_actions.dart @@ -0,0 +1,171 @@ +import '../../features/shared/job_files_repository.dart'; +import '../../features/shared/tenant_team_repository.dart'; +import '../../models/job_file.dart'; +import '../../models/tenant.dart'; +import '../api/pocketbase_client.dart'; + +// ── Message segments ────────────────────────────────────────────────────────── + +sealed class MessageSegment {} + +class TextSegment extends MessageSegment { + TextSegment(this.text); + final String text; +} + +class ActionSegment extends MessageSegment { + ActionSegment(this.action); + final AiAction action; +} + +// ── Action model ────────────────────────────────────────────────────────────── + +class AiAction { + const AiAction({ + required this.type, + required this.params, + required this.label, + }); + final String type; + final Map params; + final String label; + + bool get isDangerous => type == 'cancel_job'; + bool get isFileAction => type == 'job_files'; +} + +// ── Action outcome ──────────────────────────────────────────────────────────── + +sealed class ActionOutcome {} + +class ActionSuccess extends ActionOutcome { + ActionSuccess(this.message); + final String message; +} + +class ActionError extends ActionOutcome { + ActionError(this.error); + final String error; +} + +class ActionFiles extends ActionOutcome { + ActionFiles(this.files); + final List files; +} + +// ── Parser ──────────────────────────────────────────────────────────────────── + +List parseSegments(String text) { + // Strip code fences wrapping tags that the AI sometimes emits. + // Handles: ```xml\n\n``` and ```\n\n``` + text = text.replaceAllMapped( + RegExp(r'```(?:xml)?\s*\n(\s*]*/>)\s*\n\s*```'), + (m) => m.group(1)!, + ); + // Also handle inline variant: ```xml ``` + text = text.replaceAllMapped( + RegExp(r'```(?:xml)?\s*(]*/>)\s*```'), + (m) => m.group(1)!, + ); + + final pattern = RegExp(r'', dotAll: true); + final segments = []; + int last = 0; + + for (final m in pattern.allMatches(text)) { + final before = text.substring(last, m.start).trim(); + if (before.isNotEmpty) segments.add(TextSegment(before)); + + final attrs = _parseAttrs(m.group(1) ?? ''); + segments.add(ActionSegment(AiAction( + type: attrs['type'] ?? '', + params: attrs, + label: attrs['label'] ?? attrs['type'] ?? 'İşlem', + ))); + last = m.end; + } + + final rest = text.substring(last).trim(); + if (rest.isNotEmpty) segments.add(TextSegment(rest)); + return segments; +} + +Map _parseAttrs(String s) { + final result = {}; + for (final m in RegExp(r'(\w+)="([^"]*)"').allMatches(s)) { + result[m.group(1)!] = m.group(2)!; + } + return result; +} + +// ── Executor ────────────────────────────────────────────────────────────────── + +class AiActionExecutor { + static final _pb = PocketBaseClient.instance.pb; + + static Future execute( + AiAction action, + TenantMembership membership, + ) async { + try { + return switch (action.type) { + 'cancel_job' => await _cancelJob(action.params), + 'mark_delivered' => await _markDelivered(action.params), + 'job_files' => await _jobFiles(action.params), + 'add_member' => await _addMember(action.params, membership), + _ => ActionError('Bilinmeyen işlem türü: ${action.type}'), + }; + } catch (e) { + final msg = e.toString(); + if (msg.length > 120) return ActionError('Sunucu hatası'); + return ActionError(msg); + } + } + + static Future _cancelJob(Map p) async { + final id = p['job_id']; + if (id == null || id.isEmpty) return ActionError('İş ID bulunamadı.'); + await _pb.collection('jobs').update(id, body: {'status': 'cancelled'}); + return ActionSuccess('İş başarıyla iptal edildi.'); + } + + static Future _markDelivered(Map p) async { + final id = p['job_id']; + if (id == null || id.isEmpty) return ActionError('İş ID bulunamadı.'); + await _pb.collection('jobs').update(id, body: {'status': 'delivered'}); + return ActionSuccess('İş teslim edildi olarak işaretlendi.'); + } + + static Future _jobFiles(Map p) async { + final id = p['job_id']; + if (id == null || id.isEmpty) return ActionError('İş ID bulunamadı.'); + final files = await JobFilesRepository.instance.listForJob(id); + if (files.isEmpty) return ActionSuccess('Bu iş için henüz dosya yüklenmemiş.'); + return ActionFiles(files); + } + + static Future _addMember( + Map p, + TenantMembership membership, + ) async { + final email = p['email']; + final firstName = p['first_name']; + final lastName = p['last_name'] ?? ''; + final role = p['role']; + final password = p['password']; + + if (email == null || firstName == null || role == null || password == null) { + return ActionError('Eksik bilgi: e-posta, ad, rol ve şifre gerekli.'); + } + + await TenantTeamRepository.instance.addMember( + tenantId: membership.tenant.id, + email: email, + password: password, + firstName: firstName, + lastName: lastName, + role: TenantMembership.parseRole(role), + ); + return ActionSuccess('$firstName $lastName ekibe eklendi.'); + } +} diff --git a/lib/core/services/ai_context_builder.dart b/lib/core/services/ai_context_builder.dart new file mode 100644 index 0000000..e99a798 --- /dev/null +++ b/lib/core/services/ai_context_builder.dart @@ -0,0 +1,226 @@ +import 'package:pocketbase/pocketbase.dart'; +import '../api/pocketbase_client.dart'; +import '../../models/tenant.dart'; + +class AiContextBuilder { + AiContextBuilder._(); + static final instance = AiContextBuilder._(); + + PocketBase get _pb => PocketBaseClient.instance.pb; + + Future build(TenantMembership membership) async { + final tenant = membership.tenant; + final tenantId = tenant.id; + final isLab = tenant.kind == TenantKind.lab; + + final now = DateTime.now(); + final dateStr = '${now.day}.${now.month}.${now.year}'; + + final results = await Future.wait([ + _fetchActiveJobs(tenantId, isLab), + _fetchRecentDelivered(tenantId, isLab), + _fetchFinance(tenantId, isLab), + _fetchTeam(tenantId), + ]); + + final actions = _actionsPrompt(isLab); + + return 'Sen DLS (Dental Lab System) uygulamasinin akilli asistanisin.\n' + '${tenant.companyName} adli ${isLab ? 'dental laboratuvarinin' : 'dis kliniginin'} verilerine erisebilirsin.\n' + 'Kullanici rolu: ${isLab ? 'LABORATUVAR' : 'KLINIK'}\n' + '\n' + 'Tarih: $dateStr\n' + '\n' + '${results[0]}\n' + '\n' + '${results[1]}\n' + '\n' + '${results[2]}\n' + '\n' + '${results[3]}\n' + '\n' + '$actions\n' + '\n' + 'Yanit kurallari:\n' + '- Turkce, kisa ve net yaz\n' + '- Sadece yukaridaki verilerden hareketle yorum yap\n' + '- Listelerde madde isareti (- ) kullan\n' + '- Onemli bilgileri **kalin** yaz\n' + '- Aksiyon etiketlerini HERZAMAN metnin sonuna koy\n' + '- ${isLab ? 'Is kodlari icin [ID:...] formatini kullan' : 'Hasta kodlari ve is durumlarini net belirt'}\n'; + } + + static String _actionsPrompt(bool isLab) { + final buf = StringBuffer(); + buf.writeln('## EYLEM YETKILERIN'); + buf.writeln('Kullanici bir islem yapmak istediginde asagidaki XML etiketlerini yanita ekle:'); + buf.writeln(''); + buf.writeln('Is dosyalarini gostermek:'); + buf.writeln(''); + buf.writeln(''); + buf.writeln('Is iptal etmek:'); + buf.writeln(''); + if (!isLab) { + buf.writeln(''); + buf.writeln('Teslim edildi isaretlemek (sadece klinik):'); + buf.writeln(''); + } + buf.writeln(''); + buf.writeln('Ekip uyesi eklemek (TUM bilgiler alindiktan sonra):'); + buf.writeln(''); + buf.writeln(''); + buf.writeln('KURALLAR:'); + buf.writeln('- Etiketi SADECE kullanici acikca islem istediginde ekle'); + buf.writeln('- Sifre sorulursa kullanicidan al, ASLA uydurma'); + buf.writeln('- iptal gibi geri alinmaz islemleri acikca belirt'); + buf.writeln('- Etiket icindeki job_id degerini yukaridaki is listesinden al'); + buf.writeln('- etiketlerini KESINLİKLE kod blogu (```xml veya ```) icine ALMA, duz metin olarak yaz'); + return buf.toString(); + } + + Future _fetchActiveJobs(String tenantId, bool isLab) async { + try { + final tenantField = isLab ? 'lab_tenant_id' : 'clinic_tenant_id'; + final counterpartField = isLab ? 'clinic_tenant_id' : 'lab_tenant_id'; + final result = await _pb.collection('jobs').getList( + filter: '$tenantField = "$tenantId" && status != "delivered" && status != "cancelled"', + perPage: 60, + sort: '-created', + expand: counterpartField, + ); + + if (result.items.isEmpty) return '## Aktif Isler\nSu an aktif is yok.'; + + final counterpartLabel = isLab ? 'Klinik' : 'Lab'; + final lines = result.items.map((r) { + final j = r.toJson(); + final jobId = j['id'] as String? ?? ''; + final expand = j['expand'] as Map?; + final counterpart = + (expand?[counterpartField] as Map?)?['company_name'] as String? ?? '-'; + final status = _statusTr(j['status'] as String? ?? ''); + final prosthetic = j['prosthetic_type'] as String? ?? '-'; + final patient = j['patient_code'] as String? ?? '-'; + final step = j['current_step'] as String?; + final stepPart = (step != null && step.isNotEmpty) ? ' | Adim: $step' : ''; + final due = j['due_date'] as String? ?? ''; + final duePart = due.isNotEmpty ? ' | Termin: ${due.substring(0, 10)}' : ''; + return '- [ID:$jobId] Hasta: $patient | $prosthetic | $status$stepPart | $counterpartLabel: $counterpart$duePart'; + }).join('\n'); + + return '## Aktif Isler (${result.items.length})\n$lines'; + } catch (e) { + return '## Aktif Isler\n(Veri alinamadi: $e)'; + } + } + + Future _fetchRecentDelivered(String tenantId, bool isLab) async { + try { + final tenantField = isLab ? 'lab_tenant_id' : 'clinic_tenant_id'; + final counterpartField = isLab ? 'clinic_tenant_id' : 'lab_tenant_id'; + final result = await _pb.collection('jobs').getList( + filter: '$tenantField = "$tenantId" && status = "delivered"', + perPage: 10, + sort: '-updated', + expand: counterpartField, + ); + + if (result.items.isEmpty) return '## Son Teslim Edilenler\nHenuz teslim edilen is yok.'; + + final counterpartLabel = isLab ? 'Klinik' : 'Lab'; + final lines = result.items.map((r) { + final j = r.toJson(); + final jobId = j['id'] as String? ?? ''; + final expand = j['expand'] as Map?; + final counterpart = + (expand?[counterpartField] as Map?)?['company_name'] as String? ?? '-'; + final prosthetic = j['prosthetic_type'] as String? ?? '-'; + final patient = j['patient_code'] as String? ?? '-'; + final updated = (j['updated'] as String? ?? ''); + final datePart = updated.length >= 10 ? updated.substring(0, 10) : ''; + return '- [ID:$jobId] Hasta: $patient | $prosthetic | $counterpartLabel: $counterpart${datePart.isNotEmpty ? ' | Tarih: $datePart' : ''}'; + }).join('\n'); + + return '## Son Teslim Edilenler (son 10)\n$lines'; + } catch (_) { + return '## Son Teslim Edilenler\n(Veri alinamadi)'; + } + } + + Future _fetchFinance(String tenantId, bool isLab) async { + try { + final type = isLab ? 'receivable' : 'payable'; + final result = await _pb.collection('finance_entries').getList( + filter: 'tenant_id = "$tenantId" && type = "$type"', + perPage: 200, + ); + + double pending = 0, paid = 0; + for (final r in result.items) { + final j = r.toJson(); + final amount = (j['amount'] as num?)?.toDouble() ?? 0; + if (j['status'] == 'pending') { + pending += amount; + } else { + paid += amount; + } + } + + final label = isLab ? 'alacak' : 'borc'; + return '## Finans\n' + '- Bekleyen $label: ${pending.toStringAsFixed(0)} TL\n' + '- Tahsil edilen: ${paid.toStringAsFixed(0)} TL'; + } catch (_) { + return '## Finans\n(Veri alinamadi)'; + } + } + + Future _fetchTeam(String tenantId) async { + try { + final result = await _pb.collection('tenant_members').getList( + filter: 'tenant_id = "$tenantId"', + expand: 'user_id', + perPage: 50, + ); + + if (result.items.isEmpty) return '## Ekip\nUye yok.'; + + final lines = result.items.map((r) { + final j = r.toJson(); + final expand = j['expand'] as Map?; + final user = expand?['user_id'] as Map?; + final first = (user?['first_name'] as String?) ?? ''; + final last = (user?['last_name'] as String?) ?? ''; + final email = (user?['email'] as String?) ?? ''; + final name = + '$first $last'.trim().isNotEmpty ? '$first $last'.trim() : email; + final role = _roleTr(j['role'] as String? ?? ''); + return '- $name ($role)'; + }).join('\n'); + + return '## Ekip (${result.items.length} uye)\n$lines'; + } catch (_) { + return '## Ekip\n(Veri alinamadi)'; + } + } + + static String _statusTr(String s) => switch (s) { + 'pending' => 'Bekliyor', + 'in_progress' => 'Devam ediyor', + 'sent' => 'Gonderildi', + 'revision' => 'Revizyon', + 'delivered' => 'Teslim edildi', + 'cancelled' => 'Iptal', + _ => s, + }; + + static String _roleTr(String s) => switch (s) { + 'owner' => 'Sahibi', + 'admin' => 'Yonetici', + 'technician' => 'Teknisyen', + 'delivery' => 'Teslimat', + 'finance' => 'Finans', + 'doctor' => 'Hekim', + _ => 'Uye', + }; +} diff --git a/lib/core/services/ai_service.dart b/lib/core/services/ai_service.dart new file mode 100644 index 0000000..7ceeb48 --- /dev/null +++ b/lib/core/services/ai_service.dart @@ -0,0 +1,71 @@ +import 'dart:async'; +import 'dart:convert'; +import 'package:http/http.dart' as http; + +class AiService { + static const _baseUrl = 'https://api.featherless.ai/v1'; + static const _apiKey = + 'rc_e10f49aaa4f7af03dcd9da115cfc12cc1988665e895955c11f77788ee5ad93c6'; + static const _model = 'Qwen/Qwen2.5-7B-Instruct'; + + AiService._(); + static final instance = AiService._(); + + Stream streamChat({ + required String systemPrompt, + required List> messages, + }) async* { + final client = http.Client(); + try { + final request = http.Request( + 'POST', + Uri.parse('$_baseUrl/chat/completions'), + ); + request.headers.addAll({ + 'Authorization': 'Bearer $_apiKey', + 'Content-Type': 'application/json', + }); + request.body = jsonEncode({ + 'model': _model, + 'messages': [ + {'role': 'system', 'content': systemPrompt}, + ...messages, + ], + 'stream': true, + 'max_tokens': 2048, + 'temperature': 0.7, + }); + + final response = await client.send(request); + if (response.statusCode != 200) { + final body = await response.stream.bytesToString(); + String msg = 'API hatası ${response.statusCode}'; + try { + final j = jsonDecode(body) as Map; + msg = (j['error'] as Map?)?['message'] as String? ?? msg; + } catch (_) {} + throw Exception(msg); + } + + final lines = response.stream + .transform(utf8.decoder) + .transform(const LineSplitter()); + + await for (final line in lines) { + if (!line.startsWith('data: ')) continue; + final payload = line.substring(6).trim(); + if (payload == '[DONE]') break; + try { + final j = jsonDecode(payload) as Map; + final choices = j['choices'] as List?; + if (choices == null || choices.isEmpty) continue; + final delta = choices.first['delta'] as Map?; + final content = delta?['content'] as String?; + if (content != null && content.isNotEmpty) yield content; + } catch (_) {} + } + } finally { + client.close(); + } + } +} diff --git a/lib/core/services/job_history_service.dart b/lib/core/services/job_history_service.dart new file mode 100644 index 0000000..196ed3a --- /dev/null +++ b/lib/core/services/job_history_service.dart @@ -0,0 +1,117 @@ +import 'package:pocketbase/pocketbase.dart'; +import '../api/pocketbase_client.dart'; +import '../../models/job.dart'; + +class JobHistoryEntry { + const JobHistoryEntry({ + required this.id, + required this.action, + required this.createdAt, + this.step, + this.note, + }); + final String id; + final JobHistoryAction action; + final JobStep? step; + final String? note; + final DateTime createdAt; +} + +enum JobHistoryAction { + accepted, + handedToClinic, + approved, + revisionRequested, + delivered, + cancelled, +} + +extension JobHistoryActionExt on JobHistoryAction { + String get value => switch (this) { + JobHistoryAction.accepted => 'accepted', + JobHistoryAction.handedToClinic => 'handed_to_clinic', + JobHistoryAction.approved => 'approved', + JobHistoryAction.revisionRequested => 'revision_requested', + JobHistoryAction.delivered => 'delivered', + JobHistoryAction.cancelled => 'cancelled', + }; +} + +class JobHistoryService { + JobHistoryService._(); + static final instance = JobHistoryService._(); + + PocketBase get _pb => PocketBaseClient.instance.pb; + + String get _currentUserId => + (_pb.authStore.record?.id) ?? (_pb.authStore.model as dynamic)?.id as String? ?? ''; + + Future> listForJob(String jobId) async { + try { + final result = await _pb.collection('job_status_history').getList( + filter: 'job_id = "$jobId"', + perPage: 200, + ); + return (result.items.map((r) { + final j = r.toJson(); + String? str(dynamic v) { + final s = v as String?; + return (s == null || s.isEmpty) ? null : s; + } + return JobHistoryEntry( + id: j['id'] as String, + action: _parseAction(j['action_type'] as String? ?? ''), + step: str(j['step']) != null ? _parseStep(j['step'] as String) : null, + note: str(j['note']), + createdAt: DateTime.parse(j['created'] as String), + ); + }).toList()..sort((a, b) => a.createdAt.compareTo(b.createdAt))); + } catch (_) { + return []; + } + } + + static JobHistoryAction _parseAction(String s) => switch (s) { + 'accepted' => JobHistoryAction.accepted, + 'handed_to_clinic' => JobHistoryAction.handedToClinic, + 'approved' => JobHistoryAction.approved, + 'revision_requested' => JobHistoryAction.revisionRequested, + 'delivered' => JobHistoryAction.delivered, + _ => JobHistoryAction.cancelled, + }; + + static JobStep _parseStep(String s) => switch (s) { + 'alt_yapi_prova' => JobStep.altYapiProva, + 'ust_yapi_prova' => JobStep.ustYapiProva, + 'mum_prova' => JobStep.mumProva, + 'disler_prova' => JobStep.dislerProva, + 'dayanak_prova' => JobStep.dayanakProva, + 'kron_prova' => JobStep.kronProva, + 'cila_bitim' => JobStep.cilaBitim, + _ => JobStep.olcu, + }; + + Future append({ + required String jobId, + required String clinicTenantId, + required String labTenantId, + required JobHistoryAction action, + JobStep? step, + String? note, + String? userId, + }) async { + try { + await _pb.collection('job_status_history').create(body: { + 'job_id': jobId, + 'clinic_tenant_id': clinicTenantId, + 'lab_tenant_id': labTenantId, + 'completed_by': userId ?? _currentUserId, + 'action_type': action.value, + if (step != null) 'step': step.value, + if (note != null && note.isNotEmpty) 'note': note, + }); + } catch (_) { + // history failures must never block the main mutation + } + } +} diff --git a/lib/core/services/notification_service.dart b/lib/core/services/notification_service.dart new file mode 100644 index 0000000..0886db6 --- /dev/null +++ b/lib/core/services/notification_service.dart @@ -0,0 +1,64 @@ +import 'dart:io'; +import 'package:flutter/foundation.dart'; +import 'package:go_router/go_router.dart'; +import 'package:onesignal_flutter/onesignal_flutter.dart'; + +// ─── Replace with your OneSignal App ID from onesignal.com ────────────────── +const _kOneSignalAppId = '524cb6d8-2640-4f85-bb24-c9c762233de7'; +// ──────────────────────────────────────────────────────────────────────────── + +class NotificationService { + NotificationService._(); + + static GoRouter? _router; + static bool _initialized = false; + + static void setRouter(GoRouter router) => _router = router; + + static bool get _supported => + !kIsWeb && (Platform.isIOS || Platform.isAndroid || Platform.isMacOS); + + static Future init() async { + if (!_supported || _initialized) return; + _initialized = true; + + OneSignal.initialize(_kOneSignalAppId); + await OneSignal.Notifications.requestPermission(true); + + // Show notification even when app is in foreground + OneSignal.Notifications.addForegroundWillDisplayListener((event) { + event.notification.display(); + }); + + // Tap → navigate to job detail + OneSignal.Notifications.addClickListener((event) { + final data = event.notification.additionalData; + if (data == null) return; + final jobId = data['job_id'] as String?; + final tenantType = data['tenant_type'] as String?; + if (jobId == null || _router == null) return; + if (tenantType == 'lab') { + _router!.push('/lab/jobs/$jobId'); + } else { + _router!.push('/clinic/jobs/$jobId'); + } + }); + } + + /// Call after successful login. Links the OneSignal player to this user. + static Future loginUser(String userId, {bool isLab = false}) async { + if (!_supported) return; + try { + await OneSignal.login(userId); + OneSignal.User.addTagWithKey('tenant_type', isLab ? 'lab' : 'clinic'); + } catch (_) {} + } + + /// Call on logout. + static Future logoutUser() async { + if (!_supported) return; + try { + await OneSignal.logout(); + } catch (_) {} + } +} diff --git a/lib/core/services/realtime_service.dart b/lib/core/services/realtime_service.dart new file mode 100644 index 0000000..f5c0628 --- /dev/null +++ b/lib/core/services/realtime_service.dart @@ -0,0 +1,37 @@ +import 'package:pocketbase/pocketbase.dart'; +import '../api/pocketbase_client.dart'; + +typedef UnsubFn = Future Function(); + +class RealtimeService { + RealtimeService._(); + static final instance = RealtimeService._(); + + final _pb = PocketBaseClient.instance.pb; + + UnsubFn watch( + String collection, { + String topic = '*', + String filter = '', + required void Function(RecordSubscriptionEvent) onEvent, + }) { + UnsubFn? cancel; + + _pb.collection(collection).subscribe(topic, onEvent, filter: filter).then((fn) { + cancel = fn; + }); + + return () async { + try { + final fn = cancel; + if (fn != null) { + await fn(); + } else { + await _pb.collection(collection).unsubscribe(topic); + } + } catch (_) { + await _pb.collection(collection).unsubscribe(topic); + } + }; + } +} diff --git a/lib/core/theme/app_theme.dart b/lib/core/theme/app_theme.dart new file mode 100644 index 0000000..9172e29 --- /dev/null +++ b/lib/core/theme/app_theme.dart @@ -0,0 +1,299 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:google_fonts/google_fonts.dart'; + +abstract final class AppColors { + // Primary — professional navy + static const primary = Color(0xFF1E3A5F); + static const onPrimary = Color(0xFFFFFFFF); + + // Accent — sky blue CTA + static const accent = Color(0xFF0369A1); + static const onAccent = Color(0xFFFFFFFF); + + // Status + static const pending = Color(0xFFF59E0B); + static const pendingBg = Color(0xFFFFFBEB); + static const inProgress = Color(0xFF0369A1); + static const inProgressBg = Color(0xFFEFF6FF); + static const success = Color(0xFF059669); + static const successBg = Color(0xFFECFDF5); + static const cancelled = Color(0xFFDC2626); + static const cancelledBg = Color(0xFFFEF2F2); + + // Surfaces + static const background = Color(0xFFF1F5F9); + static const surface = Color(0xFFFFFFFF); + static const surfaceVariant = Color(0xFFF8FAFC); + static const muted = Color(0xFFE2E8F0); + static const border = Color(0xFFE2E8F0); + + // Text + static const textPrimary = Color(0xFF0F172A); + static const textSecondary = Color(0xFF64748B); + static const textMuted = Color(0xFF94A3B8); + + // Dark variants + static const darkBackground = Color(0xFF0F172A); + static const darkSurface = Color(0xFF1E293B); + static const darkSurfaceVariant = Color(0xFF273344); + static const darkBorder = Color(0xFF334155); + static const darkTextPrimary = Color(0xFFF1F5F9); + static const darkTextSecondary = Color(0xFF94A3B8); +} + +abstract final class AppLayout { + /// Window width above which the sidebar navigation is shown instead of bottom nav. + static const double sidebarBreakpoint = 720.0; + + /// Window width above which wide-desktop content layouts activate + /// (e.g., 3-column stat card row, 2-column forms). + static const double wideBreakpoint = 1100.0; + + /// Maximum content width used for dashboard horizontal padding. + static const double contentMaxWidth = 1040.0; +} + +abstract final class AppTheme { + static TextTheme _buildTextTheme(Color bodyColor, Color displayColor) { + final base = GoogleFonts.plusJakartaSansTextTheme(); + return base.copyWith( + displayLarge: base.displayLarge?.copyWith(color: displayColor, fontWeight: FontWeight.w800), + displayMedium: base.displayMedium?.copyWith(color: displayColor, fontWeight: FontWeight.w700), + headlineLarge: base.headlineLarge?.copyWith(color: displayColor, fontWeight: FontWeight.w700), + headlineMedium: base.headlineMedium?.copyWith(color: displayColor, fontWeight: FontWeight.w700), + headlineSmall: base.headlineSmall?.copyWith(color: displayColor, fontWeight: FontWeight.w600), + titleLarge: base.titleLarge?.copyWith(color: displayColor, fontWeight: FontWeight.w600), + titleMedium: base.titleMedium?.copyWith(color: displayColor, fontWeight: FontWeight.w600), + titleSmall: base.titleSmall?.copyWith(color: displayColor, fontWeight: FontWeight.w500), + bodyLarge: base.bodyLarge?.copyWith(color: bodyColor), + bodyMedium: base.bodyMedium?.copyWith(color: bodyColor), + bodySmall: base.bodySmall?.copyWith(color: AppColors.textSecondary), + labelLarge: base.labelLarge?.copyWith(fontWeight: FontWeight.w600), + labelMedium: base.labelMedium?.copyWith(fontWeight: FontWeight.w500), + ); + } + + static final light = ThemeData( + useMaterial3: true, + colorScheme: ColorScheme( + brightness: Brightness.light, + primary: AppColors.primary, + onPrimary: AppColors.onPrimary, + primaryContainer: const Color(0xFFDBEAFE), + onPrimaryContainer: AppColors.primary, + secondary: AppColors.accent, + onSecondary: AppColors.onAccent, + secondaryContainer: const Color(0xFFE0F2FE), + onSecondaryContainer: AppColors.accent, + tertiary: AppColors.success, + onTertiary: Colors.white, + tertiaryContainer: AppColors.successBg, + onTertiaryContainer: AppColors.success, + error: AppColors.cancelled, + onError: Colors.white, + errorContainer: AppColors.cancelledBg, + onErrorContainer: AppColors.cancelled, + surface: AppColors.surface, + onSurface: AppColors.textPrimary, + surfaceContainerHighest: AppColors.surfaceVariant, + onSurfaceVariant: AppColors.textSecondary, + outline: AppColors.border, + outlineVariant: AppColors.muted, + scrim: Colors.black54, + inverseSurface: AppColors.darkSurface, + onInverseSurface: AppColors.darkTextPrimary, + inversePrimary: const Color(0xFF93C5FD), + ), + scaffoldBackgroundColor: AppColors.background, + textTheme: _buildTextTheme(AppColors.textPrimary, AppColors.textPrimary), + appBarTheme: AppBarTheme( + backgroundColor: AppColors.surface, + foregroundColor: AppColors.textPrimary, + surfaceTintColor: Colors.transparent, + elevation: 0, + scrolledUnderElevation: 0, + shadowColor: Colors.transparent, + centerTitle: false, + systemOverlayStyle: SystemUiOverlayStyle.dark, + titleTextStyle: GoogleFonts.plusJakartaSans( + fontSize: 17, + fontWeight: FontWeight.w600, + color: AppColors.textPrimary, + ), + iconTheme: const IconThemeData(color: AppColors.textPrimary, size: 22), + ), + cardTheme: CardThemeData( + elevation: 0, + color: AppColors.surface, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16), + side: const BorderSide(color: AppColors.border, width: 1), + ), + margin: EdgeInsets.zero, + clipBehavior: Clip.antiAlias, + ), + navigationBarTheme: NavigationBarThemeData( + backgroundColor: AppColors.surface, + elevation: 0, + shadowColor: Colors.transparent, + indicatorColor: const Color(0xFFDBEAFE), + iconTheme: WidgetStateProperty.resolveWith((states) { + if (states.contains(WidgetState.selected)) { + return const IconThemeData(color: AppColors.primary, size: 22); + } + return IconThemeData(color: AppColors.textSecondary, size: 22); + }), + labelTextStyle: WidgetStateProperty.resolveWith((states) { + final style = GoogleFonts.plusJakartaSans(fontSize: 11); + if (states.contains(WidgetState.selected)) { + return style.copyWith(fontWeight: FontWeight.w600, color: AppColors.primary); + } + return style.copyWith(fontWeight: FontWeight.w500, color: AppColors.textSecondary); + }), + surfaceTintColor: Colors.transparent, + ), + filledButtonTheme: FilledButtonThemeData( + style: FilledButton.styleFrom( + backgroundColor: AppColors.primary, + foregroundColor: AppColors.onPrimary, + minimumSize: const Size(0, 48), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), + textStyle: GoogleFonts.plusJakartaSans(fontSize: 15, fontWeight: FontWeight.w600), + ), + ), + outlinedButtonTheme: OutlinedButtonThemeData( + style: OutlinedButton.styleFrom( + foregroundColor: AppColors.primary, + minimumSize: const Size(0, 48), + side: const BorderSide(color: AppColors.border, width: 1.5), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), + textStyle: GoogleFonts.plusJakartaSans(fontSize: 15, fontWeight: FontWeight.w600), + ), + ), + inputDecorationTheme: InputDecorationTheme( + filled: true, + fillColor: AppColors.surfaceVariant, + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide.none, + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide.none, + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: const BorderSide(color: AppColors.accent, width: 2), + ), + errorBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: const BorderSide(color: AppColors.cancelled, width: 1.5), + ), + contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16), + labelStyle: GoogleFonts.plusJakartaSans(color: AppColors.textSecondary), + hintStyle: GoogleFonts.plusJakartaSans(color: AppColors.textMuted), + ), + chipTheme: ChipThemeData( + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), + side: BorderSide.none, + ), + dividerTheme: const DividerThemeData( + color: AppColors.border, + thickness: 1, + space: 1, + ), + listTileTheme: const ListTileThemeData( + contentPadding: EdgeInsets.symmetric(horizontal: 16, vertical: 4), + ), + ); + + static final dark = ThemeData( + useMaterial3: true, + colorScheme: ColorScheme( + brightness: Brightness.dark, + primary: const Color(0xFF93C5FD), + onPrimary: const Color(0xFF1E3A5F), + primaryContainer: const Color(0xFF1E3A5F), + onPrimaryContainer: const Color(0xFFDBEAFE), + secondary: const Color(0xFF7DD3FC), + onSecondary: const Color(0xFF0C4A6E), + secondaryContainer: const Color(0xFF0C4A6E), + onSecondaryContainer: const Color(0xFFE0F2FE), + tertiary: const Color(0xFF6EE7B7), + onTertiary: const Color(0xFF064E3B), + tertiaryContainer: const Color(0xFF064E3B), + onTertiaryContainer: const Color(0xFFD1FAE5), + error: const Color(0xFFFCA5A5), + onError: const Color(0xFF7F1D1D), + errorContainer: const Color(0xFF7F1D1D), + onErrorContainer: const Color(0xFFFEE2E2), + surface: AppColors.darkSurface, + onSurface: AppColors.darkTextPrimary, + surfaceContainerHighest: AppColors.darkSurfaceVariant, + onSurfaceVariant: AppColors.darkTextSecondary, + outline: AppColors.darkBorder, + outlineVariant: const Color(0xFF1E293B), + scrim: Colors.black87, + inverseSurface: const Color(0xFFF1F5F9), + onInverseSurface: AppColors.textPrimary, + inversePrimary: AppColors.primary, + ), + scaffoldBackgroundColor: AppColors.darkBackground, + textTheme: _buildTextTheme(AppColors.darkTextPrimary, AppColors.darkTextPrimary), + appBarTheme: AppBarTheme( + backgroundColor: AppColors.darkSurface, + foregroundColor: AppColors.darkTextPrimary, + elevation: 0, + scrolledUnderElevation: 1, + systemOverlayStyle: SystemUiOverlayStyle.light, + titleTextStyle: GoogleFonts.plusJakartaSans( + fontSize: 18, + fontWeight: FontWeight.w600, + color: AppColors.darkTextPrimary, + ), + ), + cardTheme: CardThemeData( + elevation: 0, + color: AppColors.darkSurface, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16), + side: const BorderSide(color: AppColors.darkBorder, width: 1), + ), + margin: EdgeInsets.zero, + clipBehavior: Clip.antiAlias, + ), + navigationBarTheme: NavigationBarThemeData( + backgroundColor: AppColors.darkSurface, + elevation: 0, + indicatorColor: const Color(0xFF1E3A5F), + iconTheme: WidgetStateProperty.resolveWith((states) { + if (states.contains(WidgetState.selected)) { + return const IconThemeData(color: Color(0xFF93C5FD), size: 22); + } + return IconThemeData(color: AppColors.darkTextSecondary, size: 22); + }), + labelTextStyle: WidgetStateProperty.resolveWith((states) { + final style = GoogleFonts.plusJakartaSans(fontSize: 11); + if (states.contains(WidgetState.selected)) { + return style.copyWith(fontWeight: FontWeight.w600, color: const Color(0xFF93C5FD)); + } + return style.copyWith(fontWeight: FontWeight.w500, color: AppColors.darkTextSecondary); + }), + ), + filledButtonTheme: FilledButtonThemeData( + style: FilledButton.styleFrom( + backgroundColor: const Color(0xFF93C5FD), + foregroundColor: const Color(0xFF1E3A5F), + minimumSize: const Size(0, 48), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), + textStyle: GoogleFonts.plusJakartaSans(fontSize: 15, fontWeight: FontWeight.w600), + ), + ), + dividerTheme: const DividerThemeData( + color: AppColors.darkBorder, + thickness: 1, + space: 1, + ), + ); +} diff --git a/lib/core/utils/currency_formatter.dart b/lib/core/utils/currency_formatter.dart new file mode 100644 index 0000000..6384c72 --- /dev/null +++ b/lib/core/utils/currency_formatter.dart @@ -0,0 +1,35 @@ +class CurrencyFormatter { + static const _symbols = { + 'TRY': '₺', + 'USD': '\$', + 'EUR': '€', + 'GBP': '£', + 'AED': 'د.إ', + }; + + static const _rtlSymbols = {'AED'}; + + static String symbol(String code) => _symbols[code] ?? code; + + static String format(double amount, String currencyCode) { + final sym = symbol(currencyCode); + final isRtl = _rtlSymbols.contains(currencyCode); + final value = _formatNumber(amount); + return isRtl ? '$value $sym' : '$sym$value'; + } + + static String _formatNumber(double amount) { + final formatted = amount.toStringAsFixed(2); + final parts = formatted.split('.'); + final intPart = parts[0]; + final decPart = parts[1]; + final buf = StringBuffer(); + final digits = intPart.replaceAll('-', ''); + final isNeg = intPart.startsWith('-'); + for (int i = 0; i < digits.length; i++) { + if (i > 0 && (digits.length - i) % 3 == 0) buf.write(','); + buf.write(digits[i]); + } + return '${isNeg ? '-' : ''}$buf.$decPart'; + } +} diff --git a/lib/core/utils/file_download_helper.dart b/lib/core/utils/file_download_helper.dart new file mode 100644 index 0000000..b5940a8 --- /dev/null +++ b/lib/core/utils/file_download_helper.dart @@ -0,0 +1,40 @@ +import 'dart:io'; +import 'package:flutter/material.dart'; +import 'package:http/http.dart' as http; +import 'package:path_provider/path_provider.dart'; +import 'package:share_plus/share_plus.dart'; + +import '../api/pocketbase_client.dart'; +import '../../models/job_file.dart'; +import '../theme/app_theme.dart'; + +class FileDownloadHelper { + static Future download(BuildContext context, JobFile file, {Rect? shareOrigin}) async { + if (file.downloadUrl.isEmpty) return; + final messenger = ScaffoldMessenger.of(context); + try { + final pb = PocketBaseClient.instance.pb; + final fileToken = await pb.files.getToken(); + final uri = Uri.parse('${file.downloadUrl}?token=$fileToken'); + final response = await http.get(uri); + if (response.statusCode != 200) throw Exception('HTTP ${response.statusCode}'); + final dir = await getTemporaryDirectory(); + final path = '${dir.path}/${file.name}'; + await File(path).writeAsBytes(response.bodyBytes); + await Share.shareXFiles( + [XFile(path, mimeType: file.mimeType ?? 'application/octet-stream')], + subject: file.name, + sharePositionOrigin: shareOrigin ?? const Rect.fromLTWH(0, 0, 1, 1), + ); + } catch (e) { + if (context.mounted) { + messenger.showSnackBar( + SnackBar( + content: Text('İndirilemedi: $e'), + backgroundColor: AppColors.cancelled, + ), + ); + } + } + } +} diff --git a/lib/core/widgets/app_search_field.dart b/lib/core/widgets/app_search_field.dart new file mode 100644 index 0000000..3d1418d --- /dev/null +++ b/lib/core/widgets/app_search_field.dart @@ -0,0 +1,72 @@ +import 'package:flutter/material.dart'; + +import '../theme/app_theme.dart'; + +class AppSearchField extends StatelessWidget { + const AppSearchField({ + super.key, + required this.controller, + required this.onChanged, + this.hint, + }); + + final TextEditingController controller; + final ValueChanged onChanged; + final String? hint; + + @override + Widget build(BuildContext context) { + return Container( + color: AppColors.surface, + padding: const EdgeInsets.fromLTRB(16, 12, 16, 0), + child: ListenableBuilder( + listenable: controller, + builder: (context, _) => Container( + decoration: BoxDecoration( + color: AppColors.surfaceVariant, + borderRadius: BorderRadius.circular(12), + border: Border.all(color: AppColors.border), + ), + child: TextField( + controller: controller, + onChanged: onChanged, + style: const TextStyle( + fontSize: 14, + color: AppColors.textPrimary, + ), + decoration: InputDecoration( + hintText: hint ?? 'Ara...', + hintStyle: const TextStyle( + color: AppColors.textMuted, + fontSize: 14, + ), + prefixIcon: const Icon( + Icons.search_rounded, + color: AppColors.textMuted, + size: 20, + ), + suffixIcon: controller.text.isNotEmpty + ? GestureDetector( + onTap: () { + controller.clear(); + onChanged(''); + }, + child: const Padding( + padding: EdgeInsets.all(12), + child: Icon( + Icons.close_rounded, + color: AppColors.textMuted, + size: 16, + ), + ), + ) + : null, + border: InputBorder.none, + contentPadding: const EdgeInsets.symmetric(vertical: 12), + ), + ), + ), + ), + ); + } +} diff --git a/lib/core/widgets/gradient_app_bar.dart b/lib/core/widgets/gradient_app_bar.dart new file mode 100644 index 0000000..bac5030 --- /dev/null +++ b/lib/core/widgets/gradient_app_bar.dart @@ -0,0 +1,328 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; + +import '../theme/app_theme.dart'; +import 'tooth_logo.dart'; + +class GradientAppBar extends StatelessWidget implements PreferredSizeWidget { + const GradientAppBar({ + super.key, + required this.title, + required this.category, + this.actions = const [], + this.searchController, + this.onSearchChanged, + this.searchHint, + }); + + final String title; + final String category; + final List actions; + final TextEditingController? searchController; + final ValueChanged? onSearchChanged; + final String? searchHint; + + bool get _hasSearch => + searchController != null && onSearchChanged != null; + + @override + Size get preferredSize => + Size.fromHeight(kToolbarHeight + (_hasSearch ? 52.0 : 0.0)); + + @override + Widget build(BuildContext context) { + final isDesktop = + MediaQuery.sizeOf(context).width > AppLayout.sidebarBreakpoint; + final searchBottom = _hasSearch + ? _SearchBarBottom( + controller: searchController!, + onChanged: onSearchChanged!, + hint: searchHint ?? 'Ara...', + ) + : null; + + if (isDesktop) { + return AppBar( + backgroundColor: AppColors.surface, + foregroundColor: AppColors.textPrimary, + elevation: 0, + scrolledUnderElevation: 0, + automaticallyImplyLeading: false, + titleSpacing: 24, + title: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Text( + 'DLS', + style: TextStyle( + fontSize: 11, + color: AppColors.textSecondary.withValues(alpha: 0.8), + letterSpacing: 0.3, + ), + ), + Text( + title, + style: const TextStyle( + fontSize: 17, + fontWeight: FontWeight.w700, + color: AppColors.textPrimary, + ), + ), + ], + ), + actions: [ + ...actions, + if (actions.isNotEmpty) const SizedBox(width: 8), + ], + iconTheme: + const IconThemeData(color: AppColors.textSecondary, size: 22), + actionsIconTheme: + const IconThemeData(color: AppColors.textSecondary, size: 22), + bottom: searchBottom, + ); + } + + return AppBar( + backgroundColor: AppColors.primary, + foregroundColor: Colors.white, + elevation: 0, + systemOverlayStyle: SystemUiOverlayStyle.light, + automaticallyImplyLeading: false, + leadingWidth: 60, + leading: Padding( + padding: const EdgeInsets.only(left: 16, top: 8, bottom: 8), + child: Container( + decoration: BoxDecoration( + color: Colors.white.withValues(alpha: 0.15), + borderRadius: BorderRadius.circular(10), + ), + child: + const Center(child: ToothLogo(size: 20, color: Colors.white)), + ), + ), + titleSpacing: 8, + title: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Text( + category, + style: TextStyle( + color: Colors.white.withValues(alpha: 0.65), + fontSize: 11, + fontWeight: FontWeight.w600, + letterSpacing: 1.5, + ), + ), + Text( + title, + style: const TextStyle( + color: Colors.white, + fontSize: 15, + fontWeight: FontWeight.w700, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ], + ), + actions: actions.isNotEmpty + ? [...actions, const SizedBox(width: 4)] + : null, + iconTheme: const IconThemeData(color: Colors.white, size: 22), + actionsIconTheme: + const IconThemeData(color: Colors.white, size: 22), + flexibleSpace: Container( + decoration: const BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topLeft, + end: Alignment.bottomRight, + colors: [Color(0xFF0F172A), AppColors.primary], + ), + ), + ), + bottom: searchBottom, + ); + } +} + +// ── iOS-26-style search bar shown below the AppBar title ───────────────────── + +class _SearchBarBottom extends StatelessWidget implements PreferredSizeWidget { + const _SearchBarBottom({ + required this.controller, + required this.onChanged, + required this.hint, + }); + + final TextEditingController controller; + final ValueChanged onChanged; + final String hint; + + @override + Size get preferredSize => const Size.fromHeight(52); + + @override + Widget build(BuildContext context) { + final isDesktop = + MediaQuery.sizeOf(context).width > AppLayout.sidebarBreakpoint; + final bg = isDesktop + ? AppColors.surfaceVariant + : Colors.white.withValues(alpha: 0.15); + final textColor = isDesktop ? AppColors.textPrimary : Colors.white; + final iconColor = isDesktop + ? AppColors.textMuted + : Colors.white.withValues(alpha: 0.65); + final hintColor = isDesktop + ? AppColors.textMuted + : Colors.white.withValues(alpha: 0.5); + + return SizedBox( + height: 52, + child: Padding( + padding: const EdgeInsets.fromLTRB(16, 4, 16, 10), + child: ListenableBuilder( + listenable: controller, + builder: (context, _) => Container( + height: 38, + decoration: BoxDecoration( + color: bg, + borderRadius: BorderRadius.circular(12), + border: isDesktop + ? Border.all(color: AppColors.border) + : null, + ), + child: TextField( + controller: controller, + onChanged: onChanged, + style: TextStyle(color: textColor, fontSize: 15), + decoration: InputDecoration( + hintText: hint, + hintStyle: TextStyle(color: hintColor, fontSize: 15), + prefixIcon: Padding( + padding: const EdgeInsets.only(left: 10, right: 6), + child: Icon(Icons.search_rounded, + size: 18, color: iconColor), + ), + prefixIconConstraints: + const BoxConstraints(minWidth: 36, minHeight: 36), + suffixIcon: controller.text.isNotEmpty + ? GestureDetector( + onTap: () { + controller.clear(); + onChanged(''); + }, + child: Padding( + padding: const EdgeInsets.only(right: 10), + child: Icon(Icons.close_rounded, + size: 16, color: iconColor), + ), + ) + : null, + suffixIconConstraints: + const BoxConstraints(minWidth: 32, minHeight: 36), + border: InputBorder.none, + contentPadding: const EdgeInsets.symmetric(vertical: 10), + isDense: true, + ), + ), + ), + ), + ), + ); + } +} + +// ── Sort / filter bottom sheet ──────────────────────────────────────────────── + +Future showSortSheet( + BuildContext context, { + required String title, + required List options, + required int current, +}) { + return showModalBottomSheet( + context: context, + backgroundColor: Colors.transparent, + builder: (ctx) => _SortSheet( + title: title, + options: options, + current: current, + ), + ); +} + +class _SortSheet extends StatelessWidget { + const _SortSheet({ + required this.title, + required this.options, + required this.current, + }); + + final String title; + final List options; + final int current; + + @override + Widget build(BuildContext context) { + return Container( + decoration: const BoxDecoration( + color: AppColors.surface, + borderRadius: BorderRadius.vertical(top: Radius.circular(20)), + ), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const SizedBox(height: 12), + Container( + width: 40, + height: 4, + decoration: BoxDecoration( + color: AppColors.border, + borderRadius: BorderRadius.circular(2), + ), + ), + const SizedBox(height: 16), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20), + child: Align( + alignment: Alignment.centerLeft, + child: Text( + title, + style: const TextStyle( + fontSize: 17, + fontWeight: FontWeight.w700, + color: AppColors.textPrimary, + ), + ), + ), + ), + const SizedBox(height: 8), + for (int i = 0; i < options.length; i++) + ListTile( + contentPadding: + const EdgeInsets.symmetric(horizontal: 20), + title: Text( + options[i], + style: TextStyle( + color: i == current + ? AppColors.primary + : AppColors.textPrimary, + fontWeight: i == current + ? FontWeight.w600 + : FontWeight.normal, + ), + ), + trailing: i == current + ? const Icon(Icons.check_rounded, + color: AppColors.primary, size: 20) + : null, + onTap: () => Navigator.pop(context, i), + ), + SizedBox(height: MediaQuery.paddingOf(context).bottom + 8), + ], + ), + ); + } +} diff --git a/lib/core/widgets/pill_tabs.dart b/lib/core/widgets/pill_tabs.dart new file mode 100644 index 0000000..ac91e66 --- /dev/null +++ b/lib/core/widgets/pill_tabs.dart @@ -0,0 +1,121 @@ +import 'package:flutter/material.dart'; + +import '../theme/app_theme.dart'; + +class PillTabs extends StatelessWidget { + const PillTabs({ + super.key, + required this.tabs, + required this.selected, + required this.onSelect, + this.counts, + }); + + final List tabs; + final int selected; + final ValueChanged onSelect; + final List? counts; + + @override + Widget build(BuildContext context) { + return Container( + color: AppColors.surface, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + SingleChildScrollView( + scrollDirection: Axis.horizontal, + padding: const EdgeInsets.fromLTRB(16, 10, 16, 10), + child: Row( + children: [ + for (int i = 0; i < tabs.length; i++) ...[ + if (i > 0) const SizedBox(width: 8), + _PillTab( + label: tabs[i], + count: counts != null && i < counts!.length ? counts![i] : null, + selected: selected == i, + onTap: () => onSelect(i), + ), + ], + ], + ), + ), + const Divider(height: 1, thickness: 1, color: AppColors.border), + ], + ), + ); + } +} + +class _PillTab extends StatelessWidget { + const _PillTab({ + required this.label, + required this.selected, + required this.onTap, + this.count, + }); + + final String label; + final bool selected; + final VoidCallback onTap; + final int? count; + + @override + Widget build(BuildContext context) { + return Semantics( + label: label, + button: true, + excludeSemantics: true, + child: GestureDetector( + onTap: onTap, + child: AnimatedContainer( + duration: const Duration(milliseconds: 200), + padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 8), + decoration: BoxDecoration( + color: selected ? AppColors.primary : Colors.transparent, + borderRadius: BorderRadius.circular(20), + border: Border.all( + color: selected ? AppColors.primary : AppColors.border, + width: 1.5, + ), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + label, + style: TextStyle( + color: selected ? Colors.white : AppColors.textSecondary, + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + if (count != null) ...[ + const SizedBox(width: 6), + AnimatedContainer( + duration: const Duration(milliseconds: 200), + padding: + const EdgeInsets.symmetric(horizontal: 6, vertical: 1), + decoration: BoxDecoration( + color: selected + ? Colors.white.withValues(alpha: 0.25) + : AppColors.inProgressBg, + borderRadius: BorderRadius.circular(10), + ), + child: Text( + '$count', + style: TextStyle( + color: selected ? Colors.white : AppColors.inProgress, + fontSize: 11, + fontWeight: FontWeight.w700, + ), + ), + ), + ], + ], + ), + ), + ), + ); + } +} diff --git a/lib/core/widgets/tooth_logo.dart b/lib/core/widgets/tooth_logo.dart new file mode 100644 index 0000000..c53ff3a --- /dev/null +++ b/lib/core/widgets/tooth_logo.dart @@ -0,0 +1,104 @@ +import 'package:flutter/material.dart'; + +/// Renders the DLS brand logo — navy tooth + cyan chevrons. +/// +/// [color] null → brand colors (#00397C tooth + #57B8CE chevrons). +/// Pass a color (e.g. Colors.white) for monochrome override on dark backgrounds. +class ToothLogo extends StatelessWidget { + const ToothLogo({ + super.key, + required this.size, + this.color, + }); + + final double size; + final Color? color; + + @override + Widget build(BuildContext context) { + return SizedBox( + width: size * 1.9, + height: size, + child: CustomPaint( + painter: _DlsLogoPainter(color: color), + ), + ); + } +} + +class _DlsLogoPainter extends CustomPainter { + const _DlsLogoPainter({this.color}); + final Color? color; + + static const _navy = Color(0xFF00397C); + static const _cyan = Color(0xFF57B8CE); + + @override + void paint(Canvas canvas, Size size) { + final toothColor = color ?? _navy; + final chevronColor = color ?? _cyan; + + // Content bounding box in SVG 200×200 space: x=[42.5..157.5], y=[72..133] + // Width=115, Height=61 → aspect ~1.885 ≈ widget aspect 1.9 + const svgLeft = 42.5, svgTop = 72.0, svgWidth = 115.0, svgHeight = 61.0; + final s = size.height / svgHeight; + final dx = (size.width - svgWidth * s) / 2.0 - svgLeft * s; + final dy = (size.height - svgHeight * s) / 2.0 - svgTop * s; + + canvas.translate(dx, dy); + canvas.scale(s); + + _drawTooth(canvas, toothColor); + _drawChevrons(canvas, chevronColor); + } + + static void _drawTooth(Canvas canvas, Color color) { + // SVG path with scale(0.58) + translate(100,100) applied inline. + const cx = 100.0, cy = 100.0, sc = 0.58; + double px(double v) => cx + v * sc; + double py(double v) => cy + v * sc; + + final path = Path() + ..moveTo(px(0), py(-46)) + ..cubicTo(px(-22), py(-50), px(-44), py(-38), px(-44), py(-12)) + ..cubicTo(px(-44), py(8), px(-34), py(32), px(-26), py(46)) + ..cubicTo(px(-20), py(57), px(-11), py(53), px(-8), py(33)) + ..cubicTo(px(-6), py(19), px(-2), py(17), px(0), py(17)) + ..cubicTo(px(2), py(17), px(6), py(19), px(8), py(33)) + ..cubicTo(px(11), py(53), px(20), py(57), px(26), py(46)) + ..cubicTo(px(34), py(32), px(44), py(8), px(44), py(-12)) + ..cubicTo(px(44), py(-38), px(22), py(-50), px(0), py(-46)) + ..close(); + + canvas.drawPath(path, Paint()..color = color..style = PaintingStyle.fill); + } + + static void _drawChevrons(Canvas canvas, Color color) { + final paint = Paint() + ..color = color + ..style = PaintingStyle.stroke + ..strokeWidth = 11.0 + ..strokeCap = StrokeCap.round + ..strokeJoin = StrokeJoin.round; + + // Polyline points + translate(100,100). Left: (-52,-22)→(-34,0)→(-52,22) + canvas.drawPath( + Path() + ..moveTo(48, 78) + ..lineTo(66, 100) + ..lineTo(48, 122), + paint, + ); + // Right: (52,-22)→(34,0)→(52,22) + canvas.drawPath( + Path() + ..moveTo(152, 78) + ..lineTo(134, 100) + ..lineTo(152, 122), + paint, + ); + } + + @override + bool shouldRepaint(_DlsLogoPainter old) => old.color != color; +} diff --git a/lib/features/auth/auth_widgets.dart b/lib/features/auth/auth_widgets.dart new file mode 100644 index 0000000..e6e8aa0 --- /dev/null +++ b/lib/features/auth/auth_widgets.dart @@ -0,0 +1,104 @@ +import 'package:flutter/material.dart'; +import '../../core/theme/app_theme.dart'; + +/// Animated floating blob background used on auth screens. +/// [bright] = true → white blobs (for dark/gradient backgrounds). +/// [bright] = false → primary/accent blobs (for light backgrounds). +class AnimatedAuthBg extends StatefulWidget { + const AnimatedAuthBg({super.key, this.bright = false}); + final bool bright; + + @override + State createState() => _AnimatedAuthBgState(); +} + +class _AnimatedAuthBgState extends State + with SingleTickerProviderStateMixin { + late AnimationController _ctrl; + late Animation _anim; + + @override + void initState() { + super.initState(); + _ctrl = AnimationController( + vsync: this, + duration: const Duration(seconds: 8), + )..repeat(reverse: true); + _anim = CurvedAnimation(parent: _ctrl, curve: Curves.easeInOut); + } + + @override + void dispose() { + _ctrl.dispose(); + super.dispose(); + } + + Color _blob(double alpha) => widget.bright + ? Colors.white.withValues(alpha: alpha * 1.5) + : AppColors.primary.withValues(alpha: alpha); + + Color _blobAccent(double alpha) => widget.bright + ? Colors.white.withValues(alpha: alpha * 1.2) + : AppColors.accent.withValues(alpha: alpha); + + @override + Widget build(BuildContext context) { + return AnimatedBuilder( + animation: _anim, + builder: (_, __) { + final t = _anim.value; + return Stack( + children: [ + Positioned( + top: -80 + t * 30, + left: -60 + t * 20, + child: AuthBlob(size: 300, color: _blob(0.08)), + ), + Positioned( + top: 200 - t * 40, + right: -100 + t * 25, + child: AuthBlob(size: 250, color: _blobAccent(0.06)), + ), + Positioned( + bottom: 100 + t * 30, + left: 50 - t * 15, + child: AuthBlob(size: 200, color: _blob(0.05)), + ), + Positioned( + bottom: -50 + t * 20, + right: -50 + t * 10, + child: AuthBlob(size: 280, color: _blobAccent(0.07)), + ), + Positioned( + top: 350 + t * 25, + left: 80 + t * 20, + child: AuthBlob(size: 160, color: _blob(0.04)), + ), + Positioned( + top: -40 - t * 10, + left: 120 + t * 30, + child: AuthBlob(size: 180, color: _blobAccent(0.05)), + ), + ], + ); + }, + ); + } +} + +/// A simple solid circle used as a background blob. +class AuthBlob extends StatelessWidget { + const AuthBlob({super.key, required this.size, required this.color}); + + final double size; + final Color color; + + @override + Widget build(BuildContext context) { + return Container( + width: size, + height: size, + decoration: BoxDecoration(shape: BoxShape.circle, color: color), + ); + } +} diff --git a/lib/features/auth/onboarding_repository.dart b/lib/features/auth/onboarding_repository.dart new file mode 100644 index 0000000..a263715 --- /dev/null +++ b/lib/features/auth/onboarding_repository.dart @@ -0,0 +1,32 @@ +import 'package:pocketbase/pocketbase.dart'; +import '../../core/api/pocketbase_client.dart'; +import '../../core/auth/auth_repository.dart'; + +class OnboardingRepository { + OnboardingRepository._(); + static final instance = OnboardingRepository._(); + + PocketBase get _pb => PocketBaseClient.instance.pb; + + Future createTenantAndJoin({ + required String kind, + required String companyName, + }) async { + final userId = _pb.authStore.record!.id; + + final tenant = await _pb.collection('tenants').create(body: { + 'kind': kind, + 'company_name': companyName, + 'status': 'active', + 'default_currency': 'TRY', + }); + + await _pb.collection('tenant_members').create(body: { + 'tenant_id': tenant.id, + 'user_id': userId, + 'role': 'owner', + }); + + return AuthRepository.instance.refreshSession(); + } +} diff --git a/lib/features/auth/onboarding_screen.dart b/lib/features/auth/onboarding_screen.dart new file mode 100644 index 0000000..0e0cecc --- /dev/null +++ b/lib/features/auth/onboarding_screen.dart @@ -0,0 +1,461 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import '../../core/providers/auth_provider.dart'; +import 'onboarding_repository.dart'; + +class OnboardingScreen extends ConsumerStatefulWidget { + const OnboardingScreen({super.key}); + + @override + ConsumerState createState() => _OnboardingScreenState(); +} + +class _OnboardingScreenState extends ConsumerState + with SingleTickerProviderStateMixin { + final _formKey = GlobalKey(); + final _nameCtrl = TextEditingController(); + String _selectedKind = 'clinic'; + bool _loading = false; + String? _error; + late AnimationController _animCtrl; + late Animation _fadeAnim; + late Animation _slideAnim; + + @override + void initState() { + super.initState(); + _animCtrl = AnimationController( + vsync: this, + duration: const Duration(milliseconds: 600), + ); + _fadeAnim = CurvedAnimation(parent: _animCtrl, curve: Curves.easeOut); + _slideAnim = Tween( + begin: const Offset(0, 0.08), + end: Offset.zero, + ).animate(CurvedAnimation(parent: _animCtrl, curve: Curves.easeOutCubic)); + _animCtrl.forward(); + } + + @override + void dispose() { + _animCtrl.dispose(); + _nameCtrl.dispose(); + super.dispose(); + } + + Future _create() async { + if (!_formKey.currentState!.validate()) return; + setState(() { + _loading = true; + _error = null; + }); + try { + final result = await OnboardingRepository.instance.createTenantAndJoin( + kind: _selectedKind, + companyName: _nameCtrl.text.trim(), + ); + if (!mounted) return; + ref.read(authProvider.notifier).setActiveTenant(result.tenants.first); + } catch (e) { + setState(() { + _error = 'Hesap oluşturulamadı. Lütfen tekrar deneyin.'; + _loading = false; + }); + } + } + + @override + Widget build(BuildContext context) { + final cs = Theme.of(context).colorScheme; + final size = MediaQuery.sizeOf(context); + + return Scaffold( + backgroundColor: const Color(0xFF4F46E5), + body: Stack( + children: [ + // ── Gradient background ────────────────────────────────────────── + Container( + decoration: const BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topLeft, + end: Alignment.bottomCenter, + colors: [Color(0xFF3730A3), Color(0xFF6366F1)], + ), + ), + ), + + // ── Decorative circles ─────────────────────────────────────────── + Positioned( + top: -40, + right: -60, + child: Container( + width: 220, + height: 220, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: Colors.white.withValues(alpha: 0.06), + ), + ), + ), + Positioned( + top: 80, + left: -70, + child: Container( + width: 200, + height: 200, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: Colors.white.withValues(alpha: 0.04), + ), + ), + ), + + // ── Content ────────────────────────────────────────────────────── + Column( + children: [ + // Header + SafeArea( + bottom: false, + child: SizedBox( + height: size.height * 0.26, + child: Stack( + children: [ + // Sign out + Positioned( + right: 8, + top: 4, + child: TextButton.icon( + onPressed: () => + ref.read(authProvider.notifier).signOut(), + icon: const Icon(Icons.logout_rounded, + color: Colors.white70, size: 18), + label: const Text( + 'Çıkış', + style: TextStyle(color: Colors.white70), + ), + ), + ), + Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Container( + width: 68, + height: 68, + decoration: BoxDecoration( + color: Colors.white.withValues(alpha: 0.15), + borderRadius: BorderRadius.circular(20), + border: Border.all( + color: Colors.white.withValues(alpha: 0.3), + width: 1.5, + ), + ), + child: const Icon( + Icons.domain_add_rounded, + size: 32, + color: Colors.white, + ), + ), + const SizedBox(height: 14), + const Text( + 'Kurumunuzu Oluşturun', + style: TextStyle( + color: Colors.white, + fontSize: 20, + fontWeight: FontWeight.w700, + letterSpacing: 0.3, + ), + ), + const SizedBox(height: 4), + Text( + 'Klinik veya laboratuvar olarak kayıt olun', + style: TextStyle( + color: Colors.white.withValues(alpha: 0.70), + fontSize: 13, + ), + ), + ], + ), + ), + ], + ), + ), + ), + + // Form card + Expanded( + child: FadeTransition( + opacity: _fadeAnim, + child: SlideTransition( + position: _slideAnim, + child: Container( + decoration: BoxDecoration( + color: cs.surface, + borderRadius: const BorderRadius.vertical( + top: Radius.circular(32), + ), + boxShadow: [ + BoxShadow( + color: Colors.black.withValues(alpha: 0.15), + blurRadius: 24, + offset: const Offset(0, -4), + ), + ], + ), + child: SingleChildScrollView( + padding: const EdgeInsets.fromLTRB(28, 32, 28, 24), + child: Form( + key: _formKey, + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Text( + 'Kurum Türünü Seçin', + style: Theme.of(context) + .textTheme + .titleMedium + ?.copyWith(fontWeight: FontWeight.w600), + ), + const SizedBox(height: 14), + + // Kind cards + Row( + children: [ + Expanded( + child: _KindCard( + icon: Icons.local_hospital_outlined, + label: 'Klinik', + description: 'Diş kliniği', + value: 'clinic', + selected: _selectedKind == 'clinic', + onTap: () => setState( + () => _selectedKind = 'clinic'), + ), + ), + const SizedBox(width: 12), + Expanded( + child: _KindCard( + icon: Icons.science_outlined, + label: 'Laboratuvar', + description: 'Diş laboratuvarı', + value: 'lab', + selected: _selectedKind == 'lab', + onTap: () => + setState(() => _selectedKind = 'lab'), + ), + ), + ], + ), + + const SizedBox(height: 24), + + // Company name + Text( + 'Kurum Adı', + style: Theme.of(context) + .textTheme + .titleMedium + ?.copyWith(fontWeight: FontWeight.w600), + ), + const SizedBox(height: 10), + TextFormField( + controller: _nameCtrl, + textInputAction: TextInputAction.done, + textCapitalization: TextCapitalization.words, + onFieldSubmitted: (_) => _create(), + decoration: InputDecoration( + labelText: _selectedKind == 'clinic' + ? 'Klinik Adı' + : 'Laboratuvar Adı', + prefixIcon: const Icon( + Icons.business_outlined, + size: 20), + filled: true, + fillColor: cs.surfaceContainerHighest, + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(14), + borderSide: BorderSide.none, + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(14), + borderSide: BorderSide.none, + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(14), + borderSide: const BorderSide( + color: Color(0xFF4F46E5), width: 2), + ), + errorBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(14), + borderSide: BorderSide( + color: cs.error, width: 1.5), + ), + focusedErrorBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(14), + borderSide: + BorderSide(color: cs.error, width: 2), + ), + contentPadding: const EdgeInsets.symmetric( + horizontal: 16, vertical: 16), + ), + validator: (v) { + if (v == null || v.trim().isEmpty) { + return 'Kurum adı gereklidir'; + } + if (v.trim().length < 3) { + return 'En az 3 karakter olmalıdır'; + } + return null; + }, + ), + + // Error banner + if (_error != null) ...[ + const SizedBox(height: 14), + Container( + padding: const EdgeInsets.symmetric( + horizontal: 14, vertical: 10), + decoration: BoxDecoration( + color: cs.errorContainer, + borderRadius: BorderRadius.circular(12), + ), + child: Row( + children: [ + Icon(Icons.error_outline_rounded, + color: cs.onErrorContainer, size: 18), + const SizedBox(width: 8), + Expanded( + child: Text( + _error!, + style: TextStyle( + color: cs.onErrorContainer, + fontSize: 13), + ), + ), + ], + ), + ), + ], + + const SizedBox(height: 28), + + FilledButton( + onPressed: _loading ? null : _create, + style: FilledButton.styleFrom( + minimumSize: const Size.fromHeight(52), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(14), + ), + backgroundColor: const Color(0xFF4F46E5), + ), + child: _loading + ? const SizedBox( + height: 22, + width: 22, + child: CircularProgressIndicator( + strokeWidth: 2.5, + color: Colors.white, + ), + ) + : const Text( + 'Devam Et', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + ), + ), + ), + ], + ), + ), + ), + ), + ), + ), + ), + ], + ), + ], + ), + ); + } +} + +class _KindCard extends StatelessWidget { + const _KindCard({ + required this.icon, + required this.label, + required this.description, + required this.value, + required this.selected, + required this.onTap, + }); + + final IconData icon; + final String label; + final String description; + final String value; + final bool selected; + final VoidCallback onTap; + + @override + Widget build(BuildContext context) { + final cs = Theme.of(context).colorScheme; + return InkWell( + onTap: onTap, + borderRadius: BorderRadius.circular(16), + child: AnimatedContainer( + duration: const Duration(milliseconds: 180), + padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 12), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(16), + border: Border.all( + color: selected ? const Color(0xFF4F46E5) : cs.outlineVariant, + width: selected ? 2 : 1, + ), + color: selected + ? const Color(0xFF4F46E5).withValues(alpha: 0.08) + : cs.surfaceContainerLow, + ), + child: Column( + children: [ + Container( + width: 48, + height: 48, + decoration: BoxDecoration( + color: selected + ? const Color(0xFF4F46E5).withValues(alpha: 0.12) + : cs.surfaceContainerHighest, + borderRadius: BorderRadius.circular(12), + ), + child: Icon( + icon, + size: 26, + color: selected + ? const Color(0xFF4F46E5) + : cs.onSurfaceVariant, + ), + ), + const SizedBox(height: 10), + Text( + label, + style: TextStyle( + fontWeight: FontWeight.w600, + fontSize: 14, + color: selected ? const Color(0xFF4F46E5) : cs.onSurface, + ), + ), + const SizedBox(height: 2), + Text( + description, + style: TextStyle( + fontSize: 11, + color: cs.onSurfaceVariant, + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/features/auth/sign_in_screen.dart b/lib/features/auth/sign_in_screen.dart new file mode 100644 index 0000000..ef9d038 --- /dev/null +++ b/lib/features/auth/sign_in_screen.dart @@ -0,0 +1,888 @@ +import 'dart:ui'; +import 'package:flutter/material.dart'; +import 'package:flutter_animate/flutter_animate.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:go_router/go_router.dart'; +import '../../core/l10n/app_strings.dart'; +import '../../core/providers/auth_provider.dart'; +import '../../core/providers/locale_provider.dart'; +import '../../core/router/app_router.dart'; +import '../../core/theme/app_theme.dart'; +import '../../core/widgets/tooth_logo.dart'; + +class SignInScreen extends ConsumerStatefulWidget { + const SignInScreen({super.key}); + + @override + ConsumerState createState() => _SignInScreenState(); +} + +class _SignInScreenState extends ConsumerState { + final _formKey = GlobalKey(); + final _emailCtrl = TextEditingController(); + final _passCtrl = TextEditingController(); + bool _obscure = true; + + @override + void dispose() { + _emailCtrl.dispose(); + _passCtrl.dispose(); + super.dispose(); + } + + Future _submit() async { + if (!_formKey.currentState!.validate()) return; + await ref + .read(authProvider.notifier) + .signIn(_emailCtrl.text.trim(), _passCtrl.text); + } + + @override + Widget build(BuildContext context) { + final auth = ref.watch(authProvider); + final s = ref.watch(stringsProvider); + final locale = ref.watch(localeProvider); + final isDesktop = MediaQuery.sizeOf(context).width > 800; + + return Scaffold( + backgroundColor: AppColors.background, + body: isDesktop + ? _buildDesktop(context, auth, s, locale) + : _buildMobile(context, auth, s, locale), + ); + } + + // ── Mobile ───────────────────────────────────────────────────────────────── + + Widget _buildMobile( + BuildContext context, dynamic auth, AppStrings s, Locale locale) { + return Stack( + children: [ + SafeArea( + child: SingleChildScrollView( + padding: const EdgeInsets.symmetric(horizontal: 24), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + const SizedBox(height: 56), + + // Logo mark + Center( + child: Container( + width: 68, + height: 68, + decoration: BoxDecoration( + gradient: const LinearGradient( + begin: Alignment.topLeft, + end: Alignment.bottomRight, + colors: [Color(0xFF0B1D35), Color(0xFF1A5C8A)], + ), + borderRadius: BorderRadius.circular(20), + boxShadow: [ + BoxShadow( + color: const Color(0xFF0B1D35).withValues(alpha: 0.3), + blurRadius: 20, + offset: const Offset(0, 8), + ), + ], + ), + child: + const Center(child: ToothLogo(size: 34, color: Colors.white)), + ), + ).animate().fadeIn(duration: 400.ms).scale(begin: const Offset(0.8, 0.8)), + const SizedBox(height: 24), + + Center( + child: Text( + s.signInWelcome, + style: const TextStyle( + fontSize: 26, + fontWeight: FontWeight.w800, + color: AppColors.textPrimary, + letterSpacing: -0.5, + ), + ), + ).animate(delay: 60.ms).fadeIn(duration: 400.ms).slideY(begin: 0.1), + const SizedBox(height: 6), + Center( + child: Text( + s.signInSubtitle, + style: const TextStyle( + fontSize: 14, color: AppColors.textSecondary), + ), + ).animate(delay: 100.ms).fadeIn(duration: 400.ms), + const SizedBox(height: 36), + + _buildFormFields(auth, s), + + const SizedBox(height: 24), + _buildSignUpLink(context, s), + const SizedBox(height: 32), + ], + ), + ), + ), + Positioned( + top: MediaQuery.paddingOf(context).top + 12, + right: 12, + child: _LanguageButton(locale: locale, s: s, ref: ref), + ), + ], + ); + } + + // ── Desktop ──────────────────────────────────────────────────────────────── + + Widget _buildDesktop( + BuildContext context, dynamic auth, AppStrings s, Locale locale) { + return Row( + children: [ + // LEFT PANEL + Expanded( + flex: 55, + child: Stack( + fit: StackFit.expand, + children: [ + Container( + decoration: const BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topLeft, + end: Alignment.bottomRight, + stops: [0.0, 0.55, 1.0], + colors: [ + Color(0xFF080F1E), + Color(0xFF0D2D58), + Color(0xFF0E4A82), + ], + ), + ), + ), + const Positioned(top: -140, left: -140, child: _Ring(size: 520, opacity: 0.06)), + const Positioned(bottom: -100, right: -100, child: _Ring(size: 400, opacity: 0.05)), + const Positioned(top: 160, right: 60, child: _Ring(size: 100, opacity: 0.09)), + const Positioned(bottom: 220, left: 60, child: _Ring(size: 70, opacity: 0.07)), + Padding( + padding: + const EdgeInsets.symmetric(horizontal: 64, vertical: 52), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + width: 38, + height: 38, + decoration: BoxDecoration( + color: Colors.white.withValues(alpha: 0.12), + borderRadius: BorderRadius.circular(10), + border: Border.all( + color: Colors.white.withValues(alpha: 0.2), + ), + ), + child: const Center( + child: ToothLogo(size: 20, color: Colors.white)), + ), + const SizedBox(width: 12), + const Text( + 'DLS', + style: TextStyle( + color: Colors.white, + fontSize: 18, + fontWeight: FontWeight.w800, + letterSpacing: 1.5, + ), + ), + ], + ).animate().fadeIn(duration: 500.ms), + const Spacer(), + Text( + s.signInHeadline, + style: const TextStyle( + color: Colors.white, + fontSize: 46, + fontWeight: FontWeight.w800, + height: 1.1, + letterSpacing: -1.0, + ), + ) + .animate(delay: 100.ms) + .fadeIn(duration: 500.ms) + .slideY(begin: 0.1, end: 0), + const SizedBox(height: 18), + Text( + s.signInTagline, + style: TextStyle( + color: Colors.white.withValues(alpha: 0.6), + fontSize: 16, + height: 1.6, + ), + ).animate(delay: 160.ms).fadeIn(duration: 500.ms), + const SizedBox(height: 44), + const _DashboardPreviewCard() + .animate(delay: 220.ms) + .fadeIn(duration: 600.ms) + .slideY(begin: 0.12, end: 0), + const Spacer(), + Text( + s.footerCopyright, + style: TextStyle( + color: Colors.white.withValues(alpha: 0.3), + fontSize: 12, + ), + ).animate(delay: 300.ms).fadeIn(duration: 500.ms), + ], + ), + ), + ], + ), + ), + + // RIGHT PANEL + Stack( + children: [ + Container( + width: 460, + color: Colors.white, + child: SafeArea( + child: LayoutBuilder( + builder: (context, constraints) => SingleChildScrollView( + child: ConstrainedBox( + constraints: + BoxConstraints(minHeight: constraints.maxHeight), + child: IntrinsicHeight( + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 52, vertical: 40), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Container( + width: 44, + height: 44, + decoration: BoxDecoration( + gradient: const LinearGradient( + begin: Alignment.topLeft, + end: Alignment.bottomRight, + colors: [ + Color(0xFF0B1D35), + Color(0xFF1A5C8A) + ], + ), + borderRadius: BorderRadius.circular(12), + ), + child: const Center( + child: ToothLogo( + size: 24, color: Colors.white)), + ).animate().fadeIn(duration: 400.ms), + const SizedBox(height: 32), + Text( + s.signInWelcome, + style: const TextStyle( + fontSize: 28, + fontWeight: FontWeight.w800, + color: AppColors.textPrimary, + letterSpacing: -0.5, + ), + ) + .animate(delay: 60.ms) + .fadeIn(duration: 400.ms) + .slideY(begin: 0.08, end: 0), + const SizedBox(height: 6), + Text( + s.signInSubtitle, + style: const TextStyle( + fontSize: 15, + color: AppColors.textSecondary, + ), + ).animate(delay: 100.ms).fadeIn(duration: 400.ms), + const SizedBox(height: 40), + _buildFormFields(auth, s) + .animate(delay: 140.ms) + .fadeIn(duration: 400.ms) + .slideY(begin: 0.08, end: 0), + const SizedBox(height: 28), + _buildSignUpLink(context, s) + .animate(delay: 200.ms) + .fadeIn(duration: 400.ms), + ], + ), + ), + ), + ), + ), + ), + ), + ), + Positioned( + top: MediaQuery.paddingOf(context).top + 16, + right: 16, + child: _LanguageButton(locale: locale, s: s, ref: ref), + ), + ], + ), + ], + ); + } + + // ── Form fields (shared) ──────────────────────────────────────────────────── + + Widget _buildFormFields(dynamic auth, AppStrings s) { + return Form( + key: _formKey, + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + _Field( + controller: _emailCtrl, + label: s.emailAddress, + icon: Icons.email_outlined, + keyboardType: TextInputType.emailAddress, + textInputAction: TextInputAction.next, + validator: (v) => + (v == null || v.trim().isEmpty) ? s.emailRequired : null, + ), + const SizedBox(height: 14), + + _Field( + controller: _passCtrl, + label: s.password, + icon: Icons.lock_outline_rounded, + obscureText: _obscure, + textInputAction: TextInputAction.done, + onFieldSubmitted: (_) => _submit(), + suffixIcon: IconButton( + icon: Icon( + _obscure + ? Icons.visibility_outlined + : Icons.visibility_off_outlined, + size: 20, + color: AppColors.textSecondary, + ), + onPressed: () => setState(() => _obscure = !_obscure), + ), + validator: (v) => + (v == null || v.isEmpty) ? s.passwordRequired : null, + ), + + if (auth.error != null) ...[ + const SizedBox(height: 14), + Container( + padding: + const EdgeInsets.symmetric(horizontal: 14, vertical: 10), + decoration: BoxDecoration( + color: const Color(0xFFFEF2F2), + borderRadius: BorderRadius.circular(10), + border: Border.all( + color: AppColors.cancelled.withValues(alpha: 0.25)), + ), + child: Row( + children: [ + const Icon(Icons.error_outline_rounded, + color: AppColors.cancelled, size: 16), + const SizedBox(width: 8), + Expanded( + child: Text( + auth.error!, + style: const TextStyle( + color: AppColors.cancelled, fontSize: 13), + ), + ), + ], + ), + ), + ], + + const SizedBox(height: 24), + + DecoratedBox( + decoration: BoxDecoration( + gradient: const LinearGradient( + colors: [Color(0xFF0B1D35), Color(0xFF1A5C8A)], + ), + borderRadius: BorderRadius.circular(12), + boxShadow: [ + BoxShadow( + color: const Color(0xFF0B1D35).withValues(alpha: 0.35), + blurRadius: 16, + offset: const Offset(0, 6), + ), + ], + ), + child: FilledButton( + onPressed: auth.isLoading ? null : _submit, + style: FilledButton.styleFrom( + backgroundColor: Colors.transparent, + foregroundColor: Colors.white, + disabledForegroundColor: Colors.white.withValues(alpha: 0.5), + disabledBackgroundColor: Colors.transparent, + shadowColor: Colors.transparent, + minimumSize: const Size.fromHeight(52), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12)), + ), + child: auth.isLoading + ? const SizedBox( + width: 22, + height: 22, + child: CircularProgressIndicator( + strokeWidth: 2.5, color: Colors.white), + ) + : Text( + s.signIn, + style: const TextStyle( + fontSize: 15, fontWeight: FontWeight.w600), + ), + ), + ), + ], + ), + ); + } + + // ── Sign-up link ─────────────────────────────────────────────────────────── + + Widget _buildSignUpLink(BuildContext context, AppStrings s) { + return Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + s.noAccount, + style: + const TextStyle(color: AppColors.textSecondary, fontSize: 14), + ), + TextButton( + onPressed: () => context.go(routeSignUp), + style: TextButton.styleFrom( + foregroundColor: const Color(0xFF0D4C85), + padding: const EdgeInsets.symmetric(horizontal: 8), + ), + child: Text( + s.signUp, + style: + const TextStyle(fontWeight: FontWeight.w700, fontSize: 14), + ), + ), + ], + ); + } +} + +// ── Language button ─────────────────────────────────────────────────────────── + +class _LanguageButton extends StatelessWidget { + const _LanguageButton( + {required this.locale, required this.s, required this.ref}); + final Locale locale; + final AppStrings s; + final WidgetRef ref; + + static const _flags = { + 'tr': '🇹🇷', + 'en': '🇬🇧', + 'ru': '🇷🇺', + 'ar': '🇸🇦', + 'de': '🇩🇪', + }; + + @override + Widget build(BuildContext context) { + final flag = _flags[locale.languageCode] ?? '🌐'; + return GestureDetector( + onTap: () => _showPicker(context), + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6), + decoration: BoxDecoration( + color: AppColors.surface, + borderRadius: BorderRadius.circular(20), + border: Border.all(color: AppColors.border), + boxShadow: [ + BoxShadow( + color: Colors.black.withValues(alpha: 0.06), + blurRadius: 8, + offset: const Offset(0, 2), + ), + ], + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text(flag, style: const TextStyle(fontSize: 15)), + const SizedBox(width: 4), + const Icon(Icons.expand_more_rounded, + size: 14, color: AppColors.textSecondary), + ], + ), + ), + ); + } + + void _showPicker(BuildContext context) { + final options = [ + ('tr', '🇹🇷', s.languageTurkish), + ('en', '🇬🇧', s.languageEnglish), + ('ru', '🇷🇺', s.languageRussian), + ('ar', '🇸🇦', s.languageArabic), + ('de', '🇩🇪', s.languageGerman), + ]; + showModalBottomSheet( + context: context, + backgroundColor: Colors.transparent, + builder: (_) => Container( + decoration: const BoxDecoration( + color: AppColors.surface, + borderRadius: BorderRadius.vertical(top: Radius.circular(20)), + ), + padding: const EdgeInsets.all(20), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Center( + child: Container( + width: 40, + height: 4, + decoration: BoxDecoration( + color: AppColors.border, + borderRadius: BorderRadius.circular(2), + ), + ), + ), + const SizedBox(height: 16), + Text( + s.languageSelection, + style: const TextStyle( + fontSize: 17, + fontWeight: FontWeight.w700, + color: AppColors.textPrimary, + ), + ), + const SizedBox(height: 12), + for (final (code, flag, label) in options) + ListTile( + contentPadding: const EdgeInsets.symmetric(horizontal: 4), + leading: Text(flag, style: const TextStyle(fontSize: 24)), + title: Text( + label, + style: const TextStyle( + fontSize: 15, + fontWeight: FontWeight.w500, + color: AppColors.textPrimary, + ), + ), + trailing: locale.languageCode == code + ? const Icon(Icons.check_circle_rounded, + color: AppColors.accent) + : null, + onTap: () { + ref.read(localeProvider.notifier).setLocale(Locale(code)); + Navigator.pop(context); + }, + ), + SizedBox(height: MediaQuery.paddingOf(context).bottom + 4), + ], + ), + ), + ); + } +} + +// ── Decorative ring ─────────────────────────────────────────────────────────── + +class _Ring extends StatelessWidget { + const _Ring({required this.size, required this.opacity}); + final double size; + final double opacity; + + @override + Widget build(BuildContext context) { + return Container( + width: size, + height: size, + decoration: BoxDecoration( + shape: BoxShape.circle, + border: Border.all( + color: Colors.white.withValues(alpha: opacity), + width: 1.5, + ), + ), + ); + } +} + +// ── Dashboard preview card (glassmorphism) ──────────────────────────────────── + +class _DashboardPreviewCard extends StatelessWidget { + const _DashboardPreviewCard(); + + @override + Widget build(BuildContext context) { + return ClipRRect( + borderRadius: BorderRadius.circular(20), + child: BackdropFilter( + filter: ImageFilter.blur(sigmaX: 16, sigmaY: 16), + child: Container( + width: 340, + padding: const EdgeInsets.all(20), + decoration: BoxDecoration( + color: Colors.white.withValues(alpha: 0.08), + borderRadius: BorderRadius.circular(20), + border: Border.all( + color: Colors.white.withValues(alpha: 0.12), + ), + ), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Container( + width: 28, + height: 28, + decoration: BoxDecoration( + color: Colors.white.withValues(alpha: 0.12), + borderRadius: BorderRadius.circular(8), + ), + child: const Icon( + Icons.bar_chart_rounded, + color: Colors.white, + size: 15, + ), + ), + const SizedBox(width: 10), + Text( + 'Bugünkü Durum', + style: TextStyle( + color: Colors.white.withValues(alpha: 0.9), + fontWeight: FontWeight.w600, + fontSize: 13, + ), + ), + const Spacer(), + Container( + padding: + const EdgeInsets.symmetric(horizontal: 10, vertical: 4), + decoration: BoxDecoration( + color: Colors.white.withValues(alpha: 0.1), + borderRadius: BorderRadius.circular(20), + ), + child: Text( + 'Canlı', + style: TextStyle( + color: Colors.white.withValues(alpha: 0.7), + fontSize: 11, + fontWeight: FontWeight.w500, + ), + ), + ), + ], + ), + const SizedBox(height: 18), + const Row( + children: [ + _StatChip(value: '24', label: 'Aktif', color: Color(0xFF60A5FA)), + SizedBox(width: 8), + _StatChip( + value: '8', label: 'Bekliyor', color: Color(0xFFFBBF24)), + SizedBox(width: 8), + _StatChip( + value: '142', label: 'Bu ay', color: Color(0xFF34D399)), + ], + ), + const SizedBox(height: 18), + const _PreviewBar( + label: 'Zirkon', value: 0.76, color: Color(0xFF60A5FA)), + const SizedBox(height: 10), + const _PreviewBar( + label: 'Metal alt.', value: 0.48, color: Color(0xFFFBBF24)), + const SizedBox(height: 10), + const _PreviewBar( + label: 'Porselen', value: 0.62, color: Color(0xFF34D399)), + ], + ), + ), + ), + ); + } +} + +class _StatChip extends StatelessWidget { + const _StatChip({ + required this.value, + required this.label, + required this.color, + }); + final String value; + final String label; + final Color color; + + @override + Widget build(BuildContext context) { + return Expanded( + child: Container( + padding: const EdgeInsets.symmetric(vertical: 10), + decoration: BoxDecoration( + color: color.withValues(alpha: 0.12), + borderRadius: BorderRadius.circular(10), + border: Border.all(color: color.withValues(alpha: 0.2)), + ), + child: Column( + children: [ + Text( + value, + style: TextStyle( + color: color, + fontSize: 18, + fontWeight: FontWeight.w800, + ), + ), + const SizedBox(height: 2), + Text( + label, + style: TextStyle( + color: Colors.white.withValues(alpha: 0.55), + fontSize: 11, + ), + ), + ], + ), + ), + ); + } +} + +class _PreviewBar extends StatelessWidget { + const _PreviewBar({ + required this.label, + required this.value, + required this.color, + }); + final String label; + final double value; + final Color color; + + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + label, + style: TextStyle( + color: Colors.white.withValues(alpha: 0.65), + fontSize: 12, + ), + ), + Text( + '${(value * 100).toInt()}%', + style: TextStyle( + color: Colors.white.withValues(alpha: 0.65), + fontSize: 12, + fontWeight: FontWeight.w600, + ), + ), + ], + ), + const SizedBox(height: 5), + LayoutBuilder( + builder: (_, constraints) => Stack( + children: [ + Container( + height: 5, + width: constraints.maxWidth, + decoration: BoxDecoration( + color: Colors.white.withValues(alpha: 0.08), + borderRadius: BorderRadius.circular(10), + ), + ), + Container( + height: 5, + width: constraints.maxWidth * value, + decoration: BoxDecoration( + color: color, + borderRadius: BorderRadius.circular(10), + ), + ), + ], + ), + ), + ], + ); + } +} + +// ── Form field ──────────────────────────────────────────────────────────────── + +class _Field extends StatelessWidget { + const _Field({ + required this.controller, + required this.label, + required this.icon, + this.keyboardType, + this.textInputAction, + this.obscureText = false, + this.suffixIcon, + this.onFieldSubmitted, + this.validator, + }); + + final TextEditingController controller; + final String label; + final IconData icon; + final TextInputType? keyboardType; + final TextInputAction? textInputAction; + final bool obscureText; + final Widget? suffixIcon; + final ValueChanged? onFieldSubmitted; + final FormFieldValidator? validator; + + @override + Widget build(BuildContext context) { + return TextFormField( + controller: controller, + keyboardType: keyboardType, + textInputAction: textInputAction, + obscureText: obscureText, + onFieldSubmitted: onFieldSubmitted, + validator: validator, + style: const TextStyle(fontSize: 15, color: AppColors.textPrimary), + decoration: InputDecoration( + labelText: label, + prefixIcon: Icon(icon, size: 20, color: AppColors.textSecondary), + suffixIcon: suffixIcon, + filled: true, + fillColor: const Color(0xFFF8FAFC), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: const BorderSide(color: AppColors.border), + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: const BorderSide(color: AppColors.border), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: const BorderSide(color: Color(0xFF0D4C85), width: 2), + ), + errorBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: + const BorderSide(color: AppColors.cancelled, width: 1.5), + ), + focusedErrorBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: const BorderSide(color: AppColors.cancelled, width: 2), + ), + contentPadding: + const EdgeInsets.symmetric(horizontal: 16, vertical: 14), + labelStyle: const TextStyle( + color: AppColors.textSecondary, fontSize: 14), + ), + ); + } +} diff --git a/lib/features/auth/sign_up_screen.dart b/lib/features/auth/sign_up_screen.dart new file mode 100644 index 0000000..fb1914e --- /dev/null +++ b/lib/features/auth/sign_up_screen.dart @@ -0,0 +1,619 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_animate/flutter_animate.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:go_router/go_router.dart'; +import '../../core/providers/auth_provider.dart'; +import '../../core/router/app_router.dart'; +import '../../core/theme/app_theme.dart'; +import '../../core/widgets/tooth_logo.dart'; +import 'auth_widgets.dart'; + +class SignUpScreen extends ConsumerStatefulWidget { + const SignUpScreen({super.key}); + + @override + ConsumerState createState() => _SignUpScreenState(); +} + +class _SignUpScreenState extends ConsumerState { + final _formKey = GlobalKey(); + final _firstNameCtrl = TextEditingController(); + final _lastNameCtrl = TextEditingController(); + final _emailCtrl = TextEditingController(); + final _passCtrl = TextEditingController(); + final _confirmPassCtrl = TextEditingController(); + bool _obscure = true; + bool _obscureConfirm = true; + bool _loading = false; + String? _error; + + @override + void dispose() { + _firstNameCtrl.dispose(); + _lastNameCtrl.dispose(); + _emailCtrl.dispose(); + _passCtrl.dispose(); + _confirmPassCtrl.dispose(); + super.dispose(); + } + + Future _submit() async { + if (!_formKey.currentState!.validate()) return; + setState(() { + _loading = true; + _error = null; + }); + try { + await ref.read(authProvider.notifier).register( + email: _emailCtrl.text.trim(), + password: _passCtrl.text, + firstName: _firstNameCtrl.text.trim(), + lastName: _lastNameCtrl.text.trim(), + ); + } catch (e) { + setState(() { + _error = _parseError(e.toString()); + _loading = false; + }); + } + } + + String _parseError(String msg) { + if (msg.contains('already') || msg.contains('unique') || msg.contains('UNIQUE')) { + return 'Bu e-posta adresi zaten kayıtlı.'; + } + if (msg.contains('403') || msg.contains('Forbidden')) { + return 'Kayıt şu anda kapalı. Lütfen yönetici ile iletişime geçin.'; + } + return 'Kayıt olunamadı. Lütfen tekrar deneyin.'; + } + + @override + Widget build(BuildContext context) { + final isDesktop = MediaQuery.sizeOf(context).width > 800; + + return Scaffold( + backgroundColor: AppColors.background, + body: isDesktop ? _buildDesktop(context) : _buildMobile(context), + ); + } + + // ── Mobile layout ────────────────────────────────────────────────────────── + + Widget _buildMobile(BuildContext context) { + return SafeArea( + child: SingleChildScrollView( + padding: const EdgeInsets.symmetric(horizontal: 24), + child: _buildForm(context, isMobile: true), + ), + ); + } + + // ── Desktop layout ───────────────────────────────────────────────────────── + + Widget _buildDesktop(BuildContext context) { + return Row( + children: [ + // LEFT PANEL — solid gradient + white animated blobs on top + Expanded( + flex: 5, + child: Stack( + fit: StackFit.expand, + children: [ + Container( + decoration: const BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topLeft, + end: Alignment.bottomRight, + colors: [AppColors.primary, Color(0xFF1A5C8A)], + ), + ), + ), + const AnimatedAuthBg(bright: true), + Center( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 56), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + width: 72, + height: 72, + decoration: BoxDecoration( + color: Colors.white.withValues(alpha: 0.15), + borderRadius: BorderRadius.circular(20), + border: Border.all( + color: Colors.white.withValues(alpha: 0.3), + width: 1.5, + ), + ), + child: const Center(child: ToothLogo(size: 38, color: Colors.white)), + ), + const SizedBox(height: 24), + const Text( + 'DLS', + style: TextStyle( + fontSize: 48, + fontWeight: FontWeight.w800, + color: Colors.white, + letterSpacing: 2, + ), + ), + const SizedBox(height: 4), + Text( + 'Dental Lab Sistemi', + style: TextStyle( + fontSize: 17, + color: Colors.white.withValues(alpha: 0.7), + fontWeight: FontWeight.w500, + ), + ), + const SizedBox(height: 48), + const _FeatureBullet( + icon: Icons.dashboard_rounded, + text: 'İş takibi tek ekranda', + ), + const SizedBox(height: 16), + const _FeatureBullet( + icon: Icons.link_rounded, + text: 'Klinik-lab bağlantısı', + ), + const SizedBox(height: 16), + const _FeatureBullet( + icon: Icons.bolt_rounded, + text: 'Gerçek zamanlı durum', + ), + ], + ), + ), + ), + ], + ), + ), + + // RIGHT PANEL — light gray so white card stands out + Container( + width: 480, + color: AppColors.background, + child: SafeArea( + child: LayoutBuilder( + builder: (context, constraints) => SingleChildScrollView( + child: ConstrainedBox( + constraints: BoxConstraints(minHeight: constraints.maxHeight), + child: IntrinsicHeight( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 40, vertical: 32), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [_buildForm(context, isMobile: false)], + ), + ), + ), + ), + ), + ), + ), + ), + ], + ); + } + + // ── Shared form content ──────────────────────────────────────────────────── + + Widget _buildForm(BuildContext context, {required bool isMobile}) { + return Form( + key: _formKey, + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + if (isMobile) const SizedBox(height: 48), + + // ── Back button + branding (mobile only) ─────────────────────── + if (isMobile) ...[ + Row( + children: [ + IconButton( + onPressed: () => context.go(routeSignIn), + icon: const Icon(Icons.arrow_back_ios_new_rounded, size: 20), + style: IconButton.styleFrom( + foregroundColor: AppColors.textPrimary, + backgroundColor: AppColors.surface, + padding: const EdgeInsets.all(10), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + side: const BorderSide(color: AppColors.border), + ), + ), + ), + ], + ).animate().fadeIn(duration: 300.ms), + + const SizedBox(height: 28), + + Center( + child: Column( + children: [ + Container( + width: 72, + height: 72, + decoration: BoxDecoration( + gradient: const LinearGradient( + begin: Alignment.topLeft, + end: Alignment.bottomRight, + colors: [AppColors.primary, AppColors.accent], + ), + borderRadius: BorderRadius.circular(20), + boxShadow: [ + BoxShadow( + color: AppColors.accent.withValues(alpha: 0.3), + blurRadius: 18, + offset: const Offset(0, 6), + ), + ], + ), + child: const Icon( + Icons.person_add_alt_1_rounded, + size: 32, + color: Colors.white, + ), + ), + const SizedBox(height: 16), + const Text( + 'Hesap Oluştur', + style: TextStyle( + fontSize: 26, + fontWeight: FontWeight.w800, + color: AppColors.textPrimary, + ), + ), + const SizedBox(height: 4), + const Text( + 'DLS ağına katılın', + style: TextStyle( + fontSize: 14, + color: AppColors.textSecondary, + fontWeight: FontWeight.w500, + ), + ), + ], + ), + ).animate().fadeIn(duration: 400.ms).slideY(begin: -0.08, end: 0), + + const SizedBox(height: 32), + ], + + // Desktop back button (outside card) + if (!isMobile) ...[ + Row( + children: [ + IconButton( + onPressed: () => context.go(routeSignIn), + icon: const Icon(Icons.arrow_back_ios_new_rounded, size: 18), + style: IconButton.styleFrom( + foregroundColor: AppColors.textPrimary, + backgroundColor: AppColors.surface, + padding: const EdgeInsets.all(8), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + side: const BorderSide(color: AppColors.border), + ), + ), + ), + ], + ).animate().fadeIn(duration: 300.ms), + const SizedBox(height: 20), + ], + + // ── Form card ────────────────────────────────────────────────── + Container( + padding: EdgeInsets.all(isMobile ? 24 : 32), + decoration: BoxDecoration( + color: AppColors.surface, + borderRadius: BorderRadius.circular(20), + border: Border.all(color: AppColors.border), + boxShadow: [ + BoxShadow( + color: Colors.black.withValues(alpha: isMobile ? 0.05 : 0.09), + blurRadius: isMobile ? 16 : 28, + spreadRadius: 0, + offset: const Offset(0, 4), + ), + ], + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + // Heading inside card on desktop + if (!isMobile) ...[ + const Text( + 'Hesap Oluştur', + style: TextStyle( + fontSize: 24, + fontWeight: FontWeight.w800, + color: AppColors.textPrimary, + ), + ).animate().fadeIn(duration: 400.ms).slideY(begin: -0.08, end: 0), + const SizedBox(height: 4), + const Text( + 'DLS ağına katılın', + style: TextStyle(fontSize: 14, color: AppColors.textSecondary), + ).animate(delay: 40.ms).fadeIn(duration: 400.ms), + const SizedBox(height: 24), + ], + // Ad / Soyad satırı + Row( + children: [ + Expanded( + child: _Field( + controller: _firstNameCtrl, + label: 'Ad', + icon: Icons.badge_outlined, + textCapitalization: TextCapitalization.words, + textInputAction: TextInputAction.next, + validator: (v) => + (v == null || v.trim().isEmpty) ? 'Gerekli' : null, + ), + ), + const SizedBox(width: 12), + Expanded( + child: _Field( + controller: _lastNameCtrl, + label: 'Soyad', + icon: Icons.badge_outlined, + textCapitalization: TextCapitalization.words, + textInputAction: TextInputAction.next, + validator: (v) => + (v == null || v.trim().isEmpty) ? 'Gerekli' : null, + ), + ), + ], + ), + const SizedBox(height: 12), + + _Field( + controller: _emailCtrl, + label: 'E-posta', + icon: Icons.email_outlined, + keyboardType: TextInputType.emailAddress, + textInputAction: TextInputAction.next, + validator: (v) { + if (v == null || v.trim().isEmpty) return 'E-posta gereklidir'; + if (!v.contains('@')) return 'Geçerli bir e-posta girin'; + return null; + }, + ), + const SizedBox(height: 12), + + _Field( + controller: _passCtrl, + label: 'Şifre', + icon: Icons.lock_outline_rounded, + obscureText: _obscure, + textInputAction: TextInputAction.next, + suffixIcon: IconButton( + icon: Icon( + _obscure ? Icons.visibility_outlined : Icons.visibility_off_outlined, + size: 20, + color: AppColors.textSecondary, + ), + onPressed: () => setState(() => _obscure = !_obscure), + ), + validator: (v) { + if (v == null || v.isEmpty) return 'Şifre gereklidir'; + if (v.length < 8) return 'En az 8 karakter olmalıdır'; + return null; + }, + ), + const SizedBox(height: 12), + + _Field( + controller: _confirmPassCtrl, + label: 'Şifre Tekrar', + icon: Icons.lock_outline_rounded, + obscureText: _obscureConfirm, + textInputAction: TextInputAction.done, + onFieldSubmitted: (_) => _submit(), + suffixIcon: IconButton( + icon: Icon( + _obscureConfirm + ? Icons.visibility_outlined + : Icons.visibility_off_outlined, + size: 20, + color: AppColors.textSecondary, + ), + onPressed: () => setState(() => _obscureConfirm = !_obscureConfirm), + ), + validator: (v) => + (v != _passCtrl.text) ? 'Şifreler eşleşmiyor' : null, + ), + + if (_error != null) ...[ + const SizedBox(height: 12), + Container( + padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 10), + decoration: BoxDecoration( + color: AppColors.cancelledBg, + borderRadius: BorderRadius.circular(10), + border: Border.all( + color: AppColors.cancelled.withValues(alpha: 0.3)), + ), + child: Row( + children: [ + const Icon(Icons.error_outline_rounded, + color: AppColors.cancelled, size: 16), + const SizedBox(width: 8), + Expanded( + child: Text( + _error!, + style: const TextStyle( + color: AppColors.cancelled, fontSize: 13), + ), + ), + ], + ), + ), + ], + + const SizedBox(height: 20), + + FilledButton( + onPressed: _loading ? null : _submit, + style: FilledButton.styleFrom( + backgroundColor: AppColors.primary, + minimumSize: const Size.fromHeight(52), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12)), + ), + child: _loading + ? const SizedBox( + width: 22, + height: 22, + child: CircularProgressIndicator( + strokeWidth: 2.5, color: Colors.white), + ) + : const Text( + 'Kayıt Ol', + style: + TextStyle(fontSize: 15, fontWeight: FontWeight.w600), + ), + ), + ], + ), + ).animate(delay: 100.ms).fadeIn(duration: 400.ms).slideY(begin: 0.1, end: 0), + + const SizedBox(height: 20), + + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Text( + 'Zaten hesabın var mı?', + style: TextStyle(color: AppColors.textSecondary, fontSize: 14), + ), + TextButton( + onPressed: () => context.go(routeSignIn), + style: TextButton.styleFrom( + foregroundColor: AppColors.accent, + padding: const EdgeInsets.symmetric(horizontal: 8), + ), + child: const Text( + 'Giriş Yap', + style: TextStyle(fontWeight: FontWeight.w600, fontSize: 14), + ), + ), + ], + ).animate(delay: 200.ms).fadeIn(duration: 400.ms), + + SizedBox(height: isMobile ? 32 : 16), + ], + ), + ); + } +} + +// ── Feature bullet (desktop left panel) ────────────────────────────────────── + +class _FeatureBullet extends StatelessWidget { + const _FeatureBullet({required this.icon, required this.text}); + final IconData icon; + final String text; + + @override + Widget build(BuildContext context) { + return Row( + children: [ + Container( + width: 36, + height: 36, + decoration: BoxDecoration( + color: Colors.white.withValues(alpha: 0.15), + borderRadius: BorderRadius.circular(10), + ), + child: Icon(icon, size: 18, color: Colors.white), + ), + const SizedBox(width: 14), + Text( + text, + style: TextStyle( + fontSize: 15, + color: Colors.white.withValues(alpha: 0.9), + fontWeight: FontWeight.w500, + ), + ), + ], + ); + } +} + +// ── Form field ──────────────────────────────────────────────────────────────── + +class _Field extends StatelessWidget { + const _Field({ + required this.controller, + required this.label, + required this.icon, + this.keyboardType, + this.textCapitalization = TextCapitalization.none, + this.textInputAction, + this.obscureText = false, + this.suffixIcon, + this.onFieldSubmitted, + this.validator, + }); + + final TextEditingController controller; + final String label; + final IconData icon; + final TextInputType? keyboardType; + final TextCapitalization textCapitalization; + final TextInputAction? textInputAction; + final bool obscureText; + final Widget? suffixIcon; + final ValueChanged? onFieldSubmitted; + final FormFieldValidator? validator; + + @override + Widget build(BuildContext context) { + return TextFormField( + controller: controller, + keyboardType: keyboardType, + textCapitalization: textCapitalization, + textInputAction: textInputAction, + obscureText: obscureText, + onFieldSubmitted: onFieldSubmitted, + validator: validator, + style: const TextStyle(fontSize: 15, color: AppColors.textPrimary), + decoration: InputDecoration( + labelText: label, + prefixIcon: Icon(icon, size: 20, color: AppColors.textSecondary), + suffixIcon: suffixIcon, + filled: true, + fillColor: AppColors.background, + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: const BorderSide(color: AppColors.border), + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: const BorderSide(color: AppColors.border), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: const BorderSide(color: AppColors.accent, width: 2), + ), + errorBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: const BorderSide(color: AppColors.cancelled, width: 1.5), + ), + focusedErrorBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: const BorderSide(color: AppColors.cancelled, width: 2), + ), + contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14), + labelStyle: const TextStyle(color: AppColors.textSecondary, fontSize: 14), + ), + ); + } +} diff --git a/lib/features/clinic/connections/clinic_connections_repository.dart b/lib/features/clinic/connections/clinic_connections_repository.dart new file mode 100644 index 0000000..3dbd7e4 --- /dev/null +++ b/lib/features/clinic/connections/clinic_connections_repository.dart @@ -0,0 +1,40 @@ +import 'package:pocketbase/pocketbase.dart'; +import '../../../core/api/pocketbase_client.dart'; +import '../../../models/connection.dart'; + +class ClinicConnectionsRepository { + ClinicConnectionsRepository._(); + static final instance = ClinicConnectionsRepository._(); + + PocketBase get _pb => PocketBaseClient.instance.pb; + + Future> listConnections(String clinicTenantId) async { + final result = await _pb.collection('connections').getList( + filter: 'clinic_tenant_id = "$clinicTenantId"', + expand: 'lab_tenant_id,clinic_tenant_id', + perPage: 100, + ); + return (result.items.map((r) => Connection.fromJson(r.toJson())).toList() + ..sort((a, b) => (b.dateCreated ?? '').compareTo(a.dateCreated ?? ''))); + } + + Future requestConnection({ + required String clinicTenantId, + required String labTenantId, + }) async { + final record = await _pb.collection('connections').create(body: { + 'clinic_tenant_id': clinicTenantId, + 'lab_tenant_id': labTenantId, + 'status': 'pending', + }); + return Connection.fromJson(record.toJson()); + } + + Future>> searchLabs(String query) async { + final result = await _pb.collection('tenants').getList( + filter: 'kind = "lab" && company_name ~ "$query"', + perPage: 20, + ); + return result.items.map((r) => r.toJson()).toList(); + } +} diff --git a/lib/features/clinic/connections/clinic_connections_screen.dart b/lib/features/clinic/connections/clinic_connections_screen.dart new file mode 100644 index 0000000..f89acfa --- /dev/null +++ b/lib/features/clinic/connections/clinic_connections_screen.dart @@ -0,0 +1,441 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +import '../../../core/providers/auth_provider.dart'; +import '../../../core/theme/app_theme.dart'; +import '../../../models/connection.dart'; +import 'clinic_connections_repository.dart'; + +class ClinicConnectionsScreen extends ConsumerStatefulWidget { + const ClinicConnectionsScreen({super.key}); + + @override + ConsumerState createState() => + _ClinicConnectionsScreenState(); +} + +class _ClinicConnectionsScreenState + extends ConsumerState { + late Future> _future; + + @override + void initState() { + super.initState(); + _load(); + } + + void _load() { + final tenantId = ref.read(authProvider).activeTenant!.tenant.id; + setState(() { + _future = ClinicConnectionsRepository.instance + .listConnections(tenantId); + }); + } + + void _showSearchDialog() { + showDialog( + context: context, + builder: (ctx) => _LabSearchDialog( + onRequested: (labId, labName) async { + Navigator.of(ctx).pop(); + final tenantId = + ref.read(authProvider).activeTenant!.tenant.id; + try { + await ClinicConnectionsRepository.instance.requestConnection( + clinicTenantId: tenantId, + labTenantId: labId, + ); + _load(); + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text( + '$labName\'a bağlantı talebi gönderildi.')), + ); + } + } catch (e) { + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('Hata: $e')), + ); + } + } + }, + ), + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Bağlantılar'), + actions: [ + IconButton( + icon: const Icon(Icons.add_link), + tooltip: 'Laboratuvar Bul', + onPressed: _showSearchDialog, + ), + ], + ), + body: RefreshIndicator( + color: AppColors.accent, + onRefresh: () async => _load(), + child: FutureBuilder>( + future: _future, + builder: (ctx, snap) { + if (snap.connectionState == ConnectionState.waiting) { + return const Center( + child: CircularProgressIndicator(color: AppColors.accent)); + } + if (snap.hasError) { + return Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + width: 64, + height: 64, + decoration: BoxDecoration( + color: AppColors.cancelledBg, + borderRadius: BorderRadius.circular(16)), + child: const Icon(Icons.wifi_off_rounded, + color: AppColors.cancelled, size: 30), + ), + const SizedBox(height: 16), + Text('Hata: ${snap.error}', + style: + const TextStyle(color: AppColors.textSecondary)), + const SizedBox(height: 12), + FilledButton.icon( + onPressed: _load, + icon: const Icon(Icons.refresh_rounded, size: 18), + label: const Text('Tekrar Dene'), + ), + ], + ), + ); + } + final connections = snap.data!; + if (connections.isEmpty) { + return Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + width: 72, + height: 72, + decoration: BoxDecoration( + color: AppColors.inProgressBg, + borderRadius: BorderRadius.circular(20)), + child: const Icon(Icons.link_off, + color: AppColors.inProgress, size: 32), + ), + const SizedBox(height: 16), + const Text( + 'Henüz bağlantı yok', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + color: AppColors.textPrimary), + ), + const SizedBox(height: 8), + FilledButton.icon( + onPressed: _showSearchDialog, + icon: const Icon(Icons.search), + label: const Text('Laboratuvar Bul'), + ), + ], + ), + ); + } + return ListView.builder( + padding: const EdgeInsets.fromLTRB(16, 12, 16, 24), + itemCount: connections.length, + itemBuilder: (context, index) { + final conn = connections[index]; + final statusColor = _statusColor(conn.status); + final statusBg = _statusBg(conn.status); + return Padding( + padding: const EdgeInsets.only(bottom: 10), + child: Container( + padding: const EdgeInsets.all(14), + decoration: BoxDecoration( + color: AppColors.surface, + borderRadius: BorderRadius.circular(14), + border: Border.all(color: AppColors.border), + boxShadow: [ + BoxShadow( + color: Colors.black.withValues(alpha: 0.04), + blurRadius: 8, + offset: const Offset(0, 2)) + ]), + child: Row( + children: [ + Container( + width: 44, + height: 44, + decoration: BoxDecoration( + color: statusBg, + borderRadius: BorderRadius.circular(12)), + child: Icon(Icons.science_outlined, + color: statusColor, size: 22), + ), + const SizedBox(width: 12), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + conn.labName ?? 'Laboratuvar', + style: const TextStyle( + fontSize: 15, + fontWeight: FontWeight.w600, + color: AppColors.textPrimary), + ), + if (conn.dateCreated != null) ...[ + const SizedBox(height: 2), + Text( + _formatDate(conn.dateCreated!), + style: const TextStyle( + fontSize: 12, + color: AppColors.textSecondary), + ), + ], + ], + ), + ), + _StatusChip(status: conn.status), + ], + ), + ), + ); + }, + ); + }, + ), + ), + floatingActionButton: FloatingActionButton.extended( + onPressed: _showSearchDialog, + backgroundColor: AppColors.accent, + foregroundColor: Colors.white, + icon: const Icon(Icons.search), + label: const Text('Laboratuvar Bul'), + ), + ); + } + + Color _statusColor(ConnectionStatus s) { + switch (s) { + case ConnectionStatus.pending: + return AppColors.pending; + case ConnectionStatus.approved: + return AppColors.success; + case ConnectionStatus.rejected: + return AppColors.cancelled; + } + } + + Color _statusBg(ConnectionStatus s) { + switch (s) { + case ConnectionStatus.pending: + return AppColors.pendingBg; + case ConnectionStatus.approved: + return AppColors.successBg; + case ConnectionStatus.rejected: + return AppColors.cancelledBg; + } + } + + String _formatDate(String dateStr) { + try { + final d = DateTime.parse(dateStr); + return '${d.day.toString().padLeft(2, '0')}.${d.month.toString().padLeft(2, '0')}.${d.year}'; + } catch (_) { + return dateStr; + } + } +} + +class _StatusChip extends StatelessWidget { + const _StatusChip({required this.status}); + final ConnectionStatus status; + + @override + Widget build(BuildContext context) { + final color = _color(status); + final bg = _bg(status); + return Container( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4), + decoration: BoxDecoration( + color: bg, + borderRadius: BorderRadius.circular(8), + ), + child: Text( + status.label, + style: TextStyle( + color: color, + fontSize: 12, + fontWeight: FontWeight.w600, + ), + ), + ); + } + + Color _color(ConnectionStatus s) { + switch (s) { + case ConnectionStatus.pending: + return AppColors.pending; + case ConnectionStatus.approved: + return AppColors.success; + case ConnectionStatus.rejected: + return AppColors.cancelled; + } + } + + Color _bg(ConnectionStatus s) { + switch (s) { + case ConnectionStatus.pending: + return AppColors.pendingBg; + case ConnectionStatus.approved: + return AppColors.successBg; + case ConnectionStatus.rejected: + return AppColors.cancelledBg; + } + } +} + +class _LabSearchDialog extends StatefulWidget { + const _LabSearchDialog({required this.onRequested}); + final void Function(String labId, String labName) onRequested; + + @override + State<_LabSearchDialog> createState() => _LabSearchDialogState(); +} + +class _LabSearchDialogState extends State<_LabSearchDialog> { + final _searchController = TextEditingController(); + List> _results = []; + bool _isLoading = false; + bool _searched = false; + + @override + void dispose() { + _searchController.dispose(); + super.dispose(); + } + + Future _search() async { + final query = _searchController.text.trim(); + if (query.isEmpty) return; + + setState(() { + _isLoading = true; + _searched = true; + }); + try { + final results = + await ClinicConnectionsRepository.instance.searchLabs(query); + setState(() { + _results = results; + _isLoading = false; + }); + } catch (e) { + setState(() => _isLoading = false); + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('Hata: $e')), + ); + } + } + } + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: const Text('Laboratuvar Bul'), + content: SizedBox( + width: double.maxFinite, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Row( + children: [ + Expanded( + child: TextField( + controller: _searchController, + decoration: const InputDecoration( + hintText: 'Lab adı ile arayın...', + contentPadding: EdgeInsets.symmetric( + horizontal: 12, vertical: 8), + ), + onSubmitted: (_) => _search(), + ), + ), + const SizedBox(width: 8), + FilledButton( + onPressed: _search, + child: const Text('Ara'), + ), + ], + ), + const SizedBox(height: 12), + if (_isLoading) + const Padding( + padding: EdgeInsets.all(16), + child: CircularProgressIndicator(color: AppColors.accent), + ) + else if (_searched && _results.isEmpty) + const Padding( + padding: EdgeInsets.all(16), + child: Text('Sonuç bulunamadı', + style: TextStyle(color: AppColors.textSecondary)), + ) + else if (_results.isNotEmpty) + ConstrainedBox( + constraints: const BoxConstraints(maxHeight: 240), + child: ListView.builder( + shrinkWrap: true, + itemCount: _results.length, + itemBuilder: (context, index) { + final lab = _results[index]; + final name = + lab['company_name'] as String? ?? 'Lab'; + return ListTile( + dense: true, + leading: Container( + width: 36, + height: 36, + decoration: BoxDecoration( + color: AppColors.inProgressBg, + borderRadius: BorderRadius.circular(8)), + child: const Icon(Icons.science_outlined, + color: AppColors.inProgress, size: 18), + ), + title: Text(name, + style: const TextStyle( + fontWeight: FontWeight.w600, + color: AppColors.textPrimary)), + subtitle: lab['member_number'] != null + ? Text('No: ${lab['member_number']}', + style: const TextStyle( + color: AppColors.textSecondary)) + : null, + onTap: () => + widget.onRequested(lab['id'] as String, name), + ); + }, + ), + ), + ], + ), + ), + actions: [ + TextButton( + onPressed: () => Navigator.of(context).pop(), + child: const Text('İptal'), + ), + ], + ); + } +} diff --git a/lib/features/clinic/dashboard/clinic_dashboard_screen.dart b/lib/features/clinic/dashboard/clinic_dashboard_screen.dart new file mode 100644 index 0000000..0562ab8 --- /dev/null +++ b/lib/features/clinic/dashboard/clinic_dashboard_screen.dart @@ -0,0 +1,1230 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_animate/flutter_animate.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:go_router/go_router.dart'; + +import '../../../core/providers/auth_provider.dart'; +import '../../../core/router/app_router.dart'; +import '../../../core/services/realtime_service.dart'; +import '../../../core/theme/app_theme.dart'; +import '../../../core/widgets/tooth_logo.dart'; +import '../../../models/job.dart'; +import '../jobs/clinic_jobs_repository.dart'; +import '../patients/clinic_patients_repository.dart'; + +class ClinicDashboardScreen extends ConsumerStatefulWidget { + const ClinicDashboardScreen({super.key}); + + @override + ConsumerState createState() => + _ClinicDashboardScreenState(); +} + +class _ClinicDashboardScreenState extends ConsumerState { + late Future<_DashboardData> _future; + late UnsubFn _unsub; + final Map _actingJobs = {}; + + @override + void initState() { + super.initState(); + _load(); + final tenantId = ref.read(authProvider).activeTenant!.tenant.id; + _unsub = RealtimeService.instance.watch( + 'jobs', + filter: "clinic_tenant_id='$tenantId'", + onEvent: (_) { if (mounted) _load(); }, + ); + } + + @override + void dispose() { + _unsub(); + super.dispose(); + } + + void _load() { + final tenantId = ref.read(authProvider).activeTenant!.tenant.id; + setState(() { + _future = _loadAll(tenantId); + }); + } + + Future _approveAtClinic(Job job) async { + final confirmed = await showDialog( + context: context, + builder: (ctx) => AlertDialog( + title: Text(job.patientCode), + content: Text('${job.prostheticType.label} işini onaylıyor musunuz?'), + actions: [ + TextButton(onPressed: () => Navigator.pop(ctx, false), child: const Text('İptal')), + FilledButton( + style: FilledButton.styleFrom(backgroundColor: AppColors.success), + onPressed: () => Navigator.pop(ctx, true), + child: const Text('Onayla'), + ), + ], + ), + ); + if (confirmed != true || !mounted) return; + setState(() => _actingJobs[job.id] = true); + try { + await ClinicJobsRepository.instance.approveAtClinic(job.id, job); + _load(); + } catch (e) { + if (mounted) ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('Hata: $e'))); + } finally { + if (mounted) setState(() => _actingJobs.remove(job.id)); + } + } + + Future _markDelivered(Job job) async { + final confirmed = await showDialog( + context: context, + builder: (ctx) => AlertDialog( + title: Text(job.patientCode), + content: Text('${job.prostheticType.label} işi teslim alındı olarak işaretlensin mi?'), + actions: [ + TextButton(onPressed: () => Navigator.pop(ctx, false), child: const Text('İptal')), + FilledButton( + onPressed: () => Navigator.pop(ctx, true), + child: const Text('Teslim Aldım'), + ), + ], + ), + ); + if (confirmed != true || !mounted) return; + setState(() => _actingJobs[job.id] = true); + try { + await ClinicJobsRepository.instance.markDelivered(job.id, job); + _load(); + } catch (e) { + if (mounted) ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('Hata: $e'))); + } finally { + if (mounted) setState(() => _actingJobs.remove(job.id)); + } + } + + Future<_DashboardData> _loadAll(String tenantId) async { + final now = DateTime.now(); + final thisMonthStart = DateTime(now.year, now.month, 1); + final lastMonthStart = DateTime(now.year, now.month - 1, 1); + + final results = await Future.wait([ + ClinicJobsRepository.instance.listOutbound(tenantId, statuses: ['pending'], limit: 200), + ClinicJobsRepository.instance.listOutbound(tenantId, statuses: ['in_progress'], limit: 200), + ClinicJobsRepository.instance.listOutbound(tenantId, statuses: ['sent'], limit: 200), + ClinicJobsRepository.instance.listOutbound(tenantId, limit: 5), + ClinicPatientsRepository.instance.listPatients(tenantId, limit: 200), + ]); + final thisMonth = await ClinicJobsRepository.instance.countDelivered(tenantId, from: thisMonthStart); + final lastMonth = await ClinicJobsRepository.instance.countDelivered(tenantId, from: lastMonthStart, to: thisMonthStart); + + final inProgressJobs = results[1] as List; + final sentJobs = results[2] as List; + final provaAtClinic = inProgressJobs.where((j) => j.location == JobLocation.atClinic).toList(); + final actionJobs = [...provaAtClinic, ...sentJobs]; + + return _DashboardData( + pendingCount: (results[0] as List).length, + inProgressCount: inProgressJobs.length, + sentCount: sentJobs.length, + patientCount: (results[4] as List).length, + recentJobs: results[3] as List, + thisMonthDelivered: thisMonth, + lastMonthDelivered: lastMonth, + actionJobs: actionJobs, + ); + } + + @override + Widget build(BuildContext context) { + final companyName = + ref.watch(authProvider).activeTenant?.tenant.companyName ?? ''; + + return Scaffold( + backgroundColor: AppColors.background, + body: LayoutBuilder( + builder: (context, constraints) { + const maxContent = 1040.0; + final hPad = constraints.maxWidth > maxContent + ? (constraints.maxWidth - maxContent) / 2 + : 16.0; + + return RefreshIndicator( + color: AppColors.accent, + onRefresh: () async => _load(), + child: FutureBuilder<_DashboardData>( + future: _future, + builder: (ctx, snap) { + if (snap.connectionState == ConnectionState.waiting) { + return _DashboardSkeleton(companyName: companyName, hPad: hPad); + } + if (snap.hasError) { + return _ErrorBody(onRetry: _load); + } + final data = snap.data!; + final isDesktop = MediaQuery.sizeOf(ctx).width > AppLayout.sidebarBreakpoint; + return CustomScrollView( + slivers: [ + _DashboardHeader(companyName: companyName), + if (isDesktop) + SliverPadding( + padding: const EdgeInsets.fromLTRB(16, 16, 16, 0), + sliver: SliverToBoxAdapter( + child: _StatsRow( + pending: data.pendingCount, + inProgress: data.inProgressCount, + sent: data.sentCount, + patients: data.patientCount, + ), + ), + ), + if (isDesktop) ...[ + SliverPadding( + padding: const EdgeInsets.fromLTRB(16, 12, 16, 0), + sliver: SliverToBoxAdapter( + child: _MonthlyReportSection(data: data) + .animate().fadeIn(duration: 300.ms).slideY(begin: 0.08, end: 0), + ), + ), + SliverPadding( + padding: const EdgeInsets.fromLTRB(16, 8, 16, 0), + sliver: SliverToBoxAdapter( + child: _GamificationRow(data: data) + .animate().fadeIn(duration: 300.ms, delay: 60.ms).slideY(begin: 0.08, end: 0), + ), + ), + ], + SliverPadding( + padding: EdgeInsets.fromLTRB(hPad, 16, hPad, 0), + sliver: SliverToBoxAdapter( + child: FilledButton.icon( + onPressed: () => context.push(routeClinicJobNew), + icon: const Icon(Icons.add_rounded, size: 20), + label: const Text('Yeni İş Oluştur'), + style: FilledButton.styleFrom( + minimumSize: const Size(double.infinity, 52), + backgroundColor: AppColors.accent, + ), + ).animate().fadeIn(duration: 300.ms).slideY(begin: 0.1, end: 0), + ), + ), + if (data.actionJobs.isNotEmpty) + SliverPadding( + padding: EdgeInsets.fromLTRB(hPad, 20, hPad, 0), + sliver: SliverToBoxAdapter( + child: _ActionSection( + jobs: data.actionJobs, + actingJobs: _actingJobs, + onApprove: _approveAtClinic, + onDeliver: _markDelivered, + ).animate().fadeIn(duration: 300.ms).slideY(begin: 0.06, end: 0), + ), + ), + SliverPadding( + padding: EdgeInsets.fromLTRB(hPad, 20, hPad, 4), + sliver: SliverToBoxAdapter( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('Son İşler', + style: Theme.of(context).textTheme.titleMedium), + TextButton( + onPressed: () => context.go(routeClinicJobs), + style: TextButton.styleFrom( + foregroundColor: AppColors.accent, + padding: const EdgeInsets.symmetric(horizontal: 8), + ), + child: const Text('Tümünü Gör'), + ), + ], + ), + ), + ), + if (data.recentJobs.isEmpty) + const SliverFillRemaining( + hasScrollBody: false, child: _EmptyJobs()) + else + SliverPadding( + padding: EdgeInsets.fromLTRB(hPad, 0, hPad, 24), + sliver: SliverList.separated( + itemCount: data.recentJobs.length, + separatorBuilder: (_, __) => const SizedBox(height: 10), + itemBuilder: (ctx, i) => + _JobCard(job: data.recentJobs[i]) + .animate(delay: (i * 60).ms) + .fadeIn(duration: 300.ms) + .slideY(begin: 0.12, end: 0), + ), + ), + ], + ); + }, + ), + ); + }, + ), + ); + } +} + +class _DashboardData { + const _DashboardData({ + required this.pendingCount, + required this.inProgressCount, + required this.sentCount, + required this.patientCount, + required this.recentJobs, + required this.thisMonthDelivered, + required this.lastMonthDelivered, + required this.actionJobs, + }); + final int pendingCount; + final int inProgressCount; + final int sentCount; + final int patientCount; + final List recentJobs; + final int thisMonthDelivered; + final int lastMonthDelivered; + final List actionJobs; + + int get points => thisMonthDelivered * 10; + double get changePercent => lastMonthDelivered == 0 + ? (thisMonthDelivered > 0 ? 100 : 0) + : (thisMonthDelivered - lastMonthDelivered) / lastMonthDelivered * 100; +} + +// ── Action Section ─────────────────────────────────────────────────────────── + +class _ActionSection extends StatelessWidget { + const _ActionSection({ + required this.jobs, + required this.actingJobs, + required this.onApprove, + required this.onDeliver, + }); + + final List jobs; + final Map actingJobs; + final Future Function(Job) onApprove; + final Future Function(Job) onDeliver; + + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Container( + width: 26, height: 26, + decoration: BoxDecoration(color: AppColors.pending, borderRadius: BorderRadius.circular(7)), + child: const Icon(Icons.priority_high_rounded, size: 15, color: Colors.white), + ), + const SizedBox(width: 8), + Text('Yapılacaklar', style: Theme.of(context).textTheme.titleMedium?.copyWith(fontWeight: FontWeight.w700)), + const SizedBox(width: 8), + Container( + padding: const EdgeInsets.symmetric(horizontal: 7, vertical: 2), + decoration: BoxDecoration(color: AppColors.pending, borderRadius: BorderRadius.circular(10)), + child: Text('${jobs.length}', style: const TextStyle(fontSize: 11, fontWeight: FontWeight.w800, color: Colors.white)), + ), + ], + ), + const SizedBox(height: 12), + ...jobs.asMap().entries.map((entry) => Padding( + padding: const EdgeInsets.only(bottom: 10), + child: _ActionJobCard( + job: entry.value, + acting: actingJobs[entry.value.id] == true, + onApprove: () => onApprove(entry.value), + onDeliver: () => onDeliver(entry.value), + ).animate(delay: (entry.key * 50).ms).fadeIn(duration: 250.ms).slideY(begin: 0.08, end: 0), + )), + ], + ); + } +} + +class _ActionJobCard extends StatelessWidget { + const _ActionJobCard({ + required this.job, + required this.acting, + required this.onApprove, + required this.onDeliver, + }); + + final Job job; + final bool acting; + final VoidCallback onApprove; + final VoidCallback onDeliver; + + bool get _isProva => job.status == JobStatus.inProgress && job.location == JobLocation.atClinic; + + @override + Widget build(BuildContext context) { + final isProva = _isProva; + final borderColor = isProva ? AppColors.pending : AppColors.accent; + final bgColor = isProva ? AppColors.pendingBg : AppColors.inProgressBg; + final iconColor = isProva ? AppColors.pending : AppColors.accent; + final icon = isProva ? Icons.rate_review_outlined : Icons.inventory_2_outlined; + final statusLabel = isProva ? 'Onay Bekliyor' : 'Teslimat Bekliyor'; + + return Semantics( + label: job.patientCode, + button: true, + excludeSemantics: true, + child: Material( + color: AppColors.surface, + borderRadius: BorderRadius.circular(14), + child: InkWell( + onTap: () => context.push('/clinic/jobs/${job.id}'), + borderRadius: BorderRadius.circular(14), + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(14), + border: Border.all(color: borderColor.withValues(alpha: 0.45), width: 1.5), + boxShadow: [BoxShadow(color: Colors.black.withValues(alpha: 0.05), blurRadius: 10, offset: const Offset(0, 3))], + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.fromLTRB(14, 12, 14, 10), + child: Row( + children: [ + Container( + width: 40, height: 40, + decoration: BoxDecoration(color: bgColor, borderRadius: BorderRadius.circular(11)), + child: Icon(icon, color: iconColor, size: 19), + ), + const SizedBox(width: 10), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(job.patientCode, style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w700, color: AppColors.textPrimary)), + const SizedBox(height: 2), + Text( + '${job.prostheticType.label} · ${job.labName ?? 'Lab'}', + style: const TextStyle(fontSize: 12, color: AppColors.textSecondary), + maxLines: 1, overflow: TextOverflow.ellipsis, + ), + ], + ), + ), + const SizedBox(width: 8), + Container( + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3), + decoration: BoxDecoration(color: bgColor, borderRadius: BorderRadius.circular(8)), + child: Text(statusLabel, style: TextStyle(fontSize: 10, fontWeight: FontWeight.w700, color: iconColor)), + ), + ], + ), + ), + Container( + decoration: BoxDecoration( + color: bgColor.withValues(alpha: 0.45), + borderRadius: const BorderRadius.only(bottomLeft: Radius.circular(13), bottomRight: Radius.circular(13)), + ), + padding: const EdgeInsets.fromLTRB(12, 8, 12, 10), + child: acting + ? const Center( + child: Padding( + padding: EdgeInsets.symmetric(vertical: 4), + child: SizedBox(width: 20, height: 20, child: CircularProgressIndicator(strokeWidth: 2.5, color: AppColors.accent)), + ), + ) + : isProva + ? Row(children: [ + Expanded( + child: FilledButton.icon( + onPressed: onApprove, + icon: const Icon(Icons.check_circle_outline, size: 15), + label: const Text('Onayla', style: TextStyle(fontSize: 13)), + style: FilledButton.styleFrom( + backgroundColor: AppColors.success, + minimumSize: const Size(0, 36), + tapTargetSize: MaterialTapTargetSize.shrinkWrap, + padding: const EdgeInsets.symmetric(horizontal: 12), + ), + ), + ), + const SizedBox(width: 8), + OutlinedButton.icon( + onPressed: () => context.push('/clinic/jobs/${job.id}'), + icon: const Icon(Icons.open_in_new_rounded, size: 14), + label: const Text('Detay', style: TextStyle(fontSize: 13)), + style: OutlinedButton.styleFrom( + minimumSize: const Size(0, 36), + tapTargetSize: MaterialTapTargetSize.shrinkWrap, + padding: const EdgeInsets.symmetric(horizontal: 12), + foregroundColor: AppColors.pending, + side: BorderSide(color: AppColors.pending.withValues(alpha: 0.6)), + ), + ), + ]) + : Row(children: [ + Expanded( + child: FilledButton.icon( + onPressed: onDeliver, + icon: const Icon(Icons.inventory_2_outlined, size: 15), + label: const Text('Teslim Aldım', style: TextStyle(fontSize: 13)), + style: FilledButton.styleFrom( + minimumSize: const Size(0, 36), + tapTargetSize: MaterialTapTargetSize.shrinkWrap, + padding: const EdgeInsets.symmetric(horizontal: 12), + ), + ), + ), + const SizedBox(width: 8), + OutlinedButton.icon( + onPressed: () => context.push('/clinic/jobs/${job.id}'), + icon: const Icon(Icons.open_in_new_rounded, size: 14), + label: const Text('Detay', style: TextStyle(fontSize: 13)), + style: OutlinedButton.styleFrom( + minimumSize: const Size(0, 36), + tapTargetSize: MaterialTapTargetSize.shrinkWrap, + padding: const EdgeInsets.symmetric(horizontal: 12), + foregroundColor: AppColors.accent, + side: BorderSide(color: AppColors.accent.withValues(alpha: 0.6)), + ), + ), + ]), + ), + ], + ), + ), + ), + ), + ); + } +} + +// ── Monthly Report ────────────────────────────────────────────────────────── + +class _MonthlyReportSection extends StatelessWidget { + const _MonthlyReportSection({required this.data}); + final _DashboardData data; + + @override + Widget build(BuildContext context) { + final pct = data.changePercent; + final isUp = pct >= 0; + final pctStr = '${isUp ? '+' : ''}${pct.toStringAsFixed(0)}%'; + return Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: AppColors.surface, + borderRadius: BorderRadius.circular(12), + border: Border.all(color: AppColors.border), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + const Icon(Icons.bar_chart_rounded, size: 18, color: AppColors.accent), + const SizedBox(width: 6), + Text('Aylık Rapor', style: Theme.of(context).textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w600)), + ], + ), + const SizedBox(height: 12), + Row( + children: [ + Expanded(child: _MonthStat(label: 'Bu Ay', value: data.thisMonthDelivered, highlighted: true)), + const SizedBox(width: 12), + Expanded(child: _MonthStat(label: 'Geçen Ay', value: data.lastMonthDelivered, highlighted: false)), + const SizedBox(width: 12), + Container( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6), + decoration: BoxDecoration( + color: isUp ? AppColors.successBg : AppColors.cancelledBg, + borderRadius: BorderRadius.circular(8), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon( + isUp ? Icons.trending_up_rounded : Icons.trending_down_rounded, + size: 16, + color: isUp ? AppColors.success : AppColors.cancelled, + ), + const SizedBox(width: 4), + Text( + pctStr, + style: TextStyle( + fontSize: 13, + fontWeight: FontWeight.w700, + color: isUp ? AppColors.success : AppColors.cancelled, + ), + ), + ], + ), + ), + ], + ), + ], + ), + ); + } +} + +class _MonthStat extends StatelessWidget { + const _MonthStat({required this.label, required this.value, required this.highlighted}); + final String label; + final int value; + final bool highlighted; + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10), + decoration: BoxDecoration( + color: highlighted ? AppColors.accent.withValues(alpha: 0.06) : AppColors.background, + borderRadius: BorderRadius.circular(8), + border: highlighted ? Border.all(color: AppColors.accent.withValues(alpha: 0.2)) : null, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(label, style: TextStyle(fontSize: 11, color: AppColors.textSecondary, fontWeight: FontWeight.w500)), + const SizedBox(height: 2), + Text( + '$value iş', + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.w700, + color: highlighted ? AppColors.accent : AppColors.textPrimary, + ), + ), + ], + ), + ); + } +} + +// ── Gamification Row ───────────────────────────────────────────────────────── + +const _monthlyGoal = 20; + +class _GamificationRow extends StatelessWidget { + const _GamificationRow({required this.data}); + final _DashboardData data; + + @override + Widget build(BuildContext context) { + final progress = (data.thisMonthDelivered / _monthlyGoal).clamp(0.0, 1.0); + final remaining = (_monthlyGoal - data.thisMonthDelivered).clamp(0, _monthlyGoal); + return Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: AppColors.surface, + borderRadius: BorderRadius.circular(12), + border: Border.all(color: AppColors.border), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + const Text('🏆', style: TextStyle(fontSize: 16)), + const SizedBox(width: 6), + Text('Aylık Hedef', style: Theme.of(context).textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w600)), + const Spacer(), + Container( + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3), + decoration: BoxDecoration( + color: AppColors.primary.withValues(alpha: 0.08), + borderRadius: BorderRadius.circular(6), + ), + child: Text( + '${data.points} puan', + style: const TextStyle(fontSize: 11, fontWeight: FontWeight.w700, color: AppColors.primary), + ), + ), + ], + ), + const SizedBox(height: 10), + ClipRRect( + borderRadius: BorderRadius.circular(6), + child: LinearProgressIndicator( + value: progress, + minHeight: 8, + backgroundColor: AppColors.background, + valueColor: AlwaysStoppedAnimation( + progress >= 1.0 ? AppColors.success : AppColors.accent, + ), + ), + ), + const SizedBox(height: 8), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + '${data.thisMonthDelivered} / $_monthlyGoal iş teslim edildi', + style: TextStyle(fontSize: 12, color: AppColors.textSecondary), + ), + Text( + progress >= 1.0 ? 'Hedef tamamlandı!' : '$remaining iş kaldı', + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w600, + color: progress >= 1.0 ? AppColors.success : AppColors.textSecondary, + ), + ), + ], + ), + ], + ), + ); + } +} + +// ── Header ────────────────────────────────────────────────────────────────── + +class _DashboardHeader extends StatelessWidget { + const _DashboardHeader({required this.companyName}); + final String companyName; + + static const double _desktopToolbarHeight = 64; + + @override + Widget build(BuildContext context) { + final isDesktop = MediaQuery.sizeOf(context).width > AppLayout.sidebarBreakpoint; + + if (isDesktop) { + return SliverAppBar( + pinned: true, + toolbarHeight: _desktopToolbarHeight, + backgroundColor: AppColors.surface, + surfaceTintColor: Colors.transparent, + elevation: 0, + scrolledUnderElevation: 0, + centerTitle: false, + automaticallyImplyLeading: false, + titleSpacing: 0, + title: Padding( + padding: const EdgeInsets.symmetric(horizontal: 24), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Text('Genel Bakış', style: TextStyle(fontSize: 11, color: AppColors.textSecondary.withValues(alpha: 0.8), letterSpacing: 0.3)), + const Text('Bugünkü Durum', style: TextStyle(fontSize: 17, fontWeight: FontWeight.w700, color: AppColors.textPrimary)), + ], + ), + ), + actions: [ + IconButton( + onPressed: () => context.go(routeClinicSettings), + icon: const Icon(Icons.settings_outlined, color: AppColors.textSecondary, size: 22), + ), + const SizedBox(width: 8), + ], + ); + } + + return SliverAppBar( + pinned: true, + expandedHeight: 148, + backgroundColor: AppColors.primary, + surfaceTintColor: Colors.transparent, + shadowColor: Colors.transparent, + systemOverlayStyle: SystemUiOverlayStyle.light, + centerTitle: false, + leadingWidth: 60, + leading: Padding( + padding: const EdgeInsets.only(left: 16, top: 8, bottom: 8), + child: Container( + decoration: BoxDecoration( + color: Colors.white.withValues(alpha: 0.15), + borderRadius: BorderRadius.circular(10), + ), + child: const Center(child: ToothLogo(size: 20, color: Colors.white)), + ), + ), + titleSpacing: 8, + title: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Text('DLS', style: TextStyle(color: Colors.white.withValues(alpha: 0.65), fontSize: 11, fontWeight: FontWeight.w600, letterSpacing: 1.5)), + Text(companyName, style: const TextStyle(color: Colors.white, fontSize: 15, fontWeight: FontWeight.w700), maxLines: 1, overflow: TextOverflow.ellipsis), + ], + ), + actions: [ + IconButton( + onPressed: () => context.go(routeClinicSettings), + icon: const Icon(Icons.settings_outlined, color: Colors.white, size: 22), + ), + ], + flexibleSpace: FlexibleSpaceBar( + collapseMode: CollapseMode.pin, + background: Container( + decoration: const BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topLeft, + end: Alignment.bottomRight, + colors: [AppColors.primary, AppColors.accent], + ), + ), + child: Padding( + padding: const EdgeInsets.fromLTRB(20, 0, 20, 24), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Text('Genel Bakış', style: TextStyle(color: Colors.white.withValues(alpha: 0.65), fontSize: 12, fontWeight: FontWeight.w500, letterSpacing: 0.5)), + const SizedBox(height: 4), + const Text('Bugünkü Durum', style: TextStyle(color: Colors.white, fontSize: 24, fontWeight: FontWeight.w800, letterSpacing: -0.5)), + ], + ), + ), + ), + ), + ); + } +} + +// ── Stats ──────────────────────────────────────────────────────────────────── + +class _StatsRow extends StatelessWidget { + const _StatsRow({ + required this.pending, + required this.inProgress, + required this.sent, + required this.patients, + }); + final int pending; + final int inProgress; + final int sent; + final int patients; + + @override + Widget build(BuildContext context) { + final isWideDesktop = MediaQuery.sizeOf(context).width >= AppLayout.wideBreakpoint; + + final c1 = _StatCard(label: 'Bekleyen', value: '$pending', icon: Icons.hourglass_top_rounded, color: AppColors.pending, bgColor: AppColors.pendingBg) + .animate().fadeIn(duration: 350.ms).slideY(begin: 0.2, end: 0); + final c2 = _StatCard(label: 'Devam Eden', value: '$inProgress', icon: Icons.autorenew_rounded, color: AppColors.inProgress, bgColor: AppColors.inProgressBg) + .animate(delay: 80.ms).fadeIn(duration: 350.ms).slideY(begin: 0.2, end: 0); + final c3 = _StatCard(label: 'Toplam Hasta', value: '$patients', icon: Icons.people_outline_rounded, color: AppColors.success, bgColor: AppColors.successBg) + .animate(delay: 160.ms).fadeIn(duration: 350.ms).slideY(begin: 0.2, end: 0); + + // Wide desktop (≥ 1100px): 4 cards side by side — full lifecycle view. + if (isWideDesktop) { + final c4 = _StatCard(label: 'Klinik\'te', value: '$sent', icon: Icons.local_hospital_outlined, color: AppColors.accent, bgColor: AppColors.inProgressBg) + .animate(delay: 120.ms).fadeIn(duration: 350.ms).slideY(begin: 0.2, end: 0); + return Row( + children: [ + Expanded(child: c1), + const SizedBox(width: 12), + Expanded(child: c2), + const SizedBox(width: 12), + Expanded(child: c4), + const SizedBox(width: 12), + Expanded(child: c3), + ], + ); + } + + // Mobile + narrow sidebar (< 1100px): 2+1 column layout. + return Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Row( + children: [ + Expanded(child: c1), + const SizedBox(width: 12), + Expanded(child: c2), + ], + ), + const SizedBox(height: 12), + c3, + ], + ); + } +} + +class _StatCard extends StatelessWidget { + const _StatCard({ + required this.label, + required this.value, + required this.icon, + required this.color, + required this.bgColor, + }); + final String label; + final String value; + final IconData icon; + final Color color; + final Color bgColor; + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: AppColors.surface, + borderRadius: BorderRadius.circular(16), + border: Border.all(color: AppColors.border), + boxShadow: [ + BoxShadow( + color: Colors.black.withValues(alpha: 0.06), + blurRadius: 12, + offset: const Offset(0, 4)) + ], + ), + child: Row( + children: [ + Container( + width: 44, + height: 44, + decoration: + BoxDecoration(color: bgColor, borderRadius: BorderRadius.circular(12)), + child: Icon(icon, color: color, size: 22), + ), + const SizedBox(width: 12), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(value, + style: TextStyle( + fontSize: 28, + fontWeight: FontWeight.w800, + color: color, + height: 1)), + const SizedBox(height: 3), + Text(label, + style: const TextStyle( + fontSize: 12, + color: AppColors.textSecondary, + fontWeight: FontWeight.w500)), + ], + ), + ], + ), + ); + } +} + +// ── Job Card ───────────────────────────────────────────────────────────────── + +class _JobCard extends StatelessWidget { + const _JobCard({required this.job}); + final Job job; + + @override + Widget build(BuildContext context) { + final due = job.dueDate; + final isOverdue = due != null && due.isBefore(DateTime.now()); + final dueText = due != null + ? '${due.day.toString().padLeft(2, '0')}.${due.month.toString().padLeft(2, '0')}.${due.year}' + : null; + + final statusColor = _statusColor(job.status); + final statusBg = _statusBg(job.status); + + return Semantics( + label: job.patientCode, + button: true, + excludeSemantics: true, + child: Material( + color: AppColors.surface, + borderRadius: BorderRadius.circular(14), + child: InkWell( + onTap: () => context.push('/clinic/jobs/${job.id}'), + borderRadius: BorderRadius.circular(14), + child: Container( + padding: const EdgeInsets.all(14), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(14), + border: Border.all(color: AppColors.border)), + child: Row( + children: [ + Container( + width: 46, + height: 46, + decoration: BoxDecoration( + color: statusBg, borderRadius: BorderRadius.circular(12)), + child: Icon(Icons.work_outline_rounded, + color: statusColor, size: 22), + ), + const SizedBox(width: 12), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(job.patientCode, + style: const TextStyle( + fontSize: 15, + fontWeight: FontWeight.w600, + color: AppColors.textPrimary)), + const SizedBox(height: 2), + Text(job.labName ?? 'Laboratuvar', + style: const TextStyle( + fontSize: 13, color: AppColors.textSecondary)), + const SizedBox(height: 6), + Wrap( + spacing: 6, + children: [ + _Tag( + label: job.prostheticType.label, + color: AppColors.inProgress, + bg: AppColors.inProgressBg), + _Tag( + label: job.status.label, + color: statusColor, + bg: statusBg), + ], + ), + ], + ), + ), + if (dueText != null) ...[ + const SizedBox(width: 8), + Column( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Icon(Icons.calendar_today_outlined, + size: 13, + color: isOverdue + ? AppColors.cancelled + : AppColors.textMuted), + const SizedBox(height: 3), + Text( + dueText, + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w500, + color: isOverdue + ? AppColors.cancelled + : AppColors.textSecondary), + ), + ], + ), + ], + ], + ), + ), + ), + ), + ); + } + + Color _statusColor(JobStatus s) { + switch (s) { + case JobStatus.pending: + return AppColors.pending; + case JobStatus.inProgress: + return AppColors.inProgress; + case JobStatus.sent: + return AppColors.accent; + case JobStatus.delivered: + return AppColors.success; + case JobStatus.cancelled: + return AppColors.cancelled; + } + } + + Color _statusBg(JobStatus s) { + switch (s) { + case JobStatus.pending: + return AppColors.pendingBg; + case JobStatus.inProgress: + return AppColors.inProgressBg; + case JobStatus.sent: + return AppColors.inProgressBg; + case JobStatus.delivered: + return AppColors.successBg; + case JobStatus.cancelled: + return AppColors.cancelledBg; + } + } +} + +class _Tag extends StatelessWidget { + const _Tag({required this.label, required this.color, required this.bg}); + final String label; + final Color color; + final Color bg; + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3), + decoration: + BoxDecoration(color: bg, borderRadius: BorderRadius.circular(6)), + child: Text(label, + style: TextStyle( + fontSize: 11, fontWeight: FontWeight.w600, color: color)), + ); + } +} + +// ── Empty / Error / Skeleton ───────────────────────────────────────────────── + +class _EmptyJobs extends StatelessWidget { + const _EmptyJobs(); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.all(40), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Container( + width: 72, + height: 72, + decoration: BoxDecoration( + color: AppColors.inProgressBg, + borderRadius: BorderRadius.circular(20)), + child: const Icon(Icons.work_off_outlined, + color: AppColors.inProgress, size: 32), + ), + const SizedBox(height: 16), + const Text('Henüz iş yok', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + color: AppColors.textPrimary)), + const SizedBox(height: 6), + const Text( + 'Yeni iş oluşturduğunuzda\nburada görünecek', + textAlign: TextAlign.center, + style: + TextStyle(fontSize: 13, color: AppColors.textSecondary), + ), + ], + ), + ); + } +} + +class _ErrorBody extends StatelessWidget { + const _ErrorBody({required this.onRetry}); + final VoidCallback onRetry; + + @override + Widget build(BuildContext context) { + return Center( + child: Padding( + padding: const EdgeInsets.all(32), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + width: 64, + height: 64, + decoration: BoxDecoration( + color: AppColors.cancelledBg, + borderRadius: BorderRadius.circular(16)), + child: const Icon(Icons.wifi_off_rounded, + color: AppColors.cancelled, size: 30), + ), + const SizedBox(height: 16), + const Text('Bağlantı hatası', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + color: AppColors.textPrimary)), + const SizedBox(height: 12), + FilledButton.icon( + onPressed: onRetry, + icon: const Icon(Icons.refresh_rounded, size: 18), + label: const Text('Tekrar Dene')), + ], + ), + ), + ); + } +} + +class _DashboardSkeleton extends StatelessWidget { + const _DashboardSkeleton({required this.companyName, required this.hPad}); + final String companyName; + final double hPad; + + @override + Widget build(BuildContext context) { + return CustomScrollView( + physics: const NeverScrollableScrollPhysics(), + slivers: [ + _DashboardHeader(companyName: companyName), + SliverPadding( + padding: EdgeInsets.fromLTRB(hPad, 16, hPad, 0), + sliver: const SliverToBoxAdapter( + child: Column( + children: [ + Row(children: [ + Expanded(child: _ShimmerBox(height: 84, radius: 16)), + SizedBox(width: 12), + Expanded(child: _ShimmerBox(height: 84, radius: 16)), + ]), + SizedBox(height: 12), + _ShimmerBox(height: 84, radius: 16), + ], + ), + ), + ), + SliverPadding( + padding: EdgeInsets.fromLTRB(hPad, 8, hPad, 0), + sliver: SliverList.builder( + itemCount: 4, + itemBuilder: (_, i) => const Padding( + padding: EdgeInsets.only(bottom: 10), + child: _ShimmerBox(height: 92, radius: 14)), + ), + ), + ], + ); + } +} + +class _ShimmerBox extends StatefulWidget { + const _ShimmerBox({required this.height, required this.radius}); + final double height; + final double radius; + + @override + State<_ShimmerBox> createState() => _ShimmerBoxState(); +} + +class _ShimmerBoxState extends State<_ShimmerBox> + with SingleTickerProviderStateMixin { + late AnimationController _ctrl; + late Animation _anim; + + @override + void initState() { + super.initState(); + _ctrl = AnimationController( + vsync: this, duration: const Duration(milliseconds: 1100)) + ..repeat(reverse: true); + _anim = CurvedAnimation(parent: _ctrl, curve: Curves.easeInOut); + } + + @override + void dispose() { + _ctrl.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return AnimatedBuilder( + animation: _anim, + builder: (_, __) => Container( + height: widget.height, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(widget.radius), + color: Color.lerp(const Color(0xFFE2E8F0), + const Color(0xFFF1F5F9), _anim.value)), + ), + ); + } +} diff --git a/lib/features/clinic/finance/clinic_finance_repository.dart b/lib/features/clinic/finance/clinic_finance_repository.dart new file mode 100644 index 0000000..0843e49 --- /dev/null +++ b/lib/features/clinic/finance/clinic_finance_repository.dart @@ -0,0 +1,49 @@ +import 'package:pocketbase/pocketbase.dart'; +import '../../../core/api/pocketbase_client.dart'; +import '../../../models/finance_entry.dart'; + +class ClinicFinanceRepository { + ClinicFinanceRepository._(); + static final instance = ClinicFinanceRepository._(); + + PocketBase get _pb => PocketBaseClient.instance.pb; + + Future> listEntries( + String tenantId, { + String? status, + int page = 1, + int limit = 30, + }) async { + final filterParts = ['tenant_id = "$tenantId"', 'type = "payable"']; + if (status != null) filterParts.add('status = "$status"'); + + final result = await _pb.collection('finance_entries').getList( + page: page, + perPage: limit, + filter: filterParts.join(' && '), + expand: 'job_id', + ); + return (result.items.map((r) => FinanceEntry.fromJson(r.toJson())).toList() + ..sort((a, b) => (b.dateCreated ?? '').compareTo(a.dateCreated ?? ''))); + } + + Future> summary(String tenantId) async { + final all = await listEntries(tenantId, limit: 200); + double pending = 0, paid = 0; + for (final e in all) { + if (e.status == FinanceStatus.pending) { + pending += e.amount; + } else { + paid += e.amount; + } + } + return {'pending': pending, 'paid': paid}; + } + + Future markPaid(String entryId) async { + await _pb.collection('finance_entries').update(entryId, body: { + 'status': 'paid', + 'paid_at': DateTime.now().toIso8601String(), + }); + } +} diff --git a/lib/features/clinic/finance/clinic_finance_screen.dart b/lib/features/clinic/finance/clinic_finance_screen.dart new file mode 100644 index 0000000..fffd4b7 --- /dev/null +++ b/lib/features/clinic/finance/clinic_finance_screen.dart @@ -0,0 +1,534 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +import '../../../core/providers/auth_provider.dart'; +import '../../../core/providers/locale_provider.dart'; +import '../../../core/theme/app_theme.dart'; +import '../../../core/utils/currency_formatter.dart'; +import '../../../core/widgets/gradient_app_bar.dart'; +import '../../../core/widgets/pill_tabs.dart'; +import '../../../models/finance_entry.dart'; +import 'clinic_finance_repository.dart'; + +enum _FinanceSort { newestFirst, byAmountDesc, byAmountAsc } + +class ClinicFinanceScreen extends ConsumerStatefulWidget { + const ClinicFinanceScreen({super.key}); + + @override + ConsumerState createState() => + _ClinicFinanceScreenState(); +} + +class _ClinicFinanceScreenState extends ConsumerState + with SingleTickerProviderStateMixin { + late TabController _tabController; + late Future> _summaryFuture; + _FinanceSort _sort = _FinanceSort.newestFirst; + + @override + void initState() { + super.initState(); + _tabController = TabController(length: 2, vsync: this); + _tabController.addListener(() { + if (mounted) setState(() {}); + }); + _loadSummary(); + } + + @override + void dispose() { + _tabController.dispose(); + super.dispose(); + } + + void _loadSummary() { + final tenantId = ref.read(authProvider).activeTenant!.tenant.id; + setState(() { + _summaryFuture = + ClinicFinanceRepository.instance.summary(tenantId); + }); + } + + Future _showSortOptions() async { + final s = ref.read(stringsProvider); + final result = await showSortSheet( + context, + title: s.sort, + options: [s.sortNewest, s.sortAmountDesc, s.sortAmountAsc], + current: _sort.index, + ); + if (result != null) { + setState(() => _sort = _FinanceSort.values[result]); + } + } + + @override + Widget build(BuildContext context) { + final isSortActive = _sort != _FinanceSort.newestFirst; + final s = ref.watch(stringsProvider); + final currencyCode = + ref.watch(authProvider).activeTenant?.tenant.defaultCurrency ?? 'TRY'; + + return Scaffold( + backgroundColor: AppColors.background, + appBar: GradientAppBar( + title: s.finance, + category: s.clinicCategory, + actions: [ + IconButton( + onPressed: _showSortOptions, + tooltip: 'Sırala', + icon: Badge( + isLabelVisible: isSortActive, + smallSize: 8, + backgroundColor: AppColors.accent, + child: const Icon(Icons.sort_rounded), + ), + ), + ], + ), + body: Column( + children: [ + FutureBuilder>( + future: _summaryFuture, + builder: (ctx, snap) { + if (snap.connectionState == ConnectionState.waiting) { + return const LinearProgressIndicator( + color: AppColors.accent); + } + final summary = snap.data ?? {'pending': 0.0, 'paid': 0.0}; + return Padding( + padding: const EdgeInsets.all(16), + child: Row( + children: [ + Expanded( + child: _SummaryCard( + label: s.pendingReceivable, + amount: summary['pending'] ?? 0.0, + currencyCode: currencyCode, + color: AppColors.pending, + bgColor: AppColors.pendingBg, + icon: Icons.hourglass_empty_rounded, + ), + ), + const SizedBox(width: 12), + Expanded( + child: _SummaryCard( + label: s.collected, + amount: summary['paid'] ?? 0.0, + currencyCode: currencyCode, + color: AppColors.success, + bgColor: AppColors.successBg, + icon: Icons.check_circle_outline, + ), + ), + ], + ), + ); + }, + ), + PillTabs( + tabs: [s.pending, s.collected], + selected: _tabController.index, + onSelect: (i) => _tabController.animateTo(i), + ), + Expanded( + child: TabBarView( + controller: _tabController, + children: [ + _FinanceTab( + status: 'pending', + sort: _sort, + onPaymentMade: _loadSummary, + currencyCode: currencyCode, + ), + _FinanceTab( + status: 'paid', + sort: _sort, + onPaymentMade: _loadSummary, + currencyCode: currencyCode, + ), + ], + ), + ), + ], + ), + ); + } +} + +class _SummaryCard extends StatelessWidget { + const _SummaryCard({ + required this.label, + required this.amount, + required this.currencyCode, + required this.color, + required this.bgColor, + required this.icon, + }); + + final String label; + final double amount; + final String currencyCode; + final Color color; + final Color bgColor; + final IconData icon; + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: AppColors.surface, + borderRadius: BorderRadius.circular(16), + border: Border.all(color: AppColors.border), + boxShadow: [ + BoxShadow( + color: Colors.black.withValues(alpha: 0.06), + blurRadius: 12, + offset: const Offset(0, 4)) + ], + ), + child: Row( + children: [ + Container( + width: 44, + height: 44, + decoration: BoxDecoration( + color: bgColor, + borderRadius: BorderRadius.circular(12)), + child: Icon(icon, color: color, size: 22), + ), + const SizedBox(width: 12), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + CurrencyFormatter.format(amount, currencyCode), + style: TextStyle( + fontSize: 20, + fontWeight: FontWeight.w800, + color: color, + height: 1), + ), + const SizedBox(height: 3), + Text(label, + style: const TextStyle( + fontSize: 12, + color: AppColors.textSecondary, + fontWeight: FontWeight.w500)), + ], + ), + ), + ], + ), + ); + } +} + +class _FinanceTab extends ConsumerStatefulWidget { + const _FinanceTab({ + required this.status, + required this.sort, + required this.onPaymentMade, + required this.currencyCode, + }); + + final String status; + final _FinanceSort sort; + final VoidCallback onPaymentMade; + final String currencyCode; + + @override + ConsumerState<_FinanceTab> createState() => _FinanceTabState(); +} + +class _FinanceTabState extends ConsumerState<_FinanceTab> { + late Future> _future; + + @override + void initState() { + super.initState(); + _load(); + } + + void _load() { + final tenantId = ref.read(authProvider).activeTenant!.tenant.id; + setState(() { + _future = ClinicFinanceRepository.instance.listEntries( + tenantId, + status: widget.status, + limit: 100, + ); + }); + } + + List _sorted(List entries) { + final list = List.from(entries); + switch (widget.sort) { + case _FinanceSort.newestFirst: + list.sort((a, b) { + final da = a.dateCreated != null + ? DateTime.tryParse(a.dateCreated!) + : null; + final db = b.dateCreated != null + ? DateTime.tryParse(b.dateCreated!) + : null; + if (da == null && db == null) return 0; + if (da == null) return 1; + if (db == null) return -1; + return db.compareTo(da); + }); + case _FinanceSort.byAmountDesc: + list.sort((a, b) => b.amount.compareTo(a.amount)); + case _FinanceSort.byAmountAsc: + list.sort((a, b) => a.amount.compareTo(b.amount)); + } + return list; + } + + Future _markPaid(FinanceEntry entry) async { + final confirmed = await showDialog( + context: context, + builder: (ctx) => AlertDialog( + title: const Text('Ödeme Onayı'), + content: Text( + '${entry.counterpartyName ?? "Bu kayıt"} için ' + '${CurrencyFormatter.format(entry.amount, widget.currencyCode)} tutarındaki borcu ' + 'ödendi olarak işaretlemek istiyor musunuz?', + ), + actions: [ + TextButton( + onPressed: () => Navigator.pop(ctx, false), + child: const Text('İptal'), + ), + FilledButton( + onPressed: () => Navigator.pop(ctx, true), + child: const Text('Ödendi'), + ), + ], + ), + ); + if (confirmed != true || !mounted) return; + try { + await ClinicFinanceRepository.instance.markPaid(entry.id); + _load(); + widget.onPaymentMade(); + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('Ödeme kaydedildi.')), + ); + } + } catch (e) { + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('Hata: $e')), + ); + } + } + } + + @override + Widget build(BuildContext context) { + return RefreshIndicator( + color: AppColors.accent, + onRefresh: () async => _load(), + child: FutureBuilder>( + future: _future, + builder: (ctx, snap) { + if (snap.connectionState == ConnectionState.waiting) { + return const Center( + child: CircularProgressIndicator(color: AppColors.accent)); + } + if (snap.hasError) { + return Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + width: 64, + height: 64, + decoration: BoxDecoration( + color: AppColors.cancelledBg, + borderRadius: BorderRadius.circular(16)), + child: const Icon(Icons.wifi_off_rounded, + color: AppColors.cancelled, size: 30), + ), + const SizedBox(height: 16), + Text('Hata: ${snap.error}', + style: const TextStyle( + color: AppColors.textSecondary)), + const SizedBox(height: 12), + FilledButton.icon( + onPressed: _load, + icon: const Icon(Icons.refresh_rounded, size: 18), + label: const Text('Tekrar Dene'), + ), + ], + ), + ); + } + final entries = _sorted(snap.data!); + if (entries.isEmpty) { + return Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + width: 72, + height: 72, + decoration: BoxDecoration( + color: AppColors.inProgressBg, + borderRadius: BorderRadius.circular(20)), + child: const Icon(Icons.receipt_long_outlined, + color: AppColors.inProgress, size: 32), + ), + const SizedBox(height: 16), + const Text( + 'Kayıt bulunamadı', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + color: AppColors.textPrimary), + ), + ], + ), + ); + } + return ListView.builder( + padding: const EdgeInsets.fromLTRB(16, 12, 16, 24), + itemCount: entries.length, + itemBuilder: (context, index) { + final entry = entries[index]; + final isPending = entry.status == FinanceStatus.pending; + final statusColor = + isPending ? AppColors.pending : AppColors.success; + final statusBg = + isPending ? AppColors.pendingBg : AppColors.successBg; + + return Padding( + padding: const EdgeInsets.only(bottom: 10), + child: Material( + color: AppColors.surface, + borderRadius: BorderRadius.circular(14), + child: InkWell( + onTap: isPending ? () => _markPaid(entry) : null, + borderRadius: BorderRadius.circular(14), + child: Container( + padding: const EdgeInsets.all(14), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(14), + border: Border.all(color: AppColors.border), + boxShadow: [ + BoxShadow( + color: Colors.black.withValues(alpha: 0.04), + blurRadius: 8, + offset: const Offset(0, 2)) + ]), + child: Row( + children: [ + Container( + width: 44, + height: 44, + decoration: BoxDecoration( + color: statusBg, + borderRadius: BorderRadius.circular(12)), + child: Icon( + isPending + ? Icons.hourglass_empty_rounded + : Icons.check_circle_outline, + color: statusColor, + size: 22, + ), + ), + const SizedBox(width: 12), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Expanded( + child: Text( + entry.counterpartyName ?? + 'Bilinmiyor', + style: const TextStyle( + fontSize: 15, + fontWeight: FontWeight.w600, + color: AppColors.textPrimary), + ), + ), + Text( + CurrencyFormatter.format( + entry.amount, widget.currencyCode), + style: TextStyle( + fontSize: 15, + fontWeight: FontWeight.w700, + color: statusColor), + ), + ], + ), + if (entry.patientCode != null) ...[ + const SizedBox(height: 2), + Text( + 'Protokol: ${entry.patientCode}', + style: const TextStyle( + fontSize: 12, + color: AppColors.textSecondary), + ), + ], + if (entry.dateCreated != null) ...[ + const SizedBox(height: 2), + Text( + _formatDate(entry.dateCreated!), + style: const TextStyle( + fontSize: 12, + color: AppColors.textMuted), + ), + ], + ], + ), + ), + if (isPending) ...[ + const SizedBox(width: 8), + Container( + padding: const EdgeInsets.symmetric( + horizontal: 10, vertical: 5), + decoration: BoxDecoration( + color: AppColors.pending, + borderRadius: BorderRadius.circular(8), + ), + child: const Text( + 'Öde', + style: TextStyle( + color: Colors.white, + fontSize: 12, + fontWeight: FontWeight.w600, + ), + ), + ), + ], + ], + ), + ), + ), + ), + ); + }, + ); + }, + ), + ); + } + + String _formatDate(String dateStr) { + try { + final d = DateTime.parse(dateStr); + return '${d.day.toString().padLeft(2, '0')}.${d.month.toString().padLeft(2, '0')}.${d.year}'; + } catch (_) { + return dateStr; + } + } +} diff --git a/lib/features/clinic/jobs/clinic_job_detail_screen.dart b/lib/features/clinic/jobs/clinic_job_detail_screen.dart new file mode 100644 index 0000000..bc0a047 --- /dev/null +++ b/lib/features/clinic/jobs/clinic_job_detail_screen.dart @@ -0,0 +1,749 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +import '../../../core/providers/auth_provider.dart'; +import '../../../core/services/realtime_service.dart'; +import '../../../core/theme/app_theme.dart'; +import '../../../models/job.dart'; +import '../../../models/job_file.dart'; +import '../../../features/shared/job_files_repository.dart'; +import '../../../features/shared/job_files_panel.dart'; +import '../../../core/services/job_history_service.dart'; +import 'clinic_jobs_repository.dart'; + +class ClinicJobDetailScreen extends ConsumerStatefulWidget { + const ClinicJobDetailScreen({super.key, required this.jobId}); + final String jobId; + + @override + ConsumerState createState() => + _ClinicJobDetailScreenState(); +} + +class _ClinicJobDetailScreenState + extends ConsumerState { + Job? _job; + String? _loadError; + late Future> _filesFuture; + bool _isActing = false; + late UnsubFn _unsub; + + @override + void initState() { + super.initState(); + _load(); + _loadFiles(); + _unsub = RealtimeService.instance.watch( + 'jobs', + topic: widget.jobId, + onEvent: (_) { if (mounted && !_isActing) _load(); }, + ); + } + + @override + void dispose() { + _unsub(); + super.dispose(); + } + + Future _load() async { + if (mounted) setState(() { _loadError = null; }); + try { + final job = await ClinicJobsRepository.instance.getJob(widget.jobId); + if (mounted) setState(() { _job = job; }); + } catch (e) { + if (mounted) setState(() { _loadError = e.toString(); }); + } + } + + void _loadFiles() { + setState(() { + _filesFuture = JobFilesRepository.instance.listForJob(widget.jobId); + }); + } + + Future _approve(Job job) async { + setState(() => _isActing = true); + try { + final updated = await ClinicJobsRepository.instance.approveAtClinic(job.id, job); + if (mounted) { + setState(() { _job = updated.copyWith(clinicName: job.clinicName, labName: job.labName); _isActing = false; }); + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('İş onaylandı.')), + ); + } + } catch (e) { + if (mounted) { + setState(() => _isActing = false); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('Hata: $e')), + ); + } + } + } + + Future _cancelJob(Job job) async { + final confirmed = await showDialog( + context: context, + builder: (ctx) => AlertDialog( + title: const Text('İşi İptal Et'), + content: const Text('Bu iş geri alınamaz şekilde iptal edilir. Onaylıyor musunuz?'), + actions: [ + TextButton(onPressed: () => Navigator.pop(ctx, false), child: const Text('Vazgeç')), + FilledButton( + style: FilledButton.styleFrom(backgroundColor: AppColors.cancelled), + onPressed: () => Navigator.pop(ctx, true), + child: const Text('İptal Et'), + ), + ], + ), + ); + if (confirmed != true || !mounted) return; + setState(() => _isActing = true); + try { + final updated = await ClinicJobsRepository.instance.cancelJob(job.id, job); + if (mounted) { + setState(() { _job = _job!.copyWith(status: updated.status); _isActing = false; }); + ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('İş iptal edildi.'))); + } + } catch (e) { + if (mounted) { + setState(() => _isActing = false); + ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('Hata: $e'))); + } + } + } + + Future _requestRevision(Job job) async { + final noteController = TextEditingController(); + final confirmed = await showDialog( + context: context, + builder: (ctx) => AlertDialog( + title: const Text('Revizyon Talebi'), + content: TextField( + controller: noteController, + decoration: const InputDecoration( + labelText: 'Açıklama', + hintText: 'Revizyon sebebini belirtin...', + ), + minLines: 3, + maxLines: 5, + ), + actions: [ + TextButton( + onPressed: () => Navigator.pop(ctx, false), + child: const Text('İptal'), + ), + FilledButton( + onPressed: () => Navigator.pop(ctx, true), + child: const Text('Gönder'), + ), + ], + ), + ); + if (confirmed != true || !mounted) return; + if (noteController.text.trim().isEmpty) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('Lütfen bir açıklama girin.')), + ); + return; + } + + setState(() => _isActing = true); + try { + final updated = await ClinicJobsRepository.instance.requestRevision( + job.id, + job, + note: noteController.text.trim(), + ); + if (mounted) { + setState(() { _job = updated.copyWith(clinicName: job.clinicName, labName: job.labName); _isActing = false; }); + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('Revizyon talebi gönderildi.')), + ); + } + } catch (e) { + if (mounted) { + setState(() => _isActing = false); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('Hata: $e')), + ); + } + } + } + + Future _markDelivered(Job job) async { + final noteCtrl = TextEditingController(); + final confirmed = await showDialog( + context: context, + builder: (ctx) => AlertDialog( + title: const Text('Teslim Alındı'), + content: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text('Bu işi teslim aldığınızı onaylıyor musunuz?'), + const SizedBox(height: 12), + TextField( + controller: noteCtrl, + decoration: const InputDecoration( + labelText: 'Teslimat notu (isteğe bağlı)', + hintText: 'Teslim eden kişi, durum vb...', + isDense: true, + ), + maxLines: 2, + ), + ], + ), + actions: [ + TextButton( + onPressed: () => Navigator.pop(ctx, false), + child: const Text('İptal'), + ), + FilledButton( + style: FilledButton.styleFrom(backgroundColor: AppColors.success), + onPressed: () => Navigator.pop(ctx, true), + child: const Text('Teslim Alındı'), + ), + ], + ), + ); + if (confirmed != true || !mounted) return; + + setState(() => _isActing = true); + try { + final note = noteCtrl.text.trim().isNotEmpty ? noteCtrl.text.trim() : null; + final updated = await ClinicJobsRepository.instance.markDelivered(job.id, job, note: note); + if (mounted) { + setState(() { _job = updated.copyWith(clinicName: job.clinicName, labName: job.labName); _isActing = false; }); + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('İş teslim alındı olarak işaretlendi.')), + ); + } + } catch (e) { + if (mounted) { + setState(() => _isActing = false); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('Hata: $e')), + ); + } + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: AppColors.background, + appBar: AppBar(title: const Text('İş Detayı')), + body: _buildBody(), + ); + } + + Widget _buildBody() { + if (_job == null && _loadError == null) { + return const Center(child: CircularProgressIndicator(color: AppColors.accent)); + } + if (_loadError != null && _job == null) { + return Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + width: 64, + height: 64, + decoration: BoxDecoration( + color: AppColors.cancelledBg, + borderRadius: BorderRadius.circular(16)), + child: const Icon(Icons.wifi_off_rounded, + color: AppColors.cancelled, size: 30), + ), + const SizedBox(height: 16), + Text('Hata: $_loadError', + style: const TextStyle(color: AppColors.textSecondary)), + const SizedBox(height: 12), + FilledButton.icon( + onPressed: _load, + icon: const Icon(Icons.refresh_rounded, size: 18), + label: const Text('Tekrar Dene'), + ), + ], + ), + ); + } + if (_job == null) return const Center(child: CircularProgressIndicator(color: AppColors.accent)); + final job = _job!; + final membership = ref.read(authProvider).activeTenant; + final canDeliver = membership?.canDeliverJobs ?? true; + final canCancel = membership?.canCancelJobs ?? true; + final canManage = !(membership?.isDeliveryOnly ?? false); + return _JobDetailBody( + job: job, + filesFuture: _filesFuture, + isActing: _isActing, + canDeliver: canDeliver, + canManage: canManage, + onApprove: canManage ? () => _approve(job) : () {}, + onRevision: canManage ? () => _requestRevision(job) : () {}, + onDelivered: () => _markDelivered(job), + onCancel: (canCancel && job.status == JobStatus.pending) ? () => _cancelJob(job) : null, + onFilesRefresh: _loadFiles, + ); + } +} + +class _JobDetailBody extends StatelessWidget { + const _JobDetailBody({ + required this.job, + required this.filesFuture, + required this.isActing, + required this.canDeliver, + required this.canManage, + required this.onApprove, + required this.onRevision, + required this.onDelivered, + required this.onFilesRefresh, + this.onCancel, + }); + + final Job job; + final Future> filesFuture; + final bool isActing; + final bool canDeliver; + final bool canManage; + final VoidCallback onApprove; + final VoidCallback onRevision; + final VoidCallback onDelivered; + final VoidCallback? onCancel; + final VoidCallback onFilesRefresh; + + @override + Widget build(BuildContext context) { + final steps = job.stepTemplate; + final currentStepIndex = + job.currentStep != null ? steps.indexOf(job.currentStep!) : -1; + + final canApproveOrRevise = canManage && + job.location == JobLocation.atClinic && + job.status == JobStatus.inProgress; + final canMarkDelivered = canDeliver && job.status == JobStatus.sent; + + return ListView( + padding: const EdgeInsets.all(16), + children: [ + // Info card + Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: AppColors.surface, + borderRadius: BorderRadius.circular(16), + border: Border.all(color: AppColors.border), + boxShadow: [ + BoxShadow( + color: Colors.black.withValues(alpha: 0.05), + blurRadius: 12, + offset: const Offset(0, 4)) + ], + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Patient code + status + Row( + children: [ + Expanded( + child: Text( + job.patientCode, + style: Theme.of(context).textTheme.headlineSmall + ?.copyWith( + fontWeight: FontWeight.bold, + color: AppColors.textPrimary), + ), + ), + _StatusBadge(status: job.status), + ], + ), + const SizedBox(height: 16), + const Divider(height: 1, color: AppColors.border), + const SizedBox(height: 12), + + // Patient + Lab + _SectionLabel(title: 'Hasta & Laboratuvar'), + _InfoRow(label: 'Protokol No', value: job.patientCode), + if (job.patientId != null) + _InfoRow(label: 'Hasta ID', value: job.patientId!), + _InfoRow( + label: 'Laboratuvar', value: job.labName ?? 'Bilinmiyor'), + const SizedBox(height: 12), + + // Prosthetic + _SectionLabel(title: 'Protez Bilgisi'), + _InfoRow(label: 'Tür', value: job.prostheticType.label), + _InfoRow(label: 'Üye Sayısı', value: '${job.memberCount}'), + if (job.teeth.isNotEmpty) + _InfoRow(label: 'Dişler', value: job.teeth.join(', ')), + if (job.color != null && job.color!.isNotEmpty) + _InfoRow(label: 'Renk', value: job.color!), + if (job.description != null && job.description!.isNotEmpty) + _InfoRow(label: 'Açıklama', value: job.description!), + if (job.dueDate != null) + _InfoRow(label: 'Son Tarih', value: _formatDate(job.dueDate!, withTime: true)), + if (job.price != null) + _InfoRow( + label: 'Fiyat', + value: + '${job.price!.toStringAsFixed(2)} ${job.currency ?? 'TRY'}'), + ], + ), + ), + + const SizedBox(height: 16), + + // Stepper card + Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: AppColors.surface, + borderRadius: BorderRadius.circular(16), + border: Border.all(color: AppColors.border), + boxShadow: [ + BoxShadow( + color: Colors.black.withValues(alpha: 0.05), + blurRadius: 12, + offset: const Offset(0, 4)) + ], + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + 'İş Adımları', + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w600, + color: AppColors.textPrimary), + ), + const SizedBox(height: 16), + _StepperWidget( + steps: steps, + currentStepIndex: currentStepIndex, + historyFuture: JobHistoryService.instance.listForJob(job.id), + ), + ], + ), + ), + + const SizedBox(height: 24), + + // Action buttons + if (isActing) + const Center( + child: CircularProgressIndicator(color: AppColors.accent)) + else if (canApproveOrRevise) ...[ + FilledButton.icon( + onPressed: onApprove, + icon: const Icon(Icons.check_circle_outline), + label: const Text('Onayla'), + style: FilledButton.styleFrom( + minimumSize: const Size(double.infinity, 50), + backgroundColor: AppColors.success, + ), + ), + const SizedBox(height: 12), + OutlinedButton.icon( + onPressed: onRevision, + icon: const Icon(Icons.replay_outlined), + label: const Text('Revizyon İste'), + style: OutlinedButton.styleFrom( + minimumSize: const Size(double.infinity, 50), + foregroundColor: AppColors.pending, + side: const BorderSide(color: AppColors.pending), + ), + ), + ] else if (canMarkDelivered) + FilledButton.icon( + onPressed: onDelivered, + icon: const Icon(Icons.inventory_2_outlined), + label: const Text('Teslim Aldım'), + style: FilledButton.styleFrom( + minimumSize: const Size(double.infinity, 50), + ), + ), + + if (onCancel != null) ...[ + const SizedBox(height: 12), + OutlinedButton.icon( + onPressed: onCancel, + icon: const Icon(Icons.close_rounded), + label: const Text('İşi İptal Et'), + style: OutlinedButton.styleFrom( + minimumSize: const Size(double.infinity, 50), + foregroundColor: AppColors.cancelled, + side: const BorderSide(color: AppColors.cancelled), + ), + ), + ], + + const SizedBox(height: 20), + + // Files panel + JobFilesPanel( + job: job, + filesFuture: filesFuture, + onRefresh: onFilesRefresh, + ), + + const SizedBox(height: 12), + Text( + 'Oluşturulma: ${_formatDate(job.dateCreated)}', + style: const TextStyle(fontSize: 12, color: AppColors.textMuted), + textAlign: TextAlign.center, + ), + const SizedBox(height: 8), + ], + ); + } + + String _formatDate(DateTime d, {bool withTime = false}) { + final s = '${d.day.toString().padLeft(2, '0')}.${d.month.toString().padLeft(2, '0')}.${d.year}'; + if (!withTime || (d.hour == 0 && d.minute == 0)) return s; + return '$s ${d.hour.toString().padLeft(2, '0')}:${d.minute.toString().padLeft(2, '0')}'; + } +} + +class _StepperWidget extends StatelessWidget { + const _StepperWidget({ + required this.steps, + required this.currentStepIndex, + required this.historyFuture, + }); + + final List steps; + final int currentStepIndex; + final Future> historyFuture; + + @override + Widget build(BuildContext context) { + return FutureBuilder>( + future: historyFuture, + builder: (ctx, snap) { + final history = snap.data ?? []; + final Map revisionCounts = {}; + for (final e in history) { + if (e.action == JobHistoryAction.revisionRequested && e.step != null) { + revisionCounts[e.step!] = (revisionCounts[e.step!] ?? 0) + 1; + } + } + + return Column( + children: steps.asMap().entries.map((entry) { + final index = entry.key; + final step = entry.value; + final isCompleted = index < currentStepIndex; + final isCurrent = index == currentStepIndex; + final revCount = revisionCounts[step] ?? 0; + + Color dotColor; + IconData dotIcon; + if (isCompleted) { + dotColor = AppColors.success; + dotIcon = Icons.check_circle; + } else if (isCurrent) { + dotColor = AppColors.inProgress; + dotIcon = Icons.radio_button_checked; + } else { + dotColor = AppColors.muted; + dotIcon = Icons.radio_button_unchecked; + } + + return Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Column( + children: [ + Icon(dotIcon, color: dotColor, size: 24), + if (index < steps.length - 1) + Container( + width: 2, + height: 44, + color: index < currentStepIndex + ? AppColors.success.withValues(alpha: 0.35) + : AppColors.border, + ), + ], + ), + const SizedBox(width: 12), + Expanded( + child: Padding( + padding: const EdgeInsets.only(top: 2, bottom: 12), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Text( + step.label, + style: TextStyle( + fontWeight: isCurrent + ? FontWeight.bold + : FontWeight.normal, + color: isCompleted + ? AppColors.success + : isCurrent + ? AppColors.inProgress + : AppColors.textMuted, + fontSize: 15, + ), + ), + if (revCount > 0) ...[ + const SizedBox(width: 6), + Container( + padding: const EdgeInsets.symmetric( + horizontal: 6, vertical: 1), + decoration: BoxDecoration( + color: AppColors.cancelledBg, + borderRadius: BorderRadius.circular(6), + ), + child: Text( + '$revCount revizyon', + style: const TextStyle( + fontSize: 10, + fontWeight: FontWeight.w700, + color: AppColors.cancelled, + ), + ), + ), + ], + ], + ), + if (isCurrent) + Text( + step.description, + style: const TextStyle( + fontSize: 12, + color: AppColors.textSecondary, + ), + ), + ], + ), + ), + ), + ], + ); + }).toList(), + ); + }, + ); + } +} + +class _SectionLabel extends StatelessWidget { + const _SectionLabel({required this.title}); + final String title; + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(bottom: 6), + child: Text( + title, + style: const TextStyle( + fontSize: 11, + fontWeight: FontWeight.w600, + color: AppColors.accent, + letterSpacing: 0.5), + ), + ); + } +} + +class _InfoRow extends StatelessWidget { + const _InfoRow({required this.label, required this.value}); + final String label; + final String value; + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 4), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + width: 110, + child: Text( + label, + style: const TextStyle( + fontSize: 13, color: AppColors.textSecondary), + ), + ), + Expanded( + child: Text( + value, + style: const TextStyle( + fontSize: 14, + fontWeight: FontWeight.w500, + color: AppColors.textPrimary), + ), + ), + ], + ), + ); + } +} + +class _StatusBadge extends StatelessWidget { + const _StatusBadge({required this.status}); + final JobStatus status; + + @override + Widget build(BuildContext context) { + final color = _color(status); + final bg = _bg(status); + return Container( + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 5), + decoration: BoxDecoration( + color: bg, + borderRadius: BorderRadius.circular(10), + ), + child: Text( + status.label, + style: TextStyle( + color: color, + fontSize: 13, + fontWeight: FontWeight.w600, + ), + ), + ); + } + + Color _color(JobStatus s) { + switch (s) { + case JobStatus.pending: + return AppColors.pending; + case JobStatus.inProgress: + return AppColors.inProgress; + case JobStatus.sent: + return AppColors.accent; + case JobStatus.delivered: + return AppColors.success; + case JobStatus.cancelled: + return AppColors.cancelled; + } + } + + Color _bg(JobStatus s) { + switch (s) { + case JobStatus.pending: + return AppColors.pendingBg; + case JobStatus.inProgress: + return AppColors.inProgressBg; + case JobStatus.sent: + return AppColors.inProgressBg; + case JobStatus.delivered: + return AppColors.successBg; + case JobStatus.cancelled: + return AppColors.cancelledBg; + } + } +} + diff --git a/lib/features/clinic/jobs/clinic_jobs_repository.dart b/lib/features/clinic/jobs/clinic_jobs_repository.dart new file mode 100644 index 0000000..76e462f --- /dev/null +++ b/lib/features/clinic/jobs/clinic_jobs_repository.dart @@ -0,0 +1,177 @@ +import 'dart:async'; +import 'package:pocketbase/pocketbase.dart'; +import '../../../core/api/pocketbase_client.dart'; +import '../../../core/services/job_history_service.dart'; +import '../../../models/job.dart'; + +const _listExpand = 'clinic_tenant_id,lab_tenant_id'; +const _detailExpand = 'clinic_tenant_id,lab_tenant_id,patient_id,prosthetic_id'; + +class ClinicJobsRepository { + ClinicJobsRepository._(); + static final instance = ClinicJobsRepository._(); + + PocketBase get _pb => PocketBaseClient.instance.pb; + + Future> listOutbound( + String clinicTenantId, { + List? statuses, + String? location, + String? filterExtra, + int page = 1, + int limit = 30, + }) async { + final filterParts = ['clinic_tenant_id = "$clinicTenantId"']; + if (statuses != null && statuses.isNotEmpty) { + final statusFilter = statuses.map((s) => 'status = "$s"').join(' || '); + filterParts.add('($statusFilter)'); + } + if (location != null) { + filterParts.add('location = "$location"'); + } + if (filterExtra != null) { + filterParts.add('($filterExtra)'); + } + + final result = await _pb.collection('jobs').getList( + page: page, + perPage: limit, + filter: filterParts.join(' && '), + expand: _listExpand, + ); + return (result.items.map((r) => Job.fromJson(r.toJson())).toList() + ..sort((a, b) => b.dateCreated.compareTo(a.dateCreated))); + } + + Future getJob(String jobId) async { + final record = await _pb.collection('jobs').getOne(jobId, expand: _detailExpand); + return Job.fromJson(record.toJson()); + } + + Future createJob({ + required String clinicTenantId, + required String labTenantId, + required String patientCode, + required String prostheticId, + required ProstheticType prostheticType, + required List teeth, + String? patientId, + String? color, + String? description, + String? dueDate, + bool provaRequired = true, + }) async { + final record = await _pb.collection('jobs').create(body: { + 'clinic_tenant_id': clinicTenantId, + 'lab_tenant_id': labTenantId, + 'patient_code': patientCode, + if (patientId != null) 'patient_id': patientId, + 'prosthetic_id': prostheticId, + 'prosthetic_type': prostheticType.value, + 'member_count': teeth.length, + 'teeth': teeth, + if (color != null) 'color': color, + if (description != null) 'description': description, + if (dueDate != null) 'due_date': dueDate, + 'status': 'pending', + 'location': 'at_clinic', + 'prova_required': provaRequired, + }); + return Job.fromJson(record.toJson()); + } + + Future approveAtClinic(String jobId, Job job, {String? note}) async { + final nextStep = job.nextStep; + if (nextStep == null) throw Exception('Bu aşamadan ileri gidilemez.'); + + final record = await _pb.collection('jobs').update(jobId, body: { + 'current_step': nextStep.value, + 'location': 'at_lab', + }); + final updated = Job.fromJson(record.toJson()); + unawaited(JobHistoryService.instance.append( + jobId: jobId, + clinicTenantId: job.clinicTenantId, + labTenantId: job.labTenantId, + action: JobHistoryAction.approved, + step: job.currentStep, + note: note, + )); + return updated; + } + + Future requestRevision(String jobId, Job job, {required String note}) async { + final record = await _pb.collection('jobs').update(jobId, body: { + 'location': 'at_lab', + }); + final updated = Job.fromJson(record.toJson()); + unawaited(JobHistoryService.instance.append( + jobId: jobId, + clinicTenantId: job.clinicTenantId, + labTenantId: job.labTenantId, + action: JobHistoryAction.revisionRequested, + step: job.currentStep, + note: note, + )); + return updated; + } + + Future markDelivered(String jobId, Job job, {String? note}) async { + final record = await _pb.collection('jobs').update(jobId, body: { + 'status': 'delivered', + }); + unawaited(JobHistoryService.instance.append( + jobId: jobId, + clinicTenantId: job.clinicTenantId, + labTenantId: job.labTenantId, + action: JobHistoryAction.delivered, + note: note, + )); + return Job.fromJson(record.toJson()); + } + + Future cancelJob(String jobId, Job job) async { + final record = await _pb.collection('jobs').update(jobId, body: { + 'status': 'cancelled', + }); + unawaited(JobHistoryService.instance.append( + jobId: jobId, + clinicTenantId: job.clinicTenantId, + labTenantId: job.labTenantId, + action: JobHistoryAction.cancelled, + )); + return Job.fromJson(record.toJson()); + } + + Future>> listApprovedLabs(String clinicTenantId) async { + final result = await _pb.collection('connections').getList( + filter: 'clinic_tenant_id = "$clinicTenantId" && status = "approved"', + expand: 'lab_tenant_id', + perPage: 100, + ); + return result.items.map((r) { + final expand = r.toJson()['expand'] as Map?; + return expand?['lab_tenant_id'] as Map? ?? {'id': r.data['lab_tenant_id']}; + }).toList(); + } + + Future> listJobsByPatient(String patientId, {int limit = 50}) async { + final result = await _pb.collection('jobs').getList( + filter: 'patient_id = "$patientId"', + perPage: limit, + expand: _listExpand, + ); + return (result.items.map((r) => Job.fromJson(r.toJson())).toList() + ..sort((a, b) => b.dateCreated.compareTo(a.dateCreated))); + } + + Future countDelivered(String clinicTenantId, {DateTime? from, DateTime? to}) async { + final parts = ['clinic_tenant_id = "$clinicTenantId"', 'status = "delivered"']; + if (from != null) parts.add('updated >= "${_date(from)}"'); + if (to != null) parts.add('updated < "${_date(to)}"'); + final r = await _pb.collection('jobs').getList(perPage: 1, filter: parts.join(' && ')); + return r.totalItems; + } + + static String _date(DateTime d) => d.toIso8601String().split('T').first; +} diff --git a/lib/features/clinic/jobs/clinic_jobs_screen.dart b/lib/features/clinic/jobs/clinic_jobs_screen.dart new file mode 100644 index 0000000..884b55e --- /dev/null +++ b/lib/features/clinic/jobs/clinic_jobs_screen.dart @@ -0,0 +1,570 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:go_router/go_router.dart'; + +import '../../../core/providers/auth_provider.dart'; +import '../../../core/router/app_router.dart'; +import '../../../core/services/realtime_service.dart'; +import '../../../core/theme/app_theme.dart'; +import '../../../core/widgets/gradient_app_bar.dart'; +import '../../../core/widgets/pill_tabs.dart'; +import '../../../models/job.dart'; +import 'clinic_jobs_repository.dart'; + +enum _JobSort { newestFirst, oldestFirst, byDueDate, byType } + +const _kSortLabels = [ + 'Yeniden Eskiye', + 'Eskiden Yeniye', + 'Vade Tarihine Göre', + 'Türe Göre', +]; + +class ClinicJobsScreen extends ConsumerStatefulWidget { + const ClinicJobsScreen({super.key}); + + @override + ConsumerState createState() => _ClinicJobsScreenState(); +} + +class _ClinicJobsScreenState extends ConsumerState + with SingleTickerProviderStateMixin { + late TabController _tabController; + final _searchController = TextEditingController(); + String _searchQuery = ''; + _JobSort _sort = _JobSort.newestFirst; + + @override + void initState() { + super.initState(); + _tabController = TabController(length: 5, vsync: this); + _tabController.addListener(() { + if (mounted) setState(() {}); + }); + } + + @override + void dispose() { + _tabController.dispose(); + _searchController.dispose(); + super.dispose(); + } + + void _onSearchChanged(String value) { + setState(() => _searchQuery = value); + } + + Future _showSortOptions() async { + final result = await showSortSheet( + context, + title: 'Sıralama', + options: _kSortLabels, + current: _sort.index, + ); + if (result != null) { + setState(() => _sort = _JobSort.values[result]); + } + } + + @override + Widget build(BuildContext context) { + final isSortActive = _sort != _JobSort.newestFirst; + + return Scaffold( + backgroundColor: AppColors.background, + appBar: GradientAppBar( + title: 'İşlerim', + category: 'KLİNİK', + searchController: _searchController, + onSearchChanged: _onSearchChanged, + searchHint: 'Protokol, laboratuvar veya tür ara...', + actions: [ + IconButton( + onPressed: _showSortOptions, + tooltip: 'Sırala', + icon: Badge( + isLabelVisible: isSortActive, + smallSize: 8, + backgroundColor: AppColors.accent, + child: const Icon(Icons.sort_rounded), + ), + ), + if (ref.watch(authProvider).activeTenant?.canCreateJobs ?? true) + IconButton( + onPressed: () => context.push(routeClinicJobNew), + tooltip: 'Yeni İş', + icon: const Icon(Icons.add_rounded), + ), + ], + ), + body: Column( + children: [ + PillTabs( + tabs: const ['Tümü', 'Onay Bekleyen', 'Lab\'da', 'Teslimat', 'Teslim Alındı'], + selected: _tabController.index, + onSelect: (i) => _tabController.animateTo(i), + ), + Expanded( + child: TabBarView( + controller: _tabController, + children: [ + _JobsTab( + statuses: const ['pending', 'in_progress', 'sent', 'delivered'], + searchQuery: _searchQuery, + sort: _sort, + ), + _JobsTab( + statuses: const ['in_progress'], + location: 'at_clinic', + searchQuery: _searchQuery, + sort: _sort, + ), + _JobsTab( + filterExtra: 'status = "pending" || (status = "in_progress" && location = "at_lab")', + searchQuery: _searchQuery, + sort: _sort, + ), + _JobsTab( + statuses: const ['sent'], + searchQuery: _searchQuery, + sort: _sort, + ), + _JobsTab( + statuses: const ['delivered'], + searchQuery: _searchQuery, + sort: _sort, + ), + ], + ), + ), + ], + ), + ); + } +} + +class _JobsTab extends ConsumerStatefulWidget { + const _JobsTab({ + this.statuses, + this.location, + this.filterExtra, + required this.searchQuery, + required this.sort, + }); + + final List? statuses; + final String? location; + final String? filterExtra; + final String searchQuery; + final _JobSort sort; + + @override + ConsumerState<_JobsTab> createState() => _JobsTabState(); +} + +class _JobsTabState extends ConsumerState<_JobsTab> { + final List _jobs = []; + bool _isLoading = false; + bool _hasMore = true; + int _page = 1; + static const _limit = 20; + String? _error; + late UnsubFn _unsub; + + final _scrollController = ScrollController(); + + @override + void initState() { + super.initState(); + _load(); + _scrollController.addListener(_onScroll); + final tenantId = ref.read(authProvider).activeTenant!.tenant.id; + _unsub = RealtimeService.instance.watch( + 'jobs', + filter: 'clinic_tenant_id="$tenantId"', + onEvent: (_) { if (mounted) _load(); }, + ); + } + + @override + void dispose() { + _unsub(); + _scrollController.dispose(); + super.dispose(); + } + + void _onScroll() { + if (_scrollController.position.pixels >= + _scrollController.position.maxScrollExtent - 200 && + !_isLoading && + _hasMore) { + _loadMore(); + } + } + + Future _load() async { + if (_isLoading) return; + setState(() { + _isLoading = true; + _error = null; + _page = 1; + _jobs.clear(); + _hasMore = true; + }); + await _fetch(); + } + + Future _loadMore() async { + if (_isLoading || !_hasMore) return; + _page++; + await _fetch(); + } + + Future _fetch() async { + setState(() => _isLoading = true); + try { + final tenantId = ref.read(authProvider).activeTenant!.tenant.id; + final results = await ClinicJobsRepository.instance.listOutbound( + tenantId, + statuses: widget.statuses, + location: widget.location, + filterExtra: widget.filterExtra, + page: _page, + limit: _limit, + ); + setState(() { + _jobs.addAll(results); + _hasMore = results.length == _limit; + _isLoading = false; + }); + } catch (e) { + setState(() { + _error = e.toString(); + _isLoading = false; + }); + } + } + + List get _filtered { + var list = _jobs.toList(); + + final q = widget.searchQuery.toLowerCase().trim(); + if (q.isNotEmpty) { + list = list.where((j) { + return j.patientCode.toLowerCase().contains(q) || + (j.labName?.toLowerCase().contains(q) ?? false) || + j.prostheticType.label.toLowerCase().contains(q); + }).toList(); + } + + switch (widget.sort) { + case _JobSort.newestFirst: + list.sort((a, b) => b.dateCreated.compareTo(a.dateCreated)); + case _JobSort.oldestFirst: + list.sort((a, b) => a.dateCreated.compareTo(b.dateCreated)); + case _JobSort.byDueDate: + list.sort((a, b) { + if (a.dueDate == null && b.dueDate == null) return 0; + if (a.dueDate == null) return 1; + if (b.dueDate == null) return -1; + return a.dueDate!.compareTo(b.dueDate!); + }); + case _JobSort.byType: + list.sort( + (a, b) => a.prostheticType.label.compareTo(b.prostheticType.label)); + } + return list; + } + + @override + Widget build(BuildContext context) { + if (_error != null && _jobs.isEmpty) { + return Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + width: 64, + height: 64, + decoration: BoxDecoration( + color: AppColors.cancelledBg, + borderRadius: BorderRadius.circular(16)), + child: const Icon(Icons.wifi_off_rounded, + color: AppColors.cancelled, size: 30), + ), + const SizedBox(height: 16), + Text('Hata: $_error', + style: const TextStyle(color: AppColors.textSecondary)), + const SizedBox(height: 12), + FilledButton.icon( + onPressed: _load, + icon: const Icon(Icons.refresh_rounded, size: 18), + label: const Text('Tekrar Dene'), + ), + ], + ), + ); + } + + if (_isLoading && _jobs.isEmpty) { + return const Center( + child: CircularProgressIndicator(color: AppColors.accent)); + } + + final filtered = _filtered; + + if (filtered.isEmpty) { + return Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + width: 72, + height: 72, + decoration: BoxDecoration( + color: AppColors.inProgressBg, + borderRadius: BorderRadius.circular(20)), + child: const Icon(Icons.work_off_outlined, + color: AppColors.inProgress, size: 32), + ), + const SizedBox(height: 16), + Text( + widget.searchQuery.isNotEmpty + ? 'Sonuç bulunamadı' + : 'Henüz iş yok', + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + color: AppColors.textPrimary), + ), + ], + ), + ); + } + + return RefreshIndicator( + color: AppColors.accent, + onRefresh: _load, + child: ListView.builder( + controller: _scrollController, + padding: const EdgeInsets.fromLTRB(16, 12, 16, 24), + itemCount: + filtered.length + (_hasMore && widget.searchQuery.isEmpty ? 1 : 0), + itemBuilder: (context, index) { + if (index == filtered.length) { + return const Padding( + padding: EdgeInsets.all(16), + child: Center( + child: CircularProgressIndicator(color: AppColors.accent)), + ); + } + final job = filtered[index]; + return Padding( + padding: const EdgeInsets.only(bottom: 10), + child: _JobListCard( + job: job, + onTap: () => context.push('/clinic/jobs/${job.id}'), + ), + ); + }, + ), + ); + } +} + +class _JobListCard extends StatelessWidget { + const _JobListCard({required this.job, required this.onTap}); + + final Job job; + final VoidCallback onTap; + + @override + Widget build(BuildContext context) { + final statusColor = _statusColor(job); + final statusBg = _statusBg(job); + final isOverdue = + job.dueDate != null && job.dueDate!.isBefore(DateTime.now()); + + return Semantics( + label: job.patientCode, + button: true, + excludeSemantics: true, + child: Material( + color: AppColors.surface, + borderRadius: BorderRadius.circular(14), + child: InkWell( + onTap: onTap, + borderRadius: BorderRadius.circular(14), + child: Container( + padding: const EdgeInsets.all(14), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(14), + border: Border.all(color: AppColors.border), + boxShadow: [ + BoxShadow( + color: Colors.black.withValues(alpha: 0.04), + blurRadius: 8, + offset: const Offset(0, 2)) + ]), + child: Row( + children: [ + Container( + width: 44, + height: 44, + decoration: BoxDecoration( + color: statusBg, + borderRadius: BorderRadius.circular(12)), + child: Icon(Icons.medical_services_outlined, + color: statusColor, size: 20), + ), + const SizedBox(width: 12), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Expanded( + child: Text( + job.patientCode, + style: const TextStyle( + fontSize: 15, + fontWeight: FontWeight.w600, + color: AppColors.textPrimary), + ), + ), + _StatusBadge(status: job.status, location: job.location), + ], + ), + const SizedBox(height: 3), + Text(job.prostheticType.label, + style: const TextStyle( + fontSize: 12, color: AppColors.textSecondary)), + if (job.labName != null) ...[ + const SizedBox(height: 2), + Text( + job.labName!, + style: const TextStyle( + fontSize: 12, color: AppColors.textMuted), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ], + if (job.dueDate != null) ...[ + const SizedBox(height: 2), + Row( + children: [ + Icon(Icons.calendar_today_outlined, + size: 11, + color: isOverdue + ? AppColors.cancelled + : AppColors.textMuted), + const SizedBox(width: 3), + Text( + _fmt(job.dueDate!), + style: TextStyle( + fontSize: 12, + color: isOverdue + ? AppColors.cancelled + : AppColors.textMuted, + fontWeight: isOverdue + ? FontWeight.w600 + : FontWeight.normal, + ), + ), + ], + ), + ], + ], + ), + ), + const SizedBox(width: 8), + const Icon(Icons.chevron_right, + color: AppColors.textMuted, size: 20), + ], + ), + ), + ), + ), + ); + } + + String _fmt(DateTime d) => + '${d.day.toString().padLeft(2, '0')}.${d.month.toString().padLeft(2, '0')}.${d.year}'; + + Color _statusColor(Job job) { + if (job.status == JobStatus.inProgress && job.location == JobLocation.atClinic) return AppColors.pending; + switch (job.status) { + case JobStatus.pending: return AppColors.pending; + case JobStatus.inProgress: return AppColors.inProgress; + case JobStatus.sent: return AppColors.accent; + case JobStatus.delivered: return AppColors.success; + case JobStatus.cancelled: return AppColors.cancelled; + } + } + + Color _statusBg(Job job) { + if (job.status == JobStatus.inProgress && job.location == JobLocation.atClinic) return AppColors.pendingBg; + switch (job.status) { + case JobStatus.pending: return AppColors.pendingBg; + case JobStatus.inProgress: return AppColors.inProgressBg; + case JobStatus.sent: return AppColors.inProgressBg; + case JobStatus.delivered: return AppColors.successBg; + case JobStatus.cancelled: return AppColors.cancelledBg; + } + } +} + +class _StatusBadge extends StatelessWidget { + const _StatusBadge({required this.status, required this.location}); + + final JobStatus status; + final JobLocation location; + + String get _label { + if (status == JobStatus.inProgress && location == JobLocation.atClinic) return 'Onay Bekliyor'; + if (status == JobStatus.sent) return 'Teslimat Bekliyor'; + return status.label; + } + + Color get _color { + if (status == JobStatus.inProgress && location == JobLocation.atClinic) return AppColors.pending; + switch (status) { + case JobStatus.pending: return AppColors.pending; + case JobStatus.inProgress: return AppColors.inProgress; + case JobStatus.sent: return AppColors.accent; + case JobStatus.delivered: return AppColors.success; + case JobStatus.cancelled: return AppColors.cancelled; + } + } + + Color get _bg { + if (status == JobStatus.inProgress && location == JobLocation.atClinic) return AppColors.pendingBg; + switch (status) { + case JobStatus.pending: return AppColors.pendingBg; + case JobStatus.inProgress: return AppColors.inProgressBg; + case JobStatus.sent: return AppColors.inProgressBg; + case JobStatus.delivered: return AppColors.successBg; + case JobStatus.cancelled: return AppColors.cancelledBg; + } + } + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3), + decoration: BoxDecoration( + color: _bg, + borderRadius: BorderRadius.circular(8), + ), + child: Text( + _label, + style: TextStyle( + color: _color, + fontSize: 11, + fontWeight: FontWeight.w600, + ), + ), + ); + } +} diff --git a/lib/features/clinic/jobs/new_job_screen.dart b/lib/features/clinic/jobs/new_job_screen.dart new file mode 100644 index 0000000..95bed00 --- /dev/null +++ b/lib/features/clinic/jobs/new_job_screen.dart @@ -0,0 +1,1067 @@ +import 'dart:math'; +import 'package:file_picker/file_picker.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:go_router/go_router.dart'; +import 'package:http/http.dart' as http; + +import '../../../core/api/pocketbase_client.dart'; +import '../../../core/providers/auth_provider.dart'; +import '../../../core/theme/app_theme.dart'; +import '../../../models/clinic_discount.dart'; +import '../../../models/job.dart'; +import '../../../models/patient.dart'; +import '../../../models/prosthetic_product.dart'; +import '../../lab/discounts/discount_repository.dart'; +import '../../lab/products/lab_products_repository.dart'; +import 'clinic_jobs_repository.dart'; +import '../patients/clinic_patients_repository.dart'; + +String _mimeFromExt(String ext) => switch (ext) { + 'jpg' || 'jpeg' => 'image/jpeg', + 'png' => 'image/png', + 'webp' => 'image/webp', + 'pdf' => 'application/pdf', + 'stl' => 'model/stl', + 'obj' => 'model/obj', + 'ply' => 'model/ply', + 'zip' => 'application/zip', + 'mp3' => 'audio/mpeg', + 'mp4' => 'video/mp4', + 'opus' => 'audio/opus', + _ => 'application/octet-stream', + }; + +class NewJobScreen extends ConsumerStatefulWidget { + const NewJobScreen({super.key}); + + @override + ConsumerState createState() => _NewJobScreenState(); +} + +class _NewJobScreenState extends ConsumerState { + final _formKey = GlobalKey(); + + // Form fields + Map? _selectedLab; + Patient? _selectedPatient; + final _patientCodeController = TextEditingController(); + ProstheticType? _selectedProstheticType; + final Set _selectedTeeth = {}; + final _colorController = TextEditingController(); + final _descriptionController = TextEditingController(); + DateTime? _dueDate; + bool _provaRequired = true; + + // State + List> _labs = []; + bool _labsLoading = true; + bool _isSubmitting = false; + String? _labsError; + + // File upload + final List _pendingFiles = []; + + // Patient search + bool _showPatientSearch = false; + final _patientSearchController = TextEditingController(); + List _patientResults = []; + bool _patientSearchLoading = false; + + // Price preview + ProstheticProduct? _labProduct; + double? _effectivePrice; + bool _priceLoading = false; + + @override + void initState() { + super.initState(); + _loadLabs(); + } + + @override + void dispose() { + _patientCodeController.dispose(); + _colorController.dispose(); + _descriptionController.dispose(); + _patientSearchController.dispose(); + super.dispose(); + } + + Future _loadLabs() async { + setState(() { + _labsLoading = true; + _labsError = null; + }); + try { + final tenantId = + ref.read(authProvider).activeTenant!.tenant.id; + final labs = + await ClinicJobsRepository.instance.listApprovedLabs(tenantId); + setState(() { + _labs = labs; + _labsLoading = false; + }); + } catch (e) { + setState(() { + _labsError = e.toString(); + _labsLoading = false; + }); + } + } + + Future _fetchPrice() async { + if (_selectedLab == null || _selectedProstheticType == null) { + setState(() { _labProduct = null; _effectivePrice = null; }); + return; + } + final labId = _selectedLab!['id'] as String; + final ptValue = _selectedProstheticType!.value; + final clinicTenantId = ref.read(authProvider).activeTenant!.tenant.id; + + setState(() => _priceLoading = true); + try { + final products = await LabProductsRepository.instance.listProducts(labId, isActive: true); + ProstheticProduct? product; + try { + product = products.firstWhere((p) => p.prostheticType == ptValue); + } catch (_) { + product = null; + } + if (product == null || product.unitPrice == null) { + setState(() { _labProduct = null; _effectivePrice = null; _priceLoading = false; }); + return; + } + final discounts = await DiscountRepository.instance.listDiscounts(labId); + final applicable = discounts.where((d) => + d.isActive && + (d.appliesToAll || d.clinicTenantId == clinicTenantId) && + (d.appliesToAllTypes || d.prostheticType == ptValue) + ).toList(); + + double price = product.unitPrice!; + for (final d in applicable) { + price = d.discountType == DiscountType.percentage + ? price * (1 - d.discountValue / 100) + : price - d.discountValue; + } + setState(() { + _labProduct = product; + _effectivePrice = price.clamp(0, double.infinity); + _priceLoading = false; + }); + } catch (_) { + setState(() { _labProduct = null; _effectivePrice = null; _priceLoading = false; }); + } + } + + Future _searchPatients(String query) async { + if (query.trim().isEmpty) { + setState(() => _patientResults = []); + return; + } + setState(() => _patientSearchLoading = true); + try { + final tenantId = + ref.read(authProvider).activeTenant!.tenant.id; + final results = await ClinicPatientsRepository.instance + .listPatients(tenantId, search: query, limit: 10); + setState(() { + _patientResults = results; + _patientSearchLoading = false; + }); + } catch (_) { + setState(() => _patientSearchLoading = false); + } + } + + Future _pickDueDate() async { + final pickedDate = await showDatePicker( + context: context, + initialDate: DateTime.now().add(const Duration(days: 7)), + firstDate: DateTime.now(), + lastDate: DateTime.now().add(const Duration(days: 365)), + ); + if (pickedDate == null || !mounted) return; + final pickedTime = await showTimePicker( + context: context, + initialTime: const TimeOfDay(hour: 17, minute: 0), + ); + if (!mounted) return; + setState(() { + _dueDate = DateTime( + pickedDate.year, pickedDate.month, pickedDate.day, + pickedTime?.hour ?? 17, pickedTime?.minute ?? 0, + ); + }); + } + + String _generateProtocolNo() { + final now = DateTime.now(); + final date = + '${now.year}${now.month.toString().padLeft(2, '0')}${now.day.toString().padLeft(2, '0')}'; + const chars = '0123456789ABCDEFGHJKLMNPQRSTUVWXYZ'; + final rand = List.generate(4, (_) => chars[Random().nextInt(chars.length)]).join(); + return 'PR-$date-$rand'; + } + + Future _submit() async { + if (!_formKey.currentState!.validate()) return; + if (_selectedLab == null) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('Lütfen bir laboratuvar seçin.')), + ); + return; + } + if (_selectedProstheticType == null) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('Lütfen protez türünü seçin.')), + ); + return; + } + if (_selectedTeeth.isEmpty) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('En az bir diş seçmelisiniz.')), + ); + return; + } + + setState(() => _isSubmitting = true); + try { + final auth = ref.read(authProvider); + final tenantId = auth.activeTenant!.tenant.id; + final rawCode = _patientCodeController.text.trim(); + final protocolNo = rawCode.isNotEmpty ? rawCode : _generateProtocolNo(); + final job = await ClinicJobsRepository.instance.createJob( + clinicTenantId: tenantId, + labTenantId: _selectedLab!['id'] as String, + patientCode: protocolNo, + prostheticId: '', + prostheticType: _selectedProstheticType!, + teeth: _selectedTeeth.map((t) => t.toString()).toList()..sort(), + patientId: _selectedPatient?.id, + color: _colorController.text.trim().isNotEmpty + ? _colorController.text.trim() + : null, + description: _descriptionController.text.trim().isNotEmpty + ? _descriptionController.text.trim() + : null, + dueDate: _dueDate?.toIso8601String(), + provaRequired: _provaRequired, + ); + + // Upload pending files + if (_pendingFiles.isNotEmpty) { + final pb = PocketBaseClient.instance.pb; + final token = pb.authStore.token; + final uploaderId = (pb.authStore.record?.id) ?? (auth.profile?.id ?? ''); + for (final file in _pendingFiles) { + final bytes = file.bytes; + if (bytes == null) continue; + final ext = (file.extension ?? '').toLowerCase(); + final kind = (ext == 'stl' || ext == 'obj' || ext == 'ply') + ? 'scan' + : (ext == 'pdf') ? 'document' : 'image'; + final mimeType = _mimeFromExt(ext); + final req = http.MultipartRequest( + 'POST', + Uri.parse('https://pocket.kovaksoft.com/api/collections/job_files/records'), + ) + ..headers['Authorization'] = 'Bearer $token' + ..fields['job_id'] = job.id + ..fields['clinic_tenant_id'] = job.clinicTenantId + ..fields['lab_tenant_id'] = job.labTenantId + ..fields['uploaded_by'] = uploaderId + ..fields['kind'] = kind + ..fields['name'] = file.name + ..fields['size'] = bytes.length.toString() + ..fields['mime_type'] = mimeType + ..files.add(http.MultipartFile.fromBytes( + 'file', + bytes, + filename: file.name, + )); + final response = await req.send(); + if (response.statusCode < 200 || response.statusCode >= 300) { + final body = await response.stream.bytesToString(); + debugPrint('File upload failed: ${response.statusCode} $body'); + } + } + } + + if (mounted) { + context.go('/clinic/jobs/${job.id}'); + } + } catch (e) { + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('Hata: $e')), + ); + } + } finally { + if (mounted) setState(() => _isSubmitting = false); + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: const Text('Yeni İş')), + body: Form( + key: _formKey, + child: ListView( + padding: const EdgeInsets.all(16), + children: [ + // Lab selection + _SectionLabel(label: 'Laboratuvar *'), + if (_labsLoading) + const Center(child: CircularProgressIndicator()) + else if (_labsError != null) + Row( + children: [ + Text('Hata: $_labsError', + style: const TextStyle(color: AppColors.cancelled)), + TextButton( + onPressed: _loadLabs, + child: const Text('Tekrar Dene'), + ), + ], + ) + else + DropdownButtonFormField>( + initialValue: _selectedLab, + decoration: const InputDecoration( + hintText: 'Laboratuvar seçin', + ), + items: _labs + .map( + (lab) => DropdownMenuItem( + value: lab, + child: Text(lab['company_name'] as String? ?? ''), + ), + ) + .toList(), + onChanged: (val) { + setState(() => _selectedLab = val); + _fetchPrice(); + }, + validator: (val) => + val == null ? 'Laboratuvar seçimi zorunludur' : null, + ), + const SizedBox(height: 16), + + // Protocol number + _SectionLabel(label: 'Protokol No (İsteğe Bağlı)'), + Row( + children: [ + Expanded( + child: TextFormField( + controller: _patientCodeController, + decoration: InputDecoration( + hintText: 'Boş bırakılırsa otomatik üretilir', + suffixIcon: _selectedPatient != null + ? const Icon(Icons.person, + color: AppColors.success) + : null, + ), + ), + ), + const SizedBox(width: 8), + OutlinedButton.icon( + onPressed: () { + setState(() => _showPatientSearch = !_showPatientSearch); + }, + icon: const Icon(Icons.search), + label: const Text('Ara'), + ), + ], + ), + + // Patient search panel + if (_showPatientSearch) ...[ + const SizedBox(height: 8), + TextField( + controller: _patientSearchController, + decoration: const InputDecoration( + hintText: 'Ad, soyad veya kod ile arayın...', + prefixIcon: Icon(Icons.search), + ), + onChanged: _searchPatients, + ), + if (_patientSearchLoading) + const Padding( + padding: EdgeInsets.all(8), + child: Center(child: CircularProgressIndicator()), + ), + ..._patientResults.map( + (p) => ListTile( + dense: true, + leading: const Icon(Icons.person_outline), + title: Text(p.displayName), + subtitle: Text(p.patientCode), + onTap: () { + setState(() { + _selectedPatient = p; + _patientCodeController.text = p.patientCode; + _showPatientSearch = false; + _patientSearchController.clear(); + _patientResults.clear(); + }); + }, + ), + ), + ], + const SizedBox(height: 16), + + // Prosthetic type + _SectionLabel(label: 'Protez Türü *'), + DropdownButtonFormField( + initialValue: _selectedProstheticType, + decoration: const InputDecoration( + hintText: 'Protez türü seçin', + ), + items: ProstheticType.values + .map( + (pt) => DropdownMenuItem( + value: pt, + child: Text(pt.label), + ), + ) + .toList(), + onChanged: (val) { + setState(() => _selectedProstheticType = val); + _fetchPrice(); + }, + validator: (val) => + val == null ? 'Protez türü zorunludur' : null, + ), + // Price preview + if (_priceLoading) + const Padding( + padding: EdgeInsets.only(top: 8), + child: Row(children: [ + SizedBox(width: 14, height: 14, child: CircularProgressIndicator(strokeWidth: 1.5)), + SizedBox(width: 8), + Text('Fiyat yükleniyor...', style: TextStyle(fontSize: 12, color: AppColors.textMuted)), + ]), + ) + else if (_labProduct != null && _effectivePrice != null) ...[ + const SizedBox(height: 8), + _PricePreviewChip( + product: _labProduct!, + effectivePrice: _effectivePrice!, + ), + ], + const SizedBox(height: 12), + + // Prova flag + _ProvaToggle( + value: _provaRequired, + prostheticType: _selectedProstheticType, + onChanged: (v) => setState(() => _provaRequired = v), + ), + const SizedBox(height: 16), + + // Teeth selection + _SectionLabel( + label: 'Dişler * (${_selectedTeeth.length} seçili)', + ), + const SizedBox(height: 6), + // Bulk select row + _TeethBulkBar( + selectedTeeth: _selectedTeeth, + onSelectAll: () => setState(() { + _selectedTeeth.addAll([ + for (int i = 11; i <= 18; i++) i, + for (int i = 21; i <= 28; i++) i, + for (int i = 31; i <= 38; i++) i, + for (int i = 41; i <= 48; i++) i, + ]); + }), + onSelectUpper: () => setState(() { + final upper = {...[for (int i = 11; i <= 18; i++) i], ...[for (int i = 21; i <= 28; i++) i]}; + if (upper.every(_selectedTeeth.contains)) { + _selectedTeeth.removeAll(upper); + } else { + _selectedTeeth.addAll(upper); + } + }), + onSelectLower: () => setState(() { + final lower = {...[for (int i = 31; i <= 38; i++) i], ...[for (int i = 41; i <= 48; i++) i]}; + if (lower.every(_selectedTeeth.contains)) { + _selectedTeeth.removeAll(lower); + } else { + _selectedTeeth.addAll(lower); + } + }), + onClear: () => setState(() => _selectedTeeth.clear()), + ), + const SizedBox(height: 8), + _TeethGrid( + selectedTeeth: _selectedTeeth, + onToggle: (t) { + setState(() { + if (_selectedTeeth.contains(t)) { + _selectedTeeth.remove(t); + } else { + _selectedTeeth.add(t); + } + }); + }, + ), + const SizedBox(height: 16), + + // Color (optional) + _SectionLabel(label: 'Renk (İsteğe Bağlı)'), + TextFormField( + controller: _colorController, + decoration: const InputDecoration( + hintText: 'Ör: A2, B3', + ), + ), + const SizedBox(height: 16), + + // Description (optional) + _SectionLabel(label: 'Açıklama (İsteğe Bağlı)'), + TextFormField( + controller: _descriptionController, + decoration: const InputDecoration( + hintText: 'Ek notlar...', + ), + minLines: 2, + maxLines: 4, + ), + const SizedBox(height: 16), + + // Due date (optional) + _SectionLabel(label: 'Son Tarih (İsteğe Bağlı)'), + InkWell( + onTap: _pickDueDate, + child: InputDecorator( + decoration: const InputDecoration( + suffixIcon: Icon(Icons.calendar_today), + ), + child: Text( + _dueDate != null + ? '${_dueDate!.day.toString().padLeft(2, '0')}.${_dueDate!.month.toString().padLeft(2, '0')}.${_dueDate!.year} ${_dueDate!.hour.toString().padLeft(2, '0')}:${_dueDate!.minute.toString().padLeft(2, '0')}' + : 'Tarih ve saat seçin', + style: _dueDate != null + ? null + : TextStyle( + color: Theme.of(context).colorScheme.onSurfaceVariant, + ), + ), + ), + ), + const SizedBox(height: 16), + + // File attachments (optional) + _SectionLabel(label: 'Dosya Ekle (İsteğe Bağlı)'), + _FilePicker( + files: _pendingFiles, + onAdd: () async { + final result = await FilePicker.platform.pickFiles( + allowMultiple: true, + withData: true, + ); + if (result != null) { + setState(() => _pendingFiles.addAll(result.files)); + } + }, + onRemove: (i) => setState(() => _pendingFiles.removeAt(i)), + ), + const SizedBox(height: 24), + + // Submit button + if (_isSubmitting) + const Center(child: CircularProgressIndicator()) + else + FilledButton.icon( + onPressed: _submit, + icon: const Icon(Icons.check), + label: const Text('İş Oluştur'), + style: FilledButton.styleFrom( + minimumSize: const Size(double.infinity, 52), + ), + ), + const SizedBox(height: 24), + ], + ), + ), + ); + } +} + +class _TeethBulkBar extends StatelessWidget { + const _TeethBulkBar({ + required this.selectedTeeth, + required this.onSelectAll, + required this.onSelectUpper, + required this.onSelectLower, + required this.onClear, + }); + + final Set selectedTeeth; + final VoidCallback onSelectAll; + final VoidCallback onSelectUpper; + final VoidCallback onSelectLower; + final VoidCallback onClear; + + bool _allUpperSelected() { + final upper = [for (int i = 11; i <= 18; i++) i, for (int i = 21; i <= 28; i++) i]; + return upper.every(selectedTeeth.contains); + } + + bool _allLowerSelected() { + final lower = [for (int i = 31; i <= 38; i++) i, for (int i = 41; i <= 48; i++) i]; + return lower.every(selectedTeeth.contains); + } + + @override + Widget build(BuildContext context) { + final allSelected = _allUpperSelected() && _allLowerSelected(); + return SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Row( + children: [ + _BulkChip( + label: 'Tüm Dişler', + active: allSelected, + onTap: allSelected ? onClear : onSelectAll, + icon: Icons.select_all_rounded, + ), + const SizedBox(width: 6), + _BulkChip( + label: 'Üst Çene', + active: _allUpperSelected(), + onTap: onSelectUpper, + icon: Icons.arrow_upward_rounded, + ), + const SizedBox(width: 6), + _BulkChip( + label: 'Alt Çene', + active: _allLowerSelected(), + onTap: onSelectLower, + icon: Icons.arrow_downward_rounded, + ), + const SizedBox(width: 6), + if (selectedTeeth.isNotEmpty) + _BulkChip( + label: 'Temizle', + active: false, + onTap: onClear, + icon: Icons.clear_rounded, + destructive: true, + ), + ], + ), + ); + } +} + +class _BulkChip extends StatelessWidget { + const _BulkChip({ + required this.label, + required this.active, + required this.onTap, + required this.icon, + this.destructive = false, + }); + + final String label; + final bool active; + final VoidCallback onTap; + final IconData icon; + final bool destructive; + + @override + Widget build(BuildContext context) { + final color = destructive + ? AppColors.cancelled + : active + ? AppColors.accent + : AppColors.textSecondary; + final bg = destructive + ? AppColors.cancelledBg + : active + ? AppColors.inProgressBg + : AppColors.surfaceVariant; + + return GestureDetector( + onTap: onTap, + child: AnimatedContainer( + duration: const Duration(milliseconds: 120), + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6), + decoration: BoxDecoration( + color: bg, + borderRadius: BorderRadius.circular(20), + border: Border.all( + color: active && !destructive ? AppColors.accent : AppColors.border, + ), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon(icon, size: 14, color: color), + const SizedBox(width: 4), + Text( + label, + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w600, + color: color), + ), + ], + ), + ), + ); + } +} + +class _TeethGrid extends StatelessWidget { + const _TeethGrid({ + required this.selectedTeeth, + required this.onToggle, + }); + + final Set selectedTeeth; + final ValueChanged onToggle; + + @override + Widget build(BuildContext context) { + // Upper jaw: 18-11, 21-28 + // Lower jaw: 48-41, 31-38 + final upperRight = List.generate(8, (i) => 18 - i); // 18..11 + final upperLeft = List.generate(8, (i) => 21 + i); // 21..28 + final lowerRight = List.generate(8, (i) => 48 - i); // 48..41 + final lowerLeft = List.generate(8, (i) => 31 + i); // 31..38 + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Upper jaw label + Text('Üst Çene', + style: Theme.of(context).textTheme.bodySmall?.copyWith( + color: Theme.of(context).colorScheme.onSurfaceVariant, + )), + const SizedBox(height: 4), + Row( + children: [ + ...upperRight.map((t) => _ToothButton( + tooth: t, + selected: selectedTeeth.contains(t), + onTap: () => onToggle(t), + )), + const VerticalDivider(width: 8), + ...upperLeft.map((t) => _ToothButton( + tooth: t, + selected: selectedTeeth.contains(t), + onTap: () => onToggle(t), + )), + ], + ), + const SizedBox(height: 8), + // Lower jaw + Text('Alt Çene', + style: Theme.of(context).textTheme.bodySmall?.copyWith( + color: Theme.of(context).colorScheme.onSurfaceVariant, + )), + const SizedBox(height: 4), + Row( + children: [ + ...lowerRight.map((t) => _ToothButton( + tooth: t, + selected: selectedTeeth.contains(t), + onTap: () => onToggle(t), + )), + const VerticalDivider(width: 8), + ...lowerLeft.map((t) => _ToothButton( + tooth: t, + selected: selectedTeeth.contains(t), + onTap: () => onToggle(t), + )), + ], + ), + ], + ); + } +} + +class _ToothButton extends StatelessWidget { + const _ToothButton({ + required this.tooth, + required this.selected, + required this.onTap, + }); + + final int tooth; + final bool selected; + final VoidCallback onTap; + + @override + Widget build(BuildContext context) { + return Expanded( + child: GestureDetector( + onTap: onTap, + child: Container( + margin: const EdgeInsets.all(1.5), + height: 36, + decoration: BoxDecoration( + color: selected + ? Theme.of(context).colorScheme.primary + : Theme.of(context).colorScheme.surfaceContainerHighest, + borderRadius: BorderRadius.circular(4), + ), + child: Center( + child: Text( + '$tooth', + style: TextStyle( + fontSize: 9, + fontWeight: FontWeight.bold, + color: selected + ? Theme.of(context).colorScheme.onPrimary + : Theme.of(context).colorScheme.onSurfaceVariant, + ), + ), + ), + ), + ), + ); + } +} + +class _FilePicker extends StatelessWidget { + const _FilePicker({ + required this.files, + required this.onAdd, + required this.onRemove, + }); + + final List files; + final VoidCallback onAdd; + final void Function(int index) onRemove; + + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (files.isNotEmpty) ...[ + ...files.asMap().entries.map((e) { + final i = e.key; + final f = e.value; + return Container( + margin: const EdgeInsets.only(bottom: 6), + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), + decoration: BoxDecoration( + color: AppColors.surfaceVariant, + borderRadius: BorderRadius.circular(10), + border: Border.all(color: AppColors.border), + ), + child: Row( + children: [ + const Icon(Icons.attach_file, size: 16, color: AppColors.textSecondary), + const SizedBox(width: 8), + Expanded( + child: Text( + f.name, + style: const TextStyle( + fontSize: 13, color: AppColors.textPrimary), + overflow: TextOverflow.ellipsis, + ), + ), + Text( + _formatSize(f.size), + style: const TextStyle( + fontSize: 11, color: AppColors.textMuted), + ), + const SizedBox(width: 4), + GestureDetector( + onTap: () => onRemove(i), + child: const Icon(Icons.close, size: 16, color: AppColors.textSecondary), + ), + ], + ), + ); + }), + const SizedBox(height: 4), + ], + OutlinedButton.icon( + onPressed: onAdd, + icon: const Icon(Icons.upload_file_outlined, size: 18), + label: const Text('Dosya Seç'), + style: OutlinedButton.styleFrom( + foregroundColor: AppColors.accent, + side: const BorderSide(color: AppColors.accent), + ), + ), + ], + ); + } + + String _formatSize(int bytes) { + if (bytes < 1024) return '$bytes B'; + if (bytes < 1024 * 1024) return '${(bytes / 1024).toStringAsFixed(1)} KB'; + return '${(bytes / (1024 * 1024)).toStringAsFixed(1)} MB'; + } +} + +class _PricePreviewChip extends StatelessWidget { + const _PricePreviewChip({required this.product, required this.effectivePrice}); + + final ProstheticProduct product; + final double effectivePrice; + + @override + Widget build(BuildContext context) { + final currency = product.currency ?? 'TRY'; + final unitPrice = product.unitPrice!; + final hasDiscount = (effectivePrice - unitPrice).abs() > 0.01; + + return Container( + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), + decoration: BoxDecoration( + color: AppColors.successBg, + borderRadius: BorderRadius.circular(10), + border: Border.all(color: AppColors.success.withValues(alpha: 0.25)), + ), + child: Row( + children: [ + const Icon(Icons.sell_outlined, size: 16, color: AppColors.success), + const SizedBox(width: 8), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + '${product.name} — ${effectivePrice.toStringAsFixed(2)} $currency', + style: const TextStyle(fontSize: 13, fontWeight: FontWeight.w700, color: AppColors.success), + ), + if (hasDiscount) + Text( + 'Liste: ${unitPrice.toStringAsFixed(2)} $currency · İndirim uygulandı', + style: TextStyle(fontSize: 11, color: AppColors.success.withValues(alpha: 0.75)), + ) + else + Text( + 'Liste fiyatı · İndirim yok', + style: TextStyle(fontSize: 11, color: AppColors.success.withValues(alpha: 0.75)), + ), + ], + ), + ), + ], + ), + ); + } +} + +class _SectionLabel extends StatelessWidget { + const _SectionLabel({required this.label}); + final String label; + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(bottom: 6), + child: Text( + label, + style: Theme.of(context).textTheme.bodyMedium?.copyWith( + fontWeight: FontWeight.w600, + ), + ), + ); + } +} + +class _ProvaToggle extends StatelessWidget { + const _ProvaToggle({ + required this.value, + required this.onChanged, + this.prostheticType, + }); + + final bool value; + final ValueChanged onChanged; + final ProstheticType? prostheticType; + + @override + Widget build(BuildContext context) { + final steps = prostheticType != null + ? jobStepTemplate(prostheticType!, value) + : []; + + return Container( + padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 10), + decoration: BoxDecoration( + color: value ? AppColors.inProgressBg : AppColors.surfaceVariant, + borderRadius: BorderRadius.circular(12), + border: Border.all( + color: value ? AppColors.inProgress.withValues(alpha: 0.3) : AppColors.border, + ), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Icon( + value ? Icons.swap_horiz_rounded : Icons.straighten_rounded, + size: 20, + color: value ? AppColors.inProgress : AppColors.textSecondary, + ), + const SizedBox(width: 10), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + value ? 'Provalı İş' : 'Provasız İş', + style: TextStyle( + fontWeight: FontWeight.w700, + color: value ? AppColors.inProgress : AppColors.textPrimary, + fontSize: 14, + ), + ), + Text( + value + ? 'Lab her adımda klinik onayı bekler' + : 'Lab doğrudan üretip teslime gönderir', + style: const TextStyle( + fontSize: 12, color: AppColors.textSecondary), + ), + ], + ), + ), + Switch( + value: value, + onChanged: onChanged, + activeColor: AppColors.inProgress, + ), + ], + ), + if (steps.isNotEmpty) ...[ + const SizedBox(height: 8), + Wrap( + spacing: 6, + children: steps.map((s) => Container( + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3), + decoration: BoxDecoration( + color: AppColors.surface, + borderRadius: BorderRadius.circular(6), + border: Border.all(color: AppColors.border), + ), + child: Text( + s.label, + style: const TextStyle( + fontSize: 11, color: AppColors.textSecondary), + ), + )).toList(), + ), + ], + ], + ), + ); + } +} diff --git a/lib/features/clinic/patients/clinic_patient_detail_screen.dart b/lib/features/clinic/patients/clinic_patient_detail_screen.dart new file mode 100644 index 0000000..c95160a --- /dev/null +++ b/lib/features/clinic/patients/clinic_patient_detail_screen.dart @@ -0,0 +1,717 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +import '../../../core/theme/app_theme.dart'; +import '../../../models/job.dart'; +import '../../../models/patient.dart'; +import '../jobs/clinic_jobs_repository.dart'; +import 'clinic_patients_repository.dart'; + +class ClinicPatientDetailScreen extends ConsumerStatefulWidget { + const ClinicPatientDetailScreen({super.key, required this.patientId}); + final String patientId; + + @override + ConsumerState createState() => + _ClinicPatientDetailScreenState(); +} + +class _ClinicPatientDetailScreenState + extends ConsumerState { + late Future _future; + late Future> _jobsFuture; + bool _editMode = false; + bool _isSaving = false; + + final _patientCodeController = TextEditingController(); + final _firstNameController = TextEditingController(); + final _lastNameController = TextEditingController(); + final _phoneController = TextEditingController(); + final _notesController = TextEditingController(); + String? _birthDate; + + final _formKey = GlobalKey(); + + @override + void initState() { + super.initState(); + _load(); + } + + @override + void dispose() { + _patientCodeController.dispose(); + _firstNameController.dispose(); + _lastNameController.dispose(); + _phoneController.dispose(); + _notesController.dispose(); + super.dispose(); + } + + void _load() { + setState(() { + _future = ClinicPatientsRepository.instance + .getPatient(widget.patientId) + .then((p) { + _populateControllers(p); + return p; + }); + _jobsFuture = ClinicJobsRepository.instance + .listJobsByPatient(widget.patientId); + }); + } + + void _populateControllers(Patient p) { + _patientCodeController.text = p.patientCode; + _firstNameController.text = p.firstName ?? ''; + _lastNameController.text = p.lastName ?? ''; + _phoneController.text = p.phone ?? ''; + _notesController.text = p.notes ?? ''; + _birthDate = p.birthDate; + } + + Future _pickBirthDate() async { + DateTime initial = DateTime(1990); + if (_birthDate != null) { + try { + initial = DateTime.parse(_birthDate!); + } catch (_) {} + } + final picked = await showDatePicker( + context: context, + initialDate: initial, + firstDate: DateTime(1900), + lastDate: DateTime.now(), + ); + if (picked != null) { + setState(() { + _birthDate = picked.toIso8601String().split('T').first; + }); + } + } + + Future _save() async { + if (!_formKey.currentState!.validate()) return; + setState(() => _isSaving = true); + try { + final patch = { + 'patient_code': _patientCodeController.text.trim(), + 'first_name': _firstNameController.text.trim().isNotEmpty + ? _firstNameController.text.trim() + : null, + 'last_name': _lastNameController.text.trim().isNotEmpty + ? _lastNameController.text.trim() + : null, + 'phone': _phoneController.text.trim().isNotEmpty + ? _phoneController.text.trim() + : null, + 'birth_date': _birthDate, + 'notes': _notesController.text.trim().isNotEmpty + ? _notesController.text.trim() + : null, + }; + final updated = await ClinicPatientsRepository.instance + .updatePatient(widget.patientId, patch); + _populateControllers(updated); + setState(() { + _editMode = false; + _future = Future.value(updated); + }); + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('Hasta bilgileri güncellendi.')), + ); + } + } catch (e) { + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('Hata: $e')), + ); + } + } finally { + if (mounted) setState(() => _isSaving = false); + } + } + + Future _delete() async { + // Check for existing jobs first + List? jobs; + try { + jobs = await ClinicJobsRepository.instance + .listJobsByPatient(widget.patientId, limit: 1); + } catch (_) { + jobs = null; + } + + if (!mounted) return; + final hasJobs = (jobs?.isNotEmpty) ?? false; + + final confirmed = await showDialog( + context: context, + builder: (_) => AlertDialog( + title: const Text('Hastayı Sil'), + content: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text('Bu hastayı silmek istediğinizden emin misiniz?'), + if (hasJobs) ...[ + const SizedBox(height: 12), + Container( + padding: const EdgeInsets.all(10), + decoration: BoxDecoration( + color: AppColors.cancelledBg, + borderRadius: BorderRadius.circular(8), + ), + child: const Row( + children: [ + Icon(Icons.warning_amber_rounded, + color: AppColors.cancelled, size: 18), + SizedBox(width: 8), + Expanded( + child: Text( + 'Bu hastaya ait işler bulunmaktadır. Hasta silinirse bu bağlantı kopar.', + style: TextStyle( + fontSize: 13, color: AppColors.cancelled), + ), + ), + ], + ), + ), + ], + ], + ), + actions: [ + TextButton( + onPressed: () => Navigator.pop(context, false), + child: const Text('Vazgeç')), + FilledButton( + onPressed: () => Navigator.pop(context, true), + style: + FilledButton.styleFrom(backgroundColor: AppColors.cancelled), + child: const Text('Sil'), + ), + ], + ), + ); + + if (confirmed != true || !mounted) return; + + try { + await ClinicPatientsRepository.instance.deletePatient(widget.patientId); + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('Hasta silindi.')), + ); + Navigator.of(context).pop(true); // signal that a delete happened + } + } catch (e) { + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('Silme hatası: $e')), + ); + } + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Hasta Detayı'), + actions: [ + if (!_editMode) ...[ + IconButton( + icon: const Icon(Icons.edit_outlined), + tooltip: 'Düzenle', + onPressed: () => setState(() => _editMode = true), + ), + IconButton( + icon: const Icon(Icons.delete_outline, color: AppColors.cancelled), + tooltip: 'Sil', + onPressed: _delete, + ), + ] else ...[ + if (_isSaving) + const Padding( + padding: EdgeInsets.all(12), + child: SizedBox( + width: 20, + height: 20, + child: CircularProgressIndicator(strokeWidth: 2), + ), + ) + else ...[ + TextButton( + onPressed: () { + setState(() => _editMode = false); + _future.then(_populateControllers); + }, + child: const Text('İptal'), + ), + FilledButton( + onPressed: _save, + child: const Text('Kaydet'), + ), + const SizedBox(width: 8), + ], + ], + ], + ), + body: FutureBuilder( + future: _future, + builder: (ctx, snap) { + if (snap.connectionState == ConnectionState.waiting) { + return const Center(child: CircularProgressIndicator()); + } + if (snap.hasError) { + return Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text('Hata: ${snap.error}'), + const SizedBox(height: 12), + FilledButton( + onPressed: _load, + child: const Text('Tekrar Dene'), + ), + ], + ), + ); + } + + if (_editMode) { + return _EditForm( + formKey: _formKey, + patientCodeController: _patientCodeController, + firstNameController: _firstNameController, + lastNameController: _lastNameController, + phoneController: _phoneController, + notesController: _notesController, + birthDate: _birthDate, + onPickBirthDate: _pickBirthDate, + ); + } + + final patient = snap.data!; + return _PatientView(patient: patient, jobsFuture: _jobsFuture); + }, + ), + ); + } +} + +// ── View ─────────────────────────────────────────────────────────────────── + +class _PatientView extends StatelessWidget { + const _PatientView({required this.patient, required this.jobsFuture}); + final Patient patient; + final Future> jobsFuture; + + @override + Widget build(BuildContext context) { + return ListView( + padding: const EdgeInsets.all(16), + children: [ + // Avatar + name header + Center( + child: Column( + children: [ + Container( + width: 72, + height: 72, + decoration: BoxDecoration( + color: AppColors.inProgressBg, + borderRadius: BorderRadius.circular(20)), + child: Center( + child: Text( + patient.displayName.isNotEmpty + ? patient.displayName[0].toUpperCase() + : '?', + style: const TextStyle( + fontSize: 28, + fontWeight: FontWeight.w800, + color: AppColors.inProgress), + ), + ), + ), + const SizedBox(height: 12), + Text( + patient.displayName, + style: Theme.of(context).textTheme.headlineSmall?.copyWith( + fontWeight: FontWeight.bold, + color: AppColors.textPrimary, + ), + ), + Text( + patient.patientCode, + style: const TextStyle( + fontSize: 13, color: AppColors.textSecondary), + ), + ], + ), + ), + const SizedBox(height: 24), + + Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: AppColors.surface, + borderRadius: BorderRadius.circular(16), + border: Border.all(color: AppColors.border), + boxShadow: [ + BoxShadow( + color: Colors.black.withValues(alpha: 0.05), + blurRadius: 12, + offset: const Offset(0, 4)) + ], + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _DetailRow(label: 'Hasta Kodu', value: patient.patientCode), + if (patient.firstName != null) + _DetailRow(label: 'Ad', value: patient.firstName!), + if (patient.lastName != null) + _DetailRow(label: 'Soyad', value: patient.lastName!), + if (patient.phone != null && patient.phone!.isNotEmpty) + _DetailRow(label: 'Telefon', value: patient.phone!), + if (patient.birthDate != null && patient.birthDate!.isNotEmpty) + _DetailRow( + label: 'Doğum Tarihi', value: patient.birthDate!), + if (patient.notes != null && patient.notes!.isNotEmpty) + _DetailRow(label: 'Notlar', value: patient.notes!), + ], + ), + ), + const SizedBox(height: 24), + + // Job history + _JobHistory(jobsFuture: jobsFuture), + const SizedBox(height: 24), + ], + ); + } +} + +// ── Job history ──────────────────────────────────────────────────────────── + +class _JobHistory extends StatelessWidget { + const _JobHistory({required this.jobsFuture}); + final Future> jobsFuture; + + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + 'İŞ GEÇMİŞİ', + style: TextStyle( + fontSize: 13, + fontWeight: FontWeight.w700, + color: AppColors.textSecondary, + letterSpacing: 0.5, + ), + ), + const SizedBox(height: 8), + FutureBuilder>( + future: jobsFuture, + builder: (ctx, snap) { + if (snap.connectionState == ConnectionState.waiting) { + return const Center(child: CircularProgressIndicator()); + } + if (snap.hasError) { + return Text('Yüklenemedi: ${snap.error}', + style: + const TextStyle(color: AppColors.textSecondary)); + } + final jobs = snap.data ?? []; + if (jobs.isEmpty) { + return Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: AppColors.surface, + borderRadius: BorderRadius.circular(16), + border: Border.all(color: AppColors.border), + ), + child: const Center( + child: Text( + 'Henüz iş kaydı yok.', + style: TextStyle(color: AppColors.textSecondary), + ), + ), + ); + } + return Container( + decoration: BoxDecoration( + color: AppColors.surface, + borderRadius: BorderRadius.circular(16), + border: Border.all(color: AppColors.border), + boxShadow: [ + BoxShadow( + color: Colors.black.withValues(alpha: 0.05), + blurRadius: 12, + offset: const Offset(0, 4)) + ], + ), + child: Column( + children: jobs.asMap().entries.map((entry) { + final i = entry.key; + final job = entry.value; + final isLast = i == jobs.length - 1; + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + Padding( + padding: const EdgeInsets.symmetric( + horizontal: 16, vertical: 12), + child: Row( + children: [ + Container( + width: 40, + height: 40, + decoration: BoxDecoration( + color: _statusBg(job.status), + borderRadius: BorderRadius.circular(10), + ), + child: Icon( + _statusIcon(job.status), + color: _statusColor(job.status), + size: 20, + ), + ), + const SizedBox(width: 12), + Expanded( + child: Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text( + job.patientCode, + style: const TextStyle( + fontWeight: FontWeight.w700, + color: AppColors.textPrimary, + fontSize: 14, + ), + ), + Text( + '${job.prostheticType.label} · ${_statusLabel(job.status)}', + style: const TextStyle( + fontSize: 12, + color: AppColors.textSecondary, + ), + ), + ], + ), + ), + Text( + _formatDate(job.dateCreated), + style: const TextStyle( + fontSize: 12, + color: AppColors.textSecondary, + ), + ), + ], + ), + ), + if (!isLast) + const Divider( + height: 1, + indent: 68, + color: AppColors.border), + ], + ); + }).toList(), + ), + ); + }, + ), + ], + ); + } + + static Color _statusBg(JobStatus s) => switch (s) { + JobStatus.delivered => AppColors.successBg, + JobStatus.cancelled => AppColors.cancelledBg, + JobStatus.inProgress => AppColors.inProgressBg, + _ => AppColors.pendingBg, + }; + + static Color _statusColor(JobStatus s) => switch (s) { + JobStatus.delivered => AppColors.success, + JobStatus.cancelled => AppColors.cancelled, + JobStatus.inProgress => AppColors.inProgress, + _ => AppColors.pending, + }; + + static IconData _statusIcon(JobStatus s) => switch (s) { + JobStatus.delivered => Icons.check_circle_outline, + JobStatus.cancelled => Icons.cancel_outlined, + JobStatus.inProgress => Icons.autorenew, + _ => Icons.hourglass_empty_outlined, + }; + + static String _statusLabel(JobStatus s) => switch (s) { + JobStatus.pending => 'Bekliyor', + JobStatus.inProgress => 'Üretimde', + JobStatus.sent => 'Gönderildi', + JobStatus.delivered => 'Teslim Edildi', + JobStatus.cancelled => 'İptal', + }; + + static String _formatDate(DateTime d) { + return '${d.day.toString().padLeft(2, '0')}.${d.month.toString().padLeft(2, '0')}.${d.year}'; + } +} + +// ── Helpers ──────────────────────────────────────────────────────────────── + +class _DetailRow extends StatelessWidget { + const _DetailRow({required this.label, required this.value}); + final String label; + final String value; + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 6), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + width: 120, + child: Text( + label, + style: const TextStyle( + fontSize: 13, color: AppColors.textSecondary), + ), + ), + Expanded( + child: Text( + value, + style: const TextStyle( + fontSize: 14, + fontWeight: FontWeight.w500, + color: AppColors.textPrimary), + ), + ), + ], + ), + ); + } +} + +// ── Edit form ────────────────────────────────────────────────────────────── + +class _EditForm extends StatelessWidget { + const _EditForm({ + required this.formKey, + required this.patientCodeController, + required this.firstNameController, + required this.lastNameController, + required this.phoneController, + required this.notesController, + required this.birthDate, + required this.onPickBirthDate, + }); + + final GlobalKey formKey; + final TextEditingController patientCodeController; + final TextEditingController firstNameController; + final TextEditingController lastNameController; + final TextEditingController phoneController; + final TextEditingController notesController; + final String? birthDate; + final VoidCallback onPickBirthDate; + + @override + Widget build(BuildContext context) { + return SingleChildScrollView( + padding: const EdgeInsets.all(16), + child: Form( + key: formKey, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + TextFormField( + controller: patientCodeController, + decoration: const InputDecoration( + labelText: 'Hasta Kodu *', + border: OutlineInputBorder(), + ), + validator: (val) => + (val == null || val.trim().isEmpty) + ? 'Hasta kodu zorunludur' + : null, + ), + const SizedBox(height: 12), + Row( + children: [ + Expanded( + child: TextFormField( + controller: firstNameController, + decoration: const InputDecoration( + labelText: 'Ad', + border: OutlineInputBorder(), + ), + ), + ), + const SizedBox(width: 12), + Expanded( + child: TextFormField( + controller: lastNameController, + decoration: const InputDecoration( + labelText: 'Soyad', + border: OutlineInputBorder(), + ), + ), + ), + ], + ), + const SizedBox(height: 12), + TextFormField( + controller: phoneController, + decoration: const InputDecoration( + labelText: 'Telefon', + border: OutlineInputBorder(), + ), + keyboardType: TextInputType.phone, + ), + const SizedBox(height: 12), + InkWell( + onTap: onPickBirthDate, + child: InputDecorator( + decoration: const InputDecoration( + labelText: 'Doğum Tarihi', + border: OutlineInputBorder(), + suffixIcon: Icon(Icons.calendar_today), + ), + child: Text( + birthDate ?? 'Tarih seçin', + style: birthDate != null + ? null + : TextStyle( + color: Theme.of(context) + .colorScheme + .onSurfaceVariant, + ), + ), + ), + ), + const SizedBox(height: 12), + TextFormField( + controller: notesController, + decoration: const InputDecoration( + labelText: 'Notlar', + border: OutlineInputBorder(), + ), + minLines: 3, + maxLines: 5, + ), + ], + ), + ), + ); + } +} diff --git a/lib/features/clinic/patients/clinic_patients_repository.dart b/lib/features/clinic/patients/clinic_patients_repository.dart new file mode 100644 index 0000000..92a204e --- /dev/null +++ b/lib/features/clinic/patients/clinic_patients_repository.dart @@ -0,0 +1,67 @@ +import 'package:pocketbase/pocketbase.dart'; +import '../../../core/api/pocketbase_client.dart'; +import '../../../models/patient.dart'; + +class ClinicPatientsRepository { + ClinicPatientsRepository._(); + static final instance = ClinicPatientsRepository._(); + + PocketBase get _pb => PocketBaseClient.instance.pb; + + Future> listPatients( + String tenantId, { + String? search, + int page = 1, + int limit = 30, + }) async { + final filterParts = ['tenant_id = "$tenantId"']; + if (search != null && search.isNotEmpty) { + filterParts.add( + '(patient_code ~ "$search" || first_name ~ "$search" || last_name ~ "$search")', + ); + } + + final result = await _pb.collection('patients').getList( + page: page, + perPage: limit, + filter: filterParts.join(' && '), + ); + return (result.items.map((r) => Patient.fromJson(r.toJson())).toList() + ..sort((a, b) => a.patientCode.compareTo(b.patientCode))); + } + + Future getPatient(String patientId) async { + final record = await _pb.collection('patients').getOne(patientId); + return Patient.fromJson(record.toJson()); + } + + Future createPatient({ + required String tenantId, + required String patientCode, + String? firstName, + String? lastName, + String? birthDate, + String? phone, + String? notes, + }) async { + final record = await _pb.collection('patients').create(body: { + 'tenant_id': tenantId, + 'patient_code': patientCode, + if (firstName != null) 'first_name': firstName, + if (lastName != null) 'last_name': lastName, + if (birthDate != null) 'birth_date': birthDate, + if (phone != null) 'phone': phone, + if (notes != null) 'notes': notes, + }); + return Patient.fromJson(record.toJson()); + } + + Future updatePatient(String patientId, Map patch) async { + final record = await _pb.collection('patients').update(patientId, body: patch); + return Patient.fromJson(record.toJson()); + } + + Future deletePatient(String patientId) async { + await _pb.collection('patients').delete(patientId); + } +} diff --git a/lib/features/clinic/patients/clinic_patients_screen.dart b/lib/features/clinic/patients/clinic_patients_screen.dart new file mode 100644 index 0000000..a92615e --- /dev/null +++ b/lib/features/clinic/patients/clinic_patients_screen.dart @@ -0,0 +1,575 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:go_router/go_router.dart'; + +import '../../../core/providers/auth_provider.dart'; +import '../../../core/theme/app_theme.dart'; +import '../../../core/widgets/gradient_app_bar.dart'; +import '../../../models/patient.dart'; +import 'clinic_patients_repository.dart'; + +enum _PatientSort { nameAZ, nameZA, byCode } + +const _kSortLabels = [ + 'Ada Göre (A → Z)', + 'Ada Göre (Z → A)', + 'Hasta Koduna Göre', +]; + +void _showAdaptive(BuildContext context, Widget content) { + final isDesktop = + MediaQuery.sizeOf(context).width > AppLayout.sidebarBreakpoint; + if (isDesktop) { + showDialog( + context: context, + builder: (_) => Dialog( + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)), + child: ConstrainedBox( + constraints: const BoxConstraints(maxWidth: 560), + child: content, + ), + ), + ); + } else { + showModalBottomSheet( + context: context, + isScrollControlled: true, + backgroundColor: Colors.transparent, + builder: (_) => content, + ); + } +} + +class ClinicPatientsScreen extends ConsumerStatefulWidget { + const ClinicPatientsScreen({super.key}); + + @override + ConsumerState createState() => + _ClinicPatientsScreenState(); +} + +class _ClinicPatientsScreenState extends ConsumerState { + late Future> _future; + final _searchController = TextEditingController(); + String _searchQuery = ''; + _PatientSort _sort = _PatientSort.nameAZ; + + @override + void initState() { + super.initState(); + _load(); + } + + @override + void dispose() { + _searchController.dispose(); + super.dispose(); + } + + void _load([String? search]) { + final tenantId = ref.read(authProvider).activeTenant!.tenant.id; + setState(() { + _future = ClinicPatientsRepository.instance.listPatients( + tenantId, + search: search?.trim().isNotEmpty == true ? search : null, + limit: 100, + ); + }); + } + + void _onSearchChanged(String value) { + setState(() => _searchQuery = value); + _load(value); + } + + Future _showSortOptions() async { + final result = await showSortSheet( + context, + title: 'Sıralama', + options: _kSortLabels, + current: _sort.index, + ); + if (result != null) { + setState(() => _sort = _PatientSort.values[result]); + } + } + + List _sorted(List patients) { + final list = List.from(patients); + switch (_sort) { + case _PatientSort.nameAZ: + list.sort((a, b) => a.displayName.compareTo(b.displayName)); + case _PatientSort.nameZA: + list.sort((a, b) => b.displayName.compareTo(a.displayName)); + case _PatientSort.byCode: + list.sort((a, b) => a.patientCode.compareTo(b.patientCode)); + } + return list; + } + + void _showNewPatientSheet() { + _showAdaptive( + context, + _NewPatientSheet( + onCreated: () { + Navigator.of(context).pop(); + _load(_searchQuery); + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('Hasta oluşturuldu.')), + ); + }, + ), + ); + } + + @override + Widget build(BuildContext context) { + final isSortActive = _sort != _PatientSort.nameAZ; + + return Scaffold( + backgroundColor: AppColors.background, + appBar: GradientAppBar( + title: 'Hastalar', + category: 'KLİNİK', + searchController: _searchController, + onSearchChanged: _onSearchChanged, + searchHint: 'Ad, soyad veya kod ile arayın...', + actions: [ + IconButton( + onPressed: _showSortOptions, + tooltip: 'Sırala', + icon: Badge( + isLabelVisible: isSortActive, + smallSize: 8, + backgroundColor: AppColors.accent, + child: const Icon(Icons.sort_rounded), + ), + ), + IconButton( + onPressed: _showNewPatientSheet, + tooltip: 'Yeni Hasta', + icon: const Icon(Icons.person_add_outlined), + ), + ], + ), + body: Column( + children: [ + Expanded( + child: RefreshIndicator( + color: AppColors.accent, + onRefresh: () async => _load(_searchQuery), + child: FutureBuilder>( + future: _future, + builder: (ctx, snap) { + if (snap.connectionState == ConnectionState.waiting) { + return const Center( + child: CircularProgressIndicator( + color: AppColors.accent)); + } + if (snap.hasError) { + return Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + width: 64, + height: 64, + decoration: BoxDecoration( + color: AppColors.cancelledBg, + borderRadius: BorderRadius.circular(16)), + child: const Icon(Icons.wifi_off_rounded, + color: AppColors.cancelled, size: 30), + ), + const SizedBox(height: 16), + Text('Hata: ${snap.error}', + style: const TextStyle( + color: AppColors.textSecondary)), + const SizedBox(height: 12), + FilledButton.icon( + onPressed: () => _load(_searchQuery), + icon: const Icon(Icons.refresh_rounded, size: 18), + label: const Text('Tekrar Dene'), + ), + ], + ), + ); + } + final patients = _sorted(snap.data!); + if (patients.isEmpty) { + return Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + width: 72, + height: 72, + decoration: BoxDecoration( + color: AppColors.inProgressBg, + borderRadius: BorderRadius.circular(20)), + child: const Icon(Icons.people_outline, + color: AppColors.inProgress, size: 32), + ), + const SizedBox(height: 16), + Text( + _searchQuery.isNotEmpty + ? 'Sonuç bulunamadı' + : 'Henüz hasta yok', + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + color: AppColors.textPrimary), + ), + if (_searchQuery.isEmpty) ...[ + const SizedBox(height: 8), + const Text( + 'Yeni hasta eklemek için + düğmesine dokunun', + style: TextStyle( + fontSize: 13, + color: AppColors.textSecondary), + ), + ], + ], + ), + ); + } + return ListView.builder( + padding: const EdgeInsets.fromLTRB(16, 12, 16, 24), + itemCount: patients.length, + itemBuilder: (context, index) { + final patient = patients[index]; + return Padding( + padding: const EdgeInsets.only(bottom: 10), + child: _PatientCard( + patient: patient, + onTap: () => context + .push('/clinic/patients/${patient.id}'), + ), + ); + }, + ); + }, + ), + ), + ), + ], + ), + ); + } +} + +class _PatientCard extends StatelessWidget { + const _PatientCard({required this.patient, required this.onTap}); + + final Patient patient; + final VoidCallback onTap; + + String get _initials { + final name = patient.displayName; + if (name.isEmpty) return '?'; + final parts = name.trim().split(' '); + if (parts.length >= 2) { + return '${parts.first[0]}${parts.last[0]}'.toUpperCase(); + } + return name[0].toUpperCase(); + } + + @override + Widget build(BuildContext context) { + return Material( + color: AppColors.surface, + borderRadius: BorderRadius.circular(14), + child: InkWell( + onTap: onTap, + borderRadius: BorderRadius.circular(14), + child: Container( + padding: const EdgeInsets.all(14), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(14), + border: Border.all(color: AppColors.border), + boxShadow: [ + BoxShadow( + color: Colors.black.withValues(alpha: 0.04), + blurRadius: 8, + offset: const Offset(0, 2)) + ]), + child: Row( + children: [ + Container( + width: 46, + height: 46, + decoration: BoxDecoration( + gradient: const LinearGradient( + begin: Alignment.topLeft, + end: Alignment.bottomRight, + colors: [Color(0xFF1E3A5F), Color(0xFF0369A1)], + ), + borderRadius: BorderRadius.circular(13), + ), + child: Center( + child: Text( + _initials, + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.w700, + color: Colors.white, + ), + ), + ), + ), + const SizedBox(width: 12), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + patient.displayName.isNotEmpty + ? patient.displayName + : patient.patientCode, + style: const TextStyle( + fontSize: 15, + fontWeight: FontWeight.w600, + color: AppColors.textPrimary), + ), + const SizedBox(height: 2), + Row( + children: [ + Container( + padding: const EdgeInsets.symmetric( + horizontal: 6, vertical: 2), + decoration: BoxDecoration( + color: AppColors.surfaceVariant, + borderRadius: BorderRadius.circular(6), + ), + child: Text( + patient.patientCode, + style: const TextStyle( + fontSize: 11, + color: AppColors.textSecondary, + fontWeight: FontWeight.w500, + ), + ), + ), + if (patient.phone != null && + patient.phone!.isNotEmpty) ...[ + const SizedBox(width: 8), + const Icon(Icons.phone_outlined, + size: 11, color: AppColors.textMuted), + const SizedBox(width: 3), + Text( + patient.phone!, + style: const TextStyle( + fontSize: 11, color: AppColors.textMuted), + ), + ], + ], + ), + ], + ), + ), + const Icon(Icons.chevron_right, + color: AppColors.textMuted, size: 20), + ], + ), + ), + ), + ); + } +} + +// ── New Patient Sheet ───────────────────────────────────────────────────────── + +class _NewPatientSheet extends ConsumerStatefulWidget { + const _NewPatientSheet({required this.onCreated}); + + final VoidCallback onCreated; + + @override + ConsumerState<_NewPatientSheet> createState() => _NewPatientSheetState(); +} + +class _NewPatientSheetState extends ConsumerState<_NewPatientSheet> { + final _formKey = GlobalKey(); + final _patientCodeController = TextEditingController(); + final _firstNameController = TextEditingController(); + final _lastNameController = TextEditingController(); + final _phoneController = TextEditingController(); + final _notesController = TextEditingController(); + String? _birthDate; + bool _isSubmitting = false; + + @override + void dispose() { + _patientCodeController.dispose(); + _firstNameController.dispose(); + _lastNameController.dispose(); + _phoneController.dispose(); + _notesController.dispose(); + super.dispose(); + } + + Future _pickBirthDate() async { + final picked = await showDatePicker( + context: context, + initialDate: DateTime(1990), + firstDate: DateTime(1900), + lastDate: DateTime.now(), + ); + if (picked != null) { + setState(() { + _birthDate = picked.toIso8601String().split('T').first; + }); + } + } + + Future _submit() async { + if (!_formKey.currentState!.validate()) return; + setState(() => _isSubmitting = true); + try { + final tenantId = ref.read(authProvider).activeTenant!.tenant.id; + await ClinicPatientsRepository.instance.createPatient( + tenantId: tenantId, + patientCode: _patientCodeController.text.trim(), + firstName: _firstNameController.text.trim().isNotEmpty + ? _firstNameController.text.trim() + : null, + lastName: _lastNameController.text.trim().isNotEmpty + ? _lastNameController.text.trim() + : null, + phone: _phoneController.text.trim().isNotEmpty + ? _phoneController.text.trim() + : null, + birthDate: _birthDate, + notes: _notesController.text.trim().isNotEmpty + ? _notesController.text.trim() + : null, + ); + widget.onCreated(); + } catch (e) { + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('Hata: $e')), + ); + } + } finally { + if (mounted) setState(() => _isSubmitting = false); + } + } + + @override + Widget build(BuildContext context) { + final isDesktop = + MediaQuery.sizeOf(context).width > AppLayout.sidebarBreakpoint; + return Container( + decoration: BoxDecoration( + color: AppColors.surface, + borderRadius: BorderRadius.vertical( + top: isDesktop ? Radius.zero : const Radius.circular(20), + ), + ), + padding: EdgeInsets.only( + bottom: isDesktop ? 0 : MediaQuery.of(context).viewInsets.bottom, + ), + child: SingleChildScrollView( + padding: const EdgeInsets.all(20), + child: Form( + key: _formKey, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Row( + children: [ + Expanded( + child: Text( + 'Yeni Hasta', + style: Theme.of(context) + .textTheme + .titleLarge + ?.copyWith(fontWeight: FontWeight.bold), + ), + ), + IconButton( + icon: const Icon(Icons.close), + onPressed: () => Navigator.of(context).pop(), + ), + ], + ), + const SizedBox(height: 16), + TextFormField( + controller: _patientCodeController, + decoration: const InputDecoration( + labelText: 'Hasta Kodu *', + hintText: 'Ör: P-001', + ), + validator: (val) => + (val == null || val.trim().isEmpty) + ? 'Hasta kodu zorunludur' + : null, + ), + const SizedBox(height: 12), + Row( + children: [ + Expanded( + child: TextFormField( + controller: _firstNameController, + decoration: const InputDecoration(labelText: 'Ad'), + ), + ), + const SizedBox(width: 12), + Expanded( + child: TextFormField( + controller: _lastNameController, + decoration: const InputDecoration(labelText: 'Soyad'), + ), + ), + ], + ), + const SizedBox(height: 12), + TextFormField( + controller: _phoneController, + decoration: const InputDecoration(labelText: 'Telefon'), + keyboardType: TextInputType.phone, + ), + const SizedBox(height: 12), + InkWell( + onTap: _pickBirthDate, + child: InputDecorator( + decoration: const InputDecoration( + labelText: 'Doğum Tarihi', + suffixIcon: Icon(Icons.calendar_today), + ), + child: Text( + _birthDate ?? 'Tarih seçin', + style: _birthDate != null + ? null + : const TextStyle(color: AppColors.textMuted), + ), + ), + ), + const SizedBox(height: 12), + TextFormField( + controller: _notesController, + decoration: const InputDecoration(labelText: 'Notlar'), + minLines: 2, + maxLines: 3, + ), + const SizedBox(height: 20), + if (_isSubmitting) + const Center( + child: CircularProgressIndicator(color: AppColors.accent)) + else + FilledButton( + onPressed: _submit, + style: FilledButton.styleFrom( + minimumSize: const Size(double.infinity, 48), + ), + child: const Text('Hasta Oluştur'), + ), + const SizedBox(height: 8), + ], + ), + ), + ), + ); + } +} diff --git a/lib/features/clinic/settings/clinic_settings_screen.dart b/lib/features/clinic/settings/clinic_settings_screen.dart new file mode 100644 index 0000000..ca532dd --- /dev/null +++ b/lib/features/clinic/settings/clinic_settings_screen.dart @@ -0,0 +1,680 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +import 'package:go_router/go_router.dart'; + +import '../../../core/l10n/app_strings.dart'; +import '../../../core/providers/auth_provider.dart'; +import '../../../core/providers/locale_provider.dart'; +import '../../../core/router/app_router.dart'; +import '../../../core/theme/app_theme.dart'; +import '../../../models/tenant.dart'; +import '../../shared/tenant_team_screen.dart'; +import '../connections/clinic_connections_screen.dart'; + +class ClinicSettingsScreen extends ConsumerWidget { + const ClinicSettingsScreen({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final auth = ref.watch(authProvider); + final s = ref.watch(stringsProvider); + final profile = auth.profile; + final membership = auth.activeTenant; + final tenant = membership?.tenant; + final canEdit = membership?.isAdmin ?? false; + + return Scaffold( + appBar: AppBar(title: Text(s.settings)), + body: ListView( + padding: const EdgeInsets.all(16), + children: [ + // User card + _SectionHeader(title: s.userInfo), + _UserCard(profile: profile), + const SizedBox(height: 20), + + // Clinic info + _SectionHeader( + title: s.clinicInfo, + action: canEdit + ? IconButton( + icon: const Icon(Icons.edit_outlined, + size: 18, color: AppColors.accent), + tooltip: s.edit, + onPressed: () => _showEditSheet(context, ref, tenant, s), + ) + : null, + ), + _InfoCard(children: [ + _InfoTile( + icon: Icons.business, + label: s.clinicName, + value: tenant?.companyName ?? '-', + ), + _InfoTile( + icon: Icons.category_outlined, + label: s.type, + value: _tenantKindLabel(tenant?.kind, s), + ), + _InfoTile( + icon: Icons.star_outline, + label: s.role, + value: _roleLabel(membership?.role, s), + ), + ]), + const SizedBox(height: 20), + + // Connections + if (membership?.showConnections ?? false) ...[ + _SectionHeader(title: s.connections), + _InfoCard(children: [ + _NavTile( + icon: Icons.link_rounded, + iconColor: AppColors.inProgress, + iconBg: AppColors.inProgressBg, + title: s.labConnections, + subtitle: s.labConnectionsSub, + onTap: () => Navigator.push( + context, + MaterialPageRoute( + builder: (_) => const ClinicConnectionsScreen()), + ), + ), + ]), + const SizedBox(height: 20), + ], + + // Other memberships + if (auth.memberships.length > 1) ...[ + _SectionHeader(title: s.otherMemberships), + _InfoCard(children: [ + for (final m + in auth.memberships.where((m) => m.id != membership?.id)) + _NavTile( + icon: Icons.switch_account_outlined, + iconColor: AppColors.inProgress, + iconBg: AppColors.inProgressBg, + title: m.tenant.companyName, + subtitle: _tenantKindLabel(m.tenant.kind, s), + onTap: () { + ref.read(authProvider.notifier).setActiveTenant(m); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text(s.tenantSelected(m.tenant.companyName))), + ); + }, + ), + ]), + const SizedBox(height: 20), + ], + + // Team management + Reports + if (membership?.canManageUsers ?? false) ...[ + _SectionHeader(title: s.management), + _InfoCard(children: [ + _NavTile( + icon: Icons.group_outlined, + iconColor: AppColors.inProgress, + iconBg: AppColors.inProgressBg, + title: s.team, + subtitle: s.teamSub, + onTap: () => Navigator.push( + context, + MaterialPageRoute( + builder: (_) => const TenantTeamScreen()), + ), + ), + _NavTile( + icon: Icons.bar_chart_rounded, + iconColor: AppColors.accent, + iconBg: AppColors.inProgressBg, + title: s.reports, + subtitle: s.reportsSub, + onTap: () => context.push(routeClinicReports), + ), + _NavTile( + icon: Icons.auto_awesome_outlined, + iconColor: const Color(0xFF7C3AED), + iconBg: const Color(0xFFF3E8FF), + title: s.aiAssistant, + subtitle: s.aiAssistantSub, + onTap: () => context.push(routeClinicAi), + ), + ]), + const SizedBox(height: 20), + ], + + // Preferences (language) + _SectionHeader(title: s.preferences), + _InfoCard(children: [ + _NavTile( + icon: Icons.language_outlined, + iconColor: AppColors.accent, + iconBg: AppColors.inProgressBg, + title: s.appLanguage, + subtitle: _currentLanguageLabel(ref.watch(localeProvider).languageCode, s), + onTap: () => _showLanguagePicker(context, ref, s), + ), + ]), + const SizedBox(height: 20), + + // Sign out + _SignOutCard(ref: ref, s: s), + const SizedBox(height: 32), + const Center( + child: Text('DLS — Dental Lab System', + style: TextStyle(fontSize: 12, color: AppColors.textMuted)), + ), + const SizedBox(height: 8), + ], + ), + ); + } + + void _showEditSheet(BuildContext context, WidgetRef ref, Tenant? tenant, AppStrings s) { + if (tenant == null) return; + showModalBottomSheet( + context: context, + isScrollControlled: true, + backgroundColor: Colors.transparent, + builder: (_) => _EditTenantSheet( + tenant: tenant, + s: s, + onSave: (name) async { + await ref + .read(authProvider.notifier) + .updateTenantInfo(tenantId: tenant.id, companyName: name); + }, + ), + ); + } + + void _showLanguagePicker(BuildContext context, WidgetRef ref, AppStrings s) { + showModalBottomSheet( + context: context, + backgroundColor: Colors.transparent, + builder: (_) => _LanguagePickerSheet(s: s, ref: ref), + ); + } + + static String _currentLanguageLabel(String code, AppStrings s) => switch (code) { + 'en' => s.languageEnglish, + 'ru' => s.languageRussian, + 'ar' => s.languageArabic, + 'de' => s.languageGerman, + _ => s.languageTurkish, + }; + + static String _tenantKindLabel(TenantKind? kind, AppStrings s) => + switch (kind) { + TenantKind.clinic => s.tenantKindClinic, + TenantKind.lab => s.tenantKindLab, + null => '-', + }; + + static String _roleLabel(TenantRole? role, AppStrings s) => switch (role) { + TenantRole.owner => s.roleOwner, + TenantRole.admin => s.roleAdmin, + TenantRole.technician => s.roleTechnician, + TenantRole.delivery => s.roleDelivery, + TenantRole.finance => s.roleFinance, + TenantRole.doctor => s.roleDoctor, + TenantRole.member => s.roleMember, + null => '-', + }; +} + +// ── Language picker sheet ───────────────────────────────────────────────────── + +class _LanguagePickerSheet extends ConsumerWidget { + const _LanguagePickerSheet({required this.s, required this.ref}); + final AppStrings s; + final WidgetRef ref; + + @override + Widget build(BuildContext context, WidgetRef _) { + final currentLocale = ref.watch(localeProvider); + final options = [ + ('tr', '🇹🇷', s.languageTurkish), + ('en', '🇬🇧', s.languageEnglish), + ('ru', '🇷🇺', s.languageRussian), + ('ar', '🇸🇦', s.languageArabic), + ('de', '🇩🇪', s.languageGerman), + ]; + + return Container( + decoration: const BoxDecoration( + color: AppColors.surface, + borderRadius: BorderRadius.vertical(top: Radius.circular(20)), + ), + padding: const EdgeInsets.all(20), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Center( + child: Container( + width: 40, + height: 4, + decoration: BoxDecoration( + color: AppColors.border, + borderRadius: BorderRadius.circular(2), + ), + ), + ), + const SizedBox(height: 16), + Text( + s.languageSelection, + style: const TextStyle( + fontSize: 17, + fontWeight: FontWeight.w700, + color: AppColors.textPrimary, + ), + ), + const SizedBox(height: 12), + for (final (code, flag, label) in options) + ListTile( + contentPadding: const EdgeInsets.symmetric(horizontal: 4), + leading: Text(flag, style: const TextStyle(fontSize: 24)), + title: Text( + label, + style: const TextStyle( + fontSize: 15, + fontWeight: FontWeight.w500, + color: AppColors.textPrimary, + ), + ), + trailing: currentLocale.languageCode == code + ? const Icon(Icons.check_circle_rounded, + color: AppColors.accent) + : null, + onTap: () { + ref.read(localeProvider.notifier).setLocale(Locale(code)); + ref.read(authProvider.notifier).updateLanguage(code); + Navigator.pop(context); + }, + ), + SizedBox(height: MediaQuery.paddingOf(context).bottom + 4), + ], + ), + ); + } +} + +// ── Edit sheet ──────────────────────────────────────────────────────────────── + +class _EditTenantSheet extends StatefulWidget { + const _EditTenantSheet({ + required this.tenant, + required this.s, + required this.onSave, + }); + final Tenant tenant; + final AppStrings s; + final Future Function(String companyName) onSave; + + @override + State<_EditTenantSheet> createState() => _EditTenantSheetState(); +} + +class _EditTenantSheetState extends State<_EditTenantSheet> { + late final TextEditingController _nameController; + bool _saving = false; + + @override + void initState() { + super.initState(); + _nameController = TextEditingController(text: widget.tenant.companyName); + } + + @override + void dispose() { + _nameController.dispose(); + super.dispose(); + } + + Future _submit() async { + final name = _nameController.text.trim(); + if (name.isEmpty) return; + setState(() => _saving = true); + final navigator = Navigator.of(context); + final messenger = ScaffoldMessenger.of(context); + try { + await widget.onSave(name); + navigator.pop(); + } catch (e) { + messenger.showSnackBar( + SnackBar(content: Text('${widget.s.errorPrefix}: $e'))); + } finally { + if (mounted) setState(() => _saving = false); + } + } + + @override + Widget build(BuildContext context) { + final s = widget.s; + return Padding( + padding: + EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom), + child: Container( + decoration: const BoxDecoration( + color: AppColors.surface, + borderRadius: BorderRadius.vertical(top: Radius.circular(20)), + ), + padding: const EdgeInsets.all(20), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Center( + child: Container( + width: 40, + height: 4, + decoration: BoxDecoration( + color: AppColors.border, + borderRadius: BorderRadius.circular(2)), + ), + ), + const SizedBox(height: 16), + Text(s.editClinicInfo, + style: const TextStyle( + fontSize: 17, + fontWeight: FontWeight.w700, + color: AppColors.textPrimary)), + const SizedBox(height: 16), + TextFormField( + controller: _nameController, + decoration: InputDecoration( + labelText: s.clinicName, + hintText: s.clinicNameHint, + ), + textCapitalization: TextCapitalization.words, + ), + const SizedBox(height: 20), + if (_saving) + const Center( + child: CircularProgressIndicator(color: AppColors.accent)) + else + FilledButton( + onPressed: _submit, + style: FilledButton.styleFrom( + minimumSize: const Size(double.infinity, 48)), + child: Text(s.save), + ), + SizedBox(height: MediaQuery.paddingOf(context).bottom + 4), + ], + ), + ), + ); + } +} + +// ── Reusable UI pieces ──────────────────────────────────────────────────────── + +class _UserCard extends StatelessWidget { + const _UserCard({required this.profile}); + final dynamic profile; + + @override + Widget build(BuildContext context) { + final displayName = (profile?.displayName?.isNotEmpty == true) + ? profile!.displayName as String + : 'Kullanıcı'; + final initial = (profile?.displayName?.isNotEmpty == true + ? (profile!.displayName as String)[0] + : (profile?.email as String?)?[0] ?? '?') + .toUpperCase(); + + return Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: AppColors.surface, + borderRadius: BorderRadius.circular(16), + border: Border.all(color: AppColors.border), + boxShadow: [ + BoxShadow( + color: Colors.black.withValues(alpha: 0.05), + blurRadius: 12, + offset: const Offset(0, 4)) + ], + ), + child: Row( + children: [ + Container( + width: 56, + height: 56, + decoration: BoxDecoration( + color: AppColors.inProgressBg, + borderRadius: BorderRadius.circular(14)), + child: Center( + child: Text(initial, + style: const TextStyle( + fontSize: 22, + fontWeight: FontWeight.w700, + color: AppColors.inProgress)), + ), + ), + const SizedBox(width: 16), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(displayName, + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + color: AppColors.textPrimary)), + const SizedBox(height: 2), + Text(profile?.email as String? ?? '', + style: const TextStyle( + fontSize: 13, color: AppColors.textSecondary)), + ], + ), + ), + ], + ), + ); + } +} + +class _SectionHeader extends StatelessWidget { + const _SectionHeader({required this.title, this.action}); + final String title; + final Widget? action; + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(bottom: 8), + child: Row( + children: [ + Expanded( + child: Text( + title, + style: const TextStyle( + fontSize: 13, + fontWeight: FontWeight.w600, + color: AppColors.accent, + letterSpacing: 0.3), + ), + ), + if (action != null) action!, + ], + ), + ); + } +} + +class _InfoCard extends StatelessWidget { + const _InfoCard({required this.children}); + final List children; + + @override + Widget build(BuildContext context) { + return Container( + decoration: BoxDecoration( + color: AppColors.surface, + borderRadius: BorderRadius.circular(16), + border: Border.all(color: AppColors.border), + boxShadow: [ + BoxShadow( + color: Colors.black.withValues(alpha: 0.05), + blurRadius: 12, + offset: const Offset(0, 4)) + ], + ), + child: Column( + children: [ + for (int i = 0; i < children.length; i++) ...[ + children[i], + if (i < children.length - 1) + const Divider( + height: 1, indent: 16, endIndent: 16, color: AppColors.border), + ], + ], + ), + ); + } +} + +class _InfoTile extends StatelessWidget { + const _InfoTile( + {required this.icon, required this.label, required this.value}); + final IconData icon; + final String label; + final String value; + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), + child: Row( + children: [ + Icon(icon, size: 18, color: AppColors.textSecondary), + const SizedBox(width: 12), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(label, + style: const TextStyle( + fontSize: 11, color: AppColors.textMuted)), + const SizedBox(height: 2), + Text(value, + style: const TextStyle( + fontSize: 14, + fontWeight: FontWeight.w500, + color: AppColors.textPrimary)), + ], + ), + ), + ], + ), + ); + } +} + +class _NavTile extends StatelessWidget { + const _NavTile({ + required this.icon, + required this.iconColor, + required this.iconBg, + required this.title, + required this.onTap, + this.subtitle, + }); + + final IconData icon; + final Color iconColor; + final Color iconBg; + final String title; + final String? subtitle; + final VoidCallback onTap; + + @override + Widget build(BuildContext context) { + return ListTile( + contentPadding: + const EdgeInsets.symmetric(horizontal: 16, vertical: 2), + leading: Container( + width: 36, + height: 36, + decoration: BoxDecoration( + color: iconBg, borderRadius: BorderRadius.circular(9)), + child: Icon(icon, color: iconColor, size: 18), + ), + title: Text(title, + style: const TextStyle( + fontWeight: FontWeight.w600, color: AppColors.textPrimary)), + subtitle: subtitle != null + ? Text(subtitle!, + style: const TextStyle(color: AppColors.textSecondary)) + : null, + trailing: + const Icon(Icons.chevron_right, color: AppColors.textSecondary), + onTap: onTap, + ); + } +} + +class _SignOutCard extends StatelessWidget { + const _SignOutCard({required this.ref, required this.s}); + final WidgetRef ref; + final AppStrings s; + + @override + Widget build(BuildContext context) { + return Container( + decoration: BoxDecoration( + color: AppColors.surface, + borderRadius: BorderRadius.circular(16), + border: Border.all(color: AppColors.cancelledBg), + boxShadow: [ + BoxShadow( + color: Colors.black.withValues(alpha: 0.05), + blurRadius: 12, + offset: const Offset(0, 4)) + ], + ), + child: ListTile( + contentPadding: + const EdgeInsets.symmetric(horizontal: 16, vertical: 2), + leading: Container( + width: 36, + height: 36, + decoration: BoxDecoration( + color: AppColors.cancelledBg, + borderRadius: BorderRadius.circular(9)), + child: const Icon(Icons.logout, + color: AppColors.cancelled, size: 18), + ), + title: Text(s.signOut, + style: const TextStyle( + color: AppColors.cancelled, fontWeight: FontWeight.w600)), + onTap: () async { + final confirmed = await showDialog( + context: context, + builder: (ctx) => AlertDialog( + title: Text(s.signOutTitle), + content: Text(s.signOutConfirm), + actions: [ + TextButton( + onPressed: () => Navigator.pop(ctx, false), + child: Text(s.cancel)), + FilledButton( + onPressed: () => Navigator.pop(ctx, true), + style: FilledButton.styleFrom( + backgroundColor: AppColors.cancelled), + child: Text(s.signOut), + ), + ], + ), + ); + if (confirmed == true) { + await ref.read(authProvider.notifier).signOut(); + } + }, + ), + ); + } +} diff --git a/lib/features/lab/connections/connection_detail_screen.dart b/lib/features/lab/connections/connection_detail_screen.dart new file mode 100644 index 0000000..b402c20 --- /dev/null +++ b/lib/features/lab/connections/connection_detail_screen.dart @@ -0,0 +1,581 @@ +import 'package:flutter/material.dart'; + +import '../../../core/theme/app_theme.dart'; +import '../../../models/connection.dart'; +import '../../../models/job.dart'; +import 'connection_stats_repository.dart'; + +class ConnectionDetailScreen extends StatefulWidget { + const ConnectionDetailScreen({ + super.key, + required this.connection, + required this.labTenantId, + }); + + final Connection connection; + final String labTenantId; + + @override + State createState() => _ConnectionDetailScreenState(); +} + +class _ConnectionDetailScreenState extends State { + late Future _future; + + @override + void initState() { + super.initState(); + _load(); + } + + void _load() { + setState(() { + _future = ConnectionStatsRepository.instance.fetchStats( + labTenantId: widget.labTenantId, + clinicTenantId: widget.connection.clinicTenantId, + ); + }); + } + + @override + Widget build(BuildContext context) { + final conn = widget.connection; + final clinicName = conn.clinicName ?? 'Klinik'; + + return Scaffold( + backgroundColor: AppColors.background, + body: CustomScrollView( + slivers: [ + SliverAppBar( + pinned: true, + expandedHeight: 140, + backgroundColor: AppColors.primary, + foregroundColor: Colors.white, + flexibleSpace: FlexibleSpaceBar( + background: Container( + decoration: const BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topLeft, + end: Alignment.bottomRight, + colors: [Color(0xFF0F172A), AppColors.primary], + ), + ), + child: SafeArea( + child: Padding( + padding: const EdgeInsets.fromLTRB(20, 48, 20, 16), + child: Row( + children: [ + Container( + width: 52, + height: 52, + decoration: BoxDecoration( + color: Colors.white.withValues(alpha: 0.15), + borderRadius: BorderRadius.circular(14), + border: Border.all(color: Colors.white.withValues(alpha: 0.2)), + ), + child: const Icon(Icons.local_hospital_outlined, color: Colors.white, size: 26), + ), + const SizedBox(width: 14), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Text( + clinicName, + style: const TextStyle( + color: Colors.white, + fontSize: 18, + fontWeight: FontWeight.w700, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + const SizedBox(height: 3), + _StatusBadge(status: conn.status), + ], + ), + ), + ], + ), + ), + ), + ), + ), + ), + SliverToBoxAdapter( + child: FutureBuilder( + future: _future, + builder: (ctx, snap) { + if (snap.connectionState == ConnectionState.waiting) { + return const Padding( + padding: EdgeInsets.symmetric(vertical: 80), + child: Center(child: CircularProgressIndicator(color: AppColors.accent)), + ); + } + if (snap.hasError) { + return Padding( + padding: const EdgeInsets.all(32), + child: Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const Icon(Icons.wifi_off_rounded, color: AppColors.cancelled, size: 40), + const SizedBox(height: 12), + Text('Hata: ${snap.error}', style: const TextStyle(color: AppColors.textSecondary), textAlign: TextAlign.center), + const SizedBox(height: 12), + FilledButton.icon( + onPressed: _load, + icon: const Icon(Icons.refresh_rounded, size: 16), + label: const Text('Tekrar Dene'), + ), + ], + ), + ), + ); + } + + final stats = snap.data!; + return _StatsBody(stats: stats); + }, + ), + ), + ], + ), + ); + } +} + +// ── Stats body ──────────────────────────────────────────────────────────────── + +class _StatsBody extends StatelessWidget { + const _StatsBody({required this.stats}); + final ConnectionStats stats; + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // KPI row + Row( + children: [ + Expanded(child: _KpiCard(label: 'Toplam İş', value: '${stats.totalJobs}', icon: Icons.work_outline_rounded, color: AppColors.accent)), + const SizedBox(width: 10), + Expanded(child: _KpiCard(label: 'Aktif', value: '${stats.activeJobs}', icon: Icons.pending_outlined, color: AppColors.inProgress)), + const SizedBox(width: 10), + Expanded(child: _KpiCard(label: 'Teslim', value: '${stats.deliveredJobs}', icon: Icons.check_circle_outline, color: AppColors.success)), + ], + ), + const SizedBox(height: 10), + Row( + children: [ + Expanded(child: _KpiCard(label: 'Bu Ay', value: '${stats.thisMonthJobs}', icon: Icons.calendar_month_outlined, color: AppColors.accent)), + const SizedBox(width: 10), + Expanded(child: _KpiCard(label: 'Geçen Ay', value: '${stats.lastMonthJobs}', icon: Icons.history_rounded, color: AppColors.textSecondary)), + const SizedBox(width: 10), + Expanded(child: _KpiCard(label: 'İptal', value: '${stats.cancelledJobs}', icon: Icons.cancel_outlined, color: AppColors.cancelled)), + ], + ), + const SizedBox(height: 16), + + // Revenue + _SectionTitle('Finans'), + const SizedBox(height: 8), + _RevenueCard(stats: stats), + const SizedBox(height: 16), + + // Prosthetic type breakdown + if (stats.byType.isNotEmpty) ...[ + _SectionTitle('Ürün Tipi Dağılımı'), + const SizedBox(height: 8), + _TypeBreakdownCard(byType: stats.byType, total: stats.totalJobs), + const SizedBox(height: 16), + ], + + // Monthly trend + _SectionTitle('Aylık Karşılaştırma'), + const SizedBox(height: 8), + _MonthCompareCard(stats: stats), + const SizedBox(height: 16), + + // Recent jobs + if (stats.recentJobs.isNotEmpty) ...[ + _SectionTitle('Son İşler'), + const SizedBox(height: 8), + _RecentJobsCard(jobs: stats.recentJobs), + const SizedBox(height: 16), + ], + + const SizedBox(height: 32), + ], + ), + ); + } +} + +// ── KPI card ────────────────────────────────────────────────────────────────── + +class _KpiCard extends StatelessWidget { + const _KpiCard({required this.label, required this.value, required this.icon, required this.color}); + final String label; + final String value; + final IconData icon; + final Color color; + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.all(14), + decoration: BoxDecoration( + color: AppColors.surface, + borderRadius: BorderRadius.circular(14), + border: Border.all(color: AppColors.border), + boxShadow: [BoxShadow(color: Colors.black.withValues(alpha: 0.03), blurRadius: 6, offset: const Offset(0, 2))], + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Icon(icon, size: 18, color: color), + const SizedBox(height: 8), + Text(value, style: TextStyle(fontSize: 22, fontWeight: FontWeight.w800, color: color)), + const SizedBox(height: 2), + Text(label, style: const TextStyle(fontSize: 11, color: AppColors.textMuted, fontWeight: FontWeight.w500)), + ], + ), + ); + } +} + +// ── Revenue card ────────────────────────────────────────────────────────────── + +class _RevenueCard extends StatelessWidget { + const _RevenueCard({required this.stats}); + final ConnectionStats stats; + + @override + Widget build(BuildContext context) { + final collected = stats.totalRevenue - stats.pendingRevenue; + return Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: AppColors.surface, + borderRadius: BorderRadius.circular(14), + border: Border.all(color: AppColors.border), + ), + child: Column( + children: [ + _RevRow( + label: 'Toplam Gelir', + value: _fmt(stats.totalRevenue), + color: AppColors.textPrimary, + bold: true, + ), + const Divider(height: 20), + _RevRow(label: 'Tahsil Edilen', value: _fmt(collected), color: AppColors.success), + const SizedBox(height: 8), + _RevRow(label: 'Bekleyen Alacak', value: _fmt(stats.pendingRevenue), color: AppColors.pending), + ], + ), + ); + } + + String _fmt(double v) => '${v.toStringAsFixed(0)} TL'; +} + +class _RevRow extends StatelessWidget { + const _RevRow({required this.label, required this.value, required this.color, this.bold = false}); + final String label; + final String value; + final Color color; + final bool bold; + + @override + Widget build(BuildContext context) { + final style = TextStyle( + fontSize: bold ? 15 : 14, + fontWeight: bold ? FontWeight.w700 : FontWeight.w500, + color: color, + ); + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(label, style: style.copyWith(color: bold ? AppColors.textPrimary : AppColors.textSecondary)), + Text(value, style: style), + ], + ); + } +} + +// ── Type breakdown ──────────────────────────────────────────────────────────── + +class _TypeBreakdownCard extends StatelessWidget { + const _TypeBreakdownCard({required this.byType, required this.total}); + final Map byType; + final int total; + + @override + Widget build(BuildContext context) { + final sorted = byType.entries.toList()..sort((a, b) => b.value.compareTo(a.value)); + + return Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: AppColors.surface, + borderRadius: BorderRadius.circular(14), + border: Border.all(color: AppColors.border), + ), + child: Column( + children: [ + for (int i = 0; i < sorted.length; i++) ...[ + if (i > 0) const SizedBox(height: 10), + _TypeBar( + label: _typeLabel(sorted[i].key), + count: sorted[i].value, + total: total, + ), + ], + ], + ), + ); + } + + String _typeLabel(String key) => switch (key) { + 'metal_porselen' => 'Metal Porselen', + 'zirkonyum' => 'Zirkonyum', + 'implant_ustu_zirkonyum'=> 'İmplant Üstü Zirkonyum', + 'gecici' => 'Geçici', + 'e_max' => 'E-Max', + 'tam_protez' => 'Tam Protez', + 'parsiyel' => 'Parsiyel Protez', + _ => 'Diğer', + }; +} + +class _TypeBar extends StatelessWidget { + const _TypeBar({required this.label, required this.count, required this.total}); + final String label; + final int count; + final int total; + + @override + Widget build(BuildContext context) { + final pct = total > 0 ? count / total : 0.0; + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(label, style: const TextStyle(fontSize: 13, fontWeight: FontWeight.w500, color: AppColors.textPrimary)), + Text('$count adet · ${(pct * 100).toStringAsFixed(0)}%', + style: const TextStyle(fontSize: 12, color: AppColors.textMuted)), + ], + ), + const SizedBox(height: 5), + ClipRRect( + borderRadius: BorderRadius.circular(4), + child: LinearProgressIndicator( + value: pct, + minHeight: 7, + backgroundColor: AppColors.border, + valueColor: const AlwaysStoppedAnimation(AppColors.accent), + ), + ), + ], + ); + } +} + +// ── Monthly compare ─────────────────────────────────────────────────────────── + +class _MonthCompareCard extends StatelessWidget { + const _MonthCompareCard({required this.stats}); + final ConnectionStats stats; + + @override + Widget build(BuildContext context) { + final thisMonth = stats.thisMonthJobs; + final lastMonth = stats.lastMonthJobs; + final maxBar = (thisMonth > lastMonth ? thisMonth : lastMonth).clamp(1, 999); + final trend = lastMonth == 0 + ? null + : ((thisMonth - lastMonth) / lastMonth * 100).toStringAsFixed(0); + final isUp = thisMonth >= lastMonth; + + return Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: AppColors.surface, + borderRadius: BorderRadius.circular(14), + border: Border.all(color: AppColors.border), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (trend != null) ...[ + Row( + children: [ + Icon( + isUp ? Icons.trending_up_rounded : Icons.trending_down_rounded, + size: 18, + color: isUp ? AppColors.success : AppColors.cancelled, + ), + const SizedBox(width: 6), + Text( + isUp ? '+$trend% geçen aya göre' : '$trend% geçen aya göre', + style: TextStyle( + fontSize: 13, + fontWeight: FontWeight.w600, + color: isUp ? AppColors.success : AppColors.cancelled, + ), + ), + ], + ), + const SizedBox(height: 12), + ], + Row( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + _BarColumn(label: 'Geçen Ay', count: lastMonth, maxCount: maxBar, color: AppColors.border), + const SizedBox(width: 16), + _BarColumn(label: 'Bu Ay', count: thisMonth, maxCount: maxBar, color: AppColors.accent), + ], + ), + ], + ), + ); + } +} + +class _BarColumn extends StatelessWidget { + const _BarColumn({required this.label, required this.count, required this.maxCount, required this.color}); + final String label; + final int count; + final int maxCount; + final Color color; + + @override + Widget build(BuildContext context) { + final height = maxCount > 0 ? (count / maxCount * 80).clamp(4.0, 80.0) : 4.0; + return Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text('$count', style: TextStyle(fontSize: 16, fontWeight: FontWeight.w700, color: color == AppColors.border ? AppColors.textSecondary : AppColors.accent)), + const SizedBox(height: 4), + Container( + width: 40, + height: height, + decoration: BoxDecoration( + color: color, + borderRadius: BorderRadius.circular(4), + ), + ), + const SizedBox(height: 4), + Text(label, style: const TextStyle(fontSize: 11, color: AppColors.textMuted)), + ], + ); + } +} + +// ── Recent jobs ─────────────────────────────────────────────────────────────── + +class _RecentJobsCard extends StatelessWidget { + const _RecentJobsCard({required this.jobs}); + final List jobs; + + @override + Widget build(BuildContext context) { + return Container( + decoration: BoxDecoration( + color: AppColors.surface, + borderRadius: BorderRadius.circular(14), + border: Border.all(color: AppColors.border), + ), + child: Column( + children: [ + for (int i = 0; i < jobs.length; i++) ...[ + if (i > 0) const Divider(height: 1), + _RecentJobRow(job: jobs[i]), + ], + ], + ), + ); + } +} + +class _RecentJobRow extends StatelessWidget { + const _RecentJobRow({required this.job}); + final Job job; + + @override + Widget build(BuildContext context) { + final color = job.status == JobStatus.delivered ? AppColors.success : AppColors.inProgress; + final bg = job.status == JobStatus.delivered ? AppColors.successBg : AppColors.inProgressBg; + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 11), + child: Row( + children: [ + Container( + width: 36, + height: 36, + decoration: BoxDecoration(color: bg, borderRadius: BorderRadius.circular(10)), + child: Center(child: Icon(Icons.assignment_outlined, size: 18, color: color)), + ), + const SizedBox(width: 10), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(job.patientCode, style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w600, color: AppColors.textPrimary)), + Text(job.prostheticType.label, style: const TextStyle(fontSize: 12, color: AppColors.textSecondary)), + ], + ), + ), + Container( + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3), + decoration: BoxDecoration(color: bg, borderRadius: BorderRadius.circular(6)), + child: Text(job.status.label, style: TextStyle(fontSize: 11, fontWeight: FontWeight.w600, color: color)), + ), + ], + ), + ); + } +} + +// ── Helpers ─────────────────────────────────────────────────────────────────── + +class _SectionTitle extends StatelessWidget { + const _SectionTitle(this.text); + final String text; + + @override + Widget build(BuildContext context) { + return Text(text, style: const TextStyle(fontSize: 13, fontWeight: FontWeight.w700, color: AppColors.textMuted, letterSpacing: 0.5)); + } +} + +class _StatusBadge extends StatelessWidget { + const _StatusBadge({required this.status}); + final ConnectionStatus status; + + @override + Widget build(BuildContext context) { + final (label, color) = switch (status) { + ConnectionStatus.approved => ('Onaylı', AppColors.success), + ConnectionStatus.pending => ('Bekliyor', AppColors.pending), + ConnectionStatus.rejected => ('Reddedildi', AppColors.cancelled), + }; + return Container( + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3), + decoration: BoxDecoration( + color: Colors.white.withValues(alpha: 0.15), + borderRadius: BorderRadius.circular(6), + ), + child: Text(label, style: TextStyle(fontSize: 11, fontWeight: FontWeight.w600, color: color)), + ); + } +} diff --git a/lib/features/lab/connections/connection_stats_repository.dart b/lib/features/lab/connections/connection_stats_repository.dart new file mode 100644 index 0000000..9037cb1 --- /dev/null +++ b/lib/features/lab/connections/connection_stats_repository.dart @@ -0,0 +1,124 @@ +import 'package:pocketbase/pocketbase.dart'; +import '../../../core/api/pocketbase_client.dart'; +import '../../../models/job.dart'; + +class ConnectionStats { + const ConnectionStats({ + required this.totalJobs, + required this.byStatus, + required this.byType, + required this.totalRevenue, + required this.pendingRevenue, + required this.thisMonthJobs, + required this.lastMonthJobs, + required this.revisionCount, + required this.recentJobs, + }); + + final int totalJobs; + final Map byStatus; // 'in_progress', 'sent', 'delivered', 'cancelled' + final Map byType; // prosthetic_type -> count + final double totalRevenue; + final double pendingRevenue; + final int thisMonthJobs; + final int lastMonthJobs; + final int revisionCount; + final List recentJobs; + + int get deliveredJobs => byStatus['delivered'] ?? 0; + int get activeJobs => (byStatus['in_progress'] ?? 0) + (byStatus['sent'] ?? 0); + int get cancelledJobs => byStatus['cancelled'] ?? 0; + double get revisionRate => totalJobs > 0 ? revisionCount / totalJobs * 100 : 0; + double get completionRate => totalJobs > 0 ? deliveredJobs / totalJobs * 100 : 0; +} + +class ConnectionStatsRepository { + ConnectionStatsRepository._(); + static final instance = ConnectionStatsRepository._(); + + PocketBase get _pb => PocketBaseClient.instance.pb; + + Future fetchStats({ + required String labTenantId, + required String clinicTenantId, + }) async { + final now = DateTime.now(); + final thisMonthStart = DateTime(now.year, now.month, 1); + final lastMonthStart = DateTime(now.year, now.month - 1, 1); + final lastMonthEnd = thisMonthStart.subtract(const Duration(seconds: 1)); + + final filter = 'lab_tenant_id = "$labTenantId" && clinic_tenant_id = "$clinicTenantId"'; + + final results = await Future.wait([ + _pb.collection('jobs').getList( + filter: filter, + perPage: 500, + expand: 'clinic_tenant_id,lab_tenant_id', + ), + _pb.collection('finance_entries').getList( + filter: 'tenant_id = "$labTenantId" && job_id.clinic_tenant_id = "$clinicTenantId"', + perPage: 500, + ), + ]); + + final jobsResult = results[0]; + final financeResult = results[1]; + + final allJobs = jobsResult.items + .map((r) => Job.fromJson(r.toJson())) + .toList(); + + // Status breakdown + final byStatus = {}; + final byType = {}; + int thisMonthJobs = 0; + int lastMonthJobs = 0; + int revisionCount = 0; + + for (final job in allJobs) { + // Status + final s = job.status.value; + byStatus[s] = (byStatus[s] ?? 0) + 1; + + // Type + final t = job.prostheticType.value; + byType[t] = (byType[t] ?? 0) + 1; + + // Monthly + final created = job.dateCreated; + if (created.isAfter(thisMonthStart)) thisMonthJobs++; + else if (created.isAfter(lastMonthStart) && created.isBefore(lastMonthEnd)) lastMonthJobs++; + + // Revision + if (job.status == JobStatus.inProgress && job.currentStep == null) revisionCount++; + } + + // Finance + double totalRevenue = 0; + double pendingRevenue = 0; + for (final r in financeResult.items) { + final j = r.toJson(); + final amount = (j['amount'] as num?)?.toDouble() ?? 0; + totalRevenue += amount; + if (j['status'] == 'pending') pendingRevenue += amount; + } + + // Recent jobs (last 5 delivered or sent) + final recent = allJobs + .where((j) => j.status == JobStatus.delivered || j.status == JobStatus.sent) + .toList() + ..sort((a, b) => b.dateCreated.compareTo(a.dateCreated)); + + return ConnectionStats( + totalJobs: allJobs.length, + byStatus: byStatus, + byType: byType, + totalRevenue: totalRevenue, + pendingRevenue: pendingRevenue, + thisMonthJobs: thisMonthJobs, + lastMonthJobs: lastMonthJobs, + revisionCount: revisionCount, + recentJobs: recent.take(5).toList(), + ); + } +} diff --git a/lib/features/lab/connections/lab_connections_repository.dart b/lib/features/lab/connections/lab_connections_repository.dart new file mode 100644 index 0000000..496d253 --- /dev/null +++ b/lib/features/lab/connections/lab_connections_repository.dart @@ -0,0 +1,30 @@ +import 'package:pocketbase/pocketbase.dart'; +import '../../../core/api/pocketbase_client.dart'; +import '../../../models/connection.dart'; + +class LabConnectionsRepository { + LabConnectionsRepository._(); + static final instance = LabConnectionsRepository._(); + + PocketBase get _pb => PocketBaseClient.instance.pb; + + Future> listConnections(String labTenantId) async { + final result = await _pb.collection('connections').getList( + filter: 'lab_tenant_id = "$labTenantId"', + expand: 'clinic_tenant_id,lab_tenant_id', + perPage: 100, + ); + return (result.items.map((r) => Connection.fromJson(r.toJson())).toList() + ..sort((a, b) => (b.dateCreated ?? '').compareTo(a.dateCreated ?? ''))); + } + + Future respondToRequest({ + required String connectionId, + required bool approve, + }) async { + final record = await _pb.collection('connections').update(connectionId, body: { + 'status': approve ? 'approved' : 'rejected', + }); + return Connection.fromJson(record.toJson()); + } +} diff --git a/lib/features/lab/connections/lab_connections_screen.dart b/lib/features/lab/connections/lab_connections_screen.dart new file mode 100644 index 0000000..4edc804 --- /dev/null +++ b/lib/features/lab/connections/lab_connections_screen.dart @@ -0,0 +1,453 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import '../../../core/providers/auth_provider.dart'; +import '../../../core/theme/app_theme.dart'; +import '../../../core/widgets/gradient_app_bar.dart'; +import '../../../models/connection.dart'; +import 'connection_detail_screen.dart'; +import 'lab_connections_repository.dart'; + +class LabConnectionsScreen extends ConsumerStatefulWidget { + const LabConnectionsScreen({super.key}); + + @override + ConsumerState createState() => + _LabConnectionsScreenState(); +} + +class _LabConnectionsScreenState extends ConsumerState { + late Future> _future; + final _searchController = TextEditingController(); + String _searchQuery = ''; + + @override + void initState() { + super.initState(); + _load(); + } + + @override + void dispose() { + _searchController.dispose(); + super.dispose(); + } + + void _load() { + final tenantId = ref.read(authProvider).activeTenant!.tenant.id; + setState(() { + _future = LabConnectionsRepository.instance.listConnections(tenantId); + }); + } + + Future _respond(String connectionId, bool approve) async { + try { + await LabConnectionsRepository.instance.respondToRequest( + connectionId: connectionId, + approve: approve, + ); + _load(); + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text( + approve ? 'Bağlantı onaylandı' : 'Bağlantı reddedildi'), + ), + ); + } + } catch (e) { + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('Hata: $e')), + ); + } + } + } + + Color _statusColor(ConnectionStatus status) { + return switch (status) { + ConnectionStatus.pending => AppColors.pending, + ConnectionStatus.approved => AppColors.success, + ConnectionStatus.rejected => AppColors.cancelled, + }; + } + + Color _statusBg(ConnectionStatus status) { + return switch (status) { + ConnectionStatus.pending => AppColors.pendingBg, + ConnectionStatus.approved => AppColors.successBg, + ConnectionStatus.rejected => AppColors.cancelledBg, + }; + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: AppColors.background, + appBar: GradientAppBar( + title: 'Bağlantılar', + category: 'LABORATUVAR', + searchController: _searchController, + onSearchChanged: (v) => setState(() => _searchQuery = v), + searchHint: 'Klinik adı ara...', + ), + body: RefreshIndicator( + color: AppColors.accent, + onRefresh: () async => _load(), + child: FutureBuilder>( + future: _future, + builder: (ctx, snap) { + if (snap.connectionState == ConnectionState.waiting) { + return const Center( + child: CircularProgressIndicator(color: AppColors.accent)); + } + if (snap.hasError) { + return Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + width: 64, + height: 64, + decoration: BoxDecoration( + color: AppColors.cancelledBg, + borderRadius: BorderRadius.circular(16)), + child: const Icon(Icons.wifi_off_rounded, + color: AppColors.cancelled, size: 30), + ), + const SizedBox(height: 16), + Text('Hata: ${snap.error}', + style: + const TextStyle(color: AppColors.textSecondary)), + const SizedBox(height: 12), + FilledButton.icon( + onPressed: _load, + icon: const Icon(Icons.refresh_rounded, size: 18), + label: const Text('Tekrar Dene'), + ), + ], + ), + ); + } + + final allConnections = snap.data!; + final q = _searchQuery.toLowerCase().trim(); + final connections = q.isEmpty + ? allConnections + : allConnections.where((c) => + (c.clinicName ?? '').toLowerCase().contains(q)).toList(); + + if (allConnections.isEmpty) { + return Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + width: 72, + height: 72, + decoration: BoxDecoration( + color: AppColors.inProgressBg, + borderRadius: BorderRadius.circular(20)), + child: const Icon(Icons.link_off, + color: AppColors.inProgress, size: 32), + ), + const SizedBox(height: 16), + const Text( + 'Henüz bağlantı yok', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + color: AppColors.textPrimary), + ), + const SizedBox(height: 8), + const Text( + 'Kliniklerden gelen istekler burada görünür.', + style: TextStyle( + color: AppColors.textSecondary, fontSize: 13), + textAlign: TextAlign.center, + ), + ], + ), + ); + } + + final pending = connections + .where((c) => c.status == ConnectionStatus.pending) + .toList(); + final others = connections + .where((c) => c.status != ConnectionStatus.pending) + .toList(); + + return ListView( + padding: const EdgeInsets.fromLTRB(16, 12, 16, 24), + children: [ + if (pending.isNotEmpty) ...[ + _SectionHeader( + label: 'Bekleyen İstekler', + count: pending.length, + countColor: AppColors.pending, + countBg: AppColors.pendingBg, + ), + const SizedBox(height: 8), + ...pending.map((c) => Padding( + padding: const EdgeInsets.only(bottom: 10), + child: _ConnectionCard( + connection: c, + statusColor: _statusColor(c.status), + statusBg: _statusBg(c.status), + onApprove: () => _respond(c.id, true), + onReject: () => _respond(c.id, false), + ), + )), + const SizedBox(height: 8), + ], + if (others.isNotEmpty) ...[ + _SectionHeader( + label: 'Bağlantılar', + count: others.length, + countColor: AppColors.textSecondary, + countBg: AppColors.surfaceVariant, + ), + const SizedBox(height: 8), + ...others.map((c) { + final tenantId = ref.read(authProvider).activeTenant!.tenant.id; + return Padding( + padding: const EdgeInsets.only(bottom: 10), + child: _ConnectionCard( + connection: c, + statusColor: _statusColor(c.status), + statusBg: _statusBg(c.status), + onTap: c.status == ConnectionStatus.approved + ? () => Navigator.push( + context, + MaterialPageRoute( + builder: (_) => ConnectionDetailScreen( + connection: c, + labTenantId: tenantId, + ), + ), + ) + : null, + ), + ); + }), + ], + ], + ); + }, + ), + ), + ); + } +} + +class _SectionHeader extends StatelessWidget { + const _SectionHeader({ + required this.label, + required this.count, + required this.countColor, + required this.countBg, + }); + final String label; + final int count; + final Color countColor; + final Color countBg; + + @override + Widget build(BuildContext context) { + return Row( + children: [ + Text( + label, + style: const TextStyle( + fontSize: 13, + fontWeight: FontWeight.w600, + color: AppColors.accent, + letterSpacing: 0.3, + ), + ), + const SizedBox(width: 8), + Container( + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2), + decoration: BoxDecoration( + color: countBg, + borderRadius: BorderRadius.circular(10), + ), + child: Text( + '$count', + style: TextStyle( + color: countColor, + fontSize: 12, + fontWeight: FontWeight.w600, + ), + ), + ), + ], + ); + } +} + +class _ConnectionCard extends StatefulWidget { + const _ConnectionCard({ + required this.connection, + required this.statusColor, + required this.statusBg, + this.onApprove, + this.onReject, + this.onTap, + }); + final Connection connection; + final Color statusColor; + final Color statusBg; + final VoidCallback? onApprove; + final VoidCallback? onReject; + final VoidCallback? onTap; + + @override + State<_ConnectionCard> createState() => _ConnectionCardState(); +} + +class _ConnectionCardState extends State<_ConnectionCard> { + bool _loading = false; + + Future _act(VoidCallback? cb) async { + if (cb == null) return; + setState(() => _loading = true); + cb(); + await Future.delayed(const Duration(milliseconds: 500)); + if (mounted) setState(() => _loading = false); + } + + String _formatDate(String? raw) { + if (raw == null) return ''; + try { + final dt = DateTime.parse(raw); + return '${dt.day.toString().padLeft(2, '0')}.${dt.month.toString().padLeft(2, '0')}.${dt.year}'; + } catch (_) { + return ''; + } + } + + @override + Widget build(BuildContext context) { + final c = widget.connection; + final isPending = c.status == ConnectionStatus.pending; + + return Material( + color: AppColors.surface, + borderRadius: BorderRadius.circular(14), + child: InkWell( + onTap: widget.onTap, + borderRadius: BorderRadius.circular(14), + child: Container( + padding: const EdgeInsets.all(14), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(14), + border: Border.all(color: AppColors.border), + boxShadow: [ + BoxShadow( + color: Colors.black.withValues(alpha: 0.04), + blurRadius: 8, + offset: const Offset(0, 2)) + ], + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Container( + width: 44, + height: 44, + decoration: BoxDecoration( + color: widget.statusBg, + borderRadius: BorderRadius.circular(12)), + child: Icon(Icons.business_outlined, + color: widget.statusColor, size: 22), + ), + const SizedBox(width: 12), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + c.clinicName ?? 'Klinik', + style: const TextStyle( + fontSize: 15, + fontWeight: FontWeight.w600, + color: AppColors.textPrimary), + ), + if (c.dateCreated != null) ...[ + const SizedBox(height: 2), + Text( + _formatDate(c.dateCreated), + style: const TextStyle( + color: AppColors.textMuted, fontSize: 12), + ), + ], + ], + ), + ), + Container( + padding: + const EdgeInsets.symmetric(horizontal: 10, vertical: 4), + decoration: BoxDecoration( + color: widget.statusBg, + borderRadius: BorderRadius.circular(8), + ), + child: Text( + c.status.label, + style: TextStyle( + color: widget.statusColor, + fontWeight: FontWeight.w600, + fontSize: 12, + ), + ), + ), + if (widget.onTap != null) ...[ + const SizedBox(width: 4), + const Icon(Icons.chevron_right_rounded, + size: 18, color: AppColors.textMuted), + ], + ], + ), + if (isPending) ...[ + const SizedBox(height: 12), + Row( + children: [ + Expanded( + child: OutlinedButton( + onPressed: + _loading ? null : () => _act(widget.onReject), + style: OutlinedButton.styleFrom( + foregroundColor: AppColors.cancelled, + side: const BorderSide(color: AppColors.cancelled)), + child: const Text('Reddet'), + ), + ), + const SizedBox(width: 8), + Expanded( + child: FilledButton( + onPressed: + _loading ? null : () => _act(widget.onApprove), + style: FilledButton.styleFrom( + backgroundColor: AppColors.success), + child: _loading + ? const SizedBox( + width: 16, + height: 16, + child: CircularProgressIndicator( + strokeWidth: 2, color: Colors.white), + ) + : const Text('Onayla'), + ), + ), + ], + ), + ], + ], + ), + ), + ), + ); + } +} diff --git a/lib/features/lab/dashboard/lab_dashboard_screen.dart b/lib/features/lab/dashboard/lab_dashboard_screen.dart new file mode 100644 index 0000000..16e390d --- /dev/null +++ b/lib/features/lab/dashboard/lab_dashboard_screen.dart @@ -0,0 +1,883 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_animate/flutter_animate.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:go_router/go_router.dart'; +import '../../../core/providers/auth_provider.dart'; +import '../../../core/router/app_router.dart'; +import '../../../core/theme/app_theme.dart'; +import '../../../core/widgets/tooth_logo.dart'; +import '../../../core/services/realtime_service.dart'; +import '../../../models/job.dart'; +import '../jobs/lab_jobs_repository.dart'; + +class LabDashboardScreen extends ConsumerStatefulWidget { + const LabDashboardScreen({super.key}); + @override + ConsumerState createState() => _LabDashboardScreenState(); +} + +class _LabDashboardScreenState extends ConsumerState { + late Future<_DashboardData> _future; + bool _acceptingAll = false; + late UnsubFn _unsub; + + @override + void initState() { + super.initState(); + _load(); + final tenantId = ref.read(authProvider).activeTenant!.tenant.id; + _unsub = RealtimeService.instance.watch( + 'jobs', + filter: "lab_tenant_id='$tenantId'", + onEvent: (_) { if (mounted) _load(); }, + ); + } + + @override + void dispose() { + _unsub(); + super.dispose(); + } + + void _load() { + final tenantId = ref.read(authProvider).activeTenant!.tenant.id; + final now = DateTime.now(); + final thisMonthStart = DateTime(now.year, now.month, 1); + final lastMonthStart = DateTime(now.year, now.month - 1, 1); + setState(() { + _future = Future.wait([ + Future.wait>([ + LabJobsRepository.instance.listInbound(tenantId, status: 'pending'), + LabJobsRepository.instance.listInProgress(tenantId), + LabJobsRepository.instance.listInProgress(tenantId, location: 'at_lab'), + LabJobsRepository.instance.listInProgress(tenantId, location: 'at_clinic'), + LabJobsRepository.instance.listInbound(tenantId, status: 'sent', limit: 200), + LabJobsRepository.instance.listInbound(tenantId, status: 'delivered', limit: 200), + ]), + LabJobsRepository.instance.countDelivered(tenantId, from: thisMonthStart), + LabJobsRepository.instance.countDelivered(tenantId, from: lastMonthStart, to: thisMonthStart), + ]).then((r) { + final jobs = r[0] as List>; + return _DashboardData( + pendingJobs: jobs[0], + inProgressJobs: jobs[1], + atLabJobs: jobs[2], + atClinicJobs: jobs[3], + sentCount: jobs[4].length, + deliveredCount: jobs[5].length, + thisMonthDelivered: r[1] as int, + lastMonthDelivered: r[2] as int, + ); + }); + }); + } + + Future _bulkAccept() async { + final tenantId = ref.read(authProvider).activeTenant!.tenant.id; + setState(() => _acceptingAll = true); + try { + await LabJobsRepository.instance.bulkAcceptPending(tenantId); + _load(); + } catch (e) { + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('Hata: $e'), behavior: SnackBarBehavior.floating), + ); + } + } finally { + if (mounted) setState(() => _acceptingAll = false); + } + } + + @override + Widget build(BuildContext context) { + final companyName = ref.watch(authProvider).activeTenant?.tenant.companyName ?? ''; + return Scaffold( + backgroundColor: AppColors.background, + body: LayoutBuilder( + builder: (context, constraints) { + const maxContent = 1040.0; + final hPad = constraints.maxWidth > maxContent + ? (constraints.maxWidth - maxContent) / 2 + : 16.0; + + return RefreshIndicator( + color: AppColors.accent, + onRefresh: () async => _load(), + child: FutureBuilder<_DashboardData>( + future: _future, + builder: (ctx, snap) { + if (snap.connectionState == ConnectionState.waiting) { + return _DashboardSkeleton(companyName: companyName, hPad: hPad); + } + if (snap.hasError) return _ErrorBody(onRetry: _load); + final data = snap.data!; + final isDesktop = MediaQuery.sizeOf(ctx).width > AppLayout.sidebarBreakpoint; + return CustomScrollView( + slivers: [ + _DashboardHeader(companyName: companyName), + if (isDesktop) + SliverPadding( + padding: const EdgeInsets.fromLTRB(16, 16, 16, 0), + sliver: SliverToBoxAdapter( + child: _StatsRow( + pending: data.pendingJobs.length, + inProgress: data.inProgressJobs.length, + sent: data.sentCount, + delivered: data.deliveredCount, + ), + ), + ), + if (data.pendingJobs.isNotEmpty) + SliverPadding( + padding: const EdgeInsets.fromLTRB(16, 4, 16, 0), + sliver: SliverToBoxAdapter( + child: _AcceptAllBanner( + count: data.pendingJobs.length, + loading: _acceptingAll, + onTap: _bulkAccept, + ).animate().fadeIn(duration: 300.ms).slideY(begin: 0.1, end: 0), + ), + ), + if (isDesktop) ...[ + SliverPadding( + padding: const EdgeInsets.fromLTRB(16, 12, 16, 0), + sliver: SliverToBoxAdapter( + child: _MonthlyReportSection(data: data) + .animate().fadeIn(duration: 300.ms).slideY(begin: 0.08, end: 0), + ), + ), + SliverPadding( + padding: const EdgeInsets.fromLTRB(16, 8, 16, 0), + sliver: SliverToBoxAdapter( + child: _GamificationRow(data: data) + .animate().fadeIn(duration: 300.ms, delay: 60.ms).slideY(begin: 0.08, end: 0), + ), + ), + ], + // ── Yapılacaklar (at_lab) ──────────────────────────── + SliverPadding( + padding: const EdgeInsets.fromLTRB(16, 20, 16, 4), + sliver: SliverToBoxAdapter( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('Yapılacaklar', style: Theme.of(context).textTheme.titleMedium), + TextButton( + onPressed: () => context.go(routeLabJobsAll), + style: TextButton.styleFrom(foregroundColor: AppColors.accent, padding: const EdgeInsets.symmetric(horizontal: 8)), + child: const Text('Tümünü Gör'), + ), + ], + ), + ), + ), + if (data.atLabJobs.isEmpty) + const SliverToBoxAdapter( + child: Padding( + padding: EdgeInsets.fromLTRB(16, 4, 16, 0), + child: _EmptySection(message: 'Yapılacak iş yok'), + ), + ) + else + SliverPadding( + padding: const EdgeInsets.fromLTRB(16, 0, 16, 0), + sliver: SliverList.separated( + itemCount: data.atLabJobs.take(5).length, + separatorBuilder: (_, __) => const SizedBox(height: 10), + itemBuilder: (ctx, i) => _JobCard(job: data.atLabJobs[i]) + .animate(delay: (i * 60).ms) + .fadeIn(duration: 300.ms) + .slideY(begin: 0.12, end: 0), + ), + ), + // ── Klinikte Onay Bekliyor (at_clinic) ─────────────── + if (data.atClinicJobs.isNotEmpty) ...[ + SliverPadding( + padding: const EdgeInsets.fromLTRB(16, 20, 16, 4), + sliver: SliverToBoxAdapter( + child: Text('Klinikte Onay Bekliyor', style: Theme.of(context).textTheme.titleMedium), + ), + ), + SliverPadding( + padding: const EdgeInsets.fromLTRB(16, 0, 16, 0), + sliver: SliverList.separated( + itemCount: data.atClinicJobs.take(5).length, + separatorBuilder: (_, __) => const SizedBox(height: 10), + itemBuilder: (ctx, i) => _JobCard(job: data.atClinicJobs[i]) + .animate(delay: (i * 60).ms) + .fadeIn(duration: 300.ms) + .slideY(begin: 0.12, end: 0), + ), + ), + ], + const SliverToBoxAdapter(child: SizedBox(height: 24)), + ], + ); + }, + ), + ); + }, + ), + ); + } +} + +class _DashboardHeader extends StatelessWidget { + const _DashboardHeader({required this.companyName}); + final String companyName; + + // Must stay in sync with _DesktopSidebar.headerHeight in app_router.dart + static const double _desktopToolbarHeight = 64; + + @override + Widget build(BuildContext context) { + final isDesktop = MediaQuery.sizeOf(context).width > AppLayout.sidebarBreakpoint; + + if (isDesktop) { + return SliverAppBar( + pinned: true, + toolbarHeight: _desktopToolbarHeight, + backgroundColor: AppColors.surface, + surfaceTintColor: Colors.transparent, + elevation: 0, + scrolledUnderElevation: 0, + centerTitle: false, + automaticallyImplyLeading: false, + titleSpacing: 0, + title: Padding( + padding: const EdgeInsets.symmetric(horizontal: 24), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Text('Genel Bakış', style: TextStyle(fontSize: 11, color: AppColors.textSecondary.withValues(alpha: 0.8), letterSpacing: 0.3)), + const Text('Bugünkü Durum', style: TextStyle(fontSize: 17, fontWeight: FontWeight.w700, color: AppColors.textPrimary)), + ], + ), + ), + actions: [ + IconButton( + onPressed: () => context.go(routeLabSettings), + icon: const Icon(Icons.settings_outlined, color: AppColors.textSecondary, size: 22), + ), + const SizedBox(width: 8), + ], + ); + } + + return SliverAppBar( + pinned: true, + expandedHeight: 148, + backgroundColor: AppColors.primary, + surfaceTintColor: Colors.transparent, + shadowColor: Colors.transparent, + systemOverlayStyle: SystemUiOverlayStyle.light, + centerTitle: false, + leadingWidth: 60, + leading: Padding( + padding: const EdgeInsets.only(left: 16, top: 8, bottom: 8), + child: Container( + decoration: BoxDecoration( + color: Colors.white.withValues(alpha: 0.15), + borderRadius: BorderRadius.circular(10), + ), + child: const Center(child: ToothLogo(size: 20, color: Colors.white)), + ), + ), + titleSpacing: 8, + title: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Text('DLS', style: TextStyle(color: Colors.white.withValues(alpha: 0.65), fontSize: 11, fontWeight: FontWeight.w600, letterSpacing: 1.5)), + Text(companyName, style: const TextStyle(color: Colors.white, fontSize: 15, fontWeight: FontWeight.w700), maxLines: 1, overflow: TextOverflow.ellipsis), + ], + ), + actions: [ + IconButton( + onPressed: () => context.go(routeLabSettings), + icon: const Icon(Icons.settings_outlined, color: Colors.white, size: 22), + ), + ], + flexibleSpace: FlexibleSpaceBar( + collapseMode: CollapseMode.pin, + background: Container( + decoration: const BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topLeft, + end: Alignment.bottomRight, + colors: [AppColors.primary, AppColors.accent], + ), + ), + child: Padding( + padding: const EdgeInsets.fromLTRB(20, 0, 20, 24), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Text('Genel Bakış', style: TextStyle(color: Colors.white.withValues(alpha: 0.65), fontSize: 12, fontWeight: FontWeight.w500, letterSpacing: 0.5)), + const SizedBox(height: 4), + const Text('Bugünkü Durum', style: TextStyle(color: Colors.white, fontSize: 24, fontWeight: FontWeight.w800, letterSpacing: -0.5)), + ], + ), + ), + ), + ), + ); + } +} + +class _StatsRow extends StatelessWidget { + const _StatsRow({ + required this.pending, + required this.inProgress, + required this.sent, + required this.delivered, + }); + final int pending; + final int inProgress; + final int sent; + final int delivered; + + @override + Widget build(BuildContext context) { + final isWideDesktop = MediaQuery.sizeOf(context).width >= AppLayout.wideBreakpoint; + + final pendingCard = _StatCard(label: 'Bekleyen', value: '$pending', icon: Icons.hourglass_top_rounded, color: AppColors.pending, bgColor: AppColors.pendingBg) + .animate().fadeIn(duration: 350.ms).slideY(begin: 0.2, end: 0); + final inProgressCard = _StatCard(label: 'Devam Eden', value: '$inProgress', icon: Icons.autorenew_rounded, color: AppColors.inProgress, bgColor: AppColors.inProgressBg) + .animate(delay: 80.ms).fadeIn(duration: 350.ms).slideY(begin: 0.2, end: 0); + + if (isWideDesktop) { + final sentCard = _StatCard(label: 'Klinik\'te', value: '$sent', icon: Icons.local_hospital_outlined, color: AppColors.accent, bgColor: AppColors.inProgressBg) + .animate(delay: 120.ms).fadeIn(duration: 350.ms).slideY(begin: 0.2, end: 0); + final deliveredCard = _StatCard(label: 'Tamamlanan', value: '$delivered', icon: Icons.task_alt, color: AppColors.success, bgColor: AppColors.successBg) + .animate(delay: 160.ms).fadeIn(duration: 350.ms).slideY(begin: 0.2, end: 0); + + return Row( + children: [ + Expanded(child: pendingCard), + const SizedBox(width: 12), + Expanded(child: inProgressCard), + const SizedBox(width: 12), + Expanded(child: sentCard), + const SizedBox(width: 12), + Expanded(child: deliveredCard), + ], + ); + } + + return Row( + children: [ + Expanded(child: pendingCard), + const SizedBox(width: 12), + Expanded(child: inProgressCard), + ], + ); + } +} + +class _StatCard extends StatelessWidget { + const _StatCard({required this.label, required this.value, required this.icon, required this.color, required this.bgColor}); + final String label; + final String value; + final IconData icon; + final Color color; + final Color bgColor; + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: AppColors.surface, + borderRadius: BorderRadius.circular(16), + border: Border.all(color: AppColors.border), + boxShadow: [BoxShadow(color: Colors.black.withValues(alpha: 0.06), blurRadius: 12, offset: const Offset(0, 4))], + ), + child: Row( + children: [ + Container( + width: 44, height: 44, + decoration: BoxDecoration(color: bgColor, borderRadius: BorderRadius.circular(12)), + child: Icon(icon, color: color, size: 22), + ), + const SizedBox(width: 12), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(value, style: TextStyle(fontSize: 28, fontWeight: FontWeight.w800, color: color, height: 1)), + const SizedBox(height: 3), + Text(label, style: const TextStyle(fontSize: 12, color: AppColors.textSecondary, fontWeight: FontWeight.w500)), + ], + ), + ], + ), + ); + } +} + +class _AcceptAllBanner extends StatelessWidget { + const _AcceptAllBanner({required this.count, required this.loading, required this.onTap}); + final int count; + final bool loading; + final VoidCallback onTap; + @override + Widget build(BuildContext context) { + return Material( + color: AppColors.pendingBg, + borderRadius: BorderRadius.circular(14), + child: InkWell( + onTap: loading ? null : onTap, + borderRadius: BorderRadius.circular(14), + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14), + decoration: BoxDecoration(borderRadius: BorderRadius.circular(14), border: Border.all(color: AppColors.pending.withValues(alpha: 0.35))), + child: Row( + children: [ + Container( + width: 38, height: 38, + decoration: BoxDecoration(color: AppColors.pending.withValues(alpha: 0.15), borderRadius: BorderRadius.circular(10)), + child: const Icon(Icons.notifications_active_outlined, color: AppColors.pending, size: 18), + ), + const SizedBox(width: 12), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('$count yeni iş bekliyor', style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w600, color: AppColors.textPrimary)), + const Text('Tümünü hızlıca kabul et', style: TextStyle(fontSize: 12, color: AppColors.textSecondary)), + ], + ), + ), + loading + ? const SizedBox(width: 20, height: 20, child: CircularProgressIndicator(strokeWidth: 2, color: AppColors.pending)) + : Container( + padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 8), + decoration: BoxDecoration(color: AppColors.pending, borderRadius: BorderRadius.circular(8)), + child: const Text('Kabul Et', style: TextStyle(color: Colors.white, fontSize: 13, fontWeight: FontWeight.w600)), + ), + ], + ), + ), + ), + ); + } +} + +class _JobCard extends StatelessWidget { + const _JobCard({required this.job}); + final Job job; + @override + Widget build(BuildContext context) { + final due = job.dueDate; + final isOverdue = due != null && due.isBefore(DateTime.now()); + final dueText = due != null ? '${due.day.toString().padLeft(2, '0')}.${due.month.toString().padLeft(2, '0')}.${due.year}' : null; + return Semantics( + label: job.patientCode, + button: true, + excludeSemantics: true, + child: Material( + color: AppColors.surface, + borderRadius: BorderRadius.circular(14), + child: InkWell( + onTap: () => context.push('/lab/jobs/${job.id}'), + borderRadius: BorderRadius.circular(14), + child: Container( + padding: const EdgeInsets.all(14), + decoration: BoxDecoration(borderRadius: BorderRadius.circular(14), border: Border.all(color: AppColors.border)), + child: Row( + children: [ + Container( + width: 46, height: 46, + decoration: BoxDecoration(color: AppColors.inProgressBg, borderRadius: BorderRadius.circular(12)), + child: Center(child: Text('${job.memberCount}', style: const TextStyle(fontSize: 20, fontWeight: FontWeight.w800, color: AppColors.inProgress))), + ), + const SizedBox(width: 12), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(job.patientCode, style: const TextStyle(fontSize: 15, fontWeight: FontWeight.w600, color: AppColors.textPrimary)), + const SizedBox(height: 2), + Text(job.clinicName ?? 'Klinik', style: const TextStyle(fontSize: 13, color: AppColors.textSecondary)), + const SizedBox(height: 6), + Wrap( + spacing: 6, + children: [ + _Tag(label: job.prostheticType.label, color: AppColors.inProgress, bg: AppColors.inProgressBg), + if (job.currentStep != null) _Tag(label: job.currentStep!.label, color: AppColors.success, bg: AppColors.successBg), + ], + ), + ], + ), + ), + if (dueText != null) ...[ + const SizedBox(width: 8), + Column( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Icon(Icons.calendar_today_outlined, size: 13, color: isOverdue ? AppColors.cancelled : AppColors.textMuted), + const SizedBox(height: 3), + Text(dueText, style: TextStyle(fontSize: 12, fontWeight: FontWeight.w500, color: isOverdue ? AppColors.cancelled : AppColors.textSecondary)), + ], + ), + ], + ], + ), + ), + ), + ), + ); + } +} + +class _Tag extends StatelessWidget { + const _Tag({required this.label, required this.color, required this.bg}); + final String label; + final Color color; + final Color bg; + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3), + decoration: BoxDecoration(color: bg, borderRadius: BorderRadius.circular(6)), + child: Text(label, style: TextStyle(fontSize: 11, fontWeight: FontWeight.w600, color: color)), + ); + } +} + + +class _EmptySection extends StatelessWidget { + const _EmptySection({required this.message}); + final String message; + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 16), + decoration: BoxDecoration( + color: AppColors.surface, + borderRadius: BorderRadius.circular(12), + border: Border.all(color: AppColors.border), + ), + child: Row( + children: [ + Icon(Icons.check_circle_outline_rounded, color: AppColors.textSecondary.withValues(alpha: 0.5), size: 20), + const SizedBox(width: 10), + Text(message, style: TextStyle(fontSize: 14, color: AppColors.textSecondary)), + ], + ), + ); + } +} + +class _ErrorBody extends StatelessWidget { + const _ErrorBody({required this.onRetry}); + final VoidCallback onRetry; + @override + Widget build(BuildContext context) { + return Center( + child: Padding( + padding: const EdgeInsets.all(32), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + width: 64, height: 64, + decoration: BoxDecoration(color: AppColors.cancelledBg, borderRadius: BorderRadius.circular(16)), + child: const Icon(Icons.wifi_off_rounded, color: AppColors.cancelled, size: 30), + ), + const SizedBox(height: 16), + const Text('Bağlantı hatası', style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600, color: AppColors.textPrimary)), + const SizedBox(height: 12), + FilledButton.icon(onPressed: onRetry, icon: const Icon(Icons.refresh_rounded, size: 18), label: const Text('Tekrar Dene')), + ], + ), + ), + ); + } +} + +class _DashboardSkeleton extends StatelessWidget { + const _DashboardSkeleton({required this.companyName, required this.hPad}); + final String companyName; + final double hPad; + @override + Widget build(BuildContext context) { + return CustomScrollView( + physics: const NeverScrollableScrollPhysics(), + slivers: [ + _DashboardHeader(companyName: companyName), + SliverPadding( + padding: EdgeInsets.fromLTRB(hPad, 16, hPad, 0), + sliver: const SliverToBoxAdapter( + child: Row(children: [ + Expanded(child: _ShimmerBox(height: 84, radius: 16)), + SizedBox(width: 12), + Expanded(child: _ShimmerBox(height: 84, radius: 16)), + ]), + ), + ), + SliverPadding( + padding: EdgeInsets.fromLTRB(hPad, 8, hPad, 0), + sliver: SliverList.builder( + itemCount: 4, + itemBuilder: (_, i) => const Padding(padding: EdgeInsets.only(bottom: 10), child: _ShimmerBox(height: 92, radius: 14)), + ), + ), + ], + ); + } +} + +class _ShimmerBox extends StatefulWidget { + const _ShimmerBox({required this.height, required this.radius}); + final double height; + final double radius; + @override + State<_ShimmerBox> createState() => _ShimmerBoxState(); +} + +class _ShimmerBoxState extends State<_ShimmerBox> with SingleTickerProviderStateMixin { + late AnimationController _ctrl; + late Animation _anim; + @override + void initState() { + super.initState(); + _ctrl = AnimationController(vsync: this, duration: const Duration(milliseconds: 1100))..repeat(reverse: true); + _anim = CurvedAnimation(parent: _ctrl, curve: Curves.easeInOut); + } + @override + void dispose() { _ctrl.dispose(); super.dispose(); } + @override + Widget build(BuildContext context) { + return AnimatedBuilder( + animation: _anim, + builder: (_, __) => Container( + height: widget.height, + decoration: BoxDecoration(borderRadius: BorderRadius.circular(widget.radius), color: Color.lerp(const Color(0xFFE2E8F0), const Color(0xFFF1F5F9), _anim.value)), + ), + ); + } +} + +// ── Monthly Report ────────────────────────────────────────────────────────── + +class _MonthlyReportSection extends StatelessWidget { + const _MonthlyReportSection({required this.data}); + final _DashboardData data; + + @override + Widget build(BuildContext context) { + final pct = data.changePercent; + final isUp = pct >= 0; + final pctStr = '${isUp ? '+' : ''}${pct.toStringAsFixed(0)}%'; + return Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: AppColors.surface, + borderRadius: BorderRadius.circular(12), + border: Border.all(color: AppColors.border), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + const Icon(Icons.bar_chart_rounded, size: 18, color: AppColors.accent), + const SizedBox(width: 6), + Text('Aylık Rapor', style: Theme.of(context).textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w600)), + ], + ), + const SizedBox(height: 12), + Row( + children: [ + Expanded( + child: _MonthStat( + label: 'Bu Ay', + value: data.thisMonthDelivered, + highlighted: true, + ), + ), + const SizedBox(width: 12), + Expanded( + child: _MonthStat( + label: 'Geçen Ay', + value: data.lastMonthDelivered, + highlighted: false, + ), + ), + const SizedBox(width: 12), + Container( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6), + decoration: BoxDecoration( + color: isUp ? AppColors.successBg : AppColors.cancelledBg, + borderRadius: BorderRadius.circular(8), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon( + isUp ? Icons.trending_up_rounded : Icons.trending_down_rounded, + size: 16, + color: isUp ? AppColors.success : AppColors.cancelled, + ), + const SizedBox(width: 4), + Text( + pctStr, + style: TextStyle( + fontSize: 13, + fontWeight: FontWeight.w700, + color: isUp ? AppColors.success : AppColors.cancelled, + ), + ), + ], + ), + ), + ], + ), + ], + ), + ); + } +} + +class _MonthStat extends StatelessWidget { + const _MonthStat({required this.label, required this.value, required this.highlighted}); + final String label; + final int value; + final bool highlighted; + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10), + decoration: BoxDecoration( + color: highlighted ? AppColors.accent.withValues(alpha: 0.06) : AppColors.background, + borderRadius: BorderRadius.circular(8), + border: highlighted ? Border.all(color: AppColors.accent.withValues(alpha: 0.2)) : null, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(label, style: TextStyle(fontSize: 11, color: AppColors.textSecondary, fontWeight: FontWeight.w500)), + const SizedBox(height: 2), + Text( + '$value iş', + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.w700, + color: highlighted ? AppColors.accent : AppColors.textPrimary, + ), + ), + ], + ), + ); + } +} + +// ── Gamification Row ───────────────────────────────────────────────────────── + +const _monthlyGoal = 50; + +class _GamificationRow extends StatelessWidget { + const _GamificationRow({required this.data}); + final _DashboardData data; + + @override + Widget build(BuildContext context) { + final progress = (data.thisMonthDelivered / _monthlyGoal).clamp(0.0, 1.0); + final remaining = (_monthlyGoal - data.thisMonthDelivered).clamp(0, _monthlyGoal); + return Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: AppColors.surface, + borderRadius: BorderRadius.circular(12), + border: Border.all(color: AppColors.border), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + const Text('🏆', style: TextStyle(fontSize: 16)), + const SizedBox(width: 6), + Text('Aylık Hedef', style: Theme.of(context).textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w600)), + const Spacer(), + Container( + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3), + decoration: BoxDecoration( + color: AppColors.primary.withValues(alpha: 0.08), + borderRadius: BorderRadius.circular(6), + ), + child: Text( + '${data.points} puan', + style: const TextStyle(fontSize: 11, fontWeight: FontWeight.w700, color: AppColors.primary), + ), + ), + ], + ), + const SizedBox(height: 10), + ClipRRect( + borderRadius: BorderRadius.circular(6), + child: LinearProgressIndicator( + value: progress, + minHeight: 8, + backgroundColor: AppColors.background, + valueColor: AlwaysStoppedAnimation( + progress >= 1.0 ? AppColors.success : AppColors.accent, + ), + ), + ), + const SizedBox(height: 8), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + '${data.thisMonthDelivered} / $_monthlyGoal iş teslim edildi', + style: TextStyle(fontSize: 12, color: AppColors.textSecondary), + ), + Text( + progress >= 1.0 ? 'Hedef tamamlandı!' : '$remaining iş kaldı', + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w600, + color: progress >= 1.0 ? AppColors.success : AppColors.textSecondary, + ), + ), + ], + ), + ], + ), + ); + } +} + +// ── Data Model ─────────────────────────────────────────────────────────────── + +class _DashboardData { + final List pendingJobs; + final List inProgressJobs; + final List atLabJobs; + final List atClinicJobs; + final int sentCount; + final int deliveredCount; + final int thisMonthDelivered; + final int lastMonthDelivered; + const _DashboardData({ + required this.pendingJobs, + required this.inProgressJobs, + required this.atLabJobs, + required this.atClinicJobs, + required this.sentCount, + required this.deliveredCount, + required this.thisMonthDelivered, + required this.lastMonthDelivered, + }); + + int get points => thisMonthDelivered * 10; + double get changePercent => lastMonthDelivered == 0 + ? (thisMonthDelivered > 0 ? 100 : 0) + : (thisMonthDelivered - lastMonthDelivered) / lastMonthDelivered * 100; +} diff --git a/lib/features/lab/discounts/discount_repository.dart b/lib/features/lab/discounts/discount_repository.dart new file mode 100644 index 0000000..97fde40 --- /dev/null +++ b/lib/features/lab/discounts/discount_repository.dart @@ -0,0 +1,92 @@ +import 'package:pocketbase/pocketbase.dart'; +import '../../../core/api/pocketbase_client.dart'; +import '../../../models/clinic_discount.dart'; + +class DiscountRepository { + DiscountRepository._(); + static final instance = DiscountRepository._(); + + PocketBase get _pb => PocketBaseClient.instance.pb; + + Future> listDiscounts(String labTenantId) async { + final result = await _pb.collection('clinic_discounts').getList( + filter: 'lab_tenant_id = "$labTenantId"', + expand: 'clinic_tenant_id', + perPage: 200, + ); + final list = result.items + .map((r) => ClinicDiscount.fromJson(r.toJson())) + .toList(); + list.sort((a, b) { + // Active first, then by clinic name + if (a.isActive != b.isActive) return a.isActive ? -1 : 1; + final ca = a.clinicName ?? ''; + final cb = b.clinicName ?? ''; + return ca.compareTo(cb); + }); + return list; + } + + Future createDiscount({ + required String labTenantId, + String? clinicTenantId, + String? prostheticType, + required DiscountType discountType, + required double discountValue, + int minQuantity = 0, + bool isActive = true, + String? notes, + }) async { + final body = { + 'lab_tenant_id': labTenantId, + 'discount_type': discountType.value, + 'discount_value': discountValue, + 'is_active': isActive, + }; + if (clinicTenantId != null && clinicTenantId.isNotEmpty) { + body['clinic_tenant_id'] = clinicTenantId; + } + if (prostheticType != null && prostheticType.isNotEmpty) { + body['prosthetic_type'] = prostheticType; + } + if (minQuantity > 0) body['min_quantity'] = minQuantity; + if (notes != null && notes.isNotEmpty) body['notes'] = notes; + + final record = await _pb.collection('clinic_discounts').create( + body: body, + expand: 'clinic_tenant_id', + ); + return ClinicDiscount.fromJson(record.toJson()); + } + + Future updateDiscount( + String id, { + String? clinicTenantId, + String? prostheticType, + DiscountType? discountType, + double? discountValue, + int? minQuantity, + bool? isActive, + String? notes, + }) async { + final body = {}; + if (clinicTenantId != null) body['clinic_tenant_id'] = clinicTenantId.isEmpty ? null : clinicTenantId; + if (prostheticType != null) body['prosthetic_type'] = prostheticType.isEmpty ? '' : prostheticType; + if (discountType != null) body['discount_type'] = discountType.value; + if (discountValue != null) body['discount_value'] = discountValue; + if (minQuantity != null) body['min_quantity'] = minQuantity; + if (isActive != null) body['is_active'] = isActive; + if (notes != null) body['notes'] = notes; + + final record = await _pb.collection('clinic_discounts').update( + id, + body: body, + expand: 'clinic_tenant_id', + ); + return ClinicDiscount.fromJson(record.toJson()); + } + + Future deleteDiscount(String id) async { + await _pb.collection('clinic_discounts').delete(id); + } +} diff --git a/lib/features/lab/discounts/discounts_screen.dart b/lib/features/lab/discounts/discounts_screen.dart new file mode 100644 index 0000000..39c975a --- /dev/null +++ b/lib/features/lab/discounts/discounts_screen.dart @@ -0,0 +1,940 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +import '../../../core/api/pocketbase_client.dart'; +import '../../../core/providers/auth_provider.dart'; +import '../../../core/theme/app_theme.dart'; +import '../../../core/widgets/gradient_app_bar.dart'; +import '../../../models/clinic_discount.dart'; +import 'discount_repository.dart'; + +// Simple local record for clinic picker +class _ClinicOption { + const _ClinicOption({required this.id, required this.name}); + final String id; + final String name; +} + +class DiscountsScreen extends ConsumerStatefulWidget { + const DiscountsScreen({super.key}); + + @override + ConsumerState createState() => _DiscountsScreenState(); +} + +class _DiscountsScreenState extends ConsumerState { + late Future> _future; + String _searchQuery = ''; + final _searchController = TextEditingController(); + + @override + void initState() { + super.initState(); + _load(); + } + + @override + void dispose() { + _searchController.dispose(); + super.dispose(); + } + + void _load() { + final tenantId = ref.read(authProvider).activeTenant!.tenant.id; + setState(() { + _future = DiscountRepository.instance.listDiscounts(tenantId); + }); + } + + Future _showSheet({ClinicDiscount? existing}) async { + final tenantId = ref.read(authProvider).activeTenant!.tenant.id; + final result = await showModalBottomSheet( + context: context, + isScrollControlled: true, + backgroundColor: Colors.transparent, + builder: (_) => _DiscountSheet( + labTenantId: tenantId, + existing: existing, + ), + ); + if (result == true) _load(); + } + + Future _delete(ClinicDiscount discount) async { + final confirmed = await showDialog( + context: context, + builder: (ctx) => AlertDialog( + title: const Text('İndirimi Sil'), + content: Text( + '${discount.clinicName ?? 'Tüm Klinikler'} — ${discount.displayValue} indirimi silinsin mi?'), + actions: [ + TextButton( + onPressed: () => Navigator.pop(ctx, false), + child: const Text('İptal')), + FilledButton( + style: + FilledButton.styleFrom(backgroundColor: AppColors.cancelled), + onPressed: () => Navigator.pop(ctx, true), + child: const Text('Sil'), + ), + ], + ), + ); + if (confirmed != true) return; + try { + await DiscountRepository.instance.deleteDiscount(discount.id); + _load(); + } catch (e) { + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text('Hata: $e'), + backgroundColor: AppColors.cancelled), + ); + } + } + } + + Future _toggleActive(ClinicDiscount discount) async { + try { + await DiscountRepository.instance + .updateDiscount(discount.id, isActive: !discount.isActive); + _load(); + } catch (e) { + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text('Hata: $e'), + backgroundColor: AppColors.cancelled), + ); + } + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: AppColors.background, + appBar: GradientAppBar( + title: 'İndirimler', + category: 'LABORATUVAR', + searchController: _searchController, + onSearchChanged: (v) => setState(() => _searchQuery = v), + searchHint: 'Klinik veya ürün tipi ara...', + ), + floatingActionButton: FloatingActionButton.extended( + onPressed: () => _showSheet(), + backgroundColor: AppColors.accent, + foregroundColor: Colors.white, + icon: const Icon(Icons.add), + label: const Text('Yeni İndirim'), + ), + body: FutureBuilder>( + future: _future, + builder: (ctx, snap) { + if (snap.connectionState == ConnectionState.waiting) { + return const Center( + child: CircularProgressIndicator(color: AppColors.accent)); + } + if (snap.hasError) { + return Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const Icon(Icons.wifi_off_rounded, + color: AppColors.cancelled, size: 40), + const SizedBox(height: 12), + Text('Hata: ${snap.error}', + style: + const TextStyle(color: AppColors.textSecondary)), + const SizedBox(height: 12), + FilledButton.icon( + onPressed: _load, + icon: const Icon(Icons.refresh_rounded, size: 16), + label: const Text('Tekrar Dene')), + ], + ), + ); + } + + final allDiscounts = snap.data!; + final q = _searchQuery.toLowerCase().trim(); + final discounts = q.isEmpty + ? allDiscounts + : allDiscounts + .where((d) => + (d.clinicName ?? 'tüm klinikler') + .toLowerCase() + .contains(q) || + d.prostheticLabel.toLowerCase().contains(q)) + .toList(); + + if (allDiscounts.isEmpty) { + return Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + width: 72, + height: 72, + decoration: BoxDecoration( + color: AppColors.inProgressBg, + borderRadius: BorderRadius.circular(20), + ), + child: const Icon(Icons.discount_outlined, + size: 32, color: AppColors.inProgress), + ), + const SizedBox(height: 16), + const Text('Henüz indirim tanımlanmadı', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + color: AppColors.textPrimary)), + const SizedBox(height: 8), + const Text( + 'Klinik ve ürün bazlı özel indirimler ekleyin.', + style: TextStyle( + color: AppColors.textSecondary, fontSize: 13), + textAlign: TextAlign.center), + const SizedBox(height: 20), + FilledButton.icon( + onPressed: () => _showSheet(), + icon: const Icon(Icons.add), + label: const Text('İlk İndirimi Ekle'), + ), + ], + ), + ); + } + + if (discounts.isEmpty) { + return const Center( + child: Text('Sonuç bulunamadı', + style: TextStyle(color: AppColors.textSecondary)), + ); + } + + final active = discounts.where((d) => d.isActive).toList(); + final inactive = discounts.where((d) => !d.isActive).toList(); + + return RefreshIndicator( + color: AppColors.accent, + onRefresh: () async => _load(), + child: ListView( + padding: const EdgeInsets.fromLTRB(16, 12, 16, 100), + children: [ + if (active.isNotEmpty) ...[ + _GroupHeader('Aktif (${active.length})'), + for (final d in active) + _DiscountCard( + discount: d, + onEdit: () => _showSheet(existing: d), + onDelete: () => _delete(d), + onToggle: () => _toggleActive(d), + ), + ], + if (inactive.isNotEmpty) ...[ + const SizedBox(height: 8), + _GroupHeader('Pasif (${inactive.length})'), + for (final d in inactive) + _DiscountCard( + discount: d, + onEdit: () => _showSheet(existing: d), + onDelete: () => _delete(d), + onToggle: () => _toggleActive(d), + ), + ], + ], + ), + ); + }, + ), + ); + } +} + +// ── Group header ────────────────────────────────────────────────────────────── + +class _GroupHeader extends StatelessWidget { + const _GroupHeader(this.text); + final String text; + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(bottom: 8, top: 4), + child: Text(text, + style: const TextStyle( + fontSize: 12, + fontWeight: FontWeight.w700, + color: AppColors.textMuted, + letterSpacing: 0.5)), + ); + } +} + +// ── Discount card ───────────────────────────────────────────────────────────── + +class _DiscountCard extends StatelessWidget { + const _DiscountCard({ + required this.discount, + required this.onEdit, + required this.onDelete, + required this.onToggle, + }); + + final ClinicDiscount discount; + final VoidCallback onEdit; + final VoidCallback onDelete; + final VoidCallback onToggle; + + @override + Widget build(BuildContext context) { + final d = discount; + final isActive = d.isActive; + + return Padding( + padding: const EdgeInsets.only(bottom: 10), + child: Material( + color: isActive ? AppColors.surface : AppColors.surfaceVariant, + borderRadius: BorderRadius.circular(14), + child: InkWell( + onTap: onEdit, + borderRadius: BorderRadius.circular(14), + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(14), + border: Border.all( + color: isActive ? AppColors.border : AppColors.muted), + boxShadow: isActive + ? [ + BoxShadow( + color: Colors.black.withValues(alpha: 0.03), + blurRadius: 6, + offset: const Offset(0, 2)) + ] + : [], + ), + child: IntrinsicHeight( + child: Row( + children: [ + Container( + width: 4, + decoration: BoxDecoration( + color: + isActive ? AppColors.success : AppColors.border, + borderRadius: const BorderRadius.only( + topLeft: Radius.circular(14), + bottomLeft: Radius.circular(14), + ), + ), + ), + Expanded( + child: Padding( + padding: const EdgeInsets.fromLTRB(12, 12, 4, 12), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Container( + padding: const EdgeInsets.symmetric( + horizontal: 10, vertical: 4), + decoration: BoxDecoration( + color: isActive + ? AppColors.successBg + : AppColors.background, + borderRadius: BorderRadius.circular(8), + ), + child: Text( + '${d.displayValue} İndirim', + style: TextStyle( + fontSize: 15, + fontWeight: FontWeight.w800, + color: isActive + ? AppColors.success + : AppColors.textMuted, + ), + ), + ), + if (d.minQuantity > 0) ...[ + const SizedBox(width: 8), + Container( + padding: const EdgeInsets.symmetric( + horizontal: 7, vertical: 4), + decoration: BoxDecoration( + color: AppColors.pendingBg, + borderRadius: BorderRadius.circular(6), + ), + child: Text( + '≥${d.minQuantity} adet', + style: const TextStyle( + fontSize: 11, + fontWeight: FontWeight.w600, + color: AppColors.pending), + ), + ), + ], + const Spacer(), + Transform.scale( + scale: 0.8, + child: Switch( + value: isActive, + onChanged: (_) => onToggle(), + activeColor: AppColors.success, + ), + ), + ], + ), + const SizedBox(height: 8), + Row( + children: [ + _Tag( + icon: Icons.local_hospital_outlined, + label: d.appliesToAll + ? 'Tüm Klinikler' + : (d.clinicName ?? 'Klinik'), + color: AppColors.inProgress, + ), + const SizedBox(width: 6), + _Tag( + icon: Icons.science_outlined, + label: d.prostheticLabel, + color: AppColors.accent, + ), + ], + ), + if (d.notes != null && d.notes!.isNotEmpty) ...[ + const SizedBox(height: 6), + Text(d.notes!, + style: const TextStyle( + fontSize: 12, + color: AppColors.textMuted), + maxLines: 1, + overflow: TextOverflow.ellipsis), + ], + ], + ), + ), + ), + IconButton( + onPressed: onDelete, + icon: const Icon(Icons.delete_outline_rounded, + size: 18, color: AppColors.cancelled), + tooltip: 'Sil', + ), + ], + ), + ), + ), + ), + ), + ); + } +} + +class _Tag extends StatelessWidget { + const _Tag( + {required this.icon, required this.label, required this.color}); + final IconData icon; + final String label; + final Color color; + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.symmetric(horizontal: 7, vertical: 3), + decoration: BoxDecoration( + color: color.withValues(alpha: 0.1), + borderRadius: BorderRadius.circular(6), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon(icon, size: 11, color: color), + const SizedBox(width: 4), + Text(label, + style: TextStyle( + fontSize: 11, + fontWeight: FontWeight.w600, + color: color)), + ], + ), + ); + } +} + +// ── Discount sheet ──────────────────────────────────────────────────────────── + +class _DiscountSheet extends StatefulWidget { + const _DiscountSheet({required this.labTenantId, this.existing}); + final String labTenantId; + final ClinicDiscount? existing; + + @override + State<_DiscountSheet> createState() => _DiscountSheetState(); +} + +class _DiscountSheetState extends State<_DiscountSheet> { + final _valueCtrl = TextEditingController(); + final _minQtyCtrl = TextEditingController(); + final _notesCtrl = TextEditingController(); + + DiscountType _discountType = DiscountType.percentage; + String? _selectedClinicId; + String? _selectedType; + bool _isActive = true; + bool _saving = false; + + List<_ClinicOption>? _clinics; + + @override + void initState() { + super.initState(); + final e = widget.existing; + if (e != null) { + _valueCtrl.text = e.discountValue.toStringAsFixed( + e.discountValue % 1 == 0 ? 0 : 2); + _minQtyCtrl.text = + e.minQuantity > 0 ? e.minQuantity.toString() : ''; + _notesCtrl.text = e.notes ?? ''; + _discountType = e.discountType; + _selectedClinicId = e.clinicTenantId; + _selectedType = e.prostheticType; + _isActive = e.isActive; + } + _loadClinics(); + } + + Future _loadClinics() async { + try { + final pb = PocketBaseClient.instance.pb; + final result = await pb.collection('tenant_connections').getList( + filter: + 'lab_tenant_id = "${widget.labTenantId}" && status = "approved"', + expand: 'clinic_tenant_id', + perPage: 100, + ); + final clinics = result.items.map((r) { + final j = r.toJson(); + final expand = j['expand'] as Map?; + final clinic = + expand?['clinic_tenant_id'] as Map?; + return _ClinicOption( + id: j['clinic_tenant_id'] as String? ?? '', + name: clinic?['company_name'] as String? ?? 'Klinik', + ); + }).where((c) => c.id.isNotEmpty).toList(); + if (mounted) setState(() => _clinics = clinics); + } catch (_) { + if (mounted) setState(() => _clinics = []); + } + } + + @override + void dispose() { + _valueCtrl.dispose(); + _minQtyCtrl.dispose(); + _notesCtrl.dispose(); + super.dispose(); + } + + Future _save() async { + final valueStr = _valueCtrl.text.trim().replaceAll(',', '.'); + final value = double.tryParse(valueStr); + if (value == null || value <= 0) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('Geçerli bir indirim değeri girin.'), + backgroundColor: AppColors.cancelled), + ); + return; + } + if (_discountType == DiscountType.percentage && value > 100) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text("Yüzde indirim 100'ü geçemez."), + backgroundColor: AppColors.cancelled), + ); + return; + } + + final minQty = int.tryParse(_minQtyCtrl.text.trim()) ?? 0; + setState(() => _saving = true); + final navigator = Navigator.of(context); + final messenger = ScaffoldMessenger.of(context); + try { + if (widget.existing != null) { + await DiscountRepository.instance.updateDiscount( + widget.existing!.id, + clinicTenantId: _selectedClinicId ?? '', + prostheticType: _selectedType ?? '', + discountType: _discountType, + discountValue: value, + minQuantity: minQty, + isActive: _isActive, + notes: _notesCtrl.text.trim(), + ); + } else { + await DiscountRepository.instance.createDiscount( + labTenantId: widget.labTenantId, + clinicTenantId: _selectedClinicId, + prostheticType: _selectedType, + discountType: _discountType, + discountValue: value, + minQuantity: minQty, + isActive: _isActive, + notes: _notesCtrl.text.trim(), + ); + } + navigator.pop(true); + } catch (e) { + if (mounted) setState(() => _saving = false); + messenger.showSnackBar( + SnackBar( + content: Text('Hata: $e'), + backgroundColor: AppColors.cancelled), + ); + } + } + + static const _prostheticTypes = [ + ('', 'Tüm Türler'), + ('metal_porselen', 'Metal Porselen'), + ('zirkonyum', 'Zirkonyum'), + ('implant_ustu_zirkonyum', 'İmplant Üstü Zirkonyum'), + ('gecici', 'Geçici'), + ('e_max', 'E-Max'), + ('tam_protez', 'Tam Protez'), + ('parsiyel', 'Parsiyel Protez'), + ('diger', 'Diğer'), + ]; + + @override + Widget build(BuildContext context) { + final bottom = MediaQuery.paddingOf(context).bottom; + return Container( + decoration: const BoxDecoration( + color: AppColors.surface, + borderRadius: BorderRadius.vertical(top: Radius.circular(20)), + ), + padding: EdgeInsets.only(bottom: bottom), + child: SingleChildScrollView( + padding: EdgeInsets.only( + left: 20, + right: 20, + top: 20, + bottom: MediaQuery.viewInsetsOf(context).bottom + 20, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Center( + child: Container( + width: 40, + height: 4, + decoration: BoxDecoration( + color: AppColors.border, + borderRadius: BorderRadius.circular(2)), + ), + ), + const SizedBox(height: 16), + Text( + widget.existing != null + ? 'İndirimi Düzenle' + : 'Yeni İndirim', + style: const TextStyle( + fontSize: 18, + fontWeight: FontWeight.w700, + color: AppColors.textPrimary), + ), + const SizedBox(height: 20), + + const Text('İndirim Türü', + style: TextStyle( + fontSize: 13, + fontWeight: FontWeight.w600, + color: AppColors.textSecondary)), + const SizedBox(height: 8), + Row( + children: [ + Expanded( + child: _TypeButton( + label: 'Yüzde (%)', + icon: Icons.percent_rounded, + selected: _discountType == DiscountType.percentage, + onTap: () => setState( + () => _discountType = DiscountType.percentage), + ), + ), + const SizedBox(width: 10), + Expanded( + child: _TypeButton( + label: 'Sabit Tutar', + icon: Icons.currency_lira_rounded, + selected: _discountType == DiscountType.fixed, + onTap: () => + setState(() => _discountType = DiscountType.fixed), + ), + ), + ], + ), + const SizedBox(height: 16), + + const Text('İndirim Değeri', + style: TextStyle( + fontSize: 13, + fontWeight: FontWeight.w600, + color: AppColors.textSecondary)), + const SizedBox(height: 8), + TextField( + controller: _valueCtrl, + keyboardType: + const TextInputType.numberWithOptions(decimal: true), + decoration: InputDecoration( + hintText: _discountType == DiscountType.percentage + ? 'Örn: 10' + : 'Örn: 150', + suffixText: + _discountType == DiscountType.percentage ? '%' : 'TL', + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(12)), + ), + ), + const SizedBox(height: 16), + + const Text('Klinik', + style: TextStyle( + fontSize: 13, + fontWeight: FontWeight.w600, + color: AppColors.textSecondary)), + const SizedBox(height: 8), + _ClinicDropdown( + selectedId: _selectedClinicId, + clinics: _clinics, + onChanged: (id, _) => setState(() { + _selectedClinicId = id; + }), + ), + const SizedBox(height: 16), + + const Text('Ürün Tipi', + style: TextStyle( + fontSize: 13, + fontWeight: FontWeight.w600, + color: AppColors.textSecondary)), + const SizedBox(height: 8), + DropdownButtonFormField( + value: _selectedType ?? '', + decoration: InputDecoration( + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(12)), + contentPadding: const EdgeInsets.symmetric( + horizontal: 14, vertical: 12), + ), + items: _prostheticTypes + .map((t) => + DropdownMenuItem(value: t.$1, child: Text(t.$2))) + .toList(), + onChanged: (v) => + setState(() => _selectedType = v == '' ? null : v), + ), + const SizedBox(height: 16), + + const Text('Minimum Sipariş Adedi (İsteğe Bağlı)', + style: TextStyle( + fontSize: 13, + fontWeight: FontWeight.w600, + color: AppColors.textSecondary)), + const SizedBox(height: 4), + const Text( + 'Aylık bu adede ulaşılınca indirim devreye girer. 0 = koşulsuz.', + style: + TextStyle(fontSize: 11, color: AppColors.textMuted)), + const SizedBox(height: 8), + TextField( + controller: _minQtyCtrl, + keyboardType: TextInputType.number, + decoration: InputDecoration( + hintText: '0', + suffixText: 'adet', + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(12)), + ), + ), + const SizedBox(height: 16), + + const Text('Not (İsteğe Bağlı)', + style: TextStyle( + fontSize: 13, + fontWeight: FontWeight.w600, + color: AppColors.textSecondary)), + const SizedBox(height: 8), + TextField( + controller: _notesCtrl, + maxLines: 2, + decoration: InputDecoration( + hintText: 'Açıklama...', + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(12)), + ), + ), + const SizedBox(height: 16), + + Row( + children: [ + const Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Aktif', + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w600, + color: AppColors.textPrimary)), + Text('Pasif indirimler uygulanmaz.', + style: TextStyle( + fontSize: 12, color: AppColors.textMuted)), + ], + ), + ), + Switch( + value: _isActive, + onChanged: (v) => setState(() => _isActive = v), + activeColor: AppColors.success, + ), + ], + ), + const SizedBox(height: 24), + + SizedBox( + width: double.infinity, + height: 50, + child: FilledButton( + onPressed: _saving ? null : _save, + style: FilledButton.styleFrom( + backgroundColor: AppColors.accent, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(14)), + ), + child: _saving + ? const SizedBox( + width: 20, + height: 20, + child: CircularProgressIndicator( + strokeWidth: 2, color: Colors.white)) + : Text( + widget.existing != null ? 'Güncelle' : 'Kaydet', + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600)), + ), + ), + ], + ), + ), + ); + } +} + +class _TypeButton extends StatelessWidget { + const _TypeButton({ + required this.label, + required this.icon, + required this.selected, + required this.onTap, + }); + final String label; + final IconData icon; + final bool selected; + final VoidCallback onTap; + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: onTap, + child: AnimatedContainer( + duration: const Duration(milliseconds: 150), + padding: const EdgeInsets.symmetric(vertical: 12), + decoration: BoxDecoration( + color: selected ? AppColors.accent : AppColors.background, + borderRadius: BorderRadius.circular(12), + border: Border.all( + color: selected ? AppColors.accent : AppColors.border), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon(icon, + size: 16, + color: selected ? Colors.white : AppColors.textSecondary), + const SizedBox(width: 6), + Text(label, + style: TextStyle( + fontSize: 13, + fontWeight: FontWeight.w600, + color: selected + ? Colors.white + : AppColors.textSecondary)), + ], + ), + ), + ); + } +} + +class _ClinicDropdown extends StatelessWidget { + const _ClinicDropdown({ + required this.selectedId, + required this.clinics, + required this.onChanged, + }); + + final String? selectedId; + final List<_ClinicOption>? clinics; + final void Function(String? id, String? name) onChanged; + + @override + Widget build(BuildContext context) { + if (clinics == null) { + return Container( + height: 48, + decoration: BoxDecoration( + border: Border.all(color: AppColors.border), + borderRadius: BorderRadius.circular(12), + ), + child: const Center( + child: SizedBox( + width: 16, + height: 16, + child: CircularProgressIndicator(strokeWidth: 2))), + ); + } + + final items = >[ + const DropdownMenuItem(value: '', child: Text('Tüm Klinikler')), + for (final c in clinics!) + DropdownMenuItem(value: c.id, child: Text(c.name)), + ]; + + return DropdownButtonFormField( + value: selectedId ?? '', + decoration: InputDecoration( + border: + OutlineInputBorder(borderRadius: BorderRadius.circular(12)), + contentPadding: + const EdgeInsets.symmetric(horizontal: 14, vertical: 12), + ), + items: items, + onChanged: (v) { + if (v == null || v.isEmpty) { + onChanged(null, null); + } else { + final clinic = clinics!.firstWhere((c) => c.id == v); + onChanged(v, clinic.name); + } + }, + ); + } +} diff --git a/lib/features/lab/finance/lab_finance_repository.dart b/lib/features/lab/finance/lab_finance_repository.dart new file mode 100644 index 0000000..07646fc --- /dev/null +++ b/lib/features/lab/finance/lab_finance_repository.dart @@ -0,0 +1,42 @@ +import 'package:pocketbase/pocketbase.dart'; +import '../../../core/api/pocketbase_client.dart'; +import '../../../models/finance_entry.dart'; + +class LabFinanceRepository { + LabFinanceRepository._(); + static final instance = LabFinanceRepository._(); + + PocketBase get _pb => PocketBaseClient.instance.pb; + + Future> listEntries( + String tenantId, { + String? status, + int page = 1, + int limit = 30, + }) async { + final filterParts = ['tenant_id = "$tenantId"', 'type = "receivable"']; + if (status != null) filterParts.add('status = "$status"'); + + final result = await _pb.collection('finance_entries').getList( + page: page, + perPage: limit, + filter: filterParts.join(' && '), + expand: 'job_id', + ); + return (result.items.map((r) => FinanceEntry.fromJson(r.toJson())).toList() + ..sort((a, b) => (b.dateCreated ?? '').compareTo(a.dateCreated ?? ''))); + } + + Future> summary(String tenantId) async { + final all = await listEntries(tenantId, limit: 200); + double pending = 0, paid = 0; + for (final e in all) { + if (e.status == FinanceStatus.pending) { + pending += e.amount; + } else { + paid += e.amount; + } + } + return {'pending': pending, 'paid': paid}; + } +} diff --git a/lib/features/lab/finance/lab_finance_screen.dart b/lib/features/lab/finance/lab_finance_screen.dart new file mode 100644 index 0000000..56f342d --- /dev/null +++ b/lib/features/lab/finance/lab_finance_screen.dart @@ -0,0 +1,467 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +import '../../../core/providers/auth_provider.dart'; +import '../../../core/providers/locale_provider.dart'; +import '../../../core/theme/app_theme.dart'; +import '../../../core/utils/currency_formatter.dart'; +import '../../../core/widgets/gradient_app_bar.dart'; +import '../../../core/widgets/pill_tabs.dart'; +import '../../../models/finance_entry.dart'; +import 'lab_finance_repository.dart'; + +enum _FinanceSort { newestFirst, byAmountDesc, byAmountAsc } + +class LabFinanceScreen extends ConsumerStatefulWidget { + const LabFinanceScreen({super.key}); + + @override + ConsumerState createState() => _LabFinanceScreenState(); +} + +class _LabFinanceScreenState extends ConsumerState + with SingleTickerProviderStateMixin { + late TabController _tabController; + late Future<_FinanceData> _future; + _FinanceSort _sort = _FinanceSort.newestFirst; + + @override + void initState() { + super.initState(); + _tabController = TabController(length: 2, vsync: this); + _tabController.addListener(() { + if (mounted) setState(() {}); + }); + _load(); + } + + @override + void dispose() { + _tabController.dispose(); + super.dispose(); + } + + void _load() { + final tenantId = ref.read(authProvider).activeTenant!.tenant.id; + setState(() { + _future = Future.wait([ + LabFinanceRepository.instance.listEntries(tenantId, status: 'pending'), + LabFinanceRepository.instance.listEntries(tenantId, status: 'paid'), + LabFinanceRepository.instance.summary(tenantId), + ]).then((results) => _FinanceData( + pending: results[0] as List, + paid: results[1] as List, + summary: results[2] as Map, + )); + }); + } + + Future _showSortOptions() async { + final s = ref.read(stringsProvider); + final result = await showSortSheet( + context, + title: s.sort, + options: [s.sortNewest, s.sortAmountDesc, s.sortAmountAsc], + current: _sort.index, + ); + if (result != null) { + setState(() => _sort = _FinanceSort.values[result]); + } + } + + List _sorted(List entries) { + final list = List.from(entries); + switch (_sort) { + case _FinanceSort.newestFirst: + list.sort((a, b) { + final da = a.dateCreated != null ? DateTime.tryParse(a.dateCreated!) : null; + final db = b.dateCreated != null ? DateTime.tryParse(b.dateCreated!) : null; + if (da == null && db == null) return 0; + if (da == null) return 1; + if (db == null) return -1; + return db.compareTo(da); + }); + case _FinanceSort.byAmountDesc: + list.sort((a, b) => b.amount.compareTo(a.amount)); + case _FinanceSort.byAmountAsc: + list.sort((a, b) => a.amount.compareTo(b.amount)); + } + return list; + } + + String _formatDate(String? raw) { + if (raw == null) return ''; + try { + final dt = DateTime.parse(raw); + return '${dt.day.toString().padLeft(2, '0')}.${dt.month.toString().padLeft(2, '0')}.${dt.year}'; + } catch (_) { + return ''; + } + } + + @override + Widget build(BuildContext context) { + final isSortActive = _sort != _FinanceSort.newestFirst; + final s = ref.watch(stringsProvider); + final currencyCode = + ref.watch(authProvider).activeTenant?.tenant.defaultCurrency ?? 'TRY'; + String formatAmount(double amount) => + CurrencyFormatter.format(amount, currencyCode); + + return Scaffold( + backgroundColor: AppColors.background, + appBar: GradientAppBar( + title: s.finance, + category: s.laboratoryCategory, + actions: [ + IconButton( + onPressed: _showSortOptions, + tooltip: 'Sırala', + icon: Badge( + isLabelVisible: isSortActive, + smallSize: 8, + backgroundColor: AppColors.accent, + child: const Icon(Icons.sort_rounded), + ), + ), + ], + ), + body: RefreshIndicator( + color: AppColors.accent, + onRefresh: () async => _load(), + child: FutureBuilder<_FinanceData>( + future: _future, + builder: (ctx, snap) { + if (snap.connectionState == ConnectionState.waiting) { + return const Center( + child: CircularProgressIndicator(color: AppColors.accent)); + } + if (snap.hasError) { + return Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + width: 64, + height: 64, + decoration: BoxDecoration( + color: AppColors.cancelledBg, + borderRadius: BorderRadius.circular(16)), + child: const Icon(Icons.wifi_off_rounded, + color: AppColors.cancelled, size: 30), + ), + const SizedBox(height: 16), + Text('Hata: ${snap.error}', + style: const TextStyle( + color: AppColors.textSecondary)), + const SizedBox(height: 12), + FilledButton.icon( + onPressed: _load, + icon: const Icon(Icons.refresh_rounded, size: 18), + label: const Text('Tekrar Dene'), + ), + ], + ), + ); + } + + final data = snap.data!; + final pendingTotal = data.summary['pending'] ?? 0.0; + final paidTotal = data.summary['paid'] ?? 0.0; + final pending = _sorted(data.pending); + final paid = _sorted(data.paid); + + return Column( + children: [ + Padding( + padding: const EdgeInsets.all(16), + child: Row( + children: [ + Expanded( + child: _SummaryCard( + label: s.pendingReceivable, + amount: formatAmount(pendingTotal), + color: AppColors.pending, + bgColor: AppColors.pendingBg, + icon: Icons.hourglass_empty_rounded, + ), + ), + const SizedBox(width: 12), + Expanded( + child: _SummaryCard( + label: s.collected, + amount: formatAmount(paidTotal), + color: AppColors.success, + bgColor: AppColors.successBg, + icon: Icons.check_circle_outline, + ), + ), + ], + ), + ), + PillTabs( + tabs: [s.pending, s.collected], + selected: _tabController.index, + onSelect: (i) => _tabController.animateTo(i), + counts: [pending.length, paid.length], + ), + Expanded( + child: TabBarView( + controller: _tabController, + children: [ + _EntriesList( + entries: pending, + emptyMessage: s.noPendingEntries, + emptyIcon: Icons.hourglass_empty_rounded, + formatDate: _formatDate, + formatAmount: formatAmount, + ), + _EntriesList( + entries: paid, + emptyMessage: s.noPaidEntries, + emptyIcon: Icons.check_circle_outline, + formatDate: _formatDate, + formatAmount: formatAmount, + ), + ], + ), + ), + ], + ); + }, + ), + ), + ); + } +} + +class _FinanceData { + const _FinanceData({ + required this.pending, + required this.paid, + required this.summary, + }); + + final List pending; + final List paid; + final Map summary; +} + +class _SummaryCard extends StatelessWidget { + const _SummaryCard({ + required this.label, + required this.amount, + required this.color, + required this.bgColor, + required this.icon, + }); + + final String label; + final String amount; + final Color color; + final Color bgColor; + final IconData icon; + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: AppColors.surface, + borderRadius: BorderRadius.circular(16), + border: Border.all(color: AppColors.border), + boxShadow: [ + BoxShadow( + color: Colors.black.withValues(alpha: 0.06), + blurRadius: 12, + offset: const Offset(0, 4)) + ], + ), + child: Row( + children: [ + Container( + width: 44, + height: 44, + decoration: BoxDecoration( + color: bgColor, borderRadius: BorderRadius.circular(12)), + child: Icon(icon, color: color, size: 22), + ), + const SizedBox(width: 12), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + amount, + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.w800, + color: color, + height: 1), + ), + const SizedBox(height: 3), + Text(label, + style: const TextStyle( + fontSize: 12, + color: AppColors.textSecondary, + fontWeight: FontWeight.w500)), + ], + ), + ), + ], + ), + ); + } +} + +class _EntriesList extends StatelessWidget { + const _EntriesList({ + required this.entries, + required this.emptyMessage, + required this.emptyIcon, + required this.formatDate, + required this.formatAmount, + }); + + final List entries; + final String emptyMessage; + final IconData emptyIcon; + final String Function(String?) formatDate; + final String Function(double) formatAmount; + + @override + Widget build(BuildContext context) { + if (entries.isEmpty) { + return Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + width: 72, + height: 72, + decoration: BoxDecoration( + color: AppColors.inProgressBg, + borderRadius: BorderRadius.circular(20)), + child: Icon(emptyIcon, size: 32, color: AppColors.inProgress), + ), + const SizedBox(height: 16), + Text(emptyMessage, + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + color: AppColors.textPrimary)), + ], + ), + ); + } + + return ListView.builder( + padding: const EdgeInsets.fromLTRB(16, 8, 16, 24), + itemCount: entries.length, + itemBuilder: (ctx, i) { + final entry = entries[i]; + final isPending = entry.status == FinanceStatus.pending; + final statusColor = isPending ? AppColors.pending : AppColors.success; + final statusBg = isPending ? AppColors.pendingBg : AppColors.successBg; + + return Padding( + padding: const EdgeInsets.only(bottom: 10), + child: Container( + padding: const EdgeInsets.all(14), + decoration: BoxDecoration( + color: AppColors.surface, + borderRadius: BorderRadius.circular(14), + border: Border.all(color: AppColors.border), + boxShadow: [ + BoxShadow( + color: Colors.black.withValues(alpha: 0.04), + blurRadius: 8, + offset: const Offset(0, 2)) + ], + ), + child: Row( + children: [ + Container( + width: 44, + height: 44, + decoration: BoxDecoration( + color: statusBg, + borderRadius: BorderRadius.circular(12)), + child: Icon( + isPending + ? Icons.hourglass_empty_rounded + : Icons.check_circle_outline, + color: statusColor, + size: 22, + ), + ), + const SizedBox(width: 12), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + entry.counterpartyName ?? 'Klinik', + style: const TextStyle( + fontSize: 15, + fontWeight: FontWeight.w600, + color: AppColors.textPrimary), + ), + if (entry.patientCode != null) ...[ + const SizedBox(height: 2), + Text( + 'Protokol: ${entry.patientCode}', + style: const TextStyle( + fontSize: 12, color: AppColors.textSecondary), + ), + ], + if (entry.dateCreated != null) ...[ + const SizedBox(height: 2), + Text( + formatDate(entry.dateCreated), + style: const TextStyle( + fontSize: 12, color: AppColors.textMuted), + ), + ], + ], + ), + ), + const SizedBox(width: 8), + Column( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Text( + formatAmount(entry.amount), + style: TextStyle( + fontWeight: FontWeight.w700, + color: statusColor, + fontSize: 15, + ), + ), + const SizedBox(height: 4), + Container( + padding: const EdgeInsets.symmetric( + horizontal: 8, vertical: 2), + decoration: BoxDecoration( + color: statusBg, + borderRadius: BorderRadius.circular(8), + ), + child: Text( + entry.status.label, + style: TextStyle( + color: statusColor, + fontSize: 11, + fontWeight: FontWeight.w600, + ), + ), + ), + ], + ), + ], + ), + ), + ); + }, + ); + } +} diff --git a/lib/features/lab/jobs/lab_all_jobs_screen.dart b/lib/features/lab/jobs/lab_all_jobs_screen.dart new file mode 100644 index 0000000..482cbe7 --- /dev/null +++ b/lib/features/lab/jobs/lab_all_jobs_screen.dart @@ -0,0 +1,896 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:go_router/go_router.dart'; + +import '../../../core/providers/auth_provider.dart'; +import '../../../core/services/realtime_service.dart'; +import '../../../core/theme/app_theme.dart'; +import '../../../core/widgets/gradient_app_bar.dart'; +import '../../../core/widgets/pill_tabs.dart'; +import '../../../models/job.dart'; +import 'lab_jobs_repository.dart'; + +enum _JobSort { newestFirst, oldestFirst, byDueDate, byType } + +const _kSortLabels = [ + 'Yeniden Eskiye', + 'Eskiden Yeniye', + 'Vade Tarihine Göre', + 'Türe Göre', +]; + +class LabAllJobsScreen extends ConsumerStatefulWidget { + const LabAllJobsScreen({super.key}); + + @override + ConsumerState createState() => _LabAllJobsScreenState(); +} + +class _LabAllJobsScreenState extends ConsumerState + with SingleTickerProviderStateMixin { + late TabController _tabController; + final _searchController = TextEditingController(); + String _searchQuery = ''; + _JobSort _sort = _JobSort.newestFirst; + bool _bulkAccepting = false; + final Map _counts = { + 'all': null, + 'pending': null, + 'in_progress': null, + 'sent': null, + 'delivered': null, + }; + final _pendingTabKey = GlobalKey<_PendingJobsTabState>(); + + // null entry = Tümü (bütün statüsler) + static const List _statuses = [null, 'pending', 'in_progress', 'sent', 'delivered']; + static const _tabLabels = ['Tümü', 'Onay Bekleyen', 'Devam Eden', 'Gönderildi', 'Teslim Edildi']; + String _countKey(String? s) => s ?? 'all'; + + @override + void initState() { + super.initState(); + final isDelivery = ref.read(authProvider).activeTenant?.isDeliveryOnly ?? false; + _tabController = TabController(length: 5, vsync: this, initialIndex: isDelivery ? 3 : 0); + _tabController.addListener(() { + if (mounted) setState(() {}); + }); + _fetchAllCounts(); + } + + Future _fetchAllCounts() async { + final tenantId = ref.read(authProvider).activeTenant?.tenant.id; + if (tenantId == null) return; + final results = await Future.wait( + _statuses.map((s) => LabJobsRepository.instance.countByStatus(tenantId, s)), + ); + if (!mounted) return; + setState(() { + for (var i = 0; i < _statuses.length; i++) { + _counts[_countKey(_statuses[i])] = results[i]; + } + }); + } + + @override + void dispose() { + _tabController.dispose(); + _searchController.dispose(); + super.dispose(); + } + + void _onSearchChanged(String value) { + setState(() => _searchQuery = value); + } + + Future _showSortOptions() async { + final result = await showSortSheet( + context, + title: 'Sıralama', + options: _kSortLabels, + current: _sort.index, + ); + if (result != null) { + setState(() => _sort = _JobSort.values[result]); + } + } + + Future _bulkAccept() async { + final tenantId = ref.read(authProvider).activeTenant!.tenant.id; + setState(() => _bulkAccepting = true); + try { + await LabJobsRepository.instance.bulkAcceptPending(tenantId); + _pendingTabKey.currentState?._load(); + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('Tüm işler kabul edildi')), + ); + } + } catch (e) { + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('Hata: $e')), + ); + } + } finally { + if (mounted) setState(() => _bulkAccepting = false); + } + } + + @override + Widget build(BuildContext context) { + final isSortActive = _sort != _JobSort.newestFirst; + final onPendingTab = _tabController.index == 1; + final pendingCount = _counts['pending']; + + return Scaffold( + backgroundColor: AppColors.background, + appBar: GradientAppBar( + title: 'İşler', + category: 'LABORATUVAR', + searchController: _searchController, + onSearchChanged: _onSearchChanged, + searchHint: 'Protokol, klinik veya tür ara...', + actions: [ + if (!onPendingTab) + IconButton( + onPressed: _showSortOptions, + tooltip: 'Sırala', + icon: Badge( + isLabelVisible: isSortActive, + smallSize: 8, + backgroundColor: AppColors.accent, + child: const Icon(Icons.sort_rounded), + ), + ), + ], + ), + floatingActionButton: onPendingTab && (pendingCount == null || pendingCount > 0) + ? FloatingActionButton.extended( + onPressed: _bulkAccepting ? null : _bulkAccept, + icon: _bulkAccepting + ? const SizedBox( + width: 18, + height: 18, + child: CircularProgressIndicator(strokeWidth: 2, color: Colors.white), + ) + : const Icon(Icons.done_all), + label: Text(_bulkAccepting ? 'Kabul ediliyor...' : 'Tümünü Kabul Et'), + backgroundColor: AppColors.pending, + foregroundColor: Colors.white, + ) + : null, + body: Column( + children: [ + PillTabs( + tabs: _tabLabels, + selected: _tabController.index, + onSelect: (i) => _tabController.animateTo(i), + counts: _statuses.map((s) => _counts[_countKey(s)]).toList(), + ), + Expanded( + child: TabBarView( + controller: _tabController, + children: [ + _LabJobsTab( + status: null, + searchQuery: _searchQuery, + sort: _sort, + onCountLoaded: (c) => setState(() => _counts['all'] = c), + ), + _PendingJobsTab( + key: _pendingTabKey, + searchQuery: _searchQuery, + onCountLoaded: (c) => setState(() => _counts['pending'] = c), + ), + _LabJobsTab( + status: 'in_progress', + searchQuery: _searchQuery, + sort: _sort, + onCountLoaded: (c) => setState(() => _counts['in_progress'] = c), + ), + _LabJobsTab( + status: 'sent', + searchQuery: _searchQuery, + sort: _sort, + onCountLoaded: (c) => setState(() => _counts['sent'] = c), + ), + _LabJobsTab( + status: 'delivered', + searchQuery: _searchQuery, + sort: _sort, + onCountLoaded: (c) => setState(() => _counts['delivered'] = c), + ), + ], + ), + ), + ], + ), + ); + } +} + +// ── Pending (Onay Bekleyen) tab ─────────────────────────────────────────────── + +class _PendingJobsTab extends ConsumerStatefulWidget { + const _PendingJobsTab({super.key, required this.searchQuery, this.onCountLoaded}); + final String searchQuery; + final void Function(int)? onCountLoaded; + + @override + ConsumerState<_PendingJobsTab> createState() => _PendingJobsTabState(); +} + +class _PendingJobsTabState extends ConsumerState<_PendingJobsTab> { + late Future> _future; + late UnsubFn _unsub; + + @override + void initState() { + super.initState(); + _load(); + final tenantId = ref.read(authProvider).activeTenant!.tenant.id; + _unsub = RealtimeService.instance.watch( + 'jobs', + filter: 'lab_tenant_id="$tenantId" && status="pending"', + onEvent: (_) { if (mounted) _load(); }, + ); + } + + @override + void dispose() { + _unsub(); + super.dispose(); + } + + void _load() { + final tenantId = ref.read(authProvider).activeTenant!.tenant.id; + setState(() { + _future = LabJobsRepository.instance.listInbound(tenantId, status: 'pending', limit: 50); + }); + } + + Future _acceptJob(Job job) async { + try { + await LabJobsRepository.instance.acceptJob(job); + _load(); + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('İş kabul edildi')), + ); + } + } catch (e) { + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('Hata: $e')), + ); + } + } + } + + List _filtered(List jobs) { + final q = widget.searchQuery.toLowerCase().trim(); + if (q.isEmpty) return jobs; + return jobs.where((j) => + j.patientCode.toLowerCase().contains(q) || + (j.clinicName?.toLowerCase().contains(q) ?? false) || + j.prostheticType.label.toLowerCase().contains(q) + ).toList(); + } + + @override + Widget build(BuildContext context) { + return RefreshIndicator( + color: AppColors.accent, + onRefresh: () async => _load(), + child: FutureBuilder>( + future: _future, + builder: (ctx, snap) { + if (snap.connectionState == ConnectionState.waiting) { + return const Center(child: CircularProgressIndicator(color: AppColors.accent)); + } + if (snap.hasError) { + return Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + width: 64, + height: 64, + decoration: BoxDecoration( + color: AppColors.cancelledBg, + borderRadius: BorderRadius.circular(16), + ), + child: const Icon(Icons.wifi_off_rounded, color: AppColors.cancelled, size: 30), + ), + const SizedBox(height: 16), + Text('Hata: ${snap.error}', style: const TextStyle(color: AppColors.textSecondary)), + const SizedBox(height: 12), + FilledButton.icon( + onPressed: _load, + icon: const Icon(Icons.refresh_rounded, size: 18), + label: const Text('Tekrar Dene'), + ), + ], + ), + ); + } + + final all = snap.data!; + WidgetsBinding.instance.addPostFrameCallback((_) { + widget.onCountLoaded?.call(all.length); + }); + + final jobs = _filtered(all); + + if (jobs.isEmpty) { + return Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + width: 72, + height: 72, + decoration: BoxDecoration( + color: AppColors.successBg, + borderRadius: BorderRadius.circular(20), + ), + child: const Icon(Icons.inbox_outlined, color: AppColors.success, size: 32), + ), + const SizedBox(height: 16), + Text( + widget.searchQuery.isNotEmpty ? 'Sonuç bulunamadı' : 'Onay bekleyen iş yok', + style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600, color: AppColors.textPrimary), + ), + if (widget.searchQuery.isEmpty) ...[ + const SizedBox(height: 6), + const Text('Tüm işler kabul edildi', style: TextStyle(color: AppColors.textSecondary, fontSize: 13)), + ], + ], + ), + ); + } + + return ListView.builder( + padding: const EdgeInsets.fromLTRB(16, 12, 16, 100), + itemCount: jobs.length, + itemBuilder: (ctx, i) { + final job = jobs[i]; + return Padding( + padding: const EdgeInsets.only(bottom: 10), + child: _PendingJobCard( + job: job, + onAccept: () => _acceptJob(job), + ), + ); + }, + ); + }, + ), + ); + } +} + +class _PendingJobCard extends StatefulWidget { + const _PendingJobCard({required this.job, required this.onAccept}); + final Job job; + final VoidCallback onAccept; + + @override + State<_PendingJobCard> createState() => _PendingJobCardState(); +} + +class _PendingJobCardState extends State<_PendingJobCard> { + bool _accepting = false; + + @override + Widget build(BuildContext context) { + final job = widget.job; + return Dismissible( + key: ValueKey(job.id), + direction: DismissDirection.endToStart, + background: Container( + alignment: Alignment.centerRight, + padding: const EdgeInsets.only(right: 20), + decoration: BoxDecoration( + color: AppColors.success, + borderRadius: BorderRadius.circular(14), + ), + child: const Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon(Icons.check_rounded, color: Colors.white, size: 28), + SizedBox(height: 4), + Text('Kabul Et', style: TextStyle(color: Colors.white, fontSize: 12, fontWeight: FontWeight.w600)), + ], + ), + ), + confirmDismiss: (_) async { + setState(() => _accepting = true); + try { + await LabJobsRepository.instance.acceptJob(job); + return true; + } catch (e) { + if (context.mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('Hata: $e')), + ); + } + return false; + } finally { + if (mounted) setState(() => _accepting = false); + } + }, + child: Material( + color: AppColors.surface, + borderRadius: BorderRadius.circular(14), + child: InkWell( + onTap: () => context.push('/lab/jobs/${job.id}'), + borderRadius: BorderRadius.circular(14), + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(14), + border: Border.all(color: AppColors.border), + boxShadow: [ + BoxShadow(color: Colors.black.withValues(alpha: 0.04), blurRadius: 8, offset: const Offset(0, 2)), + ], + ), + child: IntrinsicHeight( + child: Row( + children: [ + Container( + width: 4, + decoration: const BoxDecoration( + color: AppColors.pending, + borderRadius: BorderRadius.only( + topLeft: Radius.circular(14), + bottomLeft: Radius.circular(14), + ), + ), + ), + Expanded( + child: Padding( + padding: const EdgeInsets.all(14), + child: Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + job.patientCode, + style: const TextStyle(fontSize: 15, fontWeight: FontWeight.w700, color: AppColors.textPrimary), + ), + const SizedBox(height: 4), + Row( + children: [ + const Icon(Icons.local_hospital_outlined, size: 12, color: AppColors.textMuted), + const SizedBox(width: 4), + Expanded( + child: Text( + job.clinicName ?? 'Klinik', + style: const TextStyle(fontSize: 12, color: AppColors.textSecondary), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ), + ], + ), + const SizedBox(height: 6), + Row( + children: [ + Container( + padding: const EdgeInsets.symmetric(horizontal: 7, vertical: 3), + decoration: BoxDecoration( + color: AppColors.pendingBg, + borderRadius: BorderRadius.circular(6), + ), + child: Text( + job.prostheticType.label, + style: const TextStyle(fontSize: 11, color: AppColors.pending, fontWeight: FontWeight.w600), + ), + ), + if (job.dueDate != null) ...[ + const SizedBox(width: 6), + const Icon(Icons.calendar_today_outlined, size: 11, color: AppColors.textMuted), + const SizedBox(width: 3), + Text( + '${job.dueDate!.day.toString().padLeft(2, '0')}.${job.dueDate!.month.toString().padLeft(2, '0')}.${job.dueDate!.year}', + style: const TextStyle(fontSize: 11, color: AppColors.textMuted), + ), + ], + ], + ), + ], + ), + ), + const SizedBox(width: 8), + _accepting + ? const SizedBox( + width: 20, + height: 20, + child: CircularProgressIndicator(strokeWidth: 2, color: AppColors.success), + ) + : Container( + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 7), + decoration: BoxDecoration( + color: AppColors.successBg, + borderRadius: BorderRadius.circular(8), + border: Border.all(color: AppColors.success.withValues(alpha: 0.3)), + ), + child: const Text( + 'Kabul Et', + style: TextStyle(fontSize: 12, fontWeight: FontWeight.w700, color: AppColors.success), + ), + ), + ], + ), + ), + ), + ], + ), + ), + ), + ), + ), + ); + } +} + +class _LabJobsTab extends ConsumerStatefulWidget { + const _LabJobsTab({ + required this.status, + required this.searchQuery, + required this.sort, + this.onCountLoaded, + }); + + final String? status; // null = tüm statüsler + final String searchQuery; + final _JobSort sort; + final void Function(int)? onCountLoaded; + + @override + ConsumerState<_LabJobsTab> createState() => _LabJobsTabState(); +} + +class _LabJobsTabState extends ConsumerState<_LabJobsTab> { + late Future> _future; + late UnsubFn _unsub; + + @override + void initState() { + super.initState(); + _load(); + final tenantId = ref.read(authProvider).activeTenant!.tenant.id; + _unsub = RealtimeService.instance.watch( + 'jobs', + filter: 'lab_tenant_id="$tenantId"', + onEvent: (_) { if (mounted) _load(); }, + ); + } + + @override + void dispose() { + _unsub(); + super.dispose(); + } + + void _load() { + final tenantId = ref.read(authProvider).activeTenant!.tenant.id; + setState(() { + _future = LabJobsRepository.instance + .listInbound(tenantId, status: widget.status, limit: 50); + }); + } + + List _applyFilters(List jobs) { + var list = jobs; + + final q = widget.searchQuery.toLowerCase().trim(); + if (q.isNotEmpty) { + list = list.where((j) { + return j.patientCode.toLowerCase().contains(q) || + (j.clinicName?.toLowerCase().contains(q) ?? false) || + j.prostheticType.label.toLowerCase().contains(q) || + (j.currentStep?.label.toLowerCase().contains(q) ?? false); + }).toList(); + } + + final sorted = List.from(list); + switch (widget.sort) { + case _JobSort.newestFirst: + sorted.sort((a, b) => b.dateCreated.compareTo(a.dateCreated)); + case _JobSort.oldestFirst: + sorted.sort((a, b) => a.dateCreated.compareTo(b.dateCreated)); + case _JobSort.byDueDate: + sorted.sort((a, b) { + if (a.dueDate == null && b.dueDate == null) return 0; + if (a.dueDate == null) return 1; + if (b.dueDate == null) return -1; + return a.dueDate!.compareTo(b.dueDate!); + }); + case _JobSort.byType: + sorted.sort( + (a, b) => a.prostheticType.label.compareTo(b.prostheticType.label)); + } + return sorted; + } + + @override + Widget build(BuildContext context) { + return RefreshIndicator( + color: AppColors.accent, + onRefresh: () async => _load(), + child: FutureBuilder>( + future: _future, + builder: (ctx, snap) { + if (snap.connectionState == ConnectionState.waiting) { + return const Center( + child: CircularProgressIndicator(color: AppColors.accent)); + } + if (snap.hasError) { + return Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + width: 64, + height: 64, + decoration: BoxDecoration( + color: AppColors.cancelledBg, + borderRadius: BorderRadius.circular(16)), + child: const Icon(Icons.wifi_off_rounded, + color: AppColors.cancelled, size: 30), + ), + const SizedBox(height: 16), + Text('Hata: ${snap.error}', + style: + const TextStyle(color: AppColors.textSecondary)), + const SizedBox(height: 12), + FilledButton.icon( + onPressed: _load, + icon: const Icon(Icons.refresh_rounded, size: 18), + label: const Text('Tekrar Dene'), + ), + ], + ), + ); + } + + final all = snap.data!; + WidgetsBinding.instance.addPostFrameCallback((_) { + widget.onCountLoaded?.call(all.length); + }); + + final jobs = _applyFilters(all); + + if (jobs.isEmpty) { + return Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + width: 72, + height: 72, + decoration: BoxDecoration( + color: AppColors.inProgressBg, + borderRadius: BorderRadius.circular(20)), + child: const Icon(Icons.work_off_outlined, + color: AppColors.inProgress, size: 32), + ), + const SizedBox(height: 16), + Text( + widget.searchQuery.isNotEmpty + ? 'Sonuç bulunamadı' + : 'Henüz iş yok', + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + color: AppColors.textPrimary), + ), + ], + ), + ); + } + + return ListView.builder( + padding: const EdgeInsets.fromLTRB(16, 12, 16, 24), + itemCount: jobs.length, + itemBuilder: (ctx, i) { + final job = jobs[i]; + return Padding( + padding: const EdgeInsets.only(bottom: 10), + child: _LabJobCard( + job: job, + onTap: () => context.push('/lab/jobs/${job.id}'), + ), + ); + }, + ); + }, + ), + ); + } +} + +class _LabJobCard extends StatelessWidget { + const _LabJobCard({required this.job, required this.onTap}); + + final Job job; + final VoidCallback onTap; + + @override + Widget build(BuildContext context) { + final isOverdue = + job.dueDate != null && job.dueDate!.isBefore(DateTime.now()); + final accentColor = _statusColor(job.status); + + return Semantics( + label: job.patientCode, + button: true, + excludeSemantics: true, + child: Material( + color: AppColors.surface, + borderRadius: BorderRadius.circular(14), + child: InkWell( + onTap: onTap, + borderRadius: BorderRadius.circular(14), + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(14), + border: Border.all(color: AppColors.border), + boxShadow: [ + BoxShadow( + color: Colors.black.withValues(alpha: 0.04), + blurRadius: 8, + offset: const Offset(0, 2), + ), + ], + ), + child: IntrinsicHeight( + child: Row( + children: [ + Container( + width: 4, + decoration: BoxDecoration( + color: accentColor, + borderRadius: const BorderRadius.only( + topLeft: Radius.circular(14), + bottomLeft: Radius.circular(14), + ), + ), + ), + Expanded( + child: Padding( + padding: const EdgeInsets.all(14), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Expanded( + child: Text( + job.patientCode, + style: const TextStyle( + fontSize: 15, + fontWeight: FontWeight.w700, + color: AppColors.textPrimary, + ), + ), + ), + if (job.currentStep != null) + Container( + padding: const EdgeInsets.symmetric( + horizontal: 8, vertical: 3), + decoration: BoxDecoration( + color: AppColors.inProgressBg, + borderRadius: BorderRadius.circular(8), + ), + child: Text( + job.currentStep!.label, + style: const TextStyle( + color: AppColors.inProgress, + fontSize: 11, + fontWeight: FontWeight.w600, + ), + ), + ), + ], + ), + const SizedBox(height: 5), + Row( + children: [ + const Icon(Icons.local_hospital_outlined, + size: 12, color: AppColors.textMuted), + const SizedBox(width: 4), + Expanded( + child: Text( + job.clinicName ?? 'Klinik', + style: const TextStyle( + fontSize: 12, + color: AppColors.textSecondary), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ), + ], + ), + const SizedBox(height: 5), + Row( + children: [ + Container( + padding: const EdgeInsets.symmetric( + horizontal: 6, vertical: 2), + decoration: BoxDecoration( + color: AppColors.surfaceVariant, + borderRadius: BorderRadius.circular(6), + ), + child: Text( + job.prostheticType.label, + style: const TextStyle( + fontSize: 11, + color: AppColors.textSecondary, + fontWeight: FontWeight.w500, + ), + ), + ), + if (job.dueDate != null) ...[ + const SizedBox(width: 8), + Icon(Icons.calendar_today_outlined, + size: 11, + color: isOverdue + ? AppColors.cancelled + : AppColors.textMuted), + const SizedBox(width: 3), + Text( + _fmt(job.dueDate!), + style: TextStyle( + fontSize: 11, + color: isOverdue + ? AppColors.cancelled + : AppColors.textMuted, + fontWeight: isOverdue + ? FontWeight.w600 + : FontWeight.normal, + ), + ), + ], + ], + ), + ], + ), + ), + ), + const Padding( + padding: EdgeInsets.only(right: 10), + child: Icon(Icons.chevron_right, + color: AppColors.textMuted, size: 20), + ), + ], + ), + ), + ), + ), + ), + ); + } + + String _fmt(DateTime d) => + '${d.day.toString().padLeft(2, '0')}.${d.month.toString().padLeft(2, '0')}.${d.year}'; + + Color _statusColor(JobStatus status) { + switch (status) { + case JobStatus.pending: + return AppColors.pending; + case JobStatus.inProgress: + return AppColors.inProgress; + case JobStatus.sent: + return AppColors.accent; + case JobStatus.delivered: + return AppColors.success; + case JobStatus.cancelled: + return AppColors.cancelled; + } + } +} diff --git a/lib/features/lab/jobs/lab_job_detail_screen.dart b/lib/features/lab/jobs/lab_job_detail_screen.dart new file mode 100644 index 0000000..e46d171 --- /dev/null +++ b/lib/features/lab/jobs/lab_job_detail_screen.dart @@ -0,0 +1,764 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:go_router/go_router.dart'; +import '../../../core/providers/auth_provider.dart'; +import '../../../core/services/realtime_service.dart'; +import '../../../core/theme/app_theme.dart'; +import '../../../models/job.dart'; +import '../../../models/job_file.dart'; +import '../../../features/shared/job_files_repository.dart'; +import '../../../features/shared/job_files_panel.dart'; +import '../../../core/services/job_history_service.dart'; +import 'lab_jobs_repository.dart'; + +// ── Adaptive sheet helper ──────────────────────────────────────────────────── + +void _showAdaptive(BuildContext context, Widget content) { + final isDesktop = MediaQuery.sizeOf(context).width > AppLayout.sidebarBreakpoint; + if (isDesktop) { + showDialog( + context: context, + builder: (_) => Dialog( + shape: + RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)), + child: ConstrainedBox( + constraints: const BoxConstraints(maxWidth: 560), + child: content, + ), + ), + ); + } else { + showModalBottomSheet( + context: context, + isScrollControlled: true, + backgroundColor: Colors.transparent, + builder: (_) => content, + ); + } +} + +class LabJobDetailScreen extends ConsumerStatefulWidget { + const LabJobDetailScreen({super.key, required this.jobId}); + final String jobId; + + @override + ConsumerState createState() => _LabJobDetailScreenState(); +} + +class _LabJobDetailScreenState extends ConsumerState { + Job? _job; + bool _loadingJob = false; + String? _loadError; + bool _isActing = false; + late Future> _filesFuture; + late UnsubFn _unsub; + + @override + void initState() { + super.initState(); + _load(); + _loadFiles(); + _unsub = RealtimeService.instance.watch( + 'jobs', + topic: widget.jobId, + onEvent: (_) { if (mounted && !_isActing) _load(); }, + ); + } + + @override + void dispose() { + _unsub(); + super.dispose(); + } + + Future _load() async { + setState(() { _loadingJob = true; _loadError = null; }); + try { + final job = await LabJobsRepository.instance.getJob(widget.jobId); + if (mounted) setState(() { _job = job; _loadingJob = false; }); + } catch (e) { + if (mounted) setState(() { _loadError = e.toString(); _loadingJob = false; }); + } + } + + void _loadFiles() { + setState(() { + _filesFuture = JobFilesRepository.instance.listForJob(widget.jobId); + }); + } + + Future _cancelJob(Job job) async { + final confirmed = await showDialog( + context: context, + builder: (ctx) => AlertDialog( + title: const Text('İşi İptal Et'), + content: const Text('Bu iş geri alınamaz şekilde iptal edilir. Onaylıyor musunuz?'), + actions: [ + TextButton(onPressed: () => Navigator.pop(ctx, false), child: const Text('Vazgeç')), + FilledButton( + style: FilledButton.styleFrom(backgroundColor: AppColors.cancelled), + onPressed: () => Navigator.pop(ctx, true), + child: const Text('İptal Et'), + ), + ], + ), + ); + if (confirmed != true || !mounted) return; + setState(() => _isActing = true); + try { + final updated = await LabJobsRepository.instance.cancelJob(job.id, job); + if (mounted) { + setState(() { _job = _job!.copyWith(status: updated.status); _isActing = false; }); + ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('İş iptal edildi.'))); + } + } catch (e) { + if (mounted) { + setState(() => _isActing = false); + ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('Hata: $e'))); + } + } + } + + Future _acceptJob(Job job) async { + setState(() => _isActing = true); + try { + final updated = await LabJobsRepository.instance.acceptJob(job); + if (mounted) { + setState(() { _job = updated.copyWith(clinicName: job.clinicName, labName: job.labName); _isActing = false; }); + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('İş kabul edildi')), + ); + } + } catch (e) { + if (mounted) { + setState(() => _isActing = false); + ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('Hata: $e'))); + } + } + } + + void _showHandToClinicSheet(Job job) { + _showAdaptive( + context, + _HandToClinicSheet( + job: job, + onDone: (Job updated) { + if (mounted) setState(() => _job = updated.copyWith(clinicName: job.clinicName, labName: job.labName)); + }, + ), + ); + } + + Color _statusColor(JobStatus status) { + return switch (status) { + JobStatus.pending => AppColors.pending, + JobStatus.inProgress => AppColors.inProgress, + JobStatus.sent => AppColors.accent, + JobStatus.delivered => AppColors.success, + JobStatus.cancelled => AppColors.cancelled, + }; + } + + Color _statusBg(JobStatus status) { + return switch (status) { + JobStatus.pending => AppColors.pendingBg, + JobStatus.inProgress => AppColors.inProgressBg, + JobStatus.sent => AppColors.inProgressBg, + JobStatus.delivered => AppColors.successBg, + JobStatus.cancelled => AppColors.cancelledBg, + }; + } + + String _formatDate(DateTime dt, {bool withTime = false}) { + final d = '${dt.day.toString().padLeft(2, '0')}.${dt.month.toString().padLeft(2, '0')}.${dt.year}'; + if (!withTime || (dt.hour == 0 && dt.minute == 0)) return d; + return '$d ${dt.hour.toString().padLeft(2, '0')}:${dt.minute.toString().padLeft(2, '0')}'; + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('İş Detayı'), + leading: IconButton( + icon: const Icon(Icons.arrow_back), + onPressed: () => context.pop(), + ), + ), + body: _buildBody(), + ); + } + + Widget _buildBody() { + if (_loadingJob && _job == null) { + return const Center( + child: CircularProgressIndicator(color: AppColors.accent)); + } + if (_loadError != null && _job == null) { + return Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + width: 64, + height: 64, + decoration: BoxDecoration( + color: AppColors.cancelledBg, + borderRadius: BorderRadius.circular(16)), + child: const Icon(Icons.wifi_off_rounded, + color: AppColors.cancelled, size: 30), + ), + const SizedBox(height: 16), + Text('Hata: $_loadError', + style: const TextStyle(color: AppColors.textSecondary)), + const SizedBox(height: 12), + FilledButton.icon( + onPressed: _load, + icon: const Icon(Icons.refresh_rounded, size: 18), + label: const Text('Tekrar Dene'), + ), + ], + ), + ); + } + + if (_job == null) return const SizedBox.shrink(); + + { + final job = _job!; + final membership = ref.read(authProvider).activeTenant; + final isDeliveryOnly = membership?.isDeliveryOnly ?? false; + final canCancelJobs = membership?.canCancelJobs ?? true; + final canSendToClinic = !isDeliveryOnly && + job.status == JobStatus.inProgress && + job.location == JobLocation.atLab; + final canAccept = !isDeliveryOnly && job.status == JobStatus.pending; + + return ListView( + padding: const EdgeInsets.all(16), + children: [ + // Header card + Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: AppColors.surface, + borderRadius: BorderRadius.circular(16), + border: Border.all(color: AppColors.border), + boxShadow: [ + BoxShadow( + color: Colors.black.withValues(alpha: 0.05), + blurRadius: 12, + offset: const Offset(0, 4)) + ], + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Expanded( + child: Text( + job.patientCode, + style: Theme.of(context) + .textTheme + .headlineSmall + ?.copyWith(fontWeight: FontWeight.bold, + color: AppColors.textPrimary), + ), + ), + Container( + padding: const EdgeInsets.symmetric( + horizontal: 12, vertical: 5), + decoration: BoxDecoration( + color: _statusBg(job.status), + borderRadius: BorderRadius.circular(10), + ), + child: Text( + job.status.label, + style: TextStyle( + color: _statusColor(job.status), + fontWeight: FontWeight.w600, + fontSize: 13, + ), + ), + ), + ], + ), + const SizedBox(height: 12), + _InfoRow( + icon: Icons.business, + label: 'Klinik', + value: job.clinicName ?? '-'), + _InfoRow( + icon: Icons.medical_services_outlined, + label: 'Protez Tipi', + value: job.prostheticType.label), + _InfoRow( + icon: Icons.format_list_numbered, + label: 'Üye Sayısı', + value: '${job.memberCount} üye'), + if (job.color != null) + _InfoRow( + icon: Icons.color_lens_outlined, + label: 'Renk', + value: job.color!), + if (job.dueDate != null) + _InfoRow( + icon: Icons.calendar_today, + label: 'Teslim Tarihi', + value: _formatDate(job.dueDate!, withTime: true), + valueColor: job.dueDate!.isBefore(DateTime.now()) + ? AppColors.cancelled + : null), + _InfoRow( + icon: Icons.add_circle_outline, + label: 'Oluşturulma', + value: _formatDate(job.dateCreated)), + if (job.price != null && job.currency != null) + _InfoRow( + icon: Icons.attach_money, + label: 'Fiyat', + value: + '${job.price!.toStringAsFixed(2)} ${job.currency}'), + if (job.description != null && + job.description!.isNotEmpty) + _InfoRow( + icon: Icons.notes, + label: 'Açıklama', + value: job.description!), + ], + ), + ), + + const SizedBox(height: 16), + + // Stepper + Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: AppColors.surface, + borderRadius: BorderRadius.circular(16), + border: Border.all(color: AppColors.border), + boxShadow: [ + BoxShadow( + color: Colors.black.withValues(alpha: 0.05), + blurRadius: 12, + offset: const Offset(0, 4)) + ], + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Text( + 'İş Adımları', + style: Theme.of(context) + .textTheme + .titleMedium + ?.copyWith(fontWeight: FontWeight.w600, + color: AppColors.textPrimary), + ), + const SizedBox(width: 8), + Container( + padding: const EdgeInsets.symmetric( + horizontal: 8, vertical: 2), + decoration: BoxDecoration( + color: job.provaRequired + ? AppColors.inProgressBg + : AppColors.successBg, + borderRadius: BorderRadius.circular(6), + ), + child: Text( + job.provaRequired ? 'Provalı' : 'Provasız', + style: TextStyle( + fontSize: 11, + fontWeight: FontWeight.w600, + color: job.provaRequired + ? AppColors.inProgress + : AppColors.success, + ), + ), + ), + ], + ), + const SizedBox(height: 16), + _JobStepper( + steps: job.stepTemplate, + currentStep: job.currentStep, + historyFuture: JobHistoryService.instance + .listForJob(job.id), + ), + ], + ), + ), + + const SizedBox(height: 24), + + // Action buttons + if (_isActing) + const Padding( + padding: EdgeInsets.symmetric(vertical: 8), + child: Center(child: CircularProgressIndicator(color: AppColors.accent)), + ) + else ...[ + if (canAccept) + FilledButton.icon( + onPressed: () => _acceptJob(job), + icon: const Icon(Icons.check_circle_outline), + label: const Text('Kabul Et'), + style: FilledButton.styleFrom( + minimumSize: const Size.fromHeight(52), + backgroundColor: AppColors.success, + ), + ), + + if (canSendToClinic) + FilledButton.icon( + onPressed: () => _showHandToClinicSheet(job), + icon: const Icon(Icons.send_outlined), + label: Text( + (job.isLastStep) + ? 'Son Prova - Teslime Gönder' + : 'Prova için Kliniğe Gönder', + ), + style: FilledButton.styleFrom( + minimumSize: const Size.fromHeight(52), + backgroundColor: (job.isLastStep) + ? AppColors.success + : AppColors.inProgress, + ), + ), + + if (canCancelJobs && job.status == JobStatus.pending) ...[ + const SizedBox(height: 12), + OutlinedButton.icon( + onPressed: () => _cancelJob(job), + icon: const Icon(Icons.close_rounded), + label: const Text('İşi İptal Et'), + style: OutlinedButton.styleFrom( + minimumSize: const Size.fromHeight(50), + foregroundColor: AppColors.cancelled, + side: const BorderSide(color: AppColors.cancelled), + ), + ), + ], + ], + + const SizedBox(height: 20), + + JobFilesPanel( + job: job, + filesFuture: _filesFuture, + onRefresh: _loadFiles, + ), + + const SizedBox(height: 16), + ], + ); + } + } +} + +// ── Hand to Clinic Sheet ───────────────────────────────────────────────────── + +class _HandToClinicSheet extends StatefulWidget { + const _HandToClinicSheet({required this.job, required this.onDone}); + final Job job; + final void Function(Job updatedJob) onDone; + + @override + State<_HandToClinicSheet> createState() => _HandToClinicSheetState(); +} + +class _HandToClinicSheetState extends State<_HandToClinicSheet> { + final _noteController = TextEditingController(); + bool _sending = false; + + @override + void dispose() { + _noteController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final isDesktop = MediaQuery.sizeOf(context).width > AppLayout.sidebarBreakpoint; + final isLast = widget.job.isLastStep; + final stepLabel = widget.job.currentStep?.label ?? ''; + final buttonLabel = isLast + ? (widget.job.provaRequired ? 'Son Prova · Teslime Gönder' : 'Teslime Gönder') + : '$stepLabel için Kliniğe Gönder'; + final buttonColor = isLast ? AppColors.success : AppColors.inProgress; + + return Container( + decoration: BoxDecoration( + color: AppColors.surface, + borderRadius: BorderRadius.vertical( + top: isDesktop ? Radius.zero : const Radius.circular(20), + ), + ), + padding: EdgeInsets.only( + left: 20, + right: 20, + top: 24, + bottom: isDesktop + ? 24 + : MediaQuery.of(context).viewInsets.bottom + 24, + ), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Text( + buttonLabel, + style: Theme.of(context) + .textTheme + .titleMedium + ?.copyWith(fontWeight: FontWeight.bold, + color: AppColors.textPrimary), + ), + const SizedBox(height: 8), + Text( + isLast + ? 'İş teslim edilecek olarak işaretlenecek.' + : 'İş klinikteki prova için gönderilecek.', + style: const TextStyle(color: AppColors.textSecondary), + ), + const SizedBox(height: 16), + TextField( + controller: _noteController, + decoration: const InputDecoration( + labelText: 'Not (isteğe bağlı)', + hintText: 'Klinik için not ekleyin...', + ), + maxLines: 3, + ), + const SizedBox(height: 16), + FilledButton( + onPressed: _sending + ? null + : () async { + setState(() => _sending = true); + final navigator = Navigator.of(context); + final messenger = ScaffoldMessenger.of(context); + try { + final updated = await LabJobsRepository.instance.handToClinic( + widget.job.id, + widget.job, + note: _noteController.text.trim().isEmpty + ? null + : _noteController.text.trim(), + ); + navigator.pop(); + messenger.showSnackBar( + SnackBar( + content: Text(isLast + ? 'İş teslim için gönderildi' + : 'Prova için klinik\'e gönderildi')), + ); + if (context.mounted) widget.onDone(updated); + } catch (e) { + if (context.mounted) { + setState(() => _sending = false); + messenger.showSnackBar( + SnackBar(content: Text('Hata: $e')), + ); + } + } + }, + style: FilledButton.styleFrom( + minimumSize: const Size.fromHeight(48), + backgroundColor: buttonColor, + ), + child: _sending + ? const SizedBox( + width: 20, + height: 20, + child: CircularProgressIndicator( + strokeWidth: 2, color: Colors.white), + ) + : Text(buttonLabel), + ), + ], + ), + ); + } +} + +// ── Info Row ───────────────────────────────────────────────────────────────── + +class _InfoRow extends StatelessWidget { + const _InfoRow({ + required this.icon, + required this.label, + required this.value, + this.valueColor, + }); + final IconData icon; + final String label; + final String value; + final Color? valueColor; + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(bottom: 10), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Icon(icon, size: 18, color: AppColors.textMuted), + const SizedBox(width: 10), + SizedBox( + width: 110, + child: Text( + label, + style: const TextStyle(color: AppColors.textSecondary, fontSize: 13), + ), + ), + Expanded( + child: Text( + value, + style: TextStyle( + fontWeight: FontWeight.w500, + color: valueColor ?? AppColors.textPrimary, + fontSize: 14, + ), + ), + ), + ], + ), + ); + } +} + +// ── Job Stepper ─────────────────────────────────────────────────────────────── + +class _JobStepper extends StatelessWidget { + const _JobStepper({ + required this.steps, + required this.currentStep, + required this.historyFuture, + }); + final List steps; + final JobStep? currentStep; + final Future> historyFuture; + + @override + Widget build(BuildContext context) { + return FutureBuilder>( + future: historyFuture, + builder: (ctx, snap) { + final history = snap.data ?? []; + // Revizyon sayısı per adım + final Map revisionCounts = {}; + for (final e in history) { + if (e.action == JobHistoryAction.revisionRequested && e.step != null) { + revisionCounts[e.step!] = (revisionCounts[e.step!] ?? 0) + 1; + } + } + final currentIndex = + currentStep != null ? steps.indexOf(currentStep!) : -1; + + return Column( + children: List.generate(steps.length, (i) { + final step = steps[i]; + final isCompleted = i < currentIndex; + final isCurrent = i == currentIndex; + final isLastItem = i == steps.length - 1; + final revCount = revisionCounts[step] ?? 0; + + Color dotColor; + IconData dotIcon; + if (isCompleted) { + dotColor = AppColors.success; + dotIcon = Icons.check_circle; + } else if (isCurrent) { + dotColor = AppColors.inProgress; + dotIcon = Icons.radio_button_checked; + } else { + dotColor = AppColors.muted; + dotIcon = Icons.radio_button_unchecked; + } + + return Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Column( + children: [ + Icon(dotIcon, color: dotColor, size: 24), + if (!isLastItem) + Container( + width: 2, + height: 44, + color: i < currentIndex + ? AppColors.success.withValues(alpha: 0.35) + : AppColors.border, + ), + ], + ), + const SizedBox(width: 12), + Expanded( + child: Padding( + padding: const EdgeInsets.only(top: 2, bottom: 12), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Text( + step.label, + style: TextStyle( + fontWeight: isCurrent + ? FontWeight.bold + : FontWeight.normal, + color: isCompleted + ? AppColors.success + : isCurrent + ? AppColors.inProgress + : AppColors.textMuted, + fontSize: 15, + ), + ), + if (revCount > 0) ...[ + const SizedBox(width: 6), + Container( + padding: const EdgeInsets.symmetric( + horizontal: 6, vertical: 1), + decoration: BoxDecoration( + color: AppColors.cancelledBg, + borderRadius: BorderRadius.circular(6), + ), + child: Text( + '$revCount revizyon', + style: const TextStyle( + fontSize: 10, + fontWeight: FontWeight.w700, + color: AppColors.cancelled, + ), + ), + ), + ], + ], + ), + if (isCurrent) + Text( + step.description, + style: const TextStyle( + fontSize: 12, + color: AppColors.textSecondary, + ), + ), + ], + ), + ), + ), + ], + ); + }), + ); + }, + ); + } +} + diff --git a/lib/features/lab/jobs/lab_jobs_inbound_screen.dart b/lib/features/lab/jobs/lab_jobs_inbound_screen.dart new file mode 100644 index 0000000..b8dea87 --- /dev/null +++ b/lib/features/lab/jobs/lab_jobs_inbound_screen.dart @@ -0,0 +1,335 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import '../../../core/providers/auth_provider.dart'; +import '../../../core/services/realtime_service.dart'; +import '../../../core/theme/app_theme.dart'; +import '../../../core/widgets/gradient_app_bar.dart'; +import '../../../models/job.dart'; +import 'lab_jobs_repository.dart'; + +class LabJobsInboundScreen extends ConsumerStatefulWidget { + const LabJobsInboundScreen({super.key}); + + @override + ConsumerState createState() => + _LabJobsInboundScreenState(); +} + +class _LabJobsInboundScreenState extends ConsumerState { + late Future> _future; + bool _acceptingAll = false; + late UnsubFn _unsub; + + @override + void initState() { + super.initState(); + _load(); + final tenantId = ref.read(authProvider).activeTenant!.tenant.id; + _unsub = RealtimeService.instance.watch( + 'jobs', + filter: "lab_tenant_id='$tenantId'", + onEvent: (_) { if (mounted) _load(); }, + ); + } + + @override + void dispose() { + _unsub(); + super.dispose(); + } + + void _load() { + final tenantId = ref.read(authProvider).activeTenant!.tenant.id; + setState(() { + _future = + LabJobsRepository.instance.listInbound(tenantId, status: 'pending'); + }); + } + + Future _acceptJob(Job job) async { + try { + await LabJobsRepository.instance.acceptJob(job); + _load(); + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('İş kabul edildi')), + ); + } + } catch (e) { + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('Hata: $e')), + ); + } + } + } + + Future _bulkAccept() async { + final tenantId = ref.read(authProvider).activeTenant!.tenant.id; + setState(() => _acceptingAll = true); + try { + await LabJobsRepository.instance.bulkAcceptPending(tenantId); + _load(); + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('Tüm işler kabul edildi')), + ); + } + } catch (e) { + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('Hata: $e')), + ); + } + } finally { + if (mounted) setState(() => _acceptingAll = false); + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: AppColors.background, + appBar: GradientAppBar( + title: 'Gelen İşler', + category: 'LABORATUVAR', + ), + floatingActionButton: FloatingActionButton.extended( + onPressed: _acceptingAll ? null : _bulkAccept, + icon: _acceptingAll + ? const SizedBox( + width: 18, + height: 18, + child: CircularProgressIndicator( + strokeWidth: 2, color: Colors.white), + ) + : const Icon(Icons.done_all), + label: + Text(_acceptingAll ? 'Kabul ediliyor...' : 'Tümünü Kabul Et'), + backgroundColor: AppColors.pending, + foregroundColor: Colors.white, + ), + body: RefreshIndicator( + onRefresh: () async => _load(), + child: FutureBuilder>( + future: _future, + builder: (ctx, snap) { + if (snap.connectionState == ConnectionState.waiting) { + return const Center(child: CircularProgressIndicator()); + } + if (snap.hasError) { + return Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text('Hata: ${snap.error}'), + const SizedBox(height: 12), + FilledButton( + onPressed: _load, + child: const Text('Tekrar Dene'), + ), + ], + ), + ); + } + + final jobs = snap.data!; + + if (jobs.isEmpty) { + return Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Icon(Icons.inbox_outlined, size: 64, color: AppColors.textMuted), + const SizedBox(height: 16), + Text( + 'Bekleyen iş yok', + style: TextStyle( + fontSize: 16, color: AppColors.textSecondary), + ), + ], + ), + ); + } + + return ListView.builder( + padding: const EdgeInsets.fromLTRB(16, 16, 16, 80), + itemCount: jobs.length, + itemBuilder: (ctx, i) { + final job = jobs[i]; + return _InboundJobCard( + job: job, + onAccept: () => _acceptJob(job), + ); + }, + ); + }, + ), + ), + ); + } +} + +class _InboundJobCard extends StatefulWidget { + const _InboundJobCard({required this.job, required this.onAccept}); + final Job job; + final VoidCallback onAccept; + + @override + State<_InboundJobCard> createState() => _InboundJobCardState(); +} + +class _InboundJobCardState extends State<_InboundJobCard> { + bool _accepting = false; + + String _formatDate(DateTime dt) => + '${dt.day.toString().padLeft(2, '0')}.${dt.month.toString().padLeft(2, '0')}.${dt.year}'; + + @override + Widget build(BuildContext context) { + final job = widget.job; + return Semantics( + label: job.patientCode, + button: true, + excludeSemantics: true, + child: Dismissible( + key: ValueKey(job.id), + direction: DismissDirection.endToStart, + background: Container( + alignment: Alignment.centerRight, + padding: const EdgeInsets.only(right: 20), + margin: const EdgeInsets.only(bottom: 8), + decoration: BoxDecoration( + color: AppColors.success, + borderRadius: BorderRadius.circular(12), + ), + child: const Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon(Icons.check, color: Colors.white, size: 28), + SizedBox(height: 4), + Text('Kabul Et', + style: TextStyle(color: Colors.white, fontSize: 12)), + ], + ), + ), + confirmDismiss: (_) async { + setState(() => _accepting = true); + try { + await LabJobsRepository.instance.acceptJob(job); + return true; + } catch (e) { + if (context.mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('Hata: $e')), + ); + } + return false; + } finally { + if (mounted) setState(() => _accepting = false); + } + }, + child: Card( + margin: const EdgeInsets.only(bottom: 8), + child: Padding( + padding: const EdgeInsets.all(14), + child: Row( + children: [ + CircleAvatar( + backgroundColor: AppColors.pendingBg, + child: const Icon(Icons.assignment_outlined, + color: AppColors.pending), + ), + const SizedBox(width: 12), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + job.patientCode, + style: const TextStyle(fontWeight: FontWeight.bold), + ), + const SizedBox(height: 2), + Text( + job.clinicName ?? 'Klinik', + style: TextStyle( + color: AppColors.textSecondary, fontSize: 13), + ), + const SizedBox(height: 4), + Row( + children: [ + _Chip( + label: job.prostheticType.label, + color: AppColors.inProgressBg, + textColor: AppColors.inProgress, + ), + const SizedBox(width: 6), + _Chip( + label: '${job.memberCount} üye', + color: AppColors.background, + textColor: AppColors.textSecondary, + ), + ], + ), + const SizedBox(height: 4), + Text( + _formatDate(job.dateCreated), + style: TextStyle( + color: AppColors.textMuted, fontSize: 12), + ), + ], + ), + ), + const SizedBox(width: 8), + _accepting + ? const SizedBox( + width: 24, + height: 24, + child: CircularProgressIndicator(strokeWidth: 2), + ) + : FilledButton( + onPressed: widget.onAccept, + style: FilledButton.styleFrom( + backgroundColor: AppColors.success, + padding: const EdgeInsets.symmetric( + horizontal: 12, vertical: 8), + minimumSize: Size.zero, + tapTargetSize: MaterialTapTargetSize.shrinkWrap, + ), + child: const Text('Kabul Et', + style: TextStyle(fontSize: 13)), + ), + ], + ), + ), + ), + ), + ); + } +} + +class _Chip extends StatelessWidget { + const _Chip( + {required this.label, + required this.color, + required this.textColor}); + final String label; + final Color color; + final Color textColor; + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2), + decoration: BoxDecoration( + color: color, + borderRadius: BorderRadius.circular(10), + ), + child: Text(label, + style: TextStyle( + color: textColor, + fontSize: 11, + fontWeight: FontWeight.w500)), + ); + } +} diff --git a/lib/features/lab/jobs/lab_jobs_repository.dart b/lib/features/lab/jobs/lab_jobs_repository.dart new file mode 100644 index 0000000..711d2c2 --- /dev/null +++ b/lib/features/lab/jobs/lab_jobs_repository.dart @@ -0,0 +1,131 @@ +import 'dart:async'; +import 'package:pocketbase/pocketbase.dart'; +import '../../../core/api/pocketbase_client.dart'; +import '../../../core/services/job_history_service.dart'; +import '../../../models/job.dart'; + +const _listExpand = 'clinic_tenant_id,lab_tenant_id'; +const _detailExpand = 'clinic_tenant_id,lab_tenant_id,patient_id'; + +class LabJobsRepository { + LabJobsRepository._(); + static final instance = LabJobsRepository._(); + + PocketBase get _pb => PocketBaseClient.instance.pb; + + Future> listInbound( + String labTenantId, { + String? status, + int page = 1, + int limit = 30, + }) async { + final filterParts = ['lab_tenant_id = "$labTenantId"']; + if (status != null) filterParts.add('status = "$status"'); + + final result = await _pb.collection('jobs').getList( + page: page, + perPage: limit, + filter: filterParts.join(' && '), + expand: _listExpand, + ); + return (result.items.map((r) => Job.fromJson(r.toJson())).toList() + ..sort((a, b) => b.dateCreated.compareTo(a.dateCreated))); + } + + Future> listInProgress(String labTenantId, {int limit = 50, String? location}) async { + final filterParts = ['lab_tenant_id = "$labTenantId"', 'status = "in_progress"']; + if (location != null) filterParts.add('location = "$location"'); + final result = await _pb.collection('jobs').getList( + perPage: limit, + filter: filterParts.join(' && '), + expand: _listExpand, + ); + return (result.items.map((r) => Job.fromJson(r.toJson())).toList() + ..sort((a, b) { + if (a.dueDate == null && b.dueDate == null) return b.dateCreated.compareTo(a.dateCreated); + if (a.dueDate == null) return 1; + if (b.dueDate == null) return -1; + final cmp = a.dueDate!.compareTo(b.dueDate!); + return cmp != 0 ? cmp : b.dateCreated.compareTo(a.dateCreated); + })); + } + + Future getJob(String jobId) async { + final record = await _pb.collection('jobs').getOne(jobId, expand: _detailExpand); + return Job.fromJson(record.toJson()); + } + + Future acceptJob(Job pendingJob) async { + final firstStep = pendingJob.stepTemplate.first; + final record = await _pb.collection('jobs').update(pendingJob.id, body: { + 'status': 'in_progress', + 'current_step': firstStep.value, + 'location': 'at_lab', + }); + final job = Job.fromJson(record.toJson()); + unawaited(JobHistoryService.instance.append( + jobId: pendingJob.id, + clinicTenantId: job.clinicTenantId, + labTenantId: job.labTenantId, + action: JobHistoryAction.accepted, + step: firstStep, + )); + return job; + } + + Future handToClinic(String jobId, Job job, {String? note}) async { + final isFinal = job.currentStep == JobStep.cilaBitim; + final patch = isFinal + ? {'status': 'sent', 'location': 'at_clinic'} + : {'location': 'at_clinic'}; + + final record = await _pb.collection('jobs').update(jobId, body: patch); + final updated = Job.fromJson(record.toJson()); + unawaited(JobHistoryService.instance.append( + jobId: jobId, + clinicTenantId: job.clinicTenantId, + labTenantId: job.labTenantId, + action: JobHistoryAction.handedToClinic, + step: job.currentStep, + note: note, + )); + return updated; + } + + Future cancelJob(String jobId, Job job) async { + final record = await _pb.collection('jobs').update(jobId, body: { + 'status': 'cancelled', + }); + unawaited(JobHistoryService.instance.append( + jobId: jobId, + clinicTenantId: job.clinicTenantId, + labTenantId: job.labTenantId, + action: JobHistoryAction.cancelled, + step: job.currentStep, + )); + return Job.fromJson(record.toJson()); + } + + Future bulkAcceptPending(String labTenantId) async { + final pending = await listInbound(labTenantId, status: 'pending', limit: 200); + await Future.wait(pending.map((j) => acceptJob(j))); + } + + Future countByStatus(String labTenantId, String? status) async { + final filter = status != null + ? 'lab_tenant_id = "$labTenantId" && status = "$status"' + : 'lab_tenant_id = "$labTenantId"'; + final r = await _pb.collection('jobs').getList(perPage: 1, filter: filter); + return r.totalItems; + } + + Future countDelivered(String labTenantId, {DateTime? from, DateTime? to}) async { + final parts = ['lab_tenant_id = "$labTenantId"', 'status = "delivered"']; + if (from != null) parts.add('updated >= "${_date(from)}"'); + if (to != null) parts.add('updated < "${_date(to)}"'); + final r = await _pb.collection('jobs').getList(perPage: 1, filter: parts.join(' && ')); + return r.totalItems; + } + + static String _date(DateTime d) => d.toIso8601String().split('T').first; +} diff --git a/lib/features/lab/products/lab_products_repository.dart b/lib/features/lab/products/lab_products_repository.dart new file mode 100644 index 0000000..f00acb4 --- /dev/null +++ b/lib/features/lab/products/lab_products_repository.dart @@ -0,0 +1,39 @@ +import 'package:pocketbase/pocketbase.dart'; +import '../../../core/api/pocketbase_client.dart'; +import '../../../models/prosthetic_product.dart'; + +class LabProductsRepository { + LabProductsRepository._(); + static final instance = LabProductsRepository._(); + + PocketBase get _pb => PocketBaseClient.instance.pb; + + Future> listProducts( + String labTenantId, { + bool? isActive, + }) async { + final filterParts = ['lab_tenant_id = "$labTenantId"']; + if (isActive != null) filterParts.add('is_active = $isActive'); + + final result = await _pb.collection('prosthetic_products').getList( + filter: filterParts.join(' && '), + perPage: 200, + ); + return (result.items.map((r) => ProstheticProduct.fromJson(r.toJson())).toList() + ..sort((a, b) => a.name.compareTo(b.name))); + } + + Future createProduct(ProstheticProduct product) async { + final record = await _pb.collection('prosthetic_products').create(body: product.toJson()); + return ProstheticProduct.fromJson(record.toJson()); + } + + Future updateProduct(String id, Map patch) async { + final record = await _pb.collection('prosthetic_products').update(id, body: patch); + return ProstheticProduct.fromJson(record.toJson()); + } + + Future deleteProduct(String id) async { + await _pb.collection('prosthetic_products').delete(id); + } +} diff --git a/lib/features/lab/products/lab_products_screen.dart b/lib/features/lab/products/lab_products_screen.dart new file mode 100644 index 0000000..66d69a3 --- /dev/null +++ b/lib/features/lab/products/lab_products_screen.dart @@ -0,0 +1,618 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import '../../../core/providers/auth_provider.dart'; +import '../../../core/theme/app_theme.dart'; +import '../../../core/widgets/gradient_app_bar.dart'; +import '../../../models/prosthetic_product.dart'; +import 'lab_products_repository.dart'; + +const _prostheticTypes = [ + ('metal_porselen', 'Metal Porselen'), + ('zirkonyum', 'Zirkonyum'), + ('implant_ustu_zirkonyum', 'İmplant Üstü Zirkonyum'), + ('gecici', 'Geçici'), + ('e_max', 'E-Max'), + ('diger', 'Diğer'), +]; + +String _typeLabel(String value) { + for (final t in _prostheticTypes) { + if (t.$1 == value) return t.$2; + } + return value; +} + +// ── Adaptive sheet helper ──────────────────────────────────────────────────── + +void _showAdaptive(BuildContext context, Widget content) { + final isDesktop = MediaQuery.sizeOf(context).width > AppLayout.sidebarBreakpoint; + if (isDesktop) { + showDialog( + context: context, + builder: (_) => Dialog( + shape: + RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)), + child: ConstrainedBox( + constraints: const BoxConstraints(maxWidth: 560), + child: content, + ), + ), + ); + } else { + showModalBottomSheet( + context: context, + isScrollControlled: true, + backgroundColor: Colors.transparent, + builder: (_) => content, + ); + } +} + +class LabProductsScreen extends ConsumerStatefulWidget { + const LabProductsScreen({super.key}); + + @override + ConsumerState createState() => _LabProductsScreenState(); +} + +class _LabProductsScreenState extends ConsumerState { + late Future> _future; + final _searchController = TextEditingController(); + String _searchQuery = ''; + + @override + void initState() { + super.initState(); + _load(); + } + + @override + void dispose() { + _searchController.dispose(); + super.dispose(); + } + + void _load() { + final tenantId = ref.read(authProvider).activeTenant!.tenant.id; + setState(() { + _future = LabProductsRepository.instance.listProducts(tenantId); + }); + } + + Future _toggleActive(ProstheticProduct product) async { + try { + await LabProductsRepository.instance + .updateProduct(product.id, {'is_active': !product.isActive}); + _load(); + } catch (e) { + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('Hata: $e')), + ); + } + } + } + + Future _deleteProduct(ProstheticProduct product) async { + final confirmed = await showDialog( + context: context, + builder: (ctx) => AlertDialog( + title: const Text('Ürünü Sil'), + content: Text( + '"${product.name}" ürününü silmek istediğinize emin misiniz?'), + actions: [ + TextButton( + onPressed: () => Navigator.of(ctx).pop(false), + child: const Text('İptal'), + ), + FilledButton( + onPressed: () => Navigator.of(ctx).pop(true), + style: FilledButton.styleFrom( + backgroundColor: AppColors.cancelled), + child: const Text('Sil'), + ), + ], + ), + ); + if (confirmed != true) return; + try { + await LabProductsRepository.instance.deleteProduct(product.id); + _load(); + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('Ürün silindi')), + ); + } + } catch (e) { + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('Hata: $e')), + ); + } + } + } + + void _showProductSheet({ProstheticProduct? existing}) { + final tenantId = ref.read(authProvider).activeTenant!.tenant.id; + _showAdaptive( + context, + _ProductForm( + labTenantId: tenantId, + existing: existing, + onSaved: () { + Navigator.of(context).pop(); + _load(); + }, + ), + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: AppColors.background, + appBar: GradientAppBar( + title: 'Ürün Kataloğu', + category: 'LABORATUVAR', + searchController: _searchController, + onSearchChanged: (v) => setState(() => _searchQuery = v), + searchHint: 'Ürün adı veya türü ara...', + ), + floatingActionButton: FloatingActionButton.extended( + onPressed: () => _showProductSheet(), + backgroundColor: AppColors.accent, + foregroundColor: Colors.white, + icon: const Icon(Icons.add), + label: const Text('Yeni Ürün'), + ), + body: RefreshIndicator( + color: AppColors.accent, + onRefresh: () async => _load(), + child: FutureBuilder>( + future: _future, + builder: (ctx, snap) { + if (snap.connectionState == ConnectionState.waiting) { + return const Center( + child: CircularProgressIndicator(color: AppColors.accent)); + } + if (snap.hasError) { + return Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + width: 64, + height: 64, + decoration: BoxDecoration( + color: AppColors.cancelledBg, + borderRadius: BorderRadius.circular(16)), + child: const Icon(Icons.wifi_off_rounded, + color: AppColors.cancelled, size: 30), + ), + const SizedBox(height: 16), + Text('Hata: ${snap.error}', + style: const TextStyle( + color: AppColors.textSecondary)), + const SizedBox(height: 12), + FilledButton.icon( + onPressed: _load, + icon: const Icon(Icons.refresh_rounded, size: 18), + label: const Text('Tekrar Dene')), + ], + ), + ); + } + + final allProducts = snap.data!; + final q = _searchQuery.toLowerCase().trim(); + final products = q.isEmpty + ? allProducts + : allProducts.where((p) => + p.name.toLowerCase().contains(q) || + _typeLabel(p.prostheticType).toLowerCase().contains(q)).toList(); + + if (allProducts.isEmpty) { + return Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + width: 72, + height: 72, + decoration: BoxDecoration( + color: AppColors.inProgressBg, + borderRadius: BorderRadius.circular(20)), + child: const Icon(Icons.inventory_2_outlined, + size: 32, color: AppColors.inProgress), + ), + const SizedBox(height: 16), + Text( + q.isNotEmpty ? 'Sonuç bulunamadı' : 'Henüz ürün eklenmedi', + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + color: AppColors.textPrimary), + ), + const SizedBox(height: 12), + if (q.isEmpty) FilledButton.icon( + onPressed: () => _showProductSheet(), + icon: const Icon(Icons.add), + label: const Text('İlk Ürünü Ekle'), + ), + ], + ), + ); + } + + if (products.isEmpty) { + return Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + width: 72, + height: 72, + decoration: BoxDecoration( + color: AppColors.inProgressBg, + borderRadius: BorderRadius.circular(20)), + child: const Icon(Icons.search_off_rounded, + size: 32, color: AppColors.inProgress), + ), + const SizedBox(height: 16), + const Text( + 'Sonuç bulunamadı', + style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600, color: AppColors.textPrimary), + ), + ], + ), + ); + } + + return ListView.builder( + padding: const EdgeInsets.fromLTRB(16, 16, 16, 80), + itemCount: products.length, + itemBuilder: (ctx, i) { + final product = products[i]; + final statusColor = + product.isActive ? AppColors.inProgress : AppColors.textMuted; + final statusBg = + product.isActive ? AppColors.inProgressBg : AppColors.surfaceVariant; + return Padding( + padding: const EdgeInsets.only(bottom: 10), + child: GestureDetector( + onLongPress: () => _deleteProduct(product), + child: Material( + color: AppColors.surface, + borderRadius: BorderRadius.circular(14), + child: InkWell( + onTap: () => _showProductSheet(existing: product), + borderRadius: BorderRadius.circular(14), + child: Container( + padding: const EdgeInsets.all(14), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(14), + border: Border.all(color: AppColors.border), + boxShadow: [ + BoxShadow( + color: + Colors.black.withValues(alpha: 0.04), + blurRadius: 8, + offset: const Offset(0, 2)) + ]), + child: Row( + children: [ + Container( + width: 44, + height: 44, + decoration: BoxDecoration( + color: statusBg, + borderRadius: BorderRadius.circular(12)), + child: Icon(Icons.medical_services_outlined, + color: statusColor, size: 22), + ), + const SizedBox(width: 12), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + product.name, + style: TextStyle( + fontSize: 15, + fontWeight: FontWeight.w600, + color: product.isActive + ? AppColors.textPrimary + : AppColors.textMuted), + ), + const SizedBox(height: 2), + Text( + _typeLabel(product.prostheticType), + style: const TextStyle( + fontSize: 12, + color: AppColors.textSecondary), + ), + if (product.unitPrice != null) ...[ + const SizedBox(height: 2), + Text( + '${product.unitPrice!.toStringAsFixed(2)} ${product.currency ?? 'TRY'}', + style: const TextStyle( + fontSize: 12, + fontWeight: FontWeight.w600, + color: AppColors.success), + ), + ], + ], + ), + ), + Row( + mainAxisSize: MainAxisSize.min, + children: [ + Switch( + value: product.isActive, + onChanged: (_) => _toggleActive(product), + activeTrackColor: AppColors.accent, + ), + IconButton( + icon: const Icon(Icons.edit_outlined, + color: AppColors.textSecondary, + size: 20), + onPressed: () => + _showProductSheet(existing: product), + ), + ], + ), + ], + ), + ), + ), + ), + ), + ); + }, + ); + }, + ), + ), + ); + } +} + +// ── Product Form ───────────────────────────────────────────────────────────── + +class _ProductForm extends StatefulWidget { + const _ProductForm({ + required this.labTenantId, + required this.onSaved, + this.existing, + }); + + final String labTenantId; + final ProstheticProduct? existing; + final VoidCallback onSaved; + + @override + State<_ProductForm> createState() => _ProductFormState(); +} + +class _ProductFormState extends State<_ProductForm> { + final _formKey = GlobalKey(); + late final TextEditingController _nameCtrl; + late final TextEditingController _priceCtrl; + late final TextEditingController _descCtrl; + late String _selectedType; + late String _currency; + late bool _isActive; + bool _saving = false; + + @override + void initState() { + super.initState(); + final p = widget.existing; + _nameCtrl = TextEditingController(text: p?.name ?? ''); + _priceCtrl = TextEditingController( + text: p?.unitPrice != null ? p!.unitPrice!.toString() : ''); + _descCtrl = TextEditingController(text: p?.description ?? ''); + _selectedType = p?.prostheticType ?? _prostheticTypes.first.$1; + _currency = p?.currency ?? 'TRY'; + _isActive = p?.isActive ?? true; + } + + @override + void dispose() { + _nameCtrl.dispose(); + _priceCtrl.dispose(); + _descCtrl.dispose(); + super.dispose(); + } + + Future _save() async { + if (!_formKey.currentState!.validate()) return; + setState(() => _saving = true); + + final price = double.tryParse(_priceCtrl.text.trim()); + final product = ProstheticProduct( + id: widget.existing?.id ?? '', + labTenantId: widget.labTenantId, + name: _nameCtrl.text.trim(), + prostheticType: _selectedType, + unitPrice: price, + currency: _currency, + isActive: _isActive, + description: + _descCtrl.text.trim().isEmpty ? null : _descCtrl.text.trim(), + ); + + try { + if (widget.existing != null) { + await LabProductsRepository.instance.updateProduct( + widget.existing!.id, + product.toJson(), + ); + } else { + await LabProductsRepository.instance.createProduct(product); + } + widget.onSaved(); + } catch (e) { + setState(() => _saving = false); + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('Hata: $e')), + ); + } + } + } + + @override + Widget build(BuildContext context) { + final isDesktop = MediaQuery.sizeOf(context).width > AppLayout.sidebarBreakpoint; + final isEdit = widget.existing != null; + return Container( + decoration: BoxDecoration( + color: AppColors.surface, + borderRadius: BorderRadius.vertical( + top: isDesktop ? Radius.zero : const Radius.circular(20), + ), + ), + padding: EdgeInsets.only( + left: 20, + right: 20, + top: 24, + bottom: isDesktop + ? 24 + : MediaQuery.of(context).viewInsets.bottom + 24, + ), + child: Form( + key: _formKey, + child: SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + mainAxisSize: MainAxisSize.min, + children: [ + Row( + children: [ + Expanded( + child: Text( + isEdit ? 'Ürünü Düzenle' : 'Yeni Ürün', + style: Theme.of(context) + .textTheme + .titleMedium + ?.copyWith( + fontWeight: FontWeight.bold, + color: AppColors.textPrimary), + ), + ), + IconButton( + icon: const Icon(Icons.close, + color: AppColors.textSecondary), + onPressed: () => Navigator.of(context).pop(), + ), + ], + ), + const SizedBox(height: 16), + + // Name + TextFormField( + controller: _nameCtrl, + decoration: + const InputDecoration(labelText: 'Ürün Adı *'), + validator: (v) => + v == null || v.trim().isEmpty ? 'Ürün adı gerekli' : null, + ), + const SizedBox(height: 12), + + // Prosthetic type dropdown + DropdownButtonFormField( + initialValue: _selectedType, + decoration: + const InputDecoration(labelText: 'Protez Tipi *'), + items: _prostheticTypes + .map((t) => DropdownMenuItem( + value: t.$1, + child: Text(t.$2), + )) + .toList(), + onChanged: (v) => setState(() => _selectedType = v!), + ), + const SizedBox(height: 12), + + // Price + currency row + Row( + children: [ + Expanded( + flex: 3, + child: TextFormField( + controller: _priceCtrl, + decoration: + const InputDecoration(labelText: 'Birim Fiyat'), + keyboardType: const TextInputType.numberWithOptions( + decimal: true), + validator: (v) { + if (v != null && v.isNotEmpty) { + if (double.tryParse(v) == null) { + return 'Geçerli fiyat girin'; + } + } + return null; + }, + ), + ), + const SizedBox(width: 8), + Expanded( + flex: 2, + child: DropdownButtonFormField( + initialValue: _currency, + decoration: + const InputDecoration(labelText: 'Para Birimi'), + items: ['TRY', 'USD', 'EUR'] + .map((c) => DropdownMenuItem( + value: c, + child: Text(c), + )) + .toList(), + onChanged: (v) => setState(() => _currency = v!), + ), + ), + ], + ), + const SizedBox(height: 12), + + // Description + TextFormField( + controller: _descCtrl, + decoration: const InputDecoration( + labelText: 'Açıklama (isteğe bağlı)'), + maxLines: 2, + ), + const SizedBox(height: 12), + + // Active toggle + SwitchListTile( + title: const Text('Aktif', + style: TextStyle(color: AppColors.textPrimary)), + value: _isActive, + onChanged: (v) => setState(() => _isActive = v), + contentPadding: EdgeInsets.zero, + activeTrackColor: AppColors.accent, + ), + const SizedBox(height: 8), + + FilledButton( + onPressed: _saving ? null : _save, + style: FilledButton.styleFrom( + minimumSize: const Size.fromHeight(48)), + child: _saving + ? const SizedBox( + width: 20, + height: 20, + child: CircularProgressIndicator( + strokeWidth: 2, color: Colors.white), + ) + : Text(isEdit ? 'Kaydet' : 'Ekle'), + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/features/lab/settings/lab_settings_screen.dart b/lib/features/lab/settings/lab_settings_screen.dart new file mode 100644 index 0000000..6d21355 --- /dev/null +++ b/lib/features/lab/settings/lab_settings_screen.dart @@ -0,0 +1,785 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +import 'package:go_router/go_router.dart'; + +import '../../../core/l10n/app_strings.dart'; +import '../../../core/providers/auth_provider.dart'; +import '../../../core/providers/locale_provider.dart'; +import '../../../core/router/app_router.dart'; +import '../../../core/theme/app_theme.dart'; +import '../../../models/tenant.dart'; +import '../../shared/tenant_team_screen.dart'; +import '../connections/lab_connections_screen.dart'; + +class LabSettingsScreen extends ConsumerWidget { + const LabSettingsScreen({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final auth = ref.watch(authProvider); + final s = ref.watch(stringsProvider); + final profile = auth.profile; + final membership = auth.activeTenant; + final tenant = membership?.tenant; + final canEdit = membership?.isAdmin ?? false; + + return Scaffold( + appBar: AppBar(title: Text(s.settings)), + body: ListView( + padding: const EdgeInsets.all(16), + children: [ + // User card + _SectionHeader(title: s.userInfo), + _UserCard(profile: profile), + const SizedBox(height: 20), + + // Lab info + _SectionHeader( + title: s.labInfo, + action: canEdit + ? IconButton( + icon: const Icon(Icons.edit_outlined, + size: 18, color: AppColors.accent), + tooltip: s.edit, + onPressed: () => _showEditSheet(context, ref, tenant, s), + ) + : null, + ), + _InfoCard(children: [ + _InfoTile( + icon: Icons.science_outlined, + label: s.labName, + value: tenant?.companyName ?? '-', + ), + _InfoTile( + icon: Icons.payments_outlined, + label: s.currency, + value: tenant?.defaultCurrency ?? 'TRY', + ), + _InfoTileBadge( + icon: Icons.circle_outlined, + label: s.status, + value: tenant?.status == 'active' ? s.active : (tenant?.status ?? '-'), + badgeColor: AppColors.success, + badgeBg: AppColors.successBg, + ), + _InfoTile( + icon: Icons.star_outline, + label: s.role, + value: _roleLabel(membership?.role, s), + ), + ]), + const SizedBox(height: 20), + + // Connections + if (membership?.showConnections ?? false) ...[ + _SectionHeader(title: s.connections), + _InfoCard(children: [ + _NavTile( + icon: Icons.link_rounded, + iconColor: AppColors.inProgress, + iconBg: AppColors.inProgressBg, + title: s.clinicConnections, + subtitle: s.clinicConnectionsSub, + onTap: () => Navigator.push( + context, + MaterialPageRoute( + builder: (_) => const LabConnectionsScreen()), + ), + ), + ]), + const SizedBox(height: 20), + ], + + // Other memberships + if (auth.memberships.length > 1) ...[ + _SectionHeader(title: s.otherMemberships), + _InfoCard(children: [ + for (final m + in auth.memberships.where((m) => m.id != membership?.id)) + _NavTile( + icon: Icons.switch_account_outlined, + iconColor: AppColors.inProgress, + iconBg: AppColors.inProgressBg, + title: m.tenant.companyName, + subtitle: _tenantKindLabel(m.tenant.kind, s), + onTap: () { + ref.read(authProvider.notifier).setActiveTenant(m); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text(s.tenantSelected(m.tenant.companyName))), + ); + }, + ), + ]), + const SizedBox(height: 20), + ], + + // Team management + Reports + if (membership?.canManageUsers ?? false) ...[ + _SectionHeader(title: s.management), + _InfoCard(children: [ + _NavTile( + icon: Icons.group_outlined, + iconColor: AppColors.inProgress, + iconBg: AppColors.inProgressBg, + title: s.team, + subtitle: s.teamSub, + onTap: () => Navigator.push( + context, + MaterialPageRoute( + builder: (_) => const TenantTeamScreen()), + ), + ), + _NavTile( + icon: Icons.discount_outlined, + iconColor: AppColors.success, + iconBg: AppColors.successBg, + title: s.discounts, + subtitle: s.discountsSub, + onTap: () => context.push(routeLabDiscounts), + ), + _NavTile( + icon: Icons.bar_chart_rounded, + iconColor: AppColors.accent, + iconBg: AppColors.inProgressBg, + title: s.reports, + subtitle: s.reportsSub, + onTap: () => context.push(routeLabReports), + ), + _NavTile( + icon: Icons.auto_awesome_outlined, + iconColor: const Color(0xFF7C3AED), + iconBg: const Color(0xFFF3E8FF), + title: s.aiAssistant, + subtitle: s.aiAssistantSub, + onTap: () => context.push(routeLabAi), + ), + ]), + const SizedBox(height: 20), + ], + + // Preferences (language) + _SectionHeader(title: s.preferences), + _InfoCard(children: [ + _NavTile( + icon: Icons.language_outlined, + iconColor: AppColors.accent, + iconBg: AppColors.inProgressBg, + title: s.appLanguage, + subtitle: _currentLanguageLabel(ref.watch(localeProvider).languageCode, s), + onTap: () => _showLanguagePicker(context, ref, s), + ), + ]), + const SizedBox(height: 20), + + // Sign out + _SignOutCard(ref: ref, s: s), + const SizedBox(height: 32), + const Center( + child: Text('DLS — Dental Lab System', + style: TextStyle(fontSize: 12, color: AppColors.textMuted)), + ), + const SizedBox(height: 8), + ], + ), + ); + } + + void _showEditSheet(BuildContext context, WidgetRef ref, Tenant? tenant, AppStrings s) { + if (tenant == null) return; + showModalBottomSheet( + context: context, + isScrollControlled: true, + backgroundColor: Colors.transparent, + builder: (_) => _EditTenantSheet( + tenant: tenant, + s: s, + onSave: (name, currency) async { + await ref.read(authProvider.notifier).updateTenantInfo( + tenantId: tenant.id, + companyName: name, + defaultCurrency: currency, + ); + }, + ), + ); + } + + void _showLanguagePicker(BuildContext context, WidgetRef ref, AppStrings s) { + showModalBottomSheet( + context: context, + backgroundColor: Colors.transparent, + builder: (_) => _LanguagePickerSheet(s: s, ref: ref), + ); + } + + static String _tenantKindLabel(TenantKind? kind, AppStrings s) => + switch (kind) { + TenantKind.clinic => s.tenantKindClinic, + TenantKind.lab => s.tenantKindLab, + null => '-', + }; + + static String _currentLanguageLabel(String code, AppStrings s) => switch (code) { + 'en' => s.languageEnglish, + 'ru' => s.languageRussian, + 'ar' => s.languageArabic, + 'de' => s.languageGerman, + _ => s.languageTurkish, + }; + + static String _roleLabel(TenantRole? role, AppStrings s) => switch (role) { + TenantRole.owner => s.roleOwner, + TenantRole.admin => s.roleAdmin, + TenantRole.technician => s.roleTechnician, + TenantRole.delivery => s.roleDelivery, + TenantRole.finance => s.roleFinance, + TenantRole.doctor => s.roleDoctor, + TenantRole.member => s.roleMember, + null => '-', + }; +} + +// ── Language picker sheet ───────────────────────────────────────────────────── + +class _LanguagePickerSheet extends ConsumerWidget { + const _LanguagePickerSheet({required this.s, required this.ref}); + final AppStrings s; + final WidgetRef ref; + + @override + Widget build(BuildContext context, WidgetRef _) { + final currentLocale = ref.watch(localeProvider); + + final options = [ + ('tr', '🇹🇷', s.languageTurkish), + ('en', '🇬🇧', s.languageEnglish), + ('ru', '🇷🇺', s.languageRussian), + ('ar', '🇸🇦', s.languageArabic), + ('de', '🇩🇪', s.languageGerman), + ]; + + return Container( + decoration: const BoxDecoration( + color: AppColors.surface, + borderRadius: BorderRadius.vertical(top: Radius.circular(20)), + ), + padding: const EdgeInsets.all(20), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Center( + child: Container( + width: 40, + height: 4, + decoration: BoxDecoration( + color: AppColors.border, + borderRadius: BorderRadius.circular(2), + ), + ), + ), + const SizedBox(height: 16), + Text( + s.languageSelection, + style: const TextStyle( + fontSize: 17, + fontWeight: FontWeight.w700, + color: AppColors.textPrimary, + ), + ), + const SizedBox(height: 12), + for (final (code, flag, label) in options) + ListTile( + contentPadding: const EdgeInsets.symmetric(horizontal: 4), + leading: Text(flag, style: const TextStyle(fontSize: 24)), + title: Text( + label, + style: const TextStyle( + fontSize: 15, + fontWeight: FontWeight.w500, + color: AppColors.textPrimary, + ), + ), + trailing: currentLocale.languageCode == code + ? const Icon(Icons.check_circle_rounded, + color: AppColors.accent) + : null, + onTap: () { + ref.read(localeProvider.notifier).setLocale(Locale(code)); + ref.read(authProvider.notifier).updateLanguage(code); + Navigator.pop(context); + }, + ), + SizedBox(height: MediaQuery.paddingOf(context).bottom + 4), + ], + ), + ); + } +} + +// ── Edit sheet ──────────────────────────────────────────────────────────────── + +class _EditTenantSheet extends StatefulWidget { + const _EditTenantSheet({ + required this.tenant, + required this.s, + required this.onSave, + }); + final Tenant tenant; + final AppStrings s; + final Future Function(String companyName, String currency) onSave; + + @override + State<_EditTenantSheet> createState() => _EditTenantSheetState(); +} + +class _EditTenantSheetState extends State<_EditTenantSheet> { + late final TextEditingController _nameController; + late String _selectedCurrency; + bool _saving = false; + + static const _currencies = [ + ('TRY', '₺', 'Türk Lirası'), + ('USD', '\$', 'US Dollar'), + ('EUR', '€', 'Euro'), + ('GBP', '£', 'British Pound'), + ('AED', 'د.إ', 'UAE Dirham'), + ]; + + @override + void initState() { + super.initState(); + _nameController = TextEditingController(text: widget.tenant.companyName); + _selectedCurrency = widget.tenant.defaultCurrency; + } + + @override + void dispose() { + _nameController.dispose(); + super.dispose(); + } + + Future _submit() async { + final name = _nameController.text.trim(); + if (name.isEmpty) return; + setState(() => _saving = true); + final navigator = Navigator.of(context); + final messenger = ScaffoldMessenger.of(context); + try { + await widget.onSave(name, _selectedCurrency); + navigator.pop(); + } catch (e) { + messenger.showSnackBar( + SnackBar(content: Text('${widget.s.errorPrefix}: $e'))); + } finally { + if (mounted) setState(() => _saving = false); + } + } + + @override + Widget build(BuildContext context) { + final s = widget.s; + return Padding( + padding: + EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom), + child: Container( + decoration: const BoxDecoration( + color: AppColors.surface, + borderRadius: BorderRadius.vertical(top: Radius.circular(20)), + ), + padding: const EdgeInsets.all(20), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Center( + child: Container( + width: 40, + height: 4, + decoration: BoxDecoration( + color: AppColors.border, + borderRadius: BorderRadius.circular(2)), + ), + ), + const SizedBox(height: 16), + Text(s.editLabInfo, + style: const TextStyle( + fontSize: 17, + fontWeight: FontWeight.w700, + color: AppColors.textPrimary)), + const SizedBox(height: 16), + TextFormField( + controller: _nameController, + decoration: InputDecoration( + labelText: s.labName, + hintText: s.labNameHint, + ), + textCapitalization: TextCapitalization.words, + ), + const SizedBox(height: 14), + Text(s.currency, + style: const TextStyle( + fontSize: 13, + fontWeight: FontWeight.w500, + color: AppColors.textSecondary)), + const SizedBox(height: 8), + DropdownButtonFormField( + value: _selectedCurrency, + decoration: InputDecoration( + contentPadding: + const EdgeInsets.symmetric(horizontal: 16, vertical: 14), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: const BorderSide(color: AppColors.border)), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: const BorderSide(color: AppColors.border)), + ), + items: [ + for (final (code, symbol, name) in _currencies) + DropdownMenuItem( + value: code, + child: Text('$symbol $name ($code)', + style: const TextStyle(fontSize: 14)), + ), + ], + onChanged: (v) { + if (v != null) setState(() => _selectedCurrency = v); + }, + ), + const SizedBox(height: 20), + if (_saving) + const Center( + child: CircularProgressIndicator(color: AppColors.accent)) + else + FilledButton( + onPressed: _submit, + style: FilledButton.styleFrom( + minimumSize: const Size(double.infinity, 48)), + child: Text(s.save), + ), + SizedBox(height: MediaQuery.paddingOf(context).bottom + 4), + ], + ), + ), + ); + } +} + +// ── Reusable UI pieces ──────────────────────────────────────────────────────── + +class _UserCard extends StatelessWidget { + const _UserCard({required this.profile}); + final dynamic profile; + + @override + Widget build(BuildContext context) { + final displayName = (profile?.displayName?.isNotEmpty == true) + ? profile!.displayName as String + : 'Kullanıcı'; + final initial = (profile?.displayName?.isNotEmpty == true + ? (profile!.displayName as String)[0] + : (profile?.email as String?)?[0] ?? '?') + .toUpperCase(); + + return Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: AppColors.surface, + borderRadius: BorderRadius.circular(16), + border: Border.all(color: AppColors.border), + boxShadow: [ + BoxShadow( + color: Colors.black.withValues(alpha: 0.05), + blurRadius: 12, + offset: const Offset(0, 4)) + ], + ), + child: Row( + children: [ + Container( + width: 56, + height: 56, + decoration: BoxDecoration( + color: AppColors.inProgressBg, + borderRadius: BorderRadius.circular(14)), + child: Center( + child: Text(initial, + style: const TextStyle( + fontSize: 22, + fontWeight: FontWeight.w700, + color: AppColors.inProgress)), + ), + ), + const SizedBox(width: 16), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(displayName, + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + color: AppColors.textPrimary)), + const SizedBox(height: 2), + Text(profile?.email as String? ?? '', + style: const TextStyle( + fontSize: 13, color: AppColors.textSecondary)), + ], + ), + ), + ], + ), + ); + } +} + +class _SectionHeader extends StatelessWidget { + const _SectionHeader({required this.title, this.action}); + final String title; + final Widget? action; + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(bottom: 8), + child: Row( + children: [ + Expanded( + child: Text( + title, + style: const TextStyle( + fontSize: 13, + fontWeight: FontWeight.w600, + color: AppColors.accent, + letterSpacing: 0.3), + ), + ), + if (action != null) action!, + ], + ), + ); + } +} + +class _InfoCard extends StatelessWidget { + const _InfoCard({required this.children}); + final List children; + + @override + Widget build(BuildContext context) { + return Container( + decoration: BoxDecoration( + color: AppColors.surface, + borderRadius: BorderRadius.circular(16), + border: Border.all(color: AppColors.border), + boxShadow: [ + BoxShadow( + color: Colors.black.withValues(alpha: 0.05), + blurRadius: 12, + offset: const Offset(0, 4)) + ], + ), + child: Column( + children: [ + for (int i = 0; i < children.length; i++) ...[ + children[i], + if (i < children.length - 1) + const Divider( + height: 1, indent: 16, endIndent: 16, color: AppColors.border), + ], + ], + ), + ); + } +} + +class _InfoTile extends StatelessWidget { + const _InfoTile( + {required this.icon, required this.label, required this.value}); + final IconData icon; + final String label; + final String value; + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), + child: Row( + children: [ + Icon(icon, size: 18, color: AppColors.textSecondary), + const SizedBox(width: 12), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(label, + style: const TextStyle( + fontSize: 11, color: AppColors.textMuted)), + const SizedBox(height: 2), + Text(value, + style: const TextStyle( + fontSize: 14, + fontWeight: FontWeight.w500, + color: AppColors.textPrimary)), + ], + ), + ), + ], + ), + ); + } +} + +class _InfoTileBadge extends StatelessWidget { + const _InfoTileBadge({ + required this.icon, + required this.label, + required this.value, + required this.badgeColor, + required this.badgeBg, + }); + final IconData icon; + final String label; + final String value; + final Color badgeColor; + final Color badgeBg; + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), + child: Row( + children: [ + Icon(icon, size: 18, color: AppColors.textSecondary), + const SizedBox(width: 12), + Expanded( + child: Text(label, + style: const TextStyle( + fontSize: 11, color: AppColors.textMuted)), + ), + Container( + padding: + const EdgeInsets.symmetric(horizontal: 10, vertical: 4), + decoration: BoxDecoration( + color: badgeBg, + borderRadius: BorderRadius.circular(8), + ), + child: Text(value, + style: TextStyle( + color: badgeColor, + fontSize: 12, + fontWeight: FontWeight.w600)), + ), + ], + ), + ); + } +} + +class _NavTile extends StatelessWidget { + const _NavTile({ + required this.icon, + required this.iconColor, + required this.iconBg, + required this.title, + required this.onTap, + this.subtitle, + }); + + final IconData icon; + final Color iconColor; + final Color iconBg; + final String title; + final String? subtitle; + final VoidCallback onTap; + + @override + Widget build(BuildContext context) { + return ListTile( + contentPadding: + const EdgeInsets.symmetric(horizontal: 16, vertical: 2), + leading: Container( + width: 36, + height: 36, + decoration: BoxDecoration( + color: iconBg, borderRadius: BorderRadius.circular(9)), + child: Icon(icon, color: iconColor, size: 18), + ), + title: Text(title, + style: const TextStyle( + fontWeight: FontWeight.w600, color: AppColors.textPrimary)), + subtitle: subtitle != null + ? Text(subtitle!, + style: const TextStyle(color: AppColors.textSecondary)) + : null, + trailing: + const Icon(Icons.chevron_right, color: AppColors.textSecondary), + onTap: onTap, + ); + } +} + +class _SignOutCard extends StatelessWidget { + const _SignOutCard({required this.ref, required this.s}); + final WidgetRef ref; + final AppStrings s; + + @override + Widget build(BuildContext context) { + return Container( + decoration: BoxDecoration( + color: AppColors.surface, + borderRadius: BorderRadius.circular(16), + border: Border.all(color: AppColors.cancelledBg), + boxShadow: [ + BoxShadow( + color: Colors.black.withValues(alpha: 0.05), + blurRadius: 12, + offset: const Offset(0, 4)) + ], + ), + child: ListTile( + contentPadding: + const EdgeInsets.symmetric(horizontal: 16, vertical: 2), + leading: Container( + width: 36, + height: 36, + decoration: BoxDecoration( + color: AppColors.cancelledBg, + borderRadius: BorderRadius.circular(9)), + child: const Icon(Icons.logout, + color: AppColors.cancelled, size: 18), + ), + title: Text(s.signOut, + style: const TextStyle( + color: AppColors.cancelled, fontWeight: FontWeight.w600)), + onTap: () async { + final confirmed = await showDialog( + context: context, + builder: (ctx) => AlertDialog( + title: Text(s.signOutTitle), + content: Text(s.signOutConfirm), + actions: [ + TextButton( + onPressed: () => Navigator.pop(ctx, false), + child: Text(s.cancel)), + FilledButton( + onPressed: () => Navigator.pop(ctx, true), + style: FilledButton.styleFrom( + backgroundColor: AppColors.cancelled), + child: Text(s.signOut), + ), + ], + ), + ); + if (confirmed == true) { + await ref.read(authProvider.notifier).signOut(); + } + }, + ), + ); + } +} diff --git a/lib/features/shared/ai_chat_screen.dart b/lib/features/shared/ai_chat_screen.dart new file mode 100644 index 0000000..573c0c0 --- /dev/null +++ b/lib/features/shared/ai_chat_screen.dart @@ -0,0 +1,930 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +import '../../core/providers/auth_provider.dart'; +import '../../core/services/ai_actions.dart'; +import '../../core/services/ai_context_builder.dart'; +import '../../core/services/ai_service.dart'; +import '../../core/theme/app_theme.dart'; +import '../../core/utils/file_download_helper.dart'; +import '../../models/job_file.dart'; +import '../../models/tenant.dart'; + +class AiChatScreen extends ConsumerStatefulWidget { + const AiChatScreen({super.key}); + + @override + ConsumerState createState() => _AiChatScreenState(); +} + +class _AiChatScreenState extends ConsumerState { + final _inputController = TextEditingController(); + final _scrollController = ScrollController(); + final List<_Message> _messages = []; + + String? _systemPrompt; + bool _loadingContext = true; + bool _streaming = false; + + @override + void initState() { + super.initState(); + _loadContext(); + } + + @override + void dispose() { + _inputController.dispose(); + _scrollController.dispose(); + super.dispose(); + } + + Future _loadContext() async { + try { + final membership = ref.read(authProvider).activeTenant!; + final prompt = await AiContextBuilder.instance.build(membership); + if (mounted) setState(() { _systemPrompt = prompt; _loadingContext = false; }); + } catch (_) { + if (mounted) setState(() => _loadingContext = false); + } + } + + Future _send([String? override]) async { + final text = (override ?? _inputController.text).trim(); + if (text.isEmpty || _streaming || _systemPrompt == null) return; + _inputController.clear(); + FocusScope.of(context).unfocus(); + + final apiMessages = [ + ..._messages.map((m) => {'role': m.isUser ? 'user' : 'assistant', 'content': m.rawText}), + {'role': 'user', 'content': text}, + ]; + + setState(() { + _messages.add(_Message.user(text)); + _messages.add(_Message.assistantStreaming()); + _streaming = true; + }); + _scrollToBottom(); + + var accumulated = ''; + try { + final stream = AiService.instance.streamChat( + systemPrompt: _systemPrompt!, + messages: apiMessages, + ); + await for (final chunk in stream) { + if (!mounted) break; + accumulated += chunk; + setState(() => _messages[_messages.length - 1] = _Message.assistantStreaming(accumulated)); + _scrollToBottom(); + } + if (mounted) { + setState(() => _messages[_messages.length - 1] = _Message.assistantDone(accumulated)); + } + } catch (e) { + if (mounted) { + setState(() => _messages[_messages.length - 1] = _Message.error('Bir hata olustu, tekrar deneyin.')); + } + } finally { + if (mounted) setState(() => _streaming = false); + } + } + + void _scrollToBottom() { + WidgetsBinding.instance.addPostFrameCallback((_) { + if (_scrollController.hasClients) { + _scrollController.animateTo( + _scrollController.position.maxScrollExtent, + duration: const Duration(milliseconds: 200), + curve: Curves.easeOut, + ); + } + }); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: AppColors.background, + appBar: AppBar( + title: Row( + children: [ + Container( + width: 28, + height: 28, + decoration: BoxDecoration( + color: AppColors.accent, + borderRadius: BorderRadius.circular(8), + ), + child: const Icon(Icons.auto_awesome, size: 16, color: Colors.white), + ), + const SizedBox(width: 10), + const Text('AI Asistan'), + ], + ), + actions: [ + if (_messages.isNotEmpty) + IconButton( + onPressed: () => setState(() => _messages.clear()), + icon: const Icon(Icons.refresh_outlined, size: 20), + tooltip: 'Sohbeti temizle', + ), + ], + ), + body: Column( + children: [ + Expanded( + child: _loadingContext + ? const _LoadingContext() + : _messages.isEmpty + ? _WelcomeView(onSuggestion: _send) + : ListView.builder( + controller: _scrollController, + padding: const EdgeInsets.fromLTRB(16, 12, 16, 8), + itemCount: _messages.length, + itemBuilder: (_, i) => _MessageBubble( + message: _messages[i], + membership: ref.read(authProvider).activeTenant!, + ), + ), + ), + _InputBar( + controller: _inputController, + enabled: !_loadingContext && !_streaming, + streaming: _streaming, + onSend: _send, + ), + ], + ), + ); + } +} + +// ── Message model ───────────────────────────────────────────────────────────── + +enum _MsgKind { user, streaming, done, error } + +class _Message { + _Message._({required this.kind, required this.rawText}); + + factory _Message.user(String text) => + _Message._(kind: _MsgKind.user, rawText: text); + + factory _Message.assistantStreaming([String text = '']) => + _Message._(kind: _MsgKind.streaming, rawText: text); + + factory _Message.assistantDone(String text) => + _Message._(kind: _MsgKind.done, rawText: text); + + factory _Message.error(String text) => + _Message._(kind: _MsgKind.error, rawText: text); + + final _MsgKind kind; + final String rawText; + + bool get isUser => kind == _MsgKind.user; + bool get isStreaming => kind == _MsgKind.streaming; + bool get isError => kind == _MsgKind.error; +} + +// ── Welcome view ────────────────────────────────────────────────────────────── + +class _WelcomeView extends StatelessWidget { + const _WelcomeView({required this.onSuggestion}); + final void Function(String) onSuggestion; + + static const _suggestions = [ + 'Bekleyen islerimin ozeti nedir?', + 'Gecikmiş iş var mı?', + 'Bu ay kac is tamamlandi?', + 'Revizyon oranım ne durumda?', + 'Finans durumumu ozetle.', + 'Son yuklenen dosyalari goster.', + ]; + + @override + Widget build(BuildContext context) { + return SingleChildScrollView( + padding: const EdgeInsets.fromLTRB(20, 32, 20, 12), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + width: 56, + height: 56, + decoration: BoxDecoration( + color: AppColors.accent, + borderRadius: BorderRadius.circular(16), + ), + child: const Icon(Icons.auto_awesome, size: 28, color: Colors.white), + ), + const SizedBox(height: 16), + const Text( + 'Merhaba! Size nasil yardimci olabilirim?', + style: TextStyle(fontSize: 20, fontWeight: FontWeight.w700, color: AppColors.textPrimary), + ), + const SizedBox(height: 6), + const Text( + 'Isler, finans ve ekip hakkinda soru sorabilir, islem yapabilirsiniz.', + style: TextStyle(fontSize: 14, color: AppColors.textSecondary), + ), + const SizedBox(height: 28), + const Text( + 'ONERILER', + style: TextStyle(fontSize: 11, fontWeight: FontWeight.w700, color: AppColors.textMuted, letterSpacing: 0.8), + ), + const SizedBox(height: 10), + Wrap( + spacing: 8, + runSpacing: 8, + children: _suggestions + .map((s) => _SuggestionChip(label: s, onTap: () => onSuggestion(s))) + .toList(), + ), + ], + ), + ); + } +} + +class _SuggestionChip extends StatelessWidget { + const _SuggestionChip({required this.label, required this.onTap}); + final String label; + final VoidCallback onTap; + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: onTap, + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 9), + decoration: BoxDecoration( + color: AppColors.surface, + borderRadius: BorderRadius.circular(20), + border: Border.all(color: AppColors.border), + ), + child: Text(label, style: const TextStyle(fontSize: 13, color: AppColors.textPrimary)), + ), + ); + } +} + +// ── Message bubble ──────────────────────────────────────────────────────────── + +class _MessageBubble extends StatelessWidget { + const _MessageBubble({required this.message, required this.membership}); + final _Message message; + final TenantMembership membership; + + @override + Widget build(BuildContext context) { + if (message.isUser) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 4), + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Flexible( + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 10), + decoration: BoxDecoration( + color: AppColors.accent, + borderRadius: const BorderRadius.only( + topLeft: Radius.circular(16), + topRight: Radius.circular(16), + bottomLeft: Radius.circular(16), + bottomRight: Radius.circular(4), + ), + ), + child: Text( + message.rawText, + style: const TextStyle(fontSize: 14, height: 1.5, color: Colors.white), + ), + ), + ), + ], + ), + ); + } + + return Padding( + padding: const EdgeInsets.symmetric(vertical: 4), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + width: 28, + height: 28, + margin: const EdgeInsets.only(right: 8, top: 2), + decoration: BoxDecoration(color: AppColors.accent, borderRadius: BorderRadius.circular(8)), + child: const Icon(Icons.auto_awesome, size: 14, color: Colors.white), + ), + Flexible( + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 10), + decoration: BoxDecoration( + color: AppColors.surface, + borderRadius: const BorderRadius.only( + topLeft: Radius.circular(4), + topRight: Radius.circular(16), + bottomLeft: Radius.circular(16), + bottomRight: Radius.circular(16), + ), + border: Border.all(color: AppColors.border), + ), + child: message.isStreaming && message.rawText.isEmpty + ? const _TypingDots() + : _MessageContent(message: message, membership: membership), + ), + ), + ], + ), + ); + } +} + +// ── Message content: markdown + action buttons ──────────────────────────────── + +class _MessageContent extends StatelessWidget { + const _MessageContent({required this.message, required this.membership}); + final _Message message; + final TenantMembership membership; + + @override + Widget build(BuildContext context) { + if (message.isError) { + return Text( + message.rawText, + style: const TextStyle(fontSize: 14, height: 1.5, color: AppColors.cancelled), + ); + } + + // During streaming: show raw text without parsing actions + if (message.isStreaming) { + return _MarkdownText(message.rawText, color: AppColors.textPrimary); + } + + // Done: parse segments → text + action buttons + final segments = parseSegments(message.rawText); + if (segments.isEmpty) { + return _MarkdownText(message.rawText, color: AppColors.textPrimary); + } + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: segments.map((seg) { + if (seg is TextSegment) { + return _MarkdownText(seg.text, color: AppColors.textPrimary); + } + if (seg is ActionSegment) { + return Padding( + padding: const EdgeInsets.only(top: 10), + child: _ActionCard(action: seg.action, membership: membership), + ); + } + return const SizedBox.shrink(); + }).toList(), + ); + } +} + +// ── Action card ─────────────────────────────────────────────────────────────── + +enum _ActionState { idle, confirming, loading, success, error, files } + +class _ActionCard extends StatefulWidget { + const _ActionCard({required this.action, required this.membership}); + final AiAction action; + final TenantMembership membership; + + @override + State<_ActionCard> createState() => _ActionCardState(); +} + +class _ActionCardState extends State<_ActionCard> { + _ActionState _state = _ActionState.idle; + String _resultMsg = ''; + List _files = []; + + AiAction get action => widget.action; + + Future _execute() async { + setState(() => _state = _ActionState.loading); + final outcome = await AiActionExecutor.execute(action, widget.membership); + if (!mounted) return; + switch (outcome) { + case ActionSuccess(:final message): + setState(() { _state = _ActionState.success; _resultMsg = message; }); + case ActionError(:final error): + setState(() { _state = _ActionState.error; _resultMsg = error; }); + case ActionFiles(:final files): + setState(() { _state = _ActionState.files; _files = files; }); + } + } + + void _onTap() { + if (action.isDangerous) { + setState(() => _state = _ActionState.confirming); + } else { + _execute(); + } + } + + @override + Widget build(BuildContext context) { + return switch (_state) { + _ActionState.idle => _idleButton(), + _ActionState.confirming => _confirmCard(), + _ActionState.loading => _loadingCard(), + _ActionState.success => _resultCard(success: true), + _ActionState.error => _resultCard(success: false), + _ActionState.files => _filesCard(), + }; + } + + Widget _idleButton() { + final isDanger = action.isDangerous; + final isFile = action.isFileAction; + final color = isDanger + ? AppColors.cancelled + : isFile + ? AppColors.inProgress + : AppColors.accent; + final bgColor = isDanger + ? AppColors.cancelledBg + : isFile + ? AppColors.inProgressBg + : AppColors.accent.withValues(alpha: 0.1); + final icon = isDanger + ? Icons.cancel_outlined + : isFile + ? Icons.folder_outlined + : action.type == 'add_member' + ? Icons.person_add_outlined + : Icons.check_circle_outline; + + return GestureDetector( + onTap: _onTap, + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 10), + decoration: BoxDecoration( + color: bgColor, + borderRadius: BorderRadius.circular(10), + border: Border.all(color: color.withValues(alpha: 0.3)), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon(icon, size: 16, color: color), + const SizedBox(width: 8), + Flexible( + child: Text( + action.label, + style: TextStyle(fontSize: 13, fontWeight: FontWeight.w600, color: color), + ), + ), + ], + ), + ), + ); + } + + Widget _confirmCard() { + return Container( + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + color: AppColors.cancelledBg, + borderRadius: BorderRadius.circular(10), + border: Border.all(color: AppColors.cancelled.withValues(alpha: 0.3)), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Row( + children: [ + const Icon(Icons.warning_amber_rounded, size: 16, color: AppColors.cancelled), + const SizedBox(width: 6), + const Expanded( + child: Text( + 'Bu islem geri alinamayabilir.', + style: TextStyle(fontSize: 12, fontWeight: FontWeight.w600, color: AppColors.cancelled), + ), + ), + ], + ), + const SizedBox(height: 4), + Text(action.label, style: const TextStyle(fontSize: 13, color: AppColors.textPrimary)), + const SizedBox(height: 10), + Row( + children: [ + Expanded( + child: OutlinedButton( + onPressed: () => setState(() => _state = _ActionState.idle), + style: OutlinedButton.styleFrom( + padding: const EdgeInsets.symmetric(vertical: 8), + side: BorderSide(color: AppColors.border), + ), + child: const Text('Vazgec', style: TextStyle(fontSize: 13)), + ), + ), + const SizedBox(width: 8), + Expanded( + child: FilledButton( + onPressed: _execute, + style: FilledButton.styleFrom( + backgroundColor: AppColors.cancelled, + padding: const EdgeInsets.symmetric(vertical: 8), + ), + child: const Text('Onayla', style: TextStyle(fontSize: 13)), + ), + ), + ], + ), + ], + ), + ); + } + + Widget _loadingCard() { + return Container( + padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 10), + decoration: BoxDecoration( + color: AppColors.background, + borderRadius: BorderRadius.circular(10), + border: Border.all(color: AppColors.border), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + const SizedBox( + width: 14, + height: 14, + child: CircularProgressIndicator(strokeWidth: 2, color: AppColors.accent), + ), + const SizedBox(width: 8), + Text('Isleniyor...', style: const TextStyle(fontSize: 13, color: AppColors.textSecondary)), + ], + ), + ); + } + + Widget _resultCard({required bool success}) { + return Container( + padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 10), + decoration: BoxDecoration( + color: success ? AppColors.successBg : AppColors.cancelledBg, + borderRadius: BorderRadius.circular(10), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon( + success ? Icons.check_circle_outline : Icons.error_outline, + size: 16, + color: success ? AppColors.success : AppColors.cancelled, + ), + const SizedBox(width: 8), + Flexible( + child: Text( + _resultMsg, + style: TextStyle( + fontSize: 13, + fontWeight: FontWeight.w500, + color: success ? AppColors.success : AppColors.cancelled, + ), + ), + ), + ], + ), + ); + } + + Widget _filesCard() { + return Container( + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + color: AppColors.inProgressBg, + borderRadius: BorderRadius.circular(10), + border: Border.all(color: AppColors.inProgress.withValues(alpha: 0.2)), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Row( + children: [ + const Icon(Icons.folder_open_outlined, size: 15, color: AppColors.inProgress), + const SizedBox(width: 6), + Text( + '${_files.length} dosya', + style: const TextStyle(fontSize: 12, fontWeight: FontWeight.w700, color: AppColors.inProgress), + ), + ], + ), + const SizedBox(height: 8), + ..._files.map((f) => _FileDownloadRow(file: f)), + ], + ), + ); + } +} + +// ── File download row ───────────────────────────────────────────────────────── + +class _FileDownloadRow extends StatefulWidget { + const _FileDownloadRow({required this.file}); + final JobFile file; + + @override + State<_FileDownloadRow> createState() => _FileDownloadRowState(); +} + +class _FileDownloadRowState extends State<_FileDownloadRow> { + bool _downloading = false; + + IconData get _icon => switch (widget.file.kind) { + JobFileKind.scan => Icons.view_in_ar_rounded, + JobFileKind.image => Icons.image_outlined, + JobFileKind.document => Icons.description_outlined, + }; + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 3), + child: Row( + children: [ + Icon(_icon, size: 14, color: AppColors.inProgress), + const SizedBox(width: 6), + Expanded( + child: Text( + widget.file.name, + style: const TextStyle(fontSize: 12, color: AppColors.textPrimary), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ), + const SizedBox(width: 4), + Text( + widget.file.sizeLabel, + style: const TextStyle(fontSize: 11, color: AppColors.textMuted), + ), + const SizedBox(width: 6), + _downloading + ? const SizedBox( + width: 16, + height: 16, + child: CircularProgressIndicator(strokeWidth: 1.5, color: AppColors.inProgress), + ) + : GestureDetector( + onTap: () async { + setState(() => _downloading = true); + await FileDownloadHelper.download(context, widget.file); + if (mounted) setState(() => _downloading = false); + }, + child: const Icon(Icons.download_outlined, size: 18, color: AppColors.inProgress), + ), + ], + ), + ); + } +} + +// ── Simple markdown renderer ────────────────────────────────────────────────── + +class _MarkdownText extends StatelessWidget { + const _MarkdownText(this.text, {required this.color}); + final String text; + final Color color; + + @override + Widget build(BuildContext context) { + if (text.isEmpty) return const SizedBox.shrink(); + final lines = text.split('\n'); + final widgets = []; + bool prevEmpty = false; + + for (final raw in lines) { + final line = raw.trimRight(); + if (line.isEmpty) { + if (!prevEmpty) widgets.add(const SizedBox(height: 6)); + prevEmpty = true; + continue; + } + prevEmpty = false; + + // Header ## or ### + if (line.startsWith('## ') || line.startsWith('### ')) { + final content = line.replaceFirst(RegExp(r'^#{2,3}\s+'), ''); + widgets.add(Padding( + padding: const EdgeInsets.only(top: 4, bottom: 2), + child: Text( + content, + style: TextStyle(fontSize: 14, fontWeight: FontWeight.w700, color: color), + ), + )); + continue; + } + + // Bullet + if (line.startsWith('- ') || line.startsWith('• ')) { + final content = line.substring(2); + widgets.add(Padding( + padding: const EdgeInsets.only(left: 4), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('• ', style: TextStyle(color: color, height: 1.5, fontSize: 14)), + Expanded(child: _inlineText(content, color)), + ], + ), + )); + continue; + } + + widgets.add(_inlineText(line, color)); + } + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: widgets, + ); + } + + Widget _inlineText(String text, Color baseColor) { + final spans = []; + final boldPattern = RegExp(r'\*\*(.+?)\*\*'); + int last = 0; + + for (final m in boldPattern.allMatches(text)) { + if (m.start > last) { + spans.add(TextSpan(text: text.substring(last, m.start))); + } + spans.add(TextSpan( + text: m.group(1), + style: const TextStyle(fontWeight: FontWeight.w700), + )); + last = m.end; + } + if (last < text.length) spans.add(TextSpan(text: text.substring(last))); + + return RichText( + text: TextSpan( + style: TextStyle(fontSize: 14, height: 1.5, color: baseColor), + children: spans, + ), + ); + } +} + +// ── Typing dots ─────────────────────────────────────────────────────────────── + +class _TypingDots extends StatefulWidget { + const _TypingDots(); + @override + State<_TypingDots> createState() => _TypingDotsState(); +} + +class _TypingDotsState extends State<_TypingDots> with SingleTickerProviderStateMixin { + late final AnimationController _ctrl; + + @override + void initState() { + super.initState(); + _ctrl = AnimationController(vsync: this, duration: const Duration(milliseconds: 900))..repeat(); + } + + @override + void dispose() { _ctrl.dispose(); super.dispose(); } + + @override + Widget build(BuildContext context) { + return SizedBox( + height: 18, + child: AnimatedBuilder( + animation: _ctrl, + builder: (_, __) => Row( + mainAxisSize: MainAxisSize.min, + children: List.generate(3, (i) { + final t = ((_ctrl.value * 3) - i).clamp(0.0, 1.0); + final opacity = (t < 0.5 ? t * 2 : (1 - t) * 2).clamp(0.3, 1.0); + return Container( + width: 6, height: 6, + margin: const EdgeInsets.symmetric(horizontal: 2), + decoration: BoxDecoration( + color: AppColors.accent.withValues(alpha: opacity), + shape: BoxShape.circle, + ), + ); + }), + ), + ), + ); + } +} + +// ── Input bar ───────────────────────────────────────────────────────────────── + +class _InputBar extends StatelessWidget { + const _InputBar({ + required this.controller, + required this.enabled, + required this.streaming, + required this.onSend, + }); + + final TextEditingController controller; + final bool enabled; + final bool streaming; + final VoidCallback onSend; + + @override + Widget build(BuildContext context) { + final bottom = MediaQuery.paddingOf(context).bottom; + return Container( + padding: EdgeInsets.fromLTRB(12, 8, 12, 8 + bottom), + decoration: BoxDecoration( + color: AppColors.surface, + border: Border(top: BorderSide(color: AppColors.border)), + ), + child: Row( + children: [ + Expanded( + child: TextField( + controller: controller, + enabled: enabled, + maxLines: 4, + minLines: 1, + textInputAction: TextInputAction.send, + onSubmitted: (_) => onSend(), + decoration: InputDecoration( + hintText: streaming ? 'Yanit bekleniyor...' : 'Bir sey sorun...', + hintStyle: const TextStyle(color: AppColors.textMuted), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(22), + borderSide: BorderSide(color: AppColors.border), + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(22), + borderSide: BorderSide(color: AppColors.border), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(22), + borderSide: const BorderSide(color: AppColors.accent, width: 1.5), + ), + contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10), + isDense: true, + ), + ), + ), + const SizedBox(width: 8), + GestureDetector( + onTap: enabled ? onSend : null, + child: AnimatedContainer( + duration: const Duration(milliseconds: 200), + width: 40, + height: 40, + decoration: BoxDecoration( + color: enabled ? AppColors.accent : AppColors.border, + shape: BoxShape.circle, + ), + child: Center( + child: streaming + ? const SizedBox( + width: 16, + height: 16, + child: CircularProgressIndicator(strokeWidth: 2, color: Colors.white), + ) + : const Icon(Icons.arrow_upward_rounded, color: Colors.white, size: 18), + ), + ), + ), + ], + ), + ); + } +} + +// ── Loading context ─────────────────────────────────────────────────────────── + +class _LoadingContext extends StatelessWidget { + const _LoadingContext(); + + @override + Widget build(BuildContext context) { + return const Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + CircularProgressIndicator(color: AppColors.accent, strokeWidth: 2), + SizedBox(height: 12), + Text('Veriler yukleniyor...', style: TextStyle(color: AppColors.textSecondary, fontSize: 13)), + ], + ), + ); + } +} diff --git a/lib/features/shared/job_files_panel.dart b/lib/features/shared/job_files_panel.dart new file mode 100644 index 0000000..ee910f1 --- /dev/null +++ b/lib/features/shared/job_files_panel.dart @@ -0,0 +1,619 @@ +import 'dart:async'; +import 'dart:io'; +import 'package:file_picker/file_picker.dart'; +import 'package:flutter/material.dart'; +import 'package:http/http.dart' as http; +import 'dart:convert'; +import 'package:path_provider/path_provider.dart'; +import 'package:share_plus/share_plus.dart'; + +import '../../core/api/pocketbase_client.dart'; +import '../../core/theme/app_theme.dart'; +import '../../models/job.dart'; +import '../../models/job_file.dart'; +import 'job_files_repository.dart'; + +const _maxFileSizeBytes = 50 * 1024 * 1024; // 50 MB per file +const _maxFilesPerJob = 10; + +class JobFilesPanel extends StatefulWidget { + const JobFilesPanel({ + super.key, + required this.job, + required this.filesFuture, + required this.onRefresh, + }); + + final Job job; + final Future> filesFuture; + final VoidCallback onRefresh; + + @override + State createState() => _JobFilesPanelState(); +} + +class _JobFilesPanelState extends State { + _UploadState? _upload; + List? _files; + bool _loadingFiles = false; + String? _filesError; + + @override + void initState() { + super.initState(); + _subscribeToFuture(widget.filesFuture); + } + + @override + void didUpdateWidget(JobFilesPanel oldWidget) { + super.didUpdateWidget(oldWidget); + if (oldWidget.filesFuture != widget.filesFuture) { + _subscribeToFuture(widget.filesFuture); + } + } + + void _subscribeToFuture(Future> future) { + setState(() { _loadingFiles = true; _filesError = null; }); + future.then((files) { + if (mounted) setState(() { _files = files; _loadingFiles = false; }); + }).catchError((e) { + if (mounted) setState(() { _filesError = _friendlyError(e); _loadingFiles = false; }); + }); + } + + static String _friendlyError(Object e) { + final s = e.toString(); + // Strip full ClientException URL dumps — show only the message part + final msgMatch = RegExp(r'message: ([^,}]+)').firstMatch(s); + if (msgMatch != null) return msgMatch.group(1)!.trim(); + if (s.length > 100) return 'Sunucu hatası'; + return s; + } + + Future _pickAndUpload() async { + final result = await FilePicker.platform.pickFiles( + allowMultiple: true, + withData: true, + type: FileType.custom, + allowedExtensions: [ + 'pdf', 'jpg', 'jpeg', 'png', 'webp', + 'stl', 'obj', 'ply', 'zip', 'opus', 'mp3', 'mp4' + ], + ); + if (result == null || result.files.isEmpty || !mounted) return; + + // Client-side size validation + for (final pf in result.files) { + if (pf.size > _maxFileSizeBytes) { + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + content: Text('${pf.name} 50 MB sınırını aşıyor (${_formatSize(pf.size)}).'), + backgroundColor: AppColors.cancelled, + )); + return; + } + } + + final existingCount = _files?.length ?? 0; + final remaining = _maxFilesPerJob - existingCount; + if (result.files.length > remaining) { + if (!mounted) return; + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + content: Text('Bu iş en fazla $_maxFilesPerJob dosya alabilir. Şu an $existingCount dosya var; en fazla $remaining dosya daha ekleyebilirsiniz.'), + backgroundColor: AppColors.cancelled, + )); + return; + } + + int uploadedCount = 0; + for (var i = 0; i < result.files.length; i++) { + final pf = result.files[i]; + if (pf.bytes == null) continue; + + setState(() { + _upload = _UploadState( + fileName: pf.name, + fileIndex: i + 1, + totalFiles: result.files.length, + progress: 0, + speedBytesPerSec: 0, + ); + }); + + try { + await _uploadWithProgress( + pf: pf, + onProgress: (progress, speed) { + if (mounted) { + setState(() { + _upload = _upload?.copyWith(progress: progress, speedBytesPerSec: speed); + }); + } + }, + ); + uploadedCount++; + } catch (e) { + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('${pf.name} yüklenemedi: ${_friendlyError(e)}'), backgroundColor: AppColors.cancelled), + ); + } + setState(() => _upload = null); + return; + } + } + + setState(() => _upload = null); + widget.onRefresh(); + if (mounted && uploadedCount > 0) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('$uploadedCount dosya yüklendi.')), + ); + } + } + + Future _uploadWithProgress({ + required PlatformFile pf, + required void Function(double progress, double speedBytesPerSec) onProgress, + }) async { + final bytes = pf.bytes!; + final pb = PocketBaseClient.instance.pb; + final baseUrl = 'https://pocket.kovaksoft.com'; + final uri = Uri.parse('$baseUrl/api/collections/job_files/records'); + + final ext = (pf.extension ?? '').toLowerCase(); + final kind = (ext == 'stl' || ext == 'obj' || ext == 'ply') + ? JobFileKind.scan + : (ext == 'pdf') ? JobFileKind.document : JobFileKind.image; + final mimeType = _mimeFromExt(ext); + final currentUserId = (pb.authStore.record?.id) ?? ''; + final token = pb.authStore.token; + + final startTime = DateTime.now().millisecondsSinceEpoch; + int sentBytes = 0; + + Stream> progressStream(List src) async* { + const chunkSize = 65536; + var offset = 0; + while (offset < src.length) { + final end = (offset + chunkSize).clamp(0, src.length); + final chunk = src.sublist(offset, end); + yield chunk; + offset = end; + sentBytes = offset; + final elapsedMs = DateTime.now().millisecondsSinceEpoch - startTime; + final speed = elapsedMs > 0 ? sentBytes / elapsedMs * 1000 : 0.0; + onProgress(sentBytes / src.length, speed); + // yield control so Flutter can rebuild + await Future.delayed(Duration.zero); + } + } + + final request = http.MultipartRequest('POST', uri) + ..headers['Authorization'] = 'Bearer $token' + ..fields['job_id'] = widget.job.id + ..fields['clinic_tenant_id'] = widget.job.clinicTenantId + ..fields['lab_tenant_id'] = widget.job.labTenantId + ..fields['uploaded_by'] = currentUserId + ..fields['kind'] = kind.value + ..fields['name'] = pf.name + ..fields['size'] = bytes.length.toString() + ..fields['mime_type'] = mimeType + ..files.add(http.MultipartFile( + 'file', + http.ByteStream(progressStream(bytes)), + bytes.length, + filename: pf.name, + )); + + final streamed = await request.send(); + final body = await streamed.stream.bytesToString(); + + if (streamed.statusCode < 200 || streamed.statusCode >= 300) { + String msg = 'HTTP ${streamed.statusCode}'; + try { + final j = jsonDecode(body) as Map; + msg = j['message'] as String? ?? msg; + } catch (_) {} + throw Exception(msg); + } + } + + Future _bulkDownload(List files) async { + if (files.isEmpty) return; + final messenger = ScaffoldMessenger.of(context); + try { + final pb = PocketBaseClient.instance.pb; + final fileToken = await pb.files.getToken(); + final dir = await getTemporaryDirectory(); + await dir.create(recursive: true); + + // Download all files in parallel + final results = await Future.wait( + files.where((f) => f.downloadUrl.isNotEmpty).map((file) async { + final uri = Uri.parse('${file.downloadUrl}?token=$fileToken'); + final response = await http.get(uri); + if (response.statusCode != 200) return null; + final path = '${dir.path}/${file.name}'; + await File(path).writeAsBytes(response.bodyBytes); + return XFile(path, mimeType: file.mimeType ?? 'application/octet-stream'); + }), + ); + + final xFiles = results.whereType().toList(); + if (xFiles.isEmpty) return; + await Share.shareXFiles( + xFiles, + subject: '${widget.job.patientCode} dosyaları', + sharePositionOrigin: const Rect.fromLTWH(100, 100, 200, 1), + ); + } catch (e) { + if (mounted) { + messenger.showSnackBar( + SnackBar(content: Text('İndirilemedi: $e'), backgroundColor: AppColors.cancelled), + ); + } + } + } + + Future _deleteFile(JobFile file) async { + final confirmed = await showDialog( + context: context, + builder: (ctx) => AlertDialog( + title: const Text('Dosyayı Sil'), + content: Text(file.name), + actions: [ + TextButton(onPressed: () => Navigator.pop(ctx, false), child: const Text('İptal')), + FilledButton( + style: FilledButton.styleFrom(backgroundColor: AppColors.cancelled), + onPressed: () => Navigator.pop(ctx, true), + child: const Text('Sil'), + ), + ], + ), + ); + if (confirmed != true || !mounted) return; + + // Optimistic: remove immediately from local list + setState(() => _files = _files?.where((f) => f.id != file.id).toList()); + + try { + await JobFilesRepository.instance.deleteFile(file.id); + } catch (e) { + final is404 = e.toString().contains('404') || e.toString().contains('wasn\'t found'); + if (!is404) { + // Revert only on transient errors (network, 500) — not when already deleted + setState(() => _files = [...?_files, file]); + } + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(is404 ? 'Dosya zaten silinmiş.' : 'Silinemedi: ${_friendlyError(e)}'), + backgroundColor: AppColors.cancelled, + ), + ); + } + } + } + + Future _downloadFile(JobFile file, Rect shareOrigin) async { + if (file.downloadUrl.isEmpty) return; + final messenger = ScaffoldMessenger.of(context); + try { + final pb = PocketBaseClient.instance.pb; + final fileToken = await pb.files.getToken(); + final uri = Uri.parse('${file.downloadUrl}?token=$fileToken'); + final response = await http.get(uri); + if (response.statusCode != 200) { + throw Exception('HTTP ${response.statusCode}'); + } + final dir = await getTemporaryDirectory(); + await dir.create(recursive: true); + final path = '${dir.path}/${file.name}'; + await File(path).writeAsBytes(response.bodyBytes); + await Share.shareXFiles( + [XFile(path, mimeType: file.mimeType ?? 'application/octet-stream')], + subject: file.name, + sharePositionOrigin: shareOrigin, + ); + } catch (e) { + if (mounted) { + messenger.showSnackBar( + SnackBar( + content: Text('İndirilemedi: $e'), + backgroundColor: AppColors.cancelled, + ), + ); + } + } + } + + @override + Widget build(BuildContext context) { + final uploading = _upload != null; + return Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: AppColors.surface, + borderRadius: BorderRadius.circular(16), + border: Border.all(color: AppColors.border), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + const Icon(Icons.attach_file_rounded, size: 18, color: AppColors.accent), + const SizedBox(width: 6), + const Expanded( + child: Text('Dosyalar', style: TextStyle(fontWeight: FontWeight.w600, fontSize: 14)), + ), + if (!uploading) ...[ + if ((_files?.length ?? 0) >= 2) + TextButton.icon( + onPressed: () => _bulkDownload(_files!), + icon: const Icon(Icons.download_for_offline_outlined, size: 16), + label: const Text('Hepsini İndir'), + style: TextButton.styleFrom( + foregroundColor: AppColors.accent, + padding: const EdgeInsets.symmetric(horizontal: 8), + ), + ), + TextButton.icon( + onPressed: _pickAndUpload, + icon: const Icon(Icons.upload_rounded, size: 16), + label: const Text('Yükle'), + style: TextButton.styleFrom( + foregroundColor: AppColors.accent, + padding: const EdgeInsets.symmetric(horizontal: 8), + ), + ), + ], + ], + ), + + if (_upload != null) ...[ + const SizedBox(height: 10), + _UploadProgressBar(state: _upload!), + ], + + const SizedBox(height: 8), + Builder(builder: (ctx) { + if (_loadingFiles && _files == null) { + return const Padding( + padding: EdgeInsets.symmetric(vertical: 12), + child: Center(child: CircularProgressIndicator(strokeWidth: 2)), + ); + } + if (_filesError != null && _files == null) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 8), + child: Text( + 'Dosyalar yüklenemedi: $_filesError', + style: const TextStyle(color: AppColors.cancelled, fontSize: 12), + ), + ); + } + final files = _files ?? []; + if (files.isEmpty && !uploading) { + return const Padding( + padding: EdgeInsets.symmetric(vertical: 8), + child: Text( + 'Henüz dosya eklenmemiş.', + style: TextStyle(color: AppColors.textMuted, fontSize: 13), + ), + ); + } + return Column( + children: files + .map((f) => _FileRow( + file: f, + onDelete: () => _deleteFile(f), + onDownload: (origin) => _downloadFile(f, origin), + )) + .toList(), + ); + }, + ), + ], + ), + ); + } + + static String _formatSize(int bytes) { + if (bytes < 1024) return '$bytes B'; + if (bytes < 1024 * 1024) return '${(bytes / 1024).toStringAsFixed(1)} KB'; + return '${(bytes / (1024 * 1024)).toStringAsFixed(1)} MB'; + } + + static String _mimeFromExt(String ext) => switch (ext) { + 'jpg' || 'jpeg' => 'image/jpeg', + 'png' => 'image/png', + 'webp' => 'image/webp', + 'pdf' => 'application/pdf', + 'stl' => 'model/stl', + 'zip' => 'application/zip', + 'mp3' => 'audio/mpeg', + 'mp4' => 'video/mp4', + 'opus' => 'audio/opus', + _ => 'application/octet-stream', + }; +} + +// ── Upload Progress Bar ─────────────────────────────────────────────────────── + +class _UploadProgressBar extends StatelessWidget { + const _UploadProgressBar({required this.state}); + final _UploadState state; + + @override + Widget build(BuildContext context) { + final pct = (state.progress * 100).toStringAsFixed(0); + final speed = state.speedBytesPerSec; + final speedLabel = speed >= 1024 * 1024 + ? '${(speed / 1024 / 1024).toStringAsFixed(1)} MB/s' + : '${(speed / 1024).toStringAsFixed(0)} KB/s'; + + final fileLabel = state.totalFiles > 1 + ? '${state.fileIndex}/${state.totalFiles} — ${state.fileName}' + : state.fileName; + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Expanded( + child: Text( + fileLabel, + style: const TextStyle(fontSize: 12, color: AppColors.textSecondary), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ), + const SizedBox(width: 8), + Text( + '$pct% · $speedLabel', + style: const TextStyle( + fontSize: 12, + fontWeight: FontWeight.w600, + color: AppColors.accent, + fontFeatures: [FontFeature.tabularFigures()], + ), + ), + ], + ), + const SizedBox(height: 6), + ClipRRect( + borderRadius: BorderRadius.circular(4), + child: LinearProgressIndicator( + value: state.progress, + minHeight: 6, + backgroundColor: AppColors.background, + valueColor: const AlwaysStoppedAnimation(AppColors.accent), + ), + ), + ], + ); + } +} + +// ── File Row ────────────────────────────────────────────────────────────────── + +class _FileRow extends StatefulWidget { + const _FileRow({ + required this.file, + required this.onDelete, + required this.onDownload, + }); + final JobFile file; + final VoidCallback onDelete; + final Future Function(Rect) onDownload; + + @override + State<_FileRow> createState() => _FileRowState(); +} + +class _FileRowState extends State<_FileRow> { + bool _downloading = false; + + IconData get _icon => switch (widget.file.kind) { + JobFileKind.scan => Icons.view_in_ar_rounded, + JobFileKind.image => Icons.image_outlined, + JobFileKind.document => Icons.description_outlined, + }; + + final _downloadKey = GlobalKey(); + + Future _handleDownload() async { + setState(() => _downloading = true); + final box = _downloadKey.currentContext?.findRenderObject() as RenderBox?; + final origin = box != null + ? box.localToGlobal(Offset.zero) & box.size + : const Rect.fromLTWH(100, 100, 200, 1); + try { + await widget.onDownload(origin); + } finally { + if (mounted) setState(() => _downloading = false); + } + } + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 5), + child: Row( + children: [ + Icon(_icon, size: 18, color: AppColors.textMuted), + const SizedBox(width: 8), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + widget.file.name, + style: const TextStyle(fontSize: 13, fontWeight: FontWeight.w500), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + Text( + '${widget.file.kind.label} · ${widget.file.sizeLabel}', + style: const TextStyle(fontSize: 11, color: AppColors.textMuted), + ), + ], + ), + ), + if (_downloading) + const SizedBox( + width: 18, + height: 18, + child: CircularProgressIndicator(strokeWidth: 2, color: AppColors.accent), + ) + else + IconButton( + key: _downloadKey, + onPressed: _handleDownload, + icon: const Icon(Icons.download_outlined, size: 18, color: AppColors.accent), + padding: EdgeInsets.zero, + constraints: const BoxConstraints(), + tooltip: 'İndir', + ), + const SizedBox(width: 4), + IconButton( + onPressed: widget.onDelete, + icon: const Icon(Icons.delete_outline_rounded, size: 18, color: AppColors.cancelled), + padding: EdgeInsets.zero, + constraints: const BoxConstraints(), + tooltip: 'Sil', + ), + ], + ), + ); + } +} + +// ── Upload State ────────────────────────────────────────────────────────────── + +class _UploadState { + const _UploadState({ + required this.fileName, + required this.fileIndex, + required this.totalFiles, + required this.progress, + required this.speedBytesPerSec, + }); + + final String fileName; + final int fileIndex; + final int totalFiles; + final double progress; + final double speedBytesPerSec; + + _UploadState copyWith({double? progress, double? speedBytesPerSec}) => + _UploadState( + fileName: fileName, + fileIndex: fileIndex, + totalFiles: totalFiles, + progress: progress ?? this.progress, + speedBytesPerSec: speedBytesPerSec ?? this.speedBytesPerSec, + ); +} diff --git a/lib/features/shared/job_files_repository.dart b/lib/features/shared/job_files_repository.dart new file mode 100644 index 0000000..4a99d05 --- /dev/null +++ b/lib/features/shared/job_files_repository.dart @@ -0,0 +1,59 @@ +import 'package:http/http.dart' as http; +import 'package:pocketbase/pocketbase.dart'; +import '../../core/api/pocketbase_client.dart'; +import '../../models/job_file.dart'; + +class JobFilesRepository { + JobFilesRepository._(); + static final instance = JobFilesRepository._(); + + PocketBase get _pb => PocketBaseClient.instance.pb; + static const _baseUrl = 'https://pocket.kovaksoft.com'; + + String get _currentUserId => (_pb.authStore.record?.id) ?? ''; + + Future> listForJob(String jobId) async { + final result = await _pb.collection('job_files').getList( + filter: 'job_id = "$jobId"', + perPage: 200, + ); + final files = result.items.map((r) => JobFile.fromJson(r.toJson(), _baseUrl)).toList() + ..sort((a, b) => b.createdAt.compareTo(a.createdAt)); + return files; + } + + Future uploadFile({ + required String jobId, + required String clinicTenantId, + required String labTenantId, + required JobFileKind kind, + required String name, + required int size, + required List bytes, + String? mimeType, + }) async { + final multipartFile = http.MultipartFile.fromBytes( + 'file', + bytes, + filename: name, + ); + final record = await _pb.collection('job_files').create( + body: { + 'job_id': jobId, + 'clinic_tenant_id': clinicTenantId, + 'lab_tenant_id': labTenantId, + 'uploaded_by': _currentUserId, + 'kind': kind.value, + 'name': name, + 'size': size, + if (mimeType != null) 'mime_type': mimeType, + }, + files: [multipartFile], + ); + return JobFile.fromJson(record.toJson(), _baseUrl); + } + + Future deleteFile(String fileId) async { + await _pb.collection('job_files').delete(fileId); + } +} diff --git a/lib/features/shared/reports_repository.dart b/lib/features/shared/reports_repository.dart new file mode 100644 index 0000000..5e0238e --- /dev/null +++ b/lib/features/shared/reports_repository.dart @@ -0,0 +1,325 @@ +import 'package:pocketbase/pocketbase.dart'; +import '../../core/api/pocketbase_client.dart'; +import '../../models/tenant.dart'; + +// ── Value objects ───────────────────────────────────────────────────────────── + +const _monthLabels = ['Oca','Şub','Mar','Nis','May','Haz','Tem','Ağu','Eyl','Eki','Kas','Ara']; + +class MonthlyCount { + const MonthlyCount({required this.year, required this.month, required this.count}); + final int year, month, count; + String get label => _monthLabels[month - 1]; +} + +class MonthlyAmount { + const MonthlyAmount({required this.year, required this.month, required this.amount}); + final int year, month; + final double amount; + String get label => _monthLabels[month - 1]; +} + +class CounterpartStat { + const CounterpartStat({required this.name, required this.jobCount, required this.pendingRevenue, required this.paidRevenue}); + final String name; + final int jobCount; + final double pendingRevenue, paidRevenue; + double get totalRevenue => pendingRevenue + paidRevenue; +} + +class ActivityItem { + const ActivityItem({required this.jobId, this.patientCode, required this.action, required this.createdAt, this.note}); + final String jobId, action; + final String? patientCode, note; + final DateTime createdAt; + + String get actionLabel => switch (action) { + 'accepted' => 'İş kabul edildi', + 'handed_to_clinic' => 'Provaya gönderildi', + 'approved' => 'Onaylandı', + 'revision_requested' => 'Revizyon istendi', + 'delivered' => 'Teslim edildi', + 'cancelled' => 'İptal edildi', + _ => action, + }; + + bool get isNegative => action == 'revision_requested' || action == 'cancelled'; + bool get isPositive => action == 'delivered' || action == 'approved' || action == 'accepted'; +} + +// ── Aggregated metrics ──────────────────────────────────────────────────────── + +class ReportMetrics { + const ReportMetrics({ + required this.activeJobs, + required this.completedThisMonth, + required this.overdueJobs, + required this.revisionRate, + required this.avgCompletionDays, + required this.totalRevenue, + required this.pendingRevenue, + required this.currency, + required this.jobsByStatus, + required this.monthlyCounts, + required this.monthlyRevenue, + required this.byProstheticType, + required this.counterpartStats, + required this.recentActivity, + }); + + final int activeJobs; + final int completedThisMonth; + final int overdueJobs; + final double revisionRate; // 0-100 + final double avgCompletionDays; + final double totalRevenue; + final double pendingRevenue; + final String currency; + + final Map jobsByStatus; + final List monthlyCounts; + final List monthlyRevenue; + final Map byProstheticType; + final List counterpartStats; + final List recentActivity; +} + +// ── Repository ──────────────────────────────────────────────────────────────── + +class ReportsRepository { + ReportsRepository._(); + static final instance = ReportsRepository._(); + + PocketBase get _pb => PocketBaseClient.instance.pb; + + Future load(String tenantId, TenantKind kind) async { + final jobFilter = kind == TenantKind.lab + ? 'lab_tenant_id = "$tenantId"' + : 'clinic_tenant_id = "$tenantId"'; + final historyFilter = kind == TenantKind.lab + ? 'lab_tenant_id = "$tenantId"' + : 'clinic_tenant_id = "$tenantId"'; + + final results = await Future.wait([ + _pb.collection('jobs').getList( + filter: jobFilter, + perPage: 500, + expand: kind == TenantKind.lab ? 'clinic_tenant_id' : 'lab_tenant_id', + fields: 'id,status,prosthetic_type,clinic_tenant_id,lab_tenant_id,created,updated,due_date,price,currency,expand', + ).catchError((_) => ResultList()), + _pb.collection('finance_entries').getList( + filter: 'tenant_id = "$tenantId"', + perPage: 300, + fields: 'id,amount,currency,status,created,counterparty_name', + ).catchError((_) => ResultList()), + _pb.collection('job_status_history').getList( + filter: historyFilter, + perPage: 100, + expand: 'job_id', + fields: 'id,action_type,created,note,job_id,expand', + ).catchError((_) => ResultList()), + ]); + + final jobRecords = (results[0] as ResultList).items; + final financeRecords = (results[1] as ResultList).items; + final historyRecords = (results[2] as ResultList).items; + + return _aggregate(tenantId, kind, jobRecords, financeRecords, historyRecords); + } + + ReportMetrics _aggregate( + String tenantId, + TenantKind kind, + List jobRecords, + List financeRecords, + List historyRecords, + ) { + final now = DateTime.now(); + final thisMonthStart = DateTime(now.year, now.month, 1); + + // ── Parse jobs ──────────────────────────────────────────────────────────── + String _s(Map j, String k) { + final v = j[k]; + if (v == null || v == '') return ''; + return v.toString(); + } + + final jobs = jobRecords.map((r) { + final j = r.toJson(); + final exp = j['expand'] as Map?; + final cpKey = kind == TenantKind.lab ? 'clinic_tenant_id' : 'lab_tenant_id'; + final cpExp = exp?[cpKey] as Map?; + return _RawJob( + id: _s(j, 'id'), + status: _s(j, 'status'), + prostheticType: _s(j, 'prosthetic_type'), + clinicTenantId: _s(j, 'clinic_tenant_id'), + labTenantId: _s(j, 'lab_tenant_id'), + created: _parseDate(j['created']), + updated: _parseDate(j['updated']), + dueDate: j['due_date'] != null && j['due_date'] != '' ? _parseDate(j['due_date']) : null, + currency: _s(j, 'currency').isNotEmpty ? _s(j, 'currency') : 'TRY', + counterpartName: cpExp?['company_name'] as String? ?? '', + ); + }).toList(); + + // ── Parse finance ───────────────────────────────────────────────────────── + String topCurrency = 'TRY'; + final financeList = financeRecords.map((r) { + final j = r.toJson(); + final cur = (j['currency'] as String?) ?? 'TRY'; + if (cur.isNotEmpty) topCurrency = cur; + return _RawFinance( + status: (j['status'] as String?) ?? '', + amount: (j['amount'] as num?)?.toDouble() ?? 0, + created: _parseDate(j['created']), + counterpartyName: j['counterparty_name'] as String?, + ); + }).toList(); + + // ── Parse history ───────────────────────────────────────────────────────── + final activity = historyRecords.map((r) { + final j = r.toJson(); + final exp = j['expand'] as Map?; + final jobExp = exp?['job_id'] as Map?; + return ActivityItem( + jobId: (j['job_id'] as String?) ?? '', + patientCode: jobExp?['patient_code'] as String?, + action: (j['action_type'] as String?) ?? '', + createdAt: _parseDate(j['created']), + note: j['note'] as String?, + ); + }).toList() + ..sort((a, b) => b.createdAt.compareTo(a.createdAt)); + + // ── KPI metrics ─────────────────────────────────────────────────────────── + final activeJobs = jobs.where((j) => j.status == 'in_progress' || j.status == 'pending').length; + final completedThisMonth = jobs.where((j) => j.status == 'delivered' && j.updated.isAfter(thisMonthStart)).length; + final overdueJobs = jobs.where((j) => + j.dueDate != null && + j.dueDate!.isBefore(now) && + (j.status == 'in_progress' || j.status == 'pending')).length; + + final revisions = activity.where((a) => a.action == 'revision_requested').length; + final revisionRate = activity.isNotEmpty ? revisions / activity.length * 100 : 0.0; + + final deliveredJobs = jobs.where((j) => j.status == 'delivered').toList(); + final avgCompletionDays = deliveredJobs.isNotEmpty + ? deliveredJobs + .fold(0, (s, j) => s + j.updated.difference(j.created).inDays) / + deliveredJobs.length + : 0.0; + + // ── Finance totals ──────────────────────────────────────────────────────── + final totalRevenue = financeList.where((f) => f.status == 'paid').fold(0, (s, f) => s + f.amount); + final pendingRevenue = financeList.where((f) => f.status == 'pending').fold(0, (s, f) => s + f.amount); + + // ── Job status distribution ─────────────────────────────────────────────── + final Map jobsByStatus = {}; + for (final j in jobs) { + jobsByStatus[j.status] = (jobsByStatus[j.status] ?? 0) + 1; + } + + // ── Monthly job counts (last 6 months) ──────────────────────────────────── + final monthKeys = List.generate(6, (i) { + final d = DateTime(now.year, now.month - 5 + i, 1); + return '${d.year}-${d.month}'; + }); + final monthMap = {for (final k in monthKeys) k: 0}; + for (final j in jobs) { + final key = '${j.created.year}-${j.created.month}'; + if (monthMap.containsKey(key)) monthMap[key] = monthMap[key]! + 1; + } + final monthlyCounts = monthKeys.map((k) { + final parts = k.split('-'); + return MonthlyCount(year: int.parse(parts[0]), month: int.parse(parts[1]), count: monthMap[k]!); + }).toList(); + + // ── Monthly revenue (last 6 months) ────────────────────────────────────── + final revMap = {for (final k in monthKeys) k: 0.0}; + for (final f in financeList) { + if (f.status == 'paid') { + final key = '${f.created.year}-${f.created.month}'; + if (revMap.containsKey(key)) revMap[key] = revMap[key]! + f.amount; + } + } + final monthlyRevenue = monthKeys.map((k) { + final parts = k.split('-'); + return MonthlyAmount(year: int.parse(parts[0]), month: int.parse(parts[1]), amount: revMap[k]!); + }).toList(); + + // ── By prosthetic type ──────────────────────────────────────────────────── + final Map byType = {}; + for (final j in jobs) { + if (j.prostheticType.isNotEmpty) { + byType[j.prostheticType] = (byType[j.prostheticType] ?? 0) + 1; + } + } + + // ── By counterpart ──────────────────────────────────────────────────────── + final Map cpCount = {}; + final Map cpPending = {}, cpPaid = {}; + for (final j in jobs) { + final name = j.counterpartName.isNotEmpty ? j.counterpartName : '—'; + cpCount[name] = (cpCount[name] ?? 0) + 1; + } + for (final f in financeList) { + final name = f.counterpartyName ?? '—'; + if (f.status == 'pending') cpPending[name] = (cpPending[name] ?? 0) + f.amount; + if (f.status == 'paid') cpPaid[name] = (cpPaid[name] ?? 0) + f.amount; + } + final counterparts = cpCount.entries + .map((e) => CounterpartStat( + name: e.key, + jobCount: e.value, + pendingRevenue: cpPending[e.key] ?? 0, + paidRevenue: cpPaid[e.key] ?? 0, + )) + .toList() + ..sort((a, b) => b.jobCount.compareTo(a.jobCount)); + + return ReportMetrics( + activeJobs: activeJobs, + completedThisMonth: completedThisMonth, + overdueJobs: overdueJobs, + revisionRate: revisionRate, + avgCompletionDays: avgCompletionDays, + totalRevenue: totalRevenue, + pendingRevenue: pendingRevenue, + currency: topCurrency, + jobsByStatus: jobsByStatus, + monthlyCounts: monthlyCounts, + monthlyRevenue: monthlyRevenue, + byProstheticType: byType, + counterpartStats: counterparts.take(5).toList(), + recentActivity: activity.take(30).toList(), + ); + } + + static DateTime _parseDate(dynamic v) { + if (v == null || v == '') return DateTime(2000); + return DateTime.tryParse(v.toString()) ?? DateTime(2000); + } +} + +// ── Internal raw models ─────────────────────────────────────────────────────── + +class _RawJob { + const _RawJob({ + required this.id, required this.status, required this.prostheticType, + required this.clinicTenantId, required this.labTenantId, + required this.created, required this.updated, + required this.currency, required this.counterpartName, this.dueDate, + }); + final String id, status, prostheticType, clinicTenantId, labTenantId, currency, counterpartName; + final DateTime created, updated; + final DateTime? dueDate; +} + +class _RawFinance { + const _RawFinance({required this.status, required this.amount, required this.created, this.counterpartyName}); + final String status; + final double amount; + final DateTime created; + final String? counterpartyName; +} diff --git a/lib/features/shared/reports_screen.dart b/lib/features/shared/reports_screen.dart new file mode 100644 index 0000000..6393aac --- /dev/null +++ b/lib/features/shared/reports_screen.dart @@ -0,0 +1,690 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:intl/intl.dart'; + +import '../../core/providers/auth_provider.dart'; +import '../../core/theme/app_theme.dart'; +import '../../core/widgets/gradient_app_bar.dart'; +import '../../models/job.dart'; +import '../../models/tenant.dart'; +import 'reports_repository.dart'; + +class ReportsScreen extends ConsumerStatefulWidget { + const ReportsScreen({super.key}); + + @override + ConsumerState createState() => _ReportsScreenState(); +} + +class _ReportsScreenState extends ConsumerState + with SingleTickerProviderStateMixin { + late TabController _tabController; + late Future _future; + + @override + void initState() { + super.initState(); + _tabController = TabController(length: 4, vsync: this); + _tabController.addListener(() { if (mounted) setState(() {}); }); + _load(); + } + + @override + void dispose() { + _tabController.dispose(); + super.dispose(); + } + + void _load() { + final auth = ref.read(authProvider); + final tenantId = auth.activeTenant!.tenant.id; + final kind = auth.activeTenant!.tenant.kind; + setState(() { + _future = ReportsRepository.instance.load(tenantId, kind); + }); + } + + @override + Widget build(BuildContext context) { + final kind = ref.watch(authProvider).activeTenant?.tenant.kind ?? TenantKind.lab; + final counterpartLabel = kind == TenantKind.lab ? 'Klinikler' : 'Laboratuvarlar'; + + return Scaffold( + backgroundColor: AppColors.background, + appBar: GradientAppBar( + title: 'Raporlar', + category: 'YÖNETİCİ', + actions: [ + IconButton( + icon: const Icon(Icons.refresh_rounded), + onPressed: _load, + tooltip: 'Yenile', + ), + ], + ), + body: FutureBuilder( + future: _future, + builder: (context, snap) { + if (snap.connectionState == ConnectionState.waiting) { + return const Center(child: CircularProgressIndicator(color: AppColors.accent)); + } + if (snap.hasError) { + return Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const Icon(Icons.error_outline_rounded, color: AppColors.cancelled, size: 48), + const SizedBox(height: 12), + Text('Veriler yüklenemedi', style: const TextStyle(color: AppColors.textSecondary)), + const SizedBox(height: 12), + FilledButton.icon( + onPressed: _load, + icon: const Icon(Icons.refresh_rounded, size: 18), + label: const Text('Tekrar Dene'), + ), + ], + ), + ); + } + final m = snap.data!; + return Column( + children: [ + // Tab bar + Container( + color: AppColors.surface, + child: TabBar( + controller: _tabController, + labelColor: AppColors.accent, + unselectedLabelColor: AppColors.textSecondary, + indicatorColor: AppColors.accent, + indicatorSize: TabBarIndicatorSize.tab, + labelStyle: const TextStyle(fontSize: 13, fontWeight: FontWeight.w600), + tabs: [ + const Tab(text: 'Özet'), + const Tab(text: 'Finans'), + const Tab(text: 'Aktivite'), + Tab(text: counterpartLabel), + ], + ), + ), + Expanded( + child: TabBarView( + controller: _tabController, + children: [ + _SummaryTab(metrics: m), + _FinanceTab(metrics: m), + _ActivityTab(metrics: m), + _CounterpartTab(metrics: m, label: counterpartLabel), + ], + ), + ), + ], + ); + }, + ), + ); + } +} + +// ── Shared layout helpers ───────────────────────────────────────────────────── + +class _TabBody extends StatelessWidget { + const _TabBody({required this.children}); + final List children; + + @override + Widget build(BuildContext context) => ListView( + padding: const EdgeInsets.fromLTRB(16, 16, 16, 32), + children: children, + ); +} + +class _Card extends StatelessWidget { + const _Card({required this.child, this.padding = const EdgeInsets.all(16)}); + final Widget child; + final EdgeInsets padding; + + @override + Widget build(BuildContext context) => Container( + margin: const EdgeInsets.only(bottom: 12), + padding: padding, + decoration: BoxDecoration( + color: AppColors.surface, + borderRadius: BorderRadius.circular(16), + border: Border.all(color: AppColors.border), + boxShadow: [BoxShadow(color: Colors.black.withValues(alpha: 0.03), blurRadius: 8, offset: const Offset(0, 2))], + ), + child: child, + ); +} + +class _SectionHeader extends StatelessWidget { + const _SectionHeader(this.title, {this.subtitle}); + final String title; + final String? subtitle; + + @override + Widget build(BuildContext context) => Padding( + padding: const EdgeInsets.only(bottom: 10, top: 4), + child: Row( + children: [ + Expanded( + child: Text(title, style: const TextStyle(fontSize: 15, fontWeight: FontWeight.w700, color: AppColors.textPrimary)), + ), + if (subtitle != null) + Text(subtitle!, style: const TextStyle(fontSize: 12, color: AppColors.textMuted)), + ], + ), + ); +} + +// ── KPI Chips ───────────────────────────────────────────────────────────────── + +class _KpiRow extends StatelessWidget { + const _KpiRow({required this.metrics}); + final ReportMetrics metrics; + + @override + Widget build(BuildContext context) { + final fmt = NumberFormat.currency(locale: 'tr_TR', symbol: metrics.currency, decimalDigits: 0); + return SingleChildScrollView( + scrollDirection: Axis.horizontal, + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), + child: Row( + children: [ + _Kpi(label: 'Aktif İşler', value: '${metrics.activeJobs}', icon: Icons.work_outline_rounded, color: AppColors.inProgress), + const SizedBox(width: 10), + _Kpi(label: 'Bu Ay Tamamlandı', value: '${metrics.completedThisMonth}', icon: Icons.check_circle_outline_rounded, color: AppColors.success), + const SizedBox(width: 10), + _Kpi(label: 'Bekleyen Gelir', value: fmt.format(metrics.pendingRevenue), icon: Icons.hourglass_empty_rounded, color: AppColors.pending), + const SizedBox(width: 10), + _Kpi(label: 'Ort. Süre', value: '${metrics.avgCompletionDays.toStringAsFixed(1)} gün', icon: Icons.timer_outlined, color: AppColors.accent), + const SizedBox(width: 10), + _Kpi(label: 'Revizyon Oranı', value: '%${metrics.revisionRate.toStringAsFixed(0)}', icon: Icons.loop_rounded, color: metrics.revisionRate > 20 ? AppColors.cancelled : AppColors.textSecondary), + if (metrics.overdueJobs > 0) ...[ + const SizedBox(width: 10), + _Kpi(label: 'Gecikmiş', value: '${metrics.overdueJobs}', icon: Icons.schedule_rounded, color: AppColors.cancelled), + ], + ], + ), + ); + } +} + +class _Kpi extends StatelessWidget { + const _Kpi({required this.label, required this.value, required this.icon, required this.color}); + final String label, value; + final IconData icon; + final Color color; + + @override + Widget build(BuildContext context) => Container( + padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 12), + decoration: BoxDecoration( + color: AppColors.surface, + borderRadius: BorderRadius.circular(14), + border: Border.all(color: AppColors.border), + boxShadow: [BoxShadow(color: Colors.black.withValues(alpha: 0.03), blurRadius: 6)], + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon(icon, size: 14, color: color), + const SizedBox(width: 4), + Text(label, style: const TextStyle(fontSize: 11, color: AppColors.textMuted)), + ], + ), + const SizedBox(height: 6), + Text(value, style: TextStyle(fontSize: 20, fontWeight: FontWeight.w800, color: color)), + ], + ), + ); +} + +// ── Özet Tab ────────────────────────────────────────────────────────────────── + +class _SummaryTab extends StatelessWidget { + const _SummaryTab({required this.metrics}); + final ReportMetrics metrics; + + @override + Widget build(BuildContext context) { + return _TabBody(children: [ + _KpiRow(metrics: metrics), + const _SectionHeader('İş Durumu Dağılımı'), + _Card( + child: Column( + children: _statusOrder.where((s) => metrics.jobsByStatus.containsKey(s)).map((s) { + final count = metrics.jobsByStatus[s] ?? 0; + final total = metrics.jobsByStatus.values.fold(0, (a, b) => a + b); + return _HBarRow( + label: _statusLabel(s), + value: count, + max: total, + color: _statusColor(s), + ); + }).toList(), + ), + ), + const _SectionHeader('Son 6 Aylık İş Trendi'), + _Card(child: _VBarChart(data: metrics.monthlyCounts, color: AppColors.accent)), + ]); + } + + static const _statusOrder = ['in_progress', 'pending', 'sent', 'delivered', 'cancelled']; + + static String _statusLabel(String s) => switch (s) { + 'pending' => 'Bekliyor', + 'in_progress' => 'İşlemde', + 'sent' => 'Gönderildi', + 'delivered' => 'Teslim', + 'cancelled' => 'İptal', + _ => s, + }; + + static Color _statusColor(String s) => switch (s) { + 'pending' => AppColors.pending, + 'in_progress' => AppColors.inProgress, + 'sent' => AppColors.accent, + 'delivered' => AppColors.success, + 'cancelled' => AppColors.cancelled, + _ => AppColors.textMuted, + }; +} + +// ── Finans Tab ──────────────────────────────────────────────────────────────── + +class _FinanceTab extends StatelessWidget { + const _FinanceTab({required this.metrics}); + final ReportMetrics metrics; + + @override + Widget build(BuildContext context) { + final fmt = NumberFormat.currency(locale: 'tr_TR', symbol: metrics.currency, decimalDigits: 0); + final total = metrics.totalRevenue + metrics.pendingRevenue; + return _TabBody(children: [ + const _SectionHeader('Gelir Özeti'), + Row( + children: [ + Expanded( + child: _Card( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row(children: [ + Container(width: 8, height: 8, decoration: BoxDecoration(color: AppColors.success, shape: BoxShape.circle)), + const SizedBox(width: 6), + const Text('Tahsil Edildi', style: TextStyle(fontSize: 12, color: AppColors.textMuted)), + ]), + const SizedBox(height: 6), + Text(fmt.format(metrics.totalRevenue), + style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w800, color: AppColors.success)), + ], + ), + ), + ), + const SizedBox(width: 10), + Expanded( + child: _Card( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row(children: [ + Container(width: 8, height: 8, decoration: BoxDecoration(color: AppColors.pending, shape: BoxShape.circle)), + const SizedBox(width: 6), + const Text('Bekleyen', style: TextStyle(fontSize: 12, color: AppColors.textMuted)), + ]), + const SizedBox(height: 6), + Text(fmt.format(metrics.pendingRevenue), + style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w800, color: AppColors.pending)), + ], + ), + ), + ), + ], + ), + if (total > 0) _Card( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text('Tahsilat Oranı', style: TextStyle(fontSize: 13, fontWeight: FontWeight.w600)), + const SizedBox(height: 10), + ClipRRect( + borderRadius: BorderRadius.circular(6), + child: LinearProgressIndicator( + value: total > 0 ? metrics.totalRevenue / total : 0, + minHeight: 12, + backgroundColor: AppColors.pendingBg, + valueColor: const AlwaysStoppedAnimation(AppColors.success), + ), + ), + const SizedBox(height: 6), + Text('${(metrics.totalRevenue / total * 100).toStringAsFixed(0)}% tahsil edildi', + style: const TextStyle(fontSize: 12, color: AppColors.textMuted)), + ], + ), + ), + const SizedBox(height: 4), + const _SectionHeader('Aylık Gelir Trendi'), + _Card(child: _VBarChart( + data: metrics.monthlyRevenue.map((m) => MonthlyCount(year: m.year, month: m.month, count: m.amount.round())).toList(), + color: AppColors.success, + formatValue: (v) => NumberFormat.compactCurrency(locale: 'tr_TR', symbol: metrics.currency, decimalDigits: 0).format(v), + )), + ]); + } +} + +// ── Aktivite Tab ────────────────────────────────────────────────────────────── + +class _ActivityTab extends StatelessWidget { + const _ActivityTab({required this.metrics}); + final ReportMetrics metrics; + + @override + Widget build(BuildContext context) { + final items = metrics.recentActivity; + if (items.isEmpty) { + return const Center( + child: Text('Henüz aktivite kaydı yok.', style: TextStyle(color: AppColors.textMuted)), + ); + } + return _TabBody(children: [ + const _SectionHeader('Son İşlemler'), + _Card( + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), + child: Column( + children: items.asMap().entries.map((entry) { + final i = entry.key; + final item = entry.value; + return _ActivityRow(item: item, isLast: i == items.length - 1); + }).toList(), + ), + ), + ]); + } +} + +class _ActivityRow extends StatelessWidget { + const _ActivityRow({required this.item, required this.isLast}); + final ActivityItem item; + final bool isLast; + + @override + Widget build(BuildContext context) { + final color = item.isNegative ? AppColors.cancelled : item.isPositive ? AppColors.success : AppColors.accent; + final icon = switch (item.action) { + 'accepted' => Icons.check_circle_outline_rounded, + 'handed_to_clinic' => Icons.send_rounded, + 'approved' => Icons.thumb_up_outlined, + 'revision_requested' => Icons.loop_rounded, + 'delivered' => Icons.local_shipping_outlined, + 'cancelled' => Icons.cancel_outlined, + _ => Icons.history_rounded, + }; + final df = DateFormat('dd.MM.yy HH:mm'); + + return IntrinsicHeight( + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Timeline line + dot + SizedBox( + width: 28, + child: Column( + children: [ + Container( + width: 24, height: 24, + decoration: BoxDecoration( + color: color.withValues(alpha: 0.12), + shape: BoxShape.circle, + ), + child: Icon(icon, size: 13, color: color), + ), + if (!isLast) + Expanded(child: Container(width: 1.5, color: AppColors.border)), + ], + ), + ), + const SizedBox(width: 10), + Expanded( + child: Padding( + padding: EdgeInsets.only(bottom: isLast ? 0 : 10), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(item.actionLabel, + style: const TextStyle(fontSize: 13, fontWeight: FontWeight.w600, color: AppColors.textPrimary)), + if (item.patientCode != null && item.patientCode!.isNotEmpty) + Text(item.patientCode!, style: const TextStyle(fontSize: 11, color: AppColors.accent)), + if (item.note != null && item.note!.isNotEmpty) + Text(item.note!, style: const TextStyle(fontSize: 11, color: AppColors.textMuted)), + const SizedBox(height: 2), + Text(df.format(item.createdAt), style: const TextStyle(fontSize: 10, color: AppColors.textMuted)), + ], + ), + ), + ), + ], + ), + ); + } +} + +// ── Counterpart Tab ─────────────────────────────────────────────────────────── + +class _CounterpartTab extends StatelessWidget { + const _CounterpartTab({required this.metrics, required this.label}); + final ReportMetrics metrics; + final String label; + + @override + Widget build(BuildContext context) { + final stats = metrics.counterpartStats; + if (stats.isEmpty) { + return Center( + child: Text('$label henüz yok.', style: const TextStyle(color: AppColors.textMuted)), + ); + } + final fmt = NumberFormat.currency(locale: 'tr_TR', symbol: metrics.currency, decimalDigits: 0); + final maxJobs = stats.fold(0, (m, s) => s.jobCount > m ? s.jobCount : m); + + return _TabBody(children: [ + const _SectionHeader('Protez Türü Dağılımı'), + _Card( + child: Column( + children: _buildTypeRows(metrics.byProstheticType), + ), + ), + _SectionHeader('En Aktif $label'), + _Card( + child: Column( + children: stats.asMap().entries.map((entry) { + final i = entry.key; + final s = entry.value; + return Padding( + padding: EdgeInsets.only(bottom: i < stats.length - 1 ? 12 : 0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Container( + width: 22, height: 22, + decoration: BoxDecoration( + color: AppColors.inProgressBg, + shape: BoxShape.circle, + ), + child: Center( + child: Text('${i + 1}', + style: const TextStyle(fontSize: 11, fontWeight: FontWeight.w700, color: AppColors.inProgress)), + ), + ), + const SizedBox(width: 8), + Expanded( + child: Text(s.name, + style: const TextStyle(fontSize: 13, fontWeight: FontWeight.w600, color: AppColors.textPrimary), + maxLines: 1, overflow: TextOverflow.ellipsis), + ), + Text('${s.jobCount} iş', + style: const TextStyle(fontSize: 12, color: AppColors.textSecondary, fontWeight: FontWeight.w500)), + ], + ), + const SizedBox(height: 6), + ClipRRect( + borderRadius: BorderRadius.circular(4), + child: LinearProgressIndicator( + value: maxJobs > 0 ? s.jobCount / maxJobs : 0, + minHeight: 6, + backgroundColor: AppColors.surfaceVariant, + valueColor: const AlwaysStoppedAnimation(AppColors.accent), + ), + ), + if (s.totalRevenue > 0) Padding( + padding: const EdgeInsets.only(top: 4), + child: Text( + '${fmt.format(s.paidRevenue)} tahsil · ${fmt.format(s.pendingRevenue)} bekliyor', + style: const TextStyle(fontSize: 11, color: AppColors.textMuted), + ), + ), + ], + ), + ); + }).toList(), + ), + ), + ]); + } + + List _buildTypeRows(Map byType) { + if (byType.isEmpty) return [const Text('Veri yok', style: TextStyle(color: AppColors.textMuted))]; + final sorted = byType.entries.toList()..sort((a, b) => b.value.compareTo(a.value)); + final max = sorted.first.value; + return sorted.map((e) => _HBarRow( + label: _typeLabel(e.key), + value: e.value, + max: max, + color: AppColors.accent, + )).toList(); + } + + static String _typeLabel(String s) => switch (s) { + 'metal_porselen' => 'Metal Porselen', + 'zirkonyum' => 'Zirkonyum', + 'implant_ustu_zirkonyum'=> 'İmplant Üstü', + 'gecici' => 'Geçici', + 'e_max' => 'E-Max', + 'tam_protez' => 'Tam Protez', + 'parsiyel' => 'Parsiyel', + _ => 'Diğer', + }; +} + +// ── Chart widgets ───────────────────────────────────────────────────────────── + +class _HBarRow extends StatelessWidget { + const _HBarRow({required this.label, required this.value, required this.max, required this.color}); + final String label; + final int value, max; + final Color color; + + @override + Widget build(BuildContext context) { + final fraction = max > 0 ? value / max : 0.0; + return Padding( + padding: const EdgeInsets.symmetric(vertical: 5), + child: Row( + children: [ + SizedBox( + width: 100, + child: Text(label, + style: const TextStyle(fontSize: 12, color: AppColors.textSecondary), + maxLines: 1, overflow: TextOverflow.ellipsis), + ), + Expanded( + child: ClipRRect( + borderRadius: BorderRadius.circular(4), + child: Stack( + children: [ + Container(height: 22, color: AppColors.surfaceVariant), + FractionallySizedBox( + widthFactor: fraction, + child: Container( + height: 22, + decoration: BoxDecoration( + color: color.withValues(alpha: 0.8), + borderRadius: BorderRadius.circular(4), + ), + ), + ), + ], + ), + ), + ), + const SizedBox(width: 8), + SizedBox( + width: 28, + child: Text('$value', + style: const TextStyle(fontSize: 12, fontWeight: FontWeight.w700, color: AppColors.textPrimary), + textAlign: TextAlign.right), + ), + ], + ), + ); + } +} + +class _VBarChart extends StatelessWidget { + const _VBarChart({required this.data, required this.color, this.formatValue}); + final List data; + final Color color; + final String Function(int)? formatValue; + + @override + Widget build(BuildContext context) { + if (data.isEmpty) return const SizedBox.shrink(); + final maxVal = data.fold(0, (m, e) => e.count > m ? e.count : m); + final fmt = formatValue ?? (v) => '$v'; + + return SizedBox( + height: 140, + child: Row( + crossAxisAlignment: CrossAxisAlignment.end, + children: data.map((d) { + final fraction = maxVal > 0 ? d.count / maxVal : 0.0; + return Expanded( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 2), + child: Column( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + if (d.count > 0) + Text(fmt(d.count), + style: const TextStyle(fontSize: 9, color: AppColors.textMuted), + textAlign: TextAlign.center), + const SizedBox(height: 2), + AnimatedContainer( + duration: const Duration(milliseconds: 600), + curve: Curves.easeOut, + height: (fraction * 90).clamp(2, 90), + decoration: BoxDecoration( + color: color, + borderRadius: const BorderRadius.vertical(top: Radius.circular(4)), + ), + ), + const SizedBox(height: 4), + Text(d.label, + style: const TextStyle(fontSize: 10, color: AppColors.textMuted), + textAlign: TextAlign.center), + ], + ), + ), + ); + }).toList(), + ), + ); + } +} diff --git a/lib/features/shared/tenant_team_repository.dart b/lib/features/shared/tenant_team_repository.dart new file mode 100644 index 0000000..77d84cc --- /dev/null +++ b/lib/features/shared/tenant_team_repository.dart @@ -0,0 +1,82 @@ +import 'package:pocketbase/pocketbase.dart'; +import '../../core/api/pocketbase_client.dart'; +import '../../models/tenant.dart'; +import '../../models/user_profile.dart'; + +class TeamMember { + const TeamMember({ + required this.memberId, + required this.user, + required this.role, + required this.joinedAt, + }); + final String memberId; + final UserProfile user; + final TenantRole role; + final DateTime joinedAt; +} + +class TenantTeamRepository { + TenantTeamRepository._(); + static final instance = TenantTeamRepository._(); + + PocketBase get _pb => PocketBaseClient.instance.pb; + + Future> listMembers(String tenantId) async { + final result = await _pb.collection('tenant_members').getList( + filter: 'tenant_id = "$tenantId"', + expand: 'user_id', + perPage: 200, + ); + return (result.items.map((r) { + final j = r.toJson(); + final userExp = (j['expand'] as Map?)?['user_id'] as Map?; + return TeamMember( + memberId: j['id'] as String, + user: UserProfile.fromJson(userExp ?? {'id': j['user_id'], 'email': ''}), + role: TenantMembership.parseRole(j['role'] as String), + joinedAt: DateTime.parse(j['created'] as String), + ); + }).toList()..sort((a, b) => a.joinedAt.compareTo(b.joinedAt))); + } + + Future addMember({ + required String tenantId, + required String email, + required String password, + required String firstName, + required String lastName, + required TenantRole role, + }) async { + final userRecord = await _pb.collection('users').create(body: { + 'email': email.trim().toLowerCase(), + 'password': password, + 'passwordConfirm': password, + 'first_name': firstName.trim(), + 'last_name': lastName.trim(), + 'emailVisibility': true, + }); + final memberRecord = await _pb.collection('tenant_members').create(body: { + 'tenant_id': tenantId, + 'user_id': userRecord.id, + 'role': role.value, + }); + final j = memberRecord.toJson(); + return TeamMember( + memberId: j['id'] as String, + user: UserProfile.fromJson(userRecord.toJson()), + role: role, + joinedAt: DateTime.parse(j['created'] as String), + ); + } + + Future changeMemberRole(String memberId, TenantRole newRole) async { + await _pb.collection('tenant_members').update(memberId, body: { + 'role': newRole.value, + }); + } + + Future removeMember(String memberId) async { + await _pb.collection('tenant_members').delete(memberId); + } +} diff --git a/lib/features/shared/tenant_team_screen.dart b/lib/features/shared/tenant_team_screen.dart new file mode 100644 index 0000000..6470f10 --- /dev/null +++ b/lib/features/shared/tenant_team_screen.dart @@ -0,0 +1,742 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +import '../../core/providers/auth_provider.dart'; +import '../../core/theme/app_theme.dart'; +import '../../models/tenant.dart'; +import 'tenant_team_repository.dart'; + +class TenantTeamScreen extends ConsumerStatefulWidget { + const TenantTeamScreen({super.key}); + + @override + ConsumerState createState() => _TenantTeamScreenState(); +} + +class _TenantTeamScreenState extends ConsumerState { + List _members = []; + bool _loading = true; + String? _error; + + @override + void initState() { + super.initState(); + _load(); + } + + Future _load() async { + setState(() { + _loading = true; + _error = null; + }); + try { + final tenantId = ref.read(authProvider).activeTenant!.tenant.id; + final members = await TenantTeamRepository.instance.listMembers(tenantId); + if (mounted) { + setState(() { + _members = members; + _loading = false; + }); + } + } catch (e) { + if (mounted) { + setState(() { + _error = e.toString(); + _loading = false; + }); + } + } + } + + bool get _canManage => + ref.read(authProvider).activeTenant?.canManageUsers ?? false; + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Ekip'), + actions: [ + if (_canManage) + TextButton.icon( + onPressed: () => _showAddMemberSheet(context), + icon: const Icon(Icons.person_add_outlined, size: 18), + label: const Text('Üye Ekle'), + ), + ], + ), + body: _loading + ? const Center(child: CircularProgressIndicator()) + : _error != null + ? _ErrorView(error: _error!, onRetry: _load) + : RefreshIndicator( + onRefresh: _load, + child: ListView( + padding: const EdgeInsets.all(16), + children: [ + _SectionHeader( + title: 'Üyeler', + count: _members.length, + ), + const SizedBox(height: 8), + _MembersList( + members: _members, + canManage: _canManage, + currentUserId: + ref.read(authProvider).profile?.id ?? '', + onRoleChange: _changeRole, + onRemove: _removeMember, + ), + const SizedBox(height: 24), + ], + ), + ), + ); + } + + Future _showAddMemberSheet(BuildContext context) async { + final tenantId = ref.read(authProvider).activeTenant!.tenant.id; + await showModalBottomSheet( + context: context, + isScrollControlled: true, + backgroundColor: AppColors.background, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.vertical(top: Radius.circular(20)), + ), + builder: (_) => _AddMemberSheet( + onAdd: (firstName, lastName, email, password, role) async { + await TenantTeamRepository.instance.addMember( + tenantId: tenantId, + email: email, + password: password, + firstName: firstName, + lastName: lastName, + role: role, + ); + await _load(); + }, + ), + ); + } + + Future _changeRole(TeamMember member, TenantRole newRole) async { + try { + await TenantTeamRepository.instance.changeMemberRole( + member.memberId, newRole); + await _load(); + } catch (e) { + if (mounted) { + ScaffoldMessenger.of(context) + .showSnackBar(SnackBar(content: Text('Hata: $e'))); + } + } + } + + Future _removeMember(TeamMember member) async { + final name = member.user.displayName.isNotEmpty + ? member.user.displayName + : member.user.email; + final confirmed = await showDialog( + context: context, + builder: (_) => AlertDialog( + title: const Text('Üyeyi Çıkar'), + content: Text('$name adlı üyeyi ekipten çıkarmak istiyor musunuz?'), + actions: [ + TextButton( + onPressed: () => Navigator.pop(context, false), + child: const Text('Vazgeç')), + FilledButton( + onPressed: () => Navigator.pop(context, true), + style: FilledButton.styleFrom(backgroundColor: AppColors.cancelled), + child: const Text('Çıkar'), + ), + ], + ), + ); + if (confirmed != true) return; + try { + await TenantTeamRepository.instance.removeMember(member.memberId); + await _load(); + } catch (e) { + if (mounted) { + ScaffoldMessenger.of(context) + .showSnackBar(SnackBar(content: Text('Hata: $e'))); + } + } + } +} + +// ── Section header ───────────────────────────────────────────────────────── + +class _SectionHeader extends StatelessWidget { + const _SectionHeader({required this.title, required this.count}); + final String title; + final int count; + + @override + Widget build(BuildContext context) { + return Row( + children: [ + Text( + title, + style: const TextStyle( + fontSize: 13, + fontWeight: FontWeight.w700, + color: AppColors.textSecondary, + letterSpacing: 0.5, + ), + ), + const SizedBox(width: 8), + Container( + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2), + decoration: BoxDecoration( + color: AppColors.inProgressBg, + borderRadius: BorderRadius.circular(20), + ), + child: Text( + '$count', + style: const TextStyle( + fontSize: 12, + fontWeight: FontWeight.w700, + color: AppColors.inProgress, + ), + ), + ), + ], + ); + } +} + +// ── Members list ─────────────────────────────────────────────────────────── + +class _MembersList extends StatelessWidget { + const _MembersList({ + required this.members, + required this.canManage, + required this.currentUserId, + required this.onRoleChange, + required this.onRemove, + }); + + final List members; + final bool canManage; + final String currentUserId; + final Future Function(TeamMember, TenantRole) onRoleChange; + final Future Function(TeamMember) onRemove; + + @override + Widget build(BuildContext context) { + if (members.isEmpty) { + return const _EmptyCard(message: 'Henüz üye yok.'); + } + return Container( + decoration: BoxDecoration( + color: AppColors.surface, + borderRadius: BorderRadius.circular(16), + border: Border.all(color: AppColors.border), + boxShadow: [ + BoxShadow( + color: Colors.black.withValues(alpha: 0.05), + blurRadius: 12, + offset: const Offset(0, 4)) + ], + ), + child: Column( + children: members.asMap().entries.map((entry) { + final i = entry.key; + final m = entry.value; + final isLast = i == members.length - 1; + return _MemberTile( + member: m, + isSelf: m.user.id == currentUserId, + canManage: canManage && m.role != TenantRole.owner, + showDivider: !isLast, + onRoleChange: (role) => onRoleChange(m, role), + onRemove: () => onRemove(m), + ); + }).toList(), + ), + ); + } +} + +class _MemberTile extends StatelessWidget { + const _MemberTile({ + required this.member, + required this.isSelf, + required this.canManage, + required this.showDivider, + required this.onRoleChange, + required this.onRemove, + }); + + final TeamMember member; + final bool isSelf; + final bool canManage; + final bool showDivider; + final void Function(TenantRole) onRoleChange; + final VoidCallback onRemove; + + @override + Widget build(BuildContext context) { + final name = member.user.displayName.isNotEmpty + ? member.user.displayName + : member.user.email; + final initials = name.trim().isNotEmpty + ? name.trim()[0].toUpperCase() + : '?'; + + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), + child: Row( + children: [ + Container( + width: 40, + height: 40, + decoration: BoxDecoration( + color: AppColors.inProgressBg, + borderRadius: BorderRadius.circular(20), + ), + child: Center( + child: Text( + initials, + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.w700, + color: AppColors.inProgress, + ), + ), + ), + ), + const SizedBox(width: 12), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Text( + name, + style: const TextStyle( + fontWeight: FontWeight.w600, + color: AppColors.textPrimary, + ), + ), + if (isSelf) ...[ + const SizedBox(width: 6), + Container( + padding: const EdgeInsets.symmetric( + horizontal: 6, vertical: 1), + decoration: BoxDecoration( + color: AppColors.successBg, + borderRadius: BorderRadius.circular(6), + ), + child: const Text( + 'Sen', + style: TextStyle( + fontSize: 10, + fontWeight: FontWeight.w700, + color: AppColors.success, + ), + ), + ), + ], + ], + ), + Text( + member.user.email, + style: const TextStyle( + fontSize: 12, + color: AppColors.textSecondary, + ), + ), + ], + ), + ), + if (canManage) ...[ + _RoleChip( + role: member.role, + onChanged: onRoleChange, + ), + const SizedBox(width: 4), + IconButton( + onPressed: onRemove, + icon: const Icon(Icons.remove_circle_outline, + color: AppColors.cancelled, size: 20), + tooltip: 'Çıkar', + constraints: const BoxConstraints(), + padding: const EdgeInsets.all(6), + ), + ] else + _RoleBadge(role: member.role), + ], + ), + ), + if (showDivider) + const Divider(height: 1, indent: 68, color: AppColors.border), + ], + ); + } +} + +class _RoleChip extends StatelessWidget { + const _RoleChip({required this.role, required this.onChanged}); + final TenantRole role; + final void Function(TenantRole) onChanged; + + static const _selectableRoles = [ + TenantRole.admin, + TenantRole.technician, + TenantRole.delivery, + TenantRole.finance, + TenantRole.doctor, + TenantRole.member, + ]; + + @override + Widget build(BuildContext context) { + return PopupMenuButton( + initialValue: role, + onSelected: onChanged, + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), + itemBuilder: (_) => _selectableRoles + .map((r) => PopupMenuItem( + value: r, + child: Text(r.label), + )) + .toList(), + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5), + decoration: BoxDecoration( + color: _roleBg(role), + borderRadius: BorderRadius.circular(8), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + role.label, + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w600, + color: _roleColor(role), + ), + ), + const SizedBox(width: 4), + Icon(Icons.arrow_drop_down, size: 16, color: _roleColor(role)), + ], + ), + ), + ); + } +} + +class _RoleBadge extends StatelessWidget { + const _RoleBadge({required this.role}); + final TenantRole role; + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5), + decoration: BoxDecoration( + color: _roleBg(role), + borderRadius: BorderRadius.circular(8), + ), + child: Text( + role.label, + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w600, + color: _roleColor(role), + ), + ), + ); + } +} + +Color _roleBg(TenantRole r) => switch (r) { + TenantRole.owner => AppColors.inProgressBg, + TenantRole.admin => AppColors.inProgressBg, + TenantRole.doctor => AppColors.successBg, + _ => AppColors.surface, + }; + +Color _roleColor(TenantRole r) => switch (r) { + TenantRole.owner => AppColors.inProgress, + TenantRole.admin => AppColors.inProgress, + TenantRole.doctor => AppColors.success, + _ => AppColors.textSecondary, + }; + +// ── Add member sheet ──────────────────────────────────────────────────────── + +class _AddMemberSheet extends StatefulWidget { + const _AddMemberSheet({required this.onAdd}); + final Future Function( + String firstName, + String lastName, + String email, + String password, + TenantRole role, + ) onAdd; + + @override + State<_AddMemberSheet> createState() => _AddMemberSheetState(); +} + +class _AddMemberSheetState extends State<_AddMemberSheet> { + final _formKey = GlobalKey(); + final _firstNameController = TextEditingController(); + final _lastNameController = TextEditingController(); + final _emailController = TextEditingController(); + final _passwordController = TextEditingController(); + TenantRole _selectedRole = TenantRole.member; + bool _saving = false; + bool _obscurePassword = true; + + static const _selectableRoles = [ + TenantRole.admin, + TenantRole.technician, + TenantRole.delivery, + TenantRole.finance, + TenantRole.doctor, + TenantRole.member, + ]; + + @override + void dispose() { + _firstNameController.dispose(); + _lastNameController.dispose(); + _emailController.dispose(); + _passwordController.dispose(); + super.dispose(); + } + + Future _submit() async { + if (!_formKey.currentState!.validate()) return; + setState(() => _saving = true); + try { + await widget.onAdd( + _firstNameController.text.trim(), + _lastNameController.text.trim(), + _emailController.text.trim(), + _passwordController.text, + _selectedRole, + ); + if (mounted) Navigator.pop(context); + } catch (e) { + if (mounted) { + final msg = _friendlyError(e); + ScaffoldMessenger.of(context) + .showSnackBar(SnackBar(content: Text(msg))); + } + } finally { + if (mounted) setState(() => _saving = false); + } + } + + static String _friendlyError(Object e) { + final s = e.toString(); + if (s.contains('email') && s.contains('unique')) { + return 'Bu e-posta adresi zaten kayıtlı.'; + } + final msgMatch = RegExp(r'message: ([^,}]+)').firstMatch(s); + if (msgMatch != null) return msgMatch.group(1)!.trim(); + if (s.length > 120) return 'Sunucu hatası'; + return s; + } + + @override + Widget build(BuildContext context) { + final bottom = MediaQuery.of(context).viewInsets.bottom; + return Padding( + padding: EdgeInsets.fromLTRB(24, 24, 24, 24 + bottom), + child: Form( + key: _formKey, + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + const Text( + 'Üye Ekle', + style: TextStyle( + fontSize: 20, + fontWeight: FontWeight.w700, + color: AppColors.textPrimary, + ), + ), + const Spacer(), + IconButton( + onPressed: () => Navigator.pop(context), + icon: const Icon(Icons.close), + ), + ], + ), + const SizedBox(height: 20), + Row( + children: [ + Expanded( + child: TextFormField( + controller: _firstNameController, + textCapitalization: TextCapitalization.words, + textInputAction: TextInputAction.next, + decoration: const InputDecoration( + labelText: 'İsim', + prefixIcon: Icon(Icons.person_outline), + ), + validator: (val) { + if (val == null || val.trim().isEmpty) return 'Zorunlu'; + return null; + }, + ), + ), + const SizedBox(width: 12), + Expanded( + child: TextFormField( + controller: _lastNameController, + textCapitalization: TextCapitalization.words, + textInputAction: TextInputAction.next, + decoration: const InputDecoration( + labelText: 'Soyisim', + ), + validator: (val) { + if (val == null || val.trim().isEmpty) return 'Zorunlu'; + return null; + }, + ), + ), + ], + ), + const SizedBox(height: 16), + TextFormField( + controller: _emailController, + keyboardType: TextInputType.emailAddress, + textInputAction: TextInputAction.next, + autocorrect: false, + decoration: const InputDecoration( + labelText: 'E-posta', + hintText: 'ornek@email.com', + prefixIcon: Icon(Icons.email_outlined), + ), + validator: (val) { + if (val == null || val.trim().isEmpty) return 'E-posta zorunludur'; + if (!val.contains('@')) return 'Geçerli bir e-posta girin'; + return null; + }, + ), + const SizedBox(height: 16), + TextFormField( + controller: _passwordController, + obscureText: _obscurePassword, + textInputAction: TextInputAction.done, + decoration: InputDecoration( + labelText: 'Şifre', + prefixIcon: const Icon(Icons.lock_outline), + suffixIcon: IconButton( + icon: Icon(_obscurePassword + ? Icons.visibility_outlined + : Icons.visibility_off_outlined), + onPressed: () => + setState(() => _obscurePassword = !_obscurePassword), + ), + ), + validator: (val) { + if (val == null || val.isEmpty) return 'Şifre zorunludur'; + if (val.length < 8) return 'En az 8 karakter olmalı'; + return null; + }, + ), + const SizedBox(height: 16), + DropdownButtonFormField( + value: _selectedRole, + decoration: const InputDecoration( + labelText: 'Rol', + prefixIcon: Icon(Icons.badge_outlined), + ), + items: _selectableRoles + .map((r) => DropdownMenuItem( + value: r, + child: Text(r.label), + )) + .toList(), + onChanged: (v) { + if (v != null) setState(() => _selectedRole = v); + }, + ), + const SizedBox(height: 24), + SizedBox( + width: double.infinity, + child: FilledButton.icon( + onPressed: _saving ? null : _submit, + icon: _saving + ? const SizedBox( + width: 16, + height: 16, + child: CircularProgressIndicator( + strokeWidth: 2, color: Colors.white), + ) + : const Icon(Icons.person_add_outlined, size: 18), + label: const Text('Üye Ekle'), + ), + ), + ], + ), + ), + ); + } +} + +// ── Helpers ──────────────────────────────────────────────────────────────── + +class _EmptyCard extends StatelessWidget { + const _EmptyCard({required this.message}); + final String message; + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.all(24), + decoration: BoxDecoration( + color: AppColors.surface, + borderRadius: BorderRadius.circular(16), + border: Border.all(color: AppColors.border), + ), + child: Center( + child: Text( + message, + style: const TextStyle(color: AppColors.textSecondary), + ), + ), + ); + } +} + +class _ErrorView extends StatelessWidget { + const _ErrorView({required this.error, required this.onRetry}); + final String error; + final VoidCallback onRetry; + + @override + Widget build(BuildContext context) { + return Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const Icon(Icons.error_outline, color: AppColors.cancelled, size: 40), + const SizedBox(height: 12), + Text(error, + style: const TextStyle(color: AppColors.textSecondary), + textAlign: TextAlign.center), + const SizedBox(height: 12), + TextButton(onPressed: onRetry, child: const Text('Tekrar Dene')), + ], + ), + ); + } +} diff --git a/lib/main.dart b/lib/main.dart new file mode 100644 index 0000000..0922b72 --- /dev/null +++ b/lib/main.dart @@ -0,0 +1,131 @@ +import 'package:flutter_localizations/flutter_localizations.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:shadcn_flutter/shadcn_flutter.dart'; +import 'core/api/pocketbase_client.dart'; +import 'core/providers/locale_provider.dart'; +import 'core/router/router_provider.dart'; +import 'core/services/notification_service.dart'; + +void main() async { + WidgetsFlutterBinding.ensureInitialized(); + await PocketBaseClient.init(); + await NotificationService.init(); + + final initialLocale = await LocaleNotifier.load(); + + runApp( + ProviderScope( + overrides: [ + localeProvider.overrideWith( + (ref) => LocaleNotifier(initialLocale), + ), + ], + child: const DlsApp(), + ), + ); +} + +class DlsApp extends ConsumerWidget { + const DlsApp({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final router = ref.watch(routerProvider); + final locale = ref.watch(localeProvider); + NotificationService.setRouter(router); + return ShadcnApp.router( + title: 'DLS', + debugShowCheckedModeBanner: false, + locale: locale, + supportedLocales: supportedLocales, + localizationsDelegates: const [ + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + ], + theme: const ThemeData( + colorScheme: _dlsLight, + radius: 0.5, + ), + darkTheme: const ThemeData.dark( + colorScheme: _dlsDark, + radius: 0.5, + ), + routerConfig: router, + ); + } +} + +// ── DLS Light color scheme ──────────────────────────────────────────────────── + +const _dlsLight = ColorScheme( + brightness: Brightness.light, + background: Color(0xFFF1F5F9), + foreground: Color(0xFF0F172A), + card: Color(0xFFFFFFFF), + cardForeground: Color(0xFF0F172A), + popover: Color(0xFFFFFFFF), + popoverForeground: Color(0xFF0F172A), + primary: Color(0xFF1E3A5F), + primaryForeground: Color(0xFFFFFFFF), + secondary: Color(0xFFE2E8F0), + secondaryForeground: Color(0xFF0F172A), + muted: Color(0xFFE2E8F0), + mutedForeground: Color(0xFF64748B), + accent: Color(0xFFF8FAFC), + accentForeground: Color(0xFF0F172A), + destructive: Color(0xFFDC2626), + border: Color(0xFFE2E8F0), + input: Color(0xFFE2E8F0), + ring: Color(0xFF0369A1), + chart1: Color(0xFF1E3A5F), + chart2: Color(0xFF059669), + chart3: Color(0xFFF59E0B), + chart4: Color(0xFF0369A1), + chart5: Color(0xFFDC2626), + sidebar: Color(0xFFFFFFFF), + sidebarForeground: Color(0xFF0F172A), + sidebarPrimary: Color(0xFF1E3A5F), + sidebarPrimaryForeground: Color(0xFFFFFFFF), + sidebarAccent: Color(0xFFF1F5F9), + sidebarAccentForeground: Color(0xFF0F172A), + sidebarBorder: Color(0xFFE2E8F0), + sidebarRing: Color(0xFF0369A1), +); + +// ── DLS Dark color scheme ───────────────────────────────────────────────────── + +const _dlsDark = ColorScheme( + brightness: Brightness.dark, + background: Color(0xFF0F172A), + foreground: Color(0xFFF1F5F9), + card: Color(0xFF1E293B), + cardForeground: Color(0xFFF1F5F9), + popover: Color(0xFF1E293B), + popoverForeground: Color(0xFFF1F5F9), + primary: Color(0xFF93C5FD), + primaryForeground: Color(0xFF1E3A5F), + secondary: Color(0xFF273344), + secondaryForeground: Color(0xFFF1F5F9), + muted: Color(0xFF273344), + mutedForeground: Color(0xFF94A3B8), + accent: Color(0xFF273344), + accentForeground: Color(0xFFF1F5F9), + destructive: Color(0xFFFCA5A5), + border: Color(0xFF334155), + input: Color(0xFF334155), + ring: Color(0xFF7DD3FC), + chart1: Color(0xFF93C5FD), + chart2: Color(0xFF6EE7B7), + chart3: Color(0xFFFCD34D), + chart4: Color(0xFF7DD3FC), + chart5: Color(0xFFFCA5A5), + sidebar: Color(0xFF1E293B), + sidebarForeground: Color(0xFFF1F5F9), + sidebarPrimary: Color(0xFF93C5FD), + sidebarPrimaryForeground: Color(0xFF1E3A5F), + sidebarAccent: Color(0xFF273344), + sidebarAccentForeground: Color(0xFFF1F5F9), + sidebarBorder: Color(0xFF334155), + sidebarRing: Color(0xFF7DD3FC), +); diff --git a/lib/models/clinic_discount.dart b/lib/models/clinic_discount.dart new file mode 100644 index 0000000..14bc9f3 --- /dev/null +++ b/lib/models/clinic_discount.dart @@ -0,0 +1,68 @@ +import 'job.dart'; + +enum DiscountType { percentage, fixed } + +extension DiscountTypeX on DiscountType { + String get value => name; + String get label => this == DiscountType.percentage ? 'Yüzde (%)' : 'Sabit Tutar (TL)'; +} + +class ClinicDiscount { + const ClinicDiscount({ + required this.id, + required this.labTenantId, + this.clinicTenantId, + this.clinicName, + this.prostheticType, + required this.discountType, + required this.discountValue, + this.minQuantity = 0, + required this.isActive, + this.notes, + }); + + final String id; + final String labTenantId; + final String? clinicTenantId; // null = tüm klinikler + final String? clinicName; + final String? prostheticType; // null = tüm ürün tipleri + final DiscountType discountType; + final double discountValue; + final int minQuantity; // 0 = minimum yok + final bool isActive; + final String? notes; + + bool get appliesToAll => clinicTenantId == null || clinicTenantId!.isEmpty; + bool get appliesToAllTypes => prostheticType == null || prostheticType!.isEmpty; + + String get displayValue => discountType == DiscountType.percentage + ? '%${discountValue.toStringAsFixed(discountValue % 1 == 0 ? 0 : 1)}' + : '${discountValue.toStringAsFixed(2)} TL'; + + String get prostheticLabel { + if (appliesToAllTypes) return 'Tüm Türler'; + return ProstheticType.values + .firstWhere((e) => e.value == prostheticType, + orElse: () => ProstheticType.diger) + .label; + } + + factory ClinicDiscount.fromJson(Map j) { + final expand = j['expand'] as Map?; + final clinicExp = expand?['clinic_tenant_id'] as Map?; + final dt = j['discount_type'] as String? ?? 'percentage'; + final pt = j['prosthetic_type'] as String?; + return ClinicDiscount( + id: j['id'] as String, + labTenantId: j['lab_tenant_id'] as String, + clinicTenantId: j['clinic_tenant_id'] as String?, + clinicName: clinicExp?['company_name'] as String?, + prostheticType: (pt == null || pt.isEmpty) ? null : pt, + discountType: dt == 'fixed' ? DiscountType.fixed : DiscountType.percentage, + discountValue: (j['discount_value'] as num?)?.toDouble() ?? 0, + minQuantity: (j['min_quantity'] as num?)?.toInt() ?? 0, + isActive: j['is_active'] as bool? ?? true, + notes: j['notes'] as String?, + ); + } +} diff --git a/lib/models/connection.dart b/lib/models/connection.dart new file mode 100644 index 0000000..7b119ff --- /dev/null +++ b/lib/models/connection.dart @@ -0,0 +1,53 @@ +enum ConnectionStatus { pending, approved, rejected } + +extension ConnectionStatusX on ConnectionStatus { + String get value => name; + String get label { + switch (this) { + case ConnectionStatus.pending: + return 'Bekliyor'; + case ConnectionStatus.approved: + return 'Onaylı'; + case ConnectionStatus.rejected: + return 'Reddedildi'; + } + } +} + +class Connection { + const Connection({ + required this.id, + required this.clinicTenantId, + required this.labTenantId, + required this.status, + this.clinicName, + this.labName, + this.dateCreated, + }); + + final String id; + final String clinicTenantId; + final String labTenantId; + final ConnectionStatus status; + final String? clinicName; + final String? labName; + final String? dateCreated; + + factory Connection.fromJson(Map j) { + final expand = j['expand'] as Map?; + final clinicExp = expand?['clinic_tenant_id'] as Map?; + final labExp = expand?['lab_tenant_id'] as Map?; + return Connection( + id: j['id'] as String, + clinicTenantId: j['clinic_tenant_id'] as String, + labTenantId: j['lab_tenant_id'] as String, + status: ConnectionStatus.values.firstWhere( + (e) => e.value == j['status'], + orElse: () => ConnectionStatus.pending, + ), + clinicName: clinicExp?['company_name'] as String?, + labName: labExp?['company_name'] as String?, + dateCreated: j['created'] as String?, + ); + } +} diff --git a/lib/models/finance_entry.dart b/lib/models/finance_entry.dart new file mode 100644 index 0000000..5b79e4e --- /dev/null +++ b/lib/models/finance_entry.dart @@ -0,0 +1,62 @@ +enum FinanceType { receivable, payable } + +extension FinanceTypeX on FinanceType { + String get value => name; + String get label => this == FinanceType.receivable ? 'Alacak' : 'Borç'; +} + +enum FinanceStatus { pending, paid } + +extension FinanceStatusX on FinanceStatus { + String get value => name; + String get label => this == FinanceStatus.pending ? 'Bekliyor' : 'Ödendi'; +} + +class FinanceEntry { + const FinanceEntry({ + required this.id, + required this.tenantId, + required this.jobId, + required this.type, + required this.amount, + required this.currency, + required this.status, + this.paidAt, + this.counterpartyName, + this.patientCode, + this.dateCreated, + }); + + final String id; + final String tenantId; + final String jobId; + final FinanceType type; + final double amount; + final String currency; + final FinanceStatus status; + final String? paidAt; + final String? counterpartyName; + final String? patientCode; + final String? dateCreated; + + factory FinanceEntry.fromJson(Map j) { + final expand = j['expand'] as Map?; + final jobExp = expand?['job_id'] as Map?; + String? _str(dynamic v) { final s = v as String?; return (s == null || s.isEmpty) ? null : s; } + return FinanceEntry( + id: j['id'] as String, + tenantId: j['tenant_id'] as String, + jobId: j['job_id'] as String, + type: FinanceType.values.firstWhere((e) => e.value == j['type'], + orElse: () => FinanceType.receivable), + amount: (j['amount'] as num).toDouble(), + currency: j['currency'] as String? ?? 'TRY', + status: FinanceStatus.values.firstWhere((e) => e.value == j['status'], + orElse: () => FinanceStatus.pending), + paidAt: _str(j['paid_at']), + counterpartyName: _str(j['counterparty_name']), + patientCode: jobExp?['patient_code'] as String?, + dateCreated: j['created'] as String?, + ); + } +} diff --git a/lib/models/job.dart b/lib/models/job.dart new file mode 100644 index 0000000..670c331 --- /dev/null +++ b/lib/models/job.dart @@ -0,0 +1,312 @@ +enum JobStatus { pending, inProgress, sent, delivered, cancelled } + +enum JobStep { + olcu, // legacy fallback + altYapiProva, // sabit seramik/metal — alt yapı (coping) + ustYapiProva, // sabit seramik — bisküvi prova + mumProva, // hareketli protez — mum prova + dislerProva, // hareketli protez — dişler prova + dayanakProva, // implant — dayanak prova + kronProva, // implant — kron prova + cilaBitim, // son cila / bitim (her şablonda son adım) +} + +enum JobLocation { atClinic, atLab } + +enum ProstheticType { + metalPorselen, + zirkonyum, + implantUstuZirkonyum, + gecici, + eMax, + tamProtez, + parsiyel, + diger, +} + +// ── Status ──────────────────────────────────────────────────────────────────── + +extension JobStatusExt on JobStatus { + String get label => switch (this) { + JobStatus.pending => 'Bekliyor', + JobStatus.inProgress => 'İşlemde', + JobStatus.sent => 'Gönderildi', + JobStatus.delivered => 'Teslim Alındı', + JobStatus.cancelled => 'İptal', + }; + String get value => switch (this) { + JobStatus.pending => 'pending', + JobStatus.inProgress => 'in_progress', + JobStatus.sent => 'sent', + JobStatus.delivered => 'delivered', + JobStatus.cancelled => 'cancelled', + }; +} + +// ── Step ────────────────────────────────────────────────────────────────────── + +extension JobStepExt on JobStep { + String get label => switch (this) { + JobStep.olcu => 'Ölçü', + JobStep.altYapiProva => 'Alt Yapı Prova', + JobStep.ustYapiProva => 'Üst Yapı Prova', + JobStep.mumProva => 'Mum Prova', + JobStep.dislerProva => 'Dişler Prova', + JobStep.dayanakProva => 'Dayanak Prova', + JobStep.kronProva => 'Kron Prova', + JobStep.cilaBitim => 'Cila / Bitim', + }; + + /// One-liner shown under the step on the stepper + String get description => switch (this) { + JobStep.olcu => 'İlk ölçü alındı', + JobStep.altYapiProva => 'Metal/zirkonyum coping klinik onayı', + JobStep.ustYapiProva => 'Bisküvi pişirimi sonrası klinik onayı', + JobStep.mumProva => 'Mum prova klinik onayı', + JobStep.dislerProva => 'Diş dizimi klinik onayı', + JobStep.dayanakProva => 'Dayanak klinik onayı', + JobStep.kronProva => 'Kron klinik onayı', + JobStep.cilaBitim => 'Son cila ve teslim hazırlığı', + }; + + String get value => switch (this) { + JobStep.olcu => 'olcu', + JobStep.altYapiProva => 'alt_yapi_prova', + JobStep.ustYapiProva => 'ust_yapi_prova', + JobStep.mumProva => 'mum_prova', + JobStep.dislerProva => 'disler_prova', + JobStep.dayanakProva => 'dayanak_prova', + JobStep.kronProva => 'kron_prova', + JobStep.cilaBitim => 'cila_bitim', + }; +} + +// ── Prosthetic type ─────────────────────────────────────────────────────────── + +extension ProstheticTypeExt on ProstheticType { + String get label => switch (this) { + ProstheticType.metalPorselen => 'Metal Porselen', + ProstheticType.zirkonyum => 'Zirkonyum', + ProstheticType.implantUstuZirkonyum => 'İmplant Üstü Zirkonyum', + ProstheticType.gecici => 'Geçici', + ProstheticType.eMax => 'E-Max', + ProstheticType.tamProtez => 'Tam Protez', + ProstheticType.parsiyel => 'Parsiyel Protez', + ProstheticType.diger => 'Diğer', + }; + String get value => switch (this) { + ProstheticType.metalPorselen => 'metal_porselen', + ProstheticType.zirkonyum => 'zirkonyum', + ProstheticType.implantUstuZirkonyum => 'implant_ustu_zirkonyum', + ProstheticType.gecici => 'gecici', + ProstheticType.eMax => 'e_max', + ProstheticType.tamProtez => 'tam_protez', + ProstheticType.parsiyel => 'parsiyel', + ProstheticType.diger => 'diger', + }; +} + +// ── Step template ───────────────────────────────────────────────────────────── + +/// Returns the ordered step list for a given prosthetic type + prova flag. +List jobStepTemplate(ProstheticType type, bool provaRequired) { + if (!provaRequired) return const [JobStep.cilaBitim]; + return switch (type) { + // Sabit seramik: alt yapı coping + bisküvi prova + cila + ProstheticType.metalPorselen || + ProstheticType.zirkonyum || + ProstheticType.eMax => + const [JobStep.altYapiProva, JobStep.ustYapiProva, JobStep.cilaBitim], + // İmplant: dayanak + kron prova + cila + ProstheticType.implantUstuZirkonyum => + const [JobStep.dayanakProva, JobStep.kronProva, JobStep.cilaBitim], + // Hareketli protez: mum + dişler prova + cila + ProstheticType.tamProtez || + ProstheticType.parsiyel => + const [JobStep.mumProva, JobStep.dislerProva, JobStep.cilaBitim], + // Geçici: sadece cila (prova gereksiz) + ProstheticType.gecici => + const [JobStep.cilaBitim], + // Diğer: tek ara prova + cila + _ => + const [JobStep.altYapiProva, JobStep.cilaBitim], + }; +} + +// ── Job ─────────────────────────────────────────────────────────────────────── + +class Job { + const Job({ + required this.id, + required this.clinicTenantId, + required this.labTenantId, + required this.patientCode, + required this.prostheticType, + required this.memberCount, + required this.status, + required this.dateCreated, + this.patientId, + this.prostheticId, + this.teeth = const [], + this.color, + this.description, + this.price, + this.currency, + this.currentStep, + this.location = JobLocation.atClinic, + this.dueDate, + this.clinicName, + this.labName, + this.attachments = const [], + this.provaRequired = true, + }); + + final String id; + final String clinicTenantId; + final String labTenantId; + final String? patientId; + final String patientCode; + final String? prostheticId; + final ProstheticType prostheticType; + final int memberCount; + final List teeth; + final String? color; + final String? description; + final double? price; + final String? currency; + final JobStatus status; + final JobStep? currentStep; + final JobLocation location; + final DateTime? dueDate; + final DateTime dateCreated; + final List attachments; + final bool provaRequired; + + // Denormalized from relation joins — list views only + final String? clinicName; + final String? labName; + + // ── copyWith ────────────────────────────────────────────────────────────── + + Job copyWith({ + JobStatus? status, + JobStep? currentStep, + JobLocation? location, + String? clinicName, + String? labName, + bool clearCurrentStep = false, + }) => + Job( + id: id, + clinicTenantId: clinicTenantId, + labTenantId: labTenantId, + patientId: patientId, + patientCode: patientCode, + prostheticId: prostheticId, + prostheticType: prostheticType, + memberCount: memberCount, + teeth: teeth, + color: color, + description: description, + price: price, + currency: currency, + status: status ?? this.status, + currentStep: clearCurrentStep ? null : (currentStep ?? this.currentStep), + location: location ?? this.location, + dueDate: dueDate, + dateCreated: dateCreated, + attachments: attachments, + provaRequired: provaRequired, + clinicName: clinicName ?? this.clinicName, + labName: labName ?? this.labName, + ); + + // ── Step helpers ────────────────────────────────────────────────────────── + + List get stepTemplate => jobStepTemplate(prostheticType, provaRequired); + + bool get isLastStep => + currentStep != null && currentStep == stepTemplate.last; + + /// The next step after currentStep in this job's template, or null if done. + JobStep? get nextStep { + if (currentStep == null) return stepTemplate.firstOrNull; + final idx = stepTemplate.indexOf(currentStep!); + if (idx < 0 || idx >= stepTemplate.length - 1) return null; + return stepTemplate[idx + 1]; + } + + factory Job.fromJson(Map j) { + final expand = j['expand'] as Map?; + final clinicExp = expand?['clinic_tenant_id'] as Map?; + final labExp = expand?['lab_tenant_id'] as Map?; + String? str(dynamic v) { + final s = v as String?; + return (s == null || s.isEmpty) ? null : s; + } + + return Job( + id: j['id'] as String, + clinicTenantId: j['clinic_tenant_id'] as String, + labTenantId: j['lab_tenant_id'] as String, + patientId: str(j['patient_id']), + patientCode: j['patient_code'] as String, + prostheticId: str(j['prosthetic_id']), + prostheticType: _parseProstheticType(j['prosthetic_type'] as String), + memberCount: (j['member_count'] as num).toInt(), + teeth: j['teeth'] is List + ? (j['teeth'] as List).map((e) => e.toString()).toList() + : [], + color: str(j['color']), + description: str(j['description']), + price: (j['price'] as num?)?.toDouble(), + currency: str(j['currency']), + status: _parseStatus(j['status'] as String), + currentStep: str(j['current_step']) != null + ? _parseStep(j['current_step'] as String) + : null, + location: + j['location'] == 'at_lab' ? JobLocation.atLab : JobLocation.atClinic, + dueDate: str(j['due_date']) != null + ? DateTime.parse(j['due_date'] as String) + : null, + dateCreated: DateTime.parse(j['created'] as String), + clinicName: clinicExp?['company_name'] as String?, + labName: labExp?['company_name'] as String?, + attachments: j['attachments'] is List + ? (j['attachments'] as List).map((e) => e.toString()).toList() + : [], + provaRequired: (j['prova_required'] as bool?) ?? true, + ); + } + + static JobStatus _parseStatus(String s) => switch (s) { + 'in_progress' => JobStatus.inProgress, + 'sent' => JobStatus.sent, + 'delivered' => JobStatus.delivered, + 'cancelled' => JobStatus.cancelled, + _ => JobStatus.pending, + }; + + static JobStep _parseStep(String s) => switch (s) { + 'alt_yapi_prova' => JobStep.altYapiProva, + 'ust_yapi_prova' => JobStep.ustYapiProva, + 'mum_prova' => JobStep.mumProva, + 'disler_prova' => JobStep.dislerProva, + 'dayanak_prova' => JobStep.dayanakProva, + 'kron_prova' => JobStep.kronProva, + 'cila_bitim' => JobStep.cilaBitim, + _ => JobStep.olcu, + }; + + static ProstheticType _parseProstheticType(String s) => switch (s) { + 'zirkonyum' => ProstheticType.zirkonyum, + 'implant_ustu_zirkonyum'=> ProstheticType.implantUstuZirkonyum, + 'gecici' => ProstheticType.gecici, + 'e_max' => ProstheticType.eMax, + 'tam_protez' => ProstheticType.tamProtez, + 'parsiyel' => ProstheticType.parsiyel, + 'diger' => ProstheticType.diger, + _ => ProstheticType.metalPorselen, + }; +} diff --git a/lib/models/job_file.dart b/lib/models/job_file.dart new file mode 100644 index 0000000..82e3ae7 --- /dev/null +++ b/lib/models/job_file.dart @@ -0,0 +1,84 @@ +enum JobFileKind { scan, image, document } + +extension JobFileKindExt on JobFileKind { + String get label => switch (this) { + JobFileKind.scan => 'Tarama', + JobFileKind.image => 'Görsel', + JobFileKind.document => 'Belge', + }; + String get value => switch (this) { + JobFileKind.scan => 'scan', + JobFileKind.image => 'image', + JobFileKind.document => 'document', + }; + + static JobFileKind fromValue(String s) => switch (s) { + 'image' => JobFileKind.image, + 'document' => JobFileKind.document, + _ => JobFileKind.scan, + }; +} + +class JobFile { + const JobFile({ + required this.id, + required this.jobId, + required this.clinicTenantId, + required this.labTenantId, + required this.uploadedBy, + required this.kind, + required this.fileName, + required this.name, + required this.size, + required this.createdAt, + required this.downloadUrl, + this.mimeType, + }); + + final String id; + final String jobId; + final String clinicTenantId; + final String labTenantId; + final String uploadedBy; + final JobFileKind kind; + final String fileName; + final String name; + final int size; + final String? mimeType; + final DateTime createdAt; + final String downloadUrl; + + String get sizeLabel { + if (size < 1024) return '$size B'; + if (size < 1024 * 1024) return '${(size / 1024).toStringAsFixed(1)} KB'; + return '${(size / (1024 * 1024)).toStringAsFixed(2)} MB'; + } + + factory JobFile.fromJson(Map j, String baseUrl) { + String str(String key, [String fallback = '']) => + (j[key] as String?) ?? fallback; + final id = str('id'); + final collectionId = str('collectionId', 'job_files'); + final fileName = str('file'); + final url = fileName.isNotEmpty + ? '$baseUrl/api/files/$collectionId/$id/$fileName' + : ''; + final createdRaw = str('created'); + return JobFile( + id: id, + jobId: str('job_id'), + clinicTenantId: str('clinic_tenant_id'), + labTenantId: str('lab_tenant_id'), + uploadedBy: str('uploaded_by'), + kind: JobFileKindExt.fromValue(str('kind')), + fileName: fileName, + name: str('name'), + size: (j['size'] as num?)?.toInt() ?? 0, + mimeType: j['mime_type'] as String?, + createdAt: createdRaw.isNotEmpty + ? DateTime.tryParse(createdRaw) ?? DateTime(2000) + : DateTime(2000), + downloadUrl: url, + ); + } +} diff --git a/lib/models/patient.dart b/lib/models/patient.dart new file mode 100644 index 0000000..67aa81b --- /dev/null +++ b/lib/models/patient.dart @@ -0,0 +1,49 @@ +class Patient { + const Patient({ + required this.id, + required this.tenantId, + required this.patientCode, + this.firstName, + this.lastName, + this.birthDate, + this.phone, + this.notes, + }); + + final String id; + final String tenantId; + final String patientCode; + final String? firstName; + final String? lastName; + final String? birthDate; + final String? phone; + final String? notes; + + String get displayName { + final parts = [firstName, lastName].where((s) => s != null && s.isNotEmpty); + return parts.isEmpty ? patientCode : parts.join(' '); + } + + factory Patient.fromJson(Map j) => Patient( + id: j['id'] as String, + tenantId: j['tenant_id'] is Map + ? (j['tenant_id'] as Map)['id'] as String + : j['tenant_id'] as String, + patientCode: j['patient_code'] as String, + firstName: j['first_name'] as String?, + lastName: j['last_name'] as String?, + birthDate: j['birth_date'] as String?, + phone: j['phone'] as String?, + notes: j['notes'] as String?, + ); + + Map toJson() => { + 'tenant_id': tenantId, + 'patient_code': patientCode, + if (firstName != null) 'first_name': firstName, + if (lastName != null) 'last_name': lastName, + if (birthDate != null) 'birth_date': birthDate, + if (phone != null) 'phone': phone, + if (notes != null) 'notes': notes, + }; +} diff --git a/lib/models/prosthetic_product.dart b/lib/models/prosthetic_product.dart new file mode 100644 index 0000000..9e3abc6 --- /dev/null +++ b/lib/models/prosthetic_product.dart @@ -0,0 +1,45 @@ +class ProstheticProduct { + const ProstheticProduct({ + required this.id, + required this.labTenantId, + required this.name, + required this.prostheticType, + this.unitPrice, + this.currency, + this.isActive = true, + this.description, + }); + + final String id; + final String labTenantId; + final String name; + final String prostheticType; + final double? unitPrice; + final String? currency; + final bool isActive; + final String? description; + + factory ProstheticProduct.fromJson(Map j) { + String? _str(dynamic v) { final s = v as String?; return (s == null || s.isEmpty) ? null : s; } + return ProstheticProduct( + id: j['id'] as String, + labTenantId: j['lab_tenant_id'] as String, + name: j['name'] as String, + prostheticType: j['prosthetic_type'] as String, + unitPrice: (j['unit_price'] as num?)?.toDouble(), + currency: j['currency'] as String? ?? 'TRY', + isActive: j['is_active'] as bool? ?? true, + description: _str(j['description']), + ); + } + + Map toJson() => { + 'lab_tenant_id': labTenantId, + 'name': name, + 'prosthetic_type': prostheticType, + if (unitPrice != null) 'unit_price': unitPrice, + 'currency': currency ?? 'TRY', + 'is_active': isActive, + if (description != null) 'description': description, + }; +} diff --git a/lib/models/tenant.dart b/lib/models/tenant.dart new file mode 100644 index 0000000..863bc44 --- /dev/null +++ b/lib/models/tenant.dart @@ -0,0 +1,145 @@ +enum TenantKind { lab, clinic } + +enum TenantRole { + owner, + admin, + technician, // lab: işler + ürünler + delivery, // lab: işler + finance, // lab+clinic: finans + doctor, // clinic: işler + hastalar + member, // legacy — full access + ; + + String get value => name; + + String get label => switch (this) { + TenantRole.owner => 'Sahibi', + TenantRole.admin => 'Yönetici', + TenantRole.technician => 'Teknisyen', + TenantRole.delivery => 'Teslimat Elemanı', + TenantRole.finance => 'Finans Elemanı', + TenantRole.doctor => 'Hekim', + TenantRole.member => 'Üye', + }; +} + +enum TenantPlan { starter, pro, enterprise } + +class Tenant { + const Tenant({ + required this.id, + required this.kind, + required this.memberNumber, + required this.companyName, + this.logo, + this.defaultCurrency = 'TRY', + this.status = 'active', + this.plan, + this.maxMembers, + }); + + final String id; + final TenantKind kind; + final String memberNumber; + final String companyName; + final String? logo; + final String defaultCurrency; + final String status; + final TenantPlan? plan; + final int? maxMembers; + + bool get isLab => kind == TenantKind.lab; + bool get isClinic => kind == TenantKind.clinic; + + factory Tenant.fromJson(Map j) => Tenant( + id: j['id'] as String, + kind: j['kind'] == 'lab' ? TenantKind.lab : TenantKind.clinic, + memberNumber: (j['member_number'] as String?) ?? '', + companyName: j['company_name'] as String, + logo: j['logo'] as String?, + defaultCurrency: (j['default_currency'] as String?) ?? 'TRY', + status: (j['status'] as String?) ?? 'active', + plan: _parsePlan(j['plan'] as String?), + maxMembers: (j['max_members'] as num?)?.toInt(), + ); + + static TenantPlan? _parsePlan(String? p) => switch (p) { + 'starter' => TenantPlan.starter, + 'pro' => TenantPlan.pro, + 'enterprise' => TenantPlan.enterprise, + _ => null, + }; +} + +class TenantMembership { + const TenantMembership({ + required this.id, + required this.tenant, + required this.role, + }); + + final String id; + final Tenant tenant; + final TenantRole role; + + // ── Access helpers ──────────────────────────────────────────────────────── + bool get isOwner => role == TenantRole.owner; + bool get isAdmin => role == TenantRole.admin || role == TenantRole.owner; + bool get canManageUsers => role == TenantRole.owner || role == TenantRole.admin; + bool get canManageJobs => role != TenantRole.finance; + bool get canManageFinance => role == TenantRole.owner || role == TenantRole.admin || role == TenantRole.finance || role == TenantRole.member; + bool get canManageProducts => role == TenantRole.owner || role == TenantRole.admin || role == TenantRole.technician || role == TenantRole.member; + bool get canViewPatients => role == TenantRole.owner || role == TenantRole.admin || role == TenantRole.doctor || role == TenantRole.member; + bool get canManageConnections => role == TenantRole.owner || role == TenantRole.admin || role == TenantRole.member; + + // ── Fine-grained job actions ────────────────────────────────────────────── + /// Can create new jobs (clinic side: owner/admin/doctor/member; not delivery/finance) + bool get canCreateJobs => role != TenantRole.delivery && role != TenantRole.finance; + + /// Can confirm physical delivery (delivery role + supervisors) + bool get canDeliverJobs => role != TenantRole.finance; + + /// Can cancel or fully manage job lifecycle (not delivery-only or finance) + bool get canCancelJobs => role == TenantRole.owner || role == TenantRole.admin || role == TenantRole.member || role == TenantRole.doctor; + + /// Primary focus is delivery — restrict to delivery-relevant UI + bool get isDeliveryOnly => role == TenantRole.delivery; + + // ── Nav visibility ──────────────────────────────────────────────────────── + bool get showDashboard => true; + bool get showJobs => canManageJobs; + bool get showProducts => tenant.isLab && canManageProducts; + bool get showPatients => tenant.isClinic && canViewPatients; + bool get showConnections => canManageConnections; + bool get showFinance => canManageFinance; + + factory TenantMembership.fromJson(Map j) { + final expand = j['expand'] as Map?; + final tenantData = expand?['tenant_id'] as Map?; + return TenantMembership( + id: j['id'] as String, + tenant: Tenant.fromJson(tenantData!), + role: parseRole(j['role'] as String), + ); + } + + static TenantRole parseRole(String r) => switch (r) { + 'owner' => TenantRole.owner, + 'admin' => TenantRole.admin, + 'technician' => TenantRole.technician, + 'delivery' => TenantRole.delivery, + 'finance' => TenantRole.finance, + 'doctor' => TenantRole.doctor, + _ => TenantRole.member, + }; + + String get roleLabel => switch (role) { + TenantRole.owner => 'Sahibi', + TenantRole.admin => 'Yönetici', + TenantRole.technician => 'Teknisyen', + TenantRole.delivery => 'Teslimat Elemanı', + TenantRole.finance => 'Finans Elemanı', + TenantRole.doctor => 'Hekim', + TenantRole.member => 'Üye', + }; +} diff --git a/lib/models/tenant_invite.dart b/lib/models/tenant_invite.dart new file mode 100644 index 0000000..3c02533 --- /dev/null +++ b/lib/models/tenant_invite.dart @@ -0,0 +1,37 @@ +import 'tenant.dart'; + +class TenantInvite { + const TenantInvite({ + required this.id, + required this.tenantId, + required this.email, + required this.jobRole, + required this.token, + required this.expiresAt, + required this.status, + required this.invitedById, + }); + + final String id; + final String tenantId; + final String email; + final TenantRole jobRole; + final String token; + final DateTime expiresAt; + final String status; // pending | accepted | expired + final String invitedById; + + bool get isPending => status == 'pending'; + bool get isExpired => status == 'expired' || expiresAt.isBefore(DateTime.now()); + + factory TenantInvite.fromJson(Map j) => TenantInvite( + id: j['id'] as String, + tenantId: j['tenant_id'] as String, + email: j['email'] as String, + jobRole: TenantMembership.parseRole(j['job_role'] as String), + token: j['token'] as String, + expiresAt: DateTime.parse(j['expires_at'] as String), + status: j['status'] as String, + invitedById: j['invited_by'] as String, + ); +} diff --git a/lib/models/user_profile.dart b/lib/models/user_profile.dart new file mode 100644 index 0000000..beec4c2 --- /dev/null +++ b/lib/models/user_profile.dart @@ -0,0 +1,31 @@ +class UserProfile { + const UserProfile({ + required this.id, + required this.email, + this.firstName, + this.lastName, + this.preferredLanguage, + }); + + final String id; + final String email; + final String? firstName; + final String? lastName; + final String? preferredLanguage; + + String get displayName => + [firstName, lastName].where((s) => s != null && s.isNotEmpty).join(' '); + + factory UserProfile.fromJson(Map j) => UserProfile( + id: j['id'] as String, + email: j['email'] as String, + firstName: _str(j['first_name']), + lastName: _str(j['last_name']), + preferredLanguage: _str(j['preferred_language']), + ); + + static String? _str(dynamic v) { + final s = v as String?; + return (s == null || s.isEmpty) ? null : s; + } +} diff --git a/linux/.gitignore b/linux/.gitignore new file mode 100644 index 0000000..d3896c9 --- /dev/null +++ b/linux/.gitignore @@ -0,0 +1 @@ +flutter/ephemeral diff --git a/linux/CMakeLists.txt b/linux/CMakeLists.txt new file mode 100644 index 0000000..2752c54 --- /dev/null +++ b/linux/CMakeLists.txt @@ -0,0 +1,128 @@ +# Project-level configuration. +cmake_minimum_required(VERSION 3.13) +project(runner LANGUAGES CXX) + +# The name of the executable created for the application. Change this to change +# the on-disk name of your application. +set(BINARY_NAME "lab_app") +# The unique GTK application identifier for this application. See: +# https://wiki.gnome.org/HowDoI/ChooseApplicationID +set(APPLICATION_ID "com.kovaksoft.lab_app") + +# Explicitly opt in to modern CMake behaviors to avoid warnings with recent +# versions of CMake. +cmake_policy(SET CMP0063 NEW) + +# Load bundled libraries from the lib/ directory relative to the binary. +set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") + +# Root filesystem for cross-building. +if(FLUTTER_TARGET_PLATFORM_SYSROOT) + set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT}) + set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT}) + set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) + set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) + set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) + set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +endif() + +# Define build configuration options. +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") +endif() + +# Compilation settings that should be applied to most targets. +# +# Be cautious about adding new options here, as plugins use this function by +# default. In most cases, you should add new options to specific targets instead +# of modifying this function. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_14) + target_compile_options(${TARGET} PRIVATE -Wall -Werror) + target_compile_options(${TARGET} PRIVATE "$<$>:-O3>") + target_compile_definitions(${TARGET} PRIVATE "$<$>:NDEBUG>") +endfunction() + +# Flutter library and tool build rules. +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) + +# Application build; see runner/CMakeLists.txt. +add_subdirectory("runner") + +# Run the Flutter tool portions of the build. This must not be removed. +add_dependencies(${BINARY_NAME} flutter_assemble) + +# Only the install-generated bundle's copy of the executable will launch +# correctly, since the resources must in the right relative locations. To avoid +# people trying to run the unbundled copy, put it in a subdirectory instead of +# the default top-level location. +set_target_properties(${BINARY_NAME} + PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run" +) + + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# By default, "installing" just makes a relocatable bundle in the build +# directory. +set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle") +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +# Start with a clean build bundle directory every time. +install(CODE " + file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\") + " COMPONENT Runtime) + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES}) + install(FILES "${bundled_library}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endforeach(bundled_library) + +# Copy the native assets provided by the build.dart from all packages. +set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/linux/") +install(DIRECTORY "${NATIVE_ASSETS_DIR}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +if(NOT CMAKE_BUILD_TYPE MATCHES "Debug") + install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() diff --git a/linux/flutter/CMakeLists.txt b/linux/flutter/CMakeLists.txt new file mode 100644 index 0000000..d5bd016 --- /dev/null +++ b/linux/flutter/CMakeLists.txt @@ -0,0 +1,88 @@ +# This file controls Flutter-level build steps. It should not be edited. +cmake_minimum_required(VERSION 3.10) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. + +# Serves the same purpose as list(TRANSFORM ... PREPEND ...), +# which isn't available in 3.10. +function(list_prepend LIST_NAME PREFIX) + set(NEW_LIST "") + foreach(element ${${LIST_NAME}}) + list(APPEND NEW_LIST "${PREFIX}${element}") + endforeach(element) + set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE) +endfunction() + +# === Flutter Library === +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) +pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0) +pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0) + +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "fl_basic_message_channel.h" + "fl_binary_codec.h" + "fl_binary_messenger.h" + "fl_dart_project.h" + "fl_engine.h" + "fl_json_message_codec.h" + "fl_json_method_codec.h" + "fl_message_codec.h" + "fl_method_call.h" + "fl_method_channel.h" + "fl_method_codec.h" + "fl_method_response.h" + "fl_plugin_registrar.h" + "fl_plugin_registry.h" + "fl_standard_message_codec.h" + "fl_standard_method_codec.h" + "fl_string_codec.h" + "fl_value.h" + "fl_view.h" + "flutter_linux.h" +) +list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}") +target_link_libraries(flutter INTERFACE + PkgConfig::GTK + PkgConfig::GLIB + PkgConfig::GIO +) +add_dependencies(flutter flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CMAKE_CURRENT_BINARY_DIR}/_phony_ + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh" + ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE} + VERBATIM +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} +) diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc new file mode 100644 index 0000000..f6f23bf --- /dev/null +++ b/linux/flutter/generated_plugin_registrant.cc @@ -0,0 +1,15 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#include "generated_plugin_registrant.h" + +#include + +void fl_register_plugins(FlPluginRegistry* registry) { + 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_plugin_registrant.h b/linux/flutter/generated_plugin_registrant.h new file mode 100644 index 0000000..e0f0a47 --- /dev/null +++ b/linux/flutter/generated_plugin_registrant.h @@ -0,0 +1,15 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#ifndef GENERATED_PLUGIN_REGISTRANT_ +#define GENERATED_PLUGIN_REGISTRANT_ + +#include + +// Registers Flutter plugins. +void fl_register_plugins(FlPluginRegistry* registry); + +#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake new file mode 100644 index 0000000..df8d2f7 --- /dev/null +++ b/linux/flutter/generated_plugins.cmake @@ -0,0 +1,25 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST + url_launcher_linux +) + +list(APPEND FLUTTER_FFI_PLUGIN_LIST + jni +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin}) + target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) + list(APPEND PLUGIN_BUNDLED_LIBRARIES $) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) +endforeach(plugin) + +foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin) diff --git a/linux/runner/CMakeLists.txt b/linux/runner/CMakeLists.txt new file mode 100644 index 0000000..e97dabc --- /dev/null +++ b/linux/runner/CMakeLists.txt @@ -0,0 +1,26 @@ +cmake_minimum_required(VERSION 3.13) +project(runner LANGUAGES CXX) + +# Define the application target. To change its name, change BINARY_NAME in the +# top-level CMakeLists.txt, not the value here, or `flutter run` will no longer +# work. +# +# Any new source files that you add to the application should be added here. +add_executable(${BINARY_NAME} + "main.cc" + "my_application.cc" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" +) + +# Apply the standard set of build settings. This can be removed for applications +# that need different build settings. +apply_standard_settings(${BINARY_NAME}) + +# Add preprocessor definitions for the application ID. +add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}") + +# Add dependency libraries. Add any application-specific dependencies here. +target_link_libraries(${BINARY_NAME} PRIVATE flutter) +target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK) + +target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") diff --git a/linux/runner/main.cc b/linux/runner/main.cc new file mode 100644 index 0000000..e7c5c54 --- /dev/null +++ b/linux/runner/main.cc @@ -0,0 +1,6 @@ +#include "my_application.h" + +int main(int argc, char** argv) { + g_autoptr(MyApplication) app = my_application_new(); + return g_application_run(G_APPLICATION(app), argc, argv); +} diff --git a/linux/runner/my_application.cc b/linux/runner/my_application.cc new file mode 100644 index 0000000..3c66f1d --- /dev/null +++ b/linux/runner/my_application.cc @@ -0,0 +1,148 @@ +#include "my_application.h" + +#include +#ifdef GDK_WINDOWING_X11 +#include +#endif + +#include "flutter/generated_plugin_registrant.h" + +struct _MyApplication { + GtkApplication parent_instance; + char** dart_entrypoint_arguments; +}; + +G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION) + +// Called when first Flutter frame received. +static void first_frame_cb(MyApplication* self, FlView* view) { + gtk_widget_show(gtk_widget_get_toplevel(GTK_WIDGET(view))); +} + +// Implements GApplication::activate. +static void my_application_activate(GApplication* application) { + MyApplication* self = MY_APPLICATION(application); + GtkWindow* window = + GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application))); + + // Use a header bar when running in GNOME as this is the common style used + // by applications and is the setup most users will be using (e.g. Ubuntu + // desktop). + // If running on X and not using GNOME then just use a traditional title bar + // in case the window manager does more exotic layout, e.g. tiling. + // If running on Wayland assume the header bar will work (may need changing + // if future cases occur). + gboolean use_header_bar = TRUE; +#ifdef GDK_WINDOWING_X11 + GdkScreen* screen = gtk_window_get_screen(window); + if (GDK_IS_X11_SCREEN(screen)) { + const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen); + if (g_strcmp0(wm_name, "GNOME Shell") != 0) { + use_header_bar = FALSE; + } + } +#endif + if (use_header_bar) { + GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); + gtk_widget_show(GTK_WIDGET(header_bar)); + gtk_header_bar_set_title(header_bar, "lab_app"); + gtk_header_bar_set_show_close_button(header_bar, TRUE); + gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); + } else { + gtk_window_set_title(window, "lab_app"); + } + + gtk_window_set_default_size(window, 1280, 720); + + g_autoptr(FlDartProject) project = fl_dart_project_new(); + fl_dart_project_set_dart_entrypoint_arguments( + project, self->dart_entrypoint_arguments); + + FlView* view = fl_view_new(project); + GdkRGBA background_color; + // Background defaults to black, override it here if necessary, e.g. #00000000 + // for transparent. + gdk_rgba_parse(&background_color, "#000000"); + fl_view_set_background_color(view, &background_color); + gtk_widget_show(GTK_WIDGET(view)); + gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view)); + + // Show the window when Flutter renders. + // Requires the view to be realized so we can start rendering. + g_signal_connect_swapped(view, "first-frame", G_CALLBACK(first_frame_cb), + self); + gtk_widget_realize(GTK_WIDGET(view)); + + fl_register_plugins(FL_PLUGIN_REGISTRY(view)); + + gtk_widget_grab_focus(GTK_WIDGET(view)); +} + +// Implements GApplication::local_command_line. +static gboolean my_application_local_command_line(GApplication* application, + gchar*** arguments, + int* exit_status) { + MyApplication* self = MY_APPLICATION(application); + // Strip out the first argument as it is the binary name. + self->dart_entrypoint_arguments = g_strdupv(*arguments + 1); + + g_autoptr(GError) error = nullptr; + if (!g_application_register(application, nullptr, &error)) { + g_warning("Failed to register: %s", error->message); + *exit_status = 1; + return TRUE; + } + + g_application_activate(application); + *exit_status = 0; + + return TRUE; +} + +// Implements GApplication::startup. +static void my_application_startup(GApplication* application) { + // MyApplication* self = MY_APPLICATION(object); + + // Perform any actions required at application startup. + + G_APPLICATION_CLASS(my_application_parent_class)->startup(application); +} + +// Implements GApplication::shutdown. +static void my_application_shutdown(GApplication* application) { + // MyApplication* self = MY_APPLICATION(object); + + // Perform any actions required at application shutdown. + + G_APPLICATION_CLASS(my_application_parent_class)->shutdown(application); +} + +// Implements GObject::dispose. +static void my_application_dispose(GObject* object) { + MyApplication* self = MY_APPLICATION(object); + g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev); + G_OBJECT_CLASS(my_application_parent_class)->dispose(object); +} + +static void my_application_class_init(MyApplicationClass* klass) { + G_APPLICATION_CLASS(klass)->activate = my_application_activate; + G_APPLICATION_CLASS(klass)->local_command_line = + my_application_local_command_line; + G_APPLICATION_CLASS(klass)->startup = my_application_startup; + G_APPLICATION_CLASS(klass)->shutdown = my_application_shutdown; + G_OBJECT_CLASS(klass)->dispose = my_application_dispose; +} + +static void my_application_init(MyApplication* self) {} + +MyApplication* my_application_new() { + // Set the program name to the application ID, which helps various systems + // like GTK and desktop environments map this running application to its + // corresponding .desktop file. This ensures better integration by allowing + // the application to be recognized beyond its binary name. + g_set_prgname(APPLICATION_ID); + + return MY_APPLICATION(g_object_new(my_application_get_type(), + "application-id", APPLICATION_ID, "flags", + G_APPLICATION_NON_UNIQUE, nullptr)); +} diff --git a/linux/runner/my_application.h b/linux/runner/my_application.h new file mode 100644 index 0000000..db16367 --- /dev/null +++ b/linux/runner/my_application.h @@ -0,0 +1,21 @@ +#ifndef FLUTTER_MY_APPLICATION_H_ +#define FLUTTER_MY_APPLICATION_H_ + +#include + +G_DECLARE_FINAL_TYPE(MyApplication, + my_application, + MY, + APPLICATION, + GtkApplication) + +/** + * my_application_new: + * + * Creates a new Flutter-based application. + * + * Returns: a new #MyApplication. + */ +MyApplication* my_application_new(); + +#endif // FLUTTER_MY_APPLICATION_H_ diff --git a/macos/.gitignore b/macos/.gitignore new file mode 100644 index 0000000..746adbb --- /dev/null +++ b/macos/.gitignore @@ -0,0 +1,7 @@ +# Flutter-related +**/Flutter/ephemeral/ +**/Pods/ + +# Xcode-related +**/dgph +**/xcuserdata/ diff --git a/macos/Flutter/Flutter-Debug.xcconfig b/macos/Flutter/Flutter-Debug.xcconfig new file mode 100644 index 0000000..4b81f9b --- /dev/null +++ b/macos/Flutter/Flutter-Debug.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/macos/Flutter/Flutter-Release.xcconfig b/macos/Flutter/Flutter-Release.xcconfig new file mode 100644 index 0000000..5caa9d1 --- /dev/null +++ b/macos/Flutter/Flutter-Release.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift new file mode 100644 index 0000000..a400395 --- /dev/null +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -0,0 +1,18 @@ +// +// Generated file. Do not edit. +// + +import FlutterMacOS +import Foundation + +import file_picker +import share_plus +import shared_preferences_foundation +import sqflite_darwin + +func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + FilePickerPlugin.register(with: registry.registrar(forPlugin: "FilePickerPlugin")) + SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin")) + SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) + SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) +} diff --git a/macos/Podfile b/macos/Podfile new file mode 100644 index 0000000..ff5ddb3 --- /dev/null +++ b/macos/Podfile @@ -0,0 +1,42 @@ +platform :osx, '10.15' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\"" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_macos_podfile_setup + +target 'Runner' do + use_frameworks! + + flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__)) + target 'RunnerTests' do + inherit! :search_paths + end +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_macos_build_settings(target) + end +end diff --git a/macos/Podfile.lock b/macos/Podfile.lock new file mode 100644 index 0000000..f591201 --- /dev/null +++ b/macos/Podfile.lock @@ -0,0 +1,42 @@ +PODS: + - file_picker (0.0.1): + - FlutterMacOS + - FlutterMacOS (1.0.0) + - share_plus (0.0.1): + - FlutterMacOS + - shared_preferences_foundation (0.0.1): + - Flutter + - FlutterMacOS + - sqflite_darwin (0.0.4): + - Flutter + - FlutterMacOS + +DEPENDENCIES: + - file_picker (from `Flutter/ephemeral/.symlinks/plugins/file_picker/macos`) + - FlutterMacOS (from `Flutter/ephemeral`) + - share_plus (from `Flutter/ephemeral/.symlinks/plugins/share_plus/macos`) + - shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin`) + - sqflite_darwin (from `Flutter/ephemeral/.symlinks/plugins/sqflite_darwin/darwin`) + +EXTERNAL SOURCES: + file_picker: + :path: Flutter/ephemeral/.symlinks/plugins/file_picker/macos + FlutterMacOS: + :path: Flutter/ephemeral + share_plus: + :path: Flutter/ephemeral/.symlinks/plugins/share_plus/macos + shared_preferences_foundation: + :path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin + sqflite_darwin: + :path: Flutter/ephemeral/.symlinks/plugins/sqflite_darwin/darwin + +SPEC CHECKSUMS: + file_picker: 7584aae6fa07a041af2b36a2655122d42f578c1a + FlutterMacOS: d0db08ddef1a9af05a5ec4b724367152bb0500b1 + share_plus: 510bf0af1a42cd602274b4629920c9649c52f4cc + shared_preferences_foundation: 7036424c3d8ec98dfe75ff1667cb0cd531ec82bb + sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0 + +PODFILE CHECKSUM: 54d867c82ac51cbd61b565781b9fada492027009 + +COCOAPODS: 1.16.2 diff --git a/macos/Runner.xcodeproj/project.pbxproj b/macos/Runner.xcodeproj/project.pbxproj new file mode 100644 index 0000000..958b6ee --- /dev/null +++ b/macos/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,812 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 54; + objects = { + +/* Begin PBXAggregateTarget section */ + 33CC111A2044C6BA0003C045 /* Flutter Assemble */ = { + isa = PBXAggregateTarget; + buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */; + buildPhases = ( + 33CC111E2044C6BF0003C045 /* ShellScript */, + ); + dependencies = ( + ); + name = "Flutter Assemble"; + productName = FLX; + }; +/* End PBXAggregateTarget section */ + +/* Begin PBXBuildFile section */ + 331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C80D7294CF71000263BE5 /* RunnerTests.swift */; }; + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; }; + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; }; + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; + 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; + D86BA1358569CDEEC563AEE8 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3757D3A5D288E2CE07281282 /* Pods_Runner.framework */; }; + FB0DB25F8D85A21F9038C00C /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B45D24BEC14499565196D8D1 /* Pods_RunnerTests.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 33CC10E52044A3C60003C045 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 33CC10EC2044A3C60003C045; + remoteInfo = Runner; + }; + 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 33CC10E52044A3C60003C045 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 33CC111A2044C6BA0003C045; + remoteInfo = FLX; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 33CC110E2044A8840003C045 /* Bundle Framework */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Bundle Framework"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 193F1620662F80E606E49FE9 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; + 331C80D5294CF71000263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; + 33CC10ED2044A3C60003C045 /* lab_app.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = lab_app.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; }; + 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; + 33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = ""; }; + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = ""; }; + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = ""; }; + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = ""; }; + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = ""; }; + 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; }; + 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; }; + 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; }; + 3757D3A5D288E2CE07281282 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 46182606C9DD22A9B984EF6C /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; }; + 67CE13E5BD24E015FD9F63F2 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; + 7C746E1484667EC1724EFD80 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; + 86931C0E7B6C1DF763FDACE4 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; + 8D934720EB9A13518055507D /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; + B45D24BEC14499565196D8D1 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 331C80D2294CF70F00263BE5 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + FB0DB25F8D85A21F9038C00C /* Pods_RunnerTests.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 33CC10EA2044A3C60003C045 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + D86BA1358569CDEEC563AEE8 /* Pods_Runner.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 331C80D6294CF71000263BE5 /* RunnerTests */ = { + isa = PBXGroup; + children = ( + 331C80D7294CF71000263BE5 /* RunnerTests.swift */, + ); + path = RunnerTests; + sourceTree = ""; + }; + 33BA886A226E78AF003329D5 /* Configs */ = { + isa = PBXGroup; + children = ( + 33E5194F232828860026EE4D /* AppInfo.xcconfig */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */, + ); + path = Configs; + sourceTree = ""; + }; + 33CC10E42044A3C60003C045 = { + isa = PBXGroup; + children = ( + 33FAB671232836740065AC1E /* Runner */, + 33CEB47122A05771004F2AC0 /* Flutter */, + 331C80D6294CF71000263BE5 /* RunnerTests */, + 33CC10EE2044A3C60003C045 /* Products */, + D73912EC22F37F3D000D13A0 /* Frameworks */, + 723D63EA1F50C8248AB65096 /* Pods */, + ); + sourceTree = ""; + }; + 33CC10EE2044A3C60003C045 /* Products */ = { + isa = PBXGroup; + children = ( + 33CC10ED2044A3C60003C045 /* lab_app.app */, + 331C80D5294CF71000263BE5 /* RunnerTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 33CC11242044D66E0003C045 /* Resources */ = { + isa = PBXGroup; + children = ( + 33CC10F22044A3C60003C045 /* Assets.xcassets */, + 33CC10F42044A3C60003C045 /* MainMenu.xib */, + 33CC10F72044A3C60003C045 /* Info.plist */, + ); + name = Resources; + path = ..; + sourceTree = ""; + }; + 33CEB47122A05771004F2AC0 /* Flutter */ = { + isa = PBXGroup; + children = ( + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */, + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */, + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */, + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */, + ); + path = Flutter; + sourceTree = ""; + }; + 33FAB671232836740065AC1E /* Runner */ = { + isa = PBXGroup; + children = ( + 33CC10F02044A3C60003C045 /* AppDelegate.swift */, + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */, + 33E51913231747F40026EE4D /* DebugProfile.entitlements */, + 33E51914231749380026EE4D /* Release.entitlements */, + 33CC11242044D66E0003C045 /* Resources */, + 33BA886A226E78AF003329D5 /* Configs */, + ); + path = Runner; + sourceTree = ""; + }; + 723D63EA1F50C8248AB65096 /* Pods */ = { + isa = PBXGroup; + children = ( + 67CE13E5BD24E015FD9F63F2 /* Pods-Runner.debug.xcconfig */, + 8D934720EB9A13518055507D /* Pods-Runner.release.xcconfig */, + 193F1620662F80E606E49FE9 /* Pods-Runner.profile.xcconfig */, + 86931C0E7B6C1DF763FDACE4 /* Pods-RunnerTests.debug.xcconfig */, + 7C746E1484667EC1724EFD80 /* Pods-RunnerTests.release.xcconfig */, + 46182606C9DD22A9B984EF6C /* Pods-RunnerTests.profile.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; + D73912EC22F37F3D000D13A0 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 3757D3A5D288E2CE07281282 /* Pods_Runner.framework */, + B45D24BEC14499565196D8D1 /* Pods_RunnerTests.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 331C80D4294CF70F00263BE5 /* RunnerTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; + buildPhases = ( + 2030560B746BE6458B92025E /* [CP] Check Pods Manifest.lock */, + 331C80D1294CF70F00263BE5 /* Sources */, + 331C80D2294CF70F00263BE5 /* Frameworks */, + 331C80D3294CF70F00263BE5 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 331C80DA294CF71000263BE5 /* PBXTargetDependency */, + ); + name = RunnerTests; + productName = RunnerTests; + productReference = 331C80D5294CF71000263BE5 /* RunnerTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 33CC10EC2044A3C60003C045 /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 03FAA69D7458CEF6E05E5905 /* [CP] Check Pods Manifest.lock */, + 33CC10E92044A3C60003C045 /* Sources */, + 33CC10EA2044A3C60003C045 /* Frameworks */, + 33CC10EB2044A3C60003C045 /* Resources */, + 33CC110E2044A8840003C045 /* Bundle Framework */, + 3399D490228B24CF009A79C7 /* ShellScript */, + 3883C84F0A7CA8A2EA960026 /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 33CC11202044C79F0003C045 /* PBXTargetDependency */, + ); + name = Runner; + productName = Runner; + productReference = 33CC10ED2044A3C60003C045 /* lab_app.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 33CC10E52044A3C60003C045 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = YES; + LastSwiftUpdateCheck = 0920; + LastUpgradeCheck = 1510; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 331C80D4294CF70F00263BE5 = { + CreatedOnToolsVersion = 14.0; + TestTargetID = 33CC10EC2044A3C60003C045; + }; + 33CC10EC2044A3C60003C045 = { + CreatedOnToolsVersion = 9.2; + LastSwiftMigration = 1100; + ProvisioningStyle = Automatic; + SystemCapabilities = { + com.apple.Sandbox = { + enabled = 1; + }; + }; + }; + 33CC111A2044C6BA0003C045 = { + CreatedOnToolsVersion = 9.2; + ProvisioningStyle = Manual; + }; + }; + }; + buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 33CC10E42044A3C60003C045; + productRefGroup = 33CC10EE2044A3C60003C045 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 33CC10EC2044A3C60003C045 /* Runner */, + 331C80D4294CF70F00263BE5 /* RunnerTests */, + 33CC111A2044C6BA0003C045 /* Flutter Assemble */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 331C80D3294CF70F00263BE5 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 33CC10EB2044A3C60003C045 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */, + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 03FAA69D7458CEF6E05E5905 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + 2030560B746BE6458B92025E /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + 3399D490228B24CF009A79C7 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh embed\n"; + }; + 33CC111E2044C6BF0003C045 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + Flutter/ephemeral/FlutterInputs.xcfilelist, + ); + inputPaths = ( + Flutter/ephemeral/tripwire, + ); + outputFileListPaths = ( + Flutter/ephemeral/FlutterOutputs.xcfilelist, + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; + }; + 3883C84F0A7CA8A2EA960026 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 331C80D1294CF70F00263BE5 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 33CC10E92044A3C60003C045 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */, + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */, + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 331C80DA294CF71000263BE5 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 33CC10EC2044A3C60003C045 /* Runner */; + targetProxy = 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */; + }; + 33CC11202044C79F0003C045 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */; + targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 33CC10F42044A3C60003C045 /* MainMenu.xib */ = { + isa = PBXVariantGroup; + children = ( + 33CC10F52044A3C60003C045 /* Base */, + ); + name = MainMenu.xib; + path = Runner; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 331C80DB294CF71000263BE5 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 86931C0E7B6C1DF763FDACE4 /* Pods-RunnerTests.debug.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MACOSX_DEPLOYMENT_TARGET = 11.0; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.kovakyazilim.labapp.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/lab_app.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/lab_app"; + }; + name = Debug; + }; + 331C80DC294CF71000263BE5 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7C746E1484667EC1724EFD80 /* Pods-RunnerTests.release.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MACOSX_DEPLOYMENT_TARGET = 11.0; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.kovakyazilim.labapp.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/lab_app.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/lab_app"; + }; + name = Release; + }; + 331C80DD294CF71000263BE5 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 46182606C9DD22A9B984EF6C /* Pods-RunnerTests.profile.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MACOSX_DEPLOYMENT_TARGET = 11.0; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.kovakyazilim.labapp.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/lab_app.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/lab_app"; + }; + name = Profile; + }; + 338D0CE9231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.15; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + STRING_CATALOG_GENERATE_SYMBOLS = YES; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Profile; + }; + 338D0CEA231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + MACOSX_DEPLOYMENT_TARGET = 11.0; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Profile; + }; + 338D0CEB231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + MACOSX_DEPLOYMENT_TARGET = 11.0; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Profile; + }; + 33CC10F92044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.15; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + STRING_CATALOG_GENERATE_SYMBOLS = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 33CC10FA2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.15; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + STRING_CATALOG_GENERATE_SYMBOLS = YES; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Release; + }; + 33CC10FC2044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + MACOSX_DEPLOYMENT_TARGET = 11.0; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + 33CC10FD2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + MACOSX_DEPLOYMENT_TARGET = 11.0; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; + 33CC111C2044C6BA0003C045 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + MACOSX_DEPLOYMENT_TARGET = 11.0; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 33CC111D2044C6BA0003C045 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + MACOSX_DEPLOYMENT_TARGET = 11.0; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 331C80DB294CF71000263BE5 /* Debug */, + 331C80DC294CF71000263BE5 /* Release */, + 331C80DD294CF71000263BE5 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10F92044A3C60003C045 /* Debug */, + 33CC10FA2044A3C60003C045 /* Release */, + 338D0CE9231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10FC2044A3C60003C045 /* Debug */, + 33CC10FD2044A3C60003C045 /* Release */, + 338D0CEA231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC111C2044C6BA0003C045 /* Debug */, + 33CC111D2044C6BA0003C045 /* Release */, + 338D0CEB231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 33CC10E52044A3C60003C045 /* Project object */; +} diff --git a/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 0000000..e6f511d --- /dev/null +++ b/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/macos/Runner.xcworkspace/contents.xcworkspacedata b/macos/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..21a3cc1 --- /dev/null +++ b/macos/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/macos/Runner/AppDelegate.swift b/macos/Runner/AppDelegate.swift new file mode 100644 index 0000000..b3c1761 --- /dev/null +++ b/macos/Runner/AppDelegate.swift @@ -0,0 +1,13 @@ +import Cocoa +import FlutterMacOS + +@main +class AppDelegate: FlutterAppDelegate { + override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { + return true + } + + override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool { + return true + } +} diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..a2ec33f --- /dev/null +++ b/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,68 @@ +{ + "images" : [ + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "app_icon_16.png", + "scale" : "1x" + }, + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "app_icon_32.png", + "scale" : "2x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "app_icon_32.png", + "scale" : "1x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "app_icon_64.png", + "scale" : "2x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "app_icon_128.png", + "scale" : "1x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "app_icon_256.png", + "scale" : "2x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "app_icon_256.png", + "scale" : "1x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "app_icon_512.png", + "scale" : "2x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "app_icon_512.png", + "scale" : "1x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "app_icon_1024.png", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png new file mode 100644 index 0000000000000000000000000000000000000000..3b8f85cad86ed61be85aa5a18be2594a08e46897 GIT binary patch literal 326031 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%0A7n@J%}z)hOIJ62{FI49FhM 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
  • K+VKBec(sma`Yw>|Mle0rYQI=YU zf9-V8#bR<000bPi&<}ClAVDMCf2J4d*LXbM`n=kj!Hc@0E%!Sk&)kj|SxGIOMVOW;lo_Zaq z!obB`h!(&GuL-EWRZ39-JPfl#YTH8cP6+o~nbo5$#S)W|(HUOHxUX*?sQqLzQeZz& zNlcHAJY|xGo$=~iR}`!L952Rw|BcYv3@^j{+$VGU)CVZn|~d%BH>KDMmIO!Mn3F7X_}9Go(*XC#;AKdILat%ejA-x8?Cvh2I{F%%VM5J(S;ahoyj6-u8@D z2pHVBK3_?LT*jxSxu=gNl5IU=&gFp5i${LHmBos)>UoKTy~X%x9YGd@(tE0PADqvj zBMqks8le4I<^&3ES6HUwdcROa*bV@lFO(a^#V)ABIT~*Wp~^2FL2!IV2>b0kh^W>5 z4-~7P%}Q5k>(McmJEJt~BZu+jfZCd;U8%c~5+vb#(ZoWmJs9*J1)~0a@w+#Ot3RW> zUG6TFskkX0)zHXc|D{iFo|Q%~oK>Hd@JvvB;qK5+ z?@SytY1rT2ua6r05l)A@H3^7+@Ic8M*T-74fMgW<8ms+RQb2WUAgWu&>u9WAa-k;v z2Q0|TVw`Qx)8{RPDeLJ6lOUhf7L>c)7P+sfNU5~u*@yC)(r}#1wBAjOz z(tf3-BT-p#@cuZ#vlV|namoC}z+py4#?@`NNgHF}=hPtloN_pQ=RegovYs@U7OhRL z8dOdLxj~`X-ACrKQNGL1Xas%NI{Y)ifHfLG#@vd()%Bb0C4>C0wVyb zcMTxzm&0|W*y^Fy9)KcnEMfv*wKEQaSaEsuX6i2Q{r1evydU5?NUm@n@&Z|0SfcsT z#f}mK1ytC7D+5=TuYMtq{1HD&9a_KaRKWrBWS%LDik2c)JpG?44{6q;M?CJm;?)6) zjP6<+BtJggTA%hgI{xU_O}M9O|1z!k-S%$@y0zT*3DO={EdCgAz`XZB*B2eCF}`0J zC!bV^z$YFHS?uzvVnb@Ic_aHFg58+aHERYYlJ9s&wrCKVz~t!G?3pm@KJpM66fBJLd5u*~ zQZ3vy5UPG#nU?JRAv^#I#2E?Ncz;5qiQ4=VcEpCl z*2BCcg+<%8eYX~-GaK$U=>D%tJLoM;VDF*qGa?M0%@=0}i*Mwh@}`3F?qys8yvKR) z>2-2sqrk;0o=6b^f;}orqNc)^I^!2b+K?p@)wE6XgjBkB_;p{}T5ihg?!6AF=%S|5 zdy?rTk{fZqm$h#27wbmhh{?#Wx`k)XR`*n`lEptU>&5e&;htizmFxC&l{GrXrAUhM zY`E1CNNQatT$Lku*SJHi&Nsl5@M9>s+!|wFgQVhG7}1M58;-Gu7E@O<60n_T9eZoe zm_?9Br##tZVDdaIGz&!yCa{noN>f}8)oJG$tKf57CIp~S27$)wejl*1K!`c%F9>!} zw}1*r8R$iiUU#32P2=5seCYYFhFB;JJB}B{tLqINP!aD#fldg&Rd?~w<%=Pj2P_Ei zh%&?mp)ZHd#WLv8sZ%f7a+8lFZR}a=vY4Qk@!xIz?_%@8Sukp7_7YD>!9k_gdmQjoZsZ_Evi zHNSb{UF{^0A*{OBORi2*79bC{&MO zn0xEMr*YBGV7CeAAfn3RPLOf54{l;mCt8kl52wRLe%SV925|%!2>js~f%T22=Op>$ zY*Tt*IiHV^m7!5N_-qUJ5wQbA*!WmgLW%CW(nMNGK5I!z(n$YZOytaZPW<;&GmQ)- zhQ(MGx{t-OK1*91;htRE**>;0s$F2+*84IwGA16JSY+GK#lA!^kn_8&DZ|z&rpYO@ zbRGvEjKeqCGs0a1&=oJs(;$TltXj$JY#b)?5wg@i;Xaam8Z0_((CIRF5lFSA3yHjs(GFGy?*#cqDfov zsC-_q#KV%PPZW4$P6Yma7U~g;QvpQT&8gO;aj`hT;Gb>#tA7kF-WO!t*K7NFR?__> z%+7q&85inVLBsXZUz2E~V^`-{$S4cq+ZSI*Q0A@H4f&Ggjl(fu$RmqzqvF3Y!HL%f z0Sfhbn_{D1-{C*~H7LEozH-a}Kf~}hrYqOY8@m|++=pA($B1lan*DPa-QTkLzo_tk z0OA_ z!^+;4@!0TJYg$Hz$<#-g8`y=*PWKv$Yx<|e#NV6<13w1Hp-nA=Csrg@Eav!cgSD(UqPCkT2S}~Aqjwm(|(oZsE+G=gyO&a_vhM`Do z3ZIK<*?cT-sS`WUQsS#&d@fU35YzD`uCk=|zPH$bujYke&4VR4pub5St zN6&-?T?@tcdQ%Wq?7Qv7qcG)Bc#4#Fm=yt*M#C+h9OtXbqaxs2_8yIUpL^{a31uMvp})L3#zdleo)4lw5^&P2?)1$00`HsLv{ zwWTZrBKDW{#)@35MVX&fNRYOhs+1+Rt7JSuTKD$~(!q}n?MX=M1+2}(NGTfCox?{X zBv$hpceiqm!WX0?`pmZ+wl1R041zzXibHd{yH|c*;e|C9dlX8%3#~cG9m$%g+N%G#DhuXn7f4E(oJ`-VrU3GG z_a0zv(q90XVA1o-iuY24a5ZmCZ1g?b-=-!0_G1yGlR*EsbiF)E-Zf%^4_+>y&eYTO zdY6n9AWvN%?D6`ESSo{0Ds-(LJxdD_fk+Z*kFb$@Yh;*x?1?v+<$fO!WFSA#Gs(Gu z&DKPSLCW>W2;D2-_$tB~K7gHVs*4TSs-|59Lr(ls{d{MI@;k*Am6WVF0v7p*u*sYV zEAD443hZFY)uCc*KDJs*~W<+$Z>Y*eah26I!`#HKe74Qc+|rxp!jE(yDsxm&)@eUPC56*#0F>Rs?m>%wDNvaisXyEX-wS~q?a z?=9~?OZydp(ur$>J(!5l?p`hQhnO4{fe$KOJq${iFy1Do7%3Z$TsBY-*|}R6-~RWs z!&E0TEN}Jr1Ijapw7M(zW;Q%oG^6{YhuBoZgRJ(T!=L;8SRZOv zerg{sXFRCFk%Gm8YTx^xYWqUfPPHbEYwRqZirHHUm-6}b#M03xKfKgaODzS%0C>iM zgqo%f6t8!Of7PZVW!9M5-%dz30hE}nAKeDrG#DWbdP_q)(Bghc`M7_$fWBXP3_p}` zMlj-%G0(&I;N|35UgT|H2eU|p_|VSTf*?u=5~q6F{io0^d|o*q#ZW%N^6*wbt`5ZN zCZH8F7=UVN`y}pa#D8N&`O!pW7p8jM>vK!!xxfq6g;q)lQ%7_DMV*xXA176$vy9U3 zcFVKp8o=Xb8Rc)Z#zx~XEl^xpWW0ESb)L$rWCX`PwTCtG0fwOoF^o-#GX5-~=h*bj zxxzjkm_%$5#lA9D#Y%yFyj*f}#H(p!P_;=51V$eH6u9h3yIr-fKCAw^%@lTaWkSFvd+(K>2K#Lk{J#@QqG5=!enb&=9zZXch zGfP8T$O0TP`1TRF+CHq#UbCG(8rA|y)pJfZ@J^(^vpz4?Z4pR~$K{XjgaWHb z3~F;aV{cS{Wzgnwh&KN{3sq6zck;M>5H>_#=uYnNvxJt8Sq7uO=C7& z$2S@|%G1;F>m>>EvOQa*p!>Y_{HOC3!OLF8gRhQ0Qe5+S;Q?z}ypiUi?gM0By}NzJ z=G~?Mb_~%^WJMt|60C|Gqxv zkO3ZuKclks2VZfK;j34p5O_p@Qir-$3S)Q05&?js)x7}JUM}y;8GS~lOM8Jf>@-0r zI*)!}YFEFadRZsOi2urZ1WDW1GLe&t`$ssYoo8ak@Y#djyRT1W?N86{RV=^lDs%Gk zqg+Di?_%X(#MV6$U3GdcICMya=7t538-9T$dal@ECgQ3c>CPUw8;R(hXMufBNr)4( z>$SY0tAql$?q6Ulvuy>UwlKS!Jp!R_*Z{z9g0s=vD{OTn+jkzPpG zmJshg{Ddm7UdH}ti$mdcguHal8=ZNXd149=jhnQ>8t*e?ud7qDW~pX}$EP)TXO)c> z2BjTkR-1h(>@IZ@TW0{jWS6L}eT%4MN1jQCMu@Gq>X!dBk165eR2mUZu1XcQ;`H1X zn5tLS4g4t^e#>`K9|L)UC#gQ{+F%0-2*Y93xZ^NtXzquKbVN4GEigy2mpsX|73+-m zcfI*u3I_BCXJeiR()~eXDg$=B1jl#{lO#T`H~0yldLK0e9>~oKl|NRLqNWBW9lVni z-0Q~`UzerNSHK8kO>Z;|2}MD4&Pp!(1S@-WWT3UnO3ThDu*h*8SFfNXxK zuhAE>XTavPd)Wj$HGu*p+ev3`{*)W zMBJyc^91jlz(b`+4u#b2?$};m^?may{oWCEjoFtM=hfE))mQtc`T}g-2`s2h_nQH` z7VVr3%*P(lHQVpZ6;|(D#xze~=HF@W3sU4euthQ!5hz3B#F={x6?M%Vt-fjIu ze=lf@Qf&n1F|lqbe31{Wu4t#7!Lg8Hyk$Ji`AZ4!@NvtYYzbob3b%=%;g)WJ*^02B zvmXL3ULw+82;B=!K6`=NYl{QdHThvU<;j6q(3m64{gA$%(kMdMMsO3la>m`iB+R(q zNp~fKnhc%!4^H*y`^~Bp{HFJm_%Y=pLb3o{M|IGT;uJh^M~f?N9>9NThuWs-9X-CavNjSWvf5C-ya<;0w)4}fHrrYt2yT26G*6kT}=#tSB%kuc2 zo8)JI;-T!~&s3LnOfG-2y&faSX9_Xua=LH?0el~#Ak5e0e1+u}DDwFJaN*uRJx&H6 z|HJ?Of@>cy5(TiWRy{^i*sdpn)>KmaJ9)sF2W8ojo1GM<1xbpY58J8jwr$HZ1IE}8 z{YyKb+OF>;oj{s)lArC2SO>;_1R}K^ zj|yN)_Yj;S)eIuJ(A+*!khQuM*%L0}-aH-1+*kakw|AbA0PC6gKX}r<$K}rh{{OBJ zveL!!{hpAi*SJG^v|`P=6eVnLTyDw&w+(eD1d-2Q(Q1s*B-mQyMren~0`X>?(x+-k zYW&&NRmf;@nd8@Z#>&^_4DG4ai2@A25p|chA2Pa(y=-+}qAWhZK>#;$91EZ&7E18} z=dkkp99Fm#%*a9mB*NDwn9|ASMK2LDQahHcdM#^t0n@G~gzzPs2=3Hw4ai{Rpk5&M zygps&7Vwwx4x}Ikl8j&=@yiLj5Mtljo(${{7u?P|(yJ6I7Xr(?rwwF1n81)bAlj>M zvI6|?MnEECFAFC)JD^}+IX{!BCWl`=pO4~@uk_b^9EHw0b{V3U4f=Ve&hxYV6#p5K z1~YZo+vNZF9|@Q>zUwb!e6czZgHnxIo-+iU|Enm{s{WPtDMqwWsn@U$o>cQrr$~^t zezu5~4VWz^llfEcK<0(!0@zEtTB4WUa*Q~ln#Aa19|c9f*IDTD4SdF3c_=Yk`Nena z(VLe3>qYOz=WpVjI+W;rk?1Y*dCQ?4U`U`1kF%>X)0e(6ILBxHAZpNp?w#_qd&DLz z&go;J#zHl=DV@BM(J9N@#hW`D-`LE9Y&liAc9=6y;(oRSh8rjbmK~^#* z@UZs4{g~Q?Bj(riHG)l~iA+1q#LCp|at|iQdKG5-fDi2huHQ2gEWOX7A&~-R=3Q@K z|Nn*()NC`j$iW645)b(Gl;3t9Iwa*C(viFw6(9-ecN++F%X$wf#HB|x)dOoIgN?T+ zU^fW(WhU6%Pw+MBRE)r=0to_^t@%?qcdnw*O#`+i~y0g+$ zse8dhp+X`X5Nw7p6ZPP3KioLyJJtZB4Y7&fatuyY9N2IAtxvi`?zPEi4UNrbZ`Ud{ zc*lLia&LeAb&ZH?Yxl#O=fxgB*S%Hq2%Hr)4wc?qZF}RYR;$9+sZE0k0Ggq%K^40# zn7s$hmaHR|pan*lqgRUrJktB@|7TS<(~(xVeV|A(1(j8nI}XUsmS6!lIv^L;k8A_( zUTyHf4l4%iu)+aZ$kGOK#0`pD*>8J%EX2(mi5O`k_miZ8+PULc1j#$FQR$gwJkh`C z9u^$g#JcRj5VfG;1w^TVXeNtNQX^J0IPOh@q zR%NeiQt%)H{9}dxw6H^eQ%>a4?xz$iP~>v>5&lQ*cZ%{XDvI>mM0qLAn;c1qLWAc~ zjgZ4@*{V=E40zD7jp^~6@;EL*mHzH+p<7XqRoNxED4+xP&laxu=6XUeB~p4n|4S&B z)9#1L=f!S6%Y5?hh1XKO^kyD=Vx}(+b#YLfiVgo2L*^2*z*9ALJ`y>gAN68yP`>sp z{JKr=d9apH!KeBvJOrLsX?zI`#Fi{ZXdqTD934S&hRL*dIQ<5t)9<;6UxMuKtK}n50FfK0!D~FEUW*V_n{gPK7i&S)WoER)1Zk31hX z&Yd5W9LhX89>KRroR~-jp|Y={JQlNlC|W!rx&FYynr+;hC@MyyLYHurPi z`sUr`&&J0ObvgnJFI%W9K9H+WnsTm^$h<8Z+lxfevPm>M#Wf$TPQOr%cR{r;e~l!S z^p5EoO=0=c`aWxgw3%qMy?pVKTF&!{l3MtxDiStl2y^4vpfW=}o(HbiQ`X5)1m^}E zu&J+U4L1c}d$Rltgj&|T*nnG7aWo;olnSj%@P$CD7kM{AU~8UjQ>R_qj?tpR({az7YQ>*Xn1KxbHo|T;Bmv=DrTVj5O*yuun{%gcbp(Y^cukEao)0Ki0!7@IuS@$&r zJ*wR81H3#3fl&05t(=sg1+F$C(hTApw)3_qzxBLV0Q7_wf&8puIK*3&_U^}8N)6k6 z@@Z-s;DDkbYd!$aNz?X;-KB_KPwQB8Z-a+s5^|A&XH)Eri1K^&Wy(ERsP5?le)f0p zJ!tk0y>J8H!H_2IjmmE+{hN)C3(%oaYp9gPwBNGhX%w;cI>&kQf5DGRvM(-yv#d4r zH9$q6&s=$qrbmdYsaSo{3l~u@qkiwk8o@*SN-+xBnR=4ql{H5}(IaYu%{;X|-K<-s zt%jGi;o)|2xh0u*cyPM-5Hw=aYIBlzV)JHJ9iwfj5l*W5rPO64+&v+y9bv!w9_0 zRxongoKcu`nzBj~`QzN!m7zF34D{$SGo;NDyf?VE;AbPYh?t1cb&SEY)ZIjbbhj_# zBuSZy;oK?|d7Qvw)xVsI>20%V9Ia2CDdJJK!|_2}LN(fVQCIsh2W(I0ckyK&$mA+b z(Xnr($ch#SBBQxlMi-L(%3^o(#^$S0y@X1xX?7sVNS$CMTeXqNX&!3p{B*Z^u~fZu ztleUY1cIP}vh`spUqb)-eBN;3If?d}RIri68P>f~i`JpoP}0K{RrFVLLiT$bi$B@E^G^F9kf>p|JM&t5f-L!U-!M5cap(v0PAY{S4UP zoJB**)n1Ak(dl8fV6`ANvXG_Yi?|O2;#1(G-Z-kgc2N4@I%V!eK}gMKWv1I*`0x9sc!070Huwi)@cfy$;Wo^EpCyHV)Jrs{?Pv z{+^#q+N$XWvAcbfSKPwe08r<1vA9T`ZR34f&C&@?*Je0oohxc1D+*VpVEP|_76N$k z1G)IU&v${AauaOw-1bIXOHhjn>xSySjyKsY%|7>iffm}M5*KHz5mKwWSTWpO3i$yY z{_sg<1sOE#tJ8r(+mypiALPg7>L@S_x&8`K!=y5k+qMu}!WxXlvKb=_WG7kB1ivfX z!C!&}e#%`dHUUR&JeIo_t%D{v7K2KZtAJ>-{Fh20sZc^+!@}DVbXwkn?wA~LSKckp zTAoUA_o!??+YDBrQG+(hw*-L3OAw9)Gw6%UXaXkVq+)||siY^8>=&Hf%?|Xly$lL# z=#+>|h0`zeH-@n>7xk65t+p7g3F!q?JY*`(z|Wh|M1k9YH+)BUj&Fqz@W#vbs0biA zWxY^CNAZ@2a6c#|QVcr<+cE?i;6!aa8Yz^7jChLrJw;m#zIJSBQFHn0fR2>YVTd!TRAF%`#Npj-cszd- z$D)?bI7Q<{SXN%Vbd@E}Z^F3o!Fd@R5+jEsHWQd!49uX}9zqyG`!qtS-|=7HCetDE zvImiuV^(vIDo6XZB}LHEJ5i8)SwB#jeLN%dsNI+X6D%FVBpLu|K}v#-7`&6k+Pm`K zVLn-J-;t|qLx(p4*scfHE;zl%*Z{RZ_!~bv@?H*?*&s)Nr{$QU{|OZz?mb9 zA*2cv0&h2YV8=PhTI0OhkPVlkaAYDyp%UT9zOwi2rQpD4>rVNZ(_AgU8PYdYj5cP3|)KS@)8JO5TPBrR8^#AH&P(NCWL&bq_)(%+3ar zZ_wPMX?e5|USnc^Gl9|s7u){Beja$@&wDihENafsjRQtV>R4@b>_PY(W<7a#<1lvb z^G6EfhSKok#)HTf?=@t3!WeV7{Ny`i6B}WYl3MrQn;x)KGt>jm$S-_5lzNVsl)}ju z8~3Qh+{9%jJ)h-5#DjRi2@}XRI}bk*WM1Xw7e{#E`bOx-{$7ZUWs;vB^!O4h?mGI? ziga$JR_6c*Q*6NVdFq| zJ!|hd6-R}i<>|xMGr=aJ^{0XK18W`owjF$%;qis%7bc+5pk1c7Y9o~P!<5>m+FHSA zv@!n|z`WvR`zK}skFf>OiM!K>7J_-aGcTxy-nqV2GaUDd`+Zulmmwa~)ngykg*giC z*#0#$+k8-`hnpv#-9_0F)RWG+D1PT2b%GbK%X=fzsMG-}xa<#B$VWu4?+sqEFuzow zH=-=dWgEoRq6A#xtiU{NmAB}k8AGyrmqM7zJ@sC0h16Se)OXzFfb|QwwcL*ICYJVe z@{3(NIYE-T_I8o^{nMW(f<~t_{TU0Du;~5>jc`K@?JODL9GT(Eu|ys>G$K-QF;Fi{YLz4oRPh(JP&)5qcEX$&I4}+0b0aS z_^1^{8DUwtz}c4GksVh#tR+$eiL0$WP>8typJhG((2Etf$vku%{IMg299ruK; z3ml{#SR0NXU8Lz@=R!4+e4f*F@(Mmes6OA3+d-Uh(Ka=e9-lqCdjs5T&<>0hSy&Sj zbZD=MUmw^_12T1C1MjP^zxkHk6B~=C$N^>&n9LvDGp4VUVBsxy6UN3OdLNS+Te0J( zY;GrGu#YeY9?@ZG%|hyj=~a~Mg^#K~cnihM{bEH5wm|FXWLl9=r?7C)QF zVDZSffDR%pl&#RJlV2$)V7jaB$tH}UEij|jUhdqAVF16|i2A9;?fp}{*W*j~rMJR{ z?Mz3V(ZHs{cGz%eHXTX+;h3jlUJZT!^!4pl189+tcMT(?`kNbcZwTMZdjwz2KBg!U z-k%jS`h^3@(nnxrUlb$s!Pfx)!V&Pgd>z0$-k6l20~=hK*`968O*ZXNud;0?1Wn$n z4O&jfLz@Y0YX#$0D^Wy{b=l%=t#Kj@+=z+yPWGQ~fl|A=;ed?SfUW%kYUaqu{Yi|W z(kU<2ci^{zvp|LOYVNqp6s*E=GGC9}FlP}AgAMGVQpH=zt1z15ULu+mWfN^TXXq|x zY0jq!$}`0M8b~Fjz{^Bmj2XfbqhaKpAHdMC+m|6uImxrZ?%Q+Y%v^hM9e-m%Ff0G= z+S&=R(W!60_TgBvq^J?Q9LfFzf-!#-{c&fPvBcZ5{=H-ZfUgK^a&@GRvp|~&z7yd% zBO|UWlHLv7qtov*{{r=9wyvl&+n+F$D;Cd!g%%*E3z9w}9Dr_L5W^@r&izcp&2d%v z8o~iMAEL{JDQ7l_B>ci*-K;>=Ncc!AxnBrmb#c4TV)#d)+p=U`xVDh&xrqjk1@kiz z)EMvg{DeLj!*Q@_s|9@J0=l$Rtn7O$Dh2K-TP|iIHb2ECK%t_T%PdPtxdNxl>o#z@ z2+Lh%tZJO(tO*G^EW9qi_gQzTXQ2?t74;6PrUhOtP63gynry@-Rp8|--Cyat%Lyok zlUeELi8bwIi6;{j@sj(<8tRbBs|%~I(d^Po;}q{xtg8t zy7tqX#OU;O|J+1oVFc+zl6S$+Bnij-8T7~LTmguLB0xmoqaNvj2!EU06Rc`TZ-z`M z40fNQgH5t0Ii+FE&23Pe|8%x+#y6ymTXxhGA_RYn z9)=^7Kp=X&Nv2zo5vMa#_=ef9eh!J|R75baH(9<9iKcPO2^@vbww{%Ho z$EFWq=zgHf-ujOcZJsJDEDMf(Dykozs)Cks&IcH*r!o{2^f&3vFU7rH0kg9l*8uF6 zw$Pa7%mzs2He@~Nu}T&g{AY7Eg6oAiN;|lHgckb(YryX+#t5CwztFbYr*wa@TKT!_ ze&)C4!l~obj1SvAoBCJZbTG?At6QLATi6GU&hrIn-m*cuRDM-PfQ0X?)Z+dndX}$f z1lNm>OS$$*z&?5Zft}+wwe_QC=R}d)t^leyO9NJ;(HHQl*LlgGDZu2jn*L2&7V}bT zcCY}DfVr9eXKt>7++ahFJ}f;FGRX@LDX{8T`VJ!JfWuYxy^RtHP?qq&C`&I1{sBtd z-sk-!3>99%bxOZgjI8G^AQFDqd$i;OH$+DkHn%Ik%+}tJ3eCxOd-B*u#cwc6k6hrKQ_`6SExUS-#GK>` z8IbMSnFq!o-068w&=u^I-)`J?YT63+AcigB@hp%SrxyU5pY(4tj z-;qt5z&XcuV*C~L;Vth6ERCZ$Yx(ZZe`86%+p+yQmMB&lnj9~{F^YRn>(qdZkp`~$ zMO$ya9rt8{>WqDiZ3LLOc~eB>;zj1TU24eglRqyN_(Y70Jd6Vt<3Gj1pY%IHx4Ga= zo8E`c()|$cgiaJ5TkrW6#1Ls)CiH7dHC|Zeye(219zMO0+I+uG>2Wimu;~an(j`ywXqIPu)~&(uDI;<^y7^rQ)ci0y}FDXN8Ua8%hY)!ag9G^pRHgW z+i2hG(&CuvNiCk9Z!PpTc#%f>wIO!v#Y?rJq~tiMPVjrOZNiH-iIrk^Li&A|GvX!o z=JfI}>CX|+eMA*~GFM`M<%0wRTZ;+?D(LPWLB2CQ9YDqT9lHI|)PDNP7oJkZ0s(Xm z-TT8-mR!EL5ECKtT@Gd05kbZv(#4K!nH;=*VV8Fq98v(gsucNrGqj&BxUCB>UqDDs z7KG%4rX?Q5{w_lPq?N{(VS+f}(vQZ)tk7LNB;KB++()MWV&7@JWRScs8+=#Ro8R&} zbEM^`qdiz8FMgejuL5@pMmDy6hCy9D+^Lx&tu4?Jxw{T#y!tMStd~38W{(8e0TrEkLlHJh=oan93n;BKt z8M*P1hI>I|uerutBGk<@`FVE_s&9#eapT+nlK9feDJtw62YYxVm%g3QrJo6(o1Kzi zU0juL_SSsUxJ37n717R?UPU^Fvi?y>X-#~?=x37oda(jEsyWE4AyZm|-H}8c3@jc5 zhRNn7UebddlwaS3Kvwv*2&Wet>2AyanBo8M>~n2jo0vJAP&ITqdFb5>jYs&O@u+S) zoV>Ud-7g4tHHevmvOlKAGvD!$C~Pyf4Nho zS4XOSLBbGB0_34eD5a_BatpMw(Vpri(?;wzhZ64%@XUnyVVDrPDBP-f!zi}zj5;&1 zD@2$+K&5&rXM9`DDitfL)8(^r79M>_6d#?&(_>6(x}>+r-Q~>9%rpVPhL;e%+^4DQ zdeg};g-0+(_SxMd%Ra;K9_o(%J^))j4!WoAO4`wWR6QtFPGQMD`N}ZiL zI~nbj%svwAn0bGL0rOeLTcPppk{N6iJG24Z=n9^Vbq)lnM!~zze)1ZSzC0NczE0;x zA@Ta+zDWxChx(rJ;8|*c>kiuBn~F0md||wfZ#y+8 z3P9@t_!=}8Q=zd4zV7Nr7*WSx4I!h-c5WH@e92H-nA3plcu(U`Ua-P4avldgbT4AP zSa8kOd8yoXakpFy@Vyd!`?bYdlZ)tQ#L~dZsR}JS$e(6In2wkUF#w!T&G zaAZeY)BX-l=;j>uhkH;`{ooSF*G(R4$tY%($J`p$jE<8Ovf`O})T{-sYpQNzIq427QiGY;9-tXd%$XiKd(HQ- zzT4P<<;FQTcEy{A#G$KUib^$MFl`?oRdh23^7mN$FkvJ$y+1v5Hv(PMc^r+xQS-ou z+2`v=Wc^mAe&msro!6v?@&-V?k2PCzbVN17zULqRH4#$~oG&T9;<}axr!iu=(}-B^ zAgYbfh6O^Hd1G1V#>k)_4GUkL+CYsuxtZEN;;{V8TkIYr7-Oyjpf&}qt@5wdM)S`7 ztF^g!t4SiBX6D|tZGBs6oQQLgP_AC+5qjA(5jW-`FM&9I#66ao&ljSlEd=l5{M`bhaWeb(2`S>2UM5RW%3)K=`@7^v$}aLfd!Pr&MD}Zi46nGSH)MZuRm9;SMF-7c zd0AU<&$EpQfL|r8OK|B*CNWMj4)#3^(q-&?tgpam=GX z`zSAlNsO@zT)rO5{jM=^qsUN=VIH<=PRnqsIpRAYzD6m=nTTT#Y}c-O49xdN_)$>< zWLZsjco8TainOFe8q{YV_+XUa`&P7!91mJ4eVA|&qN7bBaE5L!JbEVJVF`YgIXO=4 z;7xP)VB`R=bjr_G@$Tsl-gh=hyR!1NUwi+IzwM{YpPq50ze27Pr7(yJe~vD$vf9!s z9N=?~z#E9()%>Zimw{1^_4k?^?HK=QX6vZ56F##zcC_dk?|@C%l4ra+6DYTqoJP+btt&$L{(0zs+=w+>b2 zQ5Q026sL3NT@ub&-4?zrzmt1^C}esRP-~7AWz=XulH3TAf}vK97Z^=1^M0@` zXFJ7&y!ytwI>y+>kZVSEKiDUh{&#=4QA5G6XX9Jy0sQs6#~4Ec))&Py)0 zhLPJ2+>&?btQ^v1Pyy3$>&7ACK6%|SI-Le&IfI{dH%ZX!ZO3M|4%M_}D0`w~|8#Sh zOtX719gWiIviYNeEnPUYnz!}ezm=QlrJCm92Dj-&wyCyix@+>bHhbjoA7~zNu+4?e z9|aHF^~lpao*ZKMlOmB&P>^2vTOiIHH2VXAc+3GIItS$LRc7$%hAu3lg5-(t{45p*Ys73vQ2 zSSKZQi**GFwd?aZ*dol7hsk~!CrgfB8^yg9KYe`AzcQxdBNunu&paigGGb9L*CJp@ zHX1XjNfIRKFYRXs0#nbNaCXXgFbKFyU=S7^pJwh|enR4FZei!xtG4d>Y--#orQ+Z! zP==E?*j|5xAtCL(l4^Ii#Vu;9%a6I?xhx7IRIHP&x%MUw4%TTVUia4oY?QO7ZPn7| zk9|A!AhB{35)2zC;q9LXD>1kfN?Dk|S)e|I%IbO(NG2ZENnBaEm*g2kd_uNO#;%q%0_B1g!)#?fi$(`X^ zs-QB-RDN`Y6Z;zS%=rfR`9Yw6F1L?)iFXjtn19f_Yrxy5ap2wSeZ~t9%G~%rltcnc zqACNic)x~GH91fFt8p)~jsaxrLBr=e<*F)>v5cE*tW^#yd2$O1%W1h|+^z=;cg;~U zlssn`@KYr^2<(DGcjt*xw61=kxL&2G4CHw_iYUz<-rS=tPH0SCiRoeWerIy$b?g~I zTB^tvZK0U266L|Cgi4+^w*$u}%=x~OL{8eqxldEnmb3VqRGbN1B}3s^{T=&1DVL&mBIibb@_lqRi|im(xi`c^9flU zld=;0*&9uPH;d$s0`l|=zDp}hy{g=pWwX+YDBJpE_Wv0B>Zq!^uHOSFA*G;nN~wT! zN`o|rfP#Rev`8x{AT1>!-Ho&~N=hi*Eh!C3$D!`p2lVlIzxTWM{^uBi!?EX{^H+1N z9nzNYq$HO=?^{}(sC?IxW&@^RFD9!yUpHZBp6cD8WQVy=lofOrn=T@Zj zl5_UI??Eri>mBWT_m6gwpuRU{cPQb;1d`y)5oR<+>S?eaV&L-G6!=J z9Cw=44%AZup!a|DJ+8>xRm*E7o4yW#&AsmT6)%&;siRiNM_sOTyTPJopyKY9B!g?S zP>#Vlo&Iyb>N9U|qh)9ycHt9Kv0>li6go|;U`jm<)xfyx;L*ci&W|V!K6Ck~e%!ra zifLOOZ6IBsHpAYSyj5zGB*Os?Er8?Z^Kfl2`Q;!;Ob3&{uu~jhYW+X&{ouuW?^9Tb zWQEP*!`<=r;O=0ld3y8%fpZK%uP1y{x@HS*?sAM^LCAVBarjN4c4C8O&W2$>PGHLj zBtg=?OQSo+Tq!1>Tm@YDmF0lvV8V9;G5$56@$XuA>3q|Q^bX_uE6{@nZ^-zf?65iU zub7_eVE$A7rj8T$mM}+JmOZUZHFdl0Cv#bugh%?IsPKH_6MCvj!B7__7;4YC`StUh zc=KC!?;W(1(Vp>ERm~%ojbbhgPhnZUaAg8zmHM5p z#UeO2*~|s|>5w8=KR95J#vvcyQ*gQv`1W=E^UeMXb#hyi(@uceP`iPt<2eVWPIIM< zHS-^a{%VV^!6*KUdVJ33_k}YPW{MEt9%=n#GiB6KUj_FrEY^g90ImEr^$aQJ>Z&cV zfuYF)5+kK~xnwN?m^1ya7Lb0@D7dt3JmrB+pclZzx>&z}Z_jWv3y8R!6ZT6wFxJux z01b$;rHOypYBcvK7K;UUs9liBmn(6c&`y3Td#YV?7wN2?l^))rYBaD@W{G+zWnvXo%K9424d7JXK z&1UkzWGuNOips=m7v} za-5j^PC@O;Z+aPz%t6>tN$pEu3hAX#uw*g9@UHcnaI^7Va6|E?7w&aAy1tFLl%G2< zT%qRG;H+fKMiIGlT~L zxxb5W)cN>@;MWd|YYtQx+-t8(vRp~t-Iw1Y^OE6o8eJU{lfyKI$WIy-4@O;8#a*}S z-*V6Mh#>0s&wn!@-d(P|d<6fw(z)kQQuh-&89N-u*}h*BRxF_LxnTN4dV5SK`-x6c zI}sx&hB}BYcQ?ym;`m9m9Jp4Lrn4$d`@(07XD4U3^eugn{A5DKfa|5wfzouIa|Y~V zqKRw5ThtE$aE}S_uT?uTf5rhj(f_8P^g*qmLSfl~w-OM3%mw-JXj?UBw)_sfn8NeX z&u#aZkNS}(fo}6mR*j@`H zwJ`~e^g(8fM+Xofu!jG2?nlz>`5>C@6ua+4?TRw+e)8_O@$K*NUE33XFtDaAsyF@bWm*4MSw3aCBBy#!F%pyN;RXFg zg*wiM(n0@My+Tn0D@|~bDWYCvB{05YInVgJHJpp;eu5=X4KSCi3)UZP+)%ez&A{qh zeZ$iC7ar8U(8Sz-YCs+Xz4%|ymQd>tq=-=&k<)v?F5>ll|3xFv4n9VN*vE}B?LzzR zDKS!qH~%fZ9_Ql@@4o(X=L9AqEAsF1+k$duDBTa|WiX)lQUfhP@1C>@6IK5T|E<3a zGn7;Avxb{i)rn=Tmi3IFE6fl(P&$5mjX}5*iJ_vag8puDP%WYLQhU)glD;Sv-37pf zto&49tzMkh)t}I1sgKYgdW2@xWH!{XInGM`J5)&{UWzS4PjjD-lxVwu!n9^i}l_EOPmxxEq)8JEEZby-DtI zvqjS6N_RbcNjO~x?6w$!z5DCP>92MlEwVMR13|De|A^olnXIe0ytH^jgo1vsuOPBx z{IQC_$6&nHmK&A?xBD=cF^Rvo2+M78ySxdnJ)=7vAaT}l+n$d|klntYTwy3R z>34m2iq&Xt-(g@*`sC+9PjhfKG&HRi-L6HlV#;QI$g^XDzymEz_sh=;moXYD zjRWlP;%oG5c0<)d3pYN~`4R_EHW=M80nWol9Be@)8Q~4z8HGzDwk|5ISIl`UV#7&e z=|?D{j^xJLc=EZZI`{7U(Pa&UrbELEDS&`Z4;aTjt;qLeFP-V_^#ETFf9Gp^S%6sD z1KPRxiQnDX(*aH#-X%D=9RBi03EROSzJ_!mzne`v_j{0*4Z5*#}=(p4pL{ zF@0-4!(li{+*o!>S_n5s6MFLN+4|?Cxh=bUZ)guLI=#$VF$}#I`y>0^T zkT(|cickTH1Q2iycUe;W33WxI^N)5bOF zT$DKo_kG@jl+{KRv}hNbk6Y-?ak|2WYvQtu>K=1RF0$!-Cxgf!qi0S3M6BU&G4Sby zCK@=%rN&BW{&?K!Hnb8m77M-lwsHoDSD)42mVJS3BA}k=fWKX{KO$vY2#TpMRGJ2k8Fw190*IOO8VD_C9i7tr2bQYO2^Z;-Zad2d6dF08ArNV$kB@@+FA*!7ac)cCbPUi@Zd;d4Ap^ z+Q@*F^ON*6q3l>_cgu7D=darvT8Q0%CtU!{x66>k=Me9Ftqj6C25-FaOT@5`2+)%x=Z`jDNhT7;{rtz+!C>Z6Hy^h3LE zjK9JF#&B`-V6WAriT|6|4d$4I$ zUHIU@G%f42!z@vIe98qwWmkrGe!eO03!b@V@WOpX>(iP^M?s2RUc8UGrc|xSWD~hxcQ3Mx#0OpFW$wepNLEFSnbomo+9V;tTGf!M| z0N1^G!xpQ_fW`hXq6-h+Ac7T04QGN6F8Mfs`-z_M-S`*f$vUSzZ@MHieiEEYQhBa3 zX782vxoe{Y1$|0^At+Dg>hzmQ_7p*&%IpfUKO$6spB0~`?gBEPoF<`RKm-IdUBz$o z-f({RS>-r0gf=*coP|Kz)bv0vy;?wH>to`_58vY9_4;>@*qvLan)TVd40paMGhtrK zs_ihltD&M(>y@)b8^2{U+pGs4i%fIWyIoM{+|fv?pSKuVFP0QJO%>+w*~pQYZvm>t z_t*RHH4)_?^&6cUBc-ZlLloraw~-AD)!3e0;?thsFIdizMWJt>gfEaB)4LyDH!%pT zXn7}FE`+zv{Ffvsn+1zZvFjF9h3WY&{ro6>&y(I1)+4NZZ9*62l44y~3yQWLilgUD zBlhF`;Fhl1GcH`Ksf-{}82uF^g1NuF>CDmlNOi2h%hLjE$bCWZa-y8%g*jB9nIAMb zuMILU#7bZXVd>vw)K9$Y8<ytB~R@A#oat_40 zt~-={EDdMhyoR9u$Q`?KKB<292UUAO1m|`68Cu&g?`*IM1*n z_9aawmnMBVw|rhcXXC8}3Yv&#!yHzjJ#~pF@STb}*`A^h-jc;vHr^NUaFVY4g&#iR z&P9diGHo&CNx`pBD1EfgSCc+ShYU) z;Q*uhTXgIBx~f2MwUzlk1>$%fk~!&}-1pbS4t43np?5|ss|3*hXB!k-&rZOxgZyh6 zCDcGGo1JUr-(5oZ%n@|So7?f1IYXB|o=Cf+S3 zR7V`KZLwcYK&v4T+$d9Qg6cR?hyV1s%><^!=T>lnl5T-nkhV{=TMw$qmES00bachV z5;9Yv=@q*k>P(V%cmE^FqwgUmV1*L0Q^)#vT4Ym35xsI@XefR>)q$ZJ;dwrhh+rUk z>*#&}t{|o~XCQn=QM6TMWUzsBAsl{q4d)DNV-%Ehzp zNm8n>VRMp^nys;XaSLl2MC7O>BJaBVI&_{g?~bFts@J`={Oq`+?VZv49Tc>>c z(L*&09<&-s`7A^4T}G2gBQ}p~b;DUc!U?D=q_KJiz_XYZ~}$xj#}lVK$EAA+Zj%>6t|Y6uL!1Z!-Xo`j!_ark0`WAvrom0-#@l} zi;l%sx_CwF7bk0?s0|)|>d()eNVKY9PZe^gs=MNLu+4f?Bn8^ko}=b!E%e8F1>fOU zvZpGWM>4xRnZZ4(c4fFFm<2V8n(l-yFSka+?&!tHF16*ZQLX*g5AnU3Nx@vTL|ZKP zDfdapca~9BM`HObna4#NHETY`S?&-==@>3oqVu?LEzQ_0jb;v3duVX9{F1cUGE>+M z%V-+G#V_|(>)UD=ba@-g?YUcrA@SfNJF^chfAW##n|VEV)8$ub^R#gL*xpVyhaJv@ zNdF<7hb5eIv~k8ub(gF@U+0W;pB$n|^@(LOj8G*HuE;V+YxKu7?Br z(}&5u2dr6WDQNL7QbBwNN&$S>Pvs0F)*1zg)P=6*kCW~VR#g{Q)TXm(gGZEbEuS#~ zwn5fx4`mSu&qA>7sE1}?fMlpZho8O>_H-y&Dxn9J{EPu_T3 zXt#Yo1pT%505e_E#%_IXim_b&%EJ4x19F)Gw3l3ia*=oj!tx^o+A{5EYTLKX#;}IY zxUUNSX%Cd%NMEGPjH%E_l=b`A*^`~rlbi_}eo!fR`Y~dS{a9ck4k?a-?llW)9*7880J-(9?NIhBdW|aXeEB~qL6tk$~FUO%B-V7G=MKiJ-}p; zkYbtyyx^5Q2DpCaKZ&1g3hKa=E`f@qRw=vU4n(62A6%uP@FKDGtlfYO4^)_RG~jY} zwcFbT_EkoApLC!VG}prZ>JOR17vO7z=lx;x!Qf6lu0Cz#&5Ag)NbFe`tbIWxwRP`= zLCuW4Z{TE?Z4k%h0uM#X?&)HvERG4k%kp$l9*$bJq(mdw)(ToAZ#(Bj#6WVYC(K^S zY#E6YwfWNk!_iwS!?Dd~UE-TFEPCYYgUS`_O$BItgY5#j_K%!KmKxpk4slT{cF+|n z;$+Xny8>0>nzbBq89zBnhFBkRDOW^HYz-#+20&#Q3JQlGJk7~|rPl7$g>{4XQg$M! z%c033GpNdf8zoj|;xj`HEE!(i{VBBeFR=dER~gM-HK;+gapIrZ5+ZL56(&E4ZN4PA zjkQVV4)M}vENfLCd_$olS4r`; zn;bE~{K8tsOS2hcVwno}NKnw|ynU0ksh;DqbwL8Egj!6;H!L=*U~Bx7YqO7KRB~_M zkoe_Hq6wBCK z&7QswB5GYrD4d^y2N(3PI4tOOtY3`$BJ#|d9T@b z&YaCxuahUAGOzM+s7_x^hBp>h&Zo`eQvH-CO*l(njdluqUnHR$9kcwi4nNYDE&VyF z^F33W;YZ`0Gn1U}BX8`IJxl}RYWdY^S~DTcUFd$BXA54>xRXQ#Ys)PShKgFB66{CZ zQt~80i(`p7CJZ`b2E%r1e_@Db5Ff7zTh=x6l$MEP7J+)M-G_6Nfr3;8RmRU|$6b)7 z52qhCqIB$w1o2q~1)y!hQXlC!fWlzC;9QtiK1UDRHP1zq;U(hAOjkZNp1q-{Qo&bu z4UB)-$u4GC=G+if30^jFy($Mx`1cvI zIeIvafV6bqfSL5=-d8og1yyNyOkz$EA-q@vVO?3(50yRN;vf%r3`91Y->vl+Vn6SX z8Qo$zL`9ef*7_~ zR>|+{WNQg&FAS~D_~8?RCV$r_k-6*x>0`6GR}6dysgb+!#CNRFm0V}kC&D6ev===q zIV|i>_ZxLrWavwto9G*5emc`ur)b&Q`P6TN7yc@?w~MWe^G5sIi}5u}oH^u%0qZpI zYRZUAD@2#K$I??+c1hmm)*8d&w-*~8;{aF^wHIC4vW?M=3I_j3+N$$Fw5MFqwhcK1 z>nMssX7G*)-woJVfWOf8@$T6P<#7^{oe%7m;HU_yf)*yFt!F@pH_C9ZP&eAe%)>57 zGx&`s%zKiY{{v8Q1kxQQqmfs=9nLOa$Dr}^4uQR9>{x)^O#IN(lyW)P&{=1L;J^qfH*f%E9ud&K)MUc9yW)dmMFDme5U{(&c zi`{99sLO*(82v=4_BN~LyyHrSwoU214w=@8^<|n(x4wM^@STF6|o ztree29Y}oIV&gf7lLgXif-T3$^{5@xAYo1zNdQ_^_5PV)D<*ZK@xn)H$* z?Suw{;>})yvXnXmc^pKJAviF(4psy;-YN1>r#5D_0z-B2Gd0GV5@us!oCyc+tq{kU zJvVGOC(02w5AXh_9g`nxKFpFcsw&l!25XhaS8`NjUmTkC3>KB#&$UlW=YAa(=E*Xc zDMTaX{aX4-Y8D>6KO4#dR0#C~i;1gbuDm20y4(2%RC? z-)Qnfwd6b1U=UOcCv%IB5a9~OMSJ_SQK!Co)LgV;NrH!9ZKscC+$M6SKFFBH zt+`$GOz%t52+y9D?hGffAPL_my@1})<@W`@i0<936I+Q0BNd|9uwq+-;Dk13*$p3S zAh%3ADKt$?I33I^o0=WiY}dfj@h@KwbIGvxrfjq`?gzinrb*N(n%;dRURy63uD2h2 zXb*?#+Sao19%8WRYzmYU`Z&C4A9FP9Kwm383(~RY%F#OgR%xofA_N zT#D=JxybKzwc0pa^w!2RVpsM?d*X6+<*cG7nFMqL7`L`;p@4OxoeqaPC$hjgq;nsw zVP}eQxZoz@lB^pwAnN6k3y}godvbCYqn{pox;jp%1*SS(gF!XBbQawS?Ms32vVX~t zXAacC(+&G0!nIv#*bYKJoq?3Gw0m66rhLR2kRteC+9}JU`1Lo@SS)*A+rYS`kgFB4 zu^K>*IPDa~E(FUKp)a!u&9W-V@h7cFDsqiuD}R>`7$(ZD7@|LVyKTn1i9izZyzm+V zJ7+~Zg9gQ@+R?BQEHth}N%-3@*WKOBek!*P(&5m)e zrf-+rpmn}A{qF6mnK`8JsFcNzk5^G;EDk3=sF~ON>5_S;Wu&IA0aMhod-|-qw2XTG z^{gEI-`%8`aP=MGhtK%@zuoIJS&Y_*eQS7swXlaNh_}J$WUn>iDK;FnhpAP8%@ngH zUrGYQAP_OODocs&R-P4@kw|zx5`VJ+cdL9~b+*+1g6Yi-z zP`2pH^=4FQ^+jpEJA+%SxTldl-rpNU^HjlU!KRCyY`}qJ*`Vi?2mU!!$oEV#Fv;AF zNPd%a+*1hi`UCu)w9sB`8K1@+$#TJe3WH1dWiL{r7dvchi*Mqf-02(VDDG{t07331 z?U><1sy?r1C6v@vw+DWS&)D02Xw80}I_r1x2MjQD!A2i#ZKd`)J9F6LLw6Hair zcbrn?rF|pjzmfi2G@h%JZeBTrN+GOlDYoYR4JjgE5`m|Tx;S93N-0R?lG-b!uz!D_ zdZRA2gQpgDV9v(I)4{1Oklb52(l54*#GnJDNd?d;wk!Pn2OSeA>kYL#SX%wH@wueV zt8!zB_o8}<`QC3W$n8e_P;FJ!plp(6y8nV_kcqTul<22(a={p8!h4IrQ6Xe;ECJ~M zM6$F~{5s@4Ukp-6!)fyBWm?*AAAEph+{RY5?D>epA!({5G|wsx8fFQP|HE_CoWwl} z6Nq}z*JxC-+q8%PU42&QUCl-Qowukz!?^s%^F}0vnouF+e7CVQgTv1!EPBUl zM}|FHS>yf`nAcx*J{kV%P+|PCx^I_kt_9IAj&n5IKlDc?l^Lt~G9ra6?@cMP#Lyp6 zdm9wD`iwBz3Q{V4F-kst<-G(kFX_~UgI`yA`zG=GXN{AZC7kV@cF+p2DFn~Q7y{%= zULQZ>*71>WQwo<1&oqin93$g;g&wOP%53GC z6`DV$?$Ve=NXAp0$iM2WPkM|3zs_>`H{8GW=9(WGwkl6)Tkw-f+Z530+FFu3S|w?1 z8CCzS=pPy20O66`%bQh@246y&!4DELt7fUjZP+G!w0*fg9D9V81UB5*I>ru~j z{v)+Le>%)=Bc=)sWq2eCj6h%!H1k3YY3lq_OVapK6yt2m(yC@IkJ9N5yjvn%#)LN1!s|nXNAV`osx`7e}u#^D#KG-DLVCd z>X3G9*0$+Tp6t)Xa!X3RNbdaaG{&4S2$;1t_B|n@TaPi^++S^KCTo673#LxEdt$?9 zjE`kv7G&3)`CTo&yU*=_ZO(X}#hrVK0=wm|$OMulTW#5R; zo1u}y&>D=$@;B6tr~(eexh4P$$7R3Dfip>PW8ayu)qMx-NqAVdp~j|ugo%w}Ev z1$1Iqzs#7kt|R=3DFSKy_$2Tx?Rvw4*wo%(j0mK*pdBJkLmefHs>5eyAC4tZaYEq0dt5v6q-zmJk7FP6WhKCW0 zmq-GocPXu<*+V_~!k6*VZHGvq zj}350f^1v(1t<};7{EW&Or8w;EU>j30^USqD8S3a(wdvq*Q@7ps-N9T zHwQWfnu!J%Z0*LJyE*VH*Q;!a6GByP|D`T>B=%mtMgmq0>4g<@G2Uj@nZ!{FLdu-; z(!Wi!jh%|vI}?UI!p@=MU)1D07cn$s9uLuIER`G${uyv4h&B>0Bq)%7kBjR6sF70l z=~`LxcyDhIQo`LDBS%T%=r|3UnvbUT3h>XzLd!`@XH+{+VCgbv=42MS@A6# z(RDSBwaTJRSXMsVcN1|nGrbdeBcBqOGqab9Uv8LeUWO%Gno!Wt-Yp|@y(t~tay36( zQ|C)YXR-=|ZN8vf?+aH`HzZq6WBf$yHBwtgA*eJsB#S>tP=NtJkhJJ2Imcm(+lX?? z&gY6G628g4AzB*e4gQMK7Ai>;2}Btnxr8?6F8Ld;OhJmyM& zv&65n)I)@Mr{QM06fbfbj^t$WR?FQKaYGJW06Hc?%^W>r>1y6;UxIN_-#%(&DaL_F z+Qa1*cg20}s)!Oao*@mHLXlj{7LOg`L-Ic1X3EVY^P|8i$E&B&M~`;)n;3LQcvc%K zFL@Ubo8u)|#3U$|*R;%=_bpuvx#v0sH@;uZ$>{cT5>ZphfA(j{O@&8du|^Dx!EP$} zVQ^77!CQIHE0e-^l5C63gsUe#inaH0BzG_{TAXvUMOEXmy4(GrNv2rRO=o?xx>Gul z)oY>DCpU)Pca0yC>~X7(5J3vZFrLN=GG-zxGZ*y>nb+DKQ48!{Lr83`QlJ?JsZB8B zB)jXme#)&OS#=8RU9NXygemaCPHt=^n*gj$bS_@&3yx6XPb6m( z9e6enIsef%3{%RCZ!*bL9^x*!)K;v6&kz~U)uIg#>`MYm#-Kc;{whj0d)Ku}R53z} zyr(v-4D5*UCQkXy(@R!9*XK4#s+3N$lV_xTbST9PdLTcMqh}lGCoi1$o$ek3eW_do z-6H1U+BdYZ0XeYwH1*ogzE~~w#r+JpOqO$@R@*2_V9=zr6t{)8Oj1Bn=;^3y)35xQ zq=r|d4U`^rjR&Jg)I?j$B&1v>;p`y7;&1V^)cy8qct^PN#Ex4-2C=adThB{ofJNOz`Ebv2_Gnyd)+ zj;1S=Qm|e_>Au$VRJ+*ywv2eedi9;K+P>1to30eKG$$ON zW*2Sgg>>p)Ep=z=%#_%lwe8FqZ~E$f&9XI+M;05t1l4Igz5hms{e1gs+4f~MbK;xhTn*?JtTrzr}|>9XoKi*g=@g)6zd18tZjcLIwgId)mSVK&-XRv42`ZZiD4sPN6@f%hl_+FC z&lunU0|#Z;PO%`(vpd(ho?5=!to^P~Y>s z#2~*DhlG-oAvmlqAk)3Z*p-_y=PCMPcO16}%(@bTVXF{IM%XJHl<+%u{=_WChv}~Ust(I*6nejxh*a>{x_sT?82Y1_@aRQ zOzi|G`_Hmq8x;><`XYVjs^2|)``$j`yc~v@j*ry-v2(^TCZ^rwx8oWn6?axR(^gp~ zqh;H@%0Rp<**oDk#i$a%f;1p?o@O?yT*};G%)1SufpZl!w$7L6Vruzxj3oCtR~&{5LOniQol<|L_8%BM>K(b@!q_EiHcets(xa z$alwe%U@z2KTzM02s!+a31tCYK=)urKb!u|+z@@Ur)987d#-!h&`_H0%tWc9In&u; z@_(TGt1J|zK6PPTXB>UP88nn#)>8-KLRzhn!lgo|@t+o6kW*GXYUy6abw3F|oPHp( zL?YtmxAZ*DE>`&n>#Wj6fg~Y}M~J;{tlKbuh2Af*OGgkgqS+Np&^|Dfx+BF}vIf-9 z6GB~6?S7zFRr~sHY6%Hb4);e~$SZB!Ysl{wUoMWA01d!n)3QUIIJ;pEj?XJtI$2>|e zpb1ma?c0FkxrhIwg1$wWK|!+{8Yu2nn&RtK-&ISVCoj$l;jb%X*YsSBw=d&-KB!uh z=KJdYfMbuiO=O4s5EV02WIP8U5pog(Q95-m^;0Qe802kdzxWX`s^>n`_lAq$K_5fS zozEovLPWHWG3x3i#~D*XrXHRo_kVtHcYY12B{gbCO=j`+1NdUeyyxUAY#y*9a?o?y z)+%@AQ12y-Qs|JhnD(;mpz6d|T|<0{;K~VgYKcXksq8w(@!Zx8(`T}i2Vajj*q`YP zy?x=nv^+70Qav_c^dnmYKv6OCbD8$bh1(+O7;Gu15)aPY|Aj$AsW{+>?H9_x~>u2Bw_x?e7mKw3phdf8uL|) z_<;Gnu~ZOJWA`{T`LyKtIzCEy06+Tj+lxVhSA%8F?Vz3 zp=DH%v9l=ZCoRqU5?Fd{&h4o>O^+PoXzVZHs1xoCD}Ih|i0dLaCS zk+4Y2f(2^85dz(KL3)4}iOzK0_W1dA3rCoEn^92(ehq);5vyWYG{Yq@cwYaP) z;SeLY7o&qI4?;i2?ElTLCw;Dv($ELB*amW3Dj;`F^c>Ob<>l^b27WbyATz_+(Ev-Z z>fvP=X0-~UC^`_Jyw1GW^iHsd1-9Xa+LLk{tDcMd?L_4hn4s<^8{&sknq7Pf(%5BR z&TQi{QTh=a-6T0`(MrSxqb}Y>X54m>ou4L4S+Q1C4usM0d%&fDqJvySbWnvL+e^>; z=ry3|AlTZcp6hD0iNSi9Cts*X_4e4*r1!H&;@2hmtHLIe4atqt=P4l##FA}SwD3O~ z=q{SSLL*C2FA%6{uT+JzV|Su6h!u3NKW$WN>!DeW|K;y?hnY~Y8~aM!YtlnErg7$R zg2xBjTVHFZ1~-B?IZlvAgkn%mVKO{K{Dq2T8L9oIvf~C?wKs$0zxkWb{Xi7S)uFea z@9!cd-|UF}1$u}`*33c_yR&{Lq#5iwJ&VMaF;Ns|d5Y4GqMrCVQxj*zc=|6g7n0pe zYPzj{G_dyI?kD~WX93d{SlB-Qf+Lddt4(}w5erStwaCM~IDxjs6WW>}Dtn8B7E45V(dw=j!$&iULe$~->9%yUCdH>f#$N2c&fp-*tTsDAhc=JV1T zJowW4mCFeWf<~s2_|Xzk8)`DfE>duYj~jiz1k_F7oPBOr$MP+jZ=7O%|M+X0ztrH^ zRBeKtrwZ$98s^}*iZucT(U(IPO()1R2;8B89neb&QtbNjn~7`^?|vI1dON%>}F zU;&v#0N!ANn06r!racj!Y5(M-BjZYX22eg4oW+q2Xmu&nQjx}Z7rGTnfJlR?8u+Sw z&ex#8?uL~@((u0S335FqZb30f9TX|>QHDPtDEWV3+#!jQYXK$*xz#I$3xcGbKPk{Z z{?ECBEhJDbxiuZ**Dc|UKZt9`ZOSn&rCXUIC=N0pr1#(Azgi)to_sFkns!wYDEzkP zNLjztpWAaLCgsG*PGjbOssz}>#7YHAG1OEBh^E?)-U_5=y-}0S+*!oD{QQ^n87F}@ zJOPcg^FR`=pdGctlOkMuioR}JKKsM|;n8NH_`L(?73`50@Gntd{&{ufK4gA)m0!Ws zIj~)==_~pR08VC8QDv#Pcb)jhgN)On)|SZ zM&KtNg)|V@UHGYqPNfIPupS=HkZX8R8cXJaH`&v5^b|n+aN*bm8-G2g)+1`uz*3EY zELFrqzW?9?ptKMX5!epYgt7*fS~Oo4m;fc95KRG)&P@i(RKK|fxr?^YSv}WYhVgXl zUSO3(?zvB%{hvH9FqNX`(8Y3*@@%@dFQyWzlNk%B6MjCLe3O-cIE`Iz%NQM+>CJ(J z;nTglw2asQo_dxMPQxsgqt>Lm3~KOa(fOdKV&Wy8u)%(9bEd@f2TqS1xKxFRrXSb2 z`UjN*i29^llWZm=)XwKDf1lA*j^nu;|M06mVSjIN0Q6~r%*ziP6HceFPW_I!B2S)p zbyy=E6}Re<&n=!_F8aRX!-AB8SH5n}v(=lFI z)J`O9Q6_RAmiR9$VjI3K3@x&0vu4#G*-n!%TxMf~!iWGgz#<^lCkZ?U@zz^G0f!Ua zF4JQl8Bh_b>taLk{{~k~R~+b})dP_-LjK$enf&lxY)fMzNYIWW1nu8L@ZnWvC*(l{ ztx$)bGd{n&DJi2y?klFne`v+~cBG35i0t|UXZKAk$}cuK4sgY&37v(hkA;k}6a6W1 zDH%K-jfDMiqorfQ&qy8hcD3U^_#w@PRC-zZvw7Z`*K5}5hQ~OSu=2Rt4LYg;j~)ju zGh&I}9LM|4SidS=?vuoX;Saa?5+2p&y`N8^?2CCJK)@+yDNqw^t)Hj7{KAuf@SAC1FCO@iC8r<9!W z-$Y$ek}bZg2q@fViq9&koyOzFbkVJIJ4~Bd?|Xt7xfFXZLW!WSuW%Gg`N`t7r((Ws z|E*%;&(i^BeFd2HXnRv7vDcGSHOdOp=lg*Oe>cZ+lE`e2f$-N`bFy%ZR@0Zqy0vcC zbp-CYalUS5pSmd@jK4bymsk1kLwI(^ngdmsa8;J{CTg1$fPL)3_IA+M~p;f zC;sjr2D=M8qN%)yj`@aF;$HxE>0%@j85xlXlPS=&&tfBlul$5bFKHz=*y4q(qjR=# zAjn;IfG6TAUP8vxoKCRIOmU4?-oS6@TYjNcd2-wTquro^=M73xjC1kqZ}J>`-aC$i zrsDOWaF6JB|G0aqL3{xDNP#bo2+EU=VsN!q_q-4)Ee$L}Wc@bA|B3zFH$-=6_Xfqx z2rdiYLlQW7%p;+5Jbg%S@8zys3XrbVRJFTl=L`wPab zsb*2%n_f-@G1DlPlHhtt%d89>GGfK&ZQRF?_vpCCv@u+N$nFkJv>NdlhsbbZR76l~ z_VFdkP@XiUw7z~vJx6=&WpLCMWqTYl<5@Es(jFpXn@(#RpG^6ROqsZjRNH+%lfeZk zj30eWT*A3syz~9bHPB^&fzv_eb6)~nfyfdnU0NpNN$h#V7(-u*GKkMq-*MpnWR0tV zHSEeIed85Q8lhtqOqp{nj4KbcurbiW-*tf&7ELQV$s*`4Qq?=TK?2A;&uTEV1tgB} zki2V$Xu!7bjlb=+_qK{JGI0-nQf&td}K`Lym7 zE_^m0qt}Q+2UZyr3Gk8r;U1vbAUc_{njIMSw2~O59W8ZGus0EmTQ#8t5v~@-qqjXV zJ>|c%^jj-PU(l>C;no&zhq!H+EXz+Ar`BMA@fcU?KlE+MXYPnW~ zN<=vLF8%i4)yuu949Pe_#mRr!7npY}^6>aoxlvR@1X2Hb}Pd<}jr=anq`_`*qaevQF# z;sKp9Bdt9(7>#h7%>J+Jr_Y4&X=ohg{`47>lhefwo+s2y%_wi_o!RSPM5IWGJ1M;{=rs^C52U`JQe>P!6t7Kh zw-`AHAuOnKj)+p=($W3f@nacO-V*UY#R2#k?wX(jlV|RdaG@U~&3>6&QtTb7;rj$D zYkQS5nOz3I>VJ$w3uxJ)udkv$9VL`UX;K}eg3nR26A3;PJA?@>KU!2@JJ5C4JlVU< z-3OX#0?|~8j*_@9l%6(HhIajuhgYJ-ZPtv2vf)iyJ(m+&JEYXE9I-uBuc6yF` zLa*BZzYs-CJ?7=Lz4Q=AeU8`nM2_)li!HKTdVcL6swuoKVRQ2f$T>9Fdpp2F7jE%o zvaU?6uhXAxmB!IGcYup=|C69Fq4M>jdth^@-jQRXYOH!ex9z)U`qxH>A^ zW{}^zONpO$6T&U4qF-_vy}maD6GmI`;R8m`OvD(K1RIb$0&%08wehiNa(YsHjCA2v zpNwc@B>0dqn~8+|?t}dyDE;Lw#pMzabV3*0gEuIg-iD@9R{_GzcIe$Dv zhJLj(jqSoz1pF`?N{*XdP=|XOjLUqJDq9kvt-VAx!r!}NET(}RHDf)*z2!Dr85-cw zUGJ1AlmlOo4S|1?4b(((KY(P@aP^ATXNvmCKa1Rk(WIp_AH7xK%zj1UUQm^CyG=|3 zP7rNJwe?1pG}p_=hz!gljr6~}4p78%G*iv^KYV?6Jk{<0zm=n`r0k6Bn-j9PvO^S= zm6@F_*&}J*2O)`&>8AaJbMne3q_xl{B`~G}?f7c_AN0--iJ?HDa9bhZt za1nD``YTA3rmdHdiA?F2daz5OD_9bxzx~_@-Io_X{hpd(WL5b)&okwVj1{PB%O9Rc zPw>>r*#>1@B12u5Z)Z7{O6bNw0}UL_sd{?A#Dd^gEq`{dLqapx=B9P;=#f*VAKq{x zDNJadm9s38^ed_{Eh=lC@Vs~rqR{UP11(k)T9;2n9o-=@ypG(Cntm%3@*CA#e%uU8 zxsR;+3X;HVwV+5mQyyUhNGBJ*c zunS$bSHSiaX?DS@{vqJ*1%7ZEPo)~$tuqnk%I^fo)9;1*v4c0o zhQ46@!?n-L&8N9)Hv&1q<0c0^Zt=-H67JCBW~M{nXh_dQw&|2C@@ zKK~%w-#QeES^%Bu0=!(3M5xT#DCZ zW{P!7xzeg?Ri^#6K9votoB{}Or681wpqQ^=$^!dRkY39**{!;J&p6; z(f8BQ*nZr7`~4@S3Fm~byDhGu+FP6Qh{g|q$yRzswyW7`w^z38eOZ;nhhMkBzOZ2+ z*t5L{vSQ2DQ>4DWSh3Wv!V(7SCzlqy`mCA`Arjpma82g_Gis?__ZYQ&Z8U*@%-_`r z87rEpoP;Uxm|+4hb>;K8pEFw+r1U{TQ`^G{rIE?vQ~*XO5B5`l`UqOg-95K;T5#!C z&tt1GL)tSDB^gLiXiA0JF*>Zdm!Bi@KtA)F_QrMCQi)ic+r=}nb!Nn9RQRbVWc08E z$TYVP2L_3JScIWSiWmOTwz7-Pk%SwUM$;{5MPN&xOkphjT!Eb1QBq6#()p4~Ip7`t zGQAoM0}*f~vWUC~0AJy+E3x)W>SfJs8boK+2QHbTcgm1;%|n8+4lCkJmwrC=ZFeB9 z^pW}`y7f8h@#kH%FB?juO@8#|MtQ~it(rCbUMIHOSHPpDPj-1$D1E99oR)G^IQF!B zRj>N0p!;YKqmjc5`n2sQIdm`LCpumsEsphQ=UiB&9mr7Fta`VaHOQoX*kF|M!cZ~m z2OPH;?FeJ2jB#*4HxtTk68lCe6C7{0r}Y3X>BCv_XIjn;`uE4%Zql z$q%4(TmTLw1o;CZsOwi3qc3GzPrAEEKe0ATG5-fl5hWDrBZM!C%DJ~COYy%B zGH#N^&fFK#OU$H=P@?Ud~8C+gnte1G+Yt)C{-2w>t&|!ZRjKjI}NcHlZsKD>@O8JK3*- zr26VuEl%ksAImu+VQl zo&g{uWcml50H|a059%N+Md@vnsX!Dh>?-#gx(8~d*a-`iF;etRpB8Zkprbg$_Jp6J z#FMlw@R5R~ftyv`j+~boYp0Zhx`hhp=lt0|3T#X|t_DqaYW*sC%if~MZ$p36*zQ^M zguE;>sqz)hkmhyi157%#_2*elx`jz6-w%hb#Kf|YC_L0*syyzHB&tYNEV zK%9z)+OkAt3m069@R7;6GKppbcJ?WGq9VMUJyk<aM3*?2mxV^M#M<`T9eo(I(Rb&<(&;~TCe=NxZeNeGf+ExL@dBbA?qROP z*#RYkyvrCCW1M0lLo>?bD2#LkW)U)FFxn$vWCCRU;XywzrCbdX+n|i2B#+xn;iW{vmhrhLYCw~u>$75?mWHSrfx)p)}4DbOCR|Q%!n=vrESdqjExs5pj&id9pdPCZP@74Uog>3BH!@1w}l5WU=Fs;9q5u z1NVmLkHyxtBZc()oIsp zpm8vlALzz|gM-n^*e9P?q3MpCy1A zFIIf)gH0D=tm)F#Le+BXbl_*gH2UyTZ!{OT>xo9vMQMjZX-LD$p|1k2i=wQYJys4O zAUS7!==^Mqe?$GR4ZKuGmsPv1XQ#NE6I_@CvA5*goWjajnZlr!+N80m%G2%qE9Y`% zUYvi_n~^0uGYF)R*{&4??-JE#`nT4-B%g}{kOCZc!`w|a%cw$X4Qkm#S@Yt71li*f zeyKC!9?%XYmnRswh z8ziTuq@v*a5sHaYir{mp?e~yiM$N`pI7hi$6=G$-k7@<|&%seCwy_&>eF%B=;ewC) z;j^;*I)%&Ad!gaUYP{0Nx4S$)5mIWl@o|nni&f1BjS+ti%VM`{B%s$uY99B5Fzd{K zu3CIDNU2JJ}w>ct1LEI z5x4uoAJav^0AzALdpIcNUcO>>g8Sv2b(})n15%dSqMHt64>cJ)O2iolt?H7CvsyMC zPibueAw!J(#To2V_NGZ`2tfQCohQ>W)A!RB2o%w5&|$eT^30h_aP1A4B()lGx;M&9 zP2tkgp8b5sXC&Qw&);fqdPcKR`#UL z$NQYPEE3=~BP5xqGK?SN9(K0y64Tv@d%o?Ik-Ii4)t@Ar6@9cIU<8ktUl8DY1V4$6 zFHVV|kp6bMj91({Z5K9BHZgOu*DL#+jVLN@O-E-`T7Og1wgv?)B2;fM&>BZD>kVLI z)8_LmMc~uzqlZMRj2meNTafUvqiQ&%2UWLS81FHGn-GHrA%^^k4ob&6uHx*tedWX9 zW#hv~90YSPkC`YIf~baB|CAGA6cJu(^L*uuZQ@yhjw7s9;rN2Cw))(1I>Jq6qWfp7 ztVfv})i;py2nN02&QXO?TuQ#Xebu)|bW2;=H158l@LkeaQe|Xgvyq^KXxS@UQvK=U ztVmB{^*q_V$>kqg5bA|^Fm>Dwd41YF=R2?W*Ulck}k6pq1beESsDRgF_u`;c$sVL6vqQnQn z28K+--WWJl1Hhlx){SwTj)wG z_w}vUmH63x9{rV4&CUhVp*%;FDGOFS;*TkQdQ}OV{j0q}gTv+^Wjek=Wvt!^ot)4`N|8&o-b|f)>ZLE_LG>GJ zrkUjVK~e2PC`1t7*#V0}mN=bJm>bfG@!qmmC~UQ0HR3^ViDp_p$***4F5K-bKU zI0iq~WAS62hz*5(d>g5h;PIKw43w}M*)$vBJfU~YV>`|7%q$UegbqL@Mw4re*1aSX z=#8taYYGgf=jtQOqdh^_A!0euhj)NNGF(hgMg=Dh5djdX8|f7=En^Azb!42{7R4ON zwO(act=*{l1-}K|(10NSudHhdp{gF;o$23(^&OILtf<)=Rr?i+o~8dNHE6-ZPO93* z>BC=K@_kTAZHv`Im+OyOT$9dZx9DKZ4W5GmPtmo zpvh@B*ra)>4>-U(3y=5ECm$(o|6`w z3El)8v_imvDU0NIDxWP7W|?)wRz1cx-sm=TQ4S;hc5vuTivnVFE|d3+wW`}FX4z+tZP9e_gB&?&wms|z7e+BGJ7X`_r{dX<$IT8tS;c0Si^9wxb39vk3lt_)i zB1s2WL^Q{kxiI?(*tKxyadWV*M8N{`ZBi(ENIA#~bKGKBID!R^mI4lus>pd@r19&> zLh;j6XL-p|CGOjaTESPEjV!LKc}fW6U8hNClBC0v23{{HIR%SY0m=roIO()aIFLR? zMUHv>0J03U6L?Y&DiJ|~35`c!Bmyp4^BWL+p>zF>MXruZfptBPp4VOgMz?~dk1U)# zoffCTyl;VBXOJ28=U#pGuvE&0rp)X_KLeoGP=`OZ$xWZ+a@i~*Pu_gdI`SnbfBg)$ z47k{9d>f!g@V*UcrekEsh7#Rs{@2ZpOeW9+#|kdXU~U3+^mbPUvr>%+E|(kgK`#P!FMiI{Zv3#vy%T}=H(E2F3`4zX?plM|le@kf;f zi{1^h&?@A!u-Nq*4d`{wuR##?ho@XOM{3w!N)VWG(Wqh-JAfwb^3qv?=`;CQ#?}+* z-QiHwRw7G4cbpOuwVwKvvE9%8@;ki42{e8`c+8Mq2Yy;*UK?i4Ce;E~SbjjuenS2` zX51sI9hg9U!e%^RZ$z3wn_oaqme$#AG2t;BVl`(0A z4Sc4SDxYj^}bi6+jijMl^W(kr8 z3j(U~=?=wwv)fz1jRC-fZieRnx*g|joaw-#*6qgUL=f^{q^k7|Kowt4kIBkISbsBT zoU6X1<<{wlG&4}ysg09~gQo6U<>0+8zi#De1k+@t_a*#!LEaQXN$Q0Chi|?iJp@uW zH!ZB$7b$!i5J8bw{j+&N@>D$mxBBe^o~b)G@W1rw_9KX%JFo4#)*Vkxj<@KjGNk-Q zbjk1$BiVqS*Hnl8r|O z>hT7FGV*K+;uNU2J+XqAcWw0I#xu@=D}P*><(b_nCMkUh1V-qWv${`(7NPyP;%=F$ zq#B``^==%EuT^@du`H5B0d1D{+{OVjY~r{E1iZlQTGA@HLy*kD-Yi7iS>vTkoaYT%n)*I+ed%^2oyjlp{OR%;6y35a>1DghF1Hoy4R)9IUznJrINz{BS#WJe;SDvf9Y zy;vNhg>ZWDVMeOnW%)Yf>4(B`YY>E#`WZ0uDGk)i?_`u>A!M%LLRpDXjA}B$?I7Rt>z6yn zLyBS5QLGjGQ+Vtkf%Sz4Ku8;ZC=7z`V9*lmouq-iPP)+xt-qf$IVM&3Ig{#j^_4ph zy=P0l9KI(J&97#36*T(Gy|vA7Fjb6H$msq4XSI7)cx0h{j~GI)Hqg~0 z6Y(Q8{G0y_*27irW#V3bHi@?VLOxuPmrqa0Jg~M|)#1jGW-1zgO5QR%mH-qhC?)5dPc1dFeI(#h_IYix-^~dK z=0lnM95|Cd4GOKzAba&|9f*Y8EK>A9XL8#8eZL_Jon^XYVeag1j1!D|2q7NUsfO84 zCxF_eM_zv!C&Z&cT@wU@U2bz+%KVE&*FYp1Upj0hLTQ42fCGB7ssm?^yb@f1p#2%e z&3lXi$u-1nIiXzPRBL%3(k;jY%&L`5CYhT^AK2jcLwX0q$iz@{m%;9=u97@Mnvau} z`SEazhAdK)nzU%A;ALs8tG$oY>xpkej~0@H zKH|(|?=tNjA6+{`-}vo~$mYFbC8zeU0cKN0$ssF>E5#&98`(POiQ!{kKI-=4vDqqo z8@!s*=h2!t>}G0ODX`1p2lP#Fm0G+T(J(#u(G*EvSS{H$d4eQE=hUDL`wd5>G!F1GOh}NLleRSPj^}3@7D@|dAFzu znK~eT^t4{?#}}#;5KKjLS)Vn{7&>7yAvnh>27rdiZSSSo#bv`s91cH5B_+*o*cH=% zdh=kPagQR3zvA)MkJVSfDAURidT;hy6k{#|>AG7yxfUL+7B5NOZDi+1PXv+1-kH)O zj?JT}HogvWS1*A~V%KoGK7ywY{UK532IDNtrmr@+udU#@q#nXNc8F0E@{HQuWnnvo z0{}<7k{VM3oZlg%6jY)KtVtbGGxOYKEK6z%Mp5|&+D%4r>t50I4gn@wOQ+YA8jjaV zIJ|?o5qb0xqG{Nw^GVdqk;x8>O0Tq-3v#IdZ^*9;iIKP7k(P%Y>ccl86}061EzT;M#8NX$#{>2+A5Y$NSq8K4exE)T>UXw$c9=Cbtqa)s_+b#-;z+{hja8I(hNOMcG#O_Zp^^1DJzzvN_E6%qs)1C&kt(91d}JXg8lTw$}vPF1Deos zp%CIIc}`9CU_{VyQLP+OEkQP+q}!Hg z% z-7L*n_hn>Lt#?fPll1;6VO9ThxEl^LaKh@7RM$E zdhZ5)U6n+eodC|+Azc{A`1GO+C_dcT%a}YWoc_=(Q+keYdmCCqdP+oz1e}LMR}_>I zHj@Pzf&bqvSv|9IBmnGohBM%QLBe~^axE_5AQh5?zZvcMdw&$1{?I~M>pf$X0}Jlh za@VixlPhSN@gcJ;FW0#eJ(%clQxj>I0(DeDYijIZ`u$>4`eTtsM7Xx|+m$J!I`aV9^ zK{KW!|8vZS#C|dQA1awarYFtwC&f8XZU-veYByzBC(fLLnwGsrCk8#pKHGp;TiDd= zG_X|t0A-XA0-gR;_u3I0#ZO0wq2M==KY-SV88vtim!VCbprB-@$#~{(g&1SL4CJPP z=K8_g)(#8Ao`j)0V6%V;_~yeJ722h)39vAu-z@C#+r|?4VPm1^To{c_^>rb9ow)J- zUmRLbABm@1HcfgUu4uv}4D+mgi?BMke)(hx3HDXv|1qjO`|8+g331cwlQq+o7hgI` z@gypN^jt?*)&jA}G-}sX#%Ap7R0f= zc%vstR)H$;JjfJVHzSMGMnMdM%XfwGKQrgr`=6PsQ56zS-e^h0`Aezd)Mp*QmoWa^ zFxk@A)fA})-^0B!M<4jpY%6y)_}IJo>WyqyY+{o0kq$h zYX^0d3dxRx;BbmL_?rO$9U5D|qs8p^DscIpb&wgRks)F&KEB!r$K%Kj;&Gh@9TgNb zMm~~7AaB*gcID>sEN)2aDY-r(BR-5S48kgNerNGdA`Wr?LDc9-FKh?OVC`R>= zIMZO)Jw_&8Ch|-|;IxQkjv(EUX}=w!8`oKU7YV_GcCGZfvX$@5`*o9R-Zg!X=11?mCLctK9tNGPX3qyx$erjTWVI**1)Z@el zuRQy{T1_WF5GbOI0hCz+lG>WE{2?*29rxUa1`QpW(jWdzdaYuRKwjk^93mlR4ipvB zn`a?ePz?NIlAm~W!X_G{757-94TOF)pVU8-Ujve){`yiPr3MW&|#=^4=4hR4W z?808O&nt#4Yj-I=s8idj;(auUeu{y&61;F<7{H#jH1H7>VA60UceM%whSeW zRY+w#_(Pyv2_!V)!^)La#}tN6WLnADcfW3&RhJ-ztL@_1wY^@g>ziRAztzbcp$LdpgoZM`Fn+5^XtY?_sGtvnoi$|xk&rhIX%dN5+GT83Z-`C zUc26ZVe3qBak7;?H@ABz5(1a>%vzK#wS}OJ5p1sHYgqUjrDVKE;PyukEgZT+!2p)< zHtbzBP7lNnu$z&nX#n}pNOd(Z(I_Iaz&3e%K+E+8sqoL>(p^rR=q z?^l;>1RUfHKxpBom_N2fZRq`dCC^5)s}GflK`95Ni+|soU>_;D2jMqQu%Mb#;&Ek? zJuCm4ccpkAJ#W*ovvb@(0C2MG4fOb{@bV^D=R%v0lAQ*P4f^B#b(YHb)V#yw9buAm_T)?WUAjH|9uerWfvOq{>u1!VC^klzmuk zO0W7{Navw5bRDNFkGdF57A82lHojxrPThU8mhW5Ie8G-xWO@ z-P~Y&HDX1<#8kLzdtn$Gw}U>lnNX07~A)24Bz7P2oiyH6sfbky=O*3 zFGorhiSan~w0MCiBp&{4*6oqUBk@fds@iBcrFGTLT;9e_&4llxY&-kiRoD zm8djPFqJCyjkAhrr^45~K=MZ*;^gq_1s0a0kE!Ir4kl_|AOKWU0w&1f18Q=`>=hJIWzdtEb zyY!6khNhN=602`(;pNw{pZ3W2T%RC!JC^i}- z@iZb{oy=LKf4JQlk+GHL5TQSG&+;_TLPE@kiuUFjL_`)$5I$?m2#)>*|!)3FM8 zP#nc)2rMEwVL@ma$&cqPSrXRteXH7DAfk<~U}52HzY(Zw8}xqxj9$+XBc%Q18vKQL zDpF3tH=<#bFF~Pm%C}k&ZGdpgZ_W-&p8hKgP(h&T=d9jAV2BD8JPXlF%~pi_5{-TE8!@Eb*2dL=%_?)S4;rSqGYib_LH z^Kr!}aj^Zb{v1Z?4H~7yuL3<;`{^=CN;BS~Y0PCA3(o0BW z9}+n+0B2}Q-KT@FIUXo3py8OldJ|lAmZgJ_y0ixS9?{JSx|1ff1^w9P-uJt8SJs%2 z&bXN2!-Ts24(ZGBh4LpeV*8GW#g?=d!yYm|Gtt{6I!c~~4juvtF5WtyBkCG#Q_8ay z+^uj9O{8CsuHNpHi=r6o5X=>8^5?%ZyRYp8YycHB=%{^weg+U@EFdxFxGn+sVpcZ! zNG5xP6niTrU0*l#U_vvUk=KfZHwhim3w)1(LjJd0y*s`-7Jk6+gAQcSfk96=qjyq( zd*rKLh~p|vNg{|hOkp0!zrZTp%oO+zQ{>D~yiW~cL`>z$*g=e>a-{`vN--jHabEC! znhsp=7AXv10_6``*RS*HDN4^|HUQ7Ce7<)~+GLA4`nO9*8{EByTaKTqsIM@3)2(qX zU&20r!2BF1zm{l5QOz@H)v*Ucr@(d}q!;c0jU>=wz;>TYb#>>%iT@E6`K^40Mc0U= zA9@?5x23>I>RwsgQ}!w(=koYgd8CAQHaU1*;6;!&+IDk3uj0;Gb%)i_G~1qXlIQi- zS=~dzx*SZq@0ot>K6;sIpn~}Bs3Ns+)W7A0mGqBMf7)XD$~EI&aEPg|9`FFX+7zP7 zJ32*^=Ip5(U0U(rox?JVW9yK2fSktn+OPM&Mzmb~zN{|&`JPGZi_f1;7Sx`9Kd%rq zV@j%p4+T}=bG&;dI$BhB7<5osp5ZX}1SEku?D_8;I{ar2fq!?5v2!mR4yy3Bpmbkh z-PeCnM`|lbT(e<{u#*Z}5wh0oYFxe1jlDkCq^8fAk%EJs=7BZ|8VdF?LtUK&huc28#tot<`-bzN#}%z$DJqW8d}h3BCuP&)QG1(F9O+ttb+e0i{UaUpOvVtEQO z@J7b`8$sZbp5Gq?E~Bi)LEzF!;su5|N^O?kVYvmlu4!AqxXK=jzZlP*;%uu?= z70g)*7KI{8hZod^XC>~M{WE8^NLWcZny|c^&J_QtJ9g>KC5Zq{&$(X#{p)0jOKW zaf}V)N?r*$jC8J`Sa9t{`kCnDA~1*~xW5Uc^?kxffgdf_RDmV8Ek5v)y}a@&XeS98Yh19Ptcw5g_c z!IvW*Spm()wNX6?oXj<-{{b#qQh;G4!LrWlh$H`ns80o-T73Ni&aLCYIJE9!tp?-h zSz#T$5wS6daEyUddlzVDUceYH9vJwl{w-9pIsmB|)LL*b?2`WcsXqYcu96zDIqyaf z$BRjDcrfFSfZ&b!@dCTW@jrve);n(5tE`TgxoaxyeVc8eMbJ|?xY<$q#^pf)uADf( z7KfE{b5mpAe$H#I%(V1nbJ}l_Y7{ALC-%r(hP84BEYDfu|K9V@*(S}fu^N)x_BF8##I`bg7HNYu|}l?aCN=IXunQ=g+hH9fSk zTd=tLlQ|>1#(p04=VC2u#{=~q%q+e;Jc~9Zb>Fu<9l{hiUxovBzgP6eZJ6L0d}9n~biQJ46e@{nU6@62k?rvmn%5 zo{UwxIUw3myZ@C|m-M_)1`q)XU;z$oc2<5+ixy$R8%AK5876K5NkD;ikWs7o#R^v6 zVnsFkS(CJ`0GvYv>84CTH$^jVPGEV*_!)huWsuVKk5D%*#33hp2VI#la1}YNRS;+~ z0>CO2jTn(LzQqWRuz6^-h|;{x<$qnP?9&w>{cyglu*92bu39GjQ}qN@Q=E$TO7aaL}*)vf3ZyTdo)=MnQ2PCX?51zS$*=$@yOyEzOu$h-CUlp@3~^y>G^W5I8=H4bT*Bd)iF+A0XCOm2=*s-2<1d;V4q9|4q4 z#3DKl)F7vhqUL6T?b^*So<$PA?G5AA=99eK`KN*N9+9K8TL>@2JMb*l%WhNu5=jSz zEQ=Bg&I>KV+pyL1o9uXHXD=GQtSOsOo54eBtSw4n7=R zg0m&6LSTUgARhXi2bRVmCG`YWNrlALFHpVsZw-80>TL{6JWBR|6Az3W;EK1Ae+E&A zD)&;KCIFGYoqHhicS4k1LlLyIqKts)4*7&O&mb|?%n=Pt3*nZY|K6_9fUZ!SEWn@s z=G%L4y{etryv^?W#?9%Us&B9$@Q5Lb++o!~6Zt`yB=T9-4>$b5qhWThyI=U%Jt6O& zeQ4n-c{4xQ%3*ttr`jiWorZfZ;#c9;(2}2H%kXlbpTf*?Y7uWEm1$~WNi3J5)1=8i z+3t3Ylz}0(DPD0r72U+xGmTH7{(3wm8Q-fuF9w=Z=um!k~V zHsadY_gDEic%;B(Z${jLVlS2^Igrj1PesfdGl36wZgiBtLlqY7WYi=OVPY>;dUjwV ze9R>9>ly#cC}D-*pRS`=g}@gm1b<(<@b!whc4Iz(FI;k0S@T2ptuVNh9h_vkCP0g; zT7vl#9n3*7oxJplnUAJ!9I2r?PrLwlg{%JjKF5_zSSG`M9;xe}Bx#uW88Ctgn9*dv zD~)u;ySm1pb;S&in{j94HNX9p-_NBe>}&v$^~ghO{KB11(+@OPqkiE>XETo6gi!2v zv-Ot~-_TZQW#%i4)((4|zJ=r*=U@inCVj!DjBrx*$l$LWw+iW^(BiFvT&mloL1tWY z(jV(A&W(NU|4ep|mw$_I@kC#Uk48e7G+kJQG*h>naA? z`J#ug)U8`J`TsCoC|l>%s6n5JtV<`&W7G=1zl!mNnIldMIpH`VeXC%ONekBBfXB4H zyv!ft?swppgCo7-op7=Mipl^Tc4q-SNsJ>m(eponkXc?7Wp#@yp7XVX$S$kNzb@cE zbIDXSod#lP3?v!2Sm(o*42x}tJj}%z{ts*$nj-Z_?^*SQnaSfFM`&v${xsNH=}3W$ zZ;O)RJZ^Qnr$rwl`ULs5c9wHSJU-D!0(JBLeo!y=ih?>Nq~QhH*<7acmbM}P^Q4B! z5GNW_s6|40!4Z^DvGZthp|T1S@^s2EuwY>BmbVdUqgj!3dEOd1glq4-I^TY?q9Bz# zf4qLnGvE%3{mrnLRfBGWn9!0mf99ecw6jNoHT8MT*GwXouhY65SzDIFpsy$LmC1uk z_#X?jq_j(>1xbn7@~U>~|0lfD}j*fjEJavpG&cM`f5=WnHd)CvlR8W`|tYml2b za;9a!&^9lGQfjC4oz-}c7V~NO;s6053q9E=4Eb7u{Mx~w$)_511Z?a;M4eC0L%(|a zEou{^utQhzjTaQgc>fY*Sw+)A56t6yvY9e0z8%~DgKupi`5c6JA$Xg%`-?b$h6vjC z3qvDen01-m?p(w3+`^WDJZ^ci?KxL&@ZomSQ}(z%`L%QhgLAC@#Iy|4XoiS zDM1`YUy8)%K08AIY~vTLY#ef(;sDs1j%3kfSPO*NlJfRBkl6f9Qtm26{hGpr(jaUE z16y$Q3io{F$;U@QN69YoJz;22-6#Lgl=2bs?LbqycV6JZ`aOr9bNB7%QXT7_;FjMR zsf+Yr??`E(HY44iX84i+(sR214ErOC?y=W5 zAmB^Hdt@~5KuZ_^H9jb=vFLJ}A1sxHxGLBV4064{-H`*<=@)YQoX>G(=+*@*l0NQ3 z0jY?tNo2-Xuu33i=B67RdmP8g8Tb(ex%qG#LL+sMnSEm&+hYTngt!?a)=ut&EQ zzLfUQs+sxg#}Cp9CQs>d({5&e?8Y}$PS^Og z&yDl$yMp;kkJhh|PD;g711rTP8!w;x5TVVCAw6&X5m*>ZAd;>R;%;WD0ltCr_*j`e z|5=EFWt)i^_5rn1X^RBrrUI&B-2x*Z+yjAMh`3NlVUtuC0mxt;+Iy5Cd__8a4FpF+ zUy0qfOPo0gfNqvAP-L+u2`RE_nlO^ZW;*!Nz8?t)xhWZY5GJ)vYc(XCS*D?P;0?q3 zzaVZr$crB2ZvF*}o}(I3!yc9xQd`qX{0pt0;*8n|rl zeXki&I%KDboF9W;Psxt~7AHpEOB(M!F7oZm;r_N}U@58;{&U=h>GMSRs`-zEB|o0d z(57OdbKsvZN-gs0{3z2jFPK{+Yf?h}^wQ<`poZE`kBEPefMf+CEqNRG3K7qe5hIZ# zp8dAt9(!GVZ(`nAaud;7@?EBCC9GfI%6~XH$)(pm&nMjQMX3h|uEQlo{i;bc`P%c}VhRl;=Tf`hQ-nI>w7n zLoTQ2FZzS+`C5`adT$iV82DGuh^1=hUOr=k$ET!;$-bRe7hEy>3)Ld|-v!*K<`NdE z74M+Sv;{=vZxGZo)AN;*n>^|D57j4_Q>42Q6)SUPMCg=y<#l#FxhV+Jg72cF>8r0L znyD`51@#gHb?>dTzAnB+Cs0&#&E3zcW9`!G#r?pQ!4{rTSLV)86wB1h-n1akQO|43 zAE|LuXF@E(iI~cmvYkDr0HXbv@bfFnqa0`;Tj7Cx%!hly$(C&P2va_L$|39{SGLZ8 z!9Ke7kVo>i_v}#+){($P5enyjyKGl7Hyqr}>jgg(f(c4XDiDVsfNnAsY>iDfFL zed|$E=tWIQU_euFKXN9A*0sQ-^N*fqLT30{oD=NJ0b7w~=~|oj3>=CQX04h)E%Bht zIqlhD(L|RwocpC{e`i0Bj?qPn1hAFTFMs7e*k2o-CUQbai?<%Ek`^GO3e@Xtp zBr!HU9Sq`d*s-+P{|HtS;I4rMrFqX>nh5g57e_Z%9-sT)z?SZD zFkxwn3UZT-e{&?@5UxEdP+3Fx$|vM@ddbfx>(w$z@5USlv!qj-T5Xuqbof6SJ-msF zvU1^78g`V{&v(rQm+YPS01Ld)hI!l<%Rlejt1P}n<@{uVL$X$IdZ_R0hQGj+@_NM5 z&)AnwO=Fl#Q(}IOh(GU6Su%T3w8o&jsMxcaodpzy(+GKPq`{F;D?`ROn|al1BhpgG z-3dneYYE=Omq`vIDnQ*CNO4DrBs78}2Nhk;nb{V=YU!l!KY_Ew@|QrM2P!hX@$_Mu z0bj(H`Ly?Xre1hsX_lA4p`(H%%R?f!M+))aK~h~5x(|~oIH6r4cBB!uO`tXmW&0Rj z5DATE;1%<=$Bp>{ s&PZt-CK$VI!o&ui37|SHF$ohu)TGxa75z$brKw;5}!#Bmv zgTz>INu=o3fJ@Q#D3$1v|qcMpZPk-x~GoMzVq+|&D~4^y*CHqyO{p=iGi=Ui_<@F7p#yOM9~8rc|@7hJWFu_zhBNgF-|?$ZXgAAbV9X4y6Quc z0w-2dSj+@IWYzv>8xzP|MtZ@U9sv%51=*lKB`3_`!Z_EB^r1y2bmvOpVdpk8w$qyy4i*8xLY84aAl49*i)gFNOi47ju z9fDeglj2J9;c4U&kUoTBLA4`MkK#aQBo-7c1YLprA&GS}OOY5$`p9(I_4Ia5{lTF( zf~l8#RPgBsKlJ)tn#>eBu6gJzm4}VIZ3Q}SM6&zllBsmsFW8>G*=@2r+G3z9aBA$5Unr1<5@sQ$H`UZGBBZ8iS z#}f~JJcYpH**;DiTjA%;CpB3$U&SIu7vC&-wkVkA?&+e?FjPU})vx&A+beB!K;?#Y z+@45j;E;bAy8aYOjjWn}A0twYaDhd>TRrxWqy44^huy?aEoCTC{!(JnD~oTnU;z_-cLuEwXUlyNCHGmbmwKX2QY}&KVkPKLFp>d;GML zRj-W-DACwbsB=C0`~EHYfl-To!y>6%F;*>MSo({_N`LfG+Wdxnd)V{{7*l)%^Vz)q zA)pkYiRh42&5|ZJ%ESIZT-LPuPrawObBAhT)(x4oR>mBUa&x+!zOcpm5K{hjTtHv%{slbH;6se4(;y`?w5= zy=i=0I|8?+;`F)(rR=*WOBD+WCv6xEsO%`pZgX=34}#N(&Y0*g#I#12e4L3(3-Q~4@00A{q{gHyd~%{Ll_Rw=b z`u)HoF;a2Q-(IILMxftWW~5xy$U8mDi&b-ZXTFH%jp*irwtc{>l63-1%>2T-Pmc|j zgF4uBJjikc%LNHvqV9Vg8dokp4!)hG6Rx9n>1x+?5z!xavbr~Lk97ZXR~%_q^@+xi zP$h}>G-`k{%ax-fmgOQ(j9*54nO@lMmmNe*O$d%(7-2AD^tOD^cAG zXbd|)UZ%d3cGTK*7;0fJO{w%8NgMuQwO@&QCC(r*4wU&^CINqOuww+@2Gjs*(aXir zz~VDKp`D31EHrdHZIWlN2Ma>OT9BQ1RU^tjyyhlw&3de4z{<3t0Ne9Cn@Iyk@#e-M zAW>4-jA(OZ1&I{>IrricnxUJ-oRMMc-M1xKPZBtf|DY-?rKoC>mhsz9o znFfIkqaOmj;J*`F*703WF~@;)UGR_+79yn|9SR&wNbem9sCn07eZ)H+oAQD{l+)fl ziy&#}Yq+jU)N2Q|0!$16WP;!rLWj<;Q(ko|=V<~%!kH5IaLkrbE0@hN(b#j3-c+Sf zfW0KZZ%M)twwhZIkBtr1h++X>1l7R=y#5+dVI^Ce^M8~fSWpNQUpnP97x_V0WI0}D z5JCB9XDPBQF@B4Pv?(5FLnyZdX+EH`y~w3<8`tLr^LYtn-ws{9Ys)l_EcPSb>_PX|KAO;49eOTjbSI&YR<9`>W3E z)aRE=U}s8G)cN-xE2ID`O$H#?Vz?CFR56|Oms$X?h7d>89Ydp|*9oAY5IvBHZcyzX z_Z}XA3RrN$B%O$LDovLxAQgjyuavE`c)>MPXh!cW>g#g>ZGp-F(iSp`W`Oa3NG1ZJ zE?n-xq%bk7cr%1urm*2>uus=soE6T!f;eYvRZ9&y=!FQHF~YhntQDbL=XR&tFdtMq z07o#*sXeHk0zMOXSk{FLJ;bR;kq`eOrC}JY9&`IrriK7gnh^HMu42&_kROoviu z=3+eUB+E~sY_G0)FJZG@YUVj?zVd>W@A3pAsl4IXp53(rQVOo#o@`2R^}Z3bxOv9B z_D1G{Q?IfLea_tlKh{)>?)p#cwFUimy9}b(rL{YsmDmqf@9omJ7^pnH&|zMrK1KiQ zT7tzc2b0MDI{CJ2XR9&I%8z$UCZn%zm=Cv_g3Tr`C#LnbExKjDtKl3!-&&IN{$s+y zIlf#RBsNv93l0Bu@hC|>I*HBEY8FU+;2@RT^{=oa$AKqG_~8e}cDe9-HbX7te8T_o z&z4EJ_0Z!=$$8djN}#j$C$lRq7+A-FTeenN>d-9T#68q>4Z42$9u*7N#LC-xIU1?J$AW~}jU$X_GFE|i=QI5uiY@5FU z?tf)IfqJmgB=A5T@>F8(YAc7V&{TLd!+c*e`f2BAME-wA6KkAV8RRiU8+{)I#@V1; z;w4>tSXZ#ixKBtkIM8c!X_8gQN%O?BVFMK^t@%gtXf;0r^o3OIs1t9sJF8b0nD$I| zcbZ4_Lc7&TaHHZpZgZ^6=x^{gL}c&<%~nx-_C6$@}w@KY$=js@jTLn zbsKn`aO?z9jf!pZDG#+Ip@M(bbyE--qJs+lplDq0s^3La<^H`%2C_zfRYI$K<6YTY zp-vX`gn|~USgohgTbvaitb}{uwupSt6@hQuy0jG576H+U!yX#^ehOYa@I%Qle&{%~ z#-2}yK8zlH?$KCr3PoP>OhDWTN)G`qD1<4cLS&X4NF6_`KWGPEQZu9tNU`D$+e#zkgKpkkMWuU+rvef7CT z_tRN0pw{PS=v6K^+im*9E9S5xLNg!eMDUu{F>We!^?!S6jOE`|u=~=2Ey6-+1{bBea(c}Xd0?2zU>{aRNK%5#Gq`p)f%y)$ z9Vt1MxWe2<%>R$1B>DuFlsx^WXyFfrg>(fcyl;!QM~U#x-aU4Yko zer7>aL*xP7zE|lN)|0Z1YKbPKLA0ii0|dj&_dyxmT}W|Qwf$CLApY?KS&+57jQKaO z!2l&PU&0#__gx}8A(&LJFzxsH!XR@~s})vxupM^g@@jMB(eL?rrJie!Rya4OLPH-P zT*8l=4OHlHL33GsHCl$JeSPA4?`)~;X8Xv#y)~od8ds6=vFImrvR0#(k81OMAAn|M z%F_Yk?G%8PY?q*yHN)Z^g&RZU75xg_-|&}r$Bj9ksO+i#(b(+6;CD#hOYiDgqcRV| z51`R#ca#4q>HlNwz2m9=|NnoZNQ97`EtHX20ViRCRik?tltBk$5BBQLPkFk z28_{flmp(5@!T?VpwIQzAN1kA)Yx?N!v37Ri-zEpX^6XR28E z7j|%0=nl_s3x;L2ex!E43q3>i$Lf-d;!pUpR%n9t2z=*5ME6iZy991B0jRg-P9a8v zdX3)q;qO-bf4jmkG5iMBiZ75W?60g0x1Dk{FSlfB`9K4183a=3jGw?UZd)^29&m8E zIFP|U8eZBBoG4iLq_bPWIm&B(Z52YZg);22j+d9m9Wmg%LS*t3i_A$7DCCpCc@iHtC4fybehAEx${~GK%X6 zuKx(Y^5)y~$(Fy8_FAy>w0PE9`6%-0{H!85D*5`Nr&5V>CXf6LnWyPfnz7n$9s=K{ zRwJQrG%M{mOWSQ7@L<)8%3K5y^aaKDX5$$M1`Z;-UOKI0wn&uVy=*o7?e}vx6UBOv z`4cCs$7lVED>UL-Ts#y)35ByPql0Rqal+?!r2F z*Ks5?R>%I+p=-fFrjx+PFGfO4jPs9}8svnErBSkbTUf%`v zHhQzoR)r)##r#?Sr>sF@wpVPU#EK#Q6kurL%wGRt(cgvvg(V9S*m)bHz~6alpIs{c z9Bs_>A`uMjP`PC{XZh0R3lPkH>P^@o2Hvxcous&BcBGcF`1GBjzENZSFApr%t+p7zY+|BaP~g$4;29QJUezJq7INzk{PjG8y0E8NCIFWD4L z%QhAybRx=zWdSw3x7_rBbni)G5ZcH9g}0)$(qlgie0AwC)||Ac$&ZI-&b6;rjvw;= zizU@Vtu%?N=Ipx9x3Il87efnLYU#lH`@7)Hb)HO10oN!i3KZ_kS<&>Y=3~F}HrbaB zjU0~iyuZ^-_O`wr`^cIb9;L5OYF>CM7dEXrOnIJ9;lkKFZns0BFK6_sg+{aBtayC`HZmTh%l7C3ut{<ArMSxh6E8}xIahY) z33$D8{E|Iw>cQ8$gJqF;sd}4V7!}6DST7-1_CohGJZXxE?xOPMX%m!2@n7-4vhI-2 zzU;@VtYRNwtF)%l#!O4kW6;x*+d3Opxg=qvnB+P(4($;K&Uo10IurVZK7kyS1a43K)C=VVo2rpIqYh03dM;|L;?4KquNJZ_( z&$4KRfiv6F)W}yw3U|a$QBleorM3&hNpyRIE2T{~vi6*T2WbMbeu`dA#88M=q9B^9 zm1%rfoH%^Crgt?lo=?cw!21mBs}Cwi9nlUXYRv0_ZY&dPOO7w2bT~0?UEumWInN|c z3R{6;Ec7)GAq#!#5z?1>D$l$Izsqy{aGk1$J>HTMi?BQ4vuu>+dh~?A_5STr&M!%}sizKp z9Q1oXL2&DMHQsjo*<>PP;8UZ)=8RbFHc6k~A@Cm4|1=xLV75nYa;#J~!S$OPFayFz zY+tM>>`&me3%?rOd2A6gooS?q9+i|Mh%+gD1BBFgvXns5^JN|<^U!fsxTvA>|{A97DapSYe>bA#EXB{Q3(7j3ME9uA~F zkbvQL$?!x>;8Ws01>gF111E$r`rw4nlMu{F;g~rI9tAzvQ!0hfrt)R3IccZ7aUULd zh0Z9|zp@zoUU7%i;iW-7mHeBZ(BHf0jq1Ms3L}9d%z2w;5hhrP7w?7RCZNfoeh=Em z+p0j@bsKl+>n&oi7BydxIw7|V=^}MgO7jX z%}`c<+mCL6ah??4xd<;(8$A^kRrh!+&y0d8lO9IBgS;0JeDM?e0_kVVyT90B1L7OA z{`!W>RN3tTOPTzk(Lf884!d`eEMG5EWXCelJsAZYxT!xzHQrN{^v1E#?tPu10{LWX z)#$f^>i``-HNRI-Q*3NclyP8^5T~mPbekchXO=EV7-O2Hw}d_f9qJ z81NwB9Z7Kgi3ZJ)e?r6C-m4=6m@_6&k*Rh?G$k>Q8=rJ1JZ~S0ehdD|q=%2)CHSw3 z40cj*Y`y*(9p18Jx>UsM1cm{lH=dVA(2(`FaMQC!hd{r(1a;qQkobmS6F?_V3(#`m z*&P3Cd#x7?UBiaFf-G+;zqFWZxKlw;BD8fMJU$LdZ!zdZW%~b0R6N6yM%un3w8Wm3 z8oZa%eDyZd1_bq$d6v=D_(`bP5CFh%E%Q5x2R{&Hs0sE@>TZsv!tQFyo=?)U@+58v zUflb_m!W3A{M15U4}RCLcW1f15QZvOF znXx+wX`jz%ZN%J_$%B^SDV@zc|MZn@!Z$ho3IE+woj0cxNH9+OMRK$wd!U&s_LuAyaG#VR82*EyS8~7Mc}p!Mp}MKN zP%^^x!K-xQjHFgBeZS_X!4)YPG7-ubrWHARG3a_Ln02o*n_tJwWENn2v@}kHmd0g! z{T(u#E`q(2D)ECc=&9Dvig)1amFH+}R7UGMc83)`Lj&hFU;1a~sS(TbZSy>5K@nte zexVWW4U|>e5r8@>*{<|s($BM3Dvzx$C**qk6hN$%iq72dE?EJX zTQtR3f}Q@Wg$t+?4S+M@g9~m51D|c1v@kcYyk6n>-t0gZ_c|~NHdvGq07amzb-jI;9~q1d!o&8qqRbSnaP26;i?#$)6WmKuEl!{c}7%65=S(kjcNY zK~O9e@yR?3b__mWi@6fLD2TqL0H$7{UQ6eLjiG~W?*iI$;uXHQEclqR3^dnCGcj;T zQQT3#>T6f^-XFsd#~;a{)l~>UKu^r{vtXR}U&U*Kz|(^qjE>9W=8wS#UU8qb7|3;5 ztWG*fd`{1kGdr@hRB+kQ(*A2|inRs@?KA@4o@BN>euV$}6}Z0dEl;mb1doEan=Rrc zyjB!#nT1^1rYz2NHJNLKD^WBR93eR)ue5n!V=}S3@mg9a8i1GtK~G&f=x6L6U0Xg4 zhNg?q(3CCFsu7=e2Rxi5xyxQkus9y28Z+a1QVel$yY|zI@yjb=GjD58=mv6o&hJhM zC?U+tgpBQOsKwZ*ZQypqM-M$)#XRSa{f2Aj_ney!&N@I$r_x$%L= zWoHpQvH-=F6MD)O8}^p~hrz?pOL$>xdN6R*DT@%rds6dHg4nZ`*mWLk!Bhh2zR-va zkqKZNEJ+L~_H8ZcfS#hUlk7b`1)e;4Cawfs8_HP8sGt=aaN5+_LB3;ki^3?(^Ah;R z$~NIZ>lcMmq8N&|D05&Mgf>`1x5o!*HFeTOXWOEL-skBJ8W?O1zDT37#(T`f)v;W& z5;L$7Cx?y4DyP&ozQ)ONA^+B0RRgXZ>mJd(Wb-ev?5DMfOhglTDhr)8Wj-&MmX?1M zY*{;~JP#Dv+^MIY;=y$l~~BOVK#RgG7A zrB)Dt-8uZ=r7nUz){J1;<%9i?d6WGN^OSzt;L4aqA(P9hV%{rvQR>K|62q&{HxFVA z$BYRjr=8TOw%=}NyFW@{_2xRbV0?^w2-`Esz%G}UL*Fjf%==a`cgJW!oT4JX_&t6K z*Gj`^;p1V_w7M2=K9We!eQja5Smax*pu%=c%iR2Kzu6;pBbtwogbjJ#8OB+@_pA0U zh$;{*JndLAcJk^9(d*%JL$ws@Ejc`li5Wc6H0+VsGBj&eTB059=adQL7wXsiax67i z)Zp1=DmjA;#agFqKkOP@D)qpYdKnQ*_QZ+-Chw@b*DAc$;{5r}trj(!lpN1T5_8OR zG4Wd(u1q~!Fc|*FLU_~Kn-|W=GI&hE2K`r$AS$fq7r*Kwl!j7c3b+u?*gO`j)6}1f zOU7*v@Q03SY;z(P`9fA3r>?=GOg6bIXjj$V7_D_+CxQ5?zIzk@ZeOy z=+498QTs+o)pz(jdzM}vckg<+s%&dipBP>+(0G<0#}%k#jJqCG={=(2Ly|8O`XwYR zqun*{oGm{QWm7-Z2=|t(rkGpt-LDfkOz;?-+~VSh)Lw7qRDSUl+Y0^Ok^#&2!wn@5 zhL%UE1IP`6%Z`T$iAgvc66gI441<5BpT@rpR!BV@WqW-u;%uHUw)VBV1ZGnVg97*? zL&?r^#6~YGHiOl&eNz|)llZU~LJaw3GuPV(bdCjW-Y|USGSTtjhw14DKiN7ZCDPXK zYkZPq=~?^c+4g{K2Rn_m2gYIKtX?(f%%X;JdxZTt)g>R5W!J0p!Go7)QZd%dN5c!X zlyE6g(ODyn6-8>35~TQsW9`Z^@%wNI`I(M4{qYt#119&LrHWStfiKFn<4!zuWiq`q z$Z%P&pT&Up+?h1EhHYmEyI>0LnKv8{>{vTw*)?>+xG3|tLO&Kec52_!o4tVlCVTW| zGI3*4cyCClCI8dwgs)ExzIFr#7}fLh-?1+jAHUzxDy(h(=&D#Nwj4^Vy5!?KB`-tU z=Ug~nhtJn|pfZBV$_2|b5!VUXt98^=SU)~>Up&~BJmAACHso#)xA(iThA7lkUx^c$ z+o3w(F0h}cDqAirQZy3u`5X^P)-y)iZLNF8tfpVHOGm zmwmXJh>F5$`?O_FUuh@j;uuti&2$ZxSs1!!CHFV0CDgggj+tOlTsuiTQxDHK_9x4k z=uh6B6#d?FJ3(WT-s6!(1iU(EFc#%`_32`$`%ikC2kKR`=$P7w4S|&HRT^@G{zh@F zvNV`gt15iSCi($xb zo*HSwC_a~z7k>6hIocj>5=LS7&x?$t3Kws%4HjL>QETDCvKg7!KAzx!U)g*?=n>A8 zLL6~98P08y)3aHBr7Fp}I2Wh{lktu8>xX|y$zu(EdS91whHZJQbM@Xzmb`ypeoVZq z!>Xnn!B~?1ch|?-oL`>VCWp%yEw#N4Pm^3?;fs}O6Uh@Mb>`^es_i?sJ4kh4IUX>& z_F}r9h@m3&z@&+!Ksv}|@+#uXbX;Mq*Oq+!?d3fWb+T+~ysoMYK4+Cv9C>lsgXgbO zswhd65{L%Uk;uozcL`~Q-2$Rsc7Ma3uBqFqHVO4`z3y{%vd6jnBOd(K(FH{6;*|JQ zU3%qJJhb>{afjZCtkGa|lv3LfQrft9%-$EhYJXX^ous=@ApZ$jJ66V%7io2tQO8{O z9i&yoC9OyEUWlvMC8*}Z%%0Qs=QunA@7r8s0D!Q39mHQ z(utSnT{(FQUSDymgOWuJ|4X+5N^Z1-`hRrWd%w%F$?yuE@!fXV-MvS(CsVAZ%rmPNzX>{SJKurWXSU%hda2xYycH7~ z?OZg56{%~@NL!_SCQBH*2g(6vo6{8Wx_<#JAu^X-R_4XJ4hWAD|6dL-9e%~ zv1Q5c>{3w)AlMPuLAH^2lp~*w?a^gA)_qQt%ZE0lg{tYoT<7Q3S6^&LW?piKF-+Gu zhr9H+h6rH2%QuZW*JkC!<^Mou!{_m-Cn2C|Npg2eE8mXyAx{+O#=4?QsT7Htpl&{= z!N%j<=RS&CQloNah@m)7$nisy*h&itYY~T2vp99u&4n>>c}MHJEq}J(?d|C|%*d*s zER9j8gC{~0TkG_Wb#BB61pKI6`>d5wVTnW)Y^1vMh|VRiic{6&JoTr?72mvo@T$Kw zl)N+GyzaUgxvn?AEtqCFKCN@DDropERIWE47;?;I3T6Qs|o*eGv%YoGee5uU(U`^-TKa&QnO{yOVz|SMC6%do7nG_*Ly z=esq?jXF&jE{}Fw9ZT&qd(EEWh=0SLe9K5q=F2tv(N$@hQI;ev92%xre(%lLm>IY) zRJXr-pqfF7dyG`YKe3Cr7L!~tx4PwM{bMrd^e}m)S)c+k5N1$wf_O2%;>KKU%!`!< zmW6et*qxEc#qG=Zn}-d0wU+f?9r_fisv64?YSu_$RF;hdmAV(ZGPT6GE=v#0^J%Iq zR3&=g><_D*ZqRX%8$75gbSsH(NC*Adb?qy|9X_mYo(cFY4<>g5?yR{)@_e`!I<}^z zUPU>7&Avp`ZnBtTQ$`=>43(Dt=0mUac>ALg9>{JGEOgdikPhlpX7sNpb?IK~ebLXY zPP{hwQm7dHY*tu!yM7f#0#i7ds{C7>>7T@SkQ;?;Y$ zvl46DBlpw9<@cZS;kXsfc<`$_)$}E}4&om)tN6qUT)xLeDa!X=jwi6ejQS_b_4pmE zM0j;SE}qE6!3t$RTDl!Y$*lkvOUJ1J$Fc>d>(yT~XGV>F^tl}GV?CfA1 zYE6~K##on+rmr4UV@IJJf$XPikHsS?FU4_kEX_Hui%x%IHZos&H1k?S{apELao70K z=zZy9&)Fauz?vB%v|hS_lcEHq~3q-9J$qXb<%AB?;T4)K&PJ)`&kJr zj`I4h{0aqIqGj|HT`=vLGTp|e>3KTi)_m5CW{Nn4R%5x2d>Sif0NJO!oRO~yb-R3P zphl}wj!}*J@2(c&s{7PL2Pm0bxR=J4CG6Lj@s_}yh|X$b0gTSO{1v-`q8r=0Ni7gHzp!)!lb~!_y&Imc(uv z?o#Cs^Cm}GMdI-8j1GqIc~J+zGMHx@m}<->X1N~Ax?dEVKk7l`7-cbM$6>%3QR@U!q>;y_y=`BoQt3)-N!r*dPuLp^-w zeAMlTwNV7B-L#=AS+u-^DZA}>Z!rC!#6y?X&G`A+JH`}l#|+-20m3+ z_(xJ|wr--6Qd?PtXhQX@w9D(J3mYHJwiEjm^dfWoIs|OWI?6!(y>k6ED)%sWL$$lI zO(I-niGm}elJC2hO?H(i@w>zVc08D0^9vDsD(MTJ7+k1h+!>F|Z0enRTK{BGFb%%g zFy;Je{R$tB$UIU7#SB_&`+b>rJ=h0YeAuC>305UJru>SyVfG+!Qlr!rr_ICT6;EV> z_aZhXpE5q$Pht+Ux;zfP^akxFaX)t5?YAB7Io&*N{C)x+D*2nt1uER1|ZH`Oxbt!Lul zB$S#)5+Op8bR{sx84dW%K8;Q7@|@^ruMK*58qEE0MAn=o#G(qx>IeHQ=&EX!e9_!g z`X@y39xdF2@A=$)DZKRx^>uB5dG5{Ne=k7ZzZW2mUX!mP8u%juYS~{=Z(wwNWxCJJ zVtVw*K|`fNlI135u71uEw(WQjsZuTD$8qtFu9)s&E{3f{RT!5w6a?F4teR>o1(DQ; z@O*c=P;k0!HQeP1AwJiv3v78r*-Q1C|Ka*Gs}I(nXXgi<59g**ukAE9$Gx=eCVKlA z?KMQM?r5D>_jXH~g>Emo#jT>(TKy6lSvOPPBd)uO1C`7nc$+i|7hmJ zn`LC5VA5)Z)pzw*!Di>_$}8I(2LmVY&Avxj};VQhJ1X%`b2&$ z7uV%qa&KW>gt_MZUUc>T9+$rz2Mq+~Jg$$hJC?do{#ZVlK24N2094+VCjZ@ZPJGOo zE4KH97us@{NzEp=r9XfbNKgNrh3(>Lx`BE%=Wq+_9YUYS$_*Lx!*2${G#l4b^aV}C z@oPsGR?%J5Lt-!$cc;p#(i_ZRPXWW~l)*G4_PhZQASj_7v|+YLVUl8;5mKF(OenFs7cG*sV2 zwU0ro?wj|^-rI#@-w~X;`gQNSw`WZ7g<6d}8cp@m93w6@Qp!&C3EK0s*=LK>`(Ms?xMM z@!FP^`&5mc86rp(pXB?1X#=ZTET*~Vfr+R29 zoFIOcrd#MjBDzhBsmbf~Kep`=fr?;@*^1yS+@c>lv$ZaYTsTA)9WVEQVGh6x7zeVP zP@I&V$W@SFRZ71mqZLd?5`J8AOc;~M2jtMJEu>YYb=D@=-fLgmF#YXo{hT5BKkfPJfVxXXGXe^_S(CsXU&*- zu)XB@k3l%td|N@RmHGVOm$+)Hm>E`WI!ozf;+zD8VQBPy-Mmc1>D=@t!2zL^T`Q06 zyQmYH7xf15#Sg-ZS~RU|6m`uM2N%2_nb*D2ELvQ@LP%b&QSC*n5A(UY>b;zRMN?g_ z$mQ2t7Mbm48&b1P8F_uy3v6{`q;uE695594k zlozm1VY+Ct_mwz54+2w-=};Q7?5hQEmdmAYCm|l;__k;=2il4p+(iIXKsX%)JCkmm z6)nF2rW)>9sJ`Z-ECv|R1JFE_i46~Y@h2607jKi{O$4=0PqBmNI=cOFfar=V?B&%c>xN^VV3N8SE-g>~~e+?sMMjU3K2PEy$lC zw+-ul#5g@=BJ{I#s9&2nVL%ix80OC&XsU3~v|RMb#$x!v&+$NlF%Dx%s{AEqrTqaa z>=cJN9mMIT^n%60M+8N&DyvAAti#-+JQ`?|W=4-^Kd0IWG{(8+4Ry~ofR!rD9!(yO zv(})b+LihIv7=b(oxC?u5QH9yb|ZgP)qTSo;Y^4+jo=UzzS@w=)AHE16g=? zlI__3;!1y#L+pW>7{#?W%-bHXxx81W_jDK(*E}^4*q|AH-(48NRMqg^ro+@T(pd^) z?K&<}PM0mdgYEvh(gb!j&5A@RtqeZl`Y`y;I@fs(_-o6Hxi%X&;o`2#q_?&7KRHKA z*g*|b4e`L291ugbSvmdJI>jC2MR*?tiIED?L7n1if582yu6LNGSrVZ5LHhEW_o7kd z+^xm7Ng5C24GH7$W0b`_u+;5+6AbQ!Uk*fkdBP+Ha&HEHYIW*#Zf$U15gG&ko`K+b zfg{tB%U#}~ewId!YiTD`K=3Yf@*flY8T8{7RUhxc@@*JQ!2ZG>UcJv^oJLyIH!U^f zLG`gnil%Ut;`R;XI4`-cMLb=8NXn;8*BEl zsidnB$Id?f#!+^=_7Snin~|9q&VwKJ$RSu)P0XxoEh_9q)SeIG%XW=PAB67nD~+*q z*%Wa>l?0f`CYg_yZ$au1^P=cz{=~di9e^Ox;{jm?5XiTk|B;_?~NVc5RTre&E;cqNu#dZp|_JU)CEu{%x1; zR!BzQPdQPLc;2|)^BrDN@8rGN=kp?O8O&WGz_Tx`_A=IghT=l50?eynGHhRlzY`N_ zM%{m(2TZL`Tb&i&hq+n)o^aTQx=v_DfJvEp^&Hu>Z>bw=IGAlbi7VX{hZ3(Ci@pkv3E{b*0Y_^xZ>PkRNaWe8yeEvY zI7wc^97}TL%T;4ePS1_F@C@b-TR0CkgB}Hfmhx0G;yNpsRL>kusvU#z z7Ybn7YPOR(p5a&~$N6#GS#EasDUKFdy`pN!s5vv#O@;7%7jWj1rD`g<4I{kgIT%!% zMT2*qV#XOtX9&;_It!F_9aU6kOdRi!4&Pz?mx#gV_7@-t#`wIj8^k3Gonn2mPTk# zm&esgQ5V5he~Q}Phsz>Y$$Xk8@*h%;A#rPz6#0q1CG+xOdu`X9l%foaPM~CNah%fT zcB*;JWXu0qGYnbJk}@;Os7Qx<={%oCj!+(daS&*vz(3ps9EZI6@|i^;Xii?s=~C4 z-04*wWHw$R+lIlVOMlC#e25J3uZQIROP*vM#PiSsjp@50+_6*IVAF&Renjly+KkI0 zt@2R-9c0pZhIaP6`KnTqv-wbjB;W+80E0ar)+*d@UC(*Hz9VyPgx4`MGfho`3e9cx zl3)D*!;ksswN#wQ*-uG~x$I*NCCxXJD|43gN-<&#AoQfa3H^~6^SH`zDmmiVuhoNl z%l@JyLw0;7P{a9_4GGc6XNyER!{6%VEs;v^?H9dgr{zyu597&`9GKc{%L(qUCU_&< zS-AOw1=8fW@WQGWJ^D@m!UkJ0>_% zSGF7Y>W=Alvb)*<@%|F8e21$NID-qj1*Ea~c`xSI^tP-u$8@uOBhKsq%?)Dn&F1$U ziviP%KTZM-v>81f3iSj$t@+GFL=X25gkb*{LU5R$qmPnJI9~uWi=nlU zjr2ATf?_KFGZ*4Qu+^_zkDI=63|{!XURFj1S;f>Yb+}*6a)mE%Puf(Z?cgKb>blVY zsArc14QO*dQpv{?Uclh^hmsTZQWzX>BK~Wo{1?kJr%%O~?Q;Jmhf5gT#=7DQFOyZAmX7=Bp3j`L2D04`iO7UVwi~{l)UYkw9pq@Rh@>(|RKIFrdzBGayYCt+ zCEwp#)~OzFy*bNbf8jnqr&!J4p78_^^2c zSWxUqzo(=n)ysK1#I5XHa73&0F= z@+j^i&OtT@bYGR)Y`XIc(GI4Pqaoi>Ax1W6%Y1%K_skEJ;q3TP)1H$KagpP-L*%WL_2cQm zi=FeR*Mrw%)+c~bKS~vYeo_-GGAVJ1%PyA!^fPn{qVDFo^8YhM!En@PYhcX!*#fh2 zc28+P?Kev)^0*8}tJ|?YxQDu#V&o3h1@jn==vncd(>56B{vbQKAH#lrhz0*kM)@7j z$yCKLcsRiBv&={k(p0~S<@REud%(KTgHgT*o#ob!`w;t{8bo(l7KeA~xLS=xDiMqNRlv823B7DV3i^=~0Kb4XD_HOFjN!gX5 zOG#@X!v7)#CN{IlRaWnBe{Da9EVX2QdcDh|ay#lOnN^~XC))t&57AOr7IiLHprxsg}S((R#R6Jlr)AMlUB*&DaRV;JG7cM+o!Aw z`aj-8*6pxM4e)YPvcuKct^;YHHJekEug#yKiRqM%N?wWK8qml-E4X1x{pzPzkx>mH zZIhqPM7wU(n4*}LhcX6B`A>2HBHeM$u){L44St3S)*#wW()LRO-xkbHAs`oE)KZ^g2@&1xeMg(xvkGg2FLd&_7 zH|NUXok|2sKomPvyD`SHOS~{g0}m-Uja!7KX=f}caVLTbkbCh@Ow46R;YAo zCHQ0Nu9F>7{Ypg=BGzD|kj+c&Ah^kYOqk)dq-D93ze6^hTHgL9gQRzUYi;ztCm(hW z9x$lbbbKz8SL9Evpa2x!9G#<_r?^Wx>?lzcp}8&dEC#F1jgsvbsRd8 z%7p<-H`A~VcD<+=fpIm2Y;=Oj_pZo053(6AUJG}T&NT6^8bPE7;)8Sj-ZxQkN%zfv zP@!Ze6Cyp{+~RTSnGup<^B(Y1KdbqE3!FFL;wlbGw{4e0dSsi)g zhnL}3Wve3MZO*qWj&O1uF`>x4A_F=lhV!nW`l|1ffrQ|r(gHtX2@rr!9Fa=|j9TWm z$oVTr?^k9G(sv1hTB}t|T*$p;7s(hl$|i3kmT3H`5*Qa8+A`hoDbaP-noQYvt?%&< z*Gb}4vlBx0ewjhuk&B-=TkH0^yxTz|RZ~~%oIA%crCupK@ngl`gw2b}8})9_RNM&J zu&HyKP;U6#hCiq2yVW0}_QIQS>wpgV1nR_5s&2Gv^M)8ubofKuLCeWFGztz>&ruc{ z!!-^F_;v0j1M5*U16dh0gVNPs`^@BVDjtxdlo&`PlgfutS@1H@D$AJvjNiYeUT-Sa zb6rZw@q58SuJMDsN&4@=Qu7%sHB2TrXc=2>F6!04+i4MuZpH*)onRrv8q7pId;#Bs z7_q$f7$SZS(+5wfu$bGhBKA7R5tn!)XT4mBK|24B_6gNn@PU#j{94qiRK0p?U7y1t zX@?3f0PQd3;%CoSp=@f2sAK*g1q-gc7Fs{Lb`=(z{tV zJ`Q^)jZHtOdUsDde!bgjYU0Vk)9UX%gQ{;;?v$q$sg6_5kju0|HMgXHSZ8kHC)sC> z0|YLwB|F~-Sx|E`blFUlLD=wY67`kiW|FZ#JSRD1e)Vmko?2mF;v8Eb$;|rCOr_h# zSOzV~3#J9y%@Ls(!=U+Z)G(q0p$6_V<7@erhea0yksO<5mfQO_sj!Y8?JxgcPrXFO zoSBgT(;d9#Lb!tsH44P@sn8aNxU(L7$2l5|_+6RNgApxyFzN+^(R)-RA6(uU@*<2k zKX~@{hUx&k2r!Hn!5848H%6c3S{5rK3%EGqRYmv$@?SV<%S6XZwNS+21NmF`vZWIE zAAbtGJ@b1th2gsvdT^KZV;sHE2J?txi0GS>5|<@6Lb2_#T#Hz>HsZpnxmHTniBksb zDp>>P5^yXa*>lgR?~A%{H(sI1zLGI%^)TL`kM~kNaw)`Ycgke{IfWnRn#@z_DL$tZ zLv`T_8vGfNR9E5DVak=D9|2|kb$s~7o#`}pRIJO>F1}!C6v^X|#U2Bz%ola_VI@P3Nwo_RHwl{{$e|J+4@w-(5msfP@4Y}VLp5fCSEt$PB zwn}D+^}wKkMeQj`y1#?W6+Jl_v`=umL6cZp;LWkvVVB*?SG1AhnmudJ$n8dSTvsgY z{#av70yp0REoBp}jDk%<9aw5?4++pU9QZKXK9@F`02( zf9D8%WA;hdn(q;}-FegPFkKX{gz?b#sTIbh@2viE77FO}Y5_(-A&&oZCAE|oIdhLE zkqQ?>8tPT@D`r`JkC5O`r$W5#sKeFeXf`?ZV`?P1s)-jDEA zwn0GGXX*-kAN7WCwlH;mbTeM4Yql{HZTW1G9Ba8CPI+NW>EM<`!4tyq}*Sx=7zXGxPi!d~+k6zPO{mM^- zyjT`{zH0*~=PKjoshVTC`=maHd+HZp2&QVv_K45t4Ldsx`m0RJ_7fj+{opk;B8=%` zDvf!09OK^(Mf=6pvF;=&IB^>FcR1 zjW2O~D^F)rOC?ch6VZA{NmQnq^__}w8o3f7U-IlLoyOKTj&-Z=NeOFh%Nv3u?0uno zLixMov)4e|l^~F_zxlI9A1N6;V$`$d3c0x+|9iKZDV&>#unN81)PoQf=34)(YQr4fE(C*9A{0~F{ z`Y+^iI5e(6)XBS$6%t8%-!Mjy{Pp+1-I`FIB5C1GSWi?Eq`+? zpN+!c|1apM?g>D=KWTL4xX+<&bW1*RM&{(NcFYah@dmx;Cn`krf574Ys~LNUApcr_ z0$L``LOcBm_>a6scwv9ILtEi~)i)*L^=*G+;%Y3a@^s#T3I+P6OO5=z^lU#b!InR; zNs{?~3xi5`ms*0vM*~|e5;!<(YiRL4w8ry- zx23wQzk{}h4^BDj9-JGIJD4T=)5;h}*RM*X`OZr(rY@Rlb#%BNPgfI>yAk;bsq$oZ zI@S2hy$|w~1&;d&IW$53bK)xDro=z%_kUFK45*Upbz)_x2Bl?e7-O7+yFB;?8mM6U zDvQ^3z#ClgW%Z%;Uu)6lzE7clxPxK%ULqujnI-K1ZqNt$5X{f=TmdgptaR!*xR8(C z|I-9IuCp`_85u79HZoAggo2iDq{Co`_5_;E=cUF*W@S@q|1yBK&lqJikW*jcKFbXq zVA!BRB2={o)`xEHH3jBLNx+M25WkQ|UrI&vn=S4{N-U!wPw*PY<|^8;_JKMFT} zsQjuWn^qWhkUl+dan9(Yp)ryWOCVb*JOnm9<@({z&1jw;O5U7st;>2mryynLpJkhj zrUc-q6;!@0@Ok%MRfW{@KXKGKv}(tHq}tKAm%E*&A2qm_kM!iwm;zTfr)bE5Z#oFI z`nAK|O3k%qV)&_r0AU+y6us9CkEB zw)%Tg;(!a7VmRFkY)4)5Kd%PDk(v3Y^Cst2h$cm2)jr7WI0$YuvrYXE1%7lMB#>?=&g zt_DHgkSRL#8vCHy7s}O8z5_PmY!oS;tC#5k=+0Q3sgY)VKV;Qoly}}>f7A2gm zdcRQb`KQ9VnDW-n^2|127+HJHCY6Xba4zfw2uO(iM`+sr|29@NpN9h z?@;;6Vfe@kTj~@)3Eo`P9}4Y}Hoyq)0>aNjApAgM^`AZ#!8SslgBhZQKwqd%liQ3P zrw^vXE>Drq8kwrzToZMIukyjWm#eSHUHVB8mtyrk0XVwh!{QXNUq4~&gb0kCFx{YY z-98CAz)=BmdR)6b(KQ#u2amJoKjfI5{C9btGv#77y-j{gJrp41BU?m6H8rpko`w!^ z@|wl+AX|j_iKmd{1C{48rBEQHm&oZmI8xwq-ZIFOSzLTf{P5*1p%d(d_pPOOcOCIJ zWC}h>$ER`T901#X_J%QitV$}qe2I`D*U**c3N?}k3Z25&;;%~@H@SN4=bxu%(>hsW z#N}zJF759yIZIk>lsEM5V_N2$1f=8vS-Q2Wmo?S$wQSIZqi2X1URVLHD6N|yGUBv1rP5ww%4Aga&Miq$omSv%i~wS8)vnxh1-sIZ4n^87`Z|hh@kXt$k>F!NxDA%fMJA zASuCXQqQUFf>qf)fnv+U^QvEd{X1yCzB}aCZ{8*x&q3=xlOLRwAeT~!E0;Yi4s$M- zlUu!MEQFS56CMWOX_$U2aw+QWd*5K;pa&#cfi$$52iYX5b%qwQleIgk8G-~Kw^NS= zDidy`vPcsDqx0&zOw_Z0GiSQnep8Zr5ou?anD`z`HHkzrd}=^`1N9navL7VU%82BM zEBCwOQ=&o@Wq%64s+XT3cWXM%EwBPuL$4Lp%5QK8tn66<(iX*l> zR)o;nU5&{snoI6jnP5RG(5e_vfpUQgRO!vM$nkH2b+-*I+=8`u)eF}ZtWv#61^9BA%c1iDy*@R~b&Xy*%8F@%?`1Ek=Y7 zE_TQ*)kJW`NBD7YgE=cFp%*%Okx`R4e7R>4nmRX>Qb89@vt=EwA7^|U9JnGD7` z1HCsA4kR*4KZfBxvn?U8un~G+7GA7bc+f5~CF!3qkKDUBSNkbqfU$WZSV@I2yKgKO zNrkKh0W|^vEuZ-r1~S%rmCTfD$aUeWOei+KO?QN`XC-<~0tI?@oh2#K3|weXi1PVC zf%f3Jvef!9k|*~|921mUu*KOd27=UgbxltG;8PlUhD6Z*m+UC zvZHg!1N)hTo+cZ zvE-3y6LIh!?{FnJw@&St+X9G&Ucs90b2UC}+leD6!aCQE7VnGb*-Qrr*^#Rfz>XU3 z@uacC*5)^x=1IqR7^NT&Be$iGX3lPTETJoPf13@tYq*+(zUZidE;^hs7adWzF#|T;_EEgE90(;zFJakV z3Q>I1=DLV-o)!m8I|OV)>LEtpbp}_@XwyH&!?*|jsej^3eMLxf$k&=N zL;S@<{#~-e4f%3a*njho$XIP{1lQ_wQnYz7$ym&pORBY8J79F|_}QgvP1YA7`*L_1 z%~HC_v2x$_@5J#pE?NAH*j_Ds;HOXh)#--FJsW|(jVBpYyJoa2T)a%uZWM zQ5ssQ&HuEUa^tdMdT^P1RNckJ;G$Fdvi4WMZEz16x@>AE^I`W=l@{rWN7N{0@e{3n z?5m>cKAf<*t-fD|A*t03Ps;bIFlB%PkV)WyoW6fDXZ$LU>E}$0zOTB#yq8J^JUHQy zW(bZc9~C3xH_Aujf1!}4T};w1AKZR1 zx_Cb{-1c)|*FI1pDKF_xj00)0PEcRF0qHZS$_R zqq=>x9sjjE0h{C1_#L4{#vlyp;(1m{!DK#G{I%9sUbFF;KK&_~j(K|h?+^F3_KP#H zACNbu!&5$yGq*7bab!^od1a*x-02I`tSTi?@$)UPe3EHA-#Gd6IK!-DtXIew`>}`? z62sYf<7gN9<(c*4D{sw_+FH#bHSwPkZ^#Yfl`+eKD3`_B$v+;Kx|+;~)xQ0h5~+wbNOb}OSV=;fKLbYg7pUmfpm(Ucx=fj#Qk8b?6P`skY~Dr81@xS0QU+^^;s+;Y39pb% z_d%Bf?QSJ%C?<}Wh9ys5Wkw&=3D%T4A1+Xvyk-Q5;mDYKNrS=bKa!yG9cfOdi~Hp) zcfLAB6Hqum@*Vdd#H!%{3I!+}Bo{(~-^hvT^4Z@261cE_PjNL|U& zmO$AVhEz`Iph5oL7p@>cQra9h8Yz|RDgdgdN^!A{-TcWo1uQ{p%P*9OS~?Ad^NIQY z<|v(siYH%f2bq=HKqI)z-y`_r&$IXlU%2%keISUfqzZ1Ml|X12}0=EkXsT zj|B7^o==3x>{u9Iu}Zf?;5J7$8$|A8rCo%LDvF zFRKwOjwjWoEYp0hnk@c6W=|ls8|uE)QmIX7VW`Sunun)|mHJxj+VJchOs5v!zb`e-Jj~5t|1)^`D|i!JE^pMQdibmlC;N0UbA_M1n_M2vl)_%0qNkiO z)mUjtS}%3CmB6LUKKe#`90zBJ`vFV=%Op~13cYPd^sYg8(i@V*vatIebsV3tJu2`h7-CZ$Yq_`8)ACt@&?=*lOzD z?WaVP4#9HSQ&=uD`IlS7SYsUoSNIURr19itDYvqrE{KrnDvR)05OC!v#!ZH^(e@^{Ogy+Q$A ziwX1_tL1WQ(b9A5lJ3gBABbYzsH)C&Q7QQNb88xJTTrEt9NekoMpF8nVJ?<5FHU-( z+N+mCi1!dncY2}nm;^7WIf0LmAaePn1tK{8Ec~8VN1ngbNp&Bnqk-db9mZ59b{3#r zVU#^Y;Ac|*#bZOpbrqo2MD8+dS)z@NK%WrFx7Mg$1Y2@J=vzK$`;{z`m);k)vy!XQ zTiRQ^|G8U>LqGxeke8(lpJTfASBU7Zsjb*d3P{*oYav3ob_cd4Oz0gl(n>!lTp`eX zf&_r>CBo9s)V3`ApF1Q^%FK?xN^fDe(2a4TiM)J15K+;}PXxvmRmyc5=>^ zY`s?N{Ef&&*ry>2cA`V$TLe=YJg;&SS%-bG5B4-)Q^Th!$)rS>C`5pRPGmLbfL94^ z-OyO>s5Ql~@bw?e2pR|Kvs*N1Bd&qjE_7JM={cZ*K6$?!laC~`EZ09@h>GS~sbdL# zSqVe`#4=D?em|NE_g(m4_kqC5+C|164r;q&O)eP&4sxi;9C9pInSp0DO`Z4~)SaV) zec1H=__#k>8IKy%69Cp5GC^{pVLfz4?9)89#4j5LuzJ^`e{23MiEW)?Gt7?^D0jnP zR`wmJ4T}rp1lk;&5esijgWU~E3ulyGc0~LB;v>m==2z1S_>M=a*Z+BD3$nP=a-!^w z&n1)lnIPiQz6%%q_grFgO0h&E$QZfUBJQ;9v3;9=Psc0mTY!;2C8Dnb@d?jRwOw%` zppzK-lU{wY6@35J+j2yjf})iDh{P-%b7pU($;y+FbHjT#xjxv)(+RCF>l74-Mysie zEVLy1Vh32qj`I?nyJqFnIp?ab8JJ^9O`hNu3ZN_dS@tOVgbY1u9I=JzJw}F?C?@Pl zTI!gg1^fkPb()q(8`jtL2JvO%^e{`zSY84347nH&L)DAgzf8}yNag$JETlyBH#Ts@ z_es5DrMPGB$`&nokNqY-ItFo^xxMg$0t1%sg^BhTNK-rDe7J#z8^L?ptIVBas3-&s zR(JIH=lWAgu2B68H6f-jh*o(u(C$0tke|NY6tSiZ^^IKtePb{v4-Id3cX9O*LbyM? ziLEGe{!gLwXT%zzAw}ATJ0(hU`BZ8a?+BTg3HE~9-YgvaMX#ulq8pUn7g8dSB3 zX4IO(#Vg!LUgfZm76XD0uv7}WlwD{X0h;0DIa!@N*f#hwC!oJ@lYqkhAr8N1)(uAU zmTFJ02z+S*t6_J2`w1Yrc+&om&|b&+K5>oj3-6wEdAer@b@G~YBOhXedB?J1J9?a0 zfRUXj?{}SAE~n-j;>L^``pMiG%T3cg&H@`8UVi$-=K;+>mKFykzp68&D7^B_dVP;w3G-WC)5rXxM{OHgFXCo0wSMf65|MXnDs;X`k)6*B>`mT@i5E}C_jeb+WMSf^u#`XE0d)^|)5g(}3LhNlH8Xm)% zT${eF^`Gl_ppt0;SjVfwM!}2S@Hy0M1Vy#~ZC>(3n~@HPw75Y*wZWhav{PLS*0hPo z7=J3WF`sswhx<-!b!?44*^d97bY^JX84w3OWI%o4gNsgJxJaev0#$e6=DE11Qpb*; z9vu>)(*9+G>LHt=KyT#N4i6?M{~W)~$TsD@8(V?NG17=%nwJ#yX#6 z3huJB2d}9{TEqjcPG34k8+g}-eT>R;JFGr=5$ju~fRDCBak`Qw8`GI0K5u7H$y<7% zDo+O|R8}@LmVY4Mnj{hE-4hedy^)j{y+;uk<;@gne$(BkpTv-+ zr{FQoDv?m5#}bsBc(J@EIsK7^YQDavf&yLoHELKn=lKaky;oZb!+Y2MIk%MiC} z{-m!+0p5J!AFqC89r!QZA z`EFbk3GsuXAk#Kcog9wJQ;?p^*^nEenWmxy>C|6IO-Mf52=XxF|MdMnwIyzKu`;$ur+SPeZMM9w;a&Q)w0D0qdy`XG z+y^%nx4l(*e7>}9FkTW*=HyHvlXzHhT0V_r&BIzto?h=`pS*JT^)b07`?Q)9Ys6SO zsldyBxbnn#;5Mg7*6m6}U3?r*4P-f@Crsew4}g_wnQ;byFa%SEMD2Nc;18*T7rq`# zSk&62s-pE62=d=jtofThb0I?doIe7oNd965N&X22HYd{YTJ43~BH#Pd0xh2dvHkwb zS>~&s0DeXg(8+vT1-RUn?{0%s6cplb?Bk!`yzXDf-fq9(k1QK(%s%^o?}DMa0M@hvG9Qo( zz5mCj{wS1mAj19qx{uIg4;eo_P4zTW^J!{ic$s*_Hq{LW$o51Ft_r6HJ_NE3WYzIG zL4B4q;_L&Gx+2A1mq_#?>xY&3YVi?PFQ5JNJ>Ten>3oH;4iR{Jl4=z9O@5&kext=l zmelLr+6~8N9FJ)^Nzk2f4;YNuvUxRFkre81@o}=#GOdPg;%Fx8;v+LvDK+2A*^Mfx z*5-59I`XW$i)Jq4$e0nQg7!G@%Qx94fL}h93VQj2KmN9BfPuzRly9o(mNNzdyj)uv z80S%-5?ijy`FSx53@pkOR6jt$W246`XVA8p?wjTURmdc@Zc!YoqA=qAj(YQ>l8=X< z{MDog_>2D&^~GPHB;dZ&8xAe(-3&C|#q~Sb5|V$7dtIXC?n5X(&DwKdE0C=P@S+?)xXyAP~{5jZ)=*jC&rd*q$e9+tb7& z_C@xR73Q8iN{XN$&a;mOlPw^nrnaC=mgt&dTGAz`(CSVF*Jn62j!j$F=7Yy-HJCZO zazTmHl%V}M)Eyeq?clwA{MZ|A*i(5JVgx8OuAT}C#}eb3m$X`XgB{*o;ydD-eo<*O zbtHf$r5!jIE-9K(o-lX2th0XI%Yw_g2eTlS2al61zb8#Qms#@-?v38zhV7X$GPmi< z=1M~);EdpBhfJEkDiCSc!fzgIAPr>dHkWZ{vQ{*{q+KYbYgtOf(uc?Y#N#)s#8=n0 zno4Ym!3PTGn!_OR^-?O8GVKW&WT(%;vNi}UYcy-mq`P1CT@_2bsKYmK;D@FFKlDSd z^L_TBDx0^H)q0NKx`2&&9DJrW}$OuZNsW!79RVf;*`xCj+sjy0dnhmdT0TEvjb*Qz_k>2VA)k0eBuPHDs7hH zph=YT1N-dJij{*rT*z#y3$lXo%PaoDg%L0Rgd6ixA0D55$$chs%1jDbDmM#x-FBQM zljiOqnPI^wV<{@Gs#pNbVL}SikP^cKjv@8`{pP-E`|hf`g8Z)}dG}*)gpYWkX(xW) zsU!W}J@Rt<&fc=Pt4~@h-sv{*;~p0xZ~YbGOgI)TP)~g0wKjnThL%#aNsMoy>qc%b ziOIP4%;jx={TPjD#hp*<>^%t%7fL#29v8E8XPC`ah9&#HI&P})B1!I!0FRQoCHVWL zCNU7~h@4RLyu+IxDf-k6$m`JNw&u0B@d1G-^7IOt$41~`tZU};TJWK@)!cF2;t?5A zb0-fS2jxsu;IVcCs?wpC!298OD`EtXQdDvS$p}&bVT|~!0^{w+!Fyd!jxKWo#BK^Q zio$rUL5qCPK7$%~5x_QX{b~;P=>z(vC>Hs)P6wzuiQ0rS z&TLtn&sR91D$(l1J`w}=u(6cz3|r8*qP;*?itckp$8K<22+qKc=mv)8|SYUW!9XbsUK@ZZu(Jdf7@H8 zr(rV|WF&A^-c*LZ$%36o?CoHS8$puX3~#5yxgtWN99(wfZ}B9D%r|T|$5#~ca`o5W`#HvyCV$z zAr$bsGPXDpZH01SCM{KNRAPskWB0BFKv`O}z7(d-}6q4yoFtQS#CoxgTcL>1T%$%ABA=^j*va+)@&HU59oBO9>|>#@I-!0e0281q;5_w`tO-a04S*UF)@Bz(|z3|o=2oK4EQre&dFrr z0DeEocrvHW<3LDf210spT54^VKMR>TY>?*9DF7LD=#_ma=vS)1mkm1H!^d42{(5US zM;9G+%jlwk5iT<+t?hpNTB5n>VRTY9boP-W%s)6+9>E{}pA#8WbtfhgAq!1p_Ja%W z=xxQ ze&C>_K7B>{6P8{qk4i6fsUs^fo;h{Yr{b*IsQWfgTK3@w)q#A+CmYpEC{Ss&x%yt# z^80ijgNNs}?obSC7JXs=gc)KqX%zmh2KK&d_q}ft+F4+U77Qdw9=yAOg980|e-*_F zxIB<=cs)^nx2dDP9ruwcWr(!_p)X~cT?uo|MvFKe#?x2d@8EIbsFwYHzVpK3EI0gx zmXGnh)YDwBTV#)Lv=Sd--~AXFl4JI4Po%VaeaCH>J;?2z1Isp_i0aJa-)!AK9C&** zP=D!yodx`kWcT_6W~d!@WJX=nHb@PoDpXqY0;e%PYaM&%@3p7}#YwD#zsgx<7jRQ~h8^?h+suagPpSx88)hfjGb!N(Uw$>t66nuU6PvaTrk! z`-l)d3S`tn7}S`|vH5zn%MS+3vC@Y@-)!UJEG96G$tz1}R&2Zcl)puknO?(Q;k zY)MG2^{)BIg_*&)wqAUvd--9%2#5-)#jseq$1k2=PS%mdd*W;KVfk8rRJujsJ?vby zSvooVWM<)?Fy0!bw$_A=fNkh&+(IMF8!NUuY>3B@rOH zpL{60qD(Vbb^9Mnn8aK8Mc<&rWY-M@fpu78=o8X7K%QWZPzeNl+<8?1an&CcJ7llIteB>7IubOdNEq>kG~nt;i}fG#^nC0IOenBVQn||fsOM1?3Tp!mBGZe-WA<+ zdORBfN^QaRRN*{N5Sh3X%TVuX;_lo&Xtl187Fw4!k-Q{!vpLjXr zDoN@k@Hnt~UYevVh_4>)4mFyht&4Q{egMWaS-K3}`>KdQ59<~2#Gp=y2DA@X7bxCb zluF~av@VJQ=wkj8O+~Jbv2}z(A6sFB%v{Oi^ONBEEYv5&k;myF;9odShtY)fO~j)` za~=IOw9inW={KHvvQ{09wMalv^jvR?mH`ckOHHU~HS|#E6@`}yD`r#x>PJWmOjmr?ToJWQm--d_O;)-9_bjZD#!6q(Pn@CvwoeUGJDPQ zr^HnVu&5Y%!qqE0^V*PP^6D=)0}l*|cV~b?CvwxA6}89(nxb4c@Y^E$k9GZiO>y2< zO`K0z9{w)uxb9OlP+r@DhxAk9$|@i%_V@JCVI&ai2>3-ZV>hIBDbZr~K*A&6Q z-s4<3($sUH(&^JTMkCM_8UFvlSlEjb+q;=)T03g@T zOt(^#F}b809`8xw@yGUf*#yHktT)qtn&Wa4i@h&ZDk%jn;=bP{T|Q-|`kDPRMdbo< znQohD|Fz8vLaG+)R-vl*lY$E-NKfF^-JcF96GZx(+4 z90DyC#PTYS*}5~foDd0?yAW0nGNZt$1K$fzpCD)}&z*e&HUuAJ7axeGG>rrdgW14# zB>yq@LcuBCaJiPP(O3s5>xt8*hz_!TZsy_o_f{4LW6)24!xGmfR9cPhJgz{sQ$ZUbXr~$iwbfQ1UP2q7 zZV+k#%sZ;uRB69%u1i$B^7&Qk5lNN_D%#X;Khf*$bUrf`EHAf)SRrvf@nneIFoSjX zQh@7rktD3rnNPx&%qy34gaYiyxni70GPn!mO}@f&pcSAW17jilnc#5q^St_X4qgp< z%kwMMm0~LbA%#vDgK@WSn56rX$_m1wPi(R7wu)k24{sOdY4P@{$xU6cq+9Gg!=S8l zVfS;iLlNig&y+P!!27@#ybqp57Dd7D1C>-(nvWRvRy2;*+7~hBaAG@M@j}bnO}_6| zxfxN_ZiGIf?*llSB`!2I+|&jD7THtpR84!!;3h6mD#)o zTo(Z?kkqp;coXZvDGD0+6vgaCJEHqQ3XqPS+;Hbj^g}%hkVU)ORg3m$aerU}qwdYX zjt+4uVC*Joq0lkFr2h3ZjE#LQ|DlH%E5|{C2J?87Mz07e=T@)7;r_eE$s^hHoQ(2{ z+^P4nCvkNOP9226JF72TmHjzJJ0^-e^yna)X1J(_mHGt|*5*+PPeA+a#s-@uas-4$ z!xA1eIkL{!Hu&-J6_gbNZW8}D10_h|zy{rjG-o|pvIURB)GWLys&5ZhujA2Nay#Ku z&>b~)%y-rb6+ zO7|Zv#*_iEbEXNbtlAsm)bC5u`OCojnc+PG$GC3#Olf_Hxy3_7b|cl2$M4y0^BmuOZrS35d1{;rlBFS zv??)(?TcOBQ(t1F&_Ebt#dd^Hi3$r=QhPD8$+f$T>N!49V&O^`ke?2u~#(5R3oHsQ0e4RyOb;W9m+K>B_Z#Nd=1J82`_nAmfzqR_FayZ=;Nvx#792S@4nq6^1IKXdvKDD+ zp$h-|uFm#YBJc0jCft<6`*B4W+0gUEzca*2JzL8s!b}TFeft|-E;cpj(?G2dX;J7m z!cgK(uHld%;giulh3lnYiFgGs5uw~bI4VO_`8D+^0r@w$0>JYKr*AILcyC0c-&V`U zQ4GK9yN>nrs%r0*Z&;azF5|5lDzbU?4V-87bj$T8FU<3fQuapZKYbu{GHpFhwPaZ_ zc1P~9Q3)6k=AaQFp`e}=leNwQj0h$d1tmhjxTrWQgB};SuOVjB0k-thN;#K3EuAN3`%YE}! zDhXF)c)sHd%5$UWJ;ThjPqXe957eC;TaP<0aUIFh98mNt_i;gyV*MsKoX~W?uLqp# z{NKyOstDAE2DD<)r?orRK%E-ku;2u%3uu1XYdPIVhy^&F=JFDqJ}(Z##yE*k+|Cg; z5~!%@>!d5((}bbSmh^f5&;^(oM97&W8mVw6t2>bHEm#eB3pv>GU=;3>y!-KYaF$e^;<`(j0}l|5x9zNodw?BSP;2~?fDAS z=n#Kb0(v>LSQ4sR%k>V2v8#xls@_bTzdh4ZQM?w+lCd#`Y1n!{{40!HGtgy zMs*wqi!kuKz?uW}WB?)`U|M62cqk&@$hFfFFz^5J_CtOpp(J|syl@jP4F!cU-?mZBob-fUz`UQvK<43Y?!d+C#+H`s>R+pcbJPz zbFmQpE8=Wt8^W=;6NQTjZrdEf`fL!ak6JXXcKk2Rp?V2^3g-1>7RskWEd3S{6tm}^rSr1I&SoN4uJK&PA`_>=n&2#UJ~bl+e8{dbstc2LOQn3q#;z&$S zD>}T($G)jp1TOwFYbTZA~PeBRy z36MW{o+FG(#RkcvJ2Odx9Y;Vc_R}eucm!%4 z6L4oM@%eb~Kz}sxhvMzO$(pJr3vxjqNPt^7u}Z9pLhAEc00!*2w2E~;K_l6}{8rq{6Q23WG9!C+}n#l)+aD8_9 zG0{)O3uTzDQYctm-oE~B?Km8hZUxXLVE>At8f3u!l`!sxDEU2f!;qkYE(Aco{c7xA z>t(SSiroiAHIG11P12Uj{f?qL%6<D=@A3 zZ|ecfk2Xu434#X2bcK7qsc*`>=pL- zi4R5fiilTN&D`_Jt#_r~-!~EU@q6H!E@!e4!*rDxyaC)M;39a98 zpgOKxPlRyI!K0lJMCJ=Dz_o`e;XaCo2A?GUCjy8rLI6@U6y5@=R+%jWuh+5SXE&zl?hS8CiR9bqy9V3&5H^3qX@0CDw zJaZZX0DYXFnG896zYS@QNn1u9b! zP1kjp2(H4oPOvoTCBfV>;t8mKr|?8T23n6P-T^l+b@Q{XKh)UPrFpjPp@%?-N6b5s zn5!g0>%*3w*$$AGpyc%r`vMDMkkf-s4bxKIf)>azN3cL*fCbWF4_vy_6>SSBx{@0b zk&@_rI4q{RG`nqm(nD~~cczZ4ipcdwHlResM)$t#U>4#RB2R*jvflgDCyFSrKPjd_ z3=Yp;N5eq$U~y25mOCOWz9{33a9#dOSpu|(x!TiXf6Wb7vahK3v_9BjY@>`rsxI(}XNDLlVPCVU^d~Yo+ z>W2kh@y~qiH=n&&i6r3&;|^V}%`>PKQ1vO=NY0U+mEKp9vz}|hs{!|>&V~&J2X5t# z4Ti||AbvC=Ko?L(n4J0dj<|K9Np=C}-|zWnK@KCX7!2I*bR|AI{PtE@=CI+2`ryy-g?qJ| z>a(&CAxW0gkP4T^mpNaxn#h~fJFBly``n=E$QQML+q-HY7Kbxd@J2H3M+<02r4RC8 z-0?m^oEH4sI+{#BAur*j{so!1IQ}2kx{ZZYAGr3&acpSOXsh;UNF}Fga*LjfN;^rz z&`>mX%4=u zqHB12?~VN}@j=d$rfU08&u^w-LQ_hV*gIhpFp|6S(|8^eHvQfLsRob^v8&JD8T2Xe))_cX7_H6j{FclgU6Qyr#pv#P6 zvIisJWd{z4&k$^&fnXi5v%9WSX+UJXEn?7)O*SC}?&;z>(YB#5;i2&OpOk|LHs;ur|_A zRN)Y#NT<-$q@hd$JsZbM^(igNpnB;i0_F7b94&@mPxYYkVZ;cFNN>!B4G0iab6JQ<+58-jzgz#%Az z3k9+5Iebr@4oEZzVsFGUw_7~OcpX;iKTydwJ6cT1N7Qzbwc;oO4&>$9_Ehob`-JG> zW^iR3g@+d2j{R;vZKi}<{-yo=G^Eg{iPbN(SE?^*`GT!oSXGiAu;-FHS z39qRxfGl50h72qw3h5)BiS+HRcs$M{Ox-ZUF$p3&TOaK~in~IES_?>VosMs+JEg_i zZ<2q#8xDUq9tUffOMsf#?B6}dW`+70?!UPFybu}oc2c{_X(oWqwNgeH_jh%|$XlJU zC%?@z0&-RRE;D{}>6l=xvuBy%qYVvdrw!l|cQBc9JOiXf6MUBcep}p*3g4NNWC{k2 z9UJ;s43NkR%!h*1Awz9gJYaDu1tB3+rBZnBG87h&baggUkV1&vn%jC@aWE2`;tiE+ zDFP=f{w*x!kGJ^HqbbCdq|*glME|)V3?p6K?|HQ1BbX$gCU44Thv~8ZbQnVMhf!Nj zp{{H_k>bFALO&C_b`XE0QK@uii5@z(5L?VWB@ubvic%%Du8s^{`TZlp@!%5f(FHLO zb$7F3XA;~1M2&;J?tL`ob`WW}ngf%DVTz#r?24oKWU{a45jl|!kVw_Oq{YP0Jq6@5 z#}TM{h0lCC49o-@Q#a!L<7$&-@wb_$kNk4fef0y3%ja zJ>&7G{O_2?JlzR?4XTN^KkYPK&706?+8R{d$*&Z70k{a!Y&X~7&pmiqIi;giKhg3X! zoGoj#1l`sO@S(-FQ%r%f2r{7CS`3|0bugw-Iho;(Jit{3Y&U+26@r-dXVje>#SX^2 zUg)enI^)~vkS9tuzFMLTZaUgr(;l#?1*ce{86ivGVp4|}!9|+t1x{O~TjO6Qp~TVS zP5ieDeds{~*U9au>(l`@gaV*4FE24{Tu)sAM_0d>koS?%V9v3Gi`+nx^Q0#qoI8q! z5VfYAcpp?Sgvm9M4xB=U0t5S^L8c-LNPxn)3QzSXb0>YajVbFyOe1q9#kV>_XtROS zlk|6q5L=tarivehIR5gux8!E(6SL5?|9(HzrN>(sdJ6au_lWe!GBo22&CO6TPY1u$XW=)FC`Mc#6) z=K=?KFeFJ8+;N^#+&goboTcy0azGweu+VAYV!~XRSsJ-fktYzr_NgFp&Wu<+$c|_1 zC)TP5e$|E-6d}}><9iTd)SN${kaN<+4p0hjlAtUB&0Bx>;&>nBB|G5+|)R{m0Tm;pm$M+=(8#9JatN0mZT=Hws&^g_ z%CG}B2K}DNw|gZujXNC^1kDZOUi^cz;8MEy+S-6dWw*2P9~O^Yly)>#_0SMNE9lyf z96H!L_(yOE6(pJ8#FSX^;&(}$qENwppN0hrxFkQOi}YmlnHcx%-|86S)B)`es|+_C z2o$Km?s&wXo?)A0pd5Z}*#^Ul25)%yR_uRW_;3EaQ^I@F<>R@h<$0Pq z*VRh5aC)e3MnRR(c^nS42ZrQ-PnLE~vY4QwoC?#}Dk*akYopK72bB>=eLk?)@L6VT z>+2wYIvx1a*nm}`+J~1;pBja91Q+QGf1Kvt`qoO)c~{)f%je9{mf)|6YJ66dIYQ?0`=i=y z_Rno^VZ5{%Yh`sV8qhcI`Cx#IRO{DT3brXB7$qQKK{JXi{u%8Lzus3XgsFCkpLuyP z3QTN<==g>_MI;i%%-nQsh1(81u1~_#Dh6z-?>Ix7W1l#S~Q=LK#;G3Z8Gf}xQL0MVKWkB98%@qygcK@^f1}=8SxFD z^jm7yKw_t;cW0ly+v|lETTMxe(L`Gp{0}RVlI9jxz_opl!S0D%!Dy|g{8JjNF{*e=c!l$4# z}R zPokrzcjzkwjL27g;bX!_sWxg}Q#BAPR113qZLr3e~g5~AFD9*}$?VzVqg4kOSP?VuKA;7%f- zj$JdLJ;6l-y55hvkQLXF`9I<@p#}7)2LY7lfF=A1pgdzEM1b?OycFK00^$xa^?m_; z@_<@+ny4&CJH|MrIDRV5{tdSDMJ{O7aCq#d!70}@+?X{Hb!>8wi$uT?yd~;ySxW7J z9vDBe1^vzTCHI8MC0%`-+DkV>ZF-MocXmF}wzM93d#YF=)sfIzS+$u)t0kF1sh93M zQpZoPT_22^0vRg4rskiU02TX~TYFG~dXf>8_n1t73Qt=$21nQd@dl)#3xzu%b_CSu z6Lje^Af0?wGCUfvEdXW+5$RF@UlYK#2#iErE)VmEn<)-Ot8jSZ*cv&)ydA31QM|rVmEpI{Q0E97iTNKA1(*^{ zN^PW_%6t1l4$r*(A@|tA_N2TTg?&mY1cPqt#V7r8+ffLK6h{gjsm;%3VZCmHIu{O} z4rT?{+#|$WG~21hc_$O))SCfc>Luzd#pi+xON6i)THT_dQ^nz$QLByrKaZNIG(`BC zX`aZ#I1QM2ydn!A*mD#Eyr4knG-X3=BQh3vR7Aj|GT8U1%x*k`J*tysTGNn zS5(F7aZ!T>E5ik&Gg%Xx>R=gtIDV_8cwFbnhS9Y?6>W3FEN(pKJo&Cor>dps-Jg5C z_WCW0rV={F*c%UnJFepiT4Wc!)aPeU(X`o>9%qI>B)SeJ8}WV=Q`?u1v)0|6z83X9 zjLl51(GihNjPcN02N>EyDiuA;c+ib_sqQLzx zcphvcqN4n+jIvL~ncHt8uQVdO7;U2GHIN9*tj?V|^jr?%-q`2ux5X zj-VJgAYhq80#+;}VBrfwS@rwRfw$4L54}t$YPX}P*t*1b8sPr;YSI#>1d4Lf*)p^N zn=EA!zMOUCa`N@Thee4Co}GR1-h~L zWCVoEYwzAcG*`Rg^u*J{wFRcL!BeZeLsLkJ_v`jM0gleP`1P$*p`6m3f_ad0(jAIa&L!U3Hv_fF!GA?z4aONaXIN}zgd$d zxemrmcXi8DU`*4l8(=)#U}@eFkrN}cY@Tnr99BFaI70h3<#KV)ffr#9U&4WZ{5ly} zkbuw@u7j+ch*LpSb(Q(bEgkmhluYS0rM<6M&sf9>!{m0A+B>*k>~wBd%iGLIS-6adseU!E{bpms zZlzTtd7L4?l+VzhyPR;%~7PHwUs_3BM20+S`( zXUM!1m5zF&FUmjU89xSnFr4;eVgC~TVnF!Y+&E539hz^sBuYX-be|-tqdF2t>{h44 z*&LL4X}keWzLrYN8EBckEjsKMZL`Bmoe3*xgu-wC+&>`yGKzg#cxq=%3vL$;REpK1 znOK-ifxa1`bQ6K6si7ul$A5a++PlL}AV&oYRlU&JL9|d6w;{faiLhhh=85G%UA7;9 z5*Mq7j|4hC363y>cB;YMo4g3+c4kx~70rGll~22nQ;Q{#se}Rc<_%P;Grne_ zT`HMWP@cWpm_c{B`JqJ2QzdM+5eoqoLs={#cEOS0ittp?)L8GNU z#3>yPvXu}at&;;Am$Xjlr*yi`#hD)G`v`Rznfx_WHWyS${)+y;whP*-P(z$P$zL=+ zA|7juY)+U^uLXNMs`1^bbF4ovn^^x0pZ5&kks9e>aMccdz9AyfCwel>u~QU8X#mxx z$6Wug4i3U6fhv<24_9!%f#3R0BapBkm*0Ce0HF-4WawinN9s#ZDumKrKGrjlg(9?z z9s99RF#;S|M?Vyxri^aOke7>sQsc(&ikN?S8s0I4zJTgmtA|d(t{n9CVzoeNSyc#_H=9{x79v++g#YIBlxM(b%Kc(osVZ}f=`F4A4ePr-^?({AbqT!*6 z)J0aEOz7nxn17S0b;)c0xk#PvXKI-gE|i$;k>ubMP!zm&nzSr*zunAJ}s?9 z`5_bserL@WcNg>9}rFvz05D~)6LN=PRAdpRJOIi)D{xbeAx7V z?i{XD`vCp)v>7XiS+9X>vyRH|R^00%vUM-?&_kYfO|cj>H0b0W=_);vvIId2F_GtQ z*%#uF650cnkc64EmGK?KEEA7hnFaV;D3+b%(8{X-dth{flpO9y-Y=f(z-a0+(e|sCK zO3D$AhdgXemr+M}!a(Q}ZFyh-+UwZ zcEmq=FQIwEMLvv@qMT)C?M&P4@0HuRD}&=1KQ}GhlZr-nSP_zC1N39*zW6tJHAXEd zz|tY6zsQqWcG8(3_|dC_!nf%eeVRMhhoV;?yTzG_~yS(@|A;Ad{Kwz&)&;HGgyt(2c5 ztNO8ig5%)#`7+m+MIc$?`d#BHYDlvRjRIxV{hTmEAC*?#`9=9>yYNdmi_7y_cG?RL zVIGW^0D{m#hE;*vmwr`LKhCY_FwvLNT*r{8x$A5`$sqL^G*QoS0tkX9C3dIOJIV<e_PcL191*?h_?loOdkw#gNTBQN!@)LYC zQ+0R_jq-GS!(jkMX2nBbSvuqc*!Pbb~SCn>r{+D?Ntn%cZN5IE2Wv(pE2%R4Ib%?$E8;f{2e4_`>Rh5YzdYuRC zTj@eL%)YgB(j5TOP?+Zbo*`d8*Dc~?h+-=!Y#xDfRdwE5*rbj^0C_5L$2$W$7eI0X zdpd$5Q8MasTHetq@}sMgnDODOKsL7bl-wQgt!R|zo$uKa(<~ejU1bW=3tt!2L3XaG zxNq!vWG-E^oE;tp5A|}m?#RoZ>gTlHSg{dOVCsd&WB)>F(Pdn>iF#3r)i!3*lhIfF$ZHbK25v@(&%03o{fe$(%0TG ze$%;g7I>OD@fP26;%g-2zKJoJ8-G@D)M5bWlwvndGuYw2(N1NkH;M4=goL{ zG2{lWc#qmIgvOEb+|bn+orOOF&t37)o(a$fs@7H^4p|IuZ=yd-qWX0W$zMV5=896ThRJ6dPb-N>4&CSr@uK^JXt79 zg~H?gp^Dq1`@<+H`dSdKI1;+t2W4m2;q1(5?77cN*{J6LjXw_gZCue5Uatgw3XAdp zbiC@sFyC}kd;~59=t}BvUj)bb^0>#U9!i1(xhwFwn53)VxX6_yom1=@qS#PaCOq?OP7mU4^AHm*%kIC?1@+&HFZy;`UJTKy}niU_?Yw}~AXgLABIJ96ML0$AgY1345-vD9)Bez0y}I3d8TD)!pBmr=y@k*jq0c~QG9v~@DNMhE zViKI73$puEe8f_jAXH+XZT9as0jS`C5;L;M3Ib$UZxg^@?y9rNB+*LCfuMD$C zmyXjKiD4PgbJ`c&V+|NAZ&s{a2@;Pf;-&pLSogN}~@G{J&+C4Rmo@7`obSYV(p z2lsIu#Iovyh>;UoN!2he$BLaO#R7KSDcV}5I(0~J%sX`}fUDPmk(*#(sB#oAD%=Y} zmLJNbiL4lVNb$iPKw#QnWV6*Nb3gVjJbVsL-}MBiUQeP8ZD(<(g6wg0JL2Q@pWv1s zgu~P1M6@y=WnR!_$V-i8l5pd7Fh{!!$jO|9HUaI0XwbMd=mPq9LQNU>`*?2i_CX{> zsp!#Kj&xK!CjJ&AKLc^GxE#^AMKA`s)Py{e?}(f-`NW?E20~{#$fx47tU?J)=f8_V zWw_scG%YoGdeDv6JVK5!a%dtNZB>DMj02y1&oK7EcL91Dpt*3*V|ACiuaf)iLd`>V z3%jtX9E21Op0D?D(n4|`DT%ATVMe;45&m!F>Nz9Amq7od=JT;?WxDWnMiGT|3~`p6 zp+G)`7h^0^lnn;tA4^YYWQIwTkXjs*J}&3zLnGfbtBA>Xr2Dt zcKeFXQn;VtZAXl3{M9oCx?HLCmuf9H#y##h@x^@|vrMmi&!&Am*f`}k#ujy>&$TH^ zgqMq+UGs`2y$}YHx}zKi2q4H*L7_ScMNDlbQ=)`y^D7_DcD5J}js4*|w<{uOB2Qou zsvmq)*@BkqlJLOD3ETu@qXN!SZTrG%&%}iQ`%cNn8dvNQuUInmn3zwH+en;fU}^TL zd)F;}ZuM#gPO-1X3Duzv4CNsg=H#7nLc1aS2ZYYzJzUl=NH0B-meJ9nDc&hhr$kie z`{86r7rmPBP9%12^<$4j+?;0WR(VX9V<(pUVc>)3$6sc&cPzwpYwFm~y!%D7hFL`~ z;HfH2`?&s<(l_)*lkqb$TKC{rIq@n z5Y{w}b3hd42Zxgp&UEe+l53d`9?zc8$Px{#)@W%eL>9_tSM<lx6 zDPAs9DNJOLrgfRj$>(NR_IF|OuM-cRui?#5V5RZnW}dcxW-k3~RBfinpT3m3&$C`F zE9=~yi`KN~E_CyzG?_mSHy#F!01uE~*Vp!LYRI3%`Ty8^&!8x`?cKMVC|Pn2q9g$U zr4J04w0DOMYB-O1;?q8&>D!y28p*M66#KwnkB8djDQNb{C zSVy-2yib{5Bx^v2HS-mmvYK}76WrcjFaCHa^F17X+qapi*qj`pPxHSm4@Pka< z7O$0xcviNK_N-2~NJ#c-Fo3G3DeJ_;Qf)J0!Do)rjly|39ZDqyVe)-{gI6;Wl^LJNCE zHkJ1|(x4$r@ieVa-sFfmx$|u?REtiz`*@wLWOA^s2F1w)Et+; zE_H`K)2T1^#}4ANGWP+K4|{2hu-WwdXb8h0yudqt zF-4aMd zQ=ltQY|3E%c8UFRtN#Zd5u#&x1oxI)F9}88qWb!j<*FvP;&x^|G4(3D<*>4awu*6 z^BT_NFHPd9=zcUm3;J7ajZvJKN>EIP7i|$7A16$!P$Hn!iz|0E^;2;=h zzUzw3AJkluCSk9N?NLHH@>mt6r)$%1;;k0z&OD|4WYF5f-hstFtiB_r)N#s2=~6|9 z!iK1kMhwF{iuOtPApwMl!1%?{d}S#srt)mAHPZ{ET-RbhtTbaeR!b!ty*F=1NStLa zi5GtQIrsyAqM*m)=?Q-_yOMw_l!Vk}Mc-Xa;KIJwOqncf-&75$x)1j$5UfAT&E?Z> zd|I2ZgNs~zx2$$%3Q=R%dx>(qI;7b_cH4_&M!z=v)XKRLT$r)U-uWAxD`1$!_IRjU zI(n!2!<5|XLZ^qiTXITy1CdKoMPDUkrjAH_oYlalG*_#}t`1T(KaKC#*1nuZTA8qE z!t|<{&2jiCYiEFBTq0gtPEEMB(vib*@dY0r^TcKOyfF$1^N1E+8*{Bp_Z4)sJswG34}-`O7ops$|~sN01x0PLopGzZX8p<9x9AV880K zc}o!zH<71byviC8o*ot73mYpMYq0Zg%JpXT^KL)?o)GpTZ8&b&fLXV28_R8`j7dxf zR0ixPR-3AD*_y%GUU&}CQhUs5f|^CLppc)2d17Uh4{5~c$cfCQ>Z7#72vKsvJL)NP zmbXOkN)BSR1Qd}K=lF|Tg!m8?U$d&`dE2~EM-bKTH;*tH@^yd1m~5QJ0q2iC9@^Q= zr+w`LctNrijmxlnX(*O70+$;exx@XOh@Y9tL6@Lq*j~p`ue~R=AMnC zB1GkWju8lveoMH1C#P4qlOtNa@kU@_{a1H2Ue*V*g(`oJrBsQ&7A?HAmDDas{?e^qAglktlzRp<9J>a5(2(P`tbK!$JbivFI+|SJM zoMV`g9U`d?YX5kR$7pFuypnY40&ijjJhsJ0gz=cVBZFVc#xZn#(0%H9DCTHn`5|k~ z<1%v9H$2|^8Gp{le{__Iax@#p!=wtkAVOiQ+Db$!Gl&feOL=>-dSJ)1750&sD;T+HR^9%PuvY zH)#?YZYY~Zde)`j^%97;_OD^VCg}uUFZ<&AF}(8RSM>_}u+I>1o;9Bh^fT z`V^0-zdacojycfFR!jIOxmQHT%CVe-*HOgbo_KcA)8wmx*q;(oF0azvY`XL&ie4_P z=-ZN19Nt^ShN-qff7T7qksijS%gy~xPY~Y$&j~A9UCO+^tuyu%LET!LMN{I>@N}W9 zO8{2))%|>+X2b%$i?qN3!kFn?uj;lwA9xtv+SL)kKC^5W!^<}Ee5Dt3WbQi){HO+R zmi?{b2idc=Y|pKy^)IG;72s`M^eWQD#?FblZj|uZ@!93DL~#kSSwDj^tW(Esnn)&- zByd{>icK3>ypTPv(RBF(g*^4S!Kk+iBIJ46N2+a*LQZ{w7pNl%9JtdurpeWU55Hau z+CmChfmMUT?9E@f+a>6~ML((+hJk93H>&*9Y(PZYG}llXT@4!66{ow~fLtRyoJub6 zseXJF7a8VXNRD7GJV(Lh7w?owGyd4pXuXL&T=34&K#wya`c1fYVFUkQ_?TGo{TI9q z*4e0#ouyj6M?F@Ch*SOLA8yOw=PZWkmU$DX@g<7L6GT)!=JefeICOk)jZ;bgyksbz z#Jt3vh1ZK-5^B=0`WT$0NTug++Do*~-t(47fxbgtE&{NCk`!#tsXFj4Bh_EQKa~UG zpC)A{G}g6^=xT4dUu%t_14oRtcf>EKNyS>4hNEf*9OFqyy;MC4XDN8fd;#!0M|Nf!Nq@7%c+f4Cf$nntmM5g$BzOJ0mVWdUJrG)$an z_wJ5;#fOR;H-}fm^RYeN5)PIhiQKk7sh5#SGLz|L7UpJsnLTgx2GND56e_Y0 zspUPfldwP26!AVroDO9lNS^ADf4BREzoYXOOgh9J}K7Ac{9^GbEalZ%i-1F zoIoQJ-X^#S6A3W)1?*Qpr=>bo0L8C3!HzMCxMdD$sA?Y4}-td(}Ut2G9A?U6lKje^s-wrUiZ0`MZsg;BJ6{ zvI`y?BXfD*-N+$+150|L(Gv@r#WW`+rIQ>N6+0hGCD(0g&1WiRB02oU4<8PrGWy% zHy^oA+=WU)2-@SlIrT-Md~i;$N!H$jMIZlh5MzSuN~1r_JgPzSMS|KJyfROmC5E?V z?w3T9ZyX6faZKyIf=IO5(F4->D_(ZO_^1!evBNzWAedI76&S`=%>hip;YscZgQsuYXLITZi4X#UE>TFrfZx?Q1cj@Wi|iE9SN___m{|)P&zY>>t}Uz z-z0gw+{>t*UC%HQC43(X>JH9aEnujfjbzZv2FkjKD9axwHzrCq4 zu?fb9gN;TZ#NfmmEKdzf&ju8BNgS%9N1pHkKR$0C3Dr4j&jPmmb-r`5YI%sP{y%u)Lu@^27K^@3MzuB%?ULs134bskn#ys3PU#@WXZU zZfbwQN53L|SX83Uew5#a4h~>{_KGf)T4-T-c1K@*o`xE93h}8ff)35+RS|i=RSDHR zOYs=aG6mZ7oJj|F8hABmBy^Fg*3LxlO)`OZyfZK|s`^fEbMB1jA*d+^j%$ zqv!Vb?eA)AD&r%nd>$&h|+uVNNhee7F$J%fb11vEl#UBxzXn*t)P2s z)f7#vz423LP~&FURLR;@zqjm>d_b0m!2MkIM?au5RGwKo@vF`-{GAkrNJQzUtl;I) zS;!+5Ghd}Y5em;L;DibQft8Wsw5?!N2ybjy0%k$?SiNm52LEni3Q#%r`Xr~xfeDWPG zzo(wKrcTb7n~tI(UI?y&9f(h2r|P?up6sU35z?Y)6?{xuwQFpN56j~^qTQP)*QMzG zr?lPL02c1rCq^e4^n(`#@1)+D17#aLsok2As|(fv;Jx_Zl*;iu!{g>~p;;TXx^%y* z)|eL^Rh8XVJAt5c#@@yFAxP3l`LgIw4$%>vieyrgEFu{n;p5{_7|7H%oF!4nqjI~f znnA7jFjY7)Jp(9`cG4%jXWQPwFF$eA1oxlI?>QDs2L!-kyzOx&b$EgjcC4UFpD$v@?-W;%4co19wzXLc03`6r>sB=5~8?SmT4~(=|)tK(IfHq^vLM%x6}j} ziPE-D;5ww0(f*=J=Z6ek7OWZruUmuPQovHZhGtO1CEhqn)x7tMZt?})a#Pdp-*j=H z2D0Aq6PDFS>&a7417Z87x)6KoLN~83oQ>od<2(Cxw%(Z>FKN~7-r6DpmV)Rr%Wk;u zsHZ71`g^N@LVclcBMhigfdQxpUr~5S3W~lMj}v-E3^KCnKm##8p^NkJLFcv*%{3-b z`wwL1SK{M>l->31)D*JnK6p?R=xF?t{zQ;QcrIg(VBO!i7yOK*CHH6N@P)Rz+n2+0 z;KL&aRjBPABYK)k*k!hD{3r^0n{?dV;ZcU04gr*K`k!Lc5l}p}8Qg}(H zLwP79d0q{kpbXs&zJ{`iR}$}e>KaS3Hm$HfOR(@=a(Qt){G58NZ%NnIge2V%fjKkI zK@o~Uq9-dPX4hmVw`SW{D<3_1IA{KnCloHDatc#6D2zUzXxl3rw@7L9XJTS5Va5>A zJM+OwP}RUyJfeC__$*VP&H;au`hL7YN~~nKFw}YWDc^nJx{XmO^{wlwT4cz^5j6Lt z+qvalGP&ANlvcfEt7gJeA??!GojixHy1@^Y>h+i$p|F-8F=ndx*gyk`k#+mT$W0(d zPVH8jKUKJybUK6mSRu)W9fMR_7@R~!zJSc?*c2EOZQP^@)b1RU_J*Dc z6B3tV;8a{fyK?PkD3beI$qa4f;MwRVg4`Cj zKtjj6I2rUb(Q=84fd^tyv6-5F%_UbSA5j;UWghyH%b8YY)K#w+SX2LKV?weR>36u^ zTTtIGp439u^EHwO$)(IA&66`K9~6!4dR zH>@P4agoT`0IOr}49OI4!;*+RfI!5UqK2iF6|QEOB8LRh0y7_qKdlf&-$Jvh`8~*B z;reU%j>1&OGM1m18v1!SXPwz4ZUA?cs|=b#AQqAX{Y?uBxLLngxqbSUKw&iHo(i`% z8U=Ib6o=l7_D!7Fg#G?R7ORU|Lmda=zq^i=z62SGYjJtP3jI2&0#ss}XaEAp7{ z-tpd@6{ot)@$PtPuat9bK#%Z48NHO>|4f){Shee(ym8a%jid7I==1p;5e4oeu0))- zQfxfjhZ`^F8@&sXmb+K9O+B?!jj08Oc@EZDL0e#gcySGHUw@}qI)lf{^*QcZ#y2<33H4>9f~!O%mT+~ko`Xe3ElZNqce`5ZIENR zy}fs!PLlfW3|AmVpHAo*xeppiKbG%aeC?-|z|Kb|ogmFj|CHMNBGjuCz-+<&o{1 z-nF(D<>`H$sS1G^83wRAy$;uQ27VLCuk-ddS9>m+%=p3p67Xbe*qPM*Ju&FQhT08j8-OyNv9w2o4+5fLuF2CV~mPlb*snL!=Y!y z6@_f1(=*yr$t>X=sr>Z#m+lD{p)^^_%QU2S_>Ue9e+w*5(H(nsje_%gA~pNv zCr;cM9n&e5s(;82pAVaN?#d5|#rFAHGTWwTg}QX>xWd8~7Ls8R{76t$qv?)g1a`PR z9uHpSF8UDY;~1Iir-)XnX+{Pdt5T<_hDXYamj|;~NZFtl<0_4^daYf$9r#sIVsGQN z?ACfssBY9Rwz#b>Tp{7=amg{@EOxb*DYz?nm&O;RD|*#WCT_7Vw;Z(Xsa@PA>J6Dz z@smZyBC;Vf?Mb-VG;i9Wi=v$6nfAGghD*AhqK)#dYvAJpEdhlc+r(2Z#Da?Eagl<3 zPIWlURR#hu3*rCN_v2-mRf3K{rG7P`kD0SJyi^B@{9((XMxOm&nszpURMMRk6e&MqqKW9!K*J z|JKFOmDx1-aWOhwNnwC~*ex6wYpeIR<_iu573^-!PjcvnqP_2!ti}uSQ>JDAQptR^ z6=fooui35k&GoA$$vVv;PW;|R8^Ao5!x@J+^dc4uRT@DuD0p0)kP04GlCzSl5BqD z8{)2#V#r@v`k9_7cYVFb^6T|fQS@UnSjV>^B>gH^4~tW!&QgkHcKMg^iL!L*O1Xj65GA|z%~|JBIBiE@Qau~H172sjmCQSy8Tkc zZsE|tzTA*H5uzyX^OmGnHN-64Yca^WVh@an2abk_KPqofKH+?QSti?wT|JZJVM5UB zSbc2CMIe`-B@|SP{_drQ-~RdF@}wf@H#j>!teQl8=+k2RaXX-oN-qV!?8jaBg@EYs zT&{-YR(p}S_p0XoC+?=a#O)nEw3H2){qYjk&Je}8QYpw2F=nUdd2BN$edGd@L;6eY zhMVWh*e&glPIZH$E^<%JCe>u`^+*ilJk`GP@`QB=(*w)n*3bWv0qpuov|iiuxKF5dV-Cx@|_4D!~)sE}?>x5f>yr z635si-6V2sYU5p(pI9H8mmH`D{kK<{1f{sT%kW-po)%_G#!q%c^D)5PjDN;}=3{b! zu+SwfZD3`8tSSHiR@Sa<{rds(q zM1q&lUn&K-9;^Dm^D67ZaW6`DuyGI=wr)K=ZR7zAF%~ou_4BQ$ZblKO$F4XR>C4=2 zeV(2GYz=-~r5X1X^DE5VkFj9r~s3fgiT{AoZ0C0v@R8%Tkq8E?OD$CD%rgO4Q7c z;S79vn*Hu{;w#V*B$xXfQ0je{XD<;W0$-&g<8I^VL1uRQhaVgbJ;=FWjl~)d>G2 z>8m2@MyODJ#T0zzt&_n$T|_0^S2Pgq%l$%3?2TUtbWAJ^(bMObQ-{6Rf;!y!(aOZ> zWJl~{kN0kd;EJLjSeyr`)@ibHpR7P1h~-Z5f%NlZV_Zndd}ZwZND3ejl}EIB7tIgt z5)dErmEpf>nd25taES#?(P_4-JZ7F7rBUXlvb&a8#s*IuT9sb+C$rV1=cz-=?NpLY z@!!KPjjod~HApGlROf1R`jA98{z)VJ9g^e&s&|qW_GWlVJa@s$t1GvRv(=L%!X(@i zZYVP(kKc2;3Km6Ld9J$J?XWHJP86DpF-7Wot}`s^cLCyI}%42&<&UPiO%U(@EC=G%3=P|0C`&d zRucFvo%GAO)oztEU&jiAPmYWnd|UIoE&)P~5dpRUqZAZ1gxTA^5r3=o&#fl9nV6$* zh_tqMML!?+YPawkKa41m*dN&#^xssr^D!jL_Qe%tx@q=i{89G&k?3eAQhc*(>qK(7 zrq{_FU#6?#oAn96w@w_)NpIPELD(B@RU3MxJw%149Ex@(fGJq`0_}v$0yQ%{AE=qA zKp0p0q{Mt5WscVK=Vohx*AP%+flR*|2e``9CkZZ9C`TKRCLX)_mT*h=e5)c!C(-(a zQ`VYM4ETl9H$g*DJa)1|*|1>74SF=*ZynixCrHkZ(8=Z%(+8;?A4LMA6eDp1qjY^1 z^pBo07Ki;~Evm&WQ4dr?CZ8fW;!bN2flIM|;vilpU6#Lz1hrd0egH2(6SFNtjd0bbyFT=0_U<9}>4B(MHV3#(ZSQM$4iN}NXNv3>}9 z22giX)yMcPOwkl3gsC=U^|wu zK+XJvPDb1hNyZCC$9*fB{pCdD7!s%CltUo}15B}dQYR#5xk>gM0ub33ucvoWuEiF8 zHvJ#KbE68L8_>S2MDDLIV0Q!E5X-pzxUPkx+T}CB%CU;!f}>20{&(;BmWWihd$;o7 zt4a}^y7grO7x~T0j2=IkW;~DTm7xNDUskiA%b{&1!rnpaqu#EF$ubdg!uazkt)-0i z(JoT`pM4GvQ!TQ6DuxO%wQtQdL z@I;L=X7FD&dOqDIlKW`gS69a~&ipQ`0%MT6v*0tgQe4xccuj_nl`KW5N^K|&?gjX@ zAZx9A6Te-Itc)ADzg81q{ZS&RBQviF?9;ZMeM=$UDlc=}#j`ngSzI)07b%ywr5lo9 zh>Ff*d?A#UfGBP$4Q-nx@}t7mKWufB#|f4%B`JDNtB&J%v9mF-%uX}UameSuB1{xf ziuWdHeV|r^S65ihM^a9&uQr1gh5(JJ)4e|HTaTXMi!!;>BqbLZ@E43^>C-txj`%sQ za?w2aSl!AKdWM-!7j*ec6(cK7GJFb&=O0n!*T$XPQ#_?#zq32)eGPG}Kj{!Q`byA( zpCzPmw*7Kd8saW~SZ4x=t%+0V7n6xpghcq{(H6V*HO*c8g3v4eoP#`$Rh_5cL}qin zMrht5gZC@Eb8{9yf1st5v~yn1IE8@-!kHcV(*%z`nPr)ci6I zY`d`55qa4in1egjobP$Q>yrR-{iIrB2THRRM=c`lercR~2ZteX@I&e)USOQeqF?ka z-wp=DVY6;%g~!SQRWS$jP4D%K@_8azp~Fp`?QbH{8J%sLQ7&VdXP%DMal+Dt+OLc?7;gOu&zZ}cp3fY!F1)*xr^6tYRI z`EE=eJ&ih?!?fXSWVTI#O!ap}-j2;nT3w$-OQT$A-k^#wtQ7a(TB&%e(+@vkS_dZE z!x7{yY&6otI^AU@2?W(Tfr~F{V6YuutTeo3+;H(}lAl*5{;6fvDKzC@<`ddO4%qG& z7ewMVR_S)7+j zSig10--*T|RHI7&ZiDj|yZvyPA4zYwq)e`VZO$W3#AqVUIL%wVr59?smk!k~#U(XY z964@9=g^HP7bmlN%gwryG?7B!;a)^OCX(S(gIBgG54}Xx)s}{^T!3ud>HTp}E>5o_ z5sKHyH6K2G;1z|qV(KSotn{W0gKU|zc{dgUNDuS)ICT5-jWjEj)2g`3{BciyQ2yN1 za(q+kLl)U~5*1=b*4ozyqZ+>A{7P+6If;#K3Vj{xGiJn$y4Lf@2q?{gPb~sPKRm7i zx(JRD$5?(fPgKzJ%`=Svws66cB2+A9y20F%!ut0mEo`+3$Io^d)xhXHT4Lm*8xt4r zD$gRC-#*7;J(rRhgk5%UaFdr)(l@R{H-RIJ7OU+rnxEkLZz56z*>^J*XK;(&)~tip+Nx%zAH^aH-Zm}(0JMUZ4-}6 z9sUV+4U>4l50=jbLs*yP9=g*>%Phj|&oF*b?@lZOzM?YeF2jiic@s#CM{Oi?Q${I7 zVd@lG0u^+gY9S_$U%0F?s5dbUY#L0ClzAAZ=2Xy{pQvG`C5mvWq}lfB#kl8kayDrp z+nzk%a;sqne!+Cmg#;k{j$Js_RsA;{nA9a#uRHWhp%yR7L1RriNO~xA;Xl-M+pTf$0F6G^vn(U zK>DKtRBe#o{x1pzl=VQnCqJ+);Hwt4YDv;3uW^ggfM3v(;Sw0dMaowt(!$718r(BE zo!$$VSK;Ui)|K_F`e-1~qSrgKq03lpycK61>sbP~48qD+Ji)|i1#A*?l|e4Lp|#C* zKx(+hP-L9yRCtfdq;)xIy>y03p)H1he3Fi#XW3AY{rGmxc$`cs>8*Ug#8lWN*O1-7 zRv}u_Nve$;$^Dj~)yF4T7G*$6;_=Sq0DpgA$fwIHqT{l@on!?cvk7 zuU5?D^zxyUy4yV?=vv5EHGOHXGdq*{UU2>nP#ue*i3Y~w!6UE9=+z$m*PI`LM0lFy z?LutbhKRdrwaHT?Bl6|m4VlU_LnfgbM{$SxVl7_8%y5yI<>u1LW7|nTaeNc|2>Yqo z#9!cE2;4%~4Q=e*y78+-w#DHxHC{ps+js)6s?wymT?4z})iSFqBhiel;P{K2PLk#T zz<-+)3v))V^DFWVg@dg_Q-01kpJur>-gLBbwHtH-O>nnr#~8bmy);D~3`* z7#E%@tu7rKWB};WB#b)$z%hIa)dCZi!3+iHi$%T|&0p(4iF0>#o+0QoUrB5n!nT4; zwxN;`G>psZ`Rq05KC>pV-||)L0{(qL(eQ>FLh@XV%hhHi#L50EB+n_1T?e*mx;Z(> zth%r%mJ3ntr*S@de)%Vtz4&2+(4oIQ4-nK_!%8W64N(k_c%Y$q@gyf76YpC!$-9`+ zV5ktPVlYhPx89CWDPYB-EWkvn$P4*fHt&-9LssgOj;d$a)}JXa zH4oJ1Pi}yfy;IDJoN}Zl4dy2c`qwYIi1aq(IhlBAQVcensJ8RZboYdo6#hnZf&DuO8HNY?Pf3MI7&{PJF}e_JB8^V+?DdIB*uBJ}7;yVvobh^s#>`h2R<2ye z7kfgd;MX_RFf}5<1%Bid?;Qr+<|L_YgBLzUQ@BVVOMH8Ea}#IaepUK0dol$l1`Ery z%1q9Hq~~L1`a`rDw#hdGE|589BI|pjF49 z3F(?@X`jz%PM>%rRP?Jrv3sD4!8Nc05GwIZ=66kdDEs_xX4sb1z2iUJFbrJthie)f z?lA63bUDuEh0kzrUA(;loRCSZjvzh)w>x3s0vANploxynb9|P21N4EOL?NgxEWe=< z-6J{ZMz6BJ@I9KPdhWB9?c)QtZz*IOxG?_%#Q!k?kkg|_NBZ-hig`yIL41?a3 zZvPwEoSM%tKv)Vz+;9*NTWD#zeVJsR^9c-b1x)%v#8;l#!|P)ULFsF>6ty1j4o|B- zGED`_@pR3=u$UqtMspI%{q(5&gd4!(Ls-UU3hEl8xg2t@!cn3+6e9R12(?b=iv~V4 z>nJ}JDwQpOxyHx%_Hbh7WBUH%t9^*>^@sAipVhjG6R zcUv}qMDeJ<;Vj#@w97=rDW~(HbDF*3nf|BJbw#J^9}>0%iBNTaUO9ltWBq#Ob+^T? zbaG;FYRJ!QRVxbO%z^m(8V3Pb`AN%^{Y6?9FJ6TYGOQl__4K<@M^`9|rg33siY&c9 zqrMus@h;kr6Nsd9si?2Lrw~ElA)c9Nbv~r$Nf_TgR5~Yn5Yx(uuq$jxG}Hs}@oXP} z<_5V`KCw2zfos9nVb}(kxhJef5$Vv(2YCFVywPP&oBUZn`r}47$8Rcm3PEo(&{nElfy((u-l?Hp z(9F-jqn3UCQokeSa#P&HIc`?df0v(~Eb|xtEvl=2GWchU?gq+0hJb z3_6Ap3&k+lu@$*!fP&cb+5nBgWNlt+tijqX2GDgN`YiCheo-v^9($cxI=_4Cxub~B)RyZ(hR zLw^F`vA^YR7@}YA^=Z|zivA#XBai9Fo$SzY{oK@kyd`@YGkN)Uas8`!Sz3fu1?S#V zz0{Dj6#PAHhdy@~5XV(haPmt!)MAk|s8NKwl}q{AMtbbv)4)+xbeneE3bbhu3i%B@ zQQl<<=^z}T_s$*k+Pwbbck~Wh(kPD%q#M2_@?#i2zvxbhqW1urA9Rv_J$3ElFAs#A zI=ikWB4UGNCM7l63A$@gx4OQ~q=x5w7W&GUPHt~{1NiZbZQ|WkTv%F=e{7s!w3+RW zLdb(rLoMAWbr^%;N-F*I^22{R#Z*Js_#O>CXwTDD^mJV`K-kZ$q<3W;V><@Q0J7;d zUKTJBoF47PDgy*q2iGVSo{mmjL1$~R@ekVXL@@RY{eIz+SYkedO@Lgkeu3~ zgyDZYxo03rHxo3ZY4s%-N=p(w53smc0oeE72Jy3|&QO33s4+4EuQi4N{jTvV{l4$( zokP3MqqY(+v(@6!qxhe^(tO@#FZA`^Wp%uj-NyefKiy^8x4?3i=&am*v84@QrZ-qA z=3qR{}nE(5R#|||(=D8um{H_Mp?y)dd*UUL~)hyK5#w3#ZDQ zdCd=$3YKO*twKqh&w8!lk~6;s8N!HBvB1;OpfnlTdf`0v)#dh+YtEN7zc4Eq7{W5? zrWn*Um^-S^#lPgV!K0y zw`D|nAGb2z`(`M(dI^QIe<*cef;M}y)=f5UW$#Eqy#jHavz5J_ zhDvBJXIES?Kf6&?26f!XT$~YRNQFfMMiJFY5MX523I14cyn0xisPjp)632V&+f0Hy zQeZ6xn30chfX6Rj@5TML1SoY7e_mNC-UlS3pzR30MQ52T=Hvp0ZX*;gXUTRjtCQ0{ zf+7oDpBi`7lJ=791Hon5++1K!g4+>gtqTIs=nMrHau_~6@k{BOG7vzJ0I{UNr6|o) zQ?eFTZgMegZFDc3aBNRRy>V-PH}g2fafxsch^Pi&gp!!q&8$4z-YvZU`2jz9>iM^i zwsG4OL!pa+4+iwvWBH69(cozjSf#j(NxhFBCx8NvxU1T}KJK3V8n`UP^&py+s!8%cEZn6}R z{?Tt;mZtMafJjR>-B6ZY?L`rhmFc?H8ZgMfXD0=+fdE3Ac7#eD1!WIVD4AF*B9#W1%Y}I?*FO z$B_gV;}*d$jD4Qi_e_QWmaGjV!>(oFNYPv|Hk`W(aW}NCEFWHNiWQrkNxz!U{K})y zm$w>5N9yu|d)l<5i-fR<;8S0L{;cU(Hf|C4rf%~~#f3dQ`(J!*|3@9w?}@GQRARLT zYL2P{tXb~sKc7F)FVN|nNbNq}M?8b#DfHE>fejDgo%~ws=YhKY{WeyDp7X>~5&V`= zY1<~Qg?hxkisu;|2)PDmK%vGEcm~;4@rAXCG>|SxG(7>}IBx*QApji5kAdTaG(dE;%77~r{J4;3EC6~>7MJ;m68l{g`bp*yo0lIf(c2q7cT#t9=XTR#kzDb<@-Mafx^?cw_=rt<= z6Hol0ge1|w*HcWjqw=zU%{XbxEb!B%)Q0X_wecp(7*YS1#Suji9;9~ywo3Ly8U z0@k6xB??|w8(1-bq|>EY11Z$5e21KS0j7A~jG)3fJ*!>&{-=1?tyoG`4)W1e;^AqY zy}OF+wapQ}S1~cgJa#$Mlx^q9?Pm8)@EHat(-K8zpHqyd&-pJqIqng;Z`>X2MV`sK>dDeNZe3l{dQg0ctKe+K zj`BJd8)ydFHjmz47a>w+ICi(Lb>Y3gt5|dQ&1K|XH<0pulwAGI0yXKL=nVzLl-}kW z`U>$Ut?$D9WSg(J&sb|PK-MOdFE-8;++8vN%~k2N)(3yiRXS&g4!GylS1f|2j&671 z5SWbrX@2Tlcn=>%{vHGT-}n>R)j6G2KjuJNMlfu?nJsFj%#YQjAz9ht=h7*;kj*w2 zhn{*v_@a6ez@bED&ptzMoMHqK7&WP5v4FglijqrwcM$`${PNA+S)S&b1R>jEs~9NPvX!`(U9hc`8ePU@^yl>ZFFz@uOHCOn z;}ObR0*M|4o>=SEVx7q6OePkwE`Bs`*@Bjc03HJM5@^AZneIJME)-0fO86Q|(yUDc zD4|7upk4dlLWZct5rCn0Fa_&Nl|F(5r{665!^bQ8^ZotzC86^$mOZAEgRd?RAXz#a z_;~(dHRH@x?6A8}Sn?m*E8UQR+p3^?y{$Z@c{*N^iJt8lkN?|nq`uiU?u^bshfjBp z{cIqrfP0GG(g}g0xorm}j|dKBVDZrt2VUlpsspg7{x9Q_jig-8ogIXhh_v#{lQ(Z$d)?RvFhsewDZ}}TNCns)%q3N{=G(h;8PAc{w2@X!0+pL zqs^o4bPnWRt){AR8$AyRz|br?;BvkKgzv`+ooRPHDE$2U4jr`C4-{iRzkP&8G4Bo6 zJJ0`>d%xGVukr=#h*x%E=xD^yr`uo?J3j&Ol zf6*6fqJRXk(I%RKbu8LHi;@Ur7N;%tG{wOXZ`>f}{^5MSq=qt?E(q8L9uF2aMllZI zwacYO%N}?J+WPlI5HGQH87E15Cr}nbz71^tC%COIT;@-fhYIl_)$JDG?Z;8#F=Yfr z+;t}xfGu4-4)Q39wLhn^%&texm86Nkqru4gb%9<#UeJ*)$HuH9wF93g5B#!T3mszJ z4JsC(xvbq8?Sm@!`>=#Rw5v(f6eN87Z6^jOBrBv{?4p0!oON#lGlWlrhh zY`Z((_XNph5l7^9)yb&PCxVI!4cS$Nr?|5;hJP^v_Cp!a!|8#eHFCaG;*cTQ&`I3Q zv;V1b{P>W;E)Qsr8Wd)?n+0|?>f`SDeR*e+ws2vJwhA(_mA+NAWp}M@x^b-eLE12z zRm_W~ypoN2d#?U1XEQ3F7*uF$gQawDQt-&802BFLk5mkPY4hsU1{Tb`R`S1f<{;}n zSpa6-J^BlEu|IWMT%Q0DuIPrUQzUO2^wJOb|2Xvkk#>C-gJy-YX^)@WOdBvyzGYE zRYxFnY-%*!6d+gbDmI`PqXGx(qd~B+@JS}ymvTJw78Y#pB56q>LoB7DF_08$nnV)P@E&|6k3#QGT1wpp|-o3wT z0CU6G1M(Pq03dYyokdT10)fSs4%oW#ea`GJ_|-mW<0OP%{g2Sb&Y8lb-Ww(qQI~(N zP=q$8PPgV>ftKyhTU!BbLX!*e;1EV{-iPGh;oglyg6faRp(6_kJDLyYpwUfqocgb zV3XJjdcfxkk9VXRtZi(g8A>SAv3N#ka|qB_Xzp5JKX;vO@da_`%oB!XG0Ym9-4z9( z1;d`vXkx}!R$(5HGGAtW653rfSD1>H)5M`=>334dwEkFdxX)?$b1I2P0{Whn_^2TdnJo*3bbh8-t_S|s+HYx&JKAFuL&tT}vC_f~R z`DDwW39L(g?sjl#!@SwN1WHieloLQlHh-?U}|< zpZNq9pPihY?`n+Q2hsU0*$~1|Xi;I%4O0 zS%1VR5{s)lm1KB=nbQEQtA~Gh<$3z2Ly-#fi4hl?Qy5%!9#J)iTEYb_*%1y|yX1gP z=3d!zE~4kNMg4|v``9;Z|m!{YsnQtYyLY1OdvQda1n)rg@M z)mtA|{k|YkwW)b#WaK{UvPy1S*Y{olW2UVwd~?3Fx$$El+b3#Sk-fJ_-|=}czrs^v zNqevuPb3S53#BXCn_*C13)%lGqqqGenBkjeg_XwPNO|P7-N;q}HO(7{~GE4u5&B;H2&E3e>zubu# z^npuhAah>A71QtC)aJ|kb(J~+mrqoSz(#7&vZmzbsYZ7O){P1>NiRA9>@zE>KGP%S z--o&+yvAi}V@MqK-U>-IC|CRsM5h9x7qgK-ZYL{F%-tW{5ibHiY$69d9zpeUHDjy! z=`3S#jtVr3ZBPDGN_CTG_EtZ_^(1t;cIUb1Pm1JD+nT^?gyLL|-(H#MRYP?~UO^-Q ze{eaIGgbO>Dk>mO%OUg+d2OD#CB30epP<9(Zq1Q)>sE4+B7Nkclx4zu|J{D?N zr|9n{bkKJUHg~dBfB(v(e}s48(9TT%-*#qRt4RP->d%*BZ=w3ea~&}N@!ba6dgVFq z3Men^wjzTE7TDA*f=Jy_3ajFD?aqo`p~Cn$zemBuQEG1J}Ws6JH)*vMgse{tp7EL;UJg27xLTD z5OZDvK&_we87PRcPIMCdFb92R-4T+E?3m+!E~^3t)uWyH}UmqOWtVj(JQd>Y0k+N++_`XQXa%L;qm-Or6 z;{U_hn+HO@zW@J&P+2CaWC@X_4Q0v>(*^Ks-cY7B{eo1yVF{Oob% zx;{@~NeuWGivkVEWHb`|2B}J1Akq1%l{G_@_Y5?QD^MbjVW(bxgQSIvKQ8EaQ__&; z2!GNjQ>SIswFkiENF<>*dDYqU*)4H86MwX?Nq29ggHq zs~&iWG+jRw9wHTJde=#G(h4t%W0~D}YH7n1N5kL(NSWPj0=KENw8TnC>Znp+DKvr&s9Rt&c+nFUD`VkUixm(j&D`y$?vrlQP%vXN49Vw`o zUGP7dI!3>pdU6j2cijTl)17S2kAZ|8Ze9#ytASnr2{(1af6R}%>{V@pcaB#Kju-S` z1YGFr9wYw8{^HELJ$-+9je(%V`ojmf*xd3IqXx7Hq|4YJg%+M27U~`iJXD3EQNNka zAgq3SlKPk|5;d)cMS`|#8k2kBFW$ptzT3)M#;K3Vjy7*OECn7#eS9Z>Bf>98*6`P& z7_hel#fo=1AeJB`0%HCjcLdwFOC$0q&M0~89xtMW*F<3wl-PEQ8(TWug<_ctz3)k# zO~FnaQNk*tIg5_mL>5yD&>mzR6jU+L#x+k(#ZmZ$TIaLXSiIJ{F2XQi(#XGnv>;-?_U!keLzF>iLxx5st8@&Jns>hSt=Tg^6VP7V35M zF?g)a=%*%eQfuIlqJ-I5htHVmamY zFL?HKE_wEieF>SphQyJ~2wfeuZ6vE(=p=$VIf*bg#*HNjsZ)xO)NP&QA*NxoKh{(q z-g(PvrX*hK^>pQmYBwE2rDchHOga#C9q%o;B60dgh`0$$8x+-KoN-!&XQeS=^}Am8XbkdK7wk=+DA2WId^3m(>A^m(67fZZbt!Ym^KD&?r&C)4bv`cD5a(xf1tne(atVr^?>-XTdD=e+P|ALLYrjO9^ z!5&?R2A1v4`P!pkIraV`pGsC&I?yMyFzppNO~7FvqkI`YYlnn9XLyvrrcNOeL_tl& zHV*SZM{`z_;|X(_6KL+uutUOkFGICrv43HfE!gpBFx(cI#N9=hv;%Jq`uC+3_GE&$ z9uhXa@!^~36K0R`4QXOS{Ei#x!5F?gI2+fvVl|N@pZvoB@Tq7o`tN!&tkaN~oB(fQ z2JZFO#^jP&VCA<7_LNru+e||-)|;>k>&(?Qu0(PK@``I7tb89UQ+ad5d4WB!NE}(Y z_g;BK#>%%zjS+L#I!!fI;HdV)VqTtYk2w1sfm6?hx4<7nwwIhuBh0__>MOL!>C4=o zJ5GVd7~2U&$hj#TOMa`vwwCt~etCob-~AH+{zKT5;~!G#VzrHK+6AZ_PfwhBb>-`Z zS?Y#84W-hvXxazd^jIs=eTec7+S?w%h?+}7j?OouLZw+toC|~c6f_o05-QP$Sno(( z)79Ng_wVwcCQUS=_#$mj?IvqMt<&WtYz2#%`m}so5OmvU+l8qJbeM)fkd60WAv^*diL|*IWNO6SXxn@Y7h}(bTY9 zdq-MZZ%)5qVAQ~&9b2lqwD;39r<|^>G$t&1qLeV^Pj#o^!^ueuUhODq2W4~>`x_mB zHJP^m;wJsV(`?Zk;rnk*@JQFYN8WM%+H%aRlVF)Win@6aAgEk|)2Jw&~Y z9iCCkF@nmVW8~NN7!G2sMw7e%Rd{p$FJ3FZ-zbWc zU^zPbx%Yoq1R8$Qdthe6vy8i0H2b>RUPZ3Ju7kGa&l3QELrYMa;SJHR*~Ij-to4Ql zNvplq(y^}(P_k^|^<96_3KpCK;iWr4u)qdP;mq5Q2L}DICJm^FO!AQxi3L4k@O;15 z>WdQ#*28oYaw_v`%=72HKL}ieNvL)F2y_a{Yvb&T%v5QkQ6bYDcCse;(KE5PeT$MN zZAXAsCEAd|Mvwli_zLEY$ToTYSb`LM+P=W82%)kkU*;$*H9_op58J(60d{Y_)Gcnp z_>0ZI8mznQ<>A%Y6}I%BWSoxMu8sT98HFxH3weAhbcecmuyxQEtOaI`wH}I0! zqBmMK14emJXy3E)pIMWl4lHqw_9aCXqZhX9U^1HD(VE&^DS0p(U$0UPxQFkZ5s&Lg zS*mQ+YPx&)nZymKN+*^XM_@efx1a;Ykr8$fYDc?iL6@=k4TM!>2?G0pk_L&nLyKEF zU|P~$auHtghZCTCm;hU!bh1_meqPB(k!@Mr-5#_o%IGfoh5l6hGxlev7aT)>NZW#t z99N@#rM1z1n@w2S{qvEuZr{hiZg7(t!H-2O0|)8CVR})kU-I}fz6o|C7g+@R^PKpS zPXZTERo5GoK$SK>0)-8H9;HGz*QR4UlV7jE8l4f;T>zWuf^FGF4;}X7U?D5i%sGGi z9B>b|Zv<8Fxnn-SZTk48m+{yDPO$X8!Uc3WPgvrh)`AWHvCY3z>%P^G<&8^==2z!6 z{=qs0Xp=bi52%uY8YQRp|K;Sx5L#9)ZAiu#P7}5pmw?x2ts)hs^A( zkY<<`Yg6t8jui}=coDw{nur1){u2QRM|Z|?o#hlqSF#~u7Fa|}zDeP-GgksNy1u<5 zeK&7yE3jxE4W|y^X=<}~uth{wQT1D+=y8#D6nR>xJgXCJ*eIREKxas3+{ModpMato zGxFJam&kOBzBc)E=!_`vHu(_Ab~HtSmu^=KE9vwZlUX)ziVB47oXNAo&f-00GQCC%iC7t0v}Q{V>Ug7=vBU1|Wx*oFr^0pC zFCff-6*Q~5>LQ^Eha5EhE^rB|+)w8g9zj`YuYI(B1-CHH!$0Lp=+VXQn?L0 z?KNxNHWdtD=;Z-yAHLw|nF7qRcyKN_Ve=!T;=;s~> znb>+3seku;wlShK(x7S_En*L07MsO7zP>#>ug6_TeWC*$DRdw>M6PdnL;iDbe5KBp zm_;=MUL!~W_E_C?dn_vRHCKC}oM|+|zz6|^O|`+Y7dV)VId^YH7A6nzT#s!IZbbvd zD4ak>L83IGsz3SEDp1?bdrexDL>SX58AtQKU$owu3;eCdI;MV$RaB29Y&J$K-OZ3E%!t0Vkdjuq6;4WCoonC!K#OY z=E=Wi>*xLM=VsvGJ=m)Q>dhX*1d1}UZA^l&8Jh`RFeIvyK9k#tp5zXG@X`zBO%cd@ zBpt~6vcVP0;YM~NmZ3)(qWg$Czcovy-s0#pO7*#j{3bA<^teA4*ltViv~#~08u!aT z<`L8L%P^x^SA#OBf?6m>LhRUsnn(u)WJZ5+hbbbTix>%{kuS!15 z6W0K9g|+0r01{hUaz-Ky9S7_{Zns>`)FWqzI`9vRYE4it3V9`W15?ly>I_<8sUe*NG%epn&!Ch=jAxqC-hfjHZDa6fQ zCiKnnKo%yeXGIXwid`sy&0xbM(d)xWx<5r;>#^CPTn`2~*h^;04)MUyveb*E?rlH^ z7;_Zn0KoZZKIQ4#&Vc&$%heM62NvLfV;Dg(V8PGS>bqS+o&`OoqdwYEiyF-ob{hf{ zQQ(507!I-i8R;cz2XI(VpKU_>OcQm$$}5cQAQ*Qopn`AR`N5t}%}p^uOnOw?C@jP| zJ`di0J!bt!klPNhl80U^5`iWg`(I(rm%nHXKdoZ<)g*PMhWsKRFaNRH00IllX8vQf z;j5%uUny-a5H*=ZSWi0FB{bYTez}dr4di4RPD6W=i>a8LB`QFdk-w{MK-uk%{fpHp zeQYfWn2n(4m6ob+G!?wB%5M%*9l&s+RA@mD6Bu2abIM5?SO*YaK*FP8dtYJ=Xq`gi zvcw$A&Irh!R)Lxrt7@Z(@(xwZz|@uG@eDz5^$yLZIWAbGccd@bV=wS`3LEE{^QT5> ztP^~9^#5?VWs(TAI5Xa--0rffg`mN-N-T0^SiX|^-BnA$1Xgcf^yz^wJ+_x z?h5JtCcT2Zkxw6Cpu!^zy%t^Gf91ss%XO`0PGwsL#{Vmi=i{$Kyb#|;KvIoEPV}>K zEV6jO8vF1E;=>C@x!Tn>)~(8{zB_C@56FKB)GU~vrZ6J3{tAMZyX`e2L}>{m&OIf1 zxV|JYK?6t>d@el}~$MX?FK;0ok#!_;+{X^-*-I0asA^)>UFghpQRr3JJs;Zgxe2OT^E5VzryAMeNX;#ak3HMnho3-G%%X8 z?Z#TZ$Bc)37s)w-(kUB1uFkDUTiw6KCfTag8@{)tUP*`rP?UiEZrqX&;Qt@Sn$ct_?I&R(9Qe(P(4KjZLJBk#Q@<$7 z?QusPr4oO;`036J)~a!n>7~0iIZOP*YF(S2ghVb++ zcv*qxXJEB7qGj~!54~K3 zVjhctTCf3Qcqwjn;B9Qwp%%>a#S23XxhOL23 zT(y}q?{>b3Sp8pX(EoEdDO*Pvzco87@loM1OaF(@ajF2piC6O|arYW33LKpaAH|UCu_hq=<3>y^p-YD4G&f$dqeSI*zmi{My%!y^3u9;i3V5^Q3D7Yn z$re}6v|gM5{I5ey+_}jEbZh(c7|{%208*=v*aVi@L4vjI2_RpP31fHA6ar=U+~_BD z(sXAt0UAY7vZ0RovHb{XRZ@jw;Qp`VeFi;vXAe!*S!c&T`OE-^1rGxQKJ%LeWKWELhpbZorqt7u?UMecAJTXV??|icrPFc&JGEmaJN2zJV%-ei7_c!hJX!M@?All@vT=3k zXiQ)R(;*E3$B=wV4_O7mO%C8zk56~>p~!5=Hfm~HprpEt`ZmXIgto3>?te2=C-FRg zW2VkoT$jn=nP6;xC>^lB9uSrPpIE7<#u$YyTg{mGrG)&Na=q!~ZFhZ*(e3in|GD+? zPoqYzm{^@g*vMCmi|Qbp&zmrId|V6wOJHp3!HhGpE6D@hTV_MZp7pr7wMF)*35d5H zKMwK6c{#L%ZY^Dy7Dr2Sqnws(8W+jyix_2(nBL+>uR6>#Q`lxiEK-`m!F1~vVPr#HuP|y?+%7SF=?IzG{ig|I6#1e=<)JPX~I_+_GmgVVcQR6LO1(?KiF5V99BDP_%_o=`|00NQ3zf z3LIjclp^}>i~pN5Mdn~8Ji@SIv*UkyZ|-lUlO$ko=A^#=H{{5uEBOhUrsCkG(3vTL zqBN?HjTMk*t_-o3s_{AnTfvsR3drJ+^Goi1f8Y$k^naY)Cf0{anhosmTu&*O4t>o9 zf~kyXh)aqQ-O>KgO4ztx*mUb_-0SJkoURUtZ0}WE2+g6XLLy$hk(n%QWSj@2MG0ga zKX5m+eMsdIA&>OZGavUoX0`ukNzjMn_zZjb0IV(Chg9!j&c6g)(_uSZpV#tqS7%}T z+ix4r9^7u+`{r_JTuf2oFw7N9oKFuVLWQF~uI>|=d0nHbV9J5kt>tyP z^N?n?#7>wW^Qh;6c1p6>S}{>hFt+E_55VeliVz5KlK8Ctm#r#(TurNg8$2LxC^5nL zvjz~3k8p)9MpXu=$01Y)g}k6-AHJ zKZ>3s3x%Bpu(`?9vKHG*YJ(lSf-;oaV)^UMw`Oz zgPyhb;4Ny70B>p%iNJhc=&_Qq;Yj($NTUSu=ZQm$yfENZ4}f8?1;1&W2*exD8D7PZ zbQ`}!xL@XaG!t$9Y##mtaNk9`&jM_JWC#6zoA5qCU;xPCl6K_HZ`HMdk50lVcJ9Of zZVp`MxF#|hU;=hIGB?OzFoJcOGuw)N@Ti^sp8&S$E?rZ1q(3E`x0uH<;Je z@sd^-(G^H*o)@yc2U&Q-*_0=_Oq#lOL%tgCj47Q;d&FIa^nj;LT=my(@^iis-37Gy zEZEa-1r8xVsX7QNRlp7SK6fM1C+MvFqx4IE674(sw?MHFVTLd#wABrqSq10|Gz*17 zp5Bxljkt_$c{L1JlRs`?wk9UpD-O8Hb83bWG2|PjZ2A-o0@bz4=OUKH;Y~-*=>wUf zR_yIrAK;-V6{Nw@z=IHi)+|YkcR#A)%`~A*1EE{bY8}ehUGQCNChqgHwtRL`C#azVfqHm!$<|92_!!N;>J5&p;x;cKeTTXHNkg;gK7kj#PC>9;r|>9dh@cw)4c*@ zpUbRIYp^c&-L8RZjb&oBlqNb4{;-V7eR+U7+}JWT1c znLfhVowAtoOxl@vQGvbfciXD`l%u-cM*!LLmh7QD)$&ghy98hPggYl}a){L1g*zRL z?S`N^4m6b_H0R!Ht4m>Jgd*C=!(He#?KuDOGzVBO(zSd7Gi~CpU7YWWF?prc4>b4- zNZ=V>I;WwFX8KrPNthR6M@qpxn5;AJ(oMc?v&6il8>4~Ery#GdbQ_bxGOi5tSYT@& z^p&t>99p#w3PEVLGHvy7e|Sh5xXe^sR-R^iCJ}8SNFLj?bF)Icq0#&tY`ztN^A7?b zy&-BCt6gf?ee9jvpD)Q@od<#F*|~^p{R`A4L=6ceZD1Vd??@Xh0&w#8QTz(`(y!8c z>HPoMONUk8>pZujVrOiEwJN`fyqVg6_SA}p;%O{HN;g%KWAJX_r``TvKX~c0G*S8|!y7z`^049{0V@tepe~{Nl@`K_aCMh+G?{_3 zllEF($`E==MTnM*CGCubMyauwbKKAkA8=jvWDC){Mk(2b!q`o2A{)9w0(#aY{qSQ; zWs5-d)vZxIL_mB?mH5?63xQY*qhYV(GMilr3m$hrK`5%sM(n93VA|?{wxh(_^&g2fy_W>e2v*Pu?K!HG%hsZlK0mkbrx8^+ ze}Gh_yQv+%yP)8UNL`n$o}Q@zCOWWU-}Sl#?!Mg2X#^0bES@p>?rU2B{XH7q^o(k? zd-_0u1^W%L4^U?m;y9N{swH!kT^r+gQapQJmngF`m7hIlr0$^qctei8eI4k+(D|u> zIJ)zl(B+Pj2TZW@-OBB?n$I1J)51vi zuUY<}$}Fv{0b&TF8^yV^2SD(^-}dNv)GBeSxP1@6u(|fFtw}1@no>_#GQ&Iy3Dfg} zG({yGrq`?k+5ky2C>@|3l3Uk9)G1Rr$R~PFb5}tA+c#8MDP&X!Q1!g0G(QW-e08s3 zLjdlJvXayhIw~mqy>$+1f%`(!hUd7unDdjorjt}X#-VN!SMK?%XoM1iq#!Rq-W^H>|^2xfG z--)-bg0BJbHnSyDgReU`;8&|`Fc)|&->hZ*g5)=3g;_y_r+fplA*Z}AJdoPzreCGf zXKHAciU%!G8A;c+R=BGP?}St77I$%8%3uG9^Z-b)v+L;e%I(3H(Tl+46!Vcjp zc%FW;&tqZEg~g6v_FFcE?+M@uj?0Pja7c+S*q$<_S;eddNIV<{k_<0u4kS;hQh=cy zV2h5N)3`7ydv70bcHOF8iks0H*k2YWmzP6YC^Uapt4izy1o}l2oU& zAgEslnR`D>bNbKKpdERBfGYSy^nPQqLPhQ84()AcVpasRTU3blD3CC|yUjO(0g^wy zPBG|4K<#c?6fgO~g4Dj`%XR9?M+puP&q)zb*7x9ilzbF&QhAj!{=6NxbZI(=mhn5uxTm z$yVna>=E^v4UxT-{ZMjqrll~k?3PF||NT#UZW&)pJH~xhgNXFBgr^IbC7r;=L_b;< z-k1>iQ*CK;~hsiq)GRhqQ$Hzaj;ykI^ahNp;_vopEB>Pu+sr|HCJAaoslvw8B5am>sF0 z{y-*%FT(ywx259~I0h5k;}@*GY>&W9S*?>hJHiMQaA8icp{DL2MZ5voq*=E{vJl7<{v^3>M4KnXl_7zG3zwULt6(GFpy>*I z95kRo@2b%K5&T*--<6XRx^f1h8*%X z$zTMLx{Qk9h>%p~&>-f%Tu3D|mZ9;hyCNVZw0S2`&;p^Rwu8rSMUys1v&ssoI4i`h z4A5d(t^`!?LKTpq_Ky?G-{l;8IJ+Lfx~UUxdOS%U$YQktmAUWQH&?-wq&$&8L6P(> z=By&$=@QeJB{>Dw&Yi*gH-PNj1{l=9n%H%V&T5HQ>ms3EP8_T9apoo%YV&-T>0L#d%;&(gwjnU7X{+U zP0|M@ok)e)VI43mWwEaK?x!aOl`;DYc zerm{h1NRkW+~9etrBF3dcuy<-HVGMKxB#pCiJC8Ho?+Hl#0<>%^*O%!-9Wb~!Cm5R ze=67)kGt_I{^&=ZrSaF_5OUQvE9ay6-PXYK(fQp!Nc|=wA&xTqzGXhmUP^2$hBVH+ zpO#EL4OS5~{YEL@Zm3hltywSZ!y!j^#OlblflS%9N;DjPw)iMAO zWxTDqr^*<(T^NTyc2t3)tD~TJCAu8UEoLvWiI42U4QPZ@nhLW~Jr|G!sLj=@Lx|FY zn!%}222s=#Est)3aHM~rH~yOOaFsQ;TxH4L-xaMDjC;lerY%+g%ju)3-=@Krg{l75 z!~GqC3#h-Q+fGj#Cn*8Wx{E=&;UQSlejMysxt(1W9zo|vSJVxp!#8^`D7@>K@JZ%& zd+~rSP=Tf}%ohem^ZdULIKtD9P%T+bOi-FPJ2YeGql5|1LCKng)45hT>A<3T{8f-{Qwu?I-#m}DHk zi;laJSNFBIAaX0}We&qnkD@lS0k43YnPdXXeQX~zD>M%RBk@3*VG_?I(2i4Ib(<_v z{qYTzqzBNQlYsy5n$_|1)M=rFN(OE{Gw+RVbd~RZZ}*tRsExClER)$V3xz) zGpA1;wi;mC7banncbx>OUdZ5YTT6h~(#joM`nF^ycwc;y`*0KoZ0MOiWP!KQ9(5fh zu$^N0)Sci9$7JiV%@m(?{BCpAHUHS{U4eSNz%kk!4U{_AFdURky(`Nmdy&P)YtSKU z%_9FD=hh?zO3y|~yOHaNw5nUNLD0E;GJ%y+kDZq8aZfFjj(N`s)oAdXoKB&^oQ7{IT?NbLz`wX@(MR*Ag7+fWo z0Dua_9u^qZUKM zZL&z@@<3Xfc37rGTwE&(bMWAX?XBJ$?&*fH-Z_&Jbn<85QQ+v3Bp`gn2y(6_m|x<+ zN?9xxQ9I@T44=(6{`~Ig6_@_wm^!Y-;tJ7|=A3p>~ z4ivmAfoOm0v&{-I1d}2)DVc~)!kTt|mq3EKxYy6$Cee;-shK%7Jxk!}5S5pP1d7%S z1~>`_g_4`nEW+#U{(}Qj_IT zkL(r=T`X+D@#a3CXC5~zE7z63 zz^6OYv_s5#5@dm7wB8^ z@$~ZdSaF0Q*!HT)t-h_fM)p9?${enZjq(CDweeOg7t}U73*@BvRzgTWmjbdRL~T^+ zF+5f_S@-Df8n)7nkt4dkwXsnG^!rV5iooK{t##Djel8O#MM8H_W7+G}NR_hq0I2x} zc3if z$tqiSPMMPVlj{BrYjvV^%}7*0fJn-V)yKE*_7@di-z_Yb!p(epVBJQUqha2r+oB5P z61m-LoG`ib>P}v=P`)9OasQd%*k>VixeOD4Qyx00Y4Q9Mi zy+yzwyCe6TZhhPrl3wtmCr0)>{vI0at9snky{;9B-eS*a+~t3c=`*`0m;o?6*&VAS zcI?}|H_v=t>3)@n3HhOyX{XKi?W|Upf|$9_x4@hix>F}TOPCSHxlfZ=kIC>O$2GYv z3)SZ1f^42*-Xa@6CmX!q8=tAH!eG92cz>PVh>rB_gW<|JBk?)8^W={lU<0zDSyskD zqDG}sBo7mO=f2{tFS=?%C%Dguuq2iAsO6SAEa0**s1N6lV-pFnM=Mo zV2Bw}lQYhUxE13*cb?&ER7Vx~hdoRNBYTeRsSxz;lR3tTp~>bs%@&M$tbgI_>x){` zHgnnjvz+%OWkaWBF4s653FLchk97T9)q8DIP>4k|tJ=?ZJ!{&?O2=6{MKe9FexPkseEMm9 zr{Ezte2KjpH>?%ugH0`bh^(U zCXKxm74+Ggm}{2C&q|1^wI;z$=??Y<52CDr)lNvxQD07ax?|4HEMX~ZKg~XxtWOEPYF1YL zhEdE$RhPf&PR2AldrQzHXWA`$!7h<*j(6A8_jAv@JJ3*&A016{kk;0&sz;1i*(K4# z_9K3HDQsIqyvCf06;(IY`?N~fB* z;>`!VlP*kE_n^IqwT4>+1U$sa(b8^J6H(FX?KoT4qSOMSQrzfNxW+?oOW1bj^5;Ce z8&yd!!@Kk$x7G=gJzAao<(yH57~8h=fSN7Goa0pFqe+%^=JmoyJxKc(f=E>r{!!p# zDTZ#Y2)&0XQX>|f{?0pf-KdP!(v)g~T#TU+Nmt|O_sVVYdoBFkI>RdcxJ5L!4;M|X z>AS30o67I_q>(RkJ0^CTt7EtRi@-FpL>@xJMZ-~sIc=DMmw4F5#G}1Pt*VytkwaOG zyETUTT9&Qn!u?#m#-XAek5ZEjc4-x^4SO}d%~npjp$Qy3o(YA=8J?ugjs7_wFzOmp zKgl`bL#FwH-<|}zi->&SO3aBoIeh3{k4xZnaj{*Iz2zi{E~_@`se3}3*4~KCcAm|M zXuWlZ0T<9@3ZFb1|3dGQ-J$11kJVmnaeEgZxtD5n)b2e;BbK>3eGg?P;);V!Rv%9K z?QC#e`W-~pMXeTpURS4($lGV85vqY7LZ~&YQmFY5=wt3|TR}mA1xp@pyCN;0VA4uc z5h_TE#^&`9QSkXg!{?9OgDYxzV0l2WnLBjaTVke@Knv}?3r*q5qSx(~zOEo^g_ITN z$aKl_nFYTrAI%vV6k_FPKQ~j~c74jJqnKy^mRpn&s9UP#uB3OvKKBySt2Ixeeqb`r zxZLQBZakCl{T`eDU?1B6C#QU#y@DaPt<45|1)}U8g-k!U=x%Rhb-tSig0QcXOY$GUzj%G& znHV%exW2i@)J=og)6~LiA$H#avN9tdh)=v%mfgeFy)t^%;;GGUT={CL*RGuBpSYgP z)uvj)yS}wWRVdEwx`)Y~&0hpJ_i|yfBpMD``)$V$uCd;}w(?!z?r(K0uei^I-$LRb1?9?mg83CHaeaAVM~YbY!7^KZUY`FJ~1f|GXD`wlk-Z#lfD zRC_E^$!X+FB3CMJYdRaEDAwa3x9dZe&t@JhQCVFCSL*D%6rNUEszMVI@K9(+v$&QZ zDWes`P&uUcLfv3^-tywyhn>i#iY$aobcf6C9%bL|#9`Kt=J~D+8gBa=o!+KJo|!g` zd|dvnaAR+?mS|KJ8yu_dvy=#kRTWsu%1X%Tq?EgvYAve*#P%zt#Rdp)HPCI zpe>R0_-1vPMojG92{{AOW&Y0A?U7^2CuS;t?B{SipEw<~_X3{#USv~HyLwHe zV*R*8SL+Ow7?Gqp8dO1*Ql`DsET-+vm(F9WXBq!!Rb)lnDIl)eviF1hUiqq$RGolc zUc0$k0r12nc3!qNnLppYrJJdQL%(H9MS-1xvvI8yV|xIHql7qL6H<0#+E0d-3<`m^ zwQX|LC>hDgsm`B6In|m=p<<_xBsUOMT3HftnIn$p!p195Z%2!rpFSd~7YdE7venS} zqQ-uo+r)|G>Biz-bMfJeB?%oT1zyXv366zDR}JmD8Dez@?=d)j50WLLA@s~cMJe1| z5ybO?>t(VCJ}pOsMeBt`oc}prBQA3>#dIHc$Ic7BZ?w~&Y7E4e_w7fzct{`APfwM5 zaH{XQUhv5yWo-xI)8Dd$cNOCAy1ImMrTDiCET6GDy?BCe(Vpb7Zsz^oA5kBY zY||SqB*tN)@V0jcPw1b-pGOGa56IcA-@?yn`Dx7O!xvK6gCzJAW=1;N9uGNtjo7lg zZe8h#_j{kT1)bP&&InhzH_r}x7CedjjQte@Nt!SEn(h&8xO~4e?Lpqa=ocLmzaDSC z`SsmFqn9ix>EYag3jX^3XLzpYi`ZZaCh`MYupdUgpNdyc9%DpEulmOK`c8wSgv3#h zd&$H*R&+O%)yeT^#$L5d)Wb%FM%xxnp&8_CJrZe(h)$n{l%)Lf>rUBNQbtG-vp3=9 zV0(LeT6r5}0dev$w)(F9HdFm+(pdpRX$c5sL!|KrS?(kb2kE-!hq}+?V`&DT){pN| z4l5hu!I~hHHh-W!Qrs;x+g@tu_;#}YkSPbp0B0n7ir~Oej%OnC;&oT_6M{e>!1ZXg zJB~44@>%{C7gn7(s(W(%w(FfyWiLspwRcY(>24KY`~0qaI{g@l8lt*(U#?LI?|OlP+F0`*_XSpxG)lTThcDhi>Kr_uMLri4kz)6)(m?q2<`Yvn07T zhtZKM`%*tzjBK*L)wSbXS|ix9LEIa@vyDQDWDAaYE$5RoEalxCY{f)K7sBQp1PdTVyK zr0I#CmCdmNxvJW1FgQUGPQ0g)AAUaUnS#2~LZDHoz1@HzPbfbWvd5oScWoXf$M_jV zdgXmAH8?G8@BvYs5!T24F#5f8e-uL(Gh$%Pg^J=Y=H}nE%&Y@!19w)Y1s;@(=Gn_F zRJ#@&$nv)9@-KWp&ts`jT25C4mV2X>O{3tJHK`dcrF8=RN2_{O`7*Zax7-PBb1Boa zVc59(Z7`9UF~o(HwZ+a=`ns|Ft;mU}k&+i;PWIgX9$l){*rVP-Dnb|fg2%%5Sn+>m zr(Q`uezJ@h^KkoBoo`S>H#FauphGkUP zlPC^H?PkW8tJXE zK0YcBS}$%IueuB_LCpYXV|hpEbK8~^d}n!Y_dO~$_4l@&J1Wqm8}{Z(lmrnM!xrw| zHe*t8EcV8aaRQ?r{Hjoo*q7=l9N-zF`V1QPlft^`VR>Ux8T9p?X|exO8Kjj@8x6x#CglrUQtUH(N(Ov8#%OeE9SdJBF8TRGTYE=H%q0$z{==WJ(dq zdDy!zWT@x$1yM;`!KeQe$0e@4v(GaC)xM_5``&p{1Dd8wYVXX`On>^Vr7}US^0C~* zje&ZVnXIZDcU#y0D9zY4lTF@FnZVXN|&W^Ql8DC=eY>swG zGQVMBdy=EGTTvP=&v@1r_c1!+Rg~Hl__9=*OkyXm##&r@^)uKlTN1onGSpxM_YL)r zNx@e5GwUOcDahK9L*rkSsW~1_e6!`E4%3;?7X=PS&+mK&o*ug&e(vvElO3D2Ntc9P zTaZ3Q4`n+tcpoQDIcRXd@((1puSxHS^&Bg@7MtW*XM42VueJB*s>lB6Y(}ylgmNYM z&h&biFQxj=*8U@WE=LDvSL?S_Y&yfrWN%j`nzD_n{H{#T2{p~#2Q+V3S{Q$NXceo9 zbxIyNEA`=LXU}%*(7rm>N6tbtvQy#9)?heWJRHWoA?e4M$>ZuQGbfuX)=_uQ87Rmd zOGH(bgQUZO!J8f8MV`UH7S)=;~P*9%=4Lx5ev5N=o##N`;a}DK1 zb(**BGj!bAm;s`azq))ODi+Qr2dmbPfUJGr9l2)5ctdK;)_=>|;~AGYeh~N0(+vbyro9 z7+zSE_GR4a_~~;|@Ij#arMn3SA5Xl9jSGL8zsvpGO?}(^eGkln$f2Uw8Yk9KPSx+s z>v)@t+~EPd*(5Y{a_mla*4bQG$lRU9IZkKYEvu`P(b%C=ca{G&58@E1sgG$?SV+#! zvUB7#=XLj}(5UNN2*ssLf}fUcx4)3)#uXCHHqT@7-*y#<08PW4eTLbyZ$+lwF6+c#+ApE?{2edwO^IA1q3vzl6g1QU5{IMZ zE<*CV_>s|w=p1Ri*=D=h+}}?iX@`&J5x5v z!N7$}zR)H0LqN{{_s3K@ZP!MxO_M5%(rz$5!FFMsBc#yZTl(NNL(J&iQMA{PzK}+b z-qE8mZmC1o2-mEjCcelh^R5O_QDWGDfpgt?#q-jPuUg4?#QlQQj#zbjjo`-}`q>DL zpXC;EU#s&}X9gh}eA)FbTz0#%Xj|aRj)uLvHjGFNaHptF@0CJ)n`u?Pa0K&3SRB;5 zBg3>tY>jGGmi@X^Nd-lEDRuYCz_1evc zxiQyFJ{MVL_)#u6!PT&G9;oXiFAG}32W#QEQg{ZGDA|smhmKlGjBtQ7{l?+MbBdk& z@bZh{Iu~^uKR~QxMm%M@&AsVuTJl5W*WS#688Ps-*WU@n5+sLk=VU&+2jiy;8Za~v>ar6*TqBQc2$M4*rZr< zT$o8cwHuj3p@9Qs?={sNj?5iZIDS-F({b?aJFxx(-y1P@1`GW<;p1zhCP)_N(Ki*@NWfmB{XDK&dFI4+ZwZ9`` zw}ER(m*JM<>P{7<;X`Og?j5NSo8Apf_aB;k@TpGVP?Cj(;N7M$ARd&*ImGLfY6_ORK`!O$}$ zjkdska-3Zc)}G{_>TAM^A<(weBo-7y7K_dR#v7LwB6f-il-ZBz@AS!%Kv1mooF7w zV66OSWfwa0%#HL^GPTy3bwI6YS#gASQ^++)qpq6Ist!+xkNgSJL=NFwuWzifny{3S@S}5Kfk2X%oCS6(E z%ynJvJ}ev$#IVEF@S}l-DmB|@86$g{7vg4LoiIc51G%t!Ez?eRp|~Mc16~ZhQjX@O z%e0H-u-VSq*(ax;zuP#ad&TnO*tPjrAn{*Nc*`1;*TDHhP(jLDW#r6$?Yi?E?Ix99 zmJZi?v6$!PANvgM)$w&kGn#<&C5Up+%ObTrslew>9z4%ZmJ2^@lld;zYcv(~1EQ0c zblwTQNSd64Vspt5fzh&QGpMEvo#%3Z&ii2Dy$Xiks~fweDa{5Zw3imDf}NnOa`wSa zeCChf$(<1Q_&nEVMQ!m9200$>LtJ&nCH_j`4=J9*G!m4^KLHZK+t+HZaM6|BL*bm? z!CS=#Yx3;sQh5Vyr?AcS75+Tv)m_4cY!i)yPIGK6;r+cdZr(?058TQzl~E`dIPTCsx-%B8~zqKn;vrSrzl&#HiymgIxD5#!NK^6~k_p6`A7(V{J;`)Ej z*{^J2&-ox9lJHP_o8qUiQ&X!lHZYriFWhw1nyDdZ*O4NeK#KZgoOJfp4};LeiDb?l zLb+3yD>(3T*LIya_fjv$4Xq=p=ejcIla2mWaoOOwfx7!EW9LVHV@6yuh`QYA+C}j7 zB^n9N+`xKxYUNWfLuTg@)kG$K?Q<9{=SA&yMMBXOoF%5$R2ZyVn5uN;VxC(2c10lP zF$y(ikfkA{>lk$_=MF{cC?U_IKXZ!EopJ5FL88lx|41*cpO=)IjcK<@5D#aF zsQf5wv;NGH6PNoR7y-4$p>V7DWFiQlQPdLR}?K+EqD-sDJ23sZ08ivG~E4V%?(;wbV73hQ@t} zWz633IQB=M&Fi?6uKemzftLYv28(j-e%z9KGZj!`%UxBcE&>?jCiM4zkv9 z)<8ntC$paQluWbWeWTWl-tQvo8tJv}sGWF@^85vhO`m_y@m}$Nw)~isag_IQR_IS# zL(!%UuS8F{Ix7h!xV;`G=WN>ZSZy82A)--R)$9_FbwEc`vS5a_&codjoWt7=pWa&) z+GoI7ziriM>rC#9(~mU#yBTJs%@}Hh-PyJc)j_pFL#sde1unI$mTRYAj`1e`+;KF6 zuz7y=#X+J@WqfZ9^VVpXBih+;ZU7TbGA0PL{FP8pJBNmX?mU4{4sU3{xqaRF9`5=f{62v{v5=B};R^oI!06h6mlwoXEw`MJN% z(6_kE!Q`ii;QK#$eczT_NvkbLMm!pzuu5e^%o=a6%~_(j?;b>M|&k9I7NGF_c}x0#HJ;&8`GrY z_fou`e<}!0=x)afo-erSu5l347{$Y;*;1SHEzoAF9Ej!bK_l2&a`a}Mx>4fLwM&ma zpRAKe3&#HdMRX?!4)?$oYwhTpn|Zd{4|?2pJNiQWoi_VXj;dG9^T)(RtY5P@NSj+M zo|3g6scB*>{Iy+5Z2NkR+l) z5+YjM$*Pc%L=mEe&@!{jUS&i@q{t?sC1qrEqfn8IQeA}y58kM-U&Vw5I7%4c@YmJcf+s3p zrO;Of#ap~!r8q^datkv;eCr!dn~!i>!Qrs?w79W0*SJl|W6sqqNtIeA&%llD|Z*bsFkJ}|6$h2Hk{F|0+o11%o)41*>!a-I)&r1V(4(B4WA z*C>`Kr<&`gc$hW%^;-EVOZMP5abGIce2!ig`Ry0}bPhIL+2bYxFw7Of3~p|y8+=C6 z@#UyC(Dz-Gu;jwlWcec|o1Q{}mW@_Ij_X$#IxeZQyXL;mNtX)ZPc@3z(~A{ZKS);T zwqH6fqdAztNm$;Sa%JN-y*I@N*c@!lwkU7&Q?mG*c%o0`56aLwdFb0@3bVe0-<;z+ z&ma;Qq((g8COajn#S5ooBSsKFwTW4db{oD|)b(Ax6(g~f3H#D6VmY9d>v{KoG+W8b>k>GiZ!Y%oB zo`Xy((V=nLt@N}Y2?_Fc9~hjFsU+ISr#qMnKDSt0TUVxA>70+eYIwMexg1A9SU`hj zc^bQEd{2#JiKCdic8;(P}svNGBcl}n%-N(h5I`u-wzE4$c59-p9;p@h6XLpf>QjRKa#fwT-EG z0FRJ^9_p4qWk=U129r+(|MWB4no@o&j4e4NF#p)vDGG)cpJ>H#6hlb#P?puIrdS@_ z022Xu4Sl*P((1UlNMjBiA-yRmjLhhno1{9q-vDIJ0gmL=#w~4BhdHy7J_MGJrr-Tp zKE5ODIEEaY_2m^=0(#E)_-xpIwq3c-SnoxqkwJ@4N548RtfL})_@}na-JV(%8JsaS zTYb`KFKihY3HMD>>61qamLK~Q$-*N6je@hLiO%yKN)ObHTWAX;s9jn*2}b~;8-0$c zkGK@QJ8wNU>kU~$0bf!2?dFF7(Jhcl0BaA&ROhc~X`XYRx$8!YH5L~Akx1gT?76!K z{VE7!*)lwJOQlp?36}negTad>+bTe!*XYMk(C<;+v1Q7{wJ1IRajqy=AQx+F@?X#WE1C2>Y5n3adNPsOXydfG)SJmTnKd z3F`%1)%XDtmJ{xG?GU>v9PAD%7W;d-45;&8OIU3=5EcB)Z}&P+%|jx+6Z7|>q4@lz zWFR#ZF}jjbu{M30mKn;1!8ZE1wR>ISA4$xO>Ga{Kih3%{*h&2O^0r^Uy;5Afvs*9P z`ZL^ROq9B{j>DzG_N884ckSu;1Atw-v{4h=YC>3v5t)zb6lFHP(8u|@YwRiAGSt!3 zL7eMmQlli?6WLY=;viG#ELTJus?XDQ)l)^=q&cRe&f-$Kp>#OWo!36{ZPk1}aUy_7 z(VchQoBmnNRp+aG(@Jvu82w$px;;T;mubizeG0kJ1II8Jpr>17;$J%acJmi;!=7?g zz{G1PCTzT6OCnsaC<6CmSU|Tf77(^+FI(oYUo3(R!dkY#!z_gRTi z?+OY(&)oIGW8(zoL!@)X{u$q$H?Pi&^jP4&pPDk?W2LozZ&Pv^6jIVNpWA6e z>FnERL-n0&RkPTJdd9bTh>4scOHEJ#TNfsLnntNp$>NQ_J#hp4LvMZr0K0QjQ-<9WXxS-qG2gf#O_S+)(e zgT)NyEnD0i5yCmbAgjP>^u&rvld86wCwMXQ- zroH^;hr*UNnbk(=QIX${H%PWd_8DI2*ENg4S7)h~Kw(;STJ_m!F+YG~VhqHgt?Y7g z3dA&9o#)K-cCWHlLtGsL&=6in{)GtNdZ@}fr4eW`<6LPfD*LUug}d8G+)u9U32E|r zxn|T?s)6O4LfM{kW-o)J$!uzv%8k@-QjYT->ZtDE%-0)pMBEx}mby53n!+I5OfP-4+S}>#rKNcU zcI;5N9H$9$V>WY=7Kg-$!w?!Wu-#aXGmRcjWS z{Pu&Py7s8ca|r$SE?k+QBRLAs?x`gb^0%va*HS_3mC&Ix+4@9KGJYz2AWK96)c zI{0p%X1%7-kA(QGd4|>l%I7McX+D-;w-pBs+>VhqRwLqDQSM02sz1db?Gb{e%#zls z9X}j*u*CoP{3P?XJS_9=lS-qvWWB9_Y&16<%HByBcK0_ zv)B$sdUYc`h?JO5*taa!t&h4)DF_$dj4g*Jsm}%%Yd;J3>iDp!XX<4;j;e2nBY*N` zXSKcSstva#HK5jnrfdmk-3S{hj$DHcWa>+(OYiOa2ul93 zcv-@DoU53!YKuPCLBf2mOqgiHH`Esn@vbOy9y%>$JoP!rjT%H9(*eY}@ZnjBj^N*7 zOBffOaqF9ceLY`pg$G+Xp7SE1!f6LXebK(>Y_b(+m~(vD$aIH`M%NQ$F)qx3AOt|q z=3ize5*R#Ag6%Od_(X1^y%0s*5N-b+?-9NboA@v*{LY&1@T6DXqO8{V-N$4Bj_`;wt$+^0`pLB66pHB4P_B&GVtbhli0khb znPsJz4W*Emp<$acqr%XUyE$F&x*Oc*1S~$q(Ib^^wqs~+Pjhfy|4xt`)LTks>Joz` zNcsye*uL8NjCXVKT_K#@HXs>1Z)c0gT*S9 z2>gh5(g`1HiUBd$4)p9ngfT$nBi>N;NU0m3lg6(5D!4!E0L;s`^Sh z&Rn%;dN!y%U|-@r5lTtIM>di1kxsLXDfBBAu|>s8UD+Vm<)E(iuenX}Tw?AtMZ-o3 zQEU6Lsg!6#2h1u)0yQW=vi zx()WecI)3pgkJ^`E-=TH7;~)Zau+|DHvzFP(l%bryi);0`vy0G?>UBYcpB4`-&%($ zKRy&;izwm=Y;;UB9RAiT@3rmG2I`}XAG(9hzHxG!x_Vv~O#helyOXVbh3wxLh~{3F z# zUy&+$!~|9bo%L4Br##m6rq< zBoR}tua6iuvTXV8l=Hbn^c>cB>EN|73 z0_|G)nq_R(@^%rH-gIcoTubMz6!$ey#ghl88A~l^w-Oy+r0C$mTVASg`@SBjaeR?F zohV}-|1M`35hiurs|Utgtj)44vp?F;WqiWnW+nCgk(e|39sT@NilEGhWz((Cekru` zj+WoHY?>ISIAG(oMR#V4U^p!+f%6gPhk-9Fm;Kxzi3 zG?+k7A(V8*@Lt@Bzq!V){XD?s|20CvSMx^n*;{)MnWdPo&P_^s6~z0 za;${Ud(^A?dXMA$=jTrH7CY9q4JBe7X7^!V*v3g? zt$M$-F(_Su&M$YioP9UaBr&&kCsPXjjcY}MEd6aMS40iktAiQJ$^cbd!hQt+EOB6} z?Ko?9R&VrBbE+=kAuZYivjpDBP*0{0&Y5U&yK`37(9G$|&h}EQPPdlmc<=o%C!K<5vBZe1JsQUthwo zXup4_c{z9Ot1;59OA4*ORl(AxXAfCEK7{nSSh+9vYpzP15)5*V=m=!2tSqUZhHfy) z$DPz?_-}*ORXrbD`G)$>MLFvT>HgJkOV@))oq_NhUzy-^GCrrJZt&(Vs>d3LODW3j zp=9-8Yybh^9Vv{~e;eGB{yREb=b`t$y=jZvpmJGJPTZYD=wtV<`$?8G(SYLO{ecsQ zP1klmHeEn{A0FvwjsCUO)Tc+=*1zwm9bc_YPN8HlbNLC8#!-PI&%ES4_c2K)ZNc|O zyLOJeD;>N$9Xrx>I^TS1j^Mg-LoupsU$;K4A)zHA6%TsU>bJkLaG5?#0XWu3NGGmg zFt2nlZ^VHBnhNJl`Xha;IO6cyyl%fC9K}Qr@Q6^>+>@<9ndvv%r@Fl7d}yUd-;DO? z?Je`~I_WALHpAz8#A52F&D<_a#ZEKB^lS8%1Tl3o0uNNl!1EXY?1QKef1IlhZtIw ztr8hPj&B9Q657xo8MR3}%$8EP$au`0bKx12^RaZe+Wx$$Zho&UUVK$GGs3U(Q@?s( zvP=n}=mEcay?^+CakO;}k&cmjZ8Y~yo%kuW{NJq;1R^*H2F?!oQ@9T#65_Vb`&q_J z08BObyq$wMJTnr0wc?o>mltNbf3YaV*J0%!N|&-H0>?`hHJXuut@x~_(xSR-7|5KC7{Y3Q5a9QEWQ%+2 zK0?A7)c|MAVccA2rFNTFRaA54NqwM*$hUuz!G3F0S@Y})VVM*oEHlXE1WMerl_#4B zK>xR7B0!7E?@0D#J{^Q^X|34ly$Uqa)xJ+AqI-`Cy)B;K_HLM}vN&pz^4B;nmz|Yr zh)NxQ@Rdc@iuXm<1|sxUU|X zl9{#CSVzQX4rTMPdtUGUlCbw}bJIKI9G|CU#WGa5^O6s&>rrU=Q0%;8h)Aj{Bs&X-JNk+_t) zWcbO^3uOpX;jz5A2{^4tp4O%2)4II*1oM*w6nNw;c#M=}K__0BGSNQj{%$)cr*Btl z^F!j?br3>+)gf9rRy}XJ@pxyij}Cv`2mNKcO}PfQ_{UO%vAS;hL2h@7EU^LYruq2h zY?l&Ny&KbLFennCZ+YT@T`Bpq!Cj#5yZN$TK7u*Z3lx5@ zzgkJI)dwvF!^)SAT^mZNw#-s9(LRW9>k=%z={6&-#h0uTxrbnc9NwR$Zagyejf)$+!P2nsFo|?3$Deq*0MSRze(}&>H@xb; zFidW3HlGdo7u8}5^di0p#)2pixCAIs>DU@CF~ZNoU+861$Z|6{0@YYCJ{C)FSWmV0 z^V@1Eoz1P$*p~)BN4I3bnb-@F%t4pS|62D&8#E2?Zu**N!1=TQG%g9zp_N6sSAwqX z8A>!Pe?t%;XwSaqeK@%99$YyBUArNyGF@ z#*F+mf%;s3em@-i7!*l~e8N%}yWOcB`VQeT@`>)~3+j07-u)@0xiiKtP0iiexQOtL z$hF1G*&vtVm6MfqbFA66xTP=X>g09Ao3Ktw&daQ7iyevPXv)NQ`wt%4=F-@9X&pL| zk#L|Jy|yao?SXaYJX6`1Xt{WCAetOpMRgY}QImSV!>`h=di21>9ETHSGZb%J^5KZO z8Co%rKOB;6^q&k067u2Kz+9aP*nL1&d9Q!{{fD$9I5P7wUHL#`KG0jI#VW6Vl2fZ( zaYc|xLovF`B(JrpahddJN#LAdc%e~Uu`2hBCDVS#CC_%Tm7Z?!?^^JZR)VJ4{J&DZiKFt_;y0cAT!aZj-E`p>unM zEb0iSKjrZ=ISh-*ZE1*Pka1>#`8j8YvN%8mBI27YKD7cw@IGr+gZN=&pDy^K&NdIr z#Aj3$9QAY%RB`G`c1reG$*YiaXcaQV}Lgv&ec1}DXE2rdJ7n6ecsQFvVM*+Ecb>Y3lMnCf*FTV&p>m4U%51V+NT_dUdMKmgDjaxdpy};?XkT*g0q}{;0BU~xdAj35k zUEZf*g{g|U$O>_et*!ble=7MxZ zQ^cr#CKy{{7PMbUlK35KjH#+gIKT!FXqu=wjz&4wB!Acid8R?WEBiX1n?#q-YlBME%RR7-fq9yFFV43W(-9B=mR+G<470; z>p$3;HX|BXbH?`-DEWURIH?YT&GyH0X!99sBa7a%etAQC>Arok;~S*{>9X6dS>g?n z+gP?_pe^$WPr!3hs6^jLSgwlp+wrX)OG_q1O5_D-wKAmI>w&H57iMev??}xpCIWdH z>rLqXPM;$8fw011wnl~qj;Pw6coT{n%_;g!6};CiY^g!`z8K=SY+@Pq29LSvk(Xw}!bNc-Xp5?rNQ2u(k(!8)_!Yrus@sj~&U zu@S63&*k!$O!#Zu?@r^uSkW|1KlqAB$WPYdm(tJ>22%%(FF{eSrwRTPW$bwDqqcKxa|G*El{$8vX!!N?P8*++ixa%pV1U zEI6(cPX15X-^e6i;;lFZ?H!gDgXt6sUt$Uc&SBtWDZ!;z3^9xYCHP+(EVcC}QzO)` z%Opd=x~D3ys1YW5@3G~~_3;7iE!N|Q`rY=}(7O%SM9kOPTq`c2?`<0Bm5V0JxjkPi zZ>Fh@+JOYbHMt()HD@0oyJcD4usy9(Y`Zhg^ils~Km5ZUP=?$o7mOoQyq|?X{u{x| zj~uh=;Fx{osF(2mRHBaLKihb4*JG&vlxLA8$#l;g#p*)&tN=$@^79ruv{fMG_dn5o z1)oHE{kN?vD87bclJ4m>re8?TF?i^Jg#GeyMpY{5oD|gwvbe?!nN+pyp1Bge2{@j~RLNoEa))~Quy;Y&=pA@OYEqH6c_gx{^ zC}vcnvTfgwj10Pe97zX97q*LhF|%+Xf<|Tj$CDv3gAs4Dxz3p4iY7180yEJ3EZ$K+P3Xk)y@Yk z1G5?3E(Trq3&9q7hyXYaP)kN-5vMT-9nU9;6*@-BL=i^4E%76L)c^_8Jlct8l&32hQ1Z{c=jGCwHe=j-I#lHxfRM! zH+>V@OE#WI#jSb0c}df4yP)a*)+M}G{^?U&&5C2jZPA?Zm|DokQDMt~w^$dJc4J3M zyIC+qP@ny~F!6lIUo&*STiQB%2ve-YOm`%OaJWwkmiBYatM%U4kIv@%l~@$PA*F+L z^Inhft?zyhk9IQ<`|3kJ9UMF13$lCXy%UNa#G%KN7XtHWCTnhE6Bah^V$J;3hlH@* zE+xO^2Jams&f1J;>b`E6IrxdA?$m&keK&M-yl~SnSLw;(fQ>b|J!bNHKc3!Nap$!c zV3TUH^qCtzDjP9x$~Y_LOD<0e*S~oVPqpXh2&~8*o_o_8ef10PfMX&@GGfJ$XYW@B zn*=7~vUt=Gs0{j3Z*t!_qTFAUQBLn?%Udz72cJu8bhXr87L+hl_XKr~lZDwWbzB|= zWlt!vb$-}~(Ex_6O2L)T+&3Nzhecl_lPBrY0kwsYhQBsM5Z+QYRD~yjB}{oj9I<3b zL=;#yy5c`Jk2?(~HW#&mAZPv9tFCD~KJm7qsV zjj$4KA^u`zC@Ot2xr>A0XO{mZ&9tR>0Xzk6XW z{FNTX8HB1-W}J(BolPFD@)~J}23=!_R~$!dQw|8}smZz9y=8n%vzJ% zSmPztv#8(NwBoz86^&PRpcE~0kG`g%+2;Q8D@H7AD;D6%8 z08IA&tr`Mgvd|IZTCfhjzfh%SQ8#ptEKX_SCr?;PJ1HN?dxdA!aQd1Kdh3;xd*(q* z)@RF{70|)sGv`}J5@Xzsi9T9TkGL^vyVn-8q$YZ*BI3n-P0obT#s*Z|_)(`ohwWHU=WnD|pX_>xB zdJ0mVo)I7Ix*OhWr4FB=x6vVSzDZWj3Hz zP2EHQab3UnFU2F=Rw>&-@-&Ko76o8lEKTmrfA0@uHR+ce4$I|RIJWDFI4;5Qx#ebB z-IJvh_4t0)=G=yn2rl`Iy{_yGh`18JSN1UGi&lBbbvBPs#ZmpC$me(SDFBk%a^9V; zr4#bwPOqGq-&t4Vr8psQ)tuGniUn*9KaKfxn!7%9x(~~hwntBI*P?we8$jGDcv;>; zPnXb^u?%0o@%|^@TF<`nFQn}{@bB$hs0M zo0M3#SoD`aU`MXHymERph@Q>0q4e0kJq1Y={hX^96ZY)(h3t{b38P@Xe-ljex&LxO zJ;YKxiNn*s4)t4Ze7klrcU23tk;|ZqNX>i?O1Ko>cnS!?!a)eu9Tb8UohDWlvv*xd z*`7}2U=k(OHY9#tmg5z2*q@+#vVQA#FjUBDIadVM{Q7aha6;0!-!O@Wl69(zBZjFhhpea{)|}ZX#Vgp8dA$?^=8v7 z&*gO=algjXOYJc+9?N6t`7+8&k?$C=?m8;rCEqgbtxKqI+R6o)<(^F05NP0~WRINlk^9yZ>??_uyI8$jj{pt8$PalzL}zH4 zIk`bjopOrB+;Tl=gTxoXbQ2Gskc(hm_1oJO*A?s$nHphT7;`65ARG9O^~ZfZkTj8_ zoZdRcP^qJ#xB7TilGt5vBckODjs&(iJ`ggv->7|pCf5`AR;n-qL8+_skWP_wIGALD zD>eiP{$gN~sn7PB1wAip43>Xw3~T5t*LHhrRfq*`%V}-eY?#9jQXyeHBwmqodNyN` zR~eMOH%MeIs8z&#Groyv$T*)ALy8p?{?b33=)rPuyhN>i>pRs_PA=lg@1MsWe}4_^ zRK1e87%D&g@s$TWSSf+TtiBcz82UTF`qlIO=35arD)y78Oa~lGXS`jgAAZATi}#2 zE()io0`8s*=9H)%WZRojpP*N5xc(-Te+0B>Pqdyr&>(|Rlk?5Sdb$+F?5#_=Nmmqe zRm#ei$>n@T<ANG(c!L-J02#ZLML>wAp}!*%M1*sa`S#9USXM>3c^66q2Ud0=b9 zO|!eEK86Cn2FbXlC~y=mh4gN#A3St7fB0=_LW0I@CNTS$7;c$&A$#)M?bx+D^QT!X zbuEaV^ZJj2(j)1zwGB)RWNg=sD~nxD}{i}{moC~oMg-1wq3-}@gcM_zvbX$EnWL1Lm2bg z3F`inHvFmahwUodnwTFORqBjuQ_?B=UcCuhL&g44^);J7uayHwfCTO8*VXT9w5-jc z;;S4U-Dvu)Kf!^dq83Cd>ir#uMANtM{=`$f6QIoYvpIw)B@!)*^4#KHDE~0nCjs{7 zqB;;WL{;OLiy7c!Sj3vpg6*-oumI2{O~nHhRR=Ff)+Ak$AGAI3%qRW!7d?I_4bjvu zQ(pq(&Ql+)_Q!ChQL>l;!^w;n4R`p)*h6sheXYJ@5^tk*53Nc%2b zf_nBA&S&2fJRy;BH-H@W>SAq=6<*odVuNalVRQ_~)5u^ddrnpgXcmX+s6FP*-V2?R z^b=8p_5jR8NpR5D=@$fbE?9OxZiS8!-MMW!Ys#cU%Rd}W8Ieh!K6%FNp zu?9m>#2KA;-tuXdzh9pUSW=Dt(HTBgc&UCurp{4yEMs&zStOvad0Pf(*>r!4cidO$eo7AQI9kTk znWIMn#rKv@PMd?LEi%uqbFlNop1JNl#Y^V=yc9$XM*9gC zis%&R303lUU4K2E$fp3THBw)y%aVAz)1KI}dFMA*o-mf6?PHe{g07qgT1wc2|5$T* z2?@VYbKi|LB9>DRb9?ml-hOEad44#AW>};(;WQphXefY)3@L?aXmTidI>?Ek1TWe3 z+ojo>E6)WFaLc9NOX=_QrYTf0(c1Zgd89%?j*E#(ZdT4(8`qZWQijTCm1 zNk>)ZaZN5{YZ2lm)&m{({#V$OnA~T~#zVySE}uuEvveJXz4w3ths%S5_(Lb8US`(% z$;Tm^O+T-kq0bDuV(KQYnO#O*8oXt+rBe*&!jF;zM*WgOq`j9brrIxy$LD35P?BWQ^#>J5NSE z+y0L27&b9A9-=e;68@nSx1I2J5wcxYge=mLdA5XyD>lyhPJ-jVqe>*c4uTP(I3sAa zr}{3C+xCC9fIsMOD(zY9D|6G>ab9NivrKd=qJR<&IM`V_zF zlz;UeOI0|7Ry3$b*7M(p9o@Q)x>nKe>}R{(r}F2-b*+w@g@)DJxCR}fn%RH*$&qJd z3J4O9R|+?r63r9=x)gK&ZDS~4Hlqh!!Gyn5^-Et3?SOIA`pd*78EL=(YYnal;b$D9A$k59&+Ogn6UxPI`}WGuh&^*0*> zxAWql3H-K$9$;x}SMXG>e{0&p_5gp#K#sEuWpW)I%7c4uPh!h)S5x*=VC5REEKLos zsCBgJow?Wqc{I_6H=_fAF&$v4xWpXtIP4oYNzM-Hsd&;}>?N^fY^XG3SANfG*sbn1 zP2U84wE{lDrC(8;qq@MHFCyGBL2;*%)ZFizI2>`_QUQr_4}KM*Xt-f|bD;Mq^HlHA zRPVDBuRbVNnY+>pxO_rUxAZ_DSj>lI?&fl8HhO|82!=5+N7B2B3ULGT5-nw)oou(_TK|%|8mDA zf8<8ZqmtIHpRcB8A))p!PrYJ#_WU$6Tt21)=m&2{Gd8E(uAiv;Xf z1Q_numoCbq9~t`EEF|HEU3Qs6bZGA)9csSz!X>X0w!O=y&IQH5i?_=EHu9YC%7QSU z`!~RrvX4!gW$>Eb#jCP=xg7foLvf$x3=-xqd$ev7dsX{tBf_*@3OVM}KhQ_p`Zppp z2&XqWB2nuaIvLJHbx*U#OZTi4~R)ozx^}FnPd{fDvKTBH;p&*nrv~~P{gawzbq$p zVcuui%AEw!(8u>l1n~sf;;Jiv=C77s4vZrCSub1yicOo!!EFp&KUbQy#_4c_CSCB6 zC|V2NAk7U~JA)EpmSh@ydqvMpPLtG4jbwGx*`gyw#gG=bRPZZmqx>M`+ef1C>O!;` z(2bEt0T<0K(K&Wc(Ead8$8~N?yu$51O+A0bsK(xB>i#3UUDw6m(hs8yfprqnqq}XF%XzgRHR%D z_zq^>sGab*GxAcbVxvY@5>k@+I3M!KN+S8b|8>0oCmL;WX=QGLovmED3v~ zwtp0uq&=|=XHa(V)0HR0wllODjH!IDz~1pGqVdQa#Ud`@W{clpfPe9A+W(5^`F8Om z3}q6RZfF^z2M&&~Zq`{4UF>MA3E~^O#&?!I4Z@iXd9|nClz|rcn=M&nv44g*KfXo8 zCq>`z0%dkyrH}LURE4^}-jD1R%olef5ua`1P5D%rScE$}ozF%>3vNiP*QXl(MNaKgz0a{1Qh~`P)dl5CL^lMRqduBdZWch+ zAsqvR6OEVe!z_WN+$2Pc{^M!1XeBKhSHnud@740mcY1CCdup#+n@?vz{h*hP%iK;|otHQ}V*{DfXs=E9 z7vK~0$UWYdMN`rg78n3-tf!uQ0-#*((q{V>^!p=~Hj$HJOn14ByfW`7*Z*8LPGBJ0Xv{!6z<#!lql}XUQ{MWHil&hY`x&HfI6Q|?JweqF5=sL zOysc~3I7MFo?^vQ)~3(DJT8pjs9ef2WymTE3(M8Tw<(8Hw_avAO>VA^T-iZK3-sq5 zWq^9gZ^04q_E+^K?iIstL6ETuiiekUWtz=@tB{y`3ur>iW-YgIfu^5L!!p6u>MFYf z&+P^tYffd`(F(}Q$ymXBhp8vv>Hf=3+xcc_7^u%cz1X6E#%F1@`q?PQ{A)6dRDKLa zq*>xG8-1_ecGJbz=cDkwE`L^vC%J~p<*R$eBuW=hH_6rp1s{d-T#Gp`XKey`8)Pr^Lnf4Zwr%rmurXO&Z*_~CU(XtNMYJ3$Q*4_SfM}31y}Ht z>FCcOU&^esmS4J0ShipdtE6aUh3i+|AHl~@t~a=|4P8}peDi8xRV;6<2xb^-vsv1s zUSNBaX`G*A<-|9IrYnivxzIXvvraNjDOUdlILkE&kII<2q5RW`H_kl1fWs7 zPjyTCrfkr-s=q5WO1Pf9Fjd{nnm!hKM0Z>r#?e+)qF2g!7#-W`g(}=amhc)c2qJtV zw?1=2S-*-rrZ;u6W{2SZuP8SXlhak-hI#$OLT%BaDRRiz`@8>9AfxZ~*v&>DLm`P9 z?LQ2yNFkvMFXSN695B@Pi(mP6iSE`dzRNO9h&CSP_ zY5_Id|5n6W4M`)6a#%+n3v`6Eaj&(;{B?`aGD2-Uzmia!Phkrn0z$avvomq|#CgUF zeQl};2D4W2BmVfid#;C~eu_3#dU*10zq&>#733ekwCmy<>_r#PKkWXsQ0QFr(AXz= z2vc<`{nV#Qr1`eVug;asaAW6qhqSQh9tcabu_P(7J}HH>vQpeHaVUM5&VWqqC)gc>oYAEfUcT`Oc5T|- z@bU%00KQPt2|}8_08~~^T#ME~`uGIpQOG5H5~g zcdcRuVT~ht*BR#XBh?(6SVHM>c}%~#3kj52!}_@&ko6?J_!1`vb^`ax@OK+zOO}^+ zfH&6*uQDi1nT;JN=Vz$1_^8qA?pku+1`kke$keQzSl$b9Yvd)Er)- z7t@`9Qjv2nyOy-TTJJr_Q;jMgirxOidb$!{&YVn*xkzaGL1|652;M`5U+^dZt@5$L z`Q>|3g3!NCm$%y(V7NIg!e=vZtyiF-DhK&U(DL-~EaFuXoW4PRT^H@pra%oUnC#&f z2z=912B-ufXRz^x9LIo(#$yEbKvePIeHDlQTNIAi$>Wqby4oAfO1FOb+UTh0*@h*0sZY{O8Yx?_xMXmtd;CfZ}}owJ(^ad3_*} zxdrG)-r4s9<2XzA&nD9&n|_)+@&<+_gYlv{z9st;WB_`OLJ=Ysgto4b4a`$|p}+Nj z4kEbvL~}n_mH~8MQjtB9r7m@SaI%6pB7^VY8Wwx;#XS4W&^zfF_M=a5DZh0o{e(jv zhHK^=;)$>|L0+D_xIKkpt9G%Nlf(@M`;>l7vainY<;-T?^gZme2V2T1QphHVtJ{lk zZVp$s9$`dx?eKqQ2!opp{sG&^v^FpieAd7{S2AEEZHyxoAtB7%n*jdai~2@LsThoU z?|QYFf^v!1A=2F4jwWSkhc^tEfU~3?`*}4|kPwS$Anz_LA+>mKw}{bY7Y5j2{<+WY zlX{2W8nhN25yci+s%22MWMXXD_SBTKmEs7j>K z7y<$Nk@%_-_!6nvwwp&{RX!s8a=8VO<;_JbSW}Fx{H|Fcs{L_NHLnLMAy^agf4m+m zBX3SH|A)dnaq@5}05;wO6Vz4|5s-YaF=RWh+UN*?HlG8FOP)K$1fLK~ICp6|wTQ7- znJBOC_BCXy0vWI2I<9rRZz22Ht*s&OcQ$`&(SgklB8F`w=YfTd$fB|uIL213qY%HL zs<-pH7Q<1`Gcdc2r`51=8pRJ6n}M=n{{SuTbScH~$X1TttSa9Snej^^gS6yFUg89g zT;u1g_SaRrZn1NeT^iLHf&I09wwhs3r^$2XetG~*H>~tqvXWwk*C04@7k3<65bK(c z;=XNf9%V-`9}$9+pYn zNJJsa)_{9;er8b|dn36_Vs*=krZIvfw{$&He5Sib8EL7Z4YqB|#7TgQsXJkohsO;( z)jT}zHT2y}(zk@`HqK$?nkeEgjp>LXInmV42lCyPQtW%9NjDf704(`7Xx&&MjFsI2 zi=$4(*M^6!;SdK*5+#P6E*AVC_t&d_0`{sJ&O4@q>?50Uk6sZ+c8^cU?y*sKm2W6p zt!^oT5h8*)Ok_a&nL(;Efp(NFG+N~!Ykcq2B7jV!cJ`FZez#%w%|aBL?_9}i&sQ=F zWAE=X`0C$LTX9@L`h;{3$__Xq59W>TqBy3HhbL&iM_zfy8rBNJ=jx4Cag}`$*7kwG z2-?XDiXPH4X0pX>PFQupB(?%>J%w;Pl;e{rK5hvOi(6HZ* zXV)ahSgG}H38MpaH?IGpAu91u)T#PX;IfJXzC7E>kKGyy4CR#zh(@Oc8vVlqM5F%= z)bA2Yf_%kCp`TBef1ccAv5YFI>f%PD4xwCayl-Fb#$Ky;2iXjn9kW=gA4)*Hc~Y(7 zvRyWjK}8@{YxmN4x%8sHC)NZH(pn8)W<3P*&d4)O;`)Y8s?KZeJqrtqp53bjCE>uxA3j-kd^{?yB6Sl$X`J_k< z-$2$&p6_4Klj2NN@RWmoE||ePBLE>T{K{z_Tw&crX@q?FI`e1ACj^i#Rj$L;q6TXY!82l`|v^VMp{t zC#~=Y?Fo;_?#BS)oX$!LtGkJ;O~_p9rRiCBn3Q$$IkMu38kVty-FJrjV~y=Y@rp7c zIGwF_EoBe=u*8700e-PU6}_m-+8M#y>W}{)FMLR+Tc~c9RA+t4IV^sI(Z$kX3X}}p z2Zi0k82UuN7MLHmFpy~;uX%Y!Z;TMV!W!Yk!?VQUjM7rKo^ypYUhIbTQ0-MMhjoVL zgz}JF;j_>xuCXCiq8@>Tq*eG%lNa*fYx&xZO-j*jZ*FgS3JT~)6l6%JO?_4S)}_7H zn3OuUK+<|%#e4G)9Pcx3`w<3F$}T8M8Q76Wal>g*W_$D~Vkg2MTbap1(A6C+MLl#W zs7P!2L%2Fa^rw`}Hv95KF^qd6qHYF!Ab_Ncd35HcVk3BhU7CXm_t8rMR*_b`1{ z#yillIqLNti3Ssm4<3_3Zy)qQh5+CT`_^3c?L_(3WSNijlFY}Rm-km$<0zW>dtg3z z)opCi7)`1@*winfu4Bz~GYg}ucyxl#EP@J36$*2GVv3(?ue9XP>)L%C5o?}TfF%i1 zNV9Txv?R#{&G%N6YY&K{lQ5O)`5KB#as1jWi{bUd>4Q^GI~fM?)DPlu;}AV@{&tk1 zX=865A(ezIkaY@?`B+VY&!URe*>Pg@-qL4L!xJp`!?s*1a>r^F@yZIvl7-2?!Vv=% z9O(el{8)55dn92QsHPnt0+&2|et0t+vaJ8$N37&m!o><5O*^`ca4fw?5RijS{oE70 zq=$_(Re32GVXM>%F}m1+Y%+Ftpyy?( zcvpnU2u*C6czN%K7ft~>2sq}oeM4OXDh>2zyTuCc3#g_hp&)A@?v7t(4tdb^8C?HI z<=_I-Ov7inM#W}hJ73otFNYLH*qt?HZ-`si;)fGHwXApt-eRN}9bMnJRCVaOjU-sienIQRD*jVfwt){{u+?h(8BJb3q_BK_zlK98__s1b6&kAY zW@Qu6T)#pd=K7$8x!$rEt{K5%79`_n_NS&|zJ74JeisOe>nW(QLRV!?kgoQDcsJe) z3CN1f#oP@L9|_6q^N@}Wo9NJE@z=U%ApwC4bq^iAk(QaqVqw_|IV$^@DUQG7HWenK z^8acr&haO<@B$~6njIbCPg9pwCfA;YWQ1b#fm$5aWq~5#g2c6p`oDCo`U%Y&eAXsf7?7)sRR6J==<|ZkvgM=S!i{e4 zZm3e}FSJ5>w~mtwGJml{_j|FpLy`MDA@N8T;&2a?61@XqknP$heB0ReRlv!h@B>E* zrvSWc$6^&C&-25*O6N(sC`Y$$Ean0MI$>UyO7?E~}|I z5c@UF+FI(n5$Cv~^BZWyIT+HdBSsQFZNbDKNBUO0tGA{!xZzs+`lht_3Vi0}!+pr$_@(>MdWP^b)%akTtAc@}#nNgLG;c=AHXkUFTcevC(WeNlx? zSz(IHfxWw}(3k^vDi$IgNF7-R zlIDBYHwf;hy5^=NeFQ>2?IQJV|D<#d==!(@6YTp)NLJ-O#N}SEyG4aIsYM8ZU(WHh zA^5j3P)D3&my71R-so#TY#_0!h;rUzayqnm78Uw7HO_9m(dM=V(b*iML!V#Dh{l(n zCW#xd1be^?-buDrO+F@0$KXwfsze2N@Ww_*^m~GhWVkR^#Fm=x0kLzNke>EJkg zv9yNBrI1}DLl>N4lI{BLn@B)eU5UupgFwb=&g^Np9R^S_s_P8tGJW~rmNyVzei)x; zQc$IrN0t6H0|EIVAv+yo2~nimKCvguR#~Gp?3;vK7-|6RcYHk&ZIYGrMEv0D^QHb;a?d+9ecY2E9ZTz>`G2r0LAHYE}00#uxC*|D9 zCaBoAp<=HnKW7f6t+QjVQS>%e>amypk%P;7Y78_+)ZiV<)R)Tkv9MW*%d4j6YTzF# zLozxhwtTz&A&s$>^l=aUsi8juB+8zIKFvOBSYRRy>1-x@;Qt<_q+2lfiN4CXNxJwU z$BKj;UX7DHV*;}kocHBRZE*cPK{CR_gAc`A|w@3n_5VC14F{}LN zw>(&)p59LV+R~!wxI10HKhzy5&w#iOG9j)S6~9mB(x=i8UrsWBptw^xlS2$%``Q=m zrn}a*F{ve934Gkek9#0%%6z|uGZ3w^r_eb00_92I^aEzz7dOt7Qd~;v5p>^664kI~ z62A(BmJ&Y_Z#-M4ul?xZ>nasNH)m;d*GY3ut81yVl}vDYVpw+6xkw6L$LT3SCs-kY zguS&6EY*F?HxG$QIBTwd2peqFYp3&m8Pt5_fVIGY%*vuhKTcKrfhzDURv^zJCa9d5 zN_L#Q#&|ctW@`phWLku>&*wJM!$#feqd3(kgk~)f{ zVqF!^V5B!p=~Dfr1or}J3N*1};PLf|q`8X98y_FNT~b(@#5M%}r}DCVi)vaFG?9=4 z#;~aP-(-w(e>g`y3)*APm@aC~0HU-7$bKEB2A6sj^33pk#|O$Dq%{MSGQvN4xdNeW z+Y!5(Yv_S%yUibVznDEB9E!5Lz=Z{O+wE>1l9ur{ox7E{QN%9%=?BZ_q3x{1PpyEW z6)O);;=bh|ls?h}aLF*GC;9yuM+9ykJcVKg_BXaNS;#-L#Ky>9OJiht-s`ort2t#@ z(CYfoLHKku@+MyXUVk`FRQ6D>^^Giw*agHUY@%^Y;1Iy{kCwO*;HA{Mx0f?6i0{?2 zk}{C^z7&vpVNLS$Z;PJjo@s`_l!mI0p4ikL%s*h_4$T!&g6fD8%vq3rz!n_6HwxX* zAn=_VYW#u}e^FJ&(R-MJFj9Bqc=Q-MsxrvEm~9_`DG^|TH~dm`P7ht2+sL+MRaJ)Y z=<4jp`+lvpT}@>|RpqyP!!dx%zVmHQk>o}SkmNf7X|-pNL_T*2N2Tjhdl=wGV8pxL zf;u0)_XkPpR@_xe?!5a7!WWamv}Z#;lavQuFh;)ZZ>Mh_CThmauK&)SaINf1e_tCP zTgmO1#_vE0mTw4Dzf(+~`re?L*Lt6qbV*Nn5qr96O?PodYf~WHw`HaT zNkY=3B%!a~&jBJMqhu~bkOQ`Sf3;We!Z~+u{~I(rp`e0Xqn%SxXn)$Z?q)V@uD(cU zsj%hDv6E>ABW)gJB}inMV#Sy^2s;$lel?vPVW_M}@Kr*+TZr-m*o? z&fc5v|NZ%VRqp$_f9HSBbI$#o^PIY$-;T1P^aa(j5zMIWk8&7b4rB80@$jWbl#>_#+dTM^LL1x5$UHx z1#o6-AqyO6mYog@xI0InH_D;eB!TI*V>Sg>c>pdK`fGoK%db);6~{?qkaE2!@P@BD z&`|#NUnT`EtHw}REg$3go$1l0)6NkO;+WOpKu8hBRt{8gLkoLPQ&Ni{gXKJbGH)Q{zZ2F}(p_r;2|+#CC; zjCnnB^Dgl&?Q73wbZ}wiW~KJcSnfer>-Sy{4tjucURMUY*WZye-2Iv(cEs8pqz9rP zV9{Amt#ux8^tYi+(s1$pd;V~o5~UqODw^WBEhO1sSY8+G3hYFQ$KY_LzALkXYAFV5 z*J?fy4DIVxKp9|Y$biR8z9R6m8gWjXi(4%FrRKNBv@Q2nMhhH|eH0Q2r_REl$+xZh zDMag1K#f7IarpY)arn-#PcXeA1A*5so>~liu(hv3hY6Ff`1fi)wUt9Zx}u3B_t5hX8K#dN!|S`Wzyl9GlRt& z0Ox?vr6swFZsyxK<+-Qsw*uM~rk+hPh8~Sit?F%?_x=fhBcE#+eE6&;YfuKxbjP`X zuj&!+?Km!@== zKXiQhOT6;$>@fnCVKqnPIn31%jI|?dA z=SO;mfnNw$m6NR?^^n=V0TiP+UNFQCi#UIK!Cj~qR34Y2r|hJVZ36C+F~cf8pjwY^ zHw@T8io(&q6$NIJ8H+}k))EZb^9y}uU$f=P%geQ>?Gamr_H_{l7Luo)zezmQKatnV&VK1B_ z+FnyxOnvzDYC}`p@%qe9H|&MMF__!NQ?3eb@H(LX#`_=F$!7fF(YXGf)o*!7pE{}X z-%vIbg8A0arS(r(sEFD>-vaabDs9m}{kW;1%b|1c+VPr)wlY2B_bCjU3d~zZdnE68 z1>-jcTNi^7P$-jd2g}pOaer^qh}DCUz9f;haXy*vz^bUd$*z1HK-lAXtHpA zC#MMqpfomU^4CnkEU}Z%(SJXPH087aF$*HDS~$nR0MGj@)eQ@O1X41?lgWPxZ5Ezb zjhz-JJ@gbep~Gf%peCnL@wT2t9p}>!iv_=)%5mU2UnZlS+PA5~MsNZy=n(ABK=~^X ztM1n-TTKa)a@ z`Y%R`d}cgeFZ~y#wqS1Qy^32aw+a<{ePIP3&12W246W~K`Kjh5>g{&0z~R$WFtT+F z6$W3aL+G>d_Sz36r~oAs zmpOE^$@26ul+z8v$I7RRrXy+oEkq1doCa4eli*>LEBk&!=gtE=*F_z-J&c07Ze-Hw za>HLGBGM@M|68HNV^gmv!ci*fGus7Q;n87cmXcr~=;~jkbL}V`gp}pf*7aqNg}GZ8 zi)#Dv29V>02bAUreaLlDd>}hQ!&@hA0*z)_l7D>&FEBfHyhI3~=-}9i=pa8!xw7B- zWK5gby`*nmLPl)ICwUrHq|8e1m{})&9)}K_d!t#`m4;<0UqTVHtk^%qOmp}x`dTLj z9k^;aV~t!IZRK1(B1(VttJU`E*Z~M96df;Yn58+w7BgL=2$8Y;rTyW`(KPb(*lIjD z3N|Tp$PB6ycn4wN9pul3&neHGQ97?M>Cj}XzKLZ%@wrx2$*eDT-Lpj;9_|Csso|8(mtxTQ94bhiiHQXb3iO&3DY7CWi9hD2ebF9iRd+8NyqBz}klEuOEoo zMZM(?6Xb>#`GM%50FEga!PP$oi>B6!G-1_)lC`&Vp>t9zRlI#3m{Q{=O2cc`xq>!s zeI6A$YcosUs0B;d$R8tXXUA5P)8vkxX((~J1-8*5H$5h&4?0(N0{l06U>oMRG?moe`pcI zRc1#~&~i9Peq*Z-bn|k>JInd_9p3Ro1Q0+0MrD)*Cq7HfmaeBshh+BRX=K9}5C5b# zp&(BN#XP+*bR`~+GvMRc%+ZDqIi<&)OC3o1hPE$d53g#Fp}5JQGSJZWHsSyKYp=Bu zJ|OX6cg>1EWKQa!CA7k>WMU#-s*jXQ{_ATS@BRZ{k2Rs!^K|yYP=9ve9jKakJvEOk zimUx zVx*>SRF0vjIv|~DlWjja35wE&o&-TF6Tt|Uh?-r9Kg76Zbc_6I#j`w&+3LiI_QQWe zQx4q)+&Uh3yL^E(j?lj#s|>7Npdjl*$b^A6{(xf(#J{B>C`4Wi=Sw!XRlpX4j--YH z;k%9LO6?WvWMDG%BB>}?H2QKVNb`mN)b9v66ks-ZSSvML{aCu6Wg48TW{Ik}a$Y>5 z7cc}!uV|wd0im|eZ|>blAy0>ZXO0{%l|2A@@;Fq~D-zqTIyln{V*A_1x?9(lwT?18 z_Ovv2apf2b*NPethtDOB)}}P{4o~}Id;)2QYNptF1>=WiLh)qVn|#rUN0k(CiuO}8 zV9Y(TOhZEE=c~2Ia$I=8VNf|M;meP>D}_r3&ZSLc`E48d{UOYEV8Wb&MolXjQG#WM zCKVlo%89ecnsnf9f}LXEW+WH}Wd@QQJ8Is6VjgzB=t0&2cD_`Or6Evw;raJRS-ixoGA-;le=s7@!f+W8wyl3AOD8?LiUp_YOLsBflAt1lih7 zee!i;o=><=_RC@bPe9eE{~Th=YZ~guDS;eBDON_)s=)8ne=WXH<0)u(A0!Q5Uu6)$ zX_@c8Jn((F9R31Vao1GQcHI#adQXUZYv{|7Us|g?Hos|jeU>}7`iLc>a_9SuZdo}3 zN_n~na=8xSw-WxWRNjk8v86B-IdvE85hoFL{XhB_kE-Xv3~F@XFG+5L-QqD}mA7ZV87OXm_cFI$ zMNMJ69K3hnXS26m8eeQsGO;C^sC4P81gddIP5v_DG5X4->+<;1tdTF6Y1#-=+QO$k zlS@v@r5g#yQWVi^&L=}#D(4(CejYwFtB2fS6R8E6tRUwyc(7>6EDzlJLYVM^TVIQy zsQ)LEyV(%|!pk7~IUDyma1+SaSKBb4Ch!-uO3`|Ym0wK(Ku3^Pkm(q=ssT$LGR_>| zk^M1mOuxaxUgEYehXAgH5PUEXvgCW9&Eg@bl`|flU?1}IB>-CULW0r30DC}P$s;&_ z-UsE+K`3ho&!gp-)P*km$2dd|%$s|bd@n)bs6iCnVRY)cN*Hq7Couh=BiAl9SzXi?F7-J!R$zJ2fE?ne(#6q=GBTS3+tF}hiI6;+4K z7L9}LR?ntlY<4eb&ImzE9NrLCu5YUce3mf#1UK?#W!(eX6xd6aA~)ja5fBQHWs14> z$)-!)voOkWwe|tDb4-}zv;x>UcKvBCyq52==tU^D#erd14vm$9ln{xm^Z{+Z*n!q_ zMfOJ<&~5>E2OP@LhxfVuNzai6*s~t{${}*pgL{7m+>c)hL>)r$YA${NJ;kz^RuKG) zNMUhM4NB_v9GTs1r3+gxe+&wkAt+!eTYTTH{};~s7qP(Ke)KT(H^8m7jcW=qYn+1< zL<=4D$1-5O#3vzX>v}IRj*AV+uyBCL3nq7azoOr}&j>+=k7oZ5P_&8>E9qc9_pl-~ zk)%=r+^BrR%5fNqJV2fcg&R5;c>QJdta8&qPV&VRnw~3iD#lFLYwlnwbTh@n7~t9b zw`w8#TYSOl9%I_DJ30nvL3G)IG0011A{1lub`mr8johdiru5X+9?*>S z>Fdaf)CI4|S|F1xB|KU@?63;DZdeq3&wD_8o{JOO{*cz8w!no}9_WE1Wp&-v?SO+X zFkEkky;J0f>_<(Fc+?gtw!q{D+Ls2nk3iXrzucM-;?_o}L?e_)ZH{336S#m3e<(~By?)lD z>C%iW%9`4*JWVQlkk_=Tul+FR~~ z;Ja#JS(J9ravCJ`}xFf9aTsk0!>|8GAplc(MjH5EfD{0+mjdP0=bIkq*=3 zug}1C9idZ`de~R!e}Ulrl7k}jO)M8&sup){dP2lwK@Cw^fF$+YyFFl_N3&qSirF~> z?g|!pRIZLWqa|eaKtMNk)M4~XQM{4Q-u`#6@CV@B5C}@;{)JLcGbq6R8L8hU-D^;% zc+!-Mk^_X^&X-WegIg?o8r=BZKb$l5f1m%Veh4=H*y|o=0RHm4J9cr$JTH?O(*HJ( z^*?w&8PGsbE<{rpYQ=65zbBq7;NT}|c&z6YWw6UVGMp`jY9B1_5Hj|6Y4HJ5Wxbc) zj~btN7$;Jux|x#xI%|>N!0ruVp08mmY(t`VewMI3DOZjd#Bu3Z(m_QrRCE`To$p~N zem2g{SU(YUYKC7>!LmV>15d|C)L30e`?&0kM= z{tZulkqhl0!YmQKGPQ<}9NnWpY%E$=SJDlBxk^#l3jRQjl$-lnIH*tM-A5FjpRQ{! z)J=1i9F}5l@!>^pP5~Xtm`8zh$e4RR{0d8rs?uh=dW3pJz#ZME?*a4k0;F4g><9RT z_4ZwI_w!?xYc z#C<$8-ydpQL5*_&)#D15)A3wWX*ds-VHZdRz)wN*7}x^SS`-aTn2RX;L8FzFOVA9r zm<9OlJr10ja1Frg9!pc4lt5;k_AHOc9c5(`r!(k(%8^hbqKw8*<)k(h|M#i3Y}qYp0J22$PJua zDKB4dMAG$wsY>zi*7#jXa9B#xjkmH8|Hr(iB)^~j{E!}+sSRkntO9MgILnq4$RA*YI52=Br|P6S{@4m^oSvcR zWqLBLYtH@8$8qI07_edbJzxXZr^FjzV`cPBd-#}Yix!#9whqcaEd-9m0<9D(Z|&!j zAMFj(KadUUIl|w*VLAaS6Coorthd7R_3R*&3b}XvpRgyzxMIzQ1DKYkqK7*lhypwI zSS2?gpB#ioiy`oo0`3?Xp3`WNHL2V4Zemp{0ds#qAqPGDBR0T4YfB%TvllIMIvnkkG;81cdHgnR+D9zqHcdaEwj{CST`e8Z`(qjHs&d-e zT)F!Lh#~^AF->pVRZ9kaPXO6>ePRbBFpw>WX1NF3|HvZv(3BIm6_e_S=#&lzq(uF@ zLj4w_AfFVdOko^=>^)mzB!gsF)C}Nz$2_jE(8MHCV)j<}u7@0-kt8TqU|3Bpx@-Sk zWQ=dYTb1@HcxRk_WfC}s;K~V2o6S^%D9s5B>)CNOV0}vWKOTnRXe=}sLXoeurO9rb zw+P1bT%aya@#zDl-Pmy#*Z!j8QngN<95HCF3-%qi4!G`Pb{anu z+3AaSoOrZvO|K}Bs8<@|bnP;^a2WQ&4Ed*SLXS-#-JVzYM1N91KNFK*T{nT-Zhn$# zWF~rs#A}1CDv2(MH4n09N>@u%AK6e~_8{;>j9A;haM?MYYB4T1Q5gne2piSMpx;@( zcSLrPy&>Nuc(gyt*&o5}1Am9aE{D#k;+pTYxyjI{xmcz(J2@4f zV|o~H%*%}|AM%P{`VDsjDTe2KUj;k?d(E!wyAPbpgoH$aFXz8M$mV5uk_e`zz^2{j zb?QWtdRgCpPsVy3$LG0j+UJ8syOiJpo)($f%!|6+mtaDfhBra`8q>cj7yN3DE7w)i z8Bn=8zgU4KD{ZKBD-eI7c~Ld%HIf12G)EZwZpdL*-D|4`jNLk}e+T z4x_jFc56kA5~32EsL*j3SlrRkB3by+zA`5;+&`NTzj59HF{vDf5>Ms_43;L`n$7@8@y+jcjK;7I)+KtNP>0ggf`ySZubq zX5N`~AP;g%w(iY~3C}ESJ9Y43b*gIIryR{zJ3whNFz^Umb4iiVMaFB(J9imtI(F z`kYLeu5L!ydpc_SU_8}^+tTus0&2OR?qhME(lo@vr$y*w(3w}h8#z`)W``|@? z4YgWrIp!@v1-vMX0RAL*X()`^nojvx2hkNsno{tgwFWhHX_gp7B(*w6uLq` zLr7@bF5ZlT=%Q#hx$~vncSXa>zRR*rI)ev0nB1Pq8IN-$Y>>YbH@o`_ZLV3{gi}pn zICimu+T?oXBNMjk$!)voTCeapDyTL*+h(kU-yskckfUOU@X+< zcURXo`0Z`mU~yr^n!t^IY_b%#2hi5r+OrCWpLtQ${}$ z2w`HK^C_2R)sm0s(pO+sDwqRn0L2Tu@_21SdzOYr4|~hB3Z%aypU*rVIQmj_sU_gK zcH+I!^jU%Zwa{hfN=zCWn&%{vJ56tzknYU6U6sEs`VFVC(5}i#c8PDiQ7ba{c3mp( zd>Svly-q%bcQGTa1(PDHT!B$pyxXryUxp^(b|O<-Oz1}fT>aDLo5cLK2-_^@>P;=y z@XASU9wQ&(!9J>Uxl^mu^@b#*Y+C&TZns=GNwd_F*ld!5-%bPzAsJk|#1xctsCnjz zy%P6utgg$|ESTJt;vq6@ua2Mqi8;Cn{l5W&VN8_tLpM7 z?g6daPqb{`&|R?%o#=p`^I2-*x3aGKX&!Q-9)$=Rq8NMWmz}Dcy|JK;Mj?JOMadn8 zEi38d_jN2cjF)H8(uN8XKXt1IWdEXl)iBsiW|G=8WlBJK<+L5T$uL4dR>qsEy$wx( zxj$UbtM_7sS?1%>b4yP&w>=xb466XAKbk%`(Bbi`r)}#@q@}&4ahF1jlpWz|)trDfFdKk+L~YW^ zzg)%q@E)o>h?~J2m^C06-sXgrqKFeI>(d%A}Zc;#x zP9bIs4G8hJqs{NgSo^gxaVA+)erP&;QGw2O6<0HHA~Hzh2_iR8+aN8 znR-~Q1)_aFvze$8NcIES5^I7Kr0|P%&qX!p<`kgaS`9$%R3Ml4y%uGWk!ur3k3*Bd zca~XJX;jhKoxTU(%c>-<7pkC{4H)g77ot9d!EPVkZZ&Vjn+O7BFEOvK&4z=A* zp}NT9e{>NA><3O_dRi)mwQ_-#)dHZ6+`9p{kpRK#Ap_rQhq8zzCh3@2E(b?#`d6zT z`Use0C02i=3r~)S7T-`L|l-L)>m)+dw)8_xVl>jRKqP;X~!}eS$%{le%6Hs(6E6l0+zlS0rO6m=5^0D9Eyw zrr2vz8!6t5jAeTBR#-nwn$;$O-vE@a*U|EU4yS@X&!DfoX3)_ErW3j!OVE9JmMV#cxXYc_Xisn|g6_`;p)GFMz2c$3@rq`>B)(+p z63uCyp161H4d5Z+650mGHO<_cYEgNo5WRk@@WRKNd)hY$<&0prx%s|9HolFtR6Z$8+iP(<6gG!N{tku9?!R-JP@KszmeR zY;?}P4QBOg(w$031@zJ@kQ*JB4P--Fuh#ok^|V(dJaRY)( ziR?OUN%Xr6>FdB}@v`ItjfJNmxO!!L!JR5vK{-JAebe`D4E1~~S(D^MlKVZvGH*Ri z&KcJI%x3%F%EguT-C^6UZgrIB8~agc_1KZe7=7F*vYF$OUd029zL)_`!m7_+XOXL; zT)Fyi<-(?4HQe`*Uw6|zn(5}dz~M?~l))sDaA1!b3zMr#6#5R^V?j@G= z|9T^|ixmGS?2?z1I(MRW{a~oWiL}7z#lyijjlNf4$tjRx*0s_=&yEmm(An7vEX{HK zek2g8zw7rQ<70n08=ZU254Y{WICXat=!>M_A8m{=>+YBaDjf!QrshYky^BKJYZ(2R zt?0lmJ}nn0sAY?eU`NNwb(A9K=1*Eh6mra|23Wg>lA=&cjN-7xOjz|1bmvFv=Aw0X~bgKhYV@b@cd2@>NU zWNr`~GZ8tf_nV$=1Kc*!uY!p&rmG9j?w_S2k^oKQaqxuH5O(V#FOJKc7>_4}fKgN< zWqp-Wi!h;6e1a5F9Y{WSmRkt^iT@|?!W?K(K<`A`2gqxr2}38Dea-212`_1?#>oaB zarYYT$9rV9^tCfiUc>0o?cV?t2I({ke{t=u^Bg<@WJ99P2+JqdX%8&@pPBQ7TH;*l zSwg*!ms=F~k0BSBfoixRgipV>{X_3c`*`qcQCGf$rK&JiAv zBqIt`EcQ>jWz`{)aQ%0w^45I!UWC8;C`;lQ76Bsr7NOKY&{Z}VX5CgZ&JHFRT)7cC zFq9l--mT5QT~DUPl*yT-U{lx7Xa6*({;h|UUiUvx@QGSw+iALGwSbm=%Su@%&@Ff5 zazrlI%*QqqxCP(B%j#X|ItAd3)ox#K5K`V0oVge6tS8a3yGh~p0%7EyT}M2sk+eaT zAJi^iZ-S=o5ch-@5Y41+3hGA<0wpnFRNc4lt!Zvvpt`YH)P=ztpECn~_cpW^yj`4i zlFb6z8ps>EV!X&o%<}Z~75?;)K?*tgzJ+0;&(CNsbPIH7s0ZgIbF^F!Z*8aLZc2D5 zlqQ#94v~1k4`=h@4$ka}NGvSZs7YphlUVh@lh|37%_&RR?~pvqHxVVVin}R}XRSwP zKX<=Oa(6ql&=GnhZ4pObzLgfmZ#xD0**tRFyUNk&UAF2r<`K9gttRTUFI9c&z{HcV za%*Jmr>A%+T4jG<4ErYfrZf$cH;%QZmhTI0t2lj=GCcj$AI~7r@%@#P>M`sCk2M+9Io~jP2!40r;zcU{kMaZ%SNV_OCZnGQsYgi8d|V~G*Mnz^ z6$nq$0{{TN(C;F*L8*21<49EJVOw%)4gQA|abY#V5D3iGHoC^$%5JN(h!oblmGM&K zLlx_$)CNocR~bw4vxbC05O*Z`KOc-LWv1b2je@AuK7Jv7`JsR>v6yTGGVi3K1*O`< zy7UDNQgqLa5KWLG)jVo3O5GICSqv$(-pi?~MxJe^d~Ge1UYHZ`Jkf5Ud%v{Gy`7H} z=qkk=_NnpvV`Ul0)oF*f&c{O+Po}-tR4v@3M83an@8IK(Cb&CoClUT!}_i>0Xsq} zryj<_&aayuJTSn^qEZTXA>YR|EqGK;7h!;o;o$8i-QQSs9+p&s?a9(qt3MI&q|55c z^GdNUaskwp-*!pM!PYHkY`1aw$R=oa(q%^ondf)tyxQ8KX{DD;Sg-aC$J1y*t{Q+~ z)rgDPBV&gE$>LB}ErnUR=9fS`zZ>x5IMA&DEwKM>YcQbDy4 zq3ejQWXF$Tkr;b7LjCc6-MM>HOK(~^>T_akiobHQU6s^Aw0RbnSasoDHK0VXRdmhA=1V@w8%wF)buyS@DCWKFMPo`Epc~#uVVJhn6)0Fm9ej zV~z)cm~4k*+hh4A74vVrGZ~h*ZlrD$y{Ge!YXTntO{d50biDZnC!iv#1z9S(lkQ#* zI(f5b^CubDq}vUa*Ljw`;$ebE?-#Dlp#1_@}^s-FtUPfCRx!a3-8f(H0{LxtGt3`FjZSOAS&*++l-^@D9 z^}C&WF`=P6wI!+H12bn^Ej7s;+8LA_xS*7B5$v!lE_*+ z%w)Nt&^_pHv{dN0PFRcGZsb$jV=5o^e|$>Bbgw>|H^e2*{>;_fL;Fj=H8Uo;|DRgartld{pqwi{F<<8abon&<+87@)AcgqR;jntA?p4cc=2ZtaQXTLNV~SP z{3eS-Rw;9y0T!!bW;_P*Dy`0CIWf;z<>&JurYIIdv7z-2k)XJGgOLM!;{zQr)z_Td zl!D5(h}^HHaJ5PnUx!Le!se~{U(}8D60phz$swS<*HD9xQ4rPngQtK_N^c@s0u4c( ze|B*WXfmFlYxJ9gLRRmJ1rZX8xAPDoiNe_Gbu!EZ^kzllSW3Z(Cbrhrig$)|-;aCR zK(g_k4M4T{;aoz$eU*ZXiJzrR*9E&jSJwsRyO{c5>2NhJ@O)&G9@Sr7t1p!J6&{Ay zqijcnKX;Sb$cv;!#-S-g2`laWq5K=De>`y;>)vl!KD43V6T$+(>rRen)jOqTDXu&_LJLreRK&jqIm3FWXs`euRq@*@oX9$kWiAJWA7I^qZyhi@mK0z*l_-b=2!s8 zp(*(P|M-5PhRrlNkFm0WHL-wmviSp+MllIA&k^WpknJhZ5wqY)c2iq`+Rcih?eDv9 zFWuXz#n8#O;LmnB9hPw|hpqeoKzV>diEoF}YXGxm(-D zIBoV;XO2P!pZZMEv`Bt?dt{Hy34O9(wMF=v>a&lIaPU=Wdz`0CbUgxkNBKy#qUGB~ z1@P;rJF;;vE1T#%Tqb8-DRI1$ZlJV*q-i?rpK>eOPh2WMZXcFu4>o%S$5?IaX&;F( zT*s<=wwW;#1aT7Rll-%68T-ji%)ftTrNA0-UQs`hX2OBQmeWPWovhBtUJ3XzkuzflUyPeUyTXf+!0%E^t ze|Vr0KlrhqCbR|Ew}K7mjnDZoGR^OJ0~XUgi~F~&LtW<@N>BmD$fufW`|8Sf>@q2h6b< zQeHUi`pzlDBM4*NIAK_Io`U3gN=Tlk@^>T6+VsUPzMaKxr@CG9zubf;zQ*V@JuKlT zeYt+9dXwYs=_?2p?>7BA-Vm<+A3a$A0WaVuzc`8;;zZO$34n4&p@lCz!P9e=kuTA@ z^<7T-E#=s+V@Y)Cu0+@L8OP&(vQ#J3>-u-fm7({Uq5u~&_eixk#!#gRH7hGyIFpMa z;ScJtFudCwe&=I4NJaP?&{-pWuT1u-;3Z@pexw2n#d6(^K0xiDDgq#-cu^$ol-7?m zV!a04B^zw@$L&=MI8F%<;xTgb?0Sqh_K~hjqqsa|H7m&Ham&LV9mSf(`y5X>27-C+ zxXxW0Z5(q5kgvRpd3iDcpe!K6bKg^<5?3+Zmu`p2&U&=`7;)Qf9fU@4q=#nnJ(C(hS7*oHK6cw3N8LoKBZ;m~a!^#bR zom_K>l1HCFXQHw`4#%-3IO#P1Cnfte3W!Z+`LDBdFY7W&+W}%sT zrx3Yhw)%MJ*@fVBpy)iRczLh?;wgx{QNk0yAFZh7O`{5VC6w4Y$pFa#1uWhyw$(iN zoq~n9l-sR}KXwTyU%*2YT=4!WLArP~jWZ0wU7XyjOi|y8fZ%xCNA$urSGlgj($+qzcLLsV$h z{P-iLgy*;xfzx=uA#W;A*?lbVIhydIP3z{9rE!%59-Sh8!J|tbAn5F>z0_&I%GS-^ zoy29U>dJ!&DZXjlH1bBw!lo{7O^f{Y{Qn{NvS)Fvvt$-fJ$}SA-C#ZjL@EVBbGJU{ z(gwRyLI_hi#8FE9Ea#6V}MRq0D!zWy$L5eRQ`7e??M&S<`6%+K@XDF~^y z{qptCTX@6Odky%A0lx~a_+xFVpcF>NPV@)@x$g;IeQwDlcW#~AlpXcL-y{l zL&>sRF9wUHLWj1I z4)qEx{Tat$(xtI;PG$6;9smKw!}WR*GTkHh^iC3$tg$(7(KO~+gMKly4ci*-t~uu3 zFud{kB$=E$$3kxtB8or(+Zs|9f8$&M-`4^CW{^i!B0fN{5mOj81`x2OI`>xJKfn}i!WBDcnz8)_pb zBow)ccVd1)H$J_!Wai{5ZfL(;3~_T&0{HLUiR#SnFey;Do(wSBSX%#Sm0z6+kjB^p z`&E`)kj8kjY1r?-wwQ_%XX|506V0z!jN!gpPn2XXqBh$_i5sa-p8h576dQruyM#xE zuW~&J_rE+p`uPPZXt|Q;u~UZx?bj^2>vGa`<+L}y9JA@wYvsE|bVY6ifDEeouk9nN z)^7)H`6H`XQ@OT^{En(vH;D$_)BrV%2-;T#DOGjt<07-q5(S&1-H+XF&H zr`eYVNB4hje=<+TjK}osJeBrZomB73vpFwF8cgPDYylF~3+gNC{FMa9G(t(neoG`x z#2g4vH_RYH7s!=j@P|Rv;eL`1Bt^_I>t0})@~rG-M)?_qm40W*pI?t{DtxssxaCA( zsQM9@eG5mpBP%c@66bOWnGX4m~`buhq zo$#nWpmGb{JwF<5{=@lu21`(kd`Z^V=5MM&l3*XtRgfl8&P?^~y>zCSVw|r#TdI%> z^8RG!PvZ&Mj!7-tPeB`K<#ADge)WS3gltEF2&IazK@_}BmL_X-NnHlax48hQN(7*4 z0s6qQ)9`4i%b>|=cGUTpqtJ5=$(+#u;R(_PP8ib~#wWptzI zGcFSzYNBXF{9~;gD=J?p_O)eQd}j+vz6Oz4+5FjUAl)Yb=S+swGJxoMN|4T%duC>JL zI?3u-x{IIk?UU1W?HP)DSBWF)G z0Ryh%(1KUnv~`lTR{#at_T?@7Vk{X7;E|Im^EiJK*m$|2q-0nMiRp#tn|~=D1shgc zo3KjR1gv-z@85$Jk0ZCIOY!c#O!|Q)Z#=D{5+%B(1)qfZr8T}tSH2>XnX^}@_(W~@ z*P`A6XwVDgKe;6P#MigSOu`w*zHp>rD{1lZSG8Zmu+E+x9KR?uY9Z-?ToQz>T`h@& z{!j1QY>)BP zJOBAkSaQxyobo(|OI(}&O=0f7a8}{_vkS-Sxe$?yB2dru96{VF1&Ukg`k0|_O^MO! z9skJrwc5;9Ae+f%63x3Gz)b$q1_k}zAXu!0#1A<1woY~2oQ>e-%mtdW(Pe;o_dr|0 zrC)dIF9~XE`q%`e7L;S>C?V-wuK)2B9B%VG`hUF>p0>sNDG;5*i=h0|oVj7BB-P5B zDY5`qL(;;bjoU`})b`QpK9WXY;J*ERpm>Pefad&+GvWRPS5C!(NOT-K3$+iV_J3+gL#u)}5%ljQ;hr;>gY2rDw4RieO?%? z6ZJt*tF}%ra>@A1vj6*?pd5`4?FkVPJVK`19Rkqrb4EWI$XmHXEITQc*d7Q;C}gS+ z@>b4xKkb82!~uMeqxw3!X)KzM7+?a^0Tt^nu0jd8zsW?h7{b$hq1DNd79d*gH6HAA z9y-4J#Jr@#pW#5_zq*HC#XqUG>Iiq)Qw1OrzNXbAO?;U9baz}U9`#%(lNhu%PW2}8 zke^Syr1@MNM@59l$y;g6qjhM(YnJ37N#pS!x?_1ND$aUA>bwAOkG~Yz8YXbqJjsY=D$OV)g(l=~4&77OT z2QXB|j7qDtfI*T;#V>CF*`GXfL^xqxsu zRE64l@q0H6VNSFSQW&2yvg@Za0!h< zf_zE^7i{2Uw9#GUA)f2dv0-5Rn8~l1WQ94Gl~R(((IP{;b&1=KVf@sa&RlRZ_m9Xi zK{|Rz!sC^^{h-V+>yJ-nX@urW6Id4h&iX{EwXbn?w*p63C7(pfgG8c`0-St{h~IA< zW!ZTGeT5b970iX@Qrlm37eC@_KDXqo`4s8lL=PBBrF*?lY5v%TL10j`i7^^(r(?|8 zntToS*Zvn_RJ`p!=FfFQW*;L?`_O!Bw--jwI41lyIOjPlEva9h&FX}Lq3c&nyN+WD zzdfa}Tx!_@f__J~yB2)2z0Eq>B~OhYj(rnwYXzq@<>Krg3u`3l%l|?ZzHci3>v0_LA$f7Asx})W8;pNcLZA3F_-8t%Hztk8c(& zx^v($P~n}e2XVi+u?@dc*Jj99ertZ8Y7}}E=GB{zMu4?Mg5kith_AEF#p!(u&)ZzR zvjC^y(ifOx)7w@lTF385*3WbxUAIftEA_jnAGj@&G`R<>)0^AVivnDQ;ca#FVM@N! zU1__#G0qqYSOtf_sa&jG^3B=tl*id2=VCCgYEE43Z)*MBHUdXL#`N)1$*F8$`TYO~ z3m0N3v?KE#x?w;qqd?C-%?m@mXFw$T&daZFf=^!}voG%_&MKe2aVc~_=U=@%kh&rq zSWBYU5ow|Sw3hw>WiLZe_LkKgb>lDHK@g^z0?#ao{rDZ+1{UmfI$0E1PHv#xhyhOl zZ+|?D=7E^y8Pt#X*k1x9I~5pw!6De*Py!JsP5=|b0YntAwWrDo2fyW<(KZSt5 zPhp&Tn^}w$1QLGk$e>)igl>o_5P?pBvVltEpU1Af-6~Gz7W8RoV2A-R)Bmw9k|P&V zhRs`XlM1bpK;!P#g7Clw8Jq(BOYA_BDqfnf$+a>C|&5nds4i zM}AXHk)5YpWjO9}gShiGZ_1Q?MHrDP`4eq4yl9`X6<}P@BJk+X+|A+=lv;nc_UL3? zqWN2w-lFPKAn4gQ+Wn7`N{V6>TIJ^Rf$n6nK3TjN95qV)zjVXLg66`5pePpVZ5z|& zS`00{LeLy&&+kLfHXu%+O&(UA{s7n78EQE4q1fJUJbntH#DAT8z!fEOK*k1?{)9CS zRAG8%H53EBD@U;cJ^ZKc0IK)z7a=d6u*2{=p>bf-#eswYrg)bY_5Sqe_Z43AM;}ab zUl;(}C|c0#waHzO3?bph$FM0}_FtR!*snwKIQAfG24V;9mmx)3_g;OY5&U|&CilRo z7khZ5kzkEzvzj61`L}qC5rCU`_trhoNRU_r2`4L%=obye1<`Vsm7eRI>=-~Kd4pz4 z^5gA(PGOKfMzJ$rRQ+WPN`-V8KZxPESx-2#a<~ooSIh7;TG4d3SY4`L`!H}fn!EyS zQ##P%?v13uvQ`6Xuc$?bPF7&JGbpubL{yo`duo#ol-C#zoKY|~yB|hx`fv?#B zzV;mEwz2?>b0%9}56*P}pY(*fu|cMjFJJ)_4r)^C_ERW-?ij;n*Sq@BDdH~Ya8`R- zEnEX?-y#)2U9fHyvv&a=9WYiXu!e)4$7ussBJpsCbJx-Fv=#z(fQ6?H0?|-TM+99K z&wl@n1mgs+#8bQE`sP0)Qjj3SHG?6DY0*#(?N@aHMmFXt>K9fD+z#I^Y_|9~U)q^Vqdx z={DW&JVve{h*r!F(Qbt{*5NcRD6m#@2521PSJHUkh+5Ln;Uci^F3V6#C)=TO$N2na z6Toc!7wvB9_W{~#5Jq-4#^Bp@ANr4~v;xqD4&n# z!m~J|rA_83J~R+Yv}=A-8d@>W6xgq!8+U%B3g~_B(HO9!O;?DhwqqTeQpjG%<~*PK9F6bq_q(3w`R{vOo$GtK?)(0{ z-|yFYe?IHTBIG}Fz>mIt*i8rri1?bv{%bI>p4)@w)d6!oZ>v%+K!ZaJHmcCf5Wwgb z^AB$u?%QVL2m-b4iU=V6yz~VzIbJ2z0Sa&Thgb+lfWraoZEqX+$rq4(FQ}Z(>!0GUe`1t3WR%&vF-KMuyl7^qSA2N^iwgFEDtKdLd6cvr}*lICnjysy6G zDAI7X^;A>tX1xoUtJ&$Fx6hwz{ z{Wvx4{VjL?d4vt|T-5fnz%jcmG$W55;3Z*XAw>%MIgP?^Lit3mGbNu+D_J;G$Uy$# zJMewP7p zy1oQ8>CrilpLX1imFu&9uySDIG#84<>5`fWunNFi0zU<1t4%@jZdrX37Y{qa=QM`fph=<1j#waS^1+K65$U2xxy z=hcz!)u=g<_u47_l_UqoRVH(D13+n}`KM~md(9qsgAmVQ+hjj@1CRA2TQ90#oEu~! zKhLki+-@U~3kk4;&hMA;3yx`O*l! zG=rDSE3B>@P)w#APY$Nmx>5q)gwY@v2*RRr4n%4ad3hA_3!-Z)Gy*WbYEqj28 z7+mshId7fhXmXTnoqvr1qQnK29~Fao)Z1KHK&v4@n{-NilFcIeI-~{3iV|g?GixUd z`+b+&M+;dV+3ei{Gi+W0jsM-6VGGfW{szVn#Jg?i6||SHX%7o1E_{Z1gInV<&H07- z%~eg9+?LJIUhi?VwHx4>T$;^yI=w71UaJc>I5+zp_})%7^8NiF)#Q7Ew?j}GpXf!F zZ2euT(3SKw9SKN+%EHTv^>5#SUp<|Bzh!Aaz5r9r{_v`gUhGu3IflJhV+v;p*o^FU zU~2x@I$q+^@twf@J+FOufAs5_=P!DWCpiLtCedI1R$+Y4yH6I=yOHttP~2V>HUgX- zRz zZD3e791e=xeKqeAHt4Yny_Z&{QCGwD8cp}Aq&;?2rHHRhU)+U5Q3{<)KTTC?zaHx4 zk6f_DCYMr5tf2w0cHa%OaWKO-k~8DpMVt(rH_^FV^Uk}}+`aYT@zvi969LtYd0Q-< zt~EOH)-;-NSDwj6`I8A`3w)>Wn6rV-mG_TK^pF~nt|;fbdKoekP(ezac$C%9m%bYi z0B+OFzHG<4R=ckgjlot;{?;FIEQ~-aJqQvJcucni+6Fn3n*`rkHtx6>2MA86{;q2& z_RH4#)TB+0P_W-aGnGlbj{P%r*xMS@$`ui zu)-1rj3hh2d+aC0fUyY^eVhP9FoYRkReeAh*fC9isDb?ez(GS~m4FNIc2*`N7a6Gp zF9xQJhzZ6N1oVbKpoRW2{$$edM*i2|DAp*n`S6_&wJkE_u+&k2%K-49Eo=btYD_~Z zl`UYLsZA1dFf=iS#9R}IjQOx{UxKD7R_NC$W4k|hfj0mRU|tvc!{t9Az5n}T&oBJUO-dA} zX&!0pS0B#jeGSS4xS$pAPxT2mgh5TgwYSJT`ue3Pa^SFhW%j|aQK4BxQ%}`n!L!G2 zPo3tvPv6-gva17Ejupm#F7W8E>>XEc_k6V89WOQV26!$P$TyboEal&Kf2~jYE30;L zf?A_?Ci79K$r(cPwf*rx5mty<_STFa_PmfVZ&sRhSV%R6)ji}PQDOK}+rlVFlH#-M znqF5>f>k2Nst4^QVa#gPVCS>4pPO3LPS_14V3$Sl&)>C5v}UOsq{;Ax0GpBd zH+oyG5=A+Pw73kMu^rlaTbHb(9j}}bdV0yEO|vKC zqu-HTu=8M1Cb;(Dk2QIbX6iidKi*epLamY`eE4H*hx7`b&c5-nJl!2zw+G@zS(a(k z-9mKI*de=L8j76yBRf#FfsC%z*f=70(fggIjG|~Of~-os@6&-nDft=lkhMtFh2DzI z(HkQm>`uL1xj8p~ViMk~w<8MwY=zf8#=X<`z^OuSeM)t{a96J3ZZP+bUy*~&jRnD= z7qi>qUAI;c!*0#3aRC^{EW?UTe}tbigU41nFmap*RO{H;DpBOp~@}nTXwA!IhF)hfKO2G%FWt8Bb@uCNq&XnlpV65j0r{Wnl&mU>#Iuf6uMIAH4r~!u}5yfzW*=pJJ1W z`1}0*H;5pgO#|iW%hB@6JPHUrq>e>HGw^$&^f#R;TyLCm`KIa3!T0Cj>PSe-=1pfv z;?6Jpa@$KL2*C)91mqdKueAH?j}Iss6nncJ%15*aCkV2h7aYp%<^n`tCcqGElb6y! z)(0p(tSS;EgU+jKNM2S?T(o`JE_|CZ$f=Imnb#=XmZ8ra@O)oRCF962=;g^lBQ+j?1Ni$S&8kC69f8CEoHoc-fK!n6YxgHxWGZlugOG)E(vtK9{d0l>2Tf1^ zbC@-msX+@wK!iA{J^IeRG6c|dh-FCCJAf)~E#2gi2}8~i;$bMhdEhd5F>pZjs?D)% z8s+js@7Dx`$6Upn!onkI9sPEjSuk#=&;@E;ooWF&$5K`W*4?@ThVvRm8-dMUrF{F% zJb~(H+j)3?mR~yS$UNhWK?F78)}sOsgMmyB>unJ)_UjAR@GHZg@hfH9qI-pApZmr^ zC{lmPh6^LP^{x!;Dlq9t#?Wlo91-}*B@RMI-l-_j!NcK4w^7b^1V`c@hsKD0dn z?-BTkxt9e{f4li(y#l_;z&xU&tqTA2M>^%%BuaJ`w-JC1L_uueKrWxcGkMhoj;Ol>ax({* z1X|{k8B5K7_MM~bkxRRd86~O{=Tg+aoMcNfLj+y@u`aRu#7VLZyc0gq)>mcmzCQ=O zPf(QfRC18?R$n>Rw_t||Ep%jagxkWXqALaO>bi#6e&7PQ<|+ zGq(Ftv`nlySiWReb^e!zPj!a(iG}*Qy5xujB+(Y7liFWsQawHr^`*}#wgY`|BC zjuur^4$VecI)wA&y)7{_{rXGAUD`S3IzAVG6qfRzHS^68`WB4L_qh&!rE>FX6(!pM%e^&uLdvkc3I^8}NILkH}JPsm|#PuG!U5*#6NL=S~fjx5ZH029c zXgLuD!v%VZ-)aL3;SkUt39+BH`|U?&(S=?EWQ29Cf)S5^1qj*?cDf#_08pDK4?$-A zN7h|$N%EjWkZcnGh`+#If>B@buv?S`P)cVxFZVoJJZ|BcP^!oNyKm5LWryu0&IfaY z*vS;U28GqdA{Iq}yuXv}S@_u3P|VrDPX_dkFfGcm;0`}U-?08YwyKb@hzig&XyBWq zLG&NyxJjS5zvIGtHkR=SYIwwJyc76o+OC$I`6vJ!0vg#TOJqdK!%>lApYm9oy&+av zw$_DJv@-R7|s!`DZN_P$!S9TG+X<W$Ef!Lh-#3N*8g^9SEo=i@AOQIQJU}%xyT^ zWcU8WyDr36%sg{I*O=f##X`PcG$qLfD6K1wNvNzCBTNWN@EF09AH#>)7a2 zo9p-{Gb7^F*E`E1iiz0Ew|_N*-5-j8j=k4pbrMqTq|;4nIg$cIQ#u6=t&a&SC7VB~ zU1ETJAOV`4gyJpT|6)IIB4P+9A{Z3p)-4IjJam#>)_l;um439xXj3Ic_0Ah!J*_w|SvFw7}&i9pSs!`3<**_om} zDJXa*_2F+8u;8PVe_ZVV>{e@%^!oYoq;%&{0te_FaQyw6pzz7krjOCev>%K!B3^vV z&q%!F|6~Ohzu{>XBno_zBfuxwzWZEWtn>}|?t@QI2I$pD8l9LGyvZeXb5q32&$^Og zv0{Re&t27dtqxu=HD+H=i@2^UhL9$f| zO17E;FESX6Fktna)cr>yo3P|RZ9x?&vWj9SVC`JZYR8^VF^cTyKogK{Bx=(ro$^h} zZ!aO|@x!LSfbaKL&pWIHSUw|bV7a6$77gd~CbUtgged+8V5h7{SRLj(3{qR30s;m; z0o9T@OI1}v)sTh+EM?G4^l-dfBJ?lG*I!Cdrwdv3JHgjr^WlHU?^_c%+zCilx@>DR zgdLCGdjB=+u`%u38U9-SvIvEg2;+4v*7HJR0)1SMD!ho#e03|Y0q-A6Qd|VO<%TMd zeb(!ky*(-fQ!ck983vx92vn~jt19g?c_H;8SV%=8Wjl!F1{h=)j)4g^-)Aow&%28} z@Yp5?9r0oLgQhbJB608b$6pJWr}olnCo-TeKt}Urq8Wihhh0nD38)GY;BuSfo|CH@ z)jqg|yEHXJi@6L0QQ0FR4d6eUX5yH;zB`81$+?-hu2SY4bFGNJ16%3bhU2P3C(BTCXXe;s2r<#g&-6_t-zJMqcKtki9}>f7?J)&HULdYyr;reXOZg7BFW z4D`C=rL}A%*dco@Gj5WtSL7X+t=LwJCt`hlAlVc8*8jY)lrh8v8$@rcKG>%?djA30 zjha1~AMZITD@{YhQufPk>YSO87n;Iby|1(@)C;bdfA@9ZlSv8E2!(?J>Rb9ntci#Z zY758(Gt@E|je9$~RI2y;;HM z3}?naBi)>wTQJ)^1I3BeVK4C;eCXu_{FG;NUSZ{A!XxU7x-M*)p{-W=NNe06nsdhQF1^a?KPow-E-fxf3Ik zdT(N2pyI%AX~!4hDSV` zMhIToF9~%cP)P1bkJ;D9ohg>^sNvd*gZ&<+U^G>G8{lpKjt~q=e0cZn+n}NW=i-XZ z8=uP!2GXki_Ml0~5j$VmCfI@O_d=S;=3LDZM9p5iHG72fWN+h@V`v^ek2cw|Ny&L& zSbAcZK`TDc{*;X8pBpSR!VS0U3;ADYMuF ztt~F^)^HfMTQlsvR4rm4SFWO(V<_LsnvZdRGHykq-TVqh5mZeipqS7asvjRyl!*(t zq_7J3nSaktc}N37bAaalaMC6FedQ+0$Hx;ErihR#wT8pJ?@;I$F6PLW+t*w4h2M<8!k(=asV zT`zOsH^#mR-bx0rkf(AgQY;8z6UNxK!J-v{R86U?Gn4s8h9K}8oOi@iUIGX0pS~=! zUXPIMRtK)W{E!DIhR>iW2thd7BP`rf@EJ$jYute&mwXWX?)Yy-Qv~HfYFo1+L;%{6 zMn$40G)Pi*3C8}l$5%ZCw1+@FP^Zc^Se||lJJA!1UpCV!u#yBQh(IU{;rFp`;_$lJleNCzu2$Nk!{tE1!Bguf^+j z>~=YBaa(WWel!O&BKlvyAoFEm^R-GSZ?h2K)t>}@flL6Ke{uL|+^x$`!pWPzR}hzZ zy0hlnL_p2Q|MmzKOz)Oa2$Yr(g=A{SoGIB5G3bR9LlOdb)cab$ z=(G3|3UX|V=IIq>NMJsoYJld1HsDCyKTN5#?<4<4coUR4|2-lhy>$lg*vs{Sh5rso z|3S|IIMl0Xd70S|Mtw|onVf031_mt{Xy|k!q(lBPn2+|`g&ZFdH?TMP?Zw)1LsQXS zPEZ+$sVwlE-i@Z4dR~a~{uTJyJkqGa+bCj@B=4TngDbgAB;0)gULowE?8|U`F=!&R&uQ)0zqW(|7 ze6D5izKK0p2Wh&lS|A2Tp=nhrqXQ}zW+ZP#Z>pY#CY+1Tq>6tm|2d+ zZ#@(vy`Q!wZG4hOJcACzFaCW0wc`EcJ5A&FruA2WF#vr3_xEsk7X7?EI6kw%{^lmW zL-1!RiT=v$Tq?;EY5k4Bb!X4TUK(D(rmNvGWGT}R&w(jPx;@Zvg*m%0%p!eroRym- zv;^yve|=nNQg;9`F{{Ko{jE8OarYq`1P?O!_!F2$eMp-#@eJDcjFy#!jOa17?5Y;p zh6qNHJ3{yP?RM5o!BN;c%10h>Ev5hPT9Q9?=+}6?YWG_Rdef;p>eArm=0N zII2F)$v`O(Z?AwpadGZ2<2FJEAjcAq!H}xp3P_N8MSc3QN*zqTLJLrIH3~&p-qZlS z3wm7wNP_}I4FZ634MHt(Ky-+)JSay5zLhSDG5e29O&n0SV7IczTxUa0>0jK5b z5bq5I=pf`az033S7L=zV>6V(vrJTcH0WZA%u&0Q5#6gI-kbM4JITrhM>hRF1L}UGTp8PwE0IV=z_>1XRS(IuZxP-dQ1OVTLeFn%oDI@J0hMQ0g+dYO z)kwjo;^$U6A$>yeTxjDhh{qFI^UD)3@7V*h6Yk1u?N90j$Y3L|eBR>#RyrkOoH)AL zF;C`Nk%4{KEkx4dfYdBQ<$4&yt?Ac}*=r=`SJ2OUEeWG*=n2Xo&=Mvj`COhl*Hbls zc7U*NTG=ZuE8cx;D|(oF=x5sePQ;#3k=?HSTAii?gXbPQulO}Jd_xA*6buXur`$`j zr}3z0n9G%O6%6ez8xkb$*Txj+;#S(W-mdKOkqJ*dw9H@KN**9Hf=NWcKreVTrD|VX zL>yVu`eS|_wA)F*+C+-trR99ZeRESDNxgDmB7FEavBmKx*VGD7Xv+}Nf4+TtF!n3n zo%~@3t)ZELzUb{1$3yV=h!$wonE_d?(W-VeWtGraGyE6c+ze4}AhzR&<%g9z#;qg>)}^t%Vq^pA z`7|U#tM{M<%e=R#IweJJvSf2q{`eSunA5n2!^sgns!t-h%4*g}lW>M-@cts$BpZS6 z2ivdzKK`0Zs`Z={O^2!0Te>)%+}E2XZ!{%^%rKQ-+=*gAHzqBd@LWpbe!}hcgd`64 z#@Pw!ws4O5#PJ^dr;JCuZfpgXedV9PlTQavp8gz)fs5Kd{$L;QdPf4M#$W}$$IKc3 zh@0$y-krOhSk5GMH)-X}I9XoGfgrZ~k}|wMx z;;Q;jC;knNB|~-(=49jQ*g)=_cdY@Sh9rcTrU+zHf)pId(8YkO%P#u2gmdm@k=eGo zpG#`E;0YF@vkTMOhfqsaxl1Nt6hkQKC+On{^5{jV5FH6wS-u94L$9O1)E@&MG!?O1 zL^J%DJ{ZNC4fgyd!_JsD39u4mpZYypCtd6=Bl6fRwc6jIs8f<0P^lNzWu6{kN}A_s zC6$m^X-st}x;}9gGD&P?bIqWi_J=bw!V8|~WHN4(2vba3Tak4gGYq!~l%#!s>c~8; z$Bmt4n@#ngt2ruhxb1~$eSAcG{?`t_*rzAishGE}P*n?vh-KecSJd8db- zf*cu4$~q4)6JKSLKDk{Q0LJcmV2#|RYCj0RVEK5l5V30{&@%<;wtcPCHNJ%Y+sw)v zj9DC5?r`CBLa!P5R*TRWY$KAroXl>q3Z)IC1<**UgPHBw{>2V;1JCH8Mw6G6jZ zdp5#-Tl{-@33fiocMi%ll4n=y*>sNk%CN6B9738;>z)35C(|6xT301Wo`;O0kkgE! zeG5_BKZB+!?qBD>w`t`3?fR8`f8m)1hhG-dOqFU9<5~z zP*&X@l!gPvJA(q!t51jk#*R@6a(%Ju#%NKFMQ`4bW8jB-g8c#a<;GkRHEYOPuwan2 z4RI!^jXg}*lcJyk8DO)zuknQ$Q;(Saua59K#iiBZ6G;C!Hvi#JyD0^ZD7_wJ>EWR% zR-B))%TCy$1$vu03fc06WC+`*I>r{+2^#YYBY&~kj8du_t~Ifx+mh2G%r*tI@f&dF zwsb5~!O{;U(psn40c&-T*D{GVV(>BXe~~G+QfpjV`E2Pe??AECGs4=kjo2dCYQb7-Xys&+(D9 zEao|Tou1HwZ*1Fa>g`VbZkW=`2> zUX`jz?UF*!uJNQaWCKunmO^V|Npki*FmqV|Y{ObCrt%1q0$0N`P~>#j*Lq5fvdlkA zN$a8Zy<)J5a%gj}+V{xdZpbQTOp|_0H*8m+)`}b~nE~ACV`H_s%b#P4Qw=QPLKQ=l z0HxgoDD6ze6T~WdKC#jv1!9m8c>9snZ_A{&B+(u(4e6{-+{zxH>6rL_qK6{oGF@f} ztJ`rN*|WNJX7Ke7^T*wGtU25>xbS$+&wxY4H4vP8O9c@+1)RG#?!`hAkgEoLY>@>R zfkJzOmpN`vl*48m-7ky*k;hMhzj?-pJyVZR3AG zd{ktYzi}c_5fH&W7$2w=2ken*e!VD=n6vsK?$x26C0+LO6<0pd&pS}tWLApmDuoO*H)t4#J&>ejsss&4|( zVowN(5-vUm!j=rKopJk8oB7Hs+IpAJIi!vq-5I<8l%KnK4b_6mHQFBI7O4?4_0^La zH&#xURoLYc9y|?v2@UMSPZQ;v)$kGNZ}j?Bs?=pCd96{qH$u9bSCW!O(qY+W%6iaW z$|&*Qe-Z2$wb$+)aJR>!3bk})zv%0%`d=MjFo5C8&BX#dz?K;fuPtD|Ak3d4ZbGpq zOBgm|JY||*)*k7Iag}?=Uw;9{4SaYjSJnDC1a1q@&(y@VZDoi!-EDXp2Dl=^Gs3Vy zWe3pzKkWcp6b9%6%#>=#ddv(6TP=i6koMCa6PbWb+y z)2DY$A)D9g!S?ICt5qU%>?;98@tqi)lQMdOnRCXR+&4d3JUo-ubnwixlX&>Y0wMPY~U&f1U-q6nEG9uFuf1(0a0- zbPEwAJa>0&>S=ib&GaJ`jB-HEC2tm&z}Nuviz`Q zfJoshSheHunI%{?Sm;2Wk~NY6v3De<>plB*KUWOmB65LnNddwm%Sz}+GiCuDAAK+7 zqr>SxYRplY+b@M$ima%cgR9a7sMBZzro&k34=(>Xn67dYM zKjJ~>+0_)O|J8r3o!}6M%|?cG;8{;wK9?&xd8wEg0fDfS81N*Mi922c6yhz*M|J%c zNjx>>7!Je13ZD-16dA|g6KN{jLf=qq5@pM#DL@8_yc5E!T7*5jVa8xx6SEZ=AhZSI z&C7^XHn)(yxCw#E_Ind*k?8kGU)#*HnBC{kANL~gvgXUl~QM) zP$vi9R{pg#peVeg(UtxZfMG&0nkSobEH|O|5LiZ@`#Y<#4Zq#A> z362ar>kAP(t?I*#?VIEC^B^XtZ1zS^BvPn_ zVI}@)2}(lu-V9Tc*+fyV{+WfW4yFK<+?yX%1c~`X=~Ax`t37HS>uo-i+7wYW@*xvg zv&kVLQVx2U&#gWKttOy;yVVg$7w99vCW*BI%h|oiT81*?SX|FZ?Bg?M+LqxG6dEko z_erSx;LE8**oU|elfA44L?@W6HAioCm7A7!g3aKie@;VV0>cXyV9RUU%XQWblN8(^ zh$8dz!v&NXy-)c%*m%!GQ8ewo`q1~HNNVfrd<%Cx(NM9CoLO|k6SpN8TnsnWj_FPi z6wOWVM|T&vjC1Wb2UQMfGS;TWx^@?HV7jyB{zOl~hnRT@92QF+K@($5T#pADEL(%( zLJ(3wcq9Ay$!6Dl{dvUtwr;j-_-qp?oxY^|NUz3VgQY58yK~@HswGO_OyRO^Un3B$oGk<_7GJNtm4ly|VB&S<0t-WJiN{0d zC*UrEPy!CT2Rymu;LoIy;P$RD3f8f}Z_yW~*=_G4x}Q;ww?SL{tPtQ<5F)w*e&Px7 zqq;$83PeU8RQUdAosoh`2&D=2-%iPC(7by4YxrqlD3x!=?p7!%YgYijnE7{#stWSF zN&wOL)@7fP0k>)gT&hdIm;>ZUo`asKTbMqSJ0*(s^_R^d1+PSqfE zAQ{`x=cKy)PoO#UrRc{^e*K_nl~ELx_?(_h|4YDXSNv)fzzJt{%kTs9yPnT_7Zbg_ zdG&D;W?~quShsqNYpg7ik%64|au)Jt>j?jO{x{pNAwdV!CT~~8-(JWTNAdF! zi8`XgI+zLf9`Uji;}R91GFOOcg6Wb5Qr80NU0gWGv8ISPNL!1Uxo_KwILE#zAv$}I zWRKcQk6QS+V49{R-|C%bUYo9R$zf~@CnL+=S|Pv%Qj{`)M3Q{}9!i!Fgtoc?5^RA@ z3->?{`hcVPAFstDDQO$vTICGCcK!_RkGuH5{99lJyPSrW22w)L(Qs|$q0v4a0$97+ z=H7I1crLUe8w!tjG&hv;!JR(IKj$%#!G2B`Nl$EQsmd_zW;m4G+B4c~IgwdoInF`Z zQR|0ZKc_O^cJ147(3f9a-y8CrBji5MPJNm1tzCaV)=uiN*}exNDU>x(6j5?^)286- zwS)@EglfslIy{5j7zx{q=<3NSgEpi{&tS`a89&l&*57%%8&J`7+QgQ99^?dMZafr(p#Q_!R1=P5T(|R$zdgON$t>bP$GPwp1Cv8>b^h2CQ|55jy&&! zL(nZ78l|}Z8KtT_z-4zv_s)dM7pO~tyC$^QWwEChouya7CQ;M4iGw7z$+Ql2ws()> z=!%)8CMXunhi;Dz=)3>F9E}0Wv`omC_clr_HvdQFw0fuP?S@Aklr=N}znMY!4U0Ge z=%hPp@RNYQuncUPJwMMIIWT&0_~% zJDiM6?}6R~J3dWc2!2e1eX`H(ZK~wX#yoJ>gM}q-KaFhXmV2KZ@dAxP`kO4UI-{apgG2%BK%(L ztLW<8kP32$#S(t-d0t++wgeS&MnhT^RHgpm;@o_-c3Tm+fA9u*6TnvsB?1lbK954yJs z7JO``Eie^MmoYIk&_Qw?^_>VQ$_R-%ffmZQ&A-?e{}=s^`rXkFS9SQpx~ctoYTFjB;D?w8JRv+{WQpxIZ-?M*IBz8 z(#T1hJ@ShzN0bsqULzSJr;pdbny-xb?n}(D(#j-E*^eL;bBM8VJWo;cSq#>H6U0;~ z!3|uxL1H1-n1NUB!G1fy3eC){02`_o-Q&fC(({2^u27U}!FEV{40=UlC#`@}Lx7y{ zreLcg0PqoN+w+-K@5Mj`_@~y-15vDEv2Qj>2p&ixW$}kEazKJh)4}G+V}Clrt9ib! ztNLFSE=jFMaDucsw1eG)G1*?)0DoLi4adU zWE7G*Gel{&cSzrRJY2kQ@Il~5L-2XlhW~< zNkN*Vk@k$EXbIES>RzWY;U4t6=HmND_fJ%AaC-of!H+OHpUAeJShemAo8GUeTx&?V@z?WEcE_C;w2$ zVfV3dGyUzyL08vqZZ1|LbB4!f@pNtmCNU;D6Xy8Qlblj`3Zaqby)&>_)=N|T2`ENb zZPmhn{oJ=g*O909%-C z11Q`tkjmI_dLT#2GViKyj{Mkylg37s;qb*ui?9_bR@xT2tAg$S9E)c|7XKa#`HO>e z*hB*tnS#fN0@2B!JV;dgOQF_(a8|QAcvtMaEaK!!JxoW7RTo?F-zETFgxxHkEnQ?N z#9=h1I&II^t<^^|>@D>#Bae{Gh~~xdT3eWEy%kq;JehDW+I4q~f1rFRY+7Wc2RPg9 zc|^CmMqS+3BUOt=Gl#=>M|aky7HZn_%a4^|q;^a<9qBvlirc<&Wa2b6k+-lYWrdhc> zvGT*fn%J1coE4wL?FE*|F#AIDQH?=1mVrLG)q8^`%FF;gko(pp^p}EJ+%x6%xCn{< zq!7I-?k_iXHa(3nA@S?wKW-1g-TTNOU9;@-mC~A|ivtV0lKHYRCRQ_F(d+DCQj?)O zFftet^yGq?)ehf6fA8#GM`%~mJUUDQTv_nGc@D3_f*wB|9#d9P#@n}n^^&2xP*V=+jV|VNGXX1lFe4w3Y*00- zA`q&K)|&B_x@Yu)sz$$ckezoYUl9R;!;qw?JOi|hBbbG7&w%cBMS95PzoXEFpHGeT zxxSQ-!Lk^@)u#67$FnfFH&}8G5><4n3I#Te<U6qPy>#7}|n{d{QAsbngM8C119$ zQ9^NK${y)?&$(r->+>lXdh1?o9nJ0C zk#3wzVeS}m+A+H=SR;~ipo3W;w}%3vEhdD_Jc+c$*%{Fl7`RX^!;i9=cUoJnb>u#G zdYl>M@W|o*>_lSe7pAI6S0S<$Z#JbT?gG`IiRv(uWfjgL4A>lka8M6Mf*#BReUk}3 zV5Z;418d)t>u~`nlAMy%N0-5~21W|$$nAm#5h05Ie(WqHwWs|HGyhVk=X};AXdJj%50tWld5v}{ruddo+jLPA_{avyIGS$Evda}C}pcgY!6ydhKjR0xER}dGyX<5@oHgPSh35GUM}gF%ZF3D-ww1ZeD)Gxo zjen}=iySX71gAlR=~B74`EOAnO%xY!!B8X=+R1a5-$p*1=_JU4VAGV`l(~S0n>PYE z-^0O{FtkDoH>}r9bP6KitduRxrTcgo3z|P{cEHO{pB*fpp)RJSsLj7@#wr^MhvPW6!4e;`>k7&74v0 zgUl6Xl}pc;6Fp7Hn_FQ9?@?DaX<@yF(pCy@#WSu5o6q2Uh({z$+esLhR-*BH&lR0j zP@We@v>F&0@RDT}zij8+Jcgf$_I7w~j9%vvzg2+0tYr{e;fTTunNM%&5?(>!`wQ9p zH_Bz`j;C!NFhwbC-memhb6SSr z$XAQ3g!@A}PmAW^(OPi1pSCc)@MuD*!b?tIe1y~=nj=A^V2)kb>U8+u2SRf)XbFNM zG}dvBSGa{{-kn_`bydVcjB3iQ3ScwBt=X`eb@=TH*6>BUMnvY{DT_=>Xf&v)+!+N< zEm#5OX|>K3$XFE)_D&q*5w-Gm(VF2jXDT>%VxTuT?_(f61q>VtkKE(d^w9$8siysy z+!Zf@-S~s8{dG6NF(snO&E&nliL$t zI<@FlH)BmD>vuSNDCmZGsVTy&88{SlJx1lZoHG*zSt!v~1U;P1W?-n)KLJPLq-u~KRK1!q^lHY_{J>dFU zo;Sj*;VTm+$%F(u(ScXcB;2V-xW{Y3iCc1O1YF^&vKVF0Hz#G^W}=nCh>MjKinRaZ6~}uE_&0m)jmYk2s0bP>SPPOOI428$-OP54oqhja5J=wKzusF0lXm z>Etqs$9sT=C<4~X6K0LSX$W35ex66D6w3$QpZ%jho#M`j6V($U#QSLi&fT|}W_nO# z$w8zZMglK!ODm_5VzKD;TZk&0Wrg0jR+VHXz+KrZ^6~7_T`n#>l;44133X4%IRs^s zSB`a?wE*D76HpKJ-FBLaBY!?^rl14=$ER(nP^ds0FijC&V6UUTke&s|e#GYe$Ofbj z^t83dgK&kpf+d{WCjMCJzYkk#Oi`s+)wxn}v;mBBm#=I-bpBR$CI9Pd)LC3~4k=jy z<1%YO7T{w(TNs+}m+fCC9-~Z^6LGa>-!)LESMk z{oK{{Z#M1{6WwKwHhXjN;0E@sKa;6WMfU|@$4d$oCWy_ z`Tp`r02+ctXK03X(#`A zfsDyUnJ}==pO+vC|EBm0()!fX_PvM(zD{&yfV z7w3aoXNFx!6YMZ#Plq;W3&CX&Z8cORRr%(RAEEVeNeAY_6F!llvyJ9r4$?g6_>kpF z@ce0M_BTB=7dU!Pxa|<^cXCj4b9KwU45_zA7euS4O5R+|CZZ%eT>I<_M?oF1LGxTS z;B-}uC+1PFtHOJ92LTYtO6pJ08^+PAQrzx?(Ibr!ax z;0-A1WV5Q8ON!0t(lsW`sT{bs61s9_&SS4=)ChAI7$4>uoWrCXw_mIVLo&OACVsq# z$MP%s1dG;LaYzrFJe=6;&E*A9!ykV#(Qy9;lt(L}F7%cVyQ&PT5(!(Y86m-y=X2u9#YM z9`sPX@{v;-)gd8NfM6|W%r&XC7RUwt{}A@(;ZV1K`}o)sqU^gUOSWW7mLY3Jl#*;y zku_ULmN7~tBqW5a$&$5@-H=e(_p(QJ*#|RYX1>>Z)cv_X&-46_-@n!IZjNg?&+~P@ zma9$$@c_q^zEJC_1{9!aqzl^;1<;CLq3+0{p4gHNYFN6Ff|3%V*xzP8ZVk4Hc?ybB ztV+zKAh%{H+1|rVppC6|wOXJD^amU)a&hnEw53oCAyX{p@TvS1Q@xaUI~xobD@%6D zAe?{vx9Fi8>E#Z(w#}lyqxPQFBrNH53>EL{`GOR7#ew-xH-9T~T5y|Xylm@`xe=^% zRzurzFeAN?*Ea8Jd{UXu8~@rDW*1r_)xgrzpISqsCwoTHA}=)Lkf!}jsZlhrp-7&+ z>#dMCY5vP@!e7!HF6H%28F6%d-pi&$!L8oL;i6<`g@FkbtxG*+>Bw7J*OxI5($npK zS^Lh$8ZWp7w7W_Tn`j`n=f3&qpzo+QP<+_N-7ietq*Vu_+$$VYMXp#~y!4M+{2C~< zDGD%VarSd&M}0Aj93>7rj)AI%3aF~#OxpHWf5AJEUYq7nJ&3;1s_M|2^*C}o+RoaY za1vD5>Ufn`wY1E7{z(#ox)Q6CED@q07NzQiVRRz=nAB6rY1>%2{xaW!Sh6Q+zR%x1 z*T6h>r~%-ty&nGA@%_xLw^amA^Zuo^V}qk-ogZrzZ%glidPAm20)jZA?>(_YH4=J2 z0<=ID;T!V1!kJkXdmcC8Tx7~l6`YkwNVaYW&c4)R4@Q_b{l%Ok#%dt#3oUzJf$u>j zN4!MvYq7oUG+)wYZCiomLIk1ZM&vP({S{N@J;iNaq}<)3fXSf(;AUnjSo(v?Wy-NbRt zf*y=WVACXTvgbzVxi3=nTatYNXcyw!>E`(d^gjuCwzF-;w978l-#?;R3*I@%t*5LY zfq}3q7l(o7*LDY&nP&au#Pl~F#zm+Imv8;07NGhAzK0RC=WANmiWYROWmia>! zDh4g=r&a;CChZW3P^6^RlF4*;!;3iYk7a=UCK~?Ueh`eN_GoNFeFc9(4wUfa%I0Of zOFItVeyp5!tPOO_HrX*P8F zfuEI}6{{|iswgjt2fVR91?o9v2VfYg?prq6QDPxGX-oTFWzzO@72P+=AW@t6vE6y@ z<)xhCiPw;C{6!q!O`p0f3)NSRPfs{MU#fP8T=}p?$&>hy)Vt2UHMZ_9Uuml*r8)Nt znO;`;=oJp|#ocrP&T}aPSGQ7#lD4G{*Lc>&d>I9aGJ!Y#dk>Z9zE+-{6leCwqE(BCYC%R_!s02;JbM{^AlXz z&u9)_7eueWR&wlfCuRhIvT}v51r>)C2EeI5w)vnvxukbCH<>jezCM8^a=f3;O0ZCs zB@%CjJyPb|E&;4C2PPBavR6Y-r^$YNEy*hCq05To4=|h;%$>3Sz7BBSTH)gV6!gLG z??ZzAELA`F{Z)QarZ4YS$rVT41O?`U2P$d5r){!ZwFQFUAy3(xTsH!dt%H_ zv!*Tj={`lAh1iyxJiy8Q$^F6`KwPQ;Q?h>JCyJK}lqK}uGwe;QUrowgDa$S%L14IOT`#_PA3HAR@_NLWf^kw7rndH@_~U$bTPR}v*>72j z(=H?ospg)w@oXyYqDw7Kjl<41A#fUJ{^zJ?DyD1YOW(Lx)1MxB>J$sxY`X>T7VrnG zs@xbSn_$H)#f=5_=3UMy7&$BTC=9OCo%oJ#6wv7mH8knnA16R*!U-1J;q!1r z%ldnFFcynrdoGY>=)*W&7R6$wr<=fqMzDIh%{<)Iq7Tr#8PIE^o77t!B`ZL^f9+pM zf)@PkpPm*-*idLmWkMzU1ln$I8}(Giaa55$7@2Q`X!QAA=w&~>VD#4pa))?a>5JR67xA zU>iB;&D|9_fb!x}geBCrwW85%XMO3BG!0)G$yT#^PhqdA{PC==gBW6@_9!%Qus5i~7_)z{5>SGKT<#;!Av*mwh4|VxI0LR@&l4YNyujLyNf$7N*!)h}g^ss>XGrQj2w>b? zdRoYDAqF+kU_yy+qpZ`#470 zc6W|$&f0Z$)%~m@&pe)=?O;F<>sLtk+a7a_d1E_r+)?x}E6*9?%8IdHqOPLpgqDiw z%5shP`r_u*lDl_ic!yfUW5R}M?NY-BgJ}yt27}iZX$uXUg~U4(#hf>A44_i5#q=V0 z9RYg$5ma*KkNJ^?^rbA$4w?>K7i=5Y97z9L4njl7D?Xff*N}nCuW?HHW3SB>{FVpi6f(gsy_TB?hj;um@NucOo-jkTvDFu=MIUR(azoX$RmGYUluO32 z;8#~dE3;XXim&N2tJG0}ny8Op1DeuEEk96l z^%5o;LqCrjLw_WX*n7U>vs(2zGc|AZ)oY20prPo``CXS9zr7ck_%3hu%*@KIj|}r> zH?)^8hnRi)qG|F%B=i1Mju|NJn*Qf?tcjfk?OO0>c7Lr5ysLMCZV5=8~PjNm1 zz>oQz2DQ?0z3wZmhYKZZ?y~~V;4USY9qVB&ShKJs(*p_=^a|>rK-K{YWKo^hJfxSb z(}SWFIy-D7KyjsDxw(k;(6KdNL68u{NSCyNs+=aB>CdygSB7*hP3$|sH1BhP{#O;+ zXNm6Vuaks1e=rbbd<#^Vk;7RE|0X&s@&m^%*^*ESN zpyF=A1Jt@pREgs>p;#k-gmg7%3hp@Qy0Oj>?_4+w6eiQa$0FL0Vf$_lM`=lmV=sE% zxr%iAYffF@6zbDF71WzdwFBk}FbKl#v;G`}-)~!ybSGSmaI0TKkV;pd`F%LNyYj`L zHqhqXmzLu7NOjw0$L1L&`a*gm3)jS}zVJ;p@>=iuOL95m-B5Nr-43ro_akYK;5!6g zP`+he;apeseqi&3OUqU=YCaXTDjI#5XD3^~)gL01Z;pq*yBN%8m(#Z*#v<4Kz!{6uy+P)EacV@)9Ho(hHr&(~q0;LcYyG z+H$6hj_&x8pXnH`FuStFZ}y4B{&!*xslM)hdWoim)2vOnx4>yU!~o#ulno!e^Aso<{&P&!%_^IQMhF*0#rUAD zT~!}28naFs{H~0lv=QX*4R`aG87^}SbX)JO)dl|t!zcZQr;5R(nPY~D za=L8*Tm0EiQjJUbO$Do20Zl32t#>;kqKZs^W^7>FCp8b#vF-UOze}re zc)ielnOjm%RHov*qmg5;b6bJM!*=;|E`#<)MhIKQ@^^Nxw{lr)Y@^Vny2^-b_yLol ziyRk~EBp`f{4F%w51;_RFP?tvbDMJ&;#n2?TW6Uni~Sm3Sw7u z|J-jO@+lT2%m25WgwB23au7K8TJt7DTd?+_{h~9SHz3pPAteK4y1QYRkgSDKwN&z; z9}vi9R>Ty>LNdOs7KCf;xfMyXihXg9+MjK6UjUu8#~5EcZ%dDQBna%`-~0A3>9adz z+ElQ@$mIp0j?!>ee4l7X= z(jBW`lq)h^I2UzxrIqdrVYaWUWPVnkL=h}lKA+u1!tLUHW&JyiuX}G!!|S*2)TG*8 zYH^!jjtxSN(|-_f3AF7p_Z5k9Z1z<# z^61bIpADZlPzTx0>%wd`ZO(#sNP>!L&Nwih`OcFr!!tKHO*VwZtYaG{oC?<1`zu~h zsZqdATPX-U#nKM^S$tqKN!b*|!%J;7Pcs^JipiBd-lkpcPZJ1h>0b(4f4SmM|MgIXO%ix(5}};U}p0 z;_Y-WCa#fJ-{)O*IaKyMX_m?2VL9a06x}fCEt!|MoB3!ob?#VBVOfXf5_l>iS77N_ z-xnQ0ZZ9vQL~v{xL4QmaQQ4cBtRXf&Gr#ii+TIIYW!ViB0TDDBb*^Q8(@=L%Z1ZD% zyL`sSTO$X{QV%CmM4m!Ykk?!PGL@ERXMv)S57Ou=m-Cw!x0C{jUD~LHXTUa4?qY@s zq`^vb>Fw6H-rJ~mF@k1jv=*=>Q+11<9xtoQ$bU`$J;PaExK&#ky8KAW!Q4=*?Q(%Fo=hDzmkdG)ZVb? zLyM&!G$n&SA;$lx-YtoZ)DX`jle7MmFfb}_khgOQ^m7PGdEt(q3k!7?n!R+%c_4|Z zP`cY<#7wWePW8b9S8YdzviOfQkc?Duy?wi>-@#Dk%S(-(91n9}uRV!iP{3#Ai*F@z zMjYb*Yst_02s`W!k+7}zJ3n(F-SWQR?8XNZ{6zZ=t%iy+6f@4{7m8RPDlWmZAJtI$5bR&p~z>|4OKBa4*=LC3T z@S&{rxI2%F)9d+99{_-E`Lu=V)V~2;;b}9+-@2Ip$)$#%OTzWDMOZdL#o-xEKjVK% z7xIe#0=CRqX-}&skUK_ZRHEx5h?_#8xasRntD(7Sq*@D zI;LRI`J!agu!+=+z3hz}BW%Nzg>PLcETUozE?QpyQ4;MJx#(GJxG=3jFs4x3vB#Lg zz~FPJ&v2c?HP3tZ3o?3`BaGv;2i{7U}J71?aE?aIpZ8 z9?*SSIqEH$6g9z=S!2bbs|EB>V~OQY$?gh~05}C~D!uHn3z&2dRZcn~!B~tO9!@n5 z@KOqZ5)hBgTNds!1CWs93clx?Pz4{W2U7{UGchuUIcwa|(!Q?mOZ%C^5Br<2@SOwm zU@em+?CqC;d#dz8Nq+9nQ8bhC4v1kS4m>9HZb&$qya7C3w;i1a0yUR%oyNQ4Z@52| z9_MB>i47PaX5}u!cOFeLUW?^Jua_lv32c*a zsE@jKW43i?)8HpQ*tpbH$zGNBw61y5{zxye-mqHAF~=?yrpq5})x#5AU4QR{K?he{ z+_!gO1nq`}Rv$2>>2!p%krVT|#$c7ddv7S>< z!IltEi_fFf%SL5N9Z{XvnTD@Y^~{vRV1`mh_yb0g2k3)0!bt8r z#(T0CP8B?6pfY;*S^Hx*SN<*2{8_5&bpx->o|I>pf9tSmX!s~mMsw=I2<@?Yox8*5 z2U*HAPW>#dXMQQc4|HN%@&GvOdsz2*@-&4*{GDYe0;=mm>aCuPuC-#)#K$|~VXMr@ z&fHfxqWMx6GA}92RW%x`S0<|9TpsuRWX&_J3&#~H*y7veeUKejAVfd)osa!adXAX- z>?|gM0=&n9sCfF_f`y=wn=0>5NJoPb?VNhO{+H8i#Mv#STNisWmDB6Z$dbFTRela6qNQsyJ;{qWdhG*yJtZ<=vH=X%Jbl`AI!WLa z*8i%)H4Di3Ww%1qthpfbXmg7PU_L#kD+G-7{yM*)Xv1tc&Ff-h4kS{cr_DiY;fs^E zTiB#sYU6VDgQG>7pZbll<|xptLyP>bt-WVf;?mVcXhD~aXr~d#?_8h*=LH1=ZfvYO zDCAvcl6>k1B)itq^}>NiAE^Vh-@i<*3x42g>d0Uh|B=ZcFzlCIT$~X@;!tiqr$vk6 z9>Ye*&m7p=?D}>TUY3L3-)bj;gs4&9YDDktpz#x@*6UzfewnxS>bhgG^6sRRHKW24<^5*b)clTJ*LF=b0|grD-#l0N**(`4 zw1fIC9aG-D`gqarR`(_=jGWR|3&WX`NtF!~ZCy)FHl2$OF_hokEo~hScA)`b6X@a- z1o)u7-AyJ|fHmI~po5$%mekn+v#SGn{DNtQtmbngtOq#IFdWxZg z;1LMg&r?)D>oWiW50EM>+iUZFw)pLupGj;|%vNa@lG{TnrR!S{`jeet)nxrBA)oDd zeY#MMTs_lfBKu|U10At&34nYQ1oAa2IbcN2@-}~6SEe>+;QR!hv<9X!g7-Ds69e}s z3V--K>^Z3z6@FQ=ec)1GZH9DXs)_5)#Tz#U*>IGJw?oZXKlGS=$WF(^(36&hG3CTk z*dYS?z}mLg&KjOXI5M_ocxR3cg;Rx(sLz&7OP%pj$@-Exu&@q0Yt-vNz0zt74KA>z*4dUAiw+oM4xSg(MJmx%ccuX6GZPy0V@^%na zXnY%Kv$$H|I>Xr7qTkJK@`z@Ju(xLkdDnF4^F71;zsQV}D-4c7Gtqln)5xKeB1?c! z(sNn7BQOhkh_6d|6mlpQfh}*X&{^2(B~;(dSakI|>lEmo`uOQux_cBKfKe8@yh5u! zDh0C6d0?|i2BlFLOR~^#0ka951#C&Tx0`zQ-^>&w=;O8r!G7d{L0@J#wWSn2`$4C; zc7}ODGVPACxzv833s9l=AhnL>sBS;f##4O&?J*8M*ARY@x|)N+$i2g02cGyl+7?AT z8Ju&5Va!;Ek1R7aUv}H3q!l%a0NsZ_G6o*qDT^zRa75aDbH6gds|Z0p^X*jZl&^Pj zKo^)d<*)YpCurUAZ+ru|cY%aKpmxw&X>@vq>f1RjWyH~hd_6Nq%ZKgC!txo{vx^&^ z{J70%aUw4)y;4DQ$;c=z`JguPiZgeCUYz-fko>u98#Jaz{~j~ptvvW%)!vk3byPip zl>@U=l|I2cu-T9gW?%QOd#2-$_zRv?``jAh6hPURwQ%A zkJ~TiUFToRIfBcus?}FD{YvMJZ{Z)>Eu35xfI3V%0%HIp4o1sIuo zJg}h-{h&&pKsFOnS%F|jx@B6uYX-}m&Lx&4KJV(M5RBvy!7~i=+$?j z?k)updZuZ~rTrF0ju%SAzQnImFuL(-TWZ<6SUzZXF#Mut_LV23ogsTquk;2GL43n$ zG#*ho^#$a~cW1UvZ*SwPyM~GU@3u&|s0qRxny88+5e{Q)18+DR&UV#RIomzPxUyP^ zB$W^QizYC*#(`VWTt(rfsGSxIZOCQXML|ZML7vGIn`|% z8_FCi)e8fp9Xceg15R#S5WT*ol$m`do*ZhG+<6=lD zhCgdB{&%no<;cfHCOTWvuLUd+Eyr+AR}OdKB_a-~hu@2#=IDh0x= z_8v=uAzw>T0HCmIlBhh@pECNE-t!Mo*w*rNQkq~7Jl-q2rQ9||4dm z>-oQfnabO^%C25!)$(*|Ftkn!J$PAO@Alh|JSV<4oG3g}bCCF5WkGI7!w$~2HYSTi z>~!Q4hA0R`jy=+*dPx0dGHP<;*ncQIk<$nRxWOTy zx88Yb-;&>FB&Z7Q3bZxg8p>5nt#LtnWe2W9F{+PwrId4^kRx9C|Rge5TRS z+(m%9Oye3Aur;FNPG)6%2Xbn%;iDWV(zhHrXs6*&{qpLES)t7rfs8>1C!30Kh-zEr z#;*3=kcz;vfX23Qhn|eKh%~(ESg{(h8=pLv0I)w`Ql8YcjwaI8zy~+ws+6y#6javx zoOQ7)HA93km9}wxyn0zg>pefUQIEfA-(2chYeAr5Esn>3Yp9wNKw3|#&FAIZt5636 z=J?Nm*>1(Yg8nIJnQpw=diA6~a+CS;zc_vQhGKumSKZ`5zRR4_m~f?GIw6bYx4`W` z=Fj_IWX|7u>>pWcG<1IG_?quQ$aCfw8tMa-{xNgC;U7xx=S+Hk&lEIMk+;D=oBMTI ztNcGHb;7C?h6U|VhtODB7+Mh|BIwfm@`s0N@@z>h@6S)=sX2g{wJi(&29;uFS zlknzu9X);%U234Z$Z8aB5N;Xo@v1<#Tu%>o`j|!O5+kO&#fmdBdtAWO!d;Qf?9xr0 zH#`Qpa0gFW4j!_zu|0clc=3u?w||`fXrDmP*_{c*E?2}MX`p2}kw4Ez%qCjqtsck7 z%)E947?wtPx_|eYtbYLL7`3_+bE?ABD}Ug5^7ciu?1Qc@CujI$xO-0a4P_XIush}( zVh@VTFq!8rd0o7j8+BefWFQw1{Mx8j=jXG5B<9h~fO%PXt}0aU$j@W7yKU@^3Rve{ z!ds~yK7=#c?tp)R80EGk-)m~{il|juAYXX>pz1K)l>hQusC1Ly)9K6t9(3G4-C$Ks zJzUF~0g5fLgC`Y0X^+|N!7YC3Ln{!k7rO%gLir#6qWKkz$RAqYgD(>-mwhG@ z25IV#jkVlpeV0`(vEZF>so=bCZ+cj~*7C-|XJL<1o@{)gtfNq+sLgPydoL%fd?ZWu z^fBpC4{;f(d7YDTpV*rvI7AO{dXp|O-qsiAG0eSq;px4H)7&o4t5Q{Fxi(9-$0wTb zMaJ8{r1~Z9uG0lovo`oKyzeRXUd^5=gZ4}YbXEHc=AjC>Jh z^)&O6r^p7c{%FHS@Wyu5vum8wZ9Y+9yeRsKBh>L)`NcJt`Zvn49gf@dd0M065% z4EFRX*3oo(7Q{Pig2Y*p`9-77j%Y766^t)wjd?kGlPe-X7&XiE%JS!==xN#*(eHW} zcaAQ|yVr4KdDdHZEnb+!4aP1#!iX@v3ew82dwhn~Y1SGep*yEo;^V2IZm}k@96%|% zM+q}`>RqP(?E6kv}-x;oN`A@KE?;yqoQYNG#h?@q?T9*Ww>_OjoYTmQ8WmG}EN zbv%m?YfuLS4{t4;FnAuW4Tc-I#`8UTB+jFFQW#Azg@JEh5+Lo8it*n_>!@jE(!1g% z1l4XFnw~WG1TKr#U0(S;nx_9qoGXqlM)}yOOvfx);k=EfT27*hl++INv8T~!ao1V~ z`NFF=w2mY<&1wc6K)OF;Ng3xwZSN2YWZ|TQz2Lp=Dl7wPPZdG@B#1|Sb*DL_uRixF z6t~8FdT8=AtDn{^GneJz8PU^9hGLP*cgG^Ke&4Tgp)klFGF9+ne2*I~71iZAiM&dc zO~wc5c`*U@bs*#Ae(?9&!Kr-nLmGqlnYVYQq0Sb}WbgIa~KP zQP+JBCMH_S$E4MPXv`waOXNGlJHZG3LZPQMULon%db5?o-d69m%l4c^lFP-Z=%mIA|ag@=wkTjZ9{ zw-GDm^YV$t?13{%xd>v~L!vTiYmR`X z`MF9YwW2XmLD*H&Hc3eT?53XYYmrL$-fDxJo6VLtJn7+@DhuY?^tQM16mu3Ey$uvLhi<|VSt7Uze!a^&&`mH6ch(RZrvm>d z$vW3RMzaUN4Rjl3O$`wP!3#08;WhybYNn40DT*ToE`_^kymbs-8miCI9bcr zeiny6`mNvHPsc{J61{M_u1dbLGEMRs8Hnf|(N25>eN9k z35Y$0`qehn8gEeE9dVupjs%?R~gyE zm`6$^b-_@$)}X02)Mg6$1e(6XuUF+DmJ>#O{n*X+{v{oaUsg}lTb?W5bbH*y^V7FU zL9=?9`|Xb?u%>RHvV5srsAJ=Sbsq#z<+4sU2?o$vPM2yCbwpbKx&=)2E! z$C@(j#B8C4N!gTX`;LyyCJiiCtZ62qMNGIp^I&$`c})0fmgBN~hYZ^*YpKmA8aHt7 zXbqzjZk%x%oS6OEU7`_I@k<>1uL4C0H`EJqE-Sp9pQVy_&-oWsP~druy*xghn$s(| zzhF^@pnP3@xo827ktH@Hq!ExLaOeGYovaQR=?AsR;x|Iz zs{u0=U2UWp-@P7LG={Y9@T=3DhIP~r{^j;omQtX%VO^;M}(oaE?;2_t#+}kH(;G4AVz$;_r3o&5*mU?M1^_cXA znV>jN?$ASp&f=jf5O4_$no3xhp%-Il!P%!?a^Do!jR;KlKbLRQd#7%(IyjR(O&}4v z9L*!Usb6>l!FWs>r@*sQ>cUy<&Gq&nui^X9O#L#XJ(HkQNq}y@8r`I&{c9~t z>NoL0ao`LzhN1%LJvQWjwaV8AHO-4xPJD|=ymUH3T6h7lh@Yq;&&l=|qMoGGpHtS~ z`@Sbrsd#_w2zU~)5Mc9Yz9S%&Pu?gBy*aarT}jI$IRtlY&4siQdr%Lh&fm%}fp5Jq zKr8t%2E6)`=EvG{Pp66=yaaP@TAg*}T<=*~r;hTIDP{iZy0NX%7KmKrxroJUOs^nF z3o{E~@Dc58=fwptJZM>Jb}^MmgPj#TnWC_*5i^9OfGwW$Way(-l~_-4?lHJIajpk& zPAP+OjpHoUzy%A89?wbj-hYAF6U@2V_-oJE`yN%>XL%Ba6Uy?(^#n^-*iOk;*qiM| z=BS*$5wz5eg4vTIoq&yxqV{%(d9tWY(q`XXyKZd1x~+*}SKM=Mi+rm4 zVIBw`0BPNQ*{WW=Y*to@ek?mlO`$PI(v^_@)NBSkj(1|({oijLlluHz5YL3(YGWom zT#cR35Jc@76Psn>d+r1+?C&@`PCLEY@vf;>;%~%8Rq;qMiE6|0^ZmNkOGoAH5=1sb z?d;jQZbcYWtKBk5Jr_yKSb9v5uCr;C^&9u<+0;G@4@%Bgb;k1n*3h=3@X3#c+?pVu zL5~uAw+%2BHw>lGFo7j4!LiD}3=BFIfzqnuo4%I)MPiw`?pSZJkwBAx{g7bEP4@2{ z9TA-Y**_Apiv4R`ZnD+c7^}p$vJ@fkFE|F6|D5!~ZI1idi^n@^<-yIdEUi{bcWlnl z*A^77#@aNvpkXk~uS%ny-OrzWbD_I((U4i8#v|QEt+$=7-4dDZxSaoYi-0J@ji`y- z;lJ-%89$($c3EK`(;jc}|Nry+4r+BGhjgYv9~s-4Tobh0N?Kvg!fSIiZw027poyBPJSYaX=vhwXqt3}GBlpQ^SgL`n&e1ID+^cBCbbalu- zKbUy4jQzvw=r4Dg-+ZTiArr;r(wJo*JbC`bPUUH|G9jMIt{6W?N zco*Bg8oLzUf?4u^km-e7%8L$a9&rjB-PZ#;IzihY2j_UaGxCOPZV>h21HP%<^wC2H zbKHiuYG#@`;G1oz#?Sz;QTQm5M&NFO^3Ger(Py{(>w!L~J2WZn;oiLk(~yDFTfbA@ zp>6cLuXH>-pyTl47>;x^aF--m6yJXrPEMOB#NOGD4gc5NZE{u*$Eb9FdZT;=iMT3_&{=G9)v}p$-6PsyBsgr8fubc`W zvIgGWOr5-iOXWC<>kP2qh#jAhE>02LyDhkS0k(&oB(A|7+#ZMU{^Zh0n%r)sa8ey# z@rQxZ5q2X1?E;ORJ1-!LhG;;9i7f>`Vi?2xssg9k^WA}`_?k8E4P3@Tj31pwbD-=L z1_2B9e=+*bG4SD(>XqK`%vaX}#dK}cobFLHE(<3q14j9~@>IHWL?zQF{|#Ws6-$@s zkJE)b!JVzre#eep(B$n3uh{fHE9vP*9iW8~F=772(n^>1`vR=CYKWPnR}F8tW!RMf z5UUs<)&Q}A-@n>Bt()6wv@2UjpjajIWG`j^JL9N_!sl2cU88@LZV0tW*a1nJMi|50 z+^G3goc0)jO(uHaNl~lYyVt_&JDG#9aJ(0KuMUnXCGXGgfHUk+UmonidhNjuaOnav ze?BAkh%8bu+W$JSSL(9J>+(fZ-p5ii}$S8k2k za=a4~qwYeX=l77AluSBEY}wTmJuAV&9E zCN`%LLDQ?m>5Y-ETg&=SdXDqWJhr2Ee9^2FeFTs`gR!&H4fAtb0-SUBptqPu9B;}G z+C+KMJx@g_%a4~zHX#Yh^YCcMqB8VtU5F01BAAz825oqTn;uis3fqmVQnf~4_i#bEf~djiGYbBXVm*~Xx4kx#qqZ9`|tJD0BysbhuptL-~y z$7dN}CdTn2?2*P-0(IF9;c<)XfBD`*R#ZIcviIf#KtzGzAa>cmf%JoE4&i@Q@-*&8 z(skygJT{}F$kvg0+rgB_;txRJ$XSB^Ul+zZg5}~^ktA^yMj8>^Bg(?(=!iE}`~!%K zh&>7H8W>=)w}ZlyWcAzS(r)3K40gg!hA*h7cBCt+23MS1W)8FpqV-6DW)X#JP=j?4MV@E$osN=)u==<--RmNEYROh z!=x^Y-F-UX=;|5`Y%*SVj+J9IZY`J1}*}-GI2>w*f zhT*A{0h39-hB_77c^;=bv6s#dwiw)97mUZhpIB?!7-^+J?U3-OU%pu+F6^rxpK6uA zKIZn=TjE78HjzwW@VzCJ)Go6Uhke5BGO5zhXes*058p*E>j}s?PM_;wG}Pcua+vyw zujy^6@z9Cwvo$D`?FV7`{|Dmn z6mu6Fzun6VYP@!HmBFt@7N)80bi?&rX)MP?9!KQMMXD)vp+f(2mUSf=%=;w&7w5XX zbjdhZXn{B>>tdCLALPk0s%!AxcOroo#`=EBU9F>$zU?({qm2pSKtGkr=XI?rYqpCi z#nMEl;IvI+ViSTG1MD!HZM`VvZPJt&Q4(uULcg0>?0ZGS(TRQOofh2uDpj68AnC#6 z@k%%sh63obxA;fnLZ_qBE`|Mjk0f6zY^t`YBojE~1y<8_)aV`1LnZ^Yy4#P z`r0nOdei%+u?y$xNe1s7pYSET>66d#6KS;XFQF0~rsva`e|6EiZ_I^|wqyt3g5qCQ z&|RS@O$uE3*W2HlmT^?GW~VC;KiA5lU(k?CDNC%V5v5(Y=gR920})@j8CeBD%j(@* ziAsr3fP#4c^z}2}hsCo6lLQidCqd*-Tl*Vw#MCOJdD1G$j+po?gl*q}btbd6Je356 zp38;%k^+3}A;8Lie9D)Zp)R5x%uBp8A>9lB_h2be0smYc5MEw1pZzxv^~zGf5Qwi> zI@Aj3JZT`b@{GBVh`|oM`7;W2haB!P&9x&PjARbd zMB976!DRvQ{Oz4sFMkY89)Cgn=j0=;Z^Xx!OYYU8emNkqVI+KU^n`}Mc2vx& zoMm*>w_j&fG&a!n>je15Nd4Xn+Jaa&`|QWE*0uG^JF>$}}#O4MMhFai!N8k&4sNOel?A8!Q%28bYP^bNIciXhg2f%7^{ z>GA1h(rXXopWr{Ib2(1QWX~-tyy4diPH?o378HbkX7!zSYPs_8Q77AddoS@doobN} zLW&R_Nfns)mb54*zCHe%Scf1mp!bpx8&m-GrmhjQN%L*11Y_dsy?fKx8PakWenXXn zLD>Z1&4F+rOe2W=1TiH_nUyY;>zRt}Q=HM4H=e$~XXtEy!{VIH+FD&rum4$wzF`_! z1B!V29XF(GI16&irl0A&T*LZLMu!64$F83zHpf6L&_&d**C)MR8-p*c5;m+ihS=A9 zaU`5EkqL`+$IziJnpCb&zFuV}(b=kIjvi>$OV7bS^4_G}bQoW$2D%#q>F&x;CWS3-4lwBA?=!IN(DcwNNhVLYDH(BG#2~uY4SiUYOi&Axm#r&mf(@ zqVFD>`t)h8bNtCQ|uL-$A*>%X@0`X5NJecl_F?V6xtKO&}Ajd&zVD_%5b*qVAz zh$86WTsOjckHvU2BZqfDYzu@x)!e~5uwCJ?!`Gvex}|?KrB9}3w8);iAnm%o8b|t! zCP~%@uI>~Nj_yb|!1ag`LBFHEx#z!(#cs9B41X2GO2aoYh?vN^$aP1 zsnis!y5W_0=@d`Y6n?%?f$F!K2#O|qz;2a6Ef8&HVNBNL41lm!f}}m|D;e45JmI|F|_EyMQ06GKe5M zrSAz1MFFcs_V^wB9Pl4wq5H!zK^Wr z*a0Lq+JpnC10~EugHU^P>$5i$mnesIbZi=koqhC$pR2f+=@jYqlLz;IT?nQcROWhn z2@7qM-neA?582?UH(cGD_mX-J9c#F-#)r#~5S$8AeKy~MIajCUk?MA6QXN)r;H$%t zC7NrUmP&u{P?GbWaBM$HPs4y10~qU9#SR_mCt7e9PLLsJ{y3!)7RptKKjf|~QeT4Y zj3V*?GFt;1xQgFs{S}dX^tZ&FQ|F1?8}i`@&2L{t=%m$~sHy&JQWUgF&mB#vWepC; zR|k}R=0f3u9vA0*g$3`*Gzo}qIE+84 z;y>i_Xo9H^wor@sRCg9iD{zK**R(`}nOh!+qN@Qqf4!M-Al5{iy&YR8pPu|_s@9Xhc4cN@jg(nWF-4=dE~^xcNg3S4JY}^{YR1mzW|jiq1}W#X7f$n zNW$M*-bA56-Pyf2+eXtL6A<|??%EfAjN2heA)4yZ+I)g9N9a9Ul3WZ`jzkNFV3_or zuGBl_muTumuW)M0b$_-t>>6=0RA$@Lkq_P5-94IGsckIp+m55VT8|2*2^yH~_!7w*x&@IP^rJjSj;TAsTgb^v$-Y^zz|--$p;_2Ei}; ziwmTx6OEyD$|zz!N$B4CQcIO~t6q7QP!EIoT)O`#bN%7dRg-g);kRtoQ`n5;QWR>| zFqt9b6wm>XxcuuyoSP=9U3&!C2QM8g3T5q%OUwchhkLo~yF2=o+Vi;(jiD>U_qAk>z8WQ$nb z^9(&aR(a{N!$Gw%DWOeWh-rK@D*}xh!=EQ5&k4UIIuHqn^_e+h6Z}F@fFN-Xfpb9+ z!%!ICbpvIp5=o73iupHkL%G;XQsg$QwB4gb3KZqrF5R#&vRPY4Rsu?i$T2!xE2L|l zG!`1lApU-qR-)BV`D8Ql&S`yK7rk4_p~$w~W6lkcbaP15E*wf6>yeX{JBFnQgQz_q z&y<48{3C(W%(8M5$^M_XCOfu;uw=EL-Li$O%!+CazyjFV>VITFI~|r1_MIzh{nOK( zr_p~Sn@=G>nwjge-d8}`1G4c$Uyg3@(uLUH#c#%3&gki2Qx=^8kpKKj)>)h72X-Q0>=-oGH+*lDev5tfuB`opOJnI}R+WO*kSVAI- z&*e9_DECQSn)CpUz`%*ZvefP=r+vh+-?2u*zea!W#D6_0s!&?VHFHnIPjb5M0<``e z$zEdCAZIBErCvn%c*ETAqGPh)uw8*Wjg>=@%lrMv1t_tee=9L~*VVeL$2Psdth7JNg(*p_kSStKx%eLU|o_EB5%{R?IQAwg{fI1j7oh6EE#MBi<;Hj z+jw?L>JKi=POP6O02yPB57cg51-q)oVr56>>`Mgu`Tm-ex1P!mS~+}EN#~j?ICcJK z@7a-R6_(d3W6Hs|(!*+ArPlPlx+e~CG|WhLa!M%rRneoV7iVKP#Z>{k*l}br&&RJj zng9F>8>r~)!mGfS+5`#G=;aQ$i3~yYm+_>-moCdwHD|jf)rq`^cGseS$(4qeB_;9w zK4r;Xb6$Qzacf)o({O;j-$BDm6!WRNY3K>s)Xu2|Q))4s@EBpRIg~--{R*vk($4@- zMvp)xy1%RL@j|lm@2aOQ|M<~C6)mvB3t-h`Yh{=#YsAN`mVPhW-(HtSi@y)eUsuDc-cc2L4fHDQJ_ciAu z|JpayXLjbQ$Pvj~wLTD#EP;Sz#o8qr5UnA|u1ML@kEUM#-@tMXuM2D5JyXL+|1IjL zZN#(Q+`ysgbH_v`kyO2PA_{VoQ~tz@w$+W3V)r@7@%v|fJ#P_7894=&s1MT-TftwR zY*8d-8W&SGi)o9mbzs4xt!elfUm`F37`l!Ly)i*(LIic8Hs)q3C|us4>I`L8EfJ-H zL=1d;hM5qC7DpGJsJCVq%gA+&K?=7^e7BX_d8Fs9pH_HD{}|$U z=R98T$>)3H%535I?;i0=gU_0knDHMcb%uhImFG~49dxJ$6&}gf-8hL2mQH%Y#b4T; zZ^T%2&8%E@M#c)6c5D<|6g&@`{ zG6dBh448PZL!yNB>ryHma{6fSKZ6loibu&0%DOM`1pIOTV=h|!mR+;@n}{s0HyHv3 z!XLIhgU>(I5fsI(PxXPiEd2j~{c}|_Y~n3LKV}R}f*1uY?Oy85)C^h+C;CkU+pogH zqi$(-O#r^-+WYLyF{!>GpCQY73?q7U4VxI(&bdI5$aI|6F-fEDpri(O7VZ|P8kp$? zHd9N^w&yGBOe2j!#pION)>()+!Ue?~eP6f}ammop-cv4NI9z#^(ll=i}RD6(q7s*9&O37<5*^7&SKX4Gm% z+#sign(O(-h|5pkt_Ym))SD1Ib?oCNF`aR$^tyNjZ<{6es`V4rya(>%1&!P_@V$!m z-yRzX<3V7Jm_=lJa~P z>i;qg^))6zB1%sBYR1AMIwt@+Y+JI?WS(JpJnbWLUm#MO+|IG90fHcMj#^>7LzcX!c=x-Qpe6XzX43 zC2EInU(1_4{M^m8Tb-!+i)zhYPTyWTj3O!9xVRVSjK|Gw8LK+a`Cpx8!6lvl{n%@m4W$yHbbBG4v3&@3jx2=?>{Gzxy}nInVpN-}|qZa><^( z_gZ`HweMlB_{oqvreG|9E(SUhPZM$2CgV}^m4py71=Fb=d#-3$V`>{cD_!^Hfkz&$ zL`8!L&eOyNFI2;pf|2JEm3srd6z+M|tA-hX0rP%y7<50co4I%GhS3>Rn4j`u{o2et zeGRWT`jy?p^X+N1Im^pAzINr%r0aCI%>u6MC{8pkZp7Z*08cLS6q7631=OD8g{_{* z0tA@EGutv_q_VH+wU}6WvcWGMl485lL>n%1O8!&}ca(6^-;-mTWs!gFiPIT@y#)P_ z%N@!&-x=>7kbt2#x}ioHENn|rkAdxEnz2&EQZMrFJG7vaU@JkY-+?!mzE*u>pB;nxO%0{m3D02iuoMm_uGuR? z>;k@@##pDX%@~iCC2xMji7B8nB!Hs7y1=hI!ATi4R@8%`_NAbzIi;=TY{u*Oq~UbI zXpg7?h!O5p^gnm#ybdD0j+{LYm=Y&5f+J2VukfBdJ1_m!{pS;Hp$fC=kshcFLhy zYs))AnU*&L5s7c9v{g?&4BA)(nKju1uyq=svt%AZ*8qVec0c8Rs6+-5K5iouKsEPv z*cUlYyX9=Qu%8j1_K(`2M&iph;8+X7pOw`A_*q>0d_aju>{<^pNVX!f(aZqV1Q3?~ zR8b`I6w%M}KR<(OyxuZlm!4|JC1<@m-c7J=u~#{DTln=2EaDQ+*ZI$1{h9>tEy#PG z_cTw+dEy;XWH!~@XTm)trw4!Mh+}RMFX6lr<5Y{bVGnj%u-?$$NVM0^apUJ4U(lVW z2{FV;&O}P*jjJ00=rWL2+svU@$x{dB-HihpvhQ`1{WC2t-*iQ2A4^Sqy3A3FE7@qw zunqe)uuL6vIpCov$F4*)Of2z9BE0B~j=Qk|t)dGXs~2pSaXHR|2N^E?LN{>dvpz~0<~J_u45K`;U+{>KRL)vER- z#T9ExNS;_D25buaEN_QYpT+L7y(ys?{U0qz!sXG9idd_|^(jf_D9I=9IQax|rRPIM z!X%W1xrAhg_m-tZ>3;c5?i_d?HDw;u9c$p6PRShC<-Id!J{)E_Dm}rDful=pd+s6h~%g z5IeXS3HnYr#})EYWpQ3)iT?S6B)?j%wEGhf`1ml3)~QI=-I!$eKm)=O;z; z1rMB*IvnBIT1s`(Z__x348zIZ2c7*`Ao5}$^6cBb(Vz&zi#RL269x(nd{cQz#+!t! z_g&g+jf%H$@+u`qr=q1kD`E8L@wHwPK0SvQ?A8xZ4!&{nRVn$}U8laWN<)@9j`HZ& zL#B4M-Dsfe1te$OH}+i!Qj67h&I;F6qX(MZSp}=ET2z=O3E#>fBgoG&{pSVn(eSR zFyZzfvmqq`YD}A#B_ID5ZBpc24P}O^@ecZ)-#d9=wR@UYck-N7ve8(G4Ptu3QVB_)3nAJhJtA=cQR0pkOy?G7( zZF}v3nT*a%#LvgUbRRc1`@aL&oVmx#900TCI?z?hn-S{1hiKh z^ybDazQ!*zmDYzMsB1kUoq#`<0&3yA;i81dZLqiHf?a$ZgWHvGFW^B2{0?6b}%rYc(Ugj!A zpS;4_9XR+xG|ZpR_97s9LMa_hD};^T>zY=6(&E)~!49LBJ{F|ZJrWFX*zxU|G~@!i z{spk>#|KraW&&{tXugSU7ubC98PGs0@lr!Wjtkoo+Sez|oH<8?(P&DUPNj_ux~s2A zu><*(LznY@zQ|&*`OO9up-Q+6Cx6TV6qv7{#Sh9SE)*E};%vq%LFRk1g~2^A(SjXE zd9X53{w?xm<{4pmyN+K<OD{7;+4l(H*zG1*;Pq;L#k2-la4A7%{|u7I7ayQ5+hXvf6Neg?zz9OypbE<&=aB_ zsmJqFS6=(;Oa^g7gY#dmHqZBV-}zF#(dBUTJr&*cOlK+QI#|_n7e~=-wC`v~9`Lq= zln}rMi#upOolZA&DkRvYbp}aiK4FTTe|ioH=+=Rztb;BZU(OW2jS#79l*DT~)UN}o zeQaw}_@?I~ud<+i_$I1;Ekdwq)F0L5ve$`r9|858DLnEF8aIhvJQ{lODr^J6lVZmS zTIm6F`tX7^AQd0|&mh8nUA*7eKB7!$+VQ1SwJ@q*bU1Cf7m z)gL}{8ZHP{8Md;h!XNd71tpu61!>r!M@R903& zxmmbgyz0-NoGgs{N~h1-?7(}sTzwns3RCXE5-C~ccmJ*_ zw`x|nJeqs7*)K+DqSR7)KRSqeUNjo^U&XJSvs3?kpJEwWv7y2o3NhEdiA>0lN@Y@||0>yk&=>H2NaME2<$vb)=p)QUhkA8U%!A*t3+kG9hC8<<-Jrlc;Vcw91jH6>YlU46&F?^*|6IU^~`$ZiPctN-ez+-^?QtNJv>cLa>lB9ufp!USU7Zx zC391-QcPkDt~W;5*fG{|L`EIPyS+a+M2Mt@eL6L}Z?jkCOq8h-k%H-OruOaYVT+8M z@H69LTlE!ZOzAmy*hvyM#}3EuF+HU3pFARnd*==r$Du31u1iQ8hn8WCqMP!7P800(o z%4Mtz3j8~wQG)0x@YA{af7g98=I6A`CeML6W)=B5_%_#SH;q+kovaI498;iaYWPFL zR5P45A|9WEs}}o|U&FD%L9)m(%cVKd{0huPOrJQ?gdW0HGnTD8i+N8yjm20sn(v0j z8|5VwHT-aojq}htf8W<#KgL7IZ8_2?nZeDmb#n6H%|XFvu(Tn*qLCmHCI%Zgu?=k1 zUT>*C;GXDRnBi%l8~hI7znMdeRYtLb=Os6AN2kAj)%k!k!2V$a(I(5H+t-e#V#zX3 ztb_bgr@yK=Og!$bzvj^~X~^rh>-zKeXpyQ277I}E`}Bb(3XJGW>=AWE-)To3v6QsB zGn0Uv0hsDpmiud7jK&ocs>v+_!TATk{xr|>Nfa6QGNDbU(o!% zCb&CTv4$yak3T__YTA@8{9#SNK*Rw1 zC!7hLtg*+9ZB0G7Rx^ScGH<;lI~=4TKFi-6qv!LLo6U1RIxX6~@oq}x*Q|tM5Y4~h zq%_apcXVe|Kcd#1c^l#)No9BHQ809(Wu=(OgOkHcJK>y$w^Zls-}=d|)FOPN_`c-0 zJJ0&|C%(G$Ex<&_gr-?=t%lq;@zM(T#M@aEvMKsh&v+Vx(l&m#QngQwG?F;W$f^_% zrQ?zYxWG`ga^3lVqEZq_Qk5z(#K+$%(%V&zP5r-?G`jxr2sRx_0z+->lBC@rqNz>i zQPZy0)&zP|Bc`6q3{&cRKS+NNA z1B^`-F~ct6)85be`i*5Q?5?n7*O=LL@`*j4THV-Y1NP9!m$&cD3(Jcr>ol@&PGI$( zV^xUSG8$49xE#w}nj`fTE*oOPd8n-WP&G7mPyR{C^ue`?U}Cw_?Yf&QSRq1WQp z*7A#&NV?(gQWFiAI6f&$b{ZQ>{|xflN0iLry!dvhZ;C}_@$Admts&t46T;5>vdWtx znQxok1Bt;EzJ)u&(VyjI1t4zRXW8oL?#}l^-Cd$PhL@W?nP`kzv?|sj}wB zPoUsQ5A5Ojz}V~*b%nXEq!OvVmZ=Kl?kM|_Y}5Gs@5)-)mzrKRE>zt@++H>9D?;vX zL)iOMl6kLvW&q*4`lesCnAF3S1z$1X)5%>Bzg7GYquQ!k(anrycM!ZU|CV@_u(|tD zi;U-9+`yH-d+3DhFYB;p=P(}TvBibSiEose*+kc`Zj>a;)&BHpPi^$5_9~{bGzL~+ zw9=CCat!G0YKubUjt^C+N*UFFdUN2nQ-f9vTwZ`d^>jueBP^djbOwnB*-KuU=l(<} zNDVathM(QXy!qmfI#0MfMr+5kp818~3@j6ek3`MZZGm>2SpIJwHfZi#DZ;~Z81F8A z4bX@2%5U_E)%)A(OL#V-iGA}N%Q2FMheTVlR@$e}q2*e~^dh#Q7wdL+|2508B9#;X zAKZB1+urki?&R9T#p8Y>PJ4-|L7b>Jr}MA0OFy>|7dIAVA6-+fQH49eG)=soN8;b} z&*_w#-ejC{cjaYZ*;O&ZEn2k~(%+qieRW6zJ)E`rL}fjKC_e=rDCvSseAwO%~F^q<=K13SNW- zjE~s;PF-}CzQs=rIUWcGoGT)yfCX6f&>iy2lOlF=wk};U(!4aJ=J>n3sjP)%k6%M8 zSO8;~LoOt#xBhqxSfmUTd`@hC=?8syM-}cM{W@8KUJvWrv!XeN3^bzOu_d9NA9T!p z44)3afrN2xc$-Mu1nJ$ZBCX}C#p1!-l*aCbW21|ku0Y8Fo3$Urbow&$MNBoL1udh< z)6H8rsdUeKM4pI@f0GW$_psk2mbbdsARRPb>4CuDx^4BMK3vl?hHjkq=BvKjZR82~ zO3{AJ(`U^`yXRsGl8Nh?eY3M%J2kZ9)f4Mm=TOI-k-KgsaK5P#rADJmC(7IV3Ig;% zsSGYLOe?4AYL?d#ii>rlYXaz{(n4)?pW}-3703z&JFp>Ry$GdjZEsTrnjuA@{G2eICh{S=Otvhd&wv@|Vf&`Cvu4uYu~fDUYSmagZaVWD+giTAxJ58RYGGlR{g zW6@hVbkr^+GOQ17Y%=;J$kLK#WW-Q(ll!yXp_%v5M2rwHQB?7) zZyp)wB^F$6Q!-O16ETUC>5N#1-A#-ACeX|u%^9E|Mll~Wa58^+%b?bX2T@C7_~Q$3 zD^kIlIOZj`Mh!L6pm#NR&PK-V~D2Tb$e%?;JlI4fwHzcU0rs{ZF|$6&NH zT_CxW;Ozudk!+4Db*)1DfnxYE4@$qwod8h;Q33P6j%pLNNxUd@2j8g}M5&Ki111$7 z7o%aW?16&g9IAs8?*$)ZYs z5QYehu4DUblZdXHruGcg#1BLaU9}}ZqQcGkeZm+G3jpZ&T$;OdsJC}}`*{{ev_P#^ z(PhGU)n;Z5nt5M6=_y*SbK(rm*BzgZvaGr<>EJ%~YAtGKo@`j4n?XLgt7xDcjP>xx z=VdE65-{}FQHR}9e3D(4G(-vyK5SVVK>bnJ06c`>BDgxzo&x(NtzinLo(wYDW|w!9 zmwr6a^4pSLxU1eQd5PpAnEg;N4ZZBo?sF8cV*`;X0Q9dEdNk^wsmuP`L?O5Wy!1ae z1R9+>+rW4~%qea}y+sK*8j%Oyg8T2S>0Z?m1TfgV1+)iJl<0-O2iNk*8}fqebJJUt z{vlIXvAW?Py-st7{5{);&$_+K?f|ymmy$Ml<}R@4!bp~mi5qY*9C`8MoMrjiIz*D* z)M}4&hEf@lZHNr4iOu@rEs0;M<)Ejpd2OwI6yhF=`xwho8S!^_mTtx)>baYd3v>qN zLgukLar5Oe4Q6;7S{&)6KlCHqs4JmSWCO7igivKJb#& zcg*a^Rv_)yK{WOL&o($Fjz;LQJ!kYni$ISI;K%}cG8{v7PSFp{v8c$~pUp*?+KmjX z;bo(l&S&oNN-Jeez;rM+#0J(Rpm^zDsx4qLkS%HHCv4V}ow^i~7M-Aj+twb^%&e0< z@25xHzVdTUvXQRx=)>1}gxILXqOWdWuwI>N#8Ra}mwk{WspUJiD?<22pM5kooI$vb zJgcVkQY!ZxOx-?poK$UHdYjFZ|F}?K0#I%>9utRfg+O@)G>ABV9Wh2m*2Ki#{wIdc zy%|sp4Q2dxYnc=wYgEfwYAcvN%5wi{e9y*5%S*0;E1PqFXo;(8y1_BG0|ilOMvG6a zEQ}3EYbg&O?O<+YfK}{hGcf_+IvtONz>lC+amesn{G+RBsq~LVgM2RPD`^;pY-eiJ z?Agy+f0wS97tW&hb(kDk$|^??y%y)h8eY?UsGKJ?)1-02%433YQA5(iR<}NjeMa3a z@5X}Ft4kT4wwN`qZQ(~D$lsY)8?V~*WSdQgW?q1+@^VQ{?1AFMyPuz@*gdqWxB7DI zCW7Nr{PYK{KC<+u=PUcq%WFpAYI3^whN5y0s%U0h=FS&9q6<<{Zr>21Ao&!638A;K;M3KQ2ng6Vd)pMN{w1HsSw zPX8M(*fqeW+qL>N*1`L8!sW=r3oX)LlT2UvBKO9ILj$pI0W&`2Bp zd(J;=Uo~U#gdC&~uE~_mVESmFxCW%oHnHrmPkUUG5Rt|j*CO#XYWQ!6s#9J+>MjZ_ ze;Bsq=)5C?QOJ6tbw+WRt)nrzc|^nI$WeJ}Rl(YynXjBT|K3}0D|@%{20U`2-z?aC z&yzCa!;Vsh^+Mz9ibL+58{r2wvqaT_k`Joj6y%`or4=x13!bjr$Yzm7lbKy1>U};P zJKP?vsi}N41{+mus7&sOprRZ~v=00&wv$Zis=Z>XK?Z#t0F+LR9$ zV4i(?Ee9xNiuJ$DnQyIDyn(7P)z9255bTyg1ic-$Rt87F=pw^s)DHx5xS%xqFwQ57 zHfKpHH8Bb>^M&T))V_Rh`+J@Q9P+#9I1V95GI$kk9kY3M3#qY4J-)ZzCh*kJ2@)Rx z9W;&eP9QB5%jTtcoNN{Trxg;8}M}kLJ08JLn52*9Tyqf);V@BqhK0d z(Xqog*7z{YNVdME|MDY3P{gLV+2NZbbk~9R%wIL4`|d5XN^8ZD)o52Efqr=5+lk;W za3aTC)Z>=wc0nlOdC;a0K;1=Jds>BmR*bjhO<2VTz%O$~m~Cg0?vdD2?<2&Z5c$PzsVC%a_O3 zwBz?-sxkUTFq3`2t_`5oy*%Z;DQ%07$NIe9>?A5_mOL!}z#StP* z=N={PDlI_AVM$9|z`;zY4h-_>iBIzvk}$aFiPA6jnL$rn5RUi07gvGla3l7@tsJ)s3p);6Pv7BtlEWYs7~}2|2vGy(ep>ph6a!Ogq9}Fbi@E>wMovNVZJN` zl(W&Fa=y;BT=GohAym$cf+=+tl2gN#M7zP~Y&dgx{uQ4!7Ia1iVmrC~KM#~WaEIrW zJt%xqSz=z*f3>7<|Aq8+&3fO*uN_XV`*)MYJ6z()};5xwBMe<|w--5Rj! z>w#;hF4+BfK7l*wgy<-B=K4N{ML$fM2I!wq%6@90)BCC zlY3&U=#iuYJX0d@UNZBdE!M01_k*Oo%qEC>NFtX1VawizBE_9*r$#3!e)Cp<8QU9j zoW#zP0gL_IEpH7J&j9OjEN7OotY`z&7^wyDd+`%?&-er9E3yQ`<(F3#PDx!i^yJ*9>+U#cR8xo5Tc0=1J`X6k@`g{dPq-c* z9Y$1p2h%wz80%a)(`I&t=&5Xj3>jL0EklX2OJugwj-m82eD2abcV#)Wxw$B^0mbgMH#Y%@9etxqdMP;xvPVey^3IhPIMG=Q zagVuPwa1xx;m5g6%}9GC#yg&$YYdt)iM)&_`JEV@XK4&NeG{=WyS(vCl5t3lNy0k*`qcV!w(d1?g@4Fb-5GK{I zGT)Q`9L|kr3tu8?(_Wxu743LigFou$x8T~H9T_&%=+-)a_W>Y=4UKmRP#@{;p(Dr? zVP46#CHgU0K30S}Dm-o||O8Fc5q107XNa<1(bh%#+2TGUwM>LqUEqvd`0#2f- z$~mDz`jnet@PIlaQlkwBkRKTrsL~oe+R7ju)@XCn01(3RqZ4ux?XE-$Za)2=P|{LK zQBBn!^#$|)4xWYG6Xxui44`4ZiKuc@v^_G3VruW;z0EcKm~dP-l*&(WB38LVBm&P} z7R49hG80SsfR#lOc0@Bz4+Ss4Hbh=HrI^a`#jftc3O7>#?8EgzMs>Z_^1t{^zde5% zg_*Ny3%jT8KcN76U!N2kxTP>cFixP{nzgnpGdMx-YZcn_b6oxUZ_!Hn+YhHPH2%q~dPyUmLaj1w8z&`h)GPV;<_V@s>Vl5pAa7UuGgF!Rn8 z@1OLgJFj|vklwy4|2;T8t?xSl9(>=CKhOnV*jHshJqJC@o0Hw*`E_*u4pN#m`zIOd=WMtm&260un9=FR1Yjs_OhaLH-;ew!FPl-T!N zg;7`dO$NS~BK)0K^hC#VW5}})!v1Qdo8VQ9#HZrgRIcBY#L6tB%21D62-*I)rDR|# zV^ERs^2KQr6SF!k2@N5!UY|VxjW17-sA@m~Z(ZWC4u_N&FQ=0j(7^LVP;n5A4hA$D zVldao0Ez1#P@NS+)URC1OZ934Q}HuH0ohu_p2Z-rrf|*CL5BZ#G1gRiNMQAxe>CQX zo`)6u;?)>{wRYnfFbL(K#Blxx3)Dcfz^^cEJMoLQr%Cord2P;NLS1z8eQE6UP9tYk zO}^dNwk$PTqEIU-1}8`A z4WW{`%ewb`!sup!n9y)1^{k6;NQ`6Wckn{~1kVNN{nc-G=p~KjE@fvzs>-S!>N16z zn8RO`b)hZ|59`mtCT~rCM~MaQ_KBZPJZC4`xH8k_|%IhxaWiT{~`JODu6y z$+z9J+FGsu@YGLcCU1H`VVWgPqUV*I^c*0~E+q@>MQ1+-{r?%UeWx^wkp1ujU8jr|V%(;i-ndxg*O6vn#L#(D;-wHmQ z!zsLaB|SDfTdpK0KTY+JaGIZItMy3tP- zu{ymBRoD)OsDpfX? zNP#sN=cn<>Jq+0+youRaD%~5?-wDSp?y})_d6xQ`HSoh+eM(-Eg#~MrcTQep9(-Efp0F8HX`)Nfs&wnSs`xTfw!&wR9*GL7w18n{kZ?*QuH=8T%rBv}|f zl~W5{hRzhx^S}biH?BFzm+qt}QQDHn-$#et4>z_+S&4%f2|@TRGl;5NN=RnwdcGk2 z0m4=Dft|;}N#$4}J!`r>znA?>h?SHAR&oOrlSNEsF*IYDPt&medaZLqywu!K1hlu+ z=p#IMoBJfj_vyCBk9-^+78)S|sUsT~KJ7LW;Y~VbB}U4kqXuVvED`|1 zcfQWZ4mX~%hu18| zBMKBvqI%rESnP9?BjlDekd7~75sL1?t;c2opcUS-0()p{QA|AIAMPqsFrY=RxNH#Q5ja=5?y}PIA@?B90F{^obl-zF-?;cvb^Bw6mQWKUn+^t3!B z0CAg2(JL)j0?IHq_!jDh86flpqi${fZ315O$h%tFJW-lvvkGlEq|3B8B6#Z&{rJoI z;v3+E`>#-II>w?G#{(L)Zj%p7EAS;vaZ5={8BlZZHS=^Zspv73rMDc@p3nqnUW;rE zx5HQrn0T=EtVE!B(dTz_ll#SvAv$}v!W^et#`aaFSA0BwedWMK;7;N#_e;uWFNHLe z*pNjAYrHq!BYSU;Lz^S_+>+!y*Ef9?o8i$^{+>g? z-&U$=C;`Ofc<=}ZM?@XSwh6%Etg)mWhjAuwW~5m2GeZwDqTpwF0d!1JjNlg3=G~5@ z@GL%@6)sWIRrj@D{9uOWy|}SN(4Nq~0Aa2oV^>8A6%YdwYx?jr@lp(^Y30}~IL@dr zUn0i^d_T+BG8)a^~&_xshQW@#=#mmA~aL+fV;(%_N4)PN($M&VlouaRP z`rIG|beIZ*^Z2!n3*g9D{mlZefrYzrw)RKlS&_X=25TYqM|)koV3%U^^^4xG4qyD~ zPfl_q&MMk(v3*gFlIKd^VO$gT(eeCpC)SGWWPVQ@-B1?$x5*JrB}_~Duo=>=F0!{` z%;GxAEwQv$g-Rhh!s;KkoF*v@i21sJE=Q$zYxXGAec}t{hvWTdL&R z5-bwtxq%#xH-(<06WtKmXk4MR&{eycYHX%q^jP^_tfrBbL|oc#rS?q5v=vup*Xgtv z$3&JjqFb+)qr1+ZcMdl=aBcL{9&#Csof7CceKA2M)}$n&r=@nj*j5Cu^U1$q0_T#0 z^I-F}MX$CEhM!Gubg_ALfJ?8{6;z4c5?E$m-SISa{{%?Ct!Y$ysyx!%-$5(=06Y$I zGw(Z*$$XnJylf^Dm;!_y|78*JIb0N(1q6ZI4U1X z=5__WBUSWudVi+m{kzr*pSioS&k~6g_ox{m`*YE0uN6wiVX2}m1h3+HhLs)7d4Ba-dMDTX}!1+w7Vc4 z+vg}dE|c@wl<;6yEM=|`(4|Wd`k9|!!2XT*!4Rs-fVYcqak{^+a7AqK_{gsipheNW4 zzveZ4^9$B69aSpeyAyt$2~AVGcJEntR=7wrSVqHCN)>>AOC5(b^A(Q3P5m&qh$aJnS}y zwKo_wj>X4z3l^D@5;<2;KTZ2NI#ux$LeDx&~s6;Ekqc7j}ZmPkJ7had3k z?BEu?X=>|z^`Z>ixww5A>qiS@mL)>V26!VdBQZ2r8!tE_*z}WQkR3 zWhfzAdkE&m$4`EVbb^ffI>y=NcCj%)W*x#nojy6n!i1*FlLYa8Q-RkP%pIyZBGg}? z>D|%7d$4v_o!k#v8r*5-BrO(DcJa1Xu;JY2MBQL!d+IsX|`Dc{=(n}Yo?{^ZX z`6GpSh|2ZNs!>vJ_JeDGIp1&@J~MJBU50De@q5)?50QDB$)lj%96N8+Ohpv9iS3MP zY6~cEKP=d!$jn^8#Y}K#fVoNK{?MH8t`w}U7JE%RirI#U6gf$82r%Ii>#e+=zUo~Y zsh9|MEFJcOT^o59IG67^gfLwFhB?$`6xk!Qsoirz43^EtJ;Il5C?PQ{ePo4dLv=7t zandDw4G}Lg;=-6A@O!ONEMNZOWern5-`|ex1tD^18SF`1@F=U3orDcX+F?{_{4`W> z`OsWQFuqUp(!g>*X6iY;sK%qkn-XtMspUnj^#wUG5H=O(|JbX*pFVN{yH*j?e0F)(lI*q1e<%#g=Hyz+C-5N58qFc$B!+ z<|}vFI9+KWr2&>to&2qfupNT|2B%8ujXlYj)UpIzDv^%zCh;uljU6+a-_F5a)T$o3Mb4i%Y>kH6PCWr4Y+@E?9@lXOCak#9|F{8^sXh8E~uzsKjo zC~1!-=m`Pmq5n^avLCDsrYRdO>L=X3J6A3#;gT&v;Wmb%{U?QrpB*cKTNgsZ^v2m$ z$!7I;#&Om^?o#51Voc25ubPZ#g$>c0MtwJk^|4W|zm56B-seRz_2@nPPcOsx@MNaB~Qd;AD`l#neB@ z))3pbvzhiq1I%w^>8J!$De!9f!-02b?X10~0b_Hdx8<`*N@MCBqD`EB##{B_(~S4+?uadadnQ&VW0+B-sHiGCf4T+(-J1`>x70xkyZ^VVu6XCS5ATq{vCyE~qgf zj&aoAC#nwrkx4D~eL$=qb$PjZj5!Qbiv$l`IP6b!{{6!U<3o?|l_@p0b5?n9k^&ZR z>1X-hiSUY$2&XfMYfGB{C$fD6$o7D9-$FeXyA?Sq;LH|IHYjcyhv#m+xZzI@y0-G6 z!N6S006t!?V`59+aQoGU0)q(bMa^rI*hL9p%B0QZ@!U}qGiU3|`we`Zn3jx5bH1(- zlt_W?fV*WARMf@uSDWo%JqHhcpJYm{*c$cXFVJZoOtHq%-g7HMu;!oK>|&UIW0gGL zdhU??o87r$ncF|`Z=5ux7?1Q+ih2e8+WY+Hoc8$uN)Inf_(cL$Ac+r8N|^rnVW zXOnBrL#1f&?SjwNmzf)JQzcYcD{}7VdsSzfz{B}{mIU55T@O=7kM*M zXiqAr!Bae7Zo5+NjR9;0lMfeN5PcV-fzFLu`EuKrm(Ypy*$(|wflnkvmZERpQ#=XR zEa@kz%SKEo)KB1N5~aAKr;}UrKYf-!&JeByt9nXCZ(urgUz+td<fU zouh6sxZQ=fr+*8f(@FKxyLXd$jMi)Q{z2DGs?neoazDQ_PK#yE0M7lfgo)Ykbg%Oi+olaE5`RME9X=K~ z)5|uILzox4at2um_KK-u%2vJZLH|bJVuAZ+2~_Ym@Xn}A2793+`<*Q`ed-v`>eZRUns8>6xgD{p_sdu4#GciodE5KkIbO$>V+Zmd8uF>5D(8;W`XS1!T*)^9ZPuYB}7It?T# zXZ?IbI`%168saD{N23rPAf%Kg2kd^B40Fr~T69mCbbx=a_4MAI`HOuw7P33q+&rMd zV099Xhh%^}?FNKKXg=HVI?;2K5m*To`Eh^D`tM4suV?Cy`GN)Z#NOPv>5=xM2AH{u zADGHx>0A}I8iU;HNTsO+k8TS#%h{jJ-`)=XHt_RS(-$#(_xjP?wYac$(&oWr)%~5OJY0*CNnfu-cR7(M8XLQeve8~o1mz=pOPMGc)7@_muL~(3En8Vd z#qQoqw13nc`=$Cm$Q@4F`cIy9=I2dJjSAuMcV6W2n{u$*Bkvd6@x3auimZ1e_E`?fzk|M0 z6CnoKBj6;G^bfOk*sqZG@QDT}ud~D)7TqVXRrGr?1I1$${GoirJXl{{y)kG<-tI=N&!EY^YKXSmD zc6{kEj^{P-#ka#UO0o~4KK6cDiSAO72Lh3EBtTUA48!Q9Zaowls$=AIVlNoO&a=Pu z;2;T_GNfD*u8R%JQM$!IztJCDuPR{yjmGuEQgPX@{MmCX9(1nb6FIBz#fp<=>@cyB`Arkjq>mG}|m^N5xEM%a4R8HwgidNU9{O7Szjvrh5MYoZ^{y#*e(fbb`vX>1fKgnA=NJ7lh;i*b3r3C>kTmvoAKwM7Nq)bITY#JiWC`;{B}^@O`kgTwO(#1Kk7Y zjgW~|t^4d!BZ)++IwX5MtosG+P4am~Wsgh#cj^eO5^=VJq|B!Tf$jD%WX(Pv?nC%pqI%C9MGCTW%cHWJx~}vbqdKfHDBx zw+zB}YQNH=-V^3NWxQn)N96ALyQ7#Lx(|Hu?g|yc{vp8SM8QyX)bB3c)OjHX}PuX2NA~Nqh8XYC-p2;1kYb5{T%K#8P}^@ zmQJ7Ju=oEym=qsQ{lzW#t?g}N2l?rVrHkI;chOrQjK-7WJ#uB-I0)Z1ufZ-Uu*+w9 zZF257g0vO|a|5vZ0YCBU=!rFv|6MxfM|xF4`>VQO8QDX0-80}!L;(Ph)7ZH8j0|gD z>I#x0FI9)dl}A z=)ho>sS$jrCEs_ke;11M=YH{0J;Qq!QoIT;6b>yAj*_xZq-|c%1ZX7uY8{P*0`tCx3vqmG;MQN<6; z)pxerS47Ql`8*>WOIbr-F#@}Eq{(PB)GsmyHolAybA~3XcB?MOR8Oo$+n$3t z7v(+C(k@Jj9T&rJh`kZyJ`KP9r)J2R_Jzz3-E-ezYq8P`p3Gz+*~MIqJ{=IsN%3`S zD9(A1>D)CprEJzJyzJMUCprc4V|`E9r2KltUtX;N6O$Ag`ioyYhRX)sD$j@NlfQIZ zDORNh>MaNB^#*l@;`u3aP)*pnPA(o#!La-i-PIxw{yPkHj-@yL2}3;;hPwYd4Ck&- z&wZ*)CQ@&Klwf4}k(9TUPq6{o2LvwZXGI(Jx1G;;2j$3S0VI(=ALi)Fb|0n~4S136 zp^;JW4v+I=T8H@5S8>oeDxS*Zn2X5l2F=J(Yzvty3dy2dr^S9dW}OsiUFo=2Kvo9z zJjbFC1widO-fyR>G3a<5t%>Xijc>#hnv7x?bsR4qCjG#y}70Z>iY{M&NH2P(6@ zcA=cd_nv8sn?BmP@s!xtCzt%P4Nu)h3&@HVT0mDd)!I*_A{SuQ87l%VDxpUx_|CCi zIhV`$1o-U>Pc#HwxSOO>T~ut2eYp3r(#>b|GUv_HQ8gv(%yoxU zcyleWY}6sqMRo6-{2~(^{>_AJVUHO4ZV{kMIiz1&{|prdiEb~EDLhQio;^H>rL1Nv2eB0s*@popg$4#IZlty(5q(6(CP z18zfepObA#!Zy)Jd)e4Ule7X`dOqns5QLr>zV_g8s4;LzuUYkN9x3bKwX0LmxPDeJ z!tu_^PeNxva^bD_(L~dh!uN@r^FpSL+yqik3p}ZH+6apbSkJnn-i%M>w{-KP+M*E# zRJS!%d^{y-MXd&l71`zVbPEJ{OC@u|PX_-od|OCrV#7ty3;OjX@n6%t#tc0{neBX= zrC!Ba6cY0`>1AXA!u@ycADG}J-9Hop-5Ko-@5#Du6~3R=0nMNnGy{RJ<8L9D z0D~?EPy)(MOrFW;cDbN&Vs5ebL8`PW$vl^m4(Di*Ko<4EybDF>E0XFIRNJAbd9RT1 zBMj4U>Um#InI3{RKTe7-45VK$3>l~;G@mZrG8(7CoO+)6Voe_LH^aT_PlkG?P}4WZ zSN901j_Xs_kR?TfpH|*AasXWR_Eh#a!xiH`+Gl%DO zyNDF$|MB%D{!q8y`<6 z-0n9)3WPrzKl4ZkNQXbe+FTPi6)g@eu}65dT=IEVi&Q}SKlrtH(OVV{bwZr7S@A(| z^wCU?KU`?Ub8sdFkcr?acU+BR`7wR*&_~p6 zWJ2@r+v0cQqirvk6x%f%xtxMp$uB-f*9>_Pi3qYnmxXxv9WY;Srs^yHe$-*ckNagJ z>H304BHbr|eY_kFPA?9QeN+IzH=uE3eW;$`2(R<(#jp7GmY+IWhe~4ySI&Sd9)Z-J zL<`!8U-BGqlKz>B1}WgIFfc={se1|6;!2=ef*bJ9Dgq7wogi!SCfi{0l)Lr31V4Cp zN*(OyGb_~8HCDVjg;{zC6SdCXTRWoh0t;;g;X1?YD-?jRC~$l;m_GFQc;){eh&1{LZT`VsO#BC zPqJhGP;dWOz|>+IDb|*JKuca*ZV5R3xJLN+Xq#}r?8a_LEUISniaU0qv+9mB zF(0xU2mvyDfI#iQ=a5y`A39`l`@$m>Vx@{alm#Y?OrGIwUvtBRiK$~#YnGJ!94hAp z;Yu(cC^uD!dICb;8HWpxq%*Hsi9ns1mw46^(&-Pcx)kyZFW!1)uVgx97zM@sE~JsA zM*vrMzE1RMQlnv1CY(7}>QAdf0DT}cS(&=Gj)hT=Pg_4c4N;#ydORZzM+vxY4 z4r^0C-Oe@eIjw|_q}=lcCundj)M?V!v!N$X{;s;IG)uTvRLO$eKBO-lt(ME|+XVdn zP=k$Mi~g4&8LJvkT*A{0fok3m!>dRiTt? zQ35Kb9DBa>=-9s_UTfGVEfX)Vw^AKlF0p)HSETdeU$!xm0YNd&W)3fPZ)pn|>+V!} z1m2LQN6+LNEZp-gMl|8(^aM>vQGSh-9Q4|*^=?#9+ap(^*l6#!f*bcV# zJkZsE;^cO!ojjPXhFgJJR)3qmz5KsSM{UC%z#b);fm`3Q*k`EDE-ocq-r|EuL7G)_ zifdICi@vB*6Gw%9l0bxvY1hAP4;)rxxA!PDc>1>B$afPxa_AxAo#gZwNt>8w^2)jD z%{(E~18F|=Z<*FM152CTO?e}evdu2o4{@qS#EO2AklgFmk}Hy zJ3S7;k>Zr(i=nkZ0UyBL9W-~W!G8HoJ_>riWf6V6@|((MH9z%-p4D8p90;~kTxI^o zGHTIDA>3K~n`^ymW4=&?SIk+Zfx_e_Jo-5^$BE-+ctD)L5H)v!qI}Q8j9TH#$HQ(T+2^R0Jh9I=DXm%38r11BEB zPVU9E(e6g2NXxvGH(nWzEFa?dos|<{!*lfkPM*IbrIV87I+t*13TaA0cqFCFBsGoI2>v=b(jj?QUY8`;fg*M^UtW_8=|oKpH;l0 znd{aICe^*wxI1`|YyFp;p6_XM?mA=G`0}$0$>xU-^@Yf9%jeru88=pzHJ?@^UYEw)yNi2DSl7K4vv)HQHAMZTygvj1(q`b`3<@p*b)18o^?$&B`m z6F)>^O1tKWS_*Z=S}20}un6^R9~KnEoDeP}JdcCEQcnlii#&xcZa5T70^atwx%N+1 zr(FlR&6Y5;Ll`QH|IxKS5*Y_0rza0aI+j%7=9?wk-^G#*~Us zcF|bsGm=Q&o$0J{4I<993H$CE?^B<%gZm4?>&GXPbGa{kx0jz!WX&xcz&(7sd?qd< zYQeTy`DU!K(yb!vYvP9JFQ~&3FJp`KyPIUjl0W43tbdA1;WIvY^ULD)_AQ{_vlUkE z9&u}0324)N`mL$L{Q+?vmQfd0e!Xbp^p7ah%NCo;F_B*4cf5~wP9KU`P4Oi%`5lY- zbhJ7bgu9zLKzcWlEd6-V2E;qr*XTcqy$$S^A-Vn3O8z_8D?zY>pIIJn+m4QQ7T=P! z<`FqX7A0ghIyqS|B6{I;Jk?&O@Tp{7J$M_A6UQdvT$ixV&RDhpdFguK_J7+i%zAK{ z9$5IS9Sq34Fzw$zH?^E`?Z64^3P<$S(fy;t=OyzJ@h_a=d6@`UbSumKc*5Qj!FIKq z*b$)NE{C^wEbD2Yo!dpeB<@n zsqF1I$8h*|lkWxVd$3kB22KI{G7jyKLCcSO*3@f2v0VYh_9L8vKu2097ZlsedZ=b) zm&+C=kK6dRMGqB4^8SX6*n`PpcEQ^$XpQKIbccwfYxZ& zL@9`G!bXYNE}I12%IeM3M6XcRxtCM!LH`xtytkkLkAMQ4J!0MsM%$S`(Z-6EqjKR> zR5=7`zG4Kv#Nn$MTS;ZGj8ohx+UN|f_Ra;g}()hCOVvhJHLRiOF$eI^Ut-* ziCpbH0SQ^s155rWWd}3%6-_ASpHx>YA2XHD$h=EdICv;Be-som>L2==V%`$J9aN+) z*_L&vXGj%Sd9GI4>)%}1Rj{CX4-FmRARGs)JZh zIuo=)*<0l$yq{cRfx}uyQ5S68y`?>8ofLj19Dg}+Ffw~Q%pNdw@947%s&gM!0-N3R z$sq08#@nZb)%~f7V6bAYvt5{F@SRLLd>cL#7H=ykHHQJ+EQ^8iLx0=vaNtO;)2VRE zR@j@@_~7v=`Tj~X(-apMwg`l{2ss9#`LQf50W+>!Z~4t#-=ZLrH&`-%;v#BibmRdm z=b_GX9=Mn;tWRLdg*7pr1^tcwc)Ez|*UO6b$Zp%JJKW__YB~mE+AWBut+(z!+-FlX zBppFp)>&jr00EoKwwOX_)*DD@qoLoAS7*0F=!Ey(cY=B)4A>* zrp=_RKr0{A@u5JPwGlta>kfbW-<1sXO=t#p*|P6F)`_@lv3oawkS|LEqLzM({_qN; zUU}lYDcf(>2}5u_uM%W^6WD7y`M<5#cbyGX!xeYQ7B;X8UNakps(GU%p>OKSPO7J` z%MdQHa{vOUG`vn6pG+x+IA43QfchP=R&K--aPbWTRD2c65{_TH_k_sxk{rUjJ@20n zT5yGJ{NSs$CHpJIOQ!ioew_JgYyV{QV}(p3t$8x5J?V=ro3ZY06{kIwYtDi#>>0i8 zp{7x;f0YzM!Cv97yuTils!(!c;x#)tbO?ng0Fl#N3Dh-uJXV|?EN$7lZ_jvgW{AvG;P@SQt7vy z?(f>m*p3}rydV0Dd-LE94aqNx0v+j{mw29wbF%t@cp)CQ=hS~ zu?7`da->(EQ)nbqs}y1~kLK-z++*ewP8li2rAg^turFH<35KLAvqVjWm<>HuaiTU< z|AI`%y2%CS%Coue_HUo~Bmn^a68si?FR-}{e!f+(1Y1H?y7nuP!A)4=S*3@=X&ipu zLNQwjjomEbR+gwal?iQb18uYKlcF{@m&VyR`a-n8YiR6M(9IK}r*($D52)beR}Llf zYIi?KgyT^C`FtByc%v52Wyiddwt%OH;&9@ZUr70R5gXC|X&7M3(D|19ig;S09 z7Fh77aRVTdPpw-66o1=5DLk|{^7tFaHiN6knd8n4d~qV!a`*rk17da}XzwA7s#dDF z`#&^}z2Y|U=uE&Q$n|O0w}1gg9>C8<%VNqZfLr_7D62`cM4YNBYyGws=s&+mD1x*L zyVLm-Kcg&pB|j&S--pM4-?L=#FXuMbGfuKJy99aatgc{QCS)(Yw$uo3AeS(HHPe0q zAwK;$?Qfsjd`?U{tT72j4+5O>&0H>b$+0rM`V_vQyps}c>Rvb?Tc^r8OIPqj`&ZiHkU2%89As8H6DyV z-#-rhg)ES?0EBt+C*wy=dEbpkM5X0#TRu~Txda_Hs% z>{apHWBEorQ*S9Lr$EXl{J#4@df8|b%ZYK+i3W2gRh>idp`+z567DTbSZI7|&_|6g zB~omE|KPLk9p!NzIQ+T%x}wr~?{>+ivb706o28bb^(fuk=(xSP$Qh=;YeRq5Yn@rta|^vO7{XDE8Kc z-Rn>)9>6$LBW^4GL9w2PlR!$hhQ21{Z3t)-ZEWl_|67juL-mGa?s_%WUnONEIJh1!(vZMuPvel1Rfk&=8LI-CvQ-8N8}OvD#>hexWdnl`&SEcbHCoj>s_ zDnaw$KOpWp^tr)jN&Yo=no8i8v4up|SEvKqc-GLDU9OwC^!1hrne-O&RmLnZ>Ot0_ zC~4Gm7e1X-?6`dSx!BT^C*Im}`DT-syA;EDbj4I01O>!bX?SDzLXM;Fuauz0Cit@(90S}3e$4z(JoYdQCU_fG3Y&X2_kCd%iVGhciZ%jz@7~y7TagO#t=(5} zt;p%?as(rGnPjO+oS|9UPS=}PML=CO+;FLqhvk0147cwwxg~dZ@v(-$g`?Pnvbr|i zMRsmS;L6+ii7wLbFSM*7-&hk3vlBGArxL<@_&9(o3pcHYAl{`eAFj8Fg7ew*v&_so zy9Q*{{Xy*oI&FSRj%l9lCEJmzKdr%1NL2852Os($*6sbjtb1U!baCP1qpOSHk_%TB z*&WCoXrtFj!^3G{0wW`d)#Pn`mKH~r;5Q4otadijauqe+wA(K~5_ehJNM?Clw3~a? z6E8EO$bK4~>4S|9JM;VHL#u?26g4$|#V%J9lzfzHPzj)<%*yL(AUHeg1;+HV+HYp7 z-okF{ZN2qFbh)Uj6C)WGiP$_|*d4DxrgoAZfd=R!=fQ5l2;Oxt`m~iKd~_v<6>;%v zfQ?+QkVKx@{^6sbouF3KIaaDD``^5~qo%}DN+udeZxPTy&sCV)Vk01-m8o3y4;D3d zhU`FlnE5AhTlZ2Pdtsxy5i-EaJO5buRj=S9QNh$kddd#6vO7_pFI1Ezg><3%)+6-8 zfQ0lCwSirVzs{NTP)9T|3+Hs71RN(BMFFHfec~-?HksYI?+ctwG(`PQ^O4Q(7zsb` z(%_3AqY+&7Kvt9!793PBiK|`FT=C8MTOLb10_rHXd4~Ja zzxBe+lGZN@)(saOz`RnOrbU!0jT5%MB^Yp^`%ziED{Q!;5`+E3Z5@;}Yp-Jto*{0s z^f$IR`k>X!;?wpw0f2z+G`N{!59F0x?yA{SGkB&@_h$bTH|vSycaFJEHO3pM#QI2j z?KS#*QRf`Hu1Jf0ad`b%$-lnJ=ZQD@3OF2bE{B=A*BX|0SP3jQR!u*?PZmW!3bkcX zXequ0{%7CjXi=mpR`_Xy!_)VbKbk4_8Jl;v+<7sILc@7bp5=hB+!d1kPgp)4hPbD# ztcV4EEMlJfvUkhh?Jg+A)Y#b+#l-{cn?vb^X)zBti|PE(zCxFBRG|g>q$O2&4dE&B zmP7e~wewrn*7VxV9mMIC`!%>I+euvFxzEw3b1xY&oHb%T^1tl!8>>Tr ziD@Qa2koPrN4uRr-oPA*b&)-fEkXv!Zv%h+L(vM3R#QZ(n<@q&S&iHRtKG~AWL)=~ zkkZ@v$zNin9({{;Y0#xpr>j~v=A5_KZb({?tOk5RS2j(MoR?QYNQ z;Fqp^QKqs~%F`{xrhI|#5DhsEA$AhpJK`ikJbAh0wktA+TiN(z4 zf28t28&WSBVfJYV+7Kw@S9q*Zxffghv~FxJ+MBzdHup{up$-~G`2uMh1zQKY07RlH z6p3UjmWXvxqz)c94wfQOSANQgsC_Gcx4!kz^8m6_ss8U7Rh@fT#3tZ0|F|J zy5+nhTh3#z2?E5L9f_DGtAdfrmqVhAAfwkETdM@s=5yyz^~a#^P_Z{88v+?xNd-{oR#JfdytMP2O=Vr+v)`)jtpDwKp%)908Lhq(2FEEtbKbyo+CX;Nv=3AHMlvhNZ z^Cbwg!z}0v;4)o43;759=E$%8CqhMFqTP8FrBMMQcNH%g^U4rxV=ULtvNXHiC?WM8 z=BDL%<^EP9#&`ZU%K2>-O9>j}Fq4_LLh8kp5I&N;W5boWqt{MaR#3W^q^A+qj z=2AegCWt)*PE~=N8cja`Ju${N9R45kFV9aI(b3Dk7j z;AZ_|hWlB*L(L-X_ddSnrPrvTl=6vu>)HIpaEZ&5Ld~b(xM1AkrQ6zt0X)qhG)4Kx z46-_{v(<*IL~pO2d94(`k}*pwUF}1YA$C0j94m^hA$AD{5W)T_v4N(m_1Gaun-KsB zx^_NUGjG9`;?+4w#OkLAOTu+Jw}&VhwunkG`>G%~CwRH8FsDSJ+BM2|?k@}Fj6vd| zbN9eL3N`!6<=04rHVZxs^NpzfnYV!Ut@%Eae`K=u)-X-AB-lmUeUZXB1!JEr&Arvm zKJ9X6Vj0fzreXfL$2J3ie;yChlKji_c5GeOOvpDWeQT#u~H14kh=FjGC{sOLUwj*2?v{I zX@w~@N)_TEM~S}bbp z$Bl~{VfIo6AcdIJfE42V9_6**c-v-B_`_(LDKz=87i?-%gKF61)UwCKAkCy0qpHlc zDW(sy$Sx#7C}LIg5lL?jv7(Xss|x>NwF(s!tunKu#5B^aUC7M!ua7e-p)Ts8mKpuA zCnoVc>sHTSPrU7+JM=RmSu+CVE0t2IUz^RnY}YAY7-t47Y-KXF=29K&Ue!wJ2Nwny zHJc*adj!UuvpV}eY1mc1a9pu7QgiaMTho~tuBdYlDV)`qu_w4%+Amnt5nG!l>@!5s z;tmI{{IM50RN@nkeF~?6nmP__;u^+OYp7u&fczmE(+-baY5ATYMIzEe&%oFI{fpCn!7Mdv4?+eV+(o`Vy-Hmh@s~X)`(x#Y?euunC(W7h)n_VH)>`nwnxnQ>W$SlmHBrL@wg`xrB-gToa%mHnl?oUlj?ZbCVN zH3kb5OQaGyo&x4a$H1hML$bo$uZ0v2jsLLi2dL(9eTB+h7=v7z`EQpdy4$l+-f0Z7 z<5M)Y>pT{9=>Qd`f2U9kXASl6iUuPMx27X8)j1APtppCEbSqy(-^MZj{qqj-PSG8K z`pa3rC8O3*?>Dl$+L1rYrOB{md|2)P{ZPt!t(Lc4xx~mkt$*_sUqNTjq=|QPLG{~q zDgBMYb>2OBKR$-)naSAaFB=m$Nrs2y_U+Xi(N@@uqPxHyUJjH?Tc=Ug=$xR-ABCwQ zb_=e9tl5S+7jFRO+l<8PK}k&=CYAp?t9%%R?0k)CU667Fc4jl}yvI20ZLI!sf^Fc( z&s5P@7Afpt2h$KV4YTT4>N%8jWiU06TnLBz#~fAYw-z284%w?q&D!+Su~^azXh8>t zcl;&d3u+F1vp_3nmph`}I8kxOc4d2Y4&nX#0!v8W_YqgU!-`MUlZXh?>d(hPKl%cl zJuzn#ddxUzXK>(Q3sv_z_$``g&<%T0$Qfw8zjKtz6kh-OrWhbux5vk6JCm-9Fk zT#1Epz3dM4ihT|TK#Ay#*ab#*E+JU(Krn>bFiHUR3bot?f@mvVn?Ssh^k~ClO^pD- zwoC;7S_vOb1N&eP7wh!524|xXFX&O6FgZ*OmkUXz;hm;otK8q_qOW;*VfYGM!;%BeNR^X z7~hJ%f2d7(pz%U?N%h;_b5Ty+Gg=qFS7(SG&eP=A%J-jC_yTV^$@uYpzs{~S=d}=? zH0RPsk^b2I!t^ET#tk2o+c(^bMyb76C`E+WNcl0!vZVlON-7n6`&jG$+hAxYhonzC zOup0v47UH5!C)gNcX5XS*!qP=AgP0*;HmLw3*`@kL~wn$3A zkIR1afS=3#tj(+GQf&>*Xu`sArw!$oj#>kqYI7Yej?9W)~mq4Pm-= zN?>B`DBxW@@t5F!JWKt+!8+U;?fZf{hYAXG6|Vem8*f0(_-bHo=3iwtYY~@Ztkm zf}ZO*={kg`zYz(1oc^osWBt`~scTJ!jdyaKaVLpUmuULYib5q9Q9t(?IQkX;SUxF7 zvrTb6boaJpa+C6B;oLTd!eus+;bFObVkPzKY{bQlcDxX!JA3{IM(&7otULooC{s9< z<|xQ67J4SHZ0W&9!AH#IrOOHok zV9LaH54)HI{(+O(H%!w`X31FzDT7qkZPsEdz$TpdEh~0#@c09bWn~MQrRSB_c9kxD z+p>sg`0GxAdG1NlF+Ku@ci~E_Ni%n+q#WuYM_o~KBO{SwbwSM1%n8hto}XIo%C2O( z>(S$x`HCu96rCuiD!xX0!1Pl+u}5fR1>}s)zmhQd8p6V*FY5o7EryUSLMg)9gH6Ut zA+47{32mbxsq+C~v~8SiK%_18Fm(2zqpS&VlPM1vpi8mb9;%?+o@2rh#48waR6(cA z_%BR7F;ktGwPSp8n>gHHUWbMZZV9P7to867G_ z8e+~qU$&MJ$~?_aI}9AGA2d9GUvC~+0`e|$YHz_c3&+9=rAeGMqPq(hr9H~f9g=-KWldUg{cbhqZvkXyP)3}?V&s`nmn=lV1qUbD?z|O$8Slrc-*ScyK zpiBVaKGzh4g;?c2c0meiqgVx{;CD3EmuXbOIc%f8F@!=F$aAVGsVsv{CQ22YFVaRt zuSB&=bG#^H2kmHCzX|=L)`Xie&@>MCNJ)c~F@z;VlY>}xX2KKjoWCQkOUvSz^DO}} z_Cg4QDyyi=&*CgSKP~9jHEkCaAWyPBRhRWX)+w0Jj1nS6PgxA=R7D|Y@NAMjlUZm} zLR6ovdBEi&+n*;lE<%3V8I;&BoL)}?!>kn4IFWNteEr@Kw7ZkA2U!#_h5`7x(IP}D zaqOx%*lwkf8sLu`UzqnM;&68B&+KI$qfhM- zIgavQZgv?vJnj>(11*|9M~7zPH8(Q-HU;M1xoVZOT0_aTxaHK7oSfVd-8Jp|uAYt5 zWaZ>@c(1YV{+DttyVqgO8Y z%3niFeI<%aq?2{=W<|(yi)3KS37?j@iALc(ub@j{pZQP{vl3g zS`LMj4jpc5#e2!E4HnH7^E)ri`i9q&O0H6dblJBoNh!9;N_A$s1i*fnP<9rW@^TCs z@lMMC8Oj`SfCUtJ$DrxonCCMfjE;gZa=$J7gV%0J|B#0B3x^M%J<+3S&vJO|%{@MX zsX->n)O8}lFZL3fDY(CWf#Mzekjd#(M|BgGcuXmkJtu#NUQ+Hb|I7X! zwpgrhe#^Ppb!OzecXt=Y5ZY>?xM6ZH(kWC2`+YwDqYY-)iq&=59ChMciV(v( z#2t?^n+(g$*NkQL*qe&bMuf}Zy)?~>enuuQ_2pEipCG#wccc1(jCGnrx{j;k;M$4~ zX1XJW1HcGmmP#H@;B50qc{n9`*0G|~Fny-HAD^K^2rRE1%YBGxBK8T2fqxFsngo6u zx*KBx=Ef2iz&Xd1Ro7O}+UrK3mSI-k1IIyUOihMBxL>%P$~FM|w=t%&b!rwrdT-lI ziT(#^b*ahQLyfnPrVSy@8QI4bqM%zi43qP%x+?!=Byv$T$63Mq9joyKmwAAaT(II& z(F+yw^Ef#QiWU-7>A4~|4Wx} zaB%rj@bikgrRMWFr>gGSjJcRNd9oHc^0tys_HhyK1%il^T%y&E=PJr(?c(ZO3NpqV?eX!pGg|Vp~QTUCL)Op^MLG z>nNzq4(&+Wpb<&6o)W{GFkFpQAT zuCVJ(H-jNp4sf=6c= zOsR!7 zQc`mxojzgUN?FEFODrj^s`O zuzj1*uI{Md*TTWt>90rYQIutgm+92sd&EU<$t;1bfe#+F9hTzLrBx&HR}N{Nsf9gP zo6}w|uP5F7)KPB0PMI%%L%)M^8*--M{I|nJA-C8x6Z_H8Jc0Ag0x0$Y+<*=#L-uZew3+QvKH@w{(((v`j(TDsu1glePiBW2-hZx78HerBWytAptsUZuEI)L z>V9au(a9KrV=Z$8jyeS#WfOJSw4W2S!o>tucJ%e8vEubn%-8;U0P)WaBb5sWiLTp? zT0gLd-cvX8LX+1-0WFXz=HNPv?Pwd5hO*}s^&>)iQE&N;mrTVcByzRduC43lcM+Pp z`i`*sUD6JWoLY_rSD!mC!Ur|(U6InX9OsYhcdjfv-oXvys}#O;85q)CvT9S7N=*Qn z<_ZLkOz;d(%Kqw*OX@pEd7h>kA^q?~-zgXuGg}LHCSB9-mn~>< z=uU`$%@yS)ZSKX~zRF z8@74Z73W}M<>tc+U2js2##_DxibEifnXwOdD5$(WaRvy1;cU?AR(l)C{wo^1qB}yG-o5uS?&F2`KL#>KOZPFb&IW+b6dD` zPHV||Zv42ic=}QY?cRKU4Xh-|WVF@Pw){nGOFc}Js_bkzs4<;o=(Dhwrf0i*MIr$E z$A%mK*dV7&gi`%}FWpkzLNK5U%|VTph`^gbb0#98>@(C8Vba!i$c!AXrT0fre@m~g zSDGoRO5q-(1S2u96{JoHx0{jO(XTP%Os5o_HTqO};edIfgU~GhBNA>mVVro5Gl;K) zJzVYcCp}x1(7iV)^IXD6{cCBP%BZ@_axmAK!W8<=A)HO3Gr<++Fyl=8T7|HBV_3lQ z)sW(BuqZCy|sC~q8{@oz@)5*~5= zWi11Cz0DH{Wn7fLUTdZqC>1ruezPp@ppj!5|1ks1-}_hNXWzI7CE>5p3~NzQy}pKnV{gu=NA&?nd>e(Nh?F75x-#u+Pr~!!nz`| zK^C*Uaf$}^b&FA9V0SE)^0T*=(fKB+^6ABXQD3D9J(P?>9ZZw2TLpp!El%UH&ql9!r)u=qxxef=Un{>^^tv zX@8tEF_cyiw7jvrb_&^{pa_(Erhz zH@!SZ^QV0G5)M3)=&)IGiqr%$?`Po!L7*mRERf72g801rCq7+=Rt^>{NwdgqUdUPK z^g1!`LpiuYvp#ceaiSKkEMZ<|FrFh1fueQwLGT%9nO_txFih_`l8M9XFznC!WiGpK z<`t4}P!PB}l3(8dHAswjpysnLiMZ95MOJU2nSk$vjna^*2=FuGjCcyy2l0S!H7h|GDNpiM!yJtF2PMG@~O+Bo6LcsyXo5-K+vTqRQ% zV+bw&G4-!zJ|_)J#y(>9G7DC$_T`M^TqBLKZ`CZPk3)k`Xr zD;Wfwg02E;lt8`X?jBZ{5;@PCGkO536ON^E7|w)8sun90ERhH8Zx#<`kK7{9dws(^ z)+OFh0jEb9s@G&~IfDpGs%-F6nYuDk3;H^Cnc%})ZnX0q2lGjk;tq6GCP$tlgQqM& z-j-0_DI?(~kJgR3AtwJ81+z3B4s8rppL6fvEi(!9h%bUE3_0@>8&D;p!2l$RG3&HB zO801Kmt?URjZ|3d6gFazbu6v)nfXvzYN`yF(DbBP<@Apm_RsUkzE68D&G(|09S|WN zzZz}7BF$=@hWK_ORFGX~mlL{AJqcNY*v&xYytxZ;V(Qs>!$H@aT>h0j%M2P<;QjA% zdigaa`h(_qY0dvE{`*Z{r=|d!??Uu!1|`-}$bj2b+~j5a>ci|n_rV)%q1S~UFr$7jj}(sZ&^+8vegCk1Z1=Jzh&>8I+)q zdcS^(b%X2cq?1o{7s-csXg1B1W6Hz~fHxqv(;g2J5eX?~q0g;y3ZM^7%~9PMbTFkG zV=gs!EL``qD=Z^Ks)_Zb+JZGj^#GUY)12)t@Aa%*s&GDJkY0JEqd@dc-0eG$_vcv+Jh zE$l`)k*4m6fKCoWvZeneS$ZdOy;x*m=OqX)`k*{{H7Z_|L|pO;k-Xcl$AT9WrdWST z{p|^`r?YD>Y}1-ioO88pQt@h2hZz&-`A%#er*OJzQuF%0-^}hwHZ%mtTJHK%_MD=L z{j_#iXW@%qa)s)Q>w=)I+UFbMHnKFm+o=0#)9X&du>o!!#t_Pn0ph;LXdWuoApT4P z&kx)pqfX2a@ffNx6DRrZITC{_{Fz)7h0z=8vt%-!NiW*5y}PC8K1EQj-wT#0F`_2; zW&QohW`9sqRRxMi-%qZu52vY6Oy?*ihDMo{7qVaJ)XP0L8PCz!S=Qa$x_b1f?q^=K znZ#|4TT&jQs3_8n5G9P?9IIb324Ai(@rV?2ufssN7sfBVfc6W$%(%J4?y;h?jm<;l z98Kr?@R(wcp0Ynh7+GRjUSI#MnRZ9)Sv|(UKL?uSlnVtEqZ*rIH}>R{W;45(zY-=8 zL9=Im_E<7`yzmg75pBxka=oEHu<~ z7IQw7!z(nKckeskZ_Tb?MtH*uOJ@$wg}cp+izU?l{>Xj#TmxAAKEqWKRbv*|e1Rx2TGsQj>>aYpH zUn#UNmAX?sr1o=laSiOWS4(Lz%7OW{F+;p<_?1VN3(%GcXkP({ql?rjYhC9&G^?Sh zrDhmT9U1pQOX3@bc1$&U^KkxOYh0*fV7bfsg9LI+i|Iv>Hyuhjpi`gXB4cb#80M07 zb`P{S=!D&?^av}|xIsPH6Gmj9#O>qrkvBDhX z1U^qukuGK?&4%%s0g^sRr{raVq}dJTSr$Xvhams-heHx2_M4=daf9{&1$}<0@7i8U z80yGPkJD9n3Bo|keYHg&isTBy_Rsf)A2N}eJ4HfsmK{vh)-;itpY7zh%Xi{?Q@{=V z;;IGb3d+1+x{fHu(ax=@SEXz^&!VvVy%M}mHuPEGgzd|*Bj6*uZ_EWBVXjzWYsK9) zw8aYgqFdd@st;vq7}StOmCJ^?O5rW3XSqgIVE&bNUFMSz-LO%{(KO zKqNXGo4R@fZ7FscwwYE!^PB%bE=?L>?^eALV{rpiaAw{@kd%sHt{7(K@jlGsqtq1<#KYZM1aE{aRR39TVTMpP~*)s|5l%1%^_DSFSOy{^l~^q@{HfEO>4QQ zx|w`=;s>YMcc2<&g_{?`J1pjP5({UMh&BgoSRuY7r;MvPRVh>Hd+1vE(T0Y#x7vGt zw+%?tO2$5(qwGd!(q#MTX!ONiOuR0|oU*Pydw)FTl-N8FP)vqnhbnxUl4!q$P4j@o z7R$XjNUy~Q<;3c)N$02YIRP`v8rj(}x{9IwrFTl84#j762m|Y300UYap*g(J2fZC! z^+lNZ7&Cij?r<>gENPYOTIaXCeyh*iuMh=J{z@SiZAo((dtN^3cIkkiaheqGtgS6WqJz;y@8l9*-w zOJ=@uv|mvN1$&?yI~SC0U^!pj2-3&!JY|fy-m$7PLqCVOXOlH*wsh7VPwDRs+6^&+ zj*huU!T3`(dPnC6t6`>Rz$kCMgX zou_lFo%`)Z)Wyw@{*GofU#v1WNi;9%VidO{FG;GLH=LR9REm&2;~oh{L?l8NqusP5{(R%eikaSRp_^Lm?Q2cp zm30xc>xZ#Frnx2OV_PIOJsh9KFR^CW9-MZ*ypUkY`{gQ!XLd(g;mC-Q4~}j2b1b__ zehE%p#^GJ(+m>_mXjAt%waLU~L+|Q9%?pgy%YEAA$VRvVf4Tv(KU^^_7*9VI3nowJ zDV^z+k-Ay~^&1u6bG*i$WR@g=<_(=pW@idglt_OVD}IBwXjGh7g+CfM2J!DPh0XEQ z6e35d>I*1O(^+P3LLc4V{p{BrnOm!G5u#k%<58#j#m=JgIm%D4}*>non~6 z{#4K#1Qt3vZ}NRjw5Fo&?W4S_99iuri^#_P^}{sdO{)R3oi_hedm|D_M-OHMsvVV`41vW5)^+9q|J3bwNi zh!t`PEbkcCzS=|WKYMSsqMP%EvgFA1j6!Z3Ngtc{`qm${rU%Yq&tQMr>clM0D@2u4 zf3`_$d7jcHFddu!yYv-yXF!IM+E7=jp0P@7{AJthmYL}dqk)3NCH|G3N$qKEE?l`2sR>`;_jB(|#Q<6VK=`JEiE`6q;zU?l4nn>f93gRpFYXPkK&k&%@8H z>;6{k`Q=ws`$VIs*Tgi$Jo2+AWN#&b8n!-z)zq6Gd$gOSpa9omI9k4Ys~MNAJj)~D)_wQl$iE~d34Mqg8W zvys<1bZ|Vnu2;{&BL%VhY1-lV8%<=FfV5UIMu8`sCl>G0PcN2Grn+aft|P5D@;6@cMJO zjdvFfXgllu)_PqtJp;-#&^g(zeQWhnofXNhcHnz@`S-ouS03A`ndW`nNckMJgi;;n zvXP3wdjr4oGvW8z%B%Q>x3g@<%&{r7`vvpE*9L@KZsxx`5fIz%*9Sg-YQ}i^nO%-g zN?mF>CXLsN@;WojpUO&jtaV9z{FUB@(DZOIj=eLTS}1)!qwvX^aQ3{~5X_~a%;r>K zr`99)AbXXHp%nosvR5?_G(lFv3I=Q~&IF1SF_3@~ zWhaan_OMt8BLgHPG9w}h5Fj9g5!UxaeV*s@`2)VceeR#|rQGLzuJ`r6-q*R$@xG1j zIHGjvfkNsrAF)PpOIGJp#73qwCCkJKD)BXM&6HUof{YBjQD2MpE(|BId=VL&y7P-1 zU*+P(L~an`52_(zAt9T0_PX9B#~3KA&N{}45pX?VbqPeS2P$k7=tNVYs9AUN!D|1i z>_BhjyN2fl@h$!G8r$euY#CD%d=x=txsd`z< zgnH4J*u(wKNI$3FHx8$j-xazzu&$*sEhki78JLHK^MvKDwQl=~|$@AhvF|s9c zdPdtlA(v?1CH7ZK;)@m@^IM-suY~PD+6+bMBUtMzT2Z%*FaZ?l)iGvU*MQUvZLYvw zq$h6jYi;`KisOH?`CS>ZTu1ePpRoZ;>ZewEuiGQ)X7~fagLYB3O~(!yndf&!5me{y znP28+FGdAX97?UOY>u4E*}lCfS{c+G*}LnQ_v!?NL+uWDVc<@w;bzXiHm`8TFh2Ma zwti?FiH%~9JVM!|^|c{b#by~#HW$Tjz3Vn2?p5UoWu|nYwHG~x4-hV*ElOYL)CXP?Hnm&SGjZeM0Dt3u=vd0gmyqm{@DXL%3y%S5OX7r$d%-lKxHs|H zCqCfRLJZFqWc~=I{?D;VJ9`S9gYkn->#r(=$%t=M=oY2H5$5?Xo_M@i?DNh~gSP6X z(Ge^M2UgE(vt%TCtq(r!+!-(+_CeMuDVOl*%@XE=Vhv%%Sfm_S&8IqciCKdBXbN4dl@Yf( zMa^(aq%Y3YUSRzwq16}{!H?Ttw+R}b-ib{hv3Jw4tT+GGHLcd25KHISvwIPBjVHce zXY8Q+U8l?BvKCN~Da3mesFJ>gj}hhA{?p=(%2CE#ryh418~rIqZ98;bchZN*)~9sm z^u>e?Iqmm4XB?JJARn=kJoO z{~$Os+?XX6ax+(BK(OXy=zn6>Q#F#QTaeTc@x1|+zT&RzfO!(MK(Dj1G(ehlTaOdi=e^d(_pX2+#bzBu5Of?Yxf0=`@j7b6j`vi#^oz4 z%~J-fJhh|^H zC#3pq57Tgd7*`iUeMpM=uEE*x!edFI^l!SXlu;Qo_1?4(d!AL7s^pKdBir;Jo6n^2 zN)s=PP;S*FyRRQZhEZvYt1qBX)?exkeg&#nHi@lBZ2MTu-kav0WvvNYR}VKIy#LSi!r-)ueCE=5f*$$-(?_ zD^koO=wo%QY4`LVfNk znJIP)>-CO0>|;^R?vFu{&?FZ!&B+WA-(Aox@ltE3KW#%FUv+e*)?6yI zPOON&LZVBA<`aOJ!Mn%g#j_`ytwL|;IQ~gWV~+z6>BS~#^iAmr?&7-!dbq(bcQMz6 z95(z8z10MPPVVx`SAUAQ-v{Q3?ESjf9nH%^mUSVcdNr#y!=G_TTl0gJrBo+T?vYe& z46oHEz{7s=XNl)-tJQn;GpO9Lj)+O31IH|Nm}3Wz#lCqF`TMRVS=nMq;)PKPzOK=! zp^jrEb*H!;tDq}SE~q|6K1b|u%SZil!ht_N4Wt}3hn^{zZYhQfUXz{zkNWoS*ewuG z>aZtoCleK%;rJ~qqitXX5xZQgxMCk4K-JYvFo4h&R zao^y4#!yW02I6DP?dA=8gqXt2AA2X%BRUKKVAOJUdym;Or2oKDVy{a325Dw{*$oE} z^c(7MAP8Cn?Jc1Ax26_#tK@@(plpWTBo&>&)e^iW%;(y@yS>7wug;hD?K)C%gE%oi z{?CNP)u8X0^F0sS`WL8fFJ_*yBYq^KmY+=3?kN==JF;{nC_IrSu9@)W2S(4w>l%3B z)tBNM7fx+DDpS(yX8bugnKSRGc)|QJG7qtX9C}<1EKt#Gwl*Nbn=0S7<7k_kp!fgK z*{jCx-~6cbL88EVQ0%$zaj4!U5_=!nD@mRqcTyQ5$xYx|7lC2YJw3(7uQcoAEXY^P zmpn-?Hznm~+qqv49IA)?X?z;q4|=IlKs`^(Z0RBFOcUd9T5nrK69XxbG0?-cHH?>q zdKe4_-0)K=A-P`22yYYe(X^~h5I&^37;^fTQ&qu!;_ueh%_`=%t(qYbCZR~77eaZ&FU zGy@|K=A(FN%mH678_8pI-RV2m;L{GJHZLSvRv8ar&l~d(r)9K7HsvEF@-SsdjDIE~ z_*bAnknrY6D<@jl#x+ydYJ zI^QJsf0R}-Xr9L};S;{vOIG;TE3f7v#Xj$1Cr^5Fk)&{F*TQ3DMHw&bkrifhy`J;>|f!E~=ipbTKee(Q9TYeA;ibI;kpqL_ZSK zEFGViC{%K9a6|Uq3#8DG3T2@7kERE4i4{0i>_#?QkKPd5JLOYr2->5<%a)yxCm!>8 z-(A8t>QhucEn%Xa05{7c(T!%dNC6#C>pz25r8{-##K%#so#Vo=I(qG^wa zaAI5#6zMLkQJ-j|vb+bzmxmCq*TUv=cwj5#A+k|Nwd+voK81$^~p{$Fws_yXytSW6&>a1!1p=5Ibo!8=P z?-$HvGG80<;;`ldG%G&7FH)aeW&k+@0;_(9rtO#MJVz|-MzUhDI{>(0r_i@OQNuV@ z60G-`rYbITqy+DR49bjphoC%m)7yp_qkZA;^FoA^=Re}>ly@I_6sh+3ZPj_7y31?>}dp_eK6nrtYEtE6rSK-xbioIUA%=9hc# zZ_1-$(hFme=AGYe%w}j~_tgH5OACRwST51FwLTcchGgeS{l=q5mY;E!yg9M$YqK{> z4T`kFy3LJ8V*VDB`sQsWDc)qZj~dF6;_VhZhKG9@$p%e0wr(`x)F$RzSiT_yJ^Cf& zbk77lugM=6wN%P_DF$}g_`_gze*6&^$9UG70N5DBGsRo^A zUL!_uCzzuI_CbjI)x&>JASz%+zs2pyfkDky6(b55?ybHm=_ zyUK<2Dqt;kdh!KlhdgrLA=(x@?b4_MgxsJ5=3hibz}CZ<7-RSee~8AL60@hIqbh`VPl zjz^i><1>wd1-U3Eu+lD3u^?^vbq8<_F_}@Xd?J(GYK7JG@W^DGs!nH7*81LuY(*#~ z-OKYcMv4oyVi3)W?wzN563u9AejYRr8TdV8!b0juoe?QZ2D(<>sSI&`Gdsh{I7ySu zMQ4<1P&QJB305&Y3px)&5QlKR2()=LNlG%{c=#<*Px$;|CSMwQnnCj3ehHcVnw6Dv z(MTq&g4bJ{6hW__A`-RTT)z{;ZT@;T|A&i@r__p`TLrk(=L_J!9z>B2?K!KtaivET zcK)ylSn@qwS%38=f?ZoH+=_D05S^@&&B1g7v~o8rFu{up+$FAVNt7YKzkp^CEb@wy ztp;Z|73FpGRh>a`7v*bBNDokgsW$Ye0uME*L zB)tOX2+vEJee3R531cjF#V3Oo#>Nu|#^py|$neVnzc8M5lqRed zoDggtC->UCwxEexF=5k8qbda6Txb4#wp)JFD`8CuqKJqEpRD>Z7*f4k4}7XUD+=a| z^3Q25D6Bka%T5L40xf^>?1S`U%nO%$m$>(bF(cBiO7$l9`R7!>t_$avzN4%kQ@?G! z))Gt-_&pp&*O`~*ue=`p)nDgSuW4_JmdTjKaK~MleLUH^)IJvL@9f;*DyHPrEV@%% zf`k0qX_F<$v57Tf1g`|!c`OceufZzRMuO(p|_W>xqVBr(h-M z(88U^=geIX(LV!19Zs)HRzj}ZOrK7A6>x=TX7dcjyas2jyC%fqnc^q;s(=?wghh?e zF*}u*1@;u-QW%9?w*h#T#Xd7xVp+_t1-3ShoY!&E=h*uL?t5+y$TK3yG;wUeJ`KwR z5{M!r?!#J6*q3{6gZ!-@#ag zyz$3dcbRA?&#Q`DP!|)|OY^YyK4uhLLE;OO$yK`)7YXHgoI0^TB-Gr{E7)(;Nw5`! z(O)%L;+lNX$j#}gA$XS?$V=Gi$gN7UqqV}%97QL4(vorJI-H`qg+H}%-a4+2YnltI zDjp2WYZ&Dr$xAN}=UCmPp#JZxb!kt*fJjQ95T?F6+{!EZI7En^$Bm zPEJ%bFCK}|RLtjfR7Um?+ReqaK?(Ngp*pxAmu8NNHsAhIzWiK1ny*$Qm0mL9vCynI zY$$pt0=X|xLnX>R+bttud>_E{gsx~ussD{4kn4BnORq{FxVJX@`Lvkp3f`Y-0B;d1 zZ#-MguHPfQ$T+ty6<=6S2&(Y@4iC=kF7a$bM!&WcY^IM{sgHJR6Oz@0kYaj@ZIu~3H{bEUgRz1QgL&`Br1FGT8!Tl)kafZTXmPq?&|tR(jZwJ7dt(K4Z*WBhWz|4Q zglk+c{{Hz@IF}N)`Uuzz(2z>hA)89_q(s$r?^usALX>1pXG&!UppNt?<yx^{rpT19;vIxa0 z#@Ex0Q!+pp0c=N}W3`9G=Qa(f?jE>)O&_jlq*CHy^ieuPE zs8=y_X}uc86@lvJOw92Ht)iV8(!+vc#=Pm!p=a@^K69dcH4JIo2BsdMFNW5CBx9bb z`vG%@k~mNo>A}2Hx;BvEK*D`zXeXrpRLBi3IecxRwZycV z(OX#cp{|1Dt-~)Op(R6All8xq{3wDfBkCcLgx(40b!@sDJ}S*0=%pIj$?bsc=fSV@ zw>~PHBzXf@KNbeX{J}p)UAXVwbT#Pih2*xt>lFl@rN`gdF;thwYkCdcjV;m$?TE1# z2DuyE!v&m)d&&3WODLN8yiOAu$=h(CfP^j`s>=I-%RE^Or16Fopnua8`qe?6hj*`+ z$+VGJTBARC>D<2epbXF1Q>0x%*5O~1E5j!#AtS<_`T5J}oVyqkLjyZ7Yt}#@N@qm> z{A{w-#-q*1?1f~4hIZCKN9TLr_Pdy{@%iM`_$d-5Y?n@xP91sib%WHeD$H?A#mJ4v z?)cZNq8$1!^VO=$8vBdbwZD~m0q9-wPu~AV(pI2jj-cE*-7Wp*C{Z?ZV`Sk#%)-3_ z58r9Qi%4VIJfbh;h$UR39`!H5NRyT*b&duLq6Lr9BdH7a4Y#nv`Y6uV`NL|k^9lCT z&*LZRLIs%}hKQmPQu>!~^T9^WvV)Te`f1ZUC_d_J0PR1B6M#292%Y|GyP=%#%^GLF zzz%!1T>alOMmE4MHN#?U(Zkq>{(#IUQKq$89aEKKgKk`batHdviuYpEGr2oEXoLs2s~8wTA@J4dnB; z4M2wsp_t-`$p-zr_iA)SN!qHrj$!(z!ByphgN#=-&eXy$56$vR_@dSVSEZ!%V?3rd z;J6eKhP~+%2m2~Uec!^l(Ij~Ql!We%H-Qy)#?FC;NUN1csrp+-A{as4F`;d}qeccf z+4{7VKIR2~onjz3A0rriB_V?6{;{LgFi+qxGb9frwb-EZ+X_=%Jir>XFz3U?WA(BmliT5peYOE-{P#)k7q920m$%C)pOro3Eirx6 zU2gG*h@o{58=OZduO!b;Y19Q1L!yt;(tA6qUu!KuMfS_2IT=wiOg&~-{%~j2WY5wM zm$~kQRUSIe#bZp#{oqZTBw!BGh?-xOlr~YlJQcn`^28_@SBX({xXscKL1BW8(hKoj zivFBtHT!DVy(fCArFSvsiou?OG2#1EmSP{%+qu!$!2f-RZxDtv_;{ycJX z#{uL_sw;Brpws$`J2oTI@9QJ}uz2*9?7qALlqopJqsTPY;6h*+hEayY4ZvE}Hy;Lg zN>a@hlLri;Km+m&BO&VyJPEYFzCOrMSTi8 z!KgGiSJe%k=ZQ)odh1k$Pd04-v3vdE@%rqRF3-c`I=TN=+`hmM##S}C@|`N3$RN!a zLQUeMrbdp(Vrh(~6-*i!90?{(bcc2&cG9~h)Kojg69r8-Y%F-wO71hi!Y_|)S)Kt+8q`(Yn=LBTdfIIF^;wjs%=nU4O-|2YKmH<7jji#xTMs^#pap^gEOZ zUoy30&?^q2c}p1r@t=u(>}eQ~qgK4=S&n?KX-4<2^l2JTh&|xsybr*1FWhq9!a+IH z_Y%}U?jZxF_qWA9(m_4G%u8S{dho_N5^&6?!Sx{tM-9_@JFW%p3K;mk@#=-J`lsb! zC%jQ?eWXMdGu=oBB5yQVe*u!xyb}Vk_t4b+Lwd6u-e_$C^@}JuS#qIVm~AF?j|M8E z@Yj{`r@wrG?T$UH?keAVjghC6ID|-d62i2k+~up zJQUSZj!=G|twK-o&?9C)-R}5t>Q^oIC7)1TKPR_0c5-r+NZ<3JtrfWBMDX#9O}(ai zI2om-(iB|sY3gk?Xwv85>z^<*IwuG>6#8{^MXGf7qc<5~ZGH3c3gn%iG0t1OC6ILk z%foq-i^?KfGO>o6)f1H%fhe9jo0Lvs({P&5(WdY&QL<=ZFkGuor?x`=B%|{S$aXyl1XuT=>u|~T45N9emmvaQ z)ZC$jtYl5hwaP2?ltRVfNk6;M(B?T4`SAjJJ3JIqfovG{gfeFIVcAVn9=up|b^~k) zm&S4mbgF^BF!+7r>RR~V6OM-7_(Wrvr}QJHjeq)h5+HwqCz}vHLBW9cyrsy;Q^CMZ zxO(g|_aLK)lzyscO*_AF7Gu{H)47#;7vs_WCM$f;$AcF|K`783xzN@JzJ#5v*q#V= zf`cTsPk(=nR^8DrRJzrh%K>LCOT+rtNBj~C%77!nd+R7d%t23nX(_ygkGZFhXxN@* zv?TSSO>*4opc@3&fLN=;<7x@BO15F$lL2<4VVU!9zN@$&GJG_vSegt|(Xc=Q0n82X zTvd0T3Qr+Z_O}2|In0L!Z!zPCDe4V&0Thl9hBDAA0%&iKnSvR=Br;Zv7O7EtBFAZ zsIc46-ypF+#yqtr)AdY7_#WE;kUBngC}y%)e~*$MQ2X?T`N%E5?ghCUWZanD#|vT< zN;MOBQ=sD(o=Q%ygEq1zPHbDv-5#{h`uhjqJU}$XNTHkPa4I#XE6q==VXvlPGCDb! zEzC-386U(jrVj;A##Dmpzs5R3wC7s9;bXBelMiAz{zgXxh0=;GZx4gUS{=ZD_2=oy zhC5GV7YPlsD^*Bh_ zI5?t5;fyx|m4lT`2pI1Ehx3i{!*yLGzY4nOgJLtD#BKNxTn=K4zcJ*3T;6h`L|b2H zuFFcyq|l&|u?JDRd_EbM2M!8va{t%nJJ>F|sttqtl9c@$rOP;GF^cKah$Jwf?nr-D zjorFSSeFT!wJ6V2x=CxxT65>Nn`&}uY<5e#-g7Yuj&6&q$40oyj1H2??SfXbowEA! z`vqa$nB{{RnlF(Qq8r>POE~xCwqQn?j}B*7WlQ>|0^x8fYlRM6|C1OQeen&d z;6rO9BVw}mP*TQjhK))350L~lB(L2EaXzNzxSd~_8j6GdZNUna^~AP_4m z{3l>`bAKXYshOrtYY=GS&61X64;){D4w zCWmQb#FsDFfM4m4n;Ou$)A|8JlwI8gSxy0N;jPKw`bGD#ro?_ztCkv!2!Et#F2pu0 zf}WVV^8ANq3Hp0K9{YJnq8(|_@=_LlaA{aw_gE?(85OxpV%zmAR#P)kz?XK9Yc)c* zVG+!;&rPx|9=VpbzKL6b%$}1EQN_euR2+WgZPdD&kMD6M7RL;G=BjcIezF6e7uH)Y zr!fbmfgTm86!bLz?$@Zbo>2eIO-%Aa@{qn$bki>=4tVyVs`858{KOd_SFq3u+z(J@ z$W1*tqnbLgH|KScPS}N6cl_MOHN^#$-ny%nF8FK61$&spTcx=3wkcCu`0AsnacEq|5NE+U@PoG(9_PTXD_D9HQ-EtlGv+_zbOrRG+0|OD@jrSL zt+;NZ^@;soV4+f7Rp(2v{?BdwU|+xwcCw5=9qkvXF}u!ktfiCwTMq;zUVsYuW}zI_ zXG|)W{c>fZxM24wRE3yMpt$2m>?&^`p{sCQz_%9(g_kji4 zuwHt4x^^wj>cRNaGF9<<&(U!mw#N4E>Rid9b*A*yj50jqe5{_TuXbTbGVR-oNmuO-R8c{=9#v>G-v+usooER{}7D){%RH z8EwL&AM$)*-YSfZvr%UfOhdc!TL;d;w!Hj^3+K%0mS5}?z-R3xISAXZ%Q0X~lV0^3 zpL@JJG@%gO)H7}yL(2fkZhE(!zepCrIG%lLcEZ1qKk1APCXU|~?oHNMP=U?l&OzR$ zfP;6C8?wJER3+=Kr^ZiBQEy#cb2L&2I2@eOUMvkPg(mJB*tP!S!5UMoU70u3H#_5i z@8lf1H^~(Yy%&zPPHTK!qd|%L19ca8S>9vF>^FeP z5YA@oUP|kY%}dAytOrYS((k@&A63UXiARz14K%}${Z!5kGB+a_L)A=%9r6q>a>B-DLe zN8avh`U#PdSbpPfj_DpU3albw#0i1Geb`LI-N!04OL@XS6t4{Kb{uF0q@no^fBd1U z^pv+kX>T=13Vx$`#_}5LxjU=f)5@s0-55g!dhZ)>$9uTu8S8fD&zI&q9N?gw$aq}V zf@2f#IE77JNW)}S+uF8P(nf=WPz7a$h6|P0g}qt}@YZdR$MOhigVHh`Z-siq6qqj_ zM;gMu&P>Y_zTpuPyDZ$_cUbDmdNaPN+W0VRfMQXn?7@4dR;`0joVa{8Pdy8TSWrW~ zp@W+}1DzK#6Fy;E4aCZ7SMgj*VzE)=j;7UTMtY7c!%`G6tvkYp%HCNSX61itzE&Fl zu)T8LiA0IZ6DkFrk@<{nh13F@1)1U9_$k!yw%R)3PN319Q32v!Nwc$QJJZ>9e%kPK z_C$v|l$E(!wK7JpKfN@-Sr2I>#Z9uARc1}@P_m8TcF4=qz}CFXPg3Y2I7D)r+AM!4 zHdEhp2OL{F;0n*q%#3OJP2UwnVitf|vyRkV!d(u|*>5l($~H_LVdl3nw?IyV4GGBG z#M-s+p~-+it+HO<(n+OxLYvU=wQp&{HDp3K-Uj2phzko>V z)y4*!g&OkT_c^0gZ8V&H4rg5OKdfV+2YHz&m)PZVh1*w|V^(Rj@whS*Vf2PF zMSJrJX^=|`)5b}EczZ0FF|IyZ&f;cEf0FnBN$oW>fxPqp(hnZf6>i_08naHLk7Jij zgt6dPQMg;t7;j1AaEs@w(%EBtQHOQQ{OPoK{^^F&zCk;GiTV~uz#aLdm7gb=wfS!t zdKC``>?OwaZ+eT>8C=aEPl@?UAyEs(G=yPserjTn4QN;Y0)s#*sR6#2 zup?G|#l=naXBLkQtJaw6p;aw@VUf}ED?wrn8Vpywx)rjVCa=r3iH$Gwmlj`V1rVg2 zPFJ)Bl)Tb}Ycyd=AVSF|Henef>M{e{R$oBYuE|vpC*DZ9y6~vS!f7ly0B62bS1PNi zP2no550JZ8(8+O(gsl+G-vGcjYi=j}rrMx{#|16_qj%}7_)1m=n#FZnErgvm=l?cm zQ?3iS$&jbNv2m0HXLRTaZoN)v83W%?##YO{A;{V}JCdJ6K^8SL^x-fpUHj9qV? zMxv*_ODI$`yx|ncGS$6>dJjmrW-pKIM!rMS7wmevAAw;f*myAdeAK9i8C6^6EJy%< zog#NUFT)5Q9tcTQjVkl61~RmC61ulT*5rrez@hIGdfSaR=NC;;HXHdi6}Q(ODnR6? zXd#f;*ZFtq=D<^_K$Z4KPkwKxcvIq z%H%&-Lgc9hS1jcJZuAj||HJ4b4FAU?x!aRp{~t=^W@5`9NB!~F G-~S)VTE(vb literal 0 HcmV?d00001 diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png new file mode 100644 index 0000000000000000000000000000000000000000..df3e47d650fec318d6d57074ecb71f73c52a79a8 GIT binary patch literal 13069 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 Mqas}+X&n6j0PY6L>;M1& literal 0 HcmV?d00001 diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png new file mode 100644 index 0000000000000000000000000000000000000000..3ce116a6e8cc694c750b83c9e56fa6035ac02cb7 GIT binary patch literal 580 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 literal 0 HcmV?d00001 diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png new file mode 100644 index 0000000000000000000000000000000000000000..0a519d752653d4387d247130944024b97c7f2491 GIT binary patch literal 4321 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 P00000NkvXXu0mjfawP(Y literal 0 HcmV?d00001 diff --git a/macos/Runner/Base.lproj/MainMenu.xib b/macos/Runner/Base.lproj/MainMenu.xib new file mode 100644 index 0000000..80e867a --- /dev/null +++ b/macos/Runner/Base.lproj/MainMenu.xib @@ -0,0 +1,343 @@ + + + + + + + + + + + + + + + + + + + + + + +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/macos/Runner/Configs/AppInfo.xcconfig b/macos/Runner/Configs/AppInfo.xcconfig new file mode 100644 index 0000000..6c689af --- /dev/null +++ b/macos/Runner/Configs/AppInfo.xcconfig @@ -0,0 +1,14 @@ +// Application-level settings for the Runner target. +// +// This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the +// future. If not, the values below would default to using the project name when this becomes a +// 'flutter create' template. + +// The application's name. By default this is also the title of the Flutter window. +PRODUCT_NAME = DLS + +// The application's bundle identifier +PRODUCT_BUNDLE_IDENTIFIER = com.kovaksoft.labApp + +// The copyright displayed in application information +PRODUCT_COPYRIGHT = Copyright © 2026 com.kovaksoft. All rights reserved. diff --git a/macos/Runner/Configs/Debug.xcconfig b/macos/Runner/Configs/Debug.xcconfig new file mode 100644 index 0000000..36b0fd9 --- /dev/null +++ b/macos/Runner/Configs/Debug.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Debug.xcconfig" +#include "Warnings.xcconfig" diff --git a/macos/Runner/Configs/Release.xcconfig b/macos/Runner/Configs/Release.xcconfig new file mode 100644 index 0000000..dff4f49 --- /dev/null +++ b/macos/Runner/Configs/Release.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Release.xcconfig" +#include "Warnings.xcconfig" diff --git a/macos/Runner/Configs/Warnings.xcconfig b/macos/Runner/Configs/Warnings.xcconfig new file mode 100644 index 0000000..42bcbf4 --- /dev/null +++ b/macos/Runner/Configs/Warnings.xcconfig @@ -0,0 +1,13 @@ +WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings +GCC_WARN_UNDECLARED_SELECTOR = YES +CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES +CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE +CLANG_WARN__DUPLICATE_METHOD_MATCH = YES +CLANG_WARN_PRAGMA_PACK = YES +CLANG_WARN_STRICT_PROTOTYPES = YES +CLANG_WARN_COMMA = YES +GCC_WARN_STRICT_SELECTOR_MATCH = YES +CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES +CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES +GCC_WARN_SHADOW = YES +CLANG_WARN_UNREACHABLE_CODE = YES diff --git a/macos/Runner/DebugProfile.entitlements b/macos/Runner/DebugProfile.entitlements new file mode 100644 index 0000000..cff5a4b --- /dev/null +++ b/macos/Runner/DebugProfile.entitlements @@ -0,0 +1,16 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.cs.allow-jit + + com.apple.security.network.server + + com.apple.security.network.client + + com.apple.security.files.user-selected.read-write + + + diff --git a/macos/Runner/Info.plist b/macos/Runner/Info.plist new file mode 100644 index 0000000..4789daa --- /dev/null +++ b/macos/Runner/Info.plist @@ -0,0 +1,32 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIconFile + + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSMinimumSystemVersion + $(MACOSX_DEPLOYMENT_TARGET) + NSHumanReadableCopyright + $(PRODUCT_COPYRIGHT) + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + diff --git a/macos/Runner/MainFlutterWindow.swift b/macos/Runner/MainFlutterWindow.swift new file mode 100644 index 0000000..a650a57 --- /dev/null +++ b/macos/Runner/MainFlutterWindow.swift @@ -0,0 +1,29 @@ +import Cocoa +import FlutterMacOS + +class MainFlutterWindow: NSWindow { + override func awakeFromNib() { + let flutterViewController = FlutterViewController() + + // Center window with a sensible default size + let screenSize = NSScreen.main?.frame.size ?? CGSize(width: 1440, height: 900) + let windowWidth: CGFloat = min(1280, screenSize.width * 0.85) + let windowHeight: CGFloat = min(820, screenSize.height * 0.85) + let origin = CGPoint( + x: (screenSize.width - windowWidth) / 2, + y: (screenSize.height - windowHeight) / 2 + ) + let windowFrame = NSRect(origin: origin, size: CGSize(width: windowWidth, height: windowHeight)) + + self.contentViewController = flutterViewController + self.setFrame(windowFrame, display: true) + + // Keep sidebar always visible — desktop layout kicks in at 720px + self.minSize = CGSize(width: 880, height: 560) + self.titlebarAppearsTransparent = true + + RegisterGeneratedPlugins(registry: flutterViewController) + + super.awakeFromNib() + } +} diff --git a/macos/Runner/Release.entitlements b/macos/Runner/Release.entitlements new file mode 100644 index 0000000..afcec2c --- /dev/null +++ b/macos/Runner/Release.entitlements @@ -0,0 +1,14 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.network.client + + com.apple.security.files.user-selected.read-write + + aps-environment + production + + diff --git a/macos/RunnerTests/RunnerTests.swift b/macos/RunnerTests/RunnerTests.swift new file mode 100644 index 0000000..61f3bd1 --- /dev/null +++ b/macos/RunnerTests/RunnerTests.swift @@ -0,0 +1,12 @@ +import Cocoa +import FlutterMacOS +import XCTest + +class RunnerTests: XCTestCase { + + func testExample() { + // If you add code to the Runner application, consider adding tests here. + // See https://developer.apple.com/documentation/xctest for more information about using XCTest. + } + +} diff --git a/maestro/flows/00_login_clinic.yaml b/maestro/flows/00_login_clinic.yaml new file mode 100644 index 0000000..87bc258 --- /dev/null +++ b/maestro/flows/00_login_clinic.yaml @@ -0,0 +1,11 @@ +appId: com.kovaksoft.labApp +--- +- launchApp: + clearState: true +- assertVisible: "Tekrar hoş geldiniz" +- tapOn: "E-posta adresi" +- inputText: "egecankomur@icloud.com" +- tapOn: "Şifre" +- inputText: "Oyuncu21" +- tapOn: "Giriş Yap" +- assertVisible: "Bugünkü Durum" diff --git a/maestro/flows/00_login_lab.yaml b/maestro/flows/00_login_lab.yaml new file mode 100644 index 0000000..10dc834 --- /dev/null +++ b/maestro/flows/00_login_lab.yaml @@ -0,0 +1,11 @@ +appId: com.kovaksoft.labApp +--- +- launchApp: + clearState: true +- assertVisible: "Tekrar hoş geldiniz" +- tapOn: "E-posta adresi" +- inputText: "egecankomur@gmail.com" +- tapOn: "Şifre" +- inputText: "Oyuncu21" +- tapOn: "Giriş Yap" +- assertVisible: "Bugünkü Durum" diff --git a/maestro/flows/00_logout.yaml b/maestro/flows/00_logout.yaml new file mode 100644 index 0000000..1da7760 --- /dev/null +++ b/maestro/flows/00_logout.yaml @@ -0,0 +1,14 @@ +appId: com.kovaksoft.labApp +--- +# "Ayarlar" tab — koordinat bazlı (NavBar text Maestro XCTest'e görünmüyor) +- tapOn: + point: "90%,94%" +- scrollUntilVisible: + element: + text: "Çıkış Yap" + direction: DOWN +- tapOn: "Çıkış Yap" +- tapOn: + text: "Çıkış Yap" + index: 1 +- assertVisible: "Tekrar hoş geldiniz" diff --git a/maestro/flows/01_lab_send_prova.yaml b/maestro/flows/01_lab_send_prova.yaml new file mode 100644 index 0000000..406ca1c --- /dev/null +++ b/maestro/flows/01_lab_send_prova.yaml @@ -0,0 +1,28 @@ +appId: com.kovaksoft.labApp +--- +# ADIM 1: Lab → ust_yapi_prova adımını klinik provaya gönder +- runFlow: "00_login_lab.yaml" + +# Job card dashboard'da görünüyor — direkt tıkla +- tapOn: "PR-20260607-5YPP" +- assertVisible: "İş Detayı" + +# "Prova için Kliniğe Gönder" butonunu bul ve tıkla (detay ekranındaki) +- scrollUntilVisible: + element: + text: "Prova için Kliniğe Gönder" + direction: DOWN +- tapOn: "Prova için Kliniğe Gönder" + +# Sheet açıldı — başlık görünüyor +- assertVisible: "Üst Yapı Prova için Kliniğe Gönder" +- tapOn: "Not (isteğe bağlı)" +- inputText: "Bisküvi prova için gönderildi" + +# Sheet'teki submit butonu — title ile aynı metin, index:1 kullan +- tapOn: + text: "Üst Yapı Prova için Kliniğe Gönder" + index: 1 + +# Başarı — sheet kapandı, "Prova için Kliniğe Gönder" butonu artık yok (location=at_clinic) +- assertNotVisible: "Prova için Kliniğe Gönder" diff --git a/maestro/flows/02_clinic_approve_prova.yaml b/maestro/flows/02_clinic_approve_prova.yaml new file mode 100644 index 0000000..4ec40f8 --- /dev/null +++ b/maestro/flows/02_clinic_approve_prova.yaml @@ -0,0 +1,20 @@ +appId: com.kovaksoft.labApp +--- +# ADIM 2: Klinik → ust_yapi_prova onayı ver +# Başlangıç durumu: at_clinic, in_progress + +- runFlow: "00_login_clinic.yaml" + +# Job "Son İşler" dashboard'unda görünür — direkt tıkla +- tapOn: "PR-20260607-5YPP" +- assertVisible: "İş Detayı" + +# Prova onay butonu görünmeli +- scrollUntilVisible: + element: + text: "Onayla" + direction: DOWN +- tapOn: "Onayla" + +# Başarı — prova onaylandı, "Onayla" butonu artık yok +- assertNotVisible: "Onayla" diff --git a/maestro/flows/03_lab_send_final.yaml b/maestro/flows/03_lab_send_final.yaml new file mode 100644 index 0000000..6372810 --- /dev/null +++ b/maestro/flows/03_lab_send_final.yaml @@ -0,0 +1,30 @@ +appId: com.kovaksoft.labApp +--- +# ADIM 3: Lab → cila_bitim adımı, son gönderim +# Başlangıç durumu: cila_bitim, at_lab, in_progress + +- runFlow: "00_login_lab.yaml" + +# Job dashboard "Devam Eden İşler"'de görünür +- tapOn: "PR-20260607-5YPP" +- assertVisible: "İş Detayı" + +# Son Prova - Teslime Gönder butonu görünmeli +- scrollUntilVisible: + element: + text: "Son Prova - Teslime Gönder" + direction: DOWN +- tapOn: "Son Prova - Teslime Gönder" + +# Sheet: son gönderim notu +- assertVisible: "Son Prova · Teslime Gönder" +- tapOn: "Not (isteğe bağlı)" +- inputText: "Son işlem tamamlandı, teslim için gönderiliyor" + +# Sheet'teki submit butonu (başlık ile aynı metin, index:1) +- tapOn: + text: "Son Prova · Teslime Gönder" + index: 1 + +# İş artık "Gönderildi" — gönder butonu artık yok +- assertNotVisible: "Son Prova - Teslime Gönder" diff --git a/maestro/flows/04_clinic_deliver.yaml b/maestro/flows/04_clinic_deliver.yaml new file mode 100644 index 0000000..9825eea --- /dev/null +++ b/maestro/flows/04_clinic_deliver.yaml @@ -0,0 +1,30 @@ +appId: com.kovaksoft.labApp +--- +# ADIM 4: Klinik → "Teslim Aldım" işareti +# Başlangıç durumu: status=sent, at_clinic + +- runFlow: "00_login_clinic.yaml" + +# Job "Son İşler"'de görünür — direkt tıkla +- tapOn: "PR-20260607-5YPP" +- assertVisible: "İş Detayı" + +# Teslim Al butonu +- scrollUntilVisible: + element: + text: "Teslim Aldım" + direction: DOWN +- tapOn: "Teslim Aldım" + +# Dialog +- assertVisible: "Teslim Alındı" +- tapOn: "Teslimat notu (isteğe bağlı)" +- inputText: "Teslim alındı, hasta bilgilendirildi" + +# Dialog'daki "Teslim Alındı" butonuna bas (index:1 — başlık ile aynı) +- tapOn: + text: "Teslim Alındı" + index: 1 + +# Başarı — "Teslim Aldım" butonu artık yok +- assertNotVisible: "Teslim Aldım" diff --git a/maestro/flows/05_new_job_no_black_screen.yaml b/maestro/flows/05_new_job_no_black_screen.yaml new file mode 100644 index 0000000..5e47a19 --- /dev/null +++ b/maestro/flows/05_new_job_no_black_screen.yaml @@ -0,0 +1,38 @@ +appId: com.kovaksoft.labApp +--- +# ADIM 5: Klinik → Yeni iş oluştur, siyah ekran olmamalı + +- runFlow: "00_login_clinic.yaml" + +# "İşler" tab — koordinat bazlı +- tapOn: + point: "30%,94%" + +# Yeni iş butonu (FAB veya AppBar action) +- tapOn: + description: "Yeni İş" +- assertVisible: "Yeni İş" + +# Lab seç +- scrollUntilVisible: + element: + text: "IO Lab" + direction: DOWN +- tapOn: "IO Lab" + +# Protez türü seç +- tapOn: "Protez Türü" +- tapOn: "Zirkonyum" + +# Diş seç — "Üst Çene" kısa yolu +- tapOn: "Üst Çene" + +# Kaydet +- scrollUntilVisible: + element: + text: "Kaydet" + direction: DOWN +- tapOn: "Kaydet" + +# Siyah ekran olmamalı — iş detay ekranı açılmalı +- assertVisible: "İş Detayı" diff --git a/maestro/login_flow.yaml b/maestro/login_flow.yaml new file mode 100644 index 0000000..38f75db --- /dev/null +++ b/maestro/login_flow.yaml @@ -0,0 +1,10 @@ +appId: com.kovaksoft.labApp +--- +- launchApp +- assertVisible: "Hoş geldiniz" +- tapOn: "E-posta" +- inputText: "test@test.com" +- tapOn: "Şifre" +- inputText: "test123" +- hideKeyboard +- tapOn: "Giriş Yap" diff --git a/pb_hooks/jobs_notifications.pb.js b/pb_hooks/jobs_notifications.pb.js new file mode 100644 index 0000000..931e1fd --- /dev/null +++ b/pb_hooks/jobs_notifications.pb.js @@ -0,0 +1,129 @@ +/// @ts-check + +onRecordAfterCreateSuccess((e) => { + const APP_ID = "524cb6d8-2640-4f85-bb24-c9c762233de7"; + const API_KEY = "os_v2_app_kjglnwbgibhylozezhdweiz546iv5ns5h2hujfvrsao64p7qkqk6lwqthgwirm6naxdy37tmd5nivppfqmxrnqreoewpgqkcnawjjpy"; + try { + const rec = e.record; + if (rec.getString("status") !== "pending") return; + const labTenantId = rec.getString("lab_tenant_id"); + const code = rec.getString("patient_code") || "Is"; + const type = rec.getString("prosthetic_type") || ""; + const label = type ? code + " - " + type : code; + const jobId = rec.getString("id"); + let userIds = []; + try { + const members = $app.findRecordsByFilter("tenant_members", 'tenant_id = "' + labTenantId + '"', "", 100, 0); + userIds = members.map(function(r) { return r.getString("user_id"); }).filter(Boolean); + } catch (_) {} + if (!userIds.length) return; + const res = $http.send({ + url: "https://onesignal.com/api/v1/notifications", + method: "POST", + headers: { "Content-Type": "application/json", "Authorization": "Basic " + API_KEY }, + body: JSON.stringify({ app_id: APP_ID, include_external_user_ids: userIds, channel_for_external_user_ids: "push", + headings: { en: "Yeni Is Talebi", tr: "Yeni Is Talebi" }, + contents: { en: label + " icin yeni bir is talebi geldi.", tr: label + " icin yeni bir is talebi geldi." }, + data: { job_id: jobId, tenant_type: "lab" } }), + timeout: 10, + }); + console.log("[notif-create] status=" + res.statusCode + " users=" + userIds.length); + } catch (err) { + console.log("[notif-create] error: " + String(err)); + } +}, "jobs"); + +onRecordAfterUpdateSuccess((e) => { + const APP_ID = "524cb6d8-2640-4f85-bb24-c9c762233de7"; + const API_KEY = "os_v2_app_kjglnwbgibhylozezhdweiz546iv5ns5h2hujfvrsao64p7qkqk6lwqthgwirm6naxdy37tmd5nivppfqmxrnqreoewpgqkcnawjjpy"; + try { + const rec = e.record; + const newStatus = rec.getString("status"); + const newLocation = rec.getString("location"); + const clinicTenantId = rec.getString("clinic_tenant_id"); + const labTenantId = rec.getString("lab_tenant_id"); + const code = rec.getString("patient_code") || "Is"; + const type = rec.getString("prosthetic_type") || ""; + const label = type ? code + " - " + type : code; + const jobId = rec.getString("id"); + let oldStatus = ""; + let oldLocation = ""; + try { + const orig = rec.original(); + if (orig) { + oldStatus = orig.getString("status"); + oldLocation = orig.getString("location"); + } + } catch (_) {} + + if (oldStatus && oldStatus === newStatus && oldLocation === newLocation) return; + + let targetTenantId = ""; + let title = ""; + let body = ""; + let tenantType = "clinic"; + + if (oldStatus === "pending" && newStatus === "in_progress" && newLocation === "at_lab") { + targetTenantId = clinicTenantId; title = "Is Kabul Edildi"; + body = label + " laboratuvar tarafindan kabul edildi."; tenantType = "clinic"; + } else if (newStatus === "in_progress" && newLocation === "at_clinic" && oldLocation !== "at_clinic") { + targetTenantId = clinicTenantId; title = "Prova Onayi Bekleniyor"; + body = label + " prova icin klinik onayini bekliyor."; tenantType = "clinic"; + } else if (oldLocation === "at_clinic" && newLocation === "at_lab" && newStatus === "in_progress") { + targetTenantId = labTenantId; title = "Klinik Geri Bildirimi"; + body = label + " icin klinikten geri bildirim geldi."; tenantType = "lab"; + } else if (newStatus === "sent" && oldStatus !== "sent") { + targetTenantId = clinicTenantId; title = "Teslimat Hazir"; + body = label + " teslim icin hazir."; tenantType = "clinic"; + } else if (newStatus === "delivered" && oldStatus !== "delivered") { + targetTenantId = labTenantId; title = "Is Teslim Alindi"; + body = label + " klinik tarafindan teslim alindi."; tenantType = "lab"; + } else if (newStatus === "cancelled" && oldStatus !== "cancelled") { + let allIds = []; + try { + const cm = $app.findRecordsByFilter("tenant_members", 'tenant_id = "' + clinicTenantId + '"', "", 100, 0); + const lm = $app.findRecordsByFilter("tenant_members", 'tenant_id = "' + labTenantId + '"', "", 100, 0); + const raw = cm.concat(lm).map(function(r) { return r.getString("user_id"); }).filter(Boolean); + allIds = raw.filter(function(id, idx) { return raw.indexOf(id) === idx; }); + } catch (_) {} + if (!allIds.length) return; + const res = $http.send({ + url: "https://onesignal.com/api/v1/notifications", + method: "POST", + headers: { "Content-Type": "application/json", "Authorization": "Basic " + API_KEY }, + body: JSON.stringify({ app_id: APP_ID, include_external_user_ids: allIds, channel_for_external_user_ids: "push", + headings: { en: "Is Iptal Edildi", tr: "Is Iptal Edildi" }, + contents: { en: label + " iptal edildi.", tr: label + " iptal edildi." }, + data: { job_id: jobId, tenant_type: "clinic" } }), + timeout: 10, + }); + console.log("[notif-update] cancelled status=" + res.statusCode + " users=" + allIds.length); + return; + } else { + return; + } + + let userIds = []; + try { + const members = $app.findRecordsByFilter("tenant_members", 'tenant_id = "' + targetTenantId + '"', "", 100, 0); + userIds = members.map(function(r) { return r.getString("user_id"); }).filter(Boolean); + } catch (_) {} + + if (!userIds.length) return; + + const res = $http.send({ + url: "https://onesignal.com/api/v1/notifications", + method: "POST", + headers: { "Content-Type": "application/json", "Authorization": "Basic " + API_KEY }, + body: JSON.stringify({ app_id: APP_ID, include_external_user_ids: userIds, channel_for_external_user_ids: "push", + headings: { en: title, tr: title }, + contents: { en: body, tr: body }, + data: { job_id: jobId, tenant_type: tenantType } }), + timeout: 10, + }); + console.log("[notif-update] " + title + " status=" + res.statusCode + " users=" + userIds.length); + + } catch (err) { + console.log("[notif-update] error: " + String(err)); + } +}, "jobs"); diff --git a/pubspec.lock b/pubspec.lock new file mode 100644 index 0000000..4b0d620 --- /dev/null +++ b/pubspec.lock @@ -0,0 +1,1311 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + _fe_analyzer_shared: + dependency: transitive + description: + name: _fe_analyzer_shared + sha256: da0d9209ca76bde579f2da330aeb9df62b6319c834fa7baae052021b0462401f + url: "https://pub.dev" + source: hosted + version: "85.0.0" + analyzer: + dependency: transitive + description: + name: analyzer + sha256: f4ad0fea5f102201015c9aae9d93bc02f75dd9491529a8c21f88d17a8523d44c + url: "https://pub.dev" + source: hosted + version: "7.6.0" + analyzer_plugin: + dependency: transitive + description: + name: analyzer_plugin + sha256: a5ab7590c27b779f3d4de67f31c4109dbe13dd7339f86461a6f2a8ab2594d8ce + url: "https://pub.dev" + source: hosted + version: "0.13.4" + args: + dependency: transitive + description: + name: args + sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04 + url: "https://pub.dev" + source: hosted + version: "2.7.0" + async: + dependency: transitive + description: + name: async + sha256: e2eb0491ba5ddb6177742d2da23904574082139b07c1e33b8503b9f46f3e1a37 + url: "https://pub.dev" + source: hosted + version: "2.13.1" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + build: + dependency: transitive + description: + name: build + sha256: "51dc711996cbf609b90cbe5b335bbce83143875a9d58e4b5c6d3c4f684d3dda7" + url: "https://pub.dev" + source: hosted + version: "2.5.4" + build_config: + dependency: transitive + description: + name: build_config + sha256: "4ae2de3e1e67ea270081eaee972e1bd8f027d459f249e0f1186730784c2e7e33" + url: "https://pub.dev" + source: hosted + version: "1.1.2" + build_daemon: + dependency: transitive + description: + name: build_daemon + sha256: bf05f6e12cfea92d3c09308d7bcdab1906cd8a179b023269eed00c071004b957 + url: "https://pub.dev" + source: hosted + version: "4.1.1" + build_resolvers: + dependency: transitive + description: + name: build_resolvers + sha256: ee4257b3f20c0c90e72ed2b57ad637f694ccba48839a821e87db762548c22a62 + url: "https://pub.dev" + source: hosted + version: "2.5.4" + build_runner: + dependency: "direct dev" + description: + name: build_runner + sha256: "382a4d649addbfb7ba71a3631df0ec6a45d5ab9b098638144faf27f02778eb53" + url: "https://pub.dev" + source: hosted + version: "2.5.4" + build_runner_core: + dependency: transitive + description: + name: build_runner_core + sha256: "85fbbb1036d576d966332a3f5ce83f2ce66a40bea1a94ad2d5fc29a19a0d3792" + url: "https://pub.dev" + source: hosted + version: "9.1.2" + built_collection: + dependency: transitive + description: + name: built_collection + sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100" + url: "https://pub.dev" + source: hosted + version: "5.1.1" + built_value: + dependency: transitive + description: + name: built_value + sha256: "34e4067d30ce212937df995f03b69992eea683539ceeac7f679a1f1eba055b56" + url: "https://pub.dev" + source: hosted + version: "8.12.6" + cached_network_image: + dependency: "direct main" + description: + name: cached_network_image + sha256: "7c1183e361e5c8b0a0f21a28401eecdbde252441106a9816400dd4c2b2424916" + url: "https://pub.dev" + source: hosted + version: "3.4.1" + cached_network_image_platform_interface: + dependency: transitive + description: + name: cached_network_image_platform_interface + sha256: "35814b016e37fbdc91f7ae18c8caf49ba5c88501813f73ce8a07027a395e2829" + url: "https://pub.dev" + source: hosted + version: "4.1.1" + cached_network_image_web: + dependency: transitive + description: + name: cached_network_image_web + sha256: "980842f4e8e2535b8dbd3d5ca0b1f0ba66bf61d14cc3a17a9b4788a3685ba062" + url: "https://pub.dev" + source: hosted + version: "1.3.1" + characters: + dependency: transitive + description: + name: characters + sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 + 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: + name: checked_yaml + sha256: "959525d3162f249993882720d52b7e0c833978df229be20702b33d48d91de70f" + url: "https://pub.dev" + source: hosted + version: "2.0.4" + cli_util: + dependency: transitive + description: + name: cli_util + sha256: ff6785f7e9e3c38ac98b2fb035701789de90154024a75b6cb926445e83197d1c + url: "https://pub.dev" + source: hosted + version: "0.4.2" + clock: + dependency: transitive + description: + name: clock + sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b + url: "https://pub.dev" + source: hosted + version: "1.1.2" + code_assets: + dependency: transitive + description: + name: code_assets + sha256: bf394f466ba9205f1812a0433b392d6af280f155f56651eda7c18cc32ed493b8 + url: "https://pub.dev" + source: hosted + version: "1.2.1" + code_builder: + dependency: transitive + description: + name: code_builder + sha256: "6a6cab2ba4680d6423f34a9b972a4c9a94ebe1b62ecec4e1a1f2cba91fd1319d" + url: "https://pub.dev" + source: hosted + version: "4.11.1" + collection: + dependency: transitive + description: + name: collection + sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" + url: "https://pub.dev" + source: hosted + version: "1.19.1" + convert: + dependency: transitive + description: + name: convert + sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68 + url: "https://pub.dev" + source: hosted + version: "3.1.2" + country_flags: + dependency: transitive + description: + name: country_flags + sha256: f022d18337f3861f1f4e319b936cb53920de9259f38cb09e169eace9942e2b79 + url: "https://pub.dev" + source: hosted + version: "4.1.2" + cross_file: + dependency: transitive + description: + name: cross_file + sha256: "28bb3ae56f117b5aec029d702a90f57d285cd975c3c5c281eaca38dbc47c5937" + url: "https://pub.dev" + source: hosted + version: "0.3.5+2" + crypto: + dependency: transitive + description: + name: crypto + sha256: c8ea0233063ba03258fbcf2ca4d6dadfefe14f02fab57702265467a19f27fadf + url: "https://pub.dev" + source: hosted + version: "3.0.7" + custom_lint_core: + dependency: transitive + description: + name: custom_lint_core + sha256: "31110af3dde9d29fb10828ca33f1dce24d2798477b167675543ce3d208dee8be" + url: "https://pub.dev" + source: hosted + version: "0.7.5" + custom_lint_visitor: + dependency: transitive + description: + name: custom_lint_visitor + sha256: "4a86a0d8415a91fbb8298d6ef03e9034dc8e323a599ddc4120a0e36c433983a2" + url: "https://pub.dev" + source: hosted + version: "1.0.0+7.7.0" + dart_style: + dependency: transitive + description: + name: dart_style + sha256: "8a0e5fba27e8ee025d2ffb4ee820b4e6e2cf5e4246a6b1a477eb66866947e0bb" + url: "https://pub.dev" + source: hosted + version: "3.1.1" + data_widget: + dependency: transitive + description: + name: data_widget + sha256: "4947aae3c50635496d56f94ad18de98e19015c5ebf01abee0f39a2c098c7021a" + url: "https://pub.dev" + source: hosted + version: "0.0.3" + drift: + dependency: transitive + description: + name: drift + sha256: "540cf382a3bfa99b76e51514db5b0ebcd81ce3679b7c1c9cb9478ff3735e47a1" + url: "https://pub.dev" + source: hosted + version: "2.28.2" + drift_dev: + dependency: "direct dev" + description: + name: drift_dev + sha256: "68c138e884527d2bd61df2ade276c3a144df84d1adeb0ab8f3196b5afe021bd4" + url: "https://pub.dev" + source: hosted + version: "2.28.0" + email_validator: + dependency: transitive + description: + name: email_validator + sha256: b19aa5d92fdd76fbc65112060c94d45ba855105a28bb6e462de7ff03b12fa1fb + url: "https://pub.dev" + source: hosted + version: "3.0.0" + equatable: + dependency: "direct main" + description: + name: equatable + sha256: "3e0141505477fd8ad55d6eb4e7776d3fe8430be8e497ccb1521370c3f21a3e2b" + url: "https://pub.dev" + source: hosted + version: "2.0.8" + expressions: + dependency: transitive + description: + name: expressions + sha256: f3b0e99563a9a1bde1138e728eb722f292cc7d2aec55d28136c49b1a370306c5 + url: "https://pub.dev" + source: hosted + version: "0.2.5+3" + fake_async: + dependency: transitive + description: + name: fake_async + sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" + url: "https://pub.dev" + source: hosted + version: "1.3.3" + ffi: + dependency: transitive + description: + name: ffi + sha256: "6d7fd89431262d8f3125e81b50d3847a091d846eafcd4fdb88dd06f36d705a45" + url: "https://pub.dev" + source: hosted + version: "2.2.0" + file: + dependency: transitive + description: + name: file + sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 + url: "https://pub.dev" + source: hosted + version: "7.0.1" + file_picker: + dependency: "direct main" + description: + name: file_picker + sha256: ab13ae8ef5580a411c458d6207b6774a6c237d77ac37011b13994879f68a8810 + url: "https://pub.dev" + source: hosted + version: "8.3.7" + fixnum: + dependency: transitive + description: + name: fixnum + sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be + url: "https://pub.dev" + source: hosted + version: "1.1.1" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_animate: + dependency: "direct main" + description: + name: flutter_animate + sha256: "7befe2d3252728afb77aecaaea1dec88a89d35b9b1d2eea6d04479e8af9117b5" + url: "https://pub.dev" + source: hosted + version: "4.5.2" + flutter_cache_manager: + dependency: transitive + description: + name: flutter_cache_manager + sha256: "400b6592f16a4409a7f2bb929a9a7e38c72cceb8ffb99ee57bbf2cb2cecf8386" + url: "https://pub.dev" + source: hosted + version: "3.4.1" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + sha256: "3f41d009ba7172d5ff9be5f6e6e6abb4300e263aab8866d2a0842ed2a70f8f0c" + url: "https://pub.dev" + source: hosted + version: "4.0.0" + flutter_localizations: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_plugin_android_lifecycle: + dependency: transitive + description: + name: flutter_plugin_android_lifecycle + sha256: "3854fe5e3bff0b113c658f260b90c95dea17c92db0f2addeac2e343dd9969785" + url: "https://pub.dev" + source: hosted + version: "2.0.35" + flutter_riverpod: + dependency: "direct main" + description: + name: flutter_riverpod + sha256: "9532ee6db4a943a1ed8383072a2e3eeda041db5657cdf6d2acecf3c21ecbe7e1" + url: "https://pub.dev" + source: hosted + version: "2.6.1" + flutter_shaders: + dependency: transitive + description: + name: flutter_shaders + sha256: "34794acadd8275d971e02df03afee3dee0f98dbfb8c4837082ad0034f612a3e2" + url: "https://pub.dev" + source: hosted + version: "0.1.3" + flutter_svg: + dependency: "direct main" + description: + name: flutter_svg + sha256: "35882981abcbfb8c15b286f0cd690ff25bac12d95eff3e25ee207f37d4c42e7f" + url: "https://pub.dev" + source: hosted + version: "2.3.0" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + freezed: + dependency: "direct dev" + description: + name: freezed + sha256: "59a584c24b3acdc5250bb856d0d3e9c0b798ed14a4af1ddb7dc1c7b41df91c9c" + url: "https://pub.dev" + source: hosted + version: "2.5.8" + freezed_annotation: + dependency: "direct main" + description: + name: freezed_annotation + sha256: c2e2d632dd9b8a2b7751117abcfc2b4888ecfe181bd9fca7170d9ef02e595fe2 + url: "https://pub.dev" + source: hosted + version: "2.4.4" + frontend_server_client: + dependency: transitive + description: + name: frontend_server_client + sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694 + url: "https://pub.dev" + source: hosted + version: "4.0.0" + gap: + dependency: transitive + description: + name: gap + sha256: f19387d4e32f849394758b91377f9153a1b41d79513ef7668c088c77dbc6955d + url: "https://pub.dev" + source: hosted + version: "3.0.1" + glob: + dependency: transitive + description: + name: glob + sha256: c3f1ee72c96f8f78935e18aa8cecced9ab132419e8625dc187e1c2408efc20de + url: "https://pub.dev" + source: hosted + version: "2.1.3" + go_router: + dependency: "direct main" + description: + name: go_router + sha256: f02fd7d2a4dc512fec615529824fdd217fecb3a3d3de68360293a551f21634b3 + url: "https://pub.dev" + source: hosted + version: "14.8.1" + google_fonts: + dependency: "direct main" + description: + name: google_fonts + sha256: ba03d03bcaa2f6cb7bd920e3b5027181db75ab524f8891c8bc3aa603885b8055 + url: "https://pub.dev" + source: hosted + version: "6.3.3" + graphs: + dependency: transitive + description: + name: graphs + sha256: "741bbf84165310a68ff28fe9e727332eef1407342fca52759cb21ad8177bb8d0" + url: "https://pub.dev" + source: hosted + version: "2.3.2" + hooks: + dependency: transitive + description: + name: hooks + sha256: "9a62a50b50b769a737bc0a8ff381f333529df3ab746b2f6b02e83760231455ba" + url: "https://pub.dev" + source: hosted + version: "2.0.2" + http: + dependency: "direct main" + description: + name: http + sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412" + url: "https://pub.dev" + source: hosted + version: "1.6.0" + http_multi_server: + dependency: transitive + description: + name: http_multi_server + sha256: aa6199f908078bb1c5efb8d8638d4ae191aac11b311132c3ef48ce352fb52ef8 + url: "https://pub.dev" + source: hosted + version: "3.2.2" + http_parser: + dependency: transitive + description: + name: http_parser + sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571" + url: "https://pub.dev" + source: hosted + version: "4.1.2" + intl: + dependency: "direct main" + description: + name: intl + sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5" + url: "https://pub.dev" + source: hosted + version: "0.20.2" + io: + dependency: transitive + description: + name: io + sha256: dfd5a80599cf0165756e3181807ed3e77daf6dd4137caaad72d0b7931597650b + url: "https://pub.dev" + source: hosted + version: "1.0.5" + jni: + dependency: transitive + description: + name: jni + sha256: c2230682d5bc2362c1c9e8d3c7f406d9cbba23ab3f2e203a025dd47e0fb2e68f + url: "https://pub.dev" + source: hosted + version: "1.0.0" + jni_flutter: + dependency: transitive + description: + name: jni_flutter + sha256: "8b59e590786050b1cd866677dddaf76b1ade5e7bc751abe04b86e84d379d3ba6" + url: "https://pub.dev" + source: hosted + version: "1.0.1" + jovial_misc: + dependency: transitive + description: + name: jovial_misc + sha256: "065b5240badae6b13472efdea28fffe8baf914a7831361469a95c6456d9b8dc8" + url: "https://pub.dev" + source: hosted + version: "0.10.0" + jovial_svg: + dependency: transitive + description: + name: jovial_svg + sha256: "99e9c3afcf7371ae38083ad52de23677d6d751f46150c3c6ae842e009e84d9f0" + url: "https://pub.dev" + source: hosted + version: "1.1.29" + js: + dependency: transitive + description: + name: js + sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 + url: "https://pub.dev" + source: hosted + version: "0.6.7" + json_annotation: + dependency: "direct main" + description: + name: json_annotation + sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" + url: "https://pub.dev" + source: hosted + version: "4.9.0" + json_serializable: + dependency: "direct dev" + description: + name: json_serializable + sha256: c50ef5fc083d5b5e12eef489503ba3bf5ccc899e487d691584699b4bdefeea8c + url: "https://pub.dev" + source: hosted + version: "6.9.5" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de" + url: "https://pub.dev" + source: hosted + version: "11.0.2" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1" + url: "https://pub.dev" + source: hosted + version: "3.0.10" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1" + url: "https://pub.dev" + source: hosted + version: "3.0.2" + lints: + dependency: transitive + description: + name: lints + sha256: "976c774dd944a42e83e2467f4cc670daef7eed6295b10b36ae8c85bcbf828235" + url: "https://pub.dev" + source: hosted + version: "4.0.0" + logging: + dependency: transitive + description: + name: logging + sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61 + url: "https://pub.dev" + source: hosted + version: "1.3.0" + matcher: + dependency: transitive + description: + name: matcher + sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 + url: "https://pub.dev" + source: hosted + version: "0.12.17" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec + url: "https://pub.dev" + source: hosted + version: "0.11.1" + meta: + dependency: transitive + description: + name: meta + sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394" + url: "https://pub.dev" + source: hosted + version: "1.17.0" + mime: + dependency: transitive + description: + name: mime + sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6" + url: "https://pub.dev" + source: hosted + version: "2.0.0" + objective_c: + dependency: transitive + description: + name: objective_c + sha256: "6cb691c686fa2838c6deb34980d426145c2a5d537491cb83d463c33cdbc726ed" + url: "https://pub.dev" + source: hosted + version: "9.4.1" + octo_image: + dependency: transitive + description: + name: octo_image + sha256: "34faa6639a78c7e3cbe79be6f9f96535867e879748ade7d17c9b1ae7536293bd" + url: "https://pub.dev" + source: hosted + version: "2.1.0" + onesignal_flutter: + dependency: "direct main" + description: + name: onesignal_flutter + sha256: "74521a8bfbe9253a5fdfdd52981e8bb4f6da2fbbb92792d4ff368917ac395716" + url: "https://pub.dev" + source: hosted + version: "5.5.8" + package_config: + dependency: transitive + description: + name: package_config + sha256: f096c55ebb7deb7e384101542bfba8c52696c1b56fca2eb62827989ef2353bbc + url: "https://pub.dev" + source: hosted + version: "2.2.0" + path: + dependency: transitive + description: + name: path + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" + url: "https://pub.dev" + source: hosted + version: "1.9.1" + path_parsing: + dependency: transitive + description: + name: path_parsing + sha256: "883402936929eac138ee0a45da5b0f2c80f89913e6dc3bf77eb65b84b409c6ca" + url: "https://pub.dev" + source: hosted + version: "1.1.0" + path_provider: + dependency: "direct main" + description: + name: path_provider + sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd" + url: "https://pub.dev" + source: hosted + version: "2.1.5" + path_provider_android: + dependency: transitive + description: + name: path_provider_android + sha256: "69cbd515a62b94d32a7944f086b2f82b4ac40a1d45bebfc00813a430ab2dabcd" + url: "https://pub.dev" + source: hosted + version: "2.3.1" + path_provider_foundation: + dependency: transitive + description: + name: path_provider_foundation + sha256: "2a376b7d6392d80cd3705782d2caa734ca4727776db0b6ec36ef3f1855197699" + url: "https://pub.dev" + source: hosted + version: "2.6.0" + path_provider_linux: + dependency: transitive + description: + name: path_provider_linux + sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 + url: "https://pub.dev" + source: hosted + version: "2.2.1" + path_provider_platform_interface: + dependency: transitive + description: + name: path_provider_platform_interface + sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + path_provider_windows: + dependency: transitive + description: + name: path_provider_windows + sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7 + url: "https://pub.dev" + source: hosted + version: "2.3.0" + petitparser: + dependency: transitive + description: + name: petitparser + sha256: "91bd59303e9f769f108f8df05e371341b15d59e995e6806aefab827b58336675" + url: "https://pub.dev" + source: hosted + version: "7.0.2" + phonecodes: + dependency: transitive + description: + name: phonecodes + sha256: d963c19d35914cd83620e64125689a0c09047e25046639f2a124142ccf5868bb + url: "https://pub.dev" + source: hosted + version: "0.0.4" + platform: + dependency: transitive + description: + name: platform + sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984" + url: "https://pub.dev" + source: hosted + version: "3.1.6" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" + url: "https://pub.dev" + source: hosted + version: "2.1.8" + pocketbase: + dependency: "direct main" + description: + name: pocketbase + sha256: f70353ea9c583fabe4165880e054504c5036c428f59c0fc588d64bb19218df05 + url: "https://pub.dev" + source: hosted + version: "0.22.0" + pool: + dependency: transitive + description: + name: pool + sha256: "978783255c543aa3586a1b3c21f6e9d720eb315376a915872c61ef8b5c20177d" + url: "https://pub.dev" + source: hosted + version: "1.5.2" + pub_semver: + dependency: transitive + description: + name: pub_semver + sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585" + url: "https://pub.dev" + source: hosted + version: "2.2.0" + pubspec_parse: + dependency: transitive + description: + name: pubspec_parse + sha256: "0560ba233314abbed0a48a2956f7f022cce7c3e1e73df540277da7544cad4082" + url: "https://pub.dev" + source: hosted + version: "1.5.0" + quiver: + dependency: transitive + description: + name: quiver + sha256: ea0b925899e64ecdfbf9c7becb60d5b50e706ade44a85b2363be2a22d88117d2 + url: "https://pub.dev" + source: hosted + version: "3.2.2" + recase: + dependency: transitive + description: + name: recase + sha256: e4eb4ec2dcdee52dcf99cb4ceabaffc631d7424ee55e56f280bc039737f89213 + url: "https://pub.dev" + source: hosted + version: "4.1.0" + record_use: + dependency: transitive + description: + name: record_use + sha256: "2551bd8eecfe95d14ae75f6021ad0248be5c27f138c2ec12fcb52b500b3ba1ed" + url: "https://pub.dev" + source: hosted + version: "0.6.0" + riverpod: + dependency: transitive + description: + name: riverpod + sha256: "59062512288d3056b2321804332a13ffdd1bf16df70dcc8e506e411280a72959" + url: "https://pub.dev" + source: hosted + version: "2.6.1" + riverpod_analyzer_utils: + dependency: transitive + description: + name: riverpod_analyzer_utils + sha256: "837a6dc33f490706c7f4632c516bcd10804ee4d9ccc8046124ca56388715fdf3" + url: "https://pub.dev" + source: hosted + version: "0.5.9" + riverpod_annotation: + dependency: "direct main" + description: + name: riverpod_annotation + sha256: e14b0bf45b71326654e2705d462f21b958f987087be850afd60578fcd502d1b8 + url: "https://pub.dev" + source: hosted + version: "2.6.1" + riverpod_generator: + dependency: "direct dev" + description: + name: riverpod_generator + sha256: "120d3310f687f43e7011bb213b90a436f1bbc300f0e4b251a72c39bccb017a4f" + url: "https://pub.dev" + source: hosted + version: "2.6.4" + rxdart: + dependency: transitive + description: + name: rxdart + sha256: "5c3004a4a8dbb94bd4bf5412a4def4acdaa12e12f269737a5751369e12d1a962" + url: "https://pub.dev" + source: hosted + version: "0.28.0" + shadcn_flutter: + dependency: "direct main" + description: + name: shadcn_flutter + sha256: "1fd4f798c39d6308dc8f7e94d9e870b5db39fbf417ea95c423c7555ce8227a1c" + url: "https://pub.dev" + source: hosted + version: "0.0.47" + share_plus: + dependency: "direct main" + description: + name: share_plus + sha256: fce43200aa03ea87b91ce4c3ac79f0cecd52e2a7a56c7a4185023c271fbfa6da + url: "https://pub.dev" + source: hosted + version: "10.1.4" + share_plus_platform_interface: + dependency: transitive + description: + name: share_plus_platform_interface + sha256: cc012a23fc2d479854e6c80150696c4a5f5bb62cb89af4de1c505cf78d0a5d0b + url: "https://pub.dev" + source: hosted + version: "5.0.2" + shared_preferences: + dependency: "direct main" + description: + name: shared_preferences + sha256: c3025c5534b01739267eb7d76959bbc25a6d10f6988e1c2a3036940133dd10bf + url: "https://pub.dev" + source: hosted + version: "2.5.5" + shared_preferences_android: + dependency: transitive + description: + name: shared_preferences_android + sha256: e8d4762b1e2e8578fc4d0fd548cebf24afd24f49719c08974df92834565e2c53 + url: "https://pub.dev" + source: hosted + version: "2.4.23" + shared_preferences_foundation: + dependency: transitive + description: + name: shared_preferences_foundation + sha256: "4e7eaffc2b17ba398759f1151415869a34771ba11ebbccd1b0145472a619a64f" + url: "https://pub.dev" + source: hosted + version: "2.5.6" + shared_preferences_linux: + dependency: transitive + description: + name: shared_preferences_linux + sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + shared_preferences_platform_interface: + dependency: transitive + description: + name: shared_preferences_platform_interface + sha256: "649dc798a33931919ea356c4305c2d1f81619ea6e92244070b520187b5140ef9" + url: "https://pub.dev" + source: hosted + version: "2.4.2" + shared_preferences_web: + dependency: transitive + description: + name: shared_preferences_web + sha256: c49bd060261c9a3f0ff445892695d6212ff603ef3115edbb448509d407600019 + url: "https://pub.dev" + source: hosted + version: "2.4.3" + shared_preferences_windows: + dependency: transitive + description: + name: shared_preferences_windows + sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + shelf: + dependency: transitive + description: + name: shelf + sha256: e7dd780a7ffb623c57850b33f43309312fc863fb6aa3d276a754bb299839ef12 + url: "https://pub.dev" + source: hosted + version: "1.4.2" + shelf_web_socket: + dependency: transitive + description: + name: shelf_web_socket + sha256: "3632775c8e90d6c9712f883e633716432a27758216dfb61bd86a8321c0580925" + url: "https://pub.dev" + source: hosted + version: "3.0.0" + shimmer: + dependency: "direct main" + description: + name: shimmer + sha256: "5f88c883a22e9f9f299e5ba0e4f7e6054857224976a5d9f839d4ebdc94a14ac9" + url: "https://pub.dev" + source: hosted + version: "3.0.0" + skeletonizer: + dependency: transitive + description: + name: skeletonizer + sha256: "9f38f9b47ec3cf2235a6a4f154a88a95432bc55ba98b3e2eb6ced5c1974bc122" + url: "https://pub.dev" + source: hosted + version: "2.1.3" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + source_gen: + dependency: transitive + description: + name: source_gen + sha256: "35c8150ece9e8c8d263337a265153c3329667640850b9304861faea59fc98f6b" + url: "https://pub.dev" + source: hosted + version: "2.0.0" + source_helper: + dependency: transitive + description: + name: source_helper + sha256: a447acb083d3a5ef17f983dd36201aeea33fedadb3228fa831f2f0c92f0f3aca + url: "https://pub.dev" + source: hosted + version: "1.3.7" + source_span: + dependency: transitive + description: + name: source_span + sha256: "56a02f1f4cd1a2d96303c0144c93bd6d909eea6bee6bf5a0e0b685edbd4c47ab" + url: "https://pub.dev" + source: hosted + version: "1.10.2" + sqflite: + dependency: transitive + description: + name: sqflite + sha256: "564cfed0746fe53140c23b70b308e045c3b31f17778f2f326ccb7d804ea0250a" + url: "https://pub.dev" + source: hosted + version: "2.4.2+1" + sqflite_android: + dependency: transitive + description: + name: sqflite_android + sha256: "881e28efdcc9950fd8e9bb42713dcf1103e62a2e7168f23c9338d82db13dec40" + url: "https://pub.dev" + source: hosted + version: "2.4.2+3" + sqflite_common: + dependency: transitive + description: + name: sqflite_common + sha256: "1581ffbf7a0e333b380d6a30737d78516b826cb35beb7fb0bf8a3ea0c678b465" + url: "https://pub.dev" + source: hosted + version: "2.5.8" + sqflite_darwin: + dependency: transitive + description: + name: sqflite_darwin + sha256: "279832e5cde3fe99e8571879498c9211f3ca6391b0d818df4e17d9fff5c6ccb3" + url: "https://pub.dev" + source: hosted + version: "2.4.2" + sqflite_platform_interface: + dependency: transitive + description: + name: sqflite_platform_interface + sha256: "8dd4515c7bdcae0a785b0062859336de775e8c65db81ae33dd5445f35be61920" + url: "https://pub.dev" + source: hosted + version: "2.4.0" + sqlite3: + dependency: transitive + description: + name: sqlite3 + sha256: "3145bd74dcdb4fd6f5c6dda4d4e4490a8087d7f286a14dee5d37087290f0f8a2" + url: "https://pub.dev" + source: hosted + version: "2.9.4" + sqlparser: + dependency: transitive + description: + name: sqlparser + sha256: "57090342af1ce32bb499aa641f4ecdd2d6231b9403cea537ac059e803cc20d67" + url: "https://pub.dev" + source: hosted + version: "0.41.2" + stack_trace: + dependency: transitive + description: + name: stack_trace + sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" + url: "https://pub.dev" + source: hosted + version: "1.12.1" + state_notifier: + dependency: transitive + description: + name: state_notifier + sha256: b8677376aa54f2d7c58280d5a007f9e8774f1968d1fb1c096adcb4792fba29bb + url: "https://pub.dev" + source: hosted + version: "1.0.0" + stream_channel: + dependency: transitive + description: + name: stream_channel + sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + stream_transform: + dependency: transitive + description: + name: stream_transform + sha256: ad47125e588cfd37a9a7f86c7d6356dde8dfe89d071d293f80ca9e9273a33871 + url: "https://pub.dev" + source: hosted + version: "2.1.1" + string_scanner: + dependency: transitive + description: + name: string_scanner + sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" + url: "https://pub.dev" + source: hosted + version: "1.4.1" + synchronized: + dependency: transitive + description: + name: synchronized + sha256: c254ade258ec8282947a0acbbc90b9575b4f19673533ee46f2f6e9b3aeefd7c0 + url: "https://pub.dev" + source: hosted + version: "3.4.0" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" + url: "https://pub.dev" + source: hosted + version: "1.2.2" + test_api: + dependency: transitive + description: + name: test_api + sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55 + url: "https://pub.dev" + source: hosted + version: "0.7.7" + timing: + dependency: transitive + description: + name: timing + sha256: "62ee18aca144e4a9f29d212f5a4c6a053be252b895ab14b5821996cff4ed90fe" + url: "https://pub.dev" + source: hosted + version: "1.0.2" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 + url: "https://pub.dev" + source: hosted + version: "1.4.0" + url_launcher_linux: + dependency: transitive + description: + name: url_launcher_linux + sha256: d5e14138b3bc193a0f63c10a53c94b91d399df0512b1f29b94a043db7482384a + url: "https://pub.dev" + source: hosted + version: "3.2.2" + url_launcher_platform_interface: + dependency: transitive + description: + name: url_launcher_platform_interface + sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029" + url: "https://pub.dev" + source: hosted + version: "2.3.2" + url_launcher_web: + dependency: transitive + description: + name: url_launcher_web + sha256: "85c81589622fbc87c1c683aaea164d3604a7777495a79d91e39ffcdec39ddb34" + url: "https://pub.dev" + source: hosted + version: "2.4.3" + url_launcher_windows: + dependency: transitive + description: + name: url_launcher_windows + sha256: "712c70ab1b99744ff066053cbe3e80c73332b38d46e5e945c98689b2e66fc15f" + url: "https://pub.dev" + source: hosted + version: "3.1.5" + uuid: + dependency: "direct main" + description: + name: uuid + sha256: "1fef9e8e11e2991bb773070d4656b7bd5d850967a2456cfc83cf47925ba79489" + url: "https://pub.dev" + source: hosted + version: "4.5.3" + vector_graphics: + dependency: transitive + description: + name: vector_graphics + sha256: "2306c03da2ba81724afeb589c351ebbc0aa7d86005925be8f8735856dbe5e42d" + url: "https://pub.dev" + source: hosted + version: "1.2.2" + vector_graphics_codec: + dependency: transitive + description: + name: vector_graphics_codec + sha256: "99fd9fbd34d9f9a32efd7b6a6aae14125d8237b10403b422a6a6dfeac2806146" + url: "https://pub.dev" + source: hosted + version: "1.1.13" + vector_graphics_compiler: + dependency: transitive + description: + name: vector_graphics_compiler + sha256: "7ee12e6dffe0fc8e755179d6d91b3b34f5924223fc104d85572ef9180d73d172" + url: "https://pub.dev" + source: hosted + version: "1.2.5" + vector_math: + dependency: transitive + description: + name: vector_math + sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b + url: "https://pub.dev" + source: hosted + version: "2.2.0" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: "0016aef94fc66495ac78af5859181e3f3bf2026bd8eecc72b9565601e19ab360" + url: "https://pub.dev" + source: hosted + version: "15.2.0" + watcher: + dependency: transitive + description: + name: watcher + sha256: "1398c9f081a753f9226febe8900fce8f7d0a67163334e1c94a2438339d79d635" + url: "https://pub.dev" + source: hosted + version: "1.2.1" + web: + dependency: transitive + description: + name: web + sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" + url: "https://pub.dev" + source: hosted + version: "1.1.1" + web_socket: + dependency: transitive + description: + name: web_socket + sha256: "34d64019aa8e36bf9842ac014bb5d2f5586ca73df5e4d9bf5c936975cae6982c" + url: "https://pub.dev" + source: hosted + version: "1.0.1" + web_socket_channel: + dependency: transitive + description: + name: web_socket_channel + sha256: d645757fb0f4773d602444000a8131ff5d48c9e47adfe9772652dd1a4f2d45c8 + url: "https://pub.dev" + source: hosted + version: "3.0.3" + win32: + dependency: transitive + description: + name: win32 + sha256: d7cb55e04cd34096cd3a79b3330245f54cb96a370a1c27adb3c84b917de8b08e + url: "https://pub.dev" + source: hosted + version: "5.15.0" + xdg_directories: + dependency: transitive + description: + name: xdg_directories + sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15" + url: "https://pub.dev" + source: hosted + version: "1.1.0" + xml: + dependency: transitive + description: + name: xml + sha256: "971043b3a0d3da28727e40ed3e0b5d18b742fa5a68665cca88e74b7876d5e025" + url: "https://pub.dev" + source: hosted + version: "6.6.1" + yaml: + dependency: transitive + description: + name: yaml + sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce + url: "https://pub.dev" + source: hosted + version: "3.1.3" +sdks: + dart: ">=3.10.3 <4.0.0" + flutter: ">=3.38.4" diff --git a/pubspec.yaml b/pubspec.yaml new file mode 100644 index 0000000..9130310 --- /dev/null +++ b/pubspec.yaml @@ -0,0 +1,60 @@ +name: dls_app +description: DLS — Dental Lab System (Flutter + PocketBase) +publish_to: none +version: 1.0.0+1 + +environment: + sdk: ">=3.3.0 <4.0.0" + +dependencies: + flutter: + sdk: flutter + flutter_localizations: + sdk: flutter + + # Backend + pocketbase: ^0.22.0 + + # State management + flutter_riverpod: ^2.5.1 + riverpod_annotation: ^2.3.5 + + # Navigation + go_router: ^14.2.7 + + + # File handling + file_picker: ^8.1.2 + path_provider: ^2.1.3 + http: ^1.2.0 + shared_preferences: ^2.3.2 + share_plus: ^10.1.4 + onesignal_flutter: ^5.2.5 + + # UI + flutter_svg: ^2.0.10+1 + cached_network_image: ^3.3.1 + shimmer: ^3.0.0 + intl: ^0.20.2 + google_fonts: ^6.2.1 + flutter_animate: ^4.5.0 + + # Utilities + freezed_annotation: ^2.4.1 + json_annotation: ^4.9.0 + equatable: ^2.0.5 + uuid: ^4.4.0 + shadcn_flutter: ^0.0.47 + +dev_dependencies: + flutter_test: + sdk: flutter + build_runner: ^2.4.11 + freezed: ^2.5.2 + json_serializable: ^6.8.0 + riverpod_generator: ^2.4.0 + drift_dev: ^2.18.0 + flutter_lints: ^4.0.0 + +flutter: + uses-material-design: true diff --git a/test/widget_test.dart b/test/widget_test.dart new file mode 100644 index 0000000..af23248 --- /dev/null +++ b/test/widget_test.dart @@ -0,0 +1,7 @@ +import 'package:flutter_test/flutter_test.dart'; + +void main() { + testWidgets('placeholder', (tester) async { + expect(true, isTrue); + }); +} diff --git a/web/favicon.png b/web/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..8aaa46ac1ae21512746f852a42ba87e4165dfdd1 GIT binary patch literal 917 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|I14-?iy0X7 zltGxWVyS%@P(fs7NJL45ua8x7ey(0(N`6wRUPW#JP&EUCO@$SZnVVXYs8ErclUHn2 zVXFjIVFhG^g!Ppaz)DK8ZIvQ?0~DO|i&7O#^-S~(l1AfjnEK zjFOT9D}DX)@^Za$W4-*MbbUihOG|wNBYh(yU7!lx;>x^|#0uTKVr7USFmqf|i<65o z3raHc^AtelCMM;Vme?vOfh>Xph&xL%(-1c06+^uR^q@XSM&D4+Kp$>4P^%3{)XKjo zGZknv$b36P8?Z_gF{nK@`XI}Z90TzwSQO}0J1!f2c(B=V`5aP@1P1a|PZ!4!3&Gl8 zTYqUsf!gYFyJnXpu0!n&N*SYAX-%d(5gVjrHJWqXQshj@!Zm{!01WsQrH~9=kTxW#6SvuapgMqt>$=j#%eyGrQzr zP{L-3gsMA^$I1&gsBAEL+vxi1*Igl=8#8`5?A-T5=z-sk46WA1IUT)AIZHx1rdUrf zVJrJn<74DDw`j)Ki#gt}mIT-Q`XRa2-jQXQoI%w`nb|XblvzK${ZzlV)m-XcwC(od z71_OEC5Bt9GEXosOXaPTYOia#R4ID2TiU~`zVMl08TV_C%DnU4^+HE>9(CE4D6?Fz oujB08i7adh9xk7*FX66dWH6F5TM;?E2b5PlUHx3vIVCg!0Dx9vYXATM literal 0 HcmV?d00001 diff --git a/web/icons/Icon-192.png b/web/icons/Icon-192.png new file mode 100644 index 0000000000000000000000000000000000000000..b749bfef07473333cf1dd31e9eed89862a5d52aa GIT binary patch literal 5292 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 literal 0 HcmV?d00001 diff --git a/web/icons/Icon-512.png b/web/icons/Icon-512.png new file mode 100644 index 0000000000000000000000000000000000000000..88cfd48dff1169879ba46840804b412fe02fefd6 GIT binary patch literal 8252 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 literal 0 HcmV?d00001 diff --git a/web/icons/Icon-maskable-192.png b/web/icons/Icon-maskable-192.png new file mode 100644 index 0000000000000000000000000000000000000000..eb9b4d76e525556d5d89141648c724331630325d GIT binary patch literal 5594 zcmdT|`#%%j|KDb2V@0DPm$^(Lx5}lO%Yv(=e*7hl@QqKS50#~#^IQPxBmuh|i9sXnt4ch@VT0F7% zMtrs@KWIOo+QV@lSs66A>2pz6-`9Jk=0vv&u?)^F@HZ)-6HT=B7LF;rdj zskUyBfbojcX#CS>WrIWo9D=DIwcXM8=I5D{SGf$~=gh-$LwY?*)cD%38%sCc?5OsX z-XfkyL-1`VavZ?>(pI-xp-kYq=1hsnyP^TLb%0vKRSo^~r{x?ISLY1i7KjSp z*0h&jG(Rkkq2+G_6eS>n&6>&Xk+ngOMcYrk<8KrukQHzfx675^^s$~<@d$9X{VBbg z2Fd4Z%g`!-P}d#`?B4#S-9x*eNlOVRnDrn#jY@~$jfQ-~3Od;A;x-BI1BEDdvr`pI z#D)d)!2_`GiZOUu1crb!hqH=ezs0qk<_xDm_Kkw?r*?0C3|Io6>$!kyDl;eH=aqg$B zsH_|ZD?jP2dc=)|L>DZmGyYKa06~5?C2Lc0#D%62p(YS;%_DRCB1k(+eLGXVMe+=4 zkKiJ%!N6^mxqM=wq`0+yoE#VHF%R<{mMamR9o_1JH8jfnJ?NPLs$9U!9!dq8 z0B{dI2!M|sYGH&9TAY34OlpIsQ4i5bnbG>?cWwat1I13|r|_inLE?FS@Hxdxn_YZN z3jfUO*X9Q@?HZ>Q{W0z60!bbGh557XIKu1?)u|cf%go`pwo}CD=0tau-}t@R2OrSH zQzZr%JfYa`>2!g??76=GJ$%ECbQh7Q2wLRp9QoyiRHP7VE^>JHm>9EqR3<$Y=Z1K^SHuwxCy-5@z3 zVM{XNNm}yM*pRdLKp??+_2&!bp#`=(Lh1vR{~j%n;cJv~9lXeMv)@}Odta)RnK|6* zC+IVSWumLo%{6bLDpn)Gz>6r&;Qs0^+Sz_yx_KNz9Dlt^ax`4>;EWrIT#(lJ_40<= z750fHZ7hI{}%%5`;lwkI4<_FJw@!U^vW;igL0k+mK)-j zYuCK#mCDK3F|SC}tC2>m$ZCqNB7ac-0UFBJ|8RxmG@4a4qdjvMzzS&h9pQmu^x&*= zGvapd1#K%Da&)8f?<9WN`2H^qpd@{7In6DNM&916TRqtF4;3`R|Nhwbw=(4|^Io@T zIjoR?tB8d*sO>PX4vaIHF|W;WVl6L1JvSmStgnRQq zTX4(>1f^5QOAH{=18Q2Vc1JI{V=yOr7yZJf4Vpfo zeHXdhBe{PyY;)yF;=ycMW@Kb>t;yE>;f79~AlJ8k`xWucCxJfsXf2P72bAavWL1G#W z;o%kdH(mYCM{$~yw4({KatNGim49O2HY6O07$B`*K7}MvgI=4x=SKdKVb8C$eJseA$tmSFOztFd*3W`J`yIB_~}k%Sd_bPBK8LxH)?8#jM{^%J_0|L z!gFI|68)G}ex5`Xh{5pB%GtlJ{Z5em*e0sH+sU1UVl7<5%Bq+YrHWL7?X?3LBi1R@_)F-_OqI1Zv`L zb6^Lq#H^2@d_(Z4E6xA9Z4o3kvf78ZDz!5W1#Mp|E;rvJz&4qj2pXVxKB8Vg0}ek%4erou@QM&2t7Cn5GwYqy%{>jI z)4;3SAgqVi#b{kqX#$Mt6L8NhZYgonb7>+r#BHje)bvaZ2c0nAvrN3gez+dNXaV;A zmyR0z@9h4@6~rJik-=2M-T+d`t&@YWhsoP_XP-NsVO}wmo!nR~QVWU?nVlQjNfgcTzE-PkfIX5G z1?&MwaeuzhF=u)X%Vpg_e@>d2yZwxl6-r3OMqDn8_6m^4z3zG##cK0Fsgq8fcvmhu z{73jseR%X%$85H^jRAcrhd&k!i^xL9FrS7qw2$&gwAS8AfAk#g_E_tP;x66fS`Mn@SNVrcn_N;EQm z`Mt3Z%rw%hDqTH-s~6SrIL$hIPKL5^7ejkLTBr46;pHTQDdoErS(B>``t;+1+M zvU&Se9@T_BeK;A^p|n^krIR+6rH~BjvRIugf`&EuX9u69`9C?9ANVL8l(rY6#mu^i z=*5Q)-%o*tWl`#b8p*ZH0I}hn#gV%|jt6V_JanDGuekR*-wF`u;amTCpGG|1;4A5$ zYbHF{?G1vv5;8Ph5%kEW)t|am2_4ik!`7q{ymfHoe^Z99c|$;FAL+NbxE-_zheYbV z3hb0`uZGTsgA5TG(X|GVDSJyJxsyR7V5PS_WSnYgwc_D60m7u*x4b2D79r5UgtL18 zcCHWk+K6N1Pg2c;0#r-)XpwGX?|Iv)^CLWqwF=a}fXUSM?n6E;cCeW5ER^om#{)Jr zJR81pkK?VoFm@N-s%hd7@hBS0xuCD0-UDVLDDkl7Ck=BAj*^ps`393}AJ+Ruq@fl9 z%R(&?5Nc3lnEKGaYMLmRzKXow1+Gh|O-LG7XiNxkG^uyv zpAtLINwMK}IWK65hOw&O>~EJ}x@lDBtB`yKeV1%GtY4PzT%@~wa1VgZn7QRwc7C)_ zpEF~upeDRg_<#w=dLQ)E?AzXUQpbKXYxkp>;c@aOr6A|dHA?KaZkL0svwB^U#zmx0 zzW4^&G!w7YeRxt<9;d@8H=u(j{6+Uj5AuTluvZZD4b+#+6Rp?(yJ`BC9EW9!b&KdPvzJYe5l7 zMJ9aC@S;sA0{F0XyVY{}FzW0Vh)0mPf_BX82E+CD&)wf2!x@{RO~XBYu80TONl3e+ zA7W$ra6LcDW_j4s-`3tI^VhG*sa5lLc+V6ONf=hO@q4|p`CinYqk1Ko*MbZ6_M05k zSwSwkvu;`|I*_Vl=zPd|dVD0lh&Ha)CSJJvV{AEdF{^Kn_Yfsd!{Pc1GNgw}(^~%)jk5~0L~ms|Rez1fiK~s5t(p1ci5Gq$JC#^JrXf?8 z-Y-Zi_Hvi>oBzV8DSRG!7dm|%IlZg3^0{5~;>)8-+Nk&EhAd(}s^7%MuU}lphNW9Q zT)DPo(ob{tB7_?u;4-qGDo!sh&7gHaJfkh43QwL|bbFVi@+oy;i;M zM&CP^v~lx1U`pi9PmSr&Mc<%HAq0DGH?Ft95)WY`P?~7O z`O^Nr{Py9M#Ls4Y7OM?e%Y*Mvrme%=DwQaye^Qut_1pOMrg^!5u(f9p(D%MR%1K>% zRGw%=dYvw@)o}Fw@tOtPjz`45mfpn;OT&V(;z75J*<$52{sB65$gDjwX3Xa!x_wE- z!#RpwHM#WrO*|~f7z}(}o7US(+0FYLM}6de>gQdtPazXz?OcNv4R^oYLJ_BQOd_l172oSK$6!1r@g+B@0ofJ4*{>_AIxfe-#xp>(1 z@Y3Nfd>fmqvjL;?+DmZk*KsfXJf<%~(gcLwEez%>1c6XSboURUh&k=B)MS>6kw9bY z{7vdev7;A}5fy*ZE23DS{J?8at~xwVk`pEwP5^k?XMQ7u64;KmFJ#POzdG#np~F&H ze-BUh@g54)dsS%nkBb}+GuUEKU~pHcYIg4vSo$J(J|U36bs0Use+3A&IMcR%6@jv$ z=+QI+@wW@?iu}Hpyzlvj-EYeop{f65GX0O%>w#0t|V z1-svWk`hU~m`|O$kw5?Yn5UhI%9P-<45A(v0ld1n+%Ziq&TVpBcV9n}L9Tus-TI)f zd_(g+nYCDR@+wYNQm1GwxhUN4tGMLCzDzPqY$~`l<47{+l<{FZ$L6(>J)|}!bi<)| zE35dl{a2)&leQ@LlDxLQOfUDS`;+ZQ4ozrleQwaR-K|@9T{#hB5Z^t#8 zC-d_G;B4;F#8A2EBL58s$zF-=SCr`P#z zNCTnHF&|X@q>SkAoYu>&s9v@zCpv9lLSH-UZzfhJh`EZA{X#%nqw@@aW^vPcfQrlPs(qQxmC|4tp^&sHy!H!2FH5eC{M@g;ElWNzlb-+ zxpfc0m4<}L){4|RZ>KReag2j%Ot_UKkgpJN!7Y_y3;Ssz{9 z!K3isRtaFtQII5^6}cm9RZd5nTp9psk&u1C(BY`(_tolBwzV_@0F*m%3G%Y?2utyS zY`xM0iDRT)yTyYukFeGQ&W@ReM+ADG1xu@ruq&^GK35`+2r}b^V!m1(VgH|QhIPDE X>c!)3PgKfL&lX^$Z>Cpu&6)6jvi^Z! literal 0 HcmV?d00001 diff --git a/web/icons/Icon-maskable-512.png b/web/icons/Icon-maskable-512.png new file mode 100644 index 0000000000000000000000000000000000000000..d69c56691fbdb0b7efa65097c7cc1edac12a6d3e GIT binary patch literal 20998 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 MPgg&ebxsLQ025IeI{*Lx literal 0 HcmV?d00001 diff --git a/web/index.html b/web/index.html new file mode 100644 index 0000000..b491443 --- /dev/null +++ b/web/index.html @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + lab_app + + + + + + diff --git a/web/manifest.json b/web/manifest.json new file mode 100644 index 0000000..d3f9c18 --- /dev/null +++ b/web/manifest.json @@ -0,0 +1,35 @@ +{ + "name": "lab_app", + "short_name": "lab_app", + "start_url": ".", + "display": "standalone", + "background_color": "#0175C2", + "theme_color": "#0175C2", + "description": "A new Flutter project.", + "orientation": "portrait-primary", + "prefer_related_applications": false, + "icons": [ + { + "src": "icons/Icon-192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "icons/Icon-512.png", + "sizes": "512x512", + "type": "image/png" + }, + { + "src": "icons/Icon-maskable-192.png", + "sizes": "192x192", + "type": "image/png", + "purpose": "maskable" + }, + { + "src": "icons/Icon-maskable-512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "maskable" + } + ] +} diff --git a/windows/.gitignore b/windows/.gitignore new file mode 100644 index 0000000..d492d0d --- /dev/null +++ b/windows/.gitignore @@ -0,0 +1,17 @@ +flutter/ephemeral/ + +# Visual Studio user-specific files. +*.suo +*.user +*.userosscache +*.sln.docstates + +# Visual Studio build-related files. +x64/ +x86/ + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt new file mode 100644 index 0000000..a8996aa --- /dev/null +++ b/windows/CMakeLists.txt @@ -0,0 +1,108 @@ +# Project-level configuration. +cmake_minimum_required(VERSION 3.14) +project(lab_app LANGUAGES CXX) + +# The name of the executable created for the application. Change this to change +# the on-disk name of your application. +set(BINARY_NAME "lab_app") + +# Explicitly opt in to modern CMake behaviors to avoid warnings with recent +# versions of CMake. +cmake_policy(VERSION 3.14...3.25) + +# Define build configuration option. +get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) +if(IS_MULTICONFIG) + set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release" + CACHE STRING "" FORCE) +else() + if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") + endif() +endif() +# Define settings for the Profile build mode. +set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}") +set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") +set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}") +set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}") + +# Use Unicode for all projects. +add_definitions(-DUNICODE -D_UNICODE) + +# Compilation settings that should be applied to most targets. +# +# Be cautious about adding new options here, as plugins use this function by +# default. In most cases, you should add new options to specific targets instead +# of modifying this function. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_17) + target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100") + target_compile_options(${TARGET} PRIVATE /EHsc) + target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0") + target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>") +endfunction() + +# Flutter library and tool build rules. +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# Application build; see runner/CMakeLists.txt. +add_subdirectory("runner") + + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# Support files are copied into place next to the executable, so that it can +# run in place. This is done instead of making a separate bundle (as on Linux) +# so that building and running from within Visual Studio will work. +set(BUILD_BUNDLE_DIR "$") +# Make the "install" step default, as it's required to run. +set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +if(PLUGIN_BUNDLED_LIBRARIES) + install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() + +# Copy the native assets provided by the build.dart from all packages. +set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/windows/") +install(DIRECTORY "${NATIVE_ASSETS_DIR}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + CONFIGURATIONS Profile;Release + COMPONENT Runtime) diff --git a/windows/flutter/CMakeLists.txt b/windows/flutter/CMakeLists.txt new file mode 100644 index 0000000..903f489 --- /dev/null +++ b/windows/flutter/CMakeLists.txt @@ -0,0 +1,109 @@ +# This file controls Flutter-level build steps. It should not be edited. +cmake_minimum_required(VERSION 3.14) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. +set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") + +# Set fallback configurations for older versions of the flutter tool. +if (NOT DEFINED FLUTTER_TARGET_PLATFORM) + set(FLUTTER_TARGET_PLATFORM "windows-x64") +endif() + +# === Flutter Library === +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "flutter_export.h" + "flutter_windows.h" + "flutter_messenger.h" + "flutter_plugin_registrar.h" + "flutter_texture_registrar.h" +) +list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib") +add_dependencies(flutter flutter_assemble) + +# === Wrapper === +list(APPEND CPP_WRAPPER_SOURCES_CORE + "core_implementations.cc" + "standard_codec.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_PLUGIN + "plugin_registrar.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_APP + "flutter_engine.cc" + "flutter_view_controller.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/") + +# Wrapper sources needed for a plugin. +add_library(flutter_wrapper_plugin STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} +) +apply_standard_settings(flutter_wrapper_plugin) +set_target_properties(flutter_wrapper_plugin PROPERTIES + POSITION_INDEPENDENT_CODE ON) +set_target_properties(flutter_wrapper_plugin PROPERTIES + CXX_VISIBILITY_PRESET hidden) +target_link_libraries(flutter_wrapper_plugin PUBLIC flutter) +target_include_directories(flutter_wrapper_plugin PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_plugin flutter_assemble) + +# Wrapper sources needed for the runner. +add_library(flutter_wrapper_app STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_APP} +) +apply_standard_settings(flutter_wrapper_app) +target_link_libraries(flutter_wrapper_app PUBLIC flutter) +target_include_directories(flutter_wrapper_app PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_app flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_") +set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE) +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} + ${PHONY_OUTPUT} + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" + ${FLUTTER_TARGET_PLATFORM} $ + VERBATIM +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} +) diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc new file mode 100644 index 0000000..c3384ec --- /dev/null +++ b/windows/flutter/generated_plugin_registrant.cc @@ -0,0 +1,17 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#include "generated_plugin_registrant.h" + +#include +#include + +void RegisterPlugins(flutter::PluginRegistry* registry) { + SharePlusWindowsPluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("SharePlusWindowsPluginCApi")); + UrlLauncherWindowsRegisterWithRegistrar( + registry->GetRegistrarForPlugin("UrlLauncherWindows")); +} diff --git a/windows/flutter/generated_plugin_registrant.h b/windows/flutter/generated_plugin_registrant.h new file mode 100644 index 0000000..dc139d8 --- /dev/null +++ b/windows/flutter/generated_plugin_registrant.h @@ -0,0 +1,15 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#ifndef GENERATED_PLUGIN_REGISTRANT_ +#define GENERATED_PLUGIN_REGISTRANT_ + +#include + +// Registers Flutter plugins. +void RegisterPlugins(flutter::PluginRegistry* registry); + +#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake new file mode 100644 index 0000000..4b61cfd --- /dev/null +++ b/windows/flutter/generated_plugins.cmake @@ -0,0 +1,26 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST + share_plus + url_launcher_windows +) + +list(APPEND FLUTTER_FFI_PLUGIN_LIST + jni +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) + target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) + list(APPEND PLUGIN_BUNDLED_LIBRARIES $) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) +endforeach(plugin) + +foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin) diff --git a/windows/runner/CMakeLists.txt b/windows/runner/CMakeLists.txt new file mode 100644 index 0000000..394917c --- /dev/null +++ b/windows/runner/CMakeLists.txt @@ -0,0 +1,40 @@ +cmake_minimum_required(VERSION 3.14) +project(runner LANGUAGES CXX) + +# Define the application target. To change its name, change BINARY_NAME in the +# top-level CMakeLists.txt, not the value here, or `flutter run` will no longer +# work. +# +# Any new source files that you add to the application should be added here. +add_executable(${BINARY_NAME} WIN32 + "flutter_window.cpp" + "main.cpp" + "utils.cpp" + "win32_window.cpp" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" + "Runner.rc" + "runner.exe.manifest" +) + +# Apply the standard set of build settings. This can be removed for applications +# that need different build settings. +apply_standard_settings(${BINARY_NAME}) + +# Add preprocessor definitions for the build version. +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION=\"${FLUTTER_VERSION}\"") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MAJOR=${FLUTTER_VERSION_MAJOR}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MINOR=${FLUTTER_VERSION_MINOR}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_PATCH=${FLUTTER_VERSION_PATCH}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_BUILD=${FLUTTER_VERSION_BUILD}") + +# Disable Windows macros that collide with C++ standard library functions. +target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") + +# Add dependency libraries and include directories. Add any application-specific +# dependencies here. +target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) +target_link_libraries(${BINARY_NAME} PRIVATE "dwmapi.lib") +target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") + +# Run the Flutter tool portions of the build. This must not be removed. +add_dependencies(${BINARY_NAME} flutter_assemble) diff --git a/windows/runner/Runner.rc b/windows/runner/Runner.rc new file mode 100644 index 0000000..2dae856 --- /dev/null +++ b/windows/runner/Runner.rc @@ -0,0 +1,121 @@ +// Microsoft Visual C++ generated resource script. +// +#pragma code_page(65001) +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_APP_ICON ICON "resources\\app_icon.ico" + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +#if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD) +#define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD +#else +#define VERSION_AS_NUMBER 1,0,0,0 +#endif + +#if defined(FLUTTER_VERSION) +#define VERSION_AS_STRING FLUTTER_VERSION +#else +#define VERSION_AS_STRING "1.0.0" +#endif + +VS_VERSION_INFO VERSIONINFO + FILEVERSION VERSION_AS_NUMBER + PRODUCTVERSION VERSION_AS_NUMBER + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef _DEBUG + FILEFLAGS VS_FF_DEBUG +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_APP + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904e4" + BEGIN + VALUE "CompanyName", "com.kovaksoft" "\0" + VALUE "FileDescription", "lab_app" "\0" + VALUE "FileVersion", VERSION_AS_STRING "\0" + VALUE "InternalName", "lab_app" "\0" + VALUE "LegalCopyright", "Copyright (C) 2026 com.kovaksoft. All rights reserved." "\0" + VALUE "OriginalFilename", "lab_app.exe" "\0" + VALUE "ProductName", "lab_app" "\0" + VALUE "ProductVersion", VERSION_AS_STRING "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED diff --git a/windows/runner/flutter_window.cpp b/windows/runner/flutter_window.cpp new file mode 100644 index 0000000..955ee30 --- /dev/null +++ b/windows/runner/flutter_window.cpp @@ -0,0 +1,71 @@ +#include "flutter_window.h" + +#include + +#include "flutter/generated_plugin_registrant.h" + +FlutterWindow::FlutterWindow(const flutter::DartProject& project) + : project_(project) {} + +FlutterWindow::~FlutterWindow() {} + +bool FlutterWindow::OnCreate() { + if (!Win32Window::OnCreate()) { + return false; + } + + RECT frame = GetClientArea(); + + // The size here must match the window dimensions to avoid unnecessary surface + // creation / destruction in the startup path. + flutter_controller_ = std::make_unique( + frame.right - frame.left, frame.bottom - frame.top, project_); + // Ensure that basic setup of the controller was successful. + if (!flutter_controller_->engine() || !flutter_controller_->view()) { + return false; + } + RegisterPlugins(flutter_controller_->engine()); + SetChildContent(flutter_controller_->view()->GetNativeWindow()); + + flutter_controller_->engine()->SetNextFrameCallback([&]() { + this->Show(); + }); + + // Flutter can complete the first frame before the "show window" callback is + // registered. The following call ensures a frame is pending to ensure the + // window is shown. It is a no-op if the first frame hasn't completed yet. + flutter_controller_->ForceRedraw(); + + return true; +} + +void FlutterWindow::OnDestroy() { + if (flutter_controller_) { + flutter_controller_ = nullptr; + } + + Win32Window::OnDestroy(); +} + +LRESULT +FlutterWindow::MessageHandler(HWND hwnd, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + // Give Flutter, including plugins, an opportunity to handle window messages. + if (flutter_controller_) { + std::optional result = + flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, + lparam); + if (result) { + return *result; + } + } + + switch (message) { + case WM_FONTCHANGE: + flutter_controller_->engine()->ReloadSystemFonts(); + break; + } + + return Win32Window::MessageHandler(hwnd, message, wparam, lparam); +} diff --git a/windows/runner/flutter_window.h b/windows/runner/flutter_window.h new file mode 100644 index 0000000..6da0652 --- /dev/null +++ b/windows/runner/flutter_window.h @@ -0,0 +1,33 @@ +#ifndef RUNNER_FLUTTER_WINDOW_H_ +#define RUNNER_FLUTTER_WINDOW_H_ + +#include +#include + +#include + +#include "win32_window.h" + +// A window that does nothing but host a Flutter view. +class FlutterWindow : public Win32Window { + public: + // Creates a new FlutterWindow hosting a Flutter view running |project|. + explicit FlutterWindow(const flutter::DartProject& project); + virtual ~FlutterWindow(); + + protected: + // Win32Window: + bool OnCreate() override; + void OnDestroy() override; + LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, + LPARAM const lparam) noexcept override; + + private: + // The project to run. + flutter::DartProject project_; + + // The Flutter instance hosted by this window. + std::unique_ptr flutter_controller_; +}; + +#endif // RUNNER_FLUTTER_WINDOW_H_ diff --git a/windows/runner/main.cpp b/windows/runner/main.cpp new file mode 100644 index 0000000..b101a1d --- /dev/null +++ b/windows/runner/main.cpp @@ -0,0 +1,43 @@ +#include +#include +#include + +#include "flutter_window.h" +#include "utils.h" + +int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, + _In_ wchar_t *command_line, _In_ int show_command) { + // Attach to console when present (e.g., 'flutter run') or create a + // new console when running with a debugger. + if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { + CreateAndAttachConsole(); + } + + // Initialize COM, so that it is available for use in the library and/or + // plugins. + ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); + + flutter::DartProject project(L"data"); + + std::vector command_line_arguments = + GetCommandLineArguments(); + + project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); + + FlutterWindow window(project); + Win32Window::Point origin(10, 10); + Win32Window::Size size(1280, 720); + if (!window.Create(L"lab_app", origin, size)) { + return EXIT_FAILURE; + } + window.SetQuitOnClose(true); + + ::MSG msg; + while (::GetMessage(&msg, nullptr, 0, 0)) { + ::TranslateMessage(&msg); + ::DispatchMessage(&msg); + } + + ::CoUninitialize(); + return EXIT_SUCCESS; +} diff --git a/windows/runner/resource.h b/windows/runner/resource.h new file mode 100644 index 0000000..66a65d1 --- /dev/null +++ b/windows/runner/resource.h @@ -0,0 +1,16 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by Runner.rc +// +#define IDI_APP_ICON 101 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/windows/runner/resources/app_icon.ico b/windows/runner/resources/app_icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..c04e20caf6370ebb9253ad831cc31de4a9c965f6 GIT binary patch literal 33772 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 literal 0 HcmV?d00001 diff --git a/windows/runner/runner.exe.manifest b/windows/runner/runner.exe.manifest new file mode 100644 index 0000000..153653e --- /dev/null +++ b/windows/runner/runner.exe.manifest @@ -0,0 +1,14 @@ + + + + + PerMonitorV2 + + + + + + + + + diff --git a/windows/runner/utils.cpp b/windows/runner/utils.cpp new file mode 100644 index 0000000..3a0b465 --- /dev/null +++ b/windows/runner/utils.cpp @@ -0,0 +1,65 @@ +#include "utils.h" + +#include +#include +#include +#include + +#include + +void CreateAndAttachConsole() { + if (::AllocConsole()) { + FILE *unused; + if (freopen_s(&unused, "CONOUT$", "w", stdout)) { + _dup2(_fileno(stdout), 1); + } + if (freopen_s(&unused, "CONOUT$", "w", stderr)) { + _dup2(_fileno(stdout), 2); + } + std::ios::sync_with_stdio(); + FlutterDesktopResyncOutputStreams(); + } +} + +std::vector GetCommandLineArguments() { + // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. + int argc; + wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); + if (argv == nullptr) { + return std::vector(); + } + + std::vector command_line_arguments; + + // Skip the first argument as it's the binary name. + for (int i = 1; i < argc; i++) { + command_line_arguments.push_back(Utf8FromUtf16(argv[i])); + } + + ::LocalFree(argv); + + return command_line_arguments; +} + +std::string Utf8FromUtf16(const wchar_t* utf16_string) { + if (utf16_string == nullptr) { + return std::string(); + } + unsigned int target_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, + -1, nullptr, 0, nullptr, nullptr) + -1; // remove the trailing null character + int input_length = (int)wcslen(utf16_string); + std::string utf8_string; + if (target_length == 0 || target_length > utf8_string.max_size()) { + return utf8_string; + } + utf8_string.resize(target_length); + int converted_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, + input_length, utf8_string.data(), target_length, nullptr, nullptr); + if (converted_length == 0) { + return std::string(); + } + return utf8_string; +} diff --git a/windows/runner/utils.h b/windows/runner/utils.h new file mode 100644 index 0000000..3879d54 --- /dev/null +++ b/windows/runner/utils.h @@ -0,0 +1,19 @@ +#ifndef RUNNER_UTILS_H_ +#define RUNNER_UTILS_H_ + +#include +#include + +// Creates a console for the process, and redirects stdout and stderr to +// it for both the runner and the Flutter library. +void CreateAndAttachConsole(); + +// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string +// encoded in UTF-8. Returns an empty std::string on failure. +std::string Utf8FromUtf16(const wchar_t* utf16_string); + +// Gets the command line arguments passed in as a std::vector, +// encoded in UTF-8. Returns an empty std::vector on failure. +std::vector GetCommandLineArguments(); + +#endif // RUNNER_UTILS_H_ diff --git a/windows/runner/win32_window.cpp b/windows/runner/win32_window.cpp new file mode 100644 index 0000000..60608d0 --- /dev/null +++ b/windows/runner/win32_window.cpp @@ -0,0 +1,288 @@ +#include "win32_window.h" + +#include +#include + +#include "resource.h" + +namespace { + +/// Window attribute that enables dark mode window decorations. +/// +/// Redefined in case the developer's machine has a Windows SDK older than +/// version 10.0.22000.0. +/// See: https://docs.microsoft.com/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute +#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE +#define DWMWA_USE_IMMERSIVE_DARK_MODE 20 +#endif + +constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW"; + +/// Registry key for app theme preference. +/// +/// A value of 0 indicates apps should use dark mode. A non-zero or missing +/// value indicates apps should use light mode. +constexpr const wchar_t kGetPreferredBrightnessRegKey[] = + L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize"; +constexpr const wchar_t kGetPreferredBrightnessRegValue[] = L"AppsUseLightTheme"; + +// The number of Win32Window objects that currently exist. +static int g_active_window_count = 0; + +using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd); + +// Scale helper to convert logical scaler values to physical using passed in +// scale factor +int Scale(int source, double scale_factor) { + return static_cast(source * scale_factor); +} + +// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module. +// This API is only needed for PerMonitor V1 awareness mode. +void EnableFullDpiSupportIfAvailable(HWND hwnd) { + HMODULE user32_module = LoadLibraryA("User32.dll"); + if (!user32_module) { + return; + } + auto enable_non_client_dpi_scaling = + reinterpret_cast( + GetProcAddress(user32_module, "EnableNonClientDpiScaling")); + if (enable_non_client_dpi_scaling != nullptr) { + enable_non_client_dpi_scaling(hwnd); + } + FreeLibrary(user32_module); +} + +} // namespace + +// Manages the Win32Window's window class registration. +class WindowClassRegistrar { + public: + ~WindowClassRegistrar() = default; + + // Returns the singleton registrar instance. + static WindowClassRegistrar* GetInstance() { + if (!instance_) { + instance_ = new WindowClassRegistrar(); + } + return instance_; + } + + // Returns the name of the window class, registering the class if it hasn't + // previously been registered. + const wchar_t* GetWindowClass(); + + // Unregisters the window class. Should only be called if there are no + // instances of the window. + void UnregisterWindowClass(); + + private: + WindowClassRegistrar() = default; + + static WindowClassRegistrar* instance_; + + bool class_registered_ = false; +}; + +WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr; + +const wchar_t* WindowClassRegistrar::GetWindowClass() { + if (!class_registered_) { + WNDCLASS window_class{}; + window_class.hCursor = LoadCursor(nullptr, IDC_ARROW); + window_class.lpszClassName = kWindowClassName; + window_class.style = CS_HREDRAW | CS_VREDRAW; + window_class.cbClsExtra = 0; + window_class.cbWndExtra = 0; + window_class.hInstance = GetModuleHandle(nullptr); + window_class.hIcon = + LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON)); + window_class.hbrBackground = 0; + window_class.lpszMenuName = nullptr; + window_class.lpfnWndProc = Win32Window::WndProc; + RegisterClass(&window_class); + class_registered_ = true; + } + return kWindowClassName; +} + +void WindowClassRegistrar::UnregisterWindowClass() { + UnregisterClass(kWindowClassName, nullptr); + class_registered_ = false; +} + +Win32Window::Win32Window() { + ++g_active_window_count; +} + +Win32Window::~Win32Window() { + --g_active_window_count; + Destroy(); +} + +bool Win32Window::Create(const std::wstring& title, + const Point& origin, + const Size& size) { + Destroy(); + + const wchar_t* window_class = + WindowClassRegistrar::GetInstance()->GetWindowClass(); + + const POINT target_point = {static_cast(origin.x), + static_cast(origin.y)}; + HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST); + UINT dpi = FlutterDesktopGetDpiForMonitor(monitor); + double scale_factor = dpi / 96.0; + + HWND window = CreateWindow( + window_class, title.c_str(), WS_OVERLAPPEDWINDOW, + Scale(origin.x, scale_factor), Scale(origin.y, scale_factor), + Scale(size.width, scale_factor), Scale(size.height, scale_factor), + nullptr, nullptr, GetModuleHandle(nullptr), this); + + if (!window) { + return false; + } + + UpdateTheme(window); + + return OnCreate(); +} + +bool Win32Window::Show() { + return ShowWindow(window_handle_, SW_SHOWNORMAL); +} + +// static +LRESULT CALLBACK Win32Window::WndProc(HWND const window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + if (message == WM_NCCREATE) { + auto window_struct = reinterpret_cast(lparam); + SetWindowLongPtr(window, GWLP_USERDATA, + reinterpret_cast(window_struct->lpCreateParams)); + + auto that = static_cast(window_struct->lpCreateParams); + EnableFullDpiSupportIfAvailable(window); + that->window_handle_ = window; + } else if (Win32Window* that = GetThisFromHandle(window)) { + return that->MessageHandler(window, message, wparam, lparam); + } + + return DefWindowProc(window, message, wparam, lparam); +} + +LRESULT +Win32Window::MessageHandler(HWND hwnd, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + switch (message) { + case WM_DESTROY: + window_handle_ = nullptr; + Destroy(); + if (quit_on_close_) { + PostQuitMessage(0); + } + return 0; + + case WM_DPICHANGED: { + auto newRectSize = reinterpret_cast(lparam); + LONG newWidth = newRectSize->right - newRectSize->left; + LONG newHeight = newRectSize->bottom - newRectSize->top; + + SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth, + newHeight, SWP_NOZORDER | SWP_NOACTIVATE); + + return 0; + } + case WM_SIZE: { + RECT rect = GetClientArea(); + if (child_content_ != nullptr) { + // Size and position the child window. + MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left, + rect.bottom - rect.top, TRUE); + } + return 0; + } + + case WM_ACTIVATE: + if (child_content_ != nullptr) { + SetFocus(child_content_); + } + return 0; + + case WM_DWMCOLORIZATIONCOLORCHANGED: + UpdateTheme(hwnd); + return 0; + } + + return DefWindowProc(window_handle_, message, wparam, lparam); +} + +void Win32Window::Destroy() { + OnDestroy(); + + if (window_handle_) { + DestroyWindow(window_handle_); + window_handle_ = nullptr; + } + if (g_active_window_count == 0) { + WindowClassRegistrar::GetInstance()->UnregisterWindowClass(); + } +} + +Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept { + return reinterpret_cast( + GetWindowLongPtr(window, GWLP_USERDATA)); +} + +void Win32Window::SetChildContent(HWND content) { + child_content_ = content; + SetParent(content, window_handle_); + RECT frame = GetClientArea(); + + MoveWindow(content, frame.left, frame.top, frame.right - frame.left, + frame.bottom - frame.top, true); + + SetFocus(child_content_); +} + +RECT Win32Window::GetClientArea() { + RECT frame; + GetClientRect(window_handle_, &frame); + return frame; +} + +HWND Win32Window::GetHandle() { + return window_handle_; +} + +void Win32Window::SetQuitOnClose(bool quit_on_close) { + quit_on_close_ = quit_on_close; +} + +bool Win32Window::OnCreate() { + // No-op; provided for subclasses. + return true; +} + +void Win32Window::OnDestroy() { + // No-op; provided for subclasses. +} + +void Win32Window::UpdateTheme(HWND const window) { + DWORD light_mode; + DWORD light_mode_size = sizeof(light_mode); + LSTATUS result = RegGetValue(HKEY_CURRENT_USER, kGetPreferredBrightnessRegKey, + kGetPreferredBrightnessRegValue, + RRF_RT_REG_DWORD, nullptr, &light_mode, + &light_mode_size); + + if (result == ERROR_SUCCESS) { + BOOL enable_dark_mode = light_mode == 0; + DwmSetWindowAttribute(window, DWMWA_USE_IMMERSIVE_DARK_MODE, + &enable_dark_mode, sizeof(enable_dark_mode)); + } +} diff --git a/windows/runner/win32_window.h b/windows/runner/win32_window.h new file mode 100644 index 0000000..e901dde --- /dev/null +++ b/windows/runner/win32_window.h @@ -0,0 +1,102 @@ +#ifndef RUNNER_WIN32_WINDOW_H_ +#define RUNNER_WIN32_WINDOW_H_ + +#include + +#include +#include +#include + +// A class abstraction for a high DPI-aware Win32 Window. Intended to be +// inherited from by classes that wish to specialize with custom +// rendering and input handling +class Win32Window { + public: + struct Point { + unsigned int x; + unsigned int y; + Point(unsigned int x, unsigned int y) : x(x), y(y) {} + }; + + struct Size { + unsigned int width; + unsigned int height; + Size(unsigned int width, unsigned int height) + : width(width), height(height) {} + }; + + Win32Window(); + virtual ~Win32Window(); + + // Creates a win32 window with |title| that is positioned and sized using + // |origin| and |size|. New windows are created on the default monitor. Window + // sizes are specified to the OS in physical pixels, hence to ensure a + // consistent size this function will scale the inputted width and height as + // as appropriate for the default monitor. The window is invisible until + // |Show| is called. Returns true if the window was created successfully. + bool Create(const std::wstring& title, const Point& origin, const Size& size); + + // Show the current window. Returns true if the window was successfully shown. + bool Show(); + + // Release OS resources associated with window. + void Destroy(); + + // Inserts |content| into the window tree. + void SetChildContent(HWND content); + + // Returns the backing Window handle to enable clients to set icon and other + // window properties. Returns nullptr if the window has been destroyed. + HWND GetHandle(); + + // If true, closing this window will quit the application. + void SetQuitOnClose(bool quit_on_close); + + // Return a RECT representing the bounds of the current client area. + RECT GetClientArea(); + + protected: + // Processes and route salient window messages for mouse handling, + // size change and DPI. Delegates handling of these to member overloads that + // inheriting classes can handle. + virtual LRESULT MessageHandler(HWND window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Called when CreateAndShow is called, allowing subclass window-related + // setup. Subclasses should return false if setup fails. + virtual bool OnCreate(); + + // Called when Destroy is called. + virtual void OnDestroy(); + + private: + friend class WindowClassRegistrar; + + // OS callback called by message pump. Handles the WM_NCCREATE message which + // is passed when the non-client area is being created and enables automatic + // non-client DPI scaling so that the non-client area automatically + // responds to changes in DPI. All other messages are handled by + // MessageHandler. + static LRESULT CALLBACK WndProc(HWND const window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Retrieves a class instance pointer for |window| + static Win32Window* GetThisFromHandle(HWND const window) noexcept; + + // Update the window frame's theme to match the system theme. + static void UpdateTheme(HWND const window); + + bool quit_on_close_ = false; + + // window handle for top level window. + HWND window_handle_ = nullptr; + + // window handle for hosted content. + HWND child_content_ = nullptr; +}; + +#endif // RUNNER_WIN32_WINDOW_H_