Implement filterable offer list

This commit is contained in:
Björn Fromme
2019-07-16 17:35:35 +02:00
parent b3234be447
commit ad64228692
22 changed files with 861 additions and 176 deletions
@@ -6,6 +6,9 @@ import Vue from 'vue'
import $ from 'jquery'
import VueSession from 'vue-session'
import 'lazysizes';
import 'lazysizes/plugins/attrchange/ls.attrchange';
const slick = require('slick-carousel');
const Sticky = require('sticky-js');
const $window = $(window);
@@ -15,6 +18,7 @@ Vue.use(VueSession);
import InquiryForm from './components/InquiryForm.vue'
import Configurator from './components/Configurator.vue'
import TeaserCarousel from './components/TeaserCarousel.vue'
import OfferTeasers from './components/OfferTeasers.vue'
const carouselArrowLeft = '<svg class="carousel__arrow carousel__arrow--prev" viewBox="0 0 56 56"><polyline points="40,5 16,28 40,51"></polyline></svg>';
const carouselArrowRight = '<svg class="carousel__arrow carousel__arrow--next" viewBox="0 0 56 56"><polyline points="16,5 40,28 16,51"></polyline></svg>';
@@ -24,7 +28,8 @@ new Vue({
components: {
InquiryForm,
Configurator,
TeaserCarousel
TeaserCarousel,
OfferTeasers
},
data () {
return {
@@ -0,0 +1,131 @@
<template>
<div class="offerlist">
<div class="offerlist__filterpanel">
<form class="form form--horizontal">
<div class="form__item">
<label>{{ language.type }}</label>
<select v-model="filterSettings.travelType" @change="load" class="form__field">
<option value="0">{{ language.noSelection }}</option>
<option v-for="item in filterOptions.travelTypes" :value="item.type">{{ item.label }}</option>
</select>
</div>
<div class="form__item">
<label>{{ language.destination }}</label>
<select v-model="filterSettings.locationType" @change="load" class="form__field">
<option value="0">{{ language.noSelection }}</option>
<option v-for="item in filterOptions.locationTypes" :value="item.type">{{ item.label }}</option>
</select>
</div>
<div class="form__item">
<label>{{ language.distance }}</label>
<select v-model="filterSettings.locationDistance" @change="load" class="form__field">
<option value="0">{{ language.noSelection }}</option>
<option v-for="item in filterOptions.locationDistances" :value="item.type">{{ item.label }}
</option>
</select>
</div>
<div class="form__item">
<label>{{ language.hotel }}</label>
<select v-model="filterSettings.hotelType" @change="load" class="form__field">
<option value="0">{{ language.noSelection }}</option>
<option v-for="item in filterOptions.hotelTypes" :value="item.type">{{ item.label }}</option>
</select>
</div>
<div class="form__item">
<label>{{ language.programme }}</label>
<select v-model="filterSettings.activityType" @change="load" class="form__field">
<option value="0">{{ language.noSelection }}</option>
<option v-for="item in filterOptions.activityTypes" :value="item.type">{{ item.label }}</option>
</select>
</div>
<div class="form__item form__item--no-label">
<button type="submit" @click.prevent="init" class="form__button">{{ language.reset }}</button>
</div>
</form>
</div>
<div class="offerlist__teasers">
<a v-for="offer in offers"
:href="offer.detailPageUri"
class="offerlist-item"
:class="offer.cssClass"
:title="'Beispielangebot ' + offer.name + offer.locationName">
<div class="card card--no-form offerlist-item__card">
<div class="card__inner">
<picture>
<source media="(max-width: 512px)" :data-srcset="offer.teaserImageMobile + ' 500w'"/>
<img class="card__image scale lazyload" data-sizes="auto"
:data-src="offer.teaserImage" alt="">
</picture>
<div class="card__caption">
<span class="card__subtitle" v-html="offer.name"></span>
<span class="card__title">{{ offer.locationName }}</span>
</div>
</div>
</div>
</a>
</div>
<div class="offerlist__overlay" v-show="loading"></div>
</div>
</template>
<script>
import axios from 'axios';
import $ from 'jquery';
export default {
name: 'OfferTeasers',
data() {
return {
offers: [],
filterSettings: {},
filterOptions: {},
loading: false,
}
},
props: {
endpointUri: {
type: String,
required: true
},
language: {
type: Object,
required: true
}
},
methods: {
load() {
this.loading = true;
sessionStorage.setItem('filterSettings', JSON.stringify(this.filterSettings));
axios({
url: this.endpointUri,
method: 'post',
data: $.param({'tx_epevents_ajax[filterSettings]': this.filterSettings})
}).then(response => {
this.offers = response.data.offers;
this.filterOptions = response.data.filterOptions;
this.loading = false;
}).catch(() => {
this.loading = false;
});
},
init() {
this.filterSettings = {
travelType: 0,
locationType: 0,
locationDistance: 0,
hotelType: 0,
activityType: 0
};
this.load();
}
},
mounted() {
if (sessionStorage.getItem('filterSettings')) {
this.filterSettings = JSON.parse(sessionStorage.getItem('filterSettings'));
this.load();
} else {
this.init();
}
}
}
</script>
@@ -10,7 +10,7 @@ require('cookieconsent/build/cookieconsent.min.css');
require('flatpickr/dist/flatpickr.css');
// Require main app
require('./app');
require('./_app');
// Require main styles
require('../scss/main.scss');
@@ -1,122 +1,147 @@
.form {
margin: 0;
padding: 0;
position: relative;
margin: 0;
padding: 0;
position: relative;
max-width: 100%;
@include mq($from: desktop) {
&:not(&--horizontal) {
max-width: 75%;
}
}
&--box {
max-width: 100%;
}
@include mq($from: desktop) {
max-width: 75%;
}
&--box {
max-width: 100%;
&--horizontal {
width: 100%;
@include mq($from: tablet) {
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
}
}
}
.form__item {
display: block;
margin-bottom: 8px;
&--textarea {
padding-top: 16px;
}
&--button {
padding-top: 16px;
}
&--radios {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
background-color: $color-brand-light;
color: white;
border-radius: 8px;
}
&--mobile {
display: block;
margin-bottom: 8px;
&--textarea {
padding-top: 16px;
@include mq($from: tablet) {
display: none;
}
}
&--tablet {
display: none;
@include mq($from: tablet, $until: desktop) {
display: block;
}
}
&--desktop {
display: none;
@include mq($from: desktop) {
display: block;
}
}
.form--horizontal & {
width: 100%;
padding: 0 8px;
&--no-label {
padding-top: 20px;
}
&--button {
padding-top: 16px;
}
&--radios {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
background-color: $color-brand-light;
color: white;
border-radius: 8px;
}
&--mobile {
display: block;
@include mq($from: tablet) {
display: none;
}
}
&--tablet {
display: none;
@include mq($from: tablet, $until: desktop) {
display: block;
}
}
&--desktop {
display: none;
@include mq($from: desktop) {
display: block;
}
@include mq($from: tablet) {
width: 33.3333%;
}
}
}
.form__item__error {
padding: 8px;
font-size: 90%;
padding: 8px;
font-size: 90%;
}
.form__field {
@include nooutline;
border: none;
border-radius: 8px;
width: 100%;
padding: 8px 16px;
color: white;
@include nooutline;
border: none;
border-radius: 8px;
width: 100%;
padding: 8px 16px;
color: white;
background-color: $color-brand-light;
.form--box & {
background-color: $color-brand-light;
color: white;
}
.form--box & {
background-color: $color-brand-light;
color: white;
}
&--textarea {
resize: vertical;
}
&--textarea {
resize: vertical;
&--radio {
background-color: transparent;
display: block;
padding: 8px;
width: 100%;
@include mq($from: tablet) {
width: 50%;
}
@include mq($from: desktop) {
width: 25%;
}
}
&--radio {
background-color: transparent;
display: block;
padding: 8px;
width: 100%;
@include mq($from: tablet) {
width: 50%;
}
@include mq($from: desktop) {
width: 25%;
}
}
&--configurator {
background-color: rgba(white, 0.6);
}
&--configurator {
background-color: rgba(white, 0.6);
}
.has-errors & {
border: 1px solid $color-formerror;
}
.has-errors & {
border: 1px solid $color-formerror;
}
}
.form__link {
display: block;
font-size: 80%;
display: block;
font-size: 80%;
&--indent {
text-align: right;
}
&--indent {
text-align: right;
}
.configurator & {
color: white;
padding-top: 16px;
}
.configurator & {
color: white;
padding-top: 16px;
}
}
.form__text {
@@ -128,101 +153,101 @@
}
.form__button {
outline: none;
display: block;
outline: none;
display: block;
width: 100%;
color: white;
background-color: $color-brand-secondary;
border: none;
font-weight: 700;
text-decoration: none;
text-align: center;
padding: 8px 32px;
margin-bottom: 16px;
@include mq($from: tablet) {
display: inline-block;
width: auto;
margin-bottom: 0;
}
.form--box & {
width: 100%;
color: white;
background-color: $color-brand-secondary;
border: none;
font-weight: 700;
text-decoration: none;
text-align: center;
padding: 8px 32px;
margin-bottom: 16px;
@include mq($from: tablet) {
display: inline-block;
width: auto;
margin-bottom: 0;
}
.form--box & {
width: 100%;
font-size: 20px;
padding: 8px 0;
}
font-size: 20px;
padding: 8px 0;
}
}
.form__overlay {
@include size(100%);
position: absolute;
top: 0;
left: 0;
display: flex;
flex-wrap: wrap;
justify-content: center;
align-items: center;
background-color: rgba($color-divider, 0.75);
@include size(100%);
position: absolute;
top: 0;
left: 0;
display: flex;
flex-wrap: wrap;
justify-content: center;
align-items: center;
background-color: rgba($color-divider, 0.75);
.form--box & {
background-color: rgba($color-brand-primary, 0.75);
}
.form--box & {
background-color: rgba($color-brand-primary, 0.75);
}
}
.form__overlay__text {
text-align: center;
text-align: center;
}
::-webkit-input-placeholder { /* WebKit, Blink, Edge */
color: white;
text-transform: uppercase;
font-size: 90%;
color: white;
text-transform: uppercase;
font-size: 90%;
}
:-moz-placeholder { /* Mozilla Firefox 4 to 18 */
color: white;
opacity: 1;
text-transform: uppercase;
font-size: 90%;
color: white;
opacity: 1;
text-transform: uppercase;
font-size: 90%;
}
::-moz-placeholder { /* Mozilla Firefox 19+ */
color: white;
opacity: 1;
text-transform: uppercase;
font-size: 90%;
color: white;
opacity: 1;
text-transform: uppercase;
font-size: 90%;
}
:-ms-input-placeholder { /* Internet Explorer 10-11 */
color: white;
text-transform: uppercase;
font-size: 90%;
color: white;
text-transform: uppercase;
font-size: 90%;
}
::-ms-input-placeholder { /* Microsoft Edge */
color: white;
text-transform: uppercase;
font-size: 90%;
color: white;
text-transform: uppercase;
font-size: 90%;
}
.form--box {
::-webkit-input-placeholder { /* WebKit, Blink, Edge */
color: white;
}
::-webkit-input-placeholder { /* WebKit, Blink, Edge */
color: white;
}
:-moz-placeholder { /* Mozilla Firefox 4 to 18 */
color: white;
}
:-moz-placeholder { /* Mozilla Firefox 4 to 18 */
color: white;
}
::-moz-placeholder { /* Mozilla Firefox 19+ */
color: white;
}
::-moz-placeholder { /* Mozilla Firefox 19+ */
color: white;
}
:-ms-input-placeholder { /* Internet Explorer 10-11 */
color: white;
}
:-ms-input-placeholder { /* Internet Explorer 10-11 */
color: white;
}
::-ms-input-placeholder { /* Microsoft Edge */
color: white;
}
::-ms-input-placeholder { /* Microsoft Edge */
color: white;
}
}
@@ -0,0 +1,35 @@
.offerlist {
position: relative;
}
.offerlist__overlay {
@include size(100%);
position: absolute;
top: 0;
left: 0;
z-index: 10;
background: rgba(white, 0.85) url(../images/spinner_default.gif) no-repeat 50% 5%;
}
.offerlist__teasers {
@include mq($from: tablet) {
display: flex;
flex-wrap: wrap;
}
}
.offerlist-item {
width: 100%;
text-decoration: none;
margin-bottom: 32px;
display: block;
@include mq($from: mobile) {
display: flex;
}
@include mq($from: tablet) {
width: 50%;
}
@include mq($from: desktop) {
width: 33.3333%;
}
}
@@ -35,6 +35,7 @@ $output-bourbon-deprecation-warnings: false;
@import "team";
@import "cookieconsent";
@import "search";
@import "offerlist";
@import "paginator";
@import "news";
@import "datepicker";
@@ -58,6 +58,15 @@
<trans-unit id="tx_epevents_domain_model_location.configurator_label.4">
<target>Spezial</target>
</trans-unit>
<trans-unit id="tx_epevents_domain_model_distance.configurator_label.1">
<target>Deutschland</target>
</trans-unit>
<trans-unit id="tx_epevents_domain_model_distance.configurator_label.2">
<target>Europa</target>
</trans-unit>
<trans-unit id="tx_epevents_domain_model_distance.configurator_label.3">
<target>Welt</target>
</trans-unit>
<trans-unit id="tx_epevents_domain_model_activity.configurator_label.1">
<target>Outdoor &amp; Action</target>
</trans-unit>
@@ -220,6 +229,9 @@
<trans-unit id="tx_epevents.label.search">
<target>Suchen</target>
</trans-unit>
<trans-unit id="tx_epevents.label.reset">
<target>Reset</target>
</trans-unit>
<trans-unit id="tx_epevents.label.search_intro">
<target><![CDATA[Ihre Suche nach <i>%s</i> ergab %d Treffer:]]></target>
</trans-unit>
@@ -58,6 +58,15 @@
<trans-unit id="tx_epevents_domain_model_location.configurator_label.4">
<source>Special</source>
</trans-unit>
<trans-unit id="tx_epevents_domain_model_distance.configurator_label.1">
<source>Germany</source>
</trans-unit>
<trans-unit id="tx_epevents_domain_model_distance.configurator_label.2">
<source>Europe</source>
</trans-unit>
<trans-unit id="tx_epevents_domain_model_distance.configurator_label.3">
<source>World</source>
</trans-unit>
<trans-unit id="tx_epevents_domain_model_activity.configurator_label.1">
<source>Outdoor &amp; Action</source>
</trans-unit>
@@ -220,6 +229,9 @@
<trans-unit id="tx_epevents.label.search">
<source>Search</source>
</trans-unit>
<trans-unit id="tx_epevents.label.reset">
<source>Reset</source>
</trans-unit>
<trans-unit id="tx_epevents.label.search_intro">
<source><![CDATA[Your search for <i>%s</i> results in %d findings:]]></source>
</trans-unit>
@@ -19,14 +19,14 @@
<f:if condition="{offer.imageTeaserMobile}">
<f:then>
<source media="(max-width: 512px)"
srcset="{f:uri.image(image: offer.imageTeaserMobile, width: '500', cropVariant: 'default')} 500w"/>
srcset="{f:uri.image(image: offer.imageTeaserMobile, width: '500')} 500w"/>
</f:then>
<f:else>
<source media="(max-width: 512px)"
srcset="{f:uri.image(image: offer.imageTeaser, width: '500', cropVariant: 'mobile')} 500w"/>
</f:else>
</f:if>
<img class="card__image scale" data-lazy="{f:uri.image(image: offer.imageTeaser, cropVariant: 'default', width: '500')}" alt="{offer.imageTeaser.alternative}">
<img class="card__image scale" data-lazy="{f:uri.image(image: offer.imageTeaser, width: '500')}" alt="{offer.imageTeaser.alternative}">
</picture>
</f:then>
<f:else>
@@ -0,0 +1,10 @@
<div xmlns="http://www.w3.org/1999/xhtml" lang="en"
xmlns:f="http://typo3.org/ns/fluid/ViewHelpers">
<f:layout name="Default"/>
<f:section name="Main">
<offer-teasers endpoint-uri="{f:uri.action(pageUid: settings.ajaxPageUid, action: 'list', controller: 'AjaxOffer', pluginName: 'ajax', pageType: '1703')}" :language='{language}'></offer-teasers>
</f:section>
</div>