import { httpApi } from '@/lib/utils'; import { Resume, ResumeComponentData, ResumeComponentPlacement } from '@/types/resume'; import { set } from '@vueuse/core'; import { defineStore } from 'pinia' import { computed, ComputedRef, watch } from 'vue'; const useResumesStore = defineStore('resumes', { state: () => ({ resumes: [] as Array, resumesAreFetched: false as boolean, currentResumeIndex: -1 as number, selectedResumePlacementIndex: -1 as number, }), getters: { hasResumes: (state) => computed(() => state.resumes.length > 0), /* === CURRENT RESUME === */ currentResume(state): ComputedRef { console.debug("Current resume index : ", state.currentResumeIndex); return computed(() => state.currentResumeIndex >= 0 ? state.resumes[state.currentResumeIndex] : null); }, hasCurrentResume: (state) => computed(() => state.currentResumeIndex >= 0 && state.currentResumeIndex < state.resumes.length), currentResumeName() { return computed(() => { const resume = this.currentResume; return resume ? (resume.value?.name || 'Sans titre') : 'Sans titre'; }); }, /* === SELECTED RESUME PLACEMENT === */ hasCurrentSelectedResumePlacement(): ComputedRef { return computed(() => { const currentResume = this.currentResume; const selectedPlacementIndex = this.selectedResumePlacementIndex; return currentResume !== null && selectedPlacementIndex >= 0 && selectedPlacementIndex < (currentResume.value?.components_placements?.length ?? 0); }); }, currentSelectedResumePlacement(): ComputedRef { return computed(() => { if (!this.hasCurrentSelectedResumePlacement.value) return null; const currentResume = this.currentResume; const selectedPlacementIndex = this.selectedResumePlacementIndex; return currentResume.value.components_placements[selectedPlacementIndex]; }); }, }, actions: { async fetchResumes() { try { this.resumesAreFetched = false; // get from cache const cachedResumes = localStorage.resumes; if (cachedResumes) { this.setResumes(JSON.parse(cachedResumes)); } const { data: resumes, error } = await httpApi(route("resumes.index")); if (error || !resumes) { console.error('Failed to fetch resumes:', error); return; } this.setResumes(resumes); // Store in cache this.saveResumesToCache(); this.resumesAreFetched = true; } catch (error) { console.error('Failed to fetch resumes:', error); } }, async updateResumeToApi(resumeIndex: number) { try { if (!this.resumesAreFetched) { console.warn("Resumes are not fetched yet. Cannot update to API."); return; } if (resumeIndex < 0 || resumeIndex >= this.resumes.length) { console.warn("Invalid resume index. Cannot update to API."); return; } const resumeToUpdate = this.resumes[resumeIndex]; const { data: updatedResume, error } = await httpApi(route("resumes.update", { resume: resumeToUpdate.id }), { method: 'PUT', headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]')?.getAttribute('content') || '', 'Accept': 'application/json' }, body: JSON.stringify(resumeToUpdate), }); if (error || !updatedResume) { console.error('Failed to update resumes:', error); return; } } catch (error) { console.error('Failed to update resumes:', error); } }, async updateCurrentResumeToApi() { await this.updateResumeToApi(this.currentResumeIndex); }, async saveResumesToCache() { localStorage.resumes = JSON.stringify(this.resumes); }, setResumes(resumes: Array) { this.resumes = resumes; }, addResume(resume: Resume) { this.resumes.push(resume); this.currentResumeIndex = this.resumes.length - 1; }, removeResume(index: number) { if (index < 0 || index >= this.resumes.length) return; this.resumes.splice(index, 1); if (this.currentResumeIndex >= this.resumes.length) { this.currentResumeIndex = this.resumes.length - 1; } }, removeResumeById(id: number) { const index = this.resumes.findIndex(resume => resume.id === id); if (index === -1) return; this.removeResume(index); }, setCurrentResume(index: number) { if (index < 0 || index >= this.resumes.length) return; this.currentResumeIndex = index; }, setCurrentResumeById(id: number) { const index = this.resumes.findIndex(resume => resume.id === id); if (index === -1) return; this.setCurrentResume(index); }, setAndUpdateCurrentResume(resume: Resume) { this.setCurrentResumeById(resume.id); this.updateCurrentResume(resume); }, setAndUpdateCurrentResumeWhenFetched(resume: Resume) { watch(() => this.resumesAreFetched, (newVal) => { if (newVal === true) { this.setAndUpdateCurrentResume(resume); } }); }, updateCurrentResume(updatedResume: Resume) { if (this.currentResumeIndex < 0 || this.currentResumeIndex >= this.resumes.length) return; set(this.resumes, this.currentResumeIndex, updatedResume); this.saveResumesToCache(); }, setCurrentResumeName(name: string) { if (this.currentResumeIndex < 0 || this.currentResumeIndex >= this.resumes.length) return; this.resumes[this.currentResumeIndex].name = name; this.saveResumesToCache(); }, /* === RESUME COMPONENT PLACEMENTS === */ hasComponentsPlacements(): ComputedRef { return computed(() => { const currentResume = this.currentResume; return currentResume !== null && (currentResume.value?.components_placements?.length ?? 0) > 0; }); }, async updateCurrentResumePlacementsToApi(index: number) { // resume-component-placements.update try { if (!this.hasCurrentResume.value) { console.warn("No current resume selected. Cannot update placements to API."); return; } const currentResume = this.currentResume.value; if (!currentResume) { console.warn("Current resume is null. Cannot update placements to API."); return; } const componentPlacements = currentResume.components_placements; if (!componentPlacements) { console.warn("Current resume has no components placements. Cannot update placements to API."); return; } const componentPlacement = componentPlacements[index]; if (!componentPlacement) { console.warn("Invalid component placement index. Cannot update placements to API."); return; } console.debug("Updating component placement:", componentPlacement); const { data: updatedPlacement, error } = await httpApi(route("resume-component-placements.update", componentPlacement.id), { method: 'PUT', headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]')?.getAttribute('content') || '', 'Accept': 'application/json' }, body: JSON.stringify(componentPlacement), }); if (error || !updatedPlacement) { console.error('Failed to update resume placements:', error); return; } // Update the local state with the updated placement this.modifyResumePlacements(index, updatedPlacement); this.saveResumesToCache(); } catch (error) { console.error('Failed to update resume placements:', error); } }, setSelectedResumePlacement(index: number) { const currentResume = this.currentResume; if (!this.hasCurrentResume.value || !currentResume.value?.components_placements) { this.selectedResumePlacementIndex = -1; return; } if (index < 0 || index >= currentResume.value.components_placements.length) { this.selectedResumePlacementIndex = -1; return; } this.selectedResumePlacementIndex = index; }, setSelectedResumePlacementById(id: number) { if (!this.hasCurrentResume.value || !this.currentResume.value?.components_placements) return; const resumePlacementIndex = this.currentResume.value?.components_placements.findIndex(placement => placement.id === id) ?? -1; this.setSelectedResumePlacement(resumePlacementIndex); }, modifyResumePlacements(index:number, modifiedPlacement: ResumeComponentPlacement) { if (!this.hasCurrentResume.value || !this.currentResume.value?.components_placements) return; const currentResume = this.currentResume.value; set(currentResume.components_placements!, index, modifiedPlacement); }, modifyCurrentSelectedResumePlacement(updatedPlacement: ResumeComponentPlacement) { if (!this.hasCurrentSelectedResumePlacement.value) return; this.modifyResumePlacements(this.selectedResumePlacementIndex, updatedPlacement); }, swapComponentsPlacementsOrder(indexA: number, indexB: number) { if (!this.hasCurrentResume.value || !this.currentResume.value?.components_placements) return; const currentResume = this.currentResume.value; const placements = currentResume.components_placements!; if (indexA < 0 || indexA >= placements.length || indexB < 0 || indexB >= placements.length) return; // Swap the order values const tempOrder = placements[indexA].order; placements[indexA].order = placements[indexB].order; placements[indexB].order = tempOrder; // Swap the placements in the array [placements[indexA], placements[indexB]] = [placements[indexB], placements[indexA]]; // Update the components placements this.updateCurrentResumePlacementsToApi(indexA); this.updateCurrentResumePlacementsToApi(indexB); }, async unlinkComponentPlacement(index: number) { if (!this.hasComponentsPlacements) return; const currentResume = this.currentResume.value!; if (index < 0 || index >= currentResume.components_placements!.length) return; // Call the 'resume-component-placements.unlink' API endpoint // It will return the new component_data that need to be set to the placement const placementToUnlink = currentResume.components_placements![index]; try { const { data: newComponentData, error } = await httpApi(route("resume-component-placements.unlink", placementToUnlink.id), { method: 'PUT', headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]')?.getAttribute('content') || '', 'Accept': 'application/json' }, }); if (error || !newComponentData) { console.error('Failed to unlink resume placement:', error); return; } const updatedPlacement = { ...placementToUnlink, component_data: newComponentData }; // Update the local state with the updated placement this.modifyResumePlacements(index, updatedPlacement); this.saveResumesToCache(); } catch (error) { console.error('Failed to unlink resume placement:', error); } }, async deleteComponentPlacement(index: number) { if (!this.hasComponentsPlacements) return; const currentResume = this.currentResume.value!; if (index < 0 || index >= currentResume.components_placements!.length) return; const placementToDelete = currentResume.components_placements![index]; try { const { error } = await httpApi(route("resume-component-placements.destroy", placementToDelete.id), { method: 'DELETE', headers: { 'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]')?.getAttribute('content') || '', 'Accept': 'application/json' }, }); if (error) { console.error('Failed to delete resume placement:', error); return; } // Remove the placement from the local state currentResume.components_placements!.splice(index, 1); this.saveResumesToCache(); // Clear selected placement if it was the deleted one if (this.selectedResumePlacementIndex === index) { this.clearSelectedResumePlacement(); } } catch (error) { console.error('Failed to delete resume placement:', error); } }, clearSelectedResumePlacement() { this.selectedResumePlacementIndex = -1; }, }, }); // const useCurrentResumeStore = defineStore('currentResume', { // state: () => ({ // currentResume: null as Resume | null, // }), // getters: { // hasCurrentResume: (state) => computed(() => state.currentResume !== null), // currentResumeName: (state) => computed(() => state.currentResume?.name ?? 'Sans titre'), // }, // actions: { // setCurrentResume(resume: Resume) { // this.currentResume = resume; // }, // setResumeName(name: string) { // if (this.currentResume) { // this.currentResume.name = name; // } // } // }, // }); // const useSelectedResumePlacementStore = defineStore('selectedResumePlacement', { // state: () => ({ // selectedResumePlacement: null as ResumeComponentPlacement | null, // }), // actions: { // setSelectedResumePlacement(placement: ResumeComponentPlacement | null) { // this.selectedResumePlacement = placement; // }, // }, // }); export { useResumesStore };