├── .gitignore
├── README.md
├── bunker
├── .gitignore
├── README.md
├── package.json
├── public
│ ├── 404.html
│ ├── bunkertransparent.gif
│ ├── bunkertransparent.png
│ ├── favicon.ico
│ ├── index.html
│ ├── landingbackground.jpg
│ └── manifest.json
├── semantic.json
└── src
│ ├── App.js
│ ├── commonComponents
│ ├── CheckInOutCalendar.js
│ ├── Navigation
│ │ └── Navigation.js
│ ├── RoomQuantitySelect.js
│ └── RoomTypeSelect.js
│ ├── constants
│ ├── roles.js
│ └── routes.js
│ ├── images
│ ├── LandingBackground.jpg
│ ├── accountBackground.jpeg
│ ├── accountBackground.jpg
│ ├── accountBackground1.jpg
│ ├── bunker.png
│ └── linkedin_banner_image_1.png
│ ├── index.css
│ ├── index.js
│ ├── pages
│ ├── Account
│ │ └── Account.js
│ ├── Admin
│ │ └── Admin.js
│ ├── Home
│ │ ├── Home.js
│ │ ├── components
│ │ │ ├── FilterSort.js
│ │ │ ├── HotelCard.js
│ │ │ ├── ListingBase.js
│ │ │ ├── Maps.js
│ │ │ ├── MapsHotel.js
│ │ │ └── SearchBar.js
│ │ ├── dummydata.js
│ │ └── sticky.css
│ ├── Hotel
│ │ ├── Carousel.js
│ │ ├── CheckOut
│ │ │ ├── CheckOut.js
│ │ │ ├── CheckOutForm.js
│ │ │ └── conflict.css
│ │ └── Hotel.js
│ ├── Landing
│ │ └── Landing.js
│ ├── PasswordChange
│ │ └── PasswordChange.js
│ ├── PasswordForget
│ │ └── PasswordForget.js
│ ├── Reservation
│ │ ├── CancelReservation
│ │ │ ├── CancelReservation.js
│ │ │ └── CancelReservationForm.js
│ │ ├── ChangeReservation
│ │ │ ├── ChangeReservation.js
│ │ │ ├── ChangeReservationForm.js
│ │ │ ├── ChangeReservationForm_alt2.js
│ │ │ └── ChangeReservation_alt.js
│ │ └── Reservation.js
│ ├── SignIn
│ │ └── SignIn.js
│ ├── SignUp
│ │ └── SignUp.js
│ └── Users
│ │ ├── UserItem.js
│ │ ├── UserList.js
│ │ └── index.js
│ ├── server
│ ├── Firebase
│ │ ├── context.js
│ │ ├── firebase.js
│ │ ├── firebase3.js
│ │ └── index.js
│ ├── Payment
│ │ └── PayPalButton.js
│ └── Session
│ │ ├── context.js
│ │ ├── index.js
│ │ ├── withAuthentication.js
│ │ ├── withAuthorization.js
│ │ └── withEmailVerification.js
│ └── serviceWorker.js
├── package-lock.json
└── package.json
/.gitignore:
--------------------------------------------------------------------------------
1 | # Created by .ignore support plugin (hsz.mobi)
2 | ### Windows template
3 | # Windows thumbnail cache files
4 | Thumbs.db
5 | ehthumbs.db
6 | ehthumbs_vista.db
7 |
8 | # Dump file
9 | *.stackdump
10 |
11 | # Folder config file
12 | [Dd]esktop.ini
13 |
14 | # Recycle Bin used on file shares
15 | $RECYCLE.BIN/
16 |
17 | # Windows Installer files
18 | *.cab
19 | *.msi
20 | *.msix
21 | *.msm
22 | *.msp
23 |
24 | # Windows shortcuts
25 | *.lnk
26 | ### JetBrains template
27 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
28 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
29 |
30 | # User-specific stuff
31 | .idea/**/workspace.xml
32 | .idea/**/tasks.xml
33 | .idea/**/dictionaries
34 | .idea/**/shelf
35 |
36 | # Sensitive or high-churn files
37 | .idea/**/dataSources/
38 | .idea/**/dataSources.ids
39 | .idea/**/dataSources.local.xml
40 | .idea/**/sqlDataSources.xml
41 | .idea/**/dynamic.xml
42 | .idea/**/uiDesigner.xml
43 | .idea/**/dbnavigator.xml
44 |
45 | # Gradle
46 | .idea/**/gradle.xml
47 | .idea/**/libraries
48 |
49 | # CMake
50 | cmake-build-debug/
51 | cmake-build-release/
52 |
53 | # Mongo Explorer plugin
54 | .idea/**/mongoSettings.xml
55 |
56 | # File-based project format
57 | *.iws
58 |
59 | # IntelliJ
60 | out/
61 |
62 | # mpeltonen/sbt-idea plugin
63 | .idea_modules/
64 |
65 | # JIRA plugin
66 | atlassian-ide-plugin.xml
67 |
68 | # Cursive Clojure plugin
69 | .idea/replstate.xml
70 |
71 | # Crashlytics plugin (for Android Studio and IntelliJ)
72 | com_crashlytics_export_strings.xml
73 | crashlytics.properties
74 | crashlytics-build.properties
75 | fabric.properties
76 |
77 | # Editor-based Rest Client
78 | .idea/httpRequests
79 | ### macOS template
80 | # General
81 | .DS_Store
82 | .AppleDouble
83 | .LSOverride
84 |
85 | # Icon must end with two \r
86 | Icon
87 |
88 | # Thumbnails
89 | ._*
90 |
91 | # Files that might appear in the root of a volume
92 | .DocumentRevisions-V100
93 | .fseventsd
94 | .Spotlight-V100
95 | .TemporaryItems
96 | .Trashes
97 | .VolumeIcon.icns
98 | .com.apple.timemachine.donotpresent
99 |
100 | # Directories potentially created on remote AFP share
101 | .AppleDB
102 | .AppleDesktop
103 | Network Trash Folder
104 | Temporary Items
105 | .apdisk
106 |
107 | # dependencies
108 | /node_modules
109 |
110 |
111 | package-lock.json
112 | .idea
113 | .env
114 | server/firebase/config.js
115 | yarn.lock
116 | bunker/src/server/firebase/config.js
117 | bunker/yarn.lock
118 | bunker/.firebase/hosting.cHVibGlj.cache
119 | bunker/.firebase/hosting.YnVpbGQ.cache
120 | bunker/.firebaserc
121 | bunker/database.rules.json
122 | bunker/firebase.json
123 | bunker/firestore.indexes.json
124 | bunker/firestore.rules
125 | bunker/package.json
126 | bunker/src/server/firebase/test.js
127 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Bunker
2 | 
3 |
4 | ### Statement of Work
5 | LikeHome is a web application (or mobile application) which shares catalog of ideas. LikeHomeis a mini clone of the very successful product hotels.com.
6 |
7 | ### Scope of Work
8 | The scope of this project is to design and develop LikeHome, which can manifest as an iOS, Android, Windows application. LikeHome should minimally provide the following functions:
9 | * Authentication (login/sign up)
10 | * Search:
11 | * Filter result
12 | * Sort results
13 | * Reservation
14 | * New
15 | * Change
16 | * Cancel
17 | * No multiple books in the same date under the same ID.
18 | * Payment Cancelation charge
19 | * Mybooking(show all my reservation)
20 | * Reward point
21 | * Redeem points for free stay System Interfaces LikeHome interacts with various interfaces, including but not limited to a web interface.
22 |
23 | ### User Interface
24 | LikeHome will have user interfaces that must look the same on iOS, Android, Windows or the Web.
25 |
26 | ### Design
27 | LikeHome is an easy to use application. Usability good GUI design and user-friendness should be the design focuses.
28 |
29 | ## Setup
30 | For team members of this project, please run the following commands:
31 |
32 | ```git clone https://github.com/cmpe165spring2019/ReactApp.git```
33 |
34 | ```cd bunker```
35 |
36 | ```npm install```
37 |
38 | ```npm start```
39 |
40 |
41 | To keep updated and get the latest commits from master
42 |
43 | ```git fetch origin```
44 |
45 | ```git rebase origin/master```
46 |
--------------------------------------------------------------------------------
/bunker/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 | bunker/yarn.lock
25 | bunker/src/server/firebase/config.js
26 |
--------------------------------------------------------------------------------
/bunker/README.md:
--------------------------------------------------------------------------------
1 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
2 |
3 | ## Available Scripts
4 |
5 | In the project directory, you can run:
6 |
7 | ### `npm start`
8 |
9 | Runs the app in the development mode.
10 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
11 |
12 | The page will reload if you make edits.
13 | You will also see any lint errors in the console.
14 |
15 | ### `npm test`
16 |
17 | Launches the test runner in the interactive watch mode.
18 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
19 |
20 | ### `npm run build`
21 |
22 | Builds the app for production to the `build` folder.
23 | It correctly bundles React in production mode and optimizes the build for the best performance.
24 |
25 | The build is minified and the filenames include the hashes.
26 | Your app is ready to be deployed!
27 |
28 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
29 |
30 | ### `npm run eject`
31 |
32 | **Note: this is a one-way operation. Once you `eject`, you can’t go back!**
33 |
34 | If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
35 |
36 | Instead, it will copy all the configuration files and the transitive dependencies (Webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.
37 |
38 | You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
39 |
40 | ## Learn More
41 |
42 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
43 |
44 | To learn React, check out the [React documentation](https://reactjs.org/).
45 |
46 | ### Code Splitting
47 |
48 | This section has moved here: https://facebook.github.io/create-react-app/docs/code-splitting
49 |
50 | ### Analyzing the Bundle Size
51 |
52 | This section has moved here: https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size
53 |
54 | ### Making a Progressive Web App
55 |
56 | This section has moved here: https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app
57 |
58 | ### Advanced Configuration
59 |
60 | This section has moved here: https://facebook.github.io/create-react-app/docs/advanced-configuration
61 |
62 | ### Deployment
63 |
64 | This section has moved here: https://facebook.github.io/create-react-app/docs/deployment
65 |
66 | ### `npm run build` fails to minify
67 |
68 | This section has moved here: https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify
69 |
--------------------------------------------------------------------------------
/bunker/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "bunker",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@wojtekmaj/react-daterange-picker": "^2.1.0",
7 | "bootstrap": "^4.3.1",
8 | "css-loader": "^2.1.1",
9 | "firebase": "^5.8.5",
10 | "firebase-tools": "^6.7.1",
11 | "google-maps-react": "^2.0.2",
12 | "google-map-react": "^1.1.4",
13 | "lodash": "^4.17.11",
14 | "moment": "^2.24.0",
15 | "nuka-carousel": "^4.5.3",
16 | "prop-types": "^15.7.2",
17 | "react": "^16.8.3",
18 | "react-async-script-loader": "^0.3.0",
19 | "react-bootstrap": "^1.0.0-beta.8",
20 | "react-dom": "^16.8.2",
21 | "react-input-slider": "^5.0.6",
22 | "react-router-dom": "^4.3.1",
23 | "react-scripts": "^2.1.8",
24 | "react-sticky": "^6.0.3",
25 | "recompose": "^0.30.0",
26 | "semantic-ui-calendar-react": "^0.14.2",
27 | "semantic-ui-carousel-react": "^1.0.3",
28 | "semantic-ui-css": "^2.4.1",
29 | "semantic-ui-react": "^0.85.0",
30 | "style-loader": "^0.23.1"
31 | },
32 | "scripts": {
33 | "start": "react-scripts start",
34 | "build": "react-scripts build",
35 | "test": "react-scripts test",
36 | "eject": "react-scripts eject"
37 | },
38 | "eslintConfig": {
39 | "extends": "react-app"
40 | },
41 | "browserslist": [
42 | ">0.2%",
43 | "not dead",
44 | "not ie <= 11",
45 | "not op_mini all"
46 | ]
47 | }
48 |
--------------------------------------------------------------------------------
/bunker/public/404.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Page Not Found
7 |
8 |
23 |
24 |
25 |
26 |
404
27 | Page Not Found
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/bunker/public/bunkertransparent.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cmpe165spring2019/ReactApp/04c8604998d86d7e92afde4d95c532d5ac32bb95/bunker/public/bunkertransparent.gif
--------------------------------------------------------------------------------
/bunker/public/bunkertransparent.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cmpe165spring2019/ReactApp/04c8604998d86d7e92afde4d95c532d5ac32bb95/bunker/public/bunkertransparent.png
--------------------------------------------------------------------------------
/bunker/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cmpe165spring2019/ReactApp/04c8604998d86d7e92afde4d95c532d5ac32bb95/bunker/public/favicon.ico
--------------------------------------------------------------------------------
/bunker/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 |
11 |
15 |
16 |
17 |
26 |
27 |
28 |
29 |
33 |
34 |
38 |
39 |
40 |
41 |
74 |
75 |
81 |
82 | Bunker
83 |
84 |
85 |
86 |
87 | You need to enable JavaScript to run this app.
88 |
89 |
90 |
91 |
92 |
93 |
103 |
104 |
105 |
--------------------------------------------------------------------------------
/bunker/public/landingbackground.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cmpe165spring2019/ReactApp/04c8604998d86d7e92afde4d95c532d5ac32bb95/bunker/public/landingbackground.jpg
--------------------------------------------------------------------------------
/bunker/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | }
10 | ],
11 | "start_url": ".",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/bunker/semantic.json:
--------------------------------------------------------------------------------
1 | {
2 | "base": "semantic/",
3 | "paths": {
4 | "source": {
5 | "config": "src/theme.config",
6 | "definitions": "src/definitions/",
7 | "site": "src/site/",
8 | "themes": "src/themes/"
9 | },
10 | "output": {
11 | "packaged": "/out/",
12 | "uncompressed": "/out/components/",
13 | "compressed": "/out/components/",
14 | "themes": "/out/themes/"
15 | },
16 | "clean": "dist/"
17 | },
18 | "permission": false,
19 | "autoInstall": false,
20 | "rtl": false,
21 | "components": ["reset", "site", "button", "container", "divider", "flag", "header", "icon", "image", "input", "label", "list", "loader", "placeholder", "rail", "reveal", "segment", "step", "breadcrumb", "form", "grid", "menu", "message", "table", "ad", "card", "comment", "feed", "item", "statistic", "accordion", "checkbox", "dimmer", "dropdown", "embed", "modal", "nag", "popup", "progress", "rating", "search", "shape", "sidebar", "sticky", "tab", "transition", "api", "form", "state", "visibility"],
22 | "version": "2.4.2"
23 | }
--------------------------------------------------------------------------------
/bunker/src/App.js:
--------------------------------------------------------------------------------
1 | import React, {Component} from "react";
2 | import {BrowserRouter as Router, Route} from "react-router-dom";
3 | import {PayPalButton} from "./server/Payment/PayPalButton";
4 |
5 | import Navigation from './commonComponents/Navigation/Navigation';
6 |
7 | import LandingPage from './pages/Landing/Landing';
8 | import SignUpPage from './pages/SignUp/SignUp';
9 | import SignInPage from './pages/SignIn/SignIn';
10 | import PasswordForgetPage from './pages/PasswordForget/PasswordForget';
11 | import HomePage from './pages/Home/Home';
12 | import AccountPage from './pages/Account/Account';
13 | import AdminPage from './pages/Admin/Admin';
14 | import HotelPage from './pages/Hotel/Hotel';
15 | import ReservationPage from './pages/Reservation/Reservation';
16 |
17 | import * as ROUTES from './constants/routes';
18 | import { withAuthentication } from './server/Session';
19 |
20 |
21 | const App = () => (
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 | );
37 |
38 | export default withAuthentication(App);
39 |
--------------------------------------------------------------------------------
/bunker/src/commonComponents/CheckInOutCalendar.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { DatesRangeInput } from "semantic-ui-calendar-react";
4 | import * as moment from 'moment';
5 |
6 |
7 | const today=moment().format('MM-DD-YYYY');
8 | const aWeekFromToday = moment().add(5, 'days').format('MM-DD-YYYY');
9 | const defaultDateRangeArray = [today, aWeekFromToday];
10 | const defaultDateRange = defaultDateRangeArray.join(" - ");
11 |
12 | const CheckInOutCalendar = (props) => (
13 |
23 | )
24 |
25 | export default CheckInOutCalendar;
26 |
--------------------------------------------------------------------------------
/bunker/src/commonComponents/Navigation/Navigation.js:
--------------------------------------------------------------------------------
1 | import React, {Component} from "react";
2 |
3 | // Backend functionality
4 | import {AuthUserContext} from "../../server/Session";
5 | import * as ROUTES from "../../constants/routes";
6 | import * as ROLES from "../../constants/roles";
7 | import {withFirebase} from "../../server/Firebase/index";
8 |
9 | // Components
10 | import {Link} from "react-router-dom";
11 | import {Menu, Button, Dropdown, Image, Icon} from "semantic-ui-react";
12 | import BunkerImage from "../../images/bunker.png";
13 |
14 | const user = JSON.parse(localStorage.getItem("authUser"));
15 |
16 | class Navigation extends Component {
17 | constructor(props) {
18 | super(props);
19 |
20 | this.state = {
21 | firebase: null,
22 | location: "",
23 | activeItem: ""
24 | };
25 | }
26 |
27 | componentDidMount() {
28 | const authUser = this.props.authUser || {};
29 | this.setState({
30 | reward_points: authUser.reward_points || 0
31 | });
32 | }
33 |
34 | handleItemClick = (e, {name}) => {
35 | this.setState({activeItem: name});
36 | if (name === "signout") {
37 | this.signOut();
38 | }
39 | };
40 |
41 | signOut = () => {
42 | this.props.firebase.doSignOut();
43 | };
44 |
45 | render() {
46 | const {activeItem} = this.state.activeItem;
47 |
48 | const NavigationAuth = ({authUser}) => {
49 | const [rewardPoints, setRewardPoints] = React.useState(0);
50 | React.useEffect(
51 | () => {
52 | setRewardPoints(authUser.reward_points);
53 | },
54 | [authUser]
55 | );
56 | return (
57 |
58 |
59 |
60 |
65 | {/* */}
66 |
67 | Bunker
68 |
69 |
70 |
71 |
76 |
77 | Book Hotels
78 |
79 |
80 |
81 |
86 |
87 | Account
88 |
89 |
90 | {authUser.roles.includes(ROLES.ADMIN) && (
91 |
92 |
97 |
98 | )}
99 |
100 |
101 |
102 |
103 | Reward Points: {rewardPoints}
104 |
105 |
106 |
111 |
112 | My Reservations
113 |
114 |
115 |
116 |
121 |
122 | Sign Out
123 |
124 |
125 |
126 |
127 | );
128 | };
129 |
130 | const NavigationNonAuth = () => (
131 |
132 |
133 |
134 |
139 | {/* */}
140 |
141 | Bunker
142 |
143 |
144 |
145 |
150 |
151 | Book Hotels
152 |
153 |
154 |
155 |
156 |
157 |
162 |
163 | Sign In/Sign Up
164 |
165 |
166 |
167 |
168 |
169 | );
170 |
171 | return (
172 |
173 | {authUser =>
174 | authUser ? (
175 |
176 | ) : (
177 |
178 | )
179 | }
180 |
181 | );
182 | }
183 | }
184 |
185 | export default withFirebase(Navigation);
186 |
--------------------------------------------------------------------------------
/bunker/src/commonComponents/RoomQuantitySelect.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { Dropdown } from 'semantic-ui-react';
4 |
5 | import * as util from 'util' // has no default export
6 |
7 |
8 | const RoomQuantitySelect = (props) => {
9 | // console.log('props: ' + typeof(Integer(props.defaultValue)));
10 |
11 | let roomQuantityOptions = [];
12 | for(let i = 1; i < 17; i++){
13 | let obj = {
14 | key: i,
15 | text: i,
16 | value: i
17 | };
18 | roomQuantityOptions.push(obj);
19 | }
20 |
21 | return(
22 |
31 | )
32 | }
33 |
34 | export default RoomQuantitySelect;
35 |
--------------------------------------------------------------------------------
/bunker/src/commonComponents/RoomTypeSelect.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { Select } from 'semantic-ui-react';
4 |
5 | const roomTypeOptions = [
6 | {
7 | key: 'single',
8 | text: 'Single-Person',
9 | value: 'single'
10 | },
11 | {
12 | key: 'double',
13 | text: 'Double-Person',
14 | value: 'double'
15 | },
16 | {
17 | key: 'multiple',
18 | text: 'Multiple-Person',
19 | value: 'multiple'
20 | }
21 | ];
22 |
23 | const RoomTypeSelect = (props) => (
24 |
32 | )
33 |
34 | export default RoomTypeSelect;
35 |
--------------------------------------------------------------------------------
/bunker/src/constants/roles.js:
--------------------------------------------------------------------------------
1 | export const ADMIN = 'ADMIN';
2 |
--------------------------------------------------------------------------------
/bunker/src/constants/routes.js:
--------------------------------------------------------------------------------
1 | export const LANDING = '/';
2 | export const SIGN_UP = '/signup';
3 | export const SIGN_IN = '/signin';
4 | export const HOME = '/home';
5 | export const ACCOUNT = '/account';
6 | export const PASSWORD_FORGET = '/pw-forget';
7 | export const ADMIN = '/admin';
8 | export const ADMIN_DETAILS = '/admin/:id';
9 | export const HOTEL = '/hotel';
10 | export const HOTEL_DETAIL = "hotel/:id";
11 | export const HOTEL_RESERVATION='/reservation';
12 | //export const HOTEL_RESERVATION_DETAIL='/reservation/:id';
13 |
--------------------------------------------------------------------------------
/bunker/src/images/LandingBackground.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cmpe165spring2019/ReactApp/04c8604998d86d7e92afde4d95c532d5ac32bb95/bunker/src/images/LandingBackground.jpg
--------------------------------------------------------------------------------
/bunker/src/images/accountBackground.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cmpe165spring2019/ReactApp/04c8604998d86d7e92afde4d95c532d5ac32bb95/bunker/src/images/accountBackground.jpeg
--------------------------------------------------------------------------------
/bunker/src/images/accountBackground.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cmpe165spring2019/ReactApp/04c8604998d86d7e92afde4d95c532d5ac32bb95/bunker/src/images/accountBackground.jpg
--------------------------------------------------------------------------------
/bunker/src/images/accountBackground1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cmpe165spring2019/ReactApp/04c8604998d86d7e92afde4d95c532d5ac32bb95/bunker/src/images/accountBackground1.jpg
--------------------------------------------------------------------------------
/bunker/src/images/bunker.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cmpe165spring2019/ReactApp/04c8604998d86d7e92afde4d95c532d5ac32bb95/bunker/src/images/bunker.png
--------------------------------------------------------------------------------
/bunker/src/images/linkedin_banner_image_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cmpe165spring2019/ReactApp/04c8604998d86d7e92afde4d95c532d5ac32bb95/bunker/src/images/linkedin_banner_image_1.png
--------------------------------------------------------------------------------
/bunker/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | padding: 0;
4 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
5 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
6 | sans-serif;
7 | -webkit-font-smoothing: antialiased;
8 | -moz-osx-font-smoothing: grayscale;
9 | }
10 |
11 | code {
12 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
13 | monospace;
14 | }
15 |
--------------------------------------------------------------------------------
/bunker/src/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom";
3 | import App from "./App";
4 | import * as serviceWorker from "./serviceWorker";
5 | import Firebase, {FirebaseContext} from "./server/Firebase";
6 | import "./index.css";
7 | import "semantic-ui-css/semantic.min.css";
8 | ReactDOM.render(
9 |
10 |
11 | ,
12 | document.getElementById("root")
13 | );
14 |
15 | // If you want your app to work offline and load faster, you can change
16 | // unregister() to register() below. Note this comes with some pitfalls.
17 | // Learn more about service workers: http://bit.ly/CRA-PWA
18 | serviceWorker.unregister();
19 |
--------------------------------------------------------------------------------
/bunker/src/pages/Account/Account.js:
--------------------------------------------------------------------------------
1 | //This page can accessed by any registered user, but not guests
2 | //You can change your password, email, etc here
3 |
4 | import React, { Component } from 'react';
5 | import { compose } from 'recompose';
6 |
7 | import {
8 | AuthUserContext,
9 | withAuthorization,
10 | withEmailVerification,
11 | } from '../../server/Session';
12 | import { withFirebase } from '../../server/Firebase';
13 | import PasswordChangeForm from '../PasswordChange/PasswordChange';
14 | import {
15 | Button,
16 | Form,
17 | Grid,
18 | Header,
19 | Segment,
20 | Message,
21 | Icon,
22 | Input,
23 | } from 'semantic-ui-react';
24 | // import Background from "../../images/accountBackground.jpeg";
25 | import Background from "../../images/accountBackground1.jpg";
26 |
27 |
28 | // const SIGN_IN_METHODS = [
29 | // {
30 | // id: 'password',
31 | // provider: null,
32 | // },
33 | // {
34 | // id: 'google.com',
35 | // provider: 'googleProvider',
36 | // },
37 | // {
38 | // id: 'facebook.com',
39 | // provider: 'facebookProvider',
40 | // },
41 | // {
42 | // id: 'twitter.com',
43 | // provider: 'twitterProvider',
44 | // },
45 | // ];
46 | const backgroundStyle = {
47 | // width: "100%",
48 | // height: "100%",
49 | backgroundImage: `url(${Background})`,
50 | backgroundRepeat: "null",
51 | backgroundSize: 'cover',
52 | overflow: 'hidden',
53 | };
54 |
55 | const AccountPage = () => (
56 |
57 |
58 | {authUser => (
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 | {/* */}
68 | {"Welcome, "}{authUser.username}
69 |
70 |
71 |
72 |
73 | {authUser.email}
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 | {/* */}
94 |
95 |
96 | )}
97 |
98 | );
99 |
100 | const SocialLoginToggle = ({
101 | onlyOneLeft,
102 | isEnabled,
103 | signInMethod,
104 | onLink,
105 | onUnlink,
106 | }) =>
107 | isEnabled ?
108 | (
109 | onUnlink(signInMethod.id)}
112 | disabled={onlyOneLeft}
113 | >
114 | Deactivate {signInMethod.id}
115 |
116 | ) : (
117 | onLink(signInMethod.provider)}
120 | >
121 |
122 | Sign In with Facebook
123 |
124 | ) ;
125 |
126 | class DefaultLoginToggle extends Component {
127 | constructor(props) {
128 | super(props);
129 |
130 | this.state = { passwordOne: '', passwordTwo: '' };
131 | }
132 |
133 | onSubmit = event => {
134 | event.preventDefault();
135 |
136 | this.props.onLink(this.state.passwordOne);
137 | this.setState({ passwordOne: '', passwordTwo: '' });
138 | };
139 |
140 | onChange = event => {
141 | this.setState({ [event.target.name]: event.target.value });
142 | };
143 |
144 | render() {
145 | const {
146 | onlyOneLeft,
147 | isEnabled,
148 | signInMethod,
149 | onUnlink,
150 | } = this.props;
151 |
152 | const { passwordOne, passwordTwo } = this.state;
153 |
154 | const isInvalid =
155 | passwordOne !== passwordTwo || passwordOne === '';
156 |
157 | return isEnabled ? (
158 |
159 | onUnlink(signInMethod.id)}
162 | disabled={onlyOneLeft}
163 | >
164 | Deactivate {signInMethod.id}
165 |
166 | ) : (
167 |
168 | */}
178 | {/* */}
185 |
186 | {/**/}
187 | {/*Link {signInMethod.id}*/}
188 | {/* */}
189 |
190 |
191 | );
192 | }
193 | }
194 |
195 | // const LoginManagement = withFirebase(LoginManagementBase);
196 |
197 | const condition = authUser => !!authUser;
198 |
199 | export default compose(
200 | withEmailVerification,
201 | withAuthorization(condition),
202 | )(AccountPage);
203 |
--------------------------------------------------------------------------------
/bunker/src/pages/Admin/Admin.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Switch, Route } from 'react-router-dom';
3 | import { compose } from 'recompose';
4 |
5 | import { withAuthorization, withEmailVerification } from '../../server/Session';
6 | import { UserList, UserItem } from '../Users';
7 | import * as ROLES from '../../constants/roles';
8 | import * as ROUTES from '../../constants/routes';
9 |
10 | const AdminPage = () => (
11 |
12 |
Admin
13 |
The Admin Page is accessible by every signed in admin user.
14 |
15 |
16 |
17 |
18 |
19 |
20 | );
21 |
22 | const condition = authUser =>
23 | authUser && authUser.roles.includes(ROLES.ADMIN);
24 |
25 | export default compose(
26 | withEmailVerification,
27 | withAuthorization(condition),
28 | )(AdminPage);
29 |
--------------------------------------------------------------------------------
/bunker/src/pages/Home/Home.js:
--------------------------------------------------------------------------------
1 | // This page can be accessed by anyone including guests
2 |
3 | import React, { Component } from "react";
4 |
5 | //Components
6 | import SearchBar from "./components/SearchBar";
7 | import ListingBase from "./components/ListingBase";
8 | import FilterSort from "./components/FilterSort";
9 | import { Divider, Grid, Container, Segment } from "semantic-ui-react";
10 | import * as moment from "moment";
11 | import Maps from "./components/Maps";
12 | import './sticky.css'
13 |
14 | // Backend functionalities
15 | import { withFirebase } from '../../server/Firebase/index';
16 |
17 | //Debugging purposes
18 | import * as util from 'util' // has no default export
19 |
20 | class HomePage extends Component {
21 | constructor(props) {
22 | super(props);
23 | this.state = {
24 | allHotels: [],
25 | filteredHotels: [],
26 | searchedSortedHotels: [],
27 | locationOptions: [],
28 | datesRange: "",
29 | search: {
30 | location: {},
31 | checkInDate: null,
32 | checkOutDate: null,
33 | roomType: "single",
34 | roomQuantity: 1
35 | },
36 | filter: {
37 | x: 100,
38 | price: 1000,
39 | minPrice: 0,
40 | maxPrice: 1000,
41 | rating: 0
42 | },
43 | defaultLocationValue: "",
44 | sort: "ratingHL"
45 | };
46 | }
47 |
48 | componentDidUpdate() {
49 | console.log(this.state);
50 | }
51 |
52 | componentDidMount() {
53 |
54 | //set initial values of checkIn/Out calendar
55 | const today=moment().format('MM-DD-YYYY');
56 | const aWeekFromToday = moment().add(5, 'days').format('MM-DD-YYYY');
57 |
58 | let locationOptions = [];
59 | const historyState = this.props.location.state || {};
60 |
61 | if(historyState.locationID){
62 | this.props.firebase.locationRef(historyState.locationID).then(
63 | snapshot =>{
64 | const location = {id: snapshot.id, data: snapshot.data()};
65 | this.setState({
66 | search: {
67 | ...this.state.search,
68 |
69 | location: location,
70 | passedLocation: true
71 | }
72 | })
73 | }
74 | )
75 | }
76 |
77 | this.props.firebase.getCities()
78 | .then(locationData => {
79 | locationData.sort((a,b)=>{
80 | if(a.data.city.toLowerCase() > b.data.city.toLowerCase()){
81 | return 1
82 | }
83 | else{
84 | return -1
85 | }
86 | })
87 |
88 | locationOptions = locationData.map(
89 | (location) =>
90 | ({
91 | key: location.data.city,
92 | text: `${location.data.city} , ${location.data.state}, ${location.data.country}`,
93 | value: location
94 | })
95 | );
96 |
97 |
98 | });
99 |
100 |
101 | this.props.firebase.getAllHotels()
102 | .then(result => {
103 | this.setState({
104 | datesRange: historyState.datesRange || (today + " - " + aWeekFromToday),
105 | locationOptions: locationOptions,
106 | allHotels: result,
107 | searchedSortedHotels: result,
108 | filteredHotels: result,
109 | },
110 | ()=>{
111 | this.handleSearch();
112 | });
113 | });
114 |
115 | }
116 |
117 | handleCheckInOut = (event, { name, value }) => {
118 | // console.log("name: " + name + " value: " + value);
119 | if (this.state.hasOwnProperty(name)) {
120 | this.setState({ [name]: value });
121 | }
122 |
123 | //parse the dates into checkInDate and checkOutDate as Date objects after the user clicks the 2nd date
124 | if (value.length > 13) {
125 | let parsedValue = value.split(" ");
126 | let checkInString = parsedValue[0];
127 | let checkOutString = parsedValue[2];
128 | let checkInArray = checkInString.split("-");
129 | let checkOutArray = checkOutString.split("-");
130 | let checkInDate = new Date(
131 | parseInt(checkInArray[2]),
132 | parseInt(checkInArray[0] - 1),
133 | parseInt(checkInArray[1])
134 | );
135 | let checkOutDate = new Date(
136 | parseInt(checkOutArray[2]),
137 | parseInt(checkOutArray[0] - 1),
138 | parseInt(checkOutArray[1])
139 | );
140 | this.setState({
141 | search: {
142 | ...this.state.search,
143 | checkInDate: checkInDate,
144 | checkOutDate: checkOutDate
145 | }
146 | });
147 |
148 | // console.log("check in :" + checkInDate + " check out: " + checkOutDate);
149 | }
150 | };
151 |
152 | handleRoomTypeQuantity = (e, { name, value }) => {
153 | this.setState({
154 | search: {
155 | ...this.state.search,
156 | [name]: value
157 | }
158 | });
159 | };
160 |
161 | handleLocation = (e, { name, value }) => {
162 | // console.log(value);
163 | this.setState({
164 | search: {
165 | ...this.state.search,
166 | location: value
167 | }
168 | });
169 | };
170 |
171 | handleSearch = e => {
172 | //*** */BACK-END IMPLEMENTATION:
173 | // filter hotels[] by search criteria & store into searchedHotels[]
174 | let searchedHotels = this.state.searchedSortedHotels;
175 |
176 | // filter by location
177 | if (this.state.search.location.hasOwnProperty("data")) {
178 | const { city, state, country } = this.state.search.location.data;
179 | const roomType = this.state.search;
180 | if (city && state && country) {
181 | searchedHotels = this.state.allHotels.filter(
182 | hotel =>
183 | hotel.data.address.city
184 | .toLowerCase()
185 | .includes(city.toLowerCase()) &&
186 | hotel.data.address.state
187 | .toLowerCase()
188 | .includes(state.toLowerCase()) &&
189 | hotel.data.address.country
190 | .toLowerCase()
191 | .includes(country.toLowerCase())
192 | );
193 | }
194 | }
195 |
196 | // filter by room type
197 | // console.log("roomType is: " + this.state.search.roomType);
198 | // console.log("hotel data room types: " + this.state.allHotels[0].data.room_types)
199 | let maxRoomPrice, minRoomPrice;
200 | if (this.state.search.roomType) {
201 | let roomTypePrices = [];
202 | searchedHotels = searchedHotels.filter(hotel => {
203 | let roomTypeOfHotel = hotel.data.room_types.filter(
204 | room_type => room_type.type === this.state.search.roomType
205 | );
206 | let roomTypePrice = roomTypeOfHotel[0].price;
207 |
208 | // push price onto array to check for max/min at the end of if statement
209 | roomTypePrices.push(roomTypePrice);
210 |
211 | // set the price of rooms based on the current roomType search criteria to an easily accessible attribute (hotel.data.currentRoomPrice)
212 | hotel.data.currentRoomPrice = roomTypePrice;
213 | return roomTypeOfHotel;
214 | });
215 | // check for min and max to display range of slider for each roomType search
216 | maxRoomPrice = Math.max(...roomTypePrices);
217 | minRoomPrice = Math.min(...roomTypePrices);
218 | }
219 |
220 | //***filter by dateRanges***
221 | // NEED TO IMPLEMENT
222 |
223 | //sort the hotels
224 | let searchedSortedHotels = this.sortHotels(searchedHotels, this.state.sort);
225 |
226 | if (searchedSortedHotels !== this.state.searchedSortedHotels) {
227 | // set state of searchedHotels[]
228 | this.setState(
229 | {
230 | searchedSortedHotels: searchedSortedHotels,
231 | filteredHotels: searchedSortedHotels,
232 | filter: {
233 | ...this.state.filter,
234 | maxPrice: maxRoomPrice,
235 | minPrice: minRoomPrice,
236 | price: maxRoomPrice
237 | }
238 | },
239 | () => {
240 | this.handleFilter();
241 | }
242 | );
243 | }
244 |
245 | // call methods to re-filter and re-sort hotel cards
246 | // set state of filteredHotels[]
247 | // page gets re-rendered & displays filteredHotels[]
248 | };
249 |
250 | handleSort = (e, { name, value }) => {
251 | //sets the state of sort
252 | this.setState({
253 | sort: value
254 | });
255 | this.sortHotels(this.state.filteredHotels, value);
256 | };
257 |
258 | sortHotels = (hotels, value) => {
259 | // const filteredHotels = this.state.filteredHotels;
260 | const filteredHotels = hotels;
261 | let sortedHotels = hotels;
262 |
263 | if (value.includes("rating")) {
264 | if (value.includes("LH")) {
265 | sortedHotels = filteredHotels.sort((a, b) => {
266 | return a.data.rating - b.data.rating;
267 | });
268 | } else {
269 | sortedHotels = filteredHotels.sort((a, b) => {
270 | return b.data.rating - a.data.rating;
271 | });
272 | }
273 | } else if (value.includes("price")) {
274 | if (value.includes("LH")) {
275 | sortedHotels = filteredHotels.sort((a, b) => {
276 | return a.data.currentRoomPrice - b.data.currentRoomPrice;
277 | });
278 | } else {
279 | sortedHotels = filteredHotels.sort((a, b) => {
280 | return b.data.currentRoomPrice - a.data.currentRoomPrice;
281 | });
282 | }
283 | } else if (value.includes("name")) {
284 | if (value.includes("AZ")) {
285 | sortedHotels = filteredHotels.sort((a, b) => {
286 | let x = a.data.name.toLowerCase();
287 | let y = b.data.name.toLowerCase();
288 | if (x < y) return -1;
289 | else if (x > y) return 1;
290 | else return 0;
291 | });
292 | } else {
293 | sortedHotels = filteredHotels.sort((a, b) => {
294 | let x = a.data.name.toLowerCase();
295 | let y = b.data.name.toLowerCase();
296 | if (x < y) return 1;
297 | else if (x > y) return -1;
298 | else return 0;
299 | });
300 | }
301 | }
302 |
303 | if (sortedHotels !== filteredHotels) {
304 | this.setState({
305 | searchedSortedHotels: sortedHotels,
306 | filteredHotels: sortedHotels
307 | });
308 | }
309 |
310 | return sortedHotels;
311 | };
312 |
313 | handleSlider = e => {
314 | this.setState(
315 | {
316 | filter: {
317 | ...this.state.filter,
318 | price:
319 | ((this.state.filter.maxPrice - this.state.filter.minPrice) * e.x) /
320 | 100 +
321 | this.state.filter.minPrice,
322 | x: e.x
323 | }
324 | },
325 | () => {
326 | this.handleFilter('price');
327 | }
328 | );
329 | };
330 |
331 | handleRating = (e, { name, value }) => {
332 | console.log("name: " + name + "value: " + value);
333 | this.setState(
334 | {
335 | filter: {
336 | ...this.state.filter,
337 | rating: value
338 | }
339 | },
340 | () => {
341 | this.handleFilter('rating');
342 | }
343 | );
344 | };
345 |
346 | handleFilter = (type) => {
347 | //update state for filteredHotels
348 | const searchedSortedHotels = this.state.searchedSortedHotels;
349 | const price = this.state.filter.price;
350 | const rating = this.state.filter.rating;
351 |
352 | let filteredHotels = this.state.filteredHotels;
353 |
354 | //filter by rating
355 | if(type==='rating'){
356 | filteredHotels = searchedSortedHotels.filter(
357 | hotel => hotel.data.rating >= rating
358 | );
359 | filteredHotels = filteredHotels.filter(
360 | hotel => {
361 | const roomTypeData = hotel.data.room_types.filter(roomType=> roomType.type === this.state.search.roomType);
362 | const roomPrice = roomTypeData[0].price;
363 | hotel.data.currentRoomPrice = roomPrice;
364 | return(
365 | roomPrice <= price
366 | );
367 | }
368 | );
369 | }
370 |
371 | // filter by price
372 | else if(type==='price'){
373 | filteredHotels = searchedSortedHotels.filter(
374 | hotel => {
375 | const roomTypeData = hotel.data.room_types.filter(roomType=> roomType.type === this.state.search.roomType);
376 | const roomPrice = roomTypeData[0].price;
377 | hotel.data.currentRoomPrice = roomPrice;
378 | return(
379 | roomPrice <= price
380 | );
381 | }
382 | );
383 |
384 | filteredHotels = filteredHotels.filter(
385 | hotel => hotel.data.rating >= rating
386 | );
387 | }
388 |
389 | this.sortHotels(filteredHotels, this.state.sort)
390 |
391 | if(filteredHotels!==this.state.filteredHotels){
392 | this.setState({
393 | filteredHotels: filteredHotels
394 |
395 | });
396 | }
397 | }
398 | // contextRef = createRef()
399 | render() {
400 | console.log('render location: ' + util.inspect(this.state.search.location));
401 | return (
402 |
403 |
415 |
427 |
428 |
429 |
430 |
431 |
432 |
438 |
439 |
440 |
441 |
446 |
447 |
448 |
449 |
450 |
451 |
452 |
453 |
454 | )
455 | }
456 | }
457 |
458 | export default withFirebase(HomePage);
459 |
--------------------------------------------------------------------------------
/bunker/src/pages/Home/components/FilterSort.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Grid, Menu, Container, Icon, Select, Segment } from 'semantic-ui-react';
3 | import Slider from 'react-input-slider';
4 |
5 | const RatingFilter = () => {
6 | let starOptions = [];
7 | for (let i = 0; i < 5; i++) {
8 | let obj = {
9 | key: i,
10 | text: i + "+",
11 | value: i
12 | };
13 | starOptions.push(obj);
14 | }
15 | return (
16 | starOptions
17 | );
18 | }
19 |
20 | const ratingOptions = RatingFilter();
21 |
22 | const sortOptions = [
23 | {
24 | key: 'ratingHL',
25 | text: 'Rating (High to Low)',
26 | value: 'ratingHL'
27 | },
28 | {
29 | key: 'ratingLH',
30 | text: 'Rating (Low to High)',
31 | value: 'ratingLH'
32 | },
33 | {
34 | key: 'priceHL',
35 | text: 'Price (High to Low)',
36 | value: 'priceHL'
37 | },
38 | {
39 | key: 'priceLH',
40 | text: 'Price (Low to High)',
41 | value: 'priceLH'
42 | },
43 | {
44 | key: 'nameAZ',
45 | text: 'Name (A-Z)',
46 | value: 'nameAZ'
47 | },
48 | {
49 | key: 'nameZA',
50 | text: 'Name (Z-A)',
51 | value: 'nameZA'
52 | },
53 | ];
54 |
55 | const SearchFilter = (props) => (
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 | Price: ${props.minPrice} to ${Math.round(props.price)}
64 |
65 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 | Rating:
78 |
79 |
85 |
86 |
87 |
88 |
89 |
90 | Sort By:
91 |
92 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 | )
106 |
107 | export default SearchFilter;
108 |
--------------------------------------------------------------------------------
/bunker/src/pages/Home/components/HotelCard.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Grid, Card, Icon, Image, Segment, List, Divider } from 'semantic-ui-react'
3 |
4 | const HotelCard = (props) => {
5 | const {image, name, currentRoomPrice, address, rating, details} = props.hotel.data;
6 | return (
7 |
8 |
9 |
10 | {name}
11 |
12 | {`${address.city}, ${address.country}`}
13 |
14 |
15 |
16 | {details.split(", ").slice(0,5).map(item => (
17 | {item}
18 |
19 | ))}
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | {currentRoomPrice} / night
28 |
29 |
30 |
31 | {rating}
32 |
33 |
34 |
35 | )}
36 |
37 | export default HotelCard;
38 |
--------------------------------------------------------------------------------
/bunker/src/pages/Home/components/ListingBase.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 | import * as ROUTES from "../../../constants/routes";
3 | import { Link } from "react-router-dom";
4 | import HotelCard from "./HotelCard";
5 | import { Grid, Select } from 'semantic-ui-react';
6 | import * as util from 'util' // has no default export
7 |
8 |
9 |
10 |
11 | export default class ListingBase extends Component {
12 | constructor(props) {
13 | super(props);
14 | this.state = {
15 | datesRange:'',
16 | roomtype:'',
17 | };
18 | }
19 | componentDidUpdate(prevProps){
20 | if(this.props.datesRange!== this.state.datesRange){
21 | this.setState({
22 | datesRange:this.props.datesRange,
23 | })
24 | }
25 |
26 | }
27 |
28 | render() {
29 |
30 | const { datesRange, roomType, roomQuantity } = this.props;
31 |
32 | return (
33 |
34 |
35 |
36 | {
37 | this.props.hotels.map(hotel => {
38 | // const roomTypeData = hotel.data.room_types.filter(roomType=> roomType.type === this.props.roomType);
39 | // const price = roomTypeData[0].price;
40 | const price = hotel.data.currentRoomPrice;
41 | return(
42 |
43 |
48 |
51 |
52 |
53 | );
54 | })
55 | }
56 |
57 |
58 |
59 | );
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/bunker/src/pages/Home/components/Maps.js:
--------------------------------------------------------------------------------
1 | import { Map, InfoWindow, Marker, GoogleApiWrapper } from "google-maps-react";
2 | import { withRouter } from "react-router-dom";
3 | import React, { Component } from "react";
4 | import { Link } from "react-router-dom";
5 | import * as ROUTES from "../../../constants/routes";
6 | import { Segment, Image, Button } from "semantic-ui-react";
7 | // Backend functionalities
8 | import { withFirebase } from "../../../server/Firebase/index";
9 |
10 | export class MapContainer extends Component {
11 | constructor(props) {
12 | super(props);
13 | this.state = {
14 | showingInfoWindow: false,
15 | activeMarker: {},
16 | selectedPlace: {}
17 | };
18 | }
19 |
20 | onMarkerClick = (props, marker, e) => {
21 | this.setState({
22 | selectedPlace: props,
23 | activeMarker: marker,
24 | showingInfoWindow: true
25 | });
26 | };
27 |
28 | onMapClicked = props => {
29 | if (this.state.showingInfoWindow) {
30 | this.setState({
31 | showingInfoWindow: false,
32 | activeMarker: null
33 | });
34 | }
35 | };
36 | onClick = () => {
37 | console.log("123");
38 | };
39 |
40 | //loop this marker
41 | render() {
42 | const { datesRange, roomType, roomQuantity } = this.props;
43 | return (
44 |
45 |
54 | {this.props.hotels.map(hotel => {
55 | return (
56 |
67 | );
68 | })}
69 |
70 |
74 |
75 | {this.state.selectedPlace.name}
76 |
77 | {/*Hotel */}
78 |
79 |
80 |
81 |
82 | );
83 | }
84 | }
85 |
86 | export default GoogleApiWrapper({
87 | apiKey: "AIzaSyBEBj6JuJ5LgehsCQtT3cF2d3Qloo84KC0"
88 | })(withRouter(MapContainer));
89 |
--------------------------------------------------------------------------------
/bunker/src/pages/Home/components/MapsHotel.js:
--------------------------------------------------------------------------------
1 | import { Map, InfoWindow, Marker, GoogleApiWrapper } from "google-maps-react";
2 | import React, { Component } from "react";
3 | import { Link } from "react-router-dom";
4 | import * as ROUTES from "../../../constants/routes";
5 |
6 | // Backend functionalities
7 | import { withFirebase } from "../../../server/Firebase/index";
8 |
9 | export class MapContainer extends Component {
10 | constructor(props) {
11 | super(props);
12 | this.state = {
13 | showingInfoWindow: false,
14 | activeMarker: {},
15 | selectedPlace: {}
16 | };
17 | }
18 |
19 | onMarkerClick = (props, marker, e) =>
20 | this.setState({
21 | selectedPlace: props,
22 | activeMarker: marker,
23 | showingInfoWindow: true
24 | });
25 |
26 | onMapClicked = props => {
27 | if (this.state.showingInfoWindow) {
28 | this.setState({
29 | showingInfoWindow: false,
30 | activeMarker: null
31 | });
32 | }
33 | };
34 |
35 | //loop this marker
36 | render() {
37 | return (
38 |
39 |
47 |
55 |
56 |
60 |
61 |
{this.state.selectedPlace.name}
62 |
63 |
64 |
65 |
66 | );
67 | }
68 | }
69 |
70 | export default GoogleApiWrapper({
71 | apiKey: "AIzaSyBEBj6JuJ5LgehsCQtT3cF2d3Qloo84KC0"
72 | })(MapContainer);
73 |
--------------------------------------------------------------------------------
/bunker/src/pages/Home/components/SearchBar.js:
--------------------------------------------------------------------------------
1 | // This component contains the search bar with filters for users to narrow down their search based on date, guests, price, location, stars
2 | import * as ROUTES from "../../../constants/routes";
3 | import {Link} from "react-router-dom";
4 | import React, { Component } from "react";
5 | import {
6 | Button,
7 | Grid,
8 | Menu,
9 | Input,
10 | Dropdown,
11 | Segment,
12 | Image,
13 | Icon,
14 | Container
15 | } from "semantic-ui-react";
16 | import BunkerImage from '../../../images/bunker.png';
17 |
18 |
19 | import * as moment from 'moment';
20 |
21 | import {withFirebase} from '../../../server/Firebase';
22 | import CheckInOutCalendar from '../../../commonComponents/CheckInOutCalendar';
23 | import RoomTypeSelect from "../../../commonComponents/RoomTypeSelect";
24 | import RoomQuantitySelect from '../../../commonComponents/RoomQuantitySelect';
25 |
26 | import * as util from 'util' // has no default export
27 |
28 |
29 | class SearchBar extends Component {
30 | constructor(props) {
31 | super(props);
32 | this.state={
33 |
34 | }
35 | }
36 |
37 |
38 |
39 | render() {
40 | return (
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 | Location:
57 |
58 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 | Check In/Out:
77 |
78 |
82 |
83 |
84 |
85 |
86 |
87 | Room Type/Quantity:
88 |
92 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
108 |
109 | Search
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 | );
122 | }
123 | }
124 |
125 | export default withFirebase(SearchBar);
126 |
--------------------------------------------------------------------------------
/bunker/src/pages/Home/dummydata.js:
--------------------------------------------------------------------------------
1 | export const DUMMYHOTELS = [
2 | {
3 | uid: "1",
4 | name: "Park Inn San Jose by Radisson",
5 | location: "San Jose, CA",
6 | rating: "4",
7 | image1:
8 | "https://s-ec.bstatic.com/images/hotel/max1024x768/681/68184730.jpg",
9 | image2:
10 | "https://s-ec.bstatic.com/images/hotel/max1280x900/151/151408199.jpg",
11 | image3:
12 | "https://t-ec.bstatic.com/images/hotel/max1280x900/144/144211371.jpg",
13 | image4:
14 | "https://s-ec.bstatic.com/images/hotel/max1280x900/512/51237756.jpg",
15 | image5:
16 | "https://s-ec.bstatic.com/images/hotel/max1280x900/144/144210300.jpg",
17 | detail: "Free Wifi & Parking & Gym ",
18 | address: "1111",
19 | price: "80",
20 | oneBedPrice: "175",
21 | twoBedPrice: "275",
22 | LuxuryPrice: "375",
23 | oneBedCap: "2",
24 | twoBesCap: "4",
25 | LuxuryCap: "6",
26 | description:
27 | "Walking distance from cal train station and Downtown San Jose"
28 | },
29 |
30 | {
31 | uid: "1",
32 | name: "Fairmont",
33 | location: "San Jose, CA",
34 | rating: "3",
35 | image1:
36 | "https://thumbnails.trvl-media.com/vff-vkeZvCEFxU78UgLUmpictkY=/773x530/smart/filters:quality(60)/images.trvl-media.com/hotels/1000000/20000/18200/18200/397a578b_z.jpg",
37 | image2:
38 | "https://s-ec.bstatic.com/images/hotel/max1280x900/151/151408199.jpg",
39 | image3:
40 | "https://t-ec.bstatic.com/images/hotel/max1280x900/144/144211371.jpg",
41 | image4:
42 | "https://s-ec.bstatic.com/images/hotel/max1280x900/512/51237756.jpg",
43 | image5:
44 | "https://s-ec.bstatic.com/images/hotel/max1280x900/144/144210300.jpg",
45 | detail: "Free Wifi & Parking & Gym ",
46 | address: "1111",
47 | price: "33",
48 | oneBedPrice: "200",
49 | twoBedPrice: "275",
50 | LuxuryPrice: "375",
51 | oneBedCap: "2",
52 | twoBesCap: "4",
53 | LuxuryCap: "6",
54 | description:
55 | "Luxurious complex with swimming pool, spa, and massage. Hiking lessons and camping available."
56 | },
57 |
58 | {
59 | uid: "0",
60 | name: "Fairmont",
61 | location: "San Jose, CA",
62 | rating: "2",
63 | image1:
64 | "https://thumbnails.trvl-media.com/vff-vkeZvCEFxU78UgLUmpictkY=/773x530/smart/filters:quality(60)/images.trvl-media.com/hotels/1000000/20000/18200/18200/397a578b_z.jpg",
65 | image2:
66 | "https://s-ec.bstatic.com/images/hotel/max1280x900/151/151408199.jpg",
67 | image3:
68 | "https://t-ec.bstatic.com/images/hotel/max1280x900/144/144211371.jpg",
69 | image4:
70 | "https://s-ec.bstatic.com/images/hotel/max1280x900/512/51237756.jpg",
71 | image5:
72 | "https://s-ec.bstatic.com/images/hotel/max1280x900/144/144210300.jpg",
73 | detail: "Free Wifi & Parking & Gym ",
74 | address: "1111",
75 | price: "22",
76 | oneBedPrice: "200",
77 | twoBedPrice: "275",
78 | LuxuryPrice: "375",
79 | oneBedCap: "2",
80 | twoBesCap: "4",
81 | LuxuryCap: "6",
82 | description:
83 | "Luxurious complex with swimming pool, spa, and massage. Hiking lessons and camping available."
84 | },
85 | {
86 | uid: "5",
87 | name: "Fairmont",
88 | location: "San Jose, CA",
89 | rating: "2",
90 | image1:
91 | "https://thumbnails.trvl-media.com/vff-vkeZvCEFxU78UgLUmpictkY=/773x530/smart/filters:quality(60)/images.trvl-media.com/hotels/1000000/20000/18200/18200/397a578b_z.jpg",
92 | image2:
93 | "https://s-ec.bstatic.com/images/hotel/max1280x900/151/151408199.jpg",
94 | image3:
95 | "https://t-ec.bstatic.com/images/hotel/max1280x900/144/144211371.jpg",
96 | image4:
97 | "https://s-ec.bstatic.com/images/hotel/max1280x900/512/51237756.jpg",
98 | image5:
99 | "https://s-ec.bstatic.com/images/hotel/max1280x900/144/144210300.jpg",
100 | detail: "Free Wifi & Parking & Gym ",
101 | address: "1111",
102 | price: "99",
103 | oneBedPrice: "200",
104 | twoBedPrice: "275",
105 | LuxuryPrice: "375",
106 | oneBedCap: "2",
107 | twoBesCap: "4",
108 | LuxuryCap: "6",
109 | description:
110 | "Luxurious complex with swimming pool, spa, and massage. Hiking lessons and camping available."
111 | },
112 | {
113 | uid: "2",
114 | name: "Fairmont",
115 | location: "San Jose, CA",
116 | rating: "2",
117 | image1:
118 | "https://thumbnails.trvl-media.com/vff-vkeZvCEFxU78UgLUmpictkY=/773x530/smart/filters:quality(60)/images.trvl-media.com/hotels/1000000/20000/18200/18200/397a578b_z.jpg",
119 | image2:
120 | "https://s-ec.bstatic.com/images/hotel/max1280x900/151/151408199.jpg",
121 | image3:
122 | "https://t-ec.bstatic.com/images/hotel/max1280x900/144/144211371.jpg",
123 | image4:
124 | "https://s-ec.bstatic.com/images/hotel/max1280x900/512/51237756.jpg",
125 | image5:
126 | "https://s-ec.bstatic.com/images/hotel/max1280x900/144/144210300.jpg",
127 | detail: "Free Wifi & Parking & Gym ",
128 | address: "1111",
129 | price: "30",
130 | oneBedPrice: "200",
131 | twoBedPrice: "275",
132 | LuxuryPrice: "375",
133 | oneBedCap: "2",
134 | twoBesCap: "4",
135 | LuxuryCap: "6",
136 | description:
137 | "Luxurious complex with swimming pool, spa, and massage. Hiking lessons and camping available."
138 | },
139 | {
140 | uid: "2",
141 | name: "Fairmont",
142 | location: "San Jose, CA",
143 | rating: "2",
144 | image1:
145 | "https://thumbnails.trvl-media.com/vff-vkeZvCEFxU78UgLUmpictkY=/773x530/smart/filters:quality(60)/images.trvl-media.com/hotels/1000000/20000/18200/18200/397a578b_z.jpg",
146 | image2:
147 | "https://s-ec.bstatic.com/images/hotel/max1280x900/151/151408199.jpg",
148 | image3:
149 | "https://t-ec.bstatic.com/images/hotel/max1280x900/144/144211371.jpg",
150 | image4:
151 | "https://s-ec.bstatic.com/images/hotel/max1280x900/512/51237756.jpg",
152 | image5:
153 | "https://s-ec.bstatic.com/images/hotel/max1280x900/144/144210300.jpg",
154 | detail: "Free Wifi & Parking & Gym ",
155 | address: "1111",
156 | price: "40",
157 | oneBedPrice: "200",
158 | twoBedPrice: "275",
159 | LuxuryPrice: "375",
160 | oneBedCap: "2",
161 | twoBesCap: "4",
162 | LuxuryCap: "6",
163 | description:
164 | "Luxurious complex with swimming pool, spa, and massage. Hiking lessons and camping available."
165 | },
166 | {
167 | uid: "2",
168 | name: "Fairmont",
169 | location: "San Jose, CA",
170 | rating: "2",
171 | image1:
172 | "https://thumbnails.trvl-media.com/vff-vkeZvCEFxU78UgLUmpictkY=/773x530/smart/filters:quality(60)/images.trvl-media.com/hotels/1000000/20000/18200/18200/397a578b_z.jpg",
173 | image2:
174 | "https://s-ec.bstatic.com/images/hotel/max1280x900/151/151408199.jpg",
175 | image3:
176 | "https://t-ec.bstatic.com/images/hotel/max1280x900/144/144211371.jpg",
177 | image4:
178 | "https://s-ec.bstatic.com/images/hotel/max1280x900/512/51237756.jpg",
179 | image5:
180 | "https://s-ec.bstatic.com/images/hotel/max1280x900/144/144210300.jpg",
181 | detail: "Free Wifi & Parking & Gym ",
182 | address: "1111",
183 | price: "50",
184 | oneBedPrice: "200",
185 | twoBedPrice: "275",
186 | LuxuryPrice: "375",
187 | oneBedCap: "2",
188 | twoBesCap: "4",
189 | LuxuryCap: "6",
190 | description:
191 | "Luxurious complex with swimming pool, spa, and massage. Hiking lessons and camping available."
192 | },
193 | ]
194 |
195 | export default {
196 | DUMMYHOTELS,
197 | }
--------------------------------------------------------------------------------
/bunker/src/pages/Home/sticky.css:
--------------------------------------------------------------------------------
1 | .sticky {
2 | position: -webkit-sticky;
3 | position: sticky;
4 | top: 0;
5 | }
6 |
--------------------------------------------------------------------------------
/bunker/src/pages/Hotel/Carousel.js:
--------------------------------------------------------------------------------
1 | // import {withFirebase} from "../../server/Firebase";
2 | // import React, {Component} from "react";
3 | // // import Carousel from 'react-bootstrap/Carousel';
4 | // import Carousel from 'semantic-ui-carousel-react';
5 | // import { Image, Button } from 'semantic-ui-react'
6 | //
7 | // class ControlledCarousel extends React.Component {
8 | //
9 | // constructor(props, context) {
10 | // super(props, context);
11 | //
12 | // this.handleSelect = this.handleSelect.bind(this);
13 | //
14 | // this.state = {
15 | // index: 0,
16 | // direction: null,
17 | // };
18 | // const images = this.props.images;
19 | // // console.log(images)
20 | //
21 | // }
22 | //
23 | // handleSelect(selectedIndex, e) {
24 | // this.setState({
25 | // index: selectedIndex,
26 | // direction: e.direction,
27 | // });
28 | // }
29 | //
30 | // render() {
31 | // const { index, direction } = this.state;
32 | // // const image1 = images[0];
33 | //
34 | // return (
35 | //
41 | //
42 | //
46 | //
47 | // First slide label
48 | // Nulla vitae elit libero, a pharetra augue mollis interdum.
49 | //
50 | //
51 | //
52 | //
57 | //
58 | //
59 | // Second slide label
60 | // Lorem ipsum dolor sit amet, consectetur adipiscing elit.
61 | //
62 | //
63 | //
64 | //
69 | //
70 | //
71 | // Third slide label
72 | //
73 | // Praesent commodo cursus magna, vel scelerisque nisl consectetur.
74 | //
75 | //
76 | //
77 | //
78 | // );
79 | // }
80 | // }
81 | //
82 | //
83 | // export default withFirebase(ControlledCarousel);
--------------------------------------------------------------------------------
/bunker/src/pages/Hotel/CheckOut/CheckOut.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import {Modal, Button, Message, Image, Segment, Grid} from "semantic-ui-react";
3 | import {withFirebase} from "../../../server/Firebase";
4 | import CheckOutForm from "./CheckOutForm";
5 | import PayPalButton from "../../../server/Payment/PayPalButton";
6 | import {withAuthentication} from "../../../server/Session";
7 |
8 | const CheckOut = props => {
9 | const [isError, setIsError] = React.useState(false);
10 | const [isUseReward, setIsUseReward] = React.useState(false);
11 | const [error, setError] = React.useState(new Error("null"));
12 | const [isSuccess, setIsSuccess] = React.useState(false);
13 | const [user, setUser] = React.useState({});
14 | React.useEffect(
15 | () => {
16 | setUser(props.authUser);
17 | },
18 | [props.authUser]
19 | );
20 |
21 | const handleUseReward = () => {
22 | setIsUseReward(!isUseReward);
23 | };
24 | const {reservation, hotel, datesRange} = props;
25 |
26 | const onSuccess = payment => {
27 | console.log("Successful payment!", payment);
28 | const reservation_with_payment = {
29 | user_id: user.uid,
30 | payment: payment,
31 | hotel_id: hotel.id,
32 | datesRange,
33 | ...reservation
34 | };
35 | console.log(reservation_with_payment);
36 | props.firebase.addReservationToDB(
37 | user.uid,
38 | reservation_with_payment,
39 | isUseReward,
40 | user.reward_points
41 | );
42 | setIsSuccess(payment.paid);
43 | setIsError(false);
44 | };
45 |
46 | const onCancel = data => {
47 | console.log("Cancel: ", data);
48 | };
49 |
50 | const onError = paypalError => {
51 | console.log(paypalError);
52 | setError(paypalError);
53 | setIsSuccess(false);
54 | setIsError(true);
55 | };
56 |
57 | return (
58 |
69 | Book now
70 |
71 | }
72 | >
73 | Payment confirmation
74 |
75 |
76 |
77 |
83 |
84 |
85 |
86 | {user ? (
87 |
88 |
89 |
90 |
91 |
103 |
104 |
105 |
116 |
117 |
118 |
119 |
120 | ) : (
121 |
122 | )}
123 |
124 | {isError ? (
125 |
126 | Oops!!!
127 | {error.message === "MultipleBookingError" ? (
128 |
129 | {" "}
130 | Multiple Booking Error: Sorry, Bunker does not allow multiple
131 | hotel bookings on concurrent dates!
132 |
133 | ) : (
134 | Something went wrong!
135 | )}
136 |
137 | ) : null}
138 | {isSuccess ? (
139 |
144 | ) : null}
145 |
146 | );
147 | };
148 |
149 | export default withFirebase(withAuthentication(CheckOut));
150 |
--------------------------------------------------------------------------------
/bunker/src/pages/Hotel/CheckOut/CheckOutForm.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import {Icon, Segment} from "semantic-ui-react";
3 | import * as util from 'util';
4 |
5 | const CheckOutForm = props => {
6 | const {reservation, isUseReward,user, hotel} = props;
7 |
8 | const totalPrice = isUseReward
9 | ? ((reservation.price > user.reward_points) ? reservation.price - user.reward_points : 0)
10 | : reservation.price;
11 |
12 | const { name, address } = hotel.data;
13 | const { roomQuantity, room_types } = reservation;
14 | const roomTypeString = room_types.charAt(0).toUpperCase() + room_types.slice(1) + '-Person Room';
15 | console.log(util.inspect(reservation));
16 |
17 | return (
18 |
19 | Your booking for:
20 | {name}
21 |
22 | {address.street}, {address.city},{" "}
23 | {address.state}, {address.country}
24 |
25 |
26 |
27 |
28 | {new Date(reservation.start_date).toDateString()} -
29 | {new Date(reservation.end_date).toDateString()}
30 |
31 |
32 |
33 |
34 | {roomQuantity} x {roomTypeString}
35 |
36 |
37 | {!isUseReward ? (
38 | Price: ${totalPrice}
39 | ) : (
40 | Price: $ {totalPrice} {reservation.price}
41 | )}
42 |
43 | );
44 | };
45 |
46 | export default CheckOutForm;
47 |
--------------------------------------------------------------------------------
/bunker/src/pages/Hotel/CheckOut/conflict.css:
--------------------------------------------------------------------------------
1 | .modal-open {
2 | overflow: unset;
3 | }
4 |
5 | .modal-open .modal {
6 | overflow-x: unset;
7 | overflow-y: unset;
8 | }
9 |
10 | .modal {
11 | display: none;
12 | position: unset;
13 | top: unset;
14 | left: unset;
15 | z-index: unset;
16 | display: unset;
17 | width: unset;
18 | height: unset;
19 | overflow: unset;
20 | outline: unset;
21 | }
22 |
23 | .modal-dialog {
24 | position: relative;
25 | width: auto;
26 | margin: 0.5rem;
27 | pointer-events: none;
28 | }
29 |
30 | .modal.fade .modal-dialog {
31 | transition: -webkit-transform 0.3s ease-out;
32 | transition: transform 0.3s ease-out;
33 | transition: transform 0.3s ease-out, -webkit-transform 0.3s ease-out;
34 | -webkit-transform: translate(0, -50px);
35 | transform: translate(0, -50px);
36 | }
37 |
38 | @media (prefers-reduced-motion: reduce) {
39 | .modal.fade .modal-dialog {
40 | transition: none;
41 | }
42 | }
43 |
44 | .modal.show .modal-dialog {
45 | -webkit-transform: none;
46 | transform: none;
47 | }
48 |
49 | .modal-dialog-scrollable {
50 | display: -ms-flexbox;
51 | display: flex;
52 | max-height: calc(100% - 1rem);
53 | }
54 |
55 | .modal-dialog-scrollable .modal-content {
56 | max-height: calc(100vh - 1rem);
57 | overflow: hidden;
58 | }
59 |
60 | .modal-dialog-scrollable .modal-header,
61 | .modal-dialog-scrollable .modal-footer {
62 | -ms-flex-negative: 0;
63 | flex-shrink: 0;
64 | }
65 |
66 | .modal-dialog-scrollable .modal-body {
67 | overflow-y: auto;
68 | }
69 |
70 | .modal-dialog-centered {
71 | display: -ms-flexbox;
72 | display: flex;
73 | -ms-flex-align: center;
74 | align-items: center;
75 | min-height: calc(100% - 1rem);
76 | }
77 |
78 | .modal-dialog-centered::before {
79 | display: block;
80 | height: calc(100vh - 1rem);
81 | content: "";
82 | }
83 |
84 | .modal-dialog-centered.modal-dialog-scrollable {
85 | -ms-flex-direction: column;
86 | flex-direction: column;
87 | -ms-flex-pack: center;
88 | justify-content: center;
89 | height: 100%;
90 | }
91 |
92 | .modal-dialog-centered.modal-dialog-scrollable .modal-content {
93 | max-height: none;
94 | }
95 |
96 | .modal-dialog-centered.modal-dialog-scrollable::before {
97 | content: none;
98 | }
99 |
100 | .modal-content {
101 | position: relative;
102 | display: -ms-flexbox;
103 | display: flex;
104 | -ms-flex-direction: column;
105 | flex-direction: column;
106 | width: 100%;
107 | pointer-events: auto;
108 | background-color: #fff;
109 | background-clip: padding-box;
110 | border: 1px solid rgba(0, 0, 0, 0.2);
111 | border-radius: 0.3rem;
112 | outline: 0;
113 | }
114 |
115 | .modal-backdrop {
116 | display: none;
117 | position: fixed;
118 | top: 0;
119 | left: 0;
120 | z-index: 1040;
121 | width: 100vw;
122 | height: 100vh;
123 | background-color: #000;
124 | }
125 |
126 | .modal-backdrop.fade {
127 | opacity: 0;
128 | }
129 |
130 | .modal-backdrop.show {
131 | opacity: 0.5;
132 | }
133 |
134 | .modal-header {
135 | display: -ms-flexbox;
136 | display: flex;
137 | -ms-flex-align: start;
138 | align-items: flex-start;
139 | -ms-flex-pack: justify;
140 | justify-content: space-between;
141 | padding: 1rem 1rem;
142 | border-bottom: 1px solid #dee2e6;
143 | border-top-left-radius: 0.3rem;
144 | border-top-right-radius: 0.3rem;
145 | }
146 |
147 | .modal-header .close {
148 | padding: 1rem 1rem;
149 | margin: -1rem -1rem -1rem auto;
150 | }
151 |
152 | .modal-title {
153 | margin-bottom: 0;
154 | line-height: 1.5;
155 | }
156 |
157 | .modal-body {
158 | position: relative;
159 | -ms-flex: 1 1 auto;
160 | flex: 1 1 auto;
161 | padding: 1rem;
162 | }
163 |
164 | .modal-footer {
165 | display: -ms-flexbox;
166 | display: flex;
167 | -ms-flex-align: center;
168 | align-items: center;
169 | -ms-flex-pack: end;
170 | justify-content: flex-end;
171 | padding: 1rem;
172 | border-top: 1px solid #dee2e6;
173 | border-bottom-right-radius: 0.3rem;
174 | border-bottom-left-radius: 0.3rem;
175 | }
176 |
177 | .modal-footer > :not(:first-child) {
178 | margin-left: .25rem;
179 | }
180 |
181 | .modal-footer > :not(:last-child) {
182 | margin-right: .25rem;
183 | }
184 |
185 | .modal-scrollbar-measure {
186 | position: absolute;
187 | top: -9999px;
188 | width: 50px;
189 | height: 50px;
190 | overflow: scroll;
191 | }
192 |
193 | @media (min-width: 576px) {
194 | .modal-dialog {
195 | max-width: 500px;
196 | display: none;
197 | margin: 1.75rem auto;
198 | }
199 | .modal-dialog-scrollable {
200 | max-height: calc(100% - 3.5rem);
201 | }
202 | .modal-dialog-scrollable .modal-content {
203 | max-height: calc(100vh - 3.5rem);
204 | }
205 | .modal-dialog-centered {
206 | min-height: calc(100% - 3.5rem);
207 | }
208 | .modal-dialog-centered::before {
209 | height: calc(100vh - 3.5rem);
210 | }
211 | .modal-sm {
212 | max-width: 300px;
213 | }
214 | }
215 |
216 | @media (min-width: 992px) {
217 | .modal-lg,
218 | .modal-xl {
219 | max-width: 800px;
220 | }
221 | }
222 |
223 | @media (min-width: 1200px) {
224 | .modal-xl {
225 | max-width: 1140px;
226 | }
227 | }
--------------------------------------------------------------------------------
/bunker/src/pages/Hotel/Hotel.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 | import Carousel from "react-bootstrap/Carousel";
3 | import "bootstrap/dist/css/bootstrap.css";
4 | import "../Hotel/CheckOut/conflict.css";
5 | // import CarouselComponent from './Carousel'
6 | import MapsHotel from "../Home/components/MapsHotel";
7 |
8 | // Components
9 | import { withFirebase } from "../../server/Firebase";
10 | import {
11 | Grid,
12 | Button,
13 | Container,
14 | Header,
15 | Segment,
16 | Icon,
17 | List,
18 | Divider,
19 | Rating,
20 | Card,
21 | Placeholder,
22 | Image
23 | } from "semantic-ui-react";
24 | import CheckOut from "./CheckOut/CheckOut";
25 |
26 | // import Carousel from 'react-bootstrap/Carousel';
27 | // import { Carousel } from 'react-responsive-carousel';
28 |
29 | import * as moment from "moment";
30 |
31 | //Debugging purposes
32 | import * as util from "util"; // has no default export
33 | import CheckInOutCalendar from "../../commonComponents/CheckInOutCalendar";
34 | import RoomTypeSelect from "../../commonComponents/RoomTypeSelect";
35 | import RoomQuantitySelect from "../../commonComponents/RoomQuantitySelect";
36 |
37 | class HotelPage extends Component {
38 | constructor(props) {
39 | super(props);
40 | console.log(props.location.state);
41 | this.state = {
42 | ...props.location.state,
43 | checkInDate: "",
44 | checkOutDate: "",
45 | start_date: 0,
46 | end_date: 0,
47 | index: 0,
48 | direction: null
49 | };
50 | this.handleSelect = this.handleSelect.bind(this);
51 | }
52 |
53 | componentDidMount() {
54 | //parse dates into check in and out
55 | console.log("Hotel.js this.state: " + this.state);
56 | this.parseDatesRange(this.state.datesRange);
57 | this.calculatePricePerNight();
58 | }
59 |
60 | componentDidUpdate(prevProps, prevState) {}
61 |
62 | parseDatesRange = datesRange => {
63 | if (datesRange.length > 13) {
64 | let parsedValue = datesRange.split(" ");
65 | let checkInString = parsedValue[0];
66 | let checkOutString = parsedValue[2];
67 | let checkInArray = checkInString.split("-");
68 | let checkOutArray = checkOutString.split("-");
69 | let checkInDate = new Date(
70 | parseInt(checkInArray[2]),
71 | parseInt(checkInArray[0] - 1),
72 | parseInt(checkInArray[1])
73 | );
74 | let checkOutDate = new Date(
75 | parseInt(checkOutArray[2]),
76 | parseInt(checkOutArray[0] - 1),
77 | parseInt(checkOutArray[1])
78 | );
79 |
80 | this.setState({
81 | ...this.state,
82 | checkInDate: checkInDate,
83 | checkOutDate: checkOutDate,
84 | start_date: checkInDate.getTime(),
85 | end_date: checkOutDate.getTime()
86 | });
87 | }
88 | };
89 |
90 | handleCheckInOut = (event, { name, value }) => {
91 | //set datesRange whenever calendar range is updated
92 | if (this.state.hasOwnProperty(name)) {
93 | this.setState({ [name]: value }, () => {
94 | //parse the dates into checkInDate and checkOutDate as Date objects after the user clicks the 2nd date
95 | this.parseDatesRange(value);
96 | });
97 | }
98 | };
99 |
100 | handleRoomTypeQuantity = (e, { name, value }) => {
101 | this.setState(
102 | {
103 | [name]: value
104 | },
105 | () => {
106 | this.calculatePricePerNight();
107 | }
108 | );
109 | };
110 |
111 | calculatePricePerNight() {
112 | const { room_types } = this.state.hotel.data;
113 | const { roomQuantity } = this.state;
114 | const roomTypeData = room_types.filter(
115 | roomType => roomType.type === this.state.roomType
116 | );
117 | const roomPrice = roomTypeData[0].price;
118 |
119 | const pricePerNight = roomPrice * roomQuantity;
120 | console.log(pricePerNight);
121 |
122 | this.setState({
123 | currentRoomPrice: roomPrice,
124 | pricePerNight: pricePerNight
125 | });
126 | }
127 |
128 | calculateTotalPrice() {
129 | const { pricePerNight, checkInDate, checkOutDate } = this.state;
130 | let a = moment(checkInDate);
131 | let b = moment(checkOutDate);
132 | let totalDays = b.diff(a, "days");
133 | let totalPrice = totalDays * pricePerNight;
134 | // console.log(totalPrice);
135 | return totalPrice;
136 | }
137 | handleSelect(selectedIndex, e) {
138 | this.setState({
139 | index: selectedIndex,
140 | direction: e.direction
141 | });
142 | }
143 |
144 | render() {
145 | const { name, address, details, image, rating } = this.state.hotel.data;
146 | const {
147 | hotel,
148 | start_date,
149 | end_date,
150 | roomQuantity,
151 | roomType,
152 | pricePerNight,
153 | datesRange
154 | } = this.state;
155 |
156 | const image0 = this.state.hotel.data.image[0];
157 | const image1 = this.state.hotel.data.image[1];
158 | const image2 = this.state.hotel.data.image[2];
159 | const image3 = this.state.hotel.data.image[3];
160 | const { index, direction } = this.state;
161 | const images = [image0, image1, image2, image3];
162 | return (
163 |
164 |
165 |
170 | {this.state.hotel.data.image.map((i, index) => (
171 |
172 |
178 |
179 | ))}
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 | {address.street}, {address.city}, {address.state}{" "}
194 | {address.country}
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 | {hotel.data.details.split(", ").map(item => (
205 | {item}
206 | ))}
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 | {hotel.data.spots.split(", ").map(item => (
215 | {item}
216 | ))}
217 |
218 |
219 |
220 |
221 |
222 |
223 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
248 |
249 |
250 |
251 |
252 | Check In/Out Date:
253 |
257 |
258 |
259 | Room Type/Quantity:
260 |
261 |
265 |
269 |
270 |
271 |
272 |
273 |
284 |
285 |
286 |
287 |
288 |
289 | );
290 | }
291 | }
292 |
293 | export default HotelPage;
294 |
--------------------------------------------------------------------------------
/bunker/src/pages/Landing/Landing.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Background from '../../images/LandingBackground.jpg';
3 | import {Form, Select,Dropdown, Image, Segment} from 'semantic-ui-react';
4 | import CheckInOutCalendar from '../../commonComponents/CheckInOutCalendar'
5 | import {withFirebase} from '../../server/Firebase' ;
6 |
7 |
8 | import * as ROUTES from '../../constants/routes';
9 | import * as moment from "moment";
10 | import BunkerImage from '../../images/bunker.png';
11 |
12 |
13 |
14 | const today=moment().format('MM-DD-YYYY');
15 | const fivedayfromtoday=moment().add(5,'days').format('MM-DD-YYYY');
16 |
17 | class Landing extends React.Component{
18 | constructor(props){
19 | super(props);
20 | this.state = {
21 | location: {},
22 | roomType:'',
23 | roomQuantity: 0,
24 | datesRange: '',
25 | locationOptions: '',
26 |
27 |
28 | i : 0,
29 | maintxt :'Decentralized Certificates on the Ethereum Blockchain',
30 | speed : 100,
31 | displaytxt: '',
32 | tmpTitle: 'Start your trip with Bunker! ',
33 | fullTitle: 'Start your trip with Bunker!',
34 | j: 0
35 |
36 | }
37 | const spinner = document.getElementById('spinner');
38 |
39 | if (spinner && !spinner.hasAttribute('hidden')) {
40 | spinner.setAttribute('hidden', 'true');
41 | }
42 |
43 | }
44 | componentDidMount(){
45 | this.setState({
46 | roomType: 'single',
47 | roomQuantity: 1,
48 | datesRange: today + " - " + fivedayfromtoday
49 | })
50 |
51 | this.props.firebase.getCities().then(locationData => {
52 | locationData.sort((a, b) => {
53 | if (a.data.city.toLowerCase() > b.data.city.toLowerCase()) {
54 | return 1;
55 | } else {
56 | return -1;
57 | }
58 | });
59 |
60 | const locationOptions = locationData.map(location => ({
61 | key: location.data.city,
62 | text: `${location.data.city} , ${location.data.state}, ${
63 | location.data.country
64 | }`,
65 | value: location
66 | }));
67 |
68 | this.setState({locationOptions: locationOptions});
69 | });
70 |
71 | this.timeout = setInterval(() => {
72 | if (this.state.i < this.state.maintxt.length) {
73 | let newI = this.state.i+1;
74 | this.setState({ i: newI });
75 | }
76 | // else{
77 | // console.log("eh");
78 | // this.setState({i:0});
79 | // }
80 | }, 50);
81 | this.timeout = setInterval(() => {
82 | if(this.state.j < this.state.tmpTitle.length){
83 | let newJ = this.state.j+1;
84 | this.setState({ j: newJ });
85 | }
86 | // else{
87 | // console.log("eh");
88 | // this.setState({i:0});
89 | // }
90 | }, 65);
91 |
92 | }
93 |
94 | onChange = (event, {name, value}) =>{
95 | this.setState({[name]: value})
96 | }
97 |
98 | onClick = () => {
99 | const {location, roomType, roomQuantity, datesRange} = this.state;
100 | const locationID = location.id;
101 |
102 | this.props.history.push({
103 | pathname: ROUTES.HOME,
104 | state: {
105 | locationID,
106 | roomType,
107 | roomQuantity,
108 | datesRange,
109 | }
110 | })
111 | }
112 |
113 | componentWillUnmount() {
114 | clearInterval(this.timeout);
115 | }
116 |
117 | goTo(event){
118 | var destination = event.target.value;
119 | this.props.history.push(`/${destination}`);
120 | }
121 |
122 | render() {
123 | let displaytext = this.state.maintxt.substring(0,this.state.i);
124 | let displayTitle ='';
125 | if(this.state.j >= this.state.tmpTitle.length){
126 | displayTitle = this.state.fullTitle;
127 | }else{
128 | displayTitle = this.state.tmpTitle.substring(0,this.state.j);
129 | }
130 | return(
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
{displayTitle}
141 |
142 |
174 |
175 |
176 |
177 |
178 |
179 |
180 | {/*
181 |
182 | */}
183 |
184 | );
185 | }}
186 | const GuestNum = () => {
187 | let Guests = [];
188 | for (var i = 1; i < 6; i++) {
189 | let obj = {
190 | key: i,
191 | text: i,
192 | value: i
193 | };
194 | Guests.push(obj);
195 | }
196 | return (
197 |
198 | );
199 | }
200 | const bunkerStyle = {
201 | margin: "auto",
202 | width: "300px",
203 | height: "15%",
204 | };
205 | const introDiv = {
206 | margin:"20px auto 0 auto ",
207 | width:"360px",
208 | height: "70px",
209 | // color: 'white'
210 | }
211 | // const introD = {
212 | // width:"360px",
213 | // height: "70px",
214 | // fontcolor:"grey",
215 | // }
216 | const Place = {
217 | margin:"0px auto 0 auto ",
218 | width:"360px",
219 | };
220 | const InOutDiv = {
221 | width:"360px",
222 | margin:"20px auto 0 auto ",
223 | height: "60px",
224 | }
225 | const CheckIn = {
226 | float:'left',
227 | width:"180px",
228 | };
229 | const CheckOut = {
230 | float:'left',
231 | width:"180px",
232 | };
233 | const Guests = {
234 | margin:"20px auto 0 auto ",
235 | width:"360px",
236 | };
237 | const buttonDiv = {
238 | margin:"-46px 0px 0 340px ",
239 | width:"360px"
240 | };
241 |
242 | const image = {
243 | display: "block",
244 | margin: '50px auto 0 auto',
245 | width: "50%"
246 |
247 | }
248 |
249 | const boxStyle = {
250 | margin: "15px auto 0px auto",
251 | // padding-left:'auto'
252 | // padding-right:'auto'
253 | border: '5px solid white',
254 | borderRadius:"5px",
255 | width: '500px',
256 | height: '450px',
257 | backgroundColor: 'white',
258 | backgroundRepeat:'',
259 | position:'center',
260 | opacity: '0.95'
261 | };
262 |
263 | const backgroundStyle = {
264 | // width: "100%",
265 | height: "100vh",
266 | backgroundImage: `url(${Background})`,
267 | backgroundRepeat: "null",
268 | backgroundSize: 'cover',
269 | overflow: 'hidden',
270 |
271 | };
272 |
273 | export default withFirebase(Landing);
274 |
--------------------------------------------------------------------------------
/bunker/src/pages/PasswordChange/PasswordChange.js:
--------------------------------------------------------------------------------
1 | //will push to Github 03/24/19
2 | import React, { Component } from 'react';
3 |
4 | import { withFirebase } from '../../server/Firebase';
5 |
6 | import {
7 | Container,
8 | Button,
9 | Form,
10 | Grid,
11 | Header,
12 | Segment,
13 | Divider,
14 | Message,
15 | Icon,
16 | Input,
17 | }from 'semantic-ui-react';
18 |
19 | const INITIAL_STATE = {
20 | passwordOne: '',
21 | passwordTwo: '',
22 | error: null,
23 | };
24 |
25 | class PasswordChangeForm extends Component {
26 | constructor(props) {
27 | super(props);
28 |
29 | this.state = { ...INITIAL_STATE };
30 | }
31 |
32 | onSubmit = event => {
33 | const { passwordOne } = this.state;
34 |
35 | this.props.firebase
36 | .doPasswordUpdate(passwordOne)
37 | .then(() => {
38 | this.setState({ ...INITIAL_STATE });
39 | })
40 | .catch(error => {
41 | this.setState({ error });
42 | });
43 |
44 | event.preventDefault();
45 | };
46 |
47 | onChange = event => {
48 | this.setState({ [event.target.name]: event.target.value });
49 | };
50 |
51 | render() {
52 | const { passwordOne, passwordTwo, error } = this.state;
53 |
54 | const isInvalid =
55 | passwordOne !== passwordTwo || passwordOne === '';
56 |
57 | return (
58 |
59 |
60 | {/* */}
61 |
62 |
63 |
79 |
86 |
87 | Reset My Password
88 |
89 |
90 | {error && {error.message}
}
91 |
92 |
93 |
94 | {/* */}
95 |
96 |
97 | );
98 | }
99 | }
100 |
101 | export default withFirebase(PasswordChangeForm);
102 |
--------------------------------------------------------------------------------
/bunker/src/pages/PasswordForget/PasswordForget.js:
--------------------------------------------------------------------------------
1 | // will push to Github 03/24/29
2 | import React, { Component } from 'react';
3 | import { Link } from 'react-router-dom';
4 |
5 | import { withFirebase } from '../../server/Firebase';
6 | import * as ROUTES from '../../constants/routes';
7 |
8 | import {
9 | Button,
10 | Form,
11 | Grid,
12 | Header,
13 | Segment,
14 | Message,
15 | } from 'semantic-ui-react';
16 |
17 | const PasswordForgetPage = () => (
18 |
19 |
20 |
21 |
22 |
PasswordForget
23 |
24 |
No worry, please enter your registered email
25 |
26 |
27 |
28 |
29 | );
30 |
31 | const INITIAL_STATE = {
32 | email: '',
33 | error: null,
34 | };
35 |
36 | class PasswordForgetFormBase extends Component {
37 | constructor(props) {
38 | super(props);
39 |
40 | this.state = { ...INITIAL_STATE };
41 | }
42 |
43 | onSubmit = event => {
44 | const { email } = this.state;
45 |
46 | this.props.firebase
47 | .doPasswordReset(email)
48 | .then(() => {
49 | this.setState({ ...INITIAL_STATE });
50 | })
51 | .catch(error => {
52 | this.setState({ error });
53 | });
54 |
55 | event.preventDefault();
56 | };
57 |
58 | onChange = event => {
59 | this.setState({ [event.target.name]: event.target.value });
60 | };
61 |
62 | render() {
63 | const { email, error } = this.state;
64 |
65 | const isInvalid = email === '';
66 |
67 | return (
68 |
69 |
70 |
71 |
72 |
83 |
84 | Reset My Password
85 |
86 |
87 |
88 |
89 | {error && ****{error.message}****
}
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 | );
98 | }
99 | }
100 |
101 | const PasswordForgetLink = () => (
102 |
103 | Forgot Password?
104 |
105 | );
106 |
107 | export default PasswordForgetPage;
108 |
109 | const PasswordForgetForm = withFirebase(PasswordForgetFormBase);
110 |
111 | export { PasswordForgetForm, PasswordForgetLink };
112 |
--------------------------------------------------------------------------------
/bunker/src/pages/Reservation/CancelReservation/CancelReservation.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import {
3 | Modal,
4 | Icon,
5 | Button,
6 | Message,
7 | Segment,
8 | Image,
9 | Confirm
10 | } from "semantic-ui-react";
11 | import {withFirebase} from "../../../server/Firebase";
12 | import CancelReservationForm from "./CancelReservationForm";
13 |
14 | const CancelReservation = props => {
15 |
16 | const [openModal, setOpenModal] = React.useState(false);
17 | const [isConfirmOpen, setIsConfirmOpen] = React.useState(false);
18 | const [isError, setIsError] = React.useState(false);
19 | const [isOpen, setIsOpen] = React.useState(false);
20 |
21 | //fake data
22 |
23 | const {reservation, hotel} = props;
24 | const user = JSON.parse(localStorage.getItem("authUser"));
25 |
26 | const handleDeleteReservation = () => {
27 | console.log("handle Delete for: " + reservation.id);
28 |
29 | props.firebase
30 | .deleteReservationFromDB(reservation.id, user.uid, reservation.data.price)
31 | .catch(error => {
32 | setIsError(true);
33 | console.log(error);
34 | });
35 | };
36 |
37 | return (
38 | setOpenModal(true)}>
42 |
43 | Delete this Reservation
44 |
45 | }
46 | open={openModal}
47 | onClose={()=>setOpenModal(false)}
48 | >
49 | Cancel Reservation
50 |
51 |
52 |
58 |
59 |
60 |
61 |
62 | setOpenModal(false)}
65 | />
66 |
67 | setIsConfirmOpen(true)}
71 | />
72 |
73 |
74 | setIsConfirmOpen(false)}
81 | onConfirm={handleDeleteReservation}
82 | size="mini"
83 | />
84 |
85 |
86 | {isError ? (
87 |
88 | Oops!!!
89 | Something when wrong
90 |
91 | ) : null}
92 |
93 | );
94 | };
95 | // 111
96 | export default withFirebase(CancelReservation);
97 |
--------------------------------------------------------------------------------
/bunker/src/pages/Reservation/CancelReservation/CancelReservationForm.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import {Grid, Image, Divider, Header, Icon, Label, Segment, Container} from "semantic-ui-react";
3 | import _ from "lodash";
4 | const CancelReservationForm = props => {
5 | const {reservation, hotel} = props;
6 |
7 | return (
8 |
9 |
10 |
11 | Your Reservation For:
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | {hotel.data.name}
23 |
24 | {hotel.data.address.street}, {hotel.data.address.city}, {hotel.data.address.state}, {hotel.data.address.country}
25 |
26 |
27 | Date Booked:
28 |
29 | {new Date(reservation.data.start_date).toDateString()} -{" "}
30 | {new Date(reservation.data.end_date).toDateString()}
31 |
35 | {reservation.data.roomQuantity}{" "}
36 | {_.upperFirst(reservation.data.room_types)}-Person Room(s)
37 |
38 | Total Price: ${reservation.data.price}
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 | Cancellation Policy:
47 |
48 |
49 | Hotel cancellations are eligible for refunds up to 80% within 48 hours of check-in date.
50 | After that, all fees and payments are non-refundable and any reward points spent are not guaranteed to be returned.
51 | By clicking "Confirm Cancellation" you agree to abide by these rules and this policy.
52 | Bunker is not responsible for any miscalculated bookings, late cancellations, or forgetfulness upon your actions.
53 |
54 |
55 | If you have further questions about Bunker's Reservation Policy, please contact (408) 165-9999 with your
56 | Reservation ID displayed in the top right corner of this dialog.
57 |
58 |
59 |
60 |
61 | Reservation ID: {reservation.id}
62 |
63 |
64 |
65 |
66 | );
67 | };
68 |
69 | export default CancelReservationForm;
70 |
--------------------------------------------------------------------------------
/bunker/src/pages/Reservation/ChangeReservation/ChangeReservation.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import {
3 | Modal,
4 | Grid,
5 | TransitionablePortal,
6 | Button,
7 | Icon,
8 | Message,
9 | Segment,
10 | Header,
11 | Image,
12 | Confirm
13 | } from "semantic-ui-react";
14 | import {withFirebase} from "../../../server/Firebase";
15 | import ChangeReservationForm from "./ChangeReservationForm";
16 |
17 | const ChangeReservation = props => {
18 |
19 | const [openModal, setOpenModal] = React.useState(false);
20 | const [isConfirmOpen, setIsConfirmOpen] = React.useState(false);
21 | const [isError, setIsError] = React.useState(false);
22 | const [error, setError] = React.useState(new Error("null"));
23 | const [isEditable, setIsEditable] = React.useState(false);
24 | const [newReservationData, setNewReservationData] = React.useState({});
25 | const [isSuccess, setIsSuccess] = React.useState(false);
26 |
27 | const {hotel} = props;
28 | const oldReservation = props.reservation;
29 | const user = JSON.parse(localStorage.getItem("authUser"));
30 |
31 | const handleChangeReservation = () => {
32 | console.log(newReservationData);
33 | props.firebase
34 | .editReservationInfo(oldReservation.id, newReservationData, user.uid)
35 | .then(() =>{
36 | setIsSuccess(true);
37 | setIsConfirmOpen(false);
38 | setOpenModal(false);
39 | })
40 | .catch(error => {
41 | setIsError(true);
42 | setError(error);
43 | console.log(error);
44 | });
45 | console.log("handle Edit");
46 | };
47 |
48 | return (
49 | setOpenModal(true)}>
52 |
53 | Change this Reservation
54 |
55 | }
56 | open={openModal}
57 | onClose={()=>setOpenModal(false)}
58 | >
59 | Edit Reservation
60 |
61 |
62 |
72 |
73 |
74 |
75 |
76 | setOpenModal(false)}
79 | />
80 |
81 | setIsConfirmOpen(true)}
85 | disabled={newReservationData === {} ? true : false}
86 | />
87 |
88 |
89 |
90 |
91 | setIsConfirmOpen(false)}
96 | onConfirm={handleChangeReservation}
97 | />
98 |
99 | {isError ? (
100 | setIsError(false)} style={{ 'min-width':"350px", left: "40%", position: "fixed", top: "80%"}} negative>
101 | Oops!!!
102 | {false ? (
103 |
104 | It seem like you have a reservation for the same day.
105 |
106 | ) : (
107 | Something when wrong
108 | )}
109 |
110 | ) : null}
111 | {isSuccess ? (
112 | setIsSuccess(false)}
114 | style={{
115 | width: "330px",
116 | height: "75px",
117 | left: "40%",
118 | position: "fixed",
119 | top: "80%"
120 | }}
121 | positive
122 | >
123 | Edit Successed
124 | Your reservation have been changed
125 |
126 | ) : null}
127 |
128 | );
129 | };
130 |
131 | export default withFirebase(ChangeReservation);
132 |
--------------------------------------------------------------------------------
/bunker/src/pages/Reservation/ChangeReservation/ChangeReservationForm.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import {Button, Icon, Grid, Segment, Container, Divider, Image, Header, Label} from "semantic-ui-react";
3 | import * as moment from "moment";
4 | import _ from "lodash";
5 | import CheckInOutCalendar from "../../../commonComponents/CheckInOutCalendar";
6 | import RoomTypeSelect from "../../../commonComponents/RoomTypeSelect";
7 | import RoomQuantitySelect from "../../../commonComponents/RoomQuantitySelect";
8 | import {DatesRangeInput} from "semantic-ui-calendar-react";
9 |
10 | const ChangeReservationForm = props => {
11 | const [newDatesRange, setNewDatesRange] = React.useState("");
12 | const [currentPrice, setCurrentPrice] = React.useState(0);
13 | const {
14 | start_date,
15 | end_date,
16 | roomQuantity,
17 | room_types,
18 | datesRange,
19 | price
20 | } = props.oldReservation.data;
21 | const {newReservationData, setNewReservationData, hotel, oldReservation} = props;
22 | React.useEffect(() => {
23 | setNewDatesRange(datesRange);
24 | setCurrentPrice(price);
25 | }, []);
26 |
27 | React.useEffect(
28 | () => {
29 | parseDatesRange(newDatesRange);
30 | },
31 | [newDatesRange]
32 | );
33 |
34 | React.useEffect(
35 | () => {
36 | if (newReservationData) {
37 | console.log(newReservationData);
38 | const currentPrice = (_.find(hotel.data.room_types, {
39 | type: newReservationData.room_types || room_types
40 | })).price;
41 | const currentQuantity = newReservationData.roomQuantity || roomQuantity;
42 | console.log(currentPrice);
43 | setCurrentPrice((
44 | (currentQuantity *
45 | currentPrice *
46 | (newReservationData.end_date - newReservationData.start_date)) /
47 | 86400000
48 | ) || 0);
49 | }
50 | },
51 | [newReservationData]
52 | );
53 | React.useEffect(
54 | () => {
55 | setNewReservationData({...newReservationData, price: currentPrice})
56 | },
57 | [currentPrice])
58 |
59 | const parseDatesRange = editDateRanges => {
60 | if (editDateRanges.length > 13) {
61 | let parsedValue = editDateRanges.split(" - ");
62 |
63 | let checkInDate = new Date(parsedValue[0].split("-"));
64 | let checkOutDate = new Date(parsedValue[1].split("-"));
65 | console.log(checkInDate, checkOutDate, parsedValue);
66 | console.log(props.editDateRanges);
67 | setNewReservationData({
68 | ...newReservationData,
69 | start_date: checkInDate.getTime(),
70 | end_date: checkOutDate.getTime(),
71 | datesRange: newDatesRange,
72 | });
73 | }
74 | };
75 |
76 | const handleChange = (event, {name, value}) => {
77 | props.setNewReservationData({
78 | ...newReservationData,
79 | [name]: value
80 | });
81 | };
82 | return (
83 |
84 |
85 | Your Reservation For:
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 | {hotel.data.name}
97 |
98 | {hotel.data.address.street}, {hotel.data.address.city}, {hotel.data.address.state}, {hotel.data.address.country}
99 |
100 |
101 | Edit Date Booked:
102 |
103 | {
105 | setNewDatesRange(value);
106 | }}
107 | value={newDatesRange}
108 | />
109 |
110 |
111 | Edit Rooms:
112 |
113 |
118 |
123 |
124 | Total Price: ${currentPrice}
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 | Note to User:
133 |
134 |
135 | If changes need to be made to check-in/check-out dates or room types/quantity, requests will be granted based off of availability at the time of change.
136 | Total price will be affected based off of changes made by the user.
137 | Additional/reduced fees will be charged/refunded to the same method of payment used for the previous booking.
138 | Refunds are only eligible within 48 hours of check-in date from the original booking date.
139 | After that, all fees and payments are non-refundable and any reward points spent are not guaranteed to be returned.
140 | By clicking "Edit Cancellation" you agree to abide by these rules and this policy.
141 | Bunker is not responsible for any miscalculated bookings, late cancellations, or forgetfulness upon your actions.
142 |
143 |
144 |
145 | Reservation ID: {oldReservation.id}
146 |
147 |
148 | );
149 | };
150 |
151 | export default ChangeReservationForm;
152 |
--------------------------------------------------------------------------------
/bunker/src/pages/Reservation/ChangeReservation/ChangeReservationForm_alt2.js:
--------------------------------------------------------------------------------
1 | import React, {Component} from "react";
2 | import {
3 | Icon,
4 | Card,
5 | Image,
6 | Button,
7 | Table,
8 | Grid,
9 | Input,
10 | Header,
11 | Select,
12 | Dropdown
13 | } from "semantic-ui-react";
14 | import * as moment from "moment";
15 | import {DatesRangeInput, DateInput} from "semantic-ui-calendar-react";
16 | import DateRangePicker from "@wojtekmaj/react-daterange-picker";
17 |
18 | const today = moment().format("MM-DD-YYYY");
19 | const tomorrow = moment()
20 | .add(1, "days")
21 | .format("MM-DD-YYYY");
22 | const aWeekFromToday = moment()
23 | .add(5, "days")
24 | .format("MM-DD-YYYY");
25 | const defaultDateRangeArray = [today, aWeekFromToday];
26 | const defaultDateRange = defaultDateRangeArray.join(" - ");
27 |
28 | class ChangeReservationForm extends React.Component {
29 | constructor(props) {
30 | super(props);
31 | this.state = {
32 | newReservation: {
33 | start_date: props.oldReservation.data.start_date,
34 | end_date: props.oldReservation.data.end_date,
35 | room_types: props.oldReservation.data.room_types,
36 | roomQuantity: props.oldReservation.data.roomQuantity,
37 | datesRange: props.oldReservation.data.datesRange
38 | },
39 | isUpdated: false
40 | // datesRange:[this.state.newReservation.start_date, this.state.newReservation.end_date],
41 | };
42 |
43 | this.handleRoomType = this.handleRoomType.bind(this);
44 | }
45 |
46 | // handleCheckInOut=(event,{name,value})=>{
47 | //
48 | // //parse the dates into checkInDate and checkOutDate as Date objects after the user clicks the 2nd date
49 | // if(value.length > 13) {
50 | // const tempa = value.split(" - ")
51 | // let checkInDate = new Date(tempa[0]).getTime();
52 | // let checkOutDate = new Date(tempa[1]).getTime();
53 | //
54 | //
55 | // this.setState({
56 | // newReservation: {
57 | // start_date: checkInDate,
58 | // end_date: checkOutDate
59 | // }
60 | // });
61 | // }
62 | //
63 | // }
64 | // handleCheckInOut=(event,{name,value})=>{
65 | // // console.log("name: " + name + " value: " + value);
66 | // if(this.state.hasOwnProperty(name)){
67 | // this.setState({[name]:value});
68 | // }
69 | //
70 | // //parse the dates into checkInDate and checkOutDate as Date objects after the user clicks the 2nd date
71 | // if(value.length > 13){
72 | // let parsedValue = value.split(" ");
73 | // let checkInString = parsedValue[0];
74 | // let checkOutString = parsedValue[2];
75 | // let checkInArray = checkInString.split("-");
76 | // let checkOutArray = checkOutString.split("-");
77 | // let checkInDate = new Date(
78 | // parseInt(checkInArray[2]),
79 | // parseInt(checkInArray[0]-1),
80 | // parseInt(checkInArray[1])
81 | // );
82 | // let checkOutDate = new Date(
83 | // parseInt(checkOutArray[2]),
84 | // parseInt(checkOutArray[0]-1),
85 | // parseInt(checkOutArray[1])
86 | // );
87 | // this.setState({
88 | // newReservation: {
89 | // start_date: checkInDate,
90 | // end_date: checkOutDate
91 | // }
92 | // });
93 | //
94 | // // console.log("check in :" + checkInDate + " check out: " + checkOutDate);
95 | // }
96 | // }
97 |
98 | handleRoomType = (event, {name, value}) => {
99 | console.log("name: " + name + " value: " + value);
100 | const {newReservation} = this.state;
101 | this.props.setIsEditable(false);
102 | this.setState({
103 | newReservation: {
104 | ...newReservation,
105 | room_types: value
106 | },
107 | isUpdated: true
108 | });
109 | // console.log('new type: '+ this.state.newReservation.room_types)
110 | // alert(this.state.newReservation.room_types)
111 | };
112 |
113 | handleRoomQuantityOptions = (event, {name, value}) => {
114 | // console.log("name: " + name + " value: " + value);
115 | const {newReservation} = this.state;
116 | this.props.setIsEditable(false);
117 | this.setState({
118 | newReservation: {
119 | ...newReservation,
120 | roomQuantity: value
121 | },
122 | isUpdated: true
123 | });
124 | console.log("new quantity: " + this.state.newReservation.roomQuantity);
125 | // alert(this.state.newReservation.room_types)
126 | };
127 |
128 | // handleDate = (event) =>{
129 | // console.log(event.target.value);
130 | // }
131 |
132 | handleDate = date => {
133 | // console.log(date[0]);
134 | const {newReservation} = this.state;
135 | this.props.setIsEditable(false);
136 | this.setState({
137 | newReservation: {
138 | ...newReservation,
139 | start_date: date[0].getTime(),
140 | end_date: date[1].getTime()
141 | },
142 | isUpdated: true
143 | });
144 |
145 | // const dateRange = moment(this.state.newReservation.start_date).format('MM-DD-YYYY') + " - " +
146 | // moment(this.state.newReservation.end_date).format('MM-DD-YYYY');
147 | // const start = moment(this.state.newReservation.start_date).format('MM-DD-YYYY')
148 | // console.log(start);
149 | // console.log(" + ")
150 | console.log(this.state.newReservation.start_date);
151 | const start = moment(this.state.newReservation.start_date).format(
152 | "MM-DD-YYYY"
153 | );
154 | const end = moment(this.state.newReservation.end_date).format("MM-DD-YYYY");
155 | console.log("is:" + start);
156 | console.log(this.state.newReservation.end_date);
157 | console.log("is:" + end);
158 |
159 | // this.setState({ date })
160 | };
161 |
162 | render() {
163 | const roomTypeOptions = [
164 | {
165 | key: "single",
166 | text: "Single-Person",
167 | value: "single"
168 | },
169 | {
170 | key: "double",
171 | text: "Double-Person",
172 | value: "double"
173 | },
174 | {
175 | key: "multiple",
176 | text: "Multiple-Person",
177 | value: "multiple"
178 | }
179 | ];
180 |
181 | const roomQuantityOptions = [];
182 |
183 | for (let i = 0; i < 17; i++) {
184 | let obj = {
185 | key: i,
186 | text: i,
187 | value: i
188 | };
189 | roomQuantityOptions.push(obj);
190 | }
191 |
192 | // const dateRange = moment(this.state.newReservation.start_date).format('MM-DD-YYYY') + " - " +
193 | // moment(this.state.newReservation.end_date).format('MM-DD-YYYY');
194 |
195 | const datesRange = [
196 | this.state.newReservation.start_date,
197 | this.state.newReservation.end_date
198 | ];
199 | // const datesRange = [new Date().setDate(new Date().getDate()-1), new Date()];
200 | console.log(this.state.newReservation.start_date);
201 | return (
202 |
203 |
204 |
205 |
206 |
207 | Changes.....
208 |
209 |
210 | {/* {const newdat = Date.now(); setNewReservationData({end_date : Number(newdat)})}}/>*/}
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
256 |
257 |
258 |
259 |
260 | {this.state.isUpdated ? (
261 | {
265 | this.props.setNewReservationData(
266 | this.state.newReservation
267 | );
268 | this.props.setIsEditable(true);
269 | this.setState({isUpdated: false});
270 | }}
271 | >
272 | Confirm Changes
273 |
274 | ) : (
275 | {
279 | this.props.setNewReservationData(
280 | this.state.newReservation
281 | );
282 | this.props.setIsEditable(true);
283 | }}
284 | >
285 | Confirm Changes
286 |
287 | )}
288 |
289 |
290 |
291 |
292 |
293 | );
294 | }
295 | }
296 |
297 | export default ChangeReservationForm;
298 |
--------------------------------------------------------------------------------
/bunker/src/pages/Reservation/ChangeReservation/ChangeReservation_alt.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import {Modal, Button, Message, Segment, Confirm} from "semantic-ui-react";
3 | import {withFirebase} from "../../../server/Firebase";
4 | import ChangeReservationForm from "./ChangeReservationForm";
5 |
6 | const ChangeReservation = props => {
7 | const [isConfirmOpen, setIsConfirmOpen] = React.useState(false);
8 | const [isError, setIsError] = React.useState(false);
9 | const [error, setError] = React.useState(new Error("null"));
10 | const [newReservationData, setNewReservationData] = React.useState({
11 | ...props.reservation.data
12 | });
13 |
14 | const {hotel} = props;
15 | const oldReservation = props.reservation;
16 | const user = JSON.parse(localStorage.getItem("authUser"));
17 |
18 | const handleChangeReservation = () => {
19 | console.log(newReservationData);
20 | props.firebase
21 | .editReservationInfo(oldReservation.id, newReservationData, user.uid)
22 | .catch(error => {
23 | setIsError(true);
24 | setError(error);
25 | console.log(error);
26 | });
27 | console.log("handle Edit");
28 | };
29 |
30 | return (
31 |
34 | Change this reservation
35 |
36 | }
37 | >
38 | Edit Reservation
39 |
40 |
41 |
49 |
50 |
51 |
52 | setIsConfirmOpen(true)}
56 | />
57 | setIsConfirmOpen(false)}
62 | onConfirm={handleChangeReservation}
63 | />
64 |
65 | {isError ? (
66 |
67 | Opps!!!
68 | {error.message === "multiplebooking" ? (
69 |
70 | It seem like you have a reservation for the same day.
71 |
72 | ) : (
73 | Something when wrong
74 | )}
75 |
76 | ) : null}
77 |
78 | );
79 | };
80 |
81 | export default withFirebase(ChangeReservation);
82 |
--------------------------------------------------------------------------------
/bunker/src/pages/Reservation/Reservation.js:
--------------------------------------------------------------------------------
1 | import React, {Component} from "react";
2 | import {compose} from "recompose";
3 | import {withFirebase} from "../../server/Firebase";
4 | import {withRouter} from "react-router-dom";
5 | import {Link} from "react-router-dom";
6 | import {AuthUserContext, withAuthorization} from "../../server/Session";
7 | import * as ROUTES from "../../constants/routes";
8 |
9 | import _ from "lodash";
10 | import {
11 | Message,
12 | Header,
13 | Icon,
14 | Loader,
15 | Label,
16 | Grid,
17 | Segment,
18 | Image,
19 | Button,
20 | Dropdown
21 | } from "semantic-ui-react";
22 | import ChangeReservation from "./ChangeReservation/ChangeReservation";
23 | import CancelReservation from "./CancelReservation/CancelReservation";
24 | import moment from "moment";
25 | import * as util from 'util';
26 |
27 | const ReservationPage = () => (
28 |
29 |
30 | {authUser => }
31 |
32 |
33 | );
34 | class Reservation extends Component {
35 | constructor(props) {
36 | super(props);
37 | this.state = {
38 | hotels: [],
39 | user: {},
40 | reservations: [],
41 | isLoading: true,
42 | isEmpty: true,
43 | isError: false
44 | };
45 | }
46 |
47 | componentDidMount() {
48 | const {user} = this.props;
49 |
50 | this.setState({
51 | isLoading: true,
52 | isEmpty: false,
53 | isError: false,
54 | user: user
55 | },
56 | ()=>{
57 | console.log('this.state.user: ' + util.inspect(this.state.user));
58 | });
59 | this.subscribe = this.props.firebase.subscribeReservations(
60 | user.uid,
61 | // Date.now(),
62 | newreservations => {
63 | let hotelIDs = [];
64 | const today = new Date(
65 | moment()
66 | .format("MM-DD-YYYY")
67 | .split("-")
68 | );
69 | const reservations = newreservations.filter(
70 | item => item.data.start_date >= today.getTime()
71 | );
72 | console.log(newreservations);
73 | console.log(reservations);
74 |
75 | reservations.forEach(reservation =>
76 | hotelIDs.push(reservation.data.hotel_id)
77 | );
78 | console.log(reservations);
79 | this.props.firebase
80 | .getHotels(hotelIDs)
81 | .then(hotels => {
82 | this.setState({
83 | reservations: reservations,
84 | isLoading: false,
85 | isEmpty: reservations.length || true,
86 | hotels: hotels,
87 | isError: false
88 | });
89 | })
90 | .catch(err => {
91 | console.log(err);
92 | this.setState({
93 | isLoading: false,
94 | isError: true
95 | });
96 | });
97 | },
98 | error => {
99 | this.setState({
100 | isError: true,
101 | isLoading: false
102 | });
103 | }
104 | );
105 | // );
106 | // this.props.firebase.getReservations(user.reservations).then(result => {
107 | // console.log(result);
108 | // const reservations = result.filter(
109 | // item => item.data.start_date <= Date.now() && item
110 | // );
111 | // let hotelIDs = [];
112 | // reservations.forEach(reservation =>
113 | // hotelIDs.push(reservation.data.hotel_id)
114 | // );
115 | // this.props.firebase
116 | // .getHotels(hotelIDs)
117 | // .then(hotels => {
118 | // console.log(hotels);
119 | // this.setState({
120 | // reservations: reservations,
121 | // hotels: hotels,
122 | // user: user,
123 | // isEmpty: reservations.length === 0 ? true : false,
124 | // isLoading: false,
125 | // isError: false
126 | // });
127 | // })
128 | // .catch(error => {
129 | // console.log(error);
130 | // this.setState({
131 | // isError: true,
132 | // isLoading: false,
133 | // isEmpty: reservations.length === 0 ? true : false
134 | // });
135 | // });
136 | // });
137 | }
138 |
139 | componentWillUnmount() {
140 | this.subscribe();
141 | }
142 |
143 | render() {
144 | const {reservations, isLoading, isEmpty, isError, user} = this.state;
145 | return (
146 |
147 | {
148 | isEmpty === true ?
149 | (
150 |
151 |
152 |
153 | No Reservations Booked
154 |
155 |
156 | Book a Hotel
157 |
158 |
159 | )
160 | :
161 | (
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 | Welcome, {user.username}!
170 |
171 |
172 | Your currently booked reservations:
173 |
174 | {
175 | reservations.map((reservation, i) => {
176 |
177 | console.log('reservation: ' + util.inspect(reservation));
178 | const datesRange = reservation.data.datesRange;
179 | const roomType = reservation.data.room_types;
180 | const roomQuantity = reservation.data.roomQuantity;
181 | const hotel = this.state.hotels[i];
182 | const startDate = new Date(reservation.data.start_date);
183 | const endDate = new Date(reservation.data.end_date);
184 | return (
185 |
186 |
187 | Reservation ID: {reservation.id}
188 |
189 |
190 |
191 |
192 |
196 |
200 |
201 |
202 |
203 |
204 | {hotel.data.name}
205 |
206 | {hotel.data.address.street}, {hotel.data.address.city}, {hotel.data.address.state}, {hotel.data.address.country}
207 |
208 |
209 | Date Booked:
210 |
211 | {startDate.toDateString()} - {endDate.toDateString()}
212 |
216 | {reservation.data.roomQuantity}{" "}
217 | {_.upperFirst(reservation.data.room_types)} Room(s)
218 |
219 |
220 | Total Price: ${reservation.data.price}
221 |
222 |
223 |
224 |
225 |
226 |
230 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 | )
242 | })
243 | }
244 |
245 |
246 |
247 | )
248 | }
249 |
250 | {
251 | isError ?
252 | (
253 |
254 | We could't load that content
255 | Please contact an administrator to resolve the problem
256 |
257 | ) : null
258 | }
259 |
260 | );
261 | }
262 | }
263 | const condition = authUser =>
264 | authUser;
265 |
266 | const Reservations = compose(
267 | withRouter,
268 | withFirebase,
269 | withAuthorization(condition),
270 | )(Reservation);
271 |
272 | export default ReservationPage;
273 |
--------------------------------------------------------------------------------
/bunker/src/pages/SignIn/SignIn.js:
--------------------------------------------------------------------------------
1 | import React, {Component} from "react";
2 | import {withRouter} from "react-router-dom";
3 | import {compose} from "recompose";
4 |
5 | import {SignUpLink} from "../SignUp/SignUp";
6 | import {PasswordForgetLink} from "../PasswordForget/PasswordForget";
7 | import {withFirebase} from "../../server/Firebase";
8 | import * as ROUTES from "../../constants/routes";
9 | import Alert from "react-bootstrap/Alert";
10 |
11 | import {Button, Form, Grid, Header, Segment, Message} from "semantic-ui-react";
12 |
13 | const SignInPage = () => {
14 | const [error, setError] = React.useState(null);
15 | console.log(error && error);
16 | return (
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | {error && (
28 |
29 | Oh snap! You got an error!
30 | {error.message}
31 |
32 | )}
33 |
34 |
35 | );
36 | };
37 |
38 | const INITIAL_STATE = {
39 | email: "",
40 | password: "",
41 | error: null
42 | };
43 |
44 | const ERROR_CODE_ACCOUNT_EXISTS =
45 | "auth/account-exists-with-different-credential";
46 |
47 | const ERROR_MSG_ACCOUNT_EXISTS = `
48 | An account with an E-Mail address to
49 | this social account already exists. Try to login from
50 | this account instead and associate your social accounts on
51 | your personal account page.
52 | `;
53 |
54 | class SignInFormBase extends Component {
55 | constructor(props) {
56 | super(props);
57 |
58 | this.state = {...INITIAL_STATE};
59 | }
60 |
61 | onSubmit = event => {
62 | const {email, password} = this.state;
63 |
64 | this.props.firebase
65 | .doSignInWithEmailAndPassword(email, password)
66 | .then(() => {
67 | this.setState({...INITIAL_STATE});
68 | this.props.history.push(ROUTES.HOME);
69 | })
70 | .catch(error => {
71 | this.props.setErrorMessage(error);
72 | });
73 |
74 | event.preventDefault();
75 | };
76 |
77 | onChange = event => {
78 | this.setState({[event.target.name]: event.target.value});
79 | };
80 |
81 | render() {
82 | const {email, password, error} = this.state;
83 |
84 | const isInvalid = password === "" || email === "";
85 |
86 | return (
87 |
88 |
89 |
92 |
93 |
104 |
114 |
121 | Sign In
122 |
123 |
124 |
125 |
126 |
127 | {" "}
128 | Don't have an Account?
129 | Sign Up
130 |
131 |
132 |
133 |
134 | );
135 | }
136 | }
137 |
138 | class SignInGoogleBase extends Component {
139 | constructor(props) {
140 | super(props);
141 |
142 | this.state = {error: null};
143 | }
144 |
145 | onSubmit = event => {
146 | this.props.firebase
147 | .doSignInWithGoogle()
148 | .then(socialAuthUser => {
149 | // Create a user in your Firebase Realtime Database too
150 | return this.props.firebase.addGoogleUserToDB(socialAuthUser);
151 | })
152 | .then(() => {
153 | this.setState({error: null});
154 | this.props.history.push(ROUTES.HOME);
155 | })
156 | .catch(error => {
157 | if (error.code === ERROR_CODE_ACCOUNT_EXISTS) {
158 | error.message = ERROR_MSG_ACCOUNT_EXISTS;
159 | }
160 |
161 | this.props.setErrorMessage(error);
162 | });
163 |
164 | event.preventDefault();
165 | };
166 |
167 | render() {
168 | const {error} = this.state;
169 |
170 | return (
171 |
172 | Sign In with Google
173 |
174 | );
175 | }
176 | }
177 |
178 | class SignInFacebookBase extends Component {
179 | constructor(props) {
180 | super(props);
181 |
182 | this.state = {error: null};
183 | }
184 |
185 | onSubmit = event => {
186 | this.props.firebase
187 | .doSignInWithFacebook()
188 | .then(socialAuthUser => {
189 | // Create a user in your Firebase Realtime Database too
190 | return this.props.firebase.user(socialAuthUser.user.uid).set(
191 | {
192 | username: socialAuthUser.additionalUserInfo.profile.name,
193 | email: socialAuthUser.additionalUserInfo.profile.email
194 | },
195 | {merge: true}
196 | );
197 | })
198 | .then(() => {
199 | this.setState({error: null});
200 | this.props.history.push(ROUTES.HOME);
201 | })
202 | .catch(error => {
203 | if (error.code === ERROR_CODE_ACCOUNT_EXISTS) {
204 | error.message = ERROR_MSG_ACCOUNT_EXISTS;
205 | }
206 |
207 | this.props.setErrorMessage(error);
208 | });
209 |
210 | event.preventDefault();
211 | };
212 |
213 | render() {
214 | const {error} = this.state;
215 |
216 | return (
217 |
218 | Sign In with Facebook
219 |
220 | );
221 | }
222 | }
223 |
224 | class SignInTwitterBase extends Component {
225 | constructor(props) {
226 | super(props);
227 |
228 | this.state = {error: null};
229 | }
230 |
231 | onSubmit = event => {
232 | this.props.firebase
233 | .doSignInWithTwitter()
234 | .then(socialAuthUser => {
235 | // Create a user in your Firebase Realtime Database too
236 | return this.props.firebase.user(socialAuthUser.user.uid).set(
237 | {
238 | username: socialAuthUser.additionalUserInfo.profile.name,
239 | email: socialAuthUser.additionalUserInfo.profile.email
240 | },
241 | {merge: true}
242 | );
243 | })
244 | .then(() => {
245 | this.setState({error: null});
246 | this.props.history.push(ROUTES.HOME);
247 | })
248 | .catch(error => {
249 | if (error.code === ERROR_CODE_ACCOUNT_EXISTS) {
250 | error.message = ERROR_MSG_ACCOUNT_EXISTS;
251 | }
252 |
253 | this.props.setErrorMessage(error);
254 | });
255 |
256 | event.preventDefault();
257 | };
258 |
259 | render() {
260 | const {error} = this.state;
261 |
262 | return (
263 |
264 | Sign In with Twitter
265 |
266 | );
267 | }
268 | }
269 |
270 | const SignInForm = compose(
271 | withRouter,
272 | withFirebase
273 | )(SignInFormBase);
274 |
275 | const SignInGoogle = compose(
276 | withRouter,
277 | withFirebase
278 | )(SignInGoogleBase);
279 |
280 | const SignInFacebook = compose(
281 | withRouter,
282 | withFirebase
283 | )(SignInFacebookBase);
284 |
285 | const SignInTwitter = compose(
286 | withRouter,
287 | withFirebase
288 | )(SignInTwitterBase);
289 |
290 | export default SignInPage;
291 |
292 | export {SignInForm, SignInGoogle, SignInFacebook, SignInTwitter};
293 |
--------------------------------------------------------------------------------
/bunker/src/pages/SignUp/SignUp.js:
--------------------------------------------------------------------------------
1 | import React, {Component} from "react";
2 | import {Link, withRouter} from "react-router-dom";
3 | import {compose} from "recompose";
4 |
5 | import {withFirebase} from "../../server/Firebase";
6 | import * as ROUTES from "../../constants/routes";
7 | import * as ROLES from "../../constants/roles";
8 |
9 | import {Button, Form, Grid, Header, Segment, Message} from "semantic-ui-react";
10 |
11 | const SignUpPage = () => (
12 |
13 |
14 |
15 |
16 | Sign Up to Join Us
17 |
18 |
19 |
20 |
21 | );
22 |
23 | const INITIAL_STATE = {
24 | username: "",
25 | email: "",
26 | passwordOne: "",
27 | passwordTwo: "",
28 | isAdmin: false,
29 | error: null
30 | };
31 |
32 | const ERROR_CODE_ACCOUNT_EXISTS = "auth/email-already-in-use";
33 |
34 | const ERROR_MSG_ACCOUNT_EXISTS = `
35 | An account with this E-Mail address already exists.
36 | `;
37 |
38 | class SignUpFormBase extends Component {
39 | constructor(props) {
40 | super(props);
41 |
42 | this.state = {...INITIAL_STATE};
43 | }
44 |
45 | onSubmit = event => {
46 | const {username, email, passwordOne, isAdmin} = this.state;
47 | const roles = [];
48 |
49 | if (isAdmin) {
50 | roles.push(ROLES.ADMIN);
51 | }
52 |
53 | this.props.firebase
54 | .doCreateUserWithEmailAndPassword(email, passwordOne)
55 | .then(authUser => {
56 | console.log("add to DB");
57 | return this.props.firebase.addUserToDB(authUser, email, username);
58 | })
59 | .then(() => {
60 | this.setState({...INITIAL_STATE});
61 | this.props.history.push(ROUTES.HOME);
62 | })
63 | .catch(error => {
64 | if (error.code === ERROR_CODE_ACCOUNT_EXISTS) {
65 | error.message = ERROR_MSG_ACCOUNT_EXISTS;
66 | }
67 |
68 | this.setState({error});
69 | });
70 |
71 | event.preventDefault();
72 | };
73 |
74 | onChange = event => {
75 | this.setState({[event.target.name]: event.target.value});
76 | };
77 |
78 | onChangeCheckbox = event => {
79 | this.setState({[event.target.name]: event.target.checked});
80 | };
81 |
82 | render() {
83 | const {
84 | username,
85 | email,
86 | passwordOne,
87 | passwordTwo,
88 | isAdmin,
89 | error
90 | } = this.state;
91 |
92 | const isInvalid =
93 | passwordOne !== passwordTwo ||
94 | passwordOne === "" ||
95 | email === "" ||
96 | username === "";
97 |
98 | return (
99 |
100 |
181 | {error &&
182 |
183 | Oh snap! You got an error!
184 | {error.message}
185 | }
186 |
187 | );
188 | }
189 | }
190 |
191 | const SignUpLink = () => (
192 |
193 | Don't have an account? Sign Up
194 |
195 | );
196 |
197 | const SignUpForm = compose(
198 | withRouter,
199 | withFirebase
200 | )(SignUpFormBase);
201 |
202 | export default SignUpPage;
203 |
204 | export {SignUpForm, SignUpLink};
205 |
--------------------------------------------------------------------------------
/bunker/src/pages/Users/UserItem.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 |
3 | import { withFirebase } from '../../server/Firebase';
4 |
5 | class UserItem extends Component {
6 | constructor(props) {
7 | super(props);
8 |
9 | this.state = {
10 | loading: false,
11 | user: null,
12 | ...props.location.state,
13 | };
14 | }
15 |
16 | componentDidMount() {
17 | if (this.state.user) {
18 | return;
19 | }
20 |
21 | this.setState({ loading: true });
22 |
23 | this.props.firebase
24 | .user(this.props.match.params.id)
25 | .on('value', snapshot => {
26 | this.setState({
27 | user: snapshot.val(),
28 | loading: false,
29 | });
30 | });
31 | }
32 |
33 | componentWillUnmount() {
34 | this.props.firebase.user(this.props.match.params.id).off();
35 | }
36 |
37 | onSendPasswordResetEmail = () => {
38 | this.props.firebase.doPasswordReset(this.state.user.email);
39 | };
40 |
41 | render() {
42 | const { user, loading } = this.state;
43 |
44 | return (
45 |
46 |
User ({this.props.match.params.id})
47 | {loading &&
Loading ...
}
48 |
49 | {user && (
50 |
51 |
52 | ID: {user.uid}
53 |
54 |
55 | E-Mail: {user.email}
56 |
57 |
58 | Username: {user.username}
59 |
60 |
61 |
65 | Send Password Reset
66 |
67 |
68 |
69 | )}
70 |
71 | );
72 | }
73 | }
74 |
75 | export default withFirebase(UserItem);
76 |
--------------------------------------------------------------------------------
/bunker/src/pages/Users/UserList.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Link } from 'react-router-dom';
3 |
4 | import { withFirebase } from '../../server/Firebase';
5 | import * as ROUTES from '../../constants/routes';
6 |
7 | class UserList extends Component {
8 | constructor(props) {
9 | super(props);
10 |
11 | this.state = {
12 | loading: false,
13 | users: [],
14 | };
15 | }
16 |
17 | componentDidMount() {
18 | this.setState({ loading: true });
19 |
20 | this.props.firebase.users().on('value', snapshot => {
21 | const usersObject = snapshot.val();
22 |
23 | const usersList = Object.keys(usersObject).map(key => ({
24 | ...usersObject[key],
25 | uid: key,
26 | }));
27 |
28 | this.setState({
29 | users: usersList,
30 | loading: false,
31 | });
32 | });
33 | }
34 |
35 | componentWillUnmount() {
36 | this.props.firebase.users().off();
37 | }
38 |
39 | render() {
40 | const { users, loading } = this.state;
41 |
42 | return (
43 |
44 |
Users
45 | {loading &&
Loading ...
}
46 |
47 | {users.map(user => (
48 |
49 |
50 | ID: {user.uid}
51 |
52 |
53 | E-Mail: {user.email}
54 |
55 |
56 | Username: {user.username}
57 |
58 |
59 |
65 | Details
66 |
67 |
68 |
69 | ))}
70 |
71 |
72 | );
73 | }
74 | }
75 |
76 | export default withFirebase(UserList);
77 |
--------------------------------------------------------------------------------
/bunker/src/pages/Users/index.js:
--------------------------------------------------------------------------------
1 | import UserList from './UserList';
2 | import UserItem from './UserItem';
3 |
4 | export { UserList, UserItem };
5 |
--------------------------------------------------------------------------------
/bunker/src/server/Firebase/context.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | const FirebaseContext = React.createContext(null);
4 |
5 | export const withFirebase = Component => props => (
6 |
7 | {firebase => }
8 |
9 | );
10 |
11 | export default FirebaseContext;
12 |
--------------------------------------------------------------------------------
/bunker/src/server/Firebase/firebase.js:
--------------------------------------------------------------------------------
1 | import app from "firebase/app";
2 | import "firebase/auth";
3 | import "firebase/database";
4 | import "firebase/firestore";
5 | import _ from "lodash";
6 |
7 | import Config from "./config";
8 |
9 | const config = Config;
10 | class Firebase {
11 | constructor() {
12 | app.initializeApp(config);
13 |
14 | /* Helper */
15 |
16 | this.serverValue = app.database.ServerValue;
17 | this.emailAuthProvider = app.auth.EmailAuthProvider;
18 |
19 | /* Firebase APIs */
20 |
21 | this.auth = app.auth();
22 | //this.db = app.database();
23 | this.database = app.firestore();
24 |
25 | this.FieldValue = app.firestore.FieldValue;
26 |
27 | /* Social Sign In Method Provider */
28 |
29 | this.googleProvider = new app.auth.GoogleAuthProvider();
30 | this.facebookProvider = new app.auth.FacebookAuthProvider();
31 | this.twitterProvider = new app.auth.TwitterAuthProvider();
32 | }
33 |
34 | // *** Auth API ***
35 |
36 | doCreateUserWithEmailAndPassword = (email, password) =>
37 | this.auth.createUserWithEmailAndPassword(email, password);
38 |
39 | doSignInWithEmailAndPassword = (email, password) =>
40 | this.auth.signInWithEmailAndPassword(email, password);
41 |
42 | doSignInWithGoogle = () => this.auth.signInWithPopup(this.googleProvider);
43 |
44 | doSignInWithFacebook = () => this.auth.signInWithPopup(this.facebookProvider);
45 |
46 | doSignInWithTwitter = () => this.auth.signInWithPopup(this.twitterProvider);
47 |
48 | doSignOut = () => this.auth.signOut();
49 |
50 | doPasswordReset = email => this.auth.sendPasswordResetEmail(email);
51 |
52 | doSendEmailVerification = () => {
53 | this.auth.currentUser
54 | .sendEmailVerification({
55 | url: config.url
56 | })
57 | .then(() => console.log("Verification email sent."))
58 | .catch(error => error);
59 | };
60 | doPasswordUpdate = password => this.auth.currentUser.updatePassword(password);
61 | //Base API call
62 | user = uid => this.database.collection("users").doc(uid);
63 |
64 | hotelRef = uid => this.database.collection("hotels").doc(uid);
65 |
66 | locationRef = uid => this.database.collection("locations").doc(uid).get();
67 |
68 | reservationRef = uid => this.database.collection("reservations").doc(uid);
69 | reservationsRef = () => this.database.collection("reservations");
70 |
71 | // *** Merge Auth and DB User API *** //
72 |
73 | onAuthUserListener = (next, fallback) =>
74 | this.auth.onAuthStateChanged(authUser => {
75 | let subscribe;
76 | if (authUser) {
77 | subscribe = this.user(authUser.uid)
78 | .onSnapshot( snapshot => {
79 | console.log(1);
80 | const dbUser = snapshot.data();
81 |
82 | // default empty roles
83 | if (!dbUser.roles) {
84 | dbUser.roles = [];
85 | }
86 |
87 | // merge auth and db user
88 | authUser = {
89 | uid: authUser.uid,
90 | email: authUser.email,
91 | emailVerified: authUser.emailVerified,
92 | providerData: authUser.providerData,
93 | ...dbUser
94 | };
95 |
96 | next(authUser);
97 | }
98 | )
99 |
100 | } else {
101 | fallback();
102 | }
103 | });
104 |
105 | // *** Database API *** //
106 |
107 | getAllHotels = () =>
108 | this.database
109 | .collection("hotels")
110 | .get()
111 | .then(hotels => {
112 | let result = [];
113 | hotels.forEach(snapshot => {
114 | const obj = {
115 | id: snapshot.id,
116 | data: {...snapshot.data()}
117 | };
118 | result.push(obj);
119 | });
120 | return result;
121 | });
122 |
123 | addGoogleUserToDB = socialAuthUser => {
124 | return this.user(socialAuthUser.user.uid).set(
125 | {
126 | username: socialAuthUser.user.displayName,
127 | email: socialAuthUser.user.email,
128 | reservations: this.FieldValue.arrayUnion(""),
129 | reward_points: this.FieldValue.increment(0)
130 | },
131 | {merge: true}
132 | );
133 | };
134 |
135 | addUserToDB = (authUser, email, username) => {
136 | const data = {
137 | user_id: authUser.user.uid,
138 | username: username,
139 | email: email,
140 | reservations: [],
141 | reward_points: 0
142 | };
143 |
144 | return this.user(data.user_id)
145 | .set(data)
146 | .then(() => {
147 | console.log("Successfully created account.");
148 | return this.doSendEmailVerification();
149 | })
150 | .catch(error => error);
151 | };
152 |
153 | editUserAccount = (user_id, data) => {
154 | return this.user(user_id)
155 | .update(data)
156 | .then(() => {
157 | console.log("User data was successfully changed");
158 | return true;
159 | })
160 | .catch(error => error);
161 | };
162 |
163 | checkForConflictWithDates = (new_start, new_end, user_id) => {
164 | return this.reservationsRef()
165 | .where("user_id", "==", user_id)
166 | .get()
167 | .then(snapshots => {
168 | let reservations = [];
169 | snapshots.forEach(snapshot => {
170 | reservations.push({
171 | data: snapshot.data()
172 | });
173 | });
174 | return reservations.every(res => {
175 | const check =
176 | new_end < res.data.start_date || new_start > res.data.end_date;
177 | console.log(
178 | new_start,
179 | res.data.start_date,
180 | new_end,
181 | res.data.end_date,
182 | check
183 | );
184 | return check;
185 | });
186 | });
187 | };
188 |
189 | addReservationToDB = (user_id, data, isUseReward, usedReward) => {
190 | return this.checkForConflictWithDates(
191 | data.start_date,
192 | data.end_date,
193 | user_id
194 | ).then(check => {
195 | if (check) {
196 | this.reservationsRef()
197 | .add(data)
198 | .then(res_doc => {
199 | if (isUseReward) {
200 | this.editUserAccount(user_id, {
201 | reservations: this.FieldValue.arrayUnion(res_doc.id),
202 | reward_points: this.FieldValue.increment(-usedReward)
203 | });
204 | return true;
205 | } else {
206 | this.editUserAccount(user_id, {
207 | reservations: this.FieldValue.arrayUnion(res_doc.id),
208 | reward_points: this.FieldValue.increment(
209 | Math.floor((data.price || 0) / 10)
210 | )
211 | });
212 | return true;
213 | }
214 | })
215 | .catch(error => console.log("Failed to add res " + error));
216 | } else {
217 | console.log("Cant have multiple booking");
218 | return false;
219 | }
220 | });
221 | };
222 |
223 | //edit reservation data
224 | editReservationInfo = (reservation_id, data, user_id) => {
225 | return this.reservationRef(reservation_id)
226 | .update(data)
227 | .then(() => {
228 | console.log("Reservation data was successfully changed");
229 | return true;
230 | })
231 | .catch(error => {
232 | console.error("Error editing document: ", error);
233 | return error;
234 | });
235 | };
236 |
237 | //Delete reservation
238 | deleteReservationFromDB = (reservation_id, user_id, price = 0) => {
239 | return this.user(user_id)
240 | .update({
241 | reservations: this.FieldValue.arrayRemove(reservation_id),
242 | reward_points: this.FieldValue.increment(-Math.floor(price / 10))
243 | })
244 | .then(() => {
245 | return this.reservationRef(reservation_id)
246 | .delete()
247 | .then(() => {
248 | console.log("Done delete reservation");
249 | })
250 | .catch(err => {
251 | console.log("Error in delete reservation", err);
252 | return err;
253 | });
254 | })
255 | .catch(err => {
256 | console.log(
257 | "Error in remove reservations id or decrease reward_points"
258 | );
259 | return err;
260 | });
261 | };
262 |
263 | //location Search function
264 | getCities = next =>
265 | this.database
266 | .collection("locations")
267 | .get()
268 | .then(snapshot => {
269 | let cities = [];
270 | snapshot.forEach(city => {
271 | const obj = {
272 | id: city.id,
273 | data: {...city.data()}
274 | };
275 | cities.push(obj);
276 | });
277 |
278 | return cities;
279 | });
280 | subscribeUserReward = (userID, doChange, doError) => {
281 | return this.user(userID).onSnapshot(
282 | snapshot => {
283 | doChange(snapshot.data().reward_points);
284 | },
285 | error => {
286 | console.log(error);
287 | }
288 | );
289 | };
290 |
291 | subscribeReservations = (
292 | userID,
293 | // start_date,
294 | doChange,
295 | doError
296 | ) => {
297 | return this.reservationsRef()
298 | .where("user_id", "==", userID)
299 | .orderBy("start_date")
300 | .onSnapshot(
301 | snapshot => {
302 | let reservations = [];
303 | snapshot.forEach(doc =>
304 | reservations.push({id: doc.id, data: doc.data()})
305 | );
306 | doChange(reservations);
307 | },
308 | error => {
309 | console.log(error);
310 | doError(error);
311 | }
312 | );
313 | };
314 |
315 | getReservations = reservationIDs => {
316 | let result = [];
317 | let promise = [];
318 | reservationIDs.forEach(reservationID =>
319 | promise.push(this.reservationRef(reservationID).get())
320 | );
321 | return Promise.all(promise).then(snapshots => {
322 | snapshots.forEach(snapshot => {
323 | const obj = {
324 | id: snapshot.id,
325 | data: snapshot.data()
326 | };
327 | result.push(obj);
328 | });
329 | return result;
330 | });
331 | };
332 |
333 | getHotels = hotelIDs => {
334 | let promise = hotelIDs.map(hotelID => this.hotelRef(hotelID).get());
335 | return Promise.all(promise).then(snapshots => {
336 | let result = snapshots.map(snapshot => ({
337 | id: snapshot.id,
338 | data: snapshot.data()
339 | }));
340 | return result;
341 | });
342 | };
343 |
344 | //Data Retrive and filter
345 | getLocationHotel = location => {
346 | const promises = [];
347 | location.data.hotels.forEach(hotelRef => {
348 | promises.push(hotelRef.get());
349 | });
350 | return Promise.all(promises).then(snapshots =>{
351 | let result = snapshots.map(snapshot => ({
352 | id: snapshot.id,
353 | data: snapshot.data()
354 | }))
355 | return result;
356 | })
357 | };
358 |
359 | getHotelsRoomTypeSearch = (hotels, room_types) => {
360 | let result = [];
361 | hotels.forEach(hotel => {
362 | const check = room_types.every(room_type =>
363 | hotel.data.room_types.includes(room_type)
364 | );
365 | if (check) result.push(hotel);
366 | });
367 | return result;
368 | };
369 |
370 | getHotelRoomAvailableDate = (hotels, date_start, date_end) => {
371 | let result = [];
372 | hotels.forEach(hotel => {
373 | const isAvailable = hotel.data.rooms.some(room =>
374 | room.unavailable_dates.every(dateRange => {
375 | const roomCheck =
376 | date_end < dateRange.startDate || date_start > dateRange.endDate;
377 | if (!roomCheck) hotel.data.rooms.pop(room);
378 | return roomCheck;
379 | })
380 | );
381 | if (isAvailable) {
382 | result.push(hotel);
383 | }
384 | });
385 | return result;
386 | };
387 |
388 | updateUnavailableDatetoRoom = (hotel, date_start, date_end) => {
389 | let availableRoom = hotel[0].data.rooms[0];
390 | const hotelRef = this.hotelRef(hotel.id);
391 | //remove the current available room
392 | hotelRef
393 | .update({
394 | rooms: this.firebase.firestore.FieldValue.arrayRemove(availableRoom)
395 | })
396 | .then(() => console.log("Successfully Remove available room"))
397 | .catch(error => console.log(error));
398 | //Add edited available room
399 | availableRoom.unavailable_dates.push({startDate: 8, endDate: 8});
400 | this.hotelRef(hotel.id)
401 | .update({
402 | rooms: this.firebase.firestore.FieldValue.arrayUnion(availableRoom)
403 | })
404 | .then(() => console.log("Successfully add edited available room"))
405 | .catch(error => console.log(error));
406 | };
407 | /**
408 | * filter hotels arrays
409 | * @param {array} hotels hotels object arrays
410 | * @param {array string} field the deep of the filter object. Ex: ['room', 'price'] will the{ hotel: { room: { price: 1}}}
411 | * Ex2: ['price'] will filter the {hotel: { price}}
412 | * @param {function} compareFunction function to compare. Ex: (a,b) => a < b
413 | * @param {any} compareValue value that will be compare to: filter everything larger smaller than 2, compareFunction = (a, b) => a < b, compareValue = 2
414 | * @return {array} array of hotels
415 | */
416 | filterHotels = (hotels, field, compareFunction, compareValue) => {
417 | return hotels.filter(hotel => {
418 | return compareFunction(_.get(hotel, field), compareValue);
419 | });
420 | };
421 | /**
422 | * sorted hotels arrays
423 | * @param {array} hotels hotels object arrays
424 | * @param {array string} field the deep of the filter object. Ex: ['room', 'price'] will the{ hotel: { room: { price: 1}}}
425 | * Ex2: ['price'] will filter the {hotel: { price}}
426 | * @param {boolean} isAscending if true, sort Asccending, if false sort descending
427 | * @return {array} array of sorted hotels
428 | */
429 | sortHotels = (hotels, field, isAscending = true) => {
430 | let type = typeof _.get(hotels[0], field);
431 | let compareFunction;
432 | if (type === "string") {
433 | compareFunction = (a, b) => {
434 | var stringA = _.get(a, field).toUpperCase(); // ignore upper and lowercase
435 | var stringB = _.get(b, field).toUpperCase(); // ignore upper and lowercase
436 | if (stringA < stringB) {
437 | return -1;
438 | }
439 | if (stringA > stringB) {
440 | return 1;
441 | }
442 | // names must be equal
443 | return 0;
444 | };
445 | } else {
446 | compareFunction = (a, b) => _.get(a, field) - _.get(b, field);
447 | }
448 | if (isAscending) {
449 | return hotels.sort(compareFunction);
450 | } else return hotels.sort(compareFunction).reverse();
451 | };
452 | }
453 |
454 | export default Firebase;
455 |
--------------------------------------------------------------------------------
/bunker/src/server/Firebase/firebase3.js:
--------------------------------------------------------------------------------
1 | import app from "firebase/app";
2 | import "firebase/auth";
3 | import "firebase/firestore";
4 | import "firebase/database";
5 |
6 | import Config from "./config";
7 |
8 | const config = Config;
9 |
10 | class Firebase {
11 | constructor() {
12 | app.initializeApp(config);
13 | //Initialize firebase authentication
14 | this.auth = app.auth();
15 | //Initialize firebase database
16 | this.database = app.firestore();
17 | //Initialize Google Authentication
18 | this.googleProvider = new app.auth.GoogleAuthProvider();
19 | }
20 |
21 | //Auth API
22 | //Google SignIn
23 | googleSignIn = () => {
24 | this.auth
25 | .signInWithPopup(this.googleProvider)
26 | .then(result => {
27 | console.log(result);
28 | console.log("Google Account Linked");
29 | })
30 | .catch(err => {
31 | console.log(err);
32 | console.log("Failed to link.");
33 | });
34 | };
35 |
36 | signIn = (email, password) => {
37 | this.auth.signInWithEmailAndPassword(email, password).catch(error => {
38 | return error;
39 | });
40 | };
41 |
42 | //Google Logout
43 | signOut = () => {
44 | this.auth.signOut().catch(error => {
45 | return error;
46 | });
47 | };
48 |
49 | createAccount = (email, password, data) => {
50 | this.auth
51 | .createUserWithEmailAndPassword(email, password)
52 | .then(authUser => {
53 | data.user_id = authUser.user.uid;
54 | data.reservations = [];
55 | data.reward_points = 0;
56 | this.database
57 | .collection("users")
58 | .doc(data.user_id)
59 | .set(data)
60 | .then(console.log("Successfully created account."))
61 | .catch(error => error);
62 | })
63 | .catch(error => {
64 | return error;
65 | });
66 | };
67 |
68 | // ******User API**********
69 | //edit user data
70 | editUser = (user_id, data) => {
71 | this.database
72 | .collection("users")
73 | .doc(user_id)
74 | .update(data)
75 | .then(() => {
76 | console.log("User data was successfully changed");
77 | return true;
78 | })
79 | .catch(error => {
80 | console.error("Error editing document: ", error);
81 | return false;
82 | });
83 | };
84 |
85 | getCities = () =>
86 | this.firestore
87 | .collection("locations")
88 | .get()
89 | .then(snapshot => {
90 | let cities = [];
91 | snapshot.forEach(city => {
92 | const obj = {
93 | id: city.id,
94 | data: {...city.data()}
95 | };
96 | cities.push(obj);
97 | });
98 |
99 | return cities;
100 | });
101 | getLocationHotel = location => {
102 | let hotels = [];
103 | location.data.hotels.forEach(hotelRef => {
104 | hotelRef.get().then(hotel => {
105 | const obj = {
106 | id: hotel.id,
107 | data: {...hotel.data()}
108 | };
109 | hotels.push(obj);
110 | });
111 | });
112 | return hotels;
113 | };
114 |
115 | getHotelsRoomTypeSearch = (hotels, room_types) => {
116 | let result = [];
117 | hotels.forEach(hotel => {
118 | const check = room_types.every(room_type =>
119 | hotel.data.room_types.includes(room_type)
120 | );
121 | if (check) result.push(hotel);
122 | });
123 | return result;
124 | };
125 |
126 | getHotelRoomAvailableDate = (hotels, date_start, date_end) => {
127 | let result = [];
128 | hotels.forEach(hotel => {
129 | const isAvaliable = hotel.data.rooms.some(room =>
130 | room.unavailable_dates.every(dateRange => {
131 | const roomCheck =
132 | date_end < dateRange.startDate || date_start > dateRange.endDate;
133 | if (!roomCheck) hotel.data.rooms.pop(room);
134 | return roomCheck;
135 | })
136 | );
137 | if (isAvaliable) {
138 | result.push(hotel);
139 | }
140 | });
141 | return result;
142 | };
143 |
144 | updateUnavailableDatetoRoom = (hotel, date_start, date_end) => {
145 | let availableRoom = hotel[0].data.rooms[0];
146 | const hotelRef = this.database.collection("hotels").doc(hotel.id);
147 | //remove the current available room
148 | hotelRef
149 | .update({
150 | rooms: this.firebase.firestore.FieldValue.arrayRemove(availableRoom)
151 | })
152 | .then(() => console.log("Successfully Remove available room"))
153 | .catch(error => console.log(error));
154 | //Add edited available room
155 | availableRoom.unavailable_dates.push({startDate: 8, endDate: 8});
156 | this.database
157 | .collection("hotels")
158 | .doc(hotel.id)
159 | .update({
160 | rooms: this.firebase.firestore.FieldValue.arrayUnion(availableRoom)
161 | })
162 | .then(() => console.log("Successfully add edited available room"))
163 | .catch(error => console.log(error));
164 | };
165 |
166 | //*****Reservation API*********
167 | //add reservation data
168 | addReservation = (user_id, data) => {
169 | //Check that data has valid properties
170 | //get user's document
171 | let user;
172 | this.database
173 | .collection("users")
174 | .doc(user_id)
175 | .get()
176 | .then(snapshot => {
177 | user = {
178 | id: snapshot.id,
179 | data: snapshot.data()
180 | };
181 | })
182 | .catch(error => error("Users not exits"));
183 | if (
184 | data.hasOwnProperty("user_id") &&
185 | data.hasOwnProperty("hotel_id") &&
186 | data.hasOwnProperty("room_id") &&
187 | data.hasOwnProperty("price") &&
188 | data.hasOwnProperty("start_date") &&
189 | data.hasOwnProperty("end_date")
190 | ) {
191 | //Create new reservation document
192 | this.database
193 | .collection("reservations")
194 | .add(data)
195 | .then(res_doc => {
196 | //Add reservation_id to reservation document
197 | data.reservation_id = res_doc.id;
198 | this.editReservation(res_doc.id, data);
199 | let new_res = user.data.reservations;
200 | new_res.push(data.reservation_id);
201 | this.editUser(user_id, {reservation: new_res});
202 | })
203 | .catch(err => {
204 | console.log("Failed to add new reservation. " + err);
205 | return false;
206 | });
207 | return true;
208 | } else return false;
209 | };
210 |
211 | //edit reservation data
212 | editReservation = (reservation_id, data) => {
213 | this.database
214 | .collection("reservations")
215 | .doc(reservation_id)
216 | .update(data)
217 | .then(() => {
218 | console.log("Reservation data was successfully changed");
219 | return true;
220 | })
221 | .catch(error => {
222 | console.error("Error editing document: ", error);
223 | return false;
224 | });
225 | };
226 |
227 | //Delete reservation
228 | deleteReservation = (reservation_id, user_id) => {
229 | //delete from reservations collection
230 | this.database
231 | .collection("reservations")
232 | .doc(reservation_id)
233 | .delete()
234 | .then(() => {
235 | console.log(
236 | "Successfully deleted reservation from reservation collection."
237 | );
238 | //delete reservation from users reservations
239 | this.database
240 | .collection("users")
241 | .doc(user_id)
242 | .get()
243 | .then(doc => {
244 | let user_res = doc.data().reservations; //array of users reservation_id's
245 | //Update user's reservations
246 | if (user_res.indexOf(reservation_id) >= 0) {
247 | user_res.splice(user_res.indexOf(reservation_id), 1); //remove reservation_id from array
248 | this.editUser(user_id, {reservations: user_res});
249 | } else {
250 | console.log("Reservation was not present.");
251 | return false;
252 | }
253 | })
254 | .catch(err => {
255 | console.log("Failed to delete reservation from user. " + err);
256 | return false;
257 | });
258 | })
259 | .catch(err => {
260 | console.log(
261 | "Failed to delete reservation from reservation collection. " + err
262 | );
263 | return false;
264 | });
265 | return true;
266 | };
267 | }
268 |
269 | export default Firebase;
270 |
--------------------------------------------------------------------------------
/bunker/src/server/Firebase/index.js:
--------------------------------------------------------------------------------
1 | import FirebaseContext, { withFirebase } from './context';
2 | import Firebase from './firebase';
3 |
4 | export default Firebase;
5 |
6 | export { FirebaseContext, withFirebase };
7 |
--------------------------------------------------------------------------------
/bunker/src/server/Payment/PayPalButton.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom";
3 | import scriptLoader from "react-async-script-loader";
4 | import {paypalConfig} from "../Firebase/config";
5 | import {withFirebase} from "../Firebase";
6 | const CLIENT = paypalConfig;
7 |
8 | class PayPalButton extends React.Component {
9 | constructor(props) {
10 | super(props);
11 |
12 | this.state = {
13 | showButton: false
14 | };
15 |
16 | window.React = React;
17 | window.ReactDOM = ReactDOM;
18 | }
19 |
20 | componentDidMount() {
21 | const {isScriptLoaded, isScriptLoadSucceed} = this.props;
22 |
23 | if (isScriptLoaded && isScriptLoadSucceed) {
24 | this.setState({showButton: true});
25 | }
26 | }
27 |
28 | componentWillReceiveProps(nextProps) {
29 | const {isScriptLoaded, isScriptLoadSucceed} = nextProps;
30 |
31 | const isLoadedButWasntLoadedBefore =
32 | !this.state.showButton && !this.props.isScriptLoaded && isScriptLoaded;
33 |
34 | if (isLoadedButWasntLoadedBefore) {
35 | if (isScriptLoadSucceed) {
36 | this.setState({showButton: true});
37 | }
38 | }
39 | }
40 |
41 | render() {
42 | const {total, currency, commit, onSuccess, onError, onCancel, new_start, new_end, user_id} = this.props;
43 | const env = "sandbox";
44 | const client = CLIENT;
45 | const {showButton} = this.state;
46 |
47 | const paypal = window.PAYPAL;
48 |
49 | const payment = () =>
50 | paypal.rest.payment.create(env, client, {
51 | transactions: [
52 | {
53 | amount: {
54 | total,
55 | currency
56 | }
57 | }
58 | ]
59 | });
60 |
61 | const onAuthorize = (data, actions) =>{
62 | this.props.firebase.checkForConflictWithDates(new_start,new_end,user_id).then( check => {
63 | if(check){
64 | actions.payment.execute().then(() => {
65 | const payment = {
66 | paid: true,
67 | cancelled: false,
68 | payerID: data.payerID,
69 | paymentID: data.paymentID,
70 | paymentToken: data.paymentToken,
71 | returnUrl: data.returnUrl
72 | };
73 | onSuccess(payment);
74 | });
75 | }
76 | else{
77 | onError(new Error("MultipleBookingError"))
78 | }
79 | })
80 | }
81 | const style = {
82 | size: 'medium',
83 | color: 'blue',
84 | shape: 'rect',
85 | }
86 |
87 | return (
88 |
89 | {showButton && (
90 |
100 | )}
101 |
102 | );
103 | }
104 | }
105 |
106 | export default scriptLoader("https://www.paypalobjects.com/api/checkout.js")(
107 | withFirebase(PayPalButton)
108 | );
109 |
--------------------------------------------------------------------------------
/bunker/src/server/Session/context.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const AuthUserContext = React.createContext(null);
4 |
5 | export default AuthUserContext;
6 |
--------------------------------------------------------------------------------
/bunker/src/server/Session/index.js:
--------------------------------------------------------------------------------
1 | import AuthUserContext from './context';
2 | import withAuthentication from './withAuthentication';
3 | import withAuthorization from './withAuthorization';
4 | import withEmailVerification from './withEmailVerification';
5 |
6 | export {
7 | AuthUserContext,
8 | withAuthentication,
9 | withAuthorization,
10 | withEmailVerification,
11 | };
12 |
--------------------------------------------------------------------------------
/bunker/src/server/Session/withAuthentication.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import AuthUserContext from './context';
4 | import { withFirebase } from '../Firebase/index';
5 |
6 | const withAuthentication = Component => {
7 | class WithAuthentication extends React.Component {
8 | constructor(props) {
9 | super(props);
10 |
11 | this.state = {
12 | authUser: JSON.parse(localStorage.getItem('authUser')),
13 | };
14 | }
15 |
16 | componentDidMount() {
17 | this.listener = this.props.firebase.onAuthUserListener(
18 | authUser => {
19 | localStorage.setItem('authUser', JSON.stringify(authUser));
20 | this.setState({ authUser });
21 | },
22 | () => {
23 | localStorage.removeItem('authUser');
24 | this.setState({ authUser: null });
25 | },
26 | );
27 | }
28 |
29 | componentWillUnmount() {
30 | this.listener();
31 | }
32 |
33 | render() {
34 | return (
35 |
36 |
37 |
38 | );
39 | }
40 | }
41 |
42 | return withFirebase(WithAuthentication);
43 | };
44 |
45 | export default withAuthentication;
46 |
--------------------------------------------------------------------------------
/bunker/src/server/Session/withAuthorization.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { withRouter } from 'react-router-dom';
3 | import { compose } from 'recompose';
4 |
5 | import AuthUserContext from './context';
6 | import { withFirebase } from '../Firebase/index';
7 | import * as ROUTES from '../../constants/routes';
8 |
9 | const withAuthorization = condition => Component => {
10 | class WithAuthorization extends React.Component {
11 | componentDidMount() {
12 | this.listener = this.props.firebase.onAuthUserListener(
13 | authUser => {
14 | if (!condition(authUser)) {
15 | this.props.history.push(ROUTES.SIGN_IN);
16 | }
17 | },
18 | () => this.props.history.push(ROUTES.SIGN_IN),
19 | );
20 | }
21 |
22 | componentWillUnmount() {
23 | this.listener();
24 | }
25 |
26 | render() {
27 | return (
28 |
29 | {authUser =>
30 | condition(authUser) ? : null
31 | }
32 |
33 | );
34 | }
35 | }
36 |
37 | return compose(
38 | withRouter,
39 | withFirebase,
40 | )(WithAuthorization);
41 | };
42 |
43 | export default withAuthorization;
44 |
--------------------------------------------------------------------------------
/bunker/src/server/Session/withEmailVerification.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import AuthUserContext from './context';
4 | import { withFirebase } from '../Firebase/index';
5 |
6 | const needsEmailVerification = authUser =>
7 | authUser &&
8 | !authUser.emailVerified &&
9 | authUser.providerData
10 | .map(provider => provider.providerId)
11 | .includes('password');
12 |
13 | const withEmailVerification = Component => {
14 | class WithEmailVerification extends React.Component {
15 | constructor(props) {
16 | super(props);
17 |
18 | this.state = { isSent: false };
19 | }
20 |
21 | onSendEmailVerification = () => {
22 | this.props.firebase
23 | .doSendEmailVerification()
24 | .then(() => this.setState({ isSent: true }));
25 | };
26 |
27 | render() {
28 | return (
29 |
30 | {authUser =>
31 | needsEmailVerification(authUser) ? (
32 |
33 | {this.state.isSent ? (
34 |
35 | E-Mail confirmation sent: Check your E-Mails (Spam
36 | folder included) for a confirmation E-Mail.
37 | Refresh this page once you confirmed your E-Mail.
38 |
39 | ) : (
40 |
41 | Verify your E-Mail: Check your E-Mails (Spam folder
42 | included) for a confirmation E-Mail or send
43 | another confirmation E-Mail.
44 |
45 | )}
46 |
47 |
52 | Send confirmation E-Mail
53 |
54 |
55 | ) : (
56 |
57 | )
58 | }
59 |
60 | );
61 | }
62 | }
63 |
64 | return withFirebase(WithEmailVerification);
65 | };
66 |
67 | export default withEmailVerification;
68 |
--------------------------------------------------------------------------------
/bunker/src/serviceWorker.js:
--------------------------------------------------------------------------------
1 | // This optional code is used to register a service worker.
2 | // register() is not called by default.
3 |
4 | // This lets the app load faster on subsequent visits in production, and gives
5 | // it offline capabilities. However, it also means that developers (and users)
6 | // will only see deployed updates on subsequent visits to a page, after all the
7 | // existing tabs open on the page have been closed, since previously cached
8 | // resources are updated in the background.
9 |
10 | // To learn more about the benefits of this model and instructions on how to
11 | // opt-in, read http://bit.ly/CRA-PWA
12 |
13 | const isLocalhost = Boolean(
14 | window.location.hostname === 'localhost' ||
15 | // [::1] is the IPv6 localhost address.
16 | window.location.hostname === '[::1]' ||
17 | // 127.0.0.1/8 is considered localhost for IPv4.
18 | window.location.hostname.match(
19 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
20 | )
21 | );
22 |
23 | export function register(config) {
24 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
25 | // The URL constructor is available in all browsers that support SW.
26 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);
27 | if (publicUrl.origin !== window.location.origin) {
28 | // Our service worker won't work if PUBLIC_URL is on a different origin
29 | // from what our page is served on. This might happen if a CDN is used to
30 | // serve assets; see https://github.com/facebook/create-react-app/issues/2374
31 | return;
32 | }
33 |
34 | window.addEventListener('load', () => {
35 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
36 |
37 | if (isLocalhost) {
38 | // This is running on localhost. Let's check if a service worker still exists or not.
39 | checkValidServiceWorker(swUrl, config);
40 |
41 | // Add some additional logging to localhost, pointing developers to the
42 | // service worker/PWA documentation.
43 | navigator.serviceWorker.ready.then(() => {
44 | console.log(
45 | 'This web app is being served cache-first by a service ' +
46 | 'worker. To learn more, visit http://bit.ly/CRA-PWA'
47 | );
48 | });
49 | } else {
50 | // Is not localhost. Just register service worker
51 | registerValidSW(swUrl, config);
52 | }
53 | });
54 | }
55 | }
56 |
57 | function registerValidSW(swUrl, config) {
58 | navigator.serviceWorker
59 | .register(swUrl)
60 | .then(registration => {
61 | registration.onupdatefound = () => {
62 | const installingWorker = registration.installing;
63 | if (installingWorker == null) {
64 | return;
65 | }
66 | installingWorker.onstatechange = () => {
67 | if (installingWorker.state === 'installed') {
68 | if (navigator.serviceWorker.controller) {
69 | // At this point, the updated precached content has been fetched,
70 | // but the previous service worker will still serve the older
71 | // content until all client tabs are closed.
72 | console.log(
73 | 'New content is available and will be used when all ' +
74 | 'tabs for this page are closed. See http://bit.ly/CRA-PWA.'
75 | );
76 |
77 | // Execute callback
78 | if (config && config.onUpdate) {
79 | config.onUpdate(registration);
80 | }
81 | } else {
82 | // At this point, everything has been precached.
83 | // It's the perfect time to display a
84 | // "Content is cached for offline use." message.
85 | console.log('Content is cached for offline use.');
86 |
87 | // Execute callback
88 | if (config && config.onSuccess) {
89 | config.onSuccess(registration);
90 | }
91 | }
92 | }
93 | };
94 | };
95 | })
96 | .catch(error => {
97 | console.error('Error during service worker registration:', error);
98 | });
99 | }
100 |
101 | function checkValidServiceWorker(swUrl, config) {
102 | // Check if the service worker can be found. If it can't reload the page.
103 | fetch(swUrl)
104 | .then(response => {
105 | // Ensure service worker exists, and that we really are getting a JS file.
106 | const contentType = response.headers.get('content-type');
107 | if (
108 | response.status === 404 ||
109 | (contentType != null && contentType.indexOf('javascript') === -1)
110 | ) {
111 | // No service worker found. Probably a different app. Reload the page.
112 | navigator.serviceWorker.ready.then(registration => {
113 | registration.unregister().then(() => {
114 | window.location.reload();
115 | });
116 | });
117 | } else {
118 | // Service worker found. Proceed as normal.
119 | registerValidSW(swUrl, config);
120 | }
121 | })
122 | .catch(() => {
123 | console.log(
124 | 'No internet connection found. App is running in offline mode.'
125 | );
126 | });
127 | }
128 |
129 | export function unregister() {
130 | if ('serviceWorker' in navigator) {
131 | navigator.serviceWorker.ready.then(registration => {
132 | registration.unregister();
133 | });
134 | }
135 | }
136 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "se165-backend",
3 | "version": "1.0.0",
4 | "dependencies": {
5 | "lodash": "^4.17.11",
6 | "react": "^16.8.3"
7 | },
8 | "devDependencies": {
9 | "firebase": "^5.8.3"
10 | }
11 | }
12 |
--------------------------------------------------------------------------------