Donut-hover met percentages en werkende update-notificatie

- Donut: naadloze segmenten (donkere spleten weg), hover-tooltip per
  categorie met naam, bedrag en aandeel, middenwaarde schaalt mee
- Update-check via de eigen backend (/api/update-check) i.p.v. direct
  naar de Gitea API, die geen CORS-headers stuurt
- Semver-vergelijking, herhaalde check en release-notes in de melding
- release.ps1 maakt de Gitea-release aan (stap 4)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jordy Hoogeveen
2026-09-04 09:19:23 +02:00
co-authored by Claude Opus 5
parent e45d5b73fb
commit 3c67381de9
24 changed files with 506 additions and 241 deletions
+6 -16
View File
@@ -1,6 +1,7 @@
import { useState, useEffect } from "react";
import { useState } from "react";
import { TABS } from "../constants/index.js";
import { version, updateCheckUrl } from "../../package.json";
import { version } from "../../package.json";
import { useUpdateCheck } from "../hooks/useUpdateCheck.js";
import { useApp } from "../context/AppContext.jsx";
const TAB_ICONS = [
@@ -123,7 +124,7 @@ function OverPopup({ onClose, nieuweVersie, T }) {
}}>
{/* Modal */}
<div onClick={(e) => e.stopPropagation()} style={{
background: T.card,
background: T.popupBg,
border: `1px solid ${T.border}`,
borderRadius: 16, padding: "28px 28px 24px", width: 300,
boxShadow: "0 24px 60px rgba(0,0,0,0.5)",
@@ -166,7 +167,7 @@ function OverPopup({ onClose, nieuweVersie, T }) {
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 10 }}>
<span style={{ fontSize: 13, color: T.muted }}>Nieuw beschikbaar</span>
<span style={{ fontSize: 12, fontWeight: 700, color: "#f97316", background: "rgba(249,115,22,0.12)", borderRadius: 6, padding: "3px 10px" }}>
v{nieuweVersie}
v{nieuweVersie.latest}
</span>
</div>
)}
@@ -213,20 +214,9 @@ function DarkModeToggle({ darkMode, setDarkMode, T }) {
export default function NavBar() {
const { tab, setTab, setShowUsers, setShowInstellingen, T, logout, darkMode, setDarkMode, currentUser, avatar } = useApp();
const [nieuweVersie, setNieuweVersie] = useState(null);
const nieuweVersie = useUpdateCheck();
const [overOpen, setOverOpen] = useState(false);
useEffect(() => {
if (!updateCheckUrl) return;
fetch(updateCheckUrl)
.then(r => r.json())
.then(data => {
const latest = data?.tag_name?.replace(/^v/, "");
if (latest && latest !== version) setNieuweVersie(latest);
})
.catch(() => {});
}, []);
return (
<div style={{
width: 260, minWidth: 260, flexShrink: 0,