Skip to content

Commit

Permalink
MU WPCOM: dashboard: add launchpad (#41434)
Browse files Browse the repository at this point in the history
  • Loading branch information
ellatrix authored Jan 30, 2025
1 parent 66f8a78 commit 8308501
Show file tree
Hide file tree
Showing 12 changed files with 1,211 additions and 13 deletions.
743 changes: 738 additions & 5 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: minor
Type: added

Dashboard: add launchpad
5 changes: 5 additions & 0 deletions projects/packages/jetpack-mu-wpcom/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"@babel/core": "7.26.0",
"@babel/plugin-transform-react-jsx": "7.25.9",
"@babel/preset-react": "7.26.3",
"@babel/runtime": "7.24.7",
"@playwright/test": "1.48.2",
"@types/node": "^20.4.2",
"@types/react": "^18.2.28",
Expand All @@ -48,9 +49,11 @@
"dependencies": {
"@automattic/calypso-color-schemes": "3.1.3",
"@automattic/color-studio": "4.0.0",
"@automattic/components": "2.2.0",
"@automattic/i18n-utils": "1.2.3",
"@automattic/jetpack-base-styles": "workspace:*",
"@automattic/jetpack-shared-extension-utils": "workspace:*",
"@automattic/launchpad": "1.1.0",
"@automattic/page-pattern-modal": "1.1.5",
"@automattic/typography": "1.0.0",
"@popperjs/core": "^2.11.8",
Expand All @@ -73,6 +76,8 @@
"@wordpress/url": "4.16.0",
"clsx": "2.1.1",
"debug": "4.4.0",
"events": "^3.3.0",
"i18n-calypso": "7.0.0",
"preact": "^10.13.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
import { Gridicon, ConfettiAnimation } from '@automattic/components';
import { Button, Modal, Tooltip } from '@wordpress/components';
import { useCopyToClipboard } from '@wordpress/compose';
import { useState, useEffect } from '@wordpress/element';
import { Icon, copy } from '@wordpress/icons';
import { useTranslate } from 'i18n-calypso';
import { wpcomTrackEvent } from '../../../common/tracks';

import './celebrate-launch-modal.scss';

/**
* CelebrateLaunchModal component
*
* @param {object} props - Props.
* @param {Function} props.onRequestClose - Callback on modal close.
* @param {object} props.sitePlan - The site plan.
* @param {string} props.siteDomain - The site domain.
* @param {string} props.siteUrl - The site URL.
* @param {boolean} props.hasCustomDomain - Whether the site has a custom domain.
*
* @return {JSX.Element} The CelebrateLaunchModal component.
*/
export default function CelebrateLaunchModal( {
onRequestClose,
sitePlan,
siteDomain: siteSlug,
siteUrl,
hasCustomDomain,
} ) {
const translate = useTranslate();
const isPaidPlan = !! sitePlan;
const isBilledMonthly = sitePlan?.product_slug?.includes( 'monthly' );
const [ clipboardCopied, setClipboardCopied ] = useState( false );

useEffect( () => {
wpcomTrackEvent( `calypso_launchpad_celebration_modal_view`, {
product_slug: sitePlan?.product_slug,
} );
}, [ sitePlan?.product_slug ] );

/**
* Render the upsell content.
*
* @return {JSX.Element} The upsell content.
*/
function renderUpsellContent() {
let contentElement;
let buttonText;
let buttonHref;

if ( ! isPaidPlan && ! hasCustomDomain ) {
contentElement = (
<p>
{ translate(
'Supercharge your website with a {{strong}}custom address{{/strong}} that matches your blog, brand, or business.',
{ components: { strong: <strong /> } }
) }
</p>
);
buttonText = translate( 'Claim your domain' );
buttonHref = `https://wordpress.com/domains/add/${ siteSlug }`;
} else if ( isPaidPlan && isBilledMonthly && ! hasCustomDomain ) {
contentElement = (
<p>
{ translate(
'Interested in a custom domain? It’s free for the first year when you switch to annual billing.'
) }
</p>
);
buttonText = translate( 'Claim your domain' );
buttonHref = `https://wordpress.com/domains/add/${ siteSlug }`;
} else if ( isPaidPlan && ! hasCustomDomain ) {
contentElement = (
<p>
{ translate(
'Your paid plan includes a domain name {{strong}}free for one year{{/strong}}. Choose one that’s easy to remember and even easier to share.',
{ components: { strong: <strong /> } }
) }
</p>
);
buttonText = translate( 'Claim your free domain' );
buttonHref = `https://wordpress.com/domains/add/${ siteSlug }`;
} else if ( hasCustomDomain ) {
return null;
}

return (
<div className="launched__modal-upsell">
<div className="launched__modal-upsell-content">{ contentElement }</div>
<Button
variant="primary"
href={ buttonHref }
onClick={ () =>
wpcomTrackEvent( `calypso_launchpad_celebration_modal_upsell_clicked`, {
product_slug: sitePlan?.product_slug,
} )
}
>
<span>{ buttonText }</span>
</Button>
</div>
);
}

const ref = useCopyToClipboard( siteSlug, () => setClipboardCopied( true ) );

return (
<Modal onRequestClose={ onRequestClose } className="launched__modal">
<ConfettiAnimation />
<div className="launched__modal-content">
<div className="launched__modal-text">
<h1 className="launched__modal-heading">
{ translate( 'Congrats, your site is live!' ) }
</h1>
<p className="launched__modal-body">
{ translate( 'Now you can head over to your site and share it with the world.' ) }
</p>
</div>
<div className="launched__modal-actions">
<div className="launched__modal-site">
<div className="launched__modal-domain">
<p className="launched__modal-domain-text">{ siteSlug }</p>
<Tooltip
text={ clipboardCopied ? translate( 'Copied to clipboard!' ) : '' }
delay={ 0 }
hideOnClick={ false }
>
<Button
label={ translate( 'Copy URL' ) }
className="launchpad__clipboard-button"
borderless
size="compact"
ref={ ref }
onMouseLeave={ () => setClipboardCopied( false ) }
>
<Icon icon={ copy } size={ 18 } />
</Button>
</Tooltip>
</div>

<Button href={ siteUrl } target="_blank" className="launched__modal-view-site">
<Gridicon icon="domains" size={ 18 } />
<span className="launched__modal-view-site-text">{ translate( 'View site' ) }</span>
</Button>
</div>
</div>
</div>
{ renderUpsellContent() }
</Modal>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
$breakpoint-mobile: 782px; //Mobile size.

.launched__modal {
p {
font-size: 16px;
margin: 0;
}

&.components-modal__frame {
max-width: 640px;
}

.components-modal__header {
padding: 48px;
}

.components-modal__content {
margin: 0;
padding: 0;
}

&-content {
padding: 48px;
padding-bottom: 40px;
display: flex;
flex-direction: column;
gap: 32px;

@media (min-width: $breakpoint-mobile) {
min-width: 640px;
}
}

&-heading {
color: var(--studio-gray-100);
font-family: Recoleta, "Noto Serif", Georgia, "Times New Roman", Times, serif;
font-size: 2rem;
line-height: 40px;
font-weight: 400;
letter-spacing: 0.2px;
margin: 0;
padding-bottom: 8px;
}

&-domain {
display: flex;
justify-content: center;
align-items: center;
max-width: 100%;
@media (min-width: $breakpoint-mobile) {
max-width: 75%;
}
}

&-body,
&-domain-text {
margin: 0;
color: var(--studio-gray-80);
font-size: 1rem;
line-height: 24px;
}

&-domain-text {
padding: 0 8px;

// prevent text overflow
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}

&-buttons {
display: flex;
flex-direction: row;
justify-content: end;
gap: 16px;
}

&-site {
padding: 8px;
background: var(--studio-gray-0);
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: center;
@media (min-width: $breakpoint-mobile) {
flex-direction: row;
}
}

.launchpad__clipboard-button {
min-width: 18px;
opacity: 0;
}

.launchpad__clipboard-button:focus {
opacity: 1;
}

&-site:hover {
.launchpad__clipboard-button {
opacity: 1;
}
}

&-customize {
color: var(--studio-blue-50);
font-size: 0.875rem;
display: inline-flex;
flex-direction: row;
justify-content: start;
gap: 6px;
padding: 0;
margin: 0;
margin-top: 16px;
}

&-upsell {
border-top: 1px solid var(--studio-gray-5);
background: var(--studio-gray-0);
display: flex;
justify-content: center;
align-items: center;
padding: 32px 48px;
gap: 32px;
flex-direction: column;
@media (min-width: $breakpoint-mobile) {
flex-direction: row;
}
.components-button {
height: 42px;
}
}

&-upsell-content p {
margin-bottom: 0;
}

&-upsell-content-highlight {
font-weight: bold;
}

&-view-site {
display: flex;
gap: 4px;

font-weight: 500;
/* stylelint-disable-next-line */
font-size: 14px;
line-height: 20px;
letter-spacing: -0.154px;
color: #101517;
}

&-view-site:visited {
color: #101517;
}

&-view-site:hover {
text-decoration: underline;
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
import '../../common/public-path';
import React from 'react';
import ReactDOM from 'react-dom/client';
import CelebrateLaunchModal from './celebrate-launch/celebrate-launch-modal';
import WpcomLaunchpadWidget from './wpcom-launchpad-widget';
import WpcomSiteManagementWidget from './wpcom-site-management-widget';

const data = typeof window === 'object' ? window.JETPACK_MU_WPCOM_DASHBOARD_WIDGETS : {};

const widgets = [
{
id: 'wpcom_launchpad_widget_main',
Widget: WpcomLaunchpadWidget,
},
{
id: 'wpcom_site_preview_widget_main',
Widget: WpcomSiteManagementWidget,
Expand All @@ -19,3 +24,21 @@ widgets.forEach( ( { id, Widget } ) => {
root.render( <Widget { ...data } /> );
}
} );

const url = new URL( window.location.href );
if ( url.searchParams.has( 'celebrate-launch' ) ) {
url.searchParams.delete( 'celebrate-launch' );
window.history.replaceState( null, '', url.toString() );
const rootElement = document.createElement( 'div' );
document.body.appendChild( rootElement );
const root = ReactDOM.createRoot( rootElement );
root.render(
<CelebrateLaunchModal
{ ...data }
onRequestClose={ () => {
root.unmount();
rootElement.remove();
} }
/>
);
}
Loading

0 comments on commit 8308501

Please sign in to comment.