diff --git a/admin-frontend/src/App.js b/admin-frontend/src/App.js
index d5c6dd9..4718783 100644
--- a/admin-frontend/src/App.js
+++ b/admin-frontend/src/App.js
@@ -23,7 +23,7 @@ import AdminNotificationPage from "./components/notification/AdminNotificationPa
import VerifyEmailPage from "./components/VerifyEmailPage";
import ViewActiveBookingsPage from "./components/booking/ViewActiveBookingsPage";
import ViewPastBookingsPage from "./components/booking/ViewPastBookingsPage";
-import ActivityThemesPage from "./components/activitythemes/ActivityThemesPage";
+import ActivityThemesPage from "./components/activitytheme/ActivityThemesPage";
function App() {
return (
diff --git a/admin-frontend/src/components/activityThemes/ActivityThemesPage.jsx b/admin-frontend/src/components/activitytheme/ActivityThemesPage.jsx
similarity index 100%
rename from admin-frontend/src/components/activityThemes/ActivityThemesPage.jsx
rename to admin-frontend/src/components/activitytheme/ActivityThemesPage.jsx
diff --git a/admin-frontend/src/components/activityThemes/ActivityThemesTable.jsx b/admin-frontend/src/components/activitytheme/ActivityThemesTable.jsx
similarity index 100%
rename from admin-frontend/src/components/activityThemes/ActivityThemesTable.jsx
rename to admin-frontend/src/components/activitytheme/ActivityThemesTable.jsx
diff --git a/admin-frontend/src/components/activityThemes/AddThemeModal.jsx b/admin-frontend/src/components/activitytheme/AddThemeModal.jsx
similarity index 100%
rename from admin-frontend/src/components/activityThemes/AddThemeModal.jsx
rename to admin-frontend/src/components/activitytheme/AddThemeModal.jsx
diff --git a/admin-frontend/src/components/activityThemes/EditThemeModal.jsx b/admin-frontend/src/components/activitytheme/EditThemeModal.jsx
similarity index 100%
rename from admin-frontend/src/components/activityThemes/EditThemeModal.jsx
rename to admin-frontend/src/components/activitytheme/EditThemeModal.jsx
diff --git a/client-frontend/src/containers/ActivityDetailsPage/ActivityDetailsPage.jsx b/client-frontend/src/containers/ActivityDetailsPage/ActivityDetailsPage.jsx
index 2f04087..3993f4e 100644
--- a/client-frontend/src/containers/ActivityDetailsPage/ActivityDetailsPage.jsx
+++ b/client-frontend/src/containers/ActivityDetailsPage/ActivityDetailsPage.jsx
@@ -160,7 +160,7 @@ const ActivityDetailsPage = () => {
const calculateWeekendAddOn = (
selectedDate,
weekendPricing,
- totalBasePrice,
+ totalBasePrice
) => {
if (
weekendPricing.amount !== null &&
@@ -175,7 +175,7 @@ const ActivityDetailsPage = () => {
return 0;
};
- const calculateOnlineAddOn = (location, onlinePricing, totalBasePrice) => {
+ const calculateOfflineAddOn = (location, onlinePricing, totalBasePrice) => {
if (
onlinePricing.amount !== null &&
(location.toLowerCase().includes("off-site") ||
@@ -190,7 +190,7 @@ const ActivityDetailsPage = () => {
return 0;
};
- const calculateOfflineAddOn = (location, offlinePricing, totalBasePrice) => {
+ const calculateOnlineAddOn = (location, offlinePricing, totalBasePrice) => {
if (
offlinePricing.amount !== null &&
location.toLowerCase().includes("virtual")
@@ -211,16 +211,19 @@ const ActivityDetailsPage = () => {
const weekendAddOn = calculateWeekendAddOn(
selectedDate,
currentActivity.weekendPricing,
+ totalBasePrice
);
- const onlineAddOn = calculateOnlineAddOn(
+ const offlineAddOn = calculateOfflineAddOn(
location,
currentActivity.offlinePricing,
+ totalBasePrice
);
- const offlineAddOn = calculateOfflineAddOn(
+ const onlineAddOn = calculateOnlineAddOn(
location,
currentActivity.onlinePricing,
+ totalBasePrice
);
const totalPriceCalculated =
@@ -241,15 +244,24 @@ const ActivityDetailsPage = () => {
const weekendAddOn = calculateWeekendAddOn(
selectedDate,
currentActivity.weekendPricing,
+ totalBasePrice
);
- const onlineAddOn = calculateOnlineAddOn(
+ const offlineAddOn = calculateOfflineAddOn(
location,
currentActivity.offlinePricing,
+ totalBasePrice
);
- const offlineAddOn = calculateOfflineAddOn(
+ const onlineAddOn = calculateOnlineAddOn(
location,
currentActivity.onlinePricing,
+ totalBasePrice
);
+ let activityPricingRule;
+ for (const pricingRule of currentActivity?.activityPricingRules) {
+ if (pax >= pricingRule.start && pax <= pricingRule.end) {
+ activityPricingRule = pricingRule;
+ }
+ }
const timeParts = time.split(",");
const cartItem = {
activityId: currentActivity._id,
@@ -262,6 +274,7 @@ const ActivityDetailsPage = () => {
offlineAddOnCost: offlineAddOn,
startDateTime: timeParts[0],
endDateTime: timeParts[1],
+ activityPricingRule: activityPricingRule._id,
};
if (
cartItem.activityId !== null &&
@@ -435,7 +448,7 @@ const ActivityDetailsPage = () => {
format="DD/MM/YYYY"
minDate={dayjs().add(
currentActivity?.bookingNotice,
- "days",
+ "days"
)}
shouldDisableDate={shouldDisableDate}
sx={{ marginRight: "12px" }}
@@ -593,8 +606,9 @@ const ActivityDetailsPage = () => {
? "-"
: ""}
{currentActivity?.weekendPricing?.amount?.toFixed(
- 2,
+ 2
)}
+ %
@@ -615,8 +629,9 @@ const ActivityDetailsPage = () => {
: "+"}
{""}
{currentActivity?.offlinePricing?.amount?.toFixed(
- 2,
- )}
+ 2
+ )}{" "}
+ %
@@ -636,8 +651,9 @@ const ActivityDetailsPage = () => {
: "+"}
{""}
{currentActivity?.onlinePricing?.amount?.toFixed(
- 2,
+ 2
)}
+ %
diff --git a/client-frontend/src/containers/Vendor/Booking/BookingDetailsForm.jsx b/client-frontend/src/containers/Vendor/Booking/BookingDetailsForm.jsx
index 97242eb..e44b31f 100644
--- a/client-frontend/src/containers/Vendor/Booking/BookingDetailsForm.jsx
+++ b/client-frontend/src/containers/Vendor/Booking/BookingDetailsForm.jsx
@@ -5,10 +5,18 @@ import PaidIcon from "@mui/icons-material/Paid";
import ThumbUpAltIcon from "@mui/icons-material/ThumbUpAlt";
import {
Grid,
+ InputAdornment,
List,
ListItem,
ListItemIcon,
ListItemText,
+ Paper,
+ Table,
+ TableBody,
+ TableCell,
+ TableContainer,
+ TableHead,
+ TableRow,
TextField,
Typography,
useTheme,
@@ -39,6 +47,36 @@ const BookingDetailsForm = ({ appointmentData }) => {
PENDING_PAYMENT: "Updated to Pending Payment",
PAID: "Updated to Paid",
};
+ const totalPrice = () => {
+ return (
+ appointmentData?.totalPax *
+ appointmentData?.activityPricingRule?.pricePerPax
+ );
+ };
+
+ const addons = {
+ weekendPricing: {
+ name: "Weekend Pricing",
+ vendor: "vendorWeekendAddOnCost",
+ },
+ onlinePricing: { name: "Online Pricing", vendor: "vendorOnlineAddOnCost" },
+ offlinePricing: {
+ name: "Offline Pricing",
+ vendor: "vendorOfflineAddOnCost",
+ },
+ };
+
+ const addOnNaming = (addon) => {
+ return addons[addon]?.name;
+ };
+
+ const vendorNaming = (addon) => {
+ return addons[addon]?.vendor;
+ };
+
+ const calculateTotal = () => {
+ return;
+ };
return (
{
fullWidth
/>
+
+
+
+
+
+
+ Num. pax
+
+
+ Price per Pax
+
+
+ Total Price
+
+
+
+
+
+
+
+ {appointmentData?.totalPax} pax
+
+
+
+
+ ${appointmentData?.activityPricingRule?.pricePerPax}
+
+
+
+ ${totalPrice()}
+
+
+
+ {appointmentData?.activityId &&
+ Object.keys(appointmentData?.activityId)?.map(
+ (addon, index) => {
+ return (
+ appointmentData[vendorNaming(addon)] != 0 &&
+ !isNaN(appointmentData[vendorNaming(addon)]) &&
+ appointmentData?.activityId[addon].amount && (
+
+
+
+
+ {addOnNaming(addon)}
+ {appointmentData?.activityId[addon].isDiscount
+ ? " Discount"
+ : " Add-on"}
+
+
+
+
+ {appointmentData?.activityId[addon].isDiscount
+ ? "-"
+ : "+"}
+ {appointmentData?.activityId[addon].amount}%{" "}
+
+ (
+ {appointmentData?.activityId[addon].isDiscount
+ ? "-"
+ : "+"}{" "}
+ $
+ {Math.abs(appointmentData[vendorNaming(addon)])}
+ )
+
+
+
+
+ )
+ );
+ }
+ )}
+
+
+
+ Total Price
+
+
+
+ ${appointmentData?.totalVendorAmount}
+
+
+
+
+
+
+
Status Changelog
diff --git a/server/controller/cartItemController.js b/server/controller/cartItemController.js
index a1e88c9..5099ed6 100644
--- a/server/controller/cartItemController.js
+++ b/server/controller/cartItemController.js
@@ -8,6 +8,7 @@ import {
getTimeslotAvailability,
generateAllTimeslots,
} from "./bookingController.js";
+import ActivityPricingRulesModel from "../model/activityPricingRules.js";
export const addCartItem = async (req, res) => {
const errors = validationResult(req);
@@ -49,6 +50,10 @@ export const addCartItem = async (req, res) => {
});
}
+ const activityPricingRule = await ActivityPricingRulesModel.findById(
+ restBody.activityPricingRule
+ );
+
// Get activity title, vendor name and vendorId
const activityTitle = activity.title;
const vendorName = activity.linkedVendor.companyName;
@@ -61,6 +66,30 @@ export const addCartItem = async (req, res) => {
onlineAddOnCost +
offlineAddOnCost;
+ const vendorCost = activityPricingRule.pricePerPax * totalPax;
+
+ const vendorWeekendAddOnCost =
+ weekendAddOnCost != 0
+ ? ((vendorCost * activity.weekendPricing.amount) / 100) *
+ (activity.weekendPricing.isDiscount ? -1 : 1)
+ : 0;
+
+ const vendorOnlineAddOnCost =
+ onlineAddOnCost != 0
+ ? ((vendorCost * activity.onlinePricing.amount) / 100) *
+ (activity.onlinePricing.isDiscount ? -1 : 1)
+ : 0;
+ const vendorOfflineAddOnCost =
+ offlineAddOnCost != 0
+ ? ((vendorCost * activity.offlinePricing.amount) / 100) *
+ (activity.offlinePricing.isDiscount ? -1 : 1)
+ : 0;
+ const totalVendorAmount =
+ vendorCost +
+ vendorWeekendAddOnCost +
+ vendorOnlineAddOnCost +
+ vendorOfflineAddOnCost;
+
const newCartItem = new CartItemModel({
clientId: client.id,
activityId,
@@ -73,6 +102,10 @@ export const addCartItem = async (req, res) => {
activityTitle,
vendorName,
vendorId,
+ totalVendorAmount,
+ vendorOfflineAddOnCost,
+ vendorOnlineAddOnCost,
+ vendorWeekendAddOnCost,
...restBody,
});
@@ -104,7 +137,9 @@ export const getCartItemsByClientId = async (req, res) => {
try {
const cartItems = await CartItemModel.find({
clientId: client._id,
- });
+ }).select(
+ "-vendorWeekendAddOnCost -vendorOnlineAddOnCost -vendorOfflineAddOnCost -totalVendorAmount"
+ );
// for each cartItem, check if the activity is still available
for (const cartItem of cartItems) {
const activity = await ActivityModel.findById(cartItem.activityId);
@@ -113,10 +148,10 @@ export const getCartItemsByClientId = async (req, res) => {
const updatedCartItems = await Promise.all(
cartItems.map(async (cartItem) => {
const isTimeslotAvailable = await isCartItemStillAvailable(
- cartItem._id,
+ cartItem._id
);
return { cartItem, isItemStillAvailable: isTimeslotAvailable }; // Add the 'isAvailable' field to the updated cart items
- }),
+ })
);
res.status(200).json(updatedCartItems);
@@ -175,14 +210,14 @@ export async function isCartItemStillAvailable(cartItemId) {
activity.startTime.getHours(),
activity.startTime.getMinutes(),
0,
- 0,
+ 0
);
const latestStartTime = new Date(cartItem.startDateTime);
latestStartTime.setHours(
activity.endTime.getHours(),
activity.endTime.getMinutes(),
0,
- 0,
+ 0
);
const interval = 30; // 30 minutes
@@ -218,14 +253,14 @@ export async function isCartItemStillAvailable(cartItemId) {
activity.capacity,
bookings,
blockedTimeslots,
- activity.duration,
+ activity.duration
);
// use isTimeslotAvailable
const isTimeslotAvailable = await getTimeslotAvailability(
allTimeslots,
cartItem.startDateTime,
- cartItem.endDateTime,
+ cartItem.endDateTime
);
return isTimeslotAvailable;
}
diff --git a/server/model/bookingModel.js b/server/model/bookingModel.js
index 35b4180..fe29307 100644
--- a/server/model/bookingModel.js
+++ b/server/model/bookingModel.js
@@ -31,6 +31,10 @@ const bookingSchema = new mongoose.Schema({
type: Number,
required: true,
},
+ totalVendorAmount: {
+ type: Number,
+ required: true,
+ },
totalPax: {
type: Number,
required: true,
@@ -51,6 +55,15 @@ const bookingSchema = new mongoose.Schema({
type: Number,
required: true,
},
+ vendorWeekendAddOnCost: {
+ type: Number,
+ },
+ vendorOnlineAddOnCost: {
+ type: Number,
+ },
+ vendorOfflineAddOnCost: {
+ type: Number,
+ },
activityTitle: {
type: String,
required: true,
@@ -130,6 +143,11 @@ const bookingSchema = new mongoose.Schema({
},
},
],
+ activityPricingRule: {
+ type: mongoose.Schema.Types.ObjectId,
+ required: true,
+ ref: "ActivityPricingRules",
+ },
});
const BookingModel = mongoose.model("Booking", bookingSchema, "bookings");
diff --git a/server/model/cartItemModel.js b/server/model/cartItemModel.js
index 2656a61..3b022f0 100644
--- a/server/model/cartItemModel.js
+++ b/server/model/cartItemModel.js
@@ -29,6 +29,10 @@ const cartItemSchema = new mongoose.Schema({
type: Number,
required: true,
},
+ totalVendorAmount: {
+ type: Number,
+ required: true,
+ },
totalPax: {
type: Number,
required: true,
@@ -49,6 +53,15 @@ const cartItemSchema = new mongoose.Schema({
type: Number,
required: true,
},
+ vendorWeekendAddOnCost: {
+ type: Number,
+ },
+ vendorOnlineAddOnCost: {
+ type: Number,
+ },
+ vendorOfflineAddOnCost: {
+ type: Number,
+ },
activityTitle: {
type: String,
required: true,
@@ -68,6 +81,11 @@ const cartItemSchema = new mongoose.Schema({
preSignedImages: {
type: Array,
},
+ activityPricingRule: {
+ type: mongoose.Schema.Types.ObjectId,
+ required: true,
+ ref: "ActivityPricingRules",
+ },
});
const cartItemModel = mongoose.model("CartItem", cartItemSchema, "cartitems");
diff --git a/server/service/bookingService.js b/server/service/bookingService.js
index a245959..26274fa 100644
--- a/server/service/bookingService.js
+++ b/server/service/bookingService.js
@@ -3,10 +3,22 @@ import BookingModel from "../model/bookingModel.js";
export const getAllBookingsForVendor = async (vendorId) => {
return await BookingModel.find({
vendorId: vendorId,
- }).populate({
- path: "clientId",
- select: "-password",
- });
+ })
+ .select(
+ "-weekendAddOnCost -onlineAddOnCost -offlineAddOnCost -basePricePerPax -totalCost"
+ )
+ .populate({
+ path: "clientId",
+ select: "-password",
+ })
+ .populate({
+ path: "activityPricingRule",
+ select: "-clientPrice",
+ })
+ .populate({
+ path: "activityId",
+ select: "weekendPricing onlinePricing offlinePricing",
+ });
};
export const updateBookingStatusActionHistory = async (
bookingId,