차량위치 위젯 기존꺼 분할 완료
This commit is contained in:
@@ -27,40 +27,62 @@ interface CustomerIssue {
|
||||
}
|
||||
|
||||
interface DeliveryStatusWidgetProps {
|
||||
element?: any; // 대시보드 요소 (dataSource 포함)
|
||||
refreshInterval?: number;
|
||||
}
|
||||
|
||||
export default function DeliveryStatusWidget({ refreshInterval = 60000 }: DeliveryStatusWidgetProps) {
|
||||
export default function DeliveryStatusWidget({ element, refreshInterval = 60000 }: DeliveryStatusWidgetProps) {
|
||||
const [deliveries, setDeliveries] = useState<DeliveryItem[]>([]);
|
||||
const [issues, setIssues] = useState<CustomerIssue[]>([]);
|
||||
const [todayStats, setTodayStats] = useState({
|
||||
shipped: 0,
|
||||
delivered: 0,
|
||||
});
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [lastUpdate, setLastUpdate] = useState<Date>(new Date());
|
||||
const [selectedStatus, setSelectedStatus] = useState<string>("all"); // 필터 상태 추가
|
||||
|
||||
const loadData = async () => {
|
||||
setIsLoading(true);
|
||||
|
||||
// TODO: 실제 API 연동 시 아래 주석 해제
|
||||
// try {
|
||||
// const response = await fetch('/api/delivery/status', {
|
||||
// headers: {
|
||||
// 'Authorization': `Bearer ${localStorage.getItem('authToken')}`,
|
||||
// },
|
||||
// });
|
||||
// const data = await response.json();
|
||||
// setDeliveries(data.deliveries);
|
||||
// setIssues(data.issues);
|
||||
// setTodayStats(data.todayStats);
|
||||
// setLastUpdate(new Date());
|
||||
// } catch (error) {
|
||||
// console.error('배송 데이터 로드 실패:', error);
|
||||
// } finally {
|
||||
// setIsLoading(false);
|
||||
// }
|
||||
// 설정된 쿼리가 없으면 로딩 중단 (더미 데이터 사용 안 함)
|
||||
if (!element?.dataSource?.query) {
|
||||
setIsLoading(false);
|
||||
setDeliveries([]);
|
||||
setIssues([]);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch("/api/dashboards/execute-query", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${typeof window !== "undefined" ? localStorage.getItem("authToken") || "" : ""}`,
|
||||
},
|
||||
body: JSON.stringify({ query: element.dataSource.query }),
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const result = await response.json();
|
||||
if (result.success && result.data.rows.length > 0) {
|
||||
// TODO: DB 데이터를 DeliveryItem 형식으로 변환
|
||||
setDeliveries(result.data.rows);
|
||||
setLastUpdate(new Date());
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("배송 데이터 로드 실패:", error);
|
||||
}
|
||||
|
||||
setIsLoading(false);
|
||||
};
|
||||
|
||||
// 데이터 로드 및 자동 새로고침
|
||||
useEffect(() => {
|
||||
loadData();
|
||||
const interval = setInterval(loadData, refreshInterval);
|
||||
return () => clearInterval(interval);
|
||||
}, [element?.dataSource?.query, refreshInterval]);
|
||||
|
||||
// 더미 데이터 완전히 제거 (아래 코드 삭제)
|
||||
/*
|
||||
// 가상 배송 데이터 (개발용 - 실제 DB 연동 시 삭제)
|
||||
const dummyDeliveries: DeliveryItem[] = [
|
||||
{
|
||||
@@ -148,23 +170,7 @@ export default function DeliveryStatusWidget({ refreshInterval = 60000 }: Delive
|
||||
},
|
||||
];
|
||||
|
||||
setTimeout(() => {
|
||||
setDeliveries(dummyDeliveries);
|
||||
setIssues(dummyIssues);
|
||||
setTodayStats({
|
||||
shipped: 24,
|
||||
delivered: 18,
|
||||
});
|
||||
setLastUpdate(new Date());
|
||||
setIsLoading(false);
|
||||
}, 500);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
loadData();
|
||||
const interval = setInterval(loadData, refreshInterval);
|
||||
return () => clearInterval(interval);
|
||||
}, [refreshInterval]);
|
||||
*/
|
||||
|
||||
const getStatusColor = (status: DeliveryItem["status"]) => {
|
||||
switch (status) {
|
||||
@@ -259,7 +265,32 @@ export default function DeliveryStatusWidget({ refreshInterval = 60000 }: Delive
|
||||
pickup_waiting: deliveries.filter((d) => d.status === "pickup_waiting").length,
|
||||
};
|
||||
|
||||
const delayedDeliveries = deliveries.filter((d) => d.status === "delayed");
|
||||
// 필터링된 배송 목록
|
||||
const filteredDeliveries = selectedStatus === "all"
|
||||
? deliveries
|
||||
: deliveries.filter((d) => d.status === selectedStatus);
|
||||
|
||||
// 오늘 통계 계산
|
||||
const today = new Date();
|
||||
today.setHours(0, 0, 0, 0);
|
||||
|
||||
const todayStats = {
|
||||
// 오늘 발송 건수 (created_at이 오늘인 것)
|
||||
shipped: deliveries.filter((d: any) => {
|
||||
if (!d.created_at) return false;
|
||||
const createdDate = new Date(d.created_at);
|
||||
createdDate.setHours(0, 0, 0, 0);
|
||||
return createdDate.getTime() === today.getTime();
|
||||
}).length,
|
||||
// 오늘 도착 건수 (status가 delivered이고 estimated_delivery가 오늘인 것)
|
||||
delivered: deliveries.filter((d: any) => {
|
||||
if (d.status !== "delivered" && d.status !== "delivered") return false;
|
||||
if (!d.estimated_delivery && !d.estimatedDelivery) return false;
|
||||
const deliveredDate = new Date(d.estimated_delivery || d.estimatedDelivery);
|
||||
deliveredDate.setHours(0, 0, 0, 0);
|
||||
return deliveredDate.getTime() === today.getTime();
|
||||
}).length,
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="h-full w-full bg-gradient-to-br from-slate-50 to-blue-50 p-4 overflow-auto">
|
||||
@@ -284,24 +315,63 @@ export default function DeliveryStatusWidget({ refreshInterval = 60000 }: Delive
|
||||
|
||||
{/* 배송 상태 요약 */}
|
||||
<div className="mb-3">
|
||||
<h4 className="mb-2 text-sm font-semibold text-gray-700">배송 상태 요약</h4>
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-2">
|
||||
<div className="rounded-lg bg-white p-1.5 shadow-sm border-l-4 border-blue-500">
|
||||
<h4 className="mb-2 text-sm font-semibold text-gray-700">배송 상태 요약 (클릭하여 필터링)</h4>
|
||||
<div className="grid grid-cols-2 md:grid-cols-5 gap-2">
|
||||
<button
|
||||
onClick={() => setSelectedStatus("all")}
|
||||
className={`rounded-lg p-1.5 shadow-sm border-l-4 transition-all ${
|
||||
selectedStatus === "all"
|
||||
? "border-gray-900 bg-gray-100 ring-2 ring-gray-900"
|
||||
: "border-gray-500 bg-white hover:bg-gray-50"
|
||||
}`}
|
||||
>
|
||||
<div className="text-xs text-gray-600 mb-0.5">전체</div>
|
||||
<div className="text-lg font-bold text-gray-900">{deliveries.length}</div>
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setSelectedStatus("in_transit")}
|
||||
className={`rounded-lg p-1.5 shadow-sm border-l-4 transition-all ${
|
||||
selectedStatus === "in_transit"
|
||||
? "border-blue-900 bg-blue-100 ring-2 ring-blue-900"
|
||||
: "border-blue-500 bg-white hover:bg-blue-50"
|
||||
}`}
|
||||
>
|
||||
<div className="text-xs text-gray-600 mb-0.5">배송중</div>
|
||||
<div className="text-lg font-bold text-blue-600">{statusStats.in_transit}</div>
|
||||
</div>
|
||||
<div className="rounded-lg bg-white p-1.5 shadow-sm border-l-4 border-green-500">
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setSelectedStatus("delivered")}
|
||||
className={`rounded-lg p-1.5 shadow-sm border-l-4 transition-all ${
|
||||
selectedStatus === "delivered"
|
||||
? "border-green-900 bg-green-100 ring-2 ring-green-900"
|
||||
: "border-green-500 bg-white hover:bg-green-50"
|
||||
}`}
|
||||
>
|
||||
<div className="text-xs text-gray-600 mb-0.5">완료</div>
|
||||
<div className="text-lg font-bold text-green-600">{statusStats.delivered}</div>
|
||||
</div>
|
||||
<div className="rounded-lg bg-white p-1.5 shadow-sm border-l-4 border-red-500">
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setSelectedStatus("delayed")}
|
||||
className={`rounded-lg p-1.5 shadow-sm border-l-4 transition-all ${
|
||||
selectedStatus === "delayed"
|
||||
? "border-red-900 bg-red-100 ring-2 ring-red-900"
|
||||
: "border-red-500 bg-white hover:bg-red-50"
|
||||
}`}
|
||||
>
|
||||
<div className="text-xs text-gray-600 mb-0.5">지연</div>
|
||||
<div className="text-lg font-bold text-red-600">{statusStats.delayed}</div>
|
||||
</div>
|
||||
<div className="rounded-lg bg-white p-1.5 shadow-sm border-l-4 border-yellow-500">
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setSelectedStatus("pickup_waiting")}
|
||||
className={`rounded-lg p-1.5 shadow-sm border-l-4 transition-all ${
|
||||
selectedStatus === "pickup_waiting"
|
||||
? "border-yellow-900 bg-yellow-100 ring-2 ring-yellow-900"
|
||||
: "border-yellow-500 bg-white hover:bg-yellow-50"
|
||||
}`}
|
||||
>
|
||||
<div className="text-xs text-gray-600 mb-0.5">픽업 대기</div>
|
||||
<div className="text-lg font-bold text-yellow-600">{statusStats.pickup_waiting}</div>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -322,20 +392,24 @@ export default function DeliveryStatusWidget({ refreshInterval = 60000 }: Delive
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 지연 중인 화물 리스트 */}
|
||||
{/* 필터링된 화물 리스트 */}
|
||||
<div className="mb-3">
|
||||
<h4 className="mb-2 text-sm font-semibold text-gray-700 flex items-center gap-2">
|
||||
<AlertTriangle className="h-4 w-4 text-red-600" />
|
||||
지연 중인 화물 ({delayedDeliveries.length})
|
||||
<Package className="h-4 w-4 text-gray-600" />
|
||||
{selectedStatus === "all" && `전체 화물 (${filteredDeliveries.length})`}
|
||||
{selectedStatus === "in_transit" && `배송 중인 화물 (${filteredDeliveries.length})`}
|
||||
{selectedStatus === "delivered" && `배송 완료 (${filteredDeliveries.length})`}
|
||||
{selectedStatus === "delayed" && `지연 중인 화물 (${filteredDeliveries.length})`}
|
||||
{selectedStatus === "pickup_waiting" && `픽업 대기 (${filteredDeliveries.length})`}
|
||||
</h4>
|
||||
<div className="rounded-lg bg-white shadow-sm border border-gray-200 overflow-hidden">
|
||||
{delayedDeliveries.length === 0 ? (
|
||||
{filteredDeliveries.length === 0 ? (
|
||||
<div className="p-6 text-center text-sm text-gray-500">
|
||||
지연 중인 화물이 없습니다
|
||||
{selectedStatus === "all" ? "화물이 없습니다" : "해당 상태의 화물이 없습니다"}
|
||||
</div>
|
||||
) : (
|
||||
<div className="max-h-[200px] overflow-y-auto scrollbar-thin scrollbar-thumb-gray-300 scrollbar-track-gray-100">
|
||||
{delayedDeliveries.map((delivery) => (
|
||||
{filteredDeliveries.map((delivery) => (
|
||||
<div
|
||||
key={delivery.id}
|
||||
className="p-3 border-b border-gray-200 last:border-b-0 hover:bg-gray-50 transition-colors"
|
||||
|
||||
Reference in New Issue
Block a user