feat: solar KPIs, version display, feature flags (v1.4.0)

New dashboard KPI cards:
- Performance Ratio (PR) with color thresholds
- Equivalent Utilization Hours
- Daily Revenue (¥)
- Self-Consumption Rate

Version display for field engineers:
- Login page footer: "v1.4.0 | Core: v1.4.0"
- Sidebar footer: version when expanded
- System Settings: full version breakdown

Backend (core sync):
- GET /api/v1/version (no auth) — reads VERSIONS.json
- GET /api/v1/kpi/solar — PR, revenue, equiv hours calculations
- Dashboard energy_today fallback from raw energy_data
- PV device filter includes sungrow_inverter type

Feature flags:
- Sidebar hides disabled features (charging, bigscreen_3d)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Du Wenbo
2026-04-06 22:37:09 +08:00
parent 1274e77cb4
commit 93af4bc16b
11 changed files with 275 additions and 13 deletions

View File

@@ -12,7 +12,7 @@ import {
import { Outlet, useNavigate, useLocation } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import { getUser, removeToken } from '../utils/auth';
import { getAlarmStats, getAlarmEvents, getBranding } from '../services/api';
import { getAlarmStats, getAlarmEvents, getBranding, getVersion } from '../services/api';
import { useTheme } from '../contexts/ThemeContext';
const { Header, Sider, Content } = Layout;
@@ -29,6 +29,7 @@ export default function MainLayout() {
const [alarmCount, setAlarmCount] = useState(0);
const [recentAlarms, setRecentAlarms] = useState<any[]>([]);
const [features, setFeatures] = useState<Record<string, boolean>>({});
const [versionInfo, setVersionInfo] = useState<any>(null);
const navigate = useNavigate();
const location = useLocation();
const user = getUser();
@@ -39,6 +40,7 @@ export default function MainLayout() {
getBranding().then((res: any) => {
setFeatures(res?.features || {});
}).catch(() => {});
getVersion().then(setVersionInfo).catch(() => {});
}, []);
// Map feature flags to menu keys that should be hidden when the feature is disabled
@@ -172,6 +174,14 @@ export default function MainLayout() {
}
}}
/>
{!collapsed && versionInfo && (
<div style={{
padding: '8px 16px', fontSize: 11, color: 'rgba(255,255,255,0.3)',
borderTop: '1px solid rgba(255,255,255,0.06)', textAlign: 'center',
}}>
v{versionInfo.project_version}
</div>
)}
</Sider>
<Layout>
<Header style={{