/home/altere25/.trash/wp-to-sanity-companion.1/assets/js
Edit: /home/altere25/.trash/wp-to-sanity-companion.1/assets/js/admin.js (16906B)
/**
* Altered Digital Migration — admin JS
*
* - ADProgress class: real-time polling of Worker /v2/migrations.
* - updateSlug gated to plugin settings pages only (no global post-editor
* side effect — the classic editor already has its own slug UI).
* - wpToSanitySyncPost kept for the meta-box "Sync to Sanity" button.
* - wpToSanityRegenerateSecret kept.
*
* Dead v1 code removed: pollMigrationStatus, updateMigrationMetaBox,
* wpToSanityTrackProgress, showNotification, export/status polling (the
* vestigial migration_queue table no longer exists).
*/
(function($) {
'use strict';
var W = window.wpToSanity = window.wpToSanity || {};
// -----------------------------------------------------------------
// Regenerate webhook secret (settings page)
// -----------------------------------------------------------------
W.wpToSanityRegenerateSecret = function() {
if (!confirm('Regenerate the webhook secret? You will need to update your Worker configuration.')) {
return;
}
var newSecret = '';
var chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
for (var i = 0; i < 32; i++) {
newSecret += chars.charAt(Math.floor(Math.random() * chars.length));
}
// Both v1 + v2 option names accept the value; the active input has
// name="ad_settings[webhook_secret]" on v2.
$('input[name="ad_settings[webhook_secret]"], input[name="wp_to_sanity_settings[webhook_secret]"]').val(newSecret);
};
// -----------------------------------------------------------------
// Sync-now button (meta box on post-edit screen)
// -----------------------------------------------------------------
W.wpToSanitySyncPost = function(postId) {
$.ajax({
url: W.restUrl + 'export',
method: 'POST',
headers: { 'X-WP-Nonce': W.restNonce },
data: { post_id: postId },
success: function(resp) {
if (resp && resp.success) {
alert('Sync triggered. Status will update shortly.');
} else {
alert('Sync failed: ' + (resp && resp.error ? resp.error : 'unknown error'));
}
},
error: function() { alert('Sync request failed.'); }
});
};
// -----------------------------------------------------------------
// Slug helper — gated to plugin settings pages only.
// The classic editor already manages its own slug; running this on
// every post_title input was a v1 side-effect that overwrote user
// slugs. Now it only runs when an AD settings field with
// name="post_name" exists (rare; left for back-compat).
// -----------------------------------------------------------------
function maybeUpdateSlug() {
if (!W || !W.restUrl) { return; }
// Only on a plugin settings page that exposes a post_name input.
if (!$('input[name="post_name"]').length) { return; }
if (!$('body').hasClass('wp-to-sanity_page_wp-to-sanity-settings') &&
!$('body').hasClass('toplevel_page_wp-to-sanity')) {
return;
}
var title = $('input[name="post_title"]').val();
if (!title) { return; }
var slug = title.toLowerCase()
.replace(/[^a-z0-9\s-]/g, '')
.replace(/\s+/g, '-')
.replace(/-+/g, '-')
.trim();
$('input[name="post_name"]').val(slug);
}
$(document).on('input', 'input[name="post_title"]', maybeUpdateSlug);
// -----------------------------------------------------------------
// ADProgress — real-time migration progress
// -----------------------------------------------------------------
function ADProgress() {
this.pollMs = 5000;
this.timer = null;
}
ADProgress.prototype.start = function() {
var self = this;
this.tick();
this.timer = setInterval(function() { self.tick(); }, this.pollMs);
};
ADProgress.prototype.stop = function() {
if (this.timer) { clearInterval(this.timer); this.timer = null; }
};
ADProgress.prototype.tick = function() {
if (!W || !W.restUrl) { return; }
var self = this;
$.ajax({
url: W.restUrl + 'progress',
method: 'GET',
headers: { 'X-WP-Nonce': W.restNonce },
success: function(resp) {
if (resp && resp.success && resp.data) {
self.render(resp.data);
}
},
error: function() {
// Worker unreachable — leave last-known state in place.
}
});
};
ADProgress.prototype.render = function(data) {
var stats = data.stats || {};
var total = parseInt(stats.total || 0, 10);
var success = parseInt(stats.success || 0, 10);
var pending = parseInt(stats.pending || 0, 10);
var failed = parseInt(stats.failed || 0, 10);
$('#ad-stat-total').text(total);
$('#ad-stat-success').text(success);
$('#ad-stat-pending').text(pending);
$('#ad-stat-failed').text(failed);
var pct = total > 0 ? Math.round((success / total) * 100) : 0;
$('#ad-progress-fill').css('width', pct + '%');
$('#ad-progress-pct').text(pct + '%');
var recent = data.recent || [];
var $tbody = $('#ad-progress-table tbody');
if (!$tbody.length) { return; }
if (!recent.length) {
$tbody.html('
| No migrations yet. |
');
return;
}
var rows = '';
for (var i = 0; i < recent.length; i++) {
var r = recent[i];
rows += '
' +
'| ' + (r.post_id || '') + ' | ' +
'' + (r.action || '') + ' | ' +
'' + (r.status || '') + ' | ' +
'' + (r.sanity_id || '') + ' | ' +
'' + (r.updated_at || r.created_at || '') + ' | ' +
'
';
}
$tbody.html(rows);
};
$(function() {
if ($('#ad-progress-table').length) {
new ADProgress().start();
}
});
// Expose for any future dashboard widget.
W.ADProgress = ADProgress;
// -----------------------------------------------------------------
// Wizard (onboarding-v2) — Phase B shell only.
// Phase C wires the real flows; here we just advance panels.
// -----------------------------------------------------------------
$(function() {
if (!$('.ad-wizard-steps').length) { return; }
function showPanel(n) {
$('.ad-wizard-panel').attr('hidden', true);
$('.ad-wizard-panel[data-panel="' + n + '"]').removeAttr('hidden');
$('.ad-wizard-steps li').removeClass('active');
$('.ad-wizard-steps li[data-step="' + n + '"]').addClass('active');
}
// Login submit → Supabase auth via AD_Auth::login().
$('#ad-login-form').on('submit', function(e) {
e.preventDefault();
var $form = $(this);
var $status = $('#ad-login-status').removeAttr('hidden')
.text('Signing in…').css('color', '#555');
$.ajax({
url: W.restUrl + 'auth/login',
method: 'POST',
headers: { 'X-WP-Nonce': W.restNonce },
data: {
email: $form.find('input[name="email"]').val(),
password: $form.find('input[name="password"]').val()
},
success: function(resp) {
if (resp && resp.success) {
showPanel(2);
} else {
var msg = (resp && resp.data && resp.data.error) ? resp.data.error
: (resp && resp.error) ? resp.error : 'Login failed.';
$status.text(msg).css('color', '#d63638');
}
},
error: function(xhr) {
var msg = 'Login request failed';
try {
var body = JSON.parse(xhr.responseText);
if (body && body.data && body.data.error) { msg = body.data.error; }
else if (body && body.message) { msg = body.message; }
else if (body && body.error) { msg = body.error; }
} catch (ex) {
if (xhr.status) { msg += ' (HTTP ' + xhr.status + ')'; }
if (xhr.responseText) { msg += ': ' + xhr.responseText.substring(0, 200); }
}
$status.text(msg).css('color', '#d63638');
}
});
});
$('#ad-analyze-trigger').on('click', function() {
var $btn = $(this).prop('disabled', true).text('Analyzing…');
showPanel(3);
$('#ad-analyze-stage').text('Gathering site data…');
// Step 1: POST /analyze → returns analysis_id + analysis synchronously.
$.ajax({
url: W.restUrl + 'analyze',
method: 'POST',
headers: { 'X-WP-Nonce': W.restNonce },
success: function(resp) {
if (!resp || !resp.success) {
var msg = (resp && resp.data && resp.data.error) ? resp.data.error
: (resp && resp.error) ? resp.error : 'Analysis failed.';
$('#ad-analyze-stage').text('Error: ' + msg).css('color', '#d63638');
$btn.prop('disabled', false).text('Analyze my site');
return;
}
var analysisId = resp.data.analysis_id;
var analysis = resp.data.analysis || {};
$('#ad-analyze-fill').css('width', '60%');
$('#ad-analyze-stage').text('Generating quote…');
// Step 2: POST /quote with the analysis_id.
$.ajax({
url: W.restUrl + 'quote',
method: 'POST',
headers: { 'X-WP-Nonce': W.restNonce },
data: { analysis_id: analysisId, add_ons: [] },
success: function(qresp) {
if (!qresp || !qresp.success) {
var msg = (qresp && qresp.data && qresp.data.error) ? qresp.data.error
: (qresp && qresp.error) ? qresp.error : 'Quote failed.';
$('#ad-analyze-stage').text('Error: ' + msg).css('color', '#d63638');
$btn.prop('disabled', false).text('Analyze my site');
return;
}
$('#ad-analyze-fill').css('width', '100%');
$('#ad-analyze-stage').text('Done!');
renderQuote(qresp.data);
showPanel(4);
},
error: function(xhr) {
var msg = 'Quote request failed';
try {
var body = JSON.parse(xhr.responseText);
if (body && body.data && body.data.error) { msg = body.data.error; }
else if (body && body.error) { msg = body.error; }
} catch (ex) {
if (xhr.status) { msg += ' (HTTP ' + xhr.status + ')'; }
}
$('#ad-analyze-stage').text('Error: ' + msg).css('color', '#d63638');
$btn.prop('disabled', false).text('Analyze my site');
}
});
},
error: function(xhr) {
var msg = 'Analysis request failed';
try {
var body = JSON.parse(xhr.responseText);
if (body && body.data && body.data.error) { msg = body.data.error; }
else if (body && body.error) { msg = body.error; }
} catch (ex) {
if (xhr.status) { msg += ' (HTTP ' + xhr.status + ')'; }
if (xhr.responseText) { msg += ': ' + xhr.responseText.substring(0, 200); }
}
$('#ad-analyze-stage').text('Error: ' + msg).css('color', '#d63638');
$btn.prop('disabled', false).text('Analyze my site');
}
});
});
function centsToUsd(cents) {
return '$' + (cents / 100).toFixed(2);
}
function renderQuote(data) {
var quote = data.quote || {};
var html = '
';
html += '| Item | Qty | Price | Total |
';
(quote.line_items || []).forEach(function(item) {
html += '| ' + (item.label || item.key) + ' | ' +
item.quantity + ' | ' + centsToUsd(item.unit_price_cents) +
' | ' + centsToUsd(item.total_cents) + ' |
';
});
html += '
';
if (quote.add_ons && quote.add_ons.length) {
html += '
Add-ons
';
quote.add_ons.forEach(function(a) {
html += '- ' + (a.label || a.key) + ' — ' + centsToUsd(a.total_cents) + '
';
});
html += '
';
}
html += '
Subtotal: ' + centsToUsd(quote.subtotal_cents) + '
';
html += '
Total: ' + centsToUsd(quote.total_cents) + '
';
html += '
Upfront (50%): ' + centsToUsd(quote.upfront_50_cents) +
' · Final (50%): ' + centsToUsd(quote.final_50_cents) + '
';
if (quote.estimated_days) {
html += '
Estimated time: ' + quote.estimated_days + ' days
';
}
if (data.deterministic) {
html += '
Note: using estimated pricing (AI pricing model unavailable).
';
}
var quoteId = data.quote_id || '';
html += '
';
$('#ad-quote-body').html(html);
// Wire checkout button → /checkout → redirect to Stripe.
$('#ad-checkout-trigger').on('click', function() {
var $cbtn = $(this).prop('disabled', true).text('Redirecting…');
$.ajax({
url: W.restUrl + 'checkout',
method: 'POST',
headers: { 'X-WP-Nonce': W.restNonce },
data: { quote_id: quoteId },
success: function(cresp) {
if (cresp && cresp.success && cresp.data && cresp.data.checkout_url) {
window.location.href = cresp.data.checkout_url;
} else {
var msg = (cresp && cresp.data && cresp.data.error) ? cresp.data.error
: 'Checkout failed.';
$cbtn.prop('disabled', false).text('Continue to payment');
alert(msg);
}
},
error: function(xhr) {
var msg = 'Checkout failed';
try {
var body = JSON.parse(xhr.responseText);
if (body && body.data && body.data.error) { msg = body.data.error; }
else if (body && body.error) { msg = body.error; }
} catch (ex) {
if (xhr.status) { msg += ' (HTTP ' + xhr.status + ')'; }
}
$cbtn.prop('disabled', false).text('Continue to payment');
alert(msg);
}
});
});
}
});
})(jQuery);