Add comprehensive models and script for handling inline scripts and structured data

- Implement `remove-inline-scripts.js` to externalize Next.js inline scripts, enhancing CSP compliance.
- Add models for `Book`, `Character`, `Story`, `Editor`, `System`, and `BookSerie` with relevant properties and utilities.
- Include macOS entitlements plist for app development with advanced permissions.
- Add utility functions to handle script hashing, cookie management, and content formatting.
This commit is contained in:
natreex
2025-11-16 19:56:14 -05:00
parent a1fcca45cb
commit b4eafca3bc
41 changed files with 4142 additions and 0 deletions

135
lib/models/Book.ts Normal file
View File

@@ -0,0 +1,135 @@
import {Author} from './User';
import {ActChapter, ChapterProps} from "@/lib/models/Chapter";
import {SelectBoxProps} from "@/shared/interface";
export interface BookProps {
bookId: string;
type: string;
title: string;
author?: Author;
serie?: number;
subTitle?: string;
summary?: string;
publicationDate?: string;
desiredWordCount?: number;
totalWordCount?: number;
coverImage?: string;
chapters?: ChapterProps[];
}
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;
}
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;
dialogueType: number;
globalResume: string;
atmosphere: string;
verbeTense: number;
langue: number;
themes: string;
}
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[];
}
export const bookTypes: SelectBoxProps[] = [
{label: 'bookTypes.short', value: 'short'},
{label: 'bookTypes.novelette', value: 'novelette'},
{label: 'bookTypes.novella', value: 'long'},
{label: 'bookTypes.chapbook', value: 'chapbook'},
{label: 'bookTypes.novel', value: 'novel'},
]
export default class Book {
constructor() {
}
static booksToSelectBox(books: BookProps[]): SelectBoxProps[] {
return books.map((book: BookProps) => {
return {
label: book.title,
value: book.bookId,
}
});
}
static getBookTypeLabel(value: string): string {
switch (value) {
case 'short':
return 'bookTypes.short';
case 'novelette':
return 'bookTypes.novelette';
case 'long':
return 'bookTypes.novella';
case 'chapbook':
return 'bookTypes.chapbook';
case 'novel':
return 'bookTypes.novel';
default:
return 'bookTypes.novel';
}
}
}

3
lib/models/BookSerie.ts Executable file
View File

@@ -0,0 +1,3 @@
export default class BookSerie{
}

196
lib/models/Chapter.ts Normal file
View File

@@ -0,0 +1,196 @@
import {SelectBoxProps} from "@/shared/interface";
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 type TiptapNode = {
type: string;
content?: TiptapNode[];
text?: string;
attrs?: {
[key: string]: any;
};
};
export const chapterVersions: SelectBoxProps[] = [
{value: '1', label: 'chapterVersions.prompt'},
{value: '2', label: 'chapterVersions.draft'},
{value: '3', label: 'chapterVersions.refine'},
{value: '4', label: 'chapterVersions.review'},
{value: '5', label: 'chapterVersions.final'},
];
export default class Chapter {
public static getPageCount(text: string): number {
const charactersPerLine = 90;
const linesPerPage = 40;
const lines: string[] = text.split('\n');
let totalLines: number = 0;
lines.forEach((line: string) => {
const lineLength: number = line.length;
const estimatedLines: number = Math.ceil(lineLength / charactersPerLine);
totalLines += estimatedLines;
});
// Calcul du nombre de pages
return Math.ceil(totalLines / linesPerPage);
}
static convertTiptapToHTML(node: TiptapNode): string {
let html = '';
switch (node.type) {
case 'doc':
if (node.content) {
node.content.forEach(childNode => {
html += this.convertTiptapToHTML(childNode);
});
}
break;
case 'paragraph':
html += '<p>';
if (node.content) {
node.content.forEach(childNode => {
html += this.convertTiptapToHTML(childNode);
});
}
html += '</p>';
break;
case 'text':
let textContent = node.text || '';
// Apply attributes like bold, italic, etc.
if (node.attrs) {
if (node.attrs.bold) {
textContent = `<strong>${textContent}</strong>`;
}
if (node.attrs.italic) {
textContent = `<em>${textContent}</em>`;
}
if (node.attrs.underline) {
textContent = `<u>${textContent}</u>`;
}
if (node.attrs.strike) {
textContent = `<s>${textContent}</s>`;
}
if (node.attrs.link) {
textContent = `<a href="${node.attrs.link.href}">${textContent}</a>`;
}
}
html += textContent;
break;
case 'heading':
const level = node.attrs?.level || 1;
html += `<h${level}>`;
if (node.content) {
node.content.forEach(childNode => {
html += this.convertTiptapToHTML(childNode);
});
}
html += `</h${level}>`;
break;
case 'bulletList':
html += '<ul>';
if (node.content) {
node.content.forEach(childNode => {
html += this.convertTiptapToHTML(childNode);
});
}
html += '</ul>';
break;
case 'orderedList':
html += '<ol>';
if (node.content) {
node.content.forEach(childNode => {
html += this.convertTiptapToHTML(childNode);
});
}
html += '</ol>';
break;
case 'listItem':
html += '<li>';
if (node.content) {
node.content.forEach(childNode => {
html += this.convertTiptapToHTML(childNode);
});
}
html += '</li>';
break;
case 'blockquote':
html += '<blockquote>';
if (node.content) {
node.content.forEach(childNode => {
html += this.convertTiptapToHTML(childNode);
});
}
html += '</blockquote>';
break;
case 'codeBlock':
html += '<pre><code>';
if (node.content) {
node.content.forEach(childNode => {
html += this.convertTiptapToHTML(childNode);
});
}
html += '</code></pre>';
break;
default:
console.warn(`Unhandled node type: ${node.type}`);
if (node.content) {
node.content.forEach(childNode => {
html += this.convertTiptapToHTML(childNode);
});
}
break;
}
return html;
}
}

