알림목록 시간봉 필터 추가
This commit is contained in:
@@ -23,13 +23,18 @@ import TradeNotificationGridLayoutPicker from './tradeNotification/TradeNotifica
|
||||
import {
|
||||
loadTradeNotificationGridPreset,
|
||||
loadTradeNotificationListLayout,
|
||||
loadTradeNotificationCandleFilter,
|
||||
loadTradeNotificationListSort,
|
||||
saveTradeNotificationCandleFilter,
|
||||
saveTradeNotificationGridPreset,
|
||||
saveTradeNotificationListLayout,
|
||||
saveTradeNotificationListSort,
|
||||
TRADE_NOTIFICATION_CANDLE_FILTER_OPTIONS,
|
||||
type TradeNotificationCandleFilter,
|
||||
type TradeNotificationListLayout,
|
||||
type TradeNotificationListSort,
|
||||
} from '../utils/tradeNotificationListLayout';
|
||||
import { normalizeStartCandleType } from '../utils/strategyStartNodes';
|
||||
import {
|
||||
getTradeNotificationGridPreset,
|
||||
type TradeNotificationGridPresetId,
|
||||
@@ -120,6 +125,9 @@ export const TradeNotificationListPage: React.FC<Props> = ({
|
||||
const [listSort, setListSort] = useState<TradeNotificationListSort>(
|
||||
() => loadTradeNotificationListSort(),
|
||||
);
|
||||
const [candleFilter, setCandleFilter] = useState<TradeNotificationCandleFilter>(
|
||||
() => loadTradeNotificationCandleFilter(),
|
||||
);
|
||||
const [listLayout, setListLayout] = useState<TradeNotificationListLayout>(
|
||||
() => loadTradeNotificationListLayout(),
|
||||
);
|
||||
@@ -246,9 +254,17 @@ export const TradeNotificationListPage: React.FC<Props> = ({
|
||||
else setFillSell(req);
|
||||
}, [selectedMarket]);
|
||||
|
||||
const filtered = filter === 'unread'
|
||||
const filtered = useMemo(() => {
|
||||
let items = filter === 'unread'
|
||||
? allNotifications.filter(n => !n.isRead)
|
||||
: allNotifications;
|
||||
if (candleFilter) {
|
||||
items = items.filter(
|
||||
n => normalizeStartCandleType(n.candleType) === candleFilter,
|
||||
);
|
||||
}
|
||||
return items;
|
||||
}, [allNotifications, filter, candleFilter]);
|
||||
|
||||
const sorted = useMemo(
|
||||
() => sortNotifications(filtered, listSort),
|
||||
@@ -260,6 +276,11 @@ export const TradeNotificationListPage: React.FC<Props> = ({
|
||||
saveTradeNotificationListSort(sort);
|
||||
}, []);
|
||||
|
||||
const handleCandleFilterChange = useCallback((value: TradeNotificationCandleFilter) => {
|
||||
setCandleFilter(value);
|
||||
saveTradeNotificationCandleFilter(value);
|
||||
}, []);
|
||||
|
||||
const handleListLayoutChange = useCallback((layout: TradeNotificationListLayout) => {
|
||||
setListLayout(layout);
|
||||
saveTradeNotificationListLayout(layout);
|
||||
@@ -360,7 +381,17 @@ export const TradeNotificationListPage: React.FC<Props> = ({
|
||||
이전시간순
|
||||
</button>
|
||||
</div>
|
||||
<div className="tnl-toolbar-end">
|
||||
<select
|
||||
className="tnl-candle-filter-select"
|
||||
value={candleFilter}
|
||||
aria-label="시간봉 필터"
|
||||
onChange={e => handleCandleFilterChange(e.target.value as TradeNotificationCandleFilter)}
|
||||
>
|
||||
<option value="">전체</option>
|
||||
{TRADE_NOTIFICATION_CANDLE_FILTER_OPTIONS.map(o => (
|
||||
<option key={o.value} value={o.value}>{o.label}</option>
|
||||
))}
|
||||
</select>
|
||||
<div className="tnl-layout-toggle vtd-view-toggle" role="group" aria-label="목록 표시 방식">
|
||||
<button
|
||||
type="button"
|
||||
@@ -377,6 +408,8 @@ export const TradeNotificationListPage: React.FC<Props> = ({
|
||||
그리드형
|
||||
</button>
|
||||
</div>
|
||||
<span className="header-toolbar-divider" aria-hidden="true" />
|
||||
<div className="tnl-toolbar-end">
|
||||
<button
|
||||
type="button"
|
||||
className={['tnl-icon-btn', refreshing ? 'tnl-icon-btn--refreshing' : ''].filter(Boolean).join(' ')}
|
||||
@@ -392,7 +425,6 @@ export const TradeNotificationListPage: React.FC<Props> = ({
|
||||
onChange={handleGridPresetChange}
|
||||
disabled={isListView}
|
||||
/>
|
||||
<span className="tnl-toolbar-divider" aria-hidden="true" />
|
||||
<button
|
||||
type="button"
|
||||
className="tnl-icon-btn"
|
||||
|
||||
@@ -260,7 +260,12 @@ export default function BuilderPageShell({
|
||||
{headerCenter ? (
|
||||
<>
|
||||
<div className="bps-header-center">{headerCenter}</div>
|
||||
{headerActions && <div className="bps-header-actions">{headerActions}</div>}
|
||||
{headerActions && (
|
||||
<>
|
||||
<span className="header-toolbar-divider" aria-hidden="true" />
|
||||
<div className="bps-header-actions">{headerActions}</div>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
headerActions && <div className="bps-header-actions">{headerActions}</div>
|
||||
|
||||
@@ -38,6 +38,17 @@
|
||||
|
||||
.bps-header-actions { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
|
||||
|
||||
/** 옵션 컨트롤(토글·셀렉트)과 아이콘 버튼 영역 사이 구분선 */
|
||||
.header-toolbar-divider,
|
||||
.tnl-toolbar-divider {
|
||||
width: 1px;
|
||||
height: 22px;
|
||||
margin: 0 2px;
|
||||
background: color-mix(in srgb, var(--se-border, var(--border)) 90%, var(--se-text-muted, var(--text3)));
|
||||
flex-shrink: 0;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
/* 가상투자 — 타이틀바 3열 (좌측패널폭 | 세션설정 | 뷰옵션) */
|
||||
.bps-header--vtd {
|
||||
display: grid;
|
||||
|
||||
@@ -953,6 +953,23 @@
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.tnl-candle-filter-select {
|
||||
height: 32px;
|
||||
padding: 0 10px;
|
||||
border: 1px solid var(--se-border, var(--border));
|
||||
border-radius: 8px;
|
||||
background: var(--bg2, var(--se-panel-card-bg));
|
||||
color: var(--text);
|
||||
font-size: 12px;
|
||||
flex-shrink: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.tnl-candle-filter-select:focus-visible {
|
||||
outline: 2px solid color-mix(in srgb, var(--accent, #3f7ef5) 55%, transparent);
|
||||
outline-offset: 1px;
|
||||
}
|
||||
|
||||
.tnl-toolbar-end {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
@@ -39,6 +39,8 @@ export interface UiPreferences {
|
||||
gridPreset?: string;
|
||||
/** 목록 정렬 — market | newest | oldest */
|
||||
listSort?: 'market' | 'newest' | 'oldest';
|
||||
/** 시간봉 필터 — 빈 문자열=전체, 그 외 candleType (1m, 5m, …) */
|
||||
candleFilter?: string;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
import { getUiPreferences, patchUiPreferences } from './uiPreferencesDb';
|
||||
import {
|
||||
normalizeStartCandleType,
|
||||
STRATEGY_CANDLE_TYPE_OPTIONS,
|
||||
} from './strategyStartNodes';
|
||||
import {
|
||||
DEFAULT_TRADE_NOTIFICATION_GRID_PRESET,
|
||||
normalizeTradeNotificationGridPreset,
|
||||
@@ -38,3 +42,21 @@ export function loadTradeNotificationListSort(): TradeNotificationListSort {
|
||||
export function saveTradeNotificationListSort(sort: TradeNotificationListSort): void {
|
||||
patchUiPreferences({ tradeNotifications: { listSort: sort } });
|
||||
}
|
||||
|
||||
/** 알림 목록 시간봉 필터 — '' = 전체 */
|
||||
export type TradeNotificationCandleFilter = '' | (typeof STRATEGY_CANDLE_TYPE_OPTIONS)[number]['value'];
|
||||
|
||||
export function loadTradeNotificationCandleFilter(): TradeNotificationCandleFilter {
|
||||
const raw = getUiPreferences().tradeNotifications?.candleFilter;
|
||||
if (raw == null || raw === '') return '';
|
||||
const n = normalizeStartCandleType(raw);
|
||||
return STRATEGY_CANDLE_TYPE_OPTIONS.some(o => o.value === n)
|
||||
? (n as TradeNotificationCandleFilter)
|
||||
: '';
|
||||
}
|
||||
|
||||
export function saveTradeNotificationCandleFilter(filter: TradeNotificationCandleFilter): void {
|
||||
patchUiPreferences({ tradeNotifications: { candleFilter: filter || '' } });
|
||||
}
|
||||
|
||||
export { STRATEGY_CANDLE_TYPE_OPTIONS as TRADE_NOTIFICATION_CANDLE_FILTER_OPTIONS };
|
||||
|
||||
Reference in New Issue
Block a user