Interactive matcher with vuejs
Tags: vue, webdev
Published: 2022-04-15 07:13:21

 When you apply to a new job as a front-end developer, you can get any type of interview and some tasks from some level of the interview process. Nowadays we have 2 kinds of tasks "many popular test (interview) tasks" and "not popular test tasks" for frontend developers.BTW many companies think that test tasks from their companies are unique, and they ask that you(candidate) keep it secret and you will not publish a public GitHub repo. But when your search keywords of the task, in many cases you can find complete GitHub public projects with the same task with the same API.. so on. Let's solve this task [interactive matcher](https://github.com/Ilyoskhuja/ft/blob/master/Frontend%20Test.pdf) from interview tasks with vuejs and you can find the complete project from this public GitHub [repo](https://github.com/Ilyoskhuja/ft).
  We will create new vuejs project, inside this project we create assets folder with 2 cvs file 1 is "sound_recordings.cvs" 2 is "sound_recordings_input_report.cvs".
For working with State management we will create store.js inside the project src folder and we will add the code below:

```
import Vue from "vue";
import Vuex from "vuex";
import recordingsdata from "./assets/sound_recordings.csv";

import inputsdata from "./assets/sound_recordings_input_report.csv";
Vue.use(Vuex);

const appendId = (tempArray) => {
  return tempArray.map((item) => ({
    id: Math.random().toString().slice(2),
    ...item,
  }));
};

const postRecordingsData = appendId(recordingsdata);
const postInputs = appendId(inputsdata);

export const store = new Vuex.Store({
  state: {
    // origin
    recordings: postRecordingsData,

    // left aside list
    inputs: postInputs,

    // right aside list
    results: postRecordingsData,

    selectedInputsItem: {},
    selectedResultsItem: {},
  },
  mutations: {
    SET_STATE(state, payload) {
      Object.keys(payload).forEach((key) => {
        if (key in state) {
          state[key] = payload[key];
        }
      });
    },
    S_INPUT(state, payload) {
      state.selectedInputsItem = payload;
    },
    addInput(state) {
        console.log("state.selectedInputsItem:", state.selectedInputsItem);
	state.recordings.push(state.selectedInputsItem.item);
	state.results.push(state.selectedInputsItem.item)
	console.log("state:",state);    
    },
  },
});

```
and inside App.vue file add the code below:

```
<template>
  <div id="app">
    <Header />
    <div class="container">
      <div class="row">
        <div class="col-md-5">
          <Inputs />
        </div>
        <div class="col-md-2">
          <Actions />
        </div>
        <div class="col-md-5">
          <Database />
        </div>
      </div>
    </div>
  </div>
</template>

<script>
import Header from "./components/Header.vue";
import Inputs from "./components/inputs/Inputs.vue";
import Actions from "./components/actions/Actions.vue";
import Database from "./components/database/Database.vue";

export default {
  name: "App",
  components: {
 
    Header,
    Inputs,
    Actions,
    Database,
  },
};
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #181c20;
  /* margin-top: 60px; */
}
</style>

```
In the component folder, we will create Inputs, Database, and Actions components, Actions add the code below:

```
<template>
 <div>
  <button class="btn btn-outline-primary" @click="register"><i class="fa fa-arrow-right"></i> Register</button>
  </div>
</template>
<style scoped>
</style>
<script>

import { mapMutations } from "vuex";
export default {

  methods: {
    ...mapMutations(["SET_STATE"]),

    register() {
      console.log("selected:----",this.$store.state.selectedInputsItem);
      this.$store.commit('addInput')
    
    },
  },
};
</script>
```
For Database component code like this:

```
<template>
  <div>
    <input type="text" v-model="search" placeholder="Search title.." />

    <div>Records: {{ recordings.length }}</div>
    <ul class="list-group list-group-flush">
      <li class="mt-4" v-for="item in recordings" :key="item.id">
        <div class="card">
          <div class="card-body">
            <h5 style="">{{ item.title }}</h5>
            <p>{{ item.artist }}</p>
            <div style="display: flex; justify-content: space-between">
              <h6>{{ item.isrc }}</h6>
              <h6>{{ item.duration | formatTime }}</h6>
            </div>
          </div>
        </div>
      </li>
    </ul>
  </div>
</template>
<script>
export default {
  data() {
    return {
      search: "",
    };
  },
  computed: {
    recordings() {
      const searchrg=new RegExp(this.search,'i');
      return this.$store.state.results.filter((item) =>
        [item.isrc, item.artist, item.title].some((el) =>
          el && el.toLowerCase().match(searchrg)
        )
      );
    },
  },

  filters: {
    formatTime(time) {
      if (time > 0) {
        var mins = ~~((time % 3600) / 60);
        var secs = ~~time % 60;

        var ret = "";
        ret += "0" + mins + "m" + " " + (secs < 10 ? "0" : "");
        ret += "" + secs + "s";
        return ret;
      } else return "";
    },
  },
  methods: {
   
};
</script>
<style scoped>
ul {
  list-style-type: none;
  overflow: scroll;
  overflow-x: hidden;
  height: 80vh;
}
</style>
```
Finally for inputs add code below:

```
<template>
  <ul class="list-group list-group-flush">
    <div>Inputs: {{ inputs.length }}</div>
    <li class="mt-4" v-for="item in inputs" :key="item.id">
      <div class="card " v-bind:class="{isActive:activeItem===item}" @click="findMatch(item)">
        <div class="card-body">
          <h5 style="">{{ item.title }}</h5>
          <p>{{ item.artist }}</p>
          <div style="display: flex; justify-content: space-between">
            <h6>{{ item.isrc }}</h6>
            <h6>{{ item.duration | formatTime }}</h6>
          </div>
        </div>
      </div>
    </li>
  </ul>
</template>
<style scoped>
ul {
  list-style-type: none;
  overflow: scroll;
  overflow-x: hidden;
  height: 80vh;
}
.isActive{
  border:18px solid #e19eae ;
}
</style>
<script>
import { mapMutations } from "vuex";
export default {
  data() {
    return {
      activeItem:null
    };
  },
  computed: {
    inputs() {
      return this.$store.state.inputs;
    },
  },
  methods: {
    ...mapMutations(["SET_STATE","S_INPUT"]),

    findMatch(item) {
      console.log(item);
      this.activeItem=item;
     this.S_INPUT({item});
 
   
      const targetFields =(item.title.toUpperCase()+' '+item.artist.toUpperCase() +(item.isrc?' ' + item.isrc?.toUpperCase():'')).split(' ');
      
      
      const t = this.$store.state.recordings.filter((elem) => {
         const ele=(elem.title.toUpperCase()+' '+elem.artist.toUpperCase() +' '+elem.isrc?.toUpperCase()).split(' ');
       
         return ele.filter(field=> targetFields.includes(field)).length>0;
         

     
      this.SET_STATE({
        results: t || [],
      });

    },
  },
  filters: {
    formatTime(time) {
      if (time > 0) {
        var mins = ~~((time % 3600) / 60);
        var secs = ~~time % 60;

        var ret = "";
        ret += "0" + mins + "m" + " " + (secs < 10 ? "0" : "");
        ret += "" + secs + "s";
        return ret;
      } else return "";
    },
  },
};
</script>
```
When you run the project you can see it in the browser tab image below.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4qqpckld9klnglwl55ma.png)


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/abas04fw3wmxx575zpii.png)


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tkj8dpgfn1gnh76wcovh.png)

I hope this article helps you to pass the interview process.