11
This commit is contained in:
@@ -76,18 +76,34 @@ export function ColumnDetailPanel({
|
||||
|
||||
if (!column) return null;
|
||||
|
||||
const refTableOpts = referenceTableOptions.length
|
||||
? referenceTableOptions
|
||||
: [
|
||||
{ value: "none", label: "선택 안함" },
|
||||
...tables.map((t) => ({
|
||||
value: t.tableName,
|
||||
label:
|
||||
t.displayName && t.displayName !== t.tableName
|
||||
? `${t.displayName} (${t.tableName})`
|
||||
: t.tableName,
|
||||
})),
|
||||
];
|
||||
const refTableOpts = useMemo(() => {
|
||||
const hasKorean = (s: string) => /[가-힣]/.test(s);
|
||||
const raw = referenceTableOptions.length
|
||||
? [...referenceTableOptions]
|
||||
: [
|
||||
{ value: "none", label: "없음" },
|
||||
...tables.map((t) => ({
|
||||
value: t.tableName,
|
||||
label:
|
||||
t.displayName && t.displayName !== t.tableName
|
||||
? `${t.displayName} (${t.tableName})`
|
||||
: t.tableName,
|
||||
})),
|
||||
];
|
||||
|
||||
const noneOpt = raw.find((o) => o.value === "none");
|
||||
const rest = raw.filter((o) => o.value !== "none");
|
||||
|
||||
rest.sort((a, b) => {
|
||||
const aK = hasKorean(a.label);
|
||||
const bK = hasKorean(b.label);
|
||||
if (aK && !bK) return -1;
|
||||
if (!aK && bK) return 1;
|
||||
return a.label.localeCompare(b.label, "ko");
|
||||
});
|
||||
|
||||
return noneOpt ? [noneOpt, ...rest] : rest;
|
||||
}, [referenceTableOptions, tables]);
|
||||
|
||||
return (
|
||||
<div className="flex h-full w-full flex-col border-l bg-card">
|
||||
@@ -183,23 +199,33 @@ export function ColumnDetailPanel({
|
||||
<CommandList className="max-h-[200px]">
|
||||
<CommandEmpty className="py-2 text-center text-xs">테이블을 찾을 수 없습니다.</CommandEmpty>
|
||||
<CommandGroup>
|
||||
{refTableOpts.map((opt) => (
|
||||
<CommandItem
|
||||
key={opt.value}
|
||||
value={`${opt.label} ${opt.value}`}
|
||||
onSelect={() => {
|
||||
onColumnChange("referenceTable", opt.value === "none" ? undefined : opt.value);
|
||||
if (opt.value !== "none") onLoadReferenceColumns?.(opt.value);
|
||||
setEntityTableOpen(false);
|
||||
}}
|
||||
className="text-xs"
|
||||
>
|
||||
<Check
|
||||
className={cn("mr-2 h-3 w-3", column.referenceTable === opt.value ? "opacity-100" : "opacity-0")}
|
||||
/>
|
||||
{opt.label}
|
||||
</CommandItem>
|
||||
))}
|
||||
{refTableOpts.map((opt) => {
|
||||
const hasKorean = opt.value !== "none" && opt.label !== opt.value && !opt.label.startsWith(opt.value);
|
||||
return (
|
||||
<CommandItem
|
||||
key={opt.value}
|
||||
value={`${opt.label} ${opt.value}`}
|
||||
onSelect={() => {
|
||||
onColumnChange("referenceTable", opt.value === "none" ? undefined : opt.value);
|
||||
if (opt.value !== "none") onLoadReferenceColumns?.(opt.value);
|
||||
setEntityTableOpen(false);
|
||||
}}
|
||||
className="text-xs"
|
||||
>
|
||||
<Check
|
||||
className={cn("mr-2 h-3 w-3", column.referenceTable === opt.value ? "opacity-100" : "opacity-0")}
|
||||
/>
|
||||
{hasKorean ? (
|
||||
<div className="flex flex-col">
|
||||
<span className="font-medium">{opt.label.replace(` (${opt.value})`, "")}</span>
|
||||
<span className="text-[10px] text-muted-foreground">{opt.value}</span>
|
||||
</div>
|
||||
) : (
|
||||
opt.label
|
||||
)}
|
||||
</CommandItem>
|
||||
);
|
||||
})}
|
||||
</CommandGroup>
|
||||
</CommandList>
|
||||
</Command>
|
||||
@@ -263,13 +289,14 @@ export function ColumnDetailPanel({
|
||||
column.referenceColumn === refCol.columnName ? "opacity-100" : "opacity-0",
|
||||
)}
|
||||
/>
|
||||
<div className="flex flex-col">
|
||||
<span className="font-medium">
|
||||
{refCol.displayName && refCol.displayName !== refCol.columnName
|
||||
? `${refCol.displayName} (${refCol.columnName})`
|
||||
: refCol.columnName}
|
||||
</span>
|
||||
</div>
|
||||
{refCol.displayName && refCol.displayName !== refCol.columnName ? (
|
||||
<div className="flex flex-col">
|
||||
<span className="font-medium">{refCol.displayName}</span>
|
||||
<span className="text-[10px] text-muted-foreground">{refCol.columnName}</span>
|
||||
</div>
|
||||
) : (
|
||||
<span>{refCol.columnName}</span>
|
||||
)}
|
||||
</CommandItem>
|
||||
))}
|
||||
</CommandGroup>
|
||||
|
||||
@@ -568,18 +568,7 @@ function AppLayoutInner({ children }: AppLayoutProps) {
|
||||
);
|
||||
}
|
||||
|
||||
if (!user) {
|
||||
return (
|
||||
<div className="flex h-screen items-center justify-center">
|
||||
<div className="flex flex-col items-center">
|
||||
<div className="border-primary mb-4 h-8 w-8 animate-spin rounded-full border-4 border-t-transparent"></div>
|
||||
<p>로딩중...</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const uiMenus = convertMenuToUI(currentMenus, user as ExtendedUserInfo);
|
||||
const uiMenus = user ? convertMenuToUI(currentMenus, user as ExtendedUserInfo) : [];
|
||||
|
||||
// 활성 탭에 해당하는 메뉴가 속한 부모 메뉴 자동 확장
|
||||
useEffect(() => {
|
||||
@@ -603,6 +592,17 @@ function AppLayoutInner({ children }: AppLayoutProps) {
|
||||
}
|
||||
}, [activeTab, uiMenus, isMenuActive, expandedMenus]);
|
||||
|
||||
if (!user) {
|
||||
return (
|
||||
<div className="flex h-screen items-center justify-center">
|
||||
<div className="flex flex-col items-center">
|
||||
<div className="border-primary mb-4 h-8 w-8 animate-spin rounded-full border-4 border-t-transparent"></div>
|
||||
<p>로딩중...</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="bg-background flex h-screen flex-col">
|
||||
{/* 모바일 헤더 */}
|
||||
|
||||
@@ -493,8 +493,8 @@ export function TabBar() {
|
||||
className={cn(
|
||||
"group relative flex h-7 shrink-0 cursor-pointer items-center gap-0.5 rounded-t-md border border-b-0 px-3 select-none",
|
||||
isActive
|
||||
? "text-foreground z-10 -mb-px h-[30px] bg-white"
|
||||
: "bg-muted/50 text-muted-foreground hover:bg-muted hover:text-foreground border-transparent",
|
||||
? "text-primary z-10 -mb-px h-[30px] bg-primary/15 dark:bg-primary/20 border-primary/40 border-t-[3px] border-t-primary font-semibold"
|
||||
: "bg-transparent text-muted-foreground hover:bg-muted/50 hover:text-foreground border-transparent",
|
||||
)}
|
||||
style={{
|
||||
width: TAB_WIDTH,
|
||||
|
||||
@@ -1552,16 +1552,22 @@ export const V2SplitPanelLayoutConfigPanel: React.FC<
|
||||
/>
|
||||
<SwitchRow
|
||||
label="수정 버튼"
|
||||
checked={config.rightPanel?.showEdit ?? false}
|
||||
checked={(config.rightPanel?.showEdit ?? config.rightPanel?.editButton?.enabled) ?? false}
|
||||
onCheckedChange={(checked) =>
|
||||
updateRightPanel({ showEdit: checked })
|
||||
updateRightPanel({
|
||||
showEdit: checked,
|
||||
editButton: { ...config.rightPanel?.editButton!, enabled: checked },
|
||||
})
|
||||
}
|
||||
/>
|
||||
<SwitchRow
|
||||
label="삭제 버튼"
|
||||
checked={config.rightPanel?.showDelete ?? false}
|
||||
checked={(config.rightPanel?.showDelete ?? config.rightPanel?.deleteButton?.enabled) ?? false}
|
||||
onCheckedChange={(checked) =>
|
||||
updateRightPanel({ showDelete: checked })
|
||||
updateRightPanel({
|
||||
showDelete: checked,
|
||||
deleteButton: { ...config.rightPanel?.deleteButton!, enabled: checked },
|
||||
})
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user