diff --git a/src/Novelly.Web/src/api/chapterLabel.ts b/src/Novelly.Web/src/api/chapterLabel.ts new file mode 100644 index 0000000..9a295c5 --- /dev/null +++ b/src/Novelly.Web/src/api/chapterLabel.ts @@ -0,0 +1,6 @@ +import type { ChapterKind } from './types' + +export function chapterLabel(kind: ChapterKind, displayNumber: number | null, title: string): string { + if (kind === 'Body' && displayNumber !== null) return `Chapter ${displayNumber}: ${title}` + return title +} diff --git a/src/Novelly.Web/src/api/types.ts b/src/Novelly.Web/src/api/types.ts index 47615a3..5741f25 100644 --- a/src/Novelly.Web/src/api/types.ts +++ b/src/Novelly.Web/src/api/types.ts @@ -28,6 +28,10 @@ export type DraftStatus = 'Planned' | 'Outlined' | 'Drafted' | 'Revised' | 'Fina export const draftStatuses: DraftStatus[] = ['Planned', 'Outlined', 'Drafted', 'Revised', 'Final'] +export type ChapterKind = 'FrontMatter' | 'Body' | 'BackMatter' + +export const chapterKinds: ChapterKind[] = ['FrontMatter', 'Body', 'BackMatter'] + export type NovelPhase = 'Brainstorming' | 'Outlining' | 'Writing' | 'Editing' | 'Complete' export const novelPhases: NovelPhase[] = ['Brainstorming', 'Outlining', 'Writing', 'Editing', 'Complete'] @@ -112,12 +116,13 @@ export interface TagSummary extends Tag { export interface TagReferences { tag: Tag characters: { id: string; name: string; role: string }[] - chapters: { id: string; number: number; title: string; summary: string | null }[] + chapters: { id: string; number: number; kind: ChapterKind; displayNumber: number | null; title: string; summary: string | null }[] beats: { id: string chapterId: string chapterNumber: number chapterTitle: string + chapterLabel: string sortOrder: number title: string characterName: string | null @@ -136,7 +141,7 @@ export interface LocationSummary extends Location { export interface LocationReferences { location: Location - chapters: { id: string; number: number; title: string; summary: string | null }[] + chapters: { id: string; number: number; kind: ChapterKind; displayNumber: number | null; title: string; summary: string | null }[] } export interface BeatCharacter { @@ -173,6 +178,7 @@ export interface ArcStage { chapterId: string | null chapterNumber: number | null chapterTitle: string | null + chapterLabel: string | null beats: CharacterBeat[] updatedAt: string } @@ -182,6 +188,7 @@ export interface CharacterBeat { chapterId: string chapterNumber: number chapterTitle: string + chapterLabel: string sortOrder: number title: string whatHappened: string | null @@ -210,6 +217,7 @@ export interface Character { sameCharacterAsName: string | null revealedInChapterId: string | null revealedInChapterNumber: number | null + revealedInChapterLabel: string | null identityNote: string | null otherIdentities: CharacterIdentity[] relationships: Relationship[] @@ -227,6 +235,8 @@ export interface ChapterSummary { id: string novelId: string number: number + kind: ChapterKind + displayNumber: number | null title: string summary: string | null locations: Location[] @@ -254,6 +264,7 @@ export interface OpenQuestion { chapterId: string | null chapterNumber: number | null chapterTitle: string | null + chapterLabel: string | null characterId: string | null characterName: string | null resolution: string | null diff --git a/src/Novelly.Web/src/components/CharacterArc.tsx b/src/Novelly.Web/src/components/CharacterArc.tsx index a8e05aa..4225177 100644 --- a/src/Novelly.Web/src/components/CharacterArc.tsx +++ b/src/Novelly.Web/src/components/CharacterArc.tsx @@ -9,7 +9,8 @@ import { useSetArcStageBeats, useUpdateArcStage, } from '../api/hooks' -import type { ArcStage, Character } from '../api/types' +import { chapterLabel } from '../api/chapterLabel' +import type { ArcStage, Character, ChapterKind } from '../api/types' import { AutoField, ErrorNote } from './ui' export function CharacterArc({ @@ -127,7 +128,7 @@ function ArcStageRow({ }: { novelId: string stage: ArcStage - chapters: { id: string; number: number; title: string }[] + chapters: { id: string; number: number; kind: ChapterKind; displayNumber: number | null; title: string }[] unassignedBeats: { id: string; chapterNumber: number; sortOrder: number; title: string }[] canMoveUp: boolean canMoveDown: boolean @@ -226,7 +227,7 @@ function ArcStageRow({ {chapters.map((chapter) => ( ))} diff --git a/src/Novelly.Web/src/components/OpenQuestions.tsx b/src/Novelly.Web/src/components/OpenQuestions.tsx index be4a672..b39ad0e 100644 --- a/src/Novelly.Web/src/components/OpenQuestions.tsx +++ b/src/Novelly.Web/src/components/OpenQuestions.tsx @@ -182,7 +182,7 @@ function QuestionRow({ {(showsChapter || showsCharacter) && (

- {showsChapter && `Ch. ${question.chapterNumber} ${question.chapterTitle}`} + {showsChapter && question.chapterLabel} {showsChapter && showsCharacter && ' · '} {showsCharacter && question.characterName}

diff --git a/src/Novelly.Web/src/components/ui.tsx b/src/Novelly.Web/src/components/ui.tsx index 452758c..9372ffa 100644 --- a/src/Novelly.Web/src/components/ui.tsx +++ b/src/Novelly.Web/src/components/ui.tsx @@ -139,11 +139,13 @@ export function AutoField({ } export function Select({ + id, label, value, options, onChange, }: { + id?: string label?: string value: T options: readonly T[] @@ -152,7 +154,7 @@ export function Select({ return (