diff --git a/README.md b/README.md index dd7eb651..89d5a312 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,7 @@ [![React DatePicker](https://img.shields.io/badge/React%20DatePicker-7.5.0-216BA5)](https://reactdatepicker.com/) [![React Icons](https://img.shields.io/badge/React%20Icons-5.3.0-E91E63)](https://react-icons.github.io/react-icons) [![React Hot Toast](https://img.shields.io/badge/React%20Hot%20Toast-2.4.1-FF4444)](https://react-hot-toast.com/) +[![Yet Another React Lightbox](https://img.shields.io/badge/Yet%20Another%20React%20Lightbox-3.17.0-00A5E0)](https://yet-another-react-lightbox.com/) [![Hello Pangea DnD](https://img.shields.io/badge/Hello%20Pangea%20DnD-17.0.0-yellow)](https://github.com/hello-pangea/dnd) ### 지도 & 소셜 diff --git a/package-lock.json b/package-lock.json index 252c7083..03af9a7d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -34,6 +34,7 @@ "react-kakao-maps-sdk": "^1.1.27", "react-responsive": "^10.0.0", "tailwind-merge": "^2.5.4", + "yet-another-react-lightbox": "^3.21.7", "zod": "^3.23.8", "zustand": "^5.0.1" }, @@ -18445,6 +18446,19 @@ "node": ">= 14" } }, + "node_modules/yet-another-react-lightbox": { + "version": "3.21.7", + "resolved": "https://registry.npmjs.org/yet-another-react-lightbox/-/yet-another-react-lightbox-3.21.7.tgz", + "integrity": "sha512-dcdokNuCIl92f0Vl+uzeKULnQhztIGpoZFUMvtVNUPmtwsQWpqWufeieDPeg9JtFyVCcbj4vYw3V00DS0QNoWA==", + "license": "MIT", + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/package.json b/package.json index 89bb857d..1be56f87 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "react-kakao-maps-sdk": "^1.1.27", "react-responsive": "^10.0.0", "tailwind-merge": "^2.5.4", + "yet-another-react-lightbox": "^3.21.7", "zod": "^3.23.8", "zustand": "^5.0.1" }, diff --git a/public/og-image.png b/public/og-image.png index 10023929..606460e2 100644 Binary files a/public/og-image.png and b/public/og-image.png differ diff --git a/src/app/(auth)/login/page.tsx b/src/app/(auth)/login/page.tsx index ecdbd3d9..d00fe6e1 100644 --- a/src/app/(auth)/login/page.tsx +++ b/src/app/(auth)/login/page.tsx @@ -19,7 +19,7 @@ export default function LoginPage() { // 로그인 훅과 로딩 상태 관리 const { login, isPending } = useLogin(); const [isSocialLogin, setIsSocialLogin] = useState(false); - const [currentProvider, setCurrentProvider] = useState(oauthProviders.GOOGLE); + const [, setCurrentProvider] = useState(oauthProviders.GOOGLE); // 폼 유효성 검사 및 상태 관리 const { @@ -86,7 +86,7 @@ export default function LoginPage() {
로그인

아직 계정이 없으신가요?{" "} - + 회원가입하기

diff --git a/src/app/(auth)/signup/page.tsx b/src/app/(auth)/signup/page.tsx index 41dfbea3..246dd8d2 100644 --- a/src/app/(auth)/signup/page.tsx +++ b/src/app/(auth)/signup/page.tsx @@ -12,7 +12,7 @@ export default function SignupPage() {

회원 유형 선택

이미 계정이 있으신가요?{" "} - + 로그인하기

@@ -22,7 +22,7 @@ export default function SignupPage() { {/* 지원자 회원가입 카드 */}
diff --git a/src/app/(home)/page.tsx b/src/app/(home)/page.tsx index ce27314b..8ee6d791 100644 --- a/src/app/(home)/page.tsx +++ b/src/app/(home)/page.tsx @@ -23,7 +23,7 @@ const slides = [ { id: 1, title: "쉽고 빨라요", - content: "1분만에 알바폼을 만들어 보세요!\n링크를 복사하여 어디서든지 사용하세요.", + content: "1분만에 워크폼을 만들어 보세요!\n링크를 복사하여 어디서든지 사용하세요.", image: "/images/land/s2.svg", }, { @@ -65,7 +65,7 @@ const slides = [ { id: 7, title: "쉽고 빨라요", - content: "1분만에 알바폼을 만들어 보세요!\n링크를 복사하여 어디서든지 사용하세요.", + content: "1분만에 워크폼을 만들어 보세요!\n링크를 복사하여 어디서든지 사용하세요.", image: "/images/land/s2.png", }, { @@ -273,7 +273,7 @@ export default function LandingPage() { src={slides[currentSlide].blackAreaImage || ""} alt={slides[currentSlide].blackAreaTitle || ""} fill - className="object-contain transition-transform duration-500 hover:scale-105" + className="object-contain transition-transform duration-500" sizes="(max-width: 768px) 100vw, 600px" />
@@ -284,7 +284,7 @@ export default function LandingPage() { {/* 텍스트 섹션 - 장식 요소 제거 */} @@ -499,7 +499,7 @@ export default function LandingPage() { {/* 텍스트 섹션 */} {slides.slice(1).map((_, index) => ( diff --git a/src/app/(pages)/(workform)/work/[formId]/components/FormImage.tsx b/src/app/(pages)/(workform)/work/[formId]/components/FormImage.tsx index 62ecabd2..8ffc75ac 100644 --- a/src/app/(pages)/(workform)/work/[formId]/components/FormImage.tsx +++ b/src/app/(pages)/(workform)/work/[formId]/components/FormImage.tsx @@ -1,6 +1,14 @@ +"use client"; + import Image from "next/image"; import Indicator from "@/app/components/pagination/Indicator"; import { isValidS3Url } from "@/utils/checkS3Url"; +import { useState } from "react"; +import Lightbox from "yet-another-react-lightbox"; +import "yet-another-react-lightbox/styles.css"; +import Zoom from "yet-another-react-lightbox/plugins/zoom"; +import Counter from "yet-another-react-lightbox/plugins/counter"; +import "yet-another-react-lightbox/plugins/counter.css"; interface FormImageProps { imageUrls: string[]; @@ -9,32 +17,65 @@ interface FormImageProps { } export default function FormImage({ imageUrls, currentPage, onPageChange }: FormImageProps) { + const [isOpen, setIsOpen] = useState(false); + + const handleImageClick = () => { + setIsOpen(true); + }; + + const slides = imageUrls.map((url) => ({ + src: url, + })); + return ( -
- {/* 이미지 표시 */} - {imageUrls?.map((imageUrl, index) => ( -
- {`알바 -
- ))} - - {/* 인디케이터 */} - {imageUrls.filter((url) => isValidS3Url(url)).length > 1 && ( -
- -
- )} -
+ <> +
+ {/* 이미지 표시 */} + {imageUrls?.map((imageUrl, index) => ( +
{ + if (e.key === "Enter" || e.key === " ") { + handleImageClick(); + } + }} + > + {`알바 +
+ ))} + + {/* 인디케이터 */} + {imageUrls.filter((url) => isValidS3Url(url)).length > 1 && ( +
+ +
+ )} +
+ + {/* Lightbox */} + setIsOpen(false)} + slides={slides} + index={currentPage} + plugins={[Zoom, Counter]} + counter={{ container: { style: { top: "unset", bottom: 0 } } }} + on={{ + view: ({ index }) => onPageChange(index), + }} + /> + ); } diff --git a/src/app/clientLayout.tsx b/src/app/clientLayout.tsx index 8f5ec7e4..e5e6e0ce 100644 --- a/src/app/clientLayout.tsx +++ b/src/app/clientLayout.tsx @@ -36,10 +36,11 @@ export default function ClientLayout({ children }: { children: React.ReactNode } // 채널톡을 표시하지 않을 경로들 const excludePaths = ["/login", "/signup", "/auth/callback"]; const showChannelTalk = !excludePaths.some((path) => pathname.startsWith(path)); + const isHome = pathname === "/"; return ( -
+
{children} void }) => { - open: () => void; + // Postcode 클래스 정의 + Postcode: new (config: { + oncomplete: (data: DaumPostcodeData) => void; // 주소 검색 완료 시 실행될 콜백 함수 + }) => { + open: () => void; // 주소 검색 팝업을 여는 메서드 }; }; } } interface LocationInputProps extends BaseInputProps { - onAddressChange?: (fullAddress: string) => void; - readOnly?: boolean; - value?: string; + onAddressChange?: (fullAddress: string) => void; // 주소가 변경될 때 호출될 콜백 함수 + readOnly?: boolean; // 입력 필드 읽기 전용 여부 + value?: string; // 초기 주소 값 } const LocationInput = forwardRef( @@ -39,26 +43,31 @@ const LocationInput = forwardRef( ) => { const [address, setAddress] = useState(""); + // 외부에서 주소 값이 변경될 경우 내부 상태 업데이트 useEffect(() => { if (value) { setAddress(value); } }, [value]); + // 다음 우편번호 검색 팝업을 여는 함수 const handleOpenPostcode = useCallback(() => { + // SSR 환경이거나 다음 스크립트가 로드되지 않은 경우 실행하지 않음 if (typeof window === "undefined" || !window.daum) return; + // 다음 우편번호 검색 팝업 생성 및 설정 new window.daum.Postcode({ oncomplete: (data: DaumPostcodeData) => { const newAddress = data.address; - setAddress(newAddress); - onAddressChange?.(newAddress); + setAddress(newAddress); // 내부 상태 업데이트 + onAddressChange?.(newAddress); // 부모 컴포넌트에 주소 변경 알림 }, }).open(); }, [onAddressChange]); return ( <> + {/* 다음 우편번호 서비스 스크립트 로드 */}