DEV Community

spiritupbro
spiritupbro

Posted on

Step 1 - Create Our Frontend Using Nuxtjs

Clone the repo of this open source marketplace frontend here :

DEPLOY TO DO BUTTON

Deploy to DO

Build Setup

# install dependencies
$ yarn install

# serve with hot reload at localhost:3000
$ yarn dev

# build for production and launch server
$ yarn build
$ yarn start

# generate static project
$ yarn generate
Enter fullscreen mode Exit fullscreen mode

For detailed explanation on how things work, check out Nuxt.js docs.




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

yarn create nuxt-app opensource-marketplace-frontend
Enter fullscreen mode Exit fullscreen mode

Create your application using this settings :

? 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
Enter fullscreen mode Exit fullscreen mode

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

homepage

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

...
vuetify: {
    defaultAssets: {icons: 'fa'},
    customVariables: ['~/assets/variables.scss'],
    theme: {
      dark: false,
      themes: {
        dark: {
...
Enter fullscreen mode Exit fullscreen mode

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 :

<template>
  <v-app >
<nuxt/>
  </v-app>
</template>
Enter fullscreen mode Exit fullscreen mode

After you finish filling the content inside layouts/default.vue, go to pages/index.vue and write this code, to create our homepage :

<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>

Enter fullscreen mode Exit fullscreen mode

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 :

<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>
Enter fullscreen mode Exit fullscreen mode

And then you will got this as a homepage :

homepage

Go to nuxt.config.js and add server object

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: {},
}
Enter fullscreen mode Exit fullscreen mode

Create a login page go to pages/login.vue to create a login form that can login to github

<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>
Enter fullscreen mode Exit fullscreen mode

Create the component in components/Login.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>

Enter fullscreen mode Exit fullscreen mode

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.

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}
Enter fullscreen mode Exit fullscreen mode

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=

Enter fullscreen mode Exit fullscreen mode

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

<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>

Enter fullscreen mode Exit fullscreen mode

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

<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>

Enter fullscreen mode Exit fullscreen mode

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 :

<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>

Enter fullscreen mode Exit fullscreen mode

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 :

<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>

Enter fullscreen mode Exit fullscreen mode

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

<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>

Enter fullscreen mode Exit fullscreen mode

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

<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>

Enter fullscreen mode Exit fullscreen mode

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 :

<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>

Enter fullscreen mode Exit fullscreen mode

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

<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>

Enter fullscreen mode Exit fullscreen mode

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

Top comments (0)