From 44e3ba1820b818acdca95e35c6a295f8bb9d2830 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=B0=AC=EC=9A=B0?= <105582057+wjdcksdn@users.noreply.github.com> Date: Thu, 11 Apr 2024 17:57:42 +0900 Subject: [PATCH 01/14] :lipstick: add enter attend icon --- app/src/main/res/drawable/ic_shake_hand.xml | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 app/src/main/res/drawable/ic_shake_hand.xml diff --git a/app/src/main/res/drawable/ic_shake_hand.xml b/app/src/main/res/drawable/ic_shake_hand.xml new file mode 100644 index 00000000..012498e6 --- /dev/null +++ b/app/src/main/res/drawable/ic_shake_hand.xml @@ -0,0 +1,12 @@ + + + + From 461ea97dd0fab144b38eb0877802426904bb6125 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=B0=AC=EC=9A=B0?= <105582057+wjdcksdn@users.noreply.github.com> Date: Mon, 22 Apr 2024 14:10:45 +0900 Subject: [PATCH 02/14] :memo: edit mistake --- .../com/msg/gcms/domain/usecase/attend/PostAttendListUseCase.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/msg/gcms/domain/usecase/attend/PostAttendListUseCase.kt b/app/src/main/java/com/msg/gcms/domain/usecase/attend/PostAttendListUseCase.kt index 6eb11f2c..fc4d4e8f 100644 --- a/app/src/main/java/com/msg/gcms/domain/usecase/attend/PostAttendListUseCase.kt +++ b/app/src/main/java/com/msg/gcms/domain/usecase/attend/PostAttendListUseCase.kt @@ -7,7 +7,7 @@ import javax.inject.Inject class PostAttendListUseCase @Inject constructor( private val attendRepository: AttendRepository ) { - suspend fun invoke(body: PostAttendListRequestData) = runCatching { + suspend operator fun invoke(body: PostAttendListRequestData) = runCatching { attendRepository.postAttendList( body = body ) From b00a32e2f84d63f27c0e69c28ea38a0ae5c6b402 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=B0=AC=EC=9A=B0?= <105582057+wjdcksdn@users.noreply.github.com> Date: Thu, 25 Apr 2024 14:43:08 +0900 Subject: [PATCH 03/14] :memo: change nullable type --- .../gcms/data/remote/datasource/attend/AttendDataSource.kt | 2 +- .../data/remote/datasource/attend/AttendDataSourceImpl.kt | 4 ++-- .../java/com/msg/gcms/data/remote/network/api/AttendAPI.kt | 4 ++-- .../java/com/msg/gcms/data/repository/AttendRepositoryImpl.kt | 4 ++-- .../gcms/domain/data/attend/GetClubAttendListResponseData.kt | 2 +- .../java/com/msg/gcms/domain/repository/AttendRepository.kt | 2 +- .../gcms/domain/usecase/attend/GetClubAttendListUseCase.kt | 2 +- 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/com/msg/gcms/data/remote/datasource/attend/AttendDataSource.kt b/app/src/main/java/com/msg/gcms/data/remote/datasource/attend/AttendDataSource.kt index 50126b9e..cc60d120 100644 --- a/app/src/main/java/com/msg/gcms/data/remote/datasource/attend/AttendDataSource.kt +++ b/app/src/main/java/com/msg/gcms/data/remote/datasource/attend/AttendDataSource.kt @@ -8,7 +8,7 @@ import kotlinx.coroutines.flow.Flow import java.time.LocalDate interface AttendDataSource { - suspend fun getClubAttendList(clubId: Long, date: LocalDate, period: String): Flow + suspend fun getClubAttendList(clubId: Long, date: LocalDate?, period: String?): Flow suspend fun postAttendList(body: PostAttendListRequest): Flow suspend fun patchAttendStatus(body: PatchAttendStatusRequest): Flow suspend fun patchAttendStatusCollectively(body: PatchAttendStatusCollectivelyRequest): Flow diff --git a/app/src/main/java/com/msg/gcms/data/remote/datasource/attend/AttendDataSourceImpl.kt b/app/src/main/java/com/msg/gcms/data/remote/datasource/attend/AttendDataSourceImpl.kt index 24176cc0..847fc22f 100644 --- a/app/src/main/java/com/msg/gcms/data/remote/datasource/attend/AttendDataSourceImpl.kt +++ b/app/src/main/java/com/msg/gcms/data/remote/datasource/attend/AttendDataSourceImpl.kt @@ -17,8 +17,8 @@ class AttendDataSourceImpl @Inject constructor( ) : AttendDataSource { override suspend fun getClubAttendList( clubId: Long, - date: LocalDate, - period: String + date: LocalDate?, + period: String? ): Flow = flow { emit( GCMSApiHandler() diff --git a/app/src/main/java/com/msg/gcms/data/remote/network/api/AttendAPI.kt b/app/src/main/java/com/msg/gcms/data/remote/network/api/AttendAPI.kt index be81c797..aaa0b2a1 100644 --- a/app/src/main/java/com/msg/gcms/data/remote/network/api/AttendAPI.kt +++ b/app/src/main/java/com/msg/gcms/data/remote/network/api/AttendAPI.kt @@ -18,8 +18,8 @@ interface AttendAPI { @GET("attend/{club_id}") suspend fun getAttendList( @Path("club_id") clubId: Long, - @Query("date") date: LocalDate, - @Query("period") period: String + @Query("date") date: LocalDate?, + @Query("period") period: String? ): GetClubAttendListResponse @POST("attend/{club_id}/club") diff --git a/app/src/main/java/com/msg/gcms/data/repository/AttendRepositoryImpl.kt b/app/src/main/java/com/msg/gcms/data/repository/AttendRepositoryImpl.kt index afd19925..9056d44a 100644 --- a/app/src/main/java/com/msg/gcms/data/repository/AttendRepositoryImpl.kt +++ b/app/src/main/java/com/msg/gcms/data/repository/AttendRepositoryImpl.kt @@ -21,8 +21,8 @@ class AttendRepositoryImpl @Inject constructor( ) : AttendRepository { override suspend fun getClubAttendList( clubId: Long, - date: LocalDate, - period: String + date: LocalDate?, + period: String? ): Flow { return attendDataSource.getClubAttendList( clubId = clubId, diff --git a/app/src/main/java/com/msg/gcms/domain/data/attend/GetClubAttendListResponseData.kt b/app/src/main/java/com/msg/gcms/domain/data/attend/GetClubAttendListResponseData.kt index e15358f2..812150a7 100644 --- a/app/src/main/java/com/msg/gcms/domain/data/attend/GetClubAttendListResponseData.kt +++ b/app/src/main/java/com/msg/gcms/domain/data/attend/GetClubAttendListResponseData.kt @@ -6,7 +6,7 @@ import java.util.UUID data class GetClubAttendListResponseData( val date: LocalDate, - val period: LocalTime, + val period: String, val users: List ) { data class User( diff --git a/app/src/main/java/com/msg/gcms/domain/repository/AttendRepository.kt b/app/src/main/java/com/msg/gcms/domain/repository/AttendRepository.kt index 5140eae6..d5d74291 100644 --- a/app/src/main/java/com/msg/gcms/domain/repository/AttendRepository.kt +++ b/app/src/main/java/com/msg/gcms/domain/repository/AttendRepository.kt @@ -8,7 +8,7 @@ import kotlinx.coroutines.flow.Flow import java.time.LocalDate interface AttendRepository { - suspend fun getClubAttendList(clubId: Long, date: LocalDate, period: String): Flow + suspend fun getClubAttendList(clubId: Long, date: LocalDate?, period: String?): Flow suspend fun postAttendList(body: PostAttendListRequestData): Flow suspend fun patchAttendStatus(body: PatchAttendStatusRequestData): Flow suspend fun patchAttendStatusCollectively(body: PatchAttendStatusCollectivelyRequestData): Flow diff --git a/app/src/main/java/com/msg/gcms/domain/usecase/attend/GetClubAttendListUseCase.kt b/app/src/main/java/com/msg/gcms/domain/usecase/attend/GetClubAttendListUseCase.kt index 099cf84a..28261a14 100644 --- a/app/src/main/java/com/msg/gcms/domain/usecase/attend/GetClubAttendListUseCase.kt +++ b/app/src/main/java/com/msg/gcms/domain/usecase/attend/GetClubAttendListUseCase.kt @@ -7,7 +7,7 @@ import javax.inject.Inject class GetClubAttendListUseCase @Inject constructor( private val attendRepository: AttendRepository ) { - suspend operator fun invoke(clubId: Long, date: LocalDate, period: String) = runCatching { + suspend operator fun invoke(clubId: Long, date: LocalDate?, period: String?) = runCatching { attendRepository.getClubAttendList( clubId = clubId, date = date, From c835869eea70b31bd5f797e640699938d73605e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=B0=AC=EC=9A=B0?= <105582057+wjdcksdn@users.noreply.github.com> Date: Thu, 25 Apr 2024 14:44:24 +0900 Subject: [PATCH 04/14] :lipstick: add and edit resources --- .../res/drawable/bg_attend_checkbox_checked.xml | 15 +++++++++++++++ app/src/main/res/drawable/ic_attend_absent.xml | 2 +- app/src/main/res/drawable/ic_attend_check.xml | 2 +- app/src/main/res/drawable/ic_attend_late.xml | 2 +- app/src/main/res/drawable/ic_attend_sick.xml | 2 +- .../res/drawable/selector_attend_checkbox.xml | 16 ++++++++++++++++ app/src/main/res/values/colors.xml | 4 ++++ 7 files changed, 39 insertions(+), 4 deletions(-) create mode 100644 app/src/main/res/drawable/bg_attend_checkbox_checked.xml create mode 100644 app/src/main/res/drawable/selector_attend_checkbox.xml diff --git a/app/src/main/res/drawable/bg_attend_checkbox_checked.xml b/app/src/main/res/drawable/bg_attend_checkbox_checked.xml new file mode 100644 index 00000000..9be25f90 --- /dev/null +++ b/app/src/main/res/drawable/bg_attend_checkbox_checked.xml @@ -0,0 +1,15 @@ + + + + diff --git a/app/src/main/res/drawable/ic_attend_absent.xml b/app/src/main/res/drawable/ic_attend_absent.xml index 7721d005..78f92014 100644 --- a/app/src/main/res/drawable/ic_attend_absent.xml +++ b/app/src/main/res/drawable/ic_attend_absent.xml @@ -10,7 +10,7 @@ android:pathData="M2,2L22,22M2,22L22,2" android:strokeWidth="4" android:fillColor="#00000000" - android:strokeColor="#ffffff" + android:strokeColor="#ff6666" android:strokeLineCap="round"/> diff --git a/app/src/main/res/drawable/ic_attend_check.xml b/app/src/main/res/drawable/ic_attend_check.xml index 8e115fa8..56ba1440 100644 --- a/app/src/main/res/drawable/ic_attend_check.xml +++ b/app/src/main/res/drawable/ic_attend_check.xml @@ -10,6 +10,6 @@ android:pathData="M12,12m-10,0a10,10 0,1 1,20 0a10,10 0,1 1,-20 0" android:strokeWidth="4" android:fillColor="#00000000" - android:strokeColor="#ffffff"/> + android:strokeColor="#FF4164E1"/> diff --git a/app/src/main/res/drawable/ic_attend_late.xml b/app/src/main/res/drawable/ic_attend_late.xml index 81c45599..56594eb4 100644 --- a/app/src/main/res/drawable/ic_attend_late.xml +++ b/app/src/main/res/drawable/ic_attend_late.xml @@ -7,5 +7,5 @@ android:pathData="M11.785,3.147C11.825,3.072 11.863,3.043 11.888,3.029C11.918,3.011 11.956,3 12,3C12.044,3 12.082,3.011 12.112,3.029C12.137,3.043 12.175,3.072 12.215,3.147L21.955,21.506C22.019,21.627 22.013,21.753 21.949,21.864C21.917,21.919 21.878,21.954 21.848,21.973C21.823,21.988 21.792,22 21.74,22H2.26C2.208,22 2.177,21.988 2.152,21.973C2.122,21.954 2.083,21.919 2.051,21.864C1.987,21.753 1.981,21.627 2.046,21.506L11.785,3.147Z" android:strokeWidth="4" android:fillColor="#00000000" - android:strokeColor="#ffffff"/> + android:strokeColor="#f4b925"/> diff --git a/app/src/main/res/drawable/ic_attend_sick.xml b/app/src/main/res/drawable/ic_attend_sick.xml index 0f9c480e..07a22949 100644 --- a/app/src/main/res/drawable/ic_attend_sick.xml +++ b/app/src/main/res/drawable/ic_attend_sick.xml @@ -5,5 +5,5 @@ android:viewportHeight="24"> + android:fillColor="#949494"/> diff --git a/app/src/main/res/drawable/selector_attend_checkbox.xml b/app/src/main/res/drawable/selector_attend_checkbox.xml new file mode 100644 index 00000000..b927d86e --- /dev/null +++ b/app/src/main/res/drawable/selector_attend_checkbox.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 77842e69..434040dc 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -46,4 +46,8 @@ #262626 #FCFFFF + + #26FFFFFF + #FFF4B925 + #FFFF6666 From 42216732ad1e535ed769985f682fdd843db7df28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=B0=AC=EC=9A=B0?= <105582057+wjdcksdn@users.noreply.github.com> Date: Mon, 29 Apr 2024 11:23:00 +0900 Subject: [PATCH 05/14] :lipstick: add icon --- .../main/res/drawable/ic_attend_absent_white.xml | 16 ++++++++++++++++ .../main/res/drawable/ic_attend_check_white.xml | 15 +++++++++++++++ .../main/res/drawable/ic_attend_late_white.xml | 11 +++++++++++ .../main/res/drawable/ic_attend_sick_white.xml | 9 +++++++++ app/src/main/res/drawable/ic_setting_solid.xml | 9 +++++++++ 5 files changed, 60 insertions(+) create mode 100644 app/src/main/res/drawable/ic_attend_absent_white.xml create mode 100644 app/src/main/res/drawable/ic_attend_check_white.xml create mode 100644 app/src/main/res/drawable/ic_attend_late_white.xml create mode 100644 app/src/main/res/drawable/ic_attend_sick_white.xml create mode 100644 app/src/main/res/drawable/ic_setting_solid.xml diff --git a/app/src/main/res/drawable/ic_attend_absent_white.xml b/app/src/main/res/drawable/ic_attend_absent_white.xml new file mode 100644 index 00000000..7721d005 --- /dev/null +++ b/app/src/main/res/drawable/ic_attend_absent_white.xml @@ -0,0 +1,16 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_attend_check_white.xml b/app/src/main/res/drawable/ic_attend_check_white.xml new file mode 100644 index 00000000..8e115fa8 --- /dev/null +++ b/app/src/main/res/drawable/ic_attend_check_white.xml @@ -0,0 +1,15 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_attend_late_white.xml b/app/src/main/res/drawable/ic_attend_late_white.xml new file mode 100644 index 00000000..81c45599 --- /dev/null +++ b/app/src/main/res/drawable/ic_attend_late_white.xml @@ -0,0 +1,11 @@ + + + diff --git a/app/src/main/res/drawable/ic_attend_sick_white.xml b/app/src/main/res/drawable/ic_attend_sick_white.xml new file mode 100644 index 00000000..0f9c480e --- /dev/null +++ b/app/src/main/res/drawable/ic_attend_sick_white.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_setting_solid.xml b/app/src/main/res/drawable/ic_setting_solid.xml new file mode 100644 index 00000000..fd78cf4b --- /dev/null +++ b/app/src/main/res/drawable/ic_setting_solid.xml @@ -0,0 +1,9 @@ + + + From 8ef53f4f2f415c83776151eddedfa101adf3c956 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=B0=AC=EC=9A=B0?= <105582057+wjdcksdn@users.noreply.github.com> Date: Mon, 29 Apr 2024 11:23:23 +0900 Subject: [PATCH 06/14] :lipstick: edit layout --- app/src/main/res/layout/activity_attend.xml | 17 +++++--- app/src/main/res/layout/attend_dialog.xml | 28 ++++++++++--- .../main/res/layout/list_member_attend.xml | 42 +++++++++++++++---- 3 files changed, 67 insertions(+), 20 deletions(-) diff --git a/app/src/main/res/layout/activity_attend.xml b/app/src/main/res/layout/activity_attend.xml index d6ba7366..5ecff132 100644 --- a/app/src/main/res/layout/activity_attend.xml +++ b/app/src/main/res/layout/activity_attend.xml @@ -2,6 +2,11 @@ + + + + + - + android:layout_marginEnd="20dp" /> @@ -97,6 +101,7 @@ + + \ No newline at end of file diff --git a/app/src/main/res/layout/list_member_attend.xml b/app/src/main/res/layout/list_member_attend.xml index 1c1dbbe9..58bfd95a 100644 --- a/app/src/main/res/layout/list_member_attend.xml +++ b/app/src/main/res/layout/list_member_attend.xml @@ -2,6 +2,11 @@ + + + + + - + + + + + + + + From accbba30596da4789a3110153d7df6796f997e80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=B0=AC=EC=9A=B0?= <105582057+wjdcksdn@users.noreply.github.com> Date: Mon, 29 Apr 2024 11:23:58 +0900 Subject: [PATCH 07/14] :memo: add goAttendActivity fun --- .../presentation/view/club/detail/DetailFragment.kt | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/msg/gcms/presentation/view/club/detail/DetailFragment.kt b/app/src/main/java/com/msg/gcms/presentation/view/club/detail/DetailFragment.kt index d8600993..b2653897 100644 --- a/app/src/main/java/com/msg/gcms/presentation/view/club/detail/DetailFragment.kt +++ b/app/src/main/java/com/msg/gcms/presentation/view/club/detail/DetailFragment.kt @@ -29,6 +29,7 @@ import com.msg.gcms.presentation.utils.enterActivity import com.msg.gcms.presentation.utils.exitActivity import com.msg.gcms.presentation.utils.exitFragment import com.msg.gcms.presentation.utils.stop +import com.msg.gcms.presentation.view.attend.AttendActivity import com.msg.gcms.presentation.view.club.ClubFragment import com.msg.gcms.presentation.view.editclub.EditClubActivity import com.msg.gcms.presentation.view.intro.IntroActivity @@ -56,6 +57,7 @@ class DetailFragment : BaseFragment(R.layout.fragment_det private val headSideBarItem = arrayListOf( DetailPageSideBar("동아리 멤버 관리하기", R.drawable.ic_person_two), + DetailPageSideBar("동아리 출석체크", R.drawable.ic_shake_hand), DetailPageSideBar("동아리 정보 수정하기", R.drawable.ic_edit), DetailPageSideBar("동아리 삭제하기", R.drawable.ic_club_delete) ) @@ -264,6 +266,9 @@ class DetailFragment : BaseFragment(R.layout.fragment_det goManageActivity() } 1 -> { + goAttendActivity() + } + 2 -> { val intent = Intent(context, EditClubActivity::class.java) intent.putExtra( "clubId", @@ -271,7 +276,7 @@ class DetailFragment : BaseFragment(R.layout.fragment_det ) startActivityForResult(intent, 0) } - 2 -> { + 3 -> { BaseDialog("동아리 삭제", "정말 삭제할꺼에요??", context!!).let { dialog -> dialog.show() dialog.dialogBinding.ok.setOnClickListener { @@ -298,6 +303,12 @@ class DetailFragment : BaseFragment(R.layout.fragment_det startActivityForResult(intent, 0) } + private fun goAttendActivity() { + val intent = Intent(context, AttendActivity::class.java) + intent.putExtra("clubId", detailViewModel.result.value!!.id) + startActivityForResult(intent, 0) + } + private fun checkRole() { binding.submitBtn.let { when (detailViewModel.result.value!!.scope) { From b0c9627374c664f40a7d482110fdc4e8df066477 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=B0=AC=EC=9A=B0?= <105582057+wjdcksdn@users.noreply.github.com> Date: Mon, 29 Apr 2024 11:24:19 +0900 Subject: [PATCH 08/14] :memo: add AttendViewModel --- .../presentation/viewmodel/AttendViewModel.kt | 144 ++++++++++++++++++ 1 file changed, 144 insertions(+) create mode 100644 app/src/main/java/com/msg/gcms/presentation/viewmodel/AttendViewModel.kt diff --git a/app/src/main/java/com/msg/gcms/presentation/viewmodel/AttendViewModel.kt b/app/src/main/java/com/msg/gcms/presentation/viewmodel/AttendViewModel.kt new file mode 100644 index 00000000..08730c65 --- /dev/null +++ b/app/src/main/java/com/msg/gcms/presentation/viewmodel/AttendViewModel.kt @@ -0,0 +1,144 @@ +package com.msg.gcms.presentation.viewmodel + +import androidx.compose.runtime.mutableStateOf +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.msg.gcms.domain.data.attend.GetClubAttendListResponseData +import com.msg.gcms.domain.data.attend.PatchAttendStatusCollectivelyRequestData +import com.msg.gcms.domain.data.attend.PatchAttendStatusRequestData +import com.msg.gcms.domain.data.attend.PostAttendListRequestData +import com.msg.gcms.domain.usecase.attend.GetClubAttendListUseCase +import com.msg.gcms.domain.usecase.attend.PatchAttendStatusCollectivelyUseCase +import com.msg.gcms.domain.usecase.attend.PatchAttendStatusUseCase +import com.msg.gcms.domain.usecase.attend.PostAttendListUseCase +import com.msg.gcms.domain.usecase.auth.SaveTokenInfoUseCase +import com.msg.gcms.presentation.viewmodel.util.Event +import com.msg.gcms.presentation.viewmodel.util.errorHandling +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.launch +import java.time.LocalDate +import javax.inject.Inject + +@HiltViewModel +class AttendViewModel @Inject constructor( + private val getClubAttendListUseCase: GetClubAttendListUseCase, + private val patchAttendStatusUseCase: PatchAttendStatusUseCase, + private val patchAttendStatusCollectivelyUseCase: PatchAttendStatusCollectivelyUseCase, + private val postClubAttendListUseCase: PostAttendListUseCase, + private val saveTokenInfoUseCase: SaveTokenInfoUseCase +) : ViewModel() { + + private val _attendList = MutableLiveData() + val attendList: LiveData get() = _attendList + + private val _getAttendListState = MutableLiveData() + val getAttendListState: LiveData get() = _getAttendListState + + private val _postAttendListState = MutableLiveData() + val postAttendListState: LiveData get() = _postAttendListState + + private val _patchAttendStatusState = MutableLiveData() + val patchAttendStatusState: LiveData get() = _patchAttendStatusState + + private val _patchAttendStatusCollectivelyState = MutableLiveData() + val patchAttendStatusCollectivelyState: LiveData get() = _patchAttendStatusCollectivelyState + + var postClubAttendData = mutableStateOf( + PostAttendListRequestData( + name = "", + date = LocalDate.now(), + periods = listOf() + ) + ) + private set + + var clubId = mutableStateOf(0) + private set + + var date = mutableStateOf(null) + private set + + var period = mutableStateOf(null) + private set + + var selectedStudentList = mutableListOf() + private set + + var selectedStudentName = mutableListOf() + private set + + var attendanceStatus = mutableStateOf("") + private set + + fun getClubAttendList() = viewModelScope.launch { + getClubAttendListUseCase( + clubId = clubId.value, + date = date.value, + period = period.value + ).onSuccess { + it.catch { remoteError -> + _getAttendListState.value = remoteError.errorHandling() + }.collect { response -> + _attendList.value = response + _getAttendListState.value = Event.Success + } + }.onFailure { error -> + _getAttendListState.value = error.errorHandling(unauthorizedAction = { saveTokenInfoUseCase() }) + } + } + + fun postClubAttendList() = viewModelScope.launch { + postClubAttendListUseCase( + body = postClubAttendData.value + ).onSuccess { + it.catch { remoteError -> + _postAttendListState.value = remoteError.errorHandling() + }.collect { + _postAttendListState.value = Event.Success + } + }.onFailure { error -> + _postAttendListState.value = error.errorHandling(unauthorizedAction = { saveTokenInfoUseCase() }) + } + } + + private fun patchAttendStatus() = viewModelScope.launch { + patchAttendStatusUseCase( + body = PatchAttendStatusRequestData( + attendanceId = selectedStudentList[0], + attendanceStatus = attendanceStatus.value + ) + ).onSuccess { + it.catch { remoteError -> + _patchAttendStatusState.value = remoteError.errorHandling() + }.collect { + _patchAttendStatusState.value = Event.Success + } + }.onFailure { error -> + _patchAttendStatusState.value = error.errorHandling(unauthorizedAction = { saveTokenInfoUseCase() }) + } + } + + private fun patchAttendStatusCollectively() = viewModelScope.launch { + patchAttendStatusCollectivelyUseCase( + body = PatchAttendStatusCollectivelyRequestData( + attendanceIds = selectedStudentList, + attendanceStatus = attendanceStatus.value + ) + ).onSuccess { + it.catch { remoteError -> + _patchAttendStatusCollectivelyState.value = remoteError.errorHandling() + }.collect { + _patchAttendStatusCollectivelyState.value = Event.Success + } + }.onFailure { error -> + _patchAttendStatusCollectivelyState.value = error.errorHandling(unauthorizedAction = { saveTokenInfoUseCase() }) + } + } + + fun useAttendFun() { + if (selectedStudentList.size > 1) { patchAttendStatusCollectively() } else { patchAttendStatus() } + } +} \ No newline at end of file From c0bc85c22a29d39c3030c80a5a2db48cf349bc6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=B0=AC=EC=9A=B0?= <105582057+wjdcksdn@users.noreply.github.com> Date: Mon, 29 Apr 2024 11:24:46 +0900 Subject: [PATCH 09/14] :memo: add Adapter --- .../adapter/attend/ClubAttendListAdapter.kt | 76 +++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 app/src/main/java/com/msg/gcms/presentation/adapter/attend/ClubAttendListAdapter.kt diff --git a/app/src/main/java/com/msg/gcms/presentation/adapter/attend/ClubAttendListAdapter.kt b/app/src/main/java/com/msg/gcms/presentation/adapter/attend/ClubAttendListAdapter.kt new file mode 100644 index 00000000..0ff9ab76 --- /dev/null +++ b/app/src/main/java/com/msg/gcms/presentation/adapter/attend/ClubAttendListAdapter.kt @@ -0,0 +1,76 @@ +package com.msg.gcms.presentation.adapter.attend + +import android.content.Context +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.core.content.ContextCompat +import androidx.recyclerview.widget.RecyclerView +import com.msg.gcms.R +import com.msg.gcms.databinding.ListMemberAttendBinding +import com.msg.gcms.domain.data.attend.GetClubAttendListResponseData + +class ClubAttendListAdapter(private val itemList: List) : + RecyclerView.Adapter() { + class ViewHolder(val binding: ListMemberAttendBinding, listener: OnItemClickListener) : RecyclerView.ViewHolder(binding.root) { + + fun bind(item: GetClubAttendListResponseData.User) { + val context = binding.userName.context + binding.member = item + binding.userClassTxt.text = "${item.grade}학년 ${item.classNum}반 ${item.number}번" + when (item.attendanceStatus) { + "ATTENDANCE" -> { + binding.userName.setTextColor(ContextCompat.getColor(context, R.color.dark_blue3)) + binding.attendState.setBackgroundResource(R.drawable.ic_attend_check) + } + "LATE" -> { + binding.userName.setTextColor(ContextCompat.getColor(context, R.color.yellow)) + binding.attendState.setBackgroundResource(R.drawable.ic_attend_late) + } + "REASONABLE_ABSENT" -> { + binding.userName.setTextColor(ContextCompat.getColor(context, R.color.gray_5)) + binding.attendState.setBackgroundResource(R.drawable.ic_attend_sick) + } + "ABSENT" -> { + binding.userName.setTextColor(ContextCompat.getColor(context, R.color.red_error)) + binding.attendState.setBackgroundResource(R.drawable.ic_attend_absent) + } + else -> { + binding.userName.setTextColor(ContextCompat.getColor(context, R.color.white)) + } + } + } + + init { + binding.checkAttendMember.setOnClickListener { + listener.select(adapterPosition, it.isSelected) + } + } + } + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + holder.bind(itemList[position]) + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + return ViewHolder( + ListMemberAttendBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false + ), + itemClickListener + ) + } + + override fun getItemCount(): Int = itemList.size + + interface OnItemClickListener { + fun select(position: Int, isChecked: Boolean) + } + + fun setItemOnClickListener(onItemClickListener: OnItemClickListener) { + this.itemClickListener = onItemClickListener + } + + private lateinit var itemClickListener: OnItemClickListener +} \ No newline at end of file From f3a920fbbe36bcbdfa4c7c7f508cc2fad1a0c741 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=B0=AC=EC=9A=B0?= <105582057+wjdcksdn@users.noreply.github.com> Date: Mon, 29 Apr 2024 11:24:59 +0900 Subject: [PATCH 10/14] :memo: add AttendModalBottomSheet --- .../view/attend/AttendModalBottomSheet.kt | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 app/src/main/java/com/msg/gcms/presentation/view/attend/AttendModalBottomSheet.kt diff --git a/app/src/main/java/com/msg/gcms/presentation/view/attend/AttendModalBottomSheet.kt b/app/src/main/java/com/msg/gcms/presentation/view/attend/AttendModalBottomSheet.kt new file mode 100644 index 00000000..4ba5d815 --- /dev/null +++ b/app/src/main/java/com/msg/gcms/presentation/view/attend/AttendModalBottomSheet.kt @@ -0,0 +1,71 @@ +package com.msg.gcms.presentation.view.attend + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.activityViewModels +import com.google.android.material.bottomsheet.BottomSheetDialogFragment +import com.msg.gcms.databinding.AttendDialogBinding +import com.msg.gcms.presentation.viewmodel.AttendViewModel + +class AttendModalBottomSheet : BottomSheetDialogFragment() { + lateinit var binding: AttendDialogBinding + private val viewModel by activityViewModels() + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + super.onCreateView(inflater, container, savedInstanceState) + binding = AttendDialogBinding.inflate(inflater, container, false) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + binding.attendStudentTv.text = viewModel.selectedStudentName.getListString() + binding.cancelBtn.setOnClickListener { + dismiss() + } + with(binding) { + attendBtn.setOnClickListener { it.onButtonClicked() } + lateBtn.setOnClickListener { it.onButtonClicked() } + absentBtn.setOnClickListener { it.onButtonClicked() } + sickBtn.setOnClickListener { it.onButtonClicked() } + } + } + + private fun View.onButtonClicked() { + when (this) { + binding.attendBtn -> { + viewModel.attendanceStatus.value = "ATTENDANCE" + } + binding.lateBtn -> { + viewModel.attendanceStatus.value = "LATE" + } + binding.absentBtn -> { + viewModel.attendanceStatus.value = "ABSENT" + } + binding.sickBtn -> { + viewModel.attendanceStatus.value = "REASONABLE_ABSENT" + } + } + viewModel.useAttendFun() + } + + private fun List.getListString(): String { + var returnString = "" + val loopNumbers = if (this.size >= 4) 4 else this.size + for (i in 0..loopNumbers) { + returnString += if (i == loopNumbers) this[i] else "${this[i]}, " + } + returnString += if (this.size > 4) "\n외 ${this.size - 4}명을 출석체크하시겠습니까?" else "\n님을 출석체크하시겠습니까?" + return returnString + } + + companion object { + const val TAG = "AttendModalBottomSheet" + } +} \ No newline at end of file From c948afb2555b2bcea4229529e17664a61e1c5453 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=B0=AC=EC=9A=B0?= <105582057+wjdcksdn@users.noreply.github.com> Date: Mon, 29 Apr 2024 11:25:08 +0900 Subject: [PATCH 11/14] :memo: add AttendActivity --- .../view/attend/AttendActivity.kt | 259 ++++++++++++++++++ 1 file changed, 259 insertions(+) create mode 100644 app/src/main/java/com/msg/gcms/presentation/view/attend/AttendActivity.kt diff --git a/app/src/main/java/com/msg/gcms/presentation/view/attend/AttendActivity.kt b/app/src/main/java/com/msg/gcms/presentation/view/attend/AttendActivity.kt new file mode 100644 index 00000000..472613f2 --- /dev/null +++ b/app/src/main/java/com/msg/gcms/presentation/view/attend/AttendActivity.kt @@ -0,0 +1,259 @@ +package com.msg.gcms.presentation.view.attend + +import android.animation.ObjectAnimator +import android.app.Activity +import android.content.Intent +import android.view.View +import androidx.activity.viewModels +import com.msg.gcms.R +import com.msg.gcms.databinding.ActivityAttendBinding +import com.msg.gcms.presentation.adapter.attend.ClubAttendListAdapter +import com.msg.gcms.presentation.base.BaseActivity +import com.msg.gcms.presentation.base.BaseModal +import com.msg.gcms.presentation.utils.enterActivity +import com.msg.gcms.presentation.view.intro.IntroActivity +import com.msg.gcms.presentation.viewmodel.util.Event +import com.msg.gcms.presentation.viewmodel.AttendViewModel +import dagger.hilt.android.AndroidEntryPoint + +@AndroidEntryPoint +class AttendActivity : BaseActivity(R.layout.activity_attend) { + + private val viewModel by viewModels() + private lateinit var clubAttendListAdapter: ClubAttendListAdapter + + override fun observeEvent() { + observeAttendList() + observeAttendState() + observePatchAttendStatusState() + observePatchAttendStatusCollectivelyState() + } + + private fun observeAttendList() { + viewModel.attendList.observe(this) { + settingClubAttendListAdapter() + } + } + + private fun observeAttendState() { + viewModel.getAttendListState.observe(this) { + when (it) { + Event.Success -> {} + Event.Unauthorized -> { + BaseModal( + title = "오류", + msg = "토큰이 만료되었습니다, 로그아웃 후 다시 로그인해주세요.", + context = this + ).let { dialog -> + dialog.show() + dialog.dialogBinding.ok.setOnClickListener { + enterActivity(this, IntroActivity()) + dialog.dismiss() + } + } + } + Event.ForBidden -> { + BaseModal( + title = "실패", + msg = "동아리 구성원만이 출석 정보를 확인할 수 있습니다", + context = this + ).show() + } + Event.NotFound -> { + BaseModal( + title = "오류", + msg = "동아리를 찾을 수 없습니다.", + context = this + ).show() + } + Event.Server -> { + BaseModal( + title = "오류", + msg = "서버에서 문제가 발생하였습니다.", + context = this + ) + } + else -> { + BaseModal( + title = "오류", + msg = "알 수 없는 오류 발생, 개발자에게 문의해주세요.", + context = this + ).show() + } + } + } + } + + private fun observePatchAttendStatusState() { + viewModel.patchAttendStatusState.observe(this) { + when (it) { + Event.Success -> {} + Event.Unauthorized -> { + BaseModal( + title = "오류", + msg = "토큰이 만료되었습니다, 로그아웃 후 다시 로그인해주세요.", + context = this + ).let { dialog -> + dialog.show() + dialog.dialogBinding.ok.setOnClickListener { + enterActivity(this, IntroActivity()) + dialog.dismiss() + } + } + } + Event.ForBidden -> { + BaseModal( + title = "실패", + msg = "동아리 부장만이 출석 상태를 변경할 수 있습니다", + context = this + ).show() + } + Event.NotFound -> { + BaseModal( + title = "오류", + msg = "동아리를 찾을 수 없습니다.", + context = this + ).show() + } + Event.Server -> { + BaseModal( + title = "오류", + msg = "서버에서 문제가 발생하였습니다.", + context = this + ) + } + else -> { + BaseModal( + title = "오류", + msg = "알 수 없는 오류 발생, 개발자에게 문의해주세요.", + context = this + ).show() + } + } + } + } + + private fun observePatchAttendStatusCollectivelyState() { + viewModel.patchAttendStatusCollectivelyState.observe(this) { + when (it) { + Event.Success -> {} + Event.Unauthorized -> { + BaseModal( + title = "오류", + msg = "토큰이 만료되었습니다, 로그아웃 후 다시 로그인해주세요.", + context = this + ).let { dialog -> + dialog.show() + dialog.dialogBinding.ok.setOnClickListener { + enterActivity(this, IntroActivity()) + dialog.dismiss() + } + } + } + Event.ForBidden -> { + BaseModal( + title = "실패", + msg = "동아리 부장만이 출석 상태를 변경할 수 있습니다", + context = this + ).show() + } + Event.NotFound -> { + BaseModal( + title = "오류", + msg = "동아리를 찾을 수 없습니다.", + context = this + ).show() + } + Event.Server -> { + BaseModal( + title = "오류", + msg = "서버에서 문제가 발생하였습니다.", + context = this + ) + } + else -> { + BaseModal( + title = "오류", + msg = "알 수 없는 오류 발생, 개발자에게 문의해주세요.", + context = this + ).show() + } + } + } + } + + override fun viewSetting() { + viewModel.clubId.value = intent.getLongExtra("clubId", 0) + onClickExpandBtn() + onClickBackBtn() + onClickAttendBtn() + onClickSettingBtn() + } + + private fun onClickExpandBtn() = with(binding) { + listOf(memberAttendListBtn, attendMemberListLayout).forEach { + it.setOnClickListener { + if (memberAttendListBtn.rotation == 0F || memberAttendListBtn.rotation == -90F) { + if (attendMemberList.visibility == View.GONE) viewModel.getClubAttendList() + expandableAnim(memberAttendListBtn, attendMemberList.visibility, attendMemberList) + } + } + } + } + + private fun expandableAnim(view: View, visibility: Int, visibleView: View) { + if (visibility == View.GONE) visibleView.visibility = View.VISIBLE + else visibleView.visibility = View.GONE + ObjectAnimator.ofFloat( + view, + View.ROTATION, + view.rotation, + view.rotation + if (visibility == View.GONE) -90 else 90 + ).setDuration(300).start() + } + + private fun onClickBackBtn() { + binding.goBackBtn.setOnClickListener { + setResult( + Activity.RESULT_OK, + Intent().putExtra("clubId", viewModel.clubId.value) + ) + finish() + } + } + + private fun onClickAttendBtn() { + if (viewModel.selectedStudentList.size == 0) { + shortToast("학생을 한명이상 선택해 주세요!") + return + } + val modal = AttendModalBottomSheet() + binding.attendBtn.setOnClickListener { + modal.show(supportFragmentManager, AttendModalBottomSheet.TAG) + } + } + + private fun settingClubAttendListAdapter() { + clubAttendListAdapter = ClubAttendListAdapter( + itemList = viewModel.attendList.value!!.users + ) + clubAttendListAdapter.setItemOnClickListener(object : ClubAttendListAdapter.OnItemClickListener { + override fun select(position: Int, isChecked: Boolean) { + if (isChecked) { + viewModel.selectedStudentList.add(viewModel.attendList.value!!.users[position].attendanceId) + viewModel.selectedStudentName.add(viewModel.attendList.value!!.users[position].name) + } else { + viewModel.selectedStudentList.remove(viewModel.attendList.value!!.users[position].attendanceId) + viewModel.selectedStudentName.remove(viewModel.attendList.value!!.users[position].name) + } + } + } + ) + } + + private fun onClickSettingBtn() { + binding.schoolPeriod.setOnClickListener { + longToast("열심히 개발중인 기능이에요! 잠시만 기다려 주세요!") + } + } +} \ No newline at end of file From 02116bfc5115c7fb2da91c401b7b00e41684ceca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=B0=AC=EC=9A=B0?= <105582057+wjdcksdn@users.noreply.github.com> Date: Mon, 29 Apr 2024 12:18:43 +0900 Subject: [PATCH 12/14] :memo: add default name of Attendance name --- .../java/com/msg/gcms/presentation/viewmodel/AttendViewModel.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/msg/gcms/presentation/viewmodel/AttendViewModel.kt b/app/src/main/java/com/msg/gcms/presentation/viewmodel/AttendViewModel.kt index 08730c65..a370b280 100644 --- a/app/src/main/java/com/msg/gcms/presentation/viewmodel/AttendViewModel.kt +++ b/app/src/main/java/com/msg/gcms/presentation/viewmodel/AttendViewModel.kt @@ -48,7 +48,7 @@ class AttendViewModel @Inject constructor( var postClubAttendData = mutableStateOf( PostAttendListRequestData( - name = "", + name = "attend", date = LocalDate.now(), periods = listOf() ) From 816ec2f420cbbb78b81807d993eb9a3e4c9b086d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=B0=AC=EC=9A=B0?= <105582057+wjdcksdn@users.noreply.github.com> Date: Thu, 2 May 2024 16:28:25 +0900 Subject: [PATCH 13/14] :memo: add AttendActivity to AndroidManifest --- app/src/main/AndroidManifest.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ce8a074c..09e2ae83 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -49,6 +49,9 @@ + Date: Fri, 3 May 2024 10:23:40 +0900 Subject: [PATCH 14/14] :memo: apply changed data type --- .../remote/dto/attend/response/GetClubAttendListResponse.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/src/main/java/com/msg/gcms/data/remote/dto/attend/response/GetClubAttendListResponse.kt b/app/src/main/java/com/msg/gcms/data/remote/dto/attend/response/GetClubAttendListResponse.kt index c563393d..e862497d 100644 --- a/app/src/main/java/com/msg/gcms/data/remote/dto/attend/response/GetClubAttendListResponse.kt +++ b/app/src/main/java/com/msg/gcms/data/remote/dto/attend/response/GetClubAttendListResponse.kt @@ -4,14 +4,13 @@ import com.google.gson.annotations.SerializedName import com.msg.gcms.domain.data.attend.GetClubAttendListResponseData import com.msg.gcms.domain.data.attend.GetClubAttendListResponseData.User as DomainUser import java.time.LocalDate -import java.time.LocalTime import java.util.UUID data class GetClubAttendListResponse( @SerializedName("date") val date: LocalDate, @SerializedName("period") - val period: LocalTime, + val period: String, @SerializedName("users") val users: List ) {