migrating vendor frontend to new architecture
This commit is contained in:
6
static/shared/img/about.txt
Normal file
6
static/shared/img/about.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
This favicon was generated using the following font:
|
||||
|
||||
- Font Title: Kotta One
|
||||
- Font Author: undefined
|
||||
- Font Source: https://fonts.gstatic.com/s/kottaone/v20/S6u_w41LXzPc_jlfNWqPHA3s5dwt7w.ttf
|
||||
- Font License: undefined)
|
||||
BIN
static/shared/img/android-chrome-192x192.png
Normal file
BIN
static/shared/img/android-chrome-192x192.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 8.4 KiB |
BIN
static/shared/img/android-chrome-512x512.png
Normal file
BIN
static/shared/img/android-chrome-512x512.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 23 KiB |
BIN
static/shared/img/apple-touch-icon.png
Normal file
BIN
static/shared/img/apple-touch-icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.3 KiB |
BIN
static/shared/img/favicon-16x16.png
Normal file
BIN
static/shared/img/favicon-16x16.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 501 B |
BIN
static/shared/img/favicon-32x32.png
Normal file
BIN
static/shared/img/favicon-32x32.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.0 KiB |
BIN
static/shared/img/favicon.ico
Normal file
BIN
static/shared/img/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
1
static/shared/img/site.webmanifest
Normal file
1
static/shared/img/site.webmanifest
Normal file
@@ -0,0 +1 @@
|
||||
{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"}
|
||||
81
static/vendor/js/dashboard.js
vendored
Normal file
81
static/vendor/js/dashboard.js
vendored
Normal file
@@ -0,0 +1,81 @@
|
||||
// app/static/vendor/js/dashboard.js
|
||||
/**
|
||||
* Vendor dashboard page logic
|
||||
*/
|
||||
|
||||
function vendorDashboard() {
|
||||
return {
|
||||
loading: false,
|
||||
error: '',
|
||||
stats: {
|
||||
products_count: 0,
|
||||
orders_count: 0,
|
||||
customers_count: 0,
|
||||
revenue: 0
|
||||
},
|
||||
recentOrders: [],
|
||||
recentProducts: [],
|
||||
|
||||
async init() {
|
||||
await this.loadDashboardData();
|
||||
},
|
||||
|
||||
async loadDashboardData() {
|
||||
this.loading = true;
|
||||
this.error = '';
|
||||
|
||||
try {
|
||||
// Load stats
|
||||
const statsResponse = await apiClient.get(
|
||||
`/api/v1/vendors/${this.vendorCode}/stats`
|
||||
);
|
||||
this.stats = statsResponse;
|
||||
|
||||
// Load recent orders
|
||||
const ordersResponse = await apiClient.get(
|
||||
`/api/v1/vendors/${this.vendorCode}/orders?limit=5&sort=created_at:desc`
|
||||
);
|
||||
this.recentOrders = ordersResponse.items || [];
|
||||
|
||||
// Load recent products
|
||||
const productsResponse = await apiClient.get(
|
||||
`/api/v1/vendors/${this.vendorCode}/products?limit=5&sort=created_at:desc`
|
||||
);
|
||||
this.recentProducts = productsResponse.items || [];
|
||||
|
||||
logInfo('Dashboard data loaded', {
|
||||
stats: this.stats,
|
||||
orders: this.recentOrders.length,
|
||||
products: this.recentProducts.length
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
logError('Failed to load dashboard data', error);
|
||||
this.error = 'Failed to load dashboard data. Please try refreshing the page.';
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
|
||||
async refresh() {
|
||||
await this.loadDashboardData();
|
||||
},
|
||||
|
||||
formatCurrency(amount) {
|
||||
return new Intl.NumberFormat('en-US', {
|
||||
style: 'currency',
|
||||
currency: 'EUR'
|
||||
}).format(amount || 0);
|
||||
},
|
||||
|
||||
formatDate(dateString) {
|
||||
if (!dateString) return '';
|
||||
const date = new Date(dateString);
|
||||
return date.toLocaleDateString('en-US', {
|
||||
year: 'numeric',
|
||||
month: 'short',
|
||||
day: 'numeric'
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
104
static/vendor/js/init-alpine.js
vendored
Normal file
104
static/vendor/js/init-alpine.js
vendored
Normal file
@@ -0,0 +1,104 @@
|
||||
// app/static/vendor/js/init-alpine.js
|
||||
/**
|
||||
* Alpine.js initialization for vendor pages
|
||||
* Provides common data and methods for all vendor pages
|
||||
*/
|
||||
|
||||
function data() {
|
||||
return {
|
||||
dark: false,
|
||||
isSideMenuOpen: false,
|
||||
isNotificationsMenuOpen: false,
|
||||
isProfileMenuOpen: false,
|
||||
currentPage: '',
|
||||
currentUser: {},
|
||||
vendor: null,
|
||||
vendorCode: null,
|
||||
|
||||
init() {
|
||||
// Set current page from URL
|
||||
const path = window.location.pathname;
|
||||
const segments = path.split('/').filter(Boolean);
|
||||
this.currentPage = segments[segments.length - 1] || 'dashboard';
|
||||
|
||||
// Get vendor code from URL
|
||||
if (segments[0] === 'vendor' && segments[1]) {
|
||||
this.vendorCode = segments[1];
|
||||
}
|
||||
|
||||
// Load user from localStorage
|
||||
const user = localStorage.getItem('currentUser');
|
||||
if (user) {
|
||||
this.currentUser = JSON.parse(user);
|
||||
}
|
||||
|
||||
// Load theme preference
|
||||
const theme = localStorage.getItem('theme');
|
||||
if (theme === 'dark') {
|
||||
this.dark = true;
|
||||
}
|
||||
|
||||
// Load vendor info
|
||||
this.loadVendorInfo();
|
||||
},
|
||||
|
||||
async loadVendorInfo() {
|
||||
if (!this.vendorCode) return;
|
||||
|
||||
try {
|
||||
const response = await apiClient.get(`/api/v1/vendors/${this.vendorCode}`);
|
||||
this.vendor = response;
|
||||
logDebug('Vendor info loaded', this.vendor);
|
||||
} catch (error) {
|
||||
logError('Failed to load vendor info', error);
|
||||
}
|
||||
},
|
||||
|
||||
toggleSideMenu() {
|
||||
this.isSideMenuOpen = !this.isSideMenuOpen;
|
||||
},
|
||||
|
||||
closeSideMenu() {
|
||||
this.isSideMenuOpen = false;
|
||||
},
|
||||
|
||||
toggleNotificationsMenu() {
|
||||
this.isNotificationsMenuOpen = !this.isNotificationsMenuOpen;
|
||||
if (this.isNotificationsMenuOpen) {
|
||||
this.isProfileMenuOpen = false;
|
||||
}
|
||||
},
|
||||
|
||||
closeNotificationsMenu() {
|
||||
this.isNotificationsMenuOpen = false;
|
||||
},
|
||||
|
||||
toggleProfileMenu() {
|
||||
this.isProfileMenuOpen = !this.isProfileMenuOpen;
|
||||
if (this.isProfileMenuOpen) {
|
||||
this.isNotificationsMenuOpen = false;
|
||||
}
|
||||
},
|
||||
|
||||
closeProfileMenu() {
|
||||
this.isProfileMenuOpen = false;
|
||||
},
|
||||
|
||||
toggleTheme() {
|
||||
this.dark = !this.dark;
|
||||
localStorage.setItem('theme', this.dark ? 'dark' : 'light');
|
||||
},
|
||||
|
||||
async handleLogout() {
|
||||
try {
|
||||
await apiClient.post('/api/v1/vendor/auth/logout');
|
||||
} catch (error) {
|
||||
logError('Logout error', error);
|
||||
} finally {
|
||||
localStorage.removeItem('accessToken');
|
||||
localStorage.removeItem('currentUser');
|
||||
window.location.href = `/vendor/${this.vendorCode}/login`;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
110
static/vendor/js/login.js
vendored
Normal file
110
static/vendor/js/login.js
vendored
Normal file
@@ -0,0 +1,110 @@
|
||||
// app/static/vendor/js/login.js
|
||||
/**
|
||||
* Vendor login page logic
|
||||
*/
|
||||
|
||||
// ✅ Use centralized logger - ONE LINE!
|
||||
// Create custom logger for login page
|
||||
const loginLog = window.LogConfig.createLogger('LOGIN');
|
||||
|
||||
function vendorLogin() {
|
||||
return {
|
||||
credentials: {
|
||||
username: '',
|
||||
password: ''
|
||||
},
|
||||
vendor: null,
|
||||
vendorCode: null,
|
||||
loading: false,
|
||||
checked: false,
|
||||
error: '',
|
||||
success: '',
|
||||
errors: {},
|
||||
dark: false,
|
||||
|
||||
async init() {
|
||||
// Load theme
|
||||
const theme = localStorage.getItem('theme');
|
||||
if (theme === 'dark') {
|
||||
this.dark = true;
|
||||
}
|
||||
|
||||
// Get vendor code from URL path
|
||||
const pathSegments = window.location.pathname.split('/').filter(Boolean);
|
||||
if (pathSegments[0] === 'vendor' && pathSegments[1]) {
|
||||
this.vendorCode = pathSegments[1];
|
||||
await this.loadVendor();
|
||||
}
|
||||
this.checked = true;
|
||||
},
|
||||
|
||||
async loadVendor() {
|
||||
this.loading = true;
|
||||
try {
|
||||
const response = await apiClient.get(`/vendor/${this.vendorCode}`);
|
||||
this.vendor = response;
|
||||
logInfo('Vendor loaded', this.vendor);
|
||||
} catch (error) {
|
||||
logError('Failed to load vendor', error);
|
||||
this.error = 'Failed to load vendor information';
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
|
||||
async handleLogin() {
|
||||
this.clearErrors();
|
||||
this.loading = true;
|
||||
|
||||
try {
|
||||
if (!this.credentials.username) {
|
||||
this.errors.username = 'Username is required';
|
||||
}
|
||||
if (!this.credentials.password) {
|
||||
this.errors.password = 'Password is required';
|
||||
}
|
||||
|
||||
if (Object.keys(this.errors).length > 0) {
|
||||
this.loading = false;
|
||||
return;
|
||||
}
|
||||
|
||||
const response = await apiClient.post('/vendor/auth/login', {
|
||||
username: this.credentials.username,
|
||||
password: this.credentials.password,
|
||||
vendor_code: this.vendorCode
|
||||
});
|
||||
|
||||
logInfo('Login successful', response);
|
||||
|
||||
localStorage.setItem('accessToken', response.access_token);
|
||||
localStorage.setItem('currentUser', JSON.stringify(response.user));
|
||||
localStorage.setItem('vendorCode', this.vendorCode);
|
||||
|
||||
this.success = 'Login successful! Redirecting...';
|
||||
|
||||
setTimeout(() => {
|
||||
window.location.href = `/vendor/${this.vendorCode}/dashboard`;
|
||||
}, 1000);
|
||||
|
||||
} catch (error) {
|
||||
logError('Login failed', error);
|
||||
|
||||
if (error.status === 401) {
|
||||
this.error = 'Invalid username or password';
|
||||
} else if (error.status === 403) {
|
||||
this.error = 'Your account does not have access to this vendor';
|
||||
} else {
|
||||
this.error = error.message || 'Login failed. Please try again.';
|
||||
}
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
|
||||
clearErrors() {
|
||||
this.error = '';
|
||||
this.errors = {};
|
||||
}
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user