[agent-pipeline] pipe-20260329080516-zyud round-2

This commit is contained in:
DDD1542
2026-03-29 18:08:02 +09:00
parent 0fc2101331
commit 729272f44d
17 changed files with 135 additions and 130 deletions
+19 -19
View File
@@ -32,7 +32,7 @@ import { useTabId } from "@/contexts/TabIdContext";
interface ScreenModalState {
isOpen: boolean;
screenId: number | null;
screen_id: number | null;
title: string;
description?: string;
size: "sm" | "md" | "lg" | "xl";
@@ -50,7 +50,7 @@ export const ScreenModal: React.FC<ScreenModalProps> = ({ className }) => {
const [modalState, setModalState] = useState<ScreenModalState>({
isOpen: false,
screenId: null,
screen_id: null,
title: "",
description: "",
size: "md",
@@ -417,7 +417,7 @@ export const ScreenModal: React.FC<ScreenModalProps> = ({ className }) => {
setModalState({
isOpen: true,
screenId,
screen_id: screenId,
title,
description: description || "",
size,
@@ -435,7 +435,7 @@ export const ScreenModal: React.FC<ScreenModalProps> = ({ className }) => {
setModalState({
isOpen: false,
screenId: null,
screen_id: null,
title: "",
description: "",
size: "md",
@@ -465,8 +465,8 @@ export const ScreenModal: React.FC<ScreenModalProps> = ({ className }) => {
setResetKey((prev) => prev + 1);
// 화면 데이터 다시 로드 (채번 규칙 새로 생성)
if (modalState.screenId) {
loadScreenData(modalState.screenId);
if (modalState.screen_id) {
loadScreenData(modalState.screen_id);
}
toast.success("저장되었습니다. 계속 입력하세요.");
@@ -489,10 +489,10 @@ export const ScreenModal: React.FC<ScreenModalProps> = ({ className }) => {
// 화면 데이터 로딩
useEffect(() => {
if (modalState.isOpen && modalState.screenId) {
loadScreenData(modalState.screenId);
if (modalState.isOpen && modalState.screen_id) {
loadScreenData(modalState.screen_id);
}
}, [modalState.isOpen, modalState.screenId]);
}, [modalState.isOpen, modalState.screen_id]);
const loadScreenData = async (screenId: number) => {
try {
@@ -921,7 +921,7 @@ export const ScreenModal: React.FC<ScreenModalProps> = ({ className }) => {
setModalState({
isOpen: false,
screenId: null,
screen_id: null,
title: "",
size: "md",
});
@@ -980,11 +980,11 @@ export const ScreenModal: React.FC<ScreenModalProps> = ({ className }) => {
let newModalId: string | undefined;
// 1순위: screenId (가장 안정적)
if (modalState.screenId) {
newModalId = `screen-modal-${modalState.screenId}`;
if (modalState.screen_id) {
newModalId = `screen-modal-${modalState.screen_id}`;
// console.log("🔑 ScreenModal modalId 생성:", {
// method: "screenId",
// screenId: modalState.screenId,
// screenId: modalState.screen_id,
// result: newModalId,
// });
}
@@ -1022,7 +1022,7 @@ export const ScreenModal: React.FC<ScreenModalProps> = ({ className }) => {
}
}, [
modalState.isOpen,
modalState.screenId,
modalState.screen_id,
modalState.title,
screenData?.screenInfo?.tableName,
screenData?.screenInfo?.screenName,
@@ -1075,7 +1075,7 @@ export const ScreenModal: React.FC<ScreenModalProps> = ({ className }) => {
</div>
) : screenData ? (
<ScreenContextProvider
screenId={modalState.screenId || undefined}
screenId={modalState.screen_id || undefined}
tableName={screenData.screenInfo?.tableName}
>
<ActiveTabProvider>
@@ -1251,13 +1251,13 @@ export const ScreenModal: React.FC<ScreenModalProps> = ({ className }) => {
window.dispatchEvent(new CustomEvent("refreshTable"));
}}
screenInfo={{
id: modalState.screenId!,
id: modalState.screen_id!,
tableName: screenData.screenInfo?.tableName,
}}
groupedData={selectedData}
userId={userId}
userName={userName}
companyCode={user?.company_code || user?.companyCode}
companyCode={user?.company_code}
isInModal={true}
/>
);
@@ -1296,12 +1296,12 @@ export const ScreenModal: React.FC<ScreenModalProps> = ({ className }) => {
window.dispatchEvent(new CustomEvent("refreshTable"));
}}
screenInfo={{
id: modalState.screenId!,
id: modalState.screen_id!,
tableName: screenData?.screenInfo?.tableName,
}}
userId={userId}
userName={userName}
companyCode={user?.company_code || user?.companyCode}
companyCode={user?.company_code}
isInModal={true}
/>
);
@@ -38,8 +38,8 @@ interface Vehicle {
status: "active" | "inactive" | "maintenance" | "warning" | "off";
speed: number;
destination: string;
userId?: string; // 이동경로 조회용
tripId?: string; // 현재 운행 ID
user_id?: string; // 이동경로 조회용
trip_id?: string; // 현재 운행 ID
}
// 이동경로 좌표
@@ -137,8 +137,8 @@ export default function VehicleMapOnlyWidget({ element, refreshInterval = 30000
: "inactive",
speed: parseFloat(row.speed) || 0,
destination: row.destination || "대기 중",
userId: row.user_id || undefined,
tripId: row.trip_id || undefined,
user_id: row.user_id || undefined,
trip_id: row.trip_id || undefined,
};
})
// 유효한 위도/경도가 있는 차량만 필터링
@@ -160,7 +160,7 @@ export default function VehicleMapOnlyWidget({ element, refreshInterval = 30000
// 이동경로 로드 함수
const loadRoute = async (vehicle: Vehicle) => {
if (!vehicle.userId && !vehicle.tripId) {
if (!vehicle.user_id && !vehicle.trip_id) {
console.log("🛣️ 이동경로 조회 불가: userId 또는 tripId 없음");
return;
}
@@ -175,15 +175,15 @@ export default function VehicleMapOnlyWidget({ element, refreshInterval = 30000
// trip_id가 있으면 해당 운행만, 없으면 user_id로 오늘 전체 조회
let query = "";
if (vehicle.tripId) {
query = `SELECT latitude, longitude, speed, recorded_at
FROM vehicle_location_history
WHERE trip_id = '${vehicle.tripId}'
if (vehicle.trip_id) {
query = `SELECT latitude, longitude, speed, recorded_at
FROM vehicle_location_history
WHERE trip_id = '${vehicle.trip_id}'
ORDER BY recorded_at ASC`;
} else if (vehicle.userId) {
query = `SELECT latitude, longitude, speed, recorded_at
FROM vehicle_location_history
WHERE user_id = '${vehicle.userId}'
} else if (vehicle.user_id) {
query = `SELECT latitude, longitude, speed, recorded_at
FROM vehicle_location_history
WHERE user_id = '${vehicle.user_id}'
AND recorded_at >= '${startOfDay}'
ORDER BY recorded_at ASC`;
}
@@ -352,7 +352,7 @@ export default function VehicleMapOnlyWidget({ element, refreshInterval = 30000
<strong>:</strong> {vehicle.destination}
</div>
{/* 이동경로 버튼 */}
{(vehicle.userId || vehicle.tripId) && (
{(vehicle.user_id || vehicle.trip_id) && (
<div className="mt-2 border-t pt-2">
<button
onClick={() => loadRoute(vehicle)}
+14 -12
View File
@@ -221,8 +221,8 @@ const convertSingleMenu = (menu: MenuItem, allMenus: MenuItem[], userInfo: Exten
icon: getMenuIcon(menu.menu_name_kor || menu.MENU_NAME_KOR || "", menu.menu_icon || menu.MENU_ICON),
url: menuUrl,
screenCode,
screenId,
menuType,
screen_id: screenId,
menu_type: menuType,
children: children.length > 0 ? children : undefined,
hasChildren: children.length > 0,
};
@@ -369,14 +369,14 @@ function AppLayoutInner({ children }: AppLayoutProps) {
}
const menuObjid = parseInt((menu.objid || menu.id)?.toString() || "0");
const isAdminMenu = menu.menuType === "0";
const isAdminMenu = menu.menu_type === "0";
console.log("[handleMenuClick] 메뉴 클릭:", {
menuName,
menuObjid,
menuType: menu.menuType,
menu_type: menu.menu_type,
isAdminMenu,
screenId: menu.screenId,
screen_id: menu.screen_id,
screenCode: menu.screenCode,
url: menu.url,
fullMenu: menu,
@@ -396,9 +396,9 @@ function AppLayoutInner({ children }: AppLayoutProps) {
// 사용자 메뉴 (menu_type = 1, 2): 화면/대시보드 할당
// 1) screenId가 메뉴 URL에서 추출된 경우 바로 screen 탭
if (menu.screenId) {
console.log("[handleMenuClick] → screen 탭 (URL에서 screenId 추출):", menu.screenId);
openTab({ type: "screen", title: menuName, screenId: menu.screenId, menuObjid });
if (menu.screen_id) {
console.log("[handleMenuClick] → screen 탭 (URL에서 screenId 추출):", menu.screen_id);
openTab({ type: "screen", title: menuName, screenId: menu.screen_id, menuObjid });
if (isMobile) setSidebarOpen(false);
return;
}
@@ -410,8 +410,9 @@ function AppLayoutInner({ children }: AppLayoutProps) {
const assignedScreens = await menuScreenApi.getScreensByMenu(menuObjid);
console.log("[handleMenuClick] → 조회 결과:", assignedScreens);
if (assignedScreens.length > 0) {
console.log("[handleMenuClick] → screen 탭 (assignments):", assignedScreens[0].screenId);
openTab({ type: "screen", title: menuName, screenId: assignedScreens[0].screenId, menuObjid });
const assignedScreenId = assignedScreens[0].screen_id;
console.log("[handleMenuClick] → screen 탭 (assignments):", assignedScreenId);
openTab({ type: "screen", title: menuName, screenId: assignedScreenId, menuObjid });
if (isMobile) setSidebarOpen(false);
return;
}
@@ -436,7 +437,7 @@ function AppLayoutInner({ children }: AppLayoutProps) {
return;
}
console.warn("[handleMenuClick] 어떤 조건에도 매칭 안 됨:", { menuName, menuType: menu.menuType, url: menu.url, screenId: menu.screenId });
console.warn("[handleMenuClick] 어떤 조건에도 매칭 안 됨:", { menuName, menu_type: menu.menu_type, url: menu.url, screen_id: menu.screen_id });
toast.warning("이 메뉴에 할당된 화면이 없습니다. 메뉴 설정을 확인해주세요.");
};
@@ -503,7 +504,8 @@ function AppLayoutInner({ children }: AppLayoutProps) {
}
if (activeTab.type === "screen") {
if (activeTab.menuObjid != null && menuObjid === activeTab.menuObjid) return true;
if (activeTab.screenId != null && menu.screenId === activeTab.screenId) return true;
const { screenId: activeTabScreenId } = activeTab;
if (activeTabScreenId != null && menu.screen_id === activeTabScreenId) return true;
}
return false;
},
+8 -8
View File
@@ -91,8 +91,8 @@ interface ProfileModalProps {
selectedImage: string;
isSaving: boolean;
departments: Array<{
deptCode: string;
deptName: string;
dept_code: string;
dept_name: string;
}>;
alertModal: {
isOpen: boolean;
@@ -233,7 +233,7 @@ export function ProfileModal({
/>
) : (
<div className="flex h-full w-full items-center justify-center rounded-full bg-slate-200 text-2xl font-semibold text-slate-700">
{formData.userName?.substring(0, 1)?.toUpperCase() || "U"}
{formData.user_name?.substring(0, 1)?.toUpperCase() || "U"}
</div>
)}
</div>
@@ -284,8 +284,8 @@ export function ProfileModal({
<Label htmlFor="userName"></Label>
<Input
id="userName"
value={formData.userName}
onChange={(e) => onFormChange("userName", e.target.value)}
value={formData.user_name}
onChange={(e) => onFormChange("user_name", e.target.value)}
placeholder="이름을 입력하세요"
/>
</div>
@@ -305,15 +305,15 @@ export function ProfileModal({
<div className="grid grid-cols-2 gap-4">
<div className="space-y-2">
<Label htmlFor="deptName"></Label>
<Select value={formData.deptName} onValueChange={(value) => onFormChange("deptName", value)}>
<Select value={formData.dept_name} onValueChange={(value) => onFormChange("dept_name", value)}>
<SelectTrigger>
<SelectValue placeholder="부서 선택" />
</SelectTrigger>
<SelectContent>
{Array.isArray(departments) && departments.length > 0 ? (
departments.map((department) => (
<SelectItem key={department.deptCode} value={department.deptName}>
{department.deptName}
<SelectItem key={department.dept_code} value={department.dept_name}>
{department.dept_name}
</SelectItem>
))
) : (
+1 -1
View File
@@ -183,7 +183,7 @@ export function TabBar() {
const screens = await menuScreenApi.getScreensByMenu(numericObjid);
if (screens.length > 0) {
openTab(
{ type: "screen", title: menuName, screenId: screens[0].screenId, menuObjid: numericObjid },
{ type: "screen", title: menuName, screenId: screens[0].screen_id, menuObjid: numericObjid },
insertIndex,
);
return;
+4 -3
View File
@@ -238,19 +238,20 @@ function TabPageRenderer({
tab: { id: string; type: string; screenId?: number; menuObjid?: number; adminUrl?: string };
refreshKey: number;
}) {
const { screenId: tabScreenId } = tab;
console.log("[TabPageRenderer] 탭 렌더링:", {
tabId: tab.id,
type: tab.type,
screenId: tab.screenId,
screenId: tabScreenId,
adminUrl: tab.adminUrl,
menuObjid: tab.menuObjid,
});
if (tab.type === "screen" && tab.screenId != null) {
if (tab.type === "screen" && tabScreenId != null) {
return (
<ScreenViewPageWrapper
key={`${tab.id}-${refreshKey}`}
screenIdProp={tab.screenId}
screenIdProp={tabScreenId}
menuObjidProp={tab.menuObjid}
/>
);
@@ -29,7 +29,7 @@ import {
import { toast } from "sonner";
interface LinkedScreenInfo {
screenId: number;
screen_id: number;
screenName: string;
screenCode: string;
references: Array<{
@@ -42,7 +42,7 @@ interface LinkedScreenInfo {
}
interface ScreenEntry {
screenId: number;
screen_id: number;
screenName: string;
newScreenName: string;
newScreenCode: string;
@@ -107,7 +107,7 @@ export function PopDeployModal({
if (isGroupMode && groupScreens) {
setGroupEntries(
groupScreens.map((s) => ({
screenId: s.screen_id ?? s.screenId,
screen_id: s.screen_id!,
screenName: s.screen_name ?? s.screenName,
newScreenName: s.screen_name ?? s.screenName,
newScreenCode: "",
@@ -120,7 +120,7 @@ export function PopDeployModal({
setScreenName(screen.screen_name ?? screen.screenName);
setScreenCode("");
setGroupEntries([]);
analyzeLinks(screen.screen_id ?? screen.screenId);
analyzeLinks(screen.screen_id!);
}
}, [open, screen, groupScreens, isGroupMode]);
@@ -169,13 +169,13 @@ export function PopDeployModal({
const linked: LinkedScreenInfo[] = result.linkedScreenIds.map(
(linkedId) => {
const linkedScreen = allScreens.find(
(s) => (s.screen_id ?? s.screenId) === linkedId,
(s) => s.screen_id === linkedId,
);
const refs = result.references.filter(
(r) => r.targetScreenId === linkedId,
);
return {
screenId: linkedId,
screen_id: linkedId,
screenName: (linkedScreen?.screen_name ?? linkedScreen?.screenName) || `화면 ${linkedId}`,
screenCode: (linkedScreen?.screen_code ?? linkedScreen?.screenCode) || "",
references: refs.map((r) => ({
@@ -211,7 +211,7 @@ export function PopDeployModal({
screensToSend = groupEntries
.filter((e) => e.included && e.newScreenCode)
.map((e) => ({
sourceScreenId: e.screenId,
sourceScreenId: e.screen_id,
screenName: e.newScreenName,
screenCode: e.newScreenCode,
}));
@@ -219,14 +219,14 @@ export function PopDeployModal({
if (!screen || !screenName || !screenCode) return;
screensToSend = [
{
sourceScreenId: screen.screen_id ?? screen.screenId,
sourceScreenId: screen.screen_id!,
screenName,
screenCode,
},
...linkedScreens
.filter((ls) => ls.deploy)
.map((ls) => ({
sourceScreenId: ls.screenId,
sourceScreenId: ls.screen_id,
screenName: ls.newScreenName,
screenCode: ls.newScreenCode,
})),
@@ -283,7 +283,7 @@ export function PopDeployModal({
{isGroupMode
? `"${groupName}" 카테고리의 화면 ${groupScreens!.length}개를 다른 회사로 복사합니다.`
: screen
? `"${screen.screen_name ?? screen.screenName}" (ID: ${screen.screen_id ?? screen.screenId}) 화면을 다른 회사로 복사합니다.`
? `"${screen.screen_name ?? screen.screenName}" (ID: ${screen.screen_id}) 화면을 다른 회사로 복사합니다.`
: "화면을 선택해주세요."}
</DialogDescription>
</DialogHeader>
@@ -340,10 +340,10 @@ export function PopDeployModal({
</div>
{/* 메인 카테고리의 직접 화면 */}
{groupEntries
.filter((e) => groupInfo.screenIds.includes(e.screenId))
.filter((e) => groupInfo.screenIds.includes(e.screen_id))
.map((entry) => (
<div
key={entry.screenId}
key={entry.screen_id}
className="flex items-center gap-2 rounded p-1.5 pl-6 text-xs hover:bg-muted/50"
>
<Checkbox
@@ -351,7 +351,7 @@ export function PopDeployModal({
onCheckedChange={(checked) => {
setGroupEntries((prev) =>
prev.map((e) =>
e.screenId === entry.screenId
e.screen_id === entry.screen_id
? { ...e, included: !!checked }
: e,
),
@@ -363,7 +363,7 @@ export function PopDeployModal({
{entry.screenName}
</span>
<span className="shrink-0 text-muted-foreground">
#{entry.screenId}
#{entry.screen_id}
</span>
</div>
))}
@@ -376,11 +376,11 @@ export function PopDeployModal({
</div>
{groupEntries
.filter((e) =>
child.screenIds.includes(e.screenId),
child.screenIds.includes(e.screen_id),
)
.map((entry) => (
<div
key={entry.screenId}
key={entry.screen_id}
className="flex items-center gap-2 rounded p-1.5 pl-10 text-xs hover:bg-muted/50"
>
<Checkbox
@@ -388,7 +388,7 @@ export function PopDeployModal({
onCheckedChange={(checked) => {
setGroupEntries((prev) =>
prev.map((e) =>
e.screenId === entry.screenId
e.screen_id === entry.screen_id
? { ...e, included: !!checked }
: e,
),
@@ -400,7 +400,7 @@ export function PopDeployModal({
{entry.screenName}
</span>
<span className="shrink-0 text-muted-foreground">
#{entry.screenId}
#{entry.screen_id}
</span>
</div>
))}
@@ -411,7 +411,7 @@ export function PopDeployModal({
<div className="space-y-1">
{groupEntries.map((entry) => (
<div
key={entry.screenId}
key={entry.screen_id}
className="flex items-center gap-2 rounded p-1.5 text-xs hover:bg-muted/50"
>
<Checkbox
@@ -419,7 +419,7 @@ export function PopDeployModal({
onCheckedChange={(checked) => {
setGroupEntries((prev) =>
prev.map((e) =>
e.screenId === entry.screenId
e.screen_id === entry.screen_id
? { ...e, included: !!checked }
: e,
),
@@ -431,7 +431,7 @@ export function PopDeployModal({
{entry.screenName}
</span>
<span className="shrink-0 text-muted-foreground">
#{entry.screenId}
#{entry.screen_id}
</span>
</div>
))}
@@ -484,13 +484,13 @@ export function PopDeployModal({
<div className="space-y-1.5">
{linkedScreens.map((ls) => (
<div
key={ls.screenId}
key={ls.screen_id}
className="flex items-center justify-between rounded bg-background p-2 text-xs"
>
<div className="flex-1">
<div className="font-medium">{ls.screenName}</div>
<div className="text-muted-foreground">
ID: {ls.screenId} |{" "}
ID: {ls.screen_id} |{" "}
{ls.references
.map((r) => r.referenceType)
.join(", ")}
@@ -502,7 +502,7 @@ export function PopDeployModal({
onCheckedChange={(checked) => {
setLinkedScreens((prev) =>
prev.map((item) =>
item.screenId === ls.screenId
item.screen_id === ls.screen_id
? { ...item, deploy: !!checked }
: item,
),
@@ -46,7 +46,7 @@ export function PopScreenPreview({ screen, className }: PopScreenPreviewProps) {
const checkLayout = async () => {
try {
setLoading(true);
const layout = await screenApi.getLayoutPop(screen.screen_id ?? screen.screenId);
const layout = await screenApi.getLayoutPop(screen.screen_id);
// v2 레이아웃: sections는 객체 (Record<string, PopSectionDefinition>)
// v1 레이아웃: sections는 배열
@@ -71,7 +71,7 @@ export function PopScreenPreview({ screen, className }: PopScreenPreviewProps) {
}, [screen]);
// 미리보기 URL
const previewUrl = screen ? `/pop/screens/${screen.screen_id ?? screen.screenId}?preview=true&device=${deviceType}` : null;
const previewUrl = screen ? `/pop/screens/${screen.screen_id}?preview=true&device=${deviceType}` : null;
// 새 탭에서 열기
const openInNewTab = () => {
@@ -114,7 +114,7 @@ export function PopScreenSettingModal({
try {
setLoading(true);
const layout = await screenApi.getLayoutPop(screen.screen_id ?? screen.screenId);
const layout = await screenApi.getLayoutPop(screen.screen_id);
if (layout && layout.subScreens) {
setSubScreens(
@@ -173,7 +173,7 @@ export function PopScreenSettingModal({
// screen_definitions 테이블에 화면명/설명 업데이트
if (screenName !== (screen.screen_name ?? screen.screenName) || screenDescription !== (screen.description || "")) {
await screenApi.updateScreenInfo(screen.screen_id ?? screen.screenId, {
await screenApi.updateScreenInfo(screen.screen_id, {
screenName,
description: screenDescription,
isActive: "Y",
@@ -181,7 +181,7 @@ export function PopScreenSettingModal({
}
// 레이아웃에 하위 화면 정보 저장
const currentLayout = await screenApi.getLayoutPop(screen.screen_id ?? screen.screenId);
const currentLayout = await screenApi.getLayoutPop(screen.screen_id);
const updatedLayout = {
...currentLayout,
version: "pop-1.0",
@@ -192,7 +192,7 @@ export function PopScreenSettingModal({
})),
};
await screenApi.saveLayoutPop(screen.screen_id ?? screen.screenId, updatedLayout);
await screenApi.saveLayoutPop(screen.screen_id, updatedLayout);
toast.success("화면 설정이 저장되었습니다.");
onSave?.(screenUpdate);
@@ -142,19 +142,19 @@ export default function PopViewerWithModals({
const unsubNavigate = subscribe("__pop_navigate__", (payload: unknown) => {
const data = payload as {
screenId?: string;
screen_id?: string;
params?: Record<string, string>;
};
if (!data?.screenId) return;
if (!data?.screen_id) return;
if (data.screenId === "back") {
if (data.screen_id === "back") {
router.back();
} else {
const query = data.params
? "?" + new URLSearchParams(data.params).toString()
: "";
window.location.href = `/pop/screens/${data.screenId}${query}`;
window.location.href = `/pop/screens/${data.screen_id}${query}`;
}
});
+16 -16
View File
@@ -59,7 +59,7 @@ interface LinkedModalScreen {
}
interface CompanyInfo {
companyCode: string;
company_code: string;
companyName: string;
}
@@ -263,10 +263,10 @@ export default function CopyScreenModal({
// 원본 회사와 같은 회사가 선택되어 있으면 다른 회사로 변경
if (sourceCompanyCode && targetCompanyCode === sourceCompanyCode) {
const otherCompany = companies.find(c => c.companyCode !== sourceCompanyCode);
const otherCompany = companies.find(c => c.company_code !== sourceCompanyCode);
if (otherCompany) {
console.log("🔄 원본 회사 선택됨 → 다른 회사로 자동 변경:", otherCompany.companyCode);
setTargetCompanyCode(otherCompany.companyCode);
console.log("🔄 원본 회사 선택됨 → 다른 회사로 자동 변경:", otherCompany.company_code);
setTargetCompanyCode(otherCompany.company_code);
}
}
}, [companies, isOpen, mode, sourceGroup, sourceScreen, targetCompanyCode]);
@@ -317,7 +317,7 @@ export default function CopyScreenModal({
const data = response.data.data || response.data || [];
console.log("📋 회사 목록 데이터:", data);
const mappedCompanies = data.map((c: any) => ({
companyCode: c.company_code,
company_code: c.company_code,
companyName: c.company_name,
}));
console.log("📋 매핑된 회사 목록:", mappedCompanies);
@@ -783,8 +783,8 @@ export default function CopyScreenModal({
company_code: sourceGroupData.company_code || '',
} as any;
}
return { screenId, displayOrder, screenRole, screenData };
}).filter(item => item.screenData && item.screenId); // screenId가 유효한 것만
return { screen_id: screenId, displayOrder, screenRole, screenData };
}).filter(item => item.screenData && item.screen_id); // screen_id가 유효한 것만
// display_order 순으로 정렬
screensWithOrder.sort((a, b) => (a.displayOrder || 0) - (b.displayOrder || 0));
@@ -970,11 +970,11 @@ export default function CopyScreenModal({
} else {
console.log(` ❌ 화면 정보 없음: screenId=${screenId}, screenName=${screenName}`);
}
return { screenId, displayOrder, screenRole, screenData };
}).filter(item => item.screenData && item.screenId); // screenId가 유효한 것만
return { screen_id: screenId, displayOrder, screenRole, screenData };
}).filter(item => item.screenData && item.screen_id); // screen_id가 유효한 것만
console.log(`🔍 매핑 완료: ${screensWithOrder.length}개 화면 복사 예정`);
screensWithOrder.forEach(item => console.log(` - ${item.screenId}: ${item.screenData?.screen_name}`));
screensWithOrder.forEach(item => console.log(` - ${item.screen_id}: ${item.screenData?.screen_name}`));
// display_order 순으로 정렬
screensWithOrder.sort((a, b) => (a.displayOrder || 0) - (b.displayOrder || 0));
@@ -1532,10 +1532,10 @@ export default function CopyScreenModal({
className="mt-1 flex h-8 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-xs ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 sm:h-10 sm:text-sm"
>
{companies
.filter((company) => company.companyCode !== sourceGroup?.company_code)
.filter((company) => company.company_code !== sourceGroup?.company_code)
.map((company) => (
<option key={company.companyCode} value={company.companyCode}>
{company.companyName} ({company.companyCode})
<option key={company.company_code} value={company.company_code}>
{company.companyName} ({company.company_code})
</option>
))}
</select>
@@ -1758,9 +1758,9 @@ export default function CopyScreenModal({
</SelectTrigger>
<SelectContent>
{companies
.filter((company) => company.companyCode !== sourceScreen?.company_code)
.filter((company) => company.company_code !== sourceScreen?.company_code)
.map((company) => (
<SelectItem key={company.companyCode} value={company.companyCode}>
<SelectItem key={company.company_code} value={company.company_code}>
{company.companyName}
</SelectItem>
))}
@@ -60,7 +60,7 @@ export default function CreateScreenModal({ open, onOpenChange, onCreated, isPop
// 화면 코드 자동 생성
const generateCode = async () => {
try {
const companyCode = (user as any)?.company_code || (user as any)?.companyCode || "*";
const companyCode = (user as any)?.company_code || "*";
const generatedCode = await screenApi.generateScreenCode(companyCode);
setScreenCode(generatedCode);
} catch (e) {
@@ -220,7 +220,7 @@ export default function CreateScreenModal({ open, onOpenChange, onCreated, isPop
if (!isValid || submitting) return;
try {
setSubmitting(true);
const companyCode = (user as any)?.company_code || (user as any)?.companyCode || "*";
const companyCode = (user as any)?.company_code || "*";
// 데이터 소스 타입에 따라 다른 정보 전달
const createData: any = {
@@ -248,9 +248,9 @@ export default function CreateScreenModal({ open, onOpenChange, onCreated, isPop
const created = await screenApi.createScreen(createData);
// POP 모드일 경우 빈 POP 레이아웃 자동 생성
if (isPop && created.screenId) {
if (isPop && created.screen_id) {
try {
await screenApi.saveLayoutPop(created.screenId, {
await screenApi.saveLayoutPop(created.screen_id, {
version: "2.0",
components: [],
});
+7 -5
View File
@@ -62,7 +62,8 @@ const findSaveButtonInComponents = (components: any[]): any | null => {
// conditional-container의 sections 내부 탐색
if (comp.componentType === "conditional-container" && comp.componentConfig?.sections) {
for (const section of comp.componentConfig.sections) {
if (section.screenId) {
const { screenId: sectionScreenId } = section;
if (sectionScreenId) {
// 조건부 컨테이너의 내부 화면은 별도로 로드해야 함
// 여기서는 null 반환하고, loadSaveButtonConfig에서 처리
continue;
@@ -203,19 +204,20 @@ export const EditModal: React.FC<EditModalProps> = ({ className }) => {
for (const comp of layoutData.components) {
if (comp.componentType === "conditional-container" && comp.componentConfig?.sections) {
for (const section of comp.componentConfig.sections) {
if (section.screenId) {
const { screenId: sectionScreenId } = section;
if (sectionScreenId) {
try {
const innerLayoutData = await screenApi.getLayout(section.screenId);
const innerLayoutData = await screenApi.getLayout(sectionScreenId);
saveButton = findSaveButtonInComponents(innerLayoutData?.components || []);
if (saveButton) {
// console.log("[EditModal] 조건부 컨테이너 내부에서 저장 버튼 발견:", {
// sectionScreenId: section.screenId,
// sectionScreenId: sectionScreenId,
// sectionLabel: section.label,
// });
break;
}
} catch (innerError) {
// console.warn("[EditModal] 내부 화면 레이아웃 조회 실패:", section.screenId);
// console.warn("[EditModal] 내부 화면 레이아웃 조회 실패:", sectionScreenId);
}
}
}
@@ -1205,7 +1205,7 @@ export const InteractiveDataTable: React.FC<InteractiveDataTableProps> = ({
});
// SaveModal 열기 (등록 모드)
const screenId = component.addModalConfig?.screenId;
const screenId = component.addModalConfig?.screen_id;
if (!screenId) {
toast.error("화면 설정이 필요합니다. 테이블 설정에서 추가 모달 화면을 지정해주세요.");
@@ -1227,7 +1227,7 @@ export const InteractiveDataTable: React.FC<InteractiveDataTableProps> = ({
if (!selectedRowData) return;
const screenId = component.addModalConfig?.screenId;
const screenId = component.addModalConfig?.screen_id;
if (!screenId) {
toast.error("화면 설정이 필요합니다. 테이블 설정에서 수정 모달 화면을 지정해주세요.");
@@ -103,11 +103,11 @@ export const InteractiveScreenViewerDynamic: React.FC<InteractiveScreenViewerPro
// 외부에서 전달받은 사용자 정보가 있으면 우선 사용 (ScreenModal 등에서)
const userName = externalUserName || authUserName;
const user =
externalUserId && externalUserId !== authUser?.userId
externalUserId && externalUserId !== authUser?.user_id
? {
userId: externalUserId,
userName: externalUserName || authUserName || "",
companyCode: externalCompanyCode || authUser?.companyCode || "",
user_id: externalUserId,
user_name: externalUserName || authUserName || "",
company_code: externalCompanyCode || authUser?.company_code || "",
isAdmin: authUser?.isAdmin || false,
}
: authUser;
+6 -6
View File
@@ -32,9 +32,9 @@ export const useProfile = (user: any, refreshUserData: () => Promise<void>, refr
const [modalState, setModalState] = useState<ProfileModalState>({
isOpen: false,
formData: {
userName: "",
user_name: "",
email: "",
deptName: "",
dept_name: "",
positionName: "",
locale: "",
},
@@ -155,9 +155,9 @@ export const useProfile = (user: any, refreshUserData: () => Promise<void>, refr
...prev,
isOpen: true,
formData: {
userName: user.user_name || "",
user_name: user.user_name || "",
email: user.email || "",
deptName: user.dept_name || "",
dept_name: user.dept_name || "",
positionName: user.position_name || "",
locale: user.locale || "KR", // 기본값을 KR로 설정
},
@@ -416,7 +416,7 @@ export const useProfile = (user: any, refreshUserData: () => Promise<void>, refr
// 사용자 정보 저장 데이터 준비
const updateData: any = {
userName: modalState.formData.userName,
userName: modalState.formData.user_name,
email: modalState.formData.email,
locale: modalState.formData.locale,
};
@@ -432,7 +432,7 @@ export const useProfile = (user: any, refreshUserData: () => Promise<void>, refr
// 운전자 정보도 저장 (운전자인 경우)
if (isDriver) {
const driverResponse = await updateDriverProfile({
userName: modalState.formData.userName,
userName: modalState.formData.user_name,
phoneNumber: driverFormData.phoneNumber,
licenseNumber: driverFormData.licenseNumber,
vehicleNumber: driverFormData.vehicleNumber,
+2 -2
View File
@@ -3,9 +3,9 @@
*/
export interface ProfileFormData {
userName: string;
user_name: string;
email: string;
deptName: string;
dept_name: string;
positionName: string;
locale: string;
}