kasy-cli 1.21.9 → 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 (267) 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 +80 -37
  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/generator.js +5 -5
  31. package/lib/scaffold/backends/firebase/tokens.js +4 -4
  32. package/lib/scaffold/backends/supabase/deploy.js +63 -11
  33. package/lib/scaffold/backends/supabase/edge-functions/admin-list-users/index.ts +149 -0
  34. package/lib/scaffold/backends/supabase/edge-functions/{llm-chat → ai-chat}/index.ts +19 -19
  35. package/lib/scaffold/backends/supabase/edge-functions/revenuecat-webhook/index.ts +2 -0
  36. package/lib/scaffold/backends/supabase/edge-functions/stripe-create-checkout-session/index.ts +123 -0
  37. package/lib/scaffold/backends/supabase/edge-functions/stripe-create-portal-session/index.ts +97 -0
  38. package/lib/scaffold/backends/supabase/edge-functions/stripe-list-prices/index.ts +83 -0
  39. package/lib/scaffold/backends/supabase/edge-functions/stripe-webhook/index.ts +138 -0
  40. package/lib/scaffold/backends/supabase/generator.js +17 -17
  41. package/lib/scaffold/backends/supabase/migrations/20240101000009_ai_messages.sql +50 -0
  42. package/lib/scaffold/backends/supabase/migrations/20240101000012_stripe_customers.sql +36 -0
  43. package/lib/scaffold/backends/supabase/migrations/20240101000013_admin_role.sql +62 -0
  44. package/lib/scaffold/backends/supabase/patch/lib/core/data/entities/user_entity.dart +4 -0
  45. package/lib/scaffold/backends/supabase/patch/lib/{environnements.dart → environments.dart} +3 -13
  46. package/lib/scaffold/backends/supabase/patch/lib/features/ai_chat/api/ai_chat_api.dart +95 -0
  47. package/lib/scaffold/backends/supabase/patch/lib/features/ai_chat/api/ai_chat_conversation_entity.dart +52 -0
  48. 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
  49. package/lib/scaffold/backends/supabase/patch/lib/features/{llm_chat/providers/llm_chat_notifier.dart → ai_chat/providers/ai_chat_notifier.dart} +63 -35
  50. package/lib/scaffold/backends/supabase/patch/lib/features/authentication/api/authentication_api.dart +1 -1
  51. package/lib/scaffold/backends/supabase/patch/lib/features/feedbacks/api/feature_request_api.dart +46 -0
  52. package/lib/scaffold/backends/supabase/patch/lib/features/onboarding/models/user_info.dart +16 -16
  53. package/lib/scaffold/backends/supabase/patch/lib/features/onboarding/ui/components/onboarding_att_setup.dart +4 -3
  54. package/lib/scaffold/backends/supabase/patch/lib/features/onboarding/ui/widgets/onboarding_radio_question.dart +38 -28
  55. package/lib/scaffold/backends/supabase/patch/lib/features/settings/ui/components/admin/admin_users_api.dart +93 -0
  56. package/lib/scaffold/backends/supabase/patch/lib/features/{subscription → subscriptions}/api/entities/subscription_entity.dart +13 -0
  57. package/lib/scaffold/backends/supabase/patch/lib/features/subscriptions/api/stripe_backend_api.dart +52 -0
  58. package/lib/scaffold/backends/supabase/patch/lib/features/{subscription → subscriptions}/api/subscription_api.dart +1 -1
  59. package/lib/scaffold/backends/supabase/pubspec.yaml.tpl +4 -5
  60. package/lib/scaffold/backends/supabase/tokens.js +3 -3
  61. package/lib/scaffold/catalog.js +9 -11
  62. package/lib/scaffold/generate.js +45 -31
  63. package/lib/scaffold/shared/generator-utils.js +188 -81
  64. package/lib/scaffold/shared/sort-imports.js +191 -0
  65. package/lib/scaffold/shared/template-strings.js +3 -3
  66. package/lib/utils/checks.js +2 -2
  67. package/lib/utils/i18n/messages-en.js +50 -35
  68. package/lib/utils/i18n/messages-es.js +50 -35
  69. package/lib/utils/i18n/messages-pt.js +52 -37
  70. package/lib/utils/updates.js +15 -15
  71. package/package.json +1 -1
  72. package/templates/firebase/.env.example +2 -2
  73. package/templates/firebase/android/app/src/main/res/drawable/background.png +0 -0
  74. package/templates/firebase/android/app/src/main/res/drawable-night/background.png +0 -0
  75. package/templates/firebase/android/app/src/main/res/drawable-night-v21/background.png +0 -0
  76. package/templates/firebase/android/app/src/main/res/drawable-v21/background.png +0 -0
  77. package/templates/firebase/android/app/src/main/res/values-night-v31/styles.xml +1 -1
  78. package/templates/firebase/android/app/src/main/res/values-v31/styles.xml +1 -1
  79. package/templates/firebase/assets/images/logo_wordmark_dark.png +0 -0
  80. package/templates/firebase/assets/images/logo_wordmark_light.png +0 -0
  81. package/templates/firebase/docs/revenuecat-setup.es.md +1 -1
  82. package/templates/firebase/docs/revenuecat-setup.pt.md +1 -1
  83. package/templates/firebase/firestore.rules +24 -5
  84. package/templates/firebase/functions/package-lock.json +22 -1
  85. package/templates/firebase/functions/package.json +2 -1
  86. package/templates/firebase/functions/src/admin/functions.ts +113 -0
  87. package/templates/firebase/functions/src/{llm_chat → ai_chat}/index.ts +16 -16
  88. package/templates/firebase/functions/src/index.ts +8 -2
  89. package/templates/firebase/functions/src/notifications/device_triggers.ts +2 -2
  90. package/templates/firebase/functions/src/notifications/triggers.ts +3 -3
  91. package/templates/firebase/functions/src/subscriptions/models/subscription_status.ts +2 -0
  92. package/templates/firebase/functions/src/subscriptions/stripe_functions.ts +222 -0
  93. package/templates/firebase/functions/src/subscriptions/subscriptions_functions.ts +2 -2
  94. package/templates/firebase/ios/Runner/Assets.xcassets/LaunchBackground.imageset/background.png +0 -0
  95. package/templates/firebase/ios/Runner/Assets.xcassets/LaunchBackground.imageset/darkbackground.png +0 -0
  96. package/templates/firebase/lib/components/components.dart +4 -1
  97. package/templates/firebase/lib/components/kasy_app_bar.dart +22 -7
  98. package/templates/firebase/lib/components/kasy_avatar.dart +7 -6
  99. package/templates/firebase/lib/components/kasy_button.dart +23 -99
  100. package/templates/firebase/lib/components/kasy_dialog.dart +11 -11
  101. package/templates/firebase/lib/components/kasy_image_viewer.dart +84 -0
  102. package/templates/firebase/lib/components/{kasy_sidebar_pro.dart → kasy_sidebar.dart} +692 -425
  103. package/templates/firebase/lib/components/kasy_status_tag.dart +91 -0
  104. package/templates/firebase/lib/components/kasy_text_area.dart +1 -3
  105. package/templates/firebase/lib/components/kasy_text_field.dart +5 -6
  106. package/templates/firebase/lib/components/kasy_text_field_otp.dart +1 -3
  107. package/templates/firebase/lib/components/kasy_toast.dart +2 -2
  108. package/templates/firebase/lib/components/kasy_web_header.dart +209 -0
  109. package/templates/firebase/lib/core/ads/ads_provider.dart +1 -1
  110. package/templates/firebase/lib/core/bottom_menu/bottom_menu.dart +57 -4
  111. package/templates/firebase/lib/core/bottom_menu/bottom_router.dart +19 -91
  112. package/templates/firebase/lib/core/bottom_menu/kasy_bottom_bar_factory.dart +196 -96
  113. package/templates/firebase/lib/core/bottom_menu/web_content_wrapper.dart +41 -0
  114. package/templates/firebase/lib/core/config/app_env.dart +5 -11
  115. package/templates/firebase/lib/core/config/features.dart +5 -4
  116. package/templates/firebase/lib/core/data/api/analytics_api.dart +1 -1
  117. package/templates/firebase/lib/core/data/api/http_client.dart +1 -1
  118. package/templates/firebase/lib/core/data/entities/user_entity.dart +3 -0
  119. package/templates/firebase/lib/core/data/models/entitlement.dart +35 -0
  120. package/templates/firebase/lib/core/data/models/subscription.dart +13 -186
  121. package/templates/firebase/lib/core/data/models/user.dart +11 -0
  122. package/templates/firebase/lib/core/data/repositories/user_repository.dart +1 -1
  123. package/templates/firebase/lib/core/home_widgets/home_widget_background_task.dart +1 -1
  124. package/templates/firebase/lib/core/home_widgets/home_widget_mywidget_service.dart +1 -1
  125. package/templates/firebase/lib/core/icons/kasy_icons.dart +31 -1
  126. package/templates/firebase/lib/core/rating/api/rating_api.dart +2 -2
  127. package/templates/firebase/lib/core/states/logout_action.dart +25 -0
  128. package/templates/firebase/lib/core/states/user_state_notifier.dart +3 -3
  129. package/templates/firebase/lib/core/theme/colors.dart +488 -188
  130. package/templates/firebase/lib/core/theme/radius.dart +22 -11
  131. package/templates/firebase/lib/core/theme/shadows.dart +66 -0
  132. package/templates/firebase/lib/core/theme/texts.dart +75 -41
  133. package/templates/firebase/lib/core/theme/universal_theme.dart +9 -4
  134. package/templates/firebase/lib/core/web_device_preview/web_device_preview.dart +5 -4
  135. package/templates/firebase/lib/core/web_viewport_scale.dart +52 -0
  136. package/templates/firebase/lib/core/widgets/kasy_brand_badge.dart +118 -0
  137. package/templates/firebase/lib/core/widgets/kasy_brand_logo.dart +40 -0
  138. package/templates/firebase/lib/core/widgets/kasy_focus_ring.dart +125 -0
  139. package/templates/firebase/lib/core/widgets/kasy_hover.dart +33 -13
  140. package/templates/firebase/lib/core/widgets/kasy_pressable_depth.dart +18 -13
  141. package/templates/firebase/lib/{environnements.dart → environments.dart} +3 -14
  142. package/templates/firebase/lib/features/ai_chat/ai_chat_page.dart +159 -0
  143. package/templates/firebase/lib/features/ai_chat/api/ai_chat_api.dart +107 -0
  144. package/templates/firebase/lib/features/ai_chat/api/ai_chat_conversation_entity.dart +64 -0
  145. package/templates/firebase/lib/features/{llm_chat/api/llm_chat_message_entity.dart → ai_chat/api/ai_chat_message_entity.dart} +6 -6
  146. package/templates/firebase/lib/features/{llm_chat/providers/llm_chat_notifier.dart → ai_chat/providers/ai_chat_notifier.dart} +73 -38
  147. package/templates/firebase/lib/features/ai_chat/providers/ai_conversations_notifier.dart +103 -0
  148. package/templates/firebase/lib/features/{llm_chat/ui/widgets/llm_chat_avatars.dart → ai_chat/ui/widgets/ai_chat_avatars.dart} +13 -13
  149. package/templates/firebase/lib/features/{llm_chat/ui/widgets/llm_chat_composer.dart → ai_chat/ui/widgets/ai_chat_composer.dart} +10 -10
  150. package/templates/firebase/lib/features/{llm_chat/llm_chat_page.dart → ai_chat/ui/widgets/ai_chat_conversation_view.dart} +80 -67
  151. package/templates/firebase/lib/features/ai_chat/ui/widgets/ai_conversation_list.dart +285 -0
  152. package/templates/firebase/lib/features/ai_chat/ui/widgets/ai_conversation_tile.dart +163 -0
  153. package/templates/firebase/lib/features/authentication/api/authentication_api.dart +52 -13
  154. package/templates/firebase/lib/features/authentication/api/popup_dismiss_watcher.dart +12 -0
  155. package/templates/firebase/lib/features/authentication/api/popup_dismiss_watcher_web.dart +35 -0
  156. package/templates/firebase/lib/features/authentication/ui/recover_password_page.dart +108 -68
  157. package/templates/firebase/lib/features/authentication/ui/signin_page.dart +38 -51
  158. package/templates/firebase/lib/features/authentication/ui/signup_page.dart +38 -51
  159. package/templates/firebase/lib/features/authentication/ui/widgets/auth_card_scaffold.dart +118 -0
  160. package/templates/firebase/lib/features/authentication/ui/widgets/recover_password_result.dart +61 -44
  161. package/templates/firebase/lib/features/feedbacks/api/feature_request_api.dart +32 -0
  162. package/templates/firebase/lib/features/feedbacks/models/feedback_state.dart +5 -5
  163. package/templates/firebase/lib/features/feedbacks/providers/feedback_page_notifier.dart +2 -2
  164. package/templates/firebase/lib/features/home/design_system_page.dart +808 -170
  165. package/templates/firebase/lib/features/home/home_components_page.dart +6 -3
  166. package/templates/firebase/lib/features/home/home_components_preview_page.dart +6 -6
  167. package/templates/firebase/lib/features/home/home_components_preview_registry.dart +325 -186
  168. package/templates/firebase/lib/features/home/home_feed.dart +289 -0
  169. package/templates/firebase/lib/features/home/home_image_grid.dart +355 -0
  170. package/templates/firebase/lib/features/home/home_page.dart +11 -250
  171. package/templates/firebase/lib/features/{local_reminder → local_reminders}/providers/reminder_notifier.dart +1 -1
  172. package/templates/firebase/lib/features/{local_reminder → local_reminders}/ui/reminder_page.dart +2 -2
  173. package/templates/firebase/lib/features/notifications/shared/att_permission.dart +1 -1
  174. package/templates/firebase/lib/features/notifications/ui/request_notification_permission.dart +25 -61
  175. package/templates/firebase/lib/features/notifications/ui/widgets/permission_request_view.dart +117 -0
  176. package/templates/firebase/lib/features/onboarding/models/user_info.dart +16 -16
  177. package/templates/firebase/lib/features/onboarding/ui/components/onboarding_att_setup.dart +4 -3
  178. package/templates/firebase/lib/features/onboarding/ui/components/onboarding_features.dart +7 -9
  179. package/templates/firebase/lib/features/onboarding/ui/components/onboarding_loader.dart +71 -48
  180. package/templates/firebase/lib/features/onboarding/ui/components/onboarding_notifications_setup.dart +3 -2
  181. package/templates/firebase/lib/features/onboarding/ui/components/onboarding_questions.dart +5 -5
  182. package/templates/firebase/lib/features/onboarding/ui/onboarding_page.dart +4 -4
  183. package/templates/firebase/lib/features/onboarding/ui/widgets/onboarding_background.dart +4 -2
  184. package/templates/firebase/lib/features/onboarding/ui/widgets/onboarding_feature.dart +39 -121
  185. package/templates/firebase/lib/features/onboarding/ui/widgets/onboarding_illustration_scaffold.dart +105 -70
  186. package/templates/firebase/lib/features/onboarding/ui/widgets/onboarding_module_mockups.dart +639 -0
  187. package/templates/firebase/lib/features/onboarding/ui/widgets/onboarding_progress.dart +62 -50
  188. package/templates/firebase/lib/features/onboarding/ui/widgets/onboarding_radio_question.dart +38 -28
  189. package/templates/firebase/lib/features/onboarding/ui/widgets/onboarding_step_header.dart +75 -0
  190. package/templates/firebase/lib/features/onboarding/ui/widgets/onboarding_sticky_footer.dart +16 -5
  191. package/templates/firebase/lib/features/onboarding/ui/widgets/selectable_row_tile.dart +26 -22
  192. package/templates/firebase/lib/features/settings/settings_page.dart +601 -90
  193. package/templates/firebase/lib/features/settings/ui/components/admin/admin_page.dart +1193 -0
  194. package/templates/firebase/lib/features/settings/ui/components/admin/admin_paywalls.dart +1 -1
  195. package/templates/firebase/lib/features/settings/ui/components/admin/admin_routes.dart +2 -3
  196. package/templates/firebase/lib/features/settings/ui/components/admin/admin_users_api.dart +86 -0
  197. package/templates/firebase/lib/features/settings/ui/components/admin/admin_users_tab.dart +1215 -0
  198. package/templates/firebase/lib/features/settings/ui/components/admin/send_push_notification_page.dart +236 -0
  199. package/templates/firebase/lib/features/settings/ui/components/avatar_component.dart +3 -3
  200. package/templates/firebase/lib/features/settings/ui/widgets/kasy_user_avatar.dart +77 -0
  201. package/templates/firebase/lib/features/settings/ui/widgets/settings_tile.dart +1 -0
  202. package/templates/firebase/lib/features/{subscription → subscriptions}/api/entities/subscription_entity.dart +17 -0
  203. package/templates/firebase/lib/features/{subscription → subscriptions}/api/inapp_subscription_api.dart +67 -46
  204. package/templates/firebase/lib/features/subscriptions/api/revenuecat_product.dart +189 -0
  205. package/templates/firebase/lib/features/subscriptions/api/stripe_backend_api.dart +55 -0
  206. package/templates/firebase/lib/features/subscriptions/api/stripe_payment_api.dart +82 -0
  207. package/templates/firebase/lib/features/subscriptions/api/stripe_product.dart +178 -0
  208. package/templates/firebase/lib/features/{subscription → subscriptions}/api/subscription_api.dart +1 -1
  209. package/templates/firebase/lib/features/subscriptions/api/subscription_payment_api.dart +53 -0
  210. package/templates/firebase/lib/features/subscriptions/api/subscription_payment_api_provider.dart +21 -0
  211. package/templates/firebase/lib/features/{subscription → subscriptions}/providers/premium_page_provider.dart +9 -6
  212. package/templates/firebase/lib/features/{subscription → subscriptions}/repositories/subscription_repository.dart +26 -30
  213. package/{lib/scaffold/backends/supabase/patch/lib/features/subscription → templates/firebase/lib/features/subscriptions}/shared/maybeshow_premium.dart +0 -2
  214. package/templates/firebase/lib/features/subscriptions/shared/subscription_management.dart +45 -0
  215. package/templates/firebase/lib/features/{subscription → subscriptions}/ui/component/active_premium_content.dart +28 -4
  216. package/templates/firebase/lib/features/{subscription → subscriptions}/ui/component/paywall_minimal.dart +7 -7
  217. package/templates/firebase/lib/features/{subscription → subscriptions}/ui/component/paywall_row.dart +8 -8
  218. package/templates/firebase/lib/features/{subscription → subscriptions}/ui/component/paywall_with_switch.dart +7 -7
  219. package/templates/firebase/lib/features/{subscription → subscriptions}/ui/component/premium_content.dart +7 -7
  220. package/templates/firebase/lib/features/{subscription → subscriptions}/ui/component/premium_page_factory.dart +5 -5
  221. package/templates/firebase/lib/features/{subscription → subscriptions}/ui/premium_page.dart +5 -5
  222. package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/comparison_table.dart +1 -1
  223. package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/paywall_empty_state.dart +4 -4
  224. package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/premium_bottom_menu.dart +3 -4
  225. package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/selectable_col.dart +1 -1
  226. package/templates/firebase/lib/i18n/en.i18n.json +171 -46
  227. package/templates/firebase/lib/i18n/es.i18n.json +175 -50
  228. package/templates/firebase/lib/i18n/pt.i18n.json +166 -41
  229. package/templates/firebase/lib/main.dart +6 -3
  230. package/templates/firebase/lib/router.dart +15 -23
  231. package/templates/firebase/pubspec.yaml +4 -5
  232. package/templates/firebase/test/core/data/repositories/user_repository_test.dart +3 -3
  233. package/templates/firebase/test/core/states/user_state_notifier_test.dart +4 -4
  234. package/templates/firebase/test/core/widgets/focus_ring_shape_test.dart +55 -0
  235. package/templates/firebase/test/features/{subscription → subscriptions}/api/fake_inapp_subscription_api.dart +11 -4
  236. package/templates/firebase/test/features/{subscription → subscriptions}/api/fake_revenuecat_product.dart +1 -0
  237. package/templates/firebase/test/features/{subscription → subscriptions}/api/fake_subscription_api.dart +2 -2
  238. package/templates/firebase/test/features/{subscription → subscriptions}/subscription_page_test.dart +4 -4
  239. package/templates/firebase/test/test_utils.dart +6 -6
  240. package/templates/firebase/web/index.html +5 -2
  241. package/lib/scaffold/backends/api/patch/lib/features/llm_chat/api/llm_chat_api.dart +0 -58
  242. package/lib/scaffold/backends/api/patch/lib/features/subscription/shared/maybeshow_premium.dart +0 -86
  243. package/lib/scaffold/backends/supabase/migrations/20240101000009_llm_messages.sql +0 -22
  244. package/lib/scaffold/backends/supabase/patch/lib/features/llm_chat/api/llm_chat_api.dart +0 -47
  245. package/templates/firebase/assets/images/onboarding/authentication-login-template.jpg +0 -0
  246. package/templates/firebase/assets/images/onboarding/img2.jpg +0 -0
  247. package/templates/firebase/assets/images/onboarding/img3.jpg +0 -0
  248. package/templates/firebase/assets/images/onboarding/notifications.png +0 -0
  249. package/templates/firebase/assets/images/onboarding/purchase.png +0 -0
  250. package/templates/firebase/lib/core/sidebar/kasy_sidebar.dart +0 -2021
  251. package/templates/firebase/lib/features/authentication/ui/widgets/auth_brand.dart +0 -35
  252. package/templates/firebase/lib/features/home/home_features_page.dart +0 -207
  253. package/templates/firebase/lib/features/llm_chat/api/llm_chat_api.dart +0 -50
  254. package/templates/firebase/lib/features/settings/ui/components/admin/admin_bottom_sheet.dart +0 -316
  255. package/templates/firebase/lib/features/subscription/shared/maybeshow_premium.dart +0 -85
  256. /package/templates/firebase/lib/features/{local_reminder → local_reminders}/repositories/reminder_preferences.dart +0 -0
  257. /package/templates/firebase/lib/features/{subscription → subscriptions}/providers/models/premium_state.dart +0 -0
  258. /package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/feature_line.dart +0 -0
  259. /package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/premium_background.dart +0 -0
  260. /package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/premium_background_gradient.dart +0 -0
  261. /package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/premium_banner.dart +0 -0
  262. /package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/premium_card.dart +0 -0
  263. /package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/premium_close_button.dart +0 -0
  264. /package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/premium_feature.dart +0 -0
  265. /package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/selectable_row.dart +0 -0
  266. /package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/trial_switcher.dart +0 -0
  267. /package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/unsubscribe_feedback_popup.dart +0 -0
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
 
