테이블 헤더 및 행 배경색 통일

- 모든 테이블 헤더의 회색 배경 제거 (bg-muted/50 → bg-background)
- 모든 테이블 행의 홀수 행 회색 배경 제거 (모든 행을 흰색 배경으로 통일)
- 호버 시에만 회색 배경이 나타나도록 통일
- TableListComponent, SingleTableWithSticky, 모든 관리자 테이블 컴포넌트에 적용
- 테이블 구조 표준화 문서 업데이트
This commit is contained in:
kjs 2025-10-30 15:49:23 +09:00
parent 4010273d67
commit 21af6c5c17
11 changed files with 121 additions and 79 deletions

View File

@ -406,6 +406,55 @@ className={cn(
- 테이블 외곽 테두리는 없지만, 행 구분선으로 데이터 구분이 가능합니다
- 시각적으로 깔끔하면서도 데이터 구분이 명확한 디자인이 적용되었습니다
### 테이블 구조 표준화 완료
#### 표준 테이블 스타일 정의
모든 테이블 컴포넌트에 동일한 스타일을 적용하여 일관성을 확보했습니다:
**표준 스타일:**
- **헤더 높이**: `h-12` (48px)
- **헤더 패딩**: `px-6 py-3`
- **헤더 텍스트**: `text-sm font-semibold`
- **헤더 배경**: `bg-background` (흰색 배경으로 통일)
- **행 높이**: `h-16` (64px)
- **행 패딩**: `px-6 py-3`
- **행 텍스트**: `text-sm`
- **행 배경**: `bg-background` (모든 행 흰색 배경으로 통일)
- **행 호버**: `hover:bg-muted/50 transition-colors`
- **행 구분선**: `border-b` (기본 TableRow에 포함)
**표준화된 컴포넌트:**
- ✅ `TableListComponent.tsx`: 행 높이 `h-12``h-16`, 호버 `hover:bg-destructive/10``hover:bg-muted/50`, 홀수 행 배경 제거 (모든 행 흰색 배경으로 통일), 헤더 배경 `bg-muted/50``bg-background`
- ✅ `FlowWidget.tsx`: 패딩 `px-3 py-2``px-6 py-3`, 텍스트 `text-xs sm:text-sm``text-sm`, 행 높이 `h-16` 명시
- ✅ `SingleTableWithSticky.tsx`: 행 높이 `h-12``h-16`, 하드코딩된 색상 제거, 배경 `bg-white``bg-background`, 홀수 행 배경 제거 (설정 기반 alternateRows 제거), 헤더 배경 `bg-muted/50``bg-background`
- ✅ 모든 관리자 테이블: 헤더 배경 `bg-muted/50``bg-background` (`UserTable`, `CompanyTable`, `MenuTable`, `ColumnDefinitionTable`, `UserAuthTable`, `MenuPermissionsTable`, `RestApiConnectionList`)
**수정 요약:**
| 항목 | 변경 내용 | 적용 상태 |
| ----------- | --------------------------------------------------- | --------- |
| 헤더 높이 | 모든 테이블 `h-12`로 통일 | ✅ 완료 |
| 헤더 패딩 | 모든 테이블 `px-6 py-3`로 통일 | ✅ 완료 |
| 헤더 배경 | 모든 테이블 헤더를 `bg-background`로 통일 (회색 배경 제거) | ✅ 완료 |
| 행 높이 | 모든 테이블 `h-16`로 통일 | ✅ 완료 |
| 행 패딩 | 모든 테이블 `px-6 py-3`로 통일 | ✅ 완료 |
| 텍스트 크기 | 모든 테이블 `text-sm`로 통일 | ✅ 완료 |
| 행 배경 | 모든 행을 `bg-background`로 통일 (호버 시에만 회색) | ✅ 완료 |
| 호버 효과 | 모든 테이블 `hover:bg-muted/50`로 통일 | ✅ 완료 |
| 색상 시스템 | 하드코딩된 색상 제거, CSS 변수 사용 | ✅ 완료 |
**결과:**
- 모든 테이블이 동일한 높이, 패딩, 텍스트 크기로 표시됩니다
- 모든 행과 헤더가 흰색 배경으로 통일되어 일관성이 확보되었습니다
- 호버 시에만 회색 배경이 나타나 깔끔하고 모던한 디자인입니다
- 일관된 호버 효과와 스타일이 적용되었습니다
- CSS 변수를 사용하여 테마 대응이 가능합니다
- 관리자 테이블과 위젯 테이블이 동일한 디자인으로 통일되었습니다
### 최종 적용률 업데이트
| 항목 | 상태 | 비율 |
@ -416,8 +465,9 @@ className={cn(
| 색상 시스템 | ✅ 완료 | ~98% |
| 패딩 중복 | ✅ 대부분 수정 | ~90% |
| 반응형 디자인 | ✅ 개선됨 | ~75% |
| 테이블 표준화 | ✅ 완료 | ~95% |
| 테이블 표준화 | ✅ 완료 | ~100% |
| **테이블 테두리 및 라운드 수정** | ✅ 완료 | ~100% |
| **테이블 구조 표준화** | ✅ 완료 | ~100% |
### 추가 완료된 작업

View File

@ -166,7 +166,7 @@ export function ColumnDefinitionTable({ columns, onChange, disabled = false }: C
<div className="overflow-hidden bg-card shadow-sm">
<Table>
<TableHeader>
<TableRow className="bg-muted/50 hover:bg-muted/50">
<TableRow>
<TableHead className="h-12 w-[150px] text-sm font-semibold">
<span className="text-destructive">*</span>
</TableHead>

View File

@ -52,7 +52,7 @@ export function CompanyTable({ companies, isLoading, onEdit, onDelete }: Company
<div className="hidden bg-card shadow-sm lg:block">
<Table>
<TableHeader>
<TableRow className="bg-muted/50 hover:bg-muted/50">
<TableRow>
{COMPANY_TABLE_COLUMNS.map((column) => (
<TableHead key={column.key} className="h-12 text-sm font-semibold">
{column.label}
@ -131,17 +131,17 @@ export function CompanyTable({ companies, isLoading, onEdit, onDelete }: Company
{/* 데스크톱 테이블 뷰 (lg 이상) */}
<div className="hidden bg-card shadow-sm lg:block">
<Table>
<TableHeader>
<TableRow className="bg-muted/50 hover:bg-muted/50">
{COMPANY_TABLE_COLUMNS.map((column) => (
<TableHead key={column.key} className="h-12 text-sm font-semibold">
{column.label}
</TableHead>
))}
<TableHead className="h-12 text-sm font-semibold"> </TableHead>
<TableHead className="h-12 text-sm font-semibold"></TableHead>
</TableRow>
</TableHeader>
<TableHeader>
<TableRow>
{COMPANY_TABLE_COLUMNS.map((column) => (
<TableHead key={column.key} className="h-12 text-sm font-semibold">
{column.label}
</TableHead>
))}
<TableHead className="h-12 text-sm font-semibold"> </TableHead>
<TableHead className="h-12 text-sm font-semibold"></TableHead>
</TableRow>
</TableHeader>
<TableBody>
{companies.map((company) => (
<TableRow key={company.regdate + company.company_code} className="transition-colors hover:bg-muted/50">

View File

@ -282,7 +282,7 @@ export function MenuPermissionsTable({ permissions, onPermissionsChange, roleGro
<div className="bg-card hidden shadow-sm lg:block">
<Table>
<TableHeader>
<TableRow className="bg-muted/50 hover:bg-muted/50">
<TableRow>
<TableHead className="h-12 w-[40%] text-sm font-semibold"></TableHead>
<TableHead className="h-12 w-[15%] text-center text-sm font-semibold">
<div className="flex flex-col items-center gap-1">

View File

@ -148,9 +148,9 @@ export const MenuTable: React.FC<MenuTableProps> = ({
<div className="bg-card shadow-sm">
<div className="max-h-[calc(100vh-350px)] overflow-auto">
<Table noWrapper>
<TableHeader className="sticky top-0 z-20 bg-muted/50 shadow-sm">
<TableRow className="bg-muted/50 hover:bg-muted/50">
<TableHead className="h-12 w-12 bg-muted/50 text-sm font-semibold">
<TableHeader className="sticky top-0 z-20 bg-background shadow-sm">
<TableRow>
<TableHead className="h-12 w-12 text-sm font-semibold">
<input
type="checkbox"
checked={
@ -161,22 +161,22 @@ export const MenuTable: React.FC<MenuTableProps> = ({
className="h-4 w-4"
/>
</TableHead>
<TableHead className="h-12 w-1/3 bg-muted/50 text-sm font-semibold">
<TableHead className="h-12 w-1/3 text-sm font-semibold">
{getText(MENU_MANAGEMENT_KEYS.TABLE_HEADER_MENU_NAME)}
</TableHead>
<TableHead className="h-12 w-16 bg-muted/50 text-sm font-semibold">
<TableHead className="h-12 w-16 text-sm font-semibold">
{getText(MENU_MANAGEMENT_KEYS.TABLE_HEADER_SEQUENCE)}
</TableHead>
<TableHead className="h-12 w-24 bg-muted/50 text-sm font-semibold">
<TableHead className="h-12 w-24 text-sm font-semibold">
{getText(MENU_MANAGEMENT_KEYS.TABLE_HEADER_COMPANY)}
</TableHead>
<TableHead className="h-12 w-48 bg-muted/50 text-sm font-semibold">
<TableHead className="h-12 w-48 text-sm font-semibold">
{getText(MENU_MANAGEMENT_KEYS.TABLE_HEADER_MENU_URL)}
</TableHead>
<TableHead className="h-12 w-20 bg-muted/50 text-sm font-semibold">
<TableHead className="h-12 w-20 text-sm font-semibold">
{getText(MENU_MANAGEMENT_KEYS.TABLE_HEADER_STATUS)}
</TableHead>
<TableHead className="h-12 w-32 bg-muted/50 text-sm font-semibold">
<TableHead className="h-12 w-32 text-sm font-semibold">
{getText(MENU_MANAGEMENT_KEYS.TABLE_HEADER_ACTIONS)}
</TableHead>
</TableRow>

View File

@ -265,7 +265,7 @@ export function RestApiConnectionList() {
<div className="bg-card shadow-sm">
<Table>
<TableHeader>
<TableRow className="bg-muted/50 hover:bg-muted/50">
<TableRow>
<TableHead className="h-12 text-sm font-semibold"></TableHead>
<TableHead className="h-12 text-sm font-semibold"> URL</TableHead>
<TableHead className="h-12 text-sm font-semibold"> </TableHead>

View File

@ -78,7 +78,7 @@ export function UserAuthTable({ users, isLoading, paginationInfo, onEditAuth, on
<div className="bg-card hidden shadow-sm lg:block">
<Table>
<TableHeader>
<TableRow className="bg-muted/50">
<TableRow>
<TableHead className="h-12 w-[80px] text-center text-sm font-semibold">No</TableHead>
<TableHead className="h-12 text-sm font-semibold"> ID</TableHead>
<TableHead className="h-12 text-sm font-semibold"></TableHead>
@ -136,7 +136,7 @@ export function UserAuthTable({ users, isLoading, paginationInfo, onEditAuth, on
<div className="bg-card hidden shadow-sm lg:block">
<Table>
<TableHeader>
<TableRow className="bg-muted/50 hover:bg-muted/50">
<TableRow>
<TableHead className="h-12 w-[80px] text-center text-sm font-semibold">No</TableHead>
<TableHead className="h-12 text-sm font-semibold"> ID</TableHead>
<TableHead className="h-12 text-sm font-semibold"></TableHead>

View File

@ -111,7 +111,7 @@ export function UserTable({
<div className="bg-card hidden shadow-sm lg:block">
<Table>
<TableHeader>
<TableRow className="bg-muted/50">
<TableRow>
{USER_TABLE_COLUMNS.map((column) => (
<TableHead key={column.key} style={{ width: column.width }} className="h-12 text-sm font-semibold">
{column.label}
@ -188,16 +188,16 @@ export function UserTable({
{/* 데스크톱 테이블 뷰 (lg 이상) */}
<div className="bg-card hidden shadow-sm lg:block">
<Table>
<TableHeader>
<TableRow className="bg-muted/50 hover:bg-muted/50">
{USER_TABLE_COLUMNS.map((column) => (
<TableHead key={column.key} style={{ width: column.width }} className="h-12 text-sm font-semibold">
{column.label}
</TableHead>
))}
<TableHead className="h-12 w-[200px] text-sm font-semibold"></TableHead>
</TableRow>
</TableHeader>
<TableHeader>
<TableRow>
{USER_TABLE_COLUMNS.map((column) => (
<TableHead key={column.key} style={{ width: column.width }} className="h-12 text-sm font-semibold">
{column.label}
</TableHead>
))}
<TableHead className="h-12 w-[200px] text-sm font-semibold"></TableHead>
</TableRow>
</TableHeader>
<TableBody>
{users.map((user, index) => (
<TableRow key={`${user.userId}-${index}`} className="hover:bg-muted/50 transition-colors">

View File

@ -853,7 +853,7 @@ export function FlowWidget({
<TableHeader>
<TableRow className="hover:bg-muted/50">
{allowDataMove && (
<TableHead className="bg-background sticky top-0 left-0 z-20 w-12 border-b px-3 py-2 text-center shadow-[0_1px_0_0_rgb(0,0,0,0.1)]">
<TableHead className="bg-background sticky top-0 left-0 z-20 w-12 border-b px-6 py-3 text-center shadow-[0_1px_0_0_rgb(0,0,0,0.1)]">
<Checkbox
checked={selectedRows.size === stepData.length && stepData.length > 0}
onCheckedChange={toggleAllRows}
@ -863,7 +863,7 @@ export function FlowWidget({
{stepDataColumns.map((col) => (
<TableHead
key={col}
className="bg-background sticky top-0 z-10 border-b px-3 py-2 text-xs font-semibold whitespace-nowrap shadow-[0_1px_0_0_rgb(0,0,0,0.1)] sm:text-sm"
className="bg-background sticky top-0 z-10 border-b px-6 py-3 text-sm font-semibold whitespace-nowrap shadow-[0_1px_0_0_rgb(0,0,0,0.1)]"
>
{columnLabels[col] || col}
</TableHead>
@ -876,10 +876,10 @@ export function FlowWidget({
return (
<TableRow
key={actualIndex}
className={`hover:bg-muted/50 ${selectedRows.has(actualIndex) ? "bg-primary/5" : ""}`}
className={`h-16 transition-colors hover:bg-muted/50 ${selectedRows.has(actualIndex) ? "bg-primary/5" : ""}`}
>
{allowDataMove && (
<TableCell className="bg-background sticky left-0 z-10 border-b px-3 py-2 text-center">
<TableCell className="bg-background sticky left-0 z-10 border-b px-6 py-3 text-center">
<Checkbox
checked={selectedRows.has(actualIndex)}
onCheckedChange={() => toggleRowSelection(actualIndex)}
@ -887,7 +887,7 @@ export function FlowWidget({
</TableCell>
)}
{stepDataColumns.map((col) => (
<TableCell key={col} className="border-b px-3 py-2 text-xs whitespace-nowrap sm:text-sm">
<TableCell key={col} className="h-16 border-b px-6 py-3 text-sm whitespace-nowrap">
{row[col] !== null && row[col] !== undefined ? (
String(row[col])
) : (

View File

@ -46,7 +46,7 @@ export const SingleTableWithSticky: React.FC<SingleTableWithStickyProps> = ({
return (
<div
className="relative h-full overflow-x-auto overflow-y-auto bg-white shadow-sm backdrop-blur-sm"
className="relative h-full overflow-x-auto overflow-y-auto bg-background shadow-sm backdrop-blur-sm"
style={{
width: "100%",
maxWidth: "100%",
@ -66,11 +66,11 @@ export const SingleTableWithSticky: React.FC<SingleTableWithStickyProps> = ({
<TableHeader
className={
tableConfig.stickyHeader
? "sticky top-0 z-20 border-b border-gray-200/40 bg-gradient-to-r from-slate-50/90 to-gray-50/70 backdrop-blur-sm"
: "border-b border-gray-200/40 bg-gradient-to-r from-slate-50/90 to-gray-50/70 backdrop-blur-sm"
? "sticky top-0 z-20 border-b bg-background backdrop-blur-sm"
: "border-b bg-background backdrop-blur-sm"
}
>
<TableRow className="border-b border-gray-200/40">
<TableRow className="border-b">
{visibleColumns.map((column, colIndex) => {
// 왼쪽 고정 컬럼들의 누적 너비 계산
const leftFixedWidth = visibleColumns
@ -91,17 +91,17 @@ export const SingleTableWithSticky: React.FC<SingleTableWithStickyProps> = ({
key={column.columnName}
className={cn(
column.columnName === "__checkbox__"
? "h-12 border-0 px-6 py-4 text-center align-middle"
: "h-12 cursor-pointer border-0 px-6 py-4 text-left align-middle font-semibold whitespace-nowrap text-gray-700 transition-all duration-200 select-none hover:text-gray-900",
? "h-12 border-0 px-6 py-3 text-center align-middle"
: "h-12 cursor-pointer border-0 px-6 py-3 text-left align-middle font-semibold whitespace-nowrap text-foreground transition-all duration-200 select-none hover:text-foreground",
`text-${column.align}`,
column.sortable && "hover:bg-orange-200/70",
column.sortable && "hover:bg-primary/10",
// 고정 컬럼 스타일
column.fixed === "left" &&
"sticky z-10 border-r border-gray-200/40 bg-gradient-to-r from-slate-50/90 to-gray-50/70 shadow-sm",
"sticky z-10 border-r border-border bg-background shadow-sm",
column.fixed === "right" &&
"sticky z-10 border-l border-gray-200/40 bg-gradient-to-r from-slate-50/90 to-gray-50/70 shadow-sm",
"sticky z-10 border-l border-border bg-background shadow-sm",
// 숨김 컬럼 스타일 (디자인 모드에서만)
isDesignMode && column.hidden && "bg-gray-100/50 opacity-40",
isDesignMode && column.hidden && "bg-muted/50 opacity-40",
)}
style={{
width: getColumnWidth(column),
@ -133,11 +133,11 @@ export const SingleTableWithSticky: React.FC<SingleTableWithStickyProps> = ({
{columnLabels[column.columnName] || column.displayName || column.columnName}
</span>
{column.sortable && sortColumn === column.columnName && (
<span className="ml-2 flex h-5 w-5 items-center justify-center rounded-md bg-white/50 shadow-sm">
<span className="ml-2 flex h-5 w-5 items-center justify-center rounded-md bg-background/50 shadow-sm">
{sortDirection === "asc" ? (
<ArrowUp className="h-3.5 w-3.5 text-blue-600" />
<ArrowUp className="h-3.5 w-3.5 text-primary" />
) : (
<ArrowDown className="h-3.5 w-3.5 text-blue-600" />
<ArrowDown className="h-3.5 w-3.5 text-primary" />
)}
</span>
)}
@ -153,10 +153,10 @@ export const SingleTableWithSticky: React.FC<SingleTableWithStickyProps> = ({
<TableBody>
{data.length === 0 ? (
<TableRow>
<TableCell colSpan={visibleColumns.length} className="py-12 text-center">
<div className="flex flex-col items-center justify-center space-y-3">
<div className="flex h-12 w-12 items-center justify-center rounded-full bg-gradient-to-br from-gray-100 to-gray-200">
<svg className="h-6 w-6 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<TableCell colSpan={visibleColumns.length} className="py-12 text-center">
<div className="flex flex-col items-center justify-center space-y-3">
<div className="flex h-12 w-12 items-center justify-center rounded-full bg-muted">
<svg className="h-6 w-6 text-muted-foreground" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
strokeLinecap="round"
strokeLinejoin="round"
@ -165,8 +165,8 @@ export const SingleTableWithSticky: React.FC<SingleTableWithStickyProps> = ({
/>
</svg>
</div>
<span className="text-sm font-medium text-gray-500"> </span>
<span className="rounded-full bg-gray-100 px-3 py-1 text-xs text-gray-400">
<span className="text-sm font-medium text-muted-foreground"> </span>
<span className="rounded-full bg-muted px-3 py-1 text-xs text-muted-foreground">
</span>
</div>
@ -177,12 +177,9 @@ export const SingleTableWithSticky: React.FC<SingleTableWithStickyProps> = ({
<TableRow
key={`row-${index}`}
className={cn(
"h-12 cursor-pointer border-b border-gray-100/40 leading-none transition-all duration-200",
tableConfig.tableStyle?.hoverEffect &&
"hover:bg-gradient-to-r hover:from-orange-50/80 hover:to-orange-100/60 hover:shadow-sm",
tableConfig.tableStyle?.alternateRows && index % 2 === 1 && "bg-gray-50/30",
"h-16 cursor-pointer border-b transition-colors bg-background",
tableConfig.tableStyle?.hoverEffect && "hover:bg-muted/50",
)}
style={{ minHeight: "48px", height: "48px", lineHeight: "1" }}
onClick={() => handleRowClick(row)}
>
{visibleColumns.map((column, colIndex) => {
@ -204,18 +201,15 @@ export const SingleTableWithSticky: React.FC<SingleTableWithStickyProps> = ({
<TableCell
key={`cell-${column.columnName}`}
className={cn(
"h-12 px-6 py-4 align-middle text-sm whitespace-nowrap text-gray-600 transition-all duration-200",
"h-16 px-6 py-3 align-middle text-sm whitespace-nowrap text-foreground transition-colors",
`text-${column.align}`,
// 고정 컬럼 스타일
column.fixed === "left" &&
"sticky z-10 border-r border-gray-200/40 bg-white/90 backdrop-blur-sm",
"sticky z-10 border-r border-border bg-background/90 backdrop-blur-sm",
column.fixed === "right" &&
"sticky z-10 border-l border-gray-200/40 bg-white/90 backdrop-blur-sm",
"sticky z-10 border-l border-border bg-background/90 backdrop-blur-sm",
)}
style={{
minHeight: "48px",
height: "48px",
verticalAlign: "middle",
width: getColumnWidth(column),
minWidth: "100px", // 최소 너비 보장
maxWidth: "300px", // 최대 너비 제한

View File

@ -993,14 +993,14 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
>
{/* 헤더 (sticky) */}
<thead
className="sticky top-0 z-10 bg-muted"
className="sticky top-0 z-10 bg-background"
>
<tr className="h-12 border-b border-border">
{visibleColumns.map((column) => (
<th
key={column.columnName}
className={cn(
"px-6 py-3 text-sm font-semibold text-foreground whitespace-nowrap bg-muted",
"h-12 px-6 py-3 text-sm font-semibold text-foreground whitespace-nowrap bg-background",
column.sortable && "cursor-pointer"
)}
style={{
@ -1063,9 +1063,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
onDragStart={(e) => handleRowDragStart(e, row, index)}
onDragEnd={handleRowDragEnd}
className={cn(
"h-12 border-b border-border/50 cursor-pointer transition-colors",
index % 2 === 1 ? "bg-muted/50" : "bg-background",
"hover:bg-destructive/10"
"h-16 border-b transition-colors bg-background hover:bg-muted/50 cursor-pointer"
)}
onClick={() => handleRowClick(row)}
>
@ -1077,7 +1075,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
<td
key={column.columnName}
className={cn(
"px-6 py-3 text-sm text-foreground whitespace-nowrap overflow-hidden text-ellipsis"
"h-16 px-6 py-3 text-sm text-foreground whitespace-nowrap overflow-hidden text-ellipsis"
)}
style={{
textAlign: column.align || "left",