Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
141,946 changes: 123,059 additions & 18,887 deletions client-side/package-lock.json

Large diffs are not rendered by default.

63 changes: 53 additions & 10 deletions client-side/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,43 +3,47 @@
"version": "0.1.0",
"private": true,
"dependencies": {
"@apollo/client": "^3.11.1",
"@babel/plugin-proposal-private-property-in-object": "^7.21.11",
"@emotion/react": "^11.11.4",
"@emotion/styled": "^11.11.5",
"@fontsource/roboto": "^5.0.13",
"@mui/icons-material": "^5.16.0",
"@mui/material": "^5.16.0",
"@mui/styled-engine-sc": "^6.0.0-alpha.18",
"@mui/x-charts": "^7.10.0",
"@mui/x-data-grid": "^7.9.0",
"@mui/x-date-pickers": "^7.12.0",
"@reduxjs/toolkit": "^2.2.6",
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@mui/x-data-grid": "^7.10.0",
"@testing-library/user-event": "^13.5.0",
"@types/redux": "^3.6.31",
"axios": "^1.7.2",
"dayjs": "^1.11.12",
"@testing-library/react": "^13.4.0",
"formik": "^2.4.6",
"html2canvas": "^1.4.1",
"html2pdf.js": "^0.10.2",
"jest": "^29.7.0",
"jspdf": "^2.5.1",
"jspdf-autotable": "^3.8.2",
"@mui/x-date-pickers": "^7.11.0",
"@reduxjs/toolkit": "^2.2.6",
"@testing-library/jest-dom": "^5.17.0",
"@types/redux": "^3.6.31",
"axios": "^1.7.2",
"dayjs": "^1.11.12",
"dotenv": "^16.4.5",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-google-recaptcha": "^3.1.0",
"react-modal": "^3.16.1",
"react-redux": "^9.1.2",
"react-router-dom": "^6.24.1",
"react-scripts": "5.0.1",
"redux": "^5.0.1",
"styled-components": "^6.1.11",
"web-vitals": "^2.1.4",
"ws": "^8.18.0",
"yup": "^1.4.0"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"test": "jest",
"eject": "react-scripts eject",
"storybook": "storybook dev -p 6006",
"build-storybook": "storybook build"
Expand All @@ -64,6 +68,11 @@
]
},
"devDependencies": {
"@apollo/client": "^3.11.4",
"@babel/core": "^7.25.2",
"@babel/plugin-transform-private-property-in-object": "^7.24.7",
"@babel/preset-env": "^7.25.3",
"@babel/preset-react": "^7.24.7",
"@chromatic-com/storybook": "^1.6.0",
"@eslint/compat": "^1.1.0",
"@eslint/js": "^9.6.0",
Expand All @@ -77,14 +86,48 @@
"@storybook/react": "^8.1.11",
"@storybook/react-webpack5": "^8.1.11",
"@storybook/test": "^8.1.11",
"@testing-library/jest-dom": "^6.4.8",
"@testing-library/react": "^16.0.0",
"babel-jest": "^29.7.0",
"eslint-config-react-app": "^7.0.1",
"eslint-plugin-react": "^7.27.1",
"eslint-plugin-storybook": "^0.8.0",
"globals": "^15.8.0",
"install": "^0.13.0",
"jest": "^29.7.0",
"jest-environment-jsdom": "^29.7.0",
"notistack": "^3.0.1",
"npm": "^10.8.2",
"prop-types": "^15.8.1",
"react-apollo": "^3.1.5",
"react-scripts": "^5.0.1",
"redux-mock-store": "^1.5.4",
"sass": "^1.77.6",
"storybook": "^8.1.11"
},
"jest": {
"transform": {
"^.+\\.(js|jsx|ts|tsx)$": "babel-jest"
},
"moduleNameMapper": {
"\\.(css|scss)$": "identity-obj-proxy",
"\\.(jpg|jpeg|png|gif|webp|svg)$": "<rootDir>/__mocks__/fileMock.js",
"^src/(.*)$": "<rootDir>/src/$1"
},
"testEnvironment": "jest-environment-jsdom",
"testEnvironmentOptions": {},
"coverageDirectory": "coverage",
"collectCoverage": true
},
"babel": {
"presets": [
"@babel/preset-env",
"@babel/preset-react"
],
"plugins": [
"@babel/plugin-transform-private-property-in-object"
],
"storybook": "^8.1.11",
"webpack-cli": "^5.1.4"
}
}
30 changes: 20 additions & 10 deletions client-side/src/App.jsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,33 @@
import React from 'react';
import { Provider } from 'react-redux';
import { RouterProvider } from 'react-router-dom';
import { HashRouter, Routes, Route } from 'react-router-dom';
import Footer from './stories/footer/FooterComponent';
import { router } from './router/router.jsx';
import { store } from './redux/store.jsx';
import { SnackbarProvider } from 'notistack';
import Layout from './router/layout.jsx';
import './App.scss';