3
3
  /**
4
- * Kasy CLI i18n messages — Português.
4
+ * Kasy CLI i18n messages — Portuguese.
5
5
  *
6
6
  * One entry per user-facing string. Keys are dotted paths describing
7
7
  * where the string is used (cli.help.*, run.*, ios.*, etc.).
@@ -43,7 +43,7 @@ module.exports = {
43
43
  'cli.command.setup.langName': 'idioma',
44
44
  'cli.command.setup.langOption': 'Idioma dos prompts (en, pt, es)',
45
45
  'cli.command.setup.backendOption': 'Adapter de backend (firebase, supabase, api)',
46
- 'cli.command.setup.featuresOption': 'Features opcionais separadas por virgula (web,widget,llm_chat,revenuecat,ci)',
46
+ 'cli.command.setup.featuresOption': 'Features opcionais separadas por virgula (web,widget,ai_chat,revenuecat,ci)',
47
47
  'cli.command.help.paramName': 'comando',
48
48
  'cli.command.doctor.description': 'Verifica se o seu computador está pronto para rodar a Kasy',
49
49
  'cli.command.modules.description': 'Mostra o que já vem incluso e o que você pode adicionar',
@@ -109,15 +109,15 @@ module.exports = {
109
109
  'modules.backend.supabase.description': 'Adapter de backend Supabase',
110
110
  'modules.backend.api.description': 'Adapter de backend API REST/GraphQL',
111
111
  'modules.feature.sentry.description': 'Rastreamento de erros e crashes com Sentry',
112
- 'modules.feature.analytics.description': 'Rastreamento de eventos de analytics (Firebase Analytics)',
112
+ 'modules.feature.analytics.description': 'Rastreamento de eventos de analytics (Mixpanel)',
113
113
  'modules.feature.facebook.description': 'Login do Facebook e rastreamento de eventos Meta Ads (vem sempre junto)',
114
114
  'modules.feature.revenuecat.description': 'Habilita pagamentos reais na tela de Subscriptions',
115
115
  'modules.feature.onboarding.description': 'Fluxo de boas-vindas mostrado no primeiro acesso',
116
116
  'modules.feature.web.description': 'Suporte Web/PWA',
117
117
  'modules.feature.widget.description': 'Widget de tela inicial iOS/Android',
118
- 'modules.feature.llm_chat.description': 'Tela de chat com IA usando OpenAI ou Gemini',
118
+ 'modules.feature.ai_chat.description': 'Tela de chat com IA usando OpenAI ou Gemini',
119
119
  'modules.feature.feedback.description': 'Pedidos e votacao de features dentro do app',
120
- 'modules.feature.local_notifications.description': 'Lembretes locais agendados pelo usuario (sem servidor)',
120
+ 'modules.feature.local_reminders.description': 'Lembretes locais agendados pelo usuario (sem servidor)',
121
121
  'modules.feature.ci.description': 'CI/CD: GitHub/GitLab (testes + build) + Codemagic (publicação nas lojas)',
122
122
  'checks.checking': 'Verificando {name}...',
123
123
  'checks.found': '{name} encontrado',
@@ -268,6 +268,9 @@ module.exports = {
268
268
  'new.supabase.created': 'Projeto criado com sucesso.',
269
269
  'new.supabase.createFailed': 'Não foi possível criar o projeto',
270
270
  'new.supabase.loginHint': 'Execute: supabase login. Depois informe a URL e chave do seu projeto existente.',
271
+ 'new.supabase.createFailed.freeLimit': 'Seu plano grátis do Supabase atingiu o limite de projetos nesta organização.',
272
+ 'new.supabase.freeLimit.title': 'Como liberar',
273
+ 'new.supabase.freeLimit.options': '• Apague um projeto que você não usa mais\n• Escolha outra organização (se você tiver mais de uma)\n• Ou faça upgrade do plano no painel do Supabase\nDepois rode o kasy new de novo.',
271
274
  'new.supabase.setup': 'Vinculando projeto e publicando…',
272
275
  'new.supabase.setupManual': 'Execute manualmente: supabase link, supabase db push, supabase functions deploy',
273
276
  'new.supabase.passwordSaved': 'Senha do banco gerada automaticamente e salva em .kasy/supabase.json (gitignorado).',
@@ -301,7 +304,7 @@ module.exports = {
301
304
  'validate.fail': 'falhou',
302
305
  'validate.passed': 'aprovado',
303
306
 
304
- // ── Fluxo Firebase ────────────────────────────────────────────────────
307
+ // ── Firebase flow ────────────────────────────────────────────────────
305
308
  'cli.command.firebase.description': '🔥 Criar app Flutter completo com Firebase (recomendado)',
306
309
  'new.banner': '✨ Novo app Flutter',
307
310
  'new.subtitle': 'Escolha o backend: Firebase, Supabase ou API REST.',
@@ -345,8 +348,10 @@ module.exports = {
345
348
  'configure.section.sentry': 'Sentry (Crash Reports)',
346
349
  'configure.section.mixpanel': 'Mixpanel (Analytics)',
347
350
  'configure.section.revenuecat': 'RevenueCat (Assinaturas)',
351
+ 'configure.section.stripe': 'Stripe (Pagamentos na Web)',
352
+ 'configure.stripe.apiNote': 'Backend API: configure STRIPE_SECRET_KEY + STRIPE_WEBHOOK_SECRET no seu próprio servidor (veja Stripe-Guia.md). A CLI não tem onde guardar.',
348
353
  'configure.section.facebook': 'Facebook (Login + Ads)',
349
- 'configure.section.llmChat': 'AI Chat (LLM)',
354
+ 'configure.section.aiChat': 'AI Chat',
350
355
  'configure.savedFacebook': 'Credenciais do Facebook gravadas em Info.plist e strings.xml.',
351
356
  'configure.facebookPlistMissing': 'ios/Runner/Info.plist não encontrado — Facebook iOS não foi atualizado.',
352
357
  'configure.facebookStringsMissing': 'android/.../strings.xml não encontrado — Facebook Android não foi atualizado.',
@@ -501,7 +506,13 @@ module.exports = {
501
506
  'doctor.revenuecat.prefixMismatch': 'Chave com prefixo errado: {key} deveria começar com {expected}',
502
507
  'doctor.revenuecat.keysTest': 'Apenas chaves Test Store (test_) configuradas — releases na loja exigem appl_/goog_, senão o app crasha',
503
508
  'doctor.revenuecat.webhookUrlSupabase': 'URL do webhook (cole no RevenueCat → Integrations → Webhooks)',
504
- 'doctor.revenuecat.webhookUrlFirebase': 'URL do webhook: Firebase Console → Functions → subscriptionsOnRcPremiumUpdate',
509
+ 'doctor.revenuecat.webhookUrlFirebase': 'URL do webhook: Firebase Console → Functions → subscriptions-revenuecatWebhook',
510
+ 'doctor.stripe.title': 'Stripe (Pagamentos na Web)',
511
+ 'doctor.stripe.serverSide': 'Só no servidor — nenhuma chave Stripe vai no app (Checkout hospedado).',
512
+ 'doctor.stripe.secretsFirebase': 'Defina STRIPE_SECRET_KEY + STRIPE_WEBHOOK_SECRET com `kasy configure stripe`, depois `kasy deploy`.',
513
+ 'doctor.stripe.secretsSupabase': 'Secrets via `supabase secrets set` (feito no `kasy new` se informadas).',
514
+ 'doctor.stripe.secretsApi': 'Configure STRIPE_SECRET_KEY + STRIPE_WEBHOOK_SECRET no seu servidor (veja Stripe-Guia.md).',
515
+ 'doctor.stripe.webhookUrl': 'URL do webhook Stripe (adicione em Stripe → Developers → Webhooks)',
505
516
 
506
517
  'update.iosRelease.success': 'Arquivos de release iOS atualizados',
507
518
  'add.iosRelease.success': 'Arquivos de release iOS adicionados',
@@ -548,14 +559,15 @@ module.exports = {
548
559
  'new.modules.header.feedback': '── Feedback (Firebase + Supabase) ──',
549
560
  'new.modules.header.ci': '── CI/CD ──',
550
561
  'new.modules.header.monetization': '── Monetizacao ──',
551
- 'new.firebase.module.revenuecat': '💰 RevenueCat (ativa Subscriptions)',
562
+ 'new.firebase.module.revenuecat': '💰 RevenueCat (assinaturas mobile)',
563
+ 'new.firebase.module.stripe': '🌐 Stripe (assinaturas na web)',
552
564
  'new.firebase.module.sentry': '🚨 Crash Reports (Sentry)',
553
565
  'new.firebase.module.analytics': '📊 Analytics (Mixpanel)',
554
566
  'new.firebase.module.facebook': '👤 Facebook (Login + Ads)',
555
567
  'new.firebase.module.web': '🌐 Web Support (PWA)',
556
568
  'new.firebase.module.widget': '📱 Home Widget (iOS/Android)',
557
- 'new.firebase.module.llm_chat': '🤖 AI Chat (OpenAI/Gemini)',
558
- 'new.firebase.module.local_notifications': '🔔 Local Reminders (sem servidor)',
569
+ 'new.firebase.module.ai_chat': '🤖 AI Chat (OpenAI/Gemini)',
570
+ 'new.firebase.module.local_reminders': '🔔 Local Reminders (sem servidor)',
559
571
  'new.firebase.module.feedback': '💬 Feature Requests (requer Firebase ou Supabase)',
560
572
  'new.firebase.module.ci': '⚙️ CI/CD (GitHub + Codemagic)',
561
573
  'new.firebase.module.onboarding': '👋 Onboarding (fluxo de boas-vindas)',
@@ -579,10 +591,12 @@ module.exports = {
579
591
  'new.firebase.q.revenuecat.ios': 'Chave de API RevenueCat para iOS',
580
592
  'new.firebase.q.paywall': 'Qual estilo de paywall?',
581
593
  'new.firebase.q.paywall.hint': 'Layout da tela de assinaturas — pode alterar depois no RevenueCat',
582
- 'new.firebase.q.revenuecat.web': 'Habilitar assinaturas na web (RevenueCat Web Billing)?',
583
- 'new.firebase.q.revenuecat.web.hint': 'Requer Web Billing no dashboard RevenueCat + Stripe. Deixe desmarcado se não precisar.',
584
- 'new.firebase.q.revenuecat.webKey': 'Chave API RevenueCat Web Billing (rcb_xxx ou rcb_sb_xxx, opcional — configure depois)',
585
- 'new.firebase.success.revenuecatWeb': ' RevenueCat Web: adicione RC_WEB_API_KEY (launch.json) para assinaturas na web',
594
+ 'new.firebase.q.stripe.secretKey': 'Stripe Secret Key (sk_test_… / sk_live_…) — opcional, configure depois',
595
+ 'new.firebase.q.stripe.webhookSecret': 'Stripe Webhook Signing Secret (whsec_…) opcional, configure depois',
596
+ 'new.firebase.q.stripe.webhookSecret.hint': 'Em Stripe Developers Webhooks seu endpoint',
597
+ 'new.firebase.q.stripe.webhookSecret.invalid': 'O signing secret do webhook deve começar com whsec_.',
598
+ 'new.firebase.q.stripe.productId': 'Stripe Product ID (prod_…) — opcional, limita o paywall a um produto',
599
+ 'new.firebase.q.stripe.productId.hint': 'Deixe em branco para listar todos os preços recorrentes ativos',
586
600
  'new.firebase.q.sentry.dsn': 'DSN do Sentry (deixe em branco para configurar depois)',
587
601
  'new.firebase.q.sentry.dsn.invalid': 'DSN inválido. Formato esperado: https://chave@host/project-id',
588
602
  'new.firebase.q.mixpanel.token': 'Token do Mixpanel (deixe em branco para configurar depois)',
@@ -594,7 +608,6 @@ module.exports = {
594
608
  'new.firebase.q.revenuecat.android.required': 'Chave Android do RevenueCat é obrigatória.',
595
609
  'new.firebase.q.revenuecat.ios.required': 'Chave iOS do RevenueCat é obrigatória.',
596
610
  'new.firebase.q.revenuecat.metaDataset.invalid': 'Pixel ID deve ser numerico (ex: 1234567890).',
597
- 'new.firebase.q.revenuecat.webKey.invalid': 'Chave Web Billing deve começar com rcb_ (ex: rcb_sb_xxx ou rcb_xxx).',
598
611
 
599
612
  'new.firebase.confirm.title': 'Resumo da configuração:',
600
613
  'new.firebase.confirm.app': 'App',
@@ -707,6 +720,8 @@ module.exports = {
707
720
  'new.outdated.upgraded': 'kasy atualizado! Rode kasy new novamente.',
708
721
  'new.success.title': 'Projeto criado com sucesso!',
709
722
  'new.success.featuresInstalled': 'Recursos ativados:',
723
+ 'new.success.bundleId': 'Identificador do app (bundle ID)',
724
+ 'new.success.bundleId.hint': 'Identificador único do seu app no Android, iOS e Firebase (push).',
710
725
  'new.success.nextSteps': 'Proximos passos:',
711
726
  'new.success.step.cd': 'Entre na pasta do projeto:',
712
727
  'new.success.step.deploy': 'Suba o servidor pro Firebase (banco + funções):',
@@ -914,7 +929,7 @@ module.exports = {
914
929
  'notifications.error.notKasyProject': 'kit_setup.json não encontrado. Execute dentro de um projeto Kasy.',
915
930
  'notifications.error.noI18n': 'lib/i18n/*.i18n.json não encontrado neste projeto.',
916
931
  'notifications.error.noFeatures': 'lib/core/config/features.dart não encontrado.',
917
- 'notifications.error.notEnabled': 'Notificações locais desativadas. Execute: kasy add local_notifications',
932
+ 'notifications.error.notEnabled': 'Notificações locais desativadas. Execute: kasy add local_reminders',
918
933
  'notifications.error.cancelled': 'Cancelado.',
919
934
  'notifications.prompt.intro': 'Textos de notificação local',
920
935
  'notifications.prompt.hint': 'Demo Home = teste instantâneo em Features. Lembrete = alerta agendado em Configurações.',
@@ -957,26 +972,26 @@ module.exports = {
957
972
  // Legacy keys — kept for compatibility with old scripts.
958
973
  'add.prompt.rcAndroidKey': 'RevenueCat Android API key (deixe em branco para configurar depois):',
959
974
  'add.prompt.rcIosKey': 'RevenueCat iOS API key (deixe em branco para configurar depois):',
960
- 'add.note.facebook': 'Adicione seu Facebook App ID e token no .vscode/launch.json (FB_APP_ID, FB_TOKEN).',
961
- 'new.q.llm_chat.configureNow': 'Configurar o agente de Chat LLM agora?',
962
- 'new.q.llm_chat.configureNow.hint': 'Pode pular e executar "kasy add llm_chat" depois',
963
- 'add.llm_chat.reconfigure': 'Feature já ativa — reconfigurando apenas as credenciais.',
964
- 'add.prompt.llmProvider': 'Provedor de LLM:',
965
- 'add.prompt.llmSystemPrompt': 'Instrução do agente — system prompt (deixe em branco para nenhuma):\n Exemplo: "Você e um assistente de suporte do app Fitsync. Responda apenas sobre treinos."\n >',
966
- 'add.prompt.llmApiKey': 'Chave de API (OpenAI ou Gemini) — fica no servidor, nunca no app (deixe em branco para configurar depois):',
967
- 'add.prompt.llmEndpoint': 'URL do seu endpoint LLM (deixe em branco para configurar depois):',
968
- 'add.llm_chat.settingSecret': 'Salvando chave de API como secret no servidor...',
969
- 'add.llm_chat.secretSet': 'Chave de API salva como secret',
970
- 'add.llm_chat.secretFailed': 'Não foi possível salvar o secret automaticamente — configure manualmente (veja instruções abaixo)',
971
- 'add.llm_chat.skipSecret': 'Chave de API ignorada — configure antes do deploy via CLI do servidor',
972
- 'add.llm_chat.deploying': 'Fazendo deploy da função LLM no servidor...',
973
- 'add.llm_chat.deployed': 'Função LLM deployada com sucesso',
974
- 'add.llm_chat.deployFailed': 'Deploy automático falhou — faca o deploy manualmente (veja instruções abaixo)',
975
- 'add.llm_chat.nextSteps.firebase': '\n Proximos passos:\n 1. O LLM_CHAT_ENDPOINT no .vscode/launch.json já foi preenchido.\n 2. Rode o app: kasy run\n',
976
- 'add.llm_chat.nextSteps.firebase.deployFailed': '\n Proximos passos:\n 1. Deploy manual: firebase deploy --only functions:llmChat\n 2. O LLM_CHAT_ENDPOINT no .vscode/launch.json já foi preenchido.\n 3. Rode o app: kasy run\n',
977
- 'add.llm_chat.nextSteps.supabase': '\n Proximos passos:\n 1. O LLM_CHAT_ENDPOINT no .vscode/launch.json já foi preenchido.\n 2. Rode o app: kasy run\n',
978
- 'add.llm_chat.nextSteps.supabase.deployFailed': '\n Proximos passos:\n 1. Deploy manual: supabase functions deploy llm-chat --no-verify-jwt\n 2. O LLM_CHAT_ENDPOINT no .vscode/launch.json já foi preenchido.\n 3. Rode o app: kasy run\n',
979
- 'add.llm_chat.nextSteps.api': '\n Proximos passos:\n 1. Crie um endpoint no seu servidor que aceite {message, history} e chame sua LLM.\n 2. Atualize LLM_CHAT_ENDPOINT no .vscode/launch.json com a URL do seu endpoint.\n 3. Rode o app: kasy run\n',
975
+ 'add.note.facebook': 'Adicione seu Facebook App ID e token no .vscode/launch.json (FB_APP_ID, FB_CLIENT_TOKEN).',
976
+ 'new.q.ai_chat.configureNow': 'Configurar o agente de Chat IA agora?',
977
+ 'new.q.ai_chat.configureNow.hint': 'Pode pular e executar "kasy add ai_chat" depois',
978
+ 'add.ai_chat.reconfigure': 'Feature já ativa — reconfigurando apenas as credenciais.',
979
+ 'add.prompt.aiProvider': 'Provedor de IA:',
980
+ 'add.prompt.aiSystemPrompt': 'Instrução do agente — system prompt (deixe em branco para nenhuma):\n Exemplo: "Você e um assistente de suporte do app Fitsync. Responda apenas sobre treinos."\n >',
981
+ 'add.prompt.aiApiKey': 'Chave de API (OpenAI ou Gemini) — fica no servidor, nunca no app (deixe em branco para configurar depois):',
982
+ 'add.prompt.aiEndpoint': 'URL do seu endpoint IA (deixe em branco para configurar depois):',
983
+ 'add.ai_chat.settingSecret': 'Salvando chave de API como secret no servidor...',
984
+ 'add.ai_chat.secretSet': 'Chave de API salva como secret',
985
+ 'add.ai_chat.secretFailed': 'Não foi possível salvar o secret automaticamente — configure manualmente (veja instruções abaixo)',
986
+ 'add.ai_chat.skipSecret': 'Chave de API ignorada — configure antes do deploy via CLI do servidor',
987
+ 'add.ai_chat.deploying': 'Fazendo deploy da função IA no servidor...',
988
+ 'add.ai_chat.deployed': 'Função IA deployada com sucesso',
989
+ 'add.ai_chat.deployFailed': 'Deploy automático falhou — faca o deploy manualmente (veja instruções abaixo)',
990
+ 'add.ai_chat.nextSteps.firebase': '\n Proximos passos:\n 1. O AI_CHAT_ENDPOINT no .vscode/launch.json já foi preenchido.\n 2. Rode o app: kasy run\n',
991
+ 'add.ai_chat.nextSteps.firebase.deployFailed': '\n Proximos passos:\n 1. Deploy manual: firebase deploy --only functions:aiChat\n 2. O AI_CHAT_ENDPOINT no .vscode/launch.json já foi preenchido.\n 3. Rode o app: kasy run\n',
992
+ 'add.ai_chat.nextSteps.supabase': '\n Proximos passos:\n 1. O AI_CHAT_ENDPOINT no .vscode/launch.json já foi preenchido.\n 2. Rode o app: kasy run\n',
993
+ 'add.ai_chat.nextSteps.supabase.deployFailed': '\n Proximos passos:\n 1. Deploy manual: supabase functions deploy ai-chat --no-verify-jwt\n 2. O AI_CHAT_ENDPOINT no .vscode/launch.json já foi preenchido.\n 3. Rode o app: kasy run\n',
994
+ 'add.ai_chat.nextSteps.api': '\n Proximos passos:\n 1. Crie um endpoint no seu servidor que aceite {message, history} e chame sua IA.\n 2. Atualize AI_CHAT_ENDPOINT no .vscode/launch.json com a URL do seu endpoint.\n 3. Rode o app: kasy run\n',
980
995
  'cli.command.remove.description': 'Remove algo que você não usa mais (ex: kasy remove sentry)',
981
996
  'remove.error.noModule': 'Informe o nome da feature. Uso: kasy remove <feature>',
982
997
  'remove.error.notKasyProject': 'kit_setup.json não encontrado. Execute este comando dentro de um projeto Kasy.',
@@ -8,9 +8,9 @@ const pkg = require('../../package.json');
8
8
  const ui = require('./ui');
9
9
  const { CONFIG_PATH } = require('./license');
10
10
 
11
- const CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000; // 24 horas
11
+ const CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000; // 24 hours
12
12
 
13
- // ── Helpers de config ────────────────────────────────────────────────────────
13
+ // ── Config helpers ────────────────────────────────────────────────────────
14
14
 
15
15
  function readConfig() {
16
16
  if (!existsSync(CONFIG_PATH)) return {};
@@ -22,7 +22,7 @@ function writeConfig(config) {
22
22
  writeJsonSync(CONFIG_PATH, config, { spaces: 2 });
23
23
  }
24
24
 
25
- // ── Comparação semver mínima (sem deps externas) ─────────────────────────────
25
+ // ── Minimal semver comparison (no external deps) ─────────────────────────────
26
26
 
27
27
  function isNewer(remote, local) {
28
28
  const parse = (v) => String(v).replace(/^v/, '').split('.').map(Number);
@@ -33,7 +33,7 @@ function isNewer(remote, local) {
33
33
  return rPat > lPat;
34
34
  }
35
35
 
36
- // ── Fetch assíncrono (dispara em background) ─────────────────────────────────
36
+ // ── Async fetch (fires in the background) ─────────────────────────────────
37
37
 
38
38
  function fetchLatestVersion() {
39
39
  return new Promise((resolve) => {
@@ -54,19 +54,19 @@ function fetchLatestVersion() {
54
54
  });
55
55
  }
56
56
 
57
- // ── Função principal ─────────────────────────────────────────────────────────
57
+ // ── Main function ─────────────────────────────────────────────────────────
58
58
 
59
59
  /**
60
- * Mostra aviso de atualização (baseado no cache da última verificação — zero latência).
61
- * Dispara refresh do cache em background se passaram 24h.
62
- * Nunca lança erro falha silenciosa em qualquer cenário.
60
+ * Shows an update notice (based on the last-check cache — zero latency).
61
+ * Triggers a background cache refresh if 24h have passed.
62
+ * Never throwsfails silently in any scenario.
63
63
  */