222
lib/models/Character.ts Executable file
View File

@@ -0,0 +1,222 @@
import {
faBrain,
faBullseye,
faExclamationTriangle,
faFire,
faRuler,
faShieldAlt,
faUsers,
faWrench,
} from '@fortawesome/free-solid-svg-icons';
import {SelectBoxProps} from "@/shared/interface";
type CharacterCategory = 'main' | 'secondary' | 'recurring' | 'none';
export const characterCategories: SelectBoxProps[] = [
{
value: 'none',
label: 'Sélectionner son rôle',
},
{
value: 'main',
label: 'Principal',
},
{
value: 'secondary',
label: 'Secondaire',
},
{
value: 'recurring',
label: 'Récurrent',
},
];
export const characterTitle: SelectBoxProps[] = [
{value: 'none', label: 'Aucun'},
{value: 'king', label: 'Roi'},
{value: 'queen', label: 'Reine'},
{value: 'emperor', label: 'Empereur'},
{value: 'empress', label: 'Impératrice'},
{value: 'prince', label: 'Prince'},
{value: 'princess', label: 'Princesse'},
{value: 'duke', label: 'Duc'},
{value: 'duchess', label: 'Duchesse'},
{value: 'count', label: 'Comte'},
{value: 'countess', label: 'Comtesse'},
{value: 'baron', label: 'Baron'},
{value: 'baroness', label: 'Baronne'},
{value: 'lord', label: 'Seigneur'},
{value: 'lady', label: 'Dame'},
{value: 'knight', label: 'Chevalier'},
{value: 'squire', label: 'Écuyer'},
{value: 'warrior', label: 'Guerrier'},
{value: 'general', label: 'Général'},
{value: 'commander', label: 'Commandant'},
{value: 'captain', label: 'Capitaine'},
{value: 'soldier', label: 'Soldat'},
{value: 'mercenary', label: 'Mercenaire'},
{value: 'assassin', label: 'Assassin'},
{value: 'thief', label: 'Voleur'},
{value: 'spy', label: 'Espion'},
{value: 'archmage', label: 'Archimage'},
{value: 'sorcerer', label: 'Sorcier'},
{value: 'witch', label: 'Sorcière'},
{value: 'warlock', label: 'Mage Noir'},
{value: 'druid', label: 'Druide'},
{value: 'priest', label: 'Prêtre'},
{value: 'prophet', label: 'Prophète'},
{value: 'oracle', label: 'Oracle'},
{value: 'seer', label: 'Voyant'},
{value: 'scholar', label: 'Érudit'},
{value: 'alchemist', label: 'Alchimiste'},
{value: 'healer', label: 'Guérisseur'},
{value: 'bard', label: 'Barde'},
{value: 'hermit', label: 'Ermite'},
{value: 'noble', label: 'Noble'},
{value: 'peasant', label: 'Paysan'},
{value: 'merchant', label: 'Marchand'},
{value: 'sailor', label: 'Marin'},
{value: 'pirate', label: 'Pirate'},
{value: 'slave', label: 'Esclave'},
{value: 'gladiator', label: 'Gladiateur'},
{value: 'champion', label: 'Champion'},
{value: 'outlaw', label: 'Hors-la-loi'},
{value: 'hunter', label: 'Chasseur'},
{value: 'beastmaster', label: 'Maître des Bêtes'},
{value: 'ranger', label: 'Rôdeur'},
{value: 'warden', label: 'Gardien'},
{value: 'sentinel', label: 'Sentinelle'},
{value: 'herald', label: 'Héraut'},
{value: 'messenger', label: 'Messager'},
{value: 'pilgrim', label: 'Pèlerin'},
{value: 'nomad', label: 'Nomade'},
{value: 'chieftain', label: 'Chef de Clan'},
{value: 'high-priest', label: 'Grand Prêtre'},
{value: 'inquisitor', label: 'Inquisiteur'},
{value: 'judge', label: 'Juge'},
{value: 'executioner', label: 'Bourreau'},
{value: 'warden', label: 'Gardien de Prison'},
{value: 'monk', label: 'Moine'},
{value: 'abbot', label: 'Abbé'},
{value: 'nun', label: 'Nonne'},
{value: 'diplomat', label: 'Diplomate'},
{value: 'ambassador', label: 'Ambassadeur'},
{value: 'scientist', label: 'Scientifique'},
{value: 'engineer', label: 'Ingénieur'},
{value: 'inventor', label: 'Inventeur'},
{value: 'architect', label: 'Architecte'},
{value: 'scribe', label: 'Scribe'},
{value: 'chronicler', label: 'Chroniqueur'},
{value: 'storyteller', label: 'Conteur'},
{value: 'actor', label: 'Acteur'},
{value: 'musician', label: 'Musicien'},
{value: 'artist', label: 'Artiste'},
{value: 'sculptor', label: 'Sculpteur'},
{value: 'orator', label: 'Orateur'},
{value: 'revolutionary', label: 'Révolutionnaire'},
{value: 'resistance-fighter', label: 'Résistant'},
{value: 'freedom-fighter', label: 'Combattant de la Liberté'},
{value: 'cult-leader', label: 'Chef de Secte'},
{value: 'warlock-lord', label: 'Seigneur Noir'},
{value: 'dark-prophet', label: 'Prophète du Chaos'},
{value: 'warlord', label: 'Seigneur de Guerre'},
{value: 'grandmaster', label: 'Grand Maître'},
{value: 'tactician', label: 'Tacticien'},
{value: 'archduke', label: 'Archiduc'},
{value: 'high-king', label: 'Haut Roi'},
{value: 'divine-champion', label: 'Champion Divin'},
];
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;
category: CharacterCategory;
title: string;
image: string;
physical: Attribute[];
psychological: Attribute[];
relations: Attribute[];
skills: Attribute[];
weaknesses: Attribute[];
strengths: Attribute[];
goals: Attribute[];
motivations: Attribute[];
role: string;
biography?: string;
history?: string;
}
export interface CharacterElement {
title: string;
section: keyof CharacterProps;
placeholder: string;
icon: any; // Replace `any` with an appropriate type if you have a specific icon type.
}
export const characterElementCategory: CharacterElement[] = [
{
title: 'Descriptions physiques',
section: 'physical',
placeholder: 'Nouvelle Description Physique',
icon: faRuler,
},
{
title: 'Descriptions psychologiques',
section: 'psychological',
placeholder: 'Nouvelle Description Psychologique',
icon: faBrain,
},
{
title: 'Relations',
section: 'relations',
placeholder: 'Nouveau Nom de Relation',
icon: faUsers,
},
{
title: 'Compétences',
section: 'skills',
placeholder: 'Nouvelle Compétence',
icon: faWrench,
},
{
title: 'Faiblesses',
section: 'weaknesses',
placeholder: 'Nouvelle Faiblesse',
icon: faExclamationTriangle,
},
{
title: 'Forces',
section: 'strengths',
placeholder: 'Nouvelle Force',
icon: faShieldAlt,
},
{
title: 'Objectifs',
section: 'goals',
placeholder: 'Nouvel Objectif',
icon: faBullseye,
},
{
title: 'Motivations',
section: 'motivations',
placeholder: 'Nouvelle Motivation',
icon: faFire,
},
];

