diff --git a/static/admin/js/components.js b/static/admin/js/components.js index 6d6f4ce0..fb8b40d0 100644 --- a/static/admin/js/components.js +++ b/static/admin/js/components.js @@ -32,8 +32,9 @@ function adminComponents() { { id: 'cards', name: 'Cards', icon: 'collection' }, { id: 'badges', name: 'Badges', icon: 'tag' }, { id: 'tables', name: 'Tables', icon: 'table' }, - { id: 'modals', name: 'Modals', icon: 'window' }, - { id: 'alerts', name: 'Alerts', icon: 'exclamation' } + { id: 'modals', name: 'Modals', icon: 'view-grid-add' }, + { id: 'alerts', name: 'Alerts', icon: 'exclamation' }, + { id: 'charts', name: 'Charts', icon: 'chart-pie' } ], // Sample form data for examples @@ -53,6 +54,10 @@ function adminComponents() { required: 'This field is required' }, + // Modal state variables for examples + showExampleModal: false, + showFormModal: false, + // ✅ CRITICAL: Proper initialization with guard async init() { componentsLog.info('=== COMPONENTS PAGE INITIALIZING ==='); @@ -72,6 +77,11 @@ function adminComponents() { this.setActiveSectionFromHash(); }); + // Initialize charts after DOM is ready + this.$nextTick(() => { + this.initializeCharts(); + }); + componentsLog.info('=== COMPONENTS PAGE INITIALIZATION COMPLETE ==='); }, @@ -114,11 +124,18 @@ function adminComponents() { async copyCode(code) { try { await navigator.clipboard.writeText(code); - Utils.showToast('Code copied to clipboard!', 'success'); + // Use the global Utils.showToast function + if (typeof Utils !== 'undefined' && Utils.showToast) { + Utils.showToast('Code copied to clipboard!', 'success'); + } else { + componentsLog.warn('Utils.showToast not available'); + } componentsLog.debug('Code copied to clipboard'); } catch (error) { componentsLog.error('Failed to copy code:', error); - Utils.showToast('Failed to copy code', 'error'); + if (typeof Utils !== 'undefined' && Utils.showToast) { + Utils.showToast('Failed to copy code', 'error'); + } } }, @@ -132,7 +149,145 @@ function adminComponents() { warning: 'Please review your input.', info: 'Here is some information.' }; - Utils.showToast(messages[type] || messages.info, type); + + if (typeof Utils !== 'undefined' && Utils.showToast) { + Utils.showToast(messages[type] || messages.info, type); + } else { + componentsLog.error('Utils.showToast not available'); + alert(messages[type] || messages.info); // Fallback to alert + } + }, + + /** + * Initialize Chart.js charts + */ + initializeCharts() { + try { + // Check if Chart.js is loaded + if (typeof Chart === 'undefined') { + componentsLog.warn('Chart.js not loaded, skipping chart initialization'); + return; + } + + componentsLog.info('Initializing charts...'); + + // Pie Chart + const pieCanvas = document.getElementById('examplePieChart'); + if (pieCanvas) { + const pieConfig = { + type: 'doughnut', + data: { + datasets: [{ + data: [33, 33, 33], + backgroundColor: ['#0694a2', '#7e3af2', '#1c64f2'], + label: 'Dataset 1', + }], + labels: ['Shoes', 'Shirts', 'Bags'], + }, + options: { + responsive: true, + cutoutPercentage: 80, + legend: { + display: false, + }, + }, + }; + new Chart(pieCanvas, pieConfig); + componentsLog.debug('Pie chart initialized'); + } + + // Line Chart + const lineCanvas = document.getElementById('exampleLineChart'); + if (lineCanvas) { + const lineConfig = { + type: 'line', + data: { + labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'], + datasets: [{ + label: 'Organic', + backgroundColor: '#0694a2', + borderColor: '#0694a2', + data: [43, 48, 40, 54, 67, 73, 70], + fill: false, + }, { + label: 'Paid', + fill: false, + backgroundColor: '#7e3af2', + borderColor: '#7e3af2', + data: [24, 50, 64, 74, 52, 51, 65], + }], + }, + options: { + responsive: true, + legend: { + display: false, + }, + tooltips: { + mode: 'index', + intersect: false, + }, + hover: { + mode: 'nearest', + intersect: true, + }, + scales: { + x: { + display: true, + scaleLabel: { + display: true, + labelString: 'Month', + }, + }, + y: { + display: true, + scaleLabel: { + display: true, + labelString: 'Value', + }, + }, + }, + }, + }; + new Chart(lineCanvas, lineConfig); + componentsLog.debug('Line chart initialized'); + } + + // Bar Chart + const barCanvas = document.getElementById('exampleBarChart'); + if (barCanvas) { + const barConfig = { + type: 'bar', + data: { + labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'], + datasets: [{ + label: 'Shoes', + backgroundColor: '#0694a2', + borderColor: '#0694a2', + borderWidth: 1, + data: [43, 48, 40, 54, 67, 73, 70], + }, { + label: 'Bags', + backgroundColor: '#7e3af2', + borderColor: '#7e3af2', + borderWidth: 1, + data: [24, 50, 64, 74, 52, 51, 65], + }], + }, + options: { + responsive: true, + legend: { + display: false, + }, + }, + }; + new Chart(barCanvas, barConfig); + componentsLog.debug('Bar chart initialized'); + } + + componentsLog.info('All charts initialized successfully'); + } catch (error) { + componentsLog.error('Error initializing charts:', error); + } } }; } diff --git a/static/admin/js/vendor-theme.js b/static/admin/js/vendor-theme.js new file mode 100644 index 00000000..0f77c1ef --- /dev/null +++ b/static/admin/js/vendor-theme.js @@ -0,0 +1,278 @@ +// static/admin/js/vendor-theme.js +/** + * Vendor Theme Management Component + * Follows the established Alpine.js pattern from FRONTEND_ALPINE_PAGE_TEMPLATE.md + */ + +const THEME_LOG_LEVEL = 3; + +const themeLog = { + error: (...args) => THEME_LOG_LEVEL >= 1 && console.error('❌ [THEME ERROR]', ...args), + warn: (...args) => THEME_LOG_LEVEL >= 2 && console.warn('⚠️ [THEME WARN]', ...args), + info: (...args) => THEME_LOG_LEVEL >= 3 && console.info('ℹ️ [THEME INFO]', ...args), + debug: (...args) => THEME_LOG_LEVEL >= 4 && console.log('🔍 [THEME DEBUG]', ...args) +}; + +// Theme presets +const THEME_PRESETS = { + modern: { + colors: { + primary: "#6366f1", + secondary: "#8b5cf6", + accent: "#ec4899" + }, + fonts: { + heading: "Inter, sans-serif", + body: "Inter, sans-serif" + }, + layout: { + style: "grid", + header: "fixed" + } + }, + classic: { + colors: { + primary: "#1e40af", + secondary: "#7c3aed", + accent: "#dc2626" + }, + fonts: { + heading: "Georgia, serif", + body: "Arial, sans-serif" + }, + layout: { + style: "list", + header: "static" + } + }, + minimal: { + colors: { + primary: "#000000", + secondary: "#404040", + accent: "#666666" + }, + fonts: { + heading: "Helvetica, sans-serif", + body: "Helvetica, sans-serif" + }, + layout: { + style: "grid", + header: "transparent" + } + }, + vibrant: { + colors: { + primary: "#f59e0b", + secondary: "#ef4444", + accent: "#8b5cf6" + }, + fonts: { + heading: "Poppins, sans-serif", + body: "Open Sans, sans-serif" + }, + layout: { + style: "masonry", + header: "fixed" + } + } +}; + +function vendorThemeData() { + return { + // ✅ CRITICAL: Inherit base layout functionality + ...data(), + + // ✅ CRITICAL: Set page identifier + currentPage: 'vendor-theme', + + // Page state + loading: false, + saving: false, + vendor: null, + vendorCode: window.location.pathname.split('/')[3], // Extract from /admin/vendors/{code}/theme + + // Theme data + themeData: { + colors: { + primary: "#6366f1", + secondary: "#8b5cf6", + accent: "#ec4899" + }, + fonts: { + heading: "Inter, sans-serif", + body: "Inter, sans-serif" + }, + layout: { + style: "grid", + header: "fixed" + }, + custom_css: "" + }, + + originalTheme: null, // For detecting changes + + // ✅ CRITICAL: Proper initialization with guard + async init() { + themeLog.info('=== VENDOR THEME PAGE INITIALIZING ==='); + + // Prevent multiple initializations + if (window._vendorThemeInitialized) { + themeLog.warn('Page already initialized, skipping...'); + return; + } + window._vendorThemeInitialized = true; + + // Load data + await this.loadVendor(); + await this.loadTheme(); + + themeLog.info('=== VENDOR THEME PAGE INITIALIZATION COMPLETE ==='); + }, + + // Load vendor info + async loadVendor() { + themeLog.info('Loading vendor:', this.vendorCode); + + try { + // ✅ CRITICAL: Use lowercase apiClient + const response = await apiClient.get(`/api/v1/admin/vendors/${this.vendorCode}`); + this.vendor = response; + themeLog.info('Vendor loaded:', this.vendor.name); + } catch (error) { + themeLog.error('Failed to load vendor:', error); + Utils.showToast('Failed to load vendor', 'error'); + } + }, + + // Load theme configuration + async loadTheme() { + themeLog.info('Loading theme...'); + this.loading = true; + + try { + const startTime = Date.now(); + + // Get vendor's theme config from vendor object + if (this.vendor && this.vendor.theme_config) { + this.themeData = { + colors: this.vendor.theme_config.colors || this.themeData.colors, + fonts: this.vendor.theme_config.fonts || this.themeData.fonts, + layout: this.vendor.theme_config.layout || this.themeData.layout, + custom_css: this.vendor.theme_config.custom_css || "" + }; + } else { + themeLog.info('No theme config found, using defaults'); + } + + // Store original for change detection + this.originalTheme = JSON.parse(JSON.stringify(this.themeData)); + + const duration = Date.now() - startTime; + themeLog.info(`Theme loaded in ${duration}ms`, this.themeData); + + } catch (error) { + themeLog.error('Failed to load theme:', error); + Utils.showToast('Failed to load theme', 'error'); + } finally { + this.loading = false; + } + }, + + // Save theme configuration + async saveTheme() { + themeLog.info('Saving theme...'); + this.saving = true; + + try { + const startTime = Date.now(); + + // Update vendor with new theme_config + const updateData = { + theme_config: this.themeData + }; + + const response = await apiClient.put( + `/api/v1/admin/vendors/${this.vendorCode}`, + updateData + ); + + const duration = Date.now() - startTime; + themeLog.info(`Theme saved in ${duration}ms`); + + // Update vendor data + this.vendor = response; + this.originalTheme = JSON.parse(JSON.stringify(this.themeData)); + + Utils.showToast('Theme saved successfully', 'success'); + + } catch (error) { + themeLog.error('Failed to save theme:', error); + Utils.showToast('Failed to save theme', 'error'); + } finally { + this.saving = false; + } + }, + + // Apply preset theme + applyPreset(presetName) { + themeLog.info('Applying preset:', presetName); + + if (!THEME_PRESETS[presetName]) { + themeLog.error('Unknown preset:', presetName); + return; + } + + const preset = THEME_PRESETS[presetName]; + + // Apply preset values + this.themeData.colors = { ...preset.colors }; + this.themeData.fonts = { ...preset.fonts }; + this.themeData.layout = { ...preset.layout }; + + Utils.showToast(`Applied ${presetName} theme preset`, 'success'); + }, + + // Reset to default theme + resetToDefault() { + themeLog.info('Resetting to default theme'); + + // Confirm with user + if (!confirm('Are you sure you want to reset to the default theme? This will discard all customizations.')) { + return; + } + + this.themeData = { + colors: { + primary: "#6366f1", + secondary: "#8b5cf6", + accent: "#ec4899" + }, + fonts: { + heading: "Inter, sans-serif", + body: "Inter, sans-serif" + }, + layout: { + style: "grid", + header: "fixed" + }, + custom_css: "" + }; + + Utils.showToast('Theme reset to default', 'info'); + }, + + // Check if theme has unsaved changes + hasChanges() { + if (!this.originalTheme) return false; + return JSON.stringify(this.themeData) !== JSON.stringify(this.originalTheme); + }, + + // Format date helper + formatDate(dateString) { + if (!dateString) return '-'; + return Utils.formatDate(dateString); + } + }; +} + +themeLog.info('Vendor theme module loaded'); \ No newline at end of file diff --git a/static/admin/partials/base-layout.html b/static/admin/partials/base-layout.html deleted file mode 100644 index 335fa4bd..00000000 --- a/static/admin/partials/base-layout.html +++ /dev/null @@ -1,72 +0,0 @@ - - - - - - - Admin Panel - Multi-Tenant Platform - - - - - - - - - - - - - - -
- - - -
- -
- - -
-
- - -
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/static/admin/users.html b/static/admin/users.html deleted file mode 100644 index 74c5f6ae..00000000 --- a/static/admin/users.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - User management - - - <-- User management --> - - diff --git a/static/admin/vendor-edit.html b/static/admin/vendor-edit.html deleted file mode 100644 index 33e60b3d..00000000 --- a/static/admin/vendor-edit.html +++ /dev/null @@ -1,498 +0,0 @@ - - - - - - Edit Vendor - Admin Portal - - - - - - -
-
-

🔐 Admin Dashboard

-
-
- - -
-
- - -
- - - - -
- -
- -

Loading vendor details...

-
- - -
-
-
-

- Edit Vendor: -

- -
- - -
- - -
- -
-
- -
-

Basic Information

- - -
- - -
Cannot be changed after creation
-
- - -
- - -
-
- - -
- - -
Lowercase letters, numbers, and hyphens only
-
-
- - -
- - -
-
- - -
-

Contact & Business Information

- - -
- - -
- ⚠️ Owner email cannot be changed here. Use "Transfer Ownership" below to change the owner. -
-
- - -
- - -
- Public business contact email (can be different from owner email) -
-
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
-
-
- - -
- -
-

⚠️ Change Vendor Owner

-

- To change the owner to a different user account, use the Transfer Ownership feature. - This will assign ownership to another user and demote the current owner to Manager. -

- -
- -
- - -
-
-
-
-
-
- - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/static/admin/vendors.html b/static/admin/vendors.html deleted file mode 100644 index ac13edf3..00000000 --- a/static/admin/vendors.html +++ /dev/null @@ -1,201 +0,0 @@ - - - - - - Vendors - Admin Portal - - - - - -
- - - -
- -
- - -
-
-
-

- Vendor Management -

- -
- - -
-
-
- - - - -
-

- Vendor Management -

-

- This page will display vendor list and management tools. -

-

- Features to be implemented: -

-
    -
  • • View all vendors with filtering and search
  • -
  • • Create new vendors with detailed forms
  • -
  • • Edit vendor information
  • -
  • • Verify/unverify vendors
  • -
  • • View vendor statistics
  • -
