Skip to content
Merged
Show file tree
Hide file tree
Changes from 38 commits
Commits
Show all changes
103 commits
Select commit Hold shift + click to select a range
72f60e4
refactor: ButtonIcon 서브컴포넌트 제거 ->prop으로 관리할수 있게 변경
seongihun Dec 31, 2025
4935d50
refactor: ButtonIcon 서브컴포넌트 제거 ->prop으로 관리할수 있게 변경
seongihun Dec 31, 2025
453d635
refactor: ref가 prop으로 자동전달 componentPropsWithRef 사용
seongihun Dec 31, 2025
a22c112
🎨 Style: reservation-view SVG 아이콘 추가
Greensod-96 Jan 1, 2026
9543318
🎨 Style: MyInfo 페이지 p-6 패딩 제거
Greensod-96 Jan 1, 2026
e7b1e7f
✨ Feat: ReservationView 구현
Greensod-96 Jan 1, 2026
629c8b3
Merge branch 'develop' into feat/reservation-view
Greensod-96 Jan 1, 2026
93ce5f6
♻️ Refactor: InputField 공통 컴포넌트 구조 정리 및 스타일 통합
Greensod-96 Jan 1, 2026
2bb347b
♻️ Refactor: PasswordInput 컴포넌트 구조 단순화 및 스타일 통합
Greensod-96 Jan 1, 2026
cd9be8a
♻️ Refactor: SearchInput 컴포넌트 구조 단순화 및 스타일 통합
Greensod-96 Jan 1, 2026
55b14fd
♻️ Refactor: Input 컴포넌트 스타일 통합 및 기본 UI 개선
Greensod-96 Jan 1, 2026
d8608da
♻️ Refactor: InputField 구조 단순화 및 children 기반 렌더링 개선
Greensod-96 Jan 1, 2026
19a6003
♻️ Refactor: PasswordInput 스타일 단순화 및 Input 책임 분리
Greensod-96 Jan 1, 2026
2acbb69
♻️ Refactor: SearchInput 스타일 단순화 및 공통 Input 활용
Greensod-96 Jan 1, 2026
1794f38
♻️ Refactor: Input 컴포넌트에 forwardRef 적용
Greensod-96 Jan 1, 2026
4d9aa6a
♻️ Refactor: InputField에 label 연결 및 접근성 개선
Greensod-96 Jan 1, 2026
6ad6326
사이드바 반응형 구현
seongihun Jan 2, 2026
18eebb0
♻️ Refactor: Input 컴포넌트에서 forwardRef 제거
Greensod-96 Jan 2, 2026
35e9641
♻️ Refactor: InputField에서 inputProps 제거 및 props 구조 단순화
Greensod-96 Jan 2, 2026
1699df1
♻️ Refactor: PasswordInput 컴포넌트 구조 개선 및 접근성 보완
Greensod-96 Jan 2, 2026
7ea8180
♻️ Refactor: SearchInput props 구조 개선
Greensod-96 Jan 2, 2026
5a66ca6
🎨 Style: PasswordInput 아이콘 이미지 alt 속성 제거
Greensod-96 Jan 3, 2026
d8dbaf6
Merge pull request #20 from 5team-gn/feat/reservation-view
Greensod-96 Jan 3, 2026
a18a794
Merge pull request #21 from 5team-gn/refactor/input-components
Greensod-96 Jan 3, 2026
2f1e732
refactor: ButtonIcon 서브컴포넌트 제거 ->prop으로 관리할수 있게 변경
seongihun Dec 31, 2025
b94af9f
refactor: ButtonIcon 서브컴포넌트 제거 ->prop으로 관리할수 있게 변경
seongihun Dec 31, 2025
6950cc1
refactor: ref가 prop으로 자동전달 componentPropsWithRef 사용
seongihun Dec 31, 2025
57bf37c
refactor: 리뷰반영수정
seongihun Jan 3, 2026
38cfe5d
Merge branch 'feat/Button' of https://github.com/5team-gn/GlobalNomad…
seongihun Jan 3, 2026
32a410d
Merge pull request #19 from 5team-gn/feat/Button
seongihun Jan 3, 2026
e5e9470
사용법 마크다운 문서로 정리
seongihun Jan 3, 2026
f51596a
Merge pull request #23 from 5team-gn/feat/Button
seongihun Jan 3, 2026
a220ced
page.tsx 수정
seongihun Jan 3, 2026
498e8a5
재수정
seongihun Jan 3, 2026
5b6167f
사이드바 반응형 구현
seongihun Jan 2, 2026
fac05b3
리뷰반영 주석 제거
seongihun Jan 3, 2026
48f7cc3
리뷰반영 수정
seongihun Jan 3, 2026
9e44b5b
Merge branch 'feat/MyInfo' of https://github.com/5team-gn/GlobalNomad…
seongihun Jan 3, 2026
23e69a1
브랜치 동기화
baejm Dec 30, 2025
79fa067
상세 작업중1 브랜치전환용
baejm Dec 24, 2025
c05d453
달력작업중
baejm Dec 25, 2025
7167da4
모바일/태블릿 하나로
baejm Dec 25, 2025
ed13745
모바일/태블릿/pc 하나로
baejm Dec 25, 2025
08365e8
모바일/태블릿/pc 하나로2
baejm Dec 26, 2025
f8b814b
레이아웃 완성
baejm Dec 26, 2025
d56717c
ui 작업중
baejm Dec 26, 2025
bea79aa
달력 라이브러리로 교체
baejm Dec 28, 2025
21ce3a0
ui완성
baejm Dec 29, 2025
ac6fba9
불필요 파일제거
baejm Dec 30, 2025
18660b0
디테일 페이지 ui 1차
baejm Jan 2, 2026
c873f62
주석 및 파일정리
baejm Jan 3, 2026
569b429
충돌해결
baejm Jan 3, 2026
1800280
주석 및 폴더정리
baejm Jan 3, 2026
be71aea
ui및 타입추가
baejm Jan 5, 2026
6f6e370
⚙️ Chore: axios, react-hot-toast 라이브러리 설치
Greensod-96 Jan 5, 2026
845aa6f
🎨 Style: 파비콘 추가
Greensod-96 Jan 5, 2026
919eddd
✨ Feat: 전역 레이아웃에 react-hot-toast Toaster 추가
Greensod-96 Jan 5, 2026
826eae5
♻️ Refactor: ReservationView UI 구조 및 로직 분리
Greensod-96 Jan 5, 2026
a4cd7a3
✨ Feat: 예약 취소 모달 CancelContent 컴포넌트 구현
Greensod-96 Jan 5, 2026
f265a35
✨ Feat: ReservationCard 컴포넌트 구현
Greensod-96 Jan 5, 2026
e7e1472
✨ Feat: ReservationEmpty 컴포넌트 구현
Greensod-96 Jan 5, 2026
1e614b4
✨ Feat: ReservationFilter 컴포넌트 구현
Greensod-96 Jan 5, 2026
0011bd7
✨ Feat: ReservationList 컴포넌트 구현
Greensod-96 Jan 5, 2026
c975314
♻️ Refactor: 예약 상태 라벨 및 스타일 상수 분리
Greensod-96 Jan 5, 2026
2f94d3a
✨ Feat: 후기 작성 모달 ReviewContent 컴포넌트 구현
Greensod-96 Jan 5, 2026
344edd9
♻️ Refactor: 예약 취소 및 후기 작성 액션 로직 hook 분리
Greensod-96 Jan 5, 2026
abca0d3
✨ Feat: 예약 목록 관리를 위한 useReservationList 훅 구현
Greensod-96 Jan 5, 2026
52e6cf7
✨ Feat: 예약 모달 상태 관리를 위한 useReservationModal 훅 구현
Greensod-96 Jan 5, 2026
a0aad37
✨ Feat: 예약 관련 API 모듈 및 axios 클라이언트 구성
Greensod-96 Jan 5, 2026
fbe3d3a
🧪 Test: 예약 목록 UI 테스트용 mock 데이터 추가
Greensod-96 Jan 5, 2026
1145376
🎨 Style: 예약 상태 및 예약 타입 정의 추가
Greensod-96 Jan 5, 2026
98f018f
🎨 Style: 예약 카드 이미지 리소스 추가
Greensod-96 Jan 5, 2026
68810fa
♻️ Refactor: 후기 작성 여부 관리를 위해 Reservation 타입 확장
Greensod-96 Jan 5, 2026
483c3d2
♻️ Refactor: ReservationList 불필요한 filter props 제거
Greensod-96 Jan 5, 2026
3975590
✨ Feat: 후기 작성 완료된 예약에 후기 버튼 미노출 처리
Greensod-96 Jan 5, 2026
124e142
♻️ Refactor: ReservationEmpty에서 상태 라벨 매핑 상수화
Greensod-96 Jan 5, 2026
1e273b9
♻️ Refactor: ReservationList 역할 단순화 및 책임 분리
Greensod-96 Jan 5, 2026
8559a8c
♻️ Refactor: useReservationList 비동기 로딩 구조 정리
Greensod-96 Jan 5, 2026
ee1281f
♻️ Refactor: completed 예약에 reviewWritten mock 케이스 추가
Greensod-96 Jan 5, 2026
7c5dd79
🎨 Style: CancelContent 색상 var 값 제거 및 tailwind 컬러로 변경
Greensod-96 Jan 5, 2026
0ca2fbe
🎨 Style: ReservationCard 색상 var 제거 및 tailwind 컬러로 변경
Greensod-96 Jan 5, 2026
f292073
🎨 Style: ReservationEmpty 색상 var 제거 및 tailwind 컬러로 변경
Greensod-96 Jan 5, 2026
64e60fb
🎨 Style: 예약 취소 모달 버튼 hover/active 스타일 개선
Greensod-96 Jan 5, 2026
497ac9f
Merge pull request #25 from 5team-gn/refactor/reservation-view
Greensod-96 Jan 6, 2026
d4f72cc
ai 코드리뷰 적용
baejm Jan 6, 2026
8493147
Merge branch 'develop' into feature/activities-detail
baejm Jan 6, 2026
26e3eef
Merge pull request #24 from 5team-gn/feature/activities-detail
baejm Jan 6, 2026
8e05434
♻️ Refactor: ReservationView 필터 안전 처리 및 렌더링 로직 단순화
Greensod-96 Jan 6, 2026
d07733e
✨ Feat: 로그인 상태 판단용 useAuth 훅 구현
Greensod-96 Jan 6, 2026
4b86544
✨ Feat: 예약 취소 및 후기 작성 API를 useReservationActions에 연동
Greensod-96 Jan 6, 2026
da0506e
✨ Feat: 예약 목록 조회 API를 useReservationList에 연동
Greensod-96 Jan 6, 2026
1b3db40
✨ Feat: 인증 헤더 및 인터셉터가 포함된 Axios client 설정
Greensod-96 Jan 6, 2026
aa22c22
♻️ Refactor: reservationApi 인증 처리 및 mock 제어 로직 정리
Greensod-96 Jan 6, 2026
043ecec
♻️ Refactor: ReservationView에서 불필요한 safeFilter 제거
Greensod-96 Jan 6, 2026
63c0825
♻️ Refactor: axios client에서 테스트 토큰 제거 및 인증 헤더 처리 정리
Greensod-96 Jan 6, 2026
e3efa8f
Merge pull request #27 from 5team-gn/feat/reservation-api
Greensod-96 Jan 6, 2026
5ef17de
사이드바 반응형 구현
seongihun Jan 2, 2026
2765b02
리뷰반영 주석 제거
seongihun Jan 3, 2026
12129b1
리뷰반영 수정
seongihun Jan 3, 2026
607bd6c
사이드바 반응형 구현
seongihun Jan 2, 2026
bd9a3a2
Merge branch 'feat/MyInfo' of https://github.com/5team-gn/GlobalNomad…
seongihun Jan 7, 2026
54edc8a
입력 컴포넌트 경로 수정: common에서 input으로 변경
seongihun Jan 7, 2026
f202b5b
예약 상태 뷰 컴포넌트 추가: 예약 목록 필터링 및 날짜 선택 기능 구현
seongihun Jan 7, 2026
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
83 changes: 49 additions & 34 deletions components/button/Button.tsx
Original file line number Diff line number Diff line change
@@ -1,62 +1,77 @@
"use client";

