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.
Files changed (269) hide show
  1. package/lib/commands/add.js +93 -80
  2. package/lib/commands/configure.js +100 -32
  3. package/lib/commands/doctor.js +28 -2
  4. package/lib/commands/new.js +86 -38
  5. package/lib/commands/notifications.js +1 -1
  6. package/lib/commands/remove.js +43 -15
  7. package/lib/commands/run.js +2 -2
  8. package/lib/commands/update.js +2 -2
  9. package/lib/scaffold/CHANGELOG.json +14 -0
  10. package/lib/scaffold/backends/api/generator.js +14 -14
  11. package/lib/scaffold/backends/api/patch/README.md +83 -0
  12. package/lib/scaffold/backends/api/patch/lib/core/data/api/storage_api.dart +1 -1
  13. package/lib/scaffold/backends/api/patch/lib/core/data/entities/user_entity.dart +5 -0
  14. package/lib/scaffold/backends/api/patch/lib/{environnements.dart → environments.dart} +3 -11
  15. package/lib/scaffold/backends/api/patch/lib/features/ai_chat/api/ai_chat_api.dart +108 -0
  16. package/lib/scaffold/backends/api/patch/lib/features/ai_chat/api/ai_chat_conversation_entity.dart +51 -0
  17. 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
  18. package/lib/scaffold/backends/api/patch/lib/features/{llm_chat/providers/llm_chat_notifier.dart → ai_chat/providers/ai_chat_notifier.dart} +71 -38
  19. package/lib/scaffold/backends/api/patch/lib/features/authentication/api/authentication_api.dart +2 -2
  20. package/lib/scaffold/backends/api/patch/lib/features/feedbacks/api/feature_request_api.dart +54 -0
  21. package/lib/scaffold/backends/api/patch/lib/features/onboarding/models/user_info.dart +16 -16
  22. package/lib/scaffold/backends/api/patch/lib/features/onboarding/ui/components/onboarding_att_setup.dart +4 -3
  23. package/lib/scaffold/backends/api/patch/lib/features/onboarding/ui/widgets/onboarding_radio_question.dart +38 -28
  24. package/lib/scaffold/backends/api/patch/lib/features/settings/ui/components/admin/admin_users_api.dart +100 -0
  25. package/lib/scaffold/backends/api/patch/lib/features/{subscription → subscriptions}/api/entities/subscription_entity.dart +13 -0
  26. package/lib/scaffold/backends/api/patch/lib/features/subscriptions/api/stripe_backend_api.dart +60 -0
  27. package/lib/scaffold/backends/api/patch/lib/features/{subscription → subscriptions}/api/subscription_api.dart +1 -1
  28. package/lib/scaffold/backends/api/pubspec.yaml.tpl +4 -5
  29. package/lib/scaffold/backends/firebase/deploy.js +87 -13
  30. package/lib/scaffold/backends/firebase/enable-auth-via-cli.js +14 -6
  31. package/lib/scaffold/backends/firebase/generator.js +5 -5
  32. package/lib/scaffold/backends/firebase/setup-from-scratch.js +69 -45
  33. package/lib/scaffold/backends/firebase/tokens.js +4 -4
  34. package/lib/scaffold/backends/supabase/deploy.js +63 -11
  35. package/lib/scaffold/backends/supabase/edge-functions/admin-list-users/index.ts +149 -0
  36. package/lib/scaffold/backends/supabase/edge-functions/{llm-chat → ai-chat}/index.ts +19 -19
  37. package/lib/scaffold/backends/supabase/edge-functions/revenuecat-webhook/index.ts +2 -0
  38. package/lib/scaffold/backends/supabase/edge-functions/stripe-create-checkout-session/index.ts +123 -0
  39. package/lib/scaffold/backends/supabase/edge-functions/stripe-create-portal-session/index.ts +97 -0
  40. package/lib/scaffold/backends/supabase/edge-functions/stripe-list-prices/index.ts +83 -0
  41. package/lib/scaffold/backends/supabase/edge-functions/stripe-webhook/index.ts +138 -0
  42. package/lib/scaffold/backends/supabase/generator.js +17 -17
  43. package/lib/scaffold/backends/supabase/migrations/20240101000009_ai_messages.sql +50 -0
  44. package/lib/scaffold/backends/supabase/migrations/20240101000012_stripe_customers.sql +36 -0
  45. package/lib/scaffold/backends/supabase/migrations/20240101000013_admin_role.sql +62 -0
  46. package/lib/scaffold/backends/supabase/patch/lib/core/data/entities/user_entity.dart +4 -0
  47. package/lib/scaffold/backends/supabase/patch/lib/{environnements.dart → environments.dart} +3 -13
  48. package/lib/scaffold/backends/supabase/patch/lib/features/ai_chat/api/ai_chat_api.dart +95 -0
  49. package/lib/scaffold/backends/supabase/patch/lib/features/ai_chat/api/ai_chat_conversation_entity.dart +52 -0
  50. 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
  51. package/lib/scaffold/backends/supabase/patch/lib/features/{llm_chat/providers/llm_chat_notifier.dart → ai_chat/providers/ai_chat_notifier.dart} +63 -35
  52. package/lib/scaffold/backends/supabase/patch/lib/features/authentication/api/authentication_api.dart +1 -1
  53. package/lib/scaffold/backends/supabase/patch/lib/features/feedbacks/api/feature_request_api.dart +46 -0
  54. package/lib/scaffold/backends/supabase/patch/lib/features/onboarding/models/user_info.dart +16 -16
  55. package/lib/scaffold/backends/supabase/patch/lib/features/onboarding/ui/components/onboarding_att_setup.dart +4 -3
  56. package/lib/scaffold/backends/supabase/patch/lib/features/onboarding/ui/widgets/onboarding_radio_question.dart +38 -28
  57. package/lib/scaffold/backends/supabase/patch/lib/features/settings/ui/components/admin/admin_users_api.dart +93 -0
  58. package/lib/scaffold/backends/supabase/patch/lib/features/{subscription → subscriptions}/api/entities/subscription_entity.dart +13 -0
  59. package/lib/scaffold/backends/supabase/patch/lib/features/subscriptions/api/stripe_backend_api.dart +52 -0
  60. package/lib/scaffold/backends/supabase/patch/lib/features/{subscription → subscriptions}/api/subscription_api.dart +1 -1
  61. package/lib/scaffold/backends/supabase/pubspec.yaml.tpl +4 -5
  62. package/lib/scaffold/backends/supabase/tokens.js +3 -3
  63. package/lib/scaffold/catalog.js +9 -11
  64. package/lib/scaffold/generate.js +45 -31
  65. package/lib/scaffold/shared/generator-utils.js +188 -81
  66. package/lib/scaffold/shared/sort-imports.js +191 -0
  67. package/lib/scaffold/shared/template-strings.js +3 -3
  68. package/lib/utils/checks.js +2 -2
  69. package/lib/utils/i18n/messages-en.js +50 -35
  70. package/lib/utils/i18n/messages-es.js +50 -35
  71. package/lib/utils/i18n/messages-pt.js +52 -37
  72. package/lib/utils/updates.js +15 -15
  73. package/package.json +1 -1
  74. package/templates/firebase/.env.example +2 -2
  75. package/templates/firebase/android/app/src/main/res/drawable/background.png +0 -0
  76. package/templates/firebase/android/app/src/main/res/drawable-night/background.png +0 -0
  77. package/templates/firebase/android/app/src/main/res/drawable-night-v21/background.png +0 -0
  78. package/templates/firebase/android/app/src/main/res/drawable-v21/background.png +0 -0
  79. package/templates/firebase/android/app/src/main/res/values-night-v31/styles.xml +1 -1
  80. package/templates/firebase/android/app/src/main/res/values-v31/styles.xml +1 -1
  81. package/templates/firebase/assets/images/logo_wordmark_dark.png +0 -0
  82. package/templates/firebase/assets/images/logo_wordmark_light.png +0 -0
  83. package/templates/firebase/docs/revenuecat-setup.es.md +1 -1
  84. package/templates/firebase/docs/revenuecat-setup.pt.md +1 -1
  85. package/templates/firebase/firestore.rules +24 -5
  86. package/templates/firebase/functions/package-lock.json +22 -1
  87. package/templates/firebase/functions/package.json +2 -1
  88. package/templates/firebase/functions/src/admin/functions.ts +113 -0
  89. package/templates/firebase/functions/src/{llm_chat → ai_chat}/index.ts +16 -16
  90. package/templates/firebase/functions/src/index.ts +8 -2
  91. package/templates/firebase/functions/src/notifications/device_triggers.ts +2 -2
  92. package/templates/firebase/functions/src/notifications/triggers.ts +3 -3
  93. package/templates/firebase/functions/src/subscriptions/models/subscription_status.ts +2 -0
  94. package/templates/firebase/functions/src/subscriptions/stripe_functions.ts +222 -0
  95. package/templates/firebase/functions/src/subscriptions/subscriptions_functions.ts +2 -2
  96. package/templates/firebase/ios/Runner/Assets.xcassets/LaunchBackground.imageset/background.png +0 -0
  97. package/templates/firebase/ios/Runner/Assets.xcassets/LaunchBackground.imageset/darkbackground.png +0 -0
  98. package/templates/firebase/lib/components/components.dart +4 -1
  99. package/templates/firebase/lib/components/kasy_app_bar.dart +22 -7
  100. package/templates/firebase/lib/components/kasy_avatar.dart +7 -6
  101. package/templates/firebase/lib/components/kasy_button.dart +23 -99
  102. package/templates/firebase/lib/components/kasy_dialog.dart +11 -11
  103. package/templates/firebase/lib/components/kasy_image_viewer.dart +84 -0
  104. package/templates/firebase/lib/components/{kasy_sidebar_pro.dart → kasy_sidebar.dart} +692 -425
  105. package/templates/firebase/lib/components/kasy_status_tag.dart +91 -0
  106. package/templates/firebase/lib/components/kasy_text_area.dart +1 -3
  107. package/templates/firebase/lib/components/kasy_text_field.dart +5 -6
  108. package/templates/firebase/lib/components/kasy_text_field_otp.dart +1 -3
  109. package/templates/firebase/lib/components/kasy_toast.dart +2 -2
  110. package/templates/firebase/lib/components/kasy_web_header.dart +209 -0
  111. package/templates/firebase/lib/core/ads/ads_provider.dart +1 -1
  112. package/templates/firebase/lib/core/bottom_menu/bottom_menu.dart +57 -4
  113. package/templates/firebase/lib/core/bottom_menu/bottom_router.dart +19 -91
  114. package/templates/firebase/lib/core/bottom_menu/kasy_bottom_bar_factory.dart +196 -96
  115. package/templates/firebase/lib/core/bottom_menu/web_content_wrapper.dart +41 -0
  116. package/templates/firebase/lib/core/config/app_env.dart +5 -11
  117. package/templates/firebase/lib/core/config/features.dart +5 -4
  118. package/templates/firebase/lib/core/data/api/analytics_api.dart +1 -1
  119. package/templates/firebase/lib/core/data/api/http_client.dart +1 -1
  120. package/templates/firebase/lib/core/data/entities/user_entity.dart +3 -0
  121. package/templates/firebase/lib/core/data/models/entitlement.dart +35 -0
  122. package/templates/firebase/lib/core/data/models/subscription.dart +13 -186
  123. package/templates/firebase/lib/core/data/models/user.dart +11 -0
  124. package/templates/firebase/lib/core/data/repositories/user_repository.dart +1 -1
  125. package/templates/firebase/lib/core/home_widgets/home_widget_background_task.dart +1 -1
  126. package/templates/firebase/lib/core/home_widgets/home_widget_mywidget_service.dart +1 -1
  127. package/templates/firebase/lib/core/icons/kasy_icons.dart +31 -1
  128. package/templates/firebase/lib/core/rating/api/rating_api.dart +2 -2
  129. package/templates/firebase/lib/core/states/logout_action.dart +25 -0
  130. package/templates/firebase/lib/core/states/user_state_notifier.dart +3 -3
  131. package/templates/firebase/lib/core/theme/colors.dart +488 -188
  132. package/templates/firebase/lib/core/theme/radius.dart +22 -11
  133. package/templates/firebase/lib/core/theme/shadows.dart +66 -0
  134. package/templates/firebase/lib/core/theme/texts.dart +75 -41
  135. package/templates/firebase/lib/core/theme/universal_theme.dart +9 -4
  136. package/templates/firebase/lib/core/web_device_preview/web_device_preview.dart +5 -4
  137. package/templates/firebase/lib/core/web_viewport_scale.dart +52 -0
  138. package/templates/firebase/lib/core/widgets/kasy_brand_badge.dart +118 -0
  139. package/templates/firebase/lib/core/widgets/kasy_brand_logo.dart +40 -0
  140. package/templates/firebase/lib/core/widgets/kasy_focus_ring.dart +125 -0
  141. package/templates/firebase/lib/core/widgets/kasy_hover.dart +33 -13
  142. package/templates/firebase/lib/core/widgets/kasy_pressable_depth.dart +18 -13
  143. package/templates/firebase/lib/{environnements.dart → environments.dart} +3 -14
  144. package/templates/firebase/lib/features/ai_chat/ai_chat_page.dart +159 -0
  145. package/templates/firebase/lib/features/ai_chat/api/ai_chat_api.dart +107 -0
  146. package/templates/firebase/lib/features/ai_chat/api/ai_chat_conversation_entity.dart +64 -0
  147. package/templates/firebase/lib/features/{llm_chat/api/llm_chat_message_entity.dart → ai_chat/api/ai_chat_message_entity.dart} +6 -6
  148. package/templates/firebase/lib/features/{llm_chat/providers/llm_chat_notifier.dart → ai_chat/providers/ai_chat_notifier.dart} +73 -38
  149. package/templates/firebase/lib/features/ai_chat/providers/ai_conversations_notifier.dart +103 -0
  150. package/templates/firebase/lib/features/{llm_chat/ui/widgets/llm_chat_avatars.dart → ai_chat/ui/widgets/ai_chat_avatars.dart} +13 -13
  151. package/templates/firebase/lib/features/{llm_chat/ui/widgets/llm_chat_composer.dart → ai_chat/ui/widgets/ai_chat_composer.dart} +10 -10
  152. package/templates/firebase/lib/features/{llm_chat/llm_chat_page.dart → ai_chat/ui/widgets/ai_chat_conversation_view.dart} +80 -67
  153. package/templates/firebase/lib/features/ai_chat/ui/widgets/ai_conversation_list.dart +285 -0
  154. package/templates/firebase/lib/features/ai_chat/ui/widgets/ai_conversation_tile.dart +163 -0
  155. package/templates/firebase/lib/features/authentication/api/authentication_api.dart +52 -13
  156. package/templates/firebase/lib/features/authentication/api/popup_dismiss_watcher.dart +12 -0
  157. package/templates/firebase/lib/features/authentication/api/popup_dismiss_watcher_web.dart +35 -0
  158. package/templates/firebase/lib/features/authentication/ui/recover_password_page.dart +108 -68
  159. package/templates/firebase/lib/features/authentication/ui/signin_page.dart +38 -51
  160. package/templates/firebase/lib/features/authentication/ui/signup_page.dart +38 -51
  161. package/templates/firebase/lib/features/authentication/ui/widgets/auth_card_scaffold.dart +118 -0
  162. package/templates/firebase/lib/features/authentication/ui/widgets/recover_password_result.dart +61 -44
  163. package/templates/firebase/lib/features/feedbacks/api/feature_request_api.dart +32 -0
  164. package/templates/firebase/lib/features/feedbacks/models/feedback_state.dart +5 -5
  165. package/templates/firebase/lib/features/feedbacks/providers/feedback_page_notifier.dart +2 -2
  166. package/templates/firebase/lib/features/home/design_system_page.dart +808 -170
  167. package/templates/firebase/lib/features/home/home_components_page.dart +6 -3
  168. package/templates/firebase/lib/features/home/home_components_preview_page.dart +6 -6
  169. package/templates/firebase/lib/features/home/home_components_preview_registry.dart +325 -186
  170. package/templates/firebase/lib/features/home/home_feed.dart +289 -0
  171. package/templates/firebase/lib/features/home/home_image_grid.dart +355 -0
  172. package/templates/firebase/lib/features/home/home_page.dart +11 -250
  173. package/templates/firebase/lib/features/{local_reminder → local_reminders}/providers/reminder_notifier.dart +1 -1
  174. package/templates/firebase/lib/features/{local_reminder → local_reminders}/ui/reminder_page.dart +2 -2
  175. package/templates/firebase/lib/features/notifications/shared/att_permission.dart +1 -1
  176. package/templates/firebase/lib/features/notifications/ui/request_notification_permission.dart +25 -61
  177. package/templates/firebase/lib/features/notifications/ui/widgets/permission_request_view.dart +117 -0
  178. package/templates/firebase/lib/features/onboarding/models/user_info.dart +16 -16
  179. package/templates/firebase/lib/features/onboarding/ui/components/onboarding_att_setup.dart +4 -3
  180. package/templates/firebase/lib/features/onboarding/ui/components/onboarding_features.dart +7 -9
  181. package/templates/firebase/lib/features/onboarding/ui/components/onboarding_loader.dart +71 -48
  182. package/templates/firebase/lib/features/onboarding/ui/components/onboarding_notifications_setup.dart +3 -2
  183. package/templates/firebase/lib/features/onboarding/ui/components/onboarding_questions.dart +5 -5
  184. package/templates/firebase/lib/features/onboarding/ui/onboarding_page.dart +4 -4
  185. package/templates/firebase/lib/features/onboarding/ui/widgets/onboarding_background.dart +4 -2
  186. package/templates/firebase/lib/features/onboarding/ui/widgets/onboarding_feature.dart +39 -121
  187. package/templates/firebase/lib/features/onboarding/ui/widgets/onboarding_illustration_scaffold.dart +105 -70
  188. package/templates/firebase/lib/features/onboarding/ui/widgets/onboarding_module_mockups.dart +639 -0
  189. package/templates/firebase/lib/features/onboarding/ui/widgets/onboarding_progress.dart +62 -50
  190. package/templates/firebase/lib/features/onboarding/ui/widgets/onboarding_radio_question.dart +38 -28
  191. package/templates/firebase/lib/features/onboarding/ui/widgets/onboarding_step_header.dart +75 -0
  192. package/templates/firebase/lib/features/onboarding/ui/widgets/onboarding_sticky_footer.dart +16 -5
  193. package/templates/firebase/lib/features/onboarding/ui/widgets/selectable_row_tile.dart +26 -22
  194. package/templates/firebase/lib/features/settings/settings_page.dart +601 -90
  195. package/templates/firebase/lib/features/settings/ui/components/admin/admin_page.dart +1193 -0
  196. package/templates/firebase/lib/features/settings/ui/components/admin/admin_paywalls.dart +1 -1
  197. package/templates/firebase/lib/features/settings/ui/components/admin/admin_routes.dart +2 -3
  198. package/templates/firebase/lib/features/settings/ui/components/admin/admin_users_api.dart +86 -0
  199. package/templates/firebase/lib/features/settings/ui/components/admin/admin_users_tab.dart +1215 -0
  200. package/templates/firebase/lib/features/settings/ui/components/admin/send_push_notification_page.dart +236 -0
  201. package/templates/firebase/lib/features/settings/ui/components/avatar_component.dart +3 -3
  202. package/templates/firebase/lib/features/settings/ui/widgets/kasy_user_avatar.dart +77 -0
  203. package/templates/firebase/lib/features/settings/ui/widgets/settings_tile.dart +1 -0
  204. package/templates/firebase/lib/features/{subscription → subscriptions}/api/entities/subscription_entity.dart +17 -0
  205. package/templates/firebase/lib/features/{subscription → subscriptions}/api/inapp_subscription_api.dart +67 -46
  206. package/templates/firebase/lib/features/subscriptions/api/revenuecat_product.dart +189 -0
  207. package/templates/firebase/lib/features/subscriptions/api/stripe_backend_api.dart +55 -0
  208. package/templates/firebase/lib/features/subscriptions/api/stripe_payment_api.dart +82 -0
  209. package/templates/firebase/lib/features/subscriptions/api/stripe_product.dart +178 -0
  210. package/templates/firebase/lib/features/{subscription → subscriptions}/api/subscription_api.dart +1 -1
  211. package/templates/firebase/lib/features/subscriptions/api/subscription_payment_api.dart +53 -0
  212. package/templates/firebase/lib/features/subscriptions/api/subscription_payment_api_provider.dart +21 -0
  213. package/templates/firebase/lib/features/{subscription → subscriptions}/providers/premium_page_provider.dart +9 -6
  214. package/templates/firebase/lib/features/{subscription → subscriptions}/repositories/subscription_repository.dart +26 -30
  215. package/{lib/scaffold/backends/supabase/patch/lib/features/subscription → templates/firebase/lib/features/subscriptions}/shared/maybeshow_premium.dart +0 -2
  216. package/templates/firebase/lib/features/subscriptions/shared/subscription_management.dart +45 -0
  217. package/templates/firebase/lib/features/{subscription → subscriptions}/ui/component/active_premium_content.dart +28 -4
  218. package/templates/firebase/lib/features/{subscription → subscriptions}/ui/component/paywall_minimal.dart +7 -7
  219. package/templates/firebase/lib/features/{subscription → subscriptions}/ui/component/paywall_row.dart +8 -8
  220. package/templates/firebase/lib/features/{subscription → subscriptions}/ui/component/paywall_with_switch.dart +7 -7
  221. package/templates/firebase/lib/features/{subscription → subscriptions}/ui/component/premium_content.dart +7 -7
  222. package/templates/firebase/lib/features/{subscription → subscriptions}/ui/component/premium_page_factory.dart +5 -5
  223. package/templates/firebase/lib/features/{subscription → subscriptions}/ui/premium_page.dart +5 -5
  224. package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/comparison_table.dart +1 -1
  225. package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/paywall_empty_state.dart +4 -4
  226. package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/premium_bottom_menu.dart +3 -4
  227. package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/selectable_col.dart +1 -1
  228. package/templates/firebase/lib/i18n/en.i18n.json +171 -46
  229. package/templates/firebase/lib/i18n/es.i18n.json +175 -50
  230. package/templates/firebase/lib/i18n/pt.i18n.json +166 -41
  231. package/templates/firebase/lib/main.dart +6 -3
  232. package/templates/firebase/lib/router.dart +15 -23
  233. package/templates/firebase/pubspec.yaml +4 -5
  234. package/templates/firebase/test/core/data/repositories/user_repository_test.dart +3 -3
  235. package/templates/firebase/test/core/states/user_state_notifier_test.dart +4 -4
  236. package/templates/firebase/test/core/widgets/focus_ring_shape_test.dart +55 -0
  237. package/templates/firebase/test/features/{subscription → subscriptions}/api/fake_inapp_subscription_api.dart +11 -4
  238. package/templates/firebase/test/features/{subscription → subscriptions}/api/fake_revenuecat_product.dart +1 -0
  239. package/templates/firebase/test/features/{subscription → subscriptions}/api/fake_subscription_api.dart +2 -2
  240. package/templates/firebase/test/features/{subscription → subscriptions}/subscription_page_test.dart +4 -4
  241. package/templates/firebase/test/test_utils.dart +6 -6
  242. package/templates/firebase/web/index.html +5 -2
  243. package/lib/scaffold/backends/api/patch/lib/features/llm_chat/api/llm_chat_api.dart +0 -58
  244. package/lib/scaffold/backends/api/patch/lib/features/subscription/shared/maybeshow_premium.dart +0 -86
  245. package/lib/scaffold/backends/supabase/migrations/20240101000009_llm_messages.sql +0 -22
  246. package/lib/scaffold/backends/supabase/patch/lib/features/llm_chat/api/llm_chat_api.dart +0 -47
  247. package/templates/firebase/assets/images/onboarding/authentication-login-template.jpg +0 -0
  248. package/templates/firebase/assets/images/onboarding/img2.jpg +0 -0
  249. package/templates/firebase/assets/images/onboarding/img3.jpg +0 -0
  250. package/templates/firebase/assets/images/onboarding/notifications.png +0 -0
  251. package/templates/firebase/assets/images/onboarding/purchase.png +0 -0
  252. package/templates/firebase/lib/core/sidebar/kasy_sidebar.dart +0 -2021
  253. package/templates/firebase/lib/features/authentication/ui/widgets/auth_brand.dart +0 -35
  254. package/templates/firebase/lib/features/home/home_features_page.dart +0 -207
  255. package/templates/firebase/lib/features/llm_chat/api/llm_chat_api.dart +0 -50
  256. package/templates/firebase/lib/features/settings/ui/components/admin/admin_bottom_sheet.dart +0 -316
  257. package/templates/firebase/lib/features/subscription/shared/maybeshow_premium.dart +0 -85
  258. /package/templates/firebase/lib/features/{local_reminder → local_reminders}/repositories/reminder_preferences.dart +0 -0
  259. /package/templates/firebase/lib/features/{subscription → subscriptions}/providers/models/premium_state.dart +0 -0
  260. /package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/feature_line.dart +0 -0
  261. /package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/premium_background.dart +0 -0
  262. /package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/premium_background_gradient.dart +0 -0
  263. /package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/premium_banner.dart +0 -0
  264. /package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/premium_card.dart +0 -0
  265. /package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/premium_close_button.dart +0 -0
  266. /package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/premium_feature.dart +0 -0
  267. /package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/selectable_row.dart +0 -0
  268. /package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/trial_switcher.dart +0 -0
  269. /package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/unsubscribe_feedback_popup.dart +0 -0
