Skip to content

Commit 59b8bdf

Browse files
authored
Merge pull request #200 from RealMatchTeam/style/matchinglist-and-test
[refactor] 제안하기/재제안하기 페이지 수정
2 parents 06778d5 + 60d087b commit 59b8bdf

11 files changed

Lines changed: 336 additions & 229 deletions

File tree

app/components/form/SelectField.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,18 @@ interface SelectFieldProps {
22
placeholder: string;
33
value?: string;
44
onClick: () => void;
5+
noTruncate?: boolean;
56
}
67

78
export default function SelectField({
89
placeholder,
910
value,
1011
onClick,
12+
noTruncate = false,
1113
}: SelectFieldProps) {
14+
// 12글자 이상이면 ...으로 표시 (noTruncate가 false일 때만)
15+
const displayValue = !noTruncate && value && value.length > 12 ? `${value.slice(0, 12)}...` : value;
16+
1217
return (
1318
<button
1419
type="button"
@@ -18,10 +23,10 @@ export default function SelectField({
1823
<span
1924
className={`text-title3 ${value ? "text-text-black" : "text-text-gray3"}`}
2025
>
21-
{value || placeholder}
26+
{displayValue || placeholder}
2227
</span>
2328
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none">
24-
<path d="M7 18L12.5 12L7 6" stroke="#9B9BA1" strokeWidth="1.5"/>
29+
<path d="M7 18L12.5 12L7 6" stroke="#9B9BA1" strokeWidth="1.5" />
2530
</svg>
2631
</button>
2732
);

app/routes/brand-detail/sponsorable/detail/sponsorable-detail-content.tsx

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { LayoutContext } from "../../../layout-context";
1212
import Button from "../../../../components/common/Button";
1313
import { fetchSponsorProductDetail } from "../../api/api";
1414
import LoadingSpinner from "../../../../components/common/LoadingSpinner";
15+
import { useCampaignProposalStore } from "../../../../stores/campaign-proposal";
1516

1617
const INTERVAL = 3000;
1718

@@ -152,6 +153,7 @@ export default function SponsorableDetailContent() {
152153
const navigate = useNavigate();
153154
const location = useLocation();
154155
const [sp] = useSearchParams();
156+
const setProposalData = useCampaignProposalStore((state) => state.setProposalData);
155157

156158
const layout = useContext(LayoutContext);
157159
const state = (location.state ?? {}) as NavState;
@@ -404,16 +406,16 @@ export default function SponsorableDetailContent() {
404406
variant="primary"
405407
size="lg"
406408
fullWidth
407-
onClick={() =>
408-
navigate("/matching/suggest/create", {
409-
state: {
410-
brandId: data?.brandId,
411-
productId: data?.productId,
412-
brandName: data?.brandName,
413-
productName: data?.productName,
414-
},
415-
})
416-
}
409+
onClick={() => {
410+
setProposalData({
411+
brandId: data?.brandId ?? 0,
412+
brandName: data?.brandName,
413+
product: data?.productName,
414+
productId: data?.productId,
415+
domain: "BEAUTY",
416+
});
417+
navigate("/matching/suggest/create?type=new");
418+
}}
417419
>
418420
{buttonText}
419421
</Button>

app/routes/business/components/MatchingTabSection.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useState, useEffect } from "react";
1+
import { useState, useEffect, useCallback } from "react";
22
import { searchCollaborations, type CampaignCollaboration } from "../calendar/api/calendar";
33
import searchIcon from "../../../assets/search2.svg";
44
import closeIcon from "../../../assets/cancel.svg";
@@ -33,7 +33,7 @@ export default function MatchingTabSection({ subTab, setSubTab, receivedCount, k
3333
const [isLoading, setIsLoading] = useState(false);
3434

3535
// 캠페인 검색 함수
36-
const fetchCampaigns = async () => {
36+
const fetchCampaigns = useCallback(async () => {
3737
if (!keyword.trim()) return;
3838
setIsLoading(true);
3939
try {
@@ -47,7 +47,7 @@ export default function MatchingTabSection({ subTab, setSubTab, receivedCount, k
4747
} finally {
4848
setIsLoading(false);
4949
}
50-
};
50+
}, [keyword, subTab]);
5151

5252
// 검색 상태에 따라 캠페인 검색
5353
useEffect(() => {
@@ -56,7 +56,7 @@ export default function MatchingTabSection({ subTab, setSubTab, receivedCount, k
5656
} else {
5757
setCampaigns([]);
5858
}
59-
}, [isSearching, keyword, subTab]);
59+
}, [isSearching, keyword, subTab, fetchCampaigns]);
6060

6161
if (isSearching) {
6262
return (

app/routes/campaign-detail/route.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ export default function CampaignDetailRoute() {
124124
return () => {
125125
alive = false;
126126
};
127-
}, [resolvedBrandId, resolvedDomain]);
127+
}, [resolvedBrandId, resolvedDomain, matchRateParam]);
128128

129129
if (!campaignId) {
130130
return (

app/routes/chat/resuggest/resuggest-content.tsx

Lines changed: 102 additions & 91 deletions
Large diffs are not rendered by default.

app/routes/home/components/BannerCarousel.tsx

Lines changed: 88 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -32,41 +32,111 @@ export default function BannerCarousel({
3232
category: CategoryKey;
3333
}) {
3434
const banners = category === "beauty" ? beautyBanners : fashionBanners;
35+
const displayBanners = [banners[banners.length - 1], ...banners, banners[0]];
3536

36-
const [current, setCurrent] = useState(0);
37+
const [current, setCurrent] = useState(1);
38+
const [isDragging, setIsDragging] = useState(false);
39+
const [isSilentJumping, setIsSilentJumping] = useState(false);
40+
const [startX, setStartX] = useState(0);
41+
const [dragOffset, setDragOffset] = useState(0);
3742
const timerRef = useRef<ReturnType<typeof setInterval> | null>(null);
3843

39-
const start = useCallback(() => {
40-
timerRef.current = setInterval(() => {
41-
setCurrent((prev) => (prev + 1) % banners.length);
42-
}, INTERVAL);
43-
}, [banners.length]);
44-
4544
const stop = useCallback(() => {
4645
if (timerRef.current) {
4746
clearInterval(timerRef.current);
4847
timerRef.current = null;
4948
}
5049
}, []);
5150

51+
const start = useCallback(() => {
52+
stop();
53+
timerRef.current = setInterval(() => {
54+
setCurrent((prev) => prev + 1);
55+
}, INTERVAL);
56+
}, [stop]);
57+
5258
useEffect(() => {
5359
start();
5460
return stop;
5561
}, [start, stop]);
5662

63+
const handleTransitionEnd = () => {
64+
if (current === 0) {
65+
setIsSilentJumping(true);
66+
setCurrent(banners.length);
67+
} else if (current === banners.length + 1) {
68+
setIsSilentJumping(true);
69+
setCurrent(1);
70+
}
71+
};
72+
73+
useEffect(() => {
74+
if (isSilentJumping) {
75+
const timeout = setTimeout(() => {
76+
setIsSilentJumping(false);
77+
}, 50);
78+
return () => clearTimeout(timeout);
79+
}
80+
}, [isSilentJumping]);
81+
82+
const handleStart = (clientX: number) => {
83+
stop();
84+
setIsDragging(true);
85+
setStartX(clientX);
86+
setDragOffset(0);
87+
};
88+
89+
const handleMove = (clientX: number) => {
90+
if (!isDragging) return;
91+
const offset = clientX - startX;
92+
setDragOffset(offset);
93+
};
94+
95+
const handleEnd = () => {
96+
if (!isDragging) return;
97+
98+
const threshold = 50;
99+
if (dragOffset > threshold) {
100+
setCurrent((prev) => prev - 1);
101+
} else if (dragOffset < -threshold) {
102+
setCurrent((prev) => prev + 1);
103+
}
104+
105+
setIsDragging(false);
106+
setDragOffset(0);
107+
start();
108+
};
109+
110+
const activeDotIndex = (current - 1 + banners.length) % banners.length;
111+
57112
return (
58113
<div className="-mx-5">
59-
<div className="relative overflow-hidden">
114+
<div
115+
className="relative overflow-hidden cursor-grab active:cursor-grabbing touch-pan-y"
116+
onMouseDown={(e) => handleStart(e.clientX)}
117+
onMouseMove={(e) => handleMove(e.clientX)}
118+
onMouseUp={handleEnd}
119+
onMouseLeave={handleEnd}
120+
onTouchStart={(e) => handleStart(e.touches[0].clientX)}
121+
onTouchMove={(e) => handleMove(e.touches[0].clientX)}
122+
onTouchEnd={handleEnd}
123+
>
60124
<div
61-
className="flex transition-transform duration-500 ease-in-out"
62-
style={{ transform: `translateX(-${current * 100}%)` }}
125+
className={`flex ${isDragging || isSilentJumping ? "" : "transition-transform duration-500 ease-in-out"}`}
126+
style={{
127+
transform: `translateX(calc(-${current * 100}% + ${dragOffset}px))`,
128+
}}
129+
onTransitionEnd={handleTransitionEnd}
63130
>
64-
{banners.map((banner, i) => (
65-
<div key={`${category}-${i}`} className="w-full shrink-0">
131+
{displayBanners.map((banner, i) => (
132+
<div
133+
key={`${category}-${i}`}
134+
className="w-full shrink-0 select-none"
135+
>
66136
<img
67137
src={banner.src}
68138
alt={banner.alt}
69-
className="h-62.5 w-full object-cover"
139+
className="h-62.5 w-full object-cover pointer-events-none"
70140
/>
71141
</div>
72142
))}
@@ -77,14 +147,14 @@ export default function BannerCarousel({
77147
<button
78148
key={i}
79149
type="button"
80-
onClick={() => {
150+
onClick={(e) => {
151+
e.stopPropagation();
81152
stop();
82-
setCurrent(i);
153+
setCurrent(i + 1);
83154
start();
84155
}}
85-
className={`h-1.5 w-1.5 rounded-full transition-colors ${
86-
i === current ? "bg-white" : "bg-white/50"
87-
}`}
156+
className={`h-1.5 w-1.5 rounded-full transition-colors ${i === activeDotIndex ? "bg-white" : "bg-white/50"
157+
}`}
88158
/>
89159
))}
90160
</div>

app/routes/matching/components/ProposalModal.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,8 @@ export default function ProposalModal({
4444

4545
<h3 className="text-callout3 text-text-black text-center">
4646
{isConfirm
47-
? (isSuggest ? "캠페인을 제안하시겠습니까?" : "지원하시겠습니까?")
48-
: (isSuggest ? "제안하기 완료" : "지원 완료")}
47+
? (isSuggest ? "제안하시겠습니까?" : "지원하시겠습니까?")
48+
: (isSuggest ? "제안 완료" : "지원 완료")}
4949
</h3>
5050

5151
{!isConfirm && (
@@ -72,7 +72,7 @@ export default function ProposalModal({
7272
className="flex-[4] h-11 text-title7 rounded-xl"
7373
onClick={onConfirm}
7474
>
75-
지원하기
75+
{isSuggest ? "제안하기" : "지원하기"}
7676
</Button>
7777
</>
7878
) : (

app/routes/matching/suggest/create/components/SelectBottomSheet.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ interface SelectBottomSheetProps {
1919
hasCustomInput?: boolean;
2020
}
2121

22-
export default function SelectBottomSheet({
22+
function SelectBottomSheetInner({
2323
isOpen,
2424
onClose,
2525
title,
@@ -53,8 +53,6 @@ export default function SelectBottomSheet({
5353
};
5454

5555
const handleClose = () => {
56-
setSelected(selectedValues);
57-
setCustomInput("");
5856
onClose();
5957
};
6058

@@ -120,3 +118,8 @@ export default function SelectBottomSheet({
120118
</FilterBottomSheet>
121119
);
122120
}
121+
122+
export default function SelectBottomSheet(props: SelectBottomSheetProps) {
123+
// Reset component state when bottom sheet opens by changing key
124+
return <SelectBottomSheetInner key={props.isOpen ? 'open' : 'closed'} {...props} />;
125+
}

0 commit comments

Comments
 (0)