Resolve merge conflicts in v2-rack-structure
Made-with: Cursor
This commit is contained in:
@@ -20,6 +20,7 @@ import {
|
||||
GeneratedLocation,
|
||||
RackStructureContext,
|
||||
} from "./types";
|
||||
import { applyLocationPattern, DEFAULT_CODE_PATTERN, DEFAULT_NAME_PATTERN } from "./patternUtils";
|
||||
|
||||
// 기존 위치 데이터 타입
|
||||
interface ExistingLocation {
|
||||
@@ -512,23 +513,27 @@ export const RackStructureComponent: React.FC<RackStructureComponentProps> = ({
|
||||
return { totalLocations, totalRows, maxLevel };
|
||||
}, [conditions]);
|
||||
|
||||
// 위치 코드 생성
|
||||
// 위치 코드 생성 (패턴 기반)
|
||||
const generateLocationCode = useCallback(
|
||||
(row: number, level: number): { code: string; name: string } => {
|
||||
const warehouseCode = context?.warehouseCode || "WH001";
|
||||
const floor = context?.floor || "1";
|
||||
const zone = context?.zone || "A";
|
||||
const vars = {
|
||||
warehouse: context?.warehouseCode || "WH001",
|
||||
warehouseName: context?.warehouseName || "",
|
||||
floor: context?.floor || "1",
|
||||
zone: context?.zone || "A",
|
||||
row,
|
||||
level,
|
||||
};
|
||||
|
||||
// 코드 생성 (예: WH001-1층D구역-01-1)
|
||||
const code = `${warehouseCode}-${floor}${zone}-${row.toString().padStart(2, "0")}-${level}`;
|
||||
const codePattern = config.codePattern || DEFAULT_CODE_PATTERN;
|
||||
const namePattern = config.namePattern || DEFAULT_NAME_PATTERN;
|
||||
|
||||
// 이름 생성 - zone에 이미 "구역"이 포함되어 있으면 그대로 사용
|
||||
const zoneName = zone.includes("구역") ? zone : `${zone}구역`;
|
||||
const name = `${zoneName}-${row.toString().padStart(2, "0")}열-${level}단`;
|
||||
|
||||
return { code, name };
|
||||
return {
|
||||
code: applyLocationPattern(codePattern, vars),
|
||||
name: applyLocationPattern(namePattern, vars),
|
||||
};
|
||||
},
|
||||
[context],
|
||||
[context, config.codePattern, config.namePattern],
|
||||
);
|
||||
|
||||
// 미리보기 생성
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import React, { useState, useEffect } from "react";
|
||||
import React, { useState, useEffect, useMemo } from "react";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Switch } from "@/components/ui/switch";
|
||||
@@ -12,6 +12,47 @@ import {
|
||||
SelectValue,
|
||||
} from "@/components/ui/select";
|
||||
import { RackStructureComponentConfig, FieldMapping } from "./types";
|
||||
import { applyLocationPattern, DEFAULT_CODE_PATTERN, DEFAULT_NAME_PATTERN, PATTERN_VARIABLES } from "./patternUtils";
|
||||
|
||||
// 패턴 미리보기 서브 컴포넌트
|
||||
const PatternPreview: React.FC<{
|
||||
codePattern?: string;
|
||||
namePattern?: string;
|
||||
}> = ({ codePattern, namePattern }) => {
|
||||
const sampleVars = {
|
||||
warehouse: "WH002",
|
||||
warehouseName: "2창고",
|
||||
floor: "2층",
|
||||
zone: "A구역",
|
||||
row: 1,
|
||||
level: 3,
|
||||
};
|
||||
|
||||
const previewCode = useMemo(
|
||||
() => applyLocationPattern(codePattern || DEFAULT_CODE_PATTERN, sampleVars),
|
||||
[codePattern],
|
||||
);
|
||||
const previewName = useMemo(
|
||||
() => applyLocationPattern(namePattern || DEFAULT_NAME_PATTERN, sampleVars),
|
||||
[namePattern],
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="rounded-md border border-primary/20 bg-primary/5 p-2.5">
|
||||
<div className="mb-1.5 text-[10px] font-medium text-primary">미리보기 (2창고 / 2층 / A구역 / 1열 / 3단)</div>
|
||||
<div className="space-y-1">
|
||||
<div className="flex items-center gap-2 text-xs">
|
||||
<span className="w-14 shrink-0 text-muted-foreground">위치코드:</span>
|
||||
<code className="rounded bg-background px-1.5 py-0.5 font-mono text-foreground">{previewCode}</code>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 text-xs">
|
||||
<span className="w-14 shrink-0 text-muted-foreground">위치명:</span>
|
||||
<code className="rounded bg-background px-1.5 py-0.5 font-mono text-foreground">{previewName}</code>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
interface RackStructureConfigPanelProps {
|
||||
config: RackStructureComponentConfig;
|
||||
@@ -205,6 +246,61 @@ export const RackStructureConfigPanel: React.FC<RackStructureConfigPanelProps> =
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 위치코드 패턴 설정 */}
|
||||
<div className="space-y-3 border-t pt-3">
|
||||
<div className="text-sm font-medium text-foreground">위치코드/위치명 패턴</div>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
변수를 조합하여 위치코드와 위치명 생성 규칙을 설정하세요
|
||||
</p>
|
||||
|
||||
{/* 위치코드 패턴 */}
|
||||
<div>
|
||||
<Label className="text-xs">위치코드 패턴</Label>
|
||||
<Input
|
||||
value={config.codePattern || ""}
|
||||
onChange={(e) => handleChange("codePattern", e.target.value || undefined)}
|
||||
placeholder="{warehouse}-{floor}{zone}-{row:02}-{level}"
|
||||
className="h-8 font-mono text-xs"
|
||||
/>
|
||||
<p className="mt-1 text-[10px] text-muted-foreground">
|
||||
비워두면 기본값: {"{warehouse}-{floor}{zone}-{row:02}-{level}"}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* 위치명 패턴 */}
|
||||
<div>
|
||||
<Label className="text-xs">위치명 패턴</Label>
|
||||
<Input
|
||||
value={config.namePattern || ""}
|
||||
onChange={(e) => handleChange("namePattern", e.target.value || undefined)}
|
||||
placeholder="{zone}-{row:02}열-{level}단"
|
||||
className="h-8 font-mono text-xs"
|
||||
/>
|
||||
<p className="mt-1 text-[10px] text-muted-foreground">
|
||||
비워두면 기본값: {"{zone}-{row:02}열-{level}단"}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* 실시간 미리보기 */}
|
||||
<PatternPreview
|
||||
codePattern={config.codePattern}
|
||||
namePattern={config.namePattern}
|
||||
/>
|
||||
|
||||
{/* 사용 가능한 변수 목록 */}
|
||||
<div className="rounded-md border bg-muted/50 p-2">
|
||||
<div className="mb-1 text-[10px] font-medium text-foreground">사용 가능한 변수</div>
|
||||
<div className="grid grid-cols-2 gap-x-3 gap-y-0.5">
|
||||
{PATTERN_VARIABLES.map((v) => (
|
||||
<div key={v.token} className="flex items-center gap-1 text-[10px]">
|
||||
<code className="rounded bg-primary/10 px-1 font-mono text-primary">{v.token}</code>
|
||||
<span className="text-muted-foreground">{v.description}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 제한 설정 */}
|
||||
<div className="space-y-3 border-t pt-3">
|
||||
<div className="text-sm font-medium text-foreground">제한 설정</div>
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
// rack-structure는 v2-rack-structure의 patternUtils를 재사용
|
||||
export {
|
||||
applyLocationPattern,
|
||||
DEFAULT_CODE_PATTERN,
|
||||
DEFAULT_NAME_PATTERN,
|
||||
PATTERN_VARIABLES,
|
||||
} from "../v2-rack-structure/patternUtils";
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import React, { useState, useEffect } from "react";
|
||||
import React, { useState, useEffect, useMemo } from "react";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Switch } from "@/components/ui/switch";
|
||||
@@ -222,6 +222,61 @@ export const RackStructureConfigPanel: React.FC<RackStructureConfigPanelProps> =
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 위치코드 패턴 설정 */}
|
||||
<div className="space-y-3 border-t pt-3">
|
||||
<div className="text-sm font-medium text-foreground">위치코드/위치명 패턴</div>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
변수를 조합하여 위치코드와 위치명 생성 규칙을 설정하세요
|
||||
</p>
|
||||
|
||||
{/* 위치코드 패턴 */}
|
||||
<div>
|
||||
<Label className="text-xs">위치코드 패턴</Label>
|
||||
<Input
|
||||
value={config.codePattern || ""}
|
||||
onChange={(e) => handleChange("codePattern", e.target.value || undefined)}
|
||||
placeholder="{warehouse}-{floor}{zone}-{row:02}-{level}"
|
||||
className="h-8 font-mono text-xs"
|
||||
/>
|
||||
<p className="mt-1 text-[10px] text-muted-foreground">
|
||||
비워두면 기본값: {"{warehouse}-{floor}{zone}-{row:02}-{level}"}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* 위치명 패턴 */}
|
||||
<div>
|
||||
<Label className="text-xs">위치명 패턴</Label>
|
||||
<Input
|
||||
value={config.namePattern || ""}
|
||||
onChange={(e) => handleChange("namePattern", e.target.value || undefined)}
|
||||
placeholder="{zone}-{row:02}열-{level}단"
|
||||
className="h-8 font-mono text-xs"
|
||||
/>
|
||||
<p className="mt-1 text-[10px] text-muted-foreground">
|
||||
비워두면 기본값: {"{zone}-{row:02}열-{level}단"}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* 실시간 미리보기 */}
|
||||
<PatternPreview
|
||||
codePattern={config.codePattern}
|
||||
namePattern={config.namePattern}
|
||||
/>
|
||||
|
||||
{/* 사용 가능한 변수 목록 */}
|
||||
<div className="rounded-md border bg-muted/50 p-2">
|
||||
<div className="mb-1 text-[10px] font-medium text-foreground">사용 가능한 변수</div>
|
||||
<div className="grid grid-cols-2 gap-x-3 gap-y-0.5">
|
||||
{PATTERN_VARIABLES.map((v) => (
|
||||
<div key={v.token} className="flex items-center gap-1 text-[10px]">
|
||||
<code className="rounded bg-primary/10 px-1 font-mono text-primary">{v.token}</code>
|
||||
<span className="text-muted-foreground">{v.description}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 제한 설정 */}
|
||||
<div className="space-y-3 border-t pt-3">
|
||||
<div className="text-sm font-medium text-foreground">제한 설정</div>
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
/**
|
||||
* 위치코드/위치명 패턴 변환 유틸리티
|
||||
*
|
||||
* 사용 가능한 변수:
|
||||
* {warehouse} - 창고 코드 (예: WH002)
|
||||
* {warehouseName} - 창고명 (예: 2창고)
|
||||
* {floor} - 층 (예: 2층)
|
||||
* {zone} - 구역 (예: A구역)
|
||||
* {row} - 열 번호 (예: 1)
|
||||
* {row:02} - 열 번호 2자리 (예: 01)
|
||||
* {row:03} - 열 번호 3자리 (예: 001)
|
||||
* {level} - 단 번호 (예: 1)
|
||||
* {level:02} - 단 번호 2자리 (예: 01)
|
||||
* {level:03} - 단 번호 3자리 (예: 001)
|
||||
*/
|
||||
|
||||
interface PatternVariables {
|
||||
warehouse?: string;
|
||||
warehouseName?: string;
|
||||
floor?: string;
|
||||
zone?: string;
|
||||
row: number;
|
||||
level: number;
|
||||
}
|
||||
|
||||
// 기본 패턴 (하드코딩 대체)
|
||||
export const DEFAULT_CODE_PATTERN = "{warehouse}-{floor}{zone}-{row:02}-{level}";
|
||||
export const DEFAULT_NAME_PATTERN = "{zone}-{row:02}열-{level}단";
|
||||
|
||||
/**
|
||||
* 패턴 문자열에서 변수를 치환하여 결과 문자열 반환
|
||||
*/
|
||||
export function applyLocationPattern(pattern: string, vars: PatternVariables): string {
|
||||
let result = pattern;
|
||||
|
||||
// zone에 "구역" 포함 여부에 따른 처리 없이 있는 그대로 치환
|
||||
const simpleVars: Record<string, string | undefined> = {
|
||||
warehouse: vars.warehouse,
|
||||
warehouseName: vars.warehouseName,
|
||||
floor: vars.floor,
|
||||
zone: vars.zone,
|
||||
};
|
||||
|
||||
// 단순 문자열 변수 치환
|
||||
for (const [key, value] of Object.entries(simpleVars)) {
|
||||
result = result.replace(new RegExp(`\\{${key}\\}`, "g"), value || "");
|
||||
}
|
||||
|
||||
// 숫자 변수 (row, level) - zero-pad 지원
|
||||
const numericVars: Record<string, number> = {
|
||||
row: vars.row,
|
||||
level: vars.level,
|
||||
};
|
||||
|
||||
for (const [key, value] of Object.entries(numericVars)) {
|
||||
// {row:02}, {level:03} 같은 zero-pad 패턴
|
||||
const padRegex = new RegExp(`\\{${key}:(\\d+)\\}`, "g");
|
||||
result = result.replace(padRegex, (_, padWidth) => {
|
||||
return value.toString().padStart(parseInt(padWidth), "0");
|
||||
});
|
||||
|
||||
// {row}, {level} 같은 단순 패턴
|
||||
result = result.replace(new RegExp(`\\{${key}\\}`, "g"), value.toString());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// 패턴에서 사용 가능한 변수 목록
|
||||
export const PATTERN_VARIABLES = [
|
||||
{ token: "{warehouse}", description: "창고 코드", example: "WH002" },
|
||||
{ token: "{warehouseName}", description: "창고명", example: "2창고" },
|
||||
{ token: "{floor}", description: "층", example: "2층" },
|
||||
{ token: "{zone}", description: "구역", example: "A구역" },
|
||||
{ token: "{row}", description: "열 번호", example: "1" },
|
||||
{ token: "{row:02}", description: "열 번호 (2자리)", example: "01" },
|
||||
{ token: "{row:03}", description: "열 번호 (3자리)", example: "001" },
|
||||
{ token: "{level}", description: "단 번호", example: "1" },
|
||||
{ token: "{level:02}", description: "단 번호 (2자리)", example: "01" },
|
||||
{ token: "{level:03}", description: "단 번호 (3자리)", example: "001" },
|
||||
];
|
||||
@@ -729,7 +729,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
const [categoryMappings, setCategoryMappings] = useState<
|
||||
Record<string, Record<string, { label: string; color?: string }>>
|
||||
>({});
|
||||
const [categoryMappingsKey, setCategoryMappingsKey] = useState(0); // 강제 리렌더링용
|
||||
const [categoryMappingsKey, setCategoryMappingsKey] = useState(0);
|
||||
const [searchValues, setSearchValues] = useState<Record<string, any>>({});
|
||||
const [selectedRows, setSelectedRows] = useState<Set<string>>(new Set());
|
||||
const [columnWidths, setColumnWidths] = useState<Record<string, number>>({});
|
||||
@@ -1064,9 +1064,14 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
const getColumnUniqueValues = async (columnName: string) => {
|
||||
const { apiClient } = await import("@/lib/api/client");
|
||||
|
||||
// 최고관리자가 특정 회사 프리뷰 시 해당 회사 카테고리만 필터링
|
||||
const filterParam = companyCode && companyCode !== "*"
|
||||
? `?filterCompanyCode=${encodeURIComponent(companyCode)}`
|
||||
: "";
|
||||
|
||||
// 1단계: 카테고리 API 시도 (columnMeta 무관하게 항상 시도)
|
||||
try {
|
||||
const response = await apiClient.get(`/table-categories/${tableConfig.selectedTable}/${columnName}/values`);
|
||||
const response = await apiClient.get(`/table-categories/${tableConfig.selectedTable}/${columnName}/values${filterParam}`);
|
||||
if (response.data.success && response.data.data && response.data.data.length > 0) {
|
||||
return response.data.data.map((item: any) => ({
|
||||
value: item.valueCode,
|
||||
@@ -1171,15 +1176,13 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
tableConfig.selectedTable,
|
||||
tableConfig.columns,
|
||||
columnLabels,
|
||||
columnMeta, // columnMeta가 변경되면 재등록 (inputType 정보 필요)
|
||||
categoryMappings, // 카테고리 매핑 변경 시 재등록 (필터 라벨 변환용)
|
||||
columnMeta,
|
||||
categoryMappings,
|
||||
columnWidths,
|
||||
tableLabel,
|
||||
data, // 데이터 자체가 변경되면 재등록 (고유 값 조회용)
|
||||
totalItems, // 전체 항목 수가 변경되면 재등록
|
||||
data,
|
||||
totalItems,
|
||||
registerTable,
|
||||
// unregisterTable은 의존성에서 제외 - 무한 루프 방지
|
||||
// unregisterTable 함수는 의존성이 없어 안정적임
|
||||
]);
|
||||
|
||||
// 🎯 초기 로드 시 localStorage에서 정렬 상태 불러오기 (없으면 defaultSort 적용)
|
||||
@@ -1423,7 +1426,13 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
const mappings: Record<string, Record<string, { label: string; color?: string }>> = {};
|
||||
const apiClient = (await import("@/lib/api/client")).apiClient;
|
||||
|
||||
// 최고관리자가 특정 회사 프리뷰 시 해당 회사 카테고리만 필터링
|
||||
const filterCompanyParam = companyCode && companyCode !== "*"
|
||||
? `&filterCompanyCode=${encodeURIComponent(companyCode)}`
|
||||
: "";
|
||||
|
||||
// 트리 구조를 평탄화하는 헬퍼 함수 (메인 테이블 + 엔티티 조인 공통 사용)
|
||||
// valueCode만 키로 사용 (valueId까지 넣으면 같은 라벨이 2번 나옴)
|
||||
const flattenTree = (items: any[], mapping: Record<string, { label: string; color?: string }>) => {
|
||||
items.forEach((item: any) => {
|
||||
if (item.valueCode) {
|
||||
@@ -1432,12 +1441,6 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
color: item.color,
|
||||
};
|
||||
}
|
||||
if (item.valueId !== undefined && item.valueId !== null) {
|
||||
mapping[String(item.valueId)] = {
|
||||
label: item.valueLabel,
|
||||
color: item.color,
|
||||
};
|
||||
}
|
||||
if (item.children && Array.isArray(item.children) && item.children.length > 0) {
|
||||
flattenTree(item.children, mapping);
|
||||
}
|
||||
@@ -1465,7 +1468,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
}
|
||||
|
||||
// 비활성화된 카테고리도 라벨로 표시하기 위해 includeInactive=true
|
||||
const response = await apiClient.get(`/table-categories/${targetTable}/${targetColumn}/values?includeInactive=true`);
|
||||
const response = await apiClient.get(`/table-categories/${targetTable}/${targetColumn}/values?includeInactive=true${filterCompanyParam}`);
|
||||
|
||||
if (response.data.success && response.data.data && Array.isArray(response.data.data)) {
|
||||
const mapping: Record<string, { label: string; color?: string }> = {};
|
||||
@@ -1548,7 +1551,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
// inputType이 category인 경우 카테고리 매핑 로드
|
||||
if (inputTypeInfo?.inputType === "category" && !mappings[col.columnName]) {
|
||||
try {
|
||||
const response = await apiClient.get(`/table-categories/${joinedTable}/${col.actualColumn}/values?includeInactive=true`);
|
||||
const response = await apiClient.get(`/table-categories/${joinedTable}/${col.actualColumn}/values?includeInactive=true${filterCompanyParam}`);
|
||||
|
||||
if (response.data.success && response.data.data && Array.isArray(response.data.data)) {
|
||||
const mapping: Record<string, { label: string; color?: string }> = {};
|
||||
@@ -1618,6 +1621,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
JSON.stringify(categoryColumns),
|
||||
JSON.stringify(tableConfig.columns),
|
||||
columnMeta,
|
||||
companyCode,
|
||||
]);
|
||||
|
||||
// ========================================
|
||||
|
||||
@@ -1508,7 +1508,38 @@ export const TableListConfigPanel: React.FC<TableListConfigPanelProps> = ({
|
||||
/>
|
||||
<Link2 className="h-3 w-3 flex-shrink-0 text-primary" />
|
||||
<span className="truncate text-xs">{column.columnLabel}</span>
|
||||
<span className="ml-auto text-[10px] text-primary/80">
|
||||
{isAlreadyAdded && (
|
||||
<button
|
||||
type="button"
|
||||
title={
|
||||
config.columns?.find((c) => c.columnName === matchingJoinColumn.joinAlias)?.editable === false
|
||||
? "편집 잠금 (클릭하여 해제)"
|
||||
: "편집 가능 (클릭하여 잠금)"
|
||||
}
|
||||
className={cn(
|
||||
"ml-auto flex-shrink-0 rounded p-0.5 transition-colors",
|
||||
config.columns?.find((c) => c.columnName === matchingJoinColumn.joinAlias)?.editable === false
|
||||
? "text-destructive hover:bg-destructive/10"
|
||||
: "text-muted-foreground hover:bg-muted",
|
||||
)}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
const currentCol = config.columns?.find((c) => c.columnName === matchingJoinColumn.joinAlias);
|
||||
if (currentCol) {
|
||||
updateColumn(matchingJoinColumn.joinAlias, {
|
||||
editable: currentCol.editable === false ? undefined : false,
|
||||
});
|
||||
}
|
||||
}}
|
||||
>
|
||||
{config.columns?.find((c) => c.columnName === matchingJoinColumn.joinAlias)?.editable === false ? (
|
||||
<Lock className="h-3 w-3" />
|
||||
) : (
|
||||
<Unlock className="h-3 w-3" />
|
||||
)}
|
||||
</button>
|
||||
)}
|
||||
<span className={cn("text-[10px] text-primary/80", !isAlreadyAdded && "ml-auto")}>
|
||||
{column.inputType || column.dataType}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -457,7 +457,38 @@ export const ColumnsConfigPanel: React.FC<ColumnsConfigPanelProps> = ({
|
||||
/>
|
||||
<Link2 className="h-3 w-3 flex-shrink-0 text-primary" />
|
||||
<span className="truncate text-xs">{column.columnLabel}</span>
|
||||
<span className="ml-auto text-[10px] text-primary/80">
|
||||
{isAlreadyAdded && (
|
||||
<button
|
||||
type="button"
|
||||
title={
|
||||
config.columns?.find((c: ColumnConfig) => c.columnName === matchingJoinColumn.joinAlias)?.editable === false
|
||||
? "편집 잠금 (클릭하여 해제)"
|
||||
: "편집 가능 (클릭하여 잠금)"
|
||||
}
|
||||
className={cn(
|
||||
"ml-auto flex-shrink-0 rounded p-0.5 transition-colors",
|
||||
config.columns?.find((c: ColumnConfig) => c.columnName === matchingJoinColumn.joinAlias)?.editable === false
|
||||
? "text-destructive hover:bg-destructive/10"
|
||||
: "text-muted-foreground hover:bg-muted",
|
||||
)}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
const currentCol = config.columns?.find((c: ColumnConfig) => c.columnName === matchingJoinColumn.joinAlias);
|
||||
if (currentCol) {
|
||||
onUpdateColumn(matchingJoinColumn.joinAlias, {
|
||||
editable: currentCol.editable === false ? undefined : false,
|
||||
});
|
||||
}
|
||||
}}
|
||||
>
|
||||
{config.columns?.find((c: ColumnConfig) => c.columnName === matchingJoinColumn.joinAlias)?.editable === false ? (
|
||||
<Lock className="h-3 w-3" />
|
||||
) : (
|
||||
<Unlock className="h-3 w-3" />
|
||||
)}
|
||||
</button>
|
||||
)}
|
||||
<span className={cn("text-[10px] text-primary/80", !isAlreadyAdded && "ml-auto")}>
|
||||
{column.inputType || column.dataType}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user