Merge branch 'main' of http://39.117.244.52:3000/kjs/ERP-node into feature/dashboard

This commit is contained in:
dohyeons
2025-10-14 10:51:28 +09:00
7 changed files with 386 additions and 6 deletions

View File

@@ -17,6 +17,11 @@ const ExchangeWidget = dynamic(() => import("@/components/dashboard/widgets/Exch
loading: () => <div className="flex h-full items-center justify-center text-sm text-gray-500"> ...</div>,
});
const CalculatorWidget = dynamic(() => import("@/components/dashboard/widgets/CalculatorWidget"), {
ssr: false,
loading: () => <div className="flex h-full items-center justify-center text-sm text-gray-500"> ...</div>,
});
// 시계 위젯 임포트
import { ClockWidget } from "./widgets/ClockWidget";
// 달력 위젯 임포트
@@ -385,6 +390,11 @@ export function CanvasElement({
}}
/>
</div>
) : element.type === "widget" && element.subtype === "calculator" ? (
// 계산기 위젯 렌더링
<div className="widget-interactive-area h-full w-full">
<CalculatorWidget />
</div>
) : element.type === "widget" && element.subtype === "calendar" ? (
// 달력 위젯 렌더링
<div className="h-full w-full">

View File

@@ -13,6 +13,7 @@ interface DashboardCanvasProps {
onRemoveElement: (id: string) => void;
onSelectElement: (id: string | null) => void;
onConfigureElement?: (element: DashboardElement) => void;
backgroundColor?: string;
}
/**
@@ -32,6 +33,7 @@ export const DashboardCanvas = forwardRef<HTMLDivElement, DashboardCanvasProps>(
onRemoveElement,
onSelectElement,
onConfigureElement,
backgroundColor = '#f9fafb',
},
ref,
) => {
@@ -104,8 +106,9 @@ export const DashboardCanvas = forwardRef<HTMLDivElement, DashboardCanvasProps>(
return (
<div
ref={ref}
className={`relative rounded-lg bg-gray-50 shadow-inner ${isDragOver ? "bg-blue-50/50" : ""} `}
className={`relative rounded-lg shadow-inner ${isDragOver ? "bg-blue-50/50" : ""} `}
style={{
backgroundColor,
width: `${GRID_CONFIG.CANVAS_WIDTH}px`,
minHeight: `${minCanvasHeight}px`,
// 12 컬럼 그리드 배경

View File

@@ -1,6 +1,6 @@
"use client";
import React, { useState, useRef, useCallback, useEffect } from "react";
import React, { useState, useRef, useCallback } from "react";
import { DashboardCanvas } from "./DashboardCanvas";
import { DashboardSidebar } from "./DashboardSidebar";
import { DashboardToolbar } from "./DashboardToolbar";
@@ -298,6 +298,8 @@ function getElementTitle(type: ElementType, subtype: ElementSubtype): string {
return "☁️ 날씨 위젯";
case "clock":
return "⏰ 시계 위젯";
case "calculator":
return "🧮 계산기 위젯";
case "calendar":
return "📅 달력 위젯";
default:
@@ -328,6 +330,8 @@ function getElementContent(type: ElementType, subtype: ElementSubtype): string {
return "서울\n23°C\n구름 많음";
case "clock":
return "clock";
case "calculator":
return "calculator";
case "calendar":
return "calendar";
default:

View File

@@ -101,7 +101,15 @@ export function DashboardSidebar() {
type="widget"
subtype="weather"
onDragStart={handleDragStart}
className="border-l-4 border-orange-500"
className="border-l-4 border-cyan-500"
/>
<DraggableItem
icon="🧮"
title="계산기 위젯"
type="widget"
subtype="calculator"
onDragStart={handleDragStart}
className="border-l-4 border-green-500"
/>
<DraggableItem
icon="⏰"

View File

@@ -1,17 +1,20 @@
'use client';
import React from 'react';
import React, { useState } from 'react';
interface DashboardToolbarProps {
onClearCanvas: () => void;
onSaveLayout: () => void;
canvasBackgroundColor: string;
onCanvasBackgroundColorChange: (color: string) => void;
}
/**
* 대시보드 툴바 컴포넌트
* - 전체 삭제, 레이아웃 저장 등 주요 액션 버튼
*/
export function DashboardToolbar({ onClearCanvas, onSaveLayout }: DashboardToolbarProps) {
export function DashboardToolbar({ onClearCanvas, onSaveLayout, canvasBackgroundColor, onCanvasBackgroundColorChange }: DashboardToolbarProps) {
const [showColorPicker, setShowColorPicker] = useState(false);
return (
<div className="absolute top-5 left-5 bg-white p-3 rounded-lg shadow-lg z-50 flex gap-3">
<button
@@ -37,6 +40,71 @@ export function DashboardToolbar({ onClearCanvas, onSaveLayout }: DashboardToolb
>
💾
</button>
{/* 캔버스 배경색 변경 버튼 */}
<div className="relative">
<button
onClick={() => setShowColorPicker(!showColorPicker)}
className="
px-4 py-2 border border-gray-300 bg-white rounded-md
text-sm font-medium text-gray-700
hover:bg-gray-50 hover:border-gray-400
transition-colors duration-200
flex items-center gap-2
"
>
🎨
<div
className="w-4 h-4 rounded border border-gray-300"
style={{ backgroundColor: canvasBackgroundColor }}
/>
</button>
{/* 색상 선택 패널 */}
{showColorPicker && (
<div className="absolute top-full left-0 mt-2 bg-white p-4 rounded-lg shadow-xl z-50 border border-gray-200 w-[280px]">
<div className="flex items-center gap-3 mb-3">
<input
type="color"
value={canvasBackgroundColor}
onChange={(e) => onCanvasBackgroundColorChange(e.target.value)}
className="h-10 w-16 border border-gray-300 rounded cursor-pointer"
/>
<input
type="text"
value={canvasBackgroundColor}
onChange={(e) => onCanvasBackgroundColorChange(e.target.value)}
placeholder="#ffffff"
className="flex-1 px-2 py-1 text-sm border border-gray-300 rounded"
/>
</div>
{/* 프리셋 색상 */}
<div className="grid grid-cols-6 gap-2 mb-3">
{[
'#ffffff', '#f9fafb', '#f3f4f6', '#e5e7eb',
'#3b82f6', '#8b5cf6', '#ec4899', '#f59e0b',
'#10b981', '#06b6d4', '#6366f1', '#84cc16',
].map((color) => (
<button
key={color}
onClick={() => onCanvasBackgroundColorChange(color)}
className={`h-8 rounded border-2 ${canvasBackgroundColor === color ? 'border-blue-500 ring-2 ring-blue-200' : 'border-gray-300'}`}
style={{ backgroundColor: color }}
title={color}
/>
))}
</div>
<button
onClick={() => setShowColorPicker(false)}
className="w-full px-3 py-1.5 text-sm text-gray-600 border border-gray-300 rounded hover:bg-gray-50"
>
</button>
</div>
)}
</div>
</div>
);
}

View File

@@ -15,7 +15,8 @@ export type ElementSubtype =
| "exchange"
| "weather"
| "clock"
| "calendar"; // 위젯 타입
| "calendar"
| "calculator"; // 위젯 타입
export interface Position {
x: number;