npm package: @stacknvault/iex2-core
Description: Package to manage the IEX2 context
Author: Andoni Arostegui
Latest version: 3.0.38

# IEX2 applications



IEX2 allows developers use the technology they want to generate expose templates to send real estate items to a given contact.

Developers can leverage the IEX2 components and the *expose* script to develop and publish expose templates.

# The iex2-core library
This dependency must be included on the `package.json` file:
`@stacknvault/iex2-core`

# Structure of the expose
An expose has *stages*. They represents the lifecycle that the customer goes through when buying real estate. Some sensitive information may be hidden until certain stage and some *sections* will be also available after certain stage.

A *section* is a block that is presented to the user. Examples:

- Provisioning Contract Agreement
- A Hero Banner
- An image carousel
- ...

#Public folder
The engine expects two files to exist on the public directory:

- assets/context/config.json

- assets/context/context.json

## How to work with *config.json* file?
The *config.json* file has a *context* block. It fills it with information retrieved from the backend. This is the block that will feed the *context.json* file with fields named same as the fields under the *context* section.
For example:
*config.json*:
```
{
	"context": {
		"foo": {...},
		"bar": {...},
		"baz": {...}
		"config": {...}
		"stage": {...}
}
```
produces the following *context.json*:
```
{
	"foo": {...},
	"bar": {...},
	"baz": {...}
	"config": {...}
	"stage": {...}
}
```
## Types of blocks supported by the *config.json* file
- string
- integer
- ff-svc
- flowdsl
- literal
- json-expression
- array-map

### string
```
"expose_title": {
	"type": "string",
	"value": "{{{context.estate.headline}}}"
},
```
produces:
```
"expose_title":"Great Villa by the sea"
```
### integer
```
"stage": {
	"type": "integer",
	"value": "{{{stage}}}"
},
```
produces:
```
"stage": 0
```
### ff-svc
It pulls data from a Flowfact service.
Example:
```
"estate": {
	"type": "ff-svc",
	"url": "/entity-service/entities/{{{estateId}}}",
	"method": "get"
}
```
produces:
```
"estate":{
    "id":"0f484937-6a1c-4202-81b0-18210179d4df",
    "parking":{
      "values":[
        "4"
      ]
    },
    "3_leerstandspreis":{
      "values":[
        86543
      ]
    },
	...
```
### flowdsl
This is a special case to pull data from the search service
Example:
```
"contacts": {
	"schema": "estates",
	"query": {
  "target": "ENTITY",
  "fetch": ["headline", "description"],
  "conditions": [
        {
            "type": "HASFIELDWITHVALUE",
            "field": "status",
            "value": "active"
        },
        {
            "type": "HASFIELDWITHVALUE",
            "field": "purchaseprice",
            "value": 50000,
            "operator": "GREATER"
        }
  ],
  "sorts": [
        {
            "field": "_metadata.createdTimestamp",
            "direction": "ASC"
        }
   ]
},
	"offset": 1,
	"size": 2
}
```
produces:
```
{
  "entries": [
    {
      "id": "5afcc1cb-4ce4-4954-8234-105b67e50dc8",
      "headline": {
        "values": [
          "Musterdaten - Großzügig geschnittenenes Haus mitten im Wald"
        ]
      },
      "_acps": [
        {
          "groupId": "3febc943-e718-436c-b717-239bfff76b78",
          "accessLevel": 7
        }
      ],
      ...
        {
          "id": "d837832a-61a8-4dd3-8de4-823a99b0cfee",
          "uri": "companies/companies/d837832a-61a8-4dd3-8de4-823a99b0cfee",
          "type": "company",
          "relation": "creator"
        }
      ]
    },
    {
      "id": "4658de53-8f34-4f78-b41e-d3b2724157e5",
      "headline": {
        "values": [
          "Musterdaten - Apartments in Berlin "
        ]
      },
        {
          "id": "d837832a-61a8-4dd3-8de4-823a99b0cfee",
          "uri": "companies/companies/d837832a-61a8-4dd3-8de4-823a99b0cfee",
          "type": "company",
          "relation": "creator"
        }
		...
      "_lock": {
        "identifier": false
      },
      "_optionalConditions": {
        "totalCount": 0,
        "matchingCount": 0
      }
    }
  ],
  "totalCount": 7,
  "page": 1,
  "pageSize": 2,
  "offset": 0,
  "size": 2
}
```
### literal
It's a literal expression.
Example:
```
"foo": {
	"type": "literal",
	"obj": {
		"bar": "baz"
	}
}
```
produces
```
"foo": {
	"bar": "baz"
}
```
### json-expression
This evaluates a json expression.
Example:
```
"estateContacts": {
	"type": "json-expression",
	"expression": "context.estate.contact"
}
```
produces:
```
"estateContacts":{
      "values":[
        "ce85ca05-9bab-4094-97e2-e029bb688c22",
        "c33ae329-8af3-4a73-b85b-b6ecc1e65697"
      ]
    }
```
###  array-map
It iterates within an array and can nest other block inside. The *from* parameter accepts a json query to produce the element array to map. The *to* element produces the target mapped array
Example:
```
"estateContacts": {
	"type": "array-map",
	"from": ".context.estate.contact.values",
	"to": {
		"type": "ff-svc",
		"url": "/entity-service/entities/<%=from=%>",
		"method": "get"
	}
},
```
produces:
```
"estateContact": [
{
    "id":"ce85ca05-9bab-4094-97e2-e029bb688c22",
    "emails":{
      "values":[
        "kate.sheleh@flowfact.onmicrosoft.com"
      ]
    },
    ...
    ]
  },
  {
    "id": "dc508439-4016-444e-9311-a52b41841b88",
    "lastName": {
      "values": [
        "Krebs"
      ]
    },
   ...
  }
]
```

