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 @@
+
+ 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/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
) {
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,
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
)
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
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
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
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) {
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..a370b280
--- /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 = "attend",
+ 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
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_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.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_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.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_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.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/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 @@
+
+
+
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 @@
+
+
+
+
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/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 @@
+
+
+
+
+
-
+
+
+
+
+
+
+
+
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