Merge branch 'dev' into 'staging'
предупреждение о смене логина пароля и обработка ошибки корзины See merge request frontend/marketplace!129
This commit is contained in:
commit
9d4f22812b
@ -47,12 +47,13 @@ function Drawers() {
|
||||
if (payCartError) {
|
||||
if (payCartError.includes("insufficient funds: ")) {
|
||||
const notEnoughMoneyAmount = parseInt(payCartError.replace(/^.*insufficient\sfunds:\s(?=\d+$)/, ""));
|
||||
|
||||
setNotEnoughMoneyAmount(notEnoughMoneyAmount);
|
||||
}
|
||||
|
||||
setLoading(false);
|
||||
|
||||
closeCartDrawer()
|
||||
navigate("payment")
|
||||
return enqueueSnackbar(payCartError);
|
||||
}
|
||||
|
||||
@ -321,7 +322,7 @@ console.log('aaaaaaaaaaAAAAAAAAAAAAAA', (cart.appliedCartPurchasesDiscount?.Targ
|
||||
onClick={() => (notEnoughMoneyAmount === 0 ? !loading && handlePayClick() : handleReplenishWallet())}
|
||||
sx={{ mt: "25px", display: "block" }}
|
||||
>
|
||||
{loading ? <Loader size={24} /> : "Оплатить"}
|
||||
{loading ? <Loader size={24} /> : notEnoughMoneyAmount === 0 ? "Оплатить" : "Пополнить"}
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
|
@ -20,6 +20,8 @@ interface Props {
|
||||
FormInputSx?: SxProps<Theme>;
|
||||
TextfieldProps: TextFieldProps;
|
||||
onChange: (e: React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>) => void;
|
||||
onBlur?: () => void
|
||||
onFocus?: () => void
|
||||
}
|
||||
|
||||
export default function InputTextfield({
|
||||
@ -31,6 +33,8 @@ export default function InputTextfield({
|
||||
TextfieldProps,
|
||||
color,
|
||||
FormInputSx,
|
||||
onBlur = ()=>{},
|
||||
onFocus = ()=>{}
|
||||
}: Props) {
|
||||
const theme = useTheme()
|
||||
const upMd = useMediaQuery(theme.breakpoints.up("md"))
|
||||
@ -45,6 +49,7 @@ export default function InputTextfield({
|
||||
|
||||
return (
|
||||
<FormControl
|
||||
|
||||
fullWidth
|
||||
variant="standard"
|
||||
sx={{
|
||||
@ -68,6 +73,12 @@ export default function InputTextfield({
|
||||
</InputLabel>
|
||||
)}
|
||||
<TextField
|
||||
onBlur={(e) => {
|
||||
onBlur()
|
||||
}}
|
||||
onFocus={(e) => {
|
||||
onFocus()
|
||||
}}
|
||||
{...TextfieldProps}
|
||||
fullWidth
|
||||
id={id}
|
||||
|
@ -8,6 +8,7 @@ import { Loader } from "./Loader";
|
||||
import { currencyFormatter } from "@root/utils/currencyFormatter";
|
||||
import { payCart } from "@root/api/cart";
|
||||
import { setUserAccount } from "@root/stores/user";
|
||||
import { useCart } from "@root/utils/hooks/useCart";
|
||||
|
||||
interface Props {
|
||||
priceBeforeDiscounts: number;
|
||||
@ -19,6 +20,7 @@ export default function TotalPrice({ priceAfterDiscounts, priceBeforeDiscounts,
|
||||
const theme = useTheme();
|
||||
const upMd = useMediaQuery(theme.breakpoints.up("md"));
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down(550));
|
||||
const cart = useCart();
|
||||
const [notEnoughMoneyAmount, setNotEnoughMoneyAmount] = useState<number>(0);
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
const navigate = useNavigate();
|
||||
@ -162,6 +164,7 @@ export default function TotalPrice({ priceAfterDiscounts, priceBeforeDiscounts,
|
||||
)}
|
||||
<Button
|
||||
variant="pena-contained-dark"
|
||||
disabled = {cart.priceAfterDiscounts === 0}
|
||||
onClick={() => (notEnoughMoneyAmount === 0 ? !loading && handlePayClick() : handleReplenishWallet())}
|
||||
sx={{ mt: "10px" }}
|
||||
>
|
||||
|
@ -23,6 +23,8 @@ interface Props {
|
||||
FormInputSx?: SxProps<Theme>;
|
||||
TextfieldProps: TextFieldProps;
|
||||
onChange: (e: React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>) => void;
|
||||
onBlur?: () => void
|
||||
onFocus?: () => void
|
||||
}
|
||||
|
||||
export default function PasswordInput({
|
||||
@ -34,6 +36,8 @@ export default function PasswordInput({
|
||||
TextfieldProps,
|
||||
color,
|
||||
FormInputSx,
|
||||
onBlur = () => {},
|
||||
onFocus = () => {},
|
||||
}: Props) {
|
||||
const theme = useTheme()
|
||||
const upMd = useMediaQuery(theme.breakpoints.up("md"))
|
||||
@ -81,6 +85,8 @@ export default function PasswordInput({
|
||||
{...TextfieldProps}
|
||||
fullWidth
|
||||
id={id}
|
||||
onBlur={onBlur}
|
||||
onFocus={onFocus}
|
||||
sx={{
|
||||
"& .MuiInputBase-root": {
|
||||
height: "48px",
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { useEffect } from "react"
|
||||
import { useEffect, useState } from "react"
|
||||
import { Box, Button, SxProps, Theme, Typography, useMediaQuery, useTheme } from "@mui/material"
|
||||
import InputTextfield from "@components/InputTextfield"
|
||||
import PasswordInput from "@components/passwordInput"
|
||||
@ -16,6 +16,7 @@ import { VerificationStatus } from "@root/model/account"
|
||||
import { verify } from "./helper"
|
||||
import { withErrorBoundary } from "react-error-boundary"
|
||||
import { handleComponentError } from "@root/utils/handleComponentError"
|
||||
import { OnChangeLoginPassword } from "./onChangeLoginPassword"
|
||||
|
||||
function AccountSettings() {
|
||||
const theme = useTheme()
|
||||
@ -24,12 +25,39 @@ function AccountSettings() {
|
||||
const isTablet = useMediaQuery(theme.breakpoints.down(1000))
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down(600))
|
||||
|
||||
const fields = useUserStore((state) => state.settingsFields)
|
||||
const { settingsFields, user } = useUserStore((state) => state)
|
||||
const verificationStatus = useUserStore((state) => state.verificationStatus)
|
||||
const verificationType = useUserStore((state) => state.verificationType)
|
||||
const comment = useUserStore((state) => state.comment)
|
||||
const userId = useUserStore((state) => state.userId) ?? ""
|
||||
|
||||
const [onChangeTypeLP, setOnChangeTypeLP] = useState<"email" | "password" | "all" | "">("")
|
||||
|
||||
const [readySend, setReadySend] = useState(false)
|
||||
const [loginPasswordFocus, setLoginPasswordFocus] = useState(false)
|
||||
|
||||
const okLP = () => {
|
||||
const ready = readySend
|
||||
|
||||
if (ready) {
|
||||
handleSendDataClick()
|
||||
}
|
||||
setReadySend(false)
|
||||
setOnChangeTypeLP("")
|
||||
}
|
||||
const cancelLP = () => {
|
||||
const type = onChangeTypeLP
|
||||
setOnChangeTypeLP("")
|
||||
setReadySend(false)
|
||||
console.log(type)
|
||||
if (type === "email" && user?.email !== settingsFields.email.value) setSettingsField("email", user?.email || "")
|
||||
if (type === "password") setSettingsField("password", "")
|
||||
if (type === "all") {
|
||||
setSettingsField("email", user?.email || "")
|
||||
setSettingsField("password", "")
|
||||
}
|
||||
}
|
||||
|
||||
// useEffect(() => {
|
||||
// verify(userId)
|
||||
// }, [])
|
||||
@ -40,6 +68,26 @@ function AccountSettings() {
|
||||
bold: true,
|
||||
}
|
||||
|
||||
const trySendData = () => {
|
||||
console.log("клик по сохранить")
|
||||
if (user?.email !== settingsFields.email.value && settingsFields.password.value.length > 0) {
|
||||
setReadySend(true)
|
||||
setOnChangeTypeLP("all")
|
||||
return
|
||||
}
|
||||
if (user?.email !== settingsFields.email.value) {
|
||||
setReadySend(true)
|
||||
setOnChangeTypeLP("email")
|
||||
return
|
||||
}
|
||||
if (settingsFields.password.value.length > 0) {
|
||||
setReadySend(true)
|
||||
setOnChangeTypeLP("password")
|
||||
return
|
||||
}
|
||||
handleSendDataClick()
|
||||
}
|
||||
|
||||
function handleSendDataClick() {
|
||||
sendUserData()
|
||||
.then(() => {
|
||||
@ -62,7 +110,7 @@ function AccountSettings() {
|
||||
>
|
||||
<DocumentsDialog />
|
||||
<Typography variant="h4" mt="20px">
|
||||
Настройки аккаунта
|
||||
Настройки аккаунта
|
||||
</Typography>
|
||||
<Box
|
||||
sx={{
|
||||
@ -85,7 +133,10 @@ function AccountSettings() {
|
||||
flexDirection: upMd ? "row" : "column",
|
||||
}}
|
||||
>
|
||||
<UserFields/>
|
||||
<UserFields
|
||||
loginPasswordFocus={loginPasswordFocus}
|
||||
setLoginPasswordFocus={setLoginPasswordFocus}
|
||||
/>
|
||||
<Box
|
||||
sx={{
|
||||
maxWidth: "246px",
|
||||
@ -102,7 +153,7 @@ function AccountSettings() {
|
||||
onClick: () => openDocumentsDialog("juridical"),
|
||||
}}
|
||||
>
|
||||
Загрузить документы для юр лиц
|
||||
Загрузить документы для юр лиц
|
||||
</UnderlinedButtonWithIcon>
|
||||
<UnderlinedButtonWithIcon
|
||||
icon={<UploadIcon />}
|
||||
@ -111,7 +162,7 @@ function AccountSettings() {
|
||||
onClick: () => openDocumentsDialog("nko"),
|
||||
}}
|
||||
>
|
||||
Загрузить документы для НКО
|
||||
Загрузить документы для НКО
|
||||
</UnderlinedButtonWithIcon>
|
||||
</>
|
||||
)}
|
||||
@ -123,7 +174,7 @@ function AccountSettings() {
|
||||
onClick: () => openDocumentsDialog(verificationType),
|
||||
}}
|
||||
>
|
||||
Посмотреть свою верификацию
|
||||
Посмотреть свою верификацию
|
||||
</UnderlinedButtonWithIcon>
|
||||
)}
|
||||
{comment && <p>{comment}</p>}
|
||||
@ -131,13 +182,18 @@ function AccountSettings() {
|
||||
</Box>
|
||||
<Button
|
||||
variant="pena-contained-dark"
|
||||
onClick={handleSendDataClick}
|
||||
disabled={fields.hasError}
|
||||
onClick={trySendData}
|
||||
disabled={settingsFields.hasError || loginPasswordFocus}
|
||||
sx={{ alignSelf: "end" }}
|
||||
>
|
||||
Сохранить
|
||||
Сохранить
|
||||
</Button>
|
||||
</Box>
|
||||
<OnChangeLoginPassword
|
||||
type={onChangeTypeLP}
|
||||
ok={okLP}
|
||||
cancel={cancelLP}
|
||||
/>
|
||||
</SectionWrapper>
|
||||
)
|
||||
}
|
||||
@ -157,8 +213,8 @@ function VerificationIndicator({
|
||||
verificationStatus,
|
||||
sx,
|
||||
}: {
|
||||
verificationStatus: VerificationStatus;
|
||||
sx?: SxProps<Theme>;
|
||||
verificationStatus: VerificationStatus;
|
||||
sx?: SxProps<Theme>;
|
||||
}) {
|
||||
return (
|
||||
<Box
|
||||
|
@ -3,23 +3,29 @@ import InputTextfield from "@components/InputTextfield"
|
||||
import PasswordInput from "@components/passwordInput"
|
||||
import { setSettingsField, useUserStore } from "@root/stores/user"
|
||||
|
||||
interface Props {
|
||||
loginPasswordFocus: boolean;
|
||||
setLoginPasswordFocus: (a:boolean) => void;
|
||||
}
|
||||
|
||||
export default function UserFields () {
|
||||
export default function UserFields({
|
||||
loginPasswordFocus,
|
||||
setLoginPasswordFocus
|
||||
}: Props) {
|
||||
const theme = useTheme()
|
||||
const upSm = useMediaQuery(theme.breakpoints.up("sm"))
|
||||
const upMd = useMediaQuery(theme.breakpoints.up("md"))
|
||||
|
||||
const fields = useUserStore((state) => state.settingsFields)
|
||||
const { settingsFields, user } = useUserStore((state) => state)
|
||||
|
||||
|
||||
console.log("fields")
|
||||
|
||||
const textFieldProps = {
|
||||
gap: upMd ? "16px" : "10px",
|
||||
color: "#F2F3F7",
|
||||
bold: true,
|
||||
}
|
||||
|
||||
return(
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
display: "grid",
|
||||
@ -34,9 +40,9 @@ export default function UserFields () {
|
||||
<InputTextfield
|
||||
TextfieldProps={{
|
||||
placeholder: "Имя",
|
||||
value: fields.firstname.value || "",
|
||||
helperText: fields.firstname.touched && fields.firstname.error,
|
||||
error: fields.firstname.touched && Boolean(fields.firstname.error),
|
||||
value: settingsFields.firstname.value || "",
|
||||
helperText: settingsFields.firstname.touched && settingsFields.firstname.error,
|
||||
error: settingsFields.firstname.touched && Boolean(settingsFields.firstname.error),
|
||||
}}
|
||||
onChange={(e) => setSettingsField("firstname", e.target.value)}
|
||||
id="firstname"
|
||||
@ -46,9 +52,9 @@ export default function UserFields () {
|
||||
<InputTextfield
|
||||
TextfieldProps={{
|
||||
placeholder: "Фамилия",
|
||||
value: fields.secondname.value || "",
|
||||
helperText: fields.secondname.touched && fields.secondname.error,
|
||||
error: fields.secondname.touched && Boolean(fields.secondname.error),
|
||||
value: settingsFields.secondname.value || "",
|
||||
helperText: settingsFields.secondname.touched && settingsFields.secondname.error,
|
||||
error: settingsFields.secondname.touched && Boolean(settingsFields.secondname.error),
|
||||
}}
|
||||
onChange={(e) => setSettingsField("secondname", e.target.value)}
|
||||
id="secondname"
|
||||
@ -58,9 +64,9 @@ export default function UserFields () {
|
||||
<InputTextfield
|
||||
TextfieldProps={{
|
||||
placeholder: "Отчество",
|
||||
value: fields.middlename.value || "",
|
||||
helperText: fields.middlename.touched && fields.middlename.error,
|
||||
error: fields.middlename.touched && Boolean(fields.middlename.error),
|
||||
value: settingsFields.middlename.value || "",
|
||||
helperText: settingsFields.middlename.touched && settingsFields.middlename.error,
|
||||
error: settingsFields.middlename.touched && Boolean(settingsFields.middlename.error),
|
||||
}}
|
||||
onChange={(e) => setSettingsField("middlename", e.target.value)}
|
||||
id="middlename"
|
||||
@ -70,9 +76,9 @@ export default function UserFields () {
|
||||
<InputTextfield
|
||||
TextfieldProps={{
|
||||
placeholder: "ООО Фирма",
|
||||
value: fields.orgname.value || "",
|
||||
helperText: fields.orgname.touched && fields.orgname.error,
|
||||
error: fields.orgname.touched && Boolean(fields.orgname.error),
|
||||
value: settingsFields.orgname.value || user?.email || "",
|
||||
helperText: settingsFields.orgname.touched && settingsFields.orgname.error,
|
||||
error: settingsFields.orgname.touched && Boolean(settingsFields.orgname.error),
|
||||
}}
|
||||
onChange={(e) => setSettingsField("orgname", e.target.value)}
|
||||
id="orgname"
|
||||
@ -82,21 +88,27 @@ export default function UserFields () {
|
||||
<InputTextfield
|
||||
TextfieldProps={{
|
||||
placeholder: "username@penahaub.com",
|
||||
value: fields.email.value || "",
|
||||
helperText: fields.email.touched && fields.email.error,
|
||||
error: fields.email.touched && Boolean(fields.email.error),
|
||||
value: settingsFields.email.value || "",
|
||||
helperText: settingsFields.email.touched && settingsFields.email.error,
|
||||
error: settingsFields.email.touched && Boolean(settingsFields.email.error),
|
||||
}}
|
||||
onChange={(e) => setSettingsField("email", e.target.value)}
|
||||
onFocus={() => {
|
||||
setLoginPasswordFocus(true)
|
||||
}}
|
||||
onBlur={() => {
|
||||
setLoginPasswordFocus(false)
|
||||
}}
|
||||
id="email"
|
||||
label="E-mail"
|
||||
label="Изменить E-mail"
|
||||
{...textFieldProps}
|
||||
/>
|
||||
<InputTextfield
|
||||
TextfieldProps={{
|
||||
placeholder: "+7 900 000 00 00",
|
||||
value: fields.phoneNumber.value || "",
|
||||
helperText: fields.phoneNumber.touched && fields.phoneNumber.error,
|
||||
error: fields.phoneNumber.touched && Boolean(fields.phoneNumber.error),
|
||||
value: settingsFields.phoneNumber.value || "",
|
||||
helperText: settingsFields.phoneNumber.touched && settingsFields.phoneNumber.error,
|
||||
error: settingsFields.phoneNumber.touched && Boolean(settingsFields.phoneNumber.error),
|
||||
}}
|
||||
onChange={(e) => setSettingsField("phoneNumber", e.target.value)}
|
||||
id="phoneNumber"
|
||||
@ -106,14 +118,21 @@ export default function UserFields () {
|
||||
<PasswordInput
|
||||
TextfieldProps={{
|
||||
placeholder: "Не менее 8 символов",
|
||||
value: fields.password.value || "",
|
||||
helperText: fields.password.touched && fields.password.error,
|
||||
error: fields.password.touched && Boolean(fields.password.error),
|
||||
value: settingsFields.password.value || "",
|
||||
helperText: settingsFields.password.touched && settingsFields.password.error,
|
||||
error: settingsFields.password.touched && Boolean(settingsFields.password.error),
|
||||
autoComplete: "new-password",
|
||||
}}
|
||||
onFocus={() => {
|
||||
setLoginPasswordFocus(true)
|
||||
}}
|
||||
onBlur={() => {
|
||||
setLoginPasswordFocus(false)
|
||||
}}
|
||||
|
||||
onChange={(e) => setSettingsField("password", e.target.value)}
|
||||
id="password"
|
||||
label="Пароль"
|
||||
label="Изменить пароль"
|
||||
{...textFieldProps}
|
||||
/>
|
||||
</Box>
|
||||
|
55
src/pages/AccountSettings/onChangeLoginPassword.tsx
Normal file
55
src/pages/AccountSettings/onChangeLoginPassword.tsx
Normal file
@ -0,0 +1,55 @@
|
||||
import { Box, Button, Modal, Typography, useMediaQuery, useTheme } from "@mui/material"
|
||||
|
||||
|
||||
interface Props {
|
||||
type: "email" | "password" | "all" | "";
|
||||
ok: () => void;
|
||||
cancel: () => void;
|
||||
}
|
||||
|
||||
export const OnChangeLoginPassword = ({
|
||||
type,
|
||||
ok,
|
||||
cancel
|
||||
}: Props) => {
|
||||
const theme = useTheme()
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down(600))
|
||||
|
||||
return (
|
||||
<Modal
|
||||
open={Boolean(type)}
|
||||
>
|
||||
<Box sx={{
|
||||
position: 'absolute' as 'absolute',
|
||||
top: '50%',
|
||||
left: '50%',
|
||||
transform: 'translate(-50%, -50%)',
|
||||
bgcolor: 'background.paper',
|
||||
borderRadius: "7px",
|
||||
boxShadow: 24,
|
||||
p: "20px 30px",
|
||||
width: isMobile ? "320px" : "487px"
|
||||
}}>
|
||||
<Typography sx={{ textAlign: "center" }} variant="h6" component="h2">
|
||||
Вы уверены, что хотите изменить
|
||||
{type === "all" || type === "email" ? " почту " : ""}
|
||||
{type === "all" ? "и" : ""}
|
||||
{type === "all" || type === "password" ? " пароль " : ""}
|
||||
?
|
||||
|
||||
</Typography>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
mt: "35px",
|
||||
flexWrap: isMobile ? "wrap" : "nowrap"
|
||||
}}
|
||||
>
|
||||
<Button sx={{ m: "15px" }} variant="pena-contained-dark" onClick={cancel}>отмена</Button>
|
||||
<Button sx={{ m: "15px" }} variant="pena-contained-dark" onClick={ok}>изменить</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
</Modal>
|
||||
)
|
||||
}
|
@ -15,7 +15,8 @@ const translateMessage: Record<string, string> = {
|
||||
"field <phoneNumber> is empty": "Поле \"Номер телефона\" не заполнено",
|
||||
"user with this email or login is exist": "Пользователь уже существует",
|
||||
"user with this login is exist": "Пользователь с таким логином уже существует",
|
||||
"unauthorized": "Ссылка просрочена"
|
||||
"unauthorized": "Ссылка просрочена",
|
||||
"insufficient funds: 910000": "Недостаточно средств"
|
||||
}
|
||||
|
||||
export const parseAxiosError = (nativeError: unknown): [string, number?] => {
|
||||
|
Loading…
Reference in New Issue
Block a user