> For the complete documentation index, see [llms.txt](https://docs.avonnicomponents.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.avonnicomponents.com/projects/use-cases/fall-course-catalog.md).

# Fall course catalog

## Overview

An outdoor school lists its fall courses on the portal: six photos, dates, price and seats left under each. The whole catalog is one component and six records. The component is **AX - Gallery** in its grid variant, on an Experience Site page. This tutorial rebuilds the fall catalog of Ridgeline Outdoor Academy, first with the six courses typed into the component, then from a Course object.

## What you build

![Avonni Gallery on a customer portal page: six outdoor courses in a three by two grid of photos, each with a title, dates, price and seats left](https://3857391697-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FdHOej9Pd5IxJNGEJMZKW%2Fuploads%2Fxmz0xcQ4ZWesKcMxjWij%2Fuc-12-course-catalog-portal.png?alt=media)

* Ridgeline Outdoor Academy opens enrollment on its customer site. The page reads **6 courses · enrolling now**, then **Fall season**, then one line: **Small groups, certified guides, gear included.**
* Six courses in a three by two grid, one photo each, the title and one line of facts underneath: days, start date, price, seats left.
* Every tile is the same size, and each photo fills its tile edge to edge.
* The page shows nothing else, which is the point.

## Before you start

{% hint style="info" %}
**Adapt this to your own org.** The figure is built with the six courses typed into the component and their photos uploaded from the panel, so it needs no object and no records. Course, the object behind the Query version in step 6, is one this tutorial creates: point the query at whatever already holds what you sell, and map your own fields. Two things to plan for. The mapped photo field must hold a Content Document ID or a public image URL, and the line under each title is a single text field here, so make it a formula if you want the price and the seats left to follow the record on their own.
{% endhint %}

* The [Avonni Components for Experience Sites](https://appexchange.salesforce.com/appxListingDetail?listingId=2e584bb3-b5e0-415d-9347-d6567158d840) package is installed, and the users of your site hold a package license and the **Avonni Experience Cloud Components User** permission set. See [License Management](https://docs.avonnicomponents.com/experience-cloud/getting-started/license-management).
* An Experience Site (LWR or Aura) with a page for the catalog. The figure shows the component on a bare page; on your site, the theme and the site header surround it.
* Six photos you own, one per course, all landscape and in the same proportions (the figure uses 3:2). This page does not provide them. On the Manual path you upload them from the component; on the Query path each Course record points at its file.
* **Manual**, which is what the figure shows, needs no object. **Query** turns the catalog into records the school edits from Salesforce, and needs a custom object:

| Object               | Field           | Type                                           | Role                                                                                                                                                                                                      |
| -------------------- | --------------- | ---------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Course (`Course__c`) | `Name`          | Text                                           | The course title, `Sea kayaking`                                                                                                                                                                          |
|                      | `Details__c`    | Text (80)                                      | The line under the title, `3 days · Sep 26 · $480 · 4 seats left`. Store the start date, the price and the seats left in their own fields and make this a formula when seats left must update on its own. |
|                      | `Photo__c`      | Text (255)                                     | The photo: the Content Document ID of a file in Salesforce Files (an ID that starts with `069`), or a public image URL                                                                                    |
|                      | `Season__c`     | Picklist: `Fall`, `Winter`, `Spring`, `Summer` | The query filter                                                                                                                                                                                          |
|                      | `Sort_Order__c` | Number, 0 decimal places                       | The position in the grid, 1 to 6                                                                                                                                                                          |

**Sample data.** Six courses. **Value** is the short unique key the Manual editor asks for; on the Query path the record takes its place.

| Course                   | Details                               | Value   |
| ------------------------ | ------------------------------------- | ------- |
| Rock climbing basics     | 2 days · Sep 19 · $320 · 5 seats left | `climb` |
| Sea kayaking             | 3 days · Sep 26 · $480 · 4 seats left | `kayak` |
| Wilderness first aid     | 2 days · Oct 3 · $290 · 8 seats left  | `wfa`   |
| Map and compass          | 1 day · Oct 10 · $140 · 12 seats left | `nav`   |
| Trail riding             | 1 day · Oct 17 · $160 · 6 seats left  | `mtb`   |
| Backcountry skiing intro | 3 days · Nov 14 · $540 · 2 seats left | `ski`   |

**Permissions.** Site users need the package license and the permission set named above. On the Query path, their profile also needs Read on Course and on its fields, and the photo files must be readable by them: shared with the site, or carrying a public link. The Avonni Query respects Salesforce sharing: a user only sees the courses Salesforce lets them see.

## Build it

{% stepper %}
{% step %}

### Add the Gallery to the catalog page

1. Open the site in **Experience Builder** and go to the catalog page.
2. In the **Components** panel, under **Custom Components**, drag **AX - Gallery** into a full-width section of the page.
3. Select the component to open its **Properties Panel**.
   {% endstep %}

{% step %}

### Choose the grid

1. Set **Variant** to `grid`.
2. Set **Number of Columns** to `3`.
3. Set **Media Position** to `top-center`.

*Why:* a catalog is browsed, not watched: the carousel shows one panel at a time, the grid shows all six. Top-center puts the photo above the text in every tile. The **Carousel** settings apply to the carousel variant only, so leave them alone.
{% endstep %}

{% step %}

### Write the header

1. Set **Caption** to `6 courses · enrolling now`.
2. Set **Title** to `Fall season` and **Title Text Style** to **Heading 2**.
3. Set **Content** to `Small groups, certified guides, gear included.`

*Why:* the header is part of the component, so the page needs no separate text component. The caption renders above the title: that is the component's layout, which is why the count goes in **Caption** and the season in **Title**.
{% endstep %}

{% step %}

### Enter the six courses

1. Open **Items**. Under **Data Source**, select **Manual**.
2. Add one item per course of the sample data. For each item, fill in **Title**, **Value** and **Description**.
3. Under **Media Source**, click **Select Image/Video**, upload the course photo and check **Public link**.
4. Leave **Media Attributes** empty: they apply to videos.

*Why:* every upload creates a new file in Salesforce Files, and the public link is what lets a site user load it. Replacing a photo later means uploading it again, which is one reason the Query path exists.
{% endstep %}

{% step %}

### Even out the photos (optional)

1. Open **Style** in the **Properties Panel**.
2. Under **Gallery/Grid/Carousel Image**, raise **Height** if the photos look short in their tiles.
3. Under **Media**, leave **Object Fit** on its default, which crops each photo to fill its tile.

*Why:* six photos taken by six people never share a frame. A fixed height and a cover fit make the grid read as one block.
{% endstep %}

{% step %}

### Switch to records

1. Open **Items** again and select **Query**. Choose the **Course** object and add the fields you will map: `Name`, `Details__c`, `Photo__c`.
2. Add a filter: **Season** equals `Fall`. Sort by **Sort Order** ascending.
3. In **Data Mappings**, map **Title** to Name, **Description** to Details and **Media Source** to Photo. Leave **Value** on its default.

*Why:* the catalog becomes six records. A new course is a new record, a sold-out course is a field change, and next season is a different filter value on the same page.
{% endstep %}

{% step %}

### Preview, then publish

1. Click **Preview** and check the six tiles. Photos stored in Salesforce Files may not render in the Experience Builder preview, because the builder does not authenticate as a site user.
2. **Publish** the site, then open the page logged in as a site user: the six photos load.
   {% endstep %}
   {% endstepper %}

## The settings that matter

| Setting                              | Value                                                                                        | Why                                                               |
| ------------------------------------ | -------------------------------------------------------------------------------------------- | ----------------------------------------------------------------- |
| **Variant** (`variant`)              | `grid`                                                                                       | A catalog, not a slideshow: all six courses in view.              |
| **Number of Columns** (`columns`)    | `3`                                                                                          | Three across, two rows for six courses.                           |
| **Media Position** (`mediaPosition`) | `top-center`                                                                                 | Photo first, text under it, in every tile.                        |
| **Caption**, **Title**, **Content**  | `6 courses · enrolling now`, `Fall season`, `Small groups, certified guides, gear included.` | The header is part of the component. The page needs nothing else. |

## Interactions

The figure has none. To make each course clickable, set **Link To**: on each item on the Manual path, once in **Data Mappings** on the Query path.

| Trigger                    | Action                                                                                                                | What to set                                                                                                                             |
| -------------------------- | --------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| A course tile, **Link To** | [Navigate](https://docs.avonnicomponents.com/experience-cloud/properties-panel/interactions/navigate)                 | A **Named Page** of the site for the course detail, or a **Web Page** for an enrollment form.                                           |
| A course tile, **Link To** | [Open Flow Dialog](https://docs.avonnicomponents.com/experience-cloud/properties-panel/interactions/open-flow-dialog) | The **Flow API Name** of an enrollment screen flow. Add the flow to a page of the site and publish first, or the dialog cannot find it. |

## Try it

1. Open the published page as a site user: six tiles, three across, every photo loaded.
2. Set **Number of Columns** to `2`: the same six courses in three rows of two. Set it back to `3`.
3. On the Query path, change the Details of Sea kayaking to `3 days · Sep 26 · $480 · 3 seats left` on the record and reload the page: the tile follows. Set the Season of one course to `Winter`: five tiles.

## Take it further

The Gallery exists on Experience Sites only. There is no Dynamic Components, Flow Screen Components or LWC Components page to point to: Carousel and Image List are different components. Three variations worth trying:

* Switch **Variant** to `carousel` and open the **Carousel** settings (**Is Infinite**, **Scroll Duration**) for a rotating banner on the home page.
* Switch **Variant** to `tabs` to show one course at a time, each behind its own tab.
* Add **Link To** on each course, so a tile opens the enrollment page.

## Troubleshooting

| Problem                                                                                | Cause                                                                                                                       | Fix                                                                                                                                                    |
| -------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Photos do not show in the Experience Builder preview but display on the published site | The builder does not authenticate as a site user, so it cannot fetch files from Salesforce Files.                           | Expected. Check the page on the published site, logged in as a site user.                                                                              |
| A tile reads "No preview available" on the Manual path                                 | The photo was uploaded without **Public link**, so site users cannot load it.                                               | Upload the photo again and check **Public link**, or switch to the Query path, where sharing rules apply.                                              |
| The gallery is empty for site users but works in the Experience Builder preview        | Their profile lacks access to the Course object or to the photo files.                                                      | Check object permissions, field-level security, and the sharing of the files or the library that holds them.                                           |
| The titles show but every photo is broken                                              | **Media Source** is mapped to a field that holds neither a Content Document ID (starting with `069`) nor a valid image URL. | Check what the mapped field contains, and map **Media Source** to a field that holds the ID or a public URL.                                           |
| The caption sits above the title                                                       | That is the component's layout. There is no setting to swap them.                                                           | Put the count in **Caption** and the season in **Title**, as this page does.                                                                           |
| A photo loses its subject at the top or the bottom of its tile                         | The gallery crops every photo to fill its tile, so a portrait or square photo loses its edges.                              | Use landscape photos in the same proportions, and adjust **Height** under **Gallery/Grid/Carousel Image** in **Style** if you need more of each photo. |


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.avonnicomponents.com/projects/use-cases/fall-course-catalog.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
