iceholidays-frontend 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/stylesheets/iceholidays/frontend/application.sass.scss +1588 -0
  3. data/app/assets/stylesheets/iceholidays/frontend/common.scss +233 -0
  4. data/app/assets/stylesheets/iceholidays/frontend/layout.scss +188 -0
  5. data/app/assets/stylesheets/iceholidays/frontend/utils/_antd_overrides.scss +107 -0
  6. data/app/assets/stylesheets/iceholidays/frontend/utils/_variables.scss +3 -0
  7. data/app/assets/stylesheets/iceholidays/frontend/widgets/filter_pills.scss +45 -0
  8. data/app/assets/stylesheets/iceholidays/frontend/widgets/search_bar.scss +112 -0
  9. data/app/controllers/iceholidays/frontend/site_controller.rb +32 -0
  10. data/app/javascript/api-services/banners-api.service.ts +28 -0
  11. data/app/javascript/api-services/locations-api.service.ts +49 -0
  12. data/app/javascript/api-services/search-api.service.ts +16 -0
  13. data/app/javascript/api-services/series-api.service.ts +44 -0
  14. data/app/javascript/api-services/testimonials-api.service.ts +27 -0
  15. data/app/javascript/interfaces/banner.interface.ts +10 -0
  16. data/app/javascript/interfaces/country.interface.ts +18 -0
  17. data/app/javascript/interfaces/itinerary.interface.ts +18 -0
  18. data/app/javascript/interfaces/testimonial.interface.ts +6 -0
  19. data/app/javascript/react/App.tsx +32 -0
  20. data/app/javascript/react/components/Destinations.tsx +72 -141
  21. data/app/javascript/react/components/Testimonials.tsx +68 -61
  22. data/app/javascript/react/components/shared/Headline.tsx +29 -0
  23. data/app/javascript/react/components/shared/Postcard.tsx +31 -0
  24. data/app/javascript/react/components/shared/RibbonSection.tsx +21 -0
  25. data/app/javascript/react/index.js +3 -5
  26. data/app/javascript/react/layouts/MainFooter.tsx +72 -0
  27. data/app/javascript/react/layouts/MainHeader.tsx +45 -0
  28. data/app/javascript/react/layouts/MainLayout.tsx +21 -0
  29. data/app/javascript/react/pages/AboutUsPage.tsx +95 -0
  30. data/app/javascript/react/pages/BlogPage.tsx +79 -0
  31. data/app/javascript/react/pages/BlogShowPage.tsx +43 -0
  32. data/app/javascript/react/pages/ContactAgentsPage.tsx +16 -0
  33. data/app/javascript/react/pages/ContactUsPage.tsx +122 -0
  34. data/app/javascript/react/pages/CountriesPage.tsx +62 -0
  35. data/app/javascript/react/pages/Homepage.tsx +90 -0
  36. data/app/javascript/react/pages/ListingPage.tsx +246 -0
  37. data/app/javascript/react/pages/ShowPage.tsx +397 -0
  38. data/app/javascript/react/widgets/FilterPills.tsx +77 -0
  39. data/app/javascript/react/widgets/SearchBarWidget.tsx +42 -0
  40. data/app/views/iceholidays/frontend/site/index.html.erb +1 -24
  41. data/app/views/layouts/iceholidays/frontend/application.html.erb +2 -6
  42. data/config/routes.rb +9 -0
  43. data/lib/iceholidays/frontend/version.rb +1 -1
  44. data/public/iceholidays-assets/application.css +1873 -0
  45. data/public/iceholidays-assets/application.js +225 -651
  46. data/public/iceholidays-assets/application.js.map +4 -4
  47. data/public/iceholidays-assets/images/8D7N.png +0 -0
  48. data/public/iceholidays-assets/images/about_us.png +0 -0
  49. data/public/iceholidays-assets/images/about_us2.png +0 -0
  50. data/public/iceholidays-assets/images/blog.png +0 -0
  51. data/public/iceholidays-assets/images/blog1.png +0 -0
  52. data/public/iceholidays-assets/images/certificate1.png +0 -0
  53. data/public/iceholidays-assets/images/certificate2.png +0 -0
  54. data/public/iceholidays-assets/images/china_listings_cover.png +0 -0
  55. data/public/iceholidays-assets/images/china_southern_airlines.png +0 -0
  56. data/public/iceholidays-assets/images/china_southern_airlines_icon.png +0 -0
  57. data/public/iceholidays-assets/images/competitiveness.png +0 -0
  58. data/public/iceholidays-assets/images/contact_agents.png +0 -0
  59. data/public/iceholidays-assets/images/contact_us.png +0 -0
  60. data/public/iceholidays-assets/images/contact_us_form.png +0 -0
  61. data/public/iceholidays-assets/images/ethical.png +0 -0
  62. data/public/iceholidays-assets/images/hw_logo.png +0 -0
  63. data/public/iceholidays-assets/images/innovative.png +0 -0
  64. data/public/iceholidays-assets/images/plane.png +0 -0
  65. data/public/iceholidays-assets/images/social/ico_fb.png +0 -0
  66. data/public/iceholidays-assets/images/social/ico_ig.png +0 -0
  67. data/public/iceholidays-assets/images/social/ico_twitter.png +0 -0
  68. data/public/iceholidays-assets/images/social/ico_yt.png +0 -0
  69. data/public/iceholidays-assets/images/social.png +0 -0
  70. data/public/iceholidays-assets/images/tour1.png +0 -0
  71. metadata +61 -49
  72. data/app/assets/stylesheets/iceholidays/frontend/application.scss +0 -904
  73. data/app/javascript/react/components/Homepage.tsx +0 -15
  74. data/app/javascript/react/components/HomepageBanner.tsx +0 -62
  75. data/app/views/layouts/iceholidays/frontend/shared/_footer.html.erb +0 -42
  76. data/app/views/layouts/iceholidays/frontend/shared/_header.html.erb +0 -20
