Step 1 - Create Our Frontend Using Nuxtjs
Tags: dohackathon
Published: 2021-01-03 03:43:46

Clone the repo of this open source marketplace frontend here :

{% github https://github.com/spiritbro1/opensource-marketplace-frontend %}

First of all create a nuxtjs application by using this command :

```bash
yarn create nuxt-app opensource-marketplace-frontend
```

Create your application using this settings :

```bash
? Project name: frontend
? Programming language: TypeScript
? Package manager: Yarn
? UI framework: Vuetify.js
? Nuxt.js modules: Axios
? Linting tools: ESLint, Prettier
? Testing framework: Jest
? Rendering mode: Universal (SSR / SSG)
? Deployment target: Static (Static/JAMStack hosting)
? Development tools: (Press <space> to select, <a> to toggle all, <i> to invert selection)
? Continuous integration: None
? Version control system: None
```

After finish run the app using `yarn dev` and you should see this screen :

![homepage](https://rino.world/api/convertimage?url=https://imgur.com/download/MldBOzB)

Now go to the `nuxt.config.js` and search for this line below, and change `dark` to `false` :

```json
...
vuetify: {
    defaultAssets: {icons: 'fa'},
    customVariables: ['~/assets/variables.scss'],
    theme: {
      dark: false,
      themes: {
        dark: {
...
```
We will be using non dark mode for now but it's a matter of choice if you want to make it `dark` you can just put it to `true`, now after you change the theme to light then change the content in `layouts/default.vue` :

```vue
<template>
  <v-app >
<nuxt/>
  </v-app>
</template>
``` 
After you finish filling the content inside `layouts/default.vue`, go to `pages/index.vue` and write this code, to create our homepage :

```vue
<template>
  <Home :login="login" />
</template>
<script lang="ts">
import Cookies from 'js-cookie'
import Home from '@/components/Home.vue'
import { Component, Vue } from 'nuxt-property-decorator'
@Component({
  components: {
    Home,
  },
})
export default class MyStore extends Vue {
  public login: boolean =false;

      mounted(){
        this.login=Cookies.get('token') && Cookies?.get('token')?.trim() !== ''
      ? true
      : false
      }
}
</script>

```

Here you can see that `index.vue` use `Home` as their base component, let's now create our `Home` component, create `components/Home.vue` then write code like this :

```vue
<template>
  <div :class="['mx-5', 'my-5']">
    <v-row justify="center">
      <div
        :class="[`text-h1`, 'text-center']"
        v-text="'Open Source Marketplace'"
      ></div>
    </v-row>
    <v-row justify="center">
      <div
        :class="[`text-h3`, 'px-5', 'text-center', 'my-5']"
        v-text="'Here you can buy or sell open source repo'"
      ></div>
    </v-row>
    <v-row justify="center" :class="['my-2']">
      <v-btn
        :class="['my-2']"
        color="info"
        elevation="2"
        v-show="login"
        href="/dashboard"
        >DASHBOARD</v-btn
      >
      <v-btn
        :class="['my-2']"
        color="info"
        elevation="2"
        href="/login"
        v-show="!login"
        >LOGIN</v-btn
      >
      <v-btn
        :class="['my-2', 'mx-2']"
        color="error"
        elevation="2"
        v-show="login"
        @click="logout"
        >LOGOUT</v-btn
      >
    </v-row>
    <v-row>
      <v-card
        class="mx-auto my-12"
        max-width="374"
        :key="index"
        v-for="(item, index) in forSale"
      >
        <v-img height="250" :src="item.openGraphImageUrl"></v-img>

        <v-card-title>{{ item.name }}</v-card-title>

        <v-card-text>
          <div>{{ item.description }}</div>
        </v-card-text>

        <v-divider class="mx-4"></v-divider>

        <v-card-actions>
          <v-flex>$ {{ item.amount }}</v-flex>
          <v-flex v-if="item.username !== username && !item.owned"
            ><div class="d-flex justify-end">
              <v-btn
              :disabled="disabled"
                class="d-flex justify-end"
                color="deep-purple lighten-2"
                @click="buyNow(item._id)"
                text
              >
                Buy Now
              </v-btn>
            </div></v-flex
          >
          <v-flex v-if="item.username === username || item.owned"
            ><div class="d-flex justify-end">

              <v-btn
              :disabled="disabled"
                class="d-flex justify-end"
                color="deep-purple lighten-2"
                :href="item.url"
                target="_blank"
                text
              >
                GO TO URL
              </v-btn>
            </div></v-flex
          >
        </v-card-actions>
      </v-card>
    </v-row>
  </div>
</template>
<script lang="ts">
import Cookies from 'js-cookie'
import { Component, Vue, Prop } from 'nuxt-property-decorator'
import {configs} from '@/utils/configs.js'
@Component
export default class MyStore extends Vue {
  @Prop({ required: true }) readonly login!: boolean
  public forSale: Array<object> = []
  public username: string = ''
  public disabled: boolean = false
  public buy_paypal_url: string = ""
  logout() {
    Cookies.remove('token')
    window.location.reload()
  }
  async mounted() {
console.log(configs)
    const token = Cookies.get('token')
    const ownedRepoUrl = configs.get_owned_repo_url
    const url = configs.for_sale_repo_url
    try {
      const { data } = await this.$axios.get(`${url}?token=${token}`)
      const ownedRepo = await this.$axios.get(`${ownedRepoUrl}?token=${token}`)

      this.forSale = data.data.data.map((a:any) => {
        if (
          ownedRepo.data.data.filter((b:any) => b.owner_username === a.username&&a.name===b.name)
            .length > 0
        ) {
          a.owned = true
          return a
        } else {
          return a
        }
      })
      this.username = data.data.username
    } catch (e) {
      console.log(e.message)
    }
  }
  async buyNow(_id:any) {
    this.disabled=true;
    const token = Cookies.get('token')
    const url=<string>configs.buy_paypal_url;
    const res = await this.$axios.post(url, { _id, token })
    if (res.data) {
      const link=res.data.result.links.filter((a:any)=>a.rel==="approve")[0].href
      window.location.href=link;
    }else{
      this.disabled=false;
      alert("error")
    }
  }
}
</script>
```
And then you will got this as a homepage :

![homepage](https://rino.world/api/convertimage?url=https://imgur.com/download/Dc3CLbs)

Go to `nuxt.config.js` and add `server` object

```javascript
import colors from 'vuetify/es5/util/colors'

export default {
  // Target (https://go.nuxtjs.dev/config-target)
  server: {
    port: 8080, // default: 3000
    host: '0.0.0.0' // default: localhost
  },
  target: 'server',
  env: {
    github_client_id: process.env.github_client_id,
    paypal_client_id:process.env.paypal_client_id,
    url:process.env.url,
    prefix:process.env.prefix,
  },
  // Global page headers (https://go.nuxtjs.dev/config-head)
  head: {
    titleTemplate: '%s ',
    title: 'Open Source Marketplace',
    meta: [
      { charset: 'utf-8' },
      { name: 'viewport', content: 'width=device-width, initial-scale=1' },
      { hid: 'description', name: 'description', content: '' },
    ],
    link: [{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }],
  },

  // Global CSS (https://go.nuxtjs.dev/config-css)
  css: [],

  // Plugins to run before rendering page (https://go.nuxtjs.dev/config-plugins)
  plugins: [],

  // Auto import components (https://go.nuxtjs.dev/config-components)
  components: true,

  // Modules for dev and build (recommended) (https://go.nuxtjs.dev/config-modules)
  buildModules: [
    // https://go.nuxtjs.dev/typescript
    '@nuxt/typescript-build',
    // https://go.nuxtjs.dev/vuetify
    '@nuxtjs/vuetify',
    '@nuxtjs/dotenv'
  ],

  // Modules (https://go.nuxtjs.dev/config-modules)
  modules: [
    // https://go.nuxtjs.dev/axios
    '@nuxtjs/axios',
  ],

  // Axios module configuration (https://go.nuxtjs.dev/config-axios)
  axios: {},

  // Vuetify module configuration (https://go.nuxtjs.dev/config-vuetify)
  vuetify: {
    defaultAssets: {icons: 'fa'},
    customVariables: ['~/assets/variables.scss'],
    theme: {
      dark: false,
      themes: {
        dark: {
          primary: colors.blue.darken2,
          accent: colors.grey.darken3,
          secondary: colors.amber.darken3,
          info: colors.teal.lighten1,
          warning: colors.amber.base,
          error: colors.deepOrange.accent4,
          success: colors.green.accent3,
        },
      },
    },
  },

  // Build Configuration (https://go.nuxtjs.dev/config-build)
  build: {},
}
```
Create a login page go to `pages/login.vue` to create a login form that can login to github

```vue
<template>
  <Login />
</template>
<script lang="ts">
import Login from '@/components/Login.vue'
import { Component, Vue } from 'nuxt-property-decorator'
@Component({
  components: {
    Login,
  },
})
export default class MyStore extends Vue {}
</script>
```
Create the component in `components/Login.vue` 

```vue
<template>

   <v-row justify="center" align="center">
<v-card>
  <v-card-text>
    <v-row :class="['my-3','mx-3','text-h4']">LOGIN USING YOUR GITHUB ACCOUNT</v-row>
    <v-row align="center" justify="center">
      <v-btn @click="login_github"><v-icon class="mx-2 my-2">fab fa-github</v-icon>LOGIN</v-btn>
    </v-row>
  </v-card-text>
</v-card>
   </v-row>

</template>
<script lang="ts">
import { Component, Vue } from 'nuxt-property-decorator'
import {configs} from '@/utils/configs.js'
@Component
export default class MyStore extends Vue {
  login_github(){
    window.location.href="https://github.com/login/oauth/authorize?scope=repo&client_id="+configs.github_client_id
  }
}
</script>

```
Notice that we added `configs.github_client_id` you can get that by creating oauth apps in github developer setting `https://github.com/settings/developers` create new oauth apps then copy paste the github client id there,now let's create our environment variable create a folder called `utils` and then create a file called `configs.js` this will be our global configuration to get variable like github_client_id, github_client_secret etc.

```javascript
const configs={}
configs.url=process.env.url;
configs.prefix=process.env.prefix;
configs.github_client_id=process.env.github_client_id
configs.paypal_client_id=process.env.paypal_client_id
configs.save_paypal_url=`${configs.url}${configs.prefix}/save-paypal`
configs.buy_paypal_url=`${configs.url}${configs.prefix}/buy-paypal`
configs.for_sale_repo_url=`${configs.url}${configs.prefix}/for-sale-repo`
configs.disconnect_paypal_url=`${configs.url}${configs.prefix}/disconnect-paypal`
configs.redirect_uri_paypal=`${configs.url}${configs.prefix}/paypal-auth`
configs.unlist_repo_url=`${configs.url}${configs.prefix}/unlist-repo`
configs.sell_repo_url=`${configs.url}${configs.prefix}/sell-repo`
configs.get_all_repo_url=`${configs.url}${configs.prefix}/get-all-repo`
configs.get_profile_url=`${configs.url}${configs.prefix}/get-profile`
configs.get_for_sale_url=`${configs.url}${configs.prefix}/get-for-sale-repo`
configs.get_owned_repo_url=`${configs.url}${configs.prefix}/get-owned-repo`
configs.get_paypal_url=`${configs.url}${configs.prefix}/get-paypal`
configs.list_repo_url=`${configs.url}${configs.prefix}/list-repo`
configs.repo_detail_url=`${configs.url}${configs.prefix}/repo-detail`
module.exports={configs}
```

When in development you also need `.env` file to make it easy to handle the environment variable go ahead and create `.env` with this value :

```
github_client_id=<github_client_id>
paypal_client_id=<paypal_client_id>
url=http://localhost:4000
prefix=

```

Change the value according to your need,after that create a dashboard go to `pages/dashboard.vue` and write this code :

```vue
<template>
  <Dashboard :login="login" />
</template>
<script lang="ts">
import Cookies from 'js-cookie'
import Dashboard from '@/components/Dashboard.vue'
import { Component, Vue } from 'nuxt-property-decorator'
@Component({
  components: {
    Dashboard,
  },
})
export default class MyStore extends Vue {
  public login: boolean =false;

      mounted(){
this.login=Cookies.get('token') && Cookies?.get('token')?.trim() !== ''
      ? true
      : false
      }
}
</script>

```

Create a dashboard component to make it more easy to maintain late go ahead and create `components/Dashboard.vue` and write this code :

```vue
<template>
  <v-container>
    <v-row justify="space-between">
      <v-col cols="4" class="text-h4 font-weight-bold"
        ><a href="/" style="text-decoration: none; color: black">Home</a></v-col
      >
      <v-col cols="4" class="d-flex justify-end"
        ><v-btn @click="logout">LOGOUT</v-btn></v-col
      >
    </v-row>
    <DashboardInfo
      :disabled="disabled"
      :username="username"
      :profilePhoto="profilePhoto"
      :paypalToken="paypalToken"
      :paypalBalance="paypalBalance"
      @disconnect="disconnectPaypal"
      @connect="connectPaypal"
    />
    <v-row justify="space-between">
      <v-col cols="4" class="text-h4 font-weight-bold"
        >For Sale Repo List</v-col
      >
    </v-row>
    <DashboardForSale
      :disabled="disabled"
      :ownedRepo="false"
      :amountRefresh="amountRefresh"
      @get-all-repo="getAllRepo"
      :listRepo="forSale"
      :paypalToken="paypalToken"
      @sell-repo="sellRepo($event.name, $event.amount)"
      @unlist-repo="unlistRepo($event.repo_id)"
    />
    <v-row justify="space-between">
      <v-col cols="4" class="text-h4 font-weight-bold">Owned Repo</v-col>
    </v-row>
    <DashboardOwned
      :amountRefresh="amountRefresh"
      :disabled="disabled"
      :ownedRepo="true"
      :paypalToken="paypalToken"
      :listRepo="ownedRepo"
    />
    <DashboardRepoAll
      :disabled="disabled"
      @list-repo="listRepo($event.item)"
      @get-all-repo="getAllRepo($event.after_, $event.before_)"
      :pageInfo_="pageInfo_"
      :dialog="dialog"
      :allRepo="allRepo"
    />
    <v-snackbar v-model="snackbarAmount">
      the amount shouldn't be negative or zero
    </v-snackbar>
  </v-container>
</template>

<script lang="ts">
import Cookies from 'js-cookie'
import DashboardInfo from '@/components/dashboard_components/DashboardInfo.vue'
import DashboardForSale from '@/components/dashboard_components/DashboardForSale.vue'
import DashboardOwned from '@/components/dashboard_components/DashboardOwned.vue'
import DashboardRepoAll from '@/components/dashboard_components/DashboardRepoAll.vue'
import { Component, Vue, Prop } from 'nuxt-property-decorator'
import { configs } from '@/utils/configs.js'
@Component({
  components: {
    DashboardInfo,
    DashboardForSale,
    DashboardOwned,
    DashboardRepoAll,
  },
})
export default class MyStore extends Vue {
  @Prop({ required: true }) readonly login!: boolean
  public dialog: boolean = false
  public username: string = ''
  public profilePhoto: string = ''
  public snackbarAmount: boolean = false
  public paypalToken: boolean = false
  public forSale: Array<object> = []
  public allRepo: Array<object> = []
  public ownedRepo: Array<object> = []
  public paypalBalance: number = 0
  public amountRefresh: number = 0
  public disabled: boolean = false
  public pageInfo_: object = {hasPreviousPage:false,hasNextPage:false,startCursor:"",endCursor:""}
  logout() {
    Cookies.remove('token')
    window.location.href = '/'
  }
  async created() {
    // if (!this.login) window.location.href = '/'
    this.disabled = true
    const token = Cookies.get('token')
    const url = configs.get_profile_url
    const forSaleUrl = configs.get_for_sale_url
    const ownedRepoUrl = configs.get_owned_repo_url
    const getPaypalUrl = configs.get_paypal_url
    const profile = await this.$axios.get(`${url}?token=${token}`)
    this.username = profile.data.data.login
    this.profilePhoto = profile.data.data.avatarUrl
    const forSale = await this.$axios.get(`${forSaleUrl}?token=${token}`)
    this.forSale = forSale.data.data
    const ownedRepo = await this.$axios.get(`${ownedRepoUrl}?token=${token}`)
    this.ownedRepo = ownedRepo.data.data
    const paypalToken = await this.$axios.get(`${getPaypalUrl}?token=${token}`)

    if (paypalToken.data.status && !paypalToken.data.data.disconnect) {
      this.paypalToken = true
      this.paypalBalance = paypalToken.data.data.amount
    }
    this.disabled = false
  }
  async getAllRepo(after_: string, before_: string): Promise<void> {
     this.disabled = true
    const token = Cookies.get('token')
    const url = configs.get_all_repo_url

    if (this.allRepo.length === 0) {
      const { data } = await this.$axios.get(`${url}?token=${token}`)
      this.allRepo = data.data.nodes.map(({...other})=>(this.forSale.filter(({repo_id}:any)=>repo_id===other.id).length>0?{added:true,...other}:{...other}))
      console.log(this.allRepo)
      this.pageInfo_ = data.data.pageInfo
    } else if(typeof after_==="string"||typeof before_==="string"){
      const { data } = await this.$axios.get(
        `${url}?token=${token}${before_ ? `&before=${before_}` : ''}${
          after_ ? `&after=${after_}` : ''
        }`
      )
      this.allRepo = data.data.nodes.map(({...other})=>(this.forSale.filter(({repo_id}:any)=>repo_id===other.id).length>0?{added:true,...other}:{...other}))
      this.pageInfo_ = data.data.pageInfo
    }

    this.dialog = true
    this.disabled = false
  }
  async sellRepo(name: string, amount: number) {
    // console.log(amount)
    this.disabled = true
    if (Number(amount) === 0 || Number(amount) < 0 || !amount) {

      this.snackbarAmount = true
      this.disabled = false
      return
    } else {
      const token = Cookies.get('token')
      const url = configs.sell_repo_url

      const { data } = await this.$axios.post(`${url}?token=${token}`, {
        name,
        amount,
      })
      this.amountRefresh = 0
      this.forSale = data.data
    }
    this.disabled = false
  }
  async listRepo(item: object): Promise<void> {
      this.disabled = true
    const token = Cookies.get('token')
    const url = configs.list_repo_url
    const { data } = await this.$axios.post(`${url}?token=${token}`, { item })
    this.forSale = data.data
    this.dialog=false
    this.disabled = false
  }
  async unlistRepo(id: string) {
    this.disabled = true
    const token = Cookies.get('token')
    const url = configs.unlist_repo_url
    const { data } = await this.$axios.post(`${url}?token=${token}`, { id })
    this.forSale = data.data
    this.disabled = false
  }
  async connectPaypal() {
    if (this.username.trim() !== '') {
      window.location.href = `https://www.sandbox.paypal.com/connect/?flowEntry=static&client_id=${configs.paypal_client_id}&response_type=code&scope=email&redirect_uri=${configs.redirect_uri_paypal}`
    } else {
      alert('please wait until username shown')
    }
  }
  async disconnectPaypal() {
    this.disabled = true
    if (this.username.trim() !== '') {
      const url = configs.disconnect_paypal_url
      const token = Cookies.get('token')
     await this.$axios.post(`${url}?token=${token}`)
      this.paypalToken = false
    } else {
      alert('please wait until username shown')
    }
    this.disabled = false
    if (this.forSale.length > 0) {
      this.getforSale()
    }
  }
  async getforSale() {
    this.disabled = true
    const token = Cookies.get('token')
    const url = configs.get_for_sale_url
    const { data } = await this.$axios.get(`${url}?token=${token}`)
    this.forSale = data.data
    this.disabled = false
  }
}
</script>

