summaryrefslogtreecommitdiff
path: root/lib/rfqs/table/BudgetaryRfqSelector.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'lib/rfqs/table/BudgetaryRfqSelector.tsx')
-rw-r--r--lib/rfqs/table/BudgetaryRfqSelector.tsx261
1 files changed, 0 insertions, 261 deletions
diff --git a/lib/rfqs/table/BudgetaryRfqSelector.tsx b/lib/rfqs/table/BudgetaryRfqSelector.tsx
deleted file mode 100644
index cea53c1d..00000000
--- a/lib/rfqs/table/BudgetaryRfqSelector.tsx
+++ /dev/null
@@ -1,261 +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"
-
-interface BudgetaryRfqSelectorProps {
- selectedRfqId?: number;
- onRfqSelect: (rfq: BudgetaryRfq | null) => void;
- placeholder?: string;
-}
-
-export function BudgetaryRfqSelector({
- selectedRfqId,
- onRfqSelect,
- placeholder = "Budgetary RFQ 선택..."
-}: BudgetaryRfqSelectorProps) {
- const [searchTerm, setSearchTerm] = React.useState("");
- const debouncedSearchTerm = useDebounce(searchTerm, 300);
-
- const [open, setOpen] = React.useState(false);
- const [loading, setLoading] = React.useState(false);
- const [budgetaryRfqs, setBudgetaryRfqs] = React.useState<BudgetaryRfq[]>([]);
- const [selectedRfq, setSelectedRfq] = React.useState<BudgetaryRfq | 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);
-
- // 초기 선택된 RFQ가 있을 경우 로드
- React.useEffect(() => {
- if (selectedRfqId && open) {
- const loadSelectedRfq = async () => {
- try {
- const result = await getBudgetaryRfqs({
- limit: 1,
- // null을 undefined로 변환하여 타입 오류 해결
- projectId: selectedRfq?.projectId ?? undefined
- });
-
- if ('rfqs' in result && result.rfqs) {
- // 옵셔널 체이닝 또는 조건부 검사로 undefined 체크
- const foundRfq = result.rfqs.find(rfq => rfq.id === selectedRfqId);
- if (foundRfq) {
- setSelectedRfq(foundRfq);
- }
- }
- } catch (error) {
- console.error("선택된 RFQ 로드 오류:", error);
- }
- };
-
- if (!selectedRfq || selectedRfq.id !== selectedRfqId) {
- loadSelectedRfq();
- }
- }
- }, [selectedRfqId, open, selectedRfq]);
-
- // 검색어 변경 시 데이터 리셋 및 재로드
- React.useEffect(() => {
- if (open) {
- setPage(1);
- setHasMore(true);
- setBudgetaryRfqs([]);
- loadBudgetaryRfqs(1, true);
- }
- }, [debouncedSearchTerm, open]);
-
- // 데이터 로드 함수
- const loadBudgetaryRfqs = async (pageToLoad: number, reset = false) => {
- if (!open) return;
-
- setLoading(true);
- try {
- const limit = 20; // 한 번에 로드할 항목 수
- const result = await getBudgetaryRfqs({
- search: debouncedSearchTerm,
- limit,
- offset: (pageToLoad - 1) * limit,
- });
-
- if ('rfqs' in result && result.rfqs) {
- if (reset) {
- setBudgetaryRfqs(result.rfqs);
- } else {
- setBudgetaryRfqs(prev => [...prev, ...result.rfqs]);
- }
-
- setTotalCount(result.totalCount);
- setHasMore(result.rfqs.length === limit && (pageToLoad * limit) < result.totalCount);
- setPage(pageToLoad);
- }
- } catch (error) {
- console.error("Budgetary RFQs 로드 오류:", error);
- } finally {
- setLoading(false);
- }
- };
-
- // 무한 스크롤 처리
- const handleScroll = () => {
- if (listRef.current) {
- const { scrollTop, scrollHeight, clientHeight } = listRef.current;
-
- // 스크롤이 90% 이상 내려갔을 때 다음 페이지 로드
- if (scrollTop + clientHeight >= scrollHeight * 0.9 && !loading && hasMore) {
- loadBudgetaryRfqs(page + 1);
- }
- }
- };
-
- // RFQ를 프로젝트별로 그룹화하는 함수
- const groupRfqsByProject = (rfqs: BudgetaryRfq[]) => {
- const groups: Record<string, {
- projectId: number | null;
- projectCode: string | null;
- projectName: string | null;
- rfqs: BudgetaryRfq[];
- }> = {};
-
- // '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(budgetaryRfqs);
- }, [budgetaryRfqs]);
-
- // RFQ 선택 처리
- const handleRfqSelect = (rfq: BudgetaryRfq | null) => {
- setSelectedRfq(rfq);
- onRfqSelect(rfq);
- setOpen(false);
- };
-
- 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="Budgetary RFQ 코드/설명/프로젝트 검색..."
- 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"
- )}
- />
- <span className="font-medium">{rfq.rfqCode || ""}</span>
- <span className="ml-2 text-gray-500 truncate">
- - {rfq.description || ""}
- </span>
- </CommandItem>
- ))}
- </CommandGroup>
- ))}
-
- {loading && (
- <div className="py-2 text-center">
- <Loader className="h-4 w-4 animate-spin mx-auto" />
- </div>
- )}
-
- {!loading && !hasMore && budgetaryRfqs.length > 0 && (
- <div className="py-2 text-center text-sm text-muted-foreground">
- 총 {totalCount}개 중 {budgetaryRfqs.length}개 표시됨
- </div>
- )}
- </CommandList>
- </Command>
- </PopoverContent>
- </Popover>
- );
-} \ No newline at end of file