[agent-pipeline] pipe-20260317063830-0nfs round-2

This commit is contained in:
DDD1542 2026-03-17 16:47:12 +09:00
parent 128872b766
commit 265f46f8d4
12 changed files with 88 additions and 84 deletions

View File

@ -478,7 +478,7 @@ const DateConfigPanel: React.FC<DateConfigPanelProps> = ({
</PopoverContent> </PopoverContent>
</Popover> </Popover>
{sourceTableName && columns.length === 0 && !loadingColumns && ( {sourceTableName && columns.length === 0 && !loadingColumns && (
<p className="mt-1 text-[10px] text-amber-600 sm:text-xs"> <p className="mt-1 text-[10px] text-warning sm:text-xs">
</p> </p>
)} )}

View File

@ -1,7 +1,6 @@
"use client"; "use client";
import React from "react"; import React from "react";
import { Card, CardContent, CardHeader } from "@/components/ui/card";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Label } from "@/components/ui/label"; import { Label } from "@/components/ui/label";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
@ -27,25 +26,24 @@ export const NumberingRuleCard: React.FC<NumberingRuleCardProps> = ({
tableName, tableName,
}) => { }) => {
return ( return (
<Card className="border-border bg-card flex-1"> <div className="config-field flex-1 rounded-lg border border-border bg-muted/50 p-3 sm:p-4">
<CardHeader className="pb-3"> <div className="mb-3 flex items-center justify-between sm:mb-4">
<div className="flex items-center justify-between">
<Badge variant="outline" className="text-xs sm:text-sm"> <Badge variant="outline" className="text-xs sm:text-sm">
{part.order} {part.order}
</Badge> </Badge>
<Button <Button
variant="ghost" variant="destructive"
size="icon" size="icon"
onClick={onDelete} onClick={onDelete}
className="text-destructive h-7 w-7 sm:h-8 sm:w-8" className="h-7 w-7 sm:h-8 sm:w-8"
disabled={isPreview} disabled={isPreview}
aria-label="규칙 삭제"
> >
<Trash2 className="h-3 w-3 sm:h-4 sm:w-4" /> <Trash2 className="h-3 w-3 sm:h-4 sm:w-4" />
</Button> </Button>
</div> </div>
</CardHeader>
<CardContent className="space-y-3 sm:space-y-4"> <div className="space-y-3 sm:space-y-4">
<div> <div>
<Label className="text-xs font-medium sm:text-sm"> </Label> <Label className="text-xs font-medium sm:text-sm"> </Label>
<Select <Select
@ -117,7 +115,7 @@ export const NumberingRuleCard: React.FC<NumberingRuleCardProps> = ({
isPreview={isPreview} isPreview={isPreview}
/> />
)} )}
</CardContent> </div>
</Card> </div>
); );
}; };

View File

