Skip to content

Latest commit

 

History

History
699 lines (522 loc) · 23.2 KB

DeepDive.md

File metadata and controls

699 lines (522 loc) · 23.2 KB

Table of Contents

Fetching content

Content Delivery API

const resolveContent = (requests: CmsRequest[]): Promise<CmsContent[]> => {
        return fetch(
            `https://${host}/content/fetch`,
                {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify({
                        "parameters": {
                            "depth": parameters.depth,
                            "format": parameters.format,
                            "locale": context.locale
                        },
                        "requests": requests
                    })
                }
            ).then(x => x.json())
                .then(x => x.responses || []) 
                .then(x => x.map((y: any) => y.content || null));
    };

top

Filter API

Sample Code

const resolveFilter = (request: GetByFilterRequest): Promise<CmsFilterResponse> => {
        return fetch(
            `https://${host}/content/filter`,
                {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify({
                        ...request,
                        "parameters": {
                            "depth": parameters.depth,
                            "format": parameters.format
                        }
                    })
                }
            ).then(x => x.json());
    }

Using Filter API

Filter API Example

let filterRequest: GetByFilterRequest =
    {
      filterBy: [
        {
          path: "/_meta/schema",
          value: "https://demostore.amplience.com/content/store"
        },
        {
          path: "/filterActive",
          value: true
        }
      ],
      sortBy: {
        key: "default",
        order: sortOrder
      }
    };

    if (parkingChecked) {
      filterRequest.filterBy.push({ path: "/keyElements/parking", value: true });
    }

    if (clickCollectChecked) {
      filterRequest.filterBy.push({ path: "/keyElements/clickAndCollect", value: true });
    }

top

Amplience Search

Instant Search Example

Search Index

Setting up Instant Search

        const search = instantsearch({
            indexName: stagingApi ? `${cms.hub}.blog-staging` : `${cms.hub}.blog-production`,
            searchClient: algoliasearch(
                appId,
                apiKey 
            ),
            hitsPerPage: 5,
        });

Filtering

        search.addWidget(
            instantsearch.widgets.configure({
                filters:
                    (locale || 'en-US').indexOf('en-') === 0
                        ? `locale:en-US`
                        : `locale:${locale}`,
                }),
        );

Search Box

        search.addWidget(
            instantsearch.widgets.searchBox({
                container: '#searchbox',
                placeholder: 'Search',
            })
        );

Refinements

        search.addWidget(
            instantsearch.widgets.refinementList({
                container: '#category-list',
                attribute: 'snippet.category',
            })
        );

        search.addWidget(
            instantsearch.widgets.refinementList({
                container: '#author-list',
                attribute: 'snippet.author',
            })
        );

Hits

        search.addWidget(
            instantsearch.widgets.hits({
                hitsPerPage: 5,
                container: '#hits',
                cssClasses: {
                    list: 'amp-dc-card-list-wrap',
                    item: 'amp-dc-card',
                },
                templates: {
                    item: ({ snippet, _meta }: any) => {
                        return `
                            <a ...>
                                ...
                            </a>
                    `;
                    },
                },
            })
        );

Pagination

        search.addWidget(
            instantsearch.widgets.pagination({
                container: '#pagination',
            })
        );

Hits per page

        search.addWidget(
            instantsearch.widgets.hitsPerPage({
                container: '#hits-per-page',
                items: [
                    { label: '5 hits per page', value: 5, default: true },
                    { label: '10 hits per page', value: 10 },
                ],
            })
        );

JSX Template

            <PageContent className="blog-list__container">
                <div className="blog-list">
                    <div className="blog-list-facets">
                    <Typography variant="h6">
                        <Breadcrumb navigationItem={navigationItem} />
                      </Typography>
                      <Typography variant="h2" component="h2">
                          Blog
                      </Typography> 
                        <div id="searchbox" className="ais-SearchBox" />
                        <ProductFacet title="Categories" className="blog-list-facet blog-list-facet--categories">
                            <div id="category-list" />
                        </ProductFacet>
                        <ProductFacet title="Author" className="blog-list-facet">
                            <div id="author-list" />
                        </ProductFacet>
                        <div id="hits-per-page" style={{ display: 'none' }} />
                    </div>
                    <div className="blog-list-results">
                        <div className="amp-dc-card-list amp-dc-prod-5-rows amp-dc-cards-hero amp-dc-cards-blog">
                            <div id="hits" />
                        </div>
                        <div id="pagination" />
                    </div>
                </div>
            </PageContent>

