# Vendor & Users Pages Migration Plan - FINAL
## Based on Actual Legacy Files

**Date:** October 23, 2025  
**Status:** Icons fixed ✅ | Logout working ✅ | Dashboard migrated ✅

---

## 📁 Legacy Files Analysis

### Existing Files:
1. **vendors.html** - Basic placeholder (needs vendor LIST implementation)
2. **vendor-edit.html** - Detailed edit form (OLD CSS, needs redesign)
3. **vendors.js** - Only has `vendorCreation()` function (NOT vendor list)
4. **vendor-edit.js** - Complete edit functionality (uses OLD auth/api pattern)
5. **init-alpine.js** - Base Alpine data (theme, menus)

### Key Findings:
- ✅ `vendors.js` currently only has CREATE vendor function
- ❌ NO vendor LIST function exists yet
- ✅ `vendor-edit.js` exists but uses OLD patterns:
  - Uses `apiClient` (should use `ApiClient`)
  - Uses `Auth.isAuthenticated()` pattern
  - Uses `Utils.confirm()` and custom modals
- ✅ Dashboard pattern: Uses `ApiClient`, `Logger`, `Utils.showToast`

### Pattern Differences:

**OLD Pattern (vendor-edit.js):**
```javascript
// OLD API client (lowercase)
const response = await apiClient.get('/admin/vendors/1');

// OLD Auth pattern
if (!Auth.isAuthenticated()) { ... }
const user = Auth.getCurrentUser();
```

**NEW Pattern (dashboard.js):**
```javascript
// NEW API client (uppercase)
const response = await ApiClient.get('/admin/vendors');

// NEW Auth - handled by cookie, no client-side check needed
// Just call API and let middleware handle it
```

---

## 🎯 Migration Strategy

### Task 1: Create Vendor List Function (HIGH PRIORITY) 🏪

**Current State:** vendors.js only has `vendorCreation()` function  
**Goal:** Add `adminVendors()` function for the vendor LIST page

#### 1.1 Update vendors.js - Add Vendor List Function

**File:** `static/admin/js/vendors.js`

**Action:** ADD new function (keep existing `vendorCreation()` function):

```javascript
// static/admin/js/vendors.js

// ============================================
// VENDOR LIST FUNCTION (NEW - Add this)
// ============================================
function adminVendors() {
    return {
        // State
        vendors: [],
        stats: {
            total: 0,
            verified: 0,
            pending: 0,
            inactive: 0
        },
        loading: false,
        error: null,

        // Initialize
        async init() {
            Logger.info('Vendors page initialized', 'VENDORS');
            await this.loadVendors();
            await this.loadStats();
        },

        // Load vendors list
        async loadVendors() {
            this.loading = true;
            this.error = null;
            try {
                const response = await ApiClient.get('/admin/vendors');
                // Handle different response structures
                this.vendors = response.vendors || response.items || response || [];
                Logger.info('Vendors loaded', 'VENDORS', { count: this.vendors.length });
            } catch (error) {
                Logger.error('Failed to load vendors', 'VENDORS', error);
                this.error = error.message || 'Failed to load vendors';
                Utils.showToast('Failed to load vendors', 'error');
            } finally {
                this.loading = false;
            }
        },

        // Load statistics
        async loadStats() {
            try {
                const response = await ApiClient.get('/admin/vendors/stats');
                this.stats = response;
                Logger.info('Stats loaded', 'VENDORS', this.stats);
            } catch (error) {
                Logger.error('Failed to load stats', 'VENDORS', error);
                // Don't show error toast for stats, just log it
            }
        },

        // Format date (matches dashboard pattern)
        formatDate(dateString) {
            if (!dateString) return '-';
            const date = new Date(dateString);
            return date.toLocaleDateString('en-US', {
                year: 'numeric',
                month: 'short',
                day: 'numeric'
            });
        },

        // View vendor details
        viewVendor(vendorCode) {
            Logger.info('View vendor', 'VENDORS', { vendorCode });
            // Navigate to details page or open modal
            window.location.href = `/admin/vendors/${vendorCode}`;
        },

        // Edit vendor
        editVendor(vendorCode) {
            Logger.info('Edit vendor', 'VENDORS', { vendorCode });
            window.location.href = `/admin/vendors/${vendorCode}/edit`;
        },

        // Delete vendor
        async deleteVendor(vendor) {
            if (!confirm(`Are you sure you want to delete vendor "${vendor.name}"?\n\nThis action cannot be undone.`)) {
                return;
            }

            try {
                await ApiClient.delete(`/admin/vendors/${vendor.vendor_code}`);
                Utils.showToast('Vendor deleted successfully', 'success');
                await this.loadVendors();
                await this.loadStats();
            } catch (error) {
                Logger.error('Failed to delete vendor', 'VENDORS', error);
                Utils.showToast(error.message || 'Failed to delete vendor', 'error');
            }
        },

        // Open create modal/page
        openCreateModal() {
            Logger.info('Open create vendor', 'VENDORS');
            // Navigate to create page (or open modal)
            window.location.href = '/admin/vendors/create';
        }
    };
}

// ============================================
// VENDOR CREATION FUNCTION (EXISTING - Keep this)
// ============================================
function vendorCreation() {
    // ... keep your existing vendorCreation function as is ...
    // (the code you already have)
}
```

