Skip to content

Commit

Permalink
feat(admin): 옵션 생성, 옵션 삭제 및 옵션 적용 (#206)
Browse files Browse the repository at this point in the history
* feat(admin): 티켓 옵션 생성하기

* feat(admin): 옵션 생성시 주관식, 객관식 구별 및 미리보기 수정

* feat(admin): 티켓 옵션 삭제하기

* feat(admin) : 티켓에 옵션 적용하기(#203)

* yarn install

* chore : 티켓 옵션 선택 스타일링

* feat(admin) : 공연 옵션 부착, 제거 dnd

* feat(admin) : 공연 옵션 부착, 제거 dnd 스타일

* feat(admin) : 공연 옵션 부착, 제거 dnd 스타일

---------

Co-authored-by: 한규진 <[email protected]>
  • Loading branch information
eugene028 and 9yujin authored Feb 25, 2023
1 parent 8148734 commit 3a125b0
Show file tree
Hide file tree
Showing 22 changed files with 1,212 additions and 26 deletions.
13 changes: 0 additions & 13 deletions apps/admin/src/components/events/options/TempOButtonSet.tsx

This file was deleted.

132 changes: 132 additions & 0 deletions apps/admin/src/components/events/options/apply/OptionDropArea.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
import { Droppable, Draggable } from 'react-beautiful-dnd';
import styled from '@emotion/styled';
import { theme, Text, Padding, FlexBox } from '@dudoong/ui';
import { OptionGroupResponse } from '@lib/apis/option/optionType';
import { ReactNode } from 'react';
import { css } from '@emotion/react';

interface OptionDropArea {
ticketItemId: number;
optionGroups: OptionGroupResponse[];
isEditable?: boolean;
}

const OptionDropArea = ({
ticketItemId,
optionGroups,
isEditable = true,
}: OptionDropArea) => {
return (
<>
<Droppable
droppableId={ticketItemId.toString()}
key={ticketItemId.toString()}
>
{(provided) => (
<section {...provided.droppableProps} ref={provided.innerRef}>
{optionGroups.length === 0 ? (
<>
<BlankOption
dropPlaceholder={provided.placeholder}
isEditable={isEditable}
/>
</>
) : (
<>
{optionGroups.map(
(item: OptionGroupResponse, index: number) => (
<Draggable
draggableId={`ticketOption-${ticketItemId}-${item.optionGroupId}`}
key={`ticketOption-${ticketItemId}-${item.optionGroupId}`}
index={index}
>
{(provided) => (
<div
{...provided.draggableProps}
{...provided.dragHandleProps}
ref={provided.innerRef}
>
<AppliedOption item={item} isEditable={isEditable} />
</div>
)}
</Draggable>
),
)}
{provided.placeholder}
</>
)}
</section>
)}
</Droppable>
</>
);
};

const AppliedOption = ({
item,
isEditable,
}: {
item: OptionGroupResponse;
isEditable: boolean;
}) => {
const additionalPrice =
item.options.find((option) => option.answer === '예')?.additionalPrice ||
'0원';
return (
<OptionWrapper key={item.optionGroupId}>
<Padding size={[16, 21]}>
<FlexBox direction="column" align="flex-start">
<Text typo="P_Text_16_SB" color="main_500">
{item.name}
</Text>
<Text typo="Text_14" color="main_400">
{item.type} {additionalPrice !== '0원' && ${additionalPrice}`}
</Text>
</FlexBox>
</Padding>
</OptionWrapper>
);
};

const BlankOption = ({
dropPlaceholder,
isEditable,
}: {
dropPlaceholder: ReactNode;
isEditable: boolean;
}) => {
return (
<RoundWrapper isEditable={isEditable}>
<Padding size={[40, 38]}>
<Text typo="P_Text_16_M" color="gray_300">
{isEditable
? '추가할 옵션을 드래그 앤 드롭 해주세요.'
: '이미 판매된 티켓의 옵션은 수정할 수 없어요.'}
</Text>
</Padding>
{dropPlaceholder}
</RoundWrapper>
);
};

export default OptionDropArea;

const RoundWrapper = styled.div<{ isEditable: boolean }>`
border-radius: 10px;
background-color: ${theme.palette.gray_100};
border: 1px solid ${({ theme }) => theme.palette.gray_200};
${({ theme, isEditable }) =>
!isEditable &&
css`
background-color: ${theme.palette.gray_200};
`}
height: 100px;
margin-top: 10px;
`;

const OptionWrapper = styled.div`
border-radius: 10px;
background-color: ${theme.palette.main_100};
height: auto;
margin-top: 12px;
`;
54 changes: 54 additions & 0 deletions apps/admin/src/components/events/options/apply/OptionItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { useLocation } from 'react-router-dom';
import { useMutation, useQueryClient } from '@tanstack/react-query';
import OptionApi from '@lib/apis/option/OptionApi';
import { FlexBox, ListRow, TagButton, Padding } from '@dudoong/ui';

export interface OptionItemProps {
name: string;
subText: string;
OptionGroupId: number;
}

const OptionItem = ({ name, subText, OptionGroupId }: OptionItemProps) => {
const { pathname } = useLocation();
const eventId = pathname.split('/')[2];
const queryClient = useQueryClient();
const { mutate: optionDeleteMutate } = useMutation(
OptionApi.PATCH_OPTION_DELETE,
{
onSuccess: (data) => {
queryClient.invalidateQueries({ queryKey: ['AllOption', eventId] });
},
},
);

const handleRemoveClick = () => {
optionDeleteMutate({
eventId: eventId,
optionGroupId: OptionGroupId,
});
};

return (
<>
<FlexBox align="center" justify="space-between">
<ListRow
text={name}
textColor={['black', 'gray_400']}
subText={subText}
padding={[13.5, 23]}
/>
<Padding size={[0, 24, 0, 0]}>
<TagButton
text="삭제"
color="warn"
size="lg"
onClick={handleRemoveClick}
/>
</Padding>
</FlexBox>
</>
);
};

export default OptionItem;
98 changes: 98 additions & 0 deletions apps/admin/src/components/events/options/apply/OptionList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import {
ListHeader,
theme,
Spacing,
FlexBox,
Text,
Padding,
} from '@dudoong/ui';
import styled from '@emotion/styled';
import OptionItem from './OptionItem';
import { Draggable, Droppable } from 'react-beautiful-dnd';
import type { OptionGroupResponse } from '@lib/apis/option/optionType';

interface OptionListProps {
optionItems: OptionGroupResponse[];
}

const OptionList = ({ optionItems }: OptionListProps) => {
console.log(optionItems);
if (!optionItems?.length) {
return (
<Wrapper>
<div>
<ListHeader padding={0} size="listHeader_18" title="옵션 목록" />
<Spacing size={42} />
<OptionItemContainer>
<Padding size={[24, 12, 24, 12]}>
<Text typo="P_Header_16_SB">옵션을 먼저 생성해주세요!</Text>
</Padding>
</OptionItemContainer>
</div>
</Wrapper>
);
} else {
return (
<Wrapper>
<FlexBox direction="column" align="flex-start">
<div>
<ListHeader padding={0} size="listHeader_18" title="옵션 목록" />
<Spacing size={42} />
</div>

<Droppable droppableId="options">
{(provided) => (
<section {...provided.droppableProps} ref={provided.innerRef}>
{optionItems?.map((item, index) => (
<Draggable
key={`eventOption-${item.optionGroupId}`}
draggableId={`eventOption-${item.optionGroupId}`}
index={index}
>
{(provided) => (
<div
{...provided.draggableProps}
{...provided.dragHandleProps}
ref={provided.innerRef}
>
<OptionItemContainer key={item.optionGroupId}>
<OptionItem
name={item.name}
subText={`${item.type}`}
OptionGroupId={item.optionGroupId}
/>
</OptionItemContainer>
<Spacing size={16} />
</div>
)}
</Draggable>
))}
{provided.placeholder}
</section>
)}
</Droppable>
</FlexBox>
</Wrapper>
);
}
};

export default OptionList;

const Wrapper = styled.div`
& > div {
position: sticky;
position: -webkit-sticky;
margin-top: 44px;
top: 36px;
}
`;

const OptionItemContainer = styled.div`
width: 400px;
height: auto;
box-sizing: border-box;
background-color: ${theme.palette.white};
border-radius: 12px;
border: 1px solid ${theme.palette.black};
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { ListHeader, Spacing, theme, Text, Divider } from '@dudoong/ui';
import styled from '@emotion/styled';
import { useQuery } from '@tanstack/react-query';
import { useLocation } from 'react-router-dom';
import OptionDropArea from './OptionDropArea';
import OptionApi from '@lib/apis/option/OptionApi';

const TicketListOption = () => {
const { pathname } = useLocation();
const eventId = pathname.split('/')[2];

const { data, isSuccess } = useQuery(['AppliedTicket', eventId], () =>
OptionApi.GET_EVENTS_APPLIEDOPTIONGROUPS(eventId),
);

return (
<Wrapper>
{isSuccess && (
<>
<Spacing size={36} />
<ListHeader padding={0} size="listHeader_18" title="티켓 목록" />
<Spacing size={42} />

{data.appliedOptionGroups?.map((item) => (
<div key={item?.ticketItemId}>
<TicketItem key={item.ticketItemId}>
<Text typo={'P_Header_16_SB'}>{item.ticketName}</Text>
<Divider line={true} />
<OptionDropArea
ticketItemId={item.ticketItemId}
optionGroups={item.optionGroups}
/>
</TicketItem>
<Spacing size={16} />
</div>
))}
</>
)}
</Wrapper>
);
};
export default TicketListOption;

const Wrapper = styled.div``;

const TicketItem = styled.div`
width: 400px;
height: auto;
box-sizing: border-box;
background-color: ${theme.palette.white};
border-radius: 12px;
border: 1px solid ${theme.palette.black};
padding: 24px 22px;
`;
Loading

0 comments on commit 3a125b0

Please sign in to comment.