Skip to content

Commit c526ec6

Browse files
committed
Add Property Scheduler
1 parent 04f3d00 commit c526ec6

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

64 files changed

+5784
-3
lines changed

api/src/controllers/bookingController.ts

+21-2
Original file line numberDiff line numberDiff line change
@@ -653,6 +653,7 @@ export const getBookings = async (req: Request, res: Response) => {
653653
} = body
654654
const location = (body.filter && body.filter.location) || null
655655
const from = (body.filter && body.filter.from && new Date(body.filter.from)) || null
656+
const dateBetween = (body.filter && body.filter.dateBetween && new Date(body.filter.dateBetween)) || null
656657
const to = (body.filter && body.filter.to && new Date(body.filter.to)) || null
657658
let keyword = (body.filter && body.filter.keyword) || ''
658659
const options = 'i'
@@ -676,10 +677,28 @@ export const getBookings = async (req: Request, res: Response) => {
676677
}
677678
if (from) {
678679
$match.$and!.push({ from: { $gte: from } })
679-
} // $from > from
680+
} // $from >= from
681+
682+
if (dateBetween) {
683+
const dateBetweenStart = new Date(dateBetween)
684+
dateBetweenStart.setHours(0, 0, 0, 0)
685+
const dateBetweenEnd = new Date(dateBetween)
686+
dateBetweenEnd.setHours(23, 59, 59, 999)
687+
688+
$match.$and!.push({
689+
$and: [
690+
{ from: { $lte: dateBetweenEnd } },
691+
{ to: { $gte: dateBetweenStart } },
692+
],
693+
})
694+
} else if (from) {
695+
$match.$and!.push({ from: { $gte: from } }) // $from >= from
696+
}
697+
680698
if (to) {
681699
$match.$and!.push({ to: { $lte: to } })
682-
} // $to < to
700+
} // $to <= to
701+
683702
if (keyword) {
684703
const isObjectId = helper.isValidObjectId(keyword)
685704
if (isObjectId) {

backend/package-lock.json

+10-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

backend/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
"react-draft-wysiwyg": "^1.15.0",
5050
"react-router-dom": "^7.1.3",
5151
"react-toastify": "^11.0.3",
52+
"rrule": "^2.8.1",
5253
"typescript": "^5.7.3",
5354
"validator": "^13.12.0",
5455
"vite": "^6.0.11"

backend/src/App.tsx

+2
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ const NoMatch = lazy(() => import('@/pages/NoMatch'))
3737
const Countries = lazy(() => import('@/pages/Countries'))
3838
const CreateCountry = lazy(() => import('@/pages/CreateCountry'))
3939
const UpdateCountry = lazy(() => import('@/pages/UpdateCountry'))
40+
const Scheduler = lazy(() => import('@/pages/Scheduler'))
4041

4142
const App = () => (
4243
<BrowserRouter>
@@ -79,6 +80,7 @@ const App = () => (
7980
<Route path="/countries" element={<Countries />} />
8081
{/* <Route path="/create-country" element={<CreateCountry />} /> */}
8182
{/* <Route path="/update-country" element={<UpdateCountry />} /> */}
83+
<Route path="/scheduler" element={<Scheduler />} />
8284

8385
<Route path="*" element={<NoMatch />} />
8486
</Routes>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
div.property-scheduler-filter {
2+
background: #fafafa;
3+
margin: 10px 10px 0 0;
4+
border: 1px solid #dadada;
5+
font-size: 13px;
6+
}
7+
8+
div.property-scheduler-filter .bf-search {
9+
margin-top: 7px;
10+
}
11+
12+
div.property-scheduler-filter .btn-search {
13+
margin: 20px 0;
14+
}

backend/src/assets/css/scheduler.css

+104
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
div.scheduler {
2+
position: absolute;
3+
bottom: 0;
4+
right: 0;
5+
left: 0;
6+
}
7+
8+
@media only screen and (width <=960px) {
9+
div.scheduler {
10+
top: 56px;
11+
overflow-y: auto;
12+
}
13+
14+
div.scheduler div.col-1 {
15+
display: flex;
16+
flex-direction: column;
17+
align-items: center;
18+
}
19+
20+
div.scheduler div.col-2 {
21+
display: flex;
22+
}
23+
24+
div.scheduler div.col-1 .cl-supplier-filter label.accordion,
25+
div.scheduler div.col-1 .cl-status-filter label.accordion,
26+
div.scheduler div.col-1 .cl-scheduler-filter label.accordion {
27+
background: #fff;
28+
}
29+
30+
div.scheduler div.col-1 .cl-supplier-filter,
31+
div.scheduler div.col-1 .cl-status-filter,
32+
div.scheduler div.col-1 .cl-scheduler-filter {
33+
margin: 5px 10px;
34+
background-color: #fff;
35+
max-width: 480px;
36+
width: calc(100% - 20px);
37+
}
38+
39+
div.scheduler div.col-1 .cl-scheduler-filter div.panel,
40+
div.scheduler div.col-1 .cl-scheduler-filter div.panel-collapse {
41+
padding-right: 15px;
42+
padding-left: 15px;
43+
}
44+
45+
div.scheduler div.col-1 .cl-new-booking {
46+
width: calc(100% - 20px);
47+
max-width: 480px;
48+
margin: 15px 10px 5px;
49+
}
50+
}
51+
52+
@media only screen and (width >=960px) {
53+
div.scheduler {
54+
top: 64px;
55+
}
56+
57+
div.scheduler div.col-1 {
58+
position: absolute;
59+
top: 0;
60+
bottom: 0;
61+
left: 0;
62+
width: 300px;
63+
padding: 12px 0 0 12px;
64+
background: #fefefe;
65+
overflow: auto;
66+
}
67+
68+
div.scheduler div.col-2 {
69+
position: absolute;
70+
top: 0;
71+
right: 0;
72+
bottom: 0;
73+
left: 300px;
74+
}
75+
76+
div.scheduler div.col-1 .cl-supplier-filter label.accordion,
77+
div.scheduler div.col-1 .cl-status-filter label.accordion,
78+
div.scheduler div.col-1 .cl-scheduler-filter label.accordion {
79+
background: #fafafa;
80+
}
81+
82+
div.scheduler div.col-1 .cl-supplier-filter,
83+
div.scheduler div.col-1 .cl-status-filter,
84+
div.scheduler div.col-1 .cl-scheduler-filter {
85+
margin: 10px 10px 10px 0;
86+
background-color: #fafafa;
87+
}
88+
89+
div.scheduler div.col-1 .cl-scheduler-filter div.panel,
90+
div.scheduler div.col-1 .cl-scheduler-filter div.panel-collapse {
91+
padding-right: 15px;
92+
padding-left: 15px;
93+
}
94+
95+
div.scheduler div.col-1 .cl-status-filter,
96+
div.scheduler div.col-1 .cl-scheduler-filter {
97+
margin-bottom: 10px;
98+
}
99+
100+
div.scheduler div.col-1 .cl-new-booking {
101+
width: 265px;
102+
margin-left: 5px;
103+
}
104+
}

backend/src/common/helper.ts

+62
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,68 @@ export const getPropertyType = (type: string) => {
8484
export const admin = (user?: movininTypes.User): boolean =>
8585
(user && user.type === movininTypes.RecordType.Admin) ?? false
8686

87+
/**
88+
* Get booking status background color.
89+
*
90+
* @param {string} status
91+
* @returns {string}
92+
*/
93+
export const getBookingStatusBackgroundColor = (status?: movininTypes.BookingStatus) => {
94+
switch (status) {
95+
case movininTypes.BookingStatus.Void:
96+
return '#D9D9D9'
97+
98+
case movininTypes.BookingStatus.Pending:
99+
return '#FBDCC2'
100+
101+
case movininTypes.BookingStatus.Deposit:
102+
return '#CDECDA'
103+
104+
case movininTypes.BookingStatus.Paid:
105+
return '#D1F9D1'
106+
107+
case movininTypes.BookingStatus.Reserved:
108+
return '#D9E7F4'
109+
110+
case movininTypes.BookingStatus.Cancelled:
111+
return '#FBDFDE'
112+
113+
default:
114+
return ''
115+
}
116+
}
117+
118+
/**
119+
* Get booking status text color.
120+
*
121+
* @param {string} status
122+
* @returns {string}
123+
*/
124+
export const getBookingStatusTextColor = (status?: movininTypes.BookingStatus) => {
125+
switch (status) {
126+
case movininTypes.BookingStatus.Void:
127+
return '#6E7C86'
128+
129+
case movininTypes.BookingStatus.Pending:
130+
return '#EF6C00'
131+
132+
case movininTypes.BookingStatus.Deposit:
133+
return '#3CB371'
134+
135+
case movininTypes.BookingStatus.Paid:
136+
return '#77BC23'
137+
138+
case movininTypes.BookingStatus.Reserved:
139+
return '#1E88E5'
140+
141+
case movininTypes.BookingStatus.Cancelled:
142+
return '#E53935'
143+
144+
default:
145+
return ''
146+
}
147+
}
148+
87149
/**
88150
* Get booking status label.
89151
*

backend/src/components/Header.tsx

+5
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import {
3131
DescriptionTwoTone as TosIcon,
3232
ExitToApp as SignoutIcon,
3333
Flag as CountriesIcon,
34+
CalendarMonth as SchedulerIcon,
3435
} from '@mui/icons-material'
3536
import { useNavigate } from 'react-router-dom'
3637
import * as movininTypes from ':movinin-types'
@@ -292,6 +293,10 @@ const Header = ({
292293
<ListItemIcon><DashboardIcon /></ListItemIcon>
293294
<ListItemText primary={strings.DASHBOARD} />
294295
</ListItemLink>
296+
<ListItemLink href="/scheduler">
297+
<ListItemIcon><SchedulerIcon /></ListItemIcon>
298+
<ListItemText primary={strings.SCHEDULER} />
299+
</ListItemLink>
295300
<ListItemLink href="/agencies">
296301
<ListItemIcon><AgenciesIcon /></ListItemIcon>
297302
<ListItemText primary={strings.AGENCIES} />

0 commit comments

Comments
 (0)