[agent-pipeline] pipe-20260329080516-zyud round-1
This commit is contained in:
@@ -646,10 +646,10 @@ function ReceivedTab() {
|
||||
// ============================================================
|
||||
|
||||
interface UserSearchResult {
|
||||
userId: string;
|
||||
userName: string;
|
||||
positionName?: string;
|
||||
deptName?: string;
|
||||
user_id: string;
|
||||
user_name: string;
|
||||
position_name?: string;
|
||||
dept_name?: string;
|
||||
}
|
||||
|
||||
function formatDateOnly(dateStr?: string) {
|
||||
@@ -722,10 +722,10 @@ function ProxyTab() {
|
||||
const data = res?.data || res || [];
|
||||
const rawUsers: any[] = Array.isArray(data) ? data : [];
|
||||
const users: UserSearchResult[] = rawUsers.map((u: any) => ({
|
||||
userId: u.user_id || "",
|
||||
userName: u.user_name || "",
|
||||
positionName: u.position_name || "",
|
||||
deptName: u.dept_name || "",
|
||||
user_id: u.user_id || "",
|
||||
user_name: u.user_name || "",
|
||||
position_name: u.position_name || "",
|
||||
dept_name: u.dept_name || "",
|
||||
}));
|
||||
setResults(users);
|
||||
} catch {
|
||||
@@ -876,16 +876,16 @@ function ProxyTab() {
|
||||
};
|
||||
|
||||
const selectOrigUser = (user: UserSearchResult) => {
|
||||
setFormOriginalUserId(user.userId);
|
||||
setFormOriginalUserLabel(`${user.userName}${user.deptName ? ` (${user.deptName})` : ""}`);
|
||||
setFormOriginalUserId(user.user_id);
|
||||
setFormOriginalUserLabel(`${user.user_name}${user.dept_name ? ` (${user.dept_name})` : ""}`);
|
||||
setOrigSearchOpen(false);
|
||||
setOrigSearchQuery("");
|
||||
setOrigSearchResults([]);
|
||||
};
|
||||
|
||||
const selectProxyUser = (user: UserSearchResult) => {
|
||||
setFormProxyUserId(user.userId);
|
||||
setFormProxyUserLabel(`${user.userName}${user.deptName ? ` (${user.deptName})` : ""}`);
|
||||
setFormProxyUserId(user.user_id);
|
||||
setFormProxyUserLabel(`${user.user_name}${user.dept_name ? ` (${user.dept_name})` : ""}`);
|
||||
setProxySearchOpen(false);
|
||||
setProxySearchQuery("");
|
||||
setProxySearchResults([]);
|
||||
@@ -1124,15 +1124,15 @@ function ProxyTab() {
|
||||
<div className="max-h-48 overflow-y-auto">
|
||||
{origSearchResults.map((user) => (
|
||||
<div
|
||||
key={user.userId}
|
||||
key={user.user_id}
|
||||
className="hover:bg-accent hover:text-accent-foreground flex cursor-pointer items-center gap-2 px-3 py-2 text-sm"
|
||||
onClick={() => selectOrigUser(user)}
|
||||
>
|
||||
<span className="font-medium">{user.userName}</span>
|
||||
<span className="font-medium">{user.user_name}</span>
|
||||
<span className="text-muted-foreground text-xs">
|
||||
{user.userId}
|
||||
{user.deptName ? ` / ${user.deptName}` : ""}
|
||||
{user.positionName ? ` / ${user.positionName}` : ""}
|
||||
{user.user_id}
|
||||
{user.dept_name ? ` / ${user.dept_name}` : ""}
|
||||
{user.position_name ? ` / ${user.position_name}` : ""}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
@@ -1178,15 +1178,15 @@ function ProxyTab() {
|
||||
<div className="max-h-48 overflow-y-auto">
|
||||
{proxySearchResults.map((user) => (
|
||||
<div
|
||||
key={user.userId}
|
||||
key={user.user_id}
|
||||
className="hover:bg-accent hover:text-accent-foreground flex cursor-pointer items-center gap-2 px-3 py-2 text-sm"
|
||||
onClick={() => selectProxyUser(user)}
|
||||
>
|
||||
<span className="font-medium">{user.userName}</span>
|
||||
<span className="font-medium">{user.user_name}</span>
|
||||
<span className="text-muted-foreground text-xs">
|
||||
{user.userId}
|
||||
{user.deptName ? ` / ${user.deptName}` : ""}
|
||||
{user.positionName ? ` / ${user.positionName}` : ""}
|
||||
{user.user_id}
|
||||
{user.dept_name ? ` / ${user.dept_name}` : ""}
|
||||
{user.position_name ? ` / ${user.position_name}` : ""}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
|
||||
@@ -320,14 +320,14 @@ export default function AuditLogPage() {
|
||||
|
||||
const fetchAuditUsers = useCallback(async () => {
|
||||
try {
|
||||
const result = await getAuditLogUsers(filters.companyCode);
|
||||
const result = await getAuditLogUsers(filters.company_code);
|
||||
if (result.success) {
|
||||
setAuditUsers(result.data);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("사용자 목록 조회 실패:", error);
|
||||
}
|
||||
}, [filters.companyCode]);
|
||||
}, [filters.company_code]);
|
||||
|
||||
const fetchLogs = useCallback(async () => {
|
||||
setLoading(true);
|
||||
@@ -346,14 +346,14 @@ export default function AuditLogPage() {
|
||||
|
||||
const fetchStats = useCallback(async () => {
|
||||
try {
|
||||
const result = await getAuditLogStats(filters.companyCode, 30);
|
||||
const result = await getAuditLogStats(filters.company_code, 30);
|
||||
if (result.success) {
|
||||
setStats(result.data);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("통계 조회 실패:", error);
|
||||
}
|
||||
}, [filters.companyCode]);
|
||||
}, [filters.company_code]);
|
||||
|
||||
useEffect(() => {
|
||||
fetchCompanies();
|
||||
@@ -377,8 +377,8 @@ export default function AuditLogPage() {
|
||||
|
||||
const handleFilterChange = (key: keyof AuditLogFilters, value: string) => {
|
||||
const updates: Partial<AuditLogFilters> = { [key]: value || undefined, page: 1 };
|
||||
if (key === "companyCode") {
|
||||
updates.userId = undefined;
|
||||
if (key === "company_code") {
|
||||
updates.user_id = undefined;
|
||||
}
|
||||
setFilters((prev) => ({ ...prev, ...updates }));
|
||||
};
|
||||
@@ -529,9 +529,9 @@ export default function AuditLogPage() {
|
||||
aria-expanded={companyComboOpen}
|
||||
className="h-9 w-full justify-between text-xs"
|
||||
>
|
||||
{filters.companyCode
|
||||
? companies.find((c) => c.company_code === filters.companyCode)
|
||||
?.company_name || filters.companyCode
|
||||
{filters.company_code
|
||||
? companies.find((c) => c.company_code === filters.company_code)
|
||||
?.company_name || filters.company_code
|
||||
: "전체 회사"}
|
||||
<ChevronsUpDown className="ml-2 h-3 w-3 shrink-0 opacity-50" />
|
||||
</Button>
|
||||
@@ -551,7 +551,7 @@ export default function AuditLogPage() {
|
||||
<CommandItem
|
||||
value="__all_companies__"
|
||||
onSelect={() => {
|
||||
handleFilterChange("companyCode", "");
|
||||
handleFilterChange("company_code", "");
|
||||
setCompanyComboOpen(false);
|
||||
}}
|
||||
className="text-xs"
|
||||
@@ -559,7 +559,7 @@ export default function AuditLogPage() {
|
||||
<Check
|
||||
className={cn(
|
||||
"mr-2 h-3 w-3",
|
||||
!filters.companyCode ? "opacity-100" : "opacity-0"
|
||||
!filters.company_code ? "opacity-100" : "opacity-0"
|
||||
)}
|
||||
/>
|
||||
전체 회사
|
||||
@@ -570,8 +570,8 @@ export default function AuditLogPage() {
|
||||
value={`${company.company_name} ${company.company_code}`}
|
||||
onSelect={() => {
|
||||
handleFilterChange(
|
||||
"companyCode",
|
||||
filters.companyCode === company.company_code
|
||||
"company_code",
|
||||
filters.company_code === company.company_code
|
||||
? ""
|
||||
: company.company_code
|
||||
);
|
||||
@@ -582,7 +582,7 @@ export default function AuditLogPage() {
|
||||
<Check
|
||||
className={cn(
|
||||
"mr-2 h-3 w-3",
|
||||
filters.companyCode === company.company_code
|
||||
filters.company_code === company.company_code
|
||||
? "opacity-100"
|
||||
: "opacity-0"
|
||||
)}
|
||||
@@ -613,9 +613,9 @@ export default function AuditLogPage() {
|
||||
aria-expanded={userComboOpen}
|
||||
className="h-9 w-full justify-between text-xs"
|
||||
>
|
||||
{filters.userId
|
||||
? auditUsers.find((u) => u.user_id === filters.userId)
|
||||
?.user_name || filters.userId
|
||||
{filters.user_id
|
||||
? auditUsers.find((u) => u.user_id === filters.user_id)
|
||||
?.user_name || filters.user_id
|
||||
: "전체"}
|
||||
<ChevronsUpDown className="ml-2 h-3 w-3 shrink-0 opacity-50" />
|
||||
</Button>
|
||||
@@ -635,7 +635,7 @@ export default function AuditLogPage() {
|
||||
<CommandItem
|
||||
value="__all_users__"
|
||||
onSelect={() => {
|
||||
handleFilterChange("userId", "");
|
||||
handleFilterChange("user_id", "");
|
||||
setUserComboOpen(false);
|
||||
}}
|
||||
className="text-xs"
|
||||
@@ -643,7 +643,7 @@ export default function AuditLogPage() {
|
||||
<Check
|
||||
className={cn(
|
||||
"mr-2 h-3 w-3",
|
||||
!filters.userId ? "opacity-100" : "opacity-0"
|
||||
!filters.user_id ? "opacity-100" : "opacity-0"
|
||||
)}
|
||||
/>
|
||||
전체
|
||||
@@ -654,8 +654,8 @@ export default function AuditLogPage() {
|
||||
value={`${u.user_name} ${u.user_id}`}
|
||||
onSelect={() => {
|
||||
handleFilterChange(
|
||||
"userId",
|
||||
filters.userId === u.user_id ? "" : u.user_id
|
||||
"user_id",
|
||||
filters.user_id === u.user_id ? "" : u.user_id
|
||||
);
|
||||
setUserComboOpen(false);
|
||||
}}
|
||||
@@ -664,7 +664,7 @@ export default function AuditLogPage() {
|
||||
<Check
|
||||
className={cn(
|
||||
"mr-2 h-3 w-3",
|
||||
filters.userId === u.user_id
|
||||
filters.user_id === u.user_id
|
||||
? "opacity-100"
|
||||
: "opacity-0"
|
||||
)}
|
||||
|
||||
@@ -456,8 +456,8 @@ export default function MenuPage() {
|
||||
if (response.data.success) {
|
||||
// console.log("🏢 회사 목록 응답:", response.data);
|
||||
const companyList = response.data.data.map((company: any) => ({
|
||||
code: company.company_code ?? company.companyCode,
|
||||
name: company.company_name ?? company.companyName,
|
||||
code: company.company_code,
|
||||
name: company.company_name,
|
||||
}));
|
||||
// console.log("🏢 변환된 회사 목록:", companyList);
|
||||
setCompanies(companyList);
|
||||
|
||||
@@ -428,7 +428,7 @@ export default function DashboardDesignerPage({ params }: { params: Promise<{ id
|
||||
title: string;
|
||||
description: string;
|
||||
assignToMenu: boolean;
|
||||
menuType?: "admin" | "user";
|
||||
menu_type?: "admin" | "user";
|
||||
menuId?: string;
|
||||
}) => {
|
||||
try {
|
||||
@@ -514,7 +514,7 @@ export default function DashboardDesignerPage({ params }: { params: Promise<{ id
|
||||
|
||||
// 대시보드 URL 생성 (관리자 메뉴면 mode=admin 추가)
|
||||
let dashboardUrl = `/dashboard/${savedDashboard.id}`;
|
||||
if (data.menuType === "admin") {
|
||||
if (data.menu_type === "admin") {
|
||||
dashboardUrl += "?mode=admin";
|
||||
}
|
||||
|
||||
|
||||
@@ -116,7 +116,7 @@ export default function PopScreenManagementPage() {
|
||||
const openDesignerId = searchParams.get("openDesigner");
|
||||
if (openDesignerId && screens.length > 0) {
|
||||
const screenId = parseInt(openDesignerId, 10);
|
||||
const targetScreen = screens.find((s) => s.screenId === screenId);
|
||||
const targetScreen = screens.find((s) => s.screen_id === screenId);
|
||||
if (targetScreen) {
|
||||
setSelectedScreen(targetScreen);
|
||||
setCurrentStep("design");
|
||||
@@ -162,7 +162,7 @@ export default function PopScreenManagementPage() {
|
||||
|
||||
// POP 화면 미리보기 (새 탭에서 열기)
|
||||
const handlePreviewScreen = (screen: ScreenDefinition) => {
|
||||
const previewUrl = `/pop/screens/${screen.screenId}?preview=true&device=${devicePreview}`;
|
||||
const previewUrl = `/pop/screens/${screen.screen_id}?preview=true&device=${devicePreview}`;
|
||||
window.open(previewUrl, "_blank", "width=800,height=900");
|
||||
};
|
||||
|
||||
@@ -178,7 +178,7 @@ export default function PopScreenManagementPage() {
|
||||
// ============================================================
|
||||
|
||||
// POP 레이아웃이 있는 화면만 필터링
|
||||
const popScreens = screens.filter((screen) => popLayoutScreenIds.has(screen.screenId));
|
||||
const popScreens = screens.filter((screen) => popLayoutScreenIds.has(screen.screen_id));
|
||||
|
||||
// 검색어 필터링
|
||||
const filteredScreens = popScreens.filter((screen) => {
|
||||
@@ -210,7 +210,7 @@ export default function PopScreenManagementPage() {
|
||||
});
|
||||
setScreens((prev) =>
|
||||
prev.map((s) =>
|
||||
s.screenId === selectedScreen.screenId
|
||||
s.screen_id === selectedScreen.screen_id
|
||||
? { ...s, ...updatedFields }
|
||||
: s
|
||||
)
|
||||
|
||||
@@ -83,7 +83,7 @@ export default function ScreenManagementPage() {
|
||||
const openDesignerId = searchParams.get("openDesigner");
|
||||
if (openDesignerId && screens.length > 0) {
|
||||
const screenId = parseInt(openDesignerId, 10);
|
||||
const targetScreen = screens.find((s) => s.screenId === screenId);
|
||||
const targetScreen = screens.find((s) => s.screen_id === screenId);
|
||||
if (targetScreen) {
|
||||
setSelectedScreen(targetScreen);
|
||||
setCurrentStep("design");
|
||||
@@ -147,7 +147,7 @@ export default function ScreenManagementPage() {
|
||||
setSelectedScreen(updated);
|
||||
setScreens((prev) =>
|
||||
prev.map((s) =>
|
||||
s.screenId === selectedScreen.screenId
|
||||
s.screen_id === selectedScreen.screen_id
|
||||
? { ...s, ...updatedFields }
|
||||
: s
|
||||
)
|
||||
@@ -326,7 +326,7 @@ export default function ScreenManagementPage() {
|
||||
<div className="grid grid-cols-2 xl:grid-cols-3 2xl:grid-cols-4 gap-3">
|
||||
{filteredScreens.map((screen) => {
|
||||
const screenType = (screen as { screenType?: string }).screenType || "form";
|
||||
const isSelected = selectedScreen?.screenId === screen.screenId;
|
||||
const isSelected = selectedScreen?.screen_id === screen.screen_id;
|
||||
const isRecentlyModified = screen.updatedDate && (Date.now() - new Date(screen.updatedDate).getTime()) < 7 * 24 * 60 * 60 * 1000;
|
||||
|
||||
const typeColorClass = screenType === "grid"
|
||||
@@ -349,7 +349,7 @@ export default function ScreenManagementPage() {
|
||||
|
||||
return (
|
||||
<div
|
||||
key={screen.screenId}
|
||||
key={screen.screen_id}
|
||||
className={`group relative overflow-hidden rounded-[12px] cursor-pointer transition-all duration-250 ease-[cubic-bezier(0.4,0,0.2,1)] ${
|
||||
isSelected
|
||||
? "border border-primary bg-primary/5 dark:bg-primary/8 shadow-[0_0_0_2px_hsl(var(--primary)/0.22),0_1px_3px_rgba(0,0,0,0.06)] dark:shadow-[0_0_0_2px_hsl(var(--primary)/0.3),0_1px_4px_rgba(0,0,0,0.3)]"
|
||||
@@ -422,7 +422,7 @@ export default function ScreenManagementPage() {
|
||||
</div>
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-xs text-muted-foreground">화면 ID</span>
|
||||
<span className="text-xs font-mono">{selectedScreen.screenId}</span>
|
||||
<span className="text-xs font-mono">{selectedScreen.screen_id}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex gap-2 pt-4 border-t border-border/50">
|
||||
|
||||
@@ -889,8 +889,8 @@ export default function I18nPage() {
|
||||
isOpen={isGenerateModalOpen}
|
||||
onClose={() => setIsGenerateModalOpen(false)}
|
||||
selectedCategory={selectedCategory}
|
||||
companyCode={user?.companyCode || ""}
|
||||
isSuperAdmin={user?.companyCode === "*"}
|
||||
companyCode={user?.company_code || ""}
|
||||
isSuperAdmin={user?.company_code === "*"}
|
||||
onSuccess={() => {
|
||||
fetchLangKeys(selectedCategory?.categoryId);
|
||||
}}
|
||||
|
||||
@@ -17,7 +17,7 @@ import { getCompanyList } from "@/lib/api/company";
|
||||
export default function DepartmentManagementPage() {
|
||||
const params = useParams();
|
||||
const router = useRouter();
|
||||
const companyCode = params.companyCode as string;
|
||||
const { companyCode } = params as { companyCode: string };
|
||||
const [selectedDepartment, setSelectedDepartment] = useState<Department | null>(null);
|
||||
const [activeTab, setActiveTab] = useState<string>("structure");
|
||||
const [companyName, setCompanyName] = useState<string>("");
|
||||
|
||||
@@ -45,8 +45,8 @@ export default function UserMngPage() {
|
||||
// 비밀번호 초기화 모달 상태
|
||||
const [passwordResetModal, setPasswordResetModal] = useState({
|
||||
isOpen: false,
|
||||
userId: null as string | null,
|
||||
userName: null as string | null,
|
||||
user_id: null as string | null,
|
||||
user_name: null as string | null,
|
||||
});
|
||||
|
||||
// 사용자 등록/수정 모달 상태
|
||||
@@ -89,8 +89,8 @@ export default function UserMngPage() {
|
||||
const handlePasswordReset = (userId: string, userName: string) => {
|
||||
setPasswordResetModal({
|
||||
isOpen: true,
|
||||
userId,
|
||||
userName,
|
||||
user_id: userId,
|
||||
user_name: userName,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -98,8 +98,8 @@ export default function UserMngPage() {
|
||||
const handlePasswordResetClose = () => {
|
||||
setPasswordResetModal({
|
||||
isOpen: false,
|
||||
userId: null,
|
||||
userName: null,
|
||||
user_id: null,
|
||||
user_name: null,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -177,8 +177,8 @@ export default function UserMngPage() {
|
||||
<UserPasswordResetModal
|
||||
isOpen={passwordResetModal.isOpen}
|
||||
onClose={handlePasswordResetClose}
|
||||
userId={passwordResetModal.userId}
|
||||
userName={passwordResetModal.userName}
|
||||
userId={passwordResetModal.user_id}
|
||||
userName={passwordResetModal.user_name}
|
||||
onSuccess={handlePasswordResetSuccess}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -44,7 +44,8 @@ function ScreenViewPage({ screenIdProp, menuObjidProp }: ScreenViewPageProps = {
|
||||
const params = useParams();
|
||||
const searchParams = useSearchParams();
|
||||
const router = useRouter();
|
||||
const screenId = screenIdProp ?? parseInt(params.screenId as string);
|
||||
const { screenId: screenIdFromParams } = params as { screenId: string };
|
||||
const screenId = screenIdProp ?? parseInt(screenIdFromParams);
|
||||
|
||||
// props 우선, 없으면 URL 쿼리에서 menuObjid 가져오기
|
||||
const menuObjid =
|
||||
@@ -99,7 +100,7 @@ function ScreenViewPage({ screenIdProp, menuObjidProp }: ScreenViewPageProps = {
|
||||
// 편집 모달 상태
|
||||
const [editModalOpen, setEditModalOpen] = useState(false);
|
||||
const [editModalConfig, setEditModalConfig] = useState<{
|
||||
screenId?: number;
|
||||
screen_id?: number;
|
||||
modalSize?: "sm" | "md" | "lg" | "xl" | "full";
|
||||
editData?: Record<string, unknown>;
|
||||
onSave?: () => void;
|
||||
@@ -132,13 +133,21 @@ function ScreenViewPage({ screenIdProp, menuObjidProp }: ScreenViewPageProps = {
|
||||
const currentActiveTabId = state[state.mode].activeTabId;
|
||||
if (tabId && tabId !== currentActiveTabId) return;
|
||||
|
||||
const { screenId: detailScreenId, modalSize, editData, onSave, modalTitle, modalDescription } = event.detail as {
|
||||
screenId?: number;
|
||||
modalSize?: "sm" | "md" | "lg" | "xl" | "full";
|
||||
editData?: Record<string, unknown>;
|
||||
onSave?: () => void;
|
||||
modalTitle?: string;
|
||||
modalDescription?: string;
|
||||
};
|
||||
setEditModalConfig({
|
||||
screenId: event.detail.screenId,
|
||||
modalSize: event.detail.modalSize,
|
||||
editData: event.detail.editData,
|
||||
onSave: event.detail.onSave,
|
||||
modalTitle: event.detail.modalTitle,
|
||||
modalDescription: event.detail.modalDescription,
|
||||
screen_id: detailScreenId,
|
||||
modalSize,
|
||||
editData,
|
||||
onSave,
|
||||
modalTitle,
|
||||
modalDescription,
|
||||
});
|
||||
setEditModalOpen(true);
|
||||
};
|
||||
@@ -787,7 +796,7 @@ function ScreenViewPage({ screenIdProp, menuObjidProp }: ScreenViewPageProps = {
|
||||
setEditModalOpen(false);
|
||||
setEditModalConfig({});
|
||||
}}
|
||||
screenId={editModalConfig.screenId}
|
||||
screenId={editModalConfig.screen_id}
|
||||
modalSize={editModalConfig.modalSize}
|
||||
editData={editModalConfig.editData}
|
||||
onSave={editModalConfig.onSave}
|
||||
|
||||
@@ -64,7 +64,8 @@ function PopScreenViewPage() {
|
||||
const params = useParams();
|
||||
const searchParams = useSearchParams();
|
||||
const router = useRouter();
|
||||
const screenId = parseInt(params.screenId as string);
|
||||
const { screenId: screenIdFromParams } = params as { screenId: string };
|
||||
const screenId = parseInt(screenIdFromParams);
|
||||
|
||||
const isPreviewMode = searchParams.get("preview") === "true";
|
||||
|
||||
|
||||
@@ -24,9 +24,9 @@ interface LangKeyModalProps {
|
||||
|
||||
export default function LangKeyModal({ isOpen, onClose, onSave, keyData, companies }: LangKeyModalProps) {
|
||||
const [formData, setFormData] = useState({
|
||||
companyCode: "",
|
||||
menuName: "",
|
||||
langKey: "",
|
||||
company_code: "",
|
||||
menu_name: "",
|
||||
lang_key: "",
|
||||
description: "",
|
||||
});
|
||||
|
||||
@@ -34,9 +34,9 @@ export default function LangKeyModal({ isOpen, onClose, onSave, keyData, compani
|
||||
if (keyData) {
|
||||
// 수정 모드
|
||||
setFormData({
|
||||
companyCode: keyData.company_code || "",
|
||||
menuName: keyData.menu_name || "",
|
||||
langKey: keyData.lang_key || "",
|
||||
company_code: keyData.company_code || "",
|
||||
menu_name: keyData.menu_name || "",
|
||||
lang_key: keyData.lang_key || "",
|
||||
description: keyData.description || "",
|
||||
});
|
||||
} else {
|
||||
@@ -57,9 +57,9 @@ export default function LangKeyModal({ isOpen, onClose, onSave, keyData, compani
|
||||
|
||||
const handleClose = () => {
|
||||
setFormData({
|
||||
companyCode: "",
|
||||
menuName: "",
|
||||
langKey: "",
|
||||
company_code: "",
|
||||
menu_name: "",
|
||||
lang_key: "",
|
||||
description: "",
|
||||
});
|
||||
onClose();
|
||||
@@ -75,8 +75,8 @@ export default function LangKeyModal({ isOpen, onClose, onSave, keyData, compani
|
||||
<div>
|
||||
<Label htmlFor="companyCode">회사</Label>
|
||||
<Select
|
||||
value={formData.companyCode}
|
||||
onValueChange={(value) => setFormData({ ...formData, companyCode: value })}
|
||||
value={formData.company_code}
|
||||
onValueChange={(value) => setFormData({ ...formData, company_code: value })}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="회사 선택" />
|
||||
@@ -95,8 +95,8 @@ export default function LangKeyModal({ isOpen, onClose, onSave, keyData, compani
|
||||
<Label htmlFor="menuName">메뉴명</Label>
|
||||
<Input
|
||||
id="menuName"
|
||||
value={formData.menuName}
|
||||
onChange={(e) => setFormData({ ...formData, menuName: e.target.value })}
|
||||
value={formData.menu_name}
|
||||
onChange={(e) => setFormData({ ...formData, menu_name: e.target.value })}
|
||||
placeholder="메뉴명 입력"
|
||||
required
|
||||
/>
|
||||
@@ -106,8 +106,8 @@ export default function LangKeyModal({ isOpen, onClose, onSave, keyData, compani
|
||||
<Label htmlFor="langKey">언어 키</Label>
|
||||
<Input
|
||||
id="langKey"
|
||||
value={formData.langKey}
|
||||
onChange={(e) => setFormData({ ...formData, langKey: e.target.value })}
|
||||
value={formData.lang_key}
|
||||
onChange={(e) => setFormData({ ...formData, lang_key: e.target.value })}
|
||||
placeholder="언어 키 입력"
|
||||
required
|
||||
/>
|
||||
|
||||
@@ -32,7 +32,7 @@ interface RoleFormModalProps {
|
||||
* 권한 그룹 생성/수정 모달
|
||||
*
|
||||
* 기능:
|
||||
* - 권한 그룹 생성 (authName, authCode, companyCode)
|
||||
* - 권한 그룹 생성 (authName, authCode, company_code)
|
||||
* - 권한 그룹 수정 (authName, authCode, status)
|
||||
* - 유효성 검사
|
||||
*
|
||||
@@ -49,7 +49,7 @@ export function RoleFormModal({ isOpen, onClose, onSuccess, editingRole }: RoleF
|
||||
const [formData, setFormData] = useState({
|
||||
authName: "",
|
||||
authCode: "",
|
||||
companyCode: currentUser?.company_code || "",
|
||||
company_code: currentUser?.company_code || "",
|
||||
status: "active",
|
||||
});
|
||||
|
||||
@@ -66,7 +66,7 @@ export function RoleFormModal({ isOpen, onClose, onSuccess, editingRole }: RoleF
|
||||
|
||||
// 폼 유효성 검사
|
||||
const isFormValid = useMemo(() => {
|
||||
return formData.authName.trim() !== "" && formData.authCode.trim() !== "" && formData.companyCode.trim() !== "";
|
||||
return formData.authName.trim() !== "" && formData.authCode.trim() !== "" && formData.company_code.trim() !== "";
|
||||
}, [formData]);
|
||||
|
||||
// 알림 표시
|
||||
@@ -108,7 +108,7 @@ export function RoleFormModal({ isOpen, onClose, onSuccess, editingRole }: RoleF
|
||||
setFormData({
|
||||
authName: editingRole.auth_name || "",
|
||||
authCode: editingRole.auth_code || "",
|
||||
companyCode: editingRole.company_code || "",
|
||||
company_code: editingRole.company_code || "",
|
||||
status: editingRole.status || "active",
|
||||
});
|
||||
} else {
|
||||
@@ -116,7 +116,7 @@ export function RoleFormModal({ isOpen, onClose, onSuccess, editingRole }: RoleF
|
||||
setFormData({
|
||||
authName: "",
|
||||
authCode: "",
|
||||
companyCode: currentUser?.company_code || "",
|
||||
company_code: currentUser?.company_code || "",
|
||||
status: "active",
|
||||
});
|
||||
}
|
||||
@@ -152,7 +152,7 @@ export function RoleFormModal({ isOpen, onClose, onSuccess, editingRole }: RoleF
|
||||
response = await roleAPI.create({
|
||||
auth_name: formData.authName,
|
||||
auth_code: formData.authCode,
|
||||
company_code: formData.companyCode,
|
||||
company_code: formData.company_code,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -229,12 +229,12 @@ export function RoleFormModal({ isOpen, onClose, onSuccess, editingRole }: RoleF
|
||||
{/* 회사 (수정 모드에서는 비활성화) */}
|
||||
{isEditMode ? (
|
||||
<div>
|
||||
<Label htmlFor="companyCode" className="text-xs sm:text-sm">
|
||||
<Label htmlFor="company_code" className="text-xs sm:text-sm">
|
||||
회사
|
||||
</Label>
|
||||
<Input
|
||||
id="companyCode"
|
||||
value={formData.companyCode}
|
||||
id="company_code"
|
||||
value={formData.company_code}
|
||||
disabled
|
||||
className="bg-muted h-8 cursor-not-allowed text-xs sm:h-10 sm:text-sm"
|
||||
/>
|
||||
@@ -242,7 +242,7 @@ export function RoleFormModal({ isOpen, onClose, onSuccess, editingRole }: RoleF
|
||||
</div>
|
||||
) : (
|
||||
<div>
|
||||
<Label htmlFor="companyCode" className="text-xs sm:text-sm">
|
||||
<Label htmlFor="company_code" className="text-xs sm:text-sm">
|
||||
회사 <span className="text-destructive">*</span>
|
||||
</Label>
|
||||
{isSuperAdmin ? (
|
||||
@@ -256,9 +256,9 @@ export function RoleFormModal({ isOpen, onClose, onSuccess, editingRole }: RoleF
|
||||
className="h-8 w-full justify-between text-xs sm:h-10 sm:text-sm"
|
||||
disabled={isLoading || isLoadingCompanies}
|
||||
>
|
||||
{formData.companyCode
|
||||
? companies.find((company) => company.company_code === formData.companyCode)?.company_name ||
|
||||
formData.companyCode
|
||||
{formData.company_code
|
||||
? companies.find((company) => company.company_code === formData.company_code)?.company_name ||
|
||||
formData.company_code
|
||||
: "회사 선택..."}
|
||||
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
||||
</Button>
|
||||
@@ -280,7 +280,7 @@ export function RoleFormModal({ isOpen, onClose, onSuccess, editingRole }: RoleF
|
||||
key={company.company_code}
|
||||
value={`${company.company_code} ${company.company_name}`}
|
||||
onSelect={() => {
|
||||
handleInputChange("companyCode", company.company_code);
|
||||
handleInputChange("company_code", company.company_code);
|
||||
setCompanyComboOpen(false);
|
||||
}}
|
||||
className="text-xs sm:text-sm"
|
||||
@@ -288,7 +288,7 @@ export function RoleFormModal({ isOpen, onClose, onSuccess, editingRole }: RoleF
|
||||
<Check
|
||||
className={cn(
|
||||
"mr-2 h-4 w-4",
|
||||
formData.companyCode === company.company_code ? "opacity-100" : "opacity-0",
|
||||
formData.company_code === company.company_code ? "opacity-100" : "opacity-0",
|
||||
)}
|
||||
/>
|
||||
<div className="flex flex-col">
|
||||
@@ -309,8 +309,8 @@ export function RoleFormModal({ isOpen, onClose, onSuccess, editingRole }: RoleF
|
||||
) : (
|
||||
<>
|
||||
<Input
|
||||
id="companyCode"
|
||||
value={formData.companyCode}
|
||||
id="company_code"
|
||||
value={formData.company_code}
|
||||
disabled
|
||||
className="bg-muted h-8 cursor-not-allowed text-xs sm:h-10 sm:text-sm"
|
||||
/>
|
||||
|
||||
@@ -41,7 +41,7 @@ interface DashboardSaveModalProps {
|
||||
title: string;
|
||||
description: string;
|
||||
assignToMenu: boolean;
|
||||
menuType?: "admin" | "user";
|
||||
menu_type?: "admin" | "user";
|
||||
menuId?: string;
|
||||
}) => Promise<void>;
|
||||
initialTitle?: string;
|
||||
@@ -158,7 +158,7 @@ export function DashboardSaveModal({
|
||||
title: title.trim(),
|
||||
description: description.trim(),
|
||||
assignToMenu,
|
||||
menuType: assignToMenu ? menuType : undefined,
|
||||
menu_type: assignToMenu ? menuType : undefined,
|
||||
menuId: assignToMenu ? selectedMenuId : undefined,
|
||||
});
|
||||
onClose();
|
||||
|
||||
@@ -250,6 +250,7 @@ export const ApprovalRequestModal: React.FC<ApprovalRequestModalProps> = ({
|
||||
|
||||
// 혼합형 여부: approvers에 step_type이 설정된 경우
|
||||
const hasMixedStepTypes = approvers.some((a) => a.step_type);
|
||||
const { screenId: eventScreenId } = eventDetail ?? {};
|
||||
|
||||
const res = await createApprovalRequest({
|
||||
title: title.trim(),
|
||||
@@ -260,7 +261,7 @@ export const ApprovalRequestModal: React.FC<ApprovalRequestModalProps> = ({
|
||||
target_record_data: eventDetail.targetRecordData,
|
||||
approval_mode: approvalType === "consensus" ? "parallel" : approvalMode,
|
||||
approval_type: approvalType,
|
||||
screen_id: eventDetail.screenId,
|
||||
screen_id: eventScreenId,
|
||||
button_component_id: eventDetail.buttonComponentId,
|
||||
approvers: approvalType === "self"
|
||||
? []
|
||||
|
||||
@@ -11,9 +11,9 @@ import { DynamicComponentRenderer } from "@/lib/registry/DynamicComponentRendere
|
||||
import { screenApi } from "@/lib/api/screen";
|
||||
import { ResponsiveGridRenderer } from "@/components/screen/ResponsiveGridRenderer";
|
||||
|
||||
// 확장된 TabItem 타입 (screenId 지원)
|
||||
// 확장된 TabItem 타입 (screen_id 지원)
|
||||
interface ExtendedTabItem extends TabItem {
|
||||
screenId?: number;
|
||||
screen_id?: number;
|
||||
screenName?: string;
|
||||
}
|
||||
|
||||
@@ -147,9 +147,9 @@ export function TabsWidget({
|
||||
(c) => c.componentType === "v2-table-list" || c.componentType === "table-list",
|
||||
);
|
||||
const selectedTable = tableComp?.componentConfig?.selectedTable;
|
||||
if (selectedTable || tab.screenId) {
|
||||
if (selectedTable || tab.screen_id) {
|
||||
map[tab.id] = {
|
||||
id: tab.screenId,
|
||||
id: tab.screen_id,
|
||||
tableName: selectedTable,
|
||||
};
|
||||
}
|
||||
@@ -170,14 +170,14 @@ export function TabsWidget({
|
||||
const extTab = tab as ExtendedTabItem;
|
||||
// screenId가 있고, 아직 로드하지 않았으며, 인라인 컴포넌트가 없는 경우만 로드
|
||||
if (
|
||||
extTab.screenId &&
|
||||
extTab.screen_id &&
|
||||
!screenLayouts[tab.id] &&
|
||||
!screenLoadingStates[tab.id] &&
|
||||
(!extTab.components || extTab.components.length === 0)
|
||||
) {
|
||||
setScreenLoadingStates((prev) => ({ ...prev, [tab.id]: true }));
|
||||
try {
|
||||
const layoutData = await screenApi.getLayout(extTab.screenId);
|
||||
const layoutData = await screenApi.getLayout(extTab.screen_id);
|
||||
if (layoutData && layoutData.components) {
|
||||
setScreenLayouts((prev) => ({ ...prev, [tab.id]: layoutData.components }));
|
||||
}
|
||||
@@ -254,8 +254,8 @@ export function TabsWidget({
|
||||
const extTab = tab as ExtendedTabItem;
|
||||
const inlineComponents = tab.components || [];
|
||||
|
||||
// 1. screenId가 있고 인라인 컴포넌트가 없는 경우 -> 화면 로드 방식
|
||||
if (extTab.screenId && inlineComponents.length === 0) {
|
||||
// 1. screen_id가 있고 인라인 컴포넌트가 없는 경우 -> 화면 로드 방식
|
||||
if (extTab.screen_id && inlineComponents.length === 0) {
|
||||
// 로딩 중
|
||||
if (screenLoadingStates[tab.id]) {
|
||||
return (
|
||||
|
||||
@@ -362,7 +362,7 @@ export const V2Layout = forwardRef<HTMLDivElement, V2LayoutProps>(
|
||||
case "screen-embed":
|
||||
return (
|
||||
<ScreenEmbedLayout
|
||||
screenId={config.screenId}
|
||||
screenId={config.screen_id}
|
||||
/>
|
||||
);
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ interface V2ItemRoutingConfigPanelProps {
|
||||
|
||||
interface TableInfo { tableName: string; displayName?: string; }
|
||||
interface ColumnInfo { columnName: string; displayName?: string; dataType?: string; }
|
||||
interface ScreenInfo { screenId: number; screenName: string; screenCode: string; }
|
||||
interface ScreenInfo { screen_id: number; screenName: string; screenCode: string; }
|
||||
|
||||
// ─── 공용: 테이블 Combobox ───
|
||||
function TableCombobox({ value, onChange, tables, loading }: {
|
||||
@@ -144,7 +144,7 @@ function ScreenCombobox({ value, onChange }: { value?: number; onChange: (v?: nu
|
||||
const res = await screenApi.getScreens({ page: 1, size: 1000 });
|
||||
if (res.data) {
|
||||
setScreens(res.data.map((s: any) => ({
|
||||
screenId: s.screen_id, screenName: s.screen_name || `화면 ${s.screen_id}`, screenCode: s.screen_code || "",
|
||||
screen_id: s.screen_id, screenName: s.screen_name || `화면 ${s.screen_id}`, screenCode: s.screen_code || "",
|
||||
})));
|
||||
}
|
||||
} catch { /* ignore */ } finally { setLoading(false); }
|
||||
@@ -152,7 +152,7 @@ function ScreenCombobox({ value, onChange }: { value?: number; onChange: (v?: nu
|
||||
load();
|
||||
}, []);
|
||||
|
||||
const selected = screens.find((s) => s.screenId === value);
|
||||
const selected = screens.find((s) => s.screen_id === value);
|
||||
return (
|
||||
<Popover open={open} onOpenChange={setOpen}>
|
||||
<PopoverTrigger asChild>
|
||||
@@ -168,12 +168,12 @@ function ScreenCombobox({ value, onChange }: { value?: number; onChange: (v?: nu
|
||||
<CommandEmpty className="py-4 text-center text-xs">화면을 찾을 수 없습니다.</CommandEmpty>
|
||||
<CommandGroup className="max-h-[200px] overflow-auto">
|
||||
{screens.map((s) => (
|
||||
<CommandItem key={s.screenId} value={`${s.screenName} ${s.screenCode} ${s.screenId}`}
|
||||
onSelect={() => { onChange(s.screenId); setOpen(false); }} className="text-xs">
|
||||
<Check className={cn("mr-2 h-3 w-3", value === s.screenId ? "opacity-100" : "opacity-0")} />
|
||||
<CommandItem key={s.screen_id} value={`${s.screenName} ${s.screenCode} ${s.screen_id}`}
|
||||
onSelect={() => { onChange(s.screen_id); setOpen(false); }} className="text-xs">
|
||||
<Check className={cn("mr-2 h-3 w-3", value === s.screen_id ? "opacity-100" : "opacity-0")} />
|
||||
<div className="flex flex-col">
|
||||
<span className="font-medium">{s.screenName}</span>
|
||||
<span className="text-[10px] text-muted-foreground">ID: {s.screenId}</span>
|
||||
<span className="text-[10px] text-muted-foreground">ID: {s.screen_id}</span>
|
||||
</div>
|
||||
</CommandItem>
|
||||
))}
|
||||
|
||||
@@ -344,8 +344,8 @@ export const V2LayoutConfigPanel: React.FC<V2LayoutConfigPanelProps> = ({
|
||||
<span className="text-xs text-muted-foreground">화면 ID</span>
|
||||
<Input
|
||||
type="number"
|
||||
value={config.screenId || ""}
|
||||
onChange={(e) => updateConfig("screenId", e.target.value ? Number(e.target.value) : undefined)}
|
||||
value={config.screen_id || ""}
|
||||
onChange={(e) => updateConfig("screen_id", e.target.value ? Number(e.target.value) : undefined)}
|
||||
placeholder="화면 ID 입력"
|
||||
className="h-8 w-[180px] text-sm"
|
||||
/>
|
||||
|
||||
@@ -25,9 +25,9 @@ export const notifyLanguageChange = (newLang: string) => {
|
||||
console.log("🔄 모든 컴포넌트에 언어 변경 알림:", newLang);
|
||||
};
|
||||
|
||||
export const useMultiLang = (options: { companyCode?: string } = {}) => {
|
||||
export const useMultiLang = (options: { company_code?: string } = {}) => {
|
||||
const [userLang, setUserLang] = useState<string | null>(null); // null로 시작
|
||||
const companyCode = options.companyCode || "*";
|
||||
const companyCode = options.company_code || "*";
|
||||
|
||||
// 🎯 효율적인 언어 변경 감지 (폴링 대신 콜백 등록)
|
||||
useEffect(() => {
|
||||
|
||||
@@ -54,8 +54,8 @@ export const useProfile = (user: any, refreshUserData: () => Promise<void>, refr
|
||||
// 부서 목록 상태
|
||||
const [departments, setDepartments] = useState<
|
||||
Array<{
|
||||
deptCode: string;
|
||||
deptName: string;
|
||||
dept_code: string;
|
||||
dept_name: string;
|
||||
}>
|
||||
>([]);
|
||||
|
||||
@@ -99,7 +99,7 @@ export const useProfile = (user: any, refreshUserData: () => Promise<void>, refr
|
||||
try {
|
||||
const response = await apiCall("GET", "/admin/departments");
|
||||
if (response.success && response.data) {
|
||||
setDepartments(response.data as Array<{ deptCode: string; deptName: string }>);
|
||||
setDepartments(response.data as Array<{ dept_code: string; dept_name: string }>);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("부서 목록 로드 실패:", error);
|
||||
|
||||
@@ -89,7 +89,7 @@ export const entityJoinApi = {
|
||||
} = {
|
||||
enabled: true,
|
||||
filterColumn: "company_code",
|
||||
userField: "companyCode",
|
||||
userField: "company_code",
|
||||
};
|
||||
|
||||
// 🆕 프리뷰 모드에서 회사 코드 오버라이드 (최고 관리자만 백엔드에서 허용)
|
||||
|
||||
@@ -42,7 +42,7 @@ export interface TripDetail {
|
||||
}
|
||||
|
||||
export interface TripListFilters {
|
||||
userId?: string;
|
||||
user_id?: string;
|
||||
vehicleId?: number;
|
||||
status?: string;
|
||||
startDate?: string;
|
||||
@@ -126,7 +126,7 @@ export async function getTripList(filters?: TripListFilters) {
|
||||
const params = new URLSearchParams();
|
||||
|
||||
if (filters) {
|
||||
if (filters.userId) params.append("userId", filters.userId);
|
||||
if (filters.user_id) params.append("userId", filters.user_id);
|
||||
if (filters.vehicleId) params.append("vehicleId", String(filters.vehicleId));
|
||||
if (filters.status) params.append("status", filters.status);
|
||||
if (filters.startDate) params.append("startDate", filters.startDate);
|
||||
@@ -290,11 +290,11 @@ export async function getSummaryReport(period?: string) {
|
||||
/**
|
||||
* 일별 통계 조회
|
||||
*/
|
||||
export async function getDailyReport(filters?: { startDate?: string; endDate?: string; userId?: string }) {
|
||||
export async function getDailyReport(filters?: { startDate?: string; endDate?: string; user_id?: string }) {
|
||||
const params = new URLSearchParams();
|
||||
if (filters?.startDate) params.append("startDate", filters.startDate);
|
||||
if (filters?.endDate) params.append("endDate", filters.endDate);
|
||||
if (filters?.userId) params.append("userId", filters.userId);
|
||||
if (filters?.user_id) params.append("userId", filters.user_id);
|
||||
|
||||
const queryString = params.toString();
|
||||
const url = queryString ? `/vehicle/reports/daily?${queryString}` : "/vehicle/reports/daily";
|
||||
@@ -306,11 +306,11 @@ export async function getDailyReport(filters?: { startDate?: string; endDate?: s
|
||||
/**
|
||||
* 주별 통계 조회
|
||||
*/
|
||||
export async function getWeeklyReport(filters?: { year?: number; month?: number; userId?: string }) {
|
||||
export async function getWeeklyReport(filters?: { year?: number; month?: number; user_id?: string }) {
|
||||
const params = new URLSearchParams();
|
||||
if (filters?.year) params.append("year", String(filters.year));
|
||||
if (filters?.month) params.append("month", String(filters.month));
|
||||
if (filters?.userId) params.append("userId", filters.userId);
|
||||
if (filters?.user_id) params.append("userId", filters.user_id);
|
||||
|
||||
const queryString = params.toString();
|
||||
const url = queryString ? `/vehicle/reports/weekly?${queryString}` : "/vehicle/reports/weekly";
|
||||
@@ -322,10 +322,10 @@ export async function getWeeklyReport(filters?: { year?: number; month?: number;
|
||||
/**
|
||||
* 월별 통계 조회
|
||||
*/
|
||||
export async function getMonthlyReport(filters?: { year?: number; userId?: string }) {
|
||||
export async function getMonthlyReport(filters?: { year?: number; user_id?: string }) {
|
||||
const params = new URLSearchParams();
|
||||
if (filters?.year) params.append("year", String(filters.year));
|
||||
if (filters?.userId) params.append("userId", filters.userId);
|
||||
if (filters?.user_id) params.append("userId", filters.user_id);
|
||||
|
||||
const queryString = params.toString();
|
||||
const url = queryString ? `/vehicle/reports/monthly?${queryString}` : "/vehicle/reports/monthly";
|
||||
|
||||
@@ -308,6 +308,10 @@ export const DynamicComponentRenderer: React.FC<DynamicComponentRendererProps> =
|
||||
onDragStart,
|
||||
onDragEnd,
|
||||
children,
|
||||
screenId,
|
||||
userId,
|
||||
userName,
|
||||
companyCode,
|
||||
...props
|
||||
}) => {
|
||||
// 컬럼 메타데이터 로드 트리거 (TTL 기반 자동 갱신)
|
||||
@@ -652,7 +656,7 @@ export const DynamicComponentRenderer: React.FC<DynamicComponentRendererProps> =
|
||||
isInteractive={props.isInteractive}
|
||||
formData={props.formData}
|
||||
onFormDataChange={props.onFormDataChange}
|
||||
screenId={props.screenId}
|
||||
screenId={screenId}
|
||||
tableName={props.tableName}
|
||||
onRefresh={props.onRefresh}
|
||||
onClose={props.onClose}
|
||||
@@ -1093,11 +1097,11 @@ export const DynamicComponentRenderer: React.FC<DynamicComponentRendererProps> =
|
||||
isInteractive: props.isInteractive,
|
||||
formData: props.formData,
|
||||
onFormDataChange: props.onFormDataChange,
|
||||
screenId: props.screenId,
|
||||
screenId,
|
||||
tableName: props.tableName,
|
||||
userId: props.userId, // 🆕 사용자 ID
|
||||
userName: props.userName, // 🆕 사용자 이름
|
||||
companyCode: props.companyCode, // 🆕 회사 코드
|
||||
userId, // 🆕 사용자 ID
|
||||
userName, // 🆕 사용자 이름
|
||||
companyCode, // 🆕 회사 코드
|
||||
onRefresh: props.onRefresh,
|
||||
onClose: props.onClose,
|
||||
mode: props.mode,
|
||||
|
||||
@@ -112,7 +112,8 @@ export const ButtonPrimaryComponent: React.FC<ButtonPrimaryComponentProps> = ({
|
||||
|
||||
// 🆕 tableName이 props로 전달되지 않으면 ScreenContext에서 가져오기
|
||||
const effectiveTableName = tableName || screenContext?.tableName;
|
||||
const effectiveScreenId = screenId || screenContext?.screenId;
|
||||
const { screenId: contextScreenId } = screenContext ?? {};
|
||||
const effectiveScreenId = screenId || contextScreenId;
|
||||
|
||||
// 🆕 props에서 onSave 추출 (명시적으로 선언되지 않은 경우 ...props에서 추출)
|
||||
const propsOnSave = (props as any).onSave as (() => Promise<void>) | undefined;
|
||||
|
||||
+12
-8
@@ -213,13 +213,15 @@ export function ConditionalContainerComponent({
|
||||
{isDesignMode ? (
|
||||
// 디자인 모드: 모든 섹션 표시
|
||||
<div className={spacingClass}>
|
||||
{sections.map((section) => (
|
||||
{sections.map((section) => {
|
||||
const { screenId: sectionScreenId } = section;
|
||||
return (
|
||||
<ConditionalSectionViewer
|
||||
key={section.id}
|
||||
sectionId={section.id}
|
||||
condition={section.condition}
|
||||
label={section.label}
|
||||
screenId={section.screenId}
|
||||
screenId={sectionScreenId}
|
||||
screenName={section.screenName}
|
||||
isActive={selectedValue === section.condition}
|
||||
isDesignMode={isDesignMode}
|
||||
@@ -232,18 +234,20 @@ export function ConditionalContainerComponent({
|
||||
selectedCondition={selectedValue}
|
||||
initialData={initialData}
|
||||
/>
|
||||
))}
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
) : (
|
||||
// 실행 모드: 활성 섹션만 표시
|
||||
sections.map((section) =>
|
||||
selectedValue === section.condition ? (
|
||||
sections.map((section) => {
|
||||
const { screenId: sectionScreenId } = section;
|
||||
return selectedValue === section.condition ? (
|
||||
<ConditionalSectionViewer
|
||||
key={section.id}
|
||||
sectionId={section.id}
|
||||
condition={section.condition}
|
||||
label={section.label}
|
||||
screenId={section.screenId}
|
||||
screenId={sectionScreenId}
|
||||
screenName={section.screenName}
|
||||
isActive={true}
|
||||
isDesignMode={false}
|
||||
@@ -256,8 +260,8 @@ export function ConditionalContainerComponent({
|
||||
selectedCondition={selectedValue}
|
||||
initialData={initialData}
|
||||
/>
|
||||
) : null
|
||||
)
|
||||
) : null;
|
||||
})
|
||||
)}
|
||||
|
||||
{/* 섹션이 없는 경우 안내 */}
|
||||
|
||||
+8
-5
@@ -503,7 +503,9 @@ export function ConditionalContainerConfigPanel({
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-3">
|
||||
{localConfig.sections.map((section, index) => (
|
||||
{localConfig.sections.map((section, index) => {
|
||||
const { screenId: sectionScreenId } = section;
|
||||
return (
|
||||
<div
|
||||
key={section.id}
|
||||
className="p-3 border rounded-lg space-y-3 bg-muted/20"
|
||||
@@ -568,7 +570,7 @@ export function ConditionalContainerConfigPanel({
|
||||
</div>
|
||||
) : (
|
||||
<Select
|
||||
value={section.screenId?.toString() || "none"}
|
||||
value={sectionScreenId?.toString() || "none"}
|
||||
onValueChange={(value) => {
|
||||
if (value === "none") {
|
||||
updateSection(section.id, {
|
||||
@@ -608,14 +610,15 @@ export function ConditionalContainerConfigPanel({
|
||||
</SelectContent>
|
||||
</Select>
|
||||
)}
|
||||
{section.screenId && (
|
||||
{sectionScreenId && (
|
||||
<div className="text-[10px] text-muted-foreground">
|
||||
화면 ID: {section.screenId}
|
||||
화면 ID: {sectionScreenId}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
+2
-1
@@ -339,7 +339,8 @@ export function ModalRepeaterTableComponent({
|
||||
: rawUniqueField;
|
||||
|
||||
const filterCondition = componentConfig?.filterCondition || propFilterCondition || {};
|
||||
const companyCode = componentConfig?.companyCode || propCompanyCode;
|
||||
const { companyCode: configCompanyCode } = componentConfig ?? {};
|
||||
const companyCode = configCompanyCode || propCompanyCode;
|
||||
const [modalOpen, setModalOpen] = useState(false);
|
||||
|
||||
// 체크박스 선택 상태
|
||||
|
||||
+9
-9
@@ -16,7 +16,7 @@ import type { RelatedDataButtonsConfig } from "./types";
|
||||
|
||||
// 화면 정보 타입
|
||||
interface ScreenInfo {
|
||||
screenId: number;
|
||||
screen_id: number;
|
||||
screenName: string;
|
||||
tableName?: string;
|
||||
}
|
||||
@@ -40,7 +40,7 @@ const ScreenSelector: React.FC<ScreenSelectorProps> = ({ value, onChange, placeh
|
||||
const response = await screenApi.getScreens({ size: 500 });
|
||||
if (response.data) {
|
||||
setScreens(response.data.map((s: any) => ({
|
||||
screenId: s.screen_id,
|
||||
screen_id: s.screen_id,
|
||||
screenName: s.screen_name || s.name || `화면 ${s.screen_id}`,
|
||||
tableName: s.table_name,
|
||||
})));
|
||||
@@ -54,13 +54,13 @@ const ScreenSelector: React.FC<ScreenSelectorProps> = ({ value, onChange, placeh
|
||||
loadScreens();
|
||||
}, []);
|
||||
|
||||
const selectedScreen = screens.find(s => s.screenId === value);
|
||||
const selectedScreen = screens.find(s => s.screen_id === value);
|
||||
|
||||
return (
|
||||
<Popover open={open} onOpenChange={setOpen}>
|
||||
<PopoverTrigger asChild>
|
||||
<Button variant="outline" role="combobox" className="w-full justify-between text-xs h-9">
|
||||
{loading ? "로딩중..." : selectedScreen ? `${selectedScreen.screenName} (${selectedScreen.screenId})` : placeholder}
|
||||
{loading ? "로딩중..." : selectedScreen ? `${selectedScreen.screenName} (${selectedScreen.screen_id})` : placeholder}
|
||||
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
@@ -72,17 +72,17 @@ const ScreenSelector: React.FC<ScreenSelectorProps> = ({ value, onChange, placeh
|
||||
<CommandGroup className="max-h-[200px] overflow-auto">
|
||||
{screens.map((screen) => (
|
||||
<CommandItem
|
||||
key={screen.screenId}
|
||||
value={`${screen.screenName} ${screen.screenId}`}
|
||||
key={screen.screen_id}
|
||||
value={`${screen.screenName} ${screen.screen_id}`}
|
||||
onSelect={() => {
|
||||
onChange(screen.screenId, screen.tableName);
|
||||
onChange(screen.screen_id, screen.tableName);
|
||||
setOpen(false);
|
||||
}}
|
||||
className="text-xs"
|
||||
>
|
||||
<Check className={cn("mr-2 h-4 w-4", value === screen.screenId ? "opacity-100" : "opacity-0")} />
|
||||
<Check className={cn("mr-2 h-4 w-4", value === screen.screen_id ? "opacity-100" : "opacity-0")} />
|
||||
<span className="truncate">{screen.screenName}</span>
|
||||
<span className="ml-auto text-muted-foreground">({screen.screenId})</span>
|
||||
<span className="ml-auto text-muted-foreground">({screen.screen_id})</span>
|
||||
</CommandItem>
|
||||
))}
|
||||
</CommandGroup>
|
||||
|
||||
@@ -99,7 +99,7 @@ export class AutoGenerationUtils {
|
||||
if (token) {
|
||||
try {
|
||||
const payload = JSON.parse(atob(token.split(".")[1]));
|
||||
return payload.userId || payload.id || "unknown";
|
||||
return payload.user_id || payload.id || "unknown";
|
||||
} catch {
|
||||
// JWT 파싱 실패 시 fallback
|
||||
}
|
||||
@@ -118,7 +118,7 @@ export class AutoGenerationUtils {
|
||||
if (companyInfo) {
|
||||
try {
|
||||
const parsed = JSON.parse(companyInfo);
|
||||
return parsed.companyCode || parsed.code || "COMPANY";
|
||||
return parsed.company_code || parsed.code || "COMPANY";
|
||||
} catch {
|
||||
return "COMPANY";
|
||||
}
|
||||
|
||||
@@ -17,9 +17,9 @@ import { ButtonActionType } from "@/types/v2-core";
|
||||
|
||||
export interface ButtonExecutionContext {
|
||||
buttonId: string;
|
||||
screenId: string;
|
||||
userId: string;
|
||||
companyCode: string;
|
||||
screen_id: string;
|
||||
user_id: string;
|
||||
company_code: string;
|
||||
startTime: number;
|
||||
formData?: Record<string, any>;
|
||||
selectedRows?: any[];
|
||||
@@ -300,9 +300,9 @@ export class ImprovedButtonActionExecutor {
|
||||
contextData: config.contextData || {},
|
||||
selectedRows: context.selectedRows || [],
|
||||
flowSelectedData: context.flowSelectedData || [],
|
||||
screen_id: context.screenId,
|
||||
company_code: context.companyCode,
|
||||
user_id: context.userId,
|
||||
screen_id: context.screen_id,
|
||||
company_code: context.company_code,
|
||||
user_id: context.user_id,
|
||||
});
|
||||
|
||||
const executionTime = Date.now() - startTime;
|
||||
@@ -991,7 +991,7 @@ export class ImprovedButtonActionExecutor {
|
||||
targetRecordId: targetRecordId ? String(targetRecordId) : "",
|
||||
targetRecordData: Object.keys(selectedRow).length > 0 ? selectedRow : undefined,
|
||||
definitionId: actionConfig.approvalDefinitionId || undefined,
|
||||
screenId: context.screenId ? Number(context.screenId) : undefined,
|
||||
screenId: context.screen_id ? Number(context.screen_id) : undefined,
|
||||
buttonComponentId: context.buttonId,
|
||||
},
|
||||
}),
|
||||
|
||||
@@ -9,9 +9,9 @@ import type { ButtonDataflowConfig, ExtendedControlContext } from "@/types/contr
|
||||
|
||||
export interface ButtonExecutionContext {
|
||||
buttonId: string;
|
||||
screenId?: number;
|
||||
companyCode?: string;
|
||||
userId?: string;
|
||||
screen_id?: number;
|
||||
company_code?: string;
|
||||
user_id?: string;
|
||||
formData: Record<string, any>;
|
||||
selectedRows?: any[];
|
||||
selectedRowsData?: Record<string, any>[];
|
||||
@@ -193,9 +193,9 @@ function prepareContextData(context: ButtonExecutionContext): Record<string, any
|
||||
|
||||
const baseContext = {
|
||||
buttonId: context.buttonId,
|
||||
screenId: context.screenId,
|
||||
companyCode: context.companyCode,
|
||||
userId: context.userId,
|
||||
screen_id: context.screen_id,
|
||||
company_code: context.company_code,
|
||||
user_id: context.user_id,
|
||||
controlDataSource: dataSource,
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user