#### 1.2 Create Vendors List Template

**File:** `app/templates/admin/vendors.html`

```jinja2
{# app/templates/admin/vendors.html #}
{% extends "admin/base.html" %}

{% block title %}Vendors{% endblock %}

{% block alpine_data %}adminVendors(){% endblock %}

{% block content %}
<!-- Page Header -->
<div class="flex items-center justify-between my-6">
    <h2 class="text-2xl font-semibold text-gray-700 dark:text-gray-200">
        Vendor Management
    </h2>
    <a
        href="/admin/vendors/create"
        class="flex items-center px-4 py-2 text-sm font-medium leading-5 text-white transition-colors duration-150 bg-purple-600 border border-transparent rounded-lg hover:bg-purple-700 focus:outline-none focus:shadow-outline-purple"
    >
        <span x-html="$icon('plus', 'w-4 h-4 mr-2')"></span>
        Create Vendor
    </a>
</div>

<!-- Loading State -->
<div x-show="loading" class="text-center py-12">
    <span x-html="$icon('spinner', 'inline w-8 h-8 text-purple-600')"></span>
    <p class="mt-2 text-gray-600 dark:text-gray-400">Loading vendors...</p>
</div>

<!-- Error State -->
<div x-show="error && !loading" class="mb-6 p-4 bg-red-100 border border-red-400 text-red-700 rounded-lg flex items-start">
    <span x-html="$icon('exclamation', 'w-5 h-5 mr-3 mt-0.5 flex-shrink-0')"></span>
    <div>
        <p class="font-semibold">Error loading vendors</p>
        <p class="text-sm" x-text="error"></p>
    </div>
</div>

<!-- Stats Cards - EXACTLY like dashboard -->
<div x-show="!loading" class="grid gap-6 mb-8 md:grid-cols-2 xl:grid-cols-4">
    <!-- Card: Total Vendors -->
    <div class="flex items-center p-4 bg-white rounded-lg shadow-xs dark:bg-gray-800">
        <div class="p-3 mr-4 text-orange-500 bg-orange-100 rounded-full dark:text-orange-100 dark:bg-orange-500">
            <span x-html="$icon('user-group', 'w-5 h-5')"></span>
        </div>
        <div>
            <p class="mb-2 text-sm font-medium text-gray-600 dark:text-gray-400">
                Total Vendors
            </p>
            <p class="text-lg font-semibold text-gray-700 dark:text-gray-200" x-text="stats.total || 0">
                0
            </p>
        </div>
    </div>

    <!-- Card: Verified Vendors -->
    <div class="flex items-center p-4 bg-white rounded-lg shadow-xs dark:bg-gray-800">
        <div class="p-3 mr-4 text-green-500 bg-green-100 rounded-full dark:text-green-100 dark:bg-green-500">
            <span x-html="$icon('badge-check', 'w-5 h-5')"></span>
        </div>
        <div>
            <p class="mb-2 text-sm font-medium text-gray-600 dark:text-gray-400">
                Verified Vendors
            </p>
            <p class="text-lg font-semibold text-gray-700 dark:text-gray-200" x-text="stats.verified || 0">
                0
            </p>
        </div>
    </div>

    <!-- Card: Pending Verification -->
    <div class="flex items-center p-4 bg-white rounded-lg shadow-xs dark:bg-gray-800">
        <div class="p-3 mr-4 text-yellow-500 bg-yellow-100 rounded-full dark:text-yellow-100 dark:bg-yellow-500">
            <span x-html="$icon('clock', 'w-5 h-5')"></span>
        </div>
        <div>
            <p class="mb-2 text-sm font-medium text-gray-600 dark:text-gray-400">
                Pending
            </p>
            <p class="text-lg font-semibold text-gray-700 dark:text-gray-200" x-text="stats.pending || 0">
                0
            </p>
        </div>
    </div>

    <!-- Card: Inactive Vendors -->
    <div class="flex items-center p-4 bg-white rounded-lg shadow-xs dark:bg-gray-800">
        <div class="p-3 mr-4 text-red-500 bg-red-100 rounded-full dark:text-red-100 dark:bg-red-500">
            <span x-html="$icon('x-circle', 'w-5 h-5')"></span>
        </div>
        <div>
            <p class="mb-2 text-sm font-medium text-gray-600 dark:text-gray-400">
                Inactive
            </p>
            <p class="text-lg font-semibold text-gray-700 dark:text-gray-200" x-text="stats.inactive || 0">
                0
            </p>
        </div>
    </div>
</div>

<!-- Vendors Table - EXACTLY like dashboard table -->
<div x-show="!loading" class="w-full overflow-hidden rounded-lg shadow-xs">
    <div class="w-full overflow-x-auto">
        <table class="w-full whitespace-no-wrap">
            <thead>
                <tr class="text-xs font-semibold tracking-wide text-left text-gray-500 uppercase border-b dark:border-gray-700 bg-gray-50 dark:text-gray-400 dark:bg-gray-800">
                    <th class="px-4 py-3">Vendor</th>
                    <th class="px-4 py-3">Subdomain</th>
                    <th class="px-4 py-3">Status</th>
                    <th class="px-4 py-3">Created</th>
                    <th class="px-4 py-3">Actions</th>
                </tr>
            </thead>
            <tbody class="bg-white divide-y dark:divide-gray-700 dark:bg-gray-800">
                <!-- Empty State -->
                <template x-if="vendors.length === 0">
                    <tr>
                        <td colspan="5" class="px-4 py-8 text-center text-gray-600 dark:text-gray-400">
                            <div class="flex flex-col items-center">
                                <span x-html="$icon('user-group', 'w-12 h-12 mb-2 text-gray-300')"></span>
                                <p class="font-medium">No vendors found</p>
                                <p class="text-xs mt-1">Create your first vendor to get started</p>
                            </div>
                        </td>
                    </tr>
                </template>

                <!-- Vendor Rows -->
                <template x-for="vendor in vendors" :key="vendor.id || vendor.vendor_code">
                    <tr class="text-gray-700 dark:text-gray-400 hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors">
                        <!-- Vendor Info with Avatar -->
                        <td class="px-4 py-3">
                            <div class="flex items-center text-sm">
                                <div class="relative hidden w-8 h-8 mr-3 rounded-full md:block">
                                    <div class="absolute inset-0 rounded-full bg-purple-100 dark:bg-purple-600 flex items-center justify-center">
                                        <span class="text-xs font-semibold text-purple-600 dark:text-purple-100" 
                                              x-text="vendor.name?.charAt(0).toUpperCase() || '?'"></span>
                                    </div>
                                </div>
                                <div>
                                    <p class="font-semibold" x-text="vendor.name"></p>
                                    <p class="text-xs text-gray-600 dark:text-gray-400" x-text="vendor.vendor_code"></p>
                                </div>
                            </div>
                        </td>

                        <!-- Subdomain -->
                        <td class="px-4 py-3 text-sm" x-text="vendor.subdomain"></td>

                        <!-- Status Badge -->
                        <td class="px-4 py-3 text-xs">
                            <span class="inline-flex items-center px-2 py-1 font-semibold leading-tight rounded-full"
                                  :class="vendor.is_verified ? 'text-green-700 bg-green-100 dark:bg-green-700 dark:text-green-100' : 'text-orange-700 bg-orange-100 dark:text-white dark:bg-orange-600'">
                                <span x-show="vendor.is_verified" x-html="$icon('badge-check', 'w-3 h-3 mr-1')"></span>
                                <span x-text="vendor.is_verified ? 'Verified' : 'Pending'"></span>
                            </span>
                        </td>

                        <!-- Created Date -->
                        <td class="px-4 py-3 text-sm" x-text="formatDate(vendor.created_at)"></td>

                        <!-- Actions -->
                        <td class="px-4 py-3">
                            <div class="flex items-center space-x-2 text-sm">
                                <!-- View Button -->
                                <button
                                    @click="viewVendor(vendor.vendor_code)"
                                    class="flex items-center justify-center p-2 text-blue-600 rounded-lg hover:bg-blue-50 dark:text-blue-400 dark:hover:bg-gray-700 focus:outline-none transition-colors"
                                    title="View details"
                                >
                                    <span x-html="$icon('eye', 'w-5 h-5')"></span>
                                </button>

                                <!-- Edit Button -->
                                <button
                                    @click="editVendor(vendor.vendor_code)"
                                    class="flex items-center justify-center p-2 text-purple-600 rounded-lg hover:bg-purple-50 dark:text-purple-400 dark:hover:bg-gray-700 focus:outline-none transition-colors"
                                    title="Edit vendor"
                                >
                                    <span x-html="$icon('pencil', 'w-5 h-5')"></span>
                                </button>

                                <!-- Delete Button -->
                                <button
                                    @click="deleteVendor(vendor)"
                                    class="flex items-center justify-center p-2 text-red-600 rounded-lg hover:bg-red-50 dark:text-red-400 dark:hover:bg-gray-700 focus:outline-none transition-colors"
                                    title="Delete vendor"
                                >
                                    <span x-html="$icon('trash', 'w-5 h-5')"></span>
                                </button>
                            </div>
                        </td>
                    </tr>
                </template>
            </tbody>
        </table>
    </div>
</div>
{% endblock %}

{% block extra_scripts %}
<script src="{{ url_for('static', path='admin/js/vendors.js') }}"></script>
{% endblock %}
```