20
lib/models/Editor.ts Executable file
View File

@@ -0,0 +1,20 @@
import {IconDefinition} from "@fortawesome/free-solid-svg-icons";
export interface PanelComponent {
id: number,
title: string,
badge: string,
description: string,
icon: IconDefinition,
action?: () => void,
}
export default class Editor {
public static convertToHtml(text: string): string {
return text
.split(/\n\s*\n/)
.map((paragraph: string): string => `<p>${paragraph.trim()}</p>`)
.join('');
}
}

128
lib/models/QuillSense.ts Normal file
View File

@@ -0,0 +1,128 @@
import User, {Subscription} from "@/lib/models/User";
import {SessionProps} from "@/lib/models/Session";
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;
}
export interface AIGeneratedText {
totalTokens: number;
totalPrice: number;
response: string;
}
export interface AIResponseWithCredits<T> {
useYourKey: boolean;
totalPrice: number;
data: T;
}
export interface AIDictionary extends AIResponseWithCredits<DictionaryAIResponse> {}
export interface AIGeneratedTextData {
totalCost: number;
response: string;
}
export interface AIGeneratedText extends AIResponseWithCredits<AIGeneratedTextData> {}
export interface AIInspire extends AIResponseWithCredits<InspireAIResponse> {}
export interface AISynonyms extends AIResponseWithCredits<SynonymsAIResponse> {}
export interface AIVerbConjugation extends AIResponseWithCredits<unknown> {}
export interface AISimpleText extends AIResponseWithCredits<string> {}
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 InspiredAIResponse {
ideas: InspirationAIIdea[];
}
export interface ConversationProps {
id: string;
mode: string;
title: string;
startDate: string;
status: number;
}
export default class QuillSense {
static getSubLevel(session: SessionProps): number {
const currentSub: Subscription | null = User.getCurrentSubscription(session?.user, 'quill-sense');
if (!currentSub) return 0;
switch (currentSub?.subTier) {
case 1:
return 1;
case 2:
return 2;
case 3:
return 3;
default:
return 0;
}
};
static isBringYourKeys(session: SessionProps): boolean {
if (!session?.user) return false;
const currentSub: Subscription | null = User.getCurrentSubscription(session?.user, 'use-your-keys');
return currentSub?.status || session.user.groupId <= 4;
}
static isGeminiEnabled(session: SessionProps): boolean {
return session.user?.apiKeys.gemini || false;
}
static isAnthropicEnabled(session: SessionProps): boolean {
return session.user?.apiKeys.anthropic || false;
}
static isOpenAIEnabled(session: SessionProps): boolean {
return session.user?.apiKeys.openai || false;
}
}

