모의투자 로직 변경
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
import React from 'react';
|
||||
import { FORMAL_LOGIN_REQUIRED_MSG } from '../utils/tradingAccess';
|
||||
|
||||
interface Props {
|
||||
onLogin: () => void;
|
||||
title?: string;
|
||||
}
|
||||
|
||||
/** 게스트·비로그인 — 매매 관련 화면 대체 */
|
||||
export const FormalLoginGate: React.FC<Props> = ({
|
||||
onLogin,
|
||||
title = '정식 로그인이 필요합니다',
|
||||
}) => (
|
||||
<div className="formal-login-gate">
|
||||
<h2 className="formal-login-gate__title">{title}</h2>
|
||||
<p className="formal-login-gate__desc">{FORMAL_LOGIN_REQUIRED_MSG}</p>
|
||||
<button type="button" className="formal-login-gate__btn" onClick={onLogin}>
|
||||
로그인
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
|
||||
export default FormalLoginGate;
|
||||
@@ -33,6 +33,7 @@ import {
|
||||
import TradeAlertPopupPreview from './TradeAlertPopupPreview';
|
||||
import TrendSearchSettingsPanel from './trendSearch/TrendSearchSettingsPanel';
|
||||
import type { TrendSearchAppSettings } from '../utils/trendSearchAppSettings';
|
||||
import { isTradingSettingsCategory } from '../utils/tradingAccess';
|
||||
import {
|
||||
CHART_LEGEND_SETTING_ITEMS,
|
||||
type ChartLegendVisibility,
|
||||
@@ -168,6 +169,9 @@ interface SettingsPageProps {
|
||||
onTrendSearchSettingsChange?: (s: TrendSearchAppSettings) => void;
|
||||
/** 설정 화면 진입 시 선택할 카테고리 (예: 모의투자 화면에서 링크) */
|
||||
initialCategory?: SettingsCategoryId;
|
||||
/** 정식 로그인 여부 (게스트는 매매·전략 설정 탭 차단) */
|
||||
isFormalLogin?: boolean;
|
||||
onRequireFormalLogin?: () => void;
|
||||
}
|
||||
|
||||
// ── 카테고리 정의 ────────────────────────────────────────────────────────────
|
||||
@@ -1743,6 +1747,8 @@ const SettingsPage: React.FC<SettingsPageProps> = ({
|
||||
trendSearchSettings,
|
||||
onTrendSearchSettingsChange,
|
||||
initialCategory,
|
||||
isFormalLogin = true,
|
||||
onRequireFormalLogin,
|
||||
}) => {
|
||||
const categories = filterCategories(menuPermissions);
|
||||
const [active, setActive] = useState<CategoryId>(initialCategory ?? 'general');
|
||||
@@ -1757,10 +1763,14 @@ const SettingsPage: React.FC<SettingsPageProps> = ({
|
||||
|
||||
useEffect(() => {
|
||||
if (!initialCategory) return;
|
||||
if (!isFormalLogin && isTradingSettingsCategory(initialCategory)) {
|
||||
onRequireFormalLogin?.();
|
||||
return;
|
||||
}
|
||||
if (categories.some(c => c.id === initialCategory)) {
|
||||
setActive(initialCategory);
|
||||
}
|
||||
}, [initialCategory, categories]);
|
||||
}, [initialCategory, categories, isFormalLogin, onRequireFormalLogin]);
|
||||
|
||||
useEffect(() => {
|
||||
if (active !== 'indicators') setIndicatorSaveUi(null);
|
||||
@@ -1948,7 +1958,13 @@ const SettingsPage: React.FC<SettingsPageProps> = ({
|
||||
<button
|
||||
key={cat.id}
|
||||
className={`stg-sidebar-item ${active === cat.id ? 'stg-sidebar-item--active' : ''}`}
|
||||
onClick={() => setActive(cat.id)}
|
||||
onClick={() => {
|
||||
if (!isFormalLogin && isTradingSettingsCategory(cat.id)) {
|
||||
onRequireFormalLogin?.();
|
||||
return;
|
||||
}
|
||||
setActive(cat.id);
|
||||
}}
|
||||
>
|
||||
<span className="stg-sidebar-icon">{cat.icon}</span>
|
||||
<span className="stg-sidebar-label" style={{ fontSize: 13.5, fontWeight: 'inherit' }}>{cat.label}</span>
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
*/
|
||||
import React, { useState } from 'react';
|
||||
import { loginUser, type LoginResponse } from '../utils/backendApi';
|
||||
import SplashGradientBackdrop from './splash/SplashGradientBackdrop';
|
||||
import '../styles/splashScreen.css';
|
||||
|
||||
const DEFAULT_USERNAME = 'admin';
|
||||
@@ -36,8 +37,7 @@ const SplashScreen: React.FC<Props> = ({ onLoginSuccess, onGuest }) => {
|
||||
|
||||
return (
|
||||
<div className="splash">
|
||||
<div className="splash-photo-bg" aria-hidden />
|
||||
<div className="splash-photo-overlay" aria-hidden />
|
||||
<SplashGradientBackdrop />
|
||||
|
||||
<div className="splash-center">
|
||||
<div className="splash-glass">
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* 로그인 스플래시 — 중앙→우상단 대각선으로 흐릿한 캔들·호가 실루엣 + 메시 그라데이션
|
||||
*/
|
||||
import React from 'react';
|
||||
import SplashChartBackground from './SplashChartBackground';
|
||||
import SplashOrderbookBackground from './SplashOrderbookBackground';
|
||||
|
||||
const SplashGradientBackdrop: React.FC = () => (
|
||||
<div className="splash-backdrop" aria-hidden>
|
||||
<div className="splash-backdrop__base" />
|
||||
<div className="splash-backdrop__mesh" />
|
||||
|
||||
<div className="splash-backdrop__orbs">
|
||||
<span className="splash-backdrop__orb splash-backdrop__orb--gold" />
|
||||
<span className="splash-backdrop__orb splash-backdrop__orb--teal" />
|
||||
<span className="splash-backdrop__orb splash-backdrop__orb--indigo" />
|
||||
</div>
|
||||
|
||||
{/* 캔들 + 호가 — 화면 중앙에서 우측 상단으로 퍼지는 그라데이션 마스크 */}
|
||||
<div className="splash-backdrop__market-flow">
|
||||
<div className="splash-backdrop__flow-glow" />
|
||||
<div className="splash-backdrop__flow-inner">
|
||||
<div className="splash-backdrop__chart-layer">
|
||||
<SplashChartBackground />
|
||||
</div>
|
||||
<div className="splash-backdrop__orderbook-layer">
|
||||
<SplashOrderbookBackground />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="splash-backdrop__grid" />
|
||||
<div className="splash-backdrop__grain" />
|
||||
<div className="splash-backdrop__vignette" />
|
||||
<div className="splash-backdrop__spotlight" />
|
||||
</div>
|
||||
);
|
||||
|
||||
export default SplashGradientBackdrop;
|
||||
@@ -0,0 +1,69 @@
|
||||
import React, { useMemo } from 'react';
|
||||
|
||||
const W = 420;
|
||||
const H = 560;
|
||||
const ROWS = 14;
|
||||
const MID = H / 2;
|
||||
|
||||
/** 호가창 실루엣 — 로그인 배경용 */
|
||||
export default function SplashOrderbookBackground() {
|
||||
const rows = useMemo(() => {
|
||||
const out: { y: number; askW: number; bidW: number; askPx: number; bidPx: number }[] = [];
|
||||
for (let i = 0; i < ROWS; i++) {
|
||||
const t = i / (ROWS - 1);
|
||||
const y = 36 + t * (H - 72);
|
||||
const dist = Math.abs(y - MID) / MID;
|
||||
const spread = 0.35 + dist * 0.65;
|
||||
const askW = 40 + (1 - dist) * 90 + Math.sin(i * 1.1) * 12;
|
||||
const bidW = 38 + (1 - dist) * 85 + Math.cos(i * 0.9) * 10;
|
||||
const base = 118000 + (0.5 - t) * 4200;
|
||||
out.push({
|
||||
y,
|
||||
askW,
|
||||
bidW,
|
||||
askPx: base + spread * 120,
|
||||
bidPx: base - spread * 115,
|
||||
});
|
||||
}
|
||||
return out;
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<svg
|
||||
className="splash-orderbook-bg"
|
||||
viewBox={`0 0 ${W} ${H}`}
|
||||
preserveAspectRatio="xMidYMid meet"
|
||||
aria-hidden
|
||||
>
|
||||
<rect width={W} height={H} fill="transparent" />
|
||||
<line x1={W / 2} y1={28} x2={W / 2} y2={H - 28} className="splash-ob-mid" />
|
||||
{rows.map((r, i) => (
|
||||
<g key={i}>
|
||||
<rect
|
||||
x={W / 2 - r.askW}
|
||||
y={r.y - 9}
|
||||
width={r.askW}
|
||||
height={18}
|
||||
className="splash-ob-ask-bar"
|
||||
rx="2"
|
||||
/>
|
||||
<rect
|
||||
x={W / 2}
|
||||
y={r.y - 9}
|
||||
width={r.bidW}
|
||||
height={18}
|
||||
className="splash-ob-bid-bar"
|
||||
rx="2"
|
||||
/>
|
||||
<text x={W / 2 - 10} y={r.y + 4} className="splash-ob-ask-txt" textAnchor="end">
|
||||
{Math.round(r.askPx).toLocaleString()}
|
||||
</text>
|
||||
<text x={W / 2 + 10} y={r.y + 4} className="splash-ob-bid-txt" textAnchor="start">
|
||||
{Math.round(r.bidPx).toLocaleString()}
|
||||
</text>
|
||||
</g>
|
||||
))}
|
||||
<rect x={W / 2 - 72} y={MID - 14} width={144} height={28} className="splash-ob-spread" rx="4" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user