import { ButtonHTMLAttributes, ReactNode } from "react";
import { ReactNode, ComponentPropsWithRef } from "react";
import { cn } from "@/lib/utils/twmerge";
import {
ButtonVariants,
buttonIconVariants,
buttonLabelVariants,
} from "./ButtonVariants";
import { cn } from "@/lib/utils/twmerge";

interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
variant?: "primary" | "secondary" | "text" | "ghost";
size?: "lg" | "md" | "sm";
}
const DEFAULT_BUTTON_PROPS = {
variant: "primary",
size: "md",
} as const;

interface ButtonSubProps {
type BaseButtonProps = {
variant?: "primary" | "secondary" | "text" | "ghost";
size?: "lg" | "md" | "sm";
className?: string;
children: ReactNode;
}
leftIcon?: ReactNode;
rightIcon?: ReactNode;
};

export const ButtonIcon = ({
variant = "primary",
className,
children,
}: ButtonSubProps) => (
<span className={cn(buttonIconVariants({ variant }), className)}>
{children}
</span>
);
export type ButtonProps = ComponentPropsWithRef<"button"> & BaseButtonProps;

export const ButtonLabel = ({
variant = "primary",
size = "md",
className,
children,
}: ButtonSubProps) => (
<span className={cn(buttonLabelVariants({ variant, size }), className)}>
{children}
</span>
);
export type ButtonLabelProps = ButtonProps & {
label?: string;
};

