Files
orion/static/shared/js/api-client.js

377 lines
9.2 KiB
JavaScript

// static/js/shared/api-client.js
/**
* API Client for Multi-Tenant Ecommerce Platform
*
* Provides utilities for:
* - Making authenticated API calls
* - Token management
* - Error handling
* - Request/response interceptors
*/
const API_BASE_URL = '/api/v1';
/**
* API Client Class
*/
class APIClient {
constructor(baseURL = API_BASE_URL) {
this.baseURL = baseURL;
}
/**
* Get stored authentication token
*/
getToken() {
return localStorage.getItem('admin_token') || localStorage.getItem('vendor_token');
}
/**
* Get default headers with authentication
*/
getHeaders(additionalHeaders = {}) {
const headers = {
'Content-Type': 'application/json',
...additionalHeaders
};
const token = this.getToken();
if (token) {
headers['Authorization'] = `Bearer ${token}`;
}
return headers;
}
/**
* Make API request
*/
async request(endpoint, options = {}) {
const url = `${this.baseURL}${endpoint}`;
const config = {
...options,
headers: this.getHeaders(options.headers)
};
try {
const response = await fetch(url, config);
// Handle 401 Unauthorized
if (response.status === 401) {
this.handleUnauthorized();
throw new Error('Unauthorized - please login again');
}
// Parse response
const data = await response.json();
// Handle non-OK responses
if (!response.ok) {
throw new Error(data.detail || data.message || 'Request failed');
}
return data;
} catch (error) {
console.error('API request failed:', error);
throw error;
}
}
/**
* GET request
*/
async get(endpoint, params = {}) {
const queryString = new URLSearchParams(params).toString();
const url = queryString ? `${endpoint}?${queryString}` : endpoint;
return this.request(url, {
method: 'GET'
});
}
/**
* POST request
*/
async post(endpoint, data = {}) {
return this.request(endpoint, {
method: 'POST',
body: JSON.stringify(data)
});
}
/**
* PUT request
*/
async put(endpoint, data = {}) {
return this.request(endpoint, {
method: 'PUT',
body: JSON.stringify(data)
});
}
/**
* DELETE request
*/
async delete(endpoint) {
return this.request(endpoint, {
method: 'DELETE'
});
}
/**
* Handle unauthorized access
*/
handleUnauthorized() {
localStorage.removeItem('admin_token');
localStorage.removeItem('admin_user');
localStorage.removeItem('vendor_token');
localStorage.removeItem('vendor_user');
// Redirect to appropriate login page
if (window.location.pathname.includes('/admin/')) {
window.location.href = '/static/admin/login.html';
} else if (window.location.pathname.includes('/vendor/')) {
window.location.href = '/static/vendor/login.html';
}
}
}
// Create global API client instance
const apiClient = new APIClient();
/**
* Authentication helpers
*/
const Auth = {
/**
* Check if user is authenticated
*/
isAuthenticated() {
const token = localStorage.getItem('admin_token') || localStorage.getItem('vendor_token');
return !!token;
},
/**
* Get current user
*/
getCurrentUser() {
const userStr = localStorage.getItem('admin_user') || localStorage.getItem('vendor_user');
if (!userStr) return null;
try {
return JSON.parse(userStr);
} catch (e) {
return null;
}
},
/**
* Check if user is admin
*/
isAdmin() {
const user = this.getCurrentUser();
return user && user.role === 'admin';
},
/**
* Login
*/
async login(username, password) {
const response = await apiClient.post('/auth/login', {
username,
password
});
// Store token and user
if (response.user.role === 'admin') {
localStorage.setItem('admin_token', response.access_token);
localStorage.setItem('admin_user', JSON.stringify(response.user));
} else {
localStorage.setItem('vendor_token', response.access_token);
localStorage.setItem('vendor_user', JSON.stringify(response.user));
}
return response;
},
/**
* Logout
*/
logout() {
localStorage.removeItem('admin_token');
localStorage.removeItem('admin_user');
localStorage.removeItem('vendor_token');
localStorage.removeItem('vendor_user');
}
};
/**
* Utility functions
*/
const Utils = {
/**
* Format date
*/
formatDate(dateString) {
if (!dateString) return '-';
const date = new Date(dateString);
return date.toLocaleDateString('en-GB', {
day: '2-digit',
month: 'short',
year: 'numeric'
});
},
/**
* Format datetime
*/
formatDateTime(dateString) {
if (!dateString) return '-';
const date = new Date(dateString);
return date.toLocaleString('en-GB', {
day: '2-digit',
month: 'short',
year: 'numeric',
hour: '2-digit',
minute: '2-digit'
});
},
/**
* Format currency
*/
formatCurrency(amount, currency = 'EUR') {
if (amount === null || amount === undefined) return '-';
return new Intl.NumberFormat('en-GB', {
style: 'currency',
currency: currency
}).format(amount);
},
/**
* Debounce function
*/
debounce(func, wait) {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
},
/**
* Show toast notification
*/
showToast(message, type = 'info', duration = 3000) {
// Create toast element
const toast = document.createElement('div');
toast.className = `toast toast-${type}`;
toast.textContent = message;
// Style
toast.style.cssText = `
position: fixed;
top: 20px;
right: 20px;
padding: 16px 24px;
background: ${type === 'success' ? '#4caf50' : type === 'error' ? '#f44336' : '#2196f3'};
color: white;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
z-index: 10000;
animation: slideIn 0.3s ease;
max-width: 400px;
`;
// Add to page
document.body.appendChild(toast);
// Remove after duration
setTimeout(() => {
toast.style.animation = 'slideOut 0.3s ease';
setTimeout(() => toast.remove(), 300);
}, duration);
},
/**
* Confirm dialog
*/
async confirm(message, title = 'Confirm') {
return window.confirm(`${title}\n\n${message}`);
}
};
// Add animation styles
const style = document.createElement('style');
style.textContent = `
@keyframes slideIn {
from {
transform: translateX(400px);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
@keyframes slideOut {
from {
transform: translateX(0);
opacity: 1;
}
to {
transform: translateX(400px);
opacity: 0;
}
}
`;
document.head.appendChild(style);
// Export for use in other scripts
if (typeof module !== 'undefined' && module.exports) {
module.exports = { APIClient, apiClient, Auth, Utils };
}
// Table scroll detection helper
function initTableScrollDetection() {
const observer = new MutationObserver(() => {
const tables = document.querySelectorAll('.table-responsive');
tables.forEach(table => {
if (!table.hasAttribute('data-scroll-initialized')) {
table.setAttribute('data-scroll-initialized', 'true');
table.addEventListener('scroll', function() {
if (this.scrollLeft > 0) {
this.classList.add('is-scrolled');
} else {
this.classList.remove('is-scrolled');
}
});
// Check initial state
if (table.scrollLeft > 0) {
table.classList.add('is-scrolled');
}
}
});
});
observer.observe(document.body, {
childList: true,
subtree: true
});
}
// Initialize when DOM is ready
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initTableScrollDetection);
} else {
initTableScrollDetection();
}