Merge branch 'develop'
# Conflicts: # src/components/palmistry/step-subscription-plan/step-subscription-plan.tsx
This commit is contained in:
commit
f4c78ece2e
14
package-lock.json
generated
14
package-lock.json
generated
@ -17,6 +17,7 @@
|
|||||||
"html-react-parser": "^3.0.16",
|
"html-react-parser": "^3.0.16",
|
||||||
"i18next": "^22.5.0",
|
"i18next": "^22.5.0",
|
||||||
"i18next-react-postprocessor": "^3.1.0",
|
"i18next-react-postprocessor": "^3.1.0",
|
||||||
|
"moment": "^2.30.1",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-circular-progressbar": "^2.1.0",
|
"react-circular-progressbar": "^2.1.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
@ -2665,6 +2666,14 @@
|
|||||||
"node": "*"
|
"node": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/moment": {
|
||||||
|
"version": "2.30.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz",
|
||||||
|
"integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==",
|
||||||
|
"engines": {
|
||||||
|
"node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/ms": {
|
"node_modules/ms": {
|
||||||
"version": "2.1.2",
|
"version": "2.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||||
@ -5381,6 +5390,11 @@
|
|||||||
"brace-expansion": "^1.1.7"
|
"brace-expansion": "^1.1.7"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"moment": {
|
||||||
|
"version": "2.30.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz",
|
||||||
|
"integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how=="
|
||||||
|
},
|
||||||
"ms": {
|
"ms": {
|
||||||
"version": "2.1.2",
|
"version": "2.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||||
|
|||||||
@ -23,6 +23,7 @@
|
|||||||
"html-react-parser": "^3.0.16",
|
"html-react-parser": "^3.0.16",
|
||||||
"i18next": "^22.5.0",
|
"i18next": "^22.5.0",
|
||||||
"i18next-react-postprocessor": "^3.1.0",
|
"i18next-react-postprocessor": "^3.1.0",
|
||||||
|
"moment": "^2.30.1",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-circular-progressbar": "^2.1.0",
|
"react-circular-progressbar": "^2.1.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
|
|||||||
@ -34,6 +34,7 @@ const api = {
|
|||||||
auth: createMethod<AuthTokens.Payload, AuthTokens.Response>(AuthTokens.createRequest),
|
auth: createMethod<AuthTokens.Payload, AuthTokens.Response>(AuthTokens.createRequest),
|
||||||
appleAuth: createMethod<AppleAuth.Payload, AppleAuth.Response>(AppleAuth.createRequest),
|
appleAuth: createMethod<AppleAuth.Payload, AppleAuth.Response>(AppleAuth.createRequest),
|
||||||
googleAuth: createMethod<GoogleAuth.Payload, GoogleAuth.Response>(GoogleAuth.createRequest),
|
googleAuth: createMethod<GoogleAuth.Payload, GoogleAuth.Response>(GoogleAuth.createRequest),
|
||||||
|
getRealToken: createMethod<AuthTokens.PayloadGetRealToken, AuthTokens.ResponseGetRealToken>(AuthTokens.createGetRealTokenRequest),
|
||||||
getAppConfig: createMethod<Apps.Payload, Apps.Response>(Apps.createRequest),
|
getAppConfig: createMethod<Apps.Payload, Apps.Response>(Apps.createRequest),
|
||||||
getElement: createMethod<Element.Payload, Element.Response>(Element.createRequest),
|
getElement: createMethod<Element.Payload, Element.Response>(Element.createRequest),
|
||||||
getElements: createMethod<Elements.Payload, Elements.Response>(Elements.createRequest),
|
getElements: createMethod<Elements.Payload, Elements.Response>(Elements.createRequest),
|
||||||
@ -72,6 +73,8 @@ const api = {
|
|||||||
createSinglePayment: createMethod<SinglePayment.PayloadPost, SinglePayment.ResponsePost>(SinglePayment.createRequestPost),
|
createSinglePayment: createMethod<SinglePayment.PayloadPost, SinglePayment.ResponsePost>(SinglePayment.createRequestPost),
|
||||||
checkProductPurchased: createMethod<Products.PayloadGet, Products.ResponseGet>(Products.createRequest),
|
checkProductPurchased: createMethod<Products.PayloadGet, Products.ResponseGet>(Products.createRequest),
|
||||||
getPalmistryLines: createMethod<Palmistry.Payload, Palmistry.Response>(Palmistry.createRequest),
|
getPalmistryLines: createMethod<Palmistry.Payload, Palmistry.Response>(Palmistry.createRequest),
|
||||||
|
// New Authorization
|
||||||
|
authorization: createMethod<User.ICreateAuthorizePayload, User.ICreateAuthorizeResponse>(User.createAuthorizeRequest),
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ApiContextValue = typeof api
|
export type ApiContextValue = typeof api
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import routes from "@/routes";
|
import routes from "@/routes";
|
||||||
import { AuthToken } from "../types";
|
import { AuthToken } from "../types";
|
||||||
import { User } from "./User";
|
import { User } from "./User";
|
||||||
import { getBaseHeaders } from "../utils";
|
import { getAuthHeaders, getBaseHeaders } from "../utils";
|
||||||
|
|
||||||
export interface PayloadRegisterByEmail {
|
export interface PayloadRegisterByEmail {
|
||||||
email: string;
|
email: string;
|
||||||
@ -41,3 +41,16 @@ export const createRequest = (payload: Payload): Request => {
|
|||||||
const body = JSON.stringify({ auth: { ...payload } });
|
const body = JSON.stringify({ auth: { ...payload } });
|
||||||
return new Request(url, { method: "POST", headers: getBaseHeaders(), body });
|
return new Request(url, { method: "POST", headers: getBaseHeaders(), body });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export interface PayloadGetRealToken {
|
||||||
|
token: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ResponseGetRealToken {
|
||||||
|
token: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const createGetRealTokenRequest = ({ token }: PayloadGetRealToken): Request => {
|
||||||
|
const url = new URL(routes.server.dApiGetRealToken());
|
||||||
|
return new Request(url, { method: "POST", headers: getAuthHeaders(token) });
|
||||||
|
}
|
||||||
@ -1,6 +1,6 @@
|
|||||||
import routes from "@/routes";
|
import routes from "@/routes";
|
||||||
import { AuthPayload } from "../types";
|
import { AuthPayload } from "../types";
|
||||||
import { getAuthHeaders } from "../utils";
|
import { getAuthHeaders, getBaseHeaders } from "../utils";
|
||||||
|
|
||||||
export type GetPayload = AuthPayload;
|
export type GetPayload = AuthPayload;
|
||||||
|
|
||||||
@ -130,3 +130,57 @@ export const createPatchRequest = ({ token, user }: PatchPayload): Request => {
|
|||||||
body,
|
body,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export enum ESourceAuthorization {
|
||||||
|
"aura.main" = "aura.main",
|
||||||
|
"aura.palmistry" = "aura.palmistry",
|
||||||
|
"aura.chat" = "aura.chat",
|
||||||
|
"aura.moons" = "aura.moons"
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum EGender {
|
||||||
|
"male" = "male",
|
||||||
|
"female" = "female",
|
||||||
|
"other" = "other"
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ERelationshipStatus {
|
||||||
|
"single",
|
||||||
|
"relationship",
|
||||||
|
"married",
|
||||||
|
"complicated",
|
||||||
|
"other"
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ICreateAuthorizeUser {
|
||||||
|
name: string;
|
||||||
|
birthdate: string | null;
|
||||||
|
gender: EGender;
|
||||||
|
birthplace: {
|
||||||
|
address?: string;
|
||||||
|
coords?: string;
|
||||||
|
}
|
||||||
|
relationship_status: ERelationshipStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ICreateAuthorizePayload {
|
||||||
|
email: string;
|
||||||
|
locale: string;
|
||||||
|
timezone: string;
|
||||||
|
source: ESourceAuthorization;
|
||||||
|
profile?: Partial<ICreateAuthorizeUser>;
|
||||||
|
partner?: Partial<Exclude<ICreateAuthorizeUser, "relationship_status">>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ICreateAuthorizeResponse {
|
||||||
|
token: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const createAuthorizeRequest = (data: ICreateAuthorizePayload): Request => {
|
||||||
|
const body = JSON.stringify(data);
|
||||||
|
return new Request(routes.server.dApiAuth(), {
|
||||||
|
method: "POST",
|
||||||
|
headers: getBaseHeaders(),
|
||||||
|
body,
|
||||||
|
});
|
||||||
|
}
|
||||||
@ -198,11 +198,14 @@ function App(): JSX.Element {
|
|||||||
(async () => {
|
(async () => {
|
||||||
if (!jwtToken) return;
|
if (!jwtToken) return;
|
||||||
logout();
|
logout();
|
||||||
const auth = await api.auth({ jwt: jwtToken });
|
try {
|
||||||
const {
|
const { token } = await api.getRealToken({ token: jwtToken });
|
||||||
auth: { token, user },
|
const { user } = await api.getUser({ token });
|
||||||
} = auth;
|
signUp(token, user);
|
||||||
signUp(token, user);
|
} catch (error) {
|
||||||
|
console.log("Error of get real token or get user: ");
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
})();
|
})();
|
||||||
}, [api, jwtToken, logout, signUp]);
|
}, [api, jwtToken, logout, signUp]);
|
||||||
|
|
||||||
@ -318,6 +321,9 @@ function App(): JSX.Element {
|
|||||||
no: routes.client.epeGender(),
|
no: routes.client.epeGender(),
|
||||||
force: routes.client.epeBirthdate(),
|
force: routes.client.epeBirthdate(),
|
||||||
},
|
},
|
||||||
|
purchasedProduct: {
|
||||||
|
no: routes.client.epePayment(),
|
||||||
|
},
|
||||||
}}
|
}}
|
||||||
requiredParameters={[birthdate, isForceShortPath || gender]}
|
requiredParameters={[birthdate, isForceShortPath || gender]}
|
||||||
/>
|
/>
|
||||||
@ -487,6 +493,9 @@ function App(): JSX.Element {
|
|||||||
no: routes.client.advisorChatGender(),
|
no: routes.client.advisorChatGender(),
|
||||||
force: routes.client.advisorChatBirthdate(),
|
force: routes.client.advisorChatBirthdate(),
|
||||||
},
|
},
|
||||||
|
purchasedProduct: {
|
||||||
|
no: routes.client.advisorChatPayment(),
|
||||||
|
},
|
||||||
}}
|
}}
|
||||||
requiredParameters={[
|
requiredParameters={[
|
||||||
birthdate,
|
birthdate,
|
||||||
@ -1075,6 +1084,7 @@ interface IShortPathOutletProps {
|
|||||||
function ShortPathOutlet(props: IShortPathOutletProps): JSX.Element {
|
function ShortPathOutlet(props: IShortPathOutletProps): JSX.Element {
|
||||||
const { productKey, requiredParameters, redirectUrls, isProductPage } = props;
|
const { productKey, requiredParameters, redirectUrls, isProductPage } = props;
|
||||||
const { user, token } = useAuth();
|
const { user, token } = useAuth();
|
||||||
|
|
||||||
const api = useApi();
|
const api = useApi();
|
||||||
const isForce = useSelector(selectors.selectIsForceShortPath);
|
const isForce = useSelector(selectors.selectIsForceShortPath);
|
||||||
|
|
||||||
|
|||||||
@ -4,18 +4,17 @@ import { useNavigate, useParams } from "react-router-dom";
|
|||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
import { actions, selectors } from "@/store";
|
import { actions, selectors } from "@/store";
|
||||||
import { getClientTimezone } from "@/locales";
|
import { useApi } from "@/api";
|
||||||
import { useAuth } from "@/auth";
|
|
||||||
import { useApi, ApiError, extractErrorMessage } from "@/api";
|
|
||||||
import Title from "../Title";
|
import Title from "../Title";
|
||||||
import Policy from "../Policy";
|
import Policy from "../Policy";
|
||||||
import EmailInput from "./EmailInput";
|
import EmailInput from "./EmailInput";
|
||||||
import MainButton from "../MainButton";
|
import MainButton from "../MainButton";
|
||||||
import Loader, { LoaderColor } from "../Loader";
|
import Loader, { LoaderColor } from "../Loader";
|
||||||
import ErrorText from "../ErrorText";
|
|
||||||
import routes from "@/routes";
|
import routes from "@/routes";
|
||||||
import NameInput from "./NameInput";
|
import NameInput from "./NameInput";
|
||||||
import { ISubscriptionPlan } from "@/api/resources/SubscriptionPlans";
|
import { ISubscriptionPlan } from "@/api/resources/SubscriptionPlans";
|
||||||
|
import { useAuthentication } from "@/hooks/authentication/use-authentication";
|
||||||
|
import { ESourceAuthorization } from "@/api/resources/User";
|
||||||
|
|
||||||
interface IEmailEnterPage {
|
interface IEmailEnterPage {
|
||||||
redirectUrl?: string;
|
redirectUrl?: string;
|
||||||
@ -27,33 +26,23 @@ function EmailEnterPage({
|
|||||||
isRequiredName = false,
|
isRequiredName = false,
|
||||||
}: IEmailEnterPage): JSX.Element {
|
}: IEmailEnterPage): JSX.Element {
|
||||||
const api = useApi();
|
const api = useApi();
|
||||||
const { signUp } = useAuth();
|
|
||||||
const { t, i18n } = useTranslation();
|
const { t, i18n } = useTranslation();
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [email, setEmail] = useState("");
|
const [email, setEmail] = useState("");
|
||||||
const [name, setName] = useState("");
|
const [name, setName] = useState("");
|
||||||
const birthday = useSelector(selectors.selectBirthday);
|
|
||||||
const [isDisabled, setIsDisabled] = useState(true);
|
const [isDisabled, setIsDisabled] = useState(true);
|
||||||
const [isValidEmail, setIsValidEmail] = useState(false);
|
const [isValidEmail, setIsValidEmail] = useState(false);
|
||||||
const [isValidName, setIsValidName] = useState(!isRequiredName);
|
const [isValidName, setIsValidName] = useState(!isRequiredName);
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
|
||||||
const [isAuth, setIsAuth] = useState(false);
|
const [isAuth, setIsAuth] = useState(false);
|
||||||
const [apiError, setApiError] = useState<ApiError | null>(null);
|
|
||||||
const [error, setError] = useState<boolean>(false);
|
|
||||||
const [subPlans, setSubPlans] = useState<ISubscriptionPlan[]>([]);
|
const [subPlans, setSubPlans] = useState<ISubscriptionPlan[]>([]);
|
||||||
const activeSubPlanFromStore = useSelector(selectors.selectActiveSubPlan);
|
const activeSubPlanFromStore = useSelector(selectors.selectActiveSubPlan);
|
||||||
const [activeSubPlan, setActiveSubPlan] = useState<ISubscriptionPlan | null>(
|
const [activeSubPlan, setActiveSubPlan] = useState<ISubscriptionPlan | null>(
|
||||||
activeSubPlanFromStore
|
activeSubPlanFromStore
|
||||||
);
|
);
|
||||||
const timezone = getClientTimezone();
|
|
||||||
const locale = i18n.language;
|
const locale = i18n.language;
|
||||||
const { subPlan } = useParams();
|
const { subPlan } = useParams();
|
||||||
const {
|
const { error, isLoading, authorization } = useAuthentication();
|
||||||
gender,
|
|
||||||
birthPlace,
|
|
||||||
// flowChoice
|
|
||||||
} = useSelector(selectors.selectQuestionnaire);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (subPlan) {
|
if (subPlan) {
|
||||||
@ -99,6 +88,13 @@ function EmailEnterPage({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleValidName = (name: string) => {
|
const handleValidName = (name: string) => {
|
||||||
|
if (name) {
|
||||||
|
dispatch(
|
||||||
|
actions.user.update({
|
||||||
|
username: name,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
setName(name);
|
setName(name);
|
||||||
setIsValidName(true);
|
setIsValidName(true);
|
||||||
};
|
};
|
||||||
@ -112,62 +108,27 @@ function EmailEnterPage({
|
|||||||
}, [isValidEmail, isValidName, email, name]);
|
}, [isValidEmail, isValidName, email, name]);
|
||||||
|
|
||||||
const handleClick = () => {
|
const handleClick = () => {
|
||||||
authorization();
|
authorize();
|
||||||
};
|
};
|
||||||
|
|
||||||
const authorization = async () => {
|
const authorize = async () => {
|
||||||
try {
|
let source = ESourceAuthorization["aura.main"];
|
||||||
setIsLoading(true);
|
if (window.location.pathname.includes("advisor-chat")) {
|
||||||
const auth = await api.auth({ email, timezone, locale });
|
source = ESourceAuthorization["aura.chat"];
|
||||||
const {
|
|
||||||
auth: { token, user },
|
|
||||||
} = auth;
|
|
||||||
signUp(token, user);
|
|
||||||
const payload = {
|
|
||||||
user: {
|
|
||||||
profile_attributes: {
|
|
||||||
birthday,
|
|
||||||
gender: gender.length ? gender : "male",
|
|
||||||
full_name: name,
|
|
||||||
// relationship_status: !!flowChoice.length ? flowChoice : null,
|
|
||||||
},
|
|
||||||
birthplace_attributes: { address: birthPlace },
|
|
||||||
},
|
|
||||||
token,
|
|
||||||
};
|
|
||||||
const updatedUser = await api.updateUser(payload).catch((error) => {
|
|
||||||
console.log("Error: ", error);
|
|
||||||
});
|
|
||||||
if (updatedUser?.user) {
|
|
||||||
dispatch(actions.user.update(updatedUser.user));
|
|
||||||
}
|
|
||||||
if (name) {
|
|
||||||
dispatch(
|
|
||||||
actions.user.update({
|
|
||||||
username: name,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
dispatch(actions.status.update("registred"));
|
|
||||||
dispatch(
|
|
||||||
actions.payment.update({
|
|
||||||
activeSubPlan,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
setIsLoading(false);
|
|
||||||
setIsAuth(true);
|
|
||||||
setTimeout(() => {
|
|
||||||
navigate(redirectUrl);
|
|
||||||
}, 1000);
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error);
|
|
||||||
if (error instanceof ApiError) {
|
|
||||||
setApiError(error as ApiError);
|
|
||||||
} else {
|
|
||||||
setError(true);
|
|
||||||
}
|
|
||||||
setIsLoading(false);
|
|
||||||
}
|
}
|
||||||
|
if (window.location.pathname.includes("/epe/")) {
|
||||||
|
source = ESourceAuthorization["aura.moons"];
|
||||||
|
}
|
||||||
|
await authorization(email, source);
|
||||||
|
dispatch(
|
||||||
|
actions.payment.update({
|
||||||
|
activeSubPlan,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
setIsAuth(true);
|
||||||
|
setTimeout(() => {
|
||||||
|
navigate(redirectUrl);
|
||||||
|
}, 1000);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -220,9 +181,9 @@ function EmailEnterPage({
|
|||||||
>
|
>
|
||||||
{isLoading && <Loader color={LoaderColor.White} />}
|
{isLoading && <Loader color={LoaderColor.White} />}
|
||||||
{!isLoading &&
|
{!isLoading &&
|
||||||
!(!apiError && !error && !isLoading && isAuth) &&
|
!(!error?.length && !isLoading && isAuth) &&
|
||||||
t("_continue")}
|
t("_continue")}
|
||||||
{!apiError && !error && !isLoading && isAuth && (
|
{!error?.length && !isLoading && isAuth && (
|
||||||
<img
|
<img
|
||||||
className={styles["success-icon"]}
|
className={styles["success-icon"]}
|
||||||
src="/SuccessIcon.png"
|
src="/SuccessIcon.png"
|
||||||
@ -230,18 +191,11 @@ function EmailEnterPage({
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</MainButton>
|
</MainButton>
|
||||||
{(error || apiError) && (
|
{!!error?.length && (
|
||||||
<Title variant="h3" style={{ color: "red", margin: 0 }}>
|
<Title variant="h3" style={{ color: "red", margin: 0 }}>
|
||||||
Something went wrong
|
Something went wrong
|
||||||
</Title>
|
</Title>
|
||||||
)}
|
)}
|
||||||
{apiError && (
|
|
||||||
<ErrorText
|
|
||||||
size="medium"
|
|
||||||
isShown={Boolean(apiError)}
|
|
||||||
message={apiError ? extractErrorMessage(apiError) : null}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -26,7 +26,7 @@ function TrialChoicePage() {
|
|||||||
const email = useSelector(selectors.selectEmail);
|
const email = useSelector(selectors.selectEmail);
|
||||||
const [subPlans, setSubPlans] = useState<ISubscriptionPlan[]>([]);
|
const [subPlans, setSubPlans] = useState<ISubscriptionPlan[]>([]);
|
||||||
const [isDisabled, setIsDisabled] = useState(true);
|
const [isDisabled, setIsDisabled] = useState(true);
|
||||||
const allowedPlans = useMemo(() => ["stripe.37"], []);
|
const allowedPlans = useMemo(() => [""], []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
(async () => {
|
(async () => {
|
||||||
|
|||||||
@ -1,38 +1,27 @@
|
|||||||
import React from 'react';
|
import React from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { useDispatch } from "react-redux";
|
import { useDispatch } from "react-redux";
|
||||||
|
import useSteps from "@/hooks/palmistry/use-steps";
|
||||||
import { PatchPayload } from "@/api/resources/User";
|
import Button from "@/components/palmistry/button/button";
|
||||||
import { Step } from '@/hooks/palmistry/use-steps';
|
import Input from "@/components/palmistry/input/input";
|
||||||
import { useAuth } from "@/auth";
|
|
||||||
import { useApi, ApiError, extractErrorMessage } from "@/api";
|
|
||||||
import useSteps from '@/hooks/palmistry/use-steps';
|
|
||||||
import Button from '@/components/palmistry/button/button';
|
|
||||||
import Input from '@/components/palmistry/input/input';
|
|
||||||
import { getClientTimezone } from "@/locales";
|
|
||||||
import { actions } from "@/store";
|
import { actions } from "@/store";
|
||||||
import Title from "@/components/Title";
|
import Title from "@/components/Title";
|
||||||
import ErrorText from "@/components/ErrorText";
|
|
||||||
import Loader, { LoaderColor } from "@/components/Loader";
|
import Loader, { LoaderColor } from "@/components/Loader";
|
||||||
|
import { useAuthentication } from "@/hooks/authentication/use-authentication";
|
||||||
|
import { ESourceAuthorization } from "@/api/resources/User";
|
||||||
|
|
||||||
const emailRegex = /^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+$/;
|
const emailRegex = /^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+$/;
|
||||||
|
|
||||||
export default function StepEmail() {
|
export default function StepEmail() {
|
||||||
const api = useApi();
|
const { t } = useTranslation();
|
||||||
const { signUp } = useAuth();
|
|
||||||
const { t, i18n } = useTranslation();
|
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
const steps = useSteps();
|
const steps = useSteps();
|
||||||
|
|
||||||
const [email, setEmail] = React.useState(steps.storedValue);
|
const [email, setEmail] = React.useState(steps.storedValue);
|
||||||
const [emailIsValid, setEmailIsValid] = React.useState(false);
|
const [emailIsValid, setEmailIsValid] = React.useState(false);
|
||||||
const [isLoading, setIsLoading] = React.useState(false);
|
|
||||||
const [isAuth, setIsAuth] = React.useState(false);
|
const [isAuth, setIsAuth] = React.useState(false);
|
||||||
const [apiError, setApiError] = React.useState<ApiError | null>(null);
|
const { error, isLoading, authorization } = useAuthentication();
|
||||||
const [error, setError] = React.useState<boolean>(false);
|
|
||||||
const timezone = getClientTimezone();
|
|
||||||
const locale = i18n.language;
|
|
||||||
|
|
||||||
const onChangeEmail = (value: string) => {
|
const onChangeEmail = (value: string) => {
|
||||||
setEmail(value);
|
setEmail(value);
|
||||||
@ -49,51 +38,16 @@ export default function StepEmail() {
|
|||||||
if (emailIsValid) {
|
if (emailIsValid) {
|
||||||
dispatch(actions.form.addEmail(email));
|
dispatch(actions.form.addEmail(email));
|
||||||
}
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [email]);
|
}, [email]);
|
||||||
|
|
||||||
const authorization = async () => {
|
const authorize = async () => {
|
||||||
try {
|
await authorization(email, ESourceAuthorization["aura.palmistry"]);
|
||||||
setIsLoading(true);
|
setIsAuth(true);
|
||||||
const auth = await api.auth({ email, timezone, locale });
|
|
||||||
const { auth: { token, user } } = auth;
|
|
||||||
|
|
||||||
signUp(token, user);
|
|
||||||
const payload: PatchPayload = {
|
|
||||||
user: {
|
|
||||||
profile_attributes: {
|
|
||||||
birthday: steps.getStoredValue(Step.Birthdate),
|
|
||||||
gender: steps.getStoredValue(Step.Gender),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
token,
|
|
||||||
};
|
|
||||||
|
|
||||||
const relationshipStatus = steps.getStoredValue(Step.RelationshipStatus);
|
|
||||||
if (relationshipStatus) {
|
|
||||||
payload.user.profile_attributes!.relationship_status = relationshipStatus;
|
|
||||||
}
|
|
||||||
|
|
||||||
const updatedUser = await api.updateUser(payload).catch((error) => console.log("Error: ", error));
|
|
||||||
|
|
||||||
if (updatedUser?.user) dispatch(actions.user.update(updatedUser.user));
|
|
||||||
|
|
||||||
dispatch(actions.status.update("registred"));
|
|
||||||
|
|
||||||
setIsLoading(false);
|
|
||||||
setIsAuth(true);
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error);
|
|
||||||
if (error instanceof ApiError) {
|
|
||||||
setApiError(error as ApiError);
|
|
||||||
} else {
|
|
||||||
setError(true);
|
|
||||||
}
|
|
||||||
setIsLoading(false);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const onNext = async () => {
|
const onNext = async () => {
|
||||||
await authorization();
|
await authorize();
|
||||||
steps.saveCurrent(email);
|
steps.saveCurrent(email);
|
||||||
steps.goNext();
|
steps.goNext();
|
||||||
};
|
};
|
||||||
@ -101,7 +55,9 @@ export default function StepEmail() {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="palmistry-container__title-wrapper">
|
<div className="palmistry-container__title-wrapper">
|
||||||
<h2>Enter your email to get your advanced Palmistry reading with AURA</h2>
|
<h2>
|
||||||
|
Enter your email to get your advanced Palmistry reading with AURA
|
||||||
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Input
|
<Input
|
||||||
@ -119,12 +75,20 @@ export default function StepEmail() {
|
|||||||
|
|
||||||
<div className="palmistry-container__policy">
|
<div className="palmistry-container__policy">
|
||||||
<p>
|
<p>
|
||||||
By clicking "Continue" below you agree to AURA{' '}
|
By clicking "Continue" below you agree to AURA{" "}
|
||||||
<a href="https://aura.wit.life/terms" target="_blank" rel="noreferrer nofollow">
|
<a
|
||||||
|
href="https://aura.wit.life/terms"
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer nofollow"
|
||||||
|
>
|
||||||
EULA
|
EULA
|
||||||
</a>{' '}
|
</a>{" "}
|
||||||
and{' '}
|
and{" "}
|
||||||
<a href="https://aura.wit.life/privacy" target="_blank" rel="noreferrer nofollow">
|
<a
|
||||||
|
href="https://aura.wit.life/privacy"
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer nofollow"
|
||||||
|
>
|
||||||
Privacy Policy
|
Privacy Policy
|
||||||
</a>
|
</a>
|
||||||
.
|
.
|
||||||
@ -140,9 +104,11 @@ export default function StepEmail() {
|
|||||||
>
|
>
|
||||||
{isLoading && <Loader color={LoaderColor.White} />}
|
{isLoading && <Loader color={LoaderColor.White} />}
|
||||||
|
|
||||||
{!isLoading && !(!apiError && !error && !isLoading && isAuth) && t("_continue")}
|
{!isLoading &&
|
||||||
|
!(!error?.length && !isLoading && isAuth) &&
|
||||||
|
t("_continue")}
|
||||||
|
|
||||||
{!apiError && !error && !isLoading && isAuth && (
|
{!error?.length && !isLoading && isAuth && (
|
||||||
<img
|
<img
|
||||||
style={{ height: "30px", width: "auto" }}
|
style={{ height: "30px", width: "auto" }}
|
||||||
src="/SuccessIcon.png"
|
src="/SuccessIcon.png"
|
||||||
@ -153,19 +119,11 @@ export default function StepEmail() {
|
|||||||
|
|
||||||
{isLoading && <Loader color={LoaderColor.White} />}
|
{isLoading && <Loader color={LoaderColor.White} />}
|
||||||
|
|
||||||
{(error || apiError) && (
|
{error?.length && (
|
||||||
<Title variant="h3" style={{ color: "red", margin: 0 }}>
|
<Title variant="h3" style={{ color: "red", margin: 0 }}>
|
||||||
Something went wrong
|
Something went wrong
|
||||||
</Title>
|
</Title>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{apiError && (
|
|
||||||
<ErrorText
|
|
||||||
size="medium"
|
|
||||||
isShown={Boolean(apiError)}
|
|
||||||
message={apiError ? extractErrorMessage(apiError) : null}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,10 +1,14 @@
|
|||||||
import Button from '../button/button';
|
import Button from "../button/button";
|
||||||
import useSteps, { GenderChoice } from '../../../hooks/palmistry/use-steps';
|
import useSteps, { GenderChoice } from "../../../hooks/palmistry/use-steps";
|
||||||
|
import { useDispatch } from "react-redux";
|
||||||
|
import { actions } from "@/store";
|
||||||
|
|
||||||
export default function StepGender() {
|
export default function StepGender() {
|
||||||
const steps = useSteps();
|
const steps = useSteps();
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
const onNext = (choice: GenderChoice) => {
|
const onNext = (choice: GenderChoice) => {
|
||||||
|
dispatch(actions.questionnaire.update({ gender: choice }));
|
||||||
steps.saveCurrent(choice);
|
steps.saveCurrent(choice);
|
||||||
steps.goNext(choice);
|
steps.goNext(choice);
|
||||||
};
|
};
|
||||||
@ -14,9 +18,10 @@ export default function StepGender() {
|
|||||||
<h3 className="palmistry-container__header">What’s your gender?</h3>
|
<h3 className="palmistry-container__header">What’s your gender?</h3>
|
||||||
|
|
||||||
<p className="palmistry-container__description">
|
<p className="palmistry-container__description">
|
||||||
In Palmistry, everyone is a blend of masculine and feminine, so it helps to know yours.
|
In Palmistry, everyone is a blend of masculine and feminine, so it helps
|
||||||
|
to know yours.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div className="palmistry-container__button-wrapper">
|
<div className="palmistry-container__button-wrapper">
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
|
|||||||
@ -23,7 +23,7 @@ export default function StepSubscriptionPlan() {
|
|||||||
const api = useApi();
|
const api = useApi();
|
||||||
const { i18n } = useTranslation();
|
const { i18n } = useTranslation();
|
||||||
const activeSubPlanFromStore = useSelector(selectors.selectActiveSubPlan);
|
const activeSubPlanFromStore = useSelector(selectors.selectActiveSubPlan);
|
||||||
const allowedPlans = useMemo(() => ["stripe.37"], []);
|
const allowedPlans = useMemo(() => [""], []);
|
||||||
|
|
||||||
const storedEmail = steps.getStoredValue(Step.Email);
|
const storedEmail = steps.getStoredValue(Step.Email);
|
||||||
|
|
||||||
|
|||||||
140
src/hooks/authentication/use-authentication.ts
Normal file
140
src/hooks/authentication/use-authentication.ts
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
import { useApi } from "@/api";
|
||||||
|
import { EGender, ESourceAuthorization, ICreateAuthorizePayload } from "@/api/resources/User";
|
||||||
|
import { useAuth } from "@/auth";
|
||||||
|
import { getClientTimezone } from "@/locales";
|
||||||
|
import { getDateAsString } from "@/services/date";
|
||||||
|
import { filterNullKeysOfObject } from "@/services/filter-object";
|
||||||
|
import { actions, selectors } from "@/store";
|
||||||
|
import moment from "moment";
|
||||||
|
import { useCallback, useMemo, useState } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export const useAuthentication = () => {
|
||||||
|
const api = useApi();
|
||||||
|
const { i18n } = useTranslation();
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
const [token, setToken] = useState<string | null>(null);
|
||||||
|
const { user, signUp } = useAuth();
|
||||||
|
const locale = i18n.language;
|
||||||
|
const { username } = useSelector(selectors.selectUser)
|
||||||
|
const { name: partnerName, birthDate: partnerBirthdateFromForm } = useSelector(selectors.selectRightUser)
|
||||||
|
const {
|
||||||
|
gender,
|
||||||
|
birthdate: birthdateFromQuestionnaire,
|
||||||
|
birthtime: birthtimeFromQuestionnaire,
|
||||||
|
birthPlace,
|
||||||
|
partnerGender,
|
||||||
|
partnerBirthdate: partnerBirthdateFromQuestionnaire,
|
||||||
|
partnerBirthPlace,
|
||||||
|
partnerBirthtime,
|
||||||
|
} = useSelector(selectors.selectQuestionnaire)
|
||||||
|
|
||||||
|
const birthdateFromForm = useSelector(selectors.selectBirthdate);
|
||||||
|
const birthtimeFromForm = useSelector(selectors.selectBirthtime);
|
||||||
|
|
||||||
|
const birthdate = useMemo(() => {
|
||||||
|
if (birthdateFromQuestionnaire.length) {
|
||||||
|
return birthdateFromQuestionnaire;
|
||||||
|
}
|
||||||
|
if (birthdateFromForm.length) {
|
||||||
|
return birthdateFromForm;
|
||||||
|
}
|
||||||
|
}, [birthdateFromForm, birthdateFromQuestionnaire]);
|
||||||
|
|
||||||
|
const birthtime = useMemo(() => {
|
||||||
|
if (birthtimeFromQuestionnaire.length) {
|
||||||
|
return birthtimeFromQuestionnaire;
|
||||||
|
}
|
||||||
|
if (birthtimeFromForm.length) {
|
||||||
|
return birthtimeFromForm;
|
||||||
|
}
|
||||||
|
}, [birthtimeFromForm, birthtimeFromQuestionnaire]);
|
||||||
|
|
||||||
|
const partnerBirthdate = useMemo(() => {
|
||||||
|
const fromQuestionnaire = `${partnerBirthdateFromQuestionnaire} ${partnerBirthtime}`
|
||||||
|
if (partnerBirthdateFromQuestionnaire.length) {
|
||||||
|
return fromQuestionnaire;
|
||||||
|
}
|
||||||
|
return getDateAsString(partnerBirthdateFromForm)
|
||||||
|
}, [partnerBirthdateFromForm, partnerBirthdateFromQuestionnaire, partnerBirthtime])
|
||||||
|
|
||||||
|
const formatDate = useCallback((date: string) => {
|
||||||
|
const _date = moment(date).format("YYYY-MM-DD HH:mm");
|
||||||
|
if (_date === "Invalid date") {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return _date;
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
|
||||||
|
const getAuthorizationPayload = useCallback((email: string, source: ESourceAuthorization): ICreateAuthorizePayload => {
|
||||||
|
const timezone = getClientTimezone();
|
||||||
|
return filterNullKeysOfObject<ICreateAuthorizePayload>({
|
||||||
|
timezone,
|
||||||
|
locale,
|
||||||
|
email,
|
||||||
|
source,
|
||||||
|
profile: {
|
||||||
|
name: username || "",
|
||||||
|
gender: EGender[gender as keyof typeof EGender] || null,
|
||||||
|
birthdate: formatDate(`${birthdate} ${birthtime}`),
|
||||||
|
birthplace: {
|
||||||
|
address: birthPlace,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
partner: {
|
||||||
|
name: partnerName,
|
||||||
|
gender: EGender[partnerGender as keyof typeof EGender] || null,
|
||||||
|
birthdate: formatDate(partnerBirthdate),
|
||||||
|
birthplace: {
|
||||||
|
address: partnerBirthPlace,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [
|
||||||
|
birthPlace,
|
||||||
|
birthdate,
|
||||||
|
gender,
|
||||||
|
locale,
|
||||||
|
partnerBirthPlace,
|
||||||
|
partnerBirthdate,
|
||||||
|
partnerGender,
|
||||||
|
partnerName,
|
||||||
|
username,
|
||||||
|
birthtime,
|
||||||
|
]);
|
||||||
|
|
||||||
|
const authorization = useCallback(async (email: string, source: ESourceAuthorization) => {
|
||||||
|
try {
|
||||||
|
setIsLoading(true);
|
||||||
|
const payload = getAuthorizationPayload(email, source);
|
||||||
|
const { token } = await api.authorization(payload);
|
||||||
|
const { user } = await api.getUser({ token });
|
||||||
|
signUp(token, user);
|
||||||
|
setToken(token);
|
||||||
|
dispatch(actions.status.update("registred"));
|
||||||
|
} catch (error) {
|
||||||
|
setError((error as Error).message);
|
||||||
|
} finally {
|
||||||
|
setIsLoading(false);
|
||||||
|
}
|
||||||
|
}, [api, dispatch, getAuthorizationPayload, signUp])
|
||||||
|
|
||||||
|
return useMemo(
|
||||||
|
() => ({
|
||||||
|
isLoading,
|
||||||
|
error,
|
||||||
|
token,
|
||||||
|
user,
|
||||||
|
authorization
|
||||||
|
}),
|
||||||
|
[isLoading, error, token, user, authorization]
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
@ -215,6 +215,9 @@ const routes = {
|
|||||||
[dApiHost, "payment", "products", `${productKey}?email=${email}`].join(
|
[dApiHost, "payment", "products", `${productKey}?email=${email}`].join(
|
||||||
"/"
|
"/"
|
||||||
),
|
),
|
||||||
|
dApiAuth: () => [dApiHost, "users", "auth"].join("/"),
|
||||||
|
|
||||||
|
dApiGetRealToken: () => [dApiHost, "users", "auth", "token"].join("/"),
|
||||||
|
|
||||||
assistants: () => [apiHost, prefix, "ai", "assistants.json"].join("/"),
|
assistants: () => [apiHost, prefix, "ai", "assistants.json"].join("/"),
|
||||||
setExternalChatIdAssistants: (chatId: string) =>
|
setExternalChatIdAssistants: (chatId: string) =>
|
||||||
|
|||||||
22
src/services/filter-object/index.ts
Normal file
22
src/services/filter-object/index.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
|
||||||
|
|
||||||
|
export function filterNullKeysOfObject<T extends object>(object: T): T {
|
||||||
|
if (typeof object !== "object") {
|
||||||
|
return object
|
||||||
|
}
|
||||||
|
return Object.keys(object)
|
||||||
|
.filter(key => {
|
||||||
|
if (typeof object[key as keyof T] === "object" && object[key as keyof T] !== null) {
|
||||||
|
return Object.keys(object[key as keyof T] as object).length
|
||||||
|
}
|
||||||
|
if (typeof object[key as keyof T] === "string") {
|
||||||
|
return !!(object[key as keyof T] as string).length
|
||||||
|
}
|
||||||
|
return object[key as keyof T] !== null
|
||||||
|
})
|
||||||
|
.reduce((acc, key) => {
|
||||||
|
return Object.assign(acc, {
|
||||||
|
[key]: typeof object[key as keyof T] === "object" ? filterNullKeysOfObject<typeof object>(object[key as keyof object]) : object[key as keyof T]
|
||||||
|
});
|
||||||
|
}, Array.isArray(object) ? [] : {}) as T;
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user