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 |