Repository: Automattic/wp-calypso
Pull request #103558: Dashboard v2: lazy load php version
Author: login: ellatrix
Created: 2025-05-20T14:39:48Z | Merged: 2025-05-21T15:28:29Z

<!--
Link a related Github/Linear issue to this PR. If the PR does not immediately resolve the issue,
for example, it requires a separate deployment to production, avoid
using the "fixes" keyword. Consider using "part of" instead.
-->

<img width="331" alt="image" src="https://github.com/user-attachments/assets/00ae23fb-d9a2-4e24-9fe3-a18c03c2e969" />


Btw, in reality, the version pops in so fast it's not even visible. I had to set it to undefined to be able to take the screenshot.

Similar to #103547.

## Proposed Changes

* Don't block the site overview from rendering for the php version. I've made sure the columns are fixed width so there will be no layout shift.

## Why are these changes being made?
<!--
It's easy to see what a PR does but much harder to find out why it was made,
particularly when researching old changes in history. Record an explanation of
the motivation behind this change and how it will help.
-->

* The less requests needed for the initial site endpoint, the better.

## Testing Instructions

<!--
Add as many details as possible to help others reproduce the issue and test the fix.
"Before / After" screenshots can also be very helpful when the change is visual.
-->

* Site overview, check that php version loads for atomic sites.

## Pre-merge Checklist

<!--
Complete applicable items on this checklist **before** merging into trunk. Inapplicable items can be left unchecked.

Both the PR author and reviewer are responsible for ensuring the checklist is completed.
-->

- [ ] Has the general commit checklist been followed? (PCYsg-hS-p2)
- [ ] [Have you written new tests](https://wpcalypso.wordpress.com/devdocs/docs/testing/index.md) for your changes?
- [ ] Have you tested the feature in Simple (P9HQHe-k8-p2), Atomic (P9HQHe-jW-p2), and self-hosted Jetpack sites (PCYsg-g6b-p2)?
- [ ] Have you checked for TypeScript, React or other console errors?
- [ ] Have you used memoizing on expensive computations? More info in [Memoizing with create-selector](https://github.com/Automattic/wp-calypso/blob/trunk/packages/state-utils/src/create-selector/README.md) and [Using memoizing selectors](https://react-redux.js.org/api/hooks#using-memoizing-selectors) and [Our Approach to Data](https://github.com/Automattic/wp-calypso/blob/trunk/docs/our-approach-to-data.md)
- [ ] Have we added the "[Status] String Freeze" label as soon as any new strings were ready for translation (p4TIVU-5Jq-p2)?
    - [ ] For UI changes, have we tested the change in various languages (for example, ES, PT, FR, or DE)? The length of text and words vary significantly between languages. 
- [ ] For changes affecting Jetpack: Have we added the "[Status] Needs Privacy Updates" label if this pull request changes what data or activity we track or use (p4TIVU-aUh-p2)?



Diff:
diff --git a/client/dashboard/app/router.tsx b/client/dashboard/app/router.tsx
index 18f364bf1966..33814776f3e5 100644
--- a/client/dashboard/app/router.tsx
+++ b/client/dashboard/app/router.tsx
@@ -18,7 +18,6 @@ import {
 	siteCurrentPlanQuery,
 	siteEngagementStatsQuery,
 	siteMonitorUptimeQuery,
-	sitePHPVersionQuery,
 } from './queries';
 import { queryClient } from './query-client';
 import Root from './root';
@@ -94,9 +93,6 @@ const siteOverviewRoute = createRoute( {
 			site.jetpack && site.jetpack_modules.includes( 'monitor' )
 				? queryClient.ensureQueryData( siteMonitorUptimeQuery( siteSlug ) )
 				: undefined,
-			site.is_wpcom_atomic
-				? queryClient.ensureQueryData( sitePHPVersionQuery( siteSlug ) )
-				: undefined,
 		] );
 	},
 } ).lazy( () =>
diff --git a/client/dashboard/sites/overview/index.tsx b/client/dashboard/sites/overview/index.tsx
index ecb8aff884ff..2a23dee38d89 100644
--- a/client/dashboard/sites/overview/index.tsx
+++ b/client/dashboard/sites/overview/index.tsx
@@ -12,7 +12,6 @@ import { wordpress } from '@wordpress/icons';
 import {
 	siteQuery,
 	siteMonitorUptimeQuery,
-	sitePHPVersionQuery,
 	siteCurrentPlanQuery,
 	siteEngagementStatsQuery,
 } from '../../app/queries';
@@ -39,10 +38,6 @@ function SiteOverview() {
 		...siteMonitorUptimeQuery( siteSlug ),
 		enabled: site?.jetpack && site?.jetpack_modules.includes( 'monitor' ),
 	} );
-	const { data: phpVersion } = useQuery( {
-		...sitePHPVersionQuery( siteSlug ),
-		enabled: site?.is_wpcom_atomic,
-	} );
 	const { data: currentPlan } = useQuery( siteCurrentPlanQuery( siteSlug ) );
 	const { data: engagementStats } = useQuery( siteEngagementStatsQuery( siteSlug ) );
 
@@ -73,7 +68,7 @@ function SiteOverview() {
 			}
 		>
 			<HStack alignment="flex-start" spacing={ 8 }>
-				<Sidebar site={ site } phpVersion={ phpVersion } currentPlan={ currentPlan } />
+				<Sidebar site={ site } currentPlan={ currentPlan } />
 				<VStack spacing={ 8 }>
 					<Card style={ { padding: '16px' } }>
 						<VStack>