**Checklist Task 1:**
- [ ] Add `adminVendors()` function to `static/admin/js/vendors.js` (keep existing `vendorCreation()`)
- [ ] Create `app/templates/admin/vendors.html` template
- [ ] Add backend route for `/admin/vendors`
- [ ] Test vendor list page loads
- [ ] Test stats cards display
- [ ] Test vendor table displays data
- [ ] Test action buttons work

---

### Task 2: Migrate Vendor Edit Page (HIGH PRIORITY) ✏️

**Current State:** vendor-edit.js exists but uses OLD patterns  
**Goal:** Update to use NEW patterns (ApiClient, Logger, no Auth checks)

#### 2.1 Update vendor-edit.js to NEW Pattern

**File:** `static/admin/js/vendor-edit.js`

**Changes Needed:**
1. Replace `apiClient` → `ApiClient`
2. Remove `Auth.isAuthenticated()` checks (handled by backend)
3. Replace `Utils.confirm()` → Use `confirm()`
4. Remove custom modals, use simple confirms
5. Add Logger calls
6. Update route patterns

```javascript
// static/admin/js/vendor-edit.js

function adminVendorEdit() {
    return {
        // State
        vendor: null,
        formData: {},
        errors: {},
        loadingVendor: false,
        saving: false,
        vendorCode: null,

        // Initialize
        async init() {
            Logger.info('Vendor edit page initialized', 'VENDOR_EDIT');
            
            // Get vendor code from URL
            const path = window.location.pathname;
            const match = path.match(/\/admin\/vendors\/([^\/]+)\/edit/);
            
            if (match) {
                this.vendorCode = match[1];
                Logger.info('Editing vendor', 'VENDOR_EDIT', { vendorCode: this.vendorCode });
                await this.loadVendor();
            } else {
                Logger.error('No vendor code in URL', 'VENDOR_EDIT');
                Utils.showToast('Invalid vendor URL', 'error');
                setTimeout(() => window.location.href = '/admin/vendors', 2000);
            }
        },

        // Load vendor data
        async loadVendor() {
            this.loadingVendor = true;
            try {
                // CHANGED: apiClient → ApiClient
                const response = await ApiClient.get(`/admin/vendors/${this.vendorCode}`);
                this.vendor = response;
                
                // Initialize form data
                this.formData = {
                    name: response.name || '',
                    subdomain: response.subdomain || '',
                    description: response.description || '',
                    contact_email: response.contact_email || '',
                    contact_phone: response.contact_phone || '',
                    website: response.website || '',
                    business_address: response.business_address || '',
                    tax_number: response.tax_number || ''
                };

                Logger.info('Vendor loaded', 'VENDOR_EDIT', {
                    vendor_code: this.vendor.vendor_code,
                    name: this.vendor.name
                });
            } catch (error) {
                Logger.error('Failed to load vendor', 'VENDOR_EDIT', error);
                Utils.showToast('Failed to load vendor', 'error');
                setTimeout(() => window.location.href = '/admin/vendors', 2000);
            } finally {
                this.loadingVendor = false;
            }
        },

        // Format subdomain
        formatSubdomain() {
            this.formData.subdomain = this.formData.subdomain
                .toLowerCase()
                .replace(/[^a-z0-9-]/g, '');
        },

        // Submit form
        async handleSubmit() {
            Logger.info('Submitting vendor update', 'VENDOR_EDIT');
            this.errors = {};
            this.saving = true;

            try {
                // CHANGED: apiClient → ApiClient
                const response = await ApiClient.put(
                    `/admin/vendors/${this.vendorCode}`,
                    this.formData
                );
                
                this.vendor = response;
                Utils.showToast('Vendor updated successfully', 'success');
                Logger.info('Vendor updated', 'VENDOR_EDIT', response);
                
                // Optionally redirect back to list
                // setTimeout(() => window.location.href = '/admin/vendors', 1500);
            } catch (error) {
                Logger.error('Failed to update vendor', 'VENDOR_EDIT', error);
                
                // Handle validation errors
                if (error.details && error.details.validation_errors) {
                    error.details.validation_errors.forEach(err => {
                        const field = err.loc?.[1] || err.loc?.[0];
                        if (field) {
                            this.errors[field] = err.msg;
                        }
                    });
                }
                
                Utils.showToast(error.message || 'Failed to update vendor', 'error');
            } finally {
                this.saving = false;
            }
        },

        // Toggle verification
        async toggleVerification() {
            const action = this.vendor.is_verified ? 'unverify' : 'verify';
            
            // CHANGED: Simple confirm instead of custom modal
            if (!confirm(`Are you sure you want to ${action} this vendor?`)) {
                return;
            }

            this.saving = true;
            try {
                // CHANGED: apiClient → ApiClient
                const response = await ApiClient.put(
                    `/admin/vendors/${this.vendorCode}/verification`,
                    { is_verified: !this.vendor.is_verified }
                );
                
                this.vendor = response;
                Utils.showToast(`Vendor ${action}ed successfully`, 'success');
                Logger.info(`Vendor ${action}ed`, 'VENDOR_EDIT');
            } catch (error) {
                Logger.error(`Failed to ${action} vendor`, 'VENDOR_EDIT', error);
                Utils.showToast(`Failed to ${action} vendor`, 'error');
            } finally {
                this.saving = false;
            }
        },

        // Toggle active status
        async toggleActive() {
            const action = this.vendor.is_active ? 'deactivate' : 'activate';
            
            // CHANGED: Simple confirm instead of custom modal
            if (!confirm(`Are you sure you want to ${action} this vendor?\n\nThis will affect their operations.`)) {
                return;
            }

            this.saving = true;
            try {
                // CHANGED: apiClient → ApiClient
                const response = await ApiClient.put(
                    `/admin/vendors/${this.vendorCode}/status`,
                    { is_active: !this.vendor.is_active }
                );
                
                this.vendor = response;
                Utils.showToast(`Vendor ${action}d successfully`, 'success');
                Logger.info(`Vendor ${action}d`, 'VENDOR_EDIT');
            } catch (error) {
                Logger.error(`Failed to ${action} vendor`, 'VENDOR_EDIT', error);
                Utils.showToast(`Failed to ${action} vendor`, 'error');
            } finally {
                this.saving = false;
            }
        }
    };
}
```