export function Button({
variant = "primary",
size = "md",
export const Button = ({
variant = DEFAULT_BUTTON_PROPS.variant,
size = DEFAULT_BUTTON_PROPS.size,
disabled = false,
className,
children,
leftIcon,
rightIcon,
ref,
...props
}: ButtonProps) {
}: ButtonProps) => {
return (
<button
type="button"
disabled={disabled}
className={cn(ButtonVariants({ variant, size }), className)}
ref={ref}
{...props}
>
{leftIcon && (
<span className={cn(buttonIconVariants({ variant }))}>{leftIcon}</span>
)}
{children}
{rightIcon && (
<span className={cn(buttonIconVariants({ variant }))}>{rightIcon}</span>
)}
</button>
);
}
};

export const ButtonLabel = ({
label,
children,
variant = DEFAULT_BUTTON_PROPS.variant,
size = DEFAULT_BUTTON_PROPS.size,
ref,
...buttonProps
}: ButtonLabelProps) => {
return (
<Button variant={variant} size={size} ref={ref} {...buttonProps}>
{label && (
<span className={cn(buttonLabelVariants({ variant, size }))}>
{label}
</span>
)}
{children}
</Button>
);
};
138 changes: 138 additions & 0 deletions components/button/사용법.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
1️⃣ Button은 이렇게 생긴 걸 만들 수 있어요 👇

