Skip to content

Conversation

@dioo1461
Copy link
Contributor

@dioo1461 dioo1461 commented Feb 5, 2025

#️⃣ 연관된 이슈>

📝 작업 내용> 이번 PR에서 작업한 내용을 간략히 설명해주세요(이미지 첨부 가능)

Date Picker 컴포넌트를 구현했습니다.

  • calendarType: select
    image

  • calendarType: range
    image

🙏 여기는 꼭 봐주세요! > 리뷰어가 특별히 봐주었으면 하는 부분이 있다면 작성해주세요

현재 useDatePicker 훅 내에서 달력 정보(현재 년도, 달, 선택된 날짜 등)을 state로 관리하고 있는데, 외부와 상호작용할 수 있게 하려면 외부에서 state를 주입해주는 형태로 리팩터링해야할 것 같습니다.

Summary by CodeRabbit

  • New Features

    • Launched an advanced DatePicker featuring a new header component for improved navigation and user experience.
    • Introduced a Row component to render rows of date cells within the DatePicker.
    • Added a HighlightBox and HighlightGap components to visually indicate selected date ranges.
    • Implemented new date selection logic with enhanced highlight states for better usability.
    • Added a new arrow icon for clearer navigational cues.
  • Style

    • Refined visual styling across DatePicker elements for a modern, cohesive look.
    • Updated avatar designs with new size options and improved styling for enhanced user experience.

@dioo1461 dioo1461 added the 🖥️ FE Frontend label Feb 5, 2025
@dioo1461 dioo1461 added this to the 1차 스프린트 milestone Feb 5, 2025
@dioo1461 dioo1461 self-assigned this Feb 5, 2025
@dioo1461 dioo1461 requested a review from hamo-o as a code owner February 5, 2025 19:14
@coderabbitai
Copy link

coderabbitai bot commented Feb 5, 2025

Walkthrough

This pull request introduces a new DatePicker feature. It adds various supporting files including Storybook configurations, a dedicated DatePicker context, multiple hooks (for month navigation, date selection, and highlight range), and several UI components such as headers, cells, rows, and table layouts. Additionally, it provides new CSS modules for styling and enhancements in date utilities and icon components. Minor newline additions are also included in a few files. The changes collectively build the DatePicker interface and functionality without altering existing public APIs.

Changes

