Skip to content

Commit 2816474

Browse files
committed
Merge branch 'develop' into feat/webview-bridge
2 parents bd7528e + 5843281 commit 2816474

27 files changed

+1053
-978
lines changed

README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@
1717
## 패키지 설명
1818

1919
- `apps/admin`: 불티에서 공연을 생성하고 관리하는 사용자들을 위한 서비스입니다.
20-
- `apps/preview`: 공연 예매 페이지를 공유했을 때 랜딩될 페이지입니다. (WIP)
21-
- `apps/super-admin`: 불티 팀원이 사용할 슈퍼 어드민 페이지입니다. (WIP)
20+
- `apps/preview`: 공연 예매 페이지를 공유했을 때 랜딩될 페이지입니다.
21+
- `apps/super-admin`: 불티 팀원이 사용할 슈퍼 어드민 페이지입니다.
2222
- `apps/storybook`: 불티에서 공통적으로 사용될 디자인 컴포넌트를 확인할 수 있는 Storybook 페이지입니다.
2323
- `packages/api`: 웹 클라이언트에서 사용되는 서버 API 호출 관련 로직이 포함된 패키지입니다.
2424
- `packages/config-eslint`: 각 패키지에서 공통적으로 사용될 ESLint 관련 설정이 포함된 패키지입니다.

apps/admin/src/App.tsx