✔️ 확인 버튼

❌ 취소 버튼

➕ 아이콘이 있는 버튼

🔒 비활성화된 버튼

2️⃣ 기본 사용법 (가장 쉬운 예)

```
<Button>버튼</Button>
```
화면에 기본 버튼이 하나 생겨요
(파란색, 중간 크기)

3️⃣ 버튼의 중요한 속성들

🔹 1. variant (버튼 색깔 / 모양)

버튼의 종류를 정하는 거예요.

primary: 제일 중요한 버튼 (파란색)

secondary: 보조 버튼 (흰색 + 테두리)

text: 배경 없는 글자 버튼

ghost: 거의 안 보이는 버튼

```
<Button variant="primary">확인</Button>
<Button variant="secondary">취소</Button>
<Button variant="text">자세히 보기</Button>
<Button variant="ghost">옵션</Button>
```

🔹 2. size (버튼 크기)

버튼의 키와 두께를 정해요.

lg: 큼

md: 보통 (기본값)

sm: 작음

```
<Button size="lg">큰 버튼</Button>
<Button size="md">보통 버튼</Button>
<Button size="sm">작은 버튼</Button>
```

🔹 3. disabled (비활성화)

```
<Button disabled>비활성 버튼</Button>
```

4️⃣ 아이콘이 있는 버튼

왼쪽 아이콘

```
<Button leftIcon={<PlusIcon />}>추가</Button>
```

오른쪽 아이콘

```
<Button rightIcon={<ArrowRightIcon />}>다음</Button>
```

양쪽 다 아이콘

```
<Button
leftIcon={<PlusIcon />}
rightIcon={<ArrowRightIcon />}
>
이동
</Button>
```

5️⃣ ButtonLabel 컴포넌트 (글자 전용 버튼)

버튼 안에 글자 스타일을 자동으로 예쁘게 만들어줘요

기본 사용
```
<ButtonLabel label="저장" />
```


아이콘 + 글자 같이 쓸때
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

문서에 오타가 있습니다. '쓸때'는 '쓸 때'로 띄어 써야 올바른 맞춤법입니다.

Suggested change
아이콘 + 글자 같이 쓸때
아이콘 + 글자 같이 쓸 때


```
<ButtonLabel
label="삭제"
variant="secondary"
leftIcon={<TrashIcon />}
/>
```


6️⃣ Button vs ButtonLabel 차이

Button: 자유롭게 안에 내용 넣기

ButtonLabel: 글자(label)를 쉽게 쓰기

7️⃣ 자주 쓰는 예제 모음 ⭐
✔️ 확인 / 취소 버튼

```
<Button variant="primary">확인</Button>
<Button variant="secondary">취소</Button>
```

플러스 모양 아이콘+추가하기

```
<Button leftIcon={<PlusIcon />}>
추가하기
</Button>
```

클릭 못 하는 버튼

```
<Button disabled>
준비 중
</Button>
```
15 changes: 13 additions & 2 deletions components/input/Input.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,19 @@
import { ComponentProps } from "react";
import clsx from "clsx";

