Implement watchlist

This commit is contained in:
Björn Fromme
2018-08-24 09:46:16 +02:00
parent d34e95dfff
commit c11942bc7a
30 changed files with 583 additions and 58 deletions
@@ -25,6 +25,7 @@ require('../images/favicon_uch.ico');
// Import dependencies
import Vue from 'vue'
import VueSession from 'vue-session'
import $ from 'jquery'
import dayjs from 'dayjs'
import 'dayjs/locale/de'
@@ -49,6 +50,8 @@ import Gmap from './components/Gmap.vue'
import Calendar from './components/Calendar.vue'
import EventPricetable from './components/EventPriceTable.vue'
import ContactForm from './components/ContactForm.vue'
import WatchlistToggle from './components/WatchlistToggle.vue'
import Watchlist from './components/Watchlist.vue'
// Define global constants
const $window = $(window);
@@ -56,6 +59,9 @@ const sliderPrevArrow = '<button class="slider-pagination__arrow slider-paginati
const sliderNextArrow = '<button class="slider-pagination__arrow slider-pagination__arrow--next"><i class="fa fa-2x fa-chevron-right"></i></button>';
const sliderDot = '<button class="slider-pagination__dot" data-role="none" role="button" tabindex="0" />';
// Enable session handling
Vue.use(VueSession);
// Define date filter for vue templates
Vue.filter('date', (value) => {
return dayjs(value).format('DD.MM.YYYY');
@@ -73,7 +79,9 @@ new Vue({
Gmap,
Calendar,
EventPricetable,
ContactForm
ContactForm,
WatchlistToggle,
Watchlist
},
data () {
return {
@@ -85,6 +93,12 @@ new Vue({
searchbarFixed: false,
mobileMode: false,
backgroundImageMode: 'mobile',
watchlist: []
}
},
computed: {
watchlistCount () {
return this.watchlist.length;
}
},
methods: {
@@ -366,6 +380,25 @@ new Vue({
EventBus.$on('scrollTo', (anchorId, offset) => {
this.scrollTo(anchorId, offset);
});
EventBus.$on('watchlistToggle', uid => {
let list = this.watchlist;
let watchlistIndex = list.indexOf(uid);
if (watchlistIndex === -1) {
list.push(uid)
} else {
list.splice(watchlistIndex, 1)
}
this.watchlist = list;
this.$session.set('watchlist', this.watchlist);
});
if (!this.$session.exists()) {
this.$session.start();
}
if (!this.$session.has('watchlist')) {
this.$session.set('watchlist', []);
}
this.watchlist = this.$session.get('watchlist');
},
mounted () {
Vue.nextTick(() => {
@@ -73,6 +73,12 @@
default () {
return {};
}
},
watchlist: {
type: Array,
default () {
return [];
}
}
},
computed: {
@@ -133,7 +139,6 @@
this.loading = false;
EventBus.$emit('ajaxContentLoaded');
});
},
resetFilterSettings () {
this.filterSettings = this.defaultFilterSettings;
@@ -59,14 +59,19 @@
</a>
</div>
</div>
<watchlist-toggle :uid="item.key" :watchlist="watchlist" class="teaserbox-watchlist"></watchlist-toggle>
</div>
</template>
<script>
import dayjs from 'dayjs'
import 'dayjs/locale/de'
import WatchlistToggle from './WatchlistToggle.vue'
export default {
components: {
WatchlistToggle
},
props: {
item: {
type: Object,
@@ -75,6 +80,12 @@
section: {
type: Object,
required: true
},
watchlist: {
type: Array,
default () {
return [];
}
}
},
computed: {
@@ -0,0 +1,88 @@
<template>
<div class="watchlist">
<template v-for="section in teasers">
<h2 class="headline headline--secondary headline--underlined">{{ section.concept.name }}</h2>
<div class="searchresult-section" v-bind:class="'searchresult-section--' + section.concept.code">
<search-result-item
v-for="item in section.items"
v-bind:key="item.key"
v-bind:item="item"
v-bind:section="section"
v-bind:watchlist="watchlist"></search-result-item>
</div>
</template>
<div class="alert alert-info" v-if="total === 0">
Deine Merkliste ist aktuell leer...
</div>
<div v-show="loading" class="loader">
<img :src="loaderUri" alt="Loading...">
</div>
</div>
</template>
<script>
import Vue from 'vue'
import EventBus from '../bus'
import SearchResultItem from './SearchResultItem.vue'
export default {
components: {
SearchResultItem
},
props: {
watchlistUri: {
type: String,
required: true
},
loaderUri: {
type: String,
required: true
},
referringPageUid: {
type: Number,
required: true
},
watchlist: {
type: Array,
default () {
return [];
}
}
},
data () {
return {
teasers: {},
total: 0
}
},
methods: {
loadList () {
if (this.watchlist.length === 0) {
this.teasers = {};
this.total = 0;
return;
}
let query = {};
query['tx_epproducts_ajax[watchlistUids]'] = this.watchlist;
query['tx_epproducts_ajax[referringPageUid]'] = this.referringPageUid;
this.loading = true;
$.post(this.watchlistUri, query, (json) => {
this.teasers = json.data;
this.total = json.total;
this.loading = false;
EventBus.$emit('ajaxContentLoaded');
});
}
},
created () {
EventBus.$on('watchlistToggle', () => {
this.loadList();
});
},
mounted () {
Vue.nextTick(() => {
this.loadList();
})
}
}
</script>
@@ -0,0 +1,37 @@
<template>
<span v-on:click.prevent="toggleWatchlist()">
<template v-if="onWatchlist">
<i class="fa fa-minus tooltiptoggle" data-placement="auto" title="Merkliste"></i>
</template>
<template v-else>
<i class="fa fa-plus tooltiptoggle" data-placement="auto" title="Merkliste"></i>
</template>
</span>
</template>
<script>
import EventBus from '../bus'
export default {
props: {
uid: {
type: String,
required: true
},
watchlist: {
type: Array,
required: true
}
},
methods: {
toggleWatchlist () {
EventBus.$emit('watchlistToggle', this.uid);
}
},
computed: {
onWatchlist () {
return this.watchlist.indexOf(this.uid) !== -1;
}
}
}
</script>
@@ -36,8 +36,8 @@ $output-bourbon-deprecation-warnings: false;
@import "remix/forms";
@import "remix/contactnav";
@import "remix/faq";
@import "remix/destination-pulldown";
@import "remix/watchlist";
// core config
@import "variables";
@@ -33,6 +33,7 @@ $output-bourbon-deprecation-warnings: false;
@import "remix/socialmedia";
@import "remix/contactnav";
@import "remix/faq";
@import "remix/watchlist";
// core config
@import "variables";
@@ -33,6 +33,7 @@ $output-bourbon-deprecation-warnings: false;
@import "remix/socialmedia";
@import "remix/contactnav";
@import "remix/faq";
@import "remix/watchlist";
// core config
@import "variables";
@@ -33,6 +33,7 @@ $output-bourbon-deprecation-warnings: false;
@import "remix/socialmedia";
@import "remix/contactnav";
@import "remix/faq";
@import "remix/watchlist";
// core config
@import "variables";
@@ -108,6 +108,9 @@
&.youtube {
top: 176px;
}
&.watchlist {
top: 220px;
}
&:hover{
right: 0;
}
@@ -100,3 +100,15 @@
color: $color-label-low-contingent;
}
}
.product-detail {}
.product-detail__watchlist {
display: inline-block;
color: white;
background-color: $color-brand-primary;
font-size: 22px;
line-height: 22px;
padding: 4px 12px;
cursor: pointer;
}
@@ -6,6 +6,7 @@
text-decoration: none;
color: $color-font-base;
margin-bottom: 20px;
position: relative;
&:hover {
color: $color-font-base;
@@ -57,6 +58,11 @@
@include mq($from: tablet) {
display: flex;
flex-wrap: nowrap;
&:not(:last-child) {
padding-bottom: 20px;
border-bottom: 2px solid $color-grey;
}
}
}
@@ -150,7 +156,7 @@
.teaserbox--landscape & {
height: auto;
border-style: none solid;
border-style: none;
@include mq($from: tablet) {
display: flex;
@@ -158,7 +164,7 @@
flex-direction: column;
justify-content: space-between;
padding: 8px 16px;
border-style: solid none;
border-style: none;
}
}
@@ -189,7 +195,8 @@
border-style: solid solid solid none;
}
.teaserbox--searchresult & {
.teaserbox--searchresult &,
.teaserbox--landscape & {
border: none;
@include mq($from: tablet) {
@@ -341,3 +348,30 @@
}
}
.teaserbox-watchlist {
position: absolute;
top: 0;
right: 0;
z-index: 10;
@include size(48px);
display: block;
color: white;
&:hover,
&:focus,
&:visited,
&:active {
color: white;
}
cursor: pointer;
padding: 4px 0 0 28px;
&:before {
content: "";
position: absolute;
z-index: -1;
top: 0;
left: 0;
display: block;
@include triangle(48px, $color-brand-secondary, up-right);
}
}
@@ -0,0 +1,21 @@
.watchlist {
position: relative;
.loader {
position: absolute;
z-index: 100;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(white, 0.75);
img {
position: absolute;
top: 0;
left: 50%;
transform: translateX(-50%);
}
}
}
@@ -1,5 +1,9 @@
{namespace v=FluidTYPO3\Vhs\ViewHelpers}
<div class="ep-contactnav">
<a href="{f:uri.page(pageUid: settings.watchlistPageUid)}" class="bg-red watchlist" v-show="watchlistCount > 0" v-cloak>
<span class="fa fa-list" aria-hidden="true"></span>
<div>Merkliste ({{ watchlistCount }})</div>
</a>
<a href="tel:{settings.phoneNumberLink}" class="bg-red phone" title="Telefonische Beratung">
<span class="fa fa-phone" aria-hidden="true"></span>
<div>{settings.phoneNumber}</div>
@@ -17,7 +17,6 @@
</div>
<div class="col-sm-4 pull-left product-header__title">
<div class="head">
<!-- <a href="#" class="add-note" title="Dem Merkzettel hinzufügen"></a> -->
{product.concept.name}
</div>
<div class="content">
@@ -27,12 +26,17 @@
<f:image class="flag" src="{ep:flagiconSource(countryCode: product.country.code)}" alt="{product.country.name}"/>
<f:if condition="{hotel}">
<f:then>
<a href="{ep:gmapsUrl(hotel: hotel)}" target="_blank" class="ep-property ep-marker"><i class="fa fa-map-marker" aria-hidden="true"></i></a>
<a href="{ep:gmapsUrl(hotel: hotel)}" target="_blank" class="ep-property ep-marker">
<i class="fa fa-map-marker" aria-hidden="true"></i>
</a>
</f:then>
<f:else>
<a href="{ep:gmapsUrl(region: product.region)}" target="_blank" class="ep-property ep-marker"><i class="fa fa-map-marker" aria-hidden="true"></i></a>
<a href="{ep:gmapsUrl(region: product.region)}" target="_blank" class="ep-property ep-marker">
<i class="fa fa-map-marker" aria-hidden="true"></i>
</a>
</f:else>
</f:if>
<watchlist-toggle :watchlist="watchlist" uid="{hotel.uid}:{product.uid}" class="product-detail__watchlist"></watchlist-toggle>
<f:if condition="{product.skipassIncluded}">
<a href="#" class="ep-property">inkl. Skipass</a>
</f:if>
@@ -72,3 +76,9 @@
</f:if>
{v:render.template(file: 'EXT:ep_theme/Resources/Private/Partials/Breadcrumb.html')}
</div>
<script>
import WatchlistToggle from "../../Assets/js/components/WatchlistToggle";
export default {
components: {WatchlistToggle}
}
</script>
@@ -94,6 +94,7 @@
<a href="{uriLink}" title="{teaser.product.name}" class="button button--full searchresult-item__button">Details &amp; Buchen</a>
</div>
</div>
<watchlist-toggle uid="{teaser.key}" v-bind:watchlist="watchlist" class="teaserbox-watchlist"></watchlist-toggle>
</div>
</f:section>
@@ -8,6 +8,13 @@
<f:section name="Content">
<f:switch expression="{data.list_type}">
<f:comment>Plugin watchlist</f:comment>
<f:case value="epproducts_watchlist">
<f:render section="Header" arguments="{_all}"/>
<ep:container data="{data}">
<f:cObject typoscriptObjectPath="tt_content.list.20.{data.list_type}" data="{data}"/>
</ep:container>
</f:case>
<f:comment>Plugin snowreport</f:comment>
<f:case value="epproducts_snowreport_list">
<f:render section="Header" arguments="{_all}"/>
@@ -9,11 +9,12 @@
<div id="search">
<search-result
results-uri="{ep:uri.ajax(action: 'searchresult', controller: 'AjaxSearch', format: 'json', noCacheHash: 1)}"
:initial-filter-settings='<v:format.json.encode>{filterSettings}</v:format.json.encode>'
:initial-filter-options='<v:format.json.encode>{filterOptions}</v:format.json.encode>'
:referring-page-uid="{referringPageUid}"
v-bind:initial-filter-settings='<v:format.json.encode>{filterSettings}</v:format.json.encode>'
v-bind:initial-filter-options='<v:format.json.encode>{filterOptions}</v:format.json.encode>'
v-bind:referring-page-uid="{referringPageUid}"
argument-prefix="{ep:argumentPrefix(pluginName: 'ajax')}"
:date-presets='{settings.datePickerPresets -> v:format.json.encode()}'
v-bind:date-presets='{settings.datePickerPresets -> v:format.json.encode()}'
v-bind:watchlist="watchlist"
inline-template>
<div class="searchresult-wrapper">
<div class="pagehead pagehead--searchresult">
@@ -290,7 +291,8 @@
v-for="item in section.items"
v-bind:key="item.key"
v-bind:item="item"
v-bind:section="section"></search-result-item>
v-bind:section="section"
v-bind:watchlist="watchlist"></search-result-item>
</div>
</template>
</div>
@@ -0,0 +1,14 @@
<html xmlns="http://www.w3.org/1999/xhtml" lang="en"
xmlns:f="http://typo3.org/ns/fluid/ViewHelpers"
xmlns:ep="http://typo3.org/ns/EP/EpTheme/ViewHelpers">
<f:layout name="Default"/>
<f:section name="Main">
<watchlist :watchlist="watchlist" :referring-page-uid="{referringPageUid}"
loader-uri="{f:uri.image(src: 'EXT:ep_theme/Resources/Public/images/ajax-loader-white.gif')}"
watchlist-uri="{ep:uri.ajax(action: 'list', controller: 'AjaxWatchlist', format: 'json', noCacheHash: 1)}"></watchlist>
</f:section>
</html>