```

Inside `Dashboard.vue` component if you notice there is four more component inside the `Dashboard.vue` component we make that so that the component is more modular and easy to maintain for later use, now let's go ahead and create the first `Dashboard.vue` modular component `components/dashboard_components/DashboardInfo.vue` we will use this to get an info of our paypal balance and github username :

```vue
<template>
  <v-row>
    <v-card max-width="344" outlined>
      <v-list-item three-line>
        <v-list-item-content>
          <v-list-item-title class="headline mb-1">
            Github Username
          </v-list-item-title>
          <v-list-item-title class="text-h5">{{username}}</v-list-item-title>
        </v-list-item-content>

        <v-list-item-avatar size="80" color="grey">
          <v-img :src="profilePhoto" />
        </v-list-item-avatar>
      </v-list-item>
    </v-card>
    <v-card class="mx-5" max-width="344" outlined>
      <v-list-item three-line>
        <v-list-item-content>
          <v-list-item-title class="headline mb-1"> Balance </v-list-item-title>
          <v-list-item-title>
            <v-row justify="space-between">

              <v-col class="font-weight-bold text-h5" v-if="paypalToken">$ {{paypalBalance}}</v-col>

              <v-col v-if="paypalToken"><v-btn color="red" :disabled="disabled" @click="disconnect">DISCONNECT PAYPAL</v-btn></v-col>
              <v-col v-if="!paypalToken"><v-btn color="blue" :disabled="disabled" @click="connect">CONNECT PAYPAL</v-btn></v-col>
            </v-row>
          </v-list-item-title>
        </v-list-item-content>
      </v-list-item>
    </v-card>
  </v-row>
