diff --git a/app/src/main/java/org/sopt/certi/core/component/bottomsheet/RegisterTestInfoBottomSheet.kt b/app/src/main/java/org/sopt/certi/core/component/bottomsheet/RegisterTestInfoBottomSheet.kt index 3c5607c8..39b9bf6d 100644 --- a/app/src/main/java/org/sopt/certi/core/component/bottomsheet/RegisterTestInfoBottomSheet.kt +++ b/app/src/main/java/org/sopt/certi/core/component/bottomsheet/RegisterTestInfoBottomSheet.kt @@ -2,7 +2,6 @@ package org.sopt.certi.core.component.bottomsheet import androidx.compose.foundation.background import androidx.compose.foundation.border -import androidx.compose.foundation.gestures.detectVerticalDragGestures import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -15,12 +14,13 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.itemsIndexed +import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.verticalScroll import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon import androidx.compose.material3.ModalBottomSheet -import androidx.compose.material3.SheetState import androidx.compose.material3.Text import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.runtime.Composable @@ -33,9 +33,12 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip +import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.vector.ImageVector -import androidx.compose.ui.input.pointer.pointerInput +import androidx.compose.ui.input.nestedscroll.NestedScrollConnection +import androidx.compose.ui.input.nestedscroll.NestedScrollSource +import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.layout.onGloballyPositioned import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalDensity @@ -65,7 +68,6 @@ import org.sopt.certi.ui.theme.CertiTheme @OptIn(ExperimentalMaterial3Api::class) @Composable fun RegisterTestInfoBottomSheet( - sheetState: SheetState, forModify: Boolean, certTitle: String, onConfirm: (city: String, state: String, timeDate: String) -> Unit, @@ -74,6 +76,16 @@ fun RegisterTestInfoBottomSheet( modifier: Modifier = Modifier, certificationData: CertificationData? = null ) { + val sheetState = rememberModalBottomSheetState( + skipPartiallyExpanded = true + ) + + val scrollLock = remember { + object : NestedScrollConnection { + override fun onPostScroll(consumed: Offset, available: Offset, source: NestedScrollSource): Offset { return available } + } + } + val density = LocalDensity.current val scope = rememberCoroutineScope() @@ -155,14 +167,10 @@ fun RegisterTestInfoBottomSheet( Column( modifier = Modifier .wrapContentHeight() - .pointerInput(Unit) { - detectVerticalDragGestures( - onDragEnd = {}, - onDragCancel = {}, - onVerticalDrag = { _, _ -> /* 아무것도 안 함 */ } - ) - } .fillMaxWidth() + .heightIn(max = screenHeightDp(664.dp)) + .nestedScroll(scrollLock) + .verticalScroll(rememberScrollState()) ) { Spacer(Modifier.heightForScreenPercentage(35.dp)) @@ -368,6 +376,7 @@ fun RegisterTestInfoBottomSheet( blur = 20.dp ) .background(CertiTheme.colors.white) + .nestedScroll(scrollLock) ) { itemsIndexed(cityList) { index, placeName -> PlaceItem( @@ -396,6 +405,7 @@ fun RegisterTestInfoBottomSheet( blur = 20.dp ) .background(CertiTheme.colors.white) + .nestedScroll(scrollLock) ) { itemsIndexed(districtList) { index, placeName -> PlaceItem( @@ -485,9 +495,7 @@ fun RegisterTestInfoBottomSheet( } } }, - modifier = Modifier - .fillMaxWidth() - .heightForScreenPercentage(56.dp) + modifier = Modifier.fillMaxWidth() ) Spacer(Modifier.heightForScreenPercentage(24.dp)) @@ -537,12 +545,7 @@ private fun PlaceItem( @Preview @Composable fun RegisterTestInfoBottomSheetPreview() { - val sheetState = rememberModalBottomSheetState( - skipPartiallyExpanded = true - ) - RegisterTestInfoBottomSheet( - sheetState = sheetState, certTitle = "자격증 이름", forModify = false, certificationData = null, diff --git a/app/src/main/java/org/sopt/certi/core/component/chip/CertiDefaultChip.kt b/app/src/main/java/org/sopt/certi/core/component/chip/CertiDefaultChip.kt index 7ab7850d..3b604f70 100644 --- a/app/src/main/java/org/sopt/certi/core/component/chip/CertiDefaultChip.kt +++ b/app/src/main/java/org/sopt/certi/core/component/chip/CertiDefaultChip.kt @@ -9,6 +9,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp @@ -56,12 +57,14 @@ fun CertiDefaultChip( cornerRadius = 12.dp, backgroundColor = backgroundColor ) - .padding(horizontal = screenWidthDp(8.dp), vertical = screenHeightDp(6.dp)) + .padding(horizontal = screenWidthDp(8.dp), vertical = screenHeightDp(4.dp)) ) { Text( text = text, style = textStyle, - color = textColor + color = textColor, + maxLines = 1, + overflow = TextOverflow.Ellipsis ) } } diff --git a/app/src/main/java/org/sopt/certi/core/component/chip/ResetBadge.kt b/app/src/main/java/org/sopt/certi/core/component/chip/ResetBadge.kt new file mode 100644 index 00000000..91513662 --- /dev/null +++ b/app/src/main/java/org/sopt/certi/core/component/chip/ResetBadge.kt @@ -0,0 +1,56 @@ +package org.sopt.certi.core.component.chip + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import org.sopt.certi.core.util.screenWidthDp +import org.sopt.certi.ui.theme.CERTITheme +import org.sopt.certi.ui.theme.CertiTheme + +@Composable +fun ResetBadge( + text: String, + modifier: Modifier = Modifier, + textColor: Color = CertiTheme.colors.white, + textStyle: TextStyle = CertiTheme.typography.caption.regular_10, + backgroundColor: Color = CertiTheme.colors.mainBlue, + radius: Dp = 8.dp, + maxLines: Int = 1, + overFlow: TextOverflow = TextOverflow.Ellipsis +) { + Box( + modifier = modifier + .clip(RoundedCornerShape(radius)) + .background(backgroundColor) + .padding(horizontal = screenWidthDp(4.dp), vertical = screenWidthDp(2.dp)) + ) { + Text( + text = text, + color = textColor, + style = textStyle, + maxLines = maxLines, + overflow = overFlow + ) + } +} + +@Preview(showBackground = true) +@Composable +private fun ResetBadgePreview() { + CERTITheme { + ResetBadge( + text = "1지망" + ) + } +} diff --git a/app/src/main/java/org/sopt/certi/core/component/dialog/CertiDeleteDialog.kt b/app/src/main/java/org/sopt/certi/core/component/dialog/CertiDeleteDialog.kt index 4a6c3f95..f06e3dd3 100644 --- a/app/src/main/java/org/sopt/certi/core/component/dialog/CertiDeleteDialog.kt +++ b/app/src/main/java/org/sopt/certi/core/component/dialog/CertiDeleteDialog.kt @@ -23,6 +23,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Dialog @@ -51,13 +52,15 @@ fun CertiDeleteDialog( Text( text = title, style = CertiTheme.typography.body.semibold_18, - color = CertiTheme.colors.gray600 + color = CertiTheme.colors.gray600, + textAlign = TextAlign.Center ) Spacer(modifier = Modifier.height(screenWidthDp(16.dp))) Text( text = description, style = CertiTheme.typography.caption.regular_14, - color = CertiTheme.colors.gray600 + color = CertiTheme.colors.gray600, + textAlign = TextAlign.Center ) Spacer(modifier = Modifier.height(screenWidthDp(24.dp))) HorizontalDivider( @@ -113,6 +116,7 @@ fun DialogButton( text = text, style = CertiTheme.typography.body.semibold_18, color = textColor, + textAlign = TextAlign.Center, modifier = Modifier.padding(vertical = screenWidthDp(16.dp)) ) } diff --git a/app/src/main/java/org/sopt/certi/core/component/section/CertInfoSection.kt b/app/src/main/java/org/sopt/certi/core/component/section/CertInfoSection.kt index 95fdee99..bc8d4eed 100644 --- a/app/src/main/java/org/sopt/certi/core/component/section/CertInfoSection.kt +++ b/app/src/main/java/org/sopt/certi/core/component/section/CertInfoSection.kt @@ -37,6 +37,7 @@ fun CertInfoSection( text = testInfo, style = CertiTheme.typography.caption.regular_14, color = textColor + ) } } diff --git a/app/src/main/java/org/sopt/certi/core/component/section/CertItemTitleSection.kt b/app/src/main/java/org/sopt/certi/core/component/section/CertItemTitleSection.kt index 1c682128..f2929011 100644 --- a/app/src/main/java/org/sopt/certi/core/component/section/CertItemTitleSection.kt +++ b/app/src/main/java/org/sopt/certi/core/component/section/CertItemTitleSection.kt @@ -1,6 +1,8 @@ package org.sopt.certi.core.component.section import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.ExperimentalLayoutApi +import androidx.compose.foundation.layout.FlowRow import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.material3.Icon @@ -13,9 +15,11 @@ import androidx.compose.ui.res.vectorResource import androidx.compose.ui.unit.dp import org.sopt.certi.R import org.sopt.certi.core.util.noRippleClickable +import org.sopt.certi.core.util.screenHeightDp import org.sopt.certi.core.util.screenWidthDp import org.sopt.certi.ui.theme.CertiTheme +@OptIn(ExperimentalLayoutApi::class) @Composable fun CertItemTitleSection( certName: String, @@ -26,19 +30,25 @@ fun CertItemTitleSection( ) { Row( modifier = modifier, - verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(screenWidthDp(8.dp)) ) { - Text( - text = certName, - style = CertiTheme.typography.subtitle.semibold_20, - color = CertiTheme.colors.black - ) - Text( - text = certType, - style = CertiTheme.typography.caption.regular_12, - color = CertiTheme.colors.black - ) + FlowRow( + horizontalArrangement = Arrangement.spacedBy(screenWidthDp(8.dp)), + verticalArrangement = Arrangement.spacedBy(screenHeightDp(4.dp)) + ) { + Text( + text = certName, + style = CertiTheme.typography.subtitle.semibold_20, + color = CertiTheme.colors.black, + modifier = Modifier.align(Alignment.CenterVertically) + ) + Text( + text = certType, + style = CertiTheme.typography.caption.regular_12, + color = CertiTheme.colors.black, + modifier = Modifier.align(Alignment.CenterVertically) + ) + } Spacer(modifier = Modifier.weight(1f)) diff --git a/app/src/main/java/org/sopt/certi/core/component/section/MyCertificationListItemSection.kt b/app/src/main/java/org/sopt/certi/core/component/section/MyCertificationListItemSection.kt index 73d4fed4..b68f7cdd 100644 --- a/app/src/main/java/org/sopt/certi/core/component/section/MyCertificationListItemSection.kt +++ b/app/src/main/java/org/sopt/certi/core/component/section/MyCertificationListItemSection.kt @@ -3,6 +3,8 @@ package org.sopt.certi.core.component.section import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ExperimentalLayoutApi +import androidx.compose.foundation.layout.FlowRow import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth @@ -26,6 +28,7 @@ import org.sopt.certi.core.component.chip.CertiEditChipType import org.sopt.certi.core.util.heightForScreenPercentage import org.sopt.certi.core.util.noRippleClickable import org.sopt.certi.core.util.roundedBackgroundWithBorder +import org.sopt.certi.core.util.screenHeightDp import org.sopt.certi.core.util.screenWidthDp import org.sopt.certi.core.util.toSpacedDotDate import org.sopt.certi.core.util.widthForScreenPercentage @@ -33,6 +36,7 @@ import org.sopt.certi.domain.model.certification.CertificationData import org.sopt.certi.ui.theme.CertiTheme import java.time.format.DateTimeFormatter +@OptIn(ExperimentalLayoutApi::class) @Composable fun MyCertificationListItemSection( certificationData: CertificationData, @@ -92,30 +96,34 @@ fun MyCertificationListItemSection( Spacer(Modifier.heightForScreenPercentage(12.dp)) - Row( + FlowRow( modifier = Modifier.padding(vertical = screenWidthDp(2.dp)), - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(screenWidthDp(8.dp)) + horizontalArrangement = Arrangement.spacedBy(screenWidthDp(8.dp)), + verticalArrangement = Arrangement.spacedBy(screenHeightDp(4.dp)) ) { if (certificationData.isAcquired) { CertInfoSection( iconRes = R.drawable.ic_date_16, - testInfo = certificationData.acquisitionDate.toSpacedDotDate() + testInfo = certificationData.acquisitionDate.toSpacedDotDate(), + modifier = Modifier.align(Alignment.CenterVertically) ) CertInfoSection( iconRes = R.drawable.ic_level, iconColor = CertiTheme.colors.gray400, testInfo = if (certificationData.grade.isBlank()) stringResource(R.string.acquired_grade_empty_text) else certificationData.grade, - textColor = if (certificationData.grade.isBlank()) CertiTheme.colors.gray200 else CertiTheme.colors.black + textColor = if (certificationData.grade.isBlank()) CertiTheme.colors.gray200 else CertiTheme.colors.black, + modifier = Modifier.align(Alignment.CenterVertically) ) } else { CertInfoSection( iconRes = R.drawable.ic_placemark, - testInfo = certificationData.city + testInfo = certificationData.city, + modifier = Modifier.align(Alignment.CenterVertically) ) CertInfoSection( iconRes = R.drawable.ic_time, - testInfo = certificationData.testTime.format(DateTimeFormatter.ofPattern("HH:mm")) + testInfo = certificationData.testTime.format(DateTimeFormatter.ofPattern("HH:mm")), + modifier = Modifier.align(Alignment.CenterVertically) ) } } @@ -127,7 +135,7 @@ private fun CertificationStatus(acquired: Boolean) { Row( modifier = Modifier .background(color = if (acquired) CertiTheme.colors.mainBlue else CertiTheme.colors.purpleBlue, shape = RoundedCornerShape(100.dp)) - .padding(horizontal = screenWidthDp(6.dp), vertical = screenWidthDp(4.dp)), + .padding(horizontal = screenWidthDp(6.dp), vertical = screenWidthDp(3.dp)), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(4.dp) ) { diff --git a/app/src/main/java/org/sopt/certi/core/component/timepicker/TimePicker.kt b/app/src/main/java/org/sopt/certi/core/component/timepicker/TimePicker.kt index 1d76bf35..66785bb3 100644 --- a/app/src/main/java/org/sopt/certi/core/component/timepicker/TimePicker.kt +++ b/app/src/main/java/org/sopt/certi/core/component/timepicker/TimePicker.kt @@ -25,6 +25,10 @@ import androidx.compose.runtime.setValue import androidx.compose.runtime.snapshotFlow import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.input.nestedscroll.NestedScrollConnection +import androidx.compose.ui.input.nestedscroll.NestedScrollSource +import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview @@ -54,6 +58,18 @@ fun CustomTimePicker( var selectedHour by remember { mutableIntStateOf(convertHour(initialHour)) } var selectedMinute by remember { mutableIntStateOf(if (initialMinute == -1) 0 else initialMinute) } + val scrollLock = remember { + object : NestedScrollConnection { + override fun onPostScroll( + consumed: Offset, + available: Offset, + source: NestedScrollSource + ): Offset { + return available + } + } + } + LaunchedEffect(initialHour, initialMinute) { selectedPeriod = getPeriod(initialHour) selectedHour = convertHour(initialHour) @@ -80,7 +96,9 @@ fun CustomTimePicker( selectedMinute ) }, - modifier = Modifier.widthForScreenPercentage(45.dp) + modifier = Modifier + .widthForScreenPercentage(45.dp) + .nestedScroll(scrollLock) ) Spacer(modifier = Modifier.widthForScreenPercentage(47.dp)) @@ -100,7 +118,9 @@ fun CustomTimePicker( selectedMinute ) }, - modifier = Modifier.widthForScreenPercentage(35.dp) + modifier = Modifier + .widthForScreenPercentage(35.dp) + .nestedScroll(scrollLock) ) Text( @@ -126,7 +146,9 @@ fun CustomTimePicker( selectedMinute ) }, - modifier = Modifier.widthForScreenPercentage(38.dp) + modifier = Modifier + .widthForScreenPercentage(38.dp) + .nestedScroll(scrollLock) ) } } diff --git a/app/src/main/java/org/sopt/certi/data/mapper/todomain/acquisition/AcquisitionDetailResponseMapper.kt b/app/src/main/java/org/sopt/certi/data/mapper/todomain/acquisition/AcquisitionDetailResponseMapper.kt index 1e7a742d..317eb750 100644 --- a/app/src/main/java/org/sopt/certi/data/mapper/todomain/acquisition/AcquisitionDetailResponseMapper.kt +++ b/app/src/main/java/org/sopt/certi/data/mapper/todomain/acquisition/AcquisitionDetailResponseMapper.kt @@ -1,18 +1,18 @@ package org.sopt.certi.data.mapper.todomain.acquisition -import org.sopt.certi.core.util.toLocalDateOrMin import org.sopt.certi.data.remote.dto.response.GetAcquisitionDetailResponseDto import org.sopt.certi.domain.model.certification.CertificationData fun GetAcquisitionDetailResponseDto.toDomain(): CertificationData { return CertificationData( - certificationId = acquisitionId, + certificationId = 0, + acquisitionId = acquisitionId, certificationName = name, index = index, cardFrontImageUrl = cardFrontImageUrl, cardBackImageUrl = cardBackImageUrl, tags = tags, description = description, - createdAt = createdAt.toLocalDateOrMin() + acquisitionDate = acquisitionDate ) } diff --git a/app/src/main/java/org/sopt/certi/data/remote/dto/response/GetAcquisitionDetailResponseDto.kt b/app/src/main/java/org/sopt/certi/data/remote/dto/response/GetAcquisitionDetailResponseDto.kt index d561eb33..a9b81c30 100644 --- a/app/src/main/java/org/sopt/certi/data/remote/dto/response/GetAcquisitionDetailResponseDto.kt +++ b/app/src/main/java/org/sopt/certi/data/remote/dto/response/GetAcquisitionDetailResponseDto.kt @@ -19,6 +19,6 @@ data class GetAcquisitionDetailResponseDto( val tags: List, @SerialName("description") val description: String, - @SerialName("createdAt") - val createdAt: String + @SerialName("acquisitionDate") + val acquisitionDate: String ) diff --git a/app/src/main/java/org/sopt/certi/presentation/type/CertCardColorType.kt b/app/src/main/java/org/sopt/certi/presentation/type/CertCardColorType.kt deleted file mode 100644 index 2e8c65a2..00000000 --- a/app/src/main/java/org/sopt/certi/presentation/type/CertCardColorType.kt +++ /dev/null @@ -1,47 +0,0 @@ -package org.sopt.certi.presentation.type - -import androidx.compose.ui.graphics.Color -import org.sopt.certi.ui.theme.defaultCertiColors - -enum class CertCardColorType( - val certificationIndex: Int, - val textColor: Color, - val subTextColor: Color, - val chipBackgroundColor: Color, - val chipTextColor: Color -) { - BLUE( - certificationIndex = 2, - textColor = defaultCertiColors.white, - subTextColor = defaultCertiColors.white, - chipBackgroundColor = defaultCertiColors.lightPurple, - chipTextColor = defaultCertiColors.mainBlue - ), - SKYBLUE( - certificationIndex = 0, - textColor = defaultCertiColors.gray600, - subTextColor = defaultCertiColors.mainBlue, - chipBackgroundColor = defaultCertiColors.lightPurple, - chipTextColor = defaultCertiColors.mainBlue - ), - WHITE( - certificationIndex = 3, - textColor = defaultCertiColors.gray600, - subTextColor = defaultCertiColors.mainBlue, - chipBackgroundColor = defaultCertiColors.skyBlue, - chipTextColor = defaultCertiColors.purpleWhite - ), - YELLOW( - certificationIndex = 1, - textColor = defaultCertiColors.gray600, - subTextColor = defaultCertiColors.mainBlue, - chipBackgroundColor = defaultCertiColors.lightPurple, - chipTextColor = defaultCertiColors.mainBlue - ); - - companion object { - fun fromIndex(index: Int): CertCardColorType { - return entries.find { it.certificationIndex == index } ?: SKYBLUE - } - } -} diff --git a/app/src/main/java/org/sopt/certi/presentation/ui/activity/ResumeAddActivitiesScreen.kt b/app/src/main/java/org/sopt/certi/presentation/ui/activity/ResumeAddActivitiesScreen.kt index 207364c0..21a3d92c 100644 --- a/app/src/main/java/org/sopt/certi/presentation/ui/activity/ResumeAddActivitiesScreen.kt +++ b/app/src/main/java/org/sopt/certi/presentation/ui/activity/ResumeAddActivitiesScreen.kt @@ -2,10 +2,16 @@ package org.sopt.certi.presentation.ui.activity import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.ime +import androidx.compose.foundation.layout.navigationBars import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.layout.union +import androidx.compose.foundation.layout.windowInsetsPadding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect @@ -56,7 +62,7 @@ fun ResumeAddActivitiesRoute( onActivityValueChange = viewModel::onActivityChanged, onDescriptionValue = viewModel::onDescriptionChanged, onButtonClick = viewModel::addActivity, - modifier = Modifier.padding(padding) + modifier = Modifier.padding(top = padding.calculateTopPadding()) ) } @@ -86,7 +92,7 @@ fun ResumeEditActivitiesRoute( onActivityValueChange = viewModel::onActivityChanged, onDescriptionValue = viewModel::onDescriptionChanged, onButtonClick = viewModel::editActivity, - modifier = Modifier.padding(padding) + modifier = Modifier.padding(top = padding.calculateTopPadding()) ) } @@ -104,70 +110,57 @@ fun ResumeAddActivitiesScreen( modifier: Modifier = Modifier ) { Column( - modifier = modifier.fillMaxSize() + modifier = modifier + .fillMaxSize() + .windowInsetsPadding(WindowInsets.ime.union(WindowInsets.navigationBars)) + .verticalScroll(rememberScrollState()) + .padding(horizontal = screenWidthDp(20.dp)) ) { - LazyColumn( - modifier = Modifier.weight(1f), - contentPadding = PaddingValues( - horizontal = screenWidthDp(20.dp) + Text( + text = stringResource(titleResId), + style = CertiTheme.typography.subtitle.semibold_20, + color = CertiTheme.colors.gray600, + modifier = Modifier.padding( + top = screenHeightDp(60.dp), + bottom = screenHeightDp(24.dp) ) - ) { - item { - Text( - text = stringResource(titleResId), - style = CertiTheme.typography.subtitle.semibold_20, - color = CertiTheme.colors.gray600, - modifier = Modifier.padding( - top = screenHeightDp(60.dp), - bottom = screenHeightDp(24.dp) - ) - ) - } + ) - item { - ResumeDateInputSection( - title = stringResource(R.string.resume_activities_period), - startDate = uiState.startDate, - endDate = uiState.endDate, - onStartDateValueChange = onStartDateValueChange, - onEndDateValueChange = onEndDateValueChange, - modifier = Modifier.padding(bottom = screenHeightDp(36.dp)) - ) - } + ResumeDateInputSection( + title = stringResource(R.string.resume_activities_period), + startDate = uiState.startDate, + endDate = uiState.endDate, + onStartDateValueChange = onStartDateValueChange, + onEndDateValueChange = onEndDateValueChange, + modifier = Modifier.padding(bottom = screenHeightDp(36.dp)) + ) - item { - ResumeTextInputSection( - title = stringResource(R.string.resume_activities_organization), - value = uiState.organizationValue, - onValueChange = onOrganizationValueChange, - maxLength = 10, - modifier = Modifier.padding(bottom = screenHeightDp(36.dp)), - imeAction = ImeAction.Next - ) - } + ResumeTextInputSection( + title = stringResource(R.string.resume_activities_organization), + value = uiState.organizationValue, + onValueChange = onOrganizationValueChange, + maxLength = 10, + modifier = Modifier.padding(bottom = screenHeightDp(36.dp)), + imeAction = ImeAction.Next + ) - item { - ResumeTextInputSection( - title = stringResource(R.string.resume_activities_activity), - value = uiState.activityValue, - onValueChange = onActivityValueChange, - maxLength = 10, - modifier = Modifier.padding(bottom = screenHeightDp(36.dp)), - imeAction = ImeAction.Next - ) - } + ResumeTextInputSection( + title = stringResource(R.string.resume_activities_activity), + value = uiState.activityValue, + onValueChange = onActivityValueChange, + maxLength = 10, + modifier = Modifier.padding(bottom = screenHeightDp(36.dp)), + imeAction = ImeAction.Next + ) - item { - ResumeTextInputSection( - title = stringResource(R.string.resume_activities_description), - value = uiState.descriptionValue, - onValueChange = onDescriptionValue, - maxLength = 16, - modifier = Modifier.padding(bottom = screenHeightDp(36.dp)), - imeAction = ImeAction.Done - ) - } - } + ResumeTextInputSection( + title = stringResource(R.string.resume_activities_description), + value = uiState.descriptionValue, + onValueChange = onDescriptionValue, + maxLength = 16, + modifier = Modifier.padding(bottom = screenHeightDp(36.dp)), + imeAction = ImeAction.Done + ) CertiBasicButton( buttonText = stringResource(buttonTextResId), @@ -175,8 +168,7 @@ fun ResumeAddActivitiesScreen( enabled = uiState.addButtonEnabled, modifier = Modifier .fillMaxWidth() - .padding(bottom = screenHeightDp(24.dp)) - .padding(horizontal = screenWidthDp(20.dp)) + .padding(vertical = screenHeightDp(16.dp)) ) } } diff --git a/app/src/main/java/org/sopt/certi/presentation/ui/certdetail/CertDetailScreen.kt b/app/src/main/java/org/sopt/certi/presentation/ui/certdetail/CertDetailScreen.kt index 321f78f9..23f8758e 100644 --- a/app/src/main/java/org/sopt/certi/presentation/ui/certdetail/CertDetailScreen.kt +++ b/app/src/main/java/org/sopt/certi/presentation/ui/certdetail/CertDetailScreen.kt @@ -6,7 +6,6 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.statusBarsPadding import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue @@ -55,10 +54,6 @@ fun CertDetailRoute( val uiState by viewModel.detailUiState.collectAsStateWithLifecycle() - val sheetState = rememberModalBottomSheetState( - skipPartiallyExpanded = true - ) - LaunchedEffect(Unit) { viewModel.getCertDetailInfo(certId) @@ -153,7 +148,6 @@ fun CertDetailRoute( if (showRegisterTestInfoBottomSheet) { RegisterTestInfoBottomSheet( - sheetState = sheetState, forModify = false, certTitle = certData.certificationName, onConfirm = { city, state, timeDate -> diff --git a/app/src/main/java/org/sopt/certi/presentation/ui/editpersonalinfo/component/EditPersonalInfoTextField.kt b/app/src/main/java/org/sopt/certi/presentation/ui/editpersonalinfo/component/EditPersonalInfoTextField.kt index 810826ad..92ef7d2b 100644 --- a/app/src/main/java/org/sopt/certi/presentation/ui/editpersonalinfo/component/EditPersonalInfoTextField.kt +++ b/app/src/main/java/org/sopt/certi/presentation/ui/editpersonalinfo/component/EditPersonalInfoTextField.kt @@ -101,7 +101,6 @@ fun EditPersonalInfoTextField( textStyle = CertiTheme.typography.caption.regular_14.copy( color = CertiTheme.colors.black ), - maxLines = 1, keyboardOptions = KeyboardOptions.Default.copy( imeAction = imeAction ), diff --git a/app/src/main/java/org/sopt/certi/presentation/ui/main/MainBottomBar.kt b/app/src/main/java/org/sopt/certi/presentation/ui/main/MainBottomBar.kt index 494edf69..d1da87a6 100644 --- a/app/src/main/java/org/sopt/certi/presentation/ui/main/MainBottomBar.kt +++ b/app/src/main/java/org/sopt/certi/presentation/ui/main/MainBottomBar.kt @@ -10,8 +10,8 @@ import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.RowScope -import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.selection.selectable import androidx.compose.material3.Icon @@ -29,7 +29,7 @@ import androidx.compose.ui.semantics.Role import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.dp -import org.sopt.certi.core.util.heightForScreenPercentage +import org.sopt.certi.core.util.screenHeightDp import org.sopt.certi.ui.theme.CERTITheme import org.sopt.certi.ui.theme.CertiTheme @@ -50,9 +50,7 @@ fun MainBottomBar( color = Color.White ) { Row( - modifier = modifier - .fillMaxWidth() - .heightForScreenPercentage(50.dp) + modifier = modifier.fillMaxWidth() ) { tabs.forEach { tab -> MainBottomBarItem( @@ -75,8 +73,6 @@ private fun RowScope.MainBottomBarItem( ) { Column( modifier = modifier - .fillMaxHeight() - .align(Alignment.CenterVertically) .weight(1f) .selectable( selected = selected, @@ -84,7 +80,8 @@ private fun RowScope.MainBottomBarItem( onClick = onClick, indication = null, interactionSource = remember { MutableInteractionSource() } - ), + ) + .padding(top = screenHeightDp(12.dp), bottom = screenHeightDp(4.dp)), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.spacedBy(2.dp, Alignment.CenterVertically) ) { diff --git a/app/src/main/java/org/sopt/certi/presentation/ui/myacademicinfo/component/JobCategorySection.kt b/app/src/main/java/org/sopt/certi/presentation/ui/myacademicinfo/component/JobCategorySection.kt index a022a6ee..db407a99 100644 --- a/app/src/main/java/org/sopt/certi/presentation/ui/myacademicinfo/component/JobCategorySection.kt +++ b/app/src/main/java/org/sopt/certi/presentation/ui/myacademicinfo/component/JobCategorySection.kt @@ -5,7 +5,8 @@ import androidx.compose.foundation.border import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.ExperimentalLayoutApi +import androidx.compose.foundation.layout.FlowRow import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.shape.RoundedCornerShape @@ -29,6 +30,7 @@ import org.sopt.certi.domain.type.CategoryType import org.sopt.certi.ui.theme.CERTITheme import org.sopt.certi.ui.theme.CertiTheme +@OptIn(ExperimentalLayoutApi::class) @Composable fun JobCategorySection( jobCategoryList: List, @@ -43,33 +45,23 @@ fun JobCategorySection( style = CertiTheme.typography.body.semibold_16, color = CertiTheme.colors.gray400 ) - JobCategoryList( - jobCategoryList = jobCategoryList, - modifier = Modifier.padding(vertical = screenHeightDp(16.dp)) - ) + FlowRow( + modifier = Modifier.padding(vertical = screenHeightDp(16.dp)), + horizontalArrangement = Arrangement.spacedBy(screenWidthDp(8.dp)), + verticalArrangement = Arrangement.spacedBy(screenHeightDp(6.dp)) + ) { + jobCategoryList.forEach { job -> + JobCategoryChip( + categoryType = job + ) + } + } ReselectInterestedChip( onClick = onClick ) } } -@Composable -private fun JobCategoryList( - jobCategoryList: List, - modifier: Modifier = Modifier -) { - Row( - modifier = modifier, - horizontalArrangement = Arrangement.spacedBy(screenWidthDp(8.dp)) - ) { - jobCategoryList.forEach { job -> - JobCategoryChip( - categoryType = job - ) - } - } -} - @Composable private fun JobCategoryChip( categoryType: CategoryType, @@ -78,7 +70,7 @@ private fun JobCategoryChip( Box( modifier = modifier .roundedBackgroundWithBorder(cornerRadius = 24.dp, backgroundColor = CertiTheme.colors.gray0) - .padding(horizontal = screenWidthDp(12.dp), vertical = screenHeightDp(8.dp)) + .padding(horizontal = screenWidthDp(12.dp), vertical = screenHeightDp(6.dp)) ) { Text( text = categoryType.description, @@ -100,7 +92,7 @@ private fun ReselectInterestedChip( .background(color = if (isPressed) CertiTheme.colors.lightBlue else CertiTheme.colors.white, shape = RoundedCornerShape(24.dp)) .clip(RoundedCornerShape(24.dp)) .border(width = 1.dp, color = CertiTheme.colors.mainBlue, shape = RoundedCornerShape(24.dp)) - .padding(horizontal = 12.dp, vertical = 8.dp) + .padding(horizontal = 12.dp, vertical = 6.dp) .pressedClickable( changePressed = { isPressed = it diff --git a/app/src/main/java/org/sopt/certi/presentation/ui/mycertification/MyCertScreen.kt b/app/src/main/java/org/sopt/certi/presentation/ui/mycertification/MyCertScreen.kt index 0dd8aba1..6d01e4e8 100644 --- a/app/src/main/java/org/sopt/certi/presentation/ui/mycertification/MyCertScreen.kt +++ b/app/src/main/java/org/sopt/certi/presentation/ui/mycertification/MyCertScreen.kt @@ -8,7 +8,6 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -65,12 +64,9 @@ fun MyCertRoute( } uiState.editTargetCertification?.let { data -> - val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true) - when (uiState.selectedTab) { MyCertType.PLANNED -> { RegisterTestInfoBottomSheet( - sheetState = sheetState, certTitle = data.certificationName, forModify = true, onConfirm = viewModel::editPreCertification, @@ -81,7 +77,6 @@ fun MyCertRoute( } MyCertType.ACQUIRED -> { EditAcquiredTextInfoBottomSheet( - sheetState = sheetState, certificationData = data, onConfirm = viewModel::editAcquisitionCertification, onDismiss = viewModel::closeEditSheet diff --git a/app/src/main/java/org/sopt/certi/presentation/ui/mycertification/component/EditAcquiredTestInfoBottomSheet.kt b/app/src/main/java/org/sopt/certi/presentation/ui/mycertification/component/EditAcquiredTestInfoBottomSheet.kt index 553c3926..e43c137f 100644 --- a/app/src/main/java/org/sopt/certi/presentation/ui/mycertification/component/EditAcquiredTestInfoBottomSheet.kt +++ b/app/src/main/java/org/sopt/certi/presentation/ui/mycertification/component/EditAcquiredTestInfoBottomSheet.kt @@ -13,7 +13,6 @@ import androidx.compose.foundation.text.BasicTextField import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.ModalBottomSheet -import androidx.compose.material3.SheetState import androidx.compose.material3.Text import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.runtime.Composable @@ -50,12 +49,14 @@ import org.sopt.certi.ui.theme.CertiTheme @OptIn(ExperimentalMaterial3Api::class) @Composable fun EditAcquiredTextInfoBottomSheet( - sheetState: SheetState, certificationData: CertificationData, onConfirm: (String, String) -> Unit, onDismiss: () -> Unit, modifier: Modifier = Modifier ) { + val sheetState = rememberModalBottomSheetState( + skipPartiallyExpanded = true + ) val scope = rememberCoroutineScope() var dateText by remember { mutableStateOf(certificationData.acquisitionDate) } @@ -219,7 +220,6 @@ private fun EditAcquiredTextInfoBottomSheetPreview() { CERTITheme { EditAcquiredTextInfoBottomSheet( - sheetState = sheetState, certificationData = CertificationData( certificationId = 1, certificationName = "GTQ 1급 (그래픽기술자격)" diff --git a/app/src/main/java/org/sopt/certi/presentation/ui/mycertification/component/FavoriteCertList.kt b/app/src/main/java/org/sopt/certi/presentation/ui/mycertification/component/FavoriteCertList.kt index 3ced35a9..14a607ec 100644 --- a/app/src/main/java/org/sopt/certi/presentation/ui/mycertification/component/FavoriteCertList.kt +++ b/app/src/main/java/org/sopt/certi/presentation/ui/mycertification/component/FavoriteCertList.kt @@ -3,8 +3,9 @@ package org.sopt.certi.presentation.ui.mycertification.component import androidx.compose.foundation.border import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ExperimentalLayoutApi +import androidx.compose.foundation.layout.FlowRow import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding @@ -59,6 +60,7 @@ fun FavoriteCertList( } } +@OptIn(ExperimentalLayoutApi::class) @Composable private fun FavoriteCertItem( certificationData: CertificationData, @@ -86,18 +88,20 @@ private fun FavoriteCertItem( onFavoriteClick = { onFavoriteToggle(certificationData.certificationId) } ) - Row( + FlowRow( modifier = Modifier.padding(vertical = screenWidthDp(2.dp)), - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(screenWidthDp(8.dp)) + horizontalArrangement = Arrangement.spacedBy(screenWidthDp(8.dp)), + verticalArrangement = Arrangement.spacedBy(screenHeightDp(4.dp)) ) { CertInfoSection( iconRes = R.drawable.ic_paper_16, - testInfo = certificationData.testType + testInfo = certificationData.testType, + modifier = Modifier.align(Alignment.CenterVertically) ) CertInfoSection( iconRes = R.drawable.ic_certification_16, - testInfo = certificationData.agencyName + testInfo = certificationData.agencyName, + modifier = Modifier.align(Alignment.CenterVertically) ) } } diff --git a/app/src/main/java/org/sopt/certi/presentation/ui/mycertification/component/MyCertHeader.kt b/app/src/main/java/org/sopt/certi/presentation/ui/mycertification/component/MyCertHeader.kt index f386eb5e..124ba992 100644 --- a/app/src/main/java/org/sopt/certi/presentation/ui/mycertification/component/MyCertHeader.kt +++ b/app/src/main/java/org/sopt/certi/presentation/ui/mycertification/component/MyCertHeader.kt @@ -13,6 +13,7 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import org.sopt.certi.R @@ -46,7 +47,8 @@ fun MyCertHeader( MyCertCategory( category = type, isSelected = (selectedType == type), - onClick = onTabSelected + onClick = onTabSelected, + modifier = Modifier.weight(weight = 1f, fill = false) ) } } @@ -78,8 +80,8 @@ private fun MyCertCategory( this } } - .padding(screenWidthDp(10.dp)) - + .padding(screenWidthDp(10.dp)), + textAlign = TextAlign.Center ) } diff --git a/app/src/main/java/org/sopt/certi/presentation/ui/mypage/MyPageMainScreen.kt b/app/src/main/java/org/sopt/certi/presentation/ui/mypage/MyPageMainScreen.kt index 0f0320ed..cd4a4a4f 100644 --- a/app/src/main/java/org/sopt/certi/presentation/ui/mypage/MyPageMainScreen.kt +++ b/app/src/main/java/org/sopt/certi/presentation/ui/mypage/MyPageMainScreen.kt @@ -20,6 +20,7 @@ import androidx.lifecycle.compose.LifecycleEventEffect import androidx.lifecycle.compose.collectAsStateWithLifecycle import org.sopt.certi.R import org.sopt.certi.core.util.screenHeightDp +import org.sopt.certi.core.util.screenWidthDp import org.sopt.certi.domain.model.user.CertificationCount import org.sopt.certi.domain.model.user.MyPageInfo import org.sopt.certi.presentation.ui.mypage.component.MyPageCertMenuItem @@ -72,14 +73,15 @@ fun MyPageMainScreen( name = uiState.nickname, email = uiState.email, jobList = uiState.jobs, - profileImageUri = if (uiState.profileImageUrl.isNotBlank())uiState.profileImageUrl.toUri() else null + profileImageUri = if (uiState.profileImageUrl.isNotBlank())uiState.profileImageUrl.toUri() else null, + modifier = Modifier.padding(horizontal = screenWidthDp(20.dp)) ) LazyColumn( modifier = Modifier .fillMaxSize() - .background(CertiTheme.colors.gray0) - .padding(horizontal = screenHeightDp(20.dp), vertical = screenHeightDp(20.dp)), - verticalArrangement = Arrangement.spacedBy(screenHeightDp(16.dp)) + .background(CertiTheme.colors.gray0), + verticalArrangement = Arrangement.spacedBy(screenHeightDp(16.dp)), + contentPadding = PaddingValues(horizontal = screenWidthDp(20.dp), vertical = screenHeightDp(20.dp)) ) { item { MyPageCertMenuItem( diff --git a/app/src/main/java/org/sopt/certi/presentation/ui/mypage/component/MyPageCertMenuItem.kt b/app/src/main/java/org/sopt/certi/presentation/ui/mypage/component/MyPageCertMenuItem.kt index 1df5c740..42330a15 100644 --- a/app/src/main/java/org/sopt/certi/presentation/ui/mypage/component/MyPageCertMenuItem.kt +++ b/app/src/main/java/org/sopt/certi/presentation/ui/mypage/component/MyPageCertMenuItem.kt @@ -20,6 +20,7 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.vectorResource +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import org.sopt.certi.R @@ -77,17 +78,20 @@ fun MyPageCertMenuItem( ) { MyCertCategory( myCertCategory = stringResource(R.string.cert_detail_acquire_expected_button_text), - certCount = acquireExpectedCertCount + certCount = acquireExpectedCertCount, + modifier = Modifier.weight(weight = 1f, fill = false) ) VerticalDivider() MyCertCategory( myCertCategory = stringResource(R.string.cert_detail_acquired_button_text), - certCount = acquiredCertCount + certCount = acquiredCertCount, + modifier = Modifier.weight(weight = 1f, fill = false) ) VerticalDivider() MyCertCategory( myCertCategory = stringResource(R.string.cert_list_favorite_btn), - certCount = favoriteCertCount + certCount = favoriteCertCount, + modifier = Modifier.weight(weight = 1f, fill = false) ) } } @@ -100,19 +104,21 @@ private fun MyCertCategory( modifier: Modifier = Modifier ) { Column( - modifier = modifier, + modifier = modifier.padding(horizontal = screenWidthDp(8.dp)), horizontalAlignment = Alignment.CenterHorizontally ) { Text( text = myCertCategory, style = CertiTheme.typography.caption.regular_14, - color = CertiTheme.colors.gray500 + color = CertiTheme.colors.gray500, + textAlign = TextAlign.Center ) Text( text = stringResource(R.string.mypage_cert_count, certCount), style = CertiTheme.typography.caption.semibold_14, color = CertiTheme.colors.gray600, - modifier = Modifier.padding(top = 8.dp) + modifier = Modifier.padding(top = 8.dp), + textAlign = TextAlign.Center ) } } diff --git a/app/src/main/java/org/sopt/certi/presentation/ui/mypage/component/MyPageMenuItem.kt b/app/src/main/java/org/sopt/certi/presentation/ui/mypage/component/MyPageMenuItem.kt index affb8fac..7b93f394 100644 --- a/app/src/main/java/org/sopt/certi/presentation/ui/mypage/component/MyPageMenuItem.kt +++ b/app/src/main/java/org/sopt/certi/presentation/ui/mypage/component/MyPageMenuItem.kt @@ -16,7 +16,6 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.res.vectorResource -import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import org.sopt.certi.R @@ -56,16 +55,12 @@ fun MyPageMenuItem( Text( text = title, style = CertiTheme.typography.caption.semibold_14, - color = CertiTheme.colors.black, - overflow = TextOverflow.Ellipsis, - maxLines = 1 + color = CertiTheme.colors.black ) Text( text = description, style = CertiTheme.typography.caption.regular_12, - color = CertiTheme.colors.gray600, - overflow = TextOverflow.Ellipsis, - maxLines = 1 + color = CertiTheme.colors.gray600 ) } Icon( diff --git a/app/src/main/java/org/sopt/certi/presentation/ui/mypage/component/MyPageProfile.kt b/app/src/main/java/org/sopt/certi/presentation/ui/mypage/component/MyPageProfile.kt index 1af45230..273c55a0 100644 --- a/app/src/main/java/org/sopt/certi/presentation/ui/mypage/component/MyPageProfile.kt +++ b/app/src/main/java/org/sopt/certi/presentation/ui/mypage/component/MyPageProfile.kt @@ -2,7 +2,10 @@ package org.sopt.certi.presentation.ui.mypage.component import android.net.Uri import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ExperimentalLayoutApi +import androidx.compose.foundation.layout.FlowRow import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size @@ -10,14 +13,16 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp -import org.sopt.certi.core.component.chip.CertiChipList +import org.sopt.certi.core.component.chip.CertiDefaultChip import org.sopt.certi.core.util.screenHeightDp import org.sopt.certi.core.util.screenWidthDp import org.sopt.certi.ui.theme.CERTITheme import org.sopt.certi.ui.theme.CertiTheme +@OptIn(ExperimentalLayoutApi::class) @Composable fun MyPageProfile( name: String, @@ -41,20 +46,31 @@ fun MyPageProfile( text = name, style = CertiTheme.typography.subtitle.bold_20, color = CertiTheme.colors.mainBlue, - modifier = Modifier.padding(top = screenHeightDp(16.dp)) + modifier = Modifier.padding(top = screenHeightDp(8.dp)), + textAlign = TextAlign.Center ) Text( text = email, style = CertiTheme.typography.caption.regular_14, color = CertiTheme.colors.gray500, - modifier = Modifier.padding(top = screenHeightDp(4.dp)) + modifier = Modifier.padding(top = screenHeightDp(4.dp)), + textAlign = TextAlign.Center ) - CertiChipList( - categories = jobList, + + FlowRow( modifier = Modifier.padding(top = screenHeightDp(16.dp)), - spacing = screenWidthDp(8.dp), - backgroundColor = CertiTheme.colors.purpleWhite - ) + horizontalArrangement = Arrangement.spacedBy(screenWidthDp(8.dp)), + verticalArrangement = Arrangement.spacedBy(screenHeightDp(6.dp)) + ) { + jobList.forEach { text -> + CertiDefaultChip( + text = text, + textStyle = CertiTheme.typography.caption.semibold_14, + backgroundColor = CertiTheme.colors.purpleWhite, + textColor = CertiTheme.colors.mainBlue + ) + } + } } } diff --git a/app/src/main/java/org/sopt/certi/presentation/ui/resume/ResumeScreen.kt b/app/src/main/java/org/sopt/certi/presentation/ui/resume/ResumeScreen.kt index 3031768d..3f6e2fad 100644 --- a/app/src/main/java/org/sopt/certi/presentation/ui/resume/ResumeScreen.kt +++ b/app/src/main/java/org/sopt/certi/presentation/ui/resume/ResumeScreen.kt @@ -3,34 +3,27 @@ package org.sopt.certi.presentation.ui.resume import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel -import androidx.lifecycle.compose.LocalLifecycleOwner import androidx.lifecycle.compose.collectAsStateWithLifecycle import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.toImmutableList -import androidx.lifecycle.flowWithLifecycle import org.sopt.certi.R import org.sopt.certi.core.component.topbar.DDayoTopBar import org.sopt.certi.core.state.UiState -import org.sopt.certi.core.util.heightForScreenPercentage import org.sopt.certi.core.util.screenHeightDp -import org.sopt.certi.core.util.showIf +import org.sopt.certi.core.util.screenWidthDp import org.sopt.certi.domain.model.ActivityData import org.sopt.certi.domain.model.certification.CertificationData import org.sopt.certi.domain.model.user.UserInfoData @@ -38,7 +31,6 @@ import org.sopt.certi.presentation.ui.resume.component.ResumeCertificationSectio import org.sopt.certi.presentation.ui.resume.component.ResumeListSection import org.sopt.certi.presentation.ui.resume.component.ResumeProfile import org.sopt.certi.presentation.ui.resume.component.card.FlipCardOverlay -import org.sopt.certi.presentation.ui.resume.sideEffect.ResumeSideEffect @Composable fun ResumeRoute( @@ -50,26 +42,15 @@ fun ResumeRoute( viewModel: ResumeViewModel = hiltViewModel() ) { val uiState by viewModel.resumeUiState.collectAsStateWithLifecycle() - val lifecycleOwner = LocalLifecycleOwner.current - var showDialog by remember { mutableStateOf(false) } - LaunchedEffect(Unit) { viewModel.getResumeData() } - LaunchedEffect(viewModel.sideEffect, lifecycleOwner) { - viewModel.sideEffect.flowWithLifecycle(lifecycleOwner.lifecycle).collect { - when (it) { - ResumeSideEffect.ShowCertificationDetailModal -> showDialog = true - } - } - } - - if (showDialog) { + if (uiState.selectedCertDetail is UiState.Success) { FlipCardOverlay( certificationData = (uiState.selectedCertDetail as UiState.Success).data, nickname = viewModel.getUserName() ?: stringResource(R.string.resume_certification_card_nickname), - onDismiss = { showDialog = false } + onDismiss = viewModel::closeCertificationDetailModal ) } @@ -79,9 +60,7 @@ fun ResumeRoute( acquiredCertificationList = (uiState.acquiredCertificationListLoadState as UiState.Success>).data.toImmutableList(), experienceList = (uiState.experienceListLoadState as UiState.Success>).data.toImmutableList(), activityList = (uiState.activityListLoadState as UiState.Success>).data.toImmutableList(), - onCertificationClick = { certificationId -> - viewModel.onCertificationClick(certificationId) - }, + onCertificationClick = viewModel::onCertificationClick, navigateToMyPage = navigateToMyPage, navigateToMyCert = navigateToMyCert, navigateToWorkExperience = navigateToWorkExperience, @@ -122,7 +101,9 @@ fun ResumeScreen( birthday = userInfo.birthday ?: stringResource(R.string.resume_certification_birthday_empty), profileImageUrl = userInfo.profileImageUrl, navigateToMyPage = navigateToMyPage, - modifier = Modifier.padding(top = screenHeightDp(16.dp)) + modifier = Modifier + .padding(horizontal = screenWidthDp(20.dp)) + .padding(top = screenHeightDp(16.dp)) ) } @@ -148,7 +129,8 @@ fun ResumeScreen( title = stringResource(R.string.resume_section_experience_title), onClick = navigateToWorkExperience, emptyText = stringResource(R.string.resume_empty_experience_message), - resumeListItems = experienceList + resumeListItems = experienceList, + bottomPadding = screenHeightDp(8.dp) ) } @@ -165,16 +147,8 @@ fun ResumeScreen( title = stringResource(R.string.resume_section_activity_title), onClick = navigateToActivities, emptyText = stringResource(R.string.resume_empty_activity_message), - resumeListItems = activityList - ) - Spacer(modifier = Modifier.heightForScreenPercentage(13.dp)) - } - - item { - Spacer( - modifier = Modifier - .height(screenHeightDp(52.dp)) - .showIf(activityList.isNotEmpty()) + resumeListItems = activityList, + bottomPadding = screenHeightDp(if (activityList.isNotEmpty()) screenHeightDp(45.dp) else 23.dp) ) } } diff --git a/app/src/main/java/org/sopt/certi/presentation/ui/resume/ResumeViewModel.kt b/app/src/main/java/org/sopt/certi/presentation/ui/resume/ResumeViewModel.kt index 083ebcec..65e47678 100644 --- a/app/src/main/java/org/sopt/certi/presentation/ui/resume/ResumeViewModel.kt +++ b/app/src/main/java/org/sopt/certi/presentation/ui/resume/ResumeViewModel.kt @@ -3,12 +3,10 @@ package org.sopt.certi.presentation.ui.resume import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch import org.sopt.certi.core.network.TokenManager @@ -21,7 +19,6 @@ import org.sopt.certi.domain.usecase.acquisition.GetAcquisitionDetailUseCase import org.sopt.certi.domain.usecase.acquisition.GetAcquisitionListUseCase import org.sopt.certi.domain.usecase.activity.GetActivityListUseCase import org.sopt.certi.domain.usecase.career.GetCareerListUseCase -import org.sopt.certi.presentation.ui.resume.sideEffect.ResumeSideEffect import org.sopt.certi.presentation.ui.resume.state.ResumeUiState import javax.inject.Inject @@ -63,13 +60,10 @@ class ResumeViewModel @Inject constructor( acquiredCertificationListLoadState = UiState.Loading, experienceListLoadState = UiState.Loading, activityListLoadState = UiState.Loading, - selectedCertDetail = UiState.Loading + selectedCertDetail = UiState.Empty ) ) - private val _sideEffect = Channel() - val sideEffect = _sideEffect.receiveAsFlow() - fun getResumeData() { getUserInfo() getAcquiredCertificationList() @@ -129,13 +123,10 @@ class ResumeViewModel @Inject constructor( ) } - fun onCertificationClick(selectedCertificationId: Long) = viewModelScope.launch { - _selectedCertDetail.value = UiState.Loading - getAcquisitionDetailUseCase.invoke(selectedCertificationId).fold( - onSuccess = { - val acquisitionDetail = it + fun onCertificationClick(selectedId: Long) = viewModelScope.launch { + getAcquisitionDetailUseCase.invoke(selectedId).fold( + onSuccess = { acquisitionDetail -> _selectedCertDetail.emit(UiState.Success(acquisitionDetail)) - _sideEffect.send(ResumeSideEffect.ShowCertificationDetailModal) }, onFailure = { _selectedCertDetail.emit(UiState.Failure(it.message.toString())) @@ -144,4 +135,8 @@ class ResumeViewModel @Inject constructor( } fun getUserName(): String? = tokenManager.getNickName() + + fun closeCertificationDetailModal() { + _selectedCertDetail.value = UiState.Empty + } } diff --git a/app/src/main/java/org/sopt/certi/presentation/ui/resume/component/ResumeCertificationSection.kt b/app/src/main/java/org/sopt/certi/presentation/ui/resume/component/ResumeCertificationSection.kt index dd78e3dd..f5354f7c 100644 --- a/app/src/main/java/org/sopt/certi/presentation/ui/resume/component/ResumeCertificationSection.kt +++ b/app/src/main/java/org/sopt/certi/presentation/ui/resume/component/ResumeCertificationSection.kt @@ -2,11 +2,10 @@ package org.sopt.certi.presentation.ui.resume.component import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyRow import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.material3.Icon @@ -25,6 +24,7 @@ import org.sopt.certi.core.util.noRippleClickable import org.sopt.certi.core.util.screenHeightDp import org.sopt.certi.core.util.screenWidthDp import org.sopt.certi.domain.model.certification.CertificationData +import org.sopt.certi.presentation.ui.resume.component.card.ResumeCertificationSmallCard import org.sopt.certi.ui.theme.CERTITheme import org.sopt.certi.ui.theme.CertiTheme import java.time.LocalDate @@ -87,19 +87,14 @@ private fun ResumeCertificationContent( top = screenHeightDp(16.dp), bottom = screenHeightDp(36.dp) ), - horizontalArrangement = Arrangement.spacedBy(screenWidthDp(12.dp)) + horizontalArrangement = Arrangement.spacedBy(screenWidthDp(12.dp)), + contentPadding = PaddingValues(horizontal = screenWidthDp(20.dp)) ) { itemsIndexed(acquiredCertificationList) { index, certification -> - if (index == 0) { - Spacer(modifier = Modifier.width(screenWidthDp(20.dp))) - } ResumeCertificationSmallCard( certification = certification, - onClick = { onCertificationClick(certification.certificationId) } + onClick = { certification.acquisitionId?.let { onCertificationClick(it) } } ) - if (index == acquiredCertificationList.lastIndex) { - Spacer(modifier = Modifier.width(screenWidthDp(20.dp))) - } } } } @@ -112,7 +107,8 @@ private fun ResumeEmptyCertificationSectionPreview() { title = stringResource(R.string.resume_section_experience_title), onClick = { }, emptyText = stringResource(R.string.resume_empty_certification_message), - resumeListItems = listOf() + resumeListItems = listOf(), + bottomPadding = 13.dp ) } } diff --git a/app/src/main/java/org/sopt/certi/presentation/ui/resume/component/ResumeCertificationSmallCard.kt b/app/src/main/java/org/sopt/certi/presentation/ui/resume/component/ResumeCertificationSmallCard.kt deleted file mode 100644 index 01f01bea..00000000 --- a/app/src/main/java/org/sopt/certi/presentation/ui/resume/component/ResumeCertificationSmallCard.kt +++ /dev/null @@ -1,103 +0,0 @@ -package org.sopt.certi.presentation.ui.resume.component - -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.aspectRatio -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.layout.ContentScale -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import coil.compose.AsyncImage -import org.sopt.certi.R -import org.sopt.certi.core.component.chip.CertiChipList -import org.sopt.certi.core.util.noRippleClickable -import org.sopt.certi.core.util.screenHeightDp -import org.sopt.certi.core.util.screenWidthDp -import org.sopt.certi.core.util.widthForScreenPercentage -import org.sopt.certi.domain.model.certification.CertificationData -import org.sopt.certi.presentation.type.CertCardColorType -import org.sopt.certi.ui.theme.CERTITheme -import org.sopt.certi.ui.theme.CertiTheme -import java.time.LocalDate - -@Composable -fun ResumeCertificationSmallCard( - certification: CertificationData, - onClick: () -> Unit, - modifier: Modifier = Modifier -) { - Box( - modifier = modifier - .clip(RoundedCornerShape(12.dp)) - .noRippleClickable { onClick() } - ) { - AsyncImage( - model = certification.cardFrontImageUrl, - contentDescription = null, - modifier = Modifier - .widthForScreenPercentage(200.dp) - .aspectRatio(2f / 3f), - contentScale = ContentScale.Crop - ) - - Column( - modifier = Modifier - .padding( - horizontal = screenWidthDp(16.dp), - vertical = screenHeightDp(32.dp) - ) - ) { - Text( - text = certification.certificationName, - style = CertiTheme.typography.caption.bold_14, - color = CertCardColorType.fromIndex(certification.index).textColor - ) - Spacer(modifier = Modifier.height(screenHeightDp(4.dp))) - - Text( - text = stringResource( - id = R.string.resume_certification_card_small, - certification.createdAt.year, - certification.createdAt.monthValue, - certification.createdAt.dayOfMonth - ), - style = CertiTheme.typography.caption.regular_12, - color = CertCardColorType.fromIndex(certification.index).textColor - ) - Spacer(modifier = Modifier.height(screenHeightDp(8.dp))) - - CertiChipList( - categories = certification.tags, - textStyle = CertiTheme.typography.caption.semibold_12, - backgroundColor = CertCardColorType.fromIndex(certification.index).chipBackgroundColor, - textColor = CertCardColorType.fromIndex(certification.index).chipTextColor - ) - } - } -} - -@Preview(showBackground = true) -@Composable -private fun ResumeCertificationSmallCardPreview() { - CERTITheme { - ResumeCertificationSmallCard( - certification = - CertificationData( - certificationId = 1, - certificationName = "GTQ 1급 (그래픽기술자격)", - createdAt = LocalDate.now(), - cardFrontImageUrl = "https://sopt-certi-bucket.s3.ap-northeast-2.amazonaws.com/certi/color%3Dblue.png", - tags = listOf("태그", "태그", "태그") - ), - onClick = {} - ) - } -} diff --git a/app/src/main/java/org/sopt/certi/presentation/ui/resume/component/ResumeListContent.kt b/app/src/main/java/org/sopt/certi/presentation/ui/resume/component/ResumeListContent.kt index 94d3c874..e0d6dc22 100644 --- a/app/src/main/java/org/sopt/certi/presentation/ui/resume/component/ResumeListContent.kt +++ b/app/src/main/java/org/sopt/certi/presentation/ui/resume/component/ResumeListContent.kt @@ -14,7 +14,6 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.dp import org.sopt.certi.R -import org.sopt.certi.core.util.heightForScreenPercentage import org.sopt.certi.core.util.screenHeightDp import org.sopt.certi.core.util.screenWidthDp import org.sopt.certi.core.util.widthForScreenPercentage @@ -26,11 +25,8 @@ fun ResumeListContent( modifier: Modifier = Modifier ) { Column( - modifier = modifier.padding( - top = screenHeightDp(16.dp), - bottom = screenHeightDp(8.dp) - ), - verticalArrangement = Arrangement.spacedBy(screenHeightDp(24.dp)) + modifier = modifier, + verticalArrangement = Arrangement.spacedBy(screenHeightDp(16.dp)) ) { resumeListItems.forEach { item -> ResumeListItem( @@ -54,9 +50,7 @@ private fun ResumeListItem( Image( painter = painterResource(R.drawable.img_resume_list_item), contentDescription = null, - modifier = Modifier - .widthForScreenPercentage(36.dp) - .heightForScreenPercentage(36.dp) + modifier = Modifier.widthForScreenPercentage(36.dp) ) Spacer(modifier = Modifier.width(screenWidthDp(24.dp))) diff --git a/app/src/main/java/org/sopt/certi/presentation/ui/resume/component/ResumeListSection.kt b/app/src/main/java/org/sopt/certi/presentation/ui/resume/component/ResumeListSection.kt index 1701665d..2fce5526 100644 --- a/app/src/main/java/org/sopt/certi/presentation/ui/resume/component/ResumeListSection.kt +++ b/app/src/main/java/org/sopt/certi/presentation/ui/resume/component/ResumeListSection.kt @@ -13,6 +13,7 @@ import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.vectorResource import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import org.sopt.certi.R import org.sopt.certi.core.component.section.CertiEmptySection @@ -29,6 +30,7 @@ fun ResumeListSection( onClick: () -> Unit, emptyText: String, resumeListItems: List, + bottomPadding: Dp, modifier: Modifier = Modifier ) { Column( @@ -59,7 +61,13 @@ fun ResumeListSection( modifier = Modifier.padding(vertical = screenHeightDp(60.dp)) ) } else { - ResumeListContent(resumeListItems = resumeListItems) + ResumeListContent( + resumeListItems = resumeListItems, + modifier = Modifier.padding( + top = screenHeightDp(16.dp), + bottom = bottomPadding + ) + ) } } } @@ -72,7 +80,8 @@ private fun ResumeEmptyListSectionPreview() { title = stringResource(R.string.resume_section_experience_title), onClick = { }, emptyText = stringResource(R.string.resume_empty_experience_message), - resumeListItems = listOf() + resumeListItems = listOf(), + bottomPadding = 0.dp ) } } diff --git a/app/src/main/java/org/sopt/certi/presentation/ui/resume/component/ResumeProfile.kt b/app/src/main/java/org/sopt/certi/presentation/ui/resume/component/ResumeProfile.kt index 4f033d0d..0e1cee22 100644 --- a/app/src/main/java/org/sopt/certi/presentation/ui/resume/component/ResumeProfile.kt +++ b/app/src/main/java/org/sopt/certi/presentation/ui/resume/component/ResumeProfile.kt @@ -44,7 +44,7 @@ fun ResumeProfile( modifier: Modifier = Modifier ) { Row( - modifier = modifier.padding(horizontal = screenWidthDp(20.dp)), + modifier = modifier, verticalAlignment = Alignment.CenterVertically ) { if (profileImageUrl == null) { @@ -85,7 +85,8 @@ fun ResumeProfile( Spacer(modifier = Modifier.weight(1f)) Row( - modifier = Modifier.noRippleClickable(navigateToMyPage) + modifier = Modifier.noRippleClickable(navigateToMyPage), + verticalAlignment = Alignment.CenterVertically ) { Icon( imageVector = ImageVector.vectorResource(R.drawable.ic_edit_16), diff --git a/app/src/main/java/org/sopt/certi/presentation/ui/resume/component/ResumeTextField.kt b/app/src/main/java/org/sopt/certi/presentation/ui/resume/component/ResumeTextField.kt index 3eafcdba..1d238451 100644 --- a/app/src/main/java/org/sopt/certi/presentation/ui/resume/component/ResumeTextField.kt +++ b/app/src/main/java/org/sopt/certi/presentation/ui/resume/component/ResumeTextField.kt @@ -15,6 +15,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Modifier +import androidx.compose.ui.focus.FocusDirection import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.ImeAction @@ -50,7 +51,8 @@ fun ResumeTextField( imeAction = imeAction ), keyboardActions = KeyboardActions( - onDone = { focusManager.clearFocus() } + onDone = { focusManager.clearFocus() }, + onNext = { focusManager.moveFocus(FocusDirection.Down) } ), decorationBox = { innerTextField -> Column { diff --git a/app/src/main/java/org/sopt/certi/presentation/ui/resume/component/card/AcquiredDateChip.kt b/app/src/main/java/org/sopt/certi/presentation/ui/resume/component/card/AcquiredDateChip.kt index 791a606e..315ceeff 100644 --- a/app/src/main/java/org/sopt/certi/presentation/ui/resume/component/card/AcquiredDateChip.kt +++ b/app/src/main/java/org/sopt/certi/presentation/ui/resume/component/card/AcquiredDateChip.kt @@ -6,6 +6,7 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import org.sopt.certi.R @@ -29,7 +30,7 @@ fun AcquiredDateChip( cornerRadius = 20.dp, backgroundColor = CertiTheme.colors.white ) - .padding(horizontal = screenWidthDp(12.dp), vertical = screenHeightDp(6.dp)) + .padding(horizontal = screenWidthDp(12.dp), vertical = screenHeightDp(4.dp)) ) { Text( text = stringResource( @@ -39,7 +40,9 @@ fun AcquiredDateChip( certificationData.createdAt.dayOfMonth ), style = CertiTheme.typography.caption.semibold_14, - color = CertiTheme.colors.mainBlue + color = CertiTheme.colors.mainBlue, + maxLines = 1, + overflow = TextOverflow.Ellipsis ) } } diff --git a/app/src/main/java/org/sopt/certi/presentation/ui/resume/component/card/CertificationCardBack.kt b/app/src/main/java/org/sopt/certi/presentation/ui/resume/component/card/CertificationCardBack.kt index d942164a..9dd3c4b6 100644 --- a/app/src/main/java/org/sopt/certi/presentation/ui/resume/component/card/CertificationCardBack.kt +++ b/app/src/main/java/org/sopt/certi/presentation/ui/resume/component/card/CertificationCardBack.kt @@ -23,6 +23,7 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import coil.compose.AsyncImage @@ -57,7 +58,7 @@ fun CertificationCardBack( Column( modifier = Modifier - .padding(horizontal = screenWidthDp(24.dp), vertical = screenHeightDp(36.dp)), + .padding(horizontal = screenWidthDp(24.dp), vertical = screenHeightDp(30.dp)), verticalArrangement = Arrangement.Center ) { Text( @@ -98,7 +99,9 @@ fun CertificationCardBack( Text( text = stringResource(R.string.resume_acquired_title, nickname), style = CertiTheme.typography.caption.semibold_14, - color = CertiTheme.colors.white + color = CertiTheme.colors.white, + maxLines = 1, + overflow = TextOverflow.Ellipsis ) } Spacer(modifier = Modifier.height(screenHeightDp(4.dp))) @@ -108,13 +111,13 @@ fun CertificationCardBack( } } -@Preview(showBackground = true) +@Preview @Composable fun CertificationCardBackPreview() { val dummyCertification = CertificationData( certificationId = 1, certificationName = "GTQ 1급 (그래픽 기술 자격)", - description = "디자인 관련 자격증입니다.", + description = "2D 그래픽 툴의 기능을 활용한 사고의 시각화를 통해 이미지 제작, 수정, 편집 및 그래픽 디자인을 창출하는 업무를 수행하고 이를 통해 비지니스 커뮤니케이션을 원활하게 한다. 1급과 2급, 급수의 차이는 이 업무를 수행하는 툴 활용 능력의 범위와 숙련도 등의 고도화 차이이다.", tags = listOf("디자인", "컴퓨터", "김민지"), createdAt = LocalDate.of(2024, 5, 12), cardBackImageUrl = "" @@ -127,7 +130,7 @@ fun CertificationCardBackPreview() { modifier = Modifier .width(250.dp) .height(376.dp) - .background(CertiTheme.colors.gray100) + .background(CertiTheme.colors.black85) ) } } diff --git a/app/src/main/java/org/sopt/certi/presentation/ui/resume/component/card/CertificationCardFront.kt b/app/src/main/java/org/sopt/certi/presentation/ui/resume/component/card/CertificationCardFront.kt index 0cf5856b..d3d5a3a7 100644 --- a/app/src/main/java/org/sopt/certi/presentation/ui/resume/component/card/CertificationCardFront.kt +++ b/app/src/main/java/org/sopt/certi/presentation/ui/resume/component/card/CertificationCardFront.kt @@ -4,14 +4,14 @@ import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.IntrinsicSize +import androidx.compose.foundation.layout.ExperimentalLayoutApi +import androidx.compose.foundation.layout.FlowRow import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material3.HorizontalDivider import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import org.sopt.certi.ui.theme.CertiTheme @@ -20,26 +20,27 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.draw.clip import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import coil.compose.AsyncImage import org.sopt.certi.R -import org.sopt.certi.core.component.chip.CertiChipList +import org.sopt.certi.core.component.chip.ResetBadge +import org.sopt.certi.core.util.bottomBorder import org.sopt.certi.core.util.screenHeightDp import org.sopt.certi.core.util.screenWidthDp +import org.sopt.certi.core.util.toSpacedDotDate import org.sopt.certi.domain.model.certification.CertificationData -import org.sopt.certi.presentation.type.CertCardColorType import org.sopt.certi.ui.theme.CERTITheme +@OptIn(ExperimentalLayoutApi::class) @Composable fun CertificationCardFront( certificationData: CertificationData, modifier: Modifier = Modifier ) { Box( - modifier = modifier - .fillMaxSize() - .clip(RoundedCornerShape(12.dp)) + modifier = modifier.clip(RoundedCornerShape(12.dp)) ) { AsyncImage( model = certificationData.cardFrontImageUrl, @@ -47,60 +48,72 @@ fun CertificationCardFront( contentScale = ContentScale.Crop, modifier = Modifier.matchParentSize() ) + Column( - modifier = Modifier - .padding(horizontal = screenWidthDp(20.dp)) - .padding(top = screenHeightDp(40.dp)) + modifier = Modifier.fillMaxSize() ) { - Text( - text = certificationData.certificationName, - style = CertiTheme.typography.body.bold_18, - color = CertCardColorType.fromIndex(certificationData.index).textColor - ) - Spacer(modifier = Modifier.height(screenHeightDp(4.dp))) Text( text = stringResource( - R.string.resume_certification_flip_card, - certificationData.createdAt.year, - certificationData.createdAt.monthValue, - certificationData.createdAt.dayOfMonth + R.string.resume_certification_card_small, + certificationData.acquisitionDate.toSpacedDotDate() ), - style = CertiTheme.typography.caption.regular_12, - color = CertCardColorType.fromIndex(certificationData.index).textColor - ) - Spacer(modifier = Modifier.height(screenHeightDp(8.dp))) - CertiChipList( - categories = certificationData.tags, - textStyle = CertiTheme.typography.caption.semibold_12, - backgroundColor = CertCardColorType.fromIndex(certificationData.index).chipBackgroundColor, - textColor = CertCardColorType.fromIndex(certificationData.index).chipTextColor + style = CertiTheme.typography.caption.regular_10, + color = CertiTheme.colors.white, + textAlign = TextAlign.Center, + modifier = Modifier + .align(Alignment.CenterHorizontally) + .padding(top = screenWidthDp(77.dp)) + .padding(horizontal = screenWidthDp(12.dp)) ) - } - Box( - modifier = Modifier.fillMaxSize(), - contentAlignment = Alignment.BottomEnd - ) { + + Spacer(modifier = Modifier.weight(1f)) + Column( modifier = Modifier - .padding(end = screenWidthDp(16.dp), bottom = screenHeightDp(16.dp)) - .width(IntrinsicSize.Max), - verticalArrangement = Arrangement.spacedBy(screenHeightDp(4.dp)) + .padding(horizontal = screenWidthDp(28.dp)) + .padding(bottom = screenWidthDp(11.dp)), + verticalArrangement = Arrangement.spacedBy(screenWidthDp(6.dp)) ) { Text( - text = stringResource(id = R.string.resume_flip_card_touch), - style = CertiTheme.typography.caption.regular_12, - color = CertCardColorType.fromIndex(certificationData.index).subTextColor - ) - HorizontalDivider( - thickness = 1.dp, - color = CertCardColorType.fromIndex(certificationData.index).subTextColor + text = certificationData.certificationName, + style = CertiTheme.typography.caption.bold_14, + color = CertiTheme.colors.blueWhite ) + + FlowRow( + horizontalArrangement = Arrangement.spacedBy(screenWidthDp(6.dp)), + verticalArrangement = Arrangement.spacedBy(screenWidthDp(4.dp)) + ) { + certificationData.tags.forEach { text -> + ResetBadge( + text = text, + textStyle = CertiTheme.typography.caption.regular_10, + backgroundColor = CertiTheme.colors.white, + textColor = CertiTheme.colors.mainBlue + ) + } + } } + + Text( + text = stringResource(id = R.string.resume_flip_card_touch), + style = CertiTheme.typography.caption.semibold_10, + color = CertiTheme.colors.purpleWhite, + textAlign = TextAlign.Center, + modifier = Modifier + .align(Alignment.CenterHorizontally) + .padding(bottom = screenWidthDp(45.dp)) + .bottomBorder( + strokeWidth = 1.dp, + color = CertiTheme.colors.purpleWhite + ) + .padding(vertical = screenHeightDp(2.dp)) + ) } } } -@Preview(showBackground = true) +@Preview @Composable fun CertificationCardFrontPreview() { CERTITheme { @@ -109,12 +122,13 @@ fun CertificationCardFrontPreview() { certificationId = 1L, certificationName = "GTQ 1급 (그래픽기술자격)", cardFrontImageUrl = "", - tags = listOf("디자인", "김민지", "김민지") + tags = listOf("디자인", "김민지", "김민지"), + acquisitionDate = "2026-02-04" ), modifier = Modifier .width(250.dp) .height(376.dp) - .background(CertiTheme.colors.gray100) + .background(CertiTheme.colors.mainBlue) ) } } diff --git a/app/src/main/java/org/sopt/certi/presentation/ui/resume/component/card/FlipCard.kt b/app/src/main/java/org/sopt/certi/presentation/ui/resume/component/card/FlipCard.kt index 65999f81..883ffac5 100644 --- a/app/src/main/java/org/sopt/certi/presentation/ui/resume/component/card/FlipCard.kt +++ b/app/src/main/java/org/sopt/certi/presentation/ui/resume/component/card/FlipCard.kt @@ -5,6 +5,7 @@ import androidx.compose.animation.core.animateFloatAsState import androidx.compose.animation.core.tween import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.aspectRatio +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue @@ -14,7 +15,6 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.graphicsLayer -import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Dialog @@ -38,7 +38,6 @@ fun FlipCardOverlay( animationSpec = tween(500, easing = FastOutSlowInEasing), label = "rotationY" ) - val cameraDistance = with(LocalDensity.current) { 8.dp.toPx() } Dialog( onDismissRequest = onDismiss, @@ -54,7 +53,7 @@ fun FlipCardOverlay( .aspectRatio(2f / 3f) .graphicsLayer { this.rotationY = rotationY - this.cameraDistance = cameraDistance + this.cameraDistance = 12f * density if (rotationY > 90f) { scaleX = -1f } @@ -62,16 +61,20 @@ fun FlipCardOverlay( .clip(RoundedCornerShape(12.dp)) .noRippleClickable { isFlipped = !isFlipped } ) { - if (rotationY <= 90f) { - CertificationCardFront( - certificationData = certificationData - ) - } else { - CertificationCardBack( - certificationData = certificationData, - nickname = nickname - ) - } + CertificationCardFront( + certificationData = certificationData, + modifier = Modifier + .fillMaxSize() + .graphicsLayer { alpha = if (rotationY <= 90f) 1f else 0f } + ) + + CertificationCardBack( + certificationData = certificationData, + nickname = nickname, + modifier = Modifier + .fillMaxSize() + .graphicsLayer { alpha = if (rotationY > 90f) 1f else 0f } + ) } } } @@ -87,7 +90,7 @@ fun FlipCardInteractivePreview() { createdAt = LocalDate.now(), description = "• 1급과 2급, 급수의 차이는 이 업무를 수행하는 툴 활용 능력의 범위와 숙련도 등의 고도화 차이다.", index = 1, - cardFrontImageUrl = "https://sopt-certi-bucket.s3.ap-northeast-2.amazonaws.com/certi/color%3Dblue.png", + cardFrontImageUrl = "", cardBackImageUrl = "", tags = listOf("디자인", "컴퓨터", "김민지") ) diff --git a/app/src/main/java/org/sopt/certi/presentation/ui/resume/component/card/ResumeCertificationSmallCard.kt b/app/src/main/java/org/sopt/certi/presentation/ui/resume/component/card/ResumeCertificationSmallCard.kt new file mode 100644 index 00000000..2e7ea720 --- /dev/null +++ b/app/src/main/java/org/sopt/certi/presentation/ui/resume/component/card/ResumeCertificationSmallCard.kt @@ -0,0 +1,114 @@ +package org.sopt.certi.presentation.ui.resume.component.card + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.aspectRatio +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import coil.compose.AsyncImage +import org.sopt.certi.R +import org.sopt.certi.core.component.chip.ResetBadge +import org.sopt.certi.core.util.noRippleClickable +import org.sopt.certi.core.util.screenWidthDp +import org.sopt.certi.core.util.toSpacedDotDate +import org.sopt.certi.core.util.widthForScreenPercentage +import org.sopt.certi.domain.model.certification.CertificationData +import org.sopt.certi.ui.theme.CERTITheme +import org.sopt.certi.ui.theme.CertiTheme + +@Composable +fun ResumeCertificationSmallCard( + certification: CertificationData, + onClick: () -> Unit, + modifier: Modifier = Modifier +) { + Box( + modifier = modifier + .widthForScreenPercentage(200.dp) + .aspectRatio(2f / 3f) + .noRippleClickable(onClick) + ) { + AsyncImage( + model = certification.cardFrontImageUrl, + contentDescription = null, + contentScale = ContentScale.Crop, + modifier = Modifier.matchParentSize() + ) + + Text( + text = stringResource( + id = R.string.resume_certification_card_small, + certification.acquisitionDate.toSpacedDotDate() + ), + style = CertiTheme.typography.caption.regular_10, + color = CertiTheme.colors.white, + textAlign = TextAlign.Center, + modifier = Modifier + .padding(top = screenWidthDp(60.dp)) + .padding(horizontal = screenWidthDp(8.dp)) + .align(Alignment.TopCenter) + ) + + Column( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = screenWidthDp(32.dp)) + .padding(bottom = screenWidthDp(47.dp)) + .align(Alignment.BottomStart), + verticalArrangement = Arrangement.spacedBy(screenWidthDp(5.dp)) + ) { + Text( + text = certification.certificationName, + style = CertiTheme.typography.caption.semibold_10, + color = CertiTheme.colors.blueWhite, + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.spacedBy(screenWidthDp(6.dp)) + ) { + certification.tags.forEach { text -> + ResetBadge( + text = text, + textColor = CertiTheme.colors.mainBlue, + backgroundColor = CertiTheme.colors.white + ) + } + } + } + } +} + +@Preview(showBackground = true) +@Composable +private fun ResumeCertificationSmallCardPreview() { + CERTITheme { + ResumeCertificationSmallCard( + certification = + CertificationData( + certificationId = 1, + certificationName = "GTQ 1급 (그래픽기술자격) GTQ 1급 (그래픽기술자격) GTQ 1급 (그래픽기술자격) GTQ 1급 (그래픽기술자격)", + acquisitionDate = "2026-02-07", + cardFrontImageUrl = "", + tags = listOf("태그태그태그", "태그태그태그", "태그태그") + ), + onClick = {}, + modifier = Modifier.background(CertiTheme.colors.mainBlue) + ) + } +} diff --git a/app/src/main/java/org/sopt/certi/presentation/ui/resume/sideEffect/ResumeSideEffect.kt b/app/src/main/java/org/sopt/certi/presentation/ui/resume/sideEffect/ResumeSideEffect.kt deleted file mode 100644 index 9e125528..00000000 --- a/app/src/main/java/org/sopt/certi/presentation/ui/resume/sideEffect/ResumeSideEffect.kt +++ /dev/null @@ -1,5 +0,0 @@ -package org.sopt.certi.presentation.ui.resume.sideEffect - -sealed interface ResumeSideEffect { - data object ShowCertificationDetailModal : ResumeSideEffect -} diff --git a/app/src/main/java/org/sopt/certi/presentation/ui/resume/state/ResumeUiState.kt b/app/src/main/java/org/sopt/certi/presentation/ui/resume/state/ResumeUiState.kt index 737068fd..1012ac27 100644 --- a/app/src/main/java/org/sopt/certi/presentation/ui/resume/state/ResumeUiState.kt +++ b/app/src/main/java/org/sopt/certi/presentation/ui/resume/state/ResumeUiState.kt @@ -10,7 +10,8 @@ data class ResumeUiState( val acquiredCertificationListLoadState: UiState>, val experienceListLoadState: UiState>, val activityListLoadState: UiState>, - val selectedCertDetail: UiState + val selectedCertDetail: UiState, + val showCertificationDetailModal: Boolean = false ) { val loadState: UiState get() = when { diff --git a/app/src/main/java/org/sopt/certi/presentation/ui/setting/SettingNotificationScreen.kt b/app/src/main/java/org/sopt/certi/presentation/ui/setting/SettingNotificationScreen.kt index 90b11d60..592d7b22 100644 --- a/app/src/main/java/org/sopt/certi/presentation/ui/setting/SettingNotificationScreen.kt +++ b/app/src/main/java/org/sopt/certi/presentation/ui/setting/SettingNotificationScreen.kt @@ -83,7 +83,7 @@ fun SettingNotificationRoute( containerColor = CertiTheme.colors.white, snackbarHost = { SnackbarHost(hostState = snackbarHostState) { - MarketingConfirmSnackbar() + MarketingConfirmSnackbar(Modifier.padding(horizontal = screenWidthDp(20.dp))) } } ) { innerPadding -> @@ -153,7 +153,7 @@ private fun SettingNotificationScreen( .padding(horizontal = screenWidthDp(20.dp), vertical = screenHeightDp(16.dp)), verticalArrangement = Arrangement.spacedBy(screenHeightDp(12.dp)) ) { - Row { + Row(verticalAlignment = Alignment.CenterVertically) { CustomCheckbox( checked = uiState.checkboxChecked, onCheckedChange = onCheckboxCheckChange diff --git a/app/src/main/java/org/sopt/certi/presentation/ui/setting/component/DeleteAccountDialog.kt b/app/src/main/java/org/sopt/certi/presentation/ui/setting/component/DeleteAccountDialog.kt index 7c7c3139..74693aef 100644 --- a/app/src/main/java/org/sopt/certi/presentation/ui/setting/component/DeleteAccountDialog.kt +++ b/app/src/main/java/org/sopt/certi/presentation/ui/setting/component/DeleteAccountDialog.kt @@ -20,6 +20,7 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Dialog @@ -50,7 +51,8 @@ fun DeleteAccountDialog( Text( text = stringResource(R.string.setting_delete_account_dialog_message), style = CertiTheme.typography.body.semibold_18, - color = CertiTheme.colors.gray600 + color = CertiTheme.colors.gray600, + textAlign = TextAlign.Center ) Spacer(modifier = Modifier.height(screenHeightDp(16.dp))) diff --git a/app/src/main/java/org/sopt/certi/presentation/ui/workExperience/ResumeAddWorkExperienceScreen.kt b/app/src/main/java/org/sopt/certi/presentation/ui/workExperience/ResumeAddWorkExperienceScreen.kt index b6a67ffd..8729e01d 100644 --- a/app/src/main/java/org/sopt/certi/presentation/ui/workExperience/ResumeAddWorkExperienceScreen.kt +++ b/app/src/main/java/org/sopt/certi/presentation/ui/workExperience/ResumeAddWorkExperienceScreen.kt @@ -2,10 +2,16 @@ package org.sopt.certi.presentation.ui.workExperience import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.ime +import androidx.compose.foundation.layout.navigationBars import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.layout.union +import androidx.compose.foundation.layout.windowInsetsPadding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect @@ -53,7 +59,7 @@ fun ResumeAddWorkExperienceRoute( onRoleValueChange = viewModel::onRoleChanged, onDescriptionValueChange = viewModel::onDescriptionChanged, onButtonClick = viewModel::addCareer, - modifier = Modifier.padding(padding) + modifier = Modifier.padding(top = padding.calculateTopPadding()) ) } @@ -83,7 +89,7 @@ fun ResumeEditWorkExperienceRoute( onRoleValueChange = viewModel::onRoleChanged, onDescriptionValueChange = viewModel::onDescriptionChanged, onButtonClick = viewModel::editCareer, - modifier = Modifier.padding(padding) + modifier = Modifier.padding(top = padding.calculateTopPadding()) ) } @@ -101,73 +107,61 @@ fun ResumeAddWorkExperienceScreen( modifier: Modifier = Modifier ) { Column( - modifier = modifier.fillMaxSize() + modifier = modifier + .fillMaxSize() + .windowInsetsPadding(WindowInsets.ime.union(WindowInsets.navigationBars)) + .verticalScroll(rememberScrollState()) + .padding(horizontal = screenWidthDp(20.dp)) ) { - LazyColumn( - modifier = Modifier.weight(1f), - contentPadding = PaddingValues(horizontal = screenWidthDp(20.dp)) - ) { - item { - Text( - text = stringResource(titleResId), - style = CertiTheme.typography.subtitle.semibold_20, - color = CertiTheme.colors.gray600, - modifier = Modifier.padding(top = screenHeightDp(60.dp), bottom = screenHeightDp(24.dp)) - ) - } + Text( + text = stringResource(titleResId), + style = CertiTheme.typography.subtitle.semibold_20, + color = CertiTheme.colors.gray600, + modifier = Modifier.padding(top = screenHeightDp(60.dp), bottom = screenHeightDp(24.dp)) + ) - item { - ResumeDateInputSection( - title = stringResource(R.string.resume_work_experience_period), - startDate = uiState.startDate, - endDate = uiState.endDate, - onStartDateValueChange = onStartDateValueChange, - onEndDateValueChange = onEndDateValueChange, - modifier = Modifier.padding(bottom = screenHeightDp(36.dp)) - ) - } + ResumeDateInputSection( + title = stringResource(R.string.resume_work_experience_period), + startDate = uiState.startDate, + endDate = uiState.endDate, + onStartDateValueChange = onStartDateValueChange, + onEndDateValueChange = onEndDateValueChange, + modifier = Modifier.padding(bottom = screenHeightDp(36.dp)) + ) - item { - ResumeTextInputSection( - title = stringResource(R.string.resume_work_experience_organization), - value = uiState.organizationValue, - onValueChange = onOrganizationValueChange, - maxLength = 10, - modifier = Modifier.padding(bottom = screenHeightDp(36.dp)), - imeAction = ImeAction.Next - ) - } + ResumeTextInputSection( + title = stringResource(R.string.resume_work_experience_organization), + value = uiState.organizationValue, + onValueChange = onOrganizationValueChange, + maxLength = 10, + modifier = Modifier.padding(bottom = screenHeightDp(36.dp)), + imeAction = ImeAction.Next + ) - item { - ResumeTextInputSection( - title = stringResource(R.string.resume_work_experience_role), - value = uiState.roleValue, - onValueChange = onRoleValueChange, - maxLength = 10, - modifier = Modifier.padding(bottom = screenHeightDp(36.dp)), - imeAction = ImeAction.Next - ) - } + ResumeTextInputSection( + title = stringResource(R.string.resume_work_experience_role), + value = uiState.roleValue, + onValueChange = onRoleValueChange, + maxLength = 10, + modifier = Modifier.padding(bottom = screenHeightDp(36.dp)), + imeAction = ImeAction.Next + ) - item { - ResumeTextInputSection( - title = stringResource(R.string.resume_work_experience_description), - value = uiState.descriptionValue, - onValueChange = onDescriptionValueChange, - maxLength = 16, - modifier = Modifier.padding(bottom = screenHeightDp(36.dp)), - imeAction = ImeAction.Done - ) - } - } + ResumeTextInputSection( + title = stringResource(R.string.resume_work_experience_description), + value = uiState.descriptionValue, + onValueChange = onDescriptionValueChange, + maxLength = 16, + modifier = Modifier.padding(bottom = screenHeightDp(36.dp)), + imeAction = ImeAction.Done + ) CertiBasicButton( buttonText = stringResource(buttonTextResId), onClick = onButtonClick, modifier = Modifier .fillMaxWidth() - .padding(bottom = screenHeightDp(24.dp)) - .padding(horizontal = screenWidthDp(20.dp)), + .padding(vertical = screenHeightDp(16.dp)), enabled = uiState.addButtonEnabled ) } diff --git a/app/src/main/java/org/sopt/certi/ui/theme/Theme.kt b/app/src/main/java/org/sopt/certi/ui/theme/Theme.kt index 608e922c..ceab7301 100644 --- a/app/src/main/java/org/sopt/certi/ui/theme/Theme.kt +++ b/app/src/main/java/org/sopt/certi/ui/theme/Theme.kt @@ -11,7 +11,7 @@ import androidx.compose.ui.platform.LocalView import androidx.core.view.WindowCompat private val LightColorScheme = lightColorScheme( - background = Black85, + background = White, primary = MainBlue ) diff --git a/app/src/main/java/org/sopt/certi/ui/theme/Type.kt b/app/src/main/java/org/sopt/certi/ui/theme/Type.kt index af9cd7e5..0ce44ba5 100644 --- a/app/src/main/java/org/sopt/certi/ui/theme/Type.kt +++ b/app/src/main/java/org/sopt/certi/ui/theme/Type.kt @@ -5,6 +5,7 @@ import androidx.compose.runtime.staticCompositionLocalOf import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.Font import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.style.LineHeightStyle import androidx.compose.ui.unit.sp import org.sopt.certi.R @@ -14,6 +15,13 @@ object PretendardFont { val Regular = FontFamily(Font(R.font.pretendard_regular)) } +val BaseTextStyle = TextStyle( + lineHeightStyle = LineHeightStyle( + alignment = LineHeightStyle.Alignment.Center, + trim = LineHeightStyle.Trim.None + ) +) + sealed interface TypographyTokens { @Immutable @@ -59,13 +67,13 @@ data class CertiTypography( val defaultCertiTypography = CertiTypography( title = TypographyTokens.Title( - bold_24 = TextStyle( + bold_24 = BaseTextStyle.copy( fontFamily = PretendardFont.Bold, fontSize = 24.sp, lineHeight = 28.8.sp, // 120% letterSpacing = (-1.6).sp ), - bold_22 = TextStyle( + bold_22 = BaseTextStyle.copy( fontFamily = PretendardFont.Bold, fontSize = 22.sp, lineHeight = 26.4.sp, // 120% @@ -73,13 +81,13 @@ val defaultCertiTypography = CertiTypography( ) ), subtitle = TypographyTokens.Subtitle( - bold_20 = TextStyle( + bold_20 = BaseTextStyle.copy( fontFamily = PretendardFont.Bold, fontSize = 20.sp, lineHeight = 26.sp, // 130% letterSpacing = (-1.0).sp ), - semibold_20 = TextStyle( + semibold_20 = BaseTextStyle.copy( fontFamily = PretendardFont.SemiBold, fontSize = 20.sp, lineHeight = 26.sp, @@ -87,31 +95,31 @@ val defaultCertiTypography = CertiTypography( ) ), body = TypographyTokens.Body( - bold_18 = TextStyle( + bold_18 = BaseTextStyle.copy( fontFamily = PretendardFont.Bold, fontSize = 18.sp, lineHeight = 25.2.sp, // 140% letterSpacing = (-0.5).sp ), - semibold_18 = TextStyle( + semibold_18 = BaseTextStyle.copy( fontFamily = PretendardFont.SemiBold, fontSize = 18.sp, lineHeight = 25.2.sp, letterSpacing = (-0.5).sp ), - bold_16 = TextStyle( + bold_16 = BaseTextStyle.copy( fontFamily = PretendardFont.Bold, fontSize = 16.sp, lineHeight = 22.4.sp, // 140% letterSpacing = 0.sp ), - semibold_16 = TextStyle( + semibold_16 = BaseTextStyle.copy( fontFamily = PretendardFont.SemiBold, fontSize = 16.sp, lineHeight = 22.4.sp, letterSpacing = 0.sp ), - regular_16 = TextStyle( + regular_16 = BaseTextStyle.copy( fontFamily = PretendardFont.Regular, fontSize = 16.sp, lineHeight = 22.4.sp, @@ -119,43 +127,43 @@ val defaultCertiTypography = CertiTypography( ) ), caption = TypographyTokens.Caption( - bold_14 = TextStyle( + bold_14 = BaseTextStyle.copy( fontFamily = PretendardFont.Bold, fontSize = 14.sp, lineHeight = 19.6.sp, // 140% letterSpacing = 0.sp ), - semibold_14 = TextStyle( + semibold_14 = BaseTextStyle.copy( fontFamily = PretendardFont.SemiBold, fontSize = 14.sp, lineHeight = 19.6.sp, letterSpacing = 0.sp ), - regular_14 = TextStyle( + regular_14 = BaseTextStyle.copy( fontFamily = PretendardFont.Regular, fontSize = 14.sp, lineHeight = 19.6.sp, letterSpacing = 0.sp ), - semibold_12 = TextStyle( + semibold_12 = BaseTextStyle.copy( fontFamily = PretendardFont.SemiBold, fontSize = 12.sp, lineHeight = 18.sp, // 150% letterSpacing = 0.8.sp ), - regular_12 = TextStyle( + regular_12 = BaseTextStyle.copy( fontFamily = PretendardFont.Regular, fontSize = 12.sp, lineHeight = 18.sp, letterSpacing = 0.8.sp ), - semibold_10 = TextStyle( + semibold_10 = BaseTextStyle.copy( fontFamily = PretendardFont.SemiBold, fontSize = 10.sp, lineHeight = 15.sp, // 150% letterSpacing = 1.sp ), - regular_10 = TextStyle( + regular_10 = BaseTextStyle.copy( fontFamily = PretendardFont.Regular, fontSize = 10.sp, lineHeight = 15.sp, diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d426670d..4a39dad8 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -108,7 +108,7 @@ 생년월일 - %s년 %s월 %s일 획득 + 획득 날짜: %s 따요 - @@ -149,7 +149,6 @@ 터치해서 뒷면 보기 - %s년 %s월 %s일 획득했어요. %1$s님의 취득일자 %s년 %s월 %s일