diff --git a/server/src/services/characters.ts b/server/src/services/characters.ts index 78f66b4..99dfd51 100644 --- a/server/src/services/characters.ts +++ b/server/src/services/characters.ts @@ -633,7 +633,7 @@ export async function revertCharacter( * überhaupt zu sehen ist. */ export async function listCharacters(playerId: number) { - return prisma.character.findMany({ + const ccs = await prisma.character.findMany({ where: { members: { some: { playerId } } }, orderBy: [{ lineageId: 'asc' }, { chainPosition: 'asc' }, { createdAt: 'asc' }], include: { @@ -657,6 +657,29 @@ export async function listCharacters(playerId: number) { }, }, }); + + // Womit dieser Spieler den jeweiligen Eintrag bezahlt hat. In der Kette + // steht das am Übergang: der Pfeil zum nächsten Glied trägt die Art, mit + // der dieses Glied bezahlt wurde. + const buchungen = await prisma.creditLedger.findMany({ + where: { + characterId: { in: ccs.map((c) => c.id) }, + playerId, + source: CreditSource.CONSUME, + reversedBy: null, + }, + select: { characterId: true, kind: true }, + orderBy: { id: 'desc' }, + }); + + const artNach = new Map(); + for (const buchung of buchungen) { + if (buchung.characterId !== null && !artNach.has(buchung.characterId)) { + artNach.set(buchung.characterId, buchung.kind); + } + } + + return ccs.map((cc) => ({ ...cc, fundedWith: artNach.get(cc.id) ?? null })); } diff --git a/web/src/components/ChainView.tsx b/web/src/components/ChainView.tsx index f4a8b4b..fa8ef21 100644 --- a/web/src/components/ChainView.tsx +++ b/web/src/components/ChainView.tsx @@ -312,9 +312,9 @@ export default function ChainView({ {i < vonObenNachUnten.length - 1 && ( <> - + {/* Der Pfeil führt zu diesem Eintrag - also trägt er, + womit dieser Schritt bezahlt wurde. */} + {zone( kette.lineageId, vonObenNachUnten[i + 1]?.id ?? null, @@ -327,11 +327,9 @@ export default function ChainView({ {zone(kette.lineageId, null, `${kette.lineageId}-unten`)} - {kette.herkunft.map((frueher) => ( + {kette.herkunft.map((frueher, i) => (
- + = { + REDESIGN_KULANZ: { zeichen: 'K', titel: 'Kulanz-Redesign', klasse: 'is-kulanz' }, + REDESIGN_PAID: { zeichen: 'R', titel: 'Bezahltes Redesign', klasse: 'is-paid' }, + CC: { zeichen: 'CC', titel: 'Aus dem CC-Topf bezahlt', klasse: 'is-cc' }, +}; + +/** + * Der Pfeil zum nächsten Glied, mit dem Kurzzeichen der Bezahlart. + * + * Womit ein Schritt bezahlt wurde, gehört an den Schritt - nicht in die + * Karte darüber. So liest sich die Kette in einem Zug: Eintrag, womit, + * Eintrag. + */ +function Uebergang({ character }: { character: Character | undefined }) { + const art = character?.fundedWith ? UEBERGANG[character.fundedWith] : undefined; + + return ( +
+ + {art && ( + + {art.zeichen} + + )} +
+ ); +} + function ChainEntry({ character, playerId, diff --git a/web/src/lib/types.ts b/web/src/lib/types.ts index 08b80fe..00db94b 100644 --- a/web/src/lib/types.ts +++ b/web/src/lib/types.ts @@ -149,6 +149,8 @@ export interface Character { successors: CharacterRef[]; forumThreads?: ThreadRef[]; links?: CharacterLink[]; + /// Womit dieser Eintrag bezahlt wurde, aus Sicht des betrachteten Spielers. + fundedWith?: CreditKind | null; members?: CharacterMember[]; createdAt: string; } diff --git a/web/src/styles.css b/web/src/styles.css index 4addf71..1d31d48 100644 --- a/web/src/styles.css +++ b/web/src/styles.css @@ -1171,6 +1171,7 @@ textarea { /* Der Pfeil zeigt zum jüngeren Eintrag, also nach oben. Er ist bewusst deutlich: er ist das Einzige, was die Leserichtung der Kette verrät. */ .chain-arrow { + position: relative; text-align: center; color: var(--accent); font-size: 1.4rem; @@ -1287,3 +1288,42 @@ textarea { padding-left: 0.6rem; padding-right: 0.6rem; } + +/* Das Kurzzeichen sitzt am Pfeil, nicht in der Karte: womit ein Schritt + bezahlt wurde, gehört an den Schritt. Absolut gesetzt, damit die Pfeile + aller Ketten in einer Flucht bleiben. */ +.chain-step { + position: absolute; + top: 50%; + left: 50%; + transform: translate(0.75rem, -50%); + display: inline-flex; + align-items: center; + justify-content: center; + min-width: 1.35rem; + height: 1.35rem; + padding: 0 0.3rem; + border: 1px solid var(--border-strong); + border-radius: 999px; + background: var(--bg); + font-size: 0.72rem; + font-weight: 600; + line-height: 1; + letter-spacing: 0; +} + +.chain-step.is-kulanz { + border-color: var(--positive); + color: var(--positive); +} + +.chain-step.is-paid { + border-color: var(--accent); + color: var(--accent); +} + +/* Aus dem CC-Topf bezahlt - die Ausnahme, also auffälliger. */ +.chain-step.is-cc { + border-color: var(--warn); + color: var(--warn); +}