From 7b9c595e2c0ce98246be4d9f068bbc0aca24caea Mon Sep 17 00:00:00 2001 From: haseebzaki-07 Date: Sun, 10 Nov 2024 18:28:31 +0530 Subject: [PATCH] featured products, seasonal pricing, promotions APIs --- .../rent/SeasonalPricingController.js | 35 +++ .../rent/featuredProductController.js | 42 ++++ .../controllers/rent/promotionController.js | 59 +++++ backend/index.js | 7 + backend/logs/error.log | 0 backend/model/rent/featuredProduct.js | 16 ++ backend/model/rent/promotion.js | 20 ++ backend/model/rent/seasonalPricing.js | 16 ++ backend/package-lock.json | 222 +++++++++++++++++- backend/package.json | 3 +- backend/routes/rent/featuredProductRoutes.js | 12 + backend/routes/rent/promotionRoutes.js | 11 + backend/routes/rent/seasonalPricingRoutes.js | 10 + 13 files changed, 451 insertions(+), 2 deletions(-) create mode 100644 backend/controllers/rent/SeasonalPricingController.js create mode 100644 backend/controllers/rent/featuredProductController.js create mode 100644 backend/controllers/rent/promotionController.js create mode 100644 backend/logs/error.log create mode 100644 backend/model/rent/featuredProduct.js create mode 100644 backend/model/rent/promotion.js create mode 100644 backend/model/rent/seasonalPricing.js create mode 100644 backend/routes/rent/featuredProductRoutes.js create mode 100644 backend/routes/rent/promotionRoutes.js create mode 100644 backend/routes/rent/seasonalPricingRoutes.js diff --git a/backend/controllers/rent/SeasonalPricingController.js b/backend/controllers/rent/SeasonalPricingController.js new file mode 100644 index 00000000..149a23d5 --- /dev/null +++ b/backend/controllers/rent/SeasonalPricingController.js @@ -0,0 +1,35 @@ + +const RentProduct = require('../../model/rent/rentProduct'); +const SeasonalPricing = require('../../model/rent/seasonalPricing'); + +// Apply seasonal pricing adjustment for a product +exports.applySeasonalPricing = async (req, res) => { + try { + const { productId, season, discountPercentage, startDate, endDate } = req.body; + + // Check if the product exists + const product = await RentProduct.findById(productId); + if (!product) { + return res.status(404).json({ error: 'Product not found' }); + } + + const newSeasonalPricing = new SeasonalPricing({ + product: productId, + season, + discountPercentage, + startDate, + endDate + }); + + await newSeasonalPricing.save(); + + // Apply the seasonal price change immediately + product.rentalPricePerDay = product.rentalPricePerDay * (1 - discountPercentage / 100); + await product.save(); + + res.status(201).json({ message: 'Seasonal pricing applied successfully', seasonalPricing: newSeasonalPricing }); + } catch (error) { + console.error('Error applying seasonal pricing:', error.message); + res.status(500).json({ error: 'Failed to apply seasonal pricing' }); + } +}; diff --git a/backend/controllers/rent/featuredProductController.js b/backend/controllers/rent/featuredProductController.js new file mode 100644 index 00000000..eac8beb6 --- /dev/null +++ b/backend/controllers/rent/featuredProductController.js @@ -0,0 +1,42 @@ + +const FeaturedProduct = require('../../model/rent/featuredProduct'); +const RentProduct = require('../../model/rent/rentProduct'); + +// Feature a product +exports.featureProduct = async (req, res) => { + try { + const { productId, description, startDate, endDate } = req.body; + + // Check if the product exists + const product = await RentProduct.findById(productId); + if (!product) { + return res.status(404).json({ error: 'Product not found' }); + } + + const newFeaturedProduct = new FeaturedProduct({ + product: productId, + description, + startDate, + endDate, + status: 'active' + }); + + await newFeaturedProduct.save(); + res.status(201).json({ message: 'Product featured successfully', featuredProduct: newFeaturedProduct }); + } catch (error) { + console.error('Error featuring product:', error.message); + res.status(500).json({ error: 'Failed to feature product' }); + } +}; + +// Get all featured products +exports.getFeaturedProducts = async (req, res) => { + try { + const featuredProducts = await FeaturedProduct.find({ status: 'active' }).populate('product'); + + res.status(200).json({ featuredProducts }); + } catch (error) { + console.error('Error fetching featured products:', error.message); + res.status(500).json({ error: 'Failed to fetch featured products' }); + } +}; diff --git a/backend/controllers/rent/promotionController.js b/backend/controllers/rent/promotionController.js new file mode 100644 index 00000000..28a0d83b --- /dev/null +++ b/backend/controllers/rent/promotionController.js @@ -0,0 +1,59 @@ +const Promotion = require("../../model/rent/promotion"); + + +// Create a new promotion +exports.createPromotion = async (req, res) => { + try { + const { code, description, discountPercentage, startDate, endDate, applicableProducts, status } = req.body; + + // Create the promotion object + const newPromotion = new Promotion({ + code, + description, + discountPercentage, + startDate, + endDate, + applicableProducts, + status + }); + + // Save the promotion to the database + await newPromotion.save(); + res.status(201).json({ message: 'Promotion created successfully', promotion: newPromotion }); + } catch (error) { + console.error('Error creating promotion:', error.message); + res.status(500).json({ error: 'Failed to create promotion' }); + } +}; + +// Get all active promotions +exports.getActivePromotions = async (req, res) => { + try { + const promotions = await Promotion.find({ status: 'active' }); + + res.status(200).json({ promotions }); + } catch (error) { + console.error('Error fetching promotions:', error.message); + res.status(500).json({ error: 'Failed to fetch active promotions' }); + } +}; + +// Deactivate a promotion +exports.deactivatePromotion = async (req, res) => { + try { + const { promotionId } = req.params; + const promotion = await Promotion.findById(promotionId); + + if (!promotion) { + return res.status(404).json({ error: 'Promotion not found' }); + } + + promotion.status = 'inactive'; + await promotion.save(); + + res.status(200).json({ message: 'Promotion deactivated successfully' }); + } catch (error) { + console.error('Error deactivating promotion:', error.message); + res.status(500).json({ error: 'Failed to deactivate promotion' }); + } +}; diff --git a/backend/index.js b/backend/index.js index 951a5d9a..eeea1fc3 100644 --- a/backend/index.js +++ b/backend/index.js @@ -11,6 +11,7 @@ const shopRoutes = require('./routes/shop') const googleauth = require('./routes/googleauth') const agriProductRoutes = require('./routes/agriProductRoutes'); + const discussionRoutes = require('./routes/discussionRoutes'); const rentProductRoutes = require('./routes/rent/rentProductRoutes'); @@ -21,6 +22,9 @@ const adminOrderRoutes = require('./routes/rent/adminOrderRoutes'); const adminUserControlRoutes = require('./routes/rent/adminUserControlRoutes'); const analyticsRoutes = require('./routes/rent/analyticsRoutes'); const reviewRoutes = require('./routes/rent/reviewRoutes'); +const promotionRoutes = require('./routes/rent/promotionRoutes'); +const seasonalPricingRoutes = require('./routes/rent/seasonalPricingRoutes'); +const featuredProductRoutes = require('./routes/rent/featuredProductRoutes'); const ratingRoutes = require('./routes/rent/ratingRoutes'); @@ -61,6 +65,9 @@ app.use('/api', adminOrderRoutes); app.use('/api/admin', adminUserControlRoutes); app.use('/api', analyticsRoutes); app.use('/api', reviewRoutes); +app.use('/api', promotionRoutes); +app.use('/api', seasonalPricingRoutes); +app.use('/api', featuredProductRoutes); app.use('/api', ratingRoutes); diff --git a/backend/logs/error.log b/backend/logs/error.log new file mode 100644 index 00000000..e69de29b diff --git a/backend/model/rent/featuredProduct.js b/backend/model/rent/featuredProduct.js new file mode 100644 index 00000000..f1f99664 --- /dev/null +++ b/backend/model/rent/featuredProduct.js @@ -0,0 +1,16 @@ +const mongoose = require('mongoose'); + +const featuredProductSchema = new mongoose.Schema( + { + product: { type: mongoose.Schema.Types.ObjectId, ref: 'RentProduct', required: true }, + description: { type: String }, + startDate: { type: Date, required: true }, + endDate: { type: Date, required: true }, + status: { type: String, enum: ['active', 'inactive'], default: 'active' } + }, + { timestamps: true } +); + +const FeaturedProduct = mongoose.model('FeaturedProduct', featuredProductSchema); + +module.exports = FeaturedProduct; diff --git a/backend/model/rent/promotion.js b/backend/model/rent/promotion.js new file mode 100644 index 00000000..fb231b47 --- /dev/null +++ b/backend/model/rent/promotion.js @@ -0,0 +1,20 @@ +const mongoose = require("mongoose"); + +const promotionSchema = new mongoose.Schema( + { + code: { type: String, required: true, unique: true }, + description: { type: String, required: true }, + discountPercentage: { type: Number, required: true, min: 0, max: 100 }, + startDate: { type: Date, required: true }, + endDate: { type: Date, required: true }, + applicableProducts: [ + { type: mongoose.Schema.Types.ObjectId, ref: "RentProduct" }, + ], // Specific products or categories for the promo + status: { type: String, enum: ["active", "inactive"], default: "active" }, + }, + { timestamps: true } +); + +const Promotion = mongoose.model("Promotion", promotionSchema); + +module.exports = Promotion; diff --git a/backend/model/rent/seasonalPricing.js b/backend/model/rent/seasonalPricing.js new file mode 100644 index 00000000..0b231989 --- /dev/null +++ b/backend/model/rent/seasonalPricing.js @@ -0,0 +1,16 @@ +const mongoose = require('mongoose'); + +const seasonalPricingSchema = new mongoose.Schema( + { + product: { type: mongoose.Schema.Types.ObjectId, ref: 'RentProduct', required: true }, + season: { type: String, required: true, enum: ['summer', 'winter', 'fall', 'spring'] }, + discountPercentage: { type: Number, required: true, min: 0, max: 100 }, + startDate: { type: Date, required: true }, + endDate: { type: Date, required: true }, + }, + { timestamps: true } +); + +const SeasonalPricing = mongoose.model('SeasonalPricing', seasonalPricingSchema); + +module.exports = SeasonalPricing; diff --git a/backend/package-lock.json b/backend/package-lock.json index efc621fb..a6317fae 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -26,7 +26,8 @@ "passport": "^0.7.0", "passport-google-oauth20": "^2.0.0", "stripe": "^12.2.0", - "supertest": "^7.0.0" + "supertest": "^7.0.0", + "winston": "^3.17.0" } }, "node_modules/@ampproject/remapping": { @@ -1334,6 +1335,24 @@ "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==" }, + "node_modules/@colors/colors": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", + "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@dabh/diagnostics": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz", + "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==", + "dependencies": { + "colorspace": "1.1.x", + "enabled": "2.0.x", + "kuler": "^2.0.0" + } + }, "node_modules/@google/generative-ai": { "version": "0.21.0", "resolved": "https://registry.npmjs.org/@google/generative-ai/-/generative-ai-0.21.0.tgz", @@ -2375,6 +2394,11 @@ "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==" }, + "node_modules/@types/triple-beam": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz", + "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==" + }, "node_modules/@types/webidl-conversions": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz", @@ -2490,6 +2514,11 @@ "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==" }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==" + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -2973,6 +3002,15 @@ "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==" }, + "node_modules/color": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", + "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", + "dependencies": { + "color-convert": "^1.9.3", + "color-string": "^1.6.0" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -2989,6 +3027,37 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/color/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/colorspace": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz", + "integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==", + "dependencies": { + "color": "^3.1.3", + "text-hex": "1.0.x" + } + }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -3253,6 +3322,11 @@ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, + "node_modules/enabled": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", + "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==" + }, "node_modules/encodeurl": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", @@ -3505,6 +3579,11 @@ "bser": "2.1.1" } }, + "node_modules/fecha": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", + "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==" + }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -3547,6 +3626,11 @@ "node": ">=8" } }, + "node_modules/fn.name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", + "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" + }, "node_modules/form-data": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", @@ -4805,6 +4889,11 @@ "node": ">=6" } }, + "node_modules/kuler": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", + "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" + }, "node_modules/leven": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", @@ -4876,6 +4965,27 @@ "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", "license": "MIT" }, + "node_modules/logform": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.7.0.tgz", + "integrity": "sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==", + "dependencies": { + "@colors/colors": "1.6.0", + "@types/triple-beam": "^1.3.2", + "fecha": "^4.2.0", + "ms": "^2.1.1", + "safe-stable-stringify": "^2.3.1", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/logform/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -5312,6 +5422,14 @@ "wrappy": "1" } }, + "node_modules/one-time": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", + "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", + "dependencies": { + "fn.name": "1.x.x" + } + }, "node_modules/onetime": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", @@ -5661,6 +5779,19 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==" }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -5744,6 +5875,14 @@ ], "license": "MIT" }, + "node_modules/safe-stable-stringify": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", + "engines": { + "node": ">=10" + } + }, "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", @@ -5884,6 +6023,19 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/simple-swizzle/node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" + }, "node_modules/simple-update-notifier": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-1.1.0.tgz", @@ -5975,6 +6127,14 @@ "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", "license": "BSD-3-Clause" }, + "node_modules/stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", + "engines": { + "node": "*" + } + }, "node_modules/stack-utils": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", @@ -5995,6 +6155,14 @@ "node": ">= 0.8" } }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, "node_modules/string-length": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", @@ -6177,6 +6345,11 @@ "node": ">=8" } }, + "node_modules/text-hex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", + "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==" + }, "node_modules/tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", @@ -6232,6 +6405,14 @@ "node": ">=12" } }, + "node_modules/triple-beam": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", + "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==", + "engines": { + "node": ">= 14.0.0" + } + }, "node_modules/tslib": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", @@ -6337,6 +6518,11 @@ "browserslist": ">= 4.21.0" } }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, "node_modules/utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", @@ -6434,6 +6620,40 @@ "node": ">= 8" } }, + "node_modules/winston": { + "version": "3.17.0", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.17.0.tgz", + "integrity": "sha512-DLiFIXYC5fMPxaRg832S6F5mJYvePtmO5G9v9IgUFPhXm9/GkXarH/TUrBAVzhTCzAj9anE/+GjrgXp/54nOgw==", + "dependencies": { + "@colors/colors": "^1.6.0", + "@dabh/diagnostics": "^2.0.2", + "async": "^3.2.3", + "is-stream": "^2.0.0", + "logform": "^2.7.0", + "one-time": "^1.0.0", + "readable-stream": "^3.4.0", + "safe-stable-stringify": "^2.3.1", + "stack-trace": "0.0.x", + "triple-beam": "^1.3.0", + "winston-transport": "^4.9.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/winston-transport": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.9.0.tgz", + "integrity": "sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A==", + "dependencies": { + "logform": "^2.7.0", + "readable-stream": "^3.6.2", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", diff --git a/backend/package.json b/backend/package.json index 7ba775e3..3012e965 100644 --- a/backend/package.json +++ b/backend/package.json @@ -28,6 +28,7 @@ "passport": "^0.7.0", "passport-google-oauth20": "^2.0.0", "stripe": "^12.2.0", - "supertest": "^7.0.0" + "supertest": "^7.0.0", + "winston": "^3.17.0" } } diff --git a/backend/routes/rent/featuredProductRoutes.js b/backend/routes/rent/featuredProductRoutes.js new file mode 100644 index 00000000..a314eae7 --- /dev/null +++ b/backend/routes/rent/featuredProductRoutes.js @@ -0,0 +1,12 @@ +const express = require('express'); +const { featureProduct, getFeaturedProducts } = require('../../controllers/rent/featuredProductController'); +const router = express.Router(); + + +// Admin route to feature a product +router.post('rent/feature',featureProduct); + +// Get all featured products +router.get('rent/featured',getFeaturedProducts); + +module.exports = router; diff --git a/backend/routes/rent/promotionRoutes.js b/backend/routes/rent/promotionRoutes.js new file mode 100644 index 00000000..752b88c5 --- /dev/null +++ b/backend/routes/rent/promotionRoutes.js @@ -0,0 +1,11 @@ +const express = require('express'); +const { createPromotion, getActivePromotions, deactivatePromotion } = require('../../controllers/rent/promotionController'); +const router = express.Router(); + + +// Admin routes to manage promotions +router.post('/rent/create', createPromotion); // Create new promotion +router.get('/rent/active', getActivePromotions); // Get active promotions +router.put('/rent/deactivate/:promotionId', deactivatePromotion); // Deactivate a promotion + +module.exports = router; diff --git a/backend/routes/rent/seasonalPricingRoutes.js b/backend/routes/rent/seasonalPricingRoutes.js new file mode 100644 index 00000000..7a3e37e1 --- /dev/null +++ b/backend/routes/rent/seasonalPricingRoutes.js @@ -0,0 +1,10 @@ +const express = require('express'); +const { applySeasonalPricing } = require('../../controllers/rent/SeasonalPricingController'); +const router = express.Router(); + + + +// Admin route to apply seasonal pricing +router.post('/apply', applySeasonalPricing); + +module.exports = router;