Răsfoiți Sursa

fix(frontend): 피벗 잔재·용어 정리 (checkout 후원채널 블록 제거 외)

- checkout: '후원 채널' 표시 블록 제거(Phase B 제거 도메인 잔재) — 결제수단 줄·channel plumbing 유지
- CheerModal: '자발적 후원' → '자발적' (응원 기능 용어 일관)
- NoteComposeModal: placeholder '팔로우한 회원/채널' → '팔로우한 회원' (채널 잔재)
- FeedSuggestedFollows: '추천 크리에이터'→'추천 회원', title 크리에이터→회원
- 트랙레코드 리더보드: TierBadge 영문 enum(GoldAnt 등) 노출 → label prop 제거, 한글 meta.label 사용
- StockTagContent 데드 컴포넌트 삭제(임포터 0, $태그는 Content.tsx에 구현)

npm build 0에러.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
KIM-JINO5 2 săptămâni în urmă
părinte
comite
d70cc8e51d

+ 1 - 1
app/(main)/(forum)/post/_component/CheerModal.tsx

@@ -151,7 +151,7 @@ export default function CheerModal({ postID, onClose, onDone }: Props)
 					</div>
 
 					<p className='text-xs text-neutral-500'>
-						* 응원은 자발적 후원이며 글 열람 조건이 아닙니다. 캐시 → 토큰 순으로 차감되며 일부는 플랫폼 수수료로 사용됩니다.
+						* 응원은 자발적이며 글 열람 조건이 아닙니다. 캐시 → 토큰 순으로 차감되며 일부는 플랫폼 수수료로 사용됩니다.
 					</p>
 
 					{error && <p className='text-sm text-red-600' role='alert'>{error}</p>}

+ 1 - 11
app/(main)/checkout/page.tsx

@@ -173,19 +173,9 @@ export default function CheckoutPage()
 					</div>
 				</div>
 
-				{/* 결제수단 / 후원 채널 */}
+				{/* 결제수단 */}
 				<div className='px-4 sm:px-6 py-4 text-xs text-neutral-600 dark:text-neutral-400 space-y-1'>
 					<div>결제수단: <span className='font-semibold'>캐시 잔액</span></div>
-					{channel ? (
-						<div>
-							후원 채널: <span className='font-semibold'>{channel.name}</span>
-							{channel.donationCode ? (
-								<span className='font-mono ml-1 text-neutral-500'>({channel.donationCode})</span>
-							) : null}
-						</div>
-					) : (
-						<div className='text-neutral-500'>후원 채널: 선택 없음</div>
-					)}
 				</div>
 			</div>
 

+ 2 - 2
app/(main)/feed/_component/FeedSuggestedFollows.tsx

