-
Notifications
You must be signed in to change notification settings - Fork 1
[FE-Fix] SharedEventDto 통합 및 중복 제거 #299
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
WalkthroughThis pull request introduces adjustments to pagination rendering and shared event type validation. The Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant OngoingScheduleList
participant Pagination
User->>OngoingScheduleList: Request schedule list
OngoingScheduleList->>Pagination: Render Pagination component
Pagination->>Pagination: Check if totalPages > 1
alt totalPages > 1
Pagination->>User: Display pagination controls
else
Pagination->>User: Skip rendering controls
end
Possibly related PRs
Suggested labels
Suggested reviewers
Poem
📜 Recent review detailsConfiguration used: CodeRabbit UI 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
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: 0
🧹 Nitpick comments (3)
frontend/src/features/shared-schedule/ui/OngoingSchedules/OngoingScheduleList.tsx (1)
37-37: Address the TODO comment regarding useEffect.The comment suggests that useEffect could be replaced. Consider using React Query's built-in state management or React's useState if the effect is for simple state management.
Would you like me to suggest alternative approaches to replace the useEffect?
frontend/src/features/shared-schedule/ui/FinishedSchedules/FinishedScheduleListItem.tsx (2)
18-19: Remove commented-out code.The startDate and endDate props are commented out and no longer used. Remove them to maintain clean code.
- // startDate: Date; - // endDate: Date;
58-61: Add error handling for invalid date strings.The current implementation assumes valid date strings. Consider adding error handling:
- {sharedEventDto ? getDateTimeRangeString( - new Date(sharedEventDto.startDateTime), - new Date(sharedEventDto.endDateTime)) + {sharedEventDto ? (() => { + try { + return getDateTimeRangeString( + new Date(sharedEventDto.startDateTime), + new Date(sharedEventDto.endDateTime) + ); + } catch (error) { + console.error('Invalid date format:', error); + return '날짜 형식이 잘못되었습니다'; + } + })()
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📥 Commits
Reviewing files that changed from the base of the PR and between d88c128 and a8600f2ad6f6ae97cdf9f7132708fc710a911d02.
📒 Files selected for processing (6)
frontend/src/components/Pagination/index.tsx(1 hunks)frontend/src/features/shared-schedule/model/SharedEventDto.ts(1 hunks)frontend/src/features/shared-schedule/model/finishedSchedules.ts(1 hunks)frontend/src/features/shared-schedule/model/upcomingSchedules.ts(1 hunks)frontend/src/features/shared-schedule/ui/FinishedSchedules/FinishedScheduleListItem.tsx(3 hunks)frontend/src/features/shared-schedule/ui/OngoingSchedules/OngoingScheduleList.tsx(1 hunks)
✅ Files skipped from review due to trivial changes (2)
- frontend/src/features/shared-schedule/model/upcomingSchedules.ts
- frontend/src/features/shared-schedule/model/SharedEventDto.ts
🔇 Additional comments (3)
frontend/src/components/Pagination/index.tsx (1)
29-29: LGTM! Improved UX by conditionally rendering pagination.The addition of the
totalPages > 1condition ensures that pagination controls are only shown when there are multiple pages, providing a cleaner user interface.frontend/src/features/shared-schedule/ui/OngoingSchedules/OngoingScheduleList.tsx (1)
60-70: LGTM! Enhanced pagination with prefetching and state management.The implementation includes performance optimization through prefetching and proper state management by resetting selectedIndex on page change.
frontend/src/features/shared-schedule/model/finishedSchedules.ts (1)
3-3: LGTM! Improved schema organization and type safety.Good improvements:
- Centralized SharedEventDtoSchema reduces duplication
- Explicit null handling in the schema improves type safety
Also applies to: 9-9
a8600f2 to
82c8ec9
Compare
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: 0
🧹 Nitpick comments (8)
frontend/src/features/discussion/ui/DiscussionInviteCard/index.css.ts (1)
61-64: Add unit to width propertyThe width property is specified as a numeric value without units. While some CSS-in-JS libraries may default to pixels, it's best practice to explicitly include the unit for clarity.
export const timerButtonStyle = style({ justifyContent: 'center', - width: 126.77, + width: '126.77px', });frontend/src/features/discussion/ui/DiscussionInviteCard/TimerButton.tsx (2)
37-37: Enhance time display format for better readabilityCurrently, the timer displays time only in seconds, which may not be intuitive for users when longer times are displayed. Consider formatting the time in a more readable format like minutes:seconds.
- {`${Math.floor(remainingTime / 1000)}초`} + {`${Math.floor((remainingTime / 1000) / 60)}분 ${Math.floor((remainingTime / 1000) % 60)}초`}Or alternatively, use a utility function for a cleaner implementation:
const formatTime = (ms: number): string => { const seconds = Math.floor(ms / 1000); const minutes = Math.floor(seconds / 60); const remainingSeconds = seconds % 60; return `${minutes}분 ${remainingSeconds}초`; }; // Then in the render: {formatTime(remainingTime)}
19-29: Consider potential performance optimizationThe interval is set to update every second, which is fine for most cases. However, for performance optimization, you could consider reducing the update frequency when the remaining time is large (e.g., update every minute when more than an hour remains).
frontend/src/features/discussion/ui/DiscussionInviteCard/SubmitForm.tsx (4)
12-12: Remove unnecessary semicolon.There is a stray semicolon on line 12 that should be removed.
-import TimerButton from './TimerButton'; -; +import TimerButton from './TimerButton';
46-50: Add validation for password format.The placeholder suggests a numeric password of 4-6 digits, but there's no validation to enforce this requirement. Consider adding proper input type and pattern validation.
<input className={inputStyle} onChange={(e) => setPassword(e.target.value)} placeholder='숫자 4~6자리 비밀번호' + type="password" + pattern="[0-9]{4,6}" + maxLength={6} + minLength={4} + inputMode="numeric" />
38-38: Add user notification for timeout.When an error occurs, you set a 5-minute timeout but don't inform the user. Consider adding a notification to explain this timeout to the user.
onError: () => { setUnlockDT(new Date(Date.now() + 5 * MINUTE_IN_MILLISECONDS)); + addNoti({ + type: 'error', + title: '오류가 발생했습니다', + description: '5분 후에 다시 시도해주세요.' + }); },
28-28: Consider adding password validation when submitting.You're submitting an empty password as undefined, but it might be better to validate the password format before submission, especially since the placeholder indicates a specific format requirement.
frontend/src/features/discussion/ui/DiscussionInviteCard/index.tsx (1)
28-28: Remove or relocate TODO comment.This TODO comment about input validation is now misplaced since the input handling has been moved to the SubmitForm component. Consider removing it or moving it to the appropriate location.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📥 Commits
Reviewing files that changed from the base of the PR and between a8600f2ad6f6ae97cdf9f7132708fc710a911d02 and 82c8ec9f0119bf1c923a5da6c7398fe7cc582692.
📒 Files selected for processing (13)
frontend/src/components/Pagination/index.tsx(1 hunks)frontend/src/features/discussion/model/invitation.ts(1 hunks)frontend/src/features/discussion/ui/DiscussionInviteCard/SubmitForm.tsx(1 hunks)frontend/src/features/discussion/ui/DiscussionInviteCard/TimerButton.tsx(1 hunks)frontend/src/features/discussion/ui/DiscussionInviteCard/index.css.ts(1 hunks)frontend/src/features/discussion/ui/DiscussionInviteCard/index.tsx(3 hunks)frontend/src/features/shared-schedule/model/SharedEventDto.ts(1 hunks)frontend/src/features/shared-schedule/model/finishedSchedules.ts(1 hunks)frontend/src/features/shared-schedule/model/upcomingSchedules.ts(1 hunks)frontend/src/features/shared-schedule/ui/FinishedSchedules/FinishedScheduleListItem.tsx(3 hunks)frontend/src/features/shared-schedule/ui/OngoingSchedules/OngoingScheduleList.tsx(1 hunks)frontend/src/pages/DiscussionPage/DiscussionInvitePage/index.tsx(2 hunks)frontend/src/utils/date/time.ts(1 hunks)
✅ Files skipped from review due to trivial changes (1)
- frontend/src/utils/date/time.ts
🚧 Files skipped from review as they are similar to previous changes (6)
- frontend/src/features/shared-schedule/model/SharedEventDto.ts
- frontend/src/components/Pagination/index.tsx
- frontend/src/features/shared-schedule/model/upcomingSchedules.ts
- frontend/src/features/shared-schedule/model/finishedSchedules.ts
- frontend/src/features/shared-schedule/ui/OngoingSchedules/OngoingScheduleList.tsx
- frontend/src/features/shared-schedule/ui/FinishedSchedules/FinishedScheduleListItem.tsx
🔇 Additional comments (7)
frontend/src/features/discussion/model/invitation.ts (1)
15-15: LGTM: Well-structured schema additionThe new
timeUnlockedfield with the union type properly handles both date values and null cases.frontend/src/pages/DiscussionPage/DiscussionInvitePage/index.tsx (1)
18-18: LGTM: Proper prop passing implementationThe
timeUnlockedproperty is correctly destructured from the invitation object and properly passed down to the DiscussionInviteCard component.Also applies to: 30-30
frontend/src/features/discussion/ui/DiscussionInviteCard/SubmitForm.tsx (2)
21-60: Good component separation.The separation of the form logic into its own component improves modularity and reusability. The component handles state management and API interactions cleanly.
62-78: Well-designed conditional rendering.The JoinButton component elegantly handles the conditional rendering between a regular button and a TimerButton based on the unlock date/time.
frontend/src/features/discussion/ui/DiscussionInviteCard/index.tsx (3)
77-82: Good refactoring using the new SubmitForm component.The integration of the SubmitForm component simplifies this component and improves modularity by properly passing all required props.
19-19: Consistent use of the timeUnlocked prop.Good job adding the timeUnlocked prop to both interfaces and passing it through the component hierarchy to the SubmitForm component where it's needed.
Also applies to: 66-66
30-46: Cleaner component implementation.The refactoring of DiscussionInviteCard to use the new SubmitForm component has resulted in a cleaner, more focused component that handles only the presentation aspects while delegating form functionality.
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.
p2; 아무것도 입력하지 않으면 바로 400이 와서 락 하지 않고 따로 처리해줘야 할 것 같습니다.
82c8ec9 to
d875a58
Compare
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
🧹 Nitpick comments (4)
frontend/src/utils/error/HTTPError.ts (1)
21-22: Good addition for rate limiting detection!The implementation of
isTooManyRequestsError()follows the same pattern as existing methods and correctly identifies HTTP 429 errors (Too Many Requests). This is useful for handling rate limiting scenarios in API calls.Consider adding a descriptive comment to clarify that 429 represents "Too Many Requests" for better code documentation:
+ // Checks if the error is a 429 Too Many Requests error (rate limiting) isTooManyRequestsError = () => this.#status === 429;frontend/src/hooks/useCountdown.ts (2)
11-21: Consider adding debounce mechanism for performance optimizationFor performance optimization, especially in components that might re-render frequently, consider adding a debounce mechanism to prevent excessive updates when the countdown is far from zero.
useEffect(() => { + // Only update frequently when close to deadline + const updateInterval = remaining < 60000 ? SECOND_IN_MILLISECONDS : 10 * SECOND_IN_MILLISECONDS; const interval = setInterval(() => { const remaining = Math.max(targetDateTime.getTime() - Date.now(), 0); setRemainingTime(remaining); if (remaining === 0) { clearInterval(interval); onTimeEnd?.(); } - }, SECOND_IN_MILLISECONDS); + }, updateInterval); return () => clearInterval(interval); }, [targetDateTime, onTimeEnd]);
5-24: Consider returning a structured time object instead of raw millisecondsCurrently, the hook returns raw milliseconds which requires consumers to perform their own conversions. Consider enhancing the return value to include a structured format with hours, minutes, and seconds for easier consumption.
const useCountdown = (targetDateTime: Date, onTimeEnd?: () => void): number => { - const [remainingTime, setRemainingTime] = useState<number>( + const [remainingTime, setRemainingTime] = useState<number>( Math.max(targetDateTime.getTime() - Date.now(), 0), ); useEffect(() => { const interval = setInterval(() => { const remaining = Math.max(targetDateTime.getTime() - Date.now(), 0); setRemainingTime(remaining); if (remaining === 0) { clearInterval(interval); onTimeEnd?.(); } }, SECOND_IN_MILLISECONDS); return () => clearInterval(interval); }, [targetDateTime, onTimeEnd]); return remainingTime; + + // Alternative enhanced return type + // const seconds = Math.floor((remainingTime / 1000) % 60); + // const minutes = Math.floor((remainingTime / (1000 * 60)) % 60); + // const hours = Math.floor(remainingTime / (1000 * 60 * 60)); + // return { + // total: remainingTime, + // hours, + // minutes, + // seconds + // }; };frontend/src/features/discussion/ui/DiscussionInviteCard/SubmitForm.tsx (1)
69-79: Add accessibility attributes to the buttonTo improve accessibility, consider adding ARIA attributes and proper labels to the button component.
const JoinButton = ({ canJoin, onClick, initialUnlockDateTime, onTimeEnd }: { canJoin: boolean; onClick: () => void; onTimeEnd: () => void; initialUnlockDateTime: Date | null; }) => ( initialUnlockDateTime === null ? <Button disabled={!canJoin} onClick={onClick} size='xl' + aria-label="초대 수락하기" + role="button" > 초대 수락하기 </Button> : <TimerButton onTimeEnd={onTimeEnd} targetDateTime={initialUnlockDateTime} /> );
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📥 Commits
Reviewing files that changed from the base of the PR and between 82c8ec9f0119bf1c923a5da6c7398fe7cc582692 and d875a58.
📒 Files selected for processing (15)
frontend/src/components/Pagination/index.tsx(1 hunks)frontend/src/features/discussion/model/invitation.ts(1 hunks)frontend/src/features/discussion/ui/DiscussionInviteCard/SubmitForm.tsx(1 hunks)frontend/src/features/discussion/ui/DiscussionInviteCard/TimerButton.tsx(1 hunks)frontend/src/features/discussion/ui/DiscussionInviteCard/index.css.ts(1 hunks)frontend/src/features/discussion/ui/DiscussionInviteCard/index.tsx(3 hunks)frontend/src/features/shared-schedule/model/SharedEventDto.ts(1 hunks)frontend/src/features/shared-schedule/model/finishedSchedules.ts(1 hunks)frontend/src/features/shared-schedule/model/upcomingSchedules.ts(1 hunks)frontend/src/features/shared-schedule/ui/FinishedSchedules/FinishedScheduleListItem.tsx(3 hunks)frontend/src/features/shared-schedule/ui/OngoingSchedules/OngoingScheduleList.tsx(1 hunks)frontend/src/hooks/useCountdown.ts(1 hunks)frontend/src/pages/DiscussionPage/DiscussionInvitePage/index.tsx(2 hunks)frontend/src/utils/date/time.ts(1 hunks)frontend/src/utils/error/HTTPError.ts(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (11)
- frontend/src/features/discussion/ui/DiscussionInviteCard/index.css.ts
- frontend/src/utils/date/time.ts
- frontend/src/features/shared-schedule/model/SharedEventDto.ts
- frontend/src/features/discussion/model/invitation.ts
- frontend/src/features/shared-schedule/model/finishedSchedules.ts
- frontend/src/components/Pagination/index.tsx
- frontend/src/features/shared-schedule/ui/OngoingSchedules/OngoingScheduleList.tsx
- frontend/src/pages/DiscussionPage/DiscussionInvitePage/index.tsx
- frontend/src/features/discussion/ui/DiscussionInviteCard/TimerButton.tsx
- frontend/src/features/shared-schedule/model/upcomingSchedules.ts
- frontend/src/features/shared-schedule/ui/FinishedSchedules/FinishedScheduleListItem.tsx
🔇 Additional comments (7)
frontend/src/hooks/useCountdown.ts (1)
5-24: Well-implemented countdown hook with proper cleanupThe hook is well-designed, with appropriate state management and cleanup handling. The use of
useEffectwith the correct dependencies ensures the timer updates properly and is cleaned up when needed.frontend/src/features/discussion/ui/DiscussionInviteCard/index.tsx (4)
12-12: Good refactoring to extract form logicThe import of the new
SubmitFormcomponent aligns well with the PR objective to reduce duplication and improve modularity.
19-19: Appropriately added timeUnlocked propAdding the
timeUnlockedproperty of typeDate | nullproperly supports the new functionality of timed access control.
31-46: Clean component simplification with proper prop passingThe refactor has simplified the
DiscussionInviteCardcomponent by delegating form handling to theSubmitFormcomponent. The props are destructured and passed appropriately.
77-82: Good implementation of the extracted SubmitForm componentThe integration of the
SubmitFormcomponent with proper prop passing improves the component architecture and follows the single responsibility principle.frontend/src/features/discussion/ui/DiscussionInviteCard/SubmitForm.tsx (2)
28-29: Handle empty password input correctlyThe code converts empty password input to undefined, which is good practice, but you should validate the password format before submission as noted in a previous comment.
According to the previous review, there's an issue where empty password input causes a 400 error. Consider adding validation before submission to prevent this error.
36-41: Good error handling with retry lockoutThe error handling correctly identifies too many request errors and implements a 5-minute lockout period, improving security against brute force attacks.
| const PasswordInput = ({ onChange }: { onChange: (password: string) => void }) => ( | ||
| <input | ||
| className={inputStyle} | ||
| onChange={(e) => onChange(e.target.value)} | ||
| placeholder='숫자 4~6자리 비밀번호' | ||
| /> | ||
| ); |
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.
🛠️ Refactor suggestion
Add input validation for password format
The input placeholder indicates "숫자 4~6자리 비밀번호" (4-6 digit password), but there's no validation to enforce this constraint. Consider adding validation to match the expected format.
const PasswordInput = ({ onChange }: { onChange: (password: string) => void }) => (
<input
className={inputStyle}
+ type="password"
+ pattern="[0-9]{4,6}"
+ maxLength={6}
onChange={(e) => onChange(e.target.value)}
placeholder='숫자 4~6자리 비밀번호'
/>
);📝 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.
| const PasswordInput = ({ onChange }: { onChange: (password: string) => void }) => ( | |
| <input | |
| className={inputStyle} | |
| onChange={(e) => onChange(e.target.value)} | |
| placeholder='숫자 4~6자리 비밀번호' | |
| /> | |
| ); | |
| const PasswordInput = ({ onChange }: { onChange: (password: string) => void }) => ( | |
| <input | |
| className={inputStyle} | |
| type="password" | |
| pattern="[0-9]{4,6}" | |
| maxLength={6} | |
| onChange={(e) => onChange(e.target.value)} | |
| placeholder='숫자 4~6자리 비밀번호' | |
| /> | |
| ); |
| const SubmitForm = ({ discussionId, requirePW, canJoin, unlockDateTime }: SubmitFormProps) => { | ||
| const navigate = useNavigate(); | ||
| const { mutate } = useInvitationJoinMutation(); | ||
| const [password, setPassword] = useState(''); | ||
| const [unlockDT, setUnlockDT] = useState<Date | null>(unlockDateTime); | ||
| const handleJoinClick = () => { | ||
| mutate( | ||
| { body: { discussionId, password: password === '' ? undefined : password } }, | ||
| { onSuccess: (data) => { | ||
| if (data.isSuccess) { | ||
| navigate({ to: '/discussion/$id', params: { id: discussionId.toString() } }); | ||
| } else { | ||
| addNoti({ type: 'error', title: `비밀번호가 일치하지 않습니다 - ${data.failedCount}회 시도` }); | ||
| } | ||
| }, | ||
| onError: (error: Error) => { | ||
| if (error instanceof HTTPError && error.isTooManyRequestsError()) { | ||
| setUnlockDT(new Date(Date.now() + 5 * MINUTE_IN_MILLISECONDS)); | ||
| } | ||
| } }); | ||
| }; | ||
| return ( | ||
| <Flex align='flex-end' gap={500}> | ||
| {requirePW && canJoin && <PasswordInput onChange={setPassword} />} | ||
| <JoinButton | ||
| canJoin={canJoin} | ||
| initialUnlockDateTime={unlockDT} | ||
| onClick={handleJoinClick} | ||
| onTimeEnd={() => setUnlockDT(null)} | ||
| /> | ||
| </Flex> | ||
| ); | ||
| }; |
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.
🛠️ Refactor suggestion
Add input validation before submission
The current implementation doesn't validate the password format before submission. Consider adding validation to ensure the password meets the required format (4-6 digits) before submitting the form.
const SubmitForm = ({ discussionId, requirePW, canJoin, unlockDateTime }: SubmitFormProps) => {
const navigate = useNavigate();
const { mutate } = useInvitationJoinMutation();
const [password, setPassword] = useState('');
+ const [passwordError, setPasswordError] = useState<string | null>(null);
const [unlockDT, setUnlockDT] = useState<Date | null>(unlockDateTime);
+
+ const validatePassword = () => {
+ if (!requirePW || password === '') return true;
+ const isValid = /^\d{4,6}$/.test(password);
+ if (!isValid) {
+ setPasswordError('비밀번호는 4~6자리 숫자여야 합니다');
+ } else {
+ setPasswordError(null);
+ }
+ return isValid;
+ };
+
const handleJoinClick = () => {
+ if (!validatePassword()) return;
+
mutate(
{ body: { discussionId, password: password === '' ? undefined : password } },
{ onSuccess: (data) => {
if (data.isSuccess) {
navigate({ to: '/discussion/$id', params: { id: discussionId.toString() } });
} else {
addNoti({ type: 'error', title: `비밀번호가 일치하지 않습니다 - ${data.failedCount}회 시도` });
}
},
onError: (error: Error) => {
if (error instanceof HTTPError && error.isTooManyRequestsError()) {
setUnlockDT(new Date(Date.now() + 5 * MINUTE_IN_MILLISECONDS));
}
} });
};
return (
<Flex align='flex-end' gap={500}>
- {requirePW && canJoin && <PasswordInput onChange={setPassword} />}
+ {requirePW && canJoin && (
+ <Flex direction="column" gap={100}>
+ <PasswordInput onChange={setPassword} />
+ {passwordError && <Text color={vars.color.Ref.Red[500]} typo="b3M">{passwordError}</Text>}
+ </Flex>
+ )}
<JoinButton
canJoin={canJoin}
initialUnlockDateTime={unlockDT}
onClick={handleJoinClick}
onTimeEnd={() => setUnlockDT(null)}
/>
</Flex>
);
};📝 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.
| const SubmitForm = ({ discussionId, requirePW, canJoin, unlockDateTime }: SubmitFormProps) => { | |
| const navigate = useNavigate(); | |
| const { mutate } = useInvitationJoinMutation(); | |
| const [password, setPassword] = useState(''); | |
| const [unlockDT, setUnlockDT] = useState<Date | null>(unlockDateTime); | |
| const handleJoinClick = () => { | |
| mutate( | |
| { body: { discussionId, password: password === '' ? undefined : password } }, | |
| { onSuccess: (data) => { | |
| if (data.isSuccess) { | |
| navigate({ to: '/discussion/$id', params: { id: discussionId.toString() } }); | |
| } else { | |
| addNoti({ type: 'error', title: `비밀번호가 일치하지 않습니다 - ${data.failedCount}회 시도` }); | |
| } | |
| }, | |
| onError: (error: Error) => { | |
| if (error instanceof HTTPError && error.isTooManyRequestsError()) { | |
| setUnlockDT(new Date(Date.now() + 5 * MINUTE_IN_MILLISECONDS)); | |
| } | |
| } }); | |
| }; | |
| return ( | |
| <Flex align='flex-end' gap={500}> | |
| {requirePW && canJoin && <PasswordInput onChange={setPassword} />} | |
| <JoinButton | |
| canJoin={canJoin} | |
| initialUnlockDateTime={unlockDT} | |
| onClick={handleJoinClick} | |
| onTimeEnd={() => setUnlockDT(null)} | |
| /> | |
| </Flex> | |
| ); | |
| }; | |
| const SubmitForm = ({ discussionId, requirePW, canJoin, unlockDateTime }: SubmitFormProps) => { | |
| const navigate = useNavigate(); | |
| const { mutate } = useInvitationJoinMutation(); | |
| const [password, setPassword] = useState(''); | |
| const [passwordError, setPasswordError] = useState<string | null>(null); | |
| const [unlockDT, setUnlockDT] = useState<Date | null>(unlockDateTime); | |
| const validatePassword = () => { | |
| if (!requirePW || password === '') return true; | |
| const isValid = /^\d{4,6}$/.test(password); | |
| if (!isValid) { | |
| setPasswordError('비밀번호는 4~6자리 숫자여야 합니다'); | |
| } else { | |
| setPasswordError(null); | |
| } | |
| return isValid; | |
| }; | |
| const handleJoinClick = () => { | |
| if (!validatePassword()) return; | |
| mutate( | |
| { body: { discussionId, password: password === '' ? undefined : password } }, | |
| { onSuccess: (data) => { | |
| if (data.isSuccess) { | |
| navigate({ to: '/discussion/$id', params: { id: discussionId.toString() } }); | |
| } else { | |
| addNoti({ type: 'error', title: `비밀번호가 일치하지 않습니다 - ${data.failedCount}회 시도` }); | |
| } | |
| }, | |
| onError: (error: Error) => { | |
| if (error instanceof HTTPError && error.isTooManyRequestsError()) { | |
| setUnlockDT(new Date(Date.now() + 5 * MINUTE_IN_MILLISECONDS)); | |
| } | |
| } }); | |
| }; | |
| return ( | |
| <Flex align='flex-end' gap={500}> | |
| {requirePW && canJoin && ( | |
| <Flex direction="column" gap={100}> | |
| <PasswordInput onChange={setPassword} /> | |
| {passwordError && <Text color={vars.color.Ref.Red[500]} typo="b3M">{passwordError}</Text>} | |
| </Flex> | |
| )} | |
| <JoinButton | |
| canJoin={canJoin} | |
| initialUnlockDateTime={unlockDT} | |
| onClick={handleJoinClick} | |
| onTimeEnd={() => setUnlockDT(null)} | |
| /> | |
| </Flex> | |
| ); | |
| }; |
hamo-o
left a 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.
5번째가 아니라 6번째에 타이머가 동작하네요..!
ea3660c |
| } else { | ||
| setUnlockDT(new Date(Date.now() + LOCK_TIME_IN_MILLISECONDS)); | ||
| } |
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.
p2; 여기서도 노티는 띄워야 할 것 같습니다..! 사용자 입장에서 경고메시지 없이 잠기면 당황스러울 것 같아요.
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.
인정합니다 반영해 볼께요
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.
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.
오 좋네요!!


#️⃣ 연관된 이슈>
📝 작업 내용> 이번 PR에서 작업한 내용을 간략히 설명해주세요(이미지 첨부 가능)
🙏 여기는 꼭 봐주세요! > 리뷰어가 특별히 봐주었으면 하는 부분이 있다면 작성해주세요
Summary by CodeRabbit
New Features
Bug Fixes
Style