| 1 |
import { getOption, updateOption } from '@launch/api/WPApi'; |
| 2 |
import { AcceptTerms } from '@launch/components/BusinessInformation/AcceptTerms'; |
| 3 |
import { SiteTones } from '@launch/components/BusinessInformation/Tones'; |
| 4 |
import { LoadingIndicator } from '@launch/components/LoadingIndicator'; |
| 5 |
import { Title } from '@launch/components/Title'; |
| 6 |
import { useFetch } from '@launch/hooks/useFetch'; |
| 7 |
import { PageLayout } from '@launch/layouts/PageLayout'; |
| 8 |
import { pageState } from '@launch/state/factory'; |
| 9 |
import { usePagesStore } from '@launch/state/Pages'; |
| 10 |
import { useUserSelectionStore } from '@launch/state/user-selections'; |
| 11 |
import { getUrlParameter } from '@shared/utils/get-url-parameter'; |
| 12 |
import { Tooltip } from '@wordpress/components'; |
| 13 |
import { useEffect, useLayoutEffect, useState } from '@wordpress/element'; |
| 14 |
import { decodeEntities } from '@wordpress/html-entities'; |
| 15 |
import { __ } from '@wordpress/i18n'; |
| 16 |
import { Icon, info } from '@wordpress/icons'; |
| 17 |
|
| 18 |
const fetcher = async () => ({ title: await getOption('blogname') }); |
| 19 |
const fetchData = () => ({ key: 'site-info' }); |
| 20 |
export const state = pageState('Site Information', () => ({ |
| 21 |
ready: false, |
| 22 |
canSkip: false, |
| 23 |
useNav: true, |
| 24 |
onRemove: () => {}, |
| 25 |
})); |
| 26 |
const descriptionUrlParameter = getUrlParameter('description', false); |
| 27 |
const titleUrlParameter = getUrlParameter('title', false); |
| 28 |
|
| 29 |
export const SiteInformation = () => { |
| 30 |
const { loading } = useFetch(fetchData, fetcher); |
| 31 |
const nextPage = usePagesStore((state) => state.nextPage); |
| 32 |
const { |
| 33 |
businessInformation, |
| 34 |
setBusinessInformation, |
| 35 |
siteInformation, |
| 36 |
setSiteInformation, |
| 37 |
setSiteProfile, |
| 38 |
siteObjective, |
| 39 |
setCTALink, |
| 40 |
CTALink, |
| 41 |
setUrlParameters, |
| 42 |
setSiteQuestions, |
| 43 |
siteQA, |
| 44 |
setSiteStructure, |
| 45 |
} = useUserSelectionStore(); |
| 46 |
const isLandingPage = siteObjective === 'landing-page'; |
| 47 |
|
| 48 |
let resolvedTitle = siteInformation.title; |
| 49 |
const isTitleEmpty = !resolvedTitle; |
| 50 |
const isTitleDefault = resolvedTitle === window.extSharedData.siteTitle; |
| 51 |
if ((isTitleEmpty || isTitleDefault) && titleUrlParameter) { |
| 52 |
resolvedTitle = titleUrlParameter; |
| 53 |
} |
| 54 |
const [title, setTitle] = useState(decodeEntities(resolvedTitle || '')); |
| 55 |
const [description, setDescription] = useState( |
| 56 |
businessInformation.description || descriptionUrlParameter || '', |
| 57 |
); |
| 58 |
const [callToActionLink, setCallToActionLink] = useState(CTALink || ''); |
| 59 |
|
| 60 |
useEffect(() => { |
| 61 |
state.setState({ ready: false }); |
| 62 |
const timer = setTimeout(() => { |
| 63 |
setSiteInformation('title', title); |
| 64 |
setBusinessInformation('description', description); |
| 65 |
if (isLandingPage && callToActionLink) { |
| 66 |
setCTALink(callToActionLink); |
| 67 |
} |
| 68 |
state.setState({ ready: !!title.length }); |
| 69 |
setSiteProfile(undefined); // this also resets SOME state |
| 70 |
updateOption('extendify_site_profile', null); |
| 71 |
if (siteQA?.questions.length !== 0) { |
| 72 |
setSiteQuestions({ |
| 73 |
showHidden: false, |
| 74 |
questions: [], |
| 75 |
}); |
| 76 |
setSiteStructure(undefined); |
| 77 |
} |
| 78 |
}, 1000); |
| 79 |
return () => clearTimeout(timer); |
| 80 |
}, [ |
| 81 |
title, |
| 82 |
description, |
| 83 |
setSiteInformation, |
| 84 |
setBusinessInformation, |
| 85 |
isLandingPage, |
| 86 |
setCTALink, |
| 87 |
callToActionLink, |
| 88 |
setSiteProfile, |
| 89 |
siteQA, |
| 90 |
setSiteQuestions, |
| 91 |
setSiteStructure, |
| 92 |
]); |
| 93 |
|
| 94 |
useLayoutEffect(() => { |
| 95 |
if (!titleUrlParameter && !descriptionUrlParameter) return; |
| 96 |
|
| 97 |
setUrlParameters({ |
| 98 |
title: titleUrlParameter, |
| 99 |
description: descriptionUrlParameter, |
| 100 |
}); |
| 101 |
}, [setUrlParameters]); |
| 102 |
|
| 103 |
const pageTitle = isLandingPage |
| 104 |
? __('Tell Us About Your Landing Page', 'extendify-local') |
| 105 |
: __('Tell Us About Your Website', 'extendify-local'); |
| 106 |
|
| 107 |
const pageTitleDescription = isLandingPage |
| 108 |
? __( |
| 109 |
"Share your vision, and we'll craft a landing page that's perfectly tailored to your needs, ready to launch in no time. Let's begin by learning more about what you're building.", |
| 110 |
'extendify-local', |
| 111 |
) |
| 112 |
: __( |
| 113 |
"Share your vision, and we'll craft a website that's perfectly tailored to your needs, ready to launch in no time. Let's begin by learning more about what you're building.", |
| 114 |
'extendify-local', |
| 115 |
); |
| 116 |
|
| 117 |
return ( |
| 118 |
<PageLayout> |
| 119 |
<div className="grow overflow-y-auto px-6 py-8 md:p-12 3xl:p-16"> |
| 120 |
<Title title={pageTitle} description={pageTitleDescription} /> |
| 121 |
<div className="relative mx-auto w-full max-w-xl"> |
| 122 |
{loading ? ( |
| 123 |
<LoadingIndicator /> |
| 124 |
) : ( |
| 125 |
<form |
| 126 |
className="flex w-full flex-col gap-4" |
| 127 |
onSubmit={(e) => { |
| 128 |
e.preventDefault(); |
| 129 |
if (!state.getState().ready) return; |
| 130 |
nextPage(); |
| 131 |
}} |
| 132 |
> |
| 133 |
<SiteTitle |
| 134 |
title={title} |
| 135 |
setTitle={setTitle} |
| 136 |
isLandingPage={isLandingPage} |
| 137 |
/> |
| 138 |
<BusinessInfo |
| 139 |
description={description} |
| 140 |
setDescription={setDescription} |
| 141 |
siteObjective={siteObjective} |
| 142 |
/> |
| 143 |
{isLandingPage && !showSiteQuestions && ( |
| 144 |
<SiteCTA |
| 145 |
title={callToActionLink} |
| 146 |
setCTA={setCallToActionLink} |
| 147 |
/> |
| 148 |
)} |
| 149 |
<SiteTones /> |
| 150 |
<AcceptTerms /> |
| 151 |
</form> |
| 152 |
)} |
| 153 |
</div> |
| 154 |
</div> |
| 155 |
</PageLayout> |
| 156 |
); |
| 157 |
}; |
| 158 |
|
| 159 |
const SiteTitle = ({ title, setTitle, isLandingPage }) => { |
| 160 |
return ( |
| 161 |
<div> |
| 162 |
<label |
| 163 |
htmlFor="extendify-site-title-input" |
| 164 |
className="m-0 text-lg font-medium leading-8 text-gray-900 md:text-base md:leading-10" |
| 165 |
> |
| 166 |
{isLandingPage |
| 167 |
? __('Landing Page title (required)', 'extendify-local') |
| 168 |
: __('Website title (required)', 'extendify-local')} |
| 169 |
</label> |
| 170 |
<input |
| 171 |
data-test="site-title-input" |
| 172 |
autoComplete="off" |
| 173 |
// biome-ignore lint: allow autofocus here |
| 174 |
autoFocus |
| 175 |
type="text" |
| 176 |
name="site-title-input" |
| 177 |
id="extendify-site-title-input" |
| 178 |
className="input-focus h-12 w-full rounded-sm border border-gray-200 px-4 py-6 ring-offset-0" |
| 179 |
value={title} |
| 180 |
onChange={(e) => setTitle(e.target.value)} |
| 181 |
placeholder={__('Enter your website name', 'extendify-local')} |
| 182 |
/> |
| 183 |
</div> |
| 184 |
); |
| 185 |
}; |
| 186 |
|
| 187 |
const objectivePlaceholders = { |
| 188 |
business: __( |
| 189 |
'E.g., We are a yoga studio in London with professionally trained instructors with focus on hot yoga for therapeutic purposes.', |
| 190 |
'extendify-local', |
| 191 |
), |
| 192 |
ecommerce: __( |
| 193 |
'E.g., We are an online store specializing in eco-friendly home goods, offering sustainably sourced products to help you live a greener lifestyle.', |
| 194 |
'extendify-local', |
| 195 |
), |
| 196 |
blog: __( |
| 197 |
'E.g., A personal finance blog sharing expert tips on budgeting, investing, and achieving financial freedom for young professionals.', |
| 198 |
'extendify-local', |
| 199 |
), |
| 200 |
'landing-page': __( |
| 201 |
'E.g., A free ebook packed with actionable productivity tips and strategies to help you stay focused, manage your time effectively, and achieve your goals.', |
| 202 |
'extendify-local', |
| 203 |
), |
| 204 |
other: __( |
| 205 |
'E.g., A personal photography portfolio featuring a collection of landscape, portrait, and street photography, capturing moments from around the world.', |
| 206 |
'extendify-local', |
| 207 |
), |
| 208 |
}; |
| 209 |
const objectiveLabels = { |
| 210 |
business: __('Describe your business', 'extendify-local'), |
| 211 |
ecommerce: __('Describe your eCommerce website', 'extendify-local'), |
| 212 |
blog: __('Describe your blog', 'extendify-local'), |
| 213 |
'landing-page': __('Describe your Landing Page', 'extendify-local'), |
| 214 |
other: __('Describe your website', 'extendify-local'), |
| 215 |
}; |
| 216 |
|
| 217 |
const showSiteQuestions = window.extSharedData?.showSiteQuestions ?? false; |
| 218 |
|
| 219 |
const BusinessInfo = ({ description, setDescription, siteObjective }) => { |
| 220 |
return ( |
| 221 |
<div> |
| 222 |
<label |
| 223 |
htmlFor="extendify-site-info-input" |
| 224 |
className="m-0 text-lg font-medium leading-8 text-gray-900 md:text-base md:leading-10" |
| 225 |
> |
| 226 |
{showSiteQuestions |
| 227 |
? objectiveLabels.other |
| 228 |
: (objectiveLabels[siteObjective] ?? objectiveLabels.business)} |
| 229 |
</label> |
| 230 |
<textarea |
| 231 |
data-test="site-info-input" |
| 232 |
autoComplete="off" |
| 233 |
rows="4" |
| 234 |
name="site-info-input" |
| 235 |
id="extendify-site-info-input" |
| 236 |
className={ |
| 237 |
'input-focus placeholder:text-base h-40 w-full rounded-lg border border-gray-300 p-2 ring-offset-0 placeholder:italic placeholder:opacity-50' |
| 238 |
} |
| 239 |
value={description} |
| 240 |
onChange={(e) => setDescription(e.target.value)} |
| 241 |
placeholder={ |
| 242 |
showSiteQuestions |
| 243 |
? objectivePlaceholders.other |
| 244 |
: (objectivePlaceholders[siteObjective] ?? |
| 245 |
objectivePlaceholders.business) |
| 246 |
} |
| 247 |
/> |
| 248 |
</div> |
| 249 |
); |
| 250 |
}; |
| 251 |
|
| 252 |
const SiteCTA = ({ title, setCTA }) => { |
| 253 |
return ( |
| 254 |
<div> |
| 255 |
<div className="flex items-center space-x-1"> |
| 256 |
<label |
| 257 |
htmlFor="extendify-site-cta-input" |
| 258 |
className="m-0 text-lg font-medium leading-8 text-gray-900 md:text-base md:leading-10" |
| 259 |
> |
| 260 |
{__('Call-To-Action Link', 'extendify-local')} |
| 261 |
</label> |
| 262 |
<Tooltip |
| 263 |
delay={100} |
| 264 |
text={__( |
| 265 |
'This link will be used in all Call-to-Action buttons, directing visitors to your chosen destination.', |
| 266 |
'extendify-local', |
| 267 |
)} |
| 268 |
> |
| 269 |
<Icon icon={info} size="16" className="fill-current text-gray-600" /> |
| 270 |
</Tooltip> |
| 271 |
</div> |
| 272 |
<input |
| 273 |
data-test="site-cta-input" |
| 274 |
autoComplete="off" |
| 275 |
type="text" |
| 276 |
name="site-cta-input" |
| 277 |
id="extendify-site-cta-input" |
| 278 |
className="input-focus h-12 w-full rounded-sm border border-gray-200 px-4 py-6 ring-offset-0 placeholder:italic placeholder:opacity-50" |
| 279 |
value={title} |
| 280 |
onChange={(e) => setCTA(e.target.value)} |
| 281 |
placeholder="" |
| 282 |
/> |
| 283 |
</div> |
| 284 |
); |
| 285 |
}; |
| 286 |
|