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

플랫폼별 스케줄 추가 및 수정 #307

Merged
merged 3 commits into from
Jul 2, 2024
Merged
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
Expand Up @@ -4,12 +4,12 @@ import { useRecoilValue } from 'recoil';
import { Button, DatePickerField, InputField, RadioButtonField, SelectField } from '@/components';
import { InputSize } from '@/components/common/Input/Input.component';
import * as Styled from './ScheduleTemplate.styled';
import { $generations } from '@/store';
import { $generations, $profile, $teams } from '@/store';
import { SelectOption } from '@/components/common/Select/Select.component';
import { SessionTemplate } from '../SessionTemplate';
import Plus from '@/assets/svg/plus-20.svg';
import { EventCreateRequest } from '@/types';
import { LocationType, ScheduleFormValues } from '@/utils';
import { LocationType, ScheduleFormValues, ScheduleType } from '@/utils';
import { useScript } from '@/hooks';

const DEFAULT_SESSION: EventCreateRequest = {
Expand All @@ -26,8 +26,11 @@ const ScheduleTemplate = () => {
const { register, control, formState, getValues, watch, setValue } =
useFormContext<ScheduleFormValues>();
const generations = useRecoilValue($generations);
const [position] = useRecoilValue($profile);
const teams = useRecoilValue($teams);

const locationType = watch('locationType');
const scheduleType = watch('scheduleType');

const { fields, append, remove } = useFieldArray({
name: 'sessions',
Expand All @@ -46,6 +49,23 @@ const ScheduleTemplate = () => {
const defaultOption = generationOptions.find(
(option) => option.value === getValues('generationNumber')?.toString(),
);

const generationPlatformOptions = teams.map(({ name }) => ({
value: name.toUpperCase(),
label: name,
}));

const getTeamSelectOptions = () => {
const myTeamOptionObject = generationPlatformOptions.find(({ value }) => value === position);
return myTeamOptionObject ? [myTeamOptionObject] : generationPlatformOptions;
};

const teamSelectOptions = getTeamSelectOptions();

const defaultPlatformOption = generationPlatformOptions.find(
(option) => option.value === getValues('schedulePlatformType'),
);

const handleClickAddressSearch = () => {
new window.daum.Postcode({
async oncomplete(data: { address: string }) {
Expand Down Expand Up @@ -96,6 +116,37 @@ const ScheduleTemplate = () => {
isFullWidth
{...register('generationNumber', { required: true })}
/>
<div>
<Styled.InputLabel htmlFor="location">
<span>구분</span>
<Styled.RequiredDot />
</Styled.InputLabel>
<Styled.RadioButtonGroup>
<RadioButtonField
label="플랫폼"
required
value={ScheduleType.PLATFORM}
{...register('scheduleType', { required: true })}
/>
<RadioButtonField
label="전체"
required
value={ScheduleType.ALL}
{...register('scheduleType', { required: true })}
/>
</Styled.RadioButtonGroup>
{scheduleType === ScheduleType.PLATFORM && (
<SelectField
size="md"
options={teamSelectOptions}
defaultValue={defaultPlatformOption}
required
{...register('schedulePlatformType', {
required: scheduleType === ScheduleType.PLATFORM,
})}
/>
)}
</div>
<DatePickerField
label="스케줄 일시"
$size="md"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ import React, { useMemo } from 'react';
import { ScheduleStatus, ValueOf } from '@/types';

import * as Styled from './ScheduleInfoList.styled';
import { formatDate, getScheduleStatusText } from '@/utils';
import { formatDate, getScheduleStatusText, getScheduleType, SchedulePlatformType } from '@/utils';

interface ScheduleInfoListProps {
generationNumber: number;
scheduleType: ValueOf<typeof SchedulePlatformType>;
name: string;
createdAt: string;
startedAt: string;
Expand All @@ -20,6 +21,7 @@ interface ScheduleInfoListProps {
const ScheduleInfoList = ({
name,
generationNumber,
scheduleType,
startedAt,
createdAt,
publishedAt,
Expand All @@ -36,6 +38,10 @@ const ScheduleInfoList = ({
label: '기수',
value: `${generationNumber}기`,
},
{
label: '구분',
value: getScheduleType(scheduleType),
},
{
label: '스케줄 일시',
value: formatDate(startedAt, 'YYYY년 M월 D일 A hh시 mm분'),
Expand All @@ -62,7 +68,7 @@ const ScheduleInfoList = ({
value: getScheduleStatusText(status),
},
];
}, [createdAt, generationNumber, name, publishedAt, startedAt, status, location]);
}, [createdAt, generationNumber, name, publishedAt, startedAt, status, location, scheduleType]);

return (
<Styled.ScheduleInfoList>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@ import * as Styled from './TeamNavigationTabs.styled';

const TEAMS = ['All', 'Design', 'Android', 'iOS', 'Web', 'Node', 'Spring'];

const TeamNavigationTabs = () => {
interface TeamNavigationTabsProps {
allAltText?: string;
}

const TeamNavigationTabs = ({ allAltText }: TeamNavigationTabsProps) => {
const [searchParams, setSearchParams] = useSearchParams();

const handleClickTab = (teamParam: string) => {
Expand All @@ -23,17 +27,18 @@ const TeamNavigationTabs = () => {
const teamParam = team === 'All' ? '' : team.toUpperCase();
const active =
team === 'All' ? !searchParams.has('team') : searchParams.get('team') === teamParam;

const isAllAltText = team === 'All' && !!allAltText;
return (
<Styled.Tab
key={index}
role="link"
aria-label={`${team} tab`}
aria-current={active}
active={active}
isAllAltText={isAllAltText}
onClick={() => handleClickTab(teamParam)}
>
{team}
{isAllAltText ? allAltText : team}
</Styled.Tab>
);
})}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import styled from '@emotion/styled';

interface StyledNavigationItemProps {
active: boolean;
isAllAltText?: boolean;
}

export const Tabs = styled.nav`
Expand All @@ -15,13 +16,13 @@ export const Tabs = styled.nav`
`;

export const Tab = styled.button<StyledNavigationItemProps>`
${({ theme, active }) => css`
${({ theme, active, isAllAltText }) => css`
${theme.fonts.regular16};

display: flex;
align-items: center;
justify-content: center;
width: 8rem;
width: ${isAllAltText ? '12rem' : '8rem'};
height: 4rem;
color: ${active ? theme.colors.purple70 : theme.colors.gray50};
letter-spacing: -0.08rem;
Expand Down
2 changes: 2 additions & 0 deletions src/pages/ScheduleDetail/ScheduleDetail.page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ const ScheduleDetail = () => {

const {
name,
scheduleType,
generationNumber,
startedAt,
createdAt,
Expand Down Expand Up @@ -143,6 +144,7 @@ const ScheduleDetail = () => {
<h3>스케줄 정보</h3>
<ScheduleInfoList
name={name}
scheduleType={scheduleType}
generationNumber={generationNumber}
startedAt={startedAt}
createdAt={createdAt}
Expand Down
42 changes: 35 additions & 7 deletions src/pages/ScheduleList/ScheduleList.page.tsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,50 @@
import React, { useEffect, useMemo } from 'react';
import React, { useEffect, useMemo, MouseEvent } from 'react';
import { useLocation, useSearchParams } from 'react-router-dom';
import { useRecoilStateLoadable, useRecoilValue } from 'recoil';
import { BottomCTA, Button, Link, Pagination, SearchOptionBar, Table } from '@/components';
import {
BottomCTA,
Button,
Link,
Pagination,
SearchOptionBar,
Table,
TeamNavigationTabs,
} from '@/components';
import * as Styled from './ScheduleList.styled';
import { TableColumn } from '@/components/common/Table/Table.component';
import { ScheduleRequest, ScheduleResponse, ScheduleStatus } from '@/types/dto/schedule';
import { formatDate } from '@/utils/date';
import { getScheduleDetailPage, PATH } from '@/constants';
import { ButtonShape, ButtonSize } from '@/components/common/Button/Button.component';
import { usePagination } from '@/hooks';
import { $generationNumber } from '@/store';
import { usePagination, useToast } from '@/hooks';
import { $generationNumber, $isMaster, $profile } from '@/store';
import { $schedules } from '@/store/schedule';
import { ValueOf } from '@/types';
import { getScheduleStatusText } from '@/utils';
import { getScheduleStatusText, SchedulePlatformType } from '@/utils';
import { ToastType } from '@/styles';

const ScheduleList = () => {
const [searchParams, setSearchParams] = useSearchParams();
const teamName = searchParams.get('team');
const generationNumber = useRecoilValue($generationNumber);
const [position] = useRecoilValue($profile);
const isMaster = useRecoilValue($isMaster);
const page = searchParams.get('page') || '1';
const size = searchParams.get('size') || '10';
const searchWord = searchParams.get('searchWord') || '';

const { pathname, search } = useLocation();
const { handleAddToast } = useToast();

const handleClickLink = (event: MouseEvent<HTMLAnchorElement>) => {
if (!isMaster && teamName !== position) {
handleAddToast({
type: ToastType.error,
message: '접근 권한이 없는 플랫폼입니다.',
});
event.preventDefault();
}
};

const columns: TableColumn<ScheduleResponse>[] = [
{
Expand All @@ -31,7 +53,11 @@ const ScheduleList = () => {
widthRatio: '20%',
idAccessor: 'scheduleId',
renderCustomCell: ({ cellValue, id }) => (
<Styled.TitleLink to={getScheduleDetailPage(id)} state={{ from: `${pathname}${search}` }}>
<Styled.TitleLink
to={getScheduleDetailPage(id)}
state={{ from: `${pathname}${search}` }}
onClick={handleClickLink}
>
{cellValue as string}
</Styled.TitleLink>
),
Expand Down Expand Up @@ -75,8 +101,9 @@ const ScheduleList = () => {
size: parseInt(size, 10),
searchWord,
generationNumber,
scheduleType: (teamName as ValueOf<typeof SchedulePlatformType>) ?? 'ALL',
}),
[generationNumber, page, searchWord, size],
[generationNumber, page, searchWord, size, teamName],
);

const [{ contents }] = useRecoilStateLoadable($schedules(scheduleParams));
Expand Down Expand Up @@ -105,6 +132,7 @@ const ScheduleList = () => {
<Styled.PageWrapper>
<Styled.Heading>스케줄 정보 </Styled.Heading>
<Styled.StickyContainer>
<TeamNavigationTabs allAltText="All Seminar" />
<SearchOptionBar placeholder="스케줄명 검색" />
</Styled.StickyContainer>
<Table
Expand Down
5 changes: 5 additions & 0 deletions src/types/dto/schedule.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { SchedulePlatformType } from '@/utils';
import { ValueOf } from '..';

export const ScheduleStatus = {
Expand All @@ -10,6 +11,7 @@ export interface ScheduleRequest {
page?: number;
size?: number;
sort?: string;
scheduleType: ValueOf<typeof SchedulePlatformType>;
}

export interface EventCreateRequest {
Expand All @@ -34,6 +36,7 @@ interface Location {

export interface ScheduleCreateRequest extends Location {
generationNumber?: number;
scheduleType: ValueOf<typeof SchedulePlatformType>;
endedAt: string;
name: string;
startedAt: string;
Expand All @@ -42,6 +45,7 @@ export interface ScheduleCreateRequest extends Location {

export interface ScheduleUpdateRequest extends Location {
generationNumber?: number;
scheduleType: ValueOf<typeof SchedulePlatformType>;
endedAt: string;
name: string;
startedAt: string;
Expand All @@ -51,6 +55,7 @@ export interface ScheduleUpdateRequest extends Location {
export interface ScheduleResponse {
endedAt: string;
generationNumber: number;
scheduleType: ValueOf<typeof SchedulePlatformType>;
name: string;
scheduleId: number;
createdAt: string;
Expand Down
Loading
Loading