export function Input({ className, ...props }: ComponentProps<"input">) {
type InputProps = ComponentProps<"input"> & {
inputRef?: React.Ref<HTMLInputElement>;
};

export function Input({ className, inputRef, ...props }: InputProps) {
Comment on lines +4 to +8
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

inputRef prop으로 ref를 전달하는 대신 React.forwardRef를 사용하는 것이 더 표준적이고 권장되는 방식입니다. 이렇게 하면 컴포넌트 사용자가 일반 DOM 요소처럼 ref prop을 직접 사용할 수 있어 API가 더 직관적이게 됩니다.

리팩토링 예시:

import { ComponentProps, forwardRef } from "react";
// ...

type InputProps = ComponentProps<"input">;

export const Input = forwardRef<HTMLInputElement, InputProps>(
  ({ className, ...props }, ref) => {
    return (
      <input
        ref={ref}
        {...props}
        // ... className
      />
    );
  }
);

Input.displayName = "Input";

return (
<input {...props} className={clsx("border rounded px-3 py-2", className)} />
<input
ref={inputRef}
{...props}
className={clsx(
"h-[54px] w-full rounded-[16px] border px-[20px] text-sm outline-none",
className
)}
/>
);
}
33 changes: 24 additions & 9 deletions components/input/inputfield.tsx
Original file line number Diff line number Diff line change
@@ -1,29 +1,44 @@
import { ReactNode } from "react";
import { Input } from "./Input";
import { useId, ReactNode } from "react";
import clsx from "clsx";
import { Input } from "./Input";

interface InputFieldProps {
interface InputFieldProps extends React.ComponentProps<typeof Input> {
label?: string;
error?: string;
helperText?: string;
inputProps?: React.ComponentProps<typeof Input>;
children?: ReactNode;
className?: string;
}

export function InputField({
label,
error,
helperText,
inputProps,
children,
className,
...props
}: InputFieldProps) {
const id = useId();

return (
<div className={clsx("flex flex-col gap-1", className)}>
{label && <label className="text-sm font-medium">{label}</label>}
<div className="flex w-full flex-col gap-[10px]">
{label && (
<label htmlFor={id} className="text-[16px] font-medium text-gray-900">
{label}
</label>
)}

{children ?? <Input {...inputProps} />}
{children ?? (
<Input
id={id}
{...props}
className={clsx(
error
? "border-red-500 focus:ring-red-500"
: "border-gray-200 focus:ring-primary-500",
className
)}
/>
)}

{error ? (
<p className="text-xs text-red-500">{error}</p>
Expand Down
15 changes: 8 additions & 7 deletions components/input/passwordinput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,31 @@ import { useState } from "react";
import Image from "next/image";
import { Input } from "./Input";
import type { ComponentProps } from "react";
import clsx from "clsx";

interface PasswordInputProps extends Omit<ComponentProps<"input">, "type"> {
className?: string;
}
type PasswordInputProps = ComponentProps<typeof Input>;

export function PasswordInput({ className, ...props }: PasswordInputProps) {
const [visible, setVisible] = useState(false);

return (
<div className="relative">
<div className="relative w-full">
<Input
{...props}
type={visible ? "text" : "password"}
className={`pr-10 ${className ?? ""}`}
className={clsx("pr-12", className)}
/>

<button
type="button"
aria-label={visible ? "비밀번호 숨기기" : "비밀번호 표시"}
aria-pressed={visible}
onClick={() => setVisible((v) => !v)}
className="absolute right-3 top-1/2 -translate-y-1/2"
className="absolute right-4 top-1/2 -translate-y-1/2 focus:outline-none focus:ring-2 focus:ring-primary-500"
>
<Image
src={visible ? "/icon_active_on.svg" : "/icon_active_off.svg"}
alt={visible ? "비밀번호 숨기기" : "비밀번호 보기"}
alt=""
width={20}
height={20}
/>
Expand Down
17 changes: 8 additions & 9 deletions components/input/searchinput.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
import Image from "next/image";
import { Input } from "./Input";
import type { ComponentProps } from "react";
import clsx from "clsx";

interface SearchInputProps extends ComponentProps<"input"> {
className?: string;
}

export function SearchInput({ className, ...props }: SearchInputProps) {
export function SearchInput({
className,
...props
}: ComponentProps<typeof Input>) {
return (
<div className="relative">
<Input {...props} className={`pl-10 ${className ?? ""}`} />

<span className="pointer-events-none absolute left-3 top-1/2 -translate-y-1/2">
<div className="relative w-full">
<Input {...props} className={clsx("pl-[44px]", className)} />
<span className="pointer-events-none absolute left-4 top-1/2 -translate-y-1/2">
<Image src="/icon_search.svg" alt="검색" width={20} height={20} />
</span>
</div>
Expand Down
Loading