Skip to content
Closed
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,13 @@

.cardWrapper {
position: relative;
transition: opacity 0.15s;
}

/* 서버 요청 진행 중: 카드를 흐리게 표시하고 추가 인터랙션 차단 */
.cardPending {
opacity: 0.5;
pointer-events: none;
}

/* 드래그 중인 아이템: TodoCard와 동일한 border-radius(12px)로 테두리 플레이스홀더 표시 */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ function KanbanItem({
>
<div
ref={containerRef}
className={`${styles.cardWrapper} ${isDragging ? styles.cardWrapperDragging : ''}`}
className={`${styles.cardWrapper} ${isDragging ? styles.cardWrapperDragging : ''} ${task.pending ? styles.cardPending : ''}`}
>
{isEditing ? (
<div className={styles.editCard}>
Expand Down
25 changes: 0 additions & 25 deletions src/app/(root)/[teamid]/_domain/components/Team/SidebarWrapper.tsx

This file was deleted.

47 changes: 0 additions & 47 deletions src/app/(root)/[teamid]/_domain/components/Team/TeamNavClient.tsx

This file was deleted.

9 changes: 9 additions & 0 deletions src/app/(root)/[teamid]/_domain/hooks/useKanbanTasks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,9 +131,18 @@ export function useKanbanTasks(
}),
);

// 2초 이상 응답이 없을 때만 pending 표시 (빠른 응답 시 깜빡임 방지)
const pendingTimer = window.setTimeout(() => {
setTasks((prev) =>
prev.map((task) => (task.id === taskId ? { ...task, pending: true } : task)),
);
}, 2000);
Comment on lines +134 to +139

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

하드코딩된 숫자 2000은 '매직 넘버'로, 코드의 가독성과 유지보수성을 저해할 수 있습니다. PENDING_UI_DELAY_MS와 같이 의미를 명확히 나타내는 상수로 정의하여 사용하는 것이 좋습니다. 이 상수는 훅의 최상단에 선언하는 것이 이상적이지만, 우선 아래와 같이 수정하여 가독성을 높일 수 있습니다.

Suggested change
// 2초 이상 응답이 없을 때만 pending 표시 (빠른 응답 시 깜빡임 방지)
const pendingTimer = window.setTimeout(() => {
setTasks((prev) =>
prev.map((task) => (task.id === taskId ? { ...task, pending: true } : task)),
);
}, 2000);
// 2초 이상 응답이 없을 때만 pending 표시 (빠른 응답 시 깜빡임 방지)
const PENDING_UI_DELAY_MS = 2000;
const pendingTimer = window.setTimeout(() => {
setTasks((prev) =>
prev.map((task) => (task.id === taskId ? { ...task, pending: true } : task)),
);
}, PENDING_UI_DELAY_MS);


try {
await updateTask(groupId, taskListId, Number(itemId), { done: checked });
} finally {
// 타이머가 남아있으면 취소 (pending 노출 전에 완료된 경우)
clearTimeout(pendingTimer);
// 성공/실패 관계없이 서버 상태와 동기화
await queryClient.invalidateQueries({ queryKey });
}
Expand Down
2 changes: 2 additions & 0 deletions src/app/(root)/[teamid]/_domain/interfaces/team.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ export interface KanbanTask {
title: string;
items: TaskItem[];
status: KanbanStatus;
/** 서버 요청 진행 중 여부 (Soft Optimistic UI용) */
pending?: boolean;
}

export interface MockTeam {
Expand Down
6 changes: 3 additions & 3 deletions src/app/(root)/[teamid]/page.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
min-width: 0;
width: 100%; /* Explicitly set width to 100% */
box-sizing: border-box; /* Ensure padding is included in the width */
margin-left: 24px;
}

/* Apply max-width to direct children of mainContents to prevent overflow */
Expand All @@ -26,8 +25,9 @@
.desktopSidebar {
display: block; /* Show desktop sidebar */
/* fixed 포지션 사이드바가 flex 흐름에서 공간을 차지하지 않으므로, spacer 역할을 위해 너비 명시 */
width: 270px;
min-width: 270px;
/* 루트 레이아웃 main의 margin-left: 72px를 이미 반영하므로 270 - 72 = 198px */
width: 198px;
min-width: 198px;
flex-shrink: 0;
}
/* Tablet styles */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ export default function AddTeamSidebarWrapper() {
const { data: currentUser } = useCurrentUserQuery();
const router = useRouter();

const handleLogout = async () => {
await fetch('/api/auth/logout', { method: 'POST' });
router.push('/login');
};
Comment on lines +12 to +15

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

이곳의 로그아웃 로직은 다른 컴포넌트에서도 반복되고 있습니다. 코드 중복을 피하고 유지보수성을 높이기 위해, 이 로직을 별도의 커스텀 훅으로 추출하여 사용하는 것을 고려해 보세요.


return (
<Sidebar
isLoggedIn={!!currentUser}
Expand All @@ -18,6 +23,7 @@ export default function AddTeamSidebarWrapper() {
profileName={currentUser?.nickname}
profileTeam={currentUser?.email}
onProfileClick={() => router.push('/mypage')}
onLogout={handleLogout}
/>
);
}
Loading