From 81a016ada904b8de2d52b5d71b2e38c588c93d7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=EB=B3=91=EC=B2=A0?= Date: Wed, 25 Feb 2026 22:32:52 +0900 Subject: [PATCH 01/13] =?UTF-8?q?feat:=20[=EA=B3=B5=ED=86=B5]=20UI=20?= =?UTF-8?q?=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EA=B0=9C=EC=84=A0=20(Ta?= =?UTF-8?q?bChip,=20FormField,=20Select=20=EB=93=B1)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - TabChip, FormField, MobileCard, Select 컴포넌트 개선 - IntegratedListTemplateV2, UniversalListPage 타입 보강 - LoginPage UI 개선 Co-Authored-By: Claude Opus 4.6 --- src/components/atoms/TabChip.tsx | 16 +++++----------- src/components/molecules/FormField.tsx | 5 ++++- src/components/organisms/MobileCard.tsx | 8 ++++---- .../templates/IntegratedListTemplateV2.tsx | 3 ++- .../templates/UniversalListPage/types.ts | 1 + src/components/ui/select.tsx | 4 ++-- 6 files changed, 18 insertions(+), 19 deletions(-) diff --git a/src/components/atoms/TabChip.tsx b/src/components/atoms/TabChip.tsx index e9327f95..049a23a6 100644 --- a/src/components/atoms/TabChip.tsx +++ b/src/components/atoms/TabChip.tsx @@ -42,25 +42,19 @@ export function TabChip({ + {isAuthor && ( +
+ + +
+ )} {/* 게시글 삭제 확인 다이얼로그 */} diff --git a/src/app/[locale]/(protected)/boards/[boardCode]/page.tsx b/src/app/[locale]/(protected)/boards/[boardCode]/page.tsx index 0db94d31..ab47180d 100644 --- a/src/app/[locale]/(protected)/boards/[boardCode]/page.tsx +++ b/src/app/[locale]/(protected)/boards/[boardCode]/page.tsx @@ -223,7 +223,7 @@ function DynamicBoardListContent({ boardCode }: { boardCode: string }) { const handleRowClick = useCallback( (item: BoardPost) => { - router.push(`/ko/boards/${boardCode}/${item.id}`); + router.push(`/ko/boards/${boardCode}/${item.id}?mode=view`); }, [router, boardCode] ); diff --git a/src/components/board/BoardDetail/index.tsx b/src/components/board/BoardDetail/index.tsx index 70e93a57..00df72e4 100644 --- a/src/components/board/BoardDetail/index.tsx +++ b/src/components/board/BoardDetail/index.tsx @@ -125,17 +125,11 @@ export function BoardDetail({ post, comments: initialComments, currentUserId }: {/* 메타 정보: 작성자 | 날짜 | 조회수 */} -
- {post.authorName} - {post.authorDepartment && ( - <> - | - {post.authorDepartment} - - )} - | +
+ {post.authorName}{post.authorDepartment ? ` / ${post.authorDepartment}` : ''} + | {format(new Date(post.createdAt), 'yyyy-MM-dd HH:mm')} - | + | 조회수 {post.viewCount}
diff --git a/src/components/board/BoardManagement/BoardDetail.tsx b/src/components/board/BoardManagement/BoardDetail.tsx index 0185f55d..4d74a91e 100644 --- a/src/components/board/BoardManagement/BoardDetail.tsx +++ b/src/components/board/BoardManagement/BoardDetail.tsx @@ -7,6 +7,7 @@ import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { Badge } from '@/components/ui/badge'; import { ClipboardList, ArrowLeft, Edit, Trash2 } from 'lucide-react'; +import { useMenuStore } from '@/stores/menuStore'; import type { Board } from './types'; import { BOARD_STATUS_LABELS, @@ -33,6 +34,7 @@ const formatDateTime = (dateString: string): string => { export function BoardDetail({ board, onEdit, onDelete }: BoardDetailProps) { const router = useRouter(); + const sidebarCollapsed = useMenuStore((state) => state.sidebarCollapsed); const handleBack = () => { router.push('/ko/board/board-management'); @@ -54,7 +56,7 @@ export function BoardDetail({ board, onEdit, onDelete }: BoardDetailProps) { icon={ClipboardList} /> -
+
{/* 게시판 정보 */} @@ -93,26 +95,34 @@ export function BoardDetail({ board, onEdit, onDelete }: BoardDetailProps) { - {/* 버튼 영역 */} -
- -
- {onDelete && ( - - )} - {onEdit && ( - - )} -
+
+ + {/* 하단 액션 버튼 (sticky) */} +
+ +
+ {onDelete && ( + + )} + {onEdit && ( + + )}
diff --git a/src/components/board/BoardManagement/BoardDetailClientV2.tsx b/src/components/board/BoardManagement/BoardDetailClientV2.tsx index f490f2c4..c313fbfb 100644 --- a/src/components/board/BoardManagement/BoardDetailClientV2.tsx +++ b/src/components/board/BoardManagement/BoardDetailClientV2.tsx @@ -93,9 +93,10 @@ export function BoardDetailClientV2({ boardId, initialMode }: BoardDetailClientV // URL 쿼리 변경 감지 useEffect(() => { - if (!isNewMode && modeFromQuery === 'edit') { + if (isNewMode) return; + if (modeFromQuery === 'edit') { setMode('edit'); - } else if (!isNewMode && !modeFromQuery) { + } else { setMode('view'); } }, [modeFromQuery, isNewMode]); diff --git a/src/components/board/BoardManagement/BoardForm.tsx b/src/components/board/BoardManagement/BoardForm.tsx index a11b6331..77d51d3f 100644 --- a/src/components/board/BoardManagement/BoardForm.tsx +++ b/src/components/board/BoardManagement/BoardForm.tsx @@ -17,7 +17,8 @@ import { SelectValue, } from '@/components/ui/select'; import { Checkbox } from '@/components/ui/checkbox'; -import { ClipboardList, ArrowLeft, Save } from 'lucide-react'; +import { ClipboardList, ArrowLeft, Save, X } from 'lucide-react'; +import { useMenuStore } from '@/stores/menuStore'; import type { Board, BoardFormData, BoardTarget, BoardStatus } from './types'; import { BOARD_TARGETS, BOARD_STATUS_LABELS } from './types'; @@ -62,6 +63,7 @@ const getCurrentDateTime = (): string => { export function BoardForm({ mode, board, onSubmit }: BoardFormProps) { const router = useRouter(); + const sidebarCollapsed = useMenuStore((state) => state.sidebarCollapsed); const [formData, setFormData] = useState({ target: 'all', targetName: '', @@ -129,7 +131,7 @@ export function BoardForm({ mode, board, onSubmit }: BoardFormProps) { icon={ClipboardList} /> -
+ {/* 게시판 정보 */} @@ -254,18 +256,21 @@ export function BoardForm({ mode, board, onSubmit }: BoardFormProps) { - {/* 버튼 영역 */} -
- - -
+ + {/* 하단 액션 버튼 (sticky) */} +
+ + +
); } \ No newline at end of file diff --git a/src/components/board/CommentSection/CommentItem.tsx b/src/components/board/CommentSection/CommentItem.tsx index c1154bf5..32c08770 100644 --- a/src/components/board/CommentSection/CommentItem.tsx +++ b/src/components/board/CommentSection/CommentItem.tsx @@ -12,7 +12,7 @@ import { useState, useCallback, memo } from 'react'; import { format } from 'date-fns'; -import { User, Pencil, Trash2 } from 'lucide-react'; +import { User, Edit, Trash2 } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { Textarea } from '@/components/ui/textarea'; import { DeleteConfirmDialog } from '@/components/ui/confirm-dialog'; @@ -90,7 +90,7 @@ export const CommentItem = memo(function CommentItem({ {/* 댓글 내용 */}
{/* 작성자 정보 + 날짜 + 버튼 */} -
+
{authorInfo} @@ -107,7 +107,7 @@ export const CommentItem = memo(function CommentItem({ className="h-7 px-2 text-gray-500 hover:text-gray-700" onClick={handleEditClick} > - + 수정 - -
+ + {/* 하단 액션 버튼 (sticky) */} +
+ + +
); } diff --git a/src/components/board/DynamicBoard/DynamicBoardEditForm.tsx b/src/components/board/DynamicBoard/DynamicBoardEditForm.tsx index f0a88322..b8f90662 100644 --- a/src/components/board/DynamicBoard/DynamicBoardEditForm.tsx +++ b/src/components/board/DynamicBoard/DynamicBoardEditForm.tsx @@ -7,7 +7,7 @@ import { useState, useEffect } from 'react'; import { useRouter } from 'next/navigation'; -import { ArrowLeft, Save, MessageSquare } from 'lucide-react'; +import { ArrowLeft, X, Save, MessageSquare } from 'lucide-react'; import { DetailPageSkeleton } from '@/components/ui/skeleton'; import { PageLayout } from '@/components/organisms/PageLayout'; import { PageHeader } from '@/components/organisms/PageHeader'; @@ -17,6 +17,7 @@ import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Textarea } from '@/components/ui/textarea'; import { Checkbox } from '@/components/ui/checkbox'; +import { useMenuStore } from '@/stores/menuStore'; import { getDynamicBoardPost, updateDynamicBoardPost } from '@/components/board/DynamicBoard/actions'; import { getBoardByCode } from '@/components/board/BoardManagement/actions'; import type { PostApiData } from '@/components/customer-center/shared/types'; @@ -59,6 +60,7 @@ interface DynamicBoardEditFormProps { export function DynamicBoardEditForm({ boardCode, postId }: DynamicBoardEditFormProps) { const router = useRouter(); + const sidebarCollapsed = useMenuStore((state) => state.sidebarCollapsed); // 게시판 정보 const [boardName, setBoardName] = useState('게시판'); @@ -134,7 +136,7 @@ export function DynamicBoardEditForm({ boardCode, postId }: DynamicBoardEditForm }); if (result.success) { - router.push(`/ko/boards/${boardCode}/${postId}`); + router.push(`/ko/boards/${boardCode}/${postId}?mode=view`); } else { setError(result.error || '게시글 수정에 실패했습니다.'); setIsSubmitting(false); @@ -143,7 +145,7 @@ export function DynamicBoardEditForm({ boardCode, postId }: DynamicBoardEditForm // 취소 const handleCancel = () => { - router.push(`/ko/boards/${boardCode}/${postId}`); + router.push(`/ko/boards/${boardCode}/${postId}?mode=view`); }; // 로딩 상태 @@ -178,7 +180,7 @@ export function DynamicBoardEditForm({ boardCode, postId }: DynamicBoardEditForm icon={MessageSquare} /> -
+ 게시글 수정 @@ -230,23 +232,21 @@ export function DynamicBoardEditForm({ boardCode, postId }: DynamicBoardEditForm - {/* 버튼 영역 */} -
- - -
+ + {/* 하단 액션 버튼 (sticky) */} +
+ + +
); } From e094c5ae499fedf7207eee14fb007119651101df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=EB=B3=91=EC=B2=A0?= Date: Wed, 25 Feb 2026 22:33:17 +0900 Subject: [PATCH 03/13] =?UTF-8?q?feat:=20[HR]=20=EC=9D=B8=EC=82=AC?= =?UTF-8?q?=EA=B4=80=EB=A6=AC=20=EC=A0=84=EB=B0=98=20UI=20=EA=B0=9C?= =?UTF-8?q?=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 근태관리 다이얼로그 개선 (AttendanceInfoDialog, ReasonInfoDialog) - 카드관리 상세 페이지 개선 (CardDetail) - 부서관리 트리 컴포넌트 개선 (DepartmentToolbar, DepartmentTreeItem) - 직원관리 폼 개선 (EmployeeForm) - 급여/휴가 관리 UI 개선 Co-Authored-By: Claude Opus 4.6 --- .../AttendanceInfoDialog.tsx | 56 ++++++------- .../AttendanceManagement/ReasonInfoDialog.tsx | 22 +++--- .../hr/AttendanceManagement/index.tsx | 2 +- .../hr/CalendarManagement/index.tsx | 2 +- .../hr/CardManagement/CardDetail.tsx | 79 +++++++++++-------- src/components/hr/CardManagement/index.tsx | 3 +- .../DepartmentToolbar.tsx | 11 +-- .../DepartmentManagement/DepartmentTree.tsx | 1 - .../DepartmentTreeItem.tsx | 72 +++++++++-------- .../hr/EmployeeManagement/EmployeeForm.tsx | 77 +++++++++--------- .../SalaryManagement/SalaryDetailDialog.tsx | 14 ++-- src/components/hr/SalaryManagement/index.tsx | 1 + .../VacationGrantDialog.tsx | 2 +- .../VacationRequestDialog.tsx | 2 +- .../hr/VacationManagement/index.tsx | 6 +- 15 files changed, 184 insertions(+), 166 deletions(-) diff --git a/src/components/hr/AttendanceManagement/AttendanceInfoDialog.tsx b/src/components/hr/AttendanceManagement/AttendanceInfoDialog.tsx index 97ecbd8f..80e25776 100644 --- a/src/components/hr/AttendanceManagement/AttendanceInfoDialog.tsx +++ b/src/components/hr/AttendanceManagement/AttendanceInfoDialog.tsx @@ -94,20 +94,20 @@ export function AttendanceInfoDialog({ return ( - + {title} -
+
{/* 대상 선택 */} -
- +
+ handleChange('checkInHour', value)} > - + @@ -154,7 +154,7 @@ export function AttendanceInfoDialog({ value={formData.checkInMinute} onValueChange={(value) => handleChange('checkInMinute', value)} > - + @@ -169,14 +169,14 @@ export function AttendanceInfoDialog({
{/* 퇴근 시간 */} -
- -
+
+ +
handleChange('nightOvertimeHours', value)} > - + @@ -228,7 +228,7 @@ export function AttendanceInfoDialog({ value={formData.nightOvertimeMinutes} onValueChange={(value) => handleChange('nightOvertimeMinutes', value)} > - + @@ -243,14 +243,14 @@ export function AttendanceInfoDialog({
{/* 주말 연장 시간 */} -
- -
+
+ +
handleChange('employeeId', value)} > - + @@ -87,24 +87,24 @@ export function ReasonInfoDialog({
{/* 기준일 */} -
- +
+ handleChange('baseDate', date)} - className="w-[200px]" + className="flex-1 min-w-0" align="end" />
{/* 유형 선택 */} -
- +
+ handleDepartmentSelect(dp.id, value)} - disabled={isViewMode} - > - - - {dp.departmentName || '부서 선택'} - - - - {departments.map((dept) => ( - - - {formatDepartmentName(dept.name, dept.depth)} - - - ))} - - - +
+ + +
{!isViewMode && (
+ + {/* 하단 액션 버튼 (sticky) */} +
+ +
+ + -
- - -
@@ -330,17 +338,18 @@ export function AccountDetail({ account, mode: initialMode }: AccountDetailProps - {/* 버튼 영역 */} -
- - -
+
+ + {/* 하단 액션 버튼 (sticky) */} +
+ +
); diff --git a/src/components/settings/AccountManagement/AccountDetailForm.tsx b/src/components/settings/AccountManagement/AccountDetailForm.tsx index e00f644e..a6d419fb 100644 --- a/src/components/settings/AccountManagement/AccountDetailForm.tsx +++ b/src/components/settings/AccountManagement/AccountDetailForm.tsx @@ -12,7 +12,7 @@ import { useState, useCallback, useMemo } from 'react'; import { useRouter } from 'next/navigation'; -import { Landmark, Save, Trash2, ArrowLeft } from 'lucide-react'; +import { Landmark, Save, Trash2, ArrowLeft, Edit } from 'lucide-react'; import { toast } from 'sonner'; import { Button } from '@/components/ui/button'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; @@ -20,6 +20,7 @@ import { DeleteConfirmDialog } from '@/components/ui/confirm-dialog'; import { PageLayout } from '@/components/organisms/PageLayout'; import { PageHeader } from '@/components/organisms/PageHeader'; import { FormField } from '@/components/molecules/FormField'; +import { useMenuStore } from '@/stores/menuStore'; import type { Account, AccountCategory, AccountFormData } from './types'; import { ACCOUNT_CATEGORY_OPTIONS, @@ -98,6 +99,7 @@ export function AccountDetailForm({ isLoading, }: AccountDetailFormProps) { const router = useRouter(); + const sidebarCollapsed = useMenuStore((state) => state.sidebarCollapsed); const [mode, setMode] = useState(initialMode); const [formData, setFormData] = useState(() => getInitialFormData(initialData)); const [isSaving, setIsSaving] = useState(false); @@ -216,7 +218,7 @@ export function AccountDetailForm({ icon={Landmark} /> -
+
{/* ===== 기본 정보 ===== */} @@ -321,26 +323,30 @@ export function AccountDetailForm({ )} - {/* ===== 하단 버튼 ===== */} -
- -
+
{isViewMode ? ( <> {onDelete && ( )} - + ) : ( <> @@ -348,15 +354,16 @@ export function AccountDetailForm({ )} - )} diff --git a/src/components/settings/AccountManagement/index.tsx b/src/components/settings/AccountManagement/index.tsx index f3827391..b8f7a1b3 100644 --- a/src/components/settings/AccountManagement/index.tsx +++ b/src/components/settings/AccountManagement/index.tsx @@ -344,6 +344,7 @@ export function AccountManagement() { {ACCOUNT_STATUS_LABELS[item.status]} } + showCheckbox={false} isSelected={false} onToggleSelection={() => {}} onClick={() => handleRowClick(item)} diff --git a/src/components/settings/AttendanceSettingsManagement/index.tsx b/src/components/settings/AttendanceSettingsManagement/index.tsx index 1c069d98..fc02cf19 100644 --- a/src/components/settings/AttendanceSettingsManagement/index.tsx +++ b/src/components/settings/AttendanceSettingsManagement/index.tsx @@ -181,12 +181,10 @@ export function AttendanceSettingsManagement() { GPS 출퇴근 - + {/* GPS 출퇴근 사용 + 연동 부서 */} -
-
- GPS 출퇴근 -
+
+ GPS 출퇴근 -
+
연동 부서
{/* 출퇴근 허용 반경 */} -
-
- 출퇴근 허용 반경 -
+
+ 출퇴근 허용 반경 - onChange({ ...item, soundType: value }) - } - disabled={isDisabled} - > - - - - - {SOUND_OPTIONS.map((option) => ( - - {option.label} - - ))} - - - +
+ 알림 소리 선택 +
+ + +
{/* 추가 알림 선택 */} -
- 추가 알림 선택 +
+ 추가 알림 선택