-
Notifications
You must be signed in to change notification settings - Fork 4
feat: 톡픽 상세 조회 모바일 컴포넌트 구현 #307
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
Changes from all commits
9a9f88b
f13b0f2
adda972
facdd05
0e6efe4
a2330c1
6ad78aa
38b2c52
c9e5967
071ce13
3029212
25ea527
a6b25ed
300456e
00705ac
b64bfb2
617d4e8
6cddd7b
5227975
da4b270
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| import { css } from '@emotion/react'; | ||
| import color from '@/styles/color'; | ||
| import typo from '@/styles/typo'; | ||
|
|
||
| export const summaryItemStyling = css(typo.Mobile.Text.Medium_12, { | ||
| display: 'flex', | ||
| justifyContent: 'center', | ||
| alignItems: 'center', | ||
| padding: '4px 20px 4px 5px', | ||
| gap: '13px', | ||
| backgroundColor: color.WT_VIOLET, | ||
| color: color.BK, | ||
| borderRadius: '30px', | ||
| }); | ||
|
|
||
| export const numberItemStyling = css(typo.Mobile.Text.Medium_12, { | ||
| display: 'flex', | ||
| justifyContent: 'center', | ||
| alignItems: 'center', | ||
| flexShrink: 0, | ||
| width: '18px', | ||
| height: '18px', | ||
| backgroundColor: color.MAIN, | ||
| color: color.WT, | ||
| borderRadius: '50%', | ||
| }); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| import React, { ReactNode } from 'react'; | ||
| import { numberItemStyling, summaryItemStyling } from './SummaryItem.style'; | ||
|
|
||
| export interface SummaryItemProps { | ||
| itemNumber?: '1' | '2' | '3'; | ||
| children?: ReactNode; | ||
| } | ||
|
|
||
| const SummaryItem = ({ itemNumber = '1', children }: SummaryItemProps) => ( | ||
| <div css={summaryItemStyling}> | ||
| <div css={numberItemStyling}>{itemNumber}</div> | ||
| {children} | ||
| </div> | ||
| ); | ||
|
|
||
| export default SummaryItem; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,76 @@ | ||
| import { css } from '@emotion/react'; | ||
| import color from '@/styles/color'; | ||
| import typo from '@/styles/typo'; | ||
|
|
||
| export const reportModalStyling = css({ | ||
| display: 'flex', | ||
| flexDirection: 'column', | ||
| alignItems: 'center', | ||
| gap: '20px', | ||
| }); | ||
|
|
||
| export const reportTextWrapper = css({ | ||
| display: 'flex', | ||
| flexDirection: 'column', | ||
| alignItems: 'center', | ||
| gap: '6px', | ||
| }); | ||
|
|
||
| export const reportTextStyling = css(typo.Main.SemiBold, { | ||
| color: color.BK, | ||
| }); | ||
|
|
||
| export const buttonWrapperStyling = css({ | ||
| display: 'grid', | ||
| gridTemplateColumns: 'repeat(2, 1fr)', | ||
| gridGap: '7px', | ||
| }); | ||
|
|
||
| export const reportBtnWrapperStyling = css({ | ||
| display: 'flex', | ||
| justifyContent: 'space-between', | ||
| width: '100%', | ||
| }); | ||
|
|
||
| export const buttonStyling = css(typo.Mobile.Text.Medium_14, { | ||
| display: 'flex', | ||
| width: '143px', | ||
| height: '50px', | ||
| alignItems: 'center', | ||
| padding: '0 16px', | ||
| borderRadius: '8px', | ||
| backgroundColor: color.GY[5], | ||
| color: color.GY[6], | ||
| cursor: 'pointer', | ||
| }); | ||
|
|
||
| export const selectedButtonStyling = css({ | ||
| backgroundColor: color.WT_VIOLET, | ||
| color: color.MAIN, | ||
| outline: `2px solid ${color.MAIN}`, | ||
| }); | ||
|
|
||
| export const getButtonStyling = (selected: boolean) => | ||
| css({ | ||
| width: '293px', | ||
| height: '44px', | ||
| borderRadius: '12px', | ||
| backgroundColor: selected ? color.MAIN : color.GY[2], | ||
| }); | ||
|
Comment on lines
+53
to
+59
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion 선택된 버튼의 너비가 일관성이 없습니다.
export const getButtonStyling = (selected: boolean) =>
css({
- width: '293px',
+ width: '120px',
height: '44px',
borderRadius: '12px',
backgroundColor: selected ? color.MAIN : color.GY[2],
});
|
||
|
|
||
| export const reportInputStyling = css(typo.Mobile.Text.SemiBold_14, { | ||
| width: '100%', | ||
| padding: '6px 0', | ||
| outline: '0', | ||
| border: 'none', | ||
| borderBottom: `1px solid ${color.GY[3]}`, | ||
| color: color.BK, | ||
|
|
||
| ':-webkit-autofill': { | ||
| boxShadow: '0 0 0px 1000px white inset', | ||
| }, | ||
|
|
||
| '&::placeholder': { | ||
| color: color.GY[1], | ||
| }, | ||
| }); | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,75 @@ | ||||||||||||||||||||||||||||||||||||
| import React, { useState } from 'react'; | ||||||||||||||||||||||||||||||||||||
| import { MobileReport } from '@/assets'; | ||||||||||||||||||||||||||||||||||||
| import Modal from '@/components/mobile/atoms/Modal/Modal'; | ||||||||||||||||||||||||||||||||||||
| import Button from '@/components/mobile/atoms/Button/Button'; | ||||||||||||||||||||||||||||||||||||
| import { reportOptions } from '@/constants/reportOption'; | ||||||||||||||||||||||||||||||||||||
| import * as S from './ReportModal.style'; | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| export interface ReportModalProps { | ||||||||||||||||||||||||||||||||||||
| isOpen?: boolean; | ||||||||||||||||||||||||||||||||||||
| onConfirm?: (reason: string) => void; | ||||||||||||||||||||||||||||||||||||
| onClose?: () => void; | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
Comment on lines
+8
to
+12
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Props 타입에 필수값 표시가 필요합니다. Props 타입에서 선택적 필드( export interface ReportModalProps {
- isOpen?: boolean;
- onConfirm?: (reason: string) => void;
- onClose?: () => void;
+ isOpen: boolean;
+ onConfirm: (reason: string) => void;
+ onClose: () => void;
}📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| const ReportModal = ({ isOpen, onConfirm, onClose }: ReportModalProps) => { | ||||||||||||||||||||||||||||||||||||
| const [reportReason, setReportReason] = useState<string>(''); | ||||||||||||||||||||||||||||||||||||
| const [otherReason, setOtherReason] = useState<string>(''); | ||||||||||||||||||||||||||||||||||||
| const finalReportReason: string = | ||||||||||||||||||||||||||||||||||||
| reportReason === '기타' ? otherReason : reportReason; | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| const handleOtherReportReason = (e: React.ChangeEvent<HTMLInputElement>) => { | ||||||||||||||||||||||||||||||||||||
| setOtherReason(e.target.value); | ||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| const handleConfirm = () => { | ||||||||||||||||||||||||||||||||||||
| if (!finalReportReason.trim()) return; | ||||||||||||||||||||||||||||||||||||
| onConfirm?.(finalReportReason); | ||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||
|
Comment on lines
+24
to
+27
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion handleConfirm 함수의 오류 처리가 필요합니다. 빈 문자열 체크만 하고 있으며, 최소/최대 길이 검증이나 사용자 피드백이 없습니다. const handleConfirm = () => {
- if (!finalReportReason.trim()) return;
+ if (!finalReportReason.trim()) {
+ showToastModal('신고 사유를 입력해주세요.');
+ return;
+ }
+ if (reportReason === '기타' && otherReason.length < 10) {
+ showToastModal('신고 사유는 최소 10자 이상 입력해주세요.');
+ return;
+ }
onConfirm?.(finalReportReason);
};📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| return ( | ||||||||||||||||||||||||||||||||||||
| <Modal action="share" isOpen={isOpen} onClose={onClose}> | ||||||||||||||||||||||||||||||||||||
| <div css={S.reportModalStyling}> | ||||||||||||||||||||||||||||||||||||
| <div css={S.reportTextWrapper}> | ||||||||||||||||||||||||||||||||||||
| <MobileReport /> | ||||||||||||||||||||||||||||||||||||
| <div css={S.reportTextStyling}>신고사유 선택</div> | ||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||
| <div css={S.buttonWrapperStyling}> | ||||||||||||||||||||||||||||||||||||
| {reportOptions.map((option) => ( | ||||||||||||||||||||||||||||||||||||
| <button | ||||||||||||||||||||||||||||||||||||
| type="button" | ||||||||||||||||||||||||||||||||||||
| value={option.value} | ||||||||||||||||||||||||||||||||||||
| key={option.value} | ||||||||||||||||||||||||||||||||||||
| onClick={() => { | ||||||||||||||||||||||||||||||||||||
| setReportReason(option.value); | ||||||||||||||||||||||||||||||||||||
| setOtherReason(''); | ||||||||||||||||||||||||||||||||||||
| }} | ||||||||||||||||||||||||||||||||||||
| css={[ | ||||||||||||||||||||||||||||||||||||
| S.buttonStyling, | ||||||||||||||||||||||||||||||||||||
| option.value === reportReason && S.selectedButtonStyling, | ||||||||||||||||||||||||||||||||||||
| ]} | ||||||||||||||||||||||||||||||||||||
| > | ||||||||||||||||||||||||||||||||||||
| {option.label} | ||||||||||||||||||||||||||||||||||||
| </button> | ||||||||||||||||||||||||||||||||||||
| ))} | ||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||
| {reportReason === '기타' && ( | ||||||||||||||||||||||||||||||||||||
| <input | ||||||||||||||||||||||||||||||||||||
| css={S.reportInputStyling} | ||||||||||||||||||||||||||||||||||||
| placeholder="신고사유를 작성해주세요." | ||||||||||||||||||||||||||||||||||||
| onChange={handleOtherReportReason} | ||||||||||||||||||||||||||||||||||||
| /> | ||||||||||||||||||||||||||||||||||||
|
Comment on lines
+56
to
+60
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion 접근성을 개선하세요. 입력 필드에 적절한 레이블과 ARIA 속성이 필요합니다. +<label htmlFor="otherReason" css={S.srOnly}>
+ 기타 신고 사유
+</label>
<input
+ id="otherReason"
css={S.reportInputStyling}
placeholder="신고사유를 작성해주세요."
+ aria-label="기타 신고 사유"
onChange={handleOtherReportReason}
/>📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||
| )} | ||||||||||||||||||||||||||||||||||||
|
Comment on lines
+55
to
+61
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion 입력 필드에 대한 유효성 검사를 추가하세요. 기타 사유 입력 시 최소/최대 길이 제한과 같은 유효성 검사가 필요합니다. <input
css={S.reportInputStyling}
placeholder="신고사유를 작성해주세요."
+ minLength={10}
+ maxLength={200}
onChange={handleOtherReportReason}
+ required
/>📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||
| <Button | ||||||||||||||||||||||||||||||||||||
| size="large" | ||||||||||||||||||||||||||||||||||||
| variant="primary" | ||||||||||||||||||||||||||||||||||||
| onClick={handleConfirm} | ||||||||||||||||||||||||||||||||||||
| css={S.getButtonStyling(!!finalReportReason)} | ||||||||||||||||||||||||||||||||||||
| > | ||||||||||||||||||||||||||||||||||||
| 설정 완료 | ||||||||||||||||||||||||||||||||||||
| </Button> | ||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||
| </Modal> | ||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| export default ReportModal; | ||||||||||||||||||||||||||||||||||||
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.
children을 ReactNode로 열린 구조로 선언했다면, itemNumber의 경우에도 열린 구조로 처리를 하셔도 좋을 거 같아요.
1,2,3 에 한정된 컴포넌트를 구현한다면 지금 방식도 괜찮아보입니다!