사이드바 방식으로 변경
This commit is contained in:
@@ -21,109 +21,109 @@ interface StatusConfig {
|
||||
// 영어 상태명 → 한글 자동 변환
|
||||
const statusTranslations: { [key: string]: string } = {
|
||||
// 배송 관련
|
||||
"delayed": "지연",
|
||||
"pickup_waiting": "픽업 대기",
|
||||
"in_transit": "배송 중",
|
||||
"delivered": "배송완료",
|
||||
"pending": "대기중",
|
||||
"processing": "처리중",
|
||||
"completed": "완료",
|
||||
"cancelled": "취소됨",
|
||||
"failed": "실패",
|
||||
|
||||
delayed: "지연",
|
||||
pickup_waiting: "픽업 대기",
|
||||
in_transit: "배송 중",
|
||||
delivered: "배송완료",
|
||||
pending: "대기중",
|
||||
processing: "처리중",
|
||||
completed: "완료",
|
||||
cancelled: "취소됨",
|
||||
failed: "실패",
|
||||
|
||||
// 일반 상태
|
||||
"active": "활성",
|
||||
"inactive": "비활성",
|
||||
"enabled": "사용중",
|
||||
"disabled": "사용안함",
|
||||
"online": "온라인",
|
||||
"offline": "오프라인",
|
||||
"available": "사용가능",
|
||||
"unavailable": "사용불가",
|
||||
|
||||
active: "활성",
|
||||
inactive: "비활성",
|
||||
enabled: "사용중",
|
||||
disabled: "사용안함",
|
||||
online: "온라인",
|
||||
offline: "오프라인",
|
||||
available: "사용가능",
|
||||
unavailable: "사용불가",
|
||||
|
||||
// 승인 관련
|
||||
"approved": "승인됨",
|
||||
"rejected": "거절됨",
|
||||
"waiting": "대기중",
|
||||
|
||||
approved: "승인됨",
|
||||
rejected: "거절됨",
|
||||
waiting: "대기중",
|
||||
|
||||
// 차량 관련
|
||||
"driving": "운행중",
|
||||
"parked": "주차",
|
||||
"maintenance": "정비중",
|
||||
|
||||
driving: "운행중",
|
||||
parked: "주차",
|
||||
maintenance: "정비중",
|
||||
|
||||
// 기사 관련 (존중하는 표현)
|
||||
"waiting": "대기중",
|
||||
"resting": "휴식중",
|
||||
"unavailable": "운행불가",
|
||||
|
||||
waiting: "대기중",
|
||||
resting: "휴식중",
|
||||
unavailable: "운행불가",
|
||||
|
||||
// 기사 평가
|
||||
"excellent": "우수",
|
||||
"good": "양호",
|
||||
"average": "보통",
|
||||
"poor": "미흡",
|
||||
|
||||
excellent: "우수",
|
||||
good: "양호",
|
||||
average: "보통",
|
||||
poor: "미흡",
|
||||
|
||||
// 기사 경력
|
||||
"veteran": "베테랑",
|
||||
"experienced": "숙련",
|
||||
"intermediate": "중급",
|
||||
"beginner": "초급",
|
||||
veteran: "베테랑",
|
||||
experienced: "숙련",
|
||||
intermediate: "중급",
|
||||
beginner: "초급",
|
||||
};
|
||||
|
||||
// 영어 테이블명 → 한글 자동 변환
|
||||
const tableTranslations: { [key: string]: string } = {
|
||||
// 배송/물류 관련
|
||||
"deliveries": "배송",
|
||||
"delivery": "배송",
|
||||
"shipments": "출하",
|
||||
"shipment": "출하",
|
||||
"orders": "주문",
|
||||
"order": "주문",
|
||||
"cargo": "화물",
|
||||
"cargos": "화물",
|
||||
"packages": "소포",
|
||||
"package": "소포",
|
||||
|
||||
deliveries: "배송",
|
||||
delivery: "배송",
|
||||
shipments: "출하",
|
||||
shipment: "출하",
|
||||
orders: "주문",
|
||||
order: "주문",
|
||||
cargo: "화물",
|
||||
cargos: "화물",
|
||||
packages: "소포",
|
||||
package: "소포",
|
||||
|
||||
// 차량 관련
|
||||
"vehicles": "차량",
|
||||
"vehicle": "차량",
|
||||
"vehicle_locations": "차량위치",
|
||||
"vehicle_status": "차량상태",
|
||||
"drivers": "기사",
|
||||
"driver": "기사",
|
||||
|
||||
vehicles: "차량",
|
||||
vehicle: "차량",
|
||||
vehicle_locations: "차량위치",
|
||||
vehicle_status: "차량상태",
|
||||
drivers: "기사",
|
||||
driver: "기사",
|
||||
|
||||
// 사용자/고객 관련
|
||||
"users": "사용자",
|
||||
"user": "사용자",
|
||||
"customers": "고객",
|
||||
"customer": "고객",
|
||||
"members": "회원",
|
||||
"member": "회원",
|
||||
|
||||
users: "사용자",
|
||||
user: "사용자",
|
||||
customers: "고객",
|
||||
customer: "고객",
|
||||
members: "회원",
|
||||
member: "회원",
|
||||
|
||||
// 제품/재고 관련
|
||||
"products": "제품",
|
||||
"product": "제품",
|
||||
"items": "항목",
|
||||
"item": "항목",
|
||||
"inventory": "재고",
|
||||
"stock": "재고",
|
||||
|
||||
products: "제품",
|
||||
product: "제품",
|
||||
items: "항목",
|
||||
item: "항목",
|
||||
inventory: "재고",
|
||||
stock: "재고",
|
||||
|
||||
// 업무 관련
|
||||
"tasks": "작업",
|
||||
"task": "작업",
|
||||
"projects": "프로젝트",
|
||||
"project": "프로젝트",
|
||||
"issues": "이슈",
|
||||
"issue": "이슈",
|
||||
"tickets": "티켓",
|
||||
"ticket": "티켓",
|
||||
|
||||
tasks: "작업",
|
||||
task: "작업",
|
||||
projects: "프로젝트",
|
||||
project: "프로젝트",
|
||||
issues: "이슈",
|
||||
issue: "이슈",
|
||||
tickets: "티켓",
|
||||
ticket: "티켓",
|
||||
|
||||
// 기타
|
||||
"logs": "로그",
|
||||
"log": "로그",
|
||||
"reports": "리포트",
|
||||
"report": "리포트",
|
||||
"alerts": "알림",
|
||||
"alert": "알림",
|
||||
logs: "로그",
|
||||
log: "로그",
|
||||
reports: "리포트",
|
||||
report: "리포트",
|
||||
alerts: "알림",
|
||||
alert: "알림",
|
||||
};
|
||||
|
||||
interface StatusData {
|
||||
@@ -136,12 +136,12 @@ interface StatusData {
|
||||
* - 쿼리 결과를 상태별로 카운트해서 카드로 표시
|
||||
* - 색상과 라벨은 statusConfig로 커스터마이징 가능
|
||||
*/
|
||||
export default function StatusSummaryWidget({
|
||||
element,
|
||||
export default function StatusSummaryWidget({
|
||||
element,
|
||||
title = "상태 요약",
|
||||
icon = "📊",
|
||||
bgGradient = "from-slate-50 to-blue-50",
|
||||
statusConfig
|
||||
statusConfig,
|
||||
}: StatusSummaryWidgetProps) {
|
||||
const [statusData, setStatusData] = useState<StatusData[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
@@ -150,7 +150,7 @@ export default function StatusSummaryWidget({
|
||||
|
||||
useEffect(() => {
|
||||
loadData();
|
||||
|
||||
|
||||
// 자동 새로고침 (30초마다)
|
||||
const interval = setInterval(loadData, 30000);
|
||||
return () => clearInterval(interval);
|
||||
@@ -178,7 +178,7 @@ export default function StatusSummaryWidget({
|
||||
setLoading(true);
|
||||
const extractedTableName = extractTableName(element.dataSource.query);
|
||||
setTableName(extractedTableName);
|
||||
|
||||
|
||||
const token = localStorage.getItem("authToken");
|
||||
const response = await fetch("/api/dashboards/execute-query", {
|
||||
method: "POST",
|
||||
@@ -196,17 +196,17 @@ export default function StatusSummaryWidget({
|
||||
if (!response.ok) throw new Error("데이터 로딩 실패");
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
|
||||
// 데이터 처리
|
||||
if (result.success && result.data?.rows) {
|
||||
const rows = result.data.rows;
|
||||
|
||||
|
||||
// 상태별 카운트 계산
|
||||
const statusCounts: { [key: string]: number } = {};
|
||||
|
||||
|
||||
// GROUP BY 형식인지 확인
|
||||
const isGroupedData = rows.length > 0 && rows[0].count !== undefined;
|
||||
|
||||
|
||||
if (isGroupedData) {
|
||||
// GROUP BY 형식: SELECT status, COUNT(*) as count
|
||||
rows.forEach((row: any) => {
|
||||
@@ -244,7 +244,7 @@ export default function StatusSummaryWidget({
|
||||
|
||||
setStatusData(formattedData);
|
||||
}
|
||||
|
||||
|
||||
setError(null);
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : "데이터 로딩 실패");
|
||||
@@ -320,7 +320,6 @@ export default function StatusSummaryWidget({
|
||||
</div>
|
||||
<div className="mt-2 rounded-lg bg-blue-50 p-2 text-[10px] text-blue-700">
|
||||
<p className="font-medium">⚙️ 설정 방법</p>
|
||||
<p className="mt-0.5">우측 상단 톱니바퀴 버튼을 클릭하여</p>
|
||||
<p>SQL 쿼리를 입력하고 저장하세요</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -341,7 +340,7 @@ export default function StatusSummaryWidget({
|
||||
return tableTranslations[name.toLowerCase()];
|
||||
}
|
||||
// 언더스코어 제거하고 매칭 시도
|
||||
const nameWithoutUnderscore = name.replace(/_/g, '');
|
||||
const nameWithoutUnderscore = name.replace(/_/g, "");
|
||||
if (tableTranslations[nameWithoutUnderscore.toLowerCase()]) {
|
||||
return tableTranslations[nameWithoutUnderscore.toLowerCase()];
|
||||
}
|
||||
@@ -357,7 +356,9 @@ export default function StatusSummaryWidget({
|
||||
{/* 헤더 */}
|
||||
<div className="mb-2 flex flex-shrink-0 items-center justify-between">
|
||||
<div className="flex-1">
|
||||
<h3 className="text-sm font-bold text-gray-900">{icon} {displayTitle}</h3>
|
||||
<h3 className="text-sm font-bold text-gray-900">
|
||||
{icon} {displayTitle}
|
||||
</h3>
|
||||
{totalCount > 0 ? (
|
||||
<p className="text-xs text-gray-500">총 {totalCount.toLocaleString()}건</p>
|
||||
) : (
|
||||
@@ -366,7 +367,7 @@ export default function StatusSummaryWidget({
|
||||
</div>
|
||||
<button
|
||||
onClick={loadData}
|
||||
className="flex h-7 w-7 items-center justify-center rounded border border-border bg-white p-0 text-xs hover:bg-accent disabled:opacity-50"
|
||||
className="border-border hover:bg-accent flex h-7 w-7 items-center justify-center rounded border bg-white p-0 text-xs disabled:opacity-50"
|
||||
disabled={loading}
|
||||
>
|
||||
{loading ? "⏳" : "🔄"}
|
||||
@@ -380,10 +381,7 @@ export default function StatusSummaryWidget({
|
||||
{statusData.map((item) => {
|
||||
const colors = getColorClasses(item.status);
|
||||
return (
|
||||
<div
|
||||
key={item.status}
|
||||
className="rounded border border-gray-200 bg-white p-1.5 shadow-sm"
|
||||
>
|
||||
<div key={item.status} className="rounded border border-gray-200 bg-white p-1.5 shadow-sm">
|
||||
<div className="mb-0.5 flex items-center gap-1">
|
||||
<div className={`h-1.5 w-1.5 rounded-full ${colors.dot}`}></div>
|
||||
<div className="text-xs font-medium text-gray-600">{item.status}</div>
|
||||
@@ -397,4 +395,3 @@ export default function StatusSummaryWidget({
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user