From 8b2119a7661a7d9c59f0a02622b9542291f9379e Mon Sep 17 00:00:00 2001 From: Lina Ebeid Date: Sun, 23 Aug 2020 09:59:37 +0300 Subject: [PATCH 01/14] fetch booking types and render them at dashboard --- client/src/components/Menu/index.js | 6 +++ .../components/Table/BookingTypes/function.js | 19 ++++++++ .../components/Table/BookingTypes/index.js | 44 +++++++++++++++++++ client/src/pages/Dashboard/index.js | 7 +-- 4 files changed, 73 insertions(+), 3 deletions(-) create mode 100644 client/src/components/Table/BookingTypes/function.js create mode 100644 client/src/components/Table/BookingTypes/index.js diff --git a/client/src/components/Menu/index.js b/client/src/components/Menu/index.js index 0d41fc8..2f8057c 100644 --- a/client/src/components/Menu/index.js +++ b/client/src/components/Menu/index.js @@ -2,6 +2,7 @@ import { HomeOutlined, UserOutlined, ClockCircleOutlined, + SettingOutlined, } from '@ant-design/icons'; import { Menu } from 'antd'; import React from 'react'; @@ -38,6 +39,11 @@ function AdminMenu() { Business Hours + + + + Booking Types + ); } diff --git a/client/src/components/Table/BookingTypes/function.js b/client/src/components/Table/BookingTypes/function.js new file mode 100644 index 0000000..c460b2e --- /dev/null +++ b/client/src/components/Table/BookingTypes/function.js @@ -0,0 +1,19 @@ +/* eslint-disable import/prefer-default-export */ +export const fetchData = (component, message) => { + component.setState({ loading: true }); + fetch('/api/v1/bookingTypes') + .then((res) => { + if (!res.ok) { + message.error('could not fetch data'); + throw res.statusText; + } + return res.json(); + }) + .then((results) => { + const resultsWithKey = results.map((row) => ({ key: row.id, ...row })); + component.setState({ loading: false, data: resultsWithKey }); + }) + .catch(() => { + component.setState({ loading: false }); + }); +}; diff --git a/client/src/components/Table/BookingTypes/index.js b/client/src/components/Table/BookingTypes/index.js new file mode 100644 index 0000000..f91c3d1 --- /dev/null +++ b/client/src/components/Table/BookingTypes/index.js @@ -0,0 +1,44 @@ +import React from 'react'; +import { Table, message } from 'antd'; +import { fetchData } from './function'; + +class BookingTypes extends React.Component { + state = { + data: [], + loading: false, + }; + + columns = [ + { + title: 'Booking Type', + dataIndex: 'category', + }, + { + title: 'Color', + dataIndex: 'color', + }, + ]; + + componentDidMount() { + fetchData(this, message); + } + + render() { + // const { loading, data, visible, updateID, initialName } = this.state; + const { data, loading } = this.state; + + return ( +
+ + + ); + } +} + +export default BookingTypes; diff --git a/client/src/pages/Dashboard/index.js b/client/src/pages/Dashboard/index.js index 1986002..79c693f 100644 --- a/client/src/pages/Dashboard/index.js +++ b/client/src/pages/Dashboard/index.js @@ -1,4 +1,3 @@ -// import { LaptopOutlined, NotificationOutlined, UserOutlined } from '@ant-design/icons'; import { Layout } from 'antd'; import React from 'react'; import { Route, Switch } from 'react-router-dom'; @@ -6,6 +5,7 @@ import AdminMenu from '../../components/Menu'; import Rooms from '../../components/Table/Rooms'; import Users from '../../components/Table/Users'; import BusinessHours from '../../components/Form/BusinessHours'; +import BookingTypes from '../../components/Table/BookingTypes'; import './style.css'; const { Content, Sider } = Layout; @@ -28,9 +28,10 @@ function Dashboard() { - -

index

