# 11 | SPA routing and direct refresh

## Goal

Support internal clicks, Back/Forward and direct nested URLs.

## What you are building

There are two routers. Teloce's ui_routes maps /projects/:id to the detail component and passes id as a prop. data-teloce-link lets an ordinary anchor navigate without replacing the document; data-teloce-router-view provides the mount point. Flaxon's explicit /projects/<int:project_id> shell route handles direct visits and refreshes. The browser then fetches protected JSON. Keep Admin links as ordinary full-page navigation. Do not use a catch-all shell route that turns an unknown API path into HTML.

## Build this chapter

Stop the server. Work in project_manager. Follow each CREATE, whole-file replacement, or marked-block edit below in order.

## Step 1: EDIT - replace the marked blocks - app.py

The application factory mounts only the features already introduced. Follow the action label: a marked-block edit keeps the surrounding factory intact.

### Edit 1: find this exact block

Open `app.py`. Locate this block once. The unchanged surrounding lines identify its position; do not use line numbers from another revision.

```python
    @app.get("/")
    @app.get("/login")
    @app.get("/projects")
    async def spa(request: Request, project_id=None, slug=None):
        return await request.compile("app.html", {})
```
### Replace that block with

```python
    @app.get("/")
    @app.get("/login")
    @app.get("/projects")
    @app.get("/projects/<int:project_id>")
    async def spa(request: Request, project_id=None, slug=None):
        return await request.compile("app.html", {})
```

Leave the rest of the file unchanged.

**Say:** "The application factory mounts only the features already introduced. Follow the action label: a marked-block edit keeps the surrounding factory intact."

**Type:** Follow the action above in `app.py`; explain each new function or binding as you add it.

**Viewers should see:** The saved file matches this step. After all steps, run the chapter checks below and show the expected result.

## Run and check

```bash
python management.py runserver
# Refresh a nested /projects/ID URL, then use Back and Forward.
```

## Expected result

Click into a project, use Back and Forward and refresh /projects/ID. The same screen loads. /api/does-not-exist remains a JSON 404.

## Common errors

A successful click with a failed refresh means the server shell route is missing. A document reload on an internal link means its data-teloce-link marker is missing. Use /projects/:id in browser routes and <int:project_id> in Python routes.

## Short exercise

Put a temporary window marker in the browser console and prove an internal link preserves it.

## What to say and show

Say: "Support internal clicks, Back/Forward and direct nested URLs. The server remains responsible for persistence and authorization; the browser presents the result."

Show the expected result above, then point to the file responsible for it. Run the chapter check before the next lesson.

## Save your checkpoint

```bash
git add .
git commit -m "Complete chapter 11: SPA routing and direct refresh"
```
