Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
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
1 change: 0 additions & 1 deletion src/components/user/mypage/ContentTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@ export default function ContentTab({ filter, $justifyContent }: ContentProps) {
to={filter.url}
onClick={() => handleChangeId(filter.id as number)}
>
{' '}
<S.WrapperTitle $selected={filter?.id === filterId}>
<S.FilterTitle>{filter.title}</S.FilterTitle>
</S.WrapperTitle>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export const InquiriesTableHeadContainer = styled.div`
export const InquiriesTableHeadWrapper = styled.div`
width: 100%;
display: grid;
grid-template-columns: 8% 15% 65% 17%;
grid-template-columns: 8% 15% 65% 12%;
font-size: 1.3rem;
font-weight: 600;
margin-bottom: 0.5rem;
Expand All @@ -44,6 +44,10 @@ export const InquiriesTableHeaderState = styled.div`
text-align: center;
`;

export const MyInquiriesWrapper = styled.div<{ $headHeight: number }>`
scroll-margin-top: ${({ $headHeight }) => $headHeight + 10}px;
`;

export const InquiriesWrapper = styled.div`
margin-top: 1rem;
display: flex;
Expand Down
31 changes: 25 additions & 6 deletions src/components/user/mypage/activityLog/inquiries/Inquiries.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,35 @@
import { useParams } from 'react-router-dom';
import { useLocation, useParams } from 'react-router-dom';
import useGetUserActivity from '../../../../../hooks/admin/useGetAllUserActivity';
import ContentBorder from '../../../../common/contentBorder/ContentBorder';
import NoContent from '../../../../common/noContent/NoContent';
import Spinner from '../../Spinner';
import * as S from './Inquiries.styled';
import Inquiry from './inquiry/Inquiry';
import type { MyInquiries } from '../../../../../models/activityLog';
import { useLayoutEffect, useRef, useState } from 'react';

export default function Inquiries() {
const { userId } = useParams();
const { state } = useLocation();
const { id } = state || {};
const { userActivityData, isLoading } = useGetUserActivity(
Number(userId),
'inquiries'
);
const headRef = useRef<HTMLDivElement>(null);
const inquiriesRef = useRef<(HTMLDivElement | null)[]>([]);
const [headHeight, setHeadHeight] = useState<number>(0);

useLayoutEffect(() => {
if (!id || !headRef?.current) return;
const height = headRef.current.offsetHeight;
setHeadHeight(height);
const idx = userActivityData?.findIndex((item) => item.id == id);
const targetRef = typeof idx === 'number' ? inquiriesRef.current[idx] : '';
if (inquiriesRef?.current && targetRef) {
targetRef.scrollIntoView({ behavior: 'smooth', block: 'start' });
}
}, [userActivityData, id, headHeight]);
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

스크롤 로직 개선 필요

useLayoutEffect 구현에서 몇 가지 개선 사항이 있습니다:

  1. 라인 27: 엄격한 동등 비교 사용 권장
  2. 라인 32: 의존성 배열에서 headHeight 제거로 무한 재렌더링 방지
  3. 라인 28: 타입 체크 개선

다음과 같이 수정하시기 바랍니다:

  useLayoutEffect(() => {
    if (!id || !headRef?.current) return;
    const height = headRef.current.offsetHeight;
    setHeadHeight(height);
-   const idx = userActivityData?.findIndex((item) => item.id == id);
-   const targetRef = typeof idx === 'number' ? inquiriesRef.current[idx] : '';
+   const idx = userActivityData?.findIndex((item) => item.id === id);
+   const targetRef = idx !== undefined && idx >= 0 ? inquiriesRef.current[idx] : null;
    if (inquiriesRef?.current && targetRef) {
      targetRef.scrollIntoView({ behavior: 'smooth', block: 'start' });
    }
- }, [userActivityData, id, headHeight]);
+ }, [userActivityData, id]);
📝 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
useLayoutEffect(() => {
if (!id || !headRef?.current) return;
const height = headRef.current.offsetHeight;
setHeadHeight(height);
const idx = userActivityData?.findIndex((item) => item.id == id);
const targetRef = typeof idx === 'number' ? inquiriesRef.current[idx] : '';
if (inquiriesRef?.current && targetRef) {
targetRef.scrollIntoView({ behavior: 'smooth', block: 'start' });
}
}, [userActivityData, id, headHeight]);
useLayoutEffect(() => {
if (!id || !headRef?.current) return;
const height = headRef.current.offsetHeight;
setHeadHeight(height);
const idx = userActivityData?.findIndex((item) => item.id === id);
const targetRef = idx !== undefined && idx >= 0 ? inquiriesRef.current[idx] : null;
if (inquiriesRef?.current && targetRef) {
targetRef.scrollIntoView({ behavior: 'smooth', block: 'start' });
}
}, [userActivityData, id]);
🤖 Prompt for AI Agents
In src/components/user/mypage/activityLog/inquiries/Inquiries.tsx around lines
23 to 32, update the useLayoutEffect hook by replacing the loose equality check
(==) with strict equality (===) on line 27, remove headHeight from the
dependency array on line 32 to prevent infinite re-renders, and improve the type
check for idx on line 28 by verifying idx is not -1 instead of checking its
type. These changes will enhance correctness and performance of the scroll
logic.


if (isLoading) {
return (
Expand All @@ -36,7 +53,7 @@ export default function Inquiries() {
return (
<S.container>
<S.InquiriesContainer>
<S.InquiriesTableHeadContainer>
<S.InquiriesTableHeadContainer ref={headRef}>
<S.InquiriesTableHeadWrapper>
<S.InquiriesTableHeaderNo>No</S.InquiriesTableHeaderNo>
<S.InquiriesTableHeaderCategory>
Expand All @@ -49,11 +66,13 @@ export default function Inquiries() {
</S.InquiriesTableHeadContainer>
<S.InquiriesWrapper>
{myInquiriesData.map((list, index) => (
<Inquiry
<S.MyInquiriesWrapper
ref={(el) => (inquiriesRef.current[index] = el)}
key={`${index}-${list.title}`}
list={list}
no={myInquiriesData.length - index}
/>
$headHeight={headHeight}
>
<Inquiry list={list} no={myInquiriesData.length - index} />
</S.MyInquiriesWrapper>
))}
</S.InquiriesWrapper>
</S.InquiriesContainer>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export const InquiryTitleWrapper = styled.button`
text-align: start;
font-size: 1.1rem;
display: grid;
grid-template-columns: 8% 15% 65% 17%;
grid-template-columns: 8% 15% 65% 12%;
cursor: pointer;
`;

Expand All @@ -34,8 +34,8 @@ export const InquiryStateSpan = styled.span<{ $isCompletedAnswer: boolean }>`
$isCompletedAnswer &&
css`
background: ${({ theme }) => theme.color.navy};
border-radius: ${({ theme }) => theme.borderRadius.small};
padding: 0.2rem;
border-radius: ${({ theme }) => theme.borderRadius.large};
padding: 0.2rem 0.5rem;
`}
`;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { useRef, useState } from 'react';
import { useEffect, useRef, useState } from 'react';
import type { MyInquiries } from '../../../../../../models/activityLog';
import * as S from './Inquiry.styled';
import { My_INQUIRIES_MESSAGE } from '../../../../../../constants/user/customerService';
import ContentBorder from '../../../../../common/contentBorder/ContentBorder';
import { ChevronRightIcon } from '@heroicons/react/24/outline';
import { useLocation } from 'react-router-dom';

interface InquiryProps {
list: MyInquiries;
Expand All @@ -16,26 +17,28 @@ interface IsImageOpen {
}

export default function Inquiry({ list, no }: InquiryProps) {
const { state } = useLocation();
const { id } = state || {};
const [isOpen, setIsOpen] = useState<boolean>(false);
const [isImageOpen, setIsImageOpen] = useState<IsImageOpen>({
isImageOpen: false,
url: '',
});
const answer = list.answer || '';
const answerRef = useRef<HTMLTextAreaElement>(null);
const divRef = useRef<HTMLDivElement>(null);

const handleChangeAnswerRef = () => {
if (answerRef && answerRef.current) {
answerRef.current.style.height = 'auto';
answerRef.current.style.height = `${answerRef.current.scrollHeight}px`;
useEffect(() => {
if (list.id === id) {
setIsOpen(true);
}
};
}, [list, id, setIsOpen]);

return (
<S.Container>
<S.Container ref={divRef}>
<S.InquiryTitleWrapper
type='button'
onClick={() => setIsOpen((prev) => !prev)}
data-id={list.id}
>
<S.InquiryNumber>{no}</S.InquiryNumber>
<S.InquiryCategory>{`[${list.category}]`}</S.InquiryCategory>
Expand Down
23 changes: 12 additions & 11 deletions src/components/user/mypage/notifications/all/All.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,23 @@ import { useAlarmDelete } from '../../../../../hooks/user/useAlarmDelete';
import { useAlarmPatch } from '../../../../../hooks/user/useAlarmPatch';
import useAlarmList from '../../../../../hooks/user/useAlarmList';
import NoContent from '../../../../common/noContent/NoContent';
import { ROUTES } from '../../../../../constants/routes';

export default function All() {
const { alarmListData, isLoading } = useAlarmList();
const { filterId }: { filterId: number } = useOutletContext();
const { mutate: deleteAlarm } = useAlarmDelete();
const { mutate: patchAlarm } = useAlarmPatch();

const linkUrl = (id: number, filter: number, replier = 0) => {
// 문의, 신고 답변시 추후 수정
const linkUrl = (id: number, filter: number) => {
if (filter === 1 || filter === 3) {
if (replier === 3) {
return `/activity-log/inquiries`;
} else {
return `/project-detail/${id}`;
}
return `${ROUTES.projectDetail}/${id}`;
} else if (filter === 2) {
return `/manage/${id}`;
return `${ROUTES.manageProjectsRoot}/${id}`;
} else if (filter === 4) {
return `${ROUTES.mypage}/${ROUTES.myPageActivityLog}/${ROUTES.activityInquiries}`;
} else {
return `/mypage/notification`;
return `${ROUTES.mypage}/${ROUTES.myPageNotifications}`;
}
};

Expand All @@ -45,7 +43,7 @@ export default function All() {
return false;
}).length;

if (!alarmListData || alarmListData.length === 0 || filterLength === 0) {
if (!alarmListData?.length || filterLength === 0) {
return (
<S.WrapperNoContent data-type='noContent'>
<NoContent type='notification' />
Expand All @@ -60,6 +58,8 @@ export default function All() {
.filter((list) => {
if (filterId === 0) {
return true;
} else if (filterId === 3 && list.alarmFilterId === 4) {
return true;
} else if (list.alarmFilterId === filterId) {
return true;
}
Expand All @@ -70,7 +70,8 @@ export default function All() {
{/* 신고하기 알림 구별 */}
{list.alarmFilterId !== 5 ? (
<Link
to={linkUrl(list.routingId, list.alarmFilterId, list.replier)}
to={linkUrl(list.routingId, list.alarmFilterId)}
state={list.alarmFilterId === 4 && { id: list.routingId }}
onClick={() => patchAlarm(list.id)}
>
<S.SpanNotification
Expand Down
6 changes: 3 additions & 3 deletions src/constants/user/myPageFilter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,19 @@ export const NOTIFICATION_FILTER = [
title: '지원한 프로젝트',
url: ROUTES.notificationsAppliedProjects,
id: 1,
linkUrl: `/project-detail`,
linkUrl: ROUTES.projectDetail,
},
{
title: '지원자 확인',
url: ROUTES.notificationsCheckedApplicants,
id: 2,
linkUrl: `/project-detail`,
linkUrl: ROUTES.projectDetail,
},
{
title: '댓글&답변',
url: ROUTES.comments,
id: 3,
linkUrl: `/project-detail`,
linkUrl: ROUTES.projectDetail,
},
] as const;

Expand Down