Initial commit 🚀

This commit is contained in:
Jonas Schmedtmann
2019-06-13 15:43:15 +01:00
commit 7f81af0ddf
1052 changed files with 2123177 additions and 0 deletions

View File

@@ -0,0 +1,10 @@
footer.footer
.footer__logo
img(src='/img/logo-green.png' alt='Natour logo')
ul.footer__nav
li: a(href='#') About us
li: a(href='#') Download apps
li: a(href='#') Become a guide
li: a(href='#') Careers
li: a(href='#') Contact
p.footer__copyright © by Jonas Schmedtmann. Feel free to use this project for your own purposes, EXCEPT producing your own course or tutorials!

View File

@@ -0,0 +1,14 @@
header.header
nav.nav.nav--tours
a.nav__el(href='/') All tours
.header__logo
img(src='/img/logo-white.png' alt='Natours logo')
nav.nav.nav--user
if user
a.nav__el.nav__el--logout Log out
a.nav__el(href='/me')
img.nav__user-img(src=`/img/users/${user.photo}` alt=`Photo of ${user.name}`)
span= user.name.split(' ')[0]
else
a.nav__el(href='/login') Log in
a.nav__el.nav__el--cta(href='#') Sign up

View File

@@ -0,0 +1,10 @@
mixin reviewCard(review)
.reviews__card
.reviews__avatar
img.reviews__avatar-img(src=`/img/users/${review.user.photo}`, alt=`${review.user.name}`)
h6.reviews__user= review.user.name
p.reviews__text= review.review
.reviews__rating
each star in [1, 2, 3, 4, 5]
svg.reviews__star(class=`reviews__star--${review.rating >= star ? 'active' : 'inactive'}`)
use(xlink:href='/img/icons.svg#icon-star')

View File

@@ -0,0 +1,67 @@
extends base
mixin navItem(link, text, icon, active)
li(class=`${active ? 'side-nav--active' : ''}`)
a(href=`${link}`)
svg
use(xlink:href=`img/icons.svg#icon-${icon}`)
| #{text}
block content
main.main
.user-view
nav.user-view__menu
ul.side-nav
+navItem('#', 'Settings', 'settings', true)
+navItem('/my-tours', 'My bookings', 'briefcase')
+navItem('#', 'My reviews', 'star')
+navItem('#', 'Billing', 'credit-card')
- if (user.role === 'admin')
.admin-nav
h5.admin-nav__heading Admin
ul.side-nav
+navItem('#', 'Manage tours', 'map')
+navItem('#', 'Manage users', 'users')
+navItem('#', 'Manage reviews', 'star')
+navItem('#', 'Manage bookings', 'briefcase')
.user-view__content
.user-view__form-container
h2.heading-secondary.ma-bt-md Your account settings
//- WITHOUT API
//- form.form.form-user-data(action='/submit-user-data' method='POST' enctype='multipart/form-data')
//- WITH API
form.form.form-user-data
.form__group
label.form__label(for='name') Name
input#name.form__input(type='text', value=`${user.name}`, required, name='name')
.form__group.ma-bt-md
label.form__label(for='email') Email address
input#email.form__input(type='email', value=`${user.email}`, required, name='email')
.form__group.form__photo-upload
img.form__user-photo(src=`/img/users/${user.photo}`, alt='User photo')
input.form__upload(type='file', accept='image/*', id='photo', name='photo')
label(for='photo') Choose new photo
.form__group.right
button.btn.btn--small.btn--green Save settings
.line  
.user-view__form-container
h2.heading-secondary.ma-bt-md Password change
form.form.form-user-password
.form__group
label.form__label(for='password-current') Current password
input#password-current.form__input(type='password', placeholder='••••••••', required, minlength='8')
.form__group
label.form__label(for='password') New password
input#password.form__input(type='password', placeholder='••••••••', required, minlength='8')
.form__group.ma-bt-lg
label.form__label(for='password-confirm') Confirm password
input#password-confirm.form__input(type='password', placeholder='••••••••', required, minlength='8')
.form__group.right
button.btn.btn--small.btn--green.btn--save-password Save password

View File

