feat: pagination
This commit is contained in:
		
							parent
							
								
									95f59c3faf
								
							
						
					
					
						commit
						19f86d5b89
					
				
					 4 changed files with 95 additions and 79 deletions
				
			
		| 
						 | 
					@ -3,65 +3,47 @@
 | 
				
			||||||
    class="border-t border-gray-200 flex items-center justify-between max-w-2xl mx-auto p-4 sm:px-6 lg:max-w-7xl"
 | 
					    class="border-t border-gray-200 flex items-center justify-between max-w-2xl mx-auto p-4 sm:px-6 lg:max-w-7xl"
 | 
				
			||||||
  >
 | 
					  >
 | 
				
			||||||
    <div class="-mt-px w-0 flex-1 flex">
 | 
					    <div class="-mt-px w-0 flex-1 flex">
 | 
				
			||||||
      <a
 | 
					      <NuxtLink
 | 
				
			||||||
 | 
					        v-if="prevPageToken"
 | 
				
			||||||
 | 
					        :to="{ name: 'index', query: { pageToken: prevPageToken } }"
 | 
				
			||||||
        href="#"
 | 
					        href="#"
 | 
				
			||||||
        class="border-t-2 border-transparent pt-4 pr-1 inline-flex items-center text-sm font-medium text-gray-500 hover:text-gray-700 hover:border-gray-300"
 | 
					        class="border-t-2 border-transparent pt-4 pr-1 inline-flex items-center text-sm font-medium text-gray-500 hover:text-gray-700 hover:border-gray-300"
 | 
				
			||||||
      >
 | 
					      >
 | 
				
			||||||
        Previous
 | 
					        Previous
 | 
				
			||||||
      </a>
 | 
					      </NuxtLink>
 | 
				
			||||||
    </div>
 | 
					 | 
				
			||||||
    <div class="hidden md:-mt-px md:flex">
 | 
					 | 
				
			||||||
      <a
 | 
					 | 
				
			||||||
        href="#"
 | 
					 | 
				
			||||||
        class="border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300 border-t-2 pt-4 px-4 inline-flex items-center text-sm font-medium"
 | 
					 | 
				
			||||||
      >
 | 
					 | 
				
			||||||
        1
 | 
					 | 
				
			||||||
      </a>
 | 
					 | 
				
			||||||
      <!-- Current: "border-pink-500 text-pink-600", Default: "border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300" -->
 | 
					 | 
				
			||||||
      <a
 | 
					 | 
				
			||||||
        href="#"
 | 
					 | 
				
			||||||
        class="border-brandlight text-brandlight border-t-2 pt-4 px-4 inline-flex items-center text-sm font-medium"
 | 
					 | 
				
			||||||
        aria-current="page"
 | 
					 | 
				
			||||||
      >
 | 
					 | 
				
			||||||
        2
 | 
					 | 
				
			||||||
      </a>
 | 
					 | 
				
			||||||
      <a
 | 
					 | 
				
			||||||
        href="#"
 | 
					 | 
				
			||||||
        class="border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300 border-t-2 pt-4 px-4 inline-flex items-center text-sm font-medium"
 | 
					 | 
				
			||||||
      >
 | 
					 | 
				
			||||||
        3
 | 
					 | 
				
			||||||
      </a>
 | 
					 | 
				
			||||||
      <span
 | 
					 | 
				
			||||||
        class="border-transparent text-gray-500 border-t-2 pt-4 px-4 inline-flex items-center text-sm font-medium"
 | 
					 | 
				
			||||||
      >
 | 
					 | 
				
			||||||
        ...
 | 
					 | 
				
			||||||
      </span>
 | 
					 | 
				
			||||||
      <a
 | 
					 | 
				
			||||||
        href="#"
 | 
					 | 
				
			||||||
        class="border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300 border-t-2 pt-4 px-4 inline-flex items-center text-sm font-medium"
 | 
					 | 
				
			||||||
      >
 | 
					 | 
				
			||||||
        8
 | 
					 | 
				
			||||||
      </a>
 | 
					 | 
				
			||||||
      <a
 | 
					 | 
				
			||||||
        href="#"
 | 
					 | 
				
			||||||
        class="border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300 border-t-2 pt-4 px-4 inline-flex items-center text-sm font-medium"
 | 
					 | 
				
			||||||
      >
 | 
					 | 
				
			||||||
        9
 | 
					 | 
				
			||||||
      </a>
 | 
					 | 
				
			||||||
      <a
 | 
					 | 
				
			||||||
        href="#"
 | 
					 | 
				
			||||||
        class="border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300 border-t-2 pt-4 px-4 inline-flex items-center text-sm font-medium"
 | 
					 | 
				
			||||||
      >
 | 
					 | 
				
			||||||
        10
 | 
					 | 
				
			||||||
      </a>
 | 
					 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
    <div class="-mt-px w-0 flex-1 flex justify-end">
 | 
					    <div class="-mt-px w-0 flex-1 flex justify-end">
 | 
				
			||||||
      <a
 | 
					      <NuxtLink
 | 
				
			||||||
 | 
					        v-if="nextPageToken"
 | 
				
			||||||
 | 
					        :to="{ name: 'index', query: { pageToken: nextPageToken } }"
 | 
				
			||||||
        href="#"
 | 
					        href="#"
 | 
				
			||||||
        class="border-t-2 border-transparent pt-4 pl-1 inline-flex items-center text-sm font-medium text-gray-500 hover:text-gray-700 hover:border-gray-300"
 | 
					        class="border-t-2 border-transparent pt-4 pl-1 inline-flex items-center text-sm font-medium text-gray-500 hover:text-gray-700 hover:border-gray-300"
 | 
				
			||||||
      >
 | 
					      >
 | 
				
			||||||
        Next
 | 
					        Next
 | 
				
			||||||
      </a>
 | 
					      </NuxtLink>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
  </nav>
 | 
					  </nav>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<script lang="ts">
 | 
				
			||||||
 | 
					import Vue from 'vue'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default Vue.extend({
 | 
				
			||||||
 | 
					  name: 'SimplePagination',
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  props: {
 | 
				
			||||||
 | 
					    nextPageToken: {
 | 
				
			||||||
 | 
					      type: String,
 | 
				
			||||||
 | 
					      default: () => {
 | 
				
			||||||
 | 
					        return ''
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    prevPageToken: {
 | 
				
			||||||
 | 
					      type: String,
 | 
				
			||||||
 | 
					      default: () => {
 | 
				
			||||||
 | 
					        return ''
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,8 +1,5 @@
 | 
				
			||||||
<template>
 | 
					<template>
 | 
				
			||||||
  <div
 | 
					  <div>
 | 
				
			||||||
    id="videolist"
 | 
					 | 
				
			||||||
    class="max-w-2xl mx-auto py-8 px-4 sm:px-6 lg:max-w-7xl lg:px-8"
 | 
					 | 
				
			||||||
  >
 | 
					 | 
				
			||||||
    <h2 class="sr-only">Videos</h2>
 | 
					    <h2 class="sr-only">Videos</h2>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <div
 | 
					    <div
 | 
				
			||||||
| 
						 | 
					@ -17,34 +14,18 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
import Vue from 'vue'
 | 
					import Vue from 'vue'
 | 
				
			||||||
const maxResults: number = 2
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default Vue.extend({
 | 
					export default Vue.extend({
 | 
				
			||||||
  name: 'VideoList',
 | 
					  name: 'VideoList',
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  data(): {
 | 
					  props: {
 | 
				
			||||||
    hasError: boolean
 | 
					    videos: {
 | 
				
			||||||
    videos: []
 | 
					      type: Array,
 | 
				
			||||||
  } {
 | 
					      default: () => {
 | 
				
			||||||
    return {
 | 
					        return []
 | 
				
			||||||
      hasError: false,
 | 
					      },
 | 
				
			||||||
      videos: [],
 | 
					      required: true,
 | 
				
			||||||
    }
 | 
					    },
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
 | 
					 | 
				
			||||||
  async fetch() {
 | 
					 | 
				
			||||||
    try {
 | 
					 | 
				
			||||||
      const response = await fetch(
 | 
					 | 
				
			||||||
        `https://www.googleapis.com/youtube/v3/playlistItems?part=snippet%2CcontentDetails&maxResults=${maxResults}&playlistId=${process.env.YOUTUBE_UPLOADS_PLAYLIST_ID}&key=${process.env.YOUTUBE_API_KEY}`
 | 
					 | 
				
			||||||
      )
 | 
					 | 
				
			||||||
      const json = await response.json()
 | 
					 | 
				
			||||||
      this.videos = json.items
 | 
					 | 
				
			||||||
    } catch (error: any) {
 | 
					 | 
				
			||||||
      this.hasError = true
 | 
					 | 
				
			||||||
      throw new Error(error)
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  fetchOnServer: true,
 | 
					 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -112,6 +112,10 @@ export default {
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  router: {
 | 
				
			||||||
 | 
					    routes: [{ path: '/:pageToken', props: true }],
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Global CSS: https://go.nuxtjs.dev/config-css
 | 
					  // Global CSS: https://go.nuxtjs.dev/config-css
 | 
				
			||||||
  css: [],
 | 
					  css: [],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,8 +6,17 @@
 | 
				
			||||||
    <main>
 | 
					    <main>
 | 
				
			||||||
      <IndexHero />
 | 
					      <IndexHero />
 | 
				
			||||||
      <div class="bg-gray-200 pb-12">
 | 
					      <div class="bg-gray-200 pb-12">
 | 
				
			||||||
        <VideosList />
 | 
					        <div class="max-w-2xl mx-auto py-8 px-4 sm:px-6 lg:max-w-7xl lg:px-8">
 | 
				
			||||||
        <SimplePagination />
 | 
					          <p v-if="$fetchState.pending">Fetching videos...</p>
 | 
				
			||||||
 | 
					          <p v-else-if="$fetchState.error">An error occurred :(</p>
 | 
				
			||||||
 | 
					          <div v-else id="videolist">
 | 
				
			||||||
 | 
					            <VideosList :videos="videos" />
 | 
				
			||||||
 | 
					            <SimplePagination
 | 
				
			||||||
 | 
					              :prev-page-token="prevPageToken"
 | 
				
			||||||
 | 
					              :next-page-token="nextPageToken"
 | 
				
			||||||
 | 
					            />
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
    </main>
 | 
					    </main>
 | 
				
			||||||
  </div>
 | 
					  </div>
 | 
				
			||||||
| 
						 | 
					@ -15,8 +24,48 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
import Vue from 'vue'
 | 
					import Vue from 'vue'
 | 
				
			||||||
 | 
					const maxResults: number = 12
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default Vue.extend({
 | 
					export default Vue.extend({
 | 
				
			||||||
  name: 'IndexPage',
 | 
					  name: 'IndexPage',
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  data(): {
 | 
				
			||||||
 | 
					    videos: []
 | 
				
			||||||
 | 
					    prevPageToken: string
 | 
				
			||||||
 | 
					    nextPageToken: string
 | 
				
			||||||
 | 
					  } {
 | 
				
			||||||
 | 
					    return {
 | 
				
			||||||
 | 
					      videos: [],
 | 
				
			||||||
 | 
					      prevPageToken: '',
 | 
				
			||||||
 | 
					      nextPageToken: '',
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  async fetch() {
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      let requestUrl =
 | 
				
			||||||
 | 
					        'https://www.googleapis.com/youtube/v3/playlistItems?part=snippet%2CcontentDetails'
 | 
				
			||||||
 | 
					      requestUrl += `&maxResults=${maxResults}`
 | 
				
			||||||
 | 
					      requestUrl += `&playlistId=${process.env.NUXT_ENV_YOUTUBE_UPLOADS_PLAYLIST_ID}`
 | 
				
			||||||
 | 
					      requestUrl += `&key=${process.env.NUXT_ENV_YOUTUBE_API_KEY}`
 | 
				
			||||||
 | 
					      if (this.$route.query.pageToken)
 | 
				
			||||||
 | 
					        requestUrl += `&pageToken=${this.$route.query.pageToken}`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      const response = await fetch(requestUrl)
 | 
				
			||||||
 | 
					      const json = await response.json()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      this.prevPageToken = json.prevPageToken
 | 
				
			||||||
 | 
					      this.nextPageToken = json.nextPageToken
 | 
				
			||||||
 | 
					      this.videos = json.items
 | 
				
			||||||
 | 
					    } catch (error: any) {
 | 
				
			||||||
 | 
					      throw new Error(error)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  watch: {
 | 
				
			||||||
 | 
					    '$route.query': '$fetch',
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  fetchOnServer: true,
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		
		Reference in a new issue