summaryrefslogtreecommitdiff
path: root/lib/rfqs/table/ParentRfqSelector.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'lib/rfqs/table/ParentRfqSelector.tsx')
-rw-r--r--lib/rfqs/table/ParentRfqSelector.tsx307
1 files changed, 0 insertions, 307 deletions
diff --git a/lib/rfqs/table/ParentRfqSelector.tsx b/lib/rfqs/table/ParentRfqSelector.tsx
deleted file mode 100644
index 0edb1233..00000000
--- a/lib/rfqs/table/ParentRfqSelector.tsx
+++ /dev/null
@@ -1,307 +0,0 @@
-"use client"
-
-import * as React from "react"
-import { Check, ChevronsUpDown, Loader } from "lucide-react"
-import { Button } from "@/components/ui/button"
-import { Command, CommandInput, CommandList, CommandEmpty, CommandGroup, CommandItem } from "@/components/ui/command"
-import { Popover, PopoverTrigger, PopoverContent } from "@/components/ui/popover"
-import { cn } from "@/lib/utils"
-import { useDebounce } from "@/hooks/use-debounce"
-import { getBudgetaryRfqs, type BudgetaryRfq } from "../service"
-import { RfqType } from "../validations"
-
-// ParentRfq 타입 정의 (서비스의 BudgetaryRfq와 호환되어야 함)
-interface ParentRfq {
- id: number;
- rfqCode: string;
- description: string | null;
- rfqType: RfqType;
- projectId: number | null;
- projectCode: string | null;
- projectName: string | null;
-}
-
-interface ParentRfqSelectorProps {
- selectedRfqId?: number;
- onRfqSelect: (rfq: ParentRfq | null) => void;
- rfqType: RfqType; // 현재 생성 중인 RFQ 타입
- parentRfqTypes: RfqType[]; // 선택 가능한 부모 RFQ 타입 목록
- placeholder?: string;
-}
-
-export function ParentRfqSelector({
- selectedRfqId,
- onRfqSelect,
- rfqType,
- parentRfqTypes,
- placeholder = "부모 RFQ 선택..."
-}: ParentRfqSelectorProps) {
- const [searchTerm, setSearchTerm] = React.useState("");
- const debouncedSearchTerm = useDebounce(searchTerm, 300);
-
- const [open, setOpen] = React.useState(false);
- const [loading, setLoading] = React.useState(false);
- const [parentRfqs, setParentRfqs] = React.useState<ParentRfq[]>([]);
- const [selectedRfq, setSelectedRfq] = React.useState<ParentRfq | null>(null);
- const [page, setPage] = React.useState(1);
- const [hasMore, setHasMore] = React.useState(true);
- const [totalCount, setTotalCount] = React.useState(0);
-
- const listRef = React.useRef<HTMLDivElement>(null);
-
- // 타입별로 적절한 검색 placeholder 생성
- const getSearchPlaceholder = () => {
- if (rfqType === RfqType.PURCHASE) {
- return "BUDGETARY/PURCHASE_BUDGETARY RFQ 검색...";
- } else if (rfqType === RfqType.PURCHASE_BUDGETARY) {
- return "BUDGETARY RFQ 검색...";
- }
- return "RFQ 코드/설명/프로젝트 검색...";
- };
-
- // 초기 선택된 RFQ가 있을 경우 로드
- React.useEffect(() => {
- if (selectedRfqId && open) {
- const loadSelectedRfq = async () => {
- try {
- // 단일 RFQ를 id로 조회하는 API 호출
- const result = await getBudgetaryRfqs({
- limit: 1,
- rfqId: selectedRfqId
- });
-
- if ('rfqs' in result && result.rfqs && result.rfqs.length > 0) {
- setSelectedRfq(result.rfqs[0] as unknown as ParentRfq);
- }
- } catch (error) {
- console.error("선택된 RFQ 로드 오류:", error);
- }
- };
-
- if (!selectedRfq || selectedRfq.id !== selectedRfqId) {
- loadSelectedRfq();
- }
- }
- }, [selectedRfqId, open, selectedRfq]);
-
- // 검색어 변경 시 데이터 리셋 및 재로드
- React.useEffect(() => {
- if (open) {
- setPage(1);
- setHasMore(true);
- setParentRfqs([]);
- loadParentRfqs(1, true);
- }
- }, [debouncedSearchTerm, open, parentRfqTypes]);
-
- // 데이터 로드 함수
- const loadParentRfqs = async (pageToLoad: number, reset = false) => {
- if (!open || parentRfqTypes.length === 0) return;
-
- setLoading(true);
- try {
- const limit = 20; // 한 번에 로드할 항목 수
- const result = await getBudgetaryRfqs({
- search: debouncedSearchTerm,
- limit,
- offset: (pageToLoad - 1) * limit,
- rfqTypes: parentRfqTypes // 현재 RFQ 타입에 맞는 부모 RFQ 타입들로 필터링
- });
-
- if ('rfqs' in result && result.rfqs) {
- if (reset) {
- setParentRfqs(result.rfqs as unknown as ParentRfq[]);
- } else {
- setParentRfqs(prev => [...prev, ...(result.rfqs as unknown as ParentRfq[])]);
- }
-
- setTotalCount(result.totalCount);
- setHasMore(result.rfqs.length === limit && (pageToLoad * limit) < result.totalCount);
- setPage(pageToLoad);
- }
- } catch (error) {
- console.error("부모 RFQ 로드 오류:", error);
- } finally {
- setLoading(false);
- }
- };
-
- // 무한 스크롤 처리
- const handleScroll = () => {
- if (listRef.current) {
- const { scrollTop, scrollHeight, clientHeight } = listRef.current;
-
- // 스크롤이 90% 이상 내려갔을 때 다음 페이지 로드
- if (scrollTop + clientHeight >= scrollHeight * 0.9 && !loading && hasMore) {
- loadParentRfqs(page + 1);
- }
- }
- };
-
- // RFQ를 프로젝트별로 그룹화하는 함수
- const groupRfqsByProject = (rfqs: ParentRfq[]) => {
- const groups: Record<string, {
- projectId: number | null;
- projectCode: string | null;
- projectName: string | null;
- rfqs: ParentRfq[];
- }> = {};
-
- // 'No Project' 그룹 기본 생성
- groups['no-project'] = {
- projectId: null,
- projectCode: null,
- projectName: null,
- rfqs: []
- };
-
- // 프로젝트별로 RFQ 그룹화
- rfqs.forEach(rfq => {
- const key = rfq.projectId ? `project-${rfq.projectId}` : 'no-project';
-
- if (!groups[key] && rfq.projectId) {
- groups[key] = {
- projectId: rfq.projectId,
- projectCode: rfq.projectCode,
- projectName: rfq.projectName,
- rfqs: []
- };
- }
-
- groups[key].rfqs.push(rfq);
- });
-
- // 필터링된 결과가 있는 그룹만 남기기
- return Object.values(groups).filter(group => group.rfqs.length > 0);
- };
-
- // 그룹화된 RFQ 목록
- const groupedRfqs = React.useMemo(() => {
- return groupRfqsByProject(parentRfqs);
- }, [parentRfqs]);
-
- // RFQ 선택 처리
- const handleRfqSelect = (rfq: ParentRfq | null) => {
- setSelectedRfq(rfq);
- onRfqSelect(rfq);
- setOpen(false);
- };
-
- // RFQ 타입에 따른 표시 형식
- const getRfqTypeLabel = (type: RfqType) => {
- switch(type) {
- case RfqType.BUDGETARY:
- return "BUDGETARY";
- case RfqType.PURCHASE_BUDGETARY:
- return "PURCHASE_BUDGETARY";
- case RfqType.PURCHASE:
- return "PURCHASE";
- default:
- return type;
- }
- };
-
- return (
- <Popover open={open} onOpenChange={setOpen}>
- <PopoverTrigger asChild>
- <Button
- variant="outline"
- role="combobox"
- aria-expanded={open}
- className="w-full justify-between"
- >
- {selectedRfq
- ? `${selectedRfq.rfqCode || ""} - ${selectedRfq.description || ""}`
- : placeholder}
- <ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
- </Button>
- </PopoverTrigger>
- <PopoverContent className="w-[400px] p-0">
- <Command>
- <CommandInput
- placeholder={getSearchPlaceholder()}
- value={searchTerm}
- onValueChange={setSearchTerm}
- />
- <CommandList
- className="max-h-[300px]"
- ref={listRef}
- onScroll={handleScroll}
- >
- <CommandEmpty>검색 결과가 없습니다</CommandEmpty>
-
- <CommandGroup>
- <CommandItem
- value="none"
- onSelect={() => handleRfqSelect(null)}
- >
- <Check
- className={cn(
- "mr-2 h-4 w-4",
- !selectedRfq
- ? "opacity-100"
- : "opacity-0"
- )}
- />
- <span className="font-medium">선택 안함</span>
- </CommandItem>
- </CommandGroup>
-
- {groupedRfqs.map((group, index) => (
- <CommandGroup
- key={`group-${group.projectId || index}`}
- heading={
- group.projectId
- ? `${group.projectCode || ""} - ${group.projectName || ""}`
- : "프로젝트 없음"
- }
- >
- {group.rfqs.map((rfq) => (
- <CommandItem
- key={rfq.id}
- value={`${rfq.rfqCode || ""} ${rfq.description || ""}`}
- onSelect={() => handleRfqSelect(rfq)}
- >
- <Check
- className={cn(
- "mr-2 h-4 w-4",
- selectedRfq?.id === rfq.id
- ? "opacity-100"
- : "opacity-0"
- )}
- />
- <div className="flex flex-col">
- <div className="flex items-center">
- <span className="font-medium">{rfq.rfqCode || ""}</span>
- <span className="ml-2 text-xs px-1.5 py-0.5 rounded bg-slate-100 text-slate-700">
- {getRfqTypeLabel(rfq.rfqType)}
- </span>
- </div>
- {rfq.description && (
- <span className="text-sm text-gray-500 truncate">
- {rfq.description}
- </span>
- )}
- </div>
- </CommandItem>
- ))}
- </CommandGroup>
- ))}
-
- {loading && (
- <div className="py-2 text-center">
- <Loader className="h-4 w-4 animate-spin mx-auto" />
- </div>
- )}
-
- {!loading && !hasMore && parentRfqs.length > 0 && (
- <div className="py-2 text-center text-sm text-muted-foreground">
- 총 {totalCount}개 중 {parentRfqs.length}개 표시됨
- </div>
- )}
- </CommandList>
- </Command>
- </PopoverContent>
- </Popover>
- );
-} \ No newline at end of file