[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of storefront-permissions-ui/messages/ro.json from the storefront-permissions-ui VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "admin/storefront-permissions.b2bInfo.title": "Informații B2B",
  "admin/storefront-permissions.button.cancel": "Anulare",
  "admin/storefront-permissions.button.delete": "Ștergere informații B2B",
  "admin/storefront-permissions.button.save": "Salvare",
  "admin/storefront-permissions.description": "Funcții și gestionare utilizatori",
  "admin/storefront-permissions.navigation.keywords": "B2B, funcții și profiluri",
  "admin/storefront-permissions.navigation.label": "Permisiuni magazin",
  "admin/storefront-permissions.required": "Obligatoriu",
  "admin/storefront-permissions.tab.new.button": "Nou",
  "admin/storefront-permissions.tab.roles": "Funcții",
  "admin/storefront-permissions.tab.roles.featuresNotFound": "Nu am putut găsi nicio aplicație conectată la permisiunile magazinului",
  "admin/storefront-permissions.tab.roles.name.label": "Funcții",
  "admin/storefront-permissions.tab.roles.category.label": "Category",
  "admin/storefront-permissions.tab.users": "Utilizatori",
  "admin/storefront-permissions.tab.users.canImpersonate.label": "Pot înlocui",
  "admin/storefront-permissions.tab.users.email.label": "E-mail",
  "admin/storefront-permissions.tab.users.organization.label": "Organizație",
  "admin/storefront-permissions.tab.users.costCenter.label": "Centru de Cost",
  "admin/storefront-permissions.tab.users.error": "Eroare la salvarea informațiilor B2B",
  "admin/storefront-permissions.tab.users.name.label": "Nume",
  "admin/storefront-permissions.tab.users.success": "Informații B2B salvate",
  "admin/storefront-permissions.title": "Permisiuni magazin",
  "admin/storefront-permissions.alert-pick": "Trebuie să alegi un centru de cost înainte de a salva modificările",
  "admin/storefront-permissions.searchOrganizations": "Căutare organizații...",
  "admin/storefront-permissions.help.title": "How it works",
  "admin/storefront-permissions.help.text1": "Role Names can be renamed, but categories are fixed.",
  "admin/storefront-permissions.help.text2": "There are three categories available:",
  "admin/storefront-permissions.help.category1": "Store Admin",
  "admin/storefront-permissions.help.category1.text1": "Full access",
  "admin/storefront-permissions.help.category2": "Sales",
  "admin/storefront-permissions.help.category2.text1": "Can manage quotations, sales, and users from the its Organization.",
  "admin/storefront-permissions.help.category2.text2": "It can have different access levels.",
  "admin/storefront-permissions.help.category2.text3": "Can belong to one or many organizations.",
  "admin/storefront-permissions.help.category2.text4": "Sales users can only manage Sales roles.",
  "admin/storefront-permissions.help.category2.text5": "Sales user hierarchy: sales-admin > sales-manager > sales-representative",
  "admin/storefront-permissions.help.category3": "Customers",
  "admin/storefront-permissions.help.category3.text1": "Have access and can manage its own organization.",
  "admin/storefront-permissions.help.category3.text2": "Customers users can only manage users under the Customers roles.",
  "admin/storefront-permissions.help.category3.text3": "Customers users hierarchy: customer-amin > customer-approver > customer-buyer"
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of storefront-permissions-ui/messages/context.json from the storefront-permissions-ui VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "admin/storefront-permissions.navigation.label": "admin/storefront-permissions.navigation.label",
  "admin/storefront-permissions.navigation.keywords": "admin/storefront-permissions.navigation.keywords",
  "admin/storefront-permissions.title": "admin/storefront-permissions.title",
  "admin/storefront-permissions.description": "admin/storefront-permissions.description",
  "admin/storefront-permissions.required": "admin/storefront-permissions.required",
  "admin/storefront-permissions.tab.new.button": "admin/storefront-permissions.tab.new.button",
  "admin/storefront-permissions.button.save": "admin/storefront-permissions.button.save",
  "admin/storefront-permissions.button.delete": "admin/storefront-permissions.button.delete",
  "admin/storefront-permissions.button.cancel": "admin/storefront-permissions.button.cancel",
  "admin/storefront-permissions.tab.users": "admin/storefront-permissions.tab.users",
  "admin/storefront-permissions.tab.users.email.label": "admin/storefront-permissions.tab.users.email.label",
  "admin/storefront-permissions.tab.users.name.label": "admin/storefront-permissions.tab.users.name.label",
  "admin/storefront-permissions.tab.users.canImpersonate.label": "admin/storefront-permissions.tab.users.canImpersonate.label",
  "admin/storefront-permissions.tab.roles.name.label": "admin/storefront-permissions.tab.roles.name.label",
  "admin/storefront-permissions.tab.roles.category.label": "admin/storefront-permissions.tab.roles.category.label",
  "admin/storefront-permissions.tab.roles": "admin/storefront-permissions.tab.roles",
  "admin/storefront-permissions.tab.roles.featuresNotFound": "admin/storefront-permissions.tab.roles.featuresNotFound",
  "admin/storefront-permissions.help.title": "admin/storefront-permissions.help.title",
  "admin/storefront-permissions.help.text1": "admin/storefront-permissions.help.text1",
  "admin/storefront-permissions.help.text2": "admin/storefront-permissions.help.text2",
  "admin/storefront-permissions.help.category1": "admin/storefront-permissions.help.category1",
  "admin/storefront-permissions.help.category1.text1": "admin/storefront-permissions.help.category1.text1",
  "admin/storefront-permissions.help.category2": "admin/storefront-permissions.help.category2",
  "admin/storefront-permissions.help.category2.text1": "admin/storefront-permissions.help.category2.text1",
  "admin/storefront-permissions.help.category2.text2": "admin/storefront-permissions.help.category2.text2",
  "admin/storefront-permissions.help.category2.text3": "admin/storefront-permissions.help.category2.text3",
  "admin/storefront-permissions.help.category2.text4": "admin/storefront-permissions.help.category2.text4",
  "admin/storefront-permissions.help.category2.text5": "admin/storefront-permissions.help.category2.text5",
  "admin/storefront-permissions.help.category3": "admin/storefront-permissions.help.category3",
  "admin/storefront-permissions.help.category3.text1": "admin/storefront-permissions.help.category3.text1",
  "admin/storefront-permissions.help.category3.text2": "admin/storefront-permissions.help.category3.text2",
  "admin/storefront-permissions.help.category3.text3": "admin/storefront-permissions.help.category3.text3"
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of storefront-permissions-ui/messages/ko.json from the storefront-permissions-ui VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "admin/storefront-permissions.b2bInfo.title": "B2B 정보",
  "admin/storefront-permissions.button.cancel": "취소",
  "admin/storefront-permissions.button.delete": "B2B 정보 제거",
  "admin/storefront-permissions.button.save": "저장",
  "admin/storefront-permissions.description": "역할 및 사용자 관리",
  "admin/storefront-permissions.navigation.keywords": "B2B, 역할, 프로필",
  "admin/storefront-permissions.navigation.label": "스토어프론트 권한",
  "admin/storefront-permissions.required": "필수",
  "admin/storefront-permissions.tab.new.button": "추가",
  "admin/storefront-permissions.tab.roles": "역할",
  "admin/storefront-permissions.tab.roles.featuresNotFound": "스토어프론트 권한에 연결된 앱을 찾을 수 없습니다.",
  "admin/storefront-permissions.tab.roles.name.label": "역할",
  "admin/storefront-permissions.tab.roles.category.label": "Category",
  "admin/storefront-permissions.tab.users": "사용자",
  "admin/storefront-permissions.tab.users.canImpersonate.label": "가장할 수 있음",
  "admin/storefront-permissions.tab.users.email.label": "이메일",
  "admin/storefront-permissions.tab.users.organization.label": "조직",
  "admin/storefront-permissions.tab.users.costCenter.label": "비용 센터",
  "admin/storefront-permissions.tab.users.error": "B2B 정보를 저장하는 동안 오류가 발생했습니다.",
  "admin/storefront-permissions.tab.users.name.label": "이름",
  "admin/storefront-permissions.tab.users.success": "B2B 정보 저장됨",
  "admin/storefront-permissions.title": "스토어프론트 권한",
  "admin/storefront-permissions.alert-pick": "변경 사항을 저장하기 전에 비용 센터를 선택해야 합니다.",
  "admin/storefront-permissions.searchOrganizations": "조직 검색...",
  "admin/storefront-permissions.help.title": "How it works",
  "admin/storefront-permissions.help.text1": "Role Names can be renamed, but categories are fixed.",
  "admin/storefront-permissions.help.text2": "There are three categories available:",
  "admin/storefront-permissions.help.category1": "Store Admin",
  "admin/storefront-permissions.help.category1.text1": "Full access",
  "admin/storefront-permissions.help.category2": "Sales",
  "admin/storefront-permissions.help.category2.text1": "Can manage quotations, sales, and users from the its Organization.",
  "admin/storefront-permissions.help.category2.text2": "It can have different access levels.",
  "admin/storefront-permissions.help.category2.text3": "Can belong to one or many organizations.",
  "admin/storefront-permissions.help.category2.text4": "Sales users can only manage Sales roles.",
  "admin/storefront-permissions.help.category2.text5": "Sales user hierarchy: sales-admin > sales-manager > sales-representative",
  "admin/storefront-permissions.help.category3": "Customers",
  "admin/storefront-permissions.help.category3.text1": "Have access and can manage its own organization.",
  "admin/storefront-permissions.help.category3.text2": "Customers users can only manage users under the Customers roles.",
  "admin/storefront-permissions.help.category3.text3": "Customers users hierarchy: customer-amin > customer-approver > customer-buyer"
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of storefront-permissions-ui/messages/th.json from the storefront-permissions-ui VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "admin/storefront-permissions.b2bInfo.title": "ข้อมูล B2B",
  "admin/storefront-permissions.button.cancel": "ยกเลิก",
  "admin/storefront-permissions.button.delete": "ลบข้อมูล B2B",
  "admin/storefront-permissions.button.save": "บันทึก",
  "admin/storefront-permissions.description": "บทบาทและการบริหารจัดการผู้ใช้",
  "admin/storefront-permissions.navigation.keywords": "B2B, บทบาท, โปรไฟล์",
  "admin/storefront-permissions.navigation.label": "สิทธิ์หน้าร้าน",
  "admin/storefront-permissions.required": "บังคับ",
  "admin/storefront-permissions.tab.new.button": "ใหม่",
  "admin/storefront-permissions.tab.roles": "บทบาท",
  "admin/storefront-permissions.tab.roles.featuresNotFound": "ไม่พบว่ามีแอปใดเชือมต่อกับสิทธิ์หน้าร้าน",
  "admin/storefront-permissions.tab.roles.name.label": "บทบาท",
  "admin/storefront-permissions.tab.roles.category.label": "Category",
  "admin/storefront-permissions.tab.users": "ผู้ใช้",
  "admin/storefront-permissions.tab.users.canImpersonate.label": "สามารถเลียนแบบ",
  "admin/storefront-permissions.tab.users.email.label": "อีเมล",
  "admin/storefront-permissions.tab.users.organization.label": "องค์กร",
  "admin/storefront-permissions.tab.users.costCenter.label": "ศูนย์ต้นทุน",
  "admin/storefront-permissions.tab.users.error": "มีข้อผิดพลาดขณะบันทึกข้อมูล B2B",
  "admin/storefront-permissions.tab.users.name.label": "ชื่อ",
  "admin/storefront-permissions.tab.users.success": "บันทึกข้อมูล B2B แล้ว",
  "admin/storefront-permissions.title": "สิทธิ์หน้าร้าน",
  "admin/storefront-permissions.alert-pick": "คุณต้องเลือกศูนย์ต้นทุนก่อนบันทึกการเปลี่ยนแปลง",
  "admin/storefront-permissions.searchOrganizations": "ค้นหาองค์กร...",
  "admin/storefront-permissions.help.title": "How it works",
  "admin/storefront-permissions.help.text1": "Role Names can be renamed, but categories are fixed.",
  "admin/storefront-permissions.help.text2": "There are three categories available:",
  "admin/storefront-permissions.help.category1": "Store Admin",
  "admin/storefront-permissions.help.category1.text1": "Full access",
  "admin/storefront-permissions.help.category2": "Sales",
  "admin/storefront-permissions.help.category2.text1": "Can manage quotations, sales, and users from the its Organization.",
  "admin/storefront-permissions.help.category2.text2": "It can have different access levels.",
  "admin/storefront-permissions.help.category2.text3": "Can belong to one or many organizations.",
  "admin/storefront-permissions.help.category2.text4": "Sales users can only manage Sales roles.",
  "admin/storefront-permissions.help.category2.text5": "Sales user hierarchy: sales-admin > sales-manager > sales-representative",
  "admin/storefront-permissions.help.category3": "Customers",
  "admin/storefront-permissions.help.category3.text1": "Have access and can manage its own organization.",
  "admin/storefront-permissions.help.category3.text2": "Customers users can only manage users under the Customers roles.",
  "admin/storefront-permissions.help.category3.text3": "Customers users hierarchy: customer-amin > customer-approver > customer-buyer"
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of storefront-permissions-ui/messages/es.json from the storefront-permissions-ui VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "admin/storefront-permissions.b2bInfo.title": "Información B2B",
  "admin/storefront-permissions.button.cancel": "Cancelar",
  "admin/storefront-permissions.button.delete": "Eliminar información B2B",
  "admin/storefront-permissions.button.save": "Guardar",
  "admin/storefront-permissions.description": "Gestión de roles y usuarios",
  "admin/storefront-permissions.navigation.keywords": "B2B, roles, perfiles",
  "admin/storefront-permissions.navigation.label": "Permisos del storefront",
  "admin/storefront-permissions.required": "Obligatorio",
  "admin/storefront-permissions.tab.new.button": "Nuevo",
  "admin/storefront-permissions.tab.roles": "Roles",
  "admin/storefront-permissions.tab.roles.featuresNotFound": "No pudo encontrar ninguna aplicación conectada a los permisos del storefront",
  "admin/storefront-permissions.tab.roles.name.label": "Rol",
  "admin/storefront-permissions.tab.roles.category.label": "Category",
  "admin/storefront-permissions.tab.users": "Usuarios",
  "admin/storefront-permissions.tab.users.canImpersonate.label": "Puede suplantar",
  "admin/storefront-permissions.tab.users.email.label": "Email",
  "admin/storefront-permissions.tab.users.organization.label": "Organización",
  "admin/storefront-permissions.tab.users.costCenter.label": "Centro de costos",
  "admin/storefront-permissions.tab.users.error": "Error al guardar información B2B",
  "admin/storefront-permissions.tab.users.name.label": "Nombre",
  "admin/storefront-permissions.tab.users.success": "Información B2B guardada",
  "admin/storefront-permissions.title": "Permisos del storefront",
  "admin/storefront-permissions.alert-pick": "Debes elegir un centro de costos antes de guardar los cambios",
  "admin/storefront-permissions.searchOrganizations": "Buscar organizaciones...",
  "admin/storefront-permissions.help.title": "How it works",
  "admin/storefront-permissions.help.text1": "Role Names can be renamed, but categories are fixed.",
  "admin/storefront-permissions.help.text2": "There are three categories available:",
  "admin/storefront-permissions.help.category1": "Store Admin",
  "admin/storefront-permissions.help.category1.text1": "Full access",
  "admin/storefront-permissions.help.category2": "Sales",
  "admin/storefront-permissions.help.category2.text1": "Can manage quotations, sales, and users from the its Organization.",
  "admin/storefront-permissions.help.category2.text2": "It can have different access levels.",
  "admin/storefront-permissions.help.category2.text3": "Can belong to one or many organizations.",
  "admin/storefront-permissions.help.category2.text4": "Sales users can only manage Sales roles.",
  "admin/storefront-permissions.help.category2.text5": "Sales user hierarchy: sales-admin > sales-manager > sales-representative",
  "admin/storefront-permissions.help.category3": "Customers",
  "admin/storefront-permissions.help.category3.text1": "Have access and can manage its own organization.",
  "admin/storefront-permissions.help.category3.text2": "Customers users can only manage users under the Customers roles.",
  "admin/storefront-permissions.help.category3.text3": "Customers users hierarchy: customer-amin > customer-approver > customer-buyer"
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of storefront-permissions-ui/admin/navigation.json from the storefront-permissions-ui VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "section": "accountSettings",
  "path": "/admin/storefront-permissions/roles/list",
  "icon": "extensions-ic",
  "titleId": "admin/storefront-permissions.navigation.label",
  "searchKeyWordsHelpers": "admin/storefront-permissions.navigation.keywords"
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of storefront-permissions-ui/admin/routes.json from the storefront-permissions-ui VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "admin.app.storefront-permissions": {
    "around": ["layout"],
    "path": "/admin/app/storefront-permissions/*"
  },
  "admin.app.storefront-permissions.users-list": {
    "component": "userList",
    "path": "/admin/app/storefront-permissions/user/list"
  },
  "admin.app.storefront-permissions.users-new": {
    "component": "userNew",
    "path": "/admin/app/storefront-permissions/user/new"
  },
  "admin.app.storefront-permissions.users-edit": {
    "component": "userEdit",
    "path": "/admin/app/storefront-permissions/user/*id"
  },
  "admin.app.storefront-permissions.roles-list": {
    "component": "rolesList",
    "path": "/admin/app/storefront-permissions/roles/list"
  },
  "admin.app.storefront-permissions.roles-new": {
    "component": "roleNew",
    "path": "/admin/app/storefront-permissions/role/new"
  },
  "admin.app.storefront-permissions.roles-edit": {
    "component": "roleEdit",
    "path": "/admin/app/storefront-permissions/role/*id"
  },
  "layout": {
    "component": "index"
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of storefront-permissions-ui/docs/README.md from the storefront-permissions-ui VTEX app.

[Assistant]
Here's the implementation:

```markdown
# Storefront Permissions UI

<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->

[![All Contributors](https://img.shields.io/badge/all_contributors-0-orange.svg?style=flat-square)](#contributors-)

<!-- ALL-CONTRIBUTORS-BADGE:END -->

**Storefront Permissions UI** provides an interface for the [Storefront Permissions](https://developers.vtex.com/vtex-developer-docs/docs/vtex-storefront-permissions) app, which is part of VTEX’s [B2B Suite](https://developers.vtex.com/vtex-developer-docs/docs/vtex-b2b-suite) solution, a collection of apps that allow stores to manage organizations, storefront roles and permissions, and checkout settings for B2B commerce relationships.

B2B customers often play distinct roles within their organization, such as professional buyer, manager, or supervisor. Each role is associated with a different set of permissions, depending on the actions the user needs to perform.

This app communicates with the backstage **Storefront Permissions** app and provides the following features for this scenario:

* [Roles management](#roles-management): allow VTEX Admin users to manage B2B roles and associated app permissions through an interface.
* [Theme blocks configuration](#theme-blocks-configuration): enable conditional theme blocks so that only users with the required permissions can access determined parts of the content in your storefront.

We recommend that you use it alongside the other apps in the [B2B Suite](https://developers.vtex.com/vtex-developer-docs/docs/vtex-b2b-suite) for all functionalities to work as expected.

## Before you start

Make sure you have the [VTEX IO CLI (Command Line Interface)](https://developers.vtex.com/vtex-developer-docs/docs/vtex-io-documentation-vtex-io-cli-install) installed in your machine.

## Installation

You can install the app by running `vtex install vtex.storefront-permissions-ui` in your terminal, using the [VTEX IO CLI](https://developers.vtex.com/vtex-developer-docs/docs/vtex-io-documentation-vtex-io-cli-installation-and-command-reference).

After this command, the app will be installed in your VTEX Admin, and you can access it on **Apps** > **Storefront Permissions**.

> ℹ Installing **Storefront Permissions UI** has the effect of automatically installing the [Storefront Permissions](https://developers.vtex.com/vtex-developer-docs/docs/vtex-storefront-permissions) app as a dependency, in case it is not already installed.

## Roles management

To manage roles and their respective app permissions using the Storefront Permissions UI, follow the steps below after installing the app.

1. At the VTEX Admin, navigate to **Apps** > **Storefront Permissions**.
2. Click on one of the [available storefront roles](#available-storefront-roles).
3. Click on one of the listed apps.

    > ℹ Only apps that are integrated with **Storefront Permissions** will be on the list – check our [Storefront Permissions](https://developers.vtex.com/vtex-developer-docs/docs/vtex-storefront-permissions#advanced-app-integration-optional) documentation for more information.

4. Select which permissions you want to enable for the role, as illustrated below.
5. Once you are done, click on `Save`.

![storefront-permissions-ui](https://raw.githubusercontent.com/vtex-apps/storefront-permissions-ui/master/docs/images/storefront-en.gif)

After associating roles with the desired permissions, you may then assign roles to users. To learn more about this, read our [B2B Organizations documentation](https://developers.vtex.com/vtex-developer-docs/docs/vtex-b2b-organizations#users).

### Available storefront roles

In the following table, you can see the available storefront roles, their key used for identification in the app’s code, and their description.

| **Role** | **Key** | **Description** |
|---|---|---|
| Store Admin | `store-admin` | Store administrator, that is, a user who has access to the VTEX Admin. |
| Sales Admin | `sales-admin` | Sales administrator user who can manage all sales users. |
| Sales Manager | `sales-manager` | Sales manager user who can manage sales users in the same organization, as well as assist or impersonate buyers during navigation or purchase. |
| Sales Representative | `sales-representative` | Sales representative user who can assist or impersonate buyers in the same cost center during navigation or purchase.  |
| Organization Admin | `customer-admin` | Main organization user who manages the organization information, as well as its members and cost centers. |
| Organization Approver | `customer-approver` | Organization user who can take a saved cart or quote that was created by an **Organization Buyer** and use it to place an order. |
| Organization Buyer | `customer-buyer` | Organization user who has the ability to add items to cart. If the **B2B Quotes** app is installed, they are also able to save their cart for future use or create a quote. |

## Theme blocks configuration

If you only want users with roles that have the required permissions to be able to access determined parts of the content in your storefront, you can also configure conditional blocks in your store’s theme. These types of blocks allow you to validate information in order to display or hide content in your storefront.

In case you need more information on how to start building your own theme, read our [documentation](https://developers.vtex.com/vtex-developer-docs/docs/vtex-io-documentation-1-basicsetup) before you proceed.

If you are experienced in developing and customizing your store’s theme, follow the steps below to declare the conditional blocks enabled by **Storefront Permissions UI**.

1. Open your store theme app’s repository in your local files.
2. In the `manifest.json` file, under `dependencies`, add `"vtex.storefront-permissions-ui": "1.x"`, like in the example below.

    ```json
     "dependencies": {
       "vtex.storefront-permissions-ui": "1.x"
      },
    ```

3. Now, you can use the `check-permission`, `allowed-content` and `disallowed-content` blocks on any files you want in your store’s theme. See more information on these blocks below.

    | **Block name** | **Description** |  |
    |---|---|---|
    | `check-permission` | Checks if the current user has the required permissions to access the content wrapped with this block.<br>You can wrap content blocks with check-permission when declaring them to validate if the user has the required permissions to see that content. Example: `check-permission#carousel` |  |
    | `allowed-content` | Allows you to define on children blocks the content that will be shown for allowed users – users with roles that have the required permissions to view the block. |  |
    | `disallowed-content` | Optional block that allows you to define on children blocks the content that will be shown for disallowed users – users with roles that do not have the required permissions to view the block. |  |
    | `loading-content` | Optional block that allows you to define a loading content while permissions are loading. |  |

    This component will check if the user has permission to see the `allowed-content`, otherwise they will see the `disallowed-content`.

    The `check-permission` block has a prop named `roles`, which lets you define an array of roles that are allowed to see the blocks defined under the children blocks of `allowed-content`.

    | **Prop name** | **Type** | **Description** | **Required** | **Example** |
    |---|---|---|---|---|
    | `roles` | array of strings | List of the allowed roles, identified by their key. Learn more about each role and its key in the [Available storefront roles](#available-storefront-roles) section. | true | `["store-admin", "sales-admin"]` |

#### Example

In the example below, in a store’s home, a carousel with banners will only be visible to logged in users with the `store-admin` or `sales-admin` roles. Users with other roles will see a warning message (`**You don't have access to this content**`) instead.

```diff
{
  "store.home": {
    "blocks": [
+     "check-permission#carousel",
      "shelf#home",
      "info-card#home",
      "rich-text#question",
      "rich-text#link"
    ]
  },
+  "check-permission#carousel": {
+    "props": {
+      "roles": ["store-admin", "sales-admin"]
+    },
+    "blocks": ["allowed-content#carousel","disallowed-content#carousel", "loading-content#carousel"]
+  },
+  "allowed-content#carousel": {
+    "children": ["carousel#home"]
+  },
+  "disallowed-content#carousel": {
+    "children": ["rich-text#disallowed"]
+  },
+  "loading-content#carousel": {
+    "children": ["rich-text#loading"]
+  },
+  "rich-text#disallowed": {
+    "props": {
+      "text": "**You don't have access to this content**"
+    }
+  },
+  "rich-text#loading": {
+    "props": {
+      "text": "Waiting for permissions..."
+    }
+  },
  "carousel#home": {
    "props": {
      "autoPlay": false,
      "banners": [
        {
          "image": "https://storecomponents.vteximg.com.br/arquivos/banner-principal.png",
          "mobileImage": "https://storecomponents.vteximg.com.br/arquivos/banner-principal-mobile.jpg"
        },
        {
          "image": "https://storecomponents.vteximg.com.br/arquivos/banner.jpg",
          "mobileImage": "https://storecomponents.vteximg.com.br/arquivos/banner-principal-mobile.jpg"
        }
      ],
      "height": 720,
      "showArrows": true,
      "showDots": true
    }
  },
```

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of storefront-permissions-ui/react/index.tsx from the storefront-permissions-ui VTEX app.

[Assistant]
Here's the implementation:

```tsx
/* eslint-disable @typescript-eslint/no-explicit-any */
import type { FC } from 'react'
import React, { useState } from 'react'
import type { WrappedComponentProps } from 'react-intl'
import { FormattedMessage, injectIntl } from 'react-intl'
import { useQuery } from 'react-apollo'
import { Layout, PageBlock, PageHeader } from 'vtex.styleguide'

import GET_CONFIG from './queries/getAppSettings.gql'

const AdminB2bWaffle: FC<WrappedComponentProps> = (props) => {
  const [state, setState] = useState<any>({
    missingConfig: true,
  })

  useQuery(GET_CONFIG, {
    onCompleted: (res: any) => {
      setState({
        ...state,
        missingConfig: res.getAppSettings,
      })
    },
  })

  return (
    <Layout
      pageHeader={
        <PageHeader
          title={<FormattedMessage id="admin/storefront-permissions.title" />}
          subtitle={
            <FormattedMessage id="admin/storefront-permissions.description" />
          }
        />
      }
    >
      <PageBlock>
        <>{props.children}</>
      </PageBlock>
    </Layout>
  )
}

export default injectIntl(AdminB2bWaffle)

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of storefront-permissions-ui/react/userEdit.tsx from the storefront-permissions-ui VTEX app.

[Assistant]
Here's the implementation:

```tsx
import UserEdit from './components/userEdit'

export default UserEdit

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of storefront-permissions-ui/react/rolesList.tsx from the storefront-permissions-ui VTEX app.

[Assistant]
Here's the implementation:

```tsx
import Roles from './components/rolesList'

export default Roles

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of storefront-permissions-ui/react/roleEdit.tsx from the storefront-permissions-ui VTEX app.

[Assistant]
Here's the implementation:

```tsx
import RoleEdit from './components/roleEdit'

export default RoleEdit

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of storefront-permissions-ui/react/CheckOrganizationPermission.tsx from the storefront-permissions-ui VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'
import type { ElementType } from 'react'
import { ExtensionPoint } from 'vtex.render-runtime'
import { useQuery } from 'react-apollo'

import getOrganizationPermissions from './queries/getOrganizationPermissions.gql'

type OrganizationRole = 'createQuote'

interface Props {
  roles: OrganizationRole[]
  AllowedContent?: ElementType
  DisallowedContent?: ElementType
  LoadingContent?: ElementType
}

function CheckOrganizationPermission({
  roles = [],
  AllowedContent,
  DisallowedContent,
  LoadingContent,
}: Props) {
  const { data, called, error, loading } = useQuery(
    getOrganizationPermissions,
    {
      ssr: false,
      skip: !roles.length,
      onCompleted(insideData) {
        if (insideData?.getOrganizationByIdStorefront?.permissions) {
          sessionStorage.setItem(
            'checkout.createQuote',
            JSON.stringify(
              insideData?.getOrganizationByIdStorefront?.permissions.createQuote
            )
          )
        }
      },
    }
  )

  if (error) {
    console.error('CheckOrganizationPermission component error:', error)

    return null
  }

  if (called && loading) {
    return LoadingContent ? (
      <LoadingContent />
    ) : (
      <ExtensionPoint id="loading-content" />
    )
  }

  if (!roles.length || !data) {
    return null
  }

  const hasPermission = roles.every(
    (key) => data.getOrganizationByIdStorefront.permissions[key] === true
  )

  if (hasPermission) {
    return AllowedContent ? (
      <AllowedContent />
    ) : (
      <ExtensionPoint id="allowed-content" />
    )
  }

  return DisallowedContent ? (
    <DisallowedContent />
  ) : (
    <ExtensionPoint id="disallowed-content" />
  )
}

export default CheckOrganizationPermission

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of storefront-permissions-ui/react/BlockContent.tsx from the storefront-permissions-ui VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'
import type { PropsWithChildren } from 'react'
import { useCssHandles } from 'vtex.css-handles'
import type { CssHandlesTypes } from 'vtex.css-handles'

const CSS_HANDLES = ['checkPermissionsBlock'] as const

interface Props {
  classes?: CssHandlesTypes.CustomClasses<typeof CSS_HANDLES>
}

function BlockContent({ children, classes }: PropsWithChildren<Props>) {
  const { handles } = useCssHandles(CSS_HANDLES, { classes })

  return <div className={`${handles.checkPermissionsBlock}`}>{children}</div>
}

export default BlockContent

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of storefront-permissions-ui/react/roleNew.tsx from the storefront-permissions-ui VTEX app.

[Assistant]
Here's the implementation:

```tsx
import RoleNew from './components/roleNew'

export default RoleNew

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of storefront-permissions-ui/react/package.json from the storefront-permissions-ui VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "name": "vtexus.storefront-permissions",
  "version": "3.0.1",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "devDependencies": {
    "@types/faker": "^4.1.5",
    "@types/node": "^11.12.1",
    "@types/react": "^16.8.10",
    "apollo-client": "^2.4.7",
    "graphql": "^14.0.2",
    "prop-types": "^15.7.2",
    "react": "^16.8.6",
    "react-dom": "^16.8.6",
    "typescript": "3.9.7",
    "vtex.admin-customers-graphql": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.admin-customers-graphql@3.0.2/public/@types/vtex.admin-customers-graphql",
    "vtex.css-handles": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.css-handles@1.0.0/public/@types/vtex.css-handles",
    "vtex.render-runtime": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.render-runtime@8.132.4/public/@types/vtex.render-runtime",
    "vtex.store": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.store@2.131.1/public/@types/vtex.store",
    "vtex.storefront-permissions": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.storefront-permissions@1.34.1/public/@types/vtex.storefront-permissions",
    "vtex.storefront-permissions-ui": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.storefront-permissions-ui@1.1.0/public/@types/vtex.storefront-permissions-ui",
    "vtex.styleguide": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.styleguide@9.146.4/public/@types/vtex.styleguide"
  },
  "dependencies": {
    "faker": "^4.1.0",
    "react-apollo": "^3.1.5",
    "react-intl": "^5.24.6"
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of storefront-permissions-ui/react/userNew.tsx from the storefront-permissions-ui VTEX app.

[Assistant]
Here's the implementation:

```tsx
import UserNew from './components/userNew'

export default UserNew

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of storefront-permissions-ui/react/userList.tsx from the storefront-permissions-ui VTEX app.

[Assistant]
Here's the implementation:

```tsx
import Users from './components/usersList'

export default Users

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of storefront-permissions-ui/react/CheckPermission.tsx from the storefront-permissions-ui VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React from 'react'
import type { ElementType } from 'react'
import { ExtensionPoint } from 'vtex.render-runtime'
import { useQuery } from 'react-apollo'

import getPermissions from './queries/permissions.gql'

interface Props {
  roles: string[]
  AllowedContent?: ElementType
  DisallowedContent?: ElementType
  LoadingContent?: ElementType
}

function CheckPermission({
  roles = [],
  AllowedContent,
  DisallowedContent,
  LoadingContent,
}: Props) {
  const { data, called, error, loading } = useQuery(getPermissions, {
    ssr: false,
    skip: !roles.length,
  })

  if (error) {
    console.error('CheckPermission error:', error)

    return null
  }

  if (called && loading) {
    return LoadingContent ? (
      <LoadingContent />
    ) : (
      <ExtensionPoint id="loading-content" />
    )
  }

  if (!roles.length || !data) {
    return null
  }

  const hasPermission = roles?.length
    ? roles.indexOf(data?.checkUserPermission?.role?.slug) !== -1
    : false

  if (hasPermission) {
    return AllowedContent ? (
      <AllowedContent />
    ) : (
      <ExtensionPoint id="allowed-content" />
    )
  }

  return DisallowedContent ? (
    <DisallowedContent />
  ) : (
    <ExtensionPoint id="disallowed-content" />
  )
}

export default CheckPermission

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of storefront-permissions-ui/store/interfaces.json from the storefront-permissions-ui VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "check-organization-permission": {
    "component": "CheckOrganizationPermission",
    "allowed": ["allowed-content", "disallowed-content", "loading-content"]
  },
  "check-permission": {
    "component": "CheckPermission",
    "allowed": ["allowed-content", "disallowed-content", "loading-content"]
  },
  "allowed-content": {
    "component": "BlockContent",
    "composition": "children",
    "allowed": "*"
  },
  "disallowed-content": {
    "component": "BlockContent",
    "composition": "children",
    "allowed": "*"
  },
  "loading-content": {
    "component": "BlockContent",
    "composition": "children",
    "allowed": "*"
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of storefront-permissions-ui/public/metadata/messages/es-AR.json from the storefront-permissions-ui VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "name": "B2B Admin",
  "headline": "B2B Admin",
  "overview": "B2B Admin",
  "features": ["B2B Admin"],
  "websiteURL": "https://vtex.com/",
  "support": {
    "url": "https://support.vtex.com/hc/requests"
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of storefront-permissions-ui/public/metadata/messages/pt-BR.json from the storefront-permissions-ui VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "name": "B2B Admin",
  "headline": "B2B Admin",
  "overview": "B2B Admin",
  "features": ["B2B Admin"],
  "websiteURL": "https://vtex.com/",
  "support": {
    "url": "https://support.vtex.com/hc/requests"
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of storefront-permissions-ui/public/metadata/messages/en-US.json from the storefront-permissions-ui VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "name": "B2B Admin",
  "headline": "B2B Admin",
  "overview": "B2B Admin",
  "features": ["B2B Admin"],
  "websiteURL": "https://vtex.com/",
  "support": {
    "url": "https://support.vtex.com/hc/requests"
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of storefront-permissions-ui/react/mutations/deleteUser.gql from the storefront-permissions-ui VTEX app.

[Assistant]
Here's the implementation:

```graphql
mutation delete($id: ID!, $email: String!, $userId: ID) {
  deleteUser(id: $id, email: $email, userId: $userId)
    @context(provider: "vtex.storefront-permissions") {
    status
    message
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of storefront-permissions-ui/react/mutations/deleteRole.gql from the storefront-permissions-ui VTEX app.

[Assistant]
Here's the implementation:

```graphql
mutation delete($id: ID!) {
  deleteRole(id: $id) @context(provider: "vtex.storefront-permissions") {
    status
    message
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of storefront-permissions-ui/react/mutations/saveRole.gql from the storefront-permissions-ui VTEX app.

[Assistant]
Here's the implementation:

```graphql
mutation save(
  $id: ID
  $name: String!
  $slug: String
  $features: [FeatureInput]!
) {
  saveRole(id: $id, name: $name, slug: $slug, features: $features)
    @context(provider: "vtex.storefront-permissions") {
    id
    status
    message
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of storefront-permissions-ui/react/mutations/saveUser.gql from the storefront-permissions-ui VTEX app.

[Assistant]
Here's the implementation:

```graphql
mutation save(
  $id: ID
  $roleId: ID!
  $userId: ID!
  $canImpersonate: Boolean
  $name: String!
  $email: String!
) {
  saveUser(
    id: $id
    roleId: $roleId
    userId: $userId
    canImpersonate: $canImpersonate
    name: $name
    email: $email
  ) @context(provider: "vtex.storefront-permissions") {
    id
    status
    message
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of storefront-permissions-ui/react/mutations/saveConfiguration.gql from the storefront-permissions-ui VTEX app.

[Assistant]
Here's the implementation:

```graphql
mutation save(
  $accountID: String
  $clientID: String
  $clientSecret: String
  $refreshToken: String
  $submit: Boolean
) {
  saveAppSettings(
    accountID: $accountID
    clientID: $clientID
    clientSecret: $clientSecret
    refreshToken: $refreshToken
    submit: $submit
  ) @context(provider: "vtex.storefront-permissions") {
    status
    message
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of storefront-permissions-ui/react/typings/graphql.d.ts from the storefront-permissions-ui VTEX app.

[Assistant]
Here's the implementation:

```typescript
declare module '*.graphql' {
  import type { DocumentNode } from 'graphql'

  const value: DocumentNode
  export default value
}

declare module '*.gql' {
  const gql: any
  export default gql
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of storefront-permissions-ui/react/queries/getOrganizationPermissions.gql from the storefront-permissions-ui VTEX app.

[Assistant]
Here's the implementation:

```graphql
query GetOrganizationStorefront($id: ID) {
  getOrganizationByIdStorefront(id: $id)
    @context(provider: "vtex.b2b-organizations-graphql") {
    permissions {
      createQuote
    }
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of storefront-permissions-ui/react/queries/ListUsers.gql from the storefront-permissions-ui VTEX app.

[Assistant]
Here's the implementation:

```graphql
query ListUsers {
  listUsers @context(provider: "vtex.storefront-permissions") {
    id
    roleId
    userId
    clId
    name
    email
    canImpersonate
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of storefront-permissions-ui/react/queries/getUser.gql from the storefront-permissions-ui VTEX app.

[Assistant]
Here's the implementation:

```graphql
query GetUser($id: ID!) {
  getUser(id: $id) @context(provider: "vtex.storefront-permissions") {
    id
    roleId
    userId
    name
    email
    canImpersonate
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of storefront-permissions-ui/react/queries/getRole.gql from the storefront-permissions-ui VTEX app.

[Assistant]
Here's the implementation:

```graphql
query GetRole($id: ID!) {
  getRole(id: $id) @context(provider: "vtex.storefront-permissions") {
    id
    name
    slug
    features {
      module
      features
    }
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of storefront-permissions-ui/react/queries/ListFeatures.gql from the storefront-permissions-ui VTEX app.

[Assistant]
Here's the implementation:

```graphql
query ListFeatures {
  listFeatures @context(provider: "vtex.storefront-permissions") {
    module
    name
    features {
      label
      value
    }
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of storefront-permissions-ui/react/queries/permissions.gql from the storefront-permissions-ui VTEX app.

[Assistant]
Here's the implementation:

```graphql
query permissions {
  checkUserPermission @context(provider: "vtex.storefront-permissions") {
    role {
      id
      name
      slug
    }
    permissions
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of storefront-permissions-ui/react/queries/ListProfiles.gql from the storefront-permissions-ui VTEX app.

[Assistant]
Here's the implementation:

```graphql
query ListProfiles {
  listProfiles @context(provider: "vtex.storefront-permissions") {
    id
    name
    features {
      module
      features
    }
    roleId
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of storefront-permissions-ui/react/queries/SearchUsers.gql from the storefront-permissions-ui VTEX app.

[Assistant]
Here's the implementation:

```graphql
query SearchUsers($perPage: Int!, $pageNumber: Int!, $filter: String) {
  users(perPage: $perPage, pageNumber: $pageNumber, filter: $filter)
    @context(provider: "vtex.admin-customers-graphql") {
    data {
      id
      email
      firstName
      lastName
      gender
      userId
      document
      documentType
      birthDate
      corporateName
      isCorporate
      corporateDocument
      corporateName
      phone
      homePhone
      businessPhone
      isNewsletterOptIn
      stateRegistration
    }
    pagination {
      page
      pageSize
      total
    }
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of storefront-permissions-ui/react/queries/ListRoles.gql from the storefront-permissions-ui VTEX app.

[Assistant]
Here's the implementation:

```graphql
query ListRoles {
  listRoles @context(provider: "vtex.storefront-permissions") {
    id
    name
    slug
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of storefront-permissions-ui/react/queries/getAppSettings.gql from the storefront-permissions-ui VTEX app.

[Assistant]
Here's the implementation:

```graphql
query GetAppSettings {
  getAppSettings @context(provider: "vtex.storefront-permissions") {
    adminSetup
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of storefront-permissions-ui/react/components/userEdit.tsx from the storefront-permissions-ui VTEX app.

[Assistant]
Here's the implementation:

```tsx
/* eslint-disable @typescript-eslint/no-explicit-any */
import type { FC } from 'react'
import React from 'react'
import { injectIntl } from 'react-intl'
import { useRuntime } from 'vtex.render-runtime'
import { StorefrontPermissions } from 'vtex.storefront-permissions'

const UserEdit: FC = () => {
  const { navigate, route } = useRuntime()

  const redirect = () => {
    navigate({
      page: 'admin.app.storefront-permissions.users-list',
      fetchPage: true,
    })
  }

  return (
    <>
      <StorefrontPermissions
        id={route.params.id}
        showName={true}
        showEmail={true}
        showCancel={true}
        onCancel={redirect}
        onSave={redirect}
      />
    </>
  )
}

export default injectIntl(UserEdit)

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of storefront-permissions-ui/react/components/rolesList.tsx from the storefront-permissions-ui VTEX app.

[Assistant]
Here's the implementation:

```tsx
/* eslint-disable @typescript-eslint/no-explicit-any */
import type { FC } from 'react'
import React, { useState } from 'react'
import type { WrappedComponentProps } from 'react-intl'
import { injectIntl, defineMessages } from 'react-intl'
import { useQuery } from 'react-apollo'
import { useRuntime } from 'vtex.render-runtime'
import { Table, Card } from 'vtex.styleguide'

import QUERY_LIST_ROLES from '../queries/ListRoles.gql'

const messages = defineMessages({
  name: {
    id: 'admin/storefront-permissions.tab.roles.name.label',
    defaultMessage: 'Name',
  },
  slug: {
    id: 'admin/storefront-permissions.tab.roles.category.label',
    defaultMessage: 'Category',
  },
  new: {
    id: 'admin/storefront-permissions.tab.new.button',
    defaultMessage: 'New',
  },
  delete: {
    id: 'admin/storefront-permissions.button.delete',
    defaultMessage: 'Delete',
  },
  helpTitle: {
    id: 'admin/storefront-permissions.help.title',
    defaultMessage: 'How it works',
  },
  helpText1: {
    id: 'admin/storefront-permissions.help.text1',
    defaultMessage: 'Role Names can be renamed, but categories are fixed.',
  },
  helpText2: {
    id: 'admin/storefront-permissions.help.text2',
    defaultMessage: 'There are three categories available:',
  },
  category1: {
    id: 'admin/storefront-permissions.help.category1',
    defaultMessage: 'Store Admin',
  },
  category1Text1: {
    id: 'admin/storefront-permissions.help.category1.text1',
    defaultMessage: 'Full access',
  },
  category2: {
    id: 'admin/storefront-permissions.help.category2',
    defaultMessage: 'Sales',
  },
  category2Text1: {
    id: 'admin/storefront-permissions.help.category2.text1',
    defaultMessage:
      'Can manage quotations, sales, and users from the its Organization.',
  },
  category2Text2: {
    id: 'admin/storefront-permissions.help.category2.text2',
    defaultMessage: 'It can have different access levels.',
  },
  category2Text3: {
    id: 'admin/storefront-permissions.help.category2.text3',
    defaultMessage: 'Can belong to one or many organizations.',
  },
  category2Text4: {
    id: 'admin/storefront-permissions.help.category2.text4',
    defaultMessage: 'Sales users can only manage Sales roles.',
  },
  category2Text5: {
    id: 'admin/storefront-permissions.help.category2.text5',
    defaultMessage:
      'Sales user hierarchy: sales-admin > sales-manager > sales-representative',
  },
  category3: {
    id: 'admin/storefront-permissions.help.category3',
    defaultMessage: 'Customers',
  },
  category3Text1: {
    id: 'admin/storefront-permissions.help.category3.text1',
    defaultMessage: 'Have access and can manage its own organization.',
  },
  category3Text2: {
    id: 'admin/storefront-permissions.help.category3.text2',
    defaultMessage:
      'Customers users can only manage users under the Customers roles.',
  },
  category3Text3: {
    id: 'admin/storefront-permissions.help.category3.text3',
    defaultMessage:
      'Customers users hierarchy: customer-amin > customer-approver > customer-buyer',
  },
})

const Roles: FC<any & WrappedComponentProps> = ({ intl }: any) => {
  const [state, setState] = useState({
    items: [],
  })

  const { loading, data } = useQuery(QUERY_LIST_ROLES)

  const { navigate, route } = useRuntime()

  const { params } = route

  const { items } = state

  const customSchema = {
    properties: {
      name: {
        title: intl.formatMessage(messages.name),
      },
      slug: {
        title: intl.formatMessage(messages.slug),
      },
    },
  }

  if (data?.listRoles && !state.items.length) {
    const newItems: any = data?.listRoles

    if (
      params?.id &&
      !newItems.filter((item: any) => {
        return item.id === params.id
      }).length
    ) {
      newItems.push(params)
    }

    setState({
      ...state,
      items: newItems,
    })
  }

  return (
    <div className="w-100 pt6">
      <Table
        fullWidth
        loading={loading}
        schema={customSchema}
        items={items}
        onRowClick={({ rowData: { id } }: any) => {
          navigate({
            page: 'admin.app.storefront-permissions.roles-edit',
            params: {
              id,
            },
          })
        }}
      />
      <div className="mt6">
        <Card>
          <h3>{intl.formatMessage(messages.helpTitle)}</h3>
          <p>{intl.formatMessage(messages.helpText1)}</p>
          <p>
            {intl.formatMessage(messages.helpText2)}
            <ul>
              <li>
                <strong>{intl.formatMessage(messages.category1)}</strong>:{' '}
                {intl.formatMessage(messages.category1Text1)}
              </li>
              <li>
                <strong>{intl.formatMessage(messages.category2)}</strong>:
                <ul>
                  <li>{intl.formatMessage(messages.category2Text1)}</li>
                  <li>{intl.formatMessage(messages.category2Text2)}</li>
                  <li>{intl.formatMessage(messages.category2Text3)}</li>
                  <li>{intl.formatMessage(messages.category2Text4)}</li>
                  <li>{intl.formatMessage(messages.category2Text5)}</li>
                </ul>
              </li>
              <li>
                <strong>{intl.formatMessage(messages.category3)}</strong>:
                <ul>
                  <li>{intl.formatMessage(messages.category3Text1)}</li>
                  <li>{intl.formatMessage(messages.category3Text2)}</li>
                  <li>{intl.formatMessage(messages.category3Text3)}</li>
                </ul>
              </li>
            </ul>
          </p>
        </Card>
      </div>
    </div>
  )
}

export default injectIntl(Roles)

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of storefront-permissions-ui/react/components/roleEdit.tsx from the storefront-permissions-ui VTEX app.

[Assistant]
Here's the implementation:

```tsx
/* eslint-disable @typescript-eslint/no-explicit-any */
import type { FC } from 'react'
import React, { useState } from 'react'
import type { WrappedComponentProps } from 'react-intl'
import { injectIntl, defineMessages } from 'react-intl'
import { useQuery, useMutation } from 'react-apollo'
import { useRuntime } from 'vtex.render-runtime'
import { Input, Button, Divider, Checkbox, Collapsible } from 'vtex.styleguide'

import GET_FEATURES from '../queries/ListFeatures.gql'
import SAVE_ROLE from '../mutations/saveRole.gql'
import GET_ROLE from '../queries/getRole.gql'

const messages = defineMessages({
  name: {
    id: 'admin/storefront-permissions.tab.roles.name.label',
    defaultMessage: 'Role',
  },
  required: {
    id: 'admin/storefront-permissions.required',
    defaultMessage: 'Required',
  },
  cancel: {
    id: 'admin/storefront-permissions.button.cancel',
    defaultMessage: 'Cancel',
  },
  save: {
    id: 'admin/storefront-permissions.button.save',
    defaultMessage: 'Save',
  },
  notFound: {
    id: 'admin/storefront-permissions.tab.roles.featuresNotFound',
    defaultMessage:
      "Could't find any apps connected to the Storefront Permissions",
  },
})

const RoleNew: FC<any & WrappedComponentProps> = ({ intl }: any) => {
  const { navigate, route } = useRuntime()
  const [state, setState] = useState<any>({
    id: null,
    name: null,
    slug: null,
    features: [],
    toggle: {},
  })

  const { id, name, features, toggle, slug } = state
  const {
    loading: loadingFeatures,
    data: dataFeatures,
    called: calledFeatures,
  } = useQuery(GET_FEATURES)

  const { loading } = useQuery(GET_ROLE, {
    skip: !route?.params?.id,
    variables: {
      id: route?.params?.id,
    },
    fetchPolicy: 'network-only',
    onCompleted: (res: any) => {
      setState({
        ...state,
        ...res.getRole,
      })
    },
  })

  const [saveRole, { loading: saveRoleLoading }] = useMutation(SAVE_ROLE, {
    onCompleted: (res: any) => {
      navigate({
        page: 'admin.app.storefront-permissions.roles-list',
        fetchPage: true,
        params: {
          id: res.saveRole.id,
          name,
        },
      })
    },
  })

  const handleSaveRole = () => {
    saveRole({
      variables: {
        id,
        name,
        slug,
        features,
      },
    })
  }

  const toggleApp = (app: string) => {
    const newToggle = toggle

    newToggle[app] = !toggle[app]
    setState({
      ...state,
      toggle: newToggle,
    })
  }

  const handleFeature = (module: string, feature: string) => {
    const newFeatures = features ?? []
    const hasModule = newFeatures.findIndex((item: any) => {
      return item.module === module
    })

    if (hasModule !== -1) {
      const hasFeature = newFeatures[hasModule].features.indexOf(feature)

      if (hasFeature !== -1) {
        newFeatures[hasModule].features.splice(hasFeature, 1)
      } else {
        newFeatures[hasModule].features.push(feature)
      }
    } else {
      newFeatures.push({
        module,
        features: [feature],
      })
    }

    setState({
      ...state,
      features: newFeatures,
    })
  }

  const checkChecked = (module: string, feature: string) => {
    if (features) {
      const hasModule = features.findIndex((item: any) => {
        return item.module === module
      })

      if (hasModule !== -1) {
        const hasFeature = features[hasModule].features.indexOf(feature)

        if (hasFeature !== -1) {
          return true
        }
      }
    }

    return false
  }

  return (
    <div className="w-100 pt6">
      <div className="mb5">
        <Input
          label={intl.formatMessage(messages.name)}
          value={name}
          errorMessage={
            name === '' ? intl.formatMessage(messages.required) : ''
          }
          disabled={loading || loadingFeatures || saveRoleLoading}
          onChange={(e: any) => {
            setState({ ...state, name: e.target.value })
          }}
        />
      </div>

      <div className="mb5">
        {!loadingFeatures &&
          calledFeatures &&
          !dataFeatures?.listFeatures.length && (
            <div>{intl.formatMessage(messages.notFound)}</div>
          )}

        {dataFeatures?.listFeatures?.map((app: any) => {
          return (
            <div key={`key-${app.module}`}>
              <Collapsible
                header={
                  <div className="pv2 hover-c-on-action-secondary">
                    {app.name}
                  </div>
                }
                onClick={() => {
                  toggleApp(app.module)
                }}
                isOpen={!!toggle[app.module]}
              >
                <div className="pb2">
                  {app.features.map((feature: any) => {
                    return (
                      <div
                        key={`option-${app.module}-${feature.value}`}
                        className="pv2"
                      >
                        <Checkbox
                          checked={checkChecked(app.module, feature.value)}
                          id={`option-${app.module}-${feature.value}`}
                          label={feature.label}
                          name="default-checkbox-group"
                          onChange={() => {
                            handleFeature(app.module, feature.value)
                          }}
                          value={feature.value}
                        />
                      </div>
                    )
                  })}
                </div>
              </Collapsible>
              <div className="mv2">
                <Divider orientation="horizontal" />
              </div>
            </div>
          )
        })}
      </div>

      <div className="mv4 flex justify-between">
        <Button
          variation="tertiary"
          disabled={loadingFeatures || saveRoleLoading}
          collapseLeft
          onClick={() => {
            navigate({ page: 'admin.app.storefront-permissions.roles-list' })
          }}
        >
          {intl.formatMessage(messages.cancel)}
        </Button>
        <Button
          variation="primary"
          disabled={
            loading || loadingFeatures || saveRoleLoading || !state.name
          }
          collapseRight
          onClick={() => {
            handleSaveRole()
          }}
        >
          {intl.formatMessage(messages.save)}
        </Button>
      </div>
    </div>
  )
}

export default injectIntl(RoleNew)

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of storefront-permissions-ui/react/components/roleNew.tsx from the storefront-permissions-ui VTEX app.

[Assistant]
Here's the implementation:

```tsx
/* eslint-disable @typescript-eslint/no-explicit-any */
import type { FC } from 'react'
import React, { useState } from 'react'
import type { WrappedComponentProps } from 'react-intl'
import { injectIntl, defineMessages } from 'react-intl'
import { useQuery, useMutation } from 'react-apollo'
import { useRuntime } from 'vtex.render-runtime'
import { Input, Button, Divider, Checkbox, Collapsible } from 'vtex.styleguide'

import GET_FEATURES from '../queries/ListFeatures.gql'
import SAVE_ROLE from '../mutations/saveRole.gql'

const messages = defineMessages({
  name: {
    id: 'admin/storefront-permissions.tab.roles.name.label',
    defaultMessage: 'Role',
  },
  required: {
    id: 'admin/storefront-permissions.required',
    defaultMessage: 'Required',
  },
  cancel: {
    id: 'admin/storefront-permissions.button.cancel',
    defaultMessage: 'Cancel',
  },
  save: {
    id: 'admin/storefront-permissions.button.save',
    defaultMessage: 'Save',
  },
  notFound: {
    id: 'admin/storefront-permissions.tab.roles.featuresNotFound',
    defaultMessage:
      "Could't find any apps connected to the Storefront Permissions",
  },
})

const RoleNew: FC<any & WrappedComponentProps> = ({ intl }) => {
  const { navigate } = useRuntime()
  const [state, setState] = useState<any>({
    name: null,
    features: [],
    toggle: {},
  })

  const { name, toggle, features } = state
  const {
    loading: loadingFeatures,
    data: dataFeatures,
    called: calledFeatures,
  } = useQuery(GET_FEATURES)

  const [saveRole, { loading: saveRoleLoading }] = useMutation(SAVE_ROLE, {
    onCompleted: (res: any) => {
      navigate({
        page: 'admin.app.storefront-permissions.roles-list',
        fetchPage: true,
        params: {
          id: res.saveRole.id,
          name,
        },
      })
    },
  })

  const handleSaveRole = () => {
    saveRole({
      variables: {
        name,
        features,
      },
    })
  }

  const toggleApp = (app: string) => {
    const newToggle = toggle

    newToggle[app] = !toggle[app]
    setState({
      ...state,
      toggle: newToggle,
    })
  }

  const handleFeature = (module: string, feature: string) => {
    const newFeatures = features
    const hasModule = newFeatures.findIndex((item: any) => {
      return item.module === module
    })

    if (hasModule !== -1) {
      const hasFeature = newFeatures[hasModule].features.indexOf(feature)

      if (hasFeature !== -1) {
        newFeatures[hasModule].features.splice(hasFeature, 1)
      } else {
        newFeatures[hasModule].features.push(feature)
      }
    } else {
      newFeatures.push({
        module,
        features: [feature],
      })
    }

    setState({
      ...state,
      features: newFeatures,
    })
  }

  const checkChecked = (module: string, feature: string) => {
    const hasModule = features.findIndex((item: any) => {
      return item.module === module
    })

    if (hasModule !== -1) {
      const hasFeature = features[hasModule].features.indexOf(feature)

      if (hasFeature !== -1) {
        return true
      }
    }

    return false
  }

  return (
    <div className="w-100 pt6">
      <div className="mb5">
        <Input
          label={intl.formatMessage(messages.name)}
          value={name}
          errorMessage={
            name === '' ? intl.formatMessage(messages.required) : ''
          }
          disabled={loadingFeatures || saveRoleLoading}
          onChange={(e: any) => {
            setState({ ...state, name: e.target.value })
          }}
        />
      </div>

      <div className="mb5">
        {!loadingFeatures &&
          calledFeatures &&
          !dataFeatures.listFeatures.length && (
            <div>{intl.formatMessage(messages.notFound)}</div>
          )}

        {dataFeatures?.listFeatures?.map((app: any) => {
          return (
            <div key={`key-${app.module}`}>
              <Collapsible
                header={
                  <div className="pv2 hover-c-on-action-secondary">
                    {app.name}
                  </div>
                }
                onClick={() => {
                  toggleApp(app.module)
                }}
                isOpen={!!toggle[app.module]}
              >
                <div className="pb2">
                  {app.features.map((feature: any) => {
                    return (
                      <div
                        key={`option-${app.module}-${feature.value}`}
                        className="pv2"
                      >
                        <Checkbox
                          checked={checkChecked(app.module, feature.value)}
                          id={`option-${app.module}-${feature.value}`}
                          label={feature.label}
                          name="default-checkbox-group"
                          onChange={() => {
                            handleFeature(app.module, feature.value)
                          }}
                          value={feature.value}
                        />
                      </div>
                    )
                  })}
                </div>
              </Collapsible>
              <div className="mv2">
                <Divider orientation="horizontal" />
              </div>
            </div>
          )
        })}
      </div>

      <div className="mv4 flex justify-between">
        <Button
          variation="tertiary"
          disabled={loadingFeatures || saveRoleLoading}
          collapseLeft
          onClick={() => {
            navigate({ page: 'admin.app.storefront-permissions.roles-list' })
          }}
        >
          {intl.formatMessage(messages.cancel)}
        </Button>
        <Button
          variation="primary"
          disabled={loadingFeatures || saveRoleLoading || !state.name}
          collapseRight
          onClick={() => {
            handleSaveRole()
          }}
        >
          {intl.formatMessage(messages.save)}
        </Button>
      </div>
    </div>
  )
}

export default injectIntl(RoleNew)

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of storefront-permissions-ui/react/components/userNew.tsx from the storefront-permissions-ui VTEX app.

[Assistant]
Here's the implementation:

```tsx
/* eslint-disable @typescript-eslint/no-explicit-any */
import type { FC } from 'react'
import React, { useState } from 'react'
import type { WrappedComponentProps } from 'react-intl'
import { injectIntl, defineMessages } from 'react-intl'
import { useQuery, useMutation, useLazyQuery } from 'react-apollo'
import { useRuntime } from 'vtex.render-runtime'
import { Button, Dropdown, Toggle, AutocompleteInput } from 'vtex.styleguide'

import GET_ROLES from '../queries/ListRoles.gql'
import SEARCH_USERS from '../queries/SearchUsers.gql'
import SAVE_USER from '../mutations/saveUser.gql'

const messages = defineMessages({
  role: {
    id: 'admin/storefront-permissions.tab.roles.name.label',
    defaultMessage: 'Role',
  },
  canImpersonate: {
    id: 'admin/storefront-permissions.tab.users.canImpersonate.label',
    defaultMessage: 'Can impersonate',
  },
  name: {
    id: 'admin/storefront-permissions.tab.users.name.label',
    defaultMessage: 'Name',
  },
  email: {
    id: 'admin/storefront-permissions.tab.users.email.label',
    defaultMessage: 'Email',
  },
  required: {
    id: 'admin/storefront-permissions.required',
    defaultMessage: 'Required',
  },
  cancel: {
    id: 'admin/storefront-permissions.button.cancel',
    defaultMessage: 'Cancel',
  },
  save: {
    id: 'admin/storefront-permissions.button.save',
    defaultMessage: 'Save',
  },
})

let _timeout: any = null

const UserNew: FC<any & WrappedComponentProps> = ({ intl }) => {
  const { navigate } = useRuntime()
  const [state, setState] = useState<any>({
    userId: null,
    roleId: null,
    name: null,
    email: null,
    canImpersonate: false,
  })

  const [term, setTerm] = useState('')

  const { loading: loadingRoles, data: dataRoles } = useQuery(GET_ROLES)
  const [searchUsers, { loading, data: usersData }] = useLazyQuery(SEARCH_USERS)
  const [saveUser, { loading: saveUserLoading }] = useMutation(SAVE_USER, {
    onCompleted: (res: any) => {
      navigate({
        page: 'admin.app.storefront-permissions.users-list',
        fetchPage: true,
        params: {
          ...state,
          id: res.saveUser.id,
        },
      })
    },
  })

  const handleSaveUser = () => {
    saveUser({
      variables: {
        userId: state.userId,
        roleId: state.roleId,
        name: state.name,
        email: state.email,
        canImpersonate: state.canImpersonate,
      },
    })
  }

  const options = {
    onSelect: (args: any) => {
      const selectedUser = usersData.users.data.find((user: any) => {
        return user.userId === args.value
      })

      const { userId, firstName, lastName, email } = selectedUser

      setState({
        ...state,
        userId,
        name: `${firstName} ${lastName}`,
        email,
      })
    },
    loading,
    value: usersData?.users?.data?.length
      ? usersData.users.data.map((user: any) => {
          return {
            value: user.userId,
            label: `${user.firstName} ${user.lastName}<${user.email}>`,
          }
        })
      : [],
    // --- This is what makes the Last Searched Terms work!
    // This can be stored anywhere the dev wants. To be persistent, for example.
  }

  const input = {
    onChange: (value: any) => {
      setTerm(value)
      clearTimeout(_timeout)
      _timeout = setTimeout(() => {
        searchUsers({
          variables: {
            perPage: 10,
            pageNumber: 1,
            filter: `(email=*${term}* OR firstName=*${term}* OR lastName=*${term}*)`,
          },
        })
      }, 1000)
    },
    onClear: () => {
      setTerm('')
      setState({
        ...state,
        userId: null,
        name: null,
        email: null,
      })
    },
    placeholder: 'Search user... (e.g.: Ana)',
    value: term,
  }

  return (
    <div className="w-100 pt6">
      <div className="mb5">
        <AutocompleteInput input={input} options={options} />
      </div>
      {!!state.userId && (
        <>
          <div className="mb5">{state.name}</div>

          <div className="mb5">{state.email}</div>
          <div className="mb5">
            <Dropdown
              label={intl.formatMessage(messages.role)}
              disabled={loadingRoles || saveUserLoading}
              options={
                dataRoles?.listRoles?.map((role: any) => {
                  return {
                    value: role.id,
                    label: role.name,
                  }
                }) ?? []
              }
              value={state.roleId}
              onChange={(_: any, v: any) => {
                setState({ ...state, roleId: v })
              }}
            />
          </div>

          <div className="mb5">
            <Toggle
              label={intl.formatMessage(messages.canImpersonate)}
              size="large"
              disabled={loadingRoles || saveUserLoading}
              checked={state.canImpersonate}
              onChange={() => {
                setState({ ...state, canImpersonate: !state.canImpersonate })
              }}
            />
          </div>
        </>
      )}

      <div className="mv4 flex justify-between">
        <Button
          variation="tertiary"
          disabled={loadingRoles || saveUserLoading}
          collapseLeft
          onClick={() => {
            navigate({ page: 'admin.app.storefront-permissions.users-list' })
          }}
        >
          {intl.formatMessage(messages.cancel)}
        </Button>
        {!!state.userId && (
          <Button
            variation="primary"
            disabled={loadingRoles || saveUserLoading}
            collapseRight
            onClick={() => {
              handleSaveUser()
            }}
          >
            {intl.formatMessage(messages.save)}
          </Button>
        )}
      </div>
    </div>
  )
}

export default injectIntl(UserNew)

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of storefront-permissions-ui/react/components/usersList.tsx from the storefront-permissions-ui VTEX app.

[Assistant]
Here's the implementation:

```tsx
/* eslint-disable @typescript-eslint/no-explicit-any */
import type { FC } from 'react'
import React, { useState } from 'react'
import type { WrappedComponentProps } from 'react-intl'
import { injectIntl, defineMessages } from 'react-intl'
import { useQuery, useMutation } from 'react-apollo'
import { Table } from 'vtex.styleguide'
import { useRuntime } from 'vtex.render-runtime'

import QUERY_LIST_USERS from '../queries/ListUsers.gql'
import DELETE_USER from '../mutations/deleteUser.gql'

let deleteId: any = null
const messages = defineMessages({
  email: {
    id: 'admin/storefront-permissions.tab.users.email.label',
    defaultMessage: 'Email',
  },
  name: {
    id: 'admin/storefront-permissions.tab.users.name.label',
    defaultMessage: 'Name',
  },
  new: {
    id: 'admin/storefront-permissions.tab.new.button',
    defaultMessage: 'New',
  },
  delete: {
    id: 'admin/storefront-permissions.button.delete',
    defaultMessage: 'Delete',
  },
})

const Users: FC<any & WrappedComponentProps> = ({ intl }: any) => {
  const [state, setState] = useState({
    items: [],
  })

  const { loading, data } = useQuery(QUERY_LIST_USERS, {
    fetchPolicy: 'network-only',
  })

  const [deleteUser, { loading: deleteLoading }] = useMutation(DELETE_USER, {
    onCompleted: () => {
      setState({
        ...state,
        items: state.items.filter((item: any) => {
          return item.id !== deleteId
        }),
      })
      deleteId = null
    },
  })

  const { navigate, route } = useRuntime()

  navigate({
    page: 'admin.app.storefront-permissions.roles-list',
  })

  const { params } = route

  const { items } = state

  const customSchema = {
    properties: {
      name: {
        title: intl.formatMessage(messages.name),
      },
      email: {
        title: intl.formatMessage(messages.email),
      },
    },
  }

  if (data?.listUsers && !state.items.length) {
    const newItems: any = data?.listUsers

    if (
      params?.id &&
      !newItems.filter((item: any) => {
        return item.id === params.id
      }).length
    ) {
      newItems.push(params)
    }

    if (newItems.length) {
      setState({
        ...state,
        items: newItems,
      })
    }
  }

  const lineActions = [
    {
      label: ({ rowData }: any) =>
        `${intl.formatMessage(messages.delete)} ${rowData.name}`,
      isDangerous: true,
      onClick: ({ rowData }: any) => {
        deleteId = rowData.id
        deleteUser({
          variables: {
            id: rowData.id,
            email: rowData.email,
            userId: rowData.userId,
          },
        })
      },
    },
  ]

  return (
    <div className="w-100 pt6">
      <Table
        fullWidth
        loading={loading || deleteLoading}
        schema={customSchema}
        items={items}
        lineActions={lineActions}
        onRowClick={({ rowData: { clId: id } }: any) => {
          navigate({
            page: 'admin.app.storefront-permissions.users-edit',
            params: {
              id,
            },
          })
        }}
        toolbar={{
          newLine: {
            label: intl.formatMessage(messages.new),
            handleCallback: () => {
              navigate({ page: 'admin.app.storefront-permissions.users-new' })
            },
          },
        }}
      />
    </div>
  )
}

export default injectIntl(Users)

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of formula-graphql/docs/README.md from the formula-graphql VTEX app.

[Assistant]
Here's the implementation:

```markdown
# vtex.formula-graphql

GraphQL schema and resolvers for the VTEX Formula Hackathon Manager.

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of formula-graphql/graphql/schema.graphql from the formula-graphql VTEX app.

[Assistant]
Here's the implementation:

```graphql
type Project {
  demoURL: String
  description: String
  edition: String
  id: String
  name: String
  owner: String
  team: [ProfileData]
}

type ProfileData {
  name: String
  email: String
  picture: String
  id: String
}

type Vote {
  projectID: String
  userID: String
  execution: Int
  relevance: Int
}

type Score {
  projectID: String
  score: Int
}

enum FormulaStatus {
  REGISTRATION
  RUNNING
  VOTING
  RESULTS
}

type InfoData {
  status: FormulaStatus
  timeRemainingSeconds: Int
  timeTotalSeconds: Int
}

type Query {
  project(
    edition: String!
    id: String!
  ): Project @cacheControl(scope: PRIVATE)

  projects(
    edition: String!
  ): [Project] @cacheControl(scope: PRIVATE)

  profile: ProfileData @cacheControl(scope: PRIVATE)

  votes(
    edition: String!
  ): [Vote] @cacheControl(scope: PRIVATE)

  scores(
    edition: String!
  ): [Score] @cacheControl(scope: PRIVATE)

  info(
    edition: String!
  ): InfoData @cacheControl(scope: PRIVATE)

  finalists(
    edition: String!
  ): [Project] @cacheControl(scope: PRIVATE)
}

type Mutation {
  updateVote(
    edition: String
    projectID: String
    execution: Int
    relevance: Int
  ): Vote

  updateProject(
    edition: String!
    id: String
    demoURL: String
    description: String
    name: String
  ): Project

  deleteProject(
    edition: String!
    id: String!
  ): Boolean

  joinProject(
    edition: String!
    id: String!
  ): Project

  leaveProject(
    edition: String!
    id: String!
  ): Project

  updateScores(
    edition: String!
  ): [Score]
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of formula-graphql/node/errors.ts from the formula-graphql VTEX app.

[Assistant]
Here's the implementation:

```typescript
import {
  AuthenticationError as ApolloAuthenticationError,
  ForbiddenError as ApolloForbiddenError
} from 'apollo-server-errors'

export class AuthenticationError extends ApolloAuthenticationError {
  constructor() {
    super('User is not authenticated')
  }
}

// tslint:disable-next-line
export class NotVTEXUserError extends ApolloForbiddenError {
  constructor() {
    super('VTEX Formula is only available to VTEX employees - sorry!')
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of formula-graphql/node/service.json from the formula-graphql VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "stack": "nodejs",
  "memory": 512,
  "ttl": 30,
  "timeout": 10,
  "minReplicas": 1,
  "maxReplicas": 1
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of formula-graphql/node/typings.d.ts from the formula-graphql VTEX app.

[Assistant]
Here's the implementation:

```typescript
import {IOContext as ClientsContext} from '@vtex/api'
import {Context} from 'koa'
import {Dictionary} from 'ramda'

import Resources from './resources/index'

declare global {
  type LogLevel = 'info' | 'error' | 'warn' | 'debug'

  interface LicenseManagerUser {
    IsPersisted: boolean,
    IsRemoved: boolean,
    UserId: string,
    Login: string,
    Name: string,
    IsAdmin: boolean,
    IsReliable: boolean,
    IsBlocked: boolean
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of formula-graphql/node/package.json from the formula-graphql VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "name": "vtex.formula-graphql",
  "version": "0.2.3",
  "dependencies": {
    "@vtex/api": "1.0.0",
    "apollo-server-errors": "^2.0.2",
    "ramda": "^0.25.0",
    "uuid": "^3.3.2"
  },
  "devDependencies": {
    "@types/koa": "^2.0.39",
    "@types/mime-types": "^2.1.0",
    "@types/node": "^7.0.8",
    "@types/ramda": "^0.25.38",
    "tslint": "^5.8.0",
    "tslint-config-vtex": "^2.0.0",
    "typescript": "^3.0.3"
  },
  "scripts": {
    "lint": "tsc --noEmit && tslint -c tslint.json './**/*.ts'"
  },
  "license": "UNLICENSED"
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of formula-graphql/node/globals.ts from the formula-graphql VTEX app.

[Assistant]
Here's the implementation:

```typescript
import {MetricsAccumulator, ServiceContext} from '@vtex/api'
import Resources from './resources'

global.metrics = new MetricsAccumulator()

declare global {
  const metrics: MetricsAccumulator

  type Resolver<A, R, P = any> = (parent: P, args: A, ctx: ResolverContext, info: any) => Promise<R>

  interface ProfileData {
    name: string
    email: string
    picture: string | null
    id: string
  }

  interface ResolverContext extends ServiceContext {
    resources: Resources
    profile: ProfileData
  }

  namespace NodeJS {
    interface Global {
      metrics: MetricsAccumulator
    }
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of formula-graphql/node/index.ts from the formula-graphql VTEX app.

[Assistant]
Here's the implementation:

```typescript
import './globals'

import {ServiceContext} from '@vtex/api'
import {mapObjIndexed} from 'ramda'

import {infoQueries} from './resolvers/info'
import {profileQueries} from './resolvers/profile'
import {projectsMutations, projectsQueries} from './resolvers/projects'
import {scoreMutations, scoreQueries} from './resolvers/scores'
import {votesMutations, votesQueries} from './resolvers/votes'
import Resources from './resources'

const log = (
  {vtex: {account, workspace}}: ServiceContext,
  resolver: string,
  [sec, ns],
  err?: Error,
) => [
  new Date().toISOString(),
  err ? '❌' : '✅',
  `${account}/${workspace}`,
  resolver,
  Math.trunc(sec * 1e3 + ns / 1e6) + 'ms',
].join('   ')

const statusLabel = (e?: Error) =>
  e ? `error${e.name ? `-${e.name}` : ''}` : 'success'

const prepare = <A, R, P>(resolver: Resolver<A, R, P>) => async (parent: P, args: A, ctx: ResolverContext, info: any) => {
  if (!resolver.name) {
    throw new Error('Resolvers must be named')
  }

  let err
  const start = process.hrtime()
  ctx.resources = new Resources(ctx)

  try {
    ctx.profile = await ctx.resources.profile.getProfile()
    return resolver(parent, args, ctx, info)
  } catch (e) {
    err = e
    console.error(e)
    ctx.resources.logger.error(e, {resolver: resolver.name})
    throw e
  } finally {
    const end = process.hrtime(start)
    console.log(log(ctx, resolver.name, end, err))
    metrics.batchHrTimeMetricFromEnd(`resolver-${resolver.name}-${statusLabel(err)}`, end, ctx.vtex.production)
  }
}

export default {
  graphql: {
    resolvers: {
      Mutation: mapObjIndexed(prepare, {
        ...projectsMutations,
        ...votesMutations,
        ...scoreMutations,
      }),
      Query: mapObjIndexed(prepare, {
        ...infoQueries,
        ...profileQueries,
        ...projectsQueries,
        ...votesQueries,
        ...scoreQueries,
      }),
    },
  },
  statusTrack: metrics.statusTrack,
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of formula-graphql/node/resources/projects.ts from the formula-graphql VTEX app.

[Assistant]
Here's the implementation:

```typescript
import {Logger, VBase} from '@vtex/api'
import {ForbiddenError, UserInputError} from 'apollo-server-errors'
import {pluck} from 'ramda'
import uuidv4 from 'uuid/v4'

export interface Project {
  demoURL?: string
  description?: string
  edition?: string
  id?: string
  name?: string
  owner?: string
  team?: ProfileData[]
}

export default class Projects {
  constructor (
    public ctx: ResolverContext,
    public vbase: VBase,
    public logger: Logger,
  ) {}

  public find = async (edition: string, id: string): Promise<Project> => {
    const project = await this.vbase.getJSON<Project>(encodeURIComponent(edition), `${id}.json`, true)
    return project
  }

  public list = async (edition: string): Promise<Project[]> => {
    const projectIds = await this.vbase.listFiles(encodeURIComponent(edition))
    const paths = pluck('path', projectIds.data).map(p => p.replace('.json', ''))
    const projects = await Promise.all(paths.map(p => this.find(edition, p)))
    return projects
  }

  public update = async (edition: string, id?: string, project?: Partial<Project>) => {
    const email = this.ctx.profile.email
    await this.logger.info({edition, id, project, action: 'update', email})

    // Create new project
    if (!id) {
      const generated = uuidv4()
      const newProject = {
        ...project,
        edition,
        id: generated,
        owner: email,
        team: [this.ctx.profile],
      }

      await this.vbase.saveJSON(encodeURIComponent(edition), `${generated}.json`, newProject)
      return newProject
    }

    if (!project || !project.owner || !project.id) {
      throw new UserInputError('Project is invalid.')
    }

    await this.vbase.saveJSON(encodeURIComponent(edition), `${id}.json`, project)

    return project
  }

  public delete = async (edition: string, id: string) => {
    const savedProject = await this.find(encodeURIComponent(edition), id)
    const email = this.ctx.profile.email

    if (!savedProject) {
      return
    }

    if (savedProject.owner !== email) {
      throw new ForbiddenError(`Project ${id} is owned by ${savedProject.owner} and cannot be deleted by ${email}`)
    }

    await this.logger.info({edition, id, action: 'delete', email})
    await this.vbase.deleteFile(encodeURIComponent(edition), `${id}.json`)
    return true
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of formula-graphql/node/resources/scores.ts from the formula-graphql VTEX app.

[Assistant]
Here's the implementation:

```typescript
import {Logger, VBase} from '@vtex/api'
import {compose, equals, findIndex, keysIn, map, mapObjIndexed, mergeWith, reduce, slice, sort, sum, values} from 'ramda'

import Votes, {UserVotes} from './votes'

const SCORE_RECEIVING_PROJECTS = 3

interface ScoresByProject {
  [projectID: string]: number
}

export interface ScoreData {
  projectID: string
  score: number
}

export default class Score {
  private static file = 'scores.json'
  private static getBucket = (edition: string) => `${edition}-score`

  private static calculateUserScore(votes: UserVotes): ScoresByProject {
    const summedVotes = mapObjIndexed(compose(sum,values), votes)
    const favouriteProjects = slice(0, SCORE_RECEIVING_PROJECTS, sort((a,b) => summedVotes[b] - summedVotes[a], keysIn(summedVotes)))
    return mapObjIndexed((_, projectID) => {
      const position = findIndex(equals(projectID), favouriteProjects)
      return position === -1 ? 0 : SCORE_RECEIVING_PROJECTS - position
    }, votes)
  }

  constructor (
    public ctx: ResolverContext,
    public vbase: VBase,
    public logger: Logger,
  ) {}

  public updateScores = async (edition: string): Promise<ScoreData[]> => {
    const voteFiles = await this.vbase.listFiles(Votes.getBucket(edition))
    const votes = await Promise.all(map(
      voteFile => this.vbase.getJSON<UserVotes>(Votes.getBucket(edition), voteFile.path, true) || {},
      voteFiles.data
    ))
    const scoresByUser = map(Score.calculateUserScore, votes)
    const scores: ScoresByProject = reduce(mergeWith((a,b) => a+b), {}, scoresByUser)
    const scoreData = values(mapObjIndexed((score, projectID) => ({score,projectID}), scores))
    const sortedScoreData = sort((a,b) => b.score - a.score, scoreData)

    await this.vbase.saveJSON(Score.getBucket(edition), Score.file, sortedScoreData)
    return sortedScoreData
  }

  public getScores = async (edition: string): Promise<ScoreData[]> => {
    const scoreData = await this.vbase.getJSON<ScoreData[]>(Score.getBucket(edition), Score.file, true) || []
    return scoreData
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of formula-graphql/node/resources/votes.ts from the formula-graphql VTEX app.

[Assistant]
Here's the implementation:

```typescript
import {Logger, VBase} from '@vtex/api'

export interface Vote {
  execution: number
  relevance: number
}

export interface UserVotes {
  [projectID: string]: Vote
}

export default class Votes {
  public static getBucket = (edition: string) => `${edition}-votes`
  public static getUserFile = (id: string) => `${id}.json`

  constructor (
    public ctx: ResolverContext,
    public vbase: VBase,
    public logger: Logger,
  ) {}

  public getVotes = async (edition: string) => {
    const { id } = this.ctx.profile
    const votes = await this.vbase.getJSON<UserVotes>(Votes.getBucket(edition), Votes.getUserFile(id), true) || {}
    return votes
  }

  public setVote = async (edition: string, projectID: string, vote: Vote) => {
    const { id } = this.ctx.profile
    const votes = await this.getVotes(edition)
    votes[projectID] = vote
    await this.vbase.saveJSON<UserVotes>(Votes.getBucket(edition), Votes.getUserFile(id), votes)
    return vote
  }
}

```