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
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,6 @@ yarn-error.log*
next-env.d.ts

# Linting Cache file
.eslintcache
.eslintcache

.vscode/*
181 changes: 149 additions & 32 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,36 +1,153 @@
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
## 밥팟 FE 레포

## Getting Started
### 폴더 구조
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

폴더 구조 정리 잘 봤습니다 굿


First, run the development server:

```bash
npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun dev
```
├── Dockerfile
├── README.md
├── components.json
├── eslint.config.mjs
├── next-env.d.ts
├── next.config.ts
├── package-lock.json
├── package.json
├── pnpm-lock.yaml
├── postcss.config.mjs
├── public // 정적 데이터
│ ├── data
│ │ ├── ChatResponse.ts // ✅ 채팅 생성 클래스입니다. (상황에 맞는 채팅 생성)
│ │ ├── categories.ts // 하드코딩된 음식점 카테고리
│ │ ├── index.ts // 어디넣어야 할지 모르는 것들
│ │ ├── restaurant.ts // AI 검색 음식점 리스트
│ │ └── tracks.ts // 트랙 데이터
│ ├── fonts
│ │ ├── font.ts
│ │ └── subset-PretendardVariable-Regular.woff2
│ └── images // 서비스 이미지 (모두 SVG 추후 최적화 변경)
│ ├── babpul.svg
│ ├── logo.svg
│ ├── part_example.svg
│ └── spoon.svg
├── scripts
│ └── deploy.sh // 🌥️ 클라우드 관리 파일
├── src
│ ├── app
│ │ ├── (header) // ✅ 헤더 + 푸터 존재 구조
│ │ │ ├── layout.tsx
│ │ │ ├── mypage // 마이페이지
│ │ │ │ ├── layout.tsx
│ │ │ │ ├── babpart // 내 밥팟
│ │ │ │ │ └── page.tsx
│ │ │ │ ├── requests // 받은 정산 요청
│ │ │ │ │ └── page.tsx
│ │ │ │ └── settlement // 정산 요청하기
│ │ │ │ └── page.tsx
│ │ │ └── page.tsx
│ │ ├── (headerless) // ❌ 헤더 + 푸터 미존재 구조
│ │ │ ├── layout.tsx
│ │ │ └── part
│ │ │ └── page.tsx // 밥팟 생성 페이지
│ │ │ ├── auth
│ │ │ │ ├── layout.tsx
│ │ │ │ ├── login // 로그인 페이지
│ │ │ │ │ └── page.tsx
│ │ │ │ └── register // 회원가입 페이지
│ │ │ │ └── page.tsx
│ │ ├── favicon.ico
│ │ ├── globals.css
│ │ └── layout.tsx
│ ├── components // 공용 컴포넌트
│ │ ├── Footer.tsx
│ │ ├── Header.tsx
│ │ ├── auth
│ │ │ └── Introduce.tsx // 소개 문구
│ │ ├── common
│ │ │ ├── AutoSlider.tsx // 메인페이지 (자동 슬라이더 기능)
│ │ │ ├── Backdrop.tsx // 백드롭
│ │ │ ├── KakaoMap.tsx // ✅ 카카오맵 컴포넌트 (SDK 활용)
│ │ │ ├── LoadingShimmer.tsx // AI 응답 대기 문구
│ │ │ ├── MapPin.tsx // 카카오맵 핀
│ │ │ └── Pagination.tsx // 페이지네이션 컴포넌트
│ │ ├── main // ⬇️ 아래부터는 각 서비스별로 필요한 컴포넌트입니다
│ │ │ ├── Alarm
│ │ │ │ └── HeaderAlarm.tsx
│ │ │ ├── PartCards
│ │ │ │ ├── PartCard.tsx
│ │ │ │ └── PartCardList.tsx
│ │ │ ├── Recommend
│ │ │ │ └── RecommendCard.tsx
│ │ │ └── RecommendCardList.tsx
│ │ ├── mypage
│ │ │ ├── RouteHeader.tsx
│ │ │ ├── RouteText.tsx
│ │ │ └── settlement
│ │ │ ├── RequestSettlementModal.tsx
│ │ │ └── SettlementTable.tsx
│ │ ├── part
│ │ │ ├── AIChatButtonFrame.tsx
│ │ │ ├── AIChatFrame.tsx // AI 응답에 대한 프레임
│ │ │ ├── ChatRoom.tsx
│ │ │ ├── PartCreationModal.tsx
│ │ │ ├── PlaceList.tsx
│ │ │ └── RefuseModal.tsx
│ │ └── ui // Shadcn 설치 파일
│ │ ├── DatePicker.tsx
│ │ ├── FilterSelector.tsx
│ │ ├── Loading.tsx
│ │ ├── button.tsx
│ │ ├── calendar.tsx
│ │ ├── checkbox.tsx
│ │ ├── input.tsx
│ │ ├── label.tsx
│ │ ├── pagination.tsx
│ │ ├── popover.tsx
│ │ ├── select.tsx
│ │ ├── sonner.tsx
│ │ └── timepicker
│ │ ├── input.tsx
│ │ ├── timepicker.tsx
│ │ └── utils.tsx
│ └── lib
│ ├── HTTP // ✅ 통신 관련 파일들입니다
│ │ ├── API // 각 서비스별로 모아둔 API입니다.
│ │ │ ├── auth.ts
│ │ │ ├── chat.ts
│ │ │ ├── mypage
│ │ │ │ └── settlement.ts
│ │ │ └── part.ts
│ │ ├── Fetch.ts // 커스텀 페칭함수
│ │ ├── index.ts
│ │ └── tanstack-query.ts // 🔥 React-Query 캐싱키 관리 및 Mutation 설정 파일입니다 중요! 🔥
│ ├── colors
│ │ └── colors.ts
│ ├── constants
│ │ ├── endpoint.ts
│ │ └── routes.ts
│ ├── context // zustand 활용 전역 데이터
│ │ └── responsiveStore.ts
│ ├── hooks // ✅ 커스텀 훅입니다!!
│ │ ├── useAuthData.ts
│ │ ├── useKakaoLoader.tsx
│ │ ├── useModal.tsx
│ │ ├── usePagination.tsx
│ │ ├── useScrollLock.tsx // 모달에서 스크롤 잠그기
│ │ ├── useServiceError.tsx // HTTP 통신에서 에러 핸들링
│ │ └── useToggle.ts
│ ├── provider
│ │ ├── LucideIcon.tsx // 아이콘 라이브러리 제공 컴포넌트
│ │ ├── QueryClientProvider.tsx // React-Query
│ │ └── useResponsiveProvider.tsx // zustand + tailwind(형식)으로 반응형 제공 프로바이더
│ ├── types
│ │ ├── auth
│ │ └── part
│ │ └── part.ts
│ └── utils
│ ├── date
│ │ ├── fromDateString.ts // 일반 Date String 형식을 변환하기 위한 정적 클래스
│ │ └── fromISOString.ts // ISO String 형식을 변환하기 위한 정적 클래스
│ ├── typeUtils.ts // 타입스크립트 유틸함수 모음
│ └── utils.ts
├── tailwind.config.ts
└── tsconfig.json

Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.

You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.

This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.

## Learn More

To learn more about Next.js, take a look at the following resources:

- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.

You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!

## Deploy on Vercel

The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.

Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
```
9 changes: 6 additions & 3 deletions public/data/ChatResponse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,15 @@ export type ChatType = {
speaker: 'user' | 'ai'
content: string
type?: ResponseType
doneClicking: boolean // 메인카테고리 or 키워드 선택하였는지 여부

doneClicking: boolean // 메인카테고리 or 키워드 선택하였는지 여부 (재선택 불가 로직을 위한 인자)
lastMainCategory?: MainCategoriesType
lastKeywords?: string[]
}

/**
* 채팅이 입력되었을때 추가되는 채팅에 대한 클래스입니다.
* 각 상황에 맞는 정적 메소드를 사용합니다.
*/
export class Chatting {
// #1 유저 채팅
static UserRequest = (UserRequest: string): ChatType => {
Expand All @@ -42,9 +45,9 @@ export class Chatting {
static MainCategoryResponse(mainCategory: MainCategoriesType): ChatType {
return {
speaker: 'ai',
content: `카테고리 선택을 완료하셨군요!\n\n더 좋은 응답을 생성하기 위해 어떤 종류의 ${mainCategory}을 원하는지 추가 키워드를 선택해주세요.`,
// Todo: 메인 카테고리 선택에 따른 이름 변동이 되도록 처리
// content: `${mainCategory}을 선택하셨네요.\n\n더 좋은 응답을 생성하기 위해 어떤 종류의 ${mainCategory}을 원하는지 추가 키워드를 선택해주세요.`,
content: `카테고리 선택을 완료하셨군요!\n\n더 좋은 응답을 생성하기 위해 어떤 종류의 ${mainCategory}을 원하는지 추가 키워드를 선택해주세요.`,
type: ResponseType.MAIN_CATEGORY,
doneClicking: false,
lastMainCategory: undefined,
Expand Down
5 changes: 5 additions & 0 deletions public/data/categories.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
/**
* 챗봇에서 사용하는 카테고리입니다.
* 메인 카테고리(대분류), 키워드가 존재합니다.
*/
const 한식: string[] = ['매콤', '구수한', '뜨끈한', '칼칼한', '밥', '면', '국물', '고기']
// TODO: 짜짱/짬뽕 -> 짜장/짬뽕 (AI 노티시)
const 중식: string[] = ['짜짱/짬뽕', '마라류', '양꼬치', '훠궈', '딤섬']
const 분식: string[] = ['김밥', '떡볶이', '핫도그']
const 일식: string[] = ['덮밥류', '초밥/사시미', '카츠', '나베', '야키토리', '튀김류', '라멘', '소바', '오마카세']
Expand Down
2 changes: 0 additions & 2 deletions public/data/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,3 @@ export const SpeedTransformer = {
MIDDLE: '적당히',
SLOW: '천천히',
}

export const mypageSize = `w-[90%] max-w-xl md:w-4/5 md:max-w-4xl lg:max-w-7xl`
4 changes: 4 additions & 0 deletions public/data/restaurant.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import { placeDTO } from '@app/(headerless)/part/page'

/**
* 처음 밥봇 대화시에 보여주는 더미 음식점입니다.
* TODO: 추후 무한스크롤로 데이터 교체
*/
export const placeListDummyData: placeDTO[] = [
{
id: 145,
Expand Down
Binary file removed public/images/ktb_1.jpeg
Binary file not shown.
9 changes: 0 additions & 9 deletions public/images/ktb_2.svg

This file was deleted.

1 change: 1 addition & 0 deletions public/images/part_example.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 4 additions & 3 deletions src/app/(header)/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,19 @@ import Footer from '@components/Footer'
import Header from '@components/Header'
import { cn } from '@lib/utils/utils'

import { layoutStyle } from './page'

interface HeaderLayoutProps {
children: ReactNode
}

/**
* 헤더 레이아웃은 헤더와 푸터를 포함하는 레이아웃입니다.
* 헤더 레이아웃은 (헤더 + 푸터)를 포함하는 레이아웃입니다.
*/
const HeaderLayout = ({ children }: HeaderLayoutProps): ReactNode => {
const pageSize = `w-4/5 max-w-xl md:max-w-4xl lg:max-w-7xl`
return (
<>
<Header className={cn('z-10 h-[12dvh] w-full max-w-xl px-8 sm:px-6 md:max-w-4xl lg:max-w-7xl lg:px-8')} />
<Header className={cn('z-10 h-[12dvh] max-w-xl', layoutStyle)} />
{children}
<Footer className='z-10 h-max w-full' />
</>
Expand Down
8 changes: 4 additions & 4 deletions src/app/(header)/mypage/babpart/page.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
'use client'
import { ReactNode } from 'react'

import { mypageLayoutStyle } from '@app/(header)/page'
import RouteHeader from '@components/mypage/RouteHeader'
import RouteText from '@components/mypage/RouteText'
import { URL } from '@lib/constants/routes'
import { useAuthData } from '@lib/hooks/useAuthData'
import { cn } from '@lib/utils/utils'
import { mypageSize } from '@public/data'

interface BabPartPageProps {}

const BabPartPage = ({}: BabPartPageProps): ReactNode => {
/** TODO(Auth): 로그인 되어있지 않으면 로그인 페이지로 리다이렉트 */
const { nickname } = useAuthData()

return (
<>
<RouteHeader route={URL.MYPAGE.BABPART.value} className='w-full border-b border-solid border-rcLightGray' />
<RouteText content={`${nickname}님이 생성하신 밥팟입니다!`} className={cn(mypageSize, 'my-6')} />
<div className={cn(mypageSize, 'mt-10 w-full items-center justify-center text-center font-dohyeon text-2xl')}>
<RouteText content={`${nickname}님이 생성하신 밥팟입니다!`} className={cn(mypageLayoutStyle, 'my-6')} />
<div className={cn(mypageLayoutStyle, 'mt-10 w-full items-center justify-center text-center font-dohyeon text-2xl')}>
서비스 준비중입니다...!
</div>
</>
Expand Down
8 changes: 3 additions & 5 deletions src/app/(header)/mypage/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,17 @@
import { ReactNode } from 'react'

import { cn } from '@lib/utils/utils'
import { mypageSize } from '@public/data'

import { mypageLayoutStyle } from '../page'

interface MypageLayoutProps {
children: ReactNode
}

/**
* 헤더 레이아웃은 헤더와 푸터를 포함하는 레이아웃입니다.
*/
const MypageLayout = ({ children }: MypageLayoutProps): ReactNode => {
return (
<main className='relative flex min-h-screen w-screen flex-col items-center justify-start bg-rcWhite'>
<div className={cn(mypageSize, 'font-dohyeon text-3xl')}>마이페이지</div>
<div className={cn(mypageLayoutStyle, 'font-dohyeon text-3xl')}>마이페이지</div>
{children}
</main>
)
Expand Down
8 changes: 4 additions & 4 deletions src/app/(header)/mypage/requests/page.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
'use client'
import { ReactNode } from 'react'

import { mypageLayoutStyle } from '@app/(header)/page'
import RouteHeader from '@components/mypage/RouteHeader'
import RouteText from '@components/mypage/RouteText'
import { URL } from '@lib/constants/routes'
import { useAuthData } from '@lib/hooks/useAuthData'
import { cn } from '@lib/utils/utils'
import { mypageSize } from '@public/data'

interface RequestsPageProps {}

const RequestsPage = ({}: RequestsPageProps): ReactNode => {
/** TODO(Auth): 로그인 되어있지 않으면 로그인 페이지로 리다이렉트 */
const { nickname } = useAuthData()

return (
<>
<RouteHeader route={URL.MYPAGE.REQUESTS.value} className='w-full border-b border-solid border-rcLightGray' />
<RouteText content={`${nickname}님이 받은 밥팟 정산 알림입니다!`} className={cn(mypageSize, 'my-6')} />
<div className={cn(mypageSize, 'mt-10 w-full items-center justify-center text-center font-dohyeon text-2xl')}>
<RouteText content={`${nickname}님이 받은 밥팟 정산 알림입니다!`} className={cn(mypageLayoutStyle, 'my-6')} />
<div className={cn(mypageLayoutStyle, 'mt-10 w-full items-center justify-center text-center font-dohyeon text-2xl')}>
서비스 준비중입니다...!
</div>
</>
Expand Down
9 changes: 4 additions & 5 deletions src/app/(header)/mypage/settlement/page.tsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,27 @@
'use client'
import { ReactNode, Suspense } from 'react'

import { mypageLayoutStyle } from '@app/(header)/page'
import RouteHeader from '@components/mypage/RouteHeader'
import RouteText from '@components/mypage/RouteText'
import SettlementTable from '@components/mypage/settlement/SettlementTable'
import Loading from '@components/ui/Loading'
import { URL } from '@lib/constants/routes'
import { useAuthData } from '@lib/hooks/useAuthData'
import { cn } from '@lib/utils/utils'
import { mypageSize } from '@public/data'

interface SettlementPageProps {}

const SettlementPage = ({}: SettlementPageProps): ReactNode => {
const { nickname } = useAuthData()

/** TODO: 로그인 되어있지 않으면 로그인 페이지로 리다이렉트 */

/** TODO(Auth): 로그인 되어있지 않으면 로그인 페이지로 리다이렉트 */
return (
<>
<RouteHeader route={URL.MYPAGE.SETTLEMENT.value} className='w-full border-b border-solid border-rcLightGray' />
<RouteText content={`${nickname}님이 참여하신 밥팟의 정산현황입니다!`} className={cn(mypageSize, 'my-6')} />
<RouteText content={`${nickname}님이 참여하신 밥팟의 정산현황입니다!`} className={cn(mypageLayoutStyle, 'my-6')} />
<Suspense fallback={<Loading />}>
<SettlementTable className={cn(mypageSize)} />
<SettlementTable className={cn(mypageLayoutStyle)} />
</Suspense>
</>
)
Expand Down
Loading