# Complete source code for CRM sample

# public/index.html

<html>

<head>
  <title>Acme Inc CRM</title>
  <!-- #region header --> 	
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.1/dist/css/bootstrap.min.css" rel="stylesheet"
    integrity="sha384-+0n0xVW2eSR5OomGNYDnhzAbDsOXxcvSN1TPprVMTNDbiYZCxYbOOl7+AMvyTG2x" crossorigin="anonymous">
  <!-- development version, includes helpful console warnings -->
  <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
  <!-- #endregion header --> 	
</head>

<body>
  <div class="container my-4">
    <h1>Acme Inc CRM</h1>
    <p>Welcome to our customer database</p>

    <div id="app" class="my-4">
      <div v-if="showCustomerList">
        <h2>Customer list</h2>
        <table class="table">
          <thead>
            <th>Organisation number</th>
            <th>Name</th>
            <th>Phone</th>
            <th>Address</th>
          </thead>
          <tbody>
            <tr v-for="company in companies" :key="company.organisationNumber">
              <td>{{company.organisationNumber}}</td>
              <td>{{company.name}}</td>
              <td>{{company.phoneNumber}}</td>
              <td>{{company.address}}</td>
            </tr>
          </tbody>
        </table>
        <button class="btn btn-primary" @click="showCustomerDialog = true; showCustomerList = false">Add new customer</button>
        <div v-if="added" class="my-2 alert alert-success" role="alert">
          Customer added to CRM.
        </div>
      </div>
      <div v-show="showCustomerDialog">
        <h2>Add new customer</h2>
        <form @submit.prevent="search">
          <input class="form-control" type="text" v-model="name" placeholder="Enter company name"/>
          <br />
          <input class="btn btn-success" type="submit" value="Search" />
          <input @click="showCustomerDialog = false; showCustomerList = true;" class="btn btn-danger" type="reset" value="Cancel" />
        </form>
        <span v-if="numberOfHits != null && numberOfHits >= 0">Search results: {{numberOfHits}}</span>
        <!-- #region searchResultList --> 	
        <ul class="list-group list-group-flush">
          <li class="list-group-item" v-for="res in searchResults" :key="res.companyId">
            <button class="btn btn-primary" @click="add(res)">Add to CRM</button>
            {{res.name}} ({{res.organisationNumber}})
          </li>
        </ul>
        <!-- #endregion searchResultList --> 	
      </div>
    </div>

    <script src="main.js"></script>
  </div>
</body>

</html>

# public/main.js

var app = new Vue({
    el: '#app',
    data() {
      return {
        added: false,
        showCustomerList: true,
        showCustomerDialog: false,
        name: '',
        searchResults: [],
        numberOfHits: null,
        companies: JSON.parse(localStorage.getItem('companies')) || []
      }
    },
    methods: {
      add(company) {
        this.companies.push({
          name: company.name,
          organisationNumber: company.organisationNumber,
          phone: company.phoneNumbers && company.phoneNumbers.telephoneNumber,
          address: company.postalAddress && Object.keys(company.postalAddress).map(k => company.postalAddress[k]).filter(v => v != null).join(', ')
        })
        // Synchronize localStorage
        localStorage.setItem('companies', JSON.stringify(this.companies))

        // Hide search and reset model
        this.added = true;
        this.showCustomerDialog = false;
        this.showCustomerList = true;
        this.searchResults = []
        this.numberOfHits = null;
        this.name = ''
      },
      //#region search
      search() {
        fetch(`/search?name=${this.name}`)
          .then(r => {
            if (r.ok) {
              return r.json()
            } else { 
              return {searchResults: [], numberOfHits: 0}
            }
          })
          .then(data => {
            this.searchResults = data.companies
            this.numberOfHits = data.numberOfHits
          })
      }
      //#endregion search
    }
  })

# routes/search.js

const express = require('express');
const router = express.Router();
const axios = require('axios');

const API_TOKEN = process.env.API_TOKEN // Your API token
const API_ENDPOINT = 'https://api.proff.no/api/companies/eniropro/NO'

const httpClient = axios.create({
    timeout: 1000,
    baseURL: API_ENDPOINT,
    headers: {
        'Authorization': `Token ${API_TOKEN}`,
        'api-version': '1.1'
    },
    responseType: 'json'
});

/* GET home page. */
router.get('/', async function(req, res, next) {
    try {
        const proffApiResponse = await httpClient.get(`?name=${encodeURIComponent(req.query.name)}`);
        res.json(proffApiResponse.data);
    } catch (error) {
        next(error)
    }
});

module.exports = router;

# app.js

var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');

var app = express();

app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
//#region searchRoute
var searchRouter = require('./routes/search');
app.use('/search', searchRouter);
//#endregion searchRoute
module.exports = app;