feat: Implement smart factory log management features
- Added new API endpoints for retrieving company-specific user lists and sending immediate logs for selected users. - Enhanced the smartFactoryLogController with functions to handle user retrieval and immediate log sending, improving operational efficiency. - Updated adminRoutes to include routes for the new functionalities, ensuring proper access control for super admins. - Refactored the sendSmartFactoryLog function to improve logging and error handling, providing better insights into the log transmission process. These changes aim to enhance the smart factory log management capabilities, facilitating better user interaction and operational tracking.
This commit is contained in:
136
frontend/app/(main)/COMPANY_29/master-data/options/page.tsx
Normal file
136
frontend/app/(main)/COMPANY_29/master-data/options/page.tsx
Normal file
@@ -0,0 +1,136 @@
|
||||
"use client";
|
||||
|
||||
import React, { useState, useCallback, useRef, useEffect } from "react";
|
||||
import { Settings2, Tags, Hash } from "lucide-react";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { CategoryColumnList } from "@/components/table-category/CategoryColumnList";
|
||||
import { CategoryValueManager } from "@/components/table-category/CategoryValueManager";
|
||||
import { NumberingRuleDesigner } from "@/components/numbering-rule/NumberingRuleDesigner";
|
||||
|
||||
const TABS = [
|
||||
{ id: "category", label: "카테고리 설정", icon: Tags },
|
||||
{ id: "numbering", label: "코드 설정", icon: Hash },
|
||||
] as const;
|
||||
|
||||
type TabId = (typeof TABS)[number]["id"];
|
||||
|
||||
export default function OptionsSettingPage() {
|
||||
const [activeTab, setActiveTab] = useState<TabId>("category");
|
||||
|
||||
const [selectedColumn, setSelectedColumn] = useState<string | null>(null);
|
||||
const [selectedColumnLabel, setSelectedColumnLabel] = useState("");
|
||||
const [selectedTableName, setSelectedTableName] = useState("");
|
||||
|
||||
const [leftWidth, setLeftWidth] = useState(340);
|
||||
const [isDragging, setIsDragging] = useState(false);
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const handleMouseDown = useCallback(() => {
|
||||
setIsDragging(true);
|
||||
document.body.style.cursor = "col-resize";
|
||||
document.body.style.userSelect = "none";
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isDragging) return;
|
||||
const handleMouseMove = (e: MouseEvent) => {
|
||||
if (!containerRef.current) return;
|
||||
const rect = containerRef.current.getBoundingClientRect();
|
||||
setLeftWidth(Math.max(260, Math.min(500, e.clientX - rect.left)));
|
||||
};
|
||||
const handleMouseUp = () => {
|
||||
setIsDragging(false);
|
||||
document.body.style.cursor = "";
|
||||
document.body.style.userSelect = "";
|
||||
};
|
||||
document.addEventListener("mousemove", handleMouseMove);
|
||||
document.addEventListener("mouseup", handleMouseUp);
|
||||
return () => {
|
||||
document.removeEventListener("mousemove", handleMouseMove);
|
||||
document.removeEventListener("mouseup", handleMouseUp);
|
||||
};
|
||||
}, [isDragging]);
|
||||
|
||||
return (
|
||||
<div className="flex h-full flex-col p-3 gap-3">
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<Settings2 className="h-4 w-4 text-primary" />
|
||||
<h1 className="text-sm font-semibold">옵션 설정</h1>
|
||||
</div>
|
||||
<div className="flex bg-muted rounded-md p-0.5 gap-0.5">
|
||||
{TABS.map((tab) => {
|
||||
const Icon = tab.icon;
|
||||
return (
|
||||
<button
|
||||
key={tab.id}
|
||||
onClick={() => setActiveTab(tab.id)}
|
||||
className={cn(
|
||||
"flex items-center gap-1.5 px-3 py-1.5 rounded text-xs font-medium transition-all",
|
||||
activeTab === tab.id
|
||||
? "bg-background text-foreground shadow-sm"
|
||||
: "text-muted-foreground hover:text-foreground"
|
||||
)}
|
||||
>
|
||||
<Icon className="h-3.5 w-3.5" />
|
||||
{tab.label}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex-1 min-h-0">
|
||||
{activeTab === "category" && (
|
||||
<div ref={containerRef} className="flex h-full">
|
||||
<div
|
||||
style={{ width: leftWidth }}
|
||||
className="shrink-0 border rounded-lg bg-card overflow-hidden"
|
||||
>
|
||||
<CategoryColumnList
|
||||
tableName=""
|
||||
selectedColumn={selectedColumn}
|
||||
onColumnSelect={(uniqueKey, label, tableName) => {
|
||||
setSelectedColumn(uniqueKey);
|
||||
setSelectedColumnLabel(label);
|
||||
setSelectedTableName(tableName);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div
|
||||
onMouseDown={handleMouseDown}
|
||||
className={cn(
|
||||
"w-1.5 mx-0.5 cursor-col-resize rounded-full transition-colors shrink-0",
|
||||
isDragging ? "bg-primary" : "bg-border hover:bg-primary/50"
|
||||
)}
|
||||
/>
|
||||
|
||||
<div className="flex-1 min-w-0 border rounded-lg bg-card overflow-hidden">
|
||||
{selectedColumn && selectedTableName ? (
|
||||
<CategoryValueManager
|
||||
tableName={selectedTableName}
|
||||
columnName={selectedColumn.includes(".") ? selectedColumn.split(".").pop()! : selectedColumn}
|
||||
columnLabel={selectedColumnLabel}
|
||||
/>
|
||||
) : (
|
||||
<div className="flex h-full items-center justify-center">
|
||||
<div className="text-center space-y-2">
|
||||
<Tags className="h-8 w-8 mx-auto text-muted-foreground/30" />
|
||||
<p className="text-sm text-muted-foreground">좌측에서 카테고리 컬럼을 선택해주세요</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{activeTab === "numbering" && (
|
||||
<div className="h-full border rounded-lg bg-card overflow-auto">
|
||||
<NumberingRuleDesigner />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user