kasy-cli 1.38.0 → 1.39.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 (102) hide show
  1. package/lib/scaffold/CHANGELOG.json +14 -0
  2. package/lib/scaffold/backends/api/patch/README.md +15 -0
  3. package/lib/scaffold/backends/api/patch/lib/features/notifications/api/device_api.dart +11 -6
  4. package/lib/scaffold/backends/api/patch/lib/features/settings/ui/components/admin/admin_users_api.dart +9 -1
  5. package/lib/scaffold/backends/api/pubspec.yaml.tpl +1 -0
  6. package/lib/scaffold/backends/patch-base-hashes.json +6 -6
  7. package/lib/scaffold/backends/supabase/edge-functions/admin-list-users/index.ts +3 -1
  8. package/lib/scaffold/backends/supabase/edge-functions/send-push-notification/index.ts +3 -0
  9. package/lib/scaffold/backends/supabase/migrations/20240101000012_welcome_decouple_from_push.sql +62 -0
  10. package/lib/scaffold/backends/supabase/patch/lib/features/notifications/api/device_api.dart +11 -6
  11. package/lib/scaffold/backends/supabase/patch/lib/features/settings/ui/components/admin/admin_users_api.dart +7 -0
  12. package/lib/scaffold/backends/supabase/pubspec.yaml.tpl +1 -0
  13. package/lib/scaffold/shared/generator-utils.js +12 -6
  14. package/package.json +1 -1
  15. package/templates/firebase/.firebase/hosting.YnVpbGQvd2Vi.cache +23 -23
  16. package/templates/firebase/DESIGN_SYSTEM.md +22 -8
  17. package/templates/firebase/assets/icons/apple_black.svg +3 -0
  18. package/templates/firebase/assets/icons/apple_white.svg +4 -0
  19. package/templates/firebase/assets/icons/facebook.svg +49 -0
  20. package/templates/firebase/assets/icons/google.svg +1 -0
  21. package/templates/firebase/functions/src/admin/functions.ts +2 -0
  22. package/templates/firebase/functions/src/authentication/functions.ts +13 -7
  23. package/templates/firebase/functions/src/notifications/triggers.ts +6 -2
  24. package/templates/firebase/lib/components/components.dart +1 -1
  25. package/templates/firebase/lib/components/kasy_app_bar.dart +314 -14
  26. package/templates/firebase/lib/components/kasy_card.dart +4 -0
  27. package/templates/firebase/lib/components/kasy_drop_down.dart +584 -0
  28. package/templates/firebase/lib/components/kasy_sidebar.dart +18 -6
  29. package/templates/firebase/lib/components/kasy_tabs.dart +31 -10
  30. package/templates/firebase/lib/components/kasy_text_field.dart +29 -7
  31. package/templates/firebase/lib/core/bottom_menu/bottom_menu.dart +27 -18
  32. package/templates/firebase/lib/core/bottom_menu/web_content_wrapper.dart +12 -10
  33. package/templates/firebase/lib/core/chrome/app_bar_config.dart +214 -0
  34. package/templates/firebase/lib/core/chrome/app_bar_scope.dart +102 -0
  35. package/templates/firebase/lib/core/data/api/user_api.dart +11 -0
  36. package/templates/firebase/lib/core/dev_inspector/dev_inspector_service.dart +55 -15
  37. package/templates/firebase/lib/core/rating/widgets/review_popup.dart +18 -35
  38. package/templates/firebase/lib/core/shared_preferences/shared_preferences.dart +11 -0
  39. package/templates/firebase/lib/core/states/logout_action.dart +11 -1
  40. package/templates/firebase/lib/core/states/user_state_notifier.dart +28 -1
  41. package/templates/firebase/lib/core/theme/texts.dart +21 -6
  42. package/templates/firebase/lib/core/theme/type_scale.dart +34 -15
  43. package/templates/firebase/lib/core/web_device_preview/web_device_preview.dart +51 -19
  44. package/templates/firebase/lib/core/web_viewport_scale.dart +64 -35
  45. package/templates/firebase/lib/core/widgets/kasy_pressable_depth.dart +14 -3
  46. package/templates/firebase/lib/features/ai_chat/ui/widgets/ai_chat_composer.dart +1 -1
  47. package/templates/firebase/lib/features/ai_chat/ui/widgets/ai_chat_conversation_view.dart +52 -35
  48. package/templates/firebase/lib/features/ai_chat/ui/widgets/ai_conversation_list.dart +1 -1
  49. package/templates/firebase/lib/features/ai_chat/ui/widgets/ai_conversation_tile.dart +18 -8
  50. package/templates/firebase/lib/features/authentication/ui/signin_page.dart +11 -61
  51. package/templates/firebase/lib/features/authentication/ui/signup_page.dart +11 -61
  52. package/templates/firebase/lib/features/authentication/ui/widgets/auth_card_scaffold.dart +7 -5
  53. package/templates/firebase/lib/features/authentication/ui/widgets/social_auth_tile.dart +83 -0
  54. package/templates/firebase/lib/features/feedbacks/ui/widgets/feature_card.dart +4 -4
  55. package/templates/firebase/lib/features/home/home_components_page.dart +253 -125
  56. package/templates/firebase/lib/features/home/home_components_preview_registry.dart +231 -57
  57. package/templates/firebase/lib/features/home/home_feed.dart +2 -2
  58. package/templates/firebase/lib/features/home/home_image_grid.dart +3 -3
  59. package/templates/firebase/lib/features/local_reminders/providers/reminder_notifier.dart +8 -1
  60. package/templates/firebase/lib/features/local_reminders/ui/reminder_page.dart +111 -57
  61. package/templates/firebase/lib/features/notifications/api/device_api.dart +11 -3
  62. package/templates/firebase/lib/features/notifications/ui/notifications_page.dart +16 -4
  63. package/templates/firebase/lib/features/notifications/ui/widgets/empty_notifications.dart +7 -56
  64. package/templates/firebase/lib/features/notifications/ui/widgets/notification_tile.dart +5 -5
  65. package/templates/firebase/lib/features/notifications/ui/widgets/push_permission_banner.dart +163 -0
  66. package/templates/firebase/lib/features/notifications/ui/widgets/web_notifications_bell.dart +2 -2
  67. package/templates/firebase/lib/features/onboarding/providers/onboarding_model.dart +9 -0
  68. package/templates/firebase/lib/features/onboarding/providers/onboarding_provider.dart +28 -0
  69. package/templates/firebase/lib/features/onboarding/ui/onboarding_page.dart +51 -12
  70. package/templates/firebase/lib/features/onboarding/ui/widgets/selectable_row_tile.dart +90 -32
  71. package/templates/firebase/lib/features/settings/settings_page.dart +53 -32
  72. package/templates/firebase/lib/features/settings/ui/components/admin/admin_page.dart +895 -111
  73. package/templates/firebase/lib/features/settings/ui/components/admin/admin_users_api.dart +7 -0
  74. package/templates/firebase/lib/features/settings/ui/components/admin/admin_users_tab.dart +445 -233
  75. package/templates/firebase/lib/features/settings/ui/components/admin/send_push_notification_page.dart +171 -41
  76. package/templates/firebase/lib/features/settings/ui/components/avatar_component.dart +1 -1
  77. package/templates/firebase/lib/features/settings/ui/components/delete_user_component.dart +9 -1
  78. package/templates/firebase/lib/features/settings/ui/components/language_switcher.dart +48 -47
  79. package/templates/firebase/lib/features/settings/ui/widgets/settings_bottom_sheet_option_tile.dart +21 -18
  80. package/templates/firebase/lib/features/settings/ui/widgets/settings_tile.dart +25 -10
  81. package/templates/firebase/lib/i18n/en.i18n.json +749 -712
  82. package/templates/firebase/lib/i18n/es.i18n.json +749 -712
  83. package/templates/firebase/lib/i18n/pt.i18n.json +749 -712
  84. package/templates/firebase/lib/main.dart +20 -7
  85. package/templates/firebase/lib/router.dart +32 -26
  86. package/templates/firebase/pubspec.yaml +2 -1
  87. package/templates/firebase/test/admin_shell_chrome_test.dart +11 -5
  88. package/templates/firebase/test/components/kasy_text_field_height_test.dart +77 -0
  89. package/templates/firebase/test/core/web_viewport_scale_test.dart +23 -16
  90. package/templates/firebase/tool/design_check.dart +9 -0
  91. package/templates/firebase/assets/icons/apple.png +0 -0
  92. package/templates/firebase/assets/icons/facebook.png +0 -0
  93. package/templates/firebase/assets/icons/google.png +0 -0
  94. package/templates/firebase/assets/icons/google_play_games.png +0 -0
  95. package/templates/firebase/lib/components/kasy_web_header.dart +0 -218
  96. package/templates/firebase/lib/core/chrome/web_header_scope.dart +0 -20
  97. package/templates/firebase/lib/features/authentication/ui/components/apple_signin.dart +0 -19
  98. package/templates/firebase/lib/features/authentication/ui/components/google_signin.dart +0 -32
  99. package/templates/firebase/lib/features/authentication/ui/widgets/round_signin.dart +0 -73
  100. package/templates/firebase/lib/features/feedbacks/ui/widgets/add_feature_button.dart +0 -66
  101. package/templates/firebase/lib/features/notifications/ui/components/notification_settings_sheet.dart +0 -179
  102. package/templates/firebase/lib/features/notifications/ui/components/push_notification_switcher.dart +0 -106