</template>
<script lang="ts">
import { Component, Vue, Prop,Emit } from 'nuxt-property-decorator'
@Component
export default class MyStore extends Vue {
  @Prop({ required: true }) readonly username!: string
  @Prop({ required: true }) readonly profilePhoto!: string
    @Prop({ required: false }) readonly paypalBalance!: number
    @Prop({ required: true }) readonly paypalToken!: boolean
    @Prop({ required: true }) readonly disabled!: boolean

@Emit()
connect(){}
@Emit()
disconnect(){}
}
</script>

```

After that go ahead and create our second modular component `components/dashboard_components/DashboardForSale.vue` we will use this component to show our repo that we can sell :

```vue
<template>
  <div>
    <v-row class="mb-2" ><v-btn @click="getAllRepo" :disabled="disabled">SELL REPO</v-btn></v-row
    >
    <v-row>
      <v-card
        width="400"
        class="mx-2 my-2"
        :key="index"
        v-for="(item, index) in listRepo"
      >
        <v-card-title>
          <v-list-item class="grow">
            <v-list-item-content>
              <v-list-item-title>{{ item.name }}</v-list-item-title>
            </v-list-item-content>

            <v-row align="center" justify="end"> {{item.isPrivate?"Private":"Public"}} </v-row>
          </v-list-item>
        </v-card-title>

        <v-card-text class="text-md font-weight-bold">
          <v-list-item>
            <v-list-item-content>{{ item.description }}</v-list-item-content>
            <v-list-item-avatar tile size="100" color="grey">
              <v-img :src="item.openGraphImageUrl" />
            </v-list-item-avatar>
          </v-list-item>
        </v-card-text>
        <v-card-action
          ><v-row class="d-flex align-content-center mx-5">
            <v-col cols="5" class="d-flex justify-start align-content-center">
              <div
                class="d-flex align-center text-h5"
                :style="{ height: '100%' }"
                v-if="item.sell === 'SELL'&&!ownedRepo"
              >
                $ {{ item.amount }}
              </div>
              <v-text-field
                v-if="item.sell === 'UNLIST'"
                label="Amount"
                type="number"
                outlined
                prefix="$"
                :disabled="!paypalToken"
                v-model="item.amount"
              ></v-text-field>
            </v-col>
            <v-col class="d-flex mt-2 justify-end">
              <v-btn
                @click="sellRepo(item.name, item.amount)"
                color="red"
                v-if="item.sell === 'UNLIST'&&!ownedRepo"
                :disabled="!paypalToken||disabled"
                >SELL</v-btn
              ><v-btn :disabled="!paypalToken||disabled" v-if="item.sell === 'SELL'&&!ownedRepo" @click="unlistRepo(item.repo_id)"
                >UNLIST</v-btn
              >
              <v-btn v-if="ownedRepo" target="_blank" :href="item.url"
                >GO TO URL</v-btn
              >
            </v-col>
          </v-row></v-card-action
        >
      </v-card>
    </v-row>
  </div>
