diff --git a/apps/ui/src/components/connect-site-picker/index.tsx b/apps/ui/src/components/connect-site-picker/index.tsx new file mode 100644 index 0000000000..a0c1d42573 --- /dev/null +++ b/apps/ui/src/components/connect-site-picker/index.tsx @@ -0,0 +1,336 @@ +import { Spinner, VisuallyHidden } from '@wordpress/components'; +import { __, sprintf } from '@wordpress/i18n'; +import { external, search } from '@wordpress/icons'; +import { Badge, Button, Icon } from '@wordpress/ui'; +import { clsx } from 'clsx'; +import { useMemo, useState } from 'react'; +import { useConnector } from '@/data/core'; +import { useUserLocale } from '@/data/queries/use-user-locale'; +import { useOffline } from '@/hooks/use-offline'; +import { getLocalizedLink } from '@/lib/docs-links'; +import { presentRemoteSites, searchRemoteSites, type ConnectSiteGroup } from './site-presentation'; +import styles from './style.module.css'; +import type { SyncSite } from '@/data/core'; + +const createWpcomSiteUrl = new URL( 'https://wordpress.com/setup/new-hosted-site' ); +createWpcomSiteUrl.searchParams.set( 'ref', 'studio' ); +createWpcomSiteUrl.searchParams.set( 'section', 'studio-sync' ); +createWpcomSiteUrl.searchParams.set( 'showDomainStep', 'true' ); + +function getEnvironmentLabel( site: SyncSite ): string { + if ( site.isPressable && site.environmentType === 'development' ) return __( 'Development' ); + if ( site.isPressable && site.environmentType === 'staging' ) return __( 'Staging' ); + if ( site.isStaging ) return __( 'Staging' ); + return __( 'Production' ); +} + +function getEnvironmentIntent( site: SyncSite ) { + if ( site.isPressable && site.environmentType === 'development' ) return 'informational'; + if ( site.isStaging || ( site.isPressable && site.environmentType === 'staging' ) ) + return 'medium'; + return 'stable'; +} + +function getSiteStatus( site: SyncSite, group: ConnectSiteGroup ): string { + if ( group === 'needs-transfer' ) { + return __( 'Enable hosting features on WordPress.com before connecting this site.' ); + } + if ( group === 'needs-upgrade' ) { + return __( 'Upgrade this site to a supported plan before connecting it.' ); + } + if ( site.syncSupport === 'missing-permissions' ) { + return __( "Your account doesn't have permission to manage this site." ); + } + if ( site.syncSupport === 'deleted' ) return __( 'This site has been deleted.' ); + return __( 'This site does not support pulling into Studio.' ); +} + +export function getSiteName( site: SyncSite ): string { + if ( site.name.trim() ) return site.name.trim(); + try { + return new URL( site.url ).hostname; + } catch { + return __( 'WordPress site' ); + } +} + +function RemoteSiteCard( { + site, + group, + isSelected, + onSelect, +}: ReturnType< typeof presentRemoteSites >[ number ] & { + isSelected: boolean; + onSelect: ( id: number ) => void; +} ) { + const connector = useConnector(); + const isAvailable = group === 'available'; + const siteName = getSiteName( site ); + const providerLabel = site.isPressable ? __( 'Pressable' ) : __( 'WP.com' ); + const environmentLabel = getEnvironmentLabel( site ); + const siteStatus = isAvailable ? '' : getSiteStatus( site, group ); + const className = clsx( + styles.siteCard, + isSelected && styles.siteCardSelected, + ! isAvailable && styles.siteCardUnavailable + ); + + return ( +
{ __( 'Reconnect to load your WordPress.com and Pressable sites.' ) }
+{ __( 'Loading your sites…' ) }
+{ __( 'Check your connection and try again.' ) }
+ +{ emptyDescription }
+ ++ + + +
++ { sprintf( + // translators: %s is the site search query. + __( 'No sites match “%s”.' ), + searchQuery + ) } +
+{ section.description }
++ { __( + 'Choose the WordPress.com or Pressable site to publish to. Pushing sends this Studio site’s files and database there.' + ) } +
+ { error ? ( ++ { error } +
+ ) : null } ++ { __( 'Temporary copies of this site, for sharing work before it goes live.' ) } +
+ + { previews.length === 0 ? ( +{ __( 'No preview links yet.' ) }
+ ) : ( ++ { direction === 'push' + ? __( 'Replaces the live site with this one.' ) + : __( 'Replaces this site with the live one.' ) } +
+ +{ node.loading ? __( 'Loading…' ) : __( 'Empty' ) }
+ ) : ( + node.children.map( ( child, childIndex ) => ( +