refactor(js): migrate JavaScript files to module directories
Move 47 JS files from static/{admin,vendor,shared}/js/ to their
respective module directories app/modules/*/static/*/js/:
- Orders: orders.js, order-detail.js
- Catalog: products.js (renamed from vendor-products.js), product-*.js
- Inventory: inventory.js (admin & vendor)
- Customers: customers.js, users.js, user-*.js
- Billing: billing-history.js, subscriptions.js, subscription-tiers.js,
billing.js, invoices.js, feature-store.js, upgrade-prompts.js
- Messaging: messages.js, notifications.js, email-templates.js
- Marketplace: marketplace*.js, letzshop*.js, onboarding.js
- Monitoring: monitoring.js, background-tasks.js, imports.js, logs.js
- Dev Tools: testing-*.js, code-quality-*.js
Update 39 templates to reference new module static paths using
url_for('{module}_static', path='...') pattern.
Files staying in static/ (platform core):
- admin: dashboard, login, platforms, vendors, companies, admin-users,
settings, components, init-alpine, module-config
- vendor: dashboard, login, profile, settings, team, media, init-alpine
- shared: api-client, utils, money, icons, log-config, vendor-selector,
media-picker
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
264
app/modules/messaging/static/vendor/js/email-templates.js
vendored
Normal file
264
app/modules/messaging/static/vendor/js/email-templates.js
vendored
Normal file
@@ -0,0 +1,264 @@
|
||||
/**
|
||||
* Vendor Email Templates Management Page
|
||||
*
|
||||
* Allows vendors to customize email templates sent to their customers.
|
||||
* Platform-only templates (billing, subscription) cannot be overridden.
|
||||
*/
|
||||
|
||||
const vendorEmailTemplatesLog = window.LogConfig?.loggers?.vendorEmailTemplates ||
|
||||
window.LogConfig?.createLogger?.('vendorEmailTemplates', false) ||
|
||||
{ info: () => {}, debug: () => {}, warn: () => {}, error: () => {} };
|
||||
|
||||
vendorEmailTemplatesLog.info('Loading...');
|
||||
|
||||
function vendorEmailTemplates() {
|
||||
vendorEmailTemplatesLog.info('vendorEmailTemplates() called');
|
||||
|
||||
return {
|
||||
// Inherit base layout state
|
||||
...data(),
|
||||
|
||||
// Set page identifier
|
||||
currentPage: 'email-templates',
|
||||
|
||||
// Loading states
|
||||
loading: true,
|
||||
error: '',
|
||||
saving: false,
|
||||
|
||||
// Data
|
||||
templates: [],
|
||||
supportedLanguages: ['en', 'fr', 'de', 'lb'],
|
||||
|
||||
// Edit Modal
|
||||
showEditModal: false,
|
||||
editingTemplate: null,
|
||||
editLanguage: 'en',
|
||||
loadingTemplate: false,
|
||||
templateSource: 'platform',
|
||||
editForm: {
|
||||
subject: '',
|
||||
body_html: '',
|
||||
body_text: ''
|
||||
},
|
||||
reverting: false,
|
||||
|
||||
// Preview Modal
|
||||
showPreviewModal: false,
|
||||
previewData: null,
|
||||
|
||||
// Test Email Modal
|
||||
showTestEmailModal: false,
|
||||
testEmailAddress: '',
|
||||
sendingTest: false,
|
||||
|
||||
// Lifecycle
|
||||
async init() {
|
||||
if (window._vendorEmailTemplatesInitialized) return;
|
||||
window._vendorEmailTemplatesInitialized = true;
|
||||
|
||||
vendorEmailTemplatesLog.info('Email templates init() called');
|
||||
|
||||
// Call parent init to set vendorCode and other base state
|
||||
const parentInit = data().init;
|
||||
if (parentInit) {
|
||||
await parentInit.call(this);
|
||||
}
|
||||
|
||||
await this.loadData();
|
||||
},
|
||||
|
||||
// Data Loading
|
||||
async loadData() {
|
||||
this.loading = true;
|
||||
this.error = '';
|
||||
|
||||
try {
|
||||
const response = await apiClient.get('/vendor/email-templates');
|
||||
this.templates = response.templates || [];
|
||||
this.supportedLanguages = response.supported_languages || ['en', 'fr', 'de', 'lb'];
|
||||
} catch (error) {
|
||||
vendorEmailTemplatesLog.error('Failed to load templates:', error);
|
||||
this.error = error.detail || 'Failed to load templates';
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
|
||||
// Category styling
|
||||
getCategoryClass(category) {
|
||||
const classes = {
|
||||
'AUTH': 'bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-400',
|
||||
'ORDERS': 'bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400',
|
||||
'BILLING': 'bg-orange-100 text-orange-700 dark:bg-orange-900/30 dark:text-orange-400',
|
||||
'SYSTEM': 'bg-purple-100 text-purple-700 dark:bg-purple-900/30 dark:text-purple-400',
|
||||
'MARKETING': 'bg-pink-100 text-pink-700 dark:bg-pink-900/30 dark:text-pink-400',
|
||||
'TEAM': 'bg-indigo-100 text-indigo-700 dark:bg-indigo-900/30 dark:text-indigo-400'
|
||||
};
|
||||
return classes[category] || 'bg-gray-100 text-gray-700 dark:bg-gray-700 dark:text-gray-300';
|
||||
},
|
||||
|
||||
// Edit Template
|
||||
async editTemplate(template) {
|
||||
this.editingTemplate = template;
|
||||
this.editLanguage = 'en';
|
||||
this.showEditModal = true;
|
||||
await this.loadTemplateLanguage();
|
||||
},
|
||||
|
||||
async loadTemplateLanguage() {
|
||||
if (!this.editingTemplate) return;
|
||||
|
||||
this.loadingTemplate = true;
|
||||
|
||||
try {
|
||||
const data = await apiClient.get(
|
||||
`/vendor/email-templates/${this.editingTemplate.code}/${this.editLanguage}`
|
||||
);
|
||||
|
||||
this.templateSource = data.source;
|
||||
this.editForm = {
|
||||
subject: data.subject || '',
|
||||
body_html: data.body_html || '',
|
||||
body_text: data.body_text || ''
|
||||
};
|
||||
} catch (error) {
|
||||
if (error.status === 404) {
|
||||
// No template for this language
|
||||
this.templateSource = 'none';
|
||||
this.editForm = {
|
||||
subject: '',
|
||||
body_html: '',
|
||||
body_text: ''
|
||||
};
|
||||
Utils.showToast(`No template available for ${this.editLanguage.toUpperCase()}`, 'info');
|
||||
} else {
|
||||
vendorEmailTemplatesLog.error('Failed to load template:', error);
|
||||
Utils.showToast('Failed to load template', 'error');
|
||||
}
|
||||
} finally {
|
||||
this.loadingTemplate = false;
|
||||
}
|
||||
},
|
||||
|
||||
closeEditModal() {
|
||||
this.showEditModal = false;
|
||||
this.editingTemplate = null;
|
||||
this.editForm = {
|
||||
subject: '',
|
||||
body_html: '',
|
||||
body_text: ''
|
||||
};
|
||||
},
|
||||
|
||||
async saveTemplate() {
|
||||
if (!this.editingTemplate) return;
|
||||
|
||||
this.saving = true;
|
||||
|
||||
try {
|
||||
await apiClient.put(
|
||||
`/vendor/email-templates/${this.editingTemplate.code}/${this.editLanguage}`,
|
||||
{
|
||||
subject: this.editForm.subject,
|
||||
body_html: this.editForm.body_html,
|
||||
body_text: this.editForm.body_text || null
|
||||
}
|
||||
);
|
||||
|
||||
Utils.showToast('Template saved successfully', 'success');
|
||||
this.templateSource = 'vendor_override';
|
||||
// Refresh list to show updated status
|
||||
await this.loadData();
|
||||
} catch (error) {
|
||||
vendorEmailTemplatesLog.error('Failed to save template:', error);
|
||||
Utils.showToast(error.detail || 'Failed to save template', 'error');
|
||||
} finally {
|
||||
this.saving = false;
|
||||
}
|
||||
},
|
||||
|
||||
async revertToDefault() {
|
||||
if (!this.editingTemplate) return;
|
||||
|
||||
if (!confirm('Are you sure you want to delete your customization and revert to the platform default?')) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.reverting = true;
|
||||
|
||||
try {
|
||||
await apiClient.delete(
|
||||
`/vendor/email-templates/${this.editingTemplate.code}/${this.editLanguage}`
|
||||
);
|
||||
|
||||
Utils.showToast('Reverted to platform default', 'success');
|
||||
// Reload the template to show platform version
|
||||
await this.loadTemplateLanguage();
|
||||
// Refresh list
|
||||
await this.loadData();
|
||||
} catch (error) {
|
||||
vendorEmailTemplatesLog.error('Failed to revert template:', error);
|
||||
Utils.showToast(error.detail || 'Failed to revert', 'error');
|
||||
} finally {
|
||||
this.reverting = false;
|
||||
}
|
||||
},
|
||||
|
||||
// Preview
|
||||
async previewTemplate() {
|
||||
if (!this.editingTemplate) return;
|
||||
|
||||
try {
|
||||
const data = await apiClient.post(
|
||||
`/vendor/email-templates/${this.editingTemplate.code}/preview`,
|
||||
{
|
||||
language: this.editLanguage,
|
||||
variables: {}
|
||||
}
|
||||
);
|
||||
|
||||
this.previewData = data;
|
||||
this.showPreviewModal = true;
|
||||
} catch (error) {
|
||||
vendorEmailTemplatesLog.error('Failed to preview template:', error);
|
||||
Utils.showToast('Failed to load preview', 'error');
|
||||
}
|
||||
},
|
||||
|
||||
// Test Email
|
||||
sendTestEmail() {
|
||||
this.showTestEmailModal = true;
|
||||
},
|
||||
|
||||
async confirmSendTestEmail() {
|
||||
if (!this.testEmailAddress || !this.editingTemplate) return;
|
||||
|
||||
this.sendingTest = true;
|
||||
|
||||
try {
|
||||
const result = await apiClient.post(
|
||||
`/vendor/email-templates/${this.editingTemplate.code}/test`,
|
||||
{
|
||||
to_email: this.testEmailAddress,
|
||||
language: this.editLanguage,
|
||||
variables: {}
|
||||
}
|
||||
);
|
||||
|
||||
if (result.success) {
|
||||
Utils.showToast(`Test email sent to ${this.testEmailAddress}`, 'success');
|
||||
this.showTestEmailModal = false;
|
||||
this.testEmailAddress = '';
|
||||
} else {
|
||||
Utils.showToast(result.message || 'Failed to send test email', 'error');
|
||||
}
|
||||
} catch (error) {
|
||||
vendorEmailTemplatesLog.error('Failed to send test email:', error);
|
||||
Utils.showToast('Failed to send test email', 'error');
|
||||
} finally {
|
||||
this.sendingTest = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user