Add Carousel and MovieCard components
This commit is contained in:
49
nuxt/components/Carousel.vue
Normal file
49
nuxt/components/Carousel.vue
Normal file
@@ -0,0 +1,49 @@
|
||||
<template>
|
||||
<carousel
|
||||
:perPageCustom="[
|
||||
[320, 1],
|
||||
[480, 2],
|
||||
[768, 3]
|
||||
]"
|
||||
:loop="true"
|
||||
:navigationEnabled="true"
|
||||
:paginationEnabled="false"
|
||||
:navigationPrevLabel="arrowLeft"
|
||||
:navigationNextLabel="arrowRight"
|
||||
>
|
||||
<slide v-for="movie in dataSource" :key="movie.imdbID">
|
||||
<MovieCard :loading="loading" :movie="movie" />
|
||||
</slide>
|
||||
</carousel>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { Carousel, Slide } from "vue-carousel";
|
||||
import { mapState } from "vuex";
|
||||
import { Icon } from "ant-design-vue";
|
||||
|
||||
export default {
|
||||
components: { Carousel, Slide, Icon },
|
||||
props: {
|
||||
dataSource: {
|
||||
type: Array,
|
||||
default: []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
loading: "loading"
|
||||
})
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
arrowLeft:
|
||||
'<svg viewBox="64 64 896 896" data-icon="arrow-left" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class=""><path d="M872 474H286.9l350.2-304c5.6-4.9 2.2-14-5.2-14h-88.5c-3.9 0-7.6 1.4-10.5 3.9L155 487.8a31.96 31.96 0 0 0 0 48.3L535.1 866c1.5 1.3 3.3 2 5.2 2h91.5c7.4 0 10.8-9.2 5.2-14L286.9 550H872c4.4 0 8-3.6 8-8v-60c0-4.4-3.6-8-8-8z"></path></svg>',
|
||||
arrowRight:
|
||||
'<svg viewBox="64 64 896 896" data-icon="arrow-right" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class=""><path d="M869 487.8L491.2 159.9c-2.9-2.5-6.6-3.9-10.5-3.9h-88.5c-7.4 0-10.8 9.2-5.2 14l350.2 304H152c-4.4 0-8 3.6-8 8v60c0 4.4 3.6 8 8 8h585.1L386.9 854c-5.6 4.9-2.2 14 5.2 14h91.5c1.9 0 3.8-.7 5.2-2L869 536.2a32.07 32.07 0 0 0 0-48.4z"></path></svg>'
|
||||
};
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
62
nuxt/components/MovieCard.vue
Normal file
62
nuxt/components/MovieCard.vue
Normal file
@@ -0,0 +1,62 @@
|
||||
<template>
|
||||
<a-card :loading="loading" :bordered="false">
|
||||
<a-spin class="spinner" v-if="loading" slot="cover" />
|
||||
<img
|
||||
v-else
|
||||
slot="cover"
|
||||
:alt="movie.Title"
|
||||
:src="
|
||||
movie.Poster == 'N/A'
|
||||
? 'https://placeimg.com/200/200/any?1'
|
||||
: movie.Poster
|
||||
"
|
||||
/>
|
||||
|
||||
<a-card-meta :title="movie.Title">
|
||||
<template slot="description">
|
||||
{{ movie.Director }} -
|
||||
<span style="font-size: 12px; font-style: oblique;">{{
|
||||
movie.Year
|
||||
}}</span>
|
||||
</template>
|
||||
</a-card-meta>
|
||||
</a-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
movie: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
},
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.spinner {
|
||||
text-align: center;
|
||||
background: rgba(207, 216, 220, 0.4);
|
||||
border-radius: 4px 4px 0 0;
|
||||
margin-bottom: 20px;
|
||||
padding: 30px 50px;
|
||||
}
|
||||
|
||||
.ant-card {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.ant-card-cover img {
|
||||
max-height: 256px;
|
||||
object-fit: scale-down;
|
||||
}
|
||||
|
||||
.ant-card-cover {
|
||||
background-color: rgb(13, 10, 57);
|
||||
}
|
||||
</style>
|
||||
@@ -42,4 +42,19 @@ h2 {
|
||||
height: 48px;
|
||||
margin: 0 12px 0 0;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 414px) {
|
||||
.NuxtLogo {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
39
nuxt/package-lock.json
generated
39
nuxt/package-lock.json
generated
@@ -5204,6 +5204,11 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"dom-walk": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.2.tgz",
|
||||
"integrity": "sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w=="
|
||||
},
|
||||
"domain-browser": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz",
|
||||
@@ -6439,6 +6444,15 @@
|
||||
"is-glob": "^4.0.1"
|
||||
}
|
||||
},
|
||||
"global": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/global/-/global-4.4.0.tgz",
|
||||
"integrity": "sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w==",
|
||||
"requires": {
|
||||
"min-document": "^2.19.0",
|
||||
"process": "^0.11.10"
|
||||
}
|
||||
},
|
||||
"globals": {
|
||||
"version": "11.12.0",
|
||||
"resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
|
||||
@@ -9386,6 +9400,14 @@
|
||||
"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-3.1.0.tgz",
|
||||
"integrity": "sha512-Ysbi9uYW9hFyfrThdDEQuykN4Ey6BuwPD2kpI5ES/nFTDn/98yxYNLZJcgUAKPT/mcrLLKaGzJR9YVxJrIdASQ=="
|
||||
},
|
||||
"min-document": {
|
||||
"version": "2.19.0",
|
||||
"resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz",
|
||||
"integrity": "sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU=",
|
||||
"requires": {
|
||||
"dom-walk": "^0.1.0"
|
||||
}
|
||||
},
|
||||
"minimalistic-assert": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
|
||||
@@ -13986,6 +14008,23 @@
|
||||
"resolved": "https://registry.npmjs.org/vue/-/vue-2.6.12.tgz",
|
||||
"integrity": "sha512-uhmLFETqPPNyuLLbsKz6ioJ4q7AZHzD8ZVFNATNyICSZouqP2Sz0rotWQC8UNBF6VGSCs5abnKJoStA6JbCbfg=="
|
||||
},
|
||||
"vue-carousel": {
|
||||
"version": "0.18.0",
|
||||
"resolved": "https://registry.npmjs.org/vue-carousel/-/vue-carousel-0.18.0.tgz",
|
||||
"integrity": "sha512-a2zxh7QJioDxNMguqcuJ7TPbfgK5bGDaAXIia7NWxPAWsEvNE4ZtHgsGu40L5Aha4uyjmNKXvleB14QAXFoKig==",
|
||||
"requires": {
|
||||
"global": "^4.3.2",
|
||||
"regenerator-runtime": "^0.12.1",
|
||||
"vue": "^2.5.17"
|
||||
},
|
||||
"dependencies": {
|
||||
"regenerator-runtime": {
|
||||
"version": "0.12.1",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz",
|
||||
"integrity": "sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"vue-client-only": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/vue-client-only/-/vue-client-only-2.0.0.tgz",
|
||||
|
||||
@@ -16,7 +16,8 @@
|
||||
"ant-design-vue": "^1.6.5",
|
||||
"core-js": "^3.6.5",
|
||||
"lodash": "^4.17.20",
|
||||
"nuxt": "^2.14.5"
|
||||
"nuxt": "^2.14.5",
|
||||
"vue-carousel": "^0.18.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vue/test-utils": "^1.1.0",
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<TheHeader />
|
||||
<a-layout-content>
|
||||
<a-row type="flex" align="middle" justify="center" style="height: 100%;">
|
||||
<a-col :span="12">
|
||||
<a-col :xs="18" :md="12" :lg="8">
|
||||
<SearchInput />
|
||||
</a-col>
|
||||
</a-row>
|
||||
|
||||
@@ -3,54 +3,16 @@
|
||||
<TheHeader />
|
||||
<a-layout-content>
|
||||
<a-row type="flex" align="middle" justify="center" style="height: 100%;">
|
||||
<a-col :span="12">
|
||||
<nuxt-link to="/">
|
||||
<a-button type="primary"> <a-icon type="left" />Go back </a-button>
|
||||
</nuxt-link>
|
||||
|
||||
<h3 style="color: #FFFFFF;">
|
||||
Your search: "{{ search }}" ({{ nbResults }} match)
|
||||
</h3>
|
||||
<a-carousel arrows dot-position="top">
|
||||
<div
|
||||
slot="prevArrow"
|
||||
class="custom-slick-arrow"
|
||||
style="left: 10px;zIndex: 1"
|
||||
>
|
||||
<a-icon type="left-circle" />
|
||||
</div>
|
||||
<div
|
||||
slot="nextArrow"
|
||||
class="custom-slick-arrow"
|
||||
style="right: 10px"
|
||||
>
|
||||
<a-icon type="right-circle" />
|
||||
</div>
|
||||
<a-card
|
||||
:bordered="false"
|
||||
:key="movie.imdbID"
|
||||
v-for="movie in movies"
|
||||
>
|
||||
<img
|
||||
slot="cover"
|
||||
:alt="movie.Title"
|
||||
:src="
|
||||
movie.Poster === 'N/A'
|
||||
? 'https://place-hold.it/285x380'
|
||||
: movie.Poster
|
||||
"
|
||||
/>
|
||||
<a-card-meta :title="movie.Title">
|
||||
<template slot="description">
|
||||
{{ movie.Director }}
|
||||
-
|
||||
<span style="font-size: 12px; font-style: oblique;">{{
|
||||
movie.Year
|
||||
}}</span>
|
||||
</template>
|
||||
</a-card-meta>
|
||||
</a-card>
|
||||
</a-carousel>
|
||||
<a-col :xs="20" :md="18">
|
||||
<a-page-header
|
||||
:ghost="false"
|
||||
:title="`Your search : "${search}"`"
|
||||
:sub-title="`${nbResults} match`"
|
||||
@back="() => $router.push('/')"
|
||||
>
|
||||
<Carousel v-if="!error" :dataSource="movies" />
|
||||
<a-alert v-else :message="error" type="error" show-icon />
|
||||
</a-page-header>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-layout-content>
|
||||
@@ -65,19 +27,22 @@ export default {
|
||||
middleware: "redirect",
|
||||
async asyncData({ store }) {
|
||||
await store.dispatch("getMovies");
|
||||
|
||||
await store.dispatch("setDirectors");
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
search: "search",
|
||||
movies: "movies",
|
||||
nbResults: "nbResults",
|
||||
error: "error"
|
||||
error: "error",
|
||||
loading: "loading"
|
||||
})
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
<style>
|
||||
#main {
|
||||
height: 100vh;
|
||||
width: 100%;
|
||||
@@ -88,28 +53,49 @@ export default {
|
||||
background-image: url("~assets/background.jpg");
|
||||
}
|
||||
|
||||
.ant-carousel >>> .slick-slide {
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
.VueCarousel-navigation-prev {
|
||||
left: -8px;
|
||||
}
|
||||
|
||||
.ant-carousel >>> .slick-slide .ant-card {
|
||||
width: 285px !important;
|
||||
.VueCarousel-navigation-next {
|
||||
right: -8px;
|
||||
}
|
||||
|
||||
.ant-carousel >>> .custom-slick-arrow {
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
font-size: 25px;
|
||||
color: #fff;
|
||||
background-color: rgba(31, 45, 61, 0.11);
|
||||
opacity: 0.3;
|
||||
.ant-page-header {
|
||||
background-color: #f5f5f5;
|
||||
padding: 24px 44px;
|
||||
}
|
||||
|
||||
.ant-carousel >>> .custom-slick-arrow:before {
|
||||
display: none;
|
||||
.ant-page-header-content {
|
||||
overflow: unset;
|
||||
}
|
||||
.ant-carousel >>> .custom-slick-arrow:hover {
|
||||
opacity: 0.5;
|
||||
|
||||
.VueCarousel-slide {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
.VueCarousel-slide {
|
||||
max-width: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 992px) {
|
||||
.VueCarousel-slide {
|
||||
max-width: 33.333333%;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 1200px) {
|
||||
.VueCarousel-slide {
|
||||
max-width: 25%;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 414px) {
|
||||
.ant-page-header-heading-sub-title {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -5,7 +5,7 @@ export const state = () => ({
|
||||
movies: [],
|
||||
nbResults: 0,
|
||||
loading: false,
|
||||
error: ""
|
||||
error: null
|
||||
});
|
||||
|
||||
export const mutations = {
|
||||
@@ -18,50 +18,62 @@ export const mutations = {
|
||||
};
|
||||
|
||||
export const actions = {
|
||||
async getMovies({ commit, state, dispatch }) {
|
||||
async getMovies({ commit, state }) {
|
||||
commit("setLoading", true);
|
||||
const response = await this.$axios.$get("", {
|
||||
params: {
|
||||
apikey: "a4bf96a7",
|
||||
s: state.search,
|
||||
type: "movie"
|
||||
}
|
||||
});
|
||||
|
||||
if (response.Response === "True") {
|
||||
commit("setNbResults", response.totalResults);
|
||||
dispatch("setDirectors", response.Search).then(function(res) {
|
||||
console.log(res);
|
||||
commit("setMovies", res);
|
||||
return await this.$axios
|
||||
.$get("", {
|
||||
params: {
|
||||
apikey: "a4bf96a7",
|
||||
s: state.search,
|
||||
type: "movie"
|
||||
}
|
||||
})
|
||||
.then(response => {
|
||||
if (response.Response === "False") throw new Error(response.Error);
|
||||
|
||||
commit("setNbResults", response.totalResults);
|
||||
commit("setMovies", response.Search);
|
||||
commit("setError", null);
|
||||
})
|
||||
.catch(e => {
|
||||
commit("setMovies", []);
|
||||
commit("setNbResults", 0);
|
||||
commit("setError", e.message);
|
||||
})
|
||||
.finally(() => {
|
||||
commit("setLoading", false);
|
||||
});
|
||||
} else if (response.Response === "False") {
|
||||
commit("setMovies", []);
|
||||
commit("setNbResults", 0);
|
||||
commit("setError", response.Error);
|
||||
}
|
||||
|
||||
commit("setLoading", false);
|
||||
},
|
||||
async getDirector({}, id) {
|
||||
const response = await this.$axios.$get("", {
|
||||
params: {
|
||||
apikey: "a4bf96a7",
|
||||
i: id,
|
||||
type: "movie"
|
||||
}
|
||||
return await this.$axios
|
||||
.$get("", {
|
||||
params: {
|
||||
apikey: "a4bf96a7",
|
||||
i: id,
|
||||
type: "movie"
|
||||
}
|
||||
})
|
||||
.then(response => {
|
||||
if (response.Response === "False") throw new Error(response.Error);
|
||||
|
||||
return response.Director;
|
||||
})
|
||||
.catch(e => commit("setError", e.message));
|
||||
},
|
||||
setDirectors({ state, dispatch, commit }) {
|
||||
commit("setLoading", true);
|
||||
|
||||
let tmp = [];
|
||||
state.movies.forEach(async movie => {
|
||||
return await dispatch("getDirector", movie.imdbID).then(director => {
|
||||
movie.Director = director;
|
||||
tmp.push(movie);
|
||||
});
|
||||
});
|
||||
|
||||
if (response.Response === "True") return response.Director;
|
||||
else if (response.Response === "False") throw new Error(response.Error);
|
||||
},
|
||||
setDirectors({ dispatch }, data) {
|
||||
let tmp = [];
|
||||
data.forEach(async (movie, index) => {
|
||||
const director = await dispatch("getDirector", movie.imdbID);
|
||||
movie.Director = director;
|
||||
tmp.push(movie);
|
||||
});
|
||||
return tmp;
|
||||
commit("setMovies", tmp);
|
||||
commit("setLoading", false);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user