coregen-cli 3.1.6__py3-none-any.whl
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.
- coregen.py +1585 -0
- coregen_cli-3.1.6.dist-info/METADATA +1211 -0
- coregen_cli-3.1.6.dist-info/RECORD +172 -0
- coregen_cli-3.1.6.dist-info/WHEEL +5 -0
- coregen_cli-3.1.6.dist-info/entry_points.txt +2 -0
- coregen_cli-3.1.6.dist-info/top_level.txt +2 -0
- generators/__init__.py +1 -0
- generators/assets/__init__.py +1 -0
- generators/assets/assets.py +30 -0
- generators/assets/main.py +4 -0
- generators/config/__init__.py +1 -0
- generators/config/analisy_options.py +42 -0
- generators/config/main.py +11 -0
- generators/config/pubspec.py +139 -0
- generators/cursor/__init__.py +3 -0
- generators/cursor/setup.py +87 -0
- generators/helpers/__init__.py +56 -0
- generators/helpers/component.py +1030 -0
- generators/helpers/config.py +255 -0
- generators/helpers/data_source.py +180 -0
- generators/helpers/domain.py +469 -0
- generators/helpers/feature.py +654 -0
- generators/helpers/navigation.py +274 -0
- generators/helpers/page.py +63 -0
- generators/helpers/project.py +33 -0
- generators/helpers/utils.py +338 -0
- generators/helpers/validation.py +364 -0
- generators/initializator.py +68 -0
- generators/main.py +81 -0
- generators/static/assets/logo.png +0 -0
- generators/static/assets/mock/auth_user.json +5 -0
- generators/static/assets/svgs/apple.svg +4 -0
- generators/static/assets/svgs/google.svg +2 -0
- generators/static/cursor/AGENTS.md.jinja +37 -0
- generators/static/cursor/agents/doc-writer.md +20 -0
- generators/static/cursor/agents/epic-orchestrator.md +28 -0
- generators/static/cursor/agents/feature-implementer.md +29 -0
- generators/static/cursor/agents/integration-wiring.md +30 -0
- generators/static/cursor/agents/layer-application.md +21 -0
- generators/static/cursor/agents/layer-guardian.md +21 -0
- generators/static/cursor/agents/layer-infrastructure.md +31 -0
- generators/static/cursor/agents/layer-model.md +24 -0
- generators/static/cursor/agents/layer-presentation.md +33 -0
- generators/static/cursor/docs/architecture/APIS_AND_INTEGRATION.md.jinja +42 -0
- generators/static/cursor/docs/architecture/CARAVAGGIO_COMPONENTS.md.jinja +317 -0
- generators/static/cursor/docs/architecture/DDD_LAYERS.md.jinja +32 -0
- generators/static/cursor/docs/architecture/FILE_TEMPLATES.md.jinja +76 -0
- generators/static/cursor/docs/architecture/MOCK_AND_REMOTE_DATA.md.jinja +87 -0
- generators/static/cursor/docs/architecture/REFERENCE_IMPLEMENTATIONS.md.jinja +48 -0
- generators/static/cursor/docs/architecture/WIDGETS_AND_CARAVAGGIO.md.jinja +76 -0
- generators/static/cursor/docs/epics/README.md +17 -0
- generators/static/cursor/lib/domain/AGENTS.md +23 -0
- generators/static/cursor/lib/features/AGENTS.md +23 -0
- generators/static/cursor/lib/widgets/AGENTS.md +21 -0
- generators/static/cursor/rules/apis-layer.mdc.jinja +21 -0
- generators/static/cursor/rules/application-layer.mdc.jinja +12 -0
- generators/static/cursor/rules/architecture-core.mdc.jinja +43 -0
- generators/static/cursor/rules/dart-conventions.mdc.jinja +14 -0
- generators/static/cursor/rules/domain-layer.mdc.jinja +24 -0
- generators/static/cursor/rules/login-context.mdc.jinja +16 -0
- generators/static/cursor/rules/presentation-layer.mdc.jinja +15 -0
- generators/static/cursor/rules/quality-gate.mdc.jinja +14 -0
- generators/static/cursor/rules/ui-caravaggio.mdc.jinja +22 -0
- generators/static/cursor/skills/caravaggio-ui/SKILL.md +138 -0
- generators/static/cursor/skills/epic-delivery/SKILL.md +74 -0
- generators/static/cursor/skills/epic-delivery/templates/epic-status.md +14 -0
- generators/static/cursor/skills/epic-delivery/templates/epic.md +36 -0
- generators/static/templates/apis/common/constants_template.jinja +48 -0
- generators/static/templates/apis/common/data_source_config_template.jinja +47 -0
- generators/static/templates/apis/core/api_injectable_module_template.jinja +14 -0
- generators/static/templates/apis/interceptors/api_logger_template.jinja +135 -0
- generators/static/templates/apis/interceptors/auth_interceptor_template.jinja +207 -0
- generators/static/templates/auth/application/auth_bloc_template.jinja +36 -0
- generators/static/templates/auth/application/auth_event_template.jinja +7 -0
- generators/static/templates/auth/application/auth_state_template.jinja +8 -0
- generators/static/templates/auth/infrastructure/auth_facade_module_template.jinja +16 -0
- generators/static/templates/auth/infrastructure/auth_facade_template.jinja +288 -0
- generators/static/templates/auth/infrastructure/mock_auth_facade_template.jinja +106 -0
- generators/static/templates/auth/model/auth_failure_template.jinja +11 -0
- generators/static/templates/auth/model/i_auth_facade_template.jinja +24 -0
- generators/static/templates/auth/model/user_template.jinja +34 -0
- generators/static/templates/auth/model/value_objects_template.jinja +54 -0
- generators/static/templates/auth/presentation/login_screen_template.jinja +24 -0
- generators/static/templates/auth/sign_in_form/application/sign_in_form_bloc_template.jinja +139 -0
- generators/static/templates/auth/sign_in_form/application/sign_in_form_event_template.jinja +15 -0
- generators/static/templates/auth/sign_in_form/application/sign_in_form_state_template.jinja +26 -0
- generators/static/templates/auth/sign_in_form/presentation/apple_form_template.jinja +16 -0
- generators/static/templates/auth/sign_in_form/presentation/email_password_form_template_INACTIVE.jinja +94 -0
- generators/static/templates/auth/sign_in_form/presentation/google_form_template.jinja +15 -0
- generators/static/templates/auth/sign_in_form/presentation/sign_in_form_template.jinja +381 -0
- generators/static/templates/component/component_bloc_template.jinja +49 -0
- generators/static/templates/component/component_event_template.jinja +7 -0
- generators/static/templates/component/component_form_bloc_template.jinja +56 -0
- generators/static/templates/component/component_form_event_template.jinja +7 -0
- generators/static/templates/component/component_form_state_template.jinja +18 -0
- generators/static/templates/component/component_form_widget_template.jinja +130 -0
- generators/static/templates/component/component_list_widget_template.jinja +95 -0
- generators/static/templates/component/component_state_template.jinja +9 -0
- generators/static/templates/component/component_widget_template.jinja +113 -0
- generators/static/templates/core/bloc/base_form_bloc_template.jinja +32 -0
- generators/static/templates/core/errors/error_localizer_template.jinja +61 -0
- generators/static/templates/core/infrastructure/base_mapper_template.jinja +21 -0
- generators/static/templates/core/infrastructure/base_repository_mixin_template.jinja +78 -0
- generators/static/templates/core/infrastructure/firebase_injectable_module_template.jinja +11 -0
- generators/static/templates/core/infrastructure/firestore_helpers_template.jinja +22 -0
- generators/static/templates/core/infrastructure/repository_error_handler_template.jinja +44 -0
- generators/static/templates/core/model/common_interfaces_template.jinja +3 -0
- generators/static/templates/core/model/entity_template.jinja +5 -0
- generators/static/templates/core/model/errors_template.jinja +15 -0
- generators/static/templates/core/model/failures_template.jinja +97 -0
- generators/static/templates/core/model/value_objects_template.jinja +135 -0
- generators/static/templates/core/model/value_validators_template.jinja +105 -0
- generators/static/templates/core/presentation/app_drawer_template.jinja +52 -0
- generators/static/templates/core/presentation/app_widget_template.jinja +41 -0
- generators/static/templates/core/presentation/bottom_nav_bar_template.jinja +32 -0
- generators/static/templates/domain/domain_mapper_template.jinja +39 -0
- generators/static/templates/domain/domain_remote_service_template.jinja +46 -0
- generators/static/templates/domain/domain_repository_template.jinja +90 -0
- generators/static/templates/domain/domain_service_module_template.jinja +23 -0
- generators/static/templates/domain/enum_template.jinja +3 -0
- generators/static/templates/domain/i_domain_service_template.jinja +18 -0
- generators/static/templates/domain/mock_domain_service_template.jinja +73 -0
- generators/static/templates/feature/feature_bloc_template.jinja +104 -0
- generators/static/templates/feature/feature_dto_template.jinja +26 -0
- generators/static/templates/feature/feature_entity_template.jinja +23 -0
- generators/static/templates/feature/feature_event_template.jinja +20 -0
- generators/static/templates/feature/feature_extensions_template.jinja +25 -0
- generators/static/templates/feature/feature_failure_template.jinja +11 -0
- generators/static/templates/feature/feature_page_template.jinja +113 -0
- generators/static/templates/feature/feature_repository_template.jinja +112 -0
- generators/static/templates/feature/feature_state_template.jinja +20 -0
- generators/static/templates/feature/i_feature_repository_template.jinja +21 -0
- generators/static/templates/feature/value_object_field_template.jinja +0 -0
- generators/static/templates/feature/value_object_template.jinja +0 -0
- generators/static/templates/feature/value_validators_template.jinja +57 -0
- generators/static/templates/home/home_screen_template.jinja +68 -0
- generators/static/templates/home/presentation/home_screen_template.jinja +52 -0
- generators/static/templates/home/screen_template.jinja +12 -0
- generators/static/templates/injection_template.jinja +8 -0
- generators/static/templates/logging/analytics_logging_template.jinja +42 -0
- generators/static/templates/logging/console_template.jinja +50 -0
- generators/static/templates/logging/logger_injectable_module_template.jinja +24 -0
- generators/static/templates/logging/logger_template.jinja +118 -0
- generators/static/templates/main_template.jinja +43 -0
- generators/static/templates/page_template.jinja +29 -0
- generators/static/templates/router_template.jinja +26 -0
- generators/static/templates/splash/presentation/splash_screen_auth_template.jinja +97 -0
- generators/static/templates/splash/presentation/splash_screen_template.jinja +51 -0
- generators/static/templates/storage/storage_repository_template.jinja +80 -0
- generators/static/templates/user_profile/infrastructure/user_profile_dto_template.jinja +27 -0
- generators/static/templates/user_profile/infrastructure/user_profile_mapper_template.jinja +43 -0
- generators/static/templates/user_profile/infrastructure/user_profile_repository_template.jinja +82 -0
- generators/static/templates/user_profile/infrastructure/user_profile_service_template.jinja +27 -0
- generators/static/templates/user_profile/model/i_user_profile_repository_template.jinja +34 -0
- generators/static/templates/user_profile/model/user_profile_failure_template.jinja +11 -0
- generators/static/templates/user_profile/model/user_profile_template.jinja +31 -0
- generators/static/templates/widgets/common/custom_scaffold_template.jinja +52 -0
- generators/static/templates/widgets/common/error_widget_template.jinja +22 -0
- generators/static/templates/widgets/common/loading_widget_template.jinja +14 -0
- generators/static/templates/widgets/common/unknown_state_widget_template.jinja +23 -0
- generators/templates/__init__.py +1 -0
- generators/templates/_core/core_generator.py +121 -0
- generators/templates/apis/apis_generator.py +50 -0
- generators/templates/auth/auth_generator.py +40 -0
- generators/templates/auth/sign_in_form_generator.py +22 -0
- generators/templates/copier.py +51 -0
- generators/templates/home/home_generator.py +26 -0
- generators/templates/lib/lib_generator.py +40 -0
- generators/templates/logging/logging_generator.py +28 -0
- generators/templates/main.py +75 -0
- generators/templates/splash/splash_generator.py +20 -0
- generators/templates/storage/storage_generator.py +5 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import 'package:flutter/material.dart';
|
|
2
|
+
|
|
3
|
+
class [[screen_name]]Screen extends StatelessWidget {
|
|
4
|
+
const [[screen_name]]Screen({super.key});
|
|
5
|
+
|
|
6
|
+
@override
|
|
7
|
+
Widget build(BuildContext context) {
|
|
8
|
+
return const Center(
|
|
9
|
+
child: Text('[[screen_title]] Page'),
|
|
10
|
+
);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import 'package:[[project_name]]/injection.config.dart';
|
|
2
|
+
import 'package:get_it/get_it.dart';
|
|
3
|
+
import 'package:injectable/injectable.dart';
|
|
4
|
+
|
|
5
|
+
final GetIt getIt = GetIt.instance;
|
|
6
|
+
|
|
7
|
+
@InjectableInit()
|
|
8
|
+
Future<void> configureDependencies(String env) async => getIt.init(environment: env);
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Analytics logging integration for production monitoring
|
|
3
|
+
* Implements LoggingIntegration for external analytics services:
|
|
4
|
+
* - Placeholder for analytics service integration
|
|
5
|
+
* - Different handling per log level
|
|
6
|
+
* - Ready for services like Firebase Analytics, Crashlytics, etc.
|
|
7
|
+
*
|
|
8
|
+
* Customize the switch cases to integrate with your
|
|
9
|
+
* preferred analytics and crash reporting services
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import 'package:[[project_name]]/logging/logger.dart';
|
|
13
|
+
|
|
14
|
+
class AnalyticsLoggingIntegration implements LoggingIntegration {
|
|
15
|
+
const AnalyticsLoggingIntegration();
|
|
16
|
+
|
|
17
|
+
{% raw %}
|
|
18
|
+
@override
|
|
19
|
+
void log(String message, LoggerLevel level, {dynamic ex, dynamic st}) async {
|
|
20
|
+
switch (level) {
|
|
21
|
+
case LoggerLevel.fatal:
|
|
22
|
+
// TODO: recordFatal($message, error: $ex, stack: $st);
|
|
23
|
+
break;
|
|
24
|
+
case LoggerLevel.error:
|
|
25
|
+
// TODO: recordError($message, error: $ex, stack: $st);
|
|
26
|
+
break;
|
|
27
|
+
case LoggerLevel.warning:
|
|
28
|
+
break;
|
|
29
|
+
case LoggerLevel.info:
|
|
30
|
+
break;
|
|
31
|
+
case LoggerLevel.verbose:
|
|
32
|
+
break;
|
|
33
|
+
case LoggerLevel.success:
|
|
34
|
+
break;
|
|
35
|
+
case LoggerLevel.apiReq:
|
|
36
|
+
break;
|
|
37
|
+
case LoggerLevel.apiRes:
|
|
38
|
+
break;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
{% endraw %}
|
|
42
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Console logging integration for development
|
|
3
|
+
* Implements LoggingIntegration for console output with:
|
|
4
|
+
* - Colored console output for different log levels
|
|
5
|
+
* - The [message] argument already includes prefix and body; for error/fatal the
|
|
6
|
+
* [Logger] appends exception and stack trace so one $message line is enough here.
|
|
7
|
+
*
|
|
8
|
+
* Perfect for development and debugging scenarios
|
|
9
|
+
* with color-coded messages for easy identification
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import 'dart:developer' as developer;
|
|
13
|
+
|
|
14
|
+
import 'package:[[project_name]]/logging/logger.dart';
|
|
15
|
+
|
|
16
|
+
class ConsoleLoggingIntegration implements LoggingIntegration {
|
|
17
|
+
const ConsoleLoggingIntegration();
|
|
18
|
+
|
|
19
|
+
{% raw %}
|
|
20
|
+
@override
|
|
21
|
+
void log(String message, LoggerLevel level, {dynamic ex, dynamic st}) {
|
|
22
|
+
switch (level) {
|
|
23
|
+
case LoggerLevel.fatal:
|
|
24
|
+
developer.log('\x1B[31m$message\x1B[0m');
|
|
25
|
+
break;
|
|
26
|
+
case LoggerLevel.error:
|
|
27
|
+
developer.log('\x1B[91m$message\x1B[0m');
|
|
28
|
+
break;
|
|
29
|
+
case LoggerLevel.warning:
|
|
30
|
+
developer.log('\x1B[33m$message\x1B[0m');
|
|
31
|
+
break;
|
|
32
|
+
case LoggerLevel.info:
|
|
33
|
+
developer.log('\x1B[37m$message\x1B[0m');
|
|
34
|
+
break;
|
|
35
|
+
case LoggerLevel.verbose:
|
|
36
|
+
developer.log('\x1B[34m$message\x1B[0m');
|
|
37
|
+
break;
|
|
38
|
+
case LoggerLevel.success:
|
|
39
|
+
developer.log('\x1B[32m$message\x1B[0m');
|
|
40
|
+
break;
|
|
41
|
+
case LoggerLevel.apiReq:
|
|
42
|
+
developer.log('\x1B[93m$message\x1B[0m');
|
|
43
|
+
break;
|
|
44
|
+
case LoggerLevel.apiRes:
|
|
45
|
+
developer.log('\x1B[32m$message\x1B[0m');
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
{% endraw %}
|
|
50
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Logger dependency injection module
|
|
3
|
+
* Registers logging integrations with Injectable:
|
|
4
|
+
* - Analytics logging integration
|
|
5
|
+
* - Console logging integration
|
|
6
|
+
* - Configurable list of logging backends
|
|
7
|
+
*
|
|
8
|
+
* This module makes logging integrations available
|
|
9
|
+
* throughout the app via dependency injection
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import 'package:injectable/injectable.dart';
|
|
13
|
+
import 'package:[[project_name]]/logging/analytics_logging.dart';
|
|
14
|
+
import 'package:[[project_name]]/logging/console.dart';
|
|
15
|
+
import 'package:[[project_name]]/logging/logger.dart';
|
|
16
|
+
|
|
17
|
+
@module
|
|
18
|
+
abstract class RegisterModule {
|
|
19
|
+
@lazySingleton
|
|
20
|
+
List<LoggingIntegration> get loggingIntegrations => <LoggingIntegration>[
|
|
21
|
+
const AnalyticsLoggingIntegration(),
|
|
22
|
+
const ConsoleLoggingIntegration(),
|
|
23
|
+
];
|
|
24
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Centralized logger template for application-wide logging
|
|
3
|
+
* Provides structured logging with:
|
|
4
|
+
* - Multiple log levels (verbose, info, warning, error, etc.)
|
|
5
|
+
* - Pluggable logging integrations (console, analytics, etc.)
|
|
6
|
+
* - Dependency injection support
|
|
7
|
+
* - Consistent logging interface across the app
|
|
8
|
+
*
|
|
9
|
+
* For error/fatal, the text passed to integrations includes $message plus
|
|
10
|
+
* optional lines for $ex and $st when provided.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import 'package:injectable/injectable.dart';
|
|
14
|
+
|
|
15
|
+
enum LoggerLevel {
|
|
16
|
+
verbose,
|
|
17
|
+
info,
|
|
18
|
+
warning,
|
|
19
|
+
error,
|
|
20
|
+
fatal,
|
|
21
|
+
success,
|
|
22
|
+
apiReq,
|
|
23
|
+
apiRes,
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
abstract class LoggingIntegration {
|
|
27
|
+
void log(String message, LoggerLevel level, {dynamic ex, dynamic st});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
@injectable
|
|
31
|
+
class Logger {
|
|
32
|
+
Logger(this._integrations);
|
|
33
|
+
|
|
34
|
+
final List<LoggingIntegration> _integrations;
|
|
35
|
+
|
|
36
|
+
void logVerbose(String message) {
|
|
37
|
+
_log(message, LoggerLevel.verbose);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
void logInfo(String message) {
|
|
41
|
+
_log(message, LoggerLevel.info);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
void logWarning(String message) {
|
|
45
|
+
_log(message, LoggerLevel.warning);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
void logSuccess(String message) {
|
|
49
|
+
_log(message, LoggerLevel.success);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
void logError(String message, {dynamic ex, dynamic st}) {
|
|
53
|
+
_log(message, LoggerLevel.error, ex: ex, st: st);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
void logFatal(String message, {dynamic ex, dynamic st}) {
|
|
57
|
+
_log(message, LoggerLevel.fatal, ex: ex, st: st);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
void logApiReq(String message) {
|
|
61
|
+
_log(message, LoggerLevel.apiReq);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
void logApiRes(String message) {
|
|
65
|
+
_log(message, LoggerLevel.apiRes);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
{% raw %}
|
|
69
|
+
void _log(String message, LoggerLevel level, {dynamic ex, dynamic st}) {
|
|
70
|
+
final StringBuffer buffer = StringBuffer();
|
|
71
|
+
final String prefix = _getLogPrefix(level);
|
|
72
|
+
if (prefix.isNotEmpty) {
|
|
73
|
+
buffer.write(prefix);
|
|
74
|
+
buffer.write(' ');
|
|
75
|
+
}
|
|
76
|
+
buffer.write(message);
|
|
77
|
+
|
|
78
|
+
if (level == LoggerLevel.error || level == LoggerLevel.fatal) {
|
|
79
|
+
if (ex != null) {
|
|
80
|
+
buffer.writeln();
|
|
81
|
+
buffer.write('Exception: $ex');
|
|
82
|
+
}
|
|
83
|
+
if (st != null) {
|
|
84
|
+
buffer.writeln();
|
|
85
|
+
buffer.write('StackTrace: $st');
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
final String finalMessage = buffer.toString();
|
|
90
|
+
|
|
91
|
+
for (final LoggingIntegration integration in _integrations) {
|
|
92
|
+
integration.log(finalMessage, level, ex: ex, st: st);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
{% endraw %}
|
|
96
|
+
|
|
97
|
+
// Returns the log prefix for the given LoggingLevel
|
|
98
|
+
String _getLogPrefix(LoggerLevel level) {
|
|
99
|
+
switch (level) {
|
|
100
|
+
case LoggerLevel.verbose:
|
|
101
|
+
return "[Verbose]";
|
|
102
|
+
case LoggerLevel.info:
|
|
103
|
+
return "[Info]";
|
|
104
|
+
case LoggerLevel.warning:
|
|
105
|
+
return "[Warning]";
|
|
106
|
+
case LoggerLevel.error:
|
|
107
|
+
return "[Error]";
|
|
108
|
+
case LoggerLevel.fatal:
|
|
109
|
+
return "[Fatal error]";
|
|
110
|
+
case LoggerLevel.success:
|
|
111
|
+
return "[Done]";
|
|
112
|
+
case LoggerLevel.apiReq:
|
|
113
|
+
return "[API Request]";
|
|
114
|
+
case LoggerLevel.apiRes:
|
|
115
|
+
return "[API Response]";
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import 'package:caravaggio_ui/caravaggio_ui.dart';
|
|
2
|
+
import 'package:flutter/material.dart';
|
|
3
|
+
import 'package:injectable/injectable.dart';
|
|
4
|
+
import 'package:intl/date_symbol_data_local.dart';
|
|
5
|
+
import 'package:[[project_name]]/apis/common/constants.dart';
|
|
6
|
+
import 'package:[[project_name]]/apis/common/data_source_config.dart';
|
|
7
|
+
import 'package:[[project_name]]/injection.dart';
|
|
8
|
+
import 'package:[[project_name]]/core/presentation/app_widget.dart';
|
|
9
|
+
|
|
10
|
+
Future<void> main() async {
|
|
11
|
+
CaravaggioUI.initialize(
|
|
12
|
+
primaryColor: const [[primary_color]], // From coregen.yaml styling.primary_color
|
|
13
|
+
secondaryColor: const [[secondary_color]] // From coregen.yaml styling.secondary_color
|
|
14
|
+
// fontFamily: 'Noto Sans', // Uncomment and set your desired font family
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
WidgetsFlutterBinding.ensureInitialized();
|
|
18
|
+
await initializeDateFormatting('en_US');
|
|
19
|
+
|
|
20
|
+
initEnvironment();
|
|
21
|
+
validateDataSources();
|
|
22
|
+
|
|
23
|
+
await configureDependencies(Environment.dev);
|
|
24
|
+
|
|
25
|
+
runApp(const AppWidget());
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
void initEnvironment() {
|
|
29
|
+
// Mock-only default: leave [Constants.apiUrl] empty — no backend required.
|
|
30
|
+
// Set a real URL only when switching entities to DataSource.remote in
|
|
31
|
+
// lib/apis/common/data_source_config.dart.
|
|
32
|
+
Constants.apiUrl = '';
|
|
33
|
+
|
|
34
|
+
// Optional per-environment overrides (uncomment when API is ready):
|
|
35
|
+
// const String environment = String.fromEnvironment('ENV', defaultValue: 'local');
|
|
36
|
+
// if (environment.toLowerCase() == 'production') {
|
|
37
|
+
// Constants.apiUrl = 'https://api.example.com';
|
|
38
|
+
// }
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
void validateDataSources() {
|
|
42
|
+
DataSourceConfig.validateRemoteRequiresApiUrl();
|
|
43
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Simple page template for Flutter screens
|
|
3
|
+
* Generates a basic StatelessWidget page with:
|
|
4
|
+
* - CustomScaffold with page title
|
|
5
|
+
* - Centered content placeholder
|
|
6
|
+
* - Route name constant for navigation
|
|
7
|
+
* Use this template for creating simple, static pages
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import 'package:caravaggio_ui/caravaggio_ui.dart';
|
|
11
|
+
import 'package:flutter/material.dart';
|
|
12
|
+
import 'package:[[project_name]]/widgets/common/custom_scaffold.dart';
|
|
13
|
+
|
|
14
|
+
class [[page_name|title]]Page extends StatelessWidget {
|
|
15
|
+
static const String routeName = '/[[page_name]]';
|
|
16
|
+
|
|
17
|
+
const [[page_name|title]]Page({super.key});
|
|
18
|
+
|
|
19
|
+
@override
|
|
20
|
+
Widget build(BuildContext context) {
|
|
21
|
+
return CustomScaffold(
|
|
22
|
+
title: CText.title('[[page_name|replace("_", " ")|title]]', size: TextSize.small),
|
|
23
|
+
showBackButton: true,
|
|
24
|
+
body: Center(
|
|
25
|
+
child: CText.body('[[page_name|replace("_", " ")|title]] Page'),
|
|
26
|
+
),
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{% if has_login %}
|
|
2
|
+
import 'package:[[project_name]]/features/auth/presentation/login_page.dart';
|
|
3
|
+
{% endif %}
|
|
4
|
+
import 'package:[[project_name]]/features/home/home_page.dart';
|
|
5
|
+
import 'package:flutter/material.dart';
|
|
6
|
+
import 'package:[[project_name]]/features/splash/splash_page.dart';
|
|
7
|
+
import 'package:go_router/go_router.dart';
|
|
8
|
+
|
|
9
|
+
final GoRouter router = GoRouter(
|
|
10
|
+
routes: <RouteBase>[
|
|
11
|
+
{% if has_login %}
|
|
12
|
+
GoRoute(
|
|
13
|
+
path: LoginPage.routeName,
|
|
14
|
+
builder: (BuildContext context, GoRouterState state) => const LoginPage(),
|
|
15
|
+
),
|
|
16
|
+
{% endif %}
|
|
17
|
+
GoRoute(
|
|
18
|
+
path: HomePage.routeName,
|
|
19
|
+
builder: (BuildContext context, GoRouterState state) => const HomePage(),
|
|
20
|
+
),
|
|
21
|
+
GoRoute(
|
|
22
|
+
path: SplashPage.routeName,
|
|
23
|
+
builder: (BuildContext context, GoRouterState state) => const SplashPage(),
|
|
24
|
+
),
|
|
25
|
+
],
|
|
26
|
+
);
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import 'package:flutter/material.dart';
|
|
2
|
+
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
3
|
+
import 'package:[[project_name]]/features/auth/application/auth_bloc.dart';
|
|
4
|
+
import 'package:[[project_name]]/features/auth/presentation/login_page.dart';
|
|
5
|
+
import 'package:[[project_name]]/features/home/home_page.dart';
|
|
6
|
+
import 'package:[[project_name]]/widgets/common/custom_scaffold.dart';
|
|
7
|
+
import 'package:go_router/go_router.dart';
|
|
8
|
+
|
|
9
|
+
class SplashPage extends StatefulWidget {
|
|
10
|
+
static const String routeName = '/';
|
|
11
|
+
|
|
12
|
+
const SplashPage({super.key});
|
|
13
|
+
|
|
14
|
+
@override
|
|
15
|
+
State<SplashPage> createState() => _SplashPageState();
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
class _SplashPageState extends State<SplashPage> {
|
|
19
|
+
static const Duration _splashMinDuration = Duration(seconds: 1);
|
|
20
|
+
|
|
21
|
+
bool _minDelayElapsed = false;
|
|
22
|
+
bool _hasNavigated = false;
|
|
23
|
+
AuthState? _resolvedAuthState;
|
|
24
|
+
int _animation = 0;
|
|
25
|
+
|
|
26
|
+
@override
|
|
27
|
+
void initState() {
|
|
28
|
+
super.initState();
|
|
29
|
+
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
30
|
+
setState(() {
|
|
31
|
+
_animation = 1;
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
Future<void>.delayed(_splashMinDuration, () {
|
|
35
|
+
if (!mounted) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
setState(() {
|
|
39
|
+
_minDelayElapsed = true;
|
|
40
|
+
});
|
|
41
|
+
_tryNavigate();
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
void _onAuthStateChanged(AuthState state) {
|
|
46
|
+
if (_hasNavigated) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
if (state is Authenticated || state is Unauthenticated) {
|
|
50
|
+
_resolvedAuthState = state;
|
|
51
|
+
_tryNavigate();
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
void _tryNavigate() {
|
|
56
|
+
if (_hasNavigated || _resolvedAuthState == null) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
final AuthState state = _resolvedAuthState!;
|
|
61
|
+
|
|
62
|
+
if (state is Authenticated) {
|
|
63
|
+
_hasNavigated = true;
|
|
64
|
+
context.replace(HomePage.routeName);
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (state is Unauthenticated && _minDelayElapsed) {
|
|
69
|
+
_hasNavigated = true;
|
|
70
|
+
context.pushReplacement(LoginPage.routeName);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
@override
|
|
75
|
+
Widget build(BuildContext context) {
|
|
76
|
+
return BlocListener<AuthBloc, AuthState>(
|
|
77
|
+
listener: (BuildContext context, AuthState state) => _onAuthStateChanged(state),
|
|
78
|
+
child: CustomScaffold(
|
|
79
|
+
showBackButton: false,
|
|
80
|
+
body: Center(
|
|
81
|
+
child: Hero(
|
|
82
|
+
tag: LoginPage.logoHeroTag,
|
|
83
|
+
child: Material(
|
|
84
|
+
type: MaterialType.transparency,
|
|
85
|
+
child: AnimatedContainer(
|
|
86
|
+
duration: const Duration(seconds: 1),
|
|
87
|
+
curve: Curves.easeInOut,
|
|
88
|
+
width: _animation == 1 ? 200 : 100,
|
|
89
|
+
child: Image.asset('assets/logo.png'),
|
|
90
|
+
),
|
|
91
|
+
),
|
|
92
|
+
),
|
|
93
|
+
),
|
|
94
|
+
),
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Splash screen template for app initialization
|
|
3
|
+
* Creates an initial loading screen that:
|
|
4
|
+
* - Shows app logo or branding
|
|
5
|
+
* - Performs initialization tasks
|
|
6
|
+
* - Automatically navigates to home screen after delay
|
|
7
|
+
* - Provides smooth app startup experience
|
|
8
|
+
*
|
|
9
|
+
* Perfect for app startup, loading assets, or initialization processes
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import 'package:flutter/material.dart';
|
|
13
|
+
import 'package:go_router/go_router.dart';
|
|
14
|
+
import 'package:[[project_name]]/features/home/home_page.dart';
|
|
15
|
+
import 'package:[[project_name]]/widgets/common/custom_scaffold.dart';
|
|
16
|
+
|
|
17
|
+
class SplashPage extends StatefulWidget {
|
|
18
|
+
static const String routeName = '/';
|
|
19
|
+
|
|
20
|
+
const SplashPage({super.key});
|
|
21
|
+
|
|
22
|
+
@override
|
|
23
|
+
State<SplashPage> createState() => _SplashPageState();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
class _SplashPageState extends State<SplashPage> {
|
|
27
|
+
@override
|
|
28
|
+
void initState() {
|
|
29
|
+
super.initState();
|
|
30
|
+
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
31
|
+
// After 500 milliseconds, navigate to the next screen
|
|
32
|
+
Future<void>.delayed(const Duration(milliseconds: 500), () {
|
|
33
|
+
// Navigate to the next screen or perform any action here
|
|
34
|
+
context.replace(HomePage.routeName);
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
@override
|
|
40
|
+
Widget build(BuildContext context) {
|
|
41
|
+
return CustomScaffold(
|
|
42
|
+
showBackButton: false,
|
|
43
|
+
body: Center(
|
|
44
|
+
child: SizedBox(
|
|
45
|
+
width: 200,
|
|
46
|
+
child: Image.asset('assets/logo.png'),
|
|
47
|
+
),
|
|
48
|
+
),
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import 'package:injectable/injectable.dart';
|
|
2
|
+
import 'package:shared_preferences/shared_preferences.dart';
|
|
3
|
+
|
|
4
|
+
const String tokenDataKey = 'token_key';
|
|
5
|
+
const String refreshTokenDataKey = 'refresh_token_key';
|
|
6
|
+
const String guestTokenKey = 'guest_token_key';
|
|
7
|
+
const String isGuestModeKey = 'is_guest_mode_key';
|
|
8
|
+
|
|
9
|
+
/// Access + optional refresh token bundle (used by [AuthInterceptor] after refresh).
|
|
10
|
+
class TokenData {
|
|
11
|
+
const TokenData({required this.token, this.refreshToken = ''});
|
|
12
|
+
|
|
13
|
+
final String token;
|
|
14
|
+
final String refreshToken;
|
|
15
|
+
|
|
16
|
+
bool get hasRefresh => refreshToken.isNotEmpty;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
@Singleton()
|
|
20
|
+
class StorageRepository {
|
|
21
|
+
Future<bool> hasToken() async {
|
|
22
|
+
final SharedPreferences prefs = await SharedPreferences.getInstance();
|
|
23
|
+
final String? token = prefs.getString(tokenDataKey);
|
|
24
|
+
return token != null && token.isNotEmpty;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/// Persists access token and optional refresh token (SharedPreferences — consider flutter_secure_storage for production).
|
|
28
|
+
Future<void> saveToken(TokenData data) async {
|
|
29
|
+
final SharedPreferences prefs = await SharedPreferences.getInstance();
|
|
30
|
+
await prefs.setString(tokenDataKey, data.token);
|
|
31
|
+
await prefs.setString(refreshTokenDataKey, data.refreshToken);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
Future<String?> getToken() async {
|
|
35
|
+
final SharedPreferences prefs = await SharedPreferences.getInstance();
|
|
36
|
+
return prefs.getString(tokenDataKey);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
Future<String?> getRefreshToken() async {
|
|
40
|
+
final SharedPreferences prefs = await SharedPreferences.getInstance();
|
|
41
|
+
return prefs.getString(refreshTokenDataKey);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
Future<void> removeToken() async {
|
|
45
|
+
final SharedPreferences prefs = await SharedPreferences.getInstance();
|
|
46
|
+
await prefs.remove(tokenDataKey);
|
|
47
|
+
await prefs.remove(refreshTokenDataKey);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
Future<void> saveGuestToken(String guestToken) async {
|
|
51
|
+
final SharedPreferences prefs = await SharedPreferences.getInstance();
|
|
52
|
+
await prefs.setString(guestTokenKey, guestToken);
|
|
53
|
+
await prefs.setBool(isGuestModeKey, true);
|
|
54
|
+
await prefs.remove(tokenDataKey);
|
|
55
|
+
await prefs.remove(refreshTokenDataKey);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
Future<String?> getGuestToken() async {
|
|
59
|
+
final SharedPreferences prefs = await SharedPreferences.getInstance();
|
|
60
|
+
return prefs.getString(guestTokenKey);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
Future<bool> isGuestMode() async {
|
|
64
|
+
final SharedPreferences prefs = await SharedPreferences.getInstance();
|
|
65
|
+
return prefs.getBool(isGuestModeKey) ?? false;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
Future<void> clearGuestToken() async {
|
|
69
|
+
final SharedPreferences prefs = await SharedPreferences.getInstance();
|
|
70
|
+
await prefs.remove(guestTokenKey);
|
|
71
|
+
await prefs.remove(isGuestModeKey);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
Future<void> clearAll() async {
|
|
75
|
+
await removeToken();
|
|
76
|
+
await clearGuestToken();
|
|
77
|
+
final SharedPreferences prefs = await SharedPreferences.getInstance();
|
|
78
|
+
await prefs.clear();
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* UserProfile DTO (Data Transfer Object)
|
|
3
|
+
* Used for API communication when syncing UserProfile with backend
|
|
4
|
+
*
|
|
5
|
+
* DTOs represent the data structure expected by the API
|
|
6
|
+
* and are mapped to/from domain entities via UserProfileMapper
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import 'package:freezed_annotation/freezed_annotation.dart';
|
|
10
|
+
|
|
11
|
+
part 'user_profile_dto.freezed.dart';
|
|
12
|
+
part 'user_profile_dto.g.dart';
|
|
13
|
+
|
|
14
|
+
@freezed
|
|
15
|
+
abstract class UserProfileDto with _$UserProfileDto {
|
|
16
|
+
const factory UserProfileDto({
|
|
17
|
+
required String id,
|
|
18
|
+
required String name,
|
|
19
|
+
required String emailAddress,
|
|
20
|
+
@JsonKey(name: 'avatar_url') String? avatarUrl,
|
|
21
|
+
@JsonKey(name: 'created_at') required String createdAt,
|
|
22
|
+
@JsonKey(name: 'updated_at') required String updatedAt,
|
|
23
|
+
}) = _UserProfileDto;
|
|
24
|
+
|
|
25
|
+
factory UserProfileDto.fromJson(Map<String, dynamic> json) =>
|
|
26
|
+
_$UserProfileDtoFromJson(json);
|
|
27
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* UserProfile mapper
|
|
3
|
+
* Maps between UserProfile DTO (API layer) and UserProfile Domain Entity
|
|
4
|
+
*
|
|
5
|
+
* Extends BaseMapper which provides default toDomainList/toDtoList implementations.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import 'package:dartz/dartz.dart';
|
|
9
|
+
import 'package:injectable/injectable.dart';
|
|
10
|
+
import 'package:[[project_name]]/core/infrastructure/base_mapper.dart';
|
|
11
|
+
import 'package:[[project_name]]/core/model/value_objects.dart';
|
|
12
|
+
import 'package:[[project_name]]/domain/auth/model/value_objects.dart';
|
|
13
|
+
import 'package:[[project_name]]/domain/user_profile/infrastructure/user_profile_dto.dart';
|
|
14
|
+
import 'package:[[project_name]]/domain/user_profile/model/user_profile.dart';
|
|
15
|
+
|
|
16
|
+
@lazySingleton
|
|
17
|
+
class UserProfileMapper extends BaseMapper<UserProfile, UserProfileDto> {
|
|
18
|
+
UserProfileMapper();
|
|
19
|
+
|
|
20
|
+
@override
|
|
21
|
+
UserProfile toDomain(UserProfileDto dto) {
|
|
22
|
+
return UserProfile(
|
|
23
|
+
id: UniqueId.fromUniqueString(dto.id),
|
|
24
|
+
name: StringSingleLine(dto.name),
|
|
25
|
+
emailAddress: EmailAddress(dto.emailAddress),
|
|
26
|
+
avatarUrl: dto.avatarUrl != null ? some(dto.avatarUrl!) : none(),
|
|
27
|
+
createdAt: DateTime.parse(dto.createdAt),
|
|
28
|
+
updatedAt: DateTime.parse(dto.updatedAt),
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
@override
|
|
33
|
+
UserProfileDto toDto(UserProfile entity) {
|
|
34
|
+
return UserProfileDto(
|
|
35
|
+
id: entity.id.getOrCrash(),
|
|
36
|
+
name: entity.name.getOrCrash(),
|
|
37
|
+
emailAddress: entity.emailAddress.getOrCrash(),
|
|
38
|
+
avatarUrl: entity.avatarUrl.fold(() => null, (String url) => url),
|
|
39
|
+
createdAt: entity.createdAt.toIso8601String(),
|
|
40
|
+
updatedAt: entity.updatedAt.toIso8601String(),
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
}
|