File(s) Affected Change Summary
frontend/src/components/Calendar/Calendar.stories.ts, frontend/src/constants/date.ts Added newline at file end; no functional changes.
frontend/src/components/DatePicker/DatePicker.stories.tsx Introduced Storybook configuration for DatePicker with meta object and default story export.
frontend/src/components/DatePicker/DatePickerContext.ts Created DatePicker context and defined DatePickerContextProps for shared state.
frontend/src/components/DatePicker/Header/index.tsx, frontend/src/components/DatePicker/Header/index.css.ts Added new Header component for month navigation with chevron buttons and associated CSS styles.
frontend/src/components/DatePicker/Table/Cell/{CellWrapper.tsx,DateCell.tsx,DowCell.tsx, index.css.ts}, frontend/src/components/DatePicker/Table/Highlight/{HighlightBox.tsx,HighlightGap.tsx, index.css.ts}, frontend/src/components/DatePicker/Table/{Row.tsx,RowContainer.tsx,TableBody.tsx,TableHeader.tsx, index.css.ts, index.tsx} Introduced new components for DatePicker table layout including cells, rows, highlight components, and corresponding CSS modules.
frontend/src/components/DatePicker/index.tsx, frontend/src/components/DatePicker/index.css.ts Added DatePicker component integrating custom hooks, context, and dynamic styling based on calendar type.
frontend/src/hooks/useDatePicker/{index.ts, useDateSelect.ts, useHighlightRange.ts, useMonthNavigation.ts} Added custom hooks to manage month navigation, date selection, and highlight range for the DatePicker.
frontend/src/utils/date/{calendar/*, date.ts, index.ts} Added and updated utility functions for calendar generation and date manipulation (e.g., weekend checks).
frontend/src/components/Icon/component/ArrowLeft.tsx Added new ArrowLeft icon component for navigation.
frontend/src/components/Avatar/index.css.ts Introduced new CSS module for Avatar component styling using vanilla-extract.

Sequence Diagram(s)

sequenceDiagram
    participant U as User
    participant DC as DateCell
    participant DS as useDateSelect Hook
    participant DP as DatePicker Component
    participant DT as useDatePicker Hook

    U->>DC: Click on date cell
    DC->>DS: Trigger selectDate(date)
    DS->>DP: Call handleDateSelect(date)
    DP->>DT: Update state (baseDate, calendarDates, etc.)
    DT-->>DP: Return updated calendar data
    DP->>U: Re-render DatePicker with updated state
Loading
sequenceDiagram
    participant U as User
    participant H as Header
    participant MN as useMonthNavigation Hook
    participant DP as DatePicker Component

    U->>H: Click on chevron button
    H->>MN: Invoke goToPrevMonth/goToNextMonth
    MN-->>DP: Update baseDate accordingly
    DP->>U: Re-render DatePicker with new month view
Loading

Possibly related issues

  • [Feature] Date Picker 구현 #60: The changes implement the DatePicker component including layout, date selection, and range highlighting, which aligns with the requirements for the Date Picker feature.

Suggested reviewers

  • hamo-o

Poem

I’m a rabbit with a hop and a cheer,
Coding dates and cells, the path is clear.
With little clicks and changing month’s view,
My DatePicker components are shiny and new!
Hop along, let’s celebrate our code debut!
🐇🎉

Tip

🌐 Web search-backed reviews and chat
  • We have enabled web search-based reviews and chat for all users. This feature allows CodeRabbit to access the latest documentation and information on the web.
  • You can disable this feature by setting web_search: false in the knowledge_base settings.
  • Please share any feedback in the Discord discussion.

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🧹 Nitpick comments (30)
frontend/src/utils/jsxUtils.tsx (2)

1-2: Consider using direct React import for types.

The ReactNode type is being imported from '@tanstack/react-router' when it should typically be imported directly from 'react' for better maintainability and to avoid potential version conflicts.

-import type { ReactNode } from '@tanstack/react-router';
+import type { ReactNode, JSX } from 'react';
-import type { JSX } from 'react';

4-16: Consider improvements for React key generation and edge cases.

The implementation looks good but could be enhanced in a few areas:

  1. Using array index as React key might cause issues with reconciliation if the array is modified
  2. No validation for empty or undefined input array

Consider this enhanced implementation:

 export const intersperseElement = (
   childElements: ReactNode[],
   separator?: JSX.Element,
 ): ReactNode[] => {
+  if (!childElements?.length) return [];
   const result: ReactNode[] = [];
   childElements.forEach((child, index) => {
     result.push(child);
     if (separator && index < childElements.length - 1) {
-      result.push(<separator.type {...separator.props} key={index} />);
+      result.push(
+        <separator.type 
+          {...separator.props} 
+          key={`separator-${index}-${separator.key ?? ''}`} 
+        />
+      );
     }
   });
   return result;
 };
frontend/src/components/DatePicker/Table/index.tsx (1)

6-11: Consider adding error boundaries and prop types.

While the implementation is clean, consider these improvements:

  1. Add prop types/interface to make the component more maintainable
  2. Wrap child components with error boundaries to gracefully handle rendering failures

Example implementation:

interface TableProps {
  // Add any props that might be needed in the future
}

const Table: React.FC<TableProps> = () => (
  <ErrorBoundary fallback={<div>Something went wrong</div>}>
    <div className={calendarStyle}>
      <TableHeader />
      <TableBody />
    </div>
  </ErrorBoundary>
);
frontend/src/components/DatePicker/Table/Highlight/HighlightBox.tsx (1)

5-9: Consider adding prop validation and memoization.

The component could benefit from these improvements:

  1. Add runtime validation for highlightState prop
  2. Memoize the component to prevent unnecessary re-renders

Example implementation:

import { memo } from 'react';

const HighlightBox = memo(({ highlightState, children }: HighlightProps) => {
  if (!highlightState) {
    throw new Error('highlightState is required');
  }
  
  return (
    <div className={highlightBoxStyle({ highlightState })}>
      {children}
    </div>
  );
});

HighlightBox.displayName = 'HighlightBox';
frontend/src/components/DatePicker/Table/Highlight/HighlightGap.tsx (1)

5-9: Consider component abstraction to reduce duplication.

The component structure is identical to HighlightBox. Consider creating a base component to reduce code duplication.

Example implementation:

// BaseHighlight.tsx
const BaseHighlight = memo(({ 
  highlightState, 
  children, 
  styleFunction 
}: HighlightProps & { 
  styleFunction: (props: { highlightState: string }) => string 
}) => (
  <div className={styleFunction({ highlightState })}>
    {children}
  </div>
));

// HighlightGap.tsx
const HighlightGap = (props: HighlightProps) => (
  <BaseHighlight {...props} styleFunction={highlightGapStyle} />
);

// HighlightBox.tsx
const HighlightBox = (props: HighlightProps) => (
  <BaseHighlight {...props} styleFunction={highlightBoxStyle} />
);
frontend/src/components/DatePicker/Table/Cell/DowCell.tsx (1)

1-13: LGTM! Consider adding prop types validation.

The component is well-structured and follows React's functional component pattern. Consider adding prop types validation using PropTypes or TypeScript's strict mode for better type safety.

frontend/src/components/DatePicker/Header/index.css.ts (2)

3-9: Consider using symmetric padding and responsive units.

The header style could be improved by:

  1. Using symmetric padding (both left and right) for better visual balance
  2. Using relative units (rem/em) instead of pixels for better responsiveness
export const headerStyle = style({
  display: 'flex',
-  paddingLeft: '8px',
+  padding: '0 0.5rem',
  justifyContent: 'space-between',
  alignItems: 'center',
  alignSelf: 'stretch',
});

11-17: Consider using relative units for dimensions.

For better responsiveness across different screen sizes and font sizes:

export const chevronWrapper = style({
  display: 'flex',
  justifyContent: 'center',
  alignItems: 'center',
-  width: 32,
-  height: 32,
+  width: '2rem',
+  height: '2rem',
});
frontend/src/components/DatePicker/DatePicker.stories.tsx (1)

5-14: Enhance Storybook documentation and add more stories.

Consider adding:

  1. Component description and usage examples in the meta parameters
  2. Additional stories showcasing different states (e.g., with preselected date, disabled dates, etc.)

Add documentation and stories:

 const meta: Meta = {
   title: 'Calendar/DatePicker',
   component: DatePicker,
+  parameters: {
+    docs: {
+      description: {
+        component: 'A flexible date picker component supporting both single date selection and range selection.',
+      },
+    },
+  },
   argTypes: {
     calendarType: {
       control: { type: 'radio' },
       options: ['select', 'range'],
+      description: 'Type of calendar to display - either single date selection or date range selection',
     },
   },
 } satisfies Meta<typeof DatePicker>;

Would you like me to generate additional story examples for different states?

frontend/src/components/DatePicker/Table/Cell/CellWrapper.tsx (1)

14-14: Add null check for className prop.

The className concatenation could result in "undefined" being added to the class list. Add a null check to handle cases where className is not provided.

-    className={`${cellWrapperStyle} ${className}`}
+    className={`${cellWrapperStyle} ${className || ''}`}
frontend/src/hooks/useDatePicker/useMonthNavigation.ts (1)

5-20: Add validation and error handling for edge cases.

Consider adding:

  1. Validation for minimum/maximum allowed dates
  2. Error handling for invalid date operations
  3. Optional initial date parameter to support controlled components
-export const useMonthNavigation = () => {
+export const useMonthNavigation = (initialDate?: Date, minDate?: Date, maxDate?: Date) => {
   const today = new Date();
-  const [baseDate, setBaseDate] = useState(today);
+  const [baseDate, setBaseDate] = useState(initialDate || today);
 
   const goToPrevMonth = () => {
     const { month, year } = getPrevMonthInfo(baseDate);
-    setBaseDate(new Date(year, month));
+    const newDate = new Date(year, month);
+    if (minDate && newDate < minDate) return;
+    setBaseDate(newDate);
   };
 
   const goToNextMonth = () => {
     const { month, year } = getNextMonthInfo(baseDate);
-    setBaseDate(new Date(year, month));
+    const newDate = new Date(year, month);
+    if (maxDate && newDate > maxDate) return;
+    setBaseDate(newDate);
   };
 
   return { baseDate, goToPrevMonth, goToNextMonth };
 };
frontend/src/components/DatePicker/Table/index.css.ts (1)

12-18: Add responsive design support.

The calendar width is fixed at 320px, which might not be optimal for all screen sizes. Consider making it responsive.

 export const calendarStyle = style({
   display: 'flex',
-  width: '320px',
+  width: '100%',
+  maxWidth: '320px',
+  minWidth: '280px',
   flexDirection: 'column',
   justifyContent: 'space-between',
   alignItems: 'center',
 });
frontend/src/hooks/useDatePicker/useDateSelect.ts (1)

13-31: Consider adding validation for date range order.

While the logic handles basic range selection, it could be improved to ensure a more robust implementation.

Consider this enhancement:

 const selectDate = () => {
   handleDateSelect(date);
   if (calendarType === 'select') return;
   if (!highlightRange.start) {
     setHighlightStart(date);
     return;
   }
   if (!highlightRange.end) {
-    if (date < highlightRange.start) {
+    const isBeforeStart = date < highlightRange.start;
+    const isSameAsStart = date.getTime() === highlightRange.start.getTime();
+    if (isSameAsStart) {
+      setHighlightStart(null);
+      return;
+    }
+    if (isBeforeStart) {
       setHighlightStart(date);
       return;
     }
     setHighlightEnd(date);
     return;
   }
   handleDateSelect(null);
   setHighlightStart(null);
   setHighlightEnd(null);
 };
frontend/src/components/DatePicker/Table/Cell/index.css.ts (1)

7-14: Consider accessibility improvements for interactive cells.

The cell wrapper style should include hover and focus states for better accessibility.

 export const cellWrapperStyle = style({
   display: 'flex',
   justifyContent: 'center',
   alignItems: 'center',
   width: '24px',
   height: '24px',
   cursor: 'default',
+  ':hover': {
+    backgroundColor: vars.color.Ref.Netural[100],
+  },
+  ':focus': {
+    outline: `2px solid ${vars.color.Ref.Primary[500]}`,
+    outlineOffset: '2px',
+  },
 });
frontend/src/utils/date/calendar/index.ts (1)

11-12: Consider using Date.UTC for consistent date handling.

The current implementation might have timezone-related edge cases.

-export const getDaysInMonth = (date: Date): number =>
-  new Date(date.getFullYear(), date.getMonth() + 1, 0).getDate();
+export const getDaysInMonth = (date: Date): number => {
+  const year = date.getFullYear();
+  const month = date.getMonth();
+  return new Date(Date.UTC(year, month + 1, 0)).getUTCDate();
+};
frontend/src/components/DatePicker/Table/Highlight/index.css.ts (1)

5-51: Consider extracting shared highlight state variants to reduce duplication.

The highlightState variants are identical between highlightBoxStyle and highlightGapStyle. Consider extracting these into a shared constant to improve maintainability.

+const highlightStateVariants = {
+  none: { },
+  startOfRange: {
+    backgroundColor: vars.color.Ref.Primary[50],
+    borderTopLeftRadius: vars.radius[200],
+    borderBottomLeftRadius: vars.radius[200],
+  },
+  inRange: {
+    backgroundColor: vars.color.Ref.Primary[50],
+  },
+  endOfRange: {
+    backgroundColor: vars.color.Ref.Primary[50],
+    borderTopRightRadius: vars.radius[200],
+    borderBottomRightRadius: vars.radius[200],
+  },
+};

 export const highlightBoxStyle = recipe({
   base: {
     padding: vars.spacing[100],
   },
   variants: {
-    highlightState: {
-      none: { },
-      startOfRange: {
-        backgroundColor: vars.color.Ref.Primary[50],
-        borderTopLeftRadius: vars.radius[200],
-        borderBottomLeftRadius: vars.radius[200],
-      },
-      inRange: {
-        backgroundColor: vars.color.Ref.Primary[50],
-      },
-      endOfRange: {
-        backgroundColor: vars.color.Ref.Primary[50],
-        borderTopRightRadius: vars.radius[200],
-        borderBottomRightRadius: vars.radius[200],
-      },
-    },
+    highlightState: highlightStateVariants,
   },
 });

 export const highlightGapStyle = recipe({
   base: {
     flexGrow: 1,
   },
   variants: {
-    highlightState: {
-      none: { },
-      startOfRange: {
-        backgroundColor: vars.color.Ref.Primary[50],
-        borderTopLeftRadius: vars.radius[200],
-        borderBottomLeftRadius: vars.radius[200],
-      },
-      inRange: {
-        backgroundColor: vars.color.Ref.Primary[50],
-      },
-      endOfRange: {
-        backgroundColor: vars.color.Ref.Primary[50],
-        borderTopRightRadius: vars.radius[200],
-        borderBottomRightRadius: vars.radius[200],
-      },
-    },
+    highlightState: highlightStateVariants,
   },
 });
frontend/src/components/Icon/component/ArrowLeft.tsx (1)

6-6: Consider enhancing accessibility with role attribute.

Add the role="img" attribute to the SVG for better screen reader support.

-<svg width={width} height={width} viewBox="0 0 24 24"  xmlns="http://www.w3.org/2000/svg" aria-label="arrow-left icon" fill="none" className={className} {...rest}>
+<svg width={width} height={width} viewBox="0 0 24 24"  xmlns="http://www.w3.org/2000/svg" aria-label="arrow-left icon" role="img" fill="none" className={className} {...rest}>
frontend/src/components/DatePicker/index.tsx (2)

21-21: Address the TODO comment about container style injection.

The comment suggests implementing external style injection for the container. Consider adding a className prop to allow style customization.

Would you like me to help implement the external style injection feature?


38-44: Consider memoizing style helper functions.

The getTodayCellStyle and getSelectedCellStyle functions are recreated on every render. Consider using useMemo to optimize performance.

+import { useMemo } from 'react';

 const DatePicker = ({ calendarType }: DatePickerProps) => {
   const useDatePickerReturns = useDatePicker();
-  const todayCellStyle = getTodayCellStyle(calendarType);
-  const selectedCellStyle = getSelectedCellStyle(calendarType);
+  const todayCellStyle = useMemo(() => getTodayCellStyle(calendarType), [calendarType]);
+  const selectedCellStyle = useMemo(() => getSelectedCellStyle(calendarType), [calendarType]);
frontend/src/hooks/useDatePicker/index.ts (2)

30-32: Optimize useEffect dependencies.

The selectedDate and highlightRange in the dependency array might cause unnecessary calendar regeneration. Consider if these dependencies are really needed for regenerating the calendar.

 useEffect(() => {
   setCalendarDates(generateMonthCalendar(baseDate));
-}, [baseDate, selectedDate, highlightRange]);
+}, [baseDate]); // Only regenerate when baseDate changes

34-52: Simplify date comparison logic.

The date comparison logic could be simplified using Date utility functions. Also, consider extracting the navigation logic to a separate function for better readability.

+const shouldNavigateMonth = (date: Date, baseDate: Date): 'prev' | 'next' | null => {
+  const startOfMonth = new Date(baseDate.getFullYear(), baseDate.getMonth(), 1);
+  const endOfMonth = new Date(baseDate.getFullYear(), baseDate.getMonth() + 1, 0);
+  
+  if (date < startOfMonth) return 'prev';
+  if (date > endOfMonth) return 'next';
+  return null;
+};

 const handleDateSelect = (date: Date | null) => {
   if (!date) {
     setSelectedDate(null);
     return;
   }
-  const currentYear = baseDate.getFullYear();
-  const currentMonth = baseDate.getMonth();
-  const calendarDates = getDaysInMonth(baseDate);
-
-  const startOfCurrentMonth = new Date(currentYear, currentMonth, 1);
-  const endOfCurrentMonth = new Date(currentYear, currentMonth, calendarDates);
-
-  if (date < startOfCurrentMonth) {
-    goToPrevMonth();
-  } else if (date > endOfCurrentMonth) {
-    goToNextMonth();
-  }
+  const navigation = shouldNavigateMonth(date, baseDate);
+  if (navigation === 'prev') goToPrevMonth();
+  if (navigation === 'next') goToNextMonth();
   setSelectedDate(date);
 };
frontend/src/utils/date/date.ts (2)

101-102: Consider moving constants to a separate file.

These day code constants could be moved to the date constants file alongside WEEK_MAP for better organization and reusability.

-const SUNDAY_CODE = 0;
-const SATURDAY_CODE = 6;

Add to @/constants/date.ts:

export const DAY_CODES = {
  SUNDAY: 0,
  SATURDAY: 6
} as const;

109-110: Enhance the TODO comment with implementation details.

The TODO comment could be more specific about the implementation plan.

-// TODO: 공휴일 OPEN API에 연결
-// export const isHoliday = (date: Date): boolean => false;
+/**
+ * TODO: Implement holiday detection using the Holiday API
+ * 
+ * Implementation plan:
+ * 1. Connect to the Holiday API (e.g., https://api.holiday.info)
+ * 2. Cache the holiday data for performance
+ * 3. Implement isHoliday function to check against cached data
+ */
+// export const isHoliday = (date: Date): boolean => false;
frontend/src/utils/date/calendar/calendarGeneration.ts (2)

14-20: Optimize array partitioning for better performance.

The current implementation creates a new array for each chunk. Consider using Array.from for better performance.

-const partitionArray = <T>(array: T[], size: number): T[][] => {
-  const chunks: T[][] = [];
-  for (let i = 0; i < array.length; i += size) {
-    chunks.push(array.slice(i, i + size));
-  }
-  return chunks;
-};
+const partitionArray = <T>(array: T[], size: number): T[][] => 
+  Array.from({ length: Math.ceil(array.length / size) }, (_, i) => 
+    array.slice(i * size, i * size + size)
+  );

79-88: Optimize memory usage in generateNextMonthDays.

The function creates unnecessary intermediate variables. Consider simplifying the implementation.

 const generateNextMonthDays = (existingCount: number, baseDate: Date): Date[] => {
   const remainingCells = (WEEK_DAYS - (existingCount % WEEK_DAYS)) % WEEK_DAYS;
   const { nextDate } = getNextMonthInfo(baseDate);
-  const [nextYear, nextMonth] = [nextDate.getFullYear(), nextDate.getMonth()];
-  const dates: Date[] = [];
-  for (let day = FIRST_DAY; day <= remainingCells; day++) {
-    dates.push(new Date(nextYear, nextMonth, day));
-  }
-  return dates;
+  return Array.from({ length: remainingCells }, (_, i) => 
+    new Date(nextDate.getFullYear(), nextDate.getMonth(), FIRST_DAY + i)
+  );
 };
frontend/src/components/DatePicker/Table/Cell/DateCell.tsx (2)

34-39: Memoize inline styles to prevent unnecessary re-renders.

The inline styles object is recreated on every render. Consider using useMemo to optimize performance.

+import { useMemo } from 'react';

 const DateCell = ({ date, selected, baseDate, highlightState }: DateCellProps) => {
   const { 
     calendarType,
     todayCellStyle,
     selectedCellStyle,
   } = useSafeContext(DatePickerContext);
-  const inlineCellStyles = assignInlineVars(cellThemeVars, {
-    todayCellBackgroundColor: todayCellStyle.backgroundColor ?? 'transparent',
-    todayCellColor: todayCellStyle.color ?? 'transparent',
-    selectedCellBackgroundColor: selectedCellStyle.backgroundColor ?? 'transparent',
-    selectedCellColor: selectedCellStyle.color ?? 'transparent',
-  });
+  const inlineCellStyles = useMemo(() => 
+    assignInlineVars(cellThemeVars, {
+      todayCellBackgroundColor: todayCellStyle.backgroundColor ?? 'transparent',
+      todayCellColor: todayCellStyle.color ?? 'transparent',
+      selectedCellBackgroundColor: selectedCellStyle.backgroundColor ?? 'transparent',
+      selectedCellColor: selectedCellStyle.color ?? 'transparent',
+    }),
+    [todayCellStyle, selectedCellStyle]
+  );

68-69: Link TODO comment to the implementation plan.

The TODO comment about holiday checks should be linked to the implementation plan in the date utility file.

-  // TODO: 공휴일도 함께 체크
+  // TODO: Implement holiday check after the Holiday API integration is complete
+  // See: frontend/src/utils/date/date.ts
frontend/src/components/DatePicker/Table/Row.tsx (3)

35-36: Translate Korean comment to English for consistency.

For better maintainability and collaboration, comments should be in English.

-        // 마지막 셀 뒤에는 gap을 넣지 않음
+        // Skip adding gap after the last cell

49-57: Move highlight state logic to a separate utility file.

The highlight state logic could be reused in other components. Consider moving it to a separate utility file.

Create a new file frontend/src/utils/highlight.ts:

import { isSameDate } from './date';
import type { HighlightRange } from '@/hooks/useDatePicker/useHighlightRange';
import type { HighlightState } from '@/components/DatePicker/Table/Highlight';

export const getHighlightState = (date: Date, highlightRange: HighlightRange): HighlightState => {
  const { start, end } = highlightRange;
  if (!start || !end) return 'none';
  
  if (isSameDate(date, start)) return 'startOfRange';
  if (isSameDate(date, end)) return 'endOfRange';
  if (date > start && date < end) return 'inRange';
  return 'none';
};

23-44: Optimize array mapping to reduce complexity.

The current implementation uses array mapping with conditional returns. Consider using a more declarative approach.

-      {week.map((day, index) => {
-        const highlightState = getHighlightState(day, highlightRange);
-        const cell = (
-          <HighlightBox highlightState={highlightState} key={`cell-${day.getTime()}`}>
-            <DateCell 
-              baseDate={baseDate}
-              date={day}
-              highlightState={highlightState}
-              selected={selectedDate ? isSameDate(day, selectedDate) : false}
-            />
-          </HighlightBox>
-        );
-        // Skip adding gap after the last cell
-        if (index === week.length - 1) return cell;
-        const gap = (
-          <HighlightGap 
-            highlightState={getGapHighlightState(highlightState)} 
-            key={`gap-${day.getTime()}`}
-          />
-        );
-        return [cell, gap];
-      })}
+      {week.flatMap((day, index) => {
+        const highlightState = getHighlightState(day, highlightRange);
+        const elements = [(
+          <HighlightBox highlightState={highlightState} key={`cell-${day.getTime()}`}>
+            <DateCell 
+              baseDate={baseDate}
+              date={day}
+              highlightState={highlightState}
+              selected={selectedDate ? isSameDate(day, selectedDate) : false}
+            />
+          </HighlightBox>
+        )];
+        
+        if (index < week.length - 1) {
+          elements.push(
+            <HighlightGap 
+              highlightState={getGapHighlightState(highlightState)} 
+              key={`gap-${day.getTime()}`}
+            />
+          );
+        }
+        
+        return elements;
+      })}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 870c9a9 and ba29a0b2b87924ec14d7a8043aa07292c2d1bd4a.

⛔ Files ignored due to path filters (1)
  • frontend/src/components/Icon/svg/arrow-left.svg is excluded by !**/*.svg
📒 Files selected for processing (35)
  • frontend/src/components/Calendar/Calendar.stories.ts (1 hunks)
  • frontend/src/components/DatePicker/DatePicker.stories.tsx (1 hunks)
  • frontend/src/components/DatePicker/DatePickerContext.ts (1 hunks)
  • frontend/src/components/DatePicker/Header/index.css.ts (1 hunks)
  • frontend/src/components/DatePicker/Header/index.tsx (1 hunks)
  • frontend/src/components/DatePicker/Table/Cell/CellWrapper.tsx (1 hunks)
  • frontend/src/components/DatePicker/Table/Cell/DateCell.tsx (1 hunks)
  • frontend/src/components/DatePicker/Table/Cell/DowCell.tsx (1 hunks)
  • frontend/src/components/DatePicker/Table/Cell/index.css.ts (1 hunks)
  • frontend/src/components/DatePicker/Table/Cell/index.ts (1 hunks)
  • frontend/src/components/DatePicker/Table/Highlight/HighlightBox.tsx (1 hunks)
  • frontend/src/components/DatePicker/Table/Highlight/HighlightGap.tsx (1 hunks)
  • frontend/src/components/DatePicker/Table/Highlight/index.css.ts (1 hunks)
  • frontend/src/components/DatePicker/Table/Highlight/index.ts (1 hunks)
  • frontend/src/components/DatePicker/Table/Row.tsx (1 hunks)
  • frontend/src/components/DatePicker/Table/RowContainer.tsx (1 hunks)
  • frontend/src/components/DatePicker/Table/TableBody.tsx (1 hunks)
  • frontend/src/components/DatePicker/Table/TableHeader.tsx (1 hunks)
  • frontend/src/components/DatePicker/Table/index.css.ts (1 hunks)
  • frontend/src/components/DatePicker/Table/index.tsx (1 hunks)
  • frontend/src/components/DatePicker/index.css.ts (1 hunks)
  • frontend/src/components/DatePicker/index.tsx (1 hunks)
  • frontend/src/components/Icon/component/ArrowLeft.tsx (1 hunks)
  • frontend/src/components/Input/Input.stories.tsx (2 hunks)
  • frontend/src/components/Input/MultiInput.tsx (4 hunks)
  • frontend/src/constants/date.ts (1 hunks)
  • frontend/src/hooks/useDatePicker/index.ts (1 hunks)
  • frontend/src/hooks/useDatePicker/useDateSelect.ts (1 hunks)
  • frontend/src/hooks/useDatePicker/useHighlightRange.ts (1 hunks)
  • frontend/src/hooks/useDatePicker/useMonthNavigation.ts (1 hunks)
  • frontend/src/utils/date/calendar/calendarGeneration.ts (1 hunks)
  • frontend/src/utils/date/calendar/index.ts (1 hunks)
  • frontend/src/utils/date/date.ts (1 hunks)
  • frontend/src/utils/date/index.ts (1 hunks)
  • frontend/src/utils/jsxUtils.tsx (1 hunks)
✅ Files skipped from review due to trivial changes (5)
  • frontend/src/components/Calendar/Calendar.stories.ts
  • frontend/src/utils/date/index.ts
  • frontend/src/components/DatePicker/Table/Cell/index.ts
  • frontend/src/constants/date.ts
  • frontend/src/components/Input/Input.stories.tsx
🧰 Additional context used
📓 Learnings (1)
frontend/src/components/DatePicker/Header/index.tsx (2)
Learnt from: hamo-o
PR: softeer5th/Team4-enDolphin#80
File: frontend/src/components/Calendar/Header/CalendarHeader.tsx:8-24
Timestamp: 2025-02-04T03:20:42.270Z
Learning: The codebase uses `useSafeContext` hook to handle React context error cases by throwing an error with message "🚨 이 컴포넌트는 ContextProvider 내부에서 사용되어야 합니다!" when the context value is null.
Learnt from: hamo-o
PR: softeer5th/Team4-enDolphin#80
File: frontend/src/components/Calendar/Header/CalendarHeader.tsx:8-24
Timestamp: 2025-02-04T03:20:42.270Z
Learning: The codebase uses `useSafeContext` hook to handle React context error cases, making explicit null checks redundant.
🪛 Biome (1.9.4)
frontend/src/components/DatePicker/Table/TableHeader.tsx

[error] 19-20: Missing key property for this element in iterable.

The order of the items may change, and having a key can help React identify which item was moved.
Check the React documentation.

(lint/correctness/useJsxKeyInIterable)

⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Send PR Notification to Reviewers
🔇 Additional comments (8)
frontend/src/components/Input/MultiInput.tsx (3)

4-5: LGTM!

The import statement follows the project's conventions and correctly imports the new utility.


33-33: LGTM!

The replacement of the previous implementation with intersperseElement is correct and maintains the same functionality.


45-45: LGTM!

Minor formatting change that improves code readability.

frontend/src/components/DatePicker/Table/RowContainer.tsx (1)

1-11: LGTM! Clean and well-structured component.

The component follows React best practices with proper TypeScript usage, children prop handling, and CSS module integration.

frontend/src/components/DatePicker/Table/Highlight/index.ts (1)

1-11: LGTM! Well-structured type definitions for range highlighting.

The types and interface are well-defined, covering all necessary states for range highlighting in the calendar component.

frontend/src/hooks/useDatePicker/useDateSelect.ts (1)

4-12: LGTM! Proper context usage with error handling.

The hook correctly uses useSafeContext to handle context error cases, as per the codebase conventions.

frontend/src/components/DatePicker/Table/Cell/index.css.ts (1)

16-42: LGTM! Consistent use of theme variables.

The styles properly utilize theme variables for colors and maintain consistency across different cell states.

frontend/src/utils/date/calendar/index.ts (1)

19-26: LGTM! Well-documented and handles edge cases.

The month navigation functions are well-documented and properly handle year transitions.

Also applies to: 33-40

@dioo1461 dioo1461 changed the title Feature/fe/date picker component [FE-Feat] Date Picker 컴포넌트 구현 Feb 5, 2025
@hamo-o hamo-o linked an issue Feb 6, 2025 that may be closed by this pull request
2 tasks
Copy link
Contributor

@hamo-o hamo-o left a comment

Choose a reason for hiding this comment

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

수고하셨습니다!! disabled된 날짜 이벤트 처리는 기/디 논의 해보아요~~~

Copy link
Contributor

Choose a reason for hiding this comment

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

p3; 인덱스를 사용한 키값은 지양해 주세요.

Comment on lines +30 to +35
Copy link
Contributor

Choose a reason for hiding this comment

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

p2;
확인해보니 calendarDates는 한군데에서만 사용되는 것 같은데, 굳이 커다란 상태로 관리하지 않고
소비자 쪽에서 baseDategenerateMonthCalendar로 감싸서 사용하면 어떨까요?

Comment on lines +28 to +37
Copy link
Contributor

Choose a reason for hiding this comment

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

p3;
제공해주는 쪽에서 reset 함수를 만들어주면 한 번에 깔끔하게 처리할 수 있을 것 같습니다.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

반영해보겠습니다!

@dioo1461 dioo1461 force-pushed the feature/fe/date-picker-component branch from ba29a0b to 1601ef3 Compare February 6, 2025 03:15
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
frontend/src/components/DatePicker/Table/TableHeader.tsx (1)

16-26: ⚠️ Potential issue

Fix key prop placement.

The key prop is incorrectly placed on the DowCell instead of the outer div. This could cause React reconciliation issues.

-      <div className={dowCellWrapperStyle}>
-        <DowCell className={style} key={`${label}-${i}`}>
+      <div key={`${label}-${i}`} className={dowCellWrapperStyle}>
+        <DowCell className={style}>
           {label}
         </DowCell>
       </div>
🧰 Tools
🪛 Biome (1.9.4)

[error] 19-20: Missing key property for this element in iterable.

The order of the items may change, and having a key can help React identify which item was moved.
Check the React documentation.

(lint/correctness/useJsxKeyInIterable)

🧹 Nitpick comments (9)
frontend/src/components/DatePicker/Table/Row.tsx (2)

23-44: Optimize rendering performance.

The current implementation creates new arrays on each render. Consider using React.memo and useMemo for better performance.

+const MemoizedDateCell = React.memo(DateCell);
+const MemoizedHighlightBox = React.memo(HighlightBox);
+const MemoizedHighlightGap = React.memo(HighlightGap);

 const Row = ({ week, baseDate, selectedDate }: RowProps) => {
   const { highlightRange } = useSafeContext(DatePickerContext);
+  const cells = useMemo(() => 
     week.map((day, index) => {
       const highlightState = getHighlightState(day, highlightRange);
       const cell = (
-        <HighlightBox highlightState={highlightState} key={`cell-${day.getTime()}`}>
-          <DateCell 
+        <MemoizedHighlightBox highlightState={highlightState} key={`cell-${day.getTime()}`}>
+          <MemoizedDateCell 
             baseDate={baseDate}
             date={day}
             highlightState={highlightState}
             selected={selectedDate ? isSameDate(day, selectedDate) : false}
           />
-        </HighlightBox>
+        </MemoizedHighlightBox>
       );
       if (index === week.length - 1) return cell;
       const gap = (
-        <HighlightGap 
+        <MemoizedHighlightGap 
           highlightState={getGapHighlightState(highlightState)} 
           key={`gap-${day.getTime()}`}
         />
       );
       return [cell, gap];
-    })
+    }), [week, baseDate, selectedDate, highlightRange]);

49-57: Memoize highlight state calculation.

The getHighlightState function is called for each cell render. Consider memoizing the result.

+const memoizedGetHighlightState = memoize(
   (date: Date, highlightRange: HighlightRange): HighlightState => {
     const { start, end } = highlightRange;
     if (!start || !end) return 'none';
     
     if (isSameDate(date, start)) return 'startOfRange';
     if (isSameDate(date, end)) return 'endOfRange';
     if (date > start && date < end) return 'inRange';
     return 'none';
   }
+);
frontend/src/components/DatePicker/Table/Cell/DateCell.tsx (2)

68-68: Address TODO comment for holiday checks.

The TODO comment indicates missing holiday check functionality.

Would you like me to help implement the holiday check functionality using the commented-out isHoliday function from the date utils?


52-72: Simplify style selection logic.

The getDateCellStyle function can be simplified using a lookup object.

+const cellStyleMap = {
+  selected: selectedCellStyle,
+  range: (highlightState: HighlightState) => 
+    (highlightState === 'startOfRange' || highlightState === 'endOfRange') 
+      ? selectedCellStyle 
+      : null,
+  today: todayCellStyle,
+  otherMonth: otherMonthCellStyle,
+  sunday: holidayCellStyle,
+  saturday: saturdayCellStyle,
+  default: weekdayCellStyle
+};

 const getDateCellStyle = (
   date: Date,
   calendarType: CalendarType,
   selected: boolean,
   baseDate: Date,
   highlightState: HighlightState,
 ) => {
-  if (selected) return selectedCellStyle;
-  if (calendarType === 'range' && (
-    highlightState === 'startOfRange' || highlightState === 'endOfRange'
-  )) {
-    return selectedCellStyle;
-  }
-  if (calendarType === 'select' && isSameDate(date, new Date())) return todayCellStyle;
-  if (date.getMonth() !== baseDate.getMonth()) return otherMonthCellStyle;
-  if (isSunday(date)) return holidayCellStyle;
-  if (isSaturday(date)) return saturdayCellStyle;
-  return weekdayCellStyle;
+  if (selected) return cellStyleMap.selected;
+  if (calendarType === 'range') {
+    const rangeStyle = cellStyleMap.range(highlightState);
+    if (rangeStyle) return rangeStyle;
+  }
+  if (calendarType === 'select' && isSameDate(date, new Date())) 
+    return cellStyleMap.today;
+  if (date.getMonth() !== baseDate.getMonth()) 
+    return cellStyleMap.otherMonth;
+  if (isSunday(date)) return cellStyleMap.sunday;
+  if (isSaturday(date)) return cellStyleMap.saturday;
+  return cellStyleMap.default;
 };
frontend/src/utils/date/date.ts (1)

154-155: Plan holiday API integration.

The TODO comment indicates a need for holiday API integration.

Would you like me to help:

  1. Research and suggest suitable holiday APIs
  2. Create an implementation plan for the holiday check functionality
  3. Generate a template for the API integration?
frontend/src/utils/jsxUtils.tsx (1)

1-2: Consider importing ReactNode from 'react'.

ReactNode is a core React type and should be imported directly from 'react' instead of '@tanstack/react-router'.

-import type { ReactNode } from '@tanstack/react-router';
+import type { ReactNode, JSX } from 'react';
-import type { JSX } from 'react';
frontend/src/components/DatePicker/index.tsx (2)

21-22: Consider implementing external style injection.

The TODO comment aligns with the PR objectives about allowing external state management. This would improve component flexibility.

Consider adding a containerClassName prop to allow style injection:

 export interface DatePickerProps {
   calendarType: CalendarType;
+  containerClassName?: string;
 }

-const DatePicker = ({ calendarType }: DatePickerProps) => {
+const DatePicker = ({ calendarType, containerClassName }: DatePickerProps) => {

38-44: Fix typo in color variable and optimize style calculations.

There's a typo in the color variable name ('Netural' should be 'Neutral') and the style calculations could be optimized.

Apply these improvements:

-  ? { backgroundColor: vars.color.Ref.Primary[500], color: vars.color.Ref.Netural['White'] }
+  ? { backgroundColor: vars.color.Ref.Primary[500], color: vars.color.Ref.Neutral['White'] }

+const memoizedTodayCellStyle = useMemo(
+  () => getTodayCellStyle(calendarType),
+  [calendarType]
+);

+const memoizedSelectedCellStyle = useMemo(
+  () => getSelectedCellStyle(calendarType),
+  [calendarType]
+);

Don't forget to add the useMemo import:

-import { useEffect, useState } from 'react';
+import { useEffect, useState, useMemo } from 'react';
frontend/src/hooks/useDatePicker/index.ts (1)

34-52: Optimize date handling logic.

The date comparison logic could be simplified and utilize existing utility functions.

Consider this optimization:

 const handleDateSelect = (date: Date | null) => {
   if (!date) {
     setSelectedDate(null);
     return;
   }
-  const currentYear = baseDate.getFullYear();
-  const currentMonth = baseDate.getMonth();
-  const calendarDates = getDaysInMonth(baseDate);
-
-  const startOfCurrentMonth = new Date(currentYear, currentMonth, 1);
-  const endOfCurrentMonth = new Date(currentYear, currentMonth, calendarDates);
+  const { startOfMonth, endOfMonth } = getMonthBoundaries(baseDate);

-  if (date < startOfCurrentMonth) {
+  if (date < startOfMonth) {
     goToPrevMonth();
-  } else if (date > endOfCurrentMonth) {
+  } else if (date > endOfMonth) {
     goToNextMonth();
   }
   setSelectedDate(date);
 };
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ba29a0b2b87924ec14d7a8043aa07292c2d1bd4a and 1601ef30dc4a2efa5e74a0744f33b060f4b9dec0.

⛔ Files ignored due to path filters (1)
  • frontend/src/components/Icon/svg/arrow-left.svg is excluded by !**/*.svg
📒 Files selected for processing (35)
  • frontend/src/components/Calendar/Calendar.stories.ts (1 hunks)
  • frontend/src/components/DatePicker/DatePicker.stories.tsx (1 hunks)
  • frontend/src/components/DatePicker/DatePickerContext.ts (1 hunks)
  • frontend/src/components/DatePicker/Header/index.css.ts (1 hunks)
  • frontend/src/components/DatePicker/Header/index.tsx (1 hunks)
  • frontend/src/components/DatePicker/Table/Cell/CellWrapper.tsx (1 hunks)
  • frontend/src/components/DatePicker/Table/Cell/DateCell.tsx (1 hunks)
  • frontend/src/components/DatePicker/Table/Cell/DowCell.tsx (1 hunks)
  • frontend/src/components/DatePicker/Table/Cell/index.css.ts (1 hunks)
  • frontend/src/components/DatePicker/Table/Cell/index.ts (1 hunks)
  • frontend/src/components/DatePicker/Table/Highlight/HighlightBox.tsx (1 hunks)
  • frontend/src/components/DatePicker/Table/Highlight/HighlightGap.tsx (1 hunks)
  • frontend/src/components/DatePicker/Table/Highlight/index.css.ts (1 hunks)
  • frontend/src/components/DatePicker/Table/Highlight/index.ts (1 hunks)
  • frontend/src/components/DatePicker/Table/Row.tsx (1 hunks)
  • frontend/src/components/DatePicker/Table/RowContainer.tsx (1 hunks)
  • frontend/src/components/DatePicker/Table/TableBody.tsx (1 hunks)
  • frontend/src/components/DatePicker/Table/TableHeader.tsx (1 hunks)
  • frontend/src/components/DatePicker/Table/index.css.ts (1 hunks)
  • frontend/src/components/DatePicker/Table/index.tsx (1 hunks)
  • frontend/src/components/DatePicker/index.css.ts (1 hunks)
  • frontend/src/components/DatePicker/index.tsx (1 hunks)
  • frontend/src/components/Icon/component/ArrowLeft.tsx (1 hunks)
  • frontend/src/components/Input/Input.stories.tsx (2 hunks)
  • frontend/src/components/Input/MultiInput.tsx (4 hunks)
  • frontend/src/constants/date.ts (1 hunks)
  • frontend/src/hooks/useDatePicker/index.ts (1 hunks)
  • frontend/src/hooks/useDatePicker/useDateSelect.ts (1 hunks)
  • frontend/src/hooks/useDatePicker/useHighlightRange.ts (1 hunks)
  • frontend/src/hooks/useDatePicker/useMonthNavigation.ts (1 hunks)
  • frontend/src/utils/date/calendar/calendarGeneration.ts (1 hunks)
  • frontend/src/utils/date/calendar/index.ts (1 hunks)
  • frontend/src/utils/date/date.ts (3 hunks)
  • frontend/src/utils/date/index.ts (1 hunks)
  • frontend/src/utils/jsxUtils.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (27)
  • frontend/src/components/Calendar/Calendar.stories.ts
  • frontend/src/constants/date.ts
  • frontend/src/components/Input/Input.stories.tsx
  • frontend/src/components/DatePicker/Table/Highlight/HighlightBox.tsx
  • frontend/src/components/DatePicker/Table/TableBody.tsx
  • frontend/src/utils/date/index.ts
  • frontend/src/components/DatePicker/Table/RowContainer.tsx
  • frontend/src/components/DatePicker/Table/Cell/DowCell.tsx
  • frontend/src/components/DatePicker/Table/Highlight/index.ts
  • frontend/src/components/DatePicker/Header/index.css.ts
  • frontend/src/components/DatePicker/Table/index.tsx
  • frontend/src/components/DatePicker/Table/Highlight/index.css.ts
  • frontend/src/components/DatePicker/DatePickerContext.ts
  • frontend/src/components/DatePicker/Table/Highlight/HighlightGap.tsx
  • frontend/src/components/DatePicker/index.css.ts
  • frontend/src/hooks/useDatePicker/useMonthNavigation.ts
  • frontend/src/components/DatePicker/Table/Cell/index.ts
  • frontend/src/components/DatePicker/Header/index.tsx
  • frontend/src/hooks/useDatePicker/useHighlightRange.ts
  • frontend/src/hooks/useDatePicker/useDateSelect.ts
  • frontend/src/components/Input/MultiInput.tsx
  • frontend/src/components/DatePicker/Table/index.css.ts
  • frontend/src/components/Icon/component/ArrowLeft.tsx
  • frontend/src/components/DatePicker/DatePicker.stories.tsx
  • frontend/src/utils/date/calendar/calendarGeneration.ts
  • frontend/src/components/DatePicker/Table/Cell/index.css.ts
  • frontend/src/utils/date/calendar/index.ts
🧰 Additional context used
🪛 Biome (1.9.4)
frontend/src/components/DatePicker/Table/TableHeader.tsx

[error] 19-20: Missing key property for this element in iterable.

The order of the items may change, and having a key can help React identify which item was moved.
Check the React documentation.

(lint/correctness/useJsxKeyInIterable)

🔇 Additional comments (10)
frontend/src/components/DatePicker/Table/TableHeader.tsx (2)

1-14: LGTM!

The imports are well-organized, and the days array is cleanly structured with appropriate styles for different days of the week.


27-27: LGTM!

The export statement follows React conventions.

frontend/src/components/DatePicker/Table/Cell/CellWrapper.tsx (1)

14-14: Use clsx utility for className concatenation.

Replace string concatenation with the clsx utility as previously suggested.

-    className={`${cellWrapperStyle} ${className}`}
+    className={clsx(cellWrapperStyle, className)}
frontend/src/utils/date/date.ts (1)

152-153: LGTM! Clean implementation of day checks.

The new utility functions with constants improve code readability and maintainability.

frontend/src/utils/jsxUtils.tsx (1)

4-16: Verify the integration with DatePicker component.

Since this utility is being introduced alongside the DatePicker component, let's verify its usage:

frontend/src/components/DatePicker/index.tsx (2)

1-20: LGTM! Well-structured type definitions and imports.

The type definitions are clear and properly exported, with good separation of concerns between style props and component props.


23-36: LGTM! Clean implementation with proper context usage.

The component follows React best practices by using hooks for state management and context for prop drilling prevention.

frontend/src/hooks/useDatePicker/index.ts (3)

1-20: LGTM! Well-structured interface and imports.

The interface clearly defines the hook's return type with all necessary properties and methods.


22-33: Consider computing calendarDates on-the-fly.

As suggested in the previous review, calendarDates could be computed when needed instead of being stored in state.

Apply this refactor:

-  const [calendarDates, setCalendarDates] = useState<Date[][]>(
-    generateMonthCalendar(baseDate),
-  );
+  const calendarDates = useMemo(
+    () => generateMonthCalendar(baseDate),
+    [baseDate]
+  );

-  useEffect(() => {
-    setCalendarDates(generateMonthCalendar(baseDate));
-  }, [baseDate, selectedDate, highlightRange]);

54-58: LGTM! Well-structured return value.

The hook returns all necessary values and methods as defined in the interface.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (4)
frontend/src/components/DatePicker/Table/TableHeader.tsx (1)

6-14: Consider moving the days array to a constants file.

The days array is well-structured, but consider moving it to a separate constants file (e.g., constants/calendar.ts) for better maintainability and potential reuse across other calendar-related components.

frontend/src/components/DatePicker/Table/Cell/DateCell.tsx (3)

47-47: Address TODO: Improve cursor style handling.

The TODO comment suggests finding a better way to handle cursor styles and controls. I can help implement a solution.

Would you like me to propose a solution that encapsulates cursor style and control logic into a reusable hook or utility function?


80-80: Address TODO: Add holiday check.

The TODO comment indicates that holiday checking functionality needs to be implemented.

Would you like me to help implement a holiday checking utility that can be integrated with the date cell type determination?


89-103: Simplify switch case by removing redundant case.

The 'weekday' case is redundant since it returns the same value as the default case.

 switch (dateCellType) {
   case 'selected':
     return selectedCellStyle;
   case 'today':
     return todayCellStyle;
   case 'otherMonth':
     return otherMonthCellStyle;
   case 'holiday':
     return holidayCellStyle;
   case 'saturday':
     return saturdayCellStyle;
-  case 'weekday':
   default:
     return weekdayCellStyle;
 }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1601ef30dc4a2efa5e74a0744f33b060f4b9dec0 and 1cde2bbfa3595e4482a8f6f5a9952e81f0aa0a91.

📒 Files selected for processing (8)
  • frontend/src/components/DatePicker/Header/index.tsx (1 hunks)
  • frontend/src/components/DatePicker/Table/Cell/CellWrapper.tsx (1 hunks)
  • frontend/src/components/DatePicker/Table/Cell/DateCell.tsx (1 hunks)
  • frontend/src/components/DatePicker/Table/Cell/DowCell.tsx (1 hunks)
  • frontend/src/components/DatePicker/Table/Cell/index.css.ts (1 hunks)
  • frontend/src/components/DatePicker/Table/TableBody.tsx (1 hunks)
  • frontend/src/components/DatePicker/Table/TableHeader.tsx (1 hunks)
  • frontend/src/hooks/useDatePicker/useDateSelect.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • frontend/src/components/DatePicker/Table/TableBody.tsx
  • frontend/src/components/DatePicker/Header/index.tsx
  • frontend/src/components/DatePicker/Table/Cell/DowCell.tsx
🧰 Additional context used
🪛 Biome (1.9.4)
frontend/src/components/DatePicker/Table/Cell/DateCell.tsx

[error] 102-104: Useless case clause.

because the default clause is present:

Unsafe fix: Remove the useless case.

(lint/complexity/noUselessSwitchCase)

🔇 Additional comments (5)
frontend/src/components/DatePicker/Table/TableHeader.tsx (3)

1-4: LGTM! Well-organized imports.

The imports are properly organized, following consistent naming conventions and clear scoping.


16-26: LGTM! Clean implementation with proper key usage.

The component is well-structured with proper key placement on the outer div, addressing the previous review comment about React reconciliation.


27-27: LGTM! Proper export statement.

The default export is appropriate for this main component.

frontend/src/components/DatePicker/Table/Cell/CellWrapper.tsx (1)

21-21: Use clsx utility for className concatenation.

Replace string template literal with clsx utility for better className handling.

-    className={`${cellWrapperStyle({ cursorType })} ${className}`}
+    className={clsx(cellWrapperStyle({ cursorType }), className)}
frontend/src/hooks/useDatePicker/useDateSelect.ts (1)

26-38: Extract reset logic into a separate function.

The reset logic is duplicated and could be extracted into a reusable function.

+  const resetSelection = () => {
+    handleDateSelect(null);
+    setHighlightStart(null);
+    setHighlightEnd(null);
+  };
+
   if (isSameDate(date, highlightRange.start)) {
-    handleDateSelect(null);
-    setHighlightStart(null);
+    resetSelection();
     return;
   }
   
   setHighlightEnd(date);
   return;
 }
- handleDateSelect(null);
- setHighlightStart(null);
- setHighlightEnd(null);
+ resetSelection();

@dioo1461 dioo1461 force-pushed the feature/fe/date-picker-component branch from 1cde2bb to aa95f5b Compare February 6, 2025 10:05
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Nitpick comments (14)
frontend/src/components/DatePicker/Table/TableHeader.tsx (1)

6-14: Consider adding i18n support for day labels.

The day labels are currently hardcoded in Korean. For better maintainability and potential internationalization, consider using an i18n library.

Example implementation using i18n:

+import { useTranslation } from 'react-i18next';

 const days = [
-  { label: '일', style: holidayCellStyle },
-  { label: '월', style: weekdayCellStyle },
-  { label: '화', style: weekdayCellStyle },
-  { label: '수', style: weekdayCellStyle },
-  { label: '목', style: weekdayCellStyle },
-  { label: '금', style: weekdayCellStyle },
-  { label: '토', style: saturdayCellStyle },
+  { label: 'days.sun', style: holidayCellStyle },
+  { label: 'days.mon', style: weekdayCellStyle },
+  { label: 'days.tue', style: weekdayCellStyle },
+  { label: 'days.wed', style: weekdayCellStyle },
+  { label: 'days.thu', style: weekdayCellStyle },
+  { label: 'days.fri', style: weekdayCellStyle },
+  { label: 'days.sat', style: saturdayCellStyle },
 ];

 const TableHeader = () => {
+  const { t } = useTranslation();
   return (
     <RowContainer>
-      {days.map(({ label, style }, i) => (
+      {days.map(({ label, style }, i) => (
         <div className={dowCellWrapperStyle} key={`${label}-${i}`}>
-          <DowCell className={style}>{label}</DowCell>
+          <DowCell className={style}>{t(label)}</DowCell>
         </div>
       ))}
     </RowContainer>
   );
 };
frontend/src/components/DatePicker/Table/Cell/DateCell.tsx (4)

47-47: Track the TODO comment.

The TODO comment about cell cursor style control should be tracked in the issue tracker.

Would you like me to create an issue to track this TODO item?


49-56: Simplify cursor style logic.

The cursor style logic can be simplified by moving it to CSS using a class-based approach.

-      cursorType={calendarType === 'range' && dateCellType === 'otherMonth' 
-        ? 'not-allowed' 
-        : 'pointer'}
+      className={`${getDateCellStyle(dateCellType)} ${
+        calendarType === 'range' && dateCellType === 'otherMonth'
+          ? 'cursor-not-allowed'
+          : 'cursor-pointer'
+      }`}
       onClick={handleDateCellClick}
       style={inlineCellStyles}

80-80: Track the TODO comment.

The TODO comment about holiday checking should be tracked in the issue tracker.

Would you like me to create an issue to track this TODO item about holiday checking implementation?


64-84: Add JSDoc documentation for helper functions.

Consider adding JSDoc documentation to describe the purpose and parameters of the getDateCellType function.

+/**
+ * Determines the type of date cell based on selection status, calendar type, and date comparisons.
+ * @param date - The date to check
+ * @param calendarType - The type of calendar ('select' or 'range')
+ * @param selected - Whether the date is selected
+ * @param baseDate - The current month's base date
+ * @param highlightState - The highlight state for range selection
+ * @returns The type of the date cell
+ */
const getDateCellType = (
  date: Date,
  calendarType: CalendarType,
  selected: boolean,
  baseDate: Date,
  highlightState: HighlightState,
): DateCellType => {
frontend/src/components/DatePicker/index.tsx (1)

21-21: Address the TODO comment for container style injection.

The TODO comment indicates that container styles should be injectable from outside. This aligns with the PR objectives mentioning potential refactoring for better external component interaction.

Would you like me to help implement the container style injection feature? I can propose a solution that allows external style customization while maintaining default styles.

frontend/src/utils/date/calendar/index.ts (4)

1-4: Add JSDoc comments for calendar constants.

Consider adding JSDoc comments to explain the purpose and usage of these constants, especially for FIRST_MONTH and LAST_MONTH which use 0-based indexing.

+/**
+ * Number of days in a week.
+ */
 export const WEEK_DAYS = 7;
+/**
+ * First month of the year (0-based index, represents January).
+ */
 export const FIRST_MONTH = 0;
+/**
+ * Last month of the year (0-based index, represents December).
+ */
 export const LAST_MONTH = FIRST_MONTH + 11;
+/**
+ * First day of the month.
+ */
 export const FIRST_DAY = 1;

19-26: Add explicit return type for getPrevMonthInfo.

Consider adding an explicit return type for better type safety and documentation.

-export const getPrevMonthInfo = (date: Date) => {
+export const getPrevMonthInfo = (date: Date): { month: number; year: number } => {

33-40: Refactor month navigation logic to reduce duplication.

The logic in getNextMonthInfo is very similar to getPrevMonthInfo. Consider creating a shared utility function.

+type MonthInfo = { month: number; year: number };
+
+const getMonthInfo = (date: Date, offset: number): MonthInfo => {
+  const currentMonth = date.getMonth();
+  const currentYear = date.getFullYear();
+  
+  if ((offset === -1 && currentMonth === FIRST_MONTH) || 
+      (offset === 1 && currentMonth === LAST_MONTH)) {
+    return {
+      month: offset === -1 ? LAST_MONTH : FIRST_MONTH,
+      year: currentYear + offset
+    };
+  }
+  return { month: currentMonth + offset, year: currentYear };
+};
+
+export const getPrevMonthInfo = (date: Date): MonthInfo => 
+  getMonthInfo(date, -1);
+
+export const getNextMonthInfo = (date: Date): MonthInfo => 
+  getMonthInfo(date, 1);

42-46: Add JSDoc and explicit return type for getDateParts.

Consider adding documentation and an explicit return type for better maintainability.

+/**
+ * Extracts year, month (0-based), and day from a Date object.
+ * @param date - The date to extract parts from.
+ * @returns An object containing the year, month, and day.
+ */
-export const getDateParts = (date: Date) => ({
+export const getDateParts = (date: Date): { year: number; month: number; day: number } => ({
frontend/src/utils/date/calendar/calendarGeneration.ts (4)

14-20: Optimize array partitioning using Array.from.

Consider using Array.from for a more concise and potentially more efficient implementation.

-const partitionArray = <T>(array: T[], size: number): T[][] => {
-  const chunks: T[][] = [];
-  for (let i = 0; i < array.length; i += size) {
-    chunks.push(array.slice(i, i + size));
-  }
-  return chunks;
-};
+const partitionArray = <T>(array: T[], size: number): T[][] =>
+  Array.from({ length: Math.ceil(array.length / size) }, (_, i) =>
+    array.slice(i * size, i * size + size)
+  );

36-47: Improve variable naming for better readability.

Consider using more descriptive variable names to better convey their purpose.

 const generatePreviousMonthDays = (baseDate: Date): Date[] => {
   const { prevDate, prevMonthDays } = getPreviousMonthInfo(baseDate);
-  const firstDayDow = new Date(baseDate.getFullYear(), baseDate.getMonth(), FIRST_DAY)
+  const firstDayOfWeek = new Date(baseDate.getFullYear(), baseDate.getMonth(), FIRST_DAY)
     .getDay();

   const dates: Date[] = [];
-  for (let i = firstDayDow; i > 0; i--) {
+  for (let daysBeforeStart = firstDayOfWeek; daysBeforeStart > 0; daysBeforeStart--) {
     dates.push(
       new Date(
         prevDate.getFullYear(),
         prevDate.getMonth(),
-        prevMonthDays - i + 1
+        prevMonthDays - daysBeforeStart + 1
       )
     );
   }
   return dates;
 };

52-62: Consider using Array.from for a more functional approach.

The current implementation uses imperative style. Consider using Array.from for a more functional approach.

-const generateCurrentMonthDays = (baseDate: Date): Date[] => {
-  const totalDays = getDaysInMonth(baseDate);
-  const year = baseDate.getFullYear();
-  const month = baseDate.getMonth();
-  
-  const dates: Date[] = [];
-  for (let day = FIRST_DAY; day <= totalDays; day++) {
-    dates.push(new Date(year, month, day));
-  }
-  return dates;
-};
+const generateCurrentMonthDays = (baseDate: Date): Date[] => {
+  const totalDays = getDaysInMonth(baseDate);
+  const year = baseDate.getFullYear();
+  const month = baseDate.getMonth();
+  
+  return Array.from(
+    { length: totalDays },
+    (_, index) => new Date(year, month, index + FIRST_DAY)
+  );
+};

96-103: Add type annotations for better code clarity.

Consider adding type annotations to intermediate variables for better code clarity and maintainability.

 export const generateMonthCalendar = (baseDate: Date): Date[][] => {
-  const previousDays = generatePreviousMonthDays(baseDate);
-  const currentDays = generateCurrentMonthDays(baseDate);
-  const combinedDays = [...previousDays, ...currentDays];
-  const nextDays = generateNextMonthDays(combinedDays.length, baseDate);
-  const daysArray = [...combinedDays, ...nextDays];
+  const previousDays: Date[] = generatePreviousMonthDays(baseDate);
+  const currentDays: Date[] = generateCurrentMonthDays(baseDate);
+  const combinedDays: Date[] = [...previousDays, ...currentDays];
+  const nextDays: Date[] = generateNextMonthDays(combinedDays.length, baseDate);
+  const daysArray: Date[] = [...combinedDays, ...nextDays];
   return partitionArray(daysArray, WEEK_DAYS);
 };
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1cde2bbfa3595e4482a8f6f5a9952e81f0aa0a91 and aa95f5b.

⛔ Files ignored due to path filters (1)
  • frontend/src/components/Icon/svg/arrow-left.svg is excluded by !**/*.svg
📒 Files selected for processing (33)
  • frontend/src/components/Avatar/index.css.ts (1 hunks)
  • frontend/src/components/Calendar/Calendar.stories.ts (1 hunks)
  • frontend/src/components/DatePicker/DatePicker.stories.tsx (1 hunks)
  • frontend/src/components/DatePicker/DatePickerContext.ts (1 hunks)
  • frontend/src/components/DatePicker/Header/index.css.ts (1 hunks)
  • frontend/src/components/DatePicker/Header/index.tsx (1 hunks)
  • frontend/src/components/DatePicker/Table/Cell/CellWrapper.tsx (1 hunks)
  • frontend/src/components/DatePicker/Table/Cell/DateCell.tsx (1 hunks)
  • frontend/src/components/DatePicker/Table/Cell/DowCell.tsx (1 hunks)
  • frontend/src/components/DatePicker/Table/Cell/index.css.ts (1 hunks)
  • frontend/src/components/DatePicker/Table/Cell/index.ts (1 hunks)
  • frontend/src/components/DatePicker/Table/Highlight/HighlightBox.tsx (1 hunks)
  • frontend/src/components/DatePicker/Table/Highlight/HighlightGap.tsx (1 hunks)
  • frontend/src/components/DatePicker/Table/Highlight/index.css.ts (1 hunks)
  • frontend/src/components/DatePicker/Table/Highlight/index.ts (1 hunks)
  • frontend/src/components/DatePicker/Table/Row.tsx (1 hunks)
  • frontend/src/components/DatePicker/Table/RowContainer.tsx (1 hunks)
  • frontend/src/components/DatePicker/Table/TableBody.tsx (1 hunks)
  • frontend/src/components/DatePicker/Table/TableHeader.tsx (1 hunks)
  • frontend/src/components/DatePicker/Table/index.css.ts (1 hunks)
  • frontend/src/components/DatePicker/Table/index.tsx (1 hunks)
  • frontend/src/components/DatePicker/index.css.ts (1 hunks)
  • frontend/src/components/DatePicker/index.tsx (1 hunks)
  • frontend/src/components/Icon/component/ArrowLeft.tsx (1 hunks)
  • frontend/src/constants/date.ts (1 hunks)
  • frontend/src/hooks/useDatePicker/index.ts (1 hunks)
  • frontend/src/hooks/useDatePicker/useDateSelect.ts (1 hunks)
  • frontend/src/hooks/useDatePicker/useHighlightRange.ts (1 hunks)
  • frontend/src/hooks/useDatePicker/useMonthNavigation.ts (1 hunks)
  • frontend/src/utils/date/calendar/calendarGeneration.ts (1 hunks)
  • frontend/src/utils/date/calendar/index.ts (1 hunks)
  • frontend/src/utils/date/date.ts (3 hunks)
  • frontend/src/utils/date/index.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (27)
  • frontend/src/constants/date.ts
  • frontend/src/utils/date/index.ts
  • frontend/src/components/Calendar/Calendar.stories.ts
  • frontend/src/components/DatePicker/Table/Highlight/index.ts
  • frontend/src/components/DatePicker/index.css.ts
  • frontend/src/components/DatePicker/Table/Highlight/HighlightGap.tsx
  • frontend/src/components/DatePicker/Table/Highlight/HighlightBox.tsx
  • frontend/src/components/DatePicker/Table/Highlight/index.css.ts
  • frontend/src/components/DatePicker/Header/index.css.ts
  • frontend/src/components/DatePicker/Table/index.tsx
  • frontend/src/components/DatePicker/DatePickerContext.ts
  • frontend/src/components/DatePicker/Table/RowContainer.tsx
  • frontend/src/components/DatePicker/DatePicker.stories.tsx
  • frontend/src/components/DatePicker/Table/Cell/index.ts
  • frontend/src/components/Icon/component/ArrowLeft.tsx
  • frontend/src/utils/date/date.ts
  • frontend/src/components/DatePicker/Header/index.tsx
  • frontend/src/components/DatePicker/Table/Cell/DowCell.tsx
  • frontend/src/components/DatePicker/Table/Cell/CellWrapper.tsx
  • frontend/src/hooks/useDatePicker/useDateSelect.ts
  • frontend/src/hooks/useDatePicker/useMonthNavigation.ts
  • frontend/src/components/DatePicker/Table/TableBody.tsx
  • frontend/src/components/DatePicker/Table/index.css.ts
  • frontend/src/components/DatePicker/Table/Cell/index.css.ts
  • frontend/src/components/DatePicker/Table/Row.tsx
  • frontend/src/hooks/useDatePicker/useHighlightRange.ts
  • frontend/src/hooks/useDatePicker/index.ts
🧰 Additional context used
🪛 Biome (1.9.4)
frontend/src/components/DatePicker/Table/Cell/DateCell.tsx

[error] 102-104: Useless case clause.

because the default clause is present:

Unsafe fix: Remove the useless case.

(lint/complexity/noUselessSwitchCase)

🔇 Additional comments (6)
frontend/src/components/DatePicker/Table/TableHeader.tsx (3)

1-4: LGTM! Clean and well-organized imports.

The imports are properly structured, using CSS modules for styling and following good practices for component imports.


16-26: LGTM! Clean and efficient implementation.

The component follows React best practices with proper key placement and efficient rendering.


27-27: LGTM! Proper export statement.

The default export is appropriately used for the main component.

frontend/src/components/DatePicker/Table/Cell/DateCell.tsx (2)

1-26: LGTM!

The imports are well-organized and the DateCellProps interface is well-defined with clear prop types.


34-39: Consider moving styles to CSS.

As discussed in the previous review, since these styles won't change at runtime, they should be defined in CSS rather than using inline styles.

frontend/src/components/Avatar/index.css.ts (1)

6-9: LGTM!

The container style implementation is clean and follows best practices.

Comment on lines +13 to +15
backgroundColor: vars.color.Ref.Netural['White'],
borderRadius: vars.radius['Max'],
border: `2px solid ${vars.color.Ref.Netural[500]}`,
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix typo in color variable name.

There's a typo in the color variable name: "Netural" should be "Neutral".

Apply this diff to fix the typo:

-    backgroundColor: vars.color.Ref.Netural['White'],
-    borderRadius: vars.radius['Max'],
-    border: `2px solid ${vars.color.Ref.Netural[500]}`,
+    backgroundColor: vars.color.Ref.Neutral['White'],
+    borderRadius: vars.radius['Max'],
+    border: `2px solid ${vars.color.Ref.Neutral[500]}`,

-    backgroundColor: vars.color.Ref.Netural['White'],
-    color: vars.color.Ref.Netural[500],
-    border: `2px solid ${vars.color.Ref.Netural[100]}`,
+    backgroundColor: vars.color.Ref.Neutral['White'],
+    color: vars.color.Ref.Neutral[500],
+    border: `2px solid ${vars.color.Ref.Neutral[100]}`,

Also applies to: 44-46

Comment on lines +38 to +44
const getTodayCellStyle = (calendarType: CalendarType) => calendarType === 'select'
? { backgroundColor: vars.color.Ref.Primary[500], color: vars.color.Ref.Netural['White'] }
: { };

const getSelectedCellStyle = (calendarType: CalendarType) => calendarType === 'select'
? { backgroundColor: vars.color.Ref.Primary[100], color: vars.color.Ref.Primary[500] }
: { backgroundColor: vars.color.Ref.Primary[500], color: vars.color.Ref.Netural['White'] };
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix typo in color variable name.

There's a typo in the color variable name: "Netural" should be "Neutral".

Apply this diff to fix the typo:

-  ? { backgroundColor: vars.color.Ref.Primary[500], color: vars.color.Ref.Netural['White'] }
+  ? { backgroundColor: vars.color.Ref.Primary[500], color: vars.color.Ref.Neutral['White'] }

-  ? { backgroundColor: vars.color.Ref.Primary[100], color: vars.color.Ref.Primary[500] }
-  : { backgroundColor: vars.color.Ref.Primary[500], color: vars.color.Ref.Netural['White'] };
+  ? { backgroundColor: vars.color.Ref.Primary[100], color: vars.color.Ref.Primary[500] }
+  : { backgroundColor: vars.color.Ref.Primary[500], color: vars.color.Ref.Neutral['White'] };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const getTodayCellStyle = (calendarType: CalendarType) => calendarType === 'select'
? { backgroundColor: vars.color.Ref.Primary[500], color: vars.color.Ref.Netural['White'] }
: { };
const getSelectedCellStyle = (calendarType: CalendarType) => calendarType === 'select'
? { backgroundColor: vars.color.Ref.Primary[100], color: vars.color.Ref.Primary[500] }
: { backgroundColor: vars.color.Ref.Primary[500], color: vars.color.Ref.Netural['White'] };
const getTodayCellStyle = (calendarType: CalendarType) => calendarType === 'select'
? { backgroundColor: vars.color.Ref.Primary[500], color: vars.color.Ref.Neutral['White'] }
: { };
const getSelectedCellStyle = (calendarType: CalendarType) => calendarType === 'select'
? { backgroundColor: vars.color.Ref.Primary[100], color: vars.color.Ref.Primary[500] }
: { backgroundColor: vars.color.Ref.Primary[500], color: vars.color.Ref.Neutral['White'] };

Comment on lines +6 to +12
/**
* 해당 달의 일수를 반환합니다.
* @param date - 기준 날짜.
* @returns 해당 달의 일수.
*/
export const getDaysInMonth = (date: Date): number =>
new Date(date.getFullYear(), date.getMonth() + 1, 0).getDate();
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Remove duplicate implementation of getDaysInMonth.

This function is duplicated in calendarGeneration.ts. Consider importing it from this file to maintain DRY principles.

Comment on lines +69 to +73
const getNextMonthInfo = (baseDate: Date) => {
const nextDate = new Date(baseDate);
nextDate.setMonth(baseDate.getMonth() + 1);
return { nextDate, prevMonthDays: getDaysInMonth(nextDate) };
};
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix bugs in getNextMonthInfo.

The function has several issues:

  1. It mutates the date object
  2. Uses incorrect property name ('prevMonthDays' instead of 'nextMonthDays')
  3. Differs from index.ts implementation
-const getNextMonthInfo = (baseDate: Date) => {
-  const nextDate = new Date(baseDate);
-  nextDate.setMonth(baseDate.getMonth() + 1);
-  return { nextDate, prevMonthDays: getDaysInMonth(nextDate) };
-};
+const getNextMonthInfo = (baseDate: Date) => {
+  const { month, year } = getNextMonthInfo(baseDate);
+  const nextDate = new Date(year, month);
+  return { nextDate, nextMonthDays: getDaysInMonth(nextDate) };
+};

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +27 to +31
const getPreviousMonthInfo = (date: Date) => {
const prevDate = new Date(date);
prevDate.setMonth(date.getMonth() - 1);
return { prevDate, prevMonthDays: getDaysInMonth(prevDate) };
};
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Avoid date mutation and align with index.ts implementation.

The current implementation mutates the date object and differs from getPrevMonthInfo in index.ts. Consider using the existing implementation.

-const getPreviousMonthInfo = (date: Date) => {
-  const prevDate = new Date(date);
-  prevDate.setMonth(date.getMonth() - 1);
-  return { prevDate, prevMonthDays: getDaysInMonth(prevDate) };
-};
+const getPreviousMonthInfo = (date: Date) => {
+  const { month, year } = getPrevMonthInfo(date);
+  const prevDate = new Date(year, month);
+  return { prevDate, prevMonthDays: getDaysInMonth(prevDate) };
+};
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const getPreviousMonthInfo = (date: Date) => {
const prevDate = new Date(date);
prevDate.setMonth(date.getMonth() - 1);
return { prevDate, prevMonthDays: getDaysInMonth(prevDate) };
};
const getPreviousMonthInfo = (date: Date) => {
const { month, year } = getPrevMonthInfo(date);
const prevDate = new Date(year, month);
return { prevDate, prevMonthDays: getDaysInMonth(prevDate) };
};

@dioo1461 dioo1461 requested a review from hamo-o February 6, 2025 10:10
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (3)
frontend/src/components/DatePicker/Table/Cell/CellWrapper.tsx (3)

2-7: Consider using barrel exports for better import organization.

The relative import path for the Text component (../../../Text) is quite deep. Consider creating an index.ts barrel file in the components directory to simplify imports.

-import { Text } from '../../../Text';
+import { Text } from '@/components';

9-14: Use CSSProperties type for better type safety.

The style prop is typed as object which is too generic. Consider using React.CSSProperties for better type safety and IDE support.

+import type { PropsWithChildren, CSSProperties } from 'react';

 interface CellWrapperProps extends PropsWithChildren {
   cursorType: 'default' | 'pointer' | 'not-allowed';
   className?: string;
-  style?: object;
+  style?: CSSProperties;
   onClick?: () => void;
 }

15-29: Consider memoizing the component for better performance.

Since this is a purely presentational component that receives simple props, wrapping it with React.memo could help prevent unnecessary re-renders.

-export const CellWrapper = ({ 
+export const CellWrapper = React.memo(({ 
   className,
   cursorType, 
   style,
   onClick,
   children,
-}: CellWrapperProps) => (
+}: CellWrapperProps) => (
   <div
     className={clsx(cellWrapperStyle({ cursorType }), className )}
     onClick={onClick}
     style={style}
   >
     <Text typo='caption'>{children}</Text>
   </div>
-);
+));
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between aa95f5b and 0a3bfd7.

📒 Files selected for processing (1)
  • frontend/src/components/DatePicker/Table/Cell/CellWrapper.tsx (1 hunks)
🔇 Additional comments (1)
frontend/src/components/DatePicker/Table/Cell/CellWrapper.tsx (1)

31-31: LGTM!

The default export is appropriate for this single-component file.

Copy link
Contributor

@hamo-o hamo-o left a comment

Choose a reason for hiding this comment

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

수고하셨습니다! 캘린더쪽 페이지 구현하면서 어떻게 추상화할지 같이 이야기(+추가 리팩터링)해봐요!!

@dioo1461 dioo1461 merged commit 1b01f0a into dev Feb 6, 2025
2 checks passed
@dioo1461 dioo1461 deleted the feature/fe/date-picker-component branch February 6, 2025 15:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🖥️ FE Frontend

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature] Date Picker 구현

3 participants