+ + + From db08560212d0986c7b41171f567e73fd74153a72 Mon Sep 17 00:00:00 2001 From: Lina Ebeid Date: Sun, 23 Aug 2020 13:18:21 +0300 Subject: [PATCH 02/14] delete type function created --- .../components/Table/BookingTypes/function.js | 19 +++++++++++- .../components/Table/BookingTypes/index.js | 29 +++++++++++++++++-- 2 files changed, 45 insertions(+), 3 deletions(-) diff --git a/client/src/components/Table/BookingTypes/function.js b/client/src/components/Table/BookingTypes/function.js index c460b2e..0916949 100644 --- a/client/src/components/Table/BookingTypes/function.js +++ b/client/src/components/Table/BookingTypes/function.js @@ -1,4 +1,3 @@ -/* eslint-disable import/prefer-default-export */ export const fetchData = (component, message) => { component.setState({ loading: true }); fetch('/api/v1/bookingTypes') @@ -17,3 +16,21 @@ export const fetchData = (component, message) => { component.setState({ loading: false }); }); }; + +export const deleteType = (id, component, message, notification) => { + fetch(`/api/v1/bookingType/${id}`, { + method: 'delete', + }).then((res) => { + if (!res.ok) { + message.error('could not delete this type'); + } else { + notification.success({ + message: 'type has been deleted', + }); + const { data } = component.state; + component.setState({ data: data.filter((row) => row.id !== id) }); + } + }); +}; + +// export const editType = (id, component, message) diff --git a/client/src/components/Table/BookingTypes/index.js b/client/src/components/Table/BookingTypes/index.js index f91c3d1..6311289 100644 --- a/client/src/components/Table/BookingTypes/index.js +++ b/client/src/components/Table/BookingTypes/index.js @@ -1,6 +1,7 @@ import React from 'react'; -import { Table, message } from 'antd'; -import { fetchData } from './function'; +import { Table, message, Button, Modal, notification } from 'antd'; +import { DeleteOutlined } from '@ant-design/icons'; +import { fetchData, deleteType } from './function'; class BookingTypes extends React.Component { state = { @@ -17,6 +18,30 @@ class BookingTypes extends React.Component { title: 'Color', dataIndex: 'color', }, + { + title: 'Delete', + dataIndex: 'id', + render: (id) => ( + + ), + }, ]; componentDidMount() { From 1a44af6f18847fdf482c46c975aab48cc6c7145a Mon Sep 17 00:00:00 2001 From: Lina Ebeid Date: Sun, 23 Aug 2020 14:02:46 +0300 Subject: [PATCH 03/14] edit type function created --- client/src/components/Form/Login/index.js | 0 client/src/components/Form/Signup/index.js | 0 .../components/Table/BookingTypes/function.js | 25 ++++++++++++++++++- 3 files changed, 24 insertions(+), 1 deletion(-) delete mode 100644 client/src/components/Form/Login/index.js delete mode 100644 client/src/components/Form/Signup/index.js diff --git a/client/src/components/Form/Login/index.js b/client/src/components/Form/Login/index.js deleted file mode 100644 index e69de29..0000000 diff --git a/client/src/components/Form/Signup/index.js b/client/src/components/Form/Signup/index.js deleted file mode 100644 index e69de29..0000000 diff --git a/client/src/components/Table/BookingTypes/function.js b/client/src/components/Table/BookingTypes/function.js index 0916949..e2f3a7f 100644 --- a/client/src/components/Table/BookingTypes/function.js +++ b/client/src/components/Table/BookingTypes/function.js @@ -33,4 +33,27 @@ export const deleteType = (id, component, message, notification) => { }); }; -// export const editType = (id, component, message) +export const editType = (values, component, message, notification) => { + const { id, category, color } = values; + fetch(`/api/v1/bookingType/${id}`, { + method: 'PATCH', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ category, color }), + }) + .then((res) => { + if (!res.ok) { + message.error('could not update the type'); + } else { + notification.success({ + message: 'type has been updated', + }); + fetchData(component, message); + component.setState({ + visible: false, + }); + } + }) + .catch(() => message.error('could not update the type')); +}; From 3ec1e2da1f6bd3b3a84743d41fde93ed74f9c1e9 Mon Sep 17 00:00:00 2001 From: Lina Ebeid Date: Sun, 23 Aug 2020 14:03:41 +0300 Subject: [PATCH 04/14] add and edit type form created --- client/src/components/Form/AddType/index.js | 80 +++++++++++++++++++ .../components/Table/BookingTypes/index.js | 55 +++++++++++-- 2 files changed, 130 insertions(+), 5 deletions(-) create mode 100644 client/src/components/Form/AddType/index.js diff --git a/client/src/components/Form/AddType/index.js b/client/src/components/Form/AddType/index.js new file mode 100644 index 0000000..82e6c99 --- /dev/null +++ b/client/src/components/Form/AddType/index.js @@ -0,0 +1,80 @@ +import React from 'react'; +import { message, Modal, Form, Input } from 'antd'; +import PropTypes from 'prop-types'; + +const onModalOk = (form, updateID, onUpdate, onCreate) => { + form + .validateFields() + .then((values) => { + form.resetFields(); + if (updateID > 0) onUpdate({ id: updateID, ...values }); + else onCreate(values); + }) + .catch((error) => message.error(error)); +}; + +function AddType({ + visible, + onCreate, + onCancel, + onUpdate, + updateID, + category, + color, +}) { + const [form] = Form.useForm(); + if (updateID > 0) { + form.setFieldsValue({ + category, + color, + }); + } + return ( + 0 ? 'Update' : 'Create'} + cancelText="Cancel" + onCancel={onCancel} + onOk={() => onModalOk(form, updateID, onUpdate, onCreate)} + > +
+ + + + + + + +
+ ); +} + +AddType.propTypes = { + visible: PropTypes.bool.isRequired, + updateID: PropTypes.number.isRequired, + category: PropTypes.string.isRequired, + color: PropTypes.string.isRequired, + onCreate: PropTypes.func.isRequired, + onCancel: PropTypes.func.isRequired, + onUpdate: PropTypes.func.isRequired, +}; +export default AddType; diff --git a/client/src/components/Table/BookingTypes/index.js b/client/src/components/Table/BookingTypes/index.js index 6311289..93b6786 100644 --- a/client/src/components/Table/BookingTypes/index.js +++ b/client/src/components/Table/BookingTypes/index.js @@ -1,12 +1,17 @@ import React from 'react'; import { Table, message, Button, Modal, notification } from 'antd'; -import { DeleteOutlined } from '@ant-design/icons'; -import { fetchData, deleteType } from './function'; +import { DeleteOutlined, EditOutlined } from '@ant-design/icons'; +import { fetchData, deleteType, editType } from './function'; +import AddType from '../../Form/AddType'; class BookingTypes extends React.Component { state = { data: [], loading: false, + visible: false, + updateID: 0, + category: '', + color: '', }; columns = [ @@ -19,7 +24,27 @@ class BookingTypes extends React.Component { dataIndex: 'color', }, { - title: 'Delete', + title: 'Action', + dataIndex: 'id', + // width: '64px', + colSpan: 2, + render: (id, row) => ( + + ), + }, + { dataIndex: 'id', render: (id) => (
+ console.log('hi create')} + onCancel={() => { + this.setState({ visible: false }); + }} + onClick={() => { + this.setState({ visible: true, updateID: 0 }); + }} + onUpdate={(values) => + editType(values, component, message, notification) + } + updateID={updateID} + category={category} + color={color} + /> + {/* {visible ? : null} */} ); } From 3de39a4a983a00a0d15d7810eb0ee9b843964ed5 Mon Sep 17 00:00:00 2001 From: Lina Ebeid Date: Sun, 23 Aug 2020 17:49:15 +0300 Subject: [PATCH 05/14] add type function created --- client/src/components/Form/AddType/index.js | 5 ++++ .../components/Table/BookingTypes/function.js | 24 +++++++++++++++++++ .../components/Table/BookingTypes/index.js | 18 ++++++++------ 3 files changed, 40 insertions(+), 7 deletions(-) diff --git a/client/src/components/Form/AddType/index.js b/client/src/components/Form/AddType/index.js index 82e6c99..4e28129 100644 --- a/client/src/components/Form/AddType/index.js +++ b/client/src/components/Form/AddType/index.js @@ -28,6 +28,11 @@ function AddType({ category, color, }); + } else if (updateID === 0) { + form.setFieldsValue({ + category: '', + color: '', + }); } return ( { } }); }; +export const addType = (values, component, message, notification) => { + const { category, color } = values; + fetch(`/api/v1/bookingTypes`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ category, color }), + }) + .then((res) => { + if (!res.ok) { + message.error('could not add this type'); + } else { + notification.success({ + message: 'type has been added successfully', + }); + fetchData(component, message); + component.setState({ + visible: false, + }); + } + }) + .catch(() => message.error('could not add the type')); +}; export const editType = (values, component, message, notification) => { const { id, category, color } = values; diff --git a/client/src/components/Table/BookingTypes/index.js b/client/src/components/Table/BookingTypes/index.js index 93b6786..6f94c52 100644 --- a/client/src/components/Table/BookingTypes/index.js +++ b/client/src/components/Table/BookingTypes/index.js @@ -1,7 +1,7 @@ import React from 'react'; import { Table, message, Button, Modal, notification } from 'antd'; import { DeleteOutlined, EditOutlined } from '@ant-design/icons'; -import { fetchData, deleteType, editType } from './function'; +import { fetchData, deleteType, editType, addType } from './function'; import AddType from '../../Form/AddType'; class BookingTypes extends React.Component { @@ -26,7 +26,7 @@ class BookingTypes extends React.Component { { title: 'Action', dataIndex: 'id', - // width: '64px', + width: '64px', colSpan: 2, render: (id, row) => (
console.log('hi create')} + onCreate={(values) => + addType(values, component, message, notification) + } onCancel={() => { - this.setState({ visible: false }); + this.setState({ visible: false, updateID: 0 }); }} onClick={() => { this.setState({ visible: true, updateID: 0 }); @@ -105,7 +110,6 @@ class BookingTypes extends React.Component { category={category} color={color} /> - {/* {visible ? : null} */} ); } From 4970c66d909c13f88c37db1ad9421ea3158f54b0 Mon Sep 17 00:00:00 2001 From: Lina Ebeid Date: Mon, 24 Aug 2020 14:13:08 +0300 Subject: [PATCH 06/14] add slideColor input --- client/package-lock.json | 31 +++++++++++++++++++++ client/package.json | 1 + client/src/components/Form/AddType/index.js | 22 ++++++++++----- 3 files changed, 47 insertions(+), 7 deletions(-) diff --git a/client/package-lock.json b/client/package-lock.json index 1e7beca..e94fb46 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -1207,6 +1207,11 @@ "resolved": "https://registry.npmjs.org/@hapi/bourne/-/bourne-1.3.2.tgz", "integrity": "sha512-1dVNHT76Uu5N3eJNTYcvxee+jzX4Z9lfciqRRHCU27ihbUcYi+iSc2iml5Ke1LXe1SyJCLA0+14Jh4tXJgOppA==" }, + "@icons/material": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@icons/material/-/material-0.2.4.tgz", + "integrity": "sha512-QPcGmICAPbGLGb6F/yNf/KzKqvFx8z5qx3D1yFqVAjoFmXK35EgyW+cJ57Te3CNsmzblwtzakLGFqHPqrfb4Tw==" + }, "@jest/console": { "version": "24.9.0", "resolved": "https://registry.npmjs.org/@jest/console/-/console-24.9.0.tgz", @@ -8075,6 +8080,11 @@ "object-visit": "^1.0.0" } }, + "material-colors": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/material-colors/-/material-colors-1.2.6.tgz", + "integrity": "sha512-6qE4B9deFBIa9YSpOc9O0Sgc43zTeVYbgDT5veRKSlB2+ZuHNoVVxA1L/ckMUayV9Ay9y7Z/SZCLcGteW9i7bg==" + }, "md5.js": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", @@ -10853,6 +10863,19 @@ "whatwg-fetch": "^3.0.0" } }, + "react-color": { + "version": "2.18.1", + "resolved": "https://registry.npmjs.org/react-color/-/react-color-2.18.1.tgz", + "integrity": "sha512-X5XpyJS6ncplZs74ak0JJoqPi+33Nzpv5RYWWxn17bslih+X7OlgmfpmGC1fNvdkK7/SGWYf1JJdn7D2n5gSuQ==", + "requires": { + "@icons/material": "^0.2.4", + "lodash": "^4.17.11", + "material-colors": "^1.2.1", + "prop-types": "^15.5.10", + "reactcss": "^1.2.0", + "tinycolor2": "^1.4.1" + } + }, "react-dev-utils": { "version": "10.2.1", "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-10.2.1.tgz", @@ -11218,6 +11241,14 @@ } } }, + "reactcss": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/reactcss/-/reactcss-1.2.3.tgz", + "integrity": "sha512-KiwVUcFu1RErkI97ywr8nvx8dNOpT03rbnma0SSalTYjkrPYaEajR4a/MRt6DZ46K6arDRbWMNHF+xH7G7n/8A==", + "requires": { + "lodash": "^4.0.1" + } + }, "read-pkg": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", diff --git a/client/package.json b/client/package.json index e13b71d..c9ff39e 100644 --- a/client/package.json +++ b/client/package.json @@ -18,6 +18,7 @@ "moment": "^2.24.0", "prop-types": "^15.7.2", "react": "^16.13.1", + "react-color": "^2.18.1", "react-dom": "^16.13.1", "react-google-login": "^5.1.20", "react-router-dom": "^5.1.2", diff --git a/client/src/components/Form/AddType/index.js b/client/src/components/Form/AddType/index.js index 4e28129..a27cdd2 100644 --- a/client/src/components/Form/AddType/index.js +++ b/client/src/components/Form/AddType/index.js @@ -1,15 +1,17 @@ -import React from 'react'; +import React, { useState } from 'react'; import { message, Modal, Form, Input } from 'antd'; import PropTypes from 'prop-types'; +import { SliderPicker } from 'react-color'; const onModalOk = (form, updateID, onUpdate, onCreate) => { form .validateFields() .then((values) => { - form.resetFields(); + // form.resetFields(); if (updateID > 0) onUpdate({ id: updateID, ...values }); else onCreate(values); }) + .then(() => form.resetFields()) .catch((error) => message.error(error)); }; @@ -22,18 +24,21 @@ function AddType({ category, color, }) { + const [colorSlide, setColorSlide] = useState(''); const [form] = Form.useForm(); if (updateID > 0) { form.setFieldsValue({ category, color, }); - } else if (updateID === 0) { + } + + const handleChangeComplete = (result) => { + setColorSlide(result.hex); form.setFieldsValue({ - category: '', - color: '', + color: color.hex, }); - } + }; return ( - + From a8279de64c0086ac70ddd507a44b931de4e90515 Mon Sep 17 00:00:00 2001 From: Lina Ebeid Date: Mon, 24 Aug 2020 20:44:37 +0300 Subject: [PATCH 07/14] fix sliderColor component to be required --- client/src/components/Form/AddType/index.js | 8 +++++--- client/src/components/Table/BookingTypes/index.js | 7 ++++++- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/client/src/components/Form/AddType/index.js b/client/src/components/Form/AddType/index.js index a27cdd2..b183768 100644 --- a/client/src/components/Form/AddType/index.js +++ b/client/src/components/Form/AddType/index.js @@ -7,7 +7,6 @@ const onModalOk = (form, updateID, onUpdate, onCreate) => { form .validateFields() .then((values) => { - // form.resetFields(); if (updateID > 0) onUpdate({ id: updateID, ...values }); else onCreate(values); }) @@ -36,7 +35,7 @@ function AddType({ const handleChangeComplete = (result) => { setColorSlide(result.hex); form.setFieldsValue({ - color: color.hex, + color: result.hex, }); }; return ( @@ -46,7 +45,10 @@ function AddType({ okText={updateID > 0 ? 'Update' : 'Create'} cancelText="Cancel" onCancel={onCancel} - onOk={() => onModalOk(form, updateID, onUpdate, onCreate)} + onOk={() => { + onModalOk(form, updateID, onUpdate, onCreate); + setColorSlide(''); + }} >
( + + {color} + + ), }, { title: 'Action', From 12e7ab9e42c8d971a75eafeafd62e68816488aed Mon Sep 17 00:00:00 2001 From: Lina Ebeid Date: Mon, 24 Aug 2020 21:10:58 +0300 Subject: [PATCH 08/14] get bookingTypes and render select types at bookingForm --- client/src/components/DayView/Calendar.js | 6 +++++- client/src/components/DayView/functions.js | 19 ++++++++++++++++++- .../src/components/Form/Reservation/index.js | 18 +++++++++++++++++- 3 files changed, 40 insertions(+), 3 deletions(-) diff --git a/client/src/components/DayView/Calendar.js b/client/src/components/DayView/Calendar.js index daf9b5c..e0d7967 100644 --- a/client/src/components/DayView/Calendar.js +++ b/client/src/components/DayView/Calendar.js @@ -6,7 +6,7 @@ import moment from 'moment'; import Tooltip from 'tooltip.js'; import React from 'react'; import BookingForm from '../Form/Reservation'; -import { getBusinessHours } from './functions'; +import { getBusinessHours, getBookingTypes } from './functions'; import './style.css'; class Calendar extends React.Component { @@ -28,12 +28,14 @@ class Calendar extends React.Component { minTime: '00:00', maxTime: '20:00', currentDate: moment(), + types: [], }; componentDidMount() { this.setState({ loading: true }); this.fetchRoomName().then(() => this.setState({ loading: false })); getBusinessHours(this, message); + getBookingTypes(this, message); } handleHide = () => { @@ -163,6 +165,7 @@ class Calendar extends React.Component { const { loading } = this.state; const { rooms, + types, events, visible, modalData, @@ -185,6 +188,7 @@ class Calendar extends React.Component { maxTime={maxTime} minTime={minTime} hiddenDays={hiddenDays} + types={types} /> )} diff --git a/client/src/components/DayView/functions.js b/client/src/components/DayView/functions.js index 9f12fde..d294a05 100644 --- a/client/src/components/DayView/functions.js +++ b/client/src/components/DayView/functions.js @@ -21,4 +21,21 @@ export const getBusinessHours = (component, message) => { }); }; -export const fetchRoomName = () => {}; +export const getBookingTypes = (component, message) => { + component.setState({ loading: true }); + fetch('/api/v1/bookingTypes') + .then((res) => { + if (!res.ok) { + message.error('could not fetch data'); + throw res.statusText; + } + return res.json(); + }) + .then((results) => { + const resultsWithKey = results.map((row) => ({ key: row.id, ...row })); + component.setState({ loading: false, types: resultsWithKey }); + }) + .catch(() => { + component.setState({ loading: false }); + }); +}; diff --git a/client/src/components/Form/Reservation/index.js b/client/src/components/Form/Reservation/index.js index 4d05785..b970cf4 100644 --- a/client/src/components/Form/Reservation/index.js +++ b/client/src/components/Form/Reservation/index.js @@ -8,6 +8,7 @@ import { Radio, Switch, TimePicker, + Select, } from 'antd'; import moment from 'moment'; import PropTypes from 'prop-types'; @@ -41,7 +42,7 @@ class BookingForm extends React.Component { } const timeArr = this.makeBookingArr(repeat, date, daterange, time); this.setState({ confirmLoading: true }); - const body = { roomId, time: timeArr, remindMe, ...rest }; + const body = { roomId, time: timeArr, noOfPeople: 15, remindMe, ...rest }; return fetch('/api/v1/booking', { method: 'POST', headers: { @@ -158,6 +159,7 @@ class BookingForm extends React.Component { render() { const { rooms, + types, visible, handleHide, hiddenDays, @@ -252,6 +254,19 @@ class BookingForm extends React.Component { + + + Date: Mon, 24 Aug 2020 21:23:46 +0300 Subject: [PATCH 09/14] add color property to calendar booking event --- client/src/components/DayView/Calendar.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client/src/components/DayView/Calendar.js b/client/src/components/DayView/Calendar.js index e0d7967..6490e5b 100644 --- a/client/src/components/DayView/Calendar.js +++ b/client/src/components/DayView/Calendar.js @@ -87,6 +87,7 @@ class Calendar extends React.Component { return res.json(); }) .then((results) => { + const { types } = this.state; this.setState({ events: results.map((event) => ({ id: event.id, @@ -97,6 +98,7 @@ class Calendar extends React.Component { resourceId: event.room_id, userid: event.user_id, userName: event.name, + color: types.find((e) => e.id === event.bookingtype_id).color, })), }); }) From 9fc0b69ab08b7e1cbb739d2b5bc6ddcc13fab979 Mon Sep 17 00:00:00 2001 From: Lina Ebeid Date: Tue, 25 Aug 2020 09:47:59 +0300 Subject: [PATCH 10/14] update getBooking query to return color --- server/src/database/queries/booking.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/database/queries/booking.js b/server/src/database/queries/booking.js index 0209b29..c1d214d 100644 --- a/server/src/database/queries/booking.js +++ b/server/src/database/queries/booking.js @@ -56,7 +56,7 @@ const getBookingbydate = (date) => { const day = new Date(date); day.setDate(day.getDate() + 1); // get the next day return connection.query( - 'SELECT booking.id, booking.room_id, booking.user_id,booking.bookingtype_id, booking.start_time, booking.end_time, booking.title, booking.description,booking.noOfPeople, bookinguser.name FROM booking INNER JOIN bookinguser ON bookinguser.id = booking.user_id WHERE booking.start_time >= $1 AND booking.end_time < $2', + 'SELECT B.id, B.room_id, B.user_id,B.bookingtype_id, T.color, B.start_time, B.end_time, B.title, B.description,B.noOfPeople, U.name FROM booking B INNER JOIN bookinguser U ON U.id = B.user_id INNER JOIN bookingtype T ON T.id = B.bookingtype_id WHERE B.start_time >= $1 AND B.end_time < $2', [date, day] ); }; From d569d4c038ba8b2b3f63ab9c9353262dacc57b62 Mon Sep 17 00:00:00 2001 From: Lina Ebeid Date: Tue, 25 Aug 2020 09:51:08 +0300 Subject: [PATCH 11/14] fix update type to reset color onchange --- client/src/components/DayView/Calendar.js | 3 +-- client/src/components/Form/AddType/index.js | 17 ++++++++--------- .../src/components/Table/BookingTypes/index.js | 7 ++++++- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/client/src/components/DayView/Calendar.js b/client/src/components/DayView/Calendar.js index 6490e5b..d20d246 100644 --- a/client/src/components/DayView/Calendar.js +++ b/client/src/components/DayView/Calendar.js @@ -87,7 +87,6 @@ class Calendar extends React.Component { return res.json(); }) .then((results) => { - const { types } = this.state; this.setState({ events: results.map((event) => ({ id: event.id, @@ -98,7 +97,7 @@ class Calendar extends React.Component { resourceId: event.room_id, userid: event.user_id, userName: event.name, - color: types.find((e) => e.id === event.bookingtype_id).color, + color: event.color, })), }); }) diff --git a/client/src/components/Form/AddType/index.js b/client/src/components/Form/AddType/index.js index b183768..a30e2da 100644 --- a/client/src/components/Form/AddType/index.js +++ b/client/src/components/Form/AddType/index.js @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import React, { useState, useEffect } from 'react'; import { message, Modal, Form, Input } from 'antd'; import PropTypes from 'prop-types'; import { SliderPicker } from 'react-color'; @@ -10,7 +10,10 @@ const onModalOk = (form, updateID, onUpdate, onCreate) => { if (updateID > 0) onUpdate({ id: updateID, ...values }); else onCreate(values); }) - .then(() => form.resetFields()) + .then(() => { + form.resetFields(); + form.setFieldsValue({ color: '', category: '' }); + }) .catch((error) => message.error(error)); }; @@ -25,12 +28,6 @@ function AddType({ }) { const [colorSlide, setColorSlide] = useState(''); const [form] = Form.useForm(); - if (updateID > 0) { - form.setFieldsValue({ - category, - color, - }); - } const handleChangeComplete = (result) => { setColorSlide(result.hex); @@ -38,6 +35,8 @@ function AddType({ color: result.hex, }); }; + useEffect(() => form.resetFields(), [form, color, category]); + return ( - + { - this.setState({ visible: false, updateID: 0 }); + this.setState({ + visible: false, + updateID: 0, + color: '', + category: '', + }); }} onClick={() => { this.setState({ visible: true, updateID: 0 }); From d220a311118a1641171fcc20d4ce83f189c504fc Mon Sep 17 00:00:00 2001 From: Lina Ebeid Date: Wed, 26 Aug 2020 09:00:17 +0300 Subject: [PATCH 12/14] move functions at bookingForm to seperate file --- .../components/Form/Reservation/functions.js | 126 ++++++++++++++++ .../src/components/Form/Reservation/index.js | 139 +----------------- 2 files changed, 131 insertions(+), 134 deletions(-) create mode 100644 client/src/components/Form/Reservation/functions.js diff --git a/client/src/components/Form/Reservation/functions.js b/client/src/components/Form/Reservation/functions.js new file mode 100644 index 0000000..d8af791 --- /dev/null +++ b/client/src/components/Form/Reservation/functions.js @@ -0,0 +1,126 @@ +import moment from 'moment'; + +export const findRoomIdByName = (name, component) => { + const { rooms } = component.props; + const roomObj = rooms.find((room) => room.name === name); + if (roomObj) { + return roomObj.id; + } + throw new Error(`no room by ${name} name`); +}; + +export const findRoomNameById = (id, component) => { + const { rooms } = component.props; + return rooms.find((room) => room.id === Number(id)).name; +}; + +export const cancelBooking = (id, date, message, component) => { + const { fetchEvents } = component.props; + component.setState({ confirmLoading: true }); + return fetch(`api/v1//booking/${id}`, { method: 'delete' }) + .then((res) => { + if (!res.ok) { + res.json().then(({ message: msg }) => message.error(msg)); + throw res.statusText; + } + return res.json(); + }) + .then(({ msg }) => { + message.success(msg); + fetchEvents(date.format('YYYY-MM-DD')); + component.setState({ confirmLoading: false }); + }) + .catch((err) => { + message.error(err); + }); +}; + +export const range = (min, max) => { + const start = Number(min.split(':')[0]); + const end = Number(max.split(':')[0]); + const result = []; + for (let i = 0; i < 24; i += 1) { + if (i < start || i > end - 1) { + result.push(i); + } + } + return result; +}; + +export const makeBookingArr = ( + repeat, + date, + [startDate, endDate] = [], + [startTime, endTime] +) => { + const handleTime = (day, time) => + moment( + `${day.format('YYYY-MM-DD')}T${time.format('HH:mm:ss.SSSZ')}` + ).toISOString(true); + + const arr = []; + if (repeat === 'weekly') { + for (let i = startDate; i <= endDate; i = i.add(1, 'week')) { + arr.push({ + startTime: handleTime(i, startTime), + endTime: handleTime(i, endTime), + }); + } + } else if (repeat === 'daily') { + for (let i = startDate; i <= endDate; i = i.add(1, 'day')) { + if (i.format('dddd') !== 'Friday' && i.format('dddd') !== 'Saturday') { + arr.push({ + startTime: handleTime(i, startTime), + endTime: handleTime(i, endTime), + }); + } + } + } else if (repeat === 'once') { + arr.push({ + startTime: handleTime(date, startTime), + endTime: handleTime(date, endTime), + }); + } + return arr; +}; + +export const bookRoom = ( + { repeat, date, daterange, time, remind: remindMe = true, room, ...rest }, + message, + component +) => { + const { handleHide, fetchEvents } = component.props; + let roomId; + try { + roomId = findRoomIdByName(room, component); + } catch (e) { + component.setState({ confirmLoading: false }); + return Promise.reject(e.message); + } + const timeArr = makeBookingArr(repeat, date, daterange, time); + component.setState({ confirmLoading: true }); + const body = { roomId, time: timeArr, remindMe, ...rest }; + return fetch('/api/v1/booking', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(body), + }) + .then((res) => { + if (!res.ok) { + res.json().then(({ message: msg }) => message.error(msg)); + throw res.statusText; + } + return res.json(); + }) + .then(() => fetchEvents(timeArr[0].startTime.split('T')[0])) + .then(() => { + component.setState({ confirmLoading: false }); + handleHide(); + }) + .then(() => message.success('Room booked successfully', 3)) + .catch(() => { + component.setState({ confirmLoading: false }); + }); +}; diff --git a/client/src/components/Form/Reservation/index.js b/client/src/components/Form/Reservation/index.js index 7cd2c51..d2ce8d7 100644 --- a/client/src/components/Form/Reservation/index.js +++ b/client/src/components/Form/Reservation/index.js @@ -14,6 +14,7 @@ import moment from 'moment'; import PropTypes from 'prop-types'; import React from 'react'; import { AuthContext } from '../../../context'; +import { findRoomNameById, cancelBooking, range, bookRoom } from './functions'; class BookingForm extends React.Component { state = { @@ -23,139 +24,10 @@ class BookingForm extends React.Component { formRef = React.createRef(); - bookRoom = ({ - repeat, - date, - daterange, - time, - remind: remindMe = true, - room, - ...rest - }) => { - const { handleHide, fetchEvents } = this.props; - let roomId; - try { - roomId = this.findRoomIdByName(room); - } catch (e) { - this.setState({ confirmLoading: false }); - return Promise.reject(e.message); - } - const timeArr = this.makeBookingArr(repeat, date, daterange, time); - this.setState({ confirmLoading: true }); - const body = { roomId, time: timeArr, remindMe, ...rest }; - return fetch('/api/v1/booking', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify(body), - }) - .then((res) => { - if (!res.ok) { - res.json().then(({ message: msg }) => message.error(msg)); - throw res.statusText; - } - return res.json(); - }) - .then(() => fetchEvents(timeArr[0].startTime.split('T')[0])) - .then(() => { - this.setState({ confirmLoading: false }); - handleHide(); - }) - .then(() => message.success('Room booked successfully', 3)) - .catch(() => { - this.setState({ confirmLoading: false }); - }); - }; - - findRoomIdByName = (name) => { - const { rooms } = this.props; - const roomObj = rooms.find((room) => room.name === name); - if (roomObj) { - return roomObj.id; - } - throw new Error(`no room by ${name} name`); - }; - - findRoomNameById = (id) => { - const { rooms } = this.props; - return rooms.find((room) => room.id === Number(id)).name; - }; - - cancelBooking = (id, date) => { - const { fetchEvents } = this.props; - this.setState({ confirmLoading: true }); - return fetch(`api/v1//booking/${id}`, { method: 'delete' }) - .then((res) => { - if (!res.ok) { - res.json().then(({ message: msg }) => message.error(msg)); - throw res.statusText; - } - return res.json(); - }) - .then(({ msg }) => { - message.success(msg); - fetchEvents(date.format('YYYY-MM-DD')); - this.setState({ confirmLoading: false }); - }) - .catch((err) => { - message.error(err); - }); - }; - repeatOnChange = (e) => { this.setState({ repeat: e.target.value }); }; - makeBookingArr = ( - repeat, - date, - [startDate, endDate] = [], - [startTime, endTime] - ) => { - const handleTime = (day, time) => - moment( - `${day.format('YYYY-MM-DD')}T${time.format('HH:mm:ss.SSSZ')}` - ).toISOString(true); - - const arr = []; - if (repeat === 'weekly') { - for (let i = startDate; i <= endDate; i = i.add(1, 'week')) { - arr.push({ - startTime: handleTime(i, startTime), - endTime: handleTime(i, endTime), - }); - } - } else if (repeat === 'daily') { - for (let i = startDate; i <= endDate; i = i.add(1, 'day')) { - if (i.format('dddd') !== 'Friday' && i.format('dddd') !== 'Saturday') { - arr.push({ - startTime: handleTime(i, startTime), - endTime: handleTime(i, endTime), - }); - } - } - } else if (repeat === 'once') { - arr.push({ - startTime: handleTime(date, startTime), - endTime: handleTime(date, endTime), - }); - } - return arr; - }; - - range = (min, max) => { - const start = Number(min.split(':')[0]); - const end = Number(max.split(':')[0]); - const result = []; - for (let i = 0; i < 24; i += 1) { - if (i < start || i > end - 1) { - result.push(i); - } - } - return result; - }; - render() { const { rooms, @@ -212,7 +84,7 @@ class BookingForm extends React.Component { initialValues={{ time: [moment(start), moment(end)], date: moment(start), - room: this.findRoomNameById(roomId), + room: findRoomNameById(roomId, this), title, description, repeat: 'once', @@ -222,13 +94,13 @@ class BookingForm extends React.Component { ref={this.formRef} onFinish={(values) => { if (couldCancel) { - this.cancelBooking(modalData.id, moment(start)) + cancelBooking(modalData.id, moment(start), message, this) .then(() => { handleHide(); }) .catch(message.error); } else { - this.bookRoom(values).catch(message.error); + bookRoom(values, message, this).catch(message.error); } }} > @@ -347,7 +219,7 @@ class BookingForm extends React.Component { minuteStep={10} disabled={disabled} format="HH:mm" - disabledHours={() => this.range(minTime, maxTime)} + disabledHours={() => range(minTime, maxTime)} hideDisabledOptions /> @@ -387,7 +259,6 @@ BookingForm.propTypes = { readOnly: PropTypes.bool, noOfPeople: PropTypes.number, }).isRequired, - fetchEvents: PropTypes.func.isRequired, minTime: PropTypes.string.isRequired, maxTime: PropTypes.string.isRequired, hiddenDays: PropTypes.arrayOf(PropTypes.number).isRequired, From f08b69069913de8bbed0846f110f414535fc4238 Mon Sep 17 00:00:00 2001 From: Lina Ebeid Date: Wed, 26 Aug 2020 09:09:18 +0300 Subject: [PATCH 13/14] move fetchRoomname from calendar to functions file --- client/src/components/DayView/Calendar.js | 72 +++++++++------------- client/src/components/DayView/functions.js | 16 +++++ 2 files changed, 44 insertions(+), 44 deletions(-) diff --git a/client/src/components/DayView/Calendar.js b/client/src/components/DayView/Calendar.js index 8526883..a75b2f8 100644 --- a/client/src/components/DayView/Calendar.js +++ b/client/src/components/DayView/Calendar.js @@ -6,7 +6,7 @@ import moment from 'moment'; import Tooltip from 'tooltip.js'; import React from 'react'; import BookingForm from '../Form/Reservation'; -import { getBusinessHours, getBookingTypes } from './functions'; +import { getBusinessHours, getBookingTypes, fetchRoomName } from './functions'; import './style.css'; class Calendar extends React.Component { @@ -34,7 +34,7 @@ class Calendar extends React.Component { componentDidMount() { this.setState({ loading: true }); - this.fetchRoomName().then(() => this.setState({ loading: false })); + fetchRoomName(this, message).then(() => this.setState({ loading: false })); getBusinessHours(this, message); getBookingTypes(this, message); } @@ -43,7 +43,7 @@ class Calendar extends React.Component { this.setState({ visible: false }); }; - bookRoom = () => { + clearData = () => { this.setState({ modalData: { roomId: '1', @@ -62,22 +62,6 @@ class Calendar extends React.Component { this.setState({ visible: true }); }; - fetchRoomName = () => - fetch(`/api/v1/rooms`) - .then((res) => { - if (!res.ok) { - res.json().then(({ message: msg }) => message.error(msg)); - throw res.statusText; - } - return res.json(); - }) - .then((results) => { - this.setState({ rooms: results }); - }) - .catch((err) => { - message.error(err); - }); - fetchRoomEvent = (date) => fetch(`/api/v1/booking/${date}`) .then((res) => { @@ -108,30 +92,6 @@ class Calendar extends React.Component { message.error(err); }); - resourcesFunc = ({ start }, successCallback, failureCallback) => { - const { rooms } = this.state; - successCallback(rooms.map((room) => ({ id: room.id, title: room.name }))); - - this.fetchRoomEvent(moment(start).format('YYYY-MM-DD')).catch( - failureCallback - ); - }; - - handleDateSelect = ({ resource: { id: roomId }, start, end }) => { - this.setState({ - modalData: { - roomId, - start, - end, - title: '', - description: '', - readOnly: false, - noOfPeople: null, - }, - }); - this.showModal(); - }; - showEventForm = ({ event }) => { const { id, @@ -157,6 +117,30 @@ class Calendar extends React.Component { this.showModal(); }; + resourcesFunc = ({ start }, successCallback, failureCallback) => { + const { rooms } = this.state; + successCallback(rooms.map((room) => ({ id: room.id, title: room.name }))); + + this.fetchRoomEvent(moment(start).format('YYYY-MM-DD')).catch( + failureCallback + ); + }; + + handleDateSelect = ({ resource: { id: roomId }, start, end }) => { + this.setState({ + modalData: { + roomId, + start, + end, + title: '', + description: '', + readOnly: false, + noOfPeople: null, + }, + }); + this.showModal(); + }; + eventRender = (info) => { // eslint-disable-next-line no-new new Tooltip(info.el, { @@ -232,7 +216,7 @@ class Calendar extends React.Component { customButtons={{ myCustomButton: { text: 'Book Your Room', - click: this.bookRoom, + click: this.clearData, }, }} header={{ diff --git a/client/src/components/DayView/functions.js b/client/src/components/DayView/functions.js index d294a05..f6356d2 100644 --- a/client/src/components/DayView/functions.js +++ b/client/src/components/DayView/functions.js @@ -39,3 +39,19 @@ export const getBookingTypes = (component, message) => { component.setState({ loading: false }); }); }; + +export const fetchRoomName = (component, message) => + fetch(`/api/v1/rooms`) + .then((res) => { + if (!res.ok) { + res.json().then(({ message: msg }) => message.error(msg)); + throw res.statusText; + } + return res.json(); + }) + .then((results) => { + component.setState({ rooms: results }); + }) + .catch((err) => { + message.error(err); + }); From 51ce591d4d7b14ecb847826f94c5562d66d8f29f Mon Sep 17 00:00:00 2001 From: Lina Ebeid Date: Wed, 26 Aug 2020 09:20:33 +0300 Subject: [PATCH 14/14] add bookingTYpeID when readOnly --- client/src/components/DayView/Calendar.js | 4 +++- client/src/components/Form/Reservation/index.js | 5 ++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/client/src/components/DayView/Calendar.js b/client/src/components/DayView/Calendar.js index a75b2f8..9b003fa 100644 --- a/client/src/components/DayView/Calendar.js +++ b/client/src/components/DayView/Calendar.js @@ -85,6 +85,7 @@ class Calendar extends React.Component { userName: event.name, color: event.color, noOfPeople: event.noofpeople, + typeID: event.bookingtype_id, })), }); }) @@ -98,7 +99,7 @@ class Calendar extends React.Component { start, end, title, - extendedProps: { description, userName, userid, noOfPeople }, + extendedProps: { description, userName, userid, noOfPeople, typeID }, } = event; this.setState({ modalData: { @@ -112,6 +113,7 @@ class Calendar extends React.Component { userid, readOnly: true, noOfPeople, + typeID, }, }); this.showModal(); diff --git a/client/src/components/Form/Reservation/index.js b/client/src/components/Form/Reservation/index.js index d2ce8d7..734872a 100644 --- a/client/src/components/Form/Reservation/index.js +++ b/client/src/components/Form/Reservation/index.js @@ -53,6 +53,7 @@ class BookingForm extends React.Component { description, readOnly, noOfPeople, + typeID, } = modalData; const disabled = confirmLoading || readOnly; const { admin, userID } = this.context; @@ -90,6 +91,7 @@ class BookingForm extends React.Component { repeat: 'once', remind: true, noOfPeople, + bookingTypeId: typeID, }} ref={this.formRef} onFinish={(values) => { @@ -147,7 +149,7 @@ class BookingForm extends React.Component { label="Booking Type" rules={[{ required: true, message: 'Choose booking type' }]} > - {types.map((type) => ( {type.category} @@ -255,6 +257,7 @@ BookingForm.propTypes = { title: PropTypes.string, description: PropTypes.string, userName: PropTypes.string, + typeID: PropTypes.string, userid: PropTypes.number, readOnly: PropTypes.bool, noOfPeople: PropTypes.number,