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>
172 lines
5.6 KiB
JavaScript
172 lines
5.6 KiB
JavaScript
// noqa: js-006 - async init pattern is safe, loadData has try/catch
|
|
// static/admin/js/vendor-themes.js
|
|
/**
|
|
* Admin vendor themes selection page
|
|
*/
|
|
|
|
// ✅ Use centralized logger
|
|
const vendorThemesLog = window.LogConfig.loggers.vendorTheme;
|
|
|
|
vendorThemesLog.info('Loading...');
|
|
|
|
function adminVendorThemes() {
|
|
vendorThemesLog.debug('adminVendorThemes() called');
|
|
|
|
return {
|
|
// Inherit base layout state
|
|
...data(),
|
|
|
|
// Set page identifier
|
|
currentPage: 'vendor-theme',
|
|
|
|
// State
|
|
loading: false,
|
|
error: '',
|
|
vendors: [],
|
|
selectedVendorCode: '',
|
|
|
|
// Selected vendor for filter (Tom Select)
|
|
selectedVendor: null,
|
|
vendorSelector: null,
|
|
|
|
// Search/filter
|
|
searchQuery: '',
|
|
|
|
async init() {
|
|
// Guard against multiple initialization
|
|
if (window._adminVendorThemesInitialized) {
|
|
return;
|
|
}
|
|
window._adminVendorThemesInitialized = true;
|
|
|
|
vendorThemesLog.info('Vendor Themes init() called');
|
|
|
|
// Initialize vendor selector (Tom Select)
|
|
this.$nextTick(() => {
|
|
this.initVendorSelector();
|
|
});
|
|
|
|
// Check localStorage for saved vendor
|
|
const savedVendorId = localStorage.getItem('vendor_themes_selected_vendor_id');
|
|
if (savedVendorId) {
|
|
vendorThemesLog.info('Restoring saved vendor:', savedVendorId);
|
|
await this.loadVendors();
|
|
// Restore vendor after vendors are loaded
|
|
setTimeout(async () => {
|
|
await this.restoreSavedVendor(parseInt(savedVendorId));
|
|
}, 200);
|
|
} else {
|
|
await this.loadVendors();
|
|
}
|
|
|
|
vendorThemesLog.info('Vendor Themes initialization complete');
|
|
},
|
|
|
|
/**
|
|
* Restore saved vendor from localStorage
|
|
*/
|
|
async restoreSavedVendor(vendorId) {
|
|
try {
|
|
const vendor = await apiClient.get(`/admin/vendors/${vendorId}`);
|
|
if (this.vendorSelector && vendor) {
|
|
// Use the vendor selector's setValue method
|
|
this.vendorSelector.setValue(vendor.id, vendor);
|
|
|
|
// Set the filter state
|
|
this.selectedVendor = vendor;
|
|
|
|
vendorThemesLog.info('Restored vendor:', vendor.name);
|
|
}
|
|
} catch (error) {
|
|
vendorThemesLog.warn('Failed to restore saved vendor, clearing localStorage:', error);
|
|
localStorage.removeItem('vendor_themes_selected_vendor_id');
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Initialize vendor selector with Tom Select
|
|
*/
|
|
initVendorSelector() {
|
|
if (!this.$refs.vendorSelect) {
|
|
vendorThemesLog.warn('Vendor select element not found');
|
|
return;
|
|
}
|
|
|
|
this.vendorSelector = initVendorSelector(this.$refs.vendorSelect, {
|
|
placeholder: 'Search vendor...',
|
|
onSelect: (vendor) => {
|
|
vendorThemesLog.info('Vendor selected:', vendor);
|
|
this.selectedVendor = vendor;
|
|
// Save to localStorage
|
|
localStorage.setItem('vendor_themes_selected_vendor_id', vendor.id.toString());
|
|
},
|
|
onClear: () => {
|
|
vendorThemesLog.info('Vendor filter cleared');
|
|
this.selectedVendor = null;
|
|
// Clear from localStorage
|
|
localStorage.removeItem('vendor_themes_selected_vendor_id');
|
|
}
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Clear vendor filter
|
|
*/
|
|
clearVendorFilter() {
|
|
if (this.vendorSelector) {
|
|
this.vendorSelector.clear();
|
|
}
|
|
this.selectedVendor = null;
|
|
// Clear from localStorage
|
|
localStorage.removeItem('vendor_themes_selected_vendor_id');
|
|
},
|
|
|
|
async loadVendors() {
|
|
this.loading = true;
|
|
this.error = '';
|
|
|
|
try {
|
|
const response = await apiClient.get('/admin/vendors?limit=1000');
|
|
this.vendors = response.vendors || [];
|
|
vendorThemesLog.debug('Loaded vendors:', this.vendors.length);
|
|
} catch (error) {
|
|
vendorThemesLog.error('Failed to load vendors:', error);
|
|
this.error = error.message || 'Failed to load vendors';
|
|
} finally {
|
|
this.loading = false;
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Computed: Filtered vendors based on search and selected vendor
|
|
*/
|
|
get filteredVendors() {
|
|
let filtered = this.vendors;
|
|
|
|
// If a vendor is selected via Tom Select, show only that vendor
|
|
if (this.selectedVendor) {
|
|
filtered = this.vendors.filter(v => v.id === this.selectedVendor.id);
|
|
}
|
|
// Otherwise filter by search query
|
|
else if (this.searchQuery) {
|
|
const query = this.searchQuery.toLowerCase();
|
|
filtered = this.vendors.filter(v =>
|
|
v.name.toLowerCase().includes(query) ||
|
|
(v.vendor_code && v.vendor_code.toLowerCase().includes(query))
|
|
);
|
|
}
|
|
|
|
return filtered;
|
|
},
|
|
|
|
navigateToTheme() {
|
|
if (!this.selectedVendorCode) {
|
|
return;
|
|
}
|
|
window.location.href = `/admin/vendors/${this.selectedVendorCode}/theme`;
|
|
}
|
|
};
|
|
}
|
|
|
|
vendorThemesLog.info('Module loaded');
|