From 46e339fdc43786a298badfcd53980f5be2770582 Mon Sep 17 00:00:00 2001 From: Harlekin Date: Mon, 21 Sep 2026 19:16:07 +0200 Subject: [PATCH] Fremde Gruppen-CCs in der Kette bleiben voll bedienbar Der Eintrag am Fuss einer Kette - der Gruppen-CC, aus dem sich jemand herausredesignt hat - liess sich weder ziehen noch bearbeiten. Das war meine Entscheidung mit der Begruendung, es sei der Platz eines anderen. Beim Nacherfassen ist das zu eng: Dort steht erst am Ende fest, welches Redesign wohin fuehrte, und bis dahin muss sich auch einordnen lassen, was einem nicht gehoert. Er ist jetzt ein Kettenglied wie jedes andere - ziehen, bearbeiten, Bezahlart, Verweise, Herausloesen - und traegt nur zusaetzlich das Kennzeichen "war dabei" mit Zeitraum und Besitzer. Wer ihn an den Anfang seiner Kette zieht, macht aus seiner Beitrittsbuchung den Kauf und aus dem Herausredesignen ein Redesign; sein Platz bleibt dabei einer. Dass nur Beteiligte etwas einreihen duerfen, gilt unveraendert. --- README.md | 8 ++- server/test/ketten.test.ts | 27 +++++++- web/src/components/ChainView.tsx | 104 ++++++++++++++++--------------- web/src/styles.css | 7 ++- 4 files changed, 89 insertions(+), 57 deletions(-) diff --git a/README.md b/README.md index d4edb58..8bb7920 100644 --- a/README.md +++ b/README.md @@ -95,9 +95,11 @@ damit seinen eigenen Beitritt um - die Buchung des Erstellers bleibt unberührt, sein eigener Platz in seiner eigenen Kette. Einreihen darf, wer an beidem beteiligt war. Am Fuß einer Kette kann ein CC stehen, der jemand anderem gehört - der Gruppen-CC, aus dem sich -der Spieler herausredesignt hat. Er war darin, und das bleibt sichtbar; verschieben oder -entscheiden lässt sich daran nichts, denn es ist der Platz eines anderen. Frühere -Mitgliedschaften, die zu keinem seiner Plätze führen, stehen darunter unter *Früher genutzt*. +der Spieler herausredesignt hat. Er trägt das Kennzeichen *war dabei* samt Zeitraum und +Besitzer, ist aber **genauso bedienbar wie jeder andere Eintrag**: ziehen, bearbeiten, +Bezahlart, Verweise. Beim Nacherfassen steht erst am Ende fest, welches Redesign wohin führte - +da muss sich auch einordnen lassen, was einem nicht gehört. Frühere Mitgliedschaften, die zu +keinem seiner Plätze führen, stehen darunter unter *Früher genutzt*. ### Verweise an einem CC diff --git a/server/test/ketten.test.ts b/server/test/ketten.test.ts index 3540a19..7ab9e7b 100644 --- a/server/test/ketten.test.ts +++ b/server/test/ketten.test.ts @@ -758,7 +758,7 @@ describe('Eine Kette mit Vorgeschichte ausserhalb', () => { ctx, ); - return { dargon, eigenes: eigenes.character, weiter: weiter.character }; + return { dargon, fremd: fremd.character, eigenes: eigenes.character, weiter: weiter.character }; } it('zählt drei Plätze und drei verbrauchte Pakete', async () => { @@ -796,6 +796,31 @@ describe('Eine Kette mit Vorgeschichte ausserhalb', () => { assert.equal(buchung?.kind, 'REDESIGN_PAID', 'es bleibt ein Redesign'); }); + it('lässt auch den fremden Gruppen-CC selbst einreihen', async () => { + // Beim Nacherfassen muss sich einordnen lassen, was einem nicht gehört: + // Erst am Ende steht fest, welches Redesign wohin führte. + const { dargon, fremd, eigenes } = await ausgestiegen(); + + await characters.moveInChain( + fremd.id, + { lineageId: eigenes.lineageId, afterId: null, forPlayerId: dargon }, + ctx, + ); + + const glieder = await kette(dargon, eigenes.lineageId); + assert.deepEqual( + glieder.map((g) => g.name), + ['Fremde Gruppe', 'Eigener Strang', 'Und weiter'], + 'der Gruppen-CC steht jetzt am Anfang der Kette', + ); + verkettungStimmt(glieder); + + // Sein Platz bleibt einer: die Beitrittsbuchung wird zum Kauf, das + // Herausredesignen zum Redesign - nicht beides zum Kauf. + const stand = await credits.getBalances(dargon); + assert.equal(stand.CC, 0, 'drei Pakete, drei Plätze'); + }); + it('behält den Verweis auf die Herkunft', async () => { const { dargon, eigenes, weiter } = await ausgestiegen(); diff --git a/web/src/components/ChainView.tsx b/web/src/components/ChainView.tsx index 3adf66d..a8c1365 100644 --- a/web/src/components/ChainView.tsx +++ b/web/src/components/ChainView.tsx @@ -280,7 +280,24 @@ export default function ChainView({ - + setZiehe(frueher)} + onDragEnd={() => { + setZiehe(null); + setUeber(null); + }} + onAction={onAction} + onDetach={() => void herausloesen(frueher)} + onChanged={onMoved} + onError={onError} + alleinInKette={false} + herkunft + /> ))} @@ -297,11 +314,24 @@ export default function ChainView({

