diff --git a/README.md b/README.md
index b4e2853..94f03a6 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,154 @@
-# Project requirements
+# Fullstack Price Aggregator
+UK supermarket price aggregator via web scraping. This was developed for my university project as an MVP so there are aspects which don't function correctly. I may update it in the future.
 
-* Docker
 
-Install docker for your platform, open a terminal and cd into the project root, from here run the command "docker-compose up --build".
-This will build the image 
\ No newline at end of file
+## Features
+* Python REST backend via FastAPI
+* VueJS Frontend SPA with TypeScript using [this template](https://github.com/Armour/vue-typescript-admin-template).
+* JWT Authentication between backend and frontend
+* CLI
+* [Docker](https://github.com/docker)
+* [Poetry](https://github.com/python-poetry/poetry)
+* Web Scraped prices via [selectorlib](https://selectorlib.com/)
+* Scrape JavaScript enabled pages using [splash](https://github.com/scrapinghub/splash)
+
+
+## Setup
+Ensure you have Docker and Node.js installed.
+
+1. Clone this repository
+2. cd into project root and run `docker-compose up --build -d`. The starts the backend and on first run will build all the necessary containers which can take a few minutes.
+3. Once the backend has finished building, obtain a shell with `docker-compose exec backend bash`.
+4. To initialise the database run `python manage.py createdb` from within the bash shell.
+    * (Optional) To seed the db with dummy data run `python manage.py seeddb` 
+    Note all passwords are set to **password**
+5. `cd` into the client directory run `npm install` which will install the required node modules.
+6. run `npm run serve` which will serve the client frontend.
+
+
+## Project URLs
+
+| URL                                      | Description                                                                                 |
+|:----------------------------------------:|:--------------------------------------------------------------------------------------------|
+| [0.0.0.0:8000/api](0.0.0.0:8000/api)     | Backend JSON API                                                                            |
+| [0.0.0.0:8000/docs](0.0.0.0:8000/docs)   | Backend OpenAPI/Swagger-generated API Reference Documentation                               |
+| [0.0.0.0:8000/redoc](0.0.0.0:8000/redoc) | Alternative interactive documentation provided by [ReDoc](https://github.com/Redocly/redoc) |
+| [localhost:9527](localhost:9527)         | Frontend VueJS Single Page Application                                                      |
+
+
+## CLI
+The backend includes a CLI which is heavily inspired by [Netflix's Dispatch](https://github.com/Netflix/dispatch).
+
+[`typer`](https://github.com/tiangolo/typer) (same author as FastAPI) was used to create a CLI for the project and is accessed via [manage.py](./server/manage.py).
+
+To run commands you will need to a shell running inside the backend container with:
+    
+    docker-compose run backend bash
+
+To see all the available commands:
+
+    root@72293bee6b37:/app# python manage.py 
+    Usage: manage.py [OPTIONS] COMMAND [ARGS]...
+    
+    Options:
+      --help  Show this message and exit.
+    
+    Commands:
+      config      Display application configuration.
+      createdb    Creates an empty database.
+      createrole  Add role to database.
+      createuser  Create new user in the database.
+      develop     Start a development server with reload.
+      dropdb      Drop the existing database.
+      routes      Display application routes and dependencies.
+      seeddb      Add fake data to database.
+      shell       Starts an interactive shell with app object imported.
+
+#### Routes
+
+    root@72293bee6b37:/app# python manage.py routes
+    
+    Application Endpoints
+    Path                           Methods    Dependencies
+    -----------------------------  ---------  --------------------------------------------------------
+    /api/v1/users/                 GET        ['RoleChecker: Roles: admin,user']
+    /api/v1/users/                 POST       ['RoleChecker: Roles: admin,user']
+    /api/v1/users/{id}             GET        ['RoleChecker: Roles: admin,user']
+    /api/v1/users/{id}             PUT        ['RoleChecker: Roles: admin,user']
+    /api/v1/users/{id}             DELETE     ['RoleChecker: Roles: admin,user']
+    /api/v1/users/{id}/roles       GET        ['RoleChecker: Roles: admin,user']
+    /api/v1/users/{id}/roles       PUT        ['RoleChecker: Roles: admin,user']
+    /api/v1/users/{id}/shops       GET        ['RoleChecker: Roles: admin,user']
+    /api/v1/users/{id}/shops       PUT        ['RoleChecker: Roles: admin,user']
+    /api/v1/roles/                 GET        ['RoleChecker: Roles: admin,user']
+    /api/v1/roles/                 POST       ['RoleChecker: Roles: admin,user']
+    /api/v1/roles/{id}             GET        ['RoleChecker: Roles: admin,user']
+    /api/v1/roles/{id}             PUT        ['RoleChecker: Roles: admin,user']
+    /api/v1/roles/{id}             DELETE     ['RoleChecker: Roles: admin,user']
+    /api/v1/shops/                 GET        ['RoleChecker: Roles: admin,user']
+    /api/v1/shops/                 POST       ['RoleChecker: Roles: admin,user']
+    /api/v1/shops/{id}             PUT        ['RoleChecker: Roles: admin,user']
+    /api/v1/shops/{id}             DELETE     ['RoleChecker: Roles: admin,user']
+    /api/v1/shops/listings/        GET        ['RoleChecker: Roles: admin,user']
+    /api/healthcheck               GET        []
+
+#### Config
+
+    python manage.py config
+    
+    Application Configuration
+    Setting                     Value(s)
+    --------------------------  --------------------------------------------------------------------
+    APP_DIR                     /app/app
+    STATIC_DIR                  /app/app/static
+    EMAIL_TEMPLATES_DIR         /app/app/static/email-templates/html
+    PROJECT_NAME                Fastapi Backend
+    SERVER_HOST                 0.0.0.0
+    CORS_WHITELIST              ['http://localhost', 'http://localhost:8000', 'http://0.0.0.0:8000']
+    FASTAPI_ENV                 development
+    DEBUG                       False
+    LOG_LEVEL                   debug
+    FIRST_SUPERUSER             user@example.com
+    FIRST_SUPERUSER_PASSWORD    a5dbf43e07f4d19e5b73bc89a8f74
+    USERS_OPEN_REGISTRATION     True
+    SECRET_KEY                  **********
+    JWT_AUTH_LIFETIME_SECONDS   604800
+    JWT_EMAIL_LIFETIME_SECONDS  3600
+    SMTP_USER                   admin@backend.com
+    SMTP_PASSWORD               **********
+    SMTP_TLS                    False
+    SMTP_SSL                    False
+    SMTP_HOST                   mailhog
+    SMTP_PORT                   1025
+    POSTGRES_USER               postgres
+    POSTGRES_PASSWORD           **********
+    POSTGRES_HOST               postgres
+    POSTGRES_PORT               5432
+    POSTGRES_DB                 fastapi_backend
+
+## Frontend views
+
+### Login view
+![Login View](./imgs/1-login.png "Login View")
+
+### Admin dashboard view
+![Admin dashboard view](./imgs/2-admin-dashboard.png "Admin dashboard view")
+
+### User CRUD view
+![User CRUD View](./imgs/3.0-user-CRUD.png "User CRUD view")
+
+### User create view
+![User create view](./imgs/3.1-user-create.png "User create view")
+
+### User profile view
+![User profile view](./imgs/4-user-profile.png "User profile view")
+
+### Shop CRUD view
+![Shop CRUD view](./imgs/5.0-shop-CRUD.png "Shop CRUDview")
+
+### Shop select view
+![Shop select view](./imgs/5.1-shop-select.png "Shop select view")
+
+### Scraped listings view
+![Scraped listings view](./imgs/6-prices-display.png "Scraped listings view")
+
diff --git a/client/package-lock.json b/client/package-lock.json
index 79a4b59..3c07f94 100644
--- a/client/package-lock.json
+++ b/client/package-lock.json
@@ -1169,6 +1169,15 @@
       "integrity": "sha512-gJJX9Jjdt3bIAePQRRjYWG20dIhAgEqonguyHxXuqALxsoDsDLimihqrSg8fXgVTJ4KZCzkfglKtwsh/8dLfbA==",
       "dev": true
     },
+    "@types/codemirror": {
+      "version": "0.0.91",
+      "resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-0.0.91.tgz",
+      "integrity": "sha512-FZcfBNjhVc6slo6RbtbCqYa+KTQa9sykV5OdRLqd3FeMPddVLFuqSR3KNZUbzU9qoEBudBZX0nbItJ52ml37KA==",
+      "dev": true,
+      "requires": {
+        "@types/tern": "*"
+      }
+    },
     "@types/color-name": {
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz",
@@ -1181,6 +1190,12 @@
       "integrity": "sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag==",
       "dev": true
     },
+    "@types/estree": {
+      "version": "0.0.44",
+      "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.44.tgz",
+      "integrity": "sha512-iaIVzr+w2ZJ5HkidlZ3EJM8VTZb2MJLCjw3V+505yVts0gRC4UMvjw0d1HPtGqI/HQC/KdsYtayfzl+AXY2R8g==",
+      "dev": true
+    },
     "@types/events": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz",
@@ -1257,6 +1272,15 @@
       "integrity": "sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ==",
       "dev": true
     },
+    "@types/tern": {
+      "version": "0.23.3",
+      "resolved": "https://registry.npmjs.org/@types/tern/-/tern-0.23.3.tgz",
+      "integrity": "sha512-imDtS4TAoTcXk0g7u4kkWqedB3E4qpjXzCpD2LU5M5NAXHzCDsypyvXSaG7mM8DKYkCRa7tFp4tS/lp/Wo7Q3w==",
+      "dev": true,
+      "requires": {
+        "@types/estree": "*"
+      }
+    },
     "@types/webpack-env": {
       "version": "1.15.1",
       "resolved": "https://registry.npmjs.org/@types/webpack-env/-/webpack-env-1.15.1.tgz",
@@ -2144,6 +2168,11 @@
       "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==",
       "dev": true
     },
+    "JSV": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/JSV/-/JSV-4.0.2.tgz",
+      "integrity": "sha1-0Hf2glVx+CEy+d/67Vh7QCn+/1c="
+    },
     "abbrev": {
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
@@ -3787,6 +3816,11 @@
       "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
       "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c="
     },
+    "codemirror": {
+      "version": "5.53.2",
+      "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.53.2.tgz",
+      "integrity": "sha512-wvSQKS4E+P8Fxn/AQ+tQtJnF1qH5UOlxtugFLpubEZ5jcdH2iXTVinb+Xc/4QjshuOxRm4fUsU2QPF1JJKiyXA=="
+    },
     "collection-visit": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz",
@@ -7145,6 +7179,11 @@
         }
       }
     },
+    "has-color": {
+      "version": "0.1.7",
+      "resolved": "https://registry.npmjs.org/has-color/-/has-color-0.1.7.tgz",
+      "integrity": "sha1-ZxRKUmDDT8PMpnfQQdr1L+e3iy8="
+    },
     "has-flag": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
@@ -8323,7 +8362,6 @@
       "version": "2.1.3",
       "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz",
       "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==",
-      "dev": true,
       "requires": {
         "minimist": "^1.2.5"
       }
@@ -8343,6 +8381,15 @@
       "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=",
       "dev": true
     },
+    "jsonlint": {
+      "version": "1.6.3",
+      "resolved": "https://registry.npmjs.org/jsonlint/-/jsonlint-1.6.3.tgz",
+      "integrity": "sha512-jMVTMzP+7gU/IyC6hvKyWpUU8tmTkK5b3BPNuMI9U8Sit+YAWLlZwB6Y6YrdCxfg2kNz05p3XY3Bmm4m26Nv3A==",
+      "requires": {
+        "JSV": "^4.0.x",
+        "nomnom": "^1.5.x"
+      }
+    },
     "jsprim": {
       "version": "1.4.1",
       "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
@@ -9780,6 +9827,42 @@
       "integrity": "sha512-wp8zyQVwef2hpZ/dJH7SfSrIPD6YoJz6BDQDpGEkcA0s3LpAQoxBIYmfIq6QAhC1DhwsyCgTaTTcONwX8qzCuQ==",
       "dev": true
     },
+    "nomnom": {
+      "version": "1.8.1",
+      "resolved": "https://registry.npmjs.org/nomnom/-/nomnom-1.8.1.tgz",
+      "integrity": "sha1-IVH3Ikcrp55Qp2/BJbuMjy5Nwqc=",
+      "requires": {
+        "chalk": "~0.4.0",
+        "underscore": "~1.6.0"
+      },
+      "dependencies": {
+        "ansi-styles": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.0.0.tgz",
+          "integrity": "sha1-yxAt8cVvUSPquLZ817mAJ6AnkXg="
+        },
+        "chalk": {
+          "version": "0.4.0",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-0.4.0.tgz",
+          "integrity": "sha1-UZmj3c0MHv4jvAjBsCewYXbgxk8=",
+          "requires": {
+            "ansi-styles": "~1.0.0",
+            "has-color": "~0.1.0",
+            "strip-ansi": "~0.1.0"
+          }
+        },
+        "strip-ansi": {
+          "version": "0.1.1",
+          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.1.1.tgz",
+          "integrity": "sha1-OeipjQRNFQZgq+SmgIrPcLt7yZE="
+        },
+        "underscore": {
+          "version": "1.6.0",
+          "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz",
+          "integrity": "sha1-izixDKze9jM3uLJOT/htRa6lKag="
+        }
+      }
+    },
     "nopt": {
       "version": "4.0.3",
       "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz",
@@ -11370,6 +11453,11 @@
         "unpipe": "1.0.0"
       }
     },
