Skip to content

Commit 9875ac3

Browse files
kimnamheeeec0912jyclaude
authored
배포 (#90)
* 🧹 chore: add tanstack router plugins * ✨ feat(routes): integrate tanstack router * ✨ feat(routes): add basic routes profile, explore, login * 🧹 chore(eslint): add ignore for shared/ui * ✨ feat(components): add sidebar component * 🧹 chore(asset): add instagram logo * 💎 style(index): initialize style * ✨ feat(route): add /me to route * ✨ feat(provider): add sidebar provider * ✨ feat(route): configure provider * ✨ feat(components): add sidebar navigation item component * ✨ feat(component): add sidebar component * ✨ feat(routes): separate homepage component * ✨ feat(components): add drawer component * ✨ feat(components): add dialog component * ✨ feat(components): add action, type to navItems * ✨ feat(components): add app level modal, drawer * ✨ feat(components): open drawer on search click, open modal on create click * 로그인 페이지 UI 구현 * 비밀번호 찾기 화면 구현 * 계정 생성 UI 및 비밀번호 생성 로직 구현 * 로그인 메인화면 가입하기 버튼 버그 수정 * ✨ feat(components): add create post modal component * package-lock.json 삭제 * ✨ feat(components): add sonner * ✨ feat(provider): integrate toaster * ✨ feat(components): add dropzone component * ✨ feat(components): integrate drag and drop to createmodal * ♻️ refactor(components): separate logic using custom hook * ♻️ refactor(components): separate ui components * ✨ feat(components): add post details pane * ✨ feat(components): add double modal on exit * 🧹 chore: add testing library * 🧪 test(createmodal): add create post modal related tests * ✨ feat(components): add profile header * ✨ feat(api): initialize ky instance * ✨ feat(components): add tabs * ✨ feat(routes): separate app route layer * ✨ feat(routes): update route tree * ✨ feat(components): add fallback image * ✨ feat(profile-posts): add grid ui * ✨ feat(profilepage): add posts grid ui * 🧹 chore(homepage): remove duplicate navigation shell * ♻️ refactor(profilepage): add common style container component * ✨ feat(profile): add profile page component * ✨ feat(navigation): add useNavController hook * 🧹 chore: rerun yarn install * ✨ feat(msw): msw 기본 설정 * ✨ feat(components): add carousel * ♻️ refactor(sidebar): collapse sidebar below xl * ✨ feat(components): add stories field * ✨ feat(stories): add link to story route * ✨ feat: msw 구조 수정 * ✨ feat: msw post db 업데이트 및 기본 레이아웃 구현 * ✨ feat: 사진 기본 ui 구현 * ✨ feat: 하트 색깔 및 불투명도 수정 * ✨ feat: 하트 올라가는 애니메이션 구현 * ✨ feat: 하트 애니메이션 구체화 및 버그 수정 * ✨ feat(msw): add common response type * 🐛 fix(msw): fix common response type field name * ✨ feat(msw): add mock users * ✨ feat(msw): add follow handlers * 💎 style(sidebar): add padding * ✨ feat(components): add follow list modal component needs to be fixed based on real api * ✨ feat(components): add mock data / match data type * 💎 style(search): adjust search drawer style * ✨ feat: ... (설정) 화면 모달 구현 * ✨ feat(search): apply debounce to input value * ✨ feat: msw 구현 (comment) * 🧹 chore: remove duplicate key * ✨ feat: 댓글 ui * ✨ feat: 댓글 설정창(...) ui 구현 * ✨ feat: ... 모달 로직 수정 * ✨ feat: 글 본문 및 프로필 ui 수정 (여백 정리) * ✨ feat(msw): warn unhandled requests for debugging * ✨ feat(gitignore): add env * ✨ feat(providers): add query client provider * ✨ feat(api): add test (health check) handler * ✨ feat(route): add test route for api connection test * ✨ feat: 액션 바 (상호작용) ui 구현 * ✨ feat: env 삭제 * 💎 style(sidebar): update layout stability * ✨ feat(sidebar): updage search drawer toggle logic * ✨ feat(sidebar): remain page width when opening search drawer * ✨ feat(components): add pagination * ✨ feat(components): add shared components lazyimage / card * ✨ feat(msw): add post mock data * ✨ feat(api): add feed schema / types * ✨ feat(api): add handler / api function * ✨ feat(home): add feed ui * ✨ feat(actions): add env variable on cd workflow * 💎 style(sidebar): collapse sidebar on search drawer toggle * 🐛 fix(route): update route from profile_id to post_id * ✨ feat(feed): link to post detail on click * ✨ feat(components): add dropdown * ✨ feat(msw): add album handler * ✨ feat(api): add album api call functions * ✨ feat(api): add album api call function * 🐛 fix(msw): adjust handler order * 💎 style(dropdown): remove circle icon * ✨ feat(album): add album dropdown * 🧪 test(album): add album dropdown tests * 🐛 fix(actions): apply secrets * 🧹 chore: update test:ci command option * ✨ feat(msw): add bookmarks handler * ✨ feat(bookmarks): add bookmarked posts ui * ✨ feat(postdetail): return to previous page when closing page * ✨ feat(postdetail): validate search params with zod * ✨ feat: 하단 푸터 - 위치 화면 구현 * ✨ feat: 하단 푸터 수정 (Instagram Lite 추가) * ✨ feat: 푸터 완성 * ✨ feat: 푸터 ui, 기능, 경로 설정 완성 * ✨ feat: 로그인 msw 설정 * ✨ feat: 로그인 검사 로직 추가 및 카카오톡 로그인 ui 구현 * ✨ feat: 파일 구조 변경 실제 구현과 유사하게 경로 변경 & 유지보수 및 관리 용이 목적 * ✨ feat: 푸터 완성 * ✨ feat: 비밀번호 찾기 창 ui 수정 * 💎 style(createmodal): fix modal ratio * ✨ feat(sidebar): 만들기 버튼을 게시글/스토리 업로드로 분리 - 기존 '만들기' 버튼을 '게시글 업로드'와 '스토리 업로드'로 분리 - 아이콘 구분: SquarePlus(게시글), CirclePlus(스토리) - 모바일 하단바 배경색 추가 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * ✨ feat(create-story): 스토리 업로드 기능 구현 - 사진 1장만 업로드 가능 (초과 시 toast 알림) - 9:16 비율 미리보기, 검은 배경으로 빈 공간 채움 - 본문/앨범 단계 없이 바로 공유하기 - 이미지 업로드 후 헤더 타이틀 숨김 처리 - 이탈 시 확인 다이얼로그 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * 💎 style(ui): 모달 외곽선 제거 및 안내 문구 word-break 추가 - DialogContent 기본 스타일에서 border 제거 - 이미지 업로드 안내 문구에 break-keep 추가 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * 💎 style(story): 레이아웃 수정 - xs 너비에서 미리보기, dropzone 영역의 높이가 지나치게 높아지는 문제 해결 * 🧹 chore(constants): add constant for api errors * ✨ feat(msw): add handlers for image upload * ✨ feat(msw): add handlers for stories * ♻️ refactor(component): change component name * ✨ feat(msw): add search handler * ✨ feat(explore): separate page component * ♻️ refactor: update variable names * 🧹 chore(msw): add mock data * ✨ feat(explore): add ui / api handling * ✨ feat(profile): add albums tab * ✨ feat(profile): add albums summary ui * 💎 style(album): update badge background color * ✨ feat(msw): add relations to msw mock data * ✨ feat(msw): add like related handlers * ✨ feat(msw): add bookmark toggle handler * ✨ feat: 비밀번호 찾기 기능 구현 * ✨ feat(like/bookmark): add like, bookmark toggle functionality * ✨ feat: footer 파일 위치 변경 * ✨ feat(home): add footer * ✨ feat(album): integrate get album detail api * ✨ feat: 컨플릭트 수정 * 🐛 fix(route): fix routeTree syntax error * ✨ feat: 회원가입 ui 구현 * ✨ feat: 회원가입 아이디 중복 체크 로직 구현 * ✨ feat: 회원가입 컴포넌트 분리 및 아이디 랜덤 생성 로직 수정 * ✨ feat: reset.tsx 구조 분리 * ✨ feat(msw): add post create handler * ✨ feat(create-post): add functionality and ui for creating post * ✨ feat(create-post): show close button on post create finish * ✨ feat(utils): add crop image util function * ✨ feat(create-story): integrate upload story api / image crop logic * 🐛 fix(test): add react query provider to tests * ✨ feat: 회원가입 정보 입력 화면 구현 * ✨ feat(stories): implement story feed api / integrate with component * 🐛 fix(route): update route tree file * 💎 style: layout stability * ✨ feat: 회원가입 구현 아직 토큰 관리 미흡, 이메일 전송 로직 미구현 * ✨ feat: 회원가입 라우트 간 보안 강화 로직 추가 * ✨ feat: 회원가입 푸터 라우트 연결 * ✨ feat: 푸터 버그 수정 * ✨ feat: 로그인 버그 수정 * ✨ feat: 로그인 버그 수정 * ✨ feat(msw): add search, recent search api handler * ✨ feat(search): add api function for search * ✨ feat(api): add followed / name to search api * ✨ feat(recent): add recent search keyword management * 🧹 chore: remove unused files --------- Co-authored-by: c0912jy <c0912jy@gmail.com> Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 1a09bc4 commit 9875ac3

File tree

141 files changed

+5616
-911
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

141 files changed

+5616
-911
lines changed
3.25 MB
Binary file not shown.

src/assets/birthday-cake.png

19.6 KB
Loading
5.35 KB
Loading

src/components/auth/CitySelectView.tsx

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,17 @@ interface CitySelectViewProps {
66
country: string
77
onLoginClick: () => void
88
onSignupClick: () => void
9+
onHomeClick: () => void
910
}
1011

1112
const CitySelectView = ({
1213
country,
1314
onLoginClick,
1415
onSignupClick,
16+
onHomeClick,
1517
}: CitySelectViewProps) => {
18+
const displayCountry = country === 'south-korea' ? 'South Korea' : country
19+
1620
return (
1721
<div className="flex w-full flex-col items-center">
1822
<div className="fixed top-0 left-0 z-50 flex h-[60px] w-full justify-center border-b border-gray-300 bg-white px-4">
@@ -21,7 +25,7 @@ const CitySelectView = ({
2125
src={instagramLogo}
2226
alt="Instagram"
2327
className="w-[103px] cursor-pointer"
24-
onClick={onLoginClick}
28+
onClick={onHomeClick}
2529
/>
2630
<div className="flex items-center gap-4">
2731
<button
@@ -41,30 +45,32 @@ const CitySelectView = ({
4145
</div>
4246

4347
<div className="mt-32 mb-12 flex w-11/24 flex-col">
44-
<div className="mb-10 flex items-center gap-2">
48+
<div className="mb-[53px] flex items-center gap-2">
4549
<img
4650
src={pinIcon}
4751
alt="Location"
4852
className="h-4 w-4 object-contain"
4953
/>
5054
<span className="text-[16px] leading-none font-bold text-black">
51-
{country}
55+
{displayCountry}
5256
</span>
5357
</div>
58+
5459
<div className="mb-5 text-left">
55-
<span className="text-[12px] font-bold text-[#737373] uppercase">
56-
{country}의 도시
60+
<span className="text-[14px] font-bold text-[#737373] uppercase">
61+
{displayCountry}의 도시
5762
</span>
5863
</div>
64+
5965
<div className="flex flex-col rounded-[3px] border border-gray-300 bg-white shadow-sm">
6066
<div className="p-8">
61-
<div className="grid grid-cols-4 gap-x-8 gap-y-5">
67+
<div className="columns-4 gap-x-8">
6268
{CITIES.map((city, index) => (
6369
<div
6470
key={`${city}-${index}`}
65-
className="cursor-pointer truncate text-[14px] text-black active:text-[#4a5df9]"
71+
className="mb-1 min-h-[1.25rem] cursor-pointer break-inside-avoid truncate text-[14px] text-black active:text-[#4a5df9]"
6672
>
67-
{city}
73+
{city || '\u00A0'}
6874
</div>
6975
))}
7076
</div>

src/components/auth/LiteDownloadView.tsx

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ interface LiteDownloadViewProps {
66
}
77

88
const LiteDownloadView = ({ onBack }: LiteDownloadViewProps) => {
9+
const playStoreUrl =
10+
'https://play.google.com/store/apps/details?id=com.instagram.lite&referrer=ig_mid%3D912D7CA9-53A1-4209-ABFD-35A421C9F971%26utm_campaign%3DIGLiteCarbonSideload%26utm_content%3Dlo%26utm_source%3Dinstagramweb%26utm_medium%3Dbadge'
11+
const apkFileName = 'instagram_lite_v494.0.0.7.107.apk'
12+
913
return (
1014
<div className="flex w-full flex-col items-center justify-center px-4 pt-40 text-center tracking-tighter">
1115
<img
@@ -24,7 +28,11 @@ const LiteDownloadView = ({ onBack }: LiteDownloadViewProps) => {
2428
</p>
2529

2630
<div className="flex w-full flex-col items-center">
27-
<button className="mb-[7px] flex w-full max-w-[130px] items-center justify-center gap-2 rounded-[8px] bg-[#4a5df9] py-3 text-[13px] font-semibold tracking-tighter text-white transition-opacity hover:opacity-90">
31+
<a
32+
href={`/${apkFileName}`}
33+
download={apkFileName}
34+
className="mb-[7px] flex w-full max-w-[130px] items-center justify-center gap-2 rounded-[8px] bg-[#4a5df9] py-3 text-[13px] font-semibold tracking-tighter text-white transition-opacity hover:opacity-90"
35+
>
2836
<svg
2937
width="15"
3038
height="15"
@@ -40,16 +48,21 @@ const LiteDownloadView = ({ onBack }: LiteDownloadViewProps) => {
4048
<line x1="12" y1="15" x2="12" y2="3" />
4149
</svg>
4250
APK 다운로드
43-
</button>
51+
</a>
4452
</div>
4553

46-
<div className="cursor-pointer">
54+
<a
55+
href={playStoreUrl}
56+
target="_blank"
57+
rel="noopener noreferrer"
58+
className="inline-block transition-opacity hover:opacity-80"
59+
>
4760
<img
4861
src={googlePlayBadge}
4962
alt="Google Play"
5063
className="h-12 object-contain"
5164
/>
52-
</div>
65+
</a>
5366
</div>
5467
)
5568
}

src/components/auth/LocationSelectView.tsx

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,14 @@ import { COUNTRIES } from '../../constants/countries'
44
interface LocationSelectViewProps {
55
onLoginClick: () => void
66
onSignupClick: () => void
7-
onSelect: (country: string) => void
7+
onHomeClick: () => void
8+
onSelect?: (country: string) => void
89
}
910

1011
const LocationSelectView = ({
1112
onLoginClick,
1213
onSignupClick,
14+
onHomeClick,
1315
onSelect,
1416
}: LocationSelectViewProps) => {
1517
return (
@@ -20,7 +22,7 @@ const LocationSelectView = ({
2022
src={instagramLogo}
2123
alt="Instagram"
2224
className="w-[103px] cursor-pointer"
23-
onClick={onLoginClick}
25+
onClick={onHomeClick}
2426
/>
2527
<div className="flex items-center gap-4">
2628
<button
@@ -45,12 +47,12 @@ const LocationSelectView = ({
4547
</div>
4648
<div className="flex flex-col rounded-[3px] border border-gray-300 bg-white shadow-sm">
4749
<div className="p-8">
48-
<div className="grid grid-cols-4 gap-x-8 gap-y-5">
50+
<div className="columns-4 gap-x-8">
4951
{COUNTRIES.map((country) => (
5052
<div
5153
key={country}
52-
onClick={() => onSelect(country)}
53-
className="cursor-pointer truncate text-[14px] text-black active:text-[#4a5df9]"
54+
onClick={() => onSelect?.(country)}
55+
className="mb-1 cursor-pointer break-inside-avoid truncate text-[14px] text-black active:text-[#4a5df9]"
5456
>
5557
{country}
5658
</div>

src/components/auth/LoginCard.tsx

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,12 @@ interface FloatingInputProps {
1515

1616
interface LoginResponse {
1717
success: boolean
18-
data?: {
18+
code: string
19+
message: string
20+
data: {
1921
accessToken: string
2022
refreshToken: string
21-
}
23+
} | null
2224
}
2325

2426
const FloatingInput = ({
@@ -82,7 +84,7 @@ const LoginCard = () => {
8284

8385
try {
8486
const res = await instance
85-
.post('*/api/v1/auth/login', {
87+
.post('api/v1/auth/login', {
8688
json: { loginId: id, password: pw },
8789
})
8890
.json<LoginResponse>()
@@ -98,7 +100,7 @@ const LoginCard = () => {
98100
}
99101

100102
return (
101-
<div className="flex w-full flex-col items-center bg-white p-10">
103+
<div className="flex w-full flex-col items-center border border-gray-300 bg-white p-10 shadow-sm">
102104
<img
103105
src={instagramLogo}
104106
alt="Instagram"
@@ -154,7 +156,7 @@ const LoginCard = () => {
154156
</button>
155157

156158
<button
157-
onClick={() => navigate({ to: '/password/reset' })}
159+
onClick={() => navigate({ to: '/accounts/password/reset' })}
158160
className="mt-6 cursor-pointer text-xs text-[#00376b] hover:underline"
159161
>
160162
비밀번호를 잊으셨나요?

src/constants/api-error.ts

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
export const API_ERROR_CODE = {
2+
INTERNAL_SERVER_ERROR: 'INTERNAL_SERVER_ERROR',
3+
INVALID_INPUT_VALUE: 'INVALID_INPUT_VALUE',
4+
ACCESS_DENIED: 'ACCESS_DENIED',
5+
USER_NOT_FOUND: 'USER_NOT_FOUND',
6+
INVALID_PASSWORD: 'INVALID_PASSWORD',
7+
EMAIL_ALREADY_EXISTS: 'EMAIL_ALREADY_EXISTS',
8+
NICKNAME_ALREADY_EXISTS: 'NICKNAME_ALREADY_EXISTS',
9+
INVALID_REFRESH_TOKEN: 'INVALID_REFRESH_TOKEN',
10+
REFRESH_TOKEN_EXPIRED: 'REFRESH_TOKEN_EXPIRED',
11+
REFRESH_TOKEN_REUSE_DETECTED: 'REFRESH_TOKEN_REUSE_DETECTED',
12+
POST_NOT_FOUND: 'POST_NOT_FOUND',
13+
EMPTY_CONTENT: 'EMPTY_CONTENT',
14+
COMMENT_NOT_FOUND: 'COMMENT_NOT_FOUND',
15+
SELF_FOLLOW_NOT_ALLOWED: 'SELF_FOLLOW_NOT_ALLOWED',
16+
} as const
17+
18+
export type ApiErrorCode = (typeof API_ERROR_CODE)[keyof typeof API_ERROR_CODE]
19+
20+
type ApiErrorEntry = {
21+
status: number
22+
message: string
23+
}
24+
25+
export const API_ERROR_INFO: Record<ApiErrorCode, ApiErrorEntry> = {
26+
[API_ERROR_CODE.INTERNAL_SERVER_ERROR]: {
27+
status: 500,
28+
message: '서버 내부 에러가 발생했습니다.',
29+
},
30+
[API_ERROR_CODE.INVALID_INPUT_VALUE]: {
31+
status: 400,
32+
message: '입력값이 올바르지 않습니다.',
33+
},
34+
[API_ERROR_CODE.ACCESS_DENIED]: {
35+
status: 403,
36+
message: '접근 권한이 없습니다.',
37+
},
38+
[API_ERROR_CODE.USER_NOT_FOUND]: {
39+
status: 404,
40+
message: '존재하지 않는 회원입니다.',
41+
},
42+
[API_ERROR_CODE.INVALID_PASSWORD]: {
43+
status: 401,
44+
message: '비밀번호가 일치하지 않습니다.',
45+
},
46+
[API_ERROR_CODE.EMAIL_ALREADY_EXISTS]: {
47+
status: 409,
48+
message: '이미 가입된 이메일입니다.',
49+
},
50+
[API_ERROR_CODE.NICKNAME_ALREADY_EXISTS]: {
51+
status: 409,
52+
message: '이미 가입된 닉네임입니다.',
53+
},
54+
[API_ERROR_CODE.INVALID_REFRESH_TOKEN]: {
55+
status: 401,
56+
message: '인증 정보가 유효하지 않습니다.',
57+
},
58+
[API_ERROR_CODE.REFRESH_TOKEN_EXPIRED]: {
59+
status: 401,
60+
message: '인증 정보가 만료되었습니다.',
61+
},
62+
[API_ERROR_CODE.REFRESH_TOKEN_REUSE_DETECTED]: {
63+
status: 401,
64+
message: '재발급 토큰이 재사용되었습니다.',
65+
},
66+
[API_ERROR_CODE.POST_NOT_FOUND]: {
67+
status: 404,
68+
message: '게시글을 찾을 수 없습니다.',
69+
},
70+
[API_ERROR_CODE.EMPTY_CONTENT]: {
71+
status: 400,
72+
message: '내용이 비어 있습니다.',
73+
},
74+
[API_ERROR_CODE.COMMENT_NOT_FOUND]: {
75+
status: 404,
76+
message: '댓글을 찾을 수 없습니다.',
77+
},
78+
[API_ERROR_CODE.SELF_FOLLOW_NOT_ALLOWED]: {
79+
status: 400,
80+
message: '자기 자신은 팔로우할 수 없습니다.',
81+
},
82+
}

0 commit comments

Comments
 (0)