Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FE] 추천 로직 및 UI 개선 #448

Open
wants to merge 6 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import type { Meta, StoryObj } from '@storybook/react';

import type { MeetingRecommend } from '@apis/meetings/recommends';

import MeetingRecommendCard from './MeetingRecommendCard';

const DEFAULT_SCHEDULE: MeetingRecommend = {
rank: '1',
startDate: '2024-07-15',
startDayOfWeek: '월',
startTime: '20:00',
endDate: '2024-07-15',
endDayOfWeek: '월',
endTime: '21:00',
attendeeNames: ['다온', '마크', '해리', '낙타', '빙봉', '재즈', '배키', '페드로'],
};

const meta: Meta<typeof MeetingRecommendCard> = {
title: 'MeetingRecommendCard/Default',
component: MeetingRecommendCard,
parameters: {
layout: 'centered',
},
decorators: (Story) => (
<div css={{ width: '32rem' }}>
<Story />
</div>
),
};

export default meta;

type Story = StoryObj<typeof meta>;

export const DateTime: Story = {
args: {
schedule: DEFAULT_SCHEDULE,
totalAttendees: DEFAULT_SCHEDULE.attendeeNames,
type: 'DATETIME',
},
};

export const DaysOnly: Story = {
args: {
schedule: DEFAULT_SCHEDULE,
totalAttendees: DEFAULT_SCHEDULE.attendeeNames,
type: 'DAYSONLY',
},
};
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ export const s_baseContainer = css`
border-radius: 0.8rem;
`;

export const s_recommendContainer = css`
export const s_scheduleContainer = css`
display: flex;
flex-direction: column;
gap: 1.2rem;
gap: 0.8rem;
`;

export const s_optionContainer = css`
Expand All @@ -39,16 +39,6 @@ export const s_getSelectedStyle = (isSelected: boolean) => css`
`};
`;

export const s_attendeeInfo = css`
color: #6cd929;
text-align: start;
${theme.typography.captionBold}
`;

export const s_dateInfo = css`
${theme.typography.bodyBold}
`;

export const s_checkboxContainer = css`
display: flex;
align-items: center;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import type { MeetingType } from '@apis/meetings/meetings';
import type { MeetingRecommend } from '@apis/meetings/recommends';

import { s_baseContainer, s_scheduleContainer } from './MeetingRecommendCard.styles';
import RecommendAttendees from './RecommendAttendees';
import RecommendDateTime from './RecommendSchedule/RecommendDateTime';
import RecommendDaysOnly from './RecommendSchedule/RecommendDaysOnly';

export interface DateInfo {
fullDate: string;
time: string;
dayOfWeek: string;
}

export interface RecommendCardProps {
type: MeetingType;
totalAttendees: string[];
schedule: MeetingRecommend;
}

