From fec915fb4768600ffad9b7a1a41aa7d6806e25bb Mon Sep 17 00:00:00 2001 From: orchestrator Date: Wed, 14 Oct 2026 10:12:45 +0700 Subject: [PATCH 01/19] ui/273 polish batch --- .../includes/free/class-free-admin.php | 18 +++--- .../includes/free/free-selection.css | 64 ++++++++++--------- 2 files changed, 43 insertions(+), 39 deletions(-) diff --git a/wordpress/writeleash/includes/free/class-free-admin.php b/wordpress/writeleash/includes/free/class-free-admin.php index eb28131a..254737e4 100644 --- a/wordpress/writeleash/includes/free/class-free-admin.php +++ b/wordpress/writeleash/includes/free/class-free-admin.php @@ -2312,7 +2312,7 @@ private static function render_preview_view( string $public_id, int $offset ): v $field_label = Price_Operation::label( $field ); echo '

' . esc_html( ( Price_Operation::FIELD_SALE === $field ? sprintf( /* translators: %s: translated price-field name. */ __( 'Price to change: %s. The regular price is the sale baseline and is preserved.', 'writeleash' ), $field_label ) : sprintf( /* translators: %s: translated price-field name. */ __( 'Price to change: %s. The sale price and schedule are preserved.', 'writeleash' ), $field_label ) ) ) . '

'; - echo '
'; + echo '
' . esc_html( __( 'Product', 'writeleash' ) ) . '' . esc_html( sprintf( /* translators: %s: translated price-field name. */ __( '%s before', 'writeleash' ), $field_label ) ) . '' . esc_html( sprintf( /* translators: %s: translated price-field name. */ __( '%s after', 'writeleash' ), $field_label ) ) . '' . esc_html( __( 'Delta', 'writeleash' ) ) . '' . esc_html( __( 'Change %', 'writeleash' ) ) . '' . esc_html( __( 'Shoppers would pay after Apply', 'writeleash' ) ) . '' . esc_html( __( 'What will happen', 'writeleash' ) ) . '
'; foreach ( $page['items'] as $item ) { $state = (string) $item['result']; $detail = array(); @@ -2329,11 +2329,9 @@ private static function render_preview_view( string $public_id, int $offset ): v $ratio = $item['percentage_delta'] ?? null; $ratio_text = ( is_array( $ratio ) && isset( $ratio['display'] ) && is_string( $ratio['display'] ) ) ? self::percentage_display( $ratio['display'], $ratio['numerator'] ?? null ) : __( 'Unavailable', 'writeleash' ); echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; + echo ''; + echo ''; + echo ''; echo '
' . esc_html( __( 'Product', 'writeleash' ) ) . '' . esc_html( sprintf( /* translators: %s: translated price-field name. */ __( '%s before to after', 'writeleash' ), $field_label ) ) . '' . esc_html( __( 'Delta', 'writeleash' ) ) . '' . esc_html( __( 'Shoppers would pay after Apply', 'writeleash' ) ) . '' . esc_html( __( 'What will happen', 'writeleash' ) ) . '
'; self::render_identity( $item ); echo '' . esc_html( self::money_display( $item['stored_price'], $data['store'], false, $field ) ) . '' . esc_html( self::money_display( $item['planned_regular_price'], $data['store'], false, $field ) ) . '' . esc_html( self::money_display( $item['absolute_delta'], $data['store'], true ) ) . '' . esc_html( $ratio_text ) . '' . esc_html( self::money_display( self::effective_shopper_price( $item, $field ), $data['store'] ) ) . '' . esc_html( self::money_display( $item['stored_price'], $data['store'], false, $field ) ) . '
' . esc_html( '→ ' . self::money_display( $item['planned_regular_price'], $data['store'], false, $field ) ) . '
' . esc_html( self::money_display( $item['absolute_delta'], $data['store'], true ) ) . '
' . esc_html( $ratio_text ) . '
' . esc_html( self::money_display( self::effective_shopper_price( $item, $field ), $data['store'] ) ) . '' . esc_html( self::item_label( $state, $blocked ? Job_State::BLOCKED : Job_State::PLANNED ) ) . ''; foreach ( $detail as $reason ) { echo '

' . esc_html( self::reason_message( $reason ) ) . '

'; } self::support_details( $state . ' · ' . implode( ', ', $detail ) ); @@ -2656,16 +2654,16 @@ private static function render_job_view( string $public_id, int $offset, string $field_label = $field_name; echo '

' . esc_html( sprintf( /* translators: %s: translated price-field name. */ __( 'Expected and planned are %1$s values. Now shows the stored %1$s at page load, not the shopper price. Unavailable means the product or price could not be read.', 'writeleash' ), $field_label ) ) . '

'; - echo '
'; + echo '
' . esc_html( __( 'Product', 'writeleash' ) ) . '' . esc_html( sprintf( /* translators: %s: translated price-field name. */ __( '%s expected', 'writeleash' ), $field_name ) ) . '' . esc_html( sprintf( /* translators: %s: translated price-field name. */ __( '%s now', 'writeleash' ), $field_name ) ) . '' . esc_html( sprintf( /* translators: %s: translated price-field name. */ __( '%s planned', 'writeleash' ), $field_name ) ) . '' . esc_html( _x( 'Apply', 'merchant price workflow label', 'writeleash' ) ) . '' . esc_html( _x( 'Undo', 'merchant price workflow label', 'writeleash' ) ) . '
'; if ( ! $items['items'] ) { echo ''; } foreach ( $items['items'] as $item ) { $id = (int) $item['product_id']; $frozen = $identities[ $id ] ?? array( 'product_id' => $id ); $observation = self::product_observation( $id, $field ); echo ''; - echo ''; - echo ''; - echo ''; + echo ''; + echo ''; + echo ''; echo ''; echo '
' . esc_html( __( 'Product', 'writeleash' ) ) . '' . esc_html( sprintf( /* translators: %s: translated price-field name. */ __( '%s expected', 'writeleash' ), $field_name ) ) . '' . esc_html( sprintf( /* translators: %s: translated price-field name. */ __( '%s now', 'writeleash' ), $field_name ) ) . '' . esc_html( sprintf( /* translators: %s: translated price-field name. */ __( '%s planned', 'writeleash' ), $field_name ) ) . '' . esc_html( _x( 'Apply', 'merchant price workflow label', 'writeleash' ) ) . '' . esc_html( _x( 'Undo', 'merchant price workflow label', 'writeleash' ) ) . '
' . esc_html__( 'No retained products on this page match this view.', 'writeleash' ) . '
'; self::render_identity( $frozen ); echo '' . esc_html( self::money_display( self::expected_display( $frozen, $item, $field ), $plan->data()['store'], false, $field ) ) . '' . esc_html( self::money_display( $observation['price'], $plan->data()['store'], false, $field ) ) . '' . esc_html( self::money_display( $item['planned_price'], $plan->data()['store'], false, $field ) ) . '' . esc_html( self::money_display( self::expected_display( $frozen, $item, $field ), $plan->data()['store'], false, $field ) ) . '' . esc_html( self::money_display( $observation['price'], $plan->data()['store'], false, $field ) ) . '' . esc_html( self::money_display( $item['planned_price'], $plan->data()['store'], false, $field ) ) . ''; self::render_item_outcome( $item['apply_state'], 'UNSUPPORTED' === $item['apply_state'] ? ( $frozen['eligibility']['reason'] ?? $item['apply_reason'] ) : $item['apply_reason'], $job['status'], $observation ); echo ''; if ( null === $item['undo_state'] ) { echo esc_html( self::undo_item_label( $item, $job['status'] ) ); } diff --git a/wordpress/writeleash/includes/free/free-selection.css b/wordpress/writeleash/includes/free/free-selection.css index 59ccf96f..ca6f0a8f 100644 --- a/wordpress/writeleash/includes/free/free-selection.css +++ b/wordpress/writeleash/includes/free/free-selection.css @@ -125,45 +125,40 @@ } .writeleash-admin .writeleash-table-scroll { max-width: 100%; - overflow-x: auto; margin: 16px 0; } -.writeleash-admin .widefat { - table-layout: fixed; - min-width: 900px; -} +/* Wide viewports keep the wrapper a plain block so the sticky header can + hold against the viewport. The fixed 900px minimum and fixed layout were + removed: with the merged preview columns and wrapping cells the tables + fit 1280px and 1024px viewports without page scrolling, measured with + retail-shaped rows. Narrow screens re-enable the contained region below. */ .writeleash-admin .widefat th, .writeleash-admin .widefat td { - padding: 16px; + padding: 8px 12px; vertical-align: top; overflow-wrap: anywhere; line-height: 1.6; } -.writeleash-admin .writeleash-prices th:first-child { - width: 22%; -} -.writeleash-admin .writeleash-prices th:last-child { - width: 24%; -} -.writeleash-admin .writeleash-results th:nth-child(5), -.writeleash-admin .writeleash-results th:last-child { - width: 22%; +.writeleash-admin .widefat thead th { + position: sticky; + top: 32px; + background: #fff; + z-index: 1; } -.writeleash-admin .writeleash-prices td:nth-child(2), -.writeleash-admin .writeleash-prices td:nth-child(3), -.writeleash-admin .writeleash-prices td:nth-child(4), -.writeleash-admin .writeleash-prices td:nth-child(5) { +.writeleash-admin .widefat th.writeleash-amount, +.writeleash-admin .widefat td.writeleash-amount { + text-align: right; font-variant-numeric: tabular-nums; } -.writeleash-admin .writeleash-history th:first-child { - width: 30%; -} -.writeleash-admin .writeleash-history th:last-child { - width: 15%; -} .writeleash-admin .widefat p, .writeleash-admin .widefat details { - margin: 8px 0; + margin: 4px 0; +} +.writeleash-admin .widefat details { + font-size: 12px; +} +.writeleash-admin .widefat details p { + margin: 4px 0; } .writeleash-admin .writeleash-attention > strong { color: #795000; @@ -186,8 +181,19 @@ .writeleash-admin #writeleash-free-selection-form .writeleash-safety { grid-template-columns: minmax(0, 1fr); } - .writeleash-admin .widefat th, - .writeleash-admin .widefat td { - padding: 12px; + /* Contained fallback: the table scrolls inside its labelled region, + never the page. The 600px floor is the documented exception to the + removed 900px minimum: measured at 375px without it, six amount + columns crush to about 45px and money strings wrap mid-number. + With the floor, amount columns keep about 85px and the region -- + focusable by keyboard -- carries the scroll instead of the page. */ + .writeleash-admin .writeleash-table-scroll { + overflow-x: auto; + } + .writeleash-admin .writeleash-table-scroll .widefat { + min-width: 600px; + } + .writeleash-admin .widefat thead th { + top: 46px; } } From 29c782ca184c7ce7326cf6f6a834539b2f6d689d Mon Sep 17 00:00:00 2001 From: orchestrator Date: Wed, 14 Oct 2026 10:12:45 +0700 Subject: [PATCH 02/19] ui/274 polish batch --- .../includes/free/class-free-admin.php | 12 ++++++++---- .../includes/free/free-selection.css | 18 ++++++++++++++++-- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/wordpress/writeleash/includes/free/class-free-admin.php b/wordpress/writeleash/includes/free/class-free-admin.php index 254737e4..a02c2f74 100644 --- a/wordpress/writeleash/includes/free/class-free-admin.php +++ b/wordpress/writeleash/includes/free/class-free-admin.php @@ -2636,12 +2636,16 @@ private static function render_job_view( string $public_id, int $offset, string echo '

' . esc_html( __( 'Products', 'writeleash' ) ) . '

'; $filters = array( '' => array( __( 'All products', 'writeleash' ), null, null ), 'conflict' => array( __( 'Conflicts', 'writeleash' ), 'CONFLICT', null ), 'attention' => array( __( 'Changes needing attention', 'writeleash' ), 'FAILED', null ), 'review' => array( __( 'Uncertain changes', 'writeleash' ), 'NEEDS_REVIEW', null ), 'undo_conflict' => array( __( 'Undo conflicts', 'writeleash' ), null, 'UNDO_CONFLICT' ), 'undo_review' => array( __( 'Uncertain restorations', 'writeleash' ), null, 'UNDO_NEEDS_REVIEW' ) ); if ( ! isset( $filters[ $filter ] ) ) { $filter = ''; } - echo '

'; + echo '

    '; + $filter_keys = array_keys( $filters ); + $last_key = end( $filter_keys ); foreach ( $filters as $key => $choice ) { - if ( $key === $filter ) { echo '' . esc_html( $choice[0] ) . ' '; } - else { echo '' . esc_html( $choice[0] ) . ' '; } + $is_current = ( $key === $filter ); + echo '
  • ' . esc_html( $choice[0] ) . ''; + if ( $key !== $last_key ) { echo ' |'; } + echo '
  • '; } - echo '

    '; + echo '
'; try { $items = Undo_Repository::history_items( (int) $job['id'], $filters[ $filter ][1], $filters[ $filter ][2], $offset, self::ITEM_PAGE_SIZE ); } catch ( \Throwable $error ) { diff --git a/wordpress/writeleash/includes/free/free-selection.css b/wordpress/writeleash/includes/free/free-selection.css index ca6f0a8f..bda1356c 100644 --- a/wordpress/writeleash/includes/free/free-selection.css +++ b/wordpress/writeleash/includes/free/free-selection.css @@ -117,11 +117,25 @@ .writeleash-admin .writeleash-undo > :last-child { margin-bottom: 0; } -.writeleash-admin .writeleash-filters { +.writeleash-admin ul.writeleash-filters { display: flex; flex-wrap: wrap; align-items: baseline; - gap: 8px; + gap: 4px 8px; + margin: 8px 0 16px; + padding: 0; + list-style: none; + font-size: 13px; +} +.writeleash-admin ul.writeleash-filters li { + margin: 0; + padding: 0; + overflow-wrap: anywhere; +} +.writeleash-admin ul.writeleash-filters a.current { + color: #000; + font-weight: 600; + text-decoration: none; } .writeleash-admin .writeleash-table-scroll { max-width: 100%; From 57f0e4ded2ccee281cced30f980c1aecf0452233 Mon Sep 17 00:00:00 2001 From: orchestrator Date: Wed, 14 Oct 2026 10:12:45 +0700 Subject: [PATCH 03/19] ui/278 polish batch --- .../includes/free/class-free-admin.php | 10 +++- .../includes/free/free-selection.css | 51 ++++++++++++++++++- .../includes/free/free-selection.js | 35 ++++++++++--- 3 files changed, 87 insertions(+), 9 deletions(-) diff --git a/wordpress/writeleash/includes/free/class-free-admin.php b/wordpress/writeleash/includes/free/class-free-admin.php index a02c2f74..261f2bf1 100644 --- a/wordpress/writeleash/includes/free/class-free-admin.php +++ b/wordpress/writeleash/includes/free/class-free-admin.php @@ -2096,8 +2096,14 @@ private static function render_selector_form( array $values = array(), ?array $r foreach ( $options as $id => $item ) { echo ''; } echo '