@@ -0,0 +1,27 @@
doctype html
html
head
block head
meta(charset='UTF-8')
meta(name='viewport' content='width=device-width, initial-scale=1.0')
link(rel='stylesheet' href='/css/style.css')
link(rel='shortcut icon' type='image/png' href='/img/favicon.png')
link(rel='stylesheet' href='https://fonts.googleapis.com/css?family=Lato:300,300i,700')
title Natours | #{title}
body(data-alert=`${alert ? alert : ''}`)
// HEADER
include _header
// CONTENT
block content
h1 This is a placeholder heading
// FOOTER
include _footer
//- script(src='https://cdnjs.cloudflare.com/ajax/libs/axios/0.18.0/axios.min.js')
//- script(src='/js/mapbox.js')
//- script(src='/js/login.js')
script(src='https://js.stripe.com/v3/')
script(src='/js/bundle.js')

View File

@@ -0,0 +1,258 @@
style.
img {
border: none;
-ms-interpolation-mode: bicubic;
max-width: 100%;
}
body {
background-color: #f6f6f6;
font-family: sans-serif;
-webkit-font-smoothing: antialiased;
font-size: 14px;
line-height: 1.4;
margin: 0;
padding: 0;
-ms-text-size-adjust: 100%;
-webkit-text-size-adjust: 100%;
}
table {
border-collapse: separate;
mso-table-lspace: 0pt;
mso-table-rspace: 0pt;
width: 100%; }
table td {
font-family: sans-serif;
font-size: 14px;
vertical-align: top;
}
.body {
background-color: #f6f6f6;
width: 100%;
}
.container {
display: block;
margin: 0 auto !important;
/* makes it centered */
max-width: 580px;
padding: 10px;
width: 580px;
}
.content {
box-sizing: border-box;
display: block;
margin: 0 auto;
max-width: 580px;
padding: 10px;
}
.main {
background: #ffffff;
border-radius: 3px;
width: 100%;
}
.wrapper {
box-sizing: border-box;
padding: 20px;
}
.content-block {
padding-bottom: 10px;
padding-top: 10px;
}
.footer {
clear: both;
margin-top: 10px;
text-align: center;
width: 100%;
}
.footer td,
.footer p,
.footer span,
.footer a {
color: #999999;
font-size: 12px;
text-align: center;
}
h1,
h2,
h3,
h4 {
color: #000000;
font-family: sans-serif;
font-weight: 400;
line-height: 1.4;
margin: 0;
margin-bottom: 30px;
}
h1 {
font-size: 35px;
font-weight: 300;
text-align: center;
text-transform: capitalize;
}
p,
ul,
ol {
font-family: sans-serif;
font-size: 14px;
font-weight: normal;
margin: 0;
margin-bottom: 15px;
}
p li,
ul li,
ol li {
list-style-position: inside;
margin-left: 5px;
}
a {
color: #55c57a;
text-decoration: underline;
}
.btn {
box-sizing: border-box;
width: 100%; }
.btn > tbody > tr > td {
padding-bottom: 15px; }
.btn table {
width: auto;
}
.btn table td {
background-color: #ffffff;
border-radius: 5px;
text-align: center;
}
.btn a {
background-color: #ffffff;
border: solid 1px #55c57a;
border-radius: 5px;
box-sizing: border-box;
color: #55c57a;
cursor: pointer;
display: inline-block;
font-size: 14px;
font-weight: bold;
margin: 0;
padding: 12px 25px;
text-decoration: none;
text-transform: capitalize;
}
.btn-primary table td {
background-color: #55c57a;
}
.btn-primary a {
background-color: #55c57a;
border-color: #55c57a;
color: #ffffff;
}
.last {
margin-bottom: 0;
}
.first {
margin-top: 0;
}
.align-center {
text-align: center;
}
.align-right {
text-align: right;
}
.align-left {
text-align: left;
}
.clear {
clear: both;
}
.mt0 {
margin-top: 0;
}
.mb0 {
margin-bottom: 0;
}
.preheader {
color: transparent;
display: none;
height: 0;
max-height: 0;
max-width: 0;
opacity: 0;
overflow: hidden;
mso-hide: all;
visibility: hidden;
width: 0;
}
.powered-by a {
text-decoration: none;
}
hr {
border: 0;
border-bottom: 1px solid #f6f6f6;
margin: 20px 0;
}
@media only screen and (max-width: 620px) {
table[class=body] h1 {
font-size: 28px !important;
margin-bottom: 10px !important;
}
table[class=body] p,
table[class=body] ul,
table[class=body] ol,
table[class=body] td,
table[class=body] span,
table[class=body] a {
font-size: 16px !important;
}
table[class=body] .wrapper,
table[class=body] .article {
padding: 10px !important;
}
table[class=body] .content {
padding: 0 !important;
}
table[class=body] .container {
padding: 0 !important;
width: 100% !important;
}
table[class=body] .main {
border-left-width: 0 !important;
border-radius: 0 !important;
border-right-width: 0 !important;
}
table[class=body] .btn table {
width: 100% !important;
}
table[class=body] .btn a {
width: 100% !important;
}
table[class=body] .img-responsive {
height: auto !important;
max-width: 100% !important;
width: auto !important;
}
}
@media all {
.ExternalClass {
width: 100%;
}
.ExternalClass,
.ExternalClass p,
.ExternalClass span,
.ExternalClass font,
.ExternalClass td,
.ExternalClass div {
line-height: 100%;
}
.apple-link a {
color: inherit !important;
font-family: inherit !important;
font-size: inherit !important;
font-weight: inherit !important;
line-height: inherit !important;
text-decoration: none !important;
}
.btn-primary table td:hover {
background-color: #2e864b !important;
}
.btn-primary a:hover {
background-color: #2e864b !important;
border-color: #2e864b !important;
}
}

