iceholidays-frontend 0.4.0 → 0.6.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.
- checksums.yaml +4 -4
- data/app/assets/stylesheets/iceholidays/frontend/actiontext.scss +119 -0
- data/app/assets/stylesheets/iceholidays/frontend/application.sass.scss +968 -355
- data/app/assets/stylesheets/iceholidays/frontend/common.scss +159 -74
- data/app/assets/stylesheets/iceholidays/frontend/layout.scss +218 -125
- data/app/assets/stylesheets/iceholidays/frontend/utils/_antd_overrides.scss +22 -7
- data/app/assets/stylesheets/iceholidays/frontend/utils/_variables.scss +2 -1
- data/app/assets/stylesheets/iceholidays/frontend/widgets/filter_pills.scss +19 -12
- data/app/assets/stylesheets/iceholidays/frontend/widgets/search_bar.scss +4 -0
- data/app/controllers/iceholidays/frontend/posts_controller.rb +14 -0
- data/app/javascript/api-services/agents-api.service.ts +33 -0
- data/app/javascript/api-services/locations-api.service.ts +23 -1
- data/app/javascript/api-services/series-api.service.ts +48 -28
- data/app/javascript/interfaces/agent.interface.ts +11 -0
- data/app/javascript/interfaces/country.interface.ts +4 -3
- data/app/javascript/interfaces/itinerary.interface.ts +101 -8
- data/app/javascript/react/App.tsx +1 -1
- data/app/javascript/react/components/Destinations.tsx +30 -20
- data/app/javascript/react/components/PriceDetails.tsx +146 -0
- data/app/javascript/react/components/shared/ContactAgentsForm.tsx +44 -0
- data/app/javascript/react/components/shared/Headline.tsx +2 -1
- data/app/javascript/react/components/shared/LocationDropdown.tsx +34 -0
- data/app/javascript/react/components/shared/{Postcard.tsx → LocationPostcards.tsx} +22 -1
- data/app/javascript/react/layouts/MainFooter.tsx +64 -39
- data/app/javascript/react/layouts/MainHeader.tsx +68 -30
- data/app/javascript/react/pages/AboutUsPage.tsx +8 -8
- data/app/javascript/react/pages/BlogPage.tsx +6 -4
- data/app/javascript/react/pages/ContactAgentsPage.tsx +174 -5
- data/app/javascript/react/pages/ContactUsPage.tsx +5 -5
- data/app/javascript/react/pages/CountriesPage.tsx +3 -8
- data/app/javascript/react/pages/Homepage.tsx +23 -13
- data/app/javascript/react/pages/ListingPage.tsx +197 -145
- data/app/javascript/react/pages/ShowPage.tsx +275 -265
- data/app/javascript/react/widgets/FilterPills.tsx +83 -49
- data/app/javascript/react/widgets/SearchBarWidget.tsx +24 -8
- data/app/views/iceholidays/frontend/posts/index.html.erb +9 -0
- data/app/views/iceholidays/frontend/posts/show.html.erb +2 -0
- data/config/routes.rb +2 -1
- data/lib/iceholidays/frontend/version.rb +1 -1
- data/public/iceholidays-assets/application.css +1209 -437
- data/public/iceholidays-assets/application.js +91 -104
- data/public/iceholidays-assets/application.js.map +4 -4
- data/public/iceholidays-assets/images/TSTRibbon.png +0 -0
- data/public/iceholidays-assets/images/about-us_logo_mobile.png +0 -0
- data/public/iceholidays-assets/images/destinations_logo.png +0 -0
- data/public/iceholidays-assets/images/footer-bg_mobile.png +0 -0
- data/public/iceholidays-assets/images/logo_mobile.png +0 -0
- metadata +19 -27
- data/public/iceholidays-assets/images/8D7N.png +0 -0
- data/public/iceholidays-assets/images/Frame71.png +0 -0
- data/public/iceholidays-assets/images/africa.png +0 -0
- data/public/iceholidays-assets/images/banner1.png +0 -0
- data/public/iceholidays-assets/images/banner2.png +0 -0
- data/public/iceholidays-assets/images/china.png +0 -0
- data/public/iceholidays-assets/images/china2.png +0 -0
- data/public/iceholidays-assets/images/chongqing.png +0 -0
- data/public/iceholidays-assets/images/guangzhou.png +0 -0
- data/public/iceholidays-assets/images/guilin.png +0 -0
- data/public/iceholidays-assets/images/harbin.png +0 -0
- data/public/iceholidays-assets/images/hongkong.png +0 -0
- data/public/iceholidays-assets/images/inner_mongolia.png +0 -0
- data/public/iceholidays-assets/images/jiangxi.png +0 -0
- data/public/iceholidays-assets/images/kenya.png +0 -0
- data/public/iceholidays-assets/images/kenya2.png +0 -0
- data/public/iceholidays-assets/images/kunming.png +0 -0
- data/public/iceholidays-assets/images/slikroad.png +0 -0
- data/public/iceholidays-assets/images/southafrica.png +0 -0
- data/public/iceholidays-assets/images/tanzania.png +0 -0
- data/public/iceholidays-assets/images/tour1.png +0 -0
- data/public/iceholidays-assets/images/uganda.png +0 -0
- /data/public/iceholidays-assets/images/{Group_71.png → about-us_logo.png} +0 -0
- /data/public/iceholidays-assets/images/{logo_container.png → logo.png} +0 -0
|
@@ -93,15 +93,30 @@ a{
|
|
|
93
93
|
color: $primary-color;
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
-
.ant-select-
|
|
97
|
-
|
|
96
|
+
.ant-select-dropdown .ant-select-item-option-active:not(.ant-select-item-option-disabled),
|
|
97
|
+
.ant-select-dropdown .ant-select-item-option-selected:not(.ant-select-item-option-disabled){
|
|
98
|
+
background-color: transparent;
|
|
99
|
+
color: $primary-color;
|
|
98
100
|
}
|
|
99
101
|
|
|
100
|
-
.ant-select-
|
|
101
|
-
color:
|
|
102
|
-
background-color: #FFFFFF;
|
|
102
|
+
.ant-select-dropdown .ant-select-item{
|
|
103
|
+
color: #00000080;
|
|
103
104
|
}
|
|
104
105
|
|
|
105
|
-
.ant-
|
|
106
|
-
|
|
106
|
+
.ant-menu-light{
|
|
107
|
+
&.ant-menu-horizontal{
|
|
108
|
+
|
|
109
|
+
&>.ant-menu-item:hover,
|
|
110
|
+
&>.ant-menu-item-selected,
|
|
111
|
+
&>.ant-menu-item-active{
|
|
112
|
+
|
|
113
|
+
&::after{
|
|
114
|
+
display: none;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.ant-menu-item-selected{
|
|
120
|
+
background-color: transparent;
|
|
121
|
+
}
|
|
107
122
|
}
|
|
@@ -17,22 +17,17 @@
|
|
|
17
17
|
color: #836848;
|
|
18
18
|
cursor: pointer;
|
|
19
19
|
text-align: center;
|
|
20
|
-
min-width: 150px;
|
|
21
20
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
// &.month{
|
|
27
|
-
// min-width: 150px;
|
|
28
|
-
// }
|
|
21
|
+
&.default-filter{
|
|
22
|
+
background: #F9E298;
|
|
23
|
+
}
|
|
29
24
|
|
|
30
25
|
span{
|
|
31
26
|
font-family: $font-default;
|
|
32
|
-
font-
|
|
33
|
-
font-
|
|
34
|
-
line-height:
|
|
35
|
-
letter-spacing:
|
|
27
|
+
font-weight: 500;
|
|
28
|
+
font-size: 14px;
|
|
29
|
+
line-height: 21px;
|
|
30
|
+
letter-spacing: 2%;
|
|
36
31
|
text-align: left;
|
|
37
32
|
text-underline-position: from-font;
|
|
38
33
|
text-decoration-skip-ink: none;
|
|
@@ -43,3 +38,15 @@
|
|
|
43
38
|
color: #FFFFFF;
|
|
44
39
|
}
|
|
45
40
|
}
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@media only screen and (min-width: 769px) {
|
|
44
|
+
.filter-pill{
|
|
45
|
+
span{
|
|
46
|
+
font-size: 18px;
|
|
47
|
+
font-weight: 400;
|
|
48
|
+
line-height: 27px;
|
|
49
|
+
letter-spacing: 0.05em;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import axios from "axios";
|
|
2
|
+
import { Agent } from "../interfaces/agent.interface";
|
|
3
|
+
|
|
4
|
+
class AgentsApi
|
|
5
|
+
{
|
|
6
|
+
|
|
7
|
+
getAgents(): Promise<Agent[]> {
|
|
8
|
+
const apiUrl = "/api/v1/users/agents";
|
|
9
|
+
|
|
10
|
+
return axios.get(apiUrl)
|
|
11
|
+
.then(response => {
|
|
12
|
+
const agents = response.data.agents.map(agentData => {
|
|
13
|
+
const agent:Agent = {
|
|
14
|
+
id: agentData.id,
|
|
15
|
+
name: agentData.full_name,
|
|
16
|
+
email: agentData.email,
|
|
17
|
+
image: agentData.image,
|
|
18
|
+
phone: agentData.phone,
|
|
19
|
+
whatsapp: agentData.whatsapp_no,
|
|
20
|
+
address: agentData.address,
|
|
21
|
+
city: agentData.city,
|
|
22
|
+
state: agentData.state,
|
|
23
|
+
}
|
|
24
|
+
return agent;
|
|
25
|
+
})
|
|
26
|
+
return Promise.resolve(agents);
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export default AgentsApi
|
|
33
|
+
;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import axios from "axios";
|
|
2
|
-
import { City, Country } from "../interfaces/country.interface";
|
|
2
|
+
import { City, Country, Location } from "../interfaces/country.interface";
|
|
3
3
|
|
|
4
4
|
class LocationsApi {
|
|
5
5
|
|
|
@@ -34,6 +34,28 @@ class LocationsApi {
|
|
|
34
34
|
});
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
+
getLocation(locationId:number): Promise<Location> {
|
|
38
|
+
const apiUrl = `/api/v1/locations/${locationId}`;
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
return axios.get(apiUrl)
|
|
42
|
+
.then(response => {
|
|
43
|
+
const locationData = response.data.data.attributes;
|
|
44
|
+
const location: Location = {
|
|
45
|
+
id: locationData.id,
|
|
46
|
+
cover: locationData.cover,
|
|
47
|
+
name: locationData.name,
|
|
48
|
+
ancestry: locationData.ancestors.map(a => {
|
|
49
|
+
return {
|
|
50
|
+
id: a.id,
|
|
51
|
+
name: a.name
|
|
52
|
+
}
|
|
53
|
+
})
|
|
54
|
+
}
|
|
55
|
+
return Promise.resolve(location);
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
37
59
|
private mapCity(cityData): City{
|
|
38
60
|
const city:City = {
|
|
39
61
|
id: cityData.id,
|
|
@@ -1,44 +1,64 @@
|
|
|
1
1
|
import axios from "axios";
|
|
2
2
|
import { Itinerary } from "../interfaces/itinerary.interface";
|
|
3
3
|
|
|
4
|
-
interface SearchParams {
|
|
5
|
-
keyword: string;
|
|
6
|
-
year?: string;
|
|
7
|
-
month?: string;
|
|
8
|
-
}
|
|
9
4
|
|
|
10
5
|
class SeriesApi {
|
|
11
6
|
|
|
12
|
-
getItineraries(
|
|
13
|
-
const
|
|
7
|
+
getItineraries(searchParamsObj): Promise<Itinerary[]> {
|
|
8
|
+
const searchParamsQuery = new URLSearchParams(searchParamsObj).toString()
|
|
9
|
+
const apiUrl = `/api/v1/series?${searchParamsQuery}&type=series`;
|
|
14
10
|
|
|
15
11
|
return axios.get(apiUrl)
|
|
16
12
|
.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
|
-
})
|
|
13
|
+
const itineraries:Itinerary[] = response.data.itineraries.map(itineraryData => this.mapItinerary(itineraryData));
|
|
38
14
|
return Promise.resolve(itineraries);
|
|
39
15
|
});
|
|
40
16
|
}
|
|
41
17
|
|
|
18
|
+
|
|
19
|
+
getItinerary(id): Promise<Itinerary>{
|
|
20
|
+
const apiUrl = `/api/v1/series/${id}`;
|
|
21
|
+
|
|
22
|
+
return axios.get(apiUrl)
|
|
23
|
+
.then(response => {
|
|
24
|
+
const itinerary = this.mapItinerary(response.data);
|
|
25
|
+
return Promise.resolve(itinerary);
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
private mapItinerary(itineraryData):Itinerary {
|
|
31
|
+
const itinerary:Itinerary = {
|
|
32
|
+
id: itineraryData.id,
|
|
33
|
+
description: itineraryData.description,
|
|
34
|
+
caption: itineraryData.caption,
|
|
35
|
+
otherCaption: itineraryData.otherCaption,
|
|
36
|
+
code: itineraryData.code,
|
|
37
|
+
category: itineraryData.category,
|
|
38
|
+
country: itineraryData.country,
|
|
39
|
+
price: itineraryData.price,
|
|
40
|
+
images: itineraryData.images,
|
|
41
|
+
coverImage: itineraryData.images[0],
|
|
42
|
+
priceCurrency: itineraryData.price_currency,
|
|
43
|
+
departureDate: itineraryData.departure_date,
|
|
44
|
+
guranteedDepartureDates: itineraryData.guranteed_departure_dates,
|
|
45
|
+
almostGuaranteedDepartureDates: itineraryData.almost_guaranteed_departure_dates,
|
|
46
|
+
departureLocations: itineraryData.departure_locations,
|
|
47
|
+
totalItineraries: itineraryData.total_itineraries,
|
|
48
|
+
fileUrl: itineraryData.file_url,
|
|
49
|
+
|
|
50
|
+
autoCancel: itineraryData.auto_cancel,
|
|
51
|
+
ccRate: itineraryData.cc_rate,
|
|
52
|
+
includings: itineraryData.includings,
|
|
53
|
+
maxBookingSeats: itineraryData.max_booking_seats,
|
|
54
|
+
remark: itineraryData.remark,
|
|
55
|
+
tourType: itineraryData.tour_type,
|
|
56
|
+
tours: itineraryData.tours,
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return itinerary;
|
|
60
|
+
}
|
|
61
|
+
|
|
42
62
|
}
|
|
43
63
|
|
|
44
64
|
export default SeriesApi;
|
|
@@ -2,17 +2,110 @@ export interface Itinerary {
|
|
|
2
2
|
id: number;
|
|
3
3
|
description: string;
|
|
4
4
|
code: string;
|
|
5
|
-
fileUrl?: string;
|
|
6
5
|
category: string;
|
|
7
6
|
caption: string;
|
|
8
7
|
otherCaption: string;
|
|
9
8
|
country: string;
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
9
|
+
fileUrl?: string;
|
|
10
|
+
price?: string;
|
|
11
|
+
departureDate?: string[];
|
|
12
|
+
guranteedDepartureDates?: string[];
|
|
13
|
+
almostGuaranteedDepartureDates?: string[];
|
|
14
|
+
departureLocations?: string[];
|
|
15
|
+
coverImage?:string[];
|
|
15
16
|
images: string[];
|
|
16
|
-
priceCurrency
|
|
17
|
-
totalItineraries
|
|
17
|
+
priceCurrency?: string;
|
|
18
|
+
totalItineraries?: number;
|
|
19
|
+
|
|
20
|
+
autoCancel?:boolean;
|
|
21
|
+
ccRate?: number;
|
|
22
|
+
includings?: ItineraryIncludings;
|
|
23
|
+
maxBookingSeats?: number;
|
|
24
|
+
remark?: string;
|
|
25
|
+
tourType?: string;
|
|
26
|
+
tours?: Tour[];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
interface ItineraryIncludings {
|
|
30
|
+
acf: boolean;
|
|
31
|
+
airport_taxes: boolean;
|
|
32
|
+
gratuities: boolean;
|
|
33
|
+
group_departure: boolean;
|
|
34
|
+
hotel: boolean;
|
|
35
|
+
luggage: boolean;
|
|
36
|
+
meal_onboard: boolean;
|
|
37
|
+
tour_leader: boolean;
|
|
38
|
+
wifi: boolean;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface Tour {
|
|
42
|
+
id:number;
|
|
43
|
+
addon_others:number;
|
|
44
|
+
allow_customer_pay: boolean;
|
|
45
|
+
arrival_date: string;
|
|
46
|
+
caption: string;
|
|
47
|
+
code: string;
|
|
48
|
+
compulsory_additional_fee: number;
|
|
49
|
+
credit_available: number;
|
|
50
|
+
customer_deposit: number;
|
|
51
|
+
day_before_departure: number;
|
|
52
|
+
departure_date: string;
|
|
53
|
+
departure_location: string;
|
|
54
|
+
deposit: number;
|
|
55
|
+
deposit_date: string;
|
|
56
|
+
dobw_available: any;
|
|
57
|
+
dobw_cor_available: any;
|
|
58
|
+
dobw_ret_available: any;
|
|
59
|
+
extra_deposit: number;
|
|
60
|
+
fare_type: string;
|
|
61
|
+
final_payment_date: string;
|
|
62
|
+
flights: any[];
|
|
63
|
+
fpxb2b_available:any;
|
|
64
|
+
fpxb2c_available:any;
|
|
65
|
+
guaranteed_departure: boolean;
|
|
66
|
+
guaranteed_indicator: string;
|
|
67
|
+
highlight: string;
|
|
68
|
+
insurance: string;
|
|
69
|
+
insurance_rebate: number;
|
|
70
|
+
latest_tour_confirmation: LatestTourConfirmation;
|
|
71
|
+
max_booking_seats: number;
|
|
72
|
+
min_dta: number;
|
|
73
|
+
pay_later: string;
|
|
74
|
+
pbb2_available: string;
|
|
75
|
+
pre_commisions: any[];
|
|
76
|
+
price: number;
|
|
77
|
+
prices: Price[];
|
|
78
|
+
red_luggage_protection: boolean;
|
|
79
|
+
single_supplement_price: string;
|
|
80
|
+
subtract_others: number;
|
|
81
|
+
visa_fee: number;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
interface LatestTourConfirmation {
|
|
85
|
+
assembly_time: string;
|
|
86
|
+
confirmation_emergency_contact: string;
|
|
87
|
+
hand_carry_weight: string;
|
|
88
|
+
luggage_quantity: string;
|
|
89
|
+
luggage_weight: string;
|
|
90
|
+
meet_up_point: string;
|
|
91
|
+
tour_guide_chinese_name: string;
|
|
92
|
+
tour_guide_name: string;
|
|
93
|
+
tour_guide_phone: string;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
interface Price {
|
|
97
|
+
display_price: string;
|
|
98
|
+
adult: number;
|
|
99
|
+
child_no_bed: number;
|
|
100
|
+
child_twin: number;
|
|
101
|
+
child_with_bed: number;
|
|
102
|
+
dta_adult: number;
|
|
103
|
+
dta_child_no_bed: number;
|
|
104
|
+
dta_child_twin: number;
|
|
105
|
+
dta_child_with_bed: number;
|
|
106
|
+
dta_infant: number;
|
|
107
|
+
dta_single_supplement: number;
|
|
108
|
+
infant: number;
|
|
109
|
+
single_supplement: number;
|
|
110
|
+
type: string;
|
|
18
111
|
}
|
|
@@ -18,7 +18,7 @@ export default function App() {
|
|
|
18
18
|
<Route path="/app" element={<MainLayout />}>
|
|
19
19
|
<Route index element={<Homepage />} />
|
|
20
20
|
<Route path="/app/listing" element={<ListingPage />} />
|
|
21
|
-
<Route path="/app/
|
|
21
|
+
<Route path="/app/itinerary/:id" element={<ShowPage />} />
|
|
22
22
|
<Route path="/app/about-us" element={<AboutUsPage />} />
|
|
23
23
|
<Route path="/app/countries" element={<CountriesPage />} />
|
|
24
24
|
<Route path="/app/blog" element={<BlogPage />} />
|
|
@@ -1,19 +1,20 @@
|
|
|
1
|
-
import { notification, Tabs, TabsProps } from "antd";
|
|
1
|
+
import { notification, Select, Tabs, TabsProps } from "antd";
|
|
2
2
|
import React from "react";
|
|
3
3
|
import LocationsApi from "../../api-services/locations-api.service";
|
|
4
4
|
import { Country } from "../../interfaces/country.interface";
|
|
5
|
-
import
|
|
6
|
-
import { mdiMenuRight } from "@mdi/js";
|
|
5
|
+
import { mdiFilterVariant, mdiMenuRight } from "@mdi/js";
|
|
7
6
|
import Icon from "@mdi/react";
|
|
8
|
-
import tour from "antd/es/tour";
|
|
9
7
|
import { Link } from "react-router-dom";
|
|
8
|
+
import LocationPostcards from "./shared/LocationPostcards";
|
|
9
|
+
import LocationDropdown from "./shared/LocationDropdown";
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
export default class Destinations extends React.Component {
|
|
13
13
|
api = new LocationsApi;
|
|
14
14
|
|
|
15
15
|
state = {
|
|
16
|
-
countries: []
|
|
16
|
+
countries: [],
|
|
17
|
+
activeTab: "1"
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
componentDidMount() {
|
|
@@ -30,30 +31,35 @@ export default class Destinations extends React.Component {
|
|
|
30
31
|
const pluralize = (count: number, noun: string, suffix = 's') =>
|
|
31
32
|
`${count} ${noun}${count > 1 ? suffix : ''}`;
|
|
32
33
|
|
|
33
|
-
const listingLink = (
|
|
34
|
-
return `/app/listing?
|
|
34
|
+
const listingLink = (locationId: number) => {
|
|
35
|
+
return `/app/listing?location_id=${locationId}`;
|
|
35
36
|
}
|
|
36
37
|
|
|
38
|
+
const changeTab = (selectedKey:string) => {
|
|
39
|
+
this.setState({activeTab:selectedKey.toString()})
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
|
|
37
43
|
var items:TabsProps["items"] = this.state.countries.map((country:Country, index)=>{
|
|
38
44
|
return {
|
|
39
|
-
key:
|
|
40
|
-
label:
|
|
45
|
+
key: country.id.toString(),
|
|
46
|
+
label: <>
|
|
47
|
+
{country.name}
|
|
48
|
+
</>,
|
|
41
49
|
children: (
|
|
42
50
|
<div>
|
|
43
|
-
<Link to={listingLink(country.
|
|
51
|
+
<Link to={listingLink(country.id)} className="country">
|
|
44
52
|
<img className="country-image" src={`${country.cover}`} />
|
|
45
53
|
<h1 className="country-name"> {country.name} </h1>
|
|
46
|
-
<
|
|
54
|
+
<picture className="bottom-logo">
|
|
55
|
+
<img src="/iceholidays-assets/images/destinations_logo.png"></img>
|
|
56
|
+
</picture>
|
|
47
57
|
</Link>
|
|
48
58
|
{
|
|
49
59
|
country.topCities?.length > 0 &&
|
|
50
60
|
<div className="top-cities">
|
|
51
61
|
<h2>Top Cities</h2>
|
|
52
|
-
<
|
|
53
|
-
{
|
|
54
|
-
country.topCities?.map((city, index)=><Postcard key={index} link={listingLink(city.name)} image={city.cover} name={city.name} tourCount={city.tourCount}/>)
|
|
55
|
-
}
|
|
56
|
-
</div>
|
|
62
|
+
<LocationPostcards locations={country.topCities}/>
|
|
57
63
|
</div>
|
|
58
64
|
}
|
|
59
65
|
{
|
|
@@ -63,7 +69,7 @@ export default class Destinations extends React.Component {
|
|
|
63
69
|
<div className="other-destinations">
|
|
64
70
|
{
|
|
65
71
|
country.tags?.map((city, index)=>{
|
|
66
|
-
return <Link key={index} to={listingLink(city.
|
|
72
|
+
return <Link key={index} to={listingLink(city.id)}> {city.name} </Link>
|
|
67
73
|
})
|
|
68
74
|
}
|
|
69
75
|
</div>
|
|
@@ -75,11 +81,11 @@ export default class Destinations extends React.Component {
|
|
|
75
81
|
<div className="highlights">
|
|
76
82
|
{
|
|
77
83
|
country.highlights?.map((city, index)=>{
|
|
78
|
-
return <Link key={index} to={listingLink(city.
|
|
84
|
+
return <Link key={index} to={listingLink(city.id)}>
|
|
79
85
|
<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
86
|
<div className="details">
|
|
81
87
|
<span className="city-name"> {city.name} </span>
|
|
82
|
-
<span className="tour-count"> {pluralize(city.tourCount, "tour")} <Icon path={mdiMenuRight} size={1} /> </span>
|
|
88
|
+
<span className="tour-count"> {pluralize(city.tourCount || 0, "tour")} <Icon path={mdiMenuRight} size={1} /> </span>
|
|
83
89
|
</div>
|
|
84
90
|
</div>
|
|
85
91
|
</Link>
|
|
@@ -96,10 +102,14 @@ export default class Destinations extends React.Component {
|
|
|
96
102
|
<div id="popular-destinations">
|
|
97
103
|
<h1>Explore Popular Destination</h1>
|
|
98
104
|
|
|
105
|
+
<LocationDropdown locations={this.state.countries} selectLocation={changeTab}/>
|
|
106
|
+
|
|
99
107
|
<Tabs
|
|
100
108
|
defaultActiveKey="1"
|
|
109
|
+
activeKey={this.state.activeTab}
|
|
101
110
|
items={items}
|
|
102
|
-
type="card"
|
|
111
|
+
type="card"
|
|
112
|
+
onChange={changeTab}/>
|
|
103
113
|
|
|
104
114
|
</div>
|
|
105
115
|
)
|