- Git
$ git --version
git version 2.14.3 (Apple Git-98)
- npm
$ npm -v
6.7.0
- node
$ node -v
v12.2.0
- vue-cli
$ npm install -g @vue/cli
$ vue --version
3.7.0
- Provides basic CRUD functionality for products
- The Service will be triggered by consumers via a web app
| WEBPAGE | URI | ACTION |
|----------------|-----------------------------------------|-----------------------------|
| HOME | http://[hostname]/ | Landing Page |
| SIGNIN | http://[hostname]/login | Logs the user in |
| SIGNUP | http://[hostname]/signup | Signs up the user |
| FORGOT PWD | http://[hostname]/forgot | Forgot password page |
| CONFIRM | http://[hostname]/confirm | Confirms the user |
| INDEX | http://[hostname]/products | Gets all products |
| DETAIL | http://[hostname]/products/<product_id> | Gets one product |
| BOOK-1 | http://[hostname]/booking/step1 | Booking Step 1 |
| BOOK-2 | http://[hostname]/booking/step2 | Booking Step 2 |
| PAYMENT | http://[hostname]/booking/payment | Payment and Confirmation |
| CONFIRMATION | http://[hostname]/booking/confirmed | Booking successful |
| PROFILE | http://[hostname]/profile/<user_id> | View and update profile |
| PROFILE-0 | http://[hostname]/profile/step0 | Update profile step 0 |
| PROFILE-1 | http://[hostname]/profile/step1 | Update profile step 1 |
| PROFILE-2 | http://[hostname]/profile/step2 | Update profile step 2 |
| PROFILE-3 | http://[hostname]/profile/step3 | Update profile step 3 |
| PROFILE-4 | http://[hostname]/profile/step4 | Update profile success |
$ cd ~/environment/
$ vue create myproject-consumer-web
$ cd myproject-consumer-web
$ aws codecommit create-repository --repository-name myproject-consumer-web
- Automatically created by vue create
$ cd ~/environment/myproject-consumer-web
$ git init
$ git add .
$ git commit -m "Initial Commit"
$ git remote add origin https://git-codecommit.ap-southeast-1.amazonaws.com/v1/repos/myproject-consumer-web
$ git remote -v
$ git push origin master
$ cd ~/environment/myproject-consumer-web
$ npm run serve
$ cd myproject-consumer-web/src/assets
$ cp -R <source_dir>/assets/ myproject-consumer-web/src/assets
*You can find the assets folder in the src directory of this repository
Install dependencies:
In myproject-consumer-web
add the following dependencies into package.json
"dependencies": {
"bootstrap": "^4.3.1",
"bootstrap-select": "^1.13.5",
"jquery": "^3.3.1",
"popper.js": "^1.15.0",
},
Install module: vue-awesome-swiper In the terminal run the following:
# vuejs swiper for card components
$ npm install vue-awesome-swiper --save
# a vuejs dropdown selection library
$ npm install vue-select --save
# a vuejs datepicker library
$ npm install vuejs-datepicker --save
# will be used later for SVG rendering
$ npm install html-loader --save
after, run:
$ npm install
In myproject-vuejs-web/src/main.js
add the following lines of code:
// for Vue Swiper
import VueAwesomeSwiper from 'vue-awesome-swiper';
import 'swiper/dist/css/swiper.css';
Vue.use(VueAwesomeSwiper);
// jQuery
require('@/assets/vendor/jquery/jquery.min.js');
// bootstrap
import 'bootstrap';
// css
require('@/assets/vendor/nouislider/nouislider.css');
require('@/assets/vendor/magnific-popup/magnific-popup.css');
require('@/assets/css/style.default.css');
require('@/assets/css/custom.css');
//import css for vue-select
import 'vue-select/dist/vue-select.css';
Final Version of main.js
:
import Vue from 'vue';
import App from './App.vue';
// for Vue Swiper
import VueAwesomeSwiper from 'vue-awesome-swiper';
import 'swiper/dist/css/swiper.css';
Vue.use(VueAwesomeSwiper);
// jQuery
require('@/assets/vendor/jquery/jquery.min.js');
// bootstrap
import 'bootstrap';
// css
require('@/assets/vendor/nouislider/nouislider.css');
require('@/assets/vendor/magnific-popup/magnific-popup.css');
require('@/assets/css/style.default.css');
require('@/assets/css/custom.css');
//import css for vue-select
import 'vue-select/dist/vue-select.css';
import router from './router';
Vue.config.productionTip = false;
new Vue({
router,
render: h => h(App),
}).$mount('#app');
Add the following stylesheet imports inside the <style>
tag of App.vue
<style scoped>
.navbar-margin {
margin-bottom: 72px;
}
/*extra css*/
@import "https://fonts.googleapis.com/css?family=Playfair+Display:400,400i,700";
@import "https://fonts.googleapis.com/css?family=Poppins:300,400,400i,700";
@import "https://use.fontawesome.com/releases/v5.8.1/css/all.css";
</style>
Add SVG rendering to App.vue
In the bottom most portion inside the<template>
tag of App.vue
add the following code
<template>
<div id="app">
...START...
<!-- load this using the html loader -->
<!-- https://stackoverflow.com/questions/50834598/inline-svg-in-vuejs-component -->
<div v-html="require('!html-loader!@/assets/icons/orion-svg-sprite.svg')"></div>
...END...
</div>
</template>
In this portion we use v-html
to load .svg file: orion-svg-sprite.svg
into the div tag, using the html-loader
.
In the terminal run the following:
$ npm install eslint eslint-config-strongloop --save
In myproject-vuejs-web/src
create a file called: .eslintrc.json
In .eslintrc.json
add following:
{
"extends": "strongloop",
"parser": "babel-eslint"
}
In myproject-vuejs-web/src
create a file called: .eslintignore
In the .eslintignore
we specify directories we don't want to lint. In this case, we don't need to lint our assets
and pages
folders because they contain the templates we modified for the purposes of this project.
We also ignore dist
, which contains the packaged form of the project to be used in production
In .eslintignore
add following relative paths:
src/assets/**
src/pages/**
dist/**
In myproject-vuejs-web/src
, package.json
under
"scripts": {
...
"lint": "vue-cli-service lint"
...
}
replace:
"lint": "vue-cli-service lint"
with the following
"lint": "eslint . --fix"
In the terminal you can now run the following command:
$ npm run lint
*The above command will automatically fix spacing and report syntax errors.
$ vue add router
Output:
? Use history mode for router? (Requires proper server setup for index fa
llback in production) Yes
🚀 Invoking generator for core:router...
📦 Installing additional dependencies...
added 1 package from 1 contributor and audited 24981 packages in 6.458s
found 0 vulnerabilities
âś” Successfully invoked generator for plugin: core:router
The following files have been updated / added:
.gitignore
README.md
babel.config.js
package-lock.json
package.json
public/favicon.ico
public/index.html
src/App.vue
src/assets/logo.png
src/components/HelloWorld.vue
src/main.js
src/router.js
src/views/About.vue
src/views/Home.vue
You should review these changes with git diff and commit them.
In myproject-consumer-web/src/components/
create a file called : Navigation.vue
Add the following code in Navigation.vue
:
<template>
<nav class="navbar navbar-expand-lg fixed-top shadow navbar-light bg-white">
<div class="container-fluid">
<div class="d-flex align-items-center"><a href="/" class="navbar-brand py-1"><img src="../assets/img/hetchly-logo.svg" alt="Hetchly logo"></a>
</div>
<button type="button" data-toggle="collapse" data-target="#navbarCollapse" aria-controls="navbarCollapse" aria-expanded="false" aria-label="Toggle navigation" class="navbar-toggler navbar-toggler-right"><i class="fa fa-bars"></i></button>
<div id="navbarCollapse" class="collapse navbar-collapse">
<form action="#" id="searchcollapsed" class="form-inline mt-4 mb-2 d-sm-none">
<div class="input-label-absolute input-label-absolute-left input-reset w-100">
<label for="searchcollapsed_search" class="label-absolute"><i class="fa fa-search"></i><span class="sr-only">What are you looking for?</span></label>
<input id="searchcollapsed_search" placeholder="Search" aria-label="Search" class="form-control form-control-sm border-0 shadow-0 bg-gray-200">
<button type="reset" class="btn btn-reset btn-sm"><i class="fa-times fas"></i></button>
</div>
</form>
<ul class="navbar-nav ml-auto">
<li class="nav-item"><router-link id="homeDropdownMenuLink" to="/" class="nav-link">Home</router-link></li>
<li class="nav-item"><router-link id="logInButton" to="/products" class="nav-link">Activities</router-link></li>
<li class="nav-item" v-if="loggedIn"><router-link id="logInButton" to="/profile/1" class="nav-link">My Profile</router-link></li>
<li class="nav-item"><router-link id="logInButton" to="/login" class="nav-link" v-if="!loggedIn">Sign in</router-link></li>
<li class="nav-item" v-if="!loggedIn"><router-link to="/signup"><button id="logOutButton" class="btn btn-primary">Register</button></router-link></li>
<li class="nav-item mt-3 mt-lg-0 ml-lg-3 d-lg-none d-xl-inline-block" v-if="loggedIn"><button id="logOutButton" class="btn btn-primary" v-on:click="logOut">Logout</button></li>
</ul>
</div>
</div>
</nav>
</template>
Add the following code into the <template>
tag of App.vue
<div id="app">
... START ...
<div v-if="!['signup', 'login'].includes(this.$route.name)">
<Navigation/>
<div class="navbar-margin"></div>
</div>
... END...
</div>
In the <script>
tag of App.vue
add the following code:
<script>
...START...
import Navigation from '@/components/Navigation.vue' <--- We import the Navigation from our components
export default {
name: 'home',
components: {
Navigation <--- We add Navigation as a component
}
}
...END...
</script>
Add the following code inside the <style>
tag of App.vue
.navbar-margin {
margin-bottom: 72px;
}
In myproject-consumer-web/src/components/
create a file called : Footer.vue
Add the following code in Footer.vue
:
<template>
<footer class="position-relative z-index-10 d-print-none">
<div class="py-6 bg-gray-200 text-muted">
<div class="container">
<div class="row">
<div class="col-lg-4 mb-5 mb-lg-0">
<div class="font-weight-bold text-uppercase text-dark mb-3">Hetchly</div>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing.</p>
<ul class="list-inline">
<li class="list-inline-item"><a href="#" target="_blank" title="twitter" class="text-muted text-hover-primary"><i class="fab fa-twitter"></i></a></li>
<li class="list-inline-item"><a href="#" target="_blank" title="facebook" class="text-muted text-hover-primary"><i class="fab fa-facebook"></i></a></li>
<li class="list-inline-item"><a href="#" target="_blank" title="instagram" class="text-muted text-hover-primary"><i class="fab fa-instagram"></i></a></li>
<li class="list-inline-item"><a href="#" target="_blank" title="pinterest" class="text-muted text-hover-primary"><i class="fab fa-pinterest"></i></a></li>
<li class="list-inline-item"><a href="#" target="_blank" title="vimeo" class="text-muted text-hover-primary"><i class="fab fa-vimeo"></i></a></li>
</ul>
</div>
<div class="col-lg-2 col-md-6 mb-5 mb-lg-0">
<h6 class="text-uppercase text-dark mb-3">Destinations</h6>
<ul class="list-unstyled">
<li><a href="/" class="text-muted">Boracay</a></li>
<li><a href="/" class="text-muted">Coron</a></li>
<li><a href="/" class="text-muted">La Union</a></li>
<li><a href="/" class="text-muted">Tagaytay</a></li>
<li><a href="/" class="text-muted">Baguio</a></li>
</ul>
</div>
<div class="col-lg-2 col-md-6 mb-5 mb-lg-0">
<h6 class="text-uppercase text-dark mb-3">Pages</h6>
<ul class="list-unstyled">
<li><a href="/" class="text-muted">Contact</a></li>
<li><a href="/" class="text-muted">Pricing</a></li>
<li><a href="/" class="text-muted">Text page</a></li>
<li><a href="/" class="text-muted">F.A.Q.s<span class="badge badge-info ml-1">New</span></a></li>
<li><a href="/" class="text-muted">Coming soon</a></li>
</ul>
</div>
<div class="col-lg-4">
<h6 class="text-uppercase text-dark mb-3">Daily Offers & Discounts</h6>
<p class="mb-3"> Lorem ipsum dolor sit amet, consectetur adipisicing elit. At itaque temporibus.</p>
<form action="#" id="newsletter-form">
<div class="input-group mb-3">
<input type="email" placeholder="Your Email Address" aria-label="Your Email Address" class="form-control bg-transparent border-dark border-right-0">
<div class="input-group-append">
<button type="submit" class="btn btn-outline-dark border-left-0"> <i class="fa fa-paper-plane text-lg"></i></button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
<div class="py-4 font-weight-light bg-gray-800 text-gray-300">
<div class="container">
<div class="row align-items-center">
<div class="col-md-6 text-center text-md-left">
<p class="text-sm mb-md-0">© 2019 Hetchly. All rights reserved.</p>
</div>
<div class="col-md-6">
<ul class="list-inline mb-0 mt-2 mt-md-0 text-center text-md-right">
<li class="list-inline-item"><img src="../assets/img/visa.svg" alt="..." class="w-2rem"></li>
<li class="list-inline-item"><img src="../assets/img/mastercard.svg" alt="..." class="w-2rem"></li>
<li class="list-inline-item"><img src="../assets/img/paypal.svg" alt="..." class="w-2rem"></li>
<li class="list-inline-item"><img src="../assets/img/western-union.svg" alt="..." class="w-2rem"></li>
</ul>
</div>
</div>
</div>
</div>
</footer>
</template>
Add the following code into the <template>
tag of App.vue
...
<div class="navbar-margin"></div>
<router-view/>
... START...
<div v-if="!['signup', 'login'].includes(this.$route.name)">
<Footer/>
</div>
</div>
...
In the <script>
tag of App.vue
add the following code:
<script type="text/javascript">
// @ is an alias to /src
... START ...
import Navigation from '@/components/Navigation.vue'
import Footer from '@/components/Footer.vue' <--- We import the footer from our components
export default {
name: 'home',
components: {
Navigation,
Footer <--- We add footer as a component
}
}
... END...
</script>
Final Version of App.vue
<template>
<div id="app">
<div v-if="!['signup', 'login'].includes(this.$route.name)">
<Navigation/>
<div class="navbar-margin"></div>
</div>
<router-view/>
<div v-if="!['signup', 'login'].includes(this.$route.name)">
<Footer/>
</div>
<!-- load this using the html loader -->
<!-- https://stackoverflow.com/questions/50834598/inline-svg-in-vuejs-component -->
<div v-html="require('!html-loader!@/assets/icons/orion-svg-sprite.svg')"></div>
</div>
</template>
<script>
// @ is an alias to /src
import Navigation from '@/components/Navigation.vue'
import Footer from '@/components/Footer.vue'
export default {
name: 'home',
components: {
Navigation,
Footer
}
}
}
</script>
<style scoped>
.navbar-margin {
margin-bottom: 72px;
}
/*extra css*/
@import "https://fonts.googleapis.com/css?family=Playfair+Display:400,400i,700";
@import "https://fonts.googleapis.com/css?family=Poppins:300,400,400i,700";
@import "https://use.fontawesome.com/releases/v5.8.1/css/all.css";
</style>
Set up the individual components for our home page.
In myproject-consumer-web/src/components/
create a folder called home
$ cd myproject-consumer-web/src/components/
$ mkdir home
$ cd home
Note: There are already pre-built components available located in the home
directory of this repository. (please see myproject-consumer-web/src/components/home
) You follow the steps below to set up an example home page. You may also make use of the other available components when creating your webpage.
In myproject-consumer-web/src/components/home
create a file called : SearchBar.vue
Add the following code:
<template>
<div class="searchbar">
<section class="hero-home">
<swiper v-bind:options="swiperOption" class="hero-slider">
<div class="swiper-wrapper dark-overlay">
<swiper-slide class="swiper-slide pic_1"></swiper-slide>
</div>
</swiper>
<div class="container py-6 py-md-7 text-white z-index-20">
<div class="row">
<div class="col-xl-10">
<div class="text-center text-lg-left">
<p class="subtitle letter-spacing-4 mb-2 text-secondary text-shadow">Philippine Tourists</p>
<h1 class="display-3 font-weight-bold text-shadow">Register Here</h1>
</div>
<div class="search-bar mt-5 p-3 p-lg-1 pl-lg-4">
<form action="#">
<div class="row">
<div class="col-lg-4 d-flex align-items-center form-group">
<input type="text" name="search" placeholder="What are you searching for?" class="form-control border-0 shadow-0">
</div>
<div class="col-lg-3 d-flex align-items-center form-group">
<div class="input-label-absolute input-label-absolute-right w-100">
<label for="location" class="label-absolute"><i class="fa fa-crosshairs"></i><span class="sr-only">City</span></label>
<input type="text" name="location" placeholder="Boracay" id="location" class="form-control border-0 shadow-0">
</div>
</div>
<div class="col-lg-5">
<div class="row">
<div class="col-lg-5">
<button type="submit" class="btn btn-primary btn-block rounded-xl h-100">Search</button>
</div>
<div class="or justify-content-center col-lg-1">or</div>
<div class="col-lg-6">
<a href="/signup">
<button type=button class="btn btn-primary btn-block rounded-xl h-100">Register</button>
</a>
</div>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</section>
</div>
</template>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.pic_1 {
background: url('../../assets/img/photo/banner-2.jpg') center;
background-size: cover
}
.or {
color:#868E96;
margin-bottom:0px;
text-align: center;
}
</style>
<script>
export default {
data() {
return {
swiperOption: {
el: '.hero-slider',
effect: 'fade',
speed: 2000,
allowTouchMove: false,
autoplay: {
delay: 2000,
},
}
}
}
}
</script>
In myproject-consumer-web/src/components/home
create a file called : Services.vue
Add the following code:
<template>
<div class="services">
<section class="py-6 bg-gray-100">
<div class="container">
<div class="text-center pb-lg-4">
<p class="subtitle text-secondary">Choose from a variety of services</p>
<h2 class="mb-5">Available Services</h2>
</div>
<div class="row">
<div class="col-lg-4 mb-4 mb-lg-0 text-center" v-for="service in services" v-bind:key="service.id">
<div class="px-0 px-lg-4 icon-box">
<div class="icon-rounded bg-primary-light mb-4">
<svg class="svg-icon text-primary w-2rem h-2rem">
<use v-bind:xlink:href="service.icon"></use>
</svg>
</div>
<h3 class="h5">{{service.name}}</h3>
</div>
</div>
</div>
<div style="margin-top:5%"></div>
</div>
</section>
</div>
</template>
<script>
export default {
name: 'services',
data(){
return {
services: [
{
"id" : 1,
"name": "Activities",
"icon": "#beach-1"
},
{
"id" : 2,
"name": "Accommodations",
"icon": "#real-estate-1"
},
{
"id" : 3,
"name": "Eats",
"icon": "#dinner-1"
},
{
"id" : 4,
"name": "Local Transport",
"icon": "#bus-1"
},
{
"id" : 5,
"name": "Airport Transfer",
"icon": "#airplane-mode-1"
},
{
"id" : 6,
"name": "Wifi + Data Packages",
"icon": "#world-map-1"
},
{
"id" : 7,
"name": "Insurance",
"icon": "#shield-security-1"
},
{
"id" : 8,
"name": "Payments",
"icon": "#pay-1"
},
{
"id" : 9,
"name": "Emergency",
"icon": "#medical-emergency-1"
}
]
}
},
methods: {
}
}
</script>
<style scoped>
.icon-box {
padding:2rem;
}
</style>
In myproject-consumer-web/src/components/home
create a file called : DisplayCatalog1.vue
Add the following code:
<template>
<div class="display-catalog-1">
<section class="py-6 bg-white">
<div class="container">
<div class="row mb-5">
<div class="col-md-8">
<p class="subtitle text-primary">Stay and eat like a local</p>
<h2>Our guides</h2>
</div>
</div>
<div class="row">
<swiper v-bind:options="swiperOption" class="guides-slider">
<swiper-slide class="h-auto px-2" v-for="city in cities" v-bind:key="city.name">
<div class="card card-poster gradient-overlay mb-4 mb-lg-0"><a v-bind:href="city.url_route" class="tile-link"></a>
<img v-bind:src="city.image_url" v-bind:alt="city.name" class="bg-image">
<!-- <img src="../assets/img/photo/coron.jpg" class="bg-image"> -->
<div class="card-body overlay-content">
<h6 class="card-title text-shadow text-uppercase">{{city.name}}</h6>
<p class="card-text text-sm">{{city.description}}</p>
</div>
</div>
</swiper-slide>
</swiper>
</div>
</div>
</section>
</div>
</template>
<script scoped>
export default {
data() {
return {
swiperOption: {
slidesPerView: 5,
spaceBetween: 15,
loop: true
},
cities: [
{
"name" : "Boracay",
"image_url" : require('../../assets/img/photo/boracay.jpg'),
"description" : "Boracay is ..",
"url_route": "/products"
},
{
"name" : "Panglaw",
"image_url" : require('../../assets/img/photo/panglaw.jpg'),
"description" : "Panglaw is ..",
"url_route": "#"
},
{
"name" : "Palawan",
"image_url" : require('../../assets/img/photo/coron.jpg'),
"description" : "Palawan is ..",
"url_route": "#"
},
{
"name" : "Batanes",
"image_url" : require('../../assets/img/photo/batanes.jpg'),
"description" : "Batanes is ..",
"url_route": "#"
},
{
"name" : "Bohol",
"image_url" : require('../../assets/img/photo/bohol.jpg'),
"description" : "Bohol is ..",
"url_route": "#"
}
]
}
}
}
</script>
In myproject-consumer-web/src/components/home
create a file called : PhotoWheel.vue
Add the following code:
<template>
<section>
<div class="container-fluid px-0">
<div class="instagram-slider">
<swiper v-bind:options="swiperOption">
<swiper-slide v-for="photo in photos" v-bind:key="photo.image_url">
<div>
<img v-bind:src="photo.image_url" v-bind:alt='photo.image_url' class="img-fluid hover-scale">
</div>
</swiper-slide>
</swiper>
</div>
</div>
</section>
</template>
<script>
export default {
data() {
return {
swiperOption: {
slidesPerView: 10,
spaceBetween: 0,
},
photos: [
{
"image_url" : require('../../assets/img/instagram/image-1.jpg')
},
{
"image_url" : require('../../assets/img/instagram/image-2.jpg'),
},
{
"image_url" : require('../../assets/img/instagram/image-3.jpg'),
},
{
"image_url" : require('../../assets/img/instagram/image-4.jpg'),
},
{
"image_url" : require('../../assets/img/instagram/image-5.jpg'),
},
{
"image_url" : require('../../assets/img/instagram/image-6.jpg'),
},
{
"image_url" : require('../../assets/img/instagram/image-7.jpg'),
},
{
"image_url" : require('../../assets/img/instagram/image-8.jpg'),
},
{
"image_url" : require('../../assets/img/instagram/image-9.jpg'),
},
{
"image_url" : require('../../assets/img/instagram/image-10.jpg'),
}
]
}
}
}
</script>
Copy the following code snippet into Home.vue
<template>
<div class="home">
<SearchBar/>
<Services/>
<DisplayCatalog1/>
<DisplayProducts/>
<PhotoWheel/>
</div>
</template>
<script>
// @ is an alias to /src
import SearchBar from '@/components/home/SearchBar.vue'
import Services from '@/components/home/Services.vue'
import DisplayCatalog1 from '@/components/home/DisplayCatalog1.vue'
import DisplayProducts from '@/components/products/DisplayProduct.vue'
import PhotoWheel from '@/components/home/PhotoWheel.vue'
export default {
name: 'home',
components: {
SearchBar,
Services,
DisplayCatalog1,
PhotoWheel
}
}
</script>
Final Version of router.js
- Important to note that
router.js
takes care of routing toHome.vue
. This was configured when we ranvue add router
- Our home page is accessible via
localhost:8080/
import Vue from 'vue'
import Router from 'vue-router'
import Home from './views/Home.vue'
Vue.use(Router)
export default new Router({
mode: 'history',
base: process.env.BASE_URL,
routes: [
{
path: '/',
name: 'home',
component: Home
}
]
})
In myproject-consumer-web/src/views/
create a file called : Login.vue
Add the following snippet
<template>
<div class="login">
<div class="container-fluid px-3">
<div class="row min-vh-100">
<div class="col-md-8 col-lg-6 col-xl-5 d-flex align-items-center">
<div class="w-100 py-5 px-md-5 px-xl-6 position-relative">
<div class="mb-5">
<img src="../assets/img/hetchly-logo.svg" alt="..." style="max-width: 10rem;" class="img-fluid mb-3">
<h2>Welcome back</h2>
</div>
<router-link to="/" class="close-absolute mr-md-5 mr-xl-6 pt-5">
<svg class="svg-icon w-3rem h-3rem">
<use xlink:href="#close-1"> </use>
</svg>
</router-link>
<div class="form-validate" id="loginForm">
<div class="form-group">
<label for="loginUsername" class="form-label"> Email Address</label>
<input name="loginUsername" id="loginUsername" type="email" placeholder="name@address.com" autocomplete="off" required data-msg="Please enter your email" class="form-control" v-model="email">
</div>
<div class="form-group mb-4">
<div class="row">
<div class="col">
<label for="loginPassword" class="form-label"> Password</label>
</div>
<div class="col-auto">
<router-link to="forgot" class="form-text small">Forgot password?</router-link>
</div>
</div>
<input name="loginPassword" id="loginPassword" placeholder="Password" type="password" required data-msg="Please enter your password" class="form-control" v-model="password">
</div>
<button class="btn btn-lg btn-block btn-primary">Sign in</button>
<hr data-content="OR" class="my-3 hr-text letter-spacing-2">
</div>
<button class="btn btn btn-outline-primary btn-block btn-social mb-3"><i class="fa-2x fa-facebook-f fab btn-social-icon"> </i>Connect <span class="d-none d-sm-inline">with Facebook</span></button>
<p class="text-center"><small class="text-muted text-center">Don't have an account yet? <a href="/signup">Sign Up</a></small></p>
</div>
</div>
<div class="col-md-4 col-lg-6 col-xl-7 bG"></div>
</div>
</div>
</div>
</template>
<style>
.bG {
background: url(../assets/img/photo/photo-1497436072909-60f360e1d4b1.jpg) center center;
background-size: cover;
}
</style>
<script>
export default {
name: 'login',
data(){
return {
email: '',
password: ''
}
}
}
</script>
In myproject-consumer-web/src/
Add the following snippet in router.js
:
... START ...
{
//Added a route for login
path: '/login',
name: 'login',
component: () => import('./views/Login.vue')
}
... END...
In myproject-consumer-web/src/views/
create a file called : SignUp.vue
Add the following snippet
<template>
<div class="signup">
<div class="container-fluid px-3">
<div class="row min-vh-100">
<div class="col-md-8 col-lg-6 col-xl-5 d-flex align-items-center">
<div class="w-100 py-5 px-md-5 px-xl-6 position-relative">
<div class="mb-4"><img src="../assets/img/hetchly-logo.svg" alt="..." style="max-width: 10rem;" class="img-fluid mb-4">
<h2>Sign up</h2>
</div>
<div class="form-group">
<label for="loginUsername" class="form-label">Email Address</label>
<input name="loginUsername" id="loginUsername" type="email" placeholder="name@address.com" autocomplete="off" required data-msg="Please enter your email" class="form-control" v-model="email">
</div>
<div class="form-group">
<label for="loginPassword" class="form-label">Password</label>
<input name="loginPassword" id="loginPassword" placeholder="Password" type="password" required data-msg="Please enter your password" class="form-control" v-model="password">
</div>
<div class="form-group mb-4">
<label for="loginPassword2" class="form-label">Confirm your password</label>
<input name="loginPassword2" id="loginPassword2" placeholder="Password" type="password" required data-msg="Please enter your password" class="form-control" v-model="confirmPw">
</div>
<button id="signup" class="btn btn-lg btn-block btn-primary">Sign up</button>
<hr data-content="OR" class="my-3 hr-text letter-spacing-2">
<button id="signupfb" class="btn btn btn-outline-primary btn-block btn-social mb-3"><i class="fa-2x fa-facebook-f fab btn-social-icon"> </i>Connect <span class="d-none d-sm-inline">with Facebook</span></button>
<p class="text-center"><small class="text-muted text-center">Have an account? <router-link to="/login">Log in</router-link></small></p>
<hr class="my-4">
<p class="text-sm text-muted">By signing up you agree to Directory's <a href="#">Terms and Conditions</a> and <a href="#">Privacy Policy</a>.</p>
<router-link to="/" class="close-absolute mr-md-5 mr-xl-6 pt-5">
<svg class="svg-icon w-3rem h-3rem">
<use xlink:href="#close-1"></use>
</svg></router-link>
</a>
</div>
</div>
<div class="col-md-4 col-lg-6 col-xl-7 d-none d-md-block bG">
</div>
</div>
</div>
</div>
</template>
<style scoped>
.bG {
background: url(../assets/img/photo/photo-1497436072909-60f360e1d4b1.jpg) center center;
background-size: cover;
}
</style>
<script>
export default {
name: 'signup',
data(){
return {
email: '',
password: '',
confirmPw: ''
}
}
}
</script>
In myproject-consumer-web/src/
Add the following snippet in router.js
:
{
path: '/',
name: 'home',
component: Home,
},
{
//Added a route for login
path: '/login',
name: 'login',
component: () => import('./views/Login.vue')
},
... START...
{
path: '/signup',
name: 'sigup',
component: () => import('./views/SignUp.vue')
},
... END ...
In this step, we try to set up our product page. We create a products folder where we store all the components related to products
. Outlined here will be the steps to create:
8A. DisplayProduct Component 8B. ProductIndex Component 8C. ProductDetail Component
In myproject-consumer-web/src/components
folder create a folder called products
$ cd src/components
$ mkdir products
$ cd products
Create a product card component that will represent each product as a card.
In myproject-consumer-web/src/components/products
, create a file called ProductCard.vue
:
Add ff code snippet:
<template class="product-card">
<div data-marker-id="59c0c8e33b1527bfe2abaf92" class="w-100 h-100">
<div class="card h-100 border-0 shadow">
<div class="card-img-top overflow-hidden gradient-overlay"> <img v-bind:src="product.image_url" v-bind:alt="product.name" class="img-fluid"/>
<router-link v-bind:to="{name: 'product_detail', params: {product_id: product.id}}" class="tile-link">
</router-link>
</div>
<div class="card-body d-flex align-items-center">
<div class="w-100">
<h6 class="card-title">
<router-link v-bind:to="{name: 'product_detail', params: {product_id: product.id}}" class="tile-link">
</router-link>
{{product.name}}
</h6>
<div class="d-flex card-subtitle mb-3">
<p class="flex-grow-1 mb-0 text-muted text-sm ellipsis">{{product.description}}</p>
<p class="flex-shrink-1 mb-0 card-stars text-xs text-right"><i class="fa fa-star text-warning"></i><i class="fa fa-star text-warning"></i><i class="fa fa-star text-warning"></i><i class="fa fa-star text-warning"></i><i class="fa fa-star text-warning"></i>
</p>
</div>
<p class="card-text text-muted"><span class="h4 text-primary">Php. {{product.price}} </span>person</p>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'product-card',
props: ['product'],
data(){
return {
obj: this.product,
}
}
}
</script>
<style scoped>
.ellipsis {
text-overflow: ellipsis;
/* Required for text-overflow to do anything */
white-space: nowrap;
overflow: hidden;
}
</style>
In myproject-consumer-web/src/assets
, create a folder called json
:
$ cd src/assets
$ mkdir json
$ cd json
in the json
folder, create a file called boracay.json
:
$ cd touch boracay.json
Add the ff data:
[
{
"id":"4d196c50-aabf-4df2-afb3-fd21481259d6",
"name":"Boracay Sunset Cruise",
"description":"The Boracay sunset is a spectacular site to see and what better way than cruising on the ocean with an icy cold beverage! Tour along White Beach and take in all the tropical sights and sounds while you enjoy the spectacular arrays of color of the Boracay sunset. Stop off for a quick swim, stand up paddle and snorkel or chill out in tube before heading back to enjoy your dinner in paradise.",
"image_url":"https://cdn5.myboracayguide.com/2019/02/Boracay-Sunset-Cruise-00-400x267.jpg",
"price":"800"
},
{
"id":"6b3c211a-53f3-4c2a-a4f3-dc1fd5d42bfc",
"name":"Group Island Hopping",
"description":"Make new friends by joining a shared boat cruise where you will cruise the shores of Boracay in a traditional Banka boat. Visit the famous Puka shell beach and take a stroll on the beach, swim in the azure waters or just relax with a fresh coconut enjoying the sun. Stop off for a snorkel and see Boracay\u2019s beautiful tropical fish and corals. Finish the trip with a delicious buffet lunch.",
"image_url":"https://cdn5.myboracayguide.com/2019/03/Boracay-Group-Island-Hopping-Boracay-Activities-01-400x267.jpg",
"price":"1500"
},
{
"id":"91e759dc-f385-42e1-8098-a44399bebce8",
"name":"Ultimate Cliff Jumping Island Hopping Adventure",
"description":"Experience a day of fun on Magic Island and have the thrill of a lifetime with 5 different levels of cliff jumping. Relax and swim or go snorkeling around the Island.",
"image_url":"https://cdn5.myboracayguide.com/2016/06/Island-Hopping-Boracay-Activities-400x267-400x267.jpg",
"price":"2200"
},
{
"id":"2178bb44-32f1-4d22-bc95-05cd039a3067",
"name":"4 Hour Private Boracay Island Hopping Package",
"description":"Your Boracay adventure experience will not be complete without this trip! The island is home to more than a dozen undeveloped beaches, turquoise waters and colorful coral reefs! Feast your eyes on the amazing scenery, snorkel and get a glimpse of the thriving sea life!The boat trip includes stopover at some amazing places in Boracay where you can go snorkeling and swimming. Snorkeling gears will be provided for you.",
"image_url":"https://cdn5.myboracayguide.com/2016/09/Private-Island-Hopping-Boracay-Activity-8-400x267.jpg",
"price":"2900"
},
{
"id":"8f086c95-df7d-4914-98b5-e3378663e967",
"name":"Paraw Sailing",
"description":"Paraw Sailing is a local sail boat\u00a0activity. The boats use two outriggers and two sails. Experience the traditional way of sailing and discover the best sites around the island, perfect for photography \u2013 though do note on days with heavier waves the water can kick up a bit (exciting!). If you schedule your activity for later in the afternoon you can take advantage of the incredible sunset while relaxing on the boat for half an hour. Sea sickness? usually not a problem as the boats tend to stay closer into the shore and cut through the waves very well. \u00a0Paraw sailing around Boracay is probably a really good way to ease yourself into the sea and find out how much you like it.",
"image_url":"https://cdn5.myboracayguide.com/2016/03/Paraw-Sailing-Boracay-Activities-400x267.jpg",
"price":"3000"
},
{
"id":"6aa0ed0f-ddcc-42ff-8059-eea5ee40496d",
"name":"Parasailing",
"description":"Parasailing on Boracay is a great experience for a few adventure-minded individuals. Imagine being whisked into the sky while strapped in a seat covered by a colorful parachute! This is a popular activity where riders can view the beautiful shoreline of white beach from above while being pulled by a boat. This is a fun and exciting experience for those who love heights and want a birds-eye-view of the whole island. Up to two guests can occupy the same canopy.",
"image_url":"https://cdn5.myboracayguide.com/2016/04/Parasailing-Boracay-Activities-400x267.jpg",
"price":"2500"
},
{
"id":"c5f7878a-70cd-49da-9a24-ca0b86beb71c",
"name":"Boracay Pub Crawl",
"description":"Boracay Pub Crawl is the biggest, hippest and most happening bar-hopping event on Boracay! Meet amazing people from around the world, play get to know you games to break the ice, drink welcome shooters with your free shooter glass, get discounts on drinks, free entrance in bars, and wear your iconic pub crawl shirt! This is definitely one of the most awesome ways to experience the island\u2019s famous nightlife and also a great chance to take pictures. Boracay PubCrawl is the first of its kind to offer both travelers and locals the chance to party as one big, wild group. Discover the best night spots and make new friends over the course of a great evening out.",
"image_url":"https://cdn5.myboracayguide.com/2016/04/Boracay-Pub-Crawl-Activities-400x267.jpeg",
"price":"990"
},
{
"id":"0c3b6e63-9a92-4662-8a44-d3d9adc334c2",
"name":"Sunset Party Cruise Booty",
"description":"Aside from its amazing parties, Boracay is world famous for its breath-taking sunset. Hop on the island\u2019s 40 passenger party vessel, Booty, and jam with other travelers as you listen to great music! Definitely a memorable party at sundown!",
"image_url":"https://cdn5.myboracayguide.com/2016/04/Sunset-Party-Cruise-Booty-400x267.jpg",
"price":"2500"
},
{
"id":"36b74f58-084d-4b67-9daa-a046296604e6",
"name":"Ariels Point",
"description":"Let us help you experience everything good about Ariel\u2019s Point. Many guests come for the 5 different levels of cliff diving; the cliff diving levels are generally suitable for all types of adventurers. Those that want a great photo in a naturally beautiful place\u00a0while having a bit of a jump, won\u2019t be disappointed or terrified. Similarly, hardcore guests that want to jump from the top of a volcanically hewn outcrop into the deep blue arms of the sea won\u2019t be let down either. \u00a0Ariel\u2019s Point is located near the rustic fishing town of Buruanga, a half hour boat ride from Boracay\u2019s white beach. Gather with other travelers as you snorkel, paddle in a native canoe, or just laze under the sun while enjoying the uniquely rough, &\u00a0comfortable environment.",
"image_url":"https://cdn5.myboracayguide.com/2016/04/Ariels-Point-Boracay-Activities-1-400x267.jpg",
"price":"2800"
},
{
"id":"420cb55b-99cb-45b1-a860-d079cc1d2cea",
"name":"Stand Up Paddle on the Beach",
"description":"Experience how it\u2019s like to glide on the water surface from a Stand-Up Paddle Board. Paddling on a Stand-Up Paddle board for lets you commune with the current of the sea, either by standing up, kneeling or sitting down. It also provides a good exercise to maintain your balance and to strengthen your core, while you paddle into the water to workout your arms and upper body.",
"image_url":"https://cdn5.myboracayguide.com/2016/10/Stand-Up-Paddle-Boracay-Activity-01-400x267.jpg",
"price":"1000"
},
{
"id":"778bad3e-6fb4-4deb-8ff2-120bcbc5f27e",
"name":"Segway Tours",
"description":"Surrender to the freedom of gliding along Boracay\u2019s scenic spots on a Segway. For an exhilarating hour, you will be able to feel a gratifying oneness with the Segway and surrender to its awesome mechanism, giving you a relaxed and confident exploration into the island\u2019s diverse sceneries. This eco-freindly joyride kicks off with a short video presentation at its booth inside the panoramic grounds of Fairways and Blue Water. A routine trial thence follows, and after, the actual Segway ride takes place. The first leg encompasses a comfortable descent on the pavement, where beginners",
"image_url":"https://cdn5.myboracayguide.com/2016/08/Segway-Tours-Boracay-Activities-01-400x267.jpg",
"price":"2300"
},
{
"id":"7c72e357-7228-4b6c-bdd0-d9aab71512ac",
"name":"Helicopter Beach Tour",
"description":"Boracay Helicopter Beach Tour \u2013 It\u2019s never been better. Have a 10-minute adrenaline-filled experience of touring the island by helicopter! See Boracay\u2019s white sands, blue green waters, and reefs from above! This is the ultimate chance to snap birds-eye-view photographs!",
"image_url":"https://cdn5.myboracayguide.com/2010/01/Boracay-Helicopter-Tours-Boracay-Activity-07-400x267.jpg",
"price":"5200"
}
]
In myproject-consumer-web/src/components/products
, create a file called DisplayProduct.vue
:
Add ff code snippet:
<template>
<div class="display-catalog-2">
<h1></h1>
<section class="py-6 bg-gray-100">
<div class="container">
<div class="row mb-5">
<div class="col-md-8">
<p class="subtitle text-secondary">Hurry up, these are expiring soon.</p>
<h2>Last minute deals</h2>
</div>
</div>
<swiper v-bind:options="swiperOption">
<swiper-slide class="h-auto px-2" v-for="product in products" v-bind:key="product.id">
<ProductCard v-bind:product="product"/>
</swiper-slide>
<div class="swiper-pagination" slot="pagination"></div>
</swiper>
</div>
</section>
</div>
</template>
<script>
import ProductCard from '@/components/products/ProductCard.vue'
import data from '@/assets/json/boracay.json'
export default {
name: "display-product",
components: {
ProductCard
},
data() {
return {
products: data,
swiperOption: {
slidesPerView: 3,
spaceBetween: 20,
roundLengths :true,
pagination: {
el: '.swiper-pagination',
clickable: true,
dynamicBullets: true
},
loop: true,
breakpoints: {
1200 :{
slidesPerView: 3
},
991 :{
slidesPerView : 2
},
565 :{
slidesPerView :1
}
},
}
}
}
}
</script>
Explanations: Notice ff line in the snippet above:
import data from '@/assets/json/boracay.json'
we are importing the json data
from boracay.json
and equating this to a variable in in the data
called products
this will return to us a list of all products.
data() {
return {
products: data,
....
we then use the v-for
directive to loop through each product in the products list as seen in this part:
<swiper-slide class="h-auto px-2" v-for="product in products" v-bind:key="product.id">
<ProductCard v-bind:product="product"/>
</swiper-slide>
this will then create an individual ProductCard
component for each element in the list, where ProductCard
will take the individual product
as a prop
as seen in this snippet in ProductCard.vue
<ProductCard v-bind:product="product"/>
*the value of product is binded with the product in the products
list
In myproject-consumer-web/src/views/Home.vue
Add the ff snippets:
In <template>
...START...
<DisplayProducts/>
...END...
In <scripts>
<script>
...START...
import DisplayProducts from '@/components/products/DisplayProduct.vue'
...END...
export default {
name: 'home',
components: {
...START...
DisplayProducts,
...END...
}
}
</script>
Final Version of Home.vue
:
<template>
<div class="home">
<SearchBar/>
<Services/>
<DisplayCatalog1/>
<DisplayProducts/>
<PhotoWheel/>
</div>
</template>
<script>
// @ is an alias to /src
import SearchBar from '@/components/home/SearchBar.vue'
import Services from '@/components/home/Services.vue'
import DisplayCatalog1 from '@/components/home/DisplayCatalog1.vue'
import DisplayProducts from '@/components/products/DisplayProduct.vue'
import PhotoWheel from '@/components/home/PhotoWheel.vue'
export default {
name: 'home',
components: {
SearchBar,
Services,
DisplayCatalog1,
DisplayProducts,
PhotoWheel
}
}
</script>
In myproject-consumer-web/src/views
create a file called Products.vue
Add the following code:
<template>
<div class="product">
<ProductIndex/>
<router-view/>
</div>
</template>
<script type="text/javascript">
import ProductIndex from '@/components/products'
export default {
name: 'products',
components: {
ProductIndex
}
}
</script>
In myproject-consumer-web/src/views/products
create a file called : ProductDetail.vue
Add the following code:
<template>
<div class="product-detail">
<section>
<!-- Additional required wrapper-->
<swiper :options="swiperOption">
<!-- Slides-->
<swiper-slide>
<a href="../../assets/img/photo/photo-1426122402199-be02db90eb90.jpg" data-toggle="gallery-top" title="Our street"><img src="../../assets/img/photo/photo-1426122402199-be02db90eb90.jpg" alt="Our street" class="img-fluid"></a>
</swiper-slide>
<swiper-slide>
<a href="../../assets/img/photo/photo-1512917774080-9991f1c4c750.jpg" data-toggle="gallery-top" title="Outside"><img src="../../assets/img/photo/photo-1512917774080-9991f1c4c750.jpg" alt="Outside" class="img-fluid"></a>
</swiper-slide>
<swiper-slide>
<a href="../../assets/img/photo/photo-1494526585095-c41746248156.jpg" data-toggle="gallery-top" title="Rear entrance"><img src="../../assets/img/photo/photo-1494526585095-c41746248156.jpg" alt="Rear entrance" class="img-fluid"></a>
</swiper-slide>
<swiper-slide>
<a href="../../assets/img/photo/photo-1484154218962-a197022b5858.jpg" data-toggle="gallery-top" title="Kitchen"><img src="../../assets/img/photo/photo-1484154218962-a197022b5858.jpg" alt="Kitchen" class="img-fluid"></a>
</swiper-slide>
<swiper-slide>
<a href="../../assets/img/photo/photo-1522771739844-6a9f6d5f14af.jpg" data-toggle="gallery-top" title="Bedroom"><img src="../../assets/img/photo/photo-1522771739844-6a9f6d5f14af.jpg" alt="Bedroom" class="img-fluid"></a>
</swiper-slide>
<swiper-slide>
<a href="../../assets/img/photo/photo-1488805990569-3c9e1d76d51c.jpg" data-toggle="gallery-top" title="Bedroom"><img src="../../assets/img/photo/photo-1488805990569-3c9e1d76d51c.jpg" alt="Bedroom" class="img-fluid"></a>
</swiper-slide>
</swiper>
<div class="swiper-pagination swiper-pagination-white"></div>
<div class="swiper-button-prev swiper-button-white"></div>
<div class="swiper-button-next swiper-button-white"></div>
</section>
<div class="container py-5">
<div class="row">
<div class="col-lg-8">
<div class="text-block">
<p class="text-primary"><i class="fa-map-marker-alt fa mr-1"></i> Brooklyn, New York</p>
<h1>Mid-Century Modern Garden Paradise</h1>
<p class="text-muted text-uppercase mb-4">Entire Apartment </p>
<ul class="list-inline text-sm mb-4">
<li class="list-inline-item mr-3"><i class="fa fa-users mr-1 text-secondary"></i> 4 guests</li>
<li class="list-inline-item mr-3"><i class="fa fa-door-open mr-1 text-secondary"></i> 1 bedroom</li>
<li class="list-inline-item mr-3"><i class="fa fa-bed mr-1 text-secondary"></i> 3 beds</li>
<li class="list-inline-item mr-3"><i class="fa fa-bath mr-1 text-secondary"></i> 1 bath</li>
</ul>
<p class="text-muted font-weight-light">Our garden basement apartment is fully equipped with everything you need to enjoy your stay. Very comfortable for a couple but plenty of space for a small family. Close to many wonderful Brooklyn attractions and quick trip to Manhattan. </p>
<h6 class="mb-3">The space</h6>
<p class="text-muted font-weight-light">Welcome to Brooklyn! We are excited to share our wonderful neighborhood with you. Our modern apartment has a private entrance, fully equipped kitchen, and a very comfortable queen size bed. We are happy to accommodate additional guests with a single bed in the living room, another comfy mattress on the floor and can make arrangements for small children with a portable crib and highchair if requested. </p>
<p class="text-muted font-weight-light">Also in the apartment:</p>
<ul class="text-muted font-weight-light">
<li>TV with Netflix and DirectTVNow</li>
<li>Free WiFi</li>
<li>Gourmet Coffee/Tea making supplies</li>
<li>Fresh Sheets and Towels</li>
<li>Toaster, microwave, pots and pans and basic cooking needs like salt, pepper, sugar, and olive oil.</li>
<li>Air Conditioning to keep you cool all summer!</li>
</ul>
<p class="text-muted font-weight-light">The apartment is surprisingly quiet for being in the heart of a vibrant, bustling neighborhood.</p>
<h6 class="mb-3">Interaction with guests</h6>
<p class="text-muted font-weight-light">We live in the two floors above the garden apartment so we are usually available to answer questions. The garden apartment is separate from our living space. We are happy to provide advice on local attractions, restaurants and transportation around the city. If there's anything you need please don't hesitate to ask!</p>
</div>
<div class="text-block">
<h4 class="mb-4">Amenities</h4>
<div class="row">
<div class="col-md-6">
<ul class="list-unstyled text-muted">
<li class="mb-2"><i class="fa fa-wifi text-secondary w-1rem mr-3 text-center"></i> <span class="text-sm">Wifi</span></li>
<li class="mb-2"><i class="fa fa-tv text-secondary w-1rem mr-3 text-center"></i> <span class="text-sm">Cable TV</span></li>
<li class="mb-2"><i class="fa fa-snowflake text-secondary w-1rem mr-3 text-center"></i> <span class="text-sm">Air conditioning</span></li>
<li class="mb-2"><i class="fa fa-thermometer-three-quarters text-secondary w-1rem mr-3 text-center"></i> <span class="text-sm">Heating</span></li>
</ul>
</div>
<div class="col-md-6">
<ul class="list-unstyled text-muted">
<li class="mb-2"><i class="fa fa-bath text-secondary w-1rem mr-3 text-center"></i><span class="text-sm">Toiletteries</span></li>
<li class="mb-2"><i class="fa fa-utensils text-secondary w-1rem mr-3 text-center"></i><span class="text-sm">Equipped Kitchen</span></li>
<li class="mb-2"><i class="fa fa-laptop text-secondary w-1rem mr-3 text-center"></i><span class="text-sm">Desk for work</span></li>
<li class="mb-2"><i class="fa fa-tshirt text-secondary w-1rem mr-3 text-center"></i><span class="text-sm">Washing machine</span></li>
</ul>
</div>
</div>
</div>
<div class="text-block">
<h4 class="mb-0">Amenities alternative</h4>
<p class="subtitle text-sm text-primary mb-4">Alternative amenities display</p>
<ul class="list-inline">
<li class="list-inline-item mb-2"><span class="badge badge-pill badge-light p-3 text-muted font-weight-normal">Wifi</span></li>
<li class="list-inline-item mb-2"><span class="badge badge-pill badge-light p-3 text-muted font-weight-normal">Cable TV</span></li>
<li class="list-inline-item mb-2"><span class="badge badge-pill badge-light p-3 text-muted font-weight-normal">Air conditioning</span></li>
<li class="list-inline-item mb-2"><span class="badge badge-pill badge-light p-3 text-muted font-weight-normal">Heating</span></li>
<li class="list-inline-item mb-2"><span class="badge badge-pill badge-light p-3 text-muted font-weight-normal">Toiletteries</span></li>
<li class="list-inline-item mb-2"><span class="badge badge-pill badge-light p-3 text-muted font-weight-normal">Equipped Kitchen</span></li>
<li class="list-inline-item mb-2"><span class="badge badge-pill badge-light p-3 text-muted font-weight-normal">Desk for work</span></li>
<li class="list-inline-item mb-2"><span class="badge badge-pill badge-light p-3 text-muted font-weight-normal">Washing machine</span></li>
</ul>
</div>
<div class="text-block">
<div class="media"><img src="../../assets/img/avatar/avatar-10.jpg" alt="Jack London" class="avatar avatar-lg mr-4">
<div class="media-body">
<p> <span class="text-muted text-uppercase text-sm">Hosted by </span><br><strong>Jack London</strong></p>
<p class="text-muted text-sm mb-2">Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore.</p>
<p class="text-muted text-sm">Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. </p>
<p class="text-sm"><a href="#">See Jack's 3 other listings <i class="fa fa-long-arrow-alt-right ml-2"></i></a></p>
</div>
</div>
</div>
<div class="text-block">
<h5 class="mb-4">Listing location</h5>
<div class="map-wrapper-300 mb-3">
<div id="detailMap" class="h-100"></div>
</div>
</div>
<div class="text-block">
<h5 class="mb-4">Gallery</h5>
<div class="row gallery mb-3 ml-n1 mr-n1">
<div class="col-lg-4 col-6 px-1 mb-2"><a href="../../assets/img/photo/photo-1426122402199-be02db90eb90.jpg" data-fancybox="gallery" title="Our street"><img src="../../assets/img/photo/photo-1426122402199-be02db90eb90.jpg" alt="..." class="img-fluid"></a></div>
<div class="col-lg-4 col-6 px-1 mb-2"><a href="../../assets/img/photo/photo-1512917774080-9991f1c4c750.jpg" data-fancybox="gallery" title="Outside"><img src="../../assets/img/photo/photo-1512917774080-9991f1c4c750.jpg" alt="..." class="img-fluid"></a></div>
<div class="col-lg-4 col-6 px-1 mb-2"><a href="../../assets/img/photo/photo-1494526585095-c41746248156.jpg" data-fancybox="gallery" title="Rear entrance"><img src="../../assets/img/photo/photo-1494526585095-c41746248156.jpg" alt="..." class="img-fluid"></a></div>
<div class="col-lg-4 col-6 px-1 mb-2"><a href="../../assets/img/photo/photo-1484154218962-a197022b5858.jpg" data-fancybox="gallery" title="Kitchen"><img src="../../assets/img/photo/photo-1484154218962-a197022b5858.jpg" alt="..." class="img-fluid"></a></div>
<div class="col-lg-4 col-6 px-1 mb-2"><a href="../../assets/img/photo/photo-1522771739844-6a9f6d5f14af.jpg" data-fancybox="gallery" title="Bedroom"><img src="../../assets/img/photo/photo-1522771739844-6a9f6d5f14af.jpg" alt="..." class="img-fluid"></a></div>
<div class="col-lg-4 col-6 px-1 mb-2"><a href="../../assets/img/photo/photo-1488805990569-3c9e1d76d51c.jpg" data-fancybox="gallery" title="Bedroom"><img src="../../assets/img/photo/photo-1488805990569-3c9e1d76d51c.jpg" alt="..." class="img-fluid"></a></div>
</div>
</div>
<div class="text-block">
<p class="subtitle text-sm text-primary">Reviews </p>
<h5 class="mb-4">Listing Reviews </h5>
<div class="media d-block d-sm-flex review">
<div class="text-md-center mr-4 mr-xl-5"><img src="../../assets/img/avatar/avatar-8.jpg" alt="Padmé Amidala" class="d-block avatar avatar-xl p-2 mb-2"><span class="text-uppercase text-muted text-sm">Dec 2018</span></div>
<div class="media-body">
<h6 class="mt-2 mb-1">Padmé Amidala</h6>
<div class="mb-2"><i class="fa fa-xs fa-star text-primary"></i><i class="fa fa-xs fa-star text-primary"></i><i class="fa fa-xs fa-star text-primary"></i><i class="fa fa-xs fa-star text-primary"></i><i class="fa fa-xs fa-star text-primary"></i>
</div>
<p class="text-muted text-sm">One morning, when Gregor Samsa woke from troubled dreams, he found himself transformed in his bed into a horrible vermin. He lay on his armour-like back, and if he lifted his head a little he could see his brown belly, slightly domed and divided by arches into stiff sections </p>
</div>
</div>
<div class="media d-block d-sm-flex review">
<div class="text-md-center mr-4 mr-xl-5"><img src="../../assets/img/avatar/avatar-2.jpg" alt="Luke Skywalker" class="d-block avatar avatar-xl p-2 mb-2"><span class="text-uppercase text-muted text-sm">Dec 2018</span></div>
<div class="media-body">
<h6 class="mt-2 mb-1">Luke Skywalker</h6>
<div class="mb-2"><i class="fa fa-xs fa-star text-primary"></i><i class="fa fa-xs fa-star text-primary"></i><i class="fa fa-xs fa-star text-primary"></i><i class="fa fa-xs fa-star text-primary"></i><i class="fa fa-xs fa-star text-gray-200"></i>
</div>
<p class="text-muted text-sm">The bedding was hardly able to cover it and seemed ready to slide off any moment. His many legs, pitifully thin compared with the size of the rest of him, waved about helplessly as he looked. "What's happened to me?" he thought. It wasn't a dream. </p>
</div>
</div>
<div class="media d-block d-sm-flex review">
<div class="text-md-center mr-4 mr-xl-5"><img src="../../assets/img/avatar/avatar-3.jpg" alt="Princess Leia" class="d-block avatar avatar-xl p-2 mb-2"><span class="text-uppercase text-muted text-sm">Dec 2018</span></div>
<div class="media-body">
<h6 class="mt-2 mb-1">Princess Leia</h6>
<div class="mb-2"><i class="fa fa-xs fa-star text-primary"></i><i class="fa fa-xs fa-star text-primary"></i><i class="fa fa-xs fa-star text-primary"></i><i class="fa fa-xs fa-star text-gray-200"></i><i class="fa fa-xs fa-star text-gray-200"></i>
</div>
<p class="text-muted text-sm">His room, a proper human room although a little too small, lay peacefully between its four familiar walls. A collection of textile samples lay spread out on the table. </p>
</div>
</div>
<div class="media d-block d-sm-flex review">
<div class="text-md-center mr-4 mr-xl-5"><img src="../../assets/img/avatar/avatar-4.jpg" alt="Jabba Hut" class="d-block avatar avatar-xl p-2 mb-2"><span class="text-uppercase text-muted text-sm">Dec 2018</span></div>
<div class="media-body">
<h6 class="mt-2 mb-1">Jabba Hut</h6>
<div class="mb-2"><i class="fa fa-xs fa-star text-primary"></i><i class="fa fa-xs fa-star text-primary"></i><i class="fa fa-xs fa-star text-primary"></i><i class="fa fa-xs fa-star text-primary"></i><i class="fa fa-xs fa-star text-primary"></i>
</div>
<p class="text-muted text-sm">Samsa was a travelling salesman - and above it there hung a picture that he had recently cut out of an illustrated magazine and housed in a nice, gilded frame. </p>
</div>
</div>
<div class="py-5">
<button type="button" data-toggle="collapse" data-target="#leaveReview" aria-expanded="false" aria-controls="leaveReview" class="btn btn-outline-primary">Leave a review</button>
<div id="leaveReview" class="collapse mt-4">
<h5 class="mb-4">Leave a review</h5>
<form id="contact-form" method="get" action="#" class="form">
<div class="row">
<div class="col-sm-6">
<div class="form-group">
<label for="name" class="form-label">Your name *</label>
<input type="text" name="name" id="name" placeholder="Enter your name" required="required" class="form-control">
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<label for="rating" class="form-label">Your rating *</label>
<select name="rating" id="rating" class="custom-select focus-shadow-0">
<option value="5">★★★★★ (5/5)</option>
<option value="4">★★★★☆ (4/5)</option>
<option value="3">★★★☆☆ (3/5)</option>
<option value="2">★★☆☆☆ (2/5)</option>
<option value="1">★☆☆☆☆ (1/5)</option>
</select>
</div>
</div>
</div>
<div class="form-group">
<label for="email" class="form-label">Your email *</label>
<input type="email" name="email" id="email" placeholder="Enter your email" required="required" class="form-control">
</div>
<div class="form-group">
<label for="review" class="form-label">Review text *</label>
<textarea rows="4" name="review" id="review" placeholder="Enter your review" required="required" class="form-control"></textarea>
</div>
<button type="submit" class="btn btn-primary">Post review</button>
</form>
</div>
</div>
</div>
</div>
<div class="col-lg-4">
<div style="top: 100px;" class="p-4 shadow ml-lg-4 rounded sticky-top">
<p class="text-muted"><span class="text-primary h2">$120</span> per night</p>
<hr class="my-4">
<form id="booking-form" method="get" action="#" autocomplete="off" class="form">
<div class="form-group">
<label for="bookingDate" class="form-label">Your stay *</label>
<div class="datepicker-container datepicker-container-right">
<input type="text" name="bookingDate" id="bookingDate" placeholder="Choose your dates" required="required" class="form-control">
</div>
</div>
<div class="form-group mb-4">
<label for="guests" class="form-label">Guests *</label>
<select name="guests" id="guests" class="form-control">
<option value="1">1 Guest</option>
<option value="2">2 Guests</option>
<option value="3">3 Guests</option>
<option value="4">4 Guests</option>
<option value="5">5 Guests</option>
</select>
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary btn-block">Book your stay</button>
</div>
</form>
<p class="text-muted text-sm text-center">Some additional text can be also placed here.</p>
<hr class="my-4">
<div class="text-center">
<p> <a href="#" class="text-secondary text-sm"> <i class="fa fa-heart"></i> Bookmark This Listing</a></p>
<p class="text-muted text-sm">79 people bookmarked this place </p>
</div>
</div>
</div>
</div>
</div>
<div class="py-6 bg-gray-100">
<DisplayCatalog2/>
</div>
</div>
</template>
<script type="text/javascript">
import DisplayCatalog2 from '@/components/DisplayCatalog2.vue'
export default {
name: 'product-detail',
components: {
DisplayCatalog2,
},
data() {
return {
swiperOption: {
slidesPerView: 2,
spaceBetween: 0,
centeredSlides: true,
loop: true,
// If we need pagination
pagination: {
el: '.swiper-pagination',
clickable: true,
dynamicBullets: true
},
// Navigation arrows
navigation: {
nextEl: '.swiper-button-next',
prevEl: '.swiper-button-prev',
},
}
}
}
}
</script>
In myproject-consumer-web/src/router.js
Add the following snippet:
{
path: '/login',
name: 'login',
component: () => import('./views/Login.vue')
},
{
path: '/signup',
name: 'sigup',
component: () => import('./views/SignUp.vue')
},
... start ..
{
path: '/products',
name: 'products',
component: () => import('./views/Products.vue'),
},
{
path: '/products/:id',
name: 'product_detail',
component: () => import('./components/products/ProductDetail.vue')
}
... end ...
Final version of router.js
:
import Vue from 'vue'
import Router from 'vue-router'
import Home from './views/Home.vue'
Vue.use(Router)
export default new Router({
mode: 'history',
base: process.env.BASE_URL,
routes: [
{
path: '/',
name: 'home',
component: Home
},
{
path: '/about',
name: 'about',
component: () => import('./views/About.vue')
},
{
path: '/login',
name: 'login',
component: () => import('./views/Login.vue')
},
{
path: '/signup',
name: 'sigup',
component: () => import('./views/SignUp.vue')
},
{
path: '/products',
name: 'products',
component: () => import('./views/Products.vue'),
},
{
path: '/products/:id',
name: 'product_detail',
component: () => import('./components/products/ProductDetail.vue')
}
]
})
In src/views
folder create a file called Booking.vue
In Booking.vue
, copy the following code:
<template>
<div class="booking">
<router-view/>
</div>
</template>
<script>
</script>
In myproject-consumer-web/src/
Add the following snippet in router.js
:
{
path: '/products/:id',
name: 'product_detail',
component: () => import('./components/products/ProductDetail.vue')
},
... start ...
{
path: '/booking',
name: 'booking',
component: () => import('./views/Booking.vue'),
}
... end ...
In src/components
folder create a folder called booking
$ cd src/components
$ mkdir booking
$ cd booking
In the booking
folder, create a file called BookingPanel.vue
In BookingPanel.vue
copy the following code:
*Note that BookingPanel will be used as a child component for the succeeding steps.
<template>
<div class="booking-panel">
<div class="card border-0 shadow">
<div class="card-body p-4">
<div class="text-block pb-3">
<div class="media align-items-center">
<div class="media-body">
<h6> <a href="detail-rooms.html" class="text-reset">Modern Apt - Vibrant Neighborhood</a></h6>
<p class="text-muted text-sm mb-0">Entire home in New York</p>
<div class="mt-n1"><i class="fa fa-xs fa-star text-primary"></i><i class="fa fa-xs fa-star text-primary"></i><i class="fa fa-xs fa-star text-primary"></i><i class="fa fa-xs fa-star text-primary"></i><i class="fa fa-xs fa-star text-gray-200"></i>
</div>
</div><a href="detail-rooms.html"><img src="../../assets/img/photo/photo-1512917774080-9991f1c4c750.jpg" alt="" width="100" class="ml-3 rounded"></a>
</div>
</div>
<div class="text-block py-3">
<ul class="list-unstyled mb-0">
<li class="mb-3"><i class="fas fa-users fa-fw text-muted mr-2"></i>3 guests</li>
<li class="mb-0"><i class="far fa-calendar fa-fw text-muted mr-2"></i>Apr 17, 2019 <i class="fas fa-arrow-right fa-fw text-muted mx-3"></i>Apr 18, 2019</li>
</ul>
</div>
<div class="text-block pt-3 pb-0">
<table class="w-100">
<tbody>
<tr>
<th class="font-weight-normal py-2">$432.02 x 1 night</th>
<td class="text-right py-2">$432.02</td>
</tr>
<tr>
<th class="font-weight-normal pt-2 pb-3">Service fee</th>
<td class="text-right pt-2 pb-3">$67.48</td>
</tr>
</tbody>
<tfoot>
<tr class="border-top">
<th class="pt-3">Total</th>
<td class="font-weight-bold text-right pt-3">$499.50</td>
</tr>
</tfoot>
</table>
</div>
</div>
<div class="card-footer bg-primary-light py-4 border-0">
<div class="media align-items-center">
<div class="media-body">
<h6 class="text-primary">Flexible – free cancellation</h6>
<p class="text-sm text-primary opacity-8 mb-0">Cancel within 48 hours of booking to get a full refund. <a href="#" class="text-reset ml-3">More details</a></p>
</div>
<svg class="svg-icon svg-icon svg-icon-light w-3rem h-3rem ml-2 text-primary">
<use xlink:href="#diploma-1"> </use>
</svg>
</div>
</div>
</div>
</div>
</template>
In the booking
folder, create a file called Step1.vue
In Step1.vue
copy the following code:
<template>
<div class="booking-step1">
<!-- progress bar -->
<div style="height: 8px; top: 71px;" class="progress rounded-0 sticky-top">
<div role="progressbar" style="width: 25%" aria-valuenow="25" aria-valuemin="0" aria-valuemax="100" class="progress-bar"></div>
</div>
<section class="py-5">
<div class="container">
<div class="row">
<div class="col-lg-7">
<p class="subtitle text-primary">Book your holiday home</p>
<h1 class="h2 mb-5"> Review house rules <span class="text-muted float-right">Step 1</span> </h1>
<div class="text-block">
<div class="alert alert-warning text-sm mb-3">
<div class="media align-items-center">
<svg class="svg-icon svg-icon svg-icon-light w-2rem h-2rem mr-3 text-reset">
<use xlink:href="#heart-1"> </use>
</svg>
<div class="media-body"><strong>This home is on people’s minds.</strong> It’s been viewed 43 times in the past week.</div>
</div>
</div>
</div>
<div class="text-block">
<h5 class="mb-4">1 night in London</h5>
<div class="row mb-3">
<div class="col-md-6 d-flex align-items-center mb-3 mb-md-0">
<div class="date-tile mr-3">
<div class="text-uppercase"> <span class="text-sm">Apr</span><br><strong class="text-lg">17</strong></div>
</div>
<p class="text-sm mb-0">Wednesday check-in<br>3PM - 7PM</p>
</div>
<div class="col-md-6 d-flex align-items-center">
<div class="date-tile mr-3">
<div class="text-uppercase"> <span class="text-sm">Apr</span><br><strong class="text-lg">18</strong></div>
</div>
<p class="text-sm mb-0">Thursday check-out<br>11AM</p>
</div>
</div>
</div>
<div class="text-block">
<h5 class="mb-4">Things to keep in mind</h5>
<ul class="list-unstyled">
<li class="mb-2">
<div class="media align-items-center mb-3">
<div class="icon-rounded icon-rounded-sm bg-secondary-light mr-4"><i class="fa fas fa-child text-secondary fa-fw text-center"></i></div>
<div class="media-body"><span class="text-sm">Not suitable for children and infants - The entrance staircase doesn't have handrails</span></div>
</div>
</li>
<li class="mb-2">
<div class="media align-items-center mb-3">
<div class="icon-rounded icon-rounded-sm bg-secondary-light mr-4"><i class="fa fas fa-glass-cheers text-secondary fa-fw text-center"></i></div>
<div class="media-body"><span class="text-sm">No parties or events</span></div>
</div>
</li>
<li class="mb-2">
<div class="media align-items-center mb-3">
<div class="icon-rounded icon-rounded-sm bg-secondary-light mr-4"><i class="fa fa-smoking-ban text-secondary fa-fw text-center"></i></div>
<div class="media-body"><span class="text-sm">No smoking</span></div>
</div>
</li>
<li class="mb-2">
<div class="media align-items-center mb-3">
<div class="icon-rounded icon-rounded-sm bg-secondary-light mr-4"><i class="fa fa-cat text-secondary fa-fw text-center"></i></div>
<div class="media-body"><span class="text-sm">No pets</span></div>
</div>
</li>
</ul>
</div>
<div class="row form-block flex-column flex-sm-row">
<div class="col text-center text-sm-left">
</div>
<div class="col text-center text-sm-right"><router-link to="step2" class="btn btn-primary px-3">
Next step<i class="fa-chevron-right fa ml-2"></i></router-link></div>
</div>
</div>
<div class="col-lg-5 pl-xl-5">
<!-- Add booking panel here -->
<BookingPanel/>
</div>
</div>
</div>
</section>
</div>
</template>
<script>
import BookingPanel from '@/components/booking/BookingPanel.vue'
export default {
name: 'step1',
components: {
BookingPanel
}
}
</script>
In the booking
folder, create a file called Step1.vue
In Step2.vue
copy the following code:
<template>
<div class="booking-step2">
<div style="height: 8px; top: 71px;" class="progress rounded-0 sticky-top">
<div role="progressbar" style="width: 50%" aria-valuenow="50" aria-valuemin="0" aria-valuemax="100" class="progress-bar"></div>
</div>
<section class="py-5">
<div class="container">
<div class="row">
<div class="col-lg-7">
<p class="subtitle text-primary">Book your holiday home</p>
<h1 class="h2 mb-5"> Who's coming? <span class="text-muted float-right">Step 2</span> </h1>
<div class="text-block">
<div class="alert alert-warning text-sm mb-3">
<div class="media align-items-center">
<svg class="svg-icon svg-icon svg-icon-light w-2rem h-2rem mr-3 text-reset">
<use xlink:href="#heart-1"> </use>
</svg>
<div class="media-body"><strong>This home is on people’s minds.</strong> It’s been viewed 43 times in the past week.</div>
</div>
</div>
</div>
<div class="text-block">
<label for="form_guests" class="h5">Guests</label>
<p class="text-sm text-muted">One morning, when Gregor Samsa woke from troubled dreams, he found himself transformed in his bed in.</p>
<div class="row">
<div class="col-lg-6 mb-3">
<select name="guests" id="form_guests" data-style="btn-selectpicker" title=" " class="selectpicker form-control">
<option value="guests_0">1</option>
<option value="guests_1">2</option>
<option value="guests_2" selected>3</option>
<option value="guests_3">4</option>
<option value="guests_4">5</option>
</select>
</div>
</div>
</div>
<div class="text-block">
<h5>What's the main purpose of this trip?</h5>
<p class="text-sm text-muted">The bedding was hardly able to cover it and seemed ready to slide off any moment. His many legs, pit.</p>
<ul class="list-unstyled">
<li>
<div class="custom-control custom-radio">
<input type="radio" id="purpose_0" name="purpose" class="custom-control-input">
<label for="purpose_0" class="custom-control-label">Personal travel </label>
</div>
</li>
<li>
<div class="custom-control custom-radio">
<input type="radio" id="purpose_1" name="purpose" class="custom-control-input">
<label for="purpose_1" class="custom-control-label">Business travel </label>
</div>
</li>
</ul>
</div>
<div class="text-block">
<div class="media">
<div class="media-body">
<h5>Say hello to your host</h5>
<p class="text-sm text-muted">His room, a proper human room although a little too small, lay peacefully between its four familiar .</p>
</div><img src="../../assets/img/avatar/avatar-10.jpg" alt="Jack London" class="avatar ml-4">
</div>
<textarea name="hello" rows="4" class="form-control"></textarea>
</div>
<div class="row form-block flex-column flex-sm-row">
<div class="col text-center text-sm-left"><router-link to="step1" class="btn btn-link text-muted"> <i class="fa-chevron-left fa mr-2"></i>Back</router-link>
</div>
<div class="col text-center text-sm-right"><router-link to="payment" class="btn btn-primary px-3">
Next step<i class="fa-chevron-right fa ml-2"></i></router-link></div>
</div>
</div>
<div class="col-lg-5 pl-xl-5">
<!-- add booking panel here -->
<BookingPanel/>
</div>
</div>
</div>
</section>
</div>
</template>
<script>
import BookingPanel from '@/components/booking/BookingPanel.vue'
export default {
name: 'step1',
components: {
BookingPanel
}
}
</script>
In the booking
folder, create a file called Step3.vue
In Step3.vue
copy the following code:
<template>
<div class="booking-step3">
<div style="height: 8px; top: 71px;" class="progress rounded-0 sticky-top">
<div role="progressbar" style="width: 75%" aria-valuenow="75" aria-valuemin="0" aria-valuemax="100" class="progress-bar"></div>
</div>
<section class="py-5">
<div class="container">
<div class="row">
<div class="col-lg-7">
<p class="subtitle text-primary">Book your holiday home</p>
<h1 class="h2 mb-5"> Confirm and pay <span class="text-muted float-right">Step 3</span> </h1>
<div class="text-block">
<div class="alert alert-warning text-sm mb-3">
<div class="media align-items-center">
<svg class="svg-icon svg-icon svg-icon-light w-2rem h-2rem mr-3 text-reset">
<use xlink:href="#heart-1"> </use>
</svg>
<div class="media-body"><strong>This home is on people’s minds.</strong> It’s been viewed 43 times in the past week.</div>
</div>
</div>
</div>
<div class="text-block">
<form action="#">
<div class="d-flex justify-content-between align-items-end mb-4">
<h5 class="mb-0">Pay with your card</h5>
<div class="text-muted"><i class="fab fa-cc-amex fa-2x mr-2"> </i><i class="fab fa-cc-visa fa-2x mr-2"> </i><i class="fab fa-cc-mastercard fa-2x"></i></div>
</div>
<select name="payment" id="form_payment" data-style="btn-selectpicker" class="selectpicker form-control mb-3">
<option value="">Visa •••• 5687</option>
<option value="">Mastercard •••• 4569</option>
</select>
<button type="button" data-toggle="collapse" data-target="#addNewCard" aria-expanded="false" aria-controls="addNewCard" data-expanded-text="Close" data-collapsed-text="Add a new card" class="btn btn-link btn-collapse pl-0 text-muted">Add a new card</button>
<div id="addNewCard" class="row collapse">
<div class="form-group col-md-6">
<label for="card-name" class="form-label">Name on Card</label>
<input type="text" name="card-name" placeholder="Name on card" id="card-name" class="form-control">
</div>
<div class="form-group col-md-6">
<label for="card-number" class="form-label">Card Number</label>
<input type="text" name="card-number" placeholder="Card number" id="card-number" class="form-control">
</div>
<div class="form-group col-md-4">
<label for="expiry-date" class="form-label">Expiry Date</label>
<input type="text" name="expiry-date" placeholder="MM/YY" id="expiry-date" class="form-control">
</div>
<div class="form-group col-md-4">
<label for="cvv" class="form-label">CVC/CVV</label>
<input type="text" name="cvv" placeholder="123" id="cvv" class="form-control">
</div>
<div class="form-group col-md-4">
<label for="zip" class="form-label">ZIP</label>
<input type="text" name="zip" placeholder="123" id="zip" class="form-control">
</div>
</div>
</form>
</div>
<div class="text-block">
<h6>Cancellation policy</h6>
<p class="text-sm text-muted">Samsa was a travelling salesman - and above it there hung a picture that he had recently cut out of .</p>
<p class="text-sm text-muted">He must have tried it a hundred times, shut his eyes so that he wouldn't have to look at the flounde.</p>
</div>
<div class="row form-block flex-column flex-sm-row">
<div class="col text-center text-sm-left"><a href="step2" class="btn btn-link text-muted"> <i class="fa-chevron-left fa mr-2"></i>Back</a>
</div>
<div class="col text-center text-sm-right"><a href="confirmed" class="btn btn-primary px-3">
Next step<i class="fa-chevron-right fa ml-2"></i></a></div>
</div>
</div>
<div class="col-lg-5 pl-xl-5">
<!-- add booking panel here -->
<BookingPanel/>
</div>
</div>
</div>
</section>
</div>
</template>
<script>
import BookingPanel from '@/components/booking/BookingPanel.vue'
export default {
name: 'step1',
components: {
BookingPanel
}
}
</script>
In the booking
folder, create a file called Step4.vue
In Step4.vue
copy the following code:
<template>
<div class="booking-step4">
<div style="height: 8px; top: 71px;" class="progress rounded-0 sticky-top">
<div role="progressbar" style="width: 100%" aria-valuenow="100" aria-valuemin="0" aria-valuemax="100" class="progress-bar"></div>
</div>
<section class="py-5">
<div class="container">
<div class="row">
<div class="col-lg-7">
<p class="subtitle text-primary">Book your holiday home</p>
<h1 class="h2 mb-5"> Booking placed <span class="text-muted float-right">Step 4</span> </h1>
<div class="text-block">
<p class="text-muted">Thank you for your booking, Ondrej. </p>
<p class="text-muted mb-5">Samsa was a travelling salesman - and above it there hung a picture that he had recently cut out of an illustrated magazine and housed in a nice, gilded frame.</p>
<p class="text-center mb-5"><a href="user-booking-detail.html" class="btn btn-primary mx-2 mb-2"> <i class="far fa-file mr-2"></i>View your order</a><router-link to="/" class="btn btn-outline-muted mb-2">Or something else</router-link></p>
<p class="mb-5 text-center"><img src="img/illustration/undraw_celebration_0jvk.svg" alt="" style="width: 400px;" class="img-fluid"></p>
</div>
</div>
<div class="col-lg-5 pl-xl-5">
<!-- add booking panel here -->
<BookingPanel/>
</div>
</div>
</div>
</section>
</div>
</template>
<script>
import BookingPanel from '@/components/booking/BookingPanel.vue'
export default {
name: 'step1',
components: {
BookingPanel
}
}
</script>
In myproject-consumer-web/src/
Add the following snippet in router.js
:
under the path /booking
, add a key called children that takes in a list of child paths:
{
path: '/booking',
name: 'booking',
component: () => import('./views/Booking.vue'),
... start ...
children: [
{
path: 'step1',
component: () => import('./components/booking/Step1.vue'),
},
{
path: 'step2',
component: () => import('./components/booking/Step2.vue'),
},
{
path: 'payment',
component: () => import('./components/booking/Step3.vue'),
},
{
path: 'confirmed',
component: () => import('./components/booking/Step4.vue'),
}
]
... end ...
}
*this will allow us to access our bookings via, /booking/<path to step>
eg:
http://[hostname]/booking/step1
http://[hostname]/booking/step2
http://[hostname]/booking/payment
http://[hostname]/booking/confirmed
Final version of router.js
:
import Vue from 'vue'
import Router from 'vue-router'
import Home from './views/Home.vue'
Vue.use(Router)
export default new Router({
mode: 'history',
base: process.env.BASE_URL,
routes: [
{
path: '/',
name: 'home',
component: Home
},
{
path: '/about',
name: 'about',
component: () => import('./views/About.vue')
},
{
path: '/login',
name: 'login',
component: () => import('./views/Login.vue')
},
{
path: '/signup',
name: 'sigup',
component: () => import('./views/SignUp.vue')
},
{
path: '/products',
name: 'products',
component: () => import('./views/Products.vue'),
},
{
path: '/products/:id',
name: 'product_detail',
component: () => import('./components/products/ProductDetail.vue')
},
{
path: '/booking',
name: 'booking',
component: () => import('./views/Booking.vue'),
children: [
{
path: 'step1',
component: () => import('./components/booking/Step1.vue'),
},
{
path: 'step2',
component: () => import('./components/booking/Step2.vue'),
},
{
path: 'payment',
component: () => import('./components/booking/Step3.vue'),
},
{
path: 'confirmed',
component: () => import('./components/booking/Step4.vue'),
}
]
}
]
})
In the terminal run, inside your project folder, the following command:
$ npm install axios --save
in myproject-vuejs-web/package.json
you must see the following dependencies:
"dependencies": {
"bootstrap": "^4.3.1",
"jquery": "^3.4.1",
"popper.js": "^1.15.0",
"axios": "^0.18.0"
},
In myproject-vuejs-web/src/main.js
Add the following lines:
Vue.use(VueAwesomeSwiper);
... start ...
import Axios from 'axios';
Vue.prototype.$http = Axios;
... end ...
In myproject-vuejs-web/src/components/products/
Create a file called: DisplayProduct.vue
Add the following code:
<template>
<div class="display-catalog-2">
<h1></h1>
<section class="py-6 bg-gray-100">
<div class="container">
<div class="row mb-5">
<div class="col-md-8">
<p class="subtitle text-secondary">Hurry up, these are expiring soon.</p>
<h2>Last minute deals</h2>
</div>
<div class="col-md-4 d-lg-flex align-items-center justify-content-end"><a href="category.html" class="text-muted text-sm">
See all deals<i class="fas fa-angle-double-right ml-2"></i></a></div>
</div>
<swiper v-bind:options="swiperOption">
<swiper-slide class="h-auto px-2" v-for="product in products" v-bind:key="product.product_id">
<ProductCard v-bind:product="product"/>
</swiper-slide>
<div class="swiper-pagination" slot="pagination"></div>
</swiper>
</div>
</section>
</div>
</template>
<script>
import ProductCard from '@/components/products/ProductCard.vue'
export default {
name: "display-product",
components: {
ProductCard
},
data() {
return {
products: null,
swiperOption: {
slidesPerView: 3,
spaceBetween: 20,
roundLengths :true,
pagination: {
el: '.swiper-pagination',
clickable: true,
dynamicBullets: true
},
loop: true,
breakpoints: {
1200 :{
slidesPerView: 3
},
991 :{
slidesPerView : 2
},
565 :{
slidesPerView :1
}
},
}
}
},
methods: {
getProducts: function(){
const baseURI = 'http://localhost:5000/products'
this.$http.get(baseURI)
.then(result => {
this.products = result.data['products'];
})
.catch(error =>{
alert(error);
})
}
},
created: function(){
this.getProducts()
}
}
</script>
In the <script>
tag of DisplayProduct.vue
we call an external endpoint using axios
using the following:
methods: {
getProducts: function(){
const baseURI = 'http://localhost:5000/products'
this.$http.get(baseURI)
.then(result => {
this.products = result.data['products'];
})
.catch(error =>{
alert(error);
})
}
},
created: function(){
this.getProducts()
}
}
OnceDisplayProduct.vue
is initialized, we make an http
GET
request to our backend service returning a list of products as json.
We then loop through each product in the returned json using the v-for
function:
<swiper v-bind:options="swiperOption">
<swiper-slide class="h-auto px-2" v-for="product in products" v-bind:key="product.product_id">
<ProductCard v-bind:product="product"/>
</swiper-slide>
<div class="swiper-pagination" slot="pagination"></div>
</swiper>
For each product, we create a new ProductCard
component which takes in the individual product details such as the product.id
, product.name
, product.image_url
, product.price
<swiper-slide class="h-auto px-2" v-for="product in products" v-bind:key="product.product_id">
<ProductCard v-bind:product="product"/> <--- creates a new product card for each product in the list
</swiper-slide>
In myproject-vuejs-web/src/components/products/
Create a file called: ProductCard.vue
Add the following code:
<template class="product-card">
<div data-marker-id="59c0c8e33b1527bfe2abaf92" class="w-100 h-100">
<div class="card h-100 border-0 shadow">
<div class="card-img-top overflow-hidden gradient-overlay"> <img v-bind:src="product.image_url" v-bind:alt="product.name" class="img-fluid"/>
<router-link v-bind:to="{ name: 'product_detail', params: { product_id: product.product_id }}" class="tile-link"></router-link>
</div>
<div class="card-body d-flex align-items-center">
<div class="w-100">
<h6 class="card-title"><router-link v-bind:to="{ name: 'product_detail', params: { product_id: product.product_id }}" class="tile-link"></router-link>{{product.name }}</h6>
<div class="d-flex card-subtitle mb-3">
<p class="flex-grow-1 mb-0 text-muted text-sm ellipsis">{{product.description}}</p>
<p class="flex-shrink-1 mb-0 card-stars text-xs text-right"><i class="fa fa-star text-warning"></i><i class="fa fa-star text-warning"></i><i class="fa fa-star text-warning"></i><i class="fa fa-star text-warning"></i><i class="fa fa-star text-warning"></i>
</p>
</div>
<p class="card-text text-muted"><span class="h4 text-primary">Php. {{product.price}}</span> per night</p>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'product-card',
props: ['product'],
data(){
return {
obj: this.product,
product_id: this.product.product_id
}
}
}
</script>
<style scoped>
.ellipsis {
text-overflow: ellipsis;
/* Required for text-overflow to do anything */
white-space: nowrap;
overflow: hidden;
}
</style>
Since the product is passed into the ProductCard
as a prop, we are able to assess the data using
{{ product.id }}
{{ product.name }}
{{ product.image_url }}
{{ product.price }}
We also use a router-link to pass the individual product.id to the ProductDetail.vue
using the following
<router-link v-bind:to="{ name: 'product_detail', params: { product_id: product.id }}" class="tile-link"></router-link>
When the user clicks on the a product in the listing, the are redirected to the product with the appropriate id and record.
In myproject-vuejs-web/src/components/products/
Replace the contents of : ProductDetail.vue
, with the following code:
<template>
<div class="product-detail">
<div class="container py-5">
<div class="row">
<div class="col-lg-8">
<div class="text-block">
<h1>{{ product.name }}</h1>
<div class="text-block"><img v-bind:src="product.image_url"></div>
<ul class="list-inline text-sm mb-4">
<li class="list-inline-item mr-3"><i class="fa fa-users mr-1 text-secondary"></i> 4 guests</li>
<li class="list-inline-item mr-3"><i class="fa fa-door-open mr-1 text-secondary"></i> 1 bedroom</li>
<li class="list-inline-item mr-3"><i class="fa fa-bed mr-1 text-secondary"></i> 3 beds</li>
<li class="list-inline-item mr-3"><i class="fa fa-bath mr-1 text-secondary"></i> 1 bath</li>
</ul>
<p class="text-muted font-weight-light">{{product.description}}</p>
</div>
<div class="text-block">
<div class="media"><img src="../../assets/img/avatar/avatar-10.jpg" alt="Jack London" class="avatar avatar-lg mr-4">
<div class="media-body">
<p> <span class="text-muted text-uppercase text-sm">Hosted by </span><br><strong>Jack London</strong></p>
<p class="text-muted text-sm mb-2">Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore.</p>
<p class="text-muted text-sm">Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. </p>
<p class="text-sm"><a href="#">See Jack's 3 other listings <i class="fa fa-long-arrow-alt-right ml-2"></i></a></p>
</div>
</div>
</div>
<div class="text-block">
<h5 class="mb-4">Gallery</h5>
<div class="row gallery mb-3 ml-n1 mr-n1">
<div class="col-lg-4 col-6 px-1 mb-2"><a href="../../assets/img/photo/photo-1426122402199-be02db90eb90.jpg" data-fancybox="gallery" title="Our street"><img src="../../assets/img/photo/photo-1426122402199-be02db90eb90.jpg" alt="..." class="img-fluid"></a></div>
<div class="col-lg-4 col-6 px-1 mb-2"><a href="../../assets/img/photo/photo-1512917774080-9991f1c4c750.jpg" data-fancybox="gallery" title="Outside"><img src="../../assets/img/photo/photo-1512917774080-9991f1c4c750.jpg" alt="..." class="img-fluid"></a></div>
<div class="col-lg-4 col-6 px-1 mb-2"><a href="../../assets/img/photo/photo-1494526585095-c41746248156.jpg" data-fancybox="gallery" title="Rear entrance"><img src="../../assets/img/photo/photo-1494526585095-c41746248156.jpg" alt="..." class="img-fluid"></a></div>
<div class="col-lg-4 col-6 px-1 mb-2"><a href="../../assets/img/photo/photo-1484154218962-a197022b5858.jpg" data-fancybox="gallery" title="Kitchen"><img src="../../assets/img/photo/photo-1484154218962-a197022b5858.jpg" alt="..." class="img-fluid"></a></div>
<div class="col-lg-4 col-6 px-1 mb-2"><a href="../../assets/img/photo/photo-1522771739844-6a9f6d5f14af.jpg" data-fancybox="gallery" title="Bedroom"><img src="../../assets/img/photo/photo-1522771739844-6a9f6d5f14af.jpg" alt="..." class="img-fluid"></a></div>
<div class="col-lg-4 col-6 px-1 mb-2"><a href="../../assets/img/photo/photo-1488805990569-3c9e1d76d51c.jpg" data-fancybox="gallery" title="Bedroom"><img src="../../assets/img/photo/photo-1488805990569-3c9e1d76d51c.jpg" alt="..." class="img-fluid"></a></div>
</div>
</div>
<div class="text-block">
<p class="subtitle text-sm text-primary">Reviews </p>
<h5 class="mb-4">Listing Reviews </h5>
<div class="media d-block d-sm-flex review">
<div class="text-md-center mr-4 mr-xl-5"><img src="../../assets/img/avatar/avatar-8.jpg" alt="Padmé Amidala" class="d-block avatar avatar-xl p-2 mb-2"><span class="text-uppercase text-muted text-sm">Dec 2018</span></div>
<div class="media-body">
<h6 class="mt-2 mb-1">Padmé Amidala</h6>
<div class="mb-2"><i class="fa fa-xs fa-star text-primary"></i><i class="fa fa-xs fa-star text-primary"></i><i class="fa fa-xs fa-star text-primary"></i><i class="fa fa-xs fa-star text-primary"></i><i class="fa fa-xs fa-star text-primary"></i>
</div>
<p class="text-muted text-sm">One morning, when Gregor Samsa woke from troubled dreams, he found himself transformed in his bed into a horrible vermin. He lay on his armour-like back, and if he lifted his head a little he could see his brown belly, slightly domed and divided by arches into stiff sections </p>
</div>
</div>
<div class="media d-block d-sm-flex review">
<div class="text-md-center mr-4 mr-xl-5"><img src="../../assets/img/avatar/avatar-2.jpg" alt="Luke Skywalker" class="d-block avatar avatar-xl p-2 mb-2"><span class="text-uppercase text-muted text-sm">Dec 2018</span></div>
<div class="media-body">
<h6 class="mt-2 mb-1">Luke Skywalker</h6>
<div class="mb-2"><i class="fa fa-xs fa-star text-primary"></i><i class="fa fa-xs fa-star text-primary"></i><i class="fa fa-xs fa-star text-primary"></i><i class="fa fa-xs fa-star text-primary"></i><i class="fa fa-xs fa-star text-gray-200"></i>
</div>
<p class="text-muted text-sm">The bedding was hardly able to cover it and seemed ready to slide off any moment. His many legs, pitifully thin compared with the size of the rest of him, waved about helplessly as he looked. "What's happened to me?" he thought. It wasn't a dream. </p>
</div>
</div>
<div class="media d-block d-sm-flex review">
<div class="text-md-center mr-4 mr-xl-5"><img src="../../assets/img/avatar/avatar-3.jpg" alt="Princess Leia" class="d-block avatar avatar-xl p-2 mb-2"><span class="text-uppercase text-muted text-sm">Dec 2018</span></div>
<div class="media-body">
<h6 class="mt-2 mb-1">Princess Leia</h6>
<div class="mb-2"><i class="fa fa-xs fa-star text-primary"></i><i class="fa fa-xs fa-star text-primary"></i><i class="fa fa-xs fa-star text-primary"></i><i class="fa fa-xs fa-star text-gray-200"></i><i class="fa fa-xs fa-star text-gray-200"></i>
</div>
<p class="text-muted text-sm">His room, a proper human room although a little too small, lay peacefully between its four familiar walls. A collection of textile samples lay spread out on the table. </p>
</div>
</div>
<div class="media d-block d-sm-flex review">
<div class="text-md-center mr-4 mr-xl-5"><img src="../../assets/img/avatar/avatar-4.jpg" alt="Jabba Hut" class="d-block avatar avatar-xl p-2 mb-2"><span class="text-uppercase text-muted text-sm">Dec 2018</span></div>
<div class="media-body">
<h6 class="mt-2 mb-1">Jabba Hut</h6>
<div class="mb-2"><i class="fa fa-xs fa-star text-primary"></i><i class="fa fa-xs fa-star text-primary"></i><i class="fa fa-xs fa-star text-primary"></i><i class="fa fa-xs fa-star text-primary"></i><i class="fa fa-xs fa-star text-primary"></i>
</div>
<p class="text-muted text-sm">Samsa was a travelling salesman - and above it there hung a picture that he had recently cut out of an illustrated magazine and housed in a nice, gilded frame. </p>
</div>
</div>
<div class="py-5">
<button type="button" data-toggle="collapse" data-target="#leaveReview" aria-expanded="false" aria-controls="leaveReview" class="btn btn-outline-primary">Leave a review</button>
<div id="leaveReview" class="collapse mt-4">
<h5 class="mb-4">Leave a review</h5>
<form id="contact-form" method="get" action="#" class="form">
<div class="row">
<div class="col-sm-6">
<div class="form-group">
<label for="name" class="form-label">Your name *</label>
<input type="text" name="name" id="name" placeholder="Enter your name" required="required" class="form-control">
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<label for="rating" class="form-label">Your rating *</label>
<select name="rating" id="rating" class="custom-select focus-shadow-0">
<option value="5">★★★★★ (5/5)</option>
<option value="4">★★★★☆ (4/5)</option>
<option value="3">★★★☆☆ (3/5)</option>
<option value="2">★★☆☆☆ (2/5)</option>
<option value="1">★☆☆☆☆ (1/5)</option>
</select>
</div>
</div>
</div>
<div class="form-group">
<label for="email" class="form-label">Your email *</label>
<input type="email" name="email" id="email" placeholder="Enter your email" required="required" class="form-control">
</div>
<div class="form-group">
<label for="review" class="form-label">Review text *</label>
<textarea rows="4" name="review" id="review" placeholder="Enter your review" required="required" class="form-control"></textarea>
</div>
<button type="submit" class="btn btn-primary">Post review</button>
</form>
</div>
</div>
</div>
</div>
<div class="col-lg-4">
<div style="top: 100px;" class="p-4 shadow ml-lg-4 rounded sticky-top">
<p class="text-muted"><span class="text-primary h2">Php. {{product.price}}</span> per night</p>
<hr class="my-4">
<form id="booking-form" method="get" action="#" autocomplete="off" class="form">
<div class="form-group">
<label for="bookingDate" class="form-label">Your stay *</label>
<div class="datepicker-container datepicker-container-right">
<input type="text" name="bookingDate" id="bookingDate" placeholder="Choose your dates" required="required" class="form-control">
</div>
</div>
<div class="form-group mb-4">
<label for="guests" class="form-label">Guests *</label>
<select name="guests" id="guests" class="form-control">
<option value="1">1 Guest</option>
<option value="2">2 Guests</option>
<option value="3">3 Guests</option>
<option value="4">4 Guests</option>
<option value="5">5 Guests</option>
</select>
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary btn-block">Book your stay</button>
</div>
</form>
<p class="text-muted text-sm text-center">Some additional text can be also placed here.</p>
<hr class="my-4">
<div class="text-center">
<p> <a href="#" class="text-secondary text-sm"> <i class="fa fa-heart"></i> Bookmark This Listing</a></p>
<p class="text-muted text-sm">79 people bookmarked this place </p>
</div>
</div>
</div>
</div>
</div>
<div class="py-6 bg-gray-100">
<DisplayProduct/>
</div>
</div>
</template>
<script type="text/javascript">
import DisplayProduct from '@/components/products/DisplayProduct.vue'
export default {
name: 'product-detail',
components: {
DisplayProduct,
},
data() {
return {
product: '',
swiperOption: {
slidesPerView: 2,
spaceBetween: 0,
centeredSlides: true,
loop: true,
// If we need pagination
pagination: {
el: '.swiper-pagination',
clickable: true,
dynamicBullets: true
},
// Navigation arrows
navigation: {
nextEl: '.swiper-button-next',
prevEl: '.swiper-button-prev',
},
}
}
},
methods: {
getProduct: function(product_id){
const baseURI = 'http://localhost:5000/products/' + product_id;
this.$http.get(baseURI)
.then(result => {
this.product = result.data['products'];
console.log(this.product);
})
.catch(error =>{
alert(error);
})
}
},
created: function(){
this.getProduct(this.$route.params.product_id)
}
}
</script>
We use axios
to send a http
GET
request to get a product with given its id.
methods: {
getProduct: function(product_id){
const baseURI = 'http://localhost:5000/products/' + product_id;
this.$http.get(baseURI)
.then(result => {
this.product = result.data['products'];
console.log(this.product);
})
.catch(error =>{
alert(error);
})
}
},
created: function(){
this.getProduct(this.$route.params.product_id)
}
TODO
Install the dependencies for AWS Cognito:
npm install --save aws-sdk
npm install --save amazon-cognito-identity-js
Install the dependencies for state management
npm install --save vuex
npm install --save vuex-persistedstate
In myproject-vuejs-web/src/main.js
add the following lines of code:
import Vue from 'vue';
import App from './App.vue';
... START ...
import Vuex from 'vuex';
import createPersistedState from 'vuex-persistedstate';
Vue.use(Vuex);
... END ...
import VueAwesomeSwiper from 'vue-awesome-swiper';
import 'swiper/dist/css/swiper.css';
Vue.use(VueAwesomeSwiper);
In src/views/SignUp.vue
add the following code below the <style>
tag:
</style>
... START ...
<script>
import * as AmazonCognitoIdentity from 'amazon-cognito-identity-js';
export default {
name: 'signup',
mounted() {
var cognitoUserPoolId = process.env.VUE_APP_USER_POOL_ID; // example: 'us-east-1_abcd12345'
var cognitoUserPoolClientId = process.env.VUE_APP_USER_POOL_CLIENT_ID; // example: 'abcd12345abcd12345abcd12345'
var navigate = this.$router;
$(document).on('click', '#signup', function(event) {
event.preventDefault();
var poolData = {
UserPoolId : cognitoUserPoolId,
ClientId : cognitoUserPoolClientId
};
var userPool = new AmazonCognitoIdentity.CognitoUserPool(poolData);
var attributeList = [];
var email = document.getElementById('loginUsername').value;
var pw = document.getElementById('loginPassword').value;
var confirmPw = document.getElementById('loginPassword2').value;
var dataEmail = {
Name : 'email',
Value : email
};
var attributeEmail = new AmazonCognitoIdentity.CognitoUserAttribute(dataEmail);
attributeList.push(attributeEmail);
if (pw === confirmPw) {
userPool.signUp(email, pw, attributeList, null, function(err, result){
if (err) {
alert(err.message);
return;
}
localStorage.setItem('email', email);
alert("Successfully signed up!!")
navigate.push('/confirm');
});
} else {
alert('Passwords do not match.')
}
});
}
}
</script>
... END ...
In root folder of the project: myproject-consumer-web
create the following file:
.env
VUE_APP_USER_POOL_ID=<REPLACE_ME_COGNITO_USER_POOL_ID>
VUE_APP_USER_POOL_CLIENT_ID=<REPLACE_ME_USER_POOL_CLIENT_ID>
In src/views/Login.vue
add the following code below the <style>
tag:
</style>
... START ...
<script>
import * as AmazonCognitoIdentity from 'amazon-cognito-identity-js';
import * as AWS from 'aws-sdk';
export default {
name: 'login',
mounted(){
var cognitoUserPoolId = process.env.VUE_APP_USER_POOL_ID; // example: 'us-east-1_abcd12345'
var cognitoUserPoolClientId = process.env.VUE_APP_USER_POOL_CLIENT_ID; // example:
var awsRegion = 'ap-southeast-1';
initializeStorage();
var configString = localStorage.getItem("awsConfig");
var config = JSON.parse(configString);
var navigate = this.$router;
var store = this.$store
if(config != null) {
refreshAWSCredentials();
loggedInDisplay();
}
function loggedInDisplay() {
//changes the value of store to loggedIn
store.commit('login')
navigate.push('/')
}
function initializeStorage() {
var identityPoolId = cognitoUserPoolId;
var userPoolId = cognitoUserPoolId;
var clientId = cognitoUserPoolClientId;
var loginPrefix = 'cognito-idp.' + awsRegion + '.amazonaws.com/' + identityPoolId;
localStorage.setItem('identityPoolId', identityPoolId);
localStorage.setItem('userPoolId', userPoolId);
localStorage.setItem('clientId', clientId);
localStorage.setItem('loginPrefix', loginPrefix);
}
function loginUser() {
var userPoolId = localStorage.getItem('userPoolId');
var clientId = localStorage.getItem('clientId');
var identityPoolId = localStorage.getItem('identityPoolId');
var loginPrefix = localStorage.getItem('loginPrefix');
var poolData = {
UserPoolId : userPoolId, // Your user pool id here
ClientId : clientId // Your client id here
};
var userPool = new AmazonCognitoIdentity.CognitoUserPool(poolData);
var email = document.getElementById('loginUsername').value;
var pwd = document.getElementById('loginPassword').value;
var authenticationData =
{
'UserName': email,
'Password': pwd
}
var userData = {
Username : email,
Pool : userPool
};
var authenticationDetails = new AmazonCognitoIdentity.AuthenticationDetails(authenticationData);
var cognitoUser = new AmazonCognitoIdentity.CognitoUser(userData);
cognitoUser.authenticateUser(authenticationDetails, {
onSuccess: function (result) {
console.log('access token + \n' + result.getAccessToken().getJwtToken());
var sessionTokens =
{
IdToken: result.getIdToken(),
AccessToken: result.getAccessToken(),
RefreshToken: result.getRefreshToken()
};
localStorage.setItem('sessionTokens', JSON.stringify(sessionTokens));
//POTENTIAL: Region needs to be set if not already set previously elsewhere.
AWS.config.region = 'ap-southeast-1';
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId : identityPoolId, // your identity pool id here
Logins : {
// Change the key below according to the specific region your user pool is in.
loginPrefix : sessionTokens.IdToken.jwtToken
}
});
localStorage.setItem('awsConfig', JSON.stringify(AWS.config));
localStorage.setItem('email', email);
cognitoUser.getUserAttributes(function(err, result) {
if (err) {
console.log(err)
alert(err);
return;
}
for (var i = 0; i < result.length; i++) {
console.log('attribute ' + result[i].getName() + ' has value ' + result[i].getValue());
if (result[i].getName() == 'sub') {
console.log('Overwriting userId into local storage');
localStorage.setItem('userId', result[i].getValue());
}
}
});
loggedInDisplay();
},
onFailure: function(err) {
alert(err.message);
},
});
}
function refreshAWSCredentials() {
var userPoolId = localStorage.getItem('userPoolId');
var clientId = localStorage.getItem('clientId');
var identityPoolId = localStorage.getItem('identityPoolId');
var loginPrefix = localStorage.getItem('loginPrefix');
var poolData = {
UserPoolId : userPoolId, // Your user pool id here
ClientId : clientId // Your client id here
}
var userPool = new AmazonCognitoIdentity.CognitoUserPool(poolData);
var cognitoUser = userPool.getCurrentUser();
if (cognitoUser != null) {
cognitoUser.getSession(function(err, result) {
if (result) {
console.log('You are now logged in.');
cognitoUser.refreshSession(result.getRefreshToken(), function(err, result) {
if (err) {//throw err;
console.log('In the err: '+err);
}
else{
localStorage.setItem('awsConfig', JSON.stringify(AWS.config));
var sessionTokens =
{
IdToken: result.getIdToken(),
AccessToken: result.getAccessToken(),
RefreshToken: result.getRefreshToken()
};
localStorage.setItem("sessionTokens", JSON.stringify(sessionTokens));
}
});
}
});
}
}
$("#loginForm").submit(function(event) {
event.preventDefault();
loginUser();
});
}
}
</script>
... END ...
$ aws codecommit delete-repository --repository-name myproject-consumer-web
$ rm -rf ~/environment/myproject-consumer-web