goldenChat base source add
This commit is contained in:
@@ -0,0 +1,562 @@
|
||||
import React, { useState } from 'react';
|
||||
import {
|
||||
Dialog,
|
||||
DialogTitle,
|
||||
DialogContent,
|
||||
DialogActions,
|
||||
Button,
|
||||
Box,
|
||||
Typography,
|
||||
Switch,
|
||||
FormControlLabel,
|
||||
Divider,
|
||||
useTheme,
|
||||
useMediaQuery,
|
||||
Alert,
|
||||
Paper,
|
||||
TextField,
|
||||
Slider,
|
||||
IconButton,
|
||||
} from '@mui/material';
|
||||
import { DragIndicator, Close } from '@mui/icons-material';
|
||||
import { useChartColors, ChartColorSettings } from '../contexts/ChartColorContext';
|
||||
import { useIndicatorSettings, IndicatorSettings } from '../contexts/IndicatorSettingsContext';
|
||||
import {
|
||||
DndContext,
|
||||
closestCenter,
|
||||
KeyboardSensor,
|
||||
PointerSensor,
|
||||
TouchSensor,
|
||||
useSensor,
|
||||
useSensors,
|
||||
DragEndEvent,
|
||||
DragStartEvent,
|
||||
DragOverlay,
|
||||
} from '@dnd-kit/core';
|
||||
import {
|
||||
arrayMove,
|
||||
SortableContext,
|
||||
sortableKeyboardCoordinates,
|
||||
useSortable,
|
||||
verticalListSortingStrategy,
|
||||
} from '@dnd-kit/sortable';
|
||||
import { CSS } from '@dnd-kit/utilities';
|
||||
import { useIndicatorVisibility, IndicatorVisibilitySettings, IndicatorOrder } from '../contexts/IndicatorVisibilityContext';
|
||||
import { useAuth } from '../contexts/AuthContext';
|
||||
import DraggablePaper from './DraggablePaper';
|
||||
|
||||
interface IndicatorSettingsDialogProps {
|
||||
open: boolean;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
// 지표별 색상/굵기 설정 키 매핑 (여러 색상 지원)
|
||||
interface ColorConfig {
|
||||
label: string;
|
||||
colorKey: keyof ChartColorSettings;
|
||||
widthKey?: keyof ChartColorSettings;
|
||||
}
|
||||
|
||||
const indicatorColorConfigs: Record<string, ColorConfig[]> = {
|
||||
macdLine: [
|
||||
{ label: 'MACD', colorKey: 'macdLine', widthKey: 'macdLineWidth' },
|
||||
{ label: '시그널', colorKey: 'signalLine', widthKey: 'signalLineWidth' },
|
||||
],
|
||||
rsi: [
|
||||
{ label: '', colorKey: 'rsiColor', widthKey: 'rsiWidth' },
|
||||
],
|
||||
stochasticK: [
|
||||
{ label: '%K', colorKey: 'stochasticK', widthKey: 'stochasticKWidth' },
|
||||
{ label: '%D', colorKey: 'stochasticD', widthKey: 'stochasticDWidth' },
|
||||
],
|
||||
cci: [
|
||||
{ label: '', colorKey: 'cciColor', widthKey: 'cciWidth' },
|
||||
],
|
||||
adx: [
|
||||
{ label: '', colorKey: 'adxColor', widthKey: 'adxWidth' },
|
||||
],
|
||||
plusDi: [
|
||||
{ label: '+DI', colorKey: 'plusDiColor', widthKey: 'plusDiWidth' },
|
||||
{ label: '-DI', colorKey: 'minusDiColor', widthKey: 'minusDiWidth' },
|
||||
],
|
||||
minusDi: [], // plusDi에서 함께 처리
|
||||
williamsR: [
|
||||
{ label: '', colorKey: 'williamsRColor', widthKey: 'williamsRWidth' },
|
||||
],
|
||||
trix: [
|
||||
{ label: 'TRIX', colorKey: 'trixColor', widthKey: 'trixWidth' },
|
||||
{ label: '시그널', colorKey: 'trixSignalColor', widthKey: 'trixSignalWidth' },
|
||||
],
|
||||
newPsychological: [
|
||||
{ label: '', colorKey: 'newPsychologicalColor', widthKey: 'newPsychologicalWidth' },
|
||||
],
|
||||
investPsychological: [
|
||||
{ label: '', colorKey: 'investPsychologicalColor', widthKey: 'investPsychologicalWidth' },
|
||||
],
|
||||
bwi: [
|
||||
{ label: '', colorKey: 'bwiColor', widthKey: 'bwiWidth' },
|
||||
],
|
||||
obv: [
|
||||
{ label: '', colorKey: 'obvColor', widthKey: 'obvWidth' },
|
||||
],
|
||||
volumeOsc: [
|
||||
{ label: '', colorKey: 'volumeOscColor', widthKey: 'volumeOscWidth' },
|
||||
],
|
||||
vr: [
|
||||
{ label: '', colorKey: 'vrColor', widthKey: 'vrWidth' },
|
||||
],
|
||||
disparity: [
|
||||
{ label: '초단기', colorKey: 'disparityUltraShortColor', widthKey: 'disparityUltraShortWidth' },
|
||||
{ label: '단기', colorKey: 'disparityShortColor', widthKey: 'disparityShortWidth' },
|
||||
{ label: '중기', colorKey: 'disparityMidColor', widthKey: 'disparityMidWidth' },
|
||||
{ label: '장기', colorKey: 'disparityLongColor', widthKey: 'disparityLongWidth' },
|
||||
],
|
||||
};
|
||||
|
||||
// 지표별 파라미터 키 매핑
|
||||
const indicatorParamKeys: Record<string, { key: string; label: string }[]> = {
|
||||
macdLine: [{ key: 'macdFastPeriod', label: '단기 EMA' }, { key: 'macdSlowPeriod', label: '장기 EMA' }, { key: 'macdSignalPeriod', label: '시그널' }],
|
||||
rsi: [{ key: 'rsiPeriod', label: '기간' }],
|
||||
stochasticK: [{ key: 'stochasticKPeriod', label: '기간' }, { key: 'stochasticSlowing', label: '%K' }, { key: 'stochasticDPeriod', label: '%D' }],
|
||||
cci: [{ key: 'cciPeriod', label: '기간' }, { key: 'cciSignalPeriod', label: '시그널' }],
|
||||
adx: [{ key: 'adxPeriod', label: '기간' }],
|
||||
williamsR: [{ key: 'williamsRPeriod', label: '기간' }],
|
||||
trix: [{ key: 'trixPeriod', label: '기간' }, { key: 'trixSignalPeriod', label: '시그널' }],
|
||||
newPsychological: [{ key: 'newPsychologicalPeriod', label: '기간' }],
|
||||
investPsychological: [{ key: 'investPsychologicalPeriod', label: '기간' }],
|
||||
bwi: [{ key: 'bwiPeriod', label: '기간' }],
|
||||
obv: [{ key: 'obvPeriod', label: '기간' }],
|
||||
volumeOsc: [{ key: 'volumeOscShortPeriod', label: '단기' }, { key: 'volumeOscLongPeriod', label: '장기' }],
|
||||
vr: [{ key: 'vrPeriod', label: '기간' }],
|
||||
disparity: [
|
||||
{ key: 'disparityUltraShortPeriod', label: '초단기' },
|
||||
{ key: 'disparityShortPeriod', label: '단기' },
|
||||
{ key: 'disparityMidPeriod', label: '중기' },
|
||||
{ key: 'disparityLongPeriod', label: '장기' },
|
||||
],
|
||||
plusDi: [{ key: 'dmiPeriod', label: '기간' }],
|
||||
minusDi: [{ key: 'dmiPeriod', label: '기간' }],
|
||||
};
|
||||
|
||||
// Sortable 아이템 컴포넌트 - 이동평균선 설정과 동일한 스타일
|
||||
interface SortableItemProps {
|
||||
indicator: IndicatorOrder;
|
||||
isDisabled: boolean;
|
||||
checked: boolean;
|
||||
onToggle: () => void;
|
||||
colors: ChartColorSettings;
|
||||
updateColors: (c: Partial<ChartColorSettings>) => void;
|
||||
indicatorSettings: IndicatorSettings;
|
||||
updateIndicatorSettings: (s: Partial<IndicatorSettings>) => void;
|
||||
}
|
||||
|
||||
const SortableItem: React.FC<SortableItemProps> = ({
|
||||
indicator,
|
||||
isDisabled,
|
||||
checked,
|
||||
onToggle,
|
||||
colors,
|
||||
updateColors,
|
||||
indicatorSettings,
|
||||
updateIndicatorSettings,
|
||||
}) => {
|
||||
const {
|
||||
attributes,
|
||||
listeners,
|
||||
setNodeRef,
|
||||
transform,
|
||||
transition,
|
||||
isDragging,
|
||||
} = useSortable({ id: indicator.id, disabled: isDisabled || indicator.settingKey === 'candleChart' });
|
||||
|
||||
const style = {
|
||||
transform: CSS.Transform.toString(transform),
|
||||
transition,
|
||||
opacity: isDragging ? 0.5 : 1,
|
||||
};
|
||||
|
||||
const canDrag = !isDisabled && indicator.settingKey !== 'candleChart';
|
||||
const colorConfigs = indicatorColorConfigs[indicator.id] || [];
|
||||
const paramKeys = indicatorParamKeys[indicator.id];
|
||||
const hasSettings = indicator.settingKey !== 'candleChart' && indicator.settingKey !== 'volume';
|
||||
|
||||
// 첫 번째 색상/굵기 설정
|
||||
const primaryColor = colorConfigs[0];
|
||||
// 추가 색상 설정 (시그널 등)
|
||||
const secondaryColors = colorConfigs.slice(1);
|
||||
|
||||
return (
|
||||
<Paper
|
||||
ref={setNodeRef}
|
||||
style={style}
|
||||
sx={{
|
||||
mb: 1,
|
||||
border: isDragging ? '2px solid #1976d2' : checked ? '1px solid' : '1px solid',
|
||||
borderColor: isDragging ? '#1976d2' : checked ? 'primary.main' : 'divider',
|
||||
borderRadius: 1,
|
||||
bgcolor: isDragging ? 'action.hover' : checked ? 'action.selected' : 'background.paper',
|
||||
boxShadow: isDragging ? 4 : 1,
|
||||
opacity: checked ? 1 : 0.7,
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
px: 1.5,
|
||||
py: 1.2,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: 1.5,
|
||||
minHeight: 48,
|
||||
}}
|
||||
>
|
||||
{/* 사용 체크박스 */}
|
||||
<Box sx={{ width: 40, display: 'flex', justifyContent: 'center' }}>
|
||||
<Switch
|
||||
checked={checked}
|
||||
onChange={onToggle}
|
||||
disabled={indicator.settingKey === 'candleChart'}
|
||||
size="small"
|
||||
/>
|
||||
</Box>
|
||||
|
||||
{/* 드래그 핸들 + 지표명 */}
|
||||
<Box sx={{ width: 100, display: 'flex', alignItems: 'center', gap: 0.5 }}>
|
||||
{canDrag && (
|
||||
<Box
|
||||
{...attributes}
|
||||
{...listeners}
|
||||
sx={{ cursor: 'grab', '&:active': { cursor: 'grabbing' }, display: 'flex' }}
|
||||
>
|
||||
<DragIndicator color="action" sx={{ fontSize: 18 }} />
|
||||
</Box>
|
||||
)}
|
||||
<Typography variant="body2" sx={{ fontWeight: checked ? 'bold' : 'normal', fontSize: '13px' }}>
|
||||
{indicator.label}
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
{/* 파라미터 영역 */}
|
||||
<Box sx={{ width: 180, display: 'flex', alignItems: 'center', gap: 0.5 }}>
|
||||
{hasSettings && paramKeys && paramKeys.map((param, idx) => (
|
||||
<TextField
|
||||
key={param.key}
|
||||
type="number"
|
||||
size="small"
|
||||
placeholder={param.label}
|
||||
value={(indicatorSettings as any)[param.key] || ''}
|
||||
onChange={(e) => updateIndicatorSettings({ [param.key]: Number(e.target.value) })}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
disabled={!checked}
|
||||
inputProps={{ min: 1, max: 999, style: { fontSize: 12, padding: '6px 8px', textAlign: 'center' } }}
|
||||
sx={{
|
||||
width: paramKeys.length > 2 ? 50 : 60,
|
||||
'& .MuiOutlinedInput-root': { bgcolor: 'background.default' },
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</Box>
|
||||
|
||||
{/* 색상 영역 */}
|
||||
<Box sx={{ width: 100, display: 'flex', alignItems: 'center', gap: 1 }}>
|
||||
{hasSettings && primaryColor && (
|
||||
<Box sx={{ position: 'relative' }}>
|
||||
<Box
|
||||
sx={{
|
||||
width: 32,
|
||||
height: 32,
|
||||
borderRadius: 1,
|
||||
bgcolor: colors[primaryColor.colorKey] as string,
|
||||
border: '2px solid',
|
||||
borderColor: 'divider',
|
||||
cursor: checked ? 'pointer' : 'default',
|
||||
opacity: checked ? 1 : 0.5,
|
||||
}}
|
||||
/>
|
||||
<input
|
||||
type="color"
|
||||
value={colors[primaryColor.colorKey] as string || '#666666'}
|
||||
onChange={(e) => updateColors({ [primaryColor.colorKey]: e.target.value })}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
disabled={!checked}
|
||||
style={{
|
||||
position: 'absolute', top: 0, left: 0,
|
||||
width: 32, height: 32, opacity: 0, cursor: 'pointer'
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
{/* 추가 색상 (시그널 등) */}
|
||||
{hasSettings && secondaryColors.map((cfg) => (
|
||||
<Box key={cfg.colorKey} sx={{ position: 'relative' }}>
|
||||
<Box
|
||||
sx={{
|
||||
width: 24,
|
||||
height: 24,
|
||||
borderRadius: 0.5,
|
||||
bgcolor: colors[cfg.colorKey] as string,
|
||||
border: '1px solid',
|
||||
borderColor: 'divider',
|
||||
cursor: checked ? 'pointer' : 'default',
|
||||
opacity: checked ? 1 : 0.5,
|
||||
}}
|
||||
/>
|
||||
<input
|
||||
type="color"
|
||||
value={colors[cfg.colorKey] as string || '#666666'}
|
||||
onChange={(e) => updateColors({ [cfg.colorKey]: e.target.value })}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
disabled={!checked}
|
||||
style={{
|
||||
position: 'absolute', top: 0, left: 0,
|
||||
width: 24, height: 24, opacity: 0, cursor: 'pointer'
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
))}
|
||||
</Box>
|
||||
|
||||
{/* 선 굵기 영역 */}
|
||||
<Box sx={{ flex: 1, display: 'flex', alignItems: 'center', gap: 1 }}>
|
||||
{hasSettings && primaryColor?.widthKey && (
|
||||
<>
|
||||
<Slider
|
||||
value={colors[primaryColor.widthKey] as number || 1}
|
||||
onChange={(_, value) => updateColors({ [primaryColor.widthKey!]: value as number })}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
disabled={!checked}
|
||||
min={0.5}
|
||||
max={5}
|
||||
step={0.5}
|
||||
sx={{ flex: 1, maxWidth: 150 }}
|
||||
size="small"
|
||||
/>
|
||||
<Typography variant="caption" sx={{ minWidth: 30, fontSize: '11px', color: 'text.secondary' }}>
|
||||
{colors[primaryColor.widthKey]}px
|
||||
</Typography>
|
||||
{/* 선 미리보기 */}
|
||||
<Box
|
||||
sx={{
|
||||
width: 40,
|
||||
height: Math.max((colors[primaryColor.widthKey] as number || 1) * 2, 2),
|
||||
bgcolor: checked ? colors[primaryColor.colorKey] as string : 'grey.400',
|
||||
borderRadius: 0.5,
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
</Paper>
|
||||
);
|
||||
};
|
||||
|
||||
const IndicatorSettingsDialog: React.FC<IndicatorSettingsDialogProps> = ({ open, onClose }) => {
|
||||
const theme = useTheme();
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down('sm'));
|
||||
const { settings, order, updateSettings, updateOrder, resetSettings, saveSettings } = useIndicatorVisibility();
|
||||
const { colors, updateColors } = useChartColors();
|
||||
const { indicatorSettings, updateIndicatorSettings } = useIndicatorSettings();
|
||||
const { isAuthenticated } = useAuth();
|
||||
const [tempSettings, setTempSettings] = useState<IndicatorVisibilitySettings>(settings);
|
||||
const [tempOrder, setTempOrder] = useState<IndicatorOrder[]>(order);
|
||||
const [activeId, setActiveId] = useState<string | null>(null);
|
||||
|
||||
// 드래그 센서 설정 - 즉시 드래그 시작 가능하도록 설정
|
||||
const sensors = useSensors(
|
||||
useSensor(PointerSensor, {
|
||||
activationConstraint: {
|
||||
distance: 5, // 5px 이동 후 드래그 시작
|
||||
},
|
||||
}),
|
||||
useSensor(TouchSensor, {
|
||||
activationConstraint: {
|
||||
delay: 100, // 100ms 터치 후 드래그 시작
|
||||
tolerance: 5,
|
||||
},
|
||||
}),
|
||||
useSensor(KeyboardSensor, {
|
||||
coordinateGetter: sortableKeyboardCoordinates,
|
||||
})
|
||||
);
|
||||
|
||||
// 다이얼로그가 열릴 때마다 현재 설정으로 초기화
|
||||
React.useEffect(() => {
|
||||
if (open) {
|
||||
setTempSettings(settings);
|
||||
setTempOrder(order);
|
||||
}
|
||||
}, [open, settings, order]);
|
||||
|
||||
const handleDragStart = (event: DragStartEvent) => {
|
||||
setActiveId(event.active.id as string);
|
||||
};
|
||||
|
||||
const handleDragEnd = (event: DragEndEvent) => {
|
||||
const { active, over } = event;
|
||||
setActiveId(null);
|
||||
|
||||
if (over && active.id !== over.id) {
|
||||
setTempOrder((items) => {
|
||||
const oldIndex = items.findIndex((item) => item.id === active.id);
|
||||
const newIndex = items.findIndex((item) => item.id === over.id);
|
||||
|
||||
return arrayMove(items, oldIndex, newIndex);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleToggle = (key: keyof IndicatorVisibilitySettings) => {
|
||||
// candleChart는 변경 불가
|
||||
if (key === 'candleChart') return;
|
||||
|
||||
setTempSettings(prev => ({ ...prev, [key]: !prev[key] }));
|
||||
};
|
||||
|
||||
const handleSave = async () => {
|
||||
updateSettings(tempSettings);
|
||||
updateOrder(tempOrder);
|
||||
await saveSettings(isAuthenticated);
|
||||
onClose();
|
||||
};
|
||||
|
||||
const handleReset = () => {
|
||||
if (window.confirm('모든 보조지표 설정을 기본값으로 초기화하시겠습니까?')) {
|
||||
resetSettings();
|
||||
onClose();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
open={open}
|
||||
onClose={onClose}
|
||||
maxWidth="md"
|
||||
fullWidth
|
||||
fullScreen={isMobile}
|
||||
hideBackdrop={true}
|
||||
disableScrollLock={true}
|
||||
PaperComponent={DraggablePaper}
|
||||
sx={{
|
||||
pointerEvents: 'none',
|
||||
'& .MuiDialog-container': {
|
||||
pointerEvents: 'none',
|
||||
alignItems: 'flex-start',
|
||||
},
|
||||
}}
|
||||
PaperProps={{
|
||||
sx: {
|
||||
minHeight: isMobile ? '100%' : '60vh',
|
||||
maxHeight: isMobile ? '100%' : '90vh',
|
||||
width: isMobile ? '100%' : 750,
|
||||
pointerEvents: 'auto',
|
||||
boxShadow: '0 8px 32px rgba(0, 0, 0, 0.3)',
|
||||
border: '2px solid rgba(80, 100, 160, 0.5)',
|
||||
}
|
||||
}}
|
||||
>
|
||||
<DialogTitle sx={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
bgcolor: '#1976d2',
|
||||
color: '#fff',
|
||||
py: 1.2,
|
||||
}}>
|
||||
<Typography variant="h6" sx={{ fontWeight: 600, fontSize: '16px', color: '#fff' }}>
|
||||
📊 보조지표 표시 설정
|
||||
</Typography>
|
||||
<IconButton onClick={onClose} size="small" sx={{ color: '#fff' }}>
|
||||
<Close />
|
||||
</IconButton>
|
||||
</DialogTitle>
|
||||
<DialogContent dividers>
|
||||
{/* 헤더 행 - 이동평균선 설정과 동일한 스타일 */}
|
||||
<Paper sx={{ p: 1.5, mb: 2, bgcolor: 'action.hover' }}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1.5 }}>
|
||||
<Box sx={{ width: 40, textAlign: 'center' }}>
|
||||
<Typography variant="caption" fontWeight="bold" color="text.secondary">사용</Typography>
|
||||
</Box>
|
||||
<Box sx={{ width: 100 }}>
|
||||
<Typography variant="caption" fontWeight="bold" color="text.secondary">이름</Typography>
|
||||
</Box>
|
||||
<Box sx={{ width: 180 }}>
|
||||
<Typography variant="caption" fontWeight="bold" color="text.secondary">파라미터</Typography>
|
||||
</Box>
|
||||
<Box sx={{ width: 100 }}>
|
||||
<Typography variant="caption" fontWeight="bold" color="text.secondary">색상</Typography>
|
||||
</Box>
|
||||
<Box sx={{ flex: 1 }}>
|
||||
<Typography variant="caption" fontWeight="bold" color="text.secondary">선 굵기</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
</Paper>
|
||||
|
||||
<DndContext
|
||||
sensors={sensors}
|
||||
collisionDetection={closestCenter}
|
||||
onDragStart={handleDragStart}
|
||||
onDragEnd={handleDragEnd}
|
||||
>
|
||||
<SortableContext
|
||||
items={tempOrder.map(item => item.id)}
|
||||
strategy={verticalListSortingStrategy}
|
||||
>
|
||||
{tempOrder.map((indicator) => {
|
||||
const value = tempSettings[indicator.settingKey];
|
||||
const isChecked = typeof value === 'boolean' ? value : false;
|
||||
return (
|
||||
<SortableItem
|
||||
key={indicator.id}
|
||||
indicator={indicator}
|
||||
isDisabled={false}
|
||||
checked={isChecked}
|
||||
onToggle={() => handleToggle(indicator.settingKey)}
|
||||
colors={colors}
|
||||
updateColors={updateColors}
|
||||
indicatorSettings={indicatorSettings}
|
||||
updateIndicatorSettings={updateIndicatorSettings}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</SortableContext>
|
||||
<DragOverlay>
|
||||
{activeId ? (
|
||||
<Paper
|
||||
sx={{
|
||||
p: 1.5,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
border: '2px solid #1976d2',
|
||||
borderRadius: 1,
|
||||
bgcolor: 'background.paper',
|
||||
boxShadow: 6,
|
||||
opacity: 0.9,
|
||||
}}
|
||||
>
|
||||
<DragIndicator color="primary" sx={{ mr: 1.5 }} />
|
||||
<Typography variant="body1">
|
||||
{tempOrder.find(item => item.id === activeId)?.label}
|
||||
</Typography>
|
||||
</Paper>
|
||||
) : null}
|
||||
</DragOverlay>
|
||||
</DndContext>
|
||||
</DialogContent>
|
||||
<DialogActions sx={{ p: 2 }}>
|
||||
<Button onClick={handleReset} color="error" variant="outlined">
|
||||
초기화
|
||||
</Button>
|
||||
<Box sx={{ flex: 1 }} />
|
||||
<Button onClick={onClose} variant="outlined">
|
||||
취소
|
||||
</Button>
|
||||
<Button onClick={handleSave} variant="contained" color="primary">
|
||||
저장
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
export default IndicatorSettingsDialog;
|
||||
|
||||
Reference in New Issue
Block a user