fix: remove vendorCode from vendor API paths
Vendor API endpoints use JWT authentication, not URL path parameters. The vendorCode should only be used for page URLs (navigation), not API calls. Fixed API paths in 10 vendor JS files: - analytics.js, customers.js, inventory.js, notifications.js - order-detail.js, orders.js, products.js, profile.js - settings.js, team.js Added architecture rule JS-014 to prevent this pattern from recurring. Added validation check _check_vendor_api_paths to validate_architecture.py. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -131,6 +131,42 @@ javascript_rules:
|
|||||||
- "init-alpine.js"
|
- "init-alpine.js"
|
||||||
- "login.js"
|
- "login.js"
|
||||||
|
|
||||||
|
- id: "JS-014"
|
||||||
|
name: "Vendor API calls must not include vendorCode in path"
|
||||||
|
severity: "error"
|
||||||
|
description: |
|
||||||
|
Vendor API endpoints use JWT token authentication, NOT URL path parameters.
|
||||||
|
The vendor is identified from the JWT token via get_current_vendor_api dependency.
|
||||||
|
|
||||||
|
Do NOT include vendorCode in API paths for authenticated vendor endpoints.
|
||||||
|
|
||||||
|
WRONG (vendorCode in API path):
|
||||||
|
apiClient.get(`/vendor/${this.vendorCode}/orders`)
|
||||||
|
apiClient.post(`/vendor/${this.vendorCode}/products`, data)
|
||||||
|
|
||||||
|
RIGHT (no vendorCode, uses JWT):
|
||||||
|
apiClient.get(`/vendor/orders`)
|
||||||
|
apiClient.post(`/vendor/products`, data)
|
||||||
|
|
||||||
|
EXCEPTIONS (these endpoints DO use vendorCode in path):
|
||||||
|
- /vendor/{vendor_code} - Public vendor info (info.router)
|
||||||
|
- /vendor/{vendor_code}/content-pages/* - Content pages management
|
||||||
|
- Page URLs (not API calls) like window.location.href = `/vendor/${vendorCode}/...`
|
||||||
|
|
||||||
|
Why this matters:
|
||||||
|
- Including vendorCode causes 404 errors ("/vendor/wizamart/orders" not found)
|
||||||
|
- The JWT token already identifies the vendor
|
||||||
|
- Consistent with the API design pattern
|
||||||
|
pattern:
|
||||||
|
file_pattern: "static/vendor/js/**/*.js"
|
||||||
|
anti_patterns:
|
||||||
|
- "apiClient\\.(get|post|put|delete|patch)\\s*\\(\\s*`/vendor/\\$\\{this\\.vendorCode\\}/(orders|products|customers|inventory|analytics|dashboard|profile|settings|team|notifications|invoices|payments|media|marketplace|letzshop|billing|features|usage)"
|
||||||
|
exceptions:
|
||||||
|
- "init-alpine.js"
|
||||||
|
- "login.js"
|
||||||
|
- "content-pages.js"
|
||||||
|
- "content-page-edit.js"
|
||||||
|
|
||||||
- id: "JS-007"
|
- id: "JS-007"
|
||||||
name: "Set loading state before async operations"
|
name: "Set loading state before async operations"
|
||||||
severity: "warning"
|
severity: "warning"
|
||||||
|
|||||||
@@ -2865,6 +2865,9 @@ class ArchitectureValidator:
|
|||||||
# JS-013: Check that components overriding init() call parent init
|
# JS-013: Check that components overriding init() call parent init
|
||||||
self._check_parent_init_call(file_path, content, lines)
|
self._check_parent_init_call(file_path, content, lines)
|
||||||
|
|
||||||
|
# JS-014: Check that vendor API calls don't include vendorCode in path
|
||||||
|
self._check_vendor_api_paths(file_path, content, lines)
|
||||||
|
|
||||||
def _check_platform_settings_usage(
|
def _check_platform_settings_usage(
|
||||||
self, file_path: Path, content: str, lines: list[str]
|
self, file_path: Path, content: str, lines: list[str]
|
||||||
):
|
):
|
||||||
@@ -3051,6 +3054,55 @@ class ArchitectureValidator:
|
|||||||
)
|
)
|
||||||
break
|
break
|
||||||
|
|
||||||
|
def _check_vendor_api_paths(
|
||||||
|
self, file_path: Path, content: str, lines: list[str]
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
JS-014: Check that vendor API calls don't include vendorCode in path.
|
||||||
|
|
||||||
|
Vendor API endpoints use JWT token authentication, NOT URL path parameters.
|
||||||
|
The vendorCode is only used for page URLs (navigation), not API calls.
|
||||||
|
|
||||||
|
Incorrect: apiClient.get(`/vendor/${this.vendorCode}/orders`)
|
||||||
|
Correct: apiClient.get(`/vendor/orders`)
|
||||||
|
|
||||||
|
Exceptions (these DO use vendorCode in path):
|
||||||
|
- /vendor/{vendor_code} (public vendor info)
|
||||||
|
- /vendor/{vendor_code}/content-pages (public content)
|
||||||
|
"""
|
||||||
|
# Only check vendor JS files
|
||||||
|
if "/vendor/js/" not in str(file_path):
|
||||||
|
return
|
||||||
|
|
||||||
|
# Pattern to match apiClient calls with vendorCode in the path
|
||||||
|
# Matches patterns like:
|
||||||
|
# apiClient.get(`/vendor/${this.vendorCode}/
|
||||||
|
# apiClient.post(`/vendor/${vendorCode}/
|
||||||
|
# apiClient.put(`/vendor/${this.vendorCode}/
|
||||||
|
# apiClient.delete(`/vendor/${this.vendorCode}/
|
||||||
|
pattern = r"apiClient\.(get|post|put|delete|patch)\s*\(\s*[`'\"]\/vendor\/\$\{(?:this\.)?vendorCode\}\/"
|
||||||
|
|
||||||
|
for i, line in enumerate(lines, 1):
|
||||||
|
if re.search(pattern, line):
|
||||||
|
# Check if this is an allowed exception
|
||||||
|
# content-pages uses vendorCode for public content access
|
||||||
|
is_exception = (
|
||||||
|
"/content-pages" in line
|
||||||
|
or "content-page" in file_path.name
|
||||||
|
)
|
||||||
|
|
||||||
|
if not is_exception:
|
||||||
|
self._add_violation(
|
||||||
|
rule_id="JS-014",
|
||||||
|
rule_name="Vendor API calls must not include vendorCode in path",
|
||||||
|
severity=Severity.ERROR,
|
||||||
|
file_path=file_path,
|
||||||
|
line_number=i,
|
||||||
|
message="Vendor API endpoints use JWT authentication, not URL path parameters",
|
||||||
|
context=line.strip()[:100],
|
||||||
|
suggestion="Remove vendorCode from path: /vendor/orders instead of /vendor/${this.vendorCode}/orders",
|
||||||
|
)
|
||||||
|
|
||||||
def _validate_templates(self, target_path: Path):
|
def _validate_templates(self, target_path: Path):
|
||||||
"""Validate template patterns"""
|
"""Validate template patterns"""
|
||||||
print("📄 Validating templates...")
|
print("📄 Validating templates...")
|
||||||
|
|||||||
4
static/vendor/js/analytics.js
vendored
4
static/vendor/js/analytics.js
vendored
@@ -107,7 +107,7 @@ function vendorAnalytics() {
|
|||||||
*/
|
*/
|
||||||
async fetchAnalytics() {
|
async fetchAnalytics() {
|
||||||
try {
|
try {
|
||||||
const response = await apiClient.get(`/vendor/${this.vendorCode}/analytics?period=${this.period}`);
|
const response = await apiClient.get(`/vendor/analytics?period=${this.period}`);
|
||||||
return response;
|
return response;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Analytics might require feature access
|
// Analytics might require feature access
|
||||||
@@ -124,7 +124,7 @@ function vendorAnalytics() {
|
|||||||
*/
|
*/
|
||||||
async fetchStats() {
|
async fetchStats() {
|
||||||
try {
|
try {
|
||||||
const response = await apiClient.get(`/vendor/${this.vendorCode}/dashboard/stats`);
|
const response = await apiClient.get(`/vendor/dashboard/stats`);
|
||||||
return {
|
return {
|
||||||
total_products: response.catalog?.total_products || 0,
|
total_products: response.catalog?.total_products || 0,
|
||||||
active_products: response.catalog?.active_products || 0,
|
active_products: response.catalog?.active_products || 0,
|
||||||
|
|||||||
6
static/vendor/js/customers.js
vendored
6
static/vendor/js/customers.js
vendored
@@ -148,7 +148,7 @@ function vendorCustomers() {
|
|||||||
params.append('status', this.filters.status);
|
params.append('status', this.filters.status);
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await apiClient.get(`/vendor/${this.vendorCode}/customers?${params.toString()}`);
|
const response = await apiClient.get(`/vendor/customers?${params.toString()}`);
|
||||||
|
|
||||||
this.customers = response.customers || [];
|
this.customers = response.customers || [];
|
||||||
this.pagination.total = response.total || 0;
|
this.pagination.total = response.total || 0;
|
||||||
@@ -212,7 +212,7 @@ function vendorCustomers() {
|
|||||||
async viewCustomer(customer) {
|
async viewCustomer(customer) {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
try {
|
try {
|
||||||
const response = await apiClient.get(`/vendor/${this.vendorCode}/customers/${customer.id}`);
|
const response = await apiClient.get(`/vendor/customers/${customer.id}`);
|
||||||
this.selectedCustomer = response;
|
this.selectedCustomer = response;
|
||||||
this.showDetailModal = true;
|
this.showDetailModal = true;
|
||||||
vendorCustomersLog.info('Loaded customer details:', customer.id);
|
vendorCustomersLog.info('Loaded customer details:', customer.id);
|
||||||
@@ -230,7 +230,7 @@ function vendorCustomers() {
|
|||||||
async viewCustomerOrders(customer) {
|
async viewCustomerOrders(customer) {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
try {
|
try {
|
||||||
const response = await apiClient.get(`/vendor/${this.vendorCode}/customers/${customer.id}/orders`);
|
const response = await apiClient.get(`/vendor/customers/${customer.id}/orders`);
|
||||||
this.selectedCustomer = customer;
|
this.selectedCustomer = customer;
|
||||||
this.customerOrders = response.orders || [];
|
this.customerOrders = response.orders || [];
|
||||||
this.showOrdersModal = true;
|
this.showOrdersModal = true;
|
||||||
|
|||||||
8
static/vendor/js/inventory.js
vendored
8
static/vendor/js/inventory.js
vendored
@@ -182,7 +182,7 @@ function vendorInventory() {
|
|||||||
params.append('low_stock', this.filters.low_stock);
|
params.append('low_stock', this.filters.low_stock);
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await apiClient.get(`/vendor/${this.vendorCode}/inventory?${params.toString()}`);
|
const response = await apiClient.get(`/vendor/inventory?${params.toString()}`);
|
||||||
|
|
||||||
this.inventory = response.items || [];
|
this.inventory = response.items || [];
|
||||||
this.pagination.total = response.total || 0;
|
this.pagination.total = response.total || 0;
|
||||||
@@ -286,7 +286,7 @@ function vendorInventory() {
|
|||||||
|
|
||||||
this.saving = true;
|
this.saving = true;
|
||||||
try {
|
try {
|
||||||
await apiClient.post(`/vendor/${this.vendorCode}/inventory/adjust`, {
|
await apiClient.post(`/vendor/inventory/adjust`, {
|
||||||
product_id: this.selectedItem.product_id,
|
product_id: this.selectedItem.product_id,
|
||||||
location: this.selectedItem.location,
|
location: this.selectedItem.location,
|
||||||
quantity: this.adjustForm.quantity,
|
quantity: this.adjustForm.quantity,
|
||||||
@@ -317,7 +317,7 @@ function vendorInventory() {
|
|||||||
|
|
||||||
this.saving = true;
|
this.saving = true;
|
||||||
try {
|
try {
|
||||||
await apiClient.post(`/vendor/${this.vendorCode}/inventory/set`, {
|
await apiClient.post(`/vendor/inventory/set`, {
|
||||||
product_id: this.selectedItem.product_id,
|
product_id: this.selectedItem.product_id,
|
||||||
location: this.selectedItem.location,
|
location: this.selectedItem.location,
|
||||||
quantity: this.setForm.quantity
|
quantity: this.setForm.quantity
|
||||||
@@ -452,7 +452,7 @@ function vendorInventory() {
|
|||||||
const item = this.inventory.find(i => i.id === itemId);
|
const item = this.inventory.find(i => i.id === itemId);
|
||||||
if (item) {
|
if (item) {
|
||||||
try {
|
try {
|
||||||
await apiClient.post(`/vendor/${this.vendorCode}/inventory/adjust`, {
|
await apiClient.post(`/vendor/inventory/adjust`, {
|
||||||
product_id: item.product_id,
|
product_id: item.product_id,
|
||||||
location: item.location,
|
location: item.location,
|
||||||
quantity: this.bulkAdjustForm.quantity,
|
quantity: this.bulkAdjustForm.quantity,
|
||||||
|
|||||||
12
static/vendor/js/notifications.js
vendored
12
static/vendor/js/notifications.js
vendored
@@ -95,7 +95,7 @@ function vendorNotifications() {
|
|||||||
params.append('unread_only', 'true');
|
params.append('unread_only', 'true');
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await apiClient.get(`/vendor/${this.vendorCode}/notifications?${params}`);
|
const response = await apiClient.get(`/vendor/notifications?${params}`);
|
||||||
|
|
||||||
this.notifications = response.notifications || [];
|
this.notifications = response.notifications || [];
|
||||||
this.stats.total = response.total || 0;
|
this.stats.total = response.total || 0;
|
||||||
@@ -115,7 +115,7 @@ function vendorNotifications() {
|
|||||||
*/
|
*/
|
||||||
async markAsRead(notification) {
|
async markAsRead(notification) {
|
||||||
try {
|
try {
|
||||||
await apiClient.put(`/vendor/${this.vendorCode}/notifications/${notification.id}/read`);
|
await apiClient.put(`/vendor/notifications/${notification.id}/read`);
|
||||||
|
|
||||||
// Update local state
|
// Update local state
|
||||||
notification.is_read = true;
|
notification.is_read = true;
|
||||||
@@ -133,7 +133,7 @@ function vendorNotifications() {
|
|||||||
*/
|
*/
|
||||||
async markAllAsRead() {
|
async markAllAsRead() {
|
||||||
try {
|
try {
|
||||||
await apiClient.put(`/vendor/${this.vendorCode}/notifications/mark-all-read`);
|
await apiClient.put(`/vendor/notifications/mark-all-read`);
|
||||||
|
|
||||||
// Update local state
|
// Update local state
|
||||||
this.notifications.forEach(n => n.is_read = true);
|
this.notifications.forEach(n => n.is_read = true);
|
||||||
@@ -155,7 +155,7 @@ function vendorNotifications() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await apiClient.delete(`/vendor/${this.vendorCode}/notifications/${notificationId}`);
|
await apiClient.delete(`/vendor/notifications/${notificationId}`);
|
||||||
|
|
||||||
// Remove from local state
|
// Remove from local state
|
||||||
const wasUnread = this.notifications.find(n => n.id === notificationId && !n.is_read);
|
const wasUnread = this.notifications.find(n => n.id === notificationId && !n.is_read);
|
||||||
@@ -177,7 +177,7 @@ function vendorNotifications() {
|
|||||||
*/
|
*/
|
||||||
async openSettingsModal() {
|
async openSettingsModal() {
|
||||||
try {
|
try {
|
||||||
const response = await apiClient.get(`/vendor/${this.vendorCode}/notifications/settings`);
|
const response = await apiClient.get(`/vendor/notifications/settings`);
|
||||||
this.settingsForm = {
|
this.settingsForm = {
|
||||||
email_notifications: response.email_notifications !== false,
|
email_notifications: response.email_notifications !== false,
|
||||||
in_app_notifications: response.in_app_notifications !== false
|
in_app_notifications: response.in_app_notifications !== false
|
||||||
@@ -194,7 +194,7 @@ function vendorNotifications() {
|
|||||||
*/
|
*/
|
||||||
async saveSettings() {
|
async saveSettings() {
|
||||||
try {
|
try {
|
||||||
await apiClient.put(`/vendor/${this.vendorCode}/notifications/settings`, this.settingsForm);
|
await apiClient.put(`/vendor/notifications/settings`, this.settingsForm);
|
||||||
Utils.showToast('Notification settings saved', 'success');
|
Utils.showToast('Notification settings saved', 'success');
|
||||||
this.showSettingsModal = false;
|
this.showSettingsModal = false;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
16
static/vendor/js/order-detail.js
vendored
16
static/vendor/js/order-detail.js
vendored
@@ -94,7 +94,7 @@ function vendorOrderDetail() {
|
|||||||
try {
|
try {
|
||||||
// Load order details
|
// Load order details
|
||||||
const orderResponse = await apiClient.get(
|
const orderResponse = await apiClient.get(
|
||||||
`/vendor/${this.vendorCode}/orders/${this.orderId}`
|
`/vendor/orders/${this.orderId}`
|
||||||
);
|
);
|
||||||
this.order = orderResponse;
|
this.order = orderResponse;
|
||||||
this.newStatus = this.order.status;
|
this.newStatus = this.order.status;
|
||||||
@@ -121,7 +121,7 @@ function vendorOrderDetail() {
|
|||||||
async loadShipmentStatus() {
|
async loadShipmentStatus() {
|
||||||
try {
|
try {
|
||||||
const response = await apiClient.get(
|
const response = await apiClient.get(
|
||||||
`/vendor/${this.vendorCode}/orders/${this.orderId}/shipment-status`
|
`/vendor/orders/${this.orderId}/shipment-status`
|
||||||
);
|
);
|
||||||
this.shipmentStatus = response;
|
this.shipmentStatus = response;
|
||||||
orderDetailLog.info('Loaded shipment status:', response);
|
orderDetailLog.info('Loaded shipment status:', response);
|
||||||
@@ -138,7 +138,7 @@ function vendorOrderDetail() {
|
|||||||
try {
|
try {
|
||||||
// Search for invoices linked to this order
|
// Search for invoices linked to this order
|
||||||
const response = await apiClient.get(
|
const response = await apiClient.get(
|
||||||
`/vendor/${this.vendorCode}/invoices?order_id=${this.orderId}&limit=1`
|
`/vendor/invoices?order_id=${this.orderId}&limit=1`
|
||||||
);
|
);
|
||||||
if (response.invoices && response.invoices.length > 0) {
|
if (response.invoices && response.invoices.length > 0) {
|
||||||
this.invoice = response.invoices[0];
|
this.invoice = response.invoices[0];
|
||||||
@@ -223,7 +223,7 @@ function vendorOrderDetail() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await apiClient.put(
|
await apiClient.put(
|
||||||
`/vendor/${this.vendorCode}/orders/${this.orderId}/status`,
|
`/vendor/orders/${this.orderId}/status`,
|
||||||
payload
|
payload
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -255,7 +255,7 @@ function vendorOrderDetail() {
|
|||||||
this.saving = true;
|
this.saving = true;
|
||||||
try {
|
try {
|
||||||
await apiClient.post(
|
await apiClient.post(
|
||||||
`/vendor/${this.vendorCode}/orders/${this.orderId}/items/${itemId}/ship`,
|
`/vendor/orders/${this.orderId}/items/${itemId}/ship`,
|
||||||
{}
|
{}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -281,7 +281,7 @@ function vendorOrderDetail() {
|
|||||||
|
|
||||||
for (const item of unshippedItems) {
|
for (const item of unshippedItems) {
|
||||||
await apiClient.post(
|
await apiClient.post(
|
||||||
`/vendor/${this.vendorCode}/orders/${this.orderId}/items/${item.item_id}/ship`,
|
`/vendor/orders/${this.orderId}/items/${item.item_id}/ship`,
|
||||||
{}
|
{}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -294,7 +294,7 @@ function vendorOrderDetail() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await apiClient.put(
|
await apiClient.put(
|
||||||
`/vendor/${this.vendorCode}/orders/${this.orderId}/status`,
|
`/vendor/orders/${this.orderId}/status`,
|
||||||
payload
|
payload
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -319,7 +319,7 @@ function vendorOrderDetail() {
|
|||||||
this.creatingInvoice = true;
|
this.creatingInvoice = true;
|
||||||
try {
|
try {
|
||||||
const response = await apiClient.post(
|
const response = await apiClient.post(
|
||||||
`/vendor/${this.vendorCode}/invoices`,
|
`/vendor/invoices`,
|
||||||
{ order_id: this.orderId }
|
{ order_id: this.orderId }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
6
static/vendor/js/orders.js
vendored
6
static/vendor/js/orders.js
vendored
@@ -185,7 +185,7 @@ function vendorOrders() {
|
|||||||
params.append('date_to', this.filters.date_to);
|
params.append('date_to', this.filters.date_to);
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await apiClient.get(`/vendor/${this.vendorCode}/orders?${params.toString()}`);
|
const response = await apiClient.get(`/vendor/orders?${params.toString()}`);
|
||||||
|
|
||||||
this.orders = response.orders || [];
|
this.orders = response.orders || [];
|
||||||
this.pagination.total = response.total || 0;
|
this.pagination.total = response.total || 0;
|
||||||
@@ -273,7 +273,7 @@ function vendorOrders() {
|
|||||||
|
|
||||||
this.saving = true;
|
this.saving = true;
|
||||||
try {
|
try {
|
||||||
await apiClient.put(`/vendor/${this.vendorCode}/orders/${this.selectedOrder.id}/status`, {
|
await apiClient.put(`/vendor/orders/${this.selectedOrder.id}/status`, {
|
||||||
status: this.newStatus
|
status: this.newStatus
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -423,7 +423,7 @@ function vendorOrders() {
|
|||||||
let successCount = 0;
|
let successCount = 0;
|
||||||
for (const orderId of this.selectedOrders) {
|
for (const orderId of this.selectedOrders) {
|
||||||
try {
|
try {
|
||||||
await apiClient.put(`/vendor/${this.vendorCode}/orders/${orderId}/status`, {
|
await apiClient.put(`/vendor/orders/${orderId}/status`, {
|
||||||
status: this.bulkStatus
|
status: this.bulkStatus
|
||||||
});
|
});
|
||||||
successCount++;
|
successCount++;
|
||||||
|
|||||||
18
static/vendor/js/products.js
vendored
18
static/vendor/js/products.js
vendored
@@ -166,7 +166,7 @@ function vendorProducts() {
|
|||||||
params.append('is_featured', this.filters.featured === 'true');
|
params.append('is_featured', this.filters.featured === 'true');
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await apiClient.get(`/vendor/${this.vendorCode}/products?${params.toString()}`);
|
const response = await apiClient.get(`/vendor/products?${params.toString()}`);
|
||||||
|
|
||||||
this.products = response.products || [];
|
this.products = response.products || [];
|
||||||
this.pagination.total = response.total || 0;
|
this.pagination.total = response.total || 0;
|
||||||
@@ -227,7 +227,7 @@ function vendorProducts() {
|
|||||||
async toggleActive(product) {
|
async toggleActive(product) {
|
||||||
this.saving = true;
|
this.saving = true;
|
||||||
try {
|
try {
|
||||||
await apiClient.put(`/vendor/${this.vendorCode}/products/${product.id}/toggle-active`);
|
await apiClient.put(`/vendor/products/${product.id}/toggle-active`);
|
||||||
product.is_active = !product.is_active;
|
product.is_active = !product.is_active;
|
||||||
Utils.showToast(
|
Utils.showToast(
|
||||||
product.is_active ? 'Product activated' : 'Product deactivated',
|
product.is_active ? 'Product activated' : 'Product deactivated',
|
||||||
@@ -248,7 +248,7 @@ function vendorProducts() {
|
|||||||
async toggleFeatured(product) {
|
async toggleFeatured(product) {
|
||||||
this.saving = true;
|
this.saving = true;
|
||||||
try {
|
try {
|
||||||
await apiClient.put(`/vendor/${this.vendorCode}/products/${product.id}/toggle-featured`);
|
await apiClient.put(`/vendor/products/${product.id}/toggle-featured`);
|
||||||
product.is_featured = !product.is_featured;
|
product.is_featured = !product.is_featured;
|
||||||
Utils.showToast(
|
Utils.showToast(
|
||||||
product.is_featured ? 'Product marked as featured' : 'Product unmarked as featured',
|
product.is_featured ? 'Product marked as featured' : 'Product unmarked as featured',
|
||||||
@@ -287,7 +287,7 @@ function vendorProducts() {
|
|||||||
|
|
||||||
this.saving = true;
|
this.saving = true;
|
||||||
try {
|
try {
|
||||||
await apiClient.delete(`/vendor/${this.vendorCode}/products/${this.selectedProduct.id}`);
|
await apiClient.delete(`/vendor/products/${this.selectedProduct.id}`);
|
||||||
Utils.showToast('Product deleted successfully', 'success');
|
Utils.showToast('Product deleted successfully', 'success');
|
||||||
vendorProductsLog.info('Deleted product:', this.selectedProduct.id);
|
vendorProductsLog.info('Deleted product:', this.selectedProduct.id);
|
||||||
|
|
||||||
@@ -410,7 +410,7 @@ function vendorProducts() {
|
|||||||
for (const productId of this.selectedProducts) {
|
for (const productId of this.selectedProducts) {
|
||||||
const product = this.products.find(p => p.id === productId);
|
const product = this.products.find(p => p.id === productId);
|
||||||
if (product && !product.is_active) {
|
if (product && !product.is_active) {
|
||||||
await apiClient.put(`/vendor/${this.vendorCode}/products/${productId}/toggle-active`);
|
await apiClient.put(`/vendor/products/${productId}/toggle-active`);
|
||||||
product.is_active = true;
|
product.is_active = true;
|
||||||
successCount++;
|
successCount++;
|
||||||
}
|
}
|
||||||
@@ -438,7 +438,7 @@ function vendorProducts() {
|
|||||||
for (const productId of this.selectedProducts) {
|
for (const productId of this.selectedProducts) {
|
||||||
const product = this.products.find(p => p.id === productId);
|
const product = this.products.find(p => p.id === productId);
|
||||||
if (product && product.is_active) {
|
if (product && product.is_active) {
|
||||||
await apiClient.put(`/vendor/${this.vendorCode}/products/${productId}/toggle-active`);
|
await apiClient.put(`/vendor/products/${productId}/toggle-active`);
|
||||||
product.is_active = false;
|
product.is_active = false;
|
||||||
successCount++;
|
successCount++;
|
||||||
}
|
}
|
||||||
@@ -466,7 +466,7 @@ function vendorProducts() {
|
|||||||
for (const productId of this.selectedProducts) {
|
for (const productId of this.selectedProducts) {
|
||||||
const product = this.products.find(p => p.id === productId);
|
const product = this.products.find(p => p.id === productId);
|
||||||
if (product && !product.is_featured) {
|
if (product && !product.is_featured) {
|
||||||
await apiClient.put(`/vendor/${this.vendorCode}/products/${productId}/toggle-featured`);
|
await apiClient.put(`/vendor/products/${productId}/toggle-featured`);
|
||||||
product.is_featured = true;
|
product.is_featured = true;
|
||||||
successCount++;
|
successCount++;
|
||||||
}
|
}
|
||||||
@@ -494,7 +494,7 @@ function vendorProducts() {
|
|||||||
for (const productId of this.selectedProducts) {
|
for (const productId of this.selectedProducts) {
|
||||||
const product = this.products.find(p => p.id === productId);
|
const product = this.products.find(p => p.id === productId);
|
||||||
if (product && product.is_featured) {
|
if (product && product.is_featured) {
|
||||||
await apiClient.put(`/vendor/${this.vendorCode}/products/${productId}/toggle-featured`);
|
await apiClient.put(`/vendor/products/${productId}/toggle-featured`);
|
||||||
product.is_featured = false;
|
product.is_featured = false;
|
||||||
successCount++;
|
successCount++;
|
||||||
}
|
}
|
||||||
@@ -528,7 +528,7 @@ function vendorProducts() {
|
|||||||
try {
|
try {
|
||||||
let successCount = 0;
|
let successCount = 0;
|
||||||
for (const productId of this.selectedProducts) {
|
for (const productId of this.selectedProducts) {
|
||||||
await apiClient.delete(`/vendor/${this.vendorCode}/products/${productId}`);
|
await apiClient.delete(`/vendor/products/${productId}`);
|
||||||
successCount++;
|
successCount++;
|
||||||
}
|
}
|
||||||
Utils.showToast(`${successCount} product(s) deleted`, 'success');
|
Utils.showToast(`${successCount} product(s) deleted`, 'success');
|
||||||
|
|||||||
4
static/vendor/js/profile.js
vendored
4
static/vendor/js/profile.js
vendored
@@ -78,7 +78,7 @@ function vendorProfile() {
|
|||||||
this.error = '';
|
this.error = '';
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await apiClient.get(`/vendor/${this.vendorCode}/profile`);
|
const response = await apiClient.get(`/vendor/profile`);
|
||||||
|
|
||||||
this.profile = response;
|
this.profile = response;
|
||||||
this.form = {
|
this.form = {
|
||||||
@@ -159,7 +159,7 @@ function vendorProfile() {
|
|||||||
|
|
||||||
this.saving = true;
|
this.saving = true;
|
||||||
try {
|
try {
|
||||||
await apiClient.put(`/vendor/${this.vendorCode}/profile`, this.form);
|
await apiClient.put(`/vendor/profile`, this.form);
|
||||||
|
|
||||||
Utils.showToast('Profile updated successfully', 'success');
|
Utils.showToast('Profile updated successfully', 'success');
|
||||||
vendorProfileLog.info('Profile updated');
|
vendorProfileLog.info('Profile updated');
|
||||||
|
|||||||
4
static/vendor/js/settings.js
vendored
4
static/vendor/js/settings.js
vendored
@@ -92,7 +92,7 @@ function vendorSettings() {
|
|||||||
this.error = '';
|
this.error = '';
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await apiClient.get(`/vendor/${this.vendorCode}/settings`);
|
const response = await apiClient.get(`/vendor/settings`);
|
||||||
|
|
||||||
this.settings = response;
|
this.settings = response;
|
||||||
|
|
||||||
@@ -131,7 +131,7 @@ function vendorSettings() {
|
|||||||
async saveMarketplaceSettings() {
|
async saveMarketplaceSettings() {
|
||||||
this.saving = true;
|
this.saving = true;
|
||||||
try {
|
try {
|
||||||
await apiClient.put(`/vendor/${this.vendorCode}/settings/marketplace`, this.marketplaceForm);
|
await apiClient.put(`/vendor/settings/marketplace`, this.marketplaceForm);
|
||||||
|
|
||||||
Utils.showToast('Marketplace settings saved', 'success');
|
Utils.showToast('Marketplace settings saved', 'success');
|
||||||
vendorSettingsLog.info('Marketplace settings updated');
|
vendorSettingsLog.info('Marketplace settings updated');
|
||||||
|
|||||||
8
static/vendor/js/team.js
vendored
8
static/vendor/js/team.js
vendored
@@ -100,7 +100,7 @@ function vendorTeam() {
|
|||||||
this.error = '';
|
this.error = '';
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await apiClient.get(`/vendor/${this.vendorCode}/team/members?include_inactive=true`);
|
const response = await apiClient.get(`/vendor/team/members?include_inactive=true`);
|
||||||
|
|
||||||
this.members = response.members || [];
|
this.members = response.members || [];
|
||||||
this.stats = {
|
this.stats = {
|
||||||
@@ -123,7 +123,7 @@ function vendorTeam() {
|
|||||||
*/
|
*/
|
||||||
async loadRoles() {
|
async loadRoles() {
|
||||||
try {
|
try {
|
||||||
const response = await apiClient.get(`/vendor/${this.vendorCode}/team/roles`);
|
const response = await apiClient.get(`/vendor/team/roles`);
|
||||||
this.roles = response.roles || [];
|
this.roles = response.roles || [];
|
||||||
vendorTeamLog.info('Loaded roles:', this.roles.length);
|
vendorTeamLog.info('Loaded roles:', this.roles.length);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -155,7 +155,7 @@ function vendorTeam() {
|
|||||||
|
|
||||||
this.saving = true;
|
this.saving = true;
|
||||||
try {
|
try {
|
||||||
await apiClient.post(`/vendor/${this.vendorCode}/team/invite`, this.inviteForm);
|
await apiClient.post(`/vendor/team/invite`, this.inviteForm);
|
||||||
|
|
||||||
Utils.showToast('Invitation sent successfully', 'success');
|
Utils.showToast('Invitation sent successfully', 'success');
|
||||||
vendorTeamLog.info('Invitation sent to:', this.inviteForm.email);
|
vendorTeamLog.info('Invitation sent to:', this.inviteForm.email);
|
||||||
@@ -225,7 +225,7 @@ function vendorTeam() {
|
|||||||
|
|
||||||
this.saving = true;
|
this.saving = true;
|
||||||
try {
|
try {
|
||||||
await apiClient.delete(`/vendor/${this.vendorCode}/team/members/${this.selectedMember.user_id}`);
|
await apiClient.delete(`/vendor/team/members/${this.selectedMember.user_id}`);
|
||||||
|
|
||||||
Utils.showToast('Team member removed', 'success');
|
Utils.showToast('Team member removed', 'success');
|
||||||
vendorTeamLog.info('Removed team member:', this.selectedMember.user_id);
|
vendorTeamLog.info('Removed team member:', this.selectedMember.user_id);
|
||||||
|
|||||||
Reference in New Issue
Block a user