</template>
<script lang="ts">
import { Component, Vue, Prop, Emit } from 'nuxt-property-decorator'
@Component
export default class MyStore extends Vue {
  @Prop({ required: false }) readonly refreshRepo!: void
  @Prop({ required: false }) readonly paypalToken!: boolean
  @Prop({ required: true }) readonly listRepo!: Array<object>
  @Prop({ required: true }) readonly amountRefresh!: number
  @Prop({ required: true }) readonly ownedRepo!: boolean
    @Prop({ required: true }) readonly disabled!: boolean

  public amount: number=this.amountRefresh
  @Emit()
  getAllRepo() {

  }
  @Emit()
  sellRepo(name: string, amount: number) {
    return { name, amount }
  }
  @Emit()
  unlistRepo(repo_id: string) {
    return { repo_id }
  }
}
</script>

```

After that add `components/dashboard_component/DashboardOwned.vue` to see all of our that we bought from opensource marketplace

```vue
<template>
  <div>
    <v-row class="mb-2" v-if="refreshRepo"
      ><v-btn @click="getAllRepo" :disabled="disabled">SELL REPO</v-btn></v-row
    >
    <v-row>
      <v-card
        width="400"
        class="mx-2 my-2"
        :key="index"
        v-for="(item, index) in listRepo"
      >
        <v-card-title>
          <v-list-item class="grow">
            <v-list-item-content>
              <v-list-item-title>{{ item.name }}</v-list-item-title>
            </v-list-item-content>

            <v-row align="center" justify="end"> {{item.isPrivate?"Private":"Public"}}</v-row>
          </v-list-item>
        </v-card-title>

        <v-card-text class="text-md font-weight-bold">
          <v-list-item>
            <v-list-item-content>{{ item.description }}</v-list-item-content>
            <v-list-item-avatar tile size="100" color="grey">
              <v-img :src="item.openGraphImageUrl" />
            </v-list-item-avatar>
          </v-list-item>
        </v-card-text>
        <v-card-action
          ><v-row class="d-flex align-content-center mx-5">
            <v-col cols="5" class="d-flex justify-start align-content-center">
              <div
                class="d-flex align-center text-h5"
                :style="{ height: '100%' }"
                v-if="item.sell === 'SELL'&&!ownedRepo"
              >
                $ {{ item.amount }}
              </div>
              <v-text-field
                v-if="item.sell === 'UNLIST'"
                label="Amount"
                type="number"
                outlined
                prefix="$"
                :disabled="!paypalToken"
                v-model="amount"
              ></v-text-field>
            </v-col>
            <v-col class="d-flex mt-2 justify-end">
              <v-btn
                @click="sellRepo(item._id, amount)"
                color="red"
                v-if="item.sell === 'UNLIST'&&!ownedRepo"
                :disabled="!paypalToken||disabled"
                >SELL</v-btn
              ><v-btn :disabled="!paypalToken||disabled" v-if="item.sell === 'SELL'&&!ownedRepo" @click="unlistRepo(item._id)"
                >UNLIST</v-btn
              >
              <v-btn v-if="ownedRepo" target="_blank" :href="item.url"
                >GO TO URL</v-btn
              >
            </v-col>
          </v-row></v-card-action
        >
      </v-card>
    </v-row>
  </div>