View File

@@ -0,0 +1,43 @@
//- Email template adapted from https://github.com/leemunroe/responsive-html-email-template
//- Converted from HTML using https://html2pug.now.sh/
doctype html
html
head
meta(name='viewport', content='width=device-width')
meta(http-equiv='Content-Type', content='text/html; charset=UTF-8')
title= subject
include _style
body
table.body(role='presentation', border='0', cellpadding='0', cellspacing='0')
tbody
tr
td
td.container
.content
// START CENTERED WHITE CONTAINER
table.main(role='presentation')
// START MAIN AREA
tbody
tr
td.wrapper
table(role='presentation', border='0', cellpadding='0', cellspacing='0')
tbody
tr
td
// CONTENT
block content
// START FOOTER
.footer
table(role='presentation', border='0', cellpadding='0', cellspacing='0')
tbody
tr
td.content-block
span.apple-link Natours Inc, 123 Nowhere Road, San Francisco CA 99999
br
| Don't like these emails?
a(href='#') Unsubscribe
//- td  

View File

@@ -0,0 +1,16 @@
extends baseEmail
block content
p Hi #{firstName},
p Forgot your password? Submit a PATCH request with your new password and passwordConfirm to: #{url}.
p (Website for this action not yet implememnted.)
table.btn.btn-primary(role='presentation', border='0', cellpadding='0', cellspacing='0')
tbody
tr
td(align='left')
table(role='presentation', border='0', cellpadding='0', cellspacing='0')
tbody
tr
td
a(href=`${url}`, target='_blank') Reset your password
p If you didn't forget your password, please ignore this email!

View File

@@ -0,0 +1,17 @@
extends baseEmail
block content
p Hi #{firstName},
p Welcome to Natours, we're glad to have you 🎉🙏
p We're all a big familiy here, so make sure to upload your user photo so we get to know you a bit better!
table.btn.btn-primary(role='presentation', border='0', cellpadding='0', cellspacing='0')
tbody
tr
td(align='left')
table(role='presentation', border='0', cellpadding='0', cellspacing='0')
tbody
tr
td
a(href=`${url}`, target='_blank') Upload user photo
p If you need any help with booking your next tour, please don't hesitate to contact me!
p - Jonas Schmedtmann, CEO

View File

@@ -0,0 +1,9 @@
extends base
block content
main.main
.error
.error__title
h2.heading-secondary.heading-secondary--error Uh oh! Something went wrong!
h2.error__emoji 😢 🤯
.error__msg= msg

View File

@@ -0,0 +1,15 @@
extends base
block content
main.main
.login-form
h2.heading-secondary.ma-bt-lg Log into your account
form.form.form--login
.form__group
label.form__label(for='email') Email address
input#email.form__input(type='email', placeholder='you@example.com', required)
.form__group.ma-bt-md
label.form__label(for='password') Password
input#password.form__input(type='password', placeholder='••••••••', required, minlength='8')
.form__group
button.btn.btn--green Login

View File

