refactor: 전체 프론트엔드 하드코딩 색상 → CSS 변수 일괄 치환

447+ 파일, 4500+ 줄 변경:
- gray-* → border/bg-muted/text-foreground/text-muted-foreground
- blue-* → primary/ring
- red-* → destructive
- green-* → emerald (일관성)
- indigo-* → primary
- yellow/orange → amber (통일)
- dark mode 변형도 시맨틱 토큰으로 변환

Made-with: Cursor
This commit is contained in:
DDD1542
2026-03-09 14:31:59 +09:00
parent d967cf0a0d
commit 4f10b5e42d
447 changed files with 4520 additions and 4520 deletions

View File

@@ -537,28 +537,28 @@ const SelectBasicComponent: React.FC<SelectBasicComponentProps> = ({
onFocus={() => setIsOpen(true)}
placeholder="코드 또는 코드명 입력..."
className={cn(
"h-10 w-full rounded-lg border border-gray-300 bg-white px-3 py-2 transition-all outline-none",
"h-10 w-full rounded-lg border border-input bg-white px-3 py-2 transition-all outline-none",
!isFieldDisabled && "hover:border-orange-400 focus:border-orange-500 focus:ring-2 focus:ring-orange-200",
isSelected && "ring-2 ring-orange-500",
isFieldDisabled && "cursor-not-allowed !bg-gray-100 opacity-60",
isFieldDisabled && "cursor-not-allowed !bg-muted opacity-60",
)}
readOnly={isFieldDisabled}
disabled={isFieldDisabled}
/>
{isOpen && !isFieldDisabled && filteredOptions.length > 0 && (
<div className="absolute z-[99999] mt-1 max-h-60 w-full overflow-auto rounded-md border border-gray-300 bg-white shadow-lg">
<div className="absolute z-[99999] mt-1 max-h-60 w-full overflow-auto rounded-md border border-input bg-white shadow-lg">
{filteredOptions.map((option, index) => (
<div
key={`${option.value}-${index}`}
className="cursor-pointer bg-white px-3 py-2 hover:bg-gray-100"
className="cursor-pointer bg-white px-3 py-2 hover:bg-muted"
onClick={() => {
setSearchQuery("");
handleOptionSelect(option.value, option.label);
}}
>
<div className="flex items-center justify-between">
<span className="text-sm font-medium text-gray-900">{option.label}</span>
<span className="text-xs text-gray-500">{option.value}</span>
<span className="text-sm font-medium text-foreground">{option.label}</span>
<span className="text-xs text-muted-foreground">{option.value}</span>
</div>
</div>
))}
@@ -574,16 +574,16 @@ const SelectBasicComponent: React.FC<SelectBasicComponentProps> = ({
<div className="w-full">
<div
className={cn(
"flex h-10 w-full items-center justify-between rounded-lg border border-gray-300 bg-white px-3 py-2",
"flex h-10 w-full items-center justify-between rounded-lg border border-input bg-white px-3 py-2",
!isFieldDisabled && "cursor-pointer hover:border-orange-400",
isSelected && "ring-2 ring-orange-500",
isOpen && "border-orange-500",
isFieldDisabled && "cursor-not-allowed !bg-gray-100 opacity-60",
isFieldDisabled && "cursor-not-allowed !bg-muted opacity-60",
)}
onClick={handleToggle}
style={{ pointerEvents: isFieldDisabled ? "none" : "auto" }}
>
<span className={selectedLabel ? "text-gray-900" : "text-gray-500"}>{selectedLabel || placeholder}</span>
<span className={selectedLabel ? "text-foreground" : "text-muted-foreground"}>{selectedLabel || placeholder}</span>
<svg
className={`h-4 w-4 transition-transform ${isOpen ? "rotate-180" : ""}`}
fill="none"
@@ -594,21 +594,21 @@ const SelectBasicComponent: React.FC<SelectBasicComponentProps> = ({
</svg>
</div>
{isOpen && !isFieldDisabled && (
<div className="absolute z-[99999] mt-1 max-h-60 w-full overflow-auto rounded-md border border-gray-300 bg-white shadow-lg">
<div className="absolute z-[99999] mt-1 max-h-60 w-full overflow-auto rounded-md border border-input bg-white shadow-lg">
{isLoadingCodes ? (
<div className="bg-white px-3 py-2 text-gray-900"> ...</div>
<div className="bg-white px-3 py-2 text-foreground"> ...</div>
) : allOptions.length > 0 ? (
allOptions.map((option, index) => (
<div
key={`${option.value}-${index}`}
className="cursor-pointer bg-white px-3 py-2 text-gray-900 hover:bg-gray-100"
className="cursor-pointer bg-white px-3 py-2 text-foreground hover:bg-muted"
onClick={() => handleOptionSelect(option.value, option.label)}
>
{option.label}
</div>
))
) : (
<div className="bg-white px-3 py-2 text-gray-900"> </div>
<div className="bg-white px-3 py-2 text-foreground"> </div>
)}
</div>
)}
@@ -622,16 +622,16 @@ const SelectBasicComponent: React.FC<SelectBasicComponentProps> = ({
<div className="w-full">
<div
className={cn(
"box-border flex h-full w-full flex-wrap gap-2 rounded-lg border border-gray-300 bg-white px-3 py-2",
"box-border flex h-full w-full flex-wrap gap-2 rounded-lg border border-input bg-white px-3 py-2",
!isFieldDisabled && "hover:border-orange-400",
isSelected && "ring-2 ring-orange-500",
isFieldDisabled && "cursor-not-allowed !bg-gray-100 opacity-60",
isFieldDisabled && "cursor-not-allowed !bg-muted opacity-60",
)}
>
{selectedValues.map((val, idx) => {
const opt = allOptions.find((o) => o.value === val);
return (
<span key={idx} className="flex items-center gap-1 rounded bg-blue-100 px-2 py-1 text-sm text-blue-800">
<span key={idx} className="flex items-center gap-1 rounded bg-primary/10 px-2 py-1 text-sm text-primary">
{opt?.label || val}
<button
type="button"
@@ -642,7 +642,7 @@ const SelectBasicComponent: React.FC<SelectBasicComponentProps> = ({
onFormDataChange(component.columnName, newVals.join(","));
}
}}
className="ml-1 text-blue-600 hover:text-blue-800"
className="ml-1 text-primary hover:text-primary"
>
×
</button>
@@ -683,20 +683,20 @@ const SelectBasicComponent: React.FC<SelectBasicComponentProps> = ({
onFocus={() => !isFieldDisabled && setIsOpen(true)}
placeholder={placeholder}
className={cn(
"h-10 w-full rounded-lg border border-gray-300 bg-white px-3 py-2 transition-all outline-none",
"h-10 w-full rounded-lg border border-input bg-white px-3 py-2 transition-all outline-none",
!isFieldDisabled && "hover:border-orange-400 focus:border-orange-500 focus:ring-2 focus:ring-orange-200",
isSelected && "ring-2 ring-orange-500",
isFieldDisabled && "cursor-not-allowed !bg-gray-100 opacity-60",
isFieldDisabled && "cursor-not-allowed !bg-muted opacity-60",
)}
readOnly={isFieldDisabled}
disabled={isFieldDisabled}
/>
{isOpen && !isFieldDisabled && filteredOptions.length > 0 && (
<div className="absolute z-[99999] mt-1 max-h-60 w-full overflow-auto rounded-md border border-gray-300 bg-white shadow-lg">
<div className="absolute z-[99999] mt-1 max-h-60 w-full overflow-auto rounded-md border border-input bg-white shadow-lg">
{filteredOptions.map((option, index) => (
<div
key={`${option.value}-${index}`}
className="cursor-pointer bg-white px-3 py-2 text-gray-900 hover:bg-gray-100"
className="cursor-pointer bg-white px-3 py-2 text-foreground hover:bg-muted"
onClick={() => {
setSearchQuery(option.label);
setSelectedValue(option.value);
@@ -722,16 +722,16 @@ const SelectBasicComponent: React.FC<SelectBasicComponentProps> = ({
<div className="w-full">
<div
className={cn(
"flex h-10 w-full items-center justify-between rounded-lg border border-gray-300 bg-white px-3 py-2",
"flex h-10 w-full items-center justify-between rounded-lg border border-input bg-white px-3 py-2",
!isFieldDisabled && "cursor-pointer hover:border-orange-400",
isSelected && "ring-2 ring-orange-500",
isOpen && "border-orange-500",
isFieldDisabled && "cursor-not-allowed !bg-gray-100 opacity-60",
isFieldDisabled && "cursor-not-allowed !bg-muted opacity-60",
)}
onClick={handleToggle}
style={{ pointerEvents: isFieldDisabled ? "none" : "auto" }}
>
<span className={selectedLabel ? "text-gray-900" : "text-gray-500"}>{selectedLabel || placeholder}</span>
<span className={selectedLabel ? "text-foreground" : "text-muted-foreground"}>{selectedLabel || placeholder}</span>
<svg
className={`h-4 w-4 transition-transform ${isOpen ? "rotate-180" : ""}`}
fill="none"
@@ -742,13 +742,13 @@ const SelectBasicComponent: React.FC<SelectBasicComponentProps> = ({
</svg>
</div>
{isOpen && !isFieldDisabled && (
<div className="absolute z-[99999] mt-1 w-full rounded-md border border-gray-300 bg-white shadow-lg">
<div className="absolute z-[99999] mt-1 w-full rounded-md border border-input bg-white shadow-lg">
<input
type="text"
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
placeholder="검색..."
className="w-full border-b border-gray-300 px-3 py-2 outline-none"
className="w-full border-b border-input px-3 py-2 outline-none"
/>
<div className="max-h-60 overflow-auto">
{allOptions
@@ -760,7 +760,7 @@ const SelectBasicComponent: React.FC<SelectBasicComponentProps> = ({
.map((option, index) => (
<div
key={`${option.value}-${index}`}
className="cursor-pointer bg-white px-3 py-2 text-gray-900 hover:bg-gray-100"
className="cursor-pointer bg-white px-3 py-2 text-foreground hover:bg-muted"
onClick={() => handleOptionSelect(option.value, option.label)}
>
{option.label || option.value}
@@ -780,10 +780,10 @@ const SelectBasicComponent: React.FC<SelectBasicComponentProps> = ({
<div className="w-full" style={{ height: "100%" }}>
<div
className={cn(
"box-border flex w-full flex-wrap items-center gap-2 rounded-lg border border-gray-300 bg-white px-3 py-2",
"box-border flex w-full flex-wrap items-center gap-2 rounded-lg border border-input bg-white px-3 py-2",
!isFieldDisabled && "hover:border-orange-400",
isSelected && "ring-2 ring-orange-500",
isFieldDisabled && "cursor-not-allowed !bg-gray-100 opacity-60",
isFieldDisabled && "cursor-not-allowed !bg-muted opacity-60",
)}
onClick={() => !isFieldDisabled && setIsOpen(true)}
style={{
@@ -794,7 +794,7 @@ const SelectBasicComponent: React.FC<SelectBasicComponentProps> = ({
{selectedValues.map((val, idx) => {
const opt = allOptions.find((o) => o.value === val);
return (
<span key={idx} className="flex items-center gap-1 rounded bg-blue-100 px-2 py-1 text-sm text-blue-800">
<span key={idx} className="flex items-center gap-1 rounded bg-primary/10 px-2 py-1 text-sm text-primary">
{opt?.label || val}
<button
type="button"
@@ -807,19 +807,19 @@ const SelectBasicComponent: React.FC<SelectBasicComponentProps> = ({
onFormDataChange(component.columnName, newValue);
}
}}
className="ml-1 text-blue-600 hover:text-blue-800"
className="ml-1 text-primary hover:text-primary"
>
×
</button>
</span>
);
})}
{selectedValues.length === 0 && <span className="text-gray-500">{placeholder}</span>}
{selectedValues.length === 0 && <span className="text-muted-foreground">{placeholder}</span>}
</div>
{isOpen && !isFieldDisabled && (
<div className="absolute z-[99999] mt-1 max-h-60 w-full overflow-auto rounded-md border border-gray-300 bg-white shadow-lg">
<div className="absolute z-[99999] mt-1 max-h-60 w-full overflow-auto rounded-md border border-input bg-white shadow-lg">
{isLoadingCodes || isLoadingCategories ? (
<div className="bg-white px-3 py-2 text-gray-900"> ...</div>
<div className="bg-white px-3 py-2 text-foreground"> ...</div>
) : allOptions.length > 0 ? (
(() => {
// 부모별 그룹핑 (카테고리 연쇄관계인 경우)
@@ -840,7 +840,7 @@ const SelectBasicComponent: React.FC<SelectBasicComponentProps> = ({
return Object.entries(groupedOptions).map(([parentKey, group]) => (
<div key={parentKey}>
{/* 그룹 헤더 */}
<div className="sticky top-0 border-b bg-gray-100 px-3 py-1.5 text-xs font-semibold text-gray-600">
<div className="sticky top-0 border-b bg-muted px-3 py-1.5 text-xs font-semibold text-muted-foreground">
{group.parentLabel}
</div>
{/* 그룹 옵션들 */}
@@ -850,8 +850,8 @@ const SelectBasicComponent: React.FC<SelectBasicComponentProps> = ({
<div
key={`${option.value}-${index}`}
className={cn(
"cursor-pointer px-3 py-2 text-gray-900 hover:bg-gray-100",
isOptionSelected && "bg-blue-50 font-medium",
"cursor-pointer px-3 py-2 text-foreground hover:bg-muted",
isOptionSelected && "bg-primary/10 font-medium",
)}
onClick={() => {
const newVals = isOptionSelected
@@ -898,8 +898,8 @@ const SelectBasicComponent: React.FC<SelectBasicComponentProps> = ({
<div
key={`${option.value}-${index}`}
className={cn(
"cursor-pointer px-3 py-2 text-gray-900 hover:bg-gray-100",
isOptionSelected && "bg-blue-50 font-medium",
"cursor-pointer px-3 py-2 text-foreground hover:bg-muted",
isOptionSelected && "bg-primary/10 font-medium",
)}
onClick={() => {
const newVals = isOptionSelected
@@ -937,7 +937,7 @@ const SelectBasicComponent: React.FC<SelectBasicComponentProps> = ({
});
})()
) : (
<div className="bg-white px-3 py-2 text-gray-900"> </div>
<div className="bg-white px-3 py-2 text-foreground"> </div>
)}
</div>
)}
@@ -950,16 +950,16 @@ const SelectBasicComponent: React.FC<SelectBasicComponentProps> = ({
<div className="w-full">
<div
className={cn(
"flex h-10 w-full items-center justify-between rounded-lg border border-gray-300 bg-white px-3 py-2",
"flex h-10 w-full items-center justify-between rounded-lg border border-input bg-white px-3 py-2",
!isFieldDisabled && "cursor-pointer hover:border-orange-400",
isSelected && "ring-2 ring-orange-500",
isOpen && "border-orange-500",
isFieldDisabled && "cursor-not-allowed !bg-gray-100 opacity-60",
isFieldDisabled && "cursor-not-allowed !bg-muted opacity-60",
)}
onClick={handleToggle}
style={{ pointerEvents: isFieldDisabled ? "none" : "auto" }}
>
<span className={selectedLabel ? "text-gray-900" : "text-gray-500"}>{selectedLabel || placeholder}</span>
<span className={selectedLabel ? "text-foreground" : "text-muted-foreground"}>{selectedLabel || placeholder}</span>
<svg
className={`h-4 w-4 transition-transform ${isOpen ? "rotate-180" : ""}`}
fill="none"
@@ -970,21 +970,21 @@ const SelectBasicComponent: React.FC<SelectBasicComponentProps> = ({
</svg>
</div>
{isOpen && !isFieldDisabled && (
<div className="absolute z-[99999] mt-1 max-h-60 w-full overflow-auto rounded-md border border-gray-300 bg-white shadow-lg">
<div className="absolute z-[99999] mt-1 max-h-60 w-full overflow-auto rounded-md border border-input bg-white shadow-lg">
{isLoadingCodes ? (
<div className="bg-white px-3 py-2 text-gray-900"> ...</div>
<div className="bg-white px-3 py-2 text-foreground"> ...</div>
) : allOptions.length > 0 ? (
allOptions.map((option, index) => (
<div
key={`${option.value}-${index}`}
className="cursor-pointer bg-white px-3 py-2 text-gray-900 hover:bg-gray-100"
className="cursor-pointer bg-white px-3 py-2 text-foreground hover:bg-muted"
onClick={() => handleOptionSelect(option.value, option.label)}
>
{option.label || option.value}
</div>
))
) : (
<div className="bg-white px-3 py-2 text-gray-900"> </div>
<div className="bg-white px-3 py-2 text-foreground"> </div>
)}
</div>
)}
@@ -1006,7 +1006,7 @@ const SelectBasicComponent: React.FC<SelectBasicComponentProps> = ({
{component.label && (component.style?.labelDisplay ?? true) && (
<label className="absolute -top-6 left-0 text-sm font-medium text-slate-600">
{component.label}
{component.required && <span className="text-red-500">*</span>}
{component.required && <span className="text-destructive">*</span>}
</label>
)}