To represent an integer value
This file includes the configuration of the fields that are available for the *estate* at whatever stage. *Includes* and/or *excludes* can be used:
## including / excluding values
Values can be incuded or excluded depending on the stage
An example:

```
"estate": {
	"type": "ff-svc",
	"url": "/entity-service/entities/{{{estateId}}}",
	"method": "get",
	"includes": [
		{"name": "flowfact_geolocation", "fromStage": 1},
		{"name": "addresses", "fromStage": 1},
		{"name": "latitude", "fromStage": 1},
		{"name": "longitude", "fromStage": 1},

		{"name": "id"},
		{"name": "AnnualNetColdRentActualCommercial"},
		{"name": "AnnualNetColdRentActualOthers"},
		{"name": "AnnualNetColdRentActualResidential"},
		...
	]
}
```
- The fields listed for the fromStage 1 won't be available until that stage. The others will be always available. No field outside that list will be available at all.
- If *excludes* are used, all fields but the axcluded ones are available
- If no *includes* or *excludes* are used, all fields will be available
Feel free to use *includes* or *excludes* to whitelist or blacklist fields.

## Field mappings
There are many occassions where data is not standardized on the CRM for the different entities. This results in gaps between different companies / editions when it comes to name the same entity field. For example, `estate.textEstate`may be referenced like `estate.estateDescription` depending on the edition or the customer.

To aaddress this issue, the expose supports field mappings ike on this example:
config.json:
```
{
	...
	"fieldMapping": {
        "estate": [
            ["textEstate", "estateDescription"],
            ["textFree", "freeDescription"],
            ["textEnvironment", "equipmentDescription"],
            ["textLocation", "locationDescription"]
        ]
    }
}
```

# Stages
When Stage objects are used, like on the example below, the framework controls when to show them depending on the stage:

```
...
<Stage  level="0">
	<ContractAgreement
	theme={theme}
	contracts={ffmap`company.legislationTexts`}
	imgObj={ffmap`estate.mainImage`}/>
</Stage>
<Stage  level="1">
	<WhateverOtherSection/>
	...
</Stage>
```
Functions like *ffmap* help access the data from the context.

## src folder
`assets` - the folder static information that helps to develop the application. For example, e.g. pictures or general
styles