#### 2.2 Create Vendor Edit Template

**File:** `app/templates/admin/vendor-edit.html`

```jinja2
{# app/templates/admin/vendor-edit.html #}
{% extends "admin/base.html" %}

{% block title %}Edit Vendor{% endblock %}

{% block alpine_data %}adminVendorEdit(){% endblock %}

{% block content %}
<!-- Page Header -->
<div class="flex items-center justify-between my-6">
    <div>
        <h2 class="text-2xl font-semibold text-gray-700 dark:text-gray-200">
            Edit Vendor
        </h2>
        <p class="text-sm text-gray-600 dark:text-gray-400 mt-1" x-show="vendor">
            <span x-text="vendor?.name"></span> 
            <span class="text-gray-400">•</span> 
            <span x-text="vendor?.vendor_code"></span>
        </p>
    </div>
    <a href="/admin/vendors" 
       class="flex items-center px-4 py-2 text-sm font-medium leading-5 text-gray-700 transition-colors duration-150 bg-white border border-gray-300 rounded-lg dark:text-gray-400 dark:border-gray-600 dark:bg-gray-800 hover:border-gray-400 focus:outline-none">
        <span x-html="$icon('arrow-left', 'w-4 h-4 mr-2')"></span>
        Back to Vendors
    </a>
</div>

<!-- Loading State -->
<div x-show="loadingVendor" class="text-center py-12">
    <span x-html="$icon('spinner', 'inline w-8 h-8 text-purple-600')"></span>
    <p class="mt-2 text-gray-600 dark:text-gray-400">Loading vendor...</p>
</div>

<!-- Edit Form -->
<div x-show="!loadingVendor && vendor" class="px-4 py-3 mb-8 bg-white rounded-lg shadow-md dark:bg-gray-800">
    <!-- Quick Actions -->
    <div class="flex items-center gap-3 mb-6 pb-6 border-b dark:border-gray-700">
        <button
            @click="toggleVerification()"
            :disabled="saving"
            class="flex items-center px-4 py-2 text-sm font-medium leading-5 text-white transition-colors duration-150 rounded-lg focus:outline-none focus:shadow-outline-purple disabled:opacity-50"
            :class="vendor?.is_verified ? 'bg-orange-600 hover:bg-orange-700' : 'bg-green-600 hover:bg-green-700'">
            <span x-html="$icon(vendor?.is_verified ? 'x-circle' : 'badge-check', 'w-4 h-4 mr-2')"></span>
            <span x-text="vendor?.is_verified ? 'Unverify Vendor' : 'Verify Vendor'"></span>
        </button>

        <button
            @click="toggleActive()"
            :disabled="saving"
            class="flex items-center px-4 py-2 text-sm font-medium leading-5 text-white transition-colors duration-150 rounded-lg focus:outline-none focus:shadow-outline-purple disabled:opacity-50"
            :class="vendor?.is_active ? 'bg-red-600 hover:bg-red-700' : 'bg-green-600 hover:bg-green-700'">
            <span x-html="$icon(vendor?.is_active ? 'lock-closed' : 'lock-open', 'w-4 h-4 mr-2')"></span>
            <span x-text="vendor?.is_active ? 'Deactivate' : 'Activate'"></span>
        </button>
    </div>

    <!-- Form -->
    <form @submit.prevent="handleSubmit">
        <div class="grid gap-6 mb-8 md:grid-cols-2">
            <!-- Left Column: Basic Info -->
            <div>
                <h3 class="mb-4 text-lg font-semibold text-gray-700 dark:text-gray-200">
                    Basic Information
                </h3>

                <!-- Vendor Code (readonly) -->
                <label class="block mb-4">
                    <span class="text-sm font-medium text-gray-700 dark:text-gray-400">
                        Vendor Code
                    </span>
                    <input
                        type="text"
                        x-model="vendor.vendor_code"
                        disabled
                        class="block w-full mt-1 text-sm bg-gray-100 border-gray-300 rounded-md dark:bg-gray-700 dark:text-gray-400 dark:border-gray-600 cursor-not-allowed"
                    >
                    <span class="text-xs text-gray-600 dark:text-gray-400 mt-1">
                        Cannot be changed after creation
                    </span>
                </label>

                <!-- Name -->
                <label class="block mb-4">
                    <span class="text-sm font-medium text-gray-700 dark:text-gray-400">
                        Vendor Name <span class="text-red-600">*</span>
                    </span>
                    <input
                        type="text"
                        x-model="formData.name"
                        required
                        maxlength="255"
                        :disabled="saving"
                        class="block w-full mt-1 text-sm border-gray-300 rounded-md dark:border-gray-600 dark:bg-gray-700 dark:text-gray-300 focus:border-purple-400 focus:outline-none focus:shadow-outline-purple dark:focus:shadow-outline-gray form-input"
                        :class="{ 'border-red-600': errors.name }"
                    >
                    <span x-show="errors.name" class="text-xs text-red-600 dark:text-red-400 mt-1" x-text="errors.name"></span>
                </label>

                <!-- Subdomain -->
                <label class="block mb-4">
                    <span class="text-sm font-medium text-gray-700 dark:text-gray-400">
                        Subdomain <span class="text-red-600">*</span>
                    </span>
                    <input
                        type="text"
                        x-model="formData.subdomain"
                        @input="formatSubdomain()"
                        required
                        maxlength="100"
                        :disabled="saving"
                        class="block w-full mt-1 text-sm border-gray-300 rounded-md dark:border-gray-600 dark:bg-gray-700 dark:text-gray-300 focus:border-purple-400 focus:outline-none focus:shadow-outline-purple dark:focus:shadow-outline-gray form-input"
                        :class="{ 'border-red-600': errors.subdomain }"
                    >
                    <span class="text-xs text-gray-600 dark:text-gray-400 mt-1">
                        Lowercase letters, numbers, and hyphens only
                    </span>
                    <span x-show="errors.subdomain" class="text-xs text-red-600 dark:text-red-400 mt-1" x-text="errors.subdomain"></span>
                </label>

                <!-- Description -->
                <label class="block mb-4">
                    <span class="text-sm font-medium text-gray-700 dark:text-gray-400">
                        Description
                    </span>
                    <textarea
                        x-model="formData.description"
                        rows="3"
                        :disabled="saving"
                        class="block w-full mt-1 text-sm border-gray-300 rounded-md dark:border-gray-600 dark:bg-gray-700 dark:text-gray-300 focus:border-purple-400 focus:outline-none focus:shadow-outline-purple dark:focus:shadow-outline-gray form-textarea"
                    ></textarea>
                </label>
            </div>

            <!-- Right Column: Contact Info -->
            <div>
                <h3 class="mb-4 text-lg font-semibold text-gray-700 dark:text-gray-200">
                    Contact Information
                </h3>

                <!-- Owner Email (readonly) -->
                <label class="block mb-4">
                    <span class="text-sm font-medium text-gray-700 dark:text-gray-400">
                        Owner Email
                    </span>
                    <input
                        type="email"
                        x-model="vendor.owner_email"
                        disabled
                        class="block w-full mt-1 text-sm bg-gray-100 border-gray-300 rounded-md dark:bg-gray-700 dark:text-gray-400 dark:border-gray-600 cursor-not-allowed"
                    >
                </label>

                <!-- Contact Email -->
                <label class="block mb-4">
                    <span class="text-sm font-medium text-gray-700 dark:text-gray-400">
                        Contact Email <span class="text-red-600">*</span>
                    </span>
                    <input
                        type="email"
                        x-model="formData.contact_email"
                        required
                        :disabled="saving"
                        class="block w-full mt-1 text-sm border-gray-300 rounded-md dark:border-gray-600 dark:bg-gray-700 dark:text-gray-300 focus:border-purple-400 focus:outline-none focus:shadow-outline-purple dark:focus:shadow-outline-gray form-input"
                        :class="{ 'border-red-600': errors.contact_email }"
                    >
                    <span x-show="errors.contact_email" class="text-xs text-red-600 dark:text-red-400 mt-1" x-text="errors.contact_email"></span>
                </label>

                <!-- Phone -->
                <label class="block mb-4">
                    <span class="text-sm font-medium text-gray-700 dark:text-gray-400">
                        Phone
                    </span>
                    <input
                        type="tel"
                        x-model="formData.contact_phone"
                        :disabled="saving"
                        class="block w-full mt-1 text-sm border-gray-300 rounded-md dark:border-gray-600 dark:bg-gray-700 dark:text-gray-300 focus:border-purple-400 focus:outline-none focus:shadow-outline-purple dark:focus:shadow-outline-gray form-input"
                    >
                </label>

                <!-- Website -->
                <label class="block mb-4">
                    <span class="text-sm font-medium text-gray-700 dark:text-gray-400">
                        Website
                    </span>
                    <input
                        type="url"
                        x-model="formData.website"
                        :disabled="saving"
                        placeholder="https://example.com"
                        class="block w-full mt-1 text-sm border-gray-300 rounded-md dark:border-gray-600 dark:bg-gray-700 dark:text-gray-300 focus:border-purple-400 focus:outline-none focus:shadow-outline-purple dark:focus:shadow-outline-gray form-input"
                    >
                </label>
            </div>
        </div>

        <!-- Business Details -->
        <div class="mb-8">
            <h3 class="mb-4 text-lg font-semibold text-gray-700 dark:text-gray-200">
                Business Details
            </h3>
            
            <div class="grid gap-6 md:grid-cols-2">
                <!-- Business Address -->
                <label class="block">
                    <span class="text-sm font-medium text-gray-700 dark:text-gray-400">
                        Business Address
                    </span>
                    <textarea
                        x-model="formData.business_address"
                        rows="3"
                        :disabled="saving"
                        class="block w-full mt-1 text-sm border-gray-300 rounded-md dark:border-gray-600 dark:bg-gray-700 dark:text-gray-300 focus:border-purple-400 focus:outline-none focus:shadow-outline-purple dark:focus:shadow-outline-gray form-textarea"
                    ></textarea>
                </label>

                <!-- Tax Number -->
                <label class="block">
                    <span class="text-sm font-medium text-gray-700 dark:text-gray-400">
                        Tax Number
                    </span>
                    <input
                        type="text"
                        x-model="formData.tax_number"
                        :disabled="saving"
                        class="block w-full mt-1 text-sm border-gray-300 rounded-md dark:border-gray-600 dark:bg-gray-700 dark:text-gray-300 focus:border-purple-400 focus:outline-none focus:shadow-outline-purple dark:focus:shadow-outline-gray form-input"
                    >
                </label>
            </div>
        </div>

        <!-- Save Button -->
        <div class="flex items-center justify-end gap-3">
            <a
                href="/admin/vendors"
                class="px-4 py-2 text-sm font-medium leading-5 text-gray-700 transition-colors duration-150 bg-white border border-gray-300 rounded-lg dark:text-gray-400 dark:border-gray-600 dark:bg-gray-800 hover:border-gray-400 focus:outline-none">
                Cancel
            </a>
            <button
                type="submit"
                :disabled="saving"
                class="flex items-center px-4 py-2 text-sm font-medium leading-5 text-white transition-colors duration-150 bg-purple-600 border border-transparent rounded-lg hover:bg-purple-700 focus:outline-none focus:shadow-outline-purple disabled:opacity-50 disabled:cursor-not-allowed">
                <span x-show="!saving">Save Changes</span>
                <span x-show="saving" class="flex items-center">
                    <span x-html="$icon('spinner', 'w-4 h-4 mr-2')"></span>
                    Saving...
                </span>
            </button>
        </div>
    </form>
</div>
{% endblock %}

{% block extra_scripts %}
<script src="{{ url_for('static', path='admin/js/vendor-edit.js') }}"></script>
{% endblock %}
```

