249 lines
8.8 KiB
TypeScript
249 lines
8.8 KiB
TypeScript
"use client";
|
|
|
|
import React, { useState } from "react";
|
|
import { FileText, Download, Calendar, Folder, Search } from "lucide-react";
|
|
import { DashboardElement } from "@/components/admin/dashboard/types";
|
|
|
|
interface Document {
|
|
id: string;
|
|
name: string;
|
|
category: "계약서" | "보험" | "세금계산서" | "기타";
|
|
size: string;
|
|
uploadDate: string;
|
|
url: string;
|
|
description?: string;
|
|
}
|
|
|
|
// 목 데이터 (하드코딩 - 주석처리됨)
|
|
// const mockDocuments: Document[] = [
|
|
// {
|
|
// id: "1",
|
|
// name: "2025년 1월 세금계산서.pdf",
|
|
// category: "세금계산서",
|
|
// size: "1.2 MB",
|
|
// uploadDate: "2025-01-05",
|
|
// url: "/documents/tax-invoice-202501.pdf",
|
|
// description: "1월 매출 세금계산서",
|
|
// },
|
|
// {
|
|
// id: "2",
|
|
// name: "차량보험증권_서울12가3456.pdf",
|
|
// category: "보험",
|
|
// size: "856 KB",
|
|
// uploadDate: "2024-12-20",
|
|
// url: "/documents/insurance-vehicle-1.pdf",
|
|
// description: "1톤 트럭 종합보험",
|
|
// },
|
|
// {
|
|
// id: "3",
|
|
// name: "운송계약서_ABC물류.pdf",
|
|
// category: "계약서",
|
|
// size: "2.4 MB",
|
|
// uploadDate: "2024-12-15",
|
|
// url: "/documents/contract-abc-logistics.pdf",
|
|
// description: "ABC물류 연간 운송 계약",
|
|
// },
|
|
// {
|
|
// id: "4",
|
|
// name: "2024년 12월 세금계산서.pdf",
|
|
// category: "세금계산서",
|
|
// size: "1.1 MB",
|
|
// uploadDate: "2024-12-05",
|
|
// url: "/documents/tax-invoice-202412.pdf",
|
|
// },
|
|
// {
|
|
// id: "5",
|
|
// name: "화물배상책임보험증권.pdf",
|
|
// category: "보험",
|
|
// size: "720 KB",
|
|
// uploadDate: "2024-11-30",
|
|
// url: "/documents/cargo-insurance.pdf",
|
|
// description: "화물 배상책임보험",
|
|
// },
|
|
// {
|
|
// id: "6",
|
|
// name: "차고지 임대계약서.pdf",
|
|
// category: "계약서",
|
|
// size: "1.8 MB",
|
|
// uploadDate: "2024-11-15",
|
|
// url: "/documents/garage-lease-contract.pdf",
|
|
// },
|
|
// ];
|
|
|
|
interface DocumentWidgetProps {
|
|
element?: DashboardElement;
|
|
}
|
|
|
|
export default function DocumentWidget({ element }: DocumentWidgetProps) {
|
|
// TODO: 실제 API 연동 필요
|
|
const [documents] = useState<Document[]>([]);
|
|
const [filter, setFilter] = useState<"all" | Document["category"]>("all");
|
|
const [searchTerm, setSearchTerm] = useState("");
|
|
|
|
const filteredDocuments = documents.filter((doc) => {
|
|
const matchesFilter = filter === "all" || doc.category === filter;
|
|
const matchesSearch =
|
|
searchTerm === "" ||
|
|
doc.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
|
doc.description?.toLowerCase().includes(searchTerm.toLowerCase());
|
|
return matchesFilter && matchesSearch;
|
|
});
|
|
|
|
const getCategoryIcon = (category: Document["category"]) => {
|
|
switch (category) {
|
|
case "계약서":
|
|
return "📄";
|
|
case "보험":
|
|
return "🛡️";
|
|
case "세금계산서":
|
|
return "💰";
|
|
case "기타":
|
|
return "📁";
|
|
}
|
|
};
|
|
|
|
const getCategoryColor = (category: Document["category"]) => {
|
|
switch (category) {
|
|
case "계약서":
|
|
return "bg-primary/10 text-primary";
|
|
case "보험":
|
|
return "bg-success/10 text-success";
|
|
case "세금계산서":
|
|
return "bg-warning/10 text-warning";
|
|
case "기타":
|
|
return "bg-muted text-foreground";
|
|
}
|
|
};
|
|
|
|
const handleDownload = (doc: Document) => {
|
|
// 실제로는 백엔드 API 호출
|
|
alert(`다운로드: ${doc.name}\n(실제 구현 시 파일 다운로드 처리)`);
|
|
};
|
|
|
|
const stats = {
|
|
total: documents.length,
|
|
contract: documents.filter((d) => d.category === "계약서").length,
|
|
insurance: documents.filter((d) => d.category === "보험").length,
|
|
tax: documents.filter((d) => d.category === "세금계산서").length,
|
|
};
|
|
|
|
return (
|
|
<div className="flex h-full flex-col bg-background">
|
|
{/* 헤더 */}
|
|
<div className="border-b border-border bg-background px-4 py-3">
|
|
<div className="mb-3 flex items-center justify-between">
|
|
<h3 className="text-lg font-bold text-foreground">{element?.customTitle || "문서 관리"}</h3>
|
|
<button className="rounded-lg bg-primary px-3 py-1.5 text-sm text-primary-foreground transition-colors hover:bg-primary/90">
|
|
+ 업로드
|
|
</button>
|
|
</div>
|
|
|
|
{/* 통계 */}
|
|
<div className="mb-3 grid grid-cols-4 gap-2 text-xs">
|
|
<div className="rounded bg-muted px-2 py-1.5 text-center">
|
|
<div className="font-bold text-foreground">{stats.total}</div>
|
|
<div className="text-foreground">전체</div>
|
|
</div>
|
|
<div className="rounded bg-primary/10 px-2 py-1.5 text-center">
|
|
<div className="font-bold text-primary">{stats.contract}</div>
|
|
<div className="text-primary">계약서</div>
|
|
</div>
|
|
<div className="rounded bg-success/10 px-2 py-1.5 text-center">
|
|
<div className="font-bold text-success">{stats.insurance}</div>
|
|
<div className="text-success">보험</div>
|
|
</div>
|
|
<div className="rounded bg-warning/10 px-2 py-1.5 text-center">
|
|
<div className="font-bold text-warning">{stats.tax}</div>
|
|
<div className="text-warning">계산서</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* 검색 */}
|
|
<div className="mb-3 relative">
|
|
<Search className="absolute left-3 top-2.5 h-4 w-4 text-muted-foreground" />
|
|
<input
|
|
type="text"
|
|
placeholder="문서명 검색..."
|
|
value={searchTerm}
|
|
onChange={(e) => setSearchTerm(e.target.value)}
|
|
className="w-full rounded border border-border py-2 pl-10 pr-3 text-sm focus:border-primary focus:outline-none"
|
|
/>
|
|
</div>
|
|
|
|
{/* 필터 */}
|
|
<div className="flex gap-2">
|
|
{(["all", "계약서", "보험", "세금계산서", "기타"] as const).map((f) => (
|
|
<button
|
|
key={f}
|
|
onClick={() => setFilter(f)}
|
|
className={`rounded px-3 py-1 text-xs font-medium transition-colors ${
|
|
filter === f ? "bg-primary text-primary-foreground" : "bg-muted text-foreground hover:bg-muted"
|
|
}`}
|
|
>
|
|
{f === "all" ? "전체" : f}
|
|
</button>
|
|
))}
|
|
</div>
|
|
</div>
|
|
|
|
{/* 문서 리스트 */}
|
|
<div className="flex-1 overflow-y-auto p-4">
|
|
{filteredDocuments.length === 0 ? (
|
|
<div className="flex h-full items-center justify-center text-muted-foreground">
|
|
<div className="text-center">
|
|
<div className="mb-2 text-4xl">📭</div>
|
|
<div>문서가 없습니다</div>
|
|
</div>
|
|
</div>
|
|
) : (
|
|
<div className="space-y-2">
|
|
{filteredDocuments.map((doc) => (
|
|
<div
|
|
key={doc.id}
|
|
className="group flex items-center gap-3 rounded-lg border border-border bg-background p-3 shadow-sm transition-all hover:border-primary hover:shadow-md"
|
|
>
|
|
{/* 아이콘 */}
|
|
<div className="flex h-12 w-12 flex-shrink-0 items-center justify-center rounded-lg bg-muted text-2xl">
|
|
{getCategoryIcon(doc.category)}
|
|
</div>
|
|
|
|
{/* 정보 */}
|
|
<div className="flex-1 min-w-0">
|
|
<div className="flex items-start justify-between gap-2">
|
|
<div className="flex-1 min-w-0">
|
|
<div className="truncate font-medium text-foreground">{doc.name}</div>
|
|
{doc.description && (
|
|
<div className="mt-0.5 truncate text-xs text-foreground">{doc.description}</div>
|
|
)}
|
|
<div className="mt-1 flex items-center gap-3 text-xs text-muted-foreground">
|
|
<span className={`rounded px-2 py-0.5 ${getCategoryColor(doc.category)}`}>
|
|
{doc.category}
|
|
</span>
|
|
<span className="flex items-center gap-1">
|
|
<Calendar className="h-3 w-3" />
|
|
{new Date(doc.uploadDate).toLocaleDateString()}
|
|
</span>
|
|
<span>{doc.size}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* 다운로드 버튼 */}
|
|
<button
|
|
onClick={() => handleDownload(doc)}
|
|
className="flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-lg bg-primary text-primary-foreground transition-colors hover:bg-primary/90"
|
|
title="다운로드"
|
|
>
|
|
<Download className="h-4 w-4" />
|
|
</button>
|
|
</div>
|
|
))}
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|