Skip to content
Merged
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
38 changes: 11 additions & 27 deletions src/components/admin/layout/LNB.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,7 @@

import { useState } from 'react';
import { useParams, usePathname, useRouter } from 'next/navigation';
import {
AdminForumIcon,
AdminCalendarIcon,
AdminSettingIcon,
AdminFileoutIcon,
} from '@/assets/icons/admin';
import { AdminForumIcon, AdminCalendarIcon, AdminSettingIcon } from '@/assets/icons/admin';
import { CheckRoundIcon, ExitIcon, PeopleIcon } from '@/assets/icons';

import {
Expand Down Expand Up @@ -59,16 +54,16 @@ function LNB() {
},
];

const moveNavItems = [
{
id: 'manual',
icon: AdminFileoutIcon,
label: '운영진 매뉴얼',
path: 'https://weeth-develop-2.s3.ap-northeast-2.amazonaws.com/Weeth_%E1%84%80%E1%85%AA%E1%86%AB%E1%84%85%E1%85%B5%E1%84%8C%E1%85%A1_%E1%84%86%E1%85%A6%E1%84%82%E1%85%B2%E1%84%8B%E1%85%A5%E1%86%AF_v3.pdf',
external: true,
openInWindow: true,
},
];
// const moveNavItems = [
// {
// id: 'manual',
// icon: AdminFileoutIcon,
// label: '운영진 매뉴얼',
// path: 'https://weeth-develop-2.s3.ap-northeast-2.amazonaws.com/Weeth_%E1%84%80%E1%85%AA%E1%86%AB%E1%84%85%E1%85%B5%E1%84%8C%E1%85%A1_%E1%84%86%E1%85%A6%E1%84%82%E1%85%B2%E1%84%8B%E1%85%A5%E1%86%AF_v3.pdf',
// external: true,
// openInWindow: true,
// },
// ];

