diff --git a/src/main/java/com/example/mindflux/controller/DashboardController.java b/src/main/java/com/example/mindflux/controller/DashboardController.java index 6c19b58..9177c0f 100644 --- a/src/main/java/com/example/mindflux/controller/DashboardController.java +++ b/src/main/java/com/example/mindflux/controller/DashboardController.java @@ -6,6 +6,8 @@ import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; import java.security.Principal; +import java.util.List; +import com.example.mindflux.dto.WeeklyActivity; @Controller public class DashboardController { @@ -20,6 +22,14 @@ public class DashboardController { public String showDashboard(Model model, Principal principal) { // Echte XP/Level/Streak-Werte für die Sidebar-Widgets (statt Dummy-Daten im Template). model.addAttribute("overall", progressService.getOverallProgress(principal.getName())); + WeeklyActivity weekly = progressService.getCurrentWeekActivity(principal.getName()); + model.addAttribute("weekly", weekly != null ? weekly : emptyWeekly()); + var topGames = progressService.getTopGames(principal.getName(), 4); + model.addAttribute("topGames", topGames != null ? topGames : List.of()); return "dashboard"; } + + private WeeklyActivity emptyWeekly() { + return new WeeklyActivity(List.of(0, 0, 0, 0, 0, 0, 0), List.of(0, 0, 0, 0, 0, 0, 0), 0, 0); + } } diff --git a/src/main/java/com/example/mindflux/controller/ProfileController.java b/src/main/java/com/example/mindflux/controller/ProfileController.java index be13001..a07d76d 100644 --- a/src/main/java/com/example/mindflux/controller/ProfileController.java +++ b/src/main/java/com/example/mindflux/controller/ProfileController.java @@ -2,6 +2,7 @@ package com.example.mindflux.controller; import com.example.mindflux.model.User; import com.example.mindflux.service.UserService; +import com.example.mindflux.service.ProgressService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -27,6 +28,8 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.security.Principal; import java.util.UUID; +import java.util.List; +import com.example.mindflux.dto.WeeklyActivity; @Controller public class ProfileController { @@ -39,6 +42,9 @@ public class ProfileController { @Autowired private BCryptPasswordEncoder passwordEncoder; + @Autowired + private ProgressService progressService; + private static final String UPLOAD_DIR = System.getProperty("user.dir") + "/uploads/profiles/"; @GetMapping("/profile") @@ -97,6 +103,12 @@ public class ProfileController { User user = userService.findByUsername(username); model.addAttribute("user", user); + model.addAttribute("overall", progressService.getOverallProgress(username)); + WeeklyActivity weekly = progressService.getCurrentWeekActivity(username); + model.addAttribute("weekly", weekly != null ? weekly : new WeeklyActivity( + List.of(0, 0, 0, 0, 0, 0, 0), List.of(0, 0, 0, 0, 0, 0, 0), 0, 0)); + var topGames = progressService.getTopGames(username, 3); + model.addAttribute("topGames", topGames != null ? topGames : List.of()); return "knowledge"; } diff --git a/src/main/java/com/example/mindflux/controller/ProgressController.java b/src/main/java/com/example/mindflux/controller/ProgressController.java index 76b5db1..6ddba7f 100644 --- a/src/main/java/com/example/mindflux/controller/ProgressController.java +++ b/src/main/java/com/example/mindflux/controller/ProgressController.java @@ -11,6 +11,7 @@ import org.springframework.web.bind.annotation.GetMapping; import java.security.Principal; import java.util.List; +import com.example.mindflux.dto.WeeklyActivity; @Controller public class ProgressController { @@ -41,6 +42,9 @@ public class ProgressController { } model.addAttribute("gameProgress", games != null ? games : List.of()); // leere Liste, falls null + WeeklyActivity weekly = progressService.getCurrentWeekActivity(username); + model.addAttribute("weekly", weekly != null ? weekly : new WeeklyActivity( + List.of(0, 0, 0, 0, 0, 0, 0), List.of(0, 0, 0, 0, 0, 0, 0), 0, 0)); return "progress"; // ruft progress.html auf } diff --git a/src/main/java/com/example/mindflux/dto/WeeklyActivity.java b/src/main/java/com/example/mindflux/dto/WeeklyActivity.java new file mode 100644 index 0000000..ddecb71 --- /dev/null +++ b/src/main/java/com/example/mindflux/dto/WeeklyActivity.java @@ -0,0 +1,7 @@ +package com.example.mindflux.dto; + +import java.util.List; + +/** Tatsächlich aufgezeichnete Aktivität von Montag bis Sonntag. */ +public record WeeklyActivity(List minutes, List xp, int totalMinutes, int totalXp) { +} diff --git a/src/main/java/com/example/mindflux/repository/GameScoreRepository.java b/src/main/java/com/example/mindflux/repository/GameScoreRepository.java index 6d89984..6a60057 100644 --- a/src/main/java/com/example/mindflux/repository/GameScoreRepository.java +++ b/src/main/java/com/example/mindflux/repository/GameScoreRepository.java @@ -7,6 +7,7 @@ import org.springframework.data.repository.query.Param; import java.util.List; import java.util.Optional; +import java.time.LocalDateTime; public interface GameScoreRepository extends JpaRepository { @@ -19,6 +20,10 @@ public interface GameScoreRepository extends JpaRepository { /** Wie oft ein Spieler ein Spiel abgeschlossen hat. */ long countByUsernameAndGame(String username, String game); + /** Abgeschlossene Runden eines Zeitraums – Basis für Spielzeit und Wochenverlauf. */ + List findByUsernameAndPlayedAtGreaterThanEqualAndPlayedAtLessThanOrderByPlayedAtAsc( + String username, LocalDateTime from, LocalDateTime until); + /** Alle Spielstände eines Spielers löschen (z. B. beim Löschen des Kontos). */ void deleteByUsername(String username); diff --git a/src/main/java/com/example/mindflux/service/GameScoreService.java b/src/main/java/com/example/mindflux/service/GameScoreService.java index c786801..d0ddced 100644 --- a/src/main/java/com/example/mindflux/service/GameScoreService.java +++ b/src/main/java/com/example/mindflux/service/GameScoreService.java @@ -10,6 +10,10 @@ import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; import java.time.LocalDateTime; +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.List; +import com.example.mindflux.dto.WeeklyActivity; /** * Speichert Spielergebnisse und liefert Statistiken. Der Score wird hier @@ -110,4 +114,39 @@ public class GameScoreService { .orElse(null); return new GameStats(highscore, plays, lastPlayed); } + + /** + * Aggregiert ausschließlich gespeicherte Abschlusszeiten. Runden ohne gemessene + * Dauer zählen für XP, aber bewusst nicht als Spielzeit. + */ + public WeeklyActivity weeklyActivity(String username, LocalDate monday) { + LocalDateTime from = monday.atStartOfDay(); + LocalDateTime until = monday.plusDays(7).atStartOfDay(); + int[] seconds = new int[7]; + int[] xp = new int[7]; + + for (GameScore score : repository + .findByUsernameAndPlayedAtGreaterThanEqualAndPlayedAtLessThanOrderByPlayedAtAsc(username, from, until)) { + int day = (int) (score.getPlayedAt().toLocalDate().toEpochDay() - monday.toEpochDay()); + if (day < 0 || day >= 7) { + continue; + } + xp[day] += Math.max(score.getScore(), 0); + if (score.getDurationSeconds() != null) { + seconds[day] += Math.max(score.getDurationSeconds(), 0); + } + } + + List minutes = new ArrayList<>(7); + List weeklyXp = new ArrayList<>(7); + int totalSeconds = 0; + int totalXp = 0; + for (int day = 0; day < 7; day++) { + minutes.add((int) Math.ceil(seconds[day] / 60.0)); + weeklyXp.add(xp[day]); + totalSeconds += seconds[day]; + totalXp += xp[day]; + } + return new WeeklyActivity(minutes, weeklyXp, (int) Math.ceil(totalSeconds / 60.0), totalXp); + } } diff --git a/src/main/java/com/example/mindflux/service/ProgressService.java b/src/main/java/com/example/mindflux/service/ProgressService.java index 8546c46..8fc643e 100644 --- a/src/main/java/com/example/mindflux/service/ProgressService.java +++ b/src/main/java/com/example/mindflux/service/ProgressService.java @@ -3,6 +3,7 @@ package com.example.mindflux.service; import com.example.mindflux.dto.GameProgress; import com.example.mindflux.dto.GameStats; import com.example.mindflux.dto.ProgressData; +import com.example.mindflux.dto.WeeklyActivity; import org.springframework.stereotype.Service; @@ -11,6 +12,9 @@ import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Comparator; +import java.time.temporal.TemporalAdjusters; +import java.time.DayOfWeek; @Service public class ProgressService { @@ -91,6 +95,25 @@ public class ProgressService { return result; } + /** Aktuelle Wochenwerte, jeweils Montag bis Sonntag. */ + public WeeklyActivity getCurrentWeekActivity(String username) { + LocalDate monday = LocalDate.now().with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY)); + return gameScoreService.weeklyActivity(username, monday); + } + + /** Die tatsächlich gespielten Spiele, nach ihrem Highscore sortiert. */ + public List getTopGames(String username, int limit) { + List games = getGameProgress(username).stream() + .filter(game -> game.getPlays() > 0) + .sorted(Comparator.comparingInt(GameProgress::getHighscore).reversed() + .thenComparing(Comparator.comparingInt(GameProgress::getPlays).reversed())) + .limit(limit) + .toList(); + int topScore = games.stream().mapToInt(GameProgress::getHighscore).max().orElse(1); + games.forEach(game -> game.setProgressPercent(Math.round(game.getHighscore() * 100f / topScore))); + return games; + } + private GameProgress toGameProgress(String displayName, GameStats stats) { GameProgress game = new GameProgress(); game.setName(displayName); diff --git a/src/main/resources/static/css/accessibility-placement.css b/src/main/resources/static/css/accessibility-placement.css index e24f6a6..945e432 100644 --- a/src/main/resources/static/css/accessibility-placement.css +++ b/src/main/resources/static/css/accessibility-placement.css @@ -14,4 +14,5 @@ body[class*="mf-a11y-skin-"] .mf-a11y-close{background:var(--mf-a11y-accent);col body[class*="mf-a11y-skin-"] .mf-a11y-option[aria-pressed="true"]{border-color:var(--mf-a11y-accent);background:color-mix(in srgb,var(--mf-a11y-accent) 18%,var(--surface));box-shadow:inset 0 0 0 2px var(--mf-a11y-accent)} .bh-cabinet-level{grid-column:1/-1;margin:.5rem 0 -.15rem;padding-bottom:.35rem;border-bottom:1px solid #60473d;color:var(--bh-accent);font:700 .66rem "Courier New",monospace;letter-spacing:.12em} .bh-drawer.solved{overflow:hidden}.bh-solved-stamp{display:inline-block;border:3px solid #79bf91;border-radius:4px;padding:.24rem .48rem;color:#79bf91;font:700 .78rem/1 "Courier New",monospace;letter-spacing:.12em;transform:rotate(-12deg);text-shadow:none;box-shadow:0 0 12px color-mix(in srgb,#79bf91 26%,transparent);pointer-events:none}.bh-drawer .bh-solved-stamp{position:absolute;right:3.1rem;top:.75rem}.bh-solved-stamp-detail{position:absolute;right:1.4rem;top:1.25rem;font-size:1rem}.bh-solved-stamp-result{margin:0 0 .8rem;font-size:1.15rem} +.nw-app{background:repeating-linear-gradient(0deg,rgba(103,187,255,.12) 0 1px,transparent 1px 30px),repeating-linear-gradient(90deg,rgba(103,187,255,.12) 0 1px,transparent 1px 30px),radial-gradient(circle at 50% -20%,rgba(27,89,148,.38),transparent 48%),#071527!important}.nw-board{background:repeating-linear-gradient(0deg,rgba(103,187,255,.12) 0 1px,transparent 1px 30px),repeating-linear-gradient(90deg,rgba(103,187,255,.12) 0 1px,transparent 1px 30px),rgba(5,18,33,.9)!important} @media(max-width:700px){.mf-a11y-panel{inset:70px 6px auto 6px!important;width:auto;max-height:calc(100dvh - 82px)}} diff --git a/src/main/resources/static/css/theme.css b/src/main/resources/static/css/theme.css index dee0936..9c98602 100644 --- a/src/main/resources/static/css/theme.css +++ b/src/main/resources/static/css/theme.css @@ -126,7 +126,7 @@ a { color:var(--secondary); } .mf-card-codeescape { --accent:var(--esc-accent); --glow-s:var(--esc-glow); --glow-w:color-mix(in srgb,var(--esc-accent) 16%,transparent); background:repeating-linear-gradient(0deg,rgba(255,255,255,.015) 0 1px,transparent 1px 4px),var(--surface); } .mf-card-codeescape::after { content:"ACCESS"; position:absolute; right:12px; top:9px; color:var(--esc-accent); font:700 .52rem/1 "Courier New",monospace; letter-spacing:.12em; opacity:.6; } -.mf-card-bughunter { --accent:var(--esc-accent); --glow-s:var(--esc-glow); --glow-w:color-mix(in srgb,var(--esc-accent) 16%,transparent); background:linear-gradient(135deg,color-mix(in srgb,var(--esc-accent) 8%,transparent),var(--surface)); } +.mf-card-bughunter { --accent:var(--esc-accent); --glow-s:var(--esc-glow); --glow-w:color-mix(in srgb,var(--esc-accent) 16%,transparent); background:var(--surface); } .mf-card-bughunter::after { content:"FALLAKTE"; position:absolute; right:12px; top:9px; color:var(--esc-accent); font:700 .52rem/1 "Courier New",monospace; letter-spacing:.12em; opacity:.7; } /* ===== Panels / Widgets ===== */ diff --git a/src/main/resources/static/js/theme.js b/src/main/resources/static/js/theme.js index 54db984..f0ebf66 100644 --- a/src/main/resources/static/js/theme.js +++ b/src/main/resources/static/js/theme.js @@ -32,7 +32,7 @@ document.head.appendChild(accessibilityCss); var accessibilityPlacementCss = document.createElement('link'); accessibilityPlacementCss.rel = 'stylesheet'; - accessibilityPlacementCss.href = '/css/accessibility-placement.css?v=6'; + accessibilityPlacementCss.href = '/css/accessibility-placement.css?v=7'; accessibilityPlacementCss.setAttribute('data-accessibility', ''); document.head.appendChild(accessibilityPlacementCss); } diff --git a/src/main/resources/templates/dashboard.html b/src/main/resources/templates/dashboard.html index 53d5641..cebf941 100644 --- a/src/main/resources/templates/dashboard.html +++ b/src/main/resources/templates/dashboard.html @@ -81,22 +81,23 @@
-

Dein Fortschritt

Woche ▾
-
Gesamtfortschritt
-
65%+120 XP
-
- - - - - - +

Dein Fortschritt

Diese Woche
+
Zum nächsten Level
+
0%+0 XP
+
+
MoDiMiDoFrSaSo
-

Top Kategorien

+

Top Spiele

+

Noch keine abgeschlossene Runde. Starte ein Spiel, dann erscheinen hier echte Highscores.

+
+ MindMatch + + 0 XP +
@@ -148,9 +149,12 @@ SPIEL STARTEN `).join(''); - const SVG={globe:'',chart:'',lock:'',grid:'',code:''}; - const CATS=[['Netzwerk','globe',85],['Datenbanken','chart',78],['IT-Sicherheit','lock',72],['Hardware','grid',66],['Programmierung','code',60]]; - document.getElementById('cats').innerHTML += CATS.map(([n,i,p])=>`
${SVG[i]}${n}${p}%
`).join(''); + const weekChart = document.getElementById('weeklyXpChart'); + const values = JSON.parse(weekChart.dataset.values || '[0,0,0,0,0,0,0]'); + const max = Math.max(...values, 1); + const points = values.map((value, index) => `${Math.round(index * 40)} ${64 - Math.round(value / max * 54)}`); + const line = `M${points.join(' L')}`; + weekChart.querySelector('svg').innerHTML = ``; diff --git a/src/main/resources/templates/knowledge.html b/src/main/resources/templates/knowledge.html index b218d4e..1eb57a6 100644 --- a/src/main/resources/templates/knowledge.html +++ b/src/main/resources/templates/knowledge.html @@ -3,42 +3,41 @@ - Mindflux – Knowledge Center + Knowledge Core – Mindflux -
- -
- - +
+ Knowledge Core +

Deine Lernzentrale

+

Wähle einen Prüfungsweg oder vertiefe ein Spielmodul. Der Status hier basiert auf deinen gespeicherten XP und abgeschlossenen Runden.

-
-

Welcome to the Knowledge Center!

-

Hier entsteht dein cooles Modul 📚🧠

-
+
+
Aktuelles Level1
+
XP diese Woche0
+
Gemessene Spielzeit0 Min.
+
-
- - - +
+ +
SYSTEMINTEGRATION

Netzwerke verstehen

Topologien und Adressierung erst festigen, dann die Konfiguration in NetWorker trainieren.

+ +
+
+

Deine stärksten Spiele

+

Noch keine abgeschlossene Runde – dein erster Highscore erscheint hier.

+
    +
  • MindMatch0 XP · 0 Runden
  • +
+
+ + diff --git a/src/main/resources/templates/networker.html b/src/main/resources/templates/networker.html index 59802c4..37e268a 100644 --- a/src/main/resources/templates/networker.html +++ b/src/main/resources/templates/networker.html @@ -1 +1 @@ -NetWorker · Mindflux
← Dashboard
NETWORKERNETZPLAN · DIAGNOSE
NETZWERKPLÄNE WERDEN KALIBRIERT …
+NetWorker · Mindflux
← Dashboard
NetWorkerNETZPLAN · DIAGNOSE
NETZWERKPLÄNE WERDEN KALIBRIERT …
diff --git a/src/main/resources/templates/progress.html b/src/main/resources/templates/progress.html index e153d69..262fea0 100644 --- a/src/main/resources/templates/progress.html +++ b/src/main/resources/templates/progress.html @@ -49,28 +49,27 @@

78%

-

Korrekte Antworten: 432

-

Level: 5

+

Erfasste Spielaktionen: 432

+

Level (XP): 5

2400 / 3000 XP

- +
-

Lernzeit – Diese Woche

+

Spielzeit – Diese Woche

+

Nur die Dauer abgeschlossener, zeitgemessener Runden.

- +
-

Spiel-Fortschritte

+

Spiel-Statistiken

Hangman

-

Fortschritt: 65%

-

Beste Punktzahl: 1200

Gespielt: 15×

Letzter Zugriff: 14.04.2025

@@ -81,7 +80,7 @@
-