-
Notifications
You must be signed in to change notification settings - Fork 1
#3 [CHORE] 온보딩 페이지 마무리 #8
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
Conversation
|
Caution Review failedThe pull request is closed. Walkthrough온보딩 UI와 파일 업로드 경로/요청 방식을 변경합니다. 버튼 색상 및 진행바 색상을 업데이트하고 MultiSelect에 선택 상태와 버튼 그리드를 도입했으며, SingleSelect 기본 옵션과 채팅 업로드 메시지를 수정하고 파일 업로드를 axios + 환경변수 URL로 전환했습니다. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant U as User (UI)
participant C as FileSendButton
participant S as UploadEndpoint (env VITE_UPLOAD_URL)
rect rgb(230,245,255)
note right of C: 새 흐름 (axios + env URL)
end
U->>C: 파일 선택 + 전송
C->>S: POST formData (axios.post(uploadUrl,...))
S-->>C: 200 OK (업로드 성공)
C-->>U: 업로드 성공 처리 / 메시지 표시
(참고: 이전 흐름은 내부 Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes
Possibly related PRs
Pre-merge checks and finishing touches❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
📜 Recent review detailsConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro 📒 Files selected for processing (2)
Comment |
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.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
src/components/onboarding/SingleSelect.tsx (1)
11-29: 선택된 값을 수집할 수 없는 구조입니다.현재
select요소가value와onChange속성 없이 비제어 컴포넌트로 구현되어 있어, 사용자가 선택한 값을 부모 컴포넌트(Onboarding)에서 가져올 수 없습니다. 온보딩 플로우가 완료되어도 실제 폼 데이터를 수집하거나 제출할 수 없게 됩니다.다음과 같이 제어 컴포넌트 패턴으로 수정하세요:
-const SingleSelect = ({ title }: { title: string }) => { +interface SingleSelectProps { + title: string; + value: string; + onChange: (value: string) => void; +} + +const SingleSelect = ({ title, value, onChange }: SingleSelectProps) => { return ( <div className="flex flex-col gap-6"> {/* 제목 */} <p className="text-[24px] font-semibold">{title} 선택하세요</p> {/* 단일 선택 버튼 */} - <select className="rounded-xl border border-gray-200 px-2 py-4 text-[20px] outline-none focus:border-gray-200"> + <select + value={value} + onChange={(e) => onChange(e.target.value)} + className="rounded-xl border border-gray-200 px-2 py-4 text-[20px] outline-none focus:border-gray-200" + > <option className="" value=""> 선택하세요 </option>src/pages/onboarding/Onboarding.tsx (1)
10-46: 온보딩 폼 데이터를 수집하고 관리하는 상태가 없습니다.현재 구조에서는 사용자가 각 단계에서 입력한 값들(업종, 업체명, 위치, 매출, 타겟층)을 저장하거나 수집할 방법이 없습니다.
handleComplete함수는 단순히 페이지만 이동하며, 실제 온보딩 데이터는 모두 손실됩니다. 이는 온보딩 기능을 완전히 무용지물로 만드는 치명적인 문제입니다.다음과 같이 폼 상태 관리를 추가하세요:
const Onboarding = () => { const navigate = useNavigate(); const [currentStep, setCurrentStep] = useState(1); const totalSteps = 6; + + // 폼 데이터 상태 + const [formData, setFormData] = useState({ + businessType: '', + businessName: '', + location: '', + averageRevenue: '', + targetRevenue: '', + targetAudience: [] as string[], + }); const handleComplete = () => { + // 폼 데이터 유효성 검사 + console.log('Submitting onboarding data:', formData); + // TODO: API 호출로 데이터 전송 navigate('/chat'); }; const renderStepContent = () => { switch (currentStep) { case 1: - return <SingleSelect key="step1" title="업종을" />; + return ( + <SingleSelect + key="step1" + title="업종을" + value={formData.businessType} + onChange={(value) => setFormData(prev => ({ ...prev, businessType: value }))} + /> + ); case 2: - return <TextInput key="step2" title="업체명을" placeholder="" />; + return ( + <TextInput + key="step2" + title="업체명을" + placeholder="" + value={formData.businessName} + onChange={(value) => setFormData(prev => ({ ...prev, businessName: value }))} + /> + ); // ... 나머지 케이스도 동일하게 수정이후
TextInput과PriceInput컴포넌트도SingleSelect,MultiSelect와 같이 제어 컴포넌트 패턴으로 수정해야 합니다.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
src/components/onboarding/FormButton.tsx(1 hunks)src/components/onboarding/MultiSelect.tsx(1 hunks)src/components/onboarding/SingleSelect.tsx(1 hunks)src/pages/chat/ChatPageTest.tsx(1 hunks)src/pages/onboarding/Onboarding.tsx(2 hunks)
🔇 Additional comments (4)
src/components/onboarding/SingleSelect.tsx (1)
2-8: 업종 옵션 데이터가 적절합니다.한국 사업자 유형에 맞는 카테고리로 잘 업데이트되었습니다.
src/pages/chat/ChatPageTest.tsx (1)
119-129: LGTM!파일 업로드 성공 메시지가 더 명확하고 사용자 친화적으로 개선되었습니다.
src/pages/onboarding/Onboarding.tsx (1)
13-13: 단계 수 증가 및 테마 색상 변경이 일관성 있게 적용되었습니다.6단계로 확장된 온보딩 플로우와
#0D2D84색상 테마가FormButton,MultiSelect등 다른 컴포넌트들과 일관되게 적용되어 있습니다.Also applies to: 61-61
src/components/onboarding/FormButton.tsx (1)
3-3: LGTM!버튼 색상이 새로운 테마 색상(
#0D2D84)으로 일관성 있게 업데이트되었습니다. 이는 진행 상황 표시줄 및 선택된 옵션의 색상과 일치합니다.
| interface MultiSelectProps { | ||
| title: string; | ||
| options?: string[]; | ||
| } | ||
|
|
||
| const MultiSelect = ({ | ||
| title, | ||
| options = ['10대 이하', '20대', '30대', '40대', '50대', '60대 이상'], | ||
| }: MultiSelectProps) => { | ||
| const [selectedOptions, setSelectedOptions] = useState<string[]>([]); | ||
|
|
||
| const toggleOption = (option: string) => { | ||
| setSelectedOptions((prev) => | ||
| prev.includes(option) ? prev.filter((item) => item !== option) : [...prev, option], | ||
| ); | ||
| }; |
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.
선택된 값을 부모 컴포넌트로 전달하는 방법이 없습니다.
selectedOptions 상태가 컴포넌트 내부에만 존재하고 부모로 노출되지 않아, Onboarding.tsx에서 사용자가 선택한 타겟층 데이터를 수집할 수 없습니다. 이는 폼 제출 시 필수 데이터가 누락되는 치명적인 문제입니다.
다음과 같이 onChange 콜백을 추가하세요:
interface MultiSelectProps {
title: string;
options?: string[];
+ value?: string[];
+ onChange?: (selected: string[]) => void;
}
const MultiSelect = ({
title,
options = ['10대 이하', '20대', '30대', '40대', '50대', '60대 이상'],
+ value = [],
+ onChange,
}: MultiSelectProps) => {
- const [selectedOptions, setSelectedOptions] = useState<string[]>([]);
+ const selectedOptions = value;
const toggleOption = (option: string) => {
- setSelectedOptions((prev) =>
- prev.includes(option) ? prev.filter((item) => item !== option) : [...prev, option],
- );
+ const newSelection = selectedOptions.includes(option)
+ ? selectedOptions.filter((item) => item !== option)
+ : [...selectedOptions, option];
+ onChange?.(newSelection);
};📝 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.
| interface MultiSelectProps { | |
| title: string; | |
| options?: string[]; | |
| } | |
| const MultiSelect = ({ | |
| title, | |
| options = ['10대 이하', '20대', '30대', '40대', '50대', '60대 이상'], | |
| }: MultiSelectProps) => { | |
| const [selectedOptions, setSelectedOptions] = useState<string[]>([]); | |
| const toggleOption = (option: string) => { | |
| setSelectedOptions((prev) => | |
| prev.includes(option) ? prev.filter((item) => item !== option) : [...prev, option], | |
| ); | |
| }; | |
| interface MultiSelectProps { | |
| title: string; | |
| options?: string[]; | |
| value?: string[]; | |
| onChange?: (selected: string[]) => void; | |
| } | |
| const MultiSelect = ({ | |
| title, | |
| options = ['10대 이하', '20대', '30대', '40대', '50대', '60대 이상'], | |
| value = [], | |
| onChange, | |
| }: MultiSelectProps) => { | |
| const selectedOptions = value; | |
| const toggleOption = (option: string) => { | |
| const newSelection = selectedOptions.includes(option) | |
| ? selectedOptions.filter((item) => item !== option) | |
| : [...selectedOptions, option]; | |
| onChange?.(newSelection); | |
| }; |
🤖 Prompt for AI Agents
In src/components/onboarding/MultiSelect.tsx around lines 3 to 18, the component
keeps selectedOptions internal and never exposes changes to the parent; add an
onChange prop to MultiSelectProps (e.g., onChange?: (selected: string[]) =>
void), accept it in the component signature, and invoke it whenever selection
changes (either call onChange(updatedSelected) inside toggleOption after
updating state or use a useEffect watching selectedOptions to call onChange).
Ensure the prop is properly typed and has no-op fallback if undefined.
| {/* 6개 그리드 버튼 */} | ||
| <div className="grid grid-cols-2 gap-4 md:grid-cols-3"> | ||
| {options.map((option, index) => ( | ||
| <button | ||
| key={index} | ||
| onClick={() => toggleOption(option)} | ||
| className={`rounded-xl border-2 px-4 py-8 text-[18px] font-medium duration-200 ${ | ||
| selectedOptions.includes(option) | ||
| ? 'border-[#0D2D84] bg-[#0D2D84] text-white' | ||
| : 'border-gray-200 bg-white text-gray-700 hover:bg-gray-50' | ||
| }`} | ||
| > | ||
| {option} | ||
| </button> | ||
| ))} | ||
| </div> |
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.
접근성 개선을 위해 버튼 상태 속성을 추가하세요.
선택 가능한 버튼들에 aria-pressed 속성이 없어 스크린 리더 사용자가 현재 선택 상태를 파악하기 어렵습니다.
다음과 같이 접근성 속성을 추가하세요:
<button
key={index}
onClick={() => toggleOption(option)}
+ aria-pressed={selectedOptions.includes(option)}
className={`rounded-xl border-2 px-4 py-8 text-[18px] font-medium duration-200 ${
selectedOptions.includes(option)
? 'border-[#0D2D84] bg-[#0D2D84] text-white'
: 'border-gray-200 bg-white text-gray-700 hover:bg-gray-50'
}`}
>📝 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.
| {/* 6개 그리드 버튼 */} | |
| <div className="grid grid-cols-2 gap-4 md:grid-cols-3"> | |
| {options.map((option, index) => ( | |
| <button | |
| key={index} | |
| onClick={() => toggleOption(option)} | |
| className={`rounded-xl border-2 px-4 py-8 text-[18px] font-medium duration-200 ${ | |
| selectedOptions.includes(option) | |
| ? 'border-[#0D2D84] bg-[#0D2D84] text-white' | |
| : 'border-gray-200 bg-white text-gray-700 hover:bg-gray-50' | |
| }`} | |
| > | |
| {option} | |
| </button> | |
| ))} | |
| </div> | |
| {/* 6개 그리드 버튼 */} | |
| <div className="grid grid-cols-2 gap-4 md:grid-cols-3"> | |
| {options.map((option, index) => ( | |
| <button | |
| key={index} | |
| onClick={() => toggleOption(option)} | |
| aria-pressed={selectedOptions.includes(option)} | |
| className={`rounded-xl border-2 px-4 py-8 text-[18px] font-medium duration-200 ${ | |
| selectedOptions.includes(option) | |
| ? 'border-[#0D2D84] bg-[#0D2D84] text-white' | |
| : 'border-gray-200 bg-white text-gray-700 hover:bg-gray-50' | |
| }`} | |
| > | |
| {option} | |
| </button> | |
| ))} | |
| </div> |
🤖 Prompt for AI Agents
In src/components/onboarding/MultiSelect.tsx around lines 24 to 39, the
selectable buttons are missing ARIA state which makes it hard for screen reader
users to know which options are selected; add an aria-pressed attribute to each
button bound to the selection state (e.g.,
aria-pressed={selectedOptions.includes(option)}) so the button exposes its
pressed/selected state, and also ensure the element is a proper button
(type="button") to avoid form submission side-effects.
Summary by CodeRabbit
릴리스 노트
새로운 기능
스타일
기타