`components` - the folder with all components used in the application. It consists of 3 folders: `common`, `sections`
, `stages`
- `common` folder contains components that can be reused in different sections, e.g. a component 'ImageCarousel' is used in sections "Gallery" and "FloorPlans".
- `sections` folder contains all sections. Here we call the components frm 'common' and create new components for specific sections.
- `stages` folder consist of 2 folders: `StageZero` and `StageOne`. Here we list all sections (components) we use for stages 0 and stage 1.

`helpers` - the folder with general functions / data that help to develop the app

`hooks` - the folder with custom hooks

`Expose.jsx` file - start point of the app

`Expose.scss` and `index.scss` - general styles for the app

### multilanguage support
The exposé supports multilanguage by exposing the *language* and *setLanguage* items on the context.
context.json:
```
{
	"estate": {
		...
		"textEstate": {
			"values": [
				"Schönes Anwesen in den Bergen"
			]
		},
		"textEstateEN": {
			"values": [
				"Nice estate in the mountains"
			]
		}
	}
	...
}
```

Expose source code:
```
import {ContextStore, ffmap} from '@stacknvault/iex2-core'
...
const {language, setLanguage, iex} = useContext(ContextStore);
setLanguage('en');
...
return (
	<div>{ffmap`estate.textEstate`}</div> /* displays it in english*/
);
```
Multilanguage features can combine with field mappings, so that if for example `estate.description` is mapped to `estate.my_custom_filed_for_description` and the context contains `estate.my_custom_filed_for_descriptionES`, ffmap  `estate.description` would show the value of `estate.my_custom_filed_for_descriptionES` if the language is `es`
# Styles

1. We use `CSS modules` and preprocessor Sass with extension `scss`. Read more about __CSS Module__ here:
    - https://github.com/css-modules/css-modules
    - https://create-react-app.dev/docs/adding-a-css-modules-stylesheet/

   Read mode about __Sass__ here:
    - https://sass-lang.com/documentation

2. `Inline styles.`<br>
   Use it when you need to pass a dynamic value that can be changed depending on the state of the application or company
   settings.<br>
   For example, if you want to use a company accent color, you need to get it from __getBaseTheme()__ function (*
   location: /src/assets/styles/IEXTheme*)
   Code example:
```
    const theme = getBaseTheme()
    <h2 style={{color: theme.brand.colors.primary}}>
```
# Publish a template

Environments:

- production
- staging
- development

Templates that are published `locally` are available only to the user to whom the used access key belongs to.
Templates that are published `globally` are visible to every Flowfact user in the corresponding environment. You need special rights for that.

### Development (locally)

1. Create a file `.env.development` with following content: `export TOKEN=access-key`
2. To generate token, it is necessary to create an access key in Flowfact system (CRM): `profile / settings / API access / create access`
3. From the root of the project run the script `publishForDevLocal.sh`

### Development (globally)

1. Create a file `.env.development` with following content: `export TOKEN=access-key`
2. To generate token, it is necessary to create an access key in Flowfact system (CRM): `profile / settings / API access / create access`
3. From the root of the project run the script `publishForDevGlobal.sh`

### Production (locally)

1. Create a file `.env.production` with following content: *export TOKEN=access-key*
2. To generate token, it is necessary to create an access key in Flowfact system (CRM): `profile / settings / API access / create access`
3. From the root of the project run the script *publishForProdLocal.sh*

### Production (globally)

1. Create a file `.env.production` with following content: *export TOKEN=access-key*
2. To generate token, it is necessary to create an access key in Flowfact system (CRM): `profile / settings / API access / create access`
3. From the root of the project run the script *publishForProdGlobal.sh*

### Staging (globally)

1. Create a file `.env.staging` with following content: *export TOKEN=access-key*
2. To generate token, it is necessary to create an access key in Flowfact system (CRM): `profile / settings / API access / create access`
3. From the root of the project run the script *publishForStagingGlobal.sh*


# The _expose_ script

