검증게시판 기능 추가

This commit is contained in:
Macbook
2026-05-27 15:41:19 +09:00
parent 7a0af36b9b
commit 63693d47c0
57 changed files with 4480 additions and 14 deletions
+148
View File
@@ -0,0 +1,148 @@
/**
* GoldenChart 화면·기능별 재현 경로 카탈로그 (검증 이슈 재현경로 자동완성)
*
* path: 사용자에게 표시·저장되는 경로 (메뉴-화면-… 형식)
* keywords: 입력 검색용 별칭 (메뉴명, 기능명, 영문 등)
*/
export interface AppNavigationPathEntry {
path: string;
keywords: string[];
}
const P = (path: string, ...keywords: string[]): AppNavigationPathEntry => ({
path,
keywords: [path, ...keywords],
});
/** 프로젝트에서 제공하는 주요 화면·기능 경로 */
export const APP_NAVIGATION_PATHS: AppNavigationPathEntry[] = [
// ── 상단 메뉴 ──
P('메뉴-대시보드', 'dashboard', '홈'),
P('메뉴-실시간 차트', 'chart', '실시간차트', '캔들', '차트'),
P('메뉴-가상매매', 'virtual', '가상', '모의'),
P('메뉴-추세검색', 'trend', '추세', '검색'),
P('메뉴-검증게시판', 'verification', 'QA', '이슈', '검증'),
P('메뉴-전략편집기', 'strategy-editor', '전략', '편집'),
P('메뉴-백테스팅', 'backtest', '백테스트', '히스토리'),
P('메뉴-설정', 'settings', '환경설정'),
// ── 실시간 차트 · 툴바 ──
P('메뉴-실시간 차트-종목 변경', '심볼', '검색', '마켓'),
P('메뉴-실시간 차트-워치리스트', 'watchlist', '관심'),
P('메뉴-실시간 차트-시간봉 변경', 'timeframe', '분봉', '일봉'),
P('메뉴-실시간 차트-차트 유형', '캔들', '봉', 'line', 'heikin'),
P('메뉴-실시간 차트-지표 추가 버튼', '지표', 'indicator', '추가', 'Ctrl+I', '보조지표'),
P('메뉴-실시간 차트-지표 추가 팝업-지표 검색', '지표검색', 'RSI', 'MACD', '이동평균'),
P('메뉴-실시간 차트-지표 추가 팝업-보조지표 설정', '지표설정', '활성지표'),
P('메뉴-실시간 차트-보조지표 설정 버튼', '일괄설정', '보조지표설정'),
P('메뉴-실시간 차트-실행 취소', 'undo', 'Ctrl+Z'),
P('메뉴-실시간 차트-다시 실행', 'redo', 'Ctrl+Y'),
P('메뉴-실시간 차트-자석모드', 'magnet', '스냅'),
P('메뉴-실시간 차트-전체 드로잉 삭제', '드로잉', 'drawing'),
P('메뉴-실시간 차트-통계 패널', 'stats', '통계'),
P('메뉴-실시간 차트-자동 피보나치', 'fib', '피보'),
P('메뉴-실시간 차트-그리드 표시', 'grid', '격자'),
P('메뉴-실시간 차트-트레이딩/차트 모드 전환', 'mode', '트레이딩'),
P('메뉴-실시간 차트-백테스팅 전략 선택', '백테', '전략선택'),
P('메뉴-실시간 차트-백테스팅 설정', 'bt설정'),
P('메뉴-실시간 차트-백테스팅 결과 보기', 'bt결과'),
P('메뉴-실시간 차트-실시간 전략 체크', 'live', '라이브전략'),
P('메뉴-실시간 차트-마켓 패널', '마켓', '좌측패널'),
P('메뉴-실시간 차트-확대경', 'magnifier', '돋보기'),
P('메뉴-실시간 차트-레이아웃 설정', 'layout', '분할'),
P('메뉴-실시간 차트-화면 맞춤', 'fit', 'F키'),
P('메뉴-실시간 차트-현재 시각으로 이동', 'realtime', 'now'),
P('메뉴-실시간 차트-매매 시그널 알림', 'trade', '시그널', '알림'),
P('메뉴-실시간 차트-가격 알림', 'alert', '가격'),
P('메뉴-실시간 차트-스크린샷', 'screenshot', '캡처'),
P('메뉴-실시간 차트-전체화면', 'fullscreen'),
// ── 설정 카테고리 ──
P('메뉴-설정-일반 설정', 'general', '언어', '테마'),
P('메뉴-설정-차트 설정', 'chart설정', '캔들색', '범례'),
P('메뉴-설정-보조지표 설정', 'indicators', '지표기본값', 'Main탭'),
P('메뉴-설정-백테스팅', 'bt옵션', '자본'),
P('메뉴-설정-전략 설정', 'strategy', '전략기본'),
P('메뉴-설정-가상매매', 'paper', '가상자본'),
P('메뉴-설정-추세검색', 'trend설정', '배점'),
P('메뉴-설정-알림 설정', 'alert설정', '소리', '팝업'),
P('메뉴-설정-네트워크', 'network', 'API', 'WebSocket'),
P('메뉴-설정-관리자 설정', 'admin', '권한', '역할'),
// ── 가상매매 ──
P('메뉴-가상매매-투자대상 그리드', '타겟', '종목'),
P('메뉴-가상매매-우측 매매 탭', '주문', 'trade'),
P('메뉴-가상매매-우측 호가 탭', '호가', 'orderbook'),
P('메뉴-가상매매-우측 체결 탭', '체결', 'history'),
// ── 추세검색 ──
P('메뉴-추세검색-검색 실행', 'run', '스캔'),
P('메뉴-추세검색-결과 카드', '결과', '차트미리보기'),
// ── 검증게시판 ──
P('메뉴-검증게시판-이슈 등록', '등록', '추가'),
P('메뉴-검증게시판-이슈 수정', '수정', '편집'),
P('메뉴-검증게시판-이슈 삭제', '삭제'),
P('메뉴-검증게시판-첨부 이미지', '이미지', '스크린샷', '첨부'),
P('메뉴-검증게시판-댓글', 'comment', '코멘트'),
// ── 전략편집기 ──
P('메뉴-전략편집기-전략 목록', '목록', '리스트'),
P('메뉴-전략편집기-노드 캔버스', 'flow', '노드', '조건'),
P('메뉴-전략편집기-지표 팔레트', 'palette', '팔레트'),
P('메뉴-전략편집기-전략 저장', 'save'),
P('메뉴-전략편집기-전략 JSON보내기', 'export', '보내기'),
P('메뉴-전략편집기-전략 JSON 가져오기', 'import', '가져오기'),
// ── 백테스팅 ──
P('메뉴-백테스팅-결과 목록', '히스토리', 'history'),
P('메뉴-백테스팅-분석 차트', 'analysis'),
// ── 상단 메뉴바 기타 ──
P('메뉴-알림 목록', 'notifications', '시그널목록'),
P('메뉴-테마 전환', 'theme', '다크', '라이트'),
P('메뉴-로그인', 'login', 'auth'),
];
function normalizeForSearch(s: string): string {
return s.toLowerCase().replace(/\s+/g, '');
}
/**
* 입력 문자열과 관련된 경로 후보 (최대 limit건, 관련도 순)
*/
export function searchAppNavigationPaths(query: string, limit = 12): AppNavigationPathEntry[] {
const q = query.trim();
if (!q) return APP_NAVIGATION_PATHS.slice(0, limit);
const nq = normalizeForSearch(q);
const tokens = q.toLowerCase().split(/\s+/).filter(Boolean);
type Scored = { entry: AppNavigationPathEntry; score: number };
const scored: Scored[] = [];
for (const entry of APP_NAVIGATION_PATHS) {
let score = 0;
const pathNorm = normalizeForSearch(entry.path);
if (entry.path.includes(q)) score += 80;
if (pathNorm.includes(nq)) score += 60;
if (pathNorm.startsWith(nq)) score += 40;
for (const kw of entry.keywords) {
const kn = normalizeForSearch(kw);
if (kw.includes(q)) score += 50;
if (kn.includes(nq)) score += 35;
if (kn.startsWith(nq)) score += 25;
for (const t of tokens) {
if (kn.includes(t) || kw.toLowerCase().includes(t)) score += 15;
}
}
if (score > 0) scored.push({ entry, score });
}
scored.sort((a, b) => b.score - a.score || a.entry.path.localeCompare(b.entry.path, 'ko'));
return scored.slice(0, limit).map(s => s.entry);
}