front-hub/src/stores/user.ts
2024-04-16 12:29:28 +03:00

320 lines
9.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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<UserStore>()(
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<UserStore>((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<UserStore>((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<UserStore>((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<UserStore>((state) => {
state.isDocumentsDialogOpen = true
state.dialogType = type
})
)
export const closeDocumentsDialog = () =>
useUserStore.setState(
produce<UserStore>((state) => {
state.isDocumentsDialogOpen = false
state.dialogType = ""
})
)
export const setDocument = (type: UserDocumentTypes, file: File | null) =>
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 creating object url", error)
}
state.documents[type] = {
file,
uploadedFileName: null,
imageSrc,
}
})
)
export const setDocumentUrl = (type: UserDocumentTypes, url: string) =>
useUserStore.setState(
produce<UserStore>((state) => {
if (!url) {
state.documentsUrl[type] = ""
return
}
state.documentsUrl[type] = url
})
)
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 setSettingsField = (fieldName: UserSettingsField | keyof UserName, value: string) =>
useUserStore.setState(
produce<UserStore>((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<UserSettingsField | keyof UserName, StringSchema> = {
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(),
}