Skip to content
Merged
Show file tree
Hide file tree
Changes from 31 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
c36de1c
ticket 폴더 내 TicketOptionPage 생성 및 라우트 추가
xaexunxang Mar 10, 2025
fec23cf
feat: 티켓 생성 초기 화면 퍼블리싱(설문지 버튼 제외) 및 토글, 설문지 추가 상태 관리 구현
xaexunxang Mar 11, 2025
d75df84
feat: 객관식, 주관식, 여러 개 선택 ChoiceChip에 다른 설문지 구현
xaexunxang Mar 11, 2025
26a5e13
menuLists & checklists에 티켓에 추가 옵션 부착 추가
xaexunxang Mar 13, 2025
b990317
feat: 티켓 옵션 입력 간 DefaultTextField에 onFocus, onBlur 속성 추가
xaexunxang Mar 13, 2025
ae05602
위치 및 사이즈 CSS 수정
xaexunxang Mar 16, 2025
5858129
DefaultTextField 에 입력활성화 속성 추가 및 토글에 따른 상태값 부여
xaexunxang Mar 16, 2025
f62cd2d
마진, 패딩 위치 변경
xaexunxang Mar 16, 2025
a923471
feat: 기타 CSS 수정, 선택지 수량 제한 상태값 구현
xaexunxang Mar 16, 2025
edb0123
feat: 티켓 옵션 설정 페이지 구현 완료 및 티켓 옵션 부착 페이지 생성
xaexunxang Mar 18, 2025
bc44590
svg 불러오기 오류 및 vite 불러오기 오류로 node_modules, vite 재설치
xaexunxang Mar 20, 2025
fe7862e
Fix: 잘못만든 파일 삭제
xaexunxang Mar 20, 2025
a6ef5d1
feat: DragArea 내에서 Drag 구현
xaexunxang Mar 20, 2025
8e8804b
wip: 드래그앤드롭 기능 구현 중 중간 저장
xaexunxang Mar 20, 2025
63b5675
fix : Router 오타 수정 및 선택지 수량 제한 토글 값 공유 수정
xaexunxang Mar 25, 2025
9d3024b
wip: 드래그앤 드롭 구현 중간 저장
xaexunxang Mar 25, 2025
485b8c5
style: ChoiceChip에 buttonClassName 추가
xaexunxang Mar 25, 2025
6d0cf0c
불필요한 폴더 삭제 및 폴더이름 변경
xaexunxang Mar 28, 2025
4940d3a
asset: AddButton2 이미지 추가 및 적용
xaexunxang Mar 31, 2025
b7aa249
refact: img 추가 및 컴포넌트 수정 / fix: IconText CSS 오타 수정
xaexunxang Mar 31, 2025
d6ff8c1
refact: IconButton 정중앙정렬 수정 및 DraggableList 조건 및 이미지 추가
xaexunxang Mar 31, 2025
302ed19
refact: IconButton 속성 수정, Icon 추가 및 스타일 수정
xaexunxang Mar 31, 2025
e021cdc
feat: 필수 입력 사항 예외 처리 구현
xaexunxang Apr 1, 2025
c5fd7cd
fix: 객관식 & 여러개 선택 같은 인풋 객체 공유 문제 해결
xaexunxang Apr 1, 2025
d3831a8
feat: 필수 입력 사항 예외처리 수정
xaexunxang Apr 1, 2025
a0308ef
fix: 옵션 1개 일때 못 지우도록 수정
xaexunxang Apr 1, 2025
12e68ab
feat: 일반 영역 내 스크롤 구현
xaexunxang Apr 1, 2025
4e19b2e
fix: 옵션 컴포넌트 수정 내 객관식 옵션 작성란 오류 해결
xaexunxang Apr 1, 2025
21ecad6
style: 일반 영역 스크롤바 디자인 수정
xaexunxang Apr 1, 2025
ac86f88
asset: 대시보드 사이드바 티켓에 추가 옵션 부착 이미지 변경
xaexunxang Apr 1, 2025
5d19491
fix: Design system error
xaexunxang Apr 1, 2025
ed77565
fix: yarn error
xaexunxang Apr 1, 2025
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
4 changes: 3 additions & 1 deletion design-system/ui/ChoiceChip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ interface ChoiceChipProps {
onSelect: (selected: string) => void;
className?: string;
labelClassName?: string;
buttonClassName?: string;
}

