diff --git a/apps/admin/next.config.ts b/apps/admin/next.config.ts index e9ffa30..b11c726 100644 --- a/apps/admin/next.config.ts +++ b/apps/admin/next.config.ts @@ -1,7 +1,32 @@ -import type { NextConfig } from "next"; +import type { NextConfig } from 'next' const nextConfig: NextConfig = { /* config options here */ -}; + images: { + remotePatterns: [ + { + protocol: 'https', + hostname: '*.pstatic.net', + port: '', + pathname: '/**', + }, + { + protocol: 'https', + hostname: 't1.daumcdn.net', + pathname: '/**', + }, + { + protocol: 'http', + hostname: 't1.daumcdn.net', + pathname: '/**', + }, + { + protocol: 'https', + hostname: 'blog.kakaocdn.net', + pathname: '/**', + }, + ], + }, +} -export default nextConfig; +export default nextConfig diff --git a/apps/admin/src/app/_api/services/request.ts b/apps/admin/src/app/_api/services/request.ts new file mode 100644 index 0000000..802ebde --- /dev/null +++ b/apps/admin/src/app/_api/services/request.ts @@ -0,0 +1,8 @@ +import { API_PATH, BASE_URL } from '@/consts/path' +import { Request } from '@/app/_api/types' + +export const getRequests = async (): Promise => { + const response = await fetch(BASE_URL + API_PATH.REQUEST.GET.LIST) + const { data: res } = await response.json() + return res +} diff --git a/apps/admin/src/app/_api/types.ts b/apps/admin/src/app/_api/types.ts index dc9eaea..6d0acd4 100644 --- a/apps/admin/src/app/_api/types.ts +++ b/apps/admin/src/app/_api/types.ts @@ -1,8 +1,23 @@ import type { IconType } from '@repo/ui/components/Icon' +import { CampusType } from '@/consts/campus' + +export type Category = { + id: string + name: string + iconKey: IconType +} + +export type Tag = { + id: string + name: string + iconKey: IconType +} export type Request = { placeId: string placeName: string - icon: IconType requestDate: string + campus: CampusType + categories: Category[] + tags: Tag[] } diff --git a/apps/admin/src/app/_components/RequestListItem/RequestListItem.tsx b/apps/admin/src/app/_components/RequestListItem/RequestListItem.tsx index 5d4d528..c2dc640 100644 --- a/apps/admin/src/app/_components/RequestListItem/RequestListItem.tsx +++ b/apps/admin/src/app/_components/RequestListItem/RequestListItem.tsx @@ -8,7 +8,7 @@ import { Request } from '@/app/_api/types' export const RequestListItem = ({ placeId, placeName, - icon, + categories, requestDate, }: Request) => (
  • @@ -21,7 +21,7 @@ export const RequestListItem = ({ {placeName} - + 등록 신청 일자: {requestDate} diff --git a/apps/admin/src/app/page.tsx b/apps/admin/src/app/page.tsx index 1339cef..65f52c7 100644 --- a/apps/admin/src/app/page.tsx +++ b/apps/admin/src/app/page.tsx @@ -1,37 +1,36 @@ 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' +import { getRequests } from '@/app/_api/services/request' +import type { Request } from '@/app/_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 async function Home() { + const data = (await getRequests()) || [] + const singwan: Request[] = [] + const cheonan: Request[] = [] + const yesan: Request[] = [] + + for (const request of data) { + switch (request.campus) { + case 'SINGWAN': + singwan.push(request) + break + case 'CHEONAN': + cheonan.push(request) + break + case 'YESAN': + yesan.push(request) + break + } + } -export default function Home() { return ( <> - - - + + + ) diff --git a/apps/admin/src/app/request/[id]/RequestDetailPage.tsx b/apps/admin/src/app/request/[id]/RequestDetailPage.tsx deleted file mode 100644 index 3edba20..0000000 --- a/apps/admin/src/app/request/[id]/RequestDetailPage.tsx +++ /dev/null @@ -1,82 +0,0 @@ -'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]/_components/ActionButtonGroup/ActionButtonGroup.tsx b/apps/admin/src/app/request/[id]/_components/ActionButtonGroup/ActionButtonGroup.tsx deleted file mode 100644 index 16cff97..0000000 --- a/apps/admin/src/app/request/[id]/_components/ActionButtonGroup/ActionButtonGroup.tsx +++ /dev/null @@ -1,35 +0,0 @@ -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/Location/Location.tsx b/apps/admin/src/app/request/[id]/_components/Location/Location.tsx deleted file mode 100644 index 49efa9a..0000000 --- a/apps/admin/src/app/request/[id]/_components/Location/Location.tsx +++ /dev/null @@ -1,21 +0,0 @@ -'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]/page.tsx b/apps/admin/src/app/request/[id]/page.tsx deleted file mode 100644 index 113ae0a..0000000 --- a/apps/admin/src/app/request/[id]/page.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import { RequestDetailPage } from './RequestDetailPage' - -const Page = () => { - return ( - <> - - - ) -} - -export default Page diff --git a/apps/admin/src/app/requests/[id]/RequestDetailPage.tsx b/apps/admin/src/app/requests/[id]/RequestDetailPage.tsx new file mode 100644 index 0000000..5ca8277 --- /dev/null +++ b/apps/admin/src/app/requests/[id]/RequestDetailPage.tsx @@ -0,0 +1,86 @@ +'use client' + +import Image from 'next/image' +import { useRouter } from 'next/navigation' +import { useDisclosure } from '@heroui/react' + +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 type { RequestDetail } from './_api/types' +import { CLIENT_PATH } from '@/consts/path' +import { requestReview } from './_api/services/request' +import { Location } from './_components/Location/Location' +import { Menus } from './_components/Menus/Menus' +import { Description } from './_components/Description' +import { ActionButtonGroup } from './_components/ActionButtonGroup' +import { RejectModal } from './_components/RejectModal' + +type Props = { + data: RequestDetail +} + +export const RequestDetailPage = ({ data }: Props) => { + const { isOpen, onOpen, onOpenChange } = useDisclosure() + const { back, replace } = useRouter() + const { placeId, placeName, menus, description, tags, photos, location } = + data + + const handleReview = async (rejectedReason?: string) => { + const status = rejectedReason ? 'REJECTED' : 'APPROVED' + const reason = rejectedReason ?? null // rejectedReason이 있으면 그 값을, 없으면 null을 사용 + + await requestReview(placeId, { + status, + rejectedReason: reason, + }) + + alert('완료했슈~') + replace(CLIENT_PATH.MAIN) + } + + return ( + <> +
    + + + } + center={{placeName}} + /> + + {photos.length > 0 && ( + ( + place-photo + ))} + minHeight={180} + showIndicator={true} + /> + )} + + + + + + + + + + ) +} diff --git a/apps/admin/src/app/requests/[id]/_api/services/request.ts b/apps/admin/src/app/requests/[id]/_api/services/request.ts new file mode 100644 index 0000000..c6c3006 --- /dev/null +++ b/apps/admin/src/app/requests/[id]/_api/services/request.ts @@ -0,0 +1,23 @@ +import { API_PATH, BASE_URL } from '@/consts/path' +import type { RequestDetail, RequestReview } from '../types' + +export const getRequestDetail = async (id: string): Promise => { + const response = await fetch(BASE_URL + API_PATH.REQUEST.GET.DETAIL(id)) + const { data } = await response.json() + return data +} + +export const requestReview = async (id: number, review: RequestReview) => { + const response = await fetch(BASE_URL + API_PATH.REQUEST.POST(id), { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(review), + }) + if (!response.ok) { + throw new Error() + } + + return response.json() +} diff --git a/apps/admin/src/app/request/[id]/_api/types.ts b/apps/admin/src/app/requests/[id]/_api/types.ts similarity index 62% rename from apps/admin/src/app/request/[id]/_api/types.ts rename to apps/admin/src/app/requests/[id]/_api/types.ts index 4316ade..21cadd3 100644 --- a/apps/admin/src/app/request/[id]/_api/types.ts +++ b/apps/admin/src/app/requests/[id]/_api/types.ts @@ -1,24 +1,12 @@ -import type { IconType } from '@repo/ui/components/Icon' import { Coord } from '../_utils/toLatLng' +import type { Tag, Category } from '@/app/_api/types' 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 + placeId: number placeName: string requestDate: string photos: Photo[] @@ -29,3 +17,10 @@ export type RequestDetail = { categories: Category[] tags: Tag[] } + +export type RequestReview = { + status: 'APPROVED' | 'REJECTED' + rejectedReason: string | null +} + +export type HandleReview = (rejectedReason?: string) => Promise diff --git a/apps/admin/src/app/requests/[id]/_components/ActionButtonGroup/ActionButtonGroup.tsx b/apps/admin/src/app/requests/[id]/_components/ActionButtonGroup/ActionButtonGroup.tsx new file mode 100644 index 0000000..495aac1 --- /dev/null +++ b/apps/admin/src/app/requests/[id]/_components/ActionButtonGroup/ActionButtonGroup.tsx @@ -0,0 +1,40 @@ +import { Flex } from '@repo/ui/components/Layout' +import { cn } from '@repo/ui/utils/cn' +import { COLOR_VARIANTS } from '@repo/ui/consts/colorVariant' +import type { HandleReview } from '../../_api/types' + +type Props = { + handleReview: HandleReview + onOpen: VoidFunction +} + +export const ActionButtonGroup = ({ handleReview, onOpen }: Props) => { + return ( + + + + + ) +} diff --git a/apps/admin/src/app/request/[id]/_components/ActionButtonGroup/index.tsx b/apps/admin/src/app/requests/[id]/_components/ActionButtonGroup/index.tsx similarity index 100% rename from apps/admin/src/app/request/[id]/_components/ActionButtonGroup/index.tsx rename to apps/admin/src/app/requests/[id]/_components/ActionButtonGroup/index.tsx diff --git a/apps/admin/src/app/request/[id]/_components/Description/Description.tsx b/apps/admin/src/app/requests/[id]/_components/Description/Description.tsx similarity index 56% rename from apps/admin/src/app/request/[id]/_components/Description/Description.tsx rename to apps/admin/src/app/requests/[id]/_components/Description/Description.tsx index c85aa9f..a5a910b 100644 --- a/apps/admin/src/app/request/[id]/_components/Description/Description.tsx +++ b/apps/admin/src/app/requests/[id]/_components/Description/Description.tsx @@ -1,22 +1,25 @@ -import { Column } from '@repo/ui/components/Layout' +import { Column, Flex } 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'] + tags: RequestDetail['tags'] } -export const Description = ({ description }: Props) => { +export const Description = ({ description, tags }: Props) => { return ( 소개 {description} -
    - -
    + + {tags.map((tag) => ( + + ))} +
    ) } diff --git a/apps/admin/src/app/request/[id]/_components/Description/index.tsx b/apps/admin/src/app/requests/[id]/_components/Description/index.tsx similarity index 100% rename from apps/admin/src/app/request/[id]/_components/Description/index.tsx rename to apps/admin/src/app/requests/[id]/_components/Description/index.tsx diff --git a/apps/admin/src/app/requests/[id]/_components/Location/Location.tsx b/apps/admin/src/app/requests/[id]/_components/Location/Location.tsx new file mode 100644 index 0000000..2f3a97c --- /dev/null +++ b/apps/admin/src/app/requests/[id]/_components/Location/Location.tsx @@ -0,0 +1,26 @@ +import { useState } from 'react' +import { Column } from '@repo/ui/components/Layout' +import { Container, Marker, NaverMap } from 'react-naver-maps' +import { Text } from '@repo/ui/components/Text' +import { toLatLng } from '../../_utils/toLatLng' +import type { RequestDetail } from '../../_api/types' + +type Props = { + location: RequestDetail['location'] +} + +export const Location = ({ location }: Props) => { + const [, setMap] = useState(null) + const setLocation = toLatLng(location) + + return ( + + 위치 + + + + + + + ) +} diff --git a/apps/admin/src/app/request/[id]/_components/Location/index.tsx b/apps/admin/src/app/requests/[id]/_components/Location/index.tsx similarity index 100% rename from apps/admin/src/app/request/[id]/_components/Location/index.tsx rename to apps/admin/src/app/requests/[id]/_components/Location/index.tsx diff --git a/apps/admin/src/app/request/[id]/_components/Menus/Menu.tsx b/apps/admin/src/app/requests/[id]/_components/Menus/Menu.tsx similarity index 91% rename from apps/admin/src/app/request/[id]/_components/Menus/Menu.tsx rename to apps/admin/src/app/requests/[id]/_components/Menus/Menu.tsx index e25d24f..dfa244d 100644 --- a/apps/admin/src/app/request/[id]/_components/Menus/Menu.tsx +++ b/apps/admin/src/app/requests/[id]/_components/Menus/Menu.tsx @@ -1,7 +1,7 @@ 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' +import type { RequestDetail } from '@/app/requests/[id]/_api/types' type Props = { menu: RequestDetail['menus'][0] diff --git a/apps/admin/src/app/request/[id]/_components/Menus/Menus.tsx b/apps/admin/src/app/requests/[id]/_components/Menus/Menus.tsx similarity index 96% rename from apps/admin/src/app/request/[id]/_components/Menus/Menus.tsx rename to apps/admin/src/app/requests/[id]/_components/Menus/Menus.tsx index 8b9fc7d..bfa926a 100644 --- a/apps/admin/src/app/request/[id]/_components/Menus/Menus.tsx +++ b/apps/admin/src/app/requests/[id]/_components/Menus/Menus.tsx @@ -1,7 +1,7 @@ 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 type { RequestDetail } from '@/app/requests/[id]/_api/types' import { Menu } from './Menu' type Props = { diff --git a/apps/admin/src/app/request/[id]/_components/Menus/index.tsx b/apps/admin/src/app/requests/[id]/_components/Menus/index.tsx similarity index 100% rename from apps/admin/src/app/request/[id]/_components/Menus/index.tsx rename to apps/admin/src/app/requests/[id]/_components/Menus/index.tsx diff --git a/apps/admin/src/app/request/[id]/_components/RejectModal/RejectModal.tsx b/apps/admin/src/app/requests/[id]/_components/RejectModal/RejectModal.tsx similarity index 81% rename from apps/admin/src/app/request/[id]/_components/RejectModal/RejectModal.tsx rename to apps/admin/src/app/requests/[id]/_components/RejectModal/RejectModal.tsx index 1ebee75..34a98a8 100644 --- a/apps/admin/src/app/request/[id]/_components/RejectModal/RejectModal.tsx +++ b/apps/admin/src/app/requests/[id]/_components/RejectModal/RejectModal.tsx @@ -6,6 +6,7 @@ import { ModalFooter, ModalHeader, } from '@heroui/react' +import type { HandleReview } from '../../_api/types' import { cn } from '@repo/ui/utils/cn' import { Textarea } from '@repo/ui/components/Textarea' import { COLOR_VARIANTS } from '@repo/ui/consts/colorVariant' @@ -13,9 +14,10 @@ import { COLOR_VARIANTS } from '@repo/ui/consts/colorVariant' type Props = { isOpen: boolean onOpenChange: VoidFunction + handleReview: HandleReview } -export const RejectModal = ({ isOpen, onOpenChange }: Props) => { +export const RejectModal = ({ isOpen, onOpenChange, handleReview }: Props) => { const [value, setValue] = useState('') return ( @@ -27,8 +29,9 @@ export const RejectModal = ({ isOpen, onOpenChange }: Props) => {