From b06da4002470a19befd03b209b92c4b98ed429ca Mon Sep 17 00:00:00 2001 From: Jd Date: Tue, 12 Nov 2024 19:52:12 -0500 Subject: [PATCH] init: 26 - prompts --- instructions/context-project-structure.md | 80 ++++++++ instructions/new-feature.md | 172 ++++++++++++++++++ instructions/util-performance.md | 211 ++++++++++++++++++++++ 3 files changed, 463 insertions(+) create mode 100644 instructions/context-project-structure.md create mode 100644 instructions/new-feature.md create mode 100644 instructions/util-performance.md diff --git a/instructions/context-project-structure.md b/instructions/context-project-structure.md new file mode 100644 index 0000000..2daea0f --- /dev/null +++ b/instructions/context-project-structure.md @@ -0,0 +1,80 @@ +``` +tree -d -I 'node_modules|bin|build|android|ios' | pbcopy +``` + +. +├── art +│   └── undraw +│   └── png +├── assets +│   ├── audio +│   │   ├── music +│   │   └── sfx +│   └── images +│   ├── app +│   ├── platform +│   └── undraw +├── instructions +├── lib +│   ├── app +│   │   ├── extensions +│   │   ├── models +│   │   ├── modules +│   │   │   ├── diary +│   │   │   │   ├── bindings +│   │   │   │   ├── controllers +│   │   │   │   ├── views +│   │   │   │   │   ├── activity +│   │   │   │   │   │   └── widgets +│   │   │   │   │   ├── food +│   │   │   │   │   │   └── scanner +│   │   │   │   │   ├── water +│   │   │   │   │   └── weight +│   │   │   │   └── widgets +│   │   │   ├── home +│   │   │   │   ├── bindings +│   │   │   │   ├── controllers +│   │   │   │   └── views +│   │   │   ├── intro +│   │   │   │   ├── bindings +│   │   │   │   ├── controllers +│   │   │   │   └── views +│   │   │   │   └── widgets +│   │   │   ├── login +│   │   │   │   ├── bindings +│   │   │   │   ├── controllers +│   │   │   │   └── views +│   │   │   │   └── widgets +│   │   │   ├── profile +│   │   │   │   ├── bindings +│   │   │   │   ├── controllers +│   │   │   │   └── views +│   │   │   ├── track +│   │   │   │   ├── bindings +│   │   │   │   ├── controllers +│   │   │   │   └── views +│   │   │   ├── update +│   │   │   │   ├── bindings +│   │   │   │   ├── controllers +│   │   │   │   └── views +│   │   │   └── zone +│   │   │   ├── bindings +│   │   │   ├── controllers +│   │   │   └── views +│   │   ├── routes +│   │   ├── services +│   │   │   └── persistence +│   │   ├── style +│   │   ├── utils +│   │   └── widgets +│   │   └── skinner +│   │   ├── fluid_nav_bar +│   │   ├── math +│   │   ├── physics +│   │   └── ui +│   │   └── placeholder +│   └── gen +└── test +└── helper + +74 directories diff --git a/instructions/new-feature.md b/instructions/new-feature.md new file mode 100644 index 0000000..b268951 --- /dev/null +++ b/instructions/new-feature.md @@ -0,0 +1,172 @@ +# Feature Development Guide + +## Initial Setup + +- Create feature branch: `feature/[name]` +- Verify Firebase config in firebase.json +- Follow existing patterns for consistency + +## Backend Development + +### Firestore Service + +Add new methods to firebase_service.dart following pattern: + +``` +Future saveData(String id, Map data) async { + try { + await _db.collection('collection').doc(id).set(data); + logger.i('Data saved successfully'); + } catch (e) { + logger.e('Error saving data: $e'); + rethrow; + } +} +``` + +### Models + +Create models in lib/app/models/ with: + +- Required fields as final +- Factory constructors for JSON +- Clean serialization methods + +``` + class MyModel { + final String id; + final String name; + + MyModel({required this.id, required this.name}); + + factory MyModel.fromJson(Map json) { + return MyModel( + id: json['id'] as String, + name: json['name'] as String, + ); + } + + Map toJson() { + return { + 'id': id, + 'name': name, + }; + } + } +``` + +## Frontend Development + +### Controllers + +Use GetX controllers with: + +- Observable (.obs) variables +- Clean error handling +- Service injection + +``` + class MyController extends GetxController { + final service = Get.find(); + final data = [].obs; + + Future loadData() async { + try { + data.value = await service.getData(); + } catch (e) { + logger.e('Error: $e'); + } + } + } +``` + +### Views + +Create views with: + +- GetView base class +- Responsive layouts +- Proper state management + +``` + class MyView extends GetView { + @override + Widget build(BuildContext context) { + return Scaffold( + body: SafeArea( + child: Obx(() { + if (controller.data.isEmpty) { + return LoadingWidget(); + } else { + return ListView.builder( + itemCount: controller.data.length, + itemBuilder: (context, index) { + final item = controller.data[index]; + return ListTile( + title: Text(item.name), + ); + }, + ); + } + }), + ), + ); + } + } +``` + +### UI Components + +Build reusable widgets: + +- Extract common UI patterns +- Use composition over inheritance +- Follow material design + +``` +class MyWidget extends StatelessWidget { + final String title; + final VoidCallback onTap; + + Widget build(BuildContext context) { + return Card( + child: ListTile( + title: Text(title), + onTap: onTap, + ), + ); + } +} +``` + +## Testing + +Write tests for: + +- Models +- Controllers +- Services +- Widget behavior + +``` +test('should load data', () async { + final controller = MyController(); + await controller.loadData(); + expect(controller.data.length, greaterThan(0)); +}); +``` + +## Key Directories + +- lib/app/modules/[feature]/views/ - UI components +- lib/app/modules/[feature]/controllers/ - Business logic +- lib/app/services/ - External services +- lib/app/models/ - Data models +- test/ - Test files + +## Build & Deploy + +- Use make mobile for builds +- Use make test for testing +- Monitor Firebase Crashlytics +- Keep documentation updated diff --git a/instructions/util-performance.md b/instructions/util-performance.md new file mode 100644 index 0000000..6515e40 --- /dev/null +++ b/instructions/util-performance.md @@ -0,0 +1,211 @@ +# Code Quality & Performance Guide + +## Maintainability + +### Clean Code Structure + +Keep functions small and focused: + +``` +void handleSubmit() async { + if (!validateInput()) return; + await saveData(); + showSuccess(); +} +``` + +### Consistent Naming + +Use clear, descriptive names: + +- Controllers: FoodController, not FCtrl +- Services: AuthenticationService, not AuthSvc +- Models: UserProfile, not UP +- Methods: fetchUserData(), not getData() + +### Error Handling + +Implement proper error boundaries: + +``` +try { + await healthService.saveData(); +} catch (e) { + logger.e('Failed to save health data', e); + NotificationService.to.showError('Save failed'); +} +``` + +## Reusability + +### Widget Composition + +Break down complex widgets: + +``` +class CustomCard extends StatelessWidget { + final String title; + final Widget child; + + CustomCard({required this.title, required this.child}); + + @override + Widget build(BuildContext context) { + return Card( + child: Column( + children: [ + Text(title), + child, + ], + ), + ); + } +} +``` + +### Service Abstraction + +Create reusable service methods: + +``` +class HealthService { + Future withErrorHandling(Future Function() action) async { + try { + return await action(); + } catch (e, stackTrace) { + logger.e('Health service error', e, stackTrace); + rethrow; + } + } +} +``` + +### Extension Methods + +Add functionality without inheritance: + +``` +extension ContextExt on BuildContext { + double get width => MediaQuery.of(this).size.width; + double get height => MediaQuery.of(this).size.height; + TextTheme get textTheme => Theme.of(this).textTheme; +} +``` + +## Testability + +### Dependency Injection + +Use GetX for service injection: + +``` +class MyController extends GetxController { + final service = Get.find(); + final cache = Get.find(); +} +``` + +### Mockable Interfaces + +Create testable abstractions: + +``` +abstract class IHealthService { + Future saveData(HealthData data); +} + +class HealthService implements IHealthService { + @override + Future saveData(HealthData data) async { + // Implementation + } +} +``` + +### Testable State + +Keep state observable and isolated: + +``` +class SearchController extends GetxController { + final results = [].obs; + final isLoading = false.obs; + + Future search(String query) async { + isLoading.value = true; + try { + results.value = await performSearch(query); + } finally { + isLoading.value = false; + } + } +} +``` + +## Explainability + +### Code Documentation + +Document complex logic: + +``` +/// Calculates the user's zone points based on heart rate data +/// [heartRate] - Current heart rate in BPM +/// [maxHR] - User's maximum heart rate +/// Returns zone points (0-2) based on intensity +int calculateZonePoints(int heartRate, int maxHR) { + final percentage = heartRate / maxHR; + if (percentage >= 0.9) { + return 2; // High intensity + } else if (percentage >= 0.7) { + return 1; // Moderate intensity + } else { + return 0; // Low intensity + } +} +``` + +### Logging Strategy + +Use structured logging: + +``` +logger.i('Saving health data', { + 'type': data.type, + 'value': data.value, + 'timestamp': data.timestamp, +}); +``` + +### Clear Architecture + +Follow consistent patterns: +lib/app/ +├── modules/ # Feature modules +│ └── feature/ +│ ├── controllers/ +│ ├── views/ +│ └── widgets/ +├── services/ # Global services +├── models/ # Data models +└── utils/ # Utilities + +## Performance Tips + +### State Management + +- Use .obs only for UI-bound state +- Avoid unnecessary rebuilds +- Use GetX workers for side effects + +### Memory Management + +- Dispose controllers and streams +- Cache heavy computations +- Use const constructors where possible + +### UI Performance + +- Use ListView.builder for long lists +- Implement pagination for large datasets +- Keep widget tree depth reasonable