diff --git a/12.project_readme_final.md b/12.project_readme_final.md index e6bff81f..026514d0 100644 --- a/12.project_readme_final.md +++ b/12.project_readme_final.md @@ -38,7 +38,7 @@ A production-ready, multi-tenant ecommerce platform that enables vendors to oper ### Technology Stack -- **Backend**: Python 3.10+ with FastAPI +- **Backend**: Python 3.13+ with FastAPI - **Database**: PostgreSQL with SQLAlchemy ORM - **Frontend**: Vanilla HTML, CSS, JavaScript with Alpine.js (CDN-based, no build step) - **Authentication**: JWT tokens with role-based permissions @@ -213,8 +213,8 @@ Marketplace CSV → Import Job → MarketplaceProduct (Staging) → Product (Cat ### Prerequisites -- Python 3.10+ -- PostgreSQL 14+ +- Python 3.13+ +- PostgreSQL 14+ (SQLite for development) - Node.js (optional, only for development tools) ### Development Setup diff --git a/14.updated_complete_project_structure_final.md b/14.updated_complete_project_structure_final.md index d1aa9571..83ae1e61 100644 --- a/14.updated_complete_project_structure_final.md +++ b/14.updated_complete_project_structure_final.md @@ -470,7 +470,7 @@ Marketplace CSV → MarketplaceProduct (staging) → Product (catalog) → Order ## Technology Integration ### Database Layer -- **PostgreSQL**: Primary database with ACID compliance +- **PostgreSQL**: Primary database with ACID compliance (SQLite for development) - **Redis**: Caching and session storage - **Elasticsearch**: Search and analytics (optional) diff --git a/app/api/v1/admin/pages.py b/app/api/v1/admin/pages.py index bafdafb0..642da71b 100644 --- a/app/api/v1/admin/pages.py +++ b/app/api/v1/admin/pages.py @@ -41,7 +41,7 @@ templates = Jinja2Templates(directory="app/templates") async def admin_root(): """ Redirect /admin/ to /admin/login. - + Simple approach: - Unauthenticated users → see login form - Authenticated users → login page shows form (they can navigate to dashboard) @@ -67,9 +67,9 @@ async def admin_login_page(request: Request): @router.get("/dashboard", response_class=HTMLResponse, include_in_schema=False) async def admin_dashboard_page( - request: Request, - current_user: User = Depends(get_current_admin_user), - db: Session = Depends(get_db) + request: Request, + current_user: User = Depends(get_current_admin_user), + db: Session = Depends(get_db) ): """ Render admin dashboard page. @@ -90,9 +90,9 @@ async def admin_dashboard_page( @router.get("/vendors", response_class=HTMLResponse, include_in_schema=False) async def admin_vendors_list_page( - request: Request, - current_user: User = Depends(get_current_admin_user), - db: Session = Depends(get_db) + request: Request, + current_user: User = Depends(get_current_admin_user), + db: Session = Depends(get_db) ): """ Render vendors management page. @@ -109,9 +109,9 @@ async def admin_vendors_list_page( @router.get("/vendors/create", response_class=HTMLResponse, include_in_schema=False) async def admin_vendor_create_page( - request: Request, - current_user: User = Depends(get_current_admin_user), - db: Session = Depends(get_db) + request: Request, + current_user: User = Depends(get_current_admin_user), + db: Session = Depends(get_db) ): """ Render vendor creation form. @@ -127,10 +127,10 @@ async def admin_vendor_create_page( @router.get("/vendors/{vendor_code}", response_class=HTMLResponse, include_in_schema=False) async def admin_vendor_detail_page( - request: Request, - vendor_code: str = Path(..., description="Vendor code"), - current_user: User = Depends(get_current_admin_user), - db: Session = Depends(get_db) + request: Request, + vendor_code: str = Path(..., description="Vendor code"), + current_user: User = Depends(get_current_admin_user), + db: Session = Depends(get_db) ): """ Render vendor detail page. @@ -148,10 +148,10 @@ async def admin_vendor_detail_page( @router.get("/vendors/{vendor_code}/edit", response_class=HTMLResponse, include_in_schema=False) async def admin_vendor_edit_page( - request: Request, - vendor_code: str = Path(..., description="Vendor code"), - current_user: User = Depends(get_current_admin_user), - db: Session = Depends(get_db) + request: Request, + vendor_code: str = Path(..., description="Vendor code"), + current_user: User = Depends(get_current_admin_user), + db: Session = Depends(get_db) ): """ Render vendor edit form. @@ -172,9 +172,9 @@ async def admin_vendor_edit_page( @router.get("/users", response_class=HTMLResponse, include_in_schema=False) async def admin_users_page( - request: Request, - current_user: User = Depends(get_current_admin_user), - db: Session = Depends(get_db) + request: Request, + current_user: User = Depends(get_current_admin_user), + db: Session = Depends(get_db) ): """ Render users management page. @@ -195,9 +195,9 @@ async def admin_users_page( @router.get("/imports", response_class=HTMLResponse, include_in_schema=False) async def admin_imports_page( - request: Request, - current_user: User = Depends(get_current_admin_user), - db: Session = Depends(get_db) + request: Request, + current_user: User = Depends(get_current_admin_user), + db: Session = Depends(get_db) ): """ Render imports management page. @@ -218,9 +218,9 @@ async def admin_imports_page( @router.get("/settings", response_class=HTMLResponse, include_in_schema=False) async def admin_settings_page( - request: Request, - current_user: User = Depends(get_current_admin_user), - db: Session = Depends(get_db) + request: Request, + current_user: User = Depends(get_current_admin_user), + db: Session = Depends(get_db) ): """ Render admin settings page. @@ -233,3 +233,100 @@ async def admin_settings_page( "user": current_user, } ) + + +# ============================================================================ +# DEVELOPER TOOLS - COMPONENTS & TESTING +# ============================================================================ + +@router.get("/components", response_class=HTMLResponse, include_in_schema=False) +async def admin_components_page( + request: Request, + current_user: User = Depends(get_current_admin_user), + db: Session = Depends(get_db) +): + """ + Render UI components library page. + Reference for all available UI components. + """ + return templates.TemplateResponse( + "admin/components.html", + { + "request": request, + "user": current_user, + } + ) + +@router.get("/icons", response_class=HTMLResponse, include_in_schema=False) +async def admin_icons_page( + request: Request, + current_user: User = Depends(get_current_admin_user), + db: Session = Depends(get_db) +): + """ + Render icons browser page. + Browse and search all available icons. + """ + return templates.TemplateResponse( + "admin/icons.html", + { + "request": request, + "user": current_user, + } + ) + +@router.get("/testing", response_class=HTMLResponse, include_in_schema=False) +async def admin_testing_hub( + request: Request, + current_user: User = Depends(get_current_admin_user), + db: Session = Depends(get_db) +): + """ + Render testing hub page. + Central hub for all test suites and QA tools. + """ + return templates.TemplateResponse( + "admin/testing-hub.html", + { + "request": request, + "user": current_user, + } + ) + + +@router.get("/test/auth-flow", response_class=HTMLResponse, include_in_schema=False) +async def admin_test_auth_flow( + request: Request, + current_user: User = Depends(get_current_admin_user), + db: Session = Depends(get_db) +): + """ + Render authentication flow testing page. + Tests login, logout, token expiration, and protected routes. + """ + return templates.TemplateResponse( + "admin/test-auth-flow.html", + { + "request": request, + "user": current_user, + } + ) + + +@router.get("/test/vendors-users-migration", response_class=HTMLResponse, include_in_schema=False) +async def admin_test_vendors_users_migration( + request: Request, + current_user: User = Depends(get_current_admin_user), + db: Session = Depends(get_db) +): + """ + Render vendors and users migration testing page. + Tests CRUD operations, data migration, and form validation. + """ + return templates.TemplateResponse( + "admin/test-vendors-users-migration.html", + { + "request": request, + "user": current_user, + } + ) diff --git a/app/templates/admin/components.html b/app/templates/admin/components.html new file mode 100644 index 00000000..b8353a73 --- /dev/null +++ b/app/templates/admin/components.html @@ -0,0 +1,519 @@ +{# app/templates/admin/components.html #} +{% extends "admin/base.html" %} + +{% block title %}UI Components{% endblock %} + +{# ✅ CRITICAL: Link to Alpine.js component #} +{% block alpine_data %}adminComponents(){% endblock %} + +{% block content %} + +
+