function App() {
return (

<>
<SnackbarProvider maxSnack={3}>
<Provider store={store}>
<RouterProvider router={router} />
<SnackbarProvider maxSnack={3}>
{/* // TODO insert the real routings */}
<Provider store={store}>
<HashRouter>
<Routes>
<Route path="/" element={<Layout />}>
<Route index element={<h1>home</h1>} />
<Route path="home" element={<h1>home</h1>} />
<Route path="profiles" element={<h1>ProfileList</h1>} />
<Route path="reports" element={<h1>reports</h1>} />
<Route path="statistics" element={<h1>statistics</h1>} />
<Route path="*" element={<h1>home</h1>} />
</Route>
</Routes>
<Footer />
</Provider>
</SnackbarProvider>
</>
</HashRouter>
</Provider>
</SnackbarProvider>
);
}

export default App;
16 changes: 11 additions & 5 deletions client-side/src/stories/header/Icon.jsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,33 @@
import React from 'react';
import React, { useState } from 'react';
import { Badge, IconButton } from '@mui/material';
import { MailOutline, Mail } from '@mui/icons-material';
import useWebSocket from '../../webSocket';
import messages from '../../components/messages/messages'
import './icon.scss';

const MessageIcon = ({ messages = [] }) => {
let cntUnreadMessages = messages.filter(message => !message.read).length;
const MessageIcon = () => {
const [openMesagges,setOpenMesagges] = useState(false);
const userId = '66961bef8ca0916dcbfdabc3'; //change the userId according the correct user...
const { cntUnreadMessages } = useWebSocket(userId);

const hasUnreadMessages = cntUnreadMessages > 0;

const handleClick = () => {
// Displays a generic component with the list
};

return (
<IconButton onClick={handleClick} className="iconButton">
<IconButton onClick={()=>{setOpenMesagges(!openMesagges);}} className="iconButton">
{hasUnreadMessages ? (
<Badge badgeContent={cntUnreadMessages} color="error" className="badgeContent">
<Mail />
</Badge>
) : (
<MailOutline />
)}
{openMesagges && <messages/>}
</IconButton>
);
};

export default MessageIcon;
export default MessageIcon;
31 changes: 31 additions & 0 deletions client-side/src/webSocket.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { useEffect, useState } from 'react';

const useWebSocket = (userId) => {
const [cntUnreadMessages, setCntUnreadMessages] = useState(0);
const [socket, setSocket] = useState(null);

useEffect(() => {
const ws = new WebSocket('ws://localhost:8080');

ws.onopen = () => {
ws.send(JSON.stringify({userId,type:"countUnread"}));
};

ws.onmessage = (event) => {
const response = event.data;
setCntUnreadMessages(response);
};



setSocket(ws);

return () => {
ws.close();
};
}, [userId]);

return { cntUnreadMessages, socket };
};

export default useWebSocket;
70 changes: 43 additions & 27 deletions server-side/app.js
Original file line number Diff line number Diff line change
@@ -1,39 +1,55 @@
import express from 'express';
import morgan from 'morgan'
import cors from 'cors'
import dotenv from 'dotenv'
import morgan from 'morgan';
import cors from 'cors';
import dotenv from 'dotenv';
import preferencesRouter from './router/preference.router.js';
import websitesRouter from './router/websites.router.js';
import profilesRouter from './router/profile.router.js'
import visitedWebSitesRouter from './router/visitedWebsite.router.js'
import usersRouter from './router/user.router.js'
import {pageNotFound,serverErrors} from './middleware/handleErrors.js'
import {connectMongo} from './config/db.js'
import profilesRouter from './router/profile.router.js';
import visitedWebSitesRouter from './router/visitedWebsite.router.js';
import usersRouter from './router/user.router.js';
import { pageNotFound, serverErrors } from './middleware/handleErrors.js';
import { connectMongo } from './config/db.js';
import MessageRouter from './router/message.router.js';
import messageTypeRouter from './router/messageType.router.js';


const app=express();
app.use(express.json())
app.use(express.urlencoded({extended:true}));
app.use(morgan('dev'));//הדפסת המידע של כל הבקשה
const app = express();
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(morgan('dev'));
app.use(cors());

dotenv.config();
connectMongo();
app.get('/',(req,res)=>{

app.get('/', (req, res) => {
res.send('welcome to time out ');
})
app.use('/uploads',express.static('uploads'))
app.use('/preferences',preferencesRouter);
app.use('/websites',websitesRouter);
app.use('/profiles',profilesRouter);
app.use('/vistedWebsites',visitedWebSitesRouter);
app.use('/users',usersRouter);
});

app.use('/uploads', express.static('uploads'));
app.use('/preferences', preferencesRouter);
app.use('/websites', websitesRouter);
app.use('/profiles', profilesRouter);
app.use('/vistedWebsites', visitedWebSitesRouter);
app.use('/users', usersRouter);
app.use('/message', MessageRouter);
app.use('/messageType', messageTypeRouter);
app.use(pageNotFound);
app.use(serverErrors)
let port= process.env.PORT;
app.use(serverErrors);

const port = process.env.PORT || 3000;

app.listen(port,()=>{
console.log(` running at http://localhost:${port}`);
})
app.listen(port, () => {
console.log(`Running at http://localhost:${port}`);
}).on('error', (err) => {
if (err.code === 'EADDRINUSE') {
console.error(`Port ${port} is already in use, trying a different port...`);
app.listen(0, () => {
const newPort = app.address().port;
console.log(`Server is now running at http://localhost:${newPort}`);
});
} else {
console.error(`Failed to start server: ${err.message}`);
}
});

export default app;
export default app;
70 changes: 70 additions & 0 deletions server-side/controllers/message.controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@

import {
getAllMessages,
getMessageById,
getMessagesByUserId,
createMessage,
updateMessageById,
deleteMessageById
} from '../services/messages.service.js';

export const getMessages = async (req, res) => {
try {
const messages = await getAllMessages();
return res.status(200).json(messages);
} catch (error) {
return res.status(500).json({ error: error.message });
}
};

export const getMessageByIdController = async (req, res) => {
try {
const message = await getMessageById(req.params.id);
if (!message) {
return res.status(404).json({ error: 'Message not found' });
}
return res.status(200).json(message);
} catch (error) {
return res.status(500).json({ error: error.message });
}
};

export const getMessagesByUserIdController = async (req, res) => {
try {
const { userId } = req.params;
const messages = await getMessagesByUserId(userId);
if (!messages) {
return res.status(404).json({ error: 'Messages not found' });
}
return res.status(200).json(messages);
} catch (error) {
return res.status(500).json({ error: error.message });
}
};

export const addMessageController = async (req, res) => {
try {
const message = await createMessage(req.body);
return res.status(201).json(message);
} catch (error) {
return res.status(400).json({ error: error.message });
}
};

export const updateMessageController = async (req, res) => {
try {
const message = await updateMessageById(req.params.id, req.body);
return res.status(200).json(message);
} catch (error) {
return res.status(400).json({ error: error.message });
}
};

export const deleteMessageController = async (req, res) => {
try {
const result = await deleteMessageById(req.params.id);
return res.status(200).json(result);
} catch (error) {
return res.status(500).json({ error: error.message });
}
};
Loading