</template>
<script lang="ts">
import { Component, Vue, Prop, Emit } from 'nuxt-property-decorator'
@Component
export default class MyStore extends Vue {
  @Prop({ required: false }) readonly refreshRepo!: void
  @Prop({ required: false }) readonly paypalToken!: boolean
  @Prop({ required: true }) readonly listRepo!: Array<object>
  @Prop({ required: true }) readonly amountRefresh!: number
  @Prop({ required: true }) readonly ownedRepo!: boolean
    @Prop({ required: true }) readonly disabled!: boolean

  public amount: number=this.amountRefresh
  @Emit()
  getAllRepo() {

  }
  @Emit()
  sellRepo(_id: string, amount: number) {
    return { _id, amount }
  }
  @Emit()
  unlistRepo(_id: string) {
    return { _id }
  }
}
</script>

```

Finally add `components/dashboard_components/DashboardRepoAll.vue` to show all our repo from our github :

```vue
<template>
<v-row justify="center">
      <v-dialog
        v-model="dialog"
                max-width="600px"
      >
      <v-card>
          <v-card-title>
            <span class="headline">Choose Your Repo To Sell</span>
          </v-card-title>
          <v-card-text>
          <v-card
    class="mx-auto"
    max-width="344"
    outlined
    :key="index"
