From 2630a0b1e634b6fbf3c9f2f6f1faf14448455ec7 Mon Sep 17 00:00:00 2001
From: Duong Pham <duongph@reapit.com>
Date: Fri, 20 Mar 2020 22:53:57 +0700
Subject: [PATCH] chore: #629 migrate to config.json for fts-definitions &
 scaffolder

Changes
- Migrate to config.json for ts definitions
- Migrate to config.json for react scaffolder
---
 .../foundation-ts-definition-cronjob.yml      |   2 +-
 .github/workflows/pull-request-ci.yml         |   6 +-
 .github/workflows/release-develop.yml         |  13 +-
 .github/workflows/release-production.yml      |   6 +-
 package.json                                  |   3 +-
 .../src/utils/generate-config-ts-def.js       |   2 +-
 .../config.example.json                       |   5 +
 .../foundations-ts-definitions/package.json   |   7 +-
 .../scripts/constants.js                      |   7 +
 .../scripts}/create-index-file.js             |   0
 .../scripts}/fetch-definition.js              |   0
 .../scripts}/fetch-marketplace-definition.js  |   4 +-
 .../scripts}/fetch-platform-definition.js     |   4 +-
 .../scripts}/format-code.js                   |   2 +-
 .../scripts}/get-current-time-stamp-string.js |   0
 .../scripts}/handle-cronjob.js                |   5 +-
 .../scripts}/publish-time-stamp-tag.js        |   0
 .../scripts}/release-github.js                |   7 +-
 .../scripts}/release-npm.js                   |   2 +-
 .../scripts}/update-npm.js                    |   2 +-
 .../types/platform-schema.ts                  | 602 ++++++++++++++++++
 .../app/templates/_config.example.json        |   6 +
 .../app/templates/_jest.config.js             |   2 +-
 .../app/templates/_package.json               |   3 +-
 .../{index.tsx.snap => app.tsx.snap}          |   0
 .../src/core/__tests__/{index.tsx => app.tsx} |   2 +-
 .../apollo/src/core/private-route-wrapper.tsx |   2 +-
 .../templates/apollo/src/graphql/client.ts    |   4 +-
 .../templates/apollo/src/hooks/use-auth.ts    |   4 +-
 .../templates/apollo/src/types/global.d.ts    |  15 +
 .../app/templates/azure/_azure-pipelines.yml  |  42 --
 .../base-is-not-foundation/reapit.config.json |  15 -
 .../src/scripts/webpack-dev.js                |   1 +
 .../base-is-sass/src/scripts/webpack-dev.js   |   1 +
 .../app/templates/base/src/core/app.tsx       |  76 +++
 .../app/templates/base/src/core/index.tsx     | 124 ++--
 .../templates/base/src/scripts/jest-setup.js  |  12 +
 .../no-redux/src/components/pages/login.tsx   |   4 +-
 .../src/core/__tests__/app.tsx}               |   2 +-
 .../src/core/private-route-wrapper.tsx        |   2 +-
 .../templates/no-redux/src/hooks/use-auth.ts  |   4 +-
 .../templates/no-redux/src/types/global.d.ts  |  14 +
 .../redux/src/components/pages/login.tsx      |   2 +-
 .../src/core/__tests__/app.tsx}               |   2 +-
 .../redux/src/core/private-route-wrapper.tsx  |   2 +-
 .../redux/src/sagas/__tests__/auth.ts         |   2 +-
 .../app/templates/redux/src/sagas/auth.ts     |   2 +-
 .../app/templates/redux/src/types/global.d.ts |  15 +
 .../foundations-ts-definitions/constants.js   |   7 -
 49 files changed, 854 insertions(+), 192 deletions(-)
 create mode 100644 packages/foundations-ts-definitions/config.example.json
 create mode 100644 packages/foundations-ts-definitions/scripts/constants.js
 rename {scripts/foundations-ts-definitions => packages/foundations-ts-definitions/scripts}/create-index-file.js (100%)
 rename {scripts/foundations-ts-definitions => packages/foundations-ts-definitions/scripts}/fetch-definition.js (100%)
 rename {scripts/foundations-ts-definitions => packages/foundations-ts-definitions/scripts}/fetch-marketplace-definition.js (91%)
 rename {scripts/foundations-ts-definitions => packages/foundations-ts-definitions/scripts}/fetch-platform-definition.js (92%)
 rename {scripts/foundations-ts-definitions => packages/foundations-ts-definitions/scripts}/format-code.js (76%)
 rename {scripts/foundations-ts-definitions => packages/foundations-ts-definitions/scripts}/get-current-time-stamp-string.js (100%)
 rename {scripts/foundations-ts-definitions => packages/foundations-ts-definitions/scripts}/handle-cronjob.js (83%)
 rename {scripts/foundations-ts-definitions => packages/foundations-ts-definitions/scripts}/publish-time-stamp-tag.js (100%)
 rename {scripts/foundations-ts-definitions => packages/foundations-ts-definitions/scripts}/release-github.js (96%)
 rename {scripts/foundations-ts-definitions => packages/foundations-ts-definitions/scripts}/release-npm.js (95%)
 rename {scripts/foundations-ts-definitions => packages/foundations-ts-definitions/scripts}/update-npm.js (95%)
 create mode 100644 packages/react-app-scaffolder/app/templates/_config.example.json
 rename packages/react-app-scaffolder/app/templates/apollo/src/core/__tests__/__snapshots__/{index.tsx.snap => app.tsx.snap} (100%)
 rename packages/react-app-scaffolder/app/templates/apollo/src/core/__tests__/{index.tsx => app.tsx} (94%)
 create mode 100644 packages/react-app-scaffolder/app/templates/apollo/src/types/global.d.ts
 delete mode 100644 packages/react-app-scaffolder/app/templates/azure/_azure-pipelines.yml
 delete mode 100644 packages/react-app-scaffolder/app/templates/base-is-not-foundation/reapit.config.json
 create mode 100644 packages/react-app-scaffolder/app/templates/base/src/core/app.tsx
 rename packages/react-app-scaffolder/app/templates/{redux/src/core/__tests__/index.tsx => no-redux/src/core/__tests__/app.tsx} (94%)
 create mode 100644 packages/react-app-scaffolder/app/templates/no-redux/src/types/global.d.ts
 rename packages/react-app-scaffolder/app/templates/{no-redux/src/core/__tests__/index.tsx => redux/src/core/__tests__/app.tsx} (94%)
 create mode 100644 packages/react-app-scaffolder/app/templates/redux/src/types/global.d.ts
 delete mode 100644 scripts/foundations-ts-definitions/constants.js

diff --git a/.github/workflows/foundation-ts-definition-cronjob.yml b/.github/workflows/foundation-ts-definition-cronjob.yml
index 958772c26a..ea671d0ca8 100644
--- a/.github/workflows/foundation-ts-definition-cronjob.yml
+++ b/.github/workflows/foundation-ts-definition-cronjob.yml
@@ -36,7 +36,7 @@ jobs:
 
       - name: Fetch config
         run: |
-          yarn fetch-config DEV
+          yarn fetch-config production
 
       - name: Fetch latest definition
         run: yarn workspace @reapit/foundations-ts-definitions fetch-definition
diff --git a/.github/workflows/pull-request-ci.yml b/.github/workflows/pull-request-ci.yml
index 928ed60997..bf3899419c 100644
--- a/.github/workflows/pull-request-ci.yml
+++ b/.github/workflows/pull-request-ci.yml
@@ -92,11 +92,7 @@ jobs:
 
       - name: Fetch config
         run: |
-          yarn fetch-config DEV
-
-      - name: Fetch config1
-        run: |
-          yarn fetch-config1 development
+          yarn fetch-config development
 
       - name: Build
         run: |
diff --git a/.github/workflows/release-develop.yml b/.github/workflows/release-develop.yml
index fe60d1a250..c4c47bc26b 100644
--- a/.github/workflows/release-develop.yml
+++ b/.github/workflows/release-develop.yml
@@ -41,27 +41,31 @@ jobs:
       - name: Install Global Dependencies
         run: |
           yarn global add aws-cli serverless@1.59.3
+
       - name: Set up workspace experimental
         run: |
           yarn config set workspaces-experimental true
+
       - name: Install dependencies
         run: |
           yarn
+
       - name: Lint
         run: |
           yarn lint
+
       - name: Fetch config
         run: |
-          yarn fetch-config DEV
-      - name: Fetch config1
-        run: |
-          yarn fetch-config1 development
+          yarn fetch-config development
+
       - name: Build
         run: |
           yarn build
+
       - name: Test
         run: |
           yarn test
+
       - name: Release Develop
         run: |
           yarn release:dev
@@ -74,6 +78,7 @@ jobs:
           key: ${{ runner.os }}-jest-cache-${{ hashFiles('yarn.lock') }}
           restore-keys: |
             ${{ runner.os }}-jest-cache-
