mirror of
https://github.com/AmruthPillai/Reactive-Resume.git
synced 2025-11-17 02:01:29 +10:00
83 lines
2.7 KiB
TypeScript
83 lines
2.7 KiB
TypeScript
import { t } from "@lingui/macro";
|
|
import { createId } from "@paralleldrive/cuid2";
|
|
import { ResumeDto } from "@reactive-resume/dto";
|
|
import { CustomSectionGroup, defaultSection, SectionKey } from "@reactive-resume/schema";
|
|
import { removeItemInLayout } from "@reactive-resume/utils";
|
|
import _set from "lodash.set";
|
|
import { temporal, TemporalState } from "zundo";
|
|
import { create } from "zustand";
|
|
import { devtools } from "zustand/middleware";
|
|
import { immer } from "zustand/middleware/immer";
|
|
import { useStoreWithEqualityFn } from "zustand/traditional";
|
|
|
|
import { debouncedUpdateResume } from "../services/resume";
|
|
|
|
type ResumeStore = {
|
|
resume: ResumeDto;
|
|
|
|
// Actions
|
|
setValue: (path: string, value: unknown) => void;
|
|
|
|
// Custom Section Actions
|
|
addSection: () => void;
|
|
removeSection: (sectionId: SectionKey) => void;
|
|
};
|
|
|
|
export const useResumeStore = create<ResumeStore>()(
|
|
temporal(
|
|
immer((set) => ({
|
|
resume: {} as ResumeDto,
|
|
setValue: (path, value) => {
|
|
set((state) => {
|
|
if (path === "visibility") {
|
|
state.resume.visibility = value as "public" | "private";
|
|
} else {
|
|
state.resume.data = _set(state.resume.data, path, value);
|
|
}
|
|
|
|
void debouncedUpdateResume(JSON.parse(JSON.stringify(state.resume)));
|
|
});
|
|
},
|
|
addSection: () => {
|
|
const section: CustomSectionGroup = {
|
|
...defaultSection,
|
|
id: createId(),
|
|
name: t`Custom Section`,
|
|
items: [],
|
|
};
|
|
|
|
set((state) => {
|
|
const lastPageIndex = state.resume.data.metadata.layout.length - 1;
|
|
state.resume.data.metadata.layout[lastPageIndex][0].push(`custom.${section.id}`);
|
|
state.resume.data = _set(state.resume.data, `sections.custom.${section.id}`, section);
|
|
|
|
void debouncedUpdateResume(JSON.parse(JSON.stringify(state.resume)));
|
|
});
|
|
},
|
|
removeSection: (sectionId: SectionKey) => {
|
|
if (sectionId.startsWith("custom.")) {
|
|
const id = sectionId.split("custom.")[1];
|
|
|
|
set((state) => {
|
|
removeItemInLayout(sectionId, state.resume.data.metadata.layout);
|
|
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
|
delete state.resume.data.sections.custom[id];
|
|
|
|
void debouncedUpdateResume(JSON.parse(JSON.stringify(state.resume)));
|
|
});
|
|
}
|
|
},
|
|
})),
|
|
{
|
|
limit: 100,
|
|
wrapTemporal: (fn) => devtools(fn),
|
|
partialize: ({ resume }) => ({ resume }),
|
|
},
|
|
),
|
|
);
|
|
|
|
export const useTemporalResumeStore = <T>(
|
|
selector: (state: TemporalState<Pick<ResumeStore, "resume">>) => T,
|
|
equality?: (a: T, b: T) => boolean,
|
|
) => useStoreWithEqualityFn(useResumeStore.temporal, selector, equality);
|