refactor(cms): migrate store theme UI from tenancy to CMS module
Move store theme admin pages, templates, and JS from tenancy module
to CMS module where the data layer (model, service, API, schemas)
already lives. Eliminates split ownership.
Moved:
- Route handlers: GET /store-themes, GET /stores/{code}/theme
- Templates: store-theme.html, store-themes.html
- JS: store-theme.js, store-themes.js
- Updated static references: tenancy_static → cms_static
Deleted old tenancy files (no remaining references).
Menu item in CMS definition already pointed to correct route.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
171
app/modules/cms/static/admin/js/store-themes.js
Normal file
171
app/modules/cms/static/admin/js/store-themes.js
Normal file
@@ -0,0 +1,171 @@
|
||||
// noqa: js-006 - async init pattern is safe, loadData has try/catch
|
||||
// static/admin/js/store-themes.js
|
||||
/**
|
||||
* Admin store themes selection page
|
||||
*/
|
||||
|
||||
// ✅ Use centralized logger
|
||||
const storeThemesLog = window.LogConfig.loggers.storeTheme;
|
||||
|
||||
storeThemesLog.info('Loading...');
|
||||
|
||||
function adminStoreThemes() {
|
||||
storeThemesLog.debug('adminStoreThemes() called');
|
||||
|
||||
return {
|
||||
// Inherit base layout state
|
||||
...data(),
|
||||
|
||||
// Set page identifier
|
||||
currentPage: 'store-theme',
|
||||
|
||||
// State
|
||||
loading: false,
|
||||
error: '',
|
||||
stores: [],
|
||||
selectedStoreCode: '',
|
||||
|
||||
// Selected store for filter (Tom Select)
|
||||
selectedStore: null,
|
||||
storeSelector: null,
|
||||
|
||||
// Search/filter
|
||||
searchQuery: '',
|
||||
|
||||
async init() {
|
||||
// Guard against multiple initialization
|
||||
if (window._adminStoreThemesInitialized) {
|
||||
return;
|
||||
}
|
||||
window._adminStoreThemesInitialized = true;
|
||||
|
||||
storeThemesLog.info('Store Themes init() called');
|
||||
|
||||
// Initialize store selector (Tom Select)
|
||||
this.$nextTick(() => {
|
||||
this.initStoreSelector();
|
||||
});
|
||||
|
||||
// Check localStorage for saved store
|
||||
const savedStoreId = localStorage.getItem('store_themes_selected_store_id');
|
||||
if (savedStoreId) {
|
||||
storeThemesLog.info('Restoring saved store:', savedStoreId);
|
||||
await this.loadStores();
|
||||
// Restore store after stores are loaded
|
||||
setTimeout(async () => {
|
||||
await this.restoreSavedStore(parseInt(savedStoreId));
|
||||
}, 200);
|
||||
} else {
|
||||
await this.loadStores();
|
||||
}
|
||||
|
||||
storeThemesLog.info('Store Themes initialization complete');
|
||||
},
|
||||
|
||||
/**
|
||||
* Restore saved store from localStorage
|
||||
*/
|
||||
async restoreSavedStore(storeId) {
|
||||
try {
|
||||
const store = await apiClient.get(`/admin/stores/${storeId}`);
|
||||
if (this.storeSelector && store) {
|
||||
// Use the store selector's setValue method
|
||||
this.storeSelector.setValue(store.id, store);
|
||||
|
||||
// Set the filter state
|
||||
this.selectedStore = store;
|
||||
|
||||
storeThemesLog.info('Restored store:', store.name);
|
||||
}
|
||||
} catch (error) {
|
||||
storeThemesLog.warn('Failed to restore saved store, clearing localStorage:', error);
|
||||
localStorage.removeItem('store_themes_selected_store_id');
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Initialize store selector with Tom Select
|
||||
*/
|
||||
initStoreSelector() {
|
||||
if (!this.$refs.storeSelect) {
|
||||
storeThemesLog.warn('Store select element not found');
|
||||
return;
|
||||
}
|
||||
|
||||
this.storeSelector = initStoreSelector(this.$refs.storeSelect, {
|
||||
placeholder: 'Search store...',
|
||||
onSelect: (store) => {
|
||||
storeThemesLog.info('Store selected:', store);
|
||||
this.selectedStore = store;
|
||||
// Save to localStorage
|
||||
localStorage.setItem('store_themes_selected_store_id', store.id.toString());
|
||||
},
|
||||
onClear: () => {
|
||||
storeThemesLog.info('Store filter cleared');
|
||||
this.selectedStore = null;
|
||||
// Clear from localStorage
|
||||
localStorage.removeItem('store_themes_selected_store_id');
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Clear store filter
|
||||
*/
|
||||
clearStoreFilter() {
|
||||
if (this.storeSelector) {
|
||||
this.storeSelector.clear();
|
||||
}
|
||||
this.selectedStore = null;
|
||||
// Clear from localStorage
|
||||
localStorage.removeItem('store_themes_selected_store_id');
|
||||
},
|
||||
|
||||
async loadStores() {
|
||||
this.loading = true;
|
||||
this.error = '';
|
||||
|
||||
try {
|
||||
const response = await apiClient.get('/admin/stores?limit=1000');
|
||||
this.stores = response.stores || [];
|
||||
storeThemesLog.debug('Loaded stores:', this.stores.length);
|
||||
} catch (error) {
|
||||
storeThemesLog.error('Failed to load stores:', error);
|
||||
this.error = error.message || 'Failed to load stores';
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Computed: Filtered stores based on search and selected store
|
||||
*/
|
||||
get filteredStores() {
|
||||
let filtered = this.stores;
|
||||
|
||||
// If a store is selected via Tom Select, show only that store
|
||||
if (this.selectedStore) {
|
||||
filtered = this.stores.filter(v => v.id === this.selectedStore.id);
|
||||
}
|
||||
// Otherwise filter by search query
|
||||
else if (this.searchQuery) {
|
||||
const query = this.searchQuery.toLowerCase();
|
||||
filtered = this.stores.filter(v =>
|
||||
v.name.toLowerCase().includes(query) ||
|
||||
(v.store_code && v.store_code.toLowerCase().includes(query))
|
||||
);
|
||||
}
|
||||
|
||||
return filtered;
|
||||
},
|
||||
|
||||
navigateToTheme() {
|
||||
if (!this.selectedStoreCode) {
|
||||
return;
|
||||
}
|
||||
window.location.href = `/admin/stores/${this.selectedStoreCode}/theme`;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
storeThemesLog.info('Module loaded');
|
||||
Reference in New Issue
Block a user