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.
Files changed (71) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/stylesheets/iceholidays/frontend/actiontext.scss +119 -0
  3. data/app/assets/stylesheets/iceholidays/frontend/application.sass.scss +961 -355
  4. data/app/assets/stylesheets/iceholidays/frontend/common.scss +156 -72
  5. data/app/assets/stylesheets/iceholidays/frontend/layout.scss +218 -125
  6. data/app/assets/stylesheets/iceholidays/frontend/utils/_antd_overrides.scss +22 -7
  7. data/app/assets/stylesheets/iceholidays/frontend/utils/_variables.scss +2 -1
  8. data/app/assets/stylesheets/iceholidays/frontend/widgets/filter_pills.scss +19 -12
  9. data/app/assets/stylesheets/iceholidays/frontend/widgets/search_bar.scss +4 -0
  10. data/app/controllers/iceholidays/frontend/posts_controller.rb +14 -0
  11. data/app/javascript/api-services/agents-api.service.ts +33 -0
  12. data/app/javascript/api-services/locations-api.service.ts +23 -1
  13. data/app/javascript/api-services/series-api.service.ts +48 -28
  14. data/app/javascript/interfaces/agent.interface.ts +11 -0
  15. data/app/javascript/interfaces/country.interface.ts +4 -3
  16. data/app/javascript/interfaces/itinerary.interface.ts +101 -8
  17. data/app/javascript/react/App.tsx +1 -1
  18. data/app/javascript/react/components/Destinations.tsx +30 -20
  19. data/app/javascript/react/components/PriceDetails.tsx +146 -0
  20. data/app/javascript/react/components/shared/ContactAgentsForm.tsx +44 -0
  21. data/app/javascript/react/components/shared/Headline.tsx +2 -1
  22. data/app/javascript/react/components/shared/LocationDropdown.tsx +34 -0
  23. data/app/javascript/react/components/shared/{Postcard.tsx → LocationPostcards.tsx} +22 -1
  24. data/app/javascript/react/layouts/MainFooter.tsx +64 -39
  25. data/app/javascript/react/layouts/MainHeader.tsx +68 -30
  26. data/app/javascript/react/pages/AboutUsPage.tsx +6 -6
  27. data/app/javascript/react/pages/BlogPage.tsx +6 -4
  28. data/app/javascript/react/pages/ContactAgentsPage.tsx +174 -5
  29. data/app/javascript/react/pages/ContactUsPage.tsx +5 -5
  30. data/app/javascript/react/pages/CountriesPage.tsx +3 -8
  31. data/app/javascript/react/pages/Homepage.tsx +23 -13
  32. data/app/javascript/react/pages/ListingPage.tsx +192 -146
  33. data/app/javascript/react/pages/ShowPage.tsx +269 -264
  34. data/app/javascript/react/widgets/FilterPills.tsx +83 -49
  35. data/app/javascript/react/widgets/SearchBarWidget.tsx +24 -8
  36. data/app/views/iceholidays/frontend/posts/index.html.erb +9 -0
  37. data/app/views/iceholidays/frontend/posts/show.html.erb +2 -0
  38. data/config/routes.rb +2 -1
  39. data/lib/iceholidays/frontend/version.rb +1 -1
  40. data/public/iceholidays-assets/application.css +1190 -425
  41. data/public/iceholidays-assets/application.js +91 -104
  42. data/public/iceholidays-assets/application.js.map +4 -4
  43. data/public/iceholidays-assets/images/about-us_logo_mobile.png +0 -0
  44. data/public/iceholidays-assets/images/destinations_logo.png +0 -0
  45. data/public/iceholidays-assets/images/footer-bg_mobile.png +0 -0
  46. data/public/iceholidays-assets/images/logo_mobile.png +0 -0
  47. metadata +18 -27
  48. data/public/iceholidays-assets/images/8D7N.png +0 -0
  49. data/public/iceholidays-assets/images/Frame71.png +0 -0
  50. data/public/iceholidays-assets/images/africa.png +0 -0
  51. data/public/iceholidays-assets/images/banner1.png +0 -0
  52. data/public/iceholidays-assets/images/banner2.png +0 -0
  53. data/public/iceholidays-assets/images/china.png +0 -0
  54. data/public/iceholidays-assets/images/china2.png +0 -0
  55. data/public/iceholidays-assets/images/chongqing.png +0 -0
  56. data/public/iceholidays-assets/images/guangzhou.png +0 -0
  57. data/public/iceholidays-assets/images/guilin.png +0 -0
  58. data/public/iceholidays-assets/images/harbin.png +0 -0
  59. data/public/iceholidays-assets/images/hongkong.png +0 -0
  60. data/public/iceholidays-assets/images/inner_mongolia.png +0 -0
  61. data/public/iceholidays-assets/images/jiangxi.png +0 -0
  62. data/public/iceholidays-assets/images/kenya.png +0 -0
  63. data/public/iceholidays-assets/images/kenya2.png +0 -0
  64. data/public/iceholidays-assets/images/kunming.png +0 -0
  65. data/public/iceholidays-assets/images/slikroad.png +0 -0
  66. data/public/iceholidays-assets/images/southafrica.png +0 -0
  67. data/public/iceholidays-assets/images/tanzania.png +0 -0
  68. data/public/iceholidays-assets/images/tour1.png +0 -0
  69. data/public/iceholidays-assets/images/uganda.png +0 -0
  70. /data/public/iceholidays-assets/images/{Group_71.png → about-us_logo.png} +0 -0
  71. /data/public/iceholidays-assets/images/{logo_container.png → logo.png} +0 -0