export default function MeetingRecommendCard({
type,
schedule,
totalAttendees,
}: RecommendCardProps) {
const renderRecommendCard = (type: MeetingType) => {
switch (type) {
case 'DATETIME':
return <RecommendDateTime schedule={schedule} />;
case 'DAYSONLY':
return <RecommendDaysOnly schedule={schedule} />;
}
};

return (
<div css={s_baseContainer}>
<div css={s_scheduleContainer}>
<RecommendAttendees
totalAttendees={totalAttendees}
recommendAttendees={schedule.attendeeNames}
/>
{renderRecommendCard(type)}
</div>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import type { Meta, StoryObj } from '@storybook/react';
import { useState } from 'storybook/internal/preview-api';

import type { MeetingRecommend } from '@apis/meetings/recommends';

import MeetingRecommendCheckboxCard from './MeetingRecommendCheckboxCard';

const DEFAULT_SCHEDULE: MeetingRecommend = {
rank: '1',
startDate: '2024-07-15',
startDayOfWeek: '월',
startTime: '20:00',
endDate: '2024-07-15',
endDayOfWeek: '월',
endTime: '21:00',
attendeeNames: ['다온', '마크', '해리', '낙타', '빙봉', '재즈', '배키', '페드로'],
};

const meta: Meta<typeof MeetingRecommendCheckboxCard> = {
title: 'MeetingRecommendCard/Checkbox',
component: MeetingRecommendCheckboxCard,
parameters: {
layout: 'centered',
},
args: {
schedule: DEFAULT_SCHEDULE,
totalAttendees: DEFAULT_SCHEDULE.attendeeNames,
},
decorators: (Story) => (
<div css={{ width: '32rem' }}>
<Story />
</div>
),
};

export default meta;

type Story = StoryObj<typeof MeetingRecommendCheckboxCard>;

export const DateTime: Story = {
render: (args) => {
const [isSelect, setIsSelect] = useState(false);

const onToggle = () => {
setIsSelect((prevIsSelected) => !prevIsSelected);
};

return (
<MeetingRecommendCheckboxCard
{...args}
type="DATETIME"
isSelected={isSelect}
onSelect={onToggle}
/>
);
},
};

export const DaysOnly: Story = {
render: (args) => {
const [isSelect, setIsSelect] = useState(false);

const onToggle = () => {
setIsSelect((prevIsSelected) => !prevIsSelected);
};

return (
<MeetingRecommendCheckboxCard
{...args}
type="DAYSONLY"
isSelected={isSelect}
onSelect={onToggle}
/>
);
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import type { MeetingType } from '@apis/meetings/meetings';

import type { RecommendCardProps } from './MeetingRecommendCard';
import {
s_baseContainer,
s_checkboxContainer,
s_checkboxInput,
s_getSelectedStyle,
s_optionContainer,
s_scheduleContainer,
} from './MeetingRecommendCard.styles';
import RecommendAttendees from './RecommendAttendees';
import RecommendCardDateTime from './RecommendSchedule/RecommendDateTime';
import RecommendCardDaysOnly from './RecommendSchedule/RecommendDaysOnly';

export interface MeetingRecommendCheckboxProps extends RecommendCardProps {
isSelected: boolean;
onSelect: () => void;
}

export default function MeetingRecommendCheckboxCard({
type,
isSelected,
schedule,
totalAttendees,
onSelect,
}: MeetingRecommendCheckboxProps) {
const renderRecommendCard = (type: MeetingType) => {
switch (type) {
case 'DATETIME':
return <RecommendCardDateTime schedule={schedule} />;
case 'DAYSONLY':
return <RecommendCardDaysOnly schedule={schedule} />;
}
};

return (
<button
css={[s_baseContainer, s_optionContainer, s_getSelectedStyle(isSelected)]}
onClick={onSelect}
>
<div css={s_scheduleContainer}>
<RecommendAttendees
totalAttendees={totalAttendees}
recommendAttendees={schedule.attendeeNames}
/>
{renderRecommendCard(type)}
</div>
<div css={s_checkboxContainer}>
<input type="checkbox" checked={isSelected} onChange={onSelect} css={s_checkboxInput} />
</div>
</button>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { s_attendeeInfo } from './RecommendSchedule/RecommendSchedule.style';

interface RecommendAttendeesProps {
totalAttendees: string[];
recommendAttendees: string[];
}

export default function RecommendAttendees({
totalAttendees,
recommendAttendees,
}: RecommendAttendeesProps) {
return (
<span
css={s_attendeeInfo}
>{`${totalAttendees.length}명 중 ${recommendAttendees.length}명`}</span>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import type { MeetingRecommend } from '@apis/meetings/recommends';

import { formatFullDate, formatTimeToKorean } from '@utils/date';

import { s_dateInfo, s_recommendContainer } from './RecommendSchedule.style';

interface RecommendDateTimeProps {
schedule: MeetingRecommend;
}

export default function RecommendDateTime({ schedule }: RecommendDateTimeProps) {
const { startDate, startDayOfWeek, startTime, endDate, endDayOfWeek, endTime } = schedule;

const startRecommendDate = formatFullDate({
fullDate: startDate,
dayOfWeek: startDayOfWeek,
format: 'korean',
});

const endRecommendDate = formatFullDate({
fullDate: endDate,
dayOfWeek: endDayOfWeek,
format: 'korean',
});

return (
<div css={s_recommendContainer}>
<span css={s_dateInfo}>{`${startRecommendDate} ${formatTimeToKorean(startTime)}`}부터</span>
<span css={s_dateInfo}>{`${endRecommendDate} ${formatTimeToKorean(endTime)}`}까지</span>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { Fragment } from 'react';

import type { MeetingRecommend } from '@apis/meetings/recommends';

import { formatFullDate, isSameDate } from '@utils/date';

import { s_dateInfo, s_recommendContainer } from './RecommendSchedule.style';

interface RecommendDaysOnlyProps {
schedule: MeetingRecommend;
}

export default function RecommendDaysOnly({ schedule }: RecommendDaysOnlyProps) {
const { startDate, startDayOfWeek, endDate, endDayOfWeek } = schedule;

const startRecommendDate = formatFullDate({
fullDate: startDate,
dayOfWeek: startDayOfWeek,
format: 'korean',
});

const endRecommendDate = formatFullDate({
fullDate: endDate,
dayOfWeek: endDayOfWeek,
format: 'korean',
});

return (
<div css={s_recommendContainer}>
{/* 만약 하루만 추천해준다면 "00월 00일(수)"로 나오도록 구현(@낙타) */}
{isSameDate(new Date(startDate), new Date(endDate)) ? (
<span css={s_dateInfo}>{startRecommendDate}</span>
) : (
<Fragment>
<span css={s_dateInfo}>{startRecommendDate}부터</span>
<span css={s_dateInfo}>{endRecommendDate}까지</span>
</Fragment>
)}
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { css } from '@emotion/react';

import theme from '@styles/theme';

export const s_recommendContainer = css`
display: flex;
align-items: flex-start;
flex-direction: column;
gap: 0.8rem;
`;

export const s_attendeeInfo = css`
color: #6cd929;
text-align: start;
${theme.typography.captionBold}
`;

export const s_dateInfo = css`
${theme.typography.bodyBold}
`;
Loading
Loading