597 lines
15 KiB
JavaScript
597 lines
15 KiB
JavaScript
'use strict';
|
||
'require view';
|
||
'require ui';
|
||
'require poll';
|
||
'require rpc';
|
||
'require secubox/api as API';
|
||
'require secubox-theme/theme as Theme';
|
||
'require secubox/nav as SecuNav';
|
||
|
||
// Load theme resources
|
||
document.head.appendChild(E('link', {
|
||
'rel': 'stylesheet',
|
||
'type': 'text/css',
|
||
'href': L.resource('secubox-theme/secubox-theme.css')
|
||
}));
|
||
|
||
var secuLang = (typeof L !== 'undefined' && L.env && L.env.lang) ||
|
||
(document.documentElement && document.documentElement.getAttribute('lang')) ||
|
||
(navigator.language ? navigator.language.split('-')[0] : 'en');
|
||
Theme.init({ language: secuLang });
|
||
|
||
/**
|
||
* SecuBox Development Status View (LuCI)
|
||
* Real-time development progress tracker for LuCI interface
|
||
*/
|
||
return view.extend({
|
||
statusData: {},
|
||
// Development milestones data
|
||
milestones: {
|
||
'modules-core': {
|
||
name: _('Core Modules'),
|
||
progress: 100,
|
||
total: 13,
|
||
completed: 13,
|
||
icon: '📦',
|
||
color: '#10b981',
|
||
items: [
|
||
{ name: 'Bandwidth Manager', status: 'completed' },
|
||
{ name: 'Auth Guardian', status: 'completed' },
|
||
{ name: 'Media Flow', status: 'completed' },
|
||
{ name: 'VHost Manager', status: 'completed' },
|
||
{ name: 'CrowdSec Dashboard', status: 'completed' },
|
||
{ name: 'WireGuard Dashboard', status: 'completed' },
|
||
{ name: 'Netdata Dashboard', status: 'completed' },
|
||
{ name: 'Netifyd Dashboard', status: 'completed' },
|
||
{ name: 'Client Guardian', status: 'completed' },
|
||
{ name: 'Network Modes', status: 'completed' },
|
||
{ name: 'Traffic Shaper', status: 'completed' },
|
||
{ name: 'CDN Cache', status: 'completed' },
|
||
{ name: 'SecuBox Hub', status: 'completed' }
|
||
]
|
||
},
|
||
'hardware-support': {
|
||
name: _('Hardware Support'),
|
||
progress: 95,
|
||
total: 4,
|
||
completed: 3,
|
||
icon: '🔧',
|
||
color: '#f59e0b',
|
||
items: [
|
||
{ name: 'ESPRESSObin Ultra', status: 'completed' },
|
||
{ name: 'Sheeva64 (WiFi 6)', status: 'completed' },
|
||
{ name: 'MOCHAbin (10GbE)', status: 'completed' },
|
||
{ name: 'Performance Optimization', status: 'in-progress' }
|
||
]
|
||
},
|
||
'integration': {
|
||
name: _('Integration & Testing'),
|
||
progress: 85,
|
||
total: 6,
|
||
completed: 5,
|
||
icon: '🧪',
|
||
color: '#3b82f6',
|
||
items: [
|
||
{ name: 'LuCI Integration', status: 'completed' },
|
||
{ name: 'RPCD Backends', status: 'completed' },
|
||
{ name: 'ubus APIs', status: 'completed' },
|
||
{ name: 'Multi-platform Build', status: 'completed' },
|
||
{ name: 'Documentation', status: 'completed' },
|
||
{ name: 'Beta Testing Program', status: 'in-progress' }
|
||
]
|
||
},
|
||
'campaign-prep': {
|
||
name: _('Campaign Preparation'),
|
||
progress: 70,
|
||
total: 5,
|
||
completed: 3,
|
||
icon: '🚀',
|
||
color: '#8b5cf6',
|
||
items: [
|
||
{ name: 'Website Multi-language', status: 'completed' },
|
||
{ name: 'Demo Pages', status: 'completed' },
|
||
{ name: 'Video Tutorials', status: 'in-progress' },
|
||
{ name: 'Marketing Materials', status: 'in-progress' },
|
||
{ name: 'Crowdfunding Setup', status: 'planned' }
|
||
]
|
||
}
|
||
},
|
||
|
||
// Timeline data
|
||
timeline: [
|
||
{
|
||
phase: _('Phase 1'),
|
||
name: _('Core Development'),
|
||
period: 'Q4 2024 - Q1 2025',
|
||
status: 'completed',
|
||
progress: 100
|
||
},
|
||
{
|
||
phase: _('Phase 2'),
|
||
name: _('Advanced Modules'),
|
||
period: 'Q1 - Q2 2025',
|
||
status: 'completed',
|
||
progress: 100
|
||
},
|
||
{
|
||
phase: _('Phase 3'),
|
||
name: _('Hardware Integration'),
|
||
period: 'Q2 - Q4 2025',
|
||
status: 'in-progress',
|
||
progress: 95
|
||
},
|
||
{
|
||
phase: _('Phase 4'),
|
||
name: _('Beta Testing'),
|
||
period: 'Q1 2026',
|
||
status: 'in-progress',
|
||
progress: 40
|
||
},
|
||
{
|
||
phase: _('Phase 5'),
|
||
name: _('Crowdfunding Campaign'),
|
||
period: 'Q2 2026',
|
||
status: 'planned',
|
||
progress: 20
|
||
},
|
||
{
|
||
phase: _('Phase 6'),
|
||
name: _('Production & Delivery'),
|
||
period: 'Q3 - Q4 2026',
|
||
status: 'planned',
|
||
progress: 0
|
||
}
|
||
],
|
||
|
||
// Project statistics
|
||
stats: {
|
||
modulesCount: 13,
|
||
languagesSupported: 11,
|
||
architectures: 4,
|
||
linesOfCode: 15000,
|
||
contributors: 3,
|
||
commits: 450,
|
||
openIssues: 12,
|
||
closedIssues: 87
|
||
},
|
||
|
||
/**
|
||
* Calculate overall progress
|
||
*/
|
||
getOverallProgress: function() {
|
||
var milestones = Object.values(this.milestones);
|
||
var totalProgress = 0;
|
||
for (var i = 0; i < milestones.length; i++) {
|
||
totalProgress += milestones[i].progress;
|
||
}
|
||
return Math.round(totalProgress / milestones.length);
|
||
},
|
||
|
||
/**
|
||
* Get status icon
|
||
*/
|
||
getStatusIcon: function(status) {
|
||
var icons = {
|
||
'completed': '✅',
|
||
'in-progress': '🔄',
|
||
'planned': '📋'
|
||
};
|
||
return icons[status] || '⚪';
|
||
},
|
||
|
||
/**
|
||
* Render milestone card
|
||
*/
|
||
renderMilestone: function(key, milestone) {
|
||
var items = milestone.items.map(function(item) {
|
||
var statusClass = 'dsw-item-' + item.status;
|
||
return E('div', { 'class': 'dsw-item ' + statusClass }, [
|
||
E('span', { 'class': 'dsw-item-icon' }, this.getStatusIcon(item.status)),
|
||
E('span', { 'class': 'dsw-item-name' }, item.name)
|
||
]);
|
||
}.bind(this));
|
||
|
||
return E('div', { 'class': 'cbi-section', 'data-key': key }, [
|
||
E('div', { 'class': 'dsw-milestone-header' }, [
|
||
E('div', { 'class': 'dsw-milestone-info' }, [
|
||
E('span', { 'class': 'dsw-milestone-icon' }, milestone.icon),
|
||
E('span', { 'class': 'dsw-milestone-name' }, milestone.name)
|
||
]),
|
||
E('div', { 'class': 'dsw-milestone-stats' }, [
|
||
E('span', { 'class': 'dsw-milestone-count' },
|
||
milestone.completed + '/' + milestone.total),
|
||
E('span', { 'class': 'dsw-milestone-percent' },
|
||
{ 'style': 'color: ' + milestone.color },
|
||
milestone.progress + '%')
|
||
])
|
||
]),
|
||
E('div', { 'class': 'cbi-progressbar', 'title': milestone.progress + '%' }, [
|
||
E('div', {
|
||
'style': 'width: ' + milestone.progress + '%; background: ' + milestone.color
|
||
})
|
||
]),
|
||
E('div', { 'class': 'dsw-milestone-items' }, items)
|
||
]);
|
||
},
|
||
|
||
/**
|
||
* Render timeline item
|
||
*/
|
||
renderTimelineItem: function(phase, index, total) {
|
||
var statusClass = 'dsw-timeline-' + phase.status;
|
||
var showLine = index < total - 1;
|
||
|
||
return E('div', { 'class': 'dsw-timeline-item ' + statusClass }, [
|
||
E('div', { 'class': 'dsw-timeline-marker' }, [
|
||
E('div', { 'class': 'dsw-timeline-dot' }),
|
||
showLine ? E('div', { 'class': 'dsw-timeline-line' }) : null
|
||
]),
|
||
E('div', { 'class': 'dsw-timeline-content' }, [
|
||
E('div', { 'class': 'dsw-timeline-header' }, [
|
||
E('span', { 'class': 'dsw-timeline-phase' }, phase.phase),
|
||
E('span', { 'class': 'dsw-timeline-period' }, phase.period)
|
||
]),
|
||
E('div', { 'class': 'dsw-timeline-name' }, phase.name),
|
||
E('div', { 'class': 'cbi-progressbar', 'title': phase.progress + '%' }, [
|
||
E('div', { 'style': 'width: ' + phase.progress + '%' })
|
||
])
|
||
])
|
||
]);
|
||
},
|
||
|
||
/**
|
||
* Render statistics grid
|
||
*/
|
||
renderStats: function() {
|
||
return E('div', { 'class': 'dsw-stats-grid' }, [
|
||
E('div', { 'class': 'dsw-stat' }, [
|
||
E('div', { 'class': 'dsw-stat-value' }, String(this.stats.modulesCount)),
|
||
E('div', { 'class': 'dsw-stat-label' }, _('Modules'))
|
||
]),
|
||
E('div', { 'class': 'dsw-stat' }, [
|
||
E('div', { 'class': 'dsw-stat-value' }, String(this.stats.languagesSupported)),
|
||
E('div', { 'class': 'dsw-stat-label' }, _('Languages'))
|
||
]),
|
||
E('div', { 'class': 'dsw-stat' }, [
|
||
E('div', { 'class': 'dsw-stat-value' }, String(this.stats.architectures)),
|
||
E('div', { 'class': 'dsw-stat-label' }, _('Architectures'))
|
||
]),
|
||
E('div', { 'class': 'dsw-stat' }, [
|
||
E('div', { 'class': 'dsw-stat-value' }, (this.stats.linesOfCode / 1000).toFixed(1) + 'k'),
|
||
E('div', { 'class': 'dsw-stat-label' }, _('Lines of Code'))
|
||
]),
|
||
E('div', { 'class': 'dsw-stat' }, [
|
||
E('div', { 'class': 'dsw-stat-value' }, String(this.stats.contributors)),
|
||
E('div', { 'class': 'dsw-stat-label' }, _('Contributors'))
|
||
]),
|
||
E('div', { 'class': 'dsw-stat' }, [
|
||
E('div', { 'class': 'dsw-stat-value' }, String(this.stats.commits)),
|
||
E('div', { 'class': 'dsw-stat-label' }, _('Commits'))
|
||
]),
|
||
E('div', { 'class': 'dsw-stat' }, [
|
||
E('div', { 'class': 'dsw-stat-value' }, String(this.stats.openIssues)),
|
||
E('div', { 'class': 'dsw-stat-label' }, _('Open Issues'))
|
||
]),
|
||
E('div', { 'class': 'dsw-stat' }, [
|
||
E('div', { 'class': 'dsw-stat-value' }, String(this.stats.closedIssues)),
|
||
E('div', { 'class': 'dsw-stat-label' }, _('Closed Issues'))
|
||
])
|
||
]);
|
||
},
|
||
|
||
/**
|
||
* Load view
|
||
*/
|
||
load: function() {
|
||
var self = this;
|
||
return API.getStatus().then(function(status) {
|
||
self.statusData = status || {};
|
||
return status;
|
||
});
|
||
},
|
||
|
||
/**
|
||
* Render view
|
||
*/
|
||
render: function() {
|
||
var overallProgress = this.getOverallProgress();
|
||
|
||
// Render milestones
|
||
var milestones = [];
|
||
for (var key in this.milestones) {
|
||
milestones.push(this.renderMilestone(key, this.milestones[key]));
|
||
}
|
||
|
||
// Render timeline
|
||
var timelineItems = [];
|
||
for (var i = 0; i < this.timeline.length; i++) {
|
||
timelineItems.push(
|
||
this.renderTimelineItem(this.timeline[i], i, this.timeline.length)
|
||
);
|
||
}
|
||
|
||
var main = E('div', { 'class': 'secubox-dev-body' }, [
|
||
E('h2', { 'class': 'section-title' }, _('Development Status')),
|
||
E('div', { 'class': 'cbi-map-descr' },
|
||
_('Real-time project progress tracker showing SecuBox development milestones and achievements.')),
|
||
|
||
E('div', { 'class': 'cbi-section' }, [
|
||
E('div', { 'class': 'dsw-header' }, [
|
||
E('div', { 'class': 'dsw-overall-badge' }, [
|
||
E('div', { 'class': 'dsw-progress-value' }, overallProgress + '%'),
|
||
E('div', { 'class': 'dsw-progress-label' }, _('Overall Progress'))
|
||
])
|
||
])
|
||
]),
|
||
|
||
E('fieldset', { 'class': 'cbi-section' }, [
|
||
E('legend', {}, _('Development Milestones')),
|
||
E('div', { 'class': 'dsw-milestones-grid' }, milestones)
|
||
]),
|
||
|
||
E('fieldset', { 'class': 'cbi-section' }, [
|
||
E('legend', {}, _('Project Timeline')),
|
||
E('div', { 'class': 'dsw-timeline-container' }, timelineItems)
|
||
]),
|
||
|
||
E('fieldset', { 'class': 'cbi-section' }, [
|
||
E('legend', {}, _('Project Statistics')),
|
||
this.renderStats()
|
||
]),
|
||
|
||
E('style', {}, `
|
||
.dsw-header {
|
||
text-align: center;
|
||
padding: 20px;
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.dsw-overall-badge {
|
||
display: inline-block;
|
||
background: linear-gradient(135deg, #10b981, #06b6d4);
|
||
padding: 24px 48px;
|
||
border-radius: 12px;
|
||
color: white;
|
||
}
|
||
|
||
.dsw-progress-value {
|
||
font-size: 48px;
|
||
font-weight: 800;
|
||
line-height: 1;
|
||
}
|
||
|
||
.dsw-progress-label {
|
||
font-size: 14px;
|
||
opacity: 0.9;
|
||
margin-top: 8px;
|
||
}
|
||
|
||
.dsw-milestone-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 12px;
|
||
}
|
||
|
||
.dsw-milestone-info {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10px;
|
||
}
|
||
|
||
.dsw-milestone-icon {
|
||
font-size: 24px;
|
||
}
|
||
|
||
.dsw-milestone-name {
|
||
font-size: 16px;
|
||
font-weight: 700;
|
||
}
|
||
|
||
.dsw-milestone-stats {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12px;
|
||
font-size: 14px;
|
||
}
|
||
|
||
.dsw-milestone-count {
|
||
opacity: 0.7;
|
||
}
|
||
|
||
.dsw-milestone-percent {
|
||
font-weight: 700;
|
||
}
|
||
|
||
.dsw-milestone-items {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 8px;
|
||
margin-top: 12px;
|
||
}
|
||
|
||
.dsw-item {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
font-size: 13px;
|
||
padding: 6px 0;
|
||
}
|
||
|
||
.dsw-item-icon {
|
||
font-size: 14px;
|
||
}
|
||
|
||
.dsw-item-completed {
|
||
opacity: 0.7;
|
||
}
|
||
|
||
.dsw-item-in-progress {
|
||
color: #f59e0b;
|
||
}
|
||
|
||
.dsw-item-planned {
|
||
opacity: 0.5;
|
||
}
|
||
|
||
.dsw-milestones-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||
gap: 16px;
|
||
}
|
||
|
||
.dsw-timeline-container {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 0;
|
||
}
|
||
|
||
.dsw-timeline-item {
|
||
display: flex;
|
||
gap: 20px;
|
||
}
|
||
|
||
.dsw-timeline-marker {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
padding-top: 8px;
|
||
}
|
||
|
||
.dsw-timeline-dot {
|
||
width: 16px;
|
||
height: 16px;
|
||
border-radius: 50%;
|
||
background: #ccc;
|
||
z-index: 1;
|
||
}
|
||
|
||
.dsw-timeline-completed .dsw-timeline-dot {
|
||
background: #10b981;
|
||
}
|
||
|
||
.dsw-timeline-in-progress .dsw-timeline-dot {
|
||
background: #f59e0b;
|
||
animation: pulse 2s infinite;
|
||
}
|
||
|
||
.dsw-timeline-line {
|
||
width: 2px;
|
||
flex: 1;
|
||
background: #ccc;
|
||
margin-top: 4px;
|
||
}
|
||
|
||
.dsw-timeline-completed .dsw-timeline-line {
|
||
background: #10b981;
|
||
}
|
||
|
||
.dsw-timeline-content {
|
||
flex: 1;
|
||
padding-bottom: 24px;
|
||
}
|
||
|
||
.dsw-timeline-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 8px;
|
||
}
|
||
|
||
.dsw-timeline-phase {
|
||
font-size: 12px;
|
||
font-weight: 700;
|
||
color: #06b6d4;
|
||
text-transform: uppercase;
|
||
}
|
||
|
||
.dsw-timeline-period {
|
||
font-size: 12px;
|
||
opacity: 0.7;
|
||
}
|
||
|
||
.dsw-timeline-name {
|
||
font-size: 16px;
|
||
font-weight: 600;
|
||
margin-bottom: 8px;
|
||
}
|
||
|
||
.dsw-stats-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
|
||
gap: 16px;
|
||
}
|
||
|
||
.dsw-stat {
|
||
text-align: center;
|
||
padding: 20px;
|
||
background: rgba(16, 185, 129, 0.1);
|
||
border-radius: 8px;
|
||
}
|
||
|
||
.dsw-stat-value {
|
||
font-size: 32px;
|
||
font-weight: 800;
|
||
color: #10b981;
|
||
margin-bottom: 4px;
|
||
}
|
||
|
||
.dsw-stat-label {
|
||
font-size: 12px;
|
||
opacity: 0.7;
|
||
}
|
||
|
||
@keyframes pulse {
|
||
0%, 100% { box-shadow: 0 0 0 0 rgba(249, 115, 22, 0.7); }
|
||
50% { box-shadow: 0 0 0 10px rgba(249, 115, 22, 0); }
|
||
}
|
||
`)
|
||
]);
|
||
|
||
return E('div', { 'class': 'secubox-dev-status-page' }, [
|
||
E('link', { 'rel': 'stylesheet', 'href': L.resource('secubox-theme/core/variables.css') }),
|
||
E('link', { 'rel': 'stylesheet', 'href': L.resource('secubox/common.css') }),
|
||
SecuNav.renderTabs('dev-status'),
|
||
this.renderHeader(),
|
||
main
|
||
]);
|
||
},
|
||
|
||
renderHeader: function() {
|
||
var widget = this.getWidget();
|
||
var currentPhase = widget.getCurrentPhase() || {};
|
||
var status = this.statusData || {};
|
||
|
||
return E('div', { 'class': 'sh-page-header sh-page-header-lite' }, [
|
||
E('div', {}, [
|
||
E('h2', { 'class': 'sh-page-title' }, [
|
||
E('span', { 'class': 'sh-page-title-icon' }, '🚀'),
|
||
_('Development Status')
|
||
]),
|
||
E('p', { 'class': 'sh-page-subtitle' },
|
||
_('SecuBox roadmap, milestones, and release planning.'))
|
||
]),
|
||
E('div', { 'class': 'sh-header-meta' }, [
|
||
this.renderHeaderChip('🏷️', _('Version'), status.version || _('Unknown')),
|
||
this.renderHeaderChip('📈', _('Overall'), this.getOverallProgress() + '%'),
|
||
this.renderHeaderChip('📅', _('Current phase'),
|
||
currentPhase.phase ? currentPhase.phase + ' · ' + currentPhase.name : _('Not set'))
|
||
])
|
||
]);
|
||
},
|
||
|
||
renderHeaderChip: function(icon, label, value) {
|
||
return E('div', { 'class': 'sh-header-chip' }, [
|
||
E('span', { 'class': 'sh-chip-icon' }, icon),
|
||
E('div', { 'class': 'sh-chip-text' }, [
|
||
E('span', { 'class': 'sh-chip-label' }, label),
|
||
E('strong', {}, value.toString())
|
||
])
|
||
]);
|
||
},
|
||
|
||
handleSaveApply: null,
|
||
handleSave: null,
|
||
handleReset: null
|
||
});
|