Files
orion/app/modules/tenancy/static/admin/js/store-detail.js
Samir Boulahtit 0b37274140 fix(subscriptions): fix subscription UI and API after store→merchant migration
Store detail page now shows all platform subscriptions instead of always
"No Subscription Found". Subscriptions listing page renamed from Store
to Merchant throughout (template, JS, menu, i18n) with Platform column
added. Tiers API supports platform_id filtering.

Merchant detail page no longer hardcodes 'oms' platform — loads all
platforms, shows subscription cards per platform with labels, and the
Create Subscription modal includes a platform selector with
platform-filtered tiers. Create button always accessible in Quick Actions.

Edit modal on /admin/subscriptions loads tiers from API filtered by
platform instead of hardcoded options, sends tier_code (not tier) to
match PATCH schema, and shows platform context.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-10 19:17:51 +01:00

192 lines
7.2 KiB
JavaScript

// noqa: js-006 - async init pattern is safe, loadData has try/catch
// static/admin/js/store-detail.js
// ✅ Use centralized logger - ONE LINE!
// Create custom logger for store detail
const detailLog = window.LogConfig.createLogger('STORE-DETAIL');
function adminStoreDetail() {
return {
// Inherit base layout functionality from init-alpine.js
...data(),
// Store detail page specific state
currentPage: 'store-detail',
store: null,
subscriptions: [],
loading: false,
error: null,
storeCode: null,
// Initialize
async init() {
// Load i18n translations
await I18n.loadModule('tenancy');
detailLog.info('=== STORE DETAIL PAGE INITIALIZING ===');
// Prevent multiple initializations
if (window._storeDetailInitialized) {
detailLog.warn('Store detail page already initialized, skipping...');
return;
}
window._storeDetailInitialized = true;
// Get store code from URL
const path = window.location.pathname;
const match = path.match(/\/admin\/stores\/([^\/]+)$/);
if (match) {
this.storeCode = match[1];
detailLog.info('Viewing store:', this.storeCode);
await this.loadStore();
// Load subscription after store is loaded
if (this.store?.id) {
await this.loadSubscriptions();
}
} else {
detailLog.error('No store code in URL');
this.error = 'Invalid store URL';
Utils.showToast(I18n.t('tenancy.messages.invalid_store_url'), 'error');
}
detailLog.info('=== STORE DETAIL PAGE INITIALIZATION COMPLETE ===');
},
// Load store data
async loadStore() {
detailLog.info('Loading store details...');
this.loading = true;
this.error = null;
try {
const url = `/admin/stores/${this.storeCode}`;
window.LogConfig.logApiCall('GET', url, null, 'request');
const startTime = performance.now();
const response = await apiClient.get(url);
const duration = performance.now() - startTime;
window.LogConfig.logApiCall('GET', url, response, 'response');
window.LogConfig.logPerformance('Load Store Details', duration);
this.store = response;
detailLog.info(`Store loaded in ${duration}ms`, {
store_code: this.store.store_code,
name: this.store.name,
is_verified: this.store.is_verified,
is_active: this.store.is_active
});
detailLog.debug('Full store data:', this.store);
} catch (error) {
window.LogConfig.logError(error, 'Load Store Details');
this.error = error.message || 'Failed to load store details';
Utils.showToast(I18n.t('tenancy.messages.failed_to_load_store_details'), 'error');
} finally {
this.loading = false;
}
},
// Format date (matches dashboard pattern)
formatDate(dateString) {
if (!dateString) {
detailLog.debug('formatDate called with empty dateString');
return '-';
}
const formatted = Utils.formatDate(dateString);
detailLog.debug(`Date formatted: ${dateString} -> ${formatted}`);
return formatted;
},
// Load subscriptions data for this store via convenience endpoint
async loadSubscriptions() {
if (!this.store?.id) {
detailLog.warn('Cannot load subscriptions: no store ID');
return;
}
detailLog.info('Loading subscriptions for store:', this.store.id);
try {
const url = `/admin/subscriptions/store/${this.store.id}`;
window.LogConfig.logApiCall('GET', url, null, 'request');
const response = await apiClient.get(url);
window.LogConfig.logApiCall('GET', url, response, 'response');
this.subscriptions = response.subscriptions || [];
detailLog.info('Subscriptions loaded:', {
count: this.subscriptions.length,
platforms: this.subscriptions.map(e => e.platform_name)
});
} catch (error) {
// 404 means no subscription exists - that's OK
if (error.status === 404) {
detailLog.info('No subscriptions found for store');
this.subscriptions = [];
} else {
detailLog.warn('Failed to load subscriptions:', error.message);
}
}
},
// Get usage bar color based on percentage
getUsageBarColor(current, limit) {
if (!limit || limit === 0) return 'bg-blue-500';
const percent = (current / limit) * 100;
if (percent >= 90) return 'bg-red-500';
if (percent >= 75) return 'bg-yellow-500';
return 'bg-green-500';
},
// Delete store
async deleteStore() {
detailLog.info('Delete store requested:', this.storeCode);
if (!confirm(`Are you sure you want to delete store "${this.store.name}"?\n\nThis action cannot be undone and will delete:\n- All products\n- All orders\n- All customers\n- All team members`)) {
detailLog.info('Delete cancelled by user');
return;
}
// Second confirmation for safety
if (!confirm(`FINAL CONFIRMATION\n\nType the store code to confirm: ${this.store.store_code}\n\nAre you absolutely sure?`)) {
detailLog.info('Delete cancelled by user (second confirmation)');
return;
}
try {
const url = `/admin/stores/${this.storeCode}?confirm=true`;
window.LogConfig.logApiCall('DELETE', url, null, 'request');
detailLog.info('Deleting store:', this.storeCode);
await apiClient.delete(url);
window.LogConfig.logApiCall('DELETE', url, null, 'response');
Utils.showToast(I18n.t('tenancy.messages.store_deleted_successfully'), 'success');
detailLog.info('Store deleted successfully');
// Redirect to stores list
setTimeout(() => window.location.href = '/admin/stores', 1500);
} catch (error) {
window.LogConfig.logError(error, 'Delete Store');
Utils.showToast(error.message || 'Failed to delete store', 'error');
}
},
// Refresh store data
async refresh() {
detailLog.info('=== STORE REFRESH TRIGGERED ===');
await this.loadStore();
Utils.showToast(I18n.t('tenancy.messages.store_details_refreshed'), 'success');
detailLog.info('=== STORE REFRESH COMPLETE ===');
}
};
}
detailLog.info('Store detail module loaded');