{vergangen.map((character) => ( - setZiehe(character)} + onDragEnd={() => { + setZiehe(null); + setUeber(null); + }} onAction={onAction} + onDetach={() => void herausloesen(character)} + onChanged={onMoved} + onError={onError} + alleinInKette={false} + herkunft /> ))}
@@ -311,54 +341,6 @@ export default function ChainView({ ); } -/** - * Ein CC, in dem der Spieler einmal steckte. Er gehört ihm nicht mehr, - * deshalb gibt es hier nichts zu entscheiden und nichts zu verschieben - - * nur nachzulesen, wann er dabei war und wohin es danach ging. - */ -function FruehererEintrag({ - character, - playerId, - onAction, -}: { - character: Character; - playerId: number; - onAction: (action: ChainAction, character: Character) => void; -}) { - const eigene = eigeneMitgliedschaft(character, playerId); - const thread = character.forumThreads?.[0]; - const besitzer = (character.members ?? []).find((m) => m.playerId === character.playerId); - - return ( -
-
- {thread ? ( - - {character.name} - - ) : ( - {character.name} - )} - war dabei -
- -

- {eigene?.joinedAt && `Dabei ab ${formatDate(eigene.joinedAt)}`} - {eigene?.leftAt && ` bis ${formatDate(eigene.leftAt)}`} - {eigene?.successor && ` · weiter als „${eigene.successor.name}"`} - {besitzer && besitzer.playerId !== playerId && ` · gehört ${besitzer.player.displayName}`} -

- {eigene?.note &&

{eigene.note}

} - -
- -
-
- ); -} - function ChainEntry({ character, playerId, @@ -372,6 +354,7 @@ function ChainEntry({ onDetach, onChanged, onError, + herkunft = false, }: { character: Character; playerId: number; @@ -379,6 +362,13 @@ function ChainEntry({ admin: boolean; ziehend: boolean; alleinInKette: boolean; + /** + * Ein CC, in dem der Spieler einmal steckte und der zu dieser Kette + * führte - meist ein Gruppen-CC, der jemand anderem gehört. Er bleibt + * voll bedienbar: beim Nacherfassen muss sich auch das einordnen lassen, + * was einem nicht gehört. + */ + herkunft?: boolean; onDragStart: () => void; onDragEnd: () => void; onAction: (action: ChainAction, character: Character) => void; @@ -398,6 +388,10 @@ function ChainEntry({ if (!aktiv) klassen.push('is-inactive'); if (ziehend) klassen.push('is-dragging'); if (ruht) klassen.push('is-resting'); + if (herkunft) klassen.push('is-past'); + + const eigene = (character.members ?? []).find((m) => m.playerId === playerId); + const besitzer = (character.members ?? []).find((m) => m.playerId === character.playerId); return (
)} {ruht && dein Platz ruht} + {herkunft && war dabei} + {herkunft && ( +

+ {eigene?.joinedAt && `Dabei ab ${formatDate(eigene.joinedAt)}`} + {eigene?.leftAt && ` bis ${formatDate(eigene.leftAt)}`} + {eigene?.successor && ` · weiter als „${eigene.successor.name}"`} + {besitzer && besitzer.playerId !== playerId && ` · gehört ${besitzer.player.displayName}`} +

+ )} + {character.loreText &&

{character.loreText}

} {character.inactiveNote &&

{character.inactiveNote}

} diff --git a/web/src/styles.css b/web/src/styles.css index 486527e..4addf71 100644 --- a/web/src/styles.css +++ b/web/src/styles.css @@ -1179,11 +1179,12 @@ textarea { letter-spacing: 0; } -/* Ein CC, der jemand anderem gehört und nur zur Geschichte beiträgt. */ +/* Ein CC, der jemand anderem gehört und zur Geschichte dieser Kette führte. + Er tritt optisch zurück, bleibt aber voll bedienbar - beim Nacherfassen + muss sich auch einordnen lassen, was einem nicht gehört. */ .chain-entry.is-past { - opacity: 0.7; + opacity: 0.8; border-style: dotted; - cursor: default; } .chain-past-head {