### Setting up the environment
The following environment variables need to be set:

- __TOKEN:__ It's taken from Flowfact, from the *profile / settings / API access / create access*. <br/>__Example__:
  export TOKEN = your-token-num<br/><br/>
- __REACT_APP_STAGE:__ *development* | *staging* | *production*. <br/>__Example__: export
  REACT_APP_STAGE=development<br/><br/>
- __PUBLIC_URL__: Must be set to "." due to the way the exposes are going to be consumed by the consumer. <br/>__
  Example__: export PUBLIC_URL=.


### Show help menu `yarn expose` or `yarn expose help`

__terminal output:__
  ```
  USAGE:

npm run expose <publish|delete|list|render|set-stage|get-context>  --<option name>=<option value>

For help, run:

npm run expose help

or

npm run expose <command> help
  ```

### Publish a template without using prepared scripts

Steps:

1. Setup environment with *REACT_APP_STAGE* command
2. Setup your token with *TOKEN* command
3. Setup start point of the app with *PUBLIC_URL* command
4. Build and publish the app with *yarn build && yarn expose -- publish --template-id={TEMPLATE-ID-NUM}
   --name={TEMPLATE-NAME}*
5. __NOTE:__ if you want to publish a template globally you should to add *--global* to the command from point 4.

`yarn expose publish help`

__terminal output:__

```
Publishes a template.

USAGE:

npm run expose publish  [ --template-id=<your own template id> ] [ --name="<the name of the template>"] [--global]

If no other args are specified, they will be taken from .lastRun. The only flag not taken from .lastRun will be --global
```

### Delete a template

Steps:

1. Setup environment with *REACT_APP_STAGE* command
2. Setup your token with *TOKEN* command
3. Setup start point of the app with *PUBLIC_URL* command
4. You can delete a template by template-id *--template-id={TEMPLTAE-ID}* or by name *--name={TEMPLATE-NAME}*.<br/>
   Example *yarn expose delete --template-id={TEMPLATE-ID-NUM}*

`yarn expose delete help`

__terminal output:__

```
Deletes a template.

USAGE:

npm run expose delete  [ --template-id=<your own template id> ] [--global]

If no other args are specified, they will be taken from .lastRun. The only flag not taken from .lastRun will be --global
```

### List all templates

Steps:

1. Setup environment with *REACT_APP_STAGE* command
2. Setup your token with *TOKEN* command
3. Setup start point of the app with *PUBLIC_URL* command
4. The command *yarn expose list* will list all local templates. To list global templates just add *--global* to the
   command.

`yarn expose list help`

__terminal output:__

```
Lists templates.

USAGE:

npm run expose list [--global]

If no other args are specified, they will be taken from .lastRun. The only flag not taken from .lastRun will be --global
```
### Command *yarn run expose render help*

__terminal output:__
```
Renders a template for a given contact-id, estate-id and optional company-id.

USAGE:
npm run expose render [--render-id=<your own render id> ] --template-id=<template id> --contact-id=<contact id> --estate-id=<estate id> [ --company-id=<company id> ]

If no other args are specified, they will be taken from .lastRun. The only flag not taken from .lastRun will be --global

If company id is not specified, it will be taken from the sender

A custom render-id can be used.
```

### Command *yarn run expose set-stage help*

__terminal output:__
```
Sets the stage of a rendered template

USAGE:

npm run expose set-stage --render-id=<render id> --stage=<stage number starting from 0>

If no other args than the stage are specified, they will be taken from .lastRun. The only flag not taken from .lastRun will be --global
```

### Command *yarn run expose get-context help*

__terminal output:__
```
Gets the context for a given contact-id, estate-id and optional company-id and it writes it to public/assets/context/context.json.

USAGE:

npm run expose get-context --contact-id=<contact id> --estate-id=<estate id> --stage=<stage number starting from 0> [ --company-id=<company id> ]

If no other args than the stage are specified, they will be taken from .lastRun. The only flag not taken from .lastRun will be --global

If company id is not specified, it will be taken from the sender
```