diff --git a/Backend/package-lock.json b/Backend/package-lock.json index bc172c9..3b16a3e 100644 --- a/Backend/package-lock.json +++ b/Backend/package-lock.json @@ -19,7 +19,7 @@ "moment": "^2.30.1", "mongoose": "^8.0.2", "multer": "^1.4.5-lts.1", - "nodemailer": "^6.9.13" + "nodemailer": "^6.9.14" }, "devDependencies": { "nodemon": "^3.0.2" @@ -1482,9 +1482,9 @@ } }, "node_modules/nodemailer": { - "version": "6.9.13", - "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.13.tgz", - "integrity": "sha512-7o38Yogx6krdoBf3jCAqnIN4oSQFx+fMa0I7dK1D+me9kBxx12D+/33wSb+fhOCtIxvYJ+4x4IMEhmhCKfAiOA==", + "version": "6.9.14", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.14.tgz", + "integrity": "sha512-Dobp/ebDKBvz91sbtRKhcznLThrKxKt97GI2FAlAyy+fk19j73Uz3sBXolVtmcXjaorivqsbbbjDY+Jkt4/bQA==", "engines": { "node": ">=6.0.0" } diff --git a/Backend/package.json b/Backend/package.json index 8d4fc54..808440c 100644 --- a/Backend/package.json +++ b/Backend/package.json @@ -22,7 +22,7 @@ "moment": "^2.30.1", "mongoose": "^8.0.2", "multer": "^1.4.5-lts.1", - "nodemailer": "^6.9.13" + "nodemailer": "^6.9.14" }, "devDependencies": { "nodemon": "^3.0.2" diff --git a/Backend/src/config/database.config.js b/Backend/src/config/database.config.js index e63e76a..704d230 100644 --- a/Backend/src/config/database.config.js +++ b/Backend/src/config/database.config.js @@ -24,7 +24,7 @@ export const dbconnect = async () => { console.log('Connected successfully!'); // Watch for changes only after seeding is done - await watchUsers(); + //await watchUsers(); } catch (error) { console.log(error); } diff --git a/Backend/src/models/disasterRequest.model.js b/Backend/src/models/disasterRequest.model.js index c4b116d..5e69159 100644 --- a/Backend/src/models/disasterRequest.model.js +++ b/Backend/src/models/disasterRequest.model.js @@ -13,7 +13,8 @@ export const DisasterRequestSchema = new Schema( } ], default: []}, - affectedCount: {type: Number, required: true}, + image: {type:[{type:String}], required:false}, + affectedCount: {type: String, required: true}, medicalNeed: {type: Boolean, default: false}, otherNeeds: {type: String, required: false}, requestTime: {type: String, required: true}, diff --git a/Backend/src/models/news.model.js b/Backend/src/models/news.model.js index f1ff120..c236cc7 100644 --- a/Backend/src/models/news.model.js +++ b/Backend/src/models/news.model.js @@ -7,7 +7,7 @@ export const NewsSchema = new Schema( author: {type: String, required: true}, createdDate: {type: String, required: true}, createdTime: {type: String, required: true}, - image: {data: Buffer , contentType: String}, + image: {type:String , default:false}, newsBody: {type: String, required: true}, show: {type: Boolean, default:false} //if and only if show is true news are displayed in the user's side }, diff --git a/Backend/src/models/user.model.js b/Backend/src/models/user.model.js index 974d9d7..80d600c 100644 --- a/Backend/src/models/user.model.js +++ b/Backend/src/models/user.model.js @@ -9,7 +9,7 @@ export const UserSchema = new Schema( address: {type: String, default: "none"}, accessLevel: {type: Number, default: 1}, department: {type: String, default: "None"}, - telephoneNumber: {type: String, required: true}, + telephoneNumber: {type: String, required: false}, lastLogged: {type: Date, default: "2023/10/12"} }, { diff --git a/Backend/src/routers/maps.router.js b/Backend/src/routers/maps.router.js index ac3c6d9..ccb43b0 100644 --- a/Backend/src/routers/maps.router.js +++ b/Backend/src/routers/maps.router.js @@ -23,7 +23,7 @@ router.post('/getGeoCode', handler(async(req,res) => { router.post ('/getProvince',handler( async(req,res) => { const {location} = req.body; - const apiKey = 'AIzaSyCqnhZFna6jPPizSKO88sNgdYLc3SHAGhk'; + const apiKey = process.env.GOOGLEMAP_API; const url = `https://maps.googleapis.com/maps/api/geocode/json?address=${encodeURIComponent(location)},Sri%20Lanka&key=${apiKey}`; try { diff --git a/Backend/src/routers/news.router.js b/Backend/src/routers/news.router.js index 6db7a80..26c5546 100644 --- a/Backend/src/routers/news.router.js +++ b/Backend/src/routers/news.router.js @@ -11,9 +11,9 @@ const router = Router(); const storage = multer.memoryStorage(); const upload = multer({ storage: storage }); -router.post('/createNews', upload.single('image'), handler(async (req, res) => { +router.post('/createNews', handler(async (req, res) => { - const {heading, author, newsBody} = req.body; + const {heading, author, newsBody, image} = req.body; const currentDateTime = new Date(); //real time const createdDate = currentDateTime.toDateString(); const createdTime = currentDateTime.toTimeString(); @@ -24,22 +24,13 @@ router.post('/createNews', upload.single('image'), handler(async (req, res) => { return res.status(BAD_REQUEST).send("Missing required fields"); } const newID = await generateNewsId(heading); - - let imageData; - let contentType; - if (req.file) { - imageData = req.file.buffer; // Convert image buffer to base64 string - contentType = req.file.mimetype; - } try { const newNews = await NewsModel.create({ newsId: newID, heading, author, - - image : { data: imageData, contentType: contentType }, - + image, newsBody, createdDate, createdTime, @@ -54,9 +45,9 @@ router.post('/createNews', upload.single('image'), handler(async (req, res) => { })); -router.patch('/updateNews/:newsId', upload.single('image'), handler(async (req, res) => { +router.patch('/updateNews/:newsId', handler(async (req, res) => { const { newsId } = req.params; - const {heading, author, newsBody, show} = req.body; + const {heading, author, newsBody, show, image} = req.body; const currentDateTime = new Date(); //real time const createdDate = currentDateTime.toDateString(); const createdTime = currentDateTime.toTimeString(); @@ -64,14 +55,7 @@ router.patch('/updateNews/:newsId', upload.single('image'), handler(async (req, if (!heading || !author || !newsBody ) { return res.status(BAD_REQUEST).send("Missing required fields"); } - let updateData = { heading, author, newsBody, show, createdDate, createdTime}; - - if (req.file) { - updateData.image = { - data: req.file.buffer, - contentType: req.file.mimetype - }; - } + let updateData = { heading, author, newsBody, show, createdDate, createdTime, image}; try{ const updatedNews = await NewsModel.findOneAndUpdate( { newsId: newsId }, diff --git a/Backend/src/routers/report.router.js b/Backend/src/routers/report.router.js index 327d750..03d6468 100644 --- a/Backend/src/routers/report.router.js +++ b/Backend/src/routers/report.router.js @@ -12,6 +12,7 @@ router.post('/addReport', handler(async (req,res) => { const {disasterType, severity, disasterLocation, + disasterRequests, affectedCount, affectedLocations, finished @@ -28,6 +29,7 @@ router.post('/addReport', handler(async (req,res) => { disasterLocation, affectedCount, affectedLocations, + disasterRequests, createdDate: currentDate, updatedDate: currentDate, finished: isFinished diff --git a/Backend/src/routers/request.router.js b/Backend/src/routers/request.router.js index 83c5c56..faa3e62 100644 --- a/Backend/src/routers/request.router.js +++ b/Backend/src/routers/request.router.js @@ -2,10 +2,11 @@ import { Router } from "express"; import { BAD_REQUEST, INTERNAL_SERVER_ERROR } from "../constants/httpStatus.js"; import handler from 'express-async-handler'; import { DisasterRequestModel } from "../models/disasterRequest.model.js"; +import { DisasterReportModel } from "../models/disasterReport.model.js"; const router = Router(); -router.get('/request',handler(async (req,res) => { +router.post('/request',handler(async (req,res) => { const { disasterType, @@ -16,13 +17,14 @@ router.get('/request',handler(async (req,res) => { otherNeeds, disasterLocationLatLan, read, - requestProvince} = req.body; + image} = req.body; const currentDateTime = new Date(); const requestDate = currentDateTime.toDateString(); const requestTime = currentDateTime.toTimeString(); const newID = await generateRequestID(disasterType); + const requestProvince = await getRequestProvince(disasterLocationLatLan); const newRequest = { requestID: newID, @@ -36,7 +38,8 @@ router.get('/request',handler(async (req,res) => { requestTime, requestDate, read, - requestProvince + image, + requestProvince: requestProvince }; const result = await DisasterRequestModel.create(newRequest); @@ -88,9 +91,9 @@ router.put('/updateRequest/:requestID', handler(async (req, res) => { { read: true }, { new: true } // Return the updated document ); - return res.status(INTERNAL_SERVER_ERROR).send(request); + res.status(200).send(request); + } catch (error) { - console.error(error); return res.status(INTERNAL_SERVER_ERROR).send("Internal server error"); } })); @@ -132,7 +135,6 @@ router.post('/showUnverify', handler(async (req, res) =>{ //not finished const sendingResponds = async(requests) =>{ - try{ const sendTo = "engerrev897@gmail.com"; const sendFrom = process.env.Email_USER; @@ -161,26 +163,36 @@ const generateRequestID = async(disasterType) => { const disasterCode = disasterType.substring(0, 2); const min = 0; - const max = 100; - const requestNumber = Math.floor(Math.random() * (max - min + 1)) + min; + const max = 1000; + let requestNumber = Math.floor(Math.random() * (max - min + 1)) + min; - const newID = disasterCode + requestNumber.toString(); - console.log("ID",newID); + let newID = disasterCode + requestNumber.toString(); + + try{ + while(await DisasterRequestModel.findOne({requestID: newID})){ + requestNumber++; + newID = disasterCode + requestNumber.toString(); + } + console.log("ID",newID); + return newID; + }catch(error){ + console.log("ID generate error"); + } return newID; }; -const getRequestProvince = async (lat, lng) => { +const getRequestProvince = async (locationLat) => { try{ - const url = `https://maps.googleapis.com/maps/api/geocode/json?latlng=${lat},${lng}&key=${process.env.GOOGLEMAP_API}`; + const url = `https://maps.googleapis.com/maps/api/geocode/json?latlng=${locationLat[0]},${locationLat[1]}&key=${process.env.GOOGLEMAP_API}`; - const response = await axios.get(url); - const results = response.data.results; + const response = await fetch(url); + const data = await response.json(); - if (results.length > 0) { + if (data.results.length > 0) { // Loop through the address components to find the province - for (const component of results[0].address_components) { + for (const component of data.results[0].address_components) { if (component.types.includes("administrative_area_level_1")) { return component.long_name; // Return the province name } @@ -191,4 +203,5 @@ const getRequestProvince = async (lat, lng) => { } } -export default router \ No newline at end of file +export default router + diff --git a/Backend/src/server.js b/Backend/src/server.js index e6f37ae..e688741 100644 --- a/Backend/src/server.js +++ b/Backend/src/server.js @@ -20,7 +20,6 @@ dbconnect(); const app = express(); - app.use(express.json()); app.use(cors({ diff --git a/frontend/package-lock.json b/frontend/package-lock.json index e20dff7..6cb9744 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -22,12 +22,14 @@ "chart.js": "^4.4.2", "date-fns": "^3.4.0", "dotenv": "^16.4.5", + "firebase": "^10.12.2", "flowbite-react": "^0.7.2", "google-map-react": "^2.2.1", "leven": "^4.0.0", "pluralize": "^8.0.0", "react": "^18.2.0", "react-chartjs-2": "^5.2.0", + "react-circular-progressbar": "^2.1.0", "react-cool-onclickoutside": "^1.7.0", "react-dom": "^18.2.0", "react-dropzone": "^14.2.3", @@ -1744,6 +1746,518 @@ "resolved": "https://registry.npmjs.org/@fal-works/esbuild-plugin-global-externals/-/esbuild-plugin-global-externals-2.1.2.tgz", "integrity": "sha512-cEee/Z+I12mZcFJshKcCqC8tuX5hG3s+d+9nZ3LabqKF1vKdF41B92pJVCBggjAGORAeOzyyDDKrZwIkLffeOQ==" }, + "node_modules/@fastify/busboy": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz", + "integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@firebase/analytics": { + "version": "0.10.4", + "resolved": "https://registry.npmjs.org/@firebase/analytics/-/analytics-0.10.4.tgz", + "integrity": "sha512-OJEl/8Oye/k+vJ1zV/1L6eGpc1XzAj+WG2TPznJ7PszL7sOFLBXkL9IjHfOCGDGpXeO3btozy/cYUqv4zgNeHg==", + "dependencies": { + "@firebase/component": "0.6.7", + "@firebase/installations": "0.6.7", + "@firebase/logger": "0.4.2", + "@firebase/util": "1.9.6", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/analytics-compat": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/@firebase/analytics-compat/-/analytics-compat-0.2.10.tgz", + "integrity": "sha512-ia68RcLQLLMFWrM10JfmFod7eJGwqr4/uyrtzHpTDnxGX/6gNCBTOuxdAbyWIqXI5XmcMQdz9hDijGKOHgDfPw==", + "dependencies": { + "@firebase/analytics": "0.10.4", + "@firebase/analytics-types": "0.8.2", + "@firebase/component": "0.6.7", + "@firebase/util": "1.9.6", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/analytics-types": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/@firebase/analytics-types/-/analytics-types-0.8.2.tgz", + "integrity": "sha512-EnzNNLh+9/sJsimsA/FGqzakmrAUKLeJvjRHlg8df1f97NLUlFidk9600y0ZgWOp3CAxn6Hjtk+08tixlUOWyw==" + }, + "node_modules/@firebase/app": { + "version": "0.10.5", + "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.10.5.tgz", + "integrity": "sha512-iY/fNot+hWPk9sTX8aHMqlcX9ynRvpGkskWAdUZ2eQQdLo8d1hSFYcYNwPv0Q/frGMasw8udKWMcFOEpC9fG8g==", + "dependencies": { + "@firebase/component": "0.6.7", + "@firebase/logger": "0.4.2", + "@firebase/util": "1.9.6", + "idb": "7.1.1", + "tslib": "^2.1.0" + } + }, + "node_modules/@firebase/app-check": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/@firebase/app-check/-/app-check-0.8.4.tgz", + "integrity": "sha512-2tjRDaxcM5G7BEpytiDcIl+NovV99q8yEqRMKDbn4J4i/XjjuThuB4S+4PkmTnZiCbdLXQiBhkVxNlUDcfog5Q==", + "dependencies": { + "@firebase/component": "0.6.7", + "@firebase/logger": "0.4.2", + "@firebase/util": "1.9.6", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/app-check-compat": { + "version": "0.3.11", + "resolved": "https://registry.npmjs.org/@firebase/app-check-compat/-/app-check-compat-0.3.11.tgz", + "integrity": "sha512-t01zaH3RJpKEey0nGduz3Is+uSz7Sj4U5nwOV6lWb+86s5xtxpIvBJzu/lKxJfYyfZ29eJwpdjEgT1/lm4iQyA==", + "dependencies": { + "@firebase/app-check": "0.8.4", + "@firebase/app-check-types": "0.5.2", + "@firebase/component": "0.6.7", + "@firebase/logger": "0.4.2", + "@firebase/util": "1.9.6", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/app-check-interop-types": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@firebase/app-check-interop-types/-/app-check-interop-types-0.3.2.tgz", + "integrity": "sha512-LMs47Vinv2HBMZi49C09dJxp0QT5LwDzFaVGf/+ITHe3BlIhUiLNttkATSXplc89A2lAaeTqjgqVkiRfUGyQiQ==" + }, + "node_modules/@firebase/app-check-types": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@firebase/app-check-types/-/app-check-types-0.5.2.tgz", + "integrity": "sha512-FSOEzTzL5bLUbD2co3Zut46iyPWML6xc4x+78TeaXMSuJap5QObfb+rVvZJtla3asN4RwU7elaQaduP+HFizDA==" + }, + "node_modules/@firebase/app-compat": { + "version": "0.2.35", + "resolved": "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.2.35.tgz", + "integrity": "sha512-vgay/WRjeH0r97/Q6L6df2CMx7oyNFDsE5yPQ9oR1G+zx2eT0s8vNNh0WlKqQxUEWaOLRnXhQ8gy7uu0cBgTRg==", + "dependencies": { + "@firebase/app": "0.10.5", + "@firebase/component": "0.6.7", + "@firebase/logger": "0.4.2", + "@firebase/util": "1.9.6", + "tslib": "^2.1.0" + } + }, + "node_modules/@firebase/app-types": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.9.2.tgz", + "integrity": "sha512-oMEZ1TDlBz479lmABwWsWjzHwheQKiAgnuKxE0pz0IXCVx7/rtlkx1fQ6GfgK24WCrxDKMplZrT50Kh04iMbXQ==" + }, + "node_modules/@firebase/auth": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/@firebase/auth/-/auth-1.7.4.tgz", + "integrity": "sha512-d2Fw17s5QesojwebrA903el20Li9/YGgkoOGJjagM4I1qAT36APa/FcZ+OX86KxbYKCtQKTMqraU8pxG7C2JWA==", + "dependencies": { + "@firebase/component": "0.6.7", + "@firebase/logger": "0.4.2", + "@firebase/util": "1.9.6", + "tslib": "^2.1.0", + "undici": "5.28.4" + }, + "peerDependencies": { + "@firebase/app": "0.x", + "@react-native-async-storage/async-storage": "^1.18.1" + }, + "peerDependenciesMeta": { + "@react-native-async-storage/async-storage": { + "optional": true + } + } + }, + "node_modules/@firebase/auth-compat": { + "version": "0.5.9", + "resolved": "https://registry.npmjs.org/@firebase/auth-compat/-/auth-compat-0.5.9.tgz", + "integrity": "sha512-RX8Zh/3zz2CsVbmYfgHkfUm4fAEPCl+KHVIImNygV5jTGDF6oKOhBIpf4Yigclyu8ESQKZ4elyN0MBYm9/7zGw==", + "dependencies": { + "@firebase/auth": "1.7.4", + "@firebase/auth-types": "0.12.2", + "@firebase/component": "0.6.7", + "@firebase/util": "1.9.6", + "tslib": "^2.1.0", + "undici": "5.28.4" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/auth-interop-types": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@firebase/auth-interop-types/-/auth-interop-types-0.2.3.tgz", + "integrity": "sha512-Fc9wuJGgxoxQeavybiuwgyi+0rssr76b+nHpj+eGhXFYAdudMWyfBHvFL/I5fEHniUM/UQdFzi9VXJK2iZF7FQ==" + }, + "node_modules/@firebase/auth-types": { + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/@firebase/auth-types/-/auth-types-0.12.2.tgz", + "integrity": "sha512-qsEBaRMoGvHO10unlDJhaKSuPn4pyoTtlQuP1ghZfzB6rNQPuhp/N/DcFZxm9i4v0SogjCbf9reWupwIvfmH6w==", + "peerDependencies": { + "@firebase/app-types": "0.x", + "@firebase/util": "1.x" + } + }, + "node_modules/@firebase/component": { + "version": "0.6.7", + "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.6.7.tgz", + "integrity": "sha512-baH1AA5zxfaz4O8w0vDwETByrKTQqB5CDjRls79Sa4eAGAoERw4Tnung7XbMl3jbJ4B/dmmtsMrdki0KikwDYA==", + "dependencies": { + "@firebase/util": "1.9.6", + "tslib": "^2.1.0" + } + }, + "node_modules/@firebase/database": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@firebase/database/-/database-1.0.5.tgz", + "integrity": "sha512-cAfwBqMQuW6HbhwI3Cb/gDqZg7aR0OmaJ85WUxlnoYW2Tm4eR0hFl5FEijI3/gYPUiUcUPQvTkGV222VkT7KPw==", + "dependencies": { + "@firebase/app-check-interop-types": "0.3.2", + "@firebase/auth-interop-types": "0.2.3", + "@firebase/component": "0.6.7", + "@firebase/logger": "0.4.2", + "@firebase/util": "1.9.6", + "faye-websocket": "0.11.4", + "tslib": "^2.1.0" + } + }, + "node_modules/@firebase/database-compat": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-1.0.5.tgz", + "integrity": "sha512-NDSMaDjQ+TZEMDMmzJwlTL05kh1+0Y84C+kVMaOmNOzRGRM7VHi29I6YUhCetXH+/b1Wh4ZZRyp1CuWkd8s6hg==", + "dependencies": { + "@firebase/component": "0.6.7", + "@firebase/database": "1.0.5", + "@firebase/database-types": "1.0.3", + "@firebase/logger": "0.4.2", + "@firebase/util": "1.9.6", + "tslib": "^2.1.0" + } + }, + "node_modules/@firebase/database-types": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-1.0.3.tgz", + "integrity": "sha512-39V/Riv2R3O/aUjYKh0xypj7NTNXNAK1bcgY5Kx+hdQPRS/aPTS8/5c0CGFYKgVuFbYlnlnhrCTYsh2uNhGwzA==", + "dependencies": { + "@firebase/app-types": "0.9.2", + "@firebase/util": "1.9.6" + } + }, + "node_modules/@firebase/firestore": { + "version": "4.6.3", + "resolved": "https://registry.npmjs.org/@firebase/firestore/-/firestore-4.6.3.tgz", + "integrity": "sha512-d/+N2iUsiJ/Dc7fApdpdmmTXzwuTCromsdA1lKwYfZtMIOd1fI881NSLwK2wV4I38wkLnvfKJUV6WpU1f3/ONg==", + "dependencies": { + "@firebase/component": "0.6.7", + "@firebase/logger": "0.4.2", + "@firebase/util": "1.9.6", + "@firebase/webchannel-wrapper": "1.0.0", + "@grpc/grpc-js": "~1.9.0", + "@grpc/proto-loader": "^0.7.8", + "tslib": "^2.1.0", + "undici": "5.28.4" + }, + "engines": { + "node": ">=10.10.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/firestore-compat": { + "version": "0.3.32", + "resolved": "https://registry.npmjs.org/@firebase/firestore-compat/-/firestore-compat-0.3.32.tgz", + "integrity": "sha512-at71mwK7a/mUXH0OgyY0+gUzedm/EUydDFYSFsBoO8DYowZ23Mgd6P4Rzq/Ll3zI/3xJN7LGe7Qp4iE/V/3Arg==", + "dependencies": { + "@firebase/component": "0.6.7", + "@firebase/firestore": "4.6.3", + "@firebase/firestore-types": "3.0.2", + "@firebase/util": "1.9.6", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/firestore-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@firebase/firestore-types/-/firestore-types-3.0.2.tgz", + "integrity": "sha512-wp1A+t5rI2Qc/2q7r2ZpjUXkRVPtGMd6zCLsiWurjsQpqPgFin3AhNibKcIzoF2rnToNa/XYtyWXuifjOOwDgg==", + "peerDependencies": { + "@firebase/app-types": "0.x", + "@firebase/util": "1.x" + } + }, + "node_modules/@firebase/functions": { + "version": "0.11.5", + "resolved": "https://registry.npmjs.org/@firebase/functions/-/functions-0.11.5.tgz", + "integrity": "sha512-qrHJ+l62mZiU5UZiVi84t/iLXZlhRuSvBQsa2qvNLgPsEWR7wdpWhRmVdB7AU8ndkSHJjGlMICqrVnz47sgU7Q==", + "dependencies": { + "@firebase/app-check-interop-types": "0.3.2", + "@firebase/auth-interop-types": "0.2.3", + "@firebase/component": "0.6.7", + "@firebase/messaging-interop-types": "0.2.2", + "@firebase/util": "1.9.6", + "tslib": "^2.1.0", + "undici": "5.28.4" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/functions-compat": { + "version": "0.3.11", + "resolved": "https://registry.npmjs.org/@firebase/functions-compat/-/functions-compat-0.3.11.tgz", + "integrity": "sha512-Qn+ts/M6Lj2/6i1cp5V5TRR+Hi9kyXyHbo+w9GguINJ87zxrCe6ulx3TI5AGQkoQa8YFHUhT3DMGmLFiJjWTSQ==", + "dependencies": { + "@firebase/component": "0.6.7", + "@firebase/functions": "0.11.5", + "@firebase/functions-types": "0.6.2", + "@firebase/util": "1.9.6", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/functions-types": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/@firebase/functions-types/-/functions-types-0.6.2.tgz", + "integrity": "sha512-0KiJ9lZ28nS2iJJvimpY4nNccV21rkQyor5Iheu/nq8aKXJqtJdeSlZDspjPSBBiHRzo7/GMUttegnsEITqR+w==" + }, + "node_modules/@firebase/installations": { + "version": "0.6.7", + "resolved": "https://registry.npmjs.org/@firebase/installations/-/installations-0.6.7.tgz", + "integrity": "sha512-i6iGoXRu5mX4rTsiMSSKrgh9pSEzD4hwBEzRh5kEhOTr8xN/wvQcCPZDSMVYKwM2XyCPBLVq0JzjyerwL0Rihg==", + "dependencies": { + "@firebase/component": "0.6.7", + "@firebase/util": "1.9.6", + "idb": "7.1.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/installations-compat": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/@firebase/installations-compat/-/installations-compat-0.2.7.tgz", + "integrity": "sha512-RPcbD+3nqHbnhVjIOpWK2H5qzZ8pAAAScceiWph0VNTqpKyPQ5tDcp4V5fS0ELpfgsHYvroMLDKfeHxpfvm8cw==", + "dependencies": { + "@firebase/component": "0.6.7", + "@firebase/installations": "0.6.7", + "@firebase/installations-types": "0.5.2", + "@firebase/util": "1.9.6", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/installations-types": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@firebase/installations-types/-/installations-types-0.5.2.tgz", + "integrity": "sha512-que84TqGRZJpJKHBlF2pkvc1YcXrtEDOVGiDjovP/a3s6W4nlbohGXEsBJo0JCeeg/UG9A+DEZVDUV9GpklUzA==", + "peerDependencies": { + "@firebase/app-types": "0.x" + } + }, + "node_modules/@firebase/logger": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.4.2.tgz", + "integrity": "sha512-Q1VuA5M1Gjqrwom6I6NUU4lQXdo9IAQieXlujeHZWvRt1b7qQ0KwBaNAjgxG27jgF9/mUwsNmO8ptBCGVYhB0A==", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/@firebase/messaging": { + "version": "0.12.9", + "resolved": "https://registry.npmjs.org/@firebase/messaging/-/messaging-0.12.9.tgz", + "integrity": "sha512-IH+JJmzbFGZXV3+TDyKdqqKPVfKRqBBg2BfYYOy7cm7J+SwV+uJMe8EnDKYeQLEQhtpwciPfJ3qQXJs2lbxDTw==", + "dependencies": { + "@firebase/component": "0.6.7", + "@firebase/installations": "0.6.7", + "@firebase/messaging-interop-types": "0.2.2", + "@firebase/util": "1.9.6", + "idb": "7.1.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/messaging-compat": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/@firebase/messaging-compat/-/messaging-compat-0.2.9.tgz", + "integrity": "sha512-5jN6wyhwPgBH02zOtmmoOeyfsmoD7ty48D1m0vVPsFg55RqN2Z3Q9gkZ5GmPklFPjTPLcxB1ObcHOZvThTkm7g==", + "dependencies": { + "@firebase/component": "0.6.7", + "@firebase/messaging": "0.12.9", + "@firebase/util": "1.9.6", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/messaging-interop-types": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@firebase/messaging-interop-types/-/messaging-interop-types-0.2.2.tgz", + "integrity": "sha512-l68HXbuD2PPzDUOFb3aG+nZj5KA3INcPwlocwLZOzPp9rFM9yeuI9YLl6DQfguTX5eAGxO0doTR+rDLDvQb5tA==" + }, + "node_modules/@firebase/performance": { + "version": "0.6.7", + "resolved": "https://registry.npmjs.org/@firebase/performance/-/performance-0.6.7.tgz", + "integrity": "sha512-d+Q4ltjdJZqjzcdms5i0UC9KLYX7vKGcygZ+7zHA/Xk+bAbMD2CPU0nWTnlNFWifZWIcXZ/2mAMvaGMW3lypUA==", + "dependencies": { + "@firebase/component": "0.6.7", + "@firebase/installations": "0.6.7", + "@firebase/logger": "0.4.2", + "@firebase/util": "1.9.6", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/performance-compat": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/@firebase/performance-compat/-/performance-compat-0.2.7.tgz", + "integrity": "sha512-cb8ge/5iTstxfIGW+iiY+7l3FtN8gobNh9JSQNZgLC9xmcfBYWEs8IeEWMI6S8T+At0oHc3lv+b2kpRMUWr8zQ==", + "dependencies": { + "@firebase/component": "0.6.7", + "@firebase/logger": "0.4.2", + "@firebase/performance": "0.6.7", + "@firebase/performance-types": "0.2.2", + "@firebase/util": "1.9.6", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/performance-types": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@firebase/performance-types/-/performance-types-0.2.2.tgz", + "integrity": "sha512-gVq0/lAClVH5STrIdKnHnCo2UcPLjJlDUoEB/tB4KM+hAeHUxWKnpT0nemUPvxZ5nbdY/pybeyMe8Cs29gEcHA==" + }, + "node_modules/@firebase/remote-config": { + "version": "0.4.7", + "resolved": "https://registry.npmjs.org/@firebase/remote-config/-/remote-config-0.4.7.tgz", + "integrity": "sha512-5oPNrPFLsbsjpq0lUEIXoDF2eJK7vAbyXe/DEuZQxnwJlfR7aQbtUlEkRgQWcicXpyDmAmDLo7q7lDbCYa6CpA==", + "dependencies": { + "@firebase/component": "0.6.7", + "@firebase/installations": "0.6.7", + "@firebase/logger": "0.4.2", + "@firebase/util": "1.9.6", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/remote-config-compat": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/@firebase/remote-config-compat/-/remote-config-compat-0.2.7.tgz", + "integrity": "sha512-Fq0oneQ4SluLnfr5/HfzRS1TZf1ANj1rWbCCW3+oC98An3nE+sCdp+FSuHsEVNwgMg4Tkwx9Oom2lkKeU+Vn+w==", + "dependencies": { + "@firebase/component": "0.6.7", + "@firebase/logger": "0.4.2", + "@firebase/remote-config": "0.4.7", + "@firebase/remote-config-types": "0.3.2", + "@firebase/util": "1.9.6", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/remote-config-types": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@firebase/remote-config-types/-/remote-config-types-0.3.2.tgz", + "integrity": "sha512-0BC4+Ud7y2aPTyhXJTMTFfrGGLqdYXrUB9sJVAB8NiqJswDTc4/2qrE/yfUbnQJhbSi6ZaTTBKyG3n1nplssaA==" + }, + "node_modules/@firebase/storage": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/@firebase/storage/-/storage-0.12.5.tgz", + "integrity": "sha512-nGWBOGFNr10j0LA4NJ3/Yh3us/lb0Q1xSIKZ38N6FcS+vY54nqJ7k3zE3PENregHC8+8txRow++A568G3v8hOA==", + "dependencies": { + "@firebase/component": "0.6.7", + "@firebase/util": "1.9.6", + "tslib": "^2.1.0", + "undici": "5.28.4" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/storage-compat": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@firebase/storage-compat/-/storage-compat-0.3.8.tgz", + "integrity": "sha512-qDfY9kMb6Ch2hZb40sBjDQ8YPxbjGOxuT+gU1Z0iIVSSpSX0f4YpGJCypUXiA0T11n6InCXB+T/Dknh2yxVTkg==", + "dependencies": { + "@firebase/component": "0.6.7", + "@firebase/storage": "0.12.5", + "@firebase/storage-types": "0.8.2", + "@firebase/util": "1.9.6", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/storage-types": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/@firebase/storage-types/-/storage-types-0.8.2.tgz", + "integrity": "sha512-0vWu99rdey0g53lA7IShoA2Lol1jfnPovzLDUBuon65K7uKG9G+L5uO05brD9pMw+l4HRFw23ah3GwTGpEav6g==", + "peerDependencies": { + "@firebase/app-types": "0.x", + "@firebase/util": "1.x" + } + }, + "node_modules/@firebase/util": { + "version": "1.9.6", + "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.9.6.tgz", + "integrity": "sha512-IBr1MZbp4d5MjBCXL3TW1dK/PDXX4yOGbiwRNh1oAbE/+ci5Uuvy9KIrsFYY80as1I0iOaD5oOMA9Q8j4TJWcw==", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/@firebase/vertexai-preview": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/@firebase/vertexai-preview/-/vertexai-preview-0.0.2.tgz", + "integrity": "sha512-NOOL63kFQRq45ioi5P+hlqj/4LNmvn1URhGjQdvyV54c1Irvoq26aW861PRRLjrSMIeNeiLtCLD5pe+ediepAg==", + "dependencies": { + "@firebase/app-check-interop-types": "0.3.2", + "@firebase/component": "0.6.7", + "@firebase/logger": "0.4.2", + "@firebase/util": "1.9.6", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@firebase/app": "0.x", + "@firebase/app-types": "0.x" + } + }, + "node_modules/@firebase/webchannel-wrapper": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@firebase/webchannel-wrapper/-/webchannel-wrapper-1.0.0.tgz", + "integrity": "sha512-zuWxyfXNbsKbm96HhXzainONPFqRcoZblQ++e9cAIGUuHfl2cFSBzW01jtesqWG/lqaUyX3H8O1y9oWboGNQBA==" + }, "node_modules/@floating-ui/core": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.0.tgz", @@ -6013,6 +6527,17 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "dependencies": { + "websocket-driver": ">=0.5.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/fetch-blob": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", @@ -6090,6 +6615,40 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/firebase": { + "version": "10.12.2", + "resolved": "https://registry.npmjs.org/firebase/-/firebase-10.12.2.tgz", + "integrity": "sha512-ZxEdtSvP1I9su1yf32D8TIdgxtPgxwr6z3jYAR1TXS/t+fVfpoPc/N1/N2bxOco9mNjUoc+od34v5Fn4GeKs6Q==", + "dependencies": { + "@firebase/analytics": "0.10.4", + "@firebase/analytics-compat": "0.2.10", + "@firebase/app": "0.10.5", + "@firebase/app-check": "0.8.4", + "@firebase/app-check-compat": "0.3.11", + "@firebase/app-compat": "0.2.35", + "@firebase/app-types": "0.9.2", + "@firebase/auth": "1.7.4", + "@firebase/auth-compat": "0.5.9", + "@firebase/database": "1.0.5", + "@firebase/database-compat": "1.0.5", + "@firebase/firestore": "4.6.3", + "@firebase/firestore-compat": "0.3.32", + "@firebase/functions": "0.11.5", + "@firebase/functions-compat": "0.3.11", + "@firebase/installations": "0.6.7", + "@firebase/installations-compat": "0.2.7", + "@firebase/messaging": "0.12.9", + "@firebase/messaging-compat": "0.2.9", + "@firebase/performance": "0.6.7", + "@firebase/performance-compat": "0.2.7", + "@firebase/remote-config": "0.4.7", + "@firebase/remote-config-compat": "0.2.7", + "@firebase/storage": "0.12.5", + "@firebase/storage-compat": "0.3.8", + "@firebase/util": "1.9.6", + "@firebase/vertexai-preview": "0.0.2" + } + }, "node_modules/flat-cache": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", @@ -7445,6 +8004,16 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/http-parser-js": { + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", + "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==" + }, + "node_modules/idb": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/idb/-/idb-7.1.1.tgz", + "integrity": "sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==" + }, "node_modules/ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", @@ -11727,6 +12296,14 @@ "react": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, + "node_modules/react-circular-progressbar": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/react-circular-progressbar/-/react-circular-progressbar-2.1.0.tgz", + "integrity": "sha512-xp4THTrod4aLpGy68FX/k1Q3nzrfHUjUe5v6FsdwXBl3YVMwgeXYQKDrku7n/D6qsJA9CuunarAboC2xCiKs1g==", + "peerDependencies": { + "react": "^0.14.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/react-cool-onclickoutside": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/react-cool-onclickoutside/-/react-cool-onclickoutside-1.7.0.tgz", @@ -13935,6 +14512,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/undici": { + "version": "5.28.4", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.28.4.tgz", + "integrity": "sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g==", + "dependencies": { + "@fastify/busboy": "^2.0.0" + }, + "engines": { + "node": ">=14.0" + } + }, "node_modules/undici-types": { "version": "5.26.5", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", @@ -14385,6 +14973,27 @@ "node": ">= 8" } }, + "node_modules/websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "dependencies": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/frontend/package.json b/frontend/package.json index f409b5f..e2b6694 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -24,12 +24,14 @@ "chart.js": "^4.4.2", "date-fns": "^3.4.0", "dotenv": "^16.4.5", + "firebase": "^10.12.2", "flowbite-react": "^0.7.2", "google-map-react": "^2.2.1", "leven": "^4.0.0", "pluralize": "^8.0.0", "react": "^18.2.0", "react-chartjs-2": "^5.2.0", + "react-circular-progressbar": "^2.1.0", "react-cool-onclickoutside": "^1.7.0", "react-dom": "^18.2.0", "react-dropzone": "^14.2.3", diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 523fc95..6b9f6ea 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -13,6 +13,7 @@ import { EditReport } from './components/Controller/DisasterStatus/DisasterEditR import ContactInfoAdder from './pages/controller/ContactInfoAdder'; import { ShelterLocationPage } from './pages/controller/ShelterLocations'; import { VolunteeringPage } from './pages/controller/VolunteeringPage'; +import { DisasterInformationAdder } from './pages/controller/DisasterInformationAdder'; import { DisasterMap } from './pages/controller/DisasterMap'; import SearchResults from './components/Controller/SearchResult'; import PrivateRoute from './authanticate/PrivateRoutes'; diff --git a/frontend/src/Forms/Controller/NewsCreatorForm.jsx b/frontend/src/Forms/Controller/NewsCreatorForm.jsx index f3c2f56..dfeb3d5 100644 --- a/frontend/src/Forms/Controller/NewsCreatorForm.jsx +++ b/frontend/src/Forms/Controller/NewsCreatorForm.jsx @@ -3,6 +3,24 @@ import React, { useEffect, useState } from "react"; import axios from "axios"; import {message} from "antd"; import PropTypes from 'prop-types'; +import { CircularProgressbar } from "react-circular-progressbar"; +import { + Label, + TextInput, + Select, + Checkbox, + FileInput, + Textarea, + Alert, + Button, +} from "flowbite-react"; +import { + getDownloadURL, + getStorage, + ref, + uploadBytesResumable, +} from "firebase/storage"; +import app from "../../firebase"; const NewsCreatorForm = ({selection}) => { @@ -14,18 +32,26 @@ const NewsCreatorForm = ({selection}) => { newsBody:'', show: '' }); + const [imageFile, setImageFiles] = useState(null); + // const [imageFileUrl, setImageFileUrl] = useState(null); + const [imageUploadProgress, setImageUploadProgress] = useState(null); + const [imageUploadError, setImageUploadError] = useState(null); + const buttonStyle = { + background: 'linear-gradient(to right, #124E70, #5CDB95)', + } useEffect(() =>{ if (selection){ setFormData({ newsId: selection.newsId || '', heading: selection.heading || '', author: selection.author || '', - image: selection.author || null, + image: selection.image || '', newsBody: selection.newsBody || '', show: selection.show || '' }); } - console.log("selectionData:",selection) + console.log("selection Author:", formData.author) + console.log("selection Image:", formData.image) },[selection]); const handleChange = (e) => { @@ -45,25 +71,14 @@ const NewsCreatorForm = ({selection}) => { const handleSubmit = async(e) => { e.preventDefault(); - if (!formData.heading || !formData.author || !formData.newsBody || !formData.newsBody) { + if (!formData.heading || !formData.author || !formData.newsBody ) { message.error('Missing required fields'); return; } else{ - const formDataObj = new FormData(); - formDataObj.append('heading', formData.heading); - formDataObj.append('author', formData.author); - formDataObj.append('newsBody', formData.newsBody); - if (formData.image) { - formDataObj.append('image', formData.image); - } try{ - const response = await axios.post('http://localhost:5000/api/news/createNews', formDataObj, { - headers: { - 'Content-Type': 'multipart/form-data' - } - }); + const response = await axios.post('http://localhost:5000/api/news/createNews', {...formData}); console.log('Form submitted succedded: ', response.data); message.success('News is created!') setFormData({ @@ -90,16 +105,15 @@ const NewsCreatorForm = ({selection}) => { } else{ try{ - const formDataObj = new FormData(); - formDataObj.append('newsId', formData.newsId); - formDataObj.append('heading', formData.heading); - formDataObj.append('author', formData.author); - formDataObj.append('newsBody', formData.newsBody); - if (formData.image) { - formDataObj.append('image', formData.image); - } + const currentImageURL = selection.image; + const imageURL = formData.image? formData.image : currentImageURL; - const response = await axios.patch('http://localhost:5000/api/news/updateNews/' + formData.newsId, formDataObj); + const response = await axios.patch('http://localhost:5000/api/news/updateNews/' + formData.newsId, { + heading:formData.heading, + newsBody:formData.newsBody, + author:formData.author, + image: imageURL, + }); console.log('Form update succeeded: ', response.data); setFormData({ ...formData, @@ -117,6 +131,43 @@ const NewsCreatorForm = ({selection}) => { } } + const uploadImage = async () => { + try { + if (!imageFile) { + setImageUploadError("Please select an image file"); + return; + } + setImageUploadError(null); + const storage = getStorage(app); + const fileName = new Date().getTime() + imageFile.name; + const storageRef = ref(storage, fileName); + const uploadTask = uploadBytesResumable(storageRef, imageFile); + uploadTask.on( + "state_changed", + (snapshot) => { + const progress = + (snapshot.bytesTransferred / snapshot.totalBytes) * 100; + setImageUploadProgress(progress.toFixed(0)); + }, + (error) => { + setImageUploadError( + "Could not upload the image file must be less than 10MB" + ); + }, + () => { + getDownloadURL(uploadTask.snapshot.ref).then((downloadURL) => { + setImageUploadProgress(null); + setImageUploadError(null); + setFormData({...formData, image: downloadURL}); + }); + } + ); + } catch (err) { + setImageUploadError("An error occurred while uploading the image"); + setImageUploadProgress(null); + } + }; + return (
@@ -143,15 +194,57 @@ const NewsCreatorForm = ({selection}) => { placeholder="Enter Name" className="bg-gray-50 border border-gray-300 text-black text-sm rounded-lg focus:ring-[Gray] focus:border-[gray] block w-[50%] p-2.5 my-2" > - + {/* */} {/* */} - handleChange(e)} className="bg-gray-50 border border-gray-300 text-black text-sm rounded-lg focus:ring-[Gray] focus:border-[gray] block w-[50%] p-2.5 my-2" - > + > */} +
+
+
+
+ setImageFiles(e.target.files[0])} + /> + +
+
+ {imageUploadError && ( + {imageUploadError} + )} + {formData.image && ( + uploaded image + )} +
@@ -202,3 +295,14 @@ NewsCreatorForm.propTypes = { selection: PropTypes.any.isRequired // Adjust the PropTypes type according to your needs }; export default NewsCreatorForm; + + + + + + + + + + + diff --git a/frontend/src/Windows/RequestWindow.jsx b/frontend/src/Windows/RequestWindow.jsx index 7e9e29f..7b9a957 100644 --- a/frontend/src/Windows/RequestWindow.jsx +++ b/frontend/src/Windows/RequestWindow.jsx @@ -3,6 +3,7 @@ import { MdPermMedia } from "react-icons/md"; import { BackButton } from "../components/Common/BackButton"; import { APIProvider, Map, Marker } from "@vis.gl/react-google-maps"; import { LocationMap } from "../components/Controller/Requests/LocationMap"; +import { Button } from "flowbite-react"; export const WindowComponent = ({ requestID, @@ -13,8 +14,10 @@ export const WindowComponent = ({ medicalNeed, otherNeeds, verification, + image, locationLatLan, }) => { + const [clickButton,setclickButton] = useState(); const lat = parseFloat(locationLatLan[0]); const lng = parseFloat(locationLatLan[1]); const position = { lat: lat, lng: lng }; @@ -23,6 +26,7 @@ export const WindowComponent = ({ console.log("position",position); console.log("requestDetails:", requestID); console.log("requestLocationLatLon:", locationLatLan); + console.log("Media images", image) }, [requestID] ); @@ -106,19 +110,18 @@ export const WindowComponent = ({ id="country-option-1" className="block p-2.5 text-sm w-full text-gray-600 bg-gray-50 rounded-lg border border-b-4 border-gray-400" > - {medicalNeed.toString()} + {medicalNeed? "Medical needs required.":"Medical needs not required"}
- {otherNeeds} + > {otherNeeds && otherNeeds !== "" ? otherNeeds : "No other needs"}
@@ -127,14 +130,25 @@ export const WindowComponent = ({ className="flex items-center justify-center flex-col w-full border border-b-4 border-gray-400 rounded-lg cursor-pointer bg-gray-50 hover:bg-gray-100 " >
- {} - -

- Click to view -

-

- Watch uploaded images or videos -

+ {clickButton?( + (image && image.length > 0 && ( +
+ {image.map((image, index) => ( + {`Request + ))} +
+ )) + ):( +
+ +

+ Watch uploaded images or videos +

+

+ +

+
+ )}
diff --git a/frontend/src/components/Controller/DisasterInformation/DisasterInformationPreview.jsx b/frontend/src/components/Controller/DisasterInformation/DisasterInformationPreview.jsx new file mode 100644 index 0000000..0c344af --- /dev/null +++ b/frontend/src/components/Controller/DisasterInformation/DisasterInformationPreview.jsx @@ -0,0 +1,279 @@ +import React, {useEffect, useState, useReducer} from "react" +import { getNews } from "../../../services/newsServices"; +import { deleteNewsItem } from "../../../services/newsServices"; +import { message } from "antd"; +import NewsCreatorForm from "../../../Forms/Controller/NewsCreatorForm"; +import { FormControlLabel } from "@mui/material"; +import Switch from '@mui/material/Switch'; +import { alpha, styled } from '@mui/material/styles'; +import { green } from '@mui/material/colors'; +import axios from "axios"; +import { Buffer } from 'buffer'; + +const GreenSwitch = styled(Switch)(({ theme }) => ({ + '& .MuiSwitch-switchBase.Mui-checked': { + color: green[900], + '&:hover': { + backgroundColor: alpha(green[600], theme.palette.action.hoverOpacity), + }, + }, + '& .MuiSwitch-switchBase.Mui-checked + .MuiSwitch-track': { + backgroundColor: green[600], + }, +})); + +const intialState = { newsItems: []}; +const reducer = (state, action) => { + switch (action.type) { + case 'News_Loaded': + return {...state, newsItems: action.payload}; + default: + return state; + } +}; + + +export const DisasterInformationPreview = () =>{ + const [selectedNews, setSelectedNews] = useState(null); + const [state,dispatch] = useReducer(reducer, intialState); + const {newsItems} = state; + const [showMoreItems, setShowMoreItems] = useState(false); + const [ selectedEdit, setSelectedEdit] = useState(null); + const [IsShow, setShow] = useState(false); + + const [ formData, setFormData ] = useState({ + newsId:'', + heading:'', + author:'', + image:'', + newsBody:'', + show: '' + }); + + useEffect(() => { + if (selectedNews){ + setFormData({ + newsId: selectedNews.newsId || '', + heading: selectedNews.heading || '', + author: selectedNews.author || '', + image: selectedNews.image || '', + newsBody: selectedNews.newsBody || '', + show: selectedNews.show || '' + }); + } + + const loadNews = getNews(); + console.log("NewsDetails:", selectedNews); + console.log("FormDetails:", formData); + loadNews.then(newsItems => { + // Sort requests by date and time in descending order + const timeSortedNews = newsItems.sort((a, b) => { + const dateComparison = new Date(b.createdDate) - new Date(a.createdDate); + //const converted = requestTime.split(' ')[0].split(':'); + if (dateComparison !== 0) { + return dateComparison; + } + const timeA = (a.createdTime).split(' ')[0]; + const timeB = (b.createdTime).split(' ')[0]; + return timeB.localeCompare(timeA); + + }); + const trueItems = timeSortedNews.filter(item => item.show); + const falseItems = timeSortedNews.filter(item => !item.show); + const sortedNews = [...trueItems, ...falseItems]; + dispatch({ type: 'News_Loaded', payload: sortedNews }); + }); + + }, + [selectedNews]); + + const handleCardClick = (news) => { + setSelectedNews(news); + console.log("Selected News:", news); + }; + + const handleEdit = (selectedNews) => { + setSelectedEdit(selectedNews); + console.log("NewstobeEdited:", selectedNews); + }; + + const handleShowToggle = async (e) => { + e.preventDefault(); + const newShowValue = !formData.show; + + // Optimistically update the UI + setFormData({ ...formData, show: newShowValue }); + + try { + // const response = await axios.patch('http://localhost:5000/api/news/updateNews/' + formData.newsId, {...formData, show:newShowValue}); + message.success(`Show is ${newShowValue ? 'enabled' : 'disabled'} now!`); + setTimeout(() => { + window.location.reload(); + }, 2000); + } catch (error) { + console.error('Error updating show:', error); + message.error('Failed to update show status!'); + + // Revert the UI update if the API call fails + setFormData({ ...formData, show: !newShowValue }); + } + }; + + const handleDelete = async (newsId) => { + try { + await deleteNewsItem(selectedNews.newsId); + dispatch({ + type: "News_Loaded", + payload: newsItems.filter((item) => item.newsId !== newsId), + }); + setSelectedNews(null); + console.log("News item deleted successfully"); + message.success("Successfully deleted the news item!") + } catch (error) { + console.error("Error deleting news item:", error); + message.error("Deletion failed!") + } + }; + + const showMore = () => { + setShowMoreItems(true); + + }; + const showLess = () => { + setShowMoreItems(false); + + }; + + return( +
+
+

News Preview

+
+ {/* left Section */} +
+ {selectedNews ? + ( +
+

+ {selectedNews.heading} +

+

{selectedNews.author}

+
+

{selectedNews.createdTime.split(' ')[0]}

+

{selectedNews.createdDate}

+
+ +
+ {selectedNews.image && ( + {selectedNews.heading} + )} +
+ +
+ ') }} className="text-black text-sm leading-7 font-serif text-justify h-auto"/> +
+ + {/* Edit and delete Buttons */} +
+ + } + label={(IsShow? "Disable Show" : "Enable Show")} + name="Show switch" + id="Show switch" + className="bg-transparent p-2 rounded" + + /> + + +
+
+ ): ( +

+ Select a news item to view details. +

+ ) + } +
+ {/* Right Section */} +
+
+

+ Recent News +

+ {/* News item */} +
+ + {Array.isArray(newsItems) && newsItems.map((news, i) => ( + +
handleCardClick(news)}> +
+
+ {news.image && ( + {news.heading} + )} +
+
+
+

+ {news.heading} +

+ +
+

{news.author}

+

{news.createdDate}

+

Display :{(news.show).toString()}

+
+
+
+
+ ))} +
+
+ +
+
+
+
+
+
+ {selectedEdit? ( + + ):( + + ) + } +
+
+ ) +} \ No newline at end of file diff --git a/frontend/src/components/Controller/DisasterStatus/DisasterEditReport.jsx b/frontend/src/components/Controller/DisasterStatus/DisasterEditReport.jsx index 531a33e..cda3f2e 100644 --- a/frontend/src/components/Controller/DisasterStatus/DisasterEditReport.jsx +++ b/frontend/src/components/Controller/DisasterStatus/DisasterEditReport.jsx @@ -3,9 +3,12 @@ import { useNavigate } from "react-router-dom"; import { HeaderBar } from "../HeaderBar"; import { LanguageBar } from "../LanguageBar"; import TextField from '@mui/material/TextField'; -import { Button, Snackbar } from "@mui/material"; +import { Alert, Button, Snackbar } from "@mui/material"; +import LoadingButton from '@mui/lab/LoadingButton'; +import SyncAltIcon from '@mui/icons-material/SyncAlt'; import Autocomplete from '@mui/material/Autocomplete'; import { editReport, getCurrentReports, insertReport } from "../../../services/reportService"; +import { getUnverifiedRequests, setVerifyRequests } from "../../../services/requestService"; const disasterReports = [ { reportID: "id_1", reportName: 'report_1' }, @@ -21,6 +24,9 @@ export const EditReport = () => { const [selectedReport, setSelectedReport] = useState(null); const [selectedInputReport, setSelectedInputReport] = useState(); const [snackMessage, setSnackMessage] = useState({ message: "", severity: "" }); + const [numOfRequests, setNumOfRequests] = useState(0); + const [disable, setDisable] = useState(false); + const [sync, setSync] = useState(false); const navigate = useNavigate(); @@ -33,13 +39,56 @@ export const EditReport = () => { setSelectedInputReport((prevDetails) => ({ ...prevDetails, [name]: value })); } - const handleRequests = () => { + const handleRequests = async (date) => { + const getRequests = async () => { + const requests = await getUnverifiedRequests(); + console.log("requests: ", requests); + const today = new Date(); + if (requests != null) { + const filteredRequests = requests + .filter(request => (request.disasterType == selectedInputReport.disasterType) && (request.disasterLocation == selectedInputReport.disasterLocation)) + .map(request => request.requestID); + + console.log("filtered requests: ", filteredRequests); + + return filteredRequests; + } else { + return 0; + } + } + if (selectedInputReport.disasterType != null && selectedInputReport.disasterLocation != null) { + setDisable(true); + setSync(true); + const newRequests = await getRequests(date); + let requests = selectedInputReport.disasterRequests; + if(newRequests != 0){ + requests = [...selectedInputReport.disasterRequests, ...newRequests]; + } + console.log("AllRequests: ", requests); + setNumOfRequests(selectedInputReport.disasterRequests.length + newRequests.length); + setSelectedInputReport((prevDetails) => ({ ...prevDetails, ["disasterRequests"]: requests })); + + setSync(false); + + if (requests.length == 0) { + const message = { message: "No current requests!", severity: "error" }; + setSnackMessage(message); + } + console.log("Sync success"); + } else { + const message = { message: "Please provide disaster type and location!", severity: "error" }; + setSnackMessage(message); + } } const onSubmitForm = async (e) => { e.preventDefault(); try { + if (selectedInputReport.disasterRequests.length != 0) { + const resultVerify = await setVerifyRequests(selectedInputReport.disasterRequests); + console.log("Request verify result: ", resultVerify); + } const result = await editReport(selectedInputReport); console.log("Submit form result: ", result); setOpen(true); @@ -79,7 +128,10 @@ export const EditReport = () => { }, [reports]) useEffect(() => { - if (selectedInputReport) console.log("Report to edit: ", selectedInputReport); + if (selectedInputReport) { + console.log("Report to edit: ", selectedInputReport); + setNumOfRequests(selectedInputReport.disasterRequests.length) + } }, [selectedInputReport]) return ( @@ -161,7 +213,7 @@ export const EditReport = () => { value={selectedInputReport.confirmed} onChange={handleChange} required - disabled = {selectedInputReport.confirmed ?? true} + disabled={selectedInputReport.confirmed ?? true} > @@ -179,7 +231,7 @@ export const EditReport = () => { value={selectedInputReport.affectedCount} onChange={handleChange} required />
Total Requests
-
{selectedInputReport.disasterRequests.length}
+
{numOfRequests}
@@ -208,10 +260,14 @@ export const EditReport = () => {
- + } + loading={sync} + loadingPosition="end" + variant="contained" + > Sync Requests
} diff --git a/frontend/src/components/Controller/DisasterStatus/DisasterReportForm.jsx b/frontend/src/components/Controller/DisasterStatus/DisasterReportForm.jsx index 976a827..60fa2e8 100644 --- a/frontend/src/components/Controller/DisasterStatus/DisasterReportForm.jsx +++ b/frontend/src/components/Controller/DisasterStatus/DisasterReportForm.jsx @@ -29,6 +29,7 @@ export const ReportForm = () => { const extractLatLang = async(location) =>{ const result = await getGeoCode(location); + console.log(result); const lat = result.results[0].geometry.location.lat; const lang = result.results[0].geometry.location.lng; const marker = {latitude:lat, longitude:lang}; @@ -96,6 +97,7 @@ export const ReportForm = () => { if (details.disasterType != null && details.severity != null && details.disasterLocation != null && details.affectedCount != null && details.finished != null) { try { + console.log("Details: ", details); const result = await insertReport(details); if (details.disasterRequests.length != 0) { @@ -132,6 +134,12 @@ export const ReportForm = () => { } }, [snackMessage]) + useEffect(()=> { + if(details != null){ + console.log("Details: ", details); + } + },[details]) + return (
diff --git a/frontend/src/components/Controller/DisasterStatus/DisasterStatusBar.jsx b/frontend/src/components/Controller/DisasterStatus/DisasterStatusBar.jsx index df7cc70..8125165 100644 --- a/frontend/src/components/Controller/DisasterStatus/DisasterStatusBar.jsx +++ b/frontend/src/components/Controller/DisasterStatus/DisasterStatusBar.jsx @@ -115,7 +115,7 @@ export const DisasterStatusBar = (props) => { newReportCount != 0 &&
- {newReportCount} new Reports added within 7 days + {newReportCount} new Reports
} Last updated 1min ago diff --git a/frontend/src/components/Controller/DisasterStatus/Disaster_Map.jsx b/frontend/src/components/Controller/DisasterStatus/Disaster_Map.jsx index 5795d0b..fa85502 100644 --- a/frontend/src/components/Controller/DisasterStatus/Disaster_Map.jsx +++ b/frontend/src/components/Controller/DisasterStatus/Disaster_Map.jsx @@ -9,6 +9,8 @@ import { TestReport_Card } from "./testReportCard"; import { GoogleMap } from "./Disaster_GoogleMap"; import { BarChart } from '@mui/x-charts/BarChart'; import { ModelAffectedAdd } from "./ModelAffectedAdd"; +import { checkUnverifyRequests } from "../../../services/requestService"; +import { sendRespond } from "../../../services/emailServices"; //import { MapContainer } from "./Disaster_GoogleMap"; const testValues = [ @@ -51,6 +53,7 @@ export const Disaster_Map = (fetchedReports, currentReports) => { const [ongoingReports, setOngoingReports] = useState(''); const [selectedReport, setSelectedReport] = useState(''); const [addAffected, setAddAffected] = useState(false); + const [showRespondAVB, setShowRespondAVB] = useState(false); const formRef = useRef(null); const addAffectedClose = () => { @@ -61,7 +64,6 @@ export const Disaster_Map = (fetchedReports, currentReports) => { if (fetchedReports) { setReports(fetchedReports); setSelectedReport(fetchedReports[0]); - console.log("Selected Report: ", selectedReport); } }, [fetchedReports]); @@ -69,7 +71,6 @@ export const Disaster_Map = (fetchedReports, currentReports) => { if (currentReports) { setOngoingReports(currentReports); setSelectedReport(currentReports[0]); - console.log("Selected Report: ", selectedReport); } }, [currentReports]); @@ -78,14 +79,50 @@ export const Disaster_Map = (fetchedReports, currentReports) => { if (formRef.current) { formRef.current.scrollIntoView({ behavior: 'smooth' }); } - console.log("Selected Report: ", selectedReport); }; - const handlePenClick = (value) => { - if (value == "Alert") console.log("Alert button pressed !"); - else console.log("Respond button pressed !"); + const handlePenClick = async (value) => { + if (value == "Alert") { + console.log("Alert button pressed !"); + } + else { + try { + await sendRespond(selectedReport.disasterRequests); + console.log("Respond send success!"); + } catch (error) { + console.log("Respond send error!"); + } + } }; + useEffect(() => { + + if (selectedReport != "") { + const checkNewRequest = async () => { + if (!selectedReport.respondSent) { + if (await checkUnverifyRequests(selectedReport.disasterRequests)) { + setShowRespondAVB(true); + } else { + setShowRespondAVB(false); + } + console.log("Respond: ", showRespondAVB); + } + + } + checkNewRequest(); + } + }, [selectedReport]) + + const handleRespond = async () => { + + if (showRespondAVB) { + console.log("Respond Sent"); + } else { + console.log("No new requests"); + } + + } + return (
SELECT DISASTER TO SHOW DETAILS
@@ -106,11 +143,11 @@ export const Disaster_Map = (fetchedReports, currentReports) => { { selectedReport &&
-
+
{Disaster_Map_Card("DISASTER TYPE", selectedReport.disasterType)} {Disaster_Map_Card("SEVERITY", selectedReport.severity)} -
+
TOTAL AFFECTED
{selectedReport.affectedCount}
@@ -118,9 +155,9 @@ export const Disaster_Map = (fetchedReports, currentReports) => {
-
+
AFFECTED LOCATIONS
-
    +
      {selectedReport.disasterLocation.map(name =>
    • {name}
    • )} @@ -128,10 +165,10 @@ export const Disaster_Map = (fetchedReports, currentReports) => {
-
+
TOTAL REQUESTS
-
1522
+
{selectedReport.disasterRequests.length}
12%
@@ -142,8 +179,8 @@ export const Disaster_Map = (fetchedReports, currentReports) => { -
-
+
+
Afftected Details
{ />
@@ -167,7 +204,7 @@ export const Disaster_Map = (fetchedReports, currentReports) => {
{Disaster_Map_Card("SHELTER LOCATIONS", "None")} -
+
RESPOND SETTINGS
@@ -193,8 +230,8 @@ export const Disaster_Map = (fetchedReports, currentReports) => {
-
-
RESPOND SETTINGS
+
+
ADDITIONAL SETTINGS
EVACUATION ROUTES
diff --git a/frontend/src/components/Controller/Home/Menues.jsx b/frontend/src/components/Controller/Home/Menues.jsx index b525af2..bdc3621 100644 --- a/frontend/src/components/Controller/Home/Menues.jsx +++ b/frontend/src/components/Controller/Home/Menues.jsx @@ -7,7 +7,7 @@ export const Menues = () => { title: "Family Emergency Plans", description: "Tool to create plans during the Emergency.", image: "/controller/Full Family.png", - url: "/controller/home" + url: "/controller/information" }, { id: 2, diff --git a/frontend/src/components/Controller/News/NewsPreview.jsx b/frontend/src/components/Controller/News/NewsPreview.jsx index a97cdad..450ba74 100644 --- a/frontend/src/components/Controller/News/NewsPreview.jsx +++ b/frontend/src/components/Controller/News/NewsPreview.jsx @@ -105,15 +105,7 @@ export const NewsPreview = () =>{ setFormData({ ...formData, show: newShowValue }); try { - const updatedFormData = new FormData(); - updatedFormData.append('show', newShowValue); - updatedFormData.append('heading', formData.heading); - updatedFormData.append('author', formData.author); - updatedFormData.append('newsBody', formData.newsBody); - if (formData.image) { - updatedFormData.append('image', formData.image); - } - const response = await axios.patch('http://localhost:5000/api/news/updateNews/' + formData.newsId, updatedFormData); + const response = await axios.patch('http://localhost:5000/api/news/updateNews/' + formData.newsId, {...formData, show:newShowValue}); message.success(`Show is ${newShowValue ? 'enabled' : 'disabled'} now!`); setTimeout(() => { window.location.reload(); @@ -172,9 +164,9 @@ export const NewsPreview = () =>{
- {selectedNews.image && selectedNews.image.data && ( + {selectedNews.image && ( {selectedNews.heading} @@ -238,9 +230,9 @@ export const NewsPreview = () =>{
handleCardClick(news)}>
- {news.image && news.image.data && ( + {news.image && ( {news.heading} diff --git a/frontend/src/components/Controller/Requests/Card_Requests.jsx b/frontend/src/components/Controller/Requests/Card_Requests.jsx index ce63fda..e5c11b0 100644 --- a/frontend/src/components/Controller/Requests/Card_Requests.jsx +++ b/frontend/src/components/Controller/Requests/Card_Requests.jsx @@ -6,7 +6,7 @@ export const Card_Requests = ({ Time, Date, Location, - AffectedPeople, + AffectedPeople }) => { return (
diff --git a/frontend/src/components/Controller/Requests/RequestsCards.jsx b/frontend/src/components/Controller/Requests/RequestsCards.jsx deleted file mode 100644 index e69de29..0000000 diff --git a/frontend/src/components/Controller/Requests/RequestsDetails.jsx b/frontend/src/components/Controller/Requests/RequestsDetails.jsx index 90ea0a8..458a74e 100644 --- a/frontend/src/components/Controller/Requests/RequestsDetails.jsx +++ b/frontend/src/components/Controller/Requests/RequestsDetails.jsx @@ -76,6 +76,26 @@ export const RequestsDetails = ({flood,tsunami,fire,wind,other,today, monthly}) return eachRequest.read === false; }) : []; + const monthlyAccepted = Array.isArray(monthly) + ? monthly.filter(eachRequest => { + return eachRequest.verify === true; + }) + : []; + const monthlyNotAccepted = Array.isArray(monthly) + ? monthly.filter(eachRequest => { + return eachRequest.verify === false; + }) + : []; + const todayAccepted = Array.isArray(today) + ? today.filter(eachRequest => { + return eachRequest.verify === true; + }) + : []; + const todayNotAccepted = Array.isArray(today) + ? today.filter(eachRequest => { + return eachRequest.verify === false; + }) + : []; return(
@@ -83,7 +103,7 @@ export const RequestsDetails = ({flood,tsunami,fire,wind,other,today, monthly}) Daily Forcast
-
+
Read Requests
-
+
Accepted Requests - Analysis + {/* Analysis */} + (index === 0 ? 2 : 0)} + slotProps={{ + legend: { + direction: 'row', + position: { vertical: 'middle', horizontal: 'middle' }, + padding: 0, + labelStyle: { + fontSize: 35, + fill: 'black', + fontWeight: 'bolder' + }, + itemMarkWidth: 0, + itemMarkHeight: 0, + }, + }} + />
@@ -193,7 +253,7 @@ export const RequestsDetails = ({flood,tsunami,fire,wind,other,today, monthly})
-
+
Read Requests
-
- Analysis +
+ {/* Analysis */} + (index === 0 ? 2 : 0)} + slotProps={{ + legend: { + direction: 'row', + position: { vertical: 'middle', horizontal: 'middle' }, + padding: 0, + labelStyle: { + fontSize: 35, + fill: 'black', + fontWeight: 'bolder' + }, + itemMarkWidth: 0, + itemMarkHeight: 0, + }, + }} + /> Accepted Requests
diff --git a/frontend/src/components/Controller/UserControl/UsersCount.jsx b/frontend/src/components/Controller/UserControl/UsersCount.jsx index 9be667a..a25180d 100644 --- a/frontend/src/components/Controller/UserControl/UsersCount.jsx +++ b/frontend/src/components/Controller/UserControl/UsersCount.jsx @@ -59,6 +59,12 @@ export const UserCount = (activeUsersDate,DisctrictUsersDate, activeAdminsDate, getActiveDetails(); },[allData]) + useEffect(()=>{ + if(userActive != null){ + console.log("User Active: ", userActive); + } + },[userActive]) + return(
@@ -73,10 +79,10 @@ export const UserCount = (activeUsersDate,DisctrictUsersDate, activeAdminsDate,
{ - CountCard("USERS", "Total Users Count",3,newData) + CountCard("USERS", "Total Users Count",1,newData) } { - CountCard("ADMINS", "Total Admins Count",1, newData) + CountCard("ADMINS", "Total Admins Count",3, newData) }
diff --git a/frontend/src/components/Controller/UserControl/Users_Analysis.jsx b/frontend/src/components/Controller/UserControl/Users_Analysis.jsx index e6853dc..75f4621 100644 --- a/frontend/src/components/Controller/UserControl/Users_Analysis.jsx +++ b/frontend/src/components/Controller/UserControl/Users_Analysis.jsx @@ -12,13 +12,15 @@ const exData = [ export const UsersAnalysis = () => { + const [allData, setAllData] = useState([]); const [newData, setNewData] = useState([]); + const [newUsers, setNewUsers] = useState([]); useEffect ( () => { const fetchUserData = async () => { try{ const data = await getAllUsers(); - + setAllData(data); const groupedData = data.reduce((acc,user) => { const department = user.department; @@ -41,17 +43,40 @@ export const UsersAnalysis = () => { fetchUserData(); },[]); + useEffect(()=>{ + if(allData != null){ + const today = new Date(); + let nUser = 0, nAdmin = 0; + for(const us of allData){ + const diff = today - new Date(us.createdAt); + if(diff/(1000*60*60*24) < 7 ){ + if(us.accessLevel == 1){ + console.log(us.userName); + nUser++; + }else{ + nAdmin++; + } + } + } + + setNewUsers([ + {label: "Users", value: nUser}, + {label: "Admins", value: nAdmin}, + ]); + } + },[allData]) + useEffect(()=> { if(newData.length != 0) console.log("newData:",newData); },[newData]); return(
- { + {/* { AnalysisCard("Users By District","Each Users divided by districts",exData) - } + } */} { - AnalysisCard("New users and admin registration","Newly registered users and appointed admins",exData) + AnalysisCard("New users and admin registration","Newly registered users and appointed admins",newUsers) } { AnalysisCard("Admins by department","Each admins divided by departments shown below", newData) diff --git a/frontend/src/components/Controller/UserControl/Users_CountCard.jsx b/frontend/src/components/Controller/UserControl/Users_CountCard.jsx index c78b68c..bd708e4 100644 --- a/frontend/src/components/Controller/UserControl/Users_CountCard.jsx +++ b/frontend/src/components/Controller/UserControl/Users_CountCard.jsx @@ -7,12 +7,13 @@ export const CountCard = (title, description,accessLevel, users) => { useEffect(() => { if(users){ if(accessLevel == 1){ - const adminCount = users[1].count + users[2].count; + const adminCount = users[1].count; setUserCount(adminCount); } else { - const normalCount = users[3].count; + const normalCount = users[3].count + users[2].count; setUserCount(normalCount); } + console.log("GetCount:", users); } },[users]); diff --git a/frontend/src/components/Controller/shelters/RowCardShelter.jsx b/frontend/src/components/Controller/shelters/RowCardShelter.jsx index 9ce34c3..fdef232 100644 --- a/frontend/src/components/Controller/shelters/RowCardShelter.jsx +++ b/frontend/src/components/Controller/shelters/RowCardShelter.jsx @@ -20,15 +20,15 @@ export const RowCardShelter = ({ shelterItem, shelterDelete, shelterEdit }) => { { col: "Shelter ID", value: shelterItem.shelterId, width: "120px" }, { col: "Shelter Name", value: shelterItem.shelterName, width: "250px" }, { col: "Shelter Type", value: shelterItem.shelterType, width: "200px" }, - { col: "Location", value: shelterItem.location, width: "300px" }, - { col: "Person In Charge", value: shelterItem.personInCharge, width: "250px" }, + { col: "Location", value: shelterItem.location, width: "350px" }, + { col: "Person In Charge", value: shelterItem.personInCharge, width: "230px" }, { col: "Phone Number", value: shelterItem.phoneNumber, width: "150px" }, ]; setShelter(Data); }, []) return ( -
+
{ shelter.map((item, index) => (
{item.value}
diff --git a/frontend/src/components/Controller/shelters/ShelterAddFormTest.jsx b/frontend/src/components/Controller/shelters/ShelterAddFormTest.jsx index c8542aa..6808479 100644 --- a/frontend/src/components/Controller/shelters/ShelterAddFormTest.jsx +++ b/frontend/src/components/Controller/shelters/ShelterAddFormTest.jsx @@ -8,8 +8,8 @@ export const ShelterAddFormTest = React.forwardRef(function (props, ref) { const { initialDetails, handleClose } = props; - const [ open, setOpen ] = useState(false); - const [ snackMessage, setSnackMessage ] = useState({message:"", severity:""}); + const [open, setOpen] = useState(false); + const [snackMessage, setSnackMessage] = useState({ message: "", severity: "" }); const [details, setDetails] = useState(initialDetails || { @@ -106,7 +106,10 @@ export const ShelterAddFormTest = React.forwardRef(function (props, ref) {
- +
+ + +
{ + return ( +
+ + + +
+
+ ); +} diff --git a/frontend/src/pages/controller/Requests.jsx b/frontend/src/pages/controller/Requests.jsx index 73e4640..c351a9f 100644 --- a/frontend/src/pages/controller/Requests.jsx +++ b/frontend/src/pages/controller/Requests.jsx @@ -13,7 +13,7 @@ import { BarSlideShow } from "../../components/Controller/Requests/BarSlideShow" import { FaXmark, FaBars } from "react-icons/fa6"; const intialState = { - requests: [], + requests: [] }; const reducer = (state, action) => { @@ -51,12 +51,12 @@ export const Requests = () => { otherRequests: [], allReadRequests: [], todayReadRequests: [], - monthlyReadRequests: [], + monthlyReadRequests: [] }); const disasters = ["flood", "tsunami", "fire", "wind", "landslide"]; useEffect(() => { const loadRequests = getRequests(); - console.log("Requests", requests); + console.log("Requests", loadRequests); loadRequests.then(requests => { // Sort requests by date and time in descending order const sortedRequests = requests.sort((a, b) => { @@ -103,7 +103,7 @@ export const Requests = () => { request.requestDate.split(" ")[1] === new Date().toDateString().split(" ")[1] ); - const allRead = requests.filter(request => request.read === true); + const allRead = requests.filter(request => request.read === false); const todayRead = requests.filter( request => request.read === true && @@ -132,55 +132,77 @@ export const Requests = () => { otherRequests: other, allReadRequests: allRead, todayReadRequests: todayRead, - monthlyReadRequests: monthlyRead, + monthlyReadRequests: monthlyRead }); }); }, []); + // const handleCardClick = async request => { + // try { + // const updatedRequest = { ...request, read: true }; + // setSelectedRequest(updatedRequest); + // await axios.put( + // `http://localhost:5000/api/requests/updateRequest/${request.requestID}`, + // updatedRequest + // ); + // const updatedRequests = await getRequests(); + // setShowRequests(updatedRequests); + // console.log("Updated Request:", updatedRequest.read); + // } catch (error) { + // console.error("Error updating request:", error); + // } + // }; + const handleCardClick = async request => { try { - const updatedRequest = { ...request, read: true }; + let updatedRequest = request; + if (request.read !== true) { + console.log("Read Request:", request); + // updatedRequest = { ...request, read: true }; + const response = await axios.put( + `http://localhost:5000/api/requests/updateRequest/${request.requestID}` + ); + updatedRequest = request; + console.log("response:", response.data) + console.log("image:", updatedRequest.image) + } setSelectedRequest(updatedRequest); - await axios.put( - `http://localhost:5000/api/requests/updateRequest/${request.requestID}`, - updatedRequest - ); + console.log("request Image", request.image); const updatedRequests = await getRequests(); setShowRequests(updatedRequests); - console.log("Updated Request:", updatedRequest.read); } catch (error) { console.error("Error updating request:", error); } }; const handleTag = tag => { - if (tag == "all") { - setShowRequests(filteredRequests.allRequests); - } else if (tag === "flood") { - setShowRequests(filteredRequests.floodRequests); - } else if (tag === "tsunami") { - setShowRequests(filteredRequests.tsunamiRequests); - } else if (tag === "fire") { - setShowRequests(filteredRequests.fireRequests); - } else if (tag === "extreme wind") { - setShowRequests(filteredRequests.extremeWindRequests); - } else if (tag === "landslide") { - setShowRequests(filteredRequests.landslideRequests); - } else if (tag === "verified") { - setShowRequests(filteredRequests.verifiedRequests); - } else if (tag === "today") { - setShowRequests(filteredRequests.todayRequests); - } else if (tag === "monthly") { - setShowRequests(filteredRequests.monthlyRequests); - } else if (tag === "read") { - setShowRequests(filteredRequests.allReadRequests); - } else if (tag === "other") { - setShowRequests(filteredRequests.otherRequests); + const tagMap = { + all: filteredRequests.allRequests, + flood: filteredRequests.floodRequests, + tsunami: filteredRequests.tsunamiRequests, + fire: filteredRequests.fireRequests, + "extreme wind": filteredRequests.extremeWindRequests, + landslide: filteredRequests.landslideRequests, + verified: filteredRequests.verifiedRequests, + today: filteredRequests.todayRequests, + monthly: filteredRequests.monthlyRequests, + read: filteredRequests.allReadRequests, + other: filteredRequests.otherRequests + }; + + const requests = tagMap[tag]; + + if (requests) { + if (requests.length === 0) { + message.info("Requests are empty"); + } + setShowRequests(requests); } else { - message.info("Not Such requests"); - return []; + message.info("No such requests"); + setShowRequests([]); } }; + const [isMenuOpen, setIsMenuOpen] = useState(false); const toggleMenu = () => { setIsMenuOpen(!isMenuOpen); @@ -227,6 +249,7 @@ export const Requests = () => { medicalNeed={selectedRequest.medicalNeed} otherNeeds={selectedRequest.otherNeeds} verification={selectedRequest.verify} + image={selectedRequest.image} locationLatLan={selectedRequest.disasterLocationLatLan} />
@@ -429,22 +452,26 @@ export const Requests = () => { {Array.isArray(showRequests) && - showRequests.map(request => -
handleCardClick(request)} - > - -
- )} + (showRequests.length !== 0 + ? showRequests.map(request => +
handleCardClick(request)} + > + +
+ ) + :
+ No requests available! +
)}
}
diff --git a/frontend/src/pages/controller/ShelterLocations.jsx b/frontend/src/pages/controller/ShelterLocations.jsx index 3a20e30..704da8d 100644 --- a/frontend/src/pages/controller/ShelterLocations.jsx +++ b/frontend/src/pages/controller/ShelterLocations.jsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from "react"; +import React, { useEffect, useRef, useState } from "react"; import { LanguageBar } from "../../components/Controller/LanguageBar"; import { HeaderBar } from "../../components/Controller/HeaderBar"; import { deleteShelter, getAllShelters } from "../../services/shelterService"; @@ -14,9 +14,9 @@ export const ShelterLocationPage = () => { const [open, setOpen] = useState(false);//for alerts const [formOpen, setFormopen] = useState(false); const [change, setChange] = useState(false); + const formRef = useRef(null); - - const [ snackMessage, setSnackMessage ] = useState({message:"", severity:""}); + const [snackMessage, setSnackMessage] = useState({ message: "", severity: "" }); const getShelters = async () => { const allShelters = await getAllShelters(); @@ -40,15 +40,16 @@ export const ShelterLocationPage = () => { { col: "Shelter ID", width: "120px" }, { col: "Shelter Name", width: "250px" }, { col: "Shelter Type", width: "200px" }, - { col: "Location", width: "300px" }, - { col: "Person In Charge", width: "250px" }, + { col: "Location", width: "350px" }, + { col: "Person In Charge", width: "230px" }, { col: "Phone Number", width: "150px" }, + { col: "Action", width: "100px" }, ]; const [shelters, setShelters] = useState(null); const handleDelete = async (id) => { - try{ + try { const result = await deleteShelter(id); setChange(!change); await getShelters(); @@ -57,15 +58,15 @@ export const ShelterLocationPage = () => { setSnackMessage(msg); console.log("Result: ", result); - }catch(error){ + } catch (error) { const msg = { message: "Shelter delete failed!", severity: "error" }; setSnackMessage(msg); console.log(error); } } - const handleEdit = () => { - + const handleEdit = (id) => { + } const alertClose = () => { @@ -88,6 +89,16 @@ export const ShelterLocationPage = () => { } }, [snackMessage]) + useEffect(() => { + if (formRef.current && formOpen) { + const timer = setTimeout(() => { + formRef.current.scrollIntoView({ behavior: 'smooth' }); + }, 500); // 1 seconds delay + + return () => clearTimeout(timer); + } + }, [formOpen]) + return (
@@ -107,46 +118,48 @@ export const ShelterLocationPage = () => {
-
+
{ tableCols.map((item, index) => (
{item.col}
)) }
-
+
{ shelters && shelters.length > 0 ? shelters.map((shelter, index) => ( { handleDelete(shelter.shelterId); - }} - shelterEdit={()=>{}}/> + }} + shelterEdit={(id) => { handleEdit(id) }} /> )) :
No shelters found
}
- - - +
+ + + +
+ - {snackMessage.message} - - + severity={snackMessage.severity} + variant="filled" + sx={{ width: '100%' }} + >{snackMessage.message} + +
) } \ No newline at end of file diff --git a/frontend/src/services/emailServices.jsx b/frontend/src/services/emailServices.jsx new file mode 100644 index 0000000..35a75e1 --- /dev/null +++ b/frontend/src/services/emailServices.jsx @@ -0,0 +1,6 @@ +import axios from "axios"; + +export const sendRespond = async (ids)=>{ + const {data} = await axios.post("api/email/sendResponds",{ids}); + return data; +} \ No newline at end of file diff --git a/frontend/src/services/requestService.jsx b/frontend/src/services/requestService.jsx index 4b4c057..313329a 100644 --- a/frontend/src/services/requestService.jsx +++ b/frontend/src/services/requestService.jsx @@ -21,6 +21,11 @@ export const setVerifyRequests = async requestIDs => { return data; } +export const checkUnverifyRequests = async requestIDs => { + const {data} = await axios.post('api/requests/showUnverify', {requestIDs}); + return data; +} + // export const updateRequests = async (requestId, newData) => { // const {data} = await axios.put('/api/requests/updateRequest/' + requestId, newData); // console.log("UpdatedData", data);