diff --git a/client/dashboard/sites/overview/site-card.tsx b/client/dashboard/sites/overview/site-card.tsx
index 0c173e864278..97ea92ee6a88 100644
--- a/client/dashboard/sites/overview/site-card.tsx
+++ b/client/dashboard/sites/overview/site-card.tsx
@@ -1,3 +1,4 @@
+import { useQuery } from '@tanstack/react-query';
 import {
 	__experimentalVStack as VStack,
 	__experimentalHStack as HStack,
@@ -8,23 +9,24 @@ import {
 } from '@wordpress/components';
 import { dateI18n } from '@wordpress/date';
 import { __, sprintf } from '@wordpress/i18n';
+import { sitePHPVersionQuery } from '../../app/queries';
 import { getSiteStatusLabel } from '../../utils/site-status';
 import SitePreview from '../site-preview';
 import type { Site, Plan } from '../../data/types';
 
+function TextBlur( { text }: { text: string } ) {
+	return <span className="text-blur" data-text={ text } />;
+}
+
+function PHPVersion( { siteSlug }: { siteSlug: string } ) {
+	return useQuery( sitePHPVersionQuery( siteSlug ) ).data ?? <TextBlur text="X.Y" />;
+}
+
 /**
  * SiteCard component to display site information in a card format
  */
-export default function SiteCard( {
-	site,
-	phpVersion,
-	currentPlan,
-}: {
-	site: Site;
-	phpVersion?: string;
-	currentPlan: Plan;
-} ) {
-	const { options, URL: url, is_private } = site;
+export default function SiteCard( { site, currentPlan }: { site: Site; currentPlan: Plan } ) {
+	const { options, URL: url, is_private, is_wpcom_atomic } = site;
 	// If the site is a private A8C site, X-Frame-Options is set to same
 	// origin.
 	const iframeDisabled = site.is_a8c && is_private;
@@ -65,12 +67,18 @@ export default function SiteCard( {
 					<HStack justify="space-between">
 						<Field title={ __( 'Status' ) }>{ getSiteStatusLabel( site ) }</Field>
 					</HStack>
-					<HStack justify="space-between">
-						{ options?.software_version && (
-							<Field title={ __( 'WordPress' ) }>{ options.software_version }</Field>
-						) }
-						{ phpVersion && <Field title={ __( 'PHP' ) }>{ phpVersion }</Field> }
-					</HStack>
+					{ ( options?.software_version || is_wpcom_atomic ) && (
+						<HStack justify="space-between">
+							{ options?.software_version && (
+								<Field title={ __( 'WordPress' ) }>{ options.software_version }</Field>
+							) }
+							{ is_wpcom_atomic && (
+								<Field title={ __( 'PHP' ) }>
+									<PHPVersion siteSlug={ site.slug } />
+								</Field>
+							) }
+						</HStack>
+					) }
 					<PlanDetails site={ site } currentPlan={ currentPlan } />
 				</VStack>
 			</VStack>
@@ -80,7 +88,7 @@ export default function SiteCard( {
 
 function Field( { children, title }: { children: React.ReactNode; title: React.ReactNode } ) {
 	return (
-		<VStack className="site-overview-field">
+		<VStack className="site-overview-field" style={ { flex: 1 } }>
 			<FieldTitle>{ title }</FieldTitle>
 			<div className="site-overview-field-children">{ children }</div>
 		</VStack>
diff --git a/client/dashboard/sites/overview/style.scss b/client/dashboard/sites/overview/style.scss
index 9a919cb833aa..9c81801f7049 100644
--- a/client/dashboard/sites/overview/style.scss
+++ b/client/dashboard/sites/overview/style.scss
@@ -24,3 +24,8 @@
 		display: none;
 	}
 }
+
+.text-blur::after {
+	content: attr(data-text);
+	filter: blur(4px);
+}


Reviews:
user: login: ntsekouras
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-20T14:45:43Z
user: login: ntsekouras
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-20T14:47:33Z
user: login: ntsekouras
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-20T15:04:26Z
user: login: fushar
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-20T15:52:53Z
user: login: ellatrix
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-20T16:27:57Z
user: login: ellatrix
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-20T16:29:20Z
user: login: ellatrix
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-20T16:33:51Z
user: login: ntsekouras
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-21T05:25:02Z
user: login: ellatrix
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T06:04:53Z
user: login: fushar
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T06:21:52Z
user: login: ntsekouras
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-21T06:47:00Z
user: login: youknowriad
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T08:47:39Z
user: login: ellatrix
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T09:09:29Z
user: login: ellatrix
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T09:15:26Z
user: login: youknowriad
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T09:15:33Z
user: login: ellatrix
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T09:18:27Z
user: login: ellatrix
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T09:23:07Z
user: login: youknowriad
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T09:38:10Z
user: login: fushar
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T13:10:39Z
user: login: fushar
body: Tested locally and it works well.
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T13:13:37Z
user: login: fushar
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T13:49:00Z
user: login: ellatrix
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T13:49:49Z
user: login: ellatrix
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T13:50:05Z
user: login: fushar
body: 👍 
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T13:54:37Z
user: login: youknowriad
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T13:56:49Z
user: login: ellatrix
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T14:13:08Z
user: login: youknowriad
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T14:17:15Z
user: login: ellatrix
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T15:06:48Z
user: login: youknowriad
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T22:46:16Z
user: login: youknowriad
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T22:46:33Z
user: login: ellatrix
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-22T05:46:06Z
user: login: youknowriad
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-22T07:33:13Z

Review comments:
diff_hunk: @@ -70,10 +74,16 @@ export default function SiteCard( {
 						<Field title={ __( 'Status' ) }>{ getSiteStatusLabel( site ) }</Field>
 					</HStack>
 					<HStack justify="space-between">
-						{ options?.software_version && (
-							<Field title={ __( 'WordPress' ) }>{ options.software_version }</Field>
+						<div style={ { flex: 1 } }>
+							<Field title={ __( 'WordPress' ) }>{ options?.software_version }</Field>
+						</div>
+						{ is_wpcom_atomic && (
+							<div style={ { flex: 1 } }>
path: client/dashboard/sites/overview/site-card.tsx
user: login: ntsekouras
body: Why the div here?
created_at: 2025-05-20T14:45:43Z
author_association: MEMBER
diff_hunk: @@ -70,10 +74,16 @@ export default function SiteCard( {
 						<Field title={ __( 'Status' ) }>{ getSiteStatusLabel( site ) }</Field>
 					</HStack>
 					<HStack justify="space-between">
-						{ options?.software_version && (
-							<Field title={ __( 'WordPress' ) }>{ options.software_version }</Field>
+						<div style={ { flex: 1 } }>
+							<Field title={ __( 'WordPress' ) }>{ options?.software_version }</Field>
path: client/dashboard/sites/overview/site-card.tsx
user: login: ntsekouras
body: Also we should keep the check for `options?.software_version`, no?
created_at: 2025-05-20T14:47:33Z
author_association: MEMBER
diff_hunk: @@ -70,10 +74,16 @@ export default function SiteCard( {
 						<Field title={ __( 'Status' ) }>{ getSiteStatusLabel( site ) }</Field>
 					</HStack>
 					<HStack justify="space-between">
-						{ options?.software_version && (
-							<Field title={ __( 'WordPress' ) }>{ options.software_version }</Field>
+						<div style={ { flex: 1 } }>
+							<Field title={ __( 'WordPress' ) }>{ options?.software_version }</Field>
+						</div>
+						{ is_wpcom_atomic && (
+							<div style={ { flex: 1 } }>
path: client/dashboard/sites/overview/site-card.tsx
user: login: ntsekouras
body: Got the reason. Maybe add the style in `Field` directly? Also we should update the parent HStack to: `<HStack justify="flex-start" spacing={ 6 } alignment="flex-start">` to also align properly the PHP while loading.
created_at: 2025-05-20T15:04:26Z
author_association: MEMBER
diff_hunk: @@ -8,25 +9,28 @@ import {
 } from '@wordpress/components';
 import { dateI18n } from '@wordpress/date';
 import { __, sprintf } from '@wordpress/i18n';
+import { sitePHPVersionQuery } from '../../app/queries';
 import { getSiteStatusLabel } from '../../utils/site-status';
 import SitePreview from '../site-preview';
 import type { Site, SiteDomain, Plan } from '../../data/types';
 
+function PHPVersion( { siteSlug }: { siteSlug: string } ) {
+	return useQuery( sitePHPVersionQuery( siteSlug ) ).data;
+}
+
 /**
  * SiteCard component to display site information in a card format
  */
 export default function SiteCard( {
 	site,
-	phpVersion,
 	primaryDomain,
 	currentPlan,
 }: {
 	site: Site;
-	phpVersion?: string;
 	primaryDomain?: SiteDomain;
 	currentPlan: Plan;
 } ) {
-	const { options, URL: url, is_private } = site;
+	const { options, URL: url, is_private, is_wpcom_atomic } = site;
path: client/dashboard/sites/overview/site-card.tsx
user: login: fushar
body: I'm just curious, why not just call the original

```tsx
const { data: phpVersion } = useQuery( {
		...sitePHPVersionQuery( siteSlug ),
		enabled: site?.is_wpcom_atomic,
	} );
```

here and then render `phpVersion` as before. Is there any particular reason of using a `PHPVersion` React component? Is it because we don't want to pass the `{ enabled: ...}` option?
created_at: 2025-05-20T15:52:48Z
author_association: CONTRIBUTOR
diff_hunk: @@ -70,10 +74,16 @@ export default function SiteCard( {
 						<Field title={ __( 'Status' ) }>{ getSiteStatusLabel( site ) }</Field>
 					</HStack>
 					<HStack justify="space-between">
-						{ options?.software_version && (
-							<Field title={ __( 'WordPress' ) }>{ options.software_version }</Field>
+						<div style={ { flex: 1 } }>
+							<Field title={ __( 'WordPress' ) }>{ options?.software_version }</Field>
path: client/dashboard/sites/overview/site-card.tsx
user: login: ellatrix
body: Not sure why, I think we should anyway show the divs, maybe fall back to `?? 'Unknown'`
created_at: 2025-05-20T16:27:57Z
author_association: CONTRIBUTOR
diff_hunk: @@ -8,25 +9,28 @@ import {
 } from '@wordpress/components';
 import { dateI18n } from '@wordpress/date';
 import { __, sprintf } from '@wordpress/i18n';
+import { sitePHPVersionQuery } from '../../app/queries';
 import { getSiteStatusLabel } from '../../utils/site-status';
 import SitePreview from '../site-preview';
 import type { Site, SiteDomain, Plan } from '../../data/types';
 
+function PHPVersion( { siteSlug }: { siteSlug: string } ) {
+	return useQuery( sitePHPVersionQuery( siteSlug ) ).data;
+}
+
 /**
  * SiteCard component to display site information in a card format
  */
 export default function SiteCard( {
 	site,
-	phpVersion,
 	primaryDomain,
 	currentPlan,
 }: {
 	site: Site;
-	phpVersion?: string;
 	primaryDomain?: SiteDomain;
 	currentPlan: Plan;
 } ) {
-	const { options, URL: url, is_private } = site;
+	const { options, URL: url, is_private, is_wpcom_atomic } = site;
path: client/dashboard/sites/overview/site-card.tsx
user: login: ellatrix
body: Why call a hook if you can avoid it? I'm not sure what extra computation `useQuery` brings, so I just erred on the side of not calling it at all
created_at: 2025-05-20T16:29:20Z
author_association: CONTRIBUTOR
diff_hunk: @@ -70,10 +74,16 @@ export default function SiteCard( {
 						<Field title={ __( 'Status' ) }>{ getSiteStatusLabel( site ) }</Field>
 					</HStack>
 					<HStack justify="space-between">
-						{ options?.software_version && (
-							<Field title={ __( 'WordPress' ) }>{ options.software_version }</Field>
+						<div style={ { flex: 1 } }>
+							<Field title={ __( 'WordPress' ) }>{ options?.software_version }</Field>
path: client/dashboard/sites/overview/site-card.tsx
user: login: ellatrix
body: Added a fallback, in case the REST API returns nothing
created_at: 2025-05-20T16:33:51Z
author_association: CONTRIBUTOR
diff_hunk: @@ -70,10 +74,16 @@ export default function SiteCard( {
 						<Field title={ __( 'Status' ) }>{ getSiteStatusLabel( site ) }</Field>
 					</HStack>
 					<HStack justify="space-between">
-						{ options?.software_version && (
-							<Field title={ __( 'WordPress' ) }>{ options.software_version }</Field>
+						<div style={ { flex: 1 } }>
+							<Field title={ __( 'WordPress' ) }>{ options?.software_version }</Field>
path: client/dashboard/sites/overview/site-card.tsx
user: login: ntsekouras
body: I can't see how `Unknown` can be useful.. We'll just add noise to the UI without any good reason.
created_at: 2025-05-21T05:25:02Z
author_association: MEMBER
diff_hunk: @@ -70,10 +74,16 @@ export default function SiteCard( {
 						<Field title={ __( 'Status' ) }>{ getSiteStatusLabel( site ) }</Field>
 					</HStack>
 					<HStack justify="space-between">
-						{ options?.software_version && (
-							<Field title={ __( 'WordPress' ) }>{ options.software_version }</Field>
+						<div style={ { flex: 1 } }>
+							<Field title={ __( 'WordPress' ) }>{ options?.software_version }</Field>
path: client/dashboard/sites/overview/site-card.tsx
user: login: ellatrix
body: Ok then what do you want to display? Nothing? Why should the field disappear when people are expecting it to be there?
created_at: 2025-05-21T06:04:53Z
author_association: CONTRIBUTOR
diff_hunk: @@ -70,10 +74,16 @@ export default function SiteCard( {
 						<Field title={ __( 'Status' ) }>{ getSiteStatusLabel( site ) }</Field>
 					</HStack>
 					<HStack justify="space-between">
-						{ options?.software_version && (
-							<Field title={ __( 'WordPress' ) }>{ options.software_version }</Field>
+						<div style={ { flex: 1 } }>
+							<Field title={ __( 'WordPress' ) }>{ options?.software_version }</Field>
path: client/dashboard/sites/overview/site-card.tsx
user: login: fushar
body: What's wrong with the existing way where we don't show the bit of information (i.e. WP version) when the backend does not return it? I think the logic is cleaner.

(P.S.: I'm not sure if there's actually a case where that `software_version` option is not set in the backend)
created_at: 2025-05-21T06:21:52Z
author_association: CONTRIBUTOR
diff_hunk: @@ -70,10 +74,16 @@ export default function SiteCard( {
 						<Field title={ __( 'Status' ) }>{ getSiteStatusLabel( site ) }</Field>
 					</HStack>
 					<HStack justify="space-between">
-						{ options?.software_version && (
-							<Field title={ __( 'WordPress' ) }>{ options.software_version }</Field>
+						<div style={ { flex: 1 } }>
+							<Field title={ __( 'WordPress' ) }>{ options?.software_version }</Field>
path: client/dashboard/sites/overview/site-card.tsx
user: login: ntsekouras
body: >Ok then what do you want to display? Nothing? 

Exactly. Even if people expected that field to be there and we'd show `unknown`, it feels like a bug. 
created_at: 2025-05-21T06:47:00Z
author_association: MEMBER
diff_hunk: @@ -70,10 +74,14 @@ export default function SiteCard( {
 						<Field title={ __( 'Status' ) }>{ getSiteStatusLabel( site ) }</Field>
 					</HStack>
 					<HStack justify="space-between">
-						{ options?.software_version && (
-							<Field title={ __( 'WordPress' ) }>{ options.software_version }</Field>
+						<Field title={ __( 'WordPress' ) }>
+							{ options?.software_version ?? __( 'Unknown' ) }
+						</Field>
+						{ is_wpcom_atomic && (
path: client/dashboard/sites/overview/site-card.tsx
user: login: youknowriad
body: Why are we checking `is_wpcom_atomic` and not something like "feature php version available". (I think we should move away from checking "type of sites" in the different dashboard components as much as we can.
created_at: 2025-05-21T08:47:39Z
author_association: CONTRIBUTOR
diff_hunk: @@ -70,10 +74,14 @@ export default function SiteCard( {
 						<Field title={ __( 'Status' ) }>{ getSiteStatusLabel( site ) }</Field>
 					</HStack>
 					<HStack justify="space-between">
-						{ options?.software_version && (
-							<Field title={ __( 'WordPress' ) }>{ options.software_version }</Field>
+						<Field title={ __( 'WordPress' ) }>
+							{ options?.software_version ?? __( 'Unknown' ) }
+						</Field>
+						{ is_wpcom_atomic && (
path: client/dashboard/sites/overview/site-card.tsx
user: login: ellatrix
body: I'm not sure I understand 🤔 
created_at: 2025-05-21T09:09:29Z
author_association: CONTRIBUTOR
diff_hunk: @@ -8,25 +9,28 @@ import {
 } from '@wordpress/components';
 import { dateI18n } from '@wordpress/date';
 import { __, sprintf } from '@wordpress/i18n';
+import { sitePHPVersionQuery } from '../../app/queries';
 import { getSiteStatusLabel } from '../../utils/site-status';
 import SitePreview from '../site-preview';
 import type { Site, SiteDomain, Plan } from '../../data/types';
 
+function PHPVersion( { siteSlug }: { siteSlug: string } ) {
+	return useQuery( sitePHPVersionQuery( siteSlug ) ).data;
+}
+
 /**
  * SiteCard component to display site information in a card format
  */
 export default function SiteCard( {
 	site,
-	phpVersion,
 	primaryDomain,
 	currentPlan,
 }: {
 	site: Site;
-	phpVersion?: string;
 	primaryDomain?: SiteDomain;
 	currentPlan: Plan;
 } ) {
-	const { options, URL: url, is_private } = site;
+	const { options, URL: url, is_private, is_wpcom_atomic } = site;
path: client/dashboard/sites/overview/site-card.tsx
user: login: ellatrix
body: Btw, while working on another PR this was still returning data for me even though I disabled it. It seems `enabled` only stops it from being reactive, I'm not sure 🤔 
created_at: 2025-05-21T09:15:26Z
author_association: CONTRIBUTOR
diff_hunk: @@ -70,10 +74,14 @@ export default function SiteCard( {
 						<Field title={ __( 'Status' ) }>{ getSiteStatusLabel( site ) }</Field>
 					</HStack>
 					<HStack justify="space-between">
-						{ options?.software_version && (
-							<Field title={ __( 'WordPress' ) }>{ options.software_version }</Field>
+						<Field title={ __( 'WordPress' ) }>
+							{ options?.software_version ?? __( 'Unknown' ) }
+						</Field>
+						{ is_wpcom_atomic && (
path: client/dashboard/sites/overview/site-card.tsx
user: login: youknowriad
body: What I'm saying is that ideally we should avoid all `is_wpcom_atomic`, `is_jetpack`, `is_simple`... is_whatever kind of checks in the components, instead we have a `site.plan.features` flags that we should use instead: example `site.plan.features.includes( 'phpVersion' )` 

The reason is that we'll have more "types" of sites in the future, and the features can change over time, and it also makes the dashboard more independent from the endpoints...

I'm not sure today that all the "features" are computed properly yet though.
created_at: 2025-05-21T09:15:33Z
author_association: CONTRIBUTOR
diff_hunk: @@ -70,10 +74,16 @@ export default function SiteCard( {
 						<Field title={ __( 'Status' ) }>{ getSiteStatusLabel( site ) }</Field>
 					</HStack>
 					<HStack justify="space-between">
-						{ options?.software_version && (
-							<Field title={ __( 'WordPress' ) }>{ options.software_version }</Field>
+						<div style={ { flex: 1 } }>
+							<Field title={ __( 'WordPress' ) }>{ options?.software_version }</Field>
path: client/dashboard/sites/overview/site-card.tsx
user: login: ellatrix
body: Ok, I don't care strongly so reverted and added a condition for the whole section
created_at: 2025-05-21T09:18:27Z
author_association: CONTRIBUTOR
diff_hunk: @@ -70,10 +74,14 @@ export default function SiteCard( {
 						<Field title={ __( 'Status' ) }>{ getSiteStatusLabel( site ) }</Field>
 					</HStack>
 					<HStack justify="space-between">
-						{ options?.software_version && (
-							<Field title={ __( 'WordPress' ) }>{ options.software_version }</Field>
+						<Field title={ __( 'WordPress' ) }>
+							{ options?.software_version ?? __( 'Unknown' ) }
+						</Field>
+						{ is_wpcom_atomic && (
path: client/dashboard/sites/overview/site-card.tsx
user: login: ellatrix
body: Ok, I guess this will have to be added to the API
created_at: 2025-05-21T09:23:07Z
author_association: CONTRIBUTOR
diff_hunk: @@ -70,10 +74,14 @@ export default function SiteCard( {
 						<Field title={ __( 'Status' ) }>{ getSiteStatusLabel( site ) }</Field>
 					</HStack>
 					<HStack justify="space-between">
-						{ options?.software_version && (
-							<Field title={ __( 'WordPress' ) }>{ options.software_version }</Field>
+						<Field title={ __( 'WordPress' ) }>
+							{ options?.software_version ?? __( 'Unknown' ) }
+						</Field>
+						{ is_wpcom_atomic && (
path: client/dashboard/sites/overview/site-card.tsx
user: login: youknowriad
body: Yeah, just to be clear (discussed this in Slack with other folks). This is more like a "vision" at this point and not a blocking comment. 
created_at: 2025-05-21T09:38:09Z
author_association: CONTRIBUTOR
diff_hunk: @@ -8,25 +9,28 @@ import {
 } from '@wordpress/components';
 import { dateI18n } from '@wordpress/date';
 import { __, sprintf } from '@wordpress/i18n';
+import { sitePHPVersionQuery } from '../../app/queries';
 import { getSiteStatusLabel } from '../../utils/site-status';
 import SitePreview from '../site-preview';
 import type { Site, SiteDomain, Plan } from '../../data/types';
 
+function PHPVersion( { siteSlug }: { siteSlug: string } ) {
+	return useQuery( sitePHPVersionQuery( siteSlug ) ).data;
+}
+
 /**
  * SiteCard component to display site information in a card format
  */
 export default function SiteCard( {
 	site,
-	phpVersion,
 	primaryDomain,
 	currentPlan,
 }: {
 	site: Site;
-	phpVersion?: string;
 	primaryDomain?: SiteDomain;
 	currentPlan: Plan;
 } ) {
-	const { options, URL: url, is_private } = site;
+	const { options, URL: url, is_private, is_wpcom_atomic } = site;
path: client/dashboard/sites/overview/site-card.tsx
user: login: fushar
body: Interesting... I'll also try to experiment with it 😱 
created_at: 2025-05-21T13:10:39Z
author_association: CONTRIBUTOR
diff_hunk: @@ -8,23 +9,24 @@ import {
 } from '@wordpress/components';
 import { dateI18n } from '@wordpress/date';
 import { __, sprintf } from '@wordpress/i18n';
+import { sitePHPVersionQuery } from '../../app/queries';
 import { getSiteStatusLabel } from '../../utils/site-status';
 import SitePreview from '../site-preview';
 import type { Site, Plan } from '../../data/types';
 
+function PHPVersion( { siteSlug }: { siteSlug: string } ) {
+	return (
+		( useQuery( sitePHPVersionQuery( siteSlug ) ).data && null ) ?? (
path: client/dashboard/sites/overview/site-card.tsx
user: login: fushar
body: Why `&& null` here, I guess it's a leftover from testing?
created_at: 2025-05-21T13:48:20Z
author_association: CONTRIBUTOR
diff_hunk: @@ -8,23 +9,24 @@ import {
 } from '@wordpress/components';
 import { dateI18n } from '@wordpress/date';
 import { __, sprintf } from '@wordpress/i18n';
+import { sitePHPVersionQuery } from '../../app/queries';
 import { getSiteStatusLabel } from '../../utils/site-status';
 import SitePreview from '../site-preview';
 import type { Site, Plan } from '../../data/types';
 
+function PHPVersion( { siteSlug }: { siteSlug: string } ) {
+	return (
+		( useQuery( sitePHPVersionQuery( siteSlug ) ).data && null ) ?? (
+			<span className="text-blur" data-text="X.Y" />
path: client/dashboard/sites/overview/site-card.tsx
user: login: fushar
body: This is interesting approach... might be beneficial to extract this into a reusable placeholder loading component (or we might already have one?)
created_at: 2025-05-21T13:48:58Z
author_association: CONTRIBUTOR
diff_hunk: @@ -8,23 +9,24 @@ import {
 } from '@wordpress/components';
 import { dateI18n } from '@wordpress/date';
 import { __, sprintf } from '@wordpress/i18n';
+import { sitePHPVersionQuery } from '../../app/queries';
 import { getSiteStatusLabel } from '../../utils/site-status';
 import SitePreview from '../site-preview';
 import type { Site, Plan } from '../../data/types';
 
+function PHPVersion( { siteSlug }: { siteSlug: string } ) {
+	return (
+		( useQuery( sitePHPVersionQuery( siteSlug ) ).data && null ) ?? (
path: client/dashboard/sites/overview/site-card.tsx
user: login: ellatrix
body: Sorry my push failed, meant to remove
created_at: 2025-05-21T13:49:49Z
author_association: CONTRIBUTOR
diff_hunk: @@ -8,23 +9,24 @@ import {
 } from '@wordpress/components';
 import { dateI18n } from '@wordpress/date';
 import { __, sprintf } from '@wordpress/i18n';
+import { sitePHPVersionQuery } from '../../app/queries';
 import { getSiteStatusLabel } from '../../utils/site-status';
 import SitePreview from '../site-preview';
 import type { Site, Plan } from '../../data/types';
 
+function PHPVersion( { siteSlug }: { siteSlug: string } ) {
+	return (
+		( useQuery( sitePHPVersionQuery( siteSlug ) ).data && null ) ?? (
+			<span className="text-blur" data-text="X.Y" />
path: client/dashboard/sites/overview/site-card.tsx
user: login: ellatrix
body: If we plan to reuse, yes! :)
created_at: 2025-05-21T13:50:05Z
author_association: CONTRIBUTOR
diff_hunk: @@ -8,23 +9,24 @@ import {
 } from '@wordpress/components';
 import { dateI18n } from '@wordpress/date';
 import { __, sprintf } from '@wordpress/i18n';
+import { sitePHPVersionQuery } from '../../app/queries';
 import { getSiteStatusLabel } from '../../utils/site-status';
 import SitePreview from '../site-preview';
 import type { Site, Plan } from '../../data/types';
 
+function PHPVersion( { siteSlug }: { siteSlug: string } ) {
+	return (
+		( useQuery( sitePHPVersionQuery( siteSlug ) ).data && null ) ?? (
+			<span className="text-blur" data-text="X.Y" />
path: client/dashboard/sites/overview/site-card.tsx
user: login: youknowriad
body: The classname is not correct here, it doesn't follow our guidelines. 
created_at: 2025-05-21T13:56:49Z
author_association: CONTRIBUTOR
diff_hunk: @@ -8,23 +9,24 @@ import {
 } from '@wordpress/components';
 import { dateI18n } from '@wordpress/date';
 import { __, sprintf } from '@wordpress/i18n';
+import { sitePHPVersionQuery } from '../../app/queries';
 import { getSiteStatusLabel } from '../../utils/site-status';
 import SitePreview from '../site-preview';
 import type { Site, Plan } from '../../data/types';
 
+function PHPVersion( { siteSlug }: { siteSlug: string } ) {
+	return (
+		( useQuery( sitePHPVersionQuery( siteSlug ) ).data && null ) ?? (
+			<span className="text-blur" data-text="X.Y" />
path: client/dashboard/sites/overview/site-card.tsx
user: login: ellatrix
body: What are the guidelines? And why are there no tests?
created_at: 2025-05-21T14:13:07Z
author_association: CONTRIBUTOR
diff_hunk: @@ -8,23 +9,24 @@ import {
 } from '@wordpress/components';
 import { dateI18n } from '@wordpress/date';
 import { __, sprintf } from '@wordpress/i18n';
+import { sitePHPVersionQuery } from '../../app/queries';
 import { getSiteStatusLabel } from '../../utils/site-status';
 import SitePreview from '../site-preview';
 import type { Site, Plan } from '../../data/types';
 
+function PHPVersion( { siteSlug }: { siteSlug: string } ) {
+	return (
+		( useQuery( sitePHPVersionQuery( siteSlug ) ).data && null ) ?? (
+			<span className="text-blur" data-text="X.Y" />
path: client/dashboard/sites/overview/site-card.tsx
user: login: youknowriad
body: https://wpcalypso.wordpress.com/devdocs/docs/coding-guidelines/css.md?term=css%20guidelines

It's basically the same guidelines as Gutenberg. 
created_at: 2025-05-21T14:17:15Z
author_association: CONTRIBUTOR
diff_hunk: @@ -8,23 +9,24 @@ import {
 } from '@wordpress/components';
 import { dateI18n } from '@wordpress/date';
 import { __, sprintf } from '@wordpress/i18n';
+import { sitePHPVersionQuery } from '../../app/queries';
 import { getSiteStatusLabel } from '../../utils/site-status';
 import SitePreview from '../site-preview';
 import type { Site, Plan } from '../../data/types';
 
+function PHPVersion( { siteSlug }: { siteSlug: string } ) {
+	return (
+		( useQuery( sitePHPVersionQuery( siteSlug ) ).data && null ) ?? (
+			<span className="text-blur" data-text="X.Y" />
path: client/dashboard/sites/overview/site-card.tsx
user: login: ellatrix
body: You can tell how much I touch CSS. It would be good to have some linter for this. 
created_at: 2025-05-21T15:06:48Z
author_association: CONTRIBUTOR
diff_hunk: @@ -8,23 +9,24 @@ import {
 } from '@wordpress/components';
 import { dateI18n } from '@wordpress/date';
 import { __, sprintf } from '@wordpress/i18n';
+import { sitePHPVersionQuery } from '../../app/queries';
 import { getSiteStatusLabel } from '../../utils/site-status';
 import SitePreview from '../site-preview';
 import type { Site, Plan } from '../../data/types';
 
+function TextBlur( { text }: { text: string } ) {
+	return <span className="text-blur" data-text={ text } />;
path: client/dashboard/sites/overview/site-card.tsx
user: login: youknowriad
body: This class should have been `dashboard-overview-site-card__text-blur` or something like that. 
created_at: 2025-05-21T22:46:16Z
author_association: CONTRIBUTOR
diff_hunk: @@ -8,23 +9,24 @@ import {
 } from '@wordpress/components';
 import { dateI18n } from '@wordpress/date';
 import { __, sprintf } from '@wordpress/i18n';
+import { sitePHPVersionQuery } from '../../app/queries';
 import { getSiteStatusLabel } from '../../utils/site-status';
 import SitePreview from '../site-preview';
 import type { Site, Plan } from '../../data/types';
 
+function TextBlur( { text }: { text: string } ) {
+	return <span className="text-blur" data-text={ text } />;
path: client/dashboard/sites/overview/site-card.tsx
user: login: youknowriad
body: The current class is way too generic.
created_at: 2025-05-21T22:46:33Z
author_association: CONTRIBUTOR
diff_hunk: @@ -8,23 +9,24 @@ import {
 } from '@wordpress/components';
 import { dateI18n } from '@wordpress/date';
 import { __, sprintf } from '@wordpress/i18n';
+import { sitePHPVersionQuery } from '../../app/queries';
 import { getSiteStatusLabel } from '../../utils/site-status';
 import SitePreview from '../site-preview';
 import type { Site, Plan } from '../../data/types';
 
+function TextBlur( { text }: { text: string } ) {
+	return <span className="text-blur" data-text={ text } />;
path: client/dashboard/sites/overview/site-card.tsx
user: login: ellatrix
body: I never understand how these are named. Anyway, it's moved to components now. I guess it should be `dashboard-text-blur`, looking at those other components?
created_at: 2025-05-22T05:46:06Z
author_association: CONTRIBUTOR
diff_hunk: @@ -8,23 +9,24 @@ import {
 } from '@wordpress/components';
 import { dateI18n } from '@wordpress/date';
 import { __, sprintf } from '@wordpress/i18n';
+import { sitePHPVersionQuery } from '../../app/queries';
 import { getSiteStatusLabel } from '../../utils/site-status';
 import SitePreview from '../site-preview';
 import type { Site, Plan } from '../../data/types';
 
+function TextBlur( { text }: { text: string } ) {
+	return <span className="text-blur" data-text={ text } />;
path: client/dashboard/sites/overview/site-card.tsx
user: login: youknowriad
body: >  I guess it should be dashboard-text-blur, looking at those other components?

yes I think so
created_at: 2025-05-22T07:33:12Z
author_association: CONTRIBUTOR

PR comments:
user: login: github-actions[bot]
created_at: 2025-05-20T14:40:04Z
body: <details>
<summary>Calypso Live <a href="https://calypso.live?image=registry.a8c.com/calypso/app:build-145185">(direct link)</a></summary>
<table>
<tr>
<td>
<img src="https://chart.googleapis.com/chart?chs=150x150&cht=qr&chl=https%3A%2F%2Fcalypso.live%3Fimage%3Dregistry.a8c.com%2Fcalypso%2Fapp%3Abuild-145185%26flags%3Doauth&choe=UTF-8" />
</td>
<td>
<a href="https://calypso.live?image=registry.a8c.com/calypso/app:build-145185">https://calypso.live?image=registry.a8c.com/calypso/app:build-145185</a>
</td>
</tr>
</table>
</details>
<details>
<summary>Jetpack Cloud live <a href="https://calypso.live?image=registry.a8c.com/calypso/app:build-145185&env=jetpack">(direct link)</a></summary>
<table>
<tr>
<td>
<img src="https://chart.googleapis.com/chart?chs=150x150&cht=qr&chl=https%3A%2F%2Fcalypso.live%3Fimage%3Dregistry.a8c.com%2Fcalypso%2Fapp%3Abuild-145185%26env%3Djetpack%26flags%3Doauth&choe=UTF-8" />
</td>
<td>
<a href="https://calypso.live?image=registry.a8c.com/calypso/app:build-145185&env=jetpack">https://calypso.live?image=registry.a8c.com/calypso/app:build-145185&env=jetpack</a>
</td>
</tr>
</table>
</details>
<details>
<summary>Automattic for Agencies live <a href="https://calypso.live?image=registry.a8c.com/calypso/app:build-145185&env=a8c-for-agencies">(direct link)</a></summary>
<table>
<tr>
<td>
<img src="https://chart.googleapis.com/chart?chs=150x150&cht=qr&chl=https%3A%2F%2Fcalypso.live%3Fimage%3Dregistry.a8c.com%2Fcalypso%2Fapp%3Abuild-145185%26env%3Da8c-for-agencies%26flags%3Doauth&choe=UTF-8" />
</td>
<td>
<a href="https://calypso.live?image=registry.a8c.com/calypso/app:build-145185&env=a8c-for-agencies">https://calypso.live?image=registry.a8c.com/calypso/app:build-145185&env=a8c-for-agencies</a>
</td>
</tr>
</table>
</details>
<!--calypso-live-watermark:apr@v1-->
author_association: NONE
user: login: matticbot
created_at: 2025-05-20T14:44:23Z
body: **This PR modifies the release build for the following Calypso Apps:**

_For info about this notification, see here: PCYsg-OT6-p2_

* help-center
* notifications
* wpcom-block-editor

To test WordPress.com changes, run `install-plugin.sh $pluginSlug try/lazy-load-php-version` on your sandbox.
<!--calypso-app-artifacts-watermark:apr@v1-->
author_association: CONTRIBUTOR
user: login: matticbot
created_at: 2025-05-20T14:47:17Z
body: <!-- icfy-watermark: c52822 -->
Here is how your PR affects size of JS and CSS bundles shipped to the user's browser:

**App Entrypoints** (~29 bytes removed 📉 [gzipped])
<details>

```
name                    parsed_size           gzip_size
entry-dashboard-dotcom        -58 B  (-0.0%)      -15 B  (-0.0%)
entry-dashboard-a4a           -58 B  (-0.0%)      -14 B  (-0.0%)
```

Common code that is always downloaded and parsed every time the app is loaded, no matter which route is used.
</details>

**Legend**
<details>
<summary>What is parsed and gzip size?</summary>

**Parsed Size:** Uncompressed size of the JS and CSS files. This much code needs to be parsed and stored in memory.
**Gzip Size:** Compressed size of the JS and CSS files. This much data needs to be downloaded over network.
</details>

Generated by performance advisor bot at [iscalypsofastyet.com](http://iscalypsofastyet.com).
author_association: CONTRIBUTOR
user: login: ellatrix
created_at: 2025-05-21T13:40:11Z
body: I made the placeholder blurred unreadable text but in the expected format, so loading it in feels even more smooth
author_association: CONTRIBUTOR