iceholidays-frontend 0.4.0 → 0.5.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 +961 -355
- data/app/assets/stylesheets/iceholidays/frontend/common.scss +156 -72
- 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 +6 -6
- 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 +192 -146
- data/app/javascript/react/pages/ShowPage.tsx +269 -264
- 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 +1190 -425
- data/public/iceholidays-assets/application.js +91 -104
- data/public/iceholidays-assets/application.js.map +4 -4
- 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 +18 -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
|
@@ -1,120 +1,36 @@
|
|
|
1
1
|
import React, { useState } from "react";
|
|
2
|
-
import { Button, Carousel, Col, Flex, Form, Layout, Modal, Row, Select } from "antd";
|
|
2
|
+
import { Button, Carousel, Col, Flex, Form, Layout, Modal, notification, Row, Select, Skeleton, Space } from "antd";
|
|
3
3
|
import SlickButtonFix from "../components/shared/SlickButtonFix";
|
|
4
|
-
import { mdiAccountCash, mdiAccountGroup, mdiAirplane, mdiBagChecked, mdiBedKing, mdiEmailOutline, mdiFileDownload, mdiFlagTriangle, mdiMapMarker, mdiMapMarkerOutline, mdiMenuLeft, mdiMenuRight, mdiPhoneInTalkOutline, mdiReceiptText, mdiShieldAccount, mdiShieldAirplane, mdiSilverwareForkKnife, mdiWhatsapp } from "@mdi/js";
|
|
4
|
+
import { mdiAccountCash, mdiAccountGroup, mdiAirplane, mdiBagChecked, mdiBedKing, mdiClose, mdiEmailOutline, mdiFileDownload, mdiFlagTriangle, mdiMapMarker, mdiMapMarkerOutline, mdiMenuDown, mdiMenuLeft, mdiMenuRight, mdiMenuUp, mdiPhoneInTalkOutline, mdiReceiptText, mdiShieldAccount, mdiShieldAirplane, mdiSilverwareForkKnife, mdiWhatsapp } from "@mdi/js";
|
|
5
5
|
import Icon from "@mdi/react";
|
|
6
6
|
import Headline from "../components/shared/Headline";
|
|
7
7
|
import { Content } from "antd/es/layout/layout";
|
|
8
8
|
import Sider from "antd/es/layout/Sider";
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
description: `
|
|
17
|
-
**FREE COVID-19 TRAVEL INSURANCE!!**
|
|
18
|
-
*\*\*Applicable for passenger up to 69 years old (T&C APPLIED)*
|
|
19
|
-
|
|
20
|
-
> All non Malaysia passport holder surcharge RM300 per person!! Except Singapore passport holder.
|
|
21
|
-
|
|
22
|
-
**Special Arrangement:**
|
|
23
|
-
**✓** Essential for traveling abroad!!️ Keeps you in touch with your family at all times - **Travel Sim Card per person per card**.
|
|
24
|
-
**✓** Bottle Mineral Water per person per day.
|
|
25
|
-
**✓**2+1 Luxury VIP Air-conditioned Coach.
|
|
26
|
-
|
|
27
|
-
**Itinerary Highlight:**
|
|
28
|
-
**✓** **Liberation Monument Street** is a great place for locals to relax and hang out on weekends and was also a key location for the movie "I Belonged to You".
|
|
29
|
-
**✓** **Hongyadong** is the most popular attraction in Chongqing and is the number one must-visit attraction in Chongqing because of its peculiar architectural appearance and spectacular night view. Here is not a cave, but a most Bayu traditional characteristics of the foot-hanging building, the whole building is built according to the cliff, has more than 2000 years of history.
|
|
30
|
-
**✓** The remodeled and renovated old town of **Ciqikou** boasts a large number of gourmet and handicraft stores. In addition to the old winding stone road steps, the real life of the old generation of Chongqing people is also shown here.
|
|
31
|
-
**✓** Standing on the **Kaibu Yizhi Park** platform, you will be greeted by the breeze of the river. Drink Ba Ba Tea and taste pastries at the open-air tea houses along the river to experience the leisurely life of Chongqing people.
|
|
32
|
-
**✓** **Xijiang Qianhu Miao Village** it consists of more than ten natural villages built on the hillside, which is the largest Miao settlement in China and even in the world. On the hillside, there are layers and layers of Miao wooden footstools, coffee-colored old houses clinging to the turquoise mountains, which are very characteristic of the nationalities. It is a good place to experience the Miao flavor!
|
|
33
|
-
**✓** As a world-class business card of Guizhou, **Huangguoshu Waterfall** is one of the must-see attractions in Guizhou. The most essential ones are mainly the **Great Falls**, **Tianxingqiao Scenic Area** and **Dou Po Tang waterfall**. The scenic spots of Huangguoshu Falls, such as Shuilian Cave and Dou Po Tang waterfall, were the setting for the 86 version of the TV series \. After viewing the spectacular Huangguoshu Falls in the daytime, when night falls, immerse yourself in a **large-scale light show** staged between the mountains and the water, and feel a different travel experience from daytime play.
|
|
34
|
-
**✓** **Maling River Canyon** is known as "the most beautiful scar on earth". Its **Tian Xing Gallery** is the core part of the essence of the canyon scenic area, which is characterized by the magnificent scale of waterfalls and rock page wall hangings forming the main landscape.
|
|
35
|
-
**✓** **Guiyang netizens hot spots:** Huaxi Shi Zhi Street, Qingyun marketplace, Jai Xiu Pavilion, Hua guo yuan bai gong. **Local Cuisine:**Sichuan Cuisine,Chongqing Jianghu Cuisine, Mala Hot Pot, Long Table Feast of Miao, Sour Soup Fish Flavor, Yao Flavor, Buyi Fortune Feast, Qian Flavor
|
|
36
|
-
|
|
37
|
-
**All 5★ Hotel:*****\*\*Subject to change in the event of conflicting conferences or events.\*\****
|
|
38
|
-
**✓** 2 Nights at Chongqing Hotel
|
|
39
|
-
**✓** 1 Nights at KailiHotel
|
|
40
|
-
**✓** 1 Night at Anshun Hotel
|
|
41
|
-
**✓** 1 Night at XingyiHotel
|
|
42
|
-
**✓** 2 Nights at GuiyangHotel
|
|
43
|
-
**Note: Triple Room Sharing Will Be Base on Extra Roll in Bed Basis!**
|
|
44
|
-
`
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const agentSearchResults = [
|
|
48
|
-
{
|
|
49
|
-
company: "HO WAH GENTING HOLIDAY SDN BHD",
|
|
50
|
-
logo: "/iceholidays-assets/images/hw_logo.png",
|
|
51
|
-
contactNumbers: [ "+6018 982 6422" ],
|
|
52
|
-
address: "Ground Floor No. 31 & 33, Jalan Maharajalela, 50150 Kuala Lumpur",
|
|
53
|
-
phone: "+603 2141 6422",
|
|
54
|
-
whatsapp: "+6018 982 6422",
|
|
55
|
-
email: "enquiries@hwgholidays.com"
|
|
56
|
-
},
|
|
57
|
-
{
|
|
58
|
-
company: "CHANG HONG TRAVEL & TOUR SDN BHD",
|
|
59
|
-
contactNumbers: [ "+6012 608 0287", "+6018 669 3238" ]
|
|
60
|
-
|
|
61
|
-
}
|
|
62
|
-
]
|
|
9
|
+
import SeriesApi from "../../api-services/series-api.service";
|
|
10
|
+
import { useParams } from "react-router-dom";
|
|
11
|
+
import { Itinerary } from "../../interfaces/itinerary.interface";
|
|
12
|
+
import AgentsApi from "../../api-services/agents-api.service";
|
|
13
|
+
import { Agent } from "../../interfaces/agent.interface";
|
|
14
|
+
import PriceDetails from "../components/PriceDetails";
|
|
15
|
+
import ContactAgentsForm from "../components/shared/ContactAgentsForm";
|
|
63
16
|
|
|
64
17
|
const breadcrumbs = [
|
|
65
18
|
{ title: 'Home' },
|
|
66
19
|
]
|
|
67
20
|
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
{
|
|
82
|
-
date: "23/10/24",
|
|
83
|
-
price: "RM 8,288"
|
|
84
|
-
},
|
|
85
|
-
{
|
|
86
|
-
date: "23/10/24",
|
|
87
|
-
price: "RM 8,288"
|
|
88
|
-
},
|
|
89
|
-
{
|
|
90
|
-
date: "23/10/24",
|
|
91
|
-
price: "RM 8,288"
|
|
92
|
-
},
|
|
93
|
-
{
|
|
94
|
-
date: "23/10/24",
|
|
95
|
-
price: "RM 8,288"
|
|
96
|
-
},
|
|
97
|
-
{
|
|
98
|
-
date: "23/10/24",
|
|
99
|
-
price: "RM 8,288"
|
|
100
|
-
},
|
|
101
|
-
{
|
|
102
|
-
date: "23/10/24",
|
|
103
|
-
price: "RM 8,288"
|
|
104
|
-
},
|
|
105
|
-
]
|
|
106
|
-
|
|
107
|
-
const thingsToKnow = [
|
|
108
|
-
{icon: <Icon path={mdiShieldAccount} size={1} />, label: "Free Travel Insurance"},
|
|
109
|
-
{icon: <Icon path={mdiReceiptText} size={1} /> , label: "Airport Taxes"},
|
|
110
|
-
{icon: <Icon path={mdiFlagTriangle} size={1} />, label: "Tour Leader"},
|
|
111
|
-
{icon: <Icon path={mdiBagChecked} size={1} />, label: "Check-in Baggage"},
|
|
112
|
-
{icon: <Icon path={mdiSilverwareForkKnife} size={1} />, label: "Meal Onboard"},
|
|
113
|
-
{icon: <Icon path={mdiBedKing} size={1} />, label: "Hotel"},
|
|
114
|
-
{icon: <Icon path={mdiAccountCash} size={1} />, label: "Gratuities"},
|
|
115
|
-
{icon: <Icon path={mdiAccountGroup} size={1} />, label: "Group Departure"},
|
|
116
|
-
{icon: <Icon path={mdiFileDownload} size={1} />, label: "Itinerary Download EN | CN"}
|
|
117
|
-
]
|
|
21
|
+
const thingsToKnow = (inclusionsData) => {
|
|
22
|
+
return [
|
|
23
|
+
{icon: <Icon path={mdiShieldAccount} size={1} />, label: "Free Travel Insurance", visible: inclusionsData.acf},
|
|
24
|
+
{icon: <Icon path={mdiReceiptText} size={1} /> , label: "Airport Taxes", visible: inclusionsData.airport_taxes},
|
|
25
|
+
{icon: <Icon path={mdiFlagTriangle} size={1} />, label: "Tour Leader", visible: inclusionsData.tour_leader},
|
|
26
|
+
{icon: <Icon path={mdiBagChecked} size={1} />, label: "Check-in Baggage", visible: inclusionsData.luggage},
|
|
27
|
+
{icon: <Icon path={mdiSilverwareForkKnife} size={1} />, label: "Meal Onboard", visible: inclusionsData.meal_onboard},
|
|
28
|
+
{icon: <Icon path={mdiBedKing} size={1} />, label: "Hotel", visible: inclusionsData.hotel},
|
|
29
|
+
{icon: <Icon path={mdiAccountCash} size={1} />, label: "Gratuities", visible: inclusionsData.gratuities},
|
|
30
|
+
{icon: <Icon path={mdiAccountGroup} size={1} />, label: "Group Departure", visible: inclusionsData.group_departure},
|
|
31
|
+
{icon: <Icon path={mdiFileDownload} size={1} />, label: "Itinerary Download EN | CN"}
|
|
32
|
+
];
|
|
33
|
+
}
|
|
118
34
|
|
|
119
35
|
const layoutStyle = {
|
|
120
36
|
overflow: 'hidden',
|
|
@@ -125,20 +41,66 @@ const siderStyle = {
|
|
|
125
41
|
}
|
|
126
42
|
|
|
127
43
|
|
|
128
|
-
|
|
44
|
+
function withParams(Component) {
|
|
45
|
+
return props => <Component {...props} params={useParams()} />;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
class ShowPage extends React.Component <{params;}> {
|
|
49
|
+
seriesApi = new SeriesApi;
|
|
50
|
+
agentsApi = new AgentsApi;
|
|
129
51
|
|
|
130
52
|
state = {
|
|
131
|
-
|
|
53
|
+
loading: true,
|
|
54
|
+
itinerary: {includings:{}, tours:[], priceCurrency:"", country: "", description: "", coverImage: ""},
|
|
55
|
+
selectedTour:{id: null,guaranteed_departure:false, flights:[], caption:"", code:"", prices: []},
|
|
132
56
|
setIsModalOpen: false,
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
57
|
+
loadingAgents: true,
|
|
58
|
+
allAgents: [],
|
|
59
|
+
agents: [],
|
|
60
|
+
states: [],
|
|
61
|
+
cities: [],
|
|
62
|
+
showAgentsResults: false,
|
|
63
|
+
agentData: { name: "", image: "", address: "", phone: "", whatsapp: "", email: "", city: "", state: "" },
|
|
64
|
+
isCollapsed: true,
|
|
65
|
+
showContactAgent: false
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
componentDidMount() {
|
|
70
|
+
this.seriesApi.getItinerary(this.props.params.id)
|
|
71
|
+
.then((itineraryData:Itinerary) => {
|
|
72
|
+
this.setState({itinerary: itineraryData});
|
|
73
|
+
if(itineraryData.tours && itineraryData.tours?.length > 0){
|
|
74
|
+
this.setState({selectedTour: itineraryData.tours[0]});
|
|
75
|
+
}
|
|
76
|
+
})
|
|
77
|
+
.finally(()=>this.setState({loading: false}))
|
|
78
|
+
.catch(error => {
|
|
79
|
+
notification.error({ message: 'An error occured while loading countries.'});
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
this.getAgents();
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
getAgents(){
|
|
87
|
+
this.agentsApi.getAgents()
|
|
88
|
+
.then((agentsData:Agent[]) => {
|
|
89
|
+
const states = agentsData.filter(a => a.state != null).map(a => a.state);
|
|
90
|
+
const cities = agentsData.filter(a => a.city != null).map(a => a.city);
|
|
91
|
+
this.setState({agents: agentsData, allAgents: agentsData, loadingAgents:false, states, cities});
|
|
92
|
+
})
|
|
93
|
+
.finally(()=>this.setState({loading: false}))
|
|
94
|
+
.catch(error => {
|
|
95
|
+
notification.error({ message: 'An error occured while loading agents.'});
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
selectTour(tourId:number){
|
|
100
|
+
const selectedTour:any = this.state.itinerary.tours.find((tour:any) => tour.id == tourId);
|
|
101
|
+
if(selectedTour){
|
|
102
|
+
this.setState({selectedTour});
|
|
103
|
+
}
|
|
142
104
|
}
|
|
143
105
|
|
|
144
106
|
showModal(agentData){
|
|
@@ -149,20 +111,79 @@ export default class ShowPage extends React.Component {
|
|
|
149
111
|
this.setState({setIsModalOpen: false});
|
|
150
112
|
};
|
|
151
113
|
|
|
114
|
+
|
|
115
|
+
filterCities = (searchStr) => {
|
|
116
|
+
const allAgents:Agent[] = this.state.allAgents;
|
|
117
|
+
|
|
118
|
+
if(searchStr == undefined){
|
|
119
|
+
this.resetAgents();
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
const agents:Agent[] = allAgents.filter(a => a.state == searchStr);
|
|
123
|
+
const results = agents.map(a => a.city);
|
|
124
|
+
this.setState({cities: results});
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
filterStates= (searchStr) => {
|
|
129
|
+
const allAgents:Agent[] = this.state.allAgents;
|
|
130
|
+
|
|
131
|
+
if(searchStr == undefined){
|
|
132
|
+
this.resetAgents();
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const agents:Agent[] = allAgents.filter(a => a.city == searchStr);
|
|
137
|
+
const results = agents.map(a => a.state);
|
|
138
|
+
this.setState({states: results});
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
resetAgents(){
|
|
142
|
+
const allAgents:Agent[] = this.state.allAgents;
|
|
143
|
+
const states = allAgents.filter(a => a.state != null).map(a => a.state);
|
|
144
|
+
const cities = allAgents.filter(a => a.city != null).map(a => a.city);
|
|
145
|
+
this.setState({states, cities});
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
findAgents = (formValues) => {
|
|
149
|
+
const allAgents:Agent[] = this.state.allAgents;
|
|
150
|
+
const agents = allAgents.filter(a =>
|
|
151
|
+
(formValues.state == undefined || a.state === formValues.state) &&
|
|
152
|
+
(formValues.city == undefined || a.city === formValues.city));
|
|
153
|
+
this.setState({agents, showAgentsResults: true})
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
toggleDescription = () => {
|
|
158
|
+
const collapsed = this.state.isCollapsed;
|
|
159
|
+
this.setState({isCollapsed: !collapsed});
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
toggleContactAgent = () => {
|
|
163
|
+
const collapsed = this.state.showContactAgent;
|
|
164
|
+
this.setState({showContactAgent: !collapsed});
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
|
|
152
168
|
render(){
|
|
169
|
+
const {loading, itinerary, selectedTour, agents, states, cities, agentData, isCollapsed, showContactAgent} = this.state;
|
|
170
|
+
const dates = itinerary.tours.length == 0 ? [] : itinerary.tours.map((tour:any) => {
|
|
171
|
+
return {id: tour.id, date: tour.departure_date, price: `${itinerary.priceCurrency} ${tour.price}`}
|
|
172
|
+
});
|
|
173
|
+
|
|
153
174
|
return (
|
|
154
175
|
<div id="show-page">
|
|
155
176
|
|
|
156
177
|
<Layout style={layoutStyle}>
|
|
157
178
|
|
|
158
179
|
<>
|
|
159
|
-
<Headline bannerImage={
|
|
180
|
+
<Headline showSearch={false} bannerImage={itinerary.coverImage} breadcrumbs={breadcrumbs}>
|
|
160
181
|
<div id="show-page_header">
|
|
161
182
|
<Flex justify="space-between" vertical gap={45}>
|
|
162
|
-
<h1>{
|
|
183
|
+
<h1>{selectedTour?.caption}</h1>
|
|
163
184
|
<div className="tour_details">
|
|
164
|
-
<span className="tour_details_country"> <Icon path={mdiMapMarkerOutline} size="16px" /> {
|
|
165
|
-
<span className="tour_details_code"> <Icon path={mdiFileDownload} size="16px" /> {
|
|
185
|
+
<span className="tour_details_country"> <Icon path={mdiMapMarkerOutline} size="16px" /> {itinerary?.country} </span>
|
|
186
|
+
<span className="tour_details_code"> <Icon path={mdiFileDownload} size="16px" /> {selectedTour?.code} </span>
|
|
166
187
|
</div>
|
|
167
188
|
</Flex>
|
|
168
189
|
</div>
|
|
@@ -181,13 +202,13 @@ export default class ShowPage extends React.Component {
|
|
|
181
202
|
</SlickButtonFix>
|
|
182
203
|
}>
|
|
183
204
|
{
|
|
184
|
-
dates.map((
|
|
185
|
-
return <div key={index} className=
|
|
205
|
+
dates.map((tour, index) => {
|
|
206
|
+
return <div key={index} className={`date-box ${selectedTour?.id == tour.id && "selected"}`} onClick={()=>this.selectTour(tour.id)}>
|
|
186
207
|
<Flex justify="space-between" align="center" vertical gap={8}>
|
|
187
|
-
<div className="date-box_date"> {
|
|
208
|
+
<div className="date-box_date"> {tour.date} </div>
|
|
188
209
|
<div>
|
|
189
210
|
<span>From</span>
|
|
190
|
-
<div className="date-box_price"> {
|
|
211
|
+
<div className="date-box_price"> {tour.price} </div>
|
|
191
212
|
</div>
|
|
192
213
|
</Flex>
|
|
193
214
|
</div>
|
|
@@ -199,156 +220,138 @@ export default class ShowPage extends React.Component {
|
|
|
199
220
|
|
|
200
221
|
<Layout id="show-page_body">
|
|
201
222
|
<Content>
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
<
|
|
205
|
-
<
|
|
206
|
-
<
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
223
|
+
{
|
|
224
|
+
loading ? (
|
|
225
|
+
<Space direction="vertical" style={{width: '100%'}}>
|
|
226
|
+
<Skeleton/>
|
|
227
|
+
<Skeleton/>
|
|
228
|
+
<Skeleton/>
|
|
229
|
+
</Space>
|
|
230
|
+
) : (
|
|
231
|
+
<div className="details">
|
|
232
|
+
<Flex gap={10} vertical>
|
|
233
|
+
<div className="details-container">
|
|
234
|
+
<div className="details-container_header"> Things to know </div>
|
|
235
|
+
<div className="details-container_content">
|
|
236
|
+
<div id="things-to-know">
|
|
237
|
+
{
|
|
238
|
+
thingsToKnow(itinerary.includings)
|
|
239
|
+
.filter(item => item.visible)
|
|
240
|
+
.map(item => (
|
|
241
|
+
<div className="item">
|
|
242
|
+
<div className="icon"> {item.icon} </div>
|
|
243
|
+
<label>{item.label}</label>
|
|
244
|
+
</div>
|
|
245
|
+
))
|
|
246
|
+
}
|
|
247
|
+
{
|
|
248
|
+
selectedTour?.guaranteed_departure && (
|
|
249
|
+
<div className="item guaranteed">
|
|
250
|
+
<div className="icon"> <Icon path={mdiShieldAirplane} size={1} /></div>
|
|
251
|
+
<label>Guaranteed Departure</label>
|
|
252
|
+
</div>
|
|
253
|
+
)
|
|
254
|
+
}
|
|
255
|
+
</div>
|
|
219
256
|
</div>
|
|
220
257
|
</div>
|
|
221
|
-
</div>
|
|
222
|
-
</div>
|
|
223
258
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
259
|
+
<div className="details-container">
|
|
260
|
+
<div className="details-container_header"> Description </div>
|
|
261
|
+
<div className="details-container_content">
|
|
262
|
+
<div id="description" className={`pre-wrap ${isCollapsed && 'collapsed'}`}>{itinerary?.description}</div>
|
|
263
|
+
<Button className="toggle-description" color="default" variant="filled" block onClick={this.toggleDescription}>{ isCollapsed ? 'Expand' : 'Collapse'}
|
|
264
|
+
<Icon path={isCollapsed ? mdiMenuDown : mdiMenuUp} size="19px" />
|
|
265
|
+
</Button>
|
|
266
|
+
</div>
|
|
267
|
+
</div>
|
|
230
268
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
269
|
+
{
|
|
270
|
+
selectedTour?.flights.length > 0 && (
|
|
271
|
+
<div className="details-container">
|
|
272
|
+
<div className="details-container_header"> Schedule Flight(s) </div>
|
|
273
|
+
<div className="details-container_content">
|
|
274
|
+
<Flex gap={20} vertical>
|
|
275
|
+
<div className="schedule">
|
|
276
|
+
<div className="flight-date">
|
|
277
|
+
<Icon path={mdiAirplane} size="12px" />
|
|
278
|
+
<span>23/10/2024</span>
|
|
279
|
+
</div>
|
|
280
|
+
<div className="flight-details">
|
|
281
|
+
<label> Kuala Lumpur (KUL) > Guangzhou (CAN) </label>
|
|
282
|
+
<div className="flight-details_airline">
|
|
283
|
+
<img src="/iceholidays-assets/images/china_southern_airlines_icon.png" />
|
|
284
|
+
<span>China Southern Airlines CZ366</span>
|
|
285
|
+
</div>
|
|
286
|
+
</div>
|
|
287
|
+
<div className="flight-time">
|
|
288
|
+
<Flex gap={13}>
|
|
289
|
+
<div className="from">
|
|
290
|
+
<label>08:55</label>
|
|
291
|
+
<span>Kuala Lumpur (KUL)</span>
|
|
292
|
+
</div>
|
|
293
|
+
<div className="flight-time_icon">
|
|
294
|
+
<img src="/iceholidays-assets/images/plane.png" />
|
|
295
|
+
</div>
|
|
296
|
+
<div className="to">
|
|
297
|
+
<label>12:55</label>
|
|
298
|
+
<span>Guangzhou (CAN)</span>
|
|
299
|
+
</div>
|
|
300
|
+
</Flex>
|
|
301
|
+
</div>
|
|
302
|
+
</div>
|
|
303
|
+
</Flex>
|
|
245
304
|
</div>
|
|
246
305
|
</div>
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
<span>Kuala Lumpur (KUL)</span>
|
|
252
|
-
</div>
|
|
253
|
-
<div className="flight-time_icon">
|
|
254
|
-
<img src="/iceholidays-assets/images/plane.png" />
|
|
255
|
-
</div>
|
|
256
|
-
<div className="to">
|
|
257
|
-
<label>12:55</label>
|
|
258
|
-
<span>Guangzhou (CAN)</span>
|
|
259
|
-
</div>
|
|
260
|
-
</Flex>
|
|
261
|
-
</div>
|
|
262
|
-
</div>
|
|
263
|
-
</Flex>
|
|
264
|
-
</div>
|
|
265
|
-
</div>
|
|
306
|
+
)
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
<PriceDetails priceCurrency={itinerary.priceCurrency} prices={selectedTour?.prices}/>
|
|
266
310
|
|
|
267
|
-
|
|
268
|
-
<div className="details-container_header"> Price Details </div>
|
|
269
|
-
<div className="details-container_content">
|
|
270
|
-
<table id="pricing-table">
|
|
271
|
-
<thead>
|
|
272
|
-
<tr>
|
|
273
|
-
<th></th>
|
|
274
|
-
<th className="super-promo">1st-4th Pax
|
|
275
|
-
Super Promo</th>
|
|
276
|
-
<th className="promo">5th-6th Pax
|
|
277
|
-
Promo</th>
|
|
278
|
-
<th className="promo">Promo</th>
|
|
279
|
-
<th className="promo">Promo</th>
|
|
280
|
-
<th className="normal">Normal</th>
|
|
281
|
-
</tr>
|
|
282
|
-
</thead>
|
|
283
|
-
<tbody>
|
|
284
|
-
<tr>
|
|
285
|
-
<td>Adult</td>
|
|
286
|
-
<td className="super-promo">MYR 8,288</td>
|
|
287
|
-
<td className="promo">MYR 8,288</td>
|
|
288
|
-
<td className="promo">MYR 8,288</td>
|
|
289
|
-
<td className="promo">MYR 8,288</td>
|
|
290
|
-
<td className="normal">MYR 8,288</td>
|
|
291
|
-
</tr>
|
|
292
|
-
<tr>
|
|
293
|
-
<td>Child with extra bed</td>
|
|
294
|
-
<td className="super-promo">MYR 8,288</td>
|
|
295
|
-
<td className="promo">MYR 8,288</td>
|
|
296
|
-
<td className="promo">MYR 8,288</td>
|
|
297
|
-
<td className="promo">MYR 8,288</td>
|
|
298
|
-
<td className="normal">MYR 8,288</td>
|
|
299
|
-
</tr>
|
|
300
|
-
</tbody>
|
|
301
|
-
</table>
|
|
302
|
-
</div>
|
|
311
|
+
</Flex>
|
|
303
312
|
</div>
|
|
304
|
-
|
|
305
|
-
|
|
313
|
+
)
|
|
314
|
+
}
|
|
306
315
|
</Content>
|
|
307
|
-
<Sider width="265px" style={siderStyle}>
|
|
316
|
+
<Sider width="265px" style={siderStyle} className={`contact-agent-sider ${showContactAgent && 'show-contact-agent-mobile'}`}>
|
|
317
|
+
<div className="toggle_contact-agent" onClick={this.toggleContactAgent}>
|
|
318
|
+
<span>Contact Agent</span>
|
|
319
|
+
</div>
|
|
308
320
|
<div className="contact-agent">
|
|
309
|
-
<div className="contact-agent_header">
|
|
321
|
+
<div className="contact-agent_header">
|
|
322
|
+
<span>Contact Agent</span>
|
|
323
|
+
<Button className="collapse-contact-agent" color="default" variant="text"
|
|
324
|
+
icon={<Icon path={mdiClose} size="19px" />}
|
|
325
|
+
onClick={this.toggleContactAgent}/>
|
|
326
|
+
</div>
|
|
310
327
|
<div className="contact-agent_content">
|
|
311
|
-
<
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
<Form.Item>
|
|
319
|
-
<Button type="primary" block>Find Agent</Button>
|
|
320
|
-
</Form.Item>
|
|
321
|
-
</Form>
|
|
328
|
+
<ContactAgentsForm
|
|
329
|
+
states={states}
|
|
330
|
+
cities={cities}
|
|
331
|
+
findAgents={this.findAgents}
|
|
332
|
+
filterCities={this.filterCities}
|
|
333
|
+
filterStates={this.filterStates}/>
|
|
334
|
+
|
|
322
335
|
{
|
|
323
|
-
this.state.
|
|
336
|
+
this.state.showAgentsResults && (
|
|
324
337
|
<div className="search-results">
|
|
325
338
|
<Flex vertical gap={20}>
|
|
326
339
|
{
|
|
327
|
-
|
|
340
|
+
agents.length > 0 && agents.map((agent:Agent) => {
|
|
328
341
|
return (
|
|
329
342
|
<Row gutter={2} justify="space-between" className="agent-info">
|
|
330
|
-
<Col flex="180px"><span className="company" onClick={()=>this.showModal(
|
|
331
|
-
{
|
|
343
|
+
<Col flex="180px"><span className="company" onClick={()=>this.showModal(agent)}>
|
|
344
|
+
{agent.name}</span>
|
|
332
345
|
</Col>
|
|
333
346
|
<Col flex="none"><Button color="default" variant="filled" shape="circle" size="small" icon={<Icon path={mdiPhoneInTalkOutline} size="16px" />}/></Col>
|
|
334
347
|
<Col flex="none"><Button color="default" variant="filled" shape="circle" size="small" icon={<Icon path={mdiEmailOutline} size="16px" />}/></Col>
|
|
335
348
|
<Col>
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
<
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
<Button size="small" color="primary" variant="outlined">
|
|
343
|
-
<Icon path={mdiWhatsapp} size="12px" />
|
|
344
|
-
{contactNumber}
|
|
345
|
-
</Button>
|
|
346
|
-
)
|
|
347
|
-
})
|
|
348
|
-
}
|
|
349
|
-
</div>
|
|
350
|
-
)
|
|
351
|
-
}
|
|
349
|
+
<div className="contact-numbers">
|
|
350
|
+
<Button size="small" color="primary" variant="outlined">
|
|
351
|
+
<Icon path={mdiWhatsapp} size="12px" />
|
|
352
|
+
{agent.whatsapp}
|
|
353
|
+
</Button>
|
|
354
|
+
</div>
|
|
352
355
|
</Col>
|
|
353
356
|
</Row>
|
|
354
357
|
)
|
|
@@ -365,27 +368,27 @@ export default class ShowPage extends React.Component {
|
|
|
365
368
|
</Layout>
|
|
366
369
|
|
|
367
370
|
|
|
368
|
-
<Modal title={<img src={
|
|
369
|
-
<h2> {
|
|
371
|
+
<Modal title={<img src={agentData?.image}/>} open={this.state.setIsModalOpen} onCancel={()=>this.closeModal()} footer={null} width={325} centered className="agent-full-contact-details">
|
|
372
|
+
<h2> {agentData?.name} </h2>
|
|
370
373
|
<div className="contact-details">
|
|
371
374
|
<Row gutter={[15, 15]} align="middle">
|
|
372
375
|
<Col span={24}>
|
|
373
376
|
<Icon path={mdiMapMarker} size="15px" />
|
|
374
|
-
<span>{
|
|
377
|
+
<span>{agentData?.address}</span>
|
|
375
378
|
</Col>
|
|
376
379
|
{/* </Row>
|
|
377
380
|
<Row gutter={15} align="middle"> */}
|
|
378
|
-
<Col>
|
|
381
|
+
<Col xs={24} sm={24} lg={6}>
|
|
379
382
|
<Icon path={mdiPhoneInTalkOutline} size="15px" />
|
|
380
|
-
<span>{
|
|
383
|
+
<span>{agentData?.phone}</span>
|
|
381
384
|
</Col>
|
|
382
|
-
<Col>
|
|
385
|
+
<Col xs={24} sm={24} lg={6}>
|
|
383
386
|
<Icon path={mdiWhatsapp} size="15px" />
|
|
384
|
-
<span>{
|
|
387
|
+
<span>{agentData?.whatsapp}</span>
|
|
385
388
|
</Col>
|
|
386
|
-
<Col>
|
|
389
|
+
<Col xs={24} sm={24} lg={9}>
|
|
387
390
|
<Icon path={mdiEmailOutline} size="15px" />
|
|
388
|
-
<span>{
|
|
391
|
+
<span>{agentData?.email}</span>
|
|
389
392
|
</Col>
|
|
390
393
|
</Row>
|
|
391
394
|
</div>
|
|
@@ -394,4 +397,6 @@ export default class ShowPage extends React.Component {
|
|
|
394
397
|
)
|
|
395
398
|
}
|
|
396
399
|
|
|
397
|
-
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
export default withParams(ShowPage);
|