From aeca35ee2b77afce17bf440ca6f1a3d84cde4d87 Mon Sep 17 00:00:00 2001 From: Till JS Date: Wed, 25 Mar 2026 09:02:42 +0100 Subject: [PATCH] feat(observatory): add rivers, leaderboard, compare, and trends tabs Four new tabs on the observatory page: - Flusse: Horizontal scroll of all 6 data flow rivers with animated SVG preview, from/to labels, speed and width stats - Rangliste: Sortable table of all 20 apps with score, trend, status dot, and mini bar charts for all 8 categories. Click any column header to sort, click row for detail panel - Vergleich: Select up to 4 apps via chip selector, see overlaid radar charts and side-by-side category bar comparison - Trends: Slope chart showing score evolution with hover highlight, trend annotations for big movers (+29 Storage, +16 Todo, +15 Calendar), average score summary card Co-Authored-By: Claude Opus 4.6 (1M context) --- .../observatory/ui/CompareView.svelte | 359 ++++++++++++++++++ .../observatory/ui/Leaderboard.svelte | 295 ++++++++++++++ .../observatory/ui/RiverCard.svelte | 158 ++++++++ .../observatory/ui/TrendsChart.svelte | 232 +++++++++++ .../src/routes/(app)/observatory/+page.svelte | 61 ++- 5 files changed, 1103 insertions(+), 2 deletions(-) create mode 100644 apps/manacore/apps/web/src/lib/components/observatory/ui/CompareView.svelte create mode 100644 apps/manacore/apps/web/src/lib/components/observatory/ui/Leaderboard.svelte create mode 100644 apps/manacore/apps/web/src/lib/components/observatory/ui/RiverCard.svelte create mode 100644 apps/manacore/apps/web/src/lib/components/observatory/ui/TrendsChart.svelte diff --git a/apps/manacore/apps/web/src/lib/components/observatory/ui/CompareView.svelte b/apps/manacore/apps/web/src/lib/components/observatory/ui/CompareView.svelte new file mode 100644 index 000000000..476e445e3 --- /dev/null +++ b/apps/manacore/apps/web/src/lib/components/observatory/ui/CompareView.svelte @@ -0,0 +1,359 @@ + + +
+ +
+

Apps auswahlen (max. 4):

