ERP-node/frontend/components/dashboard/widgets/CalculatorWidget.tsx

292 lines
8.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

'use client';
/**
* 계산기 위젯 컴포넌트
* - 기본 사칙연산 지원
* - 실시간 계산
* - 대시보드 위젯으로 사용 가능
*/
import React, { useState } from 'react';
import { Button } from '@/components/ui/button';
import { DashboardElement } from '@/components/admin/dashboard/types';
interface CalculatorWidgetProps {
element?: DashboardElement;
className?: string;
}
export default function CalculatorWidget({ element, className = '' }: CalculatorWidgetProps) {
const [display, setDisplay] = useState<string>('0');
const [previousValue, setPreviousValue] = useState<number | null>(null);
const [operation, setOperation] = useState<string | null>(null);
const [waitingForOperand, setWaitingForOperand] = useState<boolean>(false);
// 숫자 입력 처리
const handleNumber = (num: string) => {
if (waitingForOperand) {
setDisplay(num);
setWaitingForOperand(false);
} else {
setDisplay(display === '0' ? num : display + num);
}
};
// 소수점 입력
const handleDecimal = () => {
if (waitingForOperand) {
setDisplay('0.');
setWaitingForOperand(false);
} else if (display.indexOf('.') === -1) {
setDisplay(display + '.');
}
};
// 연산자 입력
const handleOperation = (nextOperation: string) => {
const inputValue = parseFloat(display);
if (previousValue === null) {
setPreviousValue(inputValue);
} else if (operation) {
const currentValue = previousValue || 0;
const newValue = calculate(currentValue, inputValue, operation);
setDisplay(String(newValue));
setPreviousValue(newValue);
}
setWaitingForOperand(true);
setOperation(nextOperation);
};
// 계산 수행
const calculate = (firstValue: number, secondValue: number, operation: string): number => {
switch (operation) {
case '+':
return firstValue + secondValue;
case '-':
return firstValue - secondValue;
case '×':
return firstValue * secondValue;
case '÷':
return secondValue !== 0 ? firstValue / secondValue : 0;
default:
return secondValue;
}
};
// 등호 처리
const handleEquals = () => {
const inputValue = parseFloat(display);
if (previousValue !== null && operation) {
const newValue = calculate(previousValue, inputValue, operation);
setDisplay(String(newValue));
setPreviousValue(null);
setOperation(null);
setWaitingForOperand(true);
}
};
// 초기화
const handleClear = () => {
setDisplay('0');
setPreviousValue(null);
setOperation(null);
setWaitingForOperand(false);
};
// 백스페이스
const handleBackspace = () => {
if (!waitingForOperand) {
const newDisplay = display.slice(0, -1);
setDisplay(newDisplay || '0');
}
};
// 부호 변경
const handleSign = () => {
const value = parseFloat(display);
setDisplay(String(value * -1));
};
// 퍼센트
const handlePercent = () => {
const value = parseFloat(display);
setDisplay(String(value / 100));
};
return (
<div className={`h-full w-full p-3 bg-gradient-to-br from-slate-50 to-gray-100 ${className}`}>
<div className="h-full flex flex-col gap-2">
{/* 제목 */}
<h3 className="text-base font-semibold text-gray-900 text-center">🧮 {element?.customTitle || "계산기"}</h3>
{/* 디스플레이 */}
<div className="bg-white border-2 border-gray-200 rounded-lg p-4 shadow-inner min-h-[80px]">
<div className="text-right h-full flex flex-col justify-center">
<div className="h-4 mb-1">
{operation && previousValue !== null && (
<div className="text-xs text-gray-400">
{previousValue} {operation}
</div>
)}
</div>
<div className="text-2xl font-bold text-gray-900 truncate">
{display}
</div>
</div>
</div>
{/* 버튼 그리드 */}
<div className="flex-1 grid grid-cols-4 gap-2">
{/* 첫 번째 줄 */}
<Button
variant="outline"
onClick={handleClear}
className="h-full text-red-600 hover:bg-red-50 hover:text-red-700 font-semibold select-none"
>
AC
</Button>
<Button
variant="outline"
onClick={handleSign}
className="h-full text-gray-600 hover:bg-gray-100 font-semibold select-none"
>
+/-
</Button>
<Button
variant="outline"
onClick={handlePercent}
className="h-full text-gray-600 hover:bg-gray-100 font-semibold select-none"
>
%
</Button>
<Button
variant="default"
onClick={() => handleOperation('÷')}
className="h-full bg-blue-500 hover:bg-blue-600 text-white font-semibold select-none"
>
÷
</Button>
{/* 두 번째 줄 */}
<Button
variant="outline"
onClick={() => handleNumber('7')}
className="h-full hover:bg-gray-100 font-semibold text-lg select-none"
>
7
</Button>
<Button
variant="outline"
onClick={() => handleNumber('8')}
className="h-full hover:bg-gray-100 font-semibold text-lg select-none"
>
8
</Button>
<Button
variant="outline"
onClick={() => handleNumber('9')}
className="h-full hover:bg-gray-100 font-semibold text-lg select-none"
>
9
</Button>
<Button
variant="default"
onClick={() => handleOperation('×')}
className="h-full bg-blue-500 hover:bg-blue-600 text-white font-semibold select-none"
>
×
</Button>
{/* 세 번째 줄 */}
<Button
variant="outline"
onClick={() => handleNumber('4')}
className="h-full hover:bg-gray-100 font-semibold text-lg select-none"
>
4
</Button>
<Button
variant="outline"
onClick={() => handleNumber('5')}
className="h-full hover:bg-gray-100 font-semibold text-lg select-none"
>
5
</Button>
<Button
variant="outline"
onClick={() => handleNumber('6')}
className="h-full hover:bg-gray-100 font-semibold text-lg select-none"
>
6
</Button>
<Button
variant="default"
onClick={() => handleOperation('-')}
className="h-full bg-blue-500 hover:bg-blue-600 text-white font-semibold select-none"
>
-
</Button>
{/* 네 번째 줄 */}
<Button
variant="outline"
onClick={() => handleNumber('1')}
className="h-full hover:bg-gray-100 font-semibold text-lg select-none"
>
1
</Button>
<Button
variant="outline"
onClick={() => handleNumber('2')}
className="h-full hover:bg-gray-100 font-semibold text-lg select-none"
>
2
</Button>
<Button
variant="outline"
onClick={() => handleNumber('3')}
className="h-full hover:bg-gray-100 font-semibold text-lg select-none"
>
3
</Button>
<Button
variant="default"
onClick={() => handleOperation('+')}
className="h-full bg-blue-500 hover:bg-blue-600 text-white font-semibold select-none"
>
+
</Button>
{/* 다섯 번째 줄 */}
<Button
variant="outline"
onClick={() => handleNumber('0')}
className="h-full col-span-2 hover:bg-gray-100 font-semibold text-lg select-none"
>
0
</Button>
<Button
variant="outline"
onClick={handleDecimal}
className="h-full hover:bg-gray-100 font-semibold text-lg select-none"
>
.
</Button>
<Button
variant="default"
onClick={handleEquals}
className="h-full bg-green-500 hover:bg-green-600 text-white font-semibold select-none"
>
=
</Button>
</div>
</div>
</div>
);
}