top

Navigation Hierarchy

Site Pages

The Site Pages hierarchy defines the top navigation of the site. This hierarchy is always loaded server-side and available for the Next.js pages.

Each commerce related node (Site Pages and Category Page) have an option populate sub nodes from commerce.

Site Pages

When set to true, the navigation will auto populate sub items from the commerce category (or root).

If set to false, only CMS managed sub items will appear.

CMS managed sub items will display AFTER the commerce items.

The default automation has the Site Pages node set to true to automatically render sub items from commerce.

Other sub-hierarchies like Components and Themes are also always loaded in.

    const data = await fetchPageData({
        ...input,
        content: {
            ...input.content,
            configComponents: { key: 'config/components' }
        },
        hierarchies: {
            ...input.hierarchies,
            pages: {
                tree: { key: 'homepage' }
            },
            themes: {
                tree: { key: 'configuration/themes' }
            }
        }
    }, context);

top

Product Detail Page Layout

Demostore features product detail page layouts that can be specific to:

  • a category
  • a product
  • a Designer (could be any product attribute)

A specific UI Extension allows you to change the whole layout of the product detail page. There is a default product layout with delivery key layout/default-pdp. The Commerce Experiences hierarchy allows you to map categories, products, designers to specific layouts. In the code, the Filter API is used to search Commerce Experiences based on the context (product categories, product ID, product attributes).

Personalisation

In order to illustrate a personalisation approach this project contains a slot type which allows a user to associate content to user segments.

  • Repository: slots
  • Content type: Personalized Banner Slot
  • Schema: https://demostore.amplience.com/slots/personalized-banner
  • Component: components/cms-modern/PersonalizedBannerSlot/PersonalizedBannerSlot.tsx

Authoring

When using this slot type in the scheduler you can add multiple items and associate user segments to each item in order to illustate working with personalised content in Amplience.

Note about slots usage: Slots are designed to be created in the content library but the content of which is designed to be used in the Scheduling.

User Authoring

Rules

  • You can have multiple items in your slot
  • Each item can be associated to one or more segments
  • An item without segments associated is 'default' content

Note: This is just one approach for demonstration purposes.

Previewing (Site preview)

When previewing and testing your personlised banner slot behavior, you must be in a FULL site preview, not just the slot preview.

To select a user segment click on the user icon in the top nav:

User Icon

Then select a segment to preview, or select the blank item to behave as if you are not signed in:

Segment Selection

The following rules will apply to all instances of the personalised banner slot in your application.

Rules

  • If there are no items, nothing will display
  • If you are NOT signed in (with a user segment):
    • It will display the FIRST item without a user segment associated
    • If no content is found without a segment it will display nothing
  • If you ARE signed in (with a user segment):
    • It will display the FIRST item matching the user segment signed in with
    • If no matches are found it will:
      • Display default content (the FIRST item without a user segment aassociated)
      • If no content is found without a segment it will display nothing

Layouts

Product Layout

Commerce Experiences

Commerce Experiences

Getting default layout and related product content

const [data, product] = await Promise.all(
    [
      fetchStandardPageData(
        {
          content: {
            defaultPDPLayout: pdpLayout ? { id: pdpLayout as string } : { key: 'layout/default-pdp' },
            productContent: { key: "product/" + id },
          },
        },
        context
      ),
      getProduct({ id }, cmsContext, userContext)
    ]
  );

Configuration based on category

  // config based on category
  product.categories.forEach((category: any) => {
    experienceConfigRequests.push(
      {
        filterBy: [
          {
            path: '/_meta/schema',
            value: 'https://demostore.amplience.com/site/experiences/category'
          },
          {
            path: '/id',
            value: category.id
          }
        ],
        sortBy: {
          key: 'default',
          order: 'desc'
        },
        page: {
          size: 1
        }
      }
    )
  });

