docs(02): create phase 2 household creation plans
Phase 2: Household Creation & Invites - 6 plans in 4 waves covering SHARE-01 through SHARE-05 - Data models, repository pattern, and Supabase integration - Database schema with RLS policies for multi-tenant isolation - State management with Riverpod and business logic use cases - Complete UI components for household management - Navigation integration with authentication flow Ready for execution: /gsd:execute-phase 2
This commit is contained in:
367
.planning/phases/02-household-creation/02-03-PLAN.md
Normal file
367
.planning/phases/02-household-creation/02-03-PLAN.md
Normal file
@@ -0,0 +1,367 @@
|
||||
---
|
||||
phase: 02-household-creation
|
||||
plan: 03
|
||||
type: execute
|
||||
wave: 2
|
||||
depends_on: [02-01]
|
||||
files_modified:
|
||||
- lib/features/household/providers/household_provider.dart
|
||||
- lib/features/household/domain/usecases/create_household_usecase.dart
|
||||
- lib/features/household/domain/usecases/generate_invite_code_usecase.dart
|
||||
- lib/features/household/domain/usecases/join_household_usecase.dart
|
||||
- lib/features/household/domain/usecases/get_user_households_usecase.dart
|
||||
autonomous: true
|
||||
|
||||
must_haves:
|
||||
truths:
|
||||
- "Household state management provides reactive updates across the app"
|
||||
- "Use cases encapsulate business logic for household operations"
|
||||
- "Provider integrates with existing authentication state"
|
||||
- "Household operations handle loading states and errors properly"
|
||||
artifacts:
|
||||
- path: "lib/features/household/providers/household_provider.dart"
|
||||
provides: "Global household state management"
|
||||
contains: "class HouseholdProvider", "Riverpod", "AsyncValue"
|
||||
- path: "lib/features/household/domain/usecases/create_household_usecase.dart"
|
||||
provides: "Household creation business logic"
|
||||
contains: "class CreateHouseholdUseCase", "Future<HouseholdEntity>"
|
||||
- path: "lib/features/household/domain/usecases/join_household_usecase.dart"
|
||||
provides: "Household joining logic with validation"
|
||||
contains: "class JoinHouseholdUseCase", "invite code validation"
|
||||
- path: "lib/features/household/domain/usecases/get_user_households_usecase.dart"
|
||||
provides: "User household retrieval logic"
|
||||
contains: "class GetUserHouseholdsUseCase", "List<HouseholdEntity>"
|
||||
key_links:
|
||||
- from: "lib/features/household/providers/household_provider.dart"
|
||||
to: "lib/features/household/domain/usecases/create_household_usecase.dart"
|
||||
via: "dependency injection"
|
||||
pattern: "CreateHouseholdUseCase.*createHouseholdUseCase"
|
||||
- from: "lib/features/household/providers/household_provider.dart"
|
||||
to: "lib/providers/auth_provider.dart"
|
||||
via: "authentication state"
|
||||
pattern: "ref.watch.*authProvider"
|
||||
---
|
||||
|
||||
<objective>
|
||||
Implement household state management and business logic use cases following clean architecture patterns.
|
||||
|
||||
Purpose: Provide reactive household state that integrates with authentication and handles all household operations with proper error handling.
|
||||
Output: Complete state management layer with use cases and Riverpod provider ready for UI integration.
|
||||
</objective>
|
||||
|
||||
<execution_context>
|
||||
@~/.opencode/get-shit-done/workflows/execute-plan.md
|
||||
@~/.opencode/get-shit-done/templates/summary.md
|
||||
</execution_context>
|
||||
|
||||
<context>
|
||||
@.planning/PROJECT.md
|
||||
@.planning/ROADMAP.md
|
||||
@.planning/STATE.md
|
||||
|
||||
# State Management References
|
||||
@lib/providers/auth_provider.dart
|
||||
@lib/features/authentication/data/repositories/auth_repository_impl.dart
|
||||
</context>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto">
|
||||
<name>Create Household Use Cases</name>
|
||||
<files>
|
||||
lib/features/household/domain/usecases/create_household_usecase.dart
|
||||
lib/features/household/domain/usecases/generate_invite_code_usecase.dart
|
||||
lib/features/household/domain/usecases/join_household_usecase.dart
|
||||
lib/features/household/domain/usecases/get_user_households_usecase.dart
|
||||
</files>
|
||||
<action>
|
||||
Create use cases for household operations following clean architecture:
|
||||
|
||||
1. CreateHouseholdUseCase (create_household_usecase.dart):
|
||||
```dart
|
||||
class CreateHouseholdUseCase {
|
||||
final HouseholdRepository repository;
|
||||
|
||||
CreateHouseholdUseCase(this.repository);
|
||||
|
||||
Future<HouseholdEntity> call(String name, String userId) async {
|
||||
if (name.trim().isEmpty) {
|
||||
throw HouseholdValidationException('Household name cannot be empty');
|
||||
}
|
||||
|
||||
if (name.length > 100) {
|
||||
throw HouseholdValidationException('Household name too long (max 100 characters)');
|
||||
}
|
||||
|
||||
return await repository.createHousehold(name.trim(), userId);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
2. GenerateInviteCodeUseCase (generate_invite_code_usecase.dart):
|
||||
```dart
|
||||
class GenerateInviteCodeUseCase {
|
||||
final HouseholdRepository repository;
|
||||
|
||||
GenerateInviteCodeUseCase(this.repository);
|
||||
|
||||
Future<InviteCodeModel> call(String householdId, String userId) async {
|
||||
// Check if user is owner (will be validated in repository)
|
||||
return await repository.generateInviteCode(householdId, userId);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
3. JoinHouseholdUseCase (join_household_usecase.dart):
|
||||
```dart
|
||||
class JoinHouseholdUseCase {
|
||||
final HouseholdRepository repository;
|
||||
|
||||
JoinHouseholdUseCase(this.repository);
|
||||
|
||||
Future<HouseholdEntity> call(String inviteCode, String userId) async {
|
||||
if (inviteCode.trim().isEmpty) {
|
||||
throw HouseholdValidationException('Invite code cannot be empty');
|
||||
}
|
||||
|
||||
if (inviteCode.length != 8) {
|
||||
throw HouseholdValidationException('Invite code must be 8 characters');
|
||||
}
|
||||
|
||||
return await repository.joinHousehold(inviteCode.trim().toUpperCase(), userId);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
4. GetUserHouseholdsUseCase (get_user_households_usecase.dart):
|
||||
```dart
|
||||
class GetUserHouseholdsUseCase {
|
||||
final HouseholdRepository repository;
|
||||
|
||||
GetUserHouseholdsUseCase(this.repository);
|
||||
|
||||
Future<List<HouseholdEntity>> call(String userId) async {
|
||||
return await repository.getUserHouseholds(userId);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Include proper validation and business logic in each use case.
|
||||
Create custom exception classes for household operations.
|
||||
</action>
|
||||
<verify>flutter analyze lib/features/household/domain/usecases/*.dart passes</verify>
|
||||
<done>Household use cases encapsulate business logic with proper validation</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Create Household Provider</name>
|
||||
<files>lib/features/household/providers/household_provider.dart</files>
|
||||
<action>
|
||||
Create Riverpod provider for household state management:
|
||||
```dart
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
// Use case providers
|
||||
final createHouseholdUseCaseProvider = Provider<CreateHouseholdUseCase>((ref) {
|
||||
final repository = ref.watch(householdRepositoryProvider);
|
||||
return CreateHouseholdUseCase(repository);
|
||||
});
|
||||
|
||||
final generateInviteCodeUseCaseProvider = Provider<GenerateInviteCodeUseCase>((ref) {
|
||||
final repository = ref.watch(householdRepositoryProvider);
|
||||
return GenerateInviteCodeUseCase(repository);
|
||||
});
|
||||
|
||||
final joinHouseholdUseCaseProvider = Provider<JoinHouseholdUseCase>((ref) {
|
||||
final repository = ref.watch(householdRepositoryProvider);
|
||||
return JoinHouseholdUseCase(repository);
|
||||
});
|
||||
|
||||
final getUserHouseholdsUseCaseProvider = Provider<GetUserHouseholdsUseCase>((ref) {
|
||||
final repository = ref.watch(householdRepositoryProvider);
|
||||
return GetUserHouseholdsUseCase(repository);
|
||||
});
|
||||
|
||||
// Main household provider
|
||||
class HouseholdProvider extends StateNotifier<AsyncValue<List<HouseholdEntity>>> {
|
||||
final GetUserHouseholdsUseCase _getUserHouseholdsUseCase;
|
||||
final CreateHouseholdUseCase _createHouseholdUseCase;
|
||||
final JoinHouseholdUseCase _joinHouseholdUseCase;
|
||||
final GenerateInviteCodeUseCase _generateInviteCodeUseCase;
|
||||
|
||||
HouseholdProvider(
|
||||
this._getUserHouseholdsUseCase,
|
||||
this._createHouseholdUseCase,
|
||||
this._joinHouseholdUseCase,
|
||||
this._generateInviteCodeUseCase,
|
||||
) : super(const AsyncValue.loading()) {
|
||||
_initialize();
|
||||
}
|
||||
|
||||
Future<void> _initialize() async {
|
||||
state = const AsyncValue.loading();
|
||||
try {
|
||||
// Will be called when auth state is available
|
||||
} catch (e, stack) {
|
||||
state = AsyncValue.error(e, stack);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> loadUserHouseholds(String userId) async {
|
||||
state = const AsyncValue.loading();
|
||||
try {
|
||||
final households = await _getUserHouseholdsUseCase(userId);
|
||||
state = AsyncValue.data(households);
|
||||
} catch (e, stack) {
|
||||
state = AsyncValue.error(e, stack);
|
||||
}
|
||||
}
|
||||
|
||||
Future<HouseholdEntity> createHousehold(String name, String userId) async {
|
||||
try {
|
||||
final household = await _createHouseholdUseCase(name, userId);
|
||||
|
||||
// Refresh the list
|
||||
await loadUserHouseholds(userId);
|
||||
|
||||
return household;
|
||||
} catch (e) {
|
||||
throw HouseholdOperationException('Failed to create household: $e');
|
||||
}
|
||||
}
|
||||
|
||||
Future<HouseholdEntity> joinHousehold(String inviteCode, String userId) async {
|
||||
try {
|
||||
final household = await _joinHouseholdUseCase(inviteCode, userId);
|
||||
|
||||
// Refresh the list
|
||||
await loadUserHouseholds(userId);
|
||||
|
||||
return household;
|
||||
} catch (e) {
|
||||
throw HouseholdOperationException('Failed to join household: $e');
|
||||
}
|
||||
}
|
||||
|
||||
Future<InviteCodeModel> generateInviteCode(String householdId, String userId) async {
|
||||
try {
|
||||
return await _generateInviteCodeUseCase(householdId, userId);
|
||||
} catch (e) {
|
||||
throw HouseholdOperationException('Failed to generate invite code: $e');
|
||||
}
|
||||
}
|
||||
|
||||
void clearHouseholds() {
|
||||
state = const AsyncValue.data([]);
|
||||
}
|
||||
}
|
||||
|
||||
// Provider instance
|
||||
final householdProvider = StateNotifierProvider<HouseholdProvider, AsyncValue<List<HouseholdEntity>>>((ref) {
|
||||
return HouseholdProvider(
|
||||
ref.read(getUserHouseholdsUseCaseProvider),
|
||||
ref.read(createHouseholdUseCaseProvider),
|
||||
ref.read(joinHouseholdUseCaseProvider),
|
||||
ref.read(generateInviteCodeUseCaseProvider),
|
||||
);
|
||||
});
|
||||
|
||||
// Current selected household provider
|
||||
final currentHouseholdProvider = StateProvider<HouseholdEntity?>((ref) => null);
|
||||
|
||||
// Repository provider (to be added to main providers file)
|
||||
final householdRepositoryProvider = Provider<HouseholdRepository>((ref) {
|
||||
final datasource = ref.read(householdRemoteDatasourceProvider);
|
||||
return HouseholdRepositoryImpl(datasource);
|
||||
});
|
||||
|
||||
final householdRemoteDatasourceProvider = Provider<HouseholdRemoteDatasource>((ref) {
|
||||
final client = ref.read(supabaseClientProvider);
|
||||
return HouseholdRemoteDatasource(client);
|
||||
});
|
||||
```
|
||||
|
||||
Follow auth provider patterns for consistency.
|
||||
Include proper error handling and state transitions.
|
||||
</action>
|
||||
<verify>flutter analyze lib/features/household/providers/household_provider.dart passes</verify>
|
||||
<done>Household provider provides reactive state management with proper integration</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Create Household Exception Classes</name>
|
||||
<files>lib/features/household/domain/exceptions/household_exceptions.dart</files>
|
||||
<action>
|
||||
Create custom exception classes for household operations:
|
||||
```dart
|
||||
// Base household exception
|
||||
abstract class HouseholdException implements Exception {
|
||||
final String message;
|
||||
const HouseholdException(this.message);
|
||||
|
||||
@override
|
||||
String toString() => 'HouseholdException: $message';
|
||||
}
|
||||
|
||||
// Validation exceptions
|
||||
class HouseholdValidationException extends HouseholdException {
|
||||
const HouseholdValidationException(String message) : super(message);
|
||||
|
||||
@override
|
||||
String toString() => 'HouseholdValidationException: $message';
|
||||
}
|
||||
|
||||
// Operation exceptions
|
||||
class HouseholdOperationException extends HouseholdException {
|
||||
const HouseholdOperationException(String message) : super(message);
|
||||
|
||||
@override
|
||||
String toString() => 'HouseholdOperationException: $message';
|
||||
}
|
||||
|
||||
// Specific household exceptions
|
||||
class HouseholdNotFoundException extends HouseholdException {
|
||||
const HouseholdNotFoundException(String message) : super(message);
|
||||
|
||||
@override
|
||||
String toString() => 'HouseholdNotFoundException: $message';
|
||||
}
|
||||
|
||||
class InviteCodeException extends HouseholdException {
|
||||
const InviteCodeException(String message) : super(message);
|
||||
|
||||
@override
|
||||
String toString() => 'InviteCodeException: $message';
|
||||
}
|
||||
|
||||
class HouseholdMembershipException extends HouseholdException {
|
||||
const HouseholdMembershipException(String message) : super(message);
|
||||
|
||||
@override
|
||||
String toString() => 'HouseholdMembershipException: $message';
|
||||
}
|
||||
```
|
||||
|
||||
Follow authentication exception patterns for consistency.
|
||||
</action>
|
||||
<verify>flutter analyze lib/features/household/domain/exceptions/household_exceptions.dart passes</verify>
|
||||
<done>Household exception classes provide clear error categorization</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<verification>
|
||||
1. Use cases encapsulate business logic with proper validation
|
||||
2. Household provider provides reactive state management following Riverpod patterns
|
||||
3. Exception classes provide clear error categorization
|
||||
4. Integration with authentication state is properly structured
|
||||
5. All household operations handle loading states and errors appropriately
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
Household state management is complete with use cases, provider, and exception handling ready for UI integration.
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
After completion, create `.planning/phases/02-household-creation/02-03-SUMMARY.md` with state management implementation summary
|
||||
</output>
|
||||
Reference in New Issue
Block a user