Skip to content

Commit 31d8cdb

Browse files
authored
Dashboard v2: lazy load current plan (expiry + manage link) (#103601)
1 parent ac5fcfd commit 31d8cdb

File tree

8 files changed

+69
-36
lines changed

8 files changed

+69
-36
lines changed

client/dashboard/app/router.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,9 +84,11 @@ const siteRoute = createRoute( {
8484
const siteOverviewRoute = createRoute( {
8585
getParentRoute: () => siteRoute,
8686
path: '/',
87-
loader: ( { params: { siteSlug } } ) =>
87+
loader: ( { params: { siteSlug }, preload } ) =>
8888
Promise.all( [
89-
queryClient.ensureQueryData( siteCurrentPlanQuery( siteSlug ) ),
89+
// The current plan is nice to have preloaded, but not blocking for
90+
// navigation.
91+
preload ? queryClient.ensureQueryData( siteCurrentPlanQuery( siteSlug ) ) : undefined,
9092
queryClient.ensureQueryData( siteEngagementStatsQuery( siteSlug ) ),
9193
] ),
9294
} ).lazy( () =>
Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
import './style.scss';
22

3-
export function TextBlur( { text }: { text: string } ) {
4-
return <span className="text-blur" data-text={ text } />;
3+
export function TextBlur( { children }: { children: React.ReactNode } ) {
4+
return (
5+
<span className="dashboard-text-blur" aria-hidden="true">
6+
{ children }
7+
</span>
8+
);
59
}

client/dashboard/components/text-blur/style.scss

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
.text-blur::after {
2-
content: attr(data-text);
1+
.dashboard-text-blur {
2+
user-select: none;
33
// The larger the font-size, the more blurring is needed.
44
filter: blur(0.3em);
55
opacity: 0.5;

client/dashboard/data/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ export interface SitePlan {
5656
}
5757

5858
export interface Plan {
59-
id: string;
59+
id: string | null;
6060
current_plan?: boolean;
6161
expiry?: string;
6262
subscribed_date?: string;

client/dashboard/sites/overview/index.tsx

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {
88
} from '@wordpress/components';
99
import { __ } from '@wordpress/i18n';
1010
import { wordpress } from '@wordpress/icons';
11-
import { siteQuery, siteCurrentPlanQuery, siteEngagementStatsQuery } from '../../app/queries';
11+
import { siteQuery, siteEngagementStatsQuery } from '../../app/queries';
1212
import { siteRoute } from '../../app/router';
1313
import { PageHeader } from '../../components/page-header';
1414
import PageLayout from '../../components/page-layout';
@@ -28,10 +28,9 @@ import './style.scss';
2828
function SiteOverview() {
2929
const { siteSlug } = siteRoute.useParams();
3030
const { data: site } = useQuery( siteQuery( siteSlug ) );
31-
const { data: currentPlan } = useQuery( siteCurrentPlanQuery( siteSlug ) );
3231
const { data: engagementStats } = useQuery( siteEngagementStatsQuery( siteSlug ) );
3332

34-
if ( ! site || ! currentPlan || ! engagementStats ) {
33+
if ( ! site || ! engagementStats ) {
3534
return;
3635
}
3736
return (
@@ -55,7 +54,7 @@ function SiteOverview() {
5554
}
5655
>
5756
<HStack alignment="flex-start" spacing={ 8 }>
58-
<Sidebar site={ site } currentPlan={ currentPlan } />
57+
<Sidebar site={ site } />
5958
<VStack spacing={ 8 }>
6059
<Card style={ { padding: '16px' } }>
6160
<VStack>

client/dashboard/sites/overview/sidebar.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import { __experimentalVStack as VStack } from '@wordpress/components';
22
import SiteCard from './site-card';
3-
import type { Site, Plan } from '../../data/types';
3+
import type { Site } from '../../data/types';
44

55
/**
66
* Sidebar component for the site overview page
77
*/
8-
export default function Sidebar( props: { site: Site; phpVersion?: string; currentPlan: Plan } ) {
8+
export default function Sidebar( props: { site: Site } ) {
99
return (
1010
<VStack spacing={ 4 } style={ { minWidth: '300px' } }>
1111
<SiteCard { ...props } />

client/dashboard/sites/overview/site-card.tsx

Lines changed: 50 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -8,22 +8,23 @@ import {
88
ExternalLink,
99
} from '@wordpress/components';
1010
import { dateI18n } from '@wordpress/date';
11+
import { createInterpolateElement } from '@wordpress/element';
1112
import { __, sprintf } from '@wordpress/i18n';
12-
import { sitePHPVersionQuery } from '../../app/queries';
13+
import { sitePHPVersionQuery, siteCurrentPlanQuery } from '../../app/queries';
1314
import { TextBlur } from '../../components/text-blur';
1415
import { getSiteStatusLabel } from '../../utils/site-status';
1516
import { getFormattedWordPressVersion } from '../../utils/wp-version';
1617
import SitePreview from '../site-preview';
17-
import type { Site, Plan } from '../../data/types';
18+
import type { Site } from '../../data/types';
1819

1920
function PHPVersion( { siteSlug }: { siteSlug: string } ) {
20-
return useQuery( sitePHPVersionQuery( siteSlug ) ).data ?? <TextBlur text="X.Y" />;
21+
return useQuery( sitePHPVersionQuery( siteSlug ) ).data ?? <TextBlur>X.Y</TextBlur>;
2122
}
2223

2324
/**
2425
* SiteCard component to display site information in a card format
2526
*/
26-
export default function SiteCard( { site, currentPlan }: { site: Site; currentPlan: Plan } ) {
27+
export default function SiteCard( { site }: { site: Site } ) {
2728
const { URL: url, is_private, is_wpcom_atomic } = site;
2829
const wpVersion = getFormattedWordPressVersion( site );
2930

@@ -77,7 +78,7 @@ export default function SiteCard( { site, currentPlan }: { site: Site; currentPl
7778
) }
7879
</HStack>
7980
) }
80-
<PlanDetails site={ site } currentPlan={ currentPlan } />
81+
<PlanDetails site={ site } />
8182
</VStack>
8283
</VStack>
8384
</Card>
@@ -103,40 +104,67 @@ function FieldTitle( { children }: { children: React.ReactNode } ) {
103104
);
104105
}
105106

106-
function PlanDetails( { site, currentPlan }: { site: Site; currentPlan: Plan } ) {
107-
if ( ! site.plan || ! currentPlan ) {
107+
function PlanDetails( { site }: { site: Site } ) {
108+
const { data: currentPlan } = useQuery( siteCurrentPlanQuery( site.slug ) );
109+
110+
if ( ! site.plan ) {
108111
return null;
109112
}
110113

111114
const {
112115
plan: { product_name_short, is_free: isFree },
113116
} = site;
114-
const { expiry, id } = currentPlan;
117+
115118
return (
116119
<VStack>
117120
<FieldTitle>{ __( 'Plan' ) }</FieldTitle>
118121
{ product_name_short && <Text>{ product_name_short }</Text> }
119-
<Text>{ getPlanExpirationMessage( { isFree, expiry } ) }</Text>
120-
{ id ? (
121-
<Button href={ `/purchases/subscriptions/${ site.slug }/${ id }` } variant="link">
122-
{ __( 'Manage subscription' ) }
123-
</Button>
122+
{ isFree ? (
123+
<>
124+
<Text>{ __( 'No expiration date.' ) }</Text>
125+
<Button href={ `/plans/${ site.slug }` } variant="link">
126+
{ __( 'Upgrade' ) }
127+
</Button>
128+
</>
124129
) : (
125-
<Button href={ `/plans/${ site.slug }` } variant="link">
126-
{ __( 'Upgrade' ) }
127-
</Button>
130+
<>
131+
{ currentPlan ? (
132+
<>
133+
<Text>{ getPlanExpirationMessage( currentPlan.expiry ) }</Text>
134+
<Button
135+
href={ `/purchases/subscriptions/${ site.slug }/${ currentPlan.id }` }
136+
variant="link"
137+
>
138+
{ __( 'Manage subscription' ) }
139+
</Button>
140+
</>
141+
) : (
142+
<>
143+
<Text>
144+
<TextBlur>{ getPlanExpirationMessage( new Date().toISOString() ) }</TextBlur>
145+
</Text>
146+
{ /* @ts-expect-error inert is not typed */ }
147+
<Button inert href="" variant="link">
148+
<TextBlur>{ __( 'Manage subscription' ) }</TextBlur>
149+
</Button>
150+
</>
151+
) }
152+
</>
128153
) }
129154
</VStack>
130155
);
131156
}
132157

133-
function getPlanExpirationMessage( { isFree, expiry }: { isFree: boolean; expiry?: string } ) {
134-
if ( isFree ) {
135-
return __( 'No expiration date.' );
158+
function getPlanExpirationMessage( isoDate?: string ) {
159+
if ( ! isoDate ) {
160+
return null;
136161
}
137-
return (
138-
expiry &&
162+
163+
return createInterpolateElement(
139164
/* translators: %s: date of plan's expiration date. Eg. August 20, 2025 */
140-
sprintf( __( 'Expires on %s.' ), dateI18n( 'F j, Y', expiry ) )
165+
sprintf( __( 'Expires on <time>%s</time>.' ), dateI18n( 'F j, Y', isoDate ) ),
166+
{
167+
time: <time dateTime={ isoDate } />,
168+
}
141169
);
142170
}

client/dashboard/sites/overview/uptime-card.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ function UptimeCardEnabled( { siteSlug }: { siteSlug: string } ) {
3535
heading={
3636
uptimePercentage === undefined ? (
3737
<>
38-
<TextBlur text={ sprintf( percentageString, '100' ) } />
38+
<TextBlur>{ sprintf( percentageString, '100' ) }</TextBlur>
3939
<VisuallyHidden>{ __( 'Loading…' ) }</VisuallyHidden>
4040
</>
4141
) : (

0 commit comments

Comments
 (0)