@ -264,17 +264,18 @@ export const NumberingRuleDesigner: React.FC<NumberingRuleDesignerProps> = ({
const partItems = currentRule ? computePartDisplayItems(currentRule) : []; const partItems = currentRule ? computePartDisplayItems(currentRule) : [];
return ( return (
<div className={cn("flex h-full gap-4", className)}> <div className={cn("flex h-full", className)}>
{/* 좌측: 규칙 리스트 (code-nav, 220px) */} {/* 좌측: 규칙 리스트 (code-nav, 220px) */}
<div className="code-nav flex w-[220px] flex-shrink-0 flex-col gap-3"> <div className="code-nav flex w-[220px] flex-shrink-0 flex-col border-r border-border">
<div className="flex items-center justify-between gap-2"> <div className="code-nav-head flex items-center justify-between gap-2 border-b border-border px-3 py-2.5">
<div className="flex items-center gap-2"> <div className="flex min-w-0 flex-1 items-center gap-2">
<ListOrdered className="h-4 w-4 text-muted-foreground" /> <ListOrdered className="h-4 w-4 shrink-0 text-muted-foreground" />
<span className="text-sm font-semibold"> ({rulesList.length})</span> <span className="truncate text-xs font-bold"> ({rulesList.length})</span>
</div> </div>
<Button <Button
size="sm" size="sm"
className="h-8 gap-1 text-xs font-medium" variant="default"
className="h-8 shrink-0 gap-1 text-xs font-medium"
onClick={handleAddNewRule} onClick={handleAddNewRule}
disabled={isPreview || loading} disabled={isPreview || loading}
> >
@ -282,7 +283,7 @@ export const NumberingRuleDesigner: React.FC<NumberingRuleDesignerProps> = ({
</Button> </Button>
</div> </div>
<div className="flex-1 space-y-0.5 overflow-y-auto"> <div className="code-nav-list flex-1 overflow-y-auto">
{loading && rulesList.length === 0 ? ( {loading && rulesList.length === 0 ? (
<div className="flex h-24 items-center justify-center text-xs text-muted-foreground"> <div className="flex h-24 items-center justify-center text-xs text-muted-foreground">
... ...
@ -299,21 +300,21 @@ export const NumberingRuleDesigner: React.FC<NumberingRuleDesignerProps> = ({
key={rule.ruleId} key={rule.ruleId}
type="button" type="button"
className={cn( className={cn(
"code-nav-item flex w-full flex-col items-start gap-0.5 rounded-md px-3 py-2 text-left transition-colors", "code-nav-item flex w-full items-center gap-2 border-b border-border/50 px-3 py-2 text-left transition-colors",
isSelected isSelected
? "border-l-[3px] border-primary bg-primary/5 font-bold" ? "border-l-[3px] border-primary bg-primary/5 pl-2.5 font-bold"
: "hover:bg-accent" : "hover:bg-accent"
)} )}
onClick={() => handleSelectRule(rule)} onClick={() => handleSelectRule(rule)}
> >
<span className="rule-name min-w-0 truncate text-xs">{rule.ruleName}</span> <span className="rule-name min-w-0 flex-1 truncate text-xs font-semibold">
<span className="rule-table text-[9px] text-muted-foreground"> {rule.ruleName}
</span>
<span className="rule-table max-w-[70px] shrink-0 truncate text-[9px] text-muted-foreground">
{rule.tableName || "-"} {rule.tableName || "-"}
</span> </span>
<span className="mt-0.5 inline-flex"> <span className="rule-parts shrink-0 rounded-full bg-muted px-1.5 py-0.5 text-[8px] font-bold text-muted-foreground">
<span className="rounded bg-muted px-1.5 py-0.5 text-[10px] font-medium text-muted-foreground"> {rule.parts?.length ?? 0}
{rule.parts?.length ?? 0}
</span>
</span> </span>
</button> </button>
); );
@ -322,10 +323,8 @@ export const NumberingRuleDesigner: React.FC<NumberingRuleDesignerProps> = ({
</div> </div>
</div> </div>
<div className="h-full w-px flex-shrink-0 bg-border" /> {/* 우측: 미리보기 + 파이프라인 + 설정 + 저장 바 (code-main) */}
<div className="code-main flex min-w-0 flex-1 flex-col overflow-hidden">
{/* 우측: 미리보기 + 파이프라인 + 설정 + 저장 바 */}
<div className="flex flex-1 flex-col gap-4 overflow-hidden">
{!currentRule ? ( {!currentRule ? (
<div className="flex flex-1 flex-col items-center justify-center text-center"> <div className="flex flex-1 flex-col items-center justify-center text-center">
<ListOrdered className="mb-3 h-10 w-10 text-muted-foreground" /> <ListOrdered className="mb-3 h-10 w-10 text-muted-foreground" />
@ -336,7 +335,7 @@ export const NumberingRuleDesigner: React.FC<NumberingRuleDesignerProps> = ({
</div> </div>
) : ( ) : (
<> <>
<div className="flex flex-col gap-2"> <div className="flex flex-col gap-2 px-6 pt-4">
<Label className="text-xs font-medium"></Label> <Label className="text-xs font-medium"></Label>
<Input <Input
value={currentRule.ruleName} value={currentRule.ruleName}
@ -347,19 +346,19 @@ export const NumberingRuleDesigner: React.FC<NumberingRuleDesignerProps> = ({
</div> </div>
{/* 큰 미리보기 스트립 (code-preview-strip) */} {/* 큰 미리보기 스트립 (code-preview-strip) */}
<div className="code-preview-strip flex-shrink-0"> <div className="code-preview-strip flex-shrink-0 border-b border-border px-6 py-5">
<NumberingRulePreview config={currentRule} variant="strip" /> <NumberingRulePreview config={currentRule} variant="strip" />
</div> </div>
{/* 파이프라인 영역 (code-pipeline-area) */} {/* 파이프라인 영역 (code-pipeline-area) */}
<div className="code-pipeline-area flex flex-col gap-2"> <div className="code-pipeline-area flex flex-col gap-3 border-b border-border px-6 py-5">
<div className="flex items-center justify-between"> <div className="area-label flex items-center gap-1.5">
<span className="text-xs font-semibold text-muted-foreground"> </span> <span className="text-xs font-bold"> </span>
<span className="text-xs text-muted-foreground"> <span className="cnt text-xs font-medium text-muted-foreground">
{currentRule.parts.length}/{maxRules} {currentRule.parts.length}/{maxRules}
</span> </span>
</div> </div>
<div className="flex flex-1 flex-wrap items-center gap-2 overflow-x-auto overflow-y-hidden py-1"> <div className="code-pipeline flex flex-1 flex-wrap items-center gap-0 overflow-x-auto overflow-y-hidden pb-2">
{currentRule.parts.length === 0 ? ( {currentRule.parts.length === 0 ? (
<div className="flex h-24 min-w-[200px] items-center justify-center rounded-xl border-2 border-dashed border-border bg-muted/30 text-xs text-muted-foreground"> <div className="flex h-24 min-w-[200px] items-center justify-center rounded-xl border-2 border-dashed border-border bg-muted/30 text-xs text-muted-foreground">
@ -376,7 +375,7 @@ export const NumberingRuleDesigner: React.FC<NumberingRuleDesignerProps> = ({
<button <button
type="button" type="button"
className={cn( className={cn(
"pipe-segment min-w-[120px] rounded-[10px] border-2 px-3 py-3 text-left transition-all", "pipe-segment min-w-[120px] flex-shrink-0 rounded-[10px] border-2 px-4 py-3 text-center transition-all",
part.partType === "date" && "border-warning", part.partType === "date" && "border-warning",
part.partType === "text" && "border-primary", part.partType === "text" && "border-primary",
part.partType === "sequence" && "border-primary", part.partType === "sequence" && "border-primary",
@ -385,25 +384,27 @@ export const NumberingRuleDesigner: React.FC<NumberingRuleDesignerProps> = ({
)} )}
onClick={() => setSelectedPartOrder(part.order)} onClick={() => setSelectedPartOrder(part.order)}
> >
<div className="text-[10px] font-medium text-muted-foreground">{typeLabel}</div> <div className="seg-type text-[8px] font-bold uppercase tracking-wide text-muted-foreground">
<div className={cn("mt-0.5 truncate font-mono text-sm font-medium", getPartTypeColorClass(part.partType))}> {typeLabel}
</div>
<div className={cn("seg-value mt-1 truncate font-mono text-base font-extrabold leading-none", getPartTypeColorClass(part.partType))}>
{item?.displayValue ?? "-"} {item?.displayValue ?? "-"}
</div> </div>
</button> </button>
{index < currentRule.parts.length - 1 && ( {index < currentRule.parts.length - 1 && (
<span className="flex items-center gap-1 text-muted-foreground"> <div className="pipe-connector flex w-8 flex-shrink-0 flex-col items-center justify-center gap-0.5">
<span className="text-xs"></span> <span className="conn-line text-xs font-bold text-muted-foreground"></span>
<span className="rounded bg-muted px-1.5 py-0.5 text-[10px] font-mono"> <span className="conn-sep rounded border border-border bg-muted px-1 py-0.5 text-[8px] font-semibold text-muted-foreground">
{sep || "(없음)"} {sep || "-"}
</span>
</span> </span>
</div>
)} )}
</React.Fragment> </React.Fragment>
); );
})} })}
<button <button
type="button" type="button"
className="flex h-[52px] w-10 flex-shrink-0 items-center justify-center rounded-full border-2 border-dashed border-border text-muted-foreground transition-colors hover:border-primary hover:bg-primary/5 hover:text-primary" className="pipe-add flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-[10px] border-2 border-dashed border-border text-muted-foreground transition-colors hover:border-primary hover:bg-primary/5 hover:text-primary"
onClick={handleAddPart} onClick={handleAddPart}
disabled={currentRule.parts.length >= maxRules || isPreview || loading} disabled={currentRule.parts.length >= maxRules || isPreview || loading}
aria-label="규칙 추가" aria-label="규칙 추가"
@ -417,8 +418,8 @@ export const NumberingRuleDesigner: React.FC<NumberingRuleDesignerProps> = ({
{/* 설정 패널 (선택된 세그먼트 상세, code-config-panel) */} {/* 설정 패널 (선택된 세그먼트 상세, code-config-panel) */}
{selectedPart && ( {selectedPart && (
<div className="code-config-panel min-h-0 flex-1 overflow-y-auto rounded-lg bg-muted/30 p-4"> <div className="code-config-panel min-h-0 flex-1 overflow-y-auto px-6 py-5">
<div className="grid grid-cols-[repeat(auto-fill,minmax(180px,1fr))] gap-3"> <div className="code-config-grid grid grid-cols-[repeat(auto-fill,minmax(180px,1fr))] gap-3">
<NumberingRuleCard <NumberingRuleCard
part={selectedPart} part={selectedPart}
onUpdate={(updates) => handleUpdatePart(selectedPart.order, updates)} onUpdate={(updates) => handleUpdatePart(selectedPart.order, updates)}
@ -460,7 +461,7 @@ export const NumberingRuleDesigner: React.FC<NumberingRuleDesignerProps> = ({
)} )}
{/* 저장 바 (code-save-bar) */} {/* 저장 바 (code-save-bar) */}
<div className="code-save-bar flex flex-shrink-0 items-center justify-between gap-4 border-t border-border pt-4"> <div className="code-save-bar flex flex-shrink-0 items-center justify-between gap-4 border-t border-border bg-muted/30 px-6 py-4">
<div className="min-w-0 flex-1 text-xs text-muted-foreground"> <div className="min-w-0 flex-1 text-xs text-muted-foreground">
{currentRule.tableName && ( {currentRule.tableName && (
<span>: {currentRule.tableName}</span> <span>: {currentRule.tableName}</span>

View File

@ -1,6 +1,7 @@
"use client"; "use client";
import React, { useMemo } from "react"; import React, { useMemo } from "react";
import { cn } from "@/lib/utils";
import { NumberingRuleConfig, NumberingRulePart, CodePartType } from "@/types/numbering-rule"; import { NumberingRuleConfig, NumberingRulePart, CodePartType } from "@/types/numbering-rule";
import { CODE_PART_TYPE_OPTIONS } from "@/types/numbering-rule"; import { CODE_PART_TYPE_OPTIONS } from "@/types/numbering-rule";
@ -127,8 +128,8 @@ export const NumberingRulePreview: React.FC<NumberingRulePreviewProps> = ({
if (variant === "strip") { if (variant === "strip") {
const globalSep = config.separator ?? "-"; const globalSep = config.separator ?? "-";
return ( return (
<div className="rounded-lg bg-gradient-to-b from-muted to-card px-4 py-4"> <div className="rounded-lg bg-gradient-to-b from-muted to-card px-4 py-4 sm:px-6 sm:py-5">
<div className="font-mono text-[28px] font-extrabold tracking-tight"> <div className="font-mono text-[22px] font-extrabold tracking-tight sm:text-[28px]">
{partItems.length === 0 ? ( {partItems.length === 0 ? (
<span className="text-muted-foreground"> </span> <span className="text-muted-foreground"> </span>
) : ( ) : (
@ -145,10 +146,10 @@ export const NumberingRulePreview: React.FC<NumberingRulePreviewProps> = ({
)} )}
</div> </div>
{partItems.length > 0 && ( {partItems.length > 0 && (
<div className="mt-3 flex flex-wrap gap-x-4 gap-y-1 text-xs text-muted-foreground"> <div className="preview-desc mt-3 flex flex-wrap gap-x-4 gap-y-1 text-xs text-muted-foreground">
{CODE_PART_TYPE_OPTIONS.filter((opt) => partItems.some((p) => p.partType === opt.value)).map((opt) => ( {CODE_PART_TYPE_OPTIONS.filter((opt) => partItems.some((p) => p.partType === opt.value)).map((opt) => (
<span key={opt.value} className="flex items-center gap-1.5"> <span key={opt.value} className="flex items-center gap-1.5">
<span className={`h-1.5 w-1.5 rounded-full ${getPartTypeDotClass(opt.value)}`} /> <span className={cn("h-[6px] w-[6px] shrink-0 rounded-full", getPartTypeDotClass(opt.value))} />
{opt.label} {opt.label}
</span> </span>
))} ))}

View File

@ -120,7 +120,7 @@ export const CategoryValueAddDialog: React.FC<
<div className="space-y-3 sm:space-y-4"> <div className="space-y-3 sm:space-y-4">
<div> <div>
<Label htmlFor="valueLabel" className="text-xs sm:text-sm"> <Label htmlFor="valueLabel" className="text-xs sm:text-sm">
<span className="text-destructive">*</span>
</Label> </Label>
<Input <Input
id="valueLabel" id="valueLabel"

View File

@ -86,7 +86,7 @@ export const CategoryValueEditDialog: React.FC<
<div className="space-y-3 sm:space-y-4"> <div className="space-y-3 sm:space-y-4">
<div> <div>
<Label htmlFor="valueLabel" className="text-xs sm:text-sm"> <Label htmlFor="valueLabel" className="text-xs sm:text-sm">
<span className="text-destructive">*</span>
</Label> </Label>
<Input <Input
id="valueLabel" id="valueLabel"

View File

@ -405,7 +405,6 @@ export const CategoryValueManager: React.FC<CategoryValueManagerProps> = ({
value.isActive !== false value.isActive !== false
) )
} }
className="data-[state=checked]:bg-emerald-500"
/> />
<Button <Button

View File

@ -19,6 +19,7 @@ import {
Search, Search,
RefreshCw, RefreshCw,
} from "lucide-react"; } from "lucide-react";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { Checkbox } from "@/components/ui/checkbox"; import { Checkbox } from "@/components/ui/checkbox";
@ -144,7 +145,7 @@ const TreeNode: React.FC<TreeNodeProps> = ({
<div className="mb-px"> <div className="mb-px">
<div <div
className={cn( className={cn(
"group flex cursor-pointer items-center gap-[5px] rounded-[6px] px-2 py-[5px] transition-colors", "group flex cursor-pointer items-center gap-[5px] rounded-[6px] px-[8px] py-[5px] transition-colors",
isSelected ? "border-primary border-l-2 bg-primary/10" : "hover:bg-muted/50", isSelected ? "border-primary border-l-2 bg-primary/10" : "hover:bg-muted/50",
isChecked && "bg-primary/5", isChecked && "bg-primary/5",
)} )}
@ -633,10 +634,13 @@ export const CategoryValueManagerTree: React.FC<CategoryValueManagerTreeProps> =
return ( return (
<div className="flex h-full flex-col"> <div className="flex h-full flex-col">
{/* 헤더 */} {/* 편집기 헤더: 컬럼명 + 값 수 Badge + 선택 Badge + 액션 버튼 */}
<div className="mb-3 flex items-center justify-between border-b pb-3"> <div className="mb-3 flex items-center justify-between border-b pb-3">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<h3 className="text-base font-semibold">{columnLabel} </h3> <h3 className="text-base font-semibold">{columnLabel} </h3>
<Badge variant="secondary" className="rounded-full px-2 py-0.5 text-xs font-bold">
{countAllValues(tree)}
</Badge>
{checkedIds.size > 0 && ( {checkedIds.size > 0 && (
<span className="bg-primary/10 text-primary rounded-full px-2 py-0.5 text-xs"> <span className="bg-primary/10 text-primary rounded-full px-2 py-0.5 text-xs">
{checkedIds.size} {checkedIds.size}
@ -719,7 +723,7 @@ export const CategoryValueManagerTree: React.FC<CategoryValueManagerTreeProps> =
<p className="text-muted-foreground mt-1 text-xs"> </p> <p className="text-muted-foreground mt-1 text-xs"> </p>
</div> </div>
) : ( ) : (
<div className="p-2"> <div className="py-1">
{tree.map((node) => ( {tree.map((node) => (
<TreeNode <TreeNode
key={node.valueId} key={node.valueId}

View File

@ -162,7 +162,7 @@ export function V2CategoryManagerComponent({
className="flex h-full flex-col overflow-hidden rounded-lg border bg-card text-card-foreground shadow-sm" className="flex h-full flex-col overflow-hidden rounded-lg border bg-card text-card-foreground shadow-sm"
style={{ height: config.height }} style={{ height: config.height }}
> >
{/* Stat Strip */} {/* Stat Strip: 카테고리 컬럼(primary) | 전체 값(success) | 테이블(primary) | 비활성(warning) */}
<div className="grid grid-cols-4 border-b bg-background"> <div className="grid grid-cols-4 border-b bg-background">
<div className="border-r py-3.5 text-center last:border-r-0"> <div className="border-r py-3.5 text-center last:border-r-0">
<div className="text-[22px] font-extrabold leading-none tracking-tight text-primary"> <div className="text-[22px] font-extrabold leading-none tracking-tight text-primary">
@ -173,7 +173,7 @@ export function V2CategoryManagerComponent({
</div> </div>
</div> </div>
<div className="border-r py-3.5 text-center last:border-r-0"> <div className="border-r py-3.5 text-center last:border-r-0">
<div className="text-[22px] font-extrabold leading-none tracking-tight text-primary"> <div className="text-[22px] font-extrabold leading-none tracking-tight text-success">
{stats.totalValues} {stats.totalValues}
</div> </div>
<div className="mt-1 text-[9px] font-semibold uppercase tracking-widest text-muted-foreground"> <div className="mt-1 text-[9px] font-semibold uppercase tracking-widest text-muted-foreground">
@ -189,7 +189,7 @@ export function V2CategoryManagerComponent({
</div> </div>
</div> </div>
<div className="py-3.5 text-center"> <div className="py-3.5 text-center">
<div className="text-[22px] font-extrabold leading-none tracking-tight text-primary"> <div className="text-[22px] font-extrabold leading-none tracking-tight text-warning">
{stats.inactiveCount} {stats.inactiveCount}
</div> </div>
<div className="mt-1 text-[9px] font-semibold uppercase tracking-widest text-muted-foreground"> <div className="mt-1 text-[9px] font-semibold uppercase tracking-widest text-muted-foreground">
@ -227,7 +227,7 @@ export function V2CategoryManagerComponent({
handleColumnSelect(uniqueKey, col.columnLabel || col.columnName, col.tableName) handleColumnSelect(uniqueKey, col.columnLabel || col.columnName, col.tableName)
} }
className={cn( className={cn(
"inline-flex items-center gap-1.5 rounded-full border px-2.5 py-1.5 text-[11px] font-semibold transition-colors", "inline-flex items-center gap-1.5 rounded-full border px-[10px] py-[5px] text-[11px] font-semibold transition-colors",
isActive isActive
? "border-primary bg-primary/5 text-primary" ? "border-primary bg-primary/5 text-primary"
: "border-border bg-muted/50 hover:border-primary hover:bg-primary/5 hover:text-primary", : "border-border bg-muted/50 hover:border-primary hover:bg-primary/5 hover:text-primary",

View File

@ -839,7 +839,7 @@ export const SplitPanelLayoutComponent: React.FC<SplitPanelLayoutComponentProps>
height: "100%", height: "100%",
minHeight: getHeightValue(), minHeight: getHeightValue(),
cursor: "pointer", cursor: "pointer",
border: isSelected ? "2px solid #3b82f6" : "1px solid #e5e7eb", border: isSelected ? "2px solid hsl(var(--primary))" : "1px solid hsl(var(--border))",
} }
: { : {
position: "relative", position: "relative",
@ -1040,12 +1040,12 @@ export const SplitPanelLayoutComponent: React.FC<SplitPanelLayoutComponentProps>
const barWidth = Math.min(percentage, 100); const barWidth = Math.min(percentage, 100);
const barColor = const barColor =
percentage > 100 percentage > 100
? "bg-red-600" ? "bg-destructive"
: percentage >= 90 : percentage >= 90
? "bg-red-500" ? "bg-destructive"
: percentage >= 70 : percentage >= 70
? "bg-amber-500" ? "bg-warning"
: "bg-emerald-500"; : "bg-success";
return ( return (
<div className="flex min-w-[120px] items-center gap-2"> <div className="flex min-w-[120px] items-center gap-2">

View File

@ -367,8 +367,9 @@ export const SingleTableWithSticky: React.FC<SingleTableWithStickyProps> = ({
const hasCategoryOptions = const hasCategoryOptions =
isCategoryType && categoryOptions && Object.keys(categoryOptions).length > 0; isCategoryType && categoryOptions && Object.keys(categoryOptions).length > 0;
// 인라인 편집: 행 높이 유지를 위해 select/input 모두 h-8(32px) 고정
const commonInputClass = const commonInputClass =
"border-primary bg-background focus:ring-primary h-8 w-full rounded border px-2 text-xs focus:ring-2 focus:outline-none sm:text-sm"; "border-primary bg-background focus:ring-primary h-8 w-full shrink-0 rounded border px-2 text-xs focus:ring-2 focus:outline-none sm:text-sm";
const handleBlurSave = () => { const handleBlurSave = () => {
if (onEditKeyDown) { if (onEditKeyDown) {
const fakeEvent = { const fakeEvent = {

View File

@ -6419,7 +6419,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
onChange={(e) => setEditingValue(e.target.value)} onChange={(e) => setEditingValue(e.target.value)}
onKeyDown={handleEditKeyDown} onKeyDown={handleEditKeyDown}
onBlur={saveEditing} onBlur={saveEditing}
className="border-primary bg-background h-8 w-full border-2 px-2 py-1 text-xs focus:outline-none sm:px-4 sm:py-1.5 sm:text-sm" className="border-primary bg-background h-8 w-full shrink-0 border-2 px-2 py-1 text-xs focus:outline-none sm:px-4 sm:py-1.5 sm:text-sm"
autoFocus autoFocus
> >
<option value=""></option> <option value=""></option>
@ -6447,7 +6447,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
); );
} }
// 일반 입력 필드 // 일반 입력 필드 (행 높이 유지: h-8 고정)
return ( return (
<input <input
ref={editInputRef} ref={editInputRef}
@ -6456,7 +6456,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
onChange={(e) => setEditingValue(e.target.value)} onChange={(e) => setEditingValue(e.target.value)}
onKeyDown={handleEditKeyDown} onKeyDown={handleEditKeyDown}
onBlur={saveEditing} onBlur={saveEditing}
className="border-primary bg-background h-8 w-full border-2 px-2 py-1 text-xs focus:outline-none sm:px-4 sm:py-1.5 sm:text-sm" className="border-primary bg-background h-8 w-full shrink-0 border-2 px-2 py-1 text-xs focus:outline-none sm:px-4 sm:py-1.5 sm:text-sm"
style={{ style={{
textAlign: isNumeric ? "right" : column.align || "left", textAlign: isNumeric ? "right" : column.align || "left",
}} }}