Reactive-Resume/client/store/resume/resumeSlice.ts

128 lines
3.9 KiB
TypeScript

import { ListItem, Profile, Resume, Section } from '@reactive-resume/schema';
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
import cloneDeep from 'lodash/cloneDeep';
import get from 'lodash/get';
import merge from 'lodash/merge';
import pick from 'lodash/pick';
import set from 'lodash/set';
import { v4 as uuidv4 } from 'uuid';
type SetResumeStatePayload = { path: string; value: unknown };
type AddItemPayload = { path: string; value: ListItem };
type EditItemPayload = { path: string; value: ListItem };
type DuplicateItemPayload = { path: string; value: ListItem };
type DeleteItemPayload = { path: string; value: ListItem };
type AddSectionPayload = { value: Section };
type DeleteSectionPayload = { path: string };
type DeletePagePayload = { page: number };
const initialState: Resume = {} as Resume;
export const resumeSlice = createSlice({
name: 'resume',
initialState,
reducers: {
setResume: (_state: Resume, action: PayloadAction<Resume>) => action.payload,
setResumeState: (state: Resume, action: PayloadAction<SetResumeStatePayload>) => {
const { path, value } = action.payload;
set(state, path, value);
},
addItem: (state: Resume, action: PayloadAction<AddItemPayload>) => {
const { path, value } = action.payload;
const id = uuidv4();
const list = get(state, path, []);
const item = merge(value, { id });
list.push(item);
set(state, path, list);
},
editItem: (state: Resume, action: PayloadAction<EditItemPayload>) => {
const { path, value } = action.payload;
const list: ListItem[] = get(state, path, []);
const index = list.findIndex((item) => item.id === value.id);
list[index] = value;
set(state, path, list);
},
duplicateItem: (state: Resume, action: PayloadAction<DuplicateItemPayload>) => {
const { path, value } = action.payload;
const list: ListItem[] = get(state, path, []);
const index = list.findIndex((item) => item.id === value.id);
const newItem = cloneDeep(list[index]);
newItem.id = uuidv4();
list.push(newItem);
set(state, path, list);
},
deleteItem: (state: Resume, action: PayloadAction<DeleteItemPayload>) => {
const { path, value } = action.payload;
let list = get(state, path, []);
list = list.filter((item: Profile) => item.id !== value.id);
set(state, path, list);
},
addSection: (state: Resume, action: PayloadAction<AddSectionPayload>) => {
const id = uuidv4();
const { value } = action.payload;
state.sections[id] = value;
state.metadata.layout[0][0].push(id);
},
deleteSection: (state: Resume, action: PayloadAction<DeleteSectionPayload>) => {
const { path } = action.payload;
const id = path ? path.split('.').at(-1) : '';
const sections = Object.keys(state.sections).filter((x) => x !== id);
const layout = state.metadata.layout.map((pages) => pages.map((list) => list.filter((x) => x !== id)));
set(state, 'sections', pick(state.sections, sections));
set(state, 'metadata.layout', layout);
},
addPage: (state: Resume) => {
state.metadata.layout.push([[], []]);
},
deletePage: (state: Resume, action: PayloadAction<DeletePagePayload>) => {
const { page } = action.payload;
// Do not delete the first page
if (page === 0) return;
// Get Sections defined in Page X
const [main, sidebar] = state.metadata.layout[page];
// Add sections to page 0 as a default
state.metadata.layout[0][0].push(...main);
state.metadata.layout[0][1].push(...sidebar);
state.metadata.layout.splice(page, 1);
},
},
});
export const {
setResume,
setResumeState,
addItem,
editItem,
duplicateItem,
deleteItem,
addSection,
deleteSection,
addPage,
deletePage,
} = resumeSlice.actions;
export default resumeSlice.reducer;