-
Notifications
You must be signed in to change notification settings - Fork 1
[Refactor] - 통합 테스트 및 UI 개선 #54
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 20 commits
c2c87a7
a5a6b1e
14d31da
879a20d
ed96756
93facd1
24c88ff
7b4e488
30a51d2
d4a0bdf
df4e83d
6b9b2c1
d728ae6
4eab0b3
9b6d9ea
dfc99bc
ec875e1
7786912
c0dc13a
3e472ef
59d3f66
9f8da39
ec7bed1
173f826
64232db
3da21c8
47f3d66
2573882
c317d57
6f86a43
564945f
2a4aff4
81822e6
294bd70
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| 'use client'; | ||
|
|
||
| import { useLayoutEffect } from 'react'; | ||
| import { useAuthStore } from '@/store/authStore'; | ||
| import { type AuthUser } from '@/types/auth'; | ||
|
|
||
| interface AuthStoreInitializerProps { | ||
| user: AuthUser; | ||
| } | ||
|
|
||
| export const AuthStoreInitializer = ({ user }: AuthStoreInitializerProps) => { | ||
| const setUser = useAuthStore((s) => s.setUser); | ||
|
|
||
| useLayoutEffect(() => { | ||
| setUser(user); | ||
| }, [user, setUser]); | ||
|
|
||
| return null; | ||
| }; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 현재 사용자 인증을 서버에서 직접 하고 있는데 zustand로 인증을 관리하는 이유가 있나요?
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. UploadPanel, Step5EstimateReview에서 API 호출 시 user.id가 필요한데,
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 인증한정으로 user.id를 사용하는 거면 React Context (UserProvider) 로도 동일하게 server → client로 user 데이터를 흘려보낼 수 있고, 이쪽이 더 단순할 것 같습니다!!
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 아하 굳이 zudstand을 사용할 필요는 없었던 것 같네요! 반영하겠습니다🫡 |
||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 불필요한 주석은 삭제해주세요!! |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,259 @@ | ||
| import { vars } from '@muneo/design-system'; | ||
| import { keyframes, style } from '@vanilla-extract/css'; | ||
|
|
||
| const float = keyframes({ | ||
| '0%, 100%': { transform: 'translateY(0px)' }, | ||
| '50%': { transform: 'translateY(-12px)' }, | ||
| }); | ||
|
|
||
| const shadowPulse = keyframes({ | ||
| '0%, 100%': { transform: 'scaleX(1)', opacity: 0.18 }, | ||
| '50%': { transform: 'scaleX(0.72)', opacity: 0.08 }, | ||
| }); | ||
|
|
||
| const workingBounce = keyframes({ | ||
| '0%, 80%, 100%': { transform: 'translateY(0)', opacity: 0.35 }, | ||
| '40%': { transform: 'translateY(-6px)', opacity: 1 }, | ||
| }); | ||
|
|
||
| const msgIn = keyframes({ | ||
| from: { opacity: 0, transform: 'translateY(8px)' }, | ||
| to: { opacity: 1, transform: 'translateY(0)' }, | ||
| }); | ||
|
|
||
| const msgOut = keyframes({ | ||
| from: { opacity: 1, transform: 'translateY(0)' }, | ||
| to: { opacity: 0, transform: 'translateY(-6px)' }, | ||
| }); | ||
|
|
||
| const shimmer = keyframes({ | ||
| from: { transform: 'translateX(-100%)' }, | ||
| to: { transform: 'translateX(400%)' }, | ||
| }); | ||
|
|
||
| const cardIn = keyframes({ | ||
| from: { opacity: 0, transform: 'translateX(24px)' }, | ||
| to: { opacity: 1, transform: 'translateX(0)' }, | ||
| }); | ||
|
|
||
| const cardOut = keyframes({ | ||
| from: { opacity: 1, transform: 'translateX(0)' }, | ||
| to: { opacity: 0, transform: 'translateX(-24px)' }, | ||
| }); | ||
|
|
||
| const MOTION = '(prefers-reduced-motion: reduce)' as const; | ||
|
|
||
| export const container = style({ | ||
| display: 'flex', | ||
| flexDirection: 'column', | ||
| alignItems: 'center', | ||
| justifyContent: 'center', | ||
| minHeight: '60vh', | ||
| padding: '40px 24px', | ||
| }); | ||
|
|
||
| /* ── 마스코트 ── */ | ||
| export const mascotWrap = style({ | ||
| display: 'flex', | ||
| flexDirection: 'column', | ||
| alignItems: 'center', | ||
| marginBottom: '8px', | ||
| }); | ||
|
|
||
| export const mascotFloating = style({ | ||
| animation: `${float} 2.6s ease-in-out infinite`, | ||
| '@media': { [MOTION]: { animation: 'none' } }, | ||
| }); | ||
|
|
||
| export const mascotImg = style({ | ||
| width: '92px', | ||
| height: 'auto', | ||
| display: 'block', | ||
| }); | ||
|
|
||
| export const mascotShadow = style({ | ||
| width: '56px', | ||
| height: '8px', | ||
| borderRadius: '50%', | ||
| background: 'rgba(132, 85, 223, 0.22)', | ||
| filter: 'blur(4px)', | ||
| marginTop: '2px', | ||
| animation: `${shadowPulse} 2.6s ease-in-out infinite`, | ||
| '@media': { [MOTION]: { animation: 'none' } }, | ||
| }); | ||
|
|
||
| /* ── Working dots ── */ | ||
| export const workingDots = style({ | ||
| display: 'flex', | ||
| gap: '6px', | ||
| justifyContent: 'center', | ||
| marginTop: '12px', | ||
| marginBottom: '20px', | ||
| }); | ||
|
|
||
| export const workingDot = style({ | ||
| width: '7px', | ||
| height: '7px', | ||
| borderRadius: '50%', | ||
| backgroundColor: vars.color.brand.primary, | ||
| opacity: 0.35, | ||
| animation: `${workingBounce} 1.4s ease-in-out infinite`, | ||
| '@media': { [MOTION]: { animation: 'none', opacity: 1 } }, | ||
| }); | ||
|
|
||
| /* ── 상태 메시지 ── */ | ||
| export const msgWrap = style({ | ||
| height: '22px', | ||
| display: 'flex', | ||
| alignItems: 'center', | ||
| marginBottom: '14px', | ||
| }); | ||
|
|
||
| export const msg = style({ | ||
| fontSize: vars.typography.fontSize.sm, | ||
| color: vars.color.neutral.n500, | ||
| textAlign: 'center', | ||
| fontWeight: vars.typography.fontWeight.medium, | ||
| animation: `${msgIn} 0.35s ease-out both`, | ||
| '@media': { [MOTION]: { animation: 'none' } }, | ||
| }); | ||
|
|
||
| export const msgExiting = style({ | ||
| animation: `${msgOut} 0.25s ease-in both`, | ||
| '@media': { [MOTION]: { animation: 'none' } }, | ||
| }); | ||
|
|
||
| /* ── 프로그레스 바 ── */ | ||
| export const progressWrap = style({ | ||
| width: '320px', | ||
| height: '5px', | ||
| borderRadius: '10px', | ||
| backgroundColor: vars.color.neutral.n200, | ||
| overflow: 'hidden', | ||
| marginBottom: '32px', | ||
| position: 'relative', | ||
| }); | ||
|
|
||
| export const progressFill = style({ | ||
| height: '100%', | ||
| borderRadius: '10px', | ||
| background: 'linear-gradient(90deg, #7B5CE5 0%, #B199FF 100%)', | ||
| width: '0%', | ||
| willChange: 'width', | ||
| position: 'relative', | ||
| overflow: 'hidden', | ||
| selectors: { | ||
| '&::after': { | ||
| content: '""', | ||
| position: 'absolute', | ||
| top: 0, | ||
| left: 0, | ||
| width: '25%', | ||
| height: '100%', | ||
| background: 'linear-gradient(90deg, transparent, rgba(255,255,255,0.5), transparent)', | ||
| animation: `${shimmer} 2.2s ease-in-out infinite`, | ||
| }, | ||
| }, | ||
| '@media': { | ||
| [MOTION]: { | ||
| selectors: { | ||
| '&::after': { animation: 'none' }, | ||
| }, | ||
| }, | ||
| }, | ||
| }); | ||
|
|
||
| /* ── 팁 카드 ── */ | ||
| export const tipCard = style({ | ||
| width: '480px', | ||
| backgroundColor: '#F5F0FF', | ||
| border: '1px solid #E3D8FF', | ||
| borderRadius: '16px', | ||
| padding: '20px 24px 16px', | ||
| display: 'flex', | ||
| flexDirection: 'column', | ||
| gap: '10px', | ||
| marginBottom: '20px', | ||
| animation: `${cardIn} 0.4s cubic-bezier(0.22, 1, 0.36, 1) both`, | ||
| '@media': { [MOTION]: { animation: 'none' } }, | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
| }); | ||
|
|
||
| export const tipCardExiting = style({ | ||
| animation: `${cardOut} 0.28s ease-in both`, | ||
| '@media': { [MOTION]: { animation: 'none' } }, | ||
| }); | ||
|
|
||
| export const tipHeader = style({ | ||
| display: 'flex', | ||
| alignItems: 'center', | ||
| justifyContent: 'space-between', | ||
| }); | ||
|
|
||
| export const tipLabel = style({ | ||
| display: 'flex', | ||
| alignItems: 'center', | ||
| gap: '5px', | ||
| fontSize: vars.typography.fontSize.xs, | ||
| fontWeight: vars.typography.fontWeight.semiBold, | ||
| color: '#7B5CE5', | ||
| letterSpacing: '0.02em', | ||
| }); | ||
|
|
||
| export const tipCounter = style({ | ||
| fontSize: vars.typography.fontSize.xs, | ||
| color: '#B199FF', | ||
| fontWeight: vars.typography.fontWeight.medium, | ||
| fontVariantNumeric: 'tabular-nums', | ||
| }); | ||
|
|
||
| export const tipText = style({ | ||
| fontSize: vars.typography.fontSize.base, | ||
| color: vars.color.neutral.n700, | ||
| lineHeight: '1.75', | ||
| fontWeight: vars.typography.fontWeight.regular, | ||
| wordBreak: 'keep-all', | ||
| }); | ||
|
|
||
| export const tipProgressTrack = style({ | ||
| width: '100%', | ||
| height: '3px', | ||
| borderRadius: '10px', | ||
| backgroundColor: '#E3D8FF', | ||
| overflow: 'hidden', | ||
| marginTop: '4px', | ||
| }); | ||
|
|
||
| export const tipProgressFill = style({ | ||
| height: '100%', | ||
| borderRadius: '10px', | ||
| background: 'linear-gradient(90deg, #8455DF, #B199FF)', | ||
| transition: 'width 0.6s cubic-bezier(0.4, 0, 0.2, 1)', | ||
| }); | ||
|
|
||
| /* ── 푸터 ── */ | ||
| export const footer = style({ | ||
| display: 'flex', | ||
| flexDirection: 'column', | ||
| alignItems: 'center', | ||
| gap: '10px', | ||
| }); | ||
|
|
||
| export const footerText = style({ | ||
| fontSize: vars.typography.fontSize.xs, | ||
| color: vars.color.neutral.n400, | ||
| }); | ||
|
|
||
| export const cancelBtn = style({ | ||
| background: 'none', | ||
| border: `1px solid ${vars.color.neutral.n300}`, | ||
| borderRadius: vars.radius.sm, | ||
| padding: '8px 20px', | ||
| fontSize: vars.typography.fontSize.sm, | ||
| color: vars.color.neutral.n500, | ||
| cursor: 'pointer', | ||
| fontFamily: vars.typography.fontFamily, | ||
| transition: 'opacity 0.15s ease', | ||
| selectors: { | ||
| '&:hover': { opacity: 0.65 }, | ||
| }, | ||
| }); | ||
Uh oh!
There was an error while loading. Please reload this page.