Config based on SKU

  // config based on SKU
  experienceConfigRequests.push(
    {
      filterBy: [
        {
          path: '/_meta/schema',
          value: 'https://demostore.amplience.com/product-experience'
        },
        {
          path: '/id',
          value: product.id
        }
      ],
      sortBy: {
        key: 'default',
        order: 'desc'
      },
      page: {
        size: 1
      }
    }
  );

Config based on Designer

// config based on designer
  let designer = _.find(product?.variants?.[0]?.attributes, (att: Attribute) => att.name === 'designer')
  
  if (designer) {
    experienceConfigRequests.push(
      {
        filterBy: [
          {
            path: '/_meta/schema',
            value: 'https://demostore.amplience.com/site/experiences/designer'
          },
          {
            path: '/designer',
            value: designer
          }
        ],
        sortBy: {
          key: 'default',
          order: 'desc'
        },
        page: {
          size: 1
        }
      }
    );
  }

top

Theming

In the Configuration hierarchy, you can create Themes with Palette and Typography. You can define a default theme, and additional ones. Some components like Blog can use a Theme, and you can also wrap any component with a ThemeWrapper to change the theme locally.

Default Theme

Theme Default

Typography

Theme Palette

Palette

Theme Typography

Theme Wrapper

Theme Wrapper

top

Admin UI Panels

Admin Panels

Admin UI Panels allow you to access some admin features useful for testing your application and content. You can for instance switch from Production to Staging mode, show all components, slots or editions on the screen. You can add your own panels in the AdminPanel component.

const panels = [
  {
    label: 'Content Preview',
    icon: VisibilityIcon,
    component: ContentPreviewPanel
  },
  {
    label: 'Components',
    icon: ExtensionIcon,
    component: ComponentsPanel
  }
]

Shoppable Image

This extension allows users to define Focal Points and interactable Hotspots over an image, in a format similar to what Content Hub provides, but with the data being stored on a content item.

Shoppable Image

The dc-extension-shoppable-image is hosted on GitHub.

Note: POI is not implemented in the Component yet but will be in a future release.

There are several options that you can put in the selector column that drive specific functionality from the information in the target.

Target Selector
Links to a product by ID. On hover, will show the product name, price and thumbnail if available. On click it will go to the product details page.
123456789
.product
Links to a category by ID. On hover, will show the category name. On click it will go to the category page.
women-bags
.category
A link to any URL in the same tab. Can be relative or absolute.
https://amplience.com
.link
A link to any external URL in a new tab. Should be absolute.
https://amplience.com
.linkNew
Opens a drawer displaying Amplience content with the specified key.
content/richttext1
.deliveryKey
A tooltip that does nothing on click.
tooltip/tooltip1
.tooltip

Note: Products and categories are coming from your commerce integration (see eCommerce Configuration).

.product Product selector

If you would like to link a particular Hotspot/Polygon to a product, paste the product ID in Target and change the Selector to .product. This adds the ID of the product, along with the type of media you are using.

So you can see in the visualisation pane, we now have a product hotspot which shows a thumbnail image of the product along with the price and description.

Shoppable Image

.category Category selector

You can use the .category selector with the category from your web application as the Target. This displays the Category name when you hover over that hotspot or polygon.

Shoppable Image

.link Link selector

The .link selector can be used with the link URL as the Target which opens the link in the same tab. This displays in the visualisations as View as it links to a web page.

Shoppable Image

.linkNew Link New selector

Use the .linkNew selector with the link URL as the Target which opens the link in a new tab. This, like Link, displays in the visualisations as View as it links to a web page, but also display the icon for opening in a new tab.

Shoppable Image

.deliveryKey Content selector

The .deliveryKey selector can be used with the delivery key you have assigned to a content item. This displays as More Details and takes user to the content item which bears the corresponding delivery key.

Shoppable Image

Once you click the More Details, it will open a side-drawer container the content items linked by the Delivery Key.