64
64
  async function checkForUpdates() {
65
65
  try {
66
66
  const config = readConfig();
67
67
  const cache = config.updateCheck || {};
68
68
 
69
- // Exibe aviso baseado no cache — não espera rede
69
+ // Show a notice based on the cache — does not wait for the network
70
70
  if (cache.latestVersion && isNewer(cache.latestVersion, pkg.version)) {
71
71
  console.log(
72
72
  '\n' +
@@ -79,7 +79,7 @@ async function checkForUpdates() {
79
79
  );
80
80
  }
81
81
 
82
- // Atualiza cache em background a cada 24h — não bloqueia o CLI
82
+ // Refresh the cache in the background every 24h — does not block the CLI
83
83
  const now = Date.now();
84
84
  if (!cache.checkedAt || now - cache.checkedAt > CHECK_INTERVAL_MS) {
85
85
  fetchLatestVersion()
@@ -91,14 +91,14 @@ async function checkForUpdates() {
91
91
  .catch(() => {});
92
92
  }
93
93
  } catch {
94
- // Nunca travar o CLI por causa de verificação de update
94
+ // Never block the CLI because of an update check
95
95
  }
96
96
  }
97
97
 
98
98
  /**
99
- * Chamado antes de kasy new. Se houver versão mais nova no cache,
100
- * mostra aviso e pergunta se quer continuar ou sair para atualizar.
101
- * Nunca bloqueiase o usuário ignorar, o fluxo segue normalmente.
99
+ * Called before kasy new. If there is a newer version in the cache,
100
+ * shows a notice and asks whether to continue or exit to update.
101
+ * Never blocksif the user ignores it, the flow continues normally.
102
102
  */
103
103
  async function warnIfOutdatedBeforeNew(t) {
104
104
  try {
@@ -128,7 +128,7 @@ async function warnIfOutdatedBeforeNew(t) {
128
128
  process.exit(result.status ?? 0);
129
129
  }
130
130
  } catch {
131
- // Nunca travar o kasy new por causa do aviso de versão
131
+ // Never block kasy new because of the version notice
132
132
  }
133
133
  }
134
134
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kasy-cli",
3
- "version": "1.21.9",
3
+ "version": "1.22.0",
4
4
  "description": "CLI for scaffolding production-ready Flutter SaaS apps with Firebase, Supabase, or API REST backends.",
5
5
  "bin": {
6
6
  "kasy": "./bin/kasy.js"
@@ -6,8 +6,8 @@ ENV=dev
6
6
  # Firebase Cloud Functions (região + project id do seu projeto)
7
7
  BACKEND_URL=https://europe-west1-YOUR_PROJECT_ID.cloudfunctions.net
8
8
 
9
- # LLM Chat — proxy Cloud Function (LLM_API_KEY fica no Firebase Secret)
10
- LLM_CHAT_ENDPOINT=https://europe-west1-YOUR_PROJECT_ID.cloudfunctions.net/llmChat
9
+ # AI Chat — proxy Cloud Function (AI_API_KEY fica no Firebase Secret)
10
+ AI_CHAT_ENDPOINT=https://europe-west1-YOUR_PROJECT_ID.cloudfunctions.net/aiChat
11
11
 
12
12
  # RevenueCat
13
13
  RC_ANDROID_API_KEY=YOUR_REVENUECAT_ANDROID_KEY
@@ -6,7 +6,7 @@
6
6
  <item name="android:windowFullscreen">true</item>
7
7
  <item name="android:windowDrawsSystemBarBackgrounds">true</item>
8
8
  <item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
9
- <item name="android:windowSplashScreenBackground">#060608</item>
9
+ <item name="android:windowSplashScreenBackground">#0C0A14</item>
10
10
  <item name="android:windowSplashScreenAnimatedIcon">@drawable/android12splash</item>
11
11
  </style>
12
12
  <!-- Theme applied to the Android Window as soon as the process has started.
@@ -6,7 +6,7 @@
6
6
  <item name="android:windowFullscreen">true</item>
7
7
  <item name="android:windowDrawsSystemBarBackgrounds">true</item>
8
8
  <item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
9
- <item name="android:windowSplashScreenBackground">#F7F7F7</item>
9
+ <item name="android:windowSplashScreenBackground">#FAF9FC</item>
10
10
  <item name="android:windowSplashScreenAnimatedIcon">@drawable/android12splash</item>
11
11
  </style>
12
12
  <!-- Theme applied to the Android Window as soon as the process has started.
@@ -88,7 +88,7 @@ Encuentra la URL de la función:
88
88
  | Backend | Dónde encontrar la URL |
89
89
  |---------|------------------------|
90
90
  | **Supabase** | `https://TU_PROJECT_REF.supabase.co/functions/v1/revenuecat-webhook` |
91
- | **Firebase** | [Firebase Console → Functions](https://console.firebase.google.com/project/_/functions) → `subscriptionsOnRcPremiumUpdate` → copia la URL |
91
+ | **Firebase** | [Firebase Console → Functions](https://console.firebase.google.com/project/_/functions) → `subscriptions-revenuecatWebhook` → copia la URL |
92
92
 
93
93
  Regístrala en RevenueCat:
94
94
 
@@ -88,7 +88,7 @@ Encontre a URL da função:
88
88
  | Backend | Onde encontrar a URL |
89
89
  |---------|---------------------|
90
90
  | **Supabase** | `https://SEU_PROJECT_REF.supabase.co/functions/v1/revenuecat-webhook` |
91
- | **Firebase** | [Firebase Console → Functions](https://console.firebase.google.com/project/_/functions) → `subscriptionsOnRcPremiumUpdate` → copie a URL |
91
+ | **Firebase** | [Firebase Console → Functions](https://console.firebase.google.com/project/_/functions) → `subscriptions-revenuecatWebhook` → copie a URL |
92
92
 
93
93
  Registre no RevenueCat:
94
94
 
@@ -2,8 +2,22 @@ rules_version = '2';
2
2
  service cloud.firestore {
3
3
  match /databases/{database}/documents {
4
4
 
5
+ // True when the signed-in user has role == "admin" on their OWN user doc.
6
+ // `role` is writable only server-side (see the users rules below), so a
7
+ // client cannot forge it — this check is trustworthy and server-evaluated.
8
+ function isAdmin() {
9
+ return request.auth != null
10
+ && exists(/databases/$(database)/documents/users/$(request.auth.uid))
11
+ && get(/databases/$(database)/documents/users/$(request.auth.uid)).data.get('role', '') == 'admin';
12
+ }
13
+
5
14
  match /users/{id} {
6
- allow read, update, delete: if request.auth.uid != null && request.auth.uid == id;
15
+ allow read, delete: if request.auth.uid != null && request.auth.uid == id;
16
+ // Owners update their own profile, but NEVER their own `role`
17
+ // (no self-promotion to admin). `role` is set only server-side
18
+ // (Cloud Function / console), never from the client.
19
+ allow update: if request.auth.uid != null && request.auth.uid == id
20
+ && !request.resource.data.diff(resource.data).affectedKeys().hasAny(['role']);
7
21
  allow create: if false;
8
22
  }
9
23
  match /users/{id}/{document=**} {
@@ -23,10 +37,15 @@ service cloud.firestore {
23
37
  allow create: if request.auth.uid != null
24
38
  && request.resource.data.active == false
25
39
  && request.resource.data.votes == 0;
40
+ // Either a normal ±1 vote by any signed-in user, OR full moderation
41
+ // (toggle visibility, edit translations) by an admin.
26
42
  allow update: if request.auth.uid != null
27
- && request.resource.data.diff(resource.data).affectedKeys().hasOnly(['votes', 'last_update_date'])
28
- && (request.resource.data.votes == resource.data.votes + 1
29
- || request.resource.data.votes == resource.data.votes - 1);
43
+ && (
44
+ (request.resource.data.diff(resource.data).affectedKeys().hasOnly(['votes', 'last_update_date'])
45
+ && (request.resource.data.votes == resource.data.votes + 1
46
+ || request.resource.data.votes == resource.data.votes - 1))
47
+ || isAdmin()
48
+ );
30
49
  allow delete: if false;
31
50
  }
32
51
  match /feature_requests/{featureId}/votes/{id} {
@@ -48,4 +67,4 @@ service cloud.firestore {
48
67
  allow read, write: if false;
49
68
  }
50
69
  }
51
- }
70
+ }
@@ -7,7 +7,8 @@
7
7
  "name": "functions",
8
8
  "dependencies": {
9
9
  "firebase-admin": "^13.6.0",
10
- "firebase-functions": "^7.1.1"
10
+ "firebase-functions": "^7.1.1",
11
+ "stripe": "^18.5.0"
11
12
  },
12
13
  "devDependencies": {
13
14
  "@types/jest": "^29.5.14",
@@ -6239,6 +6240,26 @@
6239
6240
  "url": "https://github.com/sponsors/sindresorhus"
6240
6241
  }
6241
6242
  },
6243
+ "node_modules/stripe": {
6244
+ "version": "18.5.0",
6245
+ "resolved": "https://registry.npmjs.org/stripe/-/stripe-18.5.0.tgz",
6246
+ "integrity": "sha512-Hp+wFiEQtCB0LlNgcFh5uVyKznpDjzyUZ+CNVEf+I3fhlYvh7rZruIg+jOwzJRCpy0ZTPMjlzm7J2/M2N6d+DA==",
6247
+ "license": "MIT",
6248
+ "dependencies": {
6249
+ "qs": "^6.11.0"
6250
+ },
6251
+ "engines": {
6252
+ "node": ">=12.*"
6253
+ },
6254
+ "peerDependencies": {
6255
+ "@types/node": ">=12.x.x"
6256
+ },
6257
+ "peerDependenciesMeta": {
6258
+ "@types/node": {
6259
+ "optional": true
6260
+ }
6261
+ }
6262
+ },
6242
6263
  "node_modules/strnum": {
6243
6264
  "version": "2.2.0",
6244
6265
  "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.2.0.tgz",
@@ -19,7 +19,8 @@
19
19
  "main": "lib/index.js",
20
20
  "dependencies": {
21
21
  "firebase-admin": "^13.6.0",
22
- "firebase-functions": "^7.1.1"
22
+ "firebase-functions": "^7.1.1",
23
+ "stripe": "^18.5.0"
23
24
  },
24
25
  "devDependencies": {
25
26
  "@types/jest": "^29.5.14",
@@ -0,0 +1,113 @@
1
+ import {onCall, HttpsError} from "firebase-functions/v2/https";
2
+ import * as admin from "firebase-admin";
3
+ import {Logger} from "../core/logger/logger";
4
+
5
+ // In-memory cap: the admin console loads up to this many of the most recent
6
+ // users in ONE call, then searches / sorts / paginates entirely on the client
7
+ // for an instant experience (no extra reads when typing or paging). Apps that
8
+ // outgrow this should add a dedicated search index (Algolia/Typesense) — see
9
+ // README. Reads are admin-only and bounded, so the cost stays predictable.
10
+ const MAX_SCAN = 1000;
11
+
12
+ // getAll() batch size for the subscription lookups (Firestore caps a single
13
+ // getAll at a few hundred refs comfortably; chunking keeps it safe).
14
+ const SUB_CHUNK = 200;
15
+
16
+ // A user counts as a subscriber when their subscription is currently usable.
17
+ const ACTIVE_SUBSCRIPTION_STATUSES = ["ACTIVE", "LIFETIME"];
18
+
19
+ interface AdminUser {
20
+ id: string;
21
+ email: string | null;
22
+ name: string | null;
23
+ createdAt: number | null; // epoch millis
24
+ subscriber: boolean;
25
+ }
26
+
27
+ /**
28
+ * Lists app users for the admin console.
29
+ *
30
+ * Security: the caller must be authenticated AND have `role == "admin"` on their
31
+ * own `users/{uid}` document. Clients cannot read the whole users collection
32
+ * (Firestore rules), so this runs with Admin privileges and gates on the role.
33
+ *
34
+ * Returns the most-recent users (bounded to MAX_SCAN) with their subscriber
35
+ * status already resolved. The client (admin_users_tab.dart) does the search,
36
+ * sort and pagination locally so those interactions are instant. Shape:
37
+ * { users: AdminUser[], totalUsers: number, truncated: boolean }
38
+ * - totalUsers: the true size of the collection (for an honest count).
39
+ * - truncated: true when the collection is larger than what we returned, so
40
+ * the UI can warn that search only covers the loaded set.
41
+ */
42
+ export const listUsers = onCall(async (request) => {
43
+ if (!request.auth) {
44
+ throw new HttpsError("unauthenticated", "Authentication required");
45
+ }
46
+ const callerUid = request.auth.uid;
47
+ const db = admin.firestore();
48
+ const logger = new Logger("listUsers");
49
+
50
+ // Admin gate: the caller's own user doc must carry role == "admin".
51
+ const callerDoc = await db.collection("users").doc(callerUid).get();
52
+ if (!callerDoc.exists || callerDoc.get("role") !== "admin") {
53
+ throw new HttpsError("permission-denied", "Admin role required");
54
+ }
55
+
56
+ try {
57
+ const usersRef = db.collection("users");
58
+
59
+ // Bounded fetch — the client does the search/sort/paginate.
60
+ //
61
+ // IMPORTANT: do NOT order this query by "creation_date". A Firestore
62
+ // orderBy silently DROPS every document that lacks the ordered field, and
63
+ // some user docs have no creation_date (e.g. accounts whose doc was created
64
+ // client-side during anonymous→social linking; the auth trigger then only
65
+ // patches the email and never backfills creation_date). Ordering by it
66
+ // would hide exactly those real, active users. We fetch unordered and sort
67
+ // in memory below (newest first, undated last) so every user is returned.
68
+ const snapshot = await usersRef.limit(MAX_SCAN).get();
69
+ const docs = snapshot.docs;
70
+
71
+ // Subscriber status for the whole batch, in chunked getAll() reads.
72
+ const activeSubscribers = new Set<string>();
73
+ for (let i = 0; i < docs.length; i += SUB_CHUNK) {
74
+ const chunk = docs.slice(i, i + SUB_CHUNK);
75
+ const refs = chunk.map((d) => db.collection("subscriptions").doc(d.id));
76
+ const snaps = await db.getAll(...refs);
77
+ for (const s of snaps) {
78
+ if (
79
+ s.exists &&
80
+ ACTIVE_SUBSCRIPTION_STATUSES.includes(s.get("status"))
81
+ ) {
82
+ activeSubscribers.add(s.id);
83
+ }
84
+ }
85
+ }
86
+
87
+ const users: AdminUser[] = docs.map((d) => {
88
+ const createdAt = d.get("creation_date");
89
+ return {
90
+ id: d.id,
91
+ email: (d.get("email") as string) || null,
92
+ name: (d.get("name") as string) || null,
93
+ createdAt: createdAt ? createdAt.toMillis() : null,
94
+ subscriber: activeSubscribers.has(d.id),
95
+ };
96
+ });
97
+
98
+ // Newest first; users without a creation_date sort to the end.
99
+ users.sort((a, b) => (b.createdAt ?? 0) - (a.createdAt ?? 0));
100
+
101
+ const countSnap = await usersRef.count().get();
102
+ const totalUsers = countSnap.data().count;
103
+
104
+ return {
105
+ users,
106
+ totalUsers,
107
+ truncated: totalUsers > users.length,
108
+ };
109
+ } catch (e) {
110
+ logger.error(`listUsers error: ${e}`);
111
+ throw new HttpsError("internal", "Failed to list users");
112
+ }
113
+ });