feat: add errors
This commit is contained in:
parent
b31d2cf700
commit
5712cbcf56
|
@ -2,8 +2,8 @@
|
||||||
|
|
||||||
import React, {FC, useEffect, useState} from 'react';
|
import React, {FC, useEffect, useState} from 'react';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { Modal, Menu, Calendar, Radio, Button, Input, message } from 'antd';
|
import { Modal, Menu, Calendar, Radio, Button, Input, message, Form } from 'antd';
|
||||||
import type { CalendarProps, RadioChangeEvent, MenuProps } from 'antd';
|
import type { CalendarProps, MenuProps } from 'antd';
|
||||||
import { ArrowLeftOutlined } from '@ant-design/icons';
|
import { ArrowLeftOutlined } from '@ant-design/icons';
|
||||||
import { CloseOutlined } from '@ant-design/icons';
|
import { CloseOutlined } from '@ant-design/icons';
|
||||||
import locale_ru from 'antd/lib/calendar/locale/ru_RU';
|
import locale_ru from 'antd/lib/calendar/locale/ru_RU';
|
||||||
|
@ -57,7 +57,7 @@ const getLocale = (locale: string) => {
|
||||||
return locale_es;
|
return locale_es;
|
||||||
default:
|
default:
|
||||||
return locale_en;
|
return locale_en;
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return locale_en;
|
return locale_en;
|
||||||
|
@ -83,12 +83,12 @@ export const ScheduleModal: FC<ScheduleModalProps> = ({
|
||||||
checkSession,
|
checkSession,
|
||||||
}) => {
|
}) => {
|
||||||
const [selectDate, setSelectDate] = useState<Dayjs>(dayjs());
|
const [selectDate, setSelectDate] = useState<Dayjs>(dayjs());
|
||||||
const [dates, setDates] = useState<any>();
|
const [dates, setDates] = useState<Record<string, { startTime: string, endTime: string }[]> | undefined>();
|
||||||
const [tags, setTags] = useState<Tag[] | undefined>();
|
const [tags, setTags] = useState<Tag[] | undefined>();
|
||||||
const [sessionData, setSessionData] = useState<SignupSessionData>({ coachId: +expertId });
|
|
||||||
const [rawScheduler, setRawScheduler] = useState<ExpertScheduler | null>(null);
|
const [rawScheduler, setRawScheduler] = useState<ExpertScheduler | null>(null);
|
||||||
const [isPayLoading, setIsPayLoading] = useState<boolean>(false);
|
const [isPayLoading, setIsPayLoading] = useState<boolean>(false);
|
||||||
const [sessionId, setSessionId] = useState<string>('');
|
const [sessionId, setSessionId] = useState<string>('');
|
||||||
|
const [form] = Form.useForm<{ clientComment?: string, startAtUtc?: string, tagId?: number }>();
|
||||||
|
|
||||||
dayjs.locale(locale);
|
dayjs.locale(locale);
|
||||||
|
|
||||||
|
@ -117,7 +117,6 @@ export const ScheduleModal: FC<ScheduleModalProps> = ({
|
||||||
|
|
||||||
useEffect(()=> {
|
useEffect(()=> {
|
||||||
if (open && mode !== 'pay') {
|
if (open && mode !== 'pay') {
|
||||||
setSessionData({ coachId: +expertId });
|
|
||||||
getSchedulerByExpertId(expertId as string, locale as string)
|
getSchedulerByExpertId(expertId as string, locale as string)
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
setRawScheduler(data);
|
setRawScheduler(data);
|
||||||
|
@ -126,6 +125,10 @@ export const ScheduleModal: FC<ScheduleModalProps> = ({
|
||||||
console.log(err);
|
console.log(err);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!open) {
|
||||||
|
form.resetFields();
|
||||||
|
}
|
||||||
}, [open]);
|
}, [open]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -158,10 +161,6 @@ export const ScheduleModal: FC<ScheduleModalProps> = ({
|
||||||
|
|
||||||
const disabledDate = (currentDate: Dayjs) => !dates || !dates[currentDate.format('YYYY-MM-DD')];
|
const disabledDate = (currentDate: Dayjs) => !dates || !dates[currentDate.format('YYYY-MM-DD')];
|
||||||
|
|
||||||
const onChangeTimeSlot = (e: RadioChangeEvent) => setSessionData({ ...sessionData, startAtUtc: e.target.value.startTime });
|
|
||||||
|
|
||||||
const onChangeTag = (tagId: number) => setSessionData({ ...sessionData, tagId });
|
|
||||||
|
|
||||||
const cellRender: CalendarProps<Dayjs>['fullCellRender'] = (date, info) => {
|
const cellRender: CalendarProps<Dayjs>['fullCellRender'] = (date, info) => {
|
||||||
const isWeekend = date.day() === 6 || date.day() === 0;
|
const isWeekend = date.day() === 6 || date.day() === 0;
|
||||||
return React.cloneElement(info.originNode, {
|
return React.cloneElement(info.originNode, {
|
||||||
|
@ -181,6 +180,13 @@ export const ScheduleModal: FC<ScheduleModalProps> = ({
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onValidate = () => {
|
||||||
|
form.validateFields()
|
||||||
|
.then((values) => {
|
||||||
|
checkSession({ coachId: +expertId, ...values });
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
className="b-modal"
|
className="b-modal"
|
||||||
|
@ -237,35 +243,54 @@ export const ScheduleModal: FC<ScheduleModalProps> = ({
|
||||||
{selectDate.locale(locale).format('DD MMMM YYYY')}
|
{selectDate.locale(locale).format('DD MMMM YYYY')}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<div className="b-schedule-select-tag">
|
<Form form={form}>
|
||||||
{tags && (
|
<div className="b-schedule-select-tag">
|
||||||
<CustomSelect
|
{tags && (
|
||||||
label={i18nText('selectTopic', locale)}
|
<Form.Item
|
||||||
value={sessionData?.tagId}
|
name="tagId"
|
||||||
options={tags?.map(({ id, name }) => ({ value: id, label: name }))}
|
rules={[{
|
||||||
onChange={onChangeTag}
|
required: true,
|
||||||
|
message: ''
|
||||||
|
}]}
|
||||||
|
>
|
||||||
|
<CustomSelect
|
||||||
|
label={i18nText('selectTopic', locale)}
|
||||||
|
options={tags?.map(({id, name}) => ({value: id, label: name}))}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="b-schedule-radio-list">
|
||||||
|
<Form.Item
|
||||||
|
name="startAtUtc"
|
||||||
|
rules={[{
|
||||||
|
required: true,
|
||||||
|
message: ''
|
||||||
|
}]}
|
||||||
|
>
|
||||||
|
<Radio.Group>
|
||||||
|
{dates && dates[selectDate.format('YYYY-MM-DD')].map((el: any) => (
|
||||||
|
<Radio
|
||||||
|
key={el.startTime}
|
||||||
|
value={el.startTime}
|
||||||
|
>
|
||||||
|
{dayjs(el.startTime).format('HH:mm')} - {dayjs(el.endTime).format('HH:mm')}
|
||||||
|
</Radio>)
|
||||||
|
)}
|
||||||
|
</Radio.Group>
|
||||||
|
</Form.Item>
|
||||||
|
</div>
|
||||||
|
<Form.Item name="clientComment">
|
||||||
|
<Input.TextArea
|
||||||
|
className="b-textarea"
|
||||||
|
rows={2}
|
||||||
|
placeholder={i18nText('sessionWishes', locale)}
|
||||||
/>
|
/>
|
||||||
)}
|
</Form.Item>
|
||||||
</div>
|
</Form>
|
||||||
<div className="b-schedule-radio-list">
|
|
||||||
<Radio.Group name="radiogroupSlots" onChange={onChangeTimeSlot}>
|
|
||||||
{dates[selectDate.format('YYYY-MM-DD')].map((el: any) => {
|
|
||||||
return (<Radio key={dayjs(el.startTime).format()} value={el}>{dayjs(el.startTime).format('HH:mm')} - {dayjs(el.endTime).format('HH:mm')}</Radio>)
|
|
||||||
})}
|
|
||||||
</Radio.Group>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<Input.TextArea
|
|
||||||
className="b-textarea"
|
|
||||||
rows={2}
|
|
||||||
value={sessionData?.clientComment}
|
|
||||||
placeholder={i18nText('sessionWishes', locale)}
|
|
||||||
onChange={(e) => setSessionData({ ...sessionData, clientComment: e.target.value })}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<Button
|
<Button
|
||||||
className="btn-apply"
|
className="btn-apply"
|
||||||
onClick={() => checkSession(sessionData)}
|
onClick={onValidate}
|
||||||
>
|
>
|
||||||
{i18nText('pay', locale)}
|
{i18nText('pay', locale)}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { FC, useEffect, useState } from 'react';
|
import React, { FC, useEffect, useState } from 'react';
|
||||||
import type { StripeError } from '@stripe/stripe-js';
|
import type { StripeError } from '@stripe/stripe-js';
|
||||||
import {
|
import {
|
||||||
useStripe,
|
useStripe,
|
||||||
|
@ -8,11 +8,12 @@ import {
|
||||||
PaymentElement,
|
PaymentElement,
|
||||||
Elements,
|
Elements,
|
||||||
} from '@stripe/react-stripe-js';
|
} from '@stripe/react-stripe-js';
|
||||||
import { Form, Button } from 'antd';
|
import { Form, Button, message } from 'antd';
|
||||||
import getStripe from '../../utils/get-stripe';
|
import getStripe from '../../utils/get-stripe';
|
||||||
import { createPaymentIntent} from '../../actions/stripe';
|
import { createPaymentIntent} from '../../actions/stripe';
|
||||||
import { Payment } from '../../types/payment';
|
import { Payment } from '../../types/payment';
|
||||||
import { i18nText } from '../../i18nKeys';
|
import { i18nText } from '../../i18nKeys';
|
||||||
|
import { WithError } from '../view/WithError';
|
||||||
|
|
||||||
type PaymentFormProps = {
|
type PaymentFormProps = {
|
||||||
amount: number,
|
amount: number,
|
||||||
|
@ -20,39 +21,37 @@ type PaymentFormProps = {
|
||||||
locale: string
|
locale: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type PaymentInfo = 'initial' | 'error' | 'processing' | 'requires_payment_method' | 'requires_confirmation' | 'requires_action' | 'succeeded';
|
||||||
|
|
||||||
|
const PaymentStatus = ({ status }: { status?: PaymentInfo }) => {
|
||||||
|
switch (status) {
|
||||||
|
case 'processing':
|
||||||
|
case 'requires_payment_method':
|
||||||
|
case 'requires_confirmation':
|
||||||
|
return <h2>Processing...</h2>;
|
||||||
|
|
||||||
|
case 'requires_action':
|
||||||
|
return <h2>Authenticating...</h2>;
|
||||||
|
|
||||||
|
case 'succeeded':
|
||||||
|
return <h2>Payment Succeeded</h2>;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export const CheckoutForm: FC<PaymentFormProps> = ({ amount, sessionId, locale }) => {
|
export const CheckoutForm: FC<PaymentFormProps> = ({ amount, sessionId, locale }) => {
|
||||||
const [form] = Form.useForm<Payment>();
|
const [form] = Form.useForm<Payment>();
|
||||||
const formAmount = Form.useWatch('amount', form);
|
const formAmount = Form.useWatch('amount', form);
|
||||||
const [paymentType, setPaymentType] = useState<string>('');
|
const [paymentType, setPaymentType] = useState<string>('');
|
||||||
const [payment, setPayment] = useState<{
|
const [payment, setPayment] = useState<{
|
||||||
status: "initial" | "processing" | "error";
|
status: PaymentInfo
|
||||||
}>({ status: "initial" });
|
}>({ status: 'initial' });
|
||||||
const [errorMessage, setErrorMessage] = useState<string>('');
|
const [errorData, setErrorData] = useState<any>();
|
||||||
const stripe = useStripe();
|
const stripe = useStripe();
|
||||||
const elements = useElements();
|
const elements = useElements();
|
||||||
|
|
||||||
const PaymentStatus = ({ status }: { status: string }) => {
|
|
||||||
switch (status) {
|
|
||||||
case "processing":
|
|
||||||
case "requires_payment_method":
|
|
||||||
case "requires_confirmation":
|
|
||||||
return <h2>Processing...</h2>;
|
|
||||||
|
|
||||||
case "requires_action":
|
|
||||||
return <h2>Authenticating...</h2>;
|
|
||||||
|
|
||||||
case "succeeded":
|
|
||||||
return <h2>Payment Succeeded</h2>;
|
|
||||||
|
|
||||||
case "error":
|
|
||||||
console.log('errorMessage', errorMessage);
|
|
||||||
return null;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
elements?.update({ amount: formAmount * 100 });
|
elements?.update({ amount: formAmount * 100 });
|
||||||
}, [formAmount]);
|
}, [formAmount]);
|
||||||
|
@ -61,13 +60,15 @@ export const CheckoutForm: FC<PaymentFormProps> = ({ amount, sessionId, locale }
|
||||||
try {
|
try {
|
||||||
if (!elements || !stripe) return;
|
if (!elements || !stripe) return;
|
||||||
|
|
||||||
|
setErrorData(undefined);
|
||||||
setPayment({ status: "processing" });
|
setPayment({ status: "processing" });
|
||||||
|
|
||||||
const { error: submitError } = await elements.submit();
|
const { error: submitError } = await elements.submit();
|
||||||
|
|
||||||
if (submitError) {
|
if (submitError) {
|
||||||
setPayment({ status: "error" });
|
if (submitError.message) {
|
||||||
setErrorMessage(submitError.message ?? "An unknown error occurred");
|
message.error(submitError.message);
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -91,40 +92,45 @@ export const CheckoutForm: FC<PaymentFormProps> = ({ amount, sessionId, locale }
|
||||||
});
|
});
|
||||||
|
|
||||||
if (confirmError) {
|
if (confirmError) {
|
||||||
setPayment({ status: "error" });
|
setErrorData({
|
||||||
setErrorMessage(confirmError.message ?? "An unknown error occurred");
|
title: i18nText('errorPayment', locale),
|
||||||
|
message: confirmError.message ?? 'An unknown error occurred'
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
const { message } = err as StripeError;
|
const { message } = err as StripeError;
|
||||||
|
setErrorData({
|
||||||
setPayment({ status: "error" });
|
title: i18nText('errorPayment', locale),
|
||||||
setErrorMessage(message ?? "An unknown error occurred");
|
message: message ?? 'An unknown error occurred'
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Form form={form} onFinish={onSubmit} style={{ display: 'flex', overflow: 'hidden', flexDirection: 'column', gap: 16, justifyContent: 'space-between', alignItems: 'center' }}>
|
<WithError errorData={errorData}>
|
||||||
<div style={{ width: '100%' }}>
|
<Form form={form} onFinish={onSubmit} style={{ display: 'flex', overflow: 'hidden', flexDirection: 'column', gap: 16, justifyContent: 'space-between', alignItems: 'center' }}>
|
||||||
<PaymentElement
|
<div style={{ width: '100%' }}>
|
||||||
onChange={(e) => {
|
<PaymentElement
|
||||||
setPaymentType(e.value.type);
|
onChange={(e) => {
|
||||||
}}
|
setPaymentType(e.value.type);
|
||||||
/>
|
}}
|
||||||
</div>
|
/>
|
||||||
<div>
|
</div>
|
||||||
<PaymentStatus status={payment.status}/>
|
<div>
|
||||||
</div>
|
<PaymentStatus status={payment.status}/>
|
||||||
<Button
|
</div>
|
||||||
className="btn-apply"
|
<Button
|
||||||
htmlType="submit"
|
className="btn-apply"
|
||||||
disabled={
|
htmlType="submit"
|
||||||
!["initial", "succeeded", "error"].includes(payment.status) ||
|
disabled={
|
||||||
!stripe
|
!["initial", "succeeded", "error"].includes(payment.status) ||
|
||||||
}
|
!stripe
|
||||||
>
|
}
|
||||||
{`${i18nText('pay', locale)} ${amount}€`}
|
>
|
||||||
</Button>
|
{`${i18nText('pay', locale)} ${amount}€`}
|
||||||
</Form>
|
</Button>
|
||||||
|
</Form>
|
||||||
|
</WithError>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -143,7 +149,7 @@ export const StripeElementsForm: FC<PaymentFormProps> = ({ amount, sessionId, lo
|
||||||
colorPrimary: '#66A5AD',
|
colorPrimary: '#66A5AD',
|
||||||
colorBackground: '#F8F8F7',
|
colorBackground: '#F8F8F7',
|
||||||
colorText: '#000',
|
colorText: '#000',
|
||||||
colorDanger: '#D93E5C',
|
colorDanger: '#ff4d4f',
|
||||||
focusBoxShadow: 'none',
|
focusBoxShadow: 'none',
|
||||||
borderRadius: '8px'
|
borderRadius: '8px'
|
||||||
},
|
},
|
||||||
|
|
|
@ -16,8 +16,8 @@ export const WithError: FC<WithErrorProps> = ({
|
||||||
return (
|
return (
|
||||||
<Result
|
<Result
|
||||||
status="error"
|
status="error"
|
||||||
title="Submission Failed"
|
title={errorData?.title}
|
||||||
subTitle="Please check and modify the following information before resubmitting."
|
subTitle={errorData?.message}
|
||||||
extra={refresh ? (
|
extra={refresh ? (
|
||||||
<Button type="primary" onClick={refresh}>
|
<Button type="primary" onClick={refresh}>
|
||||||
Refresh page
|
Refresh page
|
||||||
|
|
|
@ -148,8 +148,8 @@ export default {
|
||||||
mExperiences: 'Führungserfahrung',
|
mExperiences: 'Führungserfahrung',
|
||||||
pay: 'Zahlung',
|
pay: 'Zahlung',
|
||||||
sessionWishes: 'Schreiben Sie Ihre Wünsche zur Sitzung',
|
sessionWishes: 'Schreiben Sie Ihre Wünsche zur Sitzung',
|
||||||
successPayment: 'Success',
|
successPayment: 'Erfolgreiche Zahlung',
|
||||||
errorPayment: 'Error',
|
errorPayment: 'Zahlungsfehler',
|
||||||
errors: {
|
errors: {
|
||||||
invalidEmail: 'Die E-Mail-Adresse ist ungültig',
|
invalidEmail: 'Die E-Mail-Adresse ist ungültig',
|
||||||
emptyEmail: 'Bitte geben Sie Ihre E-Mail ein',
|
emptyEmail: 'Bitte geben Sie Ihre E-Mail ein',
|
||||||
|
|
|
@ -148,8 +148,8 @@ export default {
|
||||||
mExperiences: 'Managerial Experience',
|
mExperiences: 'Managerial Experience',
|
||||||
pay: 'Pay',
|
pay: 'Pay',
|
||||||
sessionWishes: 'Write your wishes about the session',
|
sessionWishes: 'Write your wishes about the session',
|
||||||
successPayment: 'Success',
|
successPayment: 'Successful Payment',
|
||||||
errorPayment: 'Error',
|
errorPayment: 'Payment Error',
|
||||||
errors: {
|
errors: {
|
||||||
invalidEmail: 'The email address is not valid',
|
invalidEmail: 'The email address is not valid',
|
||||||
emptyEmail: 'Please enter your E-mail',
|
emptyEmail: 'Please enter your E-mail',
|
||||||
|
|
|
@ -148,8 +148,8 @@ export default {
|
||||||
mExperiences: 'Experiencia de dirección',
|
mExperiences: 'Experiencia de dirección',
|
||||||
pay: 'Pago',
|
pay: 'Pago',
|
||||||
sessionWishes: 'Escribe tus deseos sobre la sesión',
|
sessionWishes: 'Escribe tus deseos sobre la sesión',
|
||||||
successPayment: 'Success',
|
successPayment: 'Pago Exitoso',
|
||||||
errorPayment: 'Error',
|
errorPayment: 'Error de Pago',
|
||||||
errors: {
|
errors: {
|
||||||
invalidEmail: 'La dirección de correo electrónico no es válida',
|
invalidEmail: 'La dirección de correo electrónico no es válida',
|
||||||
emptyEmail: 'Introduce tu correo electrónico',
|
emptyEmail: 'Introduce tu correo electrónico',
|
||||||
|
|
|
@ -148,8 +148,8 @@ export default {
|
||||||
mExperiences: 'Expérience en gestion',
|
mExperiences: 'Expérience en gestion',
|
||||||
pay: 'Paiement',
|
pay: 'Paiement',
|
||||||
sessionWishes: 'Écrivez vos souhaits concernant la session',
|
sessionWishes: 'Écrivez vos souhaits concernant la session',
|
||||||
successPayment: 'Success',
|
successPayment: 'Paiement Réussi',
|
||||||
errorPayment: 'Error',
|
errorPayment: 'Erreur de Paiement',
|
||||||
errors: {
|
errors: {
|
||||||
invalidEmail: 'L\'adresse e-mail n\'est pas valide',
|
invalidEmail: 'L\'adresse e-mail n\'est pas valide',
|
||||||
emptyEmail: 'Veuillez saisir votre e-mail',
|
emptyEmail: 'Veuillez saisir votre e-mail',
|
||||||
|
|
|
@ -148,8 +148,8 @@ export default {
|
||||||
mExperiences: 'Esperienza manageriale',
|
mExperiences: 'Esperienza manageriale',
|
||||||
pay: 'Pagamento',
|
pay: 'Pagamento',
|
||||||
sessionWishes: 'Scrivi i tuoi desideri riguardo alla sessione',
|
sessionWishes: 'Scrivi i tuoi desideri riguardo alla sessione',
|
||||||
successPayment: 'Success',
|
successPayment: 'Pagamento Riuscito',
|
||||||
errorPayment: 'Error',
|
errorPayment: 'Errore di Pagamento',
|
||||||
errors: {
|
errors: {
|
||||||
invalidEmail: 'L\'indirizzo e-mail non è valido',
|
invalidEmail: 'L\'indirizzo e-mail non è valido',
|
||||||
emptyEmail: 'Inserisci l\'e-mail',
|
emptyEmail: 'Inserisci l\'e-mail',
|
||||||
|
|
|
@ -148,8 +148,8 @@ export default {
|
||||||
mExperiences: 'Управленческий опыт',
|
mExperiences: 'Управленческий опыт',
|
||||||
pay: 'Оплата',
|
pay: 'Оплата',
|
||||||
sessionWishes: 'Напишите свои пожелания по поводу сессии',
|
sessionWishes: 'Напишите свои пожелания по поводу сессии',
|
||||||
successPayment: 'Success',
|
successPayment: 'Успешная оплата',
|
||||||
errorPayment: 'Error',
|
errorPayment: 'Ошибка оплаты',
|
||||||
errors: {
|
errors: {
|
||||||
invalidEmail: 'Адрес электронной почты недействителен',
|
invalidEmail: 'Адрес электронной почты недействителен',
|
||||||
emptyEmail: 'Пожалуйста, введите ваш E-mail',
|
emptyEmail: 'Пожалуйста, введите ваш E-mail',
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
.ant-form-item-has-error .ant-radio-inner {
|
||||||
|
border-color: #ff4d4f !important;
|
||||||
|
}
|
|
@ -17,6 +17,12 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.ant-select-status-error {
|
||||||
|
.ant-select-selector {
|
||||||
|
border-color: #ff4d4f !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.ant-select-selection-overflow-item {
|
.ant-select-selection-overflow-item {
|
||||||
margin-right: 4px;
|
margin-right: 4px;
|
||||||
}
|
}
|
||||||
|
@ -88,6 +94,12 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.ant-select-status-error {
|
||||||
|
.ant-select-selector {
|
||||||
|
border-color: #ff4d4f !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.ant-select-arrow {
|
.ant-select-arrow {
|
||||||
color: #2c7873 !important;
|
color: #2c7873 !important;
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,3 +11,4 @@
|
||||||
@import "_timepicker.scss";
|
@import "_timepicker.scss";
|
||||||
@import "_calendar.scss";
|
@import "_calendar.scss";
|
||||||
@import "_schedule.scss";
|
@import "_schedule.scss";
|
||||||
|
@import "_radio.scss";
|
||||||
|
|
Loading…
Reference in New Issue