const ChoiceChip = ({ label, options, onSelect, className, labelClassName = '' }: ChoiceChipProps) => {
const ChoiceChip = ({ label, options, onSelect, className, labelClassName = '', buttonClassName = '' }: ChoiceChipProps) => {
const [selected, setSelected] = useState(options[0]);

const handleClick = (option: string) => {
Expand All @@ -26,6 +27,7 @@ const ChoiceChip = ({ label, options, onSelect, className, labelClassName = '' }
className={`
flex justify-center items-center sm:text-xs md:text-sm lg:text-base px-2 rounded-full
${selected === option ? 'bg-white text-black' : 'text-black bg-transparent'}
${buttonClassName}
`}
onClick={() => handleClick(option)}
>
Expand Down
7 changes: 4 additions & 3 deletions design-system/ui/buttons/IconButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ export interface IconButtonProps {
iconPath: React.ReactElement;
onClick: (event: React.MouseEvent<HTMLButtonElement>) => void;
size?: 'small' | 'medium' | 'large';
iconClassName?: string;
}

const IconButton = ({ size = 'medium', iconPath, onClick }: IconButtonProps) => {
const IconButton = ({ size = 'medium', iconPath, onClick, iconClassName }: IconButtonProps) => {
const sizeClasses = {
small: 'w-8 h-8',
medium: 'w-10 h-10',
Expand All @@ -15,9 +16,9 @@ const IconButton = ({ size = 'medium', iconPath, onClick }: IconButtonProps) =>

return (
<button className={`inline-flex items-center justify-center ${sizeClasses[size]}`} onClick={onClick}>
<div className="w-1/2 h-1/2">{iconPath}</div>
<div className={`flex items-center justify-center w-1/2 h-1/2 ${iconClassName}`}>{iconPath}</div>
</button>
);
};

export default IconButton;
export default IconButton;
2 changes: 2 additions & 0 deletions design-system/ui/buttons/TertiaryButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,5 @@ const TertiaryButton = ({ label, type, color, size, onClick, className }: Tertia
};

export default TertiaryButton;


15 changes: 13 additions & 2 deletions design-system/ui/textFields/DefaultTextField.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ButtonHTMLAttributes, ChangeEvent, forwardRef, ReactElement, KeyboardEvent } from 'react';
import { ButtonHTMLAttributes, ChangeEvent, forwardRef, ReactElement, KeyboardEvent, FocusEvent } from 'react';

type Button = ReactElement<ButtonHTMLAttributes<HTMLButtonElement>>;

Expand All @@ -10,10 +10,14 @@ interface DefaultTextFieldProps {
rightContent?: Button;
onChange?: (e: ChangeEvent<HTMLInputElement>) => void;
onKeyDown?: (e: KeyboardEvent<HTMLInputElement>) => void;
onFocus?: (e: FocusEvent<HTMLInputElement>) => void;
onBlur?: (e: FocusEvent<HTMLInputElement>) => void;
placeholder?: string;
errorMessage?: string;
className?: string;
labelClassName?: string;
detailClassName?:string;
disabled?: boolean;
}

const DefaultTextField = forwardRef<HTMLInputElement, DefaultTextFieldProps>(
Expand All @@ -26,26 +30,33 @@ const DefaultTextField = forwardRef<HTMLInputElement, DefaultTextFieldProps>(
rightContent,
onChange,
onKeyDown,
onFocus,
onBlur,
placeholder = '',
className = '',
labelClassName = '',
errorMessage,
detailClassName ='',
disabled = false,
...rest
},
ref
) => {
return (
<div>
<label className={`block px-1 text-sm font-semibold text-gray-700 ${labelClassName}`}>{label}</label>
<label className="block px-1 mb-1 font-medium text-placeholderText sm:text-10 md:text-13">{detail}</label>
<label className={`block px-1 mb-1 font-medium text-placeholderText sm:text-10 md:text-13 ${detailClassName}`}>{detail}</label>
<div className={`flex items-center justify-center `}>
{leftText && <div className="w-24 text-base font-bold whitespace-nowrap">{leftText}</div>}
<input
ref={ref}
value={value}
onChange={onChange}
onKeyDown={onKeyDown}
onFocus={onFocus}
onBlur={onBlur}
placeholder={placeholder}
disabled={disabled}
{...rest}
className={`w-full border border-placeholderText rounded-[3px] px-2 py-1 outline-none placeholder:text-placeholderText text-xs font-light resize-none ${className} ${
errorMessage ? 'border-red-500' : ''
Expand Down
2 changes: 1 addition & 1 deletion design-system/ui/texts/IconText.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const IconText = ({ size = 'medium', iconPath, children, className }: IconTextPr

return (
<button className={`${flexRowStart}`}>
<div className={`w-ful h-full ${sizeClasses[size]}`}>{iconPath}</div>
<div className={`w-full h-full ${sizeClasses[size]}`}>{iconPath}</div>
<span className={`text-left whitespace-nowrap ${className}`}>{children}</span>
</button>
);
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"chromatic": "npx chromatic --project-token=chpt_d390af66464013e"
},
"dependencies": {
"@hello-pangea/dnd": "^18.0.1",
"@hookform/resolvers": "^4.1.3",
"@tanstack/react-query": "^5.61.3",
"autoprefixer": "^10.4.20",
Expand Down Expand Up @@ -62,7 +63,7 @@
"globals": "^15.11.0",
"typescript": "^5.7.3",
"typescript-eslint": "^8.11.0",
"vite": "^5.4.10"
"vite": "^6.2.2"
},
"eslintConfig": {
"extends": [
Expand Down
3 changes: 3 additions & 0 deletions public/assets/dashboard/ticket/AddButton2.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions public/assets/dashboard/ticket/ModifyPencilIcon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions public/assets/dashboard/ticket/Option.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions public/assets/dashboard/ticket/deleteIcon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions public/assets/dashboard/ticket/option(black).svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions public/assets/dashboard/ticket/option(pink).svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 6 additions & 1 deletion src/app/routes/Router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,11 @@ import MailBoxPage from '../../pages/dashboard/ui/mail/MailBoxPage';
import EmailEditPage from '../../pages/dashboard/ui/mail/EmailEditPage';
import PaymentPage from '../../pages/payment/ui/PaymentPage';
import TicketConfirmPage from '../../pages/dashboard/ui/ticket/TIcketConfirmPage';
import TicketOptionPage from '../../pages/dashboard/ui/ticket/TicketOptionPage';
import TicketOptionCreatePage from '../../pages/dashboard/ui/ticket/TicketOptionCreatePage';
import ResponseManagementPage from '../../pages/dashboard/ui/ResponsesManagementPage';
import TicketOptionResponsePage from '../../pages/dashboard/ui/ticket/TicketOptionResponsePage';
import TicketOptionAttachPage from '../../pages/dashboard/ui/ticket/TicketOptionAttachPage';

const mainRoutes = [
{ path: MAIN_ROUTES.main, element: <MainPage />, requiresAuth: false },
Expand Down Expand Up @@ -63,6 +66,8 @@ const dashboardRoutes = [
{ path: DASHBOARD_ROUTES.eventTag, element: <EventTagPage />, requiresAuth: false },
{ path: DASHBOARD_ROUTES.ticketCreate, element: <TicketCreatePage />, requiresAuth: false },
{ path: DASHBOARD_ROUTES.ticket, element: <TicketListPage />, requiresAuth: false },
{ path: DASHBOARD_ROUTES.ticketOption, element: <TicketOptionPage />, requiresAuth: false },
{ path: DASHBOARD_ROUTES.ticketOptionCreate, element: <TicketOptionCreatePage />, requiresAuth: false },
{ path: DASHBOARD_ROUTES.email, element: <EmailPage />, requiresAuth: false },
{ path: DASHBOARD_ROUTES.mailBox, element: <MailBoxPage />, requiresAuth: false },
{ path: DASHBOARD_ROUTES.emailEdit, element: <EmailEditPage />, requiresAuth: false },
Expand All @@ -85,4 +90,4 @@ const router = createBrowserRouter(
}))
);

export default router;
export default router;
2 changes: 2 additions & 0 deletions src/app/routes/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ export const DASHBOARD_ROUTES = {
eventTag: `${MAIN_ROUTES.dashboard}/eventTag`,
ticket: `${MAIN_ROUTES.dashboard}/ticket`,
ticketCreate: `${MAIN_ROUTES.dashboard}/ticket/create`,
ticketOption: `${MAIN_ROUTES.dashboard}/ticket/option`,
ticketOptionCreate: `${MAIN_ROUTES.dashboard}/ticket/option/create`,
email: `${MAIN_ROUTES.dashboard}/email`,
mailBox: `${MAIN_ROUTES.dashboard}/mailBox`,
emailEdit: `${MAIN_ROUTES.dashboard}/edit-email`,
Expand Down
4 changes: 4 additions & 0 deletions src/features/dashboard/ui/Checklist.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ import { getMenuLists } from '../../../shared/types/dashboardType';
import { useParams } from 'react-router-dom';
// import completeCheck from '../../../../public/assets/dashboard/main/Check(complete).svg';

const checkLists = menuLists
.filter(item => ['이벤트 기본 정보', '이벤트 상세와 사진', '티켓 생성하기', '티켓에 추가 옵션 부착'].includes(item.text))
.map(items => ({ text: items.text, path: items.path }));
Comment on lines +7 to +9
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

중복된 checkLists 선언으로 인해 사용되지 않는 변수입니다.
첫 번째 checkLists(7-9행)는 17행 이후에 다시 선언되는 동일 이름의 checkLists 변수로 덮어씌워지고 있어 ESLint에서 미사용 변수가 된 것으로 보입니다. 두 선언 중 하나만 사용하거나, 로직을 통합해 불필요한 중복을 제거하세요.

아래와 같은 방식으로 기존 checkLists 필드를 통합해보세요:

-// 7~9행 선언부 제거
-const checkLists = menuLists
-  .filter(item => ['이벤트 기본 정보', '이벤트 상세와 사진', '티켓 생성하기', '티켓에 추가 옵션 부착'].includes(item.text))
-  .map(items => ({ text: items.text, path: items.path }));

 // 17행 이후 선언을 확장하여 새로운 항목까지 포함
 const checkLists = menuLists
   .filter(item =>
     ['이벤트 기본 정보', '이벤트 상세와 사진', '티켓 생성하기', '티켓에 추가 옵션 부착'].includes(item.text),
   )
   .map(items => ({ text: items.text, path: items.path }));
📝 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 checkLists = menuLists
.filter(item => ['이벤트 기본 정보', '이벤트 상세와 사진', '티켓 생성하기', '티켓에 추가 옵션 부착'].includes(item.text))
.map(items => ({ text: items.text, path: items.path }));
// Removed duplicate checkLists declaration from earlier in the file
// 17행 이후 선언을 확장하여 새로운 항목까지 포함
const checkLists = menuLists
.filter(item =>
['이벤트 기본 정보', '이벤트 상세와 사진', '티켓 생성하기', '티켓에 추가 옵션 부착'].includes(item.text),
)
.map(item => ({ text: item.text, path: item.path }));
🧰 Tools
🪛 ESLint

[error] 7-7: 'checkLists' is assigned a value but never used.

(@typescript-eslint/no-unused-vars)


const CheckList = () => {
const { id } = useParams();
const navigate = useNavigate();
Expand Down
112 changes: 112 additions & 0 deletions src/features/dashboard/ui/DragArea.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import { useNavigate } from 'react-router-dom';
import DraggableList from './DraggableList';
import { Droppable } from '@hello-pangea/dnd';
import AddButton2 from '../../../../public/assets/dashboard/ticket/AddButton2.svg';
import HorizontalCardButton from '../../../../design-system/ui/buttons/HorizontalCardButton';

interface OptionTitle {
id: string;
content: string;
answerToggled: boolean;
responseFormat: string;
}

interface DragAreaProps {
data: {
options: { [key: string]: OptionTitle };
dragAreas: {
[key: string]: {
id: string;
title: string;
optionIds: string[];
};
};
dragAreaOrder: string[];
};
setData: React.Dispatch<React.SetStateAction<DragAreaProps['data']>>;
droppableId: string;
answerToggled: boolean;
responseFormat: string;
ticketSurveyAddButton?: boolean;
Comment on lines +28 to +30
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

미사용 props 제거 또는 활용이 필요합니다.

컴포넌트에서 answerToggledresponseFormat props를 받고 있지만, 이 값들은 DragArea 컴포넌트 내부에서 직접적으로 사용되지 않고 있습니다. DraggableList 컴포넌트에 전달되는 option.answerToggled와 option.responseFormat은 data 객체에서 가져온 값이며, props로 받은 값이 아닙니다.

미사용 props를 제거하거나 다음과 같이 수정하는 것을 권장합니다:

 interface DragAreaProps {
   data: {
     options: { [key: string]: OptionTitle };
     dragAreas: {
       [key: string]: {
         id: string;
         title: string;
         optionIds: string[];
       };
     };
     dragAreaOrder: string[];
   };
   setData: React.Dispatch<React.SetStateAction<DragAreaProps['data']>>;
   droppableId: string;
-  answerToggled: boolean;
-  responseFormat: string;
   ticketSurveyAddButton?: boolean;
 }

 const DragArea = ({
   data,
   setData,
   droppableId,
-  answerToggled,
-  responseFormat,
   ticketSurveyAddButton = true,
 }: DragAreaProps) => {

Also applies to: 37-38

}

const DragArea = ({
data,
setData,
droppableId,
answerToggled,
responseFormat,
ticketSurveyAddButton = true,
}: DragAreaProps) => {
const navigate = useNavigate();
const dragArea = data.dragAreas[droppableId];
const isOptionsArea = droppableId === 'options';
const isTicketArea = droppableId === 'ticket';

const handleDelete = (id: string) => {
if (isTicketArea) {
setData(prev => ({
...prev,
dragAreas: {
...prev.dragAreas,
ticket: {
...prev.dragAreas.ticket,
optionIds: prev.dragAreas.ticket.optionIds.filter(optionId => optionId !== id),
},
},
}));
}
};

return (
<div className="w-full">
<Droppable droppableId={droppableId} isDropDisabled={isOptionsArea}>
{provided => (
<div
ref={provided.innerRef}
{...provided.droppableProps}
className={`${
isOptionsArea
? 'h-80 grid grid-cols-2 gap-2 grid-flow-row content-start'
: isTicketArea
? 'h-48 bg-opacity-5 flex flex-col gap-2 overflow-y-auto [&>*]:flex-shrink-0 scrollbar-thin scrollbar-thumb-gray-200 scrollbar-track-transparent hover:scrollbar-thumb-gray-300'
: 'h-80 grid grid-cols-2 gap-2 grid-flow-row content-start'
}`}
>
{dragArea.optionIds.map((optionId, index) => {
const option = data.options[optionId];
return (
<DraggableList
key={option.id}
id={option.id}
content={option.content}
index={index}
answerToggled={option.answerToggled}
responseFormat={option.responseFormat}
droppableId={droppableId}
isDragDisabled={false}
onDelete={handleDelete}
/>
);
})}
{ticketSurveyAddButton && isOptionsArea && (
<div className="col-span-1 flex items-center h-[3.5rem] bg-deDayBgLight rounded">
<HorizontalCardButton
iconPath={<img src={AddButton2} alt="추가 버튼" />}
className="text-sm !justify-start [&>div]:!justify-start"
label="티켓 설문 새로 생성하기"
onClick={() => {
navigate('/dashboard/ticket/option/create');
}}
/>
</div>
)}
{provided.placeholder}
</div>
)}
</Droppable>
</div>
);
};

export default DragArea;
Loading