361 lines
16 KiB
TypeScript
361 lines
16 KiB
TypeScript
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<Resume>,
|
|
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<Resume | null> {
|
|
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<boolean> {
|
|
return computed(() => {
|
|
const currentResume = this.currentResume;
|
|
const selectedPlacementIndex = this.selectedResumePlacementIndex;
|
|
return currentResume !== null &&
|
|
selectedPlacementIndex >= 0 &&
|
|
selectedPlacementIndex < (currentResume.value?.components_placements?.length ?? 0);
|
|
});
|
|
},
|
|
currentSelectedResumePlacement(): ComputedRef<ResumeComponentPlacement | null> {
|
|
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<Resume[]>(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<null>(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<Resume>) {
|
|
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<boolean> {
|
|
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<ResumeComponentPlacement>(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<ResumeComponentData>(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<null>(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 };
|