+
       - name: Write marketplace build cache
         uses: actions/cache@v1
         with:
diff --git a/.github/workflows/release-production.yml b/.github/workflows/release-production.yml
index 96ae0a147e..8e7f08c060 100644
--- a/.github/workflows/release-production.yml
+++ b/.github/workflows/release-production.yml
@@ -65,11 +65,7 @@ jobs:
 
       - name: Fetch config
         run: |
-          yarn fetch-config PROD
-
-      - name: Fetch config1
-        run: |
-          yarn fetch-config1 production
+          yarn fetch-config production
 
       - name: Build
         run: |
diff --git a/package.json b/package.json
index 95bb0a46c3..cf9e032332 100644
--- a/package.json
+++ b/package.json
@@ -18,8 +18,7 @@
   },
   "scripts": {
     "build": "lerna run build:prod --stream",
-    "fetch-config": "yarn config-manager getSecret reapit-marketplace-app-config",
-    "fetch-config1": "lerna run --parallel --stream fetch-config --",
+    "fetch-config": "lerna run --parallel --stream fetch-config --",
     "lint": "lerna run --parallel --stream lint",
     "lint:fix": "lerna run --parallel --stream lint:fix --",
     "release:dev": "lerna run --parallel release:dev",
diff --git a/packages/config-manager/src/utils/generate-config-ts-def.js b/packages/config-manager/src/utils/generate-config-ts-def.js
index c4280935e8..dd0ce42791 100644
--- a/packages/config-manager/src/utils/generate-config-ts-def.js
+++ b/packages/config-manager/src/utils/generate-config-ts-def.js
@@ -1,7 +1,7 @@
 const { json2ts } = require('json-ts')
 const fs = require('fs')
 const { REAPIT_TS_DEF_IN_CWD_PATH, REAPIT_TS_DEF_NAME } = require('../paths')
-const formatCode = require('../../../../scripts/foundations-ts-definitions/format-code')
+const formatCode = require('../../../foundations-ts-definitions/scripts/format-code')
 
 exports.generateConfigTsDef = config => {
   try {
diff --git a/packages/foundations-ts-definitions/config.example.json b/packages/foundations-ts-definitions/config.example.json
new file mode 100644
index 0000000000..ec3391552f
--- /dev/null
+++ b/packages/foundations-ts-definitions/config.example.json
@@ -0,0 +1,5 @@
+{
+  "MARKETPLACE_API_BASE_URL": "",
+  "MARKETPLACE_API_KEY": "",
+  "PLATFORM_API_BASE_URL": ""
+}
diff --git a/packages/foundations-ts-definitions/package.json b/packages/foundations-ts-definitions/package.json
index fac0a8d2b3..76d664b206 100644
--- a/packages/foundations-ts-definitions/package.json
+++ b/packages/foundations-ts-definitions/package.json
@@ -7,9 +7,10 @@
   ],
   "main": "./types/index.ts",
   "scripts": {
-    "fetch-definition": "node '../../scripts/foundations-ts-definitions/fetch-definition.js'",
-    "handle-cron-job": "node '../../scripts/foundations-ts-definitions/handle-cronjob.js'",
-    "release:prod": "node ../../scripts/foundations-ts-definitions/release-npm.js foundations-ts-definitions",
+    "fetch-config": "yarn config-manager fetchConfig foundations-ts-definitions",
+    "fetch-definition": "yarn fetch-config && node './scripts/fetch-definition.js'",
+    "handle-cron-job": "node './scripts/handle-cronjob.js'",
+    "release:prod": "node ./scripts/release-npm.js foundations-ts-definitions",
     "test:update-badges": "echo 'not implmented'"
   },
   "lint-staged": {
diff --git a/packages/foundations-ts-definitions/scripts/constants.js b/packages/foundations-ts-definitions/scripts/constants.js
new file mode 100644
index 0000000000..68c03c13ee
--- /dev/null
+++ b/packages/foundations-ts-definitions/scripts/constants.js
@@ -0,0 +1,7 @@
+const path = require('path')
+
+module.exports = {
+  FOUNDATION_ROOT_FOLDER: path.resolve(__dirname, '..'),
+  FOUNDATION_TYPES_FOLDER: path.resolve(__dirname, '../types'),
+  PACKAGE_NAME: 'foundations-ts-definitions',
+}
diff --git a/scripts/foundations-ts-definitions/create-index-file.js b/packages/foundations-ts-definitions/scripts/create-index-file.js
similarity index 100%
rename from scripts/foundations-ts-definitions/create-index-file.js
rename to packages/foundations-ts-definitions/scripts/create-index-file.js
diff --git a/scripts/foundations-ts-definitions/fetch-definition.js b/packages/foundations-ts-definitions/scripts/fetch-definition.js
similarity index 100%
rename from scripts/foundations-ts-definitions/fetch-definition.js
rename to packages/foundations-ts-definitions/scripts/fetch-definition.js
diff --git a/scripts/foundations-ts-definitions/fetch-marketplace-definition.js b/packages/foundations-ts-definitions/scripts/fetch-marketplace-definition.js
similarity index 91%
rename from scripts/foundations-ts-definitions/fetch-marketplace-definition.js
rename to packages/foundations-ts-definitions/scripts/fetch-marketplace-definition.js
index f30950a3d9..cafab2436f 100644
--- a/scripts/foundations-ts-definitions/fetch-marketplace-definition.js
+++ b/packages/foundations-ts-definitions/scripts/fetch-marketplace-definition.js
@@ -6,9 +6,7 @@ const sw2dts = require('sw2dts')
 const prettifyCode = require('./format-code')
 
 const { FOUNDATION_TYPES_FOLDER } = require('./constants')
-const config = require(path.resolve(__dirname, '../../reapit-config.json'))
-const configDev = config['DEV']
-const { MARKETPLACE_API_BASE_URL, MARKETPLACE_API_KEY } = configDev
+const { MARKETPLACE_API_BASE_URL, MARKETPLACE_API_KEY } = require(path.resolve(__dirname, '..', 'config.json'))
 
 // Fetch definitions for a given schema
 const fetchDefinitionsForSchema = async schemaConfig => {
diff --git a/scripts/foundations-ts-definitions/fetch-platform-definition.js b/packages/foundations-ts-definitions/scripts/fetch-platform-definition.js
similarity index 92%
rename from scripts/foundations-ts-definitions/fetch-platform-definition.js
rename to packages/foundations-ts-definitions/scripts/fetch-platform-definition.js
index 25cc70429b..f8cb9f1d2a 100644
--- a/scripts/foundations-ts-definitions/fetch-platform-definition.js
+++ b/packages/foundations-ts-definitions/scripts/fetch-platform-definition.js
@@ -6,9 +6,7 @@ const sw2dts = require('sw2dts')
 const prettifyCode = require('./format-code')
 
 const { FOUNDATION_TYPES_FOLDER } = require('./constants')
-const config = require(path.resolve(__dirname, '../../reapit-config.json'))
-const configDev = config['DEV']
-const { PLATFORM_API_BASE_URL } = configDev
+const { PLATFORM_API_BASE_URL } = require(path.resolve(__dirname, '..', 'config.json'))
 
 // Fetch definitions for a given schema
 const fetchDefinitionsForSchema = async schemaConfig => {
diff --git a/scripts/foundations-ts-definitions/format-code.js b/packages/foundations-ts-definitions/scripts/format-code.js
similarity index 76%
rename from scripts/foundations-ts-definitions/format-code.js
rename to packages/foundations-ts-definitions/scripts/format-code.js
index e663969d45..3ab229b8c0 100644
--- a/scripts/foundations-ts-definitions/format-code.js
+++ b/packages/foundations-ts-definitions/scripts/format-code.js
@@ -1,5 +1,5 @@
 const prettier = require('prettier')
-const prettierBaseConfig = require('../../.prettierrc.js')
+const prettierBaseConfig = require('../../../.prettierrc.js')
 
 module.exports = content => {
   const formatContent = prettier.format(content, {
diff --git a/scripts/foundations-ts-definitions/get-current-time-stamp-string.js b/packages/foundations-ts-definitions/scripts/get-current-time-stamp-string.js
similarity index 100%
rename from scripts/foundations-ts-definitions/get-current-time-stamp-string.js
rename to packages/foundations-ts-definitions/scripts/get-current-time-stamp-string.js
diff --git a/scripts/foundations-ts-definitions/handle-cronjob.js b/packages/foundations-ts-definitions/scripts/handle-cronjob.js
similarity index 83%
rename from scripts/foundations-ts-definitions/handle-cronjob.js
rename to packages/foundations-ts-definitions/scripts/handle-cronjob.js
index 97335081df..df48ac9762 100644
--- a/scripts/foundations-ts-definitions/handle-cronjob.js
+++ b/packages/foundations-ts-definitions/scripts/handle-cronjob.js
@@ -1,8 +1,9 @@
-const { runCommand } = require('../../scripts/release/utils')
-const gitStatus = runCommand('git', ['status', '-s'])
+const { runCommand } = require('../../../scripts/release/utils')
 const updateNPM = require('./update-npm')
 const releaseGithub = require('./release-github')
 
+const gitStatus = runCommand('git', ['status', '-s'])
+
 console.log(`Cronjob executed at ${new Date().toDateString()} - ${new Date().toTimeString()}`)
 
 if (!gitStatus) {
diff --git a/scripts/foundations-ts-definitions/publish-time-stamp-tag.js b/packages/foundations-ts-definitions/scripts/publish-time-stamp-tag.js
similarity index 100%
rename from scripts/foundations-ts-definitions/publish-time-stamp-tag.js
rename to packages/foundations-ts-definitions/scripts/publish-time-stamp-tag.js
diff --git a/scripts/foundations-ts-definitions/release-github.js b/packages/foundations-ts-definitions/scripts/release-github.js
similarity index 96%
rename from scripts/foundations-ts-definitions/release-github.js
rename to packages/foundations-ts-definitions/scripts/release-github.js
index 159649ede5..4a97d78501 100644
--- a/scripts/foundations-ts-definitions/release-github.js
+++ b/packages/foundations-ts-definitions/scripts/release-github.js
@@ -1,12 +1,13 @@
 const Octokit = require('@octokit/rest')
-const getCurrentTimeStamp = require('./get-current-time-stamp-string')
 const fs = require('fs')
 const path = require('path')
+const getCurrentTimeStamp = require('./get-current-time-stamp-string')
 const { FOUNDATION_ROOT_FOLDER } = require('./constants')
-const { GITHUB_TOKEN } = process.env
-const { runCommand } = require('../release/utils')
+const { runCommand } = require('../../../scripts/release/utils')
 const { execSync } = require('child_process')
 
+const { GITHUB_TOKEN } = process.env
+
 module.exports = async () => {
   try {
     runCommand('git', ['remote', 'add', 'sshOrigin', `git@github.com:${process.env.GITHUB_REPOSITORY}.git`])
diff --git a/scripts/foundations-ts-definitions/release-npm.js b/packages/foundations-ts-definitions/scripts/release-npm.js
similarity index 95%
rename from scripts/foundations-ts-definitions/release-npm.js
rename to packages/foundations-ts-definitions/scripts/release-npm.js
index 9d37c12c9a..982e16650e 100644
--- a/scripts/foundations-ts-definitions/release-npm.js
+++ b/packages/foundations-ts-definitions/scripts/release-npm.js
@@ -1,5 +1,5 @@
 const { execSync } = require('child_process')
-const { getPreviousTag, editReleaseNote, getVersionTag } = require('../release/utils')
+const { getPreviousTag, editReleaseNote, getVersionTag } = require('../../../scripts/release/utils')
 const publishTimeStampTag = require('./publish-time-stamp-tag')
 const { FOUNDATION_ROOT_FOLDER } = require('./constants')
 
diff --git a/scripts/foundations-ts-definitions/update-npm.js b/packages/foundations-ts-definitions/scripts/update-npm.js
similarity index 95%
rename from scripts/foundations-ts-definitions/update-npm.js
rename to packages/foundations-ts-definitions/scripts/update-npm.js
index e76d39e975..f7f4448294 100644
--- a/scripts/foundations-ts-definitions/update-npm.js
+++ b/packages/foundations-ts-definitions/scripts/update-npm.js
@@ -1,5 +1,5 @@
 const semver = require('semver')
-const { runCommand } = require('../release/utils')
+const { runCommand } = require('../../../scripts/release/utils')
 const fs = require('fs')
 const path = require('path')
 const { FOUNDATION_ROOT_FOLDER, PACKAGE_NAME } = require('./constants')
diff --git a/packages/foundations-ts-definitions/types/platform-schema.ts b/packages/foundations-ts-definitions/types/platform-schema.ts
index cf1e0eaae2..949215fc02 100644
--- a/packages/foundations-ts-definitions/types/platform-schema.ts
+++ b/packages/foundations-ts-definitions/types/platform-schema.ts
@@ -2323,6 +2323,143 @@ export interface CreateDocumentModel {
    */
   fileData?: string
 }
+/**
+ * Request body used to create a enquiries address
+ */
+export interface CreateEnquiryAddressModel {
+  /**
+   * Sets the building name
+   */
+  buildingName?: string
+  /**
+   * Sets the building number
+   */
+  buildingNumber?: string
+  /**
+   * Sets the first line of the address
+   */
+  line1?: string
+  /**
+   * Sets the second line of the address
+   */
+  line2?: string
+  /**
+   * Sets the third line of the address
+   */
+  line3?: string
+  /**
+   * Sets the fourth line of the address
+   */
+  line4?: string
+  /**
+   * Sets the postcode
+   */
+  postcode?: string
+  /**
+   * Sets the ISO-3166 country code that the address resides within
+   */
+  countryId?: string
+}
+/**
+ * Request body used to create an enquiry
+ * example:
+ * [object Object]
+ */
+export interface CreateEnquiryModel {
+  /**
+   * The title of the individual making the enquiry
+   */
+  title?: string
+  /**
+   * The forename of the individual making the enquiry
+   */
+  forename?: string
+  /**
+   * The surname of the individual making the enquiry
+   */
+  surname?: string
+  /**
+   * The selling position of the individual making the enquiry (renting/instructedThisAgent/instructedOtherAgent/privateSale/notOnMarket)
+   */
+  position?: string
+  /**
+   * The type of enquiry. Enquiries can created for applicants interested in buying/renting, as well as prospective vendors and landlords (salesApplicant/lettingsApplicant/salesProperty/lettingsProperty)
+   */
+  enquiryType?: string
+  /**
+   * Textual information about the nature of the enquiry - usually the message text from the individual making the enquiry
+   */
+  message?: string
+  /**
+   * The unique identifier of the related office
+   */
+  officeId?: string
+  /**
+   * The marketing consent status of the individual making the enquiry (grant/deny/notAsked)
+   */
+  marketingConsent?: string
+  /**
+   * The name of the source that the enquiry was generated from
+   */
+  sourceName?: string
+  /**
+   * The home phone number of the individual making the enquiry
+   */
+  homePhone?: string
+  /**
+   * The work phone number of the individual making the enquiry
+   */
+  workPhone?: string
+  /**
+   * The mobile phone number of the individual making the enquiry
+   */
+  mobilePhone?: string
+  /**
+   * The email of the individual making the enquiry
+   */
+  email?: string
+  /**
+   * The address of the individual making the enquiry
+   */
+  address?: {
+    /**
+     * Sets the building name
+     */
+    buildingName?: string
+    /**
+     * Sets the building number
+     */
+    buildingNumber?: string
+    /**
+     * Sets the first line of the address
+     */
+    line1?: string
+    /**
+     * Sets the second line of the address
+     */
+    line2?: string
+    /**
+     * Sets the third line of the address
+     */
+    line3?: string
+    /**
+     * Sets the fourth line of the address
+     */
+    line4?: string
+    /**
+     * Sets the postcode
+     */
+    postcode?: string
+    /**
+     * Sets the ISO-3166 country code that the address resides within
+     */
+    countryId?: string
+  }
+  /**
+   * A list of unique property identifiers that the enquiry relates to
+   */
+  propertyIds?: string[]
+}
 /**
  * Request body used to create a new contact identity check
  * example:
@@ -2435,6 +2572,29 @@ export interface CreateIdentityDocumentModel {
    */
   name?: string
 }
+/**
+ * Request body to create a journal entry
+ * example:
+ * [object Object]
+ */
+export interface CreateJournalEntryModel {
+  /**
+   * The unique identifier of the property the journal entry is related to. Can additionally be associated to another type
+   */
+  propertyId?: string
+  /**
+   * The entity type the journal entry has been raised against (applicant/contact/company/landlord/tenancy)
+   */
+  associatedType?: string
+  /**
+   * The unique identifier of the entity the journal entry has been raised against. Can additionally be associated to a property
+   */
+  associatedId?: string
+  /**
+   * The textual description of the journal entry event
+   */
+  description?: string
+}
 /**
  * Request body used to create a new relationship between a landlord and a contact or company
  */
@@ -2991,6 +3151,14 @@ export interface CreatePropertyModel {
    * The arrangements regarding viewing the property
    */
   viewingArrangements?: string
+  /**
+   * The url of a video associated with this property, such as a virtual tour
+   */
+  videoUrl?: string
+  /**
+   * The caption for the video url associated with this property
+   */
+  videoCaption?: string
   /**
    * Details of the EPC statistics
    */
@@ -3613,6 +3781,177 @@ export interface Documents {
   )[]
   typeId?: string[]
 }
+export interface Enquiries {
+  pageSize?: number
+  pageNumber?: number
+  sortBy?: string
+  enquiryType?: string
+  createdFrom?: string
+  createdTo?: string
+}
+/**
+ * Representation of the physical address of a building or premise
+ */
+export interface EnquiryAddressModel {
+  /**
+   * The building name
+   */
+  buildingName?: string
+  /**
+   * The building number
+   */
+  buildingNumber?: string
+  /**
+   * The first line of the address
+   */
+  line1?: string
+  /**
+   * The second line of the address
+   */
+  line2?: string
+  /**
+   * The third line of the address
+   */
+  line3?: string
+  /**
+   * The fourth line of the address
+   */
+  line4?: string
+  /**
+   * The postcode
+   */
+  postcode?: string
+  /**
+   * The ISO-3166 country code that the address resides within
+   */
+  countryId?: string
+}
+/**
+ * Representation of an enquiry
+ */
+export interface EnquiryModel {
+  /**
+   * The unique identifier of the enquiry
+   */
+  id?: number // int32
+  /**
+   * The date and time when the enquiry was created
+   * example:
+   * 2019-08-14T12:30:02.0000000Z
+   */
+  created?: string // date-time
+  /**
+   * The date and time when the enquiry was last modified
+   * example:
+   * 2019-08-14T12:30:02.0000000Z
+   */
+  modified?: string // date-time
+  /**
+   * The title of the individual making the enquiry
+   */
+  title?: string
+  /**
+   * The forename of the individual making the enquiry
+   */
+  forename?: string
+  /**
+   * The surname of the individual making the enquiry
+   */
+  surname?: string
+  /**
+   * The type of enquiry. Enquiries can created for applicants interested in buying/renting, as well as prospective vendors and landlords (salesApplicant/lettingsApplicant/salesProperty/lettingsProperty)
+   */
+  enquiryType?: string
+  /**
+   * Textual information about the nature of the enquiry - usually the message text from the individual making the enquiry
+   */
+  message?: string
+  /**
+   * The status of the enquiry (pending/added/rejected/alreadyExists/duplicateEntry/spam)
+   */
+  status?: string
+  /**
+   * The marketing consent status of the individual making the enquiry (grant/deny/notAsked)
+   */
+  marketingConsent?: string
+  /**
+   * The selling position of the individual making the enquiry (renting/instructedThisAgent/instructedOtherAgent/privateSale/notOnMarket)
+   */
+  position?: string
+  /**
+   * The unique identifier of the office related to the enquiry
+   */
+  officeId?: string
+  /**
+   * The name of the source that the enquiry was generated by
+   */
+  sourceName?: string
+  /**
+   * The home phone number of the individual making the enquiry
+   */
+  homePhone?: string
+  /**
+   * The work phone number of the individual making the enquiry
+   */
+  workPhone?: string
+  /**
+   * The mobile phone number of the individual making the enquiry
+   */
+  mobilePhone?: string
+  /**
+   * The email of the individual making the enquiry
+   */
+  email?: string
+  /**
+   * The address of the individual making the enquiry
+   */
+  address?: {
+    /**
+     * The building name
+     */
+    buildingName?: string
+    /**
+     * The building number
+     */
+    buildingNumber?: string
+    /**
+     * The first line of the address
+     */
+    line1?: string
+    /**
+     * The second line of the address
+     */
+    line2?: string
+    /**
+     * The third line of the address
+     */
+    line3?: string
+    /**
+     * The fourth line of the address
+     */
+    line4?: string
+    /**
+     * The postcode
+     */
+    postcode?: string
+    /**
+     * The ISO-3166 country code that the address resides within
+     */
+    countryId?: string
+  }
+  /**
+   * A list of unique property identifiers that this enquiry relates to
+   */
+  propertyIds?: string[]
+  readonly _links?: {
+    [name: string]: {
+      href?: string
+    }
+  }
+  readonly _embedded?: {
+    [name: string]: any
+  }
+}
 export interface EntityTagHeaderValue {
   readonly tag?: {
     readonly buffer?: string
@@ -3836,6 +4175,60 @@ export interface InsertVendorContactRelationshipModel {
    */
   isMain?: boolean
 }
+export interface JournalEntries {
+  pageSize?: number
+  pageNumber?: number
+  sortBy?: string
+  associatedType?: ('applicant' | 'contact' | 'company' | 'landlord' | 'tenancy')[]
+  associatedId?: string[]
+  negotiatorId?: string[]
+  typeId?: string[]
+  createdFrom?: string
+  createdTo?: string
+}
+/**
+ * Representation of a journal entry
+ */
+export interface JournalEntryModel {
+  /**
+   * The date and time when the journal entry was created
+   * example:
+   * 2019-08-14T12:30:02.0000000Z
+   */
+  created?: string // date-time
+  /**
+   * The unique identifier of the property the journal entry is related to. Can additionally be associated to another type
+   */
+  propertyId?: string
+  /**
+   * The entity type the journal entry has been raised against (applicant/contact/company/landlord/tenancy)
+   */
+  associatedType?: string
+  /**
+   * The unique identifier of the entity the journal entry has been raised against. Can additionally be associated to a property
+   */
+  associatedId?: string
+  /**
+   * The type of journal entry
+   */
+  typeId?: string
+  /**
+   * The unique identifier of the negotiator that created the entry
+   */
+  negotiatorId?: string
+  /**
+   * The textual description of the journal entry event
+   */
+  description?: string
+  readonly _links?: {
+    [name: string]: {
+      href?: string
+    }
+  }
+  readonly _embedded?: {
+    [name: string]: any
+  }
+}
 /**
  * Representation of the physical address of a building or premise
  */
@@ -5777,6 +6170,140 @@ export interface PagedResultDocumentModel_ {
     }
   }
 }
+export interface PagedResultEnquiryModel_ {
+  _embedded?: {
+    /**
+     * The unique identifier of the enquiry
+     */
+    id?: number // int32
+    /**
+     * The date and time when the enquiry was created
+     * example:
+     * 2019-08-14T12:30:02.0000000Z
+     */
+    created?: string // date-time
+    /**
+     * The date and time when the enquiry was last modified
+     * example:
+     * 2019-08-14T12:30:02.0000000Z
+     */
+    modified?: string // date-time
+    /**
+     * The title of the individual making the enquiry
+     */
+    title?: string
+    /**
+     * The forename of the individual making the enquiry
+     */
+    forename?: string
+    /**
+     * The surname of the individual making the enquiry
+     */
+    surname?: string
+    /**
+     * The type of enquiry. Enquiries can created for applicants interested in buying/renting, as well as prospective vendors and landlords (salesApplicant/lettingsApplicant/salesProperty/lettingsProperty)
+     */
+    enquiryType?: string
+    /**
+     * Textual information about the nature of the enquiry - usually the message text from the individual making the enquiry
+     */
+    message?: string
+    /**
+     * The status of the enquiry (pending/added/rejected/alreadyExists/duplicateEntry/spam)
+     */
+    status?: string
+    /**
+     * The marketing consent status of the individual making the enquiry (grant/deny/notAsked)
+     */
+    marketingConsent?: string
+    /**
+     * The selling position of the individual making the enquiry (renting/instructedThisAgent/instructedOtherAgent/privateSale/notOnMarket)
+     */
+    position?: string
+    /**
+     * The unique identifier of the office related to the enquiry
+     */
+    officeId?: string
+    /**
+     * The name of the source that the enquiry was generated by
+     */
+    sourceName?: string
+    /**
+     * The home phone number of the individual making the enquiry
+     */
+    homePhone?: string
+    /**
+     * The work phone number of the individual making the enquiry
+     */
+    workPhone?: string
+    /**
+     * The mobile phone number of the individual making the enquiry
+     */
+    mobilePhone?: string
+    /**
+     * The email of the individual making the enquiry
+     */
+    email?: string
+    /**
+     * The address of the individual making the enquiry
+     */
+    address?: {
+      /**
+       * The building name
+       */
+      buildingName?: string
+      /**
+       * The building number
+       */
+      buildingNumber?: string
+      /**
+       * The first line of the address
+       */
+      line1?: string
+      /**
+       * The second line of the address
+       */
+      line2?: string
+      /**
+       * The third line of the address
+       */
+      line3?: string
+      /**
+       * The fourth line of the address
+       */
+      line4?: string
+      /**
+       * The postcode
+       */
+      postcode?: string
+      /**
+       * The ISO-3166 country code that the address resides within
+       */
+      countryId?: string
+    }
+    /**
+     * A list of unique property identifiers that this enquiry relates to
+     */
+    propertyIds?: string[]
+    readonly _links?: {
+      [name: string]: {
+        href?: string
+      }
+    }
+    readonly _embedded?: {
+      [name: string]: any
+    }
+  }[]
+  pageNumber?: number // int32
+  pageSize?: number // int32
+  pageCount?: number // int32
+  totalCount?: number // int32
+  _links?: {
+    [name: string]: {
+      href?: string
+    }
+  }
+}
 export interface PagedResultIdentityCheckModel_ {
   _embedded?: {
     /**
@@ -5888,6 +6415,57 @@ export interface PagedResultIdentityCheckModel_ {
     }
   }
 }
+export interface PagedResultJournalEntryModel_ {
+  _embedded?: {
+    /**
+     * The date and time when the journal entry was created
+     * example:
+     * 2019-08-14T12:30:02.0000000Z
+     */
+    created?: string // date-time
+    /**
+     * The unique identifier of the property the journal entry is related to. Can additionally be associated to another type
+     */
+    propertyId?: string
+    /**
+     * The entity type the journal entry has been raised against (applicant/contact/company/landlord/tenancy)
+     */
+    associatedType?: string
+    /**
+     * The unique identifier of the entity the journal entry has been raised against. Can additionally be associated to a property
+     */
+    associatedId?: string
+    /**
+     * The type of journal entry
+     */
+    typeId?: string
+    /**
+     * The unique identifier of the negotiator that created the entry
+     */
+    negotiatorId?: string
+    /**
+     * The textual description of the journal entry event
+     */
+    description?: string
+    readonly _links?: {
+      [name: string]: {
+        href?: string
+      }
+    }
+    readonly _embedded?: {
+      [name: string]: any
+    }
+  }[]
+  pageNumber?: number // int32
+  pageSize?: number // int32
+  pageCount?: number // int32
+  totalCount?: number // int32
+  _links?: {
+    [name: string]: {
+      href?: string
+    }
+  }
+}
 export interface PagedResultLandlordContactRelationshipModel_ {
   _embedded?: {
     /**
@@ -6602,6 +7180,14 @@ export interface PagedResultPropertyModel_ {
      * The arrangements regarding viewing the property
      */
     viewingArrangements?: string
+    /**
+     * The url of a video associated with this property, such as a virtual tour
+     */
+    videoUrl?: string
+    /**
+     * The caption for the video url associated with this property
+     */
+    videoCaption?: string
     /**
      * Details of the external land area associated to this property
      */
@@ -8119,6 +8705,14 @@ export interface PropertyModel {
    * The arrangements regarding viewing the property
    */
   viewingArrangements?: string
+  /**
+   * The url of a video associated with this property, such as a virtual tour
+   */
+  videoUrl?: string
+  /**
+   * The caption for the video url associated with this property
+   */
+  videoCaption?: string
   /**
    * Details of the external land area associated to this property
    */
@@ -10424,6 +11018,14 @@ export interface UpdatePropertyModel {
    * The arrangements regarding viewing the property
    */
   viewingArrangements?: string
+  /**
+   * The url of a video associated with this property, such as a virtual tour
+   */
+  videoUrl?: string
+  /**
+   * The caption for the video url associated with this property
+   */
+  videoCaption?: string
   /**
    * Details of the EPC statistics
    */
diff --git a/packages/react-app-scaffolder/app/templates/_config.example.json b/packages/react-app-scaffolder/app/templates/_config.example.json
new file mode 100644
index 0000000000..b4305ac9b0
--- /dev/null
+++ b/packages/react-app-scaffolder/app/templates/_config.example.json
@@ -0,0 +1,6 @@
+{
+  "appEnv": "development",
+  "sentryDns": "",
+  "cognitoClientId": "",
+  "googleAnalyticsKey": ""
+}
diff --git a/packages/react-app-scaffolder/app/templates/_jest.config.js b/packages/react-app-scaffolder/app/templates/_jest.config.js
index fe75a215c1..e3d7b18525 100644
--- a/packages/react-app-scaffolder/app/templates/_jest.config.js
+++ b/packages/react-app-scaffolder/app/templates/_jest.config.js
@@ -6,7 +6,7 @@ module.exports = {
   setupFiles: ['<rootDir>/src/scripts/jest-setup.js'],
   collectCoverageFrom: ['<rootDir>/src/**/*.ts', '<rootDir>/src/**/*.tsx'],
   coverageDirectory: './src/tests/coverage',
-  coveragePathIgnorePatterns: ['<rootDir>[/\\\\](node_modules|src/types|src/tests|src/scripts)[/\\\\]'],
+  coveragePathIgnorePatterns: ['<rootDir>[/\\\\](node_modules|src/types|src/tests|src/scripts)[/\\\\]', 'index.tsx'],
   modulePathIgnorePatterns: ['<rootDir>[/\\\\](node_modules)[/\\\\]'],
   snapshotSerializers: ['enzyme-to-json/serializer'],
   moduleNameMapper: {
diff --git a/packages/react-app-scaffolder/app/templates/_package.json b/packages/react-app-scaffolder/app/templates/_package.json
index 2ce02dc4b7..de43c874a4 100644
--- a/packages/react-app-scaffolder/app/templates/_package.json
+++ b/packages/react-app-scaffolder/app/templates/_package.json
@@ -30,7 +30,8 @@
     "start:prod": "serve public/dist -s -l 8080",
     "test:ci": "cross-env TZ=UTC jest --ci --colors --coverage --silent --forceExit",
     "test:dev": "cross-env TZ=UTC jest --watch --verbose",
-    "test:update-badges": "yarn test:ci && jest-coverage-badges --input src/tests/coverage/coverage-summary.json --output src/tests/badges"
+    "test:update-badges": "yarn test:ci && jest-coverage-badges --input src/tests/coverage/coverage-summary.json --output src/tests/badges",
+    "fetch-config": "yarn config-manager fetchConfig <%= name %>"
   },
   <% if (isFoundation) { %>
   "dependencies": {},
diff --git a/packages/react-app-scaffolder/app/templates/apollo/src/core/__tests__/__snapshots__/index.tsx.snap b/packages/react-app-scaffolder/app/templates/apollo/src/core/__tests__/__snapshots__/app.tsx.snap
similarity index 100%
rename from packages/react-app-scaffolder/app/templates/apollo/src/core/__tests__/__snapshots__/index.tsx.snap
rename to packages/react-app-scaffolder/app/templates/apollo/src/core/__tests__/__snapshots__/app.tsx.snap
diff --git a/packages/react-app-scaffolder/app/templates/apollo/src/core/__tests__/index.tsx b/packages/react-app-scaffolder/app/templates/apollo/src/core/__tests__/app.tsx
similarity index 94%
rename from packages/react-app-scaffolder/app/templates/apollo/src/core/__tests__/index.tsx
rename to packages/react-app-scaffolder/app/templates/apollo/src/core/__tests__/app.tsx
index 6b2a931cfb..873ca25aae 100644
--- a/packages/react-app-scaffolder/app/templates/apollo/src/core/__tests__/index.tsx
+++ b/packages/react-app-scaffolder/app/templates/apollo/src/core/__tests__/app.tsx
@@ -1,7 +1,7 @@
 import * as React from 'react'
 import { shallow } from 'enzyme'
 import toJson from 'enzyme-to-json'
-import App from '../index'
+import App from '../app'
 import { render, unmountComponentAtNode } from 'react-dom'
 
 jest.mock('../router')
diff --git a/packages/react-app-scaffolder/app/templates/apollo/src/core/private-route-wrapper.tsx b/packages/react-app-scaffolder/app/templates/apollo/src/core/private-route-wrapper.tsx
index f827e39841..58587d8930 100644
--- a/packages/react-app-scaffolder/app/templates/apollo/src/core/private-route-wrapper.tsx
+++ b/packages/react-app-scaffolder/app/templates/apollo/src/core/private-route-wrapper.tsx
@@ -16,7 +16,7 @@ export const PrivateRouteWrapper: React.FunctionComponent<PrivateRouteWrapperPro
   const { loginSession, refreshParams, getLoginSession } = React.useContext(AuthContext)
 
   if (!loginSession && !refreshParams) {
-    redirectToOAuth(process.env.COGNITO_CLIENT_ID_<%= nameInConstantCase %> as string)
+    redirectToOAuth(window.reapit.config.cognitoClientId)
     return null
   }
 
diff --git a/packages/react-app-scaffolder/app/templates/apollo/src/graphql/client.ts b/packages/react-app-scaffolder/app/templates/apollo/src/graphql/client.ts
index f21681e2d4..8d768100af 100644
--- a/packages/react-app-scaffolder/app/templates/apollo/src/graphql/client.ts
+++ b/packages/react-app-scaffolder/app/templates/apollo/src/graphql/client.ts
@@ -42,9 +42,9 @@ const clientState = {
   typeDefs,
 }
 
-export const getClient = (accessToken: string) =>
+export const getClient = (accessToken: string, uri: string) =>
   new ApolloClient({
-    uri: process.env.GRAPHQL_SERVER_URI,
+    uri,
     onError,
     cache,
     request: generateRequest(accessToken),
diff --git a/packages/react-app-scaffolder/app/templates/apollo/src/hooks/use-auth.ts b/packages/react-app-scaffolder/app/templates/apollo/src/hooks/use-auth.ts
index 57efb1cf7f..004d60504a 100644
--- a/packages/react-app-scaffolder/app/templates/apollo/src/hooks/use-auth.ts
+++ b/packages/react-app-scaffolder/app/templates/apollo/src/hooks/use-auth.ts
@@ -22,7 +22,7 @@ export const useAuth = (): AuthHook => {
   const [loginSession, setLoginSession] = React.useState<LoginSession | null>(null)
   const urlParams: RefreshParams | null = getTokenFromQueryString(
     window.location.search,
-    process.env.COGNITO_CLIENT_ID_<%= nameInConstantCase %> as string,
+    window.reapit.config.cognitoClientId
   )
   const cookieParams = getSessionCookie(COOKIE_SESSION_KEY)
   const refreshParams = cookieParams ? cookieParams : urlParams
@@ -43,7 +43,7 @@ export const useAuth = (): AuthHook => {
 
   const logout = React.useCallback(() => {
     removeSession(COOKIE_SESSION_KEY)
-    redirectToLogout(process.env.COGNITO_CLIENT_ID_<%= nameInConstantCase %> as string, `${window.location.origin}/login`)
+    redirectToLogout(window.reapit.config.cognitoClientId, `${window.location.origin}/login`)
   }, [])
 
   return {
diff --git a/packages/react-app-scaffolder/app/templates/apollo/src/types/global.d.ts b/packages/react-app-scaffolder/app/templates/apollo/src/types/global.d.ts
new file mode 100644
index 0000000000..7dfea0a8ba
--- /dev/null
+++ b/packages/react-app-scaffolder/app/templates/apollo/src/types/global.d.ts
@@ -0,0 +1,15 @@
+export type Config = {
+  appEnv: 'local' | 'development' | 'production'
+  sentryDns: string
+  cognitoClientId: string
+  googleAnalyticsKey: string
+  graphqlUri: string
+}
+
+declare global {
+  interface Window {
+    reapit: {
+      config: Config
+    }
+  }
+}
diff --git a/packages/react-app-scaffolder/app/templates/azure/_azure-pipelines.yml b/packages/react-app-scaffolder/app/templates/azure/_azure-pipelines.yml
deleted file mode 100644
index 5b687b3850..0000000000
--- a/packages/react-app-scaffolder/app/templates/azure/_azure-pipelines.yml
+++ /dev/null
@@ -1,42 +0,0 @@
-# Node.js with React
-# Build a Node.js project that uses React.
-# Add steps that analyze code, save build artifacts, deploy, and more:
-# https://docs.microsoft.com/azure/devops/pipelines/languages/javascript
-
-trigger:
-  branches:
-    include:
-    - '*'
-  tags:
-    include:
-    - '*'
-
-jobs:
-- job: buildWebApp
-  displayName: Test and build web app
-  pool:
-    vmImage: 'ubuntu-latest'
-  # variables:
-  # - group: 
-  steps:
-  - task: NodeTool@0
-    inputs:
-      versionSpec: '10.x'
-    displayName: 'Install Node.js'
-  - script: yarn install
-    displayName: Install deps
-    failOnStderr: true
-
-  # - bash: printenv > src/constants/.env
-  #   displayName: Injects ENVs
-  #   env:
-
-  - script: yarn test:ci
-    displayName: Unit tests
-    env:
-      NPM_TOKEN: $(NPM_TOKEN)
-
-  - script: yarn build
-    displayName: Build static assets
-    failOnStderr: true
-
diff --git a/packages/react-app-scaffolder/app/templates/base-is-not-foundation/reapit.config.json b/packages/react-app-scaffolder/app/templates/base-is-not-foundation/reapit.config.json
deleted file mode 100644
index 98feaed15f..0000000000
--- a/packages/react-app-scaffolder/app/templates/base-is-not-foundation/reapit.config.json
+++ /dev/null
@@ -1,15 +0,0 @@
-{
-  "LOCAL": {
-    "REAPIT_ENV": "LOCAL",
-    "PLATFORM_API_BASE_URL": "https://dev.platform.reapit.cloud",
-    "MARKETPLACE_API_BASE_URL": "https://dev.platformmarketplace.reapit.cloud",
-    "MARKET_PLACE_URL": "https://dev.marketplace.reapit.cloud",
-    "NODE_ENV": "development",
-    "NPM_TOKEN": "",
-    "COGNITO_USERPOOL_ID": "eu-west-2_hbt0B7yys",
-    "COGNITO_OAUTH_URL": "https://dev.connect.reapit.cloud",
-    "COGNITO_CLIENT_ID_<%= nameInConstantCase %>": "<%= clientId %>",
-    "SWAGGER_BASE_URL": "https://dev.platform.reapit.cloud/docs",
-    "APPLICATION_URL": "http://localhost:8080"
-  }
-}
diff --git a/packages/react-app-scaffolder/app/templates/base-is-not-sass/src/scripts/webpack-dev.js b/packages/react-app-scaffolder/app/templates/base-is-not-sass/src/scripts/webpack-dev.js
index fdb466d65e..860f16980e 100644
--- a/packages/react-app-scaffolder/app/templates/base-is-not-sass/src/scripts/webpack-dev.js
+++ b/packages/react-app-scaffolder/app/templates/base-is-not-sass/src/scripts/webpack-dev.js
@@ -82,6 +82,7 @@ module.exports = {
   },
   devtool: 'inline-source-map',
   devServer: {
+    contentBase: [path.join(process.cwd(), 'public'), path.join(process.cwd())],
     open: true,
     clientLogLevel: 'warning',
     historyApiFallback: true,
diff --git a/packages/react-app-scaffolder/app/templates/base-is-sass/src/scripts/webpack-dev.js b/packages/react-app-scaffolder/app/templates/base-is-sass/src/scripts/webpack-dev.js
index 2edbc6e007..1b46cb8d63 100644
--- a/packages/react-app-scaffolder/app/templates/base-is-sass/src/scripts/webpack-dev.js
+++ b/packages/react-app-scaffolder/app/templates/base-is-sass/src/scripts/webpack-dev.js
@@ -132,6 +132,7 @@ module.exports = {
   },
   devtool: 'inline-source-map',
   devServer: {
+    contentBase: [path.join(process.cwd(), 'public'), path.join(process.cwd())],
     open: true,
     clientLogLevel: 'warning',
     historyApiFallback: true,
diff --git a/packages/react-app-scaffolder/app/templates/base/src/core/app.tsx b/packages/react-app-scaffolder/app/templates/base/src/core/app.tsx
new file mode 100644
index 0000000000..8a982f4b77
--- /dev/null
+++ b/packages/react-app-scaffolder/app/templates/base/src/core/app.tsx
@@ -0,0 +1,76 @@
+import * as React from 'react'
+import Router from './router'
+<% if (redux) { %>
+import store from './store'
+import { Provider } from 'react-redux'
+<% } %>
+<% if (graphql) { %>
+import { ApolloProvider } from '@apollo/react-hooks'
+import getClient from '@/graphql/client'
+<% } %>
+<% if (!redux) { %>
+import { useAuth } from '@/hooks/use-auth'
+import { AuthContext } from '@/context'
+<% } %>
+<% if (stylesSolution == 'sass') { %>
+import '@/styles/index.scss'
+<% } else { %>
+import { createGlobalStyle } from 'styled-components'
+
+<% if (isFoundation) { %>
+import globalCss from 'raw-loader!sass-loader!@reapit/elements/styles/index.scss'
+<% } else { %>
+import globalCss from 'raw-loader!sass-loader!@reapit/elements/dist/index.css'
+<% } %>
+
+  const GlobalStyle = createGlobalStyle`
+  ${globalCss};
+  body {
+    background-color: unset;
+  }
+`
+    <% } %>
+
+<% if (isFoundation) { %>
+import * as OfflinePluginRuntime from 'offline-plugin/runtime'
+OfflinePluginRuntime.install()
+<% } %>
+
+const rootElement = document.querySelector('#root') as Element
+
+const App = () => {
+  <% if (!redux && !graphql) { %>
+    const { loginSession, refreshParams, getLoginSession, ...rest } = useAuth()
+  <% } %>
+  <% if (graphql) { %>
+    const { loginSession, refreshParams, getLoginSession, ...rest } = useAuth()
+    if (!loginSession && refreshParams) {
+      getLoginSession(refreshParams)
+    }
+    const accessToken = loginSession?.accessToken || ''
+  <% } %>
+  return (
+    <% if (!redux) { %>
+    <AuthContext.Provider value={{ loginSession, refreshParams, getLoginSession, ...rest }}>
+    <% } %>
+    <% if (graphql) { %>
+      <ApolloProvider client={getClient(accessToken, window.reapit.config.graphqlUri)}>
+    <% } %>
+    <% if (redux) { %>
+      <Provider store={store.reduxStore}>
+    <% } %>
+      <Router />
+    <% if (stylesSolution === 'styledComponents') { %>
+      <GlobalStyle />
+    <% } %>
+    <% if (redux) { %>
+      </Provider>
+    <% } %>
+    <% if (graphql) { %>
+      </ApolloProvider>
+    <% } %>
+    <% if (!redux) { %>
+      </AuthContext.Provider>
+    <% } %>
+  )
+}
diff --git a/packages/react-app-scaffolder/app/templates/base/src/core/index.tsx b/packages/react-app-scaffolder/app/templates/base/src/core/index.tsx
index 48409ec84c..01b8303fa9 100644
--- a/packages/react-app-scaffolder/app/templates/base/src/core/index.tsx
+++ b/packages/react-app-scaffolder/app/templates/base/src/core/index.tsx
@@ -1,83 +1,59 @@
-import * as React from 'react'
+import * as OfflinePluginRuntime from 'offline-plugin/runtime'
+OfflinePluginRuntime.install()
+import '../styles/index.scss'
+import * as Sentry from '@sentry/browser'
+import React from 'react'
 import { render } from 'react-dom'
-import Router from './router'
-<% if (redux) { %>
-import store from './store'
-import { Provider } from 'react-redux'
-<% } %>
-<% if (graphql) { %>
-import { ApolloProvider } from '@apollo/react-hooks'
-import getClient from '@/graphql/client'
-<% } %>
-<% if (!redux) { %>
-import { useAuth } from '@/hooks/use-auth'
-import { AuthContext } from '@/context'
-<% } %>
-<% if (stylesSolution == 'sass') { %>
-import '@/styles/index.scss'
-<% } else { %>
-import { createGlobalStyle } from 'styled-components'
+import ReactGA from 'react-ga'
+import { Config } from '@/types/global'
+import App from './app'
 
-<% if (isFoundation) { %>
-import globalCss from 'raw-loader!sass-loader!@reapit/elements/styles/index.scss'
-<% } else { %>
-import globalCss from 'raw-loader!sass-loader!@reapit/elements/dist/index.css'
-<% } %>
+// Init global config
+window.reapit = {
+  config: {
+    appEnv: 'production',
+    sentryDns: '',
+    cognitoClientId: '',
+    googleAnalyticsKey: '',
+  },
+}
 
-  const GlobalStyle = createGlobalStyle`
-  ${globalCss};
-  body {
-    background-color: unset;
+export const renderApp = (Component: React.ComponentType) => {
+  const rootElement = document.querySelector('#root') as Element
+  if (rootElement) {
+    render(<Component />, rootElement)
   }
-`
-    <% } %>
-
-<% if (isFoundation) { %>
-import * as OfflinePluginRuntime from 'offline-plugin/runtime'
-OfflinePluginRuntime.install()
-<% } %>
-
-const rootElement = document.querySelector('#root') as Element
+}
 
-const App = () => {
-  <% if (!redux && !graphql) { %>
-    const { loginSession, refreshParams, getLoginSession, ...rest } = useAuth()
-  <% } %>
-  <% if (graphql) { %>
-    const { loginSession, refreshParams, getLoginSession, ...rest } = useAuth()
-    if (!loginSession && refreshParams) {
-      getLoginSession(refreshParams)
-    }
-    const accessToken = loginSession?.accessToken || ''
-  <% } %>
-  return (
-    <% if (!redux) { %>
-    <AuthContext.Provider value={{ loginSession, refreshParams, getLoginSession, ...rest }}>
-    <% } %>
-    <% if (graphql) { %>
-      <ApolloProvider client={getClient(accessToken)}>
-    <% } %>
-    <% if (redux) { %>
-      <Provider store={store.reduxStore}>
-    <% } %>
-      <Router />
-    <% if (stylesSolution === 'styledComponents') { %>
-      <GlobalStyle />
-    <% } %>
-    <% if (redux) { %>
-      </Provider>
-    <% } %>
-    <% if (graphql) { %>
-      </ApolloProvider>
-    <% } %>
-    <% if (!redux) { %>
-      </AuthContext.Provider>
-    <% } %>
-  )
+const run = async () => {
+  await fetch('config.json')
+    .then(response => response.json())
+    .then((config: Config) => {
+      window.reapit.config = config
+      const isLocal = config.appEnv === 'local'
+      if (!isLocal && config.sentryDns) {
+        Sentry.init({
+          release: process.env.APP_VERSION,
+          dsn: config.sentryDns,
+          environment: config.appEnv,
+        })
+      }
+      if (!isLocal && config.googleAnalyticsKey) {
+        ReactGA.initialize(config.googleAnalyticsKey)
+        ReactGA.pageview(window.location.pathname + window.location.search)
+      }
+      renderApp(App)
+    })
+    .catch(error => {
+      console.error('Cannot fetch config', error)
+    })
 }
 
-if (rootElement) {
-  render(<App />, rootElement)
+if (module['hot']) {
+  module['hot'].accept('./app', () => {
+    const NextApp = require('./app').default
+    renderApp(NextApp)
+  })
 }
 
-export default App
+run()
diff --git a/packages/react-app-scaffolder/app/templates/base/src/scripts/jest-setup.js b/packages/react-app-scaffolder/app/templates/base/src/scripts/jest-setup.js
index 794f6dadce..afdac7df0c 100644
--- a/packages/react-app-scaffolder/app/templates/base/src/scripts/jest-setup.js
+++ b/packages/react-app-scaffolder/app/templates/base/src/scripts/jest-setup.js
@@ -23,6 +23,18 @@ const mockStorage = (() => {
   }
 })()
 
+Object.defineProperty(window, 'reapit', {
+  value: {
+    config: {
+      appEnv: 'development',
+      sentryDns: '',
+      cognitoClientId: '',
+      googleAnalyticsKey: '',
+      graphqlUri: '',
+    },
+  },
+})
+
 Object.defineProperty(window, 'localStorage', {
   value: mockStorage
 })
diff --git a/packages/react-app-scaffolder/app/templates/no-redux/src/components/pages/login.tsx b/packages/react-app-scaffolder/app/templates/no-redux/src/components/pages/login.tsx
index dda1921b3e..8b7654c6e0 100644
--- a/packages/react-app-scaffolder/app/templates/no-redux/src/components/pages/login.tsx
+++ b/packages/react-app-scaffolder/app/templates/no-redux/src/components/pages/login.tsx
@@ -11,12 +11,12 @@ import { AuthContext } from '@/context'
 import { redirectToLogin } from '@reapit/cognito-auth'
 
 export const redirectToLoginPage = () => {
-  const cognitoClientId = process.env.COGNITO_CLIENT_ID_<%= nameInConstantCase %> as string
+  const cognitoClientId = window.reapit.config.cognitoClientId
   redirectToLogin(cognitoClientId, `${window.location.origin}`)
 }
 
 export const Login: React.FunctionComponent = () => {
-  const cognitoClientId = process.env.COGNITO_CLIENT_ID_<%= nameInConstantCase %> as string
+  const cognitoClientId = window.reapit.config.cognitoClientId
   const loginHandler = React.useCallback(redirectToLoginPage, [])
 
   <% if (stylesSolution == 'sass') { %>const { wrapper, container, image } = loginStyles<%}%>
diff --git a/packages/react-app-scaffolder/app/templates/redux/src/core/__tests__/index.tsx b/packages/react-app-scaffolder/app/templates/no-redux/src/core/__tests__/app.tsx
similarity index 94%
rename from packages/react-app-scaffolder/app/templates/redux/src/core/__tests__/index.tsx
rename to packages/react-app-scaffolder/app/templates/no-redux/src/core/__tests__/app.tsx
index 6b2a931cfb..873ca25aae 100644
--- a/packages/react-app-scaffolder/app/templates/redux/src/core/__tests__/index.tsx
+++ b/packages/react-app-scaffolder/app/templates/no-redux/src/core/__tests__/app.tsx
@@ -1,7 +1,7 @@
 import * as React from 'react'
 import { shallow } from 'enzyme'
 import toJson from 'enzyme-to-json'
-import App from '../index'
+import App from '../app'
 import { render, unmountComponentAtNode } from 'react-dom'
 
 jest.mock('../router')
diff --git a/packages/react-app-scaffolder/app/templates/no-redux/src/core/private-route-wrapper.tsx b/packages/react-app-scaffolder/app/templates/no-redux/src/core/private-route-wrapper.tsx
index 330652f241..c366a3035b 100644
--- a/packages/react-app-scaffolder/app/templates/no-redux/src/core/private-route-wrapper.tsx
+++ b/packages/react-app-scaffolder/app/templates/no-redux/src/core/private-route-wrapper.tsx
@@ -14,7 +14,7 @@ export type PrivateRouteWrapperProps = RouteComponentProps & {
 export const PrivateRouteWrapper: React.FunctionComponent<PrivateRouteWrapperProps> = ({ children }) => {
   const { loginSession, refreshParams, getLoginSession } = React.useContext(AuthContext)
   if (!loginSession && !refreshParams) {
-    redirectToOAuth(process.env.COGNITO_CLIENT_ID_<%= nameInConstantCase %> as string)
+    redirectToOAuth(window.reapit.config.cognitoClientId)
     return null
   }
 
diff --git a/packages/react-app-scaffolder/app/templates/no-redux/src/hooks/use-auth.ts b/packages/react-app-scaffolder/app/templates/no-redux/src/hooks/use-auth.ts
index 00bf8a917e..004d60504a 100644
--- a/packages/react-app-scaffolder/app/templates/no-redux/src/hooks/use-auth.ts
+++ b/packages/react-app-scaffolder/app/templates/no-redux/src/hooks/use-auth.ts
@@ -22,7 +22,7 @@ export const useAuth = (): AuthHook => {
   const [loginSession, setLoginSession] = React.useState<LoginSession | null>(null)
   const urlParams: RefreshParams | null = getTokenFromQueryString(
     window.location.search,
-    process.env.COGNITO_CLIENT_ID_<%= nameInConstantCase %> as string,
+    window.reapit.config.cognitoClientId
   )
   const cookieParams = getSessionCookie(COOKIE_SESSION_KEY)
   const refreshParams = cookieParams ? cookieParams : urlParams
@@ -43,7 +43,7 @@ export const useAuth = (): AuthHook => {
 
   const logout = React.useCallback(() => {
     removeSession(COOKIE_SESSION_KEY)
-    redirectToLogout(process.env.COGNITO_CLIENT_ID_<%= nameInConstantCase %>  as string, `${window.location.origin}/login`)
+    redirectToLogout(window.reapit.config.cognitoClientId, `${window.location.origin}/login`)
   }, [])
 
   return {
diff --git a/packages/react-app-scaffolder/app/templates/no-redux/src/types/global.d.ts b/packages/react-app-scaffolder/app/templates/no-redux/src/types/global.d.ts
new file mode 100644
index 0000000000..78dc3c42d1
--- /dev/null
+++ b/packages/react-app-scaffolder/app/templates/no-redux/src/types/global.d.ts
@@ -0,0 +1,14 @@
+export type Config = {
+  appEnv: 'local' | 'development' | 'production'
+  sentryDns: string
+  cognitoClientId: string
+  googleAnalyticsKey: string
+}
+
+declare global {
+  interface Window {
+    reapit: {
+      config: Config
+    }
+  }
+}
diff --git a/packages/react-app-scaffolder/app/templates/redux/src/components/pages/login.tsx b/packages/react-app-scaffolder/app/templates/redux/src/components/pages/login.tsx
index 94a4f949c3..57d133bbd3 100644
--- a/packages/react-app-scaffolder/app/templates/redux/src/components/pages/login.tsx
+++ b/packages/react-app-scaffolder/app/templates/redux/src/components/pages/login.tsx
@@ -17,7 +17,7 @@ export interface LoginProps {
   hasSession: boolean
 }
 
-const loginHandler = () => redirectToLogin(process.env.COGNITO_CLIENT_ID_<%= nameInConstantCase %> as string, `${window.location.origin}`)
+const loginHandler = () => redirectToLogin(window.reapit.config.cognitoClientId, `${window.location.origin}`)
 
 export const Login: React.FunctionComponent<LoginProps> = (props: LoginProps) => {
   <% if (stylesSolution == 'sass') { %>const { wrapper, container, image } = loginStyles<%}%>
diff --git a/packages/react-app-scaffolder/app/templates/no-redux/src/core/__tests__/index.tsx b/packages/react-app-scaffolder/app/templates/redux/src/core/__tests__/app.tsx
similarity index 94%
rename from packages/react-app-scaffolder/app/templates/no-redux/src/core/__tests__/index.tsx
rename to packages/react-app-scaffolder/app/templates/redux/src/core/__tests__/app.tsx
index 6b2a931cfb..873ca25aae 100644
--- a/packages/react-app-scaffolder/app/templates/no-redux/src/core/__tests__/index.tsx
+++ b/packages/react-app-scaffolder/app/templates/redux/src/core/__tests__/app.tsx
@@ -1,7 +1,7 @@
 import * as React from 'react'
 import { shallow } from 'enzyme'
 import toJson from 'enzyme-to-json'
-import App from '../index'
+import App from '../app'
 import { render, unmountComponentAtNode } from 'react-dom'
 
 jest.mock('../router')
diff --git a/packages/react-app-scaffolder/app/templates/redux/src/core/private-route-wrapper.tsx b/packages/react-app-scaffolder/app/templates/redux/src/core/private-route-wrapper.tsx
index 1d7408a5e4..2845f968c8 100644
--- a/packages/react-app-scaffolder/app/templates/redux/src/core/private-route-wrapper.tsx
+++ b/packages/react-app-scaffolder/app/templates/redux/src/core/private-route-wrapper.tsx
@@ -33,7 +33,7 @@ export const PrivateRouteWrapper: React.FunctionComponent<PrivateRouteWrapperPro
   location,
   hasSession,
 }) => {
-  const cognitoClientId = process.env.COGNITO_CLIENT_ID_<%= nameInConstantCase %> as string
+  const cognitoClientId = window.reapit.config.cognitoClientId
   const refreshParams = getTokenFromQueryString(location.search, cognitoClientId)
 
   if (refreshParams && !hasSession) {
diff --git a/packages/react-app-scaffolder/app/templates/redux/src/sagas/__tests__/auth.ts b/packages/react-app-scaffolder/app/templates/redux/src/sagas/__tests__/auth.ts
index 369dbce74b..ed5bc81a0e 100644
--- a/packages/react-app-scaffolder/app/templates/redux/src/sagas/__tests__/auth.ts
+++ b/packages/react-app-scaffolder/app/templates/redux/src/sagas/__tests__/auth.ts
@@ -55,7 +55,7 @@ describe('auth sagas', () => {
       const gen = doLogout()
       expect(gen.next().value).toEqual(call(removeSession, COOKIE_SESSION_KEY))
       expect(gen.next().value).toEqual(
-        call(redirectToLogout, process.env.COGNITO_CLIENT_ID_<%= nameInConstantCase %> as string, `${window.location.origin}/login`),
+        call(redirectToLogout, window.reapit.config.cognitoClientId, `${window.location.origin}/login`),
       )
       expect(gen.next().done).toBe(true)
     })
diff --git a/packages/react-app-scaffolder/app/templates/redux/src/sagas/auth.ts b/packages/react-app-scaffolder/app/templates/redux/src/sagas/auth.ts
index d133d58bfc..1fe9f1e0ed 100644
--- a/packages/react-app-scaffolder/app/templates/redux/src/sagas/auth.ts
+++ b/packages/react-app-scaffolder/app/templates/redux/src/sagas/auth.ts
@@ -23,7 +23,7 @@ export const doLogin = function*({ data }: Action<LoginParams>) {
 export const doLogout = function*() {
   try {
     yield call(removeSession, COOKIE_SESSION_KEY)
-    yield call(redirectToLogout, process.env.COGNITO_CLIENT_ID_<%= nameInConstantCase %> as string, `${window.location.origin}/login`)
+    yield call(redirectToLogout, window.reapit.config.cognitoClientId, `${window.location.origin}/login`)
   } catch (err) {
     console.error(err.message)
   }
diff --git a/packages/react-app-scaffolder/app/templates/redux/src/types/global.d.ts b/packages/react-app-scaffolder/app/templates/redux/src/types/global.d.ts
new file mode 100644
index 0000000000..793c74f491
--- /dev/null
+++ b/packages/react-app-scaffolder/app/templates/redux/src/types/global.d.ts
@@ -0,0 +1,15 @@
+export type Config = {
+  appEnv: 'local' | 'development' | 'production'
+  sentryDns: string
+  cognitoClientId: string
+  googleAnalyticsKey: string
+}
+
+declare global {
+  interface Window {
+    reapit: {
+      config: Config
+    }
+    __REDUX_DEVTOOLS_EXTENSION__?: Function
+  }
+}
diff --git a/scripts/foundations-ts-definitions/constants.js b/scripts/foundations-ts-definitions/constants.js
deleted file mode 100644
index 9e312449d6..0000000000
--- a/scripts/foundations-ts-definitions/constants.js
+++ /dev/null
@@ -1,7 +0,0 @@
-const path = require('path')
-
-module.exports = {
-  FOUNDATION_ROOT_FOLDER: path.resolve(__dirname, '../../packages/foundations-ts-definitions'),
-  FOUNDATION_TYPES_FOLDER: path.resolve(__dirname, '../../packages/foundations-ts-definitions/types'),
-  PACKAGE_NAME: '@reapit/nghia-test',
-}