From 528cc398317f09c13751a69cf93151b639423a63 Mon Sep 17 00:00:00 2001 From: bicochan Date: Fri, 4 Apr 2025 14:49:00 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=EC=82=AC=EC=9D=B4=ED=8A=B8=20=EB=B0=98?= =?UTF-8?q?=EC=98=81=20=ED=8C=9D=EC=97=85=20=EC=9E=91=EC=97=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../entities/posting/ui/posting-dialog.tsx | 94 +++++++++++++++---- .../components/src/ui/calendar/index.tsx | 2 +- .../src/ui/date-picker/date-picker.css | 2 +- .../ui/date-picker/date-picker.stories.tsx | 2 +- .../components/src/ui/date-picker/index.tsx | 2 +- .../src/ui/date-range-picker/index.tsx | 71 ++++++++++++++ packages/common/components/src/ui/index.ts | 1 + 7 files changed, 151 insertions(+), 23 deletions(-) create mode 100644 packages/common/components/src/ui/date-range-picker/index.tsx diff --git a/apps/cs/src/entities/posting/ui/posting-dialog.tsx b/apps/cs/src/entities/posting/ui/posting-dialog.tsx index ed0e25fe4..7a91d510e 100644 --- a/apps/cs/src/entities/posting/ui/posting-dialog.tsx +++ b/apps/cs/src/entities/posting/ui/posting-dialog.tsx @@ -1,3 +1,4 @@ +import dayjs from "dayjs"; import { useState } from "react"; import { useFieldArray, useForm } from "react-hook-form"; import { useQuery } from "@tanstack/react-query"; @@ -17,22 +18,29 @@ import { TableRow, Checkbox, Select, + DateRangePicker, } from "@common/components/ui"; +import { cn } from "@common/components/lib"; import { CodeSelect } from "@/entities/code"; import { SiteApi, type Site } from "@/entities/site"; import type { PageableList } from "@/shared/model"; import { ENDPOINT } from "@/shared/config"; import { type PostingType } from "../model/posting-interface"; -interface PostingTarget { +type DateRange = { + from: string; + to?: string; +}; + +type PostingTarget = { pstgTrgtNm: string; pstgTrgtNo: number; -} +}; -interface PostingDialogProps { +type PostingDialogProps = { postingType: PostingType; postingTargets: PostingTarget[]; -} +}; const POSTING_TYPE_LABEL = { POPUP: "팝업", @@ -42,9 +50,12 @@ const POSTING_TYPE_LABEL = { function PostingDialog({ postingType, postingTargets }: PostingDialogProps) { const [selectedRows, setSelectedRows] = useState([]); - const [rememberSelectValues, setRememberSelectValues] = useState< + const [rememberPeriodCode, setRememberPeriodCode] = useState< Record >({}); + const [rememberPeriodDate, setRememberPeriodDate] = useState< + Record + >({}); const { data: siteList } = useQuery>({ queryKey: [ENDPOINT.CMS_SERVICE.SITES], @@ -60,14 +71,22 @@ function PostingDialog({ postingType, postingTargets }: PostingDialogProps) { const isAllSelected = siteList && selectedRows.length === siteList.content.length; - const initialize = () => { - setSelectedRows([]); - replace([]); + const postingPeriod = (siteId: string) => { + const periodCode = Number(rememberPeriodCode[siteId]); + + if (periodCode === 99) return "상시"; + + return `${dayjs().format("YYYY-MM-DD")} ~ ${dayjs() + .add(periodCode, "day") + .format("YYYY-MM-DD")}`; }; const handleOpenChange = (open: boolean) => { if (open) return true; - initialize(); + replace([]); + setSelectedRows([]); + setRememberPeriodCode({}); + setRememberPeriodDate({}); }; const handleAppendFields = (i: number) => { @@ -76,9 +95,9 @@ function PostingDialog({ postingType, postingTargets }: PostingDialogProps) { siteId, pstgTypeCd: postingType, pstgTrgtNo: target.pstgTrgtNo, - pstgBgngDt: "", - pstgEndDt: "", - pstgPrdSeCd: rememberSelectValues[siteId] ?? "", + pstgBgngDt: rememberPeriodDate[siteId]?.from ?? "", + pstgEndDt: rememberPeriodDate[siteId]?.to ?? "", + pstgPrdSeCd: rememberPeriodCode[siteId] ?? "", delYn: "N", mode: "C", })); @@ -120,7 +139,8 @@ function PostingDialog({ postingType, postingTargets }: PostingDialogProps) { const handleSelectAll = () => { if (selectedRows.length === (siteList?.content.length ?? 0)) { - initialize(); + replace([]); + setSelectedRows([]); } else { setSelectedRows(siteList?.content.map((_, i) => i) ?? []); replace([]); @@ -128,8 +148,8 @@ function PostingDialog({ postingType, postingTargets }: PostingDialogProps) { } }; - const handleSelectValueChange = (siteId: string) => (value: string) => { - setRememberSelectValues((prev) => ({ + const handlePeriodCodeChange = (siteId: string) => (value: string) => { + setRememberPeriodCode((prev) => ({ ...prev, [siteId]: value, })); @@ -138,6 +158,20 @@ function PostingDialog({ postingType, postingTargets }: PostingDialogProps) { }); }; + const handlePeriodDateChange = + (siteId: string) => (value: DateRange | undefined) => { + const from = dayjs(value?.from).format("YYYY-MM-DD 00:00:00"); + const to = dayjs(value?.to).format("YYYY-MM-DD 23:59:59"); + setRememberPeriodDate((prev) => ({ + ...prev, + [siteId]: { from, to }, + })); + handleUpdateFields(siteId)({ + pstgBgngDt: from, + pstgEndDt: to, + }); + }; + return ( @@ -192,10 +226,32 @@ function PostingDialog({ postingType, postingTargets }: PostingDialogProps) { {site.siteNm} - +
+ + {!!Number(rememberPeriodCode[site.siteId]) && ( + + 게시 기간: {postingPeriod(site.siteId)} + + )} + {Number(rememberPeriodCode[site.siteId]) === 0 && ( + + handlePeriodDateChange(site.siteId)( + date as DateRange | undefined, + ) + } + disabled={!selectedRows.includes(i)} + /> + )} +
))} diff --git a/packages/common/components/src/ui/calendar/index.tsx b/packages/common/components/src/ui/calendar/index.tsx index 12c30816b..c1a6d5204 100644 --- a/packages/common/components/src/ui/calendar/index.tsx +++ b/packages/common/components/src/ui/calendar/index.tsx @@ -44,7 +44,7 @@ export function Calendar({ ), day_range_end: "day-range-end", day_selected: - "!bg-primary !text-primary-foreground hover:bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground", + "bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground", day_today: "bg-accent text-accent-foreground", day_outside: "day-outside text-muted-foreground aria-selected:bg-accent/50 aria-selected:text-muted-foreground", diff --git a/packages/common/components/src/ui/date-picker/date-picker.css b/packages/common/components/src/ui/date-picker/date-picker.css index bf9415652..7a1daffac 100644 --- a/packages/common/components/src/ui/date-picker/date-picker.css +++ b/packages/common/components/src/ui/date-picker/date-picker.css @@ -2,6 +2,6 @@ @layer components { .date-picker-trigger { - @apply h-[3rem] w-[12rem] justify-between border-input !bg-background px-4 py-2 text-left text-[1.3rem] font-normal leading-none text-muted-foreground hover:!bg-accent; + @apply h-[3rem] w-[12rem] justify-start border-input !bg-background px-4 py-2 text-left text-[1.3rem] font-normal text-muted-foreground hover:!bg-accent; } } diff --git a/packages/common/components/src/ui/date-picker/date-picker.stories.tsx b/packages/common/components/src/ui/date-picker/date-picker.stories.tsx index baae9d769..6b6e2080d 100644 --- a/packages/common/components/src/ui/date-picker/date-picker.stories.tsx +++ b/packages/common/components/src/ui/date-picker/date-picker.stories.tsx @@ -1,6 +1,6 @@ +import { useState } from "react"; import type { Meta, StoryObj } from "@storybook/react"; import { DatePicker } from "."; -import { useState } from "react"; const meta: Meta = { title: "Elements/DatePicker", diff --git a/packages/common/components/src/ui/date-picker/index.tsx b/packages/common/components/src/ui/date-picker/index.tsx index d6a6553cd..8a9d2155c 100644 --- a/packages/common/components/src/ui/date-picker/index.tsx +++ b/packages/common/components/src/ui/date-picker/index.tsx @@ -19,12 +19,12 @@ export function DatePicker({ className, ...props }: DatePickerProps) { variant="outline" className={cn("date-picker-trigger", className)} > + {props.selected ? ( format(props.selected, "yyyy-MM-dd") ) : ( 날짜 선택 )} - diff --git a/packages/common/components/src/ui/date-range-picker/index.tsx b/packages/common/components/src/ui/date-range-picker/index.tsx new file mode 100644 index 000000000..2c99347a2 --- /dev/null +++ b/packages/common/components/src/ui/date-range-picker/index.tsx @@ -0,0 +1,71 @@ +"use client"; + +import { useState } from "react"; +import { format } from "date-fns"; +import { CalendarIcon } from "lucide-react"; +import type { DateRange } from "react-day-picker"; + +import { cn } from "../../lib/utils"; +import { Button } from "../button"; +import { Calendar } from "../calendar"; +import { Popover, PopoverContent, PopoverTrigger } from "../popover"; + +type DateRangePickerProps = React.HTMLAttributes & { + disabled?: boolean; + handleDateChange?: (date: DateRange | undefined) => void; +}; + +export function DateRangePicker({ + disabled, + className, + handleDateChange, +}: DateRangePickerProps) { + const [date, setDate] = useState(); + + const handleSelect = (date: DateRange | undefined) => { + setDate(date); + handleDateChange?.(date); + }; + + return ( +
+ + + + + + + + +
+ ); +} diff --git a/packages/common/components/src/ui/index.ts b/packages/common/components/src/ui/index.ts index c3aad6220..c51eef045 100644 --- a/packages/common/components/src/ui/index.ts +++ b/packages/common/components/src/ui/index.ts @@ -5,6 +5,7 @@ export * from "./calendar"; export * from "./checkbox"; export * from "./data-table"; export * from "./date-picker"; +export * from "./date-range-picker"; export * from "./dialog"; export * from "./dropdown-menu"; export * from "./file-uploader";