return (
<TooltipProvider>
Expand Down Expand Up @@ -119,17 +114,6 @@ function LNB() {
collapsed={collapsed}
onClick={() => setServiceDialogOpen(true)}
/>
{moveNavItems.map(({ id, icon, label, path, external, openInWindow }) => (
<NavItem
key={id}
icon={icon}
label={label}
path={path}
collapsed={collapsed}
external={external}
openInWindow={openInWindow}
/>
))}
</NavSection>

<AlertDialog
Expand Down
2 changes: 1 addition & 1 deletion src/components/admin/layout/LNBClubInfo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ function LNBClubInfo({ collapsed }: LNBClubInfoProps) {
return (
<div
className={cn(
'border-line bg-container-neutral flex h-30 items-start border-b py-400',
'border-line bg-container-neutral flex h-[142px] items-start border-b py-400',
collapsed ? 'justify-center' : 'flex-col gap-300 px-400',
)}
Comment on lines 15 to 19
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== usages: h-[142px] =="
rg -n "h-\\[142px\\]" -S src || true

echo
echo "== tailwind config candidates =="
fd -t f -i "tailwind.config.*" . | head -n 20

echo
echo "== token-related keys in tailwind config (height/spacing) =="
for f in $(fd -t f -i "tailwind.config.*" . | head -n 5); do
  echo "---- $f ----"
  rg -n "theme\\.extend\\b|height\\b|spacing\\b" "$f" || true
done

Repository: Team-Weeth/weeth-client

Length of output: 321


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Looking for tailwind config in various locations ==="
find . -type f \( -name "*.config.*" -o -name "tailwind*" \) 2>/dev/null | grep -E "(js|ts|json|css)" | head -20

echo
echo "=== Checking package.json for tailwind config reference ==="
cat package.json | jq '.tailwindConfig, .dependencies | select(. != null)' 2>/dev/null || echo "No tailwindConfig in package.json"

echo
echo "=== Looking for CSS files with token/design system definitions ==="
fd -t f -e css | head -20

echo
echo "=== Searching for theme or token definitions ==="
rg "theme\s*[:{]|--.*-\d{2,3}|h-\[" -l --type-list | head -15

echo
echo "=== Checking the specific file for imports and context ==="
head -30 src/components/admin/layout/LNBClubInfo.tsx

Repository: Team-Weeth/weeth-client

Length of output: 3436


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== postcss.config.mjs ==="
cat postcss.config.mjs

echo
echo "=== src/app/globals.css (first 100 lines) ==="
head -100 src/app/globals.css

echo
echo "=== Search for '142' in codebase ==="
rg "142" -n src --type ts --type tsx | head -20

echo
echo "=== Look for design token or theme directories ==="
fd -t d -e "design\|token\|theme" . --exclude node_modules | head -20

Repository: Team-Weeth/weeth-client

Length of output: 3066


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Full src/app/globals.css ==="
wc -l src/app/globals.css

echo
echo "=== Last part of globals.css (lines 100-200+) ==="
tail -n +100 src/app/globals.css | head -150

echo
echo "=== Search for spacing/height token definitions ==="
rg "spacing|space-|gap-|padding|height|h-" src/app/globals.css -A 2 || echo "No spacing/height tokens found"

echo
echo "=== Look at full LNBClubInfo.tsx context ==="
cat -n src/components/admin/layout/LNBClubInfo.tsx

Repository: Team-Weeth/weeth-client

Length of output: 10705


h-[142px] 하드코딩은 디자인 토큰 클래스로 교체 필요

루트 컨테이너 높이를 h-[142px](임의 값)로 고정했는데, 코딩 가이드라인상 Tailwind는 디자인 토큰 클래스를 우선하고 하드코딩을 피하도록 규정하고 있습니다. 현재 디자인 시스템에는 --spacing-800: 40px까지의 spacing 토큰만 정의되어 있으며, 142px에 해당하는 height 토큰이 없습니다.

다음 중 하나를 선택하여 진행해주세요:

  • 142px가 의도된 고정 높이라면, 새 height 토큰(예: --height-142)을 추가한 후 토큰 클래스로 교체
  • 또는 기존 spacing 토큰 조합으로 컴포넌트 레이아웃을 재검토
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/admin/layout/LNBClubInfo.tsx` around lines 15 - 19, The root
container in LNBClubInfo.tsx hardcodes height with h-[142px]; replace this with
a design-token class or adjust layout to use existing spacing tokens. If 142px
is required, add a new height token (e.g., --height-142) to the design tokens
and use its generated Tailwind class in the className string (the JSX div using
cn(...) and the collapsed branch), otherwise refactor the component layout to
compose existing spacing tokens (e.g., spacing-700/800 combos) instead of
h-[142px] so the className no longer contains the hardcoded h-[142px].

>
Expand Down
14 changes: 9 additions & 5 deletions src/components/admin/schedule/session/SessionGroupRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,16 @@ function SessionGroupRow({
onManageAttendance,
onMore,
}: SessionGroupRowProps) {
// 반복 세션 그룹일 때만 토글과 하위 테이블을 노출
const isRecurring = group.recurrenceType !== 'NONE';
// 반복 세션 그룹일 때만 토글과 하위 테이블을 노출 (서버는 단발 세션의 recurrenceType을 null로 보냄)
const isRecurring = group.recurrenceType != null && group.recurrenceType !== 'NONE';
const [expanded, setExpanded] = useState(true);

// 그룹 시작/종료일과 오늘을 비교해 SCHEDULED / OPEN / COMPLETED 도출
const derivedGroupStatus = deriveSessionStatus(group.status, group.startDate, group.endDate);
// 단발 세션은 endDate가 null이므로 startDate로 폴백 (단일 날짜 = 시작일과 동일)
const derivedGroupStatus = deriveSessionStatus(
group.status,
group.startDate,
group.endDate ?? group.startDate,
);

return (
<div className={cn('flex flex-col', bordered && 'border-line border-t')}>
Expand Down Expand Up @@ -89,7 +93,7 @@ function SessionGroupRow({
>
<span className="typo-body1 text-text-strong truncate">
{isRecurring
? formatSessionDateRange(group.startDate, group.endDate)
? formatSessionDateRange(group.startDate, group.endDate ?? group.startDate)
: formatSessionDate(group.startDate)}
</span>
</div>
Expand Down
8 changes: 8 additions & 0 deletions src/components/admin/schedule/session/SessionTabContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,14 @@ function SessionTabContent({
toastError('수정·삭제 가능한 세션이 없습니다.');
return;
}
// 서버는 단발 세션도 그룹 wrapper로 감싸서 보내지만(groupId·recurrenceType 모두 null),
// 의미상 그룹이 아니므로 자식 세션을 직접 수정 대상으로 전달해 단일 세션 흐름을 타도록 한다.
const isSingleSessionWrapper =
isSessionGroup(target) && (target.recurrenceType == null || target.recurrenceType === 'NONE');
if (isSingleSessionWrapper) {
setEditTarget({ target: target.sessions[0]! });
return;
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
setEditTarget({ target, parentGroup });
};

Expand Down
2 changes: 1 addition & 1 deletion src/components/admin/schedule/session/SessionTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ function SessionTable({
) : (
groups.map((group, index) => (
<SessionGroupRow
key={group.groupId}
key={group.groupId ?? `single-${group.sessions[0]?.id ?? index}`}
group={group}
bordered={index > 0}
onManageAttendance={onManageAttendance}
Expand Down
5 changes: 5 additions & 0 deletions src/constants/admin/schedule.constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,8 @@ export const SCHEDULE_ERROR_MESSAGE: Record<number, string> = {
* 백엔드가 반환하는 에러 코드. 토스트 대신 호출자 UI(force-confirm 다이얼로그)에서 처리.
*/
export const SESSION_UPDATE_FORCE_REQUIRED_CODE = 20305;
export const SESSION_DELETE_FORCE_REQUIRED_CODE = 20306;
export const SESSION_FORCE_REQUIRED_CODES = [
SESSION_UPDATE_FORCE_REQUIRED_CODE,
SESSION_DELETE_FORCE_REQUIRED_CODE,
] as const;
15 changes: 10 additions & 5 deletions src/hooks/admin/useSessionMutations.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import type {

/** CLOSED 세션 포함 → force=true 재요청 동의 다이얼로그용 페이로드 */
interface ForceConfirm {
title: string;
description: string;
actionLabel: string;
retry: () => void;
Expand Down Expand Up @@ -61,6 +62,7 @@ function useSessionMutations() {
onError: (error) => {
if (!force && isSessionForceRequiredError(error)) {
setForceConfirm({
title: '종료된 세션이 포함되어 있어요',
description:
scope === 'THIS_AND_FUTURE'
? '이후 일정 중 이미 종료된 세션도 함께 수정할까요?'
Expand All @@ -87,11 +89,12 @@ function useSessionMutations() {
onError: (error) => {
if (!force && isSessionForceRequiredError(error)) {
setForceConfirm({
title: '출석 데이터가 있어요',
description:
scope === 'THIS_AND_FUTURE'
? '이후 일정 중 이미 종료된 세션도 함께 삭제할까요?'
: '이미 종료된 세션이에요. 그래도 삭제할까요?',
actionLabel: '모두 삭제',
? '이후 일정 중 출석 데이터가 있는 세션이 있어요.\n삭제하면 출석 데이터도 함께 사라져요.'
: '출석 데이터가 있는 세션이에요. 그래도 삭제할까요?\n삭제하면 출석 데이터도 함께 사라져요.',
actionLabel: scope === 'THIS_AND_FUTURE' ? '모두 삭제' : '삭제',
retry: () => submitDeleteSession(sessionId, scope, true, options),
});
}
Expand All @@ -108,7 +111,9 @@ function useSessionMutations() {
onError: (error) => {
if (!force && isSessionForceRequiredError(error)) {
setForceConfirm({
description: '종료된 세션도 포함되어 있어요. 그래도 그룹 전체를 삭제할까요?',
title: '출석 데이터가 있어요',
description:
'출석 데이터가 있는 세션이 포함되어 있어요.\n삭제하면 출석 데이터도 함께 사라져요.',
actionLabel: '모두 삭제',
retry: () => submitDeleteGroup(groupId, true, options),
});
Expand All @@ -124,7 +129,7 @@ function useSessionMutations() {
onOpenChange={(open) => {
if (!open) setForceConfirm(null);
}}
title="종료된 세션이 포함되어 있어요"
title={forceConfirm?.title ?? ''}
description={forceConfirm?.description ?? ''}
actionLabel={forceConfirm?.actionLabel ?? '확인'}
cancelLabel="취소"
Expand Down
7 changes: 4 additions & 3 deletions src/hooks/queries/admin/useAdminScheduleQueries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { isAxiosError } from 'axios';

import {
SCHEDULE_ERROR_MESSAGE,
SESSION_UPDATE_FORCE_REQUIRED_CODE,
SESSION_FORCE_REQUIRED_CODES,
} from '@/constants/admin/schedule.constants';
import { adminScheduleApi } from '@/lib/apis/adminSchedule';
import { useClubId } from '@/stores';
Expand All @@ -16,10 +16,11 @@ import type {
} from '@/types/admin/session';
import { MutationCallbacks } from '@/types';

/** 세션 update/delete 응답이 "CLOSED 포함, force 필요" 에러인지 판별 */
/** 세션 update(20305)/delete(20306) 응답이 "CLOSED 포함, force 필요" 에러인지 판별 */
function isSessionForceRequiredError(error: unknown): boolean {
if (!isAxiosError(error)) return false;
return error.response?.data?.code === SESSION_UPDATE_FORCE_REQUIRED_CODE;
const code = error.response?.data?.code;
return SESSION_FORCE_REQUIRED_CODES.includes(code);
}

export { isSessionForceRequiredError };
Expand Down
14 changes: 8 additions & 6 deletions src/types/admin/session.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,17 @@ export interface AdminSession {
}

export interface AdminSessionGroup {
groupId: number;
/** 단발(반복 없음) 세션은 그룹이 아니므로 null */
groupId: number | null;
title: string;
recurrenceType: SessionRecurrenceType;
/** "매주 목요일 오후 7:00 ~ 오후 9:00" 처럼 서버에서 렌더링된 문구 */
recurrenceDescription: string;
/** 단발 세션은 null */
recurrenceType: SessionRecurrenceType | null;
/** "매주 목요일 오후 7:00 ~ 오후 9:00" 처럼 서버에서 렌더링된 문구. 단발 세션은 null */
recurrenceDescription: string | null;
/** YYYY-MM-DD */
startDate: string;
/** YYYY-MM-DD */
endDate: string;
/** YYYY-MM-DD. 단발 세션은 반복 종료일이 없으므로 null */
endDate: string | null;
completedCount: number;
totalCount: number;
status: SessionStatus;
Expand Down
Loading