├── code
├── reactjs-client
│ ├── README.md
│ ├── src
│ │ ├── components
│ │ │ ├── FAB
│ │ │ │ ├── index.js
│ │ │ │ ├── FAB.js
│ │ │ │ └── styles.css
│ │ │ ├── Sidebar
│ │ │ │ ├── style.css
│ │ │ │ ├── SubMenu.js
│ │ │ │ ├── Sidebar.js
│ │ │ │ └── SidebarData.js
│ │ │ ├── Drive
│ │ │ │ ├── index.js
│ │ │ │ ├── Drive.js
│ │ │ │ ├── index.css
│ │ │ │ ├── Header.js
│ │ │ │ ├── css
│ │ │ │ │ ├── header.css
│ │ │ │ │ ├── data.css
│ │ │ │ │ └── sidebar.css
│ │ │ │ ├── Sidebar.js
│ │ │ │ └── Data.js
│ │ │ ├── Profile-card
│ │ │ │ ├── data.css
│ │ │ │ ├── ProfileCard.css
│ │ │ │ ├── Profile-card.js
│ │ │ │ └── Data.js
│ │ │ ├── TextEditor
│ │ │ │ ├── ContentModal.js
│ │ │ │ ├── style.css
│ │ │ │ └── TextEditor.js
│ │ │ ├── Popup
│ │ │ │ ├── Popup.css
│ │ │ │ └── Popup.js
│ │ │ ├── OTPModal
│ │ │ │ ├── OTPModal.js
│ │ │ │ └── MainComponent.js
│ │ │ ├── Dashboard.js
│ │ │ ├── Login.js
│ │ │ └── Signup.js
│ │ ├── index.js
│ │ ├── App.css
│ │ ├── pages
│ │ │ ├── Layout.js
│ │ │ ├── CarouselComponent.js
│ │ │ ├── Logster.js
│ │ │ ├── card.css
│ │ │ └── Home.js
│ │ └── App.js
│ ├── public
│ │ ├── robots.txt
│ │ ├── favicon.ico
│ │ ├── logo192.png
│ │ ├── logo512.png
│ │ ├── manifest.json
│ │ └── index.html
│ └── package.json
├── README.md
├── nodejs-server
│ ├── document.js
│ ├── package.json
│ └── server.js
└── flask-server
│ └── app.py
├── Screenshots
├── Project Screenshots.pdf
└── README.md
├── Badges
├── Magesh
│ ├── Getting_Started_with_Enterprise_Data_Science_Badge20230814-44-j23f2q.pdf
│ └── Journey_to_Cloud__Envisioning_Your_Solution_Badge20230814-28-vt5cgp.pdf
├── Manoj
│ ├── Getting_Started_with_Enterprise_Data_Science_Badge20230814-28-avi81j.pdf
│ └── Journey_to_Cloud__Envisioning_Your_Solution_Badge20230814-28-sl656f.pdf
├── Jayanthan
│ ├── Journey_to_Cloud__Envisioning_Your_Solution_Badge20230814-28-lz8xga.pdf
│ └── Getting_Started_with_Enterprise_Data_Science_Badge20230814-44-k9xl6p.pdf
├── Sanjay Kumar
│ ├── Journey_to_Cloud__Envisioning_Your_Solution_Badge20230814-28-63jsdh.pdf
│ └── Getting_Started_with_Enterprise_Data_Science_Badge20230814-44-66cd4g.pdf
└── cloudant-error.js
└── README.md
/code/reactjs-client/README.md:
--------------------------------------------------------------------------------
1 | # React frontend
2 | # Run with - npm start
--------------------------------------------------------------------------------
/code/reactjs-client/src/components/FAB/index.js:
--------------------------------------------------------------------------------
1 | import FAB from "./FAB";
2 |
3 | export default FAB;
4 |
--------------------------------------------------------------------------------
/code/reactjs-client/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/code/reactjs-client/src/components/Sidebar/style.css:
--------------------------------------------------------------------------------
1 | .xyz{
2 | display: flex;
3 | margin: 0;
4 | padding: 0;
5 | position: fixed;
6 | top: 2em;
7 | left: 2em;
8 | }
--------------------------------------------------------------------------------
/Screenshots/Project Screenshots.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smartinternz02/SBSPS-Challenge-10339-CloudCollabEdit-Empowering-Seamless-Document-Collaboration/HEAD/Screenshots/Project Screenshots.pdf
--------------------------------------------------------------------------------
/code/reactjs-client/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smartinternz02/SBSPS-Challenge-10339-CloudCollabEdit-Empowering-Seamless-Document-Collaboration/HEAD/code/reactjs-client/public/favicon.ico
--------------------------------------------------------------------------------
/code/reactjs-client/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smartinternz02/SBSPS-Challenge-10339-CloudCollabEdit-Empowering-Seamless-Document-Collaboration/HEAD/code/reactjs-client/public/logo192.png
--------------------------------------------------------------------------------
/code/reactjs-client/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smartinternz02/SBSPS-Challenge-10339-CloudCollabEdit-Empowering-Seamless-Document-Collaboration/HEAD/code/reactjs-client/public/logo512.png
--------------------------------------------------------------------------------
/code/README.md:
--------------------------------------------------------------------------------
1 | Code structure of our project
2 |
3 | 
4 |
--------------------------------------------------------------------------------
/Badges/Magesh/Getting_Started_with_Enterprise_Data_Science_Badge20230814-44-j23f2q.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smartinternz02/SBSPS-Challenge-10339-CloudCollabEdit-Empowering-Seamless-Document-Collaboration/HEAD/Badges/Magesh/Getting_Started_with_Enterprise_Data_Science_Badge20230814-44-j23f2q.pdf
--------------------------------------------------------------------------------
/Badges/Magesh/Journey_to_Cloud__Envisioning_Your_Solution_Badge20230814-28-vt5cgp.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smartinternz02/SBSPS-Challenge-10339-CloudCollabEdit-Empowering-Seamless-Document-Collaboration/HEAD/Badges/Magesh/Journey_to_Cloud__Envisioning_Your_Solution_Badge20230814-28-vt5cgp.pdf
--------------------------------------------------------------------------------
/Badges/Manoj/Getting_Started_with_Enterprise_Data_Science_Badge20230814-28-avi81j.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smartinternz02/SBSPS-Challenge-10339-CloudCollabEdit-Empowering-Seamless-Document-Collaboration/HEAD/Badges/Manoj/Getting_Started_with_Enterprise_Data_Science_Badge20230814-28-avi81j.pdf
--------------------------------------------------------------------------------
/Badges/Manoj/Journey_to_Cloud__Envisioning_Your_Solution_Badge20230814-28-sl656f.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smartinternz02/SBSPS-Challenge-10339-CloudCollabEdit-Empowering-Seamless-Document-Collaboration/HEAD/Badges/Manoj/Journey_to_Cloud__Envisioning_Your_Solution_Badge20230814-28-sl656f.pdf
--------------------------------------------------------------------------------
/Badges/Jayanthan/Journey_to_Cloud__Envisioning_Your_Solution_Badge20230814-28-lz8xga.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smartinternz02/SBSPS-Challenge-10339-CloudCollabEdit-Empowering-Seamless-Document-Collaboration/HEAD/Badges/Jayanthan/Journey_to_Cloud__Envisioning_Your_Solution_Badge20230814-28-lz8xga.pdf
--------------------------------------------------------------------------------
/Badges/Jayanthan/Getting_Started_with_Enterprise_Data_Science_Badge20230814-44-k9xl6p.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smartinternz02/SBSPS-Challenge-10339-CloudCollabEdit-Empowering-Seamless-Document-Collaboration/HEAD/Badges/Jayanthan/Getting_Started_with_Enterprise_Data_Science_Badge20230814-44-k9xl6p.pdf
--------------------------------------------------------------------------------
/Badges/Sanjay Kumar/Journey_to_Cloud__Envisioning_Your_Solution_Badge20230814-28-63jsdh.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smartinternz02/SBSPS-Challenge-10339-CloudCollabEdit-Empowering-Seamless-Document-Collaboration/HEAD/Badges/Sanjay Kumar/Journey_to_Cloud__Envisioning_Your_Solution_Badge20230814-28-63jsdh.pdf
--------------------------------------------------------------------------------
/Badges/Sanjay Kumar/Getting_Started_with_Enterprise_Data_Science_Badge20230814-44-66cd4g.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smartinternz02/SBSPS-Challenge-10339-CloudCollabEdit-Empowering-Seamless-Document-Collaboration/HEAD/Badges/Sanjay Kumar/Getting_Started_with_Enterprise_Data_Science_Badge20230814-44-66cd4g.pdf
--------------------------------------------------------------------------------
/code/nodejs-server/document.js:
--------------------------------------------------------------------------------
1 | const { Schema, model } = require("mongoose")
2 |
3 | const Document = new Schema({
4 | _id: String,
5 | name:String,
6 | data: Object,
7 | createdBy: String,
8 | createdAt: String,
9 | lastModified: String,
10 | })
11 |
12 | module.exports = model("Document", Document)
13 |
--------------------------------------------------------------------------------
/code/reactjs-client/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom/client';
3 | import './App.css';
4 | import App from './App';
5 |
6 | const root = ReactDOM.createRoot(document.getElementById('root'));
7 | root.render(
8 |
9 |
10 |
11 | );
12 |
--------------------------------------------------------------------------------
/code/reactjs-client/src/components/Drive/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom/client';
3 | import './index.css';
4 | import Drive from './Drive';
5 |
6 |
7 | const root = ReactDOM.createRoot(document.getElementById('root'));
8 | root.render(
9 |
10 |
11 |
12 | );
13 |
14 |
--------------------------------------------------------------------------------
/code/reactjs-client/src/components/Drive/Drive.js:
--------------------------------------------------------------------------------
1 | import Data from "./Data";
2 | import Header from "./Header";
3 | import Sidebar from "./Sidebar";
4 | import './index.css';
5 |
6 | function Drive() {
7 | return (
8 | <>
9 |
10 |
11 |
12 |
13 |
14 | >
15 |
16 | );
17 | }
18 |
19 | export default Drive;
20 |
--------------------------------------------------------------------------------
/code/reactjs-client/src/App.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
5 | sans-serif;
6 | -webkit-font-smoothing: antialiased;
7 | -moz-osx-font-smoothing: grayscale;
8 |
9 | }
10 |
11 | code {
12 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
13 | monospace;
14 | }
15 |
--------------------------------------------------------------------------------
/code/nodejs-server/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "server",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "keywords": [],
10 | "author": "",
11 | "license": "ISC",
12 | "dependencies": {
13 | "@ibm-cloud/cloudant": "^0.5.5",
14 | "ibm-cos-sdk": "^1.13.1",
15 | "mongoose": "^7.4.5",
16 | "socket.io": "^4.7.2"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/code/reactjs-client/src/pages/Layout.js:
--------------------------------------------------------------------------------
1 | import { Outlet, Link, useLocation } from "react-router-dom";
2 |
3 | const Layout = () => {
4 | // const location = useLocation();
5 | // console.log(location)
6 | // const hideNavbarAndFooter = ["/login", "/signup"].includes(location.pathname);
7 |
8 | return (
9 | <>
10 | {/* {!hideNavbarAndFooter && } */}
11 |
12 | {/* {!hideNavbarAndFooter && } */}
13 | >
14 | );
15 | };
16 |
17 | export default Layout;
--------------------------------------------------------------------------------
/code/reactjs-client/src/components/Drive/index.css:
--------------------------------------------------------------------------------
1 | * {
2 | margin: 0;
3 | }
4 | body {
5 | margin: 0;
6 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
7 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
8 | sans-serif;
9 | -webkit-font-smoothing: antialiased;
10 | -moz-osx-font-smoothing: grayscale;
11 | }
12 |
13 | code {
14 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
15 | monospace;
16 | }
17 | .App {
18 | display: grid;
19 | grid-template-columns: 300px auto 50px;
20 | }
--------------------------------------------------------------------------------
/code/reactjs-client/src/components/Profile-card/data.css:
--------------------------------------------------------------------------------
1 | .datap {
2 | margin-top: 7%;
3 | /* margin-left: 250px; */
4 | background-color:white;
5 | width: 150vh;
6 | height: auto;
7 | box-shadow: 0px 10px 30px hsl(189, 4%, 35%, 0.5);
8 | border-radius: 10px;
9 | }
10 | table {
11 | padding: 20px;
12 | text-align: center;
13 | }
14 | #h1 {
15 | width: 300px;
16 | }
17 | #h2 {
18 | width: 200px;
19 | }
20 | #h3{
21 | width: 300px;
22 | }
23 | #h4 {
24 | width: 100px;
25 | }
26 | #d1 {
27 | text-align: left;
28 | padding:15px;
29 | }
30 |
--------------------------------------------------------------------------------
/code/reactjs-client/src/components/TextEditor/ContentModal.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import Dialog from "@mui/material/Dialog";
3 | import DialogContent from "@mui/material/DialogContent";
4 |
5 | function ContentModal({ open, onClose, quillHtml, modalContent,either }) {
6 | return (
7 |
8 |
9 | {either=='1' ? (
10 | {quillHtml}
11 | ) : (
12 | {modalContent}
13 | )}
14 |
15 |
16 | );
17 | }
18 |
19 | export default ContentModal;
--------------------------------------------------------------------------------
/code/reactjs-client/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/code/reactjs-client/src/components/Drive/Header.js:
--------------------------------------------------------------------------------
1 | // Header.js
2 | import React from 'react';
3 | import './css/header.css';
4 | import SearchIcon from '@mui/icons-material/Search';
5 | import FormatAlignCenterIcon from '@mui/icons-material/FormatAlignCenter';
6 |
7 | const Header = () => {
8 | return (
9 |
10 |
11 |
12 |
Work Drive
13 |
14 |
15 |
16 |
17 |
18 |
19 | {/* Other header icons... */}
20 |
21 | )
22 | }
23 |
24 | export default Header;
25 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # CloudCollabEdit: Empowering Seamless Document Collaboration
2 |
3 | This Project is a Submission for IBM-SmartInternz Hack Challenge 2023
4 |
5 | We have successfully deployed our project using Docker Containerization on Openshift, and it is now accessible to everyone. You can access the project by visiting this [Link](https://react-frontend-tjmanojofficial-dev.apps.sandbox-m2.ll9k.p1.openshiftapps.com/). We have implemented security measures to protect sensitive data and ensure authorized access.
6 |
7 | TEAM- Thunder Breathing
8 |
9 | TEAM MEMBERS - Jayanthan, Magesh, Manoj, SanjayKumar
10 |
11 | [https://drive.google.com/drive/folders/1VSulveIKTxnPqQE_WQtdfMRzCLtiTGFZ?usp=sharing](https://drive.google.com/drive/folders/1ZkXGRxBtpKK7X_H6rLjWT5BeBglr3iv9?usp=sharing)
12 |
13 | Demonstration Video Link and the project report is in this folder.You can also see the Screenshots of the Application in the /images folder
14 |
15 | Thanku SmartInternz Team & IBM for this Hack Challenge. We have learned a lot from this event.🤩🎉
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/code/reactjs-client/src/components/FAB/FAB.js:
--------------------------------------------------------------------------------
1 | import { useState } from "react";
2 | import cn from "classnames";
3 | import { MdAdd } from "react-icons/md";
4 |
5 | import "./styles.css";
6 |
7 | const FAB = ({ actions }) => {
8 | const [open, setOpen] = useState(false);
9 |
10 | const mouseEnter = () => setOpen(true);
11 |
12 | const mouseLeave = () => setOpen(false);
13 |
14 | return (
15 |
20 |
21 |
22 |
23 | {actions.map((action, index) => (
24 |
30 | {action.icon}
31 | {action.label}
32 |
33 | ))}
34 |
35 | );
36 | };
37 |
38 | export default FAB;
39 |
--------------------------------------------------------------------------------
/code/reactjs-client/src/components/Popup/Popup.css:
--------------------------------------------------------------------------------
1 | .popup {
2 | position: fixed;
3 | top: 0;
4 | left: 0;
5 | width: 100%;
6 | height: 100vh;
7 | background: rgba(255, 255, 255, 0.11);
8 | backdrop-filter: blur(4.9px);
9 | -webkit-backdrop-filter: blur(4.9px);
10 | display: flex;
11 | justify-content: center;
12 | align-items: center;
13 | }
14 | .p{
15 | font-size: small;
16 | }
17 | .flex{
18 | display: flex;
19 | align-items: center;
20 | }
21 | .popup-inner {
22 | border: 1px solid rgba(241, 176, 176, 0.69);
23 | border-radius: 20px;
24 | position: relative;
25 | padding: 32px;
26 | width: 100%;
27 | max-width: 500px;
28 | background-color: #FFF;
29 | /* background: url("../src/assets/def.jpg") no-repeat right bottom; */
30 |
31 | box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1);
32 | }
33 |
34 | .popup-inner .close-btn {
35 | margin-top: 10px;
36 | margin-left: 10px;
37 | border: none;
38 | background-color: dimgray;
39 | color: white;
40 | border-radius: 2px;
41 | }
--------------------------------------------------------------------------------
/code/reactjs-client/src/components/Profile-card/ProfileCard.css:
--------------------------------------------------------------------------------
1 | .card1 {
2 | background-color: white;
3 | width: 620px;
4 | height: 180px;
5 | border-radius: 14px;
6 | box-shadow: 0px 10px 30px hsl(189, 4%, 35%, 0.5);
7 | position: relative;
8 | top: 50px;
9 | /* left: 200px; */
10 | }
11 | .card1 {
12 | display: flex;
13 | flex-direction: row;
14 | }
15 |
16 | .left {
17 | flex: 30%;
18 | }
19 |
20 | .right {
21 | flex: 70%;
22 | }
23 | header {
24 | /* background-image: url("./images/bg-pattern-card.jpg"); */
25 | background-position: 0px 0px;
26 | background-repeat: no-repeat;
27 | /* background-size: contain; */
28 | text-align: center;
29 | border-top-left-radius: 14px;
30 | height: 180px;
31 | border-bottom-left-radius: 14px;
32 | }
33 |
34 | .imagee img {
35 | width: 100px;
36 | height: auto;
37 | /* border: solid white 4px; */
38 | border-radius: 50%;
39 | margin-top: 38px;
40 | margin-right: -100%;
41 | }
42 |
43 | .bold-text {
44 | font-weight: bold;
45 | font-size: 1.1rem;
46 | text-align: center;
47 | padding: 10px 20px 0px 20px;
48 | }
49 | button {
50 | border: none;
51 | background-color: aquamarine;
52 | cursor: pointer;
53 | font-size: large;
54 | padding: 10px;
55 | border-radius: 25px;
56 | margin-top: 10px;
57 | }
58 |
--------------------------------------------------------------------------------
/code/reactjs-client/src/App.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from 'react';
2 | import { BrowserRouter, Route, Routes } from 'react-router-dom';
3 | import Layout from './pages/Layout';
4 | import Logster from './pages/Logster';
5 | import Dashboard from './components/Dashboard';
6 | import Home from './pages/Home';
7 | import TextEditor from './components/TextEditor/TextEditor';
8 | import MainComponent from './components/OTPModal/MainComponent';
9 | import Drive from './components/Drive/Drive'
10 |
11 |
12 |
13 | const App = () => {
14 | return (
15 |
16 |
17 |
18 | }>
19 | } />
20 | } />
21 | } />
22 | }>
23 | } />
24 |
25 | } />
26 | } />
27 | {/* } /> */}
28 |
29 |
30 |
31 |
32 | );
33 | };
34 |
35 | export default App;
36 |
--------------------------------------------------------------------------------
/code/reactjs-client/src/components/Drive/css/header.css:
--------------------------------------------------------------------------------
1 | .header {
2 | display: flex;
3 | align-items: center;
4 | padding: 1% 2%;
5 | background-color: white; /* Add a background color if needed */
6 | /* Other styles... */
7 | }
8 |
9 | .header__logo {
10 | display: flex;
11 | align-items: center;
12 | justify-content: space-between;
13 | }
14 |
15 | .header__logo img {
16 | width: 33px;
17 | vertical-align: middle; /* Align the image vertically in the middle */
18 | }
19 |
20 | .header__logo span {
21 | color: gray;
22 | font-size: 22px;
23 | margin-left: 8px;
24 | vertical-align: middle; /* Align the text vertically in the middle */
25 | }
26 |
27 | .header__search {
28 | display: flex;
29 | flex: 1 1;
30 | background-color: whitesmoke;
31 | padding: 11px;
32 | margin-left: 2%;
33 | border-radius: 10px;
34 | flex-direction: row;
35 | }
36 |
37 | .header__search input {
38 | background-color: transparent;
39 | border: 0;
40 | outline: 0;
41 | flex: 1;
42 | }
43 |
44 | .header__icons {
45 | display: flex;
46 | align-items: center;
47 | }
48 | .header__icons span{
49 | display: flex;
50 | align-items: center;
51 | margin-left: 10px;
52 | }
53 | .header__icons .material-icons {
54 | color: rgb(36,36,36);
55 | }
56 |
57 | .header__icons svg.MuiSvgIcon-root {
58 | margin: 0px 10px;
59 | }
--------------------------------------------------------------------------------
/code/reactjs-client/src/components/Profile-card/Profile-card.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import "./ProfileCard.css";
3 |
4 | function ProfileCard(props) {
5 | return (
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | Name : {props.name}
16 | E-Mail : {props.mail}
17 | {
20 | props.onCreateDocumentClick();
21 | }}>Create a Document
22 |
23 | {
26 | props.onEnterDocumentClick();
27 | }}>Enter Document Code
28 |
29 |
30 |
31 | );
32 | }
33 |
34 | export default ProfileCard;
35 |
--------------------------------------------------------------------------------
/code/reactjs-client/src/components/FAB/styles.css:
--------------------------------------------------------------------------------
1 | .fab-container {
2 | display: flex;
3 | list-style: none;
4 | margin: 0;
5 | padding: 0;
6 | flex-direction: column-reverse;
7 | position: fixed;
8 | right: 2em;
9 | bottom: 2em;
10 | max-height: 52px;
11 | }
12 |
13 | .fab-container.open {
14 | max-height: max-content;
15 | }
16 |
17 | .fab-container li {
18 | border-radius: 50%;
19 | box-shadow: 0 3px 6px lightgrey;
20 | display: grid;
21 | place-items: center;
22 | margin: 8px 0;
23 | font-size: 28px;
24 | padding: 12px;
25 | cursor: pointer;
26 | position: relative;
27 | }
28 |
29 | .fab-button {
30 | background-color: #00a8ff;
31 | }
32 |
33 | .fab-button svg {
34 | fill: white;
35 | }
36 |
37 | .fab-action {
38 | transform: translateY(50px) scale(0);
39 | transition: transform 300ms, opacity 300ms;
40 | opacity: 0;
41 | }
42 |
43 | .fab-action:hover .tooltip {
44 | transform: translateX(-100%) scale(1);
45 | opacity: 1;
46 | }
47 |
48 | .fab-action.open {
49 | transform: translateY(0) scale(1);
50 | opacity: 1;
51 | }
52 |
53 | .fab-action .tooltip {
54 | padding: 4px 6px;
55 | font-size: 12px;
56 | position: absolute;
57 | left: -12px;
58 | transform: translateX(-75%);
59 | background-color: #353b48;
60 | border-radius: 4px;
61 | color: white;
62 | opacity: 0;
63 | transition: transform 300ms, opacity 300ms;
64 | user-select: none;
65 | }
66 |
--------------------------------------------------------------------------------
/code/reactjs-client/src/components/Popup/Popup.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from 'react';
2 | import './Popup.css';
3 | import {v4 as uuidV4} from 'uuid'
4 | import { io } from "socket.io-client";
5 |
6 |
7 |
8 | function Popup({ setTrigger, trigger, nth, docname, onSave }) {
9 | const dynamicUuid = uuidV4();
10 | const docnamex = `${docname}-${nth}.0-`
11 |
12 | useEffect(() => {
13 | const storedContents = localStorage.getItem('editorContents');
14 | if (storedContents) {
15 | setTrigger(true); // Trigger the popup if editorContents is detected
16 | }
17 | }, [setTrigger]);
18 |
19 | function save() {
20 | setTrigger(false);
21 | // const url = `/document/${dynamicUuid}?name=${encodeURIComponent(docnamex)}`;
22 | // window.open(url, '_blank');
23 | onSave();
24 | console.log('from Popup side')
25 | }
26 |
27 | // function editItem({ id, newItem }) {
28 | // console.log(id + '_' + newItem);
29 | // }
30 |
31 | return trigger ? (
32 |
33 |
34 |
35 |
Confirm to create the ={'>'}
{docname}-{nth}.0- version of the file
36 |
37 |
Save
38 |
39 |
40 | ) : '';
41 | }
42 |
43 | export default Popup;
--------------------------------------------------------------------------------
/code/reactjs-client/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "client",
3 | "version": "0.1.0",
4 | "private": true,
5 | "proxy": "http://localhost:3000",
6 | "dependencies": {
7 | "@emotion/react": "^11.11.1",
8 | "@emotion/styled": "^11.11.0",
9 | "@mui/icons-material": "^5.14.3",
10 | "@mui/material": "^5.14.5",
11 | "@testing-library/jest-dom": "^5.17.0",
12 | "@testing-library/react": "^13.4.0",
13 | "@testing-library/user-event": "^13.5.0",
14 | "classnames": "^2.3.2",
15 | "jwt-decode": "^3.1.2",
16 | "quill": "^1.3.7",
17 | "quill-delta-to-html": "^0.12.1",
18 | "quill-languagetool": "^1.0.7",
19 | "react": "^18.2.0",
20 | "react-dom": "^18.2.0",
21 | "react-icons": "^4.10.1",
22 | "react-quill": "^2.0.0",
23 | "react-router-dom": "^6.15.0",
24 | "react-scripts": "5.0.1",
25 | "socket.io-client": "^4.7.2",
26 | "styled-components": "^6.0.7",
27 | "web-vitals": "^2.1.4"
28 | },
29 | "scripts": {
30 | "start": "react-scripts start",
31 | "build": "react-scripts build",
32 | "test": "react-scripts test",
33 | "eject": "react-scripts eject"
34 | },
35 | "eslintConfig": {
36 | "extends": [
37 | "react-app",
38 | "react-app/jest"
39 | ]
40 | },
41 | "browserslist": {
42 | "production": [
43 | ">0.2%",
44 | "not dead",
45 | "not op_mini all"
46 | ],
47 | "development": [
48 | "last 1 chrome version",
49 | "last 1 firefox version",
50 | "last 1 safari version"
51 | ]
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/code/reactjs-client/src/pages/CarouselComponent.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import "./card.css"
3 | import { TiChevronLeftOutline, TiChevronRightOutline } from 'https://cdn.skypack.dev/react-icons/ti';
4 |
5 | const MAX_VISIBILITY = 3;
6 |
7 | const Carousel = ({ children }) => {
8 | const [active, setActive] = useState(0);
9 | const count = React.Children.count(children);
10 |
11 | return (
12 |
13 | {active > 0 && (
14 |
setActive(i => i - 1)}>
15 |
16 |
17 | )}
18 |
19 | {React.Children.map(children, (child, i) => (
20 |
= MAX_VISIBILITY ? '0' : '1',
29 | display: Math.abs(active - i) > MAX_VISIBILITY ? 'none' : 'block',
30 | }}
31 | >
32 | {child}
33 |
34 | ))}
35 |
36 | {active < count - 1 && (
37 |
setActive(i => i + 1)}>
38 |
39 |
40 | )}
41 |
42 | );
43 | };
44 |
45 | export default Carousel;
46 |
--------------------------------------------------------------------------------
/code/reactjs-client/src/components/Drive/css/data.css:
--------------------------------------------------------------------------------
1 | .data{
2 | flex: 1;
3 | padding: 10px;
4 | margin: 3%;
5 | border-radius: 10px;
6 | background-color:white;
7 | width: 150vh;
8 | height: auto;
9 | }
10 | .data__header{
11 | display: flex;
12 | align-items: center;
13 | justify-content: space-between;
14 | border-bottom: 1px solid lightgray;
15 | height: 40px;
16 | }
17 | .data__headerleft {
18 | display: flex;
19 | align-items: center;
20 | }
21 | .data__headerright svg{
22 | margin: 0px 10px;
23 | }
24 | .data__grid {
25 | display: flex;
26 | align-items: center;
27 | /* margin-top: 30px; */
28 | /* margin-bottom: 30px; */
29 | }
30 | .data__file {
31 | text-align: center;
32 | border: 1px solid lightgray;
33 | margin: 10px;
34 | min-width: 200px;
35 | padding: 10px 0px 0px 0px;
36 | border-radius: 5px;
37 | }
38 | .data__file svg {
39 | font-size: 60px;
40 | color: gray;
41 | }
42 | .data__file p{
43 | border-top: 1px solid #ccc;
44 | margin-top: 5px;
45 | font-size: 12px;
46 | background-color: whitesmoke;
47 | padding: 10px 0px;
48 | }
49 | .detailsRow {
50 | display: flex;
51 | align-items: center;
52 | justify-content: space-between;
53 | border-bottom: 1px solid #ccc;
54 | padding: 10px;
55 | }
56 | .detailsRow p {
57 | display: flex;
58 | align-items: center;
59 | font-size: 13px;
60 | }
61 | .detailsRow p b {
62 | display: flex;
63 | align-items: center;
64 | }
65 | .detailsRow p svg {
66 | font-size: 22px;
67 | margin: 10px;
68 | }
--------------------------------------------------------------------------------
/code/reactjs-client/src/components/TextEditor/style.css:
--------------------------------------------------------------------------------
1 | *,*::before , *::after{
2 | box-sizing: border-box;
3 | }
4 |
5 | body{
6 | background-color: #F3F3F3;
7 | margin: 0;
8 | }
9 |
10 | .container .ql-editor{
11 | width: 8.5in;
12 | min-height: 11in;
13 | padding: 1in;
14 | margin: 1rem;
15 | box-shadow: 0 0 5px 0 rgba(0,0,0,0.5);
16 | background-color: white;
17 | }
18 |
19 | .container .ql-container.ql-snow{
20 | border: none;
21 | display: flex;
22 | justify-content: center;
23 | }
24 |
25 | .container .ql-toolbar.ql-snow{
26 | display: flex;
27 | justify-content: center;
28 | position: sticky;
29 | top: 0;
30 | z-index: 1;
31 | background-color: #F3F3F3;
32 | border: none;
33 | box-shadow: 0 0 5px 0 rgba(0,0,0,0.5);
34 | }
35 | .quill-lt-match-popup .quill-lt-powered-by{
36 | display: none;
37 | }
38 | @page {
39 | margin: 1in;
40 | }
41 |
42 | @media print {
43 | body {
44 | background: none;
45 | }
46 |
47 | .love div{
48 | display: none;
49 | }
50 |
51 | .container .ql-editor {
52 | width: 6.5in;
53 | height: 9in;
54 | padding: 0;
55 | margin: 0;
56 | box-shadow: none;
57 | align-self: flex-start;
58 | overflow: hidden;
59 | }
60 |
61 | .container .ql-toolbar.ql-snow {
62 | display: none;
63 | }
64 |
65 | .fab-container{
66 | display: none;
67 | }
68 | quill-lt-match {
69 | border: none !important;
70 | }
71 | .quill-lt-match-popup{
72 | display: none;
73 | }
74 |
75 | }
76 |
77 |
78 |
--------------------------------------------------------------------------------
/code/reactjs-client/src/pages/Logster.js:
--------------------------------------------------------------------------------
1 | import "../App.css";
2 |
3 | // Material UI imports
4 | import Chip from "@mui/material/Chip";
5 | import FaceIcon from "@mui/icons-material/Face";
6 | import Paper from "@mui/material/Paper";
7 | import LockIcon from "@mui/icons-material/Lock";
8 |
9 | import Switch from "@mui/material/Switch";
10 | import { useEffect, useState } from "react";
11 | import Login from "../components/Login";
12 | import Signup from "../components/Signup";
13 |
14 | function App() {
15 | const [checked, setChecked] = useState(true);
16 |
17 | useEffect(()=>{
18 |
19 | const checking=localStorage.getItem("hello")
20 | if(checking){
21 | setChecked(true)
22 | }
23 |
24 | },[])
25 |
26 | const handleChange = (event) => {
27 | setChecked(event.target.checked);
28 | };
29 |
30 | return (
31 |
32 |
33 |
34 | {checked ? (
35 | }
37 | label="Log In"
38 | variant="outlined"
39 | color="info"
40 | />
41 | ) : (
42 | }
44 | label="Sign Up"
45 | variant="outlined"
46 | color="info"
47 | />
48 | )}
49 |
50 |
51 |
56 |
57 |
58 | {checked ? : }
59 |
60 |
61 | );
62 | }
63 |
64 | export default App;
65 |
--------------------------------------------------------------------------------
/code/reactjs-client/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
18 |
27 | React App
28 |
29 |
30 | You need to enable JavaScript to run this app.
31 |
32 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/code/reactjs-client/src/components/Sidebar/SubMenu.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import { Link } from 'react-router-dom';
3 | import styled from 'styled-components';
4 |
5 | const SidebarLink = styled(Link)`
6 | display: flex;
7 | color: #e1e9fc;
8 | justify-content: space-between;
9 | align-items: center;
10 | padding: 20px;
11 | list-style: none;
12 | height: 60px;
13 | text-decoration: none;
14 | font-size: 18px;
15 |
16 | &:hover {
17 | background: #252831;
18 | border-left: 4px solid #632ce4;
19 | cursor: pointer;
20 | }
21 | `;
22 |
23 | const SidebarLabel = styled.span`
24 | margin-left: 16px;
25 | `;
26 |
27 | const DropdownLink = styled(Link)`
28 | background: #414757;
29 | height: 60px;
30 | padding-left: 3rem;
31 | display: flex;
32 | align-items: center;
33 | text-decoration: none;
34 | color: #f5f5f5;
35 | font-size: 18px;
36 |
37 | &:hover {
38 | background: #632ce4;
39 | cursor: pointer;
40 | }
41 | `;
42 |
43 | const SubMenu = ({ item }) => {
44 | const [subnav, setSubnav] = useState(false);
45 |
46 | const showSubnav = () => setSubnav(!subnav);
47 |
48 | return (
49 | <>
50 |
51 |
52 | {item.icon}
53 | {item.title}
54 |
55 |
56 | {item.subNav && subnav
57 | ? item.iconOpened
58 | : item.subNav
59 | ? item.iconClosed
60 | : null}
61 |
62 |
63 | {subnav &&
64 | item.subNav.map((item, index) => {
65 | return (
66 |
67 | {item.icon}
68 | {item.title}
69 |
70 | );
71 | })}
72 | >
73 | );
74 | };
75 |
76 | export default SubMenu;
77 |
--------------------------------------------------------------------------------
/code/reactjs-client/src/components/OTPModal/OTPModal.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 |
3 | class OTPModal extends Component {
4 | constructor(props) {
5 | super(props);
6 | this.state = {
7 | otp: '',
8 | };
9 | }
10 |
11 | handleInputChange = (e) => {
12 | this.setState({ otp: e.target.value });
13 | };
14 |
15 | handleVerifyOTP = () => {
16 | // Send the OTP to the server for verification
17 | // You can use fetch or Axios to make a POST request to your Flask route
18 | fetch('/generate_otp', {
19 | method: 'POST',
20 | headers: {
21 | 'Content-Type': 'application/json',
22 | },
23 | body: JSON.stringify({ otp: this.state.otp }),
24 | })
25 | .then((response) => response.json())
26 | .then((data) => {
27 | if (data.status === 'success') {
28 | // OTP verification was successful
29 | // Close the modal and handle success action
30 | this.props.onClose();
31 | // Handle success action (e.g., redirect or display a success message)
32 | } else {
33 | // OTP verification failed
34 | // Handle the error (e.g., display an error message)
35 | }
36 | })
37 | .catch((error) => {
38 | console.error('Error:', error);
39 | // Handle network or other errors
40 | });
41 | };
42 |
43 | render() {
44 | return (
45 |
46 |
47 |
48 | ×
49 |
50 |
Enter OTP
51 |
57 | Verify OTP
58 |
59 |
60 | );
61 | }
62 | }
63 |
64 | export default OTPModal;
65 |
--------------------------------------------------------------------------------
/code/reactjs-client/src/components/OTPModal/MainComponent.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import OTPModal from './OTPModal';
3 |
4 | class MainComponent extends Component {
5 | constructor(props) {
6 | super(props);
7 | this.state = {
8 | showModal: false,
9 | userEmail: '',
10 | };
11 | }
12 |
13 | handleShowModal = () => {
14 | this.setState({ showModal: true });
15 |
16 | const { userEmail } = this.state;
17 |
18 | // Send a POST request to the Flask backend to generate OTP
19 | fetch('/generate_otp', {
20 | method: 'POST',
21 | headers: {
22 | 'Content-Type': 'application/json',
23 | },
24 | body: JSON.stringify({ email: 'jeyanthvijay2000@gmail.com' }), // Pass email as JSON
25 | })
26 | .then((response) => response.json())
27 | .then((data) => {
28 | if (data.status === 'success') {
29 | // OTP sent successfully, open the modal
30 | this.setState({ showModal: true });
31 | } else {
32 | // Handle error (e.g., display an error message)
33 | console.error(data.message);
34 | }
35 | })
36 | .catch((error) => {
37 | // Handle network or other errors
38 | console.error('Error:', error);
39 | });
40 | };
41 |
42 | handleCloseModal = () => {
43 | this.setState({ showModal: false });
44 | };
45 |
46 | handleEmailChange = (e) => {
47 | this.setState({ userEmail: e.target.value });
48 | };
49 |
50 | render() {
51 | return (
52 |
53 | {/* Your main content */}
54 |
60 | Request OTP
61 |
62 | {this.state.showModal && (
63 |
64 | )}
65 |
66 | );
67 | }
68 | }
69 |
70 | export default MainComponent;
71 |
--------------------------------------------------------------------------------
/code/reactjs-client/src/components/Drive/css/sidebar.css:
--------------------------------------------------------------------------------
1 | .sidebar {
2 | margin-top: 10px;
3 | }
4 | .abc{
5 | margin: 8%;
6 | }
7 | .p2{
8 | margin-top: 4%;
9 | }
10 | .sidebar_btn button {
11 | background-color: transparent;
12 | border: 1px solid lightgray;
13 | display: flex;
14 | align-items: center;
15 | border-radius: 40px;
16 | padding: 5px 10px;
17 | margin-left: 20px;
18 | box-shadow: 2px 2px 2px #ccc;
19 | }
20 | .sidebar_btn span {
21 | font-size: 16px;
22 | margin-right: 20px;
23 | margin-left: 10px;
24 | }
25 | .sidebar_options{
26 | margin-top: 10px;
27 | }
28 | .sidebar_option {
29 | display: flex;
30 | align-items: center;
31 | padding: 8px 20px;
32 | border-radius: 0px 20px 20px 0px;
33 | }
34 | .sidebar_option:hover{
35 | background-color: whitesmoke;
36 | cursor: pointer;
37 | }
38 | .sidebar_option span {
39 | margin-left: 15px;
40 | font-size: 13px;
41 | font-weight: 500;
42 | color: rgb(78, 78, 78);
43 | }
44 | .sidebar_option-Active {
45 | background-color: #e8f0fe;
46 | }
47 | .sidebar_option-Active span{
48 | color: #1367d2 !important;
49 | }
50 | .sidebar_option-Active svg{
51 | color: #1367d2 !important;
52 | }
53 | .progress_bar {
54 | padding: 0px 20px;
55 | }
56 | .progress_bar span{
57 | display: flex;
58 | font-size: 13px;
59 | color: #333;
60 | }
61 | .modal_pop {
62 | top: 50%;
63 | background-color: #fff;
64 | width: 500px;
65 | margin: 0px auto;
66 | position: relative;
67 | transform: translateY(-50%);
68 | padding: 10px;
69 | border-radius: 10px;
70 | }
71 | .modal_heading {
72 | text-align: center;
73 | border-bottom: 1px solid lightgray;
74 | height: 40px;
75 | padding: 10px;
76 | }
77 | input.post_submit {
78 | width: 100%;
79 | background: #2381fa;
80 | padding: 10px 20px;
81 | color: #fff;
82 | text-transform: uppercase;
83 | letter-spacing: 5px;
84 | font-size: 16px;
85 | border: 0;
86 | outline: 0;
87 | border-radius: 5px;
88 | cursor: pointer;
89 | margin-top: 20px;
90 | }
--------------------------------------------------------------------------------
/code/reactjs-client/src/components/Sidebar/Sidebar.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import styled from 'styled-components';
3 | import { Link } from 'react-router-dom';
4 | import * as FaIcons from 'react-icons/fa';
5 | import * as AiIcons from 'react-icons/ai';
6 | import { SidebarData } from './SidebarData';
7 | import SubMenu from './SubMenu';
8 | import { IconContext } from 'react-icons/lib';
9 | import './style.css'
10 |
11 | const Nav = styled.div`
12 | border-radius:20%;
13 | // background: #15171c;
14 | height: 80px;
15 | display: flex;
16 | justify-content: flex-start;
17 | align-items: center;
18 | `;
19 |
20 | const NavIcon = styled(Link)`
21 | margin:1rem;
22 | font-size: 2rem;
23 | height: 80px;
24 | display: flex;
25 | justify-content: flex-start;
26 | align-items: center;
27 | `;
28 |
29 | const SidebarNav = styled.nav`
30 | background: #15171c;
31 | width: 250px;
32 | height: 100vh;
33 | display: flex;
34 | justify-content: center;
35 | position: fixed;
36 | top: 0;
37 | left: ${({ sidebar }) => (sidebar ? '0' : '-100%')};
38 | transition: 350ms;
39 | z-index: 10;
40 | `;
41 |
42 | const SidebarWrap = styled.div`
43 | width: 100%;
44 | `;
45 |
46 | const Sidebar = () => {
47 | const [sidebar, setSidebar] = useState(false);
48 |
49 | const showSidebar = () => {
50 | console.log("AMKSAM")
51 | setSidebar(!sidebar)};
52 |
53 | return (
54 | <>
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 | {SidebarData.map((item, index) => {
69 | return ;
70 | })}
71 |
72 |
73 |
74 | >
75 | );
76 | };
77 |
78 | export default Sidebar;
79 |
--------------------------------------------------------------------------------
/code/reactjs-client/src/pages/card.css:
--------------------------------------------------------------------------------
1 | .dcf {
2 | width: 100vw;
3 | height: 100vh;
4 | display: flex;
5 | align-items: center;
6 | justify-content: center;
7 | overflow: hidden;
8 | background-image: linear-gradient(45deg, #8b5cf6, #ec4899);
9 | font-family: 'Montserrat', sans-serif;
10 | }
11 |
12 | /* * {
13 | box-sizing: border-box;
14 | } */
15 |
16 | .carousel {
17 | position: relative;
18 | width: 23rem;
19 | height: 23rem;
20 | perspective: 500px;
21 | transform-style: preserve-3d;
22 | }
23 |
24 | .card-container {
25 | position: absolute;
26 | width: 100%;
27 | height: 100%;
28 | transform:
29 | rotateY(calc(var(--offset) * 50deg))
30 | scaleY(calc(1 + var(--abs-offset) * -0.4))
31 | translateZ(calc(var(--abs-offset) * -30rem))
32 | translateX(calc(var(--direction) * -5rem));
33 | filter: blur(calc(var(--abs-offset) * 1rem));
34 | transition: all 0.3s ease-out;
35 | }
36 |
37 | .card {
38 | width: 100%;
39 | height: 100%;
40 | padding: 2rem;
41 | background-color: hsl(280deg, 40%, calc(100% - var(--abs-offset) * 50%));
42 | border-radius: 1rem;
43 | color: #9ca3af;
44 | text-align: justify;
45 | transition: all 0.3s ease-out;
46 |
47 |
48 | h2 {
49 | text-align: center;
50 | font-size: 2rem;
51 | font-weight: bold;
52 | margin: 0 0 0.7em;
53 | color: #1f2937;
54 | }
55 |
56 | p, h2 {
57 | transition: all 0.3s ease-out;
58 | opacity: var(--active);
59 | }
60 | }
61 |
62 | .nav {
63 | color: white;
64 | font-size: 5rem;
65 | position: absolute;
66 | display: flex;
67 | align-items: center;
68 | justify-content: center;
69 | top: 50%;
70 | z-index: 2;
71 | cursor: pointer;
72 | user-select: none;
73 | background: unset;
74 | border: unset;
75 |
76 | &.left {
77 | transform: translateX(-100%) translatey(-50%);
78 | }
79 |
80 | &.right {
81 | right: 0;
82 | transform: translateX(100%) translatey(-50%);
83 | }
84 | }
85 | .image {
86 | max-width: 100%;
87 | width: auto;
88 | height: auto;
89 | border-radius: 0%;
90 | margin-top: 0px;
91 | }
92 |
--------------------------------------------------------------------------------
/code/reactjs-client/src/components/Sidebar/SidebarData.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import * as FaIcons from 'react-icons/fa';
3 | import * as AiIcons from 'react-icons/ai';
4 | import * as IoIcons from 'react-icons/io';
5 | import * as RiIcons from 'react-icons/ri';
6 |
7 | export const SidebarData = [
8 | {
9 | title: 'Overview',
10 | path: '/overview',
11 | icon: ,
12 | iconClosed: ,
13 | iconOpened: ,
14 |
15 | subNav: [
16 | {
17 | title: 'Users',
18 | path: '/overview/users',
19 | icon:
20 | },
21 | {
22 | title: 'Revenue',
23 | path: '/overview/revenue',
24 | icon:
25 | }
26 | ]
27 | },
28 | {
29 | title: 'Reports',
30 | path: '/reports',
31 | icon: ,
32 | iconClosed: ,
33 | iconOpened: ,
34 |
35 | subNav: [
36 | {
37 | title: 'Reports',
38 | path: '/reports/reports1',
39 | icon: ,
40 | cName: 'sub-nav'
41 | },
42 | {
43 | title: 'Reports 2',
44 | path: '/reports/reports2',
45 | icon: ,
46 | cName: 'sub-nav'
47 | },
48 | {
49 | title: 'Reports 3',
50 | path: '/reports/reports3',
51 | icon:
52 | }
53 | ]
54 | },
55 | {
56 | title: 'Products',
57 | path: '/products',
58 | icon:
59 | },
60 | {
61 | title: 'Team',
62 | path: '/team',
63 | icon:
64 | },
65 | {
66 | title: 'Messages',
67 | path: '/messages',
68 | icon: ,
69 |
70 | iconClosed: ,
71 | iconOpened: ,
72 |
73 | subNav: [
74 | {
75 | title: 'Message 1',
76 | path: '/messages/message1',
77 | icon:
78 | },
79 | {
80 | title: 'Message 2',
81 | path: '/messages/message2',
82 | icon:
83 | }
84 | ]
85 | },
86 | {
87 | title: 'Support',
88 | path: '/support',
89 | icon:
90 | }
91 | ];
92 |
--------------------------------------------------------------------------------
/code/reactjs-client/src/pages/Home.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Button from '@mui/material/Button';
3 | import { useNavigate } from 'react-router-dom';
4 | import Carousel from './CarouselComponent';
5 | import "./card.css"
6 | // import Sidebar from '../components/Sidebar/Sidebar';
7 |
8 | const containerStyle = {
9 | display: 'flex',
10 | flexDirection: 'column',
11 | alignItems: 'center',
12 | justifyContent: 'center',
13 | minHeight: '100vh',
14 | padding: '16px', // Add padding for better spacing on small screens
15 | boxSizing: 'border-box', // Ensure padding is included in element's size
16 | };
17 |
18 | const loginButtonStyle = {
19 | marginTop: '16px', // You can adjust the spacing as needed
20 | };
21 | const Card = ({ title, content, imageUrl }) => (
22 |
23 | {imageUrl &&
}
24 |
{title}
25 |
{content}
26 |
27 | );
28 | // const CARDS = 2
29 | const cardData = [
30 | {
31 | imageUrl :'https://convergence.io/assets/img/convergence-overview.jpg',
32 | title: 'Custom Card Title 1',
33 | content: 'Custom content for Card 1 goes here.',
34 | },
35 | {
36 | imageUrl: '',
37 | title: 'Custom Card Title 2',
38 | content: 'Custom content for Card 2 goes here.',
39 | },
40 | {
41 | title: 'Custom Card Title 2',
42 | content: 'Custom content for Card 2 goes here.',
43 | },
44 | {
45 | title: 'Custom Card Title 2',
46 | content: 'Custom content for Card 2 goes here.',
47 | },
48 | {
49 | title: 'Custom Card Title 2',
50 | content: 'Custom content for Card 2 goes here.',
51 | },
52 | // Add more card data as needed
53 | ];
54 | const Home = () => {
55 |
56 | const navigate = useNavigate();
57 | const loginClick=()=>{
58 | navigate('/logster');
59 | }
60 | return (
61 |
62 | {/*
s */}
63 |
Welcome to the Home Page
64 |
70 | Login
71 |
72 |
73 |
74 |
75 | {cardData.map((card, index) => (
76 |
82 | ))}
83 |
84 |
85 |
86 | );
87 | }
88 |
89 | export default Home;
90 |
--------------------------------------------------------------------------------
/code/reactjs-client/src/components/Profile-card/Data.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { styled } from '@mui/material/styles';
3 | import InsertDriveFileIcon from '@mui/icons-material/InsertDriveFile';
4 | import ArrowDownwardIcon from '@mui/icons-material/ArrowDownward';
5 | import Tooltip, { tooltipClasses } from '@mui/material/Tooltip';
6 | import { Link } from 'react-router-dom'; // Import Link from react-router-dom
7 | import "./data.css"
8 |
9 | const Data = ({ files }) => {
10 | const truncateFileName = (name) => {
11 | if (name.length > 10) {
12 | return name.slice(0, 20) + '...';
13 | }
14 | return name;
15 | };
16 |
17 | const LightTooltip = styled(({ className, ...props }) => (
18 |
19 | ))(({ theme }) => ({
20 | [`& .${tooltipClasses.tooltip}`]: {
21 | backgroundColor: theme.palette.common.white,
22 | color: 'rgba(0, 0, 0, 1)',
23 | boxShadow: theme.shadows[1],
24 | fontSize: 15,
25 | },
26 | }));
27 |
28 | return (
29 | <>
30 |
31 |
32 |
33 |
34 | Name
35 | Document Code
36 | Created At
37 | Last Modified
38 |
39 |
40 |
41 | {files.map((file) => (
42 |
43 |
44 |
45 |
46 |
49 | {truncateFileName(file.name)}
50 |
51 |
52 |
53 |
54 |
55 |
58 | {truncateFileName(`${file._id}?name=${encodeURIComponent(file.name)}`)}
59 |
60 |
61 |
62 | {file.createdAt}
63 | {file.lastModified}
64 |
65 | ))}
66 |
67 |
68 |
69 | >
70 | )
71 | }
72 |
73 | export default Data
74 |
--------------------------------------------------------------------------------
/code/reactjs-client/src/components/Drive/Sidebar.js:
--------------------------------------------------------------------------------
1 |
2 | import React, { useState, useEffect } from 'react';
3 | import "./css/sidebar.css";
4 | import CloudQueueIcon from '@mui/icons-material/CloudQueue';
5 | import AddIcon from '@mui/icons-material/Add';
6 | import Modal from '@mui/material/Modal';
7 | import { io } from "socket.io-client";
8 |
9 |
10 |
11 | function Sidebar() {
12 | const [open, setOpen] = useState(false);
13 | const [uploading] = useState(false);
14 | const [file, setFile] = useState(null);
15 | const [socket, setSocket] = useState(null);
16 | const jwtToken1=localStorage.getItem("jwtToken1")
17 | function removeDomain(email) {
18 | const atIndex = email.indexOf('@');
19 | if (atIndex !== -1) {
20 | return email.substring(0, atIndex);
21 | }
22 | return email;
23 | }
24 | const user_mail=removeDomain(jwtToken1)
25 | useEffect(() => {
26 | const s = io("http://localhost:3001");
27 | setSocket(s);
28 | return () => {
29 | s.disconnect();
30 | };
31 | }, []);
32 | const handleOpen = () => {
33 | setOpen(true);
34 | };
35 | const handleClose = () => {
36 | setOpen(false);
37 | };
38 | const handleChange = (e) => {
39 | if (e.target.files[0]) {
40 | setFile(e.target.files[0]);
41 | }
42 | };
43 |
44 |
45 |
46 | function handleUpload(event, fileName, fileData,bname) {
47 | console.log(fileName,fileData,bname)
48 | event.preventDefault();
49 | socket.emit("createfile",fileName,fileData,bname)
50 | setOpen(false)
51 | }
52 | return (
53 |
54 | <>
55 |
56 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 | New
87 |
88 |
89 |
90 |
91 |
92 |
Storage bucket created in IBM COS
93 |
Bucket name : {user_mail}
94 |
95 |
96 |
97 |
98 | Storage
99 |
100 |
101 |
102 |
6.45 GB of 15 GB used
103 |
104 |
105 |
106 | >
107 | )
108 | }
109 |
110 | export default Sidebar
--------------------------------------------------------------------------------
/Badges/cloudant-error.js:
--------------------------------------------------------------------------------
1 | const io=require('socket.io')(3001,{
2 | cors:{origin:'http://localhost:3000',
3 | methods:['GET','POST']}
4 | })
5 | const { CloudantV1 } = require('@ibm-cloud/cloudant');
6 | const client = CloudantV1.newInstance({});
7 | const dbName = 'qawsed';
8 | // console.log(client)
9 |
10 |
11 | const defaultValue=""
12 | io.on('connection',socket=>{
13 | console.log("Connected")
14 |
15 | // socket.on('get-document',socket=>{
16 | socket.on('get-document',async documentId=>{
17 | const document = await findOrCreateDocument(documentId)
18 | socket.join(documentId)
19 | socket.emit('load-document',document.data)
20 |
21 | socket.on("send-changes",delta=>{
22 | socket.broadcast.to(documentId).emit("receive-changes",delta)
23 | })
24 | socket.on('save-document',async data=>{
25 | await findOrUpdateDocument(documentId,data)
26 | })
27 | })
28 | // })
29 | socket.on('disconnect', () => {
30 | console.log("A user disconnected"); // Log when a user disconnects
31 | });
32 | })
33 |
34 | async function findOrCreateDocument(id){
35 | if(id==null) return
36 |
37 | try{
38 | const getDocumentResponse = await client.getDocument({
39 | db: dbName,
40 | docId: id,
41 | });
42 | const existingDocument = getDocumentResponse.result;
43 | console.log('Document already exists:', existingDocument.data);
44 | return existingDocument.data
45 | }
46 | catch(err){
47 | if(err.code===404){
48 | const newDocument = {
49 | _id: id,
50 | data:defaultValue
51 | };
52 |
53 | try {
54 | const createDocumentResponse = await client.postDocument({
55 | db: dbName,
56 | document: newDocument,
57 | });
58 |
59 | if (createDocumentResponse.result.ok) {
60 | console.log('Document added successfully:', newDocument);
61 | return newDocument
62 | }
63 | } catch (createErr) {
64 | console.error('Error adding document:', createErr.message);
65 | }
66 |
67 | }
68 | else{
69 | console.error('Error fetching document:', err.message);
70 | }
71 | }
72 |
73 | }
74 |
75 |
76 | async function findOrUpdateDocument(id,data){
77 | if (id==null) return
78 |
79 | try{
80 | const getDocumentResponse = await client.getDocument({
81 | db: dbName,
82 | docId: id,
83 | });
84 | const existingDocument = getDocumentResponse.result;
85 | console.log('Document already exists:', existingDocument.data,"_",data);
86 | // return existingDocument.data
87 | existingDocument.data = data;
88 | try {
89 | const updateDocumentResponse = await client.putDocument({
90 | db: dbName,
91 | docId: id,
92 | document: existingDocument,
93 | });
94 |
95 | if (updateDocumentResponse.result.ok) {
96 | console.log('Document updated successfully:', existingDocument);
97 | return existingDocument
98 | }
99 | } catch (updateErr) {
100 | console.error('Error updating document:', updateErr.message);
101 | }
102 | }
103 | catch(err){
104 | console.error('Error fetching document:', err.message)
105 | }
106 |
107 | }
108 | // const data={
109 | // property1: '....',
110 | // }
111 | // findOrCreateDocument("SudalaMaada_Kaapathu");
112 | // findOrUpdateDocument("Just_testing",data)
113 |
114 |
115 |
116 |
--------------------------------------------------------------------------------
/code/reactjs-client/src/components/Drive/Data.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from 'react';
2 | import "./css/data.css";
3 | //import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown';
4 | import ListIcon from '@mui/icons-material/List';
5 | import InsertDriveFileIcon from '@mui/icons-material/InsertDriveFile';
6 | import ArrowDownwardIcon from '@mui/icons-material/ArrowDownward';
7 | import InfoOutlinedIcon from '@mui/icons-material/InfoOutlined';
8 | import { io } from "socket.io-client";
9 |
10 | const Data = () => {
11 | const [token, setToken] = useState('');
12 | function removeDomain(email) {
13 | const atIndex = email.indexOf('@');
14 | if (atIndex !== -1) {
15 | return email.substring(0, atIndex); // Extract characters before '@'
16 | }
17 | return email; // Return the original email if '@' is not present
18 | }
19 | const jwtToken1 = localStorage.getItem("jwtToken1");
20 | const bucketName = removeDomain(jwtToken1);
21 | const [files, setFiles] = useState([]);
22 | const [bucketContents, setBucketContents] = useState([]);
23 | const [socket, setSocket] = useState(null);
24 |
25 | useEffect(() => {
26 | const storedToken = localStorage.getItem("jwtToken");
27 | setToken(storedToken);
28 |
29 | const s = io("http://localhost:3001", {
30 | auth: { token: storedToken },
31 | });
32 | setSocket(s);
33 |
34 | // s.on('objectsList', (receivedFiles) => {
35 | // console.log('Received files:', receivedFiles);
36 | // // setFiles(receivedFiles); // Update the files state with received data
37 | // });
38 |
39 | s.emit("displaycontent", bucketName);
40 | s.on("bucketContentsFetched", (contents) => { // Use s.on here, not socket.on
41 | setBucketContents(contents);
42 | });
43 | // Listen for server events
44 |
45 |
46 | // Call the display function when the component mounts
47 | //display(bucketName);
48 | return () => {
49 | s.disconnect(); // Clean up the socket connection on component unmount
50 | };
51 | }, []);
52 |
53 |
54 | function formatBytes(bytes, decimals = 2) {
55 | if (bytes === 0) return '0 Bytes';
56 | const k = 1024;
57 | const dm = decimals < 0 ? 0 : decimals;
58 | const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
59 | const i = Math.floor(Math.log(bytes) / Math.log(k));
60 | return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
61 | }
62 | // function display(bname){
63 | // const socket = io("http://localhost:3001");
64 | // s.emit("displaycontent", bname);
65 | // }
66 |
67 | return (
68 |
69 |
70 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 | {
81 | files.map((file) => {
82 | return
83 |
84 |
{file.data.filename}
85 |
86 | })
87 | }
88 |
89 |
90 |
91 |
Name
92 |
Last Modified
93 |
File Size
94 |
95 | {bucketContents.map((content, index) => (
96 |
103 | ))}
104 |
105 |
106 |
107 | );
108 | }
109 |
110 | export default Data;
--------------------------------------------------------------------------------
/code/reactjs-client/src/components/Dashboard.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from 'react';
2 | import { Button, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle, Typography } from '@mui/material';
3 | import { useNavigate, useParams} from 'react-router-dom';
4 | import Fab from '../components/FAB';
5 | import { FcAbout, FcBusinessman, FcCamera, FcFullTrash } from "react-icons/fc";
6 | import { v4 as uuidV4 } from 'uuid';
7 | import { io } from "socket.io-client";
8 | import Data from './Profile-card/Data';
9 | import ProfileCard from './Profile-card/Profile-card';
10 | import Snackbar from '@mui/material/Snackbar';
11 | import MuiAlert from '@mui/material/Alert';
12 |
13 |
14 | const Dashboard = () => {
15 | const [token, setToken] = useState('');
16 | const [open, setOpen] = useState(false);
17 | const [open1, setOpen1] = useState(false);
18 | const [documentName, setDocumentName] = useState('');
19 | const [documentCode, setDocumentCode] = useState('');
20 | const [files, setFiles] = useState([]); // State variable for files array
21 | const navigate = useNavigate();
22 | const dynamicUuid = uuidV4();
23 | const [socket, setSocket] = useState(null);
24 | const [snackbarOpen, setSnackbarOpen] = useState(false);
25 | const [snackbarMessage, setSnackbarMessage] = useState('');
26 |
27 | const user_name = localStorage.getItem("jwtToken");
28 | const user_mail = localStorage.getItem("jwtToken1")
29 | function removeDomain(email) {
30 | const atIndex = email.indexOf('@');
31 | if (atIndex !== -1) {
32 | return email.substring(0, atIndex); // Extract characters before '@'
33 | }
34 | return email; // Return the original email if '@' is not present
35 | }
36 |
37 | const openSnackbar = (message) => {
38 | setSnackbarMessage(message);
39 | setSnackbarOpen(true);
40 | };
41 |
42 | const closeSnackbar = () => {
43 | setSnackbarOpen(false);
44 | };
45 | const handleCreateDocument = () => {
46 | setOpen(true);
47 | };
48 |
49 | const handleDocumentCode=()=>{
50 | setOpen1(true)
51 | }
52 |
53 | const handleDialogClose = () => {
54 | setOpen(false);
55 | };
56 |
57 | const handleDialogClose1 = () => {
58 | setOpen1(false);
59 | };
60 |
61 | const handleCreateDocumentConfirm = () => {
62 | navigate(`/document/${dynamicUuid}?name=${encodeURIComponent(documentName)}`);
63 | };
64 |
65 | const handleEnterDocumentConfirm=()=>{
66 | navigate(`/document/${documentCode}`)
67 | }
68 |
69 | useEffect(() => {
70 | const storedToken = localStorage.getItem("jwtToken");
71 | setToken(storedToken);
72 |
73 | const s = io("http://localhost:3001", {
74 | auth: { token: storedToken },
75 | });
76 | setSocket(s);
77 |
78 | s.on('docslist', (receivedFiles) => {
79 | console.log('Received files:', receivedFiles);
80 | setFiles(receivedFiles); // Update the files state with received data
81 | });
82 |
83 | s.on('bucketMessage',(msg)=>{
84 | console.log('Msg about the bucket',msg)
85 | openSnackbar(msg);
86 | })
87 |
88 | return () => {
89 | s.disconnect();
90 | };
91 | }, []);
92 |
93 | useEffect(() => {
94 | if (socket == null) return;
95 |
96 | socket.emit('don', user_name);
97 | const bucketName = removeDomain(user_mail)
98 | socket.emit("creationReq",bucketName)
99 | }, [socket]);
100 |
101 | const drive=()=>{
102 | navigate('/workdrive')
103 | }
104 |
105 |
106 | const actions = [
107 | { label: "About", icon: , onClick: drive},
108 | { label: "Profile", icon: , onClick: console.log },
109 | { label: "Picture", icon: , onClick: console.log },
110 | { label: "Trash", icon: , onClick: console.log },
111 | ];
112 |
113 | return (
114 |
115 |
123 |
124 |
125 |
126 | Create a Document
127 |
128 | Enter the document name:
129 | setDocumentName(e.target.value)} />
130 |
131 |
132 | Cancel
133 |
134 | Create
135 |
136 |
137 |
138 |
139 | Enter the Document Code
140 |
141 | Collaboration code
142 | setDocumentCode(e.target.value)} />
143 |
144 |
145 | Cancel
146 |
147 | Collaborate
148 |
149 |
150 |
151 |
156 |
162 | {snackbarMessage}
163 |
164 |
165 |
166 | );
167 | };
168 |
169 | export default Dashboard;
170 |
--------------------------------------------------------------------------------
/Screenshots/README.md:
--------------------------------------------------------------------------------
1 | This is the Home page of our application
2 |
3 | 
4 |
5 | We can login through the appication and the user details are stroed in the IBMDB2
6 |
7 | 
8 |
9 |
10 | This is the dashboard page of our application
11 |
12 | 
13 |
14 |
15 | Create a new document through this
16 |
17 | 
18 |
19 | Add a new collaborator this document through this
20 |
21 | 
22 |
23 | Enter the document if existd
24 |
25 | 
26 |
27 |
28 |
29 | 
30 |
31 |
32 | 
33 |
34 | Print the the document in pdf format
35 |
36 | 
37 |
38 | The docuemnt we can collaborate by sending mail
39 |
40 | 
41 |
42 | The docuement for collaboration using gmailID
43 |
44 | 
45 |
46 |
47 | 
48 |
49 | Giving the HTML format of the document for the sake of people want to create a email format
50 |
51 | 
52 |
53 | Tracking the changes of the file working
54 |
55 | 
56 |
57 |
58 | 
59 |
60 | Chat GPT integration for assisting for the content
61 |
62 | 
63 |
64 |
65 |
66 | 
67 |
68 | Creating the new version of the same file
69 |
70 | 
71 |
72 |
73 | 
74 |
75 | Same document two differnt client connected
76 |
77 | 
78 |
79 | WorkDrive feature storing the user files in the IBM COS
80 |
81 | 
82 |
83 | Uploading the document to the COS bucket
84 |
85 | 
86 |
87 | FIles listed after uploading the document
88 |
89 | 
90 |
91 |
92 | Implemented the microservices for the deployemnt of the project in OPENSHIFT using docker containerization in this [Link](https://react-frontend-tjmanojofficial-dev.apps.sandbox-m2.ll9k.p1.openshiftapps.com/)
93 | 
94 |
95 |
96 |
97 | I'll give here the sample two mailIds for the login without registering
98 |
99 |
100 | mailID: jeyanthvijay2000@gmail.com
101 | password:QAWSED
102 |
103 | mailId : jayanthandrj@gmail.com
104 | password: AWSED
105 |
106 |
--------------------------------------------------------------------------------
/code/flask-server/app.py:
--------------------------------------------------------------------------------
1 | from flask import Flask, render_template, request, session, jsonify, redirect, url_for
2 | from flask_jwt_extended import create_access_token
3 | from flask_jwt_extended import get_jwt_identity
4 | from flask_jwt_extended import jwt_required
5 | from flask_jwt_extended import JWTManager
6 | import ibm_db
7 | import random
8 | from flask_cors import CORS
9 | import random
10 | import smtplib
11 | from email.mime.text import MIMEText
12 | from email.mime.multipart import MIMEMultipart
13 |
14 | import bcrypt
15 | app = Flask(__name__)
16 | CORS(app)
17 | conn = ibm_db.connect("ENTER_YOUR_URI_HERE")
18 | print(conn)
19 | connState = ibm_db.active(conn)
20 | print('IBMDB2 Connected Successfully')
21 | # ENTER_YOUR_APP_SECRET_KEY_FOR_A_FLASK_FOLDER
22 | SMTP_SERVER = 'MAILID'
23 | SMTP_PORT = "PORT"
24 | SMTP_USERNAME = 'YOUR_PERSONAL_MAIL'
25 | SMTP_PASSWORD = '_PASSWORD'
26 | SENDER_EMAIL = "YOUR_PERSONAL_MAILID"
27 |
28 | # Setup the Flask-JWT-Extended extension
29 | app.config["JWT_SECRET_KEY"] = "firstform-thunderbreathing" # Change this!
30 | jwt = JWTManager(app)
31 |
32 |
33 | # @app.route('/token',methods=['POST'])
34 | # def create_token():
35 | # email = request.json.get("email",None)
36 | # password = request.json.get("password",None)
37 | # if email != "test" or password != "test":
38 | # return jsonify({"msg":"Bad Username or password"}),401
39 |
40 | # access_token = create_access_token(identity=email)
41 | # return jsonify(access_token=access_token)
42 |
43 | @app.route("/", methods = ['POST'])
44 | def login():
45 | global uemail
46 | email = request.json.get("email", None)
47 | password = request.json.get("password", None)
48 | details = [email, password]
49 | print(details)
50 | hashed = bcrypt.hashpw(password.encode('utf-8'),bcrypt.gensalt())
51 | print ("hashed",hashed)
52 | sql = "SELECT * FROM REGISTER_HC where EMAILID=? AND PASSWORD = ?"
53 | stmt = ibm_db.prepare(conn, sql)
54 | ibm_db.bind_param(stmt, 1, email)
55 | ibm_db.bind_param(stmt, 2, password)
56 | ibm_db.execute(stmt)
57 | acc = ibm_db.fetch_assoc(stmt)
58 | print("acc",acc)
59 | if acc:
60 | name = acc.get('NAME')
61 | print(name)
62 | access_token=create_access_token(identity={"email":email, "name": name})
63 | return {"access_token": access_token},200
64 | else:
65 | return 'Invalid Login Info !',400
66 |
67 | # @app.route("/test", methods = ['POST'])
68 | # def test():
69 |
70 |
71 | # @app.route("/profile")
72 | # def profile():
73 | # return render_template("profile.html")
74 |
75 | @app.route("/register", methods=['GET', 'POST'])
76 | def register():
77 | if request.method == "POST":
78 | name = request.json.get('name')
79 | email = request.json.get('email')
80 | password = request.json.get('password')
81 | role = request.json.get('role')
82 | sql = "SELECT * FROM REGISTER_HC where EMAILID=?"
83 | stmt = ibm_db.prepare(conn, sql)
84 | ibm_db.bind_param(stmt, 1, email)
85 | # ibm_db.bind_param(stmt, 2, name)
86 | ibm_db.execute(stmt)
87 | acc = ibm_db.fetch_assoc(stmt)
88 | print("41","_",acc)
89 | if acc:
90 | response = {
91 | "status": 201,
92 | "message": "You have been already REGISTERED, please login!"
93 | }
94 | else:
95 | sql = "INSERT into REGISTER_HC VALUES (?,?,?,?)"
96 | stmt = ibm_db.prepare(conn, sql)
97 | ibm_db.bind_param(stmt, 1, name)
98 | ibm_db.bind_param(stmt, 2, email)
99 | ibm_db.bind_param(stmt, 3, password)
100 | ibm_db.bind_param(stmt, 4, role)
101 | ibm_db.execute(stmt)
102 | response = {
103 | "status":200,
104 | "message": "You have Successfully REGISTERED, Please LOGIN"
105 | }
106 | return jsonify(response)
107 |
108 | @app.route('/generate_otp', methods=['POST']) # type: ignore
109 | def generate_otp():
110 | if request.method == 'POST':
111 | user_email = request.json.get('email')
112 | otp = str(random.randint(1000, 9999))
113 |
114 | try:
115 | server = smtplib.SMTP(SMTP_SERVER, SMTP_PORT)
116 | server.starttls()
117 | server.login(SMTP_USERNAME, SMTP_PASSWORD)
118 |
119 | subject = 'OTP Verification'
120 | body = f'Your OTP: {otp}'
121 | msg = MIMEMultipart()
122 | msg['From'] = SENDER_EMAIL
123 | msg['To'] = user_email
124 | msg['Subject'] = subject
125 | msg.attach(MIMEText(body, 'plain'))
126 |
127 | server.sendmail(SENDER_EMAIL, user_email, msg.as_string())
128 | server.quit()
129 |
130 | session['otp'] = otp
131 | session['user_email'] = user_email
132 | response = {"status": "success", "message": "OTP sent successfully.","otp":otp}
133 | print(response)
134 | except Exception as e:
135 | response = {"status": "error", "message": str(e)}
136 | print(session.get('otp'))
137 | return jsonify(response)
138 |
139 | @app.route('/add_collab',methods=['POST'])
140 | def add_collab():
141 | if request.method == 'POST':
142 | sender_mail=request.json.get('sender_mail')
143 | collab_email=request.json.get('collab_email')
144 | code_string=request.json.get('code_string')
145 | try:
146 | server = smtplib.SMTP(SMTP_SERVER, SMTP_PORT)
147 | server.starttls()
148 | server.login(SMTP_USERNAME, SMTP_PASSWORD)
149 |
150 | subject = 'Thunder Docs Collaboration'
151 | body = f'Hi,\n {sender_mail} shared a document for the Collaboration\n The document ID is {code_string}\n Copy this code and use it in the Thunder Docs Collaboration feature'
152 | msg = MIMEMultipart()
153 | msg['From'] = SENDER_EMAIL
154 | msg['To'] = collab_email
155 | msg['Subject'] = subject
156 | msg.attach(MIMEText(body, 'plain'))
157 |
158 | server.sendmail(SENDER_EMAIL, collab_email, msg.as_string())
159 | server.quit()
160 | response={"status":200}
161 | except Exception as e:
162 | response = {"status": "error", "message": str(e)}
163 |
164 | return jsonify(response)
165 |
166 | if __name__ == "__main__":
167 | app.run(debug=True)
168 |
--------------------------------------------------------------------------------
/code/reactjs-client/src/components/Login.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from "react";
2 | import jwtDecode from 'jwt-decode';
3 | import { useNavigate } from 'react-router-dom';
4 | // Material UI Imports
5 | import {
6 | TextField,
7 | InputAdornment,
8 | FormControl,
9 | InputLabel,
10 | IconButton,
11 | Button,
12 | Input,
13 | Box,
14 | Alert,
15 | Stack,
16 | } from "@mui/material";
17 |
18 | // Material UI Icon Imports
19 | import Visibility from "@mui/icons-material/Visibility";
20 | import VisibilityOff from "@mui/icons-material/VisibilityOff";
21 | import LoginIcon from "@mui/icons-material/Login";
22 |
23 | // Email Validation
24 | const isEmail = (email) =>
25 | /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(email);
26 |
27 | export default function Login() {
28 | const navigate = useNavigate();
29 | const [showPassword, setShowPassword] = React.useState(false);
30 | //Inputs
31 | const [email, setemail] = useState();
32 | const [password, setpassword] = useState();
33 |
34 | // Inputs Errors
35 | const [emailError, setEmailError] = useState(false);
36 | const [passwordError, setPasswordError] = useState(false);
37 |
38 | // Overall Form Validity
39 | const [formValid, setFormValid] = useState();
40 | const [success, setSuccess] = useState();
41 |
42 | // Handles Display and Hide Password
43 | const handleClickShowPassword = () => setShowPassword((show) => !show);
44 | const handleMouseDownPassword = (event) => {
45 | event.preventDefault();
46 | };
47 | useEffect(() => {
48 | const token = localStorage.getItem("jwtToken");
49 |
50 | if (token) {
51 | navigate('/dashboard')
52 | }
53 | });
54 |
55 |
56 | // Validation for onBlur Email
57 | const handleEmail = () => {
58 | console.log(isEmail(email));
59 | if (!isEmail(email)) {
60 | setEmailError(true);
61 | return;
62 | }
63 |
64 | setEmailError(false);
65 | };
66 |
67 | // Validation for onBlur Password
68 | const handlePassword = () => {
69 | if (
70 | !password ||
71 | password.length < 5 ||
72 | password.length > 20
73 | ) {
74 | setPasswordError(true);
75 | return;
76 | }
77 |
78 | setPasswordError(false);
79 | };
80 |
81 | //handle Submittion
82 | const handleSubmit = () => {
83 | setSuccess(null);
84 | //First of all Check for Errors
85 |
86 | // If Email error is true
87 | if (emailError || !email) {
88 | setFormValid("Email is Invalid. Please Re-Enter");
89 | return;
90 | }
91 |
92 | // If Password error is true
93 | if (passwordError || !password) {
94 | setFormValid(
95 | "Password is set btw 5 - 20 characters long. Please Re-Enter"
96 | );
97 | return;
98 | }
99 | setFormValid(null);
100 | const opts={
101 | method:'POST',
102 | headers:{
103 | "Content-Type": "application/json"
104 | },
105 | body: JSON.stringify({
106 | "email": email,
107 | "password": password
108 | })
109 | }
110 | fetch('http://127.0.0.1:5000',opts)
111 | .then(resp=>{
112 | if(resp.status===200){
113 | console.log("Login Succesful");
114 | navigate('/dashboard')
115 | return resp.json();
116 | }
117 | else alert("There has been some error")
118 | })
119 | .then(data => {
120 | console.log("Response JSON:", data);
121 | if (data.access_token) {
122 | // Store the JWT token in the local storage
123 | const decodedToken = jwtDecode(data.access_token);
124 | if (decodedToken.sub.name) {
125 | console.log("Name from JWT:", decodedToken.sub.name);
126 | } else {
127 | console.log("Name not found in JWT token",decodedToken);
128 | }
129 | localStorage.setItem("jwtToken", decodedToken.sub.name);
130 | localStorage.setItem("jwtToken1", decodedToken.sub.email);
131 | console.log("Successfully stored in Local Storage");
132 | } else {
133 | throw new Error("JWT token not found in response");
134 | }
135 | })
136 | .catch(error => {
137 | console.error("There was an error!!!",error)
138 | })
139 | };
140 |
141 | return (
142 |
143 |
144 | {
156 | setemail(event.target.value);
157 | }}
158 | />
159 |
160 |
161 |
162 |
166 | Password
167 |
168 | {
174 | setpassword(event.target.value);
175 | }}
176 | value={password}
177 | endAdornment={
178 |
179 |
184 | {showPassword ? : }
185 |
186 |
187 | }
188 | />
189 |
190 |
191 |
192 |
193 |
194 | }
198 | onClick={handleSubmit}
199 | >
200 | LOGIN
201 |
202 |
203 |
204 | {/* Show Form Error if any */}
205 | {formValid && (
206 |
207 |
208 | {formValid}
209 |
210 |
211 | )}
212 |
213 | {/* Show Success if no issues */}
214 | {success && (
215 |
216 |
217 | {success}
218 |
219 |
220 | )}
221 |
222 | );
223 | }
224 |
--------------------------------------------------------------------------------
/code/nodejs-server/server.js:
--------------------------------------------------------------------------------
1 | const PORT=3001
2 | const io = require('socket.io')(PORT, {
3 | cors: {
4 | origin: '*',
5 | methods: ['GET', 'POST']
6 | }
7 | });
8 |
9 | const mongoose = require("mongoose");
10 | const Document = require("./document")
11 | const uri = "URI";
12 |
13 |
14 | const IBM = require('ibm-cos-sdk');
15 |
16 | var config = {
17 | CRENDTIALS
18 |
19 | };
20 |
21 | var s3 = new IBM.S3(config);
22 |
23 |
24 |
25 | // Dictionary to store connected client names per document
26 | const connectedClientsPerDocument = {};
27 |
28 | async function connectToDatabase() {
29 | try {
30 | await mongoose.connect(uri, {
31 | useNewUrlParser: true,
32 | useUnifiedTopology: true,
33 | });
34 | console.log("Db connected Successfully");
35 | } catch (error) {
36 | console.log("Error occurred: ", error);
37 | }
38 | }
39 | connectToDatabase();
40 |
41 | const defaultValue = ""
42 | io.use((socket, next) => {
43 | const token = socket.handshake.auth.token;
44 | console.log("Received token from client:", token);
45 | next();
46 | });
47 |
48 |
49 | io.on('connection', socket => {
50 | let documentId; // Define documentId variable for this connection
51 |
52 | socket.on('don', async (user_mail) => {
53 | const trimmedUserMail = user_mail.trim();
54 |
55 | try {
56 | const files = await Document.find({ createdBy: trimmedUserMail });
57 | socket.emit('docslist', files);
58 | } catch (error) {
59 | console.error("Error fetching files:", error);
60 | }
61 | });
62 |
63 |
64 | socket.on('creationReq',async (user_mail)=>{
65 | console.log("Bucket Creation Request",user_mail)
66 | const msg=await createBucket(user_mail)
67 | socket.emit('bucketMessage',msg)
68 | const objects=getBucketContents(user_mail)
69 | socket.emit('objectsList',objects)
70 | })
71 |
72 | socket.on("createfile", async (fileName, fileData, bname) => {
73 | await createFile(fileName, fileData, bname);
74 | });
75 | socket.on("displaycontent", async (bname) => {
76 | await getBucketContents(bname);
77 | });
78 | socket.on("bucket-creation", async bucketName=>{
79 | await createBucket(bucketName)
80 | //console.log(bucketName)
81 | });
82 |
83 | socket.on("save-button",async file=>{
84 | await createTextFile(file)
85 | })
86 |
87 |
88 | socket.on('get-document', async (docId, documentName) => {
89 | documentId = docId; // Set the documentId for this connection
90 | const clientName = socket.handshake.auth.token.trim();
91 | const document = await findOrCreateDocument(documentId, documentName, clientName)
92 | socket.join(documentId)
93 | socket.emit("load-document", document.data)
94 | socket.on('send-changes', delta => {
95 | socket.broadcast.to(documentId).emit('receive-changes', delta);
96 | });
97 |
98 | socket.on('get-user-documents', async () => {
99 | // const clientName = socket.handshake.auth.token.trim();
100 | const userDocuments = await Document.find({ createdBy: clientName });
101 |
102 | socket.emit('user-documents', userDocuments);
103 | });
104 |
105 | socket.on("save-document", async data => {
106 | const currentDate = new Date();
107 | const month = String(currentDate.getMonth() + 1).padStart(2, '0');
108 | const day = String(currentDate.getDate()).padStart(2, '0');
109 | const hours = String(currentDate.getHours()).padStart(2, '0');
110 | const minutes = String(currentDate.getMinutes()).padStart(2, '0');
111 |
112 | const formattedDate = `${hours}:${minutes} / ${day}-${month}`;
113 | const lastModified=formattedDate
114 |
115 | await Document.findByIdAndUpdate(documentId, { data,lastModified:lastModified });
116 | });
117 |
118 | if (!connectedClientsPerDocument[documentId]) {
119 | connectedClientsPerDocument[documentId] = [];
120 | }
121 | connectedClientsPerDocument[documentId].push(clientName);
122 | io.to(documentId).emit('connected-clients', connectedClientsPerDocument[documentId]);
123 |
124 | socket.on('disconnect', () => {
125 | // Remove the client name from the list for the specific document
126 | if (connectedClientsPerDocument[documentId]) {
127 | connectedClientsPerDocument[documentId] = connectedClientsPerDocument[documentId].filter(name => name !== clientName);
128 | io.to(documentId).emit('connected-clients', connectedClientsPerDocument[documentId]);
129 | }
130 | console.log(connectedClientsPerDocument);
131 | });
132 |
133 | console.log('connected..');
134 | });
135 |
136 | console.log('connected..');
137 | });
138 |
139 |
140 | async function createBucket(bucketName) {
141 | console.log(`Creating new bucket: ${bucketName}`);
142 | const msg1='Succcessfully created the bucket 🎉'
143 | const msg2=`Fetching objects from the bucket ${bucketName}`
144 | return s3.createBucket({
145 | Bucket: bucketName,
146 | CreateBucketConfiguration: {
147 | LocationConstraint: 'us-south'
148 | },
149 | }).promise()
150 | .then(() => {
151 | console.log(`Bucket: ${bucketName} created!`);
152 | return msg1
153 | })
154 | .catch((e) => {
155 | console.error(`ERROR: ${e.code} - ${e.message}\n`);
156 | if(e.code == 'BucketAlreadyExists') return msg2
157 | else return e.code
158 | });}
159 |
160 | async function getBucketContents(bucketName) {
161 | console.log(`Retrieving bucket contents from: ${bucketName}`);
162 | return s3.listObjects({ Bucket: bucketName })
163 | .promise()
164 | .then((data) => {
165 | if (data != null && data.Contents != null) {
166 | const contents = data.Contents.map((item) => {
167 | return {
168 | name: item.Key,
169 | lastModified: item.LastModified,
170 | size: item.Size,
171 | };
172 | });
173 | io.emit("bucketContentsFetched", contents);
174 | // console.log(contents)
175 | return contents;
176 | } else {
177 | return [];
178 | }
179 | })
180 | .catch((e) => {
181 | console.error(`ERROR: ${e.code} - ${e.message}\n`);
182 | });
183 | }
184 |
185 | async function findOrCreateDocument(id, name, createdBy) {
186 | if (id == null) return
187 |
188 | const document = await Document.findById(id);
189 | if (document) return document;
190 |
191 | const currentDate = new Date();
192 | const year = currentDate.getFullYear();
193 | const month = String(currentDate.getMonth() + 1).padStart(2, '0');
194 | const day = String(currentDate.getDate()).padStart(2, '0');
195 | const hours = String(currentDate.getHours()).padStart(2, '0');
196 | const minutes = String(currentDate.getMinutes()).padStart(2, '0');
197 |
198 | const formattedDate = `${hours}:${minutes} / ${day}-${month}`;
199 | const createdAt=formattedDate
200 |
201 | return await Document.create({ _id: id, name, data: defaultValue,createdBy,createdAt });
202 | }
203 |
204 |
205 | async function createFile(fname,file,bname) {
206 | console.log(`Creating new item: `);
207 | // var local = "C:/Users/sanja/OneDrive/Documents/dbms notes.pdf";
208 | // const fileStream = fs.createReadStream(local);
209 | // const fileName = path.basename(local);
210 | const testing= s3.putObject({
211 | Bucket:bname,
212 | Key:fname,
213 | Body: file,
214 | })
215 | console.log(testing)
216 | return testing.promise()
217 | .then((print) => {
218 | console.log(print)
219 | console.log(`Item:created!`);
220 | })
221 | .catch((e) => {
222 | console.error(`ERROR: ${e.code} - ${e.message}\n`);
223 | });
224 | }
--------------------------------------------------------------------------------
/code/reactjs-client/src/components/Signup.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import {
3 | TextField,
4 | InputAdornment,
5 | FormControl,
6 | InputLabel,
7 | IconButton,
8 | Button,
9 | Input,
10 | Alert,
11 | Stack,
12 | Box
13 | } from "@mui/material";
14 | import { Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle } from '@mui/material';
15 | import Visibility from "@mui/icons-material/Visibility";
16 | import VisibilityOff from "@mui/icons-material/VisibilityOff";
17 | import LoginIcon from "@mui/icons-material/Login";
18 | import Snackbar from '@mui/material/Snackbar';
19 | import MuiAlert from '@mui/material/Alert';
20 |
21 | const isEmail = (email) =>
22 | /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(email);
23 |
24 | export default function Signup() {
25 | const [showPassword, setShowPassword] = React.useState(false);
26 | const [usernameInput, setUsernameInput] = useState();
27 | const [emailInput, setEmailInput] = useState();
28 | const [passwordInput, setPasswordInput] = useState();
29 | const [usernameError, setUsernameError] = useState(false);
30 | const [emailError, setEmailError] = useState(false);
31 | const [passwordError, setPasswordError] = useState(false);
32 | const [formValid, setFormValid] = useState();
33 | const [success, setSuccess] = useState();
34 | const [open, setOpen] = useState(false);
35 | const [otptyped, setOtptyped] = useState('')
36 | const [snackbarOpen, setSnackbarOpen] = useState(false);
37 | const [successMessage, setSuccessMessage] = useState('');
38 | const [otpValidated, setOtpValidated] = useState(false);
39 | const [tpButton, settpButton] = useState(true);
40 |
41 | const handleClickShowPassword = () => setShowPassword((show) => !show);
42 | const handleMouseDownPassword = (event) => {
43 | event.preventDefault();
44 | };
45 |
46 | const handleUsername = () => {
47 | if (!usernameInput) {
48 | setUsernameError(true);
49 | return;
50 | }
51 | setUsernameError(false);
52 | };
53 |
54 | const handleEmail = () => {
55 | if (!isEmail(emailInput)) {
56 | setEmailError(true);
57 | return;
58 | }
59 | setEmailError(false);
60 | };
61 |
62 | const handlePassword = () => {
63 | if (!passwordInput || passwordInput.length < 5 || passwordInput.length > 20) {
64 | setPasswordError(true);
65 | return;
66 | }
67 | setPasswordError(false);
68 | };
69 |
70 | const handleSubmit = () => {
71 | setSuccess(null);
72 |
73 | if (usernameError || !usernameInput) {
74 | setFormValid("Username is set btw 5 - 15 characters long. Please Re-Enter");
75 | return;
76 | }
77 |
78 | if (emailError || !emailInput) {
79 | setFormValid("Email is Invalid. Please Re-Enter");
80 | return;
81 | }
82 |
83 | if (passwordError || !passwordInput) {
84 | setFormValid("Password is set btw 5 - 20 characters long. Please Re-Enter");
85 | return;
86 | }
87 |
88 | setFormValid(null);
89 |
90 | const opts = {
91 | method: "POST",
92 | headers: {
93 | "Content-Type": "application/json",
94 | },
95 | body: JSON.stringify({
96 | // name: usernameInput,
97 | email: emailInput,
98 | // password: passwordInput,
99 | }),
100 | };
101 |
102 |
103 | fetch("http://127.0.0.1:5000/generate_otp", opts)
104 | .then((resp) => {
105 | if (resp.status === 200) {
106 | console.log("OTP sent successfully");
107 | setOpen(true)
108 | return resp.json();
109 | } else {
110 | throw new Error("OTP sent Failed");
111 | }
112 | })
113 | .then((data) => {
114 | localStorage.setItem("xzcdcvf",data.otp);
115 | setSuccess("OTP Sent Successfully");
116 | })
117 | .catch((error) => {
118 | console.error("There was an error:", error);
119 | setFormValid("OTP Failed. Please try again later.");
120 | });
121 | };
122 |
123 | const validateOtp =()=>{
124 |
125 | const entered_otp=otptyped
126 | const stored_otp=localStorage.getItem("xzcdcvf")
127 | if(entered_otp === stored_otp){
128 | console.log("OTP Matched");
129 | setSuccessMessage('OTP Verification Successful');
130 | setSnackbarOpen(true);
131 | setOpen(false)
132 | setOtpValidated(true);
133 | settpButton(false)
134 | localStorage.removeItem("xzcdcvf")
135 | }
136 | else{
137 | setSuccess("Please Re-Enter the OTP")
138 | }
139 |
140 | }
141 |
142 | const handleSnackbarClose = (event, reason) => {
143 | if (reason === 'clickaway') {
144 | setSnackbarOpen(false);
145 | }
146 | };
147 |
148 | const registerApi=()=>{
149 |
150 | const opts = {
151 | method: "POST",
152 | headers: {
153 | "Content-Type": "application/json",
154 | },
155 | body: JSON.stringify({
156 | name: usernameInput,
157 | email: emailInput,
158 | password: passwordInput,
159 | }),
160 | };
161 |
162 |
163 | fetch("http://127.0.0.1:5000/register", opts)
164 | .then((resp) => {
165 | console.log(resp.statusText)
166 | if (resp.status === 200) {
167 | return resp.json();
168 | } else {
169 | throw new Error("Registration Failed");
170 | }
171 | })
172 | .then((data) => {
173 | console.log("Response JSON:", data);
174 | if(data.status === 201){
175 | setFormValid(data.message);
176 | }
177 | else if(data.status === 200)
178 | {
179 | setSuccessMessage("Registration Successful ! Now Login using the credentials");
180 | setSnackbarOpen(true);
181 | setFormValid("")
182 | setEmailInput('');
183 | setPasswordInput('');
184 | setUsernameInput('');
185 | }
186 | })
187 | .catch((error) => {
188 | console.error("There was an error:", error);
189 | setFormValid("Registration Failed. Please try again later.");
190 | });
191 | };
192 |
193 | return (
194 |
195 |
196 | {
206 | setUsernameInput(event.target.value);
207 | }}
208 | onBlur={handleUsername}
209 | />
210 |
211 |
212 |
213 | {
225 | setEmailInput(event.target.value);
226 | }}
227 | />
228 |
229 |
230 |
231 |
235 | Password
236 |
237 | {
243 | setPasswordInput(event.target.value);
244 | }}
245 | value={passwordInput}
246 | endAdornment={
247 |
248 |
253 | {showPassword ? : }
254 |
255 |
256 | }
257 | />
258 |
259 |
260 |
261 |
262 |
263 |
264 | }
268 | onClick={registerApi}
269 | disabled={!otpValidated} // Disable the button if otpValidated is false
270 | >
271 | Register
272 |
273 |
274 |
275 |
276 |
282 | Request for OTP
283 |
284 |
285 |
286 | {/* Show Form Error if any */}
287 | {formValid && (
288 |
289 |
290 | {formValid}
291 |
292 |
293 | )}
294 |
295 |
300 |
305 | {successMessage}
306 |
307 |
308 |
309 | {/* Show Success if no issues */}
310 | {success && (
311 |
312 |
313 |
314 | {success}
315 |
316 |
317 | Enter the OTP
318 |
319 | OTP sent to {emailInput}
320 | setOtptyped(e.target.value)} />
321 |
322 |
323 |
324 | Check
325 |
326 |
327 |
328 | )}
329 |
330 | );
331 | }
332 |
--------------------------------------------------------------------------------
/code/reactjs-client/src/components/TextEditor/TextEditor.js:
--------------------------------------------------------------------------------
1 | import { useCallback,useEffect, useState ,useRef} from "react";
2 | import ReactQuill, { Quill } from "react-quill";
3 | import 'quill/dist/quill.snow.css';
4 | import '../TextEditor/style.css';
5 | // import registerQuillLanguageTool from "quill-languagetool";
6 | import { useParams,useLocation,useNavigate } from "react-router-dom";
7 | import {v4 as uuidV4} from 'uuid'
8 | import { Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle,Button,Alert } from '@mui/material';
9 | import Snackbar from '@mui/material/Snackbar';
10 | import { QuillDeltaToHtmlConverter } from 'quill-delta-to-html';
11 | import ContentModal from "./ContentModal";
12 | import Fab from '../FAB';
13 | import { FcAlarmClock,FcClock,FcLinux, FcOpenedFolder, FcCollaboration, FcFullTrash,FcExport,FcHome } from "react-icons/fc";
14 | import { io } from "socket.io-client";
15 | import Popup from '../Popup/Popup';
16 |
17 | const SAVE_INTERVAL_MS=2000
18 | // registerQuillLanguageTool(Quill);
19 | const TOOLBAR_OPTIONS = [
20 | [{ header: [1, 2, 3, 4, 5, 6, false] }],
21 | [{ font: [] }],
22 | [{ list: "ordered" }, { list: "bullet" }],
23 | ["bold", "italic", "underline"],
24 | [{ color: [] }, { background: [] }],
25 | [{ script: "sub" }, { script: "super" }],
26 | [{ align: [] }],
27 | ["image", "blockquote", "code-block"],
28 | ["clean"],
29 | ]
30 |
31 | const modules = {
32 | // languageTool: {
33 | // apiOptions: {
34 | // level: "picky",
35 | // },
36 | // },
37 | toolbar:TOOLBAR_OPTIONS
38 | };
39 |
40 | export default function TextEditor(){
41 | const [socket,setSocket]=useState()
42 | const [quill,setQuill]=useState()
43 | const {id: documentId} = useParams()
44 | const location = useLocation();
45 | const queryParams = new URLSearchParams(location.search);
46 | const documentName = queryParams.get("name");
47 | const [token, setToken] = useState('');
48 | const [connectedClients, setConnectedClients] = useState([]);
49 | const [buttonPopup, setButtonPopup] = useState(false);
50 | const [nth, setNth] = useState(1);
51 | const navigate=useNavigate();
52 | const dynamicUuid = uuidV4();
53 | const [collab_email,setCollab_email]=useState('')
54 | const [open, setOpen] = useState(false);
55 | const [snackbarOpen, setSnackbarOpen] = useState(false);
56 | const [quillHtml, setQuillHtml] = useState("");
57 | const [isModalOpen, setIsModalOpen] = useState(false);
58 | const [versionIndex, setVersionIndex] = useState(1);
59 | const [either,setEither]=useState('');
60 | const [modalContent, setModalContent] = useState("");
61 | const quillRef = useRef(null);
62 |
63 | useEffect(() => {
64 | const trackedItems=localStorage.getItem('trackingContents')
65 | const storedContents = localStorage.getItem('editorContents');
66 | if(storedContents){
67 | console.log('From 57th')
68 | setButtonPopup(true)
69 | }
70 | if(trackedItems){
71 | localStorage.removeItem("trackingContents")
72 | }
73 | }, []);
74 |
75 | useEffect(() => {
76 | const storedToken = localStorage.getItem("jwtToken");
77 | setToken(storedToken);
78 |
79 | // const s = io("http://localhost:3001", {
80 | // auth: { token: storedToken } // Send the token as part of the initial connection
81 | // });
82 | const ipAddress = window.location.hostname;
83 |
84 | const s = io(`http://${ipAddress}:3001`, {
85 | auth: { token: storedToken } // Send the token as part of the initial connection
86 | });
87 | setSocket(s);
88 |
89 | s.on('connected-clients', clients => {
90 | setConnectedClients(clients);
91 | });
92 |
93 | s.on('docslist',(files)=>{
94 | console.log('Rec:',files)
95 | })
96 |
97 | return () => {
98 | s.disconnect();
99 | };
100 | }, []);
101 |
102 | const convertQuillToHtml = () => {
103 | if (quill) {
104 | const delta = quill.getContents();
105 | const converter = new QuillDeltaToHtmlConverter(delta.ops, {});
106 | const html = converter.convert();
107 | setQuillHtml(html);
108 | setIsModalOpen(true);
109 | setEither('1');
110 | }
111 | };
112 |
113 | useEffect(() => {
114 | if (quillRef.current) {
115 | const quill = quillRef.current.getEditor();
116 |
117 | const countLines = () => {
118 | const lines = quill.getLine(0, quill.getLength());
119 | console.log(`Number of lines: ${lines.length}`);
120 | };
121 |
122 | quill.on("text-change", countLines);
123 |
124 | return () => {
125 | quill.off("text-change", countLines); // Clean up the event listener
126 | };
127 | }
128 | }, []);
129 |
130 | useEffect(()=>{
131 | if(socket==null|| quill==null) return
132 |
133 | socket.once("load-document",document=>{
134 | quill.setContents(document)
135 | quill.enable()
136 | })
137 | socket.emit('get-document',documentId,documentName)
138 | },[socket,quill,documentId])
139 |
140 | useEffect(()=>{
141 |
142 | if(socket == null || quill == null) return
143 |
144 | const interval=setInterval(()=>{
145 | socket.emit('save-document',quill.getContents())
146 | }, SAVE_INTERVAL_MS)
147 |
148 | return ()=>{
149 | clearInterval(interval)
150 | }
151 |
152 | },[socket,quill])
153 |
154 | useEffect(()=>{
155 | if (socket == null || quill == null) return
156 |
157 | const handler=delta=>{
158 | quill.updateContents(delta)
159 | }
160 | socket.on('receive-changes',handler)
161 |
162 | return ()=>{
163 | socket.off('receive-changes',handler)
164 | }
165 | },[socket,quill])
166 |
167 | useEffect(()=>{
168 | if (socket == null || quill == null) return
169 |
170 | const handler=(delta,oldDelta,source)=>{
171 | if(source !=='user') return
172 | socket.emit("send-changes",delta)
173 | }
174 | quill.on('text-change',handler)
175 |
176 | return ()=>{
177 | quill.off('text-change',handler)
178 | }
179 | },[socket,quill])
180 |
181 |
182 | useEffect(() => {
183 | const interval = setInterval(() => {
184 | const currentQuillContent = quill.getContents();
185 | const storedVersions = JSON.parse(localStorage.getItem("trackingContents")) || [];
186 | storedVersions.push({
187 | version: versionIndex,
188 | content: currentQuillContent,
189 | });
190 | localStorage.setItem("trackingContents", JSON.stringify(storedVersions));
191 | setVersionIndex(versionIndex + 1);
192 | }, 10000); // Change to 10 seconds
193 |
194 | return () => {
195 | clearInterval(interval);
196 | };
197 | }, [quill, versionIndex]);
198 |
199 |
200 | const printLocalStorageContent = () => {
201 | const storedVersions = JSON.parse(localStorage.getItem("trackingContents")) || [];
202 | storedVersions.forEach((version) => {
203 | console.log(`Version ${version.version}:`, version.content);
204 | });
205 | setEither('2')
206 | };
207 |
208 |
209 | const displayContentInModal = () => {
210 | const storedVersions = JSON.parse(localStorage.getItem("trackingContents")) || [];
211 | let modalContent = "";
212 | storedVersions.forEach((version) => {
213 | modalContent += `Version ${version.version}:\n`;
214 | modalContent += JSON.stringify(version.content, null, 2); // Convert content to JSON string with indentation
215 | modalContent += "\n\n";
216 | });
217 | setModalContent(modalContent);
218 | setIsModalOpen(true);
219 | setEither('2')
220 | };
221 |
222 |
223 | const Abc = () => {
224 | window.print();
225 | };
226 |
227 | const openModal = () => {
228 |
229 | };
230 |
231 | const closeModal = () => {
232 | setIsModalOpen(false);
233 | };
234 |
235 | const def=()=>{
236 | const printing=quill.getContents()
237 | setNth(prevNth => prevNth + 1);
238 | localStorage.setItem('editorContents', JSON.stringify(printing));
239 | const docnamex = `${documentName}-${nth}.0-`
240 | // setButtonPopup(true)
241 | const url=`/document/${dynamicUuid}?name=${encodeURIComponent(docnamex)}`
242 | window.open(url, '_blank');
243 | }
244 |
245 |
246 | const handleSave = () => {
247 | const restore=localStorage.getItem('editorContents')
248 | quill.setContents(JSON.parse(restore))
249 | localStorage.removeItem('editorContents'); // Remove the stored contents
250 | };
251 | const jkl=()=>{
252 | localStorage.removeItem('jwtToken');
253 | localStorage.removeItem('jwtToken1');
254 | navigate('/logster')
255 | }
256 |
257 | const add=()=>{
258 | setOpen(true)
259 | }
260 | const dashboard=()=>{
261 | navigate('/dashboard')
262 | }
263 | const actions = [
264 | { label: "Export to PDF 💪", icon: , onClick: Abc },
265 | { label: "Create a new Version ✨", icon: , onClick: def },
266 | { label: "Add Collaborators🏆", icon: , onClick: add},
267 | { label: "Create a HTML Template", icon: , onClick: convertQuillToHtml },
268 | { label: "Tracking Changes", icon: , onClick: displayContentInModal },
269 | { label: "Go to Dashboard", icon: , onClick: dashboard },
270 | { label: "Log Out🏃♀️", icon: , onClick: jkl },
271 | ];
272 |
273 |
274 | const wrapperRef = useCallback(wrapper=>{
275 | if(wrapper==null) return;
276 |
277 | wrapper.innerHTML = '';
278 | const editor = document.createElement('div');
279 | wrapper.append(editor);
280 | const q = new Quill(editor, { theme: 'snow', modules: modules});
281 | q.disable()
282 | const storedContents = localStorage.getItem('editorContents');
283 | if (storedContents) {
284 | const delta = {
285 | ops: [{ attributes: { color: '#000000' }, insert: 'This is sample text.' }],
286 | };
287 | q.setContents(delta);
288 | } else {
289 | q.setText('Loading...');
290 | }
291 | setQuill(q);
292 | setQuill(q)
293 | }, []);
294 |
295 | const handleSnackbarClose = (event, reason) => {
296 | if (reason === 'clickaway') {
297 | setSnackbarOpen(false);
298 | }
299 | };
300 |
301 | const sendMail=()=>{
302 | setOpen(false)
303 | const sender_mail=localStorage.getItem("jwtToken1")
304 | const currentURL = window.location.href;
305 | const parts = currentURL.split('/');
306 | const lastPart = parts[parts.length - 1];
307 |
308 | const opts = {
309 | method: "POST",
310 | headers: {
311 | "Content-Type": "application/json",
312 | },
313 | body: JSON.stringify({
314 | // name: usernameInput,
315 | sender_mail:sender_mail,
316 | collab_email: collab_email,
317 | code_string: lastPart
318 | // password: passwordInput,
319 | }),
320 | };
321 |
322 | fetch("http://127.0.0.1:5000/add_collab", opts)
323 | .then((resp) => {
324 | if (resp.status === 200) {
325 | setSnackbarOpen(true);
326 | return resp.json();
327 | } else {
328 | throw new Error("OTP sent Failed");
329 | }
330 | })
331 | .then((data) => {
332 | console.log(data)
333 | })
334 | .catch((error) => {
335 | console.error("There was an error:", error);
336 | });
337 | }
338 |
339 | return (
340 | <>
341 |
342 |
343 |
Document Name
344 |
{documentName}
345 |
346 |
347 |
Connected Clients:
348 |
349 | {connectedClients.map(clientName => (
350 | {clientName}
351 | ))}
352 |
353 |
354 |
355 |
356 |
357 |
358 | Enter the user mailID to add them as a collaborator
359 |
360 | {/* OTP sent to {emailInput} */}
361 | setCollab_email(e.target.value)} />
362 |
363 |
364 |
365 | Send Document
366 |
367 |
368 |
369 |
374 |
379 | File shared successfully !
380 |
381 |
382 |
389 |
390 | >
391 | );
392 | }
--------------------------------------------------------------------------------