Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)

### 지도 & 소셜
Expand Down
14 changes: 14 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
},
Expand Down
Binary file modified public/og-image.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 5 additions & 5 deletions src/app/(home)/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const slides = [
{
id: 1,
title: "쉽고 빨라요",
content: "1분만에 알바폼을 만들어 보세요!\n링크를 복사하여 어디서든지 사용하세요.",
content: "1분만에 워크폼을 만들어 보세요!\n링크를 복사하여 어디서든지 사용하세요.",
image: "/images/land/s2.svg",
},
{
Expand Down Expand Up @@ -65,7 +65,7 @@ const slides = [
{
id: 7,
title: "쉽고 빨라요",
content: "1분만에 알바폼을 만들어 보세요!\n링크를 복사하여 어디서든지 사용하세요.",
content: "1분만에 워크폼을 만들어 보세요!\n링크를 복사하여 어디서든지 사용하세요.",
image: "/images/land/s2.png",
},
{
Expand Down Expand Up @@ -284,7 +284,7 @@ export default function LandingPage() {
{/* 텍스트 섹션 - 장식 요소 제거 */}
<motion.div className="relative">
<motion.h2
className="text-center font-hakgyo text-4xl font-bold tracking-tight md:text-5xl lg:text-6xl"
className="text-center font-hakgyo text-4xl font-bold tracking-tight md:text-4xl lg:text-6xl"
style={{
background: "linear-gradient(to right, #71db77, #56c45d)",
WebkitBackgroundClip: "text",
Expand Down Expand Up @@ -499,7 +499,7 @@ export default function LandingPage() {
{/* 텍스트 섹션 */}
<motion.div className="relative">
<motion.h2
className="text-center font-hakgyo text-4xl font-bold tracking-tight md:text-5xl lg:text-6xl"
className="text-center font-hakgyo text-4xl font-bold tracking-tight md:text-4xl lg:text-6xl"
style={{
color: "#ffffff",
letterSpacing: "-0.02em",
Expand Down Expand Up @@ -585,7 +585,7 @@ export default function LandingPage() {
className={`fixed ${
isLargeScreen
? "right-12 top-1/2 -translate-y-1/2 space-y-4"
: "bottom-12 left-1/2 flex -translate-x-1/2 space-x-4"
: "bottom-12 left-12 flex flex-col space-y-4"
}`}
>
{slides.slice(1).map((_, index) => (
Expand Down
93 changes: 67 additions & 26 deletions src/app/(pages)/(workform)/work/[formId]/components/FormImage.tsx
Original file line number Diff line number Diff line change
@@ -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[];
Expand All @@ -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 (
<div className="relative flex h-[300px] justify-center lg:h-[460px]">
{/* 이미지 표시 */}
{imageUrls?.map((imageUrl, index) => (
<div
key={imageUrl}
className={`absolute h-full w-full transition-opacity duration-300 ${
index === currentPage ? "opacity-100" : "opacity-0"
}`}
>
<Image
src={imageUrl}
alt={`알바 이미지 ${index + 1}`}
className="h-full w-full rounded-lg object-cover"
priority={index === 0}
fill
/>
</div>
))}

{/* 인디케이터 */}
{imageUrls.filter((url) => isValidS3Url(url)).length > 1 && (
<div className="absolute bottom-4 left-1/2 -translate-x-1/2">
<Indicator imageCount={imageUrls?.length ?? 0} currentPage={currentPage} onPageChange={onPageChange} />
</div>
)}
</div>
<>
<div className="relative flex h-[300px] justify-center lg:h-[460px]">
{/* 이미지 표시 */}
{imageUrls?.map((imageUrl, index) => (
<div
key={imageUrl}
className={`absolute h-full w-full cursor-zoom-in transition-opacity duration-300 ${
index === currentPage ? "opacity-100" : "opacity-0"
}`}
onClick={handleImageClick}
role="button"
tabIndex={0}
onKeyDown={(e) => {
if (e.key === "Enter" || e.key === " ") {
handleImageClick();
}
}}
>
<Image
src={imageUrl}
alt={`알바 이미지 ${index + 1}`}
className="h-full w-full rounded-lg object-cover"
priority={index === 0}
fill
/>
</div>
))}

{/* 인디케이터 */}
{imageUrls.filter((url) => isValidS3Url(url)).length > 1 && (
<div className="absolute bottom-4 left-1/2 -translate-x-1/2">
<Indicator imageCount={imageUrls?.length ?? 0} currentPage={currentPage} onPageChange={onPageChange} />
</div>
)}
</div>

{/* Lightbox */}
<Lightbox
open={isOpen}
close={() => setIsOpen(false)}
slides={slides}
index={currentPage}
plugins={[Zoom, Counter]}
counter={{ container: { style: { top: "unset", bottom: 0 } } }}
on={{
view: ({ index }) => onPageChange(index),
}}
/>
</>
);
}
3 changes: 2 additions & 1 deletion src/app/clientLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
<QueryClientProvider client={queryClient}>
<div className="relative">
<div className={`relative min-h-[80vh] ${!isHome ? "pt-16" : ""}`}>
{children}
<MouseTrail />
<Toaster
Expand Down
37 changes: 23 additions & 14 deletions src/app/components/input/text/LocationInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,30 +6,34 @@ import { BaseInputProps } from "@/types/textInput";
import { forwardRef, useCallback, useState, useEffect } from "react";
import Script from "next/script";

// DaumPostcode 타입 정의
// Daum 우편번호 서비스에서 주소 검색 후 반환되는 데이터 타입
interface DaumPostcodeData {
address: string;
addressType: string;
bname: string;
buildingName: string;
zonecode: string;
[key: string]: string;
address: string; // 기본 주소 (도로명 주소 또는 지번 주소)
addressType: string; // 검색된 주소 타입 (R: 도로명, J: 지번)
bname: string; // 법정동/법정리 이름
buildingName: string; // 건물명
zonecode: string; // 우편번호
[key: string]: string; // 기타 가능한 추가 필드들
}

// 전역 Window 객체에 다음 우편번호 서비스 타입 선언
declare global {
interface Window {
daum: {
Postcode: new (config: { oncomplete: (data: DaumPostcodeData) => 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<HTMLInputElement, LocationInputProps>(
Expand All @@ -39,26 +43,31 @@ const LocationInput = forwardRef<HTMLInputElement, LocationInputProps>(
) => {
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 (
<>
{/* 다음 우편번호 서비스 스크립트 로드 */}
<Script src="//t1.daumcdn.net/mapjsapi/bundle/postcode/prod/postcode.v2.js" strategy="afterInteractive" />
<div className="relative w-full">
<BaseInput
Expand Down
4 changes: 2 additions & 2 deletions src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ export default function RootLayout({ children }: { children: React.ReactNode })
</head>
<body className="scrollbar-custom relative">
<ClientLayout>
<Header />
<main>{children}</main>
<CustomCursor />
<MouseTrail />
<Header />
<main className="min-h-[80vh] has-[header]:pt-16">{children}</main>
</ClientLayout>
<GoogleAnalytics gaId={process.env.NEXT_PUBLIC_GA_ID || ""} />
</body>
Expand Down
Loading