Skip to content

Comments

feat: 랜딩페이지 후원 섹션 추가#262

Merged
Wonchang0314 merged 1 commit intodevelopfrom
feat/landing
Feb 8, 2026
Merged

feat: 랜딩페이지 후원 섹션 추가#262
Wonchang0314 merged 1 commit intodevelopfrom
feat/landing

Conversation

@Wonchang0314
Copy link
Collaborator

No description provided.

@Wonchang0314 Wonchang0314 requested a review from Copilot February 8, 2026 15:27
@Wonchang0314 Wonchang0314 merged commit 6eb8541 into develop Feb 8, 2026
6 checks passed
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

랜딩페이지에 “후원(계좌 복사)” 섹션을 추가해 사용자가 후원 계좌를 쉽게 복사하고, 복사 상태에 따라 UI(아이콘/이미지/툴팁)가 변경되도록 하는 PR입니다.

Changes:

  • 랜딩페이지에 DonationSection을 추가하고 페이지에 포함
  • 계좌 복사 상태를 공유하는 Context 및 복사 버튼 UI 구현
  • 공용 Icon 컴포넌트(clipboard/check) 추가

Reviewed changes

Copilot reviewed 7 out of 9 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
src/shared/components/foundation/solar_copy-line-duotone (추가) SVG 원본 파일 추가
src/shared/components/foundation/Icons.tsx clipboard/check 아이콘 및 Icon 컴포넌트 추가
src/composite/landing/DonationSection/index.tsx 후원 섹션 레이아웃 구성
src/composite/landing/DonationSection/DonationImage.tsx 복사 상태에 따라 이미지/툴팁 전환
src/composite/landing/DonationSection/CopyAccountContext.tsx 복사 상태 Context/Provider 추가
src/composite/landing/DonationSection/CopyAccountButton.tsx 클립보드 복사 버튼 추가
src/app/page.tsx 랜딩 페이지에 DonationSection 추가

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +9 to +21
type IconComponentProps = React.SVGProps<SVGSVGElement> & { name: string; className?: string };

const ClipboardIcon = ({ ...props }: IconComponentProps) => {
return (
<svg
width={props.width ?? '24'}
height={props.height ?? '24'}
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...props}
className={props.className}
>
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Icon passes the name prop down into ClipboardIcon/CheckIcon, and those components spread ...props onto the <svg>. This results in a non-standard name attribute being rendered on the SVG (and React will warn about unknown DOM attributes). Consider separating the public Icon props (with name) from the internal SVG props, or destructuring name out before spreading onto <svg>/forwarding to the icon component.

Copilot uses AI. Check for mistakes.
Comment on lines +9 to +66
type IconComponentProps = React.SVGProps<SVGSVGElement> & { name: string; className?: string };

const ClipboardIcon = ({ ...props }: IconComponentProps) => {
return (
<svg
width={props.width ?? '24'}
height={props.height ?? '24'}
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...props}
className={props.className}
>
<path
d="M6 11C6 8.172 6 6.757 6.879 5.879C7.757 5 9.172 5 12 5H15C17.828 5 19.243 5 20.121 5.879C21 6.757 21 8.172 21 11V16C21 18.828 21 20.243 20.121 21.121C19.243 22 17.828 22 15 22H12C9.172 22 7.757 22 6.879 21.121C6 20.243 6 18.828 6 16V11Z"
stroke={props.stroke ?? 'currentColor'}
strokeWidth={props.strokeWidth ?? '1.5'}
/>
<path
opacity="0.5"
d="M6 19C5.20435 19 4.44129 18.6839 3.87868 18.1213C3.31607 17.5587 3 16.7956 3 16V10C3 6.229 3 4.343 4.172 3.172C5.344 2.001 7.229 2 11 2H15C15.7956 2 16.5587 2.31607 17.1213 2.87868C17.6839 3.44129 18 4.20435 18 5"
stroke={props.stroke ?? 'currentColor'}
strokeWidth={props.strokeWidth ?? '1.5'}
/>
</svg>
);
};

const CheckIcon = ({ ...props }: IconComponentProps) => {
return (
<svg
width={props.width ?? '24'}
height={props.height ?? '24'}
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...props}
className={props.className}
>
<path
d="M20 6L9 17L4 12"
stroke={props.stroke ?? 'currentColor'}
strokeWidth={props.strokeWidth ?? '2'}
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
};

const iconMap = {
clipboard: ClipboardIcon,
check: CheckIcon,
};