@@ -1,73 +0,0 @@
1
- import 'package:flutter/material.dart';
2
- import 'package:kasy_kit/core/haptics/kasy_haptics.dart';
3
- import 'package:kasy_kit/core/theme/theme.dart';
4
-
5
- typedef SocialSigninCallback = void Function();
6
-
7
- class SocialSigninButton extends StatelessWidget {
8
- final Image iconImage;
9
- final SocialSigninCallback? onPressed;
10
-
11
- const SocialSigninButton({
12
- super.key,
13
- required this.iconImage,
14
- required this.onPressed,
15
- });
16
-
17
- factory SocialSigninButton.google(SocialSigninCallback onPressed) {
18
- return SocialSigninButton(
19
- iconImage: Image.asset("assets/icons/google.png", width: 24),
20
- onPressed: onPressed,
21
- );
22
- }
23
-
24
- factory SocialSigninButton.googlePlayGames(SocialSigninCallback onPressed) {
25
- return SocialSigninButton(
26
- iconImage: Image.asset("assets/icons/google_play_games.png", width: 24),
27
- onPressed: onPressed,
28
- );
29
- }
30
-
31
- factory SocialSigninButton.facebook(SocialSigninCallback onPressed) {
32
- return SocialSigninButton(
33
- iconImage: Image.asset("assets/icons/facebook.png", width: 24),
34
- onPressed: onPressed,
35
- );
36
- }
37
-
38
- factory SocialSigninButton.apple(SocialSigninCallback onPressed) {
39
- return SocialSigninButton(
40
- iconImage: Image.asset("assets/icons/apple.png", width: 24),
41
- onPressed: onPressed,
42
- );
43
- }
44
-
45
- @override
46
- Widget build(BuildContext context) {
47
- return Container(
48
- width: 56,
49
- decoration: BoxDecoration(
50
- border: Border.all(color: context.colors.outline),
51
- shape: BoxShape.circle,
52
- ),
53
- child: RawMaterialButton(
54
- elevation: 0,
55
- focusElevation: 0,
56
- hoverElevation: 0,
57
- highlightElevation: 0,
58
- splashColor: Colors.transparent,
59
- highlightColor: Colors.transparent,
60
- hoverColor: Colors.transparent,
61
- focusColor: Colors.transparent,
62
- onPressed: () {
63
- KasyHaptics.medium(context);
64
- onPressed?.call();
65
- },
66
- shape: const CircleBorder(),
67
- fillColor: Colors.transparent,
68
- padding: const EdgeInsets.all(KasySpacing.md),
69
- child: iconImage,
70
- ),
71
- );
72
- }
73
- }
@@ -1,66 +0,0 @@
1
- import 'package:flutter/material.dart';
2
- import 'package:kasy_kit/core/theme/theme.dart';
3
- import 'package:kasy_kit/core/widgets/kasy_hover.dart';
4
-
5
- class AddFeatureButton extends StatelessWidget {
6
- final String title;
7
- final String description;
8
- final VoidCallback onPressed;
9
-
10
- const AddFeatureButton({
11
- super.key,
12
- required this.title,
13
- required this.description,
14
- required this.onPressed,
15
- });
16
-
17
- @override
18
- Widget build(BuildContext context) {
19
- return KasyHover(
20
- onTap: onPressed,
21
- borderRadius: KasyRadius.mdBorderRadius,
22
- pressColor: context.colors.onPrimary,
23
- focusable: true,
24
- child: Container(
25
- padding: const EdgeInsets.symmetric(
26
- horizontal: KasySpacing.lg,
27
- vertical: KasySpacing.smd,
28
- ),
29
- decoration: BoxDecoration(
30
- color: context.colors.primary,
31
- borderRadius: KasyRadius.mdBorderRadius,
32
- ),
33
- child: Row(
34
- children: [
35
- Expanded(
36
- child: Column(
37
- crossAxisAlignment: CrossAxisAlignment.start,
38
- spacing: KasySpacing.xs,
39
- children: [
40
- Text(
41
- title,
42
- style: context.textTheme.titleMedium?.copyWith(
43
- color: context.colors.onPrimary,
44
- ),
45
- ),
46
- Text(
47
- description,
48
- style: context.textTheme.bodySmall?.copyWith(
49
- color: context.colors.onPrimary.withValues(alpha: 0.72),
50
- ),
51
- ),
52
- ],
53
- ),
54
- ),
55
- const SizedBox(width: KasySpacing.smd),
56
- Icon(
57
- KasyIcons.northEast,
58
- size: KasyIconSize.rowTrailing,
59
- color: context.colors.onPrimary.withValues(alpha: 0.55),
60
- ),
61
- ],
62
- ),
63
- ),
64
- );
65
- }
66
- }
@@ -1,179 +0,0 @@
1
- import 'package:flutter/material.dart';
2
- import 'package:flutter_riverpod/flutter_riverpod.dart';
3
- import 'package:kasy_kit/components/kasy_bottom_sheet.dart';
4
- import 'package:kasy_kit/core/theme/theme.dart';
5
- import 'package:kasy_kit/features/notifications/providers/models/notification.dart';
6
- import 'package:kasy_kit/features/notifications/providers/notifications_provider.dart';
7
- import 'package:kasy_kit/features/notifications/repositories/notifications_repository.dart';
8
- import 'package:kasy_kit/features/settings/ui/widgets/settings_tile.dart';
9
- import 'package:kasy_kit/i18n/translations.g.dart';
10
-
11
- void showNotificationSettingsSheet(BuildContext context) {
12
- showKasyBottomSheet<void>(
13
- context: context,
14
- isScrollControlled: true,
15
- builder: (_) => const _NotificationSettingsSheet(),
16
- );
17
- }
18
-
19
- class _NotificationSettingsSheet extends ConsumerStatefulWidget {
20
- const _NotificationSettingsSheet();
21
-
22
- @override
23
- ConsumerState<_NotificationSettingsSheet> createState() =>
24
- _NotificationSettingsSheetState();
25
- }
26
-
27
- class _NotificationSettingsSheetState
28
- extends ConsumerState<_NotificationSettingsSheet> {
29
- NotificationPermission? _permission;
30
- bool _toggling = false;
31
-
32
- @override
33
- void initState() {
34
- super.initState();
35
- _loadPermission();
36
- }
37
-
38
- Future<void> _loadPermission() async {
39
- final p =
40
- await ref.read(notificationRepositoryProvider).getPermissionStatus();
41
- if (mounted) setState(() => _permission = p);
42
- }
43
-
44
- bool get _isEnabled => _permission is NotificationPermissionGranted;
45
-
46
- String _subtitle(BuildContext context) {
47
- final tr = context.t.notifications;
48
- return switch (_permission) {
49
- NotificationPermissionGranted() => tr.push_subtitle_enabled,
50
- NotificationPermissionDenied() => tr.push_subtitle_disabled,
51
- _ => tr.push_subtitle_waiting,
52
- };
53
- }
54
-
55
- Future<void> _onToggle(bool value) async {
56
- if (_toggling || _permission == null) return;
57
- setState(() => _toggling = true);
58
- await _permission!.maybeAsk();
59
- await _loadPermission();
60
- if (mounted) setState(() => _toggling = false);
61
- }
62
-
63
- Future<void> _markAllRead() async {
64
- await ref.read(notificationsProvider.notifier).readAll();
65
- if (mounted) Navigator.of(context).pop();
66
- }
67
-
68
- @override
69
- Widget build(BuildContext context) {
70
- final tr = context.t.notifications;
71
- final notificationsState = ref.watch(notificationsProvider);
72
- final hasUnread =
73
- notificationsState.value?.data.any((n) => !n.seen) ?? false;
74
-
75
- final bool floating = kasySheetIsFloating(context);
76
-
77
- final Widget card = Container(
78
- margin: floating
79
- ? EdgeInsets.zero
80
- : EdgeInsets.only(
81
- left: KasySpacing.sm,
82
- right: KasySpacing.sm,
83
- bottom: MediaQuery.of(context).viewInsets.bottom +
84
- MediaQuery.of(context).padding.bottom +
85
- KasySpacing.sm,
86
- ),
87
- decoration: BoxDecoration(
88
- color: context.colors.background,
89
- borderRadius: KasyRadius.lgBorderRadius,
90
- border: Border.all(
91
- color: context.colors.outline.withValues(alpha: 0.5),
92
- ),
93
- ),
94
- child: Column(
95
- mainAxisSize: MainAxisSize.min,
96
- children: [
97
- // drag handle (bottom-sheet form only)
98
- if (!floating)
99
- Padding(
100
- padding: const EdgeInsets.only(top: KasySpacing.smd),
101
- child: Container(
102
- width: 36,
103
- height: 4,
104
- decoration: BoxDecoration(
105
- color: context.colors.onSurface.withValues(alpha: 0.14),
106
- borderRadius: KasyRadius.fullBorderRadius,
107
- ),
108
- ),
109
- ),
110
- Padding(
111
- padding: const EdgeInsets.fromLTRB(
112
- KasySpacing.md,
113
- KasySpacing.md,
114
- KasySpacing.md,
115
- KasySpacing.smd,
116
- ),
117
- child: Column(
118
- crossAxisAlignment: CrossAxisAlignment.start,
119
- children: [
120
- // Push toggle row
121
- SettingsSwitchTile(
122
- icon: _isEnabled
123
- ? KasyIcons.notificationActive
124
- : KasyIcons.notification,
125
- title: tr.push_title,
126
- value: _isEnabled,
127
- iconBackgroundColor: _isEnabled
128
- ? const Color(0xFFFF6B35) // design-check: ignore — category accent
129
- : const Color(0xFF78909C), // design-check: ignore — category accent
130
- onChanged: _toggling ? (_) {} : _onToggle,
131
- ),
132
- // subtitle below
133
- Padding(
134
- padding: const EdgeInsets.only(
135
- left: 32 + KasySpacing.sm,
136
- bottom: KasySpacing.xs,
137
- ),
138
- child: _permission == null
139
- ? SizedBox(
140
- height: 13,
141
- width: 140,
142
- child: LinearProgressIndicator(
143
- borderRadius: BorderRadius.circular(4),
144
- color: context.colors.primary.withValues(alpha: 0.25),
145
- backgroundColor: context.colors.onSurface
146
- .withValues(alpha: 0.06),
147
- ),
148
- )
149
- : Text(
150
- _subtitle(context),
151
- style: context.textTheme.bodySmall?.copyWith(
152
- color: context.colors.muted,
153
- ),
154
- ),
155
- ),
156
- if (hasUnread) ...[
157
- const SettingsDivider(),
158
- const SizedBox(height: KasySpacing.xs),
159
- SettingsTile(
160
- icon: KasyIcons.checkCircle,
161
- title: tr.mark_all_read,
162
- iconBackgroundColor: context.colors.primary,
163
- onTap: _markAllRead,
164
- ),
165
- ],
166
- ],
167
- ),
168
- ),
169
- ],
170
- ),
171
- );
172
-
173
- if (!floating) return card;
174
- return ConstrainedBox(
175
- constraints: const BoxConstraints(maxWidth: 420),
176
- child: card,
177
- );
178
- }
179
- }
@@ -1,106 +0,0 @@
1
- import 'package:flutter/material.dart';
2
- import 'package:flutter_riverpod/flutter_riverpod.dart';
3
- import 'package:kasy_kit/core/theme/theme.dart';
4
- import 'package:kasy_kit/features/notifications/providers/models/notification.dart';
5
- import 'package:kasy_kit/features/notifications/repositories/notifications_repository.dart';
6
- import 'package:kasy_kit/features/settings/ui/widgets/settings_tile.dart';
7
- import 'package:kasy_kit/i18n/translations.g.dart';
8
-
9
- class PushNotificationSwitcher extends ConsumerStatefulWidget {
10
- const PushNotificationSwitcher({super.key});
11
-
12
- @override
13
- ConsumerState<PushNotificationSwitcher> createState() =>
14
- _PushNotificationSwitcherState();
15
- }
16
-
17
- class _PushNotificationSwitcherState
18
- extends ConsumerState<PushNotificationSwitcher> {
19
- NotificationPermission? _permission;
20
- bool _loading = false;
21
-
22
- @override
23
- void initState() {
24
- super.initState();
25
- _loadPermission();
26
- }
27
-
28
- Future<void> _loadPermission() async {
29
- final p =
30
- await ref.read(notificationRepositoryProvider).getPermissionStatus();
31
- if (mounted) setState(() => _permission = p);
32
- }
33
-
34
- bool get _isEnabled => _permission is NotificationPermissionGranted;
35
-
36
- String _subtitle(BuildContext context) {
37
- final tr = context.t.notifications;
38
- return switch (_permission) {
39
- NotificationPermissionGranted() => tr.push_subtitle_enabled,
40
- NotificationPermissionDenied() => tr.push_subtitle_disabled,
41
- _ => tr.push_subtitle_waiting,
42
- };
43
- }
44
-
45
- Future<void> _onChanged(bool value) async {
46
- if (_loading || _permission == null) return;
47
- setState(() => _loading = true);
48
- await _permission!.maybeAsk();
49
- await _loadPermission();
50
- if (mounted) setState(() => _loading = false);
51
- }
52
-
53
- @override
54
- Widget build(BuildContext context) {
55
- final tr = context.t.notifications;
56
- return Container(
57
- padding: const EdgeInsets.symmetric(
58
- vertical: KasySpacing.smd,
59
- horizontal: KasySpacing.md,
60
- ),
61
- decoration: BoxDecoration(
62
- borderRadius: KasyRadius.smBorderRadius,
63
- color: context.colors.surface,
64
- ),
65
- child: Column(
66
- crossAxisAlignment: CrossAxisAlignment.start,
67
- children: [
68
- SettingsSwitchTile(
69
- icon: _isEnabled
70
- ? KasyIcons.notificationActive
71
- : KasyIcons.notification,
72
- title: tr.push_title,
73
- value: _isEnabled,
74
- iconBackgroundColor: _isEnabled
75
- ? const Color(0xFFFF6B35) // design-check: ignore — category accent
76
- : const Color(0xFF78909C), // design-check: ignore — category accent
77
- onChanged: _loading ? (_) {} : _onChanged,
78
- ),
79
- Padding(
80
- padding: const EdgeInsets.only(
81
- left: 32 + KasySpacing.sm,
82
- bottom: KasySpacing.xs,
83
- ),
84
- child: _permission == null
85
- ? SizedBox(
86
- height: 14,
87
- width: 120,
88
- child: LinearProgressIndicator(
89
- borderRadius: BorderRadius.circular(4),
90
- color: context.colors.primary.withValues(alpha: 0.3),
91
- backgroundColor:
92
- context.colors.onSurface.withValues(alpha: 0.06),
93
- ),
94
- )
95
- : Text(
96
- _subtitle(context),
97
- style: context.textTheme.bodySmall?.copyWith(
98
- color: context.colors.muted,
99
- ),
100
- ),
101
- ),
102
- ],
103
- ),
104
- );
105
- }
106
- }