Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,8 @@ import com.yapp.plus.feature.main.board.model.BoardNoticeUiModel
import com.yapp.plus.feature.main.board.model.NoticeUiState
import com.yapp.plus.feature.main.home.AvailableHomePreview
import com.yapp.plus.feature.main.home.HomeAttendanceCompleteScreen
import com.yapp.plus.feature.main.my.MyPreviewData
import com.yapp.plus.feature.main.my.MyScreen
import com.yapp.plus.feature.main.my.model.MyProfileUiModel
import com.yapp.plus.feature.main.my.model.MyProfileUiState
import com.yapp.plus.feature.main.my.model.MyUiState
import com.yapp.plus.feature.main.schedule.ScheduleScreen
import com.yapp.plus.feature.main.schedule.model.ScheduleSessionUiModel
import com.yapp.plus.feature.main.schedule.model.ScheduleStatusUiModel
Expand Down Expand Up @@ -87,7 +85,9 @@ class ReleaseDesignUiTest {
)
composeRule.setContent {
YappTheme(darkTheme = false, dynamicColor = false) {
Box(Modifier.requiredSize(393.dp, 756.dp).background(Color.White).testTag("screen")) {
Box(
Modifier.requiredSize(393.dp, 756.dp).background(Color.White).testTag("screen"),
) {
CompositionLocalProvider(LocalInspectionMode provides true) {
key(screen) { ReferenceScreen(screen) }
}
Expand Down Expand Up @@ -182,14 +182,7 @@ private fun ReferenceScreen(screen: String) {
"detail" -> BoardScreen(detail = NoticeUiState.Content(referenceNotices().first()))
"my" ->
ReferenceTab(MainTab.My) {
MyScreen(
MyUiState(
profile =
MyProfileUiState.Content(
MyProfileUiModel("김야뿌", "01012345678"),
),
),
)
MyScreen(MyPreviewData.state, onContactClick = {})
}
"settings" ->
SettingsScreen(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,16 @@ import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithContentDescription
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import androidx.compose.ui.test.performScrollTo
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.yapp.plus.core.designsystem.theme.YappTheme
import com.yapp.plus.feature.main.board.BoardScreen
import com.yapp.plus.feature.main.home.HomeScreen
import com.yapp.plus.feature.main.home.model.AttendanceUiState
import com.yapp.plus.feature.main.home.model.HomeNoticeUiState
import com.yapp.plus.feature.main.my.MyRoute
import com.yapp.plus.feature.main.my.MyScreen
import com.yapp.plus.feature.main.my.model.MyAttendanceScoreState
import com.yapp.plus.feature.main.my.model.MyUiState
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
Expand All @@ -30,13 +31,17 @@ class UnconnectedFeaturesUiTest {
}

@Test
fun myDoesNotExposeTestProfilesAndKeepsHistoryUnconnected() {
composeRule.setContent { YappTheme { MyRoute(onSettingsClick = {}) } }
composeRule.onNodeWithText("회원 정보 기능을 준비하고 있어요").assertIsDisplayed()
composeRule.onNodeWithText("YAPP 테스트 프로필").assertDoesNotExist()
composeRule.onNodeWithText("출석 내역").performClick()
composeRule.onNodeWithText("출석 내역 기능을 준비하고 있어요").assertIsDisplayed()
composeRule.onNodeWithText("다시 시도").assertDoesNotExist()
fun myKeepsFigmaUiWithoutInventingUnconnectedValues() {
composeRule.setContent {
YappTheme { MyScreen(state = MyUiState(score = MyAttendanceScoreState.Ineligible)) }
}
composeRule.onNodeWithText("김야뿌").assertDoesNotExist()
composeRule.onNodeWithText("지각 면제권").assertIsDisplayed()
composeRule.onNodeWithText("이용문의").assertIsDisplayed()
composeRule.onNodeWithText("내 출석 현황").assertIsDisplayed()
composeRule.onNodeWithText("0").assertDoesNotExist()
composeRule.onNodeWithText("92점").assertDoesNotExist()
composeRule.onNodeWithText("출석 내역").assertIsDisplayed()
}

@Test
Expand Down
Binary file added docs/screenshots/my-history-empty.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/screenshots/my-history.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/screenshots/my-unconnected.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions docs/wiki/product-policy.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,9 +134,9 @@ Notion 표에는 출석·지각·결석의 시간 구간이 제시되어 있지
- 사용자가 [Figma](https://www.figma.com/design/LUFGoDnVmwJT43QhecBI8x/YAPP-?node-id=222-40302)를 My와 설정의 UI/UX 기준으로 지정했습니다. 설정의 앱 버전, 탈퇴 문의 이메일·메뉴와 확장 안내를 제거합니다.
- 탈퇴 확인은 “정말 탈퇴하시겠어요?”, “탈퇴하시면 모든 정보가 삭제돼요.”, “취소 / 탈퇴하기”를 사용합니다. 서버의 비식별화·이력 보존과 다른 문구임을 설명한 뒤 사용자가 Figma를 따르도록 재확인했습니다. 서버 계약을 바꾸는 결정은 아닙니다.
- 미지원 API는 사용자 요청대로 UI를 유지하고 연결만 비웁니다. 설정의 알림 토글은 Figma 초기 상태에서 화면 내 변경·복원만 제공하며 실제 푸시 설정 저장 성공으로 취급하지 않습니다.
- 프로필 일러스트·회원 배지·기수·직군 컴포넌트와 debug Preview를 준비했습니다. 미확인 값은 `—`로 표시하고 My 메인 화면 조합은 후속입니다.
- 프로필 일러스트·회원 배지·기수·직군 컴포넌트와 debug Preview를 준비했습니다. 미확인 값은 `—`로 표시합니다. 메인은 프로필·출석 요약·규칙·메뉴를 조합하고 별도 출석 내역 화면은 상태·재시도·추가 조회 콜백을 받습니다. ViewModel과 App 이동 연결은 후속입니다.
- 출석 요약 카드·서버 상태별 배지·규칙 팝업과 표시 모델·개별 Preview를 준비했습니다. 값이 없는 요약은 `—`로 표시하고 면제권을 조퇴 횟수로 대체하지 않습니다. 규칙 문구는 Figma 원문을 표시하며 클라이언트 판정 로직으로 추가하지 않습니다.
- 적용 상태: 설정 UI와 My 요약 컴포넌트까지 반영합니다. My·출석 조회와 나머지 화면 연결은 후속 PR입니다. 근거는 [이슈 #160](https://github.com/YAPP-admin/yapp-plus-android/issues/160)과 DEC-0043입니다.
- 적용 상태: 설정과 My 메인·별도 출석 내역 UI를 반영했습니다. 실제 조회·화면 이동 연결은 후속 PR입니다. 근거는 [이슈 #160](https://github.com/YAPP-admin/yapp-plus-android/issues/160)과 DEC-0043입니다.

## 계정·기수 통합 탈퇴

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.yapp.plus.feature.main.my

import com.yapp.plus.domain.attendance.AttendanceScore
import com.yapp.plus.feature.main.my.model.MyAttendanceHistoryItem
import com.yapp.plus.feature.main.my.model.MyAttendanceHistoryState
import com.yapp.plus.feature.main.my.model.MyAttendanceScoreState
import com.yapp.plus.feature.main.my.model.MyAttendanceStatus
import com.yapp.plus.feature.main.my.model.MyProfileUiModel
import com.yapp.plus.feature.main.my.model.MyProfileUiState
import com.yapp.plus.feature.main.my.model.MySessionSummary
import com.yapp.plus.feature.main.my.model.MyUiState

object MyPreviewData {
val profile = MyProfileUiModel("김야뿌", "활동회원", "28기", "Designer")
val score = MyAttendanceScoreState.Content(AttendanceScore(1, 92, 3, 1, 0, 0), 1)
val history =
MyAttendanceHistoryState.Content(
summary = MySessionSummary(12, 6, 50),
items =
listOf(
MyAttendanceHistoryItem(6, "개발-디자인 세션", MyAttendanceStatus.Absent),
MyAttendanceHistoryItem(5, "팀세션", MyAttendanceStatus.Late),
MyAttendanceHistoryItem(4, "기획 세션", MyAttendanceStatus.Late),
MyAttendanceHistoryItem(3, "친바라 세션", MyAttendanceStatus.Absent),
MyAttendanceHistoryItem(2, "팀빌딩", MyAttendanceStatus.Present),
MyAttendanceHistoryItem(1, "OT", MyAttendanceStatus.Present),
),
)
val state =
MyUiState(
profile = MyProfileUiState.Content(profile),
score = score,
attendanceHistory = history,
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,31 +3,20 @@ package com.yapp.plus.feature.main.my
import androidx.compose.runtime.Composable
import androidx.compose.ui.tooling.preview.Preview
import com.yapp.plus.core.designsystem.theme.YappTheme
import com.yapp.plus.feature.main.my.model.MyAttendanceHistoryState
import com.yapp.plus.feature.main.my.model.MyProfileUiState
import com.yapp.plus.feature.main.my.model.MyUiState

@Preview(name = "My page", widthDp = 393, heightDp = 692)
@Preview(name = "Tall My page", widthDp = 393, heightDp = 852)
@Preview(name = "Compact My page", widthDp = 320, heightDp = 560)
@Composable
private fun MyScreenPreview() {
YappTheme(darkTheme = false, dynamicColor = false) {
MyScreen(
state =
MyUiState(
profile = MyProfileUiState.Unavailable,
attendanceHistory = MyAttendanceHistoryState.Unavailable,
isAttendanceHistoryExpanded = true,
),
)
MyScreen(state = MyPreviewData.state, onContactClick = {})
}
}

@Preview(name = "Profile preparing", widthDp = 393)
@Preview(name = "Attendance history", widthDp = 393, heightDp = 756)
@Composable
private fun MyProfileUnavailablePreview() {
private fun MyHistoryScreenPreview() {
YappTheme(darkTheme = false, dynamicColor = false) {
MyProfileContent(state = MyProfileUiState.Unavailable, onRetry = {})
MyAttendanceHistoryScreen(MyPreviewData.history, {}, {}, {})
}
}
Original file line number Diff line number Diff line change
@@ -1,32 +1,14 @@
package com.yapp.plus.feature.main.my.components

import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import com.yapp.plus.core.designsystem.theme.YappMainColor
import com.yapp.plus.core.designsystem.theme.YappTheme
import com.yapp.plus.feature.main.my.model.MyAttendanceHistoryItem
import com.yapp.plus.feature.main.my.model.MyAttendanceHistoryState
import com.yapp.plus.feature.main.my.MyPreviewData

@Preview(name = "My attendance history", widthDp = 393)
@Preview(widthDp = 393, heightDp = 702)
@Composable
private fun MyAttendanceHistoryPreview() {
YappTheme(darkTheme = false, dynamicColor = false) {
Column(Modifier.background(YappMainColor.surface)) {
MyAttendanceHistory(
state =
MyAttendanceHistoryState.Content(
listOf(
MyAttendanceHistoryItem(
sessionName = "YAPP 테스트 세션",
checkedInAtLabel = "2026.10.20 (화) 오후 7:00",
),
),
),
onRetry = {},
)
}
MyAttendanceHistory(MyPreviewData.history, showAbsenceWarning = true, onLoadMore = {})
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,13 @@ package com.yapp.plus.feature.main.my.components
import androidx.compose.runtime.Composable
import androidx.compose.ui.tooling.preview.Preview
import com.yapp.plus.core.designsystem.theme.YappTheme
import com.yapp.plus.domain.attendance.AttendanceScore
import com.yapp.plus.feature.main.my.MyPreviewData
import com.yapp.plus.feature.main.my.R
import com.yapp.plus.feature.main.my.model.MyAttendanceScoreState
import com.yapp.plus.feature.main.my.model.MyAttendanceStatus

@Preview @Composable
private fun SummaryPreview() {
YappTheme {
MyAttendanceSummary(
MyAttendanceScoreState.Content(AttendanceScore(1, 100, 3, 0, 0, 0)),
{},
{},
)
}
YappTheme { MyAttendanceSummary(MyPreviewData.score, {}, {}) }
}

@Preview @Composable
Expand All @@ -34,6 +27,26 @@ private fun BadgePreview() {
YappTheme { MyAttendanceBadge(MyAttendanceStatus.Absent) }
}

@Preview @Composable
private fun HistoryRowPreview() {
YappTheme { MyHistoryRow(MyPreviewData.history.items.first()) }
}

@Preview @Composable
private fun EmptyPreview() {
YappTheme { MyHistoryEmpty() }
}

@Preview @Composable
private fun WarningPreview() {
YappTheme { MyAbsenceWarning() }
}

@Preview @Composable
private fun PaginationPreview() {
YappTheme { MyHistoryPagination(MyPreviewData.history.copy(hasLoadMoreError = true), {}) }
}

@Preview @Composable
private fun RequestStatePreview() {
YappTheme { MyRequestState("출석 현황을 불러오지 못했어요", onRetry = {}) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ private fun MyMenuRowPreview() {
YappTheme(darkTheme = false, dynamicColor = false) {
Column(Modifier.background(YappMainColor.surface)) {
MyMenuRow(title = "출석 내역", onClick = {})
MyMenuRow(title = "펼친 출석 내역", onClick = {}, expanded = true)
MyMenuRow(title = "펼친 출석 내역", onClick = {})
MyMenuRow(title = "이용문의", onClick = {})
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package com.yapp.plus.feature.main.my

import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import com.yapp.plus.core.designsystem.components.main.YappDetailHeader
import com.yapp.plus.core.designsystem.theme.YappMainColor
import com.yapp.plus.feature.main.my.components.MyAttendanceHistory
import com.yapp.plus.feature.main.my.components.MyRequestState
import com.yapp.plus.feature.main.my.model.MyAttendanceHistoryState

@Composable
fun MyAttendanceHistoryScreen(
state: MyAttendanceHistoryState,
onBack: () -> Unit,
onRetry: () -> Unit,
onLoadMore: () -> Unit,
modifier: Modifier = Modifier,
absentCount: Int? = null,
) {
Column(modifier.fillMaxSize().background(YappMainColor.surface)) {
YappDetailHeader(
title = stringResource(R.string.my_attendance_history_title),
backDescription = stringResource(R.string.my_back),
onBack = onBack,
)
when (state) {
MyAttendanceHistoryState.NotRequested, MyAttendanceHistoryState.Loading ->
MyRequestState(
message = stringResource(R.string.my_attendance_history_loading),
isLoading = true,
)
MyAttendanceHistoryState.Error ->
MyRequestState(
message = stringResource(R.string.my_attendance_history_error),
onRetry = onRetry,
)
is MyAttendanceHistoryState.Content ->
MyAttendanceHistory(
state = state,
showAbsenceWarning =
absentCount != null && absentCount >= ABSENCE_WARNING_COUNT,
onLoadMore = onLoadMore,
)
}
}
}

private const val ABSENCE_WARNING_COUNT = 10
Original file line number Diff line number Diff line change
@@ -1,22 +1,14 @@
package com.yapp.plus.feature.main.my

import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import com.yapp.plus.feature.main.my.model.MyUiState

@Composable
fun MyRoute(
onSettingsClick: () -> Unit,
modifier: Modifier = Modifier,
) {
var isHistoryExpanded by rememberSaveable { mutableStateOf(false) }
MyScreen(
state = MyUiState(isAttendanceHistoryExpanded = isHistoryExpanded),
onAttendanceHistoryClick = { isHistoryExpanded = !isHistoryExpanded },
onSettingsClick = onSettingsClick,
modifier = modifier,
)
Expand Down
Loading
Loading