ui/phase-21: sciences CRUD list, designer, and production-picker integration

Lights up the player-defined sciences feature: a table view with sort
and filter, a designer with four percent inputs and a strict
sum-equals-100 gate, and a Research-sub-row integration so the
planet production picker lists the user's sciences alongside the
four tech buttons. Phase 21 decisions are baked back into ui/PLAN.md
(no UpdateScience on the wire — write-once via createScience +
removeScience; percentages instead of fractions; sciences live under
the existing Research segment).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Ilia Denisov
2026-05-10 21:32:37 +02:00
parent 0509f2cde2
commit 7bea22b0b5
31 changed files with 2751 additions and 71 deletions
+9 -5
View File
@@ -1,15 +1,17 @@
<!--
Active-view router for the per-entity tables. Phase 17 lights up
the ship-classes table; the other slugs (planets, ship-groups,
fleets, sciences, races) keep the Phase 10 stub copy until their
respective phases land. The wrapper preserves
`data-testid="active-view-table"` and `data-entity={entity}` for
both branches so the navigation e2e specs (`game-shell.spec.ts`,
the ship-classes table; Phase 21 lights up the sciences table; the
remaining slugs (planets, ship-groups, fleets, races) keep the
Phase 10 stub copy until their respective phases land. The wrapper
preserves `data-testid="active-view-table"` and
`data-entity={entity}` for every branch (each leaf component
mirrors them) so the navigation e2e specs (`game-shell.spec.ts`,
`view-menu`) keep matching.
-->
<script lang="ts">
import { i18n, type TranslationKey } from "$lib/i18n/index.svelte";
import TableShipClasses from "./table-ship-classes.svelte";
import TableSciences from "./table-sciences.svelte";
type Props = { entity: string };
let { entity }: Props = $props();
@@ -22,6 +24,8 @@ both branches so the navigation e2e specs (`game-shell.spec.ts`,
{#if entity === "ship-classes"}
<TableShipClasses />
{:else if entity === "sciences"}
<TableSciences />
{:else}
<section
class="active-view"