npm package: @frontend-sdk/okendo
Description: Okendo integration for Shogun Frontend.
Author: Shogun
Latest version: 0.25.1

# Okendo

Okendo integration for Shogun Frontend.

Okendo helps Shopify retailers build shopper trust and excitement, showcase customer experiences and compel buying action.

<table>
  <tbody>
    <tr>
      <td>⚠️</td>
      <td>This package runs on Shogun Frontend and is in customer Beta. It might not currently support all ecommerce platforms or cover all use cases.</td>
    </tr>
  </tbody>
</table>

[Official website →](https://www.okendo.io/)

## Overview

Users of Shogun Frontend with Shopify stores can leverage this package to display individual and aggregate reviews from Okendo, and submit new reviews to Okendo.

## Installation

`yarn add @frontend-sdk/okendo`

`npm install @frontend-sdk/okendo`

## Submit review

### Usage

Call `getOkendoReviewSubmissionUrl()` with your [Okendo subscriber ID](#how-to-find-okendo-subscriber-id) and a Shopify product ID. It will return a URL hosted by Okendo, where a new review can be entered.

Create a custom button that, when clicked, takes the user to this review submission URL.

#### Example

```jsx
import { getOkendoReviewSubmissionUrl } from '@frontend-sdk/okendo'

const SubmitReviewPage = () => {
  const subscriberId = 'abcde123-456f-ghi8-9jkl-0123mnop4567'
  const productId = '612345678901'
  const submissionUrl = getOkendoReviewSubmissionUrl(subscriberId, productId)

  const handleClick = () => {
    window.open(submissionUrl, '_blank')
  }

  return (
    <div>
      <button onClick={handleClick}>Click here to go to submit review page</button>
    </div>
  )
}
```

#### How to find Okendo subscriber ID

Your Okendo subscriber ID can be found in Okendo's admin portal (https://reviews.okendo.io/admin) in Settings → Integrations → User ID. The User ID is your subscriber ID.

## Product review aggregate

### Usage

Call `useOkendoProductReviewAggregate()` with your [Okendo subscriber ID](#how-to-find-okendo-subscriber-id) and a Shopify product ID. Destructure the result into `reviewCount`, `reviewCountByLevel`, `reviewRatingValuesTotal`, and `status`.

`reviewCount`: Total number of reviews
`reviewCountByLevel`: Number of reviews per rating
`reviewRatingValuesTotal`: Sum of all ratings
`status`: status of the request to Okendo's API: `success | fail | pending`

Create your own component(s) to display the above data as you would like. To get the average rating, divide `reviewRatingValuesTotal` by `reviewCount`.

#### Example

```jsx
import { useOkendoProductReviewAggregate } from './use-okendo-product-review-aggregate'

export const ProductReviewAggregate = ({ subscriberId, productId }) => {
  const { reviewCount, reviewCountByLevel, reviewRatingValuesTotal, status } = useOkendoProductReviewAggregate({
    subscriberId,
    productId,
  })
  const averageRating = (reviewRatingValuesTotal / reviewCount).toFixed(2)

  return (
    <div>
      <h2>Status of fetching reviews: {status}</h2>
      <StarRating rate={Number(averageRating)} />
      <h3>Average rating: {averageRating} / 5</h3>
      <h3>Number of reviews per rating:</h3>
      <div>
        {Object.entries(reviewCountByLevel).map(([key, value], i) => (
          <div key={i}>{`${key.slice(5, 6)} star: ${value}`}</div>
        ))}
      </div>
      <h3>Total number of reviews: {reviewCount}</h3>
    </div>
  )
}

const StarRating = ({ rate }) => {
  const width = `${(rate * 100) / 5}%`

  return (
    <div style={css.starRating}>
      <span style={css.starRatingA11yText}>Rated {rate.toFixed(2)} out of 5</span>
      {/* Use width to show only the percentage of the fiveFilledStarsImage that matches the rating */}
      <div style={{ ...css.starRatingActive, width }} />
    </div>
  )
}

const css = {
  starRating: {
    position: 'relative',
    width: '75px',
    height: '15px',
    overflow: 'hidden',
  },

  starRatingA11yText: {
    display: 'none',
  },

  starRatingActive: {
    position: 'absolute',
    height: '100%',
    backgroundImage: fiveFilledStarsImage,
    backgroundSize: '75px 15px',
    filter: 'brightness(0)',
  },
}

const fiveFilledStarsImage =
  "url('data:image/svg+xml;charset=UTF-8, %3Csvg%20width%3D%2280%22%20height%3D%2214%22%20viewBox%3D%220%200%2080%2014%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cg%20fill%3D%22%23FFB829%22%20fill-rule%3D%22evenodd%22%3E%3Cpath%20d%3D%22M71.996%2011.368l-3.94%202.42c-.474.292-.756.096-.628-.443l1.074-4.53-3.507-3.022c-.422-.363-.324-.693.24-.74l4.602-.37%201.778-4.3c.21-.51.55-.512.762%200l1.78%204.3%204.602.37c.552.046.666.37.237.74l-3.507%203.02%201.075%204.532c.127.536-.147.74-.628.444l-3.94-2.422zM55.996%2011.368l-3.94%202.42c-.474.292-.756.096-.628-.443l1.074-4.53-3.507-3.022c-.422-.363-.324-.693.24-.74l4.602-.37%201.778-4.3c.21-.51.55-.512.762%200l1.78%204.3%204.602.37c.552.046.666.37.237.74l-3.507%203.02%201.075%204.532c.127.536-.147.74-.628.444l-3.94-2.422zM40.123%2011.368l-3.94%202.42c-.475.292-.756.096-.63-.443l1.076-4.53-3.508-3.022c-.422-.363-.324-.693.24-.74l4.602-.37%201.778-4.3c.21-.51.55-.512.762%200l1.778%204.3%204.603.37c.554.046.667.37.24.74l-3.508%203.02%201.075%204.532c.127.536-.147.74-.628.444l-3.94-2.422zM24.076%2011.368l-3.94%202.42c-.475.292-.757.096-.63-.443l1.076-4.53-3.507-3.022c-.422-.363-.324-.693.238-.74l4.603-.37%201.78-4.3c.21-.51.55-.512.76%200l1.78%204.3%204.602.37c.554.046.667.37.24.74l-3.508%203.02%201.074%204.532c.127.536-.146.74-.628.444l-3.94-2.422zM8.123%2011.368l-3.94%202.42c-.475.292-.756.096-.63-.443l1.076-4.53L1.12%205.792c-.422-.363-.324-.693.24-.74l4.602-.37%201.778-4.3c.21-.51.55-.512.762%200l1.778%204.3%204.603.37c.554.046.667.37.24.74l-3.508%203.02%201.075%204.532c.127.536-.147.74-.628.444l-3.94-2.422z%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E')"
```

## Product reviews

### Usage

Call `useOkendoProductReviews()` with your [Okendo subscriber ID](#how-to-find-okendo-subscriber-id) and a Shopify product ID. You can pass [`params`](#optional-params) as an optional third argument to `useOkendoProductReviews` if you wish to receive fewer or sorted reviews. Destructure the result into `reviews` and `status`.

`reviews`: list of reviews
`status`: status of the request to Okendo's API: `success | fail | pending`

Create your own component(s) to map through the list of review data, and display it as you would like.

#### Optional params

`params`: takes a query string that can include:

- `limit`: integer 1 - 25
- `orderBy`: `has_media desc` | `has_media asc` | `date desc` | `date asc` | `rating desc` | `rating asc` | `helpful desc` | `helpful asc`

For example: `limit=6&orderBy=rating%20desc`

#### Example

```jsx
import { useOkendoProductReviews } from './use-okendo-product-reviews'

export const ProductReviews = ({ subscriberId, productId, params }) => {
  const { reviews, status } = useOkendoProductReviews({ subscriberId, productId, params })

  return (
    <div>
      <h2>Status of fetching reviews: {status}</h2>
      <h1>Product Reviews:</h1>
      {reviews.map((review) => (
        <div key={review.reviewId} style={{ border: '3px solid black', marginBottom: '16px', padding: '16px' }}>
          <h3>{review.title}</h3>
          <img src={review.productImageUrl} alt={review.productName} />
          <div>{review.productName}</div>
          <div>Rating: {review.rating} / 5</div>
          <div>{review.body}</div>
        </div>
      ))}
    </div>
  )
}
```