@@ -0,0 +1,28 @@
1
+ import axios from "axios";
2
+ import { Banner } from "../interfaces/banner.interface";
3
+
4
+ class BannersApi {
5
+
6
+ getBanners(): Promise<Banner[]> {
7
+ const apiUrl = "/api/v1/banner";
8
+
9
+ return axios.get(apiUrl)
10
+ .then(response => {
11
+ const banners = response.data.data.attributes.slides.map(slide => {
12
+ const banner:Banner = {
13
+ title: slide.title,
14
+ url: slide.url,
15
+ target: slide.target,
16
+ sortOrder: slide.sort_order,
17
+ desktopImage: slide.image.desktop,
18
+ mobileImage: slide.image.mobile
19
+ }
20
+ return banner;
21
+ })
22
+ return Promise.resolve(banners);
23
+ });
24
+ }
25
+
26
+ }
27
+
28
+ export default BannersApi;
@@ -0,0 +1,49 @@
1
+ import axios from "axios";
2
+ import { City, Country } from "../interfaces/country.interface";
3
+
4
+ class LocationsApi {
5
+
6
+ getCountries(): Promise<Country[]> {
7
+ const apiUrl = "/api/v1/locations";
8
+
9
+ return axios.get(apiUrl)
10
+ .then(response => {
11
+ const countries = response.data.map(locationData => {
12
+ const countryData = locationData.data.attributes;
13
+ const citiesData = countryData.children.map(childrenData => this.mapCity(childrenData.data.attributes));
14
+
15
+ const allCities = [...citiesData];
16
+ const citiesWithImages = allCities.filter(x => x.cover != null);
17
+ const topCities = citiesWithImages.splice(0,4);
18
+ const highlights = citiesWithImages.splice(0,5);
19
+
20
+ const country:Country = {
21
+ id: countryData.id,
22
+ name: countryData.name,
23
+ cover: countryData.cover,
24
+ tourCount: countryData.itineraries,
25
+ cities: allCities,
26
+ topCities: topCities,
27
+ highlights: highlights,
28
+ tags: allCities.filter(city => !topCities.includes(city) && !highlights.includes(city)),
29
+ }
30
+
31
+ return country;
32
+ })
33
+ return Promise.resolve(countries);
34
+ });
35
+ }
36
+
37
+ private mapCity(cityData): City{
38
+ const city:City = {
39
+ id: cityData.id,
40
+ name: cityData.name,
41
+ cover: cityData.cover,
42
+ tourCount: cityData.itineraries
43
+ };
44
+ return city;
45
+ }
46
+
47
+ }
48
+
49
+ export default LocationsApi;
@@ -0,0 +1,16 @@
1
+ import axios from "axios";
2
+
3
+ class SearchApi {
4
+
5
+ search(keyword:string, month?:number): Promise<any[]> {
6
+ const apiUrl = `/api/v1/series?keyword=${keyword}${month && `&month=${month}`}&type=series`;
7
+
8
+ return axios.get(apiUrl)
9
+ .then(response => {
10
+ return Promise.resolve(response.data);
11
+ });
12
+ }
13
+
14
+ }
15
+
16
+ export default SearchApi;
@@ -0,0 +1,44 @@
1
+ import axios from "axios";
2
+ import { Itinerary } from "../interfaces/itinerary.interface";
3
+
4
+ interface SearchParams {
5
+ keyword: string;
6
+ year?: string;
7
+ month?: string;
8
+ }
9
+
10
+ class SeriesApi {
11
+
12
+ getItineraries(searchParams: SearchParams): Promise<Itinerary[]> {
13
+ const apiUrl = `/api/v1/series?keyword=${searchParams.keyword}&type=series`;
14
+
15
+ return axios.get(apiUrl)
16
+ .then(response => {
17
+ const itineraries:Itinerary[] = response.data.itineraries.map(itineraryData => {
18
+ const itinerary:Itinerary = {
19
+ id: itineraryData.id,
20
+ description: itineraryData.description,
21
+ caption: itineraryData.caption,
22
+ otherCaption: itineraryData.otherCaption,
23
+ code: itineraryData.code,
24
+ category: itineraryData.category,
25
+ country: itineraryData.country,
26
+ price: itineraryData.price,
27
+ images: itineraryData.images,
28
+ priceCurrency: itineraryData.price_currency,
29
+ departureDate: itineraryData.departure_date,
30
+ guranteedDepartureDates: itineraryData.guranteed_departure_dates,
31
+ almostGuaranteedDepartureDates: itineraryData.almost_guaranteed_departure_dates,
32
+ departureLocations: itineraryData.departure_locations,
33
+ totalItineraries: itineraryData.total_itineraries,
34
+ }
35
+
36
+ return itinerary;
37
+ })
38
+ return Promise.resolve(itineraries);
39
+ });
40
+ }
41
+
42
+ }
43
+
44
+ export default SeriesApi;
@@ -0,0 +1,27 @@
1
+ import axios from "axios";
2
+ import { Testimonial } from "../interfaces/testimonial.interface";
3
+
4
+ class TestimonialsApi {
5
+
6
+ getTestimonials(): Promise<Testimonial[]> {
7
+ const apiUrl = "/api/v1/testimonials";
8
+
9
+ return axios.get(apiUrl)
10
+ .then(response => {
11
+ const testimonials = response.data.data.map(responseData => {
12
+ const testimonialData = responseData.attributes;
13
+ const testimonial:Testimonial = {
14
+ id: testimonialData.id,
15
+ tour: testimonialData.tour,
16
+ author: testimonialData.author,
17
+ comment: testimonialData.body,
18
+ }
19
+ return testimonial;
20
+ })
21
+ return Promise.resolve(testimonials);
22
+ });
23
+ }
24
+
25
+ }
26
+
27
+ export default TestimonialsApi;
@@ -0,0 +1,10 @@
1
+ export interface Banner {
2
+ id?:number;
3
+ title:string;
4
+ url?:string;
5
+ target?:string;
6
+ sortOrder?:number;
7
+ isActive?:boolean;
8
+ desktopImage?:string;
9
+ mobileImage?:string;
10
+ }
@@ -0,0 +1,18 @@
1
+ export interface Country extends Location {
2
+ cities:City[];
3
+ topCities:City[];
4
+ highlights:City[];
5
+ tags:City[];
6
+ }
7
+
8
+ export interface City extends Location {
9
+ }
10
+
11
+
12
+ interface Location {
13
+ id:number;
14
+ cover:string;
15
+ name:string;
16
+ tourCount:number;
17
+
18
+ }
@@ -0,0 +1,18 @@
1
+ export interface Itinerary {
2
+ id: number;
3
+ description: string;
4
+ code: string;
5
+ fileUrl?: string;
6
+ category: string;
7
+ caption: string;
8
+ otherCaption: string;
9
+ country: string;
10
+ price: string;
11
+ departureDate: string[];
12
+ guranteedDepartureDates: string[];
13
+ almostGuaranteedDepartureDates: string[];
14
+ departureLocations: string[];
15
+ images: string[];
16
+ priceCurrency: string;
17
+ totalItineraries: number;
18
+ }
@@ -0,0 +1,6 @@
1
+ export interface Testimonial {
2
+ id:number;
3
+ tour:string;
4
+ comment:string;
5
+ author:string;
6
+ }
@@ -0,0 +1,32 @@
1
+ import React from "react";
2
+ import { BrowserRouter, Routes, Route } from "react-router-dom";
3
+ import Homepage from "./pages/Homepage";
4
+ import ListingPage from "./pages/ListingPage";
5
+ import ShowPage from "./pages/ShowPage";
6
+ import MainLayout from "./layouts/MainLayout";
7
+ import AboutUsPage from "./pages/AboutUsPage";
8
+ import CountriesPage from "./pages/CountriesPage";
9
+ import BlogPage from "./pages/BlogPage";
10
+ import ContactAgentsPage from "./pages/ContactAgentsPage";
11
+ import ContactUsPage from "./pages/ContactUsPage";
12
+ import BlogShowPage from "./pages/BlogShowPage";
13
+
14
+ export default function App() {
15
+ return (
16
+ <BrowserRouter>
17
+ <Routes>
18
+ <Route path="/app" element={<MainLayout />}>
19
+ <Route index element={<Homepage />} />
20
+ <Route path="/app/listing" element={<ListingPage />} />
21
+ <Route path="/app/show" element={<ShowPage />} />
22
+ <Route path="/app/about-us" element={<AboutUsPage />} />
23
+ <Route path="/app/countries" element={<CountriesPage />} />
24
+ <Route path="/app/blog" element={<BlogPage />} />
25
+ <Route path="/app/blog/1" element={<BlogShowPage />} />
26
+ <Route path="/app/contact-agents" element={<ContactAgentsPage />} />
27
+ <Route path="/app/contact-us" element={<ContactUsPage />} />
28
+ </Route>
29
+ </Routes>
30
+ </BrowserRouter>
31
+ );
32
+ }
@@ -1,138 +1,69 @@
1
- import { faCaretRight } from "@fortawesome/free-solid-svg-icons/faCaretRight";
2
- import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
3
- import { Tabs, TabsProps } from "antd";
1
+ import { notification, Tabs, TabsProps } from "antd";
4
2
  import React from "react";
