// ─────────────────────────────────────────────────────────── // Huio Control Panel — Docs Views: Roadmap & Changelog (cp-docs-views.jsx) // ─────────────────────────────────────────────────────────── if (!window.CP_STYLES) throw new Error('[cp-docs-views] window.CP_STYLES not loaded (check cp-styles.jsx)'); const parseRoadmap = (md) => { if (!md) return []; const lines = md.split('\n'); const categories = []; let currentCat = null; lines.forEach(line => { if (line.startsWith('### ')) { const title = line.replace('### ', '').trim(); currentCat = { title: title, tasks: [] }; categories.push(currentCat); } else if (line.startsWith('## ') && !line.includes('🧭') && !line.includes('📈') && !line.includes('Какая') && !line.includes('Правила') && !line.includes('Настройка') && !line.includes('At A Glance') && !line.includes('Quick Navigation') && !line.includes('Стратегия')) { const title = line.replace('## ', '').trim(); currentCat = { title: title, tasks: [], isMainSection: true }; categories.push(currentCat); } else if (currentCat) { const taskMatch = line.match(/^\s*-\s*\[(x| )\]\s*(.+)$/i); if (taskMatch) { currentCat.tasks.push({ text: taskMatch[2].trim(), completed: taskMatch[1].toLowerCase() === 'x' }); } } }); return categories.filter(c => c.tasks.length > 0); }; const parseChangelog = (md) => { if (!md) return []; const lines = md.split('\n'); const releases = []; let currentRelease = null; let currentSection = null; lines.forEach(line => { const releaseMatch = line.match(/^##\s+\[([^\]]+)\]\s*—\s*(.+)$/); if (releaseMatch) { currentRelease = { version: releaseMatch[1], date: releaseMatch[2].trim(), sections: [] }; releases.push(currentRelease); currentSection = null; } else if (currentRelease && line.startsWith('### ')) { const secTitle = line.replace('### ', '').trim(); currentSection = { title: secTitle, items: [] }; currentRelease.sections.push(currentSection); } else if (currentRelease && line.trim().startsWith('- ')) { const itemText = line.trim().substring(2); if (currentSection) { currentSection.items.push(itemText); } else { if (currentRelease.sections.length === 0) { currentSection = { title: 'General', items: [] }; currentRelease.sections.push(currentSection); } currentRelease.sections[0].items.push(itemText); } } }); return releases; }; const formatTaskText = (text) => { const parts = []; let lastIndex = 0; const regex = /\[\[([^\]|]+)(?:\|([^\]]+))?\]\]/g; let match; while ((match = regex.exec(text)) !== null) { if (match.index > lastIndex) { parts.push(text.substring(lastIndex, match.index)); } const linkPath = match[1]; const displayName = match[2] || linkPath.split('/').pop(); parts.push( {displayName} ); lastIndex = regex.lastIndex; } if (lastIndex < text.length) { parts.push(text.substring(lastIndex)); } return parts.length > 0 ? parts : text; }; const renderRoadmap = ({ lang, appStyles = window.CP_STYLES, isRoadmapLoading, roadmapContent, roadmapSearch, setRoadmapSearch, roadmapFilter, setRoadmapFilter, }) => { if (isRoadmapLoading) { return (
); } const categories = parseRoadmap(roadmapContent); let total = 0; let completed = 0; categories.forEach(c => { c.tasks.forEach(t => { total++; if (t.completed) completed++; }); }); const percent = total > 0 ? Math.round((completed / total) * 100) : 95; const filteredCategories = categories.map(cat => { const filteredTasks = cat.tasks.filter(task => { if (roadmapSearch.trim() && !task.text.toLowerCase().includes(roadmapSearch.toLowerCase())) { return false; } if (roadmapFilter === 'completed' && !task.completed) return false; if (roadmapFilter === 'pending' && task.completed) return false; return true; }); return { ...cat, tasks: filteredTasks }; }).filter(cat => cat.tasks.length > 0); const activeTheme = document.documentElement.getAttribute('data-theme') || 'candy'; const btnActiveStyle = (theme) => ({ background: 'var(--hu-bg-inverse)', color: 'var(--hu-fg-inverse)', border: 'none', borderRadius: 'var(--hu-r-pill)', fontSize: '10px', fontWeight: '700', padding: '4px 10px', cursor: 'pointer' }); const btnInactiveStyle = { background: 'transparent', color: 'var(--hu-fg-3)', border: 'none', borderRadius: 'var(--hu-r-pill)', fontSize: '10px', fontWeight: '600', padding: '4px 10px', cursor: 'pointer' }; return (

{lang === 'vi' ? 'Lộ trình phát triển' : 'Product Roadmap'}

{lang === 'vi' ? 'Theo dõi tiến trình phát triển và hoàn thiện các chức năng hệ thống.' : 'Track the implementation progress of system features and release phases.'}

{lang === 'vi' ? 'Tiến độ tổng thể' : 'Overall Progress'}

{lang === 'vi' ? 'Hoàn thành' : 'Completion Rate'}: {percent}%

{completed} / {total} {lang === 'vi' ? 'nhiệm vụ' : 'tasks'}
setRoadmapSearch(e.target.value)} style={{ padding: '8px 16px', borderRadius: 'var(--hu-r-pill)', border: '1.5px solid var(--hu-border)', background: 'var(--hu-bg-card)', color: 'var(--hu-fg)', outline: 'none', fontSize: '13px', width: '100%', maxWidth: '300px', fontFamily: 'inherit' }} />
{filteredCategories.map((cat, idx) => { const catTotal = cat.tasks.length; const catCompleted = cat.tasks.filter(t => t.completed).length; const catPercent = catTotal > 0 ? Math.round((catCompleted / catTotal) * 100) : 0; return (

{cat.title}

{catPercent}%
{cat.tasks.map((task, tIdx) => (
{task.completed && '✓'} {formatTaskText(task.text)}
))}
); })} {filteredCategories.length === 0 && (
{lang === 'vi' ? 'Không tìm thấy tính năng nào khớp.' : 'No matching features found.'}
)}
); }; const renderChangelog = ({ lang, appStyles = window.CP_STYLES, isChangelogLoading, changelogContent, changelogSearch, setChangelogSearch, changelogLimit, setChangelogLimit, }) => { if (isChangelogLoading) { return (
); } const releases = parseChangelog(changelogContent); const filteredReleases = releases.map(rel => { const filteredSections = rel.sections.map(sec => { const filteredItems = sec.items.filter(item => { if (changelogSearch.trim()) { return item.toLowerCase().includes(changelogSearch.toLowerCase()); } return true; }); return { ...sec, items: filteredItems }; }).filter(sec => sec.items.length > 0); return { ...rel, sections: filteredSections }; }).filter(rel => rel.sections.length > 0); const isSearching = Boolean(changelogSearch.trim()); const visibleReleases = isSearching ? filteredReleases : filteredReleases.slice(0, changelogLimit); const hasMoreReleases = !isSearching && filteredReleases.length > changelogLimit; const parseChangelogItem = (itemText) => { const match = itemText.match(/^\*\*([^*:]+)\*\*\s*:\s*(.+)$/); if (match) { return { tag: match[1].trim(), content: match[2].trim() }; } return { tag: null, content: itemText }; }; const getTagColorClass = (tag) => { if (!tag) return ''; const t = tag.toLowerCase(); if (t.includes('bug') || t.includes('fix') || t.includes('ошиб')) return 'hu-chip-coral'; if (t.includes('perf') || t.includes('оптими')) return 'hu-chip-green'; if (t.includes('legal') || t.includes('юрид')) return 'hu-chip-ochre'; if (t.includes('design') || t.includes('дизайн') || t.includes('ui')) return 'hu-chip-gold'; if (t.includes('push') || t.includes('увед') || t.includes('notif')) return 'hu-chip-teal'; return ''; }; return (

{lang === 'vi' ? 'Lịch sử thay đổi' : 'System Changelog'}

{lang === 'vi' ? 'Nhật ký các bản cập nhật, sửa lỗi và cải tiến hệ thống.' : 'Chronological timeline of product releases, system bug fixes, and feature enhancements.'}

setChangelogSearch(e.target.value)} style={{ padding: '8px 16px', borderRadius: 'var(--hu-r-pill)', border: '1.5px solid var(--hu-border)', background: 'var(--hu-bg-card)', color: 'var(--hu-fg)', outline: 'none', fontSize: '13px', width: '100%', maxWidth: '300px', fontFamily: 'inherit' }} />
{visibleReleases.length === 0 && (

{lang === 'vi' ? 'Không tìm thấy nhật ký phiên bản nào phù hợp.' : 'No matching release logs found.'}

)} {visibleReleases.map((rel, idx) => (
v{rel.version} 📅 {rel.date}
{rel.sections.map((sec, sIdx) => (
{sec.title && sec.title !== 'General' && (

{sec.title}

)}
{sec.items.map((itemText, iIdx) => { const parsed = parseChangelogItem(itemText); return (
✦
{parsed.tag && ( {parsed.tag} )} {formatTaskText(parsed.content)}
); })}
))}
))} {hasMoreReleases && (
)} {filteredReleases.length === 0 && (
{lang === 'vi' ? 'Không tìm thấy bản cập nhật nào.' : 'No update logs found.'}
)}
); }; window.CpDocsViews = { parseRoadmap, parseChangelog, formatTaskText, renderRoadmap, renderChangelog };