ChatiumFor developersPlaygroundPricing
Sign in

General Layout for Pages

Now let's create a common template for our pages, with a shared header, featuring our website's logo and a top menu.

To do this, we will declare a common component called Layout.

export function Layout(props: {title: string}, ...children: any) {
  return <html>
    <body>
      <div class="mb-5 border-bottom pb-2">
        Top Menu
      </div>
      {children}
    </body>
  </html>
}

This function has 2 arguments:

  • props - properties of the template; all properties of the <Layout> tag will come in this object. For example:
      <Layout title="Title">
    
  • children - this variable will receive everything you place inside the tag as an array:
      <Layout>
        <h1>New Page</h1>
        <div>Some text that will also be included in children</div>
      </Layout>
    

Now, we can use it as a wrapper for our pages instead of the <html> tag.

const mainScreen = app.html('/', async(ctx, req) => {

  let items = await catalogTable.findAll(ctx)
  
  return <Layout title="Catalog of Paintings">
    <h1 class={"fs-1 fs-header fs-bold"}>Catalog of Paintings</h1>
    {/* content of our catalog */}
  </Layout>
})

Let's wrap all our pages with this Layout (replace <html> and common tags like <head> with <Layout>) and slightly modify the Layout template code by adding elements for the top menu:

Final Code

import {jsx} from '@app/html-jsx'
import {Heap} from '@app/heap'

const catalogTable = Heap.Table('pictures', {
  title: Heap.String(), // title. string
  image: Heap.ImageFile(),  // image. required
  material: Heap.Optional(Heap.String()),  // material. optional. string.
  dimensions: Heap.Optional(Heap.String()),  // dimensions. optional. string.
  price: Heap.NonRequired(Heap.Number(), 0), // price. number. default 0
})

let currency = new Intl.NumberFormat('ru-RU', {
  style: 'currency',
  currency: 'RUB',
  maximumFractionDigits: 0,
});

app.html('/', async(ctx, req) => {
  const items = await catalogTable.findAll(ctx)

  return <Layout title="Catalog of Paintings">
    <h1 class={"fs-1 fs-header fs-bold"}>Catalog of Paintings</h1>
    
    {items.map(item =>
      <a class="card" href={cardScreen({id: item.id}).url()}>
        <img src={item.image?.getThumbnailUrl(500, 300)}/>
        <div class='card-body p-2'>
          <h3>{item.title}</h3>
          <div>{item.material}</div>
          <div>{item.dimensions}</div>
        </div>
      </a>
    )}
  </Layout>
})

const cardScreen = app.html('/card/:id', async(ctx, req) => {
  const item = await catalogTable.getById(ctx, req.params.id!)
  
  return <Layout title={item.title}>
    <h3 class="mb-3"><a href={mainScreen.url()}>Catalog of Paintings</a></h3>
    <div class={"row g-10"}>
      <div class={"col-md-8 col-12"}>
        <img class="mw-100" src={item.image?.getThumbnailUrl(900)}/>
      </div>
      <div class={"col-md-4 col-12"}>
        <h1 class={"fs-1 fs-header fw-bold"}>{item.title}</h1>
        <div>{item.material}</div>
        <div>{item.dimensions}</div>
        {item.price && <div class={"fs-2"}>{currency.format(item.price)}</div>}
      </div>
    </div>
  </Layout>
})

function Layout(props: {title: string}, ...children: any) {
  
  return <html>
    <head>
      <title>{props.title}</title>
      <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.3/css/bootstrap.min.css" integrity="sha512-jnSuA4Ss2PkkikSOLtYs8BlYIeeIK1h99ty4YfvRPAlzr377vr3CXDb7sb7eEEBYjDtcYj+AjBH3FLv5uSJuXg==" crossorigin="anonymous" referrerpolicy="no-referrer" />
      <meta charset="utf-8"/>
      <meta name="viewport" content="width=device-width, initial-scale=1"></meta>
    </head>

    <body>
      <div class={'container py-2 py-md-5'}>
        <div class={"col-md-8 col-12 mx-md-auto"}>
          <div class={"mb-5 border-bottom pb-2"}>
            <div class="d-flex flex-row justify-content-between">
              <div class="d-flex flex-row">
                <img class="me-3" height='30' width='30' src='https://chatium.com/s/static/img/logos/logo_round.png'/>
                <div class={"p-1 me-3 d-none d-md-block"}>
                  <a href={mainScreen.url()}>
                    Home
                  </a>
                </div>
              </div>
            </div>
          </div>
          {children}
        </div>
      </div>
    </body>
  </html>
}

Next Step: Adding an Order Form