3
+ import LocationsApi from "../../api-services/locations-api.service";
4
+ import { Country } from "../../interfaces/country.interface";
5
+ import Postcard from "./shared/Postcard";
6
+ import { mdiMenuRight } from "@mdi/js";
7
+ import Icon from "@mdi/react";
8
+ import tour from "antd/es/tour";
9
+ import { Link } from "react-router-dom";
5
10
 
6
- const countries = [
7
- {
8
- name: "Africa",
9
- image: "iceholidays-assets/images/africa.png",
10
- topCities: [{
11
- name: "Kenya",
12
- image: "iceholidays-assets/images/kenya.png",
13
- },
14
- {
15
- name: "South Africa",
16
- image: "iceholidays-assets/images/southafrica.png",
17
- },
18
- {
19
- name: "Tanzania",
20
- image: "iceholidays-assets/images/tanzania.png",
21
- },
22
- {
23
- name: "Uaganda",
24
- image: "iceholidays-assets/images/uganda.png",
25
- }]
26
- },
27
- {
28
- name: "China",
29
- image: "iceholidays-assets/images/china.png",
30
- topCities: [{
31
- name: "Chong Qing",
32
- image: "iceholidays-assets/images/china.png",
33
- },
34
- {
35
- name: "Guangzhou",
36
- image: "iceholidays-assets/images/guangzhou.png",
37
- },
38
- {
39
- name: "Guilin",
40
- image: "iceholidays-assets/images/guilin.png",
41
- },
42
- {
43
- name: "Hongkong",
44
- image: "iceholidays-assets/images/hongkong.png",
45
- }],
46
- moreCities: [
47
- "Leizhou", "Ningxia", "Qinghai", "Shanghai", "Tibet", "Xiamen", "Yunnan"
48
- ],
49
- tours: [
50
- {
51
- name: "Harbin",
52
- image: "iceholidays-assets/images/harbin.png",
53
- },
54
- {
55
- name: "Inner Mongolia",
56
- image: "iceholidays-assets/images/inner_mongolia.png",
57
- },
58
- {
59
- name: "Jiangxi",
60
- image: "iceholidays-assets/images/jiangxi.png",
61
- },
62
- {
63
- name: "Kun Ming",
64
- image: "iceholidays-assets/images/kunming.png",
65
- },
66
- {
67
- name: "Silk Road",
68
- image: "iceholidays-assets/images/slikroad.png",
69
- }
70
- ]
71
- },
72
- {
73
- name: "Europe"
74
- },
75
- {
76
- name: "Exotic"
77
- },
78
- {
79
- name: "japan"
80
- },
81
- {
82
- name: "korea"
83
- },
84
- {
85
- name: "maldives"
86
- },
87
- {
88
- name: "taiwan"
89
- },
90
- ]
91
11
 
