Skip to content

Commit 0af2e99

Browse files
authored
Merge pull request #331 from DevKor-github/328-스케줄-겹침-경고
328-스케줄-겹침-경고: 일정 겹침 경고 기능 추가
2 parents 1e62f69 + d2eb115 commit 0af2e99

257 files changed

Lines changed: 37168 additions & 242 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/copilot-instructions.md

Lines changed: 268 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,268 @@
1+
# OnTime Flutter Project - AI Coding Agent Instructions
2+
3+
## Architecture Overview
4+
5+
This is a **Flutter app** following **Clean Architecture** with three layers:
6+
7+
- **Presentation** (`lib/presentation/`): Screens, widgets, BLoC/Cubit state management
8+
- **Domain** (`lib/domain/`): Business logic, entities, use cases, repository interfaces
9+
- **Data** (`lib/data/`): Repository implementations, data sources (remote/local), DAOs, database tables
10+
11+
Data flows: UI → BLoC → UseCase → Repository → DataSource → API/DB
12+
13+
## Critical Build Commands
14+
15+
```bash
16+
# ALWAYS run after pulling or modifying data models, entities, or DI
17+
dart run build_runner build --delete-conflicting-outputs
18+
19+
# Watch mode for development
20+
dart run build_runner watch
21+
22+
# Run tests
23+
flutter test
24+
25+
# Run app
26+
flutter run
27+
```
28+
29+
## Dependency Injection Pattern
30+
31+
- Uses **Injectable** + **GetIt** for DI
32+
- All repositories, use cases, BLoCs, and data sources must be annotated:
33+
- `@Injectable()` for standard dependencies
34+
- `@Singleton()` for app-wide singletons (e.g., `ScheduleBloc`, `AppDatabase`)
35+
- `@LazySingleton()` for lazy-initialized singletons
36+
- After adding `@Injectable()`, run `dart run build_runner build`
37+
- Register as interface: `@Injectable(as: InterfaceName)`
38+
39+
Example:
40+
41+
```dart
42+
@Injectable()
43+
class UpdateScheduleUseCase {
44+
final ScheduleRepository _repository;
45+
UpdateScheduleUseCase(this._repository);
46+
}
47+
48+
@Singleton(as: ScheduleRepository)
49+
class ScheduleRepositoryImpl implements ScheduleRepository { ... }
50+
```
51+
52+
## State Management with BLoC
53+
54+
- Use **BLoC** for complex state, **Cubit** for simpler scenarios
55+
- BLoCs live in `lib/presentation/*/bloc/` directories
56+
- Pattern: `on<EventName>(_onEventName)` event handlers
57+
- **Critical**: `ScheduleBloc` is a `@Singleton()` that manages automatic timer system for schedule start notifications (see `docs/Schedule-Timer-System.md`)
58+
- Keep business logic in **use cases**, not BLoCs
59+
- BLoCs coordinate between use cases and UI
60+
61+
Example structure:
62+
63+
```dart
64+
@Injectable()
65+
class MyBloc extends Bloc<MyEvent, MyState> {
66+
MyBloc(this._useCase) : super(MyState.initial()) {
67+
on<MyEventHappened>(_onMyEventHappened);
68+
}
69+
70+
Future<void> _onMyEventHappened(MyEventHappened event, Emitter<MyState> emit) async {
71+
final result = await _useCase();
72+
emit(state.copyWith(data: result));
73+
}
74+
}
75+
```
76+
77+
## Data Layer Patterns
78+
79+
### Entities (Domain)
80+
81+
- Use **Equatable** for value equality (most entities)
82+
- Or **Freezed** for advanced immutability (e.g., `UserEntity`)
83+
- Live in `lib/domain/entities/`
84+
- Example: `ScheduleEntity`, `PreparationEntity`
85+
86+
### Data Models (Data Layer)
87+
88+
- Use **json_serializable** for API models
89+
- Live in `lib/data/models/`
90+
- Must have `toEntity()` method to convert to domain entities
91+
- Repository boundary: models → entities when leaving data layer
92+
93+
### Database (Drift)
94+
95+
- Tables defined in `lib/data/tables/`
96+
- DAOs in `lib/data/daos/`
97+
- Database: `lib/core/database/database.dart` with `@Singleton()` annotation
98+
- Schema version tracked, migrations in `MigrationStrategy`
99+
100+
### Data Sources
101+
102+
- Abstract interface + implementation pattern
103+
// ...existing code...
104+
- Remote sources use Dio client (`lib/core/dio/app_dio.dart`)
105+
- Local sources use Drift DAOs or SharedPreferences
106+
- Example: `ScheduleRemoteDataSource` + `ScheduleRemoteDataSourceImpl`
107+
108+
## Reactive Data Fetching Pattern
109+
110+
The app uses a **reactive repository pattern** where UI updates automatically when data changes.
111+
112+
1. **Repository**: Holds a `BehaviorSubject` (Stream) of data.
113+
114+
- Methods like `getSchedulesByDate` fetch from API/DB and **add the result to the stream**.
115+
- `scheduleStream` exposes this subject as a broadcast stream.
116+
117+
2. **UseCase**: Transforms the repository stream.
118+
119+
- Can use `async*` to yield transformed states.
120+
- Combines multiple data sources if needed.
121+
122+
3. **BLoC**: Subscribes to the UseCase stream.
123+
// ...existing code...
124+
- `listen()` to the stream in event handlers.
125+
- `add()` new events to the BLoC when stream emits.
126+
127+
Example `ScheduleRepositoryImpl`:
128+
129+
```dart
130+
@Singleton(as: ScheduleRepository)
131+
class ScheduleRepositoryImpl implements ScheduleRepository {
132+
late final _scheduleStreamController = BehaviorSubject<Set<ScheduleEntity>>.seeded({});
133+
134+
@override
135+
Stream<Set<ScheduleEntity>> get scheduleStream => _scheduleStreamController.asBroadcastStream();
136+
137+
@override
138+
Future<List<ScheduleEntity>> getSchedulesByDate(...) async {
139+
final schedules = await remoteDataSource.getSchedulesByDate(...);
140+
// Update the stream with new data
141+
_scheduleStreamController.add(Set.from(_scheduleStreamController.value)..addAll(schedules));
142+
return schedules;
143+
}
144+
}
145+
```
146+
147+
## Form Management & Validation
148+
149+
The app uses a **Multi-Step Form Pattern** (e.g., `ScheduleMultiPageForm`):
150+
151+
1. **Parent BLoC**: `ScheduleFormBloc` manages the overall form state and final submission.
152+
2. **Step Cubits**: Each form step has its own Cubit (e.g., `ScheduleNameCubit`) that:
153+
- Manages local UI state for that step.
154+
- Communicates updates to the parent `ScheduleFormBloc`.
155+
3. **Validation**:
156+
- `ScheduleFormState` tracks `isValid` status.
157+
- `TopBar` enables navigation buttons based on validity.
158+
- `GlobalKey<FormState>` is used for per-page field validation.
159+
160+
## Data Transfer & Navigation
161+
162+
- **Route Arguments**: Pass IDs (e.g., `scheduleId`) via `GoRouter` path parameters or `extra` object.
163+
- **Screen Initialization**:
164+
- Screens receive arguments in their constructor.
165+
- BLoCs are initialized with these arguments (e.g., `ScheduleFormEditRequested(scheduleId)`).
166+
- BLoCs fetch full data from Repositories/UseCases using the ID.
167+
- **Avoid** passing full entity objects between screens; pass IDs and refetch/stream data.
168+
169+
## Navigation (GoRouter)
170+
171+
// ...existing code...Example `ScheduleRepositoryImpl`:
172+
173+
```dart
174+
@Singleton(as: ScheduleRepository)
175+
class ScheduleRepositoryImpl implements ScheduleRepository {
176+
late final _scheduleStreamController = BehaviorSubject<Set<ScheduleEntity>>.seeded({});
177+
178+
@override
179+
Stream<Set<ScheduleEntity>> get scheduleStream => _scheduleStreamController.asBroadcastStream();
180+
181+
@override
182+
Future<List<ScheduleEntity>> getSchedulesByDate(...) async {
183+
final schedules = await remoteDataSource.getSchedulesByDate(...);
184+
// Update the stream with new data
185+
_scheduleStreamController.add(Set.from(_scheduleStreamController.value)..addAll(schedules));
186+
return schedules;
187+
}
188+
}
189+
```
190+
191+
## Navigation (GoRouter)
192+
193+
// ...existing code...## Navigation (GoRouter)
194+
195+
- Configured in `lib/presentation/shared/router/go_router.dart`
196+
- Uses declarative routing with redirect logic based on `AuthBloc` and `ScheduleBloc` state
197+
- Routes defined with path and builder
198+
- NavigationService (`lib/core/services/navigation_service.dart`) provides programmatic navigation
199+
200+
## Code Generation Files
201+
202+
**Never manually edit** generated files (`.g.dart`, `.freezed.dart`, `di_setup.config.dart`). These are created by:
203+
204+
- `build_runner` for JSON serialization, Freezed, Injectable, Drift
205+
- Regenerate after modifying annotated classes
206+
207+
## Testing
208+
209+
- Tests in `test/` mirror `lib/` structure
210+
- Use Mockito for mocking dependencies
211+
- Test helpers in `test/helpers/`
212+
- Run coverage: `flutter test --coverage`
213+
214+
## Commit Message Convention
215+
216+
Follow **Conventional Commits** (enforced by commitlint):
217+
218+
- Format: `<type>(<optional scope>): <description>`
219+
- Types: `feat`, `fix`, `refactor`, `perf`, `style`, `test`, `docs`, `build`, `ops`, `chore`
220+
- Breaking changes: Add `!` before `:` (e.g., `feat(api)!: remove endpoint`)
221+
- See `docs/Git.md` for detailed examples
222+
223+
## Key Project Files
224+
225+
- `lib/main.dart`: App entry point, Firebase initialization, DI setup
226+
- `lib/presentation/app/screens/app.dart`: Root app widget
227+
- `lib/core/di/di_setup.dart`: DI configuration
228+
- `lib/core/database/database.dart`: Drift database setup
229+
- `docs/Architecture.md`: Comprehensive architecture documentation
230+
- `docs/Schedule-Timer-System.md`: Timer system details for ScheduleBloc
231+
232+
## Common Patterns
233+
234+
1. **Creating a new feature**:
235+
236+
- Add entity in `domain/entities/`
237+
- Create repository interface in `domain/repositories/`
238+
- Add use cases in `domain/use-cases/`
239+
- Implement repository in `data/repositories/`
240+
- Add data sources (remote/local) in `data/data_sources/`
241+
- Build UI with BLoC in `presentation/<feature>/`
242+
- Annotate all with `@Injectable()` and run build_runner
243+
244+
2. **Adding API endpoint**:
245+
246+
- Create request/response models in `data/models/` with `@JsonSerializable()`
247+
- Add method to RemoteDataSource interface and implementation
248+
- Update repository to use new data source method
249+
- Run `dart run build_runner build`
250+
251+
3. **Adding database table**:
252+
- Define table in `data/tables/`
253+
- Create DAO in `data/daos/`
254+
- Add to `@DriftDatabase` annotation in `database.dart`
255+
- Increment `schemaVersion` and add migration if needed
256+
- Run `dart run build_runner build`
257+
258+
## Environment & Configuration
259+
260+
- Environment variables in `lib/core/constants/environment_variable.dart`
261+
- Firebase configuration in `firebase_options.dart`
262+
- Analysis options in `analysis_options.yaml`
263+
264+
## Widgetbook
265+
266+
- Component showcase at `widgetbook/` (separate sub-project)
267+
- Hosted at: https://on-time-front-widgetbook.web.app/
268+
- Use for UI component development and testing

coverage/html/amber.png

141 Bytes
Loading

coverage/html/cmd_line

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
genhtml coverage/lcov.info -o coverage/html

0 commit comments

Comments
 (0)