*/ public static function config(): array { return array( 'endpoint' => rest_url( RestModule::NAMESPACE . '/sync' ), 'nonce' => wp_create_nonce( 'wp_rest' ), 'now' => time(), 'pollMs' => 3000, 'finishedMs' => 2500, 'state' => SyncState::read(), ); } public static function render(): void { $state = SyncState::read(); // A run already in progress is painted as one straight away (and stays so even if the script never runs); // sync.js fills in the bar, counts and list on its first render. $running = $state['running']; echo '
'; echo '

' . esc_html__( 'Sync', 'cortexcommerce' ) . '

'; echo '

' . esc_html__( 'Product changes you approve in CortexCommerce are written to your store every hour. Sync now writes them straight away.', 'cortexcommerce' ) . ' ' . esc_html__( 'Changed products are sent to CortexCommerce every 5 minutes.', 'cortexcommerce' ) . '

'; echo '
'; self::tile( __( 'Last synced', 'cortexcommerce' ), 'last-synced', self::last_synced( $state['last_synced_at'] ) ); self::tile( __( 'Products updated', 'cortexcommerce' ), 'last-count', number_format_i18n( $state['last_updated_count'] ) ); self::tile( __( 'Next automatic sync', 'cortexcommerce' ), 'next', self::next_sync( $state['next_sync_at'] ) ); echo '
'; echo '
'; echo '
'; echo ''; echo ''; echo '

' . ( $running ? esc_html__( 'Syncing products from CortexCommerce', 'cortexcommerce' ) : '' ) . '

'; printf( '
', esc_attr__( 'Products updated', 'cortexcommerce' ) ); echo '

'; echo ''; echo '
'; printf( '

', $running ? ' is-busy' : '', $running ? ' disabled' : '', $running ? esc_html__( 'Syncing…', 'cortexcommerce' ) : esc_html__( 'Sync now', 'cortexcommerce' ) ); echo '
'; } /** * "34 minutes", "1 hour", "2 days": the same rounding as sync.js, so the page does not change on load. */ public static function duration( int $seconds ): string { $minutes = max( 1, (int) round( $seconds / 60 ) ); if ( $minutes < 60 ) { /* translators: %d: number of minutes. */ return sprintf( _n( '%d minute', '%d minutes', $minutes, 'cortexcommerce' ), $minutes ); } $hours = (int) round( $minutes / 60 ); if ( $hours < 24 ) { /* translators: %d: number of hours. */ return sprintf( _n( '%d hour', '%d hours', $hours, 'cortexcommerce' ), $hours ); } $days = (int) round( $hours / 24 ); /* translators: %d: number of days. */ return sprintf( _n( '%d day', '%d days', $days, 'cortexcommerce' ), $days ); } private static function last_synced( int $at ): string { if ( 0 === $at ) { return __( 'Never', 'cortexcommerce' ); } if ( time() - $at < MINUTE_IN_SECONDS ) { return __( 'Just now', 'cortexcommerce' ); } /* translators: %s: how long ago, e.g. "34 minutes". */ return sprintf( __( '%s ago', 'cortexcommerce' ), self::duration( time() - $at ) ); } private static function next_sync( ?int $at ): string { if ( null === $at ) { return __( 'Not scheduled', 'cortexcommerce' ); } if ( $at - time() < MINUTE_IN_SECONDS ) { return __( 'in less than a minute', 'cortexcommerce' ); } /* translators: %s: how long until then, e.g. "26 minutes". */ return sprintf( __( 'in about %s', 'cortexcommerce' ), self::duration( $at - time() ) ); } private static function tile( string $label, string $key, string $value ): void { printf( '
%1$s%3$s
', esc_html( $label ), esc_attr( $key ), esc_html( $value ) ); } }