jskim-node #398
|
|
@ -10,6 +10,7 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@
|
||||||
import { Badge } from "@/components/ui/badge";
|
import { Badge } from "@/components/ui/badge";
|
||||||
import { ArrowLeft, Save, RefreshCw, ArrowRight, Trash2 } from "lucide-react";
|
import { ArrowLeft, Save, RefreshCw, ArrowRight, Trash2 } from "lucide-react";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
import { showErrorToast } from "@/lib/utils/toastUtils";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import {
|
import {
|
||||||
BatchAPI,
|
BatchAPI,
|
||||||
|
|
@ -133,7 +134,7 @@ export default function BatchCreatePage() {
|
||||||
setFromColumns(Array.isArray(columns) ? columns : []);
|
setFromColumns(Array.isArray(columns) ? columns : []);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("FROM 컬럼 목록 로드 실패:", error);
|
console.error("FROM 컬럼 목록 로드 실패:", error);
|
||||||
toast.error("컬럼 목록을 불러오는데 실패했습니다.");
|
showErrorToast("컬럼 목록을 불러오는 데 실패했습니다", error, { guidance: "테이블 정보를 확인해 주세요." });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -242,7 +243,7 @@ export default function BatchCreatePage() {
|
||||||
router.push("/admin/batchmng");
|
router.push("/admin/batchmng");
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("배치 설정 저장 실패:", error);
|
console.error("배치 설정 저장 실패:", error);
|
||||||
toast.error("배치 설정 저장에 실패했습니다.");
|
showErrorToast("배치 설정 저장에 실패했습니다", error, { guidance: "입력 데이터를 확인하고 다시 시도해 주세요." });
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import {
|
||||||
Database
|
Database
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
import { showErrorToast } from "@/lib/utils/toastUtils";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import {
|
import {
|
||||||
BatchAPI,
|
BatchAPI,
|
||||||
|
|
@ -75,7 +76,9 @@ export default function BatchManagementPage() {
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("배치 실행 실패:", error);
|
console.error("배치 실행 실패:", error);
|
||||||
toast.error("배치 실행 중 오류가 발생했습니다.");
|
showErrorToast("배치 실행에 실패했습니다", error, {
|
||||||
|
guidance: "배치 설정을 확인하고 다시 시도해 주세요.",
|
||||||
|
});
|
||||||
} finally {
|
} finally {
|
||||||
setExecutingBatch(null);
|
setExecutingBatch(null);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import { Badge } from "@/components/ui/badge";
|
||||||
import { Plus, Search, Edit, Trash2, TestTube, Filter } from "lucide-react";
|
import { Plus, Search, Edit, Trash2, TestTube, Filter } from "lucide-react";
|
||||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
|
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
import { showErrorToast } from "@/lib/utils/toastUtils";
|
||||||
import {
|
import {
|
||||||
ExternalCallConfigAPI,
|
ExternalCallConfigAPI,
|
||||||
ExternalCallConfig,
|
ExternalCallConfig,
|
||||||
|
|
@ -57,11 +58,15 @@ export default function ExternalCallConfigsPage() {
|
||||||
if (response.success) {
|
if (response.success) {
|
||||||
setConfigs(response.data || []);
|
setConfigs(response.data || []);
|
||||||
} else {
|
} else {
|
||||||
toast.error(response.message || "외부 호출 설정 조회 실패");
|
showErrorToast("외부 호출 설정 조회에 실패했습니다", response.message, {
|
||||||
|
guidance: "네트워크 연결을 확인하고 다시 시도해 주세요.",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("외부 호출 설정 조회 오류:", error);
|
console.error("외부 호출 설정 조회 오류:", error);
|
||||||
toast.error("외부 호출 설정 조회 중 오류가 발생했습니다.");
|
showErrorToast("외부 호출 설정 조회에 실패했습니다", error, {
|
||||||
|
guidance: "네트워크 연결을 확인하고 다시 시도해 주세요.",
|
||||||
|
});
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
|
|
@ -113,11 +118,15 @@ export default function ExternalCallConfigsPage() {
|
||||||
toast.success("외부 호출 설정이 삭제되었습니다.");
|
toast.success("외부 호출 설정이 삭제되었습니다.");
|
||||||
fetchConfigs();
|
fetchConfigs();
|
||||||
} else {
|
} else {
|
||||||
toast.error(response.message || "외부 호출 설정 삭제 실패");
|
showErrorToast("외부 호출 설정 삭제에 실패했습니다", response.message, {
|
||||||
|
guidance: "잠시 후 다시 시도해 주세요.",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("외부 호출 설정 삭제 오류:", error);
|
console.error("외부 호출 설정 삭제 오류:", error);
|
||||||
toast.error("외부 호출 설정 삭제 중 오류가 발생했습니다.");
|
showErrorToast("외부 호출 설정 삭제에 실패했습니다", error, {
|
||||||
|
guidance: "잠시 후 다시 시도해 주세요.",
|
||||||
|
});
|
||||||
} finally {
|
} finally {
|
||||||
setDeleteDialogOpen(false);
|
setDeleteDialogOpen(false);
|
||||||
setConfigToDelete(null);
|
setConfigToDelete(null);
|
||||||
|
|
@ -138,7 +147,9 @@ export default function ExternalCallConfigsPage() {
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("외부 호출 설정 테스트 오류:", error);
|
console.error("외부 호출 설정 테스트 오류:", error);
|
||||||
toast.error("외부 호출 설정 테스트 중 오류가 발생했습니다.");
|
showErrorToast("외부 호출 테스트 실행에 실패했습니다", error, {
|
||||||
|
guidance: "URL과 설정을 확인해 주세요.",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import { Textarea } from "@/components/ui/textarea";
|
||||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||||
import { Trash2, Plus, ArrowLeft, Save, RefreshCw, Globe, Database, Eye } from "lucide-react";
|
import { Trash2, Plus, ArrowLeft, Save, RefreshCw, Globe, Database, Eye } from "lucide-react";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
import { showErrorToast } from "@/lib/utils/toastUtils";
|
||||||
import { BatchManagementAPI } from "@/lib/api/batchManagement";
|
import { BatchManagementAPI } from "@/lib/api/batchManagement";
|
||||||
|
|
||||||
// 타입 정의
|
// 타입 정의
|
||||||
|
|
@ -469,7 +470,9 @@ export default function BatchManagementNewPage() {
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("배치 저장 오류:", error);
|
console.error("배치 저장 오류:", error);
|
||||||
toast.error("배치 저장 중 오류가 발생했습니다.");
|
showErrorToast("배치 설정 저장에 실패했습니다", error, {
|
||||||
|
guidance: "입력 데이터를 확인하고 다시 시도해 주세요.",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
} else if (batchType === "db-to-restapi") {
|
} else if (batchType === "db-to-restapi") {
|
||||||
|
|
@ -558,7 +561,9 @@ export default function BatchManagementNewPage() {
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("배치 저장 오류:", error);
|
console.error("배치 저장 오류:", error);
|
||||||
toast.error("배치 저장 중 오류가 발생했습니다.");
|
showErrorToast("배치 설정 저장에 실패했습니다", error, {
|
||||||
|
guidance: "입력 데이터를 확인하고 다시 시도해 주세요.",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -75,7 +75,7 @@ export default function BatchManagementPage() {
|
||||||
setJobs(data);
|
setJobs(data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("배치 작업 목록 조회 오류:", error);
|
console.error("배치 작업 목록 조회 오류:", error);
|
||||||
toast.error("배치 작업 목록을 불러오는데 실패했습니다.");
|
showErrorToast("배치 작업 목록을 불러오는 데 실패했습니다", error, { guidance: "네트워크 연결을 확인해 주세요." });
|
||||||
} finally {
|
} finally {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
}
|
}
|
||||||
|
|
@ -150,7 +150,7 @@ export default function BatchManagementPage() {
|
||||||
loadJobs();
|
loadJobs();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("배치 작업 삭제 오류:", error);
|
console.error("배치 작업 삭제 오류:", error);
|
||||||
toast.error("배치 작업 삭제에 실패했습니다.");
|
showErrorToast("배치 작업 삭제에 실패했습니다", error, { guidance: "잠시 후 다시 시도해 주세요." });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -160,7 +160,7 @@ export default function BatchManagementPage() {
|
||||||
toast.success(`"${job.job_name}" 배치 작업을 실행했습니다.`);
|
toast.success(`"${job.job_name}" 배치 작업을 실행했습니다.`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("배치 작업 실행 오류:", error);
|
console.error("배치 작업 실행 오류:", error);
|
||||||
toast.error("배치 작업 실행에 실패했습니다.");
|
showErrorToast("배치 작업 실행에 실패했습니다", error, { guidance: "배치 설정을 확인해 주세요." });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,7 @@ import {
|
||||||
GripVertical,
|
GripVertical,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
import { showErrorToast } from "@/lib/utils/toastUtils";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { cascadingAutoFillApi, AutoFillGroup, AutoFillMapping } from "@/lib/api/cascadingAutoFill";
|
import { cascadingAutoFillApi, AutoFillGroup, AutoFillMapping } from "@/lib/api/cascadingAutoFill";
|
||||||
import { tableManagementApi } from "@/lib/api/tableManagement";
|
import { tableManagementApi } from "@/lib/api/tableManagement";
|
||||||
|
|
@ -97,7 +98,7 @@ export default function AutoFillTab() {
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("그룹 목록 로드 실패:", error);
|
console.error("그룹 목록 로드 실패:", error);
|
||||||
toast.error("그룹 목록을 불러오는데 실패했습니다.");
|
showErrorToast("그룹 목록을 불러오는 데 실패했습니다", error, { guidance: "네트워크 연결을 확인해 주세요." });
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
|
|
@ -269,7 +270,7 @@ export default function AutoFillTab() {
|
||||||
toast.error(response.error || "저장에 실패했습니다.");
|
toast.error(response.error || "저장에 실패했습니다.");
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
toast.error("저장 중 오류가 발생했습니다.");
|
showErrorToast("자동입력 설정 저장에 실패했습니다", error, { guidance: "입력 데이터를 확인하고 다시 시도해 주세요." });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ import {
|
||||||
Loader2,
|
Loader2,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
import { showErrorToast } from "@/lib/utils/toastUtils";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { cascadingRelationApi, CascadingRelation, CascadingRelationCreateInput } from "@/lib/api/cascadingRelation";
|
import { cascadingRelationApi, CascadingRelation, CascadingRelationCreateInput } from "@/lib/api/cascadingRelation";
|
||||||
import { tableManagementApi } from "@/lib/api/tableManagement";
|
import { tableManagementApi } from "@/lib/api/tableManagement";
|
||||||
|
|
@ -102,7 +103,7 @@ export default function CascadingRelationsTab() {
|
||||||
setRelations(response.data);
|
setRelations(response.data);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
toast.error("연쇄 관계 목록 조회에 실패했습니다.");
|
showErrorToast("연쇄 관계 목록을 불러오는 데 실패했습니다", error, { guidance: "네트워크 연결을 확인해 주세요." });
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
|
|
@ -431,7 +432,7 @@ export default function CascadingRelationsTab() {
|
||||||
toast.error(response.message || "저장에 실패했습니다.");
|
toast.error(response.message || "저장에 실패했습니다.");
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
toast.error("저장 중 오류가 발생했습니다.");
|
showErrorToast("연쇄 관계 저장에 실패했습니다", error, { guidance: "입력 데이터를 확인하고 다시 시도해 주세요." });
|
||||||
} finally {
|
} finally {
|
||||||
setSaving(false);
|
setSaving(false);
|
||||||
}
|
}
|
||||||
|
|
@ -452,7 +453,7 @@ export default function CascadingRelationsTab() {
|
||||||
toast.error(response.message || "삭제에 실패했습니다.");
|
toast.error(response.message || "삭제에 실패했습니다.");
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
toast.error("삭제 중 오류가 발생했습니다.");
|
showErrorToast("연쇄 관계 삭제에 실패했습니다", error, { guidance: "잠시 후 다시 시도해 주세요." });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,7 @@ import {
|
||||||
TableRow,
|
TableRow,
|
||||||
} from "@/components/ui/table";
|
} from "@/components/ui/table";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
import { showErrorToast } from "@/lib/utils/toastUtils";
|
||||||
import {
|
import {
|
||||||
cascadingConditionApi,
|
cascadingConditionApi,
|
||||||
CascadingCondition,
|
CascadingCondition,
|
||||||
|
|
@ -170,7 +171,7 @@ export default function ConditionTab() {
|
||||||
toast.error(response.error || "삭제에 실패했습니다.");
|
toast.error(response.error || "삭제에 실패했습니다.");
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
toast.error("삭제 중 오류가 발생했습니다.");
|
showErrorToast("조건 삭제에 실패했습니다", error, { guidance: "잠시 후 다시 시도해 주세요." });
|
||||||
} finally {
|
} finally {
|
||||||
setIsDeleteDialogOpen(false);
|
setIsDeleteDialogOpen(false);
|
||||||
setDeletingConditionId(null);
|
setDeletingConditionId(null);
|
||||||
|
|
@ -206,7 +207,7 @@ export default function ConditionTab() {
|
||||||
toast.error(response.error || "저장에 실패했습니다.");
|
toast.error(response.error || "저장에 실패했습니다.");
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
toast.error("저장 중 오류가 발생했습니다.");
|
showErrorToast("조건 저장에 실패했습니다", error, { guidance: "입력 데이터를 확인하고 다시 시도해 주세요." });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ import {
|
||||||
} from "@/components/ui/card";
|
} from "@/components/ui/card";
|
||||||
import { Plus, Pencil, Trash2, Database, RefreshCw, Layers } from "lucide-react";
|
import { Plus, Pencil, Trash2, Database, RefreshCw, Layers } from "lucide-react";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
import { showErrorToast } from "@/lib/utils/toastUtils";
|
||||||
import { LoadingSpinner } from "@/components/common/LoadingSpinner";
|
import { LoadingSpinner } from "@/components/common/LoadingSpinner";
|
||||||
import {
|
import {
|
||||||
hierarchyColumnApi,
|
hierarchyColumnApi,
|
||||||
|
|
@ -300,7 +301,7 @@ export default function HierarchyColumnTab() {
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("저장 에러:", error);
|
console.error("저장 에러:", error);
|
||||||
toast.error("저장 중 오류가 발생했습니다.");
|
showErrorToast("계층구조 설정 저장에 실패했습니다", error, { guidance: "입력 데이터를 확인하고 다시 시도해 주세요." });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -319,7 +320,7 @@ export default function HierarchyColumnTab() {
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("삭제 에러:", error);
|
console.error("삭제 에러:", error);
|
||||||
toast.error("삭제 중 오류가 발생했습니다.");
|
showErrorToast("계층구조 설정 삭제에 실패했습니다", error, { guidance: "잠시 후 다시 시도해 주세요." });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/com
|
||||||
import { Switch } from "@/components/ui/switch";
|
import { Switch } from "@/components/ui/switch";
|
||||||
import { Badge } from "@/components/ui/badge";
|
import { Badge } from "@/components/ui/badge";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
import { showErrorToast } from "@/lib/utils/toastUtils";
|
||||||
import { ArrowLeft, Save, RotateCcw, Eye } from "lucide-react";
|
import { ArrowLeft, Save, RotateCcw, Eye } from "lucide-react";
|
||||||
import { useWebTypes, type WebTypeFormData } from "@/hooks/admin/useWebTypes";
|
import { useWebTypes, type WebTypeFormData } from "@/hooks/admin/useWebTypes";
|
||||||
import { AVAILABLE_COMPONENTS, getComponentInfo } from "@/lib/utils/availableComponents";
|
import { AVAILABLE_COMPONENTS, getComponentInfo } from "@/lib/utils/availableComponents";
|
||||||
|
|
@ -148,7 +149,7 @@ export default function EditWebTypePage() {
|
||||||
toast.success("웹타입이 성공적으로 수정되었습니다.");
|
toast.success("웹타입이 성공적으로 수정되었습니다.");
|
||||||
router.push(`/admin/standards/${webType}`);
|
router.push(`/admin/standards/${webType}`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
toast.error(error instanceof Error ? error.message : "수정 중 오류가 발생했습니다.");
|
showErrorToast("웹타입 수정에 실패했습니다", error, { guidance: "입력 데이터를 확인하고 다시 시도해 주세요." });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ import {
|
||||||
AlertDialogTrigger,
|
AlertDialogTrigger,
|
||||||
} from "@/components/ui/alert-dialog";
|
} from "@/components/ui/alert-dialog";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
import { showErrorToast } from "@/lib/utils/toastUtils";
|
||||||
import { Plus, Search, Edit, Trash2, Eye, Filter, RotateCcw, Settings, SortAsc, SortDesc } from "lucide-react";
|
import { Plus, Search, Edit, Trash2, Eye, Filter, RotateCcw, Settings, SortAsc, SortDesc } from "lucide-react";
|
||||||
import { useWebTypes } from "@/hooks/admin/useWebTypes";
|
import { useWebTypes } from "@/hooks/admin/useWebTypes";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
|
@ -90,7 +91,7 @@ export default function WebTypesManagePage() {
|
||||||
await deleteWebType(webType);
|
await deleteWebType(webType);
|
||||||
toast.success(`웹타입 '${typeName}'이 삭제되었습니다.`);
|
toast.success(`웹타입 '${typeName}'이 삭제되었습니다.`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
toast.error(error instanceof Error ? error.message : "삭제 중 오류가 발생했습니다.");
|
showErrorToast("웹타입 삭제에 실패했습니다", error, { guidance: "잠시 후 다시 시도해 주세요." });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,7 @@ import {
|
||||||
RefreshCw
|
RefreshCw
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
import { showErrorToast } from "@/lib/utils/toastUtils";
|
||||||
import { CollectionAPI, DataCollectionConfig } from "@/lib/api/collection";
|
import { CollectionAPI, DataCollectionConfig } from "@/lib/api/collection";
|
||||||
import CollectionConfigModal from "@/components/admin/CollectionConfigModal";
|
import CollectionConfigModal from "@/components/admin/CollectionConfigModal";
|
||||||
|
|
||||||
|
|
@ -69,7 +70,7 @@ export default function CollectionManagementPage() {
|
||||||
setConfigs(data);
|
setConfigs(data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("수집 설정 목록 조회 오류:", error);
|
console.error("수집 설정 목록 조회 오류:", error);
|
||||||
toast.error("수집 설정 목록을 불러오는데 실패했습니다.");
|
showErrorToast("수집 설정 목록을 불러오는 데 실패했습니다", error, { guidance: "네트워크 연결을 확인해 주세요." });
|
||||||
} finally {
|
} finally {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
}
|
}
|
||||||
|
|
@ -131,7 +132,7 @@ export default function CollectionManagementPage() {
|
||||||
toast.success(`"${config.config_name}" 수집 작업을 시작했습니다.`);
|
toast.success(`"${config.config_name}" 수집 작업을 시작했습니다.`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("수집 작업 실행 오류:", error);
|
console.error("수집 작업 실행 오류:", error);
|
||||||
toast.error("수집 작업 실행에 실패했습니다.");
|
showErrorToast("수집 작업 실행에 실패했습니다", error, { guidance: "수집 설정을 확인해 주세요." });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import DataFlowList from "@/components/dataflow/DataFlowList";
|
||||||
import { FlowEditor } from "@/components/dataflow/node-editor/FlowEditor";
|
import { FlowEditor } from "@/components/dataflow/node-editor/FlowEditor";
|
||||||
import { useAuth } from "@/hooks/useAuth";
|
import { useAuth } from "@/hooks/useAuth";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
import { showErrorToast } from "@/lib/utils/toastUtils";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { ScrollToTop } from "@/components/common/ScrollToTop";
|
import { ScrollToTop } from "@/components/common/ScrollToTop";
|
||||||
import { ArrowLeft } from "lucide-react";
|
import { ArrowLeft } from "lucide-react";
|
||||||
|
|
@ -35,7 +36,7 @@ export default function DataFlowPage() {
|
||||||
toast.success("플로우를 불러왔습니다.");
|
toast.success("플로우를 불러왔습니다.");
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error("❌ 플로우 불러오기 실패:", error);
|
console.error("❌ 플로우 불러오기 실패:", error);
|
||||||
toast.error(error.message || "플로우를 불러오는데 실패했습니다.");
|
showErrorToast("플로우 목록을 불러오는 데 실패했습니다", error, { guidance: "네트워크 연결을 확인해 주세요." });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ import {
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { LoadingSpinner } from "@/components/common/LoadingSpinner";
|
import { LoadingSpinner } from "@/components/common/LoadingSpinner";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
import { showErrorToast } from "@/lib/utils/toastUtils";
|
||||||
import { useMultiLang } from "@/hooks/useMultiLang";
|
import { useMultiLang } from "@/hooks/useMultiLang";
|
||||||
import { useAuth } from "@/hooks/useAuth";
|
import { useAuth } from "@/hooks/useAuth";
|
||||||
import { TABLE_MANAGEMENT_KEYS } from "@/constants/tableManagement";
|
import { TABLE_MANAGEMENT_KEYS } from "@/constants/tableManagement";
|
||||||
|
|
@ -331,11 +332,15 @@ export default function TableManagementPage() {
|
||||||
setTables(response.data.data);
|
setTables(response.data.data);
|
||||||
toast.success("테이블 목록을 성공적으로 로드했습니다.");
|
toast.success("테이블 목록을 성공적으로 로드했습니다.");
|
||||||
} else {
|
} else {
|
||||||
toast.error(response.data.message || "테이블 목록 로드에 실패했습니다.");
|
showErrorToast("테이블 목록을 불러오는 데 실패했습니다", response.data.message, {
|
||||||
|
guidance: "네트워크 연결을 확인해 주세요.",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// console.error("테이블 목록 로드 실패:", error);
|
// console.error("테이블 목록 로드 실패:", error);
|
||||||
toast.error("테이블 목록 로드 중 오류가 발생했습니다.");
|
showErrorToast("테이블 목록을 불러오는 데 실패했습니다", error, {
|
||||||
|
guidance: "네트워크 연결을 확인해 주세요.",
|
||||||
|
});
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
|
|
@ -403,11 +408,15 @@ export default function TableManagementPage() {
|
||||||
setTotalColumns(data.total || processedColumns.length);
|
setTotalColumns(data.total || processedColumns.length);
|
||||||
toast.success("컬럼 정보를 성공적으로 로드했습니다.");
|
toast.success("컬럼 정보를 성공적으로 로드했습니다.");
|
||||||
} else {
|
} else {
|
||||||
toast.error(response.data.message || "컬럼 정보 로드에 실패했습니다.");
|
showErrorToast("컬럼 정보를 불러오는 데 실패했습니다", response.data.message, {
|
||||||
|
guidance: "네트워크 연결을 확인해 주세요.",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// console.error("컬럼 타입 정보 로드 실패:", error);
|
// console.error("컬럼 타입 정보 로드 실패:", error);
|
||||||
toast.error("컬럼 정보 로드 중 오류가 발생했습니다.");
|
showErrorToast("컬럼 정보를 불러오는 데 실패했습니다", error, {
|
||||||
|
guidance: "네트워크 연결을 확인해 주세요.",
|
||||||
|
});
|
||||||
} finally {
|
} finally {
|
||||||
setColumnsLoading(false);
|
setColumnsLoading(false);
|
||||||
}
|
}
|
||||||
|
|
@ -777,11 +786,15 @@ export default function TableManagementPage() {
|
||||||
loadColumnTypes(selectedTable);
|
loadColumnTypes(selectedTable);
|
||||||
}, 1000);
|
}, 1000);
|
||||||
} else {
|
} else {
|
||||||
toast.error(response.data.message || "컬럼 설정 저장에 실패했습니다.");
|
showErrorToast("컬럼 설정 저장에 실패했습니다", response.data.message, {
|
||||||
|
guidance: "입력 데이터를 확인하고 다시 시도해 주세요.",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// console.error("컬럼 설정 저장 실패:", error);
|
// console.error("컬럼 설정 저장 실패:", error);
|
||||||
toast.error("컬럼 설정 저장 중 오류가 발생했습니다.");
|
showErrorToast("컬럼 설정 저장에 실패했습니다", error, {
|
||||||
|
guidance: "입력 데이터를 확인하고 다시 시도해 주세요.",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -980,12 +993,16 @@ export default function TableManagementPage() {
|
||||||
loadColumnTypes(selectedTable, 1, pageSize);
|
loadColumnTypes(selectedTable, 1, pageSize);
|
||||||
}, 1000);
|
}, 1000);
|
||||||
} else {
|
} else {
|
||||||
toast.error(response.data.message || "설정 저장에 실패했습니다.");
|
showErrorToast("설정 저장에 실패했습니다", response.data.message, {
|
||||||
|
guidance: "잠시 후 다시 시도해 주세요.",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// console.error("설정 저장 실패:", error);
|
// console.error("설정 저장 실패:", error);
|
||||||
toast.error("설정 저장 중 오류가 발생했습니다.");
|
showErrorToast("설정 저장에 실패했습니다", error, {
|
||||||
|
guidance: "잠시 후 다시 시도해 주세요.",
|
||||||
|
});
|
||||||
} finally {
|
} finally {
|
||||||
setIsSaving(false);
|
setIsSaving(false);
|
||||||
}
|
}
|
||||||
|
|
@ -1091,7 +1108,9 @@ export default function TableManagementPage() {
|
||||||
toast.error(response.data.message || "PK 설정 실패");
|
toast.error(response.data.message || "PK 설정 실패");
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
toast.error(error?.response?.data?.message || "PK 설정 중 오류가 발생했습니다.");
|
showErrorToast("PK 설정에 실패했습니다", error, {
|
||||||
|
guidance: "컬럼 정보를 확인하고 다시 시도해 주세요.",
|
||||||
|
});
|
||||||
} finally {
|
} finally {
|
||||||
setPkDialogOpen(false);
|
setPkDialogOpen(false);
|
||||||
}
|
}
|
||||||
|
|
@ -1115,7 +1134,9 @@ export default function TableManagementPage() {
|
||||||
toast.error(response.data.message || "인덱스 설정 실패");
|
toast.error(response.data.message || "인덱스 설정 실패");
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
toast.error(error?.response?.data?.message || error?.response?.data?.error || "인덱스 설정 중 오류가 발생했습니다.");
|
showErrorToast("인덱스 설정에 실패했습니다", error, {
|
||||||
|
guidance: "컬럼 정보를 확인하고 다시 시도해 주세요.",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[selectedTable, loadConstraints],
|
[selectedTable, loadConstraints],
|
||||||
|
|
@ -1154,10 +1175,14 @@ export default function TableManagementPage() {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
toast.error(response.data.message || "UNIQUE 설정 실패");
|
showErrorToast("UNIQUE 제약 조건 설정에 실패했습니다", response.data.message, {
|
||||||
|
guidance: "해당 컬럼에 중복 데이터가 없는지 확인해 주세요.",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
toast.error(error?.response?.data?.message || "UNIQUE 설정 중 오류가 발생했습니다.");
|
showErrorToast("UNIQUE 제약 조건 설정에 실패했습니다", error, {
|
||||||
|
guidance: "해당 컬럼에 중복 데이터가 없는지 확인해 주세요.",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[selectedTable],
|
[selectedTable],
|
||||||
|
|
@ -1188,12 +1213,14 @@ export default function TableManagementPage() {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
toast.error(response.data.message || "NOT NULL 설정 실패");
|
showErrorToast("NOT NULL 제약 조건 설정에 실패했습니다", response.data.message, {
|
||||||
|
guidance: "해당 컬럼에 NULL 값이 없는지 확인해 주세요.",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
toast.error(
|
showErrorToast("NOT NULL 제약 조건 설정에 실패했습니다", error, {
|
||||||
error?.response?.data?.message || "NOT NULL 설정 중 오류가 발생했습니다.",
|
guidance: "해당 컬럼에 NULL 값이 없는지 확인해 주세요.",
|
||||||
);
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[selectedTable],
|
[selectedTable],
|
||||||
|
|
@ -1225,10 +1252,14 @@ export default function TableManagementPage() {
|
||||||
// 테이블 목록 새로고침
|
// 테이블 목록 새로고침
|
||||||
await loadTables();
|
await loadTables();
|
||||||
} else {
|
} else {
|
||||||
toast.error(result.message || "테이블 삭제에 실패했습니다.");
|
showErrorToast("테이블 삭제에 실패했습니다", result.message, {
|
||||||
|
guidance: "테이블에 종속된 데이터가 있는지 확인해 주세요.",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
toast.error(error?.response?.data?.message || "테이블 삭제 중 오류가 발생했습니다.");
|
showErrorToast("테이블 삭제에 실패했습니다", error, {
|
||||||
|
guidance: "테이블에 종속된 데이터가 있는지 확인해 주세요.",
|
||||||
|
});
|
||||||
} finally {
|
} finally {
|
||||||
setIsDeleting(false);
|
setIsDeleting(false);
|
||||||
setDeleteDialogOpen(false);
|
setDeleteDialogOpen(false);
|
||||||
|
|
@ -1308,7 +1339,9 @@ export default function TableManagementPage() {
|
||||||
setSelectedTableIds(new Set());
|
setSelectedTableIds(new Set());
|
||||||
await loadTables();
|
await loadTables();
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
toast.error("테이블 삭제 중 오류가 발생했습니다.");
|
showErrorToast("테이블 삭제에 실패했습니다", error, {
|
||||||
|
guidance: "테이블에 종속된 데이터가 있는지 확인해 주세요.",
|
||||||
|
});
|
||||||
} finally {
|
} finally {
|
||||||
setIsDeleting(false);
|
setIsDeleting(false);
|
||||||
setDeleteDialogOpen(false);
|
setDeleteDialogOpen(false);
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import { ScreenDefinition, LayoutData, ComponentData } from "@/types/screen";
|
||||||
import { LayerDefinition } from "@/types/screen-management";
|
import { LayerDefinition } from "@/types/screen-management";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
import { showErrorToast } from "@/lib/utils/toastUtils";
|
||||||
import { initializeComponents } from "@/lib/registry/components";
|
import { initializeComponents } from "@/lib/registry/components";
|
||||||
import { EditModal } from "@/components/screen/EditModal";
|
import { EditModal } from "@/components/screen/EditModal";
|
||||||
import { RealtimePreview } from "@/components/screen/RealtimePreviewDynamic";
|
import { RealtimePreview } from "@/components/screen/RealtimePreviewDynamic";
|
||||||
|
|
@ -219,7 +220,7 @@ function ScreenViewPage() {
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("화면 로드 실패:", error);
|
console.error("화면 로드 실패:", error);
|
||||||
setError("화면을 불러오는데 실패했습니다.");
|
setError("화면을 불러오는데 실패했습니다.");
|
||||||
toast.error("화면을 불러오는데 실패했습니다.");
|
showErrorToast("화면을 불러오는 데 실패했습니다", error, { guidance: "화면 설정을 확인하거나 잠시 후 다시 시도해 주세요." });
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import { screenApi } from "@/lib/api/screen";
|
||||||
import { ScreenDefinition } from "@/types/screen";
|
import { ScreenDefinition } from "@/types/screen";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
import { showErrorToast } from "@/lib/utils/toastUtils";
|
||||||
import { ScreenPreviewProvider } from "@/contexts/ScreenPreviewContext";
|
import { ScreenPreviewProvider } from "@/contexts/ScreenPreviewContext";
|
||||||
import { useAuth } from "@/hooks/useAuth";
|
import { useAuth } from "@/hooks/useAuth";
|
||||||
import { TableOptionsProvider } from "@/contexts/TableOptionsContext";
|
import { TableOptionsProvider } from "@/contexts/TableOptionsContext";
|
||||||
|
|
@ -135,7 +136,7 @@ function PopScreenViewPage() {
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("[POP] 화면 로드 실패:", error);
|
console.error("[POP] 화면 로드 실패:", error);
|
||||||
setError("화면을 불러오는데 실패했습니다.");
|
setError("화면을 불러오는데 실패했습니다.");
|
||||||
toast.error("화면을 불러오는데 실패했습니다.");
|
showErrorToast("POP 화면을 불러오는 데 실패했습니다", error, { guidance: "화면 설정을 확인하거나 잠시 후 다시 시도해 주세요." });
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import { downloadFile } from "@/lib/api/file";
|
||||||
import { FileViewerModal } from "@/lib/registry/components/file-upload/FileViewerModal";
|
import { FileViewerModal } from "@/lib/registry/components/file-upload/FileViewerModal";
|
||||||
import { formatFileSize } from "@/lib/utils";
|
import { formatFileSize } from "@/lib/utils";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
import { showErrorToast } from "@/lib/utils/toastUtils";
|
||||||
import {
|
import {
|
||||||
File,
|
File,
|
||||||
FileText,
|
FileText,
|
||||||
|
|
@ -134,7 +135,7 @@ export const GlobalFileViewer: React.FC<GlobalFileViewerProps> = ({
|
||||||
toast.success(`파일 다운로드 시작: ${file.realFileName}`);
|
toast.success(`파일 다운로드 시작: ${file.realFileName}`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("파일 다운로드 오류:", error);
|
console.error("파일 다운로드 오류:", error);
|
||||||
toast.error("파일 다운로드에 실패했습니다.");
|
showErrorToast("파일 다운로드에 실패했습니다", error, { guidance: "파일이 존재하는지 확인하고 다시 시도해 주세요." });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ import {
|
||||||
import { Switch } from "@/components/ui/switch";
|
import { Switch } from "@/components/ui/switch";
|
||||||
import { Badge } from "@/components/ui/badge";
|
import { Badge } from "@/components/ui/badge";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
import { showErrorToast } from "@/lib/utils/toastUtils";
|
||||||
import { BatchAPI, BatchJob, BatchConfig } from "@/lib/api/batch";
|
import { BatchAPI, BatchJob, BatchConfig } from "@/lib/api/batch";
|
||||||
import { ExternalDbConnectionAPI } from "@/lib/api/externalDbConnection";
|
import { ExternalDbConnectionAPI } from "@/lib/api/externalDbConnection";
|
||||||
|
|
||||||
|
|
@ -123,7 +124,7 @@ export default function AdvancedBatchModal({
|
||||||
setConnections(list);
|
setConnections(list);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("연결 목록 조회 오류:", error);
|
console.error("연결 목록 조회 오류:", error);
|
||||||
toast.error("연결 목록을 불러오는데 실패했습니다.");
|
showErrorToast("연결 목록을 불러오는 데 실패했습니다", error, { guidance: "네트워크 연결을 확인해 주세요." });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -190,7 +191,7 @@ export default function AdvancedBatchModal({
|
||||||
onClose();
|
onClose();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("배치 저장 오류:", error);
|
console.error("배치 저장 오류:", error);
|
||||||
toast.error(error instanceof Error ? error.message : "저장에 실패했습니다.");
|
showErrorToast("배치 설정 저장에 실패했습니다", error, { guidance: "입력 데이터를 확인하고 다시 시도해 주세요." });
|
||||||
} finally {
|
} finally {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ import {
|
||||||
Trash2,
|
Trash2,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
import { showErrorToast } from "@/lib/utils/toastUtils";
|
||||||
import { format } from "date-fns";
|
import { format } from "date-fns";
|
||||||
import { ko } from "date-fns/locale";
|
import { ko } from "date-fns/locale";
|
||||||
import { ddlApi } from "../../lib/api/ddl";
|
import { ddlApi } from "../../lib/api/ddl";
|
||||||
|
|
@ -71,7 +72,7 @@ export function DDLLogViewer({ isOpen, onClose }: DDLLogViewerProps) {
|
||||||
setStatistics(statsResult);
|
setStatistics(statsResult);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// console.error("DDL 로그 로드 실패:", error);
|
// console.error("DDL 로그 로드 실패:", error);
|
||||||
toast.error("DDL 로그를 불러오는데 실패했습니다.");
|
showErrorToast("DDL 로그를 불러오는 데 실패했습니다", error, { guidance: "네트워크 연결을 확인해 주세요." });
|
||||||
} finally {
|
} finally {
|
||||||
if (showLoading) setLoading(false);
|
if (showLoading) setLoading(false);
|
||||||
setRefreshing(false);
|
setRefreshing(false);
|
||||||
|
|
@ -108,7 +109,7 @@ export function DDLLogViewer({ isOpen, onClose }: DDLLogViewerProps) {
|
||||||
loadData(false);
|
loadData(false);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// console.error("로그 정리 실패:", error);
|
// console.error("로그 정리 실패:", error);
|
||||||
toast.error("로그 정리에 실패했습니다.");
|
showErrorToast("로그 정리에 실패했습니다", error, { guidance: "잠시 후 다시 시도해 주세요." });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ import {
|
||||||
} from "@/components/ui/dialog";
|
} from "@/components/ui/dialog";
|
||||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
import { showErrorToast } from "@/lib/utils/toastUtils";
|
||||||
import {
|
import {
|
||||||
ExternalCallConfigAPI,
|
ExternalCallConfigAPI,
|
||||||
ExternalCallConfig,
|
ExternalCallConfig,
|
||||||
|
|
@ -259,7 +260,7 @@ export function ExternalCallConfigModal({ isOpen, onClose, onSave, editingConfig
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("외부 호출 설정 저장 오류:", error);
|
console.error("외부 호출 설정 저장 오류:", error);
|
||||||
toast.error("저장 중 오류가 발생했습니다.");
|
showErrorToast("외부 호출 설정 저장에 실패했습니다", error, { guidance: "입력 데이터를 확인하고 다시 시도해 주세요." });
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ import {
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import { LayoutCategory } from "@/types/layout";
|
import { LayoutCategory } from "@/types/layout";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
import { showErrorToast } from "@/lib/utils/toastUtils";
|
||||||
|
|
||||||
interface LayoutFormModalProps {
|
interface LayoutFormModalProps {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
|
|
@ -210,7 +211,7 @@ export const LayoutFormModal: React.FC<LayoutFormModalProps> = ({ open, onOpenCh
|
||||||
success: false,
|
success: false,
|
||||||
message: result.message || "레이아웃 생성에 실패했습니다.",
|
message: result.message || "레이아웃 생성에 실패했습니다.",
|
||||||
});
|
});
|
||||||
toast.error("레이아웃 생성 실패");
|
showErrorToast("레이아웃 생성에 실패했습니다", result.message, { guidance: "입력 데이터를 확인하고 다시 시도해 주세요." });
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("레이아웃 생성 오류:", error);
|
console.error("레이아웃 생성 오류:", error);
|
||||||
|
|
@ -218,7 +219,7 @@ export const LayoutFormModal: React.FC<LayoutFormModalProps> = ({ open, onOpenCh
|
||||||
success: false,
|
success: false,
|
||||||
message: "서버 오류가 발생했습니다.",
|
message: "서버 오류가 발생했습니다.",
|
||||||
});
|
});
|
||||||
toast.error("서버 오류");
|
showErrorToast("레이아웃 생성에 실패했습니다", error, { guidance: "입력 데이터를 확인하고 다시 시도해 주세요." });
|
||||||
} finally {
|
} finally {
|
||||||
setIsGenerating(false);
|
setIsGenerating(false);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
import { useState, useEffect } from "react";
|
import { useState, useEffect } from "react";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
import { showErrorToast } from "@/lib/utils/toastUtils";
|
||||||
import { Loader2 } from "lucide-react";
|
import { Loader2 } from "lucide-react";
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
|
|
@ -94,7 +95,7 @@ export function MenuCopyDialog({
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("회사 목록 조회 실패:", error);
|
console.error("회사 목록 조회 실패:", error);
|
||||||
toast.error("회사 목록을 불러올 수 없습니다");
|
showErrorToast("회사 목록을 불러오는 데 실패했습니다", error, { guidance: "네트워크 연결을 확인해 주세요." });
|
||||||
} finally {
|
} finally {
|
||||||
setLoadingCompanies(false);
|
setLoadingCompanies(false);
|
||||||
}
|
}
|
||||||
|
|
@ -160,7 +161,7 @@ export function MenuCopyDialog({
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error("메뉴 복사 오류:", error);
|
console.error("메뉴 복사 오류:", error);
|
||||||
toast.error(error.message || "메뉴 복사 중 오류가 발생했습니다");
|
showErrorToast("메뉴 복사에 실패했습니다", error, { guidance: "복사 대상과 설정을 확인해 주세요." });
|
||||||
} finally {
|
} finally {
|
||||||
setCopying(false);
|
setCopying(false);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ import { InteractiveScreenViewerDynamic } from "@/components/screen/InteractiveS
|
||||||
import { screenApi } from "@/lib/api/screen";
|
import { screenApi } from "@/lib/api/screen";
|
||||||
import { ComponentData } from "@/types/screen";
|
import { ComponentData } from "@/types/screen";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
import { showErrorToast } from "@/lib/utils/toastUtils";
|
||||||
import { useAuth } from "@/hooks/useAuth";
|
import { useAuth } from "@/hooks/useAuth";
|
||||||
import { TableOptionsProvider } from "@/contexts/TableOptionsContext";
|
import { TableOptionsProvider } from "@/contexts/TableOptionsContext";
|
||||||
import { TableSearchWidgetHeightProvider } from "@/contexts/TableSearchWidgetHeightContext";
|
import { TableSearchWidgetHeightProvider } from "@/contexts/TableSearchWidgetHeightContext";
|
||||||
|
|
@ -548,11 +549,15 @@ export const ScreenModal: React.FC<ScreenModalProps> = ({ className }) => {
|
||||||
setOriginalData(normalizedData); // 🆕 원본 데이터 저장 (UPDATE 판단용)
|
setOriginalData(normalizedData); // 🆕 원본 데이터 저장 (UPDATE 판단용)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
toast.error("데이터를 불러올 수 없습니다.");
|
toast.error("수정할 데이터를 불러올 수 없습니다.", {
|
||||||
|
description: "해당 항목이 삭제되었거나 접근 권한이 없을 수 있습니다.",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("수정 데이터 조회 오류:", error);
|
console.error("수정 데이터 조회 오류:", error);
|
||||||
toast.error("데이터를 불러오는 중 오류가 발생했습니다.");
|
showErrorToast("수정 데이터 조회에 실패했습니다", error, {
|
||||||
|
guidance: "네트워크 연결을 확인하고 다시 시도해 주세요.",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -604,7 +609,9 @@ export const ScreenModal: React.FC<ScreenModalProps> = ({ className }) => {
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("화면 데이터 로딩 오류:", error);
|
console.error("화면 데이터 로딩 오류:", error);
|
||||||
toast.error("화면을 불러오는 중 오류가 발생했습니다.");
|
showErrorToast("화면 데이터를 불러오는 데 실패했습니다", error, {
|
||||||
|
guidance: "화면 설정을 확인하거나 잠시 후 다시 시도해 주세요.",
|
||||||
|
});
|
||||||
handleClose();
|
handleClose();
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ import {
|
||||||
} from "@/components/ui/dialog";
|
} from "@/components/ui/dialog";
|
||||||
import { MoreHorizontal, Trash2, Copy, Plus, Search, Network, Database, Calendar, User } from "lucide-react";
|
import { MoreHorizontal, Trash2, Copy, Plus, Search, Network, Database, Calendar, User } from "lucide-react";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
import { showErrorToast } from "@/lib/utils/toastUtils";
|
||||||
import { useAuth } from "@/hooks/useAuth";
|
import { useAuth } from "@/hooks/useAuth";
|
||||||
import { apiClient } from "@/lib/api/client";
|
import { apiClient } from "@/lib/api/client";
|
||||||
|
|
||||||
|
|
@ -61,7 +62,7 @@ export default function DataFlowList({ onLoadFlow }: DataFlowListProps) {
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("플로우 목록 조회 실패", error);
|
console.error("플로우 목록 조회 실패", error);
|
||||||
toast.error("플로우 목록을 불러오는데 실패했습니다.");
|
showErrorToast("플로우 목록을 불러오는 데 실패했습니다", error, { guidance: "네트워크 연결을 확인해 주세요." });
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
|
|
@ -107,7 +108,7 @@ export default function DataFlowList({ onLoadFlow }: DataFlowListProps) {
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("플로우 복사 실패:", error);
|
console.error("플로우 복사 실패:", error);
|
||||||
toast.error("플로우 복사에 실패했습니다.");
|
showErrorToast("플로우 복사에 실패했습니다", error, { guidance: "잠시 후 다시 시도해 주세요." });
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
|
|
@ -129,7 +130,7 @@ export default function DataFlowList({ onLoadFlow }: DataFlowListProps) {
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("플로우 삭제 실패:", error);
|
console.error("플로우 삭제 실패:", error);
|
||||||
toast.error("플로우 삭제에 실패했습니다.");
|
showErrorToast("플로우 삭제에 실패했습니다", error, { guidance: "잠시 후 다시 시도해 주세요." });
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
setShowDeleteModal(false);
|
setShowDeleteModal(false);
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ import {
|
||||||
Timer,
|
Timer,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
import { showErrorToast } from "@/lib/utils/toastUtils";
|
||||||
|
|
||||||
// 타입 import
|
// 타입 import
|
||||||
import {
|
import {
|
||||||
|
|
@ -144,7 +145,7 @@ const ExternalCallTestPanel: React.FC<ExternalCallTestPanelProps> = ({
|
||||||
toast.success("API 테스트가 성공했습니다!");
|
toast.success("API 테스트가 성공했습니다!");
|
||||||
setActiveTab("response");
|
setActiveTab("response");
|
||||||
} else {
|
} else {
|
||||||
toast.error("API 호출이 실패했습니다.");
|
showErrorToast("API 호출이 실패했습니다", null, { guidance: "URL과 요청 설정을 확인해 주세요." });
|
||||||
setActiveTab("response");
|
setActiveTab("response");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -156,7 +157,7 @@ const ExternalCallTestPanel: React.FC<ExternalCallTestPanelProps> = ({
|
||||||
};
|
};
|
||||||
setTestResult(errorResult);
|
setTestResult(errorResult);
|
||||||
onTestResult(errorResult);
|
onTestResult(errorResult);
|
||||||
toast.error(response.error || "테스트 실행 중 오류가 발생했습니다.");
|
showErrorToast("API 테스트 실행에 실패했습니다", response.error, { guidance: "URL과 요청 설정을 확인해 주세요." });
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const errorResult: ApiTestResult = {
|
const errorResult: ApiTestResult = {
|
||||||
|
|
@ -167,7 +168,7 @@ const ExternalCallTestPanel: React.FC<ExternalCallTestPanelProps> = ({
|
||||||
};
|
};
|
||||||
setTestResult(errorResult);
|
setTestResult(errorResult);
|
||||||
onTestResult(errorResult);
|
onTestResult(errorResult);
|
||||||
toast.error("테스트 실행 중 오류가 발생했습니다.");
|
showErrorToast("API 테스트 실행에 실패했습니다", error, { guidance: "네트워크 연결과 URL을 확인해 주세요." });
|
||||||
} finally {
|
} finally {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
}
|
}
|
||||||
|
|
@ -179,7 +180,7 @@ const ExternalCallTestPanel: React.FC<ExternalCallTestPanelProps> = ({
|
||||||
await navigator.clipboard.writeText(text);
|
await navigator.clipboard.writeText(text);
|
||||||
toast.success("클립보드에 복사되었습니다.");
|
toast.success("클립보드에 복사되었습니다.");
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
toast.error("복사에 실패했습니다.");
|
showErrorToast("클립보드 복사에 실패했습니다", error, { guidance: "브라우저 권한을 확인해 주세요." });
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import { Label } from "@/components/ui/label";
|
||||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||||
import { Plus, Save, Edit2, Trash2, FolderTree, Check, ChevronsUpDown } from "lucide-react";
|
import { Plus, Save, Edit2, Trash2, FolderTree, Check, ChevronsUpDown } from "lucide-react";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
import { showErrorToast } from "@/lib/utils/toastUtils";
|
||||||
import { NumberingRuleConfig, NumberingRulePart, SEPARATOR_OPTIONS, SeparatorType } from "@/types/numbering-rule";
|
import { NumberingRuleConfig, NumberingRulePart, SEPARATOR_OPTIONS, SeparatorType } from "@/types/numbering-rule";
|
||||||
import { NumberingRuleCard } from "./NumberingRuleCard";
|
import { NumberingRuleCard } from "./NumberingRuleCard";
|
||||||
import { NumberingRulePreview } from "./NumberingRulePreview";
|
import { NumberingRulePreview } from "./NumberingRulePreview";
|
||||||
|
|
@ -399,10 +400,10 @@ export const NumberingRuleDesigner: React.FC<NumberingRuleDesignerProps> = ({
|
||||||
await onSave?.(response.data);
|
await onSave?.(response.data);
|
||||||
toast.success("채번 규칙이 저장되었습니다");
|
toast.success("채번 규칙이 저장되었습니다");
|
||||||
} else {
|
} else {
|
||||||
toast.error(response.error || "저장 실패");
|
showErrorToast("채번 규칙 저장에 실패했습니다", response.error, { guidance: "설정을 확인하고 다시 시도해 주세요." });
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
toast.error(`저장 실패: ${error.message}`);
|
showErrorToast("채번 규칙 저장에 실패했습니다", error, { guidance: "설정을 확인하고 다시 시도해 주세요." });
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
|
|
@ -446,10 +447,10 @@ export const NumberingRuleDesigner: React.FC<NumberingRuleDesignerProps> = ({
|
||||||
|
|
||||||
toast.success("규칙이 삭제되었습니다");
|
toast.success("규칙이 삭제되었습니다");
|
||||||
} else {
|
} else {
|
||||||
toast.error(response.error || "삭제 실패");
|
showErrorToast("채번 규칙 삭제에 실패했습니다", response.error, { guidance: "잠시 후 다시 시도해 주세요." });
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
toast.error(`삭제 실패: ${error.message}`);
|
showErrorToast("채번 규칙 삭제에 실패했습니다", error, { guidance: "잠시 후 다시 시도해 주세요." });
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ import {
|
||||||
Save,
|
Save,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
import { showErrorToast } from "@/lib/utils/toastUtils";
|
||||||
import { ScreenDefinition } from "@/types/screen";
|
import { ScreenDefinition } from "@/types/screen";
|
||||||
import { screenApi } from "@/lib/api/screen";
|
import { screenApi } from "@/lib/api/screen";
|
||||||
import { PopScreenGroup, getPopScreenGroups } from "@/lib/api/popScreenGroup";
|
import { PopScreenGroup, getPopScreenGroups } from "@/lib/api/popScreenGroup";
|
||||||
|
|
@ -191,7 +192,7 @@ export function PopScreenSettingModal({
|
||||||
onOpenChange(false);
|
onOpenChange(false);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("저장 실패:", error);
|
console.error("저장 실패:", error);
|
||||||
toast.error("저장에 실패했습니다.");
|
showErrorToast("POP 화면 설정 저장에 실패했습니다", error, { guidance: "입력 데이터를 확인하고 다시 시도해 주세요." });
|
||||||
} finally {
|
} finally {
|
||||||
setSaving(false);
|
setSaving(false);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ import { ko } from "date-fns/locale";
|
||||||
import { useAuth } from "@/hooks/useAuth";
|
import { useAuth } from "@/hooks/useAuth";
|
||||||
import { uploadFilesAndCreateData } from "@/lib/api/file";
|
import { uploadFilesAndCreateData } from "@/lib/api/file";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
import { showErrorToast } from "@/lib/utils/toastUtils";
|
||||||
import { useCascadingDropdown } from "@/hooks/useCascadingDropdown";
|
import { useCascadingDropdown } from "@/hooks/useCascadingDropdown";
|
||||||
import { CascadingDropdownConfig, LayerDefinition } from "@/types/screen-management";
|
import { CascadingDropdownConfig, LayerDefinition } from "@/types/screen-management";
|
||||||
import {
|
import {
|
||||||
|
|
@ -1265,7 +1266,7 @@ export const InteractiveScreenViewer: React.FC<InteractiveScreenViewerProps> = (
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// console.error("파일 업로드 오류:", error);
|
// console.error("파일 업로드 오류:", error);
|
||||||
toast.error("파일 업로드에 실패했습니다.");
|
showErrorToast("파일 업로드에 실패했습니다", error, { guidance: "파일 크기와 형식을 확인하고 다시 시도해 주세요." });
|
||||||
|
|
||||||
// 파일 입력 초기화
|
// 파일 입력 초기화
|
||||||
e.target.value = "";
|
e.target.value = "";
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ import { Badge } from "@/components/ui/badge";
|
||||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
import { showErrorToast } from "@/lib/utils/toastUtils";
|
||||||
import { Search, Monitor, Settings, X, Plus } from "lucide-react";
|
import { Search, Monitor, Settings, X, Plus } from "lucide-react";
|
||||||
import { menuScreenApi } from "@/lib/api/screen";
|
import { menuScreenApi } from "@/lib/api/screen";
|
||||||
import { apiClient } from "@/lib/api/client";
|
import { apiClient } from "@/lib/api/client";
|
||||||
|
|
@ -93,7 +94,7 @@ export const MenuAssignmentModal: React.FC<MenuAssignmentModalProps> = ({
|
||||||
setMenus(allMenus);
|
setMenus(allMenus);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// console.error("메뉴 목록 로드 실패:", error);
|
// console.error("메뉴 목록 로드 실패:", error);
|
||||||
toast.error("메뉴 목록을 불러오는데 실패했습니다.");
|
showErrorToast("메뉴 목록을 불러오는 데 실패했습니다", error, { guidance: "네트워크 연결을 확인해 주세요." });
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,7 @@ import {
|
||||||
CommandList,
|
CommandList,
|
||||||
} from "@/components/ui/command";
|
} from "@/components/ui/command";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
import { showErrorToast } from "@/lib/utils/toastUtils";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { Badge } from "@/components/ui/badge";
|
import { Badge } from "@/components/ui/badge";
|
||||||
import {
|
import {
|
||||||
|
|
@ -403,7 +404,7 @@ export default function NodeSettingModal({
|
||||||
]);
|
]);
|
||||||
toast.success("데이터가 새로고침되었습니다.");
|
toast.success("데이터가 새로고침되었습니다.");
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
toast.error("새로고침 실패");
|
showErrorToast("새로고침에 실패했습니다", error, { guidance: "잠시 후 다시 시도해 주세요." });
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
|
|
@ -635,10 +636,10 @@ function TableRelationTab({
|
||||||
onReload();
|
onReload();
|
||||||
onRefreshVisualization?.();
|
onRefreshVisualization?.();
|
||||||
} else {
|
} else {
|
||||||
toast.error(response.message || "저장에 실패했습니다.");
|
showErrorToast("노드 설정 저장에 실패했습니다", response.message, { guidance: "입력 데이터를 확인하고 다시 시도해 주세요." });
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
toast.error(error.message || "저장 중 오류가 발생했습니다.");
|
showErrorToast("노드 설정 저장에 실패했습니다", error, { guidance: "입력 데이터를 확인하고 다시 시도해 주세요." });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -653,10 +654,10 @@ function TableRelationTab({
|
||||||
onReload();
|
onReload();
|
||||||
onRefreshVisualization?.();
|
onRefreshVisualization?.();
|
||||||
} else {
|
} else {
|
||||||
toast.error(response.message || "삭제에 실패했습니다.");
|
showErrorToast("노드 설정 삭제에 실패했습니다", response.message, { guidance: "잠시 후 다시 시도해 주세요." });
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
toast.error(error.message || "삭제 중 오류가 발생했습니다.");
|
showErrorToast("노드 설정 삭제에 실패했습니다", error, { guidance: "잠시 후 다시 시도해 주세요." });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -1178,10 +1179,10 @@ function JoinSettingTab({
|
||||||
onReload();
|
onReload();
|
||||||
onRefreshVisualization?.();
|
onRefreshVisualization?.();
|
||||||
} else {
|
} else {
|
||||||
toast.error(response.message || "저장에 실패했습니다.");
|
showErrorToast("노드 설정 저장에 실패했습니다", response.message, { guidance: "입력 데이터를 확인하고 다시 시도해 주세요." });
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
toast.error(error.message || "저장 중 오류가 발생했습니다.");
|
showErrorToast("노드 설정 저장에 실패했습니다", error, { guidance: "입력 데이터를 확인하고 다시 시도해 주세요." });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -1196,10 +1197,10 @@ function JoinSettingTab({
|
||||||
onReload();
|
onReload();
|
||||||
onRefreshVisualization?.();
|
onRefreshVisualization?.();
|
||||||
} else {
|
} else {
|
||||||
toast.error(response.message || "삭제에 실패했습니다.");
|
showErrorToast("노드 설정 삭제에 실패했습니다", response.message, { guidance: "잠시 후 다시 시도해 주세요." });
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
toast.error(error.message || "삭제 중 오류가 발생했습니다.");
|
showErrorToast("노드 설정 삭제에 실패했습니다", error, { guidance: "잠시 후 다시 시도해 주세요." });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -1586,10 +1587,10 @@ function DataFlowTab({
|
||||||
onReload();
|
onReload();
|
||||||
onRefreshVisualization?.();
|
onRefreshVisualization?.();
|
||||||
} else {
|
} else {
|
||||||
toast.error(response.message || "저장에 실패했습니다.");
|
showErrorToast("노드 설정 저장에 실패했습니다", response.message, { guidance: "입력 데이터를 확인하고 다시 시도해 주세요." });
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
toast.error(error.message || "저장 중 오류가 발생했습니다.");
|
showErrorToast("노드 설정 저장에 실패했습니다", error, { guidance: "입력 데이터를 확인하고 다시 시도해 주세요." });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -1604,10 +1605,10 @@ function DataFlowTab({
|
||||||
onReload();
|
onReload();
|
||||||
onRefreshVisualization?.();
|
onRefreshVisualization?.();
|
||||||
} else {
|
} else {
|
||||||
toast.error(response.message || "삭제에 실패했습니다.");
|
showErrorToast("노드 설정 삭제에 실패했습니다", response.message, { guidance: "잠시 후 다시 시도해 주세요." });
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
toast.error(error.message || "삭제 중 오류가 발생했습니다.");
|
showErrorToast("노드 설정 삭제에 실패했습니다", error, { guidance: "잠시 후 다시 시도해 주세요." });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/u
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { X, Save, Loader2 } from "lucide-react";
|
import { X, Save, Loader2 } from "lucide-react";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
import { showErrorToast } from "@/lib/utils/toastUtils";
|
||||||
import { DynamicComponentRenderer } from "@/lib/registry/DynamicComponentRenderer";
|
import { DynamicComponentRenderer } from "@/lib/registry/DynamicComponentRenderer";
|
||||||
import { InteractiveScreenViewer } from "./InteractiveScreenViewer";
|
import { InteractiveScreenViewer } from "./InteractiveScreenViewer";
|
||||||
import { screenApi } from "@/lib/api/screen";
|
import { screenApi } from "@/lib/api/screen";
|
||||||
|
|
@ -76,7 +77,9 @@ export const SaveModal: React.FC<SaveModalProps> = ({
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("화면 로드 실패:", error);
|
console.error("화면 로드 실패:", error);
|
||||||
toast.error("화면을 불러오는데 실패했습니다.");
|
showErrorToast("화면 구성 정보를 불러오는 데 실패했습니다", error, {
|
||||||
|
guidance: "화면 설정을 확인하거나 잠시 후 다시 시도해 주세요.",
|
||||||
|
});
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
|
|
@ -264,7 +267,9 @@ export const SaveModal: React.FC<SaveModalProps> = ({
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
// ❌ 저장 실패 - 모달은 닫히지 않음
|
// ❌ 저장 실패 - 모달은 닫히지 않음
|
||||||
console.error("저장 실패:", error);
|
console.error("저장 실패:", error);
|
||||||
toast.error(`저장 중 오류가 발생했습니다: ${error.message || "알 수 없는 오류"}`);
|
showErrorToast("데이터 저장에 실패했습니다", error, {
|
||||||
|
guidance: "입력 값을 확인하고 다시 시도해 주세요.",
|
||||||
|
});
|
||||||
} finally {
|
} finally {
|
||||||
setIsSaving(false);
|
setIsSaving(false);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ import {
|
||||||
} from "@/components/ui/select";
|
} from "@/components/ui/select";
|
||||||
import { ScreenGroup, createScreenGroup, updateScreenGroup } from "@/lib/api/screenGroup";
|
import { ScreenGroup, createScreenGroup, updateScreenGroup } from "@/lib/api/screenGroup";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
import { showErrorToast } from "@/lib/utils/toastUtils";
|
||||||
import { apiClient } from "@/lib/api/client";
|
import { apiClient } from "@/lib/api/client";
|
||||||
import {
|
import {
|
||||||
Command,
|
Command,
|
||||||
|
|
@ -225,11 +226,11 @@ export function ScreenGroupModal({
|
||||||
onSuccess();
|
onSuccess();
|
||||||
onClose();
|
onClose();
|
||||||
} else {
|
} else {
|
||||||
toast.error(response.message || "작업에 실패했습니다");
|
showErrorToast("그룹 저장에 실패했습니다", response.message, { guidance: "잠시 후 다시 시도해 주세요." });
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error("그룹 저장 실패:", error);
|
console.error("그룹 저장 실패:", error);
|
||||||
toast.error("그룹 저장에 실패했습니다");
|
showErrorToast("그룹 저장에 실패했습니다", error, { guidance: "잠시 후 다시 시도해 주세요." });
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -89,6 +89,7 @@ import { Check, ChevronsUpDown } from "lucide-react";
|
||||||
import { ScreenGroupModal } from "./ScreenGroupModal";
|
import { ScreenGroupModal } from "./ScreenGroupModal";
|
||||||
import CopyScreenModal from "./CopyScreenModal";
|
import CopyScreenModal from "./CopyScreenModal";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
import { showErrorToast } from "@/lib/utils/toastUtils";
|
||||||
import { screenApi } from "@/lib/api/screen";
|
import { screenApi } from "@/lib/api/screen";
|
||||||
|
|
||||||
interface ScreenGroupTreeViewProps {
|
interface ScreenGroupTreeViewProps {
|
||||||
|
|
@ -581,11 +582,11 @@ export function ScreenGroupTreeView({
|
||||||
await loadGroupsData();
|
await loadGroupsData();
|
||||||
window.dispatchEvent(new CustomEvent("screen-list-refresh"));
|
window.dispatchEvent(new CustomEvent("screen-list-refresh"));
|
||||||
} else {
|
} else {
|
||||||
toast.error(response.message || "그룹 삭제에 실패했습니다");
|
showErrorToast("그룹 삭제에 실패했습니다", response.message, { guidance: "하위 항목이 있는 경우 먼저 삭제해 주세요." });
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("그룹 삭제 실패:", error);
|
console.error("그룹 삭제 실패:", error);
|
||||||
toast.error("그룹 삭제에 실패했습니다");
|
showErrorToast("그룹 삭제에 실패했습니다", error, { guidance: "잠시 후 다시 시도해 주세요." });
|
||||||
} finally {
|
} finally {
|
||||||
setIsDeleting(false);
|
setIsDeleting(false);
|
||||||
setDeleteProgress({ current: 0, total: 0, message: "" });
|
setDeleteProgress({ current: 0, total: 0, message: "" });
|
||||||
|
|
@ -614,7 +615,7 @@ export function ScreenGroupTreeView({
|
||||||
window.dispatchEvent(new CustomEvent("screen-list-refresh"));
|
window.dispatchEvent(new CustomEvent("screen-list-refresh"));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("화면 삭제 실패:", error);
|
console.error("화면 삭제 실패:", error);
|
||||||
toast.error("화면 삭제에 실패했습니다");
|
showErrorToast("화면 삭제에 실패했습니다", error, { guidance: "잠시 후 다시 시도해 주세요." });
|
||||||
} finally {
|
} finally {
|
||||||
setIsScreenDeleting(false);
|
setIsScreenDeleting(false);
|
||||||
setIsScreenDeleteDialogOpen(false);
|
setIsScreenDeleteDialogOpen(false);
|
||||||
|
|
@ -765,7 +766,7 @@ export function ScreenGroupTreeView({
|
||||||
window.dispatchEvent(new CustomEvent("screen-list-refresh"));
|
window.dispatchEvent(new CustomEvent("screen-list-refresh"));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("화면 수정 실패:", error);
|
console.error("화면 수정 실패:", error);
|
||||||
toast.error("화면 수정에 실패했습니다");
|
showErrorToast("화면 정보 수정에 실패했습니다", error, { guidance: "입력 데이터를 확인하고 다시 시도해 주세요." });
|
||||||
} finally {
|
} finally {
|
||||||
setIsEditScreenModalOpen(false);
|
setIsEditScreenModalOpen(false);
|
||||||
setEditingScreen(null);
|
setEditingScreen(null);
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,7 @@ import { isFileComponent, getComponentWebType } from "@/lib/utils/componentTypeU
|
||||||
import { TableOptionsProvider } from "@/contexts/TableOptionsContext";
|
import { TableOptionsProvider } from "@/contexts/TableOptionsContext";
|
||||||
import { RealtimePreview } from "./RealtimePreviewDynamic";
|
import { RealtimePreview } from "./RealtimePreviewDynamic";
|
||||||
import { ScreenPreviewProvider } from "@/contexts/ScreenPreviewContext";
|
import { ScreenPreviewProvider } from "@/contexts/ScreenPreviewContext";
|
||||||
|
import { showErrorToast } from "@/lib/utils/toastUtils";
|
||||||
|
|
||||||
// InteractiveScreenViewer를 동적으로 import (SSR 비활성화)
|
// InteractiveScreenViewer를 동적으로 import (SSR 비활성화)
|
||||||
const InteractiveScreenViewer = dynamic(
|
const InteractiveScreenViewer = dynamic(
|
||||||
|
|
@ -683,7 +684,7 @@ export default function ScreenList({ onScreenSelect, selectedScreen, onDesignScr
|
||||||
setPreviewLayout(layoutData);
|
setPreviewLayout(layoutData);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("❌ 레이아웃 로드 실패:", error);
|
console.error("❌ 레이아웃 로드 실패:", error);
|
||||||
toast.error("화면 레이아웃을 불러오는데 실패했습니다.");
|
showErrorToast("화면 레이아웃을 불러오는 데 실패했습니다", error, { guidance: "화면 설정을 확인하거나 잠시 후 다시 시도해 주세요." });
|
||||||
} finally {
|
} finally {
|
||||||
setIsLoadingPreview(false);
|
setIsLoadingPreview(false);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import {
|
||||||
import { generateComponentId } from "@/lib/utils/generateId";
|
import { generateComponentId } from "@/lib/utils/generateId";
|
||||||
import { screenApi } from "@/lib/api/screen";
|
import { screenApi } from "@/lib/api/screen";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
import { showErrorToast } from "@/lib/utils/toastUtils";
|
||||||
import { RealtimePreview } from "./RealtimePreviewDynamic";
|
import { RealtimePreview } from "./RealtimePreviewDynamic";
|
||||||
import DesignerToolbar from "./DesignerToolbar";
|
import DesignerToolbar from "./DesignerToolbar";
|
||||||
|
|
||||||
|
|
@ -53,7 +54,7 @@ export default function SimpleScreenDesigner({ selectedScreen, onBackToList }: S
|
||||||
toast.success("화면이 저장되었습니다.");
|
toast.success("화면이 저장되었습니다.");
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// console.error("저장 실패:", error);
|
// console.error("저장 실패:", error);
|
||||||
toast.error("저장 중 오류가 발생했습니다.");
|
showErrorToast("화면 설정 저장에 실패했습니다", error, { guidance: "잠시 후 다시 시도해 주세요." });
|
||||||
} finally {
|
} finally {
|
||||||
setIsSaving(false);
|
setIsSaving(false);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ import {
|
||||||
} from "@/components/ui/table";
|
} from "@/components/ui/table";
|
||||||
import { Textarea } from "@/components/ui/textarea";
|
import { Textarea } from "@/components/ui/textarea";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
import { showErrorToast } from "@/lib/utils/toastUtils";
|
||||||
import { Plus, Pencil, Trash2, Link2, Database } from "lucide-react";
|
import { Plus, Pencil, Trash2, Link2, Database } from "lucide-react";
|
||||||
import {
|
import {
|
||||||
getFieldJoins,
|
getFieldJoins,
|
||||||
|
|
@ -155,7 +156,7 @@ export default function FieldJoinPanel({ screenId, componentId, layoutId }: Fiel
|
||||||
toast.error(response.message || "저장에 실패했습니다.");
|
toast.error(response.message || "저장에 실패했습니다.");
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
toast.error("저장 중 오류가 발생했습니다.");
|
showErrorToast("필드 조인 설정 저장에 실패했습니다", error, { guidance: "입력 데이터를 확인하고 다시 시도해 주세요." });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -172,7 +173,7 @@ export default function FieldJoinPanel({ screenId, componentId, layoutId }: Fiel
|
||||||
toast.error(response.message || "삭제에 실패했습니다.");
|
toast.error(response.message || "삭제에 실패했습니다.");
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
toast.error("삭제 중 오류가 발생했습니다.");
|
showErrorToast("필드 조인 설정 삭제에 실패했습니다", error, { guidance: "잠시 후 다시 시도해 주세요." });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ import { FileInfo, FileUploadResponse } from "@/lib/registry/components/file-upl
|
||||||
import { uploadFiles, downloadFile, deleteFile } from "@/lib/api/file";
|
import { uploadFiles, downloadFile, deleteFile } from "@/lib/api/file";
|
||||||
import { formatFileSize, cn } from "@/lib/utils";
|
import { formatFileSize, cn } from "@/lib/utils";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
import { showErrorToast } from "@/lib/utils/toastUtils";
|
||||||
|
|
||||||
interface FileComponentConfigPanelProps {
|
interface FileComponentConfigPanelProps {
|
||||||
component: FileComponent;
|
component: FileComponent;
|
||||||
|
|
@ -536,7 +537,7 @@ export const FileComponentConfigPanel: React.FC<FileComponentConfigPanelProps> =
|
||||||
// fieldName
|
// fieldName
|
||||||
// });
|
// });
|
||||||
toast.dismiss();
|
toast.dismiss();
|
||||||
toast.error(`파일 업로드에 실패했습니다: ${error?.message || '알 수 없는 오류'}`);
|
showErrorToast("파일 업로드에 실패했습니다", error, { guidance: "파일 크기와 형식을 확인하고 다시 시도해 주세요." });
|
||||||
} finally {
|
} finally {
|
||||||
// console.log("🏁 파일 업로드 완료, 로딩 상태 해제");
|
// console.log("🏁 파일 업로드 완료, 로딩 상태 해제");
|
||||||
setUploading(false);
|
setUploading(false);
|
||||||
|
|
@ -554,7 +555,7 @@ export const FileComponentConfigPanel: React.FC<FileComponentConfigPanelProps> =
|
||||||
toast.success(`${file.realFileName || file.name} 다운로드가 완료되었습니다.`);
|
toast.success(`${file.realFileName || file.name} 다운로드가 완료되었습니다.`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// console.error('파일 다운로드 오류:', error);
|
// console.error('파일 다운로드 오류:', error);
|
||||||
toast.error('파일 다운로드에 실패했습니다.');
|
showErrorToast("파일 다운로드에 실패했습니다", error, { guidance: "파일이 존재하는지 확인하고 다시 시도해 주세요." });
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
|
@ -677,7 +678,7 @@ export const FileComponentConfigPanel: React.FC<FileComponentConfigPanelProps> =
|
||||||
toast.success('파일이 삭제되었습니다.');
|
toast.success('파일이 삭제되었습니다.');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// console.error('파일 삭제 오류:', error);
|
// console.error('파일 삭제 오류:', error);
|
||||||
toast.error('파일 삭제에 실패했습니다.');
|
showErrorToast("파일 삭제에 실패했습니다", error, { guidance: "잠시 후 다시 시도해 주세요." });
|
||||||
}
|
}
|
||||||
}, [uploadedFiles, onUpdateProperty, component.id]);
|
}, [uploadedFiles, onUpdateProperty, component.id]);
|
||||||
|
|
||||||
|
|
@ -713,7 +714,7 @@ export const FileComponentConfigPanel: React.FC<FileComponentConfigPanelProps> =
|
||||||
toast.success(`${uploadedFiles.length}개 파일이 영구 저장되었습니다.`);
|
toast.success(`${uploadedFiles.length}개 파일이 영구 저장되었습니다.`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// console.error('파일 저장 오류:', error);
|
// console.error('파일 저장 오류:', error);
|
||||||
toast.error('파일 저장에 실패했습니다.');
|
showErrorToast("파일 저장에 실패했습니다", error, { guidance: "파일 크기와 형식을 확인하고 다시 시도해 주세요." });
|
||||||
}
|
}
|
||||||
}, [uploadedFiles, onUpdateProperty, component.id, setGlobalFileState]);
|
}, [uploadedFiles, onUpdateProperty, component.id, setGlobalFileState]);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,7 @@ import { SingleTableWithSticky } from "@/lib/registry/components/table-list/Sing
|
||||||
import type { ColumnConfig } from "@/lib/registry/components/table-list/types";
|
import type { ColumnConfig } from "@/lib/registry/components/table-list/types";
|
||||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
import { showErrorToast } from "@/lib/utils/toastUtils";
|
||||||
import {
|
import {
|
||||||
Pagination,
|
Pagination,
|
||||||
PaginationContent,
|
PaginationContent,
|
||||||
|
|
@ -265,7 +266,7 @@ export function FlowWidget({
|
||||||
setSearchValues({});
|
setSearchValues({});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("필터 설정 저장 실패:", error);
|
console.error("필터 설정 저장 실패:", error);
|
||||||
toast.error("설정 저장에 실패했습니다");
|
showErrorToast("필터 설정 저장에 실패했습니다", error, { guidance: "잠시 후 다시 시도해 주세요." });
|
||||||
}
|
}
|
||||||
}, [filterSettingKey, searchFilterColumns]);
|
}, [filterSettingKey, searchFilterColumns]);
|
||||||
|
|
||||||
|
|
@ -309,7 +310,7 @@ export function FlowWidget({
|
||||||
toast.success("그룹 설정이 저장되었습니다");
|
toast.success("그룹 설정이 저장되었습니다");
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("그룹 설정 저장 실패:", error);
|
console.error("그룹 설정 저장 실패:", error);
|
||||||
toast.error("설정 저장에 실패했습니다");
|
showErrorToast("그룹 설정 저장에 실패했습니다", error, { guidance: "잠시 후 다시 시도해 주세요." });
|
||||||
}
|
}
|
||||||
}, [groupSettingKey, groupByColumns]);
|
}, [groupSettingKey, groupByColumns]);
|
||||||
|
|
||||||
|
|
@ -514,7 +515,7 @@ export function FlowWidget({
|
||||||
}
|
}
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
console.error("❌ 플로우 새로고침 실패:", err);
|
console.error("❌ 플로우 새로고침 실패:", err);
|
||||||
toast.error(err.message || "데이터를 새로고치는데 실패했습니다");
|
showErrorToast("데이터 새로고침에 실패했습니다", err, { guidance: "네트워크 연결을 확인하고 다시 시도해 주세요." });
|
||||||
} finally {
|
} finally {
|
||||||
if (selectedStepId) {
|
if (selectedStepId) {
|
||||||
setStepDataLoading(false);
|
setStepDataLoading(false);
|
||||||
|
|
@ -747,7 +748,7 @@ export function FlowWidget({
|
||||||
}
|
}
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
console.error("Failed to load step data:", err);
|
console.error("Failed to load step data:", err);
|
||||||
toast.error(err.message || "데이터를 불러오는데 실패했습니다");
|
showErrorToast("스텝 데이터를 불러오는 데 실패했습니다", err, { guidance: "네트워크 연결을 확인해 주세요." });
|
||||||
} finally {
|
} finally {
|
||||||
setStepDataLoading(false);
|
setStepDataLoading(false);
|
||||||
}
|
}
|
||||||
|
|
@ -1023,7 +1024,7 @@ export function FlowWidget({
|
||||||
toast.success(`${exportData.length}개 행이 Excel로 내보내기 되었습니다.`);
|
toast.success(`${exportData.length}개 행이 Excel로 내보내기 되었습니다.`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Excel 내보내기 오류:", error);
|
console.error("Excel 내보내기 오류:", error);
|
||||||
toast.error("Excel 내보내기에 실패했습니다.");
|
showErrorToast("Excel 파일 내보내기에 실패했습니다", error, { guidance: "데이터를 확인하고 다시 시도해 주세요." });
|
||||||
}
|
}
|
||||||
}, [sortedDisplayData, selectedRows, stepDataColumns, columnLabels, flowName, getRowKey]);
|
}, [sortedDisplayData, selectedRows, stepDataColumns, columnLabels, flowName, getRowKey]);
|
||||||
|
|
||||||
|
|
@ -1188,7 +1189,7 @@ export function FlowWidget({
|
||||||
toast.success(`${exportData.length}개 행이 PDF로 내보내기 되었습니다.`, { id: "pdf-export" });
|
toast.success(`${exportData.length}개 행이 PDF로 내보내기 되었습니다.`, { id: "pdf-export" });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("PDF 내보내기 오류:", error);
|
console.error("PDF 내보내기 오류:", error);
|
||||||
toast.error("PDF 내보내기에 실패했습니다.", { id: "pdf-export" });
|
showErrorToast("PDF 파일 내보내기에 실패했습니다", error, { guidance: "데이터를 확인하고 다시 시도해 주세요." });
|
||||||
}
|
}
|
||||||
}, [sortedDisplayData, selectedRows, stepDataColumns, columnLabels, flowName, getRowKey]);
|
}, [sortedDisplayData, selectedRows, stepDataColumns, columnLabels, flowName, getRowKey]);
|
||||||
|
|
||||||
|
|
@ -1216,7 +1217,7 @@ export function FlowWidget({
|
||||||
toast.success(`${copyData.length}개 행이 클립보드에 복사되었습니다.`);
|
toast.success(`${copyData.length}개 행이 클립보드에 복사되었습니다.`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("복사 오류:", error);
|
console.error("복사 오류:", error);
|
||||||
toast.error("복사에 실패했습니다.");
|
showErrorToast("클립보드 복사에 실패했습니다", error, { guidance: "브라우저 권한을 확인해 주세요." });
|
||||||
}
|
}
|
||||||
}, [sortedDisplayData, selectedRows, stepDataColumns, columnLabels, getRowKey]);
|
}, [sortedDisplayData, selectedRows, stepDataColumns, columnLabels, getRowKey]);
|
||||||
|
|
||||||
|
|
@ -1318,7 +1319,7 @@ export function FlowWidget({
|
||||||
toast.success("데이터를 새로고침했습니다.");
|
toast.success("데이터를 새로고침했습니다.");
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("새로고침 오류:", error);
|
console.error("새로고침 오류:", error);
|
||||||
toast.error("새로고침에 실패했습니다.");
|
showErrorToast("데이터 새로고침에 실패했습니다", error, { guidance: "잠시 후 다시 시도해 주세요." });
|
||||||
} finally {
|
} finally {
|
||||||
setStepDataLoading(false);
|
setStepDataLoading(false);
|
||||||
}
|
}
|
||||||
|
|
@ -1399,7 +1400,7 @@ export function FlowWidget({
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("편집 저장 오류:", error);
|
console.error("편집 저장 오류:", error);
|
||||||
toast.error("저장 중 오류가 발생했습니다.");
|
showErrorToast("데이터 저장에 실패했습니다", error, { guidance: "입력 데이터를 확인하고 다시 시도해 주세요." });
|
||||||
}
|
}
|
||||||
|
|
||||||
cancelEditing();
|
cancelEditing();
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ import { usePopEvent } from "./usePopEvent";
|
||||||
import { executePopAction } from "./executePopAction";
|
import { executePopAction } from "./executePopAction";
|
||||||
import type { ActionResult } from "./executePopAction";
|
import type { ActionResult } from "./executePopAction";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
import { showErrorToast } from "@/lib/utils/toastUtils";
|
||||||
|
|
||||||
// ========================================
|
// ========================================
|
||||||
// 타입 정의
|
// 타입 정의
|
||||||
|
|
@ -107,7 +108,7 @@ export function usePopAction(screenId: string) {
|
||||||
const msg = ACTION_SUCCESS_MESSAGES[action.type];
|
const msg = ACTION_SUCCESS_MESSAGES[action.type];
|
||||||
if (msg) toast.success(msg);
|
if (msg) toast.success(msg);
|
||||||
} else {
|
} else {
|
||||||
toast.error(result.error || "작업에 실패했습니다.");
|
showErrorToast("작업에 실패했습니다", result.error, { guidance: "잠시 후 다시 시도해 주세요." });
|
||||||
}
|
}
|
||||||
|
|
||||||
// 성공 시 후속 액션 실행
|
// 성공 시 후속 액션 실행
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ import {
|
||||||
AlertDialogTitle,
|
AlertDialogTitle,
|
||||||
} from "@/components/ui/alert-dialog";
|
} from "@/components/ui/alert-dialog";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
import { showErrorToast } from "@/lib/utils/toastUtils";
|
||||||
import { filterDOMProps } from "@/lib/utils/domPropsFilter";
|
import { filterDOMProps } from "@/lib/utils/domPropsFilter";
|
||||||
import { useCurrentFlowStep } from "@/stores/flowStepStore";
|
import { useCurrentFlowStep } from "@/stores/flowStepStore";
|
||||||
import { useScreenPreview } from "@/contexts/ScreenPreviewContext";
|
import { useScreenPreview } from "@/contexts/ScreenPreviewContext";
|
||||||
|
|
@ -955,7 +956,7 @@ export const ButtonPrimaryComponent: React.FC<ButtonPrimaryComponentProps> = ({
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error("❌ 데이터 전달 실패:", error);
|
console.error("❌ 데이터 전달 실패:", error);
|
||||||
toast.error(error.message || "데이터 전달 중 오류가 발생했습니다.");
|
showErrorToast("데이터 전달에 실패했습니다", error, { guidance: "대상 화면 설정과 데이터를 확인해 주세요." });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import { Card, CardContent } from "@/components/ui/card";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Badge } from "@/components/ui/badge";
|
import { Badge } from "@/components/ui/badge";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
import { showErrorToast } from "@/lib/utils/toastUtils";
|
||||||
import { uploadFiles, downloadFile, deleteFile, getComponentFiles } from "@/lib/api/file";
|
import { uploadFiles, downloadFile, deleteFile, getComponentFiles } from "@/lib/api/file";
|
||||||
import { GlobalFileManager } from "@/lib/api/globalFile";
|
import { GlobalFileManager } from "@/lib/api/globalFile";
|
||||||
import { formatFileSize } from "@/lib/utils";
|
import { formatFileSize } from "@/lib/utils";
|
||||||
|
|
@ -881,7 +882,7 @@ const FileUploadComponent: React.FC<FileUploadComponentProps> = ({
|
||||||
console.error("파일 업로드 오류:", error);
|
console.error("파일 업로드 오류:", error);
|
||||||
setUploadStatus("error");
|
setUploadStatus("error");
|
||||||
toast.dismiss("file-upload");
|
toast.dismiss("file-upload");
|
||||||
toast.error(`파일 업로드 오류: ${error instanceof Error ? error.message : "알 수 없는 오류"}`);
|
showErrorToast("파일 업로드에 실패했습니다", error, { guidance: "파일 크기와 형식을 확인하고 다시 시도해 주세요." });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[safeComponentConfig, uploadedFiles, onFormDataChange, component.columnName, component.id, formData],
|
[safeComponentConfig, uploadedFiles, onFormDataChange, component.columnName, component.id, formData],
|
||||||
|
|
@ -1006,7 +1007,7 @@ const FileUploadComponent: React.FC<FileUploadComponentProps> = ({
|
||||||
toast.success(`${fileName} 삭제 완료`);
|
toast.success(`${fileName} 삭제 완료`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("파일 삭제 오류:", error);
|
console.error("파일 삭제 오류:", error);
|
||||||
toast.error("파일 삭제에 실패했습니다.");
|
showErrorToast("파일 삭제에 실패했습니다", error, { guidance: "잠시 후 다시 시도해 주세요." });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[uploadedFiles, onUpdate, component.id, isRecordMode, onFormDataChange, recordTableName, recordId, columnName, getUniqueKey],
|
[uploadedFiles, onUpdate, component.id, isRecordMode, onFormDataChange, recordTableName, recordId, columnName, getUniqueKey],
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,7 @@ import { FileText, ChevronRightIcon, Search } from "lucide-react";
|
||||||
import { Checkbox } from "@/components/ui/checkbox";
|
import { Checkbox } from "@/components/ui/checkbox";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
import { showErrorToast } from "@/lib/utils/toastUtils";
|
||||||
import { tableDisplayStore } from "@/stores/tableDisplayStore";
|
import { tableDisplayStore } from "@/stores/tableDisplayStore";
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
|
|
@ -2491,7 +2492,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||||
console.log("✅ 배치 저장 완료:", pendingChanges.size, "개");
|
console.log("✅ 배치 저장 완료:", pendingChanges.size, "개");
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("❌ 배치 저장 실패:", error);
|
console.error("❌ 배치 저장 실패:", error);
|
||||||
toast.error("저장 중 오류가 발생했습니다.");
|
showErrorToast("데이터 저장에 실패했습니다", error, { guidance: "입력 데이터를 확인하고 다시 시도해 주세요." });
|
||||||
}
|
}
|
||||||
}, [pendingChanges, tableConfig.selectedTable, tableConfig.primaryKey]);
|
}, [pendingChanges, tableConfig.selectedTable, tableConfig.primaryKey]);
|
||||||
|
|
||||||
|
|
@ -2709,7 +2710,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||||
console.log("✅ Excel 내보내기 완료:", fileName);
|
console.log("✅ Excel 내보내기 완료:", fileName);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("❌ Excel 내보내기 실패:", error);
|
console.error("❌ Excel 내보내기 실패:", error);
|
||||||
toast.error("Excel 내보내기 중 오류가 발생했습니다.");
|
showErrorToast("Excel 파일 내보내기에 실패했습니다", error, { guidance: "데이터를 확인하고 다시 시도해 주세요." });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[
|
[
|
||||||
|
|
@ -3623,7 +3624,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||||
console.log("✅ 행 순서 변경:", { from: draggedRowIndex, to: targetIndex });
|
console.log("✅ 행 순서 변경:", { from: draggedRowIndex, to: targetIndex });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("❌ 행 순서 변경 실패:", error);
|
console.error("❌ 행 순서 변경 실패:", error);
|
||||||
toast.error("순서 변경 중 오류가 발생했습니다.");
|
showErrorToast("행 순서 변경에 실패했습니다", error, { guidance: "잠시 후 다시 시도해 주세요." });
|
||||||
}
|
}
|
||||||
|
|
||||||
handleRowDragEnd();
|
handleRowDragEnd();
|
||||||
|
|
@ -3737,7 +3738,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("❌ PDF 내보내기 실패:", error);
|
console.error("❌ PDF 내보내기 실패:", error);
|
||||||
toast.error("PDF 내보내기 중 오류가 발생했습니다.");
|
showErrorToast("PDF 파일 내보내기에 실패했습니다", error, { guidance: "데이터를 확인하고 다시 시도해 주세요." });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[
|
[
|
||||||
|
|
@ -6644,7 +6645,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||||
handleRefresh();
|
handleRefresh();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("삭제 오류:", error);
|
console.error("삭제 오류:", error);
|
||||||
toast.error("삭제 중 오류가 발생했습니다");
|
showErrorToast("데이터 삭제에 실패했습니다", error, { guidance: "삭제 대상을 확인하고 다시 시도해 주세요." });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
closeContextMenu();
|
closeContextMenu();
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ import {
|
||||||
AlertDialogTitle,
|
AlertDialogTitle,
|
||||||
} from "@/components/ui/alert-dialog";
|
} from "@/components/ui/alert-dialog";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
import { showErrorToast } from "@/lib/utils/toastUtils";
|
||||||
import { filterDOMProps } from "@/lib/utils/domPropsFilter";
|
import { filterDOMProps } from "@/lib/utils/domPropsFilter";
|
||||||
import { useCurrentFlowStep } from "@/stores/flowStepStore";
|
import { useCurrentFlowStep } from "@/stores/flowStepStore";
|
||||||
import { useScreenPreview } from "@/contexts/ScreenPreviewContext";
|
import { useScreenPreview } from "@/contexts/ScreenPreviewContext";
|
||||||
|
|
@ -1069,7 +1070,7 @@ export const ButtonPrimaryComponent: React.FC<ButtonPrimaryComponentProps> = ({
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error("❌ 데이터 전달 실패:", error);
|
console.error("❌ 데이터 전달 실패:", error);
|
||||||
toast.error(error.message || "데이터 전달 중 오류가 발생했습니다.");
|
showErrorToast("데이터 전달에 실패했습니다", error, { guidance: "대상 화면 설정과 데이터를 확인해 주세요." });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -181,6 +181,7 @@ import { FileText, ChevronRightIcon } from "lucide-react";
|
||||||
import { Checkbox } from "@/components/ui/checkbox";
|
import { Checkbox } from "@/components/ui/checkbox";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
import { showErrorToast } from "@/lib/utils/toastUtils";
|
||||||
import { tableDisplayStore } from "@/stores/tableDisplayStore";
|
import { tableDisplayStore } from "@/stores/tableDisplayStore";
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
|
|
@ -2577,7 +2578,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||||
|
|
||||||
toast.success(`${pendingChanges.size}개의 변경사항이 저장되었습니다.`);
|
toast.success(`${pendingChanges.size}개의 변경사항이 저장되었습니다.`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
toast.error("저장 중 오류가 발생했습니다.");
|
showErrorToast("데이터 저장에 실패했습니다", error, { guidance: "입력 데이터를 확인하고 다시 시도해 주세요." });
|
||||||
}
|
}
|
||||||
}, [pendingChanges, tableConfig.selectedTable, tableConfig.primaryKey]);
|
}, [pendingChanges, tableConfig.selectedTable, tableConfig.primaryKey]);
|
||||||
|
|
||||||
|
|
@ -2765,7 +2766,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||||
|
|
||||||
toast.success(`${exportData.length}개 행이 Excel로 내보내기 되었습니다.`);
|
toast.success(`${exportData.length}개 행이 Excel로 내보내기 되었습니다.`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
toast.error("Excel 내보내기 중 오류가 발생했습니다.");
|
showErrorToast("Excel 파일 내보내기에 실패했습니다", error, { guidance: "데이터를 확인하고 다시 시도해 주세요." });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[
|
[
|
||||||
|
|
@ -3216,7 +3217,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||||
|
|
||||||
toast.success(`${copyData.length}행 복사됨`);
|
toast.success(`${copyData.length}행 복사됨`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
toast.error("복사 실패");
|
showErrorToast("클립보드 복사에 실패했습니다", error, { guidance: "브라우저 권한을 확인해 주세요." });
|
||||||
}
|
}
|
||||||
}, [selectedRows, filteredData, focusedCell, visibleColumns, columnLabels, getRowKey]);
|
}, [selectedRows, filteredData, focusedCell, visibleColumns, columnLabels, getRowKey]);
|
||||||
|
|
||||||
|
|
@ -3771,7 +3772,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("❌ PDF 내보내기 실패:", error);
|
console.error("❌ PDF 내보내기 실패:", error);
|
||||||
toast.error("PDF 내보내기 중 오류가 발생했습니다.");
|
showErrorToast("PDF 파일 내보내기에 실패했습니다", error, { guidance: "데이터를 확인하고 다시 시도해 주세요." });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[
|
[
|
||||||
|
|
@ -4519,7 +4520,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||||
setSearchValues({});
|
setSearchValues({});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("필터 설정 저장 실패:", error);
|
console.error("필터 설정 저장 실패:", error);
|
||||||
toast.error("설정 저장에 실패했습니다");
|
showErrorToast("필터 설정 저장에 실패했습니다", error, { guidance: "잠시 후 다시 시도해 주세요." });
|
||||||
}
|
}
|
||||||
}, [filterSettingKey, visibleFilterColumns]);
|
}, [filterSettingKey, visibleFilterColumns]);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ import { dataApi } from "@/lib/api/data";
|
||||||
import { executePopAction } from "@/hooks/pop/executePopAction";
|
import { executePopAction } from "@/hooks/pop/executePopAction";
|
||||||
import { usePopEvent } from "@/hooks/pop/usePopEvent";
|
import { usePopEvent } from "@/hooks/pop/usePopEvent";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
import { showErrorToast } from "@/lib/utils/toastUtils";
|
||||||
import type {
|
import type {
|
||||||
PopStringListConfig,
|
PopStringListConfig,
|
||||||
CardGridConfig,
|
CardGridConfig,
|
||||||
|
|
@ -146,10 +147,10 @@ export function PopStringListComponent({
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
toast.success("작업이 완료되었습니다.");
|
toast.success("작업이 완료되었습니다.");
|
||||||
} else {
|
} else {
|
||||||
toast.error(result.error || "작업에 실패했습니다.");
|
showErrorToast("작업에 실패했습니다", result.error, { guidance: "잠시 후 다시 시도해 주세요." });
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
toast.error("알 수 없는 오류가 발생했습니다.");
|
showErrorToast("예기치 않은 오류가 발생했습니다", null, { guidance: "잠시 후 다시 시도해 주세요." });
|
||||||
} finally {
|
} finally {
|
||||||
setLoadingRowIdx(-1);
|
setLoadingRowIdx(-1);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
import { showErrorToast } from "@/lib/utils/toastUtils";
|
||||||
import { screenApi } from "@/lib/api/screen";
|
import { screenApi } from "@/lib/api/screen";
|
||||||
import { DynamicFormApi } from "@/lib/api/dynamicForm";
|
import { DynamicFormApi } from "@/lib/api/dynamicForm";
|
||||||
import { ImprovedButtonActionExecutor } from "@/lib/utils/improvedButtonActionExecutor";
|
import { ImprovedButtonActionExecutor } from "@/lib/utils/improvedButtonActionExecutor";
|
||||||
|
|
@ -456,7 +457,11 @@ export class ButtonActionExecutor {
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("버튼 액션 실행 오류:", error);
|
console.error("버튼 액션 실행 오류:", error);
|
||||||
toast.error(config.errorMessage || "작업 중 오류가 발생했습니다.");
|
showErrorToast(
|
||||||
|
config.errorMessage || `'${config.label || config.type}' 버튼 실행에 실패했습니다`,
|
||||||
|
error,
|
||||||
|
{ guidance: "설정을 확인하거나 잠시 후 다시 시도해 주세요." }
|
||||||
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2652,7 +2657,9 @@ export class ButtonActionExecutor {
|
||||||
return { handled: true, success: true };
|
return { handled: true, success: true };
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error("❌ [handleUniversalFormModalTableSectionSave] 저장 오류:", error);
|
console.error("❌ [handleUniversalFormModalTableSectionSave] 저장 오류:", error);
|
||||||
toast.error(error.message || "저장 중 오류가 발생했습니다.");
|
showErrorToast("테이블 섹션 데이터 저장에 실패했습니다", error, {
|
||||||
|
guidance: "입력값을 확인하고 다시 시도해 주세요.",
|
||||||
|
});
|
||||||
return { handled: true, success: false };
|
return { handled: true, success: false };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2894,7 +2901,9 @@ export class ButtonActionExecutor {
|
||||||
if (failCount === 0) {
|
if (failCount === 0) {
|
||||||
toast.success(`${successCount}개 항목이 저장되었습니다.`);
|
toast.success(`${successCount}개 항목이 저장되었습니다.`);
|
||||||
} else if (successCount === 0) {
|
} else if (successCount === 0) {
|
||||||
toast.error(`저장 실패: ${errors.join(", ")}`);
|
showErrorToast(`${errors.length}개 항목 저장에 모두 실패했습니다`, errors.join("\n"), {
|
||||||
|
guidance: "입력 데이터를 확인하고 다시 시도해 주세요.",
|
||||||
|
});
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
toast.warning(`${successCount}개 성공, ${failCount}개 실패: ${errors.join(", ")}`);
|
toast.warning(`${successCount}개 성공, ${failCount}개 실패: ${errors.join(", ")}`);
|
||||||
|
|
@ -2911,7 +2920,9 @@ export class ButtonActionExecutor {
|
||||||
return true;
|
return true;
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error("배치 저장 오류:", error);
|
console.error("배치 저장 오류:", error);
|
||||||
toast.error(`저장 오류: ${error.message}`);
|
showErrorToast("배치 저장 중 오류가 발생했습니다", error, {
|
||||||
|
guidance: "저장 대상 데이터를 확인하고 다시 시도해 주세요.",
|
||||||
|
});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -3263,7 +3274,9 @@ export class ButtonActionExecutor {
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("❌ 데이터 확인 실패:", error);
|
console.error("❌ 데이터 확인 실패:", error);
|
||||||
toast.error("데이터 확인 중 오류가 발생했습니다.");
|
showErrorToast("상위 데이터 조회에 실패했습니다", error, {
|
||||||
|
guidance: "데이터 소스 연결 상태를 확인해 주세요.",
|
||||||
|
});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -3436,7 +3449,9 @@ export class ButtonActionExecutor {
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("❌ 데이터 확인 실패:", error);
|
console.error("❌ 데이터 확인 실패:", error);
|
||||||
toast.error("데이터 확인 중 오류가 발생했습니다.");
|
showErrorToast("모달 데이터 확인에 실패했습니다", error, {
|
||||||
|
guidance: "데이터 소스를 확인하고 다시 시도해 주세요.",
|
||||||
|
});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3997,7 +4012,9 @@ export class ButtonActionExecutor {
|
||||||
return true;
|
return true;
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error("❌ 복사 액션 실행 중 오류:", error);
|
console.error("❌ 복사 액션 실행 중 오류:", error);
|
||||||
toast.error(`복사 중 오류가 발생했습니다: ${error.message || "알 수 없는 오류"}`);
|
showErrorToast("데이터 복사에 실패했습니다", error, {
|
||||||
|
guidance: "복사 대상 데이터를 확인하고 다시 시도해 주세요.",
|
||||||
|
});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -4228,12 +4245,18 @@ export class ButtonActionExecutor {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
console.error("❌ 노드 플로우 실행 실패:", result);
|
console.error("❌ 노드 플로우 실행 실패:", result);
|
||||||
toast.error(config.errorMessage || result.message || "플로우 실행 중 오류가 발생했습니다.");
|
showErrorToast(
|
||||||
|
config.errorMessage || "플로우 실행에 실패했습니다",
|
||||||
|
result.message,
|
||||||
|
{ guidance: "플로우 설정과 데이터를 확인해 주세요." }
|
||||||
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("❌ 노드 플로우 실행 오류:", error);
|
console.error("❌ 노드 플로우 실행 오류:", error);
|
||||||
toast.error("플로우 실행 중 오류가 발생했습니다.");
|
showErrorToast("플로우 실행 중 오류가 발생했습니다", error, {
|
||||||
|
guidance: "플로우 연결 상태와 데이터를 확인해 주세요.",
|
||||||
|
});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else if (config.dataflowConfig?.controlMode === "relationship" && config.dataflowConfig?.relationshipConfig) {
|
} else if (config.dataflowConfig?.controlMode === "relationship" && config.dataflowConfig?.relationshipConfig) {
|
||||||
|
|
@ -4277,7 +4300,11 @@ export class ButtonActionExecutor {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
console.error("❌ 관계 실행 실패:", executionResult);
|
console.error("❌ 관계 실행 실패:", executionResult);
|
||||||
toast.error(config.errorMessage || "관계 실행 중 오류가 발생했습니다.");
|
showErrorToast(
|
||||||
|
config.errorMessage || "관계 실행에 실패했습니다",
|
||||||
|
executionResult.message || executionResult.error,
|
||||||
|
{ guidance: "관계 설정과 데이터를 확인해 주세요." }
|
||||||
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -4292,7 +4319,9 @@ export class ButtonActionExecutor {
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("제어 조건 검증 중 오류:", error);
|
console.error("제어 조건 검증 중 오류:", error);
|
||||||
toast.error("제어 조건 검증 중 오류가 발생했습니다.");
|
showErrorToast("제어 조건 검증에 실패했습니다", error, {
|
||||||
|
guidance: "제어 설정을 확인해 주세요.",
|
||||||
|
});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -4420,7 +4449,12 @@ export class ButtonActionExecutor {
|
||||||
if (allSuccess) {
|
if (allSuccess) {
|
||||||
toast.success(`${successCount}개 제어 실행 완료`);
|
toast.success(`${successCount}개 제어 실행 완료`);
|
||||||
} else {
|
} else {
|
||||||
toast.error(`제어 실행 중 오류 발생 (${successCount}/${results.length} 성공)`);
|
const failedNames = results.filter((r) => !r.success).map((r) => r.flowName || r.flowId).join(", ");
|
||||||
|
showErrorToast(
|
||||||
|
`제어 실행 중 일부 실패 (${successCount}/${results.length} 성공)`,
|
||||||
|
failedNames ? `실패 항목: ${failedNames}` : undefined,
|
||||||
|
{ guidance: "실패한 제어 플로우 설정을 확인해 주세요." }
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
|
@ -4486,11 +4520,15 @@ export class ButtonActionExecutor {
|
||||||
toast.success("제어 로직 실행이 완료되었습니다.");
|
toast.success("제어 로직 실행이 완료되었습니다.");
|
||||||
} else {
|
} else {
|
||||||
console.error("❌ 저장 후 노드 플로우 실행 실패:", result);
|
console.error("❌ 저장 후 노드 플로우 실행 실패:", result);
|
||||||
toast.error("저장은 완료되었으나 제어 실행 중 오류가 발생했습니다.");
|
showErrorToast("저장은 완료되었으나 후속 제어 실행에 실패했습니다", result.message, {
|
||||||
|
guidance: "제어 플로우 설정을 확인해 주세요. 데이터는 정상 저장되었습니다.",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error("❌ 저장 후 노드 플로우 실행 오류:", error);
|
console.error("❌ 저장 후 노드 플로우 실행 오류:", error);
|
||||||
toast.error(`제어 실행 오류: ${error.message || "알 수 없는 오류"}`);
|
showErrorToast("저장은 완료되었으나 후속 제어 실행에 실패했습니다", error, {
|
||||||
|
guidance: "제어 플로우 설정을 확인해 주세요. 데이터는 정상 저장되었습니다.",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return; // 노드 플로우 실행 후 종료
|
return; // 노드 플로우 실행 후 종료
|
||||||
|
|
@ -4521,7 +4559,9 @@ export class ButtonActionExecutor {
|
||||||
// 성공 토스트는 save 액션에서 이미 표시했으므로 추가로 표시하지 않음
|
// 성공 토스트는 save 액션에서 이미 표시했으므로 추가로 표시하지 않음
|
||||||
} else {
|
} else {
|
||||||
console.error("❌ 저장 후 제어 실행 실패:", executionResult);
|
console.error("❌ 저장 후 제어 실행 실패:", executionResult);
|
||||||
toast.error("저장은 완료되었으나 연결된 제어 실행 중 오류가 발생했습니다.");
|
showErrorToast("저장은 완료되었으나 연결된 제어 실행에 실패했습니다", executionResult.message || executionResult.error, {
|
||||||
|
guidance: "제어 관계 설정을 확인해 주세요. 데이터는 정상 저장되었습니다.",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -4565,9 +4605,10 @@ export class ButtonActionExecutor {
|
||||||
const actionType = action.actionType || action.type;
|
const actionType = action.actionType || action.type;
|
||||||
console.error(`❌ 액션 ${i + 1}/${actions.length} 실행 실패:`, action.name, error);
|
console.error(`❌ 액션 ${i + 1}/${actions.length} 실행 실패:`, action.name, error);
|
||||||
|
|
||||||
// 실패 토스트
|
showErrorToast(
|
||||||
toast.error(
|
`'${action.name || `액션 ${i + 1}`}' 실행에 실패했습니다`,
|
||||||
`${action.name || `액션 ${i + 1}`} 실행 실패: ${error instanceof Error ? error.message : "알 수 없는 오류"}`,
|
error,
|
||||||
|
{ guidance: `전체 ${actions.length}개 액션 중 ${i + 1}번째에서 중단되었습니다.` }
|
||||||
);
|
);
|
||||||
|
|
||||||
// 🚨 순차 실행 중단: 하나라도 실패하면 전체 중단
|
// 🚨 순차 실행 중단: 하나라도 실패하면 전체 중단
|
||||||
|
|
@ -4635,9 +4676,11 @@ export class ButtonActionExecutor {
|
||||||
} else {
|
} else {
|
||||||
throw new Error(result.message || "저장 실패");
|
throw new Error(result.message || "저장 실패");
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error: any) {
|
||||||
console.error("❌ 저장 실패:", error);
|
console.error("❌ 저장 실패:", error);
|
||||||
toast.error(`저장 실패: ${error.message}`);
|
showErrorToast(`'${context.tableName}' 테이블 저장에 실패했습니다`, error, {
|
||||||
|
guidance: "입력 데이터를 확인하고 다시 시도해 주세요.",
|
||||||
|
});
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -4724,9 +4767,11 @@ export class ButtonActionExecutor {
|
||||||
} else {
|
} else {
|
||||||
throw new Error(result.message || "업데이트 실패");
|
throw new Error(result.message || "업데이트 실패");
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error: any) {
|
||||||
console.error("❌ 업데이트 실패:", error);
|
console.error("❌ 업데이트 실패:", error);
|
||||||
toast.error(`업데이트 실패: ${error.message}`);
|
showErrorToast(`'${context.tableName}' 데이터 수정에 실패했습니다`, error, {
|
||||||
|
guidance: "수정 데이터를 확인하고 다시 시도해 주세요.",
|
||||||
|
});
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -4780,9 +4825,11 @@ export class ButtonActionExecutor {
|
||||||
} else {
|
} else {
|
||||||
throw new Error(result.message || "삭제 실패");
|
throw new Error(result.message || "삭제 실패");
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error: any) {
|
||||||
console.error("❌ 삭제 실패:", error);
|
console.error("❌ 삭제 실패:", error);
|
||||||
toast.error(`삭제 실패: ${error.message}`);
|
showErrorToast(`'${context.tableName}' 데이터 삭제에 실패했습니다`, error, {
|
||||||
|
guidance: "삭제 대상을 확인하고 다시 시도해 주세요.",
|
||||||
|
});
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -4863,7 +4910,9 @@ export class ButtonActionExecutor {
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("❌ 삽입 실패:", error);
|
console.error("❌ 삽입 실패:", error);
|
||||||
toast.error(`삽입 실패: ${error instanceof Error ? error.message : "알 수 없는 오류"}`);
|
showErrorToast("데이터 삽입에 실패했습니다", error, {
|
||||||
|
guidance: "필수 입력 항목과 데이터 형식을 확인해 주세요.",
|
||||||
|
});
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -4964,7 +5013,9 @@ export class ButtonActionExecutor {
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("❌ 이력 모달 열기 실패:", error);
|
console.error("❌ 이력 모달 열기 실패:", error);
|
||||||
toast.error("이력 조회 중 오류가 발생했습니다.");
|
showErrorToast("이력 조회에 실패했습니다", error, {
|
||||||
|
guidance: "이력 테이블 설정을 확인해 주세요.",
|
||||||
|
});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -5004,7 +5055,9 @@ export class ButtonActionExecutor {
|
||||||
columnLabels![col] = downloadResponse.data.headers[index] || col;
|
columnLabels![col] = downloadResponse.data.headers[index] || col;
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
toast.error("마스터-디테일 데이터 조회에 실패했습니다.");
|
showErrorToast("마스터-디테일 데이터 조회에 실패했습니다", null, {
|
||||||
|
guidance: "데이터 소스 설정을 확인해 주세요.",
|
||||||
|
});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -5094,12 +5147,16 @@ export class ButtonActionExecutor {
|
||||||
dataToExport = response.data;
|
dataToExport = response.data;
|
||||||
} else {
|
} else {
|
||||||
console.error("❌ 예상치 못한 응답 형식:", response);
|
console.error("❌ 예상치 못한 응답 형식:", response);
|
||||||
toast.error("데이터를 가져오는데 실패했습니다.");
|
showErrorToast("엑셀 데이터 조회에 실패했습니다", null, {
|
||||||
|
guidance: "서버 응답 형식이 예상과 다릅니다. 관리자에게 문의해 주세요.",
|
||||||
|
});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("엑셀 다운로드: 데이터 조회 실패:", error);
|
console.error("엑셀 다운로드: 데이터 조회 실패:", error);
|
||||||
toast.error("데이터를 가져오는데 실패했습니다.");
|
showErrorToast("엑셀 다운로드용 데이터 조회에 실패했습니다", error, {
|
||||||
|
guidance: "네트워크 연결을 확인하고 다시 시도해 주세요.",
|
||||||
|
});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -5109,7 +5166,9 @@ export class ButtonActionExecutor {
|
||||||
}
|
}
|
||||||
// 테이블명도 없고 폼 데이터도 없으면 에러
|
// 테이블명도 없고 폼 데이터도 없으면 에러
|
||||||
else {
|
else {
|
||||||
toast.error("다운로드할 데이터 소스가 없습니다.");
|
toast.error("다운로드할 데이터 소스가 없습니다.", {
|
||||||
|
description: "테이블 또는 폼 데이터가 설정되어 있지 않습니다. 버튼 설정을 확인해 주세요.",
|
||||||
|
});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -5118,13 +5177,17 @@ export class ButtonActionExecutor {
|
||||||
if (typeof dataToExport === "object" && dataToExport !== null) {
|
if (typeof dataToExport === "object" && dataToExport !== null) {
|
||||||
dataToExport = [dataToExport];
|
dataToExport = [dataToExport];
|
||||||
} else {
|
} else {
|
||||||
toast.error("다운로드할 데이터 형식이 올바르지 않습니다.");
|
toast.error("다운로드할 데이터 형식이 올바르지 않습니다.", {
|
||||||
|
description: "서버에서 받은 데이터 형식이 예상과 다릅니다. 관리자에게 문의해 주세요.",
|
||||||
|
});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dataToExport.length === 0) {
|
if (dataToExport.length === 0) {
|
||||||
toast.error("다운로드할 데이터가 없습니다.");
|
toast.error("다운로드할 데이터가 없습니다.", {
|
||||||
|
description: "조회 조건에 맞는 데이터가 없습니다. 검색 조건을 변경해 보세요.",
|
||||||
|
});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -5399,7 +5462,9 @@ export class ButtonActionExecutor {
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("❌ 엑셀 다운로드 실패:", error);
|
console.error("❌ 엑셀 다운로드 실패:", error);
|
||||||
toast.error(config.errorMessage || "엑셀 다운로드 중 오류가 발생했습니다.");
|
showErrorToast(config.errorMessage || "엑셀 파일 다운로드에 실패했습니다", error, {
|
||||||
|
guidance: "데이터를 확인하고 다시 시도해 주세요.",
|
||||||
|
});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -5503,7 +5568,9 @@ export class ButtonActionExecutor {
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("❌ 엑셀 업로드 모달 열기 실패:", error);
|
console.error("❌ 엑셀 업로드 모달 열기 실패:", error);
|
||||||
toast.error(config.errorMessage || "엑셀 업로드 중 오류가 발생했습니다.");
|
showErrorToast(config.errorMessage || "엑셀 업로드 화면을 열 수 없습니다", error, {
|
||||||
|
guidance: "잠시 후 다시 시도해 주세요.",
|
||||||
|
});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -5564,7 +5631,9 @@ export class ButtonActionExecutor {
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("❌ 바코드 스캔 모달 열기 실패:", error);
|
console.error("❌ 바코드 스캔 모달 열기 실패:", error);
|
||||||
toast.error("바코드 스캔 중 오류가 발생했습니다.");
|
showErrorToast("바코드 스캔 화면을 열 수 없습니다", error, {
|
||||||
|
guidance: "카메라 권한을 확인하고 다시 시도해 주세요.",
|
||||||
|
});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -5744,13 +5813,15 @@ export class ButtonActionExecutor {
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
toast.error(response.data.message || "코드 병합에 실패했습니다.");
|
showErrorToast("코드 병합에 실패했습니다", response.data.message, {
|
||||||
|
guidance: "병합 대상 코드를 확인해 주세요.",
|
||||||
|
});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error("❌ 코드 병합 실패:", error);
|
console.error("❌ 코드 병합 실패:", error);
|
||||||
toast.dismiss();
|
toast.dismiss();
|
||||||
toast.error(error.response?.data?.message || "코드 병합 중 오류가 발생했습니다.");
|
showErrorToast("코드 병합 중 오류가 발생했습니다", error.response?.data?.message || error);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -5877,7 +5948,9 @@ export class ButtonActionExecutor {
|
||||||
return true;
|
return true;
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error("❌ 위치 추적 시작 실패:", error);
|
console.error("❌ 위치 추적 시작 실패:", error);
|
||||||
toast.error(config.errorMessage || "위치 추적 시작 중 오류가 발생했습니다.");
|
showErrorToast(config.errorMessage || "위치 추적을 시작할 수 없습니다", error, {
|
||||||
|
guidance: "위치 권한 설정과 GPS 상태를 확인해 주세요.",
|
||||||
|
});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -6131,7 +6204,9 @@ export class ButtonActionExecutor {
|
||||||
return true;
|
return true;
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error("❌ 위치 추적 종료 실패:", error);
|
console.error("❌ 위치 추적 종료 실패:", error);
|
||||||
toast.error(config.errorMessage || "위치 추적 종료 중 오류가 발생했습니다.");
|
showErrorToast(config.errorMessage || "위치 추적 종료에 실패했습니다", error, {
|
||||||
|
guidance: "잠시 후 다시 시도해 주세요.",
|
||||||
|
});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -6425,7 +6500,9 @@ export class ButtonActionExecutor {
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error("❌ 데이터 전달 실패:", error);
|
console.error("❌ 데이터 전달 실패:", error);
|
||||||
toast.error(error.message || "데이터 전달 중 오류가 발생했습니다.");
|
showErrorToast("데이터 전달에 실패했습니다", error, {
|
||||||
|
guidance: "대상 화면 설정과 데이터를 확인해 주세요.",
|
||||||
|
});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -6555,7 +6632,9 @@ export class ButtonActionExecutor {
|
||||||
toast.success(config.successMessage || "공차 등록이 완료되었습니다. 위치 추적을 시작합니다.");
|
toast.success(config.successMessage || "공차 등록이 완료되었습니다. 위치 추적을 시작합니다.");
|
||||||
} catch (saveError) {
|
} catch (saveError) {
|
||||||
console.error("❌ 위치정보 자동 저장 실패:", saveError);
|
console.error("❌ 위치정보 자동 저장 실패:", saveError);
|
||||||
toast.error("위치 정보 저장에 실패했습니다.");
|
showErrorToast("위치 정보 저장에 실패했습니다", saveError, {
|
||||||
|
guidance: "네트워크 연결을 확인해 주세요.",
|
||||||
|
});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -6585,10 +6664,14 @@ export class ButtonActionExecutor {
|
||||||
toast.error("위치 정보 요청 시간이 초과되었습니다.\n다시 시도해주세요.");
|
toast.error("위치 정보 요청 시간이 초과되었습니다.\n다시 시도해주세요.");
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
toast.error(config.errorMessage || "위치 정보를 가져오는 중 오류가 발생했습니다.");
|
showErrorToast(config.errorMessage || "위치 정보를 가져올 수 없습니다", null, {
|
||||||
|
guidance: "브라우저 설정에서 위치 권한을 확인해 주세요.",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
toast.error(config.errorMessage || "위치 정보를 가져오는 중 오류가 발생했습니다.");
|
showErrorToast(config.errorMessage || "위치 정보를 가져올 수 없습니다", error, {
|
||||||
|
guidance: "브라우저 설정에서 위치 권한을 확인해 주세요.",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -6760,7 +6843,9 @@ export class ButtonActionExecutor {
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("❌ 필드 값 교환 오류:", error);
|
console.error("❌ 필드 값 교환 오류:", error);
|
||||||
toast.error(config.errorMessage || "값 교환 중 오류가 발생했습니다.");
|
showErrorToast(config.errorMessage || "필드 값 교환에 실패했습니다", error, {
|
||||||
|
guidance: "교환 대상 필드 설정을 확인해 주세요.",
|
||||||
|
});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
import { showErrorToast } from "@/lib/utils/toastUtils";
|
||||||
import { apiClient } from "@/lib/api/client";
|
import { apiClient } from "@/lib/api/client";
|
||||||
import { ExtendedButtonTypeConfig, ButtonDataflowConfig } from "@/types/control-management";
|
import { ExtendedButtonTypeConfig, ButtonDataflowConfig } from "@/types/control-management";
|
||||||
import { ButtonActionType } from "@/types/v2-core";
|
import { ButtonActionType } from "@/types/v2-core";
|
||||||
|
|
@ -383,7 +384,9 @@ export class ImprovedButtonActionExecutor {
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
toast.success(`관계 '${config.relationshipName}' 실행 완료`);
|
toast.success(`관계 '${config.relationshipName}' 실행 완료`);
|
||||||
} else {
|
} else {
|
||||||
toast.error(`관계 '${config.relationshipName}' 실행 실패: ${result.message}`);
|
showErrorToast(`관계 '${config.relationshipName}' 실행에 실패했습니다`, result.message, {
|
||||||
|
guidance: "관계 설정과 대상 데이터를 확인해 주세요.",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|
@ -396,7 +399,9 @@ export class ImprovedButtonActionExecutor {
|
||||||
error: error.message,
|
error: error.message,
|
||||||
};
|
};
|
||||||
|
|
||||||
toast.error(errorResult.message);
|
showErrorToast(`관계 '${config.relationshipName}' 실행에 실패했습니다`, error, {
|
||||||
|
guidance: "관계 설정과 연결 상태를 확인해 주세요.",
|
||||||
|
});
|
||||||
return errorResult;
|
return errorResult;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1057,7 +1062,8 @@ export class ImprovedButtonActionExecutor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 오류 토스트 표시
|
showErrorToast("버튼 액션 실행 중 오류가 발생했습니다", error, {
|
||||||
toast.error(error.message || "작업 중 오류가 발생했습니다.");
|
guidance: "잠시 후 다시 시도해 주세요. 문제가 계속되면 관리자에게 문의해 주세요.",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,82 @@
|
||||||
|
import { toast } from "sonner";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 서버/catch 에러에서 사용자에게 보여줄 메시지를 추출
|
||||||
|
*/
|
||||||
|
function extractErrorMessage(error: unknown): string | null {
|
||||||
|
if (!error) return null;
|
||||||
|
|
||||||
|
if (error instanceof Error) {
|
||||||
|
return error.message;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof error === "string") {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof error === "object" && error !== null) {
|
||||||
|
const obj = error as Record<string, any>;
|
||||||
|
return (
|
||||||
|
obj.response?.data?.message ||
|
||||||
|
obj.response?.data?.error ||
|
||||||
|
obj.message ||
|
||||||
|
obj.error ||
|
||||||
|
null
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 친절한 에러 토스트를 표시합니다.
|
||||||
|
*
|
||||||
|
* @param title - 어떤 작업에서 문제가 발생했는지 (예: "메뉴 저장에 실패했습니다")
|
||||||
|
* @param error - catch 블록의 error 객체 또는 에러 메시지 문자열
|
||||||
|
* @param options - 추가 옵션
|
||||||
|
* @param options.guidance - 사용자에게 안내할 해결 방법 (예: "네트워크 연결을 확인해 주세요")
|
||||||
|
* @param options.duration - 토스트 표시 시간 (ms)
|
||||||
|
*/
|
||||||
|
export function showErrorToast(
|
||||||
|
title: string,
|
||||||
|
error?: unknown,
|
||||||
|
options?: {
|
||||||
|
guidance?: string;
|
||||||
|
duration?: number;
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
const errorMessage = extractErrorMessage(error);
|
||||||
|
const guidance = options?.guidance;
|
||||||
|
|
||||||
|
const descriptionParts: string[] = [];
|
||||||
|
if (errorMessage) descriptionParts.push(errorMessage);
|
||||||
|
if (guidance) descriptionParts.push(guidance);
|
||||||
|
|
||||||
|
const description =
|
||||||
|
descriptionParts.length > 0 ? descriptionParts.join("\n") : undefined;
|
||||||
|
|
||||||
|
toast.error(title, {
|
||||||
|
description,
|
||||||
|
duration: options?.duration || 5000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* API 응답 기반 에러 토스트
|
||||||
|
* API 호출 실패 시 응답 메시지를 포함하여 친절하게 표시합니다.
|
||||||
|
*/
|
||||||
|
export function showApiErrorToast(
|
||||||
|
action: string,
|
||||||
|
response?: { message?: string; error?: string } | null,
|
||||||
|
fallbackError?: unknown
|
||||||
|
) {
|
||||||
|
const apiMessage = response?.message || response?.error;
|
||||||
|
const errorMessage = apiMessage || extractErrorMessage(fallbackError);
|
||||||
|
|
||||||
|
const description = errorMessage || "잠시 후 다시 시도해 주세요.";
|
||||||
|
|
||||||
|
toast.error(`${action}에 실패했습니다`, {
|
||||||
|
description,
|
||||||
|
duration: 5000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
@ -13,6 +13,7 @@ import { V2_EVENTS } from "../events/types";
|
||||||
import type { ScheduleType, V2ScheduleGenerateRequestEvent, V2ScheduleGenerateApplyEvent } from "../events/types";
|
import type { ScheduleType, V2ScheduleGenerateRequestEvent, V2ScheduleGenerateApplyEvent } from "../events/types";
|
||||||
import { apiClient } from "@/lib/api/client";
|
import { apiClient } from "@/lib/api/client";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
import { showErrorToast } from "@/lib/utils/toastUtils";
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// 타입 정의
|
// 타입 정의
|
||||||
|
|
@ -230,7 +231,8 @@ export function useScheduleGenerator(scheduleConfig?: ScheduleGenerationConfig |
|
||||||
});
|
});
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error("[ScheduleGeneratorService] 미리보기 오류:", error);
|
console.error("[ScheduleGeneratorService] 미리보기 오류:", error);
|
||||||
toast.error("스케줄 생성 중 오류가 발생했습니다.", { id: "schedule-generate" });
|
toast.dismiss("schedule-generate");
|
||||||
|
showErrorToast("스케줄 미리보기 생성에 실패했습니다", error, { guidance: "스케줄 설정을 확인하고 다시 시도해 주세요." });
|
||||||
v2EventBus.emit(V2_EVENTS.SCHEDULE_GENERATE_ERROR, {
|
v2EventBus.emit(V2_EVENTS.SCHEDULE_GENERATE_ERROR, {
|
||||||
requestId: payload.requestId,
|
requestId: payload.requestId,
|
||||||
error: error.message,
|
error: error.message,
|
||||||
|
|
@ -295,7 +297,8 @@ export function useScheduleGenerator(scheduleConfig?: ScheduleGenerationConfig |
|
||||||
setPreviewResult(null);
|
setPreviewResult(null);
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error("[ScheduleGeneratorService] 적용 오류:", error);
|
console.error("[ScheduleGeneratorService] 적용 오류:", error);
|
||||||
toast.error("스케줄 적용 중 오류가 발생했습니다.", { id: "schedule-apply" });
|
toast.dismiss("schedule-apply");
|
||||||
|
showErrorToast("스케줄 적용에 실패했습니다", error, { guidance: "스케줄 설정과 데이터를 확인하고 다시 시도해 주세요." });
|
||||||
} finally {
|
} finally {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue