diff --git a/public/fallback.png b/public/fallback.png new file mode 100644 index 0000000..28e0904 Binary files /dev/null and b/public/fallback.png differ diff --git a/src/assets/icon/ic-calendar-clock.svg b/src/assets/icon/ic-calendar-clock.svg new file mode 100644 index 0000000..3a818ef --- /dev/null +++ b/src/assets/icon/ic-calendar-clock.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/assets/icon/ic-coins.svg b/src/assets/icon/ic-coins.svg new file mode 100644 index 0000000..2ea42d0 --- /dev/null +++ b/src/assets/icon/ic-coins.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/assets/icon/ic-map-pin.svg b/src/assets/icon/ic-map-pin.svg new file mode 100644 index 0000000..b35956a --- /dev/null +++ b/src/assets/icon/ic-map-pin.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/components/layout/container/container.tsx b/src/components/layout/container/container.tsx index cdb134d..a067966 100644 --- a/src/components/layout/container/container.tsx +++ b/src/components/layout/container/container.tsx @@ -4,6 +4,7 @@ import { ElementType, ReactNode } from 'react'; interface Props { as?: ElementType; className?: string; + isPage?: boolean; children: ReactNode; } @@ -11,13 +12,14 @@ export const Wrapper = ({ children }: { children: ReactNode }) => { return
{children}
; }; -const Container = ({ as: Component = 'div', className, children }: Props) => { +const Container = ({ as: Component = 'div', isPage = false, className, children }: Props) => { return ( diff --git a/src/components/ui/card/card.styles.ts b/src/components/ui/card/card.styles.ts index 7ce1ce8..0f3715a 100644 --- a/src/components/ui/card/card.styles.ts +++ b/src/components/ui/card/card.styles.ts @@ -19,7 +19,7 @@ const cardHeading = cva('font-bold', { defaultVariants: { size: 'md', status: 'open' }, }); -const cardInfoLayout = cva('flex flex-nowrap items-start tablet:items-center gap-1.5'); +const cardInfoLayout = cva('flex flex-nowrap items-center tablet:items-center gap-1.5'); const cardInfoText = cva('text-caption tablet:text-body-s', { variants: { @@ -34,7 +34,7 @@ const cardInfoText = cva('text-caption tablet:text-body-s', { const cardInfoIcon = cva('', { variants: { status: { - open: 'bg-red-300', + open: 'bg-gray-700', inactive: 'bg-gray-300', }, }, @@ -43,9 +43,31 @@ const cardInfoIcon = cva('', { const cardPayLayout = cva('flex items-center gap-x-3'); -const cardBadge = cva('flex items-center gap-x-0.5 rounded-full'); +const cardPayText = cva('font-bold text-modal tracking-wide', { + variants: { + status: { + open: 'text-black', + inactive: 'text-gray-300', + }, + }, + defaultVariants: { status: 'open' }, +}); +const cardBadge = cva( + 'flex items-center gap-x-0.5 rounded-full px-2 py-1 tablet:py-2 tablet:px-3', + { + variants: { + status: { + post: 'absolute bottom-3 right-3 z-[1] border border-white', + notice: '', + }, + }, + defaultVariants: { status: 'notice' }, + } +); + +const cardBadgeText = cva('whitespace-nowrap text-caption text-white font-bold tablet:text-body-s'); -const cardBadgeText = cva('whitespace-nowrap text-caption tablet:text-body-s'); +const cardBadgeIcon = cva('self-start bg-white'); export const cardLayout = { frame: cardFrame, @@ -55,7 +77,9 @@ export const cardLayout = { info: cardInfoText, infoIcon: cardInfoIcon, payLayout: cardPayLayout, + payText: cardPayText, badge: cardBadge, badgeText: cardBadgeText, + badgeIcon: cardBadgeIcon, }; export type CardStatusVariant = VariantProps['status']; diff --git a/src/components/ui/card/cardBadge.tsx b/src/components/ui/card/cardBadge.tsx new file mode 100644 index 0000000..35ccc87 --- /dev/null +++ b/src/components/ui/card/cardBadge.tsx @@ -0,0 +1,44 @@ +import { Icon } from '@/components/ui/icon'; +import { calcPayIncreasePercent } from '@/lib/utils/calcPayIncrease'; +import { cn } from '@/lib/utils/cn'; +import { type CardVariant } from '@/types/notice'; +import { cardLayout } from './card.styles'; +interface CardBadgeProps { + variant: CardVariant; + hourlyPay?: number; + originalHourlyPay?: number; +} +const CardBadge = ({ variant, hourlyPay, originalHourlyPay }: CardBadgeProps) => { + if (!hourlyPay || !originalHourlyPay) return; + + const payIncreasePercent = calcPayIncreasePercent(hourlyPay, originalHourlyPay); + const payIncreaseLabel = + payIncreasePercent && (payIncreasePercent > 100 ? '100% 이상' : `${payIncreasePercent}%`); + const badgeColorClass = + payIncreasePercent == null + ? '' + : payIncreasePercent >= 50 + ? 'bg-red-500' + : payIncreasePercent >= 30 + ? 'bg-red-300' + : 'bg-red-200'; + + return ( + <> + {payIncreasePercent !== null && ( +
+ 기존 시급 {payIncreaseLabel} + +
+ )} + + ); +}; + +export default CardBadge; diff --git a/src/components/ui/card/cardImage.tsx b/src/components/ui/card/cardImage.tsx new file mode 100644 index 0000000..e3333dc --- /dev/null +++ b/src/components/ui/card/cardImage.tsx @@ -0,0 +1,53 @@ +import { cn } from '@/lib/utils/cn'; +import { type CardVariant } from '@/types/notice'; +import Image from 'next/image'; +import { ReactNode, useState } from 'react'; +import { cardLayout } from './card.styles'; + +interface CardImageProps { + variant: CardVariant; + src?: string; + alt?: string; + className?: string; + children?: ReactNode; +} +const FALLBACK_SRC = '/fallback.png'; +const CardImage = ({ variant, src, alt, className, children }: CardImageProps) => { + const [imgSrc, setImgSrc] = useState(src ?? FALLBACK_SRC); + + const handleError = () => { + setImgSrc(FALLBACK_SRC); + }; + + const isValidSrc = + typeof imgSrc === 'string' && + (imgSrc.startsWith('https://bootcamp-project-api.s3.ap-northeast-2.amazonaws.com') || + imgSrc.startsWith('https://picsum.photos')); + return ( +
+ {`${alt} + {children} +
+ ); +}; +export default CardImage; diff --git a/src/components/ui/card/cardInfo.tsx b/src/components/ui/card/cardInfo.tsx new file mode 100644 index 0000000..4e89eea --- /dev/null +++ b/src/components/ui/card/cardInfo.tsx @@ -0,0 +1,27 @@ +import { Icon } from '@/components/ui/icon'; +import { IconName } from '@/constants/icon'; +import { ReactNode } from 'react'; +import { cardLayout, CardStatusVariant } from './card.styles'; + +interface CardInfo { + iconName: IconName; + status?: CardStatusVariant; + ariaLabel: string; + children?: ReactNode; +} + +const CardInfo = ({ iconName, ariaLabel, status, children }: CardInfo) => { + return ( +
  • + +

    {children}

    +
  • + ); +}; + +export default CardInfo; diff --git a/src/components/ui/card/notice/components/noticeImage.tsx b/src/components/ui/card/notice/components/noticeImage.tsx deleted file mode 100644 index 501d663..0000000 --- a/src/components/ui/card/notice/components/noticeImage.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import { noticeImageWrapper } from '@/components/ui/card/notice/notice.styles'; -import Image from 'next/image'; - -interface NoticeImageProps { - name?: string; - imageUrl?: string; -} -const NoticeImage = ({ name, imageUrl }: NoticeImageProps) => ( -
    - {`${name} -
    -); -export default NoticeImage; diff --git a/src/components/ui/card/notice/components/noticeInfo.tsx b/src/components/ui/card/notice/components/noticeInfo.tsx index caa31be..dbe2f3a 100644 --- a/src/components/ui/card/notice/components/noticeInfo.tsx +++ b/src/components/ui/card/notice/components/noticeInfo.tsx @@ -1,17 +1,14 @@ import { cardLayout } from '@/components/ui/card/card.styles'; -import { NoticeVariant } from '@/components/ui/card/notice/notice'; +import CardBadge from '@/components/ui/card/cardBadge'; +import CardInfo from '@/components/ui/card/cardInfo'; import { - badgeText, noticeButton, noticeInfoWrapper, noticeLabel, - payBadge, } from '@/components/ui/card/notice/notice.styles'; -import { Icon } from '@/components/ui/icon'; -import { calcPayIncreasePercent } from '@/lib/utils/calcPayIncrease'; import { getTime } from '@/lib/utils/dateFormatter'; import { formatNumber } from '@/lib/utils/formatNumber'; -import { NoticeCard } from '@/types/notice'; +import { type NoticeCard, type NoticeVariant } from '@/types/notice'; import { ReactNode } from 'react'; import NoticeHeader from './noticeHeader'; @@ -36,10 +33,9 @@ const NoticeInfo = >({ address1, shopDescription, } = value; - const payIncreasePercent = calcPayIncreasePercent(hourlyPay ?? 0, originalHourlyPay ?? 0); + const { date, startTime, endTime } = getTime(startsAt ?? '', workhour ?? 0); - const payIncreaseLabel = - payIncreasePercent && (payIncreasePercent > 100 ? '100% 이상' : `${payIncreasePercent}%`); + return (
      @@ -48,29 +44,19 @@ const NoticeInfo = >({
    • 시급

      - + {formatNumber(hourlyPay ?? 0)}원 - {payIncreasePercent !== null && ( -
      - 기존 시급 {payIncreaseLabel} - -
      - )} +
    • -
    • - -

      - {date} {startTime} ~ {endTime} ({workhour}시간) -

      -
    • + + {date} {startTime} ~ {endTime} ({workhour}시간) + )} {variant === 'shop' && ( @@ -78,10 +64,9 @@ const NoticeInfo = >({ )} -
    • - -

      {address1}

      -
    • + + {address1} +
    • {shopDescription}
    {buttonComponent}
    diff --git a/src/components/ui/card/notice/components/renderNotice.tsx b/src/components/ui/card/notice/components/renderNotice.tsx index fbd5b67..218b1c3 100644 --- a/src/components/ui/card/notice/components/renderNotice.tsx +++ b/src/components/ui/card/notice/components/renderNotice.tsx @@ -1,8 +1,8 @@ -import { noticeFrame } from '@/components/ui/card/notice/notice.styles'; -import { NoticeCard } from '@/types/notice'; +import CardImage from '@/components/ui/card/cardImage'; +import { descriptionWrapper, noticeFrame } from '@/components/ui/card/notice/notice.styles'; +import { type NoticeCard } from '@/types/notice'; import { ReactNode } from 'react'; import NoticeHeader from './noticeHeader'; -import NoticeImage from './noticeImage'; import NoticeInfo from './noticeInfo'; interface RenderNoticeProps> { @@ -26,10 +26,10 @@ const RenderNotice = >({ <>
    - +
    -
    +

    공고 설명

    {description}

    diff --git a/src/components/ui/card/notice/components/renderShop.tsx b/src/components/ui/card/notice/components/renderShop.tsx index 2f67f0c..3435770 100644 --- a/src/components/ui/card/notice/components/renderShop.tsx +++ b/src/components/ui/card/notice/components/renderShop.tsx @@ -1,9 +1,9 @@ import { cardLayout } from '@/components/ui/card/card.styles'; +import CardImage from '@/components/ui/card/cardImage'; import { noticeFrame } from '@/components/ui/card/notice/notice.styles'; import { cn } from '@/lib/utils/cn'; import { NoticeShopCard } from '@/types/shop'; import { ReactNode } from 'react'; -import NoticeImage from './noticeImage'; import NoticeInfo from './noticeInfo'; type ShopCard = Omit; @@ -26,7 +26,7 @@ const RenderShop = ({ items, buttonComponent }: RenderShopProps) => { <>

    내 가게

    - +
    diff --git a/src/components/ui/card/notice/mockData/noticeWrapper.tsx b/src/components/ui/card/notice/mockData/noticeWrapper.tsx index 38eb510..e1cb3b8 100644 --- a/src/components/ui/card/notice/mockData/noticeWrapper.tsx +++ b/src/components/ui/card/notice/mockData/noticeWrapper.tsx @@ -1,7 +1,7 @@ import { Button } from '@/components/ui/button'; import Notice from '@/components/ui/card/notice/notice'; import { getNoticeStatus } from '@/lib/utils/getNoticeStatus'; -import type { NoticeCard } from '@/types/notice'; +import { type NoticeCard } from '@/types/notice'; import { NoticeShopCard } from '@/types/shop'; import Link from 'next/link'; import mockResponse from './mockData.json'; diff --git a/src/components/ui/card/notice/notice.styles.ts b/src/components/ui/card/notice/notice.styles.ts index f57afdb..e03afdb 100644 --- a/src/components/ui/card/notice/notice.styles.ts +++ b/src/components/ui/card/notice/notice.styles.ts @@ -11,17 +11,10 @@ export const noticeFrame = cva( ) ); -export const noticeImageWrapper = cva( - cn(cardLayout.imageWrapper(), 'w-full h-[180px] tablet:h-[360px] desktop:h-auto') -); - export const noticeInfoWrapper = cva('shrink-0 desktop:w-[346px]'); export const noticeLabel = cva('pb-2 text-body-m font-bold text-red-400'); -export const payBadge = cva(cn(cardLayout.badge(), 'bg-red-400 px-2 py-1 tablet:py-2 tablet:px-3')); - -export const badgeText = cva(cn(cardLayout.badgeText(), 'text-white font-bold')); -export const description = cva('rounded-xl bg-gray-100 p-8'); +export const descriptionWrapper = cva('flex flex-col gap-3 rounded-xl bg-gray-100 p-4 tablet:p-8'); export const noticeButton = cva('mt-6 tablet:mt-10 desktop:mt-8'); diff --git a/src/components/ui/card/notice/notice.tsx b/src/components/ui/card/notice/notice.tsx index ed901de..342770d 100644 --- a/src/components/ui/card/notice/notice.tsx +++ b/src/components/ui/card/notice/notice.tsx @@ -1,11 +1,10 @@ -import { Container } from '@/components/layout'; -import { NoticeCard } from '@/types/notice'; +import { Container } from '@/components/layout/container'; +import { type NoticeCard, type NoticeVariant } from '@/types/notice'; import { ReactNode } from 'react'; import RenderNotice from './components/renderNotice'; import RenderShop from './components/renderShop'; import { noticeWrapper } from './notice.styles'; -export type NoticeVariant = 'notice' | 'shop'; interface NoticeProps> { notice: T; variant?: NoticeVariant; diff --git a/src/components/ui/card/post/mockData/postWrapper.tsx b/src/components/ui/card/post/mockData/postWrapper.tsx index 17e018b..6db0407 100644 --- a/src/components/ui/card/post/mockData/postWrapper.tsx +++ b/src/components/ui/card/post/mockData/postWrapper.tsx @@ -25,7 +25,6 @@ const toPostCard = ({ item }: RawNotice): PostCard => { const PostWrapper = () => { const notices: PostCard[] = mockResponse.items.map(toPostCard); - return (
    {notices.map(notice => ( diff --git a/src/components/ui/card/post/post.styles.ts b/src/components/ui/card/post/post.styles.ts index 512e10b..cb634da 100644 --- a/src/components/ui/card/post/post.styles.ts +++ b/src/components/ui/card/post/post.styles.ts @@ -1,45 +1,11 @@ import { cardLayout } from '@/components/ui/card/card.styles'; import { cn } from '@/lib/utils/cn'; -import { cva, type VariantProps } from 'class-variance-authority'; +import { cva } from 'class-variance-authority'; export const postFrame = cva(cn(cardLayout.frame(), 'block p-3 tablet:rounded-2xl tablet:p-4')); -export const postImageWrapper = cva(cn(cardLayout.imageWrapper(), 'h-[120px] tablet:h-[160px]')); - export const postImageDimmed = cva( - 'absolute inset-0 flex items-center justify-center bg-modal-dimmed text-heading-s text-zinc-300 font-bold' -); - -export const payLayout = cva( - cn(cardLayout.payLayout(), 'mt-4 flex-wrap justify-between tablet:flex-nowrap') + 'absolute inset-0 z-[2] flex items-center justify-center bg-modal-dimmed text-heading-s text-zinc-300 font-bold' ); -export const payBadge = cva(cn(cardLayout.badge(), 'tablet:py-2 tablet:px-3'), { - variants: { - status: { - open: 'tablet:bg-red-400', - inactive: 'tablet:bg-gray-200', - }, - }, - defaultVariants: { status: 'open' }, -}); -export const badgeText = cva(cn(cardLayout.badgeText(), 'tablet:font-bold tablet:text-white'), { - variants: { - status: { - open: 'text-red-400', - inactive: 'text-gray-300', - }, - }, - defaultVariants: { status: 'open' }, -}); -export const badgeIcon = cva('self-start tablet:bg-white', { - variants: { - status: { - open: 'bg-red-400', - inactive: 'bg-gray-300', - }, - }, - defaultVariants: { status: 'open' }, -}); -export type PostStatusVariant = VariantProps['status']; diff --git a/src/components/ui/card/post/post.tsx b/src/components/ui/card/post/post.tsx index 1d2814c..f5e1ba6 100644 --- a/src/components/ui/card/post/post.tsx +++ b/src/components/ui/card/post/post.tsx @@ -1,22 +1,13 @@ import { cardLayout, CardStatusVariant } from '@/components/ui/card/card.styles'; -import { Icon } from '@/components/ui/icon'; -import { calcPayIncreasePercent } from '@/lib/utils/calcPayIncrease'; -import { cn } from '@/lib/utils/cn'; +import CardBadge from '@/components/ui/card/cardBadge'; +import CardImage from '@/components/ui/card/cardImage'; +import CardInfo from '@/components/ui/card/cardInfo'; import { getTime } from '@/lib/utils/dateFormatter'; import { formatNumber } from '@/lib/utils/formatNumber'; import { getNoticeStatus } from '@/lib/utils/getNoticeStatus'; import type { PostCard } from '@/types/notice'; -import Image from 'next/image'; import Link from 'next/link'; -import { - badgeIcon, - badgeText, - payBadge, - payLayout, - postFrame, - postImageDimmed, - postImageWrapper, -} from './post.styles'; +import { postFrame, postImageDimmed } from './post.styles'; interface PostProps { notice: PostCard; @@ -39,75 +30,39 @@ const Post = ({ notice }: PostProps) => { shopId, } = notice; const status = getNoticeStatus(closed, startsAt); - const payIncreasePercent = calcPayIncreasePercent(hourlyPay, originalHourlyPay); const { date, startTime, endTime } = getTime(startsAt, workhour); const statusVariant: CardStatusVariant = status === 'open' ? 'open' : 'inactive'; - const payIncreaseLabel = - payIncreasePercent && (payIncreasePercent > 100 ? '100% 이상' : `${payIncreasePercent}%`); const href = `/notices/${shopId}`; return ( -
    - {`${name} + + {status !== 'open' &&
    {STATUS_LABEL[status]}
    } -
    -
    -
      -
    • {name}
    • -
    • - -

      - {date} {startTime} ~ {endTime} ({workhour}시간) -

      -
    • -
    • - -

      {address1}

      -
    • -
    -
    - + +

    + {name} +

    +
      + + {date} {startTime} ~ {endTime} ({workhour}시간) + + + {address1} + + + 시급 {''} + {formatNumber(hourlyPay)}원 - {payIncreasePercent !== null && ( -
      - - 기존 시급 {payIncreaseLabel} - - -
      - )} -
    -
    + + ); }; diff --git a/src/components/ui/filter/components/filterBody.tsx b/src/components/ui/filter/components/filterBody.tsx index 6196ca3..3fcd9ea 100644 --- a/src/components/ui/filter/components/filterBody.tsx +++ b/src/components/ui/filter/components/filterBody.tsx @@ -1,15 +1,15 @@ import { filterLayout } from '@/components/ui/filter/filter.styles'; import { Icon } from '@/components/ui/icon'; -import { DateInput, Input } from '@/components/ui/input'; +import { Input } from '@/components/ui/input'; import { ADDRESS_CODE } from '@/constants/dropdown'; import { parseRFC3339 } from '@/lib/utils/dateFormatter'; import { formatNumber } from '@/lib/utils/formatNumber'; -import { FilterQueryParams } from '@/types/api'; +import { FilterQuery } from '@/types/api'; import { ChangeEvent, useMemo } from 'react'; interface FilterBodyProps { - formData: FilterQueryParams; - onChange: (updater: (prev: FilterQueryParams) => FilterQueryParams) => void; + formData: FilterQuery; + onChange: (updater: (prev: FilterQuery) => FilterQuery) => void; } const FilterBody = ({ formData, onChange }: FilterBodyProps) => { diff --git a/src/components/ui/filter/filter.tsx b/src/components/ui/filter/filter.tsx index c0a6fe3..5c8d835 100644 --- a/src/components/ui/filter/filter.tsx +++ b/src/components/ui/filter/filter.tsx @@ -1,5 +1,5 @@ import { cn } from '@/lib/utils/cn'; -import { FilterQueryParams } from '@/types/api'; +import { FilterQuery } from '@/types/api'; import { useCallback, useEffect, useState } from 'react'; import FilterBody from './components/filterBody'; import FilterFooter from './components/filterFooter'; @@ -7,19 +7,19 @@ import FilterHeader from './components/filterHeader'; import { filterLayout } from './filter.styles'; interface FilterProps { - value: FilterQueryParams; - onSubmit: (next: FilterQueryParams) => void; + value: FilterQuery; + onSubmit: (next: FilterQuery) => void; onClose?: () => void; className: string; } -const INIT_DATA: FilterQueryParams = { +const INIT_DATA: FilterQuery = { address: undefined, startsAtGte: undefined, hourlyPayGte: undefined, }; -export function normalizeFilter(q: FilterQueryParams): FilterQueryParams { +export function normalizeFilter(q: FilterQuery): FilterQuery { return { address: q.address && q.address.length > 0 ? q.address : undefined, startsAtGte: q.startsAtGte ?? undefined, @@ -28,7 +28,7 @@ export function normalizeFilter(q: FilterQueryParams): FilterQueryParams { } const Filter = ({ value, onSubmit, onClose, className }: FilterProps) => { - const [draft, setDraft] = useState(value); + const [draft, setDraft] = useState(value); // const handleSubmit = () => onSubmit(draft); diff --git a/src/constants/icon.ts b/src/constants/icon.ts index 783f222..07db1e1 100644 --- a/src/constants/icon.ts +++ b/src/constants/icon.ts @@ -1,17 +1,20 @@ import arrowUp from '@/assets/icon/ic-arrow-up.svg'; import calendar from '@/assets/icon/ic-calendar.svg'; +import calendarClock from '@/assets/icon/ic-calendar-clock.svg' import camera from '@/assets/icon/ic-camera.svg'; import checked from '@/assets/icon/ic-checked.svg'; import chevronLeft from '@/assets/icon/ic-chevron-left.svg'; import chevronRight from '@/assets/icon/ic-chevron-right.svg'; import clock from '@/assets/icon/ic-clock.svg'; import close from '@/assets/icon/ic-close.svg'; +import coins from '@/assets/icon/ic-coins.svg'; import dropdownDown from '@/assets/icon/ic-dropdown-down.svg'; import dropdownUp from '@/assets/icon/ic-dropdown-up.svg'; import envelope from '@/assets/icon/ic-envelope.svg'; import facebook from '@/assets/icon/ic-facebook.svg'; import instagram from '@/assets/icon/ic-instagram.svg'; import map from '@/assets/icon/ic-map.svg'; +import mapPin from '@/assets/icon/ic-map-pin.svg'; import notificationOff from '@/assets/icon/ic-notification-off.svg'; import notificationOn from '@/assets/icon/ic-notification-on.svg'; import phone from '@/assets/icon/ic-phone.svg'; @@ -28,18 +31,21 @@ export const ICONS = { resultBadge: resultBadge.src, arrowUp: arrowUp.src, calendar: calendar.src, + calendarClock: calendarClock.src, camera: camera.src, checked: checked.src, chevronLeft: chevronLeft.src, chevronRight: chevronRight.src, clock: clock.src, close: close.src, + coins: coins.src, dropdownDown: dropdownDown.src, dropdownUp: dropdownUp.src, envelope: envelope.src, facebook: facebook.src, instagram: instagram.src, map: map.src, + mapPin: mapPin.src, notificationOff: notificationOff.src, notificationOn: notificationOn.src, phone: phone.src, diff --git a/src/types/api.ts b/src/types/api.ts index 0f3e137..ede0741 100644 --- a/src/types/api.ts +++ b/src/types/api.ts @@ -8,15 +8,12 @@ export interface Link { href: string; } -export interface PaginatedResponse { +export interface PaginatedResponse { offset: number; limit: number; count: number; hasNext: boolean; - items: T[]; - links: Link[]; } - export interface ApiResponse { item: T; links: Link[]; @@ -37,15 +34,16 @@ export interface AuthResponse { } export type sort = 'time' | 'pay' | 'hour' | 'shop' | undefined; -export interface FilterQueryParams { +export interface FilterQuery { address?: string[]; startsAtGte?: string; hourlyPayGte?: number; } -export interface QueryParams extends FilterQueryParams { +export interface NoticeQuery extends FilterQuery { offset?: number; limit?: number; keyword?: string; sort?: sort; } + \ No newline at end of file diff --git a/src/types/notice.ts b/src/types/notice.ts index abb3294..aab9bb2 100644 --- a/src/types/notice.ts +++ b/src/types/notice.ts @@ -1,6 +1,7 @@ /* -------------------- 공고 -------------------- */ -import { ShopSummary } from './shop'; +import { ApiResponse, Link, PaginatedResponse } from './api'; +import { Shop, ShopSummary } from './shop'; // 공고 등록 export interface NoticeBase { @@ -16,6 +17,8 @@ export interface Notice extends NoticeBase { closed: boolean; } +export type CardVariant = 'post' | 'notice'; + export type PostCard = Omit & ShopSummary & { shopId: string }; export type NoticeCard = Notice & @@ -24,3 +27,16 @@ export type NoticeCard = Notice & category: string; shopDescription: string; }; + +export interface NoticeItemResponse { + item: Notice & { + shop: { + item: Shop; + href: string; + }; + }; + links: Link[]; +} + +export type NoticeListResponse = PaginatedResponse & ApiResponse; +export type NoticeVariant = 'notice' | 'shop';