fix: resolve all JS architecture violations (JS-005 through JS-009)
Fixed 89 violations across vendor, admin, and shared JavaScript files: JS-008 (raw fetch → apiClient): - Added postFormData() and getBlob() methods to api-client.js - Updated inventory.js, messages.js to use apiClient.postFormData() - Added noqa for file downloads that need response headers JS-009 (window.showToast → Utils.showToast): - Updated admin/messages.js, notifications.js, vendor/messages.js - Replaced alert() in customers.js JS-006 (async error handling): - Added try/catch to all async init() and reload() methods - Fixed vendor: billing, dashboard, login, messages, onboarding - Fixed shared: feature-store, upgrade-prompts - Fixed admin: all page components JS-005 (init guards): - Added initialization guards to prevent duplicate init() calls - Pattern: if (window._componentInitialized) return; 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
38
static/vendor/js/billing.js
vendored
38
static/vendor/js/billing.js
vendored
@@ -29,22 +29,30 @@ function vendorBilling() {
|
||||
|
||||
// Initialize
|
||||
async init() {
|
||||
// Check URL params for success/cancel
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
if (params.get('success') === 'true') {
|
||||
this.showSuccessMessage = true;
|
||||
window.history.replaceState({}, document.title, window.location.pathname);
|
||||
}
|
||||
if (params.get('cancelled') === 'true') {
|
||||
this.showCancelMessage = true;
|
||||
window.history.replaceState({}, document.title, window.location.pathname);
|
||||
}
|
||||
if (params.get('addon_success') === 'true') {
|
||||
this.showAddonSuccessMessage = true;
|
||||
window.history.replaceState({}, document.title, window.location.pathname);
|
||||
}
|
||||
// Guard against multiple initialization
|
||||
if (window._vendorBillingInitialized) return;
|
||||
window._vendorBillingInitialized = true;
|
||||
|
||||
await this.loadData();
|
||||
try {
|
||||
// Check URL params for success/cancel
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
if (params.get('success') === 'true') {
|
||||
this.showSuccessMessage = true;
|
||||
window.history.replaceState({}, document.title, window.location.pathname);
|
||||
}
|
||||
if (params.get('cancelled') === 'true') {
|
||||
this.showCancelMessage = true;
|
||||
window.history.replaceState({}, document.title, window.location.pathname);
|
||||
}
|
||||
if (params.get('addon_success') === 'true') {
|
||||
this.showAddonSuccessMessage = true;
|
||||
window.history.replaceState({}, document.title, window.location.pathname);
|
||||
}
|
||||
|
||||
await this.loadData();
|
||||
} catch (error) {
|
||||
billingLog.error('Failed to initialize billing page:', error);
|
||||
}
|
||||
},
|
||||
|
||||
async loadData() {
|
||||
|
||||
30
static/vendor/js/content-page-edit.js
vendored
30
static/vendor/js/content-page-edit.js
vendored
@@ -37,9 +37,6 @@ function vendorContentPageEditor(pageId) {
|
||||
|
||||
// Initialize
|
||||
async init() {
|
||||
contentPageEditLog.info('=== VENDOR CONTENT PAGE EDITOR INITIALIZING ===');
|
||||
contentPageEditLog.info('Page ID:', this.pageId);
|
||||
|
||||
// Prevent multiple initializations
|
||||
if (window._vendorContentPageEditInitialized) {
|
||||
contentPageEditLog.warn('Content page editor already initialized, skipping...');
|
||||
@@ -47,17 +44,24 @@ function vendorContentPageEditor(pageId) {
|
||||
}
|
||||
window._vendorContentPageEditInitialized = true;
|
||||
|
||||
if (this.pageId) {
|
||||
// Edit mode - load existing page
|
||||
contentPageEditLog.group('Loading page for editing');
|
||||
await this.loadPage();
|
||||
contentPageEditLog.groupEnd();
|
||||
} else {
|
||||
// Create mode - use default values
|
||||
contentPageEditLog.info('Create mode - using default form values');
|
||||
}
|
||||
try {
|
||||
contentPageEditLog.info('=== VENDOR CONTENT PAGE EDITOR INITIALIZING ===');
|
||||
contentPageEditLog.info('Page ID:', this.pageId);
|
||||
|
||||
contentPageEditLog.info('=== VENDOR CONTENT PAGE EDITOR INITIALIZATION COMPLETE ===');
|
||||
if (this.pageId) {
|
||||
// Edit mode - load existing page
|
||||
contentPageEditLog.group('Loading page for editing');
|
||||
await this.loadPage();
|
||||
contentPageEditLog.groupEnd();
|
||||
} else {
|
||||
// Create mode - use default values
|
||||
contentPageEditLog.info('Create mode - using default form values');
|
||||
}
|
||||
|
||||
contentPageEditLog.info('=== VENDOR CONTENT PAGE EDITOR INITIALIZATION COMPLETE ===');
|
||||
} catch (error) {
|
||||
contentPageEditLog.error('Failed to initialize content page editor:', error);
|
||||
}
|
||||
},
|
||||
|
||||
// Load existing page
|
||||
|
||||
22
static/vendor/js/dashboard.js
vendored
22
static/vendor/js/dashboard.js
vendored
@@ -38,13 +38,17 @@ function vendorDashboard() {
|
||||
}
|
||||
window._vendorDashboardInitialized = true;
|
||||
|
||||
// IMPORTANT: Call parent init first to set vendorCode from URL
|
||||
const parentInit = data().init;
|
||||
if (parentInit) {
|
||||
await parentInit.call(this);
|
||||
}
|
||||
try {
|
||||
// IMPORTANT: Call parent init first to set vendorCode from URL
|
||||
const parentInit = data().init;
|
||||
if (parentInit) {
|
||||
await parentInit.call(this);
|
||||
}
|
||||
|
||||
await this.loadDashboardData();
|
||||
await this.loadDashboardData();
|
||||
} catch (error) {
|
||||
vendorDashLog.error('Failed to initialize dashboard:', error);
|
||||
}
|
||||
},
|
||||
|
||||
async loadDashboardData() {
|
||||
@@ -94,7 +98,11 @@ function vendorDashboard() {
|
||||
},
|
||||
|
||||
async refresh() {
|
||||
await this.loadDashboardData();
|
||||
try {
|
||||
await this.loadDashboardData();
|
||||
} catch (error) {
|
||||
vendorDashLog.error('Failed to refresh dashboard:', error);
|
||||
}
|
||||
},
|
||||
|
||||
formatCurrency(amount) {
|
||||
|
||||
1
static/vendor/js/invoices.js
vendored
1
static/vendor/js/invoices.js
vendored
@@ -329,6 +329,7 @@ function vendorInvoices() {
|
||||
throw new Error('Not authenticated');
|
||||
}
|
||||
|
||||
// noqa: js-008 - File download needs response headers for filename
|
||||
const response = await fetch(`/api/v1/vendor/invoices/${invoice.id}/pdf`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
|
||||
1
static/vendor/js/letzshop.js
vendored
1
static/vendor/js/letzshop.js
vendored
@@ -438,6 +438,7 @@ function vendorLetzshop() {
|
||||
throw new Error('Not authenticated');
|
||||
}
|
||||
|
||||
// noqa: js-008 - File download needs response headers for filename
|
||||
const response = await fetch(`/api/v1/vendor/letzshop/export?${params}`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
|
||||
39
static/vendor/js/login.js
vendored
39
static/vendor/js/login.js
vendored
@@ -24,24 +24,33 @@ function vendorLogin() {
|
||||
dark: false,
|
||||
|
||||
async init() {
|
||||
vendorLoginLog.info('=== VENDOR LOGIN PAGE INITIALIZING ===');
|
||||
// Guard against multiple initialization
|
||||
if (window._vendorLoginInitialized) return;
|
||||
window._vendorLoginInitialized = true;
|
||||
|
||||
// Load theme
|
||||
const theme = localStorage.getItem('theme');
|
||||
if (theme === 'dark') {
|
||||
this.dark = true;
|
||||
}
|
||||
vendorLoginLog.debug('Dark mode:', this.dark);
|
||||
try {
|
||||
vendorLoginLog.info('=== VENDOR LOGIN PAGE INITIALIZING ===');
|
||||
|
||||
// Get vendor code from URL path
|
||||
const pathSegments = window.location.pathname.split('/').filter(Boolean);
|
||||
if (pathSegments[0] === 'vendor' && pathSegments[1]) {
|
||||
this.vendorCode = pathSegments[1];
|
||||
vendorLoginLog.debug('Vendor code from URL:', this.vendorCode);
|
||||
await this.loadVendor();
|
||||
// Load theme
|
||||
const theme = localStorage.getItem('theme');
|
||||
if (theme === 'dark') {
|
||||
this.dark = true;
|
||||
}
|
||||
vendorLoginLog.debug('Dark mode:', this.dark);
|
||||
|
||||
// Get vendor code from URL path
|
||||
const pathSegments = window.location.pathname.split('/').filter(Boolean);
|
||||
if (pathSegments[0] === 'vendor' && pathSegments[1]) {
|
||||
this.vendorCode = pathSegments[1];
|
||||
vendorLoginLog.debug('Vendor code from URL:', this.vendorCode);
|
||||
await this.loadVendor();
|
||||
}
|
||||
this.checked = true;
|
||||
vendorLoginLog.info('=== VENDOR LOGIN PAGE INITIALIZATION COMPLETE ===');
|
||||
} catch (error) {
|
||||
vendorLoginLog.error('Failed to initialize login page:', error);
|
||||
this.checked = true;
|
||||
}
|
||||
this.checked = true;
|
||||
vendorLoginLog.info('=== VENDOR LOGIN PAGE INITIALIZATION COMPLETE ===');
|
||||
},
|
||||
|
||||
async loadVendor() {
|
||||
|
||||
65
static/vendor/js/messages.js
vendored
65
static/vendor/js/messages.js
vendored
@@ -61,20 +61,28 @@ function vendorMessages(initialConversationId = null) {
|
||||
* Initialize component
|
||||
*/
|
||||
async init() {
|
||||
messagesLog.debug('Initializing vendor messages page');
|
||||
await Promise.all([
|
||||
this.loadConversations(),
|
||||
this.loadRecipients()
|
||||
]);
|
||||
// Guard against multiple initialization
|
||||
if (window._vendorMessagesInitialized) return;
|
||||
window._vendorMessagesInitialized = true;
|
||||
|
||||
if (this.selectedConversationId) {
|
||||
await this.loadConversation(this.selectedConversationId);
|
||||
try {
|
||||
messagesLog.debug('Initializing vendor messages page');
|
||||
await Promise.all([
|
||||
this.loadConversations(),
|
||||
this.loadRecipients()
|
||||
]);
|
||||
|
||||
if (this.selectedConversationId) {
|
||||
await this.loadConversation(this.selectedConversationId);
|
||||
}
|
||||
|
||||
// Start polling for new messages
|
||||
this.startPolling();
|
||||
} catch (error) {
|
||||
messagesLog.error('Failed to initialize messages page:', error);
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
|
||||
this.loading = false;
|
||||
|
||||
// Start polling for new messages
|
||||
this.startPolling();
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -128,7 +136,7 @@ function vendorMessages(initialConversationId = null) {
|
||||
messagesLog.debug(`Loaded ${this.conversations.length} conversations`);
|
||||
} catch (error) {
|
||||
messagesLog.error('Failed to load conversations:', error);
|
||||
window.showToast?.('Failed to load conversations', 'error');
|
||||
Utils.showToast('Failed to load conversations', 'error');
|
||||
} finally {
|
||||
this.loadingConversations = false;
|
||||
}
|
||||
@@ -177,7 +185,7 @@ function vendorMessages(initialConversationId = null) {
|
||||
this.scrollToBottom();
|
||||
} catch (error) {
|
||||
messagesLog.error('Failed to load conversation:', error);
|
||||
window.showToast?.('Failed to load conversation', 'error');
|
||||
Utils.showToast('Failed to load conversation', 'error');
|
||||
} finally {
|
||||
this.loadingMessages = false;
|
||||
}
|
||||
@@ -231,20 +239,7 @@ function vendorMessages(initialConversationId = null) {
|
||||
const formData = new FormData();
|
||||
formData.append('content', this.replyContent);
|
||||
|
||||
const response = await fetch(`/api/v1/vendor/messages/${this.selectedConversationId}/messages`, {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
headers: {
|
||||
'Authorization': `Bearer ${window.getAuthToken?.() || ''}`
|
||||
}
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const error = await response.json();
|
||||
throw new Error(error.detail || 'Failed to send message');
|
||||
}
|
||||
|
||||
const message = await response.json();
|
||||
const message = await apiClient.postFormData(`/vendor/messages/${this.selectedConversationId}/messages`, formData);
|
||||
|
||||
// Add to messages
|
||||
if (this.selectedConversation) {
|
||||
@@ -260,7 +255,7 @@ function vendorMessages(initialConversationId = null) {
|
||||
this.scrollToBottom();
|
||||
} catch (error) {
|
||||
messagesLog.error('Failed to send message:', error);
|
||||
window.showToast?.(error.message || 'Failed to send message', 'error');
|
||||
Utils.showToast(error.message || 'Failed to send message', 'error');
|
||||
} finally {
|
||||
this.sendingMessage = false;
|
||||
}
|
||||
@@ -286,10 +281,10 @@ function vendorMessages(initialConversationId = null) {
|
||||
const conv = this.conversations.find(c => c.id === this.selectedConversationId);
|
||||
if (conv) conv.is_closed = true;
|
||||
|
||||
window.showToast?.('Conversation closed', 'success');
|
||||
Utils.showToast('Conversation closed', 'success');
|
||||
} catch (error) {
|
||||
messagesLog.error('Failed to close conversation:', error);
|
||||
window.showToast?.('Failed to close conversation', 'error');
|
||||
Utils.showToast('Failed to close conversation', 'error');
|
||||
}
|
||||
},
|
||||
|
||||
@@ -307,10 +302,10 @@ function vendorMessages(initialConversationId = null) {
|
||||
const conv = this.conversations.find(c => c.id === this.selectedConversationId);
|
||||
if (conv) conv.is_closed = false;
|
||||
|
||||
window.showToast?.('Conversation reopened', 'success');
|
||||
Utils.showToast('Conversation reopened', 'success');
|
||||
} catch (error) {
|
||||
messagesLog.error('Failed to reopen conversation:', error);
|
||||
window.showToast?.('Failed to reopen conversation', 'error');
|
||||
Utils.showToast('Failed to reopen conversation', 'error');
|
||||
}
|
||||
},
|
||||
|
||||
@@ -354,10 +349,10 @@ function vendorMessages(initialConversationId = null) {
|
||||
await this.loadConversations();
|
||||
await this.selectConversation(response.id);
|
||||
|
||||
window.showToast?.('Conversation created', 'success');
|
||||
Utils.showToast('Conversation created', 'success');
|
||||
} catch (error) {
|
||||
messagesLog.error('Failed to create conversation:', error);
|
||||
window.showToast?.(error.message || 'Failed to create conversation', 'error');
|
||||
Utils.showToast(error.message || 'Failed to create conversation', 'error');
|
||||
} finally {
|
||||
this.creatingConversation = false;
|
||||
}
|
||||
|
||||
10
static/vendor/js/onboarding.js
vendored
10
static/vendor/js/onboarding.js
vendored
@@ -345,7 +345,15 @@ function vendorOnboarding(initialLang = 'en') {
|
||||
|
||||
// Initialize
|
||||
async init() {
|
||||
await this.loadStatus();
|
||||
// Guard against multiple initialization
|
||||
if (window._vendorOnboardingInitialized) return;
|
||||
window._vendorOnboardingInitialized = true;
|
||||
|
||||
try {
|
||||
await this.loadStatus();
|
||||
} catch (error) {
|
||||
onboardingLog.error('Failed to initialize onboarding:', error);
|
||||
}
|
||||
},
|
||||
|
||||
// Load current onboarding status
|
||||
|
||||
Reference in New Issue
Block a user