+ UI Components Library +

+ + + Back to Dashboard + +
+ + +
+
+ +
+

Quick Reference Library

+

+ Copy-paste ready UI components for your admin pages. All components support dark mode and are fully accessible. +

+
+
+
+ + +
+ + +
+
+
+

Sections

+ +
+ + +
+
+ +
+

Pro Tip

+

+ Click any code block to copy it to your clipboard! +

+
+
+
+
+
+ + +
+ + +
+
+

+ + Forms +

+ + +
+

Basic Input

+
+ +
+ +
+ + +
+

Required Field with Error

+
+ +
+ +
+ + +
+

Textarea

+
+ +
+ +
+ + +
+

Select Dropdown

+
+ +
+ +
+ + +
+

Checkbox

+
+ +
+ +
+ + +
+

Disabled/Read-Only Input

+
+ +
+ +
+
+
+ + +
+
+

+ + Buttons +

+ + +
+

Primary Button

+
+ + +
+ +
+ + +
+

Secondary Button

+
+ + +
+ +
+ + +
+

Danger Button

+
+ + +
+ +
+ + +
+

Button States

+
+ + +
+ +
+
+
+ + +
+
+

+ + Cards +

+ + +
+

Stats Card

+
+
+
+ +
+
+

Total Users

+

1,234

+
+
+
+ +
+ + +
+

Info Card

+
+
+

Card Title

+
+
+

Field Label

+

Field Value

+
+
+

Another Label

+

Another Value

+
+
+
+
+ +
+
+
+ + +
+
+

+ + Badges +

+ +
+
+ + + + Active + + + + + Pending + + + + + Inactive + + + + + Info + +
+
+ +
+
+ + +
+
+

+ + Alerts & Toasts +

+ +
+ + + + +
+ +
+ +
+
+
+ + +
+
+

Tables

+

+ See your existing table components in the vendor and user list pages. +

+
+
+ +
+
+

Modals

+

+ Modal dialogs for confirmations and forms. +

