Skip to content

Commit f83dd0b

Browse files
authored
Merge pull request #90 from SWMTheFirstTake/dev
Dev
2 parents fe74520 + 9c17644 commit f83dd0b

24 files changed

Lines changed: 60 additions & 170 deletions

src/components/chat/closet/ClosetClothCard.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,14 @@ export default function ClosetClothCard({ cloth }: ClosetClothCardProps) {
2222
{/* 삭제 버튼 (우상단) */}
2323
<button
2424
aria-label="remove-from-closet"
25-
className="absolute top-2 right-2 z-10 w-8 h-8 rounded-full bg-white/90 dark:bg-slate-700 text-slate-600 dark:text-slate-200 shadow hover:bg-white dark:hover:bg-slate-600 flex items-center justify-center"
25+
className="absolute top-2 right-2 z-10 w-8 h-8 rounded-full bg-white/90 dark:bg-slate-700 text-slate-600 dark:text-white shadow hover:bg-white dark:hover:bg-slate-600 flex items-center justify-center"
2626
onClick={(e) => {
2727
e.preventDefault();
2828
e.stopPropagation();
2929
removeClothFromCloset(cloth.id);
3030
}}
3131
>
32-
<LucideIcon name={'X'} size={16} />
32+
<LucideIcon name={'X'} color={'blue-100'} size={16} />
3333
</button>
3434
{/* ClothModal로 감싸기 */}
3535
<ClothModal product={product}>

src/components/chat/codination/CodinationActions.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ const CodinationActions = memo(function CodinationActions({
1616
}: CodinationActionsProps) {
1717
return (
1818
<div className="space-y-2">
19-
{codination.fitting_image ? (
19+
{codination.fitting_id ? (
2020
<button
2121
onClick={onVirtualFitting}
2222
className="w-full flex items-center gap-2 p-3 rounded-lg hover:bg-green-50 dark:hover:bg-green-900/20 text-green-600 dark:text-green-400 transition-colors"

src/components/chat/codination/CodinationCard.tsx

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,21 @@
11
'use client';
22

3-
import { activeCodinationAtom } from '@/atoms/chatAtoms';
3+
import { activeCodinationAtom, panelAtom } from '@/atoms/chatAtoms';
44
import { useAtom } from 'jotai';
55
import { useState, useCallback, memo } from 'react';
66
import { useCodination } from '@/hooks/useCodination';
77
import { useVirtualFitting } from '@/hooks/useVirtualFitting';
88
import CodinationCardHeader from './CodinationCardHeader';
9-
import CodinationClothItem from './CodinationClothItem';
109
import CodinationActions from './CodinationActions';
11-
import ClothModal from '@/components/chat/modal/ClothModal';
1210

1311
interface CodinationCardProps {
1412
codination: Codination;
1513
}
1614

1715
const CodinationCard = memo(function CodinationCard({ codination }: CodinationCardProps) {
18-
const [activeCodination] = useAtom(activeCodinationAtom);
16+
const [activeCodination, setActiveCodination] = useAtom(activeCodinationAtom);
1917
const [isExpanded, setIsExpanded] = useState(false);
18+
const [_, setPanel] = useAtom(panelAtom);
2019

2120
// 스토리지 훅 사용
2221
const { removeCodination } = useCodination();
@@ -30,6 +29,12 @@ const CodinationCard = memo(function CodinationCard({ codination }: CodinationCa
3029
);
3130

3231
const handleVirtualFitting = useCallback(async () => {
32+
if (codination?.fitting_id) {
33+
setActiveCodination(codination);
34+
setPanel('fitting');
35+
return;
36+
}
37+
setActiveCodination(codination);
3338
await executeVirtualFitting(codination.cloths);
3439
}, [executeVirtualFitting, codination.cloths]);
3540

Lines changed: 2 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -1,74 +1,11 @@
11
'use client';
22

3-
import { activeCodinationAtom, closetAtom, closetCodinationAtom, panelAtom } from '@/atoms/chatAtoms';
4-
import { useAtom, useSetAtom } from 'jotai';
5-
import { Button } from '@/components/ui/button';
63
import { Plus } from 'lucide-react';
74
import CodinationCard from '@/components/chat/codination/CodinationCard';
85
import { useCodination } from '@/hooks/useCodination';
9-
import { useVirtualFitting } from '@/hooks/useVirtualFitting';
10-
import { userAtom } from '@/atoms/authAtoms';
116

127
export default function CodinationPanel() {
13-
const setPanel = useSetAtom(panelAtom);
14-
const setActiveCodination = useSetAtom(activeCodinationAtom);
15-
const [closetCodination, setClosetCodination] = useAtom(closetCodinationAtom);
16-
const [activeCodination] = useAtom(activeCodinationAtom);
17-
const [user] = useAtom(userAtom);
18-
19-
// 스토리지 훅 사용
20-
const { codinations, updateCodination } = useCodination();
21-
const { executeVirtualFitting } = useVirtualFitting({
22-
codinationId: closetCodination?.id || activeCodination?.id || '',
23-
});
24-
// 상의와 하의가 모두 선택되었는지 확인
25-
const hasUpperAndLower =
26-
closetCodination &&
27-
closetCodination.cloths.some((cloth) => cloth.url.includes('TOP')) &&
28-
closetCodination.cloths.some((cloth) => cloth.url.includes('BOTTOM'));
29-
30-
const isDisabled = !closetCodination || closetCodination.cloths.length === 0 || !hasUpperAndLower;
31-
32-
const handleAddNewCodination = () => {
33-
setPanel('closet');
34-
setActiveCodination(null);
35-
setClosetCodination(null);
36-
};
37-
38-
const handleSubmitFitting = async () => {
39-
if (isDisabled || !closetCodination) return;
40-
41-
// 사용자 프로필의 모델 이미지 체크
42-
if (user?.userId) {
43-
try {
44-
const { loadUserProfile } = await import('@/lib/indexedDB');
45-
const userProfile = await loadUserProfile(user.userId);
46-
if (!userProfile?.modelImage) {
47-
// 세팅 패널 모달 띄우기
48-
setPanel('settings');
49-
return;
50-
}
51-
} catch (error) {
52-
console.error('사용자 프로필 로드 실패:', error);
53-
// 세팅 패널 모달 띄우기
54-
setPanel('settings');
55-
return;
56-
}
57-
}
58-
59-
// 가상피팅 실행
60-
const success = await executeVirtualFitting(closetCodination.cloths);
61-
62-
if (success) {
63-
// 즉시 fitting 패널로 이동
64-
setPanel('fitting');
65-
setActiveCodination(closetCodination);
66-
setClosetCodination(null);
67-
68-
// 코디네이션 업데이트
69-
await updateCodination(closetCodination);
70-
}
71-
};
8+
const { codinations } = useCodination();
729

7310
if (codinations.length === 0)
7411
return (
@@ -79,25 +16,7 @@ export default function CodinationPanel() {
7916
</div>
8017
<h3 className="text-xl font-semibold mb-3">아직 코디가 없습니다</h3>
8118
<p className="mb-6 max-w-md">AI와 대화하여 가상피팅에 사용할 옷 조합들을 추가해보세요</p>
82-
<Button
83-
onClick={handleAddNewCodination}
84-
className="bg-blue-600 hover:bg-blue-700 dark:hover:bg-blue-500 text-white px-6 py-3"
85-
>
86-
<Plus className="w-4 h-4 mr-2" />
87-
코디 추가하기
88-
</Button>
8919
</div>
90-
{closetCodination && closetCodination.cloths.length > 0 && (
91-
<div className="p-4 border-t border-slate-200 dark:border-slate-700">
92-
<button
93-
className={`w-full cursor-pointer h-12 btn bg-navy dark:bg-blue-600 text-lg text-white disabled:bg-blue-50 dark:disabled:bg-slate-600`}
94-
disabled={isDisabled}
95-
onClick={handleSubmitFitting}
96-
>
97-
가상피팅하기
98-
</button>
99-
</div>
100-
)}
10120
</div>
10221
);
10322

@@ -106,23 +25,10 @@ export default function CodinationPanel() {
10625
<div className="flex-1 overflow-y-auto">
10726
<div className="flex flex-col gap-4 p-4">
10827
{codinations.map((codination, key) => (
109-
<CodinationCard codination={codination} key={key}></CodinationCard>
28+
<CodinationCard codination={codination} key={key} />
11029
))}
11130
</div>
11231
</div>
113-
{closetCodination && closetCodination.cloths.length > 0 && (
114-
<div className="p-4 border-t border-slate-200 dark:border-slate-700">
115-
<button
116-
className={`w-full cursor-pointer h-12 btn ${
117-
closetCodination.fitting_image ? 'bg-green-600 hover:bg-green-700' : 'bg-navy'
118-
} text-lg text-white disabled:bg-blue-50`}
119-
disabled={isDisabled}
120-
onClick={handleSubmitFitting}
121-
>
122-
{closetCodination.fitting_image ? '피팅 결과 보기' : '가상피팅하기'}
123-
</button>
124-
</div>
125-
)}
12632
</div>
12733
);
12834
}

src/components/chat/fitting/FittingCard.tsx

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,6 @@ export default function FittingCard() {
1111
const user = useAtomValue(userAtom);
1212
const { fittingStatus } = useFitting(activeCodination?.id);
1313

14-
// 코디네이션에 저장된 피팅 이미지가 있으면 우선 표시
15-
const displayImage = activeCodination?.fitting_image || fittingStatus.resultUrl;
16-
1714
return (
1815
<div className="flex bg-gray-100 dark:bg-slate-800 flex-col h-full min-h-[400px]">
1916
<div className="flex h-full items-center justify-center">
@@ -22,7 +19,7 @@ export default function FittingCard() {
2219
resultUrl={fittingStatus.resultUrl}
2320
errorMessage={fittingStatus.errorMessage}
2421
userModelImage={user?.modelImage}
25-
displayImage={displayImage}
22+
displayImage={fittingStatus.resultUrl}
2623
/>
2724
</div>
2825
</div>

src/components/chat/fitting/FittingDetail.tsx

Lines changed: 24 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -23,39 +23,25 @@ export default function FittingDetail() {
2323
<div className="border border-blue dark:bg-slate-800 rounded-2xl p-4">
2424
<h3 className="text-2xl font-bold mb-4">피팅 모델</h3>
2525
<div className="flex items-center gap-4">
26+
<div className="flex-shrink-0">
27+
<Image
28+
src={user?.modelImage || '/model_image.jpg'}
29+
alt="사용자 모델 이미지"
30+
width={80}
31+
height={80}
32+
className="rounded-lg object-cover"
33+
/>
34+
</div>
2635
{user?.modelImage ? (
27-
<>
28-
<div className="flex-shrink-0">
29-
<Image
30-
src={user.modelImage}
31-
alt="사용자 모델 이미지"
32-
width={80}
33-
height={80}
34-
className="rounded-lg object-cover"
35-
/>
36-
</div>
37-
<div className="flex-1">
38-
<p className="font-bold text-lg mb-1">개인 모델 이미지</p>
39-
<p className="text-sm text-green-600 dark:text-green-400">✅ 설정된 모델 이미지가 사용됩니다</p>
40-
</div>
41-
</>
36+
<div className="flex-1">
37+
<p className="font-bold text-lg mb-1">개인 모델 이미지</p>
38+
<p className="text-sm text-green-600 dark:text-green-400">✅ 설정된 모델 이미지가 사용됩니다</p>
39+
</div>
4240
) : (
43-
<>
44-
<div className="flex-shrink-0 w-20 h-20 bg-gray-200 dark:bg-slate-600 rounded-lg flex items-center justify-center">
45-
<svg className="w-8 h-8 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
46-
<path
47-
strokeLinecap="round"
48-
strokeLinejoin="round"
49-
strokeWidth={2}
50-
d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"
51-
/>
52-
</svg>
53-
</div>
54-
<div className="flex-1">
55-
<p className="font-bold text-lg mb-1">기본 모델 이미지</p>
56-
<p className="text-sm text-gray-500 ">[설정]에서 개인 모델 이미지를 업로드하세요</p>
57-
</div>
58-
</>
41+
<div className="flex-1">
42+
<p className="font-bold text-lg mb-1">기본 모델 이미지</p>
43+
<p className="text-sm text-gray-500 ">[설정]에서 개인 모델 이미지를 업로드하세요</p>
44+
</div>
5945
)}
6046
</div>
6147
</div>
@@ -68,7 +54,13 @@ export default function FittingDetail() {
6854
? activeCodination.cloths.map((cloth: ClosetCloth, index: number) => (
6955
<div key={index} className="flex items-center gap-3 p-3 rounded-lg">
7056
<div className="flex-shrink-0 flex items-center justify-center w-15 aspect-[3/4]">
71-
<Image src={cloth.url} alt={cloth.name} fill className="rounded-lg object-cover" />
57+
<Image
58+
src={cloth.url}
59+
alt={cloth.name}
60+
width={100}
61+
height={100}
62+
className="rounded-lg object-cover"
63+
/>
7264
</div>
7365
{/* 텍스트 영역 */}
7466
<div className="flex-1 min-w-0">

src/components/chat/modal/ClothModal.tsx

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -104,14 +104,6 @@ export default function ClothModal({ product, cloth, children }: ClothModalProps
104104
</DialogHeader>
105105

106106
<div className="space-y-6">
107-
{/* 상품 설명 */}
108-
{/* <div>
109-
<h3 className="text-2xl font-semibold mb-4">상품 설명</h3>
110-
<div className="bg-gray-50 dark:bg-gray-700 p-4 rounded-lg">
111-
<p className="text-gray-600 dark:text-gray-300 leading-relaxed">{activeCloth.description}</p>
112-
</div>
113-
</div> */}
114-
115107
{/* 스타일 정보 */}
116108
<div>
117109
<h3 className="text-2xl font-semibold mb-4">스타일 정보</h3>

src/components/chat/modal/CodinationModal.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ export default function CodinationModal({ isOpen, onClose }: CodinationModalProp
4141
if (!closetCodination) {
4242
setClosetCodination({
4343
id: '',
44-
fitting_image: null,
44+
fitting_id: null,
4545
cloths: [cloth],
4646
});
4747
return;
@@ -126,7 +126,7 @@ export default function CodinationModal({ isOpen, onClose }: CodinationModalProp
126126
{/* 선택 표시 */}
127127
{isSelected && (
128128
<div className="absolute top-2 right-2 w-6 h-6 bg-blue-500 rounded-full flex items-center justify-center z-10">
129-
<span className=" text-xs"></span>
129+
<span className=" text-xs text-white"></span>
130130
</div>
131131
)}
132132

@@ -157,7 +157,7 @@ export default function CodinationModal({ isOpen, onClose }: CodinationModalProp
157157
<button
158158
onClick={handleCreateCodination}
159159
disabled={selectedCloths.length === 0 || isCreating}
160-
className="w-full sm:flex-1 px-4 py-3 bg-blue-600 rounded-lg hover:bg-blue-700 dark:hover:bg-blue-500 disabled:bg-blue-200 disabled:dark:bg-blue-800 disabled:dark:hover:bg-blue-800 disabled:cursor-not-allowed transition-colors"
160+
className="w-full sm:flex-1 px-4 py-3 bg-blue-600 rounded-lg text-white hover:bg-blue-700 dark:hover:bg-blue-500 disabled:bg-blue-200 disabled:dark:bg-blue-800 disabled:dark:hover:bg-blue-800 disabled:cursor-not-allowed transition-colors"
161161
>
162162
{isCreating ? '생성 중...' : `코디 생성 (${selectedCloths.length}개)`}
163163
</button>

src/components/ui/icons/LucideIcon.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,15 @@ export interface LucideIconProps extends HTMLAttributes<HTMLOrSVGElement> {
99
size?: number;
1010
}
1111

12-
export default function LucideIcon({ name, color = 'blue-500', size = 16, ...props }: LucideIconProps) {
12+
export default function LucideIcon({ name, color, size = 16, ...props }: LucideIconProps) {
1313
const SelectLucideIcon = icons[name];
1414

1515
const isClickEvent = !!props.onClick;
1616
const pointerStyle = isClickEvent ? 'cursor-pointer' : '';
1717

1818
return (
1919
<SelectLucideIcon
20-
color={parseColorSet(color, props.className?.includes('dark'))}
20+
color={color && parseColorSet(color, props.className?.includes('dark'))}
2121
size={size}
2222
className={cn(pointerStyle, props.className)}
2323
{...props}

src/hooks/useCodinationCreation.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ export const useCodinationCreation = () => {
3232
const isDuplicate = codinations.some((existingCodination) =>
3333
isSameCodination(existingCodination, {
3434
id: '',
35-
fitting_image: null,
35+
fitting_id: null,
3636
cloths: closetCodination.cloths,
3737
}),
3838
);
@@ -44,7 +44,7 @@ export const useCodinationCreation = () => {
4444

4545
const newCodination: Codination = {
4646
id: `codination-${Date.now()}`,
47-
fitting_image: null,
47+
fitting_id: null,
4848
cloths: [...closetCodination.cloths],
4949
};
5050

@@ -53,7 +53,7 @@ export const useCodinationCreation = () => {
5353
await addCodination(newCodination);
5454
setClosetCodination(null);
5555
setPanel('codination');
56-
56+
5757
console.log('새 코디네이션 생성됨:', newCodination);
5858
return true;
5959
} catch (error) {

0 commit comments

Comments
 (0)