v-for="(item, index) in allRepo"
  >
    <v-list-item three-line>
      <v-list-item-content>
        <div class="overline mb-4">
          {{item.isPrivate?"PRIVATE":"PUBLIC"}}
        </div>
        <v-list-item-title class="headline mb-1">
          {{item.name}}
        </v-list-item-title>
        <v-list-item-subtitle>{{item.description}}</v-list-item-subtitle>
      </v-list-item-content>

      <v-list-item-avatar
        tile
        size="80"
        color="grey"
      ><v-img :src="item.openGraphImageUrl"/></v-list-item-avatar>
    </v-list-item>

    <v-card-actions>
      <v-btn
        v-if="!item.added"
        rounded

        color="blue"
        :disabled="disabled"
        @click="listRepo(item)"
      >
        LIST REPO
      </v-btn>

    </v-card-actions>
  </v-card>
            <v-row justify="center" class="mt-2">
               <v-btn
      class="mx-2"
      fab
      dark
      large
      color="cyan"
      :disabled="!pageInfo_.hasPreviousPage||disabled"
      @click="getAllRepo(undefined,pageInfo_.startCursor)"
    >
      <v-icon>
        fas fa-arrow-left
      </v-icon>
    </v-btn>
                <v-btn
      class="mx-2"
      fab
      dark
      large
      color="cyan"
      :disabled="!pageInfo_.hasNextPage||disabled"
      @click="getAllRepo(pageInfo_.endCursor,undefined)"
    >
      <v-icon>
        fas fa-arrow-right
      </v-icon>
    </v-btn>
            </v-row>

          </v-card-text>

        </v-card>
      </v-dialog>
    </v-row>
