Compare commits
218 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
b1207b1d3a
|
|||
|
0722bf5086
|
|||
|
b818f7842b
|
|||
|
d7f26c5e1d
|
|||
|
e3bec4d1d0
|
|||
|
0ee14ddf99
|
|||
|
d84c2879cd
|
|||
|
94659c9afa
|
|||
|
e7d83032e2
|
|||
|
535ef056fd
|
|||
|
2957930be4
|
|||
|
e7ae4c93b1
|
|||
|
f32f7ddc6e
|
|||
|
220f822f3d
|
|||
|
fb8ac9eb82
|
|||
|
3a7ee73233
|
|||
|
d4748fd99f
|
|||
|
0003858e3a
|
|||
|
30464136a3
|
|||
|
1b10587747
|
|||
|
7ec4f386b1
|
|||
|
0c205270ee
|
|||
|
8ee1f1183c
|
|||
|
ae7b868065
|
|||
|
fc9e3ac0f2
|
|||
|
b67f7557ee
|
|||
|
66d7f76133
|
|||
|
f7c27d5a98
|
|||
|
3e93d8508b
|
|||
|
872a38c768
|
|||
|
b9824307dd
|
|||
|
45419247f7
|
|||
|
ccd5debcef
|
|||
|
ad06012860
|
|||
|
2a8b49241e
|
|||
|
ed9059a812
|
|||
|
d52204c2f5
|
|||
|
3a8ed215c4
|
|||
|
f51ee5667f
|
|||
|
5792849c1e
|
|||
|
4e0d54efa0
|
|||
|
301ff3679a
|
|||
|
f5cea1062a
|
|||
|
2c6abaf577
|
|||
|
dde591049f
|
|||
|
bdaafb66d9
|
|||
|
e3935c9760
|
|||
|
4115193ef7
|
|||
|
2afda4873b
|
|||
|
bc41904e06
|
|||
|
4fb54705ca
|
|||
|
12ce8b1ec3
|
|||
|
9c3feca6a7
|
|||
|
aaf88fda56
|
|||
|
23a271fbce
|
|||
|
f3e52cda73
|
|||
|
04e69202b6
|
|||
|
001727ab85
|
|||
|
351f57229c
|
|||
|
25cfa659c7
|
|||
|
d5d1e16d1f
|
|||
|
e40eae866a
|
|||
|
3458d21567
|
|||
|
63c73bc6d9
|
|||
|
90d09350e2
|
|||
|
06b11cb2eb
|
|||
|
14295f1931
|
|||
|
0aa8a3842c
|
|||
|
1395496fce
|
|||
|
91aaa092f3
|
|||
|
a7ae00beac
|
|||
|
7ad0db73b0
|
|||
|
36693ba21b
|
|||
|
9e170a91ef
|
|||
|
911b08ba71
|
|||
|
36781b3af4
|
|||
|
a70afd8615
|
|||
|
9fe5d04fca
|
|||
|
23e12d3c7e
|
|||
|
4b6faef5eb
|
|||
|
cacf3f9c0d
|
|||
|
697a873148
|
|||
|
c123c4985c
|
|||
|
64a34fdf0d
|
|||
|
5484b122a1
|
|||
|
b2fe22195f
|
|||
| ce864c7c55 | |||
| 407d6df417 | |||
| b961ef8fd3 | |||
| aba9651c43 | |||
| 0dbb68d518 | |||
| 262446fff7 | |||
| 914501036d | |||
| 7e204690d3 | |||
| ae1b2566a3 | |||
| dc0afc8d5a | |||
| d53a4d6061 | |||
| c839011847 | |||
| 6df9befdd1 | |||
| aa97607ec4 | |||
| 0048e9f462 | |||
| 08fc0e790e | |||
| a1a0d6adc4 | |||
| 3aec29388b | |||
| dbbc0ce5f2 | |||
| 79c6c80636 | |||
| 9ac0294974 | |||
| 870185001d | |||
| 34bdbf9f9c | |||
| a9d63abf9b | |||
| 53b1e40d8f | |||
| c113a34f1f | |||
| 9e04a4b734 | |||
|
3b21ab5b63
|
|||
|
d431ff809e
|
|||
|
dcc5ce361d
|
|||
|
cc9c63d879
|
|||
|
4e6ce0e723
|
|||
|
c604fa308e
|
|||
|
a551e075b7
|
|||
|
1966a696ab
|
|||
|
7da2809399
|
|||
|
3f9e6d3dee
|
|||
|
7dd344f722
|
|||
| 54c7dbc940 | |||
| 699bcb9d2c | |||
| 51eb7d8f47 | |||
| 54d90519cb | |||
| d48736114b | |||
| 36911b5701 | |||
| fcc52ed62a | |||
| 29e2d49ef6 | |||
| 92b647fa0e | |||
| 7b2abd29b9 | |||
| 7f96da1555 | |||
| ac723810c5 | |||
| 3abcb355eb | |||
| 71a951dce9 | |||
| 04317cb9e9 | |||
| 7edab16bd3 | |||
| 86189b0f5b | |||
| 297b094de5 | |||
| bcd600c766 | |||
| 9293287566 | |||
| 22533ee75d | |||
| f8e6efb5b3 | |||
| a67e23fa6a | |||
| 9f98ebd4c4 | |||
|
1f0855c9f3
|
|||
| f3d0fd7459 | |||
|
0dbc742f42
|
|||
|
2be6de06d6
|
|||
| 5cde4a3b45 | |||
|
228ce106b2
|
|||
| 6d8d1ab5f8 | |||
|
928c3fbb12
|
|||
| c4eb3e940e | |||
| 43613b60ce | |||
|
82f457406a
|
|||
| ec2f1a939b | |||
| 490668ff61 | |||
| 21a21303f8 | |||
| 7edeef04f7 | |||
| 793a0867dd | |||
| 4b7501acf5 | |||
| a585ac58f6 | |||
| d16b59edc0 | |||
| 9e6ccc0343 | |||
| 3215da670e | |||
|
6e813383fb
|
|||
| cbff64906b | |||
| 27955fc264 | |||
| ee59400d76 | |||
| 88d229cb2d | |||
| 79a29ad982 | |||
| e98b39a9da | |||
| f41d33625f | |||
| ae9108769c | |||
| 6d0813c8db | |||
| 1c7c9425bc | |||
| 341a3a127b | |||
| 66a6ac22d0 | |||
| 466d0b7abe | |||
| 07d691ad4e | |||
| 89af132577 | |||
| 8d9339cdea | |||
| 0a43591739 | |||
| 762e246ccd | |||
| ddf28d9a46 | |||
| 586fd41133 | |||
| 982fbba413 | |||
| b805b8e34e | |||
| 956fb5604a | |||
| 6dc57b28b1 | |||
| fdee1beb05 | |||
| 58cd02b3a5 | |||
| bf03fa12f6 | |||
| 4897c53259 | |||
| c41d788328 | |||
| d5086e21ae | |||
| 355f13b33d | |||
| 68f3efc128 | |||
| 8db0eeba9a | |||
| 439dbe51b6 | |||
| 330589bdf1 | |||
| 8ad01a5c50 | |||
| d9c299a58f | |||
| f7cb1af2c4 | |||
| 1fdb739950 | |||
|
8438a9a2b7
|
|||
| 357071b967 | |||
|
75f31b5b93
|
|||
| fe2cd31200 | |||
| 793e6c2009 | |||
| 15593af2a9 | |||
|
33b5c9ccc8
|
|||
|
e33f856f21
|
|||
|
8f13ac6d4b
|
@@ -0,0 +1,22 @@
|
||||
# Database
|
||||
MONGODB_ROOT_USERNAME=mongo_user
|
||||
MONGODB_ROOT_PASSWORD=mongo_pass
|
||||
MONGODB_URI=mongodb://mongo_user:mongo_pass@database:27017
|
||||
|
||||
# SMTP
|
||||
SMTP_SERVICE=gmail
|
||||
SMTP_EMAILADDR=example@gmail.com
|
||||
SMTP_PASSWORD=app pass here
|
||||
SMTP_HOST=smtp.gmail.com
|
||||
SMTP_PORT=587
|
||||
|
||||
# Cloudinary
|
||||
CLOUDINARY_CLOUD_NAME=cloudname_here
|
||||
CLOUDINARY_API_KEY=api_key_here
|
||||
CLOUDINARY_API_SECRET=api_secret_here
|
||||
|
||||
# Gemini
|
||||
GEMINI_API_KEY=gemini_api_here
|
||||
|
||||
# Refresh token
|
||||
REFRESH_TOKEN_SECRET=a_random_60_or_more_char_secret_here
|
||||
@@ -0,0 +1,4 @@
|
||||
package-lock.json
|
||||
node_modules/
|
||||
db/
|
||||
deployed-compose.yaml
|
||||
@@ -0,0 +1,6 @@
|
||||
Dockerfile
|
||||
node_modules/
|
||||
package-lock.json
|
||||
.dockerignore
|
||||
.env.back
|
||||
.gitignore
|
||||
@@ -0,0 +1,26 @@
|
||||
PORT = 8000
|
||||
FRONTEND_URI = ${FRONTEND_URI}
|
||||
|
||||
# Database
|
||||
MONGODB_URI = ${MONGODB_URI}
|
||||
DATABASE_NAME=CropCompass
|
||||
|
||||
# Mail server
|
||||
SMPT_SERVICE = ${SMTP_SERVICE}
|
||||
SMPT_MAIL = ${SMTP_EMAILADDR}
|
||||
SMPT_PASSWORD = ${SMTP_PASSWORD}
|
||||
HOST = ${SMTP_HOST}
|
||||
EMAIL_PORT = ${SMTP_PORT}
|
||||
|
||||
# Cloudinary
|
||||
CLOUDINARY_CLOUD_NAME = ${CLOUDINARY_CLOUD_NAME}
|
||||
CLOUDINARY_API_KEY = ${CLOUDINARY_API_KEY}
|
||||
CLOUDINARY_API_SECRET = ${CLOUDINARY_API_SECRET}
|
||||
|
||||
# Gemini
|
||||
GEMINI_API_KEY = ${GEMINI_API_KEY}
|
||||
|
||||
# Refresh token
|
||||
TOKEN_NAME = uid
|
||||
REFRESH_TOKEN_EXPIRY = 10d
|
||||
REFRESH_TOKEN_SECRET = ${REFRESH_TOKEN_SECRET}
|
||||
@@ -0,0 +1,59 @@
|
||||
package-lock.json
|
||||
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# Environment files
|
||||
.env.bak
|
||||
.env.*.local
|
||||
|
||||
# Build outputs
|
||||
dist/
|
||||
build/
|
||||
|
||||
# Static files
|
||||
public/
|
||||
out/
|
||||
|
||||
# Test files
|
||||
coverage/
|
||||
*.lcov
|
||||
|
||||
# Database files
|
||||
*.sqlite3
|
||||
*.db
|
||||
*.db-wal
|
||||
*.db-shm
|
||||
|
||||
# Temporary files
|
||||
*.tmp
|
||||
*.temp
|
||||
*.swp
|
||||
*.swo
|
||||
|
||||
# IDE and editor files
|
||||
.vscode/
|
||||
.idea/
|
||||
*.iml
|
||||
|
||||
# System files
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# Other
|
||||
*.log.*
|
||||
@@ -0,0 +1,217 @@
|
||||
const Crop = require("../Models/crop.model.js");
|
||||
const Farm = require("../Models/farm.model.js");
|
||||
const { uploadOnCloudinary } = require("../Utils/cloudinary.js");
|
||||
const { run } = require("../Utils/model.js");
|
||||
|
||||
// Create a new crop
|
||||
const createCrop = async (req, res) => {
|
||||
try {
|
||||
const { name, farm, harvestDate, growthStage, healthStatus } = req.body;
|
||||
if (!req.file.path) {
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: "Avatar not uploaded on cloudinary.",
|
||||
});
|
||||
}
|
||||
|
||||
const imageUrl = await uploadOnCloudinary(req.file.path);
|
||||
|
||||
console.log("Image url is : ", imageUrl);
|
||||
|
||||
if (!imageUrl) {
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
message: "Image not uploaded on cloudinary.",
|
||||
});
|
||||
}
|
||||
|
||||
// Check if the farm exists
|
||||
const existingFarm = await Farm.findById(farm);
|
||||
if (!existingFarm)
|
||||
return res.status(404).json({ message: "Farm not found" });
|
||||
|
||||
const crop = new Crop({
|
||||
name,
|
||||
farm,
|
||||
image: imageUrl,
|
||||
harvestDate,
|
||||
growthStage,
|
||||
healthStatus,
|
||||
});
|
||||
|
||||
await crop.save();
|
||||
|
||||
// Add crop to farm
|
||||
existingFarm.crops.push(crop._id);
|
||||
await existingFarm.save();
|
||||
|
||||
res.status(201).json(crop);
|
||||
} catch (error) {
|
||||
res.status(500).json({ message: error.message });
|
||||
}
|
||||
};
|
||||
|
||||
// Get all crops for a specific farm
|
||||
const getCropsByFarm = async (req, res) => {
|
||||
try {
|
||||
console.log("My farm id is : ", req.params.farmId);
|
||||
const crops = await Crop.find({ farm: req.params.farmId });
|
||||
|
||||
res.status(200).json(crops);
|
||||
} catch (error) {
|
||||
res.status(500).json({ message: error.message });
|
||||
}
|
||||
};
|
||||
|
||||
// Get a single crop by ID
|
||||
const getCropById = async (req, res) => {
|
||||
try {
|
||||
const crop = await Crop.findById(req.params.cropId).populate("farm");
|
||||
|
||||
if (!crop) return res.status(404).json({ message: "Crop not found" });
|
||||
|
||||
res.status(200).json(crop);
|
||||
} catch (error) {
|
||||
res.status(500).json({ message: error.message });
|
||||
}
|
||||
};
|
||||
|
||||
// Update crop details
|
||||
const updateCrop = async (req, res) => {
|
||||
try {
|
||||
const updatedCrop = await Crop.findByIdAndUpdate(
|
||||
req.params.cropId,
|
||||
req.body,
|
||||
{ new: true }
|
||||
);
|
||||
|
||||
if (!updatedCrop)
|
||||
return res.status(404).json({ message: "Crop not found" });
|
||||
|
||||
res.status(200).json(updatedCrop);
|
||||
} catch (error) {
|
||||
res.status(500).json({ message: error.message });
|
||||
}
|
||||
};
|
||||
|
||||
// Delete a crop
|
||||
const deleteCrop = async (req, res) => {
|
||||
try {
|
||||
const crop = await Crop.findById(req.params.cropId);
|
||||
|
||||
if (!crop) return res.status(404).json({ message: "Crop not found" });
|
||||
|
||||
await crop.deleteOne();
|
||||
|
||||
// Remove crop from the farm
|
||||
await Farm.findByIdAndUpdate(crop.farm, { $pull: { crops: crop._id } });
|
||||
|
||||
res.status(200).json({ message: "Crop deleted successfully" });
|
||||
} catch (error) {
|
||||
res.status(500).json({ message: error.message });
|
||||
}
|
||||
};
|
||||
|
||||
// Update crop growth stage
|
||||
const updateGrowthStage = async (req, res) => {
|
||||
try {
|
||||
const { growthStage } = req.body;
|
||||
|
||||
const crop = await Crop.findById(req.params.cropId);
|
||||
if (!crop) return res.status(404).json({ message: "Crop not found" });
|
||||
|
||||
crop.growthStage = growthStage;
|
||||
await crop.save();
|
||||
|
||||
res.status(200).json({ message: "Growth stage updated", crop });
|
||||
} catch (error) {
|
||||
res.status(500).json({ message: error.message });
|
||||
}
|
||||
};
|
||||
|
||||
// Update crop health status
|
||||
const updateHealthStatus = async (req, res) => {
|
||||
try {
|
||||
const { healthStatus } = req.body;
|
||||
|
||||
const crop = await Crop.findById(req.params.cropId);
|
||||
if (!crop) return res.status(404).json({ message: "Crop not found" });
|
||||
|
||||
crop.healthStatus = healthStatus;
|
||||
await crop.save();
|
||||
|
||||
res.status(200).json({ message: "Health status updated", crop });
|
||||
} catch (error) {
|
||||
res.status(500).json({ message: error.message });
|
||||
}
|
||||
};
|
||||
|
||||
const cropHarvest = async (req, res) => {
|
||||
try {
|
||||
const crop = await Crop.findById(req.params.cropId).populate("farm");
|
||||
console.log(crop);
|
||||
|
||||
const message = `My crop is ${crop.name}, given that I have planted it on ${crop.plantedDate}, suggest me a date as to when it will be harvested. Give output in the form: ${crop.name} will take .. months to harvest around (give month name and year).`; // for harvest expectation
|
||||
|
||||
const result = await run(message);
|
||||
res.status(200).json({ message: result });
|
||||
} catch (error) {
|
||||
res.status(500).json({ message: error.message });
|
||||
}
|
||||
};
|
||||
|
||||
const suggestNextCrop = async (req, res) => {
|
||||
try {
|
||||
const crop = await Crop.findById(req.params.cropId).populate("farm");
|
||||
console.log(crop);
|
||||
|
||||
const message = `Currently I have ${crop.name} in my field. Considering the best optimal time for its harvestation give my location, ${crop.farm.location} and water content of my field is ${crop.farm.waterContent}. Suggest next crop I should grow and give more suggestions around it. Don't tell me when to harvest ${crop.name}, only tell me next best crop to plant in my field and give suggestions around it.`; // for next sowing suggestion
|
||||
|
||||
const result = await run(message);
|
||||
res.status(200).json({ message: result });
|
||||
} catch (error) {
|
||||
res.status(500).json({ message: error.message });
|
||||
}
|
||||
};
|
||||
|
||||
const suggestPesticides = async (req, res) => {
|
||||
try {
|
||||
const crop = await Crop.findById(req.params.cropId).populate("farm");
|
||||
console.log(crop);
|
||||
|
||||
const message = `Considering I have grown ${crop.name} in my field located in ${crop.farm.location} and has water content of ${crop.farm.waterContent}. Suggest pesticides I should use on the crop and when to use it considering my sow date is ${crop.sowDate}. Give precautionary measures and suggestions around it.`; // for pesticides
|
||||
|
||||
const result = await run(message);
|
||||
res.status(200).json({ message: result });
|
||||
} catch (error) {
|
||||
res.status(500).json({ message: error.message });
|
||||
}
|
||||
};
|
||||
|
||||
const suggestFertilizers = async (req, res) => {
|
||||
try {
|
||||
const crop = await Crop.findById(req.params.cropId).populate("farm");
|
||||
console.log(crop);
|
||||
|
||||
const message = `Considering I have grown ${crop.name} in my field located in ${crop.farm.location} and has water content of ${crop.farm.waterContent}. Suggest fertilizers I should use on the crop and when to use it, i.e. after how many months after sowing considering sowing date is ${crop.sowDate} considering my sow date is ${crop.sowDate}. Give me precautionary measures.`; // for fertilizers
|
||||
|
||||
const result = await run(message);
|
||||
res.status(200).json({ message: result });
|
||||
} catch (error) {
|
||||
res.status(500).json({ message: error.message });
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
createCrop,
|
||||
getCropsByFarm,
|
||||
getCropById,
|
||||
updateCrop,
|
||||
deleteCrop,
|
||||
updateGrowthStage,
|
||||
updateHealthStatus,
|
||||
cropHarvest,
|
||||
suggestNextCrop,
|
||||
suggestPesticides,
|
||||
suggestFertilizers,
|
||||
};
|
||||
@@ -0,0 +1,132 @@
|
||||
const Farm = require("../Models/farm.model.js");
|
||||
const Crop = require("../Models/crop.model.js");
|
||||
const Finance = require("../Models/finance.model.js");
|
||||
|
||||
// Create a farm
|
||||
const createFarm = async (req, res) => {
|
||||
try {
|
||||
const { name, location, waterContent, soilType, size } = req.body;
|
||||
|
||||
const farm = new Farm({
|
||||
name,
|
||||
location,
|
||||
size,
|
||||
waterContent,
|
||||
soilType,
|
||||
owner: req.user._id,
|
||||
});
|
||||
|
||||
await farm.save();
|
||||
res.status(201).json(farm);
|
||||
} catch (error) {
|
||||
res.status(500).json({ message: error.message });
|
||||
}
|
||||
};
|
||||
|
||||
// Get all farms for a user
|
||||
const getUserFarms = async (req, res) => {
|
||||
try {
|
||||
const farms = await Farm.find({ owner: req.user._id })
|
||||
.populate("crops")
|
||||
.populate("finances");
|
||||
|
||||
res.status(200).json(farms);
|
||||
} catch (error) {
|
||||
res.status(500).json({ message: error.message });
|
||||
}
|
||||
};
|
||||
|
||||
// Get a single farm by ID
|
||||
const getFarmById = async (req, res) => {
|
||||
try {
|
||||
console.log("also i am clla ing", "My farm id is : ", req.params.farmId);
|
||||
const farm = await Farm.findById(req.params.farmId)
|
||||
.populate("crops")
|
||||
.populate("finances");
|
||||
|
||||
if (!farm) return res.status(404).json({ message: "Farm not found" });
|
||||
|
||||
res.status(200).json(farm);
|
||||
} catch (error) {
|
||||
res.status(500).json({ message: error.message });
|
||||
}
|
||||
};
|
||||
|
||||
// Update farm details
|
||||
const updateFarm = async (req, res) => {
|
||||
try {
|
||||
const updatedFarm = await Farm.findByIdAndUpdate(
|
||||
req.params.farmId,
|
||||
req.body,
|
||||
{ new: true }
|
||||
);
|
||||
|
||||
if (!updatedFarm)
|
||||
return res.status(404).json({ message: "Farm not found" });
|
||||
|
||||
res.status(200).json(updatedFarm);
|
||||
} catch (error) {
|
||||
res.status(500).json({ message: error.message });
|
||||
}
|
||||
};
|
||||
|
||||
// Delete a farm
|
||||
const deleteFarm = async (req, res) => {
|
||||
try {
|
||||
const farm = await Farm.findById(req.params.farmId);
|
||||
|
||||
if (!farm) return res.status(404).json({ message: "Farm not found" });
|
||||
|
||||
await Crop.deleteMany({ farm: farm._id });
|
||||
await Finance.findByIdAndDelete(farm.finances);
|
||||
await farm.deleteOne();
|
||||
|
||||
res.status(200).json({ message: "Farm deleted successfully" });
|
||||
} catch (error) {
|
||||
res.status(500).json({ message: error.message });
|
||||
}
|
||||
};
|
||||
|
||||
// Add fertilizer to a farm
|
||||
const addFertilizer = async (req, res) => {
|
||||
try {
|
||||
const { name, quantity } = req.body;
|
||||
|
||||
const farm = await Farm.findById(req.params.farmId);
|
||||
if (!farm) return res.status(404).json({ message: "Farm not found" });
|
||||
|
||||
farm.fertilizer.push({ name, quantity });
|
||||
await farm.save();
|
||||
|
||||
res.status(200).json({ message: "Fertilizer added", farm });
|
||||
} catch (error) {
|
||||
res.status(500).json({ message: error.message });
|
||||
}
|
||||
};
|
||||
|
||||
// Add pesticide to a farm
|
||||
const addPesticide = async (req, res) => {
|
||||
try {
|
||||
const { name, quantity } = req.body;
|
||||
|
||||
const farm = await Farm.findById(req.params.farmId);
|
||||
if (!farm) return res.status(404).json({ message: "Farm not found" });
|
||||
|
||||
farm.pestisides.push({ name, quantity });
|
||||
await farm.save();
|
||||
|
||||
res.status(200).json({ message: "Pesticide added", farm });
|
||||
} catch (error) {
|
||||
res.status(500).json({ message: error.message });
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
createFarm,
|
||||
getUserFarms,
|
||||
getFarmById,
|
||||
updateFarm,
|
||||
deleteFarm,
|
||||
addFertilizer,
|
||||
addPesticide,
|
||||
};
|
||||
@@ -0,0 +1,182 @@
|
||||
const Finance = require("../Models/finance.model.js");
|
||||
const Farm = require("../Models/farm.model.js");
|
||||
|
||||
// Create finance record for a farm
|
||||
// const createFinance = async (req, res) => {
|
||||
// try {
|
||||
// const { farm } = req.body;
|
||||
|
||||
// console.log("My farm id is which is going to be created : ", req.body);
|
||||
|
||||
// // Check if the farm exists
|
||||
// const existingFarm = await Farm.findById(farm);
|
||||
// if (!existingFarm)
|
||||
// return res.status(404).json({ message: "Farm not found" });
|
||||
|
||||
// const finance = new Finance({
|
||||
// farm,
|
||||
// transactions: [],
|
||||
// totalExpenses: 0,
|
||||
// totalRevenue: 0,
|
||||
// });
|
||||
|
||||
// await finance.save();
|
||||
|
||||
// // Link finance to farm
|
||||
// existingFarm.finances = finance._id;
|
||||
// await existingFarm.save();
|
||||
|
||||
// res.status(201).json(finance);
|
||||
// } catch (error) {
|
||||
// res.status(500).json({ message: error.message });
|
||||
// }
|
||||
// };
|
||||
const createFinance = async (req, res) => {
|
||||
try {
|
||||
const { farm } = req.body;
|
||||
|
||||
console.log("My farm id is which is going to be created : ", farm);
|
||||
|
||||
// Check if the farm exists
|
||||
const existingFarm = await Farm.findById(farm);
|
||||
if (!existingFarm) {
|
||||
return res.status(404).json({ message: "Farm not found" });
|
||||
}
|
||||
|
||||
// Check if finance already exists for this farm
|
||||
if (existingFarm.finances) {
|
||||
return res
|
||||
.status(400)
|
||||
.json({ message: "Finance already exists for this farm" });
|
||||
}
|
||||
|
||||
// Create finance entry
|
||||
const finance = new Finance({
|
||||
farm,
|
||||
transactions: [],
|
||||
totalExpenses: 0,
|
||||
totalRevenue: 0,
|
||||
});
|
||||
|
||||
await finance.save();
|
||||
|
||||
// Link finance to farm
|
||||
existingFarm.finances = finance._id;
|
||||
await existingFarm.save();
|
||||
|
||||
res.status(201).json(finance);
|
||||
} catch (error) {
|
||||
res.status(500).json({ message: error.message });
|
||||
}
|
||||
};
|
||||
|
||||
// Get finance details by farm ID
|
||||
const getFinanceByFarm = async (req, res) => {
|
||||
try {
|
||||
console.log("My farm id is : ", req.params.farmId);
|
||||
const finance = await Finance.findOne({ farm: req.params.farmId });
|
||||
|
||||
if (!finance)
|
||||
return res.status(404).json({ message: "Finance record not found" });
|
||||
|
||||
res.status(200).json(finance);
|
||||
} catch (error) {
|
||||
res.status(500).json({ message: error.message });
|
||||
}
|
||||
};
|
||||
|
||||
// Add a transaction (expense/revenue)
|
||||
const addTransaction = async (req, res) => {
|
||||
try {
|
||||
const { type, amount, description } = req.body;
|
||||
|
||||
console.log("My type is : ", type);
|
||||
console.log("My amount is : ", amount);
|
||||
console.log("My description is : ", description);
|
||||
|
||||
console.log("My finance id is : ", req.params.financeId);
|
||||
|
||||
const finance = await Finance.findById(req.params.financeId);
|
||||
if (!finance)
|
||||
return res.status(404).json({ message: "Finance record not found" });
|
||||
|
||||
finance.transactions.push({ type, amount, description });
|
||||
|
||||
// Update totals
|
||||
if (type === "Expense") {
|
||||
finance.totalExpenses += amount;
|
||||
} else if (type === "Revenue") {
|
||||
finance.totalRevenue += amount;
|
||||
}
|
||||
|
||||
await finance.save();
|
||||
res.status(200).json({ message: "Transaction added", finance });
|
||||
} catch (error) {
|
||||
res.status(500).json({ message: error.message });
|
||||
}
|
||||
};
|
||||
|
||||
// Delete a transaction
|
||||
const deleteTransaction = async (req, res) => {
|
||||
try {
|
||||
const finance = await Finance.findById(req.params.financeId);
|
||||
if (!finance)
|
||||
return res.status(404).json({ message: "Finance record not found" });
|
||||
|
||||
const transaction = finance.transactions.id(req.params.transactionId);
|
||||
if (!transaction)
|
||||
return res.status(404).json({ message: "Transaction not found" });
|
||||
|
||||
// Adjust totals before removing
|
||||
if (transaction.type === "Expense") {
|
||||
finance.totalExpenses -= transaction.amount;
|
||||
} else if (transaction.type === "Revenue") {
|
||||
finance.totalRevenue -= transaction.amount;
|
||||
}
|
||||
|
||||
transaction.remove();
|
||||
await finance.save();
|
||||
|
||||
res.status(200).json({ message: "Transaction deleted", finance });
|
||||
} catch (error) {
|
||||
res.status(500).json({ message: error.message });
|
||||
}
|
||||
};
|
||||
|
||||
// Get all transactions for a farm's finance
|
||||
const getTransactions = async (req, res) => {
|
||||
try {
|
||||
const finance = await Finance.findById(req.params.financeId);
|
||||
if (!finance)
|
||||
return res.status(404).json({ message: "Finance record not found" });
|
||||
|
||||
res.status(200).json(finance.transactions);
|
||||
} catch (error) {
|
||||
res.status(500).json({ message: error.message });
|
||||
}
|
||||
};
|
||||
|
||||
// Get total expenses and revenue
|
||||
const getFinancialSummary = async (req, res) => {
|
||||
try {
|
||||
const finance = await Finance.findById(req.params.financeId);
|
||||
if (!finance)
|
||||
return res.status(404).json({ message: "Finance record not found" });
|
||||
|
||||
res.status(200).json({
|
||||
totalExpenses: finance.totalExpenses,
|
||||
totalRevenue: finance.totalRevenue,
|
||||
});
|
||||
} catch (error) {
|
||||
res.status(500).json({ message: error.message });
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
createFinance,
|
||||
getFinanceByFarm,
|
||||
addTransaction,
|
||||
deleteTransaction,
|
||||
getTransactions,
|
||||
getFinancialSummary,
|
||||
};
|
||||
@@ -0,0 +1,120 @@
|
||||
const Task = require("../Models/task.model.js");
|
||||
const Farm = require("../Models/farm.model.js");
|
||||
const Crop = require("../Models/crop.model.js");
|
||||
|
||||
// Create a new task
|
||||
const createTask = async (req, res) => {
|
||||
try {
|
||||
const { farm, crop, taskType, description, assignedDate, status } =
|
||||
req.body;
|
||||
|
||||
// Validate farm existence
|
||||
const existingFarm = await Farm.findById(farm);
|
||||
if (!existingFarm)
|
||||
return res.status(404).json({ message: "Farm not found" });
|
||||
|
||||
// Validate crop if provided
|
||||
if (crop) {
|
||||
const existingCrop = await Crop.findById(crop);
|
||||
if (!existingCrop)
|
||||
return res.status(404).json({ message: "Crop not found" });
|
||||
}
|
||||
|
||||
const task = new Task({
|
||||
farm,
|
||||
crop,
|
||||
taskType,
|
||||
description,
|
||||
assignedDate,
|
||||
status,
|
||||
});
|
||||
|
||||
await task.save();
|
||||
res.status(201).json(task);
|
||||
} catch (error) {
|
||||
res.status(500).json({ message: error.message });
|
||||
}
|
||||
};
|
||||
|
||||
// Get all tasks for a specific farm
|
||||
const getTasksByFarm = async (req, res) => {
|
||||
try {
|
||||
const tasks = await Task.find({ farm: req.params.farmId }).populate("crop");
|
||||
|
||||
res.status(200).json(tasks);
|
||||
} catch (error) {
|
||||
res.status(500).json({ message: error.message });
|
||||
}
|
||||
};
|
||||
|
||||
// Get a single task by ID
|
||||
const getTaskById = async (req, res) => {
|
||||
try {
|
||||
const task = await Task.findById(req.params.taskId).populate("farm crop");
|
||||
|
||||
if (!task) return res.status(404).json({ message: "Task not found" });
|
||||
|
||||
res.status(200).json(task);
|
||||
} catch (error) {
|
||||
res.status(500).json({ message: error.message });
|
||||
}
|
||||
};
|
||||
|
||||
// Update task details
|
||||
const updateTask = async (req, res) => {
|
||||
try {
|
||||
const updatedTask = await Task.findByIdAndUpdate(
|
||||
req.params.taskId,
|
||||
req.body,
|
||||
{ new: true }
|
||||
);
|
||||
|
||||
if (!updatedTask)
|
||||
return res.status(404).json({ message: "Task not found" });
|
||||
|
||||
res.status(200).json(updatedTask);
|
||||
} catch (error) {
|
||||
res.status(500).json({ message: error.message });
|
||||
}
|
||||
};
|
||||
|
||||
// Delete a task
|
||||
const deleteTask = async (req, res) => {
|
||||
try {
|
||||
const task = await Task.findById(req.params.taskId);
|
||||
|
||||
if (!task) return res.status(404).json({ message: "Task not found" });
|
||||
|
||||
await task.deleteOne();
|
||||
|
||||
res.status(200).json({ message: "Task deleted successfully" });
|
||||
} catch (error) {
|
||||
res.status(500).json({ message: error.message });
|
||||
}
|
||||
};
|
||||
|
||||
// Update task status (Pending → Completed)
|
||||
const updateTaskStatus = async (req, res) => {
|
||||
try {
|
||||
const { status } = req.body;
|
||||
|
||||
const task = await Task.findById(req.params.taskId);
|
||||
if (!task) return res.status(404).json({ message: "Task not found" });
|
||||
|
||||
task.status = status;
|
||||
await task.save();
|
||||
|
||||
res.status(200).json({ message: "Task status updated", task });
|
||||
} catch (error) {
|
||||
res.status(500).json({ message: error.message });
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
createTask,
|
||||
getTasksByFarm,
|
||||
getTaskById,
|
||||
updateTask,
|
||||
deleteTask,
|
||||
updateTaskStatus,
|
||||
};
|
||||
@@ -0,0 +1,481 @@
|
||||
const catchAsyncErrors = require("../Middlewares/catchAsyncErrors.js");
|
||||
const User = require("../Models/user.model.js");
|
||||
const { uploadOnCloudinary } = require("../Utils/cloudinary.js");
|
||||
const sendEmail = require("../Utils/sendmail.js");
|
||||
const crypto = require("crypto");
|
||||
const jwt = require("jsonwebtoken");
|
||||
const sha1 = require("sha1");
|
||||
const axios = require("axios");
|
||||
|
||||
// Register or Sign up new User -- Done
|
||||
const registerUser = catchAsyncErrors(async (req, res) => {
|
||||
const { name, email, password, role } = req.body;
|
||||
|
||||
// Strong password policy
|
||||
const strongPasswordRegex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&]).{8,}$/;
|
||||
if (!strongPasswordRegex.test(password)) {
|
||||
return res.status(400).json({ success: false, message: "Password must be at least 8 characters long and include uppercase, lowercase, number, and special character." });
|
||||
}
|
||||
|
||||
// Check for data breach with haveibeenpwned.com
|
||||
const hashed = sha1(password).toUpperCase();
|
||||
const prefix = hashed.slice(0, 5);
|
||||
const suffix = hashed.slice(5);
|
||||
const response = await axios.get(`https://api.pwnedpasswords.com/range/${prefix}`);
|
||||
if (response.data.includes(suffix)) {
|
||||
return res.status(400).json({ success: false, message: "This password has appeared in a data breach. Please choose a different one." });
|
||||
}
|
||||
|
||||
const user = await User.create({
|
||||
name,
|
||||
email,
|
||||
password,
|
||||
role,
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
message: "User not created something went wrong.",
|
||||
});
|
||||
}
|
||||
|
||||
return res.status(200).json({
|
||||
success: true,
|
||||
message: "User is registered successfully",
|
||||
data: user,
|
||||
});
|
||||
});
|
||||
|
||||
// Login user in our web app -- Done
|
||||
const loginUser = catchAsyncErrors(async (req, res) => {
|
||||
const { email, password } = req.body;
|
||||
|
||||
const user = await User.findOne({ email });
|
||||
|
||||
if (!user) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: "User not found",
|
||||
});
|
||||
}
|
||||
|
||||
const checkUser = await user.isPasswordCorrect(password);
|
||||
|
||||
if (!checkUser) {
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
message: "Password is incorrect",
|
||||
});
|
||||
}
|
||||
|
||||
const token = await user.generateRefreshToken();
|
||||
|
||||
if (!token) {
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
message: "token not created something went wrong.",
|
||||
});
|
||||
}
|
||||
|
||||
user.password = null;
|
||||
|
||||
return res
|
||||
.status(200)
|
||||
.cookie("uid", token, {
|
||||
httpOnly: true, // Prevent access from JavaScript (recommended for security)
|
||||
secure: false, // ⚠️ Set to `false` for localhost
|
||||
sameSite: "Lax", // Use "Lax" instead of "None" for better compatibility
|
||||
path: "/",
|
||||
expires: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000), // 7 days
|
||||
})
|
||||
.json({
|
||||
success: true,
|
||||
message: "User is successfully logged in.",
|
||||
data: user,
|
||||
});
|
||||
});
|
||||
|
||||
// Logout user in our web app -- Done
|
||||
const logoutUser = catchAsyncErrors(async (req, res) => {
|
||||
return res
|
||||
.clearCookie(process.env.TOKEN_NAME, {
|
||||
path: "/",
|
||||
sameSite: "None",
|
||||
secure: process.env.NODE_ENV === "production",
|
||||
httpOnly: true,
|
||||
expires: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000),
|
||||
})
|
||||
.status(201)
|
||||
.json({
|
||||
success: true,
|
||||
message: "User is logged out successfully",
|
||||
});
|
||||
});
|
||||
|
||||
// -- DONE
|
||||
const intializeUser = catchAsyncErrors(async (req, res) => {
|
||||
const tokenValue = req.cookies[process.env.TOKEN_NAME];
|
||||
|
||||
// console.log("I am the one who is doing this : ", tokenValue);
|
||||
|
||||
if (!tokenValue) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: "User is not logged in.",
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
const payload = await jwt.verify(
|
||||
tokenValue,
|
||||
process.env.REFRESH_TOKEN_SECRET
|
||||
);
|
||||
|
||||
if (!payload) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: "Something went wrong",
|
||||
});
|
||||
}
|
||||
|
||||
const user = await User.findById(payload._id).select("-password");
|
||||
|
||||
return res.status(200).json({
|
||||
success: true,
|
||||
message: "User data get successfully",
|
||||
data: user,
|
||||
});
|
||||
} catch (error) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: "Something went wrong",
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Update user deatails -- ADMIN
|
||||
const updateUserDetails = catchAsyncErrors(async (req, res) => {
|
||||
const user = await User.findById(req.params.id);
|
||||
if (!user) {
|
||||
return res.status(404).json({
|
||||
success: true,
|
||||
message: "User not found",
|
||||
});
|
||||
}
|
||||
|
||||
const { name, email } = req.body;
|
||||
|
||||
const updateUser = await User.findByIdAndUpdate(req.params.id, {
|
||||
$set: {
|
||||
name: name,
|
||||
email: email,
|
||||
},
|
||||
}).select("-password");
|
||||
|
||||
return res.status(200).json({
|
||||
success: true,
|
||||
message: "User is updated successfully",
|
||||
data: updateUser,
|
||||
});
|
||||
});
|
||||
|
||||
// forget password -- Done
|
||||
const forgetPassword = catchAsyncErrors(async (req, res) => {
|
||||
const { email } = req.body;
|
||||
const user = await User.findOne({ email });
|
||||
|
||||
if (!user) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: "User not found ",
|
||||
});
|
||||
}
|
||||
|
||||
// get reset password
|
||||
|
||||
const resetToken = await user.getResetPassword();
|
||||
|
||||
await user.save({ validateBeforeSave: false });
|
||||
|
||||
/*const resetPasswordUrl = `${req.protocol}://${req.get(
|
||||
"host"
|
||||
)}/api/v1/password/reset/${resetToken}`;*/
|
||||
|
||||
const resetPasswordUrl = `${process.env.FRONTEND_URI}/user/api/v1/password/reset/${resetToken}`;
|
||||
|
||||
const message = `Your password token is :-\n\n${resetPasswordUrl}\n\nIf you are not requested this email then please ingore this mail.`;
|
||||
|
||||
try {
|
||||
await sendEmail({
|
||||
email: user.email,
|
||||
subject: "MentorFlux password recovery",
|
||||
message: message,
|
||||
});
|
||||
return res.status(200).json({
|
||||
success: true,
|
||||
message: `Email sent to ${email} successfully`,
|
||||
});
|
||||
} catch (error) {
|
||||
user.resetPasswordToken = undefined;
|
||||
user.resetPasswordExpiry = undefined;
|
||||
await user.save({ validateBeforeSave: false });
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
message: "Something went wrong ",
|
||||
error: error,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// reset users password -- DONE
|
||||
const resetPassword = catchAsyncErrors(async (req, res) => {
|
||||
const token = req.params.token;
|
||||
|
||||
const { password, confirmPassword } = req.body;
|
||||
|
||||
//console.log("My password is :", password);
|
||||
//console.log("My confirmPassword is :", confirmPassword);
|
||||
//console.log("My token is :", token);
|
||||
const resetPasswordToken = await crypto
|
||||
.createHash("sha256")
|
||||
.update(token)
|
||||
.digest("hex");
|
||||
|
||||
const user = await User.findOne({
|
||||
resetPasswordToken,
|
||||
resetPasswordExpiry: { $gte: Date.now() },
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
return res.status(401).json({
|
||||
success: false,
|
||||
message: "Reset Password token is invalid or has been expired",
|
||||
});
|
||||
}
|
||||
|
||||
if (password !== confirmPassword) {
|
||||
return res.status(401).json({
|
||||
success: false,
|
||||
message: "Please enter password and confirm password",
|
||||
});
|
||||
}
|
||||
|
||||
user.password = password;
|
||||
user.resetPasswordToken = undefined;
|
||||
user.resetPasswordExpiry = undefined;
|
||||
|
||||
//console.log("To check the user ", user);
|
||||
|
||||
await user.save();
|
||||
|
||||
return res.status(200).json({
|
||||
success: true,
|
||||
message: "Password changed successfully",
|
||||
});
|
||||
});
|
||||
|
||||
// get user personal details
|
||||
const getUserDetails = catchAsyncErrors(async (req, res) => {
|
||||
const user = await User.findById(req.user._id);
|
||||
|
||||
if (!user) {
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
message: "Something went wrong ",
|
||||
});
|
||||
}
|
||||
|
||||
return res.status(200).json({
|
||||
success: true,
|
||||
message: "User details are fetched successfully",
|
||||
data: user,
|
||||
});
|
||||
});
|
||||
|
||||
// Update users password
|
||||
const updatePassword = catchAsyncErrors(async (req, res) => {
|
||||
const { password, oldPassword, confirmPassword } = req.body;
|
||||
|
||||
const user = await User.findById(req.user._id);
|
||||
|
||||
const isPasswordMatched = await user.isPasswordCorrect(oldPassword);
|
||||
|
||||
if (!user) {
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
message: "User not found",
|
||||
});
|
||||
}
|
||||
|
||||
if (!isPasswordMatched) {
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
message: "Old password is incorrect.Please enter correct password ",
|
||||
});
|
||||
}
|
||||
|
||||
if (password !== confirmPassword) {
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
message: "Password and Confirm password should be same.",
|
||||
});
|
||||
}
|
||||
|
||||
user.password = password;
|
||||
await user.save({ validateBeforeSave: false });
|
||||
|
||||
return res.status(200).json({
|
||||
success: true,
|
||||
message: "Password upadated successfully",
|
||||
});
|
||||
});
|
||||
|
||||
// update personal details
|
||||
const updatePersonalDetails = catchAsyncErrors(async (req, res) => {
|
||||
const { name, email } = req.body;
|
||||
const user = await User.findByIdAndUpdate(req.user._id, {
|
||||
$set: {
|
||||
name,
|
||||
email,
|
||||
},
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
message: "Something went wrong",
|
||||
});
|
||||
}
|
||||
|
||||
return res.status(200).json({
|
||||
success: true,
|
||||
message: "User details updated successfully",
|
||||
data: user,
|
||||
});
|
||||
});
|
||||
|
||||
// Get all users details -- ADMIN
|
||||
const getAllusersDetail = catchAsyncErrors(async (req, res) => {
|
||||
const users = await find();
|
||||
return res.status(200).json({
|
||||
success: true,
|
||||
message: "All user fetch successfully",
|
||||
data: users,
|
||||
});
|
||||
});
|
||||
|
||||
// get single user details
|
||||
const getSingaluserDetail = catchAsyncErrors(async (req, res) => {
|
||||
const user = await User.findById(req.params.id);
|
||||
|
||||
if (!user) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: "User not found",
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// upadate user Role -- ADMIN
|
||||
const updateUserRole = catchAsyncErrors(async (req, res) => {
|
||||
const { name, email, role } = req.body;
|
||||
const user = await User.findByIdAndUpdate(req.params.id, {
|
||||
$set: {
|
||||
name,
|
||||
email,
|
||||
role,
|
||||
},
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
message: "Something went wrong",
|
||||
});
|
||||
}
|
||||
|
||||
return res.status(200).json({
|
||||
success: true,
|
||||
message: "User Role updated successfully",
|
||||
data: user,
|
||||
});
|
||||
});
|
||||
|
||||
// Delete user -- ADMIN
|
||||
const DeleteUser = catchAsyncErrors(async (req, res) => {
|
||||
const user = await User.findByIdAndDelete(req.params.id);
|
||||
|
||||
if (!user) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: "User does not exist",
|
||||
});
|
||||
}
|
||||
|
||||
return res.status(200).json({
|
||||
success: true,
|
||||
message: "User deleted successfully",
|
||||
data: user,
|
||||
});
|
||||
});
|
||||
|
||||
// update avatar -- user
|
||||
const updateAvatar = catchAsyncErrors(async (req, res) => {
|
||||
//console.log("Our file is : ", req.file.path);
|
||||
|
||||
if (!req.file.path) {
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: "Avatar not uploaded on cloudinary.",
|
||||
});
|
||||
}
|
||||
|
||||
const avatarUrl = await uploadOnCloudinary(req.file.path);
|
||||
|
||||
if (!avatarUrl) {
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
message: "Avatar not uploaded on cloudinary.",
|
||||
});
|
||||
}
|
||||
|
||||
//console.log("Avatar url is : ", avatarUrl);
|
||||
|
||||
//console.log("our user is : ", req.user);
|
||||
|
||||
const user = await User.findByIdAndUpdate(req.user._id, {
|
||||
$set: {
|
||||
avatar: avatarUrl,
|
||||
},
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: "User not found.",
|
||||
});
|
||||
}
|
||||
|
||||
return res.status(200).json({
|
||||
success: true,
|
||||
message: "Avatar updated successfully.",
|
||||
data: user,
|
||||
});
|
||||
});
|
||||
|
||||
module.exports = {
|
||||
registerUser,
|
||||
loginUser,
|
||||
logoutUser,
|
||||
updateUserDetails,
|
||||
forgetPassword,
|
||||
resetPassword,
|
||||
getUserDetails,
|
||||
updatePassword,
|
||||
updatePersonalDetails,
|
||||
updateUserRole,
|
||||
DeleteUser,
|
||||
intializeUser,
|
||||
updateAvatar,
|
||||
};
|
||||
@@ -0,0 +1,25 @@
|
||||
const mongoose = require("mongoose");
|
||||
const catchAsyncErrors = require("../Middlewares/catchAsyncErrors.js");
|
||||
|
||||
const DB_connect = catchAsyncErrors(async () => {
|
||||
try {
|
||||
const dbUri = `${process.env.MONGODB_URI}/${process.env.DATABASE_NAME}?authSource=admin`;
|
||||
const connectionInstance = await mongoose.connect(dbUri, {
|
||||
useNewUrlParser: true,
|
||||
useUnifiedTopology: true,
|
||||
});
|
||||
|
||||
if (!connectionInstance) {
|
||||
console.log("MongoDB connection failed");
|
||||
} else {
|
||||
console.log(
|
||||
"MongoDB connected Successfully to:",
|
||||
connectionInstance.connection.host
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("MongoDB connection failed due to some error :", error);
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = DB_connect;
|
||||
@@ -0,0 +1,37 @@
|
||||
# Base image
|
||||
FROM node:22
|
||||
|
||||
# Metadata
|
||||
LABEL maintainer="kshitijka"
|
||||
LABEL version=1.1.0
|
||||
LABEL description="Crop Compass is a centralized management dashboard designed for farmers, enabling them to efficiently oversee their farms while leveraging advanced AI technology for disease identification and more."
|
||||
|
||||
# Update and upgrade
|
||||
RUN apt update && apt upgrade -y && \
|
||||
apt clean all && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Create non-root user
|
||||
RUN useradd -s /bin/bash nonroot
|
||||
|
||||
# Create working directory
|
||||
RUN mkdir -p /app /home/nonroot
|
||||
RUN chown -R nonroot:nonroot /app /home/nonroot
|
||||
WORKDIR /app
|
||||
|
||||
# Switch user
|
||||
USER nonroot
|
||||
|
||||
# Copy contents
|
||||
COPY . .
|
||||
|
||||
# Generate a random hex token and write it to .env
|
||||
#RUN echo "REFRESH_TOKEN_SECRET = $(openssl rand -hex 32)" >> /app/.env
|
||||
|
||||
# Install dependencies
|
||||
RUN npm install
|
||||
|
||||
# Expose backend port
|
||||
EXPOSE 8000
|
||||
|
||||
# Run backend
|
||||
CMD ["node", "index.js"]
|
||||
@@ -0,0 +1,49 @@
|
||||
const jwt = require("jsonwebtoken");
|
||||
const User = require("../Models/user.model.js");
|
||||
const dotenv = require("dotenv");
|
||||
dotenv.config({
|
||||
path: "./.env",
|
||||
});
|
||||
|
||||
async function checkAuthenticated(req, res, next) {
|
||||
const tokenValue = req.cookies[process.env.TOKEN_NAME];
|
||||
|
||||
console.log("I am called", tokenValue);
|
||||
if (!tokenValue) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: "User is not logged in.",
|
||||
});
|
||||
}
|
||||
try {
|
||||
const payload = await jwt.verify(
|
||||
tokenValue,
|
||||
process.env.REFRESH_TOKEN_SECRET
|
||||
);
|
||||
|
||||
if (!payload) {
|
||||
return next();
|
||||
}
|
||||
|
||||
req.user = payload;
|
||||
return next();
|
||||
} catch (error) {
|
||||
return next();
|
||||
}
|
||||
}
|
||||
|
||||
function authorizeRoles(...roles) {
|
||||
return async (req, res, next) => {
|
||||
if (!roles.includes(req.user.role)) {
|
||||
return res.status(401).json({
|
||||
success: false,
|
||||
message: "You are unauthorised to access this resource",
|
||||
});
|
||||
return next();
|
||||
}
|
||||
|
||||
return next();
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = { checkAuthenticated, authorizeRoles };
|
||||
@@ -0,0 +1,3 @@
|
||||
module.exports = (theFunc) => (req, res, next) => {
|
||||
Promise.resolve(theFunc(req, res, next)).catch(next);
|
||||
};
|
||||
@@ -0,0 +1,16 @@
|
||||
const multer = require("multer");
|
||||
|
||||
const storage = multer.diskStorage({
|
||||
destination: function (req, file, cb) {
|
||||
cb(null, "./uploads");
|
||||
},
|
||||
|
||||
filename: function (req, file, cb) {
|
||||
const uniquePrefix = Date.now();
|
||||
cb(null, uniquePrefix + "-" + file.originalname);
|
||||
},
|
||||
});
|
||||
|
||||
const upload = multer({ storage: storage });
|
||||
|
||||
module.exports = upload;
|
||||
@@ -0,0 +1,11 @@
|
||||
const rateLimit = require("express-rate-limit");
|
||||
|
||||
const loginLimiter = rateLimit({
|
||||
windowMs: 15 * 60 * 1000, // 15 minutes
|
||||
max: 5, // limit each IP to 5 login requests per windowMs
|
||||
message: "Too many login attempts. Try again in 15 minutes.",
|
||||
standardHeaders: true,
|
||||
legacyHeaders: false,
|
||||
});
|
||||
|
||||
module.exports = { loginLimiter };
|
||||
@@ -0,0 +1,22 @@
|
||||
const mongoose = require("mongoose");
|
||||
|
||||
const cropSchema = new mongoose.Schema(
|
||||
{
|
||||
name: { type: String, required: true },
|
||||
farm: { type: mongoose.Schema.Types.ObjectId, ref: "Farm", required: true },
|
||||
image: { type: String },
|
||||
plantedDate: { type: Date, required: true, default: Date.now() },
|
||||
harvestDate: { type: Date },
|
||||
growthStage: {
|
||||
type: String,
|
||||
enum: ["Planted", "Growing", "Ready to Harvest"],
|
||||
default: "Planted",
|
||||
},
|
||||
healthStatus: { type: String, default: "Healthy" },
|
||||
},
|
||||
{ timestamps: true }
|
||||
);
|
||||
|
||||
const Crop = mongoose.model("Crop", cropSchema);
|
||||
|
||||
module.exports = Crop;
|
||||
@@ -0,0 +1,37 @@
|
||||
const mongoose = require("mongoose");
|
||||
|
||||
const farmSchema = new mongoose.Schema(
|
||||
{
|
||||
name: { type: String, required: true },
|
||||
location: { type: String, required: true },
|
||||
owner: {
|
||||
type: mongoose.Schema.Types.ObjectId,
|
||||
ref: "User",
|
||||
required: true,
|
||||
},
|
||||
size: { type: String },
|
||||
waterContent: { type: String, required: true },
|
||||
soilType: { type: String, required: true },
|
||||
fertilizer: [
|
||||
{
|
||||
name: { type: String },
|
||||
quantity: { type: Number },
|
||||
addedAt: { type: Date, default: Date.now },
|
||||
},
|
||||
],
|
||||
pestisides: [
|
||||
{
|
||||
name: { type: String },
|
||||
quantity: { type: Number },
|
||||
addedAt: { type: Date, default: Date.now },
|
||||
},
|
||||
],
|
||||
crops: [{ type: mongoose.Schema.Types.ObjectId, ref: "Crop" }],
|
||||
finances: { type: mongoose.Schema.Types.ObjectId, ref: "Finance" },
|
||||
},
|
||||
{ timestamps: true }
|
||||
);
|
||||
|
||||
const Farm = mongoose.model("Farm", farmSchema);
|
||||
|
||||
module.exports = Farm;
|
||||
@@ -0,0 +1,21 @@
|
||||
const mongoose = require("mongoose");
|
||||
const financeSchema = new mongoose.Schema(
|
||||
{
|
||||
farm: { type: mongoose.Schema.Types.ObjectId, ref: "Farm", required: true },
|
||||
transactions: [
|
||||
{
|
||||
type: { type: String, enum: ["Expense", "Revenue"], required: true },
|
||||
amount: { type: Number, required: true },
|
||||
description: { type: String },
|
||||
date: { type: Date, default: Date.now },
|
||||
},
|
||||
],
|
||||
totalExpenses: { type: Number, default: 0 },
|
||||
totalRevenue: { type: Number, default: 0 },
|
||||
},
|
||||
{ timestamps: true }
|
||||
);
|
||||
|
||||
const Finance = mongoose.model("Finance", financeSchema);
|
||||
|
||||
module.exports = Finance;
|
||||
@@ -0,0 +1,31 @@
|
||||
const mongoose = require("mongoose");
|
||||
|
||||
const taskSchema = new mongoose.Schema(
|
||||
{
|
||||
farm: { type: mongoose.Schema.Types.ObjectId, ref: "Farm", required: true },
|
||||
crop: { type: mongoose.Schema.Types.ObjectId, ref: "Crop" },
|
||||
taskType: {
|
||||
type: String,
|
||||
enum: [
|
||||
"Sowing",
|
||||
"Watering",
|
||||
"Fertilization",
|
||||
"Pest Control",
|
||||
"Harvesting",
|
||||
],
|
||||
required: true,
|
||||
},
|
||||
description: { type: String },
|
||||
assignedDate: { type: Date, required: true, default: Date.now },
|
||||
status: {
|
||||
type: String,
|
||||
enum: ["Pending", "Completed"],
|
||||
default: "Pending",
|
||||
},
|
||||
},
|
||||
{ timestamps: true }
|
||||
);
|
||||
|
||||
const Task = mongoose.model("Task", taskSchema);
|
||||
|
||||
module.exports = Task;
|
||||
@@ -0,0 +1,85 @@
|
||||
const mongoose = require("mongoose");
|
||||
const bcrypt = require("bcrypt");
|
||||
const jwt = require("jsonwebtoken");
|
||||
const crypto = require("crypto");
|
||||
|
||||
const userSchema = new mongoose.Schema(
|
||||
{
|
||||
name: {
|
||||
type: String,
|
||||
required: [true, "Please Enter your name"],
|
||||
maxLength: [30, "Please Enter the valid name"],
|
||||
minLength: [2, "Name should have more than 5 characters"],
|
||||
},
|
||||
country: {
|
||||
type: String,
|
||||
},
|
||||
email: {
|
||||
type: String,
|
||||
required: true,
|
||||
unique: true,
|
||||
lowerCase: true,
|
||||
},
|
||||
password: {
|
||||
type: String,
|
||||
required: true,
|
||||
minLength: [6, "Password should have more than 6 characters"],
|
||||
},
|
||||
avatar: {
|
||||
type: String,
|
||||
default: "/images/profile.jpeg",
|
||||
},
|
||||
role: { type: String, enum: ["farmer", "admin"], default: "farmer" },
|
||||
farms: [{ type: mongoose.Schema.Types.ObjectId, ref: "Farm" }],
|
||||
|
||||
resetPasswordToken: String,
|
||||
resetPasswordExpiry: Date,
|
||||
},
|
||||
{
|
||||
timestamps: true,
|
||||
}
|
||||
);
|
||||
|
||||
userSchema.pre("save", async function (next) {
|
||||
if (!this.isModified("password")) return next();
|
||||
|
||||
this.password = await bcrypt.hash(this.password, 10);
|
||||
return next();
|
||||
});
|
||||
|
||||
userSchema.methods.isPasswordCorrect = async function (password) {
|
||||
return await bcrypt.compare(password, this.password);
|
||||
};
|
||||
|
||||
userSchema.methods.generateRefreshToken = async function () {
|
||||
return await jwt.sign(
|
||||
{
|
||||
_id: this._id,
|
||||
email: this.email,
|
||||
},
|
||||
process.env.REFRESH_TOKEN_SECRET
|
||||
// {
|
||||
// expiresIn: process.env.REFRESH_TOKEN_EXPIRY,
|
||||
// }
|
||||
);
|
||||
};
|
||||
|
||||
userSchema.methods.getResetPassword = async function () {
|
||||
// Generating token
|
||||
const resetToken = await crypto.randomBytes(20).toString("hex");
|
||||
|
||||
// Hashing and adding reset password token to userschema
|
||||
|
||||
this.resetPasswordToken = crypto
|
||||
.createHash("sha256")
|
||||
.update(resetToken)
|
||||
.digest("hex");
|
||||
|
||||
this.resetPasswordExpiry = Date.now() + 15 * 60 * 1000;
|
||||
|
||||
return resetToken;
|
||||
};
|
||||
|
||||
const User = mongoose.model("User", userSchema);
|
||||
|
||||
module.exports = User;
|
||||
@@ -0,0 +1,34 @@
|
||||
const express = require("express");
|
||||
const {
|
||||
createCrop,
|
||||
getCropsByFarm,
|
||||
getCropById,
|
||||
updateCrop,
|
||||
deleteCrop,
|
||||
updateHealthStatus,
|
||||
updateGrowthStage,
|
||||
cropHarvest,
|
||||
suggestNextCrop,
|
||||
suggestPesticides,
|
||||
suggestFertilizers,
|
||||
} = require("../Controllers/crop.controller.js");
|
||||
const { checkAuthenticated } = require("../Middlewares/authentication.js");
|
||||
const upload = require("../Middlewares/multer.js");
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
// Routes for crop management
|
||||
router.post("/", checkAuthenticated, upload.single("image"), createCrop); // Create a new crop
|
||||
router.get("/farm/:farmId", checkAuthenticated, getCropsByFarm); // Get all crops
|
||||
router.get("/:cropId", checkAuthenticated, getCropById); // Get a crop by ID
|
||||
router.put("/:cropId", checkAuthenticated, updateCrop); // Update crop details
|
||||
router.delete("/:cropId", checkAuthenticated, deleteCrop); // Delete a crop
|
||||
router.put("/health/:cropId", checkAuthenticated, updateHealthStatus);
|
||||
router.put("/growth/:cropId", checkAuthenticated, updateGrowthStage);
|
||||
|
||||
router.get("/harvest/:cropId", checkAuthenticated, cropHarvest);
|
||||
router.get("/nextCrop/:cropId", checkAuthenticated, suggestNextCrop);
|
||||
router.get("/pesticides/:cropId", checkAuthenticated, suggestPesticides);
|
||||
router.get("/fertilizers/:cropId", checkAuthenticated, suggestFertilizers);
|
||||
|
||||
module.exports = router;
|
||||
@@ -0,0 +1,19 @@
|
||||
const express = require("express");
|
||||
const {
|
||||
createFarm,
|
||||
getUserFarms,
|
||||
getFarmById,
|
||||
updateFarm,
|
||||
deleteFarm,
|
||||
} = require("../Controllers/farm.controller.js");
|
||||
const { checkAuthenticated } = require("../Middlewares/authentication.js");
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
router.post("/", checkAuthenticated, createFarm); // Create a new farm
|
||||
router.get("/", checkAuthenticated, getUserFarms); // Get all farms
|
||||
router.get("/:farmId", checkAuthenticated, getFarmById); // Get a farm by ID
|
||||
router.put("/:farmId", checkAuthenticated, updateFarm); // Update a farm
|
||||
router.delete("/:farmId", checkAuthenticated, deleteFarm); // Delete a farm
|
||||
|
||||
module.exports = router;
|
||||
@@ -0,0 +1,24 @@
|
||||
const express = require("express");
|
||||
const {
|
||||
addTransaction,
|
||||
createFinance,
|
||||
getFinanceByFarm,
|
||||
deleteTransaction,
|
||||
getTransactions,
|
||||
getFinancialSummary,
|
||||
} = require("../Controllers/finance.controller.js");
|
||||
const { checkAuthenticated } = require("../Middlewares/authentication.js");
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
// Routes for finance management
|
||||
router.post("/", checkAuthenticated, createFinance); // Create a new finance record
|
||||
router.get("/:farmId", checkAuthenticated, getFinanceByFarm); // Get all finance records
|
||||
router.get("/transactions/:financeId", checkAuthenticated, getTransactions); // Get a finance record by ID
|
||||
router.get("/summary/:financeId", checkAuthenticated, getFinancialSummary); //
|
||||
router.delete("/:financeId", checkAuthenticated, deleteTransaction); // Delete a finance record
|
||||
|
||||
// Add transactions (Expense/Revenue) to a finance record
|
||||
router.post("/:financeId/transaction", checkAuthenticated, addTransaction);
|
||||
|
||||
module.exports = router;
|
||||
@@ -0,0 +1,25 @@
|
||||
const { checkAuthenticated } = require("../Middlewares/authentication.js");
|
||||
|
||||
const express = require("express");
|
||||
const {
|
||||
createTask,
|
||||
getTasksByFarm,
|
||||
getTaskById,
|
||||
updateTask,
|
||||
deleteTask,
|
||||
updateTaskStatus,
|
||||
} = require("../Controllers/task.controller.js");
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
// Routes for task management
|
||||
router.post("/", checkAuthenticated, createTask); // Create a new task
|
||||
router.get("/farm/:farmId", checkAuthenticated, getTasksByFarm); // Get all tasks for a specific farm
|
||||
router.get("/:taskId", checkAuthenticated, getTaskById); // Get a task by ID
|
||||
router.put("/:taskId", checkAuthenticated, updateTask); // Update task details
|
||||
router.delete("/:taskId", checkAuthenticated, deleteTask); // Delete a task
|
||||
|
||||
// Update task status (Pending → Completed)
|
||||
router.patch("/:taskId/status", checkAuthenticated, updateTaskStatus);
|
||||
|
||||
module.exports = router;
|
||||
@@ -0,0 +1,54 @@
|
||||
const express = require("express");
|
||||
const {
|
||||
registerUser,
|
||||
loginUser,
|
||||
logoutUser,
|
||||
updateUserDetails,
|
||||
forgetPassword,
|
||||
resetPassword,
|
||||
getUserDetails,
|
||||
updatePassword,
|
||||
updatePersonalDetails,
|
||||
DeleteUser,
|
||||
updateUserRole,
|
||||
intializeUser,
|
||||
updateAvatar,
|
||||
} = require("../Controllers/user.controller.js");
|
||||
|
||||
const { checkAuthenticated } = require("../Middlewares/authentication.js");
|
||||
|
||||
const upload = require("../Middlewares/multer.js");
|
||||
|
||||
const { loginLimiter } = require("../Middlewares/rateLimiter");
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
router.route("/register").post(registerUser);
|
||||
|
||||
router.route("/login").post(loginLimiter, loginUser);
|
||||
|
||||
router.route("/password/forgot").post(forgetPassword);
|
||||
|
||||
router.route("/password/reset/:token").put(resetPassword);
|
||||
|
||||
router.route("/logout").get(logoutUser);
|
||||
|
||||
router.route("/update/:id").put(updateUserDetails);
|
||||
|
||||
router.route("/me").get(checkAuthenticated, getUserDetails);
|
||||
|
||||
router.route("/getuser").get(intializeUser);
|
||||
|
||||
router.route("/password/update").put(updatePassword);
|
||||
|
||||
router.route("/me/update").put(updatePersonalDetails);
|
||||
|
||||
router.route("/user/delete/:id").delete(DeleteUser);
|
||||
|
||||
router.route("/user/updateRole/:id").put(updateUserRole);
|
||||
|
||||
router
|
||||
.route("/user/avatar")
|
||||
.put(checkAuthenticated, upload.single("avatar"), updateAvatar);
|
||||
|
||||
module.exports = router;
|
||||
@@ -0,0 +1,23 @@
|
||||
const cloudinary = require("cloudinary").v2;
|
||||
const fs = require("fs");
|
||||
|
||||
const uploadOnCloudinary = async (localFilePath) => {
|
||||
try {
|
||||
if (!localFilePath) return null;
|
||||
|
||||
const responce = await cloudinary.uploader.upload(localFilePath, {
|
||||
resource_type: "auto",
|
||||
});
|
||||
|
||||
// console.log("File is uploaded successfully");
|
||||
fs.unlinkSync(localFilePath, () => {
|
||||
console.log("file removed successfully");
|
||||
});
|
||||
return responce.url;
|
||||
} catch (error) {
|
||||
fs.unlinkSync(localFilePath);
|
||||
console.log("file removed successfully");
|
||||
return null;
|
||||
}
|
||||
};
|
||||
module.exports = { uploadOnCloudinary };
|
||||
@@ -0,0 +1,100 @@
|
||||
const {
|
||||
GoogleGenerativeAI,
|
||||
HarmCategory,
|
||||
HarmBlockThreshold,
|
||||
} = require("@google/generative-ai");
|
||||
|
||||
const apiKey = "AIzaSyDsXug23r4umgcJpj77KeqNyYW0hQnYDgg";
|
||||
const genAI = new GoogleGenerativeAI(apiKey);
|
||||
|
||||
const model = genAI.getGenerativeModel({
|
||||
model: "gemini-2.0-flash",
|
||||
});
|
||||
|
||||
const generationConfig = {
|
||||
temperature: 1,
|
||||
topP: 0.95,
|
||||
topK: 40,
|
||||
maxOutputTokens: 8192,
|
||||
responseMimeType: "text/plain",
|
||||
};
|
||||
|
||||
async function run(message) {
|
||||
const chatSession = model.startChat({
|
||||
generationConfig,
|
||||
history: [
|
||||
{
|
||||
role: "user",
|
||||
parts: [
|
||||
{
|
||||
text: "AI Guidelines:\n- The AI must only provide answers related to farming, including crops, sowing, irrigation, harvesting, fertilizers, pesticides, soil health, and climate conditions. \n- The AI must not answer anything outside farming. \n- If asked an unrelated question, the AI must not respond. \n- If the query is unclear, the AI must default to farming-related guidance. \n- The AI must provide only one-line responses without extra details. \n- No harmful, hurtful, or controversial responses. \n- No advice on illegal, unsafe, or unethical farming practices.\n",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
role: "model",
|
||||
parts: [
|
||||
{
|
||||
text: "Understood. I will only provide one-line responses directly related to farming.\n",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
role: "user",
|
||||
parts: [
|
||||
{
|
||||
text: "you can also receive json or javascript object as an input ",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
role: "model",
|
||||
parts: [
|
||||
{
|
||||
text: "Understood. I will process the JSON or JavaScript object only if it pertains to farming and respond with a single line.\n",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
role: "user",
|
||||
parts: [
|
||||
{
|
||||
text: "Answer everything which falls under the farming and agriculture even if the prompt does not include any farm or agriculture word in it",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
role: "model",
|
||||
parts: [
|
||||
{
|
||||
text: "Understood. I will assume all queries are related to farming and provide a one-line farming-related response.\n",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
role: "user",
|
||||
parts: [
|
||||
{
|
||||
text: "Give me a percise answer no matter what, dont give useless or incomplete answer \n",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
role: "model",
|
||||
parts: [
|
||||
{
|
||||
text: "Understood. I will strive to provide precise and complete one-line farming-related answers.\n",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const result = await chatSession.sendMessage(message);
|
||||
console.log(result.response.text());
|
||||
return result.response.text();
|
||||
}
|
||||
|
||||
module.exports = { run };
|
||||
|
||||
// run("Sowing start and harvesting end which crop ?");
|
||||
@@ -0,0 +1,25 @@
|
||||
const nodemailer = require("nodemailer");
|
||||
|
||||
const sendEmail = async (options) => {
|
||||
const transporter = await nodemailer.createTransport({
|
||||
service: "gmail",
|
||||
host: process.env.HOST,
|
||||
port: process.env.EMAIL_PORT,
|
||||
secure: false,
|
||||
auth: {
|
||||
user: process.env.SMPT_MAIL, // senders email
|
||||
pass: process.env.SMPT_PASSWORD, // app passoword created for your app by using step :: google account> manage your google account >security>enable two step verification > search app password and create password for your app
|
||||
},
|
||||
});
|
||||
|
||||
const mailOptions = {
|
||||
from: "",
|
||||
to: options.email,
|
||||
subject: options.subject,
|
||||
text: options.message,
|
||||
};
|
||||
|
||||
await transporter.sendMail(mailOptions);
|
||||
};
|
||||
|
||||
module.exports = sendEmail;
|
||||
@@ -0,0 +1,57 @@
|
||||
const express = require("express");
|
||||
const cors = require("cors");
|
||||
const cookieParser = require("cookie-parser");
|
||||
const helmet = require("helmet");
|
||||
|
||||
const userRoute = require("./Routes/user.routes.js");
|
||||
const farmRoute = require("./Routes/farm.routes.js");
|
||||
const cropRoute = require("./Routes/crop.routes.js");
|
||||
const financeRoute = require("./Routes/finance.routes.js");
|
||||
const taskRoute = require("./Routes/task.routes.js");
|
||||
const { checkAuthenticated } = require("./Middlewares/authentication.js");
|
||||
const dotenv = require("dotenv");
|
||||
const { run } = require("./Utils/model.js");
|
||||
|
||||
dotenv.config({
|
||||
path: "./.env",
|
||||
});
|
||||
|
||||
const app = express();
|
||||
|
||||
app.use(helmet()); // Secure headers
|
||||
|
||||
const corsOptions = {
|
||||
origin: process.env.FRONTEND_URI,
|
||||
methods: "GET,HEAD,PUT,PATCH,POST,DELETE",
|
||||
credentials: true,
|
||||
};
|
||||
|
||||
app.use(cors(corsOptions));
|
||||
app.use(express.json({ limit: "16kb" }));
|
||||
app.use(express.urlencoded({ extended: true, limit: "16kb" }));
|
||||
app.use(express.static("public"));
|
||||
app.use(cookieParser());
|
||||
|
||||
app.get("/", (req, res) => {
|
||||
return res.send("Server is running...");
|
||||
});
|
||||
|
||||
app.use("/api/v1", userRoute);
|
||||
|
||||
app.use("/api/v1/farm", farmRoute);
|
||||
|
||||
app.use("/api/v1/crop", cropRoute);
|
||||
|
||||
app.use("/api/v1/finance", financeRoute);
|
||||
|
||||
app.use("/api/v1/task", taskRoute);
|
||||
|
||||
// Redirect HTTP to HTTPS (works behind proxy)
|
||||
app.use((req, res, next) => {
|
||||
if (req.headers["x-forwarded-proto"] !== "https" && process.env.NODE_ENV === "production") {
|
||||
return res.redirect(`https://${req.headers.host}${req.url}`);
|
||||
}
|
||||
next();
|
||||
});
|
||||
|
||||
module.exports = app;
|
||||
@@ -0,0 +1,24 @@
|
||||
const server = require("./app.js");
|
||||
const dotenv = require("dotenv");
|
||||
const cloudinary = require("cloudinary").v2;
|
||||
const DB_connect = require("./Database/DB_connect.js");
|
||||
const app = require("./app.js");
|
||||
|
||||
// dotenv Configuration
|
||||
dotenv.config({
|
||||
path: "./.env",
|
||||
});
|
||||
|
||||
cloudinary.config({
|
||||
cloud_name: process.env.CLOUDINARY_CLOUD_NAME,
|
||||
api_key: process.env.CLOUDINARY_API_KEY,
|
||||
api_secret: process.env.CLOUDINARY_API_SECRET,
|
||||
});
|
||||
|
||||
DB_connect();
|
||||
|
||||
// Listening the port
|
||||
app.listen(process.env.PORT, () => {
|
||||
console.log("Server is Running on ", process.env.PORT);
|
||||
console.log("Frontend URI : ", process.env.FRONTEND_URI);
|
||||
});
|
||||
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"name": "backend",
|
||||
"version": "1.1.0",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"dev": "nodemon index.js"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"description": "",
|
||||
"dependencies": {
|
||||
"@google/generative-ai": "^0.24.1",
|
||||
"axios": "^1.6.8",
|
||||
"bcrypt": "^6.0.0",
|
||||
"cloudinary": "^2.7.0",
|
||||
"cookie-parser": "^1.4.7",
|
||||
"cors": "^2.8.5",
|
||||
"dotenv": "^16.5.0",
|
||||
"express": "^5.1.0",
|
||||
"express-rate-limit": "^6.7.0",
|
||||
"helmet": "^7.0.0",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"mongoose": "^8.16.0",
|
||||
"multer": "^2.0.1",
|
||||
"nodemailer": "^7.0.3",
|
||||
"sha1": "^1.1.1",
|
||||
"socket.io": "^4.8.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"nodemon": "^3.1.10"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
dist/
|
||||
Dockerfile
|
||||
node_modules/
|
||||
package-lock.json
|
||||
README.md
|
||||
vercel.json
|
||||
.dockerignore
|
||||
.gitignore
|
||||
@@ -0,0 +1 @@
|
||||
VITE_API_URL = ${BACKEND_URI}
|
||||
@@ -0,0 +1,25 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
package-lock.json
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
@@ -0,0 +1,142 @@
|
||||
{
|
||||
"hash": "2bafedf9",
|
||||
"configHash": "7e00041b",
|
||||
"lockfileHash": "a954eb91",
|
||||
"browserHash": "c3bc7b53",
|
||||
"optimized": {
|
||||
"react": {
|
||||
"src": "../../node_modules/react/index.js",
|
||||
"file": "react.js",
|
||||
"fileHash": "902ee93a",
|
||||
"needsInterop": true
|
||||
},
|
||||
"react-dom/client": {
|
||||
"src": "../../node_modules/react-dom/client.js",
|
||||
"file": "react-dom_client.js",
|
||||
"fileHash": "cfbeda6d",
|
||||
"needsInterop": true
|
||||
},
|
||||
"react-router-dom": {
|
||||
"src": "../../node_modules/react-router-dom/dist/index.js",
|
||||
"file": "react-router-dom.js",
|
||||
"fileHash": "216229fd",
|
||||
"needsInterop": false
|
||||
},
|
||||
"react-redux": {
|
||||
"src": "../../node_modules/react-redux/dist/react-redux.mjs",
|
||||
"file": "react-redux.js",
|
||||
"fileHash": "b9f679da",
|
||||
"needsInterop": false
|
||||
},
|
||||
"@reduxjs/toolkit": {
|
||||
"src": "../../node_modules/@reduxjs/toolkit/dist/redux-toolkit.modern.mjs",
|
||||
"file": "@reduxjs_toolkit.js",
|
||||
"fileHash": "13da79f3",
|
||||
"needsInterop": false
|
||||
},
|
||||
"socket.io-client": {
|
||||
"src": "../../node_modules/socket.io-client/build/esm/index.js",
|
||||
"file": "socket__io-client.js",
|
||||
"fileHash": "ab2fcae8",
|
||||
"needsInterop": false
|
||||
},
|
||||
"react-icons/io": {
|
||||
"src": "../../node_modules/react-icons/io/index.mjs",
|
||||
"file": "react-icons_io.js",
|
||||
"fileHash": "fb80952c",
|
||||
"needsInterop": false
|
||||
},
|
||||
"react-icons/fa6": {
|
||||
"src": "../../node_modules/react-icons/fa6/index.mjs",
|
||||
"file": "react-icons_fa6.js",
|
||||
"fileHash": "da456fb8",
|
||||
"needsInterop": false
|
||||
},
|
||||
"react-icons/ri": {
|
||||
"src": "../../node_modules/react-icons/ri/index.mjs",
|
||||
"file": "react-icons_ri.js",
|
||||
"fileHash": "6e092d47",
|
||||
"needsInterop": false
|
||||
},
|
||||
"react-icons/bs": {
|
||||
"src": "../../node_modules/react-icons/bs/index.mjs",
|
||||
"file": "react-icons_bs.js",
|
||||
"fileHash": "c070a6ed",
|
||||
"needsInterop": false
|
||||
},
|
||||
"react-icons/fa": {
|
||||
"src": "../../node_modules/react-icons/fa/index.mjs",
|
||||
"file": "react-icons_fa.js",
|
||||
"fileHash": "aa81607f",
|
||||
"needsInterop": false
|
||||
},
|
||||
"react-icons/md": {
|
||||
"src": "../../node_modules/react-icons/md/index.mjs",
|
||||
"file": "react-icons_md.js",
|
||||
"fileHash": "60d442c6",
|
||||
"needsInterop": false
|
||||
},
|
||||
"react-icons/io5": {
|
||||
"src": "../../node_modules/react-icons/io5/index.mjs",
|
||||
"file": "react-icons_io5.js",
|
||||
"fileHash": "5d65fe60",
|
||||
"needsInterop": false
|
||||
},
|
||||
"framer-motion": {
|
||||
"src": "../../node_modules/framer-motion/dist/es/index.mjs",
|
||||
"file": "framer-motion.js",
|
||||
"fileHash": "05223771",
|
||||
"needsInterop": false
|
||||
},
|
||||
"react-intersection-observer": {
|
||||
"src": "../../node_modules/react-intersection-observer/dist/index.mjs",
|
||||
"file": "react-intersection-observer.js",
|
||||
"fileHash": "575187c6",
|
||||
"needsInterop": false
|
||||
},
|
||||
"react-chartjs-2": {
|
||||
"src": "../../node_modules/react-chartjs-2/dist/index.js",
|
||||
"file": "react-chartjs-2.js",
|
||||
"fileHash": "07d12b80",
|
||||
"needsInterop": false
|
||||
},
|
||||
"chart.js": {
|
||||
"src": "../../node_modules/chart.js/dist/chart.js",
|
||||
"file": "chart__js.js",
|
||||
"fileHash": "39dbe037",
|
||||
"needsInterop": false
|
||||
},
|
||||
"react-apexcharts": {
|
||||
"src": "../../node_modules/react-apexcharts/dist/react-apexcharts.min.js",
|
||||
"file": "react-apexcharts.js",
|
||||
"fileHash": "2bdb7b94",
|
||||
"needsInterop": true
|
||||
},
|
||||
"react-typewriter-effect": {
|
||||
"src": "../../node_modules/react-typewriter-effect/dist/index.js",
|
||||
"file": "react-typewriter-effect.js",
|
||||
"fileHash": "574603a4",
|
||||
"needsInterop": true
|
||||
}
|
||||
},
|
||||
"chunks": {
|
||||
"chunk-UU7TO5PY": {
|
||||
"file": "chunk-UU7TO5PY.js"
|
||||
},
|
||||
"chunk-2YIK36WJ": {
|
||||
"file": "chunk-2YIK36WJ.js"
|
||||
},
|
||||
"chunk-UHINIFCJ": {
|
||||
"file": "chunk-UHINIFCJ.js"
|
||||
},
|
||||
"chunk-SD42HLFO": {
|
||||
"file": "chunk-SD42HLFO.js"
|
||||
},
|
||||
"chunk-W4EHDCLL": {
|
||||
"file": "chunk-W4EHDCLL.js"
|
||||
},
|
||||
"chunk-EWTE5DHJ": {
|
||||
"file": "chunk-EWTE5DHJ.js"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
import {
|
||||
Animation,
|
||||
Animations,
|
||||
ArcElement,
|
||||
BarController,
|
||||
BarElement,
|
||||
BasePlatform,
|
||||
BasicPlatform,
|
||||
BubbleController,
|
||||
CategoryScale,
|
||||
Chart,
|
||||
DatasetController,
|
||||
DomPlatform,
|
||||
DoughnutController,
|
||||
Element,
|
||||
Interaction,
|
||||
LineController,
|
||||
LineElement,
|
||||
LinearScale,
|
||||
LogarithmicScale,
|
||||
PieController,
|
||||
PointElement,
|
||||
PolarAreaController,
|
||||
RadarController,
|
||||
RadialLinearScale,
|
||||
Scale,
|
||||
ScatterController,
|
||||
Ticks,
|
||||
TimeScale,
|
||||
TimeSeriesScale,
|
||||
_detectPlatform,
|
||||
adapters,
|
||||
animator,
|
||||
controllers,
|
||||
defaults,
|
||||
elements,
|
||||
index,
|
||||
layouts,
|
||||
plugin_colors,
|
||||
plugin_decimation,
|
||||
plugin_legend,
|
||||
plugin_subtitle,
|
||||
plugin_title,
|
||||
plugin_tooltip,
|
||||
plugins,
|
||||
registerables,
|
||||
registry,
|
||||
scales
|
||||
} from "./chunk-2YIK36WJ.js";
|
||||
import "./chunk-EWTE5DHJ.js";
|
||||
export {
|
||||
Animation,
|
||||
Animations,
|
||||
ArcElement,
|
||||
BarController,
|
||||
BarElement,
|
||||
BasePlatform,
|
||||
BasicPlatform,
|
||||
BubbleController,
|
||||
CategoryScale,
|
||||
Chart,
|
||||
plugin_colors as Colors,
|
||||
DatasetController,
|
||||
plugin_decimation as Decimation,
|
||||
DomPlatform,
|
||||
DoughnutController,
|
||||
Element,
|
||||
index as Filler,
|
||||
Interaction,
|
||||
plugin_legend as Legend,
|
||||
LineController,
|
||||
LineElement,
|
||||
LinearScale,
|
||||
LogarithmicScale,
|
||||
PieController,
|
||||
PointElement,
|
||||
PolarAreaController,
|
||||
RadarController,
|
||||
RadialLinearScale,
|
||||
Scale,
|
||||
ScatterController,
|
||||
plugin_subtitle as SubTitle,
|
||||
Ticks,
|
||||
TimeScale,
|
||||
TimeSeriesScale,
|
||||
plugin_title as Title,
|
||||
plugin_tooltip as Tooltip,
|
||||
adapters as _adapters,
|
||||
_detectPlatform,
|
||||
animator,
|
||||
controllers,
|
||||
defaults,
|
||||
elements,
|
||||
layouts,
|
||||
plugins,
|
||||
registerables,
|
||||
registry,
|
||||
scales
|
||||
};
|
||||
//# sourceMappingURL=chart__js.js.map
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"version": 3,
|
||||
"sources": [],
|
||||
"sourcesContent": [],
|
||||
"mappings": "",
|
||||
"names": []
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
var __create = Object.create;
|
||||
var __defProp = Object.defineProperty;
|
||||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
||||
var __getOwnPropNames = Object.getOwnPropertyNames;
|
||||
var __getProtoOf = Object.getPrototypeOf;
|
||||
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
||||
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
||||
var __esm = (fn, res) => function __init() {
|
||||
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
||||
};
|
||||
var __commonJS = (cb, mod) => function __require() {
|
||||
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
||||
};
|
||||
var __export = (target, all) => {
|
||||
for (var name in all)
|
||||
__defProp(target, name, { get: all[name], enumerable: true });
|
||||
};
|
||||
var __copyProps = (to, from, except, desc) => {
|
||||
if (from && typeof from === "object" || typeof from === "function") {
|
||||
for (let key of __getOwnPropNames(from))
|
||||
if (!__hasOwnProp.call(to, key) && key !== except)
|
||||
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
||||
}
|
||||
return to;
|
||||
};
|
||||
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
||||
// If the importer is in node compatibility mode or this is not an ESM
|
||||
// file that has been converted to a CommonJS file using a Babel-
|
||||
// compatible transform (i.e. "__esModule" has not been set), then set
|
||||
// "default" to the CommonJS "module.exports" for node compatibility.
|
||||
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
||||
mod
|
||||
));
|
||||
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
||||
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
||||
|
||||
export {
|
||||
__esm,
|
||||
__commonJS,
|
||||
__export,
|
||||
__toESM,
|
||||
__toCommonJS,
|
||||
__publicField
|
||||
};
|
||||
//# sourceMappingURL=chunk-EWTE5DHJ.js.map
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"version": 3,
|
||||
"sources": [],
|
||||
"sourcesContent": [],
|
||||
"mappings": "",
|
||||
"names": []
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
import {
|
||||
require_react
|
||||
} from "./chunk-W4EHDCLL.js";
|
||||
import {
|
||||
__toESM
|
||||
} from "./chunk-EWTE5DHJ.js";
|
||||
|
||||
// node_modules/react-icons/lib/iconBase.mjs
|
||||
var import_react2 = __toESM(require_react(), 1);
|
||||
|
||||
// node_modules/react-icons/lib/iconContext.mjs
|
||||
var import_react = __toESM(require_react(), 1);
|
||||
var DefaultContext = {
|
||||
color: void 0,
|
||||
size: void 0,
|
||||
className: void 0,
|
||||
style: void 0,
|
||||
attr: void 0
|
||||
};
|
||||
var IconContext = import_react.default.createContext && import_react.default.createContext(DefaultContext);
|
||||
|
||||
// node_modules/react-icons/lib/iconBase.mjs
|
||||
var _excluded = ["attr", "size", "title"];
|
||||
function _objectWithoutProperties(source, excluded) {
|
||||
if (source == null) return {};
|
||||
var target = _objectWithoutPropertiesLoose(source, excluded);
|
||||
var key, i;
|
||||
if (Object.getOwnPropertySymbols) {
|
||||
var sourceSymbolKeys = Object.getOwnPropertySymbols(source);
|
||||
for (i = 0; i < sourceSymbolKeys.length; i++) {
|
||||
key = sourceSymbolKeys[i];
|
||||
if (excluded.indexOf(key) >= 0) continue;
|
||||
if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue;
|
||||
target[key] = source[key];
|
||||
}
|
||||
}
|
||||
return target;
|
||||
}
|
||||
function _objectWithoutPropertiesLoose(source, excluded) {
|
||||
if (source == null) return {};
|
||||
var target = {};
|
||||
for (var key in source) {
|
||||
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
||||
if (excluded.indexOf(key) >= 0) continue;
|
||||
target[key] = source[key];
|
||||
}
|
||||
}
|
||||
return target;
|
||||
}
|
||||
function _extends() {
|
||||
_extends = Object.assign ? Object.assign.bind() : function(target) {
|
||||
for (var i = 1; i < arguments.length; i++) {
|
||||
var source = arguments[i];
|
||||
for (var key in source) {
|
||||
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
||||
target[key] = source[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
return target;
|
||||
};
|
||||
return _extends.apply(this, arguments);
|
||||
}
|
||||
function ownKeys(e, r) {
|
||||
var t = Object.keys(e);
|
||||
if (Object.getOwnPropertySymbols) {
|
||||
var o = Object.getOwnPropertySymbols(e);
|
||||
r && (o = o.filter(function(r2) {
|
||||
return Object.getOwnPropertyDescriptor(e, r2).enumerable;
|
||||
})), t.push.apply(t, o);
|
||||
}
|
||||
return t;
|
||||
}
|
||||
function _objectSpread(e) {
|
||||
for (var r = 1; r < arguments.length; r++) {
|
||||
var t = null != arguments[r] ? arguments[r] : {};
|
||||
r % 2 ? ownKeys(Object(t), true).forEach(function(r2) {
|
||||
_defineProperty(e, r2, t[r2]);
|
||||
}) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function(r2) {
|
||||
Object.defineProperty(e, r2, Object.getOwnPropertyDescriptor(t, r2));
|
||||
});
|
||||
}
|
||||
return e;
|
||||
}
|
||||
function _defineProperty(obj, key, value) {
|
||||
key = _toPropertyKey(key);
|
||||
if (key in obj) {
|
||||
Object.defineProperty(obj, key, { value, enumerable: true, configurable: true, writable: true });
|
||||
} else {
|
||||
obj[key] = value;
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
function _toPropertyKey(t) {
|
||||
var i = _toPrimitive(t, "string");
|
||||
return "symbol" == typeof i ? i : i + "";
|
||||
}
|
||||
function _toPrimitive(t, r) {
|
||||
if ("object" != typeof t || !t) return t;
|
||||
var e = t[Symbol.toPrimitive];
|
||||
if (void 0 !== e) {
|
||||
var i = e.call(t, r || "default");
|
||||
if ("object" != typeof i) return i;
|
||||
throw new TypeError("@@toPrimitive must return a primitive value.");
|
||||
}
|
||||
return ("string" === r ? String : Number)(t);
|
||||
}
|
||||
function Tree2Element(tree) {
|
||||
return tree && tree.map((node, i) => import_react2.default.createElement(node.tag, _objectSpread({
|
||||
key: i
|
||||
}, node.attr), Tree2Element(node.child)));
|
||||
}
|
||||
function GenIcon(data) {
|
||||
return (props) => import_react2.default.createElement(IconBase, _extends({
|
||||
attr: _objectSpread({}, data.attr)
|
||||
}, props), Tree2Element(data.child));
|
||||
}
|
||||
function IconBase(props) {
|
||||
var elem = (conf) => {
|
||||
var {
|
||||
attr,
|
||||
size,
|
||||
title
|
||||
} = props, svgProps = _objectWithoutProperties(props, _excluded);
|
||||
var computedSize = size || conf.size || "1em";
|
||||
var className;
|
||||
if (conf.className) className = conf.className;
|
||||
if (props.className) className = (className ? className + " " : "") + props.className;
|
||||
return import_react2.default.createElement("svg", _extends({
|
||||
stroke: "currentColor",
|
||||
fill: "currentColor",
|
||||
strokeWidth: "0"
|
||||
}, conf.attr, attr, svgProps, {
|
||||
className,
|
||||
style: _objectSpread(_objectSpread({
|
||||
color: props.color || conf.color
|
||||
}, conf.style), props.style),
|
||||
height: computedSize,
|
||||
width: computedSize,
|
||||
xmlns: "http://www.w3.org/2000/svg"
|
||||
}), title && import_react2.default.createElement("title", null, title), props.children);
|
||||
};
|
||||
return IconContext !== void 0 ? import_react2.default.createElement(IconContext.Consumer, null, (conf) => elem(conf)) : elem(DefaultContext);
|
||||
}
|
||||
|
||||
export {
|
||||
GenIcon
|
||||
};
|
||||
//# sourceMappingURL=chunk-SD42HLFO.js.map
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"version": 3,
|
||||
"sources": ["../../node_modules/react-icons/lib/iconBase.mjs", "../../node_modules/react-icons/lib/iconContext.mjs"],
|
||||
"sourcesContent": ["var _excluded = [\"attr\", \"size\", \"title\"];\nfunction _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }\nfunction _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } } return target; }\nfunction _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }\nfunction ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }\nfunction _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }\nfunction _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }\nfunction _toPropertyKey(t) { var i = _toPrimitive(t, \"string\"); return \"symbol\" == typeof i ? i : i + \"\"; }\nfunction _toPrimitive(t, r) { if (\"object\" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || \"default\"); if (\"object\" != typeof i) return i; throw new TypeError(\"@@toPrimitive must return a primitive value.\"); } return (\"string\" === r ? String : Number)(t); }\nimport React from \"react\";\nimport { IconContext, DefaultContext } from \"./iconContext.mjs\";\nfunction Tree2Element(tree) {\n return tree && tree.map((node, i) => /*#__PURE__*/React.createElement(node.tag, _objectSpread({\n key: i\n }, node.attr), Tree2Element(node.child)));\n}\nexport function GenIcon(data) {\n return props => /*#__PURE__*/React.createElement(IconBase, _extends({\n attr: _objectSpread({}, data.attr)\n }, props), Tree2Element(data.child));\n}\nexport function IconBase(props) {\n var elem = conf => {\n var {\n attr,\n size,\n title\n } = props,\n svgProps = _objectWithoutProperties(props, _excluded);\n var computedSize = size || conf.size || \"1em\";\n var className;\n if (conf.className) className = conf.className;\n if (props.className) className = (className ? className + \" \" : \"\") + props.className;\n return /*#__PURE__*/React.createElement(\"svg\", _extends({\n stroke: \"currentColor\",\n fill: \"currentColor\",\n strokeWidth: \"0\"\n }, conf.attr, attr, svgProps, {\n className: className,\n style: _objectSpread(_objectSpread({\n color: props.color || conf.color\n }, conf.style), props.style),\n height: computedSize,\n width: computedSize,\n xmlns: \"http://www.w3.org/2000/svg\"\n }), title && /*#__PURE__*/React.createElement(\"title\", null, title), props.children);\n };\n return IconContext !== undefined ? /*#__PURE__*/React.createElement(IconContext.Consumer, null, conf => elem(conf)) : elem(DefaultContext);\n}", "import React from \"react\";\nexport var DefaultContext = {\n color: undefined,\n size: undefined,\n className: undefined,\n style: undefined,\n attr: undefined\n};\nexport var IconContext = React.createContext && /*#__PURE__*/React.createContext(DefaultContext);"],
|
||||
"mappings": ";;;;;;;;AASA,IAAAA,gBAAkB;;;ACTlB,mBAAkB;AACX,IAAI,iBAAiB;AAAA,EAC1B,OAAO;AAAA,EACP,MAAM;AAAA,EACN,WAAW;AAAA,EACX,OAAO;AAAA,EACP,MAAM;AACR;AACO,IAAI,cAAc,aAAAC,QAAM,iBAA8B,aAAAA,QAAM,cAAc,cAAc;;;ADR/F,IAAI,YAAY,CAAC,QAAQ,QAAQ,OAAO;AACxC,SAAS,yBAAyB,QAAQ,UAAU;AAAE,MAAI,UAAU,KAAM,QAAO,CAAC;AAAG,MAAI,SAAS,8BAA8B,QAAQ,QAAQ;AAAG,MAAI,KAAK;AAAG,MAAI,OAAO,uBAAuB;AAAE,QAAI,mBAAmB,OAAO,sBAAsB,MAAM;AAAG,SAAK,IAAI,GAAG,IAAI,iBAAiB,QAAQ,KAAK;AAAE,YAAM,iBAAiB,CAAC;AAAG,UAAI,SAAS,QAAQ,GAAG,KAAK,EAAG;AAAU,UAAI,CAAC,OAAO,UAAU,qBAAqB,KAAK,QAAQ,GAAG,EAAG;AAAU,aAAO,GAAG,IAAI,OAAO,GAAG;AAAA,IAAG;AAAA,EAAE;AAAE,SAAO;AAAQ;AAC3e,SAAS,8BAA8B,QAAQ,UAAU;AAAE,MAAI,UAAU,KAAM,QAAO,CAAC;AAAG,MAAI,SAAS,CAAC;AAAG,WAAS,OAAO,QAAQ;AAAE,QAAI,OAAO,UAAU,eAAe,KAAK,QAAQ,GAAG,GAAG;AAAE,UAAI,SAAS,QAAQ,GAAG,KAAK,EAAG;AAAU,aAAO,GAAG,IAAI,OAAO,GAAG;AAAA,IAAG;AAAA,EAAE;AAAE,SAAO;AAAQ;AACtR,SAAS,WAAW;AAAE,aAAW,OAAO,SAAS,OAAO,OAAO,KAAK,IAAI,SAAU,QAAQ;AAAE,aAAS,IAAI,GAAG,IAAI,UAAU,QAAQ,KAAK;AAAE,UAAI,SAAS,UAAU,CAAC;AAAG,eAAS,OAAO,QAAQ;AAAE,YAAI,OAAO,UAAU,eAAe,KAAK,QAAQ,GAAG,GAAG;AAAE,iBAAO,GAAG,IAAI,OAAO,GAAG;AAAA,QAAG;AAAA,MAAE;AAAA,IAAE;AAAE,WAAO;AAAA,EAAQ;AAAG,SAAO,SAAS,MAAM,MAAM,SAAS;AAAG;AAClV,SAAS,QAAQ,GAAG,GAAG;AAAE,MAAI,IAAI,OAAO,KAAK,CAAC;AAAG,MAAI,OAAO,uBAAuB;AAAE,QAAI,IAAI,OAAO,sBAAsB,CAAC;AAAG,UAAM,IAAI,EAAE,OAAO,SAAUC,IAAG;AAAE,aAAO,OAAO,yBAAyB,GAAGA,EAAC,EAAE;AAAA,IAAY,CAAC,IAAI,EAAE,KAAK,MAAM,GAAG,CAAC;AAAA,EAAG;AAAE,SAAO;AAAG;AAC9P,SAAS,cAAc,GAAG;AAAE,WAAS,IAAI,GAAG,IAAI,UAAU,QAAQ,KAAK;AAAE,QAAI,IAAI,QAAQ,UAAU,CAAC,IAAI,UAAU,CAAC,IAAI,CAAC;AAAG,QAAI,IAAI,QAAQ,OAAO,CAAC,GAAG,IAAE,EAAE,QAAQ,SAAUA,IAAG;AAAE,sBAAgB,GAAGA,IAAG,EAAEA,EAAC,CAAC;AAAA,IAAG,CAAC,IAAI,OAAO,4BAA4B,OAAO,iBAAiB,GAAG,OAAO,0BAA0B,CAAC,CAAC,IAAI,QAAQ,OAAO,CAAC,CAAC,EAAE,QAAQ,SAAUA,IAAG;AAAE,aAAO,eAAe,GAAGA,IAAG,OAAO,yBAAyB,GAAGA,EAAC,CAAC;AAAA,IAAG,CAAC;AAAA,EAAG;AAAE,SAAO;AAAG;AACtb,SAAS,gBAAgB,KAAK,KAAK,OAAO;AAAE,QAAM,eAAe,GAAG;AAAG,MAAI,OAAO,KAAK;AAAE,WAAO,eAAe,KAAK,KAAK,EAAE,OAAc,YAAY,MAAM,cAAc,MAAM,UAAU,KAAK,CAAC;AAAA,EAAG,OAAO;AAAE,QAAI,GAAG,IAAI;AAAA,EAAO;AAAE,SAAO;AAAK;AAC3O,SAAS,eAAe,GAAG;AAAE,MAAI,IAAI,aAAa,GAAG,QAAQ;AAAG,SAAO,YAAY,OAAO,IAAI,IAAI,IAAI;AAAI;AAC1G,SAAS,aAAa,GAAG,GAAG;AAAE,MAAI,YAAY,OAAO,KAAK,CAAC,EAAG,QAAO;AAAG,MAAI,IAAI,EAAE,OAAO,WAAW;AAAG,MAAI,WAAW,GAAG;AAAE,QAAI,IAAI,EAAE,KAAK,GAAG,KAAK,SAAS;AAAG,QAAI,YAAY,OAAO,EAAG,QAAO;AAAG,UAAM,IAAI,UAAU,8CAA8C;AAAA,EAAG;AAAE,UAAQ,aAAa,IAAI,SAAS,QAAQ,CAAC;AAAG;AAGvT,SAAS,aAAa,MAAM;AAC1B,SAAO,QAAQ,KAAK,IAAI,CAAC,MAAM,MAAmB,cAAAC,QAAM,cAAc,KAAK,KAAK,cAAc;AAAA,IAC5F,KAAK;AAAA,EACP,GAAG,KAAK,IAAI,GAAG,aAAa,KAAK,KAAK,CAAC,CAAC;AAC1C;AACO,SAAS,QAAQ,MAAM;AAC5B,SAAO,WAAsB,cAAAA,QAAM,cAAc,UAAU,SAAS;AAAA,IAClE,MAAM,cAAc,CAAC,GAAG,KAAK,IAAI;AAAA,EACnC,GAAG,KAAK,GAAG,aAAa,KAAK,KAAK,CAAC;AACrC;AACO,SAAS,SAAS,OAAO;AAC9B,MAAI,OAAO,UAAQ;AACjB,QAAI;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,IAAI,OACJ,WAAW,yBAAyB,OAAO,SAAS;AACtD,QAAI,eAAe,QAAQ,KAAK,QAAQ;AACxC,QAAI;AACJ,QAAI,KAAK,UAAW,aAAY,KAAK;AACrC,QAAI,MAAM,UAAW,cAAa,YAAY,YAAY,MAAM,MAAM,MAAM;AAC5E,WAAoB,cAAAA,QAAM,cAAc,OAAO,SAAS;AAAA,MACtD,QAAQ;AAAA,MACR,MAAM;AAAA,MACN,aAAa;AAAA,IACf,GAAG,KAAK,MAAM,MAAM,UAAU;AAAA,MAC5B;AAAA,MACA,OAAO,cAAc,cAAc;AAAA,QACjC,OAAO,MAAM,SAAS,KAAK;AAAA,MAC7B,GAAG,KAAK,KAAK,GAAG,MAAM,KAAK;AAAA,MAC3B,QAAQ;AAAA,MACR,OAAO;AAAA,MACP,OAAO;AAAA,IACT,CAAC,GAAG,SAAsB,cAAAA,QAAM,cAAc,SAAS,MAAM,KAAK,GAAG,MAAM,QAAQ;AAAA,EACrF;AACA,SAAO,gBAAgB,SAAyB,cAAAA,QAAM,cAAc,YAAY,UAAU,MAAM,UAAQ,KAAK,IAAI,CAAC,IAAI,KAAK,cAAc;AAC3I;",
|
||||
"names": ["import_react", "React", "r", "React"]
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
import {
|
||||
__commonJS
|
||||
} from "./chunk-EWTE5DHJ.js";
|
||||
|
||||
// node_modules/object-assign/index.js
|
||||
var require_object_assign = __commonJS({
|
||||
"node_modules/object-assign/index.js"(exports, module) {
|
||||
"use strict";
|
||||
var getOwnPropertySymbols = Object.getOwnPropertySymbols;
|
||||
var hasOwnProperty = Object.prototype.hasOwnProperty;
|
||||
var propIsEnumerable = Object.prototype.propertyIsEnumerable;
|
||||
function toObject(val) {
|
||||
if (val === null || val === void 0) {
|
||||
throw new TypeError("Object.assign cannot be called with null or undefined");
|
||||
}
|
||||
return Object(val);
|
||||
}
|
||||
function shouldUseNative() {
|
||||
try {
|
||||
if (!Object.assign) {
|
||||
return false;
|
||||
}
|
||||
var test1 = new String("abc");
|
||||
test1[5] = "de";
|
||||
if (Object.getOwnPropertyNames(test1)[0] === "5") {
|
||||
return false;
|
||||
}
|
||||
var test2 = {};
|
||||
for (var i = 0; i < 10; i++) {
|
||||
test2["_" + String.fromCharCode(i)] = i;
|
||||
}
|
||||
var order2 = Object.getOwnPropertyNames(test2).map(function(n) {
|
||||
return test2[n];
|
||||
});
|
||||
if (order2.join("") !== "0123456789") {
|
||||
return false;
|
||||
}
|
||||
var test3 = {};
|
||||
"abcdefghijklmnopqrst".split("").forEach(function(letter) {
|
||||
test3[letter] = letter;
|
||||
});
|
||||
if (Object.keys(Object.assign({}, test3)).join("") !== "abcdefghijklmnopqrst") {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} catch (err) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
module.exports = shouldUseNative() ? Object.assign : function(target, source) {
|
||||
var from;
|
||||
var to = toObject(target);
|
||||
var symbols;
|
||||
for (var s = 1; s < arguments.length; s++) {
|
||||
from = Object(arguments[s]);
|
||||
for (var key in from) {
|
||||
if (hasOwnProperty.call(from, key)) {
|
||||
to[key] = from[key];
|
||||
}
|
||||
}
|
||||
if (getOwnPropertySymbols) {
|
||||
symbols = getOwnPropertySymbols(from);
|
||||
for (var i = 0; i < symbols.length; i++) {
|
||||
if (propIsEnumerable.call(from, symbols[i])) {
|
||||
to[symbols[i]] = from[symbols[i]];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return to;
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
// node_modules/prop-types/lib/ReactPropTypesSecret.js
|
||||
var require_ReactPropTypesSecret = __commonJS({
|
||||
"node_modules/prop-types/lib/ReactPropTypesSecret.js"(exports, module) {
|
||||
"use strict";
|
||||
var ReactPropTypesSecret = "SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED";
|
||||
module.exports = ReactPropTypesSecret;
|
||||
}
|
||||
});
|
||||
|
||||
// node_modules/prop-types/lib/has.js
|
||||
var require_has = __commonJS({
|
||||
"node_modules/prop-types/lib/has.js"(exports, module) {
|
||||
module.exports = Function.call.bind(Object.prototype.hasOwnProperty);
|
||||
}
|
||||
});
|
||||
|
||||
// node_modules/prop-types/checkPropTypes.js
|
||||
var require_checkPropTypes = __commonJS({
|
||||
"node_modules/prop-types/checkPropTypes.js"(exports, module) {
|
||||
"use strict";
|
||||
var printWarning = function() {
|
||||
};
|
||||
if (true) {
|
||||
ReactPropTypesSecret = require_ReactPropTypesSecret();
|
||||
loggedTypeFailures = {};
|
||||
has = require_has();
|
||||
printWarning = function(text) {
|
||||
var message = "Warning: " + text;
|
||||
if (typeof console !== "undefined") {
|
||||
console.error(message);
|
||||
}
|
||||
try {
|
||||
throw new Error(message);
|
||||
} catch (x) {
|
||||
}
|
||||
};
|
||||
}
|
||||
var ReactPropTypesSecret;
|
||||
var loggedTypeFailures;
|
||||
var has;
|
||||
function checkPropTypes(typeSpecs, values, location, componentName, getStack) {
|
||||
if (true) {
|
||||
for (var typeSpecName in typeSpecs) {
|
||||
if (has(typeSpecs, typeSpecName)) {
|
||||
var error;
|
||||
try {
|
||||
if (typeof typeSpecs[typeSpecName] !== "function") {
|
||||
var err = Error(
|
||||
(componentName || "React class") + ": " + location + " type `" + typeSpecName + "` is invalid; it must be a function, usually from the `prop-types` package, but received `" + typeof typeSpecs[typeSpecName] + "`.This often happens because of typos such as `PropTypes.function` instead of `PropTypes.func`."
|
||||
);
|
||||
err.name = "Invariant Violation";
|
||||
throw err;
|
||||
}
|
||||
error = typeSpecs[typeSpecName](values, typeSpecName, componentName, location, null, ReactPropTypesSecret);
|
||||
} catch (ex) {
|
||||
error = ex;
|
||||
}
|
||||
if (error && !(error instanceof Error)) {
|
||||
printWarning(
|
||||
(componentName || "React class") + ": type specification of " + location + " `" + typeSpecName + "` is invalid; the type checker function must return `null` or an `Error` but returned a " + typeof error + ". You may have forgotten to pass an argument to the type checker creator (arrayOf, instanceOf, objectOf, oneOf, oneOfType, and shape all require an argument)."
|
||||
);
|
||||
}
|
||||
if (error instanceof Error && !(error.message in loggedTypeFailures)) {
|
||||
loggedTypeFailures[error.message] = true;
|
||||
var stack = getStack ? getStack() : "";
|
||||
printWarning(
|
||||
"Failed " + location + " type: " + error.message + (stack != null ? stack : "")
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
checkPropTypes.resetWarningCache = function() {
|
||||
if (true) {
|
||||
loggedTypeFailures = {};
|
||||
}
|
||||
};
|
||||
module.exports = checkPropTypes;
|
||||
}
|
||||
});
|
||||
|
||||
export {
|
||||
require_object_assign,
|
||||
require_ReactPropTypesSecret,
|
||||
require_has,
|
||||
require_checkPropTypes
|
||||
};
|
||||
/*! Bundled license information:
|
||||
|
||||
object-assign/index.js:
|
||||
(*
|
||||
object-assign
|
||||
(c) Sindre Sorhus
|
||||
@license MIT
|
||||
*)
|
||||
*/
|
||||
//# sourceMappingURL=chunk-UU7TO5PY.js.map
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"type": "module"
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
import {
|
||||
BarController,
|
||||
BubbleController,
|
||||
Chart,
|
||||
DoughnutController,
|
||||
LineController,
|
||||
PieController,
|
||||
PolarAreaController,
|
||||
RadarController,
|
||||
ScatterController
|
||||
} from "./chunk-2YIK36WJ.js";
|
||||
import {
|
||||
require_react
|
||||
} from "./chunk-W4EHDCLL.js";
|
||||
import {
|
||||
__toESM
|
||||
} from "./chunk-EWTE5DHJ.js";
|
||||
|
||||
// node_modules/react-chartjs-2/dist/index.js
|
||||
var import_react = __toESM(require_react());
|
||||
var defaultDatasetIdKey = "label";
|
||||
function reforwardRef(ref, value) {
|
||||
if (typeof ref === "function") {
|
||||
ref(value);
|
||||
} else if (ref) {
|
||||
ref.current = value;
|
||||
}
|
||||
}
|
||||
function setOptions(chart, nextOptions) {
|
||||
const options = chart.options;
|
||||
if (options && nextOptions) {
|
||||
Object.assign(options, nextOptions);
|
||||
}
|
||||
}
|
||||
function setLabels(currentData, nextLabels) {
|
||||
currentData.labels = nextLabels;
|
||||
}
|
||||
function setDatasets(currentData, nextDatasets) {
|
||||
let datasetIdKey = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : defaultDatasetIdKey;
|
||||
const addedDatasets = [];
|
||||
currentData.datasets = nextDatasets.map((nextDataset) => {
|
||||
const currentDataset = currentData.datasets.find((dataset) => dataset[datasetIdKey] === nextDataset[datasetIdKey]);
|
||||
if (!currentDataset || !nextDataset.data || addedDatasets.includes(currentDataset)) {
|
||||
return {
|
||||
...nextDataset
|
||||
};
|
||||
}
|
||||
addedDatasets.push(currentDataset);
|
||||
Object.assign(currentDataset, nextDataset);
|
||||
return currentDataset;
|
||||
});
|
||||
}
|
||||
function cloneData(data) {
|
||||
let datasetIdKey = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : defaultDatasetIdKey;
|
||||
const nextData = {
|
||||
labels: [],
|
||||
datasets: []
|
||||
};
|
||||
setLabels(nextData, data.labels);
|
||||
setDatasets(nextData, data.datasets, datasetIdKey);
|
||||
return nextData;
|
||||
}
|
||||
function getDatasetAtEvent(chart, event) {
|
||||
return chart.getElementsAtEventForMode(event.nativeEvent, "dataset", {
|
||||
intersect: true
|
||||
}, false);
|
||||
}
|
||||
function getElementAtEvent(chart, event) {
|
||||
return chart.getElementsAtEventForMode(event.nativeEvent, "nearest", {
|
||||
intersect: true
|
||||
}, false);
|
||||
}
|
||||
function getElementsAtEvent(chart, event) {
|
||||
return chart.getElementsAtEventForMode(event.nativeEvent, "index", {
|
||||
intersect: true
|
||||
}, false);
|
||||
}
|
||||
function ChartComponent(props, ref) {
|
||||
const { height = 150, width = 300, redraw = false, datasetIdKey, type, data, options, plugins = [], fallbackContent, updateMode, ...canvasProps } = props;
|
||||
const canvasRef = (0, import_react.useRef)(null);
|
||||
const chartRef = (0, import_react.useRef)(null);
|
||||
const renderChart = () => {
|
||||
if (!canvasRef.current) return;
|
||||
chartRef.current = new Chart(canvasRef.current, {
|
||||
type,
|
||||
data: cloneData(data, datasetIdKey),
|
||||
options: options && {
|
||||
...options
|
||||
},
|
||||
plugins
|
||||
});
|
||||
reforwardRef(ref, chartRef.current);
|
||||
};
|
||||
const destroyChart = () => {
|
||||
reforwardRef(ref, null);
|
||||
if (chartRef.current) {
|
||||
chartRef.current.destroy();
|
||||
chartRef.current = null;
|
||||
}
|
||||
};
|
||||
(0, import_react.useEffect)(() => {
|
||||
if (!redraw && chartRef.current && options) {
|
||||
setOptions(chartRef.current, options);
|
||||
}
|
||||
}, [
|
||||
redraw,
|
||||
options
|
||||
]);
|
||||
(0, import_react.useEffect)(() => {
|
||||
if (!redraw && chartRef.current) {
|
||||
setLabels(chartRef.current.config.data, data.labels);
|
||||
}
|
||||
}, [
|
||||
redraw,
|
||||
data.labels
|
||||
]);
|
||||
(0, import_react.useEffect)(() => {
|
||||
if (!redraw && chartRef.current && data.datasets) {
|
||||
setDatasets(chartRef.current.config.data, data.datasets, datasetIdKey);
|
||||
}
|
||||
}, [
|
||||
redraw,
|
||||
data.datasets
|
||||
]);
|
||||
(0, import_react.useEffect)(() => {
|
||||
if (!chartRef.current) return;
|
||||
if (redraw) {
|
||||
destroyChart();
|
||||
setTimeout(renderChart);
|
||||
} else {
|
||||
chartRef.current.update(updateMode);
|
||||
}
|
||||
}, [
|
||||
redraw,
|
||||
options,
|
||||
data.labels,
|
||||
data.datasets,
|
||||
updateMode
|
||||
]);
|
||||
(0, import_react.useEffect)(() => {
|
||||
if (!chartRef.current) return;
|
||||
destroyChart();
|
||||
setTimeout(renderChart);
|
||||
}, [
|
||||
type
|
||||
]);
|
||||
(0, import_react.useEffect)(() => {
|
||||
renderChart();
|
||||
return () => destroyChart();
|
||||
}, []);
|
||||
return import_react.default.createElement("canvas", {
|
||||
ref: canvasRef,
|
||||
role: "img",
|
||||
height,
|
||||
width,
|
||||
...canvasProps
|
||||
}, fallbackContent);
|
||||
}
|
||||
var Chart2 = (0, import_react.forwardRef)(ChartComponent);
|
||||
function createTypedChart(type, registerables) {
|
||||
Chart.register(registerables);
|
||||
return (0, import_react.forwardRef)((props, ref) => import_react.default.createElement(Chart2, {
|
||||
...props,
|
||||
ref,
|
||||
type
|
||||
}));
|
||||
}
|
||||
var Line = createTypedChart("line", LineController);
|
||||
var Bar = createTypedChart("bar", BarController);
|
||||
var Radar = createTypedChart("radar", RadarController);
|
||||
var Doughnut = createTypedChart("doughnut", DoughnutController);
|
||||
var PolarArea = createTypedChart("polarArea", PolarAreaController);
|
||||
var Bubble = createTypedChart("bubble", BubbleController);
|
||||
var Pie = createTypedChart("pie", PieController);
|
||||
var Scatter = createTypedChart("scatter", ScatterController);
|
||||
export {
|
||||
Bar,
|
||||
Bubble,
|
||||
Chart2 as Chart,
|
||||
Doughnut,
|
||||
Line,
|
||||
Pie,
|
||||
PolarArea,
|
||||
Radar,
|
||||
Scatter,
|
||||
getDatasetAtEvent,
|
||||
getElementAtEvent,
|
||||
getElementsAtEvent
|
||||
};
|
||||
//# sourceMappingURL=react-chartjs-2.js.map
|
||||
@@ -0,0 +1,39 @@
|
||||
import {
|
||||
require_react_dom
|
||||
} from "./chunk-UHINIFCJ.js";
|
||||
import "./chunk-W4EHDCLL.js";
|
||||
import {
|
||||
__commonJS
|
||||
} from "./chunk-EWTE5DHJ.js";
|
||||
|
||||
// node_modules/react-dom/client.js
|
||||
var require_client = __commonJS({
|
||||
"node_modules/react-dom/client.js"(exports) {
|
||||
var m = require_react_dom();
|
||||
if (false) {
|
||||
exports.createRoot = m.createRoot;
|
||||
exports.hydrateRoot = m.hydrateRoot;
|
||||
} else {
|
||||
i = m.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
|
||||
exports.createRoot = function(c, o) {
|
||||
i.usingClientEntryPoint = true;
|
||||
try {
|
||||
return m.createRoot(c, o);
|
||||
} finally {
|
||||
i.usingClientEntryPoint = false;
|
||||
}
|
||||
};
|
||||
exports.hydrateRoot = function(c, h, o) {
|
||||
i.usingClientEntryPoint = true;
|
||||
try {
|
||||
return m.hydrateRoot(c, h, o);
|
||||
} finally {
|
||||
i.usingClientEntryPoint = false;
|
||||
}
|
||||
};
|
||||
}
|
||||
var i;
|
||||
}
|
||||
});
|
||||
export default require_client();
|
||||
//# sourceMappingURL=react-dom_client.js.map
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"version": 3,
|
||||
"sources": ["../../node_modules/react-dom/client.js"],
|
||||
"sourcesContent": ["'use strict';\n\nvar m = require('react-dom');\nif (process.env.NODE_ENV === 'production') {\n exports.createRoot = m.createRoot;\n exports.hydrateRoot = m.hydrateRoot;\n} else {\n var i = m.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;\n exports.createRoot = function(c, o) {\n i.usingClientEntryPoint = true;\n try {\n return m.createRoot(c, o);\n } finally {\n i.usingClientEntryPoint = false;\n }\n };\n exports.hydrateRoot = function(c, h, o) {\n i.usingClientEntryPoint = true;\n try {\n return m.hydrateRoot(c, h, o);\n } finally {\n i.usingClientEntryPoint = false;\n }\n };\n}\n"],
|
||||
"mappings": ";;;;;;;;;AAAA;AAAA;AAEA,QAAI,IAAI;AACR,QAAI,OAAuC;AACzC,cAAQ,aAAa,EAAE;AACvB,cAAQ,cAAc,EAAE;AAAA,IAC1B,OAAO;AACD,UAAI,EAAE;AACV,cAAQ,aAAa,SAAS,GAAG,GAAG;AAClC,UAAE,wBAAwB;AAC1B,YAAI;AACF,iBAAO,EAAE,WAAW,GAAG,CAAC;AAAA,QAC1B,UAAE;AACA,YAAE,wBAAwB;AAAA,QAC5B;AAAA,MACF;AACA,cAAQ,cAAc,SAAS,GAAG,GAAG,GAAG;AACtC,UAAE,wBAAwB;AAC1B,YAAI;AACF,iBAAO,EAAE,YAAY,GAAG,GAAG,CAAC;AAAA,QAC9B,UAAE;AACA,YAAE,wBAAwB;AAAA,QAC5B;AAAA,MACF;AAAA,IACF;AAjBM;AAAA;AAAA;",
|
||||
"names": []
|
||||
}
|
||||
@@ -0,0 +1,293 @@
|
||||
"use client";
|
||||
import {
|
||||
require_react
|
||||
} from "./chunk-W4EHDCLL.js";
|
||||
import {
|
||||
__toESM
|
||||
} from "./chunk-EWTE5DHJ.js";
|
||||
|
||||
// node_modules/react-intersection-observer/dist/index.mjs
|
||||
var React = __toESM(require_react(), 1);
|
||||
var React2 = __toESM(require_react(), 1);
|
||||
var __defProp = Object.defineProperty;
|
||||
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
||||
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
||||
var observerMap = /* @__PURE__ */ new Map();
|
||||
var RootIds = /* @__PURE__ */ new WeakMap();
|
||||
var rootId = 0;
|
||||
var unsupportedValue = void 0;
|
||||
function defaultFallbackInView(inView) {
|
||||
unsupportedValue = inView;
|
||||
}
|
||||
function getRootId(root) {
|
||||
if (!root) return "0";
|
||||
if (RootIds.has(root)) return RootIds.get(root);
|
||||
rootId += 1;
|
||||
RootIds.set(root, rootId.toString());
|
||||
return RootIds.get(root);
|
||||
}
|
||||
function optionsToId(options) {
|
||||
return Object.keys(options).sort().filter(
|
||||
(key) => options[key] !== void 0
|
||||
).map((key) => {
|
||||
return `${key}_${key === "root" ? getRootId(options.root) : options[key]}`;
|
||||
}).toString();
|
||||
}
|
||||
function createObserver(options) {
|
||||
const id = optionsToId(options);
|
||||
let instance = observerMap.get(id);
|
||||
if (!instance) {
|
||||
const elements = /* @__PURE__ */ new Map();
|
||||
let thresholds;
|
||||
const observer = new IntersectionObserver((entries) => {
|
||||
entries.forEach((entry) => {
|
||||
var _a;
|
||||
const inView = entry.isIntersecting && thresholds.some((threshold) => entry.intersectionRatio >= threshold);
|
||||
if (options.trackVisibility && typeof entry.isVisible === "undefined") {
|
||||
entry.isVisible = inView;
|
||||
}
|
||||
(_a = elements.get(entry.target)) == null ? void 0 : _a.forEach((callback) => {
|
||||
callback(inView, entry);
|
||||
});
|
||||
});
|
||||
}, options);
|
||||
thresholds = observer.thresholds || (Array.isArray(options.threshold) ? options.threshold : [options.threshold || 0]);
|
||||
instance = {
|
||||
id,
|
||||
observer,
|
||||
elements
|
||||
};
|
||||
observerMap.set(id, instance);
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
function observe(element, callback, options = {}, fallbackInView = unsupportedValue) {
|
||||
if (typeof window.IntersectionObserver === "undefined" && fallbackInView !== void 0) {
|
||||
const bounds = element.getBoundingClientRect();
|
||||
callback(fallbackInView, {
|
||||
isIntersecting: fallbackInView,
|
||||
target: element,
|
||||
intersectionRatio: typeof options.threshold === "number" ? options.threshold : 0,
|
||||
time: 0,
|
||||
boundingClientRect: bounds,
|
||||
intersectionRect: bounds,
|
||||
rootBounds: bounds
|
||||
});
|
||||
return () => {
|
||||
};
|
||||
}
|
||||
const { id, observer, elements } = createObserver(options);
|
||||
const callbacks = elements.get(element) || [];
|
||||
if (!elements.has(element)) {
|
||||
elements.set(element, callbacks);
|
||||
}
|
||||
callbacks.push(callback);
|
||||
observer.observe(element);
|
||||
return function unobserve() {
|
||||
callbacks.splice(callbacks.indexOf(callback), 1);
|
||||
if (callbacks.length === 0) {
|
||||
elements.delete(element);
|
||||
observer.unobserve(element);
|
||||
}
|
||||
if (elements.size === 0) {
|
||||
observer.disconnect();
|
||||
observerMap.delete(id);
|
||||
}
|
||||
};
|
||||
}
|
||||
function isPlainChildren(props) {
|
||||
return typeof props.children !== "function";
|
||||
}
|
||||
var InView = class extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
__publicField(this, "node", null);
|
||||
__publicField(this, "_unobserveCb", null);
|
||||
__publicField(this, "handleNode", (node) => {
|
||||
if (this.node) {
|
||||
this.unobserve();
|
||||
if (!node && !this.props.triggerOnce && !this.props.skip) {
|
||||
this.setState({ inView: !!this.props.initialInView, entry: void 0 });
|
||||
}
|
||||
}
|
||||
this.node = node ? node : null;
|
||||
this.observeNode();
|
||||
});
|
||||
__publicField(this, "handleChange", (inView, entry) => {
|
||||
if (inView && this.props.triggerOnce) {
|
||||
this.unobserve();
|
||||
}
|
||||
if (!isPlainChildren(this.props)) {
|
||||
this.setState({ inView, entry });
|
||||
}
|
||||
if (this.props.onChange) {
|
||||
this.props.onChange(inView, entry);
|
||||
}
|
||||
});
|
||||
this.state = {
|
||||
inView: !!props.initialInView,
|
||||
entry: void 0
|
||||
};
|
||||
}
|
||||
componentDidMount() {
|
||||
this.unobserve();
|
||||
this.observeNode();
|
||||
}
|
||||
componentDidUpdate(prevProps) {
|
||||
if (prevProps.rootMargin !== this.props.rootMargin || prevProps.root !== this.props.root || prevProps.threshold !== this.props.threshold || prevProps.skip !== this.props.skip || prevProps.trackVisibility !== this.props.trackVisibility || prevProps.delay !== this.props.delay) {
|
||||
this.unobserve();
|
||||
this.observeNode();
|
||||
}
|
||||
}
|
||||
componentWillUnmount() {
|
||||
this.unobserve();
|
||||
}
|
||||
observeNode() {
|
||||
if (!this.node || this.props.skip) return;
|
||||
const {
|
||||
threshold,
|
||||
root,
|
||||
rootMargin,
|
||||
trackVisibility,
|
||||
delay,
|
||||
fallbackInView
|
||||
} = this.props;
|
||||
this._unobserveCb = observe(
|
||||
this.node,
|
||||
this.handleChange,
|
||||
{
|
||||
threshold,
|
||||
root,
|
||||
rootMargin,
|
||||
// @ts-ignore
|
||||
trackVisibility,
|
||||
// @ts-ignore
|
||||
delay
|
||||
},
|
||||
fallbackInView
|
||||
);
|
||||
}
|
||||
unobserve() {
|
||||
if (this._unobserveCb) {
|
||||
this._unobserveCb();
|
||||
this._unobserveCb = null;
|
||||
}
|
||||
}
|
||||
render() {
|
||||
const { children } = this.props;
|
||||
if (typeof children === "function") {
|
||||
const { inView, entry } = this.state;
|
||||
return children({ inView, entry, ref: this.handleNode });
|
||||
}
|
||||
const {
|
||||
as,
|
||||
triggerOnce,
|
||||
threshold,
|
||||
root,
|
||||
rootMargin,
|
||||
onChange,
|
||||
skip,
|
||||
trackVisibility,
|
||||
delay,
|
||||
initialInView,
|
||||
fallbackInView,
|
||||
...props
|
||||
} = this.props;
|
||||
return React.createElement(
|
||||
as || "div",
|
||||
{ ref: this.handleNode, ...props },
|
||||
children
|
||||
);
|
||||
}
|
||||
};
|
||||
function useInView({
|
||||
threshold,
|
||||
delay,
|
||||
trackVisibility,
|
||||
rootMargin,
|
||||
root,
|
||||
triggerOnce,
|
||||
skip,
|
||||
initialInView,
|
||||
fallbackInView,
|
||||
onChange
|
||||
} = {}) {
|
||||
var _a;
|
||||
const [ref, setRef] = React2.useState(null);
|
||||
const callback = React2.useRef(onChange);
|
||||
const [state, setState] = React2.useState({
|
||||
inView: !!initialInView,
|
||||
entry: void 0
|
||||
});
|
||||
callback.current = onChange;
|
||||
React2.useEffect(
|
||||
() => {
|
||||
if (skip || !ref) return;
|
||||
let unobserve;
|
||||
unobserve = observe(
|
||||
ref,
|
||||
(inView, entry) => {
|
||||
setState({
|
||||
inView,
|
||||
entry
|
||||
});
|
||||
if (callback.current) callback.current(inView, entry);
|
||||
if (entry.isIntersecting && triggerOnce && unobserve) {
|
||||
unobserve();
|
||||
unobserve = void 0;
|
||||
}
|
||||
},
|
||||
{
|
||||
root,
|
||||
rootMargin,
|
||||
threshold,
|
||||
// @ts-ignore
|
||||
trackVisibility,
|
||||
// @ts-ignore
|
||||
delay
|
||||
},
|
||||
fallbackInView
|
||||
);
|
||||
return () => {
|
||||
if (unobserve) {
|
||||
unobserve();
|
||||
}
|
||||
};
|
||||
},
|
||||
// We break the rule here, because we aren't including the actual `threshold` variable
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[
|
||||
// If the threshold is an array, convert it to a string, so it won't change between renders.
|
||||
Array.isArray(threshold) ? threshold.toString() : threshold,
|
||||
ref,
|
||||
root,
|
||||
rootMargin,
|
||||
triggerOnce,
|
||||
skip,
|
||||
trackVisibility,
|
||||
fallbackInView,
|
||||
delay
|
||||
]
|
||||
);
|
||||
const entryTarget = (_a = state.entry) == null ? void 0 : _a.target;
|
||||
const previousEntryTarget = React2.useRef(void 0);
|
||||
if (!ref && entryTarget && !triggerOnce && !skip && previousEntryTarget.current !== entryTarget) {
|
||||
previousEntryTarget.current = entryTarget;
|
||||
setState({
|
||||
inView: !!initialInView,
|
||||
entry: void 0
|
||||
});
|
||||
}
|
||||
const result = [setRef, state.inView, state.entry];
|
||||
result.ref = result[0];
|
||||
result.inView = result[1];
|
||||
result.entry = result[2];
|
||||
return result;
|
||||
}
|
||||
export {
|
||||
InView,
|
||||
defaultFallbackInView,
|
||||
observe,
|
||||
useInView
|
||||
};
|
||||
//# sourceMappingURL=react-intersection-observer.js.map
|
||||
@@ -0,0 +1,6 @@
|
||||
import {
|
||||
require_react
|
||||
} from "./chunk-W4EHDCLL.js";
|
||||
import "./chunk-EWTE5DHJ.js";
|
||||
export default require_react();
|
||||
//# sourceMappingURL=react.js.map
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"version": 3,
|
||||
"sources": [],
|
||||
"sourcesContent": [],
|
||||
"mappings": "",
|
||||
"names": []
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
# Base image
|
||||
FROM node:22
|
||||
|
||||
# Environment variables
|
||||
ENV MODEL_URI=http://localhost:8081
|
||||
ENV BACKEND_URI=http://localhost:8000
|
||||
|
||||
# Metadata
|
||||
LABEL maintainer="kshitijka"
|
||||
LABEL version=1.1.0
|
||||
LABEL description="Crop Compass is a centralized management dashboard designed for farmers, enabling them to efficiently oversee their farms while leveraging advanced AI technology for disease identification and more."
|
||||
|
||||
# Update and upgrade
|
||||
RUN apt update && apt upgrade -y && \
|
||||
apt clean all && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Create non-root user
|
||||
RUN useradd -s /bin/bash nonroot
|
||||
|
||||
# Create working directory
|
||||
RUN mkdir -p /app
|
||||
RUN chown -R nonroot:nonroot /app
|
||||
WORKDIR /app
|
||||
|
||||
# Copying contents
|
||||
COPY . .
|
||||
|
||||
# Writing backend uri in .env
|
||||
RUN echo "VITE_API_URL = $BACKEND_URI" > /app/.env
|
||||
|
||||
# Building frontend
|
||||
RUN npm install
|
||||
RUN npm run build
|
||||
|
||||
# Clean-up
|
||||
RUN rm -rf eslint.config.js index.html node_modules/ package-lock.json package.json postcss.config.js public/ src/ tailwind.config.js vite.config.js .dockerignore .gitignore .vite/
|
||||
|
||||
# Install server
|
||||
RUN npm install -g serve
|
||||
|
||||
# Switch user
|
||||
USER nonroot
|
||||
|
||||
# Expose frontend port
|
||||
EXPOSE 3000
|
||||
|
||||
# Run frontend
|
||||
CMD ["serve", "-s", "/app/dist"]
|
||||
@@ -0,0 +1,8 @@
|
||||
# React + Vite
|
||||
|
||||
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
|
||||
|
||||
Currently, two official plugins are available:
|
||||
|
||||
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
|
||||
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
|
||||
@@ -0,0 +1,38 @@
|
||||
import js from '@eslint/js'
|
||||
import globals from 'globals'
|
||||
import react from 'eslint-plugin-react'
|
||||
import reactHooks from 'eslint-plugin-react-hooks'
|
||||
import reactRefresh from 'eslint-plugin-react-refresh'
|
||||
|
||||
export default [
|
||||
{ ignores: ['dist'] },
|
||||
{
|
||||
files: ['**/*.{js,jsx}'],
|
||||
languageOptions: {
|
||||
ecmaVersion: 2020,
|
||||
globals: globals.browser,
|
||||
parserOptions: {
|
||||
ecmaVersion: 'latest',
|
||||
ecmaFeatures: { jsx: true },
|
||||
sourceType: 'module',
|
||||
},
|
||||
},
|
||||
settings: { react: { version: '18.3' } },
|
||||
plugins: {
|
||||
react,
|
||||
'react-hooks': reactHooks,
|
||||
'react-refresh': reactRefresh,
|
||||
},
|
||||
rules: {
|
||||
...js.configs.recommended.rules,
|
||||
...react.configs.recommended.rules,
|
||||
...react.configs['jsx-runtime'].rules,
|
||||
...reactHooks.configs.recommended.rules,
|
||||
'react/jsx-no-target-blank': 'off',
|
||||
'react-refresh/only-export-components': [
|
||||
'warn',
|
||||
{ allowConstantExport: true },
|
||||
],
|
||||
},
|
||||
},
|
||||
]
|
||||
@@ -0,0 +1,18 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/images/logo.png" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Crop Compass</title>
|
||||
<link
|
||||
href="https://cdn.jsdelivr.net/npm/flowbite@2.5.1/dist/flowbite.min.css"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script src="https://cdn.jsdelivr.net/npm/flowbite@2.5.1/dist/flowbite.min.js"></script>
|
||||
<script type="module" src="/src/main.jsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,48 @@
|
||||
{
|
||||
"name": "frontend",
|
||||
"private": true,
|
||||
"version": "1.1.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"lint": "eslint .",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@reduxjs/toolkit": "^2.2.7",
|
||||
"@splinetool/react-spline": "^4.0.0",
|
||||
"apexcharts": "^4.5.0",
|
||||
"chart.js": "^4.4.8",
|
||||
"flowbite": "^2.5.1",
|
||||
"framer-motion": "^12.4.7",
|
||||
"react": "^18.3.1",
|
||||
"react-apexcharts": "^1.7.0",
|
||||
"react-chartjs-2": "^5.3.0",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-icons": "^5.3.0",
|
||||
"react-intersection-observer": "^9.15.1",
|
||||
"react-player": "^2.16.0",
|
||||
"react-redux": "^9.1.2",
|
||||
"react-router-dom": "^6.26.1",
|
||||
"react-typewriter-effect": "^1.1.0",
|
||||
"socket.io-client": "^4.7.5",
|
||||
"axios": "^1.6.8",
|
||||
"sha1": "^1.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.9.0",
|
||||
"@types/react": "^18.3.3",
|
||||
"@types/react-dom": "^18.3.0",
|
||||
"@vitejs/plugin-react": "^4.3.1",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"eslint": "^9.9.0",
|
||||
"eslint-plugin-react": "^7.35.0",
|
||||
"eslint-plugin-react-hooks": "^5.1.0-rc.0",
|
||||
"eslint-plugin-react-refresh": "^0.4.9",
|
||||
"globals": "^15.9.0",
|
||||
"postcss": "^8.4.45",
|
||||
"tailwindcss": "^3.4.10",
|
||||
"vite": "^5.4.1"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
export default {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
|
After Width: | Height: | Size: 108 KiB |
|
After Width: | Height: | Size: 111 KiB |
|
After Width: | Height: | Size: 136 KiB |
@@ -0,0 +1 @@
|
||||
<svg viewBox="-2.4 -2.4 28.80 28.80" xmlns="http://www.w3.org/2000/svg" fill="#000000" stroke="#000000"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round" stroke="#CCCCCC" stroke-width="3.2640000000000002"> <title></title> <g id="Complete"> <g id="add-square"> <g> <rect data-name="--Rectangle" fill="none" height="20" id="_--Rectangle" rx="2" ry="2" stroke="#ffffff" stroke-linecap="round" stroke-linejoin="round" stroke-width="2.4" width="20" x="2" y="2"></rect> <line fill="none" stroke="#ffffff" stroke-linecap="round" stroke-linejoin="round" stroke-width="2.4" x1="15.5" x2="8.5" y1="12" y2="12"></line> <line fill="none" stroke="#ffffff" stroke-linecap="round" stroke-linejoin="round" stroke-width="2.4" x1="12" x2="12" y1="15.5" y2="8.5"></line> </g> </g> </g> </g><g id="SVGRepo_iconCarrier"> <title></title> <g id="Complete"> <g id="add-square"> <g> <rect data-name="--Rectangle" fill="none" height="20" id="_--Rectangle" rx="2" ry="2" stroke="#ffffff" stroke-linecap="round" stroke-linejoin="round" stroke-width="2.4" width="20" x="2" y="2"></rect> <line fill="none" stroke="#ffffff" stroke-linecap="round" stroke-linejoin="round" stroke-width="2.4" x1="15.5" x2="8.5" y1="12" y2="12"></line> <line fill="none" stroke="#ffffff" stroke-linecap="round" stroke-linejoin="round" stroke-width="2.4" x1="12" x2="12" y1="15.5" y2="8.5"></line> </g> </g> </g> </g></svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 1.1 MiB |