diff --git a/src/lib/components/ListingCard/index.tsx b/src/lib/components/ListingCard/index.tsx index a4ae513..3dc83c1 100644 --- a/src/lib/components/ListingCard/index.tsx +++ b/src/lib/components/ListingCard/index.tsx @@ -1,6 +1,6 @@ import React, { useState } from 'react'; import { Card, Typography, Modal } from 'antd'; -import { UserOutlined } from '@ant-design/icons'; +import { UserOutlined, ArrowRightOutlined } from '@ant-design/icons'; import { Link } from 'react-router-dom'; import { iconColor, formatListingPrice, displaySuccessNotification } from '../../utils'; import { ListingCardActions } from './components'; @@ -77,16 +77,19 @@ export const ListingCard = ({ listing, viewerIsUser, refetch }: Props) => { {listingCardActionsElement} -

Are you sure you want to delete this listing?

- {title} + + {title} +

diff --git a/src/lib/graphql/globalTypes.ts b/src/lib/graphql/globalTypes.ts index aa04c40..a83f6d9 100644 --- a/src/lib/graphql/globalTypes.ts +++ b/src/lib/graphql/globalTypes.ts @@ -20,6 +20,13 @@ export enum ListingsFilter { PRICE_LOW_TO_HIGH = "PRICE_LOW_TO_HIGH", } +export interface CreateBookingInput { + id: string; + source: string; + checkIn: string; + checkOut: string; +} + export interface DeleteListingInput { id: string; } diff --git a/src/lib/graphql/mutations/CreateBooking/__generated__/CreateBooking.ts b/src/lib/graphql/mutations/CreateBooking/__generated__/CreateBooking.ts new file mode 100644 index 0000000..dc6409f --- /dev/null +++ b/src/lib/graphql/mutations/CreateBooking/__generated__/CreateBooking.ts @@ -0,0 +1,23 @@ +/* tslint:disable */ +/* eslint-disable */ +// @generated +// This file was automatically generated and should not be edited. + +import { CreateBookingInput } from "./../../../globalTypes"; + +// ==================================================== +// GraphQL mutation operation: CreateBooking +// ==================================================== + +export interface CreateBooking_createBooking { + __typename: "Booking"; + id: string; +} + +export interface CreateBooking { + createBooking: CreateBooking_createBooking; +} + +export interface CreateBookingVariables { + input: CreateBookingInput; +} diff --git a/src/lib/graphql/mutations/CreateBooking/index.ts b/src/lib/graphql/mutations/CreateBooking/index.ts new file mode 100644 index 0000000..6b5f254 --- /dev/null +++ b/src/lib/graphql/mutations/CreateBooking/index.ts @@ -0,0 +1,8 @@ +import { gql } from '@apollo/client'; +export const CREATE_BOOKING = gql` + mutation CreateBooking($input: CreateBookingInput!) { + createBooking(input: $input) { + id + } + } +`; diff --git a/src/lib/graphql/mutations/index.ts b/src/lib/graphql/mutations/index.ts index b6ffec0..2740fdf 100644 --- a/src/lib/graphql/mutations/index.ts +++ b/src/lib/graphql/mutations/index.ts @@ -1,6 +1,7 @@ -export * from './LogIn'; -export * from './LogOut'; export * from './ConnectStripe'; +export * from './CreateBooking'; export * from './DeleteListing'; export * from './DisconnectStripe'; export * from './HostListing'; +export * from './LogIn'; +export * from './LogOut'; diff --git a/src/lib/graphql/queries/EditListing/__generated__/EditListing.ts b/src/lib/graphql/queries/EditListing/__generated__/EditListing.ts index 23930e7..78c0c46 100644 --- a/src/lib/graphql/queries/EditListing/__generated__/EditListing.ts +++ b/src/lib/graphql/queries/EditListing/__generated__/EditListing.ts @@ -3,35 +3,35 @@ // @generated // This file was automatically generated and should not be edited. -import { ListingType } from './../../../globalTypes'; +import { ListingType } from "./../../../globalTypes"; // ==================================================== // GraphQL query operation: EditListing // ==================================================== export interface EditListing_listing_host { - __typename: 'User'; - id: string; + __typename: "User"; + id: string; } export interface EditListing_listing { - __typename: 'Listing'; - id: string; - title: string; - description: string; - image: string; - type: ListingType; - address: string; - city: string; - price: number; - numOfGuests: number; - host: EditListing_listing_host; + __typename: "Listing"; + id: string; + title: string; + description: string; + image: string; + type: ListingType; + address: string; + city: string; + price: number; + numOfGuests: number; + host: EditListing_listing_host; } export interface EditListing { - listing: EditListing_listing; + listing: EditListing_listing; } export interface EditListingVariables { - id: string; + id: string; } diff --git a/src/sections/AppHeader/components/MenuItems/index.tsx b/src/sections/AppHeader/components/MenuItems/index.tsx index 1774807..723bcdc 100644 --- a/src/sections/AppHeader/components/MenuItems/index.tsx +++ b/src/sections/AppHeader/components/MenuItems/index.tsx @@ -1,7 +1,8 @@ -import React, { useState } from 'react'; +import React from 'react'; +import { useHistory } from 'react-router-dom'; import { Button, Menu, Avatar } from 'antd'; import { HomeOutlined, UserOutlined, LogoutOutlined } from '@ant-design/icons'; -import { Link, Redirect } from 'react-router-dom'; +import { Link } from 'react-router-dom'; import useViewerState from '../../../../lib/context/useViewerState'; import { useMutation } from '@apollo/client'; import { LOG_OUT } from '../../../../lib/graphql/mutations'; @@ -16,15 +17,15 @@ const { SubMenu, Item } = Menu; // } export const MenuItems = () => { + const history = useHistory(); const { viewer, setViewer } = useViewerState(); - const [c, d] = useState(false); const [logOut] = useMutation(LOG_OUT, { onCompleted: (data) => { if (data && data.logout) { setViewer(data.logout); sessionStorage.removeItem('token'); - displaySuccessNotification("You've been Successfully Logged Out."); - d(true); + displaySuccessNotification('Successfully Logged Out.'); + history.push('/'); } }, onError: () => { @@ -36,10 +37,6 @@ export const MenuItems = () => { logOut(); }; - if (c) { - return ; - } - const subMenu = viewer.id && viewer.avatar ? ( }> diff --git a/src/sections/Listing/components/ListingCreateBookingModal/index.tsx b/src/sections/Listing/components/ListingCreateBookingModal/index.tsx index f30d399..865210c 100644 --- a/src/sections/Listing/components/ListingCreateBookingModal/index.tsx +++ b/src/sections/Listing/components/ListingCreateBookingModal/index.tsx @@ -1,27 +1,53 @@ import React from 'react'; -import { Button, Divider, Modal, Typography } from 'antd'; +import { Button, Divider, message, Modal, Typography } from 'antd'; import { KeyOutlined } from '@ant-design/icons'; import moment, { Moment } from 'moment'; import { CardElement, useStripe, useElements } from '@stripe/react-stripe-js'; -import { formatListingPrice } from '../../../../lib/utils'; +import { formatListingPrice, displayErrorMessage, displaySuccessNotification } from '../../../../lib/utils'; +import { useMutation } from '@apollo/client'; +import { CREATE_BOOKING } from '../../../../lib/graphql/mutations/CreateBooking'; +import { + CreateBooking as CreateBookingData, + CreateBookingVariables, +} from '../../../../lib/graphql/mutations/CreateBooking/__generated__/CreateBooking'; interface Props { + id: string; modalVisible: boolean; - setModalVisible: (modalVisible: boolean) => void; checkInDate: Moment; checkOutDate: Moment; price: number; + setModalVisible: (modalVisible: boolean) => void; + clearBookingData: () => void; + refetchListing: () => Promise; } const { Paragraph, Title, Text } = Typography; export const ListingCreateBookingModal = ({ + id, checkInDate, checkOutDate, price, modalVisible, setModalVisible, + clearBookingData, + refetchListing, }: Props) => { + const [createBooking, { loading }] = useMutation(CREATE_BOOKING, { + onCompleted: () => { + clearBookingData(); + displaySuccessNotification( + "You've successfully booked the listing!", + 'Booking history can always be found in your Profile.' + ); + refetchListing(); + }, + onError: () => { + displayErrorMessage("Sorry! We weren't able to book the listing. Please try again later!"); + }, + }); + const daysBooked = checkOutDate.diff(checkInDate, 'days') + 1; const listingPrice = price * daysBooked; @@ -35,8 +61,22 @@ export const ListingCreateBookingModal = ({ if (!cardElement) return; - const { token: stripeToken } = await stripe.createToken(cardElement); - console.log(stripeToken); + const { token: stripeToken, error } = await stripe.createToken(cardElement); + + if (stripeToken) { + createBooking({ + variables: { + input: { + id, + source: stripeToken.id, + checkIn: moment(checkInDate).format('YYYY-MM-DD'), + checkOut: moment(checkInDate).format('YYYY-MM-DD'), + }, + }, + }); + } else { + displayErrorMessage(error && error.message ? error.message : 'Unable to Book the Listing. Try again later.'); + } }; return ( @@ -81,7 +121,12 @@ export const ListingCreateBookingModal = ({ className="listing-booking-modal__stripe-card" /> - diff --git a/src/sections/Listing/index.tsx b/src/sections/Listing/index.tsx index 3955fbb..93a0b09 100644 --- a/src/sections/Listing/index.tsx +++ b/src/sections/Listing/index.tsx @@ -22,7 +22,7 @@ export const Listing = () => { const [checkOutDate, setCheckOutDate] = useState(null); const [modalVisible, setModalVisible] = useState(false); - const { data, loading, error } = useQuery(LISTING, { + const { data, loading, error, refetch } = useQuery(LISTING, { variables: { id, bookingsPage, @@ -30,6 +30,16 @@ export const Listing = () => { }, }); + const refetchListing = async () => { + await refetch(); + }; + + const clearBookingData = () => { + setCheckOutDate(null); + setCheckInDate(null); + setModalVisible(false); + }; + if (loading) { return ( @@ -58,7 +68,7 @@ export const Listing = () => { {/* Listing Details */} {listing ? : null} - {/* Bookings for above listing */} + {/* Bookings for above listing, only visible to the owner */} {listingBookings ? ( { {listing && checkInDate && checkOutDate && ( { ); } - - return ( - + { }>
{/* } /> */} - +
}> diff --git a/src/styles/index.less b/src/styles/index.less index 0177026..7555593 100644 --- a/src/styles/index.less +++ b/src/styles/index.less @@ -668,7 +668,7 @@ } .user { background-color: #fff; - padding-left: 50px; + padding: 0 50px; } // @media (max-width: 75em) { // .user {