Shoppable Image

.tooltip Tooltip selector

You can use the .tooltip selector where you can add a tooltip with bespoke text (which has no link attached). This will display the text defined in the settings.

Shoppable Image

Shoppable Image

AI Assistant

You can now use AI to automatically detect objects to set focal points & hotspots within your images rapidly to make your digital experiences interactive and drive conversions (you can read more on the blog post).

Shoppable Image

Focal Point

Once objects have been detected, you can use one of them to set the focal point.

Shoppable Image

In this example, you can set the focal point to a detected brush in the image.

Hotspots

In the following example, you can add hotspots from the AI Assistant to your list:

Shoppable Image

It will automatically be added with the same name for selector and target, for instance .lipstick for the selector, and lipstick for the target. You can then change to one of the selectors above.

Polygon

AI Assistant is powerful enough to detect complex objects and create detailed polygons out of them. You can see in the following example how a bag is fully detected:

Shoppable Image

How shoppable image interactions are rendered

This section is a deeper look in to how the demostore handles the various shoppable image interactions.

Each interaction (hotspot/polygon) will specify a "selector" and a "target" to define how a user will interact with it. Within the demostore we use the ShoppableImageInteractable component to determine how all interactions should be rendered.

For example, a category interaction would be configured with a "target" of blue-shoes and a "selector" of .category.

Each interactable will initialise a ShoppableImageInteractable component and the selector will determine how it should be rendered e.g.

const ShoppableImageInteractable = ({ children, selector, target, tooltips }: ShoppableImageInteractableProps) => {
    const { categoriesBySlug } = useECommerce();
    const [drawerOpen, setDrawerOpen] = useState(false);

    switch (selector) {
        ...
        case InteractableType.CATEGORY: {
            return (
                <Link passHref href={urlBuilder(selector, target)}>
                    <Tooltip title={categoriesBySlug[target]?.name ?? 'Category not found'} followCursor>
                        {children}
                    </Tooltip>
                </Link>
            );
        }
        ...
    }
}

Using the code snippet above as an example, we configure a category interaction with a "target" of blue-shoes and a "selector" of .category. The .category selector matches InteractableType.CATEGORY in the selector switch statement. This renders the "category" view of a ShoppableImageInteractable, and in this case we wrap the children elements with a tooltip to display the category name (using the "target" as a lookup), and a clickable link to the category page.

Stylitics

Uses the Amplience Stylitics Integration (See link for full documentation) to render Stylitics widgets as a component. Stylitics and Amplience are a great fit for our creating automated shoppable experience using the great capabilities of Stylitics to increase AOV and basket size.

The demostore implementation includes the following:

  • Sample product set that can be used when selecting products (see documentation)
  • All of the component and implementation in React/NextJS
  • Sample implementation for overriding link values
  • Sample implementation of inheriting SKU from PDP

top

Accelerated Media

A new Accelerated Media Admin Panel has been added to the demostore implementation.

Enable / Disable Accelerated Media

You can enable / disable Accelerated Media in the Admin Panel:

Accelerated Media Flag

The use of AVIF format is globally controlled on the Front-End in all demostore pages:

  • Home page
  • Category pages
  • Product detail pages, including Product Content
  • Blog page & blog entry pages
  • Stores page & store detail pages

Getting Image Statistics

You can request image statistics for the current page:

Getting Image Statistics

The number of Amplience images in the page is displayed, and a summary graph is displayed in the Admin Panel with total sizes by image formats.

Note: images statistics are cleared each time you navigate to a different page.

Note: you should scroll down the current page to get all images due to lazy loading.

Excluding Images

When images can't be converted to AVIF, a checkbox will appear to exclude these images from the summary graph calculation.

Image Statistics Details

Once image statistics are retrieved, you can access details in a new modal window:

Details Grid View

A grid view shows you all images with a bar chart showing each possible formats and their sizes, ordered by size.

Details List View

In the details modal, images that can't be converted will appear with a red outline, showing the original request type, and the acutal type that is returned.

Invalid Images

A list view shows the same information in a table view that you can export as CSV.

Export as CSV

top