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:
natreex
2026-03-22 22:37:31 -04:00
parent e8aaef108b
commit 64ed90d993
229 changed files with 15091 additions and 21289 deletions

View File

@@ -1,36 +1,26 @@
'use client'
import React, {ChangeEvent, Dispatch, SetStateAction, useContext, useState} from "react";
import {AlertContext, AlertContextProps} from "@/context/AlertContext";
import {apiPost} from "@/lib/api/client";
import {isDesktop} from '@/lib/configs';
import * as tauri from '@/lib/tauri';
import {ChangeEvent, Dispatch, RefObject, SetStateAction, useContext, useEffect, useRef, useState} from "react";
import {AlertContext} from "@/context/AlertContext";
import System from "@/lib/models/System";
import {SessionContext} from "@/context/SessionContext";
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
import {
faBook,
faBookOpen,
faCalendarAlt,
faFileWord,
faInfo,
faPencilAlt,
faX
} from "@fortawesome/free-solid-svg-icons";
import {SelectBoxProps} from "@/shared/interface";
import {BookProps, bookTypes} from "@/lib/models/Book";
import OfflineContext, {OfflineContextType} from '@/context/OfflineContext';
import {SessionContext, SessionContextProps} from "@/context/SessionContext";
import {Book, BookOpen, Calendar, FileText, Info, Pencil} from "lucide-react";
import SelectBox, {SelectBoxProps} from "@/components/form/SelectBox";
import {bookTypes} from "@/lib/constants/book";
import InputField from "@/components/form/InputField";
import TextInput from "@/components/form/TextInput";
import SelectBox from "@/components/form/SelectBox";
import DatePicker from "@/components/form/DatePicker";
import NumberInput from "@/components/form/NumberInput";
import TexteAreaInput from "@/components/form/TexteAreaInput";
import CancelButton from "@/components/form/CancelButton";
import SubmitButtonWLoading from "@/components/form/SubmitButtonWLoading";
import TextAreaInput from "@/components/form/TextAreaInput";
import Button from "@/components/ui/Button";
import Modal from "@/components/ui/Modal";
import GuideTour, {GuideStep} from "@/components/GuideTour";
import {UserProps} from "@/lib/models/User";
import {useTranslations} from "next-intl";
import {useTranslations} from '@/lib/i18n';
import {LangContext, LangContextProps} from "@/context/LangContext";
import OfflineContext, {OfflineContextType} from "@/context/OfflineContext";
import {BooksSyncContext, BooksSyncContextProps} from "@/context/BooksSyncContext";
import {SyncedBook} from "@/lib/models/SyncedBook";
import {SyncedBook} from "@/lib/types/synced-book";
interface MinMax {
min: number;
@@ -39,13 +29,11 @@ interface MinMax {
export default function AddNewBookForm({setCloseForm}: { setCloseForm: Dispatch<SetStateAction<boolean>> }) {
const t = useTranslations();
const {lang} = useContext<LangContextProps>(LangContext);
const {session} = useContext(SessionContext);
const {errorMessage} = useContext(AlertContext);
const {setServerOnlyBooks, setLocalOnlyBooks} = useContext<BooksSyncContextProps>(BooksSyncContext)
const {isCurrentlyOffline} = useContext<OfflineContextType>(OfflineContext);
const modalRef: RefObject<HTMLDivElement | null> = useRef<HTMLDivElement>(null);
const {lang}: LangContextProps = useContext<LangContextProps>(LangContext);
const {session, setSession}: SessionContextProps = useContext<SessionContextProps>(SessionContext);
const {errorMessage}: AlertContextProps = useContext<AlertContextProps>(AlertContext);
const {setServerSyncedBooks}: BooksSyncContextProps = useContext<BooksSyncContextProps>(BooksSyncContext)
const {isCurrentlyOffline}: OfflineContextType = useContext<OfflineContextType>(OfflineContext);
const [title, setTitle] = useState<string>('');
const [subtitle, setSubtitle] = useState<string>('');
const [summary, setSummary] = useState<string>('');
@@ -66,27 +54,27 @@ export default function AddNewBookForm({setCloseForm}: { setCloseForm: Dispatch<
content: (
<div className="space-y-4 max-h-96 overflow-y-auto custom-scrollbar">
<div className="space-y-3">
<div className="border-l-4 border-primary pl-4 bg-secondary/10 p-3 rounded-r-xl">
<div className="border-l-4 border-primary pl-4 bg-secondary p-3 rounded-r-xl">
<h4 className="font-semibold text-lg text-text-primary mb-1">{t("addNewBookForm.bookTypeHint.nouvelle.title")}</h4>
<p className="text-sm text-muted mb-2">{t("addNewBookForm.bookTypeHint.nouvelle.range")}</p>
<p className="text-sm text-text-secondary">{t("addNewBookForm.bookTypeHint.nouvelle.description")}</p>
</div>
<div className="border-l-4 border-primary pl-4 bg-secondary/10 p-3 rounded-r-xl">
<div className="border-l-4 border-primary pl-4 bg-secondary p-3 rounded-r-xl">
<h4 className="font-semibold text-lg text-text-primary mb-1">{t("addNewBookForm.bookTypeHint.novelette.title")}</h4>
<p className="text-sm text-muted mb-2">{t("addNewBookForm.bookTypeHint.novelette.range")}</p>
<p className="text-sm text-text-secondary">{t("addNewBookForm.bookTypeHint.novelette.description")}</p>
</div>
<div className="border-l-4 border-primary pl-4 bg-secondary/10 p-3 rounded-r-xl">
<div className="border-l-4 border-primary pl-4 bg-secondary p-3 rounded-r-xl">
<h4 className="font-semibold text-lg text-text-primary mb-1">{t("addNewBookForm.bookTypeHint.novella.title")}</h4>
<p className="text-sm text-muted mb-2">{t("addNewBookForm.bookTypeHint.novella.range")}</p>
<p className="text-sm text-text-secondary">{t("addNewBookForm.bookTypeHint.novella.description")}</p>
</div>
<div className="border-l-4 border-primary pl-4 bg-secondary/10 p-3 rounded-r-xl">
<div className="border-l-4 border-primary pl-4 bg-secondary p-3 rounded-r-xl">
<h4 className="font-semibold text-lg text-text-primary mb-1">{t("addNewBookForm.bookTypeHint.chapbook.title")}</h4>
<p className="text-sm text-muted mb-2">{t("addNewBookForm.bookTypeHint.chapbook.range")}</p>
<p className="text-sm text-text-secondary">{t("addNewBookForm.bookTypeHint.chapbook.description")}</p>
</div>
<div className="border-l-4 border-primary pl-4 bg-secondary/10 p-3 rounded-r-xl">
<div className="border-l-4 border-primary pl-4 bg-secondary p-3 rounded-r-xl">
<h4 className="font-semibold text-lg text-text-primary mb-1">{t("addNewBookForm.bookTypeHint.roman.title")}</h4>
<p className="text-sm text-muted mb-2">{t("addNewBookForm.bookTypeHint.roman.range")}</p>
<p className="text-sm text-text-secondary">{t("addNewBookForm.bookTypeHint.roman.description")}</p>
@@ -101,13 +89,6 @@ export default function AddNewBookForm({setCloseForm}: { setCloseForm: Dispatch<
),
}]
useEffect((): () => void => {
document.body.style.overflow = 'hidden';
return (): void => {
document.body.style.overflow = 'auto';
};
}, []);
async function handleAddBook(): Promise<void> {
if (!title) {
errorMessage(t('addNewBookForm.error.titleMissing'));
@@ -128,85 +109,60 @@ export default function AddNewBookForm({setCloseForm}: { setCloseForm: Dispatch<
}
setIsAddingBook(true);
try {
const bookData = {
title,
subTitle: subtitle,
type: selectedBookType,
summary,
serie: 0,
publicationDate,
desiredWordCount: wordCount
};
let bookId: string;
if (isCurrentlyOffline()) {
bookId = await tauri.createBook(bookData);
if (isDesktop && isCurrentlyOffline()) {
bookId = await tauri.createBook({
title: title,
subTitle: subtitle,
type: selectedBookType,
summary: summary,
desiredReleaseDate: publicationDate,
desiredWordCount: wordCount,
});
} else {
bookId = await System.authPostToServer<string>('book/add', bookData, token, lang);
bookId = await apiPost<string>('book/add', {
title: title,
subTitle: subtitle,
type: selectedBookType,
summary: summary,
serie: 0,
publicationDate: publicationDate,
desiredWordCount: wordCount,
}, token, lang);
}
if (!bookId) {
errorMessage(t('addNewBookForm.error.addingBook'))
errorMessage(t('addNewBookForm.error.addingBook'));
setIsAddingBook(false);
return;
}
const book: BookProps = {
bookId: bookId,
...bookData
const book: SyncedBook = {
id: bookId,
type: selectedBookType,
title: title,
subTitle: subtitle,
seriesId: null,
lastUpdate: new Date().getTime() / 1000,
chapters: [],
characters: [],
locations: [],
worlds: [],
incidents: [],
plotPoints: [],
issues: [],
actSummaries: [],
guideLine: null,
aiGuideLine: null
};
if (isCurrentlyOffline()){
setLocalOnlyBooks((prevBooks: SyncedBook[]): SyncedBook[] => [...prevBooks, {
id: book.bookId,
type: selectedBookType,
title: title,
subTitle: subtitle,
lastUpdate: new Date().getTime()/1000,
chapters: [],
characters: [],
locations: [],
worlds: [],
incidents: [],
plotPoints: [],
issues: [],
actSummaries: [],
guideLine: null,
aiGuideLine: null,
bookTools: null,
seriesId: null,
spells: [],
spellTags: []
}]);
}
else {
setServerOnlyBooks((prevBooks: SyncedBook[]): SyncedBook[] => [...prevBooks, {
id: book.bookId,
type: selectedBookType,
title: title,
subTitle: subtitle,
lastUpdate: new Date().getTime()/1000,
chapters: [],
characters: [],
locations: [],
worlds: [],
incidents: [],
plotPoints: [],
issues: [],
actSummaries: [],
guideLine: null,
aiGuideLine: null,
bookTools: null,
seriesId: null,
spells: [],
spellTags: []
}]);
}
setServerSyncedBooks((prev: SyncedBook[]): SyncedBook[] => [...prev, book])
setIsAddingBook(false);
setCloseForm(false)
} catch (e: unknown) {
console.error('[AddBook] Raw error:', e, typeof e, JSON.stringify(e));
const msg = e instanceof Error ? e.message : typeof e === 'object' && e !== null && 'message' in e ? String((e as {message:string}).message) : typeof e === 'string' ? e : t('addNewBookForm.error.addingBook');
errorMessage(msg);
if (e instanceof Error) {
errorMessage(e.message);
} else {
errorMessage(t('addNewBookForm.error.addingBook'));
}
setIsAddingBook(false);
}
}
@@ -247,96 +203,83 @@ export default function AddNewBookForm({setCloseForm}: { setCloseForm: Dispatch<
}
return (
<div
className="fixed inset-0 flex items-center justify-center bg-black/60 z-40 backdrop-blur-md animate-fadeIn">
<div ref={modalRef}
className="bg-tertiary/95 backdrop-blur-sm text-text-primary rounded-2xl border border-secondary/50 shadow-2xl md:w-3/4 xl:w-1/4 lg:w-2/4 sm:w-11/12 max-h-[85vh] flex flex-col">
<div className="flex justify-between items-center bg-primary px-6 py-4 rounded-t-2xl shadow-lg">
<h2 className="flex items-center gap-3 font-['ADLaM_Display'] text-2xl text-text-primary">
<FontAwesomeIcon icon={faBook} className="w-6 h-6"/>
{t("addNewBookForm.title")}
</h2>
<button
className="text-background hover:text-background w-10 h-10 rounded-xl hover:bg-white/20 transition-all duration-200 flex items-center justify-center hover:scale-110"
onClick={(): void => setCloseForm(false)}
>
<FontAwesomeIcon icon={faX} className={'w-5 h-5'}/>
</button>
</div>
<>
<Modal
icon={Book}
title={t("addNewBookForm.title")}
onClose={(): void => setCloseForm(false)}
size="sm"
footer={
<>
<Button variant="secondary" onClick={() => setCloseForm(false)}>{t("common.cancel")}</Button>
<Button
variant="primary"
onClick={handleAddBook}
isLoading={isAddingBook}
loadingText={t("addNewBookForm.adding")}
icon={Book}
>{t("addNewBookForm.add")}</Button>
</>
}
>
<InputField icon={BookOpen} fieldName={t("addNewBookForm.type")} input={
<SelectBox
onChangeCallBack={(e: ChangeEvent<HTMLSelectElement>): void => setSelectedBookType(e.target.value)}
data={bookTypes.map((types: SelectBoxProps): SelectBoxProps => {
return {
value: types.value,
label: t(types.label)
}
})} defaultValue={selectedBookType}
placeholder={t("addNewBookForm.typePlaceholder")}/>
} action={async (): Promise<void> => setBookTypeHint(true)} actionIcon={Info}/>
<InputField icon={Pencil} fieldName={t("addNewBookForm.bookTitle")} input={
<TextInput value={title}
setValue={(e: ChangeEvent<HTMLInputElement>): void => setTitle(e.target.value)}
placeholder={t("addNewBookForm.bookTitlePlaceholder")}/>
}/>
{
selectedBookType !== 'lyric' && (
<InputField icon={Pencil} fieldName={t("addNewBookForm.subtitle")} input={
<TextInput value={subtitle}
setValue={(e: ChangeEvent<HTMLInputElement>): void => setSubtitle(e.target.value)}
placeholder={t("addNewBookForm.subtitlePlaceholder")}/>
}/>
)
}
<div className="p-5 overflow-y-auto flex-grow custom-scrollbar">
<div className="space-y-6">
<InputField icon={faBookOpen} fieldName={t("addNewBookForm.type")} input={
<SelectBox
onChangeCallBack={(e: ChangeEvent<HTMLSelectElement>): void => setSelectedBookType(e.target.value)}
data={bookTypes.map((types: SelectBoxProps): SelectBoxProps => {
return {
value: types.value,
label: t(types.label)
}
})} defaultValue={selectedBookType}
placeholder={t("addNewBookForm.typePlaceholder")}/>
} action={async (): Promise<void> => setBookTypeHint(true)} actionIcon={faInfo}/>
<InputField icon={faPencilAlt} fieldName={t("addNewBookForm.bookTitle")} input={
<TextInput value={title}
setValue={(e: ChangeEvent<HTMLInputElement>): void => setTitle(e.target.value)}
placeholder={t("addNewBookForm.bookTitlePlaceholder")}/>
}/>
{
selectedBookType !== 'lyric' && (
<InputField icon={faPencilAlt} fieldName={t("addNewBookForm.subtitle")} input={
<TextInput value={subtitle}
setValue={(e: ChangeEvent<HTMLInputElement>): void => setSubtitle(e.target.value)}
placeholder={t("addNewBookForm.subtitlePlaceholder")}/>
}/>
)
}
<InputField icon={faCalendarAlt} fieldName={t("addNewBookForm.publicationDate")} input={
<DatePicker date={publicationDate}
setDate={(e: React.ChangeEvent<HTMLInputElement>): void => setPublicationDate(e.target.value)}/>
}/>
{
selectedBookType !== 'lyric' && (
<>
<InputField icon={faFileWord} fieldName={t("addNewBookForm.wordGoal")}
hint={selectedBookType && `${maxWordsCountHint().min.toLocaleString('fr-FR')} - ${maxWordsCountHint().max > 0 ? maxWordsCountHint().max.toLocaleString('fr-FR') : '∞'} ${t("addNewBookForm.words")}`}
input={
<NumberInput value={wordCount} setValue={setWordCount}
placeholder={t("addNewBookForm.wordGoalPlaceholder")}/>
}/>
<InputField
icon={faFileWord}
fieldName={t("addNewBookForm.summary")}
<InputField icon={Calendar} fieldName={t("addNewBookForm.publicationDate")} input={
<DatePicker date={publicationDate}
setDate={(e: React.ChangeEvent<HTMLInputElement>): void => setPublicationDate(e.target.value)}/>
}/>
{
selectedBookType !== 'lyric' && (
<>
<InputField icon={FileText} fieldName={t("addNewBookForm.wordGoal")}
hint={selectedBookType && `${maxWordsCountHint().min.toLocaleString('fr-FR')} - ${maxWordsCountHint().max > 0 ? maxWordsCountHint().max.toLocaleString('fr-FR') : '∞'} ${t("addNewBookForm.words")}`}
input={
<TexteAreaInput
value={summary}
setValue={(e: ChangeEvent<HTMLTextAreaElement>): void => setSummary(e.target.value)}
placeholder={t("addNewBookForm.summaryPlaceholder")}
/>
}
<NumberInput value={wordCount} setValue={setWordCount}
placeholder={t("addNewBookForm.wordGoalPlaceholder")}/>
}/>
<InputField
icon={FileText}
fieldName={t("addNewBookForm.summary")}
input={
<TextAreaInput
value={summary}
setValue={(e: ChangeEvent<HTMLTextAreaElement>): void => setSummary(e.target.value)}
placeholder={t("addNewBookForm.summaryPlaceholder")}
/>
</>
)
}
</div>
</div>
<div
className="flex justify-between items-center p-5 border-t border-secondary/50 bg-secondary/20 rounded-b-2xl">
<div></div>
<div className="flex gap-3">
<CancelButton callBackFunction={() => setCloseForm(false)}/>
<SubmitButtonWLoading callBackAction={handleAddBook} isLoading={isAddingBook}
text={t("addNewBookForm.add")}
loadingText={t("addNewBookForm.adding")} icon={faBook}/>
</div>
</div>
</div>
}
/>
</>
)
}
</Modal>
{bookTypeHint && <GuideTour stepId={0} steps={bookTypesHint} onClose={(): void => setBookTypeHint(false)}
onComplete={async (): Promise<void> => setBookTypeHint(false)}/>}
</div>
</>
);
}