@@ -9,9 +9,10 @@ import 'package:kasy_kit/core/haptics/haptic_feedback_notifier.dart';
9
9
  import 'package:kasy_kit/core/security/biometric_preference_notifier.dart';
10
10
  import 'package:kasy_kit/core/security/biometric_service.dart';
11
11
  import 'package:kasy_kit/core/security/biometric_ui_bundle.dart';
12
+ import 'package:kasy_kit/core/states/logout_action.dart';
12
13
  import 'package:kasy_kit/core/states/user_state_notifier.dart';
13
14
  import 'package:kasy_kit/core/theme/theme.dart';
14
- import 'package:kasy_kit/features/settings/ui/components/admin/admin_bottom_sheet.dart';
15
+ import 'package:kasy_kit/core/widgets/kasy_hover.dart';
15
16
  import 'package:kasy_kit/features/settings/ui/components/avatar_component.dart';
16
17
  import 'package:kasy_kit/features/settings/ui/components/delete_user_component.dart';
17
18
  import 'package:kasy_kit/features/settings/ui/components/language_switcher.dart';
@@ -44,101 +45,160 @@ class SettingsPage extends ConsumerWidget {
44
45
  icon: KasyIcons.logout,
45
46
  iconSize: 20,
46
47
  foregroundColor: ctx.colors.error,
47
- onPressed: () => showKasyConfirmDialog(
48
- ctx,
49
- title: tr.disconnect_confirm_title,
50
- message: tr.disconnect_confirm_message,
51
- cancelLabel: tr.disconnect_cancel,
52
- confirmLabel: tr.disconnect,
53
- onConfirm: () =>
54
- ref.read(userStateNotifierProvider.notifier).onLogout(),
55
- ),
48
+ onPressed: () => confirmLogout(ctx, ref),
56
49
  ),