@@ -0,0 +1,45 @@
extends base
block content
main.main
.card-container
each tour in tours
.card
.card__header
.card__picture
.card__picture-overlay  
img.card__picture-img(src=`/img/tours/${tour.imageCover}`, alt=`${tour.name}`)
h3.heading-tertirary
span= tour.name
.card__details
h4.card__sub-heading= `${tour.difficulty} ${tour.duration}-day tour`
p.card__text= tour.summary
.card__data
svg.card__icon
use(xlink:href='/img/icons.svg#icon-map-pin')
span= tour.startLocation.description
.card__data
svg.card__icon
use(xlink:href='/img/icons.svg#icon-calendar')
span= tour.startDates[0].toLocaleString('en-us', {month: 'long', year: 'numeric'})
.card__data
svg.card__icon
use(xlink:href='/img/icons.svg#icon-flag')
span= `${tour.locations.length} stops`
.card__data
svg.card__icon
use(xlink:href='/img/icons.svg#icon-user')
span= `${tour.maxGroupSize} people`
.card__footer
p
span.card__footer-value= `$${tour.price}`
|
span.card__footer-text per person
p.card__ratings
span.card__footer-value= tour.ratingsAverage
|
span.card__footer-text= `rating (${tour.ratingsQuantity})`
a.btn.btn--green.btn--small(href=`/tour/${tour.slug}`) Details

View File

@@ -0,0 +1,91 @@
extends base
include _reviewCard
block append head
script(src='https://api.mapbox.com/mapbox-gl-js/v0.54.0/mapbox-gl.js')
link(href='https://api.mapbox.com/mapbox-gl-js/v0.54.0/mapbox-gl.css' rel='stylesheet')
mixin overviewBox(label, text, icon)
.overview-box__detail
svg.overview-box__icon
use(xlink:href=`/img/icons.svg#icon-${icon}`)
span.overview-box__label= label
span.overview-box__text= text
block content
section.section-header
.header__hero
.header__hero-overlay  
img.header__hero-img(src=`/img/tours/${tour.imageCover}`, alt=`${tour.name}`)
.heading-box
h1.heading-primary
span= `${tour.name} tour`
.heading-box__group
.heading-box__detail
svg.heading-box__icon
use(xlink:href='/img/icons.svg#icon-clock')
span.heading-box__text= `${tour.duration} days`
.heading-box__detail
svg.heading-box__icon
use(xlink:href='/img/icons.svg#icon-map-pin')
span.heading-box__text= tour.startLocation.description
section.section-description
.overview-box
div
.overview-box__group
h2.heading-secondary.ma-bt-lg Quick facts
- const date = tour.startDates[0].toLocaleString('en-us', {month: 'long', year: 'numeric'})
+overviewBox('Next date', date, 'calendar')
+overviewBox('Difficulty', tour.difficulty, 'trending-up')
+overviewBox('Participants', `${tour.maxGroupSize} people`, 'user')
+overviewBox('Rating', `${tour.ratingsAverage} / 5`, 'star')
.overview-box__group
h2.heading-secondary.ma-bt-lg Your tour guides
each guide in tour.guides
.overview-box__detail
img.overview-box__img(src=`/img/users/${guide.photo}`, alt=`${guide.name}`)
- if (guide.role === 'lead-guide')
span.overview-box__label Lead guide
- if (guide.role === 'guide')
span.overview-box__label Tour guide
span.overview-box__text= guide.name
.description-box
h2.heading-secondary.ma-bt-lg= `About ${tour.name} tour`
- const parapraphs = tour.description.split('\n');
each p in parapraphs
p.description__text= p
section.section-pictures
each img, i in tour.images
.picture-box
img.picture-box__img(src=`/img/tours/${img}`, alt=`The Park Camper Tour ${i + 1}`, class=`picture-box__img--${i + 1}`)
section.section-map
#map(data-locations=`${JSON.stringify(tour.locations)}`)
section.section-reviews
.reviews
each review in tour.reviews
+reviewCard(review)
section.section-cta
.cta
.cta__img.cta__img--logo
img(src='/img/logo-white.png', alt='Natours logo')
img.cta__img.cta__img--1(src=`/img/tours/${tour.images[1]}`, alt='Tour picture')
img.cta__img.cta__img--2(src=`/img/tours/${tour.images[2]}`, alt='Tour picture')
.cta__content
h2.heading-secondary What are you waiting for?
p.cta__text= `${tour.duration} days. 1 adventure. Infinite memories. Make it yours today!`
if user
button.btn.btn--green.span-all-rows#book-tour(data-tour-id=`${tour.id}`) Book tour now!
else
a.btn.btn--green.span-all-rows(href='/login') Log in to book tour