diff --git a/AOS/.idea/gradle.xml b/AOS/.idea/gradle.xml index a2d7c213..ae388c2a 100644 --- a/AOS/.idea/gradle.xml +++ b/AOS/.idea/gradle.xml @@ -7,6 +7,7 @@ + diff --git a/AOS/.idea/misc.xml b/AOS/.idea/misc.xml index 773fe0fb..8978d23d 100644 --- a/AOS/.idea/misc.xml +++ b/AOS/.idea/misc.xml @@ -1,6 +1,6 @@ - + diff --git a/AOS/app/build.gradle b/AOS/app/build.gradle index 7a1abc8f..9b429041 100644 --- a/AOS/app/build.gradle +++ b/AOS/app/build.gradle @@ -2,6 +2,9 @@ plugins { id 'com.android.application' id 'org.jetbrains.kotlin.android' id "org.jlleitschuh.gradle.ktlint" version '11.6.1' + id 'org.jetbrains.kotlin.plugin.serialization' version '1.7.20' + id 'kotlin-kapt' + id 'com.google.dagger.hilt.android' } android { @@ -77,4 +80,27 @@ dependencies { debugImplementation 'androidx.compose.ui:ui-tooling' debugImplementation 'androidx.compose.ui:ui-test-manifest' implementation "androidx.navigation:navigation-compose:$nav_version" + implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.4.1" + + // Hilt Dependency + implementation "com.google.dagger:hilt-android:2.44" + kapt "com.google.dagger:hilt-compiler:2.44" + implementation 'androidx.hilt:hilt-navigation-compose:1.0.0' + implementation 'androidx.hilt:hilt-navigation-fragment:1.0.0' + + // Retrofit Dependency + implementation 'com.squareup.retrofit2:retrofit:2.9.0' + implementation 'com.squareup.retrofit2:converter-gson:2.9.0' + + // define a BOM and its version + implementation(platform("com.squareup.okhttp3:okhttp-bom:4.11.0")) + + // define any required OkHttp artifacts without version + implementation("com.squareup.okhttp3:okhttp") + implementation("com.squareup.okhttp3:logging-interceptor") +} + +// Allow references to generated code +kapt { + correctErrorTypes true } \ No newline at end of file diff --git a/AOS/app/src/main/AndroidManifest.xml b/AOS/app/src/main/AndroidManifest.xml index e1798b87..a6d88366 100644 --- a/AOS/app/src/main/AndroidManifest.xml +++ b/AOS/app/src/main/AndroidManifest.xml @@ -2,7 +2,10 @@ + + + + + + + + + + diff --git a/AOS/app/src/main/java/com/example/eeos/EEOSApplication.kt b/AOS/app/src/main/java/com/example/eeos/EEOSApplication.kt new file mode 100644 index 00000000..0b56f5b3 --- /dev/null +++ b/AOS/app/src/main/java/com/example/eeos/EEOSApplication.kt @@ -0,0 +1,7 @@ +package com.example.eeos + +import android.app.Application +import dagger.hilt.android.HiltAndroidApp + +@HiltAndroidApp +class EEOSApplication : Application() diff --git a/AOS/app/src/main/java/com/example/eeos/MainActivity.kt b/AOS/app/src/main/java/com/example/eeos/MainActivity.kt index b3b7b431..1713439b 100644 --- a/AOS/app/src/main/java/com/example/eeos/MainActivity.kt +++ b/AOS/app/src/main/java/com/example/eeos/MainActivity.kt @@ -6,9 +6,11 @@ import androidx.activity.compose.setContent import androidx.compose.runtime.Composable import androidx.compose.ui.tooling.preview.Preview import com.example.eeos.navigation.EEOSNavGraph -import com.example.eeos.ui.home.ProgramLists -import com.example.eeos.ui.theme.EeosTheme +import com.example.eeos.presentation.home.ProgramLists +import com.example.eeos.presentation.theme.EeosTheme +import dagger.hilt.android.AndroidEntryPoint +@AndroidEntryPoint class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -25,8 +27,11 @@ class MainActivity : ComponentActivity() { fun GreetingPreview() { EeosTheme { ProgramLists( + loading = false, + empty = false, programLists = listOf(), - onProgramClick = {} + onProgramClick = {}, + loadMorePrograms = {} ) } } diff --git a/AOS/app/src/main/java/com/example/eeos/consts/Consts.kt b/AOS/app/src/main/java/com/example/eeos/consts/Consts.kt new file mode 100644 index 00000000..1e211028 --- /dev/null +++ b/AOS/app/src/main/java/com/example/eeos/consts/Consts.kt @@ -0,0 +1,62 @@ +package com.example.eeos.consts + +val category = listOf("all", "weekly", "president", "event", " etc") +val programStatus = listOf("active", "end") + +val categoryChips: List = listOf( + "전체", + "주간 발표", + "회장단", + "행사부", + "기타 행사" +) +val programStatusChips: List = listOf( + "진행 중", + "완료" +) + +object ActiveStatus { + const val am = "am" + const val rm = "rm" + const val cm = "cm" + const val ob = "ob" +} + +object Category { + const val weekly = "weekly" + const val president = "president" + const val event = "event" + const val etc = "etc" +} + +object ProgramStatus { + const val active = "active" + const val end = "end" +} + +object ProgramType { + const val demand = " demand" + const val notification = "notification" +} + +object AttendStatus { + const val attend = "attend" + const val absent = "absent" + const val perceive = "perceive" + const val nonResponse = "nonResponse" + const val nonRelated = "nonRelated" +} + +val attendStatusMap = mapOf( + "attend" to "참석", + "absent" to "불참", + "perceive" to "지각", + "nonResponse" to "미정" +) + +object MemberStatus { + const val AM = "AM" + const val RM = "RM" + const val CM = "CM" + const val OB = "OB" +} diff --git a/AOS/app/src/main/java/com/example/eeos/data/model/remote/request/RequestPutActiveStatusDto.kt b/AOS/app/src/main/java/com/example/eeos/data/model/remote/request/RequestPutActiveStatusDto.kt new file mode 100644 index 00000000..1c49e006 --- /dev/null +++ b/AOS/app/src/main/java/com/example/eeos/data/model/remote/request/RequestPutActiveStatusDto.kt @@ -0,0 +1,12 @@ +package com.example.eeos.data.model.remote.request + +import kotlinx.serialization.Contextual +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class RequestPutActiveStatusDto( + @Contextual + @SerialName("activeStatus") + val activeStatus: String +) diff --git a/AOS/app/src/main/java/com/example/eeos/data/model/remote/request/RequestPutAttendStatusDto.kt b/AOS/app/src/main/java/com/example/eeos/data/model/remote/request/RequestPutAttendStatusDto.kt new file mode 100644 index 00000000..8f7bd7a2 --- /dev/null +++ b/AOS/app/src/main/java/com/example/eeos/data/model/remote/request/RequestPutAttendStatusDto.kt @@ -0,0 +1,15 @@ +package com.example.eeos.data.model.remote.request + +import kotlinx.serialization.Contextual +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class RequestPutAttendStatusDto( + @Contextual + @SerialName("beforeAttendStatus") + val beforeAttendStatus: String, + @Contextual + @SerialName("afterAttendStatus") + val afterAttendStatus: String +) diff --git a/AOS/app/src/main/java/com/example/eeos/data/model/remote/response/ResponseGetActiveStatusDto.kt b/AOS/app/src/main/java/com/example/eeos/data/model/remote/response/ResponseGetActiveStatusDto.kt new file mode 100644 index 00000000..27546577 --- /dev/null +++ b/AOS/app/src/main/java/com/example/eeos/data/model/remote/response/ResponseGetActiveStatusDto.kt @@ -0,0 +1,27 @@ +package com.example.eeos.data.model.remote.response + +import com.example.eeos.domain.model.ActiveStatus +import kotlinx.serialization.Contextual +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class ResponseGetActiveStatusDto( + @SerialName("name") + val name: String, + @Contextual + @SerialName("activeStatus") + val activeStatus: String +) { + fun toActiveStatus(): ActiveStatus = + ActiveStatus( + name = name, + activeStatus = when (activeStatus) { + "am" -> "AM" + "cm" -> "CM" + "rm" -> "RM" + "ob" -> "OB" + else -> "" + } + ) +} diff --git a/AOS/app/src/main/java/com/example/eeos/data/model/remote/response/ResponseGetAttendStatusDto.kt b/AOS/app/src/main/java/com/example/eeos/data/model/remote/response/ResponseGetAttendStatusDto.kt new file mode 100644 index 00000000..d020808b --- /dev/null +++ b/AOS/app/src/main/java/com/example/eeos/data/model/remote/response/ResponseGetAttendStatusDto.kt @@ -0,0 +1,21 @@ +package com.example.eeos.data.model.remote.response + +import com.example.eeos.domain.model.AttendStatus +import kotlinx.serialization.Contextual +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class ResponseGetAttendStatusDto( + @SerialName("name") + val name: String, + @Contextual + @SerialName("activeStatus") + val attendStatus: String +) { + fun toAttendStatus(): AttendStatus = + AttendStatus( + name = name, + attendStatus = attendStatus + ) +} diff --git a/AOS/app/src/main/java/com/example/eeos/data/model/remote/response/ResponseGetMemberListDto.kt b/AOS/app/src/main/java/com/example/eeos/data/model/remote/response/ResponseGetMemberListDto.kt new file mode 100644 index 00000000..980ac5da --- /dev/null +++ b/AOS/app/src/main/java/com/example/eeos/data/model/remote/response/ResponseGetMemberListDto.kt @@ -0,0 +1,30 @@ +package com.example.eeos.data.model.remote.response + +import com.example.eeos.domain.model.Member +import kotlinx.serialization.Contextual +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class ResponseGetMemberListDto( + @SerialName("members") + val members: List +) { + @Serializable + data class GetMemberDto( + @SerialName("memberId") + val memberId: Int, + @SerialName("name") + val name: String, + @Contextual + @SerialName("attendStatus") + val attendStatus: String + ) + + fun toMemberList(): List = members.map { member -> + Member( + name = member.name, + attendStatus = member.attendStatus + ) + } +} diff --git a/AOS/app/src/main/java/com/example/eeos/data/model/remote/response/ResponseGetProgramDetailDto.kt b/AOS/app/src/main/java/com/example/eeos/data/model/remote/response/ResponseGetProgramDetailDto.kt new file mode 100644 index 00000000..460f35a8 --- /dev/null +++ b/AOS/app/src/main/java/com/example/eeos/data/model/remote/response/ResponseGetProgramDetailDto.kt @@ -0,0 +1,35 @@ +package com.example.eeos.data.model.remote.response + +import com.example.eeos.domain.model.ProgramDetail +import kotlinx.serialization.Contextual +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class ResponseGetProgramDetailDto( + @SerialName("programId") + val programId: Int, + @SerialName("title") + val title: String, + @SerialName("deadLine") + val deadLine: String, + @SerialName("content") + val content: String, + @Contextual + @SerialName("category") + val category: String, + @SerialName("programStatus") + @Contextual + val programStatus: String, + @Contextual + @SerialName("type") + val type: String +) { + fun toProgramDetail(): ProgramDetail = ProgramDetail( + title = title, + deadLine = deadLine, + content = content, + category = category, + type = type + ) +} diff --git a/AOS/app/src/main/java/com/example/eeos/data/model/remote/response/ResponseGetProgramListDto.kt b/AOS/app/src/main/java/com/example/eeos/data/model/remote/response/ResponseGetProgramListDto.kt new file mode 100644 index 00000000..b2041108 --- /dev/null +++ b/AOS/app/src/main/java/com/example/eeos/data/model/remote/response/ResponseGetProgramListDto.kt @@ -0,0 +1,48 @@ +package com.example.eeos.data.model.remote.response + +import com.example.eeos.domain.model.Program +import kotlinx.serialization.Contextual +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class ResponseGetProgramListDto( + @SerialName("size") + val size: Int, + @SerialName("page") + val page: Int, + @SerialName("totalPage") + val totalPage: Int, + @SerialName("programs") + val programs: List +) { + @Serializable + data class GetProgramDto( + @SerialName("programId") + val programId: Int, + @SerialName("title") + val title: String, + @SerialName("deadLine") + val deadLine: String, + @Contextual + @SerialName("category") + val category: String, + @Contextual + @SerialName("programStatus") + val programStatus: String, + @Contextual + @SerialName("type") + val type: String + ) + + fun toProgramList(): List = programs.map { program -> + Program( + programId = program.programId, + title = program.title, + deadLine = program.deadLine, + category = program.category, + programStatus = program.programStatus, + type = program.type + ) + } +} diff --git a/AOS/app/src/main/java/com/example/eeos/data/model/remote/response/ResponsePutActiveStatusDto.kt b/AOS/app/src/main/java/com/example/eeos/data/model/remote/response/ResponsePutActiveStatusDto.kt new file mode 100644 index 00000000..1a54ac9b --- /dev/null +++ b/AOS/app/src/main/java/com/example/eeos/data/model/remote/response/ResponsePutActiveStatusDto.kt @@ -0,0 +1,15 @@ +package com.example.eeos.data.model.remote.response + +import com.example.eeos.consts.ActiveStatus +import kotlinx.serialization.Contextual +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class ResponsePutActiveStatusDto( + @SerialName("name") + val name: String, + @Contextual + @SerialName("activeStatus") + val activeStatus: ActiveStatus +) diff --git a/AOS/app/src/main/java/com/example/eeos/data/model/remote/response/ResponsePutAttendStatusDto.kt b/AOS/app/src/main/java/com/example/eeos/data/model/remote/response/ResponsePutAttendStatusDto.kt new file mode 100644 index 00000000..8972018a --- /dev/null +++ b/AOS/app/src/main/java/com/example/eeos/data/model/remote/response/ResponsePutAttendStatusDto.kt @@ -0,0 +1,15 @@ +package com.example.eeos.data.model.remote.response + +import com.example.eeos.consts.AttendStatus +import kotlinx.serialization.Contextual +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class ResponsePutAttendStatusDto( + @SerialName("name") + val name: String, + @Contextual + @SerialName("activeStatus") + val attendStatus: AttendStatus +) diff --git a/AOS/app/src/main/java/com/example/eeos/data/model/remote/response/base/BaseErrorResponse.kt b/AOS/app/src/main/java/com/example/eeos/data/model/remote/response/base/BaseErrorResponse.kt new file mode 100644 index 00000000..e2823b05 --- /dev/null +++ b/AOS/app/src/main/java/com/example/eeos/data/model/remote/response/base/BaseErrorResponse.kt @@ -0,0 +1,14 @@ +package com.example.eeos.data.model.remote.response.base + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class BaseErrorResponse( + @SerialName("status") + val status: String, + @SerialName("code") + val code: Int, + @SerialName("message") + val message: String +) diff --git a/AOS/app/src/main/java/com/example/eeos/data/model/remote/response/base/BaseResponse.kt b/AOS/app/src/main/java/com/example/eeos/data/model/remote/response/base/BaseResponse.kt new file mode 100644 index 00000000..cb9a3d4d --- /dev/null +++ b/AOS/app/src/main/java/com/example/eeos/data/model/remote/response/base/BaseResponse.kt @@ -0,0 +1,14 @@ +package com.example.eeos.data.model.remote.response.base + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class BaseResponse( + @SerialName("status") + val status: String, + @SerialName("message") + val message: String, + @SerialName("data") + val data: T? +) diff --git a/AOS/app/src/main/java/com/example/eeos/data/repository/InfoRepositoryImpl.kt b/AOS/app/src/main/java/com/example/eeos/data/repository/InfoRepositoryImpl.kt new file mode 100644 index 00000000..d5855020 --- /dev/null +++ b/AOS/app/src/main/java/com/example/eeos/data/repository/InfoRepositoryImpl.kt @@ -0,0 +1,21 @@ +package com.example.eeos.data.repository + +import com.example.eeos.data.model.remote.request.RequestPutActiveStatusDto +import com.example.eeos.data.source.InfoDataSource +import com.example.eeos.domain.model.ActiveStatus +import com.example.eeos.domain.repository.InfoRepository +import javax.inject.Inject + +class InfoRepositoryImpl @Inject constructor( + private val infoDataSource: InfoDataSource +) : InfoRepository { + override suspend fun getActiveStatus(): Result = + runCatching { + infoDataSource.getActiveStatus().data!!.toActiveStatus() + } + + override suspend fun putActiveStatus(requestPutActiveStatusDto: RequestPutActiveStatusDto): Result = + runCatching { + infoDataSource.putActiveStatus(requestPutActiveStatusDto).data + } +} diff --git a/AOS/app/src/main/java/com/example/eeos/data/repository/ProgramRepositoryImpl.kt b/AOS/app/src/main/java/com/example/eeos/data/repository/ProgramRepositoryImpl.kt new file mode 100644 index 00000000..c660fc2b --- /dev/null +++ b/AOS/app/src/main/java/com/example/eeos/data/repository/ProgramRepositoryImpl.kt @@ -0,0 +1,64 @@ +package com.example.eeos.data.repository + +import com.example.eeos.data.model.remote.request.RequestPutAttendStatusDto +import com.example.eeos.data.source.ProgramDataSource +import com.example.eeos.domain.model.Member +import com.example.eeos.domain.model.Program +import com.example.eeos.domain.model.ProgramDetail +import com.example.eeos.domain.repository.ProgramRepository +import javax.inject.Inject + +class ProgramRepositoryImpl @Inject constructor( + private val programDataSource: ProgramDataSource +) : ProgramRepository { + override suspend fun getProgramDetail(programId: Int): Result = + runCatching { + programDataSource.getProgramDetail(programId).data!!.toProgramDetail() + } + + override suspend fun getProgramList( + category: String, + programStatus: String, + size: Int, + page: Int + ): Result> = + runCatching { + programDataSource.getProgramLists( + category = category, + programStatus = programStatus, + size = size, + page = page + ).data!!.toProgramList() + } + + override suspend fun putAttendStatus( + programId: Int, + requestPutAttendStatusDto: RequestPutAttendStatusDto + ): Result = + runCatching { + programDataSource.putAttendStatus( + programId = programId, + requestPutAttendStatusDto = requestPutAttendStatusDto + ).data + } + + override suspend fun getAttendStatus( + programId: Int + ): Result = + runCatching { + programDataSource.getAttendStatus( + programId = programId + ).data!!.toAttendStatus() + } + + override suspend fun getMemberList( + programId: Int, + attendStatus: String + ): Result> = + runCatching { + programDataSource.getMemberList( + programId = programId, + attendStatus = attendStatus + ).data!!.toMemberList() + } +} diff --git a/AOS/app/src/main/java/com/example/eeos/data/service/AuthService.kt b/AOS/app/src/main/java/com/example/eeos/data/service/AuthService.kt new file mode 100644 index 00000000..c7175b5c --- /dev/null +++ b/AOS/app/src/main/java/com/example/eeos/data/service/AuthService.kt @@ -0,0 +1,3 @@ +package com.example.eeos.data.service + +interface AuthService diff --git a/AOS/app/src/main/java/com/example/eeos/data/service/InfoService.kt b/AOS/app/src/main/java/com/example/eeos/data/service/InfoService.kt new file mode 100644 index 00000000..7a7c794e --- /dev/null +++ b/AOS/app/src/main/java/com/example/eeos/data/service/InfoService.kt @@ -0,0 +1,19 @@ +package com.example.eeos.data.service + +import com.example.eeos.data.model.remote.request.RequestPutActiveStatusDto +import com.example.eeos.data.model.remote.response.ResponseGetActiveStatusDto +import com.example.eeos.data.model.remote.response.ResponsePutActiveStatusDto +import com.example.eeos.data.model.remote.response.base.BaseResponse +import retrofit2.http.Body +import retrofit2.http.GET +import retrofit2.http.PUT + +interface InfoService { + @GET("members/activeStatus") + suspend fun getActiveStatus(): BaseResponse + + @PUT("members/activeStatus") + suspend fun putActiveStatus( + @Body requestPutActiveStatusDto: RequestPutActiveStatusDto + ): BaseResponse +} diff --git a/AOS/app/src/main/java/com/example/eeos/data/service/ProgramService.kt b/AOS/app/src/main/java/com/example/eeos/data/service/ProgramService.kt new file mode 100644 index 00000000..52ecf257 --- /dev/null +++ b/AOS/app/src/main/java/com/example/eeos/data/service/ProgramService.kt @@ -0,0 +1,46 @@ +package com.example.eeos.data.service + +import com.example.eeos.data.model.remote.request.RequestPutAttendStatusDto +import com.example.eeos.data.model.remote.response.ResponseGetAttendStatusDto +import com.example.eeos.data.model.remote.response.ResponseGetMemberListDto +import com.example.eeos.data.model.remote.response.ResponseGetProgramDetailDto +import com.example.eeos.data.model.remote.response.ResponseGetProgramListDto +import com.example.eeos.data.model.remote.response.ResponsePutAttendStatusDto +import com.example.eeos.data.model.remote.response.base.BaseResponse +import retrofit2.http.Body +import retrofit2.http.GET +import retrofit2.http.PUT +import retrofit2.http.Path +import retrofit2.http.Query + +interface ProgramService { + @GET("programs/{programId}") + suspend fun getProgramDetail( + @Path(value = "programId") programId: Int + ): BaseResponse + + @GET("programs") + suspend fun getProgramList( + @Query("category") category: String, + @Query("programStatus") programStatus: String, + @Query("size") size: Int, + @Query("page") page: Int + ): BaseResponse + + @PUT("programs/{programId}/members/attendStatus") + suspend fun putAttendStatus( + @Path(value = "programId") programId: Int, + @Body requestPutAttendStatusDto: RequestPutAttendStatusDto + ): BaseResponse + + @GET("programs/{programId}/members/attendStatus") + suspend fun getAttendStatus( + @Path(value = "programId") programId: Int, + ): BaseResponse + + @GET("programs/{programId}/members") + suspend fun getMemberList( + @Path(value = "programId") programId: Int, + @Query("attendStatus") attendStatus: String + ): BaseResponse +} diff --git a/AOS/app/src/main/java/com/example/eeos/data/source/InfoDataSource.kt b/AOS/app/src/main/java/com/example/eeos/data/source/InfoDataSource.kt new file mode 100644 index 00000000..3b67d20e --- /dev/null +++ b/AOS/app/src/main/java/com/example/eeos/data/source/InfoDataSource.kt @@ -0,0 +1,21 @@ +package com.example.eeos.data.source + +import com.example.eeos.data.model.remote.request.RequestPutActiveStatusDto +import com.example.eeos.data.model.remote.response.ResponseGetActiveStatusDto +import com.example.eeos.data.model.remote.response.ResponsePutActiveStatusDto +import com.example.eeos.data.model.remote.response.base.BaseResponse +import com.example.eeos.data.service.InfoService +import javax.inject.Inject + +class InfoDataSource @Inject constructor( + private val infoService: InfoService +) { + suspend fun getActiveStatus(): BaseResponse = + infoService.getActiveStatus() + + suspend fun putActiveStatus( + requestPutActiveStatusDto: RequestPutActiveStatusDto + ): BaseResponse = infoService.putActiveStatus( + requestPutActiveStatusDto + ) +} diff --git a/AOS/app/src/main/java/com/example/eeos/data/source/ProgramDataSource.kt b/AOS/app/src/main/java/com/example/eeos/data/source/ProgramDataSource.kt new file mode 100644 index 00000000..c6274061 --- /dev/null +++ b/AOS/app/src/main/java/com/example/eeos/data/source/ProgramDataSource.kt @@ -0,0 +1,58 @@ +package com.example.eeos.data.source + +import com.example.eeos.data.model.remote.request.RequestPutAttendStatusDto +import com.example.eeos.data.model.remote.response.ResponseGetAttendStatusDto +import com.example.eeos.data.model.remote.response.ResponseGetMemberListDto +import com.example.eeos.data.model.remote.response.ResponseGetProgramDetailDto +import com.example.eeos.data.model.remote.response.ResponseGetProgramListDto +import com.example.eeos.data.model.remote.response.ResponsePutAttendStatusDto +import com.example.eeos.data.model.remote.response.base.BaseResponse +import com.example.eeos.data.service.ProgramService +import javax.inject.Inject + +class ProgramDataSource @Inject constructor( + private val programService: ProgramService +) { + suspend fun getProgramDetail( + programId: Int + ): BaseResponse = + programService.getProgramDetail(programId = programId) + + suspend fun getProgramLists( + category: String, + programStatus: String, + size: Int, + page: Int + ): BaseResponse = + programService.getProgramList( + category = category, + programStatus = programStatus, + size = size, + page = page + ) + + suspend fun putAttendStatus( + programId: Int, + requestPutAttendStatusDto: RequestPutAttendStatusDto + ): BaseResponse = + programService.putAttendStatus( + programId = programId, + requestPutAttendStatusDto = requestPutAttendStatusDto + ) + + suspend fun getAttendStatus( + programId: Int + ): BaseResponse = + programService.getAttendStatus( + programId = programId + ) + + suspend fun getMemberList( + programId: Int, + attendStatus: String + ): BaseResponse = + programService.getMemberList( + programId = programId, + attendStatus = attendStatus + ) +} diff --git a/AOS/app/src/main/java/com/example/eeos/di/RepositoryModule.kt b/AOS/app/src/main/java/com/example/eeos/di/RepositoryModule.kt new file mode 100644 index 00000000..34e9d0b6 --- /dev/null +++ b/AOS/app/src/main/java/com/example/eeos/di/RepositoryModule.kt @@ -0,0 +1,31 @@ +package com.example.eeos.di + +import com.example.eeos.data.repository.InfoRepositoryImpl +import com.example.eeos.data.repository.ProgramRepositoryImpl +import com.example.eeos.domain.repository.InfoRepository +import com.example.eeos.domain.repository.ProgramRepository +import dagger.Binds +import dagger.Module +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton + +/** +* 인터페이스 레포지토리와 구현체 레포지토리 결합 +* */ + +@Module +@InstallIn(SingletonComponent::class) +abstract class RepositoryModule { + @Singleton + @Binds + abstract fun bindsInfoRepository( + infoRepository: InfoRepositoryImpl + ): InfoRepository + + @Singleton + @Binds + abstract fun bindsProgramRepository( + programRepository: ProgramRepositoryImpl + ): ProgramRepository +} diff --git a/AOS/app/src/main/java/com/example/eeos/di/RetrofitModule.kt b/AOS/app/src/main/java/com/example/eeos/di/RetrofitModule.kt new file mode 100644 index 00000000..bebf1783 --- /dev/null +++ b/AOS/app/src/main/java/com/example/eeos/di/RetrofitModule.kt @@ -0,0 +1,47 @@ +package com.example.eeos.di + +import com.google.gson.GsonBuilder +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton +import okhttp3.Interceptor +import okhttp3.OkHttpClient +import okhttp3.logging.HttpLoggingInterceptor +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory + +@Module +@InstallIn(SingletonComponent::class) +object RetrofitModule { + private const val MOCK_URL = "https://562ee14c-9738-4e79-ba16-f8d78480a890.mock.pstmn.io/api/" + private const val BE_DEV_URL = "https://be.dev.eeos.store/api/" + private var gson = GsonBuilder().setLenient().create() + + @Provides + @Singleton + fun provideHttpLoggingInterceptor(): Interceptor = HttpLoggingInterceptor().apply { + level = HttpLoggingInterceptor.Level.BODY + } + + @Provides + @Singleton + fun provideOkHttpClient(): OkHttpClient = OkHttpClient.Builder() + .addInterceptor( + HttpLoggingInterceptor().apply { + level = HttpLoggingInterceptor.Level.BODY + } + ) + .build() + + @Provides + @Singleton + fun provideRetrofit( + client: OkHttpClient, + ): Retrofit = Retrofit.Builder() + .baseUrl(MOCK_URL) + .client(client) + .addConverterFactory(GsonConverterFactory.create(gson)) + .build() +} diff --git a/AOS/app/src/main/java/com/example/eeos/di/ServiceModule.kt b/AOS/app/src/main/java/com/example/eeos/di/ServiceModule.kt new file mode 100644 index 00000000..41e88330 --- /dev/null +++ b/AOS/app/src/main/java/com/example/eeos/di/ServiceModule.kt @@ -0,0 +1,24 @@ +package com.example.eeos.di + +import com.example.eeos.data.service.InfoService +import com.example.eeos.data.service.ProgramService +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton +import retrofit2.Retrofit + +@Module +@InstallIn(SingletonComponent::class) +object ServiceModule { + @Provides + @Singleton + fun provideInfoService(retrofit: Retrofit): InfoService = + retrofit.create(InfoService::class.java) + + @Provides + @Singleton + fun provideProgramService(retrofit: Retrofit): ProgramService = + retrofit.create(ProgramService::class.java) +} diff --git a/AOS/app/src/main/java/com/example/eeos/domain/model/ActiveStatus.kt b/AOS/app/src/main/java/com/example/eeos/domain/model/ActiveStatus.kt new file mode 100644 index 00000000..76e2f3c6 --- /dev/null +++ b/AOS/app/src/main/java/com/example/eeos/domain/model/ActiveStatus.kt @@ -0,0 +1,6 @@ +package com.example.eeos.domain.model + +data class ActiveStatus( + val name: String, + val activeStatus: String +) diff --git a/AOS/app/src/main/java/com/example/eeos/domain/model/AttendStatus.kt b/AOS/app/src/main/java/com/example/eeos/domain/model/AttendStatus.kt new file mode 100644 index 00000000..54b62376 --- /dev/null +++ b/AOS/app/src/main/java/com/example/eeos/domain/model/AttendStatus.kt @@ -0,0 +1,6 @@ +package com.example.eeos.domain.model + +data class AttendStatus( + val name: String, + val attendStatus: String +) diff --git a/AOS/app/src/main/java/com/example/eeos/domain/model/Member.kt b/AOS/app/src/main/java/com/example/eeos/domain/model/Member.kt new file mode 100644 index 00000000..ec2feb4c --- /dev/null +++ b/AOS/app/src/main/java/com/example/eeos/domain/model/Member.kt @@ -0,0 +1,6 @@ +package com.example.eeos.domain.model + +data class Member( + val name: String, + val attendStatus: String +) diff --git a/AOS/app/src/main/java/com/example/eeos/domain/model/MemberList.kt b/AOS/app/src/main/java/com/example/eeos/domain/model/MemberList.kt new file mode 100644 index 00000000..5596a7d0 --- /dev/null +++ b/AOS/app/src/main/java/com/example/eeos/domain/model/MemberList.kt @@ -0,0 +1,5 @@ +package com.example.eeos.domain.model + +data class MemberList( + val members: List +) diff --git a/AOS/app/src/main/java/com/example/eeos/domain/model/Program.kt b/AOS/app/src/main/java/com/example/eeos/domain/model/Program.kt new file mode 100644 index 00000000..1cfb26c8 --- /dev/null +++ b/AOS/app/src/main/java/com/example/eeos/domain/model/Program.kt @@ -0,0 +1,10 @@ +package com.example.eeos.domain.model + +data class Program( + val programId: Int, + val title: String, + val deadLine: String, + val category: String, + val programStatus: String, + val type: String +) diff --git a/AOS/app/src/main/java/com/example/eeos/domain/model/ProgramDetail.kt b/AOS/app/src/main/java/com/example/eeos/domain/model/ProgramDetail.kt new file mode 100644 index 00000000..af003514 --- /dev/null +++ b/AOS/app/src/main/java/com/example/eeos/domain/model/ProgramDetail.kt @@ -0,0 +1,9 @@ +package com.example.eeos.domain.model + +data class ProgramDetail( + val title: String, + val deadLine: String, + val content: String, + val category: String, + val type: String +) diff --git a/AOS/app/src/main/java/com/example/eeos/domain/model/ProgramList.kt b/AOS/app/src/main/java/com/example/eeos/domain/model/ProgramList.kt new file mode 100644 index 00000000..1f86650a --- /dev/null +++ b/AOS/app/src/main/java/com/example/eeos/domain/model/ProgramList.kt @@ -0,0 +1,5 @@ +package com.example.eeos.domain.model + +data class ProgramList( + val programs: List +) diff --git a/AOS/app/src/main/java/com/example/eeos/domain/repository/InfoRepository.kt b/AOS/app/src/main/java/com/example/eeos/domain/repository/InfoRepository.kt new file mode 100644 index 00000000..78cf5453 --- /dev/null +++ b/AOS/app/src/main/java/com/example/eeos/domain/repository/InfoRepository.kt @@ -0,0 +1,12 @@ +package com.example.eeos.domain.repository + +import com.example.eeos.data.model.remote.request.RequestPutActiveStatusDto +import com.example.eeos.domain.model.ActiveStatus + +interface InfoRepository { + suspend fun getActiveStatus(): Result + + suspend fun putActiveStatus( + requestPutActiveStatusDto: RequestPutActiveStatusDto + ): Result +} diff --git a/AOS/app/src/main/java/com/example/eeos/domain/repository/ProgramRepository.kt b/AOS/app/src/main/java/com/example/eeos/domain/repository/ProgramRepository.kt new file mode 100644 index 00000000..145b0f1e --- /dev/null +++ b/AOS/app/src/main/java/com/example/eeos/domain/repository/ProgramRepository.kt @@ -0,0 +1,34 @@ +package com.example.eeos.domain.repository + +import com.example.eeos.data.model.remote.request.RequestPutAttendStatusDto +import com.example.eeos.domain.model.AttendStatus +import com.example.eeos.domain.model.Member +import com.example.eeos.domain.model.Program +import com.example.eeos.domain.model.ProgramDetail + +interface ProgramRepository { + suspend fun getProgramDetail( + programId: Int + ): Result + + suspend fun getProgramList( + category: String, + programStatus: String, + size: Int, + page: Int + ): Result> + + suspend fun putAttendStatus( + programId: Int, + requestPutAttendStatusDto: RequestPutAttendStatusDto + ): Result + + suspend fun getAttendStatus( + programId: Int + ): Result + + suspend fun getMemberList( + programId: Int, + attendStatus: String + ): Result> +} diff --git a/AOS/app/src/main/java/com/example/eeos/navigation/EEOSNavGraph.kt b/AOS/app/src/main/java/com/example/eeos/navigation/EEOSNavGraph.kt index a2617ddb..8a929c75 100644 --- a/AOS/app/src/main/java/com/example/eeos/navigation/EEOSNavGraph.kt +++ b/AOS/app/src/main/java/com/example/eeos/navigation/EEOSNavGraph.kt @@ -1,16 +1,24 @@ package com.example.eeos.navigation import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState import androidx.compose.runtime.remember +import androidx.hilt.navigation.compose.hiltViewModel import androidx.navigation.NavHostController import androidx.navigation.NavType import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController import androidx.navigation.navArgument -import com.example.eeos.ui.detail.DetailScreen -import com.example.eeos.ui.home.HomeScreen -import com.example.eeos.ui.login.LoginScreen +import com.example.eeos.consts.AttendStatus +import com.example.eeos.presentation.detail.DetailScreen +import com.example.eeos.presentation.detail.MemberAttendanceViewModel +import com.example.eeos.presentation.detail.ProgramDetailViewModel +import com.example.eeos.presentation.detail.bottomsheet.UserAttendStatusViewModel +import com.example.eeos.presentation.home.HomeScreen +import com.example.eeos.presentation.home.HomeViewModel +import com.example.eeos.presentation.login.LoginScreen +import com.example.eeos.presentation.topappbar.TopAppBarViewModel @Composable fun EEOSNavGraph( @@ -37,8 +45,29 @@ fun EEOSNavGraph( composable( EEOSDestinations.HOME_ROUTE ) { + val topAppBarViewModel = hiltViewModel() + val homeViewModel = hiltViewModel() + + val topAppBarUiState = topAppBarViewModel.topAppBarUiState.collectAsState() + val homeUiState = homeViewModel.homeUiState.collectAsState() + HomeScreen( - onProgramClick = { navActions.navigateToProgramDetail(1) } + homeUiState = homeUiState, + topAppBarUiState = topAppBarUiState, + loadProgramList = { category, programStatus, page -> + (homeViewModel::getProgramList)( + category, + programStatus, + page + ) + }, + onProgramClick = { programId -> navActions.navigateToProgramDetail(programId) }, + refreshProgramList = { (homeViewModel::refreshProgramList)() }, + putActiveStatus = { activeStatus -> + (topAppBarViewModel::putActiveStatus)( + activeStatus + ) + } ) } @@ -50,7 +79,52 @@ fun EEOSNavGraph( } ) ) { - DetailScreen() + val programId = + it.arguments?.getInt(EEOSDestinationsArgs.PROGRAM_ID_ARG) ?: 1 /* TODO */ + + val topAppBarViewModel = hiltViewModel() + val programDetailViewModel = hiltViewModel() + val memberAttendanceViewModel = hiltViewModel() + val userAttendanceViewModel = hiltViewModel() + + if (programId != null) { + programDetailViewModel.getProgramDetail(programId) + + memberAttendanceViewModel.getAttendeeList(programId, AttendStatus.attend) + memberAttendanceViewModel.getAttendeeList(programId, AttendStatus.absent) + memberAttendanceViewModel.getAttendeeList(programId, AttendStatus.perceive) + memberAttendanceViewModel.getAttendeeList(programId, AttendStatus.nonResponse) + + userAttendanceViewModel.getUserAttendStatus(programId) + } else { + /* TODO */ + } + + val topAppBarUiState = topAppBarViewModel.topAppBarUiState.collectAsState() + val programDetailUiState = programDetailViewModel.detailUiState.collectAsState() + val memberAttendanceUiState = + memberAttendanceViewModel.memberDetailUiState.collectAsState() + val userAttendanceUiState = + userAttendanceViewModel.userAttendStatusUiState.collectAsState() + + DetailScreen( + detailUiState = programDetailUiState, + memberUiState = memberAttendanceUiState, + attendanceUiState = userAttendanceUiState, + topAppBarUiState = topAppBarUiState, + putUserAttendStatus = { afterAttendStatus -> + (userAttendanceViewModel::putUserAttendStatus)( + programId, + userAttendanceUiState.value.userAttendStatus, + afterAttendStatus + ) + }, + putActiveStatus = { activeStatus -> + (topAppBarViewModel::putActiveStatus)( + activeStatus + ) + } + ) } } } diff --git a/AOS/app/src/main/java/com/example/eeos/ui/detail/DetailScreen.kt b/AOS/app/src/main/java/com/example/eeos/presentation/detail/DetailScreen.kt similarity index 51% rename from AOS/app/src/main/java/com/example/eeos/ui/detail/DetailScreen.kt rename to AOS/app/src/main/java/com/example/eeos/presentation/detail/DetailScreen.kt index 6b8103c0..738fb2c2 100644 --- a/AOS/app/src/main/java/com/example/eeos/ui/detail/DetailScreen.kt +++ b/AOS/app/src/main/java/com/example/eeos/presentation/detail/DetailScreen.kt @@ -1,4 +1,4 @@ -package com.example.eeos.ui.detail +package com.example.eeos.presentation.detail import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column @@ -13,21 +13,47 @@ import androidx.compose.foundation.verticalScroll import androidx.compose.material3.BottomSheetScaffold import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.SnackbarHost import androidx.compose.runtime.Composable +import androidx.compose.runtime.State +import androidx.compose.runtime.collectAsState import androidx.compose.ui.Modifier import androidx.compose.ui.res.colorResource import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.tooling.preview.Preview +import androidx.hilt.navigation.compose.hiltViewModel import com.example.eeos.R -import com.example.eeos.ui.common.EeosTopAppBar +import com.example.eeos.presentation.detail.bottomsheet.BottomSheetContents +import com.example.eeos.presentation.detail.bottomsheet.SheetDragHandle +import com.example.eeos.presentation.detail.bottomsheet.UserAttendStatusUiState +import com.example.eeos.presentation.detail.bottomsheet.UserAttendStatusViewModel +import com.example.eeos.presentation.topappbar.EeosTopAppBar +import com.example.eeos.presentation.topappbar.TopAppBarUiState +import com.example.eeos.presentation.topappbar.TopAppBarViewModel @OptIn(ExperimentalMaterial3Api::class) @Composable -fun DetailScreen() { +fun DetailScreen( + detailUiState: State, + memberUiState: State, + attendanceUiState: State, + topAppBarUiState: State, + putUserAttendStatus: (String) -> Unit, + putActiveStatus: (String) -> Unit +) { BottomSheetScaffold( - sheetContent = { BottomSheetContents() }, + sheetContent = { + BottomSheetContents( + programDetailUiState = detailUiState, + attendanceUiState = attendanceUiState, + putUserAttendStatus = putUserAttendStatus + ) + }, topBar = { - EeosTopAppBar() + EeosTopAppBar( + topAppBarUiState = topAppBarUiState, + putActiveStatus = putActiveStatus + ) }, sheetPeekHeight = dimensionResource(id = R.dimen.height_detail_screen_sheet_peek_height), sheetShape = RoundedCornerShape( @@ -42,14 +68,21 @@ fun DetailScreen() { sheetDragHandle = { SheetDragHandle() }, + snackbarHost = { SnackbarHost(hostState = attendanceUiState.value.snackbarHostState) }, containerColor = colorResource(id = R.color.background) ) { - DetailScreenContent() + DetailScreenContent( + detailUiState = detailUiState, + memberUiState = memberUiState + ) } } @Composable -private fun DetailScreenContent() { +private fun DetailScreenContent( + detailUiState: State, + memberUiState: State +) { val state = rememberScrollState() Row( modifier = Modifier @@ -64,7 +97,12 @@ private fun DetailScreenContent() { Column( modifier = Modifier.verticalScroll(state) ) { - ProgramDetail() + ProgramDetail( + category = detailUiState.value.category, + title = detailUiState.value.title, + deadLine = detailUiState.value.deadLine, + content = detailUiState.value.content + ) Spacer( modifier = Modifier.height( height = dimensionResource( @@ -72,7 +110,9 @@ private fun DetailScreenContent() { ) ) ) - MemberLists() + MemberLists( + memberUiState = memberUiState + ) Spacer( modifier = Modifier.height( dimensionResource(R.dimen.height_detail_screen_space_bottom) @@ -91,6 +131,13 @@ private fun DetailScreenContent() { @Composable private fun DetailScreenPreview() { MaterialTheme { - DetailScreen() + DetailScreen( + detailUiState = hiltViewModel().detailUiState.collectAsState(), + memberUiState = hiltViewModel().memberDetailUiState.collectAsState(), + attendanceUiState = hiltViewModel().userAttendStatusUiState.collectAsState(), + topAppBarUiState = hiltViewModel().topAppBarUiState.collectAsState(), + putUserAttendStatus = { p -> }, + putActiveStatus = { p -> } + ) } } diff --git a/AOS/app/src/main/java/com/example/eeos/presentation/detail/MemberAttendanceViewModel.kt b/AOS/app/src/main/java/com/example/eeos/presentation/detail/MemberAttendanceViewModel.kt new file mode 100644 index 00000000..2e9068f4 --- /dev/null +++ b/AOS/app/src/main/java/com/example/eeos/presentation/detail/MemberAttendanceViewModel.kt @@ -0,0 +1,93 @@ +package com.example.eeos.presentation.detail + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.example.eeos.consts.AttendStatus +import com.example.eeos.domain.model.Member +import com.example.eeos.domain.repository.ProgramRepository +import dagger.hilt.android.lifecycle.HiltViewModel +import java.io.IOException +import javax.inject.Inject +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch +import retrofit2.HttpException + +data class MemberAttendanceUiState( + val isError: Boolean = false, + val isLoading: Boolean = false, + + val attendMembers: List = listOf(), + val absentMembers: List = listOf(), + val perceiveMembers: List = listOf(), + val nonResponseMembers: List = listOf() +) + +@HiltViewModel +class MemberAttendanceViewModel @Inject constructor( + private val programRepository: ProgramRepository +) : ViewModel() { + private val _memberAttendanceUiState = MutableStateFlow(MemberAttendanceUiState()) + val memberDetailUiState = _memberAttendanceUiState.asStateFlow() + + fun getAttendeeList(programId: Int, attendStatus: String) { + viewModelScope.launch { + programRepository.getMemberList( + programId = programId, + attendStatus = attendStatus + ) + .onSuccess { memberList -> + updateUiState(attendStatus = attendStatus, memberList = memberList) + } + .onFailure { exception -> + when (exception) { + is HttpException -> { + _memberAttendanceUiState.update { currentState -> + currentState.copy( + isError = true + ) + } + } + + is IOException -> { + _memberAttendanceUiState.update { currentState -> + currentState.copy( + isError = true + ) + } + } + } + } + } + } + + private fun updateUiState(attendStatus: String, memberList: List) { + when (attendStatus) { + AttendStatus.attend -> + _memberAttendanceUiState.update { currentState -> + currentState.copy( + attendMembers = memberList + ) + } + AttendStatus.absent -> + _memberAttendanceUiState.update { currentState -> + currentState.copy( + absentMembers = memberList + ) + } + AttendStatus.perceive -> + _memberAttendanceUiState.update { currentState -> + currentState.copy( + perceiveMembers = memberList + ) + } + AttendStatus.nonResponse -> + _memberAttendanceUiState.update { currentState -> + currentState.copy( + nonResponseMembers = memberList + ) + } + } + } +} diff --git a/AOS/app/src/main/java/com/example/eeos/ui/detail/MemberList.kt b/AOS/app/src/main/java/com/example/eeos/presentation/detail/MemberList.kt similarity index 72% rename from AOS/app/src/main/java/com/example/eeos/ui/detail/MemberList.kt rename to AOS/app/src/main/java/com/example/eeos/presentation/detail/MemberList.kt index b52085d6..9b4b72ce 100644 --- a/AOS/app/src/main/java/com/example/eeos/ui/detail/MemberList.kt +++ b/AOS/app/src/main/java/com/example/eeos/presentation/detail/MemberList.kt @@ -1,4 +1,4 @@ -package com.example.eeos.ui.detail +package com.example.eeos.presentation.detail import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Arrangement @@ -12,6 +12,8 @@ import androidx.compose.material3.Divider import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.State +import androidx.compose.runtime.collectAsState import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.painter.Painter @@ -19,50 +21,41 @@ import androidx.compose.ui.res.colorResource import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.painterResource import androidx.compose.ui.tooling.preview.Preview +import androidx.hilt.navigation.compose.hiltViewModel import com.example.eeos.R -import com.example.eeos.ui.util.NonLazyGrid +import com.example.eeos.domain.model.Member +import com.example.eeos.presentation.util.ComponentUtil.NonLazyGrid data class Attendance( val attendance: String, val painter: Painter, - val memberList: List -) - -val sampleMemberList: List = listOf( - MemberData(24, "인텔리", attendStatus = AttendStatus.ATTEND), - MemberData(22, "만두쓰", attendStatus = AttendStatus.ATTEND), - MemberData(25, "지유쓰", attendStatus = AttendStatus.ATTEND), - MemberData(25, "스티브", attendStatus = AttendStatus.ATTEND), - MemberData(25, "오션쓰", attendStatus = AttendStatus.ATTEND), - MemberData(24, "인텔리", attendStatus = AttendStatus.ATTEND), - MemberData(22, "만두쓰", attendStatus = AttendStatus.ATTEND), - MemberData(25, "지유쓰", attendStatus = AttendStatus.ATTEND), - MemberData(25, "스티브", attendStatus = AttendStatus.ATTEND), - MemberData(25, "오션쓰", attendStatus = AttendStatus.ATTEND), + val memberList: List ) @Composable -fun MemberLists() { +fun MemberLists( + memberUiState: State +) { val attendStatusList: List = listOf( Attendance( attendance = "참석", painter = painterResource(id = R.drawable.detail_ic_attend_20dp), - memberList = sampleMemberList + memberList = memberUiState.value.attendMembers ), Attendance( attendance = "불참", painter = painterResource(id = R.drawable.detail_ic_absent_20dp), - memberList = sampleMemberList + memberList = memberUiState.value.absentMembers ), Attendance( attendance = "지각", painter = painterResource(id = R.drawable.detail_ic_latecomers_20dp), - memberList = sampleMemberList + memberList = memberUiState.value.perceiveMembers ), Attendance( attendance = "미정", painter = painterResource(id = R.drawable.detail_ic_undefined_20dp), - memberList = sampleMemberList + memberList = memberUiState.value.nonResponseMembers ), ) @@ -71,7 +64,7 @@ fun MemberLists() { MemberList( title = attendStatus.attendance, painter = attendStatus.painter, - memberList = attendStatus.memberList /* ToDo */ + memberList = attendStatus.memberList, ) Spacer( modifier = Modifier.height( @@ -88,7 +81,7 @@ fun MemberLists() { private fun MemberList( title: String, painter: Painter, - memberList: List + memberList: List, ) { Column { Row( @@ -116,7 +109,7 @@ private fun MemberList( ) } Text( - text = "20명", + text = "${memberList.size}명", style = MaterialTheme.typography.bodySmall, color = colorResource(R.color.gray_500) ) @@ -148,22 +141,10 @@ private fun MemberList( } @Composable -private fun Member(member: MemberData) { +private fun Member(member: Member) { Row( horizontalArrangement = Arrangement.Center ) { - Text( - text = member.generation.toString() + "기", - style = MaterialTheme.typography.bodySmall, - color = colorResource(id = R.color.paragraph) - ) - Spacer( - modifier = Modifier.width( - dimensionResource( - id = R.dimen.margin_detail_screen_space_between_member_list_generation_and_name - ) - ) - ) Text( text = member.name, style = MaterialTheme.typography.bodySmall, @@ -176,6 +157,8 @@ private fun Member(member: MemberData) { @Composable private fun ProgramPreview() { MaterialTheme { - MemberLists() + MemberLists( + memberUiState = hiltViewModel().memberDetailUiState.collectAsState() + ) } } diff --git a/AOS/app/src/main/java/com/example/eeos/ui/detail/ProgramDetail.kt b/AOS/app/src/main/java/com/example/eeos/presentation/detail/ProgramDetail.kt similarity index 88% rename from AOS/app/src/main/java/com/example/eeos/ui/detail/ProgramDetail.kt rename to AOS/app/src/main/java/com/example/eeos/presentation/detail/ProgramDetail.kt index d59e0770..69a9b89a 100644 --- a/AOS/app/src/main/java/com/example/eeos/ui/detail/ProgramDetail.kt +++ b/AOS/app/src/main/java/com/example/eeos/presentation/detail/ProgramDetail.kt @@ -1,4 +1,4 @@ -package com.example.eeos.ui.detail +package com.example.eeos.presentation.detail import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.layout.Arrangement @@ -25,12 +25,17 @@ import androidx.compose.ui.tooling.preview.Preview import com.example.eeos.R @Composable -fun ProgramDetail() { +fun ProgramDetail( + category: String, + title: String, + deadLine: String, + content: String +) { Column { Column( horizontalAlignment = Alignment.Start ) { - Category() + Category(category) Spacer( modifier = Modifier.height( dimensionResource( @@ -38,7 +43,7 @@ fun ProgramDetail() { ) ) ) - Title() + Title(title, deadLine) Spacer( modifier = Modifier.height( dimensionResource( @@ -70,7 +75,7 @@ fun ProgramDetail() { ) ) ) - Content() + Content(content) Spacer( modifier = Modifier.width( dimensionResource( @@ -89,7 +94,7 @@ fun ProgramDetail() { } @Composable -private fun Category() { +private fun Category(category: String) { val containerColor = colorResource(R.color.warning_light) val contentColor = colorResource(R.color.warning_strong) @@ -118,7 +123,7 @@ private fun Category() { ), ) { Text( - text = "주간 발표", /* ToDo */ + text = category, style = MaterialTheme.typography.labelSmall, color = colorResource(R.color.warning_strong) ) @@ -126,9 +131,9 @@ private fun Category() { } @Composable -private fun Title() { +private fun Title(title: String, deadLine: String) { Text( - text = "10월 2주차 주간 발표", /* ToDo */ + text = title, style = MaterialTheme.typography.headlineMedium ) Spacer( @@ -137,15 +142,15 @@ private fun Title() { ) ) Text( - text = "2023년 10월 6일 (일)", /* ToDo */ + text = deadLine, style = MaterialTheme.typography.bodyLarge ) } @Composable -private fun Content() { +private fun Content(content: String) { Text( - text = "샘플 텍스트"/* ToDo */, + text = content, style = MaterialTheme.typography.bodySmall, modifier = Modifier.width( width = dimensionResource(id = R.dimen.width_detail_screen_divider) - dimensionResource( @@ -161,6 +166,11 @@ private fun Content() { @Composable private fun ProgramDetailPreview() { MaterialTheme { - ProgramDetail() + ProgramDetail( + category = "주간 발표", + title = "10월 2주차 주간 발표", + deadLine = "2023년 10월 6일 (일)", + content = "샘플 텍스트" + ) } } diff --git a/AOS/app/src/main/java/com/example/eeos/presentation/detail/ProgramDetailViewModel.kt b/AOS/app/src/main/java/com/example/eeos/presentation/detail/ProgramDetailViewModel.kt new file mode 100644 index 00000000..8ae06fb8 --- /dev/null +++ b/AOS/app/src/main/java/com/example/eeos/presentation/detail/ProgramDetailViewModel.kt @@ -0,0 +1,91 @@ +package com.example.eeos.presentation.detail + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.example.eeos.consts.categoryChips +import com.example.eeos.domain.repository.ProgramRepository +import dagger.hilt.android.lifecycle.HiltViewModel +import java.io.IOException +import javax.inject.Inject +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch +import retrofit2.HttpException + +data class ProgramDetailUiState( + val isError: Boolean = false, + val isLoading: Boolean = false, + + val category: String = "", + val title: String = "", + val deadLine: String = "", + val content: String = "", + val programType: String = "" +) + +@HiltViewModel +class ProgramDetailViewModel @Inject constructor( + private val programRepository: ProgramRepository +) : ViewModel() { + private val _detailUiState = MutableStateFlow(ProgramDetailUiState()) + val detailUiState = _detailUiState.asStateFlow() + + fun getProgramDetail(programId: Int) { + viewModelScope.launch { + programRepository.getProgramDetail(programId) + .onSuccess { programDetail -> + val category = categoryAdjustment(programDetail.category) + val title = if (programDetail.type == "demand") { + titleAdjustment( + programDetail.title + ) + } else { programDetail.title } + val deadLine = deadLineAdjustment(programDetail.deadLine) + + _detailUiState.update { currentState -> + currentState.copy( + category = category, + title = title, + deadLine = deadLine, + content = programDetail.content, + programType = programDetail.type + ) + } + } + .onFailure { + exception -> + when (exception) { + is HttpException -> { + _detailUiState.update { currentState -> + currentState.copy( + isError = true + ) + } + } + + is IOException -> { + _detailUiState.update { currentState -> + currentState.copy( + isError = true + ) + } + } + } + } + } + } + + private fun categoryAdjustment(category: String): String { + return categoryChips[com.example.eeos.consts.category.indexOf(category)] + } + + private fun deadLineAdjustment(deadLine: String): String { + /* TODO */ + return "날짜 조정 텍스트" + } + + private fun titleAdjustment(title: String): String { + return "[수요조사] $title" + } +} diff --git a/AOS/app/src/main/java/com/example/eeos/ui/detail/AttendStatusButton.kt b/AOS/app/src/main/java/com/example/eeos/presentation/detail/bottomsheet/AttendStatusButton.kt similarity index 86% rename from AOS/app/src/main/java/com/example/eeos/ui/detail/AttendStatusButton.kt rename to AOS/app/src/main/java/com/example/eeos/presentation/detail/bottomsheet/AttendStatusButton.kt index a305c4b9..c461ae88 100644 --- a/AOS/app/src/main/java/com/example/eeos/ui/detail/AttendStatusButton.kt +++ b/AOS/app/src/main/java/com/example/eeos/presentation/detail/bottomsheet/AttendStatusButton.kt @@ -1,4 +1,4 @@ -package com.example.eeos.ui.detail +package com.example.eeos.presentation.detail.bottomsheet import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.layout.PaddingValues @@ -22,20 +22,20 @@ fun AttendStatusButton( contentColor: Color, containerColor: Color, backgroundColor: Color, - isSelected: Boolean, + selectedAttendStatus: String?, onClick: () -> Unit, ) { - val contentColor = if (isSelected) { + val contentColor = if (selectedAttendStatus == buttonText) { contentColor } else { colorResource(R.color.gray_500) } - val containerColor = if (isSelected) { + val containerColor = if (selectedAttendStatus == buttonText) { containerColor } else { backgroundColor } - val strokeColor = if (isSelected) { + val strokeColor = if (selectedAttendStatus == buttonText) { contentColor } else { colorResource(R.color.transparent) @@ -77,7 +77,7 @@ private fun AttendStatusButtonPreview() { contentColor = colorResource(R.color.success_strong), containerColor = colorResource(R.color.success_light), backgroundColor = colorResource(R.color.gray_100), - isSelected = true, + selectedAttendStatus = /*rememberSaveable { mutableStateOf("참석") }*/"참석", onClick = {}, ) } diff --git a/AOS/app/src/main/java/com/example/eeos/presentation/detail/bottomsheet/AttendStatusButtons.kt b/AOS/app/src/main/java/com/example/eeos/presentation/detail/bottomsheet/AttendStatusButtons.kt new file mode 100644 index 00000000..51e94a13 --- /dev/null +++ b/AOS/app/src/main/java/com/example/eeos/presentation/detail/bottomsheet/AttendStatusButtons.kt @@ -0,0 +1,120 @@ +package com.example.eeos.presentation.detail.bottomsheet + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.runtime.State +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.colorResource +import androidx.compose.ui.res.dimensionResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.hilt.navigation.compose.hiltViewModel +import com.example.eeos.R +import com.example.eeos.consts.AttendStatus +import com.example.eeos.consts.attendStatusMap +import com.example.eeos.presentation.detail.confirmdialog.ConfirmAttendStatusDialog + +@Composable +fun AttendStatusButtons( + attendanceUiState: State, + putUserAttendStatus: (String) -> Unit +) { + Row( + modifier = Modifier.background( + color = colorResource(R.color.gray_100), + shape = RoundedCornerShape(dimensionResource(id = R.dimen.size_corner_20dp)), + ) + ) { + val selectedAttendStatus = attendStatusMap[attendanceUiState.value.userAttendStatus] + val tempAttendStatus = remember { mutableStateOf("") } + + val attendStatusDialogState = remember { mutableStateOf(false) } + if (attendStatusDialogState.value) { + ConfirmAttendStatusDialog( + onConfirmRequest = { + putUserAttendStatus(tempAttendStatus.value) + attendStatusDialogState.value = false + }, + onDismissRequest = { attendStatusDialogState.value = false }, + attendStatus = attendStatusMap[tempAttendStatus.value]!! + ) + } + + val onClick = { attendStatus: String -> + tempAttendStatus.value = attendStatus + attendStatusDialogState.value = true + } + + AttendButton( + selectedAttendStatus = selectedAttendStatus + ) { + onClick(AttendStatus.attend) + } + PercipientButton( + selectedAttendStatus = selectedAttendStatus + ) { + onClick(AttendStatus.perceive) + } + AbsentButton( + selectedAttendStatus = selectedAttendStatus + ) { + onClick(AttendStatus.absent) + } + } +} + +@Composable +private fun AttendButton(selectedAttendStatus: String?, onClick: () -> Unit) { + AttendStatusButton( + buttonText = stringResource(R.string.detail_attendees), + contentColor = colorResource(R.color.success_strong), + containerColor = colorResource(R.color.success_light), + backgroundColor = colorResource(R.color.gray_100), + selectedAttendStatus = selectedAttendStatus, + onClick = onClick + ) +} + +@Composable +private fun PercipientButton( + selectedAttendStatus: String?, + onClick: () -> Unit +) { + AttendStatusButton( + buttonText = stringResource(R.string.detail_latecomers), + contentColor = colorResource(R.color.warning_strong), + containerColor = colorResource(R.color.warning_light), + backgroundColor = colorResource(R.color.gray_100), + selectedAttendStatus = selectedAttendStatus, + onClick = onClick + ) +} + +@Composable +private fun AbsentButton(selectedAttendStatus: String?, onClick: () -> Unit) { + AttendStatusButton( + buttonText = stringResource(R.string.detail_absentees), + contentColor = colorResource(R.color.action), + containerColor = colorResource(R.color.action_light), + backgroundColor = colorResource(R.color.gray_100), + selectedAttendStatus = selectedAttendStatus, + onClick = onClick + ) +} + +@Preview(showBackground = true) +@Composable +private fun AttendStatusButtonsPreview() { + MaterialTheme { + AttendStatusButtons( + attendanceUiState = hiltViewModel().userAttendStatusUiState.collectAsState(), + putUserAttendStatus = { p1 -> } + ) + } +} diff --git a/AOS/app/src/main/java/com/example/eeos/ui/detail/AttendStatusChip.kt b/AOS/app/src/main/java/com/example/eeos/presentation/detail/bottomsheet/AttendStatusChip.kt similarity index 97% rename from AOS/app/src/main/java/com/example/eeos/ui/detail/AttendStatusChip.kt rename to AOS/app/src/main/java/com/example/eeos/presentation/detail/bottomsheet/AttendStatusChip.kt index 20b7411a..e929b8ba 100644 --- a/AOS/app/src/main/java/com/example/eeos/ui/detail/AttendStatusChip.kt +++ b/AOS/app/src/main/java/com/example/eeos/presentation/detail/bottomsheet/AttendStatusChip.kt @@ -1,4 +1,4 @@ -package com.example.eeos.ui.detail +package com.example.eeos.presentation.detail.bottomsheet import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.layout.size diff --git a/AOS/app/src/main/java/com/example/eeos/ui/detail/AttendStatusChips.kt b/AOS/app/src/main/java/com/example/eeos/presentation/detail/bottomsheet/AttendStatusChips.kt similarity index 93% rename from AOS/app/src/main/java/com/example/eeos/ui/detail/AttendStatusChips.kt rename to AOS/app/src/main/java/com/example/eeos/presentation/detail/bottomsheet/AttendStatusChips.kt index d0b309b4..da0d5b43 100644 --- a/AOS/app/src/main/java/com/example/eeos/ui/detail/AttendStatusChips.kt +++ b/AOS/app/src/main/java/com/example/eeos/presentation/detail/bottomsheet/AttendStatusChips.kt @@ -1,4 +1,4 @@ -package com.example.eeos.ui.detail +package com.example.eeos.presentation.detail.bottomsheet import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable @@ -18,7 +18,7 @@ fun AttendChip() { } @Composable -fun LateComerChip() { +fun PercipientChip() { AttendStatusChip( buttonText = stringResource(R.string.detail_latecomers), contentColor = colorResource(R.color.warning_strong), @@ -42,7 +42,7 @@ fun RequestSurveyChip() { AttendStatusChip( buttonText = stringResource(R.string.detail_bottom_sheet_do_survey), contentColor = colorResource(R.color.tertiary_strong), - containerColor = colorResource(R.color.tertiary), + containerColor = colorResource(R.color.secondary), isContentLong = true ) } diff --git a/AOS/app/src/main/java/com/example/eeos/ui/detail/AttendStatusInfo.kt b/AOS/app/src/main/java/com/example/eeos/presentation/detail/bottomsheet/AttendStatusInfo.kt similarity index 77% rename from AOS/app/src/main/java/com/example/eeos/ui/detail/AttendStatusInfo.kt rename to AOS/app/src/main/java/com/example/eeos/presentation/detail/bottomsheet/AttendStatusInfo.kt index ca30839a..a7af405b 100644 --- a/AOS/app/src/main/java/com/example/eeos/ui/detail/AttendStatusInfo.kt +++ b/AOS/app/src/main/java/com/example/eeos/presentation/detail/bottomsheet/AttendStatusInfo.kt @@ -1,4 +1,4 @@ -package com.example.eeos.ui.detail +package com.example.eeos.presentation.detail.bottomsheet import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer @@ -12,19 +12,21 @@ import androidx.compose.ui.res.colorResource import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.tooling.preview.Preview import com.example.eeos.R +import com.example.eeos.consts.AttendStatus +import com.example.eeos.domain.model.Member @Composable fun AttendStatusInfo( - memberInfo: MemberData, + memberInfo: Member, attendStatusChip: @Composable () -> Unit ) { - val infoText = "${memberInfo.generation}기 ${memberInfo.name} 님" + val userName = "${memberInfo.name} 님" Row( verticalAlignment = Alignment.CenterVertically ) { Text( - text = infoText, + text = userName, style = MaterialTheme.typography.titleSmall, color = colorResource(R.color.paragraph) ) @@ -44,10 +46,9 @@ fun AttendStatusInfo( private fun AttendStatusInfoPreview() { MaterialTheme { AttendStatusInfo( - memberInfo = MemberData( - generation = 24, - name = "장현지", - attendStatus = AttendStatus.NO_RESPONSE + memberInfo = Member( + name = "24기 장현지", + attendStatus = AttendStatus.nonResponse ) ) { AttendChip() diff --git a/AOS/app/src/main/java/com/example/eeos/ui/detail/BottomSheetContent.kt b/AOS/app/src/main/java/com/example/eeos/presentation/detail/bottomsheet/BottomSheetContent.kt similarity index 60% rename from AOS/app/src/main/java/com/example/eeos/ui/detail/BottomSheetContent.kt rename to AOS/app/src/main/java/com/example/eeos/presentation/detail/bottomsheet/BottomSheetContent.kt index 1778fc2f..4e0ae46e 100644 --- a/AOS/app/src/main/java/com/example/eeos/ui/detail/BottomSheetContent.kt +++ b/AOS/app/src/main/java/com/example/eeos/presentation/detail/bottomsheet/BottomSheetContent.kt @@ -1,4 +1,4 @@ -package com.example.eeos.ui.detail +package com.example.eeos.presentation.detail.bottomsheet import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Column @@ -8,6 +8,8 @@ import androidx.compose.foundation.layout.height import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.State +import androidx.compose.runtime.collectAsState import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.colorResource @@ -15,7 +17,12 @@ import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview +import androidx.hilt.navigation.compose.hiltViewModel import com.example.eeos.R +import com.example.eeos.consts.AttendStatus +import com.example.eeos.domain.model.Member +import com.example.eeos.presentation.detail.ProgramDetailUiState +import com.example.eeos.presentation.detail.ProgramDetailViewModel @Composable fun SheetDragHandle() { @@ -38,18 +45,34 @@ fun SheetDragHandle() { } @Composable -fun BottomSheetContents() { +fun BottomSheetContents( + programDetailUiState: State, + attendanceUiState: State, + putUserAttendStatus: (String) -> Unit +) { Column( modifier = Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally ) { AttendStatusInfo( - memberInfo = MemberData( - generation = 24, - name = "장현지", - attendStatus = AttendStatus.NO_RESPONSE + memberInfo = Member( + name = attendanceUiState.value.userName, + attendStatus = attendanceUiState.value.userAttendStatus ) - ) { RequestAttendCheckChip() } + ) { + when (attendanceUiState.value.userAttendStatus) { + AttendStatus.attend -> AttendChip() + AttendStatus.absent -> AbsentChip() + AttendStatus.perceive -> PercipientChip() + AttendStatus.nonRelated -> NonRelatedChip() + AttendStatus.nonResponse -> + if (programDetailUiState.value.programType == "demand") { + RequestSurveyChip() + } else { + RequestAttendCheckChip() + } + } + } Spacer( modifier = Modifier.height( height = dimensionResource( @@ -76,7 +99,10 @@ fun BottomSheetContents() { ) ) ) - AttendStatusButtons() + AttendStatusButtons( + attendanceUiState = attendanceUiState, + putUserAttendStatus = putUserAttendStatus + ) Spacer( modifier = Modifier.height( height = dimensionResource( @@ -91,6 +117,10 @@ fun BottomSheetContents() { @Composable private fun BottomSheetContentsPreview() { MaterialTheme { - BottomSheetContents() + BottomSheetContents( + attendanceUiState = hiltViewModel().userAttendStatusUiState.collectAsState(), + programDetailUiState = hiltViewModel().detailUiState.collectAsState(), + putUserAttendStatus = { p -> } + ) } } diff --git a/AOS/app/src/main/java/com/example/eeos/presentation/detail/bottomsheet/UserAttendStatusViewModel.kt b/AOS/app/src/main/java/com/example/eeos/presentation/detail/bottomsheet/UserAttendStatusViewModel.kt new file mode 100644 index 00000000..b54c77ea --- /dev/null +++ b/AOS/app/src/main/java/com/example/eeos/presentation/detail/bottomsheet/UserAttendStatusViewModel.kt @@ -0,0 +1,110 @@ +package com.example.eeos.presentation.detail.bottomsheet + +import androidx.compose.material3.SnackbarDuration +import androidx.compose.material3.SnackbarHostState +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.example.eeos.data.model.remote.request.RequestPutAttendStatusDto +import com.example.eeos.domain.repository.ProgramRepository +import dagger.hilt.android.lifecycle.HiltViewModel +import java.io.IOException +import javax.inject.Inject +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch +import retrofit2.HttpException + +data class UserAttendStatusUiState( + val isError: Boolean = false, + val isLoading: Boolean = false, + + val userName: String = "", + val userAttendStatus: String = "", + + val snackbarHostState: SnackbarHostState = SnackbarHostState() +) + +@HiltViewModel +class UserAttendStatusViewModel @Inject constructor( + private val programRepository: ProgramRepository +) : ViewModel() { + private val _userAttendStatusUiState = MutableStateFlow(UserAttendStatusUiState()) + val userAttendStatusUiState = _userAttendStatusUiState.asStateFlow() + + fun getUserAttendStatus(programId: Int) { + viewModelScope.launch { + programRepository.getAttendStatus(programId) + .onSuccess { userAttendStatus -> + _userAttendStatusUiState.update { currentState -> + currentState.copy( + userName = userAttendStatus.name, + userAttendStatus = userAttendStatus.attendStatus + ) + } + } + .onFailure { exception -> + when (exception) { + is HttpException -> { + _userAttendStatusUiState.update { currentState -> + currentState.copy( + isError = true + ) + } + } + + is IOException -> { + _userAttendStatusUiState.update { currentState -> + currentState.copy( + isError = true + ) + } + } + } + } + } + } + + fun putUserAttendStatus( + programId: Int, + beforeAttendStatus: String, + afterAttendStatus: String + ) { + viewModelScope.launch { + programRepository.putAttendStatus( + programId = programId, + requestPutAttendStatusDto = RequestPutAttendStatusDto( + beforeAttendStatus = beforeAttendStatus, + afterAttendStatus = afterAttendStatus + ) + ) + .onSuccess { + getUserAttendStatus(programId) + _userAttendStatusUiState.value.snackbarHostState + .showSnackbar( + message = "상태가 변경 되었습니다.", + duration = SnackbarDuration.Long + ) + } + .onFailure { exception -> + when (exception) { + is HttpException -> { + _userAttendStatusUiState.update { currentState -> + currentState.copy( + isError = true + ) + } + } + + is IOException -> { + _userAttendStatusUiState.update { currentState -> + currentState.copy( + isError = true + ) + } + } + } + } + } + } +} diff --git a/AOS/app/src/main/java/com/example/eeos/presentation/detail/confirmdialog/ConfirmAttendStatusDialog.kt b/AOS/app/src/main/java/com/example/eeos/presentation/detail/confirmdialog/ConfirmAttendStatusDialog.kt new file mode 100644 index 00000000..2548b04a --- /dev/null +++ b/AOS/app/src/main/java/com/example/eeos/presentation/detail/confirmdialog/ConfirmAttendStatusDialog.kt @@ -0,0 +1,146 @@ +package com.example.eeos.presentation.detail.confirmdialog + +import androidx.compose.foundation.BorderStroke +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.Card +import androidx.compose.material3.CardDefaults +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.colorResource +import androidx.compose.ui.res.dimensionResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.window.Dialog +import com.example.eeos.R + +@Composable +fun ConfirmAttendStatusDialog( + onConfirmRequest: () -> Unit, + onDismissRequest: () -> Unit, + attendStatus: String +) { + Dialog( + onDismissRequest = onDismissRequest + ) { + Card( + modifier = Modifier + .size( + width = dimensionResource( + id = R.dimen.width_confirm_dialog + ), + height = dimensionResource( + id = R.dimen.height_confirm_dialog + ) + ), + shape = RoundedCornerShape(dimensionResource(id = R.dimen.size_corner_20dp)), + colors = CardDefaults.cardColors( + containerColor = colorResource(R.color.background), + contentColor = colorResource(R.color.paragraph) + ) + ) { + Column( + modifier = Modifier + .padding(dimensionResource(id = R.dimen.padding_values_15dp)) + .fillMaxSize(), + verticalArrangement = Arrangement.SpaceBetween + ) { + Text( + text = String.format( + stringResource(R.string.confirm_dialog_container), + attendStatus + ), + style = MaterialTheme.typography.bodySmall + ) + Row { + Spacer( + modifier = Modifier.width( + dimensionResource(R.dimen.margin_confirm_dialog_buttons_right) + ) + ) + ConfirmDialogButton( + onClick = onDismissRequest, + innerText = stringResource(R.string.confirm_dialog_button_text_dismiss) + ) + Spacer( + modifier = Modifier.width( + dimensionResource(id = R.dimen.margin_confirm_dialog_between_buttons) + ) + ) + ConfirmDialogButton( + onClick = onConfirmRequest, + innerText = stringResource(R.string.confirm_dialog_button_text_confirm) + ) + } + } + } + } +} + +@Composable +private fun ConfirmDialogButton(onClick: () -> Unit, innerText: String) { + val containerColor = + if (innerText == stringResource(R.string.confirm_dialog_button_text_confirm)) { + colorResource(id = R.color.tertiary_regular) + } else { + colorResource(id = R.color.tertiary) + } + val contentColor = + if (innerText == stringResource(R.string.confirm_dialog_button_text_confirm)) { + colorResource(id = R.color.background) + } else { + colorResource(id = R.color.tertiary_strong) + } + val border = + if (innerText == stringResource(R.string.confirm_dialog_button_text_confirm)) { + null + } else { + BorderStroke( + width = dimensionResource(id = R.dimen.size_member_status_dialog_button_stroke), + color = contentColor + ) + } + + Button( + onClick = onClick, + modifier = Modifier + .size( + width = dimensionResource(id = R.dimen.width_confirm_dialog_button), + height = dimensionResource(id = R.dimen.height_confirm_dialog_button) + ), + shape = RoundedCornerShape(dimensionResource(id = R.dimen.size_corner_7dp)), + colors = ButtonDefaults.buttonColors( + containerColor = containerColor, + contentColor = contentColor + ), + border = border + ) { + Text( + text = innerText, + style = MaterialTheme.typography.labelMedium + ) + } +} + +@Preview(showBackground = true) +@Composable +private fun ConfirmAttendStatusDialogPreview() { + MaterialTheme { + ConfirmAttendStatusDialog( + onConfirmRequest = {}, + onDismissRequest = {}, + attendStatus = "\"출석\"" + ) + } +} diff --git a/AOS/app/src/main/java/com/example/eeos/ui/home/CategoryChips.kt b/AOS/app/src/main/java/com/example/eeos/presentation/home/CategoryChips.kt similarity index 88% rename from AOS/app/src/main/java/com/example/eeos/ui/home/CategoryChips.kt rename to AOS/app/src/main/java/com/example/eeos/presentation/home/CategoryChips.kt index b604a149..c3d7a879 100644 --- a/AOS/app/src/main/java/com/example/eeos/ui/home/CategoryChips.kt +++ b/AOS/app/src/main/java/com/example/eeos/presentation/home/CategoryChips.kt @@ -1,4 +1,4 @@ -package com.example.eeos.ui.home +package com.example.eeos.presentation.home import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.horizontalScroll @@ -26,7 +26,11 @@ import androidx.compose.ui.tooling.preview.Preview import com.example.eeos.R @Composable -fun CategoryChips(categoryChips: List, selectedCategory: MutableState) { +fun CategoryChips( + categoryChips: List, + selectedCategory: MutableState, + onCategoryChipClick: () -> Unit +) { Row( modifier = Modifier.horizontalScroll(rememberScrollState()) ) { @@ -34,7 +38,11 @@ fun CategoryChips(categoryChips: List, selectedCategory: MutableState, selectedCategory: MutableState Unit, + onCategoryChipClick: () -> Unit, ) { /*val interactionSource = remember { MutableInteractionSource() } val isFocused by interactionSource.collectIsFocusedAsState()*/ @@ -86,7 +94,7 @@ private fun CategoryChip( containerColor = containerColor, contentColor = contentColor ), - onClick = onClick, + onClick = onCategoryChipClick, border = borderStroke, contentPadding = PaddingValues(dimensionResource(id = R.dimen.size_all_0dp)) ) { @@ -110,7 +118,8 @@ private fun TabPreview() { stringResource(R.string.home_tab_party_department), stringResource(R.string.home_tab_others) ), - selectedCategory = rememberSaveable { mutableStateOf("전체") } + selectedCategory = rememberSaveable { mutableStateOf("전체") }, + onCategoryChipClick = {} ) } } @@ -121,7 +130,7 @@ private fun ChipPreview() { MaterialTheme { CategoryChip( chipName = "주간 발표", - onClick = {}, + onCategoryChipClick = {}, isSelected = true ) } diff --git a/AOS/app/src/main/java/com/example/eeos/presentation/home/HomeScreen.kt b/AOS/app/src/main/java/com/example/eeos/presentation/home/HomeScreen.kt new file mode 100644 index 00000000..fa35757e --- /dev/null +++ b/AOS/app/src/main/java/com/example/eeos/presentation/home/HomeScreen.kt @@ -0,0 +1,156 @@ +package com.example.eeos.presentation.home + +import android.annotation.SuppressLint +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Scaffold +import androidx.compose.material3.SnackbarHost +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.State +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.colorResource +import androidx.compose.ui.res.dimensionResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.hilt.navigation.compose.hiltViewModel +import com.example.eeos.R +import com.example.eeos.consts.category +import com.example.eeos.consts.categoryChips +import com.example.eeos.consts.programStatus +import com.example.eeos.consts.programStatusChips +import com.example.eeos.presentation.topappbar.EeosTopAppBar +import com.example.eeos.presentation.topappbar.TopAppBarUiState +import com.example.eeos.presentation.topappbar.TopAppBarViewModel + +@SuppressLint("StateFlowValueCalledInComposition") +@Composable +fun HomeScreen( + homeUiState: State, + topAppBarUiState: State, + loadProgramList: (String, String, Int) -> Unit, + onProgramClick: (Int) -> Unit, + refreshProgramList: () -> Unit, + putActiveStatus: (String) -> Unit +) { + val selectedCategory = rememberSaveable { mutableStateOf(categoryChips[0]) } + val selectedProgramStatus = rememberSaveable { mutableStateOf(programStatusChips[0]) } + val page = rememberSaveable { mutableStateOf(0) } + + Scaffold( + topBar = { + EeosTopAppBar( + topAppBarUiState = topAppBarUiState, + putActiveStatus = putActiveStatus + ) + }, + snackbarHost = { SnackbarHost(hostState = topAppBarUiState.value.snackbarHostState) }, + containerColor = colorResource(id = R.color.background) + ) { innerPadding -> + Row( + modifier = Modifier.padding(innerPadding) + ) { + Spacer( + modifier = Modifier.width( + width = dimensionResource(id = R.dimen.margin_common_screen) + ) + ) + Column { + Text( + text = stringResource(R.string.home_program_list), + style = MaterialTheme.typography.headlineSmall, + color = colorResource(id = R.color.paragraph) + ) + Spacer( + modifier = Modifier.height( + height = dimensionResource( + id = R.dimen.margin_home_screen_category_vertical + ) + ) + ) + CategoryChips( + categoryChips = categoryChips, + selectedCategory = selectedCategory, + onCategoryChipClick = { + refreshProgramList() + page.value = 0 + loadProgramList( + category[categoryChips.indexOf(selectedCategory.value)], + programStatus[programStatusChips.indexOf(selectedProgramStatus.value)], + page.value, + ) + } + ) + + Spacer( + modifier = Modifier.height( + height = dimensionResource( + id = R.dimen.margin_home_screen_category_vertical + ) + ) + ) + ProgramStatusChips( + programStatusChips = programStatusChips, + selectedProgramStatus = selectedProgramStatus, + onProgramStatusClick = { + refreshProgramList() + page.value = 0 + loadProgramList( + category[categoryChips.indexOf(selectedCategory.value)], + programStatus[programStatusChips.indexOf(selectedProgramStatus.value)], + page.value, + ) + } + ) + Spacer( + modifier = Modifier.height( + height = dimensionResource( + id = R.dimen.margin_home_screen_program_status_vertical + ) + ) + ) + ProgramLists( + loading = homeUiState.value.isLoading, + empty = homeUiState.value.isEmpty, + programLists = homeUiState.value.programList, + onProgramClick = onProgramClick, + loadMorePrograms = { + loadProgramList( + category[categoryChips.indexOf(selectedCategory.value)], + programStatus[programStatusChips.indexOf(selectedProgramStatus.value)], + ++page.value + ) + } + ) + } + Spacer( + modifier = Modifier.width( + width = dimensionResource(id = R.dimen.margin_common_screen) + ) + ) + } + } +} + +@Preview(showSystemUi = true) +@Composable +private fun HomeScreenPreview() { + MaterialTheme { + HomeScreen( + homeUiState = hiltViewModel().homeUiState.collectAsState(), + topAppBarUiState = hiltViewModel().topAppBarUiState.collectAsState(), + loadProgramList = { p1, p2, p3 -> }, + onProgramClick = { p1 -> }, + refreshProgramList = {}, + putActiveStatus = { p -> } + ) + } +} diff --git a/AOS/app/src/main/java/com/example/eeos/presentation/home/HomeViewModel.kt b/AOS/app/src/main/java/com/example/eeos/presentation/home/HomeViewModel.kt new file mode 100644 index 00000000..755adb5e --- /dev/null +++ b/AOS/app/src/main/java/com/example/eeos/presentation/home/HomeViewModel.kt @@ -0,0 +1,82 @@ +package com.example.eeos.presentation.home + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.example.eeos.consts.category +import com.example.eeos.consts.programStatus +import com.example.eeos.domain.model.Program +import com.example.eeos.domain.repository.ProgramRepository +import dagger.hilt.android.lifecycle.HiltViewModel +import java.io.IOException +import javax.inject.Inject +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch +import retrofit2.HttpException + +data class HomeUiState( + val isEmpty: Boolean = false, + val isLoading: Boolean = false, + val programList: List? = null +) + +@HiltViewModel +class HomeViewModel @Inject constructor( + private val programRepository: ProgramRepository +) : ViewModel() { + private val _homeUiState = MutableStateFlow(HomeUiState()) + val homeUiState = _homeUiState.asStateFlow() + + init { + viewModelScope.launch { + programRepository.getProgramList( + category = category[0], + programStatus = programStatus[0], + page = 0, + size = 7 + ) + } + } + + fun getProgramList(category: String, programStatus: String, page: Int) { + viewModelScope.launch { + programRepository.getProgramList( + category = category, + programStatus = programStatus, + page = page, + size = 7 + ) + .onSuccess { response -> + _homeUiState.update { currentState -> + currentState.copy(programList = response) + } + } + .onFailure { exception -> + when (exception) { + is HttpException -> { + _homeUiState.update { currentState -> + currentState.copy( + isEmpty = true + ) + } + } + + is IOException -> { + _homeUiState.update { currentState -> + currentState.copy( + isEmpty = true + ) + } + } + } + } + } + } + + fun refreshProgramList() { + _homeUiState.update { currentState -> + currentState.copy(programList = null) + } + } +} diff --git a/AOS/app/src/main/java/com/example/eeos/ui/home/ProgramLists.kt b/AOS/app/src/main/java/com/example/eeos/presentation/home/ProgramLists.kt similarity index 56% rename from AOS/app/src/main/java/com/example/eeos/ui/home/ProgramLists.kt rename to AOS/app/src/main/java/com/example/eeos/presentation/home/ProgramLists.kt index c5a66423..97a27ed3 100644 --- a/AOS/app/src/main/java/com/example/eeos/ui/home/ProgramLists.kt +++ b/AOS/app/src/main/java/com/example/eeos/presentation/home/ProgramLists.kt @@ -1,4 +1,4 @@ -package com.example.eeos.ui.home +package com.example.eeos.presentation.home import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement @@ -7,28 +7,49 @@ import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.material3.Divider import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.snapshotFlow import androidx.compose.ui.Modifier import androidx.compose.ui.res.colorResource import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.tooling.preview.Preview import com.example.eeos.R +import com.example.eeos.domain.model.Program @Composable -fun ProgramLists(programLists: List, onProgramClick: () -> Unit) { - LazyColumn { - items(programLists) { program -> - Program(program, onProgramClick) +fun ProgramLists( + loading: Boolean, + empty: Boolean, + programLists: List?, + onProgramClick: (Int) -> Unit, + loadMorePrograms: () -> Unit +) { + val programListState = rememberLazyListState() + + if (programLists != null) { + LazyColumn(state = programListState) { + items(programLists) { program -> + Program(program, onProgramClick) + } } } + + programListState.OnBottomReached( + loadMorePrograms = loadMorePrograms + ) } @Composable -private fun Program(program: ProgramData, onProgramClick: () -> Unit) { +private fun Program(program: Program, onProgramClick: (Int) -> Unit) { Column { Divider( thickness = dimensionResource(id = R.dimen.width_stroke_0_7dp), @@ -45,11 +66,11 @@ private fun Program(program: ProgramData, onProgramClick: () -> Unit) { width = dimensionResource(id = R.dimen.width_home_screen_program), height = dimensionResource(id = R.dimen.height_home_screen_program) ) - .clickable { onProgramClick(/* TODO : programId 넘겨주기*/) }, + .clickable { onProgramClick(program.programId) }, verticalArrangement = Arrangement.Center ) { Text( - text = program.date, + text = program.deadLine, style = MaterialTheme.typography.bodyMedium, color = colorResource(R.color.paragraph) ) @@ -67,13 +88,36 @@ private fun Program(program: ProgramData, onProgramClick: () -> Unit) { } } +@Composable +private fun LazyListState.OnBottomReached( + loadMorePrograms: () -> Unit +) { + val shouldLoadMore = remember { + derivedStateOf { + val lastVisibleItem = layoutInfo.visibleItemsInfo.lastOrNull() ?: return@derivedStateOf true + + lastVisibleItem.index == layoutInfo.totalItemsCount - 1 + } + } + + LaunchedEffect(shouldLoadMore) { + snapshotFlow { shouldLoadMore.value } + .collect { + if (it) loadMorePrograms() + } + } +} + @Preview(showBackground = true) @Composable private fun ProgramPreview() { MaterialTheme { ProgramLists( + loading = false, + empty = false, programLists = listOf(), - onProgramClick = {} + onProgramClick = {}, + loadMorePrograms = {} ) } } diff --git a/AOS/app/src/main/java/com/example/eeos/ui/home/ProgramStatusChips.kt b/AOS/app/src/main/java/com/example/eeos/presentation/home/ProgramStatusChips.kt similarity index 83% rename from AOS/app/src/main/java/com/example/eeos/ui/home/ProgramStatusChips.kt rename to AOS/app/src/main/java/com/example/eeos/presentation/home/ProgramStatusChips.kt index 1f3c1bf8..a059cf0e 100644 --- a/AOS/app/src/main/java/com/example/eeos/ui/home/ProgramStatusChips.kt +++ b/AOS/app/src/main/java/com/example/eeos/presentation/home/ProgramStatusChips.kt @@ -1,4 +1,4 @@ -package com.example.eeos.ui.home +package com.example.eeos.presentation.home import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Row @@ -20,14 +20,18 @@ import com.example.eeos.R @Composable fun ProgramStatusChips( programStatusChips: List, - selectedProgramStatus: MutableState + selectedProgramStatus: MutableState, + onProgramStatusClick: () -> Unit ) { Row { programStatusChips.forEach { chip -> ProgramStatusChip( chipName = chip, isSelected = chip == selectedProgramStatus.value, - onClick = { selectedProgramStatus.value = chip } + onProgramStatusClick = { + selectedProgramStatus.value = chip + onProgramStatusClick() + } ) Spacer( modifier = Modifier.width( @@ -42,7 +46,7 @@ fun ProgramStatusChips( private fun ProgramStatusChip( chipName: String, isSelected: Boolean, - onClick: () -> Unit, + onProgramStatusClick: () -> Unit, ) { /*val interactionSource = remember { MutableInteractionSource() } val isFocused by interactionSource.collectIsFocusedAsState()*/ @@ -58,7 +62,7 @@ private fun ProgramStatusChip( color = textColor, style = MaterialTheme.typography.titleSmall, modifier = Modifier.clickable { - onClick() + onProgramStatusClick() } ) } @@ -72,7 +76,8 @@ private fun ProgramStatusChipsPreview() { stringResource(id = R.string.home_program_status_ing), stringResource(id = R.string.home_program_status_ends) ), - selectedProgramStatus = rememberSaveable { mutableStateOf("진행 중") } + selectedProgramStatus = rememberSaveable { mutableStateOf("진행 중") }, + onProgramStatusClick = {} ) } } @@ -83,7 +88,7 @@ private fun ProgramStatusChipPreview() { MaterialTheme { ProgramStatusChip( chipName = "주간 발표", - onClick = {}, + onProgramStatusClick = {}, isSelected = true ) } diff --git a/AOS/app/src/main/java/com/example/eeos/ui/login/LoginScreen.kt b/AOS/app/src/main/java/com/example/eeos/presentation/login/LoginScreen.kt similarity index 97% rename from AOS/app/src/main/java/com/example/eeos/ui/login/LoginScreen.kt rename to AOS/app/src/main/java/com/example/eeos/presentation/login/LoginScreen.kt index c822b24b..b8d633e4 100644 --- a/AOS/app/src/main/java/com/example/eeos/ui/login/LoginScreen.kt +++ b/AOS/app/src/main/java/com/example/eeos/presentation/login/LoginScreen.kt @@ -1,4 +1,4 @@ -package com.example.eeos.ui.login +package com.example.eeos.presentation.login import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Box diff --git a/AOS/app/src/main/java/com/example/eeos/ui/theme/Color.kt b/AOS/app/src/main/java/com/example/eeos/presentation/theme/Color.kt similarity index 84% rename from AOS/app/src/main/java/com/example/eeos/ui/theme/Color.kt rename to AOS/app/src/main/java/com/example/eeos/presentation/theme/Color.kt index 9f608fd5..a4baeeb2 100644 --- a/AOS/app/src/main/java/com/example/eeos/ui/theme/Color.kt +++ b/AOS/app/src/main/java/com/example/eeos/presentation/theme/Color.kt @@ -1,4 +1,4 @@ -package com.example.eeos.ui.theme +package com.example.eeos.presentation.theme import androidx.compose.ui.graphics.Color diff --git a/AOS/app/src/main/java/com/example/eeos/ui/theme/Theme.kt b/AOS/app/src/main/java/com/example/eeos/presentation/theme/Theme.kt similarity index 97% rename from AOS/app/src/main/java/com/example/eeos/ui/theme/Theme.kt rename to AOS/app/src/main/java/com/example/eeos/presentation/theme/Theme.kt index 8479eddf..c9d3677d 100644 --- a/AOS/app/src/main/java/com/example/eeos/ui/theme/Theme.kt +++ b/AOS/app/src/main/java/com/example/eeos/presentation/theme/Theme.kt @@ -1,4 +1,4 @@ -package com.example.eeos.ui.theme +package com.example.eeos.presentation.theme import android.app.Activity import androidx.compose.foundation.isSystemInDarkTheme diff --git a/AOS/app/src/main/java/com/example/eeos/ui/theme/Type.kt b/AOS/app/src/main/java/com/example/eeos/presentation/theme/Type.kt similarity index 98% rename from AOS/app/src/main/java/com/example/eeos/ui/theme/Type.kt rename to AOS/app/src/main/java/com/example/eeos/presentation/theme/Type.kt index 32944429..93ef5a8f 100644 --- a/AOS/app/src/main/java/com/example/eeos/ui/theme/Type.kt +++ b/AOS/app/src/main/java/com/example/eeos/presentation/theme/Type.kt @@ -1,4 +1,4 @@ -package com.example.eeos.ui.theme +package com.example.eeos.presentation.theme import androidx.compose.material3.Typography import androidx.compose.ui.text.TextStyle diff --git a/AOS/app/src/main/java/com/example/eeos/ui/common/MemberStatusButtons.kt b/AOS/app/src/main/java/com/example/eeos/presentation/topappbar/ActiveStatusButtons.kt similarity index 75% rename from AOS/app/src/main/java/com/example/eeos/ui/common/MemberStatusButtons.kt rename to AOS/app/src/main/java/com/example/eeos/presentation/topappbar/ActiveStatusButtons.kt index 57b4b761..a5871ae1 100644 --- a/AOS/app/src/main/java/com/example/eeos/ui/common/MemberStatusButtons.kt +++ b/AOS/app/src/main/java/com/example/eeos/presentation/topappbar/ActiveStatusButtons.kt @@ -1,4 +1,4 @@ -package com.example.eeos.ui.common +package com.example.eeos.presentation.topappbar import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.layout.Column @@ -13,26 +13,28 @@ import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.res.colorResource import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.example.eeos.R - -private val memberStatusList = listOf("AM", "RM", "CM", "OB") +import com.example.eeos.consts.MemberStatus @Composable -fun MemberStatusButtons( +fun ActiveStatusButtons( memberStatus: String, - onClick: () -> Unit ) { + val tempActiveStatus = remember { mutableStateOf(memberStatus) } + Column { Row { MemberStatusButton( - buttonText = memberStatusList[0], - isSelected = memberStatusList[0] == memberStatus, - onClick = onClick + buttonText = MemberStatus.AM, + isSelected = MemberStatus.AM == tempActiveStatus.value, + onClick = { tempActiveStatus.value = MemberStatus.AM } ) Spacer( modifier = Modifier.width( @@ -42,9 +44,9 @@ fun MemberStatusButtons( ) ) MemberStatusButton( - buttonText = memberStatusList[1], - isSelected = memberStatusList[1] == memberStatus, - onClick = onClick + buttonText = MemberStatus.RM, + isSelected = MemberStatus.RM == tempActiveStatus.value, + onClick = { tempActiveStatus.value = MemberStatus.RM } ) } Spacer( @@ -54,9 +56,9 @@ fun MemberStatusButtons( ) Row { MemberStatusButton( - buttonText = memberStatusList[2], - isSelected = memberStatusList[2] == memberStatus, - onClick = onClick + buttonText = MemberStatus.CM, + isSelected = MemberStatus.CM == tempActiveStatus.value, + onClick = { tempActiveStatus.value = MemberStatus.CM } ) Spacer( modifier = Modifier.width( @@ -66,9 +68,9 @@ fun MemberStatusButtons( ) ) MemberStatusButton( - buttonText = memberStatusList[3], - isSelected = memberStatusList[3] == memberStatus, - onClick = onClick + buttonText = MemberStatus.OB, + isSelected = MemberStatus.OB == tempActiveStatus.value, + onClick = { tempActiveStatus.value = MemberStatus.OB } ) } } @@ -115,13 +117,12 @@ private fun MemberStatusButton( } } -@Preview(showSystemUi = true) +@Preview(showBackground = true) @Composable private fun MemberStatusButtonsPreview() { MaterialTheme { - MemberStatusButtons( + ActiveStatusButtons( memberStatus = "AM", - onClick = {} ) } } diff --git a/AOS/app/src/main/java/com/example/eeos/presentation/topappbar/ActiveStatusDialog.kt b/AOS/app/src/main/java/com/example/eeos/presentation/topappbar/ActiveStatusDialog.kt new file mode 100644 index 00000000..14fe33f2 --- /dev/null +++ b/AOS/app/src/main/java/com/example/eeos/presentation/topappbar/ActiveStatusDialog.kt @@ -0,0 +1,155 @@ +package com.example.eeos.presentation.topappbar + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Card +import androidx.compose.material3.CardDefaults +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.colorResource +import androidx.compose.ui.res.dimensionResource +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.window.Dialog +import com.example.eeos.R + +@Composable +fun ActiveStatusDialog( + name: String, + activeStatus: String, + onSaveStatusBtnClick: () -> Unit, + onDismissRequest: () -> Unit +) { + Dialog(onDismissRequest = onDismissRequest) { + Card( + modifier = Modifier + .size( + width = dimensionResource( + id = R.dimen.size_member_status_dialog_background_width + ), + height = dimensionResource( + id = R.dimen.size_member_status_dialog_background_height + ) + ), + shape = RoundedCornerShape(dimensionResource(id = R.dimen.size_corner_20dp)), + colors = CardDefaults.cardColors( + containerColor = colorResource(R.color.background), + contentColor = colorResource(R.color.paragraph) + ) + ) { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + modifier = Modifier.padding( + top = dimensionResource(id = R.dimen.margin_member_status_dialog_background_top), + start = dimensionResource( + id = R.dimen.margin_member_status_dialog_background_side + ), + end = dimensionResource( + id = R.dimen.margin_member_status_dialog_background_side + ), + bottom = dimensionResource( + id = R.dimen.margin_member_status_dialog_background_bottom + ) + ) + ) { + Row( + verticalAlignment = Alignment.CenterVertically + ) { + Spacer( + modifier = Modifier.width( + dimensionResource( + id = R.dimen.margin_member_status_dialog_space_between_text_and_background + ) + ) + ) + Text( + text = name, + style = MaterialTheme.typography.bodyLarge + ) + Spacer( + modifier = Modifier.width( + dimensionResource( + id = R.dimen.margin_member_status_dialog_space_between_text_and_logout_btn + ) + ) + ) + Icon( + painter = painterResource( + R.drawable.activestatusdialog_button_x_20dp + ), + contentDescription = stringResource( + id = R.string.home_dialog_x_button + ), + modifier = Modifier.clickable { + onDismissRequest() + } + ) + } + Spacer( + modifier = Modifier.height( + dimensionResource( + id = R.dimen.margin_member_status_dialog_space_between_texts + ) + ) + ) + Text( + text = stringResource(id = R.string.active_status_dialog_description), + style = MaterialTheme.typography.bodySmall + ) + Spacer( + modifier = Modifier.height( + dimensionResource( + id = R.dimen.margin_member_status_dialog_space_between_text_and_status_button + ) + ) + ) + ActiveStatusButtons(activeStatus) + Spacer( + modifier = Modifier.height( + dimensionResource( + id = R.dimen.margin_active_status_dialog_between_status_button_and_long_button + ) + ) + ) + SaveActiveStatusButton( + onClick = onSaveStatusBtnClick + ) + Spacer( + modifier = Modifier.height( + dimensionResource( + id = R.dimen.margin_active_status_dialog_between_long_buttons + ) + ) + ) + LogoutButton( + onClick = {} + ) + } + } + } +} + +@Preview(showSystemUi = true) +@Composable +private fun MemberStatusDialogPreview() { + MaterialTheme { + ActiveStatusDialog( + name = "24기 장현지", + activeStatus = "AM", + onSaveStatusBtnClick = {}, + onDismissRequest = {} + ) + } +} diff --git a/AOS/app/src/main/java/com/example/eeos/presentation/topappbar/ActiveStatusLongButtons.kt b/AOS/app/src/main/java/com/example/eeos/presentation/topappbar/ActiveStatusLongButtons.kt new file mode 100644 index 00000000..f536005b --- /dev/null +++ b/AOS/app/src/main/java/com/example/eeos/presentation/topappbar/ActiveStatusLongButtons.kt @@ -0,0 +1,58 @@ +package com.example.eeos.presentation.topappbar + +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.colorResource +import androidx.compose.ui.res.dimensionResource +import androidx.compose.ui.res.stringResource +import com.example.eeos.R + +@Composable +fun SaveActiveStatusButton(onClick: () -> Unit) { + Button( + onClick = onClick, + modifier = Modifier + .size( + width = dimensionResource(id = R.dimen.width_active_status_dialog_long_button), + height = dimensionResource(id = R.dimen.height_active_status_dialog_long_button) + ), + shape = RoundedCornerShape(dimensionResource(id = R.dimen.size_corner_20dp)), + colors = ButtonDefaults.buttonColors( + containerColor = colorResource(id = R.color.tertiary_regular), + contentColor = colorResource(id = R.color.background) + ) + ) { + Text( + text = stringResource(id = R.string.active_status_dialog_save_active_status), + style = MaterialTheme.typography.labelMedium + ) + } +} + +@Composable +fun LogoutButton(onClick: () -> Unit) { + Button( + onClick = onClick, + modifier = Modifier + .size( + width = dimensionResource(id = R.dimen.width_active_status_dialog_long_button), + height = dimensionResource(id = R.dimen.height_active_status_dialog_long_button) + ), + shape = RoundedCornerShape(dimensionResource(id = R.dimen.size_corner_20dp)), + colors = ButtonDefaults.buttonColors( + containerColor = colorResource(id = R.color.paragraph), + contentColor = colorResource(id = R.color.background) + ) + ) { + Text( + text = stringResource(id = R.string.active_status_dialog_logout), + style = MaterialTheme.typography.labelMedium + ) + } +} diff --git a/AOS/app/src/main/java/com/example/eeos/ui/common/EeosTopAppBar.kt b/AOS/app/src/main/java/com/example/eeos/presentation/topappbar/EeosTopAppBar.kt similarity index 68% rename from AOS/app/src/main/java/com/example/eeos/ui/common/EeosTopAppBar.kt rename to AOS/app/src/main/java/com/example/eeos/presentation/topappbar/EeosTopAppBar.kt index 48925f53..ba2de6b0 100644 --- a/AOS/app/src/main/java/com/example/eeos/ui/common/EeosTopAppBar.kt +++ b/AOS/app/src/main/java/com/example/eeos/presentation/topappbar/EeosTopAppBar.kt @@ -1,4 +1,4 @@ -package com.example.eeos.ui.common +package com.example.eeos.presentation.topappbar import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Spacer @@ -8,33 +8,33 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBarDefaults import androidx.compose.runtime.Composable +import androidx.compose.runtime.State +import androidx.compose.runtime.collectAsState import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.res.colorResource import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview +import androidx.hilt.navigation.compose.hiltViewModel import com.example.eeos.R @OptIn(ExperimentalMaterial3Api::class) @Composable -fun EeosTopAppBar() { - val memberStatus: List = listOf( - stringResource(R.string.home_dialog_member_status_am), - stringResource(R.string.home_dialog_member_status_rm), - stringResource(R.string.home_dialog_member_status_cm), - stringResource(R.string.home_dialog_member_status_ob), - ) - +fun EeosTopAppBar( + topAppBarUiState: State, + putActiveStatus: (String) -> Unit +) { val memberStatusDialogState = remember { mutableStateOf(false) } if (memberStatusDialogState.value) { - MemberStatusDialog( - onStatusBtnClick = {}, + ActiveStatusDialog( + name = topAppBarUiState.value.name, + activeStatus = topAppBarUiState.value.activeStatus, + onSaveStatusBtnClick = { putActiveStatus(topAppBarUiState.value.activeStatus) }, onDismissRequest = { memberStatusDialogState.value = false } ) } @@ -48,9 +48,7 @@ fun EeosTopAppBar() { }, actions = { MemberInfo( - memberStatus = "memberStatus", - generation = 24, - name = "name", + topAppBarUiState = topAppBarUiState, onClick = { memberStatusDialogState.value = true } ) Spacer(modifier = Modifier.width(dimensionResource(id = R.dimen.margin_common_screen))) @@ -65,6 +63,9 @@ fun EeosTopAppBar() { @Composable private fun TopAppBarPreview() { MaterialTheme { - EeosTopAppBar() + EeosTopAppBar( + topAppBarUiState = hiltViewModel().topAppBarUiState.collectAsState(), + putActiveStatus = { p -> } + ) } } diff --git a/AOS/app/src/main/java/com/example/eeos/ui/common/MemberInfo.kt b/AOS/app/src/main/java/com/example/eeos/presentation/topappbar/MemberInfo.kt similarity index 71% rename from AOS/app/src/main/java/com/example/eeos/ui/common/MemberInfo.kt rename to AOS/app/src/main/java/com/example/eeos/presentation/topappbar/MemberInfo.kt index f02f14e6..d22db1ae 100644 --- a/AOS/app/src/main/java/com/example/eeos/ui/common/MemberInfo.kt +++ b/AOS/app/src/main/java/com/example/eeos/presentation/topappbar/MemberInfo.kt @@ -1,4 +1,4 @@ -package com.example.eeos.ui.common +package com.example.eeos.presentation.topappbar import androidx.compose.foundation.Image import androidx.compose.foundation.clickable @@ -9,19 +9,20 @@ import androidx.compose.foundation.layout.width import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.State +import androidx.compose.runtime.collectAsState import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.colorResource import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.painterResource import androidx.compose.ui.tooling.preview.Preview +import androidx.hilt.navigation.compose.hiltViewModel import com.example.eeos.R @Composable fun MemberInfo( - memberStatus: String, - generation: Int, - name: String, + topAppBarUiState: State, onClick: () -> Unit ) { Row( @@ -44,26 +45,14 @@ fun MemberInfo( horizontalAlignment = Alignment.End ) { Text( - text = memberStatus, + text = topAppBarUiState.value.activeStatus, style = MaterialTheme.typography.labelSmall, color = colorResource(id = R.color.paragraph) ) Row { Text( - text = generation.toString() + "기", - style = MaterialTheme.typography.labelSmall, - color = colorResource(id = R.color.paragraph) - ) - Spacer( - modifier = Modifier.width( - dimensionResource( - id = R.dimen.margin_common_screen_member_info_space_between_texts - ) - ) - ) - Text( - text = name, + text = topAppBarUiState.value.name, style = MaterialTheme.typography.labelSmall, color = colorResource(id = R.color.paragraph) ) @@ -77,9 +66,7 @@ fun MemberInfo( private fun MemberInfoPreview() { MaterialTheme { MemberInfo( - memberStatus = "AM", - generation = 24, - name = "인텔리", + topAppBarUiState = hiltViewModel().topAppBarUiState.collectAsState(), onClick = {} ) } diff --git a/AOS/app/src/main/java/com/example/eeos/presentation/topappbar/TopAppBarViewModel.kt b/AOS/app/src/main/java/com/example/eeos/presentation/topappbar/TopAppBarViewModel.kt new file mode 100644 index 00000000..85c412f1 --- /dev/null +++ b/AOS/app/src/main/java/com/example/eeos/presentation/topappbar/TopAppBarViewModel.kt @@ -0,0 +1,108 @@ +package com.example.eeos.presentation.topappbar + +import androidx.compose.material3.SnackbarDuration +import androidx.compose.material3.SnackbarHostState +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.example.eeos.data.model.remote.request.RequestPutActiveStatusDto +import com.example.eeos.domain.repository.InfoRepository +import dagger.hilt.android.lifecycle.HiltViewModel +import java.io.IOException +import javax.inject.Inject +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch +import retrofit2.HttpException + +data class TopAppBarUiState( + val isEmpty: Boolean = false, + val isLoading: Boolean = false, + + val name: String = "", + val activeStatus: String = "", + + val snackbarHostState: SnackbarHostState = SnackbarHostState() +) + +@HiltViewModel +class TopAppBarViewModel @Inject constructor( + private val infoRepository: InfoRepository +) : ViewModel() { + private val _topAppBarUiState = MutableStateFlow(TopAppBarUiState()) + val topAppBarUiState = _topAppBarUiState.asStateFlow() + + init { + getActiveStatus() + } + + private fun getActiveStatus() { + viewModelScope.launch { + infoRepository.getActiveStatus() + .onSuccess { userInfo -> + _topAppBarUiState.update { currentState -> + currentState.copy( + name = userInfo.name, + activeStatus = userInfo.activeStatus + ) + } + } + .onFailure { exception -> + when (exception) { + is HttpException -> { + _topAppBarUiState.update { currentState -> + currentState.copy( + isEmpty = true + ) + } + } + + is IOException -> { + _topAppBarUiState.update { currentState -> + currentState.copy( + isEmpty = true + ) + } + } + } + } + } + } + + fun putActiveStatus(activeStatus: String) { + viewModelScope.launch { + infoRepository.putActiveStatus( + RequestPutActiveStatusDto( + activeStatus = activeStatus + ) + ) + .onSuccess { + getActiveStatus() + _topAppBarUiState.value.snackbarHostState + .showSnackbar( + message = "상태가 변경 되었습니다.", + duration = SnackbarDuration.Long + ) + } + .onFailure { exception -> + when (exception) { + is HttpException -> { + _topAppBarUiState.update { currentState -> + currentState.copy( + isEmpty = true + ) + } + } + + is IOException -> { + _topAppBarUiState.update { currentState -> + currentState.copy( + isEmpty = true + ) + } + } + } + } + } + } +} diff --git a/AOS/app/src/main/java/com/example/eeos/ui/util/NonLazyGrid.kt b/AOS/app/src/main/java/com/example/eeos/presentation/util/ComponentUtil/NonLazyGrid.kt similarity index 96% rename from AOS/app/src/main/java/com/example/eeos/ui/util/NonLazyGrid.kt rename to AOS/app/src/main/java/com/example/eeos/presentation/util/ComponentUtil/NonLazyGrid.kt index 20946a77..f11b050b 100644 --- a/AOS/app/src/main/java/com/example/eeos/ui/util/NonLazyGrid.kt +++ b/AOS/app/src/main/java/com/example/eeos/presentation/util/ComponentUtil/NonLazyGrid.kt @@ -1,4 +1,4 @@ -package com.example.eeos.ui.util +package com.example.eeos.presentation.util.ComponentUtil import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column diff --git a/AOS/app/src/main/java/com/example/eeos/ui/common/MemberStatusDialog.kt b/AOS/app/src/main/java/com/example/eeos/ui/common/MemberStatusDialog.kt deleted file mode 100644 index e6b7054b..00000000 --- a/AOS/app/src/main/java/com/example/eeos/ui/common/MemberStatusDialog.kt +++ /dev/null @@ -1,144 +0,0 @@ -package com.example.eeos.ui.common - -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.width -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material3.Card -import androidx.compose.material3.CardDefaults -import androidx.compose.material3.Icon -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.res.colorResource -import androidx.compose.ui.res.dimensionResource -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.window.Dialog -import com.example.eeos.R - -@Composable -fun MemberStatusDialog( - onStatusBtnClick: () -> Unit, - onDismissRequest: () -> Unit -) { - Dialog(onDismissRequest = onDismissRequest) { - Card( - modifier = Modifier - .size( - width = dimensionResource( - id = R.dimen.size_member_status_dialog_background_width - ), - height = dimensionResource( - id = R.dimen.size_member_status_dialog_background_height - ) - ), - shape = RoundedCornerShape(dimensionResource(id = R.dimen.size_corner_20dp)), - colors = CardDefaults.cardColors( - containerColor = colorResource(R.color.background), - contentColor = colorResource(R.color.paragraph) - ) - ) { - Row { - Spacer( - modifier = Modifier.width( - dimensionResource(id = R.dimen.margin_member_status_dialog_background_side) - ) - ) - Column( - horizontalAlignment = Alignment.CenterHorizontally - ) { - Spacer( - modifier = Modifier.height( - dimensionResource( - id = R.dimen.margin_member_status_dialog_background_top - ) - ) - ) - Row( - verticalAlignment = Alignment.CenterVertically - ) { - Spacer( - modifier = Modifier.width( - dimensionResource( - id = R.dimen.margin_member_status_dialog_space_between_text_and_background - ) - ) - ) - Text( - text = "/* ToDo */", - style = MaterialTheme.typography.bodyLarge - ) - Spacer( - modifier = Modifier.width( - dimensionResource( - id = R.dimen.margin_member_status_dialog_space_between_text_and_logout_btn - ) - ) - ) - Icon( - painter = painterResource( - R.drawable.memberstatusdialog_logoutbutton_24dp - ), - contentDescription = stringResource( - id = R.string.home_dialog_logout_button - ), - modifier = Modifier.clickable { - /* ToDo */ - } - ) - } - Spacer( - modifier = Modifier.height( - dimensionResource( - id = R.dimen.margin_member_status_dialog_space_between_texts - ) - ) - ) - Text( - text = stringResource(id = R.string.home_dialog_description), - style = MaterialTheme.typography.bodySmall - ) - Spacer( - modifier = Modifier.height( - dimensionResource( - id = R.dimen.margin_member_status_dialog_space_between_text_and_status_button - ) - ) - ) - MemberStatusButtons("AM", onStatusBtnClick) - Spacer( - modifier = Modifier.height( - dimensionResource( - id = R.dimen.margin_member_status_dialog_background_bottom - ) - ) - ) - } - Spacer( - modifier = Modifier.width( - dimensionResource(id = R.dimen.margin_member_status_dialog_background_side) - ) - ) - } - } - } -} - -@Preview(showSystemUi = true) -@Composable -private fun MemberStatusDialogPreview() { - MaterialTheme { - MemberStatusDialog( - onStatusBtnClick = {}, - onDismissRequest = {} - ) - } -} diff --git a/AOS/app/src/main/java/com/example/eeos/ui/detail/AttendStatusButtons.kt b/AOS/app/src/main/java/com/example/eeos/ui/detail/AttendStatusButtons.kt deleted file mode 100644 index c139473c..00000000 --- a/AOS/app/src/main/java/com/example/eeos/ui/detail/AttendStatusButtons.kt +++ /dev/null @@ -1,71 +0,0 @@ -package com.example.eeos.ui.detail - -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material3.MaterialTheme -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.res.colorResource -import androidx.compose.ui.res.dimensionResource -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.tooling.preview.Preview -import com.example.eeos.R - -@Composable -fun AttendStatusButtons() { - Row( - modifier = Modifier.background( - color = colorResource(R.color.gray_100), - shape = RoundedCornerShape(dimensionResource(id = R.dimen.size_corner_20dp)), - ) - ) { - AttendButton() {} - LateComeButton() {} - AbsentButton() {} - } -} - -@Composable -private fun AttendButton(onClick: () -> Unit) { - AttendStatusButton( - buttonText = stringResource(R.string.detail_attendees), - contentColor = colorResource(R.color.success_strong), - containerColor = colorResource(R.color.success_light), - backgroundColor = colorResource(R.color.gray_100), - isSelected = true, - onClick = onClick - ) -} - -@Composable -private fun LateComeButton(onClick: () -> Unit) { - AttendStatusButton( - buttonText = stringResource(R.string.detail_latecomers), - contentColor = colorResource(R.color.warning_strong), - containerColor = colorResource(R.color.warning_light), - backgroundColor = colorResource(R.color.gray_100), - isSelected = false, - onClick = onClick - ) -} - -@Composable -private fun AbsentButton(onClick: () -> Unit) { - AttendStatusButton( - buttonText = stringResource(R.string.detail_absentees), - contentColor = colorResource(R.color.action), - containerColor = colorResource(R.color.action_light), - backgroundColor = colorResource(R.color.gray_100), - isSelected = false, - onClick = onClick - ) -} - -@Preview(showBackground = true) -@Composable -private fun AttendStatusButtonsPreview() { - MaterialTheme { - AttendStatusButtons() - } -} diff --git a/AOS/app/src/main/java/com/example/eeos/ui/detail/MemberData.kt b/AOS/app/src/main/java/com/example/eeos/ui/detail/MemberData.kt deleted file mode 100644 index 0a99d4d6..00000000 --- a/AOS/app/src/main/java/com/example/eeos/ui/detail/MemberData.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.example.eeos.ui.detail - -data class MemberData( - val generation: Int, - val name: String, - val attendStatus: AttendStatus -) - -enum class AttendStatus { - ATTEND, ABSENT, PERCEIVE, NO_RESPONSE, NON_RELATED -} diff --git a/AOS/app/src/main/java/com/example/eeos/ui/home/HomeScreen.kt b/AOS/app/src/main/java/com/example/eeos/ui/home/HomeScreen.kt deleted file mode 100644 index c401184a..00000000 --- a/AOS/app/src/main/java/com/example/eeos/ui/home/HomeScreen.kt +++ /dev/null @@ -1,134 +0,0 @@ -package com.example.eeos.ui.home - -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.width -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Scaffold -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.saveable.rememberSaveable -import androidx.compose.ui.Modifier -import androidx.compose.ui.res.colorResource -import androidx.compose.ui.res.dimensionResource -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.tooling.preview.Preview -import com.example.eeos.R -import com.example.eeos.ui.common.EeosTopAppBar - -val programLists: List = listOf( - ProgramData( - date = "2023년 11월 06일 (월)", - title = "오늘의 행사 두구두구", - category = "주간 발표", - isEnd = false - ), - ProgramData( - date = "2023년 11월 06일 (월)", - title = "오늘의 행사 두구두구", - category = "주간 발표", - isEnd = false - ), - ProgramData( - date = "2023년 11월 06일 (월)", - title = "오늘의 행사 두구두구", - category = "주간 발표", - isEnd = false - ), -) - -@OptIn(ExperimentalMaterial3Api::class) -@Composable -fun HomeScreen( - onProgramClick: () -> Unit -) { - val categoryChips: List = listOf( - stringResource(R.string.home_tab_all), - stringResource(R.string.home_tab_presentation), - stringResource(R.string.home_tab_leaders), - stringResource(R.string.home_tab_party_department), - stringResource(R.string.home_tab_others) - ) - val programStatusChips: List = listOf( - stringResource(id = R.string.home_program_status_ing), - stringResource(id = R.string.home_program_status_ends) - ) - - val selectedCategory = rememberSaveable { mutableStateOf(categoryChips[0]) } - val selectedProgramStatus = rememberSaveable { mutableStateOf(programStatusChips[0]) } - - Scaffold( - topBar = { - EeosTopAppBar() - }, - containerColor = colorResource(id = R.color.background) - ) { innerPadding -> - Row( - modifier = Modifier.padding(innerPadding) - ) { - Spacer( - modifier = Modifier.width( - width = dimensionResource(id = R.dimen.margin_common_screen) - ) - ) - Column { - Text( - text = stringResource(R.string.home_program_list), - style = MaterialTheme.typography.headlineSmall, - color = colorResource(id = R.color.paragraph) - ) - Spacer( - modifier = Modifier.height( - height = dimensionResource( - id = R.dimen.margin_home_screen_category_vertical - ) - ) - ) - CategoryChips(categoryChips = categoryChips, selectedCategory = selectedCategory) - - Spacer( - modifier = Modifier.height( - height = dimensionResource( - id = R.dimen.margin_home_screen_category_vertical - ) - ) - ) - ProgramStatusChips( - programStatusChips = programStatusChips, - selectedProgramStatus = selectedProgramStatus - ) - Spacer( - modifier = Modifier.height( - height = dimensionResource( - id = R.dimen.margin_home_screen_program_status_vertical - ) - ) - ) - ProgramLists( - programLists = programLists, - onProgramClick = onProgramClick - ) - } - Spacer( - modifier = Modifier.width( - width = dimensionResource(id = R.dimen.margin_common_screen) - ) - ) - } - } -} - -@Preview(showSystemUi = true) -@Composable -private fun HomeScreenPreview() { - MaterialTheme { - HomeScreen( - onProgramClick = {} - ) - } -} diff --git a/AOS/app/src/main/java/com/example/eeos/ui/home/ProgramData.kt b/AOS/app/src/main/java/com/example/eeos/ui/home/ProgramData.kt deleted file mode 100644 index 1d412ac7..00000000 --- a/AOS/app/src/main/java/com/example/eeos/ui/home/ProgramData.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.example.eeos.ui.home - -data class ProgramData( - val date: String, - val title: String, - val category: String, - val isEnd: Boolean -) diff --git a/AOS/app/src/main/res/drawable/activestatusdialog_button_x_20dp.xml b/AOS/app/src/main/res/drawable/activestatusdialog_button_x_20dp.xml new file mode 100644 index 00000000..c2877d0e --- /dev/null +++ b/AOS/app/src/main/res/drawable/activestatusdialog_button_x_20dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/AOS/app/src/main/res/values/colors.xml b/AOS/app/src/main/res/values/colors.xml index a3c6f424..7bf335fc 100644 --- a/AOS/app/src/main/res/values/colors.xml +++ b/AOS/app/src/main/res/values/colors.xml @@ -17,6 +17,7 @@ #FFBAE8E8 + #FF70B5B5 #FF689696 diff --git a/AOS/app/src/main/res/values/dimension.xml b/AOS/app/src/main/res/values/dimension.xml index 32da9a35..08a3734e 100644 --- a/AOS/app/src/main/res/values/dimension.xml +++ b/AOS/app/src/main/res/values/dimension.xml @@ -27,12 +27,12 @@ 28dp - + 300dp - 238dp + 345dp 17dp 15dp - 16dp + 14dp 88dp 76dp @@ -45,6 +45,11 @@ 14dp 15dp + 21dp + 275dp + 40dp + 9dp + 85dp @@ -85,10 +90,21 @@ 30dp + + 121dp + 300dp + + 104dp + + 80dp + 35dp + 10dp + + 7dp 10dp 11dp 20dp diff --git a/AOS/app/src/main/res/values/strings.xml b/AOS/app/src/main/res/values/strings.xml index e11bf3f4..40eed806 100644 --- a/AOS/app/src/main/res/values/strings.xml +++ b/AOS/app/src/main/res/values/strings.xml @@ -18,12 +18,15 @@ - - 본인의 회원 상태를 선택해 주세요. - AM - RM&DM - CM - OB + + 본인의 회원 상태를 선택해 주세요. + AM + RM&DM + CM + OB + + 회원 상태 저장 + 로그아웃 @@ -43,8 +46,16 @@ 본인의 출석 상태를 선택해 주세요. + + + 출석 상태가 "%1$s"으로 변경됩니다. + 취소 + 확인 + + + - 로그아웃 하기 + 다이얼로그 끄기 Bottom Sheet를 올리면 출석 상태를 선택할 수 있습니다. diff --git a/AOS/build.gradle b/AOS/build.gradle index ab91fadf..b0127752 100644 --- a/AOS/build.gradle +++ b/AOS/build.gradle @@ -4,4 +4,6 @@ plugins { id 'com.android.library' version '8.0.1' apply false id 'org.jetbrains.kotlin.android' version '1.7.20' apply false id "org.jlleitschuh.gradle.ktlint" version '11.6.1' + id 'org.jetbrains.kotlin.plugin.serialization' version '1.7.20' + id 'com.google.dagger.hilt.android' version '2.44' apply false } \ No newline at end of file