465 lines
13 KiB
Markdown
465 lines
13 KiB
Markdown
# CLAUDE.md - Public Web Frontend
|
|
|
|
## Service Overview
|
|
**Public Web Frontend** for Code of Conquest - Browser-based UI using Flask + Jinja2 + HTMX.
|
|
|
|
**Tech Stack:** Flask + Jinja2 + HTMX + Vanilla CSS
|
|
**Port:** 5001 (development), 8080 (production)
|
|
**Location:** `/public_web`
|
|
|
|
---
|
|
|
|
## Architecture Role
|
|
|
|
This web frontend is a **thin UI layer** that makes HTTP requests to the API backend:
|
|
- ✅ Render HTML templates (Jinja2)
|
|
- ✅ Form validation (UI only)
|
|
- ✅ Make HTTP requests to API backend
|
|
- ✅ Display API responses
|
|
- ✅ Handle user input
|
|
|
|
**What this service does NOT do:**
|
|
- ❌ No business logic (all in `/api`)
|
|
- ❌ No direct database access (use API)
|
|
- ❌ No direct Appwrite calls (use API)
|
|
- ❌ No game mechanics calculations (use API)
|
|
|
|
**Communication:**
|
|
```
|
|
User Browser → Public Web (views) → HTTP Request → API Backend
|
|
↑ ↓
|
|
←─────────── HTTP Response ─────────┘
|
|
```
|
|
|
|
---
|
|
|
|
## Documentation Index
|
|
|
|
**Web Frontend Documentation:**
|
|
- **[README.md](README.md)** - Setup and usage guide
|
|
- **[docs/TEMPLATES.md](docs/TEMPLATES.md)** - Template structure and conventions
|
|
- **[docs/HTMX_PATTERNS.md](docs/HTMX_PATTERNS.md)** - HTMX integration patterns
|
|
- **[docs/TESTING.md](docs/TESTING.md)** - Manual testing guide
|
|
- **[docs/MULTIPLAYER.md](docs/MULTIPLAYER.md)** - Multiplayer UI implementation
|
|
|
|
**Project-Wide Documentation:**
|
|
- **[../docs/ARCHITECTURE.md](../docs/ARCHITECTURE.md)** - System architecture overview
|
|
- **[../api/docs/API_REFERENCE.md](../api/docs/API_REFERENCE.md)** - API endpoints to call
|
|
- **[../docs/DEPLOYMENT.md](../docs/DEPLOYMENT.md)** - Deployment guide
|
|
- **[../docs/WEB_VS_CLIENT_SYSTEMS.md](../docs/WEB_VS_CLIENT_SYSTEMS.md)** - Feature distribution between frontends
|
|
|
|
**Documentation Guidelines:**
|
|
- ✅ Place all public_web service documentation in `/public_web/docs/`
|
|
- ✅ Each microservice maintains its own documentation
|
|
- ❌ Do NOT add public_web-specific docs to `/docs/` (main repo docs are for cross-service architecture only)
|
|
|
|
---
|
|
|
|
## Development Guidelines
|
|
|
|
### Project Structure
|
|
|
|
```
|
|
public_web/
|
|
├── app/ # Application code
|
|
│ ├── views/ # View blueprints (Flask routes)
|
|
│ └── utils/ # Utilities (logging, auth helpers, API client)
|
|
├── templates/ # Jinja2 HTML templates
|
|
│ ├── auth/ # Authentication pages
|
|
│ ├── character/ # Character pages
|
|
│ ├── errors/ # Error pages (404, 500)
|
|
│ └── base.html # Base template
|
|
├── static/ # CSS, JS, images
|
|
│ └── css/ # Stylesheets
|
|
├── docs/ # Service-specific documentation
|
|
├── config/ # Configuration files
|
|
└── README.md # Setup and usage guide
|
|
```
|
|
|
|
### Coding Standards
|
|
|
|
**Style & Structure**
|
|
- Prefer longer, explicit code over compact one-liners
|
|
- Always include docstrings for functions/classes + inline comments
|
|
- Strong typing for view functions (type hints)
|
|
- Keep views simple (delegate to API)
|
|
|
|
**Templates & UI**
|
|
- Don't mix large HTML/CSS blocks in Python code
|
|
- Use Jinja2 templates for all HTML rendering
|
|
- Clean CSS, minimal inline styles
|
|
- Readable template logic (avoid complex Python in templates)
|
|
- Use HTMX for dynamic interactions
|
|
|
|
**Logging**
|
|
- Use structlog (pip package)
|
|
- Setup logging at app start: `logger = structlog.get_logger(__file__)`
|
|
|
|
**Preferred Pip Packages**
|
|
- Web Server: Flask
|
|
- Templates: Jinja2
|
|
- HTTP Client: Requests
|
|
- Logging: Structlog
|
|
|
|
### View Development Standards
|
|
|
|
**View Functions (Flask Routes):**
|
|
```python
|
|
from flask import Blueprint, render_template, request, redirect, url_for
|
|
import requests
|
|
from app.config import load_config
|
|
|
|
character_bp = Blueprint('character', __name__, url_prefix='/characters')
|
|
|
|
@character_bp.route('/')
|
|
def list_characters():
|
|
"""
|
|
Display list of user's characters.
|
|
|
|
Makes HTTP request to API backend to fetch character data.
|
|
"""
|
|
config = load_config()
|
|
|
|
# Make API request
|
|
response = requests.get(
|
|
f"{config.api.base_url}/api/v1/characters",
|
|
timeout=config.api.timeout
|
|
)
|
|
|
|
if response.status_code == 200:
|
|
data = response.json()
|
|
characters = data.get('result', [])
|
|
return render_template('character/list.html', characters=characters)
|
|
else:
|
|
# Handle error
|
|
return render_template('error.html', message="Failed to load characters"), 500
|
|
```
|
|
|
|
**DO:**
|
|
- ✅ Make HTTP requests to API backend
|
|
- ✅ Pass data to templates
|
|
- ✅ Handle errors gracefully
|
|
- ✅ Validate form inputs (UI validation only)
|
|
- ✅ Redirect to appropriate pages
|
|
- ✅ Use flash messages for user feedback
|
|
|
|
**DON'T:**
|
|
- ❌ No business logic in views
|
|
- ❌ No direct database access
|
|
- ❌ No game mechanics calculations
|
|
- ❌ No complex data transformations (keep it simple)
|
|
|
|
### Template Best Practices
|
|
|
|
**Base Template (`templates/base.html`):**
|
|
```html
|
|
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>{% block title %}Code of Conquest{% endblock %}</title>
|
|
<link rel="stylesheet" href="{{ url_for('static', filename='css/main.css') }}">
|
|
<script src="https://unpkg.com/htmx.org@1.9.10"></script>
|
|
</head>
|
|
<body>
|
|
<header>
|
|
{% include 'components/navbar.html' %}
|
|
</header>
|
|
|
|
<main>
|
|
{% block content %}{% endblock %}
|
|
</main>
|
|
|
|
<footer>
|
|
{% include 'components/footer.html' %}
|
|
</footer>
|
|
</body>
|
|
</html>
|
|
```
|
|
|
|
**Child Template:**
|
|
```html
|
|
{% extends "base.html" %}
|
|
|
|
{% block title %}Characters - Code of Conquest{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="container">
|
|
<h1>Your Characters</h1>
|
|
|
|
{% for character in characters %}
|
|
<div class="character-card">
|
|
<h2>{{ character.name }}</h2>
|
|
<p>{{ character.class_name }} - Level {{ character.level }}</p>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
{% endblock %}
|
|
```
|
|
|
|
**HTMX Usage:**
|
|
```html
|
|
<!-- Form with HTMX -->
|
|
<form hx-post="/api/v1/characters"
|
|
hx-target="#character-list"
|
|
hx-swap="beforeend">
|
|
<input type="text" name="name" placeholder="Character name">
|
|
<button type="submit">Create</button>
|
|
</form>
|
|
|
|
<!-- Result container -->
|
|
<div id="character-list"></div>
|
|
```
|
|
|
|
### CSS Standards
|
|
|
|
**Organization:**
|
|
- Use BEM naming convention (Block Element Modifier)
|
|
- Group related styles together
|
|
- Use CSS variables for colors/spacing
|
|
- Mobile-first responsive design
|
|
|
|
**Example:**
|
|
```css
|
|
:root {
|
|
--color-primary: #8b5cf6;
|
|
--color-bg: #1a1a1a;
|
|
--color-text: #e5e7eb;
|
|
--spacing-unit: 1rem;
|
|
}
|
|
|
|
.character-card {
|
|
background: var(--color-bg);
|
|
color: var(--color-text);
|
|
padding: calc(var(--spacing-unit) * 2);
|
|
border-radius: 8px;
|
|
}
|
|
|
|
.character-card__title {
|
|
font-size: 1.5rem;
|
|
margin-bottom: var(--spacing-unit);
|
|
}
|
|
|
|
.character-card--highlighted {
|
|
border: 2px solid var(--color-primary);
|
|
}
|
|
```
|
|
|
|
### Configuration
|
|
- Environment-specific configs in `/config/*.yaml`
|
|
- `development.yaml` - Local dev settings (API URL: http://localhost:5000)
|
|
- `production.yaml` - Production settings (API URL from env var)
|
|
- `.env` for secrets (never committed)
|
|
- Typed config loaders using dataclasses
|
|
|
|
**Configuration Structure:**
|
|
```yaml
|
|
# config/development.yaml
|
|
api:
|
|
base_url: "http://localhost:5000"
|
|
timeout: 30
|
|
verify_ssl: false
|
|
|
|
server:
|
|
host: "0.0.0.0"
|
|
port: 5001
|
|
workers: 1
|
|
|
|
session:
|
|
lifetime_hours: 24
|
|
cookie_secure: false
|
|
cookie_httponly: true
|
|
```
|
|
|
|
### Error Handling
|
|
|
|
**View Error Handling:**
|
|
```python
|
|
@character_bp.route('/<character_id>')
|
|
def character_detail(character_id):
|
|
"""Display character details."""
|
|
config = load_config()
|
|
|
|
try:
|
|
response = requests.get(
|
|
f"{config.api.base_url}/api/v1/characters/{character_id}",
|
|
timeout=config.api.timeout
|
|
)
|
|
response.raise_for_status()
|
|
|
|
data = response.json()
|
|
character = data.get('result')
|
|
return render_template('character/detail.html', character=character)
|
|
|
|
except requests.exceptions.Timeout:
|
|
logger.error("API timeout", character_id=character_id)
|
|
return render_template('error.html', message="Request timed out"), 504
|
|
|
|
except requests.exceptions.HTTPError as e:
|
|
if e.response.status_code == 404:
|
|
return render_template('error.html', message="Character not found"), 404
|
|
logger.error("API error", status=e.response.status_code)
|
|
return render_template('error.html', message="An error occurred"), 500
|
|
|
|
except Exception as e:
|
|
logger.exception("Unexpected error", character_id=character_id)
|
|
return render_template('error.html', message="An unexpected error occurred"), 500
|
|
```
|
|
|
|
### Dependency Management
|
|
- Use `requirements.txt` in `/public_web` directory
|
|
- Minimal dependencies (Flask, Jinja2, requests, structlog)
|
|
- Use virtual environment: `python3 -m venv venv`
|
|
- Activate venv before running: `source venv/bin/activate`
|
|
|
|
### Testing Standards
|
|
|
|
**Manual Testing:**
|
|
- Use the checklist in README.md
|
|
- Test all user flows:
|
|
- [ ] Login flow
|
|
- [ ] Registration flow
|
|
- [ ] Character creation wizard (all 4 steps)
|
|
- [ ] Character list and detail views
|
|
- [ ] Logout
|
|
- [ ] Error handling
|
|
|
|
**Browser Testing:**
|
|
- Test in Chrome, Firefox, Safari
|
|
- Test mobile responsive design
|
|
- Test HTMX interactions
|
|
|
|
---
|
|
|
|
## Architecture Status
|
|
|
|
✅ **COMPLETE:** All views use the `APIClient` class for HTTP requests to the API backend.
|
|
|
|
**What's Implemented:**
|
|
- All views use `get_api_client()` from `app/utils/api_client.py`
|
|
- Typed error handling with `APIError`, `APINotFoundError`, `APITimeoutError`, `APIAuthenticationError`
|
|
- Session cookie forwarding for authentication
|
|
- Proper JSON serialization/deserialization
|
|
|
|
**Minor Improvements (Optional):**
|
|
- Auth decorator could re-validate expired API sessions
|
|
- Origin/class validation could use single-item lookups instead of fetching full lists
|
|
|
|
---
|
|
|
|
## Workflow for Web Frontend Development
|
|
|
|
When implementing new pages:
|
|
|
|
1. **Design the page** - Sketch layout, user flow
|
|
2. **Create template** - Add Jinja2 template in `/templates`
|
|
3. **Create view** - Add Flask route in `/app/views`
|
|
4. **Make API calls** - Use requests library to call API backend
|
|
5. **Handle errors** - Graceful error handling with user feedback
|
|
6. **Add styles** - Update CSS in `/static/css`
|
|
7. **Test manually** - Check all user flows
|
|
|
|
**Example Flow:**
|
|
```bash
|
|
# 1. Create template
|
|
# templates/quest/list.html
|
|
|
|
# 2. Create view
|
|
# app/views/quest_views.py
|
|
@quest_bp.route('/')
|
|
def list_quests():
|
|
# Make API request
|
|
response = requests.get(f"{api_url}/api/v1/quests")
|
|
quests = response.json()['result']
|
|
return render_template('quest/list.html', quests=quests)
|
|
|
|
# 3. Register blueprint
|
|
# app/__init__.py
|
|
from .views.quest_views import quest_bp
|
|
app.register_blueprint(quest_bp)
|
|
|
|
# 4. Add styles
|
|
# static/css/quest.css
|
|
|
|
# 5. Test in browser
|
|
# http://localhost:5001/quests
|
|
```
|
|
|
|
---
|
|
|
|
## Running the Web Frontend
|
|
|
|
### Development
|
|
|
|
**Prerequisites:**
|
|
- Python 3.11+
|
|
- API backend running at http://localhost:5000
|
|
|
|
**Setup:**
|
|
```bash
|
|
cd public_web
|
|
python3 -m venv venv
|
|
source venv/bin/activate
|
|
pip install -r requirements.txt
|
|
cp .env.example .env
|
|
# Edit .env with your settings
|
|
```
|
|
|
|
**Run Development Server:**
|
|
```bash
|
|
source venv/bin/activate
|
|
export FLASK_ENV=development
|
|
python wsgi.py # → http://localhost:5001
|
|
```
|
|
|
|
### Production
|
|
|
|
**Run with Gunicorn:**
|
|
```bash
|
|
gunicorn --bind 0.0.0.0:8080 --workers 4 wsgi:app
|
|
```
|
|
|
|
**Environment Variables:**
|
|
```
|
|
FLASK_ENV=production
|
|
SECRET_KEY=...
|
|
API_BASE_URL=https://api.codeofconquest.com
|
|
```
|
|
|
|
---
|
|
|
|
## Git Standards
|
|
|
|
**Commit Messages:**
|
|
- Use conventional commit format: `feat:`, `fix:`, `docs:`, `style:`, etc.
|
|
- Examples:
|
|
- `feat(web): add quest list page`
|
|
- `fix(views): handle API timeout errors`
|
|
- `style(css): improve character card layout`
|
|
|
|
**Branch Strategy:**
|
|
- Branch off `dev` for features
|
|
- Merge back to `dev` for testing
|
|
- Promote to `master` for production
|
|
|
|
---
|
|
|
|
## Notes for Claude Code
|
|
|
|
When working on the web frontend:
|
|
|
|
1. **Thin client only** - No business logic, just UI
|
|
2. **Always call API** - Use HTTP requests for all data operations
|
|
3. **Handle errors gracefully** - Show user-friendly error messages
|
|
4. **Keep templates clean** - Avoid complex logic in Jinja2
|
|
5. **Mobile responsive** - Design for all screen sizes
|
|
6. **HTMX for interactivity** - Use HTMX instead of heavy JavaScript
|
|
7. **Document refactoring needs** - Note any technical debt you encounter
|
|
|
|
**Remember:**
|
|
- This is a thin client - all logic lives in the API backend
|
|
- The API serves multiple frontends (this web UI and Godot client)
|
|
- Security validation happens in the API, but do basic UI validation for UX
|
|
- Keep it simple - complicated logic belongs in the API
|