-
-
- - -
-
- - - - - - - - - - - - - - - - -
Vendor CodeNameSubdomainStatusCreatedActions
-
-
-
-
-
-
- - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/static/css/admin/admin.css b/static/css/admin/admin.css deleted file mode 100644 index 649c2adc..00000000 --- a/static/css/admin/admin.css +++ /dev/null @@ -1,894 +0,0 @@ -/* static/css/admin/admin.css */ -/* Admin-specific styles */ - -/* Admin Header */ -.admin-header { - background: white; - border-bottom: 1px solid var(--border-color); - padding: 16px 24px; - display: flex; - justify-content: space-between; - align-items: center; - box-shadow: var(--shadow-sm); - position: sticky; - top: 0; - z-index: 100; -} - -.admin-header h1 { - font-size: var(--font-2xl); - color: var(--text-primary); - font-weight: 600; -} - -.header-left { - display: flex; - align-items: center; - gap: var(--spacing-md); -} - -.header-right { - display: flex; - align-items: center; - gap: var(--spacing-md); -} - -.user-info { - font-size: var(--font-base); - color: var(--text-secondary); -} - -.user-info strong { - color: var(--text-primary); - font-weight: 600; -} - -.btn-logout { - padding: 8px 16px; - background: var(--danger-color); - color: white; - border: none; - border-radius: var(--radius-md); - cursor: pointer; - font-size: var(--font-base); - font-weight: 500; - transition: all var(--transition-base); -} - -.btn-logout:hover { - background: #c0392b; - transform: translateY(-1px); -} - -/* Admin Container */ -.admin-container { - display: flex; - min-height: calc(100vh - 64px); -} - -/* Admin Sidebar */ -.admin-sidebar { - width: 250px; - background: white; - border-right: 1px solid var(--border-color); - padding: var(--spacing-lg) 0; - overflow-y: auto; -} - -.nav-menu { - list-style: none; - padding: 0; - margin: 0; -} - -.nav-item { - margin-bottom: 4px; -} - -.nav-link { - display: flex; - align-items: center; - padding: 12px 24px; - color: var(--text-secondary); - text-decoration: none; - font-size: var(--font-base); - font-weight: 500; - transition: all var(--transition-base); - border-right: 3px solid transparent; -} - -.nav-link:hover { - background: var(--gray-50); - color: var(--primary-color); -} - -.nav-link.active { - background: var(--primary-color); - color: white; - border-right-color: var(--primary-dark); -} - -.nav-icon { - margin-right: var(--spacing-sm); - font-size: var(--font-lg); -} - -/* Admin Content */ -.admin-content { - flex: 1; - padding: var(--spacing-lg); - overflow-y: auto; - background: var(--bg-secondary); -} - -/* Stats Grid */ -.stats-grid { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); - gap: var(--spacing-lg); - margin-bottom: var(--spacing-xl); -} - -.stat-card { - background: white; - border-radius: var(--radius-lg); - padding: var(--spacing-lg); - box-shadow: var(--shadow-md); - border: 1px solid var(--border-color); - transition: all var(--transition-base); -} - -.stat-card:hover { - transform: translateY(-4px); - box-shadow: var(--shadow-lg); -} - -.stat-header { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: var(--spacing-md); -} - -.stat-icon { - font-size: 32px; - opacity: 0.8; -} - -.stat-title { - font-size: var(--font-base); - color: var(--text-secondary); - font-weight: 500; - text-transform: uppercase; - letter-spacing: 0.5px; -} - -.stat-value { - font-size: 32px; - font-weight: 700; - color: var(--text-primary); - margin-bottom: var(--spacing-sm); - line-height: 1; -} - -.stat-subtitle { - font-size: var(--font-sm); - color: var(--text-muted); -} - -.stat-change { - display: inline-flex; - align-items: center; - font-size: var(--font-sm); - font-weight: 600; - margin-left: var(--spacing-sm); -} - -.stat-change.positive { - color: var(--success-color); -} - -.stat-change.negative { - color: var(--danger-color); -} - -/* Content Sections */ -.content-section { - background: white; - border-radius: var(--radius-lg); - padding: var(--spacing-lg); - box-shadow: var(--shadow-md); - border: 1px solid var(--border-color); - margin-bottom: var(--spacing-lg); -} - -.section-header { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: var(--spacing-lg); - padding-bottom: var(--spacing-md); - border-bottom: 2px solid var(--gray-100); -} - -.section-title { - font-size: var(--font-xl); - font-weight: 600; - color: var(--text-primary); -} - -.section-actions { - display: flex; - gap: var(--spacing-sm); -} - -/* Data Tables */ -.data-table { - width: 100%; - border-collapse: collapse; -} - -.data-table thead { - background: var(--gray-50); -} - -.data-table th { - text-align: left; - padding: 12px; - font-size: var(--font-sm); - font-weight: 600; - color: var(--text-secondary); - text-transform: uppercase; - letter-spacing: 0.5px; - border-bottom: 2px solid var(--border-color); -} - -.data-table td { - padding: 12px; - border-bottom: 1px solid var(--border-color); - font-size: var(--font-base); -} - -.data-table tbody tr { - transition: background var(--transition-fast); -} - -.data-table tbody tr:hover { - background: var(--gray-50); -} - -.data-table tbody tr:last-child td { - border-bottom: none; -} - -.table-actions { - display: flex; - gap: var(--spacing-sm); -} - -.table-actions .btn { - padding: 6px 12px; - font-size: var(--font-sm); -} - -/* Empty State */ -.empty-state { - text-align: center; - padding: var(--spacing-2xl) var(--spacing-lg); - color: var(--text-muted); -} - -.empty-state-icon { - font-size: 48px; - margin-bottom: var(--spacing-md); - opacity: 0.5; -} - -.empty-state h3 { - font-size: var(--font-xl); - color: var(--text-secondary); - margin-bottom: var(--spacing-sm); -} - -.empty-state p { - font-size: var(--font-base); - color: var(--text-muted); -} - -/* Loading State */ -.loading { - text-align: center; - padding: var(--spacing-2xl); - color: var(--text-muted); -} - -.loading-text { - margin-top: var(--spacing-md); - font-size: var(--font-base); -} - -/* Search and Filters */ -.filter-bar { - display: flex; - gap: var(--spacing-md); - margin-bottom: var(--spacing-lg); - flex-wrap: wrap; -} - -.filter-group { - flex: 1; - min-width: 200px; -} - -.search-box { - position: relative; - flex: 2; - min-width: 300px; -} - -.search-input { - width: 100%; - padding-left: 40px; -} - -.search-icon { - position: absolute; - left: 12px; - top: 50%; - transform: translateY(-50%); - color: var(--text-muted); -} - -/* Action Buttons */ -.action-buttons { - display: flex; - gap: var(--spacing-sm); - justify-content: flex-end; -} - -/* Modal/Dialog */ -/* Modal Styles */ -.modal-overlay { - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100%; - background-color: rgba(0, 0, 0, 0.5); - display: flex; - align-items: center; - justify-content: center; - z-index: 1000; -} - -.modal-content { - background: white; - border-radius: 8px; - max-width: 600px; - width: 90%; - max-height: 90vh; - overflow-y: auto; - box-shadow: 0 10px 40px rgba(0, 0, 0, 0.3); -} - -.modal-header { - padding: 20px; - border-bottom: 1px solid #e5e7eb; - display: flex; - justify-content: space-between; - align-items: center; -} - -.modal-header h3 { - margin: 0; - font-size: 1.25rem; -} - -.btn-close { - background: none; - border: none; - font-size: 28px; - line-height: 1; - cursor: pointer; - color: #6b7280; -} - -.btn-close:hover { - color: #1f2937; -} - -.modal-body { - padding: 20px; -} - -.modal-footer { - padding: 20px; - border-top: 1px solid #e5e7eb; - display: flex; - justify-content: flex-end; - gap: 12px; -} - -.info-box { - background-color: #eff6ff; - border: 1px solid #3b82f6; - border-radius: 6px; - padding: 12px; - color: #1e40af; -} - -.checkbox-label { - display: flex; - align-items: center; - gap: 8px; - cursor: pointer; -} - -.checkbox-label input[type="checkbox"] { - width: auto; - margin: 0; -} - -.form-section-divider { - border-top: 2px solid #e5e7eb; - margin: 32px 0; -} - -/* Small modal variant for confirmations */ -.modal-sm { - max-width: 500px; -} - -/* Info box styling */ -.info-box { - background: #e3f2fd; - border-left: 4px solid #2196f3; - padding: 12px 16px; - border-radius: 4px; - font-size: 14px; -} - -/* Quick actions spacing */ -.quick-actions { - display: flex; - gap: 12px; - flex-wrap: wrap; -} - -/* Success Modal Styles */ -.modal-header-success { - background: linear-gradient(135deg, #4caf50 0%, #45a049 100%); - color: white; - border-radius: 8px 8px 0 0; -} - -.modal-header-success .btn-close { - color: white; - opacity: 0.9; -} - -.modal-header-success .btn-close:hover { - opacity: 1; -} - -.success-icon-wrapper { - display: flex; - justify-content: center; - margin: 20px 0; -} - -.success-icon { - width: 80px; - height: 80px; - border-radius: 50%; - background: linear-gradient(135deg, #4caf50 0%, #45a049 100%); - color: white; - display: flex; - align-items: center; - justify-content: center; - font-size: 48px; - font-weight: bold; - animation: successPulse 0.6s ease-out; - box-shadow: 0 4px 20px rgba(76, 175, 80, 0.3); -} - -@keyframes successPulse { - 0% { - transform: scale(0.5); - opacity: 0; - } - 50% { - transform: scale(1.1); - } - 100% { - transform: scale(1); - opacity: 1; - } -} - -.transfer-details { - background: #f8f9fa; - border-radius: 8px; - padding: 20px; - margin: 20px 0; -} - -.detail-row { - display: flex; - flex-direction: column; - gap: 8px; - padding: 12px; - background: white; - border-radius: 6px; - margin-bottom: 12px; -} - -.detail-label { - font-size: 12px; - font-weight: 600; - text-transform: uppercase; - color: #666; - letter-spacing: 0.5px; -} - -.detail-value { - font-size: 15px; -} - -.detail-value strong { - color: #333; - font-size: 16px; -} - -.detail-arrow { - text-align: center; - font-size: 24px; - color: #4caf50; - font-weight: bold; - margin: 8px 0; -} - -.modal-md { - max-width: 600px; -} - -.btn-block { - width: 100%; - padding: 12px; - font-size: 16px; - font-weight: 600; -} - -/* Alert variants in modals */ -.modal-body .alert { - margin-top: 16px; - padding: 12px 16px; - border-radius: 6px; - border: none; -} - -.modal-body .alert-info { - background: #e3f2fd; - color: #1976d2; -} - -.modal-body .alert-info strong { - color: #0d47a1; -} - -/* Text utilities */ -.text-center { - text-align: center; -} - -.text-muted { - color: #6c757d; - font-size: 14px; -} - -.mb-3 { - margin-bottom: 1rem; -} - -.mb-4 { - margin-bottom: 1.5rem; -} - -.mt-3 { - margin-top: 1rem; -} - -/* Pagination */ -.pagination { - display: flex; - justify-content: center; - align-items: center; - gap: var(--spacing-sm); - margin-top: var(--spacing-lg); -} - -.pagination-btn { - padding: 8px 12px; - border: 1px solid var(--border-color); - background: white; - border-radius: var(--radius-md); - cursor: pointer; - transition: all var(--transition-base); -} - -.pagination-btn:hover:not(:disabled) { - background: var(--gray-50); - border-color: var(--primary-color); -} - -.pagination-btn:disabled { - opacity: 0.5; - cursor: not-allowed; -} - -.pagination-btn.active { - background: var(--primary-color); - color: white; - border-color: var(--primary-color); -} - -.pagination-info { - font-size: var(--font-sm); - color: var(--text-muted); -} - -/* Responsive Design */ -@media (max-width: 1024px) { - .admin-sidebar { - width: 200px; - } - - .stats-grid { - grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); - } -} - -@media (max-width: 768px) { - .admin-container { - flex-direction: column; - } - - .admin-sidebar { - width: 100%; - border-right: none; - border-bottom: 1px solid var(--border-color); - } - - .admin-content { - padding: var(--spacing-md); - } - - .stats-grid { - grid-template-columns: 1fr; - } - - .section-header { - flex-direction: column; - align-items: flex-start; - gap: var(--spacing-md); - } - - .filter-bar { - flex-direction: column; - } - - .filter-group, - .search-box { - width: 100%; - } - - .data-table { - font-size: var(--font-sm); - } - - .data-table th, - .data-table td { - padding: 8px; - } -} - -@media (max-width: 480px) { - .admin-header { - padding: 12px 16px; - } - - .admin-header h1 { - font-size: var(--font-lg); - } - - .stat-value { - font-size: var(--font-3xl); - } - - .content-section { - padding: var(--spacing-md); - } - - /* Make table scrollable on small screens */ - .table-wrapper { - overflow-x: auto; - } - - .data-table { - min-width: 600px; - } -} - -/* Print Styles */ -@media print { - .admin-sidebar, - .admin-header .header-right, - .section-actions, - .table-actions { - display: none; - } - - .admin-content { - padding: 0; - } - - .content-section { - box-shadow: none; - border: 1px solid var(--border-color); - } - -/* Value with copy button */ -.value-with-copy { - display: flex; - gap: var(--spacing-sm); - align-items: center; - flex: 1; -} - -.credential-item.highlight { - background: #fff3cd; - border: 2px solid var(--warning-color); -} - -.credential-item.highlight .value { - background: white; - font-weight: 600; - color: var(--text-primary); -} - -.btn-copy { - background: var(--gray-100); - border: 1px solid var(--border-color); - padding: 6px 12px; - border-radius: var(--radius-sm); - cursor: pointer; - color: var(--text-secondary); - font-size: var(--font-xs); - transition: all var(--transition-base); - white-space: nowrap; -} - -.btn-copy:hover { - background: var(--primary-color); - border-color: var(--primary-color); - color: white; -} - -/* Text color utilities */ -.text-danger { - color: var(--danger-color) !important; -} - -.text-warning { - color: var(--warning-color) !important; -} - -.text-success { - color: var(--success-color) !important; -} - -/* Disabled button styles */ -.btn-icon:disabled { - opacity: 0.5; - cursor: not-allowed; -} - -.btn-icon:disabled:hover { - background: white; - border-color: var(--border-color); - transform: none; -} - -/* Alert improvements */ -.alert { - animation: slideDown 0.3s ease; -} - -@keyframes slideDown { - from { - opacity: 0; - transform: translateY(-10px); - } - to { - opacity: 1; - transform: translateY(0); - } -} - -/* Form Section Styles */ -.form-section-title { - font-size: 1.125rem; - font-weight: 600; - color: #1f2937; - margin-bottom: 16px; - padding-bottom: 8px; - border-bottom: 2px solid #e5e7eb; -} - -.form-control-disabled { - background-color: #f3f4f6; - cursor: not-allowed; - color: #6b7280; -} - -.form-help { - font-size: 0.875rem; - color: #6b7280; - margin-top: 4px; -} - -.text-muted { - color: #6b7280; -} - -.mb-2 { - margin-bottom: 0.5rem; -} - -.mb-3 { - margin-bottom: 1rem; -} - -.mt-3 { - margin-top: 1rem; -} - -/* Alert Styles */ -.alert { - padding: 12px 16px; - border-radius: 6px; - margin-bottom: 16px; -} - -.alert-warning { - background-color: #fef3c7; - border: 1px solid #f59e0b; - color: #92400e; -} - -.alert-warning strong { - color: #78350f; -} - -/* Quick Actions */ -.quick-actions { - display: flex; - gap: 12px; - flex-wrap: wrap; -} - -.quick-actions .btn { - min-width: 180px; -} - -/* Button Sizes */ -.btn-sm { - padding: 6px 12px; - font-size: 0.875rem; -} - -/* Required Field Indicator */ -.required { - color: #ef4444; - font-weight: bold; -} \ No newline at end of file diff --git a/static/css/shared/auth.css b/static/css/shared/auth.css deleted file mode 100644 index 2d12e6d7..00000000 --- a/static/css/shared/auth.css +++ /dev/null @@ -1,622 +0,0 @@ -/* static/css/shared/auth.css */ -/* Authentication pages (login, register) styles */ - -/* Auth Page Layout */ -.auth-page { - min-height: 100vh; - display: flex; - justify-content: center; - align-items: center; - background: linear-gradient(135deg, var(--primary-color) 0%, var(--primary-dark) 100%); - padding: var(--spacing-lg); -} - -.auth-page::before { - content: ''; - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - background: - radial-gradient(circle at 20% 80%, rgba(255,255,255,0.1) 0%, transparent 50%), - radial-gradient(circle at 80% 20%, rgba(255,255,255,0.1) 0%, transparent 50%); - pointer-events: none; -} - -/* Login Container */ -.login-container, -.auth-container { - background: white; - border-radius: var(--radius-xl); - box-shadow: var(--shadow-xl); - width: 100%; - max-width: 420px; - padding: var(--spacing-xl); - position: relative; - z-index: 1; - animation: slideUp 0.4s ease; -} - -@keyframes slideUp { - from { - opacity: 0; - transform: translateY(20px); - } - to { - opacity: 1; - transform: translateY(0); - } -} - -/* Login Header */ -.login-header, -.auth-header { - text-align: center; - margin-bottom: var(--spacing-xl); -} - -.auth-logo { - width: 80px; - height: 80px; - margin: 0 auto var(--spacing-md); - background: linear-gradient(135deg, var(--primary-color) 0%, var(--primary-dark) 100%); - border-radius: var(--radius-xl); - display: flex; - align-items: center; - justify-content: center; - font-size: 40px; - color: white; - box-shadow: var(--shadow-md); -} - -.login-header h1, -.auth-header h1 { - font-size: var(--font-3xl); - color: var(--text-primary); - margin-bottom: var(--spacing-sm); - font-weight: 700; -} - -.login-header p, -.auth-header p { - color: var(--text-secondary); - font-size: var(--font-base); -} - -/* Vendor Info Display */ -.vendor-info { - background: var(--gray-50); - padding: 12px 16px; - border-radius: var(--radius-lg); - margin-bottom: var(--spacing-lg); - text-align: center; - border: 1px solid var(--border-color); -} - -.vendor-info strong { - color: var(--primary-color); - font-size: var(--font-lg); - font-weight: 600; -} - -/* No Vendor Message */ -.no-vendor-message { - text-align: center; - padding: var(--spacing-2xl) var(--spacing-lg); - color: var(--text-secondary); -} - -.no-vendor-message h2 { - font-size: var(--font-2xl); - color: var(--text-primary); - margin-bottom: var(--spacing-md); -} - -.no-vendor-message p { - margin-bottom: var(--spacing-lg); - color: var(--text-muted); -} - -/* Auth Form */ -.auth-form, -.login-form { - margin-bottom: var(--spacing-lg); -} - -.form-group { - margin-bottom: var(--spacing-lg); -} - -.form-group label { - display: block; - font-weight: 600; - color: var(--text-primary); - margin-bottom: var(--spacing-sm); - font-size: var(--font-base); -} - -.form-group input { - width: 100%; - padding: 14px 16px; - border: 2px solid var(--border-color); - border-radius: var(--radius-lg); - font-size: var(--font-md); - transition: all var(--transition-base); - background: white; -} - -.form-group input:focus { - outline: none; - border-color: var(--primary-color); - box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1); -} - -.form-group input.error { - border-color: var(--danger-color); -} - -.form-group input::placeholder { - color: var(--text-muted); -} - -/* Password Toggle */ -.password-group { - position: relative; -} - -.password-toggle { - position: absolute; - right: 12px; - top: 50%; - transform: translateY(-50%); - background: none; - border: none; - cursor: pointer; - color: var(--text-muted); - padding: var(--spacing-sm); - font-size: var(--font-lg); -} - -.password-toggle:hover { - color: var(--text-primary); -} - -/* Remember Me & Forgot Password */ -.form-options { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: var(--spacing-lg); - font-size: var(--font-sm); -} - -.remember-me { - display: flex; - align-items: center; - gap: var(--spacing-sm); -} - -.remember-me input[type="checkbox"] { - width: auto; - margin: 0; -} - -.forgot-password { - color: var(--primary-color); - text-decoration: none; - font-weight: 500; -} - -.forgot-password:hover { - text-decoration: underline; -} - -/* Submit Button */ -.btn-login, -.btn-auth { - width: 100%; - padding: 14px 24px; - background: linear-gradient(135deg, var(--primary-color) 0%, var(--primary-dark) 100%); - color: white; - border: none; - border-radius: var(--radius-lg); - font-size: var(--font-lg); - font-weight: 600; - cursor: pointer; - transition: all var(--transition-base); - box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3); -} - -.btn-login:hover:not(:disabled), -.btn-auth:hover:not(:disabled) { - transform: translateY(-2px); - box-shadow: 0 8px 20px rgba(102, 126, 234, 0.4); -} - -.btn-login:active:not(:disabled), -.btn-auth:active:not(:disabled) { - transform: translateY(0); -} - -.btn-login:disabled, -.btn-auth:disabled { - opacity: 0.6; - cursor: not-allowed; - transform: none; -} - -/* Social Login Buttons */ -.social-login { - margin-top: var(--spacing-lg); - padding-top: var(--spacing-lg); - border-top: 1px solid var(--border-color); -} - -.social-login-text { - text-align: center; - color: var(--text-muted); - font-size: var(--font-sm); - margin-bottom: var(--spacing-md); -} - -.social-buttons { - display: flex; - gap: var(--spacing-md); -} - -.btn-social { - flex: 1; - padding: 12px; - border: 2px solid var(--border-color); - background: white; - border-radius: var(--radius-lg); - cursor: pointer; - transition: all var(--transition-base); - font-size: var(--font-base); - font-weight: 500; - display: flex; - align-items: center; - justify-content: center; - gap: var(--spacing-sm); -} - -.btn-social:hover { - border-color: var(--primary-color); - background: var(--gray-50); -} - -/* Login Footer */ -.login-footer, -.auth-footer { - text-align: center; - margin-top: var(--spacing-lg); - padding-top: var(--spacing-lg); - border-top: 1px solid var(--border-color); -} - -.login-footer a, -.auth-footer a { - color: var(--primary-color); - text-decoration: none; - font-size: var(--font-base); - font-weight: 500; -} - -.login-footer a:hover, -.auth-footer a:hover { - text-decoration: underline; -} - -.auth-footer-text { - font-size: var(--font-sm); - color: var(--text-muted); - margin-bottom: var(--spacing-sm); -} - -/* Back Button */ -.btn-back { - display: inline-block; - padding: 10px 20px; - background: var(--secondary-color); - color: white; - text-decoration: none; - border-radius: var(--radius-md); - font-weight: 500; - transition: all var(--transition-base); -} - -.btn-back:hover { - background: #5a6268; - transform: translateY(-1px); -} - -/* Error and Success Messages */ -.alert { - padding: 12px 16px; - border-radius: var(--radius-lg); - margin-bottom: var(--spacing-lg); - font-size: var(--font-base); - display: none; - animation: slideDown 0.3s ease; -} - -@keyframes slideDown { - from { - opacity: 0; - transform: translateY(-10px); - } - to { - opacity: 1; - transform: translateY(0); - } -} - -.alert.show { - display: block; -} - -.alert-success { - background: #d4edda; - color: #155724; - border: 1px solid #c3e6cb; -} - -.alert-error { - background: #f8d7da; - color: #721c24; - border: 1px solid #f5c6cb; -} - -.alert-warning { - background: #fff3cd; - color: #856404; - border: 1px solid #ffeaa7; -} - -.alert-info { - background: #d1ecf1; - color: #0c5460; - border: 1px solid #bee5eb; -} - -/* Field Errors */ -.error-message { - color: var(--danger-color); - font-size: var(--font-sm); - margin-top: var(--spacing-xs); - display: none; - animation: fadeIn 0.2s ease; -} - -@keyframes fadeIn { - from { opacity: 0; } - to { opacity: 1; } -} - -.error-message.show { - display: block; -} - -/* Loading State */ -.loading-spinner { - display: inline-block; - width: 16px; - height: 16px; - border: 2px solid currentColor; - border-radius: 50%; - border-top-color: transparent; - animation: spinner 0.6s linear infinite; - margin-right: var(--spacing-sm); - vertical-align: middle; -} - -@keyframes spinner { - to { transform: rotate(360deg); } -} - -/* Divider */ -.divider { - display: flex; - align-items: center; - text-align: center; - margin: var(--spacing-lg) 0; -} - -.divider::before, -.divider::after { - content: ''; - flex: 1; - border-bottom: 1px solid var(--border-color); -} - -.divider span { - padding: 0 var(--spacing-md); - color: var(--text-muted); - font-size: var(--font-sm); -} - -/* Credentials Display (for vendor creation success) */ -.credentials-card { - background: #fff3cd; - border: 2px solid var(--warning-color); - border-radius: var(--radius-lg); - padding: var(--spacing-lg); - margin-top: var(--spacing-lg); -} - -.credentials-card h3 { - margin-bottom: var(--spacing-md); - color: #856404; - font-size: var(--font-xl); -} - -.credential-item { - background: white; - padding: 12px 16px; - border-radius: var(--radius-md); - margin-bottom: 12px; - display: flex; - justify-content: space-between; - align-items: center; - border: 1px solid var(--border-color); -} - -.credential-item label { - font-weight: 600; - color: var(--text-primary); - font-size: var(--font-base); -} - -.credential-item .value { - font-family: 'Courier New', monospace; - background: var(--gray-50); - padding: 6px 12px; - border-radius: var(--radius-sm); - color: var(--text-primary); - font-size: var(--font-base); - word-break: break-all; -} - -.warning-text { - color: #856404; - font-size: var(--font-sm); - margin-top: var(--spacing-md); - font-weight: 600; - display: flex; - align-items: center; - gap: var(--spacing-sm); -} - -.warning-text::before { - content: '⚠️'; - font-size: var(--font-lg); -} - -/* Copy Button */ -.btn-copy { - background: none; - border: 1px solid var(--border-color); - padding: 4px 8px; - border-radius: var(--radius-sm); - cursor: pointer; - color: var(--text-secondary); - font-size: var(--font-xs); - transition: all var(--transition-base); -} - -.btn-copy:hover { - background: var(--gray-50); - border-color: var(--primary-color); - color: var(--primary-color); -} - -/* Responsive Design */ -@media (max-width: 480px) { - .login-container, - .auth-container { - padding: var(--spacing-lg); - max-width: 100%; - } - - .auth-logo { - width: 60px; - height: 60px; - font-size: 32px; - } - - .login-header h1, - .auth-header h1 { - font-size: var(--font-2xl); - } - - .btn-login, - .btn-auth { - padding: 12px 20px; - font-size: var(--font-base); - } - - .social-buttons { - flex-direction: column; - } - - .credential-item { - flex-direction: column; - align-items: flex-start; - gap: var(--spacing-sm); - } - - .credential-item .value { - width: 100%; - text-align: left; - } -} - -/* Dark Mode Support (optional) */ -@media (prefers-color-scheme: dark) { - .auth-page { - background: linear-gradient(135deg, #1a202c 0%, #2d3748 100%); - } - - .login-container, - .auth-container { - background: #2d3748; - color: #e2e8f0; - } - - .login-header h1, - .auth-header h1, - .form-group label { - color: #e2e8f0; - } - - .login-header p, - .auth-header p { - color: #a0aec0; - } - - .form-group input { - background: #1a202c; - border-color: #4a5568; - color: #e2e8f0; - } - - .form-group input::placeholder { - color: #718096; - } - - .vendor-info { - background: #1a202c; - border-color: #4a5568; - } - - .credential-item { - background: #1a202c; - border-color: #4a5568; - } - - .credential-item .value { - background: #2d3748; - } -} - -/* Print Styles */ -@media print { - .auth-page::before { - display: none; - } - - .login-container, - .auth-container { - box-shadow: none; - border: 1px solid var(--border-color); - } - - .btn-login, - .btn-auth, - .social-login, - .login-footer, - .auth-footer { - display: none; - } -} \ No newline at end of file diff --git a/static/css/shared/base.css b/static/css/shared/base.css deleted file mode 100644 index fa29e17f..00000000 --- a/static/css/shared/base.css +++ /dev/null @@ -1,514 +0,0 @@ -/* static/css/shared/base.css */ -/* Base styles shared across all pages */ - -/* Import responsive utilities */ -@import url('responsive-utilities.css'); - -/* Rest of your base.css... */ -:root { - /* Color Palette */ - --primary-color: #667eea; - --primary-dark: #764ba2; - --secondary-color: #6c757d; - --success-color: #28a745; - --danger-color: #e74c3c; - --warning-color: #ffc107; - --info-color: #17a2b8; - - /* Grays */ - --gray-50: #f9fafb; - --gray-100: #f5f7fa; - --gray-200: #e1e8ed; - --gray-300: #d1d9e0; - --gray-400: #b0bac5; - --gray-500: #8796a5; - --gray-600: #687785; - --gray-700: #4a5568; - --gray-800: #2d3748; - --gray-900: #1a202c; - - /* Text Colors */ - --text-primary: #333333; - --text-secondary: #666666; - --text-muted: #999999; - - /* Background Colors */ - --bg-primary: #ffffff; - --bg-secondary: #f5f7fa; - --bg-overlay: rgba(0, 0, 0, 0.5); - - /* Border Colors */ - --border-color: #e1e8ed; - --border-focus: #667eea; - - /* Shadows */ - --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.1); - --shadow-md: 0 4px 6px rgba(0, 0, 0, 0.1); - --shadow-lg: 0 10px 25px rgba(0, 0, 0, 0.15); - --shadow-xl: 0 20px 60px rgba(0, 0, 0, 0.3); - - /* Transitions */ - --transition-fast: 0.15s ease; - --transition-base: 0.2s ease; - --transition-slow: 0.3s ease; - - /* Border Radius */ - --radius-sm: 4px; - --radius-md: 6px; - --radius-lg: 8px; - --radius-xl: 12px; - --radius-full: 9999px; - - /* Spacing */ - --spacing-xs: 4px; - --spacing-sm: 8px; - --spacing-md: 16px; - --spacing-lg: 24px; - --spacing-xl: 32px; - --spacing-2xl: 48px; - - /* Font Sizes */ - --font-xs: 12px; - --font-sm: 13px; - --font-base: 14px; - --font-md: 15px; - --font-lg: 16px; - --font-xl: 18px; - --font-2xl: 20px; - --font-3xl: 24px; - --font-4xl: 32px; -} - -/* Reset and Base Styles */ -* { - margin: 0; - padding: 0; - box-sizing: border-box; -} - -html { - font-size: 16px; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -body { - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; - font-size: var(--font-base); - line-height: 1.5; - color: var(--text-primary); - background: var(--bg-secondary); -} - -/* Typography */ -h1, h2, h3, h4, h5, h6 { - margin: 0; - font-weight: 600; - line-height: 1.2; - color: var(--text-primary); -} - -h1 { font-size: var(--font-4xl); } -h2 { font-size: var(--font-3xl); } -h3 { font-size: var(--font-2xl); } -h4 { font-size: var(--font-xl); } -h5 { font-size: var(--font-lg); } -h6 { font-size: var(--font-base); } - -p { - margin: 0 0 1rem; -} - -a { - color: var(--primary-color); - text-decoration: none; - transition: color var(--transition-base); -} - -a:hover { - color: var(--primary-dark); -} - -/* Buttons */ -.btn { - display: inline-block; - padding: 10px 20px; - font-size: var(--font-base); - font-weight: 600; - text-align: center; - border: none; - border-radius: var(--radius-md); - cursor: pointer; - transition: all var(--transition-base); - text-decoration: none; - line-height: 1.5; -} - -.btn:disabled { - opacity: 0.6; - cursor: not-allowed; -} - -.btn-primary { - background: linear-gradient(135deg, var(--primary-color) 0%, var(--primary-dark) 100%); - color: white; -} - -.btn-primary:hover:not(:disabled) { - transform: translateY(-2px); - box-shadow: 0 8px 20px rgba(102, 126, 234, 0.4); -} - -.btn-secondary { - background: var(--secondary-color); - color: white; -} - -.btn-secondary:hover:not(:disabled) { - background: #5a6268; -} - -.btn-success { - background: var(--success-color); - color: white; -} - -.btn-danger { - background: var(--danger-color); - color: white; -} - -.btn-warning { - background: var(--warning-color); - color: var(--gray-900); -} - -.btn-outline { - background: transparent; - border: 2px solid var(--primary-color); - color: var(--primary-color); -} - -.btn-outline:hover:not(:disabled) { - background: var(--primary-color); - color: white; -} - -.btn-sm { - padding: 6px 12px; - font-size: var(--font-sm); -} - -.btn-lg { - padding: 14px 28px; - font-size: var(--font-lg); -} - -/* Form Elements */ -.form-group { - margin-bottom: var(--spacing-lg); -} - -.form-label { - display: block; - font-weight: 600; - margin-bottom: var(--spacing-sm); - color: var(--text-primary); - font-size: var(--font-base); -} - -.form-control, -.form-input, -.form-select, -.form-textarea { - width: 100%; - padding: 12px 16px; - font-size: var(--font-base); - line-height: 1.5; - color: var(--text-primary); - background: white; - border: 2px solid var(--border-color); - border-radius: var(--radius-md); - transition: border-color var(--transition-base); -} - -.form-control:focus, -.form-input:focus, -.form-select:focus, -.form-textarea:focus { - outline: none; - border-color: var(--border-focus); -} - -.form-control.error, -.form-input.error { - border-color: var(--danger-color); -} - -.form-textarea { - resize: vertical; - min-height: 80px; -} - -.form-help { - display: block; - margin-top: var(--spacing-xs); - font-size: var(--font-sm); - color: var(--text-muted); -} - -.error-message { - display: none; - margin-top: var(--spacing-xs); - font-size: var(--font-sm); - color: var(--danger-color); -} - -.error-message.show { - display: block; -} - -/* Cards */ -.card { - background: white; - border-radius: var(--radius-lg); - box-shadow: var(--shadow-md); - overflow: hidden; -} - -.card-header { - padding: var(--spacing-lg); - border-bottom: 1px solid var(--border-color); -} - -.card-body { - padding: var(--spacing-lg); -} - -.card-footer { - padding: var(--spacing-lg); - border-top: 1px solid var(--border-color); - background: var(--gray-50); -} - -/* Badges */ -.badge { - display: inline-block; - padding: 4px 10px; - font-size: var(--font-xs); - font-weight: 600; - line-height: 1; - border-radius: var(--radius-full); - text-transform: uppercase; - letter-spacing: 0.5px; -} - -.badge-primary { - background: rgba(102, 126, 234, 0.1); - color: var(--primary-color); -} - -.badge-success { - background: #d4edda; - color: #155724; -} - -.badge-danger { - background: #f8d7da; - color: #721c24; -} - -.badge-warning { - background: #fff3cd; - color: #856404; -} - -.badge-info { - background: #d1ecf1; - color: #0c5460; -} - -.badge-secondary { - background: var(--gray-200); - color: var(--gray-700); -} - -/* Alerts */ -.alert { - padding: 12px 16px; - border-radius: var(--radius-md); - margin-bottom: var(--spacing-lg); - font-size: var(--font-base); - border: 1px solid transparent; -} - -.alert-success { - background: #d4edda; - color: #155724; - border-color: #c3e6cb; -} - -.alert-danger, -.alert-error { - background: #f8d7da; - color: #721c24; - border-color: #f5c6cb; -} - -.alert-warning { - background: #fff3cd; - color: #856404; - border-color: #ffeaa7; -} - -.alert-info { - background: #d1ecf1; - color: #0c5460; - border-color: #bee5eb; -} - -/* Tables */ -.table { - width: 100%; - border-collapse: collapse; -} - -.table th { - text-align: left; - padding: 12px; - background: var(--gray-100); - font-size: var(--font-sm); - font-weight: 600; - color: var(--text-secondary); - text-transform: uppercase; - letter-spacing: 0.5px; - border-bottom: 2px solid var(--border-color); -} - -.table td { - padding: 12px; - border-bottom: 1px solid var(--border-color); - font-size: var(--font-base); -} - -.table tr:hover { - background: var(--gray-50); -} - -.table-striped tbody tr:nth-child(odd) { - background: var(--gray-50); -} - -/* Utilities */ -.text-center { text-align: center; } -.text-left { text-align: left; } -.text-right { text-align: right; } - -.text-muted { color: var(--text-muted); } -.text-primary { color: var(--primary-color); } -.text-success { color: var(--success-color); } -.text-danger { color: var(--danger-color); } -.text-warning { color: var(--warning-color); } - -.font-bold { font-weight: 700; } -.font-semibold { font-weight: 600; } -.font-normal { font-weight: 400; } - -.mt-0 { margin-top: 0; } -.mt-1 { margin-top: var(--spacing-sm); } -.mt-2 { margin-top: var(--spacing-md); } -.mt-3 { margin-top: var(--spacing-lg); } -.mt-4 { margin-top: var(--spacing-xl); } - -.mb-0 { margin-bottom: 0; } -.mb-1 { margin-bottom: var(--spacing-sm); } -.mb-2 { margin-bottom: var(--spacing-md); } -.mb-3 { margin-bottom: var(--spacing-lg); } -.mb-4 { margin-bottom: var(--spacing-xl); } - -.p-0 { padding: 0; } -.p-1 { padding: var(--spacing-sm); } -.p-2 { padding: var(--spacing-md); } -.p-3 { padding: var(--spacing-lg); } -.p-4 { padding: var(--spacing-xl); } - -.d-none { display: none; } -.d-block { display: block; } -.d-inline { display: inline; } -.d-inline-block { display: inline-block; } -.d-flex { display: flex; } - -.flex-column { flex-direction: column; } -.flex-row { flex-direction: row; } -.justify-start { justify-content: flex-start; } -.justify-end { justify-content: flex-end; } -.justify-center { justify-content: center; } -.justify-between { justify-content: space-between; } -.align-start { align-items: flex-start; } -.align-end { align-items: flex-end; } -.align-center { align-items: center; } - -.gap-1 { gap: var(--spacing-sm); } -.gap-2 { gap: var(--spacing-md); } -.gap-3 { gap: var(--spacing-lg); } - -/* Loading Spinner */ -.loading-spinner { - display: inline-block; - width: 16px; - height: 16px; - border: 2px solid currentColor; - border-radius: 50%; - border-top-color: transparent; - animation: spinner 0.6s linear infinite; - vertical-align: middle; -} - -@keyframes spinner { - to { transform: rotate(360deg); } -} - -.loading-overlay { - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - background: var(--bg-overlay); - display: flex; - justify-content: center; - align-items: center; - z-index: 9999; -} - -.loading-spinner-lg { - width: 48px; - height: 48px; - border-width: 4px; -} - -/* Responsive */ -@media (max-width: 768px) { - :root { - --font-base: 14px; - --font-lg: 15px; - --font-xl: 16px; - --font-2xl: 18px; - --font-3xl: 20px; - --font-4xl: 24px; - } - - .btn { - padding: 8px 16px; - } - - .card-body { - padding: var(--spacing-md); - } -} - -@media (max-width: 480px) { - .btn { - width: 100%; - } -} \ No newline at end of file diff --git a/static/css/shared/components.css b/static/css/shared/components.css deleted file mode 100644 index 05297c02..00000000 --- a/static/css/shared/components.css +++ /dev/null @@ -1,728 +0,0 @@ -/** - * Universal Components Styles - * Shared component styles for Admin, Vendor, and Shop sections - */ - -/* ============================================================================= - MODAL SYSTEM STYLES - ============================================================================= */ - -/* Modal Backdrop */ -.modal-backdrop { - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - background-color: rgba(0, 0, 0, 0.5); - display: flex; - align-items: center; - justify-content: center; - z-index: 9999; - animation: fadeIn 0.2s ease-in; -} - -@keyframes fadeIn { - from { - opacity: 0; - } - to { - opacity: 1; - } -} - -/* Modal Container */ -.modal-container { - background: white; - border-radius: 8px; - box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15); - max-width: 500px; - width: 90%; - max-height: 90vh; - overflow-y: auto; - animation: slideUp 0.3s ease-out; -} - -@keyframes slideUp { - from { - transform: translateY(20px); - opacity: 0; - } - to { - transform: translateY(0); - opacity: 1; - } -} - -/* Modal Header */ -.modal-header { - padding: 20px 24px; - border-bottom: 1px solid #e5e7eb; - display: flex; - align-items: center; - justify-content: space-between; -} - -.modal-title { - font-size: 1.25rem; - font-weight: 600; - color: #111827; - margin: 0; -} - -.modal-close { - background: none; - border: none; - font-size: 1.5rem; - color: #6b7280; - cursor: pointer; - padding: 0; - width: 32px; - height: 32px; - display: flex; - align-items: center; - justify-content: center; - border-radius: 4px; - transition: all 0.2s; -} - -.modal-close:hover { - background-color: #f3f4f6; - color: #111827; -} - -/* Modal Body */ -.modal-body { - padding: 24px; -} - -.modal-message { - font-size: 0.95rem; - color: #374151; - line-height: 1.6; - margin-bottom: 12px; -} - -.modal-warning { - background-color: #fef3c7; - border-left: 4px solid #f59e0b; - padding: 12px 16px; - border-radius: 4px; - margin-top: 16px; -} - -.modal-warning p { - margin: 0; - color: #92400e; - font-size: 0.875rem; -} - -.modal-details { - background-color: #f9fafb; - border: 1px solid #e5e7eb; - border-radius: 4px; - padding: 12px; - margin-top: 12px; - font-family: 'Courier New', monospace; - font-size: 0.875rem; - color: #374151; - white-space: pre-wrap; - word-break: break-word; -} - -/* Modal Footer */ -.modal-footer { - padding: 16px 24px; - border-top: 1px solid #e5e7eb; - display: flex; - justify-content: flex-end; - gap: 12px; -} - -/* Modal Icon Styles */ -.modal-icon { - width: 48px; - height: 48px; - border-radius: 50%; - display: flex; - align-items: center; - justify-content: center; - margin: 0 auto 16px; - font-size: 24px; -} - -.modal-icon.success { - background-color: #d1fae5; - color: #059669; -} - -.modal-icon.error { - background-color: #fee2e2; - color: #dc2626; -} - -.modal-icon.warning { - background-color: #fef3c7; - color: #f59e0b; -} - -/* Loading Overlay */ -.loading-overlay { - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - background-color: rgba(0, 0, 0, 0.3); - display: flex; - align-items: center; - justify-content: center; - z-index: 10000; -} - -.loading-spinner { - width: 48px; - height: 48px; - border: 4px solid #e5e7eb; - border-top-color: #3b82f6; - border-radius: 50%; - animation: spin 0.8s linear infinite; -} - -@keyframes spin { - to { - transform: rotate(360deg); - } -} - -/* ============================================================================= - BUTTON STYLES - ============================================================================= */ - -.btn { - padding: 10px 20px; - border: none; - border-radius: 6px; - font-size: 0.95rem; - font-weight: 500; - cursor: pointer; - transition: all 0.2s; - display: inline-flex; - align-items: center; - gap: 8px; - text-decoration: none; -} - -.btn:disabled { - opacity: 0.5; - cursor: not-allowed; -} - -.btn-primary { - background-color: #3b82f6; - color: white; -} - -.btn-primary:hover:not(:disabled) { - background-color: #2563eb; -} - -.btn-secondary { - background-color: #6b7280; - color: white; -} - -.btn-secondary:hover:not(:disabled) { - background-color: #4b5563; -} - -.btn-success { - background-color: #10b981; - color: white; -} - -.btn-success:hover:not(:disabled) { - background-color: #059669; -} - -.btn-danger { - background-color: #ef4444; - color: white; -} - -.btn-danger:hover:not(:disabled) { - background-color: #dc2626; -} - -.btn-warning { - background-color: #f59e0b; - color: white; -} - -.btn-warning:hover:not(:disabled) { - background-color: #d97706; -} - -.btn-outline { - background-color: transparent; - border: 1px solid #d1d5db; - color: #374151; -} - -.btn-outline:hover:not(:disabled) { - background-color: #f9fafb; - border-color: #9ca3af; -} - -.btn-ghost { - background-color: transparent; - color: #6b7280; -} - -.btn-ghost:hover:not(:disabled) { - background-color: #f3f4f6; -} - -.btn-sm { - padding: 6px 12px; - font-size: 0.875rem; -} - -.btn-lg { - padding: 12px 24px; - font-size: 1.05rem; -} - -/* ============================================================================= - ADMIN LAYOUT STYLES - ============================================================================= */ - -.admin-header { - background-color: #1f2937; - color: white; - padding: 0 24px; - height: 64px; - display: flex; - align-items: center; - justify-content: space-between; - position: sticky; - top: 0; - z-index: 100; - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); -} - -.header-left { - display: flex; - align-items: center; - gap: 16px; -} - -.header-title { - font-size: 1.25rem; - font-weight: 600; - margin: 0; -} - -.menu-toggle { - background: none; - border: none; - color: white; - font-size: 1.5rem; - cursor: pointer; - padding: 8px; - display: none; -} - -.header-right { - display: flex; - align-items: center; - gap: 16px; -} - -.user-name { - font-size: 0.95rem; - color: #e5e7eb; -} - -.btn-logout { - background-color: #374151; - color: white; - border: none; - padding: 8px 16px; - border-radius: 6px; - cursor: pointer; - display: flex; - align-items: center; - gap: 8px; - font-size: 0.9rem; - transition: background-color 0.2s; -} - -.btn-logout:hover { - background-color: #4b5563; -} - -.admin-sidebar { - width: 260px; - background-color: #f9fafb; - border-right: 1px solid #e5e7eb; - height: calc(100vh - 64px); - position: fixed; - left: 0; - top: 64px; - overflow-y: auto; - transition: transform 0.3s ease; -} - -.sidebar-nav { - padding: 16px 0; -} - -.nav-item { - display: flex; - align-items: center; - gap: 12px; - padding: 12px 24px; - color: #374151; - text-decoration: none; - transition: all 0.2s; - font-size: 0.95rem; -} - -.nav-item:hover { - background-color: #e5e7eb; - color: #111827; -} - -.nav-item.active { - background-color: #3b82f6; - color: white; - border-left: 4px solid #2563eb; -} - -.nav-item i { - width: 20px; - text-align: center; -} - -.admin-content { - margin-left: 260px; - padding: 24px; - min-height: calc(100vh - 64px); -} - -/* ============================================================================= - VENDOR LAYOUT STYLES - ============================================================================= */ - -.vendor-header { - background-color: #059669; - color: white; - padding: 0 24px; - height: 64px; - display: flex; - align-items: center; - justify-content: space-between; - position: sticky; - top: 0; - z-index: 100; - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); -} - -.vendor-header .btn-logout { - background-color: #047857; - color: white; - border: none; - padding: 8px 16px; - border-radius: 6px; - cursor: pointer; - display: flex; - align-items: center; - gap: 8px; - font-size: 0.9rem; - transition: background-color 0.2s; -} - -.vendor-header .btn-logout:hover { - background-color: #065f46; -} - -.vendor-header .menu-toggle { - background: none; - border: none; - color: white; - font-size: 1.5rem; - cursor: pointer; - padding: 8px; - display: none; -} - -.vendor-sidebar { - width: 260px; - background-color: #f9fafb; - border-right: 1px solid #e5e7eb; - height: calc(100vh - 64px); - position: fixed; - left: 0; - top: 64px; - overflow-y: auto; - transition: transform 0.3s ease; -} - -.vendor-content { - margin-left: 260px; - padding: 24px; - min-height: calc(100vh - 64px); -} - -/* ============================================================================= - SHOP LAYOUT STYLES - ============================================================================= */ - -.shop-header { - background-color: white; - border-bottom: 1px solid #e5e7eb; - padding: 16px 24px; - position: sticky; - top: 0; - z-index: 100; - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05); -} - -.shop-header-top { - display: flex; - align-items: center; - justify-content: space-between; - max-width: 1280px; - margin: 0 auto; -} - -.shop-logo { - font-size: 1.5rem; - font-weight: 700; - color: #111827; - text-decoration: none; -} - -.shop-search { - flex: 1; - max-width: 500px; - margin: 0 32px; -} - -.search-form { - display: flex; - gap: 8px; -} - -.search-input { - flex: 1; - padding: 10px 16px; - border: 1px solid #d1d5db; - border-radius: 6px; - font-size: 0.95rem; -} - -.search-input:focus { - outline: none; - border-color: #3b82f6; - box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1); -} - -.shop-actions { - display: flex; - align-items: center; - gap: 16px; -} - -.cart-button { - position: relative; - background: none; - border: none; - cursor: pointer; - padding: 8px; - font-size: 1.5rem; - color: #374151; -} - -.cart-button:hover { - color: #111827; -} - -.cart-count { - position: absolute; - top: 0; - right: 0; - background-color: #ef4444; - color: white; - font-size: 0.75rem; - font-weight: 600; - padding: 2px 6px; - border-radius: 10px; - min-width: 20px; - text-align: center; -} - -.shop-nav { - display: flex; - gap: 24px; - max-width: 1280px; - margin: 16px auto 0; - padding-top: 16px; - border-top: 1px solid #e5e7eb; -} - -.shop-nav-item { - color: #6b7280; - text-decoration: none; - font-size: 0.95rem; - padding: 8px 0; - transition: color 0.2s; -} - -.shop-nav-item:hover { - color: #111827; -} - -.shop-nav-item.active { - color: #3b82f6; - font-weight: 500; - border-bottom: 2px solid #3b82f6; -} - -.shop-content { - max-width: 1280px; - margin: 0 auto; - padding: 32px 24px; -} - -/* ============================================================================= - SHOP ACCOUNT LAYOUT STYLES - ============================================================================= */ - -.account-layout { - display: grid; - grid-template-columns: 260px 1fr; - gap: 32px; - max-width: 1280px; - margin: 0 auto; - padding: 32px 24px; -} - -.account-sidebar { - background-color: #f9fafb; - border: 1px solid #e5e7eb; - border-radius: 8px; - padding: 16px; - height: fit-content; -} - -.account-nav-item { - display: flex; - align-items: center; - gap: 12px; - padding: 12px 16px; - color: #374151; - text-decoration: none; - border-radius: 6px; - transition: all 0.2s; - font-size: 0.95rem; -} - -.account-nav-item:hover { - background-color: #e5e7eb; -} - -.account-nav-item.active { - background-color: #3b82f6; - color: white; -} - -.account-content { - background-color: white; - border: 1px solid #e5e7eb; - border-radius: 8px; - padding: 32px; -} - -/* ============================================================================= - RESPONSIVE STYLES - ============================================================================= */ - -@media (max-width: 768px) { - .menu-toggle { - display: block; - } - - .vendor-header .menu-toggle { - display: block; - } - - .admin-sidebar, - .vendor-sidebar { - transform: translateX(-100%); - } - - .admin-sidebar.open, - .vendor-sidebar.open { - transform: translateX(0); - } - - .admin-content, - .vendor-content { - margin-left: 0; - } - - .shop-search { - display: none; - } - - .account-layout { - grid-template-columns: 1fr; - } - - .account-sidebar { - display: none; - } - - .modal-container { - width: 95%; - margin: 16px; - } - - .modal-footer { - flex-direction: column; - } - - .modal-footer .btn { - width: 100%; - } -} - -/* ============================================================================= - UTILITY CLASSES - ============================================================================= */ - -.text-center { - text-align: center; -} - -.mb-0 { margin-bottom: 0; } -.mb-1 { margin-bottom: 8px; } -.mb-2 { margin-bottom: 16px; } -.mb-3 { margin-bottom: 24px; } -.mb-4 { margin-bottom: 32px; } - -.mt-0 { margin-top: 0; } -.mt-1 { margin-top: 8px; } -.mt-2 { margin-top: 16px; } -.mt-3 { margin-top: 24px; } -.mt-4 { margin-top: 32px; } - -.hidden { - display: none !important; -} - -.pointer { - cursor: pointer; -} \ No newline at end of file diff --git a/static/css/shared/modals.css b/static/css/shared/modals.css deleted file mode 100644 index 35ce7cd1..00000000 --- a/static/css/shared/modals.css +++ /dev/null @@ -1,428 +0,0 @@ -/** - * Enhanced Modal Styles - * Additional styling and animations for modal system - */ - -/* ============================================================================= - MODAL ANIMATIONS - ============================================================================= */ - -/* Fade In Animation */ -@keyframes modalFadeIn { - from { - opacity: 0; - } - to { - opacity: 1; - } -} - -/* Slide Down Animation */ -@keyframes modalSlideDown { - from { - transform: translateY(-50px); - opacity: 0; - } - to { - transform: translateY(0); - opacity: 1; - } -} - -/* Slide Up Animation */ -@keyframes modalSlideUp { - from { - transform: translateY(50px); - opacity: 0; - } - to { - transform: translateY(0); - opacity: 1; - } -} - -/* Scale Animation */ -@keyframes modalScale { - from { - transform: scale(0.9); - opacity: 0; - } - to { - transform: scale(1); - opacity: 1; - } -} - -/* Shake Animation for errors */ -@keyframes modalShake { - 0%, 100% { - transform: translateX(0); - } - 10%, 30%, 50%, 70%, 90% { - transform: translateX(-10px); - } - 20%, 40%, 60%, 80% { - transform: translateX(10px); - } -} - -/* ============================================================================= - MODAL VARIANTS - ============================================================================= */ - -/* Modal with slide down animation */ -.modal-container.slide-down { - animation: modalSlideDown 0.3s ease-out; -} - -/* Modal with scale animation */ -.modal-container.scale { - animation: modalScale 0.3s ease-out; -} - -/* Modal with shake animation (for errors) */ -.modal-container.shake { - animation: modalShake 0.5s ease-in-out; -} - -/* ============================================================================= - MODAL SIZES - ============================================================================= */ - -.modal-container.modal-sm { - max-width: 400px; -} - -.modal-container.modal-md { - max-width: 500px; -} - -.modal-container.modal-lg { - max-width: 700px; -} - -.modal-container.modal-xl { - max-width: 900px; -} - -.modal-container.modal-full { - max-width: 95%; - max-height: 95vh; -} - -/* ============================================================================= - MODAL THEMES - ============================================================================= */ - -/* Success Modal Theme */ -.modal-container.modal-success .modal-header { - background-color: #d1fae5; - border-bottom-color: #a7f3d0; -} - -.modal-container.modal-success .modal-title { - color: #065f46; -} - -/* Error Modal Theme */ -.modal-container.modal-error .modal-header { - background-color: #fee2e2; - border-bottom-color: #fecaca; -} - -.modal-container.modal-error .modal-title { - color: #991b1b; -} - -/* Warning Modal Theme */ -.modal-container.modal-warning .modal-header { - background-color: #fef3c7; - border-bottom-color: #fde68a; -} - -.modal-container.modal-warning .modal-title { - color: #92400e; -} - -/* Info Modal Theme */ -.modal-container.modal-info .modal-header { - background-color: #dbeafe; - border-bottom-color: #bfdbfe; -} - -.modal-container.modal-info .modal-title { - color: #1e40af; -} - -/* ============================================================================= - MODAL CONTENT STYLES - ============================================================================= */ - -/* Modal List */ -.modal-list { - list-style: none; - padding: 0; - margin: 16px 0; -} - -.modal-list-item { - padding: 12px; - border-bottom: 1px solid #e5e7eb; - display: flex; - align-items: center; - gap: 12px; -} - -.modal-list-item:last-child { - border-bottom: none; -} - -.modal-list-item i { - width: 20px; - text-align: center; - color: #6b7280; -} - -/* Modal Form Elements */ -.modal-form-group { - margin-bottom: 16px; -} - -.modal-label { - display: block; - margin-bottom: 8px; - font-weight: 500; - color: #374151; - font-size: 0.95rem; -} - -.modal-input, -.modal-textarea, -.modal-select { - width: 100%; - padding: 10px 12px; - border: 1px solid #d1d5db; - border-radius: 6px; - font-size: 0.95rem; - transition: all 0.2s; -} - -.modal-input:focus, -.modal-textarea:focus, -.modal-select:focus { - outline: none; - border-color: #3b82f6; - box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1); -} - -.modal-textarea { - min-height: 100px; - resize: vertical; -} - -.modal-input.error, -.modal-textarea.error, -.modal-select.error { - border-color: #ef4444; -} - -.modal-error-text { - color: #ef4444; - font-size: 0.875rem; - margin-top: 4px; -} - -.modal-help-text { - color: #6b7280; - font-size: 0.875rem; - margin-top: 4px; -} - -/* ============================================================================= - MODAL ALERTS - ============================================================================= */ - -.modal-alert { - padding: 12px 16px; - border-radius: 6px; - margin-bottom: 16px; - display: flex; - align-items: start; - gap: 12px; -} - -.modal-alert i { - margin-top: 2px; -} - -.modal-alert.alert-success { - background-color: #d1fae5; - color: #065f46; - border: 1px solid #a7f3d0; -} - -.modal-alert.alert-error { - background-color: #fee2e2; - color: #991b1b; - border: 1px solid #fecaca; -} - -.modal-alert.alert-warning { - background-color: #fef3c7; - color: #92400e; - border: 1px solid #fde68a; -} - -.modal-alert.alert-info { - background-color: #dbeafe; - color: #1e40af; - border: 1px solid #bfdbfe; -} - -/* ============================================================================= - MODAL PROGRESS - ============================================================================= */ - -.modal-progress { - width: 100%; - height: 8px; - background-color: #e5e7eb; - border-radius: 4px; - overflow: hidden; - margin: 16px 0; -} - -.modal-progress-bar { - height: 100%; - background-color: #3b82f6; - transition: width 0.3s ease; -} - -.modal-progress-bar.success { - background-color: #10b981; -} - -.modal-progress-bar.error { - background-color: #ef4444; -} - -.modal-progress-bar.warning { - background-color: #f59e0b; -} - -/* ============================================================================= - LOADING SPINNER VARIANTS - ============================================================================= */ - -.loading-spinner.spinner-sm { - width: 32px; - height: 32px; - border-width: 3px; -} - -.loading-spinner.spinner-lg { - width: 64px; - height: 64px; - border-width: 5px; -} - -.loading-spinner.spinner-success { - border-top-color: #10b981; -} - -.loading-spinner.spinner-error { - border-top-color: #ef4444; -} - -.loading-spinner.spinner-warning { - border-top-color: #f59e0b; -} - -/* ============================================================================= - MODAL BACKDROP VARIANTS - ============================================================================= */ - -.modal-backdrop.backdrop-dark { - background-color: rgba(0, 0, 0, 0.7); -} - -.modal-backdrop.backdrop-light { - background-color: rgba(0, 0, 0, 0.3); -} - -.modal-backdrop.backdrop-blur { - backdrop-filter: blur(4px); -} - -/* ============================================================================= - RESPONSIVE ENHANCEMENTS - ============================================================================= */ - -@media (max-width: 640px) { - .modal-container { - width: 100%; - max-width: 100%; - max-height: 100vh; - border-radius: 0; - margin: 0; - } - - .modal-header { - padding: 16px 20px; - } - - .modal-body { - padding: 20px; - } - - .modal-footer { - padding: 12px 20px; - flex-direction: column-reverse; - } - - .modal-footer .btn { - width: 100%; - } - - .modal-alert { - flex-direction: column; - } -} - -/* ============================================================================= - ACCESSIBILITY - ============================================================================= */ - -.modal-backdrop:focus { - outline: none; -} - -.modal-container:focus { - outline: 2px solid #3b82f6; - outline-offset: -2px; -} - -/* Reduce motion for users who prefer it */ -@media (prefers-reduced-motion: reduce) { - .modal-backdrop, - .modal-container, - .loading-spinner { - animation: none !important; - transition: none !important; - } -} - -/* High contrast mode support */ -@media (prefers-contrast: high) { - .modal-container { - border: 2px solid currentColor; - } - - .modal-header { - border-bottom-width: 2px; - } - - .modal-footer { - border-top-width: 2px; - } -} \ No newline at end of file diff --git a/static/css/shared/responsive-utilities.css b/static/css/shared/responsive-utilities.css deleted file mode 100644 index f65082ee..00000000 --- a/static/css/shared/responsive-utilities.css +++ /dev/null @@ -1,370 +0,0 @@ -/* static/css/shared/responsive-utilities.css */ -/* Responsive utility classes - Framework-like responsiveness without the framework */ - -/* ================================ - BREAKPOINTS (Mobile-first) - ================================ */ -:root { - --breakpoint-sm: 640px; - --breakpoint-md: 768px; - --breakpoint-lg: 1024px; - --breakpoint-xl: 1280px; - --breakpoint-2xl: 1536px; -} - -/* ================================ - CONTAINER - ================================ */ -.container { - width: 100%; - margin-left: auto; - margin-right: auto; - padding-left: var(--spacing-md); - padding-right: var(--spacing-md); -} - -@media (min-width: 640px) { - .container { max-width: 640px; } -} - -@media (min-width: 768px) { - .container { max-width: 768px; } -} - -@media (min-width: 1024px) { - .container { max-width: 1024px; } -} - -@media (min-width: 1280px) { - .container { max-width: 1280px; } -} - -/* ================================ - DISPLAY UTILITIES - ================================ */ -.hidden { display: none !important; } -.block { display: block !important; } -.inline-block { display: inline-block !important; } -.inline { display: inline !important; } -.flex { display: flex !important; } -.inline-flex { display: inline-flex !important; } -.grid { display: grid !important; } - -/* Responsive Display */ -@media (max-width: 639px) { - .sm\:hidden { display: none !important; } - .sm\:block { display: block !important; } - .sm\:flex { display: flex !important; } - .sm\:grid { display: grid !important; } -} - -@media (min-width: 640px) and (max-width: 767px) { - .md\:hidden { display: none !important; } - .md\:block { display: block !important; } - .md\:flex { display: flex !important; } - .md\:grid { display: grid !important; } -} - -@media (min-width: 768px) and (max-width: 1023px) { - .lg\:hidden { display: none !important; } - .lg\:block { display: block !important; } - .lg\:flex { display: flex !important; } - .lg\:grid { display: grid !important; } -} - -@media (min-width: 1024px) { - .xl\:hidden { display: none !important; } - .xl\:block { display: block !important; } - .xl\:flex { display: flex !important; } - .xl\:grid { display: grid !important; } -} - -/* ================================ - FLEXBOX UTILITIES - ================================ */ -.flex-row { flex-direction: row; } -.flex-col { flex-direction: column; } -.flex-row-reverse { flex-direction: row-reverse; } -.flex-col-reverse { flex-direction: column-reverse; } - -.flex-wrap { flex-wrap: wrap; } -.flex-nowrap { flex-wrap: nowrap; } - -.items-start { align-items: flex-start; } -.items-center { align-items: center; } -.items-end { align-items: flex-end; } -.items-stretch { align-items: stretch; } - -.justify-start { justify-content: flex-start; } -.justify-center { justify-content: center; } -.justify-end { justify-content: flex-end; } -.justify-between { justify-content: space-between; } -.justify-around { justify-content: space-around; } -.justify-evenly { justify-content: space-evenly; } - -.flex-1 { flex: 1 1 0%; } -.flex-auto { flex: 1 1 auto; } -.flex-none { flex: none; } - -.gap-0 { gap: 0; } -.gap-1 { gap: var(--spacing-xs); } -.gap-2 { gap: var(--spacing-sm); } -.gap-3 { gap: var(--spacing-md); } -.gap-4 { gap: var(--spacing-lg); } -.gap-5 { gap: var(--spacing-xl); } -.gap-6 { gap: var(--spacing-2xl); } - -/* ================================ - GRID UTILITIES - ================================ */ -.grid-cols-1 { grid-template-columns: repeat(1, minmax(0, 1fr)); } -.grid-cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); } -.grid-cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); } -.grid-cols-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); } -.grid-cols-6 { grid-template-columns: repeat(6, minmax(0, 1fr)); } -.grid-cols-12 { grid-template-columns: repeat(12, minmax(0, 1fr)); } - -/* Responsive Grid */ -@media (min-width: 640px) { - .sm\:grid-cols-1 { grid-template-columns: repeat(1, minmax(0, 1fr)); } - .sm\:grid-cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); } - .sm\:grid-cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); } - .sm\:grid-cols-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); } -} - -@media (min-width: 768px) { - .md\:grid-cols-1 { grid-template-columns: repeat(1, minmax(0, 1fr)); } - .md\:grid-cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); } - .md\:grid-cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); } - .md\:grid-cols-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); } -} - -@media (min-width: 1024px) { - .lg\:grid-cols-1 { grid-template-columns: repeat(1, minmax(0, 1fr)); } - .lg\:grid-cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); } - .lg\:grid-cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); } - .lg\:grid-cols-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); } - .lg\:grid-cols-6 { grid-template-columns: repeat(6, minmax(0, 1fr)); } -} - -/* ================================ - SPACING UTILITIES - ================================ */ -/* Margin */ -.m-0 { margin: 0; } -.m-1 { margin: var(--spacing-xs); } -.m-2 { margin: var(--spacing-sm); } -.m-3 { margin: var(--spacing-md); } -.m-4 { margin: var(--spacing-lg); } -.m-5 { margin: var(--spacing-xl); } -.m-auto { margin: auto; } - -.mx-auto { margin-left: auto; margin-right: auto; } -.my-auto { margin-top: auto; margin-bottom: auto; } - -.mt-0 { margin-top: 0; } -.mt-1 { margin-top: var(--spacing-xs); } -.mt-2 { margin-top: var(--spacing-sm); } -.mt-3 { margin-top: var(--spacing-md); } -.mt-4 { margin-top: var(--spacing-lg); } -.mt-5 { margin-top: var(--spacing-xl); } - -.mb-0 { margin-bottom: 0; } -.mb-1 { margin-bottom: var(--spacing-xs); } -.mb-2 { margin-bottom: var(--spacing-sm); } -.mb-3 { margin-bottom: var(--spacing-md); } -.mb-4 { margin-bottom: var(--spacing-lg); } -.mb-5 { margin-bottom: var(--spacing-xl); } - -.ml-0 { margin-left: 0; } -.ml-1 { margin-left: var(--spacing-xs); } -.ml-2 { margin-left: var(--spacing-sm); } -.ml-3 { margin-left: var(--spacing-md); } -.ml-4 { margin-left: var(--spacing-lg); } -.ml-auto { margin-left: auto; } - -.mr-0 { margin-right: 0; } -.mr-1 { margin-right: var(--spacing-xs); } -.mr-2 { margin-right: var(--spacing-sm); } -.mr-3 { margin-right: var(--spacing-md); } -.mr-4 { margin-right: var(--spacing-lg); } -.mr-auto { margin-right: auto; } - -/* Padding */ -.p-0 { padding: 0; } -.p-1 { padding: var(--spacing-xs); } -.p-2 { padding: var(--spacing-sm); } -.p-3 { padding: var(--spacing-md); } -.p-4 { padding: var(--spacing-lg); } -.p-5 { padding: var(--spacing-xl); } - -.px-0 { padding-left: 0; padding-right: 0; } -.px-1 { padding-left: var(--spacing-xs); padding-right: var(--spacing-xs); } -.px-2 { padding-left: var(--spacing-sm); padding-right: var(--spacing-sm); } -.px-3 { padding-left: var(--spacing-md); padding-right: var(--spacing-md); } -.px-4 { padding-left: var(--spacing-lg); padding-right: var(--spacing-lg); } - -.py-0 { padding-top: 0; padding-bottom: 0; } -.py-1 { padding-top: var(--spacing-xs); padding-bottom: var(--spacing-xs); } -.py-2 { padding-top: var(--spacing-sm); padding-bottom: var(--spacing-sm); } -.py-3 { padding-top: var(--spacing-md); padding-bottom: var(--spacing-md); } -.py-4 { padding-top: var(--spacing-lg); padding-bottom: var(--spacing-lg); } - -/* ================================ - WIDTH & HEIGHT UTILITIES - ================================ */ -.w-full { width: 100%; } -.w-auto { width: auto; } -.w-1\/2 { width: 50%; } -.w-1\/3 { width: 33.333333%; } -.w-2\/3 { width: 66.666667%; } -.w-1\/4 { width: 25%; } -.w-3\/4 { width: 75%; } - -.h-full { height: 100%; } -.h-auto { height: auto; } -.h-screen { height: 100vh; } - -.min-h-screen { min-height: 100vh; } -.max-w-full { max-width: 100%; } - -/* ================================ - TEXT UTILITIES - ================================ */ -.text-left { text-align: left; } -.text-center { text-align: center; } -.text-right { text-align: right; } - -.text-xs { font-size: var(--font-xs); } -.text-sm { font-size: var(--font-sm); } -.text-base { font-size: var(--font-base); } -.text-lg { font-size: var(--font-lg); } -.text-xl { font-size: var(--font-xl); } -.text-2xl { font-size: var(--font-2xl); } -.text-3xl { font-size: var(--font-3xl); } - -.font-normal { font-weight: 400; } -.font-medium { font-weight: 500; } -.font-semibold { font-weight: 600; } -.font-bold { font-weight: 700; } - -.uppercase { text-transform: uppercase; } -.lowercase { text-transform: lowercase; } -.capitalize { text-transform: capitalize; } - -.truncate { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -/* ================================ - POSITION UTILITIES - ================================ */ -.relative { position: relative; } -.absolute { position: absolute; } -.fixed { position: fixed; } -.sticky { position: sticky; } - -.top-0 { top: 0; } -.right-0 { right: 0; } -.bottom-0 { bottom: 0; } -.left-0 { left: 0; } - -.z-0 { z-index: 0; } -.z-10 { z-index: 10; } -.z-20 { z-index: 20; } -.z-30 { z-index: 30; } -.z-40 { z-index: 40; } -.z-50 { z-index: 50; } - -/* ================================ - OVERFLOW UTILITIES - ================================ */ -.overflow-auto { overflow: auto; } -.overflow-hidden { overflow: hidden; } -.overflow-visible { overflow: visible; } -.overflow-scroll { overflow: scroll; } - -.overflow-x-auto { overflow-x: auto; } -.overflow-y-auto { overflow-y: auto; } - -/* ================================ - BORDER UTILITIES - ================================ */ -.rounded-none { border-radius: 0; } -.rounded-sm { border-radius: var(--radius-sm); } -.rounded { border-radius: var(--radius-md); } -.rounded-lg { border-radius: var(--radius-lg); } -.rounded-xl { border-radius: var(--radius-xl); } -.rounded-full { border-radius: var(--radius-full); } - -.border { border: 1px solid var(--border-color); } -.border-0 { border: 0; } -.border-2 { border: 2px solid var(--border-color); } - -/* ================================ - SHADOW UTILITIES - ================================ */ -.shadow-none { box-shadow: none; } -.shadow-sm { box-shadow: var(--shadow-sm); } -.shadow { box-shadow: var(--shadow-md); } -.shadow-lg { box-shadow: var(--shadow-lg); } -.shadow-xl { box-shadow: var(--shadow-xl); } - -/* ================================ - RESPONSIVE HELPERS - ================================ */ -/* Hide on mobile, show on desktop */ -.mobile-hidden { - display: none; -} - -@media (min-width: 768px) { - .mobile-hidden { - display: block; - } -} - -/* Show on mobile, hide on desktop */ -.desktop-hidden { - display: block; -} - -@media (min-width: 768px) { - .desktop-hidden { - display: none; - } -} - -/* Tablet specific */ -@media (min-width: 640px) and (max-width: 1023px) { - .tablet-only { - display: block; - } -} - -/* ================================ - CURSOR & POINTER EVENTS - ================================ */ -.cursor-pointer { cursor: pointer; } -.cursor-default { cursor: default; } -.cursor-not-allowed { cursor: not-allowed; } -.pointer-events-none { pointer-events: none; } - -/* ================================ - OPACITY UTILITIES - ================================ */ -.opacity-0 { opacity: 0; } -.opacity-25 { opacity: 0.25; } -.opacity-50 { opacity: 0.5; } -.opacity-75 { opacity: 0.75; } -.opacity-100 { opacity: 1; } - -/* ================================ - TRANSITION UTILITIES - ================================ */ -.transition { transition: all var(--transition-base); } -.transition-fast { transition: all var(--transition-fast); } -.transition-slow { transition: all var(--transition-slow); } -.transition-none { transition: none; } \ No newline at end of file diff --git a/static/css/vendor/vendor.css b/static/css/vendor/vendor.css deleted file mode 100644 index a684a443..00000000 --- a/static/css/vendor/vendor.css +++ /dev/null @@ -1,601 +0,0 @@ -/* static/css/vendor/vendor.css */ -/* Vendor-specific styles */ - -/* Vendor Header */ -.vendor-header { - background: white; - border-bottom: 1px solid var(--border-color); - padding: 16px 24px; - display: flex; - justify-content: space-between; - align-items: center; - box-shadow: var(--shadow-sm); - position: sticky; - top: 0; - z-index: 100; -} - -.vendor-header h1 { - font-size: var(--font-2xl); - color: var(--text-primary); - font-weight: 600; -} - -.vendor-logo { - display: flex; - align-items: center; - gap: var(--spacing-md); -} - -.vendor-logo-img { - width: 40px; - height: 40px; - border-radius: var(--radius-md); - object-fit: cover; -} - -.vendor-name { - font-size: var(--font-xl); - font-weight: 700; - color: var(--primary-color); -} - -/* Vendor Container */ -.vendor-container { - display: flex; - min-height: calc(100vh - 64px); -} - -/* Vendor Sidebar */ -.vendor-sidebar { - width: 260px; - background: white; - border-right: 1px solid var(--border-color); - padding: var(--spacing-lg) 0; - overflow-y: auto; -} - -.vendor-sidebar-header { - padding: 0 var(--spacing-lg) var(--spacing-lg); - border-bottom: 1px solid var(--border-color); - margin-bottom: var(--spacing-md); -} - -.vendor-status { - display: flex; - gap: var(--spacing-sm); - margin-top: var(--spacing-sm); -} - -/* Vendor Content */ -.vendor-content { - flex: 1; - padding: var(--spacing-lg); - overflow-y: auto; - background: var(--bg-secondary); -} - -/* Vendor Dashboard Widgets */ -.dashboard-widgets { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); - gap: var(--spacing-lg); - margin-bottom: var(--spacing-xl); -} - -.widget { - background: white; - border-radius: var(--radius-lg); - padding: var(--spacing-lg); - box-shadow: var(--shadow-md); - border: 1px solid var(--border-color); -} - -.widget-header { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: var(--spacing-md); -} - -.widget-title { - font-size: var(--font-lg); - font-weight: 600; - color: var(--text-primary); -} - -.widget-icon { - font-size: var(--font-2xl); - color: var(--primary-color); -} - -.widget-content { - margin-bottom: var(--spacing-md); -} - -.widget-footer { - padding-top: var(--spacing-md); - border-top: 1px solid var(--border-color); -} - -.widget-stat { - font-size: var(--font-4xl); - font-weight: 700; - color: var(--text-primary); - line-height: 1; - margin-bottom: var(--spacing-sm); -} - -.widget-label { - font-size: var(--font-sm); - color: var(--text-muted); -} - -/* Welcome Card */ -.welcome-card { - background: linear-gradient(135deg, var(--primary-color) 0%, var(--primary-dark) 100%); - color: white; - border-radius: var(--radius-lg); - padding: var(--spacing-xl); - box-shadow: var(--shadow-lg); - margin-bottom: var(--spacing-xl); - text-align: center; -} - -.welcome-icon { - font-size: 64px; - margin-bottom: var(--spacing-lg); - opacity: 0.9; -} - -.welcome-card h2 { - color: white; - font-size: var(--font-3xl); - margin-bottom: var(--spacing-md); -} - -.welcome-card p { - color: rgba(255, 255, 255, 0.9); - font-size: var(--font-lg); - margin-bottom: var(--spacing-sm); -} - -/* Vendor Info Card */ -.vendor-info-card { - background: white; - border-radius: var(--radius-lg); - padding: var(--spacing-lg); - margin-top: var(--spacing-lg); -} - -.info-item { - display: flex; - justify-content: space-between; - padding: 12px 0; - border-bottom: 1px solid var(--border-color); -} - -.info-item:last-child { - border-bottom: none; -} - -.info-label { - font-weight: 600; - color: var(--text-secondary); - font-size: var(--font-base); -} - -.info-value { - color: var(--text-primary); - font-size: var(--font-base); - text-align: right; -} - -/* Coming Soon Badge */ -.coming-soon { - background: linear-gradient(135deg, var(--primary-color) 0%, var(--primary-dark) 100%); - color: white; - padding: 12px 24px; - border-radius: var(--radius-lg); - display: inline-block; - font-weight: 600; - margin-top: var(--spacing-lg); - box-shadow: var(--shadow-md); -} - -/* Product Grid */ -.product-grid { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); - gap: var(--spacing-lg); - margin-bottom: var(--spacing-xl); -} - -.product-card { - background: white; - border-radius: var(--radius-lg); - overflow: hidden; - box-shadow: var(--shadow-md); - border: 1px solid var(--border-color); - transition: all var(--transition-base); - cursor: pointer; -} - -.product-card:hover { - transform: translateY(-4px); - box-shadow: var(--shadow-lg); -} - -.product-image { - width: 100%; - height: 200px; - object-fit: cover; - background: var(--gray-100); -} - -.product-info { - padding: var(--spacing-md); -} - -.product-title { - font-size: var(--font-lg); - font-weight: 600; - color: var(--text-primary); - margin-bottom: var(--spacing-sm); - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.product-price { - font-size: var(--font-xl); - font-weight: 700; - color: var(--primary-color); - margin-bottom: var(--spacing-sm); -} - -.product-description { - font-size: var(--font-sm); - color: var(--text-muted); - line-height: 1.5; - display: -webkit-box; - -webkit-line-clamp: 2; - -webkit-box-orient: vertical; - overflow: hidden; -} - -.product-actions { - padding: var(--spacing-md); - border-top: 1px solid var(--border-color); - display: flex; - gap: var(--spacing-sm); -} - -/* Order List */ -.order-list { - display: flex; - flex-direction: column; - gap: var(--spacing-md); -} - -.order-card { - background: white; - border-radius: var(--radius-lg); - padding: var(--spacing-lg); - box-shadow: var(--shadow-md); - border: 1px solid var(--border-color); - transition: all var(--transition-base); -} - -.order-card:hover { - box-shadow: var(--shadow-lg); -} - -.order-header { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: var(--spacing-md); - padding-bottom: var(--spacing-md); - border-bottom: 1px solid var(--border-color); -} - -.order-number { - font-size: var(--font-lg); - font-weight: 600; - color: var(--text-primary); -} - -.order-date { - font-size: var(--font-sm); - color: var(--text-muted); -} - -.order-body { - margin-bottom: var(--spacing-md); -} - -.order-items { - font-size: var(--font-sm); - color: var(--text-secondary); -} - -.order-footer { - display: flex; - justify-content: space-between; - align-items: center; -} - -.order-total { - font-size: var(--font-xl); - font-weight: 700; - color: var(--text-primary); -} - -/* Tabs */ -.tabs { - display: flex; - gap: var(--spacing-sm); - margin-bottom: var(--spacing-lg); - border-bottom: 2px solid var(--border-color); -} - -.tab { - padding: 12px 24px; - background: none; - border: none; - border-bottom: 2px solid transparent; - cursor: pointer; - font-size: var(--font-base); - font-weight: 500; - color: var(--text-secondary); - transition: all var(--transition-base); - margin-bottom: -2px; -} - -.tab:hover { - color: var(--primary-color); -} - -.tab.active { - color: var(--primary-color); - border-bottom-color: var(--primary-color); -} - -/* File Upload */ -.upload-area { - border: 2px dashed var(--border-color); - border-radius: var(--radius-lg); - padding: var(--spacing-xl); - text-align: center; - cursor: pointer; - transition: all var(--transition-base); -} - -.upload-area:hover, -.upload-area.dragover { - border-color: var(--primary-color); - background: rgba(102, 126, 234, 0.05); -} - -.upload-icon { - font-size: 48px; - color: var(--text-muted); - margin-bottom: var(--spacing-md); -} - -.upload-text { - font-size: var(--font-base); - color: var(--text-secondary); - margin-bottom: var(--spacing-sm); -} - -.upload-hint { - font-size: var(--font-sm); - color: var(--text-muted); -} - -.file-list { - margin-top: var(--spacing-md); -} - -.file-item { - display: flex; - justify-content: space-between; - align-items: center; - padding: var(--spacing-md); - background: var(--gray-50); - border-radius: var(--radius-md); - margin-bottom: var(--spacing-sm); -} - -.file-name { - font-size: var(--font-base); - color: var(--text-primary); -} - -.file-size { - font-size: var(--font-sm); - color: var(--text-muted); -} - -.file-remove { - background: none; - border: none; - color: var(--danger-color); - cursor: pointer; - font-size: var(--font-lg); - padding: var(--spacing-sm); -} - -/* Progress Bar */ -.progress { - width: 100%; - height: 8px; - background: var(--gray-200); - border-radius: var(--radius-full); - overflow: hidden; -} - -.progress-bar { - height: 100%; - background: linear-gradient(135deg, var(--primary-color) 0%, var(--primary-dark) 100%); - transition: width var(--transition-base); -} - -.progress-text { - text-align: center; - font-size: var(--font-sm); - color: var(--text-muted); - margin-top: var(--spacing-sm); -} - -/* Settings Form */ -.settings-section { - background: white; - border-radius: var(--radius-lg); - padding: var(--spacing-lg); - box-shadow: var(--shadow-md); - border: 1px solid var(--border-color); - margin-bottom: var(--spacing-lg); -} - -.settings-header { - margin-bottom: var(--spacing-lg); - padding-bottom: var(--spacing-md); - border-bottom: 2px solid var(--border-color); -} - -.settings-title { - font-size: var(--font-xl); - font-weight: 600; - color: var(--text-primary); - margin-bottom: var(--spacing-sm); -} - -.settings-description { - font-size: var(--font-sm); - color: var(--text-muted); -} - -.settings-row { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); - gap: var(--spacing-lg); - margin-bottom: var(--spacing-lg); -} - -/* Responsive Design */ -@media (max-width: 1024px) { - .vendor-sidebar { - width: 220px; - } - - .dashboard-widgets { - grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); - } - - .product-grid { - grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); - } -} - -@media (max-width: 768px) { - .vendor-container { - flex-direction: column; - } - - .vendor-sidebar { - width: 100%; - border-right: none; - border-bottom: 1px solid var(--border-color); - padding: var(--spacing-md) 0; - } - - .vendor-content { - padding: var(--spacing-md); - } - - .dashboard-widgets, - .product-grid { - grid-template-columns: 1fr; - } - - .welcome-card { - padding: var(--spacing-lg); - } - - .welcome-icon { - font-size: 48px; - } - - .welcome-card h2 { - font-size: var(--font-2xl); - } - - .tabs { - overflow-x: auto; - -webkit-overflow-scrolling: touch; - } - - .tab { - white-space: nowrap; - } - - .settings-row { - grid-template-columns: 1fr; - } -} - -@media (max-width: 480px) { - .vendor-header { - padding: 12px 16px; - } - - .vendor-header h1 { - font-size: var(--font-lg); - } - - .vendor-name { - font-size: var(--font-lg); - } - - .widget-stat { - font-size: var(--font-3xl); - } - - .settings-section { - padding: var(--spacing-md); - } - - .info-item { - flex-direction: column; - gap: var(--spacing-xs); - } - - .info-value { - text-align: left; - } -} - -/* Print Styles */ -@media print { - .vendor-sidebar, - .vendor-header .header-right, - .product-actions, - .order-footer .btn { - display: none; - } - - .vendor-content { - padding: 0; - } - - .settings-section, - .order-card, - .product-card { - box-shadow: none; - border: 1px solid var(--border-color); - break-inside: avoid; - } -} \ No newline at end of file diff --git a/static/js/shared/alpine-components.js b/static/js/shared/alpine-components.js deleted file mode 100644 index 6080b090..00000000 --- a/static/js/shared/alpine-components.js +++ /dev/null @@ -1,581 +0,0 @@ -/** - * Alpine.js Components for Multi-Tenant E-commerce Platform - * Universal component system for Admin, Vendor, and Shop sections - */ - -// ============================================================================= -// BASE MODAL SYSTEM -// Universal modal functions used by all sections -// ============================================================================= - -window.baseModalSystem = function() { - return { - // Confirmation Modal State - confirmModal: { - show: false, - title: '', - message: '', - warning: '', - buttonText: 'Confirm', - buttonClass: 'btn-danger', - onConfirm: null, - onCancel: null - }, - - // Success Modal State - successModal: { - show: false, - title: 'Success', - message: '', - redirectUrl: null, - redirectDelay: 2000 - }, - - // Error Modal State - errorModal: { - show: false, - title: 'Error', - message: '', - details: '' - }, - - // Loading State - loading: false, - - /** - * Show confirmation modal - * @param {Object} options - Modal configuration - */ - showConfirmModal(options) { - this.confirmModal = { - show: true, - title: options.title || 'Confirm Action', - message: options.message || 'Are you sure?', - warning: options.warning || '', - buttonText: options.buttonText || 'Confirm', - buttonClass: options.buttonClass || 'btn-danger', - onConfirm: options.onConfirm || null, - onCancel: options.onCancel || null - }; - }, - - /** - * Close confirmation modal - */ - closeConfirmModal() { - if (this.confirmModal.onCancel) { - this.confirmModal.onCancel(); - } - this.confirmModal.show = false; - }, - - /** - * Handle confirmation action - */ - async handleConfirm() { - if (this.confirmModal.onConfirm) { - this.closeConfirmModal(); - await this.confirmModal.onConfirm(); - } - }, - - /** - * Show success modal - * @param {Object} options - Modal configuration - */ - showSuccessModal(options) { - this.successModal = { - show: true, - title: options.title || 'Success', - message: options.message || 'Operation completed successfully', - redirectUrl: options.redirectUrl || null, - redirectDelay: options.redirectDelay || 2000 - }; - - // Auto-redirect if URL provided - if (this.successModal.redirectUrl) { - setTimeout(() => { - window.location.href = this.successModal.redirectUrl; - }, this.successModal.redirectDelay); - } - }, - - /** - * Close success modal - */ - closeSuccessModal() { - this.successModal.show = false; - }, - - /** - * Show error modal - * @param {Object} options - Modal configuration - */ - showErrorModal(options) { - this.errorModal = { - show: true, - title: options.title || 'Error', - message: options.message || 'An error occurred', - details: options.details || '' - }; - }, - - /** - * Close error modal - */ - closeErrorModal() { - this.errorModal.show = false; - }, - - /** - * Show loading overlay - */ - showLoading() { - this.loading = true; - }, - - /** - * Hide loading overlay - */ - hideLoading() { - this.loading = false; - } - }; -}; - -// ============================================================================= -// ADMIN LAYOUT COMPONENT -// Header, Sidebar, Navigation, Modals for Admin Section -// ============================================================================= - -window.adminLayout = function() { - return { - ...window.baseModalSystem(), - - // Admin-specific state - user: null, - menuOpen: false, - currentPage: '', - - /** - * Initialize admin layout - */ - async init() { - this.currentPage = this.getCurrentPage(); - await this.loadUserData(); - }, - - /** - * Load current admin user data - */ - async loadUserData() { - try { - const response = await apiClient.get('/admin/auth/me'); - this.user = response; - } catch (error) { - console.error('Failed to load user data:', error); - // Redirect to login if not authenticated - if (error.status === 401) { - window.location.href = '/admin/login.html'; - } - } - }, - - /** - * Get current page name from URL - */ - getCurrentPage() { - const path = window.location.pathname; - const page = path.split('/').pop().replace('.html', ''); - return page || 'dashboard'; - }, - - /** - * Check if menu item is active - */ - isActive(page) { - return this.currentPage === page; - }, - - /** - * Toggle mobile menu - */ - toggleMenu() { - this.menuOpen = !this.menuOpen; - }, - - /** - * Show logout confirmation - */ - confirmLogout() { - this.showConfirmModal({ - title: 'Confirm Logout', - message: 'Are you sure you want to logout?', - buttonText: 'Logout', - buttonClass: 'btn-primary', - onConfirm: () => this.logout() - }); - }, - - /** - * Perform logout - */ - async logout() { - try { - this.showLoading(); - await apiClient.post('/admin/auth/logout'); - window.location.href = '/admin/login.html'; - } catch (error) { - this.hideLoading(); - this.showErrorModal({ - message: 'Logout failed', - details: error.message - }); - } - } - }; -}; - -// ============================================================================= -// VENDOR LAYOUT COMPONENT -// Header, Sidebar, Navigation, Modals for Vendor Dashboard -// ============================================================================= - -window.vendorLayout = function() { - return { - ...window.baseModalSystem(), - - // Vendor-specific state - user: null, - vendor: null, - menuOpen: false, - currentPage: '', - - /** - * Initialize vendor layout - */ - async init() { - this.currentPage = this.getCurrentPage(); - await this.loadUserData(); - }, - - /** - * Load current vendor user data - */ - async loadUserData() { - try { - const response = await apiClient.get('/vendor/auth/me'); - this.user = response.user; - this.vendor = response.vendor; - } catch (error) { - console.error('Failed to load user data:', error); - if (error.status === 401) { - window.location.href = '/vendor/login.html'; - } - } - }, - - /** - * Get current page name from URL - */ - getCurrentPage() { - const path = window.location.pathname; - const page = path.split('/').pop().replace('.html', ''); - return page || 'dashboard'; - }, - - /** - * Check if menu item is active - */ - isActive(page) { - return this.currentPage === page; - }, - - /** - * Toggle mobile menu - */ - toggleMenu() { - this.menuOpen = !this.menuOpen; - }, - - /** - * Show logout confirmation - */ - confirmLogout() { - this.showConfirmModal({ - title: 'Confirm Logout', - message: 'Are you sure you want to logout?', - buttonText: 'Logout', - buttonClass: 'btn-primary', - onConfirm: () => this.logout() - }); - }, - - /** - * Perform logout - */ - async logout() { - try { - this.showLoading(); - await apiClient.post('/vendor/auth/logout'); - window.location.href = '/vendor/login.html'; - } catch (error) { - this.hideLoading(); - this.showErrorModal({ - message: 'Logout failed', - details: error.message - }); - } - } - }; -}; - -// ============================================================================= -// SHOP LAYOUT COMPONENT -// Header, Cart, Search, Navigation for Customer-Facing Shop -// ============================================================================= - -window.shopLayout = function() { - return { - ...window.baseModalSystem(), - - // Shop-specific state - vendor: null, - cart: null, - cartCount: 0, - sessionId: null, - searchQuery: '', - mobileMenuOpen: false, - - /** - * Initialize shop layout - */ - async init() { - this.sessionId = this.getOrCreateSessionId(); - await this.detectVendor(); - if (this.vendor) { - await this.loadCart(); - } - }, - - /** - * Detect vendor from subdomain or vendor code - */ - async detectVendor() { - try { - const hostname = window.location.hostname; - const subdomain = hostname.split('.')[0]; - - // Try to get vendor by subdomain first - if (subdomain && subdomain !== 'localhost' && subdomain !== 'www') { - this.vendor = await apiClient.get(`/public/vendors/by-subdomain/${subdomain}`); - } else { - // Fallback: Try to get vendor code from URL or localStorage - const urlParams = new URLSearchParams(window.location.search); - const vendorCode = urlParams.get('vendor') || localStorage.getItem('vendorCode'); - - if (vendorCode) { - this.vendor = await apiClient.get(`/public/vendors/by-code/${vendorCode}`); - localStorage.setItem('vendorCode', vendorCode); - } - } - } catch (error) { - console.error('Failed to detect vendor:', error); - this.showErrorModal({ - message: 'Vendor not found', - details: 'Unable to identify the store. Please check the URL.' - }); - } - }, - - /** - * Get or create session ID for cart - */ - getOrCreateSessionId() { - let sessionId = localStorage.getItem('cartSessionId'); - if (!sessionId) { - sessionId = 'session_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9); - localStorage.setItem('cartSessionId', sessionId); - } - return sessionId; - }, - - /** - * Load cart from API - */ - async loadCart() { - if (!this.vendor) return; - - try { - this.cart = await apiClient.get( - `/public/vendors/${this.vendor.id}/cart/${this.sessionId}` - ); - this.updateCartCount(); - } catch (error) { - console.error('Failed to load cart:', error); - this.cart = { items: [] }; - this.cartCount = 0; - } - }, - - /** - * Update cart item count - */ - updateCartCount() { - if (this.cart && this.cart.items) { - this.cartCount = this.cart.items.reduce((sum, item) => sum + item.quantity, 0); - } else { - this.cartCount = 0; - } - }, - - /** - * Add item to cart - */ - async addToCart(productId, quantity = 1) { - if (!this.vendor) { - this.showErrorModal({ message: 'Vendor not found' }); - return; - } - - try { - this.showLoading(); - await apiClient.post( - `/public/vendors/${this.vendor.id}/cart/${this.sessionId}/items`, - { product_id: productId, quantity } - ); - await this.loadCart(); - this.hideLoading(); - this.showSuccessModal({ - title: 'Added to Cart', - message: 'Product added successfully' - }); - } catch (error) { - this.hideLoading(); - this.showErrorModal({ - message: 'Failed to add to cart', - details: error.message - }); - } - }, - - /** - * Toggle mobile menu - */ - toggleMobileMenu() { - this.mobileMenuOpen = !this.mobileMenuOpen; - }, - - /** - * Handle search - */ - handleSearch() { - if (this.searchQuery.trim()) { - window.location.href = `/shop/products.html?search=${encodeURIComponent(this.searchQuery)}`; - } - }, - - /** - * Go to cart page - */ - goToCart() { - window.location.href = '/shop/cart.html'; - } - }; -}; - -// ============================================================================= -// SHOP ACCOUNT LAYOUT COMPONENT -// Layout for customer account area (orders, profile, addresses) -// ============================================================================= - -window.shopAccountLayout = function() { - return { - ...window.shopLayout(), - - // Account-specific state - customer: null, - currentPage: '', - - /** - * Initialize shop account layout - */ - async init() { - this.currentPage = this.getCurrentPage(); - this.sessionId = this.getOrCreateSessionId(); - await this.detectVendor(); - await this.loadCustomerData(); - if (this.vendor) { - await this.loadCart(); - } - }, - - /** - * Load customer data - */ - async loadCustomerData() { - if (!this.vendor) return; - - try { - const response = await apiClient.get( - `/public/vendors/${this.vendor.id}/customers/me` - ); - this.customer = response; - } catch (error) { - console.error('Failed to load customer data:', error); - // Redirect to login if not authenticated - if (error.status === 401) { - window.location.href = `/shop/account/login.html?redirect=${encodeURIComponent(window.location.pathname)}`; - } - } - }, - - /** - * Get current page name from URL - */ - getCurrentPage() { - const path = window.location.pathname; - const page = path.split('/').pop().replace('.html', ''); - return page || 'orders'; - }, - - /** - * Check if menu item is active - */ - isActive(page) { - return this.currentPage === page; - }, - - /** - * Show logout confirmation - */ - confirmLogout() { - this.showConfirmModal({ - title: 'Confirm Logout', - message: 'Are you sure you want to logout?', - buttonText: 'Logout', - buttonClass: 'btn-primary', - onConfirm: () => this.logoutCustomer() - }); - }, - - /** - * Perform customer logout - */ - async logoutCustomer() { - if (!this.vendor) return; - - try { - this.showLoading(); - await apiClient.post(`/public/vendors/${this.vendor.id}/customers/logout`); - window.location.href = '/shop/home.html'; - } catch (error) { - this.hideLoading(); - this.showErrorModal({ - message: 'Logout failed', - details: error.message - }); - } - } - }; -}; \ No newline at end of file diff --git a/static/js/shared/media-upload.js b/static/js/shared/media-upload.js deleted file mode 100644 index cabbf399..00000000 --- a/static/js/shared/media-upload.js +++ /dev/null @@ -1 +0,0 @@ -// File upload utilities diff --git a/static/js/shared/modal-system.js b/static/js/shared/modal-system.js deleted file mode 100644 index 0ae3fbf6..00000000 --- a/static/js/shared/modal-system.js +++ /dev/null @@ -1,209 +0,0 @@ -/** - * Modal System Helper Functions - * Utility functions for modal operations across all sections - */ - -window.modalHelpers = { - /** - * Show a simple confirmation dialog - * Returns a Promise that resolves with true/false - */ - async confirm(options) { - return new Promise((resolve) => { - const component = Alpine.$data(document.body); - - component.showConfirmModal({ - title: options.title || 'Confirm Action', - message: options.message || 'Are you sure?', - warning: options.warning || '', - buttonText: options.buttonText || 'Confirm', - buttonClass: options.buttonClass || 'btn-danger', - onConfirm: () => resolve(true), - onCancel: () => resolve(false) - }); - }); - }, - - /** - * Show a success message - */ - success(message, options = {}) { - const component = Alpine.$data(document.body); - - component.showSuccessModal({ - title: options.title || 'Success', - message: message, - redirectUrl: options.redirectUrl || null, - redirectDelay: options.redirectDelay || 2000 - }); - }, - - /** - * Show an error message - */ - error(message, details = '') { - const component = Alpine.$data(document.body); - - component.showErrorModal({ - title: 'Error', - message: message, - details: details - }); - }, - - /** - * Show API error with proper formatting - */ - apiError(error) { - const component = Alpine.$data(document.body); - - let message = 'An error occurred'; - let details = ''; - - if (error.message) { - message = error.message; - } - - if (error.details) { - details = typeof error.details === 'string' - ? error.details - : JSON.stringify(error.details, null, 2); - } else if (error.error_code) { - details = `Error Code: ${error.error_code}`; - } - - component.showErrorModal({ - title: 'Error', - message: message, - details: details - }); - }, - - /** - * Show loading overlay - */ - showLoading() { - const component = Alpine.$data(document.body); - component.showLoading(); - }, - - /** - * Hide loading overlay - */ - hideLoading() { - const component = Alpine.$data(document.body); - component.hideLoading(); - }, - - /** - * Execute an async operation with loading state - */ - async withLoading(asyncFunction) { - try { - this.showLoading(); - const result = await asyncFunction(); - return result; - } finally { - this.hideLoading(); - } - }, - - /** - * Execute an async operation with error handling - */ - async withErrorHandling(asyncFunction, errorMessage = 'Operation failed') { - try { - return await asyncFunction(); - } catch (error) { - console.error('Operation error:', error); - this.apiError({ - message: errorMessage, - details: error.message || error.toString() - }); - throw error; - } - }, - - /** - * Execute an async operation with both loading and error handling - */ - async execute(asyncFunction, options = {}) { - const { - errorMessage = 'Operation failed', - successMessage = null, - redirectUrl = null - } = options; - - try { - this.showLoading(); - const result = await asyncFunction(); - - if (successMessage) { - this.success(successMessage, { redirectUrl }); - } - - return result; - } catch (error) { - console.error('Operation error:', error); - this.apiError({ - message: errorMessage, - details: error.message || error.toString() - }); - throw error; - } finally { - this.hideLoading(); - } - }, - - /** - * Confirm a destructive action - */ - async confirmDelete(itemName, itemType = 'item') { - return this.confirm({ - title: `Delete ${itemType}`, - message: `Are you sure you want to delete "${itemName}"?`, - warning: 'This action cannot be undone.', - buttonText: 'Delete', - buttonClass: 'btn-danger' - }); - }, - - /** - * Confirm logout - */ - async confirmLogout() { - return this.confirm({ - title: 'Confirm Logout', - message: 'Are you sure you want to logout?', - buttonText: 'Logout', - buttonClass: 'btn-primary' - }); - }, - - /** - * Show validation errors - */ - validationError(errors) { - let message = 'Please correct the following errors:'; - let details = ''; - - if (Array.isArray(errors)) { - details = errors.join('\n'); - } else if (typeof errors === 'object') { - details = Object.entries(errors) - .map(([field, error]) => `${field}: ${error}`) - .join('\n'); - } else { - details = errors.toString(); - } - - this.error(message, details); - } -}; - -// Shorthand aliases for convenience -window.showConfirm = window.modalHelpers.confirm.bind(window.modalHelpers); -window.showSuccess = window.modalHelpers.success.bind(window.modalHelpers); -window.showError = window.modalHelpers.error.bind(window.modalHelpers); -window.showLoading = window.modalHelpers.showLoading.bind(window.modalHelpers); -window.hideLoading = window.modalHelpers.hideLoading.bind(window.modalHelpers); \ No newline at end of file diff --git a/static/js/shared/modal-templates.js b/static/js/shared/modal-templates.js deleted file mode 100644 index 355a8eeb..00000000 --- a/static/js/shared/modal-templates.js +++ /dev/null @@ -1,114 +0,0 @@ -/** - * Universal Modal Templates - * Shared across all sections: Admin, Vendor, and Shop - */ - -window.modalTemplates = { - - /** - * Confirmation Modal - */ - confirmModal: () => ` - - `, - - /** - * Success Modal - */ - successModal: () => ` - - `, - - /** - * Error Modal - */ - errorModal: () => ` - - `, - - /** - * Loading Overlay - */ - loadingOverlay: () => ` -
-
-
- ` -}; \ No newline at end of file diff --git a/static/js/shared/notification.js b/static/js/shared/notification.js deleted file mode 100644 index 787cf2d7..00000000 --- a/static/js/shared/notification.js +++ /dev/null @@ -1 +0,0 @@ -// Notification handling diff --git a/static/js/shared/search.js b/static/js/shared/search.js deleted file mode 100644 index adc32660..00000000 --- a/static/js/shared/search.js +++ /dev/null @@ -1 +0,0 @@ -// Search functionality diff --git a/static/js/shared/vendor-context.js b/static/js/shared/vendor-context.js deleted file mode 100644 index 28e82f06..00000000 --- a/static/js/shared/vendor-context.js +++ /dev/null @@ -1 +0,0 @@ -// Vendor context detection and management diff --git a/static/js/shop/account.js b/static/js/shop/account.js deleted file mode 100644 index c573b782..00000000 --- a/static/js/shop/account.js +++ /dev/null @@ -1 +0,0 @@ -// Customer account diff --git a/static/js/shop/cart.js b/static/js/shop/cart.js deleted file mode 100644 index 9ab57ccb..00000000 --- a/static/js/shop/cart.js +++ /dev/null @@ -1 +0,0 @@ -// Shopping cart diff --git a/static/js/shop/catalog.js b/static/js/shop/catalog.js deleted file mode 100644 index b0d701ed..00000000 --- a/static/js/shop/catalog.js +++ /dev/null @@ -1 +0,0 @@ -// Product browsing diff --git a/static/js/shop/checkout.js b/static/js/shop/checkout.js deleted file mode 100644 index dfe60dd2..00000000 --- a/static/js/shop/checkout.js +++ /dev/null @@ -1 +0,0 @@ -// Checkout process diff --git a/static/js/shop/search.js b/static/js/shop/search.js deleted file mode 100644 index 7ec3d1e3..00000000 --- a/static/js/shop/search.js +++ /dev/null @@ -1 +0,0 @@ -// Product search diff --git a/static/js/shop/shop-layout-templates.js b/static/js/shop/shop-layout-templates.js deleted file mode 100644 index 680ff658..00000000 --- a/static/js/shop/shop-layout-templates.js +++ /dev/null @@ -1,103 +0,0 @@ -/** - * Shop Layout Templates - * Header and Navigation specific to Customer-Facing Shop - */ - -window.shopLayoutTemplates = { - - /** - * Shop Header - */ - header: () => ` -
-
- - - - - - -
- - - Account - -
-
- - - -
- `, - - /** - * Shop Account Sidebar - */ - accountSidebar: () => ` - - ` -}; \ No newline at end of file diff --git a/static/js/vendor/dashboard.js b/static/js/vendor/dashboard.js deleted file mode 100644 index f93705b4..00000000 --- a/static/js/vendor/dashboard.js +++ /dev/null @@ -1,113 +0,0 @@ -// Vendor Dashboard Component -function vendorDashboard() { - return { - currentUser: {}, - vendor: null, - vendorRole: '', - currentSection: 'dashboard', - loading: false, - stats: { - products_count: 0, - orders_count: 0, - customers_count: 0, - revenue: 0 - }, - - init() { - if (!this.checkAuth()) { - return; - } - this.loadDashboard(); - }, - - checkAuth() { - const token = localStorage.getItem('vendor_token'); - const user = localStorage.getItem('vendor_user'); - const vendorContext = localStorage.getItem('vendor_context'); - const vendorRole = localStorage.getItem('vendor_role'); - - if (!token || !user || !vendorContext) { - // Get vendor code from URL - const vendorCode = this.getVendorCodeFromUrl(); - const redirectUrl = vendorCode ? - `/vendor/${vendorCode}/login` : - '/static/vendor/login.html'; - window.location.href = redirectUrl; - return false; - } - - try { - this.currentUser = JSON.parse(user); - this.vendor = JSON.parse(vendorContext); - this.vendorRole = vendorRole || 'Member'; - return true; - } catch (e) { - console.error('Error parsing stored data:', e); - localStorage.removeItem('vendor_token'); - localStorage.removeItem('vendor_user'); - localStorage.removeItem('vendor_context'); - localStorage.removeItem('vendor_role'); - window.location.href = '/static/vendor/login.html'; - return false; - } - }, - - getVendorCodeFromUrl() { - // Try to get vendor code from URL path - const pathParts = window.location.pathname.split('/').filter(p => p); - const vendorIndex = pathParts.indexOf('vendor'); - if (vendorIndex !== -1 && pathParts[vendorIndex + 1]) { - const code = pathParts[vendorIndex + 1]; - if (!['login', 'dashboard', 'admin', 'products', 'orders'].includes(code.toLowerCase())) { - return code.toUpperCase(); - } - } - - // Fallback to query parameter - const urlParams = new URLSearchParams(window.location.search); - return urlParams.get('vendor'); - }, - - async handleLogout() { - const confirmed = await Utils.confirm( - 'Are you sure you want to logout?', - 'Confirm Logout' - ); - - if (confirmed) { - localStorage.removeItem('vendor_token'); - localStorage.removeItem('vendor_user'); - localStorage.removeItem('vendor_context'); - localStorage.removeItem('vendor_role'); - - Utils.showToast('Logged out successfully', 'success', 2000); - - setTimeout(() => { - window.location.href = `/vendor/${this.vendor.vendor_code}/login`; - }, 500); - } - }, - - async loadDashboard() { - this.loading = true; - try { - // In future slices, load actual dashboard data - // const data = await apiClient.get(`/vendor/dashboard/stats`); - // this.stats = data; - - // For now, show placeholder data - this.stats = { - products_count: 0, - orders_count: 0, - customers_count: 0, - revenue: 0 - }; - } catch (error) { - console.error('Failed to load dashboard:', error); - Utils.showToast('Failed to load dashboard data', 'error'); - } finally { - this.loading = false; - } - } - } -} \ No newline at end of file diff --git a/static/js/vendor/login.js b/static/js/vendor/login.js deleted file mode 100644 index edf64183..00000000 --- a/static/js/vendor/login.js +++ /dev/null @@ -1,170 +0,0 @@ -// Vendor Login Component -function vendorLogin() { - return { - vendor: null, - credentials: { - username: '', - password: '' - }, - loading: false, - checked: false, - error: null, - success: null, - errors: {}, - - init() { - // Check if already logged in - if (this.checkExistingAuth()) { - return; - } - - // Detect vendor from URL - this.detectVendor(); - }, - - checkExistingAuth() { - const token = localStorage.getItem('vendor_token'); - const vendorContext = localStorage.getItem('vendor_context'); - - if (token && vendorContext) { - try { - const vendor = JSON.parse(vendorContext); - window.location.href = `/vendor/${vendor.vendor_code}/dashboard`; - return true; - } catch (e) { - localStorage.removeItem('vendor_token'); - localStorage.removeItem('vendor_context'); - } - } - return false; - }, - - async detectVendor() { - this.loading = true; - - try { - const vendorCode = this.getVendorCodeFromUrl(); - - if (!vendorCode) { - this.error = 'Vendor code not found in URL. Please use the correct vendor login link.'; - this.checked = true; - this.loading = false; - return; - } - - console.log('Detected vendor code:', vendorCode); - - // Fetch vendor information - const response = await fetch(`/api/v1/public/vendors/by-code/${vendorCode}`); - - if (!response.ok) { - throw new Error('Vendor not found'); - } - - this.vendor = await response.json(); - this.checked = true; - console.log('Loaded vendor:', this.vendor); - - } catch (error) { - console.error('Error detecting vendor:', error); - this.error = 'Unable to load vendor information. The vendor may not exist or is inactive.'; - this.checked = true; - } finally { - this.loading = false; - } - }, - - getVendorCodeFromUrl() { - // Try multiple methods to get vendor code - - // Method 1: From URL path /vendor/VENDORCODE/login or /vendor/VENDORCODE/ - const pathParts = window.location.pathname.split('/').filter(p => p); - const vendorIndex = pathParts.indexOf('vendor'); - if (vendorIndex !== -1 && pathParts[vendorIndex + 1]) { - const code = pathParts[vendorIndex + 1]; - // Don't return if it's a generic route like 'login', 'dashboard', etc. - if (!['login', 'dashboard', 'admin', 'products', 'orders'].includes(code.toLowerCase())) { - return code.toUpperCase(); - } - } - - // Method 2: From query parameter ?vendor=VENDORCODE - const urlParams = new URLSearchParams(window.location.search); - const queryVendor = urlParams.get('vendor'); - if (queryVendor) { - return queryVendor.toUpperCase(); - } - - // Method 3: From subdomain (for production) - const hostname = window.location.hostname; - const parts = hostname.split('.'); - if (parts.length > 2 && parts[0] !== 'www') { - // Assume subdomain is vendor code - return parts[0].toUpperCase(); - } - - return null; - }, - - clearErrors() { - this.error = null; - this.errors = {}; - }, - - validateForm() { - this.clearErrors(); - let isValid = true; - - if (!this.credentials.username.trim()) { - this.errors.username = 'Username is required'; - isValid = false; - } - - if (!this.credentials.password) { - this.errors.password = 'Password is required'; - isValid = false; - } - - return isValid; - }, - - async handleLogin() { - if (!this.validateForm()) { - return; - } - - this.loading = true; - this.clearErrors(); - - try { - const response = await apiClient.post('/vendor/auth/login', { - username: this.credentials.username.trim(), - password: this.credentials.password, - vendor_code: this.vendor.vendor_code - }); - - // Store authentication data - localStorage.setItem('vendor_token', response.access_token); - localStorage.setItem('vendor_user', JSON.stringify(response.user)); - localStorage.setItem('vendor_context', JSON.stringify(response.vendor)); - localStorage.setItem('vendor_role', response.vendor_role); - - // Show success message - this.success = 'Login successful! Redirecting...'; - Utils.showToast('Login successful!', 'success', 2000); - - // Redirect after short delay - setTimeout(() => { - window.location.href = `/vendor/${this.vendor.vendor_code}/dashboard`; - }, 1000); - - } catch (error) { - console.error('Login error:', error); - this.error = error.message || 'Login failed. Please check your credentials.'; - Utils.showToast(this.error, 'error'); - } finally { - this.loading = false; - } - } - } -} \ No newline at end of file diff --git a/static/js/vendor/marketplace.js b/static/js/vendor/marketplace.js deleted file mode 100644 index 9b6ade64..00000000 --- a/static/js/vendor/marketplace.js +++ /dev/null @@ -1 +0,0 @@ -// Marketplace integration diff --git a/static/js/vendor/media.js b/static/js/vendor/media.js deleted file mode 100644 index 853f78a3..00000000 --- a/static/js/vendor/media.js +++ /dev/null @@ -1 +0,0 @@ -// Media management diff --git a/static/js/vendor/orders.js b/static/js/vendor/orders.js deleted file mode 100644 index 917f7f8a..00000000 --- a/static/js/vendor/orders.js +++ /dev/null @@ -1 +0,0 @@ -// Order management diff --git a/static/js/vendor/payments.js b/static/js/vendor/payments.js deleted file mode 100644 index 4c9efd9a..00000000 --- a/static/js/vendor/payments.js +++ /dev/null @@ -1 +0,0 @@ -// Payment configuration diff --git a/static/js/vendor/products.js b/static/js/vendor/products.js deleted file mode 100644 index c3f4a0b4..00000000 --- a/static/js/vendor/products.js +++ /dev/null @@ -1 +0,0 @@ -// Catalog management diff --git a/static/js/vendor/vendor-layout-templates.js b/static/js/vendor/vendor-layout-templates.js deleted file mode 100644 index 162c4d6e..00000000 --- a/static/js/vendor/vendor-layout-templates.js +++ /dev/null @@ -1,67 +0,0 @@ -/** - * Vendor Layout Templates - * Header and Sidebar specific to Vendor Dashboard - */ - -window.vendorLayoutTemplates = { - - /** - * Vendor Header - */ - header: () => ` -
-
- -

Vendor Dashboard

-
-
- - -
-
- `, - - /** - * Vendor Sidebar - */ - sidebar: () => ` - - ` -}; \ No newline at end of file diff --git a/static/shared/js/icons.js b/static/shared/js/icons.js index c8455bd6..402b8a5d 100644 --- a/static/shared/js/icons.js +++ b/static/shared/js/icons.js @@ -107,7 +107,13 @@ const Icons = { 'database': ``, 'light-bulb': ``, 'book-open': ``, - 'play': `` + 'play': ``, + + // Additional UI Icons + 'table': ``, + 'cursor-click': ``, + 'view-grid-add': ``, + 'code': `` }; /** diff --git a/static/shop/account/addresses.html b/static/shop/account/addresses.html deleted file mode 100644 index 0ca25b1b..00000000 --- a/static/shop/account/addresses.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - Address management - - - <-- Address management --> - - diff --git a/static/shop/account/login.html b/static/shop/account/login.html deleted file mode 100644 index 150e8934..00000000 --- a/static/shop/account/login.html +++ /dev/null @@ -1,249 +0,0 @@ - - - - - <!DOCTYPE html> -<html lang="en"> -<head> - <meta charset="UTF-8"> - <meta name="viewport" content="width=device-width, initial-scale=1.0"> - <title>Login - {{ vendor.name }} - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/static/shop/account/orders.html b/static/shop/account/orders.html deleted file mode 100644 index 909a3bf9..00000000 --- a/static/shop/account/orders.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - Order history - - - <-- Order history --> - - diff --git a/static/shop/account/profile.html b/static/shop/account/profile.html deleted file mode 100644 index 96b6d750..00000000 --- a/static/shop/account/profile.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - Customer profile - - - <-- Customer profile --> - - diff --git a/static/shop/account/register.html b/static/shop/account/register.html deleted file mode 100644 index 56ff2d05..00000000 --- a/static/shop/account/register.html +++ /dev/null @@ -1,341 +0,0 @@ - - - - - - Create Account - {{ vendor.name }} - - - - - - - - - - diff --git a/static/shop/cart.html b/static/shop/cart.html deleted file mode 100644 index cc14648a..00000000 --- a/static/shop/cart.html +++ /dev/null @@ -1,489 +0,0 @@ - - - - - - Shopping Cart - {{ vendor.name }} - - - - - -
- -
-
-

🛒 Shopping Cart

-
- -
- -
- -
-
-

Loading your cart...

-
- - -
-
🛒
-

Your cart is empty

-

Add some products to get started!

- Browse Products -
- - -
- -
- -
- - -
-
-

Order Summary

- -
- Subtotal ( items): - -
- -
- Shipping: - -
- -
- Total: - -
- - - - - Continue Shopping - -
-
-
-
-
- - - - - - \ No newline at end of file diff --git a/static/shop/checkout.html b/static/shop/checkout.html deleted file mode 100644 index 3270d419..00000000 --- a/static/shop/checkout.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - Checkout process - - - <-- Checkout process --> - - diff --git a/static/shop/home.html b/static/shop/home.html deleted file mode 100644 index 8e463635..00000000 --- a/static/shop/home.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - Shop homepage - - - <-- Shop homepage --> - - diff --git a/static/shop/js/shop-layout.js b/static/shop/js/shop-layout.js new file mode 100644 index 00000000..a16b5194 --- /dev/null +++ b/static/shop/js/shop-layout.js @@ -0,0 +1,228 @@ +// static/shop/js/shop-layout.js +/** + * Shop Layout Component + * Provides base functionality for vendor shop pages + * Works with vendor-specific themes + */ + +const shopLog = { + info: (...args) => console.info('🛒 [SHOP]', ...args), + warn: (...args) => console.warn('⚠️ [SHOP]', ...args), + error: (...args) => console.error('❌ [SHOP]', ...args), + debug: (...args) => console.log('🔍 [SHOP]', ...args) +}; + +/** + * Shop Layout Data + * Base Alpine.js component for shop pages + */ +function shopLayoutData() { + return { + // Theme state + dark: localStorage.getItem('shop-theme') === 'dark', + + // UI state + mobileMenuOpen: false, + searchOpen: false, + cartCount: 0, + + // Cart state + cart: [], + + // Initialize + init() { + shopLog.info('Shop layout initializing...'); + + // Load cart from localStorage + this.loadCart(); + + // Listen for cart updates + window.addEventListener('cart-updated', () => { + this.loadCart(); + }); + + shopLog.info('Shop layout initialized'); + }, + + // Theme management + toggleTheme() { + this.dark = !this.dark; + localStorage.setItem('shop-theme', this.dark ? 'dark' : 'light'); + shopLog.debug('Theme toggled:', this.dark ? 'dark' : 'light'); + }, + + // Mobile menu + toggleMobileMenu() { + this.mobileMenuOpen = !this.mobileMenuOpen; + if (this.mobileMenuOpen) { + document.body.style.overflow = 'hidden'; + } else { + document.body.style.overflow = ''; + } + }, + + closeMobileMenu() { + this.mobileMenuOpen = false; + document.body.style.overflow = ''; + }, + + // Search + openSearch() { + this.searchOpen = true; + shopLog.debug('Search opened'); + // Focus search input after a short delay + setTimeout(() => { + const input = document.querySelector('#search-input'); + if (input) input.focus(); + }, 100); + }, + + closeSearch() { + this.searchOpen = false; + }, + + // Cart management + loadCart() { + try { + const cartData = localStorage.getItem('shop-cart'); + if (cartData) { + this.cart = JSON.parse(cartData); + this.cartCount = this.cart.reduce((sum, item) => sum + item.quantity, 0); + } + } catch (error) { + shopLog.error('Failed to load cart:', error); + this.cart = []; + this.cartCount = 0; + } + }, + + addToCart(product, quantity = 1) { + shopLog.info('Adding to cart:', product.name, 'x', quantity); + + // Find existing item + const existingIndex = this.cart.findIndex(item => item.id === product.id); + + if (existingIndex !== -1) { + // Update quantity + this.cart[existingIndex].quantity += quantity; + } else { + // Add new item + this.cart.push({ + id: product.id, + name: product.name, + price: product.price, + image: product.image, + quantity: quantity + }); + } + + // Save and update + this.saveCart(); + this.showToast(`${product.name} added to cart`, 'success'); + }, + + updateCartItem(productId, quantity) { + const index = this.cart.findIndex(item => item.id === productId); + if (index !== -1) { + if (quantity <= 0) { + this.cart.splice(index, 1); + } else { + this.cart[index].quantity = quantity; + } + this.saveCart(); + } + }, + + removeFromCart(productId) { + this.cart = this.cart.filter(item => item.id !== productId); + this.saveCart(); + this.showToast('Item removed from cart', 'info'); + }, + + clearCart() { + this.cart = []; + this.saveCart(); + this.showToast('Cart cleared', 'info'); + }, + + saveCart() { + try { + localStorage.setItem('shop-cart', JSON.stringify(this.cart)); + this.cartCount = this.cart.reduce((sum, item) => sum + item.quantity, 0); + + // Dispatch custom event + window.dispatchEvent(new CustomEvent('cart-updated')); + + shopLog.debug('Cart saved:', this.cart.length, 'items'); + } catch (error) { + shopLog.error('Failed to save cart:', error); + } + }, + + // Get cart total + get cartTotal() { + return this.cart.reduce((sum, item) => sum + (item.price * item.quantity), 0); + }, + + // Toast notifications + showToast(message, type = 'info') { + const container = document.getElementById('toast-container'); + if (!container) return; + + const toast = document.createElement('div'); + toast.className = `toast toast-${type} transform transition-all duration-300 mb-2`; + + // Color based on type + const colors = { + success: 'bg-green-500', + error: 'bg-red-500', + warning: 'bg-yellow-500', + info: 'bg-blue-500' + }; + + toast.innerHTML = ` +
+ ${message} + +
+ `; + + container.appendChild(toast); + + // Auto-remove after 3 seconds + setTimeout(() => { + toast.style.opacity = '0'; + setTimeout(() => toast.remove(), 300); + }, 3000); + }, + + // Format currency + formatPrice(price) { + return new Intl.NumberFormat('en-US', { + style: 'currency', + currency: 'USD' + }).format(price); + }, + + // Format date + formatDate(dateString) { + if (!dateString) return '-'; + const date = new Date(dateString); + return date.toLocaleDateString('en-US', { + year: 'numeric', + month: 'short', + day: 'numeric' + }); + } + }; +} + +// Make available globally +window.shopLayoutData = shopLayoutData; + +shopLog.info('Shop layout module loaded'); \ No newline at end of file diff --git a/static/shop/product.html b/static/shop/product.html deleted file mode 100644 index 77e5e639..00000000 --- a/static/shop/product.html +++ /dev/null @@ -1,771 +0,0 @@ - - - - - - {{ product.name if product else 'Product' }} - {{ vendor.name }} - - - - - -
- -
-
- ← Back to Products -

{{ vendor.name }}

-
- -
- - -
-
-
-

Loading product...

-
-
- - -
-
- -
-
- -
- - - -
- - -
-

- - -
- - Brand: - - - Category: - - - SKU: - -
- - -
-
- - - SALE -
-
- -
-
- - -
- - ✓ In Stock ( available) - - - ✗ Out of Stock - -
- - -
-

Description

-

-
- - -
-

Product Details

-
    -
  • - GTIN: -
  • -
  • - Condition: -
  • -
  • - Color: -
  • -
  • - Size: -
  • -
  • - Material: -
  • -
-
- - -
- -
- -
- - - -
-
- - - - - -
- Total: -
-
-
-
- - - -
- - -
-
- - - - - - \ No newline at end of file diff --git a/static/shop/products.html b/static/shop/products.html deleted file mode 100644 index 99a44701..00000000 --- a/static/shop/products.html +++ /dev/null @@ -1,459 +0,0 @@ - - - - - - Products - {{ vendor.name }} - - - - - -
- -
-
-

{{ vendor.name }} - Products

-
- -
- -
- -
- - -
- -
- - -
- - -
-
-

Loading products...

-
- - -
-
📦
-

No products found

-

Try adjusting your filters

-

Check back soon for new products!

-
- - -
- -
- - - -
- - -
-
- - - - - - \ No newline at end of file diff --git a/static/shop/search.html b/static/shop/search.html deleted file mode 100644 index 3dfc5efa..00000000 --- a/static/shop/search.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - Search results page - - - <-- Search results page --> - -