+3-2
Original file line numberDiff line numberDiff line change
@@ -128,8 +128,9 @@ const privateRoutes = [
128128
element: <SignUpCompletePage />,
129129
},
130130
{ path: PATH.HOME, element: <HomePage /> },
131-
{ path: PATH.SHOW_ADD, element: <ShowAddPage step="info" /> },
132-
{ path: PATH.SHOW_ADD_TICKET, element: <ShowAddPage step="ticket" /> },
131+
{ path: PATH.SHOW_ADD, element: <ShowAddPage step="basic" /> },
132+
{ path: PATH.SHOW_ADD_DETAIL, element: <ShowAddPage step="detail" /> },
133+
{ path: PATH.SHOW_ADD_SALES, element: <ShowAddPage step="sales" /> },
133134
{
134135
path: PATH.SHOW_ADD_COMPLETE,
135136
element: <ShowAddCompletePage />,

apps/admin/src/components/ShowCastInfoFormDialogContent/index.tsx

+4-9
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ export interface TempShowCastInfoFormInput {
1919

2020
interface Props {
2121
prevShowCastInfo?: TempShowCastInfoFormInput;
22-
onDelete?: () => Promise<void>;
23-
onSave: (value: TempShowCastInfoFormInput) => Promise<void>;
22+
onDelete?: () => void;
23+
onSave: (value: TempShowCastInfoFormInput) => void;
2424
}
2525

2626
const ShowCastInfoFormDialogContent = ({ prevShowCastInfo, onDelete, onSave }: Props) => {
@@ -209,7 +209,6 @@ const ShowCastInfoFormDialogContent = ({ prevShowCastInfo, onDelete, onSave }: P
209209
});
210210

211211
if (isConfirm) {
212-
toast.success('팀원 정보를 삭제했습니다.');
213212
setIsMemberFieldBlurred((prev) =>
214213
prev.filter((_, blurredIndex) => blurredIndex !== index),
215214
);
@@ -227,7 +226,7 @@ const ShowCastInfoFormDialogContent = ({ prevShowCastInfo, onDelete, onSave }: P
227226
field={draggingField}
228227
index={draggingFieldIndex}
229228
isFieldBlurred={isMemberFieldBlurred[draggingFieldIndex]}
230-
/>
229+
/>
231230
</Styled.DraggableShowCastInfoMemberRow>
232231
) : null}
233232
</DragOverlay>
@@ -254,7 +253,6 @@ const ShowCastInfoFormDialogContent = ({ prevShowCastInfo, onDelete, onSave }: P
254253
if (isConfirm) {
255254
try {
256255
onDelete();
257-
toast.success('팀 정보를 삭제했습니다.');
258256
} catch {
259257
toast.error('알 수 없는 오류가 발생했습니다.');
260258
}
@@ -279,10 +277,7 @@ const ShowCastInfoFormDialogContent = ({ prevShowCastInfo, onDelete, onSave }: P
279277
);
280278

281279
try {
282-
await onSave({ id, name, members });
283-
toast.success(
284-
onDelete ? '출연진 정보를 수정했습니다.' : '출연진 정보를 생성했습니다.',
285-
);
280+
onSave({ id, name, members });
286281
} catch {
287282
toast.error('알 수 없는 오류가 발생했습니다.');
288283
}

apps/admin/src/components/ShowDetailLayout/index.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,8 @@ const toTargets = {
6060
} as const;
6161

6262
const label = {
63-
INFO: '공연 기본 정보',
64-
TICKET: '티켓 관리',
63+
INFO: '공연 정보',
64+
TICKET: '판매 정보',
6565
RESERVATION: '결제 관리',
6666
ENTRANCE: '방문자 관리',
6767
SETTLEMENT: '정산 관리',

apps/admin/src/components/ShowInfoFormContent/ShowBasicInfoFormContent.tsx

+5-3
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,14 @@ import { Controller, UseFormReturn } from 'react-hook-form';
88
import DaumPostcode from 'react-daum-postcode';
99

1010
import Styled from './ShowInfoFormContent.styles';
11-
import { ShowInfoFormInputs } from './types';
11+
import { ShowBasicInfoFormInputs } from './types';
1212
import { useBodyScrollLock } from '~/hooks/useBodyScrollLock';
1313

1414
const MAX_IMAGE_COUNT = 3;
1515
const MIN_DATE = format(add(new Date(), { days: 1 }), 'yyyy-MM-dd');
1616

1717
interface ShowBasicInfoFormContentProps {
18-
form: UseFormReturn<ShowInfoFormInputs, unknown, ShowInfoFormInputs>;
18+
form: UseFormReturn<ShowBasicInfoFormInputs, unknown, ShowBasicInfoFormInputs>;
1919
imageFiles: ImageFile[];
2020
disabled?: boolean;
2121
onDropImage: (acceptedFiles: File[]) => void;
@@ -82,7 +82,9 @@ const ShowBasicInfoFormContent = ({
8282
<Styled.ShowInfoFormContent>
8383
<Styled.ShowInfoFormLabel required>공연 포스터</Styled.ShowInfoFormLabel>
8484
<Styled.ShowInfoFormDescription>
85-
<span>원하시는 노출 순서대로 이미지를 업로드해주세요.&nbsp;</span>
85+
<span>
86+
원하시는 <strong>노출 순서대로</strong> 이미지를 업로드해주세요.&nbsp;
87+
</span>
8688
<span>표준 종이규격(A, B)의 이미지를 권장합니다.</span>
8789
<br />
8890
<span>(최소 1장, 최대 {MAX_IMAGE_COUNT}장 업로드 가능 / jpg, png 형식)</span>

apps/admin/src/components/ShowInfoFormContent/ShowCastInfoFormContent.tsx

+93-19
Original file line numberDiff line numberDiff line change
@@ -5,58 +5,132 @@ import { PlusIcon } from '@boolti/icon';
55
import ShowCastInfoFormDialogContent, {
66
TempShowCastInfoFormInput,
77
} from '../ShowCastInfoFormDialogContent';
8+
import { DndContext, DragOverEvent, KeyboardSensor, MouseSensor, TouchSensor, closestCenter, useSensor, useSensors } from '@dnd-kit/core';
9+
import { restrictToVerticalAxis } from '@dnd-kit/modifiers';
10+
import { SortableContext, arrayMove, sortableKeyboardCoordinates, verticalListSortingStrategy } from '@dnd-kit/sortable';
11+
import ShowCastInfo from '~/components/ShowCastInfo';
12+
import { useCallback, useRef, useState } from 'react';
13+
import { useEffect } from 'react';
814

9-
interface Props {
10-
onSave: (value: TempShowCastInfoFormInput) => Promise<void>;
15+
interface ShowCastInfoFormContentProps {
16+
initialCastTeamList?: TempShowCastInfoFormInput[];
17+
onChange: (value: TempShowCastInfoFormInput[]) => void;
1118
}
1219

13-
const ShowCastInfoFormContent = ({ onSave }: Props) => {
20+
const ShowCastInfoFormContent = ({ initialCastTeamList, onChange }: ShowCastInfoFormContentProps) => {
1421
const dialog = useDialog();
1522

16-
const onClick = () => {
23+
const [castTeamList, setCastTeamList] = useState<TempShowCastInfoFormInput[]>(initialCastTeamList ?? [])
24+
const prevCastTeamList = useRef<string>(JSON.stringify(castTeamList))
25+
26+
const sensors = useSensors(
27+
useSensor(MouseSensor, {
28+
activationConstraint: {
29+
distance: 10,
30+
},
31+
}),
32+
useSensor(TouchSensor, {
33+
activationConstraint: {
34+
delay: 0,
35+
tolerance: 5,
36+
},
37+
}),
38+
useSensor(KeyboardSensor, {
39+
coordinateGetter: sortableKeyboardCoordinates,
40+
})
41+
);
42+
43+
const castTeamDragEndHandler = useCallback((event: DragOverEvent) => {
44+
const { active, over } = event;
45+
46+
if (active && over && over.id !== active.id) {
47+
setCastTeamList((prev) => {
48+
const oldIndex = prev.findIndex(({ id }) => id === active.id);
49+
const newIndex = prev.findIndex(({ id }) => id === over.id);
50+
51+
return arrayMove(prev, oldIndex, newIndex);
52+
});
53+
}
54+
}, []);
55+
56+
57+
const castAddButtonClickHandler = () => {
1758
dialog.open({
1859
isAuto: true,
1960
title: '출연진 정보 등록',
2061
content: (
2162
<ShowCastInfoFormDialogContent
22-
onSave={async (value) => {
23-
try {
24-
await onSave(value);
25-
dialog.close();
26-
} catch {
27-
return new Promise((_, reject) => reject('저장 중 오류가 발생하였습니다.'));
28-
}
63+
onSave={(castInfo) => {
64+
console.log(castInfo)
65+
setCastTeamList((prev) => [
66+
...prev,
67+
castInfo
68+
])
69+
dialog.close();
2970
}}
3071
/>
3172
),
3273
});
3374
};
3475

76+
useEffect(() => {
77+
const stringifiedCastTeamList = JSON.stringify(castTeamList)
78+
79+
if (prevCastTeamList.current !== stringifiedCastTeamList) {
80+
prevCastTeamList.current = stringifiedCastTeamList
81+
onChange?.(castTeamList)
82+
}
83+
}, [castTeamList, onChange])
84+
3585
return (
3686
<Styled.ShowInfoFormGroup>
3787
<Styled.ShowInfoFormGroupHeader>
38-
<Styled.ShowInfoFormGroupInfo>
39-
<Styled.ShowInfoFormTitle>
88+
<Styled.ShowInfoFormGroupInfo style={{ marginBottom: 0 }}>
89+
<Styled.ShowInfoFormLabel style={{ justifyContent: 'space-between' }}>
4090
출연진 정보
41-
<Styled.MobileCastInfoRegisterButton type="button" onClick={onClick}>
91+
<Styled.MobileCastInfoRegisterButton type="button" onClick={castAddButtonClickHandler}>
4292
<PlusIcon />
4393
등록하기
4494
</Styled.MobileCastInfoRegisterButton>
45-
</Styled.ShowInfoFormTitle>
46-
<Styled.ShowInfoFormSubtitle>
95+
</Styled.ShowInfoFormLabel>
96+
<Styled.ShowInfoFormDescription>
4797
출연진 정보를 팀 단위로 등록해 주세요.
48-
</Styled.ShowInfoFormSubtitle>
98+
</Styled.ShowInfoFormDescription>
4999
</Styled.ShowInfoFormGroupInfo>
50100
<Styled.DesktopCastInfoRegisterButton
51101
type="button"
52102
colorTheme="netural"
53-
size="bold"
103+
size="small"
54104
icon={<PlusIcon />}
55-
onClick={onClick}
105+
onClick={castAddButtonClickHandler}
56106
>
57107
등록하기
58108
</Styled.DesktopCastInfoRegisterButton>
59109
</Styled.ShowInfoFormGroupHeader>
110+
<DndContext sensors={sensors} modifiers={[restrictToVerticalAxis]} collisionDetection={closestCenter} onDragEnd={castTeamDragEndHandler}>
111+
<SortableContext items={castTeamList.map((info) => info.id)} strategy={verticalListSortingStrategy}>
112+
{castTeamList.map((info) => (
113+
<ShowCastInfo
114+
key={info.id}
115+
showCastInfo={info}
116+
onSave={(showCastInfoFormInput: TempShowCastInfoFormInput) => {
117+
setCastTeamList((prev) =>
118+
prev.map((item) =>
119+
item.id === info.id ? showCastInfoFormInput : item,
120+
)
121+
);
122+
return new Promise((resolve) => resolve());
123+
}}
124+
onDelete={() => {
125+
setCastTeamList((prev) =>
126+
prev.filter((item) => item.id !== info.id)
127+
);
128+
return new Promise((resolve) => resolve());
129+
}}
130+
/>
131+
))}
132+
</SortableContext>
133+
</DndContext>
60134
</Styled.ShowInfoFormGroup>
61135
);
62136
};

apps/admin/src/components/ShowInfoFormContent/ShowDetailInfoFormContent.tsx

+6-6
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@ import { TextField } from '@boolti/ui';
22
import { Controller, UseFormReturn } from 'react-hook-form';
33

44
import Styled from './ShowInfoFormContent.styles';
5-
import { ShowInfoFormInputs } from './types';
5+
import { ShowDetailInfoFormInputs } from './types';
66

77
interface ShowDetailInfoFormContentProps {
8-
form: UseFormReturn<ShowInfoFormInputs>;
8+
form: UseFormReturn<ShowDetailInfoFormInputs>;
99
disabled?: boolean;
1010
}
1111

@@ -68,7 +68,7 @@ const ShowDetailInfoFormContent = ({ form, disabled }: ShowDetailInfoFormContent
6868
</Styled.ShowInfoFormRow>
6969
<Styled.ShowInfoFormResponsiveRowColumn>
7070
<Styled.ShowInfoFormContent style={{ flex: 0.58, minWidth: '216px' }}>
71-
<Styled.ShowInfoFormLabel required>대표자명</Styled.ShowInfoFormLabel>
71+
<Styled.ShowInfoFormLabel required>주최자명</Styled.ShowInfoFormLabel>
7272
<Styled.TextField>
7373
<Controller
7474
control={control}
@@ -84,7 +84,7 @@ const ShowDetailInfoFormContent = ({ form, disabled }: ShowDetailInfoFormContent
8484
<TextField
8585
inputType="text"
8686
size="big"
87-
placeholder="대표자명을 입력해 주세요"
87+
placeholder="주최자명을 입력해 주세요"
8888
required
8989
disabled={disabled}
9090
onChange={(event) => {
@@ -106,7 +106,7 @@ const ShowDetailInfoFormContent = ({ form, disabled }: ShowDetailInfoFormContent
106106
</Styled.TextField>
107107
</Styled.ShowInfoFormContent>
108108
<Styled.ShowInfoFormContent>
109-
<Styled.ShowInfoFormLabel required>대표자 연락처</Styled.ShowInfoFormLabel>
109+
<Styled.ShowInfoFormLabel required>주최자 연락처</Styled.ShowInfoFormLabel>
110110
<Styled.TextField>
111111
<Controller
112112
control={control}
@@ -118,7 +118,7 @@ const ShowDetailInfoFormContent = ({ form, disabled }: ShowDetailInfoFormContent
118118
<TextField
119119
inputType="text"
120120
size="big"
121-
placeholder="대표자 연락처를 입력해 주세요"
121+
placeholder="주최자 연락처를 입력해 주세요"
122122
required
123123
disabled={disabled}
124124
onChange={(event) => {

apps/admin/src/components/ShowInfoFormContent/ShowInfoFormContent.styles.ts

+9-3
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,8 @@ const ShowInfoFormContent = styled.div`
9696
`;
9797

9898
const ShowInfoFormLabel = styled.label<ShowInfoFormLabelProps>`
99-
display: block;
99+
display: flex;
100+
align-items: flex-end;
100101
${({ theme }) => theme.typo.b3};
101102
color: ${({ theme }) => theme.palette.grey.g90};
102103
@@ -311,6 +312,7 @@ const TextArea = styled.textarea<TextAreaProps>`
311312
${({ theme }) => theme.typo.b3};
312313
313314
&:placeholder-shown {
315+
${({ theme }) => theme.typo.b3};
314316
border: 1px solid ${({ theme }) => theme.palette.grey.g20};
315317
color: ${({ theme }) => theme.palette.grey.g30};
316318
}
@@ -358,7 +360,7 @@ const TicketGroupInfo = styled.div`
358360

359361
const TicketGroupTitle = styled.h3<TicketGroupTitleProps>`
360362
display: flex;
361-
${({ theme }) => theme.typo.sh2};
363+
${({ theme }) => theme.typo.b3};
362364
color: ${({ theme }) => theme.palette.grey.g90};
363365
364366
&::after {
@@ -370,7 +372,6 @@ const TicketGroupTitle = styled.h3<TicketGroupTitleProps>`
370372
}
371373
372374
${mq_lg} {
373-
${({ theme }) => theme.typo.h1};
374375
margin-bottom: 2px;
375376
}
376377
`;
@@ -448,6 +449,11 @@ const TicketAction = styled.div`
448449
449450
${mq_lg} {
450451
display: flex;
452+
453+
svg {
454+
width: 20px;
455+
height: 20px;
456+
}
451457
}
452458
`;
453459

apps/admin/src/components/ShowInfoFormContent/ShowInvitationTicketFormContent.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ const ShowInvitationTicketFormContent = ({
5353
<Button
5454
type="button"
5555
colorTheme="netural"
56-
size="bold"
56+
size="small"
5757
icon={<PlusIcon />}
5858
onClick={() => {
5959
invitationTicketDialog.open({

apps/admin/src/components/ShowInfoFormContent/ShowSalesTicketFormContent.tsx

+6-5
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ const ShowSalesTicketFormContent = ({
5252
<Button
5353
type="button"
5454
colorTheme="netural"
55-
size="bold"
55+
size="small"
5656
icon={<PlusIcon />}
5757
onClick={() => {
5858
salesTicketDialog.open({
@@ -109,10 +109,11 @@ const ShowSalesTicketFormContent = ({
109109
<Styled.TicketDescription>{ticket.price}</Styled.TicketDescription>
110110
</Styled.TicketInfo>
111111
<Styled.TicketAction>
112-
<Button
112+
<TextButton
113113
type="button"
114-
colorTheme="line"
115-
size="bold"
114+
colorTheme="netural"
115+
size="small"
116+
icon={<TrashIcon />}
116117
disabled={(() => {
117118
if (disabled) return disabled;
118119
if (fullEditable) return false;
@@ -122,7 +123,7 @@ const ShowSalesTicketFormContent = ({
122123
onClick={() => onDeleteTicket(ticket)}
123124
>
124125
삭제하기
125-
</Button>
126+
</TextButton>
126127
</Styled.TicketAction>
127128
<Styled.MobileTicketAction>
128129
<TextButton

0 commit comments

Comments
 (0)