' . esc_html__( 'Check the selected products below. Preview checks their eligibility before any prices change.', 'writeleash' ) . '

'; echo '

' . esc_html__( 'Selected products', 'writeleash' ) . '

    '; - - foreach ( $selected as $id => $item ) { echo '
  • ' . esc_html( $item['text'] ) . ' '; self::selection_button( 'remove:' . $id, __( 'Remove', 'writeleash' ), sprintf( /* translators: %s: product description. */ __( 'Remove %s', 'writeleash' ), $item['text'] ) ); echo '
  • '; } + foreach ( $selected as $id => $item ) { + $parts = explode( ' · ', $item['text'], 2 ); + $name = $parts[0]; + $meta = isset( $parts[1] ) ? $parts[1] : ''; + echo '
  • ' . esc_html( $name ) . ''; + if ( '' !== $meta ) { echo ' ' . esc_html( $meta ) . ''; } + echo '
  • '; + } if ( ! $selected ) { echo '
  • ' . esc_html__( 'No products selected. Search and choose products to add them.', 'writeleash' ) . '
  • '; } echo '
'; echo '
' . esc_html__( 'Search categories without live suggestions', 'writeleash' ) . ''; diff --git a/wordpress/writeleash/includes/free/free-selection.css b/wordpress/writeleash/includes/free/free-selection.css index bda1356c..ce78dfb0 100644 --- a/wordpress/writeleash/includes/free/free-selection.css +++ b/wordpress/writeleash/includes/free/free-selection.css @@ -85,12 +85,61 @@ overflow-wrap: anywhere; } .writeleash-admin #writeleash-free-selected { + display: flex; + flex-wrap: wrap; + gap: 6px; margin: 8px 0 16px; + padding: 0; + list-style: none; } .writeleash-admin #writeleash-free-selected li { - padding: 4px 0; + padding: 0; overflow-wrap: anywhere; } +/* Compact removable tokens in the SelectWoo choice language: name first, + SKU and ID as secondary text, one small icon remove per token. */ +.writeleash-admin #writeleash-free-selected .writeleash-chip { + display: inline-flex; + align-items: center; + gap: 4px; + max-width: 100%; + padding: 2px 2px 2px 8px; + background: #e4e4e4; + border: 1px solid #8c8f94; + border-radius: 4px; + color: #3c434a; + line-height: 1.6; +} +.writeleash-admin #writeleash-free-selected .writeleash-chip__text { + font-weight: 600; +} +.writeleash-admin #writeleash-free-selected .writeleash-chip__meta { + color: #50575e; + font-size: 12px; +} +.writeleash-admin #writeleash-free-selected .writeleash-chip__remove { + display: inline-flex; + align-items: center; + justify-content: center; + min-width: 24px; + min-height: 24px; + padding: 2px; + margin: 0; + background: transparent; + border: none; + border-radius: 3px; + color: #50575e; + cursor: pointer; + line-height: 1; +} +.writeleash-admin #writeleash-free-selected .writeleash-chip__remove:hover { + color: #1d2327; +} +.writeleash-admin #writeleash-free-selected .writeleash-chip__remove .dashicons { + width: 16px; + height: 16px; + font-size: 16px; +} .writeleash-admin #writeleash-free-selection-form .writeleash-safety { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); diff --git a/wordpress/writeleash/includes/free/free-selection.js b/wordpress/writeleash/includes/free/free-selection.js index f7c7a114..64b5419d 100644 --- a/wordpress/writeleash/includes/free/free-selection.js +++ b/wordpress/writeleash/includes/free/free-selection.js @@ -48,17 +48,40 @@ var status = $('#writeleash-free-discovery-status'); var products = $('#writeleash-free-products'); var category = $('#writeleash-free-category'); + function chipParts(text) { + var segments = String(text).split(' · '); + var name = segments.shift(); + return { name: name, meta: segments.join(' · ') }; + } + function focusChipRemove(index) { + var removes = $('#writeleash-free-selected').find('.writeleash-chip__remove'); + var target = removes.eq(Math.min(index, removes.length - 1)).get(0) || $('#writeleash-free-clear').get(0); + if (target && target.focus) { + try { target.focus({ preventScroll: true }); } + catch (error) { target.focus(); } + } + } function selectedList() { var list = $('#writeleash-free-selected').empty(); + var ids = products.find('option:selected').map(function () { return this.value; }).get(); products.find('option:selected').each(function () { - var row = $('
  • ').text(this.text + ' '); - $('
  • '; @@ -2637,7 +2672,7 @@ private static function render_job_view( string $public_id, int $offset, string self::render_export_form( $job ); echo '

    ' . esc_html( _x( 'Undo', 'merchant price workflow label', 'writeleash' ) ) . '

    '; if ( $complete_evidence ) { self::render_undo_section( $job, $history, $plan->price_field() ); } - else { echo '

    ' . esc_html__( 'Undo availability cannot be verified while product outcomes are missing. Reload this job or ask an administrator to check its saved records.', 'writeleash' ) . '

    '; } + else { echo '

    ' . esc_html__( 'Undo availability cannot be verified while product outcomes are missing. Refresh this job or ask an administrator to check its saved records.', 'writeleash' ) . '

    '; } echo '
    '; echo '

    ' . esc_html( __( 'Products', 'writeleash' ) ) . '

    '; $filters = array( '' => array( __( 'All products', 'writeleash' ), null, null ), 'conflict' => array( __( 'Conflicts', 'writeleash' ), 'CONFLICT', null ), 'attention' => array( __( 'Changes needing attention', 'writeleash' ), 'FAILED', null ), 'review' => array( __( 'Uncertain changes', 'writeleash' ), 'NEEDS_REVIEW', null ), 'undo_conflict' => array( __( 'Undo conflicts', 'writeleash' ), null, 'UNDO_CONFLICT' ), 'undo_review' => array( __( 'Uncertain restorations', 'writeleash' ), null, 'UNDO_NEEDS_REVIEW' ) ); @@ -2716,6 +2751,8 @@ private static function render_undo_section( array $job, array $history, string echo '

    ' . esc_html( sprintf( /* translators: %s: localized expiry date and time. */ __( 'Undo window ends: %s', 'writeleash' ), self::site_time( $history['undo_expires_at'] ) ) ) . '

    '; } echo '

    '; + $undo_bar = ! empty( $history['undo']['operation_status'] ) ? self::undo_progress_bar_values( $history['undo'] ) : array( 0, 0 ); + self::render_progress_bar( $undo_bar[0], $undo_bar[1], __( 'Undo progress', 'writeleash' ), 'data-progress-undo-bar', 'data-progress-undo-fill' ); if ( ! empty( $history['undo']['operation_status'] ) ) { $undo = $history['undo']; $undo_review = (int) $undo['needs_review']; @@ -2747,7 +2784,7 @@ private static function render_history_view( int $offset ): void { try { $page = Undo_Repository::history_jobs( $offset, self::HISTORY_PAGE_SIZE, get_current_user_id() ); } catch ( \Throwable $error ) { - echo '

    ' . esc_html( __( 'History is unavailable right now. Reload the page to try again.', 'writeleash' ) ) . '

    '; + echo '

    ' . esc_html( __( 'History is unavailable right now. Refresh the page to try again.', 'writeleash' ) ) . '

    '; return; } self::render_history_table( $page['jobs'] ); diff --git a/wordpress/writeleash/includes/free/free-progress.js b/wordpress/writeleash/includes/free/free-progress.js index 44d736f5..490640c5 100644 --- a/wordpress/writeleash/includes/free/free-progress.js +++ b/wordpress/writeleash/includes/free/free-progress.js @@ -63,7 +63,7 @@ if (!tr) { tr = document.createElement('tr'); tr.setAttribute('data-product-id', String(row.id)); - [row.name, row.expected, __( 'Reload to read the current price', 'writeleash' ), row.planned, '', ''].forEach(function (text) { + [row.name, row.expected, __( 'Refresh to read the current price', 'writeleash' ), row.planned, '', ''].forEach(function (text) { var td = document.createElement('td'); td.textContent = text; tr.appendChild(td); }); body.appendChild(tr); @@ -92,6 +92,31 @@ index += 1; }); } + // Slim progress tracks mirror the saved text counters, which stay the + // source of truth. The bars carry no live region of their own so the + // existing polite announcement is never doubled. Unknown or zero totals + // hide the track instead of guessing. + function paint(trackSelector, fillSelector, total, processed) { + var track = root.querySelector(trackSelector); + var fill = root.querySelector(fillSelector); + if (!track || !fill) { return; } + total = Math.floor(Number(total)); + processed = Math.floor(Number(processed)); + if (!isFinite(total) || !isFinite(processed) || total < 1) { track.hidden = true; return; } + if (processed < 0) { processed = 0; } + if (processed > total) { processed = total; } + track.hidden = false; + track.setAttribute('aria-valuemax', String(total)); + track.setAttribute('aria-valuenow', String(processed)); + fill.style.width = Math.round(processed * 100 / total) + '%'; + } + function paintUndo(counts) { + counts = counts || {}; + function num(value) { var n = Math.floor(Number(value)); return isFinite(n) && n > 0 ? n : 0; } + var total = num(counts.eligible); + if (total < 1) { total = num(counts.undone) + num(counts.pending) + num(counts.applying) + num(counts.conflict) + num(counts.failed) + num(counts.needs_review); } + paint('[data-progress-undo-bar]', '[data-progress-undo-fill]', total, total - num(counts.pending) - num(counts.applying)); + } function apply(data) { write('[data-progress-label]', data.label); write('[data-progress-summary]', data.summary); @@ -99,6 +124,8 @@ write('[data-progress-undo-label]', data.undo_label); write('[data-progress-undo-summary]', data.undo_summary); write('[data-progress-undo-notice]', data.undo_notice); + paint('[data-progress-bar]', '[data-progress-fill]', data.progress_total, data.progress_processed); + paintUndo(data.undo_counts); action('[data-progress-resume]', data.resume_available, __( 'Resume remaining products', 'writeleash' ), data.resume_nonce, 'writeleash_free_resume'); action('[data-progress-undo]', data.undo_available, data.undo_button, data.undo_nonce, 'writeleash_free_undo'); Array.prototype.slice.call(root.querySelectorAll('[data-progress-initial-notice], [data-progress-initial-undo]')).forEach(function (node) { @@ -139,7 +166,7 @@ failures += 1; if (status === 401 || status === 403 || failures >= 4) { stopped = true; - connectionText(status === 401 || status === 403 ? __( 'Session or permission expired. Reload and sign in to refresh saved progress.', 'writeleash' ) : __( 'Automatic updates stopped after repeated errors. Use Refresh saved progress to retry.', 'writeleash' )); + connectionText(status === 401 || status === 403 ? __( 'Session or permission expired. Sign in again, then use Refresh saved progress.', 'writeleash' ) : __( 'Automatic updates stopped after repeated errors. Use Refresh saved progress to retry.', 'writeleash' )); } else { connectionText(__( 'Saved progress may be stale. Refresh failed; retrying shortly.', 'writeleash' )); schedule(Math.min(60000, 5000 * Math.pow(2, failures))); } } function refresh() { diff --git a/wordpress/writeleash/includes/free/free-selection.css b/wordpress/writeleash/includes/free/free-selection.css index ce78dfb0..0b5caf44 100644 --- a/wordpress/writeleash/includes/free/free-selection.css +++ b/wordpress/writeleash/includes/free/free-selection.css @@ -166,6 +166,23 @@ .writeleash-admin .writeleash-undo > :last-child { margin-bottom: 0; } +/* Slim live progress tracks. Static width only: no gradients, no animation, + * so the no-JavaScript server render reads identically. */ +.writeleash-admin .writeleash-progress { + height: 8px; + margin: 8px 0 16px; + background: #f0f0f1; + border-radius: 4px; + overflow: hidden; +} +.writeleash-admin .writeleash-progress[hidden] { + display: none; +} +.writeleash-admin .writeleash-progress-fill { + height: 100%; + width: 0; + background: #2271b1; +} .writeleash-admin ul.writeleash-filters { display: flex; flex-wrap: wrap; diff --git a/wordpress/writeleash/languages/writeleash.pot b/wordpress/writeleash/languages/writeleash.pot index a919c725..3009b6cb 100644 --- a/wordpress/writeleash/languages/writeleash.pot +++ b/wordpress/writeleash/languages/writeleash.pot @@ -69,7 +69,7 @@ msgstr "" #: includes/free/class-free-admin.php:73 #: includes/free/class-free-admin.php:79 -#: includes/free/class-free-admin.php:1193 +#: includes/free/class-free-admin.php:1195 msgid "WriteLeash Bulk Prices" msgstr "" @@ -84,168 +84,168 @@ msgstr "" msgid "%1$s · #%2$d · as reviewed" msgstr "" -#: includes/free/class-free-admin.php:304 -#: includes/free/class-free-admin.php:2593 +#: includes/free/class-free-admin.php:305 +#: includes/free/class-free-admin.php:2626 msgid "Saved product outcomes unavailable; needs checking" msgstr "" -#: includes/free/class-free-admin.php:305 +#: includes/free/class-free-admin.php:306 msgid "Some saved product evidence is unavailable. Check this job before taking further action." msgstr "" -#: includes/free/class-free-admin.php:306 +#: includes/free/class-free-admin.php:307 msgid "Background processing stopped making progress. Resume only remaining products." msgstr "" -#: includes/free/class-free-admin.php:307 +#: includes/free/class-free-admin.php:308 msgid "Undo availability cannot be verified while product outcomes are missing." msgstr "" -#: includes/free/class-free-admin.php:308 +#: includes/free/class-free-admin.php:309 msgid "Undo stopped making progress. Continue Undo only for remaining products." msgstr "" -#: includes/free/class-free-admin.php:313 -#: includes/free/class-free-admin.php:2724 +#: includes/free/class-free-admin.php:314 +#: includes/free/class-free-admin.php:2761 msgid "Restore eligible prices (Undo)" msgstr "" -#: includes/free/class-free-admin.php:313 -#: includes/free/class-free-admin.php:2724 +#: includes/free/class-free-admin.php:314 +#: includes/free/class-free-admin.php:2761 msgid "Continue Undo" msgstr "" -#: includes/free/class-free-admin.php:522 +#: includes/free/class-free-admin.php:524 msgid "Saved price presets" msgstr "" -#: includes/free/class-free-admin.php:522 +#: includes/free/class-free-admin.php:524 msgid "Presets save configuration only. Load, edit, then prepare and approve a fresh Preview. Current products, permissions and limits are checked again. Up to 20 presets per store; creators and administrators can manage them." msgstr "" -#: includes/free/class-free-admin.php:523 +#: includes/free/class-free-admin.php:525 msgid "Presets save the editable selection and price range, not individual Preview exclusions. To exclude products, create a fresh Preview and confirm exclusions before approving it." msgstr "" -#: includes/free/class-free-admin.php:525 +#: includes/free/class-free-admin.php:527 msgid "Presets are unavailable right now." msgstr "" -#: includes/free/class-free-admin.php:528 +#: includes/free/class-free-admin.php:530 msgid "Load preset" msgstr "" -#: includes/free/class-free-admin.php:528 +#: includes/free/class-free-admin.php:530 msgid "Rename preset" msgstr "" -#: includes/free/class-free-admin.php:528 +#: includes/free/class-free-admin.php:530 msgid "Delete preset" msgstr "" -#: includes/free/class-free-admin.php:531 +#: includes/free/class-free-admin.php:533 msgid "New preset name" msgstr "" -#: includes/free/class-free-admin.php:789 +#: includes/free/class-free-admin.php:791 msgid "No recoverable Apply conflicts are visible to your account for this job." msgstr "" -#: includes/free/class-free-admin.php:791 -#: includes/free/class-free-admin.php:2624 +#: includes/free/class-free-admin.php:793 +#: includes/free/class-free-admin.php:2659 msgid "Re-preview conflicted products" msgstr "" #. translators: %s: complete saved price-operation description. -#: includes/free/class-free-admin.php:791 -#: includes/free/class-free-admin.php:846 +#: includes/free/class-free-admin.php:793 +#: includes/free/class-free-admin.php:848 #, php-format msgid "Original operation: %s" msgstr "" #. translators: %s: complete saved price-operation description. -#: includes/free/class-free-admin.php:792 +#: includes/free/class-free-admin.php:794 msgid "Prices or eligibility may have changed. Choose the products to review. The operation and safety settings below are editable suggestions. A fresh Preview reads current values; you must approve it separately before Apply." msgstr "" -#: includes/free/class-free-admin.php:793 -#: includes/free/class-free-admin.php:834 -#: includes/free/class-free-admin.php:848 +#: includes/free/class-free-admin.php:795 +#: includes/free/class-free-admin.php:836 +#: includes/free/class-free-admin.php:850 msgid "Cancel and return to results" msgstr "" -#: includes/free/class-free-admin.php:794 +#: includes/free/class-free-admin.php:796 msgid "No eligible Apply conflicts remain." msgstr "" -#: includes/free/class-free-admin.php:804 +#: includes/free/class-free-admin.php:806 msgid "1. Choose Apply-conflict products" msgstr "" -#: includes/free/class-free-admin.php:804 +#: includes/free/class-free-admin.php:806 msgid "No products are selected by default. Only checked products enter the fresh Preview." msgstr "" -#: includes/free/class-free-admin.php:811 +#: includes/free/class-free-admin.php:813 msgid "This product no longer exists. Preview will mark it as missing and will not apply it." msgstr "" #. translators: 1: current stored price, 2: current eligibility explanation. -#: includes/free/class-free-admin.php:813 +#: includes/free/class-free-admin.php:815 #, php-format msgid "At page load: %1$s. %2$s" msgstr "" #. translators: 1: current stored price, 2: current eligibility explanation. -#: includes/free/class-free-admin.php:814 +#: includes/free/class-free-admin.php:816 msgid "Current values may have changed again. Preview will explain missing or unsupported products and will never apply them." msgstr "" -#: includes/free/class-free-admin.php:832 -#: includes/free/class-free-admin.php:844 -#: includes/free/class-free-admin.php:869 +#: includes/free/class-free-admin.php:834 +#: includes/free/class-free-admin.php:846 +#: includes/free/class-free-admin.php:871 msgid "Repeat as new Preview" msgstr "" #. translators: %s: source job public identifier. -#: includes/free/class-free-admin.php:845 +#: includes/free/class-free-admin.php:847 #, php-format msgid "Based on previous job %s." msgstr "" #. translators: %s: complete saved price-operation description. -#: includes/free/class-free-admin.php:847 +#: includes/free/class-free-admin.php:849 msgid "This creates a new pricing operation. Current product prices and eligibility will be checked, current category membership may differ from the previous job, and a new Preview with a separate approval is required. This is not Undo, Resume or an automatic retry: no historical approval stays valid and no price changes until you approve the new Preview." msgstr "" -#: includes/free/class-free-admin.php:848 -#: includes/free/class-free-admin.php:2249 +#: includes/free/class-free-admin.php:850 +#: includes/free/class-free-admin.php:2272 msgid "View original results" msgstr "" -#: includes/free/class-free-admin.php:850 +#: includes/free/class-free-admin.php:852 msgid "Category selections use current membership: products added to or removed from the category since the previous job enter or leave the new Preview. The new Preview freezes its own product list for your review." msgstr "" -#: includes/free/class-free-admin.php:852 +#: includes/free/class-free-admin.php:854 msgid "Exact product selections reuse the previously reviewed product IDs as editable suggestions. Every ID is checked again against the current catalog, your current editing rights and supported product types; missing or unsupported products are explained in the new Preview and never applied." msgstr "" #. translators: %d: number of products the previous job excluded. -#: includes/free/class-free-admin.php:861 +#: includes/free/class-free-admin.php:863 #, php-format msgid "The previous job’s Preview excluded %d product from its frozen population; it is not included in the suggestion below. Add its ID explicitly if you want it reviewed again." msgid_plural "The previous job’s Preview excluded %d products from its frozen population; they are not included in the suggestion below. Add their IDs explicitly if you want them reviewed again." msgstr[0] "" msgstr[1] "" -#: includes/free/class-free-admin.php:1183 -#: includes/free/class-free-admin.php:1217 +#: includes/free/class-free-admin.php:1185 +#: includes/free/class-free-admin.php:1219 msgid "You are not allowed to edit products." msgstr "" #. translators: %d: products processed in this step. -#: includes/free/class-free-admin.php:1260 +#: includes/free/class-free-admin.php:1262 #, php-format msgid "Processed %d product in this step. Check the results below for remaining work and anything needing attention." msgid_plural "Processed %d products in this step. Check the results below for remaining work and anything needing attention." @@ -253,13 +253,13 @@ msgstr[0] "" msgstr[1] "" #. translators: %d: supported products per new job. -#: includes/free/class-free-admin.php:1302 +#: includes/free/class-free-admin.php:1304 #, php-format msgid "WriteLeash supports up to %d products per job in the tested configuration. Narrow the selection and build a new preview. No additional job or journal was created and no product was changed." msgstr "" #. translators: 1: supported products per job, 2: selected products. -#: includes/free/class-free-admin.php:1305 +#: includes/free/class-free-admin.php:1307 #, php-format msgid "WriteLeash supports up to %1$d products per job in the tested configuration. %2$d product was selected. Narrow the selection and build a new preview. No additional job or journal was created and no product was changed." msgid_plural "WriteLeash supports up to %1$d products per job in the tested configuration. %2$d products were selected. Narrow the selection and build a new preview. No additional job or journal was created and no product was changed." @@ -267,384 +267,384 @@ msgstr[0] "" msgstr[1] "" #. translators: 1: supported WooCommerce version range, 2: installed version. -#: includes/free/class-free-admin.php:1309 +#: includes/free/class-free-admin.php:1311 #, php-format msgid "WriteLeash supports %1$s for price mutations. Installed version: %2$s. No job was created and no product was changed." msgstr "" #. translators: 1: supported WooCommerce version range, 2: installed version. -#: includes/free/class-free-admin.php:1309 +#: includes/free/class-free-admin.php:1311 msgid "unavailable" msgstr "" -#: includes/free/class-free-admin.php:1312 +#: includes/free/class-free-admin.php:1314 msgid "WriteLeash does not support multisite. Use a single-site installation. No job was created and no product was changed." msgstr "" -#: includes/free/class-free-admin.php:1315 +#: includes/free/class-free-admin.php:1317 msgid "WriteLeash needs a standard transactional database connection (mysqli with InnoDB tables). No job was created and no product was changed." msgstr "" -#: includes/free/class-free-admin.php:1318 +#: includes/free/class-free-admin.php:1320 msgid "Selection changes were refused. Use only rows from an unapproved Preview visible to your account. No prices changed." msgstr "" -#: includes/free/class-free-admin.php:1319 +#: includes/free/class-free-admin.php:1321 msgid "The resolved selection or a product identity changed. Create a new Preview and review its candidates again. No prices changed." msgstr "" -#: includes/free/class-free-admin.php:1320 +#: includes/free/class-free-admin.php:1322 msgid "No supported included targets remain. The last reviewed Preview and its exclusions stay unchanged. Restore a row or cancel; no prices changed." msgstr "" -#: includes/free/class-free-admin.php:1321 +#: includes/free/class-free-admin.php:1323 msgid "A variable parent’s price range follows its changing included variations. Exclude the variation rows instead of their parent. No prices changed." msgstr "" -#: includes/free/class-free-admin.php:1322 +#: includes/free/class-free-admin.php:1324 msgid "This product has an invalid stored price. Open it in WooCommerce, correct the price and save it before creating a new preview." msgstr "" -#: includes/free/class-free-admin.php:1323 +#: includes/free/class-free-admin.php:1325 msgid "You need WooCommerce product management capabilities for this action." msgstr "" -#: includes/free/class-free-admin.php:1324 +#: includes/free/class-free-admin.php:1326 msgid "This saved preset is invalid or has changed. Save a new configuration; no prices were changed." msgstr "" -#: includes/free/class-free-admin.php:1325 +#: includes/free/class-free-admin.php:1327 msgid "Use a nonempty preset name of at most 100 bytes without markup or control characters." msgstr "" -#: includes/free/class-free-admin.php:1326 +#: includes/free/class-free-admin.php:1328 msgid "This store has reached its limit of 20 presets. Delete a preset before saving another." msgstr "" -#: includes/free/class-free-admin.php:1327 +#: includes/free/class-free-admin.php:1329 msgid "This preset is unavailable to your account." msgstr "" -#: includes/free/class-free-admin.php:1328 -msgid "This preset changed during your request. Reload and try again." +#: includes/free/class-free-admin.php:1330 +msgid "This preset changed during your request. Refresh and try again." msgstr "" -#: includes/free/class-free-admin.php:1329 +#: includes/free/class-free-admin.php:1331 msgid "Preset saved. No prices changed; prepare a fresh Preview before applying." msgstr "" -#: includes/free/class-free-admin.php:1330 +#: includes/free/class-free-admin.php:1332 msgid "Preset loaded into the editable form. Check the selection, prepare a fresh Preview and approve independently. No prices changed." msgstr "" -#: includes/free/class-free-admin.php:1331 +#: includes/free/class-free-admin.php:1333 msgid "Some saved product, SKU or category references are missing or unavailable. Exact saved inputs are retained. Review them before a fresh Preview, which checks current eligibility, permissions and limits. No prices changed." msgstr "" -#: includes/free/class-free-admin.php:1332 +#: includes/free/class-free-admin.php:1334 msgid "Preset updated. Existing previews, jobs, History and Undo are unchanged." msgstr "" -#: includes/free/class-free-admin.php:1333 +#: includes/free/class-free-admin.php:1335 msgid "This action requires an authenticated POST request." msgstr "" -#: includes/free/class-free-admin.php:1334 -msgid "The security token is missing or invalid. Reload the page and try again." +#: includes/free/class-free-admin.php:1336 +msgid "The security token is missing or invalid. Refresh the page and try again." msgstr "" -#: includes/free/class-free-admin.php:1335 +#: includes/free/class-free-admin.php:1337 msgid "WooCommerce must be active and initialized before bulk-price planning. Install and activate WooCommerce, then reopen this page. No job was created and no product was changed." msgstr "" -#: includes/free/class-free-admin.php:1336 +#: includes/free/class-free-admin.php:1338 #: includes/free/class-product-snapshot.php:300 msgid "You do not have permission to plan these product edits." msgstr "" -#: includes/free/class-free-admin.php:1337 +#: includes/free/class-free-admin.php:1339 msgid "The approved actor can no longer edit one or more products; nothing was executed." msgstr "" -#: includes/free/class-free-admin.php:1338 +#: includes/free/class-free-admin.php:1340 msgid "Only the job creator, approver or an administrator may perform this action." msgstr "" -#: includes/free/class-free-admin.php:1339 +#: includes/free/class-free-admin.php:1341 msgid "Choose selected products, a named category or an advanced exact SKU/manual ID selection." msgstr "" -#: includes/free/class-free-admin.php:1340 +#: includes/free/class-free-admin.php:1342 msgid "Use a shorter product or category search and start again from the first page." msgstr "" -#: includes/free/class-free-admin.php:1341 +#: includes/free/class-free-admin.php:1343 msgid "Search is unavailable. Try again, reload if your session expired, or use the native search controls." msgstr "" #. translators: %d: maximum products allowed. -#: includes/free/class-free-admin.php:1343 +#: includes/free/class-free-admin.php:1345 #, php-format msgid "Choose between one and %d products." msgstr "" #. translators: %d: maximum products allowed. -#: includes/free/class-free-admin.php:1344 +#: includes/free/class-free-admin.php:1346 msgid "Product IDs must be comma-separated positive integers." msgstr "" -#: includes/free/class-free-admin.php:1345 +#: includes/free/class-free-admin.php:1347 #: includes/free/class-product-snapshot.php:290 msgid "Use a non-empty exact SKU without whitespace or markup." msgstr "" -#: includes/free/class-free-admin.php:1346 +#: includes/free/class-free-admin.php:1348 #: includes/free/class-product-snapshot.php:291 msgid "Select an existing product category." msgstr "" -#: includes/free/class-free-admin.php:1347 +#: includes/free/class-free-admin.php:1349 msgid "Check the optional price range: enable it, enter a minimum and/or maximum price as an unsigned decimal with a dot separator, and keep the minimum at or below the maximum." msgstr "" -#: includes/free/class-free-admin.php:1348 +#: includes/free/class-free-admin.php:1350 msgid "Select one of the five supported price operations." msgstr "" -#: includes/free/class-free-admin.php:1349 +#: includes/free/class-free-admin.php:1351 msgid "Use an unsigned decimal amount string with a dot separator." msgstr "" #. translators: %d: maximum changing products allowed. -#: includes/free/class-free-admin.php:1351 +#: includes/free/class-free-admin.php:1353 #, php-format msgid "The changed-product safety limit must be between zero and %d. It cannot raise the Free supported-job boundary." msgstr "" #. translators: %d: maximum changing products allowed. -#: includes/free/class-free-admin.php:1352 +#: includes/free/class-free-admin.php:1354 msgid "Policy percentages must be unsigned decimal strings." msgstr "" -#: includes/free/class-free-admin.php:1353 +#: includes/free/class-free-admin.php:1355 msgid "The zero-price flag is invalid." msgstr "" -#: includes/free/class-free-admin.php:1354 +#: includes/free/class-free-admin.php:1356 #: includes/free/class-job-resume-rest.php:40 #: includes/free/class-undo-rest.php:42 msgid "No WriteLeash job matches that identifier." msgstr "" -#: includes/free/class-free-admin.php:1355 +#: includes/free/class-free-admin.php:1357 msgid "A request field is malformed; nothing was changed." msgstr "" -#: includes/free/class-free-admin.php:1356 +#: includes/free/class-free-admin.php:1358 msgid "The safety policy blocks this plan; it cannot be approved." msgstr "" -#: includes/free/class-free-admin.php:1357 +#: includes/free/class-free-admin.php:1359 msgid "Preview ready. No prices have changed yet. Review these saved prices before approving." msgstr "" -#: includes/free/class-free-admin.php:1358 +#: includes/free/class-free-admin.php:1360 msgid "Your safety limits block every changing product in this preview; it cannot be approved." msgstr "" -#: includes/free/class-free-admin.php:1359 +#: includes/free/class-free-admin.php:1361 msgid "This plan was already approved; no duplicate approval was recorded." msgstr "" -#: includes/free/class-free-admin.php:1360 +#: includes/free/class-free-admin.php:1362 #: includes/free/class-job-state.php:151 #: includes/free/class-undo-state.php:141 msgid "This saved job no longer matches its approved preview; no product was changed. Start a new preview." msgstr "" -#: includes/free/class-free-admin.php:1361 +#: includes/free/class-free-admin.php:1363 #: includes/free/class-job-state.php:157 msgid "This job has already finished." msgstr "" -#: includes/free/class-free-admin.php:1362 +#: includes/free/class-free-admin.php:1364 msgid "This job cannot be continued from its current state." msgstr "" -#: includes/free/class-free-admin.php:1363 +#: includes/free/class-free-admin.php:1365 msgid "This job cannot be approved in its current state." msgstr "" -#: includes/free/class-free-admin.php:1364 -msgid "Approved. WriteLeash will apply the reviewed prices in the background. Reload this page to check progress." +#: includes/free/class-free-admin.php:1366 +msgid "Approved. WriteLeash will apply the reviewed prices in the background. Progress updates automatically." msgstr "" -#: includes/free/class-free-admin.php:1365 +#: includes/free/class-free-admin.php:1367 msgid "Approved, but background processing could not start. This job is paused; use Resume remaining products to continue it." msgstr "" -#: includes/free/class-free-admin.php:1366 +#: includes/free/class-free-admin.php:1368 msgid "Results updated after this step. Check changes, remaining products and anything needing attention below." msgstr "" -#: includes/free/class-free-admin.php:1367 +#: includes/free/class-free-admin.php:1369 msgid "Undo results updated. Check restorations, remaining products and anything needing attention below." msgstr "" -#: includes/free/class-free-admin.php:1368 +#: includes/free/class-free-admin.php:1370 #: includes/free/class-undo-state.php:145 msgid "This Undo has already finished." msgstr "" -#: includes/free/class-free-admin.php:1369 +#: includes/free/class-free-admin.php:1371 msgid "Undo is currently unavailable; no price was restored." msgstr "" -#: includes/free/class-free-admin.php:1370 +#: includes/free/class-free-admin.php:1372 msgid "The background step could not run. Saved progress was kept; try Resume remaining products again." msgstr "" -#: includes/free/class-free-admin.php:1371 +#: includes/free/class-free-admin.php:1373 msgid "No repeatable configuration is visible to your account for that job. It may not exist, you may lack access, or its saved preview may be damaged. Nothing was changed." msgstr "" -#: includes/free/class-free-admin.php:1372 +#: includes/free/class-free-admin.php:1374 msgid "The previous job uses a selection, price range, operation or safety setting this version no longer supports. Repeat cannot suggest a replacement on its own; set the values directly in a new preview. Nothing was changed." msgstr "" -#: includes/free/class-free-admin.php:1373 +#: includes/free/class-free-admin.php:1375 msgid "The action could not be completed; nothing was changed." msgstr "" -#: includes/free/class-free-admin.php:1392 +#: includes/free/class-free-admin.php:1394 msgid "The outcome is unavailable. Check the saved results before taking further action." msgstr "" -#: includes/free/class-free-admin.php:1398 +#: includes/free/class-free-admin.php:1400 msgid "Awaiting review" msgstr "" -#: includes/free/class-free-admin.php:1398 -#: includes/free/class-free-admin.php:2279 +#: includes/free/class-free-admin.php:1400 +#: includes/free/class-free-admin.php:2302 msgid "This plan cannot be executed." msgstr "" -#: includes/free/class-free-admin.php:1399 +#: includes/free/class-free-admin.php:1401 msgid "Preparing preview" msgstr "" -#: includes/free/class-free-admin.php:1400 +#: includes/free/class-free-admin.php:1402 msgid "Approved; waiting to start" msgstr "" -#: includes/free/class-free-admin.php:1400 +#: includes/free/class-free-admin.php:1402 msgid "Waiting to continue" msgstr "" -#: includes/free/class-free-admin.php:1401 +#: includes/free/class-free-admin.php:1403 msgid "In progress" msgstr "" -#: includes/free/class-free-admin.php:1401 +#: includes/free/class-free-admin.php:1403 msgid "Paused" msgstr "" -#: includes/free/class-free-admin.php:1401 +#: includes/free/class-free-admin.php:1403 msgid "Finished" msgstr "" -#: includes/free/class-free-admin.php:1402 +#: includes/free/class-free-admin.php:1404 msgid "Finished with products needing attention" msgstr "" -#: includes/free/class-free-admin.php:1403 +#: includes/free/class-free-admin.php:1405 msgid "Outcome needs checking" msgstr "" -#: includes/free/class-free-admin.php:1403 +#: includes/free/class-free-admin.php:1405 msgid "Stopped by an operator" msgstr "" -#: includes/free/class-free-admin.php:1405 -#: includes/free/class-free-admin.php:1425 +#: includes/free/class-free-admin.php:1407 +#: includes/free/class-free-admin.php:1427 msgid "Outcome unavailable; needs checking" msgstr "" -#: includes/free/class-free-admin.php:1409 +#: includes/free/class-free-admin.php:1411 msgid "Not started" msgstr "" -#: includes/free/class-free-admin.php:1411 -#: includes/free/class-free-admin.php:1418 +#: includes/free/class-free-admin.php:1413 +#: includes/free/class-free-admin.php:1420 msgid "Will not run" msgstr "" -#: includes/free/class-free-admin.php:1412 +#: includes/free/class-free-admin.php:1414 msgid "Planned change; awaiting review" msgstr "" -#: includes/free/class-free-admin.php:1413 +#: includes/free/class-free-admin.php:1415 msgid "Not processed" msgstr "" -#: includes/free/class-free-admin.php:1414 +#: includes/free/class-free-admin.php:1416 msgid "Remaining" msgstr "" -#: includes/free/class-free-admin.php:1417 +#: includes/free/class-free-admin.php:1419 msgid "Changed" msgstr "" -#: includes/free/class-free-admin.php:1417 +#: includes/free/class-free-admin.php:1419 msgid "Already at the target price" msgstr "" -#: includes/free/class-free-admin.php:1418 +#: includes/free/class-free-admin.php:1420 msgid "Skipped at preview" msgstr "" -#: includes/free/class-free-admin.php:1419 +#: includes/free/class-free-admin.php:1421 msgid "In progress; outcome not yet confirmed" msgstr "" -#: includes/free/class-free-admin.php:1419 +#: includes/free/class-free-admin.php:1421 msgid "Not changed" msgstr "" -#: includes/free/class-free-admin.php:1420 +#: includes/free/class-free-admin.php:1422 msgid "Needs attention; not changed" msgstr "" -#: includes/free/class-free-admin.php:1420 +#: includes/free/class-free-admin.php:1422 msgid "Outcome uncertain; needs checking" msgstr "" -#: includes/free/class-free-admin.php:1421 +#: includes/free/class-free-admin.php:1423 msgid "Awaiting restoration" msgstr "" -#: includes/free/class-free-admin.php:1421 +#: includes/free/class-free-admin.php:1423 msgid "Restoration in progress; outcome not yet confirmed" msgstr "" -#: includes/free/class-free-admin.php:1422 +#: includes/free/class-free-admin.php:1424 msgid "Restored" msgstr "" -#: includes/free/class-free-admin.php:1422 +#: includes/free/class-free-admin.php:1424 msgid "Not restored" msgstr "" -#: includes/free/class-free-admin.php:1423 +#: includes/free/class-free-admin.php:1425 msgid "Restoration needs checking" msgstr "" -#: includes/free/class-free-admin.php:1423 +#: includes/free/class-free-admin.php:1425 msgid "Restoration uncertain; needs checking" msgstr "" #. translators: %d: number of products in this outcome. -#: includes/free/class-free-admin.php:1433 +#: includes/free/class-free-admin.php:1435 #, php-format msgid "%d changed" msgid_plural "%d changed" @@ -652,7 +652,7 @@ msgstr[0] "" msgstr[1] "" #. translators: %d: number of products in this outcome. -#: includes/free/class-free-admin.php:1436 +#: includes/free/class-free-admin.php:1438 #, php-format msgid "%d already at the target price" msgid_plural "%d already at the target price" @@ -660,7 +660,7 @@ msgstr[0] "" msgstr[1] "" #. translators: %d: number of products in this outcome. -#: includes/free/class-free-admin.php:1439 +#: includes/free/class-free-admin.php:1441 #, php-format msgid "%d skipped" msgid_plural "%d skipped" @@ -668,7 +668,7 @@ msgstr[0] "" msgstr[1] "" #. translators: %d: number of products in this outcome. -#: includes/free/class-free-admin.php:1442 +#: includes/free/class-free-admin.php:1444 #, php-format msgid "%d conflict" msgid_plural "%d conflicts" @@ -676,7 +676,7 @@ msgstr[0] "" msgstr[1] "" #. translators: %d: number of products in this outcome. -#: includes/free/class-free-admin.php:1445 +#: includes/free/class-free-admin.php:1447 #, php-format msgid "%d will not run" msgid_plural "%d will not run" @@ -684,7 +684,7 @@ msgstr[0] "" msgstr[1] "" #. translators: %d: number of products in this outcome. -#: includes/free/class-free-admin.php:1448 +#: includes/free/class-free-admin.php:1450 #, php-format msgid "%d awaiting approval" msgid_plural "%d awaiting approval" @@ -692,7 +692,7 @@ msgstr[0] "" msgstr[1] "" #. translators: %d: number of products in this outcome. -#: includes/free/class-free-admin.php:1451 +#: includes/free/class-free-admin.php:1453 #, php-format msgid "%d not processed" msgid_plural "%d not processed" @@ -700,7 +700,7 @@ msgstr[0] "" msgstr[1] "" #. translators: %d: number of products in this outcome. -#: includes/free/class-free-admin.php:1454 +#: includes/free/class-free-admin.php:1456 #, php-format msgid "%d remaining" msgid_plural "%d remaining" @@ -708,7 +708,7 @@ msgstr[0] "" msgstr[1] "" #. translators: %d: number of products in this outcome. -#: includes/free/class-free-admin.php:1457 +#: includes/free/class-free-admin.php:1459 #, php-format msgid "%d in progress" msgid_plural "%d in progress" @@ -716,7 +716,7 @@ msgstr[0] "" msgstr[1] "" #. translators: %d: number of products in this outcome. -#: includes/free/class-free-admin.php:1460 +#: includes/free/class-free-admin.php:1462 #, php-format msgid "%d needing attention" msgid_plural "%d needing attention" @@ -724,7 +724,7 @@ msgstr[0] "" msgstr[1] "" #. translators: %d: number of products in this outcome. -#: includes/free/class-free-admin.php:1463 +#: includes/free/class-free-admin.php:1465 #, php-format msgid "%d needs checking" msgid_plural "%d need checking" @@ -732,7 +732,7 @@ msgstr[0] "" msgstr[1] "" #. translators: %d: number of products in this outcome. -#: includes/free/class-free-admin.php:1466 +#: includes/free/class-free-admin.php:1468 #, php-format msgid "%d restored" msgid_plural "%d restored" @@ -740,7 +740,7 @@ msgstr[0] "" msgstr[1] "" #. translators: %d: number of products in this outcome. -#: includes/free/class-free-admin.php:1469 +#: includes/free/class-free-admin.php:1471 #, php-format msgid "%d planned change" msgid_plural "%d planned changes" @@ -748,7 +748,7 @@ msgstr[0] "" msgstr[1] "" #. translators: %d: number of products in this outcome. -#: includes/free/class-free-admin.php:1472 +#: includes/free/class-free-admin.php:1474 #, php-format msgid "%d product with warnings" msgid_plural "%d products with warnings" @@ -756,7 +756,7 @@ msgstr[0] "" msgstr[1] "" #. translators: %d: number of products in this outcome. -#: includes/free/class-free-admin.php:1475 +#: includes/free/class-free-admin.php:1477 #, php-format msgid "Large increases %d" msgid_plural "Large increases %d" @@ -764,7 +764,7 @@ msgstr[0] "" msgstr[1] "" #. translators: %d: number of products in this outcome. -#: includes/free/class-free-admin.php:1478 +#: includes/free/class-free-admin.php:1480 #, php-format msgid "large decreases %d" msgid_plural "large decreases %d" @@ -772,7 +772,7 @@ msgstr[0] "" msgstr[1] "" #. translators: %d: number of products in this outcome. -#: includes/free/class-free-admin.php:1481 +#: includes/free/class-free-admin.php:1483 #, php-format msgid "zero-price targets %d" msgid_plural "zero-price targets %d" @@ -780,73 +780,73 @@ msgstr[0] "" msgstr[1] "" #. translators: 1: planned products, 2: list of saved outcome counts. -#: includes/free/class-free-admin.php:1494 +#: includes/free/class-free-admin.php:1496 #, php-format msgid "Planned %1$d product: %2$s." msgid_plural "Planned %1$d products: %2$s." msgstr[0] "" msgstr[1] "" -#: includes/free/class-free-admin.php:1507 +#: includes/free/class-free-admin.php:1530 msgid "sale prices" msgstr "" -#: includes/free/class-free-admin.php:1507 +#: includes/free/class-free-admin.php:1530 msgid "regular prices" msgstr "" #. translators: 1: price basis (regular/sale prices), 2: excluded targets. -#: includes/free/class-free-admin.php:1510 +#: includes/free/class-free-admin.php:1533 #, php-format msgid "No products match this price range. The range was compared against each product’s stored %1$s; %2$d readable products fall outside it. Adjust the range or the selection. Preview explains skipped or unsupported products." msgstr "" #. translators: 1: matched price targets, 2: price basis, 3: excluded targets, 4: unreadable targets, 5: missing targets. -#: includes/free/class-free-admin.php:1513 +#: includes/free/class-free-admin.php:1536 #, php-format msgid "At last check: %1$d deduplicated price target matches the range on stored %2$s. %3$d excluded by the range; %4$d unreadable; %5$d missing. Preview explains skipped or unsupported products." msgid_plural "At last check: %1$d deduplicated price targets match the range on stored %2$s. %3$d excluded by the range; %4$d unreadable; %5$d missing. Preview explains skipped or unsupported products." msgstr[0] "" msgstr[1] "" -#: includes/free/class-free-admin.php:1515 +#: includes/free/class-free-admin.php:1538 msgid "No products match this selection. Choose products or another category. Preview explains skipped or unsupported products." msgstr "" #. translators: 1: deduplicated price targets, 2: unreadable targets, 3: missing targets. -#: includes/free/class-free-admin.php:1517 +#: includes/free/class-free-admin.php:1540 #, php-format msgid "At last check: %1$d deduplicated price target after variation expansion. %2$d unreadable; %3$d missing. Preview explains skipped or unsupported products." msgid_plural "At last check: %1$d deduplicated price targets after variation expansion. %2$d unreadable; %3$d missing. Preview explains skipped or unsupported products." msgstr[0] "" msgstr[1] "" -#: includes/free/class-free-admin.php:1521 +#: includes/free/class-free-admin.php:1544 msgid "Support details" msgstr "" -#: includes/free/class-free-admin.php:1533 +#: includes/free/class-free-admin.php:1556 msgid "Sale prices" msgstr "" -#: includes/free/class-free-admin.php:1533 +#: includes/free/class-free-admin.php:1556 msgid "Regular prices" msgstr "" #. translators: 1: range basis, 2: minimum bound, 3: maximum bound, 4: matched candidates, 5: range exclusions, 6: unevaluable source rows. -#: includes/free/class-free-admin.php:1534 -#: includes/free/class-free-admin.php:2424 +#: includes/free/class-free-admin.php:1557 +#: includes/free/class-free-admin.php:2447 msgid "no minimum" msgstr "" #. translators: 1: range basis, 2: minimum bound, 3: maximum bound, 4: matched candidates, 5: range exclusions, 6: unevaluable source rows. -#: includes/free/class-free-admin.php:1535 -#: includes/free/class-free-admin.php:2424 +#: includes/free/class-free-admin.php:1558 +#: includes/free/class-free-admin.php:2447 msgid "no maximum" msgstr "" #. translators: 1: matched products, 2: price basis, 3: minimum price, 4: maximum price, 5: excluded products, 6: unsupported, unreadable or missing products. -#: includes/free/class-free-admin.php:1540 +#: includes/free/class-free-admin.php:1563 #, php-format msgid "%1$d matching product frozen for review · price range on stored %2$s, inclusive: %3$s to %4$s · %5$d excluded by the range · %6$d unsupported, unreadable or missing. Apply changes only these frozen products and never adds more." msgid_plural "%1$d matching products frozen for review · price range on stored %2$s, inclusive: %3$s to %4$s · %5$d excluded by the range · %6$d unsupported, unreadable or missing. Apply changes only these frozen products and never adds more." @@ -854,76 +854,76 @@ msgstr[0] "" msgstr[1] "" #. translators: %s: formatted price or percentage. -#: includes/free/class-free-admin.php:1549 +#: includes/free/class-free-admin.php:1572 #, php-format msgid "Set sale prices to %s" msgstr "" #. translators: %s: formatted price or percentage. -#: includes/free/class-free-admin.php:1549 +#: includes/free/class-free-admin.php:1572 #, php-format msgid "Set regular prices to %s" msgstr "" #. translators: %s: formatted price or percentage. -#: includes/free/class-free-admin.php:1550 -#: includes/free/class-free-admin.php:1553 +#: includes/free/class-free-admin.php:1573 +#: includes/free/class-free-admin.php:1576 #, php-format msgid "Increase sale prices by %s" msgstr "" #. translators: %s: formatted price or percentage. -#: includes/free/class-free-admin.php:1550 -#: includes/free/class-free-admin.php:1553 +#: includes/free/class-free-admin.php:1573 +#: includes/free/class-free-admin.php:1576 #, php-format msgid "Increase regular prices by %s" msgstr "" #. translators: %s: formatted price or percentage. -#: includes/free/class-free-admin.php:1552 -#: includes/free/class-free-admin.php:1555 +#: includes/free/class-free-admin.php:1575 +#: includes/free/class-free-admin.php:1578 #, php-format msgid "Decrease sale prices by %s" msgstr "" #. translators: %s: formatted price or percentage. -#: includes/free/class-free-admin.php:1552 -#: includes/free/class-free-admin.php:1555 +#: includes/free/class-free-admin.php:1575 +#: includes/free/class-free-admin.php:1578 #, php-format msgid "Decrease regular prices by %s" msgstr "" #. translators: %s: formatted price or percentage. -#: includes/free/class-free-admin.php:1558 +#: includes/free/class-free-admin.php:1581 #, php-format msgid "Price operation: %s" msgstr "" #. translators: %s: formatted price or percentage. -#: includes/free/class-free-admin.php:1559 +#: includes/free/class-free-admin.php:1582 msgid "Clear sale prices to blank; preserve sale schedules" msgstr "" #. translators: %s: exact reviewed percentage, with dot decimal separator. -#: includes/free/class-free-admin.php:1562 +#: includes/free/class-free-admin.php:1585 #, php-format msgid "Set sale prices to %s%% below each product’s reviewed Regular Price" msgstr "" #. translators: 1: operation description, 2: selected ending or whole-number label. -#: includes/free/class-free-admin.php:1566 +#: includes/free/class-free-admin.php:1589 #, php-format msgid "%1$s; nearest Price Ending %2$s (ties upward)" msgstr "" #. translators: 1: operation description, 2: selected ending or whole-number label. -#: includes/free/class-free-admin.php:1566 -#: includes/free/class-free-admin.php:2152 +#: includes/free/class-free-admin.php:1589 +#: includes/free/class-free-admin.php:2175 msgid "Whole-number price" msgstr "" #. translators: 1: complete operation description, 2: number of targets. -#: includes/free/class-free-admin.php:1572 +#: includes/free/class-free-admin.php:1595 #, php-format msgid "%1$s · %2$d variation" msgid_plural "%1$s · %2$d variations" @@ -931,7 +931,7 @@ msgstr[0] "" msgstr[1] "" #. translators: 1: complete operation description, 2: number of targets. -#: includes/free/class-free-admin.php:1574 +#: includes/free/class-free-admin.php:1597 #, php-format msgid "%1$s · %2$d product" msgid_plural "%1$s · %2$d products" @@ -939,1095 +939,1103 @@ msgstr[0] "" msgstr[1] "" #. translators: 1: operation and product count, 2: included variations. -#: includes/free/class-free-admin.php:1577 +#: includes/free/class-free-admin.php:1600 #, php-format msgid "%1$s (%2$d variation)" msgid_plural "%1$s (%2$d variations)" msgstr[0] "" msgstr[1] "" -#: includes/free/class-free-admin.php:1584 +#: includes/free/class-free-admin.php:1607 msgid "Blank (no sale price)" msgstr "" -#: includes/free/class-free-admin.php:1588 -#: includes/free/class-free-admin.php:1609 -#: includes/free/class-free-admin.php:1627 -#: includes/free/class-free-admin.php:1629 -#: includes/free/class-free-admin.php:1658 -#: includes/free/class-free-admin.php:1672 -#: includes/free/class-free-admin.php:1683 -#: includes/free/class-free-admin.php:2330 -#: includes/free/class-free-admin.php:2537 -#: includes/free/class-free-admin.php:2557 +#: includes/free/class-free-admin.php:1611 +#: includes/free/class-free-admin.php:1632 +#: includes/free/class-free-admin.php:1650 +#: includes/free/class-free-admin.php:1652 +#: includes/free/class-free-admin.php:1681 +#: includes/free/class-free-admin.php:1695 +#: includes/free/class-free-admin.php:1706 +#: includes/free/class-free-admin.php:2353 +#: includes/free/class-free-admin.php:2560 +#: includes/free/class-free-admin.php:2580 msgid "Unavailable" msgstr "" #. translators: 1: formatted price, 2: exact decimal value, 3: ISO currency code. -#: includes/free/class-free-admin.php:1602 +#: includes/free/class-free-admin.php:1625 #, php-format msgid "%1$s (exact: %2$s %3$s)" msgstr "" -#: includes/free/class-free-admin.php:1613 +#: includes/free/class-free-admin.php:1636 msgid "Decrease <0.01%" msgstr "" -#: includes/free/class-free-admin.php:1613 +#: includes/free/class-free-admin.php:1636 msgid "Increase <0.01%" msgstr "" -#: includes/free/class-free-admin.php:1620 +#: includes/free/class-free-admin.php:1643 msgid "Not yet approved" msgstr "" #. translators: %d: deleted WordPress user ID. -#: includes/free/class-free-admin.php:1623 +#: includes/free/class-free-admin.php:1646 #, php-format msgid "Deleted user (User #%d)" msgstr "" -#: includes/free/class-free-admin.php:1644 +#: includes/free/class-free-admin.php:1667 msgid "Name unavailable at preview" msgstr "" #. translators: %s: product SKU. -#: includes/free/class-free-admin.php:1647 +#: includes/free/class-free-admin.php:1670 #: includes/free/class-product-discovery.php:69 #, php-format msgid "SKU: %s" msgstr "" #. translators: %d: variation ID. -#: includes/free/class-free-admin.php:1647 +#: includes/free/class-free-admin.php:1670 #, php-format msgid "Variation #%d · as reviewed" msgstr "" #. translators: %d: product ID. -#: includes/free/class-free-admin.php:1647 +#: includes/free/class-free-admin.php:1670 #, php-format msgid "Product #%d · as reviewed" msgstr "" -#: includes/free/class-free-admin.php:1652 +#: includes/free/class-free-admin.php:1675 msgid "Review product" msgstr "" -#: includes/free/class-free-admin.php:1689 +#: includes/free/class-free-admin.php:1712 msgid "Unavailable: no confirmed WriteLeash change to restore" msgstr "" -#: includes/free/class-free-admin.php:1694 +#: includes/free/class-free-admin.php:1717 msgid "This product’s price or other conditions changed after you reviewed the preview. WriteLeash left the newer value unchanged." msgstr "" -#: includes/free/class-free-admin.php:1695 +#: includes/free/class-free-admin.php:1718 msgid "This product changed after WriteLeash applied its price. WriteLeash preserved the newer value instead of restoring over it." msgstr "" #. translators: 1: opening link to a new preview, 2: closing link. -#: includes/free/class-free-admin.php:1724 +#: includes/free/class-free-admin.php:1747 #, php-format msgid "Review the product, then %1$sCreate a new preview%2$s if you still want to change it." msgstr "" -#: includes/free/class-free-admin.php:1726 +#: includes/free/class-free-admin.php:1749 msgid "Check the product and saved results before taking further action. This outcome is not confirmed as unchanged." msgstr "" -#: includes/free/class-free-admin.php:1741 +#: includes/free/class-free-admin.php:1764 msgid "Undo finished: eligible prices restored." msgstr "" -#: includes/free/class-free-admin.php:1742 +#: includes/free/class-free-admin.php:1765 msgid "Undo finished with conflicts or failed restorations. Review the affected products." msgstr "" -#: includes/free/class-free-admin.php:1743 +#: includes/free/class-free-admin.php:1766 msgid "Undo stopped by an operator. Already restored prices remain restored." msgstr "" -#: includes/free/class-free-admin.php:1744 +#: includes/free/class-free-admin.php:1767 msgid "Undo expired: the restoration window has ended." msgstr "" -#: includes/free/class-free-admin.php:1745 +#: includes/free/class-free-admin.php:1768 msgid "Undo unavailable: this plan cannot be executed." msgstr "" -#: includes/free/class-free-admin.php:1746 +#: includes/free/class-free-admin.php:1769 msgid "Undo unavailable: this plan is awaiting review and has not run." msgstr "" -#: includes/free/class-free-admin.php:1747 +#: includes/free/class-free-admin.php:1770 msgid "Undo unavailable: Apply has not safely finished. Check or finish the remaining work first." msgstr "" -#: includes/free/class-free-admin.php:1748 +#: includes/free/class-free-admin.php:1771 msgid "Undo unavailable: no products were changed by this job." msgstr "" -#: includes/free/class-free-admin.php:1749 +#: includes/free/class-free-admin.php:1772 msgid "Undo needs checking: a restoration outcome is uncertain. Continuing will not retry uncertain or conflicted products." msgstr "" -#: includes/free/class-free-admin.php:1750 +#: includes/free/class-free-admin.php:1773 msgid "Undo paused. Continue Undo for remaining products." msgstr "" -#: includes/free/class-free-admin.php:1751 -msgid "Undo in progress. Reload to check results or continue remaining products." +#: includes/free/class-free-admin.php:1774 +msgid "Undo in progress. Progress updates automatically." msgstr "" -#: includes/free/class-free-admin.php:1752 +#: includes/free/class-free-admin.php:1775 msgid "Undo outcome unavailable; needs checking." msgstr "" -#: includes/free/class-free-admin.php:1753 +#: includes/free/class-free-admin.php:1776 msgid "Undo available for prices changed by this job." msgstr "" -#: includes/free/class-free-admin.php:1753 +#: includes/free/class-free-admin.php:1776 msgid "Undo unavailable: saved restoration evidence is unavailable." msgstr "" -#: includes/free/class-free-admin.php:1757 +#: includes/free/class-free-admin.php:1780 msgid "Job history" msgstr "" -#: includes/free/class-free-admin.php:1757 +#: includes/free/class-free-admin.php:1780 msgid "Task / operator / time" msgstr "" -#: includes/free/class-free-admin.php:1757 +#: includes/free/class-free-admin.php:1780 msgid "Outcome" msgstr "" -#: includes/free/class-free-admin.php:1757 +#: includes/free/class-free-admin.php:1780 msgid "Undo" msgstr "" -#: includes/free/class-free-admin.php:1757 +#: includes/free/class-free-admin.php:1780 msgid "Action" msgstr "" -#: includes/free/class-free-admin.php:1758 +#: includes/free/class-free-admin.php:1781 msgid "No jobs visible to your account yet. Create a new preview to start." msgstr "" -#: includes/free/class-free-admin.php:1774 +#: includes/free/class-free-admin.php:1797 msgid "Saved task details unavailable; needs checking" msgstr "" #. translators: %s: creator display name. -#: includes/free/class-free-admin.php:1778 +#: includes/free/class-free-admin.php:1801 #, php-format msgid "Created by %s" msgstr "" #. translators: %s: approver display name. -#: includes/free/class-free-admin.php:1779 +#: includes/free/class-free-admin.php:1802 #, php-format msgid "Approved by %s" msgstr "" #. translators: %s: localized expiry date and time. -#: includes/free/class-free-admin.php:1785 +#: includes/free/class-free-admin.php:1808 #, php-format msgid "Until %s" msgstr "" -#: includes/free/class-free-admin.php:1794 +#: includes/free/class-free-admin.php:1817 msgid "This is a Preview snapshot, not the current live catalog and not an offline approval." msgstr "" -#: includes/free/class-free-admin.php:1798 +#: includes/free/class-free-admin.php:1821 msgid "Download Preview CSV" msgstr "" -#: includes/free/class-free-admin.php:1865 -#: includes/free/class-free-admin.php:1950 +#: includes/free/class-free-admin.php:1888 +#: includes/free/class-free-admin.php:1973 msgid "Saved export evidence is unavailable. No products were changed." msgstr "" -#: includes/free/class-free-admin.php:1883 +#: includes/free/class-free-admin.php:1906 msgid "Download job CSV" msgstr "" -#: includes/free/class-free-admin.php:1965 +#: includes/free/class-free-admin.php:1988 msgid "Preview bulk regular or sale prices before they happen. Run them safely. If the price or a checked setting changed after review, WriteLeash leaves the newer value alone. Undo changes that are still safe to restore." msgstr "" -#: includes/free/class-free-admin.php:1968 +#: includes/free/class-free-admin.php:1991 msgid "Change the stored price of published simple products and variations of published variable products in the base store currency. Variations can be selected as a whole product or individually. Sale dates, stock, orders and subscriptions are not changed." msgstr "" #. translators: %d: maximum selected products per new job. -#: includes/free/class-free-admin.php:1972 +#: includes/free/class-free-admin.php:1995 #, php-format msgid "Up to %d selected products per new job in the tested configuration. Planned prices are not guaranteed shopper prices: taxes, currency settings, dynamic pricing and later edits still apply. You can close this page at any time; saved progress appears again when you return." msgstr "" -#: includes/free/class-free-admin.php:1975 +#: includes/free/class-free-admin.php:1998 msgid "Your role can view this page but cannot preview or apply bulk price changes." msgstr "" -#: includes/free/class-free-admin.php:1977 +#: includes/free/class-free-admin.php:2000 msgid "New price change" msgstr "" -#: includes/free/class-free-admin.php:1980 +#: includes/free/class-free-admin.php:2003 msgid "Recent jobs" msgstr "" -#: includes/free/class-free-admin.php:1982 +#: includes/free/class-free-admin.php:2005 msgid "Open full history" msgstr "" -#: includes/free/class-free-admin.php:2023 +#: includes/free/class-free-admin.php:2046 msgid "Only change products in a price range" msgstr "" -#: includes/free/class-free-admin.php:2024 +#: includes/free/class-free-admin.php:2047 msgid "Optional. When enabled, Preview includes only products whose stored price falls within the inclusive range: the minimum and maximum both count as matches. Each variation is judged by its own price, not its parent’s. Products without a stored sale price never match a sale-price range. Use unsigned numbers with a dot for decimals; leave out currency symbols." msgstr "" -#: includes/free/class-free-admin.php:2025 +#: includes/free/class-free-admin.php:2048 msgid "Compare prices using" msgstr "" -#: includes/free/class-free-admin.php:2026 -#: includes/free/class-free-admin.php:2142 +#: includes/free/class-free-admin.php:2049 +#: includes/free/class-free-admin.php:2165 #: includes/free/class-price-operation.php:60 msgid "Regular price" msgstr "" -#: includes/free/class-free-admin.php:2026 -#: includes/free/class-free-admin.php:2142 +#: includes/free/class-free-admin.php:2049 +#: includes/free/class-free-admin.php:2165 #: includes/free/class-price-operation.php:60 msgid "Sale price" msgstr "" #. translators: %s: ISO currency code in parentheses, or empty. -#: includes/free/class-free-admin.php:2031 +#: includes/free/class-free-admin.php:2054 #, php-format msgid "Minimum price%s" msgstr "" #. translators: %s: ISO currency code in parentheses, or empty. -#: includes/free/class-free-admin.php:2033 +#: includes/free/class-free-admin.php:2056 #, php-format msgid "Maximum price%s" msgstr "" #. translators: %d: selected product ID. No product details are disclosed. -#: includes/free/class-free-admin.php:2064 +#: includes/free/class-free-admin.php:2087 #, php-format msgid "Product #%d unavailable or inaccessible. ID retained; remove it or check access before Preview." msgstr "" -#: includes/free/class-free-admin.php:2074 +#: includes/free/class-free-admin.php:2097 msgid "1. Select products" msgstr "" #. translators: %d: maximum selected products per new job. -#: includes/free/class-free-admin.php:2076 +#: includes/free/class-free-admin.php:2099 #, php-format msgid "Choose specific products or one category. Categories include direct members only by default; subcategories are not included unless you enable Include subcategories. Search matches are not automatically selected and do not guarantee eligibility. Preview checks every selected product. Maximum %d selected products." msgstr "" #. translators: %d: maximum selected products per new job. -#: includes/free/class-free-admin.php:2077 +#: includes/free/class-free-admin.php:2100 msgid "Selection method" msgstr "" -#: includes/free/class-free-admin.php:2078 +#: includes/free/class-free-admin.php:2101 msgid "Choose products by name or SKU" msgstr "" -#: includes/free/class-free-admin.php:2078 -#: includes/free/class-free-admin.php:2111 +#: includes/free/class-free-admin.php:2101 +#: includes/free/class-free-admin.php:2134 msgid "Product category" msgstr "" -#: includes/free/class-free-admin.php:2078 -#: includes/free/class-free-admin.php:2131 +#: includes/free/class-free-admin.php:2101 +#: includes/free/class-free-admin.php:2154 msgid "One exact SKU" msgstr "" -#: includes/free/class-free-admin.php:2078 +#: includes/free/class-free-admin.php:2101 msgid "Advanced: manual product IDs" msgstr "" -#: includes/free/class-free-admin.php:2081 +#: includes/free/class-free-admin.php:2104 msgid "Search and choose products. If live suggestions are unavailable, use the search without live suggestions." msgstr "" -#: includes/free/class-free-admin.php:2083 +#: includes/free/class-free-admin.php:2106 msgid "Search products without live suggestions" msgstr "" -#: includes/free/class-free-admin.php:2084 +#: includes/free/class-free-admin.php:2107 msgid "Product name or partial SKU" msgstr "" -#: includes/free/class-free-admin.php:2086 +#: includes/free/class-free-admin.php:2109 msgid "Search products" msgstr "" -#: includes/free/class-free-admin.php:2087 +#: includes/free/class-free-admin.php:2110 msgid "More product matches" msgstr "" -#: includes/free/class-free-admin.php:2088 +#: includes/free/class-free-admin.php:2111 msgid "No matches on this page. Try another product name or SKU." msgstr "" -#: includes/free/class-free-admin.php:2089 +#: includes/free/class-free-admin.php:2112 #: includes/free/free-selection.js:92 msgid "Search limit reached. Use a more specific name or SKU." msgstr "" -#: includes/free/class-free-admin.php:2090 +#: includes/free/class-free-admin.php:2113 msgid "Use Ctrl/Command to select several matches below, then Update selected products. Selected products stay on subsequent search pages." msgstr "" -#: includes/free/class-free-admin.php:2091 +#: includes/free/class-free-admin.php:2114 msgid "Update selected products" msgstr "" -#: includes/free/class-free-admin.php:2093 +#: includes/free/class-free-admin.php:2116 msgid "Choose products" msgstr "" -#: includes/free/class-free-admin.php:2097 +#: includes/free/class-free-admin.php:2120 msgid "Check the selected products below. Preview checks their eligibility before any prices change." msgstr "" -#: includes/free/class-free-admin.php:2098 +#: includes/free/class-free-admin.php:2121 msgid "Selected products" msgstr "" -#: includes/free/class-free-admin.php:2100 +#: includes/free/class-free-admin.php:2123 #: includes/free/free-selection.js:55 msgid "Remove" msgstr "" #. translators: %s: product description. -#: includes/free/class-free-admin.php:2100 +#: includes/free/class-free-admin.php:2123 #: includes/free/free-selection.js:55 #, php-format,js-format msgid "Remove %s" msgstr "" #. translators: %s: product description. -#: includes/free/class-free-admin.php:2101 +#: includes/free/class-free-admin.php:2124 #: includes/free/free-selection.js:63 msgid "No products selected. Search and choose products to add them." msgstr "" -#: includes/free/class-free-admin.php:2102 +#: includes/free/class-free-admin.php:2125 msgid "Clear selected products" msgstr "" -#: includes/free/class-free-admin.php:2103 +#: includes/free/class-free-admin.php:2126 msgid "Search categories without live suggestions" msgstr "" -#: includes/free/class-free-admin.php:2104 +#: includes/free/class-free-admin.php:2127 msgid "Category name" msgstr "" -#: includes/free/class-free-admin.php:2106 +#: includes/free/class-free-admin.php:2129 msgid "Search categories" msgstr "" -#: includes/free/class-free-admin.php:2107 +#: includes/free/class-free-admin.php:2130 msgid "More category matches" msgstr "" -#: includes/free/class-free-admin.php:2108 +#: includes/free/class-free-admin.php:2131 msgid "Search limit reached. Use a more specific category name." msgstr "" -#: includes/free/class-free-admin.php:2109 +#: includes/free/class-free-admin.php:2132 msgid "No categories match this page. Try another category name." msgstr "" -#: includes/free/class-free-admin.php:2111 +#: includes/free/class-free-admin.php:2134 msgid "Choose a category" msgstr "" -#: includes/free/class-free-admin.php:2117 +#: includes/free/class-free-admin.php:2140 msgid "Saved category unavailable — choose another category" msgstr "" -#: includes/free/class-free-admin.php:2119 +#: includes/free/class-free-admin.php:2142 msgid "Include subcategories" msgstr "" -#: includes/free/class-free-admin.php:2119 +#: includes/free/class-free-admin.php:2142 msgid "When enabled, products in all nested subcategories are included once. Variable parents expand to their variations." msgstr "" -#: includes/free/class-free-admin.php:2120 +#: includes/free/class-free-admin.php:2143 msgid "Check selection count" msgstr "" #. translators: %d: maximum selected products per new job. -#: includes/free/class-free-admin.php:2124 +#: includes/free/class-free-admin.php:2147 #, php-format msgid "This selection exceeds the supported limit of %d price targets. Choose a smaller selection before Preview." msgstr "" -#: includes/free/class-free-admin.php:2127 +#: includes/free/class-free-admin.php:2150 msgid "Check the number of price targets before Preview, including variations." msgstr "" -#: includes/free/class-free-admin.php:2128 +#: includes/free/class-free-admin.php:2151 msgid "Counts are informational and can change. Preview resolves the selection again and freezes the exact products for your review; no count authorizes a price change." msgstr "" -#: includes/free/class-free-admin.php:2129 +#: includes/free/class-free-admin.php:2152 msgid "Exact SKU or manual product IDs" msgstr "" -#: includes/free/class-free-admin.php:2129 +#: includes/free/class-free-admin.php:2152 msgid "Enter one complete SKU or comma-separated product IDs for the selection method chosen above." msgstr "" -#: includes/free/class-free-admin.php:2130 +#: includes/free/class-free-admin.php:2153 msgid "Explicit product IDs (advanced), e.g. 12,34,56" msgstr "" -#: includes/free/class-free-admin.php:2141 +#: includes/free/class-free-admin.php:2164 msgid "2. Choose the price change" msgstr "" -#: includes/free/class-free-admin.php:2141 +#: includes/free/class-free-admin.php:2164 msgid "Price to change" msgstr "" -#: includes/free/class-free-admin.php:2145 +#: includes/free/class-free-admin.php:2168 msgid "Regular price is the everyday price. Sale price is the discounted price during a sale. WriteLeash changes only the price you choose and keeps the other one." msgstr "" -#: includes/free/class-free-admin.php:2145 +#: includes/free/class-free-admin.php:2168 msgid "Operation" msgstr "" -#: includes/free/class-free-admin.php:2146 +#: includes/free/class-free-admin.php:2169 msgid "Set an exact price" msgstr "" -#: includes/free/class-free-admin.php:2146 +#: includes/free/class-free-admin.php:2169 msgid "Increase by fixed amount" msgstr "" -#: includes/free/class-free-admin.php:2146 +#: includes/free/class-free-admin.php:2169 msgid "Decrease by fixed amount" msgstr "" -#: includes/free/class-free-admin.php:2146 +#: includes/free/class-free-admin.php:2169 msgid "Increase by percent" msgstr "" -#: includes/free/class-free-admin.php:2146 +#: includes/free/class-free-admin.php:2169 msgid "Decrease by percent" msgstr "" -#: includes/free/class-free-admin.php:2146 +#: includes/free/class-free-admin.php:2169 msgid "Clear Sale Price" msgstr "" -#: includes/free/class-free-admin.php:2146 +#: includes/free/class-free-admin.php:2169 msgid "Sale discount from Regular Price (%)" msgstr "" -#: includes/free/class-free-admin.php:2150 +#: includes/free/class-free-admin.php:2173 msgid "No amount — leave blank" msgstr "" -#: includes/free/class-free-admin.php:2150 +#: includes/free/class-free-admin.php:2173 #: includes/free/free-selection.js:30 msgid "New price" msgstr "" -#: includes/free/class-free-admin.php:2150 +#: includes/free/class-free-admin.php:2173 #: includes/free/free-selection.js:30 msgid "Percentage (e.g. 8 for 8%)" msgstr "" -#: includes/free/class-free-admin.php:2150 +#: includes/free/class-free-admin.php:2173 #: includes/free/free-selection.js:30 msgid "Amount to increase by" msgstr "" -#: includes/free/class-free-admin.php:2150 +#: includes/free/class-free-admin.php:2173 #: includes/free/free-selection.js:30 msgid "Amount to decrease by" msgstr "" -#: includes/free/class-free-admin.php:2151 +#: includes/free/class-free-admin.php:2174 msgid "Price Ending (optional)" msgstr "" -#: includes/free/class-free-admin.php:2152 +#: includes/free/class-free-admin.php:2175 msgid "Unchanged / Default" msgstr "" -#: includes/free/class-free-admin.php:2152 +#: includes/free/class-free-admin.php:2175 msgid "End in .99" msgstr "" -#: includes/free/class-free-admin.php:2152 +#: includes/free/class-free-admin.php:2175 msgid "End in .95" msgstr "" -#: includes/free/class-free-admin.php:2152 +#: includes/free/class-free-admin.php:2175 msgid "End in .90" msgstr "" -#: includes/free/class-free-admin.php:2155 +#: includes/free/class-free-admin.php:2178 msgid "Nearest nonnegative matching price; exact ties go upward. Below the first ending, use 0.99, 0.95 or 0.90; whole prices may become zero. Default keeps existing calculations. .99/.95 need two decimal places; .90 needs one. Clear Sale ignores endings and stays blank. Preview checks the final rounded price against all safety limits." msgstr "" -#: includes/free/class-free-admin.php:2159 +#: includes/free/class-free-admin.php:2182 msgid "Enter a positive number or zero. Use a dot for decimals; leave out currency symbols and the % sign." msgstr "" -#: includes/free/class-free-admin.php:2160 +#: includes/free/class-free-admin.php:2183 msgid "Choose Sale price for Clear or Sale discount. Clear makes Sale Price blank and preserves sale schedules; safety caps check the return to Regular Price. Sale discount requires an explicit 0–100 percentage (up to six decimal places), calculated from each product’s own reviewed Regular Price. A changed Regular Price conflicts at Apply; the approved target is never recalculated. Zero or a sale at Regular Price still must pass eligibility and safety checks." msgstr "" -#: includes/free/class-free-admin.php:2162 +#: includes/free/class-free-admin.php:2185 msgid "Safety limits (optional)" msgstr "" -#: includes/free/class-free-admin.php:2162 +#: includes/free/class-free-admin.php:2185 msgid "Safety limits" msgstr "" -#: includes/free/class-free-admin.php:2162 +#: includes/free/class-free-admin.php:2185 msgid "A change beyond these limits blocks the whole preview." msgstr "" #. translators: %d: maximum products allowed. -#: includes/free/class-free-admin.php:2165 +#: includes/free/class-free-admin.php:2188 #, php-format msgid "Maximum changing products (0-%d)" msgstr "" #. translators: %d: maximum products allowed. -#: includes/free/class-free-admin.php:2166 +#: includes/free/class-free-admin.php:2189 msgid "Maximum increase percent" msgstr "" #. translators: %d: maximum products allowed. -#: includes/free/class-free-admin.php:2166 +#: includes/free/class-free-admin.php:2189 msgid "Maximum decrease percent" msgstr "" #. translators: %d: maximum products allowed. -#: includes/free/class-free-admin.php:2166 +#: includes/free/class-free-admin.php:2189 msgid "Warning threshold percent" msgstr "" -#: includes/free/class-free-admin.php:2167 +#: includes/free/class-free-admin.php:2190 msgid "Block a preview that sets any changing price to zero" msgstr "" -#: includes/free/class-free-admin.php:2170 +#: includes/free/class-free-admin.php:2193 msgid "Preset name" msgstr "" -#: includes/free/class-free-admin.php:2170 +#: includes/free/class-free-admin.php:2193 msgid "Save as preset" msgstr "" -#: includes/free/class-free-admin.php:2172 +#: includes/free/class-free-admin.php:2195 msgid "Preview price changes" msgstr "" -#: includes/free/class-free-admin.php:2172 +#: includes/free/class-free-admin.php:2195 msgid "Preview does not change any prices." msgstr "" -#: includes/free/class-free-admin.php:2187 +#: includes/free/class-free-admin.php:2210 msgid "No price changes yet. Preview your first change above to start." msgstr "" -#: includes/free/class-free-admin.php:2195 -#: includes/free/class-free-admin.php:2742 -msgid "History is unavailable right now. Reload the page to try again." +#: includes/free/class-free-admin.php:2218 +#: includes/free/class-free-admin.php:2779 +msgid "History is unavailable right now. Refresh the page to try again." msgstr "" -#: includes/free/class-free-admin.php:2206 +#: includes/free/class-free-admin.php:2229 msgid "Continue review" msgstr "" -#: includes/free/class-free-admin.php:2206 +#: includes/free/class-free-admin.php:2229 msgid "Review blocked plan" msgstr "" -#: includes/free/class-free-admin.php:2206 +#: includes/free/class-free-admin.php:2229 msgid "Open progress/results" msgstr "" -#: includes/free/class-free-admin.php:2223 -#: includes/free/class-free-admin.php:2229 +#: includes/free/class-free-admin.php:2246 +#: includes/free/class-free-admin.php:2252 msgid "No preview is visible to your account for that identifier." msgstr "" -#: includes/free/class-free-admin.php:2224 -#: includes/free/class-free-admin.php:2230 -#: includes/free/class-free-admin.php:2263 -#: includes/free/class-free-admin.php:2565 -#: includes/free/class-free-admin.php:2571 -#: includes/free/class-free-admin.php:2587 -#: includes/free/class-free-admin.php:2733 +#: includes/free/class-free-admin.php:2247 +#: includes/free/class-free-admin.php:2253 +#: includes/free/class-free-admin.php:2286 +#: includes/free/class-free-admin.php:2598 +#: includes/free/class-free-admin.php:2604 +#: includes/free/class-free-admin.php:2620 +#: includes/free/class-free-admin.php:2770 msgid "Back to bulk prices" msgstr "" -#: includes/free/class-free-admin.php:2248 +#: includes/free/class-free-admin.php:2271 msgid "Fresh Preview repeated from a previous job. Original results stay unchanged; this Preview needs its own approval before Apply." msgstr "" -#: includes/free/class-free-admin.php:2248 +#: includes/free/class-free-admin.php:2271 msgid "Fresh Preview from chosen Apply conflicts. Original results stay unchanged." msgstr "" -#: includes/free/class-free-admin.php:2256 +#: includes/free/class-free-admin.php:2279 msgid "Open progress and available Undo" msgstr "" -#: includes/free/class-free-admin.php:2264 +#: includes/free/class-free-admin.php:2287 msgid "Review price change" msgstr "" -#: includes/free/class-free-admin.php:2268 -#: includes/free/class-free-admin.php:2427 +#: includes/free/class-free-admin.php:2291 +#: includes/free/class-free-admin.php:2450 msgid "Category scope: direct members and all nested subcategories. This preview freezes the reviewed product IDs, including expanded variations." msgstr "" -#: includes/free/class-free-admin.php:2268 -#: includes/free/class-free-admin.php:2427 +#: includes/free/class-free-admin.php:2291 +#: includes/free/class-free-admin.php:2450 msgid "Category scope: direct members only; subcategories are not included. This preview freezes the reviewed product IDs, including expanded variations." msgstr "" #. translators: 1: selected products, 2: translated outcome counts. -#: includes/free/class-free-admin.php:2275 +#: includes/free/class-free-admin.php:2298 #, php-format msgid "Selected %1$d product: %2$s." msgid_plural "Selected %1$d products: %2$s." msgstr[0] "" msgstr[1] "" -#: includes/free/class-free-admin.php:2279 +#: includes/free/class-free-admin.php:2302 msgid "Your safety limits block every changing product. You cannot approve this preview; build a new one with different settings." msgstr "" #. translators: 1: displayed products, 2: additional blocked products. -#: includes/free/class-free-admin.php:2300 +#: includes/free/class-free-admin.php:2323 #, php-format msgid "Showing the first %1$d blocked products in this summary. Each product page below shows its own reason; %2$d more blocked product is not listed here." msgid_plural "Showing the first %1$d blocked products in this summary. Each product page below shows its own reason; %2$d more blocked products are not listed here." msgstr[0] "" msgstr[1] "" -#: includes/free/class-free-admin.php:2308 +#: includes/free/class-free-admin.php:2331 msgid "Invalid preview page; use a nonnegative offset." msgstr "" #. translators: %s: translated price-field name. -#: includes/free/class-free-admin.php:2314 +#: includes/free/class-free-admin.php:2337 #, php-format msgid "Price to change: %s. The regular price is the sale baseline and is preserved." msgstr "" #. translators: %s: translated price-field name. -#: includes/free/class-free-admin.php:2314 +#: includes/free/class-free-admin.php:2337 #, php-format msgid "Price to change: %s. The sale price and schedule are preserved." msgstr "" #. translators: %s: translated price-field name. -#: includes/free/class-free-admin.php:2315 -#: includes/free/class-free-admin.php:2659 +#: includes/free/class-free-admin.php:2338 +#: includes/free/class-free-admin.php:2694 msgid "Product prices and outcomes" msgstr "" #. translators: %s: translated price-field name. -#: includes/free/class-free-admin.php:2315 -#: includes/free/class-free-admin.php:2444 -#: includes/free/class-free-admin.php:2659 +#: includes/free/class-free-admin.php:2338 +#: includes/free/class-free-admin.php:2467 +#: includes/free/class-free-admin.php:2694 msgid "Product" msgstr "" #. translators: %s: translated price-field name. -#: includes/free/class-free-admin.php:2315 +#: includes/free/class-free-admin.php:2338 #, php-format msgid "%s before" msgstr "" #. translators: %s: translated price-field name. -#: includes/free/class-free-admin.php:2315 +#: includes/free/class-free-admin.php:2338 #, php-format msgid "%s after" msgstr "" #. translators: %s: translated price-field name. -#: includes/free/class-free-admin.php:2315 +#: includes/free/class-free-admin.php:2338 msgid "Delta" msgstr "" #. translators: %s: translated price-field name. -#: includes/free/class-free-admin.php:2315 +#: includes/free/class-free-admin.php:2338 msgid "Change %" msgstr "" #. translators: %s: translated price-field name. -#: includes/free/class-free-admin.php:2315 +#: includes/free/class-free-admin.php:2338 msgid "Shoppers would pay after Apply" msgstr "" #. translators: %s: translated price-field name. -#: includes/free/class-free-admin.php:2315 +#: includes/free/class-free-admin.php:2338 msgid "What will happen" msgstr "" -#: includes/free/class-free-admin.php:2346 -#: includes/free/class-free-admin.php:2438 +#: includes/free/class-free-admin.php:2369 +#: includes/free/class-free-admin.php:2461 msgid "Exclude individual products" msgstr "" -#: includes/free/class-free-admin.php:2348 -#: includes/free/class-free-admin.php:2589 +#: includes/free/class-free-admin.php:2371 +#: includes/free/class-free-admin.php:2622 msgid "Create a new preview" msgstr "" -#: includes/free/class-free-admin.php:2348 +#: includes/free/class-free-admin.php:2371 msgid "Changing the selection or price settings creates a new preview; this saved preview stays unchanged." msgstr "" -#: includes/free/class-free-admin.php:2350 +#: includes/free/class-free-admin.php:2373 msgid "Approve this preview" msgstr "" -#: includes/free/class-free-admin.php:2351 +#: includes/free/class-free-admin.php:2374 msgid "Approving applies exactly the products and prices shown above. Planned prices are not guaranteed shopper prices. If you change any setting, build a new preview." msgstr "" -#: includes/free/class-free-admin.php:2356 +#: includes/free/class-free-admin.php:2379 msgid "Approve and apply" msgstr "" #. translators: %s: translated job status. -#: includes/free/class-free-admin.php:2359 +#: includes/free/class-free-admin.php:2382 #, php-format msgid "Current status: %s." msgstr "" #. translators: %s: translated job status. -#: includes/free/class-free-admin.php:2359 +#: includes/free/class-free-admin.php:2382 msgid "View progress and results" msgstr "" #. translators: 1: range basis, 2: minimum bound, 3: maximum bound, 4: matched candidates, 5: range exclusions, 6: unevaluable source rows. -#: includes/free/class-free-admin.php:2424 +#: includes/free/class-free-admin.php:2447 #, php-format msgid "Source price range on %1$s, inclusive: %2$s to %3$s · %4$d candidates before individual exclusions · %5$d excluded by the range · %6$d unsupported, unreadable or missing in the source." msgstr "" #. translators: 1: resolved candidates, 2: included targets, 3: excluded targets, 4: changing targets, 5: unchanged targets, 6: refused candidates. -#: includes/free/class-free-admin.php:2429 +#: includes/free/class-free-admin.php:2452 #, php-format msgid "Resolved candidates %1$d · Included targets %2$d · Excluded targets %3$d · Changing included targets %4$d · Unchanged included targets %5$d · Unsupported/refused candidates %6$d" msgstr "" #. translators: 1: resolved candidates, 2: included targets, 3: excluded targets, 4: changing targets, 5: unchanged targets, 6: refused candidates. -#: includes/free/class-free-admin.php:2430 +#: includes/free/class-free-admin.php:2453 msgid "Only included targets belong to this immutable Plan. Excluded and refused candidates receive no Apply or Undo entries. A separate approval is required." msgstr "" -#: includes/free/class-free-admin.php:2431 +#: includes/free/class-free-admin.php:2454 msgid "This refined Preview cannot be saved as a preset. Its individual exclusions stay frozen here; an ordinary preset does not preserve them or change this Preview." msgstr "" -#: includes/free/class-free-admin.php:2436 +#: includes/free/class-free-admin.php:2459 msgid "Selection refinement is unavailable. Open an unapproved Preview visible to your account." msgstr "" -#: includes/free/class-free-admin.php:2438 +#: includes/free/class-free-admin.php:2461 msgid "Select rows and submit Exclude or Restore before navigating pages. Each submission saves a fresh immutable Preview with all previously confirmed exclusions. No prices change here." msgstr "" -#: includes/free/class-free-admin.php:2439 +#: includes/free/class-free-admin.php:2462 msgid "Checked excluded rows retain their confirmed state on reload. Unchecking alone does not restore a row: select it and use Restore selected rows." msgstr "" #. translators: %d: resolved candidate count. -#: includes/free/class-free-admin.php:2441 +#: includes/free/class-free-admin.php:2464 #, php-format msgid "Resolved candidates %d. Confirm the selection to create the final included Plan; unsupported rows will remain explained outside its target set." msgstr "" -#: includes/free/class-free-admin.php:2444 +#: includes/free/class-free-admin.php:2467 msgid "Select row" msgstr "" -#: includes/free/class-free-admin.php:2444 +#: includes/free/class-free-admin.php:2467 msgid "Membership" msgstr "" -#: includes/free/class-free-admin.php:2447 +#: includes/free/class-free-admin.php:2470 msgid "Excluded" msgstr "" -#: includes/free/class-free-admin.php:2447 +#: includes/free/class-free-admin.php:2470 msgid "Unsupported/refused" msgstr "" -#: includes/free/class-free-admin.php:2447 +#: includes/free/class-free-admin.php:2470 msgid "Included" msgstr "" #. translators: %d: exact product ID. -#: includes/free/class-free-admin.php:2448 +#: includes/free/class-free-admin.php:2471 #, php-format msgid "Select product #%d" msgstr "" -#: includes/free/class-free-admin.php:2450 +#: includes/free/class-free-admin.php:2473 msgid "Exclude selected rows" msgstr "" -#: includes/free/class-free-admin.php:2450 +#: includes/free/class-free-admin.php:2473 msgid "Restore selected rows" msgstr "" -#: includes/free/class-free-admin.php:2450 +#: includes/free/class-free-admin.php:2473 msgid "Confirm selection with no additional changes" msgstr "" -#: includes/free/class-free-admin.php:2452 +#: includes/free/class-free-admin.php:2475 msgid "Review updated Preview" msgstr "" -#: includes/free/class-free-admin.php:2452 +#: includes/free/class-free-admin.php:2475 msgid "Cancel selection changes" msgstr "" -#: includes/free/class-free-admin.php:2459 -#: includes/free/class-free-admin.php:2461 +#: includes/free/class-free-admin.php:2482 +#: includes/free/class-free-admin.php:2484 msgid "Previous page" msgstr "" -#: includes/free/class-free-admin.php:2464 -#: includes/free/class-free-admin.php:2466 -#: includes/free/free-progress.js:127 +#: includes/free/class-free-admin.php:2487 +#: includes/free/class-free-admin.php:2489 +#: includes/free/free-progress.js:154 msgid "Next page" msgstr "" -#: includes/free/class-free-admin.php:2476 +#: includes/free/class-free-admin.php:2499 msgid "At page load, this product is no longer a supported core simple product or variation." msgstr "" -#: includes/free/class-free-admin.php:2477 +#: includes/free/class-free-admin.php:2500 msgid "At page load, this variation’s parent is no longer a published core variable product." msgstr "" -#: includes/free/class-free-admin.php:2478 +#: includes/free/class-free-admin.php:2501 msgid "At page load, this product is no longer published." msgstr "" -#: includes/free/class-free-admin.php:2479 +#: includes/free/class-free-admin.php:2502 msgid "At page load, this product has a sale price or schedule; a matching regular price alone does not authorize overwriting it." msgstr "" -#: includes/free/class-free-admin.php:2494 -#: includes/free/class-free-admin.php:2527 -#: includes/free/class-free-admin.php:2537 +#: includes/free/class-free-admin.php:2517 +#: includes/free/class-free-admin.php:2550 +#: includes/free/class-free-admin.php:2560 msgid "Current product details are unavailable." msgstr "" -#: includes/free/class-free-admin.php:2564 -#: includes/free/class-free-admin.php:2570 +#: includes/free/class-free-admin.php:2597 +#: includes/free/class-free-admin.php:2603 msgid "No job is visible to your account for that identifier." msgstr "" -#: includes/free/class-free-admin.php:2581 -msgid "Saved progress and results are unavailable right now. Reload this page to try again; if it persists, ask an administrator to check the saved records." +#: includes/free/class-free-admin.php:2614 +msgid "Saved progress and results are unavailable right now. Refresh this page to try again; if it persists, ask an administrator to check the saved records." msgstr "" -#: includes/free/class-free-admin.php:2588 +#: includes/free/class-free-admin.php:2621 msgid "Open history" msgstr "" -#: includes/free/class-free-admin.php:2591 +#: includes/free/class-free-admin.php:2624 msgid "Refresh saved progress" msgstr "" -#: includes/free/class-free-admin.php:2591 +#: includes/free/class-free-admin.php:2624 msgid "Automatic updates require JavaScript. Refresh to read saved progress." msgstr "" -#: includes/free/class-free-admin.php:2592 +#: includes/free/class-free-admin.php:2625 msgid "Progress and results" msgstr "" #. translators: %d: planned products. -#: includes/free/class-free-admin.php:2595 +#: includes/free/class-free-admin.php:2628 #, php-format msgid "Planned %d product. Some saved product evidence is unavailable. Check this job before taking further action." msgid_plural "Planned %d products. Some saved product evidence is unavailable. Check this job before taking further action." msgstr[0] "" msgstr[1] "" -#: includes/free/class-free-admin.php:2601 +#: includes/free/class-free-admin.php:2630 +msgid "Apply progress" +msgstr "" + +#: includes/free/class-free-admin.php:2636 msgid "Background processing stopped making progress. Use Resume remaining products to continue; completed changes are kept." msgstr "" -#: includes/free/class-free-admin.php:2604 +#: includes/free/class-free-admin.php:2639 msgid "An outcome needs checking. Review uncertain products before continuing remaining work; uncertain products will not be retried." msgstr "" -#: includes/free/class-free-admin.php:2604 +#: includes/free/class-free-admin.php:2639 msgid "The price change finished with products needing attention. Review conflicts and failed products below, then create a new preview if needed." msgstr "" -#: includes/free/class-free-admin.php:2613 +#: includes/free/class-free-admin.php:2648 msgid "Waiting for background processing. Use Resume remaining products to run the next step now." msgstr "" -#: includes/free/class-free-admin.php:2618 -#: includes/free/free-progress.js:102 +#: includes/free/class-free-admin.php:2653 +#: includes/free/free-progress.js:129 msgid "Resume remaining products" msgstr "" -#: includes/free/class-free-admin.php:2619 +#: includes/free/class-free-admin.php:2654 msgid "Runs only the remaining products; completed changes are kept. Products changed since Preview are left alone." msgstr "" -#: includes/free/class-free-admin.php:2631 +#: includes/free/class-free-admin.php:2666 msgid "Start a new editable Preview from this job’s settings. No prices change until you approve the new Preview." msgstr "" #. translators: %s: translated price-field name. -#: includes/free/class-free-admin.php:2634 -#: includes/free/class-free-admin.php:2659 +#: includes/free/class-free-admin.php:2669 +#: includes/free/class-free-admin.php:2694 msgctxt "merchant price workflow label" msgid "Undo" msgstr "" -#: includes/free/class-free-admin.php:2636 -msgid "Undo availability cannot be verified while product outcomes are missing. Reload this job or ask an administrator to check its saved records." +#: includes/free/class-free-admin.php:2671 +msgid "Undo availability cannot be verified while product outcomes are missing. Refresh this job or ask an administrator to check its saved records." msgstr "" -#: includes/free/class-free-admin.php:2638 +#: includes/free/class-free-admin.php:2673 msgid "Products" msgstr "" -#: includes/free/class-free-admin.php:2639 +#: includes/free/class-free-admin.php:2674 msgid "All products" msgstr "" -#: includes/free/class-free-admin.php:2639 +#: includes/free/class-free-admin.php:2674 msgid "Conflicts" msgstr "" -#: includes/free/class-free-admin.php:2639 +#: includes/free/class-free-admin.php:2674 msgid "Changes needing attention" msgstr "" -#: includes/free/class-free-admin.php:2639 +#: includes/free/class-free-admin.php:2674 msgid "Uncertain changes" msgstr "" -#: includes/free/class-free-admin.php:2639 +#: includes/free/class-free-admin.php:2674 msgid "Undo conflicts" msgstr "" -#: includes/free/class-free-admin.php:2639 +#: includes/free/class-free-admin.php:2674 msgid "Uncertain restorations" msgstr "" -#: includes/free/class-free-admin.php:2650 +#: includes/free/class-free-admin.php:2685 msgid "Item details are unavailable for this page." msgstr "" #. translators: %s: translated price-field name. -#: includes/free/class-free-admin.php:2658 +#: includes/free/class-free-admin.php:2693 #, php-format msgid "Expected and planned are %1$s values. Now shows the stored %1$s at page load, not the shopper price. Unavailable means the product or price could not be read." msgstr "" #. translators: %s: translated price-field name. -#: includes/free/class-free-admin.php:2659 +#: includes/free/class-free-admin.php:2694 #, php-format msgid "%s expected" msgstr "" #. translators: %s: translated price-field name. -#: includes/free/class-free-admin.php:2659 +#: includes/free/class-free-admin.php:2694 #, php-format msgid "%s now" msgstr "" #. translators: %s: translated price-field name. -#: includes/free/class-free-admin.php:2659 +#: includes/free/class-free-admin.php:2694 #, php-format msgid "%s planned" msgstr "" #. translators: %s: translated price-field name. -#: includes/free/class-free-admin.php:2659 +#: includes/free/class-free-admin.php:2694 msgctxt "merchant price workflow label" msgid "Apply" msgstr "" #. translators: %s: translated price-field name. -#: includes/free/class-free-admin.php:2660 +#: includes/free/class-free-admin.php:2695 #: includes/free/free-progress.js:81 msgid "No retained products on this page match this view." msgstr "" #. translators: %d: maximum selected products per new job. -#: includes/free/class-free-admin.php:2694 +#: includes/free/class-free-admin.php:2729 #, php-format msgid "This older job was already approved. The current WriteLeash limit of %1$d products applies to new work: new jobs above %1$d cannot be created or approved. You can still continue this existing job and restore prices that are safe to restore." msgstr "" -#: includes/free/class-free-admin.php:2698 +#: includes/free/class-free-admin.php:2733 msgid "Older large job" msgstr "" #. translators: %s: translated price-field name. -#: includes/free/class-free-admin.php:2704 +#: includes/free/class-free-admin.php:2739 #, php-format msgid "Undo restores eligible %s values changed by this job to their values before this job. Later price or sale-setting changes are left alone. Undo does not reverse: orders, completed sales or effects in other plugins and services." msgstr "" #. translators: %s: localized expiry date and time. -#: includes/free/class-free-admin.php:2708 +#: includes/free/class-free-admin.php:2743 #, php-format msgid "Undo window ends: %s" msgstr "" -#: includes/free/class-free-admin.php:2725 +#: includes/free/class-free-admin.php:2747 +msgid "Undo progress" +msgstr "" + +#: includes/free/class-free-admin.php:2762 msgid "Newer prices and sale settings are left alone." msgstr "" -#: includes/free/class-free-admin.php:2727 +#: includes/free/class-free-admin.php:2764 msgid "You do not have permission to restore prices." msgstr "" -#: includes/free/class-free-admin.php:2734 +#: includes/free/class-free-admin.php:2771 msgid "History" msgstr "" -#: includes/free/class-free-admin.php:2736 +#: includes/free/class-free-admin.php:2773 msgid "No price changes yet. Preview a change to start." msgstr "" @@ -2704,7 +2712,7 @@ msgid "Saved counters updated; the focused review link still shows the previous msgstr "" #: includes/free/free-progress.js:66 -msgid "Reload to read the current price" +msgid "Refresh to read the current price" msgstr "" #: includes/free/free-progress.js:72 @@ -2712,41 +2720,41 @@ msgstr "" msgid "Saved counters updated; the focused outcome still shows its previous saved details. Leave the focused outcome to refresh it." msgstr "" -#: includes/free/free-progress.js:107 +#: includes/free/free-progress.js:134 msgid "Saved counters updated; the focused support details still show the previous saved values. Leave the focused support details to refresh them." msgstr "" #. translators: 1: job status, 2: Apply summary, 3: Undo summary. -#: includes/free/free-progress.js:130 +#: includes/free/free-progress.js:157 #, js-format msgid "%1$s. %2$s Undo: %3$s." msgstr "" -#: includes/free/free-progress.js:142 -msgid "Session or permission expired. Reload and sign in to refresh saved progress." +#: includes/free/free-progress.js:169 +msgid "Session or permission expired. Sign in again, then use Refresh saved progress." msgstr "" -#: includes/free/free-progress.js:142 +#: includes/free/free-progress.js:169 msgid "Automatic updates stopped after repeated errors. Use Refresh saved progress to retry." msgstr "" -#: includes/free/free-progress.js:143 +#: includes/free/free-progress.js:170 msgid "Saved progress may be stale. Refresh failed; retrying shortly." msgstr "" -#: includes/free/free-progress.js:167 +#: includes/free/free-progress.js:194 msgid "Saved progress refreshed. Automatic updates active." msgstr "" -#: includes/free/free-progress.js:167 +#: includes/free/free-progress.js:194 msgid "Saved progress refreshed. Automatic updates stopped; refresh manually for later activity." msgstr "" -#: includes/free/free-progress.js:180 +#: includes/free/free-progress.js:207 msgid "Automatic updates paused while this page is hidden. Saved progress may be stale." msgstr "" -#: includes/free/free-progress.js:187 +#: includes/free/free-progress.js:214 msgid "Checking saved progress." msgstr "" From 8921707be92f98bb7f6759b6f934ffa4ec7eb093 Mon Sep 17 00:00:00 2001 From: orchestrator Date: Wed, 14 Oct 2026 10:12:45 +0700 Subject: [PATCH 05/19] ui/275 polish batch --- .../includes/free/class-free-admin.php | 49 ++++++++++++++----- 1 file changed, 38 insertions(+), 11 deletions(-) diff --git a/wordpress/writeleash/includes/free/class-free-admin.php b/wordpress/writeleash/includes/free/class-free-admin.php index 7b877499..0a0d8e09 100644 --- a/wordpress/writeleash/includes/free/class-free-admin.php +++ b/wordpress/writeleash/includes/free/class-free-admin.php @@ -2247,6 +2247,20 @@ private static function load_job_for_view( string $public_id ): ?array { return $job; } + /** + * Single approve-form renderer for the Preview view. The top summary + * panel and the bottom approval block call this same renderer, so both + * forms carry the same action, job field and plan nonce. Presentation + * only; no contract change. + */ + private static function render_approve_form( array $job ): void { + echo '
    '; + echo ''; + echo ''; + wp_nonce_field( self::ACTION_APPROVE . '_' . $job['plan_id'] ); + echo '

    '; + } + private static function render_preview_view( string $public_id, int $offset ): void { if ( ! self::jobs_installed() ) { echo '

    ' . esc_html( __( 'No preview is visible to your account for that identifier.', 'writeleash' ) ) . '

    '; @@ -2303,7 +2317,17 @@ private static function render_preview_view( string $public_id, int $offset ): v echo '

    ' . esc_html( sprintf( /* translators: 1: selected products, 2: translated outcome counts. */ _n( 'Selected %1$d product: %2$s.', 'Selected %1$d products: %2$s.', $summary['selected'], 'writeleash' ), $summary['selected'], implode( ' · ', array( self::count_copy( 'changing', $summary['changing'] ), self::count_copy( 'unchanged', $summary['unchanged'] ), self::count_copy( 'skipped', $summary['unsupported'] ) ) ) ) ) . '

    '; $extra_counts = self::preview_extra_counts( $data['items'] ); - echo '

    ' . esc_html( implode( ' · ', array( self::count_copy( 'large_increase', $extra_counts['large_increase'] ), self::count_copy( 'large_decrease', $extra_counts['large_decrease'] ), self::count_copy( 'zero_target', $extra_counts['zero_target'] ), self::count_copy( 'warning', $summary['warning_items'] ) ) ) . '.' ) . '

    '; + echo '

    ' . esc_html( implode( ' · ', array( self::count_copy( 'large_increase', $extra_counts['large_increase'] ), self::count_copy( 'large_decrease', $extra_counts['large_decrease'] ), self::count_copy( 'zero_target', $extra_counts['zero_target'] ), self::count_copy( 'warning', $summary['warning_items'] ) ) ) . '.' ) . '

    '; + // Top approve summary: same task description and compact counts as + // above, plus the shopper-price caveat and the same approve form as + // the bottom block, so summary and approval share the first viewport + // without JavaScript. The bottom block stays unchanged. + $approvable = ! $blocked && Job_State::PLANNED === $job['status'] && self::can_mutate(); + if ( $approvable ) { + echo '

    ' . esc_html__( 'Planned prices are not guaranteed shopper prices.', 'writeleash' ) . '

    '; + self::render_approve_form( $job ); + } + echo ''; if ( $blocked ) { echo '