Files
vexplor/frontend/components/dataflow/ScreenSelector.tsx
2025-09-05 16:19:31 +09:00

175 lines
6.8 KiB
TypeScript

"use client";
import React, { useState, useEffect } from "react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Badge } from "@/components/ui/badge";
import { Search, Plus, Database, Calendar, User } from "lucide-react";
import { DataFlowAPI, ScreenDefinition } from "@/lib/api/dataflow";
interface ScreenSelectorProps {
companyCode: string;
onScreenAdd: (screen: ScreenDefinition) => void;
selectedScreens?: number[]; // 이미 추가된 화면들의 ID
}
export const ScreenSelector: React.FC<ScreenSelectorProps> = ({ companyCode, onScreenAdd, selectedScreens = [] }) => {
const [screens, setScreens] = useState<ScreenDefinition[]>([]);
const [filteredScreens, setFilteredScreens] = useState<ScreenDefinition[]>([]);
const [searchTerm, setSearchTerm] = useState("");
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
// 화면 목록 로드
useEffect(() => {
loadScreens();
}, [companyCode]);
// 검색 필터링
useEffect(() => {
if (searchTerm.trim() === "") {
setFilteredScreens(screens);
} else {
const filtered = screens.filter(
(screen) =>
screen.screenName.toLowerCase().includes(searchTerm.toLowerCase()) ||
screen.screenCode.toLowerCase().includes(searchTerm.toLowerCase()) ||
screen.tableName.toLowerCase().includes(searchTerm.toLowerCase()) ||
(screen.description && screen.description.toLowerCase().includes(searchTerm.toLowerCase())),
);
setFilteredScreens(filtered);
}
}, [screens, searchTerm]);
const loadScreens = async () => {
try {
setLoading(true);
setError(null);
const screenList = await DataFlowAPI.getScreensByCompany(companyCode);
setScreens(screenList);
} catch (error) {
console.error("화면 목록 로드 실패:", error);
setError("화면 목록을 불러오는데 실패했습니다. 로그인 상태를 확인해주세요.");
} finally {
setLoading(false);
}
};
const handleAddScreen = (screen: ScreenDefinition) => {
onScreenAdd(screen);
};
const isScreenSelected = (screenId: number) => {
return selectedScreens.includes(screenId);
};
const formatDate = (dateString: string) => {
return new Date(dateString).toLocaleDateString("ko-KR", {
year: "numeric",
month: "2-digit",
day: "2-digit",
});
};
return (
<div className="space-y-4">
<div className="flex items-center justify-between">
<h3 className="text-lg font-semibold text-gray-800"> </h3>
<Button onClick={loadScreens} variant="outline" size="sm" disabled={loading}>
{loading ? "로딩중..." : "새로고침"}
</Button>
</div>
{/* 검색 입력 */}
<div className="relative">
<Search className="absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2 text-gray-400" />
<Input
placeholder="화면명, 코드, 테이블명으로 검색..."
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
className="pl-10"
/>
</div>
{/* 오류 메시지 */}
{error && <div className="rounded-lg bg-red-50 p-4 text-sm text-red-600">{error}</div>}
{/* 화면 목록 */}
<div className="max-h-96 space-y-2 overflow-y-auto">
{loading ? (
<div className="flex items-center justify-center py-8">
<div className="text-sm text-gray-500"> ...</div>
</div>
) : filteredScreens.length === 0 ? (
<div className="flex items-center justify-center py-8">
<div className="text-center text-sm text-gray-500">
{searchTerm ? "검색 결과가 없습니다." : "등록된 화면이 없습니다."}
</div>
</div>
) : (
filteredScreens.map((screen) => (
<Card
key={screen.screenId}
className={`transition-all hover:shadow-md ${
isScreenSelected(screen.screenId) ? "border-blue-500 bg-blue-50" : "hover:border-gray-300"
}`}
>
<CardHeader className="pb-2">
<div className="flex items-center justify-between">
<CardTitle className="text-sm font-medium">{screen.screenName}</CardTitle>
<div className="flex items-center gap-2">
<Badge variant={screen.isActive === "Y" ? "default" : "secondary"} className="text-xs">
{screen.isActive === "Y" ? "활성" : "비활성"}
</Badge>
<Button
onClick={() => handleAddScreen(screen)}
disabled={isScreenSelected(screen.screenId)}
size="sm"
className="h-7 px-2"
>
<Plus className="h-3 w-3" />
{isScreenSelected(screen.screenId) ? "추가됨" : "추가"}
</Button>
</div>
</div>
</CardHeader>
<CardContent className="pt-0">
<div className="space-y-2">
<div className="flex items-center gap-2 text-xs text-gray-600">
<span className="rounded bg-gray-100 px-1.5 py-0.5 font-mono">{screen.screenCode}</span>
<Database className="h-3 w-3" />
<span className="font-mono">{screen.tableName}</span>
</div>
{screen.description && <p className="line-clamp-2 text-xs text-gray-500">{screen.description}</p>}
<div className="flex items-center justify-between text-xs text-gray-400">
<div className="flex items-center gap-1">
<User className="h-3 w-3" />
<span>{screen.createdBy || "시스템"}</span>
</div>
<div className="flex items-center gap-1">
<Calendar className="h-3 w-3" />
<span>{formatDate(screen.createdDate)}</span>
</div>
</div>
</div>
</CardContent>
</Card>
))
)}
</div>
{/* 통계 정보 */}
<div className="rounded-lg bg-gray-50 p-3 text-xs text-gray-600">
<div className="flex items-center justify-between">
<span> : {screens.length}</span>
<span>{searchTerm ? `검색 결과: ${filteredScreens.length}` : ""}</span>
</div>
{selectedScreens.length > 0 && <div className="mt-1"> : {selectedScreens.length}</div>}
</div>
</div>
);
};