</template>
<script lang="ts">
import { Component, Vue, Prop, Emit } from 'nuxt-property-decorator'
@Component
export default class MyStore extends Vue {
  @Prop({ required: true }) readonly dialog!: boolean
  @Prop({ required: true }) readonly allRepo!: Array<object>
  @Prop({ required: true }) readonly pageInfo_!: object
  @Prop({ required: true }) readonly disabled!: boolean
@Emit()
  getAllRepo(after_: string,before_:string) {
    return { after_,before_ }
  }
  @Emit()
  listRepo(item:object) {
    return { item}
  }
 
}
</script>

```

Now in order for us to accept the redirect url we need route to save the token that we get from github login create `pages/callback.vue` in order to do that and write this code :

```vue
<template>
  <v-row justify="center" align="center" :class="['text-h1']"
    >REDIRECTING...</v-row
  >
</template>
<script lang="ts">
import Cookies from 'js-cookie'
import { Component, Vue } from 'nuxt-property-decorator'
import axios from 'axios'
import {configs} from '@/utils/configs.js'
@Component
export default class MyStore extends Vue {
  async mounted() {
    if (this.$route.query.token) {
      Cookies.set('token', this.$route.query.token)
      if(Cookies.get('redirect')){
        const redirect=Cookies.get('redirect');
        window.location.href=`${redirect}`;
        return
      }
      window.location.href = '/'
    } else {
      const token = Cookies.get('token')
      const { data } = await axios.post(`${configs.save_paypal_url}`, {
        token,
        email: this.$route.query.email,
      })
      if (data.status) {
        window.location.href = '/dashboard'
      } else {
        window.location.href = '/dashboard?error=save_paypal'
      }
    }
  }
}
</script>

