From def8ba4b9df81f6dd6854491e5b0ff14a1e5d40a Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Fri, 10 May 2024 13:36:04 -0700 Subject: [PATCH 01/16] Added monitor router, controller, and model --- Server/controllers/monitorController.js | 19 ++++++++++ Server/index.js | 46 ++++++++++++++----------- Server/models/monitor.js | 37 ++++++++++++++++++++ Server/routes/monitorRoute.js | 8 +++++ Server/validation/joi.js | 12 ++++--- 5 files changed, 96 insertions(+), 26 deletions(-) create mode 100644 Server/controllers/monitorController.js create mode 100644 Server/models/monitor.js create mode 100644 Server/routes/monitorRoute.js diff --git a/Server/controllers/monitorController.js b/Server/controllers/monitorController.js new file mode 100644 index 000000000..1044d3f48 --- /dev/null +++ b/Server/controllers/monitorController.js @@ -0,0 +1,19 @@ +const { getMonitorsByIdValidation } = require("../validation/joi"); + +const getAllMonitors = async (req, res) => { + res.json({ message: "Hello from Monitor Controller" }); +}; + +const getMonitorById = async (req, res) => { + const { error } = getMonitorsByIdValidation.validate(req.params); + if (error) { + return res + .status(400) + .json({ success: false, msg: error.details[0].message }); + } + + const monitorId = req.params.monitorId; + return res.json({ success: true, msg: "Monitor found", data: { monitorId } }); +}; + +module.exports = { getAllMonitors, getMonitorById }; diff --git a/Server/index.js b/Server/index.js index 173c2c78d..3913d1a58 100644 --- a/Server/index.js +++ b/Server/index.js @@ -1,28 +1,32 @@ -const express = require('express') -const helmet = require('helmet') -const cors = require('cors') -const authRouter = require('./routes/authRoute') -const { connectDbAndRunServer } = require('./configs/db') -require('dotenv').config() +const express = require("express"); +const helmet = require("helmet"); +const cors = require("cors"); +const authRouter = require("./routes/authRoute"); +const monitorRouter = require("./routes/monitorRoute"); +const { connectDbAndRunServer } = require("./configs/db"); +require("dotenv").config(); // const { sendEmail } = require('./utils/sendEmail') /** * NOTES - * Email Service will be added + * Email Service will be added * Logger Service will be added (Winston or similar) */ -const app = express() +const app = express(); // middlewares -app.use(cors( - //We will add configuration later -)) -app.use(express.json()) -app.use(helmet()) +app.use( + cors() + //We will add configuration later +); +app.use(express.json()); +app.use(helmet()); //routes -app.use('/api/v1/auth', authRouter); +app.use("/api/v1/auth", authRouter); + +app.use("/api/v1/monitors", monitorRouter); // Testing email service // app.use('/sendEmail', async (req, res) => { @@ -31,12 +35,12 @@ app.use('/api/v1/auth', authRouter); // }) //health check -app.use('/api/v1/healthy', (req, res) => { - try { - return res.status(200).json({message:"Healthy"}) - } catch (error) { - return res.status(500).json({message:error.message}) - } -}) +app.use("/api/v1/healthy", (req, res) => { + try { + return res.status(200).json({ message: "Healthy" }); + } catch (error) { + return res.status(500).json({ message: error.message }); + } +}); connectDbAndRunServer(app); diff --git a/Server/models/monitor.js b/Server/models/monitor.js new file mode 100644 index 000000000..513d4e2be --- /dev/null +++ b/Server/models/monitor.js @@ -0,0 +1,37 @@ +const mongoose = require("mongoose"); + +const MonitorSchema = mongoose.Schema({ + userId: { + type: String, + }, + name: { + type: String, + required: true, + }, + description: { + type: String, + }, + url: { + type: String, + required: true, + }, + isActive: { + type: Boolean, + default: true, + }, + interval: { + // in milliseconds + type: Number, + default: 60000, + }, + updated_at: { + type: Date, + default: Date.now, + }, + created_at: { + type: Date, + default: Date.now, + }, +}); + +module.exports = mongoose.model("Monitor", MonitorSchema); diff --git a/Server/routes/monitorRoute.js b/Server/routes/monitorRoute.js new file mode 100644 index 000000000..724ee79b6 --- /dev/null +++ b/Server/routes/monitorRoute.js @@ -0,0 +1,8 @@ +const router = require("express").Router(); +const monitorController = require("../controllers/monitorController"); +const { getMonitorsByIdValidation } = require("../validation/joi"); + +router.get("/", monitorController.getAllMonitors); +router.get("/:monitorId", monitorController.getMonitorById); + +module.exports = router; diff --git a/Server/validation/joi.js b/Server/validation/joi.js index d28f9b7da..c5b2b58d1 100644 --- a/Server/validation/joi.js +++ b/Server/validation/joi.js @@ -1,10 +1,12 @@ -const joi = require('joi') - +const joi = require("joi"); const authValidation = joi.object({ - email: joi.string().email().required(), - password: joi.string().min(8).required(), + email: joi.string().email().required(), + password: joi.string().min(8).required(), }); +const getMonitorsByIdValidation = joi.object({ + monitorId: joi.string().required(), +}); -module.exports = {authValidation} \ No newline at end of file +module.exports = { authValidation, getMonitorsByIdValidation }; From 2e2d91937e533b1e0026324dc019018032227f9f Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Fri, 10 May 2024 13:59:33 -0700 Subject: [PATCH 02/16] Added fake DB for monitor route testing --- Server/controllers/monitorController.js | 48 +++++++++++++++++++++--- Server/db/FakeDb.js | 46 +++++++++++++++++++++++ Server/models/{monitor.js => Monitor.js} | 0 Server/routes/monitorRoute.js | 2 +- Server/validation/joi.js | 11 +++++- 5 files changed, 100 insertions(+), 7 deletions(-) create mode 100644 Server/db/FakeDb.js rename Server/models/{monitor.js => Monitor.js} (100%) diff --git a/Server/controllers/monitorController.js b/Server/controllers/monitorController.js index 1044d3f48..f7a85ce4c 100644 --- a/Server/controllers/monitorController.js +++ b/Server/controllers/monitorController.js @@ -1,9 +1,20 @@ -const { getMonitorsByIdValidation } = require("../validation/joi"); +const { + getMonitorsByIdValidation, + getMonitorsByUserIdValidation, +} = require("../validation/joi"); +const db = require("../db/FakeDb"); +// Gets all monitors const getAllMonitors = async (req, res) => { - res.json({ message: "Hello from Monitor Controller" }); + try { + const monitors = await db.getAllMonitors(); + return res.json({ success: true, msg: "Monitors found", data: monitors }); + } catch (error) { + return res.status(500).json({ success: false, msg: error.message }); + } }; +// Get a monitor by ID const getMonitorById = async (req, res) => { const { error } = getMonitorsByIdValidation.validate(req.params); if (error) { @@ -12,8 +23,35 @@ const getMonitorById = async (req, res) => { .json({ success: false, msg: error.details[0].message }); } - const monitorId = req.params.monitorId; - return res.json({ success: true, msg: "Monitor found", data: { monitorId } }); + try { + const monitorId = req.params.monitorId; + const monitor = await db.getMonitorById(monitorId); + return res.json({ success: true, msg: "Monitor found", data: monitor }); + } catch (error) { + return res.status(500).json({ success: false, msg: error.message }); + } }; -module.exports = { getAllMonitors, getMonitorById }; +// Gets a monitor by user ID +const getMonitorsByUserId = async (req, res) => { + const { error } = getMonitorsByUserIdValidation.validate(req.params); + if (error) { + return res + .status(400) + .json({ success: false, msg: error.details[0].message }); + } + + try { + const userId = req.params.userId; + const monitors = await db.getMonitorsByUserId(userId); + return res.json({ + success: true, + msg: `Monitors for user ${userId} found`, + data: monitors, + }); + } catch (error) { + return res.status(500).json({ success: false, msg: error.message }); + } +}; + +module.exports = { getAllMonitors, getMonitorById, getMonitorsByUserId }; diff --git a/Server/db/FakeDb.js b/Server/db/FakeDb.js new file mode 100644 index 000000000..b604a9294 --- /dev/null +++ b/Server/db/FakeDb.js @@ -0,0 +1,46 @@ +const Monitor = require("../models/Monitor"); + +const FAKE_MONITOR_DATA = []; + +for (let i = 0; i < 10; i++) { + FAKE_MONITOR_DATA.push( + new Monitor({ + userId: i % 2 === 0 ? 1 : 2, + name: `Monitor ${i}`, + description: `Description for Monitor ${i}`, + url: `https://monitor${i}.com`, + isActive: true, + interval: 60000, + updated_at: new Date(), + created_at: new Date(), + }) + ); +} + +const getAllMonitors = async () => { + return FAKE_MONITOR_DATA; +}; + +const getMonitorById = async (monitorId) => { + const idx = FAKE_MONITOR_DATA.findIndex((monitor) => { + return monitor.id === monitorId; + }); + if (idx === -1) { + throw new Error(`Monitor with id ${monitorId} not found`); + } + return FAKE_MONITOR_DATA[idx]; +}; + +const getMonitorsByUserId = async (userId) => { + const userMonitors = FAKE_MONITOR_DATA.filter((monitor) => { + return monitor.userId === userId; + }); + + if (userMonitors.length === 0) { + throw new Error(`Monitors for user ${userId} not found`); + } + + return userMonitors; +}; + +module.exports = { getAllMonitors, getMonitorById, getMonitorsByUserId }; diff --git a/Server/models/monitor.js b/Server/models/Monitor.js similarity index 100% rename from Server/models/monitor.js rename to Server/models/Monitor.js diff --git a/Server/routes/monitorRoute.js b/Server/routes/monitorRoute.js index 724ee79b6..7c6767364 100644 --- a/Server/routes/monitorRoute.js +++ b/Server/routes/monitorRoute.js @@ -1,8 +1,8 @@ const router = require("express").Router(); const monitorController = require("../controllers/monitorController"); -const { getMonitorsByIdValidation } = require("../validation/joi"); router.get("/", monitorController.getAllMonitors); router.get("/:monitorId", monitorController.getMonitorById); +router.get("/user/:userId", monitorController.getMonitorsByUserId); module.exports = router; diff --git a/Server/validation/joi.js b/Server/validation/joi.js index c5b2b58d1..2fb59a377 100644 --- a/Server/validation/joi.js +++ b/Server/validation/joi.js @@ -1,4 +1,5 @@ const joi = require("joi"); +const user = require("../models/user"); const authValidation = joi.object({ email: joi.string().email().required(), @@ -9,4 +10,12 @@ const getMonitorsByIdValidation = joi.object({ monitorId: joi.string().required(), }); -module.exports = { authValidation, getMonitorsByIdValidation }; +const getMonitorsByUserIdValidation = joi.object({ + userId: joi.string().required(), +}); + +module.exports = { + authValidation, + getMonitorsByIdValidation, + getMonitorsByUserIdValidation, +}; From 49a11b5abbe57b4526242f235c02e3f2af18d60e Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Fri, 10 May 2024 14:03:36 -0700 Subject: [PATCH 03/16] Added some comments RE fake database --- Server/controllers/monitorController.js | 2 ++ Server/db/FakeDb.js | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/Server/controllers/monitorController.js b/Server/controllers/monitorController.js index f7a85ce4c..deaf2d8ba 100644 --- a/Server/controllers/monitorController.js +++ b/Server/controllers/monitorController.js @@ -2,6 +2,8 @@ const { getMonitorsByIdValidation, getMonitorsByUserIdValidation, } = require("../validation/joi"); + +// Fake database, if we have standardized DB methods we can swap out databases easily const db = require("../db/FakeDb"); // Gets all monitors diff --git a/Server/db/FakeDb.js b/Server/db/FakeDb.js index b604a9294..8eed7779c 100644 --- a/Server/db/FakeDb.js +++ b/Server/db/FakeDb.js @@ -1,3 +1,9 @@ +// ************************** +// FakeDB is used to simulate the database +// If we standarize the DB methods we can swap out databases easily +// Too bad JS doesn't have interfaces, that would be nice. TypeScript anyone? +// ************************** + const Monitor = require("../models/Monitor"); const FAKE_MONITOR_DATA = []; From 7d6d2d341b1edfd313e6cf1c97cac8d0b9b68143 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Fri, 10 May 2024 14:04:13 -0700 Subject: [PATCH 04/16] Added one last comment --- Server/db/FakeDb.js | 1 + 1 file changed, 1 insertion(+) diff --git a/Server/db/FakeDb.js b/Server/db/FakeDb.js index 8eed7779c..d3b793f11 100644 --- a/Server/db/FakeDb.js +++ b/Server/db/FakeDb.js @@ -49,4 +49,5 @@ const getMonitorsByUserId = async (userId) => { return userMonitors; }; +// Conisder standardizing these methods so we can swap databases easily module.exports = { getAllMonitors, getMonitorById, getMonitorsByUserId }; From 8d6529d497c481935417d9dc187d03291d334f33 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Fri, 10 May 2024 14:34:04 -0700 Subject: [PATCH 05/16] Added some endpoint info to readme --- README.md | 92 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) diff --git a/README.md b/README.md index 19a0670e1..4b5375c8f 100644 --- a/README.md +++ b/README.md @@ -28,11 +28,103 @@ BlueWave uptime monitoring application 1. Change directory to the `Server` directory 2. Install all depencies by running `npm install` +--- + +#### Configuration + +Configure the server with the following environmental variables + +| ENV Varialbe Name | Required/Optional | Type | Description | +| -------------------- | ----------------- | ------ | --------------------------------- | +| DB_CONNECTION_STRING | Required | string | Specfies URL for MongoDB Database | + +--- + #### Starting the Development Server 1. run `npm run dev` to start the development server +--- + +#### Endpoints + +All endpoints return a response in this format: + +| Name | Type | Notes | +| ------- | --------- | ----------------------------- | +| success | `boolean` | Success or failure of request | +| msg | `string` | Message describing response | +| data | `Object` | Arbitrary Payload | + +Example: + +``` +{success: true, msg: "Successful Request", data: {test: testData}} +``` + +##### Data Types + +###### Monitor + +| Name | Type | Notes | +| ----------- | --------- | ---------------------------------------- | +| userId | `string` | Unique ID identifying monitor creator | +| name | `string` | Name of the monitor | +| description | `string` | Description of the monitor | +| url | `string` | Url the monitor will ping | +| isActive | `boolean` | Whether or not the monitor is active | +| interval | `integer` | Interval with which to ping monitor (ms) | +| updatedAt | `Date` | Last time the monitor was updated | +| CreatedAt | `Date` | When the monitor was updated | + +--- + +##### GET /api/v1/monitors + +###### Response + +| Status Code | Type | Description | +| ----------- | --------------------- | -------------------------------- | +| 200 | Response with Payload | Response with a list of monitors | + +###### Payload + +| Type | Notes | +| ---------------- | ----------------- | +| `Array[Monitor]` | Array of monitors | + +--- + +##### GET /api/v1/monitor/:id + +###### Response + +| Status Code | Type | Description | +| ----------- | --------------------- | ------------------------------ | +| 200 | Response with Payload | Response with a single monitor | + +###### Payload + +| Type | Notes | +| --------- | --------------------------------------------------- | +| `Monitor` | Single monitor with the id in the request parameter | + +--- + +##### GET /api/v1/monitors/user/:userId + +| Status Code | Type | Description | +| ----------- | --------------------- | -------------------------------- | +| 200 | Response with Payload | Response with a list of monitors | + +###### Payload + +| Type | Notes | +| ---------------- | ------------------------------------------------------------------ | +| `Array[Monitor]` | Array of monitors created by user with userId specified in request | + ## Contributors + From 8cc92e6c40d44f73a6bb92b5492707f5a34c5921 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Mon, 13 May 2024 14:21:27 -0700 Subject: [PATCH 06/16] Implemented MongoDB example db, updated documentation --- Server/configs/db.js | 29 ++++----- Server/controllers/monitorController.js | 9 +-- Server/db/FakeDb.js | 38 ++++++++++-- Server/db/MongoDB.js | 80 +++++++++++++++++++++++++ Server/index.js | 27 ++++++++- Server/routes/monitorRoute.js | 1 - 6 files changed, 154 insertions(+), 30 deletions(-) create mode 100644 Server/db/MongoDB.js diff --git a/Server/configs/db.js b/Server/configs/db.js index 174978ca3..ba3c8b660 100644 --- a/Server/configs/db.js +++ b/Server/configs/db.js @@ -1,18 +1,15 @@ -const mongoose = require('mongoose') +const PORT = process.env.PORT || 5000; -const PORT = process.env.PORT || 5000 +const connectDbAndRunServer = async (app, db) => { + try { + await db.connect(); + app.listen(PORT, () => { + console.log(`server started on port:${PORT}`); + }); + } catch (error) { + console.log("Failed to connect to DB"); + console.error(error); + } +}; -const connectDbAndRunServer = async (app) => { - await mongoose.connect(process.env.DB_CONNECTION_STRING).then(() => { - console.log("DB connected") - // run server - app.listen(PORT, () => { - console.log(`server started on port:${PORT}`) - }) - }).catch((err) => { - console.log(err) - }) -} - - -module.exports = { connectDbAndRunServer } \ No newline at end of file +module.exports = { connectDbAndRunServer }; diff --git a/Server/controllers/monitorController.js b/Server/controllers/monitorController.js index deaf2d8ba..dda18d8ff 100644 --- a/Server/controllers/monitorController.js +++ b/Server/controllers/monitorController.js @@ -3,13 +3,10 @@ const { getMonitorsByUserIdValidation, } = require("../validation/joi"); -// Fake database, if we have standardized DB methods we can swap out databases easily -const db = require("../db/FakeDb"); - // Gets all monitors const getAllMonitors = async (req, res) => { try { - const monitors = await db.getAllMonitors(); + const monitors = await req.db.getAllMonitors(); return res.json({ success: true, msg: "Monitors found", data: monitors }); } catch (error) { return res.status(500).json({ success: false, msg: error.message }); @@ -27,7 +24,7 @@ const getMonitorById = async (req, res) => { try { const monitorId = req.params.monitorId; - const monitor = await db.getMonitorById(monitorId); + const monitor = await req.db.getMonitorById(monitorId); return res.json({ success: true, msg: "Monitor found", data: monitor }); } catch (error) { return res.status(500).json({ success: false, msg: error.message }); @@ -45,7 +42,7 @@ const getMonitorsByUserId = async (req, res) => { try { const userId = req.params.userId; - const monitors = await db.getMonitorsByUserId(userId); + const monitors = await req.db.getMonitorsByUserId(userId); return res.json({ success: true, msg: `Monitors for user ${userId} found`, diff --git a/Server/db/FakeDb.js b/Server/db/FakeDb.js index d3b793f11..6dfa89ac9 100644 --- a/Server/db/FakeDb.js +++ b/Server/db/FakeDb.js @@ -1,7 +1,23 @@ // ************************** -// FakeDB is used to simulate the database -// If we standarize the DB methods we can swap out databases easily -// Too bad JS doesn't have interfaces, that would be nice. TypeScript anyone? +// The idea here is to provide a layer of abstraction between the database and whoever is using it. +// Instead of directly calling mongoose methods, we can call the methods on the DB object. +// If this were Typescript or Java or Golang an interface would be implemented to ensure the methods are available. +// But we do the best we can with Javascript. +// +// If the methods are consistent all we have to do to swap out one DB for another is simply change the import. +// +// Example: +// We start with the fake DB: +// +// const db = require("../db/FakeDb"); +// const monitors = await db.getAllMonitors(); +// +// And when we want to swtich to a real DB, all we have to do is swap the import +// +// const db = require("../db/MongoDb"); +// const monitors = await db.getAllMonitors(); +// +// The rest of the code is the same, as all the `db` methods are standardized. // ************************** const Monitor = require("../models/Monitor"); @@ -23,6 +39,14 @@ for (let i = 0; i < 10; i++) { ); } +const connect = async () => { + try { + await console.log("Connected to FakeDB"); + } catch (error) { + console.error(error); + } +}; + const getAllMonitors = async () => { return FAKE_MONITOR_DATA; }; @@ -49,5 +73,9 @@ const getMonitorsByUserId = async (userId) => { return userMonitors; }; -// Conisder standardizing these methods so we can swap databases easily -module.exports = { getAllMonitors, getMonitorById, getMonitorsByUserId }; +module.exports = { + connect, + getAllMonitors, + getMonitorById, + getMonitorsByUserId, +}; diff --git a/Server/db/MongoDB.js b/Server/db/MongoDB.js new file mode 100644 index 000000000..3c48a448d --- /dev/null +++ b/Server/db/MongoDB.js @@ -0,0 +1,80 @@ +const Monitor = require("../models/Monitor"); +const mongoose = require("mongoose"); + +const seedDatabase = async () => { + const FAKE_MONITOR_DATA = []; + + for (let i = 0; i < 10; i++) { + FAKE_MONITOR_DATA.push( + new Monitor({ + userId: i % 2 === 0 ? 1 : 2, + name: `Monitor ${i}`, + description: `Description for Monitor ${i}`, + url: `https://monitor${i}.com`, + isActive: true, + interval: 60000, + updated_at: new Date(), + created_at: new Date(), + }) + ); + } + + for (const monitor of FAKE_MONITOR_DATA) { + await monitor.save(); + } +}; + +const connect = async () => { + try { + await mongoose.connect(process.env.DB_CONNECTION_STRING); + console.log("Connected to MongoDB"); + + const monitors = await Monitor.find(); + if (monitors.length === 0) { + console.log("Seeding database with fake data"); + await seedDatabase(); + } else { + console.log("Database already seeded"); + } + } catch (error) { + console.error("Failed to connect to MongoDB"); + throw error; + } +}; + +// Gets all monitors +const getAllMonitors = async (req, res) => { + try { + const monitors = await Monitor.find(); + return monitors; + } catch (error) { + throw error; + } +}; + +// Get a monitor by ID +const getMonitorById = async (req, res) => { + try { + const monitor = await Monitor.findById(req.params.monitorId); + return monitor; + } catch (error) { + throw error; + } +}; + +// Gets a monitor by user ID +const getMonitorsByUserId = async (req, res) => { + try { + const monitors = await Monitor.find({ userId: req.params.userId }); + return monitors; + } catch (error) { + throw error; + } +}; + +module.exports = { + connect, + getAllMonitors, + getMonitorById, + getMonitorsByUserId, +}; diff --git a/Server/index.js b/Server/index.js index 3913d1a58..c8136c2d0 100644 --- a/Server/index.js +++ b/Server/index.js @@ -3,10 +3,23 @@ const helmet = require("helmet"); const cors = require("cors"); const authRouter = require("./routes/authRoute"); const monitorRouter = require("./routes/monitorRoute"); -const { connectDbAndRunServer } = require("./configs/db"); +const { connectDbAndRunServer, connectViaInterface } = require("./configs/db"); require("dotenv").config(); // const { sendEmail } = require('./utils/sendEmail') +// ************************** +// Here is where we can swap out DBs easily. Spin up a mongoDB instance and try it out. +// Simply comment out the FakeDB and uncomment the MongoDB or vice versa. +// We can easily swap between any type of data source as long as the methods are implemented +// +// FakeDB +// const db = require("./db/FakeDb"); +// +// MongoDB +const db = require("./db/MongoDB"); +// +// ************************** + /** * NOTES * Email Service will be added @@ -23,6 +36,16 @@ app.use( app.use(express.json()); app.use(helmet()); +// ************************** +// Make DB accessible anywhere we have a Request object +// By adding the DB to the request object, we can access it in any route +// Thus we do not need to import it in every route file, and we can easily swap out DBs as there is only one place to change it +// ************************** +app.use((req, res, next) => { + req.db = db; + next(); +}); + //routes app.use("/api/v1/auth", authRouter); @@ -43,4 +66,4 @@ app.use("/api/v1/healthy", (req, res) => { } }); -connectDbAndRunServer(app); +connectDbAndRunServer(app, db); diff --git a/Server/routes/monitorRoute.js b/Server/routes/monitorRoute.js index 7c6767364..e12982905 100644 --- a/Server/routes/monitorRoute.js +++ b/Server/routes/monitorRoute.js @@ -1,6 +1,5 @@ const router = require("express").Router(); const monitorController = require("../controllers/monitorController"); - router.get("/", monitorController.getAllMonitors); router.get("/:monitorId", monitorController.getMonitorById); router.get("/user/:userId", monitorController.getMonitorsByUserId); From 7957612a85a59229702e9e9652e7db895b30c1dc Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Mon, 13 May 2024 14:23:03 -0700 Subject: [PATCH 07/16] Removed unused method --- Server/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Server/index.js b/Server/index.js index c8136c2d0..ef8cdc982 100644 --- a/Server/index.js +++ b/Server/index.js @@ -3,7 +3,7 @@ const helmet = require("helmet"); const cors = require("cors"); const authRouter = require("./routes/authRoute"); const monitorRouter = require("./routes/monitorRoute"); -const { connectDbAndRunServer, connectViaInterface } = require("./configs/db"); +const { connectDbAndRunServer } = require("./configs/db"); require("dotenv").config(); // const { sendEmail } = require('./utils/sendEmail') From 1746cfaa13b3be90f5ee7b435c4a226db34edc9c Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Mon, 13 May 2024 14:50:36 -0700 Subject: [PATCH 08/16] Fixed naming conventions to comply with standard --- .../Avatar/{avatar.css => index.css} | 0 .../Avatar/{Avatar.jsx => index.jsx} | 0 .../Button/{Button.css => index.css} | 0 .../Button/{Button.jsx => index.jsx} | 0 Client/src/Components/Label/BaseLabel.jsx | 54 ------ Client/src/Components/Label/ColoredLabel.jsx | 67 -------- Client/src/Components/Label/StautsLabel.jsx | 46 ----- .../Label/{BaseLabel.css => index.css} | 0 Client/src/Components/Label/index.jsx | 157 ++++++++++++++++++ .../Components/Link/{Link.jsx => index.jsx} | 0 .../Components/Link/{Link.css => link.css} | 0 .../{ProgressStepper.css => index.css} | 0 .../{ProgressStepper.jsx => index.jsx} | 0 .../Section/{Section.jsx => index.jsx} | 2 +- Client/src/Pages/Demo/Demo.jsx | 14 +- 15 files changed, 164 insertions(+), 176 deletions(-) rename Client/src/Components/Avatar/{avatar.css => index.css} (100%) rename Client/src/Components/Avatar/{Avatar.jsx => index.jsx} (100%) rename Client/src/Components/Button/{Button.css => index.css} (100%) rename Client/src/Components/Button/{Button.jsx => index.jsx} (100%) delete mode 100644 Client/src/Components/Label/BaseLabel.jsx delete mode 100644 Client/src/Components/Label/ColoredLabel.jsx delete mode 100644 Client/src/Components/Label/StautsLabel.jsx rename Client/src/Components/Label/{BaseLabel.css => index.css} (100%) create mode 100644 Client/src/Components/Label/index.jsx rename Client/src/Components/Link/{Link.jsx => index.jsx} (100%) rename Client/src/Components/Link/{Link.css => link.css} (100%) rename Client/src/Components/ProgressStepper/{ProgressStepper.css => index.css} (100%) rename Client/src/Components/ProgressStepper/{ProgressStepper.jsx => index.jsx} (100%) rename Client/src/Components/Section/{Section.jsx => index.jsx} (98%) diff --git a/Client/src/Components/Avatar/avatar.css b/Client/src/Components/Avatar/index.css similarity index 100% rename from Client/src/Components/Avatar/avatar.css rename to Client/src/Components/Avatar/index.css diff --git a/Client/src/Components/Avatar/Avatar.jsx b/Client/src/Components/Avatar/index.jsx similarity index 100% rename from Client/src/Components/Avatar/Avatar.jsx rename to Client/src/Components/Avatar/index.jsx diff --git a/Client/src/Components/Button/Button.css b/Client/src/Components/Button/index.css similarity index 100% rename from Client/src/Components/Button/Button.css rename to Client/src/Components/Button/index.css diff --git a/Client/src/Components/Button/Button.jsx b/Client/src/Components/Button/index.jsx similarity index 100% rename from Client/src/Components/Button/Button.jsx rename to Client/src/Components/Button/index.jsx diff --git a/Client/src/Components/Label/BaseLabel.jsx b/Client/src/Components/Label/BaseLabel.jsx deleted file mode 100644 index aa23aeab3..000000000 --- a/Client/src/Components/Label/BaseLabel.jsx +++ /dev/null @@ -1,54 +0,0 @@ -import PropTypes from "prop-types"; -import "./BaseLabel.css"; -import { useTheme } from "@mui/material"; - -/** - * @typedef {Object} Styles - * @param {string} [color] - The text color - * @param {string} [backgroundColor] - The background color - * @param {string} [borderColor] - The border color - */ - -/** - * @component - * @param {Object} props - * @param {string} props.label - The label of the label - * @param {Styles} props.styles - CSS Styles passed from parent component - * @param {React.ReactNode} children - Children passed from parent component - * @returns {JSX.Element} - */ - -const BaseLabel = ({ label, styles, children }) => { - const theme = useTheme(); - // Grab the default borderRadius from the theme to match button style - const { borderRadius } = theme.shape; - // Calculate padding for the label to mimic button. Appears to scale correctly, not 100% sure though. - const padding = theme.spacing(1 * 0.75, 2); - - return ( -
- {children} - {label} -
- ); -}; - -BaseLabel.propTypes = { - label: PropTypes.string.isRequired, - styles: PropTypes.shape({ - color: PropTypes.string, - backgroundColor: PropTypes.string, - }), - children: PropTypes.node, -}; - -export default BaseLabel; diff --git a/Client/src/Components/Label/ColoredLabel.jsx b/Client/src/Components/Label/ColoredLabel.jsx deleted file mode 100644 index 744652050..000000000 --- a/Client/src/Components/Label/ColoredLabel.jsx +++ /dev/null @@ -1,67 +0,0 @@ -import { useTheme } from "@mui/material"; -import { PropTypes } from "prop-types"; -import BaseLabel from "./BaseLabel"; - -// Produces a lighter color based on a hex color and a percent -// lightenColor("#067647", 20) will produce a color 20% lighter than #067647 -const lightenColor = (color, percent) => { - let r = parseInt(color.substring(1, 3), 16); - let g = parseInt(color.substring(3, 5), 16); - let b = parseInt(color.substring(5, 7), 16); - - const amt = Math.round((255 * percent) / 100); - - r = r + amt <= 255 ? r + amt : 255; - g = g + amt <= 255 ? g + amt : 255; - b = b + amt <= 255 ? b + amt : 255; - - r = r.toString(16).padStart(2, "0"); - g = g.toString(16).padStart(2, "0"); - b = b.toString(16).padStart(2, "0"); - - return `#${r}${g}${b}`; -}; - -/** - * @component - * @param {Object} props - * @param {string} props.label - The label of the label - * @param {string} props.color - The color of the label, specified in #RRGGBB format - * @returns {JSX.Element} - * @example - * // Render a red label - * - */ - -const ColoredLabel = ({ label, color }) => { - const theme = useTheme(); - // If an invalid color is passed, default to the labelGray color - if ( - typeof color !== "string" || - !/^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/.test(color) - ) { - color = theme.palette.labelGray.color; - } - - // Calculate lighter shades for border and bg - const borderColor = lightenColor(color, 20); - const bgColor = lightenColor(color, 75); - - return ( - - ); -}; - -ColoredLabel.propTypes = { - label: PropTypes.string.isRequired, - color: PropTypes.string.isRequired, -}; - -export default ColoredLabel; diff --git a/Client/src/Components/Label/StautsLabel.jsx b/Client/src/Components/Label/StautsLabel.jsx deleted file mode 100644 index 108ca0f2f..000000000 --- a/Client/src/Components/Label/StautsLabel.jsx +++ /dev/null @@ -1,46 +0,0 @@ -import PropTypes from "prop-types"; -import BaseLabel from "./BaseLabel"; -import { Box } from "@mui/material"; -import { useTheme } from "@mui/material"; - -/** - * @component - * @param {Object} props - * @param {'Seen' | 'Waiting' | 'New' | 'Active'} props.status - The status for the label - * @returns {JSX.Element} - * @example - * // Render an active label - * - */ - -const StatusLabel = ({ status }) => { - const theme = useTheme(); - - const colorLookup = { - Seen: theme.palette.labelGray.color, - Waiting: theme.palette.labelRed.color, - New: theme.palette.labelOrange.color, - Active: theme.palette.labelGreen.color, - }; - - // Look up the color for the status, default to labelGray if not found - const color = colorLookup[status] || theme.palette.labelGray.color; - - return ( - - - - ); -}; - -StatusLabel.propTypes = { - status: PropTypes.oneOf(["Seen", "Waiting", "New", "Active"]), -}; - -export default StatusLabel; diff --git a/Client/src/Components/Label/BaseLabel.css b/Client/src/Components/Label/index.css similarity index 100% rename from Client/src/Components/Label/BaseLabel.css rename to Client/src/Components/Label/index.css diff --git a/Client/src/Components/Label/index.jsx b/Client/src/Components/Label/index.jsx new file mode 100644 index 000000000..c47b53cb8 --- /dev/null +++ b/Client/src/Components/Label/index.jsx @@ -0,0 +1,157 @@ +import PropTypes from "prop-types"; +import { Box } from "@mui/material"; +import { useTheme } from "@mui/material"; +import "./index.css"; + +/** + * @typedef {Object} Styles + * @param {string} [color] - The text color + * @param {string} [backgroundColor] - The background color + * @param {string} [borderColor] - The border color + */ + +/** + * @component + * @param {Object} props + * @param {string} props.label - The label of the label + * @param {Styles} props.styles - CSS Styles passed from parent component + * @param {React.ReactNode} children - Children passed from parent component + * @returns {JSX.Element} + */ + +const BaseLabel = ({ label, styles, children }) => { + const theme = useTheme(); + // Grab the default borderRadius from the theme to match button style + const { borderRadius } = theme.shape; + // Calculate padding for the label to mimic button. Appears to scale correctly, not 100% sure though. + const padding = theme.spacing(1 * 0.75, 2); + + return ( +
+ {children} + {label} +
+ ); +}; + +BaseLabel.propTypes = { + label: PropTypes.string.isRequired, + styles: PropTypes.shape({ + color: PropTypes.string, + backgroundColor: PropTypes.string, + }), + children: PropTypes.node, +}; + +// Produces a lighter color based on a hex color and a percent +// lightenColor("#067647", 20) will produce a color 20% lighter than #067647 +const lightenColor = (color, percent) => { + let r = parseInt(color.substring(1, 3), 16); + let g = parseInt(color.substring(3, 5), 16); + let b = parseInt(color.substring(5, 7), 16); + + const amt = Math.round((255 * percent) / 100); + + r = r + amt <= 255 ? r + amt : 255; + g = g + amt <= 255 ? g + amt : 255; + b = b + amt <= 255 ? b + amt : 255; + + r = r.toString(16).padStart(2, "0"); + g = g.toString(16).padStart(2, "0"); + b = b.toString(16).padStart(2, "0"); + + return `#${r}${g}${b}`; +}; + +/** + * @component + * @param {Object} props + * @param {string} props.label - The label of the label + * @param {string} props.color - The color of the label, specified in #RRGGBB format + * @returns {JSX.Element} + * @example + * // Render a red label + * + */ + +const ColoredLabel = ({ label, color }) => { + const theme = useTheme(); + // If an invalid color is passed, default to the labelGray color + if ( + typeof color !== "string" || + !/^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/.test(color) + ) { + color = theme.palette.labelGray.color; + } + + // Calculate lighter shades for border and bg + const borderColor = lightenColor(color, 20); + const bgColor = lightenColor(color, 75); + + return ( + + ); +}; + +ColoredLabel.propTypes = { + label: PropTypes.string.isRequired, + color: PropTypes.string.isRequired, +}; + +/** + * @component + * @param {Object} props + * @param {'Seen' | 'Waiting' | 'New' | 'Active'} props.status - The status for the label + * @returns {JSX.Element} + * @example + * // Render an active label + * + */ + +const StatusLabel = ({ status }) => { + const theme = useTheme(); + + const colorLookup = { + Seen: theme.palette.labelGray.color, + Waiting: theme.palette.labelRed.color, + New: theme.palette.labelOrange.color, + Active: theme.palette.labelGreen.color, + }; + + // Look up the color for the status, default to labelGray if not found + const color = colorLookup[status] || theme.palette.labelGray.color; + + return ( + + + + ); +}; + +StatusLabel.propTypes = { + status: PropTypes.oneOf(["Seen", "Waiting", "New", "Active"]), +}; + +export { ColoredLabel, StatusLabel }; diff --git a/Client/src/Components/Link/Link.jsx b/Client/src/Components/Link/index.jsx similarity index 100% rename from Client/src/Components/Link/Link.jsx rename to Client/src/Components/Link/index.jsx diff --git a/Client/src/Components/Link/Link.css b/Client/src/Components/Link/link.css similarity index 100% rename from Client/src/Components/Link/Link.css rename to Client/src/Components/Link/link.css diff --git a/Client/src/Components/ProgressStepper/ProgressStepper.css b/Client/src/Components/ProgressStepper/index.css similarity index 100% rename from Client/src/Components/ProgressStepper/ProgressStepper.css rename to Client/src/Components/ProgressStepper/index.css diff --git a/Client/src/Components/ProgressStepper/ProgressStepper.jsx b/Client/src/Components/ProgressStepper/index.jsx similarity index 100% rename from Client/src/Components/ProgressStepper/ProgressStepper.jsx rename to Client/src/Components/ProgressStepper/index.jsx diff --git a/Client/src/Components/Section/Section.jsx b/Client/src/Components/Section/index.jsx similarity index 98% rename from Client/src/Components/Section/Section.jsx rename to Client/src/Components/Section/index.jsx index 3ce773c81..9c5415385 100644 --- a/Client/src/Components/Section/Section.jsx +++ b/Client/src/Components/Section/index.jsx @@ -10,7 +10,7 @@ import { } from "@mui/material"; import MenuIcon from "@mui/icons-material/Menu"; import DeleteOutlineIcon from "@mui/icons-material/DeleteOutline"; -import Button from "../Button/Button"; +import Button from "../Button/"; import AddIcon from "@mui/icons-material/Add"; /** diff --git a/Client/src/Pages/Demo/Demo.jsx b/Client/src/Pages/Demo/Demo.jsx index 1c294ba5c..8ee00818b 100644 --- a/Client/src/Pages/Demo/Demo.jsx +++ b/Client/src/Pages/Demo/Demo.jsx @@ -1,7 +1,6 @@ import React from "react"; -import Button from "../../Components/Button/Button"; -import Link from "../../Components/Link/Link"; -import ColoredLabel from "../../Components/Label/ColoredLabel"; +import Button from "../../Components/Button/"; +import Link from "../../Components/Link"; import { useTheme, Switch, @@ -14,13 +13,12 @@ import { Tab, Tabs, } from "@mui/material"; -import StatusLabel from "../../Components/Label/StautsLabel"; -import Avatar from "../../Components/Avatar/Avatar"; -import ProgressStepper from "../../Components/ProgressStepper/ProgressStepper"; +import { ColoredLabel, StatusLabel } from "../../Components/Label/"; +import Avatar from "../../Components/Avatar/"; +import ProgressStepper from "../../Components/ProgressStepper"; import avatarImage from "../../assets/Images/avatar_placeholder.png"; -import Section from "../../Components/Section/Section"; +import Section from "../../Components/Section"; import { DataGrid } from "@mui/x-data-grid"; - import { DatePicker } from "@mui/x-date-pickers/DatePicker"; import { LocalizationProvider } from "@mui/x-date-pickers/LocalizationProvider"; import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs"; From 4e12778f001b0e809228fe11a74a7e82ae6aba1c Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Mon, 13 May 2024 15:22:35 -0700 Subject: [PATCH 09/16] Remove seed data, not required --- Server/db/MongoDB.js | 31 ------------------------------- 1 file changed, 31 deletions(-) diff --git a/Server/db/MongoDB.js b/Server/db/MongoDB.js index 3c48a448d..6ad783877 100644 --- a/Server/db/MongoDB.js +++ b/Server/db/MongoDB.js @@ -1,41 +1,10 @@ const Monitor = require("../models/Monitor"); const mongoose = require("mongoose"); -const seedDatabase = async () => { - const FAKE_MONITOR_DATA = []; - - for (let i = 0; i < 10; i++) { - FAKE_MONITOR_DATA.push( - new Monitor({ - userId: i % 2 === 0 ? 1 : 2, - name: `Monitor ${i}`, - description: `Description for Monitor ${i}`, - url: `https://monitor${i}.com`, - isActive: true, - interval: 60000, - updated_at: new Date(), - created_at: new Date(), - }) - ); - } - - for (const monitor of FAKE_MONITOR_DATA) { - await monitor.save(); - } -}; - const connect = async () => { try { await mongoose.connect(process.env.DB_CONNECTION_STRING); console.log("Connected to MongoDB"); - - const monitors = await Monitor.find(); - if (monitors.length === 0) { - console.log("Seeding database with fake data"); - await seedDatabase(); - } else { - console.log("Database already seeded"); - } } catch (error) { console.error("Failed to connect to MongoDB"); throw error; From 973ed3ce1c5c320490e7b79053781dfcd214bbd8 Mon Sep 17 00:00:00 2001 From: veyselboybay Date: Mon, 13 May 2024 18:47:38 -0400 Subject: [PATCH 10/16] saving changings for pulling updates --- Server/.gitignore | 3 +- Server/index.js | 5 - Server/package-lock.json | 196 ++++++++++++++++++++++++++++++++++++++- Server/package.json | 3 +- 4 files changed, 199 insertions(+), 8 deletions(-) diff --git a/Server/.gitignore b/Server/.gitignore index f469421d1..e27ddd889 100644 --- a/Server/.gitignore +++ b/Server/.gitignore @@ -1,3 +1,4 @@ node_modules .env -utils \ No newline at end of file +utils/sendEmail.js +utils/logger.js \ No newline at end of file diff --git a/Server/index.js b/Server/index.js index 173c2c78d..d379b6f1a 100644 --- a/Server/index.js +++ b/Server/index.js @@ -6,11 +6,6 @@ const { connectDbAndRunServer } = require('./configs/db') require('dotenv').config() // const { sendEmail } = require('./utils/sendEmail') -/** - * NOTES - * Email Service will be added - * Logger Service will be added (Winston or similar) - */ const app = express() diff --git a/Server/package-lock.json b/Server/package-lock.json index 19995c3d5..495d5bc2e 100644 --- a/Server/package-lock.json +++ b/Server/package-lock.json @@ -16,12 +16,31 @@ "helmet": "^7.1.0", "joi": "^17.13.1", "mailersend": "^2.2.0", - "mongoose": "^8.3.3" + "mongoose": "^8.3.3", + "winston": "^3.13.0" }, "devDependencies": { "nodemon": "3.1.0" } }, + "node_modules/@colors/colors": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", + "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@dabh/diagnostics": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz", + "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==", + "dependencies": { + "colorspace": "1.1.x", + "enabled": "2.0.x", + "kuler": "^2.0.0" + } + }, "node_modules/@hapi/hoek": { "version": "9.3.0", "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", @@ -94,6 +113,11 @@ "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==" }, + "node_modules/@types/triple-beam": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz", + "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==" + }, "node_modules/@types/webidl-conversions": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz", @@ -199,6 +223,11 @@ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" }, + "node_modules/async": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", + "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==" + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -339,6 +368,37 @@ "node": ">=10" } }, + "node_modules/color": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", + "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", + "dependencies": { + "color-convert": "^1.9.3", + "color-string": "^1.6.0" + } + }, + "node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, "node_modules/color-support": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", @@ -347,6 +407,15 @@ "color-support": "bin.js" } }, + "node_modules/colorspace": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz", + "integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==", + "dependencies": { + "color": "^3.1.3", + "text-hex": "1.0.x" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -476,6 +545,11 @@ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, + "node_modules/enabled": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", + "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==" + }, "node_modules/encodeurl": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", @@ -562,6 +636,11 @@ "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" }, + "node_modules/fecha": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", + "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==" + }, "node_modules/fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -591,6 +670,11 @@ "node": ">= 0.8" } }, + "node_modules/fn.name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", + "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" + }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -902,6 +986,11 @@ "node": ">= 0.10" } }, + "node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" + }, "node_modules/is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -992,6 +1081,32 @@ "node": ">=12.0.0" } }, + "node_modules/kuler": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", + "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" + }, + "node_modules/logform": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.6.0.tgz", + "integrity": "sha512-1ulHeNPp6k/LD8H91o7VYFBng5i1BDE7HoKxVbZiGFidS1Rj65qcywLxX+pVfAPoQJEjRdvKcusKwOupHCVOVQ==", + "dependencies": { + "@colors/colors": "1.6.0", + "@types/triple-beam": "^1.3.2", + "fecha": "^4.2.0", + "ms": "^2.1.1", + "safe-stable-stringify": "^2.3.1", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/logform/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, "node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -1441,6 +1556,14 @@ "wrappy": "1" } }, + "node_modules/one-time": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", + "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", + "dependencies": { + "fn.name": "1.x.x" + } + }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -1594,6 +1717,14 @@ } ] }, + "node_modules/safe-stable-stringify": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz", + "integrity": "sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==", + "engines": { + "node": ">=10" + } + }, "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", @@ -1708,6 +1839,14 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, "node_modules/simple-update-notifier": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", @@ -1728,6 +1867,14 @@ "memory-pager": "^1.0.2" } }, + "node_modules/stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", + "engines": { + "node": "*" + } + }, "node_modules/statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", @@ -1796,6 +1943,11 @@ "node": ">=10" } }, + "node_modules/text-hex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", + "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==" + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -1839,6 +1991,14 @@ "node": ">=14" } }, + "node_modules/triple-beam": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", + "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==", + "engines": { + "node": ">= 14.0.0" + } + }, "node_modules/type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", @@ -1919,6 +2079,40 @@ "string-width": "^1.0.2 || 2 || 3 || 4" } }, + "node_modules/winston": { + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.13.0.tgz", + "integrity": "sha512-rwidmA1w3SE4j0E5MuIufFhyJPBDG7Nu71RkZor1p2+qHvJSZ9GYDA81AyleQcZbh/+V6HjeBdfnTZJm9rSeQQ==", + "dependencies": { + "@colors/colors": "^1.6.0", + "@dabh/diagnostics": "^2.0.2", + "async": "^3.2.3", + "is-stream": "^2.0.0", + "logform": "^2.4.0", + "one-time": "^1.0.0", + "readable-stream": "^3.4.0", + "safe-stable-stringify": "^2.3.1", + "stack-trace": "0.0.x", + "triple-beam": "^1.3.0", + "winston-transport": "^4.7.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/winston-transport": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.7.0.tgz", + "integrity": "sha512-ajBj65K5I7denzer2IYW6+2bNIVqLGDHqDw3Ow8Ohh+vdW+rv4MZ6eiDvHoKhfJFZ2auyN8byXieDDJ96ViONg==", + "dependencies": { + "logform": "^2.3.2", + "readable-stream": "^3.6.0", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", diff --git a/Server/package.json b/Server/package.json index 53f0fe429..6a082cbdc 100644 --- a/Server/package.json +++ b/Server/package.json @@ -18,7 +18,8 @@ "helmet": "^7.1.0", "joi": "^17.13.1", "mailersend": "^2.2.0", - "mongoose": "^8.3.3" + "mongoose": "^8.3.3", + "winston": "^3.13.0" }, "devDependencies": { "nodemon": "3.1.0" From e6b0b9b2ca9f0fff397c7a4a54d28f9b0ebeb27c Mon Sep 17 00:00:00 2001 From: M M Date: Mon, 13 May 2024 16:12:20 -0700 Subject: [PATCH 11/16] Implemented the Dashboard Menu. --- Client/package-lock.json | 8 +-- Client/package.json | 2 +- Client/src/Components/DashboardMenu/index.css | 15 +++++ Client/src/Components/DashboardMenu/index.jsx | 57 +++++++++++++++++++ Client/src/Pages/Home/index.jsx | 2 + 5 files changed, 79 insertions(+), 5 deletions(-) create mode 100644 Client/src/Components/DashboardMenu/index.css create mode 100644 Client/src/Components/DashboardMenu/index.jsx diff --git a/Client/package-lock.json b/Client/package-lock.json index 79ac309a3..2ad129bd4 100644 --- a/Client/package-lock.json +++ b/Client/package-lock.json @@ -11,7 +11,7 @@ "@emotion/react": "^11.11.4", "@emotion/styled": "^11.11.5", "@fontsource/roboto": "^5.0.13", - "@mui/icons-material": "^5.15.16", + "@mui/icons-material": "^5.15.17", "@mui/material": "^5.15.16", "@mui/x-data-grid": "7.3.2", "@mui/x-date-pickers": "7.3.2", @@ -1127,9 +1127,9 @@ } }, "node_modules/@mui/icons-material": { - "version": "5.15.16", - "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-5.15.16.tgz", - "integrity": "sha512-s8vYbyACzTNZRKv+20fCfVXJwJqNcVotns2EKnu1wmAga6wv2LAo5kB1d5yqQqZlMFtp34EJvRXf7cy8X0tJVA==", + "version": "5.15.17", + "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-5.15.17.tgz", + "integrity": "sha512-xVzl2De7IY36s/keHX45YMiCpsIx3mNv2xwDgtBkRSnZQtVk+Gqufwj1ktUxEyjzEhBl0+PiNJqYC31C+n1n6A==", "dependencies": { "@babel/runtime": "^7.23.9" }, diff --git a/Client/package.json b/Client/package.json index 7d5c47a8d..cca88fb31 100644 --- a/Client/package.json +++ b/Client/package.json @@ -13,7 +13,7 @@ "@emotion/react": "^11.11.4", "@emotion/styled": "^11.11.5", "@fontsource/roboto": "^5.0.13", - "@mui/icons-material": "^5.15.16", + "@mui/icons-material": "^5.15.17", "@mui/material": "^5.15.16", "@mui/x-data-grid": "7.3.2", "@mui/x-date-pickers": "7.3.2", diff --git a/Client/src/Components/DashboardMenu/index.css b/Client/src/Components/DashboardMenu/index.css new file mode 100644 index 000000000..37c84655b --- /dev/null +++ b/Client/src/Components/DashboardMenu/index.css @@ -0,0 +1,15 @@ +.icon-container { + display: flex; + align-items: center; + margin-bottom: 12px; + } + + .text-container { + display: flex; + align-items: center; + } + + .dashboard-menu-text { + margin-left: 8px; + } + \ No newline at end of file diff --git a/Client/src/Components/DashboardMenu/index.jsx b/Client/src/Components/DashboardMenu/index.jsx new file mode 100644 index 000000000..70fe3511f --- /dev/null +++ b/Client/src/Components/DashboardMenu/index.jsx @@ -0,0 +1,57 @@ +import language from '@mui/icons-material/language'; +import report_gmailerrorred from '@mui/icons-material/reportGmailerrorred'; +import sensors from '@mui/icons-material/sensors'; +import settings from '@mui/icons-material/settings'; +import all_inclusive from '@mui/icons-material/AllInclusive'; +import SvgIcon from '@mui/material/SvgIcon'; +import './index.css'; + +/** + * DashboardMenu component displays a menu with icons and corresponding text. + * Each menu item consists of an icon and a text label. + * + * @returns {JSX.Element} The JSX element representing the DashboardMenu component. + */ + +function DashboardMenu() { + return ( +
+
+ +
+ Monitors +
+
+ +
+ +
+ Incidents +
+
+ +
+ +
+ Status Pages +
+
+ +
+ +
+ Integrations +
+
+ +
+ +
+ Settings +
+
+
+ ); +} + +export default DashboardMenu; diff --git a/Client/src/Pages/Home/index.jsx b/Client/src/Pages/Home/index.jsx index 8742da6f1..ff5cb454e 100644 --- a/Client/src/Pages/Home/index.jsx +++ b/Client/src/Pages/Home/index.jsx @@ -1,6 +1,7 @@ import DropdownTeamMember from "../../Components/DropdownTeamMember"; import Search from "../../Components/Search"; import "./index.css"; +import DashboardMenu from "../../Components/DashboardMenu"; const Home = () => { return ( @@ -8,6 +9,7 @@ const Home = () => {
Home
+ ); }; From ad2b48bb41ac09f8b31122e9cd095184f86de871 Mon Sep 17 00:00:00 2001 From: veyselboybay Date: Mon, 13 May 2024 19:20:56 -0400 Subject: [PATCH 12/16] Added logging service and emailing function --- README.md | 2 ++ Server/.gitignore | 4 +--- Server/index.js | 11 ++++++---- Server/utils/logger.js | 26 +++++++++++++++++++++++ Server/utils/sendEmail.js | 43 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 79 insertions(+), 7 deletions(-) create mode 100644 Server/utils/logger.js create mode 100644 Server/utils/sendEmail.js diff --git a/README.md b/README.md index 4b5375c8f..3397a324d 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,8 @@ Configure the server with the following environmental variables | ENV Varialbe Name | Required/Optional | Type | Description | | -------------------- | ----------------- | ------ | --------------------------------- | | DB_CONNECTION_STRING | Required | string | Specfies URL for MongoDB Database | +| MAILERSEND_API_KEY | Required | string | Specfies API KEY for MailerSend service | +| SYSTEM_EMAIL_ADDRESS | Required | string | Specfies System email to be used in emailing service | --- diff --git a/Server/.gitignore b/Server/.gitignore index e27ddd889..1dcef2d9f 100644 --- a/Server/.gitignore +++ b/Server/.gitignore @@ -1,4 +1,2 @@ node_modules -.env -utils/sendEmail.js -utils/logger.js \ No newline at end of file +.env \ No newline at end of file diff --git a/Server/index.js b/Server/index.js index 8487c5012..967a9899b 100644 --- a/Server/index.js +++ b/Server/index.js @@ -5,6 +5,7 @@ const authRouter = require("./routes/authRoute"); const monitorRouter = require("./routes/monitorRoute"); const { connectDbAndRunServer } = require("./configs/db"); require("dotenv").config(); +const logger = require('./utils/logger'); // const { sendEmail } = require('./utils/sendEmail') // ************************** @@ -53,15 +54,17 @@ app.use("/api/v1/monitors", monitorRouter); // Testing email service // app.use('/sendEmail', async (req, res) => { -// const response = sendEmail('veysel.boybay@outlook.com', ['veysel.boybay@bluewavelabs.ca'], 'Testing email service', '

Testing Bluewavelabs

'); +// const response = sendEmail(['veysel.boybay@bluewavelabs.ca'], 'Testing email service', '

Testing Bluewavelabs

'); // console.log(response); // }) //health check app.use("/api/v1/healthy", (req, res) => { - try { - return res.status(200).json({ message: "Healthy" }); - } catch (error) { + try { + logger.info("Checking Health of the server.") + return res.status(200).json({ message: "Healthy" }); + } catch (error) { + logger.error(error.message) return res.status(500).json({ message: error.message }); } }); diff --git a/Server/utils/logger.js b/Server/utils/logger.js new file mode 100644 index 000000000..a8f65527c --- /dev/null +++ b/Server/utils/logger.js @@ -0,0 +1,26 @@ +const winston = require('winston'); +/** + * @module + * @example + * logger.info("Registered a new user!") + * logger.warn("User not found!") + * logger.error("Cannot save") + * @example + * "Specify service and ID in the log if applicable." + * logger.error("Descriptive Message",{"service":"monitor","monitorId":"123456"}) + * logger.error("Incorrect Credentials",{"service":"Auth","userId":"654321"}) + * logger.error("User not found!",{"service":"Auth"}) + */ +const logger = winston.createLogger({ + level: 'info', + format: winston.format.combine( + winston.format.timestamp(), + winston.format.json() + ), + transports: [ + new winston.transports.Console(), + new winston.transports.File({ filename: 'app.log' }), + ], +}); + +module.exports = logger; \ No newline at end of file diff --git a/Server/utils/sendEmail.js b/Server/utils/sendEmail.js new file mode 100644 index 000000000..712a651bd --- /dev/null +++ b/Server/utils/sendEmail.js @@ -0,0 +1,43 @@ +const { MailerSend, EmailParams, Sender, Recipient } = require('mailersend') +const logger = require('../utils/logger') + + +const mailersend = new MailerSend({ + apiKey: process.env.MAILERSEND_API_KEY, +}) +/** + * @function + * @param {[string]} receivers - takes an array of strings + * @param {string} subject - takes a single string + * @param {string} contentHTML - takes a single string that contains HTML + * @returns {JSON} + * @example + * sendEmail(['veysel@bluewavelabs.ca','alex@bluewavelabs.ca','monzer@bluewavelabs.ca'],'Testing Email Servide','

BlueWaveLabs

') + */ +// TODO: from email should be in .env file +const sendEmail = async (receivers,subject,contentHTML) => { + // Sender + const from = process.env.SYSTEM_EMAIL_ADDRESS; + const sender = new Sender(from); + // receivers + let recipients = [] + receivers.map(email => recipients.push(new Recipient(email))); + // Set params + const emailParams = new EmailParams() + .setFrom(sender) + .setTo(recipients) + .setSubject(subject) + .setHtml(contentHTML); + + try { + const response = await mailersend.email.send(emailParams); + logger.info("Email sent to receivers!",{"service":"Email"}) + return response; + + } catch (error) { + logger.error(error.body,{"service":"email"}) + console.log(error.body) + } +} + +module.exports = {sendEmail} \ No newline at end of file From 3ff8c65eb5125ad5c1a7f312b55565c73c18ccbc Mon Sep 17 00:00:00 2001 From: M M Date: Tue, 14 May 2024 10:00:52 -0700 Subject: [PATCH 13/16] Addressed comments. --- Client/src/Components/DashboardMenu/index.jsx | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/Client/src/Components/DashboardMenu/index.jsx b/Client/src/Components/DashboardMenu/index.jsx index 70fe3511f..716d68497 100644 --- a/Client/src/Components/DashboardMenu/index.jsx +++ b/Client/src/Components/DashboardMenu/index.jsx @@ -1,12 +1,13 @@ -import language from '@mui/icons-material/language'; -import report_gmailerrorred from '@mui/icons-material/reportGmailerrorred'; -import sensors from '@mui/icons-material/sensors'; -import settings from '@mui/icons-material/settings'; -import all_inclusive from '@mui/icons-material/AllInclusive'; +import LanguageIcon from "@mui/icons-material/Language"; +import ReportGmailerrorredIcon from "@mui/icons-material/ReportGmailerrorred"; +import SensorsIcon from "@mui/icons-material/Sensors"; +import SettingsIcon from "@mui/icons-material/Settings"; +import AllInclusiveIcon from "@mui/icons-material/AllInclusive"; import SvgIcon from '@mui/material/SvgIcon'; import './index.css'; /** + * @component * DashboardMenu component displays a menu with icons and corresponding text. * Each menu item consists of an icon and a text label. * @@ -17,35 +18,35 @@ function DashboardMenu() { return (
- +
Monitors
- +
Incidents
- +
Status Pages
- +
Integrations
- +
Settings
From a4a61695b0249f2dc919f13124c950114ba4c8f6 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Tue, 14 May 2024 11:12:22 -0700 Subject: [PATCH 14/16] Updated gitignore to ignore log files, Refactored Authcontroller to use DB interface, Added Try/Catch to validation to catch uncaught error, Added username optional validation to Joi so it expects a username --- Server/.gitignore | 3 +- Server/controllers/authController.js | 57 +++++++++++-------- Server/db/FakeDb.js | 29 ++++++++++ Server/db/MongoDB.js | 22 ++++++++ Server/index.js | 14 ++--- Server/models/user.js | 83 ++++++++++++++-------------- Server/validation/joi.js | 1 + 7 files changed, 137 insertions(+), 72 deletions(-) diff --git a/Server/.gitignore b/Server/.gitignore index 1dcef2d9f..3fdb59050 100644 --- a/Server/.gitignore +++ b/Server/.gitignore @@ -1,2 +1,3 @@ node_modules -.env \ No newline at end of file +.env +*.log \ No newline at end of file diff --git a/Server/controllers/authController.js b/Server/controllers/authController.js index f1a2c6ecd..23d5b8877 100644 --- a/Server/controllers/authController.js +++ b/Server/controllers/authController.js @@ -1,32 +1,43 @@ -const express = require('express') -const UserModel = require('../models/user') -const { authValidation } = require('../validation/joi') +const express = require("express"); +const UserModel = require("../models/user"); +const { authValidation } = require("../validation/joi"); /** * @function - * @param {express.Request} req - * @param {express.Response} res + * @param {express.Request} req + * @param {express.Response} res * @returns {{success: Boolean, msg: String}} */ const registerController = async (req, res) => { - // joi validation - const { error } = await authValidation.validateAsync(req.body); - if (error) return res.status(400).json({ success: false, msg: error.details[0].message }); + // joi validation + try { + await authValidation.validateAsync(req.body); + } catch (error) { + return res + .status(400) + .json({ success: false, msg: error.details[0].message }); + } - // Check if the user exists - const isUser = await UserModel.findOne({ email: req.body.email }); - if (isUser) return res.status(400).json({ success: false, msg: "User already exists!" }) + // Check if the user exists + try { + const isUser = await req.db.getUserByEmail(req, res); + if (isUser) + return res + .status(400) + .json({ success: false, msg: "User already exists!" }); + } catch (error) { + return res.status(500).json({ success: false, msg: error.message }); + } - try { - // Create a new user - const newUser = await UserModel.create({ ...req.body }); + try { + // Create a new user + const newUser = await req.db.insertUser(req, res); + return res.json({ success: true, msg: "User created}", data: newUser }); + // Send an email to user + // Will add this later + } catch (error) { + return res.status(500).json({ success: false, msg: error.message }); + } +}; - // Send an email to user - // Will add this later - } catch (error) { - return res.status(500).json({success:false,msg:'Something went wrong!'}) - } - -} - -module.exports = {registerController} \ No newline at end of file +module.exports = { registerController }; diff --git a/Server/db/FakeDb.js b/Server/db/FakeDb.js index 6dfa89ac9..8cf05cf89 100644 --- a/Server/db/FakeDb.js +++ b/Server/db/FakeDb.js @@ -21,8 +21,10 @@ // ************************** const Monitor = require("../models/Monitor"); +const UserModel = require("../models/user"); const FAKE_MONITOR_DATA = []; +const USERS = []; for (let i = 0; i < 10; i++) { FAKE_MONITOR_DATA.push( @@ -47,6 +49,31 @@ const connect = async () => { } }; +const insertUser = async (req, res) => { + try { + const newUser = new UserModel({ ...req.body }); + USERS.push(newUser); + return newUser; + } catch (error) { + throw error; + } +}; + +const getUserByEmail = async (req, res) => { + const email = req.body.email; + try { + const idx = USERS.findIndex((user) => { + return user.email === email; + }); + if (idx === -1) { + return null; + } + return USERS[idx]; + } catch (error) { + throw new Error(`User with email ${email} not found`); + } +}; + const getAllMonitors = async () => { return FAKE_MONITOR_DATA; }; @@ -75,6 +102,8 @@ const getMonitorsByUserId = async (userId) => { module.exports = { connect, + insertUser, + getUserByEmail, getAllMonitors, getMonitorById, getMonitorsByUserId, diff --git a/Server/db/MongoDB.js b/Server/db/MongoDB.js index 6ad783877..72f2d1d13 100644 --- a/Server/db/MongoDB.js +++ b/Server/db/MongoDB.js @@ -1,5 +1,6 @@ const Monitor = require("../models/Monitor"); const mongoose = require("mongoose"); +const UserModel = require("../models/user"); const connect = async () => { try { @@ -11,6 +12,25 @@ const connect = async () => { } }; +const insertUser = async (req, res) => { + try { + const newUser = await UserModel.create({ ...req.body }); + return newUser; + } catch (error) { + throw error; + } +}; + +const getUserByEmail = async (req, res) => { + try { + // Returns null if no user is found + const user = await UserModel.findOne({ email: req.body.email }); + return user; + } catch (error) { + throw error; + } +}; + // Gets all monitors const getAllMonitors = async (req, res) => { try { @@ -43,6 +63,8 @@ const getMonitorsByUserId = async (req, res) => { module.exports = { connect, + insertUser, + getUserByEmail, getAllMonitors, getMonitorById, getMonitorsByUserId, diff --git a/Server/index.js b/Server/index.js index 967a9899b..7e68c72f2 100644 --- a/Server/index.js +++ b/Server/index.js @@ -5,7 +5,7 @@ const authRouter = require("./routes/authRoute"); const monitorRouter = require("./routes/monitorRoute"); const { connectDbAndRunServer } = require("./configs/db"); require("dotenv").config(); -const logger = require('./utils/logger'); +const logger = require("./utils/logger"); // const { sendEmail } = require('./utils/sendEmail') // ************************** @@ -23,7 +23,7 @@ const db = require("./db/MongoDB"); /** * NOTES - * Email Service will be added + * Email Service will be added * Logger Service will be added (Winston or similar) */ @@ -60,11 +60,11 @@ app.use("/api/v1/monitors", monitorRouter); //health check app.use("/api/v1/healthy", (req, res) => { - try { - logger.info("Checking Health of the server.") - return res.status(200).json({ message: "Healthy" }); - } catch (error) { - logger.error(error.message) + try { + logger.info("Checking Health of the server."); + return res.status(200).json({ message: "Healthy" }); + } catch (error) { + logger.error(error.message); return res.status(500).json({ message: error.message }); } }); diff --git a/Server/models/user.js b/Server/models/user.js index 27d623179..547192293 100644 --- a/Server/models/user.js +++ b/Server/models/user.js @@ -1,45 +1,46 @@ -const mongoose = require('mongoose') -const bcrypt = require('bcrypt') +const mongoose = require("mongoose"); +const bcrypt = require("bcrypt"); const UserSchema = mongoose.Schema({ - username: { - type: String - }, - email: { - type: String, - required: true - }, - password: { - type: String, - required: true - }, - profilePicUrl: { - type: String - }, - isActive: { - type: Boolean, - default: true - }, - isVerified: { - type: Boolean, - default: false - }, - updated_at: { - type: Date, - default: Date.now - }, - created_at: { - type: Date, - default: Date.now - } -}) + username: { + type: String, + }, + email: { + type: String, + required: true, + unique: true, + }, + password: { + type: String, + required: true, + }, + profilePicUrl: { + type: String, + }, + isActive: { + type: Boolean, + default: true, + }, + isVerified: { + type: Boolean, + default: false, + }, + updated_at: { + type: Date, + default: Date.now, + }, + created_at: { + type: Date, + default: Date.now, + }, +}); -UserSchema.pre('save', async function (next) { - if (!this.isModified('password')) { - next() - } - const salt = bcrypt.genSalt(10); - this.password = await bcrypt.hash(this.password, salt); -}) +UserSchema.pre("save", async function (next) { + if (!this.isModified("password")) { + next(); + } + const salt = await bcrypt.genSalt(10); //genSalt is asynchronous, need to wait + this.password = bcrypt.hash(this.password, salt); +}); -module.exports = mongoose.model('User',UserSchema) \ No newline at end of file +module.exports = mongoose.model("User", UserSchema); diff --git a/Server/validation/joi.js b/Server/validation/joi.js index 2fb59a377..53427527e 100644 --- a/Server/validation/joi.js +++ b/Server/validation/joi.js @@ -2,6 +2,7 @@ const joi = require("joi"); const user = require("../models/user"); const authValidation = joi.object({ + username: joi.string(), email: joi.string().email().required(), password: joi.string().min(8).required(), }); From ca33fcae9193188020f286a7ae2782cd5a2b5c38 Mon Sep 17 00:00:00 2001 From: veyselboybay Date: Tue, 14 May 2024 23:15:29 -0400 Subject: [PATCH 15/16] update on joi schema, added logger docs, and updated user model --- Server/controllers/authController.js | 17 ++++++++++++----- Server/controllers/monitorController.js | 5 +++++ Server/db/MongoDB.js | 4 ++-- Server/models/user.js | 7 ++++++- Server/validation/joi.js | 12 +++++++++--- 5 files changed, 34 insertions(+), 11 deletions(-) diff --git a/Server/controllers/authController.js b/Server/controllers/authController.js index 23d5b8877..eba12db7d 100644 --- a/Server/controllers/authController.js +++ b/Server/controllers/authController.js @@ -1,6 +1,7 @@ const express = require("express"); const UserModel = require("../models/user"); -const { authValidation } = require("../validation/joi"); +const { registerValidation } = require("../validation/joi"); +const logger = require('../utils/logger') /** * @function @@ -11,7 +12,7 @@ const { authValidation } = require("../validation/joi"); const registerController = async (req, res) => { // joi validation try { - await authValidation.validateAsync(req.body); + await registerValidation.validateAsync(req.body); } catch (error) { return res .status(400) @@ -21,21 +22,27 @@ const registerController = async (req, res) => { // Check if the user exists try { const isUser = await req.db.getUserByEmail(req, res); - if (isUser) + if (isUser) { + logger.warning("User already exists!", { "service": "auth", "userId": isUser._id }); return res .status(400) .json({ success: false, msg: "User already exists!" }); + } } catch (error) { + logger.error(error.message, { "service": "auth" }); return res.status(500).json({ success: false, msg: error.message }); } + try { // Create a new user const newUser = await req.db.insertUser(req, res); - return res.json({ success: true, msg: "User created}", data: newUser }); - // Send an email to user + // TODO: Send an email to user // Will add this later + logger.info("New user created!", { "service": "auth", "userId": newUser._id }); + return res.json({ success: true, msg: "User created}", data: newUser }); } catch (error) { + logger.error(error.message, { "service": "auth" }); return res.status(500).json({ success: false, msg: error.message }); } }; diff --git a/Server/controllers/monitorController.js b/Server/controllers/monitorController.js index dda18d8ff..c85da0f9d 100644 --- a/Server/controllers/monitorController.js +++ b/Server/controllers/monitorController.js @@ -2,6 +2,7 @@ const { getMonitorsByIdValidation, getMonitorsByUserIdValidation, } = require("../validation/joi"); +const logger = require('../utils/logger') // Gets all monitors const getAllMonitors = async (req, res) => { @@ -9,6 +10,7 @@ const getAllMonitors = async (req, res) => { const monitors = await req.db.getAllMonitors(); return res.json({ success: true, msg: "Monitors found", data: monitors }); } catch (error) { + logger.error(error.message, { "service": "monitor" }); return res.status(500).json({ success: false, msg: error.message }); } }; @@ -27,6 +29,7 @@ const getMonitorById = async (req, res) => { const monitor = await req.db.getMonitorById(monitorId); return res.json({ success: true, msg: "Monitor found", data: monitor }); } catch (error) { + logger.error(error.message, { "service": "monitor" }); return res.status(500).json({ success: false, msg: error.message }); } }; @@ -43,12 +46,14 @@ const getMonitorsByUserId = async (req, res) => { try { const userId = req.params.userId; const monitors = await req.db.getMonitorsByUserId(userId); + logger.info(`Monitors for user ${userId} found`, { "service": "monitor", "userId":userId }); return res.json({ success: true, msg: `Monitors for user ${userId} found`, data: monitors, }); } catch (error) { + logger.error(error.message, { "service": "monitor" }); return res.status(500).json({ success: false, msg: error.message }); } }; diff --git a/Server/db/MongoDB.js b/Server/db/MongoDB.js index 72f2d1d13..e3edbb7e2 100644 --- a/Server/db/MongoDB.js +++ b/Server/db/MongoDB.js @@ -14,7 +14,7 @@ const connect = async () => { const insertUser = async (req, res) => { try { - const newUser = await UserModel.create({ ...req.body }); + const newUser = await UserModel.create({ ...req.body }).select('-password'); return newUser; } catch (error) { throw error; @@ -24,7 +24,7 @@ const insertUser = async (req, res) => { const getUserByEmail = async (req, res) => { try { // Returns null if no user is found - const user = await UserModel.findOne({ email: req.body.email }); + const user = await UserModel.findOne({ email: req.body.email }).select('-password'); return user; } catch (error) { throw error; diff --git a/Server/models/user.js b/Server/models/user.js index 547192293..822c303d8 100644 --- a/Server/models/user.js +++ b/Server/models/user.js @@ -2,8 +2,13 @@ const mongoose = require("mongoose"); const bcrypt = require("bcrypt"); const UserSchema = mongoose.Schema({ - username: { + firstname: { type: String, + required: true, + }, + lastname: { + type: String, + required: true, }, email: { type: String, diff --git a/Server/validation/joi.js b/Server/validation/joi.js index 53427527e..874daff93 100644 --- a/Server/validation/joi.js +++ b/Server/validation/joi.js @@ -1,8 +1,13 @@ const joi = require("joi"); const user = require("../models/user"); -const authValidation = joi.object({ - username: joi.string(), +const loginValidation = joi.object({ + email: joi.string().email().required(), + password: joi.string().min(8).required(), +}); +const registerValidation = joi.object({ + firstname: joi.string().required(), + lastname: joi.string().required(), email: joi.string().email().required(), password: joi.string().min(8).required(), }); @@ -16,7 +21,8 @@ const getMonitorsByUserIdValidation = joi.object({ }); module.exports = { - authValidation, + loginValidation, + registerValidation, getMonitorsByIdValidation, getMonitorsByUserIdValidation, }; From fc05d90640a026616843f62269c4ec5fe982a1e6 Mon Sep 17 00:00:00 2001 From: Mohammad Khalilzadeh <140876993+MuhammadKhalilzadeh@users.noreply.github.com> Date: Wed, 15 May 2024 08:17:05 +0330 Subject: [PATCH 16/16] Delete Client/src/components directory --- .../AnnouncementUpdateSubscription.jsx | 71 ------------- .../announcementUpdateSubscription.css | 99 ------------------- .../AnnouncementsDualButton.jsx | 55 ----------- .../announcementsDualButton.css | 38 ------- .../announcementsDualButtonWithIcon.css | 69 ------------- .../AnnouncementsMessageBar.jsx | 36 ------- .../announcementsMessageBar.css | 12 --- .../Charts/Servers/ServerStatus.jsx | 37 ------- .../Charts/Servers/serverStatus.css | 44 --------- .../Charts/Statistics/Statistic.jsx | 35 ------- .../Charts/Statistics/statistic.css | 31 ------ .../Icons/ComplexAlert/ComplexAlert.jsx | 36 ------- .../Icons/ComplexAlert/complexAlert.css | 44 --------- .../DualButtonPopupModal.jsx | 55 ----------- .../dualButtonPopupModal.css | 60 ----------- .../DualButtonPopupModalWithTextfields.jsx | 73 -------------- .../dualButtonPopupModalWithTextfields.css | 61 ------------ .../Description/DescriptionTextField.jsx | 47 --------- .../Description/descriptionTextField.css | 45 --------- .../TextFields/Email/EmailTextField.jsx | 88 ----------------- .../TextFields/Email/emailTextField.css | 41 -------- .../TextFields/Website/WebsiteTextField.jsx | 72 -------------- .../TextFields/Website/websiteTextField.css | 82 --------------- .../TooltipWithTail/TooltipWithTail.tsx | 54 ---------- .../TooltipWithTail/tooltipWithTail.css | 17 ---- 25 files changed, 1302 deletions(-) delete mode 100644 Client/src/components/Announcements/AnnouncementUpdateSubscription/AnnouncementUpdateSubscription.jsx delete mode 100644 Client/src/components/Announcements/AnnouncementUpdateSubscription/announcementUpdateSubscription.css delete mode 100644 Client/src/components/Announcements/AnnouncementsDualButton/AnnouncementsDualButton.jsx delete mode 100644 Client/src/components/Announcements/AnnouncementsDualButton/announcementsDualButton.css delete mode 100644 Client/src/components/Announcements/AnnouncementsDualButtonWithIcon/announcementsDualButtonWithIcon.css delete mode 100644 Client/src/components/Announcements/AnnouncementsMessageBar/AnnouncementsMessageBar.jsx delete mode 100644 Client/src/components/Announcements/AnnouncementsMessageBar/announcementsMessageBar.css delete mode 100644 Client/src/components/Charts/Servers/ServerStatus.jsx delete mode 100644 Client/src/components/Charts/Servers/serverStatus.css delete mode 100644 Client/src/components/Charts/Statistics/Statistic.jsx delete mode 100644 Client/src/components/Charts/Statistics/statistic.css delete mode 100644 Client/src/components/Icons/ComplexAlert/ComplexAlert.jsx delete mode 100644 Client/src/components/Icons/ComplexAlert/complexAlert.css delete mode 100644 Client/src/components/PopupModals/DualButtonPopupModal/DualButtonPopupModal.jsx delete mode 100644 Client/src/components/PopupModals/DualButtonPopupModal/dualButtonPopupModal.css delete mode 100644 Client/src/components/PopupModals/DualButtonPopupModalWithTextfields/DualButtonPopupModalWithTextfields.jsx delete mode 100644 Client/src/components/PopupModals/DualButtonPopupModalWithTextfields/dualButtonPopupModalWithTextfields.css delete mode 100644 Client/src/components/TextFields/Description/DescriptionTextField.jsx delete mode 100644 Client/src/components/TextFields/Description/descriptionTextField.css delete mode 100644 Client/src/components/TextFields/Email/EmailTextField.jsx delete mode 100644 Client/src/components/TextFields/Email/emailTextField.css delete mode 100644 Client/src/components/TextFields/Website/WebsiteTextField.jsx delete mode 100644 Client/src/components/TextFields/Website/websiteTextField.css delete mode 100644 Client/src/components/Tooltips/TooltipWithTail/TooltipWithTail.tsx delete mode 100644 Client/src/components/Tooltips/TooltipWithTail/tooltipWithTail.css diff --git a/Client/src/components/Announcements/AnnouncementUpdateSubscription/AnnouncementUpdateSubscription.jsx b/Client/src/components/Announcements/AnnouncementUpdateSubscription/AnnouncementUpdateSubscription.jsx deleted file mode 100644 index f3363e866..000000000 --- a/Client/src/components/Announcements/AnnouncementUpdateSubscription/AnnouncementUpdateSubscription.jsx +++ /dev/null @@ -1,71 +0,0 @@ -import "./announcementUpdateSubscription.css"; -import React from "react"; -import PropTypes from "prop-types"; -import { useTheme } from "@mui/material"; - -/** - * @component - * @param {Object} props - * @param {string} props.title - Title text for the update subscription dialog (required) - * @param {string} props.text - Body text for the update subscription dialog (required) - * @param {string} props.cancel - Text for the negative (cancel) button (required) - * @param {string} props.positive - Text for the positive button (required) - * @param {string} props.header - Title text for the email subscription section (required) - * @param {string} props.button - Text for the subscribe button (required) - * @returns {JSX.Element} - Renders the announcement update subscription component - */ - -const AnnouncementUpdateSubscription = ({ - title, - text, - cancel, - positive, - header, - button, -}) => { - const theme = useTheme(); - - const fontLookup = { - default: theme.font.default.font, - }; - - const fontFamily = fontLookup["default"]; - - return ( -
-
-
{title}
-
-
{text}
-
-
- - -
-
-
-
-
{header}
-
- - -
-
-
- ); -}; - -AnnouncementUpdateSubscription.propTypes = { - title: PropTypes.string.isRequired, - text: PropTypes.string.isRequired, - cancel: PropTypes.string.isRequired, - positive: PropTypes.string.isRequired, - header: PropTypes.string.isRequired, - button: PropTypes.string.isRequired, -}; - -export default AnnouncementUpdateSubscription; diff --git a/Client/src/components/Announcements/AnnouncementUpdateSubscription/announcementUpdateSubscription.css b/Client/src/components/Announcements/AnnouncementUpdateSubscription/announcementUpdateSubscription.css deleted file mode 100644 index 7974ee799..000000000 --- a/Client/src/components/Announcements/AnnouncementUpdateSubscription/announcementUpdateSubscription.css +++ /dev/null @@ -1,99 +0,0 @@ -:root { - --update-subscription-color-0: #101828; - --update-subscription-color-1: #475467; - --update-subscription-color-2: #fff; - --update-subscription-color-3: #eaecf0; - --update-subscription-color-4: #7f56d9; - --update-subscription-color-5: #1570ef; - --update-subscription-color-6: #344054; - --update-subscription-color-7: #d0d5dd; - --update-subscription-padding-0: 12px 16px; - --update-subscription-margin-right: 12px; - --update-subscription-font-size: 13px; - --update-subscription-radius-0: 4px; - --spacing-1: 16px; - --spacing-2: 20px; -} - -.update-subscription, -.update-subscription-dialog { - max-width: 770px; - border-radius: 4px; - border: 1px solid #eaecf0; -} - -.update-subscription-dialog { - padding: 15px 30px; -} - -.subscription-dialog-title { - font-size: 1rem; - font-weight: 700; - color: var(--update-subscription-color-0); -} - -.v-spacing-small { - height: 10px; -} - -.v-spacing-medium { - height: 15px; -} - -.subscription-dialog-text { - font-size: 13px; - color: var(--update-subscription-color-1); -} - -.subscription-dialog-controls { - display: flex; -} - -.controls-negative { - padding: var(--update-subscription-padding-0); - margin-right: var(--update-subscription-margin-right); - border-radius: var(--update-subscription-radius-0); - border: 1px solid var(--update-subscription-color-3); - background-color: var(--update-subscription-color-2); - font-size: var(--update-subscription-font-size); -} - -.controls-positive, -.subscribe-button { - padding: var(--update-subscription-padding-0); - margin-right: var(--update-subscription-margin-right); - border-radius: var(--update-subscription-radius-0); - border: 1px solid var(--update-subscription-color-4); - background-color: var(--update-subscription-color-5); - color: var(--update-subscription-color-2); - font-size: var(--update-subscription-font-size); -} - -.update-subscription-title { - color: var(--update-subscription-color-6); - padding: 0 var(--spacing-2); - font-weight: 700; - font-size: 13px; - margin-bottom: 10px; -} - -.subscription-email { - display: flex; - padding: 0 var(--spacing-2); -} - -.subscription-email-field { - width: 290px; - height: 24px; - border-radius: var(--update-subscription-radius-0); - border: 1px solid var(--update-subscription-color-7); - box-shadow: 0px 1px 2px 0px rgba(101, 182, 28, 0.05); - padding: 5px 15px; - margin-right: var(--spacing-2); -} - -.subscribe-button { - width: 100px; - height: 34px; - padding: 2px 15px; -} diff --git a/Client/src/components/Announcements/AnnouncementsDualButton/AnnouncementsDualButton.jsx b/Client/src/components/Announcements/AnnouncementsDualButton/AnnouncementsDualButton.jsx deleted file mode 100644 index 2abbadb1e..000000000 --- a/Client/src/components/Announcements/AnnouncementsDualButton/AnnouncementsDualButton.jsx +++ /dev/null @@ -1,55 +0,0 @@ -import "./announcementsDualButton.css"; -import React from "react"; -import CloseIcon from "@mui/icons-material/Close"; -import PropTypes from "prop-types"; -import { useTheme } from "@mui/material"; - -/** - * @component - * @param {Object} props - * @param {string} props.subject - The announcement subject text - * @param {string} props.body - The announcement body text content - * @param {string} props.esc - The text for the escape button - * @param {string} props.primary - The text for the primary button - * @returns {JSX.Element} - Renders the announcement dual button component - */ - -const AnnouncementsDualButton = ({ subject, body, esc, primary }) => { - const theme = useTheme(); - - const fontLookup = { - default: theme.font.default.font, - }; - - const fontFamily = fontLookup["default"]; - - return ( -
-
-
{subject}
-
-
{body}
-
-
{esc}
-
{primary}
-
-
-
-
- -
-
- ); -}; - -AnnouncementsDualButton.propTypes = { - subject: PropTypes.string.isRequired, - body: PropTypes.string.isRequired, - esc: PropTypes.string.isRequired, - primary: PropTypes.string.isRequired, -}; - -export default AnnouncementsDualButton; diff --git a/Client/src/components/Announcements/AnnouncementsDualButton/announcementsDualButton.css b/Client/src/components/Announcements/AnnouncementsDualButton/announcementsDualButton.css deleted file mode 100644 index 837fd0bb5..000000000 --- a/Client/src/components/Announcements/AnnouncementsDualButton/announcementsDualButton.css +++ /dev/null @@ -1,38 +0,0 @@ -:root { - --announcement-border-color: #d0d5dd; - --announcement-border-radius: 4px; - --announcement-border-radius-2: 8px; - /* --announcement-font-family: "Roboto", "Helvetica", "Arial", sans-serif; */ - --announcement-font-size-0: 13px; - --announcement-font-color-0: #344054; - --announcement-font-color-1: #475467; - --announcement-font-color-2: #1570ef; - --announcement-padding: 16px; - --info-icon-frame-color: #eaecf0; -} - -.announcement-without-tile { - max-width: 343px; - width: fit-content; - display: flex; - border: 1px solid var(--announcement-border-color); - /* font-family: var(--announcement-font-family); */ - padding: var(--announcement-padding); - border-radius: var(--announcement-border-radius); -} - -.h-spacing { - width: 20px; -} - -.announcement-without-content-subject { - font-size: var(--announcement-font-size-0); - color: var(--announcement-font-color-0); - font-weight: 700; - margin-bottom: 10px; -} - -.announcement-without-content-body { - font-size: var(--announcement-font-size-0); - color: var(--announcement-font-color-1); -} diff --git a/Client/src/components/Announcements/AnnouncementsDualButtonWithIcon/announcementsDualButtonWithIcon.css b/Client/src/components/Announcements/AnnouncementsDualButtonWithIcon/announcementsDualButtonWithIcon.css deleted file mode 100644 index 80c6ae265..000000000 --- a/Client/src/components/Announcements/AnnouncementsDualButtonWithIcon/announcementsDualButtonWithIcon.css +++ /dev/null @@ -1,69 +0,0 @@ -:root { - --announcement-border-color: #d0d5dd; - --announcement-border-radius: 4px; - --announcement-border-radius-2: 8px; - --announcement-font-size-0: 13px; - --announcement-font-color-0: #344054; - --announcement-font-color-1: #475467; - --announcement-font-color-2: #1570ef; - --announcement-padding: 16px; - --info-icon-frame-color: #eaecf0; -} - -.announcement-tile { - width: fit-content; - display: flex; - border: 1px solid var(--announcement-border-color); - padding: var(--announcement-padding); - border-radius: var(--announcement-border-radius); -} - -.info-icon-frame { - display: flex; - width: 40px; - height: 40px; - border: 1px solid var(--info-icon-frame-color); - border-radius: var(--announcement-border-radius-2); - justify-content: center; - align-items: center; -} - -.announcement-content { - padding: 0 var(--announcement-padding); -} - -.announcement-content-subject { - font-size: var(--announcement-font-size-0); - color: var(--announcement-font-color-0); - font-weight: 700; - margin-bottom: 10px; -} - -.announcement-content-body { - font-size: var(--announcement-font-size-0); - color: var(--announcement-font-color-1); - font-weight: 300; -} - -.announcement-content-controllers { - display: flex; - font-size: var(--announcement-font-size-0); - margin-top: 15px; -} - -.controllers-button-esc { - font-size: var(--announcement-font-size-0); - font-weight: 600; - margin-right: 12px; -} - -.controllers-button-primary { - font-size: var(--announcement-font-size-0); - font-weight: 600; - color: var(--announcement-font-color-2); -} - -.announcement-close { - width: 24px; - height: 24px; -} diff --git a/Client/src/components/Announcements/AnnouncementsMessageBar/AnnouncementsMessageBar.jsx b/Client/src/components/Announcements/AnnouncementsMessageBar/AnnouncementsMessageBar.jsx deleted file mode 100644 index 20a179052..000000000 --- a/Client/src/components/Announcements/AnnouncementsMessageBar/AnnouncementsMessageBar.jsx +++ /dev/null @@ -1,36 +0,0 @@ -import "./announcementsMessageBar.css"; -import React from "react"; -import CloseIcon from "@mui/icons-material/Close"; -import PropTypes from "prop-types"; -import { useTheme } from "@mui/material"; - -/** - * @component - * @param {Object} props - * @param {string} props.message - The announcement message text content (required) - * @returns {JSX.Element} - Renders the announcement message bar component - */ -const AnnouncementsMessageBar = ({ message }) => { - const theme = useTheme(); - - const fontLookup = { - default: theme.font.default.font, - }; - - const fontFamily = fontLookup["default"]; - - return ( -
-
{message}
-
- -
-
- ); -}; - -AnnouncementsMessageBar.propTypes = { - message: PropTypes.string.isRequired, -}; - -export default AnnouncementsMessageBar; diff --git a/Client/src/components/Announcements/AnnouncementsMessageBar/announcementsMessageBar.css b/Client/src/components/Announcements/AnnouncementsMessageBar/announcementsMessageBar.css deleted file mode 100644 index 2e9f95e83..000000000 --- a/Client/src/components/Announcements/AnnouncementsMessageBar/announcementsMessageBar.css +++ /dev/null @@ -1,12 +0,0 @@ -:root { - --announcement-font-size-0: 13px; - --announcement-padding: 16px; -} - -.announcement-messagebar-body { - font-size: var(--announcement-font-size-0); - padding: 0 var(--announcement-padding) 0 0; - justify-content: center; - align-items: center; - margin: auto; -} diff --git a/Client/src/components/Charts/Servers/ServerStatus.jsx b/Client/src/components/Charts/Servers/ServerStatus.jsx deleted file mode 100644 index f910abf27..000000000 --- a/Client/src/components/Charts/Servers/ServerStatus.jsx +++ /dev/null @@ -1,37 +0,0 @@ -import React from "react"; -import "./serverStatus.css"; -import PropTypes from "prop-types"; -import { useTheme } from "@mui/material"; - -/** - * @component - * @param {Object} props - * @param {string} props.title - The title text for the server status (required) - * @param {string} props.value - The value text to be displayed (required) - * @param {string} props.state - The state of the server (e.g., "online", "offline", "warning") (required) - * @returns {JSX.Element} - Renders the server status component - */ -const ServerStatus = ({ title, value, state }) => { - const theme = useTheme(); - - const fontLookup = { - default: theme.font.default.font, - }; - - const fontFamily = fontLookup["default"]; - - return ( -
-
{title}
-
{value}
-
- ); -}; - -ServerStatus.propTypes = { - title: PropTypes.string.isRequired, - value: PropTypes.string.isRequired, - state: PropTypes.string.isRequired, -}; - -export default ServerStatus; diff --git a/Client/src/components/Charts/Servers/serverStatus.css b/Client/src/components/Charts/Servers/serverStatus.css deleted file mode 100644 index 9126d9b3b..000000000 --- a/Client/src/components/Charts/Servers/serverStatus.css +++ /dev/null @@ -1,44 +0,0 @@ -:root { - --spacing-general-0: 10px 25px; - --spacing-general-1: 20px; - --color-border-0: #eaecf0; - --border-radius-0: 4px; - --font-size-0: 1rem; - --font-size-2: 36px; - - --color-up: #17b26a; - --color-down: #f04438; - --color-pause: #fdb022; -} - -.server-status-tile { - width: 230px; - margin: var(--spacing-general-0); - margin-left: 10px; - padding: var(--spacing-general-1); - border: 1px solid var(--color-border-0); - border-radius: var(--border-radius-0); -} - -.server-status-tile-title { - font-size: var(--font-size-0); - font-weight: bold; - margin-bottom: 25px; -} - -.server-status-tile-value { - font-size: var(--font-size-2); - font-weight: bolder; -} - -.up { - color: var(--color-up); -} - -.down { - color: var(--color-down); -} - -.pause { - color: var(--color-pause); -} diff --git a/Client/src/components/Charts/Statistics/Statistic.jsx b/Client/src/components/Charts/Statistics/Statistic.jsx deleted file mode 100644 index 99ad2066f..000000000 --- a/Client/src/components/Charts/Statistics/Statistic.jsx +++ /dev/null @@ -1,35 +0,0 @@ -import React from "react"; -import "./statistic.css"; -import PropTypes from "prop-types"; -import { useTheme } from "@mui/material"; - -/** - * @component - * @param {Object} props - * @param {string} props.title - The title text for the statistic (required) - * @param {string} props.value - The numerical or textual value for the statistic (required) - * @returns {JSX.Element} - Renders the statistic component - */ -const Statistic = ({ title, value }) => { - const theme = useTheme(); - - const fontLookup = { - default: theme.font.default.font, - }; - - const fontFamily = fontLookup["default"]; - - return ( -
-
{title}
-
{value}
-
- ); -}; - -Statistic.propTypes = { - title: PropTypes.string.isRequired, - value: PropTypes.string.isRequired, -}; - -export default Statistic; diff --git a/Client/src/components/Charts/Statistics/statistic.css b/Client/src/components/Charts/Statistics/statistic.css deleted file mode 100644 index a8834199d..000000000 --- a/Client/src/components/Charts/Statistics/statistic.css +++ /dev/null @@ -1,31 +0,0 @@ -:root { - --stats-spacing-general-0: 5px 10px; - --color-border-0: #eaecf0; - --border-radius-0: 4px; - --font-size-0: 1rem; - --font-size-1: 13px; - --font-color-0: #344054; - --margin-bottom-0: 10px; -} - -.statistic-tile { - margin: var(--stats-spacing-general-0); - padding: var(--stats-spacing-general-0); - border: 1px solid var(--color-border-0); - border-radius: var(--border-radius-0); - - width: 180px; -} - -.statistic-tile-title { - font-size: var(--font-size-0); - color: var(--font-color-0); - margin-bottom: var(--margin-bottom-0); -} - -.statistic-tile-value { - font-size: var(--font-size-1); - color: var(--font-color-0); - font-weight: 700; - margin-bottom: var(--margin-bottom-0); -} diff --git a/Client/src/components/Icons/ComplexAlert/ComplexAlert.jsx b/Client/src/components/Icons/ComplexAlert/ComplexAlert.jsx deleted file mode 100644 index 67af3ab88..000000000 --- a/Client/src/components/Icons/ComplexAlert/ComplexAlert.jsx +++ /dev/null @@ -1,36 +0,0 @@ -import "./complexAlert.css"; -import React from "react"; -import ErrorOutlineIcon from "@mui/icons-material/ErrorOutline"; -import PropTypes from "prop-types"; - -/** - * @component - * @param {Object} props - * @param {"red" | "green"} props.theme - The color theme for the alert (required) - must be either "red" or "green" - * @returns {JSX.Element} - Renders the complex alert component with dynamic color theme - */ -const ComplexAlert = ({ theme }) => { - if (theme === "red") { - return ( -
-
- -
-
- ); - } else if (theme === "green") { - return ( -
-
- -
-
- ); - } -}; - -ComplexAlert.propTypes = { - theme: PropTypes.string.isRequired, -}; - -export default ComplexAlert; diff --git a/Client/src/components/Icons/ComplexAlert/complexAlert.css b/Client/src/components/Icons/ComplexAlert/complexAlert.css deleted file mode 100644 index ab3cea714..000000000 --- a/Client/src/components/Icons/ComplexAlert/complexAlert.css +++ /dev/null @@ -1,44 +0,0 @@ -.icon-holder-outer-red { - width: 38px; - height: 38px; - border-radius: 50%; - border: 2px solid #d92d2019; - display: flex; - justify-content: center; - align-items: center; -} - -.icon-holder-inner-red { - width: 28px; - height: 28px; - border-radius: 50%; - border: 2px solid #d92d204d; - display: flex; - justify-content: center; - align-items: center; -} - -.icon-holder-outer-green { - width: 38px; - height: 38px; - border-radius: 50%; - border: 2px solid #07945519; - display: flex; - justify-content: center; - align-items: center; -} - -.icon-holder-inner-green { - width: 28px; - height: 28px; - border-radius: 50%; - border: 2px solid #0794554d; - display: flex; - justify-content: center; - align-items: center; -} - -.icon-core { - width: 20px; - height: 20px; -} diff --git a/Client/src/components/PopupModals/DualButtonPopupModal/DualButtonPopupModal.jsx b/Client/src/components/PopupModals/DualButtonPopupModal/DualButtonPopupModal.jsx deleted file mode 100644 index 42a3f2a8a..000000000 --- a/Client/src/components/PopupModals/DualButtonPopupModal/DualButtonPopupModal.jsx +++ /dev/null @@ -1,55 +0,0 @@ -import "./dualButtonPopupModal.css"; -import React from "react"; -import PropTypes from "prop-types"; -import { useTheme } from "@mui/material"; - -/** - * @component - * @param {Object} props - * @param {boolean} [props.open=true] - Controls the visibility of the modal (defaults to true) - * @param {string} props.subject - The title text for the modal (required) - * @param {string} props.description - The description text for the modal (required) - * @param {string} props.esc - The text for the discard button (usually "Cancel", "Dismiss" or "Discard") (required) - * @param {string} props.save - The text for the save button (required) - * @returns {JSX.Element} - Renders the dual button popup modal component - */ -const DualButtonPopupModal = ({ - open = true, - subject, - description, - esc, - save, -}) => { - const theme = useTheme(); - - const fontLookup = { - default: theme.font.default.font, - }; - - const fontFamily = fontLookup["default"]; - - return ( -
-
{subject}
-
{description}
-
-
- - -
-
- ); -}; - -DualButtonPopupModal.propTypes = { - open: PropTypes.bool, - subject: PropTypes.string.isRequired, - description: PropTypes.string.isRequired, - esc: PropTypes.string.isRequired, - save: PropTypes.string.isRequired, -}; - -export default DualButtonPopupModal; diff --git a/Client/src/components/PopupModals/DualButtonPopupModal/dualButtonPopupModal.css b/Client/src/components/PopupModals/DualButtonPopupModal/dualButtonPopupModal.css deleted file mode 100644 index 92ac7f5d6..000000000 --- a/Client/src/components/PopupModals/DualButtonPopupModal/dualButtonPopupModal.css +++ /dev/null @@ -1,60 +0,0 @@ -:root { - --popup-color-border-0: #f2f2f2; - --popup-color-0: #101828; - --popup-color-1: #475467; - --color-0: #1570ef; - --color-1: #175cd3; -} - -.dual-button-popup-modal-holder { - width: 380px; - height: 130px; - margin: 10px 20px; - padding: 30px; - box-shadow: 0px 8px 8px -4px rgba(16, 24, 40, 0.03), - 0px 20px 24px -4px rgba(16, 24, 40, 0.08); - border: 1px solid var(--popup-color-border-0); - border-radius: 4px; -} - -.dual-button-popup-modal-subject { - color: var(--popup-color-0); - font-weight: bolder; - font-size: 16px; -} - -.dual-button-popup-modal-description { - color: var(--popup-color-1); - font-size: 13px; - margin-top: 10px; -} - -.dual-modal-spacing { - height: 40px; -} - -.dual-button-popup-modal-controllers { - display: flex; - justify-content: end; -} - -.blue-save-button { - width: 145px; - height: 35px; - padding: 5px 20px; - background-color: var(--color-0); - border: 1px solid var(--color-1); - border-radius: 4px; - margin: 5px 0; - margin-left: 20px; - color: white; - cursor: pointer; - font-size: 13px; -} - -.transparent-discard-button { - border: none; - background-color: transparent; - margin: 5px 10px; - font-size: 13px; -} diff --git a/Client/src/components/PopupModals/DualButtonPopupModalWithTextfields/DualButtonPopupModalWithTextfields.jsx b/Client/src/components/PopupModals/DualButtonPopupModalWithTextfields/DualButtonPopupModalWithTextfields.jsx deleted file mode 100644 index b8b424323..000000000 --- a/Client/src/components/PopupModals/DualButtonPopupModalWithTextfields/DualButtonPopupModalWithTextfields.jsx +++ /dev/null @@ -1,73 +0,0 @@ -import "./dualButtonPopupModalWithTextfields.css"; -import React from "react"; -import CloseIcon from "@mui/icons-material/Close"; -import PropTypes from "prop-types"; -import { useTheme } from "@mui/material"; - -/** - * @component - * @param {Object} props - * @param {string} props.title - The title text for the modal (required) - * @returns {JSX.Element} - Renders a single text field component within a popup modal - */ -const PopupModalTextfield = ({ title }) => { - const theme = useTheme(); - - const fontLookup = { - default: theme.font.default.font, - }; - - const fontFamily = fontLookup["default"]; - - return ( -
-
{title}
- -
- ); -}; - -/** - * @component - * @param {Object} props - * @param {string} props.title - The title text for the modal (required) - * @param {string} props.esc - The text for the cancel button (required) - * @param {string} props.save - The text for the save button (required) - * @returns {JSX.Element} - Renders the dual button popup modal component with text fields - */ -const DualButtonPopupModalWithTextfields = ({ title, esc, save }) => { - const theme = useTheme(); - - const fontLookup = { - default: theme.font.default.font, - }; - - const fontFamily = fontLookup["default"]; - - return ( -
-
-
{title}
-
- -
-
-
- -
-
-
- - -
-
- ); -}; - -DualButtonPopupModalWithTextfields.propTypes = { - title: PropTypes.string.isRequired, - esc: PropTypes.string.isRequired, - save: PropTypes.string.isRequired, -}; - -export default DualButtonPopupModalWithTextfields; diff --git a/Client/src/components/PopupModals/DualButtonPopupModalWithTextfields/dualButtonPopupModalWithTextfields.css b/Client/src/components/PopupModals/DualButtonPopupModalWithTextfields/dualButtonPopupModalWithTextfields.css deleted file mode 100644 index a1be3cd3a..000000000 --- a/Client/src/components/PopupModals/DualButtonPopupModalWithTextfields/dualButtonPopupModalWithTextfields.css +++ /dev/null @@ -1,61 +0,0 @@ -:root { - --popup-color-with-input-border-0: #f2f2f2; - --popup-color-with-input-border-1: #d0d5dd; - --popup-modal-with-input-title-color: #344054; -} - -.popup-modal-holder { - width: 480px; - font-family: var(--popup-font-family-0); - padding: 50px 40px; - border-radius: 4px; - margin: 10px 20px; - border: 1px solid var(--popup-color-with-input-border-0); - box-shadow: 0px 8px 8px -4px rgba(16, 24, 40, 0.03), - 0px 20px 24px -4px rgba(16, 24, 40, 0.08); -} - -.popup-modal-header { - display: flex; - justify-content: space-between; - font-weight: bold; -} - -.popup-modal-close { - cursor: pointer; -} - -.spacing-height { - height: 30px; -} - -.popup-modal-input-title { - margin-bottom: 15px; - color: var(--popup-modal-with-input-title-color); -} - -.popup-modal-text-field { - width: 100%; - height: 35px; - border: 1px solid var(--popup-color-with-input-border-1); - border-radius: 4px; -} - -.popup-modal-controllers { - display: flex; - justify-content: end; -} - -.white-cancel-button { - border: none; - background-color: transparent; - font-family: var(--popup-font-family-0); - margin: 5px 10px; - width: 145px; - height: 35px; - padding: 5px 20px; - border: 1px solid var(--popup-color-with-input-border-1); - border-radius: 4px; - cursor: pointer; - font-size: 13px; -} diff --git a/Client/src/components/TextFields/Description/DescriptionTextField.jsx b/Client/src/components/TextFields/Description/DescriptionTextField.jsx deleted file mode 100644 index 7f3ba908a..000000000 --- a/Client/src/components/TextFields/Description/DescriptionTextField.jsx +++ /dev/null @@ -1,47 +0,0 @@ -import React from "react"; -import "./descriptionTextField.css"; -import { TextField } from "@mui/material"; -import PropTypes from "prop-types"; -import { useTheme } from "@mui/material"; - -/** - * @component - * @param {Object} props - * @param {string} props.hintText - The hint text displayed within the text field (required) - * @param {boolean} props.hasError - A flag indicating if the text field has an error (required) - * @returns {JSX.Element} - Renders the description text field component - */ -const DescriptionTextField = ({ hintText, hasError }) => { - const theme = useTheme(); - - const fontLookup = { - default: theme.font.default.font, - }; - - const fontFamily = fontLookup["default"]; - - return ( -
-
Description
- -
- ); -}; - -DescriptionTextField.propTypes = { - hintText: PropTypes.string.isRequired, - hasError: PropTypes.bool.isRequired, -}; - -export default DescriptionTextField; diff --git a/Client/src/components/TextFields/Description/descriptionTextField.css b/Client/src/components/TextFields/Description/descriptionTextField.css deleted file mode 100644 index 28616863e..000000000 --- a/Client/src/components/TextFields/Description/descriptionTextField.css +++ /dev/null @@ -1,45 +0,0 @@ -:root { - --color-title-1: #344054; - --text-field-margin-0: 10px 20px; - --description-color-0: #344054; -} - -.description-field-holder { - margin: var(--text-field-margin-0); - margin-top: 20px; -} - -.text-field-title { - color: var(--color-title-1); - font-weight: bold; - margin-bottom: 10px; - font-size: 13px; - color: var(--description-color-0); - font-weight: bold; -} - -.description-field-area { - min-width: 320px; - font-size: 13px; - border-radius: 8px; -} - -.description-field-label { - margin-top: 10px; - font-size: 11px; -} - -.with-error { - color: red; -} - -#description-field-area-helper-text { - margin-left: 0; - margin-top: 10px; - font-size: 11px; -} - -#description-field-area { - min-width: 320px; - font-size: 13px; -} diff --git a/Client/src/components/TextFields/Email/EmailTextField.jsx b/Client/src/components/TextFields/Email/EmailTextField.jsx deleted file mode 100644 index b1428e1c5..000000000 --- a/Client/src/components/TextFields/Email/EmailTextField.jsx +++ /dev/null @@ -1,88 +0,0 @@ -import React, { useState } from "react"; -import "./emailTextField.css"; -import { InputAdornment, TextField } from "@mui/material"; -import ErrorOutlineIcon from "@mui/icons-material/ErrorOutline"; -import HelpOutlineIcon from "@mui/icons-material/HelpOutline"; -import { useTheme } from "@mui/material"; - -/** - * @component - * @param {Object} props - * @param {string} props.id - Unique ID for the text field (optional) - * @param {string} props.label - The label text displayed above the text field (optional) - * @param {"standard" | "outlined" | "filled"} props.variant - The variant of the text field (e.g., "outlined") (optional) - * @param {string} props.placeholder - The placeholder text displayed within the text field (optional) - * @param {"error" | "help"} [props.icon] - The type of icon to display (error or help) (optional) - * @param {string} props.helperText - The helper text displayed below the text field (optional) - * @param {boolean} props.error - A flag indicating if the text field has an error (required) - * @returns {JSX.Element} - Renders the email text field component with dynamic icon display - */ -const EmailTextField = ({ - id, - label, - variant, - placeholder, - icon, - helperText, - error, -}) => { - const theme = useTheme(); - - const fontLookup = { - default: theme.font.default.font, - }; - - const fontFamily = fontLookup["default"]; - - const [showIcon, setShowIcon] = useState(false); - - const handleMouseEnter = () => setShowIcon(true); - const handleMouseLeave = () => setShowIcon(false); - - return ( -
-
Email
-
- -
- {error && showIcon && ( - - )} - {error && !showIcon && ( - - )} - {!error && showIcon && ( - - )} -
- - ), - }} - helperText={helperText} - onMouseEnter={handleMouseEnter} - onMouseLeave={handleMouseLeave} - /> -
-
- ); -}; - -export default EmailTextField; diff --git a/Client/src/components/TextFields/Email/emailTextField.css b/Client/src/components/TextFields/Email/emailTextField.css deleted file mode 100644 index ff38a29df..000000000 --- a/Client/src/components/TextFields/Email/emailTextField.css +++ /dev/null @@ -1,41 +0,0 @@ -:root { - --textfield-color-0: #344054; -} - -.email-text-field { - position: relative; - margin: 10px 20px; -} - -#outlined-basic { - width: 320px; - height: 14px; - font-size: 13px; - padding: 8px 12px; -} - -.email-text-field-title { - margin: 0 20px; - margin-top: 30px; - font-size: 13px; - color: var(--textfield-color-0); - font-weight: bold; -} - -#outlined-basic-helper-text { - margin-left: 0; - margin-top: 10px; - font-size: 11px; -} - -.text-field-icon { - width: 16px; - height: 16px; -} - -.icon-holder { - width: 16px; - height: 16px; - padding: 0; - margin: 0; -} diff --git a/Client/src/components/TextFields/Website/WebsiteTextField.jsx b/Client/src/components/TextFields/Website/WebsiteTextField.jsx deleted file mode 100644 index d18e33dd9..000000000 --- a/Client/src/components/TextFields/Website/WebsiteTextField.jsx +++ /dev/null @@ -1,72 +0,0 @@ -import React, { useState } from "react"; -import "./websiteTextField.css"; -import HelpOutlineIcon from "@mui/icons-material/HelpOutline"; -import ContentCopyIcon from "@mui/icons-material/ContentCopy"; -import { useTheme } from "@mui/material"; - -/** - * @component - * @param {Object} props - * @param {boolean} [props.showHelp=true] - Controls the visibility of the help icon (defaults to true) - * @param {boolean} [props.hasCopyButton=false] - Controls the visibility of the copy button (defaults to false) - * @param {string} props.hintText - The hint text displayed below the text field (required) - * @returns {JSX.Element} - Renders the website text field component with optional help and copy features - */ -const WebsiteTextField = ({ - showHelp = true, - hasCopyButton = false, - hintText, -}) => { - const theme = useTheme(); - - const fontLookup = { - default: theme.font.default.font, - }; - - const fontFamily = fontLookup["default"]; - - const [copied, setCopied] = useState(false); - - const handleCopy = () => { - setCopied(true); - setTimeout(() => setCopied(false), 1000); - }; - - return ( -
-
Website
-
- - - {showHelp && ( -
- -
- )} - {hasCopyButton && ( -
- - {copied ? " Copied " : " Copy "} -
- )} -
- -
- ); -}; - -export default WebsiteTextField; diff --git a/Client/src/components/TextFields/Website/websiteTextField.css b/Client/src/components/TextFields/Website/websiteTextField.css deleted file mode 100644 index 66cdf4fa4..000000000 --- a/Client/src/components/TextFields/Website/websiteTextField.css +++ /dev/null @@ -1,82 +0,0 @@ -:root { - --website-color-0: #344054; - --border-color: #d0d5dd; - --label-font-color: #475467; - --icon-color: #98a2b3; - --text-field-margin-0: 10px 20px; - --text-field-margin-1: 10px 20px 5px; -} - -.website-textfield-title { - margin: 0 20px; - margin-top: 30px; - font-size: 13px; - color: var(--website-color-0); - font-weight: bold; -} - -.website-textfield { - display: flex; - align-items: center; - position: relative; - margin: var(--text-field-margin-1); -} - -.website-textfield-hint { - margin: var(--text-field-margin-0); - font-size: 11px; -} - -.website-label { - padding: 6px 20px 6px 10px; - border: solid 1px var(--border-color); - border-right: none; - border-radius: 4px 0 0 4px; - font-size: 13px; - text-align: center; - display: flex; - justify-content: center; - align-items: center; - color: var(--label-font-color); -} - -.website-url { - width: 248px; - height: 31.5px; - padding: 0px 15px; - border: solid 1px var(--border-color); -} - -.website-icon-holder { - width: 40px; - height: 33px; - padding: 5px; - border: solid 1px var(--border-color); - border-left: none; - text-align: center; - display: flex; - justify-content: center; - align-items: center; -} - -.copy-to-clipboard-button { - padding: 5.5px 20px 6px 10px; - border: solid 1px var(--border-color); - border-left: none; - border-radius: 0 8px 8px 0; - font-size: 13px; - text-align: center; - display: flex; - justify-content: space-evenly; - align-items: center; - color: var(--label-font-color); - cursor: pointer; -} - -.copy-icon { - margin-right: 5px; -} - -.with-no-copy-button { - display: none; -} diff --git a/Client/src/components/Tooltips/TooltipWithTail/TooltipWithTail.tsx b/Client/src/components/Tooltips/TooltipWithTail/TooltipWithTail.tsx deleted file mode 100644 index 9d7f65122..000000000 --- a/Client/src/components/Tooltips/TooltipWithTail/TooltipWithTail.tsx +++ /dev/null @@ -1,54 +0,0 @@ -import "./tooltipWithTail.css"; -import React from "react"; -import { styled } from "@mui/material/styles"; -import Tooltip, { TooltipProps, tooltipClasses } from "@mui/material/Tooltip"; -import HelpOutlineIcon from "@mui/icons-material/HelpOutline"; -import { useTheme } from "@mui/material"; - -const CustomizedTooltip = styled( - ({ className, placement, arrow, ...props }: TooltipProps) => ( - - ) -)(({ theme }) => ({ - [`& .${tooltipClasses.tooltip}`]: { - backgroundColor: "black", - }, - [`& .${tooltipClasses.arrow}`]: { - color: "black", - }, -})); - -const TooltipWithTail = ({ placement, arrow = false, title, text }) => { - const theme = useTheme(); - - const fontLookup = { - default: theme.font.default.font, - }; - - const fontFamily = fontLookup["default"]; - - return ( -
- - {title &&
{title}
} -
{text}
- - } - className="tooltip-holder" - > - -
-
- ); -}; - -export default TooltipWithTail; diff --git a/Client/src/components/Tooltips/TooltipWithTail/tooltipWithTail.css b/Client/src/components/Tooltips/TooltipWithTail/tooltipWithTail.css deleted file mode 100644 index 1b1adf507..000000000 --- a/Client/src/components/Tooltips/TooltipWithTail/tooltipWithTail.css +++ /dev/null @@ -1,17 +0,0 @@ -:root { - --tooltip-color-0: #d0d5dd; - --tooltip-margin: 5px; - --tooltip-font-size: 12px; -} - -.tooltip-title { - font-size: var(--tooltip-font-size); - font-weight: bold; - margin: var(--tooltip-margin); -} - -.tooltip-description { - font-size: var(--tooltip-font-size); - color: var(--tooltip-color-0); - margin: var(--tooltip-margin); -}