Merge branch 'dev' into 'staging'

предупреждение о смене логина пароля и обработка ошибки корзины

See merge request frontend/marketplace!129
This commit is contained in:
Nastya 2024-02-19 20:48:34 +00:00
commit 9d4f22812b
8 changed files with 195 additions and 43 deletions

@ -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>

@ -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?] => {