+
+
+ +
+
+{% endblock %} + +{% block extra_scripts %} +{# ✅ CRITICAL: Load JavaScript file #} + +{% endblock %} \ No newline at end of file diff --git a/app/templates/admin/icons.html b/app/templates/admin/icons.html new file mode 100644 index 00000000..50af40ca --- /dev/null +++ b/app/templates/admin/icons.html @@ -0,0 +1,318 @@ +{# app/templates/admin/icons.html #} +{% extends "admin/base.html" %} + +{% block title %}Icons Browser{% endblock %} + +{# ✅ CRITICAL: Link to Alpine.js component #} +{% block alpine_data %}adminIcons(){% endblock %} + +{% block content %} + +
+

+ Icons Browser +

+ + + Back to Dashboard + +
+ + +
+
+ +
+

Icon Library

+

+ Browse all available icons. Click any icon to copy its name or usage code. +

+
+
+ + Heroicons +
+
+ + Dark Mode Support +
+
+ + Fully Accessible +
+
+
+
+
+ + +
+
+ +
+ +
+ + + + +
+

+ Found icon(s) +

+
+ + +
+ +
+ +
+
+
+ + +
+
+ + + Show All Categories + +
+ +
+
+
+
+ + +
+
+ + + Showing + ( icons) + +
+ +
+ + +
+ +
+ +

No icons found

+

Try adjusting your search or filter

+ +
+ + +
+ +
+
+ + +
+

+ Selected Icon: +

+ +
+ +
+

Preview

+
+
+ +
+
+ +
+
+ +
+
+
+ + +
+

Usage Code

+ + +
+ +
+
+ +
+
+ + +
+ +
+
+ +
+
+
+
+ + +
+

Common Sizes

+
+
+
+
+ +
+ w-4 h-4 +
+
+
+ +
+ w-5 h-5 +
+
+
+ +
+ w-6 h-6 +
+
+
+ +
+ w-8 h-8 +
+
+
+ +
+ w-12 h-12 +
+
+
+
+
+ + +
+

+ + How to Use Icons +

+
+
+

In Alpine.js Templates

+

Use the x-html directive:

+
<span x-html="$icon('home', 'w-5 h-5')"></span>
+
+
+

Customizing Size & Color

+

Use Tailwind classes:

+
<span x-html="$icon('check', 'w-6 h-6 text-green-500')"></span>
+
+
+
+{% endblock %} + +{% block extra_scripts %} +{# ✅ CRITICAL: Load JavaScript file #} + +{% endblock %} \ No newline at end of file diff --git a/static/admin/test-auth-flow.html b/app/templates/admin/test-auth-flow.html similarity index 100% rename from static/admin/test-auth-flow.html rename to app/templates/admin/test-auth-flow.html diff --git a/app/templates/admin/test-vendors-users-migration.html b/app/templates/admin/test-vendors-users-migration.html new file mode 100644 index 00000000..1f80ecf9 --- /dev/null +++ b/app/templates/admin/test-vendors-users-migration.html @@ -0,0 +1,1060 @@ + + + + + + Vendors & Users Migration Testing + + + +
+

🧪 Vendors & Users Migration Testing

+

Comprehensive test suite for verifying the Jinja2 migration of admin vendor and user pages

+ + +
+

📊 Migration Status

+
+
+
Dashboard
+
✅ Complete
+
+
+
Icons & Utils
+
✅ Fixed
+
+
+
Logout Flow
+
✅ Working
+
+
+
Vendors List
+
⏳ Testing
+
+
+
Vendor Edit
+
⏳ Testing
+
+
+
Users Page
+
⏳ Testing
+
+
+
+ + +
+

🚀 Quick Actions

+
+ + + + +
+ +
+
+
+
0% Complete - Start testing!
+
+ + +
+

+ Test 1: Vendors List Page (adminVendors) + High Priority +

+

+ Tests the new vendor LIST functionality. This is a NEW function added to vendors.js alongside the existing vendorCreation() function. +

+ +
+

✅ Page Load Checklist:

+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+

📊 Stats Cards Checklist:

+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+

📋 Table Checklist:

+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+ 🔌 API Endpoints to Check: +
GET/api/v1/admin/vendors
+
GET/api/v1/admin/vendors/stats
+
DELETE/api/v1/admin/vendors/{code}
+
+ +
+ ✅ Expected Result: +
    +
  • NEW adminVendors() function works correctly
  • +
  • Uses ApiClient (uppercase), not apiClient
  • +
  • Uses Logger.info() for logging
  • +
  • Uses Utils.showToast() for notifications
  • +
  • Page matches dashboard styling exactly
  • +
+
+ +
+ + +
+
+ + +
+

+ Test 2: Vendor Edit Page (adminVendorEdit) + High Priority +

+

+ Tests the UPDATED vendor edit functionality. The old vendor-edit.js has been updated to use NEW patterns (ApiClient, Logger, no Auth checks). +

+ +
+

✅ Page Load Checklist:

+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+

🔧 Pattern Update Checklist:

+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+

💾 Form & Actions Checklist:

+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+ 🔌 API Endpoints to Check: +
GET/api/v1/admin/vendors/{code}
+
PUT/api/v1/admin/vendors/{code}
+
PUT/api/v1/admin/vendors/{code}/verification
+
PUT/api/v1/admin/vendors/{code}/status
+
+ +
+ ✅ Expected Result: +
    +
  • OLD patterns removed: apiClient, Auth checks
  • +
  • NEW patterns used: ApiClient, Logger, Utils
  • +
  • Tailwind styling matches dashboard
  • +
  • Simple confirms instead of complex modals
  • +
  • Two-column form layout works
  • +
+
+ +
+ + +
+
+ + +
+

+ Test 3: Users Page (adminUsers) + Medium Priority +

+

+ Tests the NEW users page created from scratch. Should follow the exact same pattern as vendors list. +

+ +
+

✅ Page Load Checklist:

+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+

📊 Stats Cards Checklist:

+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+

📋 Table Checklist:

+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+ 🔌 API Endpoints to Check: +
GET/api/v1/admin/users
+
GET/api/v1/admin/users/stats
+
PUT/api/v1/admin/users/{id}/status
+
+ +
+ ✅ Expected Result: +
    +
  • NEW adminUsers() function works
  • +
  • Follows same pattern as vendors list
  • +
  • Uses ApiClient, Logger, Utils
  • +
  • Matches dashboard styling
  • +
  • Role-based badge colors work
  • +
+
+ +
+ + +
+
+ + +
+

Test 4: Cross-Page Consistency

+

+ Verify that all pages maintain consistent styling and behavior. +

+ +
+

🎨 Styling Consistency:

+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+

🔧 Functional Consistency:

+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+ + +
+
+ + +
+

Test 5: Backend Routes

+

+ Verify that all backend routes are properly configured. +

+ +
+

📍 Route Checklist:

+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+ +
+
+ + +
+

📝 Test Console

+
+
+ [00:00:00] + ℹ️ Test page loaded. Start testing! +
+
+
+ + +
+

ℹ️ Testing Tips

+ +
+ + +
+

⚠️ Important Notes

+ +
+
+ + + + diff --git a/app/templates/admin/testing-hub.html b/app/templates/admin/testing-hub.html new file mode 100644 index 00000000..849516b8 --- /dev/null +++ b/app/templates/admin/testing-hub.html @@ -0,0 +1,205 @@ +{# app/templates/admin/testing-hub.html #} +{% extends "admin/base.html" %} + +{% block title %}Testing Hub{% endblock %} + +{# ✅ CRITICAL: Link to Alpine.js component #} +{% block alpine_data %}adminTestingHub(){% endblock %} + +{% block content %} + +
+

+ Testing Hub +

+
+ + +
+
+
+ +
+
+

Testing & QA Tools

+

+ Comprehensive testing tools for manual QA, feature verification, and bug reproduction. + These pages help you test specific flows without writing code. +

+
+
+
+ + +
+
+
+ +
+
+

Test Suites

+

+
+
+ +
+
+ +
+
+

Test Cases

+

+
+
+ +
+
+ +
+
+

Features Covered

+

+
+
+ +
+
+ +
+
+

Quick Tests

+

+
+
+
+ + +
+ +
+ + +
+

+ + Testing Best Practices +

+ +
+
+

Before Testing

+
    +
  • + + Ensure FastAPI server is running on localhost:8000 +
  • +
  • + + Open browser DevTools (F12) to see console logs +
  • +
  • + + Check Network tab for API requests +
  • +
  • + + Clear localStorage before starting fresh tests +
  • +
+
+ +
+

During Testing

+
    +
  • + + Follow test steps in order +
  • +
  • + + Check expected results against actual behavior +
  • +
  • + + Look for errors in console and network tabs +
  • +
  • + + Take screenshots if you find bugs +
  • +
+
+
+
+ + +
+

+ + Additional Resources +

+
+ +

Component Library

+

View all available UI components

+
+ +

Icons Browser

+

Browse all available icons

+
+ +

API Documentation

+

FastAPI endpoint reference

+
+
+
+ + + + +{% endblock %} + +{% block extra_scripts %} +{# ✅ CRITICAL: Load JavaScript file #} + +{% endblock %} \ No newline at end of file diff --git a/app/templates/admin/vendor-detail.html b/app/templates/admin/vendor-detail.html new file mode 100644 index 00000000..f74e8e5e --- /dev/null +++ b/app/templates/admin/vendor-detail.html @@ -0,0 +1,268 @@ +{# app/templates/admin/vendor-detail.html #} +{% extends "admin/base.html" %} + +{% block title %}Vendor Details{% endblock %} + +{% block alpine_data %}adminVendorDetail(){% endblock %} + +{% block content %} + +
+
+

+ Vendor Details +

+

+ + + +

+
+
+ + + Edit Vendor + + + + Back + +
+
+ + +
+ +

Loading vendor details...

+
+ + +
+ +
+

Error loading vendor

+

+
+
+ + +
+ +
+ +
+
+ +
+
+

+ Verification +

+

+ - +

+
+
+ + +
+
+ +
+
+

+ Status +

+

+ - +

+
+
+ + +
+
+ +
+
+

+ Created +

+

+ - +

+
+
+ + +
+
+ +
+
+

+ Last Updated +

+

+ - +

+
+
+
+ + +
+ +
+

+ Basic Information +

+
+
+

Vendor Code

+

-

+
+
+

Name

+

-

+
+
+

Subdomain

+

-

+
+
+

Description

+

-

+
+
+
+ + +
+

+ Contact Information +

+
+
+

Owner Email

+

-

+

Owner's authentication email

+
+
+

Contact Email

+

-

+

Public business contact

+
+
+

Phone

+

-

+
+
+

Website

+ + + - +
+
+
+
+ + +
+

+ Business Details +

+
+
+

Business Address

+

-

+
+
+

Tax Number

+

-

+
+
+
+ + +
+

+ Owner Information +

+
+
+

Owner User ID

+

-

+
+
+

Owner Username

+

-

+
+
+

Owner Email

+

-

+
+
+
+ + +
+

+ Marketplace CSV URLs +

+
+
+

French (FR)

+ + +
+
+

English (EN)

+ + +
+
+

German (DE)

+ + +
+
+
+ + +
+ +
+
+{% endblock %} + +{% block extra_scripts %} + +{% endblock %} \ No newline at end of file diff --git a/app/templates/admin/vendor-edit.html b/app/templates/admin/vendor-edit.html index a23b7158..337b67c3 100644 --- a/app/templates/admin/vendor-edit.html +++ b/app/templates/admin/vendor-edit.html @@ -13,12 +13,12 @@ Edit Vendor

- - + +

- Back to Vendors @@ -32,30 +32,61 @@ -
- -
- +
+ +
+

+ Quick Actions +

+
+ - + + + +
+ + + Verified + + + + Pending + + + Active + + + Inactive + +
+
- -
+ +
@@ -64,8 +95,8 @@ -
@@ -138,8 +169,8 @@ -
@@ -200,38 +237,38 @@

Business Details

- +
-
-
+ \ No newline at end of file diff --git a/frontend-structure.txt b/frontend-structure.txt index c479116a..c05a520a 100644 --- a/frontend-structure.txt +++ b/frontend-structure.txt @@ -1,19 +1,45 @@ Frontend Folder Structure -Generated: 18/10/2025 13:53:32.04 +Generated: 25/10/2025 16:45:08.55 ============================================================================== Folder PATH listing for volume Data2 -Volume serial number is 00000011 A008:CC27 -E:\LETZSHOP-IMPORT\STATIC +Volume serial number is 0000007B A008:CC27 +E:\FASTAPI-MULTITENANT-ECOMMERCE\STATIC +---admin -| dashboard.html -| login.html -| marketplace.html -| monitoring.html -| users.html -| vendor-edit.html -| vendors.html -| +| | marketplace.html +| | monitoring.html +| | users.html +| | vendor-edit.html +| | vendors.html +| | +| +---css +| | tailwind.output.css +| | +| +---img +| | create-account-office-dark.jpeg +| | create-account-office.jpeg +| | forgot-password-office-dark.jpeg +| | forgot-password-office.jpeg +| | login-office-dark.jpeg +| | login-office.jpeg +| | +| +---js +| | analytics.js +| | components.js +| | dashboard.js +| | icons-page.js +| | init-alpine.js +| | login.js +| | monitoring.js +| | testing-hub.js +| | users.js +| | vendor-detail.js +| | vendor-edit.js +| | vendors.js +| | +| \---partials +| base-layout.html +| +---css | +---admin | | admin.css @@ -21,6 +47,8 @@ E:\LETZSHOP-IMPORT\STATIC | +---shared | | auth.css | | base.css +| | components.css +| | modals.css | | responsive-utilities.css | | | +---shop @@ -29,17 +57,11 @@ E:\LETZSHOP-IMPORT\STATIC | vendor.css | +---js -| +---admin -| | analytics.js -| | dashboard.js -| | login.js -| | monitoring.js -| | vendor-edit.js -| | vendors.js -| | | +---shared -| | api-client.js +| | alpine-components.js | | media-upload.js +| | modal-system.js +| | modal-templates.js | | notification.js | | search.js | | vendor-context.js @@ -50,6 +72,7 @@ E:\LETZSHOP-IMPORT\STATIC | | catalog.js | | checkout.js | | search.js +| | shop-layout-templates.js | | | \---vendor | dashboard.js @@ -59,6 +82,13 @@ E:\LETZSHOP-IMPORT\STATIC | orders.js | payments.js | products.js +| vendor-layout-templates.js +| ++---shared +| \---js +| api-client.js +| icons.js +| utils.js | +---shop | | cart.html diff --git a/scripts/show-frontend-structure.ps1 b/scripts/show-frontend-structure.ps1 deleted file mode 100644 index b3ab6f04..00000000 --- a/scripts/show-frontend-structure.ps1 +++ /dev/null @@ -1,54 +0,0 @@ -# show-frontend-structure.ps1 -# Displays the frontend folder structure - -param( - [string]$Path = "static", - [string]$OutputFile = "frontend-structure.txt" -) - -function Show-Tree { - param( - [string]$Path, - [string]$Indent = "", - [bool]$IsLast = $true, - [System.IO.StreamWriter]$Writer - ) - - $item = Get-Item $Path - $prefix = if ($Indent -eq "") { "" } else { if ($IsLast) { "└── " } else { "├── " } } - - $line = $Indent + $prefix + $item.Name - $Writer.WriteLine($line) - Write-Host $line - - if ($item.PSIsContainer) { - $items = Get-ChildItem $Path | Sort-Object Name - $count = $items.Count - - for ($i = 0; $i -lt $count; $i++) { - $newIndent = $Indent + $(if ($Indent -eq "") { "" } else { if ($IsLast) { " " } else { "│ " } }) - $isLast = ($i -eq ($count - 1)) - Show-Tree -Path $items[$i].FullName -Indent $newIndent -IsLast $isLast -Writer $Writer - } - } -} - -Write-Host "Generating frontend structure..." -ForegroundColor Green -Write-Host "Output will be saved to: $OutputFile" -ForegroundColor Yellow -Write-Host "" - -$writer = [System.IO.StreamWriter]::new($OutputFile) -$writer.WriteLine("Frontend Folder Structure") -$writer.WriteLine("Generated: $(Get-Date)") -$writer.WriteLine("=" * 80) -$writer.WriteLine("") - -Show-Tree -Path $Path -Writer $writer - -$writer.Close() - -Write-Host "" -Write-Host "✅ Structure saved to $OutputFile" -ForegroundColor Green -Write-Host "" -Write-Host "Opening file..." -ForegroundColor Cyan -Start-Process notepad.exe $OutputFile \ No newline at end of file diff --git a/static/admin/js/components.js b/static/admin/js/components.js new file mode 100644 index 00000000..6d6f4ce0 --- /dev/null +++ b/static/admin/js/components.js @@ -0,0 +1,140 @@ +// static/admin/js/components.js + +// Setup logging +const COMPONENTS_LOG_LEVEL = 3; + +const componentsLog = { + error: (...args) => COMPONENTS_LOG_LEVEL >= 1 && console.error('❌ [COMPONENTS ERROR]', ...args), + warn: (...args) => COMPONENTS_LOG_LEVEL >= 2 && console.warn('⚠️ [COMPONENTS WARN]', ...args), + info: (...args) => COMPONENTS_LOG_LEVEL >= 3 && console.info('ℹ️ [COMPONENTS INFO]', ...args), + debug: (...args) => COMPONENTS_LOG_LEVEL >= 4 && console.log('🔍 [COMPONENTS DEBUG]', ...args) +}; + +/** + * Components Library Alpine.js Component + * UI components reference with live examples + */ +function adminComponents() { + return { + // ✅ CRITICAL: Inherit base layout functionality + ...data(), + + // ✅ CRITICAL: Set page identifier + currentPage: 'components', + + // Active section for navigation + activeSection: 'forms', + + // Component sections + sections: [ + { id: 'forms', name: 'Forms', icon: 'clipboard-list' }, + { id: 'buttons', name: 'Buttons', icon: 'cursor-click' }, + { 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' } + ], + + // Sample form data for examples + exampleForm: { + textInput: 'Sample text', + email: 'user@example.com', + textarea: 'Sample description text...', + select: 'option1', + checkbox: true, + radio: 'option1', + disabled: 'Read-only value' + }, + + // Sample errors for validation examples + exampleErrors: { + email: 'Please enter a valid email address', + required: 'This field is required' + }, + + // ✅ CRITICAL: Proper initialization with guard + async init() { + componentsLog.info('=== COMPONENTS PAGE INITIALIZING ==='); + + // Prevent multiple initializations + if (window._componentsInitialized) { + componentsLog.warn('Components page already initialized, skipping...'); + return; + } + window._componentsInitialized = true; + + // Set active section from URL hash if present + this.setActiveSectionFromHash(); + + // Listen for hash changes + window.addEventListener('hashchange', () => { + this.setActiveSectionFromHash(); + }); + + componentsLog.info('=== COMPONENTS PAGE INITIALIZATION COMPLETE ==='); + }, + + /** + * Set active section from URL hash + */ + setActiveSectionFromHash() { + const hash = window.location.hash.replace('#', ''); + if (hash && this.sections.find(s => s.id === hash)) { + this.activeSection = hash; + componentsLog.debug('Set active section from hash:', hash); + } + }, + + /** + * Navigate to section + */ + goToSection(sectionId) { + componentsLog.info('Navigating to section:', sectionId); + this.activeSection = sectionId; + window.location.hash = sectionId; + + // Smooth scroll to section + const element = document.getElementById(sectionId); + if (element) { + element.scrollIntoView({ behavior: 'smooth', block: 'start' }); + } + }, + + /** + * Check if section is active + */ + isSectionActive(sectionId) { + return this.activeSection === sectionId; + }, + + /** + * Copy code to clipboard + */ + async copyCode(code) { + try { + await navigator.clipboard.writeText(code); + Utils.showToast('Code copied to clipboard!', 'success'); + componentsLog.debug('Code copied to clipboard'); + } catch (error) { + componentsLog.error('Failed to copy code:', error); + Utils.showToast('Failed to copy code', 'error'); + } + }, + + /** + * Show toast example + */ + showToastExample(type) { + const messages = { + success: 'Operation completed successfully!', + error: 'An error occurred!', + warning: 'Please review your input.', + info: 'Here is some information.' + }; + Utils.showToast(messages[type] || messages.info, type); + } + }; +} + +componentsLog.info('Components module loaded'); \ No newline at end of file diff --git a/static/admin/js/icons-page.js b/static/admin/js/icons-page.js new file mode 100644 index 00000000..4e3bb0eb --- /dev/null +++ b/static/admin/js/icons-page.js @@ -0,0 +1,210 @@ +// static/admin/js/icons-page.js + +// Setup logging +const ICONS_PAGE_LOG_LEVEL = 3; + +const iconsLog = { + error: (...args) => ICONS_PAGE_LOG_LEVEL >= 1 && console.error('❌ [ICONS PAGE ERROR]', ...args), + warn: (...args) => ICONS_PAGE_LOG_LEVEL >= 2 && console.warn('⚠️ [ICONS PAGE WARN]', ...args), + info: (...args) => ICONS_PAGE_LOG_LEVEL >= 3 && console.info('ℹ️ [ICONS PAGE INFO]', ...args), + debug: (...args) => ICONS_PAGE_LOG_LEVEL >= 4 && console.log('🔍 [ICONS PAGE DEBUG]', ...args) +}; + +/** + * Icons Browser Alpine.js Component + * Browse and search all available icons + */ +function adminIcons() { + return { + // ✅ CRITICAL: Inherit base layout functionality + ...data(), + + // ✅ CRITICAL: Set page identifier + currentPage: 'icons', + + // Search and filter + searchQuery: '', + activeCategory: 'all', + + // Icon categories + categories: [ + { id: 'all', name: 'All Icons', icon: 'collection' }, + { id: 'navigation', name: 'Navigation', icon: 'menu' }, + { id: 'user', name: 'User & Profile', icon: 'user' }, + { id: 'actions', name: 'Actions', icon: 'lightning-bolt' }, + { id: 'ecommerce', name: 'E-commerce', icon: 'shopping-bag' }, + { id: 'inventory', name: 'Inventory', icon: 'cube' }, + { id: 'communication', name: 'Communication', icon: 'mail' }, + { id: 'files', name: 'Files', icon: 'document' }, + { id: 'settings', name: 'Settings', icon: 'cog' }, + { id: 'status', name: 'Status', icon: 'check-circle' }, + { id: 'testing', name: 'Testing', icon: 'beaker' } + ], + + // All icons organized by category + iconsByCategory: {}, + allIcons: [], + filteredIcons: [], + + // Selected icon for detail view + selectedIcon: null, + + // ✅ CRITICAL: Proper initialization with guard + async init() { + iconsLog.info('=== ICONS PAGE INITIALIZING ==='); + + // Prevent multiple initializations + if (window._iconsPageInitialized) { + iconsLog.warn('Icons page already initialized, skipping...'); + return; + } + window._iconsPageInitialized = true; + + // Load icons from global Icons object + this.loadIcons(); + + iconsLog.info('=== ICONS PAGE INITIALIZATION COMPLETE ==='); + }, + + /** + * Load icons from global Icons object + */ + loadIcons() { + if (!window.Icons) { + iconsLog.error('Icons object not found! Make sure icons.js is loaded.'); + return; + } + + // Get all icon names + this.allIcons = Object.keys(window.Icons).map(name => ({ + name: name, + category: this.categorizeIcon(name) + })); + + // Organize by category + this.iconsByCategory = this.allIcons.reduce((acc, icon) => { + if (!acc[icon.category]) { + acc[icon.category] = []; + } + acc[icon.category].push(icon); + return acc; + }, {}); + + // Initial filter + this.filterIcons(); + + iconsLog.info(`Loaded ${this.allIcons.length} icons across ${Object.keys(this.iconsByCategory).length} categories`); + }, + + /** + * Categorize icon based on name + */ + categorizeIcon(iconName) { + const categoryMap = { + navigation: ['home', 'menu', 'search', 'arrow', 'chevron'], + user: ['user', 'identification', 'badge'], + actions: ['edit', 'delete', 'plus', 'check', 'close', 'refresh', 'duplicate', 'eye', 'filter', 'dots'], + ecommerce: ['shopping', 'credit-card', 'currency', 'gift', 'tag', 'truck', 'receipt'], + inventory: ['cube', 'collection', 'photograph', 'chart'], + communication: ['mail', 'phone', 'chat', 'bell', 'inbox'], + files: ['document', 'folder', 'download', 'upload'], + settings: ['cog', 'adjustments', 'calendar', 'moon', 'sun'], + status: ['exclamation', 'information', 'spinner', 'star', 'heart', 'flag'], + testing: ['view-grid', 'beaker', 'clipboard-list', 'check-circle', 'lightning-bolt', 'clock', 'lock-closed', 'database', 'light-bulb', 'book-open', 'play'] + }; + + for (const [category, keywords] of Object.entries(categoryMap)) { + if (keywords.some(keyword => iconName.includes(keyword))) { + return category; + } + } + return 'navigation'; // default + }, + + /** + * Filter icons based on search and category + */ + filterIcons() { + let icons = this.allIcons; + + // Filter by category + if (this.activeCategory !== 'all') { + icons = icons.filter(icon => icon.category === this.activeCategory); + } + + // Filter by search query + if (this.searchQuery.trim()) { + const query = this.searchQuery.toLowerCase(); + icons = icons.filter(icon => icon.name.toLowerCase().includes(query)); + } + + this.filteredIcons = icons; + iconsLog.debug(`Filtered to ${icons.length} icons`); + }, + + /** + * Set active category + */ + setCategory(categoryId) { + iconsLog.info('Setting category:', categoryId); + this.activeCategory = categoryId; + this.filterIcons(); + }, + + /** + * Select icon for detail view + */ + selectIcon(iconName) { + iconsLog.info('Selected icon:', iconName); + this.selectedIcon = iconName; + }, + + /** + * Copy icon usage code to clipboard + */ + async copyIconUsage(iconName) { + const code = `x-html="$icon('${iconName}', 'w-5 h-5')"`; + try { + await navigator.clipboard.writeText(code); + Utils.showToast(`'${iconName}' code copied!`, 'success'); + iconsLog.debug('Icon usage code copied:', iconName); + } catch (error) { + iconsLog.error('Failed to copy code:', error); + Utils.showToast('Failed to copy code', 'error'); + } + }, + + /** + * Copy icon name to clipboard + */ + async copyIconName(iconName) { + try { + await navigator.clipboard.writeText(iconName); + Utils.showToast(`'${iconName}' copied!`, 'success'); + iconsLog.debug('Icon name copied:', iconName); + } catch (error) { + iconsLog.error('Failed to copy name:', error); + Utils.showToast('Failed to copy name', 'error'); + } + }, + + /** + * Get category info + */ + getCategoryInfo(categoryId) { + return this.categories.find(c => c.id === categoryId) || this.categories[0]; + }, + + /** + * Get icon count for category + */ + getCategoryCount(categoryId) { + if (categoryId === 'all') { + return this.allIcons.length; + } + return this.iconsByCategory[categoryId]?.length || 0; + } + }; +} + +iconsLog.info('Icons page module loaded'); \ No newline at end of file diff --git a/static/admin/js/testing-hub.js b/static/admin/js/testing-hub.js new file mode 100644 index 00000000..6122a79d --- /dev/null +++ b/static/admin/js/testing-hub.js @@ -0,0 +1,131 @@ +// static/admin/js/testing-hub.js + +// Setup logging +const TESTING_HUB_LOG_LEVEL = 3; + +const testingLog = { + error: (...args) => TESTING_HUB_LOG_LEVEL >= 1 && console.error('❌ [TESTING HUB ERROR]', ...args), + warn: (...args) => TESTING_HUB_LOG_LEVEL >= 2 && console.warn('⚠️ [TESTING HUB WARN]', ...args), + info: (...args) => TESTING_HUB_LOG_LEVEL >= 3 && console.info('ℹ️ [TESTING HUB INFO]', ...args), + debug: (...args) => TESTING_HUB_LOG_LEVEL >= 4 && console.log('🔍 [TESTING HUB DEBUG]', ...args) +}; + +/** + * Testing Hub Alpine.js Component + * Central hub for all test suites and QA tools + */ +function adminTestingHub() { + return { + // ✅ CRITICAL: Inherit base layout functionality + ...data(), + + // ✅ CRITICAL: Set page identifier + currentPage: 'testing', + + // Test suites data + testSuites: [ + { + id: 'auth-flow', + name: 'Authentication Flow', + description: 'Test login, logout, token expiration, redirects, and protected route access.', + url: '/admin/test/auth-flow', + icon: 'lock-closed', + color: 'blue', + testCount: 6, + features: [ + 'Login with valid/invalid credentials', + 'Token expiration handling', + 'Protected route access & redirects', + 'localStorage state monitoring' + ] + }, + { + id: 'vendors-users', + name: 'Data Migration & CRUD', + description: 'Test vendor and user creation, listing, editing, deletion, and data migration scenarios.', + url: '/admin/test/vendors-users-migration', + icon: 'database', + color: 'orange', + testCount: 10, + features: [ + 'Vendor CRUD operations', + 'User management & roles', + 'Data migration validation', + 'Form validation & error handling' + ] + } + ], + + // Stats + stats: { + totalSuites: 2, + totalTests: 16, + coverage: 'Auth, CRUD', + avgDuration: '< 5 min' + }, + + // Loading state + loading: false, + + // ✅ CRITICAL: Proper initialization with guard + async init() { + testingLog.info('=== TESTING HUB INITIALIZING ==='); + + // Prevent multiple initializations + if (window._testingHubInitialized) { + testingLog.warn('Testing hub already initialized, skipping...'); + return; + } + window._testingHubInitialized = true; + + // Calculate stats + this.calculateStats(); + + testingLog.info('=== TESTING HUB INITIALIZATION COMPLETE ==='); + }, + + /** + * Calculate test statistics + */ + calculateStats() { + this.stats.totalSuites = this.testSuites.length; + this.stats.totalTests = this.testSuites.reduce((sum, suite) => sum + suite.testCount, 0); + testingLog.debug('Stats calculated:', this.stats); + }, + + /** + * Get color classes for test suite cards + */ + getColorClasses(color) { + const colorMap = { + blue: { + gradient: 'from-blue-500 to-blue-600', + button: 'bg-blue-600 hover:bg-blue-700' + }, + orange: { + gradient: 'from-orange-500 to-orange-600', + button: 'bg-orange-600 hover:bg-orange-700' + }, + green: { + gradient: 'from-green-500 to-green-600', + button: 'bg-green-600 hover:bg-green-700' + }, + purple: { + gradient: 'from-purple-500 to-purple-600', + button: 'bg-purple-600 hover:bg-purple-700' + } + }; + return colorMap[color] || colorMap.blue; + }, + + /** + * Navigate to test suite + */ + goToTest(url) { + testingLog.info('Navigating to test suite:', url); + window.location.href = url; + } + }; +} + +testingLog.info('Testing hub module loaded'); \ No newline at end of file diff --git a/static/admin/js/vendor-detail.js b/static/admin/js/vendor-detail.js new file mode 100644 index 00000000..754eb72b --- /dev/null +++ b/static/admin/js/vendor-detail.js @@ -0,0 +1,135 @@ +// static/admin/js/vendor-detail.js + +// Log levels: 0 = None, 1 = Error, 2 = Warning, 3 = Info, 4 = Debug +const VENDOR_DETAIL_LOG_LEVEL = 3; + +const detailLog = { + error: (...args) => VENDOR_DETAIL_LOG_LEVEL >= 1 && console.error('❌ [VENDOR_DETAIL ERROR]', ...args), + warn: (...args) => VENDOR_DETAIL_LOG_LEVEL >= 2 && console.warn('⚠️ [VENDOR_DETAIL WARN]', ...args), + info: (...args) => VENDOR_DETAIL_LOG_LEVEL >= 3 && console.info('ℹ️ [VENDOR_DETAIL INFO]', ...args), + debug: (...args) => VENDOR_DETAIL_LOG_LEVEL >= 4 && console.log('🔍 [VENDOR_DETAIL DEBUG]', ...args) +}; + +function adminVendorDetail() { + return { + // Inherit base layout functionality from init-alpine.js + ...data(), + + // Vendor detail page specific state + currentPage: 'vendor-detail', + vendor: null, + loading: false, + error: null, + vendorCode: null, + + // Initialize + async init() { + detailLog.info('=== VENDOR DETAIL PAGE INITIALIZING ==='); + + // Prevent multiple initializations + if (window._vendorDetailInitialized) { + detailLog.warn('Vendor detail page already initialized, skipping...'); + return; + } + window._vendorDetailInitialized = true; + + // Get vendor code from URL + const path = window.location.pathname; + const match = path.match(/\/admin\/vendors\/([^\/]+)$/); + + if (match) { + this.vendorCode = match[1]; + detailLog.info('Viewing vendor:', this.vendorCode); + await this.loadVendor(); + } else { + detailLog.error('No vendor code in URL'); + this.error = 'Invalid vendor URL'; + Utils.showToast('Invalid vendor URL', 'error'); + } + + detailLog.info('=== VENDOR DETAIL PAGE INITIALIZATION COMPLETE ==='); + }, + + // Load vendor data + async loadVendor() { + detailLog.info('Loading vendor details...'); + this.loading = true; + this.error = null; + + try { + const startTime = Date.now(); + const response = await apiClient.get(`/admin/vendors/${this.vendorCode}`); + const duration = Date.now() - startTime; + + this.vendor = response; + + detailLog.info(`Vendor loaded in ${duration}ms`, { + vendor_code: this.vendor.vendor_code, + name: this.vendor.name, + is_verified: this.vendor.is_verified, + is_active: this.vendor.is_active + }); + detailLog.debug('Full vendor data:', this.vendor); + + } catch (error) { + detailLog.error('Failed to load vendor:', error); + this.error = error.message || 'Failed to load vendor details'; + Utils.showToast('Failed to load vendor details', 'error'); + } finally { + this.loading = false; + } + }, + + // Format date (matches dashboard pattern) + formatDate(dateString) { + if (!dateString) { + detailLog.debug('formatDate called with empty dateString'); + return '-'; + } + const formatted = Utils.formatDate(dateString); + detailLog.debug(`Date formatted: ${dateString} -> ${formatted}`); + return formatted; + }, + + // Delete vendor + async deleteVendor() { + detailLog.info('Delete vendor requested:', this.vendorCode); + + if (!confirm(`Are you sure you want to delete vendor "${this.vendor.name}"?\n\nThis action cannot be undone and will delete:\n- All products\n- All orders\n- All customers\n- All team members`)) { + detailLog.info('Delete cancelled by user'); + return; + } + + // Second confirmation for safety + if (!confirm(`FINAL CONFIRMATION\n\nType the vendor code to confirm: ${this.vendor.vendor_code}\n\nAre you absolutely sure?`)) { + detailLog.info('Delete cancelled by user (second confirmation)'); + return; + } + + try { + detailLog.info('Deleting vendor:', this.vendorCode); + await apiClient.delete(`/admin/vendors/${this.vendorCode}?confirm=true`); + + Utils.showToast('Vendor deleted successfully', 'success'); + detailLog.info('Vendor deleted successfully'); + + // Redirect to vendors list + setTimeout(() => window.location.href = '/admin/vendors', 1500); + + } catch (error) { + detailLog.error('Failed to delete vendor:', error); + Utils.showToast(error.message || 'Failed to delete vendor', 'error'); + } + }, + + // Refresh vendor data + async refresh() { + detailLog.info('=== VENDOR REFRESH TRIGGERED ==='); + await this.loadVendor(); + Utils.showToast('Vendor details refreshed', 'success'); + detailLog.info('=== VENDOR REFRESH COMPLETE ==='); + } + }; +} + +detailLog.info('Vendor detail module loaded'); \ No newline at end of file diff --git a/static/admin/js/vendors.js b/static/admin/js/vendors.js index 3bb5c88a..62be60af 100644 --- a/static/admin/js/vendors.js +++ b/static/admin/js/vendors.js @@ -18,8 +18,10 @@ function adminVendors() { // Inherit base layout functionality from init-alpine.js ...data(), - // Vendors page specific state + // ✅ CRITICAL: Page identifier for sidebar active state currentPage: 'vendors', + + // Vendors page specific state vendors: [], stats: { total: 0, @@ -30,8 +32,8 @@ function adminVendors() { loading: false, error: null, - // Pagination state - currentPage: 1, + // Pagination state (renamed from currentPage to avoid conflict) + page: 1, // ✅ FIXED: Was 'currentPage' which conflicted with sidebar itemsPerPage: 10, // Initialize @@ -53,7 +55,7 @@ function adminVendors() { // Computed: Get paginated vendors for current page get paginatedVendors() { - const start = (this.currentPage - 1) * this.itemsPerPage; + const start = (this.page - 1) * this.itemsPerPage; const end = start + this.itemsPerPage; return this.vendors.slice(start, end); }, @@ -66,12 +68,12 @@ function adminVendors() { // Computed: Start index for pagination display get startIndex() { if (this.vendors.length === 0) return 0; - return (this.currentPage - 1) * this.itemsPerPage + 1; + return (this.page - 1) * this.itemsPerPage + 1; }, // Computed: End index for pagination display get endIndex() { - const end = this.currentPage * this.itemsPerPage; + const end = this.page * this.itemsPerPage; return end > this.vendors.length ? this.vendors.length : end; }, @@ -79,7 +81,7 @@ function adminVendors() { get pageNumbers() { const pages = []; const totalPages = this.totalPages; - const current = this.currentPage; + const current = this.page; if (totalPages <= 7) { // Show all pages if 7 or fewer @@ -137,7 +139,7 @@ function adminVendors() { } // Reset to first page when data is loaded - this.currentPage = 1; + this.page = 1; } catch (error) { vendorsLog.error('Failed to load vendors:', error); @@ -167,27 +169,27 @@ function adminVendors() { }, // Pagination: Go to specific page - goToPage(page) { - if (page === '...' || page < 1 || page > this.totalPages) { + goToPage(pageNum) { + if (pageNum === '...' || pageNum < 1 || pageNum > this.totalPages) { return; } - vendorsLog.info('Going to page:', page); - this.currentPage = page; + vendorsLog.info('Going to page:', pageNum); + this.page = pageNum; }, // Pagination: Go to next page nextPage() { - if (this.currentPage < this.totalPages) { + if (this.page < this.totalPages) { vendorsLog.info('Going to next page'); - this.currentPage++; + this.page++; } }, // Pagination: Go to previous page previousPage() { - if (this.currentPage > 1) { + if (this.page > 1) { vendorsLog.info('Going to previous page'); - this.currentPage--; + this.page--; } }, diff --git a/static/shared/js/icons.js b/static/shared/js/icons.js index 0f8257d6..c8455bd6 100644 --- a/static/shared/js/icons.js +++ b/static/shared/js/icons.js @@ -53,50 +53,34 @@ const Icons = { 'cube': ``, 'collection': ``, 'photograph': ``, - 'color-swatch': ``, - 'template': ``, - 'clipboard-list': ``, - - // Analytics & Reports - 'chart': ``, - 'trending-up': ``, - 'trending-down': ``, - 'presentation-chart-line': ``, - 'calculator': ``, - + 'chart-bar': ``, + 'chart-pie': ``, + // Communication - 'bell': ``, 'mail': ``, - 'chat': ``, - 'annotation': ``, 'phone': ``, - - // System & Settings - 'cog': ``, - 'sun': ``, - 'moon': ``, - 'database': ``, - 'server': ``, - 'shield-check': ``, - 'key': ``, - 'lock-closed': ``, - 'lock-open': ``, - - // Document & File + 'chat': ``, + 'bell': ``, + 'inbox': ``, + + // Files & Documents 'document': ``, 'folder': ``, 'folder-open': ``, 'download': ``, 'upload': ``, - - // Time & Calendar + + // Settings & Tools + 'cog': ``, + 'adjustments': ``, 'calendar': ``, - 'clock': ``, - + 'moon': ``, + 'sun': ``, + // Location 'location-marker': ``, 'globe': ``, - + // Status & Indicators 'exclamation': ``, 'information-circle': ``, @@ -104,11 +88,26 @@ const Icons = { 'star': ``, 'heart': ``, 'flag': ``, - + // Links & External 'external-link': ``, 'link': ``, - 'logout': `` + 'logout': ``, + + // Developer Tools (Testing Section) + 'view-grid': ``, + 'beaker': ``, + + // Testing & QA Icons + 'clipboard-list': ``, + 'check-circle': ``, + 'lightning-bolt': ``, + 'clock': ``, + 'lock-closed': ``, + 'database': ``, + 'light-bulb': ``, + 'book-open': ``, + 'play': `` }; /** @@ -133,7 +132,7 @@ function icon(name, classes = 'w-5 h-5') { document.addEventListener('alpine:init', () => { // ✅ CORRECT: Return the function directly, not wrapped in another function Alpine.magic('icon', () => icon); - + console.log('✅ Alpine $icon magic helper registered'); }); @@ -147,4 +146,4 @@ window.icon = icon; window.Icons = Icons; console.log('📦 Icon system loaded'); -console.log('📊 Total icons available:', Object.keys(Icons).length); +console.log('📊 Total icons available:', Object.keys(Icons).length); \ No newline at end of file