**Checklist Task 2:**
- [ ] Update `static/admin/js/vendor-edit.js` with NEW patterns
- [ ] Create `app/templates/admin/vendor-edit.html` template
- [ ] Add backend route for `/admin/vendors/{vendor_code}/edit`
- [ ] Test vendor edit page loads
- [ ] Test form submission works
- [ ] Test quick action buttons (verify/activate)
- [ ] Test validation error display

---

### Task 3: Create Users Page (MEDIUM PRIORITY) 👥

**File:** `app/templates/admin/users.html`
**File:** `static/admin/js/users.js`

Create from scratch - same pattern as vendors list page (see previous plan sections for full code)

---

## ⏰ Updated Time Estimates

| Task | Estimated Time | Priority |
|------|---------------|----------|
| Task 1: Add Vendor List Function | 45-60 min | HIGH |
| Task 2: Update Vendor Edit to NEW pattern | 60-75 min | HIGH |
| Task 3: Create Users Page | 45-60 min | MEDIUM |
| Testing & Verification | 30-45 min | HIGH |
| Cleanup | 15 min | LOW |

**Total: 3-4 hours**

---

## ✅ Success Criteria

By end of session:
- [ ] Vendor LIST page working with stats and table
- [ ] Vendor EDIT page using NEW patterns (ApiClient, Logger)
- [ ] Users page created and working
- [ ] All pages match dashboard styling
- [ ] No console errors
- [ ] Old patterns removed from vendor-edit.js

---

## 📝 Key Pattern Changes

### OLD Pattern (vendor-edit.js):
```javascript
// OLD
const vendor = await apiClient.get('/admin/vendors/1');
if (!Auth.isAuthenticated()) { ... }
Utils.confirm('Message', 'Title');
```

### NEW Pattern (dashboard.js):
```javascript
// NEW
const vendor = await ApiClient.get('/admin/vendors/1');
// No Auth checks - backend handles it
confirm('Message with details');
Logger.info('Action', 'COMPONENT', data);
```

---

**Let's migrate! 🚀**