57
50
  ),
58
51
  slivers: [
59
- SliverList.list(
60
- children: [
61
- ProfileTile(
62
- title: displayName,
63
- subtitle: displayEmail,
64
- isAuthenticated: isAuthenticated,
65
- onTap: isAuthenticated ? null : () => context.push('/signup'),
66
- ),
67
- const SizedBox(height: KasySpacing.xl),
68
- _SectionLabel(tr.section_preferences_label),
69
- const SizedBox(height: KasySpacing.xs),
70
- SettingsContainer(
71
- child: Wrap(
72
- children: [
73
- const ThemeSwitcher(),
74
- const SettingsDivider(),
75
- const HapticFeedbackSwitcher(),
76
- const SettingsDivider(),
77
- const LanguageSwitcher(),
78
- if (withLocalNotifications) ...[
79
- const SettingsDivider(),
80
- SettingsTile(
81
- icon: KasyIcons.notification,
82
- title: tr.reminders,
83
- onTap: () => context.push('/reminder'),
84
- ),
85
- ],
86
- if (withFeedback) ...[
87
- const SettingsDivider(),
88
- SettingsTile(
89
- icon: KasyIcons.message,
90
- title: tr.feedback,
91
- onTap: () => context.push('/feedback'),
52
+ SliverToBoxAdapter(
53
+ child: Builder(
54
+ builder: (context) {
55
+ // Breakpoint by viewport width — the industry standard and the
56
+ // project's own `large` cutoff (1024px ≈ Tailwind lg). Two-pane
57
+ // spans the whole desktop range and only collapses to a single
58
+ // column when entering tablet territory (the layout phones and
59
+ // tablets already use), instead of switching while still wide.
60
+ final bool wide = MediaQuery.sizeOf(context).width >= 1024;
61
+
62
+ if (!wide) {
63
+ return Column(
64
+ crossAxisAlignment: CrossAxisAlignment.stretch,
65
+ children: [
66
+ ProfileTile(
67
+ title: displayName,
68
+ subtitle: displayEmail,
69
+ isAuthenticated: isAuthenticated,
70
+ onTap: isAuthenticated
71
+ ? null
72
+ : () => context.push('/signup'),
92
73
  ),
93
- ],
94
- if (withRevenuecat) ...[
95
- const SettingsDivider(),
96
- SettingsTile(
97
- icon: KasyIcons.payment,
98
- title: tr.premium,
99
- onTap: () => context.push('/premium'),
74
+ const SizedBox(height: KasySpacing.xl),
75
+ ..._sections(
76
+ context,
77
+ ref,
78
+ isAuthenticated: isAuthenticated,
79
+ isAdmin: user.isAdmin,
100
80
  ),
81
+ const SizedBox(height: KasySpacing.xl),
101
82
  ],
102
- ],
83
+ );
84
+ }
85
+
86
+ // Desktop: a SaaS-style master/detail (Vercel/Stripe/Claude) —
87
+ // a section nav on the left, the selected section on the right.
88
+ return _SettingsDesktopView(
89
+ name: displayName,
90
+ email: displayEmail,
91
+ isAuthenticated: isAuthenticated,
92
+ isAdmin: user.isAdmin,
93
+ );
94
+ },
95
+ ),
96
+ ),
97
+ ],
98
+ );
99
+ }
100
+
101
+ /// The settings sections (everything except the profile header), shared
102
+ /// between the single-column (mobile/tablet) and two-pane (desktop) layouts.
103
+ List<Widget> _sections(
104
+ BuildContext context,
105
+ WidgetRef ref, {
106
+ required bool isAuthenticated,
107
+ required bool isAdmin,
108
+ }) {
109
+ final tr = context.t.settings;
110
+ return [
111
+ _SectionLabel(tr.section_preferences_label),
112
+ const SizedBox(height: KasySpacing.xs),
113
+ SettingsContainer(
114
+ child: Wrap(
115
+ children: [
116
+ const ThemeSwitcher(),
117
+ const SettingsDivider(),
118
+ const HapticFeedbackSwitcher(),
119
+ const SettingsDivider(),
120
+ const LanguageSwitcher(),
121
+ if (withLocalReminders) ...[
122
+ const SettingsDivider(),
123
+ SettingsTile(
124
+ icon: KasyIcons.notification,
125
+ title: tr.reminders,
126
+ onTap: () => context.push('/reminder'),
103
127
  ),
104
- ),
105
- if (isAuthenticated && !kIsWeb) ...[
106
- const SizedBox(height: KasySpacing.xl),
107
- _SectionLabel(tr.section_security_label),
108
- const SizedBox(height: KasySpacing.xs),
109
- const SettingsContainer(
110
- child: BiometricSwitcher(),
128
+ ],
129
+ if (withFeedback) ...[
130
+ const SettingsDivider(),
131
+ SettingsTile(
132
+ icon: KasyIcons.message,
133
+ title: tr.feedback,
134
+ onTap: () => context.push('/feedback'),
111
135
  ),
112
136
  ],
113
- const SizedBox(height: KasySpacing.xl),
114
- _SectionLabel(tr.section_support_label),
115
- const SizedBox(height: KasySpacing.xs),
116
- SettingsContainer(
117
- child: Wrap(
118
- children: [
119
- SettingsTile(
120
- icon: KasyIcons.privacy,
121
- title: tr.privacy,
122
- onTap: () => launchUrl(Uri.parse('https://kasy.dev/privacy/')),
123
- ),
124
- const SettingsDivider(),
125
- SettingsTile(
126
- icon: KasyIcons.help,
127
- title: tr.support,
128
- onTap: () => launchUrl(Uri.parse('https://kasy.dev/')),
129
- ),
130
- ],
137
+ if (withRevenuecat) ...[
138
+ const SettingsDivider(),
139
+ SettingsTile(
140
+ icon: KasyIcons.payment,
141
+ title: tr.premium,
142
+ onTap: () => context.push('/premium'),
131
143
  ),
144
+ ],
145
+ ],
146
+ ),
147
+ ),
148
+ if (isAuthenticated && !kIsWeb) ...[
149
+ const SizedBox(height: KasySpacing.xl),
150
+ _SectionLabel(tr.section_security_label),
151
+ const SizedBox(height: KasySpacing.xs),
152
+ const SettingsContainer(
153
+ child: BiometricSwitcher(),
154
+ ),
155
+ ],
156
+ const SizedBox(height: KasySpacing.xl),
157
+ _SectionLabel(tr.section_support_label),
158
+ const SizedBox(height: KasySpacing.xs),
159
+ SettingsContainer(
160
+ child: Wrap(
161
+ children: [
162
+ SettingsTile(
163
+ icon: KasyIcons.privacy,
164
+ title: tr.privacy,
165
+ onTap: () => launchUrl(Uri.parse('https://kasy.dev/privacy/')),
166
+ ),
167
+ const SettingsDivider(),
168
+ SettingsTile(
169
+ icon: KasyIcons.help,
170
+ title: tr.support,
171
+ onTap: () => launchUrl(Uri.parse('https://kasy.dev/')),
132
172
  ),
133
- const SizedBox(height: KasySpacing.xxl),
134
- const DeleteUserButton(),
135
- const SizedBox(height: KasySpacing.xl),
136
- const _VersionLabel(),
137
- const SizedBox(height: KasySpacing.xl),
138
173
  ],
139
174
  ),
175
+ ),
176
+ // Admin entry — only for administrators or in development mode.
177
+ if (isAdmin || kDebugMode) ...[
178
+ const SizedBox(height: KasySpacing.xl),
179
+ SettingsContainer(
180
+ child: SettingsTile(
181
+ icon: Icons.admin_panel_settings_outlined,
182
+ title: t.admin_console.settings_entry.title,
183
+ onTap: () => context.push('/admin'),
184
+ ),
185
+ ),
186
+ const SizedBox(height: KasySpacing.sm),
187
+ Padding(
188
+ padding: const EdgeInsets.only(left: KasySpacing.xs),
189
+ child: Text(
190
+ t.admin_console.settings_entry.caption,
191
+ style: context.textTheme.bodySmall?.copyWith(
192
+ color: context.colors.muted,
193
+ ),
194
+ ),
195
+ ),
140
196
  ],
141
- );
197
+ const SizedBox(height: KasySpacing.xxl),
198
+ const DeleteUserButton(),
199
+ const SizedBox(height: KasySpacing.xl),
200
+ const _VersionLabel(),
201
+ ];
142
202
  }
143
203
  }
144
204
 
@@ -276,6 +336,460 @@ class ProfileTile extends StatelessWidget {
276
336
  }
277
337
  }
278
338
 
339
+ // ─── Desktop (≥1024px): SaaS-style master/detail ──────────────────────────
340
+ //
341
+ // A left nav lists the settings sections; the right pane shows the selected
342
+ // one. Mirrors the layout of Vercel / Stripe / Claude settings. The phone and
343
+ // tablet layout (single column) is untouched.
344
+
345
+ enum _DesktopSection { account, preferences, security, support, admin }
346
+
347
+ /// Title-cases an all-caps section label (e.g. "PREFERENCES" → "Preferences")
348
+ /// in a way that's safe across locales (single-word labels only).
349
+ String _titleCase(String s) =>
350
+ s.isEmpty ? s : s[0].toUpperCase() + s.substring(1).toLowerCase();
351
+
352
+ (IconData, String) _sectionMeta(BuildContext context, _DesktopSection section) {
353
+ final tr = context.t.settings;
354
+ return switch (section) {
355
+ _DesktopSection.account => (KasyIcons.person, tr.my_account),
356
+ _DesktopSection.preferences => (
357
+ KasyIcons.settings,
358
+ _titleCase(tr.section_preferences_label),
359
+ ),
360
+ _DesktopSection.security => (
361
+ KasyIcons.security,
362
+ _titleCase(tr.section_security_label),
363
+ ),
364
+ _DesktopSection.support => (
365
+ KasyIcons.help,
366
+ _titleCase(tr.section_support_label),
367
+ ),
368
+ _DesktopSection.admin => (
369
+ Icons.admin_panel_settings_outlined,
370
+ t.admin_console.settings_entry.title,
371
+ ),
372
+ };
373
+ }
374
+
375
+ class _SettingsDesktopView extends ConsumerStatefulWidget {
376
+ final String name;
377
+ final String email;
378
+ final bool isAuthenticated;
379
+ final bool isAdmin;
380
+
381
+ const _SettingsDesktopView({
382
+ required this.name,
383
+ required this.email,
384
+ required this.isAuthenticated,
385
+ required this.isAdmin,
386
+ });
387
+
388
+ @override
389
+ ConsumerState<_SettingsDesktopView> createState() =>
390
+ _SettingsDesktopViewState();
391
+ }
392
+
393
+ class _SettingsDesktopViewState extends ConsumerState<_SettingsDesktopView> {
394
+ _DesktopSection _selected = _DesktopSection.account;
395
+
396
+ List<_DesktopSection> get _sections => <_DesktopSection>[
397
+ _DesktopSection.account,
398
+ _DesktopSection.preferences,
399
+ if (widget.isAuthenticated && !kIsWeb) _DesktopSection.security,
400
+ _DesktopSection.support,
401
+ if (widget.isAdmin || kDebugMode) _DesktopSection.admin,
402
+ ];
403
+
404
+ @override
405
+ Widget build(BuildContext context) {
406
+ final List<_DesktopSection> sections = _sections;
407
+ if (!sections.contains(_selected)) _selected = sections.first;
408
+
409
+ return Center(
410
+ child: ConstrainedBox(
411
+ constraints: const BoxConstraints(maxWidth: 1080),
412
+ child: Padding(
413
+ padding: const EdgeInsets.only(
414
+ top: KasySpacing.lg,
415
+ bottom: KasySpacing.xxl,
416
+ ),
417
+ child: Row(
418
+ crossAxisAlignment: CrossAxisAlignment.start,
419
+ children: [
420
+ SizedBox(
421
+ width: 248,
422
+ child: _DesktopNav(
423
+ sections: sections,
424
+ selected: _selected,
425
+ name: widget.name,
426
+ email: widget.email,
427
+ onSelect: (s) => setState(() => _selected = s),
428
+ ),
429
+ ),
430
+ const SizedBox(width: KasySpacing.xxl),
431
+ Expanded(
432
+ child: Align(
433
+ alignment: Alignment.topLeft,
434
+ child: ConstrainedBox(
435
+ constraints: const BoxConstraints(maxWidth: 720),
436
+ child: _DesktopDetail(
437
+ section: _selected,
438
+ name: widget.name,
439
+ email: widget.email,
440
+ isAuthenticated: widget.isAuthenticated,
441
+ ),
442
+ ),
443
+ ),
444
+ ),
445
+ ],
446
+ ),
447
+ ),
448
+ ),
449
+ );
450
+ }
451
+ }
452
+
453
+ class _DesktopNav extends StatelessWidget {
454
+ final List<_DesktopSection> sections;
455
+ final _DesktopSection selected;
456
+ final String name;
457
+ final String email;
458
+ final ValueChanged<_DesktopSection> onSelect;
459
+
460
+ const _DesktopNav({
461
+ required this.sections,
462
+ required this.selected,
463
+ required this.name,
464
+ required this.email,
465
+ required this.onSelect,
466
+ });
467
+
468
+ @override
469
+ Widget build(BuildContext context) {
470
+ return Column(
471
+ crossAxisAlignment: CrossAxisAlignment.stretch,
472
+ children: [
473
+ Padding(
474
+ padding: const EdgeInsets.fromLTRB(
475
+ KasySpacing.sm,
476
+ KasySpacing.sm,
477
+ KasySpacing.sm,
478
+ KasySpacing.md,
479
+ ),
480
+ child: Row(
481
+ children: [
482
+ const EditableUserAvatar(diameter: 40),
483
+ const SizedBox(width: KasySpacing.sm),
484
+ Expanded(
485
+ child: Column(
486
+ crossAxisAlignment: CrossAxisAlignment.start,
487
+ mainAxisSize: MainAxisSize.min,
488
+ children: [
489
+ Text(
490
+ name,
491
+ maxLines: 1,
492
+ overflow: TextOverflow.ellipsis,
493
+ style: context.textTheme.titleSmall?.copyWith(
494
+ color: context.colors.onSurface,
495
+ fontWeight: FontWeight.w600,
496
+ ),
497
+ ),
498
+ if (email.isNotEmpty)
499
+ Text(
500
+ email,
501
+ maxLines: 1,
502
+ overflow: TextOverflow.ellipsis,
503
+ style: context.textTheme.bodySmall?.copyWith(
504
+ color: context.colors.muted,
505
+ ),
506
+ ),
507
+ ],
508
+ ),
509
+ ),
510
+ ],
511
+ ),
512
+ ),
513
+ for (final _DesktopSection s in sections)
514
+ Padding(
515
+ padding: const EdgeInsets.only(bottom: KasySpacing.xs),
516
+ child: _NavTile(
517
+ section: s,
518
+ selected: s == selected,
519
+ onTap: () => onSelect(s),
520
+ ),
521
+ ),
522
+ ],
523
+ );
524
+ }
525
+ }
526
+
527
+ class _NavTile extends StatelessWidget {
528
+ final _DesktopSection section;
529
+ final bool selected;
530
+ final VoidCallback onTap;
531
+
532
+ const _NavTile({
533
+ required this.section,
534
+ required this.selected,
535
+ required this.onTap,
536
+ });
537
+
538
+ @override
539
+ Widget build(BuildContext context) {
540
+ final KasyColors c = context.colors;
541
+ final (IconData icon, String label) = _sectionMeta(context, section);
542
+ final Color fg = selected ? c.onSurface : c.muted;
543
+
544
+ // Same interaction model as KasySidebar: KasyHover (MouseRegion + animated
545
+ // fill, no Material ripple) over a static selected background.
546
+ return KasyHover(
547
+ borderRadius: KasyRadius.smBorderRadius,
548
+ hoverColor: c.surfaceNeutralSoft,
549
+ pressColor: c.onSurface,
550
+ onTap: onTap,
551
+ child: Container(
552
+ padding: const EdgeInsets.symmetric(
553
+ horizontal: KasySpacing.smd,
554
+ vertical: KasySpacing.smd,
555
+ ),
556
+ decoration: BoxDecoration(
557
+ color: selected ? c.surfaceNeutralSoft : Colors.transparent,
558
+ borderRadius: KasyRadius.smBorderRadius,
559
+ ),
560
+ child: Row(
561
+ children: [
562
+ Icon(icon, size: 20, color: fg),
563
+ const SizedBox(width: KasySpacing.sm),
564
+ Text(
565
+ label,
566
+ style: context.textTheme.titleSmall?.copyWith(
567
+ color: fg,
568
+ fontWeight: selected ? FontWeight.w600 : FontWeight.w500,
569
+ ),
570
+ ),
571
+ ],
572
+ ),
573
+ ),
574
+ );
575
+ }
576
+ }
577
+
578
+ class _DesktopDetail extends ConsumerWidget {
579
+ final _DesktopSection section;
580
+ final String name;
581
+ final String email;
582
+ final bool isAuthenticated;
583
+
584
+ const _DesktopDetail({
585
+ required this.section,
586
+ required this.name,
587
+ required this.email,
588
+ required this.isAuthenticated,
589
+ });
590
+
591
+ @override
592
+ Widget build(BuildContext context, WidgetRef ref) {
593
+ final (_, String title) = _sectionMeta(context, section);
594
+
595
+ return Column(
596
+ crossAxisAlignment: CrossAxisAlignment.stretch,
597
+ children: [
598
+ Padding(
599
+ padding: const EdgeInsets.only(
600
+ left: KasySpacing.xs,
601
+ bottom: KasySpacing.lg,
602
+ ),
603
+ child: Text(
604
+ title,
605
+ style: context.textTheme.headlineSmall?.copyWith(
606
+ color: context.colors.onSurface,
607
+ fontWeight: FontWeight.w700,
608
+ ),
609
+ ),
610
+ ),
611
+ ..._content(context, ref),
612
+ ],
613
+ );
614
+ }
615
+
616
+ List<Widget> _content(BuildContext context, WidgetRef ref) {
617
+ final tr = context.t.settings;
618
+ switch (section) {
619
+ case _DesktopSection.account:
620
+ return _accountContent(context, ref);
621
+ case _DesktopSection.preferences:
622
+ return [
623
+ SettingsContainer(
624
+ child: Wrap(
625
+ children: [
626
+ const ThemeSwitcher(),
627
+ const SettingsDivider(),
628
+ const HapticFeedbackSwitcher(),
629
+ const SettingsDivider(),
630
+ const LanguageSwitcher(),
631
+ if (withLocalReminders) ...[
632
+ const SettingsDivider(),
633
+ SettingsTile(
634
+ icon: KasyIcons.notification,
635
+ title: tr.reminders,
636
+ onTap: () => context.push('/reminder'),
637
+ ),
638
+ ],
639
+ if (withFeedback) ...[
640
+ const SettingsDivider(),
641
+ SettingsTile(
642
+ icon: KasyIcons.message,
643
+ title: tr.feedback,
644
+ onTap: () => context.push('/feedback'),
645
+ ),
646
+ ],
647
+ if (withRevenuecat) ...[
648
+ const SettingsDivider(),
649
+ SettingsTile(
650
+ icon: KasyIcons.payment,
651
+ title: tr.premium,
652
+ onTap: () => context.push('/premium'),
653
+ ),
654
+ ],
655
+ ],
656
+ ),
657
+ ),
658
+ ];
659
+ case _DesktopSection.security:
660
+ return const [
661
+ SettingsContainer(child: BiometricSwitcher()),
662
+ ];
663
+ case _DesktopSection.support:
664
+ return [
665
+ SettingsContainer(
666
+ child: Wrap(
667
+ children: [
668
+ SettingsTile(
669
+ icon: KasyIcons.privacy,
670
+ title: tr.privacy,
671
+ onTap: () =>
672
+ launchUrl(Uri.parse('https://kasy.dev/privacy/')),
673
+ ),
674
+ const SettingsDivider(),
675
+ SettingsTile(
676
+ icon: KasyIcons.help,
677
+ title: tr.support,
678
+ onTap: () => launchUrl(Uri.parse('https://kasy.dev/')),
679
+ ),
680
+ ],
681
+ ),
682
+ ),
683
+ ];
684
+ case _DesktopSection.admin:
685
+ return [
686
+ SettingsContainer(
687
+ child: SettingsTile(
688
+ icon: Icons.admin_panel_settings_outlined,
689
+ title: t.admin_console.settings_entry.title,
690
+ onTap: () => context.push('/admin'),
691
+ ),
692
+ ),
693
+ const SizedBox(height: KasySpacing.sm),
694
+ Padding(
695
+ padding: const EdgeInsets.only(left: KasySpacing.xs),
696
+ child: Text(
697
+ t.admin_console.settings_entry.caption,
698
+ style: context.textTheme.bodySmall?.copyWith(
699
+ color: context.colors.muted,
700
+ ),
701
+ ),
702
+ ),
703
+ ];
704
+ }
705
+ }
706
+
707
+ List<Widget> _accountContent(BuildContext context, WidgetRef ref) {
708
+ final tr = context.t.settings;
709
+ return [
710
+ SettingsContainer(
711
+ child: Padding(
712
+ padding: const EdgeInsets.symmetric(vertical: KasySpacing.sm),
713
+ child: Row(
714
+ children: [
715
+ const EditableUserAvatar(diameter: 56),
716
+ const SizedBox(width: KasySpacing.md),
717
+ Expanded(
718
+ child: Column(
719
+ crossAxisAlignment: CrossAxisAlignment.start,
720
+ mainAxisSize: MainAxisSize.min,
721
+ children: [
722
+ Text(
723
+ name,
724
+ style: context.textTheme.titleMedium?.copyWith(
725
+ color: context.colors.onSurface,
726
+ fontWeight: FontWeight.w600,
727
+ ),
728
+ ),
729
+ if (email.isNotEmpty)
730
+ Text(
731
+ email,
732
+ style: context.textTheme.bodySmall?.copyWith(
733
+ color: context.colors.muted,
734
+ ),
735
+ ),
736
+ ],
737
+ ),
738
+ ),
739
+ if (!isAuthenticated)
740
+ KasyButton(
741
+ label: tr.register,
742
+ onPressed: () => context.push('/signup'),
743
+ ),
744
+ ],
745
+ ),
746
+ ),
747
+ ),
748
+ if (isAuthenticated) ...[
749
+ const SizedBox(height: KasySpacing.xl),
750
+ SettingsContainer(
751
+ child: _LogoutRow(onTap: () => confirmLogout(context, ref)),
752
+ ),
753
+ ],
754
+ const SizedBox(height: KasySpacing.xl),
755
+ const DeleteUserButton(),
756
+ const SizedBox(height: KasySpacing.xl),
757
+ const _VersionLabel(),
758
+ ];
759
+ }
760
+ }
761
+
762
+ /// A danger-tinted "Sign out" row used in the desktop Account section.
763
+ class _LogoutRow extends StatelessWidget {
764
+ final VoidCallback onTap;
765
+
766
+ const _LogoutRow({required this.onTap});
767
+
768
+ @override
769
+ Widget build(BuildContext context) {
770
+ return KasyHover(
771
+ borderRadius: KasyRadius.smBorderRadius,
772
+ pressColor: context.colors.error,
773
+ onTap: onTap,
774
+ child: Padding(
775
+ padding: const EdgeInsets.symmetric(vertical: KasySpacing.sm),
776
+ child: Row(
777
+ children: [
778
+ Icon(KasyIcons.logout, size: 21, color: context.colors.error),
779
+ const SizedBox(width: KasySpacing.sm),
780
+ Text(
781
+ context.t.settings.logout,
782
+ style: context.textTheme.titleMedium?.copyWith(
783
+ color: context.colors.error,
784
+ ),
785
+ ),
786
+ ],
787
+ ),
788
+ ),
789
+ );
790
+ }
791
+ }
792
+
279
793
  class _VersionLabel extends StatefulWidget {
280
794
  const _VersionLabel();
281
795
 
@@ -296,14 +810,11 @@ class _VersionLabelState extends State<_VersionLabel> {
296
810
 
297
811
  @override
298
812
  Widget build(BuildContext context) {
299
- return GestureDetector(
300
- onDoubleTap: () => showAdminBottomSheet(context),
301
- child: Center(
302
- child: Text(
303
- _version,
304
- style: context.textTheme.bodySmall?.copyWith(
305
- color: context.colors.onSurface.withValues(alpha: 0.3),
306
- ),
813
+ return Center(
814
+ child: Text(
815
+ _version,
816
+ style: context.textTheme.bodySmall?.copyWith(
817
+ color: context.colors.onSurface.withValues(alpha: 0.3),
307
818
  ),
308
819
  ),
309
820
  );