diff --git a/front/lib/app/config/routes.dart b/front/lib/app/config/routes.dart index 815dfa5..e4a2459 100644 --- a/front/lib/app/config/routes.dart +++ b/front/lib/app/config/routes.dart @@ -7,6 +7,7 @@ import 'package:front/features/mypage/presentation/screen/change_info_list.dart' import 'package:front/features/mypage/presentation/screen/main_screen.dart'; import 'package:front/features/mypage/presentation/screen/my_info_screen.dart'; import 'package:front/features/mypage/presentation/screen/social_login_info_screen.dart'; +import 'package:front/features/project/entities/project.dart'; import 'package:front/features/project/presentaion/screen/project_creation.dart'; import 'package:front/features/project/presentaion/screen/project_detail.dart'; import 'package:front/features/project/presentaion/screen/project_update.dart'; @@ -56,9 +57,9 @@ final router = GoRouter(initialLocation: '/', routes: [ }, ), GoRoute( - path: 'projectDetail', - name: 'projectDetail', - builder: (context, state) => const ProjectDetailScreen(), + path: 'projectDetail/:projectId', + name: 'projectDetail/:projectId', + builder: (context, state) => ProjectDetailScreen(projectId: int.parse(state.pathParameters['projectId']!)), ), GoRoute( path: 'projectCreate/:teamId', @@ -69,7 +70,7 @@ final router = GoRouter(initialLocation: '/', routes: [ GoRoute( path: 'projectUpdate', name: 'projectUpdate', - builder: (context, state) => ProjectUpdateScreen(), + builder: (context, state) => ProjectUpdateScreen(project: state.extra! as Project), ), GoRoute( path: 'main', diff --git a/front/lib/features/home.dart b/front/lib/features/home.dart index 2735124..0c2f853 100644 --- a/front/lib/features/home.dart +++ b/front/lib/features/home.dart @@ -28,21 +28,6 @@ class HomeScreen extends StatelessWidget { }, child: const Text('formExample'), ), - ElevatedButton( - onPressed: () { - context.go('/projectDetail'); - }, - child: const Text('프로젝트')), - ElevatedButton( - onPressed: () { - context.go('/projectCreate'); - }, - child: const Text('프로젝트 생성')), - ElevatedButton( - onPressed: () { - context.go('/projectUpdate'); - }, - child: const Text('프로젝트 수정')), ElevatedButton( onPressed: () { context.go('/main'); diff --git a/front/lib/features/project/data/data_sources/remote_data_source.dart b/front/lib/features/project/data/data_sources/remote_data_source.dart index b683cd7..29359b3 100644 --- a/front/lib/features/project/data/data_sources/remote_data_source.dart +++ b/front/lib/features/project/data/data_sources/remote_data_source.dart @@ -9,7 +9,7 @@ abstract class ProjectRemoteDataSource { Future> getProjectsByTeamId(int teamId); Future getProjectById(int projectId); Future createProject(ProjectRequestModel project, int teamId); - Future updateProjectById( + Future updateProjectById( ProjectRequestModel project, int projectId); Future deleteProjectById(int projectId); } @@ -21,6 +21,7 @@ class ProjectRemoteDataSourceImpl implements ProjectRemoteDataSource { @override Future> getProjectsByTeamId(int teamId) async { try { + dio.options.headers = {'accessToken': 'true'}; debugPrint('teamId: $teamId'); var response = await dio.get( '/v1/team/$teamId/projects', @@ -43,6 +44,7 @@ class ProjectRemoteDataSourceImpl implements ProjectRemoteDataSource { @override Future getProjectById(int projectId) async { try { + dio.options.headers = {'accessToken': 'true'}; var response = await dio.get('/v1/project/$projectId'); if (response.statusCode == 200 && response.data?['resultCode'] == 200) { @@ -59,8 +61,16 @@ class ProjectRemoteDataSourceImpl implements ProjectRemoteDataSource { Future createProject( ProjectRequestModel project, int teamId) async { try { - var response = - await dio.post('/v1/team/$teamId/project', data: project.toJson()); + dio.options.headers = {'accessToken': 'true'}; + var response = await dio.post( + '/v1/team/$teamId/project', + data: project.toJson(), + options: Options( + headers: { + 'accessToken': 'true', + }, + ), + ); if (response.statusCode == 201 && response.data?['resultCode'] == 201) { return ProjectModel.fromJson(response.data['result']); } else { @@ -75,6 +85,7 @@ class ProjectRemoteDataSourceImpl implements ProjectRemoteDataSource { @override Future deleteProjectById(int projectId) async { try { + dio.options.headers = {'accessToken': 'true'}; var response = await dio.delete('/v1/project/$projectId'); if (response.statusCode == 200 && response.data?['resultCode'] == 200) { @@ -82,24 +93,27 @@ class ProjectRemoteDataSourceImpl implements ProjectRemoteDataSource { } else { throw ServerException(); } - } on DioException { + } on DioException catch (e){ + debugPrint(e.response?.toString()); throw ServerException(); } } @override - Future updateProjectById( + Future updateProjectById( ProjectRequestModel project, int projectId) async { try { + dio.options.headers = {'accessToken': 'true'}; var response = - await dio.patch('/v1/project/$projectId', data: project.toJson()); + await dio.patch('/v1/project/$projectId', data: project.toJson(),); - if (response.statusCode == 200 && response.data?['resultCode'] == 204) { - return ProjectModel.fromJson(response.data['result']); + if (response.statusCode == 204 && response.data?['resultCode'] == 204) { + return project; } else { throw ServerException(); } - } on DioException { + } on DioException catch (e) { + debugPrint(e.response?.toString()); throw ServerException(); } } diff --git a/front/lib/features/project/data/models/project_request.dart b/front/lib/features/project/data/models/project_request.dart index 7ee0372..2713072 100644 --- a/front/lib/features/project/data/models/project_request.dart +++ b/front/lib/features/project/data/models/project_request.dart @@ -1,4 +1,5 @@ import 'package:equatable/equatable.dart'; +import 'package:front/features/project/entities/project.dart'; class ProjectRequestModel extends Equatable { const ProjectRequestModel({ @@ -15,6 +16,14 @@ class ProjectRequestModel extends Equatable { ); } + factory ProjectRequestModel.fromEntity(Project project) { + return ProjectRequestModel( + name: project.name, + description: project.description, + managerId: project.managerId, + ); + } + Map toJson() { return { 'name': name, diff --git a/front/lib/features/project/data/repositories/project_repository_impl.dart b/front/lib/features/project/data/repositories/project_repository_impl.dart index 76c8e12..1661456 100644 --- a/front/lib/features/project/data/repositories/project_repository_impl.dart +++ b/front/lib/features/project/data/repositories/project_repository_impl.dart @@ -64,12 +64,12 @@ class ProjectRepositoryImpl implements ProjectRepository { @override Future> updateProjectById( - ProjectRequestModel project, int projectId) async { + Project project, int projectId) async { try { var result = - await projectRemoteDataSource.updateProjectById(project, projectId); - projects.addAll({result.projectId: result.toEntity()}); - return Right(result.toEntity()); + await projectRemoteDataSource.updateProjectById(ProjectRequestModel.fromEntity(project), projectId); + projects.addAll({projectId: project}); + return Right(project); } on ServerException { return const Left(ServerFailure('An error has occurred')); } on SocketException { diff --git a/front/lib/features/project/presentaion/screen/project_creation.dart b/front/lib/features/project/presentaion/screen/project_creation.dart index bc5eb15..6ee1c5b 100644 --- a/front/lib/features/project/presentaion/screen/project_creation.dart +++ b/front/lib/features/project/presentaion/screen/project_creation.dart @@ -139,12 +139,12 @@ class _ProjectCreateScreenState extends ConsumerState { } } -// //component +//component -// @widgetbook.UseCase( -// name: '', -// type: ProjectCreateScreen, -// ) -// Widget projectCreateScreenUseCase(BuildContext context) { -// return ProjectCreateScreen(); -// } +@widgetbook.UseCase( + name: '', + type: ProjectCreateScreen, +) +Widget projectCreateScreenUseCase(BuildContext context) { + return ProjectCreateScreen(teamId: 1); +} diff --git a/front/lib/features/project/presentaion/screen/project_detail.dart b/front/lib/features/project/presentaion/screen/project_detail.dart index 50b9610..bbf0418 100644 --- a/front/lib/features/project/presentaion/screen/project_detail.dart +++ b/front/lib/features/project/presentaion/screen/project_detail.dart @@ -1,53 +1,76 @@ import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:front/core/const/enum.dart'; +import 'package:front/features/project/entities/project.dart'; import 'package:front/features/project/presentaion/component/task_component.dart'; +import 'package:front/features/project/presentaion/viewmodel/project.dart'; import 'package:front/shared/atom/bottom_navigation_bar.dart'; import 'package:front/shared/utils/intl_format_date.dart'; +import 'package:go_router/go_router.dart'; import 'package:widgetbook_annotation/widgetbook_annotation.dart' as widgetbook; -class ProjectDetailScreen extends StatelessWidget { - const ProjectDetailScreen({super.key}); +class ProjectDetailScreen extends ConsumerStatefulWidget { + const ProjectDetailScreen({super.key, required this.projectId}); + + final int projectId; @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar(), - body: const _Body(), - floatingActionButton: const _AddTaskButton(), - floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat, - bottomNavigationBar: const ProjectBottomNavigationBar(), - ); - } + ConsumerState createState() => + _ProjectDetailScreenState(); } -class _Body extends StatelessWidget { - const _Body({Key? key}) : super(key: key); +class _ProjectDetailScreenState extends ConsumerState { + late ProjectViewmodel viewmodel; + + @override + void initState() { + super.initState(); + viewmodel = ref.read(projectViewmodelProvider.notifier); + viewmodel.getProjectById(widget.projectId); + } @override Widget build(BuildContext context) { - var projectName = 'title'; + var projectState = ref.watch(projectViewmodelProvider); var projectStartTime = DateTime.now(); var projectTask = []; var h1Textstyle = const TextStyle(fontSize: 20, fontWeight: FontWeight.bold); - return SafeArea( - child: Padding( - padding: const EdgeInsets.all(20.0), - child: SingleChildScrollView( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - '$projectName의 TimeLine', - style: h1Textstyle, + return Scaffold( + appBar: AppBar(), + body: SafeArea( + child: Padding( + padding: const EdgeInsets.all(20.0), + child: SingleChildScrollView( + child: projectState.when( + (project) => Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + '${project.name}의 TimeLine', + style: h1Textstyle, + ), + Text('${intlFormatDate(projectStartTime)}~'), + Text(project.description), + for (int i = 0; i < projectTask.length; i++) + TaskComponent(task: projectTask[i]), + TextButton( + child: Text('수정d'), + onPressed: () => + context.push('/projectUpdate', extra: project), + ) + ], ), - Text('${intlFormatDate(projectStartTime)}~'), - const Text('프로젝트 설명'), - for (int i = 0; i < projectTask.length; i++) - TaskComponent(task: projectTask[i]) - ], + loading: () => const CircularProgressIndicator(), + error: (message) => Text(message.toString()), + + ), ), ), ), + floatingActionButton: const _AddTaskButton(), + floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat, + bottomNavigationBar: const ProjectBottomNavigationBar(), ); } } @@ -73,5 +96,6 @@ class _AddTaskButton extends StatelessWidget { type: ProjectDetailScreen, ) Widget ProjectDetailScreenUseCase(BuildContext context) { - return const ProjectDetailScreen(); + return ProjectDetailScreen( + projectId: 1); } diff --git a/front/lib/features/project/presentaion/screen/project_update.dart b/front/lib/features/project/presentaion/screen/project_update.dart index 5026f83..bb17605 100644 --- a/front/lib/features/project/presentaion/screen/project_update.dart +++ b/front/lib/features/project/presentaion/screen/project_update.dart @@ -14,67 +14,84 @@ import 'package:front/shared/atom/bottom_navigation_bar.dart'; import 'package:front/shared/helper/FormHelper/form.dart'; import 'package:widgetbook_annotation/widgetbook_annotation.dart' as widgetbook; -class ProjectUpdateScreen extends StatelessWidget { +class ProjectUpdateScreen extends ConsumerStatefulWidget { ProjectUpdateScreen({ Key? key, + required this.project, }) : super(key: key); + final Project project; + final team = { 'id': '1', 'name': '팀이름', }; - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar(), - body: Padding( - padding: const EdgeInsets.all(16.0), - child: _Body(team: team), - ), - bottomNavigationBar: const ProjectBottomNavigationBar(), - ); - } -} -//view - -class _Body extends ConsumerStatefulWidget { - _Body({ - Key? key, - required this.team, - }) : super(key: key); - - final Map team; - final TextStyle h1TextStyle = const TextStyle( fontSize: 30, fontWeight: FontWeight.bold, color: Colors.black); - final project = Project( - projectId: 1, - name: 'name', - description: 'description', - managerId: 2, - projectState: ProjectStateEnum.before, - ); - @override - ConsumerState<_Body> createState() => _BodyState(); + ConsumerState createState() => + _ProjectUpdateScreenState(); } -class _BodyState extends ConsumerState<_Body> { +class _ProjectUpdateScreenState extends ConsumerState { late ProjectViewmodel viewmodel; final _formKey = GlobalKey(); late CustomFormState? currentState; var manager = UserModel(id: 1, nickname: 'user1', email: 'email', type: 'type'); final List teamMembers = [ - UserModel(id: 1, email: 'email', nickname: 'user1', type: 'MEMBER'), - UserModel(id: 1, email: 'email', nickname: 'user2', type: 'MEMBER'), - UserModel(id: 1, email: 'email', nickname: 'user3', type: 'MEMBER'), - UserModel(id: 1, email: 'email', nickname: 'user4', type: 'MEMBER'), - UserModel(id: 1, email: 'email', nickname: 'user5', type: 'MEMBER'), + UserModel(id: 4, email: 'email', nickname: 'user1', type: 'MEMBER'), + UserModel(id: 4, email: 'email', nickname: 'user2', type: 'MEMBER'), + UserModel(id: 4, email: 'email', nickname: 'user3', type: 'MEMBER'), + UserModel(id: 4, email: 'email', nickname: 'user4', type: 'MEMBER'), + UserModel(id: 4, email: 'email', nickname: 'user5', type: 'MEMBER'), ]; + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(), + body: Padding( + padding: const EdgeInsets.all(16.0), + child: SafeArea( + child: Padding( + padding: const EdgeInsets.all(20.0), + child: Column( + children: [ + buildHeader(), + const SizedBox(height: 10), + ProjectManagerSelector( + teamMembers: teamMembers, + manager: manager, + onChanged: onManagerChanged), + Expanded( + child: ProjectFrom( + formKey: _formKey, + onFormChanged: onFormChanged, + initialValue: { + 'name': widget.project.name, + 'description': widget.project.description, + }, + ), + ), + const SizedBox(height: 20), + Row( + children: [ + buildUpdateButton(), + buildDeleteButton(), + ], + ) + ], + ), + ), + ), + ), + bottomNavigationBar: const ProjectBottomNavigationBar(), + ); + } + @override void initState() { super.initState(); @@ -94,54 +111,19 @@ class _BodyState extends ConsumerState<_Body> { }); } - @override - Widget build(BuildContext context) { - return SafeArea( - child: Padding( - padding: const EdgeInsets.all(20.0), - child: Column( - children: [ - buildHeader(), - const SizedBox(height: 10), - ProjectManagerSelector( - teamMembers: teamMembers, - manager: manager, - onChanged: onManagerChanged), - Expanded( - child: ProjectFrom( - formKey: _formKey, - onFormChanged: onFormChanged, - initialValue: { - 'name': widget.project.name, - 'description': widget.project.description, - }, - ), - ), - const SizedBox(height: 20), - Row( - children: [ - buildUpdateButton(), - ], - ) - ], - ), - ), - ); - } - Widget buildHeader() { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - widget.team['name'], + widget.team['name'].toString(), style: widget.h1TextStyle, ), Text( - 'Project 추가하기', + 'Project 수정하기', style: widget.h1TextStyle, ), - const Text('새로운 프로젝트를 추가해보세요'), + const Text('프로젝트를 수정해보세요'), ], ); } @@ -151,11 +133,13 @@ class _BodyState extends ConsumerState<_Body> { onPressed: () async { if (_formKey.currentState?.validate(null) ?? false) { var result = await viewmodel.updateProject( - ProjectRequestModel( - managerId: 1, + Project( + managerId: manager.id, name: _formKey.currentState!.fields['name']!.value, description: - _formKey.currentState!.fields['description']!.value), + _formKey.currentState!.fields['description']!.value, + projectId: widget.project.projectId, + projectState: widget.project.projectState), widget.project.projectId); result.fold( (l) => debugPrint(l.toString()), (r) => debugPrint(r.toString())); @@ -182,12 +166,12 @@ class _BodyState extends ConsumerState<_Body> { } } -//component +// //component -@widgetbook.UseCase( - name: '', - type: ProjectUpdateScreen, -) -Widget projectCreateScreenUseCase(BuildContext context) { - return ProjectUpdateScreen(); -} +// @widgetbook.UseCase( +// name: '', +// type: ProjectUpdateScreen, +// ) +// Widget projectCreateScreenUseCase(BuildContext context) { +// return ProjectUpdateScreen(); +// } diff --git a/front/lib/features/project/presentaion/viewmodel/project.dart b/front/lib/features/project/presentaion/viewmodel/project.dart index 261a620..61cf4ec 100644 --- a/front/lib/features/project/presentaion/viewmodel/project.dart +++ b/front/lib/features/project/presentaion/viewmodel/project.dart @@ -27,11 +27,12 @@ class ProjectViewmodel extends _$ProjectViewmodel { } Future> updateProject( - ProjectRequestModel project, int projectId) async { + Project project, int projectId) async { return await ref.read(updateProjectByIdUseCaseProvider)(project, projectId); } Future> deleteProject(int projectId) async { - return await ref.read(deleteProjectByIdUseCaseProvider)(projectId); + var result = await ref.read(deleteProjectByIdUseCaseProvider)(projectId); + return result; } } diff --git a/front/lib/features/project/repositories/project_repository.dart b/front/lib/features/project/repositories/project_repository.dart index 193e57e..697f807 100644 --- a/front/lib/features/project/repositories/project_repository.dart +++ b/front/lib/features/project/repositories/project_repository.dart @@ -7,7 +7,7 @@ abstract class ProjectRepository { Future>> getProjectsByTeamId(int teamId); Future> getProjectById(int projectId); Future> updateProjectById( - ProjectRequestModel project, int projectId); + Project project, int projectId); Future> createProject( ProjectRequestModel project, int teamId); Future> deleteProjectById(int projectId); diff --git a/front/lib/features/project/usecases/update_project_by_id_usecase.dart b/front/lib/features/project/usecases/update_project_by_id_usecase.dart index 1c200bb..4b22455 100644 --- a/front/lib/features/project/usecases/update_project_by_id_usecase.dart +++ b/front/lib/features/project/usecases/update_project_by_id_usecase.dart @@ -9,7 +9,7 @@ class UpdateProjectUseCase { final ProjectRepository projectRepository; Future> call( - ProjectRequestModel project, int projectId) { + Project project, int projectId) { return projectRepository.updateProjectById(project, projectId); } } diff --git a/front/lib/features/team/presentation/pages/team/team_datail.dart b/front/lib/features/team/presentation/pages/team/team_datail.dart index 17fc8d3..5586f52 100644 --- a/front/lib/features/team/presentation/pages/team/team_datail.dart +++ b/front/lib/features/team/presentation/pages/team/team_datail.dart @@ -194,7 +194,9 @@ class ProjectList extends StatelessWidget { return Padding( padding: const EdgeInsets.only(bottom: 16), child: ElevatedButton( - onPressed: () {}, + onPressed: () { + context.push('/projectDetail/${projects[index].projectId}'); + }, style: widget.elevatedButtonStyle, child: Text( projects[index].name, diff --git a/front/test/core/project/data/data_source/remote_data_source_test.dart b/front/test/core/project/data/data_source/remote_data_source_test.dart index 7d99263..d9a0c41 100644 --- a/front/test/core/project/data/data_source/remote_data_source_test.dart +++ b/front/test/core/project/data/data_source/remote_data_source_test.dart @@ -77,7 +77,7 @@ void main() { var result = await projectRemoteDataSource.getProjectById(projectId); - expect(result, equals(projectModel)); + expect(result, equals(projectRequestModel)); }); test('should return server exception when a call to api is unsuccessful', diff --git a/front/web/firebase-messaging-sw.js b/front/web/firebase-messaging-sw.js new file mode 100644 index 0000000..3d407ca --- /dev/null +++ b/front/web/firebase-messaging-sw.js @@ -0,0 +1,21 @@ +importScripts("https://www.gstatic.com/firebasejs/8.10.0/firebase-app.js"); +importScripts( + "https://www.gstatic.com/firebasejs/8.10.0/firebase-messaging.js" +); + +firebase.initializeApp({ + apiKey: "...", + authDomain: "...", + databaseURL: "...", + projectId: "...", + storageBucket: "...", + messagingSenderId: "...", + appId: "...", +}); + +const messaging = firebase.messaging(); + +// Optional: +messaging.onBackgroundMessage((message) => { + console.log("onBackgroundMessage", message); +}); diff --git a/front/web/index.html b/front/web/index.html index 45c95cc..a6faad5 100644 --- a/front/web/index.html +++ b/front/web/index.html @@ -54,8 +54,10 @@ }).then(function (appRunner) { return appRunner.runApp(); }); + navigator.serviceWorker.register('/firebase-messaging-sw.js'); }); +