92
- function Destinations(){
93
- var items:TabsProps["items"] = countries.map((country, index)=>{
94
- return {
95
- key: index.toString(),
96
- label: country.name,
97
- children: (
98
- <>
12
+ export default class Destinations extends React.Component {
13
+ api = new LocationsApi;
14
+
15
+ state = {
16
+ countries: []
17
+ }
18
+
19
+ componentDidMount() {
20
+ this.api.getCountries()
21
+ .then(locationsData => {
22
+ this.setState({countries: locationsData})
23
+ })
24
+ .catch(error => {
25
+ notification.error({ message: 'An error occured while loading countries.'});
26
+ });
27
+ }
28
+
29
+ render(){
30
+ const pluralize = (count: number, noun: string, suffix = 's') =>
31
+ `${count} ${noun}${count > 1 ? suffix : ''}`;
32
+
33
+ const listingLink = (keyword: string) => {
34
+ return `/app/listing?keyword=${keyword}`;
35
+ }
36
+
37
+ var items:TabsProps["items"] = this.state.countries.map((country:Country, index)=>{
38
+ return {
39
+ key: index.toString(),
40
+ label: country.name,
41
+ children: (
99
42
  <div>
100
- <div className="country-info">
101
- <img className="country-image"
102
- src={`${country.image}`} />
43
+ <Link to={listingLink(country.name)} className="country">
44
+ <img className="country-image" src={`${country.cover}`} />
103
45
  <h1 className="country-name"> {country.name} </h1>
104
- <img className="bottom-logo" src="iceholidays-assets/images/Frame71.png"></img>
105
- </div>
46
+ <img className="bottom-logo" src="/iceholidays-assets/images/Frame71.png"></img>
47
+ </Link>
106
48
  {
107
- country.topCities?.length &&
49
+ country.topCities?.length > 0 &&
108
50
  <div className="top-cities">
109
51
  <h2>Top Cities</h2>
110
- <div id="city-group">
52
+ <div className="postcards">
111
53
  {
112
- country.topCities?.map((city, index)=>{
113
- return (
114
- <div key={index} className="city-cover">
115
- <img src={`${city.image}`}/>
116
- <div className="city-details">
117
- <span className="city-name">{city.name}</span>
118
- <span className="tour-count">1 Tour <FontAwesomeIcon icon={faCaretRight}/> </span>
119
- </div>
120
- </div>
121
- )
122
- })
54
+ country.topCities?.map((city, index)=><Postcard key={index} link={listingLink(city.name)} image={city.cover} name={city.name} tourCount={city.tourCount}/>)
123
55
  }
124
56
  </div>
125
57
  </div>
126
58
  }
127
-
128
59
  {
129
- country.moreCities?.length &&
60
+ country.tags?.length > 0 &&
130
61
  <div className="more-cities">
131
62
  <h2>Browse more {country.name} destination</h2>
132
63
  <div className="other-destinations">
133
64
  {
134
- country.moreCities?.map((otherCity, index)=>{
135
- return <a key={index} href="/"> {otherCity} </a>
65
+ country.tags?.map((city, index)=>{
66
+ return <Link key={index} to={listingLink(city.name)}> {city.name} </Link>
136
67
  })
137
68
  }
138
69
  </div>
@@ -140,37 +71,37 @@ function Destinations(){
140
71
  }
141
72
 
142
73
  {
143
- country.tours?.length &&
144
- <div className="other-tours">
74
+ country.highlights?.length > 0 &&
75
+ <div className="highlights">
145
76
  {
146
- country.tours?.map((tour, index)=>{
147
- return <div key={index} className={"tour-story"} style={{backgroundImage:`linear-gradient(180deg, rgba(0, 0, 0, 0) 50%, rgba(0, 0, 0, 0.8) 100%), url("${tour.image}")`}}>
148
- <div className="tour-detail">
149
- <span className="city-name"> {tour.name} </span>
150
- <span className="tour-count"> 1 tour <FontAwesomeIcon icon={faCaretRight}/> </span>
77
+ country.highlights?.map((city, index)=>{
78
+ return <Link key={index} to={listingLink(city.name)}>
79
+ <div className="tour" style={{backgroundImage:`linear-gradient(180deg, rgba(0, 0, 0, 0) 50%, rgba(0, 0, 0, 0.8) 100%), url("${city.cover}")`}}>
80
+ <div className="details">
81
+ <span className="city-name"> {city.name} </span>
82
+ <span className="tour-count"> {pluralize(city.tourCount, "tour")} <Icon path={mdiMenuRight} size={1} /> </span>
83
+ </div>
151
84
  </div>
152
- </div>
85
+ </Link>
153
86
  })
154
87
  }
155
88
  </div>
156
89
  }
157
90
  </div>
158
- </>
159
- )
160
- }
161
- })
162
-
163
- return (
164
- <div id="popular-destinations">
165
- <h1>Explore Popular Destination</h1>
91
+ )
92
+ }
93
+ })
166
94
 
167
- <Tabs
168
- defaultActiveKey="1"
169
- items={items}
170
- type="card"/>
95
+ return (
96
+ <div id="popular-destinations">
97
+ <h1>Explore Popular Destination</h1>
171
98
 
172
- </div>
173
- )
174
- }
99
+ <Tabs
100
+ defaultActiveKey="1"
101
+ items={items}
102
+ type="card"/>
175
103
 
176
- export default Destinations;
104
+ </div>
105
+ )
106
+ }
107
+ }
@@ -1,19 +1,75 @@
1
- import { faPenNib } from "@fortawesome/free-solid-svg-icons/faPenNib";
2
- import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
3
- import { Carousel } from "antd";
1
+ import { Carousel, notification } from "antd";
4
2
  import React from "react";
5
3
  import SlickButtonFix from "./shared/SlickButtonFix";
6
- import { faCaretLeft } from "@fortawesome/free-solid-svg-icons/faCaretLeft";
7
- import { faCaretRight } from "@fortawesome/free-solid-svg-icons/faCaretRight";
4
+ import { mdiFountainPenTip, mdiMenuLeft, mdiMenuRight } from "@mdi/js";
5
+ import Icon from "@mdi/react";
6
+ import RibbonSection from "./shared/RibbonSection";
7
+ import TestimonialsApi from "../../api-services/testimonials-api.service";
8
+ import { Testimonial } from "../../interfaces/testimonial.interface";
9
+ import createDOMPurify from 'dompurify'
8
10
 
11
+ const DOMPurify = createDOMPurify(window)
9
12
 
10
- const contentStyle: React.CSSProperties = {
11
- height: '160px',
12
- color: '#fff',
13
- lineHeight: '160px',
14
- textAlign: 'center',
15
- background: '#364d79',
16
- };
13
+ export default class Testimonials extends React.Component {
14
+ api = new TestimonialsApi;
15
+
16
+ state = {
17
+ reviews: []
18
+ }
19
+
20
+ componentDidMount() {
21
+ this.api.getTestimonials()
22
+ .then(testimonialsData => {
23
+ this.setState({reviews: testimonialsData})
24
+ })
25
+ .catch(error => {
26
+ notification.error({ message: 'An error occured while loading testimonials.'});
27
+ });
28
+ }
29
+
30
+ render(){
31
+ return (
32
+ <RibbonSection title="Testimonial">
33
+ <div id="testimonials">
34
+ <Carousel autoplay arrows dots={false} draggable slidesToScroll= {1} slidesToShow={this.state.reviews.length > 3 ? 3 : this.state.reviews.length}
35
+ prevArrow={
36
+ <SlickButtonFix>
37
+ <Icon path={mdiMenuLeft} size={4} />
38
+ </SlickButtonFix>
39
+ }
40
+ nextArrow={
41
+ <SlickButtonFix>
42
+ <Icon path={mdiMenuRight} size={4} />
43
+ </SlickButtonFix>
44
+ }>
45
+ {
46
+ this.state.reviews.map((review:Testimonial)=>{
47
+ return (
48
+ <div key={review.id} className="testimonial">
49
+ <img src="/iceholidays-assets/images/5star.png" className="stars-icon"/>
50
+ <div className="tour">
51
+ <img src="/iceholidays-assets/images/logomark.png" className="st-logo"/>
52
+ <div>
53
+ <span> {review.tour} </span>
54
+ </div>
55
+ </div>
56
+ <div className="comment">
57
+ <span dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(review.comment) }} />
58
+ </div>
59
+ <div className="author">
60
+ <Icon path={mdiFountainPenTip} size={1} className="author-icon" />
61
+ <span>{review.author}</span>
62
+ </div>
63
+ </div>
64
+ )
65
+ })
66
+ }
67
+ </Carousel>
68
+ </div>
69
+ </RibbonSection>
70
+ )
71
+ }
72
+ }
17
73
 
18
74
  const reviews = [
19
75
  {
@@ -37,52 +93,3 @@ const reviews = [
37
93
  },
38
94
  ]
39
95
 
40
- function Testimonials(){
41
- return (
42
- <div id="testimonial">
43
- <div id="testimonial-header" style={{backgroundImage: "url('iceholidays-assets/images/Layer_1.png')"}}>
44
- <span>Testimonial</span>
45
- </div>
46
-
47
- <div id="testimonials">
48
- <Carousel autoplay arrows dots={false} draggable slidesToScroll= {1} slidesToShow={3}
49
- prevArrow={
50
- <SlickButtonFix>
51
- <FontAwesomeIcon icon={faCaretLeft} size="4x"/>
52
- </SlickButtonFix>
53
- }
54
- nextArrow={
55
- <SlickButtonFix>
56
- <FontAwesomeIcon icon={faCaretRight} size="4x"/>
57
- </SlickButtonFix>
58
- }>
59
- {
60
- reviews?.map((review)=>{
61
- return (
62
- <div key={review.key} className="testimonial">
63
- <img src="iceholidays-assets/images/5star.png" className="stars-icon"/>
64
- <div className="itinerary-summary">
65
- <img src="iceholidays-assets/images/logomark.png" className="st-logo"/>
66
- <div>
67
- <span> {review.itinerarySummaryTitle} </span>
68
- <span> {review.itinerarySummaryDesc} </span>
69
- </div>
70
- </div>
71
- <div className="comment">
72
- <span>{review.comment}</span>
73
- </div>
74
- <div className="customer-name">
75
- <FontAwesomeIcon icon={faPenNib} className="author-icon" />
76
- <span>{review.customerName}</span>
77
- </div>
78
- </div>
79
- )
80
- })
81
- }
82
- </Carousel>
83
- </div>
84
- </div>
85
- )
86
- }
87
-
88
- export default Testimonials;
@@ -0,0 +1,29 @@
1
+ import { Breadcrumb } from "antd";
2
+ import React, { JSX } from "react";
3
+ import SearchBarWidget from "../../widgets/SearchBarWidget";
4
+
5
+
6
+ const Headline = (
7
+ props: {
8
+ bannerImage: string;
9
+ title?: string;
10
+ children?: JSX.Element;
11
+ breadcrumbs: any[];
12
+ }
13
+ ) => {
14
+ const { bannerImage, title, children, breadcrumbs } = props;
15
+ return (
16
+ <>
17
+ <SearchBarWidget/>
18
+ <div id="headline">
19
+ <div id="headline_banner" style={{backgroundImage: `url(${bannerImage})`}}></div>
20
+ <div id="headline_header">
21
+ { title ? <h1> {title} </h1> : children}
22
+ </div>
23
+ <Breadcrumb items={breadcrumbs} separator=">"/>
24
+ </div>
25
+ </>
26
+ )
27
+ }
28
+
29
+ export default Headline;