Some checks failed
- Add Development URL Quick Reference section to url-routing overview with all login URLs, entry points, and full examples - Replace /shop/ path segments with /storefront/ across 50 docs files - Update file references: shop_pages.py → storefront_pages.py, templates/shop/ → templates/storefront/, api/v1/shop/ → api/v1/storefront/ - Preserve domain references (orion.shop) and /store/ staff dashboard paths - Archive docs left unchanged (historical) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
394 lines
10 KiB
Markdown
394 lines
10 KiB
Markdown
# Storefront Troubleshooting
|
|
|
|
Common issues and solutions for the store storefront.
|
|
|
|
## Cart and Product Issues
|
|
|
|
### Products Cannot Be Added to Cart
|
|
|
|
**Symptoms:**
|
|
- "Add to Cart" button is disabled
|
|
- No response when clicking "Add to Cart"
|
|
- Console shows `canAddToCart: false`
|
|
|
|
**Root Cause:**
|
|
Products have `available_inventory: 0` because the `inventory` table is empty.
|
|
|
|
**How it Works:**
|
|
- Product's `available_inventory` is a calculated property
|
|
- It sums `quantity - reserved_quantity` from all `inventory_entries`
|
|
- If there are no inventory entries, `available_inventory` = 0
|
|
- The `canAddToCart` check fails when `available_inventory <= 0`
|
|
|
|
**Solution:**
|
|
```bash
|
|
# Create inventory entries for all products
|
|
python scripts/create_inventory.py
|
|
```
|
|
|
|
This script:
|
|
- Finds all products without inventory entries
|
|
- Creates inventory with 100 units in "Main Warehouse"
|
|
- Can be run multiple times (only creates missing entries)
|
|
|
|
**Verify Fix:**
|
|
```python
|
|
# Check a product's inventory
|
|
from app.core.database import SessionLocal
|
|
from models.database.product import Product
|
|
|
|
db = SessionLocal()
|
|
product = db.query(Product).first()
|
|
print(f"Available inventory: {product.available_inventory}")
|
|
# Should show: Available inventory: 100
|
|
```
|
|
|
|
### Cart is Empty After Adding Products
|
|
|
|
**Symptoms:**
|
|
- Success toast appears: "1 item(s) added to cart!"
|
|
- Cart count in header doesn't update
|
|
- Cart page shows 0 items
|
|
- Console shows `session_1234_abc` but API returns empty cart
|
|
|
|
**Root Cause:**
|
|
Session ID not properly initialized in Alpine.js components.
|
|
|
|
**How it Works:**
|
|
- Cart uses session ID stored in localStorage as `cart_session_id`
|
|
- `storefrontLayoutData()` base component initializes `sessionId` in its `init()` method
|
|
- Child components (product, cart) must call parent `init()` to get `sessionId`
|
|
- If parent init isn't called, `sessionId` is `undefined`
|
|
|
|
**Solution Already Implemented:**
|
|
The product and cart pages now properly call parent init:
|
|
|
|
```javascript
|
|
// In product.html and cart.html
|
|
async init() {
|
|
// Call parent init to set up sessionId
|
|
if (baseData.init) {
|
|
baseData.init.call(this);
|
|
}
|
|
// Now sessionId is available
|
|
await this.loadCart();
|
|
}
|
|
```
|
|
|
|
**Verify Fix:**
|
|
Open browser console and check for:
|
|
```
|
|
🔍 [STOREFRONT] Session ID: session_1763765104510_zc866tt5d
|
|
```
|
|
|
|
If you see `undefined`, the parent init isn't being called properly.
|
|
|
|
## Styling and Layout Issues
|
|
|
|
### Page Has No Styling / CSS 404 Errors
|
|
|
|
**Symptoms:**
|
|
- Page displays with no styling
|
|
- Console shows 404 errors:
|
|
- `/static/css/shared/base.css` not found
|
|
- `/static/css/store/store.css` not found
|
|
- Page is just plain HTML
|
|
|
|
**Root Cause:**
|
|
Template doesn't extend `storefront/base.html` and has hardcoded non-existent CSS references.
|
|
|
|
**How it Works:**
|
|
- All storefront pages should extend `storefront/base.html`
|
|
- Base template includes Tailwind CSS and storefront styles
|
|
- Standalone HTML pages with `<link>` tags won't work
|
|
|
|
**Solution:**
|
|
Refactor template to extend base:
|
|
|
|
```jinja2
|
|
{# BEFORE: Standalone HTML #}
|
|
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<link rel="stylesheet" href="/static/css/shared/base.css"> ❌
|
|
</head>
|
|
<body>
|
|
<!-- content -->
|
|
</body>
|
|
</html>
|
|
|
|
{# AFTER: Extends base #}
|
|
{% extends "storefront/base.html" %}
|
|
|
|
{% block title %}My Page{% endblock %}
|
|
|
|
{% block alpine_data %}myComponent(){% endblock %}
|
|
|
|
{% block content %}
|
|
<!-- content -->
|
|
{% endblock %}
|
|
```
|
|
|
|
**Templates Already Fixed:**
|
|
- ✅ `storefront/product.html` - Refactored to extend base
|
|
- ✅ `storefront/cart.html` - Refactored to extend base
|
|
- ✅ `storefront/products.html` - Already extends base
|
|
- ✅ `storefront/home.html` - Already extends base
|
|
|
|
### Images Not Loading / Placeholder Not Showing
|
|
|
|
**Symptoms:**
|
|
- Product images show broken image icon
|
|
- Console shows 404: `/static/storefront/img/placeholder.jpg`
|
|
- OR images point to fake URLs like `https://orion.example.com/images/product-1.jpg`
|
|
|
|
**Root Cause:**
|
|
1. Test data has fake image URLs
|
|
2. Placeholder file was `.jpg` but contained SVG content
|
|
|
|
**How it Works:**
|
|
- Products have `marketplace_product.image_link` URLs
|
|
- Template uses fallback: `:src="image || '/static/storefront/img/placeholder.svg'"`
|
|
- `@error` handler switches to placeholder when image fails to load
|
|
- Browsers won't render SVG content from `.jpg` files
|
|
|
|
**Solution Already Implemented:**
|
|
- Created proper `/static/storefront/img/placeholder.svg`
|
|
- Added `@error` handlers to all image tags:
|
|
|
|
```html
|
|
<img
|
|
:src="product.image_link || '/static/storefront/img/placeholder.svg'"
|
|
@error="$el.src = '/static/storefront/img/placeholder.svg'"
|
|
alt="Product image"
|
|
>
|
|
```
|
|
|
|
**Update Test Data (Optional):**
|
|
```python
|
|
# Replace fake URLs with real placeholder images
|
|
import sqlite3
|
|
conn = sqlite3.connect('orion.db')
|
|
cursor = conn.cursor()
|
|
|
|
cursor.execute("""
|
|
UPDATE marketplace_products
|
|
SET image_link = NULL
|
|
WHERE image_link LIKE '%example.com%'
|
|
""")
|
|
conn.commit()
|
|
```
|
|
|
|
## Navigation and Routing Issues
|
|
|
|
### Product Detail Page Returns 404
|
|
|
|
**Symptoms:**
|
|
- Clicking product shows 404 error
|
|
- URL is: `/storefront/orion/storefront/products/4` (double `/storefront/`)
|
|
- Server log shows route not found
|
|
|
|
**Root Cause:**
|
|
Duplicate `/storefront` prefix in route definitions when router already has prefix.
|
|
|
|
**How it Works:**
|
|
```python
|
|
# Router is mounted with prefix
|
|
app.include_router(storefront_pages.router, prefix="/storefront")
|
|
|
|
# Route decorator should NOT repeat the prefix
|
|
@router.get("/products/{id}") # ✅ Correct
|
|
@router.get("/storefront/products/{id}") # ❌ Wrong - creates /storefront/storefront/products/{id}
|
|
```
|
|
|
|
**Solution Already Implemented:**
|
|
All routes in `storefront_pages.py` have been fixed to remove duplicate `/storefront/` prefix.
|
|
|
|
### Missing `/storefront/` in Links
|
|
|
|
**Symptoms:**
|
|
- Links go to `/storefront/orion/products` instead of correct storefront URLs
|
|
- 404 errors on navigation
|
|
- Footer/header links broken
|
|
|
|
**Root Cause:**
|
|
Template links missing `/storefront/` prefix after `{{ base_url }}`.
|
|
|
|
**Solution:**
|
|
```jinja2
|
|
{# WRONG #}
|
|
<a href="{{ base_url }}products">Products</a>
|
|
|
|
{# CORRECT #}
|
|
<a href="{{ base_url }}storefront/products">Products</a>
|
|
```
|
|
|
|
**All Templates Fixed:**
|
|
- ✅ `storefront/base.html` - Header, footer, navigation
|
|
- ✅ `storefront/products.html` - Product links
|
|
- ✅ `storefront/product.html` - Breadcrumbs, related products
|
|
- ✅ `storefront/cart.html` - Continue shopping, checkout
|
|
- ✅ `storefront/errors/404.html` - All fallback links
|
|
|
|
## Landing Page Issues
|
|
|
|
### Store Root Shows 404
|
|
|
|
**Symptoms:**
|
|
- `http://localhost:8000/stores/orion/` returns 404
|
|
- `/` path not found
|
|
|
|
**Root Cause:**
|
|
No landing page created for store.
|
|
|
|
**How it Works:**
|
|
```python
|
|
# Root handler checks for landing page
|
|
if has_landing_page():
|
|
return render_landing_page()
|
|
else:
|
|
return redirect("/storefront/") # Fallback
|
|
```
|
|
|
|
**Solution:**
|
|
```bash
|
|
# Create landing page
|
|
python scripts/create_landing_page.py
|
|
# Choose store and template
|
|
```
|
|
|
|
**Or Accept Redirect:**
|
|
The system auto-redirects to `/storefront/` if no landing page exists. This is normal behavior.
|
|
|
|
### Breadcrumb "Home" Points to Wrong Place
|
|
|
|
**Symptoms:**
|
|
- Clicking "Home" in breadcrumb goes to storefront instead of landing page
|
|
- Want "Home" to point to store root (/)
|
|
|
|
**Solution Already Implemented:**
|
|
```jinja2
|
|
{# Breadcrumb Home link points to store root #}
|
|
<a href="{{ base_url }}">Home</a>
|
|
|
|
{# Storefront homepage link #}
|
|
<a href="{{ base_url }}storefront/">Storefront</a>
|
|
```
|
|
|
|
Navigation pattern:
|
|
- **Logo click** → `/storefront/` (stays in storefront for convenience)
|
|
- **Breadcrumb "Home"** → `/` (returns to landing page/root)
|
|
- **Header "Home" link** → `/` (returns to landing page/root)
|
|
|
|
## Alpine.js / JavaScript Issues
|
|
|
|
### Component Data Not Available
|
|
|
|
**Symptoms:**
|
|
- `this.sessionId` is `undefined`
|
|
- `this.cartCount` doesn't work
|
|
- Console: "Cannot read property of undefined"
|
|
|
|
**Root Cause:**
|
|
Child component doesn't call parent `init()` method.
|
|
|
|
**Solution:**
|
|
```javascript
|
|
// Store reference to parent
|
|
const baseData = storefrontLayoutData();
|
|
|
|
return {
|
|
...baseData,
|
|
|
|
// Child properties
|
|
items: [],
|
|
|
|
// Call parent init
|
|
async init() {
|
|
if (baseData.init) {
|
|
baseData.init.call(this);
|
|
}
|
|
// Now parent properties are initialized
|
|
await this.loadData();
|
|
}
|
|
};
|
|
```
|
|
|
|
### Product ID is Undefined
|
|
|
|
**Symptoms:**
|
|
- API call: `/api/v1/storefront/products/undefined`
|
|
- Console: `productId: undefined`
|
|
|
|
**Root Cause:**
|
|
Product ID not accessible from `this.$el.dataset` when `x-data` is on `<html>` tag.
|
|
|
|
**Solution Already Implemented:**
|
|
Pass via window globals:
|
|
|
|
```html
|
|
{% block extra_scripts %}
|
|
<script>
|
|
window.PRODUCT_ID = {{ product_id }};
|
|
window.STORE_ID = {{ store.id }};
|
|
|
|
Alpine.data('productDetail', () => ({
|
|
productId: window.PRODUCT_ID,
|
|
storeId: window.STORE_ID,
|
|
// ...
|
|
}));
|
|
</script>
|
|
{% endblock %}
|
|
```
|
|
|
|
## Debugging Tips
|
|
|
|
### Enable Verbose Logging
|
|
|
|
```javascript
|
|
// In storefront-layout.js, the storefrontLog is already configured
|
|
// Check browser console for:
|
|
🛒 [STOREFRONT] Storefront layout initializing...
|
|
🔍 [STOREFRONT] Session ID: session_xxx
|
|
[STOREFRONT] Adding to cart: {...}
|
|
[STOREFRONT] Cart loaded: 3 items
|
|
```
|
|
|
|
### Check Session ID
|
|
|
|
```javascript
|
|
// In browser console
|
|
localStorage.getItem('cart_session_id')
|
|
// Should show: "session_1763765104510_zc866tt5d"
|
|
```
|
|
|
|
### Inspect Product Data
|
|
|
|
```javascript
|
|
// In browser console on product page
|
|
Alpine.$data(document.querySelector('[x-data]')).product
|
|
// Shows full product object with inventory
|
|
```
|
|
|
|
### Check API Responses
|
|
|
|
In browser Network tab, filter by "storefront" and check:
|
|
- GET `/api/v1/storefront/products` - Should return products array
|
|
- GET `/api/v1/storefront/cart/{session_id}` - Should return cart items
|
|
- POST `/api/v1/storefront/cart/{session_id}/items` - Should return success
|
|
|
|
## Getting Help
|
|
|
|
1. Check this guide first
|
|
2. Check browser console for errors
|
|
3. Check server logs for API errors
|
|
4. Verify database has inventory entries
|
|
5. Ensure all templates extend `storefront/base.html`
|
|
6. Check that session ID is initialized
|
|
|
|
If still stuck, provide:
|
|
- Browser console output
|
|
- Server log excerpt
|
|
- Network tab showing failed requests
|
|
- Steps to reproduce
|