19
lib/models/Session.ts Executable file
View File

@@ -0,0 +1,19 @@
import {UserProps} from "@/lib/models/User";
export interface SessionProps {
isConnected: boolean,
accessToken: string;
user: UserProps | null;
}
export interface LoginResponse {
valid: boolean,
message?: string,
token?: string,
userid?: string
}
export default class Session {
constructor() {
}
}

1116
lib/models/Story.ts Executable file

File diff suppressed because it is too large Load Diff

209
lib/models/System.ts Normal file
View File

@@ -0,0 +1,209 @@
import axios, {AxiosResponse} from "axios";
import {configs} from "@/lib/configs";
export default class System{
static verifyInput(input: string): boolean {
let pattern: RegExp = new RegExp('(<.*?>)|(&.*?;)|({.*?})', 'gmi');
return pattern.test(input);
}
public static formatHTMLContent(htmlContent: string): string {
return htmlContent
.replace(/<h1>/g, '<h1 style="color: #FFFFFF; text-indent: 5px; font-size: 28px; font-weight: bold; text-align: left; margin-vertical: 10px;">')
.replace(/<p>/g, '<p style="color: #d0d0d0; text-indent: 30px; font-size: 16px; line-height: 22px; margin-vertical: 5px;">')
.replace(/<blockquote>/g, '<blockquote style="border-left-width: 4px; border-left-color: #ccc; padding-left: 10px; font-style: italic; color: #555;">');
}
public static textContentToHtml(content: string): string {
const paragraphs: string[] = content
.split(/\n+/)
.map((paragraph: string) => paragraph.trim())
.filter((paragraph: string) => paragraph.length > 0);
return paragraphs
.map((paragraph: string) => `<p>${paragraph}</p>`)
.join('');
}
public static async authGetQueryToServer<T>(url: string, auth: string, lang: string = "fr", params: Record<string, any> = {}): Promise<T> {
try {
const response: AxiosResponse<T> = await axios({
method: 'GET',
headers: {
'Authorization': `Bearer ${auth}`
},
params: {
lang: lang,
plateforme: 'web',
...params
},
url: configs.apiUrl + url,
})
return response.data;
} catch (e: unknown) {
if (axios.isAxiosError(e)) {
const serverMessage: string = e.response?.data?.message || e.response?.data || e.message;
throw new Error(serverMessage as string);
} else if (e instanceof Error) {
throw new Error(e.message);
} else {
throw new Error('An unexpected error occurred');
}
}
}
public static setCookie(name: string, value: string, days: number): void {
const date = new Date();
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
const expires = `expires=${date.toUTCString()}`;
let domain: string = '';
if (!/localhost|127\.0\.0\.1/.test(window.location.hostname)) {
domain = `domain=${window.location.hostname};`;
}
const secure = 'Secure;';
const sameSite = 'SameSite=Strict;';
document.cookie = `${name}=${value}; ${expires}; ${domain} path=/; ${secure} ${sameSite}`;
}
public static async authPutToServer<T>(url: string, data: {}, auth: string, lang: string = "fr"): Promise<T> {
try {
const response: AxiosResponse<T> = await axios({
method: 'PUT',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${auth}`
},
params: {
lang: lang,
plateforme: 'web',
},
url: configs.apiUrl + url,
data: data
})
return response.data;
} catch (e: unknown) {
if (axios.isAxiosError(e)) {
const serverMessage: string = e.response?.data?.message || e.response?.data || e.message;
throw new Error(serverMessage as string);
} else if (e instanceof Error) {
throw new Error(e.message);
} else {
throw new Error('An unexpected error occurred');
}
}
}
public static async postToServer<T>(url: string, data: {}, lang: string = "fr"): Promise<T> {
try {
const response: AxiosResponse<T> = await axios({
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
url: configs.apiUrl + url,
params: {
lang: lang,
plateforme: 'web',
},
data: data
})
return response.data;
} catch (e: unknown) {
if (axios.isAxiosError(e)) {
const serverMessage: string = e.response?.data?.message || e.response?.data || e.message;
throw new Error(serverMessage as string);
} else if (e instanceof Error) {
throw new Error(e.message);
} else {
throw new Error('An unexpected error occurred');
}
}
}
public static async authPostToServer<T>(url: string, data: {}, auth: string, lang: string = "fr"): Promise<T> {
try {
const response: AxiosResponse<T> = await axios({
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${auth}`
},
url: configs.apiUrl + url,
params: {
lang: lang,
plateforme: 'web',
},
data: data
})
return response.data;
} catch (e: unknown) {
if (axios.isAxiosError(e)) {
const serverMessage: string = e.response?.data?.message || e.response?.data || e.message;
throw new Error(serverMessage as string);
} else if (e instanceof Error) {
throw new Error(e.message);
} else {
throw new Error('An unexpected error occurred');
}
}
}
static htmlToText(html: string) {
return html
.replace(/<br\s*\/?>/gi, '\n')
.replace(/<\/?(p|h[1-6]|div)(\s+[^>]*)?>/gi, '\n')
.replace(/<\/?[^>]+(>|$)/g, '')
.replace(/(\n\s*){2,}/g, '\n\n')
.replace(/^\s+|\s+$|(?<=\s)\s+/g, '')
.trim();
}
public static getCookie(name: string): string | null {
const nameEQ = `${name}=`;
const allCookies: string[] = document.cookie.split(';');
for (let i: number = 0; i < allCookies.length; i++) {
let cookie: string = allCookies[i];
while (cookie.charAt(0) === ' ') cookie = cookie.substring(1, cookie.length);
if (cookie.indexOf(nameEQ) === 0) return cookie.substring(nameEQ.length, cookie.length);
}
return null;
}
public static removeCookie(name: string): void {
let domain: string = '';
if (!/localhost|127\.0\.0\.1/.test(window.location.hostname)) {
domain = `domain=${window.location.hostname};`;
}
const secure = 'Secure;';
const sameSite = 'SameSite=Strict;';
document.cookie = `${name}=; expires=Thu, 01 Jan 1970 00:00:00 GMT; ${domain} path=/; ${secure} ${sameSite}`;
}
public static async authDeleteToServer<T>(url: string, data: {}, auth: string, lang: string = "fr"): Promise<T> {
try {
const response: AxiosResponse<T> = await axios({
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${auth}`
},
url: configs.apiUrl + url,
params: {
lang: lang,
plateforme: 'web',
},
data: data
})
return response.data;
} catch (e: unknown) {
if (axios.isAxiosError(e)) {
const serverMessage: string = e.response?.data?.message || e.response?.data || e.message;
throw new Error(serverMessage as string);
} else if (e instanceof Error) {
throw new Error(e.message);
} else {
throw new Error('An unexpected error occurred');
}
}
}
}

92
lib/models/User.ts Normal file
View File

@@ -0,0 +1,92 @@
import {SelectBoxProps} from "@/shared/interface";
import {BookProps} from "@/lib/models/Book";
import {SessionProps} from "@/lib/models/Session";
export interface Author {
id: string;
name: string;
}
export interface UserProps {
id: string;
name: string;
lastName: string;
username: string;
authorName?: string;
email?: string;
accountVerified?: boolean;
termsAccepted?: boolean;
aiUsage: number,
apiKeys: {
gemini: boolean
openai: boolean,
anthropic: boolean,
},
books?: BookProps[];
guideTour?: GuideTour[];
subscription?: Subscription[];
writingLang: number;
writingLevel: number;
ritePoints: number;
creditsBalance:number;
groupId: number;
}
export interface GuideTour {
[key: string]: boolean;
}
export interface Subscription {
subType: string;
subTier: number;
status: boolean;
}
export const writingLevel: SelectBoxProps[] = [
{value: '0', label: 'Sélectionner un niveau d\'écriture'},
{value: '1', label: 'Je suis débutant'},
{value: '2', label: 'Je suis intermédiaire'},
{value: '3', label: 'Je suis avancé'},
];
export default class User {
static getCurrentSubscription(user: UserProps | null, type: "quill-sense" | "use-your-keys"): Subscription | null {
if (!user || !user.subscription || user.subscription.length === 0) {
return null;
}
return user.subscription.find((sub: Subscription): boolean => {
return sub.subType === type && sub.status;
}) || null;
}
static getWritingLevel(level: number): string {
switch (level) {
case 1:
return 'Débutant';
case 2:
return 'Intermédiaire';
case 3:
return 'Avancé';
default:
return 'Débutant';
}
}
static guideTourDone(guide: GuideTour[], tour: string): boolean {
if (!guide || !tour) return false;
return guide.find((guide: GuideTour): boolean => guide[tour]) === undefined;
}
static setNewGuideTour(session: SessionProps, tour: string): SessionProps {
const newGuideTour: { [key: string]: boolean }[] = [
...(session?.user?.guideTour ?? []),
{[tour]: true}
];
return {
...session,
user: {
...session?.user as UserProps,
guideTour: newGuideTour
}
}
}
}

111
lib/models/World.ts Executable file
View File

@@ -0,0 +1,111 @@
import {
faCrown,
faExclamationTriangle,
faFlag,
faGavel,
faIndustry,
faLeaf,
faMountain,
faMusic,
faPeopleArrows,
faSnowflake,
faUserCog,
faUserFriends,
} from '@fortawesome/free-solid-svg-icons';
import {ElementSection} from "@/components/book/settings/world/WorldSetting";
export interface WorldElement {
id: string;
name: string;
description: string;
}
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[];
}
export const elementSections: ElementSection[] = [
{
title: 'Lois',
section: 'laws',
icon: faGavel,
},
{
title: 'Biomes',
section: 'biomes',
icon: faMountain,
},
{
title: 'Problèmes',
section: 'issues',
icon: faExclamationTriangle,
},
{
title: 'Coutumes',
section: 'customs',
icon: faPeopleArrows,
},
{
title: 'Royaumes',
section: 'kingdoms',
icon: faFlag,
},
{
title: 'Climat',
section: 'climate',
icon: faSnowflake,
},
{
title: 'Ressources',
section: 'resources',
icon: faIndustry,
},
{
title: 'Faune',
section: 'wildlife',
icon: faLeaf,
},
{
title: 'Arts',
section: 'arts',
icon: faMusic,
},
{
title: 'Groupes ethniques',
section: 'ethnicGroups',
icon: faUserFriends,
},
{
title: 'Classes sociales',
section: 'socialClasses',
icon: faUserCog,
},
{
title: 'Personnages importants',
section: 'importantCharacters',
icon: faCrown,
},
];
export default class World {
constructor() {
}
}