+    "raw-loader": {
+      "version": "0.5.1",
+      "resolved": "https://registry.npmjs.org/raw-loader/-/raw-loader-0.5.1.tgz",
+      "integrity": "sha1-DD0L6u2KAclm2Xh793goElKpeao="
+    },
     "read-pkg": {
       "version": "5.2.0",
       "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz",
@@ -11956,6 +12044,14 @@
       "resolved": "https://registry.npmjs.org/screenfull/-/screenfull-5.0.2.tgz",
       "integrity": "sha512-cCF2b+L/mnEiORLN5xSAz6H3t18i2oHh9BA8+CQlAh5DRw2+NFAGQJOSYbcGw8B2k04g/lVvFcfZ83b3ysH5UQ=="
     },
+    "script-loader": {
+      "version": "0.7.2",
+      "resolved": "https://registry.npmjs.org/script-loader/-/script-loader-0.7.2.tgz",
+      "integrity": "sha512-UMNLEvgOAQuzK8ji8qIscM3GIrRCWN6MmMXGD4SD5l6cSycgGsCo0tX5xRnfQcoghqct0tjHjcykgI1PyBE2aA==",
+      "requires": {
+        "raw-loader": "~0.5.1"
+      }
+    },
     "select": {
       "version": "1.1.2",
       "resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz",
diff --git a/client/package.json b/client/package.json
index 3dda7c1..ef8b0cf 100644
--- a/client/package.json
+++ b/client/package.json
@@ -13,16 +13,20 @@
   "dependencies": {
     "axios": "^0.19.2",
     "clipboard": "^2.0.6",
+    "codemirror": "^5.53.2",
     "core-js": "^3.6.5",
     "echarts": "^4.7.0",
     "element-ui": "^2.13.0",
     "fuse.js": "^5.1.0",
     "js-cookie": "^2.2.1",
+    "json5": "^2.1.3",
+    "jsonlint": "^1.6.3",
     "normalize.css": "^8.0.1",
     "nprogress": "^0.2.0",
     "path-to-regexp": "^6.1.0",
     "register-service-worker": "^1.7.1",
     "screenfull": "^5.0.2",
+    "script-loader": "^0.7.2",
     "simple-progress-webpack-plugin": "^1.1.2",
     "vue": "^2.6.11",
     "vue-class-component": "^7.2.3",
@@ -35,6 +39,7 @@
   },
   "devDependencies": {
     "@types/clipboard": "^2.0.1",
+    "@types/codemirror": "0.0.91",
     "@types/js-cookie": "^2.2.5",
     "@types/node": "^13.11.0",
     "@types/nprogress": "^0.2.0",
diff --git a/client/src/components/JsonEditor/index.vue b/client/src/components/JsonEditor/index.vue
new file mode 100644
index 0000000..6f9e4de
--- /dev/null
+++ b/client/src/components/JsonEditor/index.vue
@@ -0,0 +1,91 @@
+<template>
+  <div class="json-editor">
+    <textarea ref="textarea" />
+  </div>
+</template>
+
+<script lang="ts">
+import CodeMirror, { Editor } from 'codemirror';
+import 'codemirror/addon/lint/lint.css';
+import 'codemirror/lib/codemirror.css';
+import 'codemirror/theme/elegant.css';
+import 'codemirror/mode/javascript/javascript';
+import 'codemirror/addon/lint/lint';
+import 'codemirror/addon/lint/json-lint';
+import { Component, Prop, Vue, Watch } from 'vue-property-decorator';
+
+// HACK: have to use script-loader to load jsonlint
+/* eslint-disable import/no-webpack-loader-syntax */
+require('script-loader!jsonlint');
+
+@Component({
+  name: 'JsonEditor'
+})
+export default class extends Vue {
+  @Prop({ required: true }) private value!: string
+
+  private jsonEditor?: Editor
+
+  @Watch('value')
+  private onValueChange(value: string) {
+    if (this.jsonEditor) {
+      const editorValue = this.jsonEditor.getValue();
+      if (value !== editorValue) {
+        this.jsonEditor.setValue(JSON.stringify(this.value, null, 2));
+      }
+    }
+  }
+
+  mounted() {
+    this.jsonEditor = CodeMirror.fromTextArea(this.$refs.textarea as HTMLTextAreaElement, {
+      lineNumbers: true,
+      mode: 'application/json',
+      gutters: ['CodeMirror-lint-markers'],
+      theme: 'elegant',
+      lint: true
+    });
+
+    this.jsonEditor.setValue(JSON.stringify(this.value, null, 2));
+    this.jsonEditor.on('change', editor => {
+      this.$emit('changed', editor.getValue());
+      this.$emit('input', editor.getValue());
+    });
+  }
+
+  public setValue(value: string) {
+    if (this.jsonEditor) {
+      this.jsonEditor.setValue(value);
+    }
+  }
+
+  public getValue() {
+    if (this.jsonEditor) {
+      return this.jsonEditor.getValue();
+    }
+    return '';
+  }
+}
+</script>
+
+<style lang="scss">
+.CodeMirror {
+  height: auto;
+  min-height: 300px;
+  font-family: inherit;
+}
+
+.CodeMirror-scroll {
+  min-height: 300px;
+}
+
+.cm span.cm-string {
+  color: #F08047;
+}
+</style>
+
+<style lang="scss" scoped>
+.json-editor {
+  height: 100%;
+  position: relative;
+}
+</style>
diff --git a/client/src/views/prices/Index.vue b/client/src/views/prices/Index.vue
index 53b1a18..7f1071c 100644
--- a/client/src/views/prices/Index.vue
+++ b/client/src/views/prices/Index.vue
@@ -34,43 +34,70 @@
     </div>
 
     <div class="search-results">
-      <div
-        class="item"
-        v-for="shopResult in results"
-        :key="shopResult.id"
+      <el-row
+        v-for="shop in results"
+        :key="shop.id"
+        :gutter="12"
       >
-        <div
-          v-for="listing in shopResult.listings"
-          :key="listing.url"
+        <h2>{{ shop.name }}</h2>
+        <el-col
+          v-for="item in shop.listings"
+          :key="item.url"
+          :span="8"
         >
           <el-card
-          class="hover">
-            <img
-              :src="listing.image_url"
-              class="image"
-            >
-            <div style="padding: 14px;">
-              <span>{{ listing.name }}</span>
-              <div class="bottom clearfix">
-                <time class="time">{{ listing.price }}</time>
+            class="box-card small"
+            shadow="hover"
+          >
+            <el-row>
+              <el-col :span="4">
+                <div class="card-img">
+                  <el-image
+                    style="width: 100px; height: 100px"
+                    :src="item.image_url"
+                    fit="scale-down"
+                    lazy
+                  />
+                </div>
+              </el-col>
+            </el-row>
+
+            <el-row>
+              {{ item.name }}
+            </el-row>
+
+            <el-row>
+              <span class="item-price">£{{ item.price }}</span>
+            </el-row>
+
+            <el-row>
+              <span class="item-price">£{{ item.price_per_unit }}</span>
+            </el-row>
+
+            <el-row>
+              <el-col>
                 <el-button
                   type="text"
                   class="button"
                 >
-                  View item
+                  <el-link
+                    :href="item.url"
+                    type="primary"
+                  >
+                    View Item
+                  </el-link>
                 </el-button>
-              </div>
-            </div>
+              </el-col>
+            </el-row>
           </el-card>
-        </div>
-      </div>
+        </el-col>
+      </el-row>
     </div>
   </div>
 </template>
 
 <script lang="ts">
 import { Component, Vue } from 'vue-property-decorator';
-import { UserMeModule } from '@/store/modules/me';
 import { getShopListings } from '@/api/shops';
 import { IShopListings } from '@/api/types';
 import { ShopsModule } from '@/store/modules/shops';
@@ -121,8 +148,12 @@ export default class extends Vue {
   .el-checkbox-group {
     padding: 10px;
   }
-  .listing-card {
-    width: 150px;
+  .box-card {
+    margin: 5px;
+    max-width: 100rem;
+    .item-price {
+      font-weight: bold;
+    }
   }
   .search-terms {
     max-width: 700px;
@@ -132,5 +163,9 @@ export default class extends Vue {
     size: 50px;
     max-width: 250px;
   }
+  .card-img {
+    height: auto;
+    max-width: 30%;
+  }
 
 </style>
diff --git a/client/src/views/prices/Index2.vue b/client/src/views/prices/Index2.vue
new file mode 100644
index 0000000..9280b6b
--- /dev/null
+++ b/client/src/views/prices/Index2.vue
@@ -0,0 +1,166 @@
+<template>
+  <div class="app-container">
+    <div
+      class="search-prices"
+    >
+      <div class="search-terms">
+        <el-select
+          v-model="params.include"
+          multiple
+          placeholder="Select shops"
+          no-data-text="No shops available"
+        >
+          <el-option
+            v-for="shop in allShops"
+            :key="shop.id"
+            :label="shop.name"
+            :value="shop.id"
+          />
+        </el-select>
+        <el-input
+          v-model="params.query"
+          placeholder="Query"
+          clearable
+          style="width: 200px"
+        />
+        <el-button
+          :loading="loading"
+          type="primary"
+          @click="searchPrices()"
+        >
+          Search
+        </el-button>
+      </div>
+    </div>
+
+    <div class="search-results">
+      <el-row
+        v-for="shop in results"
+        :key="shop.id"
+        :gutter="12"
+      >
+        <h2>{{ shop.name }}</h2>
+        <el-col
+          v-for="item in shop.listings"
+          :key="item.url"
+          :span="8"
+        >
+          <el-card
+            class="box-card small"
+            shadow="hover"
+          >
+            <el-row>
+              <el-col :span="4">
+                <div class="card-img">
+                  <el-image
+                    style="width: 100px; height: 100px"
+                    :src="item.image_url"
+                    fit="scale-down"
+                    lazy
+                  />
+                </div>
+              </el-col>
+            </el-row>
+
+            <el-row>
+              {{ item.name }}
+            </el-row>
+
+            <el-row>
+              <span class="item-price">£{{ item.price }}</span>
+            </el-row>
+
+            <el-row>
+              <span class="item-price">£{{ item.price_per_unit }}</span>
+            </el-row>
+
+            <el-row>
+              <el-col>
+                <el-button
+                  type="text"
+                  class="button"
+                >
+                  <el-link
+                    :href="item.url"
+                    type="primary"
+                  >
+                    View Item
+                  </el-link>
+                </el-button>
+              </el-col>
+            </el-row>
+          </el-card>
+        </el-col>
+      </el-row>
+    </div>
+  </div>
+</template>
+
+<script lang="ts">
+import { Component, Vue } from 'vue-property-decorator';
+import { getShopListings } from '@/api/shops';
+import { IShopListings } from '@/api/types';
+import { ShopsModule } from '@/store/modules/shops';
+
+const jsonData = '[{"id":1,"name":"aldi","listings":[{"name":"Fresh Tagliatelle Pasta","url":"https:\\/\\/www.aldi.co.uk\\/fresh-tagliatelle-pasta\\/p\\/016774333996602","price":"1.192.38perkg","price_per_unit":null,"image_url":"https:\\/\\/cdn.aldi-digital.co.uk\\/\\/Fresh-Tagliatelle-Pasta-A.jpg?o=PDOPN2HcYse7V5Fh%24nrTgxqzg6oj&V=QuE%24&w=178&h=223&p=2&q=88, https:\\/\\/cdn.aldi-digital.co.uk\\/\\/Fresh-Tagliatelle-Pasta-A.jpg?o=PDOPN2HcYse7V5Fh%24nrTgxqzg6oj&V=QuE%24&w=356&h=446&p=2&q=83 2x"},{"name":"Cheese Pasta Salad","url":"https:\\/\\/www.aldi.co.uk\\/cheese-pasta-salad\\/p\\/002460198287502","price":"0.9924.8pper100g","price_per_unit":null,"image_url":"https:\\/\\/cdn.aldi-digital.co.uk\\/\\/Cheese-Pasta-Salad-A.jpg?o=2mt6etRnUjpczXatInBzb6orx78j&V=NP9g&w=178&h=223&p=2&q=88, https:\\/\\/cdn.aldi-digital.co.uk\\/\\/Cheese-Pasta-Salad-A.jpg?o=2mt6etRnUjpczXatInBzb6orx78j&V=NP9g&w=356&h=446&p=2&q=83 2x"},{"name":"Carbonara Pasta Sauce","url":"https:\\/\\/www.aldi.co.uk\\/carbonara-pasta-sauce\\/p\\/002806000353200","price":"0.5216.8pper100g","price_per_unit":null,"image_url":"https:\\/\\/cdn.aldi-digital.co.uk\\/\\/Carbonara-Pasta-Sauce-A.jpg?o=UnLYVAmb7OB4c3tesLdVs%40%4087vAj&V=YSDf&w=178&h=223&p=2&q=88, https:\\/\\/cdn.aldi-digital.co.uk\\/\\/Carbonara-Pasta-Sauce-A.jpg?o=UnLYVAmb7OB4c3tesLdVs%40%4087vAj&V=YSDf&w=356&h=446&p=2&q=83 2x"},{"name":"Tuna Pasta Bake","url":"https:\\/\\/www.aldi.co.uk\\/tuna-pasta-bake\\/p\\/081857200400800","price":"1.894.73perkg","price_per_unit":null,"image_url":"https:\\/\\/cdn.aldi-digital.co.uk\\/\\/Tuna-Pasta-Bake-A.jpg?o=t%40kJu1l6U6pWwwJabcSEMvRBZS4j&V=138d&w=178&h=223&p=2&q=88, https:\\/\\/cdn.aldi-digital.co.uk\\/\\/Tuna-Pasta-Bake-A.jpg?o=t%40kJu1l6U6pWwwJabcSEMvRBZS4j&V=138d&w=356&h=446&p=2&q=83 2x"},{"name":"Carbonara Pasta Sauce","url":"https:\\/\\/www.aldi.co.uk\\/carbonara-pasta-sauce\\/p\\/077398279806002","price":"0.992.83perkg","price_per_unit":null,"image_url":"https:\\/\\/cdn.aldi-digital.co.uk\\/\\/Carbonara-Pasta-Sauce-A.jpg?o=viutNWO38%40hx0Jvb7c4UiOmj0JEj&V=YSDf&w=178&h=223&p=2&q=88, https:\\/\\/cdn.aldi-digital.co.uk\\/\\/Carbonara-Pasta-Sauce-A.jpg?o=viutNWO38%40hx0Jvb7c4UiOmj0JEj&V=YSDf&w=356&h=446&p=2&q=83 2x"},{"name":"Fresh Fusilli Pasta","url":"https:\\/\\/www.aldi.co.uk\\/fresh-fusilli-pasta\\/p\\/016774333996401","price":"1.192.38perkg","price_per_unit":null,"image_url":"https:\\/\\/cdn.aldi-digital.co.uk\\/\\/Fresh-Fusilli-Pasta-A.jpg?o=8%40H%24%24cncr2y8N1fQ8%40q8O4vkzM4j&V=%40M%243&w=178&h=223&p=2&q=88, https:\\/\\/cdn.aldi-digital.co.uk\\/\\/Fresh-Fusilli-Pasta-A.jpg?o=8%40H%24%24cncr2y8N1fQ8%40q8O4vkzM4j&V=%40M%243&w=356&h=446&p=2&q=83 2x"},{"name":"Cheesy Macaroni Pasta","url":"https:\\/\\/www.aldi.co.uk\\/cheesy-macaroni-pasta\\/p\\/070916066805700","price":"0.8946.8pper100g","price_per_unit":null,"image_url":"https:\\/\\/cdn.aldi-digital.co.uk\\/\\/Cheesy-Macaroni-Pasta-A.jpg?o=3k8TrZPRA8VzbI86y7ZyLKMWKZAj&V=2Q%40z&w=178&h=223&p=2&q=88, https:\\/\\/cdn.aldi-digital.co.uk\\/\\/Cheesy-Macaroni-Pasta-A.jpg?o=3k8TrZPRA8VzbI86y7ZyLKMWKZAj&V=2Q%40z&w=356&h=446&p=2&q=83 2x"},{"name":"Chicken & Mushroom Pasta & Sauce","url":"https:\\/\\/www.aldi.co.uk\\/chicken-%26-mushroom-pasta-%26-sauce\\/p\\/043698254081501","price":"0.3933.9pper100g","price_per_unit":null,"image_url":"https:\\/\\/cdn.aldi-digital.co.uk\\/\\/Chicken-&-Mushroom-Pasta-&-Sauce-A.jpg?o=7a%40e9jK6xR7djj9w98s57poauCoj&V=oMTt&w=178&h=223&p=2&q=88, https:\\/\\/cdn.aldi-digital.co.uk\\/\\/Chicken-&-Mushroom-Pasta-&-Sauce-A.jpg?o=7a%40e9jK6xR7djj9w98s57poauCoj&V=oMTt&w=356&h=446&p=2&q=83 2x"},{"name":"Macaroni Cheese Pasta & Sauce","url":"https:\\/\\/www.aldi.co.uk\\/macaroni-cheese-pasta-%26-sauce\\/p\\/043694254081301","price":"0.3933.9pper100g","price_per_unit":null,"image_url":"https:\\/\\/cdn.aldi-digital.co.uk\\/\\/Macaroni-Cheese-Pasta-&-Sauce-A.jpg?o=XqKxTWR6Mc3Xt%40YEYf97BFWxHUMj&V=dtEQ&w=178&h=223&p=2&q=88, https:\\/\\/cdn.aldi-digital.co.uk\\/\\/Macaroni-Cheese-Pasta-&-Sauce-A.jpg?o=XqKxTWR6Mc3Xt%40YEYf97BFWxHUMj&V=dtEQ&w=356&h=446&p=2&q=83 2x"},{"name":"Cheese & Broccoli Pasta & Sauce","url":"https:\\/\\/www.aldi.co.uk\\/cheese-%26-broccoli-pasta-%26-sauce\\/p\\/043698003281800","price":"0.3933.9pper100g","price_per_unit":null,"image_url":"https:\\/\\/cdn.aldi-digital.co.uk\\/\\/Cheese-&-Broccoli-Pasta-&-Sauce-A.jpg?o=0vwg%24ZlSTGvRYs99I6IFcSqsik0j&V=hBhd&w=178&h=223&p=2&q=88, https:\\/\\/cdn.aldi-digital.co.uk\\/\\/Cheese-&-Broccoli-Pasta-&-Sauce-A.jpg?o=0vwg%24ZlSTGvRYs99I6IFcSqsik0j&V=hBhd&w=356&h=446&p=2&q=83 2x"}]},{"id":2,"name":"amazon_pantry","listings":[{"name":"Dolmio Original Bolognese Pasta Sauce, 2 x 500 g","url":"https:\\/\\/www.amazon.co.uk\\/Dolmio-Original-Bolognese-Pasta-Sauce\\/dp\\/B073QM1L3Q\\/ref=sr_1_1?dchild=1&keywords=pasta&qid=1588413654&s=pantry&sr=8-1&srs=5782660031","price":"2.00","price_per_unit":"2.00\\/kg","image_url":"https:\\/\\/m.media-amazon.com\\/images\\/I\\/91bsI1OZu6L._AC_UL320_.jpg"},{"name":"DOLMIO Bolognese 750g Extra Onion & Garlic","url":"https:\\/\\/www.amazon.co.uk\\/Dolmio-Sauce-Bolognese-Intense-Garlic\\/dp\\/B0176G8DPO\\/ref=sr_1_2?dchild=1&keywords=pasta&qid=1588413654&s=pantry&sr=8-2&srs=5782660031","price":"2.00","price_per_unit":"2.67\\/kg","image_url":"https:\\/\\/m.media-amazon.com\\/images\\/I\\/81w2DU28wFL._AC_UL320_.jpg"},{"name":"Dolmio Pasta Bake Creamy Tomato Pasta Sauce, 500 grams","url":"https:\\/\\/www.amazon.co.uk\\/Dolmio-Sauce-Pasta-Creamy-Tomato\\/dp\\/B0148K6FK6\\/ref=sr_1_3?dchild=1&keywords=pasta&qid=1588413654&s=pantry&sr=8-3&srs=5782660031","price":"1.75","price_per_unit":"3.50\\/kg","image_url":"https:\\/\\/m.media-amazon.com\\/images\\/I\\/81TWVLtaJlL._AC_UL320_.jpg"},{"name":"Uncle Ben\'s Spicy Mexican Rice 3 x 250g (750g)","url":"https:\\/\\/www.amazon.co.uk\\/Uncle-Bens-Spicy-Mexican-Rice\\/dp\\/B073H9JJL3\\/ref=sr_1_4?dchild=1&keywords=pasta&qid=1588413654&s=pantry&sr=8-4&srs=5782660031","price":"4.00","price_per_unit":"5.33\\/kg","image_url":"https:\\/\\/m.media-amazon.com\\/images\\/I\\/81V02PG4gXL._AC_UL320_.jpg"},{"name":"Domestos Original Thick Bleach, Toilet Disinfectant And Cleaner For Home And Bathroom, Removes Stains From Surafces And Kills 99.9% Of Bacteria And Germs (750 ml)","url":"https:\\/\\/www.amazon.co.uk\\/Domestos-100444506-Bleach-Original-750ml\\/dp\\/B014G50572\\/ref=sr_1_5?dchild=1&keywords=pasta&qid=1588413654&s=pantry&sr=8-5&srs=5782660031","price":"1.00","price_per_unit":"1.33\\/l","image_url":"https:\\/\\/m.media-amazon.com\\/images\\/I\\/71ERGDc7QjL._AC_UL320_.jpg"},{"name":"Loyd Grossman Tomato and Roastedgarlic Sauce, 350g","url":"https:\\/\\/www.amazon.co.uk\\/Loyd-Grossman-Tomato-Roastedgarlic-Sauce\\/dp\\/B014G2KM6Y\\/ref=sr_1_6?dchild=1&keywords=pasta&qid=1588413654&s=pantry&sr=8-6&srs=5782660031","price":"1.40","price_per_unit":"4.00\\/kg","image_url":"https:\\/\\/m.media-amazon.com\\/images\\/I\\/71rJ2hKwCIL._AC_UL320_.jpg"},{"name":"Colgate Cavity Protection Toothpaste Pump, 100 milliliters","url":"https:\\/\\/www.amazon.co.uk\\/Colgate-Cavity-Protection-Toothpaste-milliliters\\/dp\\/B014DDL4SQ\\/ref=sr_1_7?dchild=1&keywords=pasta&qid=1588413654&s=pantry&sr=8-7&srs=5782660031","price":"1.50","price_per_unit":"1.50\\/100ml","image_url":"https:\\/\\/m.media-amazon.com\\/images\\/I\\/71jrs72NWEL._AC_UL320_.jpg"},{"name":"Old El Paso Mexican Smoky BBQ Fajita Dinner Kit, 500g","url":"https:\\/\\/www.amazon.co.uk\\/Old-El-Paso-Mexican-Fajita\\/dp\\/B0161I24WG\\/ref=sr_1_8?dchild=1&keywords=pasta&qid=1588413654&s=pantry&sr=8-8&srs=5782660031","price":"2.75","price_per_unit":"5.50\\/kg","image_url":"https:\\/\\/m.media-amazon.com\\/images\\/I\\/91yzjm9uwFL._AC_UL320_.jpg"},{"name":"Heinz Baked Beans in Tomato Sauce, 415 g (Pack of 4)","url":"https:\\/\\/www.amazon.co.uk\\/Heinz-Baked-Beans-Tomato-Sauce\\/dp\\/B015O5BZDQ\\/ref=sr_1_9?dchild=1&keywords=pasta&qid=1588413654&s=pantry&sr=8-9&srs=5782660031","price":"2.50","price_per_unit":"1.51\\/kg","image_url":"https:\\/\\/m.media-amazon.com\\/images\\/I\\/81-JGC5d+dL._AC_UL320_.jpg"},{"name":"DOLMIO Sun-Ripened Tomato and Chilli Pasta Sauce, 350 g","url":"https:\\/\\/www.amazon.co.uk\\/DOLMIO-Sun-Ripened-Tomato-Chilli-Pasta\\/dp\\/B073H734C6\\/ref=sr_1_10?dchild=1&keywords=pasta&qid=1588413654&s=pantry&sr=8-10&srs=5782660031","price":"1.75","price_per_unit":"5.00\\/kg","image_url":"https:\\/\\/m.media-amazon.com\\/images\\/I\\/81XiJ8FcoBL._AC_UL320_.jpg"}]},{"id":3,"name":"asda","listings":null},{"id":4,"name":"iceland","listings":[{"name":"Pasta Reggia di Caserta Durum Semolina Pasta Spaghetti 1kg","url":"https:\\/\\/www.iceland.co.uk\\/p\\/pasta-reggia-di-caserta-durum-semolina-pasta-spaghetti-1kg\\/63445.html","price":"1.00","price_per_unit":"1.00\\/1kg","image_url":"https:\\/\\/assets.iceland.co.uk\\/i\\/iceland\\/Reggia_1kg_Spaghetti_Pasta_63445.jpg?$producttile$"},{"name":"Batchelors Pasta \'n\' Sauce Cheese & Broccoli 99g","url":"https:\\/\\/www.iceland.co.uk\\/p\\/batchelors-pasta-n-sauce-cheese-and-broccoli-99g\\/58040.html","price":"0.80","price_per_unit":"8.08\\/1kg","image_url":"https:\\/\\/assets.iceland.co.uk\\/i\\/iceland\\/batchelors_pasta_n_sauce_cheese_broccoli_99g_58040_T5.jpg?$producttile$"},{"name":"Pot Noodle Original Curry Standard 90g","url":"https:\\/\\/www.iceland.co.uk\\/p\\/pot-noodle-original-curry-standard-90g\\/40880.html","price":"1.00","price_per_unit":"11.11\\/1kg","image_url":"https:\\/\\/assets.iceland.co.uk\\/i\\/iceland\\/Pot_Noodle_90gm_Curry_Pot_40880.jpg?$producttile$"},{"name":"Pasta Reggia Fusilli 1kg","url":"https:\\/\\/www.iceland.co.uk\\/p\\/pasta-reggia-fusilli-1kg\\/63444.html","price":"1.00","price_per_unit":"1.00\\/1kg","image_url":"https:\\/\\/assets.iceland.co.uk\\/i\\/iceland\\/Reggia_1kg_Fusilli_Pasta_63444.jpg?$producttile$"},{"name":"Dolmio Bolognese Onion and Garlic Pasta Sauce 500g","url":"https:\\/\\/www.iceland.co.uk\\/p\\/dolmio-bolognese-onion-and-garlic-pasta-sauce-500g\\/37288.html","price":"1.70","price_per_unit":"34p\\/100g","image_url":"https:\\/\\/assets.iceland.co.uk\\/i\\/iceland\\/Dolmio_500g_Intense_Garlic_37288.jpg?$producttile$"},{"name":"Branston Baked Beans in a Rich and Tasty Tomato Sauce 4 x 410g","url":"https:\\/\\/www.iceland.co.uk\\/p\\/branston-baked-beans-in-a-rich-and-tasty-tomato-sauce-4-x-410g\\/55372.html","price":"1.50","price_per_unit":"91p\\/1kg","image_url":"https:\\/\\/assets.iceland.co.uk\\/i\\/iceland\\/Branston_4_X4_Beans_55372.jpg?$producttile$"},{"name":"Dolmio Lasagne Creamy White Sauce 470g","url":"https:\\/\\/www.iceland.co.uk\\/p\\/dolmio-lasagne-creamy-white-sauce-470g\\/28118.html","price":"1.70","price_per_unit":"36p\\/100g","image_url":"https:\\/\\/assets.iceland.co.uk\\/i\\/iceland\\/dolmio_lasagne_creamy_white_sauce_470g_28118_T1.jpg?$producttile$"},{"name":"Pasta Reggia Durum Semolina Pasta 1kg","url":"https:\\/\\/www.iceland.co.uk\\/p\\/pasta-reggia-durum-semolina-pasta-1kg\\/63443.html","price":"1.00","price_per_unit":"1.00\\/1kg","image_url":"https:\\/\\/assets.iceland.co.uk\\/i\\/iceland\\/Reggia_1kg_Penne_Pasta_63443.jpg?$producttile$"},{"name":"Dolmio Bolognese Pasta Sauce 500g","url":"https:\\/\\/www.iceland.co.uk\\/p\\/dolmio-bolognese-pasta-sauce-500g\\/3447.html","price":"1.70","price_per_unit":"34p\\/100g","image_url":"https:\\/\\/assets.iceland.co.uk\\/i\\/iceland\\/dolmio_bolognese_pasta_sauce_500g_3447_T517.jpg?$producttile$"},{"name":"Napolina Five Cheese Tortellini Egg Pasta 400g","url":"https:\\/\\/www.iceland.co.uk\\/p\\/napolina-five-cheese-tortellini-egg-pasta-400g\\/78602.html","price":"1.75","price_per_unit":"4.38\\/1kg","image_url":"https:\\/\\/assets.iceland.co.uk\\/i\\/iceland\\/napolina_five_cheese_tortellini_egg_pasta_400g_78602_T1.jpg?$producttile$"}]},{"id":5,"name":"morrisons","listings":[{"name":"Morrisons Free From Fusilli 500g","url":"https:\\/\\/groceries.morrisons.com\\/products\\/morrisons-free-from-fusilli-115683011","price":"0.60","price_per_unit":"1.20\\/kg","image_url":"https:\\/\\/groceries.morrisons.com\\/productImages\\/115\\/115683011_0_150x150.jpg?identifier=880b06c864fc72579e1cf9876cb26806"},{"name":"Napolina Fusilli 500g","url":"https:\\/\\/groceries.morrisons.com\\/products\\/napolina-fusilli-214994011","price":"1","price_per_unit":"2.00\\/kg","image_url":"https:\\/\\/groceries.morrisons.com\\/productImages\\/214\\/214994011_0_150x150.jpg?identifier=560bb2a3308f55e7ac4a02e2d9a7bafb"},{"name":"Morrisons Wholewheat Fusiili 500g","url":"https:\\/\\/groceries.morrisons.com\\/products\\/morrisons-wholewheat-fusiili-209572011","price":"0.55","price_per_unit":"1.10\\/kg","image_url":"https:\\/\\/groceries.morrisons.com\\/productImages\\/209\\/209572011_0_150x150.jpg?identifier=07edf5dc2e0ee269d4ed50f283729c97"},{"name":"Morrisons Fusilli 3kg","url":"https:\\/\\/groceries.morrisons.com\\/products\\/morrisons-fusilli-215025011","price":"2.90","price_per_unit":"96.7p\\/kg","image_url":"https:\\/\\/groceries.morrisons.com\\/productImages\\/215\\/215025011_0_150x150.jpg?identifier=ca55f96573a6dfeab7c060dc1535a281"},{"name":"Napolina Wholewheat Fusilli 500g","url":"https:\\/\\/groceries.morrisons.com\\/products\\/napolina-wholewheat-fusilli-114196011","price":"1.35","price_per_unit":"2.70\\/kg","image_url":"https:\\/\\/groceries.morrisons.com\\/productImages\\/114\\/114196011_0_150x150.jpg?identifier=ac6e2c451cf348c6cc8dae4a9e97beed"},{"name":"Morrisons The Best Fusilli Gigante 500g","url":"https:\\/\\/groceries.morrisons.com\\/products\\/morrisons-the-best-fusilli-gigante-347715011","price":"1.70","price_per_unit":"3.40\\/kg","image_url":"https:\\/\\/groceries.morrisons.com\\/productImages\\/347\\/347715011_0_150x150.jpg?identifier=bcb867af066076813dcee3e199d4cdd2"},{"name":"Morrisons The Best Trottole Pasta 500g","url":"https:\\/\\/groceries.morrisons.com\\/products\\/morrisons-the-best-trottole-pasta-372760011","price":"1.75","price_per_unit":"3.50\\/kg","image_url":"https:\\/\\/groceries.morrisons.com\\/productImages\\/372\\/372760011_0_150x150.jpg?identifier=617656667739cbae15ec6cf602312b2b"},{"name":"Morrisons The Best Fusillta Casareccia Pasta 500g","url":"https:\\/\\/groceries.morrisons.com\\/products\\/morrisons-the-best-fusillta-casareccia-pasta-372758011","price":"1.75","price_per_unit":"3.50\\/kg","image_url":"https:\\/\\/groceries.morrisons.com\\/productImages\\/372\\/372758011_0_150x150.jpg?identifier=1d5ac1d44e148e3cf6d19a05bdd5064f"},{"name":"Morrisons Wholefoods Vegetable Mix with Pasta 500g","url":"https:\\/\\/groceries.morrisons.com\\/products\\/morrisons-wholefoods-vegetable-mix-with-pasta-215689011","price":"0.70","price_per_unit":"1.40\\/kg","image_url":"https:\\/\\/groceries.morrisons.com\\/productImages\\/215\\/215689011_0_150x150.jpg?identifier=b190f75c4ddcf904cd0ca908dc82cb4b"},{"name":"Napolina Fusilli Bronze Die Pasta 500g","url":"https:\\/\\/groceries.morrisons.com\\/products\\/napolina-fusilli-bronze-die-pasta-276179011","price":"1.88","price_per_unit":"3.76\\/kg","image_url":"https:\\/\\/groceries.morrisons.com\\/productImages\\/276\\/276179011_0_150x150.jpg?identifier=736e08685a1c7ae206c1f99c69a1e029"}]},{"id":6,"name":"sainsburys","listings":[{"name":"Why not try Sainsbury\'s Pomodoro Sauce, Taste the Difference 350g","url":"https:\\/\\/www.sainsburys.co.uk\\/shop\\/gb\\/groceries\\/product\\/details\\/sainsburys-pomodoro-sauce--taste-the-difference-350g","price":"2.00\\/unit","price_per_unit":"5.71\\/kg","image_url":"https:\\/\\/www.sainsburys.co.uk\\/wcsstore7.46hf.15\\/ExtendedSitesCatalogAssetStore\\/images\\/catalog\\/productImages\\/66\\/0000001745566\\/0000001745566_L.jpeg"},{"name":"Why not try Batchelors Pasta \'n\' Sauce, Cheese & Broccoli 99g","url":"https:\\/\\/www.sainsburys.co.uk\\/shop\\/gb\\/groceries\\/product\\/details\\/batchelors-pasta---sauce--cheese---broccoli-123g","price":"1.05\\/unit","price_per_unit":"10.61\\/kg","image_url":"https:\\/\\/www.sainsburys.co.uk\\/wcsstore7.46hf.15\\/ExtendedSitesCatalogAssetStore\\/images\\/catalog\\/productImages\\/09\\/5000354404009\\/5000354404009_L.jpeg"},{"name":"Sainsbury\'s Fusilli 1kg","url":"https:\\/\\/www.sainsburys.co.uk\\/shop\\/gb\\/groceries\\/product\\/details\\/sainsburys-fusilli-1kg","price":"1.10\\/unit","price_per_unit":"1.10\\/kg","image_url":"https:\\/\\/www.sainsburys.co.uk\\/wcsstore7.46hf.15\\/ExtendedSitesCatalogAssetStore\\/images\\/catalog\\/productImages\\/93\\/0000000490993\\/0000000490993_L.jpeg"},{"name":"Sainsbury\'s Penne 1kg","url":"https:\\/\\/www.sainsburys.co.uk\\/shop\\/gb\\/groceries\\/product\\/details\\/sainsburys-penne-rigate--italian-1kg","price":"1.10\\/unit","price_per_unit":"1.10\\/kg","image_url":"https:\\/\\/www.sainsburys.co.uk\\/wcsstore7.46hf.15\\/ExtendedSitesCatalogAssetStore\\/images\\/catalog\\/productImages\\/57\\/0000000536257\\/0000000536257_L.jpeg"},{"name":"Napolina Spaghetti 500g","url":"https:\\/\\/www.sainsburys.co.uk\\/shop\\/gb\\/groceries\\/product\\/details\\/napolina-spaghetti-500g","price":"1.30\\/unit","price_per_unit":"2.60\\/kg","image_url":"https:\\/\\/www.sainsburys.co.uk\\/wcsstore7.46hf.15\\/ExtendedSitesCatalogAssetStore\\/images\\/catalog\\/productImages\\/72\\/5000184592372\\/5000184592372_L.jpeg"},{"name":"Sainsbury\'s Fresh Gnocchi 500g","url":"https:\\/\\/www.sainsburys.co.uk\\/shop\\/gb\\/groceries\\/product\\/details\\/sainsburys-fresh-gnocchi-500g","price":"1.70\\/unit","price_per_unit":"3.40\\/kg","image_url":"https:\\/\\/www.sainsburys.co.uk\\/wcsstore7.46hf.15\\/ExtendedSitesCatalogAssetStore\\/images\\/catalog\\/productImages\\/86\\/0000000636186\\/0000000636186_L.jpeg"},{"name":"Sainsbury\'s Fresh Egg Fusilli 500g","url":"https:\\/\\/www.sainsburys.co.uk\\/shop\\/gb\\/groceries\\/product\\/details\\/sainsburys-fresh-egg-fusilli-500g","price":"1.70\\/unit","price_per_unit":"3.40\\/kg","image_url":"https:\\/\\/www.sainsburys.co.uk\\/wcsstore7.46hf.15\\/ExtendedSitesCatalogAssetStore\\/images\\/catalog\\/productImages\\/58\\/0000001143058\\/0000001143058_L.jpeg"},{"name":"Sainsbury\'s Fusilli 500g","url":"https:\\/\\/www.sainsburys.co.uk\\/shop\\/gb\\/groceries\\/product\\/details\\/sainsburys-fusilli--italian-500g","price":"0.60","price_per_unit":"1.20\\/kg","image_url":"https:\\/\\/www.sainsburys.co.uk\\/wcsstore7.46hf.15\\/ExtendedSitesCatalogAssetStore\\/images\\/catalog\\/productImages\\/18\\/0000000321518\\/0000000321518_L.jpeg"},{"name":"Sainsbury\'s Fresh Egg Penne 500g","url":"https:\\/\\/www.sainsburys.co.uk\\/shop\\/gb\\/groceries\\/product\\/details\\/sainsburys-fresh-penne-pasta-500g","price":"1.70\\/unit","price_per_unit":"3.40\\/kg","image_url":"https:\\/\\/www.sainsburys.co.uk\\/wcsstore7.46hf.15\\/ExtendedSitesCatalogAssetStore\\/images\\/catalog\\/productImages\\/94\\/0000000636094\\/0000000636094_L.jpeg"},{"name":"Sainsbury\'s Linguine 500g","url":"https:\\/\\/www.sainsburys.co.uk\\/shop\\/gb\\/groceries\\/product\\/details\\/sainsburys-linguine-500g","price":"0.60","price_per_unit":"1.20\\/kg","image_url":"https:\\/\\/www.sainsburys.co.uk\\/wcsstore7.46hf.15\\/ExtendedSitesCatalogAssetStore\\/images\\/catalog\\/productImages\\/24\\/0000000579124\\/0000000579124_L.jpeg"}]},{"id":7,"name":"tesco","listings":[{"name":"Tesco Penne Pasta Quills 500G","url":null,"price":"0.53","price_per_unit":"1.06\\/kg","image_url":"https:\\/\\/img.tesco.com\\/Groceries\\/pi\\/845\\/5000119319845\\/IDShot_225x225.jpg"},{"name":"Tesco Penne 300G","url":null,"price":"1.25","price_per_unit":"4.17\\/kg","image_url":"https:\\/\\/img.tesco.com\\/Groceries\\/pi\\/937\\/5057545806937\\/IDShot_225x225.jpg"},{"name":"Tesco Short Spaghetti Pasta 500G","url":null,"price":"0.53","price_per_unit":"1.06\\/kg","image_url":"https:\\/\\/img.tesco.com\\/Groceries\\/pi\\/182\\/5000119117182\\/IDShot_225x225.jpg"},{"name":"Hearty Food Co. Spaghetti Pasta 500G","url":null,"price":"0.20","price_per_unit":"0.40\\/kg","image_url":"https:\\/\\/img.tesco.com\\/Groceries\\/pi\\/514\\/5057545092514\\/IDShot_225x225.jpg"},{"name":"Tesco Whole Wheat Fusilli Pasta 500G","url":null,"price":"0.53","price_per_unit":"1.06\\/kg","image_url":"https:\\/\\/img.tesco.com\\/Groceries\\/pi\\/906\\/5000119319906\\/IDShot_225x225.jpg"},{"name":"Tesco Fusilli Pasta Twists 1Kg","url":null,"price":null,"price_per_unit":null,"image_url":"https:\\/\\/img.tesco.com\\/Groceries\\/pi\\/305\\/5000119532305\\/IDShot_225x225.jpg"},{"name":"Tesco Fusilli 300G","url":null,"price":"1.25","price_per_unit":"0.42\\/100g","image_url":"https:\\/\\/img.tesco.com\\/Groceries\\/pi\\/579\\/5057753722579\\/IDShot_225x225.jpg"},{"name":"Napolina Fusilli Pasta 500G","url":null,"price":"1.28","price_per_unit":"2.56\\/kg","image_url":"https:\\/\\/img.tesco.com\\/Groceries\\/pi\\/402\\/5000184592402\\/IDShot_225x225.jpg"},{"name":"Napolina Fusilli Pasta 1Kg","url":null,"price":"2.28","price_per_unit":"2.28\\/kg","image_url":"https:\\/\\/img.tesco.com\\/Groceries\\/pi\\/458\\/5000232823458\\/IDShot_225x225.jpg"},{"name":"Napolina Penne Pasta 1Kg","url":null,"price":"2.28","price_per_unit":"2.28\\/kg","image_url":"https:\\/\\/img.tesco.com\\/Groceries\\/pi\\/397\\/5000232823397\\/IDShot_225x225.jpg"}]}]';
+
+@Component({
+  name: 'Prices',
+  components: {
+  }
+})
+export default class extends Vue {
+  private loading = false;
+  private params = {
+    include: [],
+    query: '',
+    limit: 10
+  }
+
+  private results: IShopListings[] = [];
+
+  get allShops() {
+    return ShopsModule.shops;
+  }
+
+  created() {
+    this.getData();
+  }
+
+  private async getData() {
+    await ShopsModule.GetShops({});
+  }
+
+  private async searchPrices() {
+    this.loading = true;
+    this.results = JSON.parse(jsonData);
+    this.loading = false;
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+  .el-checkbox-group {
+    padding: 10px;
+  }
+  .box-card {
+    margin: 5px;
+    max-width: 100rem;
+    .item-price {
+      font-weight: bold;
+    }
+  }
+  .search-terms {
+    max-width: 700px;
+    margin: 10px;
+  }
+  .item {
+    size: 50px;
+    max-width: 250px;
+  }
+  .card-img {
+    height: auto;
+    max-width: 30%;
+  }
+
+</style>
diff --git a/client/src/views/shops/CreateShop.vue b/client/src/views/shops/CreateShop.vue
index c462d87..6b25d5b 100644
--- a/client/src/views/shops/CreateShop.vue
+++ b/client/src/views/shops/CreateShop.vue
@@ -1,109 +1,138 @@
 <template>
   <div class="app-container">
-    <el-table
-      :data="shops"
-      style="width: 100%"
-    >
-      <el-table-column
-        v-for="prop in tableConfig.props"
-        :key="prop.name"
-        :label="prop.label"
-        :align="prop.align"
-        :min-width="prop.minWidth"
+    <el-card class="box-card">
+      <div
+        slot="header"
+        class="clearfix"
       >
-        <template slot-scope="{row}">
-          <span v-if="prop.type === 'text'">
-            {{ row[prop.name] }}
-          </span>
-          <span v-if="prop.type === 'timestamp'">
-            {{ new Date(row[prop.name]).toDateString() }}
-          </span>
-          <span v-if="prop.type === 'tag'">
-            <el-tag
-              :type="prop.tag[row[prop.name]]"
-            >
-              {{ row[prop.name] }}
-            </el-tag>
-          </span>
-        </template>
-      </el-table-column>
-
-      <el-table-column
-        v-if="tableConfig.actions"
-        fixed="right"
-        label="Actions"
+        <span>Create a new role</span>
+      </div>
+      <el-form
+        ref="loginForm"
+        :model="createForm"
+        :rules="createRules"
+        autocomplete="on"
+        label-position="left"
       >
-        <template slot-scope="{row}">
-          <el-button
-            type="primary"
-            plain
-            size="small"
-            icon="el-icon-edit"
-            @click="$router.push('/users/edit/' + row.id)"
-          >
-            Edit
-          </el-button>
+        <el-form-item
+          prop="name"
+          label="Shop Name"
+        >
+          <el-input
+            v-model="createForm.name"
+            type="text"
+          />
+        </el-form-item>
+
+        <el-form-item
+          prop="baseUrl"
+          label="Base URL"
+        >
+          <el-input
+            v-model="createForm.baseUrl"
+            type="text"
+          />
+        </el-form-item>
+
+        <el-form-item
+          prop="queryUrl"
+          label="Query URL"
+        >
+          <el-input
+            v-model="createForm.queryUrl"
+            type="text"
+          />
+        </el-form-item>
+
+        <el-form-item
+          prop="renderJS"
+          label="Render JavaScript"
+        >
+          <el-switch v-model="createForm.renderJS" />
+        </el-form-item>
 
-          <el-button
-            type="danger"
-            plain
-            size="small"
-            icon="el-icon-delete"
-            @click="handleDeleteDialog(row)"
-          >
-            Delete
-          </el-button>
-        </template>
-      </el-table-column>
-    </el-table>
+        <el-form-item
+          prop="selectors"
+          label="CSS Selectors"
+        >
+          <div class="editor-container">
+            <json-editor
+              ref="jsonEditor"
+              v-model="createForm.selectorJson"
+            />
+          </div>
+        </el-form-item>
+
+        <el-button
+          :loading="loading"
+          type="primary"
+          @click="handleCreate"
+        >
+          Create
+        </el-button>
+
+        <el-button
+          @click="$router.back()"
+        >
+          Cancel
+        </el-button>
+      </el-form>
+    </el-card>
   </div>
 </template>
 
 <script lang="ts">
 import { Component, Vue } from 'vue-property-decorator';
-import { ShopsModule } from '@/store/modules/shops';
+import { Form as ElForm, Input } from 'element-ui';
+import JsonEditor from '@/components/JsonEditor/index.vue';
 
 @Component({
   name: 'CreateShop',
-  components: {}
+  components: {
+    JsonEditor
+  }
 })
 export default class extends Vue {
-  private loading = true;
+  private loading = false;
 
-  private tableConfig = {
-    props: [
-      { name: 'id', type: 'text', label: 'ID', align: 'center', minWidth: 20 },
-      { name: 'name', type: 'text', label: 'Name', align: 'center', minWidth: 80 },
-      { name: 'created_at', type: 'timestamp', label: 'Created', align: 'center', minWidth: 80 },
-      { name: 'url', type: 'text', label: 'URL', align: 'center', minWidth: 80 },
-      {
-        name: 'render_javascript',
-        label: 'Renders JavaScript',
-        type: 'tag',
-        align: 'center',
-        minWidth: 80,
-        tag: {
-          true: 'primary',
-          false: 'info'
-        }
-      }
-    ],
-    actions: [
-      { text: 'Edit', type: 'primary', icon: 'el-icon-edit', size: 'small', goto: '/dashboard' },
-      { text: 'Delete', type: 'danger', icon: 'el-icon-delete', size: 'small', goto: '/dashboard' }
-    ]
-  }
+  private createForm = {
+    name: '',
+    baseUrl: '',
+    renderJS: false,
+    queryUrl: '',
+    selectorJson: {}
+  };
 
-  get shops() {
-    return ShopsModule.shops;
+  private createRules = {
+    name: [{ trigger: 'blur', required: true, message: 'Please enter a name' }],
+    baseUrl: [{ trigger: 'blur', required: true, message: 'Please enter the base URL' }],
+    queryUrl: [{ trigger: 'blur', required: true, message: 'Please enter the query URL' }],
+    selectorJson: [{ trigger: 'blur', required: true, message: 'Please add the CSS selector code' }]
+  };
+
+  mounted() {
+    (this.$refs.name as Input).focus();
+    (this.$refs.description as Input).focus();
   }
 
-  created() {
-    ShopsModule.GetShops({});
+  private handleCreate() {
+    (this.$refs.loginForm as ElForm).validate(async(valid: boolean) => {
+      if (valid) {
+        this.loading = true;
+      } else {
+        this.$message.error('validation failed');
+      }
+    });
   }
 }
 </script>
 
 <style lang="scss" scoped>
-
+.editor-container {
+  position: relative;
+  height: 100%;
+}
+.box-card {
+  max-width: 60rem;
+}
 </style>
diff --git a/client/src/views/users/EditUser.vue b/client/src/views/users/EditUser.vue
index 700029f..0f9ec80 100644
--- a/client/src/views/users/EditUser.vue
+++ b/client/src/views/users/EditUser.vue
@@ -2,6 +2,8 @@
   <div>
     Edit
     {{ user }}
+
+    <el-button type="primary" @click="handleClick">Log</el-button>
   </div>
 </template>
 
@@ -24,8 +26,15 @@ export default class extends Vue {
     status: ''
   };
 
+  private userID = '';
+
   public cancel() {
     router.back();
   }
+
+  public handleClick() {
+    console.log(this.$route);
+  }
+
 }
 </script>
diff --git a/client/vue.config.js b/client/vue.config.js
index c342beb..1e908a2 100644
--- a/client/vue.config.js
+++ b/client/vue.config.js
@@ -15,6 +15,7 @@ module.exports = {
   devServer: {
     port: devServerPort,
     open: true,
+    disableHostCheck: true,
     overlay: {
       warnings: false,
       errors: true
diff --git a/imgs/1-login.png b/imgs/1-login.png
new file mode 100644
index 0000000..62d8be4
Binary files /dev/null and b/imgs/1-login.png differ
diff --git a/imgs/2-admin-dashboard.png b/imgs/2-admin-dashboard.png
new file mode 100644
index 0000000..412dcee
Binary files /dev/null and b/imgs/2-admin-dashboard.png differ
diff --git a/imgs/3.0-user-CRUD.png b/imgs/3.0-user-CRUD.png
new file mode 100644
index 0000000..4dbbf3b
Binary files /dev/null and b/imgs/3.0-user-CRUD.png differ
diff --git a/imgs/3.1-user-create.png b/imgs/3.1-user-create.png
new file mode 100644
index 0000000..d35042f
Binary files /dev/null and b/imgs/3.1-user-create.png differ
diff --git a/imgs/4-user-profile.png b/imgs/4-user-profile.png
new file mode 100644
index 0000000..08c003d
Binary files /dev/null and b/imgs/4-user-profile.png differ
diff --git a/imgs/5.0-shop-CRUD.png b/imgs/5.0-shop-CRUD.png
new file mode 100644
index 0000000..4d1604f
Binary files /dev/null and b/imgs/5.0-shop-CRUD.png differ
diff --git a/imgs/5.1-shop-select.png b/imgs/5.1-shop-select.png
new file mode 100644
index 0000000..ebd61e3
Binary files /dev/null and b/imgs/5.1-shop-select.png differ
diff --git a/imgs/6-prices-display.png b/imgs/6-prices-display.png
new file mode 100644
index 0000000..92be691
Binary files /dev/null and b/imgs/6-prices-display.png differ
diff --git a/server/app/service/scraperservice.py b/server/app/service/scraperservice.py
index 86d56d5..637786d 100644
--- a/server/app/service/scraperservice.py
+++ b/server/app/service/scraperservice.py
@@ -39,7 +39,7 @@ async def query_listings(
 
         # Use Splash to render our page if required
         if self._shop.render_javascript:
-            params = {"url": url, "timeout": "5", "images": 0}
+            params = {"url": url, "timeout": "7", "images": 0}
             base_url = "http://splash-browser:8050/render.html"
             html = await fetch_page(base_url, client=client, params=params)
         else: