diff --git a/wondrous-bot-admin/package.json b/wondrous-bot-admin/package.json index 4c2eee5d46..c63272a308 100644 --- a/wondrous-bot-admin/package.json +++ b/wondrous-bot-admin/package.json @@ -21,6 +21,7 @@ "@mui/lab": "^5.0.0-alpha.152", "@mui/material": "^5.12.1", "@mui/styled-engine-sc": "^5.11.11", + "@react-oauth/google": "^0.12.1", "@reactour/tour": "^3.4.0", "@vercel/node": "^2.14.2", "@web3-react/coinbase-wallet": "^8.2.0", diff --git a/wondrous-bot-admin/public/connect-discord-bot.png b/wondrous-bot-admin/public/connect-discord-bot.png new file mode 100644 index 0000000000..18b8503f05 Binary files /dev/null and b/wondrous-bot-admin/public/connect-discord-bot.png differ diff --git a/wondrous-bot-admin/public/signup-loading.webm b/wondrous-bot-admin/public/signup-loading.webm new file mode 100644 index 0000000000..4e5ddb08bf Binary files /dev/null and b/wondrous-bot-admin/public/signup-loading.webm differ diff --git a/wondrous-bot-admin/public/wonder-white.svg b/wondrous-bot-admin/public/wonder-white.svg new file mode 100644 index 0000000000..5d4b38bd43 --- /dev/null +++ b/wondrous-bot-admin/public/wonder-white.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/wondrous-bot-admin/src/App.tsx b/wondrous-bot-admin/src/App.tsx index 79450eded7..4a828368b2 100644 --- a/wondrous-bot-admin/src/App.tsx +++ b/wondrous-bot-admin/src/App.tsx @@ -57,6 +57,9 @@ import ViewReferralPage from "pages/referrals/ViewReferral"; import { createWeb3Modal, defaultConfig } from "@web3modal/ethers5/react"; import { SUPPORTED_CHAINS_META } from "utils/web3Constants"; import RewardfulTag from "components/AddFormEntity/components/RewardfulTag"; +import { GoogleOAuthProvider } from "@react-oauth/google"; +import PlanSelectComponent from "components/Onboarding/PlanSelect"; +import OnboardingFinalizeComponent from "components/Onboarding/FinalizeComponent"; const projectId = import.meta.env.VITE_WALLET_CONNECT_PROJECT_ID; @@ -243,11 +246,22 @@ const router = createBrowserRouter([ path: "/referrals/:id", element: , }, + { + path: "/onboarding/plan-select", + element: , + }, + { + path: "/onboarding/finalize", + element: , + }, ], }, ]); const getDesignTokens = (mode) => ({ + typography: { + fontFamily: "Poppins", + }, palette: { mode, ...(mode === "light" @@ -293,12 +307,10 @@ function App() { [] ); - function getLibrary(provider): Web3Provider { - const library = new Web3Provider(provider); - return library; - } - const theme = useMemo(() => createTheme(getDesignTokens(mode)), [mode]); + + const GOOGLE_CLIENT_ID = import.meta.env.VITE_GOOGLE_CLIENT_ID; + return ( @@ -306,7 +318,9 @@ function App() { - + + + diff --git a/wondrous-bot-admin/src/components/CreateTemplate/helpers.ts b/wondrous-bot-admin/src/components/CreateTemplate/helpers.ts index 630a1ad7c8..e2d43b2399 100644 --- a/wondrous-bot-admin/src/components/CreateTemplate/helpers.ts +++ b/wondrous-bot-admin/src/components/CreateTemplate/helpers.ts @@ -115,7 +115,7 @@ const processSteps = (steps) => next.type === TYPES.VERIFY_FHENIX_FAUCET_INTERACTION || next.type === TYPES.VERIFY_FHENIX_WALLET_GAS_USAGE ) { - step.prompt = next.value?.prompt; + step.prompt = next.value; step["additionalData"] = { chain: "fhenix", }; diff --git a/wondrous-bot-admin/src/components/Layout/index.tsx b/wondrous-bot-admin/src/components/Layout/index.tsx index 6d6ace1d98..64b7c1a3aa 100644 --- a/wondrous-bot-admin/src/components/Layout/index.tsx +++ b/wondrous-bot-admin/src/components/Layout/index.tsx @@ -68,6 +68,7 @@ const Layout = () => { const subscription = orgSubscriptionData?.getOrgSubscription; const isPageWithoutHeader = matchRoute(location.pathname, PAGES_WITHOUT_HEADER); + const isExcludedPath = matchRoute(location.pathname, EXCLUDED_PATHS); const handleActiveOrg = (org) => { localStorage.setItem(LOCAL_STORAGE_ORG_ID_KEY, org?.id); setActiveOrg(org); @@ -98,7 +99,7 @@ const Layout = () => { ); useEffect(() => { - if (!isPageWithoutHeader) { + if (!isExcludedPath) { getLoggedInUserFullAccessOrgs({ variables: { excludeSharedOrgs: true, @@ -106,7 +107,7 @@ const Layout = () => { }, }); } - }, [isPageWithoutHeader]); + }, [isExcludedPath]); if (loading) { return ; diff --git a/wondrous-bot-admin/src/components/Login/index.tsx b/wondrous-bot-admin/src/components/Login/index.tsx index 8cfb126ce0..dc42e82923 100644 --- a/wondrous-bot-admin/src/components/Login/index.tsx +++ b/wondrous-bot-admin/src/components/Login/index.tsx @@ -16,6 +16,7 @@ import AuthLayout from "components/Shared/AuthLayout"; import { LinkWithQuery } from "components/Shared/LinkWithQuery"; import WalletConnect from "components/Icons/Login/walletconnect.svg"; import useWeb3Auth from "services/web3/useWeb3Auth"; +import GoogleOAuthButton from "components/OAuth/GoogleOAuth"; function Login() { // since we can't disconnect a user's wallet this is used in order to check if the user actually clicked the login button @@ -96,6 +97,7 @@ function Login() { {notSupportedChain && ( Unsupported network, change to mainnet or a supported network )} + ( + + + + + + +); + +export default GoogleIcon; \ No newline at end of file diff --git a/wondrous-bot-admin/src/components/OAuth/GoogleOAuth/index.tsx b/wondrous-bot-admin/src/components/OAuth/GoogleOAuth/index.tsx new file mode 100644 index 0000000000..3f21de0b1f --- /dev/null +++ b/wondrous-bot-admin/src/components/OAuth/GoogleOAuth/index.tsx @@ -0,0 +1,81 @@ +import { ButtonBase, Typography } from "@mui/material"; +import { useGoogleLogin, useGoogleOneTapLogin } from "@react-oauth/google"; +import GoogleIcon from "./icon"; +import { useMutation } from "@apollo/client"; +import { GOOGLE_LOGIN_MUTATION } from "graphql/mutations"; +import { storeAuthHeader } from "components/Auth"; +import { useNavigate } from "react-router-dom"; +import { getBaseUrl } from "utils/common"; +const GoogleOAuthButton = ({ isSignup = false }) => { + const handleCompleted = async ({ token, user, callback }) => { + await storeAuthHeader(token, user); + callback?.(); + }; + + const navigate = useNavigate(); + + const handleRedirect = () => { + if (isSignup) { + navigate("/onboarding/welcome?ref=signup"); + } + navigate("/"); + }; + + const [signInUser] = useMutation(GOOGLE_LOGIN_MUTATION, { + onCompleted: ({ googleSignin }) => { + const { user, token } = googleSignin; + if (googleSignin?.user) { + handleCompleted({ token, user, callback: handleRedirect }); + } + }, + onError: (error) => { + console.log(error); + }, + }); + + const login = useGoogleLogin({ + flow: "auth-code", + scope: "email", + redirect_uri: `${getBaseUrl()}/oauth/google/callback`, + onSuccess: (tokenResponse) => { + const { code } = tokenResponse; + signInUser({ variables: { code: code } }); + }, + onError: (error) => console.log(error), + }); + + return ( + login()} + sx={{ + width: "100%", + display: "flex", + gap: "10px", + justifyContent: "center", + alignItems: "center", + borderRadius: "35px", + border: "2px solid #84BCFF", + + background: "#FFF", + padding: "8px 24px", + "&:hover": { + border: "2px solid #1976D2", + }, + }} + > + + + Continue with Google + + + ); +}; + +export default GoogleOAuthButton; diff --git a/wondrous-bot-admin/src/components/Onboarding/FinalizeComponent/index.tsx b/wondrous-bot-admin/src/components/Onboarding/FinalizeComponent/index.tsx new file mode 100644 index 0000000000..9aeba5b941 --- /dev/null +++ b/wondrous-bot-admin/src/components/Onboarding/FinalizeComponent/index.tsx @@ -0,0 +1,33 @@ +import { Box } from "@mui/material"; +import { SignupAuthLayout } from "components/Shared/AuthLayout"; +import { useEffect } from "react"; +import { useNavigate } from "react-router-dom"; + +const OnboardingFinalizeComponent = () => { + const navigate = useNavigate(); + + useEffect(() => { + const timeout = setTimeout(() => { + navigate("/"); + }, 2000); + return () => clearTimeout(timeout); + }, []); + + return ( + + + + + ); +}; + +export default OnboardingFinalizeComponent; diff --git a/wondrous-bot-admin/src/components/Onboarding/PlanSelect/Panel.tsx b/wondrous-bot-admin/src/components/Onboarding/PlanSelect/Panel.tsx new file mode 100644 index 0000000000..48e8f80ceb --- /dev/null +++ b/wondrous-bot-admin/src/components/Onboarding/PlanSelect/Panel.tsx @@ -0,0 +1,101 @@ +import { Box, Grid, Typography } from "@mui/material"; +import { SharedSecondaryButton } from "components/Shared/styles"; +import { useNavigate } from "react-router-dom"; + +interface IProps { + fullWidth?: boolean; + title: string; + img?: string; + description: string; + price?: number; + onClick?: () => void; + textColor: string; + button?: () => JSX.Element; + buttonTitle?: string; + disabledButton?: boolean; +} +const Panel = ({ + fullWidth = false, + title, + img = null, + description, + price = null, + onClick = null, + textColor, + button = null, + buttonTitle = "Start 14-day Trial", + disabledButton = false, +}: IProps) => { + const navigate = useNavigate(); + + return ( + + {img ? ( + + ) : null} + + {title} + + {description ? ( + + {description} + + ) : null} + {button?.()} + {!button ? ( + + + {buttonTitle} + + + <> + + ${price} /month + + + {!price ? "Custom pricing per server" : "No credit card required"} + + + + + ) : null} + + ); +}; + +export default Panel; diff --git a/wondrous-bot-admin/src/components/Onboarding/PlanSelect/index.tsx b/wondrous-bot-admin/src/components/Onboarding/PlanSelect/index.tsx new file mode 100644 index 0000000000..cb945d8528 --- /dev/null +++ b/wondrous-bot-admin/src/components/Onboarding/PlanSelect/index.tsx @@ -0,0 +1,162 @@ +import { Box, Grid, Typography } from "@mui/material"; +import { SignupAuthLayout } from "components/Shared/AuthLayout"; +import Panel from "./Panel"; +import { SharedSecondaryButton } from "components/Shared/styles"; +import { useNavigate } from "react-router-dom"; +import { PricingOptionsTitle } from "components/Pricing/PricingOptionsListItem"; +import { useLazyQuery } from "@apollo/client"; +import { GET_CHECKOUT_LINK } from "graphql/queries/subscription"; +import useAlerts, { useGlobalContext } from "utils/hooks"; + +const PlanSelectComponent = () => { + const { setSnackbarAlertOpen, setSnackbarAlertMessage, setSnackbarAlertAutoHideDuration } = useAlerts(); + const { activeOrg } = useGlobalContext(); + + const [getCheckoutLink, { loading }] = useLazyQuery(GET_CHECKOUT_LINK, { + notifyOnNetworkStatusChange: true, + onCompleted: (data) => { + const url = data?.getCheckoutLink?.url; + if (url) { + window.location.href = url; + } + }, + onError: (err) => { + setSnackbarAlertMessage("Something went wrong. Please try again"); + setSnackbarAlertOpen(true); + }, + }); + + const startTrial = (tier) => { + getCheckoutLink({ + variables: { + tier: tier, + orgId: activeOrg?.id, + }, + }); + }; + + const handlePlanStart = () => navigate("/onboarding/finalize"); + + const talkToSalesLink = "https://calendly.com/androswong418"; + const config = [ + { + title: PricingOptionsTitle.Hobby, + color: "#FF9AD7", + description: "Unlimited quests, reward in crypto, Twitter verification, analytics page, 2 admins", + price: 10, + onClick: () => startTrial("hobby"), + disabledButton: loading, + img: "/images/tour-images/levels-page.png", + }, + { + title: PricingOptionsTitle.Premium, + color: "#2A8D5C", + description: "Unlimited members, store, YouTube verification, custom branding, batch pay, on-chain verifications", + price: 49, + disabledButton: loading, + onClick: () => startTrial("premium"), + img: "/images/tour-images/members-page.png", + }, + { + title: "Ecosystem", + color: "#F8642D", + buttonTitle: "Talk to sales", + description: "API access, NFT native minting, custom integrations, community consulting, unlimited admins", + img: "/images/tour-images/quests-page.png", + disabledButton: loading, + onClick: () => { + window.open(talkToSalesLink, "_blank"); + return handlePlanStart(); + }, + }, + ]; + + const navigate = useNavigate(); + + return ( + + + + + Select your plan + + + Try one of our premium plans to experience our full suite of products or start for free. + + + + ( + + Start for Free + + )} + /> + + {config.map((item, idx) => { + return ( + + ); + })} + + + + + ); +}; + +export default PlanSelectComponent; diff --git a/wondrous-bot-admin/src/components/Onboarding/index.tsx b/wondrous-bot-admin/src/components/Onboarding/index.tsx index 7cb26bb70a..1dabf44b8a 100644 --- a/wondrous-bot-admin/src/components/Onboarding/index.tsx +++ b/wondrous-bot-admin/src/components/Onboarding/index.tsx @@ -1,236 +1,148 @@ import { useMutation } from "@apollo/client"; -import { Box, ButtonBase, CircularProgress, FormControl, Grid, Typography } from "@mui/material"; -import { CustomTextField } from "components/AddFormEntity/components/styles"; -import { Label } from "components/CreateTemplate/styles"; -import { ErrorTypography } from "components/Login/styles"; -import AuthLayout from "components/Shared/AuthLayout"; +import { Box, Typography, useMediaQuery } from "@mui/material"; +import { SignupAuthLayout } from "components/Shared/AuthLayout"; import { SharedSecondaryButton } from "components/Shared/styles"; -import { CREATE_ORG } from "graphql/mutations"; -import { useContext, useMemo, useState } from "react"; -import { useLocation, useNavigate, useSearchParams } from "react-router-dom"; -import { useSchema } from "./validator"; +import { CREATE_CMTY_ORG } from "graphql/mutations"; +import { useCallback, useContext, useRef, useState } from "react"; +import { useLocation, useNavigate } from "react-router-dom"; import GlobalContext from "utils/context/GlobalContext"; import MetaPixel from "components/MetaPixel"; import GoogleTag from "components/GoogleTag"; -import useWonderWeb3Modal from "services/web3/useWonderWeb3Modal"; -import { logout } from "components/Auth"; +import Modal from "components/Shared/Modal"; +import { getBaseUrl } from "utils/common"; +import useAlerts from "utils/hooks"; + +const DiscordClientID = import.meta.env.VITE_DISCORD_CLIENT_ID; const OnboardingComponent = () => { - const { address, chainId, open, disconnect, isConnected } = useWonderWeb3Modal(); const { setActiveOrg } = useContext(GlobalContext); - const [orgData, setOrgData] = useState({ - name: "", - username: "", - twitterHandle: "", - productLink: "", - }); - - const { search } = useLocation(); - const searchParams = new URLSearchParams(search); - - const referrerPage = searchParams.get("ref"); - - const handleGoBack = (e) => { - e.preventDefault(); - if (referrerPage === "workspace") { - return navigate(-1); - } - if (referrerPage === "signup") { - logout("/signup"); - } - if (referrerPage === "login") { - logout(); + const errorToText = (errorMessage) => { + switch (errorMessage) { + case "guild_already_exist": + return "This discord server is already connected to another account!"; + case "guild_not_found": + return "This discord server was not found - please try again"; + default: + return "Error connecting discord - please try again"; } }; - const goBackLabel = useMemo(() => { - if (referrerPage === "workspace") { - return "Go Back"; - } - if (referrerPage === "signup") { - return "Back to Signup"; - } - if (referrerPage === "login") { - return "Back to Login"; - } - }, [referrerPage]); - const navigate = useNavigate(); - const [createOrg, { loading }] = useMutation(CREATE_ORG, { + const callbackURL = () => encodeURIComponent(`${getBaseUrl()}/discord/callback/org-connect`); + + const isMobile = useMediaQuery((theme: any) => theme.breakpoints.down("sm")); + + const [createGuildOrg, { loading: createOrgLoading }] = useMutation(CREATE_CMTY_ORG, { notifyOnNetworkStatusChange: true, - }); - const [errors, setErrors] = useState({}); - const handleChange = (e) => { - setErrors({ - ...errors, - [e.target.name]: "", - }); - setOrgData({ ...orgData, [e.target.name]: e.target.value }); - }; - const validationSchema = useSchema(); - - const formConfig = [ - { - name: "name", - label: "Name", - value: orgData.name, - onChange: handleChange, - placeholder: "Enter project title", - required: true, - }, - { - name: "username", - label: "Username", - value: orgData.username, - onChange: handleChange, - placeholder: "username", - required: true, - padding: "14px 14px 14px 24px", - startAdornment: ( - - @ - - ), - }, - { - name: "twitterHandle", - label: "Twitter Handle", - onChange: handleChange, - placeholder: "twitter handle", - required: true, - padding: "14px 14px 14px 24px", - startAdornment: ( - - @ - - ), - }, - { - name: "productLink", - label: "Product Website", - onChange: handleChange, - placeholder: "product-link.xyz", - required: true, - padding: "14px 14px 14px 65px", - startAdornment: ( - - https:// - - ), + refetchQueries: ["getLoggedInUserFullAccessOrgs"], + onCompleted: (data) => { + setActiveOrg(data?.createOrg); + navigate("/onboarding/plan-select"); }, - ]; - - const handleSubmit = async () => { - try { - await validationSchema.validate(orgData, { abortEarly: false }); - const { data } = await createOrg({ - variables: { - input: { - name: orgData.name, - username: orgData.username, - cmtyEnabled: true, - twitterHandle: orgData.twitterHandle, - productLink: orgData.productLink, + }); + + const discordWindowRef = useRef(null); + const isConnectingRef = useRef(false); + + const { setSnackbarAlertOpen, setSnackbarAlertMessage } = useAlerts(); + + const receiveMessage = useCallback( + (event) => { + if (!event.data) return; + const targetOrigin = window.location.origin; + if (event.origin !== targetOrigin) return; + let message = event.data; + try { + const data = JSON.parse(message); + if (data.type === "discordCallback") { + discordWindowRef.current?.close(); + discordWindowRef.current = null; + } + if ( + data.type !== "discordCallback" || + createOrgLoading || + isConnectingRef.current || + !data.code || + !data.guildId + ) { + return; + } + + isConnectingRef.current = true; + + createGuildOrg({ + variables: { + code: data.code, + guildId: data.guildId, }, - }, - }); - setActiveOrg(data?.createOrg); - navigate("/"); - } catch (err) { - err?.inner?.forEach((e) => { - setErrors((prev) => ({ ...prev, [e.path]: e.message })); - }); - } + }).catch((err) => { + const extensionMessage = err?.graphQLErrors?.[0]?.extensions?.message; + setSnackbarAlertMessage(errorToText(extensionMessage)); + setSnackbarAlertOpen(true); + return; + }); + } catch (error) {} + }, + [createOrgLoading, createGuildOrg] + ); + + const handleDiscordConnect = () => { + const oauthUrl = `https://discord.com/oauth2/authorize?client_id=${DiscordClientID}&permissions=8&scope=bot&response_type=code&state=${encodeURIComponent( + JSON.stringify({ create_org: true }) + )}&redirect_uri=${callbackURL()}`; + + const width = screen.width * 0.25; + const height = screen.height; + + isConnectingRef.current = false; + const left = 0; + const top = 0; + const features = "width=" + width + ",height=" + height + ",top=" + top + ",left=" + left; + + const openMethod = isMobile ? "_blank" : "NewWindow"; + discordWindowRef.current = window.open(oauthUrl, openMethod, isMobile ? "" : features); + + discordWindowRef.current?.focus(); + + window.addEventListener("message", receiveMessage, false); + + discordWindowRef.current.onbeforeunload = () => { + window.removeEventListener("message", receiveMessage); + }; }; return ( - + - {loading ? ( - - - - ) : ( - - - - Welcome! + + + + + + Connect your Community + + + The party doesn't start until you add Wonderverse to your server! +
+ 1,000+ premium communities trust our bot to help them grow.
- +
- - - {formConfig.map((config, idx) => ( - - - {errors[config.name] ? {errors[config.name]} : null} - - ))} - - - - Create Community đź’– - - - - {goBackLabel} - - + + Add to Discord -
- )} -
+ + + ); }; diff --git a/wondrous-bot-admin/src/components/Shared/AuthLayout/index.tsx b/wondrous-bot-admin/src/components/Shared/AuthLayout/index.tsx index ec7456ff39..a261e948ef 100644 --- a/wondrous-bot-admin/src/components/Shared/AuthLayout/index.tsx +++ b/wondrous-bot-admin/src/components/Shared/AuthLayout/index.tsx @@ -1,38 +1,79 @@ -import { Box, Grid } from "@mui/material"; +import { Box, Grid, useMediaQuery } from "@mui/material"; import CollectCredentials from "components/SignupComponent/CollectCredentials"; import { Link } from "react-router-dom"; import { SharedSecondaryButton } from "../styles"; import { MainWrapper } from "./styles"; -const AuthLayout = ({children, headerButton = null}) => - - - - ( + + + - - {headerButton ? headerButton() : null} + + {showHeader ? ( + + + {headerButton ? headerButton() : null} + + ) : null} + {children} + + + + +); + +const SignupHeader = () => { + const isMobile = useMediaQuery((theme: any) => theme.breakpoints.down("sm")); + + return ( + + + + ); +}; + +export const SignupAuthLayout = ({ children }) => { + return ( + + + + {children} - {children} - - - - -export default AuthLayout; \ No newline at end of file + ); +}; +export default AuthLayout; diff --git a/wondrous-bot-admin/src/components/SignupComponent/CollectCredentials/index.tsx b/wondrous-bot-admin/src/components/SignupComponent/CollectCredentials/index.tsx index c92594349e..32fce61006 100644 --- a/wondrous-bot-admin/src/components/SignupComponent/CollectCredentials/index.tsx +++ b/wondrous-bot-admin/src/components/SignupComponent/CollectCredentials/index.tsx @@ -5,13 +5,14 @@ import { DiscordConnector } from "components/Connectors"; import { Connectors, ErrorTypography } from "components/Login/styles"; import { SharedSecondaryButton } from "components/Shared/styles"; import { useEffect, useState } from "react"; -import { useLocation } from "react-router-dom"; +import { Link, useLocation } from "react-router-dom"; import { DISCORD_CONNECT_TYPES, GRAPHQL_ERRORS } from "utils/constants"; import { SUPPORTED_CHAINS } from "utils/web3Constants"; import WalletConnect from "components/Icons/Login/walletconnect.svg"; -import { Divider } from "./styles"; +import { Divider, StyledLink } from "./styles"; import { validate } from "./validator"; import useWeb3Auth from "services/web3/useWeb3Auth"; +import GoogleOAuthButton from "components/OAuth/GoogleOAuth"; const CollectCredentials = ({ moveForward }) => { const [credentials, setCredentials] = useState({ @@ -118,31 +119,36 @@ const CollectCredentials = ({ moveForward }) => { return ( <> - + - Sign up to Communities + Sign up to Wonderverse - + + + {!notSupportedChain && errorMessage ? {errorMessage} : ""} {notSupportedChain && ( Unsupported network, change to mainnet or a supported network )} - - {/* {!isMobile && } */} + {/* - {/* - */} - + */} - - Or - - { ))} - Signup + Sign up with Email + + + + + By continuing, you acknowledge that you have read and understood, and agree to Wonderverse’s
{" "} + + Terms & Conditions + {" "} + and{" "} + + Privacy Policy + + . +
+ + Already signed up?{" "} + + Go to login + + +
+
diff --git a/wondrous-bot-admin/src/components/SignupComponent/CollectCredentials/styles.tsx b/wondrous-bot-admin/src/components/SignupComponent/CollectCredentials/styles.tsx index 25ac4bf883..557f1f75e4 100644 --- a/wondrous-bot-admin/src/components/SignupComponent/CollectCredentials/styles.tsx +++ b/wondrous-bot-admin/src/components/SignupComponent/CollectCredentials/styles.tsx @@ -1,7 +1,18 @@ -import styled from 'styled-components'; +import styled from "styled-components"; export const Divider = styled.div` - width: 100%; - height: 1px; - background-color: ${({bgColor = "#CDCDCD"}) => bgColor}; -`; \ No newline at end of file + width: 100%; + height: 1px; + background-color: ${({ bgColor = "#CDCDCD" }) => bgColor}; +`; + +export const StyledLink = styled.a` + color: black; + font-size: inherit; + font-weight: inherit; + text-decoration: underline; + &:hover { + opacity: 0.8; + color: black; + } +`; diff --git a/wondrous-bot-admin/src/components/SignupComponent/index.tsx b/wondrous-bot-admin/src/components/SignupComponent/index.tsx index c8b1e4be6a..90232d22a1 100644 --- a/wondrous-bot-admin/src/components/SignupComponent/index.tsx +++ b/wondrous-bot-admin/src/components/SignupComponent/index.tsx @@ -1,5 +1,5 @@ import { Box, Grid } from "@mui/material"; -import AuthLayout from "components/Shared/AuthLayout"; +import AuthLayout, { SignupAuthLayout } from "components/Shared/AuthLayout"; import { LinkWithQuery } from "components/Shared/LinkWithQuery"; import { SharedSecondaryButton } from "components/Shared/styles"; import { Link, useLocation, useNavigate } from "react-router-dom"; @@ -27,16 +27,31 @@ const SignupComponent = () => { const moveForward = () => handleUserOnboardingRedirect(null, navigate, params, "/onboarding/welcome?ref=signup"); return ( - ( - - Back to Login - - )} - > + - - + + + + + + + + + + + + ); }; diff --git a/wondrous-bot-admin/src/graphql/fragments/quest.ts b/wondrous-bot-admin/src/graphql/fragments/quest.ts index 651511a387..5c8ee6974d 100644 --- a/wondrous-bot-admin/src/graphql/fragments/quest.ts +++ b/wondrous-bot-admin/src/graphql/fragments/quest.ts @@ -240,6 +240,7 @@ export const QuestFragment = gql` discordEventId minDuration usdValue + gitcoinPassportMinimumScoreThreshold } } } diff --git a/wondrous-bot-admin/src/graphql/mutations/org.ts b/wondrous-bot-admin/src/graphql/mutations/org.ts index 5821ddb2f2..650565c8fd 100644 --- a/wondrous-bot-admin/src/graphql/mutations/org.ts +++ b/wondrous-bot-admin/src/graphql/mutations/org.ts @@ -81,4 +81,13 @@ export const UPDATE_ORG_MODULES = gql` success } } +`; + +export const CREATE_CMTY_ORG = gql` + mutation createCmtyOrg($code: String!, $guildId: String!) { + createCmtyOrg(code: $code, guildId: $guildId) { + ...OrgFragment + } + } + ${OrgFragment} `; \ No newline at end of file diff --git a/wondrous-bot-admin/src/graphql/mutations/user.ts b/wondrous-bot-admin/src/graphql/mutations/user.ts index 9d3e9ce87e..a90d01d9ad 100644 --- a/wondrous-bot-admin/src/graphql/mutations/user.ts +++ b/wondrous-bot-admin/src/graphql/mutations/user.ts @@ -194,3 +194,15 @@ export const UPDATE_ORG_CMTY_USER_POINT_BALANCE = gql` } } `; + +export const GOOGLE_LOGIN_MUTATION = gql` + mutation googleSignin($code: String!) { + googleSignin(code: $code) { + user { + ...LoggedinUser + } + token + } + } + ${LoggedinUserFragment} +`; diff --git a/wondrous-bot-admin/src/graphql/queries/subscription.ts b/wondrous-bot-admin/src/graphql/queries/subscription.ts index 604700b724..cffd8ba69e 100644 --- a/wondrous-bot-admin/src/graphql/queries/subscription.ts +++ b/wondrous-bot-admin/src/graphql/queries/subscription.ts @@ -14,3 +14,11 @@ export const GET_ORG_SUBSCRIPTION = gql` } } `; + +export const GET_CHECKOUT_LINK = gql` + query getCheckoutLink($orgId: ID!, $tier: String!) { + getCheckoutLink(orgId: $orgId, tier: $tier) { + url + } + } +`; \ No newline at end of file diff --git a/wondrous-bot-admin/src/pages/discord/callback/org-connect/index.tsx b/wondrous-bot-admin/src/pages/discord/callback/org-connect/index.tsx index 3f7ecc2758..a5706e2ee5 100644 --- a/wondrous-bot-admin/src/pages/discord/callback/org-connect/index.tsx +++ b/wondrous-bot-admin/src/pages/discord/callback/org-connect/index.tsx @@ -2,17 +2,37 @@ import { useMutation } from "@apollo/client"; import { CircularProgress, Typography } from "@mui/material"; import Grid from "@mui/material/Grid"; import { CONNECT_DISCORD_TO_CMTY_ORG } from "graphql/mutations"; -import { useEffect, useState } from "react"; +import { useEffect, useMemo, useState } from "react"; import { useSearchParams } from "react-router-dom"; import { useNavigate } from "react-router-dom"; +import { getBaseUrl } from "utils/common"; const CallbackPage = () => { const [searchParams] = useSearchParams(); const code = searchParams?.get("code"); - const orgId = searchParams?.get("state"); + const state = searchParams?.get("state"); + const { isCreateOrg, orgId } = useMemo(() => { + if (!state) return { isCreateOrg: null, orgId: null }; + try { + let isCreateOrg = JSON.parse(state)?.create_org; + return { isCreateOrg, orgId: null }; + } catch (error) { + return { isCreateOrg: null, state }; + } + }, [state]); + + const errorToText = (errorMessage) => { + switch (errorMessage) { + case "guild_already_exist": + return "This discord server is already connected to another account!"; + case "guild_not_found": + return "This discord server was not found - please try again"; + default: + return "Error connecting discord - please try again"; + } + }; const guildId = searchParams?.get("guild_id"); const navigate = useNavigate(); - const [finishedVerification, setFinishedVerification] = useState(false); const [errorText, setErrorText] = useState(""); const [connectDiscordToCmtyOrg] = useMutation(CONNECT_DISCORD_TO_CMTY_ORG, { @@ -25,17 +45,17 @@ const CallbackPage = () => { refetchQueries: ["getLoggedInUserFullAccessOrgs"], onError: (err) => { console.error("error connecting discord", err); - if (err?.graphQLErrors && err?.graphQLErrors[0]?.extensions.message === "guild_already_exist") { - setErrorText( - "This discord server is already connected to another account! Please disconnect it from that account first." - ); + if (err?.graphQLErrors) { + const errorText = errorToText(err?.graphQLErrors[0]?.extensions.message); + setErrorText(errorText); } else { setErrorText("Error connecting discord - please try again"); } }, }); + useEffect(() => { - if (code && guildId && orgId) { + if (code && guildId && orgId && !isCreateOrg) { connectDiscordToCmtyOrg({ variables: { code, @@ -44,7 +64,13 @@ const CallbackPage = () => { }, }); } - }, [code, guildId, orgId]); + }, [code, guildId, orgId, !isCreateOrg]); + + useEffect(() => { + if (isCreateOrg && guildId) { + window.opener.postMessage(JSON.stringify({ type: "discordCallback", code: code, guildId }), getBaseUrl()); + } + }, []); return ( diff --git a/wondrous-bot-admin/src/pages/oauth/google/callback.tsx b/wondrous-bot-admin/src/pages/oauth/google/callback.tsx index 05f8b17a04..3874cef35d 100644 --- a/wondrous-bot-admin/src/pages/oauth/google/callback.tsx +++ b/wondrous-bot-admin/src/pages/oauth/google/callback.tsx @@ -7,7 +7,7 @@ import { useSearchParams } from "react-router-dom"; import { getBaseUrl } from "utils/common"; export function getGoogleOauthUrl({telegramUserId = null, discordId = null}) { - const GOOGLE_CLIENT_ID = '276263235787-efsi17itjf4d230126shg69h6r6qagf7.apps.googleusercontent.com'; + const GOOGLE_CLIENT_ID = import.meta.env.VITE_GOOGLE_CLIENT_ID; const baseUrl = `https://accounts.google.com/o/oauth2/v2/auth?client_id=${GOOGLE_CLIENT_ID}&response_type=code`; const redirectUrl = encodeURIComponent(`${getBaseUrl()}/oauth/google/callback`); diff --git a/wondrous-bot-admin/src/pages/twitter/callback/index.tsx b/wondrous-bot-admin/src/pages/twitter/callback/index.tsx index 3b03f8e412..6d6a240eba 100644 --- a/wondrous-bot-admin/src/pages/twitter/callback/index.tsx +++ b/wondrous-bot-admin/src/pages/twitter/callback/index.tsx @@ -24,10 +24,16 @@ const CallbackPage = () => { } }, onError: (e) => { + const isAlreadyConnected = e?.graphQLErrors?.[0]?.extensions?.errorCode === "twitter_already_connected"; + if (isAlreadyConnected) { + setErrorText("This Twitter account is already connected to another account!"); + setFinishedVerification(true); + return; + } console.error("error verifying twitter", e); - setErrorText("Error verifying twitter - please try again"); }, }); + useEffect(() => { if (code && !finishedVerification && (discordId || telegramUserId || migrateOrgId)) { verifyTwitter({ @@ -46,7 +52,8 @@ const CallbackPage = () => { {finishedVerification && ( - Finished connecting your Twitter account! You can close this window now and return to Discord. + {errorText || + "Finished connecting your Twitter account! You can close this window now and return to Discord."} )} {!finishedVerification && ( diff --git a/wondrous-bot-admin/src/utils/constants.tsx b/wondrous-bot-admin/src/utils/constants.tsx index 6f60670dba..4047cc839a 100644 --- a/wondrous-bot-admin/src/utils/constants.tsx +++ b/wondrous-bot-admin/src/utils/constants.tsx @@ -128,6 +128,8 @@ export const PAGES_WITHOUT_HEADER = [ "/telegram/connect", "/community-badge/claim", "/referral-campaign", + "/onboarding/plan-select", + '/onboarding/finalize' ]; export const BG_TYPES = { diff --git a/wondrous-bot-admin/src/utils/transformQuestConfig.ts b/wondrous-bot-admin/src/utils/transformQuestConfig.ts index bae465a6a1..726c074618 100644 --- a/wondrous-bot-admin/src/utils/transformQuestConfig.ts +++ b/wondrous-bot-admin/src/utils/transformQuestConfig.ts @@ -46,7 +46,7 @@ type InputQuestStep = { discordEventId?: string; minDuration?: number; usdValue?: number; - minimumThreshold?: number; + gitcoinPassportMinimumScoreThreshold?: number; }; }; @@ -135,7 +135,7 @@ type OutputQuestStep = { } | { prompt?: string; - minimumThreshold?: number; + gitcoinPassportMinimumScoreThreshold?: number; }; }; @@ -256,10 +256,18 @@ export function transformQuestConfig(obj: InputQuestStep[]): OutputQuestStep[] { } else if (step.type === TYPES.REFERRAL) { outputStep.value = step?.prompt; } else if (step.type === TYPES.VERIFY_GITCOIN_PASSPORT_SCORE) { + console.log("step", step); outputStep.value = { prompt: step?.prompt, - minimumThreshold: step?.additionalData?.minimumThreshold, + gitcoinPassportMinimumScoreThreshold: step?.additionalData?.gitcoinPassportMinimumScoreThreshold, }; + } else if ( + step.type === TYPES.VERIFY_FHENIX_ACTIVE_WALLET || + step.type === TYPES.VERIFY_FHENIX_CONTRACTS_CREATED || + step.type === TYPES.VERIFY_FHENIX_FAUCET_INTERACTION || + step.type === TYPES.VERIFY_FHENIX_WALLET_GAS_USAGE + ) { + outputStep.value = step?.prompt; } else if (step.type === TYPES.DATA_COLLECTION) { const dataCollectionType = step?.additionalData?.dataCollectionType; outputStep.value = { diff --git a/wondrous-bot-admin/vite.config.ts b/wondrous-bot-admin/vite.config.ts index bebf2aab8e..8c3054ff68 100644 --- a/wondrous-bot-admin/vite.config.ts +++ b/wondrous-bot-admin/vite.config.ts @@ -29,5 +29,6 @@ export default defineConfig({ VITE_PRODUCTION: process.env.VITE_PRODUCTION, VITE_STAGING: process.env.VITE_STAGING, VITE_WALLET_CONNECT_PROJECT_ID: process.env.VITE_WALLET_CONNECT_PROJECT_ID, + VITE_GOOGLE_CLIENT_ID: process.env.VITE_GOOGLE_CLIENT_ID, }, }); diff --git a/wondrous-bot-admin/yarn.lock b/wondrous-bot-admin/yarn.lock index 8efa87f088..c4e6781d22 100644 --- a/wondrous-bot-admin/yarn.lock +++ b/wondrous-bot-admin/yarn.lock @@ -1542,6 +1542,11 @@ resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.8.tgz#6b79032e760a0899cd4204710beede972a3a185f" integrity sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A== +"@react-oauth/google@^0.12.1": + version "0.12.1" + resolved "https://registry.yarnpkg.com/@react-oauth/google/-/google-0.12.1.tgz#b76432c3a525e9afe076f787d2ded003fcc1bee9" + integrity sha512-qagsy22t+7UdkYAiT5ZhfM4StXi9PPNvw0zuwNmabrWyMKddczMtBIOARflbaIj+wHiQjnMAsZmzsUYuXeyoSg== + "@reactour/mask@*": version "1.1.0" resolved "https://registry.yarnpkg.com/@reactour/mask/-/mask-1.1.0.tgz#e327306585ee3510e80169a7fa811e9d0b9448bb"