This commit is contained in:
kjs 2025-11-25 09:34:44 +09:00
parent 3f60f9ca3e
commit 9fda390c55
3 changed files with 84 additions and 29 deletions

View File

@ -148,11 +148,11 @@ export const updateScreenInfo = async (
try { try {
const { id } = req.params; const { id } = req.params;
const { companyCode } = req.user as any; const { companyCode } = req.user as any;
const { screenName, description, isActive } = req.body; const { screenName, tableName, description, isActive } = req.body;
await screenManagementService.updateScreenInfo( await screenManagementService.updateScreenInfo(
parseInt(id), parseInt(id),
{ screenName, description, isActive }, { screenName, tableName, description, isActive },
companyCode companyCode
); );
res.json({ success: true, message: "화면 정보가 수정되었습니다." }); res.json({ success: true, message: "화면 정보가 수정되었습니다." });

View File

@ -321,7 +321,7 @@ export class ScreenManagementService {
*/ */
async updateScreenInfo( async updateScreenInfo(
screenId: number, screenId: number,
updateData: { screenName: string; description?: string; isActive: string }, updateData: { screenName: string; tableName?: string; description?: string; isActive: string },
userCompanyCode: string userCompanyCode: string
): Promise<void> { ): Promise<void> {
// 권한 확인 // 권한 확인
@ -343,16 +343,18 @@ export class ScreenManagementService {
throw new Error("이 화면을 수정할 권한이 없습니다."); throw new Error("이 화면을 수정할 권한이 없습니다.");
} }
// 화면 정보 업데이트 // 화면 정보 업데이트 (tableName 포함)
await query( await query(
`UPDATE screen_definitions `UPDATE screen_definitions
SET screen_name = $1, SET screen_name = $1,
description = $2, table_name = $2,
is_active = $3, description = $3,
updated_date = $4 is_active = $4,
WHERE screen_id = $5`, updated_date = $5
WHERE screen_id = $6`,
[ [
updateData.screenName, updateData.screenName,
updateData.tableName || null,
updateData.description || null, updateData.description || null,
updateData.isActive, updateData.isActive,
new Date(), new Date(),

View File

@ -35,7 +35,10 @@ import {
DialogDescription, DialogDescription,
} from "@/components/ui/dialog"; } from "@/components/ui/dialog";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { MoreHorizontal, Edit, Trash2, Copy, Eye, Plus, Search, Palette, RotateCcw, Trash } from "lucide-react"; import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from "@/components/ui/command";
import { cn } from "@/lib/utils";
import { MoreHorizontal, Edit, Trash2, Copy, Eye, Plus, Search, Palette, RotateCcw, Trash, Check, ChevronsUpDown } from "lucide-react";
import { ScreenDefinition } from "@/types/screen"; import { ScreenDefinition } from "@/types/screen";
import { screenApi } from "@/lib/api/screen"; import { screenApi } from "@/lib/api/screen";
import CreateScreenModal from "./CreateScreenModal"; import CreateScreenModal from "./CreateScreenModal";
@ -127,8 +130,9 @@ export default function ScreenList({ onScreenSelect, selectedScreen, onDesignScr
isActive: "Y", isActive: "Y",
tableName: "", tableName: "",
}); });
const [tables, setTables] = useState<string[]>([]); const [tables, setTables] = useState<Array<{ tableName: string; tableLabel: string }>>([]);
const [loadingTables, setLoadingTables] = useState(false); const [loadingTables, setLoadingTables] = useState(false);
const [tableComboboxOpen, setTableComboboxOpen] = useState(false);
// 미리보기 관련 상태 // 미리보기 관련 상태
const [previewDialogOpen, setPreviewDialogOpen] = useState(false); const [previewDialogOpen, setPreviewDialogOpen] = useState(false);
@ -279,9 +283,12 @@ export default function ScreenList({ onScreenSelect, selectedScreen, onDesignScr
const { tableManagementApi } = await import("@/lib/api/tableManagement"); const { tableManagementApi } = await import("@/lib/api/tableManagement");
const response = await tableManagementApi.getTableList(); const response = await tableManagementApi.getTableList();
if (response.success && response.data) { if (response.success && response.data) {
// tableName만 추출 (camelCase) // tableName과 displayName 매핑 (백엔드에서 displayName으로 라벨을 반환함)
const tableNames = response.data.map((table: any) => table.tableName); const tableList = response.data.map((table: any) => ({
setTables(tableNames); tableName: table.tableName,
tableLabel: table.displayName || table.tableName,
}));
setTables(tableList);
} }
} catch (error) { } catch (error) {
console.error("테이블 목록 조회 실패:", error); console.error("테이블 목록 조회 실패:", error);
@ -297,6 +304,10 @@ export default function ScreenList({ onScreenSelect, selectedScreen, onDesignScr
// 화면 정보 업데이트 API 호출 // 화면 정보 업데이트 API 호출
await screenApi.updateScreenInfo(screenToEdit.screenId, editFormData); await screenApi.updateScreenInfo(screenToEdit.screenId, editFormData);
// 선택된 테이블의 라벨 찾기
const selectedTable = tables.find((t) => t.tableName === editFormData.tableName);
const tableLabel = selectedTable?.tableLabel || editFormData.tableName;
// 목록에서 해당 화면 정보 업데이트 // 목록에서 해당 화면 정보 업데이트
setScreens((prev) => setScreens((prev) =>
prev.map((s) => prev.map((s) =>
@ -304,6 +315,8 @@ export default function ScreenList({ onScreenSelect, selectedScreen, onDesignScr
? { ? {
...s, ...s,
screenName: editFormData.screenName, screenName: editFormData.screenName,
tableName: editFormData.tableName,
tableLabel: tableLabel,
description: editFormData.description, description: editFormData.description,
isActive: editFormData.isActive, isActive: editFormData.isActive,
} }
@ -1202,22 +1215,62 @@ export default function ScreenList({ onScreenSelect, selectedScreen, onDesignScr
</div> </div>
<div className="space-y-2"> <div className="space-y-2">
<Label htmlFor="edit-tableName"> *</Label> <Label htmlFor="edit-tableName"> *</Label>
<Select <Popover open={tableComboboxOpen} onOpenChange={setTableComboboxOpen}>
value={editFormData.tableName} <PopoverTrigger asChild>
onValueChange={(value) => setEditFormData({ ...editFormData, tableName: value })} <Button
disabled={loadingTables} variant="outline"
> role="combobox"
<SelectTrigger id="edit-tableName"> aria-expanded={tableComboboxOpen}
<SelectValue placeholder={loadingTables ? "로딩 중..." : "테이블을 선택하세요"} /> className="h-8 w-full justify-between text-xs sm:h-10 sm:text-sm"
</SelectTrigger> disabled={loadingTables}
<SelectContent> >
{tables.map((tableName) => ( {loadingTables
<SelectItem key={tableName} value={tableName}> ? "로딩 중..."
{tableName} : editFormData.tableName
</SelectItem> ? tables.find((table) => table.tableName === editFormData.tableName)?.tableLabel || editFormData.tableName
))} : "테이블을 선택하세요"}
</SelectContent> <ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
</Select> </Button>
</PopoverTrigger>
<PopoverContent
className="p-0"
style={{ width: "var(--radix-popover-trigger-width)" }}
align="start"
>
<Command>
<CommandInput placeholder="테이블 검색..." className="text-xs sm:text-sm" />
<CommandList>
<CommandEmpty className="text-xs sm:text-sm">
.
</CommandEmpty>
<CommandGroup>
{tables.map((table) => (
<CommandItem
key={table.tableName}
value={`${table.tableName} ${table.tableLabel}`}
onSelect={() => {
setEditFormData({ ...editFormData, tableName: table.tableName });
setTableComboboxOpen(false);
}}
className="text-xs sm:text-sm"
>
<Check
className={cn(
"mr-2 h-4 w-4",
editFormData.tableName === table.tableName ? "opacity-100" : "opacity-0"
)}
/>
<div className="flex flex-col">
<span className="font-medium">{table.tableLabel}</span>
<span className="text-[10px] text-gray-500">{table.tableName}</span>
</div>
</CommandItem>
))}
</CommandGroup>
</CommandList>
</Command>
</PopoverContent>
</Popover>
</div> </div>
<div className="space-y-2"> <div className="space-y-2">
<Label htmlFor="edit-description"></Label> <Label htmlFor="edit-description"></Label>