```

We also need to have a link where we can share the repo that we sell go create one at `pages/detail`

```vue
<template>
  <v-row justify="center" align="center" :class="['text-h1']"
    >
  <v-card

    class="mx-auto my-12"
    max-width="1000"
  >
    <template slot="progress">
      <v-progress-linear
        color="deep-purple"
        height="10"
        indeterminate
      ></v-progress-linear>
    </template>

    <v-img
      height="250"
      :src="item.openGraphImageUrl"
    ></v-img>

    <v-card-title>{{item.name}}</v-card-title>

    <v-card-text>



      <div>{{item.description}}</div>
    </v-card-text>

    <v-divider class="mx-4"></v-divider>

    <v-card-title>$ {{item.amount}}</v-card-title>



    <v-card-actions>
      <v-btn
         v-if="item.username !== username_"
        color="deep-purple lighten-2"
        text
        :disabled="disabled"
        @click="buyNow(item.repo_id,item.username,item.name)"
      >
        BUY NOW
      </v-btn>

        <v-btn
        v-if="item.username === username_"
              :disabled="disabled"
                class="d-flex justify-end"
                color="deep-purple lighten-2"
                :href="item.url"
                target="_blank"
                text
              >
                GO TO URL
              </v-btn>
    </v-card-actions>
  </v-card>
</v-row>
</template>
<script lang="ts">
import Cookies from 'js-cookie'
import { Component, Vue } from 'nuxt-property-decorator'
import {configs} from '@/utils/configs.js'
import axios from 'axios'
@Component
export default class MyStore extends Vue {
public disabled:boolean=false
public username_:string=""
public item:object={}
async mounted(){
  Cookies.remove("redirect")
  const username = this.$route.query.username
      const name = this.$route.query.name
      const url=configs.repo_detail_url
      const {data}=await axios.post(`${url}`,{username,name})
this.item=data


  if(Cookies.get("token")){
        const token=Cookies.get("token")
        const _url = configs.get_profile_url
        const profile = await this.$axios.get(`${_url}?token=${token}`)
        this.username_ = profile.data.data.login
      }
}

async buyNow(id:any,username:string,name:string) {

    this.disabled=true;
    const token = Cookies.get('token')
    if(!token){
      window.location.href="/login"
       Cookies.set('redirect', `/${username}/${name}`)
      return
    }
    Cookies.remove("redirect")
    const url=<string>configs.buy_paypal_url;
    const res = await this.$axios.post(url, { id, token })
    if (res.data) {
      const link=res.data.result.links.filter((a:any)=>a.rel==="approve")[0].href
      window.location.href=link;
    }else{
      this.disabled=false;
      alert("error")
    }
  }
}
</script>

```

Now you finish creating the frontend let's go create the backend on step 2