2023-06-02 08:22:14 +00:00
|
|
|
|
import { PatchUserRequest, UserDocumentTypes, UserDocuments, UserSettings, UserSettingsField, UserWithFields, VerificationStatus } from "@root/model/user";
|
2023-05-30 18:34:41 +00:00
|
|
|
|
import { produce } from "immer";
|
2023-05-17 11:20:11 +00:00
|
|
|
|
import { create } from "zustand";
|
|
|
|
|
import { createJSONStorage, devtools, persist } from "zustand/middleware";
|
2023-05-30 18:34:41 +00:00
|
|
|
|
import { StringSchema, string } from "yup";
|
|
|
|
|
import { patchUser } from "@root/api/user";
|
2023-05-17 11:20:11 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
interface UserStore {
|
|
|
|
|
userId: string | null;
|
2023-05-30 18:34:41 +00:00
|
|
|
|
user: UserWithFields | null;
|
|
|
|
|
settingsFields: UserSettings | null;
|
2023-06-02 08:22:14 +00:00
|
|
|
|
verificationStatus: VerificationStatus;
|
|
|
|
|
verificationType: "juridical" | "nko";
|
|
|
|
|
isDocumentsDialogOpen: boolean;
|
|
|
|
|
dialogType: "juridical" | "nko";
|
|
|
|
|
documents: UserDocuments;
|
2023-05-17 11:20:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-05-30 18:34:41 +00:00
|
|
|
|
const defaultFieldValues = {
|
|
|
|
|
value: "",
|
|
|
|
|
error: null,
|
|
|
|
|
touched: false,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const defaultFields = {
|
|
|
|
|
name: { ...defaultFieldValues },
|
|
|
|
|
surname: { ...defaultFieldValues },
|
|
|
|
|
middleName: { ...defaultFieldValues },
|
|
|
|
|
companyName: { ...defaultFieldValues },
|
|
|
|
|
email: { ...defaultFieldValues },
|
|
|
|
|
phoneNumber: { ...defaultFieldValues },
|
|
|
|
|
password: { ...defaultFieldValues },
|
|
|
|
|
};
|
|
|
|
|
|
2023-06-02 08:22:14 +00:00
|
|
|
|
const defaultDocument = {
|
|
|
|
|
file: null,
|
|
|
|
|
uploadedFileName: null,
|
|
|
|
|
imageSrc: null,
|
|
|
|
|
};
|
|
|
|
|
|
2023-05-17 11:20:11 +00:00
|
|
|
|
const initialState: UserStore = {
|
|
|
|
|
userId: null,
|
|
|
|
|
user: null,
|
2023-05-30 18:34:41 +00:00
|
|
|
|
settingsFields: { ...defaultFields },
|
2023-06-02 08:22:14 +00:00
|
|
|
|
verificationStatus: "notVerificated",
|
|
|
|
|
verificationType: "juridical",
|
|
|
|
|
isDocumentsDialogOpen: false,
|
|
|
|
|
dialogType: "juridical",
|
|
|
|
|
documents: {
|
|
|
|
|
"ИНН": { ...defaultDocument },
|
|
|
|
|
"Устав": { ...defaultDocument },
|
|
|
|
|
"Свидетельство о регистрации НКО": { ...defaultDocument },
|
|
|
|
|
}
|
2023-05-17 11:20:11 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const useUserStore = create<UserStore>()(
|
|
|
|
|
persist(
|
|
|
|
|
devtools(
|
|
|
|
|
(set, get) => initialState,
|
|
|
|
|
{
|
|
|
|
|
name: "User store",
|
2023-05-30 18:34:41 +00:00
|
|
|
|
enabled: process.env.NODE_ENV === "development",
|
2023-05-17 11:20:11 +00:00
|
|
|
|
}
|
|
|
|
|
),
|
|
|
|
|
{
|
2023-06-02 08:22:14 +00:00
|
|
|
|
version: 1,
|
2023-05-17 11:20:11 +00:00
|
|
|
|
name: "user",
|
|
|
|
|
storage: createJSONStorage(() => localStorage),
|
2023-05-30 18:34:41 +00:00
|
|
|
|
partialize: state => ({
|
|
|
|
|
userId: state.userId,
|
|
|
|
|
user: state.user,
|
|
|
|
|
})
|
2023-05-17 11:20:11 +00:00
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
export const setUserId = (userId: string | null) => useUserStore.setState({ userId });
|
2023-06-02 08:22:14 +00:00
|
|
|
|
|
|
|
|
|
export const openDocumentsDialog = (type: UserStore["dialogType"]) => useUserStore.setState(
|
|
|
|
|
produce<UserStore>(state => {
|
|
|
|
|
state.isDocumentsDialogOpen = true;
|
|
|
|
|
state.dialogType = type;
|
|
|
|
|
})
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
export const closeDocumentsDialog = () => useUserStore.setState(
|
|
|
|
|
produce<UserStore>(state => {
|
|
|
|
|
state.isDocumentsDialogOpen = false;
|
|
|
|
|
})
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
export const setDocument = (type: UserDocumentTypes, file: File | undefined) => useUserStore.setState(
|
|
|
|
|
produce<UserStore>(state => {
|
|
|
|
|
if (!file) {
|
|
|
|
|
state.documents[type] = { ...defaultDocument };
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let imageSrc: string | null = null;
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const src = state.documents[type].imageSrc;
|
|
|
|
|
if (src) URL.revokeObjectURL(src);
|
|
|
|
|
|
|
|
|
|
imageSrc = URL.createObjectURL(file);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.log(error);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
state.documents[type] = {
|
|
|
|
|
file,
|
|
|
|
|
uploadedFileName: null,
|
|
|
|
|
imageSrc,
|
|
|
|
|
};
|
|
|
|
|
})
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
export const setUploadedDocument = (type: UserDocumentTypes, fileName: string, url: string) => useUserStore.setState(
|
|
|
|
|
produce<UserStore>(state => {
|
|
|
|
|
state.documents[type] = {
|
|
|
|
|
file: null,
|
|
|
|
|
uploadedFileName: fileName,
|
|
|
|
|
imageSrc: url,
|
|
|
|
|
};
|
|
|
|
|
})
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
export const sendDocuments = () => {
|
|
|
|
|
const state = useUserStore.getState();
|
|
|
|
|
const type = state.dialogType;
|
|
|
|
|
const documents = state.documents;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// const formData = new FormData();
|
|
|
|
|
// formData.append("file1", file1);
|
|
|
|
|
// formData.append("file2", file2);
|
|
|
|
|
|
|
|
|
|
// revoke on success
|
|
|
|
|
// Object.values(documents).map(document => document?.imageSrc).forEach(src => {
|
|
|
|
|
// if (src) URL.revokeObjectURL(src);
|
|
|
|
|
// });
|
|
|
|
|
|
|
|
|
|
// useUserStore.setState(produce<UserStore>(state => {
|
|
|
|
|
// state.isDocumentsDialogOpen = false;
|
|
|
|
|
// }));
|
|
|
|
|
};
|
|
|
|
|
|
2023-05-30 18:34:41 +00:00
|
|
|
|
export const setUser = (user: UserWithFields | null) => useUserStore.setState(
|
|
|
|
|
produce<UserStore>(state => {
|
|
|
|
|
state.user = user;
|
|
|
|
|
if (!user) {
|
|
|
|
|
state.settingsFields = null;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!state.settingsFields) state.settingsFields = { ...defaultFields };
|
|
|
|
|
|
|
|
|
|
state.settingsFields.name.value = user.name || "";
|
|
|
|
|
state.settingsFields.surname.value = user.surname || "";
|
|
|
|
|
state.settingsFields.middleName.value = user.middleName || "";
|
|
|
|
|
state.settingsFields.companyName.value = user.companyName || "";
|
|
|
|
|
state.settingsFields.email.value = user.email;
|
|
|
|
|
state.settingsFields.phoneNumber.value = user.phoneNumber;
|
|
|
|
|
state.settingsFields.password.value = "";
|
|
|
|
|
})
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
export const clearUser = () => useUserStore.setState({ ...initialState });
|
|
|
|
|
|
2023-06-02 08:22:14 +00:00
|
|
|
|
export const setSettingsField = (
|
2023-05-30 18:34:41 +00:00
|
|
|
|
fieldName: UserSettingsField,
|
|
|
|
|
value: string,
|
2023-06-02 08:22:14 +00:00
|
|
|
|
) => useUserStore.setState(
|
|
|
|
|
produce<UserStore>(state => {
|
|
|
|
|
if (!state.settingsFields) return;
|
2023-05-30 18:34:41 +00:00
|
|
|
|
|
2023-06-02 08:22:14 +00:00
|
|
|
|
let errorMessage: string | null = null;
|
2023-05-30 18:34:41 +00:00
|
|
|
|
|
2023-06-02 08:22:14 +00:00
|
|
|
|
try {
|
|
|
|
|
validators[fieldName].validateSync(value);
|
|
|
|
|
} catch (error: any) {
|
|
|
|
|
errorMessage = error.message;
|
|
|
|
|
}
|
2023-05-30 18:34:41 +00:00
|
|
|
|
|
|
|
|
|
state.settingsFields[fieldName].value = value || "";
|
|
|
|
|
state.settingsFields[fieldName].touched = true;
|
|
|
|
|
state.settingsFields[fieldName].error = errorMessage;
|
2023-06-02 08:22:14 +00:00
|
|
|
|
})
|
|
|
|
|
);
|
|
|
|
|
|
2023-05-30 18:34:41 +00:00
|
|
|
|
|
|
|
|
|
export const sendUserData = async () => {
|
|
|
|
|
const state = useUserStore.getState();
|
|
|
|
|
if (!state.settingsFields) return;
|
|
|
|
|
|
|
|
|
|
const payload: PatchUserRequest = {};
|
|
|
|
|
|
|
|
|
|
for (const [fieldName, fieldValue] of Object.entries(state.settingsFields)) {
|
|
|
|
|
if (
|
|
|
|
|
fieldValue.value !== (state.user?.[fieldName as UserSettingsField] ?? "")
|
|
|
|
|
) payload[fieldName as UserSettingsField] = fieldValue.value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const user = await patchUser(payload);
|
|
|
|
|
setUser(user);
|
|
|
|
|
};
|
2023-05-17 11:20:11 +00:00
|
|
|
|
|
2023-05-30 18:34:41 +00:00
|
|
|
|
const validators: Record<UserSettingsField, StringSchema> = {
|
|
|
|
|
name: string(),
|
|
|
|
|
email: string().email("Неверный email"),
|
|
|
|
|
surname: string(),
|
|
|
|
|
phoneNumber: string().matches(/^[+\d|\d]*$/, "Неверный номер телефона").min(6, "Номер телефона должен содержать минимум 6 символов"),
|
|
|
|
|
middleName: string(),
|
|
|
|
|
password: string().min(8, "Минимум 8 символов").matches(/^[.,:;-_+\d\w]+$/, "Некорректные символы в пароле"),
|
|
|
|
|
companyName: string(),
|
|
|
|
|
};
|