import { PatchUserRequest, UserDocumentTypes, UserDocuments, UserDocumentsUrl, UserSettingsFieldStatus, UserSettingsField, } from "@root/model/user" import { produce } from "immer" import { create } from "zustand" import { createJSONStorage, devtools, persist } from "zustand/middleware" import { StringSchema, string } from "yup" import { patchUser } from "@root/api/user" import { UserAccountSettingsFieldStatus, VerificationStatus } from "@root/model/account" import { patchCurrency, deleteCart, patchCart } from "@root/api/cart" import { User, UserAccount, UserName, getInitials, patchUserAccount } from "@frontend/kitui" import { setNotEnoughMoneyAmount } from "./cart"; interface UserStore { userId: string | null; user: User | null; userAccount: UserAccount | null; settingsFields: UserSettingsFieldStatus & UserAccountSettingsFieldStatus & { hasError: boolean }; verificationStatus: VerificationStatus; verificationType: "juridical" | "nko"; isDocumentsDialogOpen: boolean; dialogType: "juridical" | "nko" | ""; documents: UserDocuments; documentsUrl: UserDocumentsUrl; comment: string; initials: string; } const defaultFieldValues = { value: "", error: null, touched: false, } const defaultFields: UserStore["settingsFields"] = { firstname: { ...defaultFieldValues }, secondname: { ...defaultFieldValues }, middlename: { ...defaultFieldValues }, orgname: { ...defaultFieldValues }, email: { ...defaultFieldValues }, phoneNumber: { ...defaultFieldValues }, password: { ...defaultFieldValues }, hasError: false, } export const defaultDocument = { file: null, uploadedFileName: null, imageSrc: null, } const initialState: UserStore = { userId: null, user: null, userAccount: null, settingsFields: { ...defaultFields }, verificationStatus: VerificationStatus.NOT_VERIFICATED, verificationType: "juridical", isDocumentsDialogOpen: false, dialogType: "", comment: "", documents: { ИНН: { ...defaultDocument }, Устав: { ...defaultDocument }, "Свидетельство о регистрации НКО": { ...defaultDocument }, }, documentsUrl: { ИНН: "", Устав: "", "Свидетельство о регистрации НКО": "", }, initials: "AA", } export const useUserStore = create()( persist( devtools((set, get) => initialState, { name: "User", enabled: process.env.NODE_ENV === "development", trace: true, }), { version: 2, name: "user", storage: createJSONStorage(() => localStorage), partialize: (state) => ({ // список полей для хранения в ЛС userId: state.userId, user: state.user, }), migrate: (persistedState, version) => ({ ...(persistedState as UserStore), user: null, }), } ) ) export const setVerificationStatus = (verificationStatus: VerificationStatus) => useUserStore.setState({ verificationStatus }) export const setVerificationType = (verificationType: "nko" | "org") => useUserStore.setState({ verificationType: verificationType === "org" ? "juridical" : "nko", }) export const setUserId = (userId: string | null) => useUserStore.setState({ userId }) export const setUser = (user: User) => useUserStore.setState( produce((state) => { state.user = user state.settingsFields.email.value = user?.email ?? "" state.settingsFields.phoneNumber.value = user?.phoneNumber ?? "" state.settingsFields.password.value = "" }) ) export const setUserAccount = (user: UserAccount) => useUserStore.setState( produce((state) => { state.userAccount = user state.settingsFields.firstname.value = user?.name.firstname ?? "" state.settingsFields.secondname.value = user?.name.secondname ?? "" state.settingsFields.middlename.value = user?.name.middlename ?? "" state.settingsFields.orgname.value = user?.name.orgname ?? "" state.initials = getInitials(state.settingsFields.firstname.value, state.settingsFields.secondname.value) }), false, { type: "setUserAccount", payload: user, } ) export const setCart = (cart: string[]) => useUserStore.setState( produce((state) => { if (state.userAccount) state.userAccount.cart = cart }) ) export const setComment = (comment: string) => useUserStore.setState({ comment }) export const clearUserData = () => useUserStore.setState({ ...initialState }) export const openDocumentsDialog = (type: UserStore["dialogType"]) => useUserStore.setState( produce((state) => { state.isDocumentsDialogOpen = true state.dialogType = type }) ) export const closeDocumentsDialog = () => useUserStore.setState( produce((state) => { state.isDocumentsDialogOpen = false state.dialogType = "" }) ) export const setDocument = (type: UserDocumentTypes, file: File | null) => useUserStore.setState( produce((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 creating object url", error) } state.documents[type] = { file, uploadedFileName: null, imageSrc, } }) ) export const setDocumentUrl = (type: UserDocumentTypes, url: string) => useUserStore.setState( produce((state) => { if (!url) { state.documentsUrl[type] = "" return } state.documentsUrl[type] = url }) ) export const setUploadedDocument = (type: UserDocumentTypes, fileName: string, url: string) => useUserStore.setState( produce((state) => { state.documents[type] = { file: null, uploadedFileName: fileName, imageSrc: url, } }) ) export const setSettingsField = (fieldName: UserSettingsField | keyof UserName, value: string) => useUserStore.setState( produce((state) => { if (!state.settingsFields) return let errorMessage: string | null = null try { if (value) validators[fieldName].validateSync(value) } catch (error: any) { errorMessage = error.message } state.settingsFields[fieldName].value = value || "" state.settingsFields[fieldName].touched = true state.settingsFields[fieldName].error = errorMessage state.settingsFields.hasError = Object.values(state.settingsFields).reduce((acc: boolean, field) => { if (typeof field === "boolean") return acc if (field.error !== null) return true return acc }, false) }) ) export const sendUserData = async () => { const state = useUserStore.getState() if (!state.settingsFields) return const isPatchingUser = state.settingsFields.email.touched || state.settingsFields.password.touched || state.settingsFields.phoneNumber.touched const isPatchingUserAccount = state.settingsFields.firstname.touched || state.settingsFields.secondname.touched || state.settingsFields.middlename.touched || state.settingsFields.orgname.touched const userPayload: PatchUserRequest = {} if (state.settingsFields.email.value.length !== 0) userPayload.email = state.settingsFields.email.value if (state.settingsFields.password.value.length !== 0) userPayload.password = state.settingsFields.password.value if (state.settingsFields.phoneNumber.value.length !== 0) userPayload.phoneNumber = state.settingsFields.phoneNumber.value const userAccountPayload: UserName = { firstname: state.settingsFields.firstname.value, secondname: state.settingsFields.secondname.value, middlename: state.settingsFields.middlename.value, orgname: state.settingsFields.orgname.value, } await Promise.all([ isPatchingUser && patchUser(userPayload).then(([user]) => user && setUser(user)), isPatchingUserAccount && patchUserAccount(userAccountPayload).then(setUserAccount), ]) } export const addTariffToCart = async (tariffId: string) => { const [patchCartResponse, patchCartError] = await patchCart(tariffId) if (patchCartError === undefined) { setCart(patchCartResponse) } return({patchCartResponse, patchCartError}) } export const removeTariffFromCart = async (tariffId: string) => { setNotEnoughMoneyAmount(0); const [deleteCartResponse, deleteCartError] = await deleteCart(tariffId) if (!deleteCartError) { setCart(deleteCartResponse) } } export const changeUserCurrency = async (currency: string) => { const [patchCurrencyResponse, patchCurrencyError] = await patchCurrency(currency) if (!patchCurrencyError && patchCurrencyResponse) { setUserAccount(patchCurrencyResponse) } } const validators: Record = { email: string().email("Неверный email"), phoneNumber: string() .matches(/^[+\d|\d]*$/, "Неверный номер телефона") .min(6, "Номер телефона должен содержать минимум 6 символов"), password: string() .min(8, "Минимум 8 символов") .matches(/^[.,:;\-_+!&()*<>\[\]\{\}`@"#$\%\^\=?\d\w]+$/, "Некорректные символы в пароле") .optional(), firstname: string(), secondname: string(), middlename: string(), orgname: string(), }