diff --git a/apps/admin/package.json b/apps/admin/package.json index b231237..da47f35 100644 --- a/apps/admin/package.json +++ b/apps/admin/package.json @@ -14,7 +14,11 @@ "react": "catalog:", "react-dom": "catalog:", "next": "catalog:", - "@repo/ui": "workspace:*" + "@heroui/react": "catalog:", + "@heroui/system": "catalog:", + "@heroui/theme": "catalog:", + "@repo/ui": "workspace:*", + "react-naver-maps": "catalog:" }, "devDependencies": { "@next/eslint-plugin-next": "catalog:", @@ -26,6 +30,7 @@ "@types/react": "catalog:", "@types/react-dom": "catalog:", "@tailwindcss/postcss": "catalog:", + "@types/navermaps": "catalog:", "postcss": "catalog:", "tailwindcss":"catalog:", "autoprefixer": "catalog:", diff --git a/apps/admin/public/fonts/PretendardVariable.woff2 b/apps/admin/public/fonts/PretendardVariable.woff2 new file mode 100644 index 0000000..32b0811 Binary files /dev/null and b/apps/admin/public/fonts/PretendardVariable.woff2 differ diff --git a/apps/admin/src/app/NaverMapProvider.tsx b/apps/admin/src/app/NaverMapProvider.tsx new file mode 100644 index 0000000..5dcdcc4 --- /dev/null +++ b/apps/admin/src/app/NaverMapProvider.tsx @@ -0,0 +1,10 @@ +'use client' + +import { NavermapsProvider } from 'react-naver-maps' +import { ReactNode } from 'react' + +export const NaverMapProvider = ({ children }: { children: ReactNode }) => { + const ncpKeyId = process.env.NEXT_PUBLIC_NAVER_MAP_ID || '' + + return {children} +} diff --git a/apps/admin/src/app/_api/types.ts b/apps/admin/src/app/_api/types.ts new file mode 100644 index 0000000..dc9eaea --- /dev/null +++ b/apps/admin/src/app/_api/types.ts @@ -0,0 +1,8 @@ +import type { IconType } from '@repo/ui/components/Icon' + +export type Request = { + placeId: string + placeName: string + icon: IconType + requestDate: string +} diff --git a/apps/admin/src/app/_components/RequestListItem/RequestListItem.tsx b/apps/admin/src/app/_components/RequestListItem/RequestListItem.tsx new file mode 100644 index 0000000..5d4d528 --- /dev/null +++ b/apps/admin/src/app/_components/RequestListItem/RequestListItem.tsx @@ -0,0 +1,31 @@ +import { Column, Flex } from '@repo/ui/components/Layout' +import { cn } from '@repo/ui/utils/cn' +import { Text } from '@repo/ui/components/Text' +import { Icon } from '@repo/ui/components/Icon' +import { CLIENT_PATH } from '@/consts/path' +import { Request } from '@/app/_api/types' + +export const RequestListItem = ({ + placeId, + placeName, + icon, + requestDate, +}: Request) => ( +
  • + + + + {placeName} + + + + + 등록 신청 일자: {requestDate} + + +
  • +) diff --git a/apps/admin/src/app/_components/RequestListItem/index.tsx b/apps/admin/src/app/_components/RequestListItem/index.tsx new file mode 100644 index 0000000..78851cb --- /dev/null +++ b/apps/admin/src/app/_components/RequestListItem/index.tsx @@ -0,0 +1 @@ +export { RequestListItem } from './RequestListItem' diff --git a/apps/admin/src/app/_components/RequestsByCampus/RequestsByCampus.tsx b/apps/admin/src/app/_components/RequestsByCampus/RequestsByCampus.tsx new file mode 100644 index 0000000..d99cd6d --- /dev/null +++ b/apps/admin/src/app/_components/RequestsByCampus/RequestsByCampus.tsx @@ -0,0 +1,23 @@ +import { Column } from '@repo/ui/components/Layout' +import { Text } from '@repo/ui/components/Text' +import { CAMPUS, type CampusType } from '@/consts/campus' +import { RequestListItem } from '@/app/_components/RequestListItem' +import { Request } from '@/app/_api/types' + +type Props = { + campus: CampusType + requestList: Request[] +} + +export const RequestsByCampus = ({ campus, requestList }: Props) => ( + + + {CAMPUS[campus]}캠 + + + +) diff --git a/apps/admin/src/app/_components/RequestsByCampus/index.tsx b/apps/admin/src/app/_components/RequestsByCampus/index.tsx new file mode 100644 index 0000000..92dc39f --- /dev/null +++ b/apps/admin/src/app/_components/RequestsByCampus/index.tsx @@ -0,0 +1 @@ +export { RequestsByCampus } from './RequestsByCampus' diff --git a/apps/admin/src/app/globals.css b/apps/admin/src/app/globals.css index 5e7ce44..bc7cdc1 100644 --- a/apps/admin/src/app/globals.css +++ b/apps/admin/src/app/globals.css @@ -1,2 +1,4 @@ @import "tailwindcss"; -@import "@repo/tailwind-config"; \ No newline at end of file +@import "@repo/tailwind-config"; +@plugin './hero.ts'; +@source '../../node_modules/@heroui/theme/dist/**/*.{js,ts,jsx,tsx}'; \ No newline at end of file diff --git a/apps/admin/src/app/hero.ts b/apps/admin/src/app/hero.ts new file mode 100644 index 0000000..331171f --- /dev/null +++ b/apps/admin/src/app/hero.ts @@ -0,0 +1,4 @@ +import { heroui } from '@heroui/theme' +import type { Config } from 'tailwindcss' + +export default heroui() as Config diff --git a/apps/admin/src/app/layout.tsx b/apps/admin/src/app/layout.tsx index 767519a..9dce728 100644 --- a/apps/admin/src/app/layout.tsx +++ b/apps/admin/src/app/layout.tsx @@ -1,34 +1,39 @@ import './globals.css' import '@repo/ui/styles.css' import type { Metadata } from 'next' -import { Geist, Geist_Mono } from 'next/font/google' - -const geistSans = Geist({ - variable: '--font-geist-sans', - subsets: ['latin'], -}) - -const geistMono = Geist_Mono({ - variable: '--font-geist-mono', - subsets: ['latin'], -}) +import localFont from 'next/font/local' +import { HeroUIProvider } from '@heroui/system' +import { Column } from '@repo/ui/components/Layout' +import { NaverMapProvider } from '@/app/NaverMapProvider' export const metadata: Metadata = { title: 'Create Next App', description: 'Generated by create next app', } +const pretendard = localFont({ + src: '../../public/fonts/PretendardVariable.woff2', + display: 'swap', + weight: '45 920', +}) + export default function RootLayout({ children, }: Readonly<{ children: React.ReactNode }>) { return ( - - - {children} + + + + +
    + + {children} + +
    +
    +
    ) diff --git a/apps/admin/src/app/page.tsx b/apps/admin/src/app/page.tsx index 1923102..1339cef 100644 --- a/apps/admin/src/app/page.tsx +++ b/apps/admin/src/app/page.tsx @@ -1,12 +1,38 @@ -import { Column } from '@repo/ui/components/Layout' -import { Text } from '@repo/ui/components/Text' -import { Button } from '@repo/ui/components/Button' +import { VerticalScrollArea } from '@repo/ui/components/Layout' +import { RequestsByCampus } from './_components/RequestsByCampus' +import { OnlyLeftHeader } from '@repo/ui/components/Header' +import type { Request } from './_api/types' + +const MOCK_DATA: Request[] = [ + { + placeId: '1', + placeName: '짬뽕집', + icon: 'chinese', + requestDate: '2025-10-29', + }, + { + placeId: '2', + placeName: '짬뽕집', + icon: 'chinese', + requestDate: '2025-10-29', + }, + { + placeId: '3', + placeName: '짬뽕집', + icon: 'chinese', + requestDate: '2025-10-29', + }, +] export default function Home() { return ( - - Hello World! - - + <> + + + + + + + ) } diff --git a/apps/admin/src/app/request/[id]/RequestDetailPage.tsx b/apps/admin/src/app/request/[id]/RequestDetailPage.tsx new file mode 100644 index 0000000..3edba20 --- /dev/null +++ b/apps/admin/src/app/request/[id]/RequestDetailPage.tsx @@ -0,0 +1,82 @@ +'use client' + +import { Header } from '@repo/ui/components/Header' +import { Icon } from '@repo/ui/components/Icon' +import { Text } from '@repo/ui/components/Text' +import { Column, VerticalScrollArea } from '@repo/ui/components/Layout' +import { Banner } from '@repo/ui/components/Banner' + +import { Location } from './_components/Location/Location' +import { Menus } from './_components/Menus/Menus' +import { Description } from './_components/Description' +import { ActionButtonGroup } from './_components/ActionButtonGroup' +import { useDisclosure } from '@heroui/react' +import { RejectModal } from '@/app/request/[id]/_components/RejectModal' +import { useRouter } from 'next/navigation' + +export const RequestDetailPage = () => { + const { isOpen, onOpen, onOpenChange } = useDisclosure() + const { back } = useRouter() + + return ( + <> +
    + + + } + center={우돈탄 다산본점} + /> + + + + + + + + + + + + ) +} diff --git a/apps/admin/src/app/request/[id]/_api/types.ts b/apps/admin/src/app/request/[id]/_api/types.ts new file mode 100644 index 0000000..4316ade --- /dev/null +++ b/apps/admin/src/app/request/[id]/_api/types.ts @@ -0,0 +1,31 @@ +import type { IconType } from '@repo/ui/components/Icon' +import { Coord } from '../_utils/toLatLng' + +type Photo = { photoId: string; photoUrl: string; displayOrder: number } + +type Menu = { name: string; price: number; isRecommended: boolean } + +type Category = { + id: string + name: string + iconKey: IconType +} + +type Tag = { + id: string + name: string + iconKey: IconType +} + +export type RequestDetail = { + placeId: string + placeName: string + requestDate: string + photos: Photo[] + address: string + location: Coord + description: string + menus: Menu[] + categories: Category[] + tags: Tag[] +} diff --git a/apps/admin/src/app/request/[id]/_components/ActionButtonGroup/ActionButtonGroup.tsx b/apps/admin/src/app/request/[id]/_components/ActionButtonGroup/ActionButtonGroup.tsx new file mode 100644 index 0000000..16cff97 --- /dev/null +++ b/apps/admin/src/app/request/[id]/_components/ActionButtonGroup/ActionButtonGroup.tsx @@ -0,0 +1,35 @@ +import { Flex } from '@repo/ui/components/Layout' +import { cn } from '@repo/ui/utils/cn' +import { COLOR_VARIANTS } from '@repo/ui/consts/colorVariant' + +type Props = { + onOpen: VoidFunction +} + +export const ActionButtonGroup = ({ onOpen }: Props) => ( + + + + +) diff --git a/apps/admin/src/app/request/[id]/_components/ActionButtonGroup/index.tsx b/apps/admin/src/app/request/[id]/_components/ActionButtonGroup/index.tsx new file mode 100644 index 0000000..2319a2b --- /dev/null +++ b/apps/admin/src/app/request/[id]/_components/ActionButtonGroup/index.tsx @@ -0,0 +1 @@ +export { ActionButtonGroup } from './ActionButtonGroup' diff --git a/apps/admin/src/app/request/[id]/_components/Description/Description.tsx b/apps/admin/src/app/request/[id]/_components/Description/Description.tsx new file mode 100644 index 0000000..c85aa9f --- /dev/null +++ b/apps/admin/src/app/request/[id]/_components/Description/Description.tsx @@ -0,0 +1,22 @@ +import { Column } from '@repo/ui/components/Layout' +import { Chip } from '@repo/ui/components/Chip' +import { Text } from '@repo/ui/components/Text' +import { RequestDetail } from '../../_api/types' + +type Props = { + description: RequestDetail['description'] +} + +export const Description = ({ description }: Props) => { + return ( + + 소개 + + {description} + +
    + +
    +
    + ) +} diff --git a/apps/admin/src/app/request/[id]/_components/Description/index.tsx b/apps/admin/src/app/request/[id]/_components/Description/index.tsx new file mode 100644 index 0000000..acdbf13 --- /dev/null +++ b/apps/admin/src/app/request/[id]/_components/Description/index.tsx @@ -0,0 +1 @@ +export { Description } from './Description' diff --git a/apps/admin/src/app/request/[id]/_components/Location/Location.tsx b/apps/admin/src/app/request/[id]/_components/Location/Location.tsx new file mode 100644 index 0000000..49efa9a --- /dev/null +++ b/apps/admin/src/app/request/[id]/_components/Location/Location.tsx @@ -0,0 +1,21 @@ +'use client' + +import { useState } from 'react' +import { Column } from '@repo/ui/components/Layout' +import { Container, NaverMap } from 'react-naver-maps' +import { Text } from '@repo/ui/components/Text' + +export const Location = () => { + const [, setMap] = useState(null) + + return ( + + 위치 + + + {/**/} + + + + ) +} diff --git a/apps/admin/src/app/request/[id]/_components/Location/index.tsx b/apps/admin/src/app/request/[id]/_components/Location/index.tsx new file mode 100644 index 0000000..9212ead --- /dev/null +++ b/apps/admin/src/app/request/[id]/_components/Location/index.tsx @@ -0,0 +1 @@ +export { Location } from './Location' diff --git a/apps/admin/src/app/request/[id]/_components/Menus/Menu.tsx b/apps/admin/src/app/request/[id]/_components/Menus/Menu.tsx new file mode 100644 index 0000000..e25d24f --- /dev/null +++ b/apps/admin/src/app/request/[id]/_components/Menus/Menu.tsx @@ -0,0 +1,27 @@ +import { Flex, JustifyBetween } from '@repo/ui/components/Layout' +import { Text } from '@repo/ui/components/Text' +import { Icon } from '@repo/ui/components/Icon' +import type { RequestDetail } from '@/app/request/[id]/_api/types' + +type Props = { + menu: RequestDetail['menus'][0] +} + +export const Menu = ({ menu }: Props) => { + const { name, price, isRecommended } = menu + + return ( + + + + {name} + + {isRecommended && } + + + + {price.toLocaleString()} 원 + + + ) +} diff --git a/apps/admin/src/app/request/[id]/_components/Menus/Menus.tsx b/apps/admin/src/app/request/[id]/_components/Menus/Menus.tsx new file mode 100644 index 0000000..8b9fc7d --- /dev/null +++ b/apps/admin/src/app/request/[id]/_components/Menus/Menus.tsx @@ -0,0 +1,65 @@ +import { Text } from '@repo/ui/components/Text' +import { Column, VerticalScrollArea } from '@repo/ui/components/Layout' +import { cn } from '@repo/ui/utils/cn' +import type { RequestDetail } from '@/app/request/[id]/_api/types' +import { Menu } from './Menu' + +type Props = { + menus: RequestDetail['menus'] +} + +export const Menus = ({ menus }: Props) => { + const { recommendedMenu, unRecommendedMenu } = menus.reduce( + (acc, menu) => { + if (menu.isRecommended) acc.recommendedMenu.push(menu) + else acc.unRecommendedMenu.push(menu) + return acc + }, + { + recommendedMenu: [] as typeof menus, + unRecommendedMenu: [] as typeof menus, + }, + ) + + return ( + + 메뉴 + + {/*메뉴 존재 유무*/} + {menus.length === 0 && ( + + 등록된 메뉴가 존재하지 않습니다 + + )} + {/*추천 메뉴 존재 유무*/} + {recommendedMenu.length > 0 && ( + <> + + 추천메뉴 + + {recommendedMenu.map((menu, index) => ( + + ))} +
    + + )} + {unRecommendedMenu.map((menu, index) => ( + + ))} + + + ) +} diff --git a/apps/admin/src/app/request/[id]/_components/Menus/index.tsx b/apps/admin/src/app/request/[id]/_components/Menus/index.tsx new file mode 100644 index 0000000..daf2580 --- /dev/null +++ b/apps/admin/src/app/request/[id]/_components/Menus/index.tsx @@ -0,0 +1 @@ +export { Menus } from './Menus' diff --git a/apps/admin/src/app/request/[id]/_components/RejectModal/RejectModal.tsx b/apps/admin/src/app/request/[id]/_components/RejectModal/RejectModal.tsx new file mode 100644 index 0000000..1ebee75 --- /dev/null +++ b/apps/admin/src/app/request/[id]/_components/RejectModal/RejectModal.tsx @@ -0,0 +1,46 @@ +import { useState } from 'react' +import { + Modal, + ModalBody, + ModalContent, + ModalFooter, + ModalHeader, +} from '@heroui/react' +import { cn } from '@repo/ui/utils/cn' +import { Textarea } from '@repo/ui/components/Textarea' +import { COLOR_VARIANTS } from '@repo/ui/consts/colorVariant' + +type Props = { + isOpen: boolean + onOpenChange: VoidFunction +} + +export const RejectModal = ({ isOpen, onOpenChange }: Props) => { + const [value, setValue] = useState('') + + return ( + + + 등록 거절 사유 + +