@@ -0,0 +1,44 @@
1
+ import { Form, Select, Button, Flex } from "antd";
2
+ import React from "react";
3
+
4
+ export default function ContactAgentsForm(
5
+ props: {
6
+ states: any[];
7
+ cities: any[];
8
+ findAgents;
9
+ filterCities;
10
+ filterStates;
11
+ }
12
+ ) {
13
+ const {states, cities, findAgents, filterCities, filterStates} = props;
14
+
15
+ return (
16
+ <Form layout="vertical" onFinish={findAgents}>
17
+ <Form.Item wrapperCol={{ span: 24 }} label="Select State" name="state">
18
+ <Select
19
+ allowClear={true}
20
+ showSearch
21
+ filterOption={(input, option) =>
22
+ (option?.label ?? '').toString().toLowerCase().includes(input.toLowerCase())
23
+ }
24
+ placeholder="Please select"
25
+ options={states.map((s:any) => {return {label: s, value: s}})}
26
+ onChange={filterCities}/>
27
+ </Form.Item>
28
+ <Form.Item wrapperCol={{ span: 24 }} label="City" name="city">
29
+ <Select
30
+ allowClear={true}
31
+ showSearch
32
+ filterOption={(input, option) =>
33
+ (option?.label ?? '').toString().toLowerCase().includes(input.toLowerCase())
34
+ }
35
+ placeholder="Please select"
36
+ options={cities.map((s:any) => {return {label: s, value: s}})}
37
+ onChange={filterStates}/>
38
+ </Form.Item>
39
+ <Flex align="flex-end" justify="center">
40
+ <Button type="primary" className="find-button" htmlType="submit">Find Agent</Button>
41
+ </Flex>
42
+ </Form>
43
+ )
44
+ }
@@ -6,6 +6,7 @@ import SearchBarWidget from "../../widgets/SearchBarWidget";
6
6
  const Headline = (
7
7
  props: {
8
8
  bannerImage: string;
9
+ showSearch?: boolean;
9
10
  title?: string;
10
11
  children?: JSX.Element;
11
12
  breadcrumbs: any[];
@@ -14,7 +15,7 @@ const Headline = (
14
15
  const { bannerImage, title, children, breadcrumbs } = props;
15
16
  return (
16
17
  <>
17
- <SearchBarWidget/>
18
+ {(props.showSearch == undefined || props.showSearch && props.showSearch == true) && <SearchBarWidget/>}
18
19
  <div id="headline">
19
20
  <div id="headline_banner" style={{backgroundImage: `url(${bannerImage})`}}></div>
20
21
  <div id="headline_header">
@@ -0,0 +1,34 @@
1
+ import React from "react";
2
+ import Icon from "@mdi/react";
3
+ import { Location } from "../../../interfaces/country.interface";
4
+ import { Select } from "antd";
5
+ import { mdiFilterVariant } from "@mdi/js";
6
+
7
+
8
+ const LocationDropdown = (
9
+ props: {
10
+ locations:Location[];
11
+ selectLocation;
12
+ }
13
+ ) => {
14
+ const { locations } = props;
15
+
16
+ return (
17
+ <div className="location-dropdown">
18
+ <Select
19
+ className="location-filter"
20
+ suffixIcon={<Icon path={mdiFilterVariant} size="18px" />}
21
+ variant="borderless"
22
+ defaultValue="china"
23
+ options={locations.map((location) => ({label: location.name, value: location.id}))}
24
+ dropdownStyle={{padding: "15px 20px", borderRadius: "20px", boxShadow: "0px 3.08px 16.47px 0px #00000040"}}
25
+ optionRender={(option) => (
26
+ <div className="location-filter_option"> {option.label} </div>
27
+ )}
28
+ onChange={props.selectLocation}
29
+ />
30
+ </div>
31
+ )
32
+ }
33
+
34
+ export default LocationDropdown;
@@ -2,6 +2,7 @@ import React from "react";
2
2
  import { Link } from "react-router-dom";
3
3
  import { mdiMenuRight } from "@mdi/js";
4
4
  import Icon from "@mdi/react";
5
+ import { Location } from "../../../interfaces/country.interface";
5
6
 
6
7
 
7
8
  const Postcard = (
@@ -28,4 +29,24 @@ const Postcard = (
28
29
  )
29
30
  }
30
31
 
31
- export default Postcard;
32
+ const LocationPostcards = (
33
+ props: {
34
+ locations:Location[];
35
+ }
36
+ ) => {
37
+ const { locations } = props;
38
+
39
+ const listingLink = (locationId: number) => {
40
+ return `/app/listing?location_id=${locationId}`;
41
+ }
42
+
43
+ return (
44
+ <div className="postcards">
45
+ {
46
+ locations.map((city, index)=><Postcard key={index} link={listingLink(city.id)} image={city.cover} name={city.name} tourCount={city.tourCount}/>)
47
+ }
48
+ </div>
49
+ )
50
+ }
51
+
52
+ export default LocationPostcards;
@@ -1,10 +1,37 @@
1
- import { notification, Space } from "antd";
1
+ import { Col, Flex, notification, Row, Space } from "antd";
2
2
  import React from "react";
3
3
  import { Link } from "react-router-dom";
4
4
  import LocationsApi from "../../api-services/locations-api.service";
5
5
  import { Country } from "../../interfaces/country.interface";
6
6
 
7
7
 
8
+ const menus = [
9
+ {
10
+ label: "Home",
11
+ link: "/app"
12
+ },
13
+ {
14
+ label: "pdpa",
15
+ link: "/app"
16
+ },
17
+ {
18
+ label: "about us",
19
+ link: "/app/about-us"
20
+ },
21
+ {
22
+ label: "privacy policy",
23
+ link: "/app"
24
+ },
25
+ {
26
+ label: "contact us",
27
+ link: "/app/contact-us"
28
+ },
29
+ {
30
+ label: "terms & conditions",
31
+ link: "/app"
32
+ },
33
+ ]
34
+
8
35
  export default class MainFooter extends React.Component {
9
36
  api = new LocationsApi;
10
37
 
@@ -25,44 +52,42 @@ export default class MainFooter extends React.Component {
25
52
  render(){
26
53
  return (
27
54
  <div id="main-footer">
28
- <div id="main-footer_links">
29
- <div>
30
- <div className="link-group-title">Countries</div>
31
- <div>
32
- {
33
- this.state.countries.map((country: Country)=> <Link to={`/app/listing?keyword=${country.name}`}>{country.name}</Link> )
34
- }
35
- </div>
36
- </div>
37
- <div>
38
- <div className="link-group-title">&nbsp;</div>
39
- <div>
40
- <Link to="/app">home</Link>
41
- <Link to="/app">pdpa</Link>
42
- <Link to="/app/about-us">about us</Link>
43
- <Link to="/app">privacy policy</Link>
44
- <Link to="/app/contact-us">contact us</Link>
45
- <Link to="/app">terms & conditions</Link>
46
- </div>
47
- </div>
48
- <div>
49
- <div>
50
- <div className="link-group-title">Contact Us</div>
51
- <div><Link to="/app/contact-agents">contact agents</Link></div>
52
- </div>
53
-
54
- <div>
55
- <div className="link-group-title">Follow us on</div>
56
- <Space size={20}>
57
- <img src="/iceholidays-assets/images/social/ico_fb.png"/>
58
- <img src="/iceholidays-assets/images/social/ico_twitter.png"/>
59
- <img src="/iceholidays-assets/images/social/ico_yt.png"/>
60
- <img src="/iceholidays-assets/images/social/ico_ig.png"/>
61
- </Space>
62
- </div>
63
- </div>
64
- <hr/>
65
- </div>
55
+ <Row id="main-footer_links" gutter={[12, 12]}>
56
+ <Col flex="1 0 25%" className="column">
57
+ <Space direction="vertical">
58
+ <div className="link-group-title">Countries</div>
59
+ <div className="link-group-items">
60
+ {
61
+ this.state.countries.map((country: Country)=> <Link key={country.id} to={`/app/listing?keyword=${country.name}`}>{country.name}</Link> )
62
+ }
63
+ </div>
64
+ </Space>
65
+ </Col>
66
+ <Col flex="1 0 30%" className="column footer-menu">
67
+ <Space direction="vertical">
68
+ <div className="link-group-items">
69
+ {
70
+ menus.map(menu => <Link to={menu.link}>{menu.label}</Link>)
71
+ }
72
+ </div>
73
+ </Space>
74
+ </Col>
75
+ <Col flex="1 0 20%" className="column contact-info">
76
+ <Space direction="vertical" align="center">
77
+ <Flex vertical gap="20px" align="center">
78
+ <div className="link-group-title">Contact Us</div>
79
+ <div><Link to="/app/contact-agents">contact agents</Link></div>
80
+ <div className="link-group-title">Follow us on</div>
81
+ <Flex gap={20} justify="space-between">
82
+ <img src="/iceholidays-assets/images/social/ico_fb.png"/>
83
+ <img src="/iceholidays-assets/images/social/ico_twitter.png"/>
84
+ <img src="/iceholidays-assets/images/social/ico_yt.png"/>
85
+ <img src="/iceholidays-assets/images/social/ico_ig.png"/>
86
+ </Flex>
87
+ </Flex>
88
+ </Space>
89
+ </Col>
90
+ </Row>
66
91
  <div id="copyright">
67
92
  <span>Copyright © 2024 Golden Destinations</span>
68
93
  </div>
@@ -1,43 +1,81 @@
1
- import { mdiFileDownload } from "@mdi/js";
1
+ import { mdiFileDownload, mdiMenu } from "@mdi/js";
2
2
  import Icon from "@mdi/react";
3
- import { Button } from "antd";
4
- import React from "react";
5
- import { NavLink } from "react-router-dom";
3
+ import { Button, Drawer, Menu } from "antd";
4
+ import React, { useState } from "react";
5
+ import type { MenuProps } from 'antd';
6
+ import { Link, useLocation, useMatch } from "react-router-dom";
6
7
 
8
+ type MenuItem = Required<MenuProps>['items'][number];
7
9
 
8
- const routes = [
9
- {label: "Home", path: "/app"},
10
- {label: "About Us", path: "/app/about-us"},
11
- {label: "Countries", path: "/app/countries"},
12
- {label: "Blog", path: "/app/blog"}
13
- ]
14
10
 
15
- const routes2 = [
16
- {label: "Contact Agents", path: "/app/contact-agents"},
17
- {label: "Contact Us", path: "/app/contact-us"}
11
+ const items: MenuItem[] = [
12
+ {
13
+ key: 'app',
14
+ label: <Link to="/app"> Home </Link>
15
+ },
16
+ {
17
+ key: 'about-us',
18
+ label: <Link to="/app/about-us"> About Us </Link>
19
+ },
20
+ {
21
+ key: 'countries',
22
+ label: <Link to="/app/countries"> Countries </Link>
23
+ },
24
+ {
25
+ key: 'blog',
26
+ label: <Link to="/app/blog"> Blog </Link>
27
+ },
28
+ {
29
+ key: 'logo',
30
+ label: <img src="/iceholidays-assets/images/logo.png" className="logo logo_desktop"/>,
31
+ disabled: true
32
+ },
33
+ {
34
+ key: 'contact-agents',
35
+ label: <Link to="/app/contact-agents"> Contact Agents </Link>
36
+ },
37
+ {
38
+ key: 'contact-us',
39
+ label: <Link to="/app/contact-us"> Contact Us </Link>
40
+ },
18
41
  ]
19
42
 
20
43
  const MainHeader = () => {
44
+ const [open, setOpen] = useState(false);
45
+ const location = useLocation();
46
+ const activeUrlKey = location.pathname.split("/").pop() || "app";
47
+ const [current, setCurrent] = useState(activeUrlKey);
48
+
49
+ const onClick: MenuProps['onClick'] = (e) => {
50
+ setCurrent(e.key);
51
+ setOpen(false);
52
+ };
53
+
54
+ const showDrawer = () => {
55
+ setOpen(true);
56
+ };
57
+
58
+ const onClose = () => {
59
+ setOpen(false);
60
+ };
61
+
21
62
  return (
22
63
  <div id="main-header">
23
- <div>
24
- <div className="nav-menu" id="left-menu">
25
- {
26
- routes.map((route, index)=> <NavLink key={index} to={route.path} end className={({ isActive }) => (isActive ? 'active' : '')}> {route.label} </NavLink>)
27
- }
28
- </div>
29
- <span className="nav-menu logo">
30
- <img src="/iceholidays-assets/images/logo_container.png"/>
31
- </span>
32
- <div className="nav-menu" id="right-menu">
33
- {
34
- routes2.map((route, index)=> <NavLink key={index} to={route.path} end className={({ isActive }) => (isActive ? 'active' : '')}> {route.label} </NavLink>)
35
- }
36
- </div>
64
+ <Menu mode="horizontal" items={items} onClick={onClick} selectedKeys={[current]} className="nav-menu"/>
65
+
66
+ <a href="/app"><img src="/iceholidays-assets/images/logo_mobile.png" className="logo logo_mobile"/></a>
67
+
68
+ <div id="nav-buttons">
69
+ <Button id="get-brochure" type="primary"><Icon path={mdiFileDownload} size="18px" /> get brochure</Button>
70
+
71
+ <Button id="toggle-menu" type="primary" onClick={showDrawer}>
72
+ <Icon path={mdiMenu} size="25px" />
73
+ </Button>
37
74
  </div>
38
- <a id="get-brochure">
39
- <Button type="primary"><Icon path={mdiFileDownload} size="18px" /> get brochure</Button>
40
- </a>
75
+
76
+ <Drawer title={null} onClose={onClose} open={open} id="nav-drawer">
77
+ <Menu items={items} onClick={onClick} selectedKeys={[current]} className="nav-menu_mobile"/>
78
+ </Drawer>
41
79
  </div>
42
80
  )
43
81
  }
@@ -50,21 +50,21 @@ const AboutUsPage = React.FC = () => (
50
50
  <p>ICE Holidays is committed to striving towards our customer satisfaction in areas of more quality value for money of our products & services. The management and all employees are committed to the company quality policy through effort in:</p>
51
51
  </div>
52
52
  </Col>
53
- <Col span={8}>
53
+ <Col xs={24} sm={24} lg={8}>
54
54
  <div className="items">
55
55
  <img src="/iceholidays-assets/images/innovative.png"/>
56
56
  <label>Innovative</label>
57
57
  <p>Talent team develops great products and services; Agile, innovative, and a quality conscious team of resourceful people dedicated to the best.</p>
58
58
  </div>
59
59
  </Col>
60
- <Col span={8}>
60
+ <Col xs={24} sm={24} lg={8}>
61
61
  <div className="items">
62
62
  <img src="/iceholidays-assets/images/competitiveness.png"/>
63
63
  <label>Competitiveness</label>
64
64
  <p>Provide competitive pricing to achieve customer satisfaction.</p>
65
65
  </div>
66
66
  </Col>
67
- <Col span={8}>
67
+ <Col xs={24} sm={24} lg={8}>
68
68
  <div className="items">
69
69
  <img src="/iceholidays-assets/images/ethical.png"/>
70
70
  <label>Ethical</label>
@@ -73,17 +73,17 @@ const AboutUsPage = React.FC = () => (
73
73
  </Col>
74
74
  </Row>
75
75
 
76
- <Row id="certificates" gutter={24}>
76
+ <Row id="certificates" align="middle" justify="center" gutter={[24, 24]}>
77
77
  <Col span={24}>
78
78
  <div className="section-header">
79
79
  <h2>Our Certificates</h2>
80
80
  <p>We have acquired recognition and certificates such as:</p>
81
81
  </div>
82
82
  </Col>
83
- <Col span={8}>
83
+ <Col xs={24} sm={24} lg={8}>
84
84
  <img src="/iceholidays-assets/images/certificate1.png"/>
85
85
  </Col>
86
- <Col span={8}>
86
+ <Col xs={24} sm={24} lg={8}>
87
87
  <img src="/iceholidays-assets/images/certificate2.png"/>
88
88
  </Col>
89
89
  </Row>
@@ -36,6 +36,10 @@ const blogs = [
36
36
  },
37
37
  ]
38
38
 
39
+ const setFilter = (selectedFilter) => {
40
+ console.log("set filter", selectedFilter)
41
+ }
42
+
39
43
 
40
44
  const BlogPage = React.FC = () => (
41
45
  <div id="blog-page">
@@ -43,16 +47,14 @@ const BlogPage = React.FC = () => (
43
47
 
44
48
  <div id="blog-page_body">
45
49
  <Space size={12} direction="vertical">
46
- <FilterPills items={types} allOption={true}></FilterPills>
47
- <FilterPills items="years" title="Year" allOption={true}></FilterPills>
48
- <FilterPills items="months" title="Month" allOption={true}></FilterPills>
50
+ <FilterPills items={types} allOption={true} initialValue={{keyword: "", year: "", month: ""}} selectFilter={(selected)=>setFilter(selected)}></FilterPills>
49
51
  </Space>
50
52
 
51
53
  <div id="blogs">
52
54
  <Row wrap gutter={20}>
53
55
  {
54
56
  blogs.map(blog => (
55
- <Col span={8}>
57
+ <Col key={blog.id} span={8}>
56
58
  <Link to={`/app/blog/${blog.id}`}>
57
59
  <Card
58
60
  hoverable
@@ -1,5 +1,13 @@
1
1
  import React from "react";
2
2
  import Headline from "../components/shared/Headline";
3
+ import ContactAgentsForm from "../components/shared/ContactAgentsForm";
4
+ import AgentsApi from "../../api-services/agents-api.service";
5
+ import { Button, Card, Col, Modal, notification, Row } from "antd";
6
+ import { Agent } from "../../interfaces/agent.interface";
7
+ import { mdiMapMarker, mdiPhoneInTalkOutline, mdiWhatsapp, mdiEmailOutline } from "@mdi/js";
8
+ import Icon from "@mdi/react";
9
+
10
+ const { Meta } = Card;
3
11
 
4
12
  const bannerPath = '/iceholidays-assets/images/contact_agents.png';
5
13
  const breadcrumbs = [
@@ -7,10 +15,171 @@ const breadcrumbs = [
7
15
  { title: 'Contact Agents' }
8
16
  ]
9
17
 
10
- const ContactAgentsPage = React.FC = () => (
11
- <div id="contact-agents-page">
12
- <Headline bannerImage={bannerPath} breadcrumbs={breadcrumbs} title="CONTACT AGENTS"/>
13
- </div>
14
- );
18
+ class ContactAgentsPage extends React.Component {
19
+ agentsApi = new AgentsApi;
20
+
21
+ state = {
22
+ loading: true,
23
+ allAgents: [],
24
+ agents: [],
25
+ states: [],
26
+ cities: [],
27
+ setIsModalOpen: false,
28
+ agentData: { name: "", image: "", address: "", phone: "", whatsapp: "", email: "", city: "", state: "" },
29
+ }
30
+
31
+
32
+ componentDidMount() {
33
+ this.agentsApi.getAgents()
34
+ .then((agentsData:Agent[]) => {
35
+ const states = agentsData.filter(a => a.state != null).map(a => a.state);
36
+ const cities = agentsData.filter(a => a.city != null).map(a => a.city);
37
+ this.setState({agents: agentsData, allAgents: agentsData, loadingAgents:false, states, cities});
38
+ })
39
+ .finally(()=>this.setState({loading: false}))
40
+ .catch(error => {
41
+ notification.error({ message: 'An error occured while loading agents.'});
42
+ });
43
+ }
44
+
45
+ showModal(agentData){
46
+ this.setState({setIsModalOpen: true, agentData: agentData});
47
+ };
48
+
49
+ closeModal(){
50
+ this.setState({setIsModalOpen: false});
51
+ };
52
+
53
+
54
+ filterCities = (searchStr) => {
55
+ const allAgents:Agent[] = this.state.allAgents;
56
+
57
+ if(searchStr == undefined){
58
+ this.resetAgents();
59
+ return;
60
+ }
61
+ const agents:Agent[] = allAgents.filter(a => a.state == searchStr);
62
+ const results = agents.map(a => a.city);
63
+ this.setState({cities: results});
64
+ }
65
+
66
+
67
+ filterStates= (searchStr) => {
68
+ const allAgents:Agent[] = this.state.allAgents;
69
+
70
+ if(searchStr == undefined){
71
+ this.resetAgents();
72
+ return;
73
+ }
74
+
75
+ const agents:Agent[] = allAgents.filter(a => a.city == searchStr);
76
+ const results = agents.map(a => a.state);
77
+ this.setState({states: results});
78
+ }
79
+
80
+ resetAgents(){
81
+ const allAgents:Agent[] = this.state.allAgents;
82
+ const states = allAgents.filter(a => a.state != null).map(a => a.state);
83
+ const cities = allAgents.filter(a => a.city != null).map(a => a.city);
84
+ this.setState({states, cities});
85
+ }
86
+
87
+ findAgents = (formValues) => {
88
+ const allAgents:Agent[] = this.state.allAgents;
89
+ const agents = allAgents.filter(a =>
90
+ (formValues.state == undefined || a.state === formValues.state) &&
91
+ (formValues.city == undefined || a.city === formValues.city));
92
+ this.setState({agents, showAgentsResults: true})
93
+ }
94
+
95
+
96
+ render(){
97
+ const {agents, states, cities, agentData} = this.state;
98
+ return (
99
+ <div id="contact-agents-page">
100
+ <Headline bannerImage={bannerPath} breadcrumbs={breadcrumbs} title="CONTACT AGENTS"/>
101
+
102
+ <div id="contact-agents-page_body">
103
+ <div id="find-agents">
104
+ <div id="find-agents_header"><span>Get Your City</span></div>
105
+ <div id="find-agents_form">
106
+ <ContactAgentsForm
107
+ states={states}
108
+ cities={cities}
109
+ findAgents={this.findAgents}
110
+ filterCities={this.filterCities}
111
+ filterStates={this.filterStates}/>
112
+ </div>
113
+ </div>
114
+
115
+ <div id="agents-list">
116
+ <Row gutter={[20, 20]}>
117
+ {
118
+ agents.map((agent:Agent) => (
119
+ <Col xs={24} sm={24} md={12} lg={6}>
120
+ <Card
121
+ hoverable
122
+ cover={<img alt={agent.name} src={agent.image} />}
123
+ >
124
+ <Meta title={agent.name} description={
125
+ <>
126
+ <div className="agent-details">
127
+ <Row gutter={[20, 20]} align="middle">
128
+ <Col span={24}>
129
+ <Icon path={mdiMapMarker} size="15px" />
130
+ <span>{agent.address}</span>
131
+ </Col>
132
+ <Col span={24}>
133
+ <Icon path={mdiPhoneInTalkOutline} size="15px" />
134
+ <span>{agent.phone}</span>
135
+ </Col>
136
+ <Col span={24}>
137
+ <Icon path={mdiWhatsapp} size="15px" />
138
+ <span>{agent.whatsapp}</span>
139
+ </Col>
140
+ <Col span={24}>
141
+ <Icon path={mdiEmailOutline} size="15px" />
142
+ <span>{agent.email}</span>
143
+ </Col>
144
+ </Row>
145
+ </div>
146
+ <Button color="primary" variant="outlined" className="more-info-button"
147
+ onClick={()=>this.showModal(agent)}>
148
+ More info
149
+ </Button>
150
+ </>
151
+ } />
152
+ </Card>
153
+ </Col>
154
+ ))
155
+ }
156
+ </Row>
157
+ </div>
158
+
159
+ <Modal title={
160
+ <>
161
+ <img src={agentData?.image}/>
162
+ <span> {agentData.name} </span>
163
+ </>
164
+ } open={this.state.setIsModalOpen} onCancel={()=>this.closeModal()} footer={null} width={350} centered className="agent-full-details">
165
+ <Row gutter={[10,10]}>
166
+ <Col span={12}> <label>Agent Name</label> </Col>
167
+ <Col span={12}> <span> {agentData.name} </span></Col>
168
+ <Col span={12}> <label>State</label> </Col>
169
+ <Col span={12}> <span> {agentData.state} </span></Col>
170
+ <Col span={12}> <label>Address</label> </Col>
171
+ <Col span={12}> <span> {agentData.address} </span></Col>
172
+ <Col span={12}> <label>Tel</label> </Col>
173
+ <Col span={12}> <span> {agentData.phone} </span></Col>
174
+ <Col span={12}> <label>Email</label> </Col>
175
+ <Col span={12}> <span> {agentData.email} </span></Col>
176
+ </Row>
177
+ {/* google maps */}
178
+ </Modal>
179
+ </div>
180
+ </div>
181
+ )
182
+ }
183
+ }
15
184
 
16
185
  export default ContactAgentsPage;
@@ -83,25 +83,25 @@ const ContactUsPage = React.FC = () => {
83
83
  <TextArea rows={4} placeholder="Write Here"/>
84
84
  </Form.Item>
85
85
 
86
- <Form.Item wrapperCol={{ offset: 10, span: 14 }}>
86
+ <Flex align="center" justify="center">
87
87
  <Button type="primary" htmlType="submit">
88
88
  Submit
89
89
  </Button>
90
- </Form.Item>
90
+ </Flex>
91
91
 
92
92
  </Form>
93
93
  </div>
94
94
  </div>
95
95
 
96
96
  <div id="contact-us-page_footer">
97
- <Row gutter={29}>
98
- <Col span={12}>
97
+ <Row gutter={[29, 20]} >
98
+ <Col xs={24} sm={24} lg={12}>
99
99
  <div>
100
100
  <label>Contact Us</label>
101
101
  <span>feedback@gd.my</span>
102
102
  </div>
103
103
  </Col>
104
- <Col span={12}>
104
+ <Col xs={24} sm={24} lg={12}>
105
105
  <div>
106
106
  <label>Connect with Us</label>
107
107
  <Space size={29}>