kasy-cli 1.21.8 → 1.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/commands/add.js +93 -80
- package/lib/commands/configure.js +100 -32
- package/lib/commands/doctor.js +28 -2
- package/lib/commands/new.js +86 -38
- package/lib/commands/notifications.js +1 -1
- package/lib/commands/remove.js +43 -15
- package/lib/commands/run.js +2 -2
- package/lib/commands/update.js +2 -2
- package/lib/scaffold/CHANGELOG.json +14 -0
- package/lib/scaffold/backends/api/generator.js +14 -14
- package/lib/scaffold/backends/api/patch/README.md +83 -0
- package/lib/scaffold/backends/api/patch/lib/core/data/api/storage_api.dart +1 -1
- package/lib/scaffold/backends/api/patch/lib/core/data/entities/user_entity.dart +5 -0
- package/lib/scaffold/backends/api/patch/lib/{environnements.dart → environments.dart} +3 -11
- package/lib/scaffold/backends/api/patch/lib/features/ai_chat/api/ai_chat_api.dart +108 -0
- package/lib/scaffold/backends/api/patch/lib/features/ai_chat/api/ai_chat_conversation_entity.dart +51 -0
- package/lib/scaffold/backends/api/patch/lib/features/{llm_chat/api/llm_chat_message_entity.dart → ai_chat/api/ai_chat_message_entity.dart} +5 -5
- package/lib/scaffold/backends/api/patch/lib/features/{llm_chat/providers/llm_chat_notifier.dart → ai_chat/providers/ai_chat_notifier.dart} +71 -38
- package/lib/scaffold/backends/api/patch/lib/features/authentication/api/authentication_api.dart +2 -2
- package/lib/scaffold/backends/api/patch/lib/features/feedbacks/api/feature_request_api.dart +54 -0
- package/lib/scaffold/backends/api/patch/lib/features/onboarding/models/user_info.dart +16 -16
- package/lib/scaffold/backends/api/patch/lib/features/onboarding/ui/components/onboarding_att_setup.dart +4 -3
- package/lib/scaffold/backends/api/patch/lib/features/onboarding/ui/widgets/onboarding_radio_question.dart +38 -28
- package/lib/scaffold/backends/api/patch/lib/features/settings/ui/components/admin/admin_users_api.dart +100 -0
- package/lib/scaffold/backends/api/patch/lib/features/{subscription → subscriptions}/api/entities/subscription_entity.dart +13 -0
- package/lib/scaffold/backends/api/patch/lib/features/subscriptions/api/stripe_backend_api.dart +60 -0
- package/lib/scaffold/backends/api/patch/lib/features/{subscription → subscriptions}/api/subscription_api.dart +1 -1
- package/lib/scaffold/backends/api/pubspec.yaml.tpl +4 -5
- package/lib/scaffold/backends/firebase/deploy.js +87 -13
- package/lib/scaffold/backends/firebase/enable-auth-via-cli.js +14 -6
- package/lib/scaffold/backends/firebase/generator.js +5 -5
- package/lib/scaffold/backends/firebase/setup-from-scratch.js +69 -45
- package/lib/scaffold/backends/firebase/tokens.js +4 -4
- package/lib/scaffold/backends/supabase/deploy.js +63 -11
- package/lib/scaffold/backends/supabase/edge-functions/admin-list-users/index.ts +149 -0
- package/lib/scaffold/backends/supabase/edge-functions/{llm-chat → ai-chat}/index.ts +19 -19
- package/lib/scaffold/backends/supabase/edge-functions/revenuecat-webhook/index.ts +2 -0
- package/lib/scaffold/backends/supabase/edge-functions/stripe-create-checkout-session/index.ts +123 -0
- package/lib/scaffold/backends/supabase/edge-functions/stripe-create-portal-session/index.ts +97 -0
- package/lib/scaffold/backends/supabase/edge-functions/stripe-list-prices/index.ts +83 -0
- package/lib/scaffold/backends/supabase/edge-functions/stripe-webhook/index.ts +138 -0
- package/lib/scaffold/backends/supabase/generator.js +17 -17
- package/lib/scaffold/backends/supabase/migrations/20240101000009_ai_messages.sql +50 -0
- package/lib/scaffold/backends/supabase/migrations/20240101000012_stripe_customers.sql +36 -0
- package/lib/scaffold/backends/supabase/migrations/20240101000013_admin_role.sql +62 -0
- package/lib/scaffold/backends/supabase/patch/lib/core/data/entities/user_entity.dart +4 -0
- package/lib/scaffold/backends/supabase/patch/lib/{environnements.dart → environments.dart} +3 -13
- package/lib/scaffold/backends/supabase/patch/lib/features/ai_chat/api/ai_chat_api.dart +95 -0
- package/lib/scaffold/backends/supabase/patch/lib/features/ai_chat/api/ai_chat_conversation_entity.dart +52 -0
- package/lib/scaffold/backends/supabase/patch/lib/features/{llm_chat/api/llm_chat_message_entity.dart → ai_chat/api/ai_chat_message_entity.dart} +6 -6
- package/lib/scaffold/backends/supabase/patch/lib/features/{llm_chat/providers/llm_chat_notifier.dart → ai_chat/providers/ai_chat_notifier.dart} +63 -35
- package/lib/scaffold/backends/supabase/patch/lib/features/authentication/api/authentication_api.dart +1 -1
- package/lib/scaffold/backends/supabase/patch/lib/features/feedbacks/api/feature_request_api.dart +46 -0
- package/lib/scaffold/backends/supabase/patch/lib/features/onboarding/models/user_info.dart +16 -16
- package/lib/scaffold/backends/supabase/patch/lib/features/onboarding/ui/components/onboarding_att_setup.dart +4 -3
- package/lib/scaffold/backends/supabase/patch/lib/features/onboarding/ui/widgets/onboarding_radio_question.dart +38 -28
- package/lib/scaffold/backends/supabase/patch/lib/features/settings/ui/components/admin/admin_users_api.dart +93 -0
- package/lib/scaffold/backends/supabase/patch/lib/features/{subscription → subscriptions}/api/entities/subscription_entity.dart +13 -0
- package/lib/scaffold/backends/supabase/patch/lib/features/subscriptions/api/stripe_backend_api.dart +52 -0
- package/lib/scaffold/backends/supabase/patch/lib/features/{subscription → subscriptions}/api/subscription_api.dart +1 -1
- package/lib/scaffold/backends/supabase/pubspec.yaml.tpl +4 -5
- package/lib/scaffold/backends/supabase/tokens.js +3 -3
- package/lib/scaffold/catalog.js +9 -11
- package/lib/scaffold/generate.js +45 -31
- package/lib/scaffold/shared/generator-utils.js +188 -81
- package/lib/scaffold/shared/sort-imports.js +191 -0
- package/lib/scaffold/shared/template-strings.js +3 -3
- package/lib/utils/checks.js +2 -2
- package/lib/utils/i18n/messages-en.js +50 -35
- package/lib/utils/i18n/messages-es.js +50 -35
- package/lib/utils/i18n/messages-pt.js +52 -37
- package/lib/utils/updates.js +15 -15
- package/package.json +1 -1
- package/templates/firebase/.env.example +2 -2
- package/templates/firebase/android/app/src/main/res/drawable/background.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-night/background.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-night-v21/background.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-v21/background.png +0 -0
- package/templates/firebase/android/app/src/main/res/values-night-v31/styles.xml +1 -1
- package/templates/firebase/android/app/src/main/res/values-v31/styles.xml +1 -1
- package/templates/firebase/assets/images/logo_wordmark_dark.png +0 -0
- package/templates/firebase/assets/images/logo_wordmark_light.png +0 -0
- package/templates/firebase/docs/revenuecat-setup.es.md +1 -1
- package/templates/firebase/docs/revenuecat-setup.pt.md +1 -1
- package/templates/firebase/firestore.rules +24 -5
- package/templates/firebase/functions/package-lock.json +22 -1
- package/templates/firebase/functions/package.json +2 -1
- package/templates/firebase/functions/src/admin/functions.ts +113 -0
- package/templates/firebase/functions/src/{llm_chat → ai_chat}/index.ts +16 -16
- package/templates/firebase/functions/src/index.ts +8 -2
- package/templates/firebase/functions/src/notifications/device_triggers.ts +2 -2
- package/templates/firebase/functions/src/notifications/triggers.ts +3 -3
- package/templates/firebase/functions/src/subscriptions/models/subscription_status.ts +2 -0
- package/templates/firebase/functions/src/subscriptions/stripe_functions.ts +222 -0
- package/templates/firebase/functions/src/subscriptions/subscriptions_functions.ts +2 -2
- package/templates/firebase/ios/Runner/Assets.xcassets/LaunchBackground.imageset/background.png +0 -0
- package/templates/firebase/ios/Runner/Assets.xcassets/LaunchBackground.imageset/darkbackground.png +0 -0
- package/templates/firebase/lib/components/components.dart +4 -1
- package/templates/firebase/lib/components/kasy_app_bar.dart +22 -7
- package/templates/firebase/lib/components/kasy_avatar.dart +7 -6
- package/templates/firebase/lib/components/kasy_button.dart +23 -99
- package/templates/firebase/lib/components/kasy_dialog.dart +11 -11
- package/templates/firebase/lib/components/kasy_image_viewer.dart +84 -0
- package/templates/firebase/lib/components/{kasy_sidebar_pro.dart → kasy_sidebar.dart} +692 -425
- package/templates/firebase/lib/components/kasy_status_tag.dart +91 -0
- package/templates/firebase/lib/components/kasy_text_area.dart +1 -3
- package/templates/firebase/lib/components/kasy_text_field.dart +5 -6
- package/templates/firebase/lib/components/kasy_text_field_otp.dart +1 -3
- package/templates/firebase/lib/components/kasy_toast.dart +2 -2
- package/templates/firebase/lib/components/kasy_web_header.dart +209 -0
- package/templates/firebase/lib/core/ads/ads_provider.dart +1 -1
- package/templates/firebase/lib/core/bottom_menu/bottom_menu.dart +57 -4
- package/templates/firebase/lib/core/bottom_menu/bottom_router.dart +19 -91
- package/templates/firebase/lib/core/bottom_menu/kasy_bottom_bar_factory.dart +196 -96
- package/templates/firebase/lib/core/bottom_menu/web_content_wrapper.dart +41 -0
- package/templates/firebase/lib/core/config/app_env.dart +5 -11
- package/templates/firebase/lib/core/config/features.dart +5 -4
- package/templates/firebase/lib/core/data/api/analytics_api.dart +1 -1
- package/templates/firebase/lib/core/data/api/http_client.dart +1 -1
- package/templates/firebase/lib/core/data/entities/user_entity.dart +3 -0
- package/templates/firebase/lib/core/data/models/entitlement.dart +35 -0
- package/templates/firebase/lib/core/data/models/subscription.dart +13 -186
- package/templates/firebase/lib/core/data/models/user.dart +11 -0
- package/templates/firebase/lib/core/data/repositories/user_repository.dart +1 -1
- package/templates/firebase/lib/core/home_widgets/home_widget_background_task.dart +1 -1
- package/templates/firebase/lib/core/home_widgets/home_widget_mywidget_service.dart +1 -1
- package/templates/firebase/lib/core/icons/kasy_icons.dart +31 -1
- package/templates/firebase/lib/core/rating/api/rating_api.dart +2 -2
- package/templates/firebase/lib/core/states/logout_action.dart +25 -0
- package/templates/firebase/lib/core/states/user_state_notifier.dart +3 -3
- package/templates/firebase/lib/core/theme/colors.dart +488 -188
- package/templates/firebase/lib/core/theme/radius.dart +22 -11
- package/templates/firebase/lib/core/theme/shadows.dart +66 -0
- package/templates/firebase/lib/core/theme/texts.dart +75 -41
- package/templates/firebase/lib/core/theme/universal_theme.dart +9 -4
- package/templates/firebase/lib/core/web_device_preview/web_device_preview.dart +5 -4
- package/templates/firebase/lib/core/web_viewport_scale.dart +52 -0
- package/templates/firebase/lib/core/widgets/kasy_brand_badge.dart +118 -0
- package/templates/firebase/lib/core/widgets/kasy_brand_logo.dart +40 -0
- package/templates/firebase/lib/core/widgets/kasy_focus_ring.dart +125 -0
- package/templates/firebase/lib/core/widgets/kasy_hover.dart +33 -13
- package/templates/firebase/lib/core/widgets/kasy_pressable_depth.dart +18 -13
- package/templates/firebase/lib/{environnements.dart → environments.dart} +3 -14
- package/templates/firebase/lib/features/ai_chat/ai_chat_page.dart +159 -0
- package/templates/firebase/lib/features/ai_chat/api/ai_chat_api.dart +107 -0
- package/templates/firebase/lib/features/ai_chat/api/ai_chat_conversation_entity.dart +64 -0
- package/templates/firebase/lib/features/{llm_chat/api/llm_chat_message_entity.dart → ai_chat/api/ai_chat_message_entity.dart} +6 -6
- package/templates/firebase/lib/features/{llm_chat/providers/llm_chat_notifier.dart → ai_chat/providers/ai_chat_notifier.dart} +73 -38
- package/templates/firebase/lib/features/ai_chat/providers/ai_conversations_notifier.dart +103 -0
- package/templates/firebase/lib/features/{llm_chat/ui/widgets/llm_chat_avatars.dart → ai_chat/ui/widgets/ai_chat_avatars.dart} +13 -13
- package/templates/firebase/lib/features/{llm_chat/ui/widgets/llm_chat_composer.dart → ai_chat/ui/widgets/ai_chat_composer.dart} +10 -10
- package/templates/firebase/lib/features/{llm_chat/llm_chat_page.dart → ai_chat/ui/widgets/ai_chat_conversation_view.dart} +80 -67
- package/templates/firebase/lib/features/ai_chat/ui/widgets/ai_conversation_list.dart +285 -0
- package/templates/firebase/lib/features/ai_chat/ui/widgets/ai_conversation_tile.dart +163 -0
- package/templates/firebase/lib/features/authentication/api/authentication_api.dart +52 -13
- package/templates/firebase/lib/features/authentication/api/popup_dismiss_watcher.dart +12 -0
- package/templates/firebase/lib/features/authentication/api/popup_dismiss_watcher_web.dart +35 -0
- package/templates/firebase/lib/features/authentication/ui/recover_password_page.dart +108 -68
- package/templates/firebase/lib/features/authentication/ui/signin_page.dart +38 -51
- package/templates/firebase/lib/features/authentication/ui/signup_page.dart +38 -51
- package/templates/firebase/lib/features/authentication/ui/widgets/auth_card_scaffold.dart +118 -0
- package/templates/firebase/lib/features/authentication/ui/widgets/recover_password_result.dart +61 -44
- package/templates/firebase/lib/features/feedbacks/api/feature_request_api.dart +32 -0
- package/templates/firebase/lib/features/feedbacks/models/feedback_state.dart +5 -5
- package/templates/firebase/lib/features/feedbacks/providers/feedback_page_notifier.dart +2 -2
- package/templates/firebase/lib/features/home/design_system_page.dart +808 -170
- package/templates/firebase/lib/features/home/home_components_page.dart +6 -3
- package/templates/firebase/lib/features/home/home_components_preview_page.dart +6 -6
- package/templates/firebase/lib/features/home/home_components_preview_registry.dart +325 -186
- package/templates/firebase/lib/features/home/home_feed.dart +289 -0
- package/templates/firebase/lib/features/home/home_image_grid.dart +355 -0
- package/templates/firebase/lib/features/home/home_page.dart +11 -250
- package/templates/firebase/lib/features/{local_reminder → local_reminders}/providers/reminder_notifier.dart +1 -1
- package/templates/firebase/lib/features/{local_reminder → local_reminders}/ui/reminder_page.dart +2 -2
- package/templates/firebase/lib/features/notifications/shared/att_permission.dart +1 -1
- package/templates/firebase/lib/features/notifications/ui/request_notification_permission.dart +25 -61
- package/templates/firebase/lib/features/notifications/ui/widgets/permission_request_view.dart +117 -0
- package/templates/firebase/lib/features/onboarding/models/user_info.dart +16 -16
- package/templates/firebase/lib/features/onboarding/ui/components/onboarding_att_setup.dart +4 -3
- package/templates/firebase/lib/features/onboarding/ui/components/onboarding_features.dart +7 -9
- package/templates/firebase/lib/features/onboarding/ui/components/onboarding_loader.dart +71 -48
- package/templates/firebase/lib/features/onboarding/ui/components/onboarding_notifications_setup.dart +3 -2
- package/templates/firebase/lib/features/onboarding/ui/components/onboarding_questions.dart +5 -5
- package/templates/firebase/lib/features/onboarding/ui/onboarding_page.dart +4 -4
- package/templates/firebase/lib/features/onboarding/ui/widgets/onboarding_background.dart +4 -2
- package/templates/firebase/lib/features/onboarding/ui/widgets/onboarding_feature.dart +39 -121
- package/templates/firebase/lib/features/onboarding/ui/widgets/onboarding_illustration_scaffold.dart +105 -70
- package/templates/firebase/lib/features/onboarding/ui/widgets/onboarding_module_mockups.dart +639 -0
- package/templates/firebase/lib/features/onboarding/ui/widgets/onboarding_progress.dart +62 -50
- package/templates/firebase/lib/features/onboarding/ui/widgets/onboarding_radio_question.dart +38 -28
- package/templates/firebase/lib/features/onboarding/ui/widgets/onboarding_step_header.dart +75 -0
- package/templates/firebase/lib/features/onboarding/ui/widgets/onboarding_sticky_footer.dart +16 -5
- package/templates/firebase/lib/features/onboarding/ui/widgets/selectable_row_tile.dart +26 -22
- package/templates/firebase/lib/features/settings/settings_page.dart +601 -90
- package/templates/firebase/lib/features/settings/ui/components/admin/admin_page.dart +1193 -0
- package/templates/firebase/lib/features/settings/ui/components/admin/admin_paywalls.dart +1 -1
- package/templates/firebase/lib/features/settings/ui/components/admin/admin_routes.dart +2 -3
- package/templates/firebase/lib/features/settings/ui/components/admin/admin_users_api.dart +86 -0
- package/templates/firebase/lib/features/settings/ui/components/admin/admin_users_tab.dart +1215 -0
- package/templates/firebase/lib/features/settings/ui/components/admin/send_push_notification_page.dart +236 -0
- package/templates/firebase/lib/features/settings/ui/components/avatar_component.dart +3 -3
- package/templates/firebase/lib/features/settings/ui/widgets/kasy_user_avatar.dart +77 -0
- package/templates/firebase/lib/features/settings/ui/widgets/settings_tile.dart +1 -0
- package/templates/firebase/lib/features/{subscription → subscriptions}/api/entities/subscription_entity.dart +17 -0
- package/templates/firebase/lib/features/{subscription → subscriptions}/api/inapp_subscription_api.dart +67 -46
- package/templates/firebase/lib/features/subscriptions/api/revenuecat_product.dart +189 -0
- package/templates/firebase/lib/features/subscriptions/api/stripe_backend_api.dart +55 -0
- package/templates/firebase/lib/features/subscriptions/api/stripe_payment_api.dart +82 -0
- package/templates/firebase/lib/features/subscriptions/api/stripe_product.dart +178 -0
- package/templates/firebase/lib/features/{subscription → subscriptions}/api/subscription_api.dart +1 -1
- package/templates/firebase/lib/features/subscriptions/api/subscription_payment_api.dart +53 -0
- package/templates/firebase/lib/features/subscriptions/api/subscription_payment_api_provider.dart +21 -0
- package/templates/firebase/lib/features/{subscription → subscriptions}/providers/premium_page_provider.dart +9 -6
- package/templates/firebase/lib/features/{subscription → subscriptions}/repositories/subscription_repository.dart +26 -30
- package/{lib/scaffold/backends/supabase/patch/lib/features/subscription → templates/firebase/lib/features/subscriptions}/shared/maybeshow_premium.dart +0 -2
- package/templates/firebase/lib/features/subscriptions/shared/subscription_management.dart +45 -0
- package/templates/firebase/lib/features/{subscription → subscriptions}/ui/component/active_premium_content.dart +28 -4
- package/templates/firebase/lib/features/{subscription → subscriptions}/ui/component/paywall_minimal.dart +7 -7
- package/templates/firebase/lib/features/{subscription → subscriptions}/ui/component/paywall_row.dart +8 -8
- package/templates/firebase/lib/features/{subscription → subscriptions}/ui/component/paywall_with_switch.dart +7 -7
- package/templates/firebase/lib/features/{subscription → subscriptions}/ui/component/premium_content.dart +7 -7
- package/templates/firebase/lib/features/{subscription → subscriptions}/ui/component/premium_page_factory.dart +5 -5
- package/templates/firebase/lib/features/{subscription → subscriptions}/ui/premium_page.dart +5 -5
- package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/comparison_table.dart +1 -1
- package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/paywall_empty_state.dart +4 -4
- package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/premium_bottom_menu.dart +3 -4
- package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/selectable_col.dart +1 -1
- package/templates/firebase/lib/i18n/en.i18n.json +171 -46
- package/templates/firebase/lib/i18n/es.i18n.json +175 -50
- package/templates/firebase/lib/i18n/pt.i18n.json +166 -41
- package/templates/firebase/lib/main.dart +6 -3
- package/templates/firebase/lib/router.dart +15 -23
- package/templates/firebase/pubspec.yaml +4 -5
- package/templates/firebase/test/core/data/repositories/user_repository_test.dart +3 -3
- package/templates/firebase/test/core/states/user_state_notifier_test.dart +4 -4
- package/templates/firebase/test/core/widgets/focus_ring_shape_test.dart +55 -0
- package/templates/firebase/test/features/{subscription → subscriptions}/api/fake_inapp_subscription_api.dart +11 -4
- package/templates/firebase/test/features/{subscription → subscriptions}/api/fake_revenuecat_product.dart +1 -0
- package/templates/firebase/test/features/{subscription → subscriptions}/api/fake_subscription_api.dart +2 -2
- package/templates/firebase/test/features/{subscription → subscriptions}/subscription_page_test.dart +4 -4
- package/templates/firebase/test/test_utils.dart +6 -6
- package/templates/firebase/web/index.html +5 -2
- package/lib/scaffold/backends/api/patch/lib/features/llm_chat/api/llm_chat_api.dart +0 -58
- package/lib/scaffold/backends/api/patch/lib/features/subscription/shared/maybeshow_premium.dart +0 -86
- package/lib/scaffold/backends/supabase/migrations/20240101000009_llm_messages.sql +0 -22
- package/lib/scaffold/backends/supabase/patch/lib/features/llm_chat/api/llm_chat_api.dart +0 -47
- package/templates/firebase/assets/images/onboarding/authentication-login-template.jpg +0 -0
- package/templates/firebase/assets/images/onboarding/img2.jpg +0 -0
- package/templates/firebase/assets/images/onboarding/img3.jpg +0 -0
- package/templates/firebase/assets/images/onboarding/notifications.png +0 -0
- package/templates/firebase/assets/images/onboarding/purchase.png +0 -0
- package/templates/firebase/lib/core/sidebar/kasy_sidebar.dart +0 -2021
- package/templates/firebase/lib/features/authentication/ui/widgets/auth_brand.dart +0 -35
- package/templates/firebase/lib/features/home/home_features_page.dart +0 -207
- package/templates/firebase/lib/features/llm_chat/api/llm_chat_api.dart +0 -50
- package/templates/firebase/lib/features/settings/ui/components/admin/admin_bottom_sheet.dart +0 -316
- package/templates/firebase/lib/features/subscription/shared/maybeshow_premium.dart +0 -85
- /package/templates/firebase/lib/features/{local_reminder → local_reminders}/repositories/reminder_preferences.dart +0 -0
- /package/templates/firebase/lib/features/{subscription → subscriptions}/providers/models/premium_state.dart +0 -0
- /package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/feature_line.dart +0 -0
- /package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/premium_background.dart +0 -0
- /package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/premium_background_gradient.dart +0 -0
- /package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/premium_banner.dart +0 -0
- /package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/premium_card.dart +0 -0
- /package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/premium_close_button.dart +0 -0
- /package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/premium_feature.dart +0 -0
- /package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/selectable_row.dart +0 -0
- /package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/trial_switcher.dart +0 -0
- /package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/unsubscribe_feedback_popup.dart +0 -0
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import 'package:flutter/material.dart';
|
|
2
|
+
import 'package:kasy_kit/core/theme/theme.dart';
|
|
3
|
+
|
|
4
|
+
/// Accent tone for [KasyStatusTag], resolved against the current theme.
|
|
5
|
+
enum KasyStatusTagTone { success, neutral, primary, warning, danger }
|
|
6
|
+
|
|
7
|
+
/// Compact, content-sized status pill: a soft-tinted rounded badge with an
|
|
8
|
+
/// optional leading dot and a colored label. Built for table cells, list rows
|
|
9
|
+
/// and detail headers where you want a quiet status indicator (active/inactive,
|
|
10
|
+
/// paid/free, visible/hidden, …) — not a loud solid badge.
|
|
11
|
+
///
|
|
12
|
+
/// It sizes to its content. Inside a tight parent (e.g. a table column wrapped
|
|
13
|
+
/// in [Expanded]) wrap it in an [Align] so it doesn't stretch:
|
|
14
|
+
///
|
|
15
|
+
/// ```dart
|
|
16
|
+
/// Expanded(
|
|
17
|
+
/// child: Align(
|
|
18
|
+
/// alignment: Alignment.centerLeft,
|
|
19
|
+
/// child: KasyStatusTag(label: 'Active', tone: KasyStatusTagTone.success),
|
|
20
|
+
/// ),
|
|
21
|
+
/// )
|
|
22
|
+
/// ```
|
|
23
|
+
class KasyStatusTag extends StatelessWidget {
|
|
24
|
+
/// The text shown in the pill.
|
|
25
|
+
final String label;
|
|
26
|
+
|
|
27
|
+
/// Semantic tone (maps to theme colors). Ignored when [color] is set.
|
|
28
|
+
final KasyStatusTagTone tone;
|
|
29
|
+
|
|
30
|
+
/// Overrides the tone's accent color (used for both the dot and the label).
|
|
31
|
+
final Color? color;
|
|
32
|
+
|
|
33
|
+
/// Whether to show the small leading status dot.
|
|
34
|
+
final bool showDot;
|
|
35
|
+
|
|
36
|
+
const KasyStatusTag({
|
|
37
|
+
super.key,
|
|
38
|
+
required this.label,
|
|
39
|
+
this.tone = KasyStatusTagTone.neutral,
|
|
40
|
+
this.color,
|
|
41
|
+
this.showDot = true,
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
@override
|
|
45
|
+
Widget build(BuildContext context) {
|
|
46
|
+
final Color accent = color ?? _toneColor(context, tone);
|
|
47
|
+
return Container(
|
|
48
|
+
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
|
|
49
|
+
decoration: BoxDecoration(
|
|
50
|
+
color: accent.withValues(alpha: context.isDark ? 0.16 : 0.1),
|
|
51
|
+
borderRadius: BorderRadius.circular(KasyRadius.full),
|
|
52
|
+
),
|
|
53
|
+
child: Row(
|
|
54
|
+
mainAxisSize: MainAxisSize.min,
|
|
55
|
+
children: [
|
|
56
|
+
if (showDot) ...[
|
|
57
|
+
Container(
|
|
58
|
+
width: 6,
|
|
59
|
+
height: 6,
|
|
60
|
+
decoration: BoxDecoration(color: accent, shape: BoxShape.circle),
|
|
61
|
+
),
|
|
62
|
+
const SizedBox(width: 6),
|
|
63
|
+
],
|
|
64
|
+
Flexible(
|
|
65
|
+
child: Text(
|
|
66
|
+
label,
|
|
67
|
+
maxLines: 1,
|
|
68
|
+
overflow: TextOverflow.ellipsis,
|
|
69
|
+
style: context.textTheme.labelSmall?.copyWith(
|
|
70
|
+
color: accent,
|
|
71
|
+
fontWeight: FontWeight.w600,
|
|
72
|
+
fontSize: 11.5,
|
|
73
|
+
),
|
|
74
|
+
),
|
|
75
|
+
),
|
|
76
|
+
],
|
|
77
|
+
),
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
Color _toneColor(BuildContext context, KasyStatusTagTone tone) {
|
|
82
|
+
final c = context.colors;
|
|
83
|
+
return switch (tone) {
|
|
84
|
+
KasyStatusTagTone.success => c.success,
|
|
85
|
+
KasyStatusTagTone.neutral => c.muted,
|
|
86
|
+
KasyStatusTagTone.primary => c.primary,
|
|
87
|
+
KasyStatusTagTone.warning => c.warning,
|
|
88
|
+
KasyStatusTagTone.danger => c.error,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
}
|
|
@@ -152,9 +152,7 @@ class _KasyTextAreaState extends State<KasyTextArea> {
|
|
|
152
152
|
final bool isApplePlatform =
|
|
153
153
|
platform == TargetPlatform.iOS || platform == TargetPlatform.macOS;
|
|
154
154
|
final double focusedBorderWidth = isApplePlatform ? 1.7 : 1.4;
|
|
155
|
-
final Color focusedBorderColor =
|
|
156
|
-
? const Color(0xFF0A84FF)
|
|
157
|
-
: context.colors.primary;
|
|
155
|
+
final Color focusedBorderColor = context.colors.primary;
|
|
158
156
|
final Color resolvedFocusedBorderColor = hasInvalidState
|
|
159
157
|
? context.colors.error
|
|
160
158
|
: focusedBorderColor;
|
|
@@ -67,8 +67,9 @@ class KasyTextField extends StatefulWidget {
|
|
|
67
67
|
/// on secondary/embedded/web). Pass an empty list to render no shadow.
|
|
68
68
|
final List<BoxShadow>? boxShadow;
|
|
69
69
|
|
|
70
|
-
/// When true (default), the field shows the
|
|
71
|
-
/// while focused. Set to false to keep the resting border in every
|
|
70
|
+
/// When true (default), the field shows the theme primary-colored focus
|
|
71
|
+
/// border while focused. Set to false to keep the resting border in every
|
|
72
|
+
/// state —
|
|
72
73
|
/// useful for read-only triggers or compact contexts where the focus
|
|
73
74
|
/// affordance would feel noisy.
|
|
74
75
|
final bool focusBorder;
|
|
@@ -331,9 +332,7 @@ class _KasyTextFieldState extends State<KasyTextField> {
|
|
|
331
332
|
final bool isApplePlatform =
|
|
332
333
|
platform == TargetPlatform.iOS || platform == TargetPlatform.macOS;
|
|
333
334
|
final double focusedBorderWidth = isApplePlatform ? 1.7 : 1.4;
|
|
334
|
-
final Color focusedBorderColor =
|
|
335
|
-
? const Color(0xFF0A84FF)
|
|
336
|
-
: context.colors.primary;
|
|
335
|
+
final Color focusedBorderColor = context.colors.primary;
|
|
337
336
|
final Color resolvedFocusedBorderColor = hasInvalidState
|
|
338
337
|
? context.colors.error
|
|
339
338
|
: focusedBorderColor;
|
|
@@ -538,7 +537,7 @@ class _KasyTextFieldState extends State<KasyTextField> {
|
|
|
538
537
|
decoration: decoration,
|
|
539
538
|
enableInteractiveSelection: widget.enableInteractiveSelection,
|
|
540
539
|
);
|
|
541
|
-
//
|
|
540
|
+
// TEST: shadow disabled to investigate the visual difference with DatePicker
|
|
542
541
|
final List<BoxShadow>? effectiveBoxShadow = widget.boxShadow ??
|
|
543
542
|
(shouldShowShadow ? <BoxShadow>[resolvedShadow] : null);
|
|
544
543
|
final Widget decoratedField = DecoratedBox(
|
|
@@ -142,9 +142,7 @@ class _KasyTextFieldOTPState extends State<KasyTextFieldOTP> {
|
|
|
142
142
|
final bool isApple =
|
|
143
143
|
Theme.of(context).platform == TargetPlatform.iOS ||
|
|
144
144
|
Theme.of(context).platform == TargetPlatform.macOS;
|
|
145
|
-
final Color focusColor =
|
|
146
|
-
? const Color(0xFF0A84FF)
|
|
147
|
-
: context.colors.primary;
|
|
145
|
+
final Color focusColor = context.colors.primary;
|
|
148
146
|
final double focusBorderWidth = isApple ? 1.7 : 1.4;
|
|
149
147
|
final Color errorColor = context.colors.error;
|
|
150
148
|
|
|
@@ -501,8 +501,8 @@ class _KasyToastLayerState extends State<_KasyToastLayer>
|
|
|
501
501
|
final int fromFront = visible.indexOf(item);
|
|
502
502
|
final bool isFront = fromFront == 0;
|
|
503
503
|
|
|
504
|
-
//
|
|
505
|
-
//
|
|
504
|
+
// For top: use the measured height so the peek is always _peekOffset px.
|
|
505
|
+
// For bottom: a fixed negative offset already guarantees a consistent peek.
|
|
506
506
|
double yOffset;
|
|
507
507
|
if (isTop && frontHeight != null) {
|
|
508
508
|
final double cardH = _heights[item] ?? frontHeight;
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
/// Application top header for the **web desktop breakpoint** (viewport ≥ 1024px).
|
|
2
|
+
///
|
|
3
|
+
/// This is the *application* chrome (global search, quick-create, notifications,
|
|
4
|
+
/// profile) — distinct from [KasyAppBar], which is *page* chrome (title / back /
|
|
5
|
+
/// theme) used on phone and tablet. On desktop the sidebar handles navigation,
|
|
6
|
+
/// so this header carries global actions instead of a title or back button.
|
|
7
|
+
///
|
|
8
|
+
/// Composed entirely from existing kit widgets: a [KasyTextField] search box
|
|
9
|
+
/// (fixed 220px), a ghost [KasyButton.iconOnly] for notifications, a neutral
|
|
10
|
+
/// pill [KasyButton] for create, and a gradient [KasyAvatar].
|
|
11
|
+
///
|
|
12
|
+
/// Barrel: [components.dart].
|
|
13
|
+
library;
|
|
14
|
+
|
|
15
|
+
import 'package:flutter/material.dart';
|
|
16
|
+
import 'package:kasy_kit/components/kasy_avatar.dart';
|
|
17
|
+
import 'package:kasy_kit/components/kasy_avatar_presets.dart';
|
|
18
|
+
import 'package:kasy_kit/components/kasy_button.dart';
|
|
19
|
+
import 'package:kasy_kit/components/kasy_text_field.dart';
|
|
20
|
+
import 'package:kasy_kit/core/theme/theme.dart';
|
|
21
|
+
|
|
22
|
+
/// Total header height (matches the design: 36px content band + 16px top/bottom).
|
|
23
|
+
const double kasyWebHeaderHeight = 68;
|
|
24
|
+
|
|
25
|
+
/// Fixed width of the leading search field (matches the design).
|
|
26
|
+
const double kasyWebHeaderSearchWidth = 220;
|
|
27
|
+
|
|
28
|
+
/// Desktop application header. Place it at the top of the content area (to the
|
|
29
|
+
/// right of the sidebar) on viewports ≥ 1024px.
|
|
30
|
+
class KasyWebHeader extends StatelessWidget {
|
|
31
|
+
/// Controller for the search field. Optional — omit for a display-only header.
|
|
32
|
+
final TextEditingController? searchController;
|
|
33
|
+
|
|
34
|
+
/// Placeholder shown in the search field.
|
|
35
|
+
final String searchHint;
|
|
36
|
+
|
|
37
|
+
/// Called as the user types in the search field.
|
|
38
|
+
final ValueChanged<String>? onSearchChanged;
|
|
39
|
+
|
|
40
|
+
/// Called when the search field is submitted (Enter).
|
|
41
|
+
final ValueChanged<String>? onSearchSubmitted;
|
|
42
|
+
|
|
43
|
+
/// Notifications (bell) action. When null the bell is disabled.
|
|
44
|
+
final VoidCallback? onNotifications;
|
|
45
|
+
|
|
46
|
+
/// Shows the unread dot on the notifications bell.
|
|
47
|
+
final bool showNotificationBadge;
|
|
48
|
+
|
|
49
|
+
/// Primary quick-create action. When null the button is disabled.
|
|
50
|
+
final VoidCallback? onCreate;
|
|
51
|
+
|
|
52
|
+
/// Label for the create button.
|
|
53
|
+
final String createLabel;
|
|
54
|
+
|
|
55
|
+
/// Gradient used for the profile avatar fallback (when [avatar] is null).
|
|
56
|
+
final KasyAvatarGradientData avatarGradient;
|
|
57
|
+
|
|
58
|
+
/// Custom avatar widget — pass the signed-in user's avatar (e.g.
|
|
59
|
+
/// `KasyUserAvatar`) to show their real photo. When null (and [showAvatar] is
|
|
60
|
+
/// true), a gradient-fill avatar is shown instead.
|
|
61
|
+
final Widget? avatar;
|
|
62
|
+
|
|
63
|
+
/// Whether the profile avatar is shown at all. Set false for a header that
|
|
64
|
+
/// carries no account chip (e.g. when the sidebar already owns the profile).
|
|
65
|
+
final bool showAvatar;
|
|
66
|
+
|
|
67
|
+
/// Profile avatar tap (open menu / profile). When null the avatar is inert.
|
|
68
|
+
final VoidCallback? onAvatarTap;
|
|
69
|
+
|
|
70
|
+
/// Theme toggle. When set, a sun/moon ghost button is shown before the bell
|
|
71
|
+
/// (on desktop the web header replaces the app bar's theme toggle).
|
|
72
|
+
final VoidCallback? onToggleTheme;
|
|
73
|
+
|
|
74
|
+
const KasyWebHeader({
|
|
75
|
+
super.key,
|
|
76
|
+
this.searchController,
|
|
77
|
+
this.searchHint = 'Search...',
|
|
78
|
+
this.onSearchChanged,
|
|
79
|
+
this.onSearchSubmitted,
|
|
80
|
+
this.onNotifications,
|
|
81
|
+
this.showNotificationBadge = false,
|
|
82
|
+
this.onCreate,
|
|
83
|
+
this.createLabel = 'Create',
|
|
84
|
+
this.avatarGradient = KasyAvatarGradients.orange,
|
|
85
|
+
this.avatar,
|
|
86
|
+
this.showAvatar = true,
|
|
87
|
+
this.onAvatarTap,
|
|
88
|
+
this.onToggleTheme,
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
@override
|
|
92
|
+
Widget build(BuildContext context) {
|
|
93
|
+
final KasyColors c = context.colors;
|
|
94
|
+
return DecoratedBox(
|
|
95
|
+
decoration: BoxDecoration(
|
|
96
|
+
// Matches KasySidebar exactly: surface fill + the same `border` hairline
|
|
97
|
+
// used by the sidebar's vertical edge line, at the same 0.5px width — so
|
|
98
|
+
// the header's bottom border and the sidebar's top divider read as one
|
|
99
|
+
// continuous line across the whole chrome in light and dark.
|
|
100
|
+
color: c.surface,
|
|
101
|
+
border: Border(
|
|
102
|
+
bottom: BorderSide(color: c.border, width: 0.5),
|
|
103
|
+
),
|
|
104
|
+
),
|
|
105
|
+
// minHeight (not a fixed height) keeps the band at 68 while letting the
|
|
106
|
+
// search field size naturally, so it never overflows the toolbar row.
|
|
107
|
+
child: ConstrainedBox(
|
|
108
|
+
constraints: const BoxConstraints(minHeight: kasyWebHeaderHeight),
|
|
109
|
+
child: Padding(
|
|
110
|
+
// Design gutters: 20px horizontal; vertical breathes around the field.
|
|
111
|
+
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12),
|
|
112
|
+
child: Row(
|
|
113
|
+
children: [
|
|
114
|
+
SizedBox(
|
|
115
|
+
width: kasyWebHeaderSearchWidth,
|
|
116
|
+
child: _buildSearch(context),
|
|
117
|
+
),
|
|
118
|
+
const Spacer(),
|
|
119
|
+
if (onToggleTheme != null) ...[
|
|
120
|
+
_buildThemeToggle(context),
|
|
121
|
+
const SizedBox(width: KasySpacing.md),
|
|
122
|
+
],
|
|
123
|
+
_buildNotifications(context),
|
|
124
|
+
const SizedBox(width: KasySpacing.md),
|
|
125
|
+
KasyButton(
|
|
126
|
+
label: createLabel,
|
|
127
|
+
variant: KasyButtonVariant.neutral,
|
|
128
|
+
size: KasyButtonSize.small,
|
|
129
|
+
onPressed: onCreate,
|
|
130
|
+
),
|
|
131
|
+
if (showAvatar) ...[
|
|
132
|
+
const SizedBox(width: KasySpacing.md),
|
|
133
|
+
avatar ??
|
|
134
|
+
KasyAvatar.gradientFill(
|
|
135
|
+
size: KasyAvatarSize.small,
|
|
136
|
+
diameter: 36,
|
|
137
|
+
gradient: avatarGradient,
|
|
138
|
+
showShadow: false,
|
|
139
|
+
onTap: onAvatarTap,
|
|
140
|
+
),
|
|
141
|
+
],
|
|
142
|
+
],
|
|
143
|
+
),
|
|
144
|
+
),
|
|
145
|
+
),
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
Widget _buildSearch(BuildContext context) {
|
|
150
|
+
return KasyTextField(
|
|
151
|
+
controller: searchController,
|
|
152
|
+
hint: searchHint,
|
|
153
|
+
onChanged: onSearchChanged,
|
|
154
|
+
onSubmitted: onSearchSubmitted,
|
|
155
|
+
prefix: Icon(
|
|
156
|
+
KasyIcons.search,
|
|
157
|
+
size: 17,
|
|
158
|
+
color: context.colors.muted,
|
|
159
|
+
),
|
|
160
|
+
contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
Widget _buildThemeToggle(BuildContext context) {
|
|
165
|
+
final bool isDark = Theme.of(context).brightness == Brightness.dark;
|
|
166
|
+
return KasyButton.iconOnly(
|
|
167
|
+
icon: isDark ? KasyIcons.lightMode : KasyIcons.darkMode,
|
|
168
|
+
variant: KasyButtonVariant.ghost,
|
|
169
|
+
size: KasyButtonSize.small,
|
|
170
|
+
iconOnlyLayoutExtent: 36,
|
|
171
|
+
iconGlyphSize: 18,
|
|
172
|
+
onPressed: onToggleTheme,
|
|
173
|
+
semanticLabel: isDark ? 'Light mode' : 'Dark mode',
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
Widget _buildNotifications(BuildContext context) {
|
|
178
|
+
final KasyColors c = context.colors;
|
|
179
|
+
final Widget bell = KasyButton.iconOnly(
|
|
180
|
+
icon: KasyIcons.notification,
|
|
181
|
+
variant: KasyButtonVariant.ghost,
|
|
182
|
+
size: KasyButtonSize.small,
|
|
183
|
+
iconOnlyLayoutExtent: 36,
|
|
184
|
+
iconGlyphSize: 18,
|
|
185
|
+
onPressed: onNotifications,
|
|
186
|
+
semanticLabel: 'Notifications',
|
|
187
|
+
);
|
|
188
|
+
if (!showNotificationBadge) return bell;
|
|
189
|
+
return Stack(
|
|
190
|
+
clipBehavior: Clip.none,
|
|
191
|
+
children: [
|
|
192
|
+
bell,
|
|
193
|
+
Positioned(
|
|
194
|
+
top: 8,
|
|
195
|
+
right: 8,
|
|
196
|
+
child: Container(
|
|
197
|
+
width: 8,
|
|
198
|
+
height: 8,
|
|
199
|
+
decoration: BoxDecoration(
|
|
200
|
+
color: c.error,
|
|
201
|
+
shape: BoxShape.circle,
|
|
202
|
+
border: Border.all(color: c.background, width: 1.5),
|
|
203
|
+
),
|
|
204
|
+
),
|
|
205
|
+
),
|
|
206
|
+
],
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// ignore_for_file: unused_element, unused_element_parameter
|
|
2
2
|
|
|
3
3
|
import 'package:kasy_kit/core/ads/ads_state.dart';
|
|
4
|
-
import 'package:kasy_kit/
|
|
4
|
+
import 'package:kasy_kit/environments.dart';
|
|
5
5
|
import 'package:logger/logger.dart';
|
|
6
6
|
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
|
7
7
|
import 'package:universal_io/io.dart';
|
|
@@ -2,11 +2,17 @@ import 'package:bart/bart.dart' as bart;
|
|
|
2
2
|
import 'package:flutter/foundation.dart';
|
|
3
3
|
import 'package:flutter/material.dart';
|
|
4
4
|
import 'package:flutter/services.dart';
|
|
5
|
-
import 'package:
|
|
5
|
+
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
6
|
+
import 'package:kasy_kit/components/kasy_sidebar.dart';
|
|
6
7
|
import 'package:kasy_kit/core/bottom_menu/bottom_router.dart';
|
|
7
8
|
import 'package:kasy_kit/core/bottom_menu/kasy_bottom_bar_factory.dart';
|
|
9
|
+
import 'package:kasy_kit/core/data/models/user.dart';
|
|
10
|
+
import 'package:kasy_kit/core/states/logout_action.dart';
|
|
11
|
+
import 'package:kasy_kit/core/states/user_state_notifier.dart';
|
|
8
12
|
import 'package:kasy_kit/core/theme/theme.dart';
|
|
9
13
|
import 'package:kasy_kit/core/widgets/responsive_layout.dart';
|
|
14
|
+
import 'package:kasy_kit/features/settings/ui/widgets/kasy_user_avatar.dart';
|
|
15
|
+
import 'package:kasy_kit/i18n/translations.g.dart';
|
|
10
16
|
|
|
11
17
|
/// Bottom navigation host powered by Bart (https://pub.dev/packages/bart).
|
|
12
18
|
///
|
|
@@ -27,6 +33,9 @@ class BottomMenu extends StatelessWidget {
|
|
|
27
33
|
);
|
|
28
34
|
final scaffoldOptions = bart.ScaffoldOptions(
|
|
29
35
|
backgroundColor: context.colors.background,
|
|
36
|
+
// Let tab content scroll behind the floating bar; the page-level scrolls
|
|
37
|
+
// reserve the bar's height via the bottom inset Scaffold injects here.
|
|
38
|
+
extendBody: true,
|
|
30
39
|
);
|
|
31
40
|
|
|
32
41
|
return AnnotatedRegion<SystemUiOverlayStyle>(
|
|
@@ -42,7 +51,7 @@ class BottomMenu extends StatelessWidget {
|
|
|
42
51
|
showBottomBarOnStart: showBottomBarOnStart,
|
|
43
52
|
scaffoldOptions: scaffoldOptions,
|
|
44
53
|
),
|
|
45
|
-
// medium (768–1024 px):
|
|
54
|
+
// medium (768–1024 px): KasySidebar auto-collapses at this width
|
|
46
55
|
medium: bart.BartScaffold(
|
|
47
56
|
routesBuilder: subRoutes,
|
|
48
57
|
bottomBar: kasyPaddedSurfaceBottomBar(),
|
|
@@ -50,7 +59,29 @@ class BottomMenu extends StatelessWidget {
|
|
|
50
59
|
showBottomBarOnStart: showBottomBarOnStart,
|
|
51
60
|
scaffoldOptions: scaffoldOptions,
|
|
52
61
|
sideBarOptions: bart.CustomSideBarOptions(
|
|
53
|
-
sideBarBuilder: (routes, onTap, current) =>
|
|
62
|
+
sideBarBuilder: (routes, onTap, current) => Consumer(
|
|
63
|
+
builder: (context, ref, _) {
|
|
64
|
+
final User user = ref.watch(userStateNotifierProvider).user;
|
|
65
|
+
final (String name, String email) = switch (user) {
|
|
66
|
+
final AuthenticatedUserData u => (
|
|
67
|
+
(u.name?.isNotEmpty ?? false)
|
|
68
|
+
? u.name!
|
|
69
|
+
: u.email.split('@').first,
|
|
70
|
+
u.email,
|
|
71
|
+
),
|
|
72
|
+
_ => (context.t.settings.my_account, ''),
|
|
73
|
+
};
|
|
74
|
+
return KasySidebar(
|
|
75
|
+
routes: routes,
|
|
76
|
+
onTapItem: onTap,
|
|
77
|
+
currentItem: current,
|
|
78
|
+
onLogout: () => confirmLogout(context, ref),
|
|
79
|
+
profileName: name,
|
|
80
|
+
profileEmail: email,
|
|
81
|
+
profileAvatar: const KasyUserAvatar(),
|
|
82
|
+
);
|
|
83
|
+
},
|
|
84
|
+
),
|
|
54
85
|
),
|
|
55
86
|
),
|
|
56
87
|
// large (1024 px+): full expanded sidebar
|
|
@@ -61,7 +92,29 @@ class BottomMenu extends StatelessWidget {
|
|
|
61
92
|
showBottomBarOnStart: showBottomBarOnStart,
|
|
62
93
|
scaffoldOptions: scaffoldOptions,
|
|
63
94
|
sideBarOptions: bart.CustomSideBarOptions(
|
|
64
|
-
sideBarBuilder: (routes, onTap, current) =>
|
|
95
|
+
sideBarBuilder: (routes, onTap, current) => Consumer(
|
|
96
|
+
builder: (context, ref, _) {
|
|
97
|
+
final User user = ref.watch(userStateNotifierProvider).user;
|
|
98
|
+
final (String name, String email) = switch (user) {
|
|
99
|
+
final AuthenticatedUserData u => (
|
|
100
|
+
(u.name?.isNotEmpty ?? false)
|
|
101
|
+
? u.name!
|
|
102
|
+
: u.email.split('@').first,
|
|
103
|
+
u.email,
|
|
104
|
+
),
|
|
105
|
+
_ => (context.t.settings.my_account, ''),
|
|
106
|
+
};
|
|
107
|
+
return KasySidebar(
|
|
108
|
+
routes: routes,
|
|
109
|
+
onTapItem: onTap,
|
|
110
|
+
currentItem: current,
|
|
111
|
+
onLogout: () => confirmLogout(context, ref),
|
|
112
|
+
profileName: name,
|
|
113
|
+
profileEmail: email,
|
|
114
|
+
profileAvatar: const KasyUserAvatar(),
|
|
115
|
+
);
|
|
116
|
+
},
|
|
117
|
+
),
|
|
65
118
|
),
|
|
66
119
|
),
|
|
67
120
|
),
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import 'package:bart/bart.dart';
|
|
2
2
|
import 'package:flutter/material.dart';
|
|
3
|
-
import 'package:kasy_kit/components/kasy_app_bar.dart';
|
|
4
3
|
import 'package:kasy_kit/core/bottom_menu/notification_bottom_item.dart';
|
|
4
|
+
import 'package:kasy_kit/core/bottom_menu/web_content_wrapper.dart';
|
|
5
|
+
import 'package:kasy_kit/core/config/features.dart';
|
|
5
6
|
import 'package:kasy_kit/core/navigation/kasy_navigation_config.dart';
|
|
6
7
|
import 'package:kasy_kit/core/navigation/kasy_route_transition.dart';
|
|
7
|
-
import 'package:kasy_kit/core/rating/widgets/rate_banner.dart';
|
|
8
8
|
import 'package:kasy_kit/core/theme/theme.dart';
|
|
9
|
+
import 'package:kasy_kit/features/ai_chat/ai_chat_page.dart';
|
|
9
10
|
import 'package:kasy_kit/features/home/home_page.dart';
|
|
10
11
|
import 'package:kasy_kit/features/notifications/ui/notifications_page.dart';
|
|
11
12
|
import 'package:kasy_kit/features/settings/settings_page.dart';
|
|
@@ -21,23 +22,27 @@ List<BartMenuRoute> subRoutes() {
|
|
|
21
22
|
label: t.navigation.home,
|
|
22
23
|
icon: KasyIcons.home,
|
|
23
24
|
path: 'home',
|
|
24
|
-
pageBuilder: (_, _, settings) =>
|
|
25
|
-
|
|
26
|
-
transitionsBuilder: bottomBarTransition,
|
|
27
|
-
),
|
|
28
|
-
BartMenuRoute.bottomBar(
|
|
29
|
-
label: t.navigation.wishlist,
|
|
30
|
-
icon: KasyIcons.favorite,
|
|
31
|
-
path: 'wishlist',
|
|
32
|
-
pageBuilder: (_, _, settings) => const WishlistPage(),
|
|
25
|
+
pageBuilder: (_, _, settings) =>
|
|
26
|
+
const WebContentWrapper(child: HomePage()),
|
|
33
27
|
transitionDuration: bottomBarTransitionDuration,
|
|
34
28
|
transitionsBuilder: bottomBarTransition,
|
|
35
29
|
),
|
|
30
|
+
if (withAiChat)
|
|
31
|
+
BartMenuRoute.bottomBar(
|
|
32
|
+
label: t.navigation.support,
|
|
33
|
+
icon: KasyIcons.help,
|
|
34
|
+
path: 'support',
|
|
35
|
+
pageBuilder: (_, _, settings) =>
|
|
36
|
+
const WebContentWrapper(child: AiChatPage(isRootTab: true)),
|
|
37
|
+
transitionDuration: bottomBarTransitionDuration,
|
|
38
|
+
transitionsBuilder: bottomBarTransition,
|
|
39
|
+
),
|
|
36
40
|
BartMenuRoute.bottomBarBuilder(
|
|
37
41
|
label: t.navigation.notifications,
|
|
38
42
|
builder: (_, isActive) => const BottomItemNotification(),
|
|
39
43
|
path: 'notifications',
|
|
40
|
-
pageBuilder: (_, _, settings) =>
|
|
44
|
+
pageBuilder: (_, _, settings) =>
|
|
45
|
+
WebContentWrapper(child: NotificationsPage()),
|
|
41
46
|
transitionDuration: bottomBarTransitionDuration,
|
|
42
47
|
transitionsBuilder: bottomBarTransition,
|
|
43
48
|
),
|
|
@@ -45,91 +50,14 @@ List<BartMenuRoute> subRoutes() {
|
|
|
45
50
|
label: t.navigation.settings,
|
|
46
51
|
icon: KasyIcons.settings,
|
|
47
52
|
path: 'settings',
|
|
48
|
-
pageBuilder: (_, _, settings) =>
|
|
53
|
+
pageBuilder: (_, _, settings) =>
|
|
54
|
+
const WebContentWrapper(child: SettingsPage()),
|
|
49
55
|
transitionDuration: bottomBarTransitionDuration,
|
|
50
56
|
transitionsBuilder: bottomBarTransition,
|
|
51
57
|
),
|
|
52
58
|
];
|
|
53
59
|
}
|
|
54
60
|
|
|
55
|
-
/// Placeholder page for the wishlist tab.
|
|
56
|
-
/// Replace this with your own implementation.
|
|
57
|
-
class WishlistPage extends StatelessWidget {
|
|
58
|
-
const WishlistPage({super.key});
|
|
59
|
-
|
|
60
|
-
@override
|
|
61
|
-
Widget build(BuildContext context) {
|
|
62
|
-
return Scaffold(
|
|
63
|
-
backgroundColor: context.colors.background,
|
|
64
|
-
body: Column(
|
|
65
|
-
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
66
|
-
children: [
|
|
67
|
-
KasyAppBar(
|
|
68
|
-
title: context.t.navigation.wishlist,
|
|
69
|
-
style: KasyAppBarStyle.rootTab,
|
|
70
|
-
),
|
|
71
|
-
Expanded(
|
|
72
|
-
child: Padding(
|
|
73
|
-
padding: EdgeInsets.fromLTRB(
|
|
74
|
-
KasySpacing.pageHorizontalGutter,
|
|
75
|
-
KasySpacing.belowChromeContentGap,
|
|
76
|
-
KasySpacing.pageHorizontalGutter,
|
|
77
|
-
MediaQuery.paddingOf(context).bottom +
|
|
78
|
-
KasySpacing.pageVerticalGutter,
|
|
79
|
-
),
|
|
80
|
-
child: Column(
|
|
81
|
-
crossAxisAlignment: CrossAxisAlignment.start,
|
|
82
|
-
children: [
|
|
83
|
-
const RateBanner(forceInDebug: true),
|
|
84
|
-
Expanded(
|
|
85
|
-
child: Center(
|
|
86
|
-
child: Column(
|
|
87
|
-
mainAxisSize: MainAxisSize.min,
|
|
88
|
-
children: [
|
|
89
|
-
Container(
|
|
90
|
-
width: 80,
|
|
91
|
-
height: 80,
|
|
92
|
-
decoration: BoxDecoration(
|
|
93
|
-
color: context.colors.primary.withValues(
|
|
94
|
-
alpha: 0.12,
|
|
95
|
-
),
|
|
96
|
-
shape: BoxShape.circle,
|
|
97
|
-
),
|
|
98
|
-
child: Icon(
|
|
99
|
-
KasyIcons.favoriteOutline,
|
|
100
|
-
size: 40,
|
|
101
|
-
color: context.colors.primary,
|
|
102
|
-
),
|
|
103
|
-
),
|
|
104
|
-
const SizedBox(height: KasySpacing.md),
|
|
105
|
-
Text(
|
|
106
|
-
context.t.wishlist.empty_title,
|
|
107
|
-
style: context.textTheme.titleLarge?.copyWith(
|
|
108
|
-
color: context.colors.muted,
|
|
109
|
-
),
|
|
110
|
-
),
|
|
111
|
-
const SizedBox(height: KasySpacing.sm),
|
|
112
|
-
Text(
|
|
113
|
-
context.t.wishlist.empty_subtitle,
|
|
114
|
-
style: context.textTheme.bodyLarge?.copyWith(
|
|
115
|
-
color: context.colors.muted,
|
|
116
|
-
),
|
|
117
|
-
textAlign: TextAlign.center,
|
|
118
|
-
),
|
|
119
|
-
],
|
|
120
|
-
),
|
|
121
|
-
),
|
|
122
|
-
),
|
|
123
|
-
],
|
|
124
|
-
),
|
|
125
|
-
),
|
|
126
|
-
),
|
|
127
|
-
],
|
|
128
|
-
),
|
|
129
|
-
);
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
|
|
133
61
|
Widget bottomBarTransition(
|
|
134
62
|
BuildContext context,
|
|
135
63
|
Animation<double> animation,
|