+
+ {#each apps as app (app.id)} + + {/each} +
+
+ + {#if selectedApps.length >= 2} +
+ +
+ + {#each rings as ring} + + {/each} + {#each axes as axis} + + {/each} + + {#each selectedApps as app, i (app.id)} + + {/each} + + {#each labelPositions as lp} + + {lp.label} + + {/each} + + + +
+ {#each selectedApps as app, i (app.id)} + + + {app.displayName} ({app.score}) + + {/each} +
+
+ + +
+ {#each categoryKeys as cat} +
+ {cat} +
+ {#each selectedApps as app, i (app.id)} +
+
+
+
+ {app.categories[cat]} +
+ {/each} +
+
+ {/each} +
+
+ {:else} +
+

Wahle mindestens 2 Apps zum Vergleichen

+
+ {/if} +
+ + diff --git a/apps/manacore/apps/web/src/lib/components/observatory/ui/Leaderboard.svelte b/apps/manacore/apps/web/src/lib/components/observatory/ui/Leaderboard.svelte new file mode 100644 index 000000000..e879e2c3f --- /dev/null +++ b/apps/manacore/apps/web/src/lib/components/observatory/ui/Leaderboard.svelte @@ -0,0 +1,295 @@ + + +
+
+ + + + + + + + {#each categoryKeys as cat} + + {/each} + + + + {#each sorted() as app, i (app.id)} + onselect(app)}> + + + + + {#each categoryKeys as cat} + + {/each} + + {/each} + +
# toggleSort('name')}> + App{sortIndicator('name')} + toggleSort('score')}> + Score{sortIndicator('score')} + toggleSort('trend')}> + Trend{sortIndicator('trend')} + toggleSort(cat)}> + {shortLabels[cat]}{sortIndicator(cat)} +
{i + 1} + + {app.displayName} + + {app.score} + + {#if app.trend > 0} + +{app.trend} + {:else if app.trend < 0} + {app.trend} + {:else} + - + {/if} + +
+
+
+ {app.categories[cat]} +
+
+
+ + diff --git a/apps/manacore/apps/web/src/lib/components/observatory/ui/RiverCard.svelte b/apps/manacore/apps/web/src/lib/components/observatory/ui/RiverCard.svelte new file mode 100644 index 000000000..ddbf16770 --- /dev/null +++ b/apps/manacore/apps/web/src/lib/components/observatory/ui/RiverCard.svelte @@ -0,0 +1,158 @@ + + +
+ +
+ + + + + + + + + + + + + +
+ +
+
+ {fromLabel} + + {toLabel} +
+
+ + Geschwindigkeit + {speedLabel} + + + Breite + {river.width}px + +
+
+
+ + diff --git a/apps/manacore/apps/web/src/lib/components/observatory/ui/TrendsChart.svelte b/apps/manacore/apps/web/src/lib/components/observatory/ui/TrendsChart.svelte new file mode 100644 index 000000000..fbb08f53a --- /dev/null +++ b/apps/manacore/apps/web/src/lib/components/observatory/ui/TrendsChart.svelte @@ -0,0 +1,232 @@ + + + + + diff --git a/apps/manacore/apps/web/src/routes/(app)/observatory/+page.svelte b/apps/manacore/apps/web/src/routes/(app)/observatory/+page.svelte index a48deeb69..72737f1de 100644 --- a/apps/manacore/apps/web/src/routes/(app)/observatory/+page.svelte +++ b/apps/manacore/apps/web/src/routes/(app)/observatory/+page.svelte @@ -2,17 +2,22 @@ import SeenplatteScene from '$lib/components/observatory/SeenplatteScene.svelte'; import PlantCard from '$lib/components/observatory/ui/PlantCard.svelte'; import LakeCard from '$lib/components/observatory/ui/LakeCard.svelte'; + import RiverCard from '$lib/components/observatory/ui/RiverCard.svelte'; + import Leaderboard from '$lib/components/observatory/ui/Leaderboard.svelte'; + import CompareView from '$lib/components/observatory/ui/CompareView.svelte'; + import TrendsChart from '$lib/components/observatory/ui/TrendsChart.svelte'; import DetailPanel from '$lib/components/observatory/ui/DetailPanel.svelte'; import { createMockEcosystem } from '$lib/components/observatory/data/mockData'; - import { LAKES } from '$lib/components/observatory/data/layout'; + import { LAKES, RIVERS } from '$lib/components/observatory/data/layout'; import type { AppData } from '$lib/components/observatory/data/types'; - type Tab = 'scene' | 'plants' | 'lakes'; + type Tab = 'scene' | 'plants' | 'lakes' | 'rivers' | 'leaderboard' | 'compare' | 'trends'; let activeTab = $state('scene'); let selectedApp = $state(null); const apps = createMockEcosystem(); const lakes = LAKES; + const rivers = RIVERS; // Sort apps by score descending for gallery const sortedApps = apps.toSorted((a, b) => b.score - a.score); @@ -27,6 +32,10 @@ { id: 'scene', label: 'Seenplatte' }, { id: 'plants', label: 'Pflanzen', count: apps.length }, { id: 'lakes', label: 'Seen', count: lakes.length }, + { id: 'rivers', label: 'Flusse', count: rivers.length }, + { id: 'leaderboard', label: 'Rangliste' }, + { id: 'compare', label: 'Vergleich' }, + { id: 'trends', label: 'Trends' }, ]; @@ -133,6 +142,54 @@ {/each} + {:else if activeTab === 'rivers'} + + {:else if activeTab === 'leaderboard'} + + {:else if activeTab === 'compare'} + + {:else if activeTab === 'trends'} + {/if}