Remove unused components and models for improved maintainability
- Deleted redundant components (`AddActionButton`, `AlertBox`, `AlertStack`, `BackButton`, `CancelButton`, and `CollapsableArea`) and related files. - Removed unused models (`Book`, `BookSerie`, `BookTables`, `Character`, and `Chapter`) to reduce codebase clutter. - Updated project structure and references to reflect these removals.
This commit is contained in:
224
lib/types/book-tables.ts
Normal file
224
lib/types/book-tables.ts
Normal file
@@ -0,0 +1,224 @@
|
||||
export interface EritBooksTable {
|
||||
book_id: string;
|
||||
type: string;
|
||||
author_id: string;
|
||||
title: string;
|
||||
hashed_title: string;
|
||||
sub_title: string | null;
|
||||
hashed_sub_title: string | null;
|
||||
summary: string | null;
|
||||
serie_id: number | null;
|
||||
desired_release_date: string | null;
|
||||
desired_word_count: number | null;
|
||||
words_count: number | null;
|
||||
cover_image: string | null;
|
||||
last_update: number;
|
||||
}
|
||||
|
||||
export interface BookActSummariesTable {
|
||||
act_sum_id: string;
|
||||
book_id: string;
|
||||
user_id: string;
|
||||
act_index: number;
|
||||
summary: string | null;
|
||||
last_update: number;
|
||||
}
|
||||
|
||||
export interface BookAIGuideLineTable {
|
||||
user_id: string;
|
||||
book_id: string;
|
||||
global_resume: string | null;
|
||||
themes: string | null;
|
||||
verbe_tense: number | null;
|
||||
narrative_type: number | null;
|
||||
langue: number | null;
|
||||
dialogue_type: number | null;
|
||||
tone: string | null;
|
||||
atmosphere: string | null;
|
||||
current_resume: string | null;
|
||||
}
|
||||
|
||||
export interface BookChaptersTable {
|
||||
chapter_id: string;
|
||||
book_id: string;
|
||||
author_id: string;
|
||||
title: string;
|
||||
hashed_title: string;
|
||||
words_count: number | null;
|
||||
chapter_order: number;
|
||||
last_update: number;
|
||||
}
|
||||
|
||||
export interface BookChapterContentTable {
|
||||
content_id: string;
|
||||
chapter_id: string;
|
||||
author_id: string;
|
||||
version: number;
|
||||
content: string | null;
|
||||
words_count: number;
|
||||
time_on_it: number;
|
||||
last_update: number;
|
||||
}
|
||||
|
||||
export interface BookChapterInfosTable {
|
||||
chapter_info_id: string;
|
||||
chapter_id: string;
|
||||
act_id: number;
|
||||
incident_id: string | null;
|
||||
plot_point_id: string | null;
|
||||
book_id: string;
|
||||
author_id: string;
|
||||
summary: string | null;
|
||||
goal: string | null;
|
||||
last_update: number;
|
||||
}
|
||||
|
||||
export interface BookCharactersTable {
|
||||
character_id: string;
|
||||
book_id: string;
|
||||
user_id: string;
|
||||
first_name: string;
|
||||
last_name: string | null;
|
||||
category: string;
|
||||
title: string | null;
|
||||
image: string | null;
|
||||
role: string | null;
|
||||
biography: string | null;
|
||||
history: string | null;
|
||||
last_update: number;
|
||||
}
|
||||
|
||||
export interface BookCharactersAttributesTable {
|
||||
attr_id: string;
|
||||
character_id: string;
|
||||
user_id: string;
|
||||
attribute_name: string;
|
||||
attribute_value: string;
|
||||
last_update: number;
|
||||
}
|
||||
|
||||
export interface BookGuideLineTable {
|
||||
user_id: string;
|
||||
book_id: string;
|
||||
tone: string | null;
|
||||
atmosphere: string | null;
|
||||
writing_style: string | null;
|
||||
themes: string | null;
|
||||
symbolism: string | null;
|
||||
motifs: string | null;
|
||||
narrative_voice: string | null;
|
||||
pacing: string | null;
|
||||
intended_audience: string | null;
|
||||
key_messages: string | null;
|
||||
}
|
||||
|
||||
export interface BookIncidentsTable {
|
||||
incident_id: string;
|
||||
author_id: string;
|
||||
book_id: string;
|
||||
title: string;
|
||||
hashed_title: string;
|
||||
summary: string | null;
|
||||
last_update: number;
|
||||
}
|
||||
|
||||
export interface BookIssuesTable {
|
||||
issue_id: string;
|
||||
author_id: string;
|
||||
book_id: string;
|
||||
name: string;
|
||||
hashed_issue_name: string;
|
||||
last_update: number;
|
||||
}
|
||||
|
||||
export interface BookLocationTable {
|
||||
loc_id: string;
|
||||
book_id: string;
|
||||
user_id: string;
|
||||
loc_name: string;
|
||||
loc_original_name: string;
|
||||
last_update: number;
|
||||
}
|
||||
|
||||
export interface LocationElementTable {
|
||||
element_id: string;
|
||||
location: string;
|
||||
user_id: string;
|
||||
element_name: string;
|
||||
original_name: string;
|
||||
element_description: string | null;
|
||||
last_update: number;
|
||||
}
|
||||
|
||||
export interface LocationSubElementTable {
|
||||
sub_element_id: string;
|
||||
element_id: string;
|
||||
user_id: string;
|
||||
sub_elem_name: string;
|
||||
original_name: string;
|
||||
sub_elem_description: string | null;
|
||||
last_update: number;
|
||||
}
|
||||
|
||||
export interface BookPlotPointsTable {
|
||||
plot_point_id: string;
|
||||
title: string;
|
||||
hashed_title: string;
|
||||
summary: string | null;
|
||||
linked_incident_id: string | null;
|
||||
author_id: string;
|
||||
book_id: string;
|
||||
last_update: number;
|
||||
}
|
||||
|
||||
export interface BookWorldTable {
|
||||
world_id: string;
|
||||
name: string;
|
||||
hashed_name: string;
|
||||
author_id: string;
|
||||
book_id: string;
|
||||
history: string | null;
|
||||
politics: string | null;
|
||||
economy: string | null;
|
||||
religion: string | null;
|
||||
languages: string | null;
|
||||
last_update: number;
|
||||
}
|
||||
|
||||
export interface BookWorldElementsTable {
|
||||
element_id: string;
|
||||
world_id: string;
|
||||
user_id: string;
|
||||
element_type: number;
|
||||
name: string;
|
||||
original_name: string;
|
||||
description: string | null;
|
||||
last_update: number;
|
||||
}
|
||||
|
||||
export interface GhostWriterSettingsTable {
|
||||
book_id: string;
|
||||
user_id: string;
|
||||
advanced_prompt: string | null;
|
||||
quillsense_enabled: number;
|
||||
}
|
||||
|
||||
export interface CompleteBook {
|
||||
eritBooks: EritBooksTable[];
|
||||
actSummaries: BookActSummariesTable[];
|
||||
aiGuideLine: BookAIGuideLineTable[];
|
||||
chapters: BookChaptersTable[];
|
||||
chapterContents: BookChapterContentTable[];
|
||||
chapterInfos: BookChapterInfosTable[];
|
||||
characters: BookCharactersTable[];
|
||||
characterAttributes: BookCharactersAttributesTable[];
|
||||
guideLine: BookGuideLineTable[];
|
||||
incidents: BookIncidentsTable[];
|
||||
issues: BookIssuesTable[];
|
||||
locations: BookLocationTable[];
|
||||
plotPoints: BookPlotPointsTable[];
|
||||
worlds: BookWorldTable[];
|
||||
worldElements: BookWorldElementsTable[];
|
||||
locationElements: LocationElementTable[];
|
||||
locationSubElements: LocationSubElementTable[];
|
||||
}
|
||||
108
lib/types/book.ts
Normal file
108
lib/types/book.ts
Normal file
@@ -0,0 +1,108 @@
|
||||
import {Author} from "@/lib/types/user";
|
||||
import {ActChapter, ChapterProps} from "@/lib/types/chapter";
|
||||
|
||||
export interface BookToolsSettings {
|
||||
characters: boolean;
|
||||
worlds: boolean;
|
||||
locations: boolean;
|
||||
spells: boolean;
|
||||
}
|
||||
|
||||
export interface BookProps {
|
||||
bookId: string;
|
||||
type: string;
|
||||
title: string;
|
||||
author?: Author;
|
||||
serie?: number;
|
||||
seriesId?: string | null;
|
||||
subTitle?: string;
|
||||
summary?: string;
|
||||
publicationDate?: string;
|
||||
desiredWordCount?: number;
|
||||
totalWordCount?: number;
|
||||
coverImage?: string;
|
||||
chapters?: ChapterProps[];
|
||||
quillsenseEnabled?: boolean;
|
||||
tools?: BookToolsSettings;
|
||||
localBook?: boolean;
|
||||
}
|
||||
|
||||
export interface BookListProps {
|
||||
id: string;
|
||||
type: string;
|
||||
authorId: string;
|
||||
title: string;
|
||||
subTitle?: string;
|
||||
summary?: string;
|
||||
serieId?: number;
|
||||
desiredReleaseDate?: string;
|
||||
desiredWordCount?: number;
|
||||
wordCount?: number;
|
||||
coverImage?: string;
|
||||
bookMeta?: string;
|
||||
quillsenseEnabled?: boolean;
|
||||
}
|
||||
|
||||
export interface GuideLine {
|
||||
tone: string;
|
||||
atmosphere: string;
|
||||
writingStyle: string;
|
||||
themes: string;
|
||||
symbolism: string;
|
||||
motifs: string;
|
||||
narrativeVoice: string;
|
||||
pacing: string;
|
||||
intendedAudience: string;
|
||||
keyMessages: string;
|
||||
}
|
||||
|
||||
export interface GuideLineAI {
|
||||
narrativeType: number | null;
|
||||
dialogueType: number | null;
|
||||
globalResume: string | null;
|
||||
atmosphere: string | null;
|
||||
verbeTense: number | null;
|
||||
langue: number | null;
|
||||
currentResume: string | null;
|
||||
themes: string | null;
|
||||
}
|
||||
|
||||
export interface PlotPoint {
|
||||
plotPointId: string;
|
||||
title: string;
|
||||
summary: string;
|
||||
linkedIncidentId: string;
|
||||
chapters?: ActChapter[];
|
||||
}
|
||||
|
||||
export interface Incident {
|
||||
incidentId: string;
|
||||
title: string;
|
||||
summary: string;
|
||||
chapters?: ActChapter[];
|
||||
}
|
||||
|
||||
export interface Issue {
|
||||
name: string;
|
||||
id: string;
|
||||
}
|
||||
|
||||
export interface Act {
|
||||
id: number;
|
||||
summary: string | null;
|
||||
incidents?: Incident[];
|
||||
plotPoints?: PlotPoint[];
|
||||
chapters?: ActChapter[];
|
||||
}
|
||||
|
||||
export interface Tag {
|
||||
label: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
export interface BookTags {
|
||||
characters: Tag[];
|
||||
locations: Tag[];
|
||||
objects: Tag[];
|
||||
worldElements: Tag[];
|
||||
}
|
||||
80
lib/types/chapter.ts
Normal file
80
lib/types/chapter.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
export interface ActChapter {
|
||||
chapterInfoId: string;
|
||||
chapterId: string;
|
||||
title: string;
|
||||
chapterOrder: number;
|
||||
actId: number;
|
||||
incidentId?: string;
|
||||
plotPointId?: string;
|
||||
summary: string;
|
||||
goal: string;
|
||||
}
|
||||
|
||||
export interface ChapterListProps {
|
||||
chapterId: string;
|
||||
title: string;
|
||||
summary?: string;
|
||||
chapterOrder?: number;
|
||||
goal?: string;
|
||||
}
|
||||
|
||||
export interface ChapterProps {
|
||||
chapterId: string;
|
||||
chapterOrder: number;
|
||||
title: string;
|
||||
chapterContent: ChapterContent;
|
||||
}
|
||||
|
||||
export interface ChapterContent {
|
||||
version: number;
|
||||
content: string;
|
||||
wordsCount: number;
|
||||
}
|
||||
|
||||
export interface ChapterVersion {
|
||||
value: number;
|
||||
label: 'Invite' | 'Brouillon' | 'Perfectionnement' | 'Révision' | 'Finale';
|
||||
}
|
||||
|
||||
export interface TiptapLinkAttrs {
|
||||
href: string;
|
||||
target?: string;
|
||||
}
|
||||
|
||||
export type TiptapAttrValue = string | number | boolean | null | TiptapLinkAttrs;
|
||||
|
||||
export type TiptapNode = {
|
||||
type: string;
|
||||
content?: TiptapNode[];
|
||||
text?: string;
|
||||
attrs?: {
|
||||
[key: string]: TiptapAttrValue;
|
||||
};
|
||||
};
|
||||
|
||||
export interface CompanionContent {
|
||||
version: number;
|
||||
content: string;
|
||||
wordsCount: number;
|
||||
}
|
||||
|
||||
export type ExportFormat = 'epub' | 'pdf' | 'docx';
|
||||
|
||||
export interface ChapterExportInfo {
|
||||
chapterId: string;
|
||||
title: string;
|
||||
chapterOrder: number;
|
||||
availableVersions: number[];
|
||||
}
|
||||
|
||||
export interface ChapterExportSelection {
|
||||
chapterId: string;
|
||||
version: number;
|
||||
selected: boolean;
|
||||
}
|
||||
|
||||
export interface ExportRequestBody {
|
||||
bookId: string;
|
||||
format: ExportFormat;
|
||||
chapters: { chapterId: string; version: number }[];
|
||||
}
|
||||
105
lib/types/character.ts
Normal file
105
lib/types/character.ts
Normal file
@@ -0,0 +1,105 @@
|
||||
import {LucideIcon} from 'lucide-react';
|
||||
|
||||
export type CharacterCategory = 'main' | 'secondary' | 'recurring' | 'none';
|
||||
export type CharacterStatus = 'alive' | 'dead' | 'unknown';
|
||||
export type CharacterAttributeSection =
|
||||
'physical'
|
||||
| 'psychological'
|
||||
| 'relations'
|
||||
| 'skills'
|
||||
| 'weaknesses'
|
||||
| 'strengths'
|
||||
| 'goals'
|
||||
| 'motivations'
|
||||
| 'arc'
|
||||
| 'secrets'
|
||||
| 'fears'
|
||||
| 'flaws'
|
||||
| 'beliefs'
|
||||
| 'conflicts'
|
||||
| 'quotes'
|
||||
| 'distinguishingMarks'
|
||||
| 'items'
|
||||
| 'affiliations';
|
||||
|
||||
export function isCharacterCategory(value: string): value is CharacterCategory {
|
||||
return value === 'main' || value === 'secondary' || value === 'recurring' || value === 'none';
|
||||
}
|
||||
|
||||
export function isCharacterStatus(value: string): value is CharacterStatus {
|
||||
return value === 'alive' || value === 'dead' || value === 'unknown';
|
||||
}
|
||||
|
||||
export interface Relation {
|
||||
name: string;
|
||||
type: string;
|
||||
description: string;
|
||||
history: string;
|
||||
}
|
||||
|
||||
export interface Attribute {
|
||||
id: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface CharacterAttribute {
|
||||
[key: string]: Array<Attribute>;
|
||||
}
|
||||
|
||||
export interface CharacterProps {
|
||||
id: string | null;
|
||||
name: string;
|
||||
lastName: string;
|
||||
nickname: string;
|
||||
age: number | null;
|
||||
gender: string;
|
||||
species: string;
|
||||
nationality: string;
|
||||
status: CharacterStatus;
|
||||
category: CharacterCategory;
|
||||
title: string;
|
||||
image: string;
|
||||
physical: Attribute[];
|
||||
psychological: Attribute[];
|
||||
relations: Attribute[];
|
||||
skills: Attribute[];
|
||||
weaknesses: Attribute[];
|
||||
strengths: Attribute[];
|
||||
goals: Attribute[];
|
||||
motivations: Attribute[];
|
||||
arc: Attribute[];
|
||||
secrets: Attribute[];
|
||||
fears: Attribute[];
|
||||
flaws: Attribute[];
|
||||
beliefs: Attribute[];
|
||||
conflicts: Attribute[];
|
||||
quotes: Attribute[];
|
||||
distinguishingMarks: Attribute[];
|
||||
items: Attribute[];
|
||||
affiliations: Attribute[];
|
||||
role: string;
|
||||
biography?: string;
|
||||
history?: string;
|
||||
speechPattern?: string;
|
||||
catchphrase?: string;
|
||||
residence?: string;
|
||||
notes?: string;
|
||||
color?: string;
|
||||
seriesCharacterId?: string | null;
|
||||
}
|
||||
|
||||
export interface CharacterListResponse {
|
||||
characters: CharacterProps[];
|
||||
enabled: boolean;
|
||||
}
|
||||
|
||||
export interface CharacterElement {
|
||||
title: string;
|
||||
section: CharacterAttributeSection;
|
||||
placeholder: string;
|
||||
icon: LucideIcon;
|
||||
}
|
||||
|
||||
export interface AttributeResponse {
|
||||
attributes: Attribute[];
|
||||
}
|
||||
10
lib/types/editor.ts
Normal file
10
lib/types/editor.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import {LucideIcon} from 'lucide-react';
|
||||
|
||||
export interface PanelComponent {
|
||||
id: number;
|
||||
title: string;
|
||||
badge: string;
|
||||
description: string;
|
||||
icon: LucideIcon;
|
||||
action?: () => void;
|
||||
}
|
||||
27
lib/types/import.ts
Normal file
27
lib/types/import.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
export interface ParsedChapterPreview {
|
||||
index: number;
|
||||
title: string;
|
||||
wordCount: number;
|
||||
}
|
||||
|
||||
export interface ParsedDocxResponse {
|
||||
importId: string;
|
||||
chapters: ParsedChapterPreview[];
|
||||
}
|
||||
|
||||
export interface ImportChapterSelection {
|
||||
index: number;
|
||||
title: string;
|
||||
wordCount: number;
|
||||
selected: boolean;
|
||||
}
|
||||
|
||||
export interface ImportConfirmBody {
|
||||
importId: string;
|
||||
title: string;
|
||||
subTitle: string;
|
||||
summary: string;
|
||||
type: string;
|
||||
version: number;
|
||||
selectedChapterIndexes: number[];
|
||||
}
|
||||
109
lib/types/quillsense.ts
Normal file
109
lib/types/quillsense.ts
Normal file
@@ -0,0 +1,109 @@
|
||||
export type MessageType = "user" | "model";
|
||||
export type QSView = 'list' | 'chat' | 'ghostwritter' | 'dictionary' | 'synonyms' | 'conjugator' | 'inspiration';
|
||||
export type ConversationType = 'dictionary' | 'synonyms' | 'conjugator' | 'chatbot' | 'inspire';
|
||||
|
||||
export interface Message {
|
||||
id: number;
|
||||
type: MessageType;
|
||||
message: string;
|
||||
date: string;
|
||||
}
|
||||
|
||||
export interface Conversation {
|
||||
id: string;
|
||||
title?: string;
|
||||
date?: string;
|
||||
type?: ConversationType;
|
||||
messages: Message[];
|
||||
status: number;
|
||||
totalPrice?: number;
|
||||
useYourKey?: boolean;
|
||||
}
|
||||
|
||||
export interface AIGeneratedTextData {
|
||||
totalCost: number;
|
||||
response: string;
|
||||
}
|
||||
|
||||
export interface AIResponseWithCredits<T> {
|
||||
useYourKey: boolean;
|
||||
totalPrice: number;
|
||||
data: T;
|
||||
}
|
||||
|
||||
export interface AIDictionary extends AIResponseWithCredits<DictionaryAIResponse> {
|
||||
}
|
||||
|
||||
export interface AIGeneratedText extends AIResponseWithCredits<AIGeneratedTextData> {
|
||||
}
|
||||
|
||||
export interface AIInspire extends AIResponseWithCredits<InspireAIResponse> {
|
||||
}
|
||||
|
||||
export interface AISynonyms extends AIResponseWithCredits<SynonymsAIResponse> {
|
||||
}
|
||||
|
||||
export interface ConjugationTenses {
|
||||
[tense: string]: {
|
||||
firstPersonSingular?: string;
|
||||
secondPersonSingular?: string;
|
||||
thirdPersonSingular?: string;
|
||||
firstPersonPlural?: string;
|
||||
secondPersonPlural?: string;
|
||||
thirdPersonPlural?: string;
|
||||
présent?: string;
|
||||
passé?: string;
|
||||
} | string;
|
||||
}
|
||||
|
||||
export interface ConjugationResponse {
|
||||
conjugations: {
|
||||
[mode: string]: ConjugationTenses;
|
||||
};
|
||||
}
|
||||
|
||||
export interface AIVerbConjugation extends AIResponseWithCredits<ConjugationResponse> {
|
||||
}
|
||||
|
||||
export interface InspireAIResponse {
|
||||
ideas: {
|
||||
idea: string;
|
||||
reason: string;
|
||||
relatedTo: string;
|
||||
}[];
|
||||
}
|
||||
|
||||
export interface DictionaryAIResponse {
|
||||
word: string;
|
||||
definition: string;
|
||||
example: string;
|
||||
literaryUsage: string;
|
||||
}
|
||||
|
||||
export interface SynonymAI {
|
||||
word: string;
|
||||
context: string;
|
||||
}
|
||||
|
||||
export interface SynonymsAIResponse {
|
||||
words: SynonymAI[];
|
||||
}
|
||||
|
||||
export interface InspirationAIIdea {
|
||||
idea: string;
|
||||
reason: string;
|
||||
relatedTo: string;
|
||||
}
|
||||
|
||||
export interface ConversationProps {
|
||||
id: string;
|
||||
mode: string;
|
||||
title: string;
|
||||
startDate: string;
|
||||
status: number;
|
||||
}
|
||||
|
||||
export interface QuillSenseSettingsProps {
|
||||
quillsenseEnabled: boolean;
|
||||
advancedPrompt: string | null;
|
||||
}
|
||||
166
lib/types/series.ts
Normal file
166
lib/types/series.ts
Normal file
@@ -0,0 +1,166 @@
|
||||
export interface SeriesProps {
|
||||
id: string | null;
|
||||
name: string;
|
||||
description: string;
|
||||
coverImage: string | null;
|
||||
}
|
||||
|
||||
export interface SeriesBookProps {
|
||||
bookId: string;
|
||||
title: string;
|
||||
order: number;
|
||||
coverImage: string | null;
|
||||
}
|
||||
|
||||
export interface SeriesDetailResponse {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
coverImage: string | null;
|
||||
books: SeriesBookProps[];
|
||||
}
|
||||
|
||||
export interface SeriesAddResponse {
|
||||
seriesId: string;
|
||||
}
|
||||
|
||||
export interface SeriesUpdateResponse {
|
||||
success: boolean;
|
||||
}
|
||||
|
||||
export interface SeriesListItemProps {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
coverImage: string | null;
|
||||
bookCount: number;
|
||||
bookIds: string[];
|
||||
}
|
||||
|
||||
export interface SeriesCharacterListItem {
|
||||
id: string;
|
||||
name: string;
|
||||
lastName: string | null;
|
||||
category: string;
|
||||
role: string | null;
|
||||
color: string | null;
|
||||
image: string | null;
|
||||
}
|
||||
|
||||
export interface SeriesCharacterDetailResponse {
|
||||
id: string;
|
||||
name: string;
|
||||
lastName: string | null;
|
||||
nickname: string | null;
|
||||
age: number | null;
|
||||
gender: string | null;
|
||||
species: string | null;
|
||||
nationality: string | null;
|
||||
status: string | null;
|
||||
category: string;
|
||||
title: string | null;
|
||||
image: string | null;
|
||||
role: string | null;
|
||||
biography: string | null;
|
||||
history: string | null;
|
||||
speechPattern: string | null;
|
||||
catchphrase: string | null;
|
||||
residence: string | null;
|
||||
notes: string | null;
|
||||
color: string | null;
|
||||
attributes?: SeriesCharacterAttribute[];
|
||||
}
|
||||
|
||||
export interface SeriesCharacterAttribute {
|
||||
id: string;
|
||||
name: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
export type SeriesCharacterProps = SeriesCharacterDetailResponse;
|
||||
|
||||
export interface SeriesWorldElementItem {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
export interface SeriesWorldListItem {
|
||||
id: string;
|
||||
name: string;
|
||||
history: string;
|
||||
politics: string;
|
||||
economy: string;
|
||||
religion: string;
|
||||
languages: string;
|
||||
laws: SeriesWorldElementItem[];
|
||||
biomes: SeriesWorldElementItem[];
|
||||
issues: SeriesWorldElementItem[];
|
||||
customs: SeriesWorldElementItem[];
|
||||
kingdoms: SeriesWorldElementItem[];
|
||||
climate: SeriesWorldElementItem[];
|
||||
resources: SeriesWorldElementItem[];
|
||||
wildlife: SeriesWorldElementItem[];
|
||||
arts: SeriesWorldElementItem[];
|
||||
ethnicGroups: SeriesWorldElementItem[];
|
||||
socialClasses: SeriesWorldElementItem[];
|
||||
importantCharacters: SeriesWorldElementItem[];
|
||||
}
|
||||
|
||||
export type SeriesWorldProps = SeriesWorldListItem;
|
||||
|
||||
export interface SeriesWorldElement {
|
||||
id: string;
|
||||
type: number;
|
||||
name: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
export interface SeriesLocationSubElement {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
export interface SeriesLocationElement {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
subElements: SeriesLocationSubElement[];
|
||||
}
|
||||
|
||||
export interface SeriesLocationItem {
|
||||
id: string;
|
||||
name: string;
|
||||
elements: SeriesLocationElement[];
|
||||
}
|
||||
|
||||
export interface SeriesSpellTag {
|
||||
id: string;
|
||||
name: string;
|
||||
color: string | null;
|
||||
}
|
||||
|
||||
export interface SeriesSpellListItem {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
tags: string[] | null;
|
||||
}
|
||||
|
||||
export interface SeriesSpellListResponse {
|
||||
spells: SeriesSpellListItem[];
|
||||
tags: SeriesSpellTag[];
|
||||
}
|
||||
|
||||
export interface SeriesSpellDetailResponse {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
appearance: string;
|
||||
tags: string[];
|
||||
powerLevel: string | null;
|
||||
components: string | null;
|
||||
limitations: string | null;
|
||||
notes: string | null;
|
||||
}
|
||||
14
lib/types/session.ts
Normal file
14
lib/types/session.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import {UserProps} from "@/lib/types/user";
|
||||
|
||||
export interface SessionProps {
|
||||
isConnected: boolean;
|
||||
accessToken: string;
|
||||
user: UserProps | null;
|
||||
}
|
||||
|
||||
export interface LoginResponse {
|
||||
valid: boolean;
|
||||
message?: string;
|
||||
token?: string;
|
||||
userid?: string;
|
||||
}
|
||||
5
lib/types/settings.ts
Normal file
5
lib/types/settings.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export type ViewMode = 'list' | 'detail' | 'edit';
|
||||
|
||||
export interface SettingRef {
|
||||
handleSave: () => Promise<void>;
|
||||
}
|
||||
58
lib/types/spell.ts
Normal file
58
lib/types/spell.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
export interface SpellTagProps {
|
||||
id: string;
|
||||
name: string;
|
||||
color: string | null;
|
||||
}
|
||||
|
||||
export interface SpellProps {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
appearance: string;
|
||||
tags: string[];
|
||||
powerLevel: string | null;
|
||||
components: string | null;
|
||||
limitations: string | null;
|
||||
notes: string | null;
|
||||
seriesSpellId?: string | null;
|
||||
}
|
||||
|
||||
export interface SpellPropsPost {
|
||||
id?: string;
|
||||
name: string;
|
||||
description: string;
|
||||
appearance: string;
|
||||
tags: string[];
|
||||
powerLevel?: string | null;
|
||||
components?: string | null;
|
||||
limitations?: string | null;
|
||||
notes?: string | null;
|
||||
seriesSpellId?: string | null;
|
||||
}
|
||||
|
||||
export interface SpellListItem {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
tags: SpellTagProps[];
|
||||
seriesSpellId?: string | null;
|
||||
}
|
||||
|
||||
export interface SpellListResponse {
|
||||
enabled: boolean;
|
||||
spells: SpellListItem[];
|
||||
tags: SpellTagProps[];
|
||||
}
|
||||
|
||||
export interface SpellEditState {
|
||||
id: string | null;
|
||||
name: string;
|
||||
description: string;
|
||||
appearance: string;
|
||||
tags: string[];
|
||||
powerLevel: string | null;
|
||||
components: string | null;
|
||||
limitations: string | null;
|
||||
notes: string | null;
|
||||
seriesSpellId?: string | null;
|
||||
}
|
||||
29
lib/types/story.ts
Normal file
29
lib/types/story.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import {ChapterListProps} from "@/lib/types/chapter";
|
||||
import {Act, Issue} from "@/lib/types/book";
|
||||
|
||||
export interface StoryProps {
|
||||
mainChapter: ChapterListProps[];
|
||||
acts: Act[];
|
||||
issues: Issue[];
|
||||
}
|
||||
|
||||
export interface VerbalTimeProps {
|
||||
actions: string;
|
||||
descriptions: string;
|
||||
dialogues: string;
|
||||
thoughts: string;
|
||||
summary: string;
|
||||
}
|
||||
|
||||
export interface DialogueProps {
|
||||
description: string;
|
||||
example: string;
|
||||
}
|
||||
|
||||
export interface GeneratedShortStory {
|
||||
title: string;
|
||||
short: string;
|
||||
resume: string;
|
||||
totalPrice: number;
|
||||
totalTokens: number;
|
||||
}
|
||||
338
lib/types/synced-book.ts
Normal file
338
lib/types/synced-book.ts
Normal file
@@ -0,0 +1,338 @@
|
||||
export interface SyncedBook {
|
||||
id: string;
|
||||
type: string;
|
||||
title: string;
|
||||
subTitle: string | null;
|
||||
seriesId: string | null;
|
||||
lastUpdate: number;
|
||||
chapters: SyncedChapter[];
|
||||
characters: SyncedCharacter[];
|
||||
locations: SyncedLocation[];
|
||||
worlds: SyncedWorld[];
|
||||
incidents: SyncedIncident[];
|
||||
plotPoints: SyncedPlotPoint[];
|
||||
issues: SyncedIssue[];
|
||||
actSummaries: SyncedActSummary[];
|
||||
guideLine: SyncedGuideLine | null;
|
||||
aiGuideLine: SyncedAIGuideLine | null;
|
||||
bookTools: SyncedBookTools | null;
|
||||
spells: SyncedSpell[];
|
||||
spellTags: SyncedSpellTag[];
|
||||
}
|
||||
|
||||
export interface SyncedChapter {
|
||||
id: string;
|
||||
name: string;
|
||||
lastUpdate: number;
|
||||
contents: SyncedChapterContent[];
|
||||
info: SyncedChapterInfo | null;
|
||||
}
|
||||
|
||||
export interface SyncedChapterContent {
|
||||
id: string;
|
||||
lastUpdate: number;
|
||||
}
|
||||
|
||||
export interface SyncedChapterInfo {
|
||||
id: string;
|
||||
lastUpdate: number;
|
||||
}
|
||||
|
||||
export interface SyncedCharacter {
|
||||
id: string;
|
||||
name: string;
|
||||
lastUpdate: number;
|
||||
attributes: SyncedCharacterAttribute[];
|
||||
}
|
||||
|
||||
export interface SyncedCharacterAttribute {
|
||||
id: string;
|
||||
name: string;
|
||||
lastUpdate: number;
|
||||
}
|
||||
|
||||
export interface SyncedLocation {
|
||||
id: string;
|
||||
name: string;
|
||||
lastUpdate: number;
|
||||
elements: SyncedLocationElement[];
|
||||
}
|
||||
|
||||
export interface SyncedLocationElement {
|
||||
id: string;
|
||||
name: string;
|
||||
lastUpdate: number;
|
||||
subElements: SyncedLocationSubElement[];
|
||||
}
|
||||
|
||||
export interface SyncedLocationSubElement {
|
||||
id: string;
|
||||
name: string;
|
||||
lastUpdate: number;
|
||||
}
|
||||
|
||||
export interface SyncedWorld {
|
||||
id: string;
|
||||
name: string;
|
||||
lastUpdate: number;
|
||||
elements: SyncedWorldElement[];
|
||||
}
|
||||
|
||||
export interface SyncedWorldElement {
|
||||
id: string;
|
||||
name: string;
|
||||
lastUpdate: number;
|
||||
}
|
||||
|
||||
export interface SyncedIncident {
|
||||
id: string;
|
||||
name: string;
|
||||
lastUpdate: number;
|
||||
}
|
||||
|
||||
export interface SyncedPlotPoint {
|
||||
id: string;
|
||||
name: string;
|
||||
lastUpdate: number;
|
||||
}
|
||||
|
||||
export interface SyncedIssue {
|
||||
id: string;
|
||||
name: string;
|
||||
lastUpdate: number;
|
||||
}
|
||||
|
||||
export interface SyncedActSummary {
|
||||
id: string;
|
||||
lastUpdate: number;
|
||||
}
|
||||
|
||||
export interface SyncedGuideLine {
|
||||
lastUpdate: number;
|
||||
}
|
||||
|
||||
export interface SyncedAIGuideLine {
|
||||
lastUpdate: number;
|
||||
}
|
||||
|
||||
export interface SyncedBookTools {
|
||||
lastUpdate: number;
|
||||
}
|
||||
|
||||
export interface SyncedSpell {
|
||||
id: string;
|
||||
name: string;
|
||||
lastUpdate: number;
|
||||
}
|
||||
|
||||
export interface SyncedSpellTag {
|
||||
id: string;
|
||||
name: string;
|
||||
lastUpdate: number;
|
||||
}
|
||||
|
||||
export interface BookSyncCompare {
|
||||
id: string;
|
||||
chapters: string[];
|
||||
chapterContents: string[];
|
||||
chapterInfos: string[];
|
||||
characters: string[];
|
||||
characterAttributes: string[];
|
||||
locations: string[];
|
||||
locationElements: string[];
|
||||
locationSubElements: string[];
|
||||
worlds: string[];
|
||||
worldElements: string[];
|
||||
incidents: string[];
|
||||
plotPoints: string[];
|
||||
issues: string[];
|
||||
actSummaries: string[];
|
||||
guideLine: boolean;
|
||||
aiGuideLine: boolean;
|
||||
bookTools: boolean;
|
||||
spells: string[];
|
||||
spellTags: string[];
|
||||
}
|
||||
|
||||
export function compareBookSyncs(newerBook: SyncedBook, olderBook: SyncedBook): BookSyncCompare | null {
|
||||
const changedChapterIds: string[] = [];
|
||||
const changedChapterContentIds: string[] = [];
|
||||
const changedChapterInfoIds: string[] = [];
|
||||
const changedCharacterIds: string[] = [];
|
||||
const changedCharacterAttributeIds: string[] = [];
|
||||
const changedLocationIds: string[] = [];
|
||||
const changedLocationElementIds: string[] = [];
|
||||
const changedLocationSubElementIds: string[] = [];
|
||||
const changedWorldIds: string[] = [];
|
||||
const changedWorldElementIds: string[] = [];
|
||||
const changedIncidentIds: string[] = [];
|
||||
const changedPlotPointIds: string[] = [];
|
||||
const changedIssueIds: string[] = [];
|
||||
const changedActSummaryIds: string[] = [];
|
||||
const changedSpellIds: string[] = [];
|
||||
const changedSpellTagIds: string[] = [];
|
||||
let guideLineChanged: boolean = false;
|
||||
let aiGuideLineChanged: boolean = false;
|
||||
let bookToolsChanged: boolean = false;
|
||||
|
||||
newerBook.chapters.forEach((newerChapter: SyncedChapter): void => {
|
||||
const olderChapter: SyncedChapter | undefined = olderBook.chapters.find((chapter: SyncedChapter): boolean => chapter.id === newerChapter.id);
|
||||
if (!olderChapter) {
|
||||
changedChapterIds.push(newerChapter.id);
|
||||
newerChapter.contents.forEach((content: SyncedChapterContent): void => { changedChapterContentIds.push(content.id); });
|
||||
if (newerChapter.info) { changedChapterInfoIds.push(newerChapter.info.id); }
|
||||
} else if (newerChapter.lastUpdate > olderChapter.lastUpdate) {
|
||||
changedChapterIds.push(newerChapter.id);
|
||||
} else {
|
||||
newerChapter.contents.forEach((newerContent: SyncedChapterContent): void => {
|
||||
const olderContent: SyncedChapterContent | undefined = olderChapter.contents.find((content: SyncedChapterContent): boolean => content.id === newerContent.id);
|
||||
if (!olderContent || newerContent.lastUpdate > olderContent.lastUpdate) { changedChapterContentIds.push(newerContent.id); }
|
||||
});
|
||||
if (newerChapter.info && olderChapter.info) {
|
||||
if (newerChapter.info.lastUpdate > olderChapter.info.lastUpdate) { changedChapterInfoIds.push(newerChapter.info.id); }
|
||||
} else if (newerChapter.info && !olderChapter.info) {
|
||||
changedChapterInfoIds.push(newerChapter.info.id);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
newerBook.characters.forEach((newerCharacter: SyncedCharacter): void => {
|
||||
const olderCharacter: SyncedCharacter | undefined = olderBook.characters.find((character: SyncedCharacter): boolean => character.id === newerCharacter.id);
|
||||
if (!olderCharacter) {
|
||||
changedCharacterIds.push(newerCharacter.id);
|
||||
newerCharacter.attributes.forEach((attribute: SyncedCharacterAttribute): void => { changedCharacterAttributeIds.push(attribute.id); });
|
||||
} else if (newerCharacter.lastUpdate > olderCharacter.lastUpdate) {
|
||||
changedCharacterIds.push(newerCharacter.id);
|
||||
} else {
|
||||
newerCharacter.attributes.forEach((newerAttribute: SyncedCharacterAttribute): void => {
|
||||
const olderAttribute: SyncedCharacterAttribute | undefined = olderCharacter.attributes.find((attribute: SyncedCharacterAttribute): boolean => attribute.id === newerAttribute.id);
|
||||
if (!olderAttribute || newerAttribute.lastUpdate > olderAttribute.lastUpdate) { changedCharacterAttributeIds.push(newerAttribute.id); }
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
newerBook.locations.forEach((newerLocation: SyncedLocation): void => {
|
||||
const olderLocation: SyncedLocation | undefined = olderBook.locations.find((location: SyncedLocation): boolean => location.id === newerLocation.id);
|
||||
if (!olderLocation) {
|
||||
changedLocationIds.push(newerLocation.id);
|
||||
newerLocation.elements.forEach((element: SyncedLocationElement): void => {
|
||||
changedLocationElementIds.push(element.id);
|
||||
element.subElements.forEach((subElement: SyncedLocationSubElement): void => { changedLocationSubElementIds.push(subElement.id); });
|
||||
});
|
||||
} else if (newerLocation.lastUpdate > olderLocation.lastUpdate) {
|
||||
changedLocationIds.push(newerLocation.id);
|
||||
} else {
|
||||
newerLocation.elements.forEach((newerElement: SyncedLocationElement): void => {
|
||||
const olderElement: SyncedLocationElement | undefined = olderLocation.elements.find((element: SyncedLocationElement): boolean => element.id === newerElement.id);
|
||||
if (!olderElement) {
|
||||
changedLocationElementIds.push(newerElement.id);
|
||||
newerElement.subElements.forEach((subElement: SyncedLocationSubElement): void => { changedLocationSubElementIds.push(subElement.id); });
|
||||
} else if (newerElement.lastUpdate > olderElement.lastUpdate) {
|
||||
changedLocationElementIds.push(newerElement.id);
|
||||
} else {
|
||||
newerElement.subElements.forEach((newerSubElement: SyncedLocationSubElement): void => {
|
||||
const olderSubElement: SyncedLocationSubElement | undefined = olderElement.subElements.find((subElement: SyncedLocationSubElement): boolean => subElement.id === newerSubElement.id);
|
||||
if (!olderSubElement || newerSubElement.lastUpdate > olderSubElement.lastUpdate) { changedLocationSubElementIds.push(newerSubElement.id); }
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
newerBook.worlds.forEach((newerWorld: SyncedWorld): void => {
|
||||
const olderWorld: SyncedWorld | undefined = olderBook.worlds.find((world: SyncedWorld): boolean => world.id === newerWorld.id);
|
||||
if (!olderWorld) {
|
||||
changedWorldIds.push(newerWorld.id);
|
||||
newerWorld.elements.forEach((element: SyncedWorldElement): void => { changedWorldElementIds.push(element.id); });
|
||||
} else if (newerWorld.lastUpdate > olderWorld.lastUpdate) {
|
||||
changedWorldIds.push(newerWorld.id);
|
||||
} else {
|
||||
newerWorld.elements.forEach((newerElement: SyncedWorldElement): void => {
|
||||
const olderElement: SyncedWorldElement | undefined = olderWorld.elements.find((element: SyncedWorldElement): boolean => element.id === newerElement.id);
|
||||
if (!olderElement || newerElement.lastUpdate > olderElement.lastUpdate) { changedWorldElementIds.push(newerElement.id); }
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
newerBook.incidents.forEach((newerIncident: SyncedIncident): void => {
|
||||
const olderIncident: SyncedIncident | undefined = olderBook.incidents.find((incident: SyncedIncident): boolean => incident.id === newerIncident.id);
|
||||
if (!olderIncident || newerIncident.lastUpdate > olderIncident.lastUpdate) { changedIncidentIds.push(newerIncident.id); }
|
||||
});
|
||||
|
||||
newerBook.plotPoints.forEach((newerPlotPoint: SyncedPlotPoint): void => {
|
||||
const olderPlotPoint: SyncedPlotPoint | undefined = olderBook.plotPoints.find((plotPoint: SyncedPlotPoint): boolean => plotPoint.id === newerPlotPoint.id);
|
||||
if (!olderPlotPoint || newerPlotPoint.lastUpdate > olderPlotPoint.lastUpdate) { changedPlotPointIds.push(newerPlotPoint.id); }
|
||||
});
|
||||
|
||||
newerBook.issues.forEach((newerIssue: SyncedIssue): void => {
|
||||
const olderIssue: SyncedIssue | undefined = olderBook.issues.find((issue: SyncedIssue): boolean => issue.id === newerIssue.id);
|
||||
if (!olderIssue || newerIssue.lastUpdate > olderIssue.lastUpdate) { changedIssueIds.push(newerIssue.id); }
|
||||
});
|
||||
|
||||
newerBook.actSummaries.forEach((newerActSummary: SyncedActSummary): void => {
|
||||
const olderActSummary: SyncedActSummary | undefined = olderBook.actSummaries.find((actSummary: SyncedActSummary): boolean => actSummary.id === newerActSummary.id);
|
||||
if (!olderActSummary || newerActSummary.lastUpdate > olderActSummary.lastUpdate) { changedActSummaryIds.push(newerActSummary.id); }
|
||||
});
|
||||
|
||||
if (newerBook.guideLine && olderBook.guideLine) {
|
||||
guideLineChanged = newerBook.guideLine.lastUpdate > olderBook.guideLine.lastUpdate;
|
||||
} else if (newerBook.guideLine && !olderBook.guideLine) {
|
||||
guideLineChanged = true;
|
||||
}
|
||||
|
||||
if (newerBook.aiGuideLine && olderBook.aiGuideLine) {
|
||||
aiGuideLineChanged = newerBook.aiGuideLine.lastUpdate > olderBook.aiGuideLine.lastUpdate;
|
||||
} else if (newerBook.aiGuideLine && !olderBook.aiGuideLine) {
|
||||
aiGuideLineChanged = true;
|
||||
}
|
||||
|
||||
if (newerBook.bookTools && olderBook.bookTools) {
|
||||
bookToolsChanged = newerBook.bookTools.lastUpdate > olderBook.bookTools.lastUpdate;
|
||||
} else if (newerBook.bookTools && !olderBook.bookTools) {
|
||||
bookToolsChanged = true;
|
||||
}
|
||||
|
||||
newerBook.spellTags.forEach((newerSpellTag: SyncedSpellTag): void => {
|
||||
const olderSpellTag: SyncedSpellTag | undefined = olderBook.spellTags.find((spellTag: SyncedSpellTag): boolean => spellTag.id === newerSpellTag.id);
|
||||
if (!olderSpellTag || newerSpellTag.lastUpdate > olderSpellTag.lastUpdate) { changedSpellTagIds.push(newerSpellTag.id); }
|
||||
});
|
||||
|
||||
newerBook.spells.forEach((newerSpell: SyncedSpell): void => {
|
||||
const olderSpell: SyncedSpell | undefined = olderBook.spells.find((spell: SyncedSpell): boolean => spell.id === newerSpell.id);
|
||||
if (!olderSpell || newerSpell.lastUpdate > olderSpell.lastUpdate) { changedSpellIds.push(newerSpell.id); }
|
||||
});
|
||||
|
||||
const hasChanges: boolean =
|
||||
changedChapterIds.length > 0 || changedChapterContentIds.length > 0 || changedChapterInfoIds.length > 0 ||
|
||||
changedCharacterIds.length > 0 || changedCharacterAttributeIds.length > 0 ||
|
||||
changedLocationIds.length > 0 || changedLocationElementIds.length > 0 || changedLocationSubElementIds.length > 0 ||
|
||||
changedWorldIds.length > 0 || changedWorldElementIds.length > 0 ||
|
||||
changedIncidentIds.length > 0 || changedPlotPointIds.length > 0 || changedIssueIds.length > 0 || changedActSummaryIds.length > 0 ||
|
||||
changedSpellIds.length > 0 || changedSpellTagIds.length > 0 ||
|
||||
guideLineChanged || aiGuideLineChanged || bookToolsChanged;
|
||||
|
||||
if (!hasChanges) { return null; }
|
||||
|
||||
return {
|
||||
id: newerBook.id,
|
||||
chapters: changedChapterIds,
|
||||
chapterContents: changedChapterContentIds,
|
||||
chapterInfos: changedChapterInfoIds,
|
||||
characters: changedCharacterIds,
|
||||
characterAttributes: changedCharacterAttributeIds,
|
||||
locations: changedLocationIds,
|
||||
locationElements: changedLocationElementIds,
|
||||
locationSubElements: changedLocationSubElementIds,
|
||||
worlds: changedWorldIds,
|
||||
worldElements: changedWorldElementIds,
|
||||
incidents: changedIncidentIds,
|
||||
plotPoints: changedPlotPointIds,
|
||||
issues: changedIssueIds,
|
||||
actSummaries: changedActSummaryIds,
|
||||
guideLine: guideLineChanged,
|
||||
aiGuideLine: aiGuideLineChanged,
|
||||
bookTools: bookToolsChanged,
|
||||
spells: changedSpellIds,
|
||||
spellTags: changedSpellTagIds
|
||||
};
|
||||
}
|
||||
280
lib/types/synced-series.ts
Normal file
280
lib/types/synced-series.ts
Normal file
@@ -0,0 +1,280 @@
|
||||
/**
|
||||
* Lightweight sync structures for series comparison.
|
||||
* These interfaces mirror the backend SyncedSeries* types from Book.ts
|
||||
* but are used in the frontend for sync status detection.
|
||||
*/
|
||||
|
||||
export interface SyncedSeriesBook {
|
||||
bookId: string;
|
||||
order: number;
|
||||
lastUpdate: number;
|
||||
}
|
||||
|
||||
export interface SyncedSeriesCharacterAttribute {
|
||||
id: string;
|
||||
name: string;
|
||||
lastUpdate: number;
|
||||
}
|
||||
|
||||
export interface SyncedSeriesCharacter {
|
||||
id: string;
|
||||
name: string;
|
||||
lastUpdate: number;
|
||||
attributes: SyncedSeriesCharacterAttribute[];
|
||||
}
|
||||
|
||||
export interface SyncedSeriesWorldElement {
|
||||
id: string;
|
||||
name: string;
|
||||
lastUpdate: number;
|
||||
}
|
||||
|
||||
export interface SyncedSeriesWorld {
|
||||
id: string;
|
||||
name: string;
|
||||
lastUpdate: number;
|
||||
elements: SyncedSeriesWorldElement[];
|
||||
}
|
||||
|
||||
export interface SyncedSeriesLocationSubElement {
|
||||
id: string;
|
||||
name: string;
|
||||
lastUpdate: number;
|
||||
}
|
||||
|
||||
export interface SyncedSeriesLocationElement {
|
||||
id: string;
|
||||
name: string;
|
||||
lastUpdate: number;
|
||||
subElements: SyncedSeriesLocationSubElement[];
|
||||
}
|
||||
|
||||
export interface SyncedSeriesLocation {
|
||||
id: string;
|
||||
name: string;
|
||||
lastUpdate: number;
|
||||
elements: SyncedSeriesLocationElement[];
|
||||
}
|
||||
|
||||
export interface SyncedSeriesSpell {
|
||||
id: string;
|
||||
name: string;
|
||||
lastUpdate: number;
|
||||
}
|
||||
|
||||
export interface SyncedSeriesSpellTag {
|
||||
id: string;
|
||||
name: string;
|
||||
lastUpdate: number;
|
||||
}
|
||||
|
||||
export interface SyncedSeries {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string | null;
|
||||
lastUpdate: number;
|
||||
books: SyncedSeriesBook[];
|
||||
characters: SyncedSeriesCharacter[];
|
||||
worlds: SyncedSeriesWorld[];
|
||||
locations: SyncedSeriesLocation[];
|
||||
spells: SyncedSeriesSpell[];
|
||||
spellTags: SyncedSeriesSpellTag[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Comparison result containing IDs of changed entities.
|
||||
* Used for partial synchronization - only changed entities are transferred.
|
||||
*/
|
||||
export interface SeriesSyncCompare {
|
||||
id: string;
|
||||
books: string[];
|
||||
characters: string[];
|
||||
characterAttributes: string[];
|
||||
worlds: string[];
|
||||
worldElements: string[];
|
||||
locations: string[];
|
||||
locationElements: string[];
|
||||
locationSubElements: string[];
|
||||
spells: string[];
|
||||
spellTags: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares two versions of a series to find changed entities.
|
||||
* The "newer" series is compared against the "older" one to find
|
||||
* entities that have been added or modified.
|
||||
*
|
||||
* @param newerSeries - The series version with potentially newer data
|
||||
* @param olderSeries - The series version to compare against
|
||||
* @returns SeriesSyncCompare with IDs of changed entities, or null if no changes
|
||||
*/
|
||||
export function compareSeriesSyncs(newerSeries: SyncedSeries, olderSeries: SyncedSeries): SeriesSyncCompare | null {
|
||||
const changedBookIds: string[] = [];
|
||||
const changedCharacterIds: string[] = [];
|
||||
const changedCharacterAttributeIds: string[] = [];
|
||||
const changedWorldIds: string[] = [];
|
||||
const changedWorldElementIds: string[] = [];
|
||||
const changedLocationIds: string[] = [];
|
||||
const changedLocationElementIds: string[] = [];
|
||||
const changedLocationSubElementIds: string[] = [];
|
||||
const changedSpellIds: string[] = [];
|
||||
const changedSpellTagIds: string[] = [];
|
||||
|
||||
// Compare books
|
||||
newerSeries.books.forEach((newerBook: SyncedSeriesBook): void => {
|
||||
const olderBook: SyncedSeriesBook | undefined = olderSeries.books.find(
|
||||
(book: SyncedSeriesBook): boolean => book.bookId === newerBook.bookId
|
||||
);
|
||||
if (!olderBook || newerBook.lastUpdate > olderBook.lastUpdate) {
|
||||
changedBookIds.push(newerBook.bookId);
|
||||
}
|
||||
});
|
||||
|
||||
// Compare characters and their attributes
|
||||
newerSeries.characters.forEach((newerCharacter: SyncedSeriesCharacter): void => {
|
||||
const olderCharacter: SyncedSeriesCharacter | undefined = olderSeries.characters.find(
|
||||
(character: SyncedSeriesCharacter): boolean => character.id === newerCharacter.id
|
||||
);
|
||||
|
||||
if (!olderCharacter) {
|
||||
changedCharacterIds.push(newerCharacter.id);
|
||||
newerCharacter.attributes.forEach((attr: SyncedSeriesCharacterAttribute): void => {
|
||||
changedCharacterAttributeIds.push(attr.id);
|
||||
});
|
||||
} else if (newerCharacter.lastUpdate > olderCharacter.lastUpdate) {
|
||||
changedCharacterIds.push(newerCharacter.id);
|
||||
} else {
|
||||
// Check attributes even if character hasn't changed
|
||||
newerCharacter.attributes.forEach((newerAttr: SyncedSeriesCharacterAttribute): void => {
|
||||
const olderAttr: SyncedSeriesCharacterAttribute | undefined = olderCharacter.attributes.find(
|
||||
(attr: SyncedSeriesCharacterAttribute): boolean => attr.id === newerAttr.id
|
||||
);
|
||||
if (!olderAttr || newerAttr.lastUpdate > olderAttr.lastUpdate) {
|
||||
changedCharacterAttributeIds.push(newerAttr.id);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Compare worlds and their elements
|
||||
newerSeries.worlds.forEach((newerWorld: SyncedSeriesWorld): void => {
|
||||
const olderWorld: SyncedSeriesWorld | undefined = olderSeries.worlds.find(
|
||||
(world: SyncedSeriesWorld): boolean => world.id === newerWorld.id
|
||||
);
|
||||
|
||||
if (!olderWorld) {
|
||||
changedWorldIds.push(newerWorld.id);
|
||||
newerWorld.elements.forEach((element: SyncedSeriesWorldElement): void => {
|
||||
changedWorldElementIds.push(element.id);
|
||||
});
|
||||
} else if (newerWorld.lastUpdate > olderWorld.lastUpdate) {
|
||||
changedWorldIds.push(newerWorld.id);
|
||||
} else {
|
||||
// Check elements even if world hasn't changed
|
||||
newerWorld.elements.forEach((newerElement: SyncedSeriesWorldElement): void => {
|
||||
const olderElement: SyncedSeriesWorldElement | undefined = olderWorld.elements.find(
|
||||
(element: SyncedSeriesWorldElement): boolean => element.id === newerElement.id
|
||||
);
|
||||
if (!olderElement || newerElement.lastUpdate > olderElement.lastUpdate) {
|
||||
changedWorldElementIds.push(newerElement.id);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Compare locations, their elements, and sub-elements
|
||||
newerSeries.locations.forEach((newerLocation: SyncedSeriesLocation): void => {
|
||||
const olderLocation: SyncedSeriesLocation | undefined = olderSeries.locations.find(
|
||||
(location: SyncedSeriesLocation): boolean => location.id === newerLocation.id
|
||||
);
|
||||
|
||||
if (!olderLocation) {
|
||||
changedLocationIds.push(newerLocation.id);
|
||||
newerLocation.elements.forEach((element: SyncedSeriesLocationElement): void => {
|
||||
changedLocationElementIds.push(element.id);
|
||||
element.subElements.forEach((subElement: SyncedSeriesLocationSubElement): void => {
|
||||
changedLocationSubElementIds.push(subElement.id);
|
||||
});
|
||||
});
|
||||
} else if (newerLocation.lastUpdate > olderLocation.lastUpdate) {
|
||||
changedLocationIds.push(newerLocation.id);
|
||||
} else {
|
||||
// Check elements
|
||||
newerLocation.elements.forEach((newerElement: SyncedSeriesLocationElement): void => {
|
||||
const olderElement: SyncedSeriesLocationElement | undefined = olderLocation.elements.find(
|
||||
(element: SyncedSeriesLocationElement): boolean => element.id === newerElement.id
|
||||
);
|
||||
|
||||
if (!olderElement) {
|
||||
changedLocationElementIds.push(newerElement.id);
|
||||
newerElement.subElements.forEach((subElement: SyncedSeriesLocationSubElement): void => {
|
||||
changedLocationSubElementIds.push(subElement.id);
|
||||
});
|
||||
} else if (newerElement.lastUpdate > olderElement.lastUpdate) {
|
||||
changedLocationElementIds.push(newerElement.id);
|
||||
} else {
|
||||
// Check sub-elements
|
||||
newerElement.subElements.forEach((newerSubElement: SyncedSeriesLocationSubElement): void => {
|
||||
const olderSubElement: SyncedSeriesLocationSubElement | undefined = olderElement.subElements.find(
|
||||
(subElement: SyncedSeriesLocationSubElement): boolean => subElement.id === newerSubElement.id
|
||||
);
|
||||
if (!olderSubElement || newerSubElement.lastUpdate > olderSubElement.lastUpdate) {
|
||||
changedLocationSubElementIds.push(newerSubElement.id);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Compare spells
|
||||
newerSeries.spells.forEach((newerSpell: SyncedSeriesSpell): void => {
|
||||
const olderSpell: SyncedSeriesSpell | undefined = olderSeries.spells.find(
|
||||
(spell: SyncedSeriesSpell): boolean => spell.id === newerSpell.id
|
||||
);
|
||||
if (!olderSpell || newerSpell.lastUpdate > olderSpell.lastUpdate) {
|
||||
changedSpellIds.push(newerSpell.id);
|
||||
}
|
||||
});
|
||||
|
||||
// Compare spell tags
|
||||
newerSeries.spellTags.forEach((newerTag: SyncedSeriesSpellTag): void => {
|
||||
const olderTag: SyncedSeriesSpellTag | undefined = olderSeries.spellTags.find(
|
||||
(tag: SyncedSeriesSpellTag): boolean => tag.id === newerTag.id
|
||||
);
|
||||
if (!olderTag || newerTag.lastUpdate > olderTag.lastUpdate) {
|
||||
changedSpellTagIds.push(newerTag.id);
|
||||
}
|
||||
});
|
||||
|
||||
// Check if there are any changes
|
||||
const hasChanges: boolean =
|
||||
changedBookIds.length > 0 ||
|
||||
changedCharacterIds.length > 0 ||
|
||||
changedCharacterAttributeIds.length > 0 ||
|
||||
changedWorldIds.length > 0 ||
|
||||
changedWorldElementIds.length > 0 ||
|
||||
changedLocationIds.length > 0 ||
|
||||
changedLocationElementIds.length > 0 ||
|
||||
changedLocationSubElementIds.length > 0 ||
|
||||
changedSpellIds.length > 0 ||
|
||||
changedSpellTagIds.length > 0;
|
||||
|
||||
if (!hasChanges) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
id: newerSeries.id,
|
||||
books: changedBookIds,
|
||||
characters: changedCharacterIds,
|
||||
characterAttributes: changedCharacterAttributeIds,
|
||||
worlds: changedWorldIds,
|
||||
worldElements: changedWorldElementIds,
|
||||
locations: changedLocationIds,
|
||||
locationElements: changedLocationElementIds,
|
||||
locationSubElements: changedLocationSubElementIds,
|
||||
spells: changedSpellIds,
|
||||
spellTags: changedSpellTagIds
|
||||
};
|
||||
}
|
||||
36
lib/types/user.ts
Normal file
36
lib/types/user.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
export interface Author {
|
||||
id: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface UserProps {
|
||||
id: string;
|
||||
username: string;
|
||||
authorName?: string;
|
||||
email?: string;
|
||||
accountVerified?: boolean;
|
||||
termsAccepted?: boolean;
|
||||
aiUsage: number;
|
||||
apiKeys: {
|
||||
gemini: boolean;
|
||||
openai: boolean;
|
||||
anthropic: boolean;
|
||||
};
|
||||
guideTour?: GuideTour[];
|
||||
subscription?: Subscription[];
|
||||
writingLang: number | null;
|
||||
writingLevel: number | null;
|
||||
ritePoints: number;
|
||||
creditsBalance: number;
|
||||
groupId: number;
|
||||
}
|
||||
|
||||
export interface GuideTour {
|
||||
[key: string]: boolean;
|
||||
}
|
||||
|
||||
export interface Subscription {
|
||||
subType: string;
|
||||
subTier: number;
|
||||
status: boolean;
|
||||
}
|
||||
57
lib/types/world.ts
Normal file
57
lib/types/world.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
import {LucideIcon} from 'lucide-react';
|
||||
|
||||
export interface ElementSection {
|
||||
title: string;
|
||||
section: WorldElementSection;
|
||||
icon: LucideIcon;
|
||||
}
|
||||
|
||||
export interface WorldElement {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
export type WorldElementSection =
|
||||
'laws'
|
||||
| 'biomes'
|
||||
| 'issues'
|
||||
| 'customs'
|
||||
| 'kingdoms'
|
||||
| 'climate'
|
||||
| 'resources'
|
||||
| 'wildlife'
|
||||
| 'arts'
|
||||
| 'ethnicGroups'
|
||||
| 'socialClasses'
|
||||
| 'importantCharacters';
|
||||
|
||||
export type WorldTextField = 'name' | 'history' | 'politics' | 'economy' | 'religion' | 'languages';
|
||||
|
||||
export interface WorldProps {
|
||||
id: string;
|
||||
name: string;
|
||||
history: string;
|
||||
politics: string;
|
||||
economy: string;
|
||||
religion: string;
|
||||
languages: string;
|
||||
laws: WorldElement[];
|
||||
biomes: WorldElement[];
|
||||
issues: WorldElement[];
|
||||
customs: WorldElement[];
|
||||
kingdoms: WorldElement[];
|
||||
climate: WorldElement[];
|
||||
resources: WorldElement[];
|
||||
wildlife: WorldElement[];
|
||||
arts: WorldElement[];
|
||||
ethnicGroups: WorldElement[];
|
||||
socialClasses: WorldElement[];
|
||||
importantCharacters: WorldElement[];
|
||||
seriesWorldId?: string | null;
|
||||
}
|
||||
|
||||
export interface WorldListResponse {
|
||||
worlds: WorldProps[];
|
||||
enabled: boolean;
|
||||
}
|
||||
Reference in New Issue
Block a user