@@ -15,7 +15,7 @@ export default async function FeedSuggestedFollows()
 
 	return (
 		<section className="feed__sidebar-section">
-			<h2 className="feed__sidebar-title">추천 크리에이터</h2>
+			<h2 className="feed__sidebar-title">추천 회원</h2>
 			<ul className="feed__sidebar-follows">
 				{list.map((member) => {
 					const display = member.name || member.sid;
@@ -33,7 +33,7 @@ export default async function FeedSuggestedFollows()
 								<Link href={`/user/${member.sid}`} className="feed__sidebar-follow-name">
 									{display}
 									{member.isCreator && (
-										<span className="feed__sidebar-follow-badge" title="크리에이터">
+										<span className="feed__sidebar-follow-badge" title="회원">
 											<BadgeCheck size={12} fill="currentColor" stroke="#fff" strokeWidth={2.5} />
 										</span>
 									)}

+ 1 - 1
app/(main)/note/_components/NoteComposeModal.tsx

@@ -211,7 +211,7 @@ export default function NoteComposeModal({ open, onClose, initialReceiver, initi
 										type="text"
 										value={searchQuery}
 										onChange={(e) => setSearchQuery(e.target.value)}
-										placeholder="팔로우한 회원/채널 검색"
+										placeholder="팔로우한 회원 검색"
 										className="w-full border rounded-lg pl-9 pr-3 py-2 text-sm"
 									/>
 								</div>

+ 1 - 1
app/(main)/ranking/track-record/page.tsx

@@ -67,7 +67,7 @@ export default async function TrackRecordLeaderboardPage({ searchParams }: Props
 									<td data-align='right'><span className='track-leaderboard__hitrate'>{row.hitRate.toFixed(1)}%</span></td>
 									<td data-align='right'>{row.bestStreak.toLocaleString()}</td>
 									<td data-align='right'>{row.predictions.toLocaleString()}</td>
-									<td data-align='center'><TierBadge tierCode={row.tierCode} label={row.tier} /></td>
+									<td data-align='center'><TierBadge tierCode={row.tierCode} /></td>
 								</tr>
 							))}
 						</tbody>

+ 0 - 107
components/community/StockTagContent.tsx

@@ -1,107 +0,0 @@
-'use client';
-
-// D2 $종목 태그 렌더러 — 서버가 내려준 본문(HTML) 안의 `$005930(삼성전자)` 토큰을
-// 클릭 가능한 종목 배지(/stock/005930 링크)로 치환한다.
-// 파싱은 서버가 저장 시 수행하고, 프론트는 표시 토큰을 렌더만 한다(본문 원문 불변).
-// dangerouslySetInnerHTML 로 삽입된 HTML 의 텍스트 노드만 순회해 치환(스크립트/속성 미변조).
-import '@/app/styles/community.scss';
-import { useEffect, useRef } from 'react';
-
-interface Props {
-	content: string;
-	className?: string;
-}
-
-// $ + 6자리 숫자 + (선택) (종목명). 6자리 뒤에 숫자가 이어지면 코드로 보지 않음(경계, 서버 파서와 동일).
-const TAG_PATTERN = /\$(\d{6})(?!\d)(?:\(([^)]{0,20})\))?/g;
-
-export default function StockTagContent({ content, className }: Props)
-{
-	const ref = useRef<HTMLDivElement>(null);
-
-	useEffect(() => {
-		const root = ref.current;
-		if (!root) {
-			return;
-		}
-
-		// 이미 배지로 치환된 노드는 건너뛰기 위해 앵커 내부 텍스트는 제외
-		const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, {
-			acceptNode(node) {
-				if (!node.nodeValue || !node.nodeValue.includes('$')) {
-					return NodeFilter.FILTER_REJECT;
-				}
-
-				// 이미 종목 태그 앵커 안이면 스킵
-				let parent = node.parentElement;
-				while (parent && parent !== root) {
-					if (parent.classList && parent.classList.contains('stock-tag')) {
-						return NodeFilter.FILTER_REJECT;
-					}
-					parent = parent.parentElement;
-				}
-
-				return NodeFilter.FILTER_ACCEPT;
-			}
-		});
-
-		const targets: Text[] = [];
-		let current = walker.nextNode();
-		while (current) {
-			targets.push(current as Text);
-			current = walker.nextNode();
-		}
-
-		for (const textNode of targets) {
-			const text = textNode.nodeValue ?? '';
-			TAG_PATTERN.lastIndex = 0;
-
-			if (!TAG_PATTERN.test(text)) {
-				continue;
-			}
-
-			TAG_PATTERN.lastIndex = 0;
-			const frag = document.createDocumentFragment();
-			let lastIndex = 0;
-			let match: RegExpExecArray|null;
-
-			while ((match = TAG_PATTERN.exec(text)) !== null) {
-				const [full, code, name] = match;
-
-				if (match.index > lastIndex) {
-					frag.appendChild(document.createTextNode(text.slice(lastIndex, match.index)));
-				}
-
-				const anchor = document.createElement('a');
-				anchor.href = `/stock/${code}`;
-				anchor.className = 'stock-tag';
-
-				const nameSpan = document.createElement('span');
-				nameSpan.textContent = name ? name : code;
-				anchor.appendChild(nameSpan);
-
-				const codeSpan = document.createElement('span');
-				codeSpan.className = 'stock-tag__code';
-				codeSpan.textContent = code;
-				anchor.appendChild(codeSpan);
-
-				frag.appendChild(anchor);
-				lastIndex = match.index + full.length;
-			}
-
-			if (lastIndex < text.length) {
-				frag.appendChild(document.createTextNode(text.slice(lastIndex)));
-			}
-
-			textNode.parentNode?.replaceChild(frag, textNode);
-		}
-	}, [content]);
-
-	return (
-		<div
-			ref={ref}
-			className={className}
-			dangerouslySetInnerHTML={{ __html: content }}
-		></div>
-	);
-}