export const Icon = ({ className = '', ...props }: IconComponentProps) => {
const IconComponent = iconMap[props.name as keyof typeof iconMap];
if (!IconComponent) {
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IconComponentProps types name as a plain string, which defeats type-safety and requires casting when indexing iconMap. Prefer typing name as keyof typeof iconMap (or exporting a type IconName = ...) so callers can only pass supported icon names and you can remove the as cast.

Copilot uses AI. Check for mistakes.
Comment on lines +1 to +4
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6 11C6 8.172 6 6.757 6.879 5.879C7.757 5 9.172 5 12 5H15C17.828 5 19.243 5 20.121 5.879C21 6.757 21 8.172 21 11V16C21 18.828 21 20.243 20.121 21.121C19.243 22 17.828 22 15 22H12C9.172 22 7.757 22 6.879 21.121C6 20.243 6 18.828 6 16V11Z" stroke="black" stroke-width="1.5"/>
<path opacity="0.5" d="M6 19C5.20435 19 4.44129 18.6839 3.87868 18.1213C3.31607 17.5587 3 16.7956 3 16V10C3 6.229 3 4.343 4.172 3.172C5.344 2.001 7.229 2 11 2H15C15.7956 2 16.5587 2.31607 17.1213 2.87868C17.6839 3.44129 18 4.20435 18 5" stroke="black" stroke-width="1.5"/>
</svg>
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file appears to be an unreferenced raw SVG sitting under components/foundation with no extension and no imports/usages in the codebase. As-is it’s dead code and can be confusing for bundling/linting. Either remove it, rename/move it to a proper .svg asset location (e.g. public/), or convert it into a TSX icon component and import it where needed.

Copilot uses AI. Check for mistakes.
export const useCopyAccountContext = () => {
const context = useContext(CopyAccountContext);
if (!context) {
throw new Error('useCopyAccountContext must be used within a CopyAccountProvider');
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The thrown error message mentions CopyAccountProvider, but the exported provider component is CopyAccountContextProvider. This mismatch can mislead debugging; update the message to match the actual provider name.

Suggested change
throw new Error('useCopyAccountContext must be used within a CopyAccountProvider');
throw new Error('useCopyAccountContext must be used within a CopyAccountContextProvider');

Copilot uses AI. Check for mistakes.
Comment on lines +17 to +19
const handleCopyToClipboard = useCallback(() => {
navigator.clipboard.writeText('3333-35-3209232');
setIsCopied(true);
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

navigator.clipboard.writeText is async and can reject (permission, insecure context, unsupported browser). Currently the code sets isCopied to true regardless of whether the write succeeded and doesn’t handle failures. Consider awaiting the promise, guarding for navigator.clipboard existence, and only setting isCopied on success (optionally surface an error/toast on failure).

Suggested change
const handleCopyToClipboard = useCallback(() => {
navigator.clipboard.writeText('3333-35-3209232');
setIsCopied(true);
const handleCopyToClipboard = useCallback(async () => {
if (!navigator.clipboard || typeof navigator.clipboard.writeText !== 'function') {
console.error('Clipboard API is not available.');
return;
}
try {
await navigator.clipboard.writeText('3333-35-3209232');
setIsCopied(true);
} catch (error) {
console.error('Failed to copy account number to clipboard:', error);
}

Copilot uses AI. Check for mistakes.

const renderToolTipText = () => {
if (isCopied) {
return '보내주신 마음 잊지 않고 \n 더 열심히 할게요!';
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

renderToolTipText() returns a string containing \n, but ToolTip renders text inside an element with whitespace-nowrap, so the newline won’t display as an actual line break. If you want a multi-line tooltip, pass a ReactNode with <br /> (or update ToolTip styling to whitespace-pre-line).

Suggested change
return '보내주신 마음 잊지 않고 \n 더 열심히 할게요!';
return (
<>
보내주신 마음 잊지 않고 <br />
열심히 할게요!
</>
);

Copilot uses AI. Check for mistakes.
height={650}
sizes="(max-width: 768px) 200px, 350px"
className="w-50 md:w-87.5 h-auto shrink-0"
alt="donation-section-image"
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The image alt text ("donation-section-image") is not very descriptive for screen readers. Consider using an alt that conveys the meaning/purpose of the image (or alt="" if it’s purely decorative and the surrounding text already covers the content).

Suggested change
alt="donation-section-image"
alt={isCopied ? '후원에 감사하는 일러스트 이미지' : '후원을 요청하는 일러스트 이미지'}

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant