# Restaurant with a daily-changing menu

A restaurant site where the menu changes every day. The HTML is built
once. After that, the owner (or you, on their behalf) updates a single
metadata field and the page reflects the new menu instantly. No HTML
edits, no rebuild, no deploy.

This is the killer pattern for content that changes on a schedule:

- Daily menus
- Weekly classes or events
- Today's special / opening hours that vary
- Seasonal product lists

It also demonstrates how multiple pages can share data via `cross_page_metadata.json`
(the main page shows tomorrow's menu by reading the menu page's
metadata).

## How it works

1. Create the project and a `menu` sub-page.
2. Set the menu page's metadata to today's dishes.
3. Build the menu page HTML so it fetches `cross_page_metadata.json`, finds itself
   by slug, and renders its own metadata.
4. Build the main page so it fetches `cross_page_metadata.json` and shows a preview
   of the menu page's metadata.
5. Each day, one PUT to `/projects/<slug>/pages/menu/metadata` updates
   the displayed menu everywhere.

## The script

```python
import requests
import time

API = "https://api.uat-beam.page"
TOKEN = "<your-token>"
H = {"Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json"}

slug = "carmen-tapas"

# 1. Create the project
requests.post(f"{API}/projects", headers=H, json={
    "slug": slug,
    "context": "A tapas bar in Barcelona called Carmen. Traditional Spanish food, daily menu del dia, live flamenco on Fridays.",
})
time.sleep(2)

# 2. Brand it — warm, traditional
requests.put(f"{API}/projects/{slug}/assets/tailwind-config.js", headers=H, json={
    "content": (
        'tailwind.config = { theme: { extend: { '
        'colors: { brand: "#7c2d12", accent: "#f59e0b" }, '
        'fontFamily: { serif: ["Playfair Display", "serif"] } '
        '} } }'
    ),
})

requests.put(f"{API}/projects/{slug}/assets/styles.css", headers=H, json={
    "content": (
        "@import url('https://fonts.googleapis.com/"
        "css2?family=Playfair+Display:wght@400;600;700&display=swap');"
    ),
})

# 3. Create the menu page
requests.post(f"{API}/projects/{slug}/pages", headers=H, json={
    "slug": "menu",
    "notes": "Daily menu del dia",
})
time.sleep(1)

# 4. Set the menu metadata. This is the source of truth for what's
# shown on the page. Update this daily and the page changes itself.
requests.put(f"{API}/projects/{slug}/pages/menu/metadata", headers=H, json={
    "date": "2026-04-08",
    "price": 14.50,
    "courses": {
        "primero": ["Gazpacho andaluz", "Ensalada mixta", "Croquetas de jamon"],
        "segundo": ["Paella valenciana", "Merluza a la plancha", "Pollo al ajillo"],
        "postre":  ["Crema catalana", "Fruta del tiempo", "Helado artesanal"],
    },
    "includes": "Pan, bebida y postre incluidos",
})

# 5. Upload the menu page HTML. It fetches cross_page_metadata.json, finds the "menu"
# page, and renders its metadata. The HTML never changes after this.
menu_html = """<!DOCTYPE html>
<html lang="es">
<head>
  <base href="/menu/">
  <meta charset="UTF-8">
  <title>Menú del Día — Carmen</title>
  <script src="https://cdn.tailwindcss.com"></script>
  <script src="/assets/tailwind-config.js"></script>
  <link rel="stylesheet" href="/assets/styles.css">
</head>
<body class="font-serif bg-stone-50 text-stone-900"
      x-data="{ p: null }"
      x-init="
        fetch('/cross_page_metadata.json').then(r => r.json())
          .then(d => p = d.pages.find(pg => pg.slug === 'menu'))
      ">

  <header class="bg-brand text-white py-8 px-6 text-center">
    <a href="/" class="text-3xl font-bold hover:text-amber-200">Carmen</a>
  </header>

  <main class="max-w-2xl mx-auto px-6 py-16">
    <template x-if="p && p.metadata && p.metadata.courses">
      <div>
        <div class="text-center mb-12">
          <h1 class="text-5xl font-bold text-brand mb-2">Menú del Día</h1>
          <p class="text-2xl text-amber-700" x-text="'€' + p.metadata.price.toFixed(2)"></p>
          <p class="text-stone-500 mt-1" x-text="p.metadata.includes"></p>
          <p class="text-stone-400 text-sm mt-2" x-text="p.metadata.date"></p>
        </div>

        <div class="space-y-8">
          <section class="bg-white rounded-lg shadow p-8">
            <h2 class="text-xl font-bold text-brand mb-4 border-b border-stone-200 pb-2">Primero</h2>
            <template x-for="dish in p.metadata.courses.primero">
              <p class="text-lg text-stone-700 py-1" x-text="dish"></p>
            </template>
          </section>

          <section class="bg-white rounded-lg shadow p-8">
            <h2 class="text-xl font-bold text-brand mb-4 border-b border-stone-200 pb-2">Segundo</h2>
            <template x-for="dish in p.metadata.courses.segundo">
              <p class="text-lg text-stone-700 py-1" x-text="dish"></p>
            </template>
          </section>

          <section class="bg-white rounded-lg shadow p-8">
            <h2 class="text-xl font-bold text-brand mb-4 border-b border-stone-200 pb-2">Postre</h2>
            <template x-for="dish in p.metadata.courses.postre">
              <p class="text-lg text-stone-700 py-1" x-text="dish"></p>
            </template>
          </section>
        </div>
      </div>
    </template>
  </main>

  <footer class="text-center py-8 text-stone-500 text-sm">
    <a href="/" class="hover:text-brand">&larr; Back to home</a>
  </footer>

  <script src="https://cdn.jsdelivr.net/npm/alpinejs@3/dist/cdn.min.js" defer></script>
</body>
</html>
"""

requests.put(
    f"{API}/projects/{slug}/pages/menu/files/index.html",
    headers=H,
    json={"content": menu_html},
)

# 6. Upload the main page HTML. It also reads from cross_page_metadata.json — shows
# the bar's intro plus a preview of today's menu.
main_html = """<!DOCTYPE html>
<html lang="es">
<head>
  <meta charset="UTF-8">
  <title>Carmen — Tapas Bar Barcelona</title>
  <script src="https://cdn.tailwindcss.com"></script>
  <script src="/assets/tailwind-config.js"></script>
  <link rel="stylesheet" href="/assets/styles.css">
</head>
<body class="font-serif bg-stone-50 text-stone-900"
      x-data="{ menu: null }"
      x-init="
        fetch('/cross_page_metadata.json').then(r => r.json())
          .then(d => menu = d.pages.find(p => p.slug === 'menu'))
      ">

  <header class="bg-brand text-white py-16 px-6 text-center">
    <h1 class="text-5xl font-bold mb-2">Carmen</h1>
    <p class="text-xl text-orange-200">Tapas Bar &middot; Poble Sec, Barcelona</p>
  </header>

  <main class="max-w-3xl mx-auto px-6 py-16">

    <section class="text-center mb-16">
      <h2 class="text-3xl font-bold text-brand mb-4">Bienvenidos</h2>
      <p class="text-lg text-stone-600 leading-relaxed">
        Cocina tradicional española en el corazón de Poble Sec.
        Tapas caseras, vinos de la tierra, y flamenco en vivo los viernes.
      </p>
    </section>

    <template x-if="menu && menu.metadata && menu.metadata.courses">
      <section class="bg-white rounded-lg shadow p-8">
        <h2 class="text-2xl font-bold text-brand mb-2">Hoy: Menú del Día</h2>
        <p class="text-amber-700 font-semibold text-lg mb-4"
           x-text="'€' + menu.metadata.price.toFixed(2) + ' — ' + menu.metadata.includes"></p>
        <div class="grid md:grid-cols-3 gap-6">
          <div>
            <h3 class="font-semibold text-brand mb-2">Primero</h3>
            <template x-for="dish in menu.metadata.courses.primero">
              <p class="text-stone-600" x-text="dish"></p>
            </template>
          </div>
          <div>
            <h3 class="font-semibold text-brand mb-2">Segundo</h3>
            <template x-for="dish in menu.metadata.courses.segundo">
              <p class="text-stone-600" x-text="dish"></p>
            </template>
          </div>
          <div>
            <h3 class="font-semibold text-brand mb-2">Postre</h3>
            <template x-for="dish in menu.metadata.courses.postre">
              <p class="text-stone-600" x-text="dish"></p>
            </template>
          </div>
        </div>
        <a href="/menu" class="inline-block mt-4 text-amber-700 hover:underline text-sm">Ver menú completo &rarr;</a>
      </section>
    </template>

  </main>

  <footer class="text-center py-8 text-stone-400 text-sm">
    <p>Carmen Tapas Bar &middot; Carrer de Blai 42, Poble Sec, Barcelona</p>
  </footer>

  <script src="https://cdn.jsdelivr.net/npm/alpinejs@3/dist/cdn.min.js" defer></script>
</body>
</html>
"""

requests.put(
    f"{API}/projects/{slug}/pages/_root/files/index.html",
    headers=H,
    json={"content": main_html},
)

print(f"Done. Live at https://{slug}.uat-beam.page")
```

## The killer move: updating tomorrow's menu

The HTML never changes. To put tomorrow's menu live, one call:

```python
import requests

API = "https://api.uat-beam.page"
TOKEN = "<your-token>"
H = {"Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json"}

requests.put(
    f"{API}/projects/carmen-tapas/pages/menu/metadata",
    headers=H,
    json={
        "date": "2026-04-09",
        "price": 14.50,
        "courses": {
            "primero": ["Salmorejo", "Patatas bravas", "Pimientos de padron"],
            "segundo": ["Arroz negro", "Lomo de cerdo", "Dorada al horno"],
            "postre":  ["Tarta de queso", "Naranja con canela", "Arroz con leche"],
        },
        "includes": "Pan, bebida y postre incluidos",
    },
)

print("Menu updated. Both the main page and the menu page reflect it instantly.")
```

That's the whole point of metadata. The owner gets a website that
updates as easily as editing a JSON object.

## Variations

- **Add a contact page** with the email action for reservations
  (requires Google login). See `contact-form.md`.
- **Add an "events" sub-page** for live music nights, again driven by
  metadata so adding a gig is one PUT.
- **Show this week's menu instead of just today** by storing an array
  of daily menus and filtering by date in the Alpine state.
