-
Notifications
You must be signed in to change notification settings - Fork 0
feat: 티켓 옵션 응답 페이지 퍼블리싱 #75
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
Merged
Merged
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
423e16a
feat: TicketOption 응답 페이지 퍼블리싱
hyeeuncho 384d31d
comment: 주석 추가
hyeeuncho 7e2e599
design: CSS 수정
hyeeuncho f3b5efe
design: CSS 수정
hyeeuncho ce6a4a7
refact: 불필요한 기능 삭제
hyeeuncho 8a1ac01
rename: TicketOption 파일 위치 이동
hyeeuncho File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| import { create } from "zustand"; | ||
|
|
||
| export interface TOption { | ||
| type: string; | ||
| optionName: string; | ||
| required: boolean; | ||
| choices: string[]; | ||
| } | ||
|
|
||
| interface TicketOptionState { | ||
| currentPage: number; | ||
| setCurrentPage: (page: number) => void; | ||
|
|
||
| selectedOptions: { [index: number]: { [key: string]: string | string[] } }; | ||
| setOption: (index: number, optionName: string, value: string | string[]) => void; | ||
| } | ||
|
|
||
| export const useTicketOptionStore = create<TicketOptionState>((set) => ({ | ||
| currentPage: 1, | ||
| setCurrentPage: (page: number) => set({ currentPage: page }), | ||
|
|
||
| selectedOptions: {}, | ||
| setOption: (index, optionName, value) => { | ||
| set((state) => { | ||
| const updatedSelectedOptions = { ...state.selectedOptions }; | ||
| if (!updatedSelectedOptions[index]) { | ||
| updatedSelectedOptions[index] = {}; | ||
| } | ||
| updatedSelectedOptions[index][optionName] = value; | ||
| return { selectedOptions: updatedSelectedOptions }; | ||
| }); | ||
| }, | ||
| })); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,90 @@ | ||
| import { TOption } from "../../dashboard/model/TicketOptionStore"; | ||
| import { useTicketOptionStore } from "../../dashboard/model/TicketOptionStore"; | ||
| import Checkbox from "../../../../design-system/ui/Checkbox"; | ||
|
|
||
| interface TicketOptionProps { | ||
| options: TOption[]; | ||
| } | ||
| const TicketOption = ({ options }: TicketOptionProps) => { | ||
| const { currentPage, selectedOptions, setOption } = useTicketOptionStore(); | ||
| const currentSelectedOptions = selectedOptions[currentPage] || {}; | ||
|
|
||
| const handleChange = (type: string, optionName: string, value: string) => { | ||
| if (type === "text" || type === "single") { | ||
| setOption(currentPage, optionName, value); | ||
| } else if (type === "multiple") { | ||
| const prevValues = (currentSelectedOptions[optionName] as string[]) || []; | ||
| const newValues = prevValues.includes(value) | ||
| ? prevValues.filter((v) => v !== value) | ||
| : [...prevValues, value]; | ||
| setOption(currentPage, optionName, newValues); | ||
| } | ||
| }; | ||
|
|
||
| return ( | ||
| <div className="text-sm md:text-base font-semibold"> | ||
| {options.map((option, index) => ( | ||
| <div key={index} className="mt-4"> | ||
| <p> | ||
| {option.optionName} {option.required && <span className="text-red-500">*</span>} | ||
| </p> | ||
|
|
||
| {option.type === "text" && ( | ||
| <input | ||
| type="text" | ||
| className="w-full p-2 border rounded-md mt-2" | ||
| placeholder="입력해주세요" | ||
| value={currentSelectedOptions[option.optionName] || ""} | ||
| onChange={(e) => handleChange("text", option.optionName, e.target.value)} | ||
| /> | ||
| )} | ||
|
|
||
| {option.type === "single" && | ||
| option.choices.map((choice) => ( | ||
| <Checkbox | ||
| key={choice} | ||
| label={choice} | ||
| checked={currentSelectedOptions[option.optionName] === choice} | ||
| onChange={() => handleChange("single", option.optionName, choice)} | ||
| className="block mt-2" | ||
| /> | ||
| ))} | ||
|
|
||
| {option.type === "multiple" && | ||
| option.choices.map((choice) => ( | ||
| <Checkbox | ||
| key={choice} | ||
| label={choice} | ||
| checked={(currentSelectedOptions[option.optionName] as string[])?.includes(choice)} | ||
| onChange={() => handleChange("multiple", option.optionName, choice)} | ||
| className="block mt-2" | ||
| /> | ||
| ))} | ||
| </div> | ||
| ))} | ||
| </div> | ||
| ); | ||
| }; | ||
| export default TicketOption; | ||
|
|
||
| // 임의 데이터 | ||
| export const options: TOption[] = [ | ||
| { | ||
| type: 'text', | ||
| optionName: '성함을 알려주세요.', | ||
| required: true, | ||
| choices: [''] | ||
| }, | ||
| { | ||
| type: 'single', | ||
| optionName: '티셔츠 사이즈 선택해주세요.', | ||
| required: true, | ||
| choices: ['S', 'M', 'L', 'XL'] | ||
| }, | ||
| { | ||
| type: 'multiple', | ||
| optionName: '선택해주세요.', | ||
| required: false, | ||
| choices: ['1', '2', '3', '4'] | ||
| }, | ||
| ] |
12 changes: 12 additions & 0 deletions
12
src/pages/dashboard/ui/ticket/TicketOptionResponsePage.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| import TicketOption, { options } from "../../../../features/payment/ui/TicketOption"; | ||
| import TicketOptionLayout from "../../../../shared/ui/backgrounds/TicketOptionLayout"; | ||
|
|
||
| const TicketOptionResponsePage = () => { | ||
| return ( | ||
| <TicketOptionLayout ticketAmount={4}> | ||
| <TicketOption options={options}> | ||
| </TicketOption> | ||
| </TicketOptionLayout> | ||
| ); | ||
| }; | ||
| export default TicketOptionResponsePage; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,95 @@ | ||
| import { useNavigate } from "react-router-dom"; | ||
| import Header from "../../../../design-system/ui/Header"; | ||
| import ticket from "../../../../public/assets/dashboard/ticket/Ticket(horizon).svg"; | ||
| import Button from "../../../../design-system/ui/Button"; | ||
| import active from "../../../../public/assets/payment/Active.svg"; | ||
| import inactive from "../../../../public/assets/payment/Inactive.svg"; | ||
| import { useTicketOptionStore } from "../../../features/dashboard/model/TicketOptionStore"; | ||
|
|
||
| interface TicketOptionLayoutProps { | ||
| children: React.ReactNode; | ||
| ticketAmount: number; | ||
| } | ||
|
|
||
| const TicketOptionLayout = ({ children, ticketAmount }: TicketOptionLayoutProps) => { | ||
| const navigate = useNavigate(); | ||
| const {currentPage, setCurrentPage} = useTicketOptionStore(); | ||
| const centerContent = `티켓 옵션 선택 (${currentPage}/${ticketAmount})`; | ||
|
|
||
| //페이지 | ||
| const pageIndicator = Array(ticketAmount).fill(" . "); | ||
| pageIndicator[currentPage - 1] = " - "; | ||
|
|
||
| //버튼 텍스트 | ||
| const isLastPage = currentPage === ticketAmount; | ||
| const buttonText = isLastPage ? "결제하기" : "다음 티켓 옵션 선택하기"; | ||
| const handleNextPage = () => { | ||
| if (isLastPage) { | ||
| {/* 데이터 전송 추가 */} | ||
| } else { | ||
| setCurrentPage(currentPage + 1); | ||
| {/* 데이터 전송 추가 */} | ||
| } | ||
| }; | ||
| return ( | ||
| <div className="relative flex flex-col"> | ||
| {/* 헤더 영역 */} | ||
| <div className="w-full h-28 md:h-36 bg-gradient-to-br from-[#FF5593] to-[rgb(255,117,119)] rounded-b-[20px] z-10"> | ||
| <Header | ||
| leftButtonLabel="<" | ||
| leftButtonClassName="text-2xl z-30 font-semibold" | ||
| leftButtonClick={() => navigate(-1)} | ||
| centerContent={centerContent} | ||
| color="white" | ||
| /> | ||
| {ticketAmount > 1 && ( | ||
| <div className="flex justify-center gap-2 mt-4 md:mt-12"> | ||
| {Array.from({ length: ticketAmount }, (_, index) => ( | ||
| <img | ||
| key={index} | ||
| src={index + 1 === currentPage ? active : inactive} | ||
| className="object-contain" | ||
| /> | ||
| ))} | ||
| </div> | ||
| )} | ||
| </div> | ||
| {/* 티켓 정보 영역 */} | ||
| <div className="flex flex-col justify-between w-[90%] h-36 md:h-40 bg-white rounded-md mt-8 mx-auto z-10 shadow-md px-6 md:px-8 py-5 md:py-6"> | ||
| <div className="flex flex-row justify-between items-center"> | ||
| <div className="flex gap-4"> | ||
| <img src={ticket} alt="ticket logo" className="w-7" /> | ||
| <div> | ||
| <p className="font-bold text-base md:text-lg">콘서트 티켓</p> | ||
| </div> | ||
| </div> | ||
| </div> | ||
| <div> | ||
| <p className="text-xs md:text-sm">티켓에 대한 설명.티켓에 대한 설명.티켓에 대한 설명.티켓에 대한 설명.티켓에 대티켓에 대한 설명.티켓에 대한 설명.티켓에 대한 설명.티켓에 대한 설명.한 설명.</p> | ||
| </div> | ||
| </div> | ||
|
|
||
| <div className="flex flex-col mt-8 mx-auto w-[85%]"> | ||
| <p className="font-bold text-sm md:text-base">추가 옵션</p> | ||
| <p className="text-xs md:text-sm text-gray-500 mt-2"> | ||
| 구매하는 티켓에 추가적으로 선택할 수 있는 옵션들이 있습니다. | ||
| </p> | ||
| </div> | ||
|
|
||
| {/* 내용 영역 */} | ||
| <div className="flex flex-col w-[85%] mx-auto mt-4"> | ||
| {children} | ||
| <div className="flex flex-grow" /> | ||
| <div className="w-full p-6"> | ||
| <Button | ||
| label={buttonText} | ||
| onClick={handleNextPage} | ||
| className="w-full h-12 rounded-full" | ||
| /> | ||
| </div> | ||
| </div> | ||
| </div> | ||
| ); | ||
| }; | ||
|
|
||
| export default TicketOptionLayout; | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이 함수에서
resetOptions(currentPage)를 호출하면 현재 페이지의 데이터가 모두 리셋하도록 설정하셨는데, 사용자가 페이지를 이동할 때 이전 데이터를 유지하는게 일반적인걸로 알고 있어요!그래서 혹시 리셋 기능을 구현한 이유가 있을까요??