Introduce series management functionality and repository updates

- Added `series-world.repo.ts` to handle database operations related to series worlds and their elements.
- Implemented `series-sync.repo.ts` for managing synchronization between books and series.
- Expanded `spell.ipc.ts` data models to include `seriesSpellId` for spell synchronization.
- Refactored `insertSpellTag` method in `spelltag.repo.ts` for improved error handling and logic clarity.
This commit is contained in:
natreex
2026-01-26 19:57:56 -05:00
parent 2359638cb0
commit cec5830360
35 changed files with 5483 additions and 203 deletions

View File

@@ -10,6 +10,7 @@ export interface LocationQueryResult extends Record<string, SQLiteValue> {
sub_element_id: string;
sub_elem_name: string;
sub_elem_description: string;
series_location_id: string | null;
}
export interface LocationElementQueryResult extends Record<string, SQLiteValue> {
@@ -89,15 +90,7 @@ export default class LocationRepo {
static getLocation(userId: string, bookId: string, lang: 'fr' | 'en' = 'fr'): LocationQueryResult[] {
try {
const db: Database = System.getDb();
const query: string = `
SELECT loc_id, loc_name, element.element_id AS element_id, element.element_name,
element.element_description, sub_elem.sub_element_id AS sub_element_id,
sub_elem.sub_elem_name, sub_elem.sub_elem_description
FROM book_location AS location
LEFT JOIN location_element AS element ON location.loc_id = element.location
LEFT JOIN location_sub_element AS sub_elem ON element.element_id = sub_elem.element_id
WHERE location.user_id = ? AND location.book_id = ?
`;
const query: string = 'SELECT loc_id, loc_name, element.element_id AS element_id, element.element_name, element.element_description, sub_elem.sub_element_id AS sub_element_id, sub_elem.sub_elem_name, sub_elem.sub_elem_description, location.series_location_id FROM book_location AS location LEFT JOIN location_element AS element ON location.loc_id = element.location LEFT JOIN location_sub_element AS sub_elem ON element.element_id = sub_elem.element_id WHERE location.user_id = ? AND location.book_id = ?';
const params: SQLiteValue[] = [userId, bookId];
const locations: LocationQueryResult[] = db.all(query, params) as LocationQueryResult[];
return locations;
@@ -122,19 +115,17 @@ export default class LocationRepo {
* @param lang - The language for error messages ('fr' or 'en')
* @returns The location ID if insertion was successful
*/
static insertLocation(userId: string, locationId: string, bookId: string, encryptedName: string, originalName: string, lang: 'fr' | 'en' = 'fr'): string {
static insertLocation(userId: string, locationId: string, bookId: string, encryptedName: string, originalName: string, lang: 'fr' | 'en' = 'fr', seriesLocationId: string | null = null): string {
let insertResult: RunResult;
try {
const db: Database = System.getDb();
const query: string = `
INSERT INTO book_location (loc_id, book_id, user_id, loc_name, loc_original_name, last_update)
VALUES (?, ?, ?, ?, ?, ?)
`;
const params: SQLiteValue[] = [locationId, bookId, userId, encryptedName, originalName, System.timeStampInSeconds()];
const insertResult: RunResult = db.run(query, params);
if (!insertResult || insertResult.changes === 0) {
throw new Error(lang === 'fr' ? `Une erreur s'est produite lors de l'ajout de la section d'emplacement.` : `Error adding location section.`);
}
return locationId;
const query: string = seriesLocationId
? 'INSERT INTO book_location (loc_id, book_id, user_id, loc_name, loc_original_name, series_location_id, last_update) VALUES (?, ?, ?, ?, ?, ?, ?)'
: 'INSERT INTO book_location (loc_id, book_id, user_id, loc_name, loc_original_name, last_update) VALUES (?, ?, ?, ?, ?, ?)';
const params: SQLiteValue[] = seriesLocationId
? [locationId, bookId, userId, encryptedName, originalName, seriesLocationId, System.timeStampInSeconds()]
: [locationId, bookId, userId, encryptedName, originalName, System.timeStampInSeconds()];
insertResult = db.run(query, params);
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
@@ -144,6 +135,10 @@ export default class LocationRepo {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
if (!insertResult || insertResult.changes === 0) {
throw new Error(lang === 'fr' ? `Une erreur s'est produite lors de l'ajout de la section d'emplacement.` : `Error adding location section.`);
}
return locationId;
}
/**
@@ -157,18 +152,12 @@ export default class LocationRepo {
* @returns The element ID if insertion was successful
*/
static insertLocationElement(userId: string, elementId: string, locationId: string, encryptedName: string, originalName: string, lang: 'fr' | 'en' = 'fr'): string {
let insertResult: RunResult;
try {
const db: Database = System.getDb();
const query: string = `
INSERT INTO location_element (element_id, location, user_id, element_name, original_name, element_description, last_update)
VALUES (?, ?, ?, ?, ?, ?, ?)
`;
const query: string = 'INSERT INTO location_element (element_id, location, user_id, element_name, original_name, element_description, last_update) VALUES (?, ?, ?, ?, ?, ?, ?)';
const params: SQLiteValue[] = [elementId, locationId, userId, encryptedName, originalName, '', System.timeStampInSeconds()];
const insertResult: RunResult = db.run(query, params);
if (!insertResult || insertResult.changes === 0) {
throw new Error(lang === 'fr' ? `Une erreur s'est produite lors de l'ajout de l'élément d'emplacement.` : `Error adding location element.`);
}
return elementId;
insertResult = db.run(query, params);
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
@@ -178,6 +167,10 @@ export default class LocationRepo {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
if (!insertResult || insertResult.changes === 0) {
throw new Error(lang === 'fr' ? `Une erreur s'est produite lors de l'ajout de l'élément d'emplacement.` : `Error adding location element.`);
}
return elementId;
}
/**
@@ -191,18 +184,12 @@ export default class LocationRepo {
* @returns The sub-element ID if insertion was successful
*/
static insertLocationSubElement(userId: string, subElementId: string, elementId: string, encryptedName: string, originalName: string, lang: 'fr' | 'en' = 'fr'): string {
let insertResult: RunResult;
try {
const db: Database = System.getDb();
const query: string = `
INSERT INTO location_sub_element (sub_element_id, element_id, user_id, sub_elem_name, original_name, sub_elem_description, last_update)
VALUES (?, ?, ?, ?, ?, ?, ?)
`;
const query: string = 'INSERT INTO location_sub_element (sub_element_id, element_id, user_id, sub_elem_name, original_name, sub_elem_description, last_update) VALUES (?, ?, ?, ?, ?, ?, ?)';
const params: SQLiteValue[] = [subElementId, elementId, userId, encryptedName, originalName, '', System.timeStampInSeconds()];
const insertResult: RunResult = db.run(query, params);
if (!insertResult || insertResult.changes === 0) {
throw new Error(lang === 'fr' ? `Une erreur s'est produite lors de l'ajout du sous-élément d'emplacement.` : `Error adding location sub-element.`);
}
return subElementId;
insertResult = db.run(query, params);
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
@@ -212,6 +199,10 @@ export default class LocationRepo {
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
if (!insertResult || insertResult.changes === 0) {
throw new Error(lang === 'fr' ? `Une erreur s'est produite lors de l'ajout du sous-élément d'emplacement.` : `Error adding location sub-element.`);
}
return subElementId;
}
/**
@@ -864,4 +855,46 @@ export default class LocationRepo {
}
}
}
/**
* Updates a location section with optional name change and series link.
* @param userId - The user's unique identifier
* @param sectionId - The section's unique identifier
* @param encryptedName - The new encrypted name (optional)
* @param originalName - The new original name (optional)
* @param seriesLocationId - The series location ID to link (optional, null to unlink)
* @param lang - The language for error messages ('fr' or 'en')
* @returns True if the update was successful
*/
static updateSectionWithSeriesLink(userId: string, sectionId: string, encryptedName: string | null, originalName: string | null, seriesLocationId: string | null, lang: 'fr' | 'en' = 'fr'): boolean {
try {
const db: Database = System.getDb();
const setClauses: string[] = ['last_update=' + System.timeStampInSeconds()];
const params: SQLiteValue[] = [];
if (encryptedName !== null && originalName !== null) {
setClauses.push('loc_name=?', 'loc_original_name=?');
params.push(encryptedName, originalName);
}
if (seriesLocationId !== undefined) {
setClauses.push('series_location_id=?');
params.push(seriesLocationId);
}
params.push(sectionId, userId);
const query: string = 'UPDATE book_location SET ' + setClauses.join(', ') + ' WHERE loc_id=? AND user_id=?';
const updateResult: RunResult = db.run(query, params);
return updateResult.changes > 0;
} catch (error: unknown) {
if (error instanceof Error) {
console.error(`DB Error: ${error.message}`);
throw new Error(lang === 'fr' ? `Impossible de mettre à jour la section d'emplacement.` : `Unable to update location section.`);
} else {
console.error("An unknown error occurred.");
throw new Error(lang === 'fr' ? "Une erreur inconnue s'est produite." : "An unknown error occurred.");
}
}
}
}