diff --git a/packages/sitebuilder/package.json b/packages/sitebuilder/package.json index 87cf9b0a..481d1b6c 100644 --- a/packages/sitebuilder/package.json +++ b/packages/sitebuilder/package.json @@ -1,6 +1,6 @@ { "name": "@moderntribe/sitebuilder", - "version": "1.5.0", + "version": "1.6.0-alpha.10", "description": "SiteBuilder foundation", "main": "dist/index.js", "files": [ @@ -22,17 +22,18 @@ "author": "", "dependencies": { "@moderntribe/wme-ui": "workspace:*", - "@moderntribe/wme-utils": "workspace:*" + "@moderntribe/wme-utils": "workspace:*", + "@tanstack/react-query": "^4.14.3" }, "peerDependencies": { - "@wordpress/element": "^4.12.0", - "@wordpress/html-entities": "^3.14.0", - "@wordpress/i18n": "^4.14.0", "@emotion/react": "^11.9.3", "@emotion/styled": "^11.9.3", "@mui/icons-material": "^5.8.4", "@mui/material": "^5.9.2", "@mui/utils": "^5.9.1", + "@wordpress/element": "^4.12.0", + "@wordpress/html-entities": "^3.14.0", + "@wordpress/i18n": "^4.14.0", "react": "^18.2.0", "react-dom": "^18.2.0", "react-router-dom": "^6.4.2" diff --git a/packages/sitebuilder/sitebuilder.d.ts b/packages/sitebuilder/sitebuilder.d.ts index d44d7503..31dce08b 100644 --- a/packages/sitebuilder/sitebuilder.d.ts +++ b/packages/sitebuilder/sitebuilder.d.ts @@ -118,6 +118,58 @@ declare global { disable?: boolean; hidePagination?: boolean; } + export interface Domain { + domain: string + is_available: boolean + package: { + id: number + identity: string + metadata: DomainMetadata + addons: DomainAddon[] + auto_renew: boolean + bandwidth: number + billing_type: string + environment_type: string + is_wildcard: boolean + label: string + name: string + orderable_terms: DomainOrderableTerms + term_fees: DomainTermFees + tld: DomainTld + trial_period: number + type: string + } + pricing: any[] + tld: DomainTld + } + interface DomainAddon { + id: number + identity: string + metadata: DomainMetadata + description: string + monthly_fee: string + name: string + term_fees: DomainTermFees + type: string + } + interface DomainTermFees { + [key: string]: string + } + + interface DomainOrderableTerms { + [key: string]: string + } + + interface DomainTld { + id: number + identity: string + metadata: DomainMetadata + } + + interface DomainMetadata { + scope: string + uri: string + } interface Window { ppcp_onboarding_productionCallback: (authCode: string, sharedId: string) => void; diff --git a/packages/sitebuilder/src/SiteBuilder.tsx b/packages/sitebuilder/src/SiteBuilder.tsx index 0753abc2..1bac228a 100644 --- a/packages/sitebuilder/src/SiteBuilder.tsx +++ b/packages/sitebuilder/src/SiteBuilder.tsx @@ -2,27 +2,34 @@ import React, { lazy } from 'react'; import { ThemeProvider, createTheme } from '@mui/material'; import { theme as WME_THEME } from '@moderntribe/wme-ui'; import { HashRouter, Routes, Route, Navigate } from 'react-router-dom'; +import { + QueryClient, + QueryClientProvider +} from '@tanstack/react-query'; import SiteBuilderProvider from '@sb/contexts/SiteBuilderProvider'; import WizardProvider from '@sb/contexts/WizardProvider'; import WizardWrapper from '@sb/wizards/WizardWrapper'; import Loadable from '@sb/components/Loadable'; import { SB_THEME } from '@sb/constants'; -const SetupScreen = Loadable(lazy(() => import('@sb/setup/SetupScreen'))); +const SetupScreen: (props: any) => JSX.Element = Loadable(lazy(() => import('@sb/setup/SetupScreen'))); const siteBuilderTheme = createTheme(WME_THEME, SB_THEME); +const queryClient = new QueryClient(); const SiteBuilder = () => ( - - - - } /> - } /> - } /> - - - + + + + + } /> + } /> + } /> + + + + ); diff --git a/packages/sitebuilder/src/constants/theme.ts b/packages/sitebuilder/src/constants/theme.ts index eaacef15..5ed63da9 100644 --- a/packages/sitebuilder/src/constants/theme.ts +++ b/packages/sitebuilder/src/constants/theme.ts @@ -48,12 +48,6 @@ declare module '@mui/material/styles/createPalette' { let theme = { palette: { - primary: { - main: '#303F9F', - dark: '#2A3353', - grey: '#949494', - white: '#FFF', - }, text: { primary: '#2A3353', secondary: '#2A3353', diff --git a/packages/sitebuilder/src/contexts/GoLiveProvider.tsx b/packages/sitebuilder/src/contexts/GoLiveProvider.tsx index ceba5d67..a52c5be5 100644 --- a/packages/sitebuilder/src/contexts/GoLiveProvider.tsx +++ b/packages/sitebuilder/src/contexts/GoLiveProvider.tsx @@ -15,7 +15,7 @@ export interface GoLiveProviderContextInterface { setIsLoading: (loading: boolean) => void; getHasDomainNextText: (hasDomain: string) => void; setHasDomain: (hasDomain: string) => void; - setShowGetDomain: (show: boolean) => void; + setShowPurchaseNavigation: (show: boolean) => void; } export interface DomainVerficationSuccessInterface { @@ -72,6 +72,7 @@ export const GoLiveContext = createContext { const [goLiveState, setGoLiveState] = useState(GoLiveData()); + const { selectedDomains } = goLiveState; const goLiveNonce = GO_LIVE_PROPS.ajax?.nonce || ''; const goLiveAction = GO_LIVE_PROPS.ajax?.action || ''; @@ -80,6 +81,7 @@ const GoLiveProvider = ({ children }: { children: React.ReactNode }) => { const navigate = useNavigate(); const [searchParams, setSearchParams] = useSearchParams(); + const purchaseNavigation = searchParams.get('purchase') === 'true'; const activeStep = searchParams.get('step') ? Number(searchParams.get('step')) @@ -94,46 +96,35 @@ const GoLiveProvider = ({ children }: { children: React.ReactNode }) => { }, [retryVerfication]); useEffect(() => { - if (activeStep === 3) { - if (! capturedDomain) { - navigate('/wizard/go-live'); - } - - if ( - goLiveState.verificationStatus && ! ( - goLiveState.verificationStatus === 'connected' || - goLiveState.verificationStatus === 'advanced' - ) - ) { - navigate('/wizard/go-live'); - } - - if (goLiveState.verificationStatus === 'error') { - navigate('/wizard/go-live?step=2'); + if (activeStep === 2) { + if (purchaseNavigation) { + if (selectedDomains.length === 0) { + searchParams.set('step', '1'); + setSearchParams(searchParams); + } } } - - if (activeStep === 1 && goLiveState.hasDomain !== null) { - const { steps, hasDomain, showGetDomain } = goLiveState; - - steps[ 0 ].nextText = continueStr; - steps[ 0 ].hideBack = true; - - if (hasDomain === 'no') { - steps[ 0 ].nextText = showGetDomain ? haveDomain : getDomain; - } - - if (hasDomain === 'no' && showGetDomain) { - steps[ 0 ].disableNext = true; - steps[ 0 ].hideBack = false; + if (activeStep === 3) { + if (! purchaseNavigation) { + if (! capturedDomain) { + navigate('/wizard/go-live'); + } + + if ( + goLiveState.verificationStatus && ! ( + goLiveState.verificationStatus === 'connected' || + goLiveState.verificationStatus === 'advanced' + ) + ) { + navigate('/wizard/go-live'); + } + + if (goLiveState.verificationStatus === 'error') { + navigate('/wizard/go-live?step=2'); + } } - - setGoLiveState({ - ...goLiveState, - steps - }); } - }, [activeStep, goLiveState.showGetDomain]); + }, [activeStep, purchaseNavigation]); const submitGoLiveForm = () => { const { steps, showLogoutButton } = goLiveState; @@ -312,7 +303,7 @@ const GoLiveProvider = ({ children }: { children: React.ReactNode }) => { const getHasDomainNextText = (hasDomain:string) => { switch (hasDomain) { case 'yes': - return goLiveState.showGetDomain ? haveDomain : continueStr; + return haveDomain; case 'no': return getDomain; default: @@ -331,14 +322,13 @@ const GoLiveProvider = ({ children }: { children: React.ReactNode }) => { }); }; - const setShowGetDomain = (show:boolean) => { - const { steps } = goLiveState; - steps[ 0 ].nextText = haveDomain; - setGoLiveState({ - ...goLiveState, - showGetDomain: show, - steps - }); + const setShowPurchaseNavigation = (show: boolean) => { + if (show) { + searchParams.set('purchase', 'true'); + } else { + searchParams.delete('purchase'); + } + setSearchParams(searchParams); }; return ( @@ -349,7 +339,7 @@ const GoLiveProvider = ({ children }: { children: React.ReactNode }) => { submitDomainVerification, setIsLoading, setHasDomain, - setShowGetDomain, + setShowPurchaseNavigation, handleDomainVerificationRequest, getHasDomainNextText } }> diff --git a/packages/sitebuilder/src/contexts/WizardProvider.tsx b/packages/sitebuilder/src/contexts/WizardProvider.tsx index 6891e37b..31e305e2 100644 --- a/packages/sitebuilder/src/contexts/WizardProvider.tsx +++ b/packages/sitebuilder/src/contexts/WizardProvider.tsx @@ -83,7 +83,8 @@ const WizardProvider = ({ children }: { children: React.ReactNode }) => { if (typeof targetStep !== 'number') { return; } - setSearchParams({ step: String(targetStep) }); + searchParams.set('step', String(targetStep)); + setSearchParams(searchParams); }; const goToNextStep = () => { diff --git a/packages/sitebuilder/src/hooks/index.ts b/packages/sitebuilder/src/hooks/index.ts index 7ae025e8..90e26d07 100644 --- a/packages/sitebuilder/src/hooks/index.ts +++ b/packages/sitebuilder/src/hooks/index.ts @@ -1,3 +1,5 @@ +export * from './useCreatePurchaseFlow'; +export * from './useFindDomain'; export * from './useFirstTimeConfiguration'; export * from './useLookAndFeel'; export * from './useGoLive'; diff --git a/packages/sitebuilder/src/hooks/useCreatePurchaseFlow.ts b/packages/sitebuilder/src/hooks/useCreatePurchaseFlow.ts new file mode 100644 index 00000000..5d55209d --- /dev/null +++ b/packages/sitebuilder/src/hooks/useCreatePurchaseFlow.ts @@ -0,0 +1,51 @@ +import { useMutation } from '@tanstack/react-query'; +import { handleActionRequest } from '@moderntribe/wme-utils'; +import { GO_LIVE_PROPS } from '@sb/constants'; + +export interface Response { + action: string + data: any + callback_url: string + return_url: any + uuid: string + site_id: number + outcome: Outcome +} + +export interface Outcome { + status: string + details: Details +} + +export interface Details { + id: number + identity: string + scope: string + purchased_domain: string +} + +async function createPurchaseFlow(domains: { domainName: string, packageId: number}[]) { + const goLiveNonce = GO_LIVE_PROPS.ajax?.nonce || ''; + const goLiveAction = GO_LIVE_PROPS.ajax?.action || ''; + const response: any = await handleActionRequest({ + _wpnonce: goLiveNonce, + action: goLiveAction, + sub_action: 'create-purchase-flow', + domains: domains.map(({ domainName, packageId }) => ({ domain_name: domainName, package_id: packageId })), + }); + return response as Response; +} + +export function useCreatePurchaseFlow() { + const goLivePurchaseFlowUrl = GO_LIVE_PROPS?.purchaseFlowUrl || ''; + const mutation = useMutation( + createPurchaseFlow, + { + onSuccess: (data) => { + window.location.href = goLivePurchaseFlowUrl.replace('UUID', data.uuid); + }, + } + ); + + return mutation; +} diff --git a/packages/sitebuilder/src/hooks/useFindDomain.ts b/packages/sitebuilder/src/hooks/useFindDomain.ts new file mode 100644 index 00000000..63a7d32e --- /dev/null +++ b/packages/sitebuilder/src/hooks/useFindDomain.ts @@ -0,0 +1,120 @@ +import * as React from 'react'; +import { useQuery, UseQueryResult } from '@tanstack/react-query'; +import { handleActionRequest } from '@moderntribe/wme-utils'; +import { GO_LIVE_PROPS } from '@sb/constants'; +import { GoLiveStringData } from '@sb/wizards/go-live/data/constants'; + +import { useGoLive } from './useGoLive'; + +type UseFindDomainProps = { + maxSelectedDomains: number; +} + +type UseFindDomain = UseQueryResult & { + search: string + setSearch: (search: string) => void + selectedDomains: Domain[] + toggleSelectedDomain: (domain: Domain) => void +} + +type Response = { + domain: Domain + alternatives: Domain[] +} +type Error = { + code?: string + message?: string +} + +export function useFindDomain(props?: UseFindDomainProps): UseFindDomain { + const { + goLiveState: { selectedDomains, searchDomain: search }, + setGoLiveState, + } = useGoLive(); + const { maxSelectedDomains = 1 } = props || {}; + const { goLiveProviderText: { checkout } } = GoLiveStringData; + + const query = useQuery(['domains', search], async () => { + const goLiveNonce = GO_LIVE_PROPS.ajax?.nonce || ''; + const goLiveAction = GO_LIVE_PROPS.ajax?.action || ''; + const response: any = await handleActionRequest({ + _wpnonce: goLiveNonce, + action: goLiveAction, + sub_action: 'search-domains', + domain: search + }); + return response as Response; + }, { + select: (data) => { + if (! data) { + return []; + } + return [ + data.domain, + ...data.alternatives, + ].filter((_) => _); + }, + enabled: !! search, + keepPreviousData: true, + refetchOnWindowFocus: false, + }); + + function toggleSelectedDomain(domain: Domain) { + const selected = selectedDomains.find((_) => _.domain === domain.domain); + setGoLiveState((prevGoLiveState) => { + const { selectedDomains: prev } = prevGoLiveState; + const newSelected = selected ? prev.filter((_) => _.domain !== domain.domain) : [domain, ...prev]; + return { + ...prevGoLiveState, + selectedDomains: newSelected.slice(0, maxSelectedDomains), + }; + }); + } + React.useEffect(() => { + setGoLiveState((prevGoLiveState) => { + const { stepsAlternative } = prevGoLiveState; + if (selectedDomains.length) { + stepsAlternative[ 0 ].nextText = `${ checkout } (${ selectedDomains.length })`; + stepsAlternative[ 0 ].disableNext = false; + } else { + stepsAlternative[ 0 ].nextText = checkout; + stepsAlternative[ 0 ].disableNext = true; + } + return { + ...prevGoLiveState, + stepsAlternative, + }; + }); + }, [selectedDomains.length]); + + function emptySelectedDomains() { + setGoLiveState((prevGoLiveState) => { + const { stepsAlternative } = prevGoLiveState; + stepsAlternative[ 0 ].nextText = checkout; + stepsAlternative[ 0 ].disableNext = true; + return { + ...prevGoLiveState, + stepsAlternative, + selectedDomains: [], + }; + }); + } + + function setSearch(newSearch: string) { + setGoLiveState((prevGoLiceState) => { + return { + ...prevGoLiceState, + searchDomain: newSearch, + }; + }); + emptySelectedDomains(); + } + + return { + ...query, + search, + setSearch, + selectedDomains, + toggleSelectedDomain, + }; +} diff --git a/packages/sitebuilder/src/hooks/useGoLive.ts b/packages/sitebuilder/src/hooks/useGoLive.ts index aaf19799..1e13989d 100644 --- a/packages/sitebuilder/src/hooks/useGoLive.ts +++ b/packages/sitebuilder/src/hooks/useGoLive.ts @@ -9,7 +9,7 @@ export function useGoLive() { submitDomainVerification, setIsLoading, setHasDomain, - setShowGetDomain, + setShowPurchaseNavigation, handleDomainVerificationRequest, getHasDomainNextText } = useContext(GoLiveContext) as GoLiveProviderContextInterface; @@ -20,7 +20,7 @@ export function useGoLive() { submitDomainVerification, setIsLoading, setHasDomain, - setShowGetDomain, + setShowPurchaseNavigation, handleDomainVerificationRequest, getHasDomainNextText }; diff --git a/packages/sitebuilder/src/utils/index.ts b/packages/sitebuilder/src/utils/index.ts index 2bb2020f..93f0c358 100644 --- a/packages/sitebuilder/src/utils/index.ts +++ b/packages/sitebuilder/src/utils/index.ts @@ -4,3 +4,4 @@ export * from './generateNewPassword'; export * from './getWizardCloseArgs'; export * from './uploadImage'; export * from './handleTelemetryRequest'; +export * from './parseDomainListItem'; diff --git a/packages/sitebuilder/src/utils/parseDomainListItem.ts b/packages/sitebuilder/src/utils/parseDomainListItem.ts new file mode 100644 index 00000000..9d56c62a --- /dev/null +++ b/packages/sitebuilder/src/utils/parseDomainListItem.ts @@ -0,0 +1,60 @@ +import { sprintf, _n } from '@wordpress/i18n'; +import { GoLiveStringData } from '@sb/wizards/go-live/data/constants'; + +type DomainListItem = { + name: string, + disabled: boolean, + price?: string, + chipLabel: string, + chipColor: 'success' | undefined, + selected: boolean, +} + +function getPrice(termFees: DomainTermFees, isAvailable: boolean) { + if (! isAvailable) { + return undefined; + } + + // Get the terms available for this domain. + const terms = Object.keys(termFees); + const shortestTerm = Math.min(...terms.map((term) => Number(term))); + const termToYear = shortestTerm / 12; + const price = termFees[ shortestTerm ]; + + // eslint-disable-next-line @wordpress/i18n-translator-comments + const perYear = _n('year', 'years', termToYear, 'nexcess-mapps'); + + return sprintf('%1$s / %2$s %3$s', + price, + termToYear, + perYear + ); +} + +function getChipLabel(isAvailable: boolean, isSelected: boolean) { + if (! isAvailable) { + return GoLiveStringData.domainItems.taken; + } + if (isSelected) { + return GoLiveStringData.domainItems.selected; + } + return GoLiveStringData.domainItems.available; +} + +function getChipColor(isAvailable: boolean, isSelected: boolean): 'success' | undefined { + if (! isAvailable || isSelected) { + return undefined; + } + return 'success'; +} + +export function parseDomainListItem(domain: Domain, selected: boolean): DomainListItem { + return ({ + name: domain.domain, + disabled: ! domain.is_available, + price: getPrice(domain.package.term_fees, domain.is_available), + chipLabel: getChipLabel(domain.is_available, selected), + chipColor: getChipColor(domain.is_available, selected), + selected, + }); +} diff --git a/packages/sitebuilder/src/wizards/first-time-configuration/ScreenWrapper.tsx b/packages/sitebuilder/src/wizards/first-time-configuration/ScreenWrapper.tsx index 6fdc5889..1e95bbf6 100644 --- a/packages/sitebuilder/src/wizards/first-time-configuration/ScreenWrapper.tsx +++ b/packages/sitebuilder/src/wizards/first-time-configuration/ScreenWrapper.tsx @@ -1,7 +1,7 @@ import { Box } from '@mui/material'; import { styled } from '@mui/material/styles'; -const ScreenWrapper = styled(Box, { +const ScreenWrapper: any = styled(Box, { name: 'FtcWrapper', slot: 'Root', })(() => ({ diff --git a/packages/sitebuilder/src/wizards/go-live/GoLiveWizard.tsx b/packages/sitebuilder/src/wizards/go-live/GoLiveWizard.tsx index 7a9a856b..7e93d9b9 100644 --- a/packages/sitebuilder/src/wizards/go-live/GoLiveWizard.tsx +++ b/packages/sitebuilder/src/wizards/go-live/GoLiveWizard.tsx @@ -3,40 +3,41 @@ import { WizardFooter } from '@moderntribe/wme-ui'; import { useTheme } from '@mui/material'; import { __ } from '@wordpress/i18n'; import { useSearchParams } from 'react-router-dom'; -import { useWizard, useGoLive } from '@sb/hooks'; -import { NEXCESS_DOMAIN_REGISTRATION_URL } from '@sb/constants'; +import { useWizard, useGoLive, useCreatePurchaseFlow } from '@sb/hooks'; import { SkipVerificationWarning } from '@go-live/screens'; import WizardCloseWarning from '@sb/wizards/WizardCloseWarning'; const GoLiveWizard = () => { - const { wizardState: { showCloseWarning }, goToNextStep, goToPreviousStep, goToStep } = useWizard(); - const { goLiveState: { steps, hasDomain, lastStep, showGetDomain, showLogoutButton, verificationStatus }, goLiveState, setShowGetDomain, setGoLiveState, submitGoLiveForm } = useGoLive(); + const { wizardState: { showCloseWarning }, goToNextStep, goToPreviousStep, goToStep, closeAll } = useWizard(); + const { goLiveState: { steps: stepsOriginal, stepsAlternative, selectedDomains, hasDomain, lastStep, showLogoutButton, verificationStatus }, setShowPurchaseNavigation, submitGoLiveForm, setGoLiveState } = useGoLive(); const [showVerificationWarning, setShowVerificationWarning] = useState(false); const theme = useTheme(); + const createPurchaseFlow = useCreatePurchaseFlow(); const [searchParams] = useSearchParams(); const activeStep = searchParams.get('step') ? Number(searchParams.get('step')) : 1; + const purchaseNavigation = searchParams.get('purchase') === 'true'; const stepIndex = activeStep >= 1 ? activeStep - 1 : 0; const handleNext = () => { if (activeStep === 1) { - if (hasDomain === 'no' && NEXCESS_DOMAIN_REGISTRATION_URL && ! showGetDomain) { - setShowGetDomain(true); - return false; + if (hasDomain === 'no' && ! purchaseNavigation) { + return setShowPurchaseNavigation(true); } - setGoLiveState({ - ...goLiveState, - hasDomain: 'yes', - showGetDomain: false, - }); - goToNextStep(); } - if (activeStep === 2 && verificationStatus === 'advanced') { - setShowVerificationWarning(true); - return false; + if (activeStep === 2) { + if (verificationStatus === 'advanced') { + return setShowVerificationWarning(true); + } + if (purchaseNavigation) { + return createPurchaseFlow.mutate(selectedDomains.map((domain) => ({ + domainName: domain.domain, + packageId: domain.package.id + }))); + } } - goToNextStep(); + return goToNextStep(); }; const handleSkipVerificationWarningClose = () => { @@ -49,19 +50,31 @@ const GoLiveWizard = () => { }; const handleBack = () => { - if (hasDomain === 'no' && showGetDomain) { - setShowGetDomain(false); - return; + if (activeStep === 1) { + if (purchaseNavigation) { + setGoLiveState((prevState) => ({ + ...prevState, + selectedDomains: [], + searchDomain: '', + })); + setShowPurchaseNavigation(false); + } } goToPreviousStep(); }; const handleSave = () => { if (activeStep === lastStep) { - submitGoLiveForm(); + if (purchaseNavigation) { + closeAll(); + } else { + submitGoLiveForm(); + } } }; + const steps = purchaseNavigation ? stepsAlternative : stepsOriginal; + return ( <> { @@ -97,6 +110,8 @@ const GoLiveWizard = () => { backText={ __('Back', 'nexcess-mapps') } skipText={ __('Skip', 'nexcess-mapps') } nextText={ steps[ stepIndex ].nextText || __('Next', 'nexcess-mapps') } + loadingText={ steps[ stepIndex ].loadingText || __('Loading', 'nexcess-mapps') } + isLoading={ createPurchaseFlow.isLoading } /> { showCloseWarning && } diff --git a/packages/sitebuilder/src/wizards/go-live/data/constants.ts b/packages/sitebuilder/src/wizards/go-live/data/constants.ts index 6f8664e1..eb0811ce 100644 --- a/packages/sitebuilder/src/wizards/go-live/data/constants.ts +++ b/packages/sitebuilder/src/wizards/go-live/data/constants.ts @@ -4,30 +4,33 @@ export const GoLiveStringData = { goLiveProviderText: { getDomain: __('Get My Domain', 'nexcess-mapps'), haveDomain: __('I\'ve Got My Domain', 'nexcess-mapps'), + checkout: __('Checkout', 'nexcess-mapps'), continueStr: __('Continue', 'nexcess-mapps'), errorMessage: __('There was an error while updating your site\'s domain.', 'nexcess-mapps'), errorMessageVerification: __('There was an error while verifying your site\'s domain.', 'nexcess-mapps'), errorNotPointed: __('This domain is not pointing to your website.', 'nexcess-mapps'), // TODO: change wording to use product name errorNotRegistered: __('This domain is not registered.', 'nexcess-mapps'), - errorGeneral: __('We\'re unable to connect your domain.', 'nexcess-mapps'), + errorGeneral: __('We\'re unable to connect your domain.', 'nexcess-mapps') }, start: { screenTitle1: __('Set your site domain', 'nexcess-mapps'), - screenTitle2: __('Own your perfect domain with Nexcess.', 'nexcess-mapps'), + screenTitle2: __('Find the perfect domain.', 'nexcess-mapps'), screenDescription1: __('Let\'s get your site set up with the perfect domain. If you don\'t already have one, don\'t worry - we can help you out.', 'nexcess-mapps'), - screenDescription2: __('Nexcess helps you find, create and own your perfect domain. It\'s so easy, even we could do it. Once you\'ve got your domain, come back here and select "I\'ve Got My Domain".', 'nexcess-mapps'), + screenDescription2: __('Enter the domain name you want your store to have, and we\'ll see if it\'s available. If it isn\'t, we\'ll make suggestions about ones that are.', 'nexcess-mapps'), actionLabel: __('Have you purchased your custom domain?', 'nexcess-mapps'), actionTitle1: __('Yes! I have my own custom domain.', 'nexcess-mapps'), actionTitle2: __('No, I need one!', 'nexcess-mapps'), actionContent1: __('If you have your own domain already then you\'re ready to connect and go live.', 'nexcess-mapps'), - actionContent2: __('Nexcess will help you find and own your perfect domain name.', 'nexcess-mapps') + actionContent2: __('Nexcess will help you find and own your perfect domain name.', 'nexcess-mapps'), + defaultError: __('Something went wrong, please try again', 'nexcess-mapps') }, verifyDomain: { screenTitle: __('Verify your domain', 'nexcess-mapps'), screenDescription: __('We\'ll verify your domain and get it ready to use. This can take a while, but don\'t worry - we\'ll walk you through it.', 'nexcess-mapps'), + screenNotice: __('If you\'re using a subdomain (ex: store.example.com), please stop here and reach out to support at', 'nexcess-mapps'), goLiveLabelText: __('Enter the domain you want to connect', 'nexcess-mapps'), goLivePlaceholderText: __('yourdomain.com', 'nexcess-mapps'), - errorDomainFormat: __('The domain entered does not appear to be a valid format.', 'nexcess-mapps'), + errorDomainFormat: __('The domain entered does not appear to be a valid format.', 'nexcess-mapps') }, updateSiteUrl: { screenTitle: __('You\'re ready to go live with', 'nexcess-mapps'), @@ -37,6 +40,14 @@ export const GoLiveStringData = { loginUrlLabelText: __('Copy Your New Login URL', 'nexcess-mapps'), loginUrlHelperText: __('Copy and save this URL. Once your domain is connected you\'ll need it to login.', 'nexcess-mapps') }, + connectWithNexcess: { + screenTitle: __('Good choice! We’ll need to connect to your Nexcess account.', 'nexcess-mapps'), + screenDescription: __('We\'ll make sure your chosen domain is managed by your Nexcess account. Once connected, we\'ll navigate you back here to complete the process.', 'nexcess-mapps'), + }, + claimYourDomain: { + screenTitle: __('is all yours!', 'nexcess-mapps'), + screenDescription: __('While a new domain is often ready for use within an hour, it can take up to 8 hours to completely process.', 'nexcess-mapps') + }, errorDomainGeneral: { accountContent: __('It looks like we\'re having trouble connecting your domain.', 'nexcess-mapps'), accountCta: __('Contact Support', 'nexcess-mapps') @@ -61,7 +72,7 @@ export const GoLiveStringData = { advancedLabelPart2: __('Skip verification', 'nexcess-mapps'), advancedContentPart1: __('By default, we check that the appropriate DNS records are set before changing your domain. If you\'re behind a proxy or are using another more advanced DNS technique, you may wish to skip this validation.', 'nexcess-mapps'), advancedContentPart2: __('Your site may become inaccessible if its DNS records are invalid, so please double-check your DNS configuration before continuing.', 'nexcess-mapps'), - advancedContentPart3: __('If anything does go wrong, please reach out to support and we\'ll get you up and running!', 'nexcess-mapps'), + advancedContentPart3: __('If anything does go wrong, please reach out to support and we\'ll get you up and running!', 'nexcess-mapps') }, successDomainConnected: { statusSuccessPart1: __('Your domain is ready to connect!', 'nexcess-mapps'), @@ -74,5 +85,10 @@ export const GoLiveStringData = { message2: __('If anything does go wrong, please reach out to support and we\'ll get you up and running!', 'nexcess-mapps'), continueButton: __('Continue without verification', 'nexcess-mapps'), nevermind: __('Do not skip verification', 'nexcess-mapps') + }, + domainItems: { + taken: __('Taken', 'nexcess-mapps'), + selected: __('Selected', 'nexcess-mapps'), + available: __('Available', 'nexcess-mapps') } }; diff --git a/packages/sitebuilder/src/wizards/go-live/data/go-live-data.tsx b/packages/sitebuilder/src/wizards/go-live/data/go-live-data.tsx index 6bec983c..3fbf8d90 100644 --- a/packages/sitebuilder/src/wizards/go-live/data/go-live-data.tsx +++ b/packages/sitebuilder/src/wizards/go-live/data/go-live-data.tsx @@ -1,20 +1,22 @@ import React from 'react'; import { __ } from '@wordpress/i18n'; import { GO_LIVE_PROPS } from '@sb/constants'; -import { Start, VerifyDomain, UpdateSiteUrl } from '../screens'; +import { Start, VerifyDomain, UpdateSiteUrl, ConnectWithNexcess, FindDomain, ClaimYourDomain } from '../screens'; export interface GoLiveInterface { isLoading: boolean; verifyingUrl: string; lastStep: number; hasDomain: string | null; + selectedDomains: Domain[]; + searchDomain: string; skipDnsVerification: boolean; verificationStatus: string; verificationErrorType: boolean | string; verificationMessage: string; - showGetDomain: boolean; showLogoutButton: boolean; steps: Array; + stepsAlternative: Array; } const localData: GoLiveInterface = { @@ -22,11 +24,12 @@ const localData: GoLiveInterface = { verifyingUrl: '', lastStep: 3, hasDomain: null, + selectedDomains: [], + searchDomain: '', skipDnsVerification: false, verificationStatus: 'default', verificationErrorType: false, verificationMessage: '', - showGetDomain: false, showLogoutButton: false, steps: [ { @@ -66,6 +69,44 @@ const localData: GoLiveInterface = { disableNext: false, }, ], + stepsAlternative: [ + { + id: 0, + label: __('Start', 'nexcess-mapps'), + hideBack: false, + hideSkip: true, + nextText: __('Checkout', 'nexcess-mapps'), + loadingText: __('Saving', 'nexcess-mapps'), + backText: __('Back', 'nexcess-mapps'), + disable: true, + disableNext: true, + screen: , + }, + { + id: 1, + label: __('Connect with Nexcess', 'nexcess-mapps'), + hideBack: false, + hideSkip: true, + nextText: __('Connect Your Nexcess Account', 'nexcess-mapps'), + loadingText: __('Connecting…', 'nexcess-mapps'), + backText: __('Back', 'nexcess-mapps'), + screen: , + disable: true, + disableNext: false, + }, + { + id: 2, + label: __('Claim Your Domain', 'nexcess-mapps'), + hideBack: false, + hideSkip: true, + nextText: __('Save & Exit', 'nexcess-mapps'), + loadingText: __('Processing', 'nexcess-mapps'), + backText: __('Back', 'nexcess-mapps'), + screen: , + disable: true, + disableNext: false, + }, + ] }; const goLiveScreenData = (): GoLiveInterface => { diff --git a/packages/sitebuilder/src/wizards/go-live/screens/ClaimYourDomain.tsx b/packages/sitebuilder/src/wizards/go-live/screens/ClaimYourDomain.tsx new file mode 100644 index 00000000..526e80cc --- /dev/null +++ b/packages/sitebuilder/src/wizards/go-live/screens/ClaimYourDomain.tsx @@ -0,0 +1,59 @@ +import React from 'react'; +import { + Box, + Typography +} from '@mui/material'; +import { __, sprintf } from '@wordpress/i18n'; +import { useSearchParams } from 'react-router-dom'; +import { IMAGE_DIR } from '@sb/constants'; +import { GoLiveStringData } from '../data/constants'; + +const ClaimYourDomain = () => { + const { + claimYourDomain: { + screenTitle, + screenDescription, + } + } = GoLiveStringData; + const [searchParams] = useSearchParams(); + + // TODO: Make it works with more than one domain. + const domain = searchParams.get('domain') || __('The domain', 'nexcess-mapps'); + const title = sprintf('%1$s %2$s', + domain, + screenTitle + ); + return ( + + + + { title } + + + { screenDescription } + + + ); +}; + +export default ClaimYourDomain; diff --git a/packages/sitebuilder/src/wizards/go-live/screens/ConnectWithNexcess.tsx b/packages/sitebuilder/src/wizards/go-live/screens/ConnectWithNexcess.tsx new file mode 100644 index 00000000..3ec096d9 --- /dev/null +++ b/packages/sitebuilder/src/wizards/go-live/screens/ConnectWithNexcess.tsx @@ -0,0 +1,89 @@ +import React from 'react'; +import { + Box, + List, + ThemeProvider, + createTheme +} from '@mui/material'; +import { ListItemCheckout, WizardSectionTitle } from '@moderntribe/wme-ui'; + +import { useGoLive } from '@sb/hooks'; +import { parseDomainListItem } from '@sb/utils/parseDomainListItem'; +import { GoLiveStringData } from '../data/constants'; + +const ConnectWithNexcess = () => { + const { + connectWithNexcess: { + screenTitle, + screenDescription + } + } = GoLiveStringData; + + const { + goLiveState, + } = useGoLive(); + + const { + selectedDomains, + } = goLiveState; + + return ( + + + createTheme({ + ...theme, + palette: { + ...theme.palette, + primary: { + ...theme.palette.primary, + dark: '#0033B8', + }, + }, + }) + } + > + + { selectedDomains.map((domain, index) => { + const domainListItem = parseDomainListItem(domain, true); + return ( + + + { index < selectedDomains.length - 1 && ( + + + ) } + + + ); + }) } + + + + + + ); +}; + +export default ConnectWithNexcess; diff --git a/packages/sitebuilder/src/wizards/go-live/screens/FindDomain.tsx b/packages/sitebuilder/src/wizards/go-live/screens/FindDomain.tsx new file mode 100644 index 00000000..5a269d0f --- /dev/null +++ b/packages/sitebuilder/src/wizards/go-live/screens/FindDomain.tsx @@ -0,0 +1,141 @@ +import React from 'react'; +import { Box, Divider, IconButton, InputAdornment, List, ThemeProvider, createTheme } from '@mui/material'; +import { ListItemCheckout, TextInput, WizardSectionTitle } from '@moderntribe/wme-ui'; +import { Search } from '@mui/icons-material'; +import { IMAGE_DIR } from '@sb/constants'; +import { useFindDomain } from '@sb/hooks'; +import { GoLiveStringData } from '@sb/wizards/go-live/data/constants'; +import { parseDomainListItem } from '@sb/utils/parseDomainListItem'; + +import { + ErrorStatusMessage +} from './partials'; + +const loadingSx = { + '@keyframes fadeIn': { + from: { + opacity: 0.25, + } + }, + '@keyframes rotate': { + from: { + transform: 'rotate(0deg)', + }, + to: { + transform: 'rotate(359deg)', + }, + }, + animation: 'rotate 1s infinite linear', +}; + +const FindDomain = () => { + const { start: { + screenTitle2: title, + screenDescription2: description, + defaultError + } } = GoLiveStringData; + + const { search, setSearch, data: domains = [], selectedDomains, toggleSelectedDomain, isFetching, isError, error } = useFindDomain(); + const [internalSearch, setInternalSearch] = React.useState(search); + + const handleSubmit = (event: React.FormEvent) => { + event.preventDefault(); + setSearch(internalSearch); + }; + + return ( + + + + + + { isFetching ? ( + + ) : ( + + + + ) } + + } + value={ internalSearch } + onChange={ (event) => setInternalSearch(event.target.value) } + /> + + + + createTheme({ + ...theme, + palette: { + ...theme.palette, + primary: { + ...theme.palette.primary, + dark: '#0033B8', + }, + }, + }) + } + > + + + { domains.map((domain, index) => { + const selected = selectedDomains.some((_) => _.domain === domain.domain); + const domainListItem = parseDomainListItem(domain, selected); + return ( + + toggleSelectedDomain(domain) } /> + { index < domains.length - 1 && ( + + + + ) } + + ); + }) } + { + isFetching && ( + + ) + } + + + { isError && } + + ); +}; + +export default FindDomain; diff --git a/packages/sitebuilder/src/wizards/go-live/screens/Start.tsx b/packages/sitebuilder/src/wizards/go-live/screens/Start.tsx index b6bfd9fe..9a664bbc 100644 --- a/packages/sitebuilder/src/wizards/go-live/screens/Start.tsx +++ b/packages/sitebuilder/src/wizards/go-live/screens/Start.tsx @@ -1,9 +1,8 @@ import React from 'react'; import { Box, Link, Typography, useTheme } from '@mui/material'; import { CardSelectGroup, CardSelectItem, WizardSectionTitle } from '@moderntribe/wme-ui'; -import { OpenInNew } from '@mui/icons-material'; import { __ } from '@wordpress/i18n'; -import { IMAGE_DIR, NEXCESS_DOMAIN_REGISTRATION_URL } from '@sb/constants'; +import { IMAGE_DIR } from '@sb/constants'; import { useGoLive } from '@sb/hooks'; import { GoLiveStringData } from '../data/constants'; @@ -26,12 +25,10 @@ const PoweredByNexcessFooter = () => { }; const Start = () => { - const { goLiveState: { hasDomain, showGetDomain }, setHasDomain } = useGoLive(); + const { goLiveState: { hasDomain }, setHasDomain } = useGoLive(); const { start: { - screenTitle1, - screenTitle2, - screenDescription1, - screenDescription2, + screenTitle1: title, + screenDescription1: description, actionLabel, actionTitle1, actionTitle2, @@ -39,8 +36,9 @@ const Start = () => { actionContent2 } } = GoLiveStringData; - const title = showGetDomain ? screenTitle2 : screenTitle1; - const description = showGetDomain ? screenDescription2 : screenDescription1; + const handleDomainPurchased = (event: React.MouseEvent, value:string) => { + setHasDomain(value); + }; const cardSx = { '& .WmeCardSelectItem-root': { @@ -49,94 +47,41 @@ const Start = () => { } }; - const registerSx = { - display: 'flex', - alignItems: 'center', - fontWeight: 500, - cursor: 'pointer', - '& .MuiSvgIcon-root': { - fontSize: '1.25rem', - marginLeft: '8px', - } - }; - - const handleDomainPurchased = (event: React.MouseEvent, value:string) => { - setHasDomain(value); - }; - - const handleRegisterDomainClick = () => { - setHasDomain('yes'); - - if (NEXCESS_DOMAIN_REGISTRATION_URL) { - window.open(NEXCESS_DOMAIN_REGISTRATION_URL, '_blank'); - } - }; - return ( - { showGetDomain - ? <> - + + + { actionLabel } + + + - - { __('Register A Domain With Nexcess', 'nexcess-mapps') } - - - - : <> - } /> - - { actionLabel } - - - - } - /> - - - } + ); }; diff --git a/packages/sitebuilder/src/wizards/go-live/screens/VerifyDomain.tsx b/packages/sitebuilder/src/wizards/go-live/screens/VerifyDomain.tsx index 6965d9e4..e53dae60 100644 --- a/packages/sitebuilder/src/wizards/go-live/screens/VerifyDomain.tsx +++ b/packages/sitebuilder/src/wizards/go-live/screens/VerifyDomain.tsx @@ -1,11 +1,14 @@ import React, { useState, useEffect } from 'react'; +import { __ } from '@wordpress/i18n'; import { Box, FormControl, FormLabel, IconButton, TextField, - InputAdornment + InputAdornment, + Link, + FormHelperText } from '@mui/material'; import { ArrowForward, @@ -46,6 +49,7 @@ const VerifyDomain = () => { verifyDomain: { screenTitle, screenDescription, + screenNotice, goLiveLabelText, goLivePlaceholderText, errorDomainFormat @@ -127,7 +131,7 @@ const VerifyDomain = () => { component="form" onSubmit={ handleSubmit } > - + { goLiveLabelText } { } - { + + { `${ screenNotice } ` } + { __('storebuilder@nexcess.net', 'nexcess-mapps') }. + + { { (verificationStatus === 'error' && ! validDomain) && } { (verificationStatus === 'connected' && validDomain) && } diff --git a/packages/sitebuilder/src/wizards/go-live/screens/index.ts b/packages/sitebuilder/src/wizards/go-live/screens/index.ts index 7dbad3d7..14b0f34c 100644 --- a/packages/sitebuilder/src/wizards/go-live/screens/index.ts +++ b/packages/sitebuilder/src/wizards/go-live/screens/index.ts @@ -1,3 +1,6 @@ +export { default as ClaimYourDomain } from './ClaimYourDomain'; +export { default as ConnectWithNexcess } from './ConnectWithNexcess'; +export { default as FindDomain } from './FindDomain'; export { default as Start } from './Start'; export { default as VerifyDomain } from './VerifyDomain'; export { default as UpdateSiteUrl } from './UpdateSiteUrl'; diff --git a/packages/storebuilder/mix-manifest.json b/packages/storebuilder/mix-manifest.json index b1486766..7e15621f 100644 --- a/packages/storebuilder/mix-manifest.json +++ b/packages/storebuilder/mix-manifest.json @@ -1,6 +1,5 @@ { "/dist/index.js": "/dist/index.js", - "/dist/assets/ftcComplete.png": "/dist/assets/ftcComplete.png", "/dist/assets/images/flat-rate-icon.png": "/dist/assets/images/flat-rate-icon.png", "/dist/assets/images/ftc-complete.png": "/dist/assets/images/ftc-complete.png", "/dist/assets/images/ftc-product-count-1-10.svg": "/dist/assets/images/ftc-product-count-1-10.svg", @@ -24,9 +23,5 @@ "/dist/assets/images/setup-shipping-poster.png": "/dist/assets/images/setup-shipping-poster.png", "/dist/assets/images/stripe-logo.png": "/dist/assets/images/stripe-logo.png", "/dist/assets/images/usps-icon-full.png": "/dist/assets/images/usps-icon-full.png", - "/dist/assets/images/usps-icon.png": "/dist/assets/images/usps-icon.png", - "/dist/assets/images.d.ts": "/dist/assets/images.d.ts", - "/dist/assets/images.d.ts.map": "/dist/assets/images.d.ts.map", - "/dist/assets/images.ts": "/dist/assets/images.ts", - "/dist/assets/index.d.ts": "/dist/assets/index.d.ts" + "/dist/assets/images/usps-icon.png": "/dist/assets/images/usps-icon.png" } diff --git a/packages/storebuilder/src/contexts/WizardProvider.tsx b/packages/storebuilder/src/contexts/WizardProvider.tsx index ea7e66f4..ef0ac0a6 100644 --- a/packages/storebuilder/src/contexts/WizardProvider.tsx +++ b/packages/storebuilder/src/contexts/WizardProvider.tsx @@ -97,7 +97,8 @@ const WizardProvider = ({ children }: { children: React.ReactNode }) => { if (typeof targetStep !== 'number') { return; } - setSearchParams({ step: String(targetStep) }); + searchParams.set('step', String(targetStep)); + setSearchParams(searchParams); }; const goToNextStep = () => { diff --git a/packages/wme-ui/src/components/list-item-checkout/list-item-checkout.tsx b/packages/wme-ui/src/components/list-item-checkout/list-item-checkout.tsx index 7fe2760d..4a462b3c 100644 --- a/packages/wme-ui/src/components/list-item-checkout/list-item-checkout.tsx +++ b/packages/wme-ui/src/components/list-item-checkout/list-item-checkout.tsx @@ -34,6 +34,7 @@ const StyledListItemCheckout = styled(MuiListItem, { margin: 0, display: 'flex', gap: theme.spacing(2), + paddingRight: theme.spacing(1), '& .MuiButton-text.WmeButton-root': { fontWeight: '600', color: theme.palette.text.disabled, @@ -48,6 +49,7 @@ const PrimaryText = styled(Typography, { fontWeight: 600, color: selected ? theme.palette.primary.dark : theme.palette.text.primary, flexGrow: 1, + fontSize: '0.88rem', })); const SecondaryText = styled(Typography, { @@ -57,6 +59,7 @@ const SecondaryText = styled(Typography, { display: 'block', fontWeight: 600, color: theme.palette.text.primary, + fontSize: '0.88rem', })); const StyledListItemCheckoutContentInner = styled(Box, { @@ -76,6 +79,13 @@ const StyledCheckCircleIcon = styled(CheckCircleIcon, { color: theme.palette.primary.dark, })); +const StyledChip = styled(Chip, { + name: 'WmeListItemCheckout', + slot: 'Chip', +})(({ theme }) => ({ + marginRight: theme.spacing(2), +})); + const ListItemCheckout: React.FC = (props) => { const { name, @@ -87,6 +97,7 @@ const ListItemCheckout: React.FC = (props) => { icon = , iconDisabled = , iconSelected = , + onClick, ...rest } = props; @@ -100,7 +111,7 @@ const ListItemCheckout: React.FC = (props) => { + {iconToRender} )} @@ -109,7 +120,7 @@ const ListItemCheckout: React.FC = (props) => { {name} {price} - {chipLabel && } + {chipLabel && } ); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 494aa12a..571681cc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -97,6 +97,7 @@ importers: '@mui/icons-material': ^5.8.4 '@mui/material': ^5.9.2 '@mui/utils': ^5.9.1 + '@tanstack/react-query': ^4.14.3 '@types/jquery': ^3.5.14 '@types/node': ^18.7.16 '@types/react': ^18.0.17 @@ -129,6 +130,7 @@ importers: '@mui/icons-material': 5.10.3_dtruv572645qlhu5xokhjjhjka '@mui/material': 5.10.4_vbxcdxy3bftsefaguotlgalomu '@mui/utils': 5.10.3_react@18.2.0 + '@tanstack/react-query': 4.14.3_react@18.2.0 '@wordpress/element': 4.14.0 '@wordpress/html-entities': 3.16.0 '@wordpress/i18n': 4.16.0 @@ -6515,6 +6517,27 @@ packages: resolution: {integrity: sha512-oocsqY7g0cR+Gur5jRQLSrX2OtpMLMse1I10JQBm8CdGMrDkh1Mg2gjsiquMHRtBs4Qwu5wgEp5GgIYHk4SNPw==} dev: false + /@tanstack/query-core/4.14.3: + resolution: {integrity: sha512-1OrxZk5jSKAjTIDgH/OHa6GfVpgGDVbCVf3KHQjXLFdutK4PSSXQIyX7I1HvBR3mYKyvFPo6yXKNG7QrkwUj9g==} + dev: false + + /@tanstack/react-query/4.14.3_react@18.2.0: + resolution: {integrity: sha512-PP7DEFsOMl/JaGQr1B4IeGN3lqHrC5EpG+AgG03OlVIOQMtXNhb0N66Nt1pdrladvzYsKD20L3O3550rBzgLtg==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-native: '*' + peerDependenciesMeta: + react-dom: + optional: true + react-native: + optional: true + dependencies: + '@tanstack/query-core': 4.14.3 + react: 18.2.0 + use-sync-external-store: 1.2.0_react@18.2.0 + dev: false + /@testing-library/dom/8.17.1: resolution: {integrity: sha512-KnH2MnJUzmFNPW6RIKfd+zf2Wue8mEKX0M3cpX6aKl5ZXrJM1/c/Pc8c2xDNYQCnJO48Sm5ITbMXgqTr3h4jxQ==} engines: {node: '>=12'} @@ -7411,7 +7434,6 @@ packages: dependencies: webpack: 5.74.0_webpack-cli@4.10.0 webpack-cli: 4.10.0_webpack@5.74.0 - dev: true /@webpack-cli/info/1.5.0_webpack-cli@4.10.0: resolution: {integrity: sha512-e8tSXZpw2hPl2uMJY6fsMswaok5FdlGNRTktvFk2sD8RjH0hE2+XistawJx1vmKteh4NmGmNUrp+Tb2w+udPcQ==} @@ -7420,7 +7442,6 @@ packages: dependencies: envinfo: 7.8.1 webpack-cli: 4.10.0_webpack@5.74.0 - dev: true /@webpack-cli/serve/1.7.0_webpack-cli@4.10.0: resolution: {integrity: sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q==} @@ -7432,7 +7453,6 @@ packages: optional: true dependencies: webpack-cli: 4.10.0_webpack@5.74.0 - dev: true /@wordpress/babel-plugin-import-jsx-pragma/3.2.0_@babel+core@7.19.0: resolution: {integrity: sha512-XK3Sdpi9MWoy5qPHnRroY/ypX0VtT5yI5809u5As1P/3k4vlXNw8USH4lJ+rkurAOVqqN5mFlf2XAL9AkpfXyg==} @@ -8131,7 +8151,7 @@ packages: loader-utils: 2.0.2 make-dir: 3.1.0 schema-utils: 2.7.1 - webpack: 5.74.0 + webpack: 5.74.0_webpack-cli@4.10.0 /babel-plugin-add-react-displayname/0.0.5: resolution: {integrity: sha512-LY3+Y0XVDYcShHHorshrDbt4KFWL4bSeniCtl4SYZbask+Syngk1uMPCeN9+nSiZo6zX5s0RTq/J9Pnaaf/KHw==} @@ -9373,7 +9393,7 @@ packages: postcss-value-parser: 4.2.0 schema-utils: 3.1.1 semver: 7.3.7 - webpack: 5.74.0 + webpack: 5.74.0_webpack-cli@4.10.0 /css-loader/6.7.1_webpack@5.74.0: resolution: {integrity: sha512-yB5CNFa14MbPJcomwNh3wLThtkZgcNyI2bNMRt8iE5Z8Vwl7f8vQXFAzn2HDOJvtDq2NTZBUGMSUNNyrv3/+cw==} @@ -10044,7 +10064,6 @@ packages: resolution: {integrity: sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==} engines: {node: '>=4'} hasBin: true - dev: true /errno/0.1.8: resolution: {integrity: sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==} @@ -11309,7 +11328,6 @@ packages: /fastest-levenshtein/1.0.16: resolution: {integrity: sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==} engines: {node: '>= 4.9.1'} - dev: true /fastq/1.13.0: resolution: {integrity: sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==} @@ -11357,7 +11375,7 @@ packages: dependencies: loader-utils: 2.0.2 schema-utils: 3.1.1 - webpack: 5.74.0 + webpack: 5.74.0_webpack-cli@4.10.0 /file-system-cache/1.1.0: resolution: {integrity: sha512-IzF5MBq+5CR0jXx5RxPe4BICl/oEhBSXKaL9fLhAXrIfIUS77Hr4vzrYyqYMHN6uTt+BOqi3fDCTjjEBCjERKw==} @@ -15883,7 +15901,7 @@ packages: klona: 2.0.5 postcss: 8.4.16 semver: 7.3.7 - webpack: 5.74.0 + webpack: 5.74.0_webpack-cli@4.10.0 /postcss-logical/5.0.4_postcss@8.4.16: resolution: {integrity: sha512-RHXxplCeLh9VjinvMrZONq7im4wjWGlRJAqmAVLXyZaXwfDWP73/oq4NdIp+OZwhQUMj0zjqDfM5Fj7qby+B4g==} @@ -17181,7 +17199,6 @@ packages: engines: {node: '>= 0.10'} dependencies: resolve: 1.22.1 - dev: true /recursive-readdir/2.2.2: resolution: {integrity: sha512-nRCcW9Sj7NuZwa2XvH9co8NPeXUBhZP7CRKJtU+cS6PW9FpCIFoI5ib0NT1ZrbNuPoRy0ylyCaUL8Gih4LSyFg==} @@ -18288,7 +18305,7 @@ packages: dependencies: loader-utils: 2.0.2 schema-utils: 3.1.1 - webpack: 5.74.0 + webpack: 5.74.0_webpack-cli@4.10.0 /style-loader/3.3.1_webpack@5.74.0: resolution: {integrity: sha512-GPcQ+LDJbrcxHORTRes6Jy2sfvK2kS6hpSfI/fXhPt+spVzxF6LJ1dHLN9zIGmVaaP044YKaIatFaufENRiDoQ==} @@ -18555,7 +18572,7 @@ packages: schema-utils: 3.1.1 serialize-javascript: 6.0.0 terser: 5.15.0 - webpack: 5.74.0 + webpack: 5.74.0_webpack-cli@4.10.0 /terser/4.8.1: resolution: {integrity: sha512-4GnLC0x667eJG0ewJTa6z/yXrbLGv80D9Ru6HIpCQmO+Q4PfEtBFi0ObSckqwL6VyQv/7ENJieXHo2ANmdQwgw==} @@ -19198,6 +19215,14 @@ packages: serialize-query-params: 1.3.6_query-string@7.1.1 dev: false + /use-sync-external-store/1.2.0_react@18.2.0: + resolution: {integrity: sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + react: 18.2.0 + dev: false + /use/3.1.1: resolution: {integrity: sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==} engines: {node: '>=0.10.0'} @@ -19429,7 +19454,6 @@ packages: rechoir: 0.7.1 webpack: 5.74.0_webpack-cli@4.10.0 webpack-merge: 5.8.0 - dev: true /webpack-dev-middleware/3.7.3_webpack@4.46.0: resolution: {integrity: sha512-djelc/zGiz9nZj/U7PTBi2ViorGJXEWo/3ltkPbDyxCXhhEXkW0ce99falaok4TPj+AsxLiXJR0EBOb0zh9fKQ==} @@ -19469,7 +19493,7 @@ packages: mime-types: 2.1.35 range-parser: 1.2.1 schema-utils: 4.0.0 - webpack: 5.74.0 + webpack: 5.74.0_webpack-cli@4.10.0 /webpack-dev-server/4.11.0_5v66e2inugklgvlh4huuavolfq: resolution: {integrity: sha512-L5S4Q2zT57SK7tazgzjMiSMBdsw+rGYIX27MgPgx7LDhWO0lViPrHKoLS7jo5In06PWYAhlYu3PbyoC6yAThbw==} @@ -19605,7 +19629,6 @@ packages: dependencies: clone-deep: 4.0.1 wildcard: 2.0.0 - dev: true /webpack-notifier/1.15.0: resolution: {integrity: sha512-N2V8UMgRB5komdXQRavBsRpw0hPhJq2/SWNOGuhrXpIgRhcMexzkGQysUyGStHLV5hkUlgpRiF7IUXoBqyMmzQ==} @@ -19763,7 +19786,6 @@ packages: - '@swc/core' - esbuild - uglify-js - dev: true /webpackbar/5.0.2_webpack@5.74.0: resolution: {integrity: sha512-BmFJo7veBDgQzfWXl/wwYXr/VFus0614qZ8i9znqcl9fnEdiVkdbi0TedLQ6xAK92HZHDJ0QmyQ0fmuZPAgCYQ==} @@ -19869,7 +19891,6 @@ packages: /wildcard/2.0.0: resolution: {integrity: sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==} - dev: true /word-wrap/1.2.3: resolution: {integrity: sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==}