'use client' import {faMapMarkerAlt, faPlus, faTrash} from '@fortawesome/free-solid-svg-icons'; import {FontAwesomeIcon} from '@fortawesome/react-fontawesome'; import {ChangeEvent, forwardRef, useContext, useEffect, useImperativeHandle, useState} from 'react'; import {SessionContext} from "@/context/SessionContext"; import {AlertContext} from "@/context/AlertContext"; import {BookContext} from "@/context/BookContext"; import System from '@/lib/models/System'; import InputField from "@/components/form/InputField"; import TextInput from '@/components/form/TextInput'; import TexteAreaInput from "@/components/form/TexteAreaInput"; import {useTranslations} from "next-intl"; import {LangContext, LangContextProps} from "@/context/LangContext"; import OfflineContext, {OfflineContextType} from "@/context/OfflineContext"; interface SubElement { id: string; name: string; description: string; } interface Element { id: string; name: string; description: string; subElements: SubElement[]; } interface LocationProps { id: string; name: string; elements: Element[]; } export function LocationComponent(props: any, ref: any) { const t = useTranslations(); const {lang} = useContext(LangContext); const {isCurrentlyOffline} = useContext(OfflineContext); const {session} = useContext(SessionContext); const {successMessage, errorMessage} = useContext(AlertContext); const {book} = useContext(BookContext); const bookId: string | undefined = book?.bookId; const token: string = session.accessToken; const [sections, setSections] = useState([]); const [newSectionName, setNewSectionName] = useState(''); const [newElementNames, setNewElementNames] = useState<{ [key: string]: string }>({}); const [newSubElementNames, setNewSubElementNames] = useState<{ [key: string]: string }>({}); useImperativeHandle(ref, function () { return { handleSave: handleSave, }; }); useEffect((): void => { getAllLocations().then(); }, []); async function getAllLocations(): Promise { try { let response: LocationProps[]; if (isCurrentlyOffline()) { response = await window.electron.invoke('db:location:all', {bookid: bookId}); } else { if (book?.localBook) { response = await window.electron.invoke('db:location:all', {bookid: bookId}); } else { response = await System.authGetQueryToServer(`location/all`, token, lang, { bookid: bookId, }); } } if (response && response.length > 0) { setSections(response); } } catch (e: unknown) { if (e instanceof Error) { errorMessage(e.message); } else { errorMessage(t('locationComponent.errorUnknownFetchLocations')); } } } async function handleAddSection(): Promise { if (!newSectionName.trim()) { errorMessage(t('locationComponent.errorSectionNameEmpty')) return } try { let sectionId: string; if (isCurrentlyOffline()) { sectionId = await window.electron.invoke('db:location:section:add', { bookId: bookId, locationName: newSectionName, }); } else { if (book?.localBook) { sectionId = await window.electron.invoke('db:location:section:add', { bookId: bookId, locationName: newSectionName, }); } else { sectionId = await System.authPostToServer(`location/section/add`, { bookId: bookId, locationName: newSectionName, }, token, lang); } } if (!sectionId) { errorMessage(t('locationComponent.errorUnknownAddSection')); return; } const newLocation: LocationProps = { id: sectionId, name: newSectionName, elements: [], }; setSections([...sections, newLocation]); setNewSectionName(''); } catch (e: unknown) { if (e instanceof Error) { errorMessage(e.message); } else { errorMessage(t('locationComponent.errorUnknownAddSection')); } } } async function handleAddElement(sectionId: string): Promise { if (!newElementNames[sectionId]?.trim()) { errorMessage(t('locationComponent.errorElementNameEmpty')) return } try { let elementId: string; if (isCurrentlyOffline()) { elementId = await window.electron.invoke('db:location:element:add', { bookId: bookId, locationId: sectionId, elementName: newElementNames[sectionId], }); } else { if (book?.localBook) { elementId = await window.electron.invoke('db:location:element:add', { bookId: bookId, locationId: sectionId, elementName: newElementNames[sectionId], }); } else { elementId = await System.authPostToServer(`location/element/add`, { bookId: bookId, locationId: sectionId, elementName: newElementNames[sectionId], }, token, lang); } } if (!elementId) { errorMessage(t('locationComponent.errorUnknownAddElement')); return; } const updatedSections: LocationProps[] = [...sections]; const sectionIndex: number = updatedSections.findIndex( (section: LocationProps): boolean => section.id === sectionId, ); updatedSections[sectionIndex].elements.push({ id: elementId, name: newElementNames[sectionId], description: '', subElements: [], }); setSections(updatedSections); setNewElementNames({...newElementNames, [sectionId]: ''}); } catch (e: unknown) { if (e instanceof Error) { errorMessage(e.message); } else { errorMessage(t('locationComponent.errorUnknownAddElement')); } } } function handleElementChange( sectionId: string, elementIndex: number, field: keyof Element, value: string, ): void { const updatedSections: LocationProps[] = [...sections]; const sectionIndex: number = updatedSections.findIndex( (section: LocationProps): boolean => section.id === sectionId, ); // @ts-ignore updatedSections[sectionIndex].elements[elementIndex][field] = value; setSections(updatedSections); } async function handleAddSubElement( sectionId: string, elementIndex: number, ): Promise { if (!newSubElementNames[elementIndex]?.trim()) { errorMessage(t('locationComponent.errorSubElementNameEmpty')) return } const sectionIndex: number = sections.findIndex( (section: LocationProps): boolean => section.id === sectionId, ); try { let subElementId: string; if (isCurrentlyOffline()) { subElementId = await window.electron.invoke('db:location:subelement:add', { elementId: sections[sectionIndex].elements[elementIndex].id, subElementName: newSubElementNames[elementIndex], }); } else { if (book?.localBook) { subElementId = await window.electron.invoke('db:location:subelement:add', { elementId: sections[sectionIndex].elements[elementIndex].id, subElementName: newSubElementNames[elementIndex], }); } else { subElementId = await System.authPostToServer(`location/sub-element/add`, { elementId: sections[sectionIndex].elements[elementIndex].id, subElementName: newSubElementNames[elementIndex], }, token, lang); } } if (!subElementId) { errorMessage(t('locationComponent.errorUnknownAddSubElement')); return; } const updatedSections: LocationProps[] = [...sections]; updatedSections[sectionIndex].elements[elementIndex].subElements.push({ id: subElementId, name: newSubElementNames[elementIndex], description: '', }); setSections(updatedSections); setNewSubElementNames({...newSubElementNames, [elementIndex]: ''}); } catch (e: unknown) { if (e instanceof Error) { errorMessage(e.message); } else { errorMessage(t('locationComponent.errorUnknownAddSubElement')); } } } function handleSubElementChange( sectionId: string, elementIndex: number, subElementIndex: number, field: keyof SubElement, value: string, ): void { const updatedSections: LocationProps[] = [...sections]; const sectionIndex: number = updatedSections.findIndex( (section: LocationProps): boolean => section.id === sectionId, ); updatedSections[sectionIndex].elements[elementIndex].subElements[ subElementIndex ][field] = value; setSections(updatedSections); } async function handleRemoveElement( sectionId: string, elementIndex: number, ): Promise { try { let response: boolean; const elementId = sections.find((section: LocationProps): boolean => section.id === sectionId) ?.elements[elementIndex].id; if (isCurrentlyOffline()) { response = await window.electron.invoke('db:location:element:delete', { elementId: elementId, }); } else { if (book?.localBook) { response = await window.electron.invoke('db:location:element:delete', { elementId: elementId, }); } else { response = await System.authDeleteToServer(`location/element/delete`, { elementId: elementId, }, token, lang); } } if (!response) { errorMessage(t('locationComponent.errorUnknownDeleteElement')); return; } const updatedSections: LocationProps[] = [...sections]; const sectionIndex: number = updatedSections.findIndex((section: LocationProps): boolean => section.id === sectionId,); updatedSections[sectionIndex].elements.splice(elementIndex, 1); setSections(updatedSections); } catch (e: unknown) { if (e instanceof Error) { errorMessage(e.message); } else { errorMessage(t('locationComponent.errorUnknownDeleteElement')); } } } async function handleRemoveSubElement( sectionId: string, elementIndex: number, subElementIndex: number, ): Promise { try { let response: boolean; const subElementId = sections.find((section: LocationProps): boolean => section.id === sectionId)?.elements[elementIndex].subElements[subElementIndex].id; if (isCurrentlyOffline()) { response = await window.electron.invoke('db:location:subelement:delete', { subElementId: subElementId, }); } else { if (book?.localBook) { response = await window.electron.invoke('db:location:subelement:delete', { subElementId: subElementId, }); } else { response = await System.authDeleteToServer(`location/sub-element/delete`, { subElementId: subElementId, }, token, lang); } } if (!response) { errorMessage(t('locationComponent.errorUnknownDeleteSubElement')); return; } const updatedSections: LocationProps[] = [...sections]; const sectionIndex: number = updatedSections.findIndex((section: LocationProps): boolean => section.id === sectionId,); updatedSections[sectionIndex].elements[elementIndex].subElements.splice(subElementIndex, 1,); setSections(updatedSections); } catch (e: unknown) { if (e instanceof Error) { errorMessage(e.message); } else { errorMessage(t('locationComponent.errorUnknownDeleteSubElement')); } } } async function handleRemoveSection(sectionId: string): Promise { try { let response: boolean; if (isCurrentlyOffline()) { response = await window.electron.invoke('db:location:delete', { locationId: sectionId, }); } else { if (book?.localBook) { response = await window.electron.invoke('db:location:delete', { locationId: sectionId, }); } else { response = await System.authDeleteToServer(`location/delete`, { locationId: sectionId, }, token, lang); } } if (!response) { errorMessage(t('locationComponent.errorUnknownDeleteSection')); return; } const updatedSections: LocationProps[] = sections.filter((section: LocationProps): boolean => section.id !== sectionId,); setSections(updatedSections); } catch (e: unknown) { if (e instanceof Error) { errorMessage(e.message); } else { errorMessage(t('locationComponent.errorUnknownDeleteSection')); } } } async function handleSave(): Promise { try { let response: boolean; if (isCurrentlyOffline()) { response = await window.electron.invoke('db:location:update', { locations: sections, }); } else { if (book?.localBook) { response = await window.electron.invoke('db:location:update', { locations: sections, }); } else { response = await System.authPostToServer(`location/update`, { locations: sections, }, token, lang); } } if (!response) { errorMessage(t('locationComponent.errorUnknownSave')); return; } successMessage(t('locationComponent.successSave')); } catch (e: unknown) { if (e instanceof Error) { errorMessage(e.message); } else { errorMessage(t('locationComponent.errorUnknownSave')); } } } return (
) => setNewSectionName(e.target.value)} placeholder={t("locationComponent.newSectionPlaceholder")} /> } actionIcon={faPlus} actionLabel={t("locationComponent.addSectionLabel")} addButtonCallBack={handleAddSection} />
{sections.length > 0 ? ( sections.map((section: LocationProps) => (

{section.name} {section.elements.length || 0}

{section.elements.length > 0 ? ( section.elements.map((element, elementIndex) => (
) => handleElementChange(section.id, elementIndex, 'name', e.target.value) } placeholder={t("locationComponent.elementNamePlaceholder")} /> } removeButtonCallBack={(): Promise => handleRemoveElement(section.id, elementIndex)} />
): void => handleElementChange(section.id, elementIndex, 'description', e.target.value)} placeholder={t("locationComponent.elementDescriptionPlaceholder")} />
{element.subElements.length > 0 && (

{t("locationComponent.subElementsHeading")}

)} {element.subElements.map((subElement: SubElement, subElementIndex: number) => (
): void => handleSubElementChange(section.id, elementIndex, subElementIndex, 'name', e.target.value) } placeholder={t("locationComponent.subElementNamePlaceholder")} /> } removeButtonCallBack={(): Promise => handleRemoveSubElement(section.id, elementIndex, subElementIndex)} />
handleSubElementChange(section.id, elementIndex, subElementIndex, 'description', e.target.value) } placeholder={t("locationComponent.subElementDescriptionPlaceholder")} />
))} ) => setNewSubElementNames({ ...newSubElementNames, [elementIndex]: e.target.value }) } placeholder={t("locationComponent.newSubElementPlaceholder")} /> } addButtonCallBack={(): Promise => handleAddSubElement(section.id, elementIndex)} />
)) ) : (
{t("locationComponent.noElementAvailable")}
)} ) => setNewElementNames({...newElementNames, [section.id]: e.target.value}) } placeholder={t("locationComponent.newElementPlaceholder")} /> } addButtonCallBack={(): Promise => handleAddElement(section.id)} />
)) ) : (

{